From 87deb8b080d0c930fe92a7a58c51f22a559033e5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Mar 2014 12:40:33 -0500 Subject: [PATCH 0001/4033] CLJS-778: RSeq does not implement INext, incorrect -rest implementation --- src/cljs/cljs/core.cljs | 6 ++++++ test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 6b559e11f..ee159d83e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -772,6 +772,12 @@ reduces them without incurring seq initialization" (-nth ci i)) (-rest [coll] (if (pos? i) + (RSeq. ci (dec i) nil) + ())) + + INext + (-next [coll] + (when (pos? i) (RSeq. ci (dec i) nil))) ICounted diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 88f0dbfd1..5b813a984 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2150,6 +2150,10 @@ (assert (= 4 (get (range 1 3) n 4))) (assert (= :fail (try (nth (range 1 3) n 4) (catch js/Error e :fail))))) + ;; CLJS-778 + (assert (= (-rest (rseq [0])) ())) + (assert (nil? (-next (rseq [0])))) + (assert (= (set (rseq [0])) #{0})) :ok ) From 00a9b7bde0c8823175560b453a00e7be09ddd250 Mon Sep 17 00:00:00 2001 From: Michael Glaesemann Date: Mon, 3 Mar 2014 18:39:48 -0500 Subject: [PATCH 0002/4033] Permit hashbang omission for nodejs target. Adding :hashbang false to the compiler build options will omit the hashbang. --- src/clj/cljs/closure.clj | 2 +- test/clj/cljs/closure_tests.clj | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 04729663c..c1d559fb3 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -510,7 +510,7 @@ (str (apply str (map #(slurp (io/resource %)) paths)) "\n")) (defn make-preamble [{:keys [target preamble hashbang]}] - (str (when (= :nodejs target) + (str (when (and (= :nodejs target) (not (false? hashbang))) (str "#!" (or hashbang "/usr/bin/env node") "\n")) (when preamble (preamble-from-paths preamble)))) diff --git a/test/clj/cljs/closure_tests.clj b/test/clj/cljs/closure_tests.clj index d28090860..8e83882e6 100644 --- a/test/clj/cljs/closure_tests.clj +++ b/test/clj/cljs/closure_tests.clj @@ -11,6 +11,14 @@ (testing "with custom hashbang" (is (= "#!/bin/env node\n" (make-preamble {:target :nodejs :hashbang "/bin/env node"})))) + (testing "with no hashbang" + (is (= "" (make-preamble {:target :nodejs + :hashbang false}))) + (testing "and preamble" + (is (= "var preamble1 = require(\"preamble1\");\n" + (make-preamble {:target :nodejs + :hashbang false + :preamble ["cljs/preamble1.js"]}))))) (testing "with preamble" (is (= "#!/usr/bin/env node\nvar preamble1 = require(\"preamble1\");\n" (make-preamble {:target :nodejs From 13cacc68b4c9816bb09ce048ca3eb6dcf44e3144 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 5 Mar 2014 09:50:31 -0500 Subject: [PATCH 0003/4033] CLJS-780: apply-to broken for arg count >= 6 --- src/clj/cljs/core.clj | 2 +- test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 9dfc4550d..f4dcc6bb2 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1540,7 +1540,7 @@ (~print-fn (str ~bs-str ", " ~expr-str ", " ~iterations " runs, " elapsed# " msecs")))))) -(def cs (into [] (map (comp symbol core/str core/char) (range 97 118)))) +(def cs (into [] (map (comp gensym core/str core/char) (range 97 118)))) (defn gen-apply-to-helper ([] (gen-apply-to-helper 1)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 5b813a984..912e46697 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2155,5 +2155,12 @@ (assert (nil? (-next (rseq [0])))) (assert (= (set (rseq [0])) #{0})) + ;; CLJS-780 + (def cljs-780 (atom {:foo (with-meta [] {:bar '(1 2 3)})})) + (swap! cljs-780 update-in [:foo] vary-meta update-in [:bar] vec) + (let [x (-> @cljs-780 :foo meta :bar)] + (assert (vector? x)) + (assert (= x [1 2 3]))) + :ok ) From bea959070ec1dadd06c8b7d0bbf35fd3da988cef Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 6 Mar 2014 19:25:02 -0500 Subject: [PATCH 0004/4033] benchmark and test Nashorn when available --- script/benchmark | 7 +++++++ script/test | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/script/benchmark b/script/benchmark index 6ce13b342..e622d43b3 100755 --- a/script/benchmark +++ b/script/benchmark @@ -28,3 +28,10 @@ else echo "Benchmarking with JavaScriptCore" "${JSC_HOME}/jsc" -f out/core-advanced-benchmark.js fi + +if [ "$NASHORN_HOME" = "" ]; then + echo "NASHORN_HOME not set, skipping JavaScriptCore benchmarks" +else + echo "Benchmarking with Nashorn" + "${NASHORN_HOME}/jjs" out/core-advanced-benchmark.js +fi diff --git a/script/test b/script/test index 5d5996044..ccc933548 100755 --- a/script/test +++ b/script/test @@ -3,7 +3,7 @@ rm -rf out mkdir -p out -possible=3 +possible=4 ran=0 #bin/cljsc test >out/core-test.js @@ -35,4 +35,12 @@ else ran=$[ran+1] fi +if [ "$NASHORN_HOME" = "" ]; then + echo "NASHORN_HOME not set, skipping Nashorn tests" +else + echo "Testing with Nashorn" + "${NASHORN_HOME}/jjs" out/core-advanced-test.js + ran=$[ran+1] +fi + echo "Tested with $ran out of $possible possible js targets" From c441a12be3390cd1b3d1eea80b14067aafa3a4ca Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 7 Mar 2014 10:01:39 -0500 Subject: [PATCH 0005/4033] typo --- script/benchmark | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/benchmark b/script/benchmark index e622d43b3..5d8320f09 100755 --- a/script/benchmark +++ b/script/benchmark @@ -30,7 +30,7 @@ else fi if [ "$NASHORN_HOME" = "" ]; then - echo "NASHORN_HOME not set, skipping JavaScriptCore benchmarks" + echo "NASHORN_HOME not set, skipping Nashorn benchmarks" else echo "Benchmarking with Nashorn" "${NASHORN_HOME}/jjs" out/core-advanced-benchmark.js From bae40ab9a9924a05a7425a734de2315c3185a5f6 Mon Sep 17 00:00:00 2001 From: Stuart Sierra Date: Fri, 7 Mar 2014 16:24:51 -0500 Subject: [PATCH 0006/4033] New Closure library release script from Git source --- .../make-closure-library-jars.sh | 210 +++++++++++++----- 1 file changed, 155 insertions(+), 55 deletions(-) diff --git a/script/closure-library-release/make-closure-library-jars.sh b/script/closure-library-release/make-closure-library-jars.sh index a69eb48e6..bd27363e2 100755 --- a/script/closure-library-release/make-closure-library-jars.sh +++ b/script/closure-library-release/make-closure-library-jars.sh @@ -1,87 +1,187 @@ #!/usr/bin/env bash -set -e +# make-closure-library-jars.sh + +# ClojureScript depends on the Google Closure JavaScript Libraries, +# but Google does not publish those libraries in a Maven repository. +# This script builds release JAR and POM files for the Google Closure +# Library and its third-party extensions. + +# The Google Closure Libraries are divided into two parts: the main +# library and third-party extensions. The main library is Apache +# licensed; the third-party extensions have a variety of different +# licenses. However, code in the main library depends on the +# third-party extensions, not the other way around. See CLJS-418 for +# details. + +# To manage this, we build two JARs, google-closure-library and +# google-closure-library-third-party, with the former declaring an +# explicit dependency on the latter. This permits consumers to exclude +# the third-party libraries (and their various licenses) if they know +# they don't need them. -## Set the version numbers to download and release: +# To match this structure, we need to alter the deps.js file that the +# Google Closure Compiler uses to resolve dependencies. See CLJS-276 +# for details. -ZIP_VERSION="20130212-95c19e7f0f5f" -RELEASE_VERSION="0.0-20130212-95c19e7f0f5f" +# The last release ZIP made by Google was 20130212-95c19e7f0f5f. To +# get newer versions, we have to go to the Git repository. -## These only need to change if the URL or file names change: +# Usage: -ZIP_BASE="closure-library-${ZIP_VERSION}" -ZIP_FILE="${ZIP_BASE}.zip" -ZIP_URL="http://closure-library.googlecode.com/files/${ZIP_FILE}" +# 1. Clone the Google Closure Library Git repository +# +# 2. cd to the directory containing this script +# +# 3. Run this script with the path to the G.Closure Library +# as a command-line argument -RELEASE_BASE="google-closure-library-${RELEASE_VERSION}" -JAR_FILE="$RELEASE_BASE.jar" -POM_FILE="$RELEASE_BASE.pom" +# If you are a Clojure release admin (you have the GPG key) then set +# the environment variable SIGN_GOOGLE_CLOSURE_LIBRARY_RELEASE to sign +# the releases with GPG. + + + +set -e -THIRD_PARTY_RELEASE_BASE="google-closure-library-third-party-${RELEASE_VERSION}" -THIRD_PARTY_JAR_FILE="$THIRD_PARTY_RELEASE_BASE.jar" -THIRD_PARTY_POM_FILE="$THIRD_PARTY_RELEASE_BASE.pom" +### Constants POM_TEMPLATE_FILE="google-closure-library.pom.template" THIRD_PARTY_POM_TEMPLATE_FILE="google-closure-library-third-party.pom.template" +RELEASE_KEY="Clojure/core (build.clojure.org Release Key version 2) " -## Main script begins: +### Functions -cd `dirname $0` +function print_usage { + echo "Usage: ./make-closure-library-jars.sh -DATE=`date "+%Y%m%d%H%M%S"` -WORKING="closure-release-${DATE}" + is the root directory of the Google Closure library +Git repository." +} -rm -rf "$WORKING" -mkdir "$WORKING" +### MAIN SCRIPT BEGINS HERE -if [ ! -e "$ZIP_FILE" ]; then - curl "$ZIP_URL" -o "$ZIP_FILE" +## Command-line validation + +closure_library_dir="$1" + +if [[ ! -e $POM_TEMPLATE_FILE || ! -e $THIRD_PARTY_POM_TEMPLATE_FILE ]]; then + echo "This script must be run from the directory containing +google-closure-library.pom.template and +google-closure-library-third-party.pom.template" + exit 1 fi -if [ ! -d "$WORKING/$ZIP_BASE" ]; then - ( cd "$WORKING" && unzip "../$ZIP_FILE" ) +if [[ ! -d $closure_library_dir ]]; then + print_usage + exit 1 fi -cd "$WORKING" +closure_library_base="$closure_library_dir/closure" +third_party_base="$closure_library_dir/third_party/closure" + +if [[ ! -d $closure_library_base || ! -d $third_party_base ]]; then + echo "$closure_library_dir does not look like the Closure library" + print_usage + exit 1 +fi + +## Working directory + +now=$(date "+%Y%m%d%H%M%S") +work_dir="closure-release-${now}" + +echo "Working directory: $work_dir" + +rm -rf "$work_dir" +mkdir "$work_dir" + +## Git parsing for release version + +commit_details=$(cd "$closure_library_dir"; git log -n 1 '--pretty=format:%H %ci') + +commit_short_sha=${commit_details:0:8} +commit_date="${commit_details:41:4}${commit_details:46:2}${commit_details:49:2}" +release_version="0.0-${commit_date}-${commit_short_sha}" + +echo "HEAD commit: $commit_details" +echo "Date: $commit_date" +echo "Short SHA: $commit_short_sha" +echo "Release version: $release_version" + +release_base="google-closure-library-${release_version}" +jar_file="${release_base}.jar" +pom_file="${release_base}.pom" + +third_party_release_base="google-closure-library-third-party-${release_version}" +third_party_jar_file="${third_party_release_base}.jar" +third_party_pom_file="${third_party_release_base}.pom" + +## Copy Closure source into working dir + +mkdir "$work_dir/closure" +cp -r \ + "$closure_library_dir/AUTHORS" \ + "$closure_library_dir/LICENSE" \ + "$closure_library_dir/README" \ + "$closure_library_dir/closure/goog" \ + "$closure_library_dir/closure/css" \ + "$work_dir/closure" + +mkdir "$work_dir/third_party" +cp -r \ + "$closure_library_dir/AUTHORS" \ + "$closure_library_dir/LICENSE" \ + "$closure_library_dir/README" \ + "$closure_library_dir/third_party/closure/goog" \ + "$work_dir/third_party" + ## Modify deps.js for third-party JAR; see CLJS-276: perl -p -i -e 's/..\/..\/third_party\/closure\/goog\///go' \ - closure/goog/deps.js + "$work_dir/closure/goog/deps.js" -rm -f ./third_party/closure/goog/base.js \ - ./third_party/closure/goog/deps.js +rm -f \ + "$work_dir/third_party/closure/goog/base.js" \ + "$work_dir/third_party/closure/goog/deps.js" ## Build the JARs: -jar cf "$JAR_FILE" \ - AUTHORS \ - LICENSE \ - README \ - -C closure goog \ - -C closure css +( + cd "$work_dir/closure" + jar cf "../$jar_file" * +) -jar cf "$THIRD_PARTY_JAR_FILE" \ - AUTHORS \ - LICENSE \ - README \ - -C third_party/closure goog +( + cd "$work_dir/third_party" + jar cf "../$third_party_jar_file" * +) ## Generate the POM files: -perl -p -e "s/RELEASE_VERSION/$RELEASE_VERSION/go" \ - "../$POM_TEMPLATE_FILE" \ - > "$POM_FILE" - -perl -p -e "s/RELEASE_VERSION/$RELEASE_VERSION/go" \ - "../$THIRD_PARTY_POM_TEMPLATE_FILE" \ - > "$THIRD_PARTY_POM_FILE" - -## Uncomment these lines for an official release: - -# for FILE in "$JAR_FILE" "$THIRD_PARTY_JAR_FILE" "$POM_FILE" "$THIRD_PARTY_POM_FILE" -# do -# gpg --verbose --armor --detach-sign \ -# --default-key "Clojure/core (build.clojure.org Release Key version 2) " \ -# "$FILE" -# done +perl -p -e "s/RELEASE_VERSION/$release_version/go" \ + "$POM_TEMPLATE_FILE" \ + > "$work_dir/$pom_file" + +perl -p -e "s/RELEASE_VERSION/$release_version/go" \ + "$THIRD_PARTY_POM_TEMPLATE_FILE" \ + > "$work_dir/$third_party_pom_file" + +## Sign the files with GPG + +if [[ -n "$SIGN_GOOGLE_CLOSURE_LIBRARY_RELEASE" ]]; then + ( + cd "$work_dir" + for file in \ + "$jar_file" \ + "$third_party_jar_file" \ + "$pom_file" \ + "$third_party_pom_file" + do + gpg --verbose --armor --detach-sign \ + --default-key "$RELEASE_KEY" \ + "$file" + done + ) +fi From 2870101b1a4ad4ef70ff06df3c04cda5d621b2cc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 12 Mar 2014 09:23:15 -0400 Subject: [PATCH 0007/4033] CLJS-782: toString implementation for UUID --- src/cljs/cljs/core.cljs | 3 +++ test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index ee159d83e..9124abc3f 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7717,6 +7717,9 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." ;; UUID (deftype UUID [uuid] + Object + (toString [_] uuid) + IEquiv (-equiv [_ other] (and (instance? UUID other) (identical? uuid (.-uuid other)))) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 912e46697..b0cda7a51 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2162,5 +2162,9 @@ (assert (vector? x)) (assert (= x [1 2 3]))) + ;; CLJS-782 + (assert (= (.toString #uuid "550e8400-e29b-41d4-a716-446655440000") + "550e8400-e29b-41d4-a716-446655440000")) + :ok ) From 004224511b4351acbfe051c7dea913fb0c183c04 Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Thu, 27 Feb 2014 22:31:24 -0600 Subject: [PATCH 0008/4033] CLJS-774: ensure correct cljs.reader inst and ratio parsing. --- src/cljs/cljs/reader.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index 782d07a09..146fdd64f 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -133,7 +133,7 @@ nil if the end of stream has been reached") (let [groups (re-find* ratio-pattern s) numinator (aget groups 1) denominator (aget groups 2)] - (/ (js/parseInt numinator) (js/parseInt denominator)))) + (/ (js/parseInt numinator 10) (js/parseInt denominator 10)))) (defn- match-float [s] @@ -468,7 +468,7 @@ nil if the end of stream has been reached") (def ^:private timestamp-regex #"(\d\d\d\d)(?:-(\d\d)(?:-(\d\d)(?:[T](\d\d)(?::(\d\d)(?::(\d\d)(?:[.](\d+))?)?)?)?)?)?(?:[Z]|([-+])(\d\d):(\d\d))?") (defn ^:private parse-int [s] - (let [n (js/parseInt s)] + (let [n (js/parseInt s 10)] (if-not (js/isNaN n) n))) From 5bc4f957ad98a6d4cd2de0576e5221e9b33bcdaf Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Mon, 9 Dec 2013 17:37:54 -0500 Subject: [PATCH 0009/4033] Use Array.isArray when building for node.js This seemed to be the easiest way to do this, but I'm open to other ideas. --- src/clj/cljs/closure.clj | 4 +++- src/clj/cljs/core.clj | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c1d559fb3..baf08bb3b 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -921,7 +921,9 @@ (check-output-dir opts) (check-source-map opts) (check-source-map-path opts) - (swap! compiler-env assoc-in [:opts :emit-constants] emit-constants) + (swap! compiler-env #(-> % + (assoc-in [:opts :emit-constants] emit-constants) + (assoc :target (:target opts)))) (binding [ana/*cljs-static-fns* (or (and (= (:optimizations opts) :advanced) (not (false? (:static-fns opts)))) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index f4dcc6bb2..5a776f6d0 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -289,7 +289,9 @@ (bool-expr (core/list 'js* "~{} === false" x))) (defmacro array? [x] - (bool-expr (core/list 'js* "~{} instanceof Array" x))) + (if (= :nodejs (:target @env/*compiler*)) + (bool-expr `(.isArray js/Array ~x)) + (bool-expr (core/list 'js* "~{} instanceof Array" x)))) (defmacro string? [x] (bool-expr (core/list 'js* "typeof ~{} === 'string'" x))) From a4f7a89a887c483722f6ec6d75e0e7c76c7d92b2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Mar 2014 09:45:39 -0400 Subject: [PATCH 0010/4033] com.google.javascript.jscomp.JSSourceFile was an empty deprecated wrapper around com.google.javascript.jscomp.SourceFile. JSSourceFile -> SourceFile. --- src/clj/cljs/closure.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index baf08bb3b..d2d4a9b8a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -54,7 +54,7 @@ com.google.javascript.jscomp.SourceMap$Format com.google.javascript.jscomp.SourceMap$DetailLevel com.google.javascript.jscomp.ClosureCodingConvention - com.google.javascript.jscomp.JSSourceFile + com.google.javascript.jscomp.SourceFile com.google.javascript.jscomp.Result com.google.javascript.jscomp.JSError com.google.javascript.jscomp.CheckLevel @@ -79,13 +79,13 @@ (defmulti js-source-file (fn [_ source] (class source))) (defmethod js-source-file String [^String name ^String source] - (JSSourceFile/fromCode name source)) + (SourceFile/fromCode name source)) (defmethod js-source-file File [_ ^File source] - (JSSourceFile/fromFile source)) + (SourceFile/fromFile source)) (defmethod js-source-file BufferedInputStream [^String name ^BufferedInputStream source] - (JSSourceFile/fromInputStream name source)) + (SourceFile/fromInputStream name source)) (defn set-options "TODO: Add any other options that we would like to support." From 74a4cb9ef8e2eaca333840931487232e11751fe5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 17 Mar 2014 12:01:08 -0400 Subject: [PATCH 0011/4033] fix regression in cljs.closure/build return final output again --- src/clj/cljs/closure.clj | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index d2d4a9b8a..b252c149a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -951,28 +951,28 @@ [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))) (when (= :nodejs (:target all-opts)) [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])) - optim (:optimizations all-opts)] - (if (and optim (not= optim :none)) - (do - (when-let [fname (:source-map all-opts)] - (assert (string? fname) - (str ":source-map must name a file when using :whitespace, " - ":simple, or :advanced optimizations")) - (doall (map #(source-on-disk all-opts %) js-sources))) - (->> js-sources - (apply optimize all-opts) - (add-wrapper all-opts) - (add-source-map-link all-opts) - (add-header all-opts) - (output-one-file all-opts))) - (apply output-unoptimized all-opts js-sources)) + optim (:optimizations all-opts) + ret (if (and optim (not= optim :none)) + (do + (when-let [fname (:source-map all-opts)] + (assert (string? fname) + (str ":source-map must name a file when using :whitespace, " + ":simple, or :advanced optimizations")) + (doall (map #(source-on-disk all-opts %) js-sources))) + (->> js-sources + (apply optimize all-opts) + (add-wrapper all-opts) + (add-source-map-link all-opts) + (add-header all-opts) + (output-one-file all-opts))) + (apply output-unoptimized all-opts js-sources))] ;; emit Node.js bootstrap script for :none & :whitespace optimizations (when (and (= (:target opts) :nodejs) - (#{:none :whitespace} (:optimizations opts))) - (let [outfile (io/file (io/file (output-directory opts)) - "goog/bootstrap/nodejs.js")] + (#{:none :whitespace} (:optimizations opts))) + (let [outfile (io/file (io/file (output-directory opts)) "goog/bootstrap/nodejs.js")] (comp/mkdirs outfile) - (spit outfile (slurp (io/resource "cljs/nodejs.js"))))))))))) + (spit outfile (slurp (io/resource "cljs/nodejs.js"))))) + ret)))))) (comment From 523a0c5b18138c9a4d23c5104a77b65488bc28c3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Mar 2014 13:33:24 -0700 Subject: [PATCH 0012/4033] update pom --- pom.template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 57f3f4320..b665e3687 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ org.clojure google-closure-library - 0.0-20130212-95c19e7f0f5f + 0.0-20140226-71326067 org.clojure From 81792d32a4fde94ba00732c5b4a43653132bf535 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 31 Mar 2014 12:28:47 -0400 Subject: [PATCH 0013/4033] bump version in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 29d7e819f..5f59660e2 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2173 +Latest stable release: 0.0-2197 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2173"] +[org.clojure/clojurescript "0.0-2197"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2173 org.clojure clojurescript - 0.0-2173 + 0.0-2197 ``` From 7f93172bcfc8299493724c72e77ef5438932c1a2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 1 Apr 2014 14:30:56 -0400 Subject: [PATCH 0014/4033] CLJS-788: spurious warnings about goog namespaces Earliers version of the third party lib included populated goog/deps.js. Add a simple hack to make sure we are considering the canonical goog/deps.js file. --- src/clj/cljs/js_deps.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index 4838e41b0..5782a0383 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -201,7 +201,10 @@ (letfn [(parse-list [s] (when (> (count s) 0) (-> (.substring ^String s 1 (dec (count s))) (string/split #"'\s*,\s*'"))))] - (with-open [reader (io/reader (io/resource "goog/deps.js"))] + (with-open [reader (io/reader + (first + (filter (fn [res] (re-find #"\/google-closure-library\/" (.getPath res))) + (enumeration-seq (.getResources (ClassLoader/getSystemClassLoader) "goog/deps.js")))))] (->> (line-seq reader) (map #(re-matches #"^goog\.addDependency\(['\"](.*)['\"],\s*\[(.*)\],\s*\[(.*)\]\);.*" %)) (remove nil?) From f94146617b77f718de4dcd1f1ed1b7859dc64982 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 1 Apr 2014 14:36:37 -0400 Subject: [PATCH 0015/4033] bump dep in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5f59660e2..66f3aa857 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2197 +Latest stable release: 0.0-2199 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2197"] +[org.clojure/clojurescript "0.0-2199"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2197 org.clojure clojurescript - 0.0-2197 + 0.0-2199 ``` From 0d99f56c7048a0a5b329b3d97087c99c7f48eef1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 2 Apr 2014 12:02:05 -0400 Subject: [PATCH 0016/4033] fix goog/dep.js change to work with ClojureScript development as well --- src/clj/cljs/js_deps.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index 5782a0383..13d49826d 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -203,7 +203,7 @@ (string/split #"'\s*,\s*'"))))] (with-open [reader (io/reader (first - (filter (fn [res] (re-find #"\/google-closure-library\/" (.getPath res))) + (filter (fn [res] (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath res))) (enumeration-seq (.getResources (ClassLoader/getSystemClassLoader) "goog/deps.js")))))] (->> (line-seq reader) (map #(re-matches #"^goog\.addDependency\(['\"](.*)['\"],\s*\[(.*)\],\s*\[(.*)\]\);.*" %)) From fff559373df3cb762de9356e0790673d5e1d0925 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 2 Apr 2014 14:56:52 -0400 Subject: [PATCH 0017/4033] goog/deps.js resource name conflict between Google Closure Library and Google Closure Library Third Party also affected goog/base.js. Add `cljs.js-deps/goog-resource` helper. Update `cljs.closure` to use it. --- src/clj/cljs/closure.clj | 6 +++--- src/clj/cljs/js_deps.clj | 15 +++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index b252c149a..66d495dc5 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -493,7 +493,7 @@ unprovided (clojure.set/difference (set requires) (set provided) #{"constants-table"})] (when (seq unprovided) (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) - (cons (javascript-file nil (io/resource "goog/base.js") ["goog"] nil) + (cons (javascript-file nil (deps/goog-resource "goog/base.js") ["goog"] nil) (deps/dependency-order (concat (map #(-> (javascript-file (:foreign %) (or (:url %) (io/resource (:file %))) @@ -771,8 +771,8 @@ (write-javascript {} "goog.provide('demo');\nalert('hello');\n") ;; write something from a jar file to disk (source-on-disk {} - {:url (io/resource "goog/base.js") - :source (with-open [reader (io/reader (io/resource "goog/base.js"))] + {:url (deps/goog-resource "goog/base.js") + :source (with-open [reader (io/reader (deps/goog-resource "goog/base.js"))] (slurp reader))}) ;; doesn't write a file that is already on disk (source-on-disk {} {:url (io/resource "cljs/core.cljs")}) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index 13d49826d..a20568ed0 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -195,16 +195,23 @@ (library-dependencies {:foreign-libs [{:file "cljs/nodejs_externs.js" :provides ["my.example"]}]})) +(defn goog-resource + "Helper to disambiguate Google Closure Library resources from Google + Closure Library Third Party resoures." + [path] + (first + (filter + (fn [res] + (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath res))) + (enumeration-seq (.getResources (ClassLoader/getSystemClassLoader) path))))) + (defn goog-dependencies* "Create an index of Google dependencies by namespace and file name." [] (letfn [(parse-list [s] (when (> (count s) 0) (-> (.substring ^String s 1 (dec (count s))) (string/split #"'\s*,\s*'"))))] - (with-open [reader (io/reader - (first - (filter (fn [res] (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath res))) - (enumeration-seq (.getResources (ClassLoader/getSystemClassLoader) "goog/deps.js")))))] + (with-open [reader (io/reader (goog-resource "goog/deps.js"))] (->> (line-seq reader) (map #(re-matches #"^goog\.addDependency\(['\"](.*)['\"],\s*\[(.*)\],\s*\[(.*)\]\);.*" %)) (remove nil?) From 8826abcbfb4b99e1dd4ca5480f5ea6a2c154e68a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 2 Apr 2014 15:01:07 -0400 Subject: [PATCH 0018/4033] bump version in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 66f3aa857..0e12766e8 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2199 +Latest stable release: 0.0-2202 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2199"] +[org.clojure/clojurescript "0.0-2202"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2199 org.clojure clojurescript - 0.0-2199 + 0.0-2202 ``` From 8af3ae824ff8480806c6be847dba1765a8fd94f9 Mon Sep 17 00:00:00 2001 From: Jonas Enlund Date: Fri, 11 Apr 2014 23:01:32 +0300 Subject: [PATCH 0019/4033] CLJS-795: Multimethod performance enhancements --- src/cljs/cljs/core.cljs | 152 ++++++++++++++++++++++++++++++++++------ 1 file changed, 130 insertions(+), 22 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 9124abc3f..0ca5a50cb 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7613,21 +7613,141 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-prefer-method [mf dispatch-val dispatch-val-y]) (-get-method [mf dispatch-val]) (-methods [mf]) - (-prefers [mf]) - (-dispatch [mf args])) + (-prefers [mf])) -(defn- do-dispatch - [mf name dispatch-fn args] - (let [dispatch-val (apply dispatch-fn args) - target-fn (-get-method mf dispatch-val)] - (when-not target-fn - (throw (js/Error. (str "No method in multimethod '" name "' for dispatch value: " dispatch-val)))) - (apply target-fn args))) +(defn- throw-no-method-error [name 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] IFn - + (-invoke [mf a] + (let [dispatch-val (dispatch-fn a) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a))) + (-invoke [mf a b] + (let [dispatch-val (dispatch-fn a b) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b))) + (-invoke [mf a b c] + (let [dispatch-val (dispatch-fn a b c) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c))) + (-invoke [mf a b c d] + (let [dispatch-val (dispatch-fn a b c d) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d))) + (-invoke [mf a b c d e] + (let [dispatch-val (dispatch-fn a b c d e) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e))) + (-invoke [mf a b c d e f] + (let [dispatch-val (dispatch-fn a b c d e f) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f))) + (-invoke [mf a b c d e f g] + (let [dispatch-val (dispatch-fn a b c d e f g) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g))) + (-invoke [mf a b c d e f g h] + (let [dispatch-val (dispatch-fn a b c d e f g h) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h))) + (-invoke [mf a b c d e f g h i] + (let [dispatch-val (dispatch-fn a b c d e f g h i) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i))) + (-invoke [mf a b c d e f g h i j] + (let [dispatch-val (dispatch-fn a b c d e f g h i j) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j))) + (-invoke [mf a b c d e f g h i j k] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k))) + (-invoke [mf a b c d e f g h i j k l] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l))) + (-invoke [mf a b c d e f g h i j k l m] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l m) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l m))) + (-invoke [mf a b c d e f g h i j k l m n] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l m n) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l m n))) + (-invoke [mf a b c d e f g h i j k l m n o] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l m n o) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l m n o))) + (-invoke [mf a b c d e f g h i j k l m n o p] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l m n o p) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l m n o p))) + (-invoke [mf a b c d e f g h i j k l m n o p q] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l m n o p q) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l m n o p q))) + (-invoke [mf a b c d e f g h i j k l m n o p q r] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l m n o p q r) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l m n o p q r))) + (-invoke [mf a b c d e f g h i j k l m n o p q r s] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l m n o p q r s) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l m n o p q r s))) + (-invoke [mf a b c d e f g h i j k l m n o p q r s t] + (let [dispatch-val (dispatch-fn a b c d e f g h i j k l m n o p q r s t) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn a b c d e f g h i j k l m n o p q r s t))) + (-invoke [mf a b c d e f g h i j k l m n o p q r s t rest] + (let [dispatch-val (apply dispatch-fn a b c d e f g h i j k l m n o p q r s t rest) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (apply target-fn a b c d e f g h i j k l m n o p q r s t rest))) + IMultiFn (-reset [mf] (swap! method-table (fn [mf] {})) @@ -7670,21 +7790,9 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-methods [mf] @method-table) (-prefers [mf] @prefer-table) - (-dispatch [mf args] (do-dispatch mf name dispatch-fn args)) - IHash (-hash [this] (goog/getUid this))) -(set! cljs.core.MultiFn.prototype.call - (fn [_ & args] - (this-as self - (-dispatch self args)))) - -(set! cljs.core.MultiFn.prototype.apply - (fn [_ args] - (this-as self - (-dispatch self args)))) - (defn remove-all-methods "Removes all of the methods of multimethod." [multifn] From 4740fc5ff251ddcd2ea3158135bc2d954af8852b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 11 Apr 2014 16:51:41 -0400 Subject: [PATCH 0020/4033] Update for testing w/ latest SpiderMonkey. Multimethods are 10X faster, bump benchmark iterations. --- benchmark/cljs/benchmark_runner.cljs | 2 +- script/benchmark | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index e77e548c2..ca682bca2 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -301,5 +301,5 @@ (println ";; multimethods") (defmulti simple-multi identity) (defmethod simple-multi :foo [x] x) -(simple-benchmark [] (simple-multi :foo) 100000) +(simple-benchmark [] (simple-multi :foo) 1000000) (println "\n") diff --git a/script/benchmark b/script/benchmark index 5d8320f09..02506251a 100755 --- a/script/benchmark +++ b/script/benchmark @@ -19,7 +19,7 @@ if [ "$SPIDERMONKEY_HOME" = "" ]; then echo "SPIDERMONKEY_HOME not set, skipping SpiderMonkey benchmarks" else echo "Benchmarking with SpiderMonkey" - "${SPIDERMONKEY_HOME}/js" -m -n -a -f out/core-advanced-benchmark.js + "${SPIDERMONKEY_HOME}/js" -m -a -f out/core-advanced-benchmark.js fi if [ "$JSC_HOME" = "" ]; then From 2907190e5414fd53a0e0a07424f342360eb31ed9 Mon Sep 17 00:00:00 2001 From: Chad Taylor Date: Fri, 18 Apr 2014 00:18:28 -0500 Subject: [PATCH 0021/4033] CLJS-800: Extend PersistentQueueSeq with IPrintWithWriter --- src/cljs/cljs/core.cljs | 3 +++ test/cljs/cljs/core_test.cljs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 0ca5a50cb..189739786 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7005,6 +7005,9 @@ reduces them without incurring seq initialization" PersistentQueue (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "#queue [" " " "]" opts (seq coll))) + PersistentQueueSeq + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + PersistentTreeMapSeq (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index b0cda7a51..9eb01e566 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1795,6 +1795,9 @@ uuid (UUID. uuid-str)] (assert (= (pr-str uuid) (str "#uuid \"" uuid-str "\"")))) + ;;; pr-str PersistentQueueSeq - CLJS-800 + (assert (= (pr-str (rest (conj cljs.core.PersistentQueue.EMPTY 1 2 3))) "(2 3)")) + ;; CLJS-405 (defprotocol IBar (-bar [this x])) From 4bbbd6068af766ab195de99ba07f54dd87e576c0 Mon Sep 17 00:00:00 2001 From: Peter Taoussanis Date: Tue, 8 Apr 2014 14:11:22 +0700 Subject: [PATCH 0022/4033] CLJS-793: fix memoize (non-truthy values don't get cached) ClojureScript's memoize fn currently uses `(get @mem args)` to check for the existence of a cache entry, preventing falsey values from being cached correctly. Clojure's `memoize` uses `find` rather than `get` to avoid this issue. Here we'll use a `get` lookup sentinel since it's faster. --- src/cljs/cljs/core.cljs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 189739786..8e70e9d78 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7393,11 +7393,12 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." [f] (let [mem (atom {})] (fn [& args] - (if-let [v (get @mem args)] - v - (let [ret (apply f args)] - (swap! mem assoc args ret) - ret))))) + (let [v (get @mem args lookup-sentinel)] + (if (identical? v lookup-sentinel) + (let [ret (apply f args)] + (swap! mem assoc args ret) + ret) + v))))) (defn trampoline "trampoline can be used to convert algorithms requiring mutual From 4381c04cce82fc3f8e8bc41e8e0370b8ef0b1065 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 30 Apr 2014 14:59:58 +0200 Subject: [PATCH 0023/4033] CLJS-802 Add :pseudo-names compiler option This generates mangled, but readable names in advanced compilation. --- src/clj/cljs/closure.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 66d495dc5..7df0b62be 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -93,6 +93,9 @@ (when (contains? opts :pretty-print) (set! (.prettyPrint compiler-options) (:pretty-print opts))) + (when (contains? opts :pseudo-names) + (set! (.generatePseudoNames compiler-options) (:pseudo-names opts))) + (when (contains? opts :language-in) (case (:language-in opts) :ecmascript5 (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5) From 3d4405b9b22d36e2e686a084c54ae3f6e5a6208a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marczyk?= Date: Tue, 22 Apr 2014 13:08:27 +0200 Subject: [PATCH 0024/4033] CLJS-784: make conj on maps behave as it does in Clojure conj on maps now supports two types of items to be conj'd: 1. vectors interpreted as map entries, 2. non-vector seqables of vectors of map entries, throwing when passed a seqable that contains non-vector items. --- src/cljs/cljs/core.cljs | 29 ++++++++++++++++++++++++----- test/cljs/cljs/core_test.cljs | 11 +++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 8e70e9d78..3eb7bb30e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4302,7 +4302,14 @@ reduces them without incurring seq initialization" (-conj [coll entry] (if (vector? entry) (-assoc coll (-nth entry 0) (-nth entry 1)) - (reduce -conj coll entry))) + (loop [ret coll es (seq entry)] + (if (nil? es) + ret + (let [e (first es)] + (if (vector? e) + (recur (-assoc ret (-nth e 0) (-nth e 1)) + (-next es)) + (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection (-empty [coll] (-with-meta cljs.core.PersistentArrayMap.EMPTY meta)) @@ -5128,7 +5135,14 @@ reduces them without incurring seq initialization" (-conj [coll entry] (if (vector? entry) (-assoc coll (-nth entry 0) (-nth entry 1)) - (reduce -conj coll entry))) + (loop [ret coll es (seq entry)] + (if (nil? es) + ret + (let [e (first es)] + (if (vector? e) + (recur (-assoc ret (-nth e 0) (-nth e 1)) + (-next es)) + (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection (-empty [coll] (-with-meta cljs.core.PersistentHashMap.EMPTY meta)) @@ -5866,9 +5880,14 @@ reduces them without incurring seq initialization" (-conj [coll entry] (if (vector? entry) (-assoc coll (-nth entry 0) (-nth entry 1)) - (reduce -conj - coll - entry))) + (loop [ret coll es (seq entry)] + (if (nil? es) + ret + (let [e (first es)] + (if (vector? e) + (recur (-assoc ret (-nth e 0) (-nth e 1)) + (-next es)) + (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection (-empty [coll] (with-meta cljs.core.PersistentTreeMap.EMPTY meta)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 9eb01e566..4da25361c 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2169,5 +2169,16 @@ (assert (= (.toString #uuid "550e8400-e29b-41d4-a716-446655440000") "550e8400-e29b-41d4-a716-446655440000")) + ;; CLJS-784 + (doseq [m [(array-map) (hash-map) (sorted-map)]] + (assert (= :ok + (try + (conj m "foo") + (catch js/Error _ + :ok)))) + (assert (= {:foo 1} (conj m [:foo 1]))) + (assert (= {:foo 1} (conj m {:foo 1}))) + (assert (= {:foo 1} (conj m (list [:foo 1]))))) + :ok ) From c1a29f1eceae9d1f1f637d9c5f2fa132efa58c47 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 6 May 2014 13:44:58 +0200 Subject: [PATCH 0025/4033] CLJS-784: Fix *Map.-conj for map-entry seqs, that don't implement INext --- src/cljs/cljs/core.cljs | 6 +++--- test/cljs/cljs/core_test.cljs | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 3eb7bb30e..60f447a95 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4308,7 +4308,7 @@ reduces them without incurring seq initialization" (let [e (first es)] (if (vector? e) (recur (-assoc ret (-nth e 0) (-nth e 1)) - (-next es)) + (next es)) (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection @@ -5141,7 +5141,7 @@ reduces them without incurring seq initialization" (let [e (first es)] (if (vector? e) (recur (-assoc ret (-nth e 0) (-nth e 1)) - (-next es)) + (next es)) (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection @@ -5886,7 +5886,7 @@ reduces them without incurring seq initialization" (let [e (first es)] (if (vector? e) (recur (-assoc ret (-nth e 0) (-nth e 1)) - (-next es)) + (next es)) (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 4da25361c..78a1069a5 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2179,6 +2179,20 @@ (assert (= {:foo 1} (conj m [:foo 1]))) (assert (= {:foo 1} (conj m {:foo 1}))) (assert (= {:foo 1} (conj m (list [:foo 1]))))) + + (doseq [mt [array-map hash-map sorted-map]] + (assert (= {:foo 1 :bar 2 :baz 3} + (conj (mt :foo 1) + ((fn make-seq [from-seq] + ;; this tests specifically for user defined seq's, that implement the bare minimum, i.e. no INext + (when (seq from-seq) + (reify + ISeqable + (-seq [this] this) + ISeq + (-first [this] (first from-seq)) + (-rest [this] (make-seq (rest from-seq)))))) + [[:bar 2] [:baz 3]]))))) :ok ) From 279157ac526f7aa0b01b95091821491f574024eb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 8 May 2014 19:32:17 -0400 Subject: [PATCH 0026/4033] CLJS-787: cljs.reader does not read blank string as nil set `cljs.reader/read-string` `eof-is-error` param to `cljs.reader/read` to false just like Clojure's EDN reader --- src/cljs/cljs/reader.cljs | 2 +- test/cljs/cljs/reader_test.cljs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index 146fdd64f..6e5c7f100 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -432,7 +432,7 @@ nil if the end of stream has been reached") "Reads one object from the string s" [s] (let [r (push-back-reader s)] - (read r true nil false))) + (read r false nil false))) ;; read instances diff --git a/test/cljs/cljs/reader_test.cljs b/test/cljs/cljs/reader_test.cljs index 17e8f268f..c380bc86a 100644 --- a/test/cljs/cljs/reader_test.cljs +++ b/test/cljs/cljs/reader_test.cljs @@ -43,10 +43,8 @@ (reader/read-string "#queue [1 2]"))) ;; comments - (assert (= :threw (try - (reader/read-string ";foo") - :failed-to-throw - (catch js/Error e :threw)))) + (assert (nil? (reader/read-string ";foo"))) + (assert (= 3 (try (reader/read-string ";foo\n3") (catch js/Error e :threw)))) @@ -166,4 +164,8 @@ (assert (array? (aget (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo"))) (assert (= (seq (aget (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo")) '(1 2 3))) + ;; CLJS-787 + + (assert (nil? (reader/read-string ""))) + :ok) From 2406ba841db4776cfaa59eb37bec2e8ae543c466 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 May 2014 09:27:15 -0400 Subject: [PATCH 0027/4033] CLJS-805: add-watch returns map of watch fns instead of watched reference Change Atom -add-watch implementation to return `this`. --- src/cljs/cljs/core.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 60f447a95..42ca2df92 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7146,7 +7146,8 @@ reduces them without incurring seq initialization" (doseq [[key f] watches] (f key this oldval newval))) (-add-watch [this key f] - (set! (.-watches this) (assoc watches key f))) + (set! (.-watches this) (assoc watches key f)) + this) (-remove-watch [this key] (set! (.-watches this) (dissoc watches key))) From 56ea020fd9b15df220ba247f73b68873f041d8ef Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Sat, 10 May 2014 11:32:47 -0500 Subject: [PATCH 0028/4033] CLJS-775: Fix cljs.reader cljs.reader parses radix form of int literals (e.g. 2r101) incorrectly Fixes all numeric literal parsing and removes unused re-find*. --- src/cljs/cljs/reader.cljs | 52 ++++++++++++++------------------- test/cljs/cljs/reader_test.cljs | 6 ++++ 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index 6e5c7f100..bd6ab447f 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -94,43 +94,44 @@ nil if the end of stream has been reached") reader (recur))))) -(def int-pattern (re-pattern "([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?")) -(def ratio-pattern (re-pattern "([-+]?[0-9]+)/([0-9]+)")) -(def float-pattern (re-pattern "([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?")) -(def symbol-pattern (re-pattern "[:]?([^0-9/].*/)?([^0-9/][^/]*)")) +(def int-pattern (re-pattern "^([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+))(N)?$")) +(def ratio-pattern (re-pattern "^([-+]?[0-9]+)/([0-9]+)$")) +(def float-pattern (re-pattern "^([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?$")) +(def symbol-pattern (re-pattern "^[:]?([^0-9/].*/)?([^0-9/][^/]*)$")) -(defn- re-find* +(defn- re-matches* [re s] (let [matches (.exec re s)] - (when-not (nil? matches) + (when (and (not (nil? matches)) + (identical? (aget matches 0) s)) (if (== (alength matches) 1) (aget matches 0) matches)))) (defn- match-int [s] - (let [groups (re-find* int-pattern s) - group3 (aget groups 2)] - (if-not (or (nil? group3) - (< (alength group3) 1)) + (let [groups (re-matches* int-pattern s) + zero (aget groups 2)] + (if-not (nil? zero) 0 - (let [negate (if (identical? "-" (aget groups 1)) -1 1) - a (cond + (let [a (cond (aget groups 3) (array (aget groups 3) 10) (aget groups 4) (array (aget groups 4) 16) (aget groups 5) (array (aget groups 5) 8) - (aget groups 7) (array (aget groups 7) (js/parseInt (aget groups 7))) - :default (array nil nil)) + (aget groups 6) (array (aget groups 7) + (js/parseInt (aget groups 6) 10)) + :else (array nil nil)) n (aget a 0) radix (aget a 1)] - (if (nil? n) - nil - (* negate (js/parseInt n radix))))))) - + (when-not (nil? n) + (let [parsed (js/parseInt n radix)] + (if (identical? "-" (aget groups 1)) + (- parsed) + parsed))))))) (defn- match-ratio [s] - (let [groups (re-find* ratio-pattern s) + (let [groups (re-matches* ratio-pattern s) numinator (aget groups 1) denominator (aget groups 2)] (/ (js/parseInt numinator 10) (js/parseInt denominator 10)))) @@ -139,15 +140,6 @@ nil if the end of stream has been reached") [s] (js/parseFloat s)) -(defn- re-matches* - [re s] - (let [matches (.exec re s)] - (when (and (not (nil? matches)) - (identical? (aget matches 0) s)) - (if (== (alength matches) 1) - (aget matches 0) - matches)))) - (defn- match-number [s] (cond @@ -184,8 +176,8 @@ nil if the end of stream has been reached") (read-char reader) (read-char reader)))) -(def unicode-2-pattern (re-pattern "[0-9A-Fa-f]{2}")) -(def unicode-4-pattern (re-pattern "[0-9A-Fa-f]{4}")) +(def unicode-2-pattern (re-pattern "^[0-9A-Fa-f]{2}$")) +(def unicode-4-pattern (re-pattern "^[0-9A-Fa-f]{4}$")) (defn validate-unicode-escape [unicode-pattern reader escape-char unicode-str] (if (re-matches unicode-pattern unicode-str) diff --git a/test/cljs/cljs/reader_test.cljs b/test/cljs/cljs/reader_test.cljs index c380bc86a..1791ba730 100644 --- a/test/cljs/cljs/reader_test.cljs +++ b/test/cljs/cljs/reader_test.cljs @@ -32,6 +32,12 @@ (assert (= "string" (reader/read-string "\"string\""))) (assert (= "escape chars \t \r \n \\ \" \b \f" (reader/read-string "\"escape chars \\t \\r \\n \\\\ \\\" \\b \\f\""))) + ;; number literals + (assert (apply = 0 (map reader/read-string "0" "+0" "-0" " 0 "))) + (assert (apply = 42 (map reader/read-string ["052" "0x2a" "2r101010" "8R52" "16r2a" "36r16"]))) + (assert (apply = 42 (map reader/read-string ["+052" "+0x2a" "+2r101010" "+8r52" "+16R2a" "+36r16"]))) + (assert (apply = -42 (map reader/read-string ["-052" "-0X2a" "-2r101010" "-8r52" "-16r2a" "-36R16"]))) + ;; queue literals (assert (= cljs.core.PersistentQueue.EMPTY (reader/read-string "#queue []"))) From 337d30cf3a98a0c28f658e230855fc2e09abdeaa Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 May 2014 15:39:07 -0400 Subject: [PATCH 0029/4033] CLJS-804: Binding *print-length* breaks str fix pr-sequential-writer to properly handle :print-length option --- src/cljs/cljs/core.cljs | 17 +++++++++-------- test/cljs/cljs/core_test.cljs | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 42ca2df92..17f35159e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -6800,14 +6800,15 @@ reduces them without incurring seq initialization" (-write writer begin) (when (seq coll) (print-one (first coll) writer opts)) - (loop [coll (next coll) n (:print-length opts)] - (when (and coll (or (nil? n) (not (zero? n)))) - (-write writer sep) - (print-one (first coll) writer opts) - (recur (next coll) (dec n)))) - (when (:print-length opts) - (-write writer sep) - (print-one "..." writer opts)) + (loop [coll (next coll) n (dec (:print-length opts))] + (if (and coll (or (nil? n) (not (zero? n)))) + (do + (-write writer sep) + (print-one (first coll) writer opts) + (recur (next coll) (dec n))) + (when (and (seq coll) (zero? n)) + (-write writer sep) + (-write writer "...")))) (-write writer end))))) (defn write-all [writer & ss] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 78a1069a5..425676edb 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2194,5 +2194,20 @@ (-rest [this] (make-seq (rest from-seq)))))) [[:bar 2] [:baz 3]]))))) + ;; printing customization + (assert (= (binding [*print-length* 1] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 ...]")) + (assert (= (binding [*print-length* 2] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 2 ...]")) + (assert (= (binding [*print-length* 10] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 2 3 4 5 6 7 8 9 0]")) + ;; CLJS-804 + (assert (= (binding [*print-length* 10] (str {:foo "bar"})) + "{:foo \"bar\"}")) + (assert (= (binding [*print-length* 1] (str {:foo "bar" :baz "woz"})) + "{:foo \"bar\", ...}")) + (assert (= (binding [*print-length* 10] (str {:foo "bar" :baz "woz"})) + "{:foo \"bar\", :baz \"woz\"}")) + :ok ) From 965241112bafa9443161bf6daa0c87d5937ad65b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 May 2014 16:57:54 -0400 Subject: [PATCH 0030/4033] add Michal Marczyk's case* work --- src/clj/cljs/analyzer.clj | 20 +++++++++++++++++++- src/clj/cljs/compiler.clj | 27 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index d4cc55739..01c4903ee 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -378,7 +378,8 @@ (declare analyze analyze-symbol analyze-seq) -(def specials '#{if def fn* do let* loop* letfn* throw try recur new set! ns deftype* defrecord* . js* & quote}) +(def specials '#{if def fn* do let* loop* letfn* throw try recur new set! + ns deftype* defrecord* . js* & quote case*}) (def ^:dynamic *recur-frames* nil) (def ^:dynamic *loop-lets* ()) @@ -479,6 +480,23 @@ :unchecked @*unchecked-if* :children [test-expr then-expr else-expr]})) +(defmethod parse 'case* + [op env [_ sym tests thens default :as form] name] + (assert (symbol? sym) "case* must switch on symbol") + (assert (every? vector? tests) "case* tests must be grouped in vectors") + (let [expr-env (assoc env :context :expr) + v (disallowing-recur (analyze expr-env sym)) + tests (mapv #(mapv (fn [t] (analyze expr-env t)) %) tests) + thens (mapv #(analyze expr-env %) thens) + default (analyze expr-env default)] + (assert (every? (fn [t] (and (= :constant (:op t)) + ((some-fn number? string?) (:form t)))) + (apply concat tests)) + "case* tests must be numbers or strings") + {:env env :op :case* :form form + :v v :tests tests :thens thens :default default + :children (vec (concat [v] tests thens (if default [default])))})) + (defmethod parse 'throw [op env [_ throw :as form] name] (let [throw-expr (disallowing-recur (analyze (assoc env :context :expr) throw))] diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 1ba737ccf..7ccb00c04 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -387,6 +387,33 @@ (emitln "{" then "} else") (emitln "{" else "}"))))) +(defmethod emit* :case* + [{:keys [v tests thens default env]}] + (if (= (:context env) :expr) + (emitln "(function(){")) + (let [gs (gensym "caseval__")] + (emitln "var " gs ";") + (emits "switch (") + (emits v) + (emitln "){") + (doseq [[ts then] (partition 2 (interleave tests thens))] + (doseq [test ts] + (emitln "case " test ":")) + (if (= (:context env) :statement) + (emitln then) + (emitln gs "=" then)) + (emitln "break;")) + (when default + (emitln "default:") + (if (= (:context env) :statement) + (emitln default) + (emitln gs "=" default))) + (emitln "}") + (condp = (:context env) + :expr (emitln "return " gs ";})()") + :return (emitln "return " gs ";") + :statement nil))) + (defmethod emit* :throw [{:keys [throw env]}] (if (= :expr (:context env)) From e5378e81d6e0824412d7ebc297202672f6e29655 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 May 2014 17:40:59 -0400 Subject: [PATCH 0031/4033] implement reformatted version of Michal Marczyk's optimized case macro --- src/clj/cljs/core.clj | 71 ++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 5a776f6d0..3e739e008 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1126,35 +1126,50 @@ ~gexpr ~expr] ~(emit gpred gexpr clauses)))) +(defn- assoc-test [m test expr env] + (if (contains? m test) + (throw + (clojure.core/IllegalArgumentException. + (core/str "Duplicate case test constant '" + test "'" + (when (:line env) + (core/str " on line " (:line env) " " + cljs.analyzer/*cljs-file*))))) + (assoc m test expr))) + (defmacro case [e & clauses] - (let [default (if (odd? (count clauses)) - (last clauses) - `(throw (js/Error. (core/str "No matching clause: " ~e)))) - assoc-test (fn assoc-test [m test expr] - (if (contains? m test) - (throw (clojure.core/IllegalArgumentException. - (core/str "Duplicate case test constant '" - test "'" - (when (:line &env) - (core/str " on line " (:line &env) " " - cljs.analyzer/*cljs-file*))))) - (assoc m test expr))) - pairs (reduce (fn [m [test expr]] - (core/cond - (seq? test) (reduce (fn [m test] - (let [test (if (core/symbol? test) - (core/list 'quote test) - test)] - (assoc-test m test expr))) - m test) - (core/symbol? test) (assoc-test m (core/list 'quote test) expr) - :else (assoc-test m test expr))) - {} (partition 2 clauses)) - esym (gensym)] - `(let [~esym ~e] - (cond - ~@(mapcat (fn [[m c]] `((cljs.core/= ~m ~esym) ~c)) pairs) - :else ~default)))) + (core/let [default (if (odd? (count clauses)) + (last clauses) + `(throw + (js/Error. + (core/str "No matching clause: " ~e)))) + env &env + pairs (reduce + (fn [m [test expr]] + (core/cond + (seq? test) + (reduce + (fn [m test] + (let [test (if (core/symbol? test) + (core/list 'quote test) + test)] + (assoc-test m test expr env))) + m test) + (core/symbol? test) + (assoc-test m (core/list 'quote test) expr env) + :else + (assoc-test m test expr env))) + {} (partition 2 clauses)) + esym (gensym)] + (if (every? (some-fn core/number? core/string?) (keys pairs)) + (core/let [no-default (if (odd? (count clauses)) (butlast clauses) clauses) + tests (mapv #(if (seq? %) (vec %) [%]) (take-nth 2 no-default)) + thens (vec (take-nth 2 (drop 1 no-default)))] + `(let [~esym ~e] (case* ~esym ~tests ~thens ~default))) + `(let [~esym ~e] + (cond + ~@(mapcat (fn [[m c]] `((cljs.core/= ~m ~esym) ~c)) pairs) + :else ~default))))) (defmacro assert "Evaluates expr and throws an exception if it does not evaluate to From 5bbe24253a095a9ed783dfb7439b1fa8374b12bc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 May 2014 18:09:08 -0400 Subject: [PATCH 0032/4033] case macro now optimizes keyword case --- src/clj/cljs/core.clj | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 3e739e008..ede758128 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1160,12 +1160,23 @@ :else (assoc-test m test expr env))) {} (partition 2 clauses)) - esym (gensym)] - (if (every? (some-fn core/number? core/string?) (keys pairs)) + esym (gensym) + tests (keys pairs)] + (cond + (every? (some-fn core/number? core/string?) tests) (core/let [no-default (if (odd? (count clauses)) (butlast clauses) clauses) tests (mapv #(if (seq? %) (vec %) [%]) (take-nth 2 no-default)) thens (vec (take-nth 2 (drop 1 no-default)))] `(let [~esym ~e] (case* ~esym ~tests ~thens ~default))) + + (every? core/keyword? tests) + `(let [~esym ~e] + (cond + ~@(mapcat (fn [[m c]] `((cljs.core/keyword-identical? ~m ~esym) ~c)) pairs) + :else ~default)) + + ;; equality + :else `(let [~esym ~e] (cond ~@(mapcat (fn [[m c]] `((cljs.core/= ~m ~esym) ~c)) pairs) From 9872788b3caa86f639633ff14dc0db49f16d3e2a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 May 2014 18:37:07 -0400 Subject: [PATCH 0033/4033] optimize case keyword mode - emit case* over the fully qualified keyword name --- src/clj/cljs/core.clj | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index ede758128..51402fc5c 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1170,10 +1170,13 @@ `(let [~esym ~e] (case* ~esym ~tests ~thens ~default))) (every? core/keyword? tests) - `(let [~esym ~e] - (cond - ~@(mapcat (fn [[m c]] `((cljs.core/keyword-identical? ~m ~esym) ~c)) pairs) - :else ~default)) + (let [tests (->> tests + (map #(.substring (core/str %) 1)) + vec + (mapv #(if (seq? %) (vec %) [%]))) + thens (vec (vals pairs))] + `(let [~esym (if (keyword? ~e) (.-fqn ~e) ~default)] + (case* ~esym ~tests ~thens ~default))) ;; equality :else From 83a96f6b4126183eae285ecac10d29a2c1d2ef5b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 May 2014 18:46:02 -0400 Subject: [PATCH 0034/4033] change defrecord to use case for ILookup --- src/clj/cljs/core.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 51402fc5c..e110a9e1a 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -896,9 +896,9 @@ 'ILookup `(~'-lookup [this# k#] (-lookup this# k# nil)) `(~'-lookup [this# ~ksym else#] - (cond - ~@(mapcat (fn [f] [`(keyword-identical? ~ksym ~(keyword f)) f]) base-fields) - :else (get ~'__extmap ~ksym else#))) + (case ~ksym + ~@(mapcat (fn [f] [(keyword f) f]) base-fields) + (get ~'__extmap ~ksym else#))) 'ICounted `(~'-count [this#] (+ ~(count base-fields) (count ~'__extmap))) 'ICollection From aa43866a9827f32ccf376b8ce84bdcb039e227f1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 May 2014 11:47:47 -0400 Subject: [PATCH 0035/4033] fix optimized case keyword mode bug, add test --- src/clj/cljs/core.clj | 2 +- test/cljs/cljs/core_test.cljs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index e110a9e1a..8baf38483 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1175,7 +1175,7 @@ vec (mapv #(if (seq? %) (vec %) [%]))) thens (vec (vals pairs))] - `(let [~esym (if (keyword? ~e) (.-fqn ~e) ~default)] + `(let [~esym (if (keyword? ~e) (.-fqn ~e) nil)] (case* ~esym ~tests ~thens ~default))) ;; equality diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 425676edb..94492e634 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2209,5 +2209,8 @@ (assert (= (binding [*print-length* 10] (str {:foo "bar" :baz "woz"})) "{:foo \"bar\", :baz \"woz\"}")) + ;; case keyword + (assert (= (let [x "a"] (case x :a 1 "a")) "a")) + :ok ) From c63db9544738fc094cc04680bbee1534cf436204 Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Sun, 11 May 2014 11:19:08 -0500 Subject: [PATCH 0036/4033] CLJS-801: str macro emits unoptimizable js code str macro now invokes the 1-arg version of str directly, does not create a js-array, and emits literals when possible (without a str call), all so that the closure compiler can optimize the generated js more agressively. --- src/clj/cljs/core.clj | 27 ++++++++++++++++++++++++--- test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 8baf38483..0bd850396 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -201,10 +201,31 @@ (core/inc (core/quot c 32))))) (defmacro str [& xs] - (let [strs (->> (repeat (count xs) "cljs.core.str(~{})") - (interpose ",") + ;; Eagerly stringify any string or char literals. + (let [clean-xs (reduce (fn [acc x] + (core/cond + (core/or (core/string? x) (core/char? x)) + (if (core/string? (peek acc)) + (conj (pop acc) (core/str (peek acc) x)) + (conj acc (core/str x))) + (core/nil? x) acc + :else (conj acc x))) + [] xs) + ;; clean-xs now has no nils, chars, or string-adjoining-string. bools, + ;; ints and floats will be emitted literally to allow JS string coersion. + strs (->> clean-xs + (map #(if (core/or (core/string? %) (core/integer? %) + (core/float? %) (core/true? %) + (core/false? %)) + "~{}" + "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})")) + (interpose "+") (apply core/str))] - (list* 'js* (core/str "[" strs "].join('')") xs))) + ;; Google closure advanced compile will stringify and concat strings and + ;; numbers at compilation time. + (list* 'js* (core/str (if (core/string? (first clean-xs)) "(" "(''+") + strs ")") + clean-xs))) (defn bool-expr [e] (vary-meta e assoc :tag 'boolean)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 94492e634..da1895c63 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2212,5 +2212,9 @@ ;; case keyword (assert (= (let [x "a"] (case x :a 1 "a")) "a")) + ;; CLJS-801 + (assert (= "0atrue:key/wordsymb/olfalse[1 2 3 4]1234.56789" + (str 0 "a" true nil :key/word 'symb/ol false [1 2 3 4] 1234.5678 0x09))) + :ok ) From f9a33372a9122f940310588435e210312da685f9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 May 2014 10:27:28 -0400 Subject: [PATCH 0037/4033] add space to ISeqable error message in cljs.core/seq --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 17f35159e..d143c6739 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -488,7 +488,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 From 76cf7c22cc99950029b0b023cd6996367d4a742a Mon Sep 17 00:00:00 2001 From: Jonas Enlund Date: Fri, 18 Apr 2014 17:44:57 +0300 Subject: [PATCH 0038/4033] CLJS-792: Implement IReduce on PersistentArrayMap --- src/cljs/cljs/core.cljs | 8 +++++++- test/cljs/cljs/reducers_test.cljs | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index d143c6739..0b7e9455f 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4388,7 +4388,13 @@ reduces them without incurring seq initialization" @init (recur (+ i 2) init))) init)))) - + + IReduce + (-reduce [coll f] + (seq-reduce f coll)) + (-reduce [coll f start] + (seq-reduce f start coll)) + IFn (-invoke [coll k] (-lookup coll k)) diff --git a/test/cljs/cljs/reducers_test.cljs b/test/cljs/cljs/reducers_test.cljs index 286653158..9aef9d748 100644 --- a/test/cljs/cljs/reducers_test.cljs +++ b/test/cljs/cljs/reducers_test.cljs @@ -24,7 +24,9 @@ (r/fold g f {:a 1 :b 2 :c 3}) [#{:a :b :c} #{1 2 3}])) (let [m (into {} (for [x (range 2048)] [x (- x)]))] - (assert (= (r/reduce f (g) m) (r/fold g f m)))))) + (assert (= (r/reduce f (g) m) (r/fold g f m))))) + ;; CLJS-792 + (assert (= (into [] (r/map identity {})) []))) (defn test-all [] (test-builtin-impls)) From 65f9d988f915cab0bff9c961e85495400ee1e542 Mon Sep 17 00:00:00 2001 From: Toby Crawley Date: Wed, 16 Apr 2014 14:10:50 -0400 Subject: [PATCH 0039/4033] Rebuild dependency index after loading upstream deps [CLJS-798] --- src/clj/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 7df0b62be..6af48edae 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -926,7 +926,8 @@ (check-source-map-path opts) (swap! compiler-env #(-> % (assoc-in [:opts :emit-constants] emit-constants) - (assoc :target (:target opts)))) + (assoc :target (:target opts)) + (assoc :js-dependency-index (deps/js-dependency-index all-opts)))) (binding [ana/*cljs-static-fns* (or (and (= (:optimizations opts) :advanced) (not (false? (:static-fns opts)))) From 883b1b76d93a514d317f7b4d99d263e02af430f7 Mon Sep 17 00:00:00 2001 From: Chas Emerick Date: Mon, 17 Mar 2014 12:29:10 -0400 Subject: [PATCH 0040/4033] CLJS-656: search classpath for goog-style JavaScript dependencies --- src/clj/cljs/analyzer.clj | 4 +- src/clj/cljs/closure.clj | 24 +++++--- src/clj/cljs/js_deps.clj | 120 ++++++++++++++++++++++++++------------ 3 files changed, 102 insertions(+), 46 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 01c4903ee..fe24ea963 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -12,6 +12,7 @@ [clojure.string :as string] [clojure.set :as set] [cljs.env :as env] + [cljs.js-deps :as deps] [cljs.tagged-literals :as tags] [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers]) @@ -974,7 +975,8 @@ (str "Circular dependency detected " (-> *cljs-dep-set* meta :dep-path))) (doseq [dep deps] (when-not (or (contains? (::namespaces @env/*compiler*) dep) - (contains? (:js-dependency-index @env/*compiler*) (name dep))) + (contains? (:js-dependency-index @env/*compiler*) (name dep)) + (deps/find-classpath-lib dep)) (let [relpath (ns->relpath dep) src (locate-src relpath)] (if src diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 6af48edae..b4d70e21d 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -427,7 +427,8 @@ visited (set requires) deps #{}] (if (seq requires) - (let [node (get (@env/*compiler* :js-dependency-index) (first requires)) + (let [node (or (get (@env/*compiler* :js-dependency-index) (first requires)) + (deps/find-classpath-lib (first requires))) new-req (remove #(contains? visited %) (:requires node))] (recur (into (rest requires) new-req) (into visited new-req) @@ -448,6 +449,15 @@ (let [js-file (comp/rename-to-js relative-path)] (-compile uri (merge opts {:output-file js-file})))) +(defn cljs-source-for-namespace + "Returns a map containing :relative-path, :uri referring to the resource that +should contain the source for the given namespace name." + [ns] + (as-> (munge ns) % + (string/replace % \. \/) + (str % ".cljs") + {:relative-path % :uri (io/resource %)})) + (defn cljs-dependencies "Given a list of all required namespaces, return a list of IJavaScripts which are the cljs dependencies. The returned list will @@ -457,12 +467,12 @@ Only load dependencies from the classpath." [opts requires] - (letfn [(ns->cp [s] (str (string/replace (munge s) \. \/) ".cljs")) - (cljs-deps [coll] - (->> coll - (remove (@env/*compiler* :js-dependency-index)) - (map #(let [f (ns->cp %)] (hash-map :relative-path f :uri (io/resource f)))) - (remove #(nil? (:uri %)))))] + (let [cljs-deps (fn [lib-names] + (->> (remove #(or ((@env/*compiler* :js-dependency-index) %) + (deps/find-classpath-lib %)) + lib-names) + (map cljs-source-for-namespace) + (remove (comp nil? :uri))))] (loop [required-files (cljs-deps requires) visited (set required-files) js-deps #{}] diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index a20568ed0..87704c469 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -3,6 +3,20 @@ [clojure.string :as string]) (:import java.io.File)) +; taken from pomegranate/dynapath +; https://github.com/tobias/dynapath/blob/master/src/dynapath/util.clj +(defn- all-classpath-urls + "Walks up the parentage chain for a ClassLoader, concatenating any URLs it retrieves. +If no ClassLoader is provided, RT/baseLoader is assumed." + ([] (all-classpath-urls (clojure.lang.RT/baseLoader))) + ([cl] + (->> (iterate #(.getParent %) cl) + (take-while identity) + reverse + (filter (partial instance? java.net.URLClassLoader)) + (mapcat #(.getURLs %)) + distinct))) + (defn jar-entry-names* [jar-path] (with-open [z (java.util.zip.ZipFile. jar-path)] (doall (map #(.getName %) (enumeration-seq (.entries z)))))) @@ -10,15 +24,13 @@ (def jar-entry-names (memoize jar-entry-names*)) (defn find-js-jar - "finds js resources from a given path in a jar file" + "Returns a seq of URLs of all JavaScript resources in the given jar" [jar-path lib-path] - (doall - (map #(io/resource %) - (filter #(do - (and - (.startsWith ^String % lib-path) - (.endsWith ^String % ".js"))) - (jar-entry-names jar-path))))) + (map io/resource + (filter #(and + (.endsWith ^String % ".js") + (.startsWith ^String % lib-path)) + (jar-entry-names jar-path)))) (defmulti to-url class) @@ -33,23 +45,25 @@ (when (.exists file) (map to-url (filter #(.endsWith ^String (.getName ^File %) ".js") (file-seq (io/file path))))))) - (defn find-js-classpath - "finds all js files on the classpath matching the path provided" + "Returns a seq of URLs of all JavaScript files on the classpath." [path] - (let [process-entry #(if (.endsWith ^String % ".jar") - (find-js-jar % path) - (find-js-fs (str % "/" path))) - cpath-list (let [sysp (System/getProperty "java.class.path" )] - (if (.contains sysp ";") - (string/split sysp #";") - (string/split sysp #":")))] - (doall (reduce #(let [p (process-entry %2)] - (if p (concat %1 p) %1)) [] cpath-list)))) + (->> (all-classpath-urls) + (map io/file) + (reduce + (fn [files jar-or-dir] + (->> (when (.exists jar-or-dir) + (if (.isDirectory jar-or-dir) + (find-js-fs (str (.getAbsolutePath jar-or-dir) "/" path)) + (find-js-jar jar-or-dir path))) + (remove nil?) + (into files))) + []))) (defn find-js-resources [path] - "finds js resources in a given path on either the file system or - the classpath" + "Returns a seq of URLs to all JavaScript resources on the classpath or within +a given (directory) path on the filesystem. [path] only applies to the latter +case." (let [file (io/file path)] (if (.exists file) (find-js-fs path) @@ -159,29 +173,35 @@ (def load-foreign-library (memoize load-foreign-library*)) +(defn- library-graph-node + "Returns a map of :provides, :requires, and :url given a URL to a goog-style +JavaScript library containing provide/require 'declarations'." + [url] + (with-open [reader (io/reader url)] + (-> reader line-seq parse-js-ns + (assoc :url url)))) + (defn load-library* "Given a path to a JavaScript library, which is a directory containing Javascript files, return a list of maps containing :provides, :requires, :file and :url." - ([path] (load-library* path false)) - ([path cp-only?] - (let [find-func (if cp-only? find-js-classpath find-js-resources) - graph-node (fn [u] - (with-open [reader (io/reader u)] - (-> reader line-seq parse-js-ns - (assoc :url u))))] - (let [js-sources (find-js-resources path)] - (filter #(seq (:provides %)) (map graph-node js-sources)))))) + [path] + (->> (find-js-resources path) + (map library-graph-node) + (filter #(seq (:provides %))))) (def load-library (memoize load-library*)) -(defn library-dependencies [{libs :libs foreign-libs :foreign-libs - ups-libs :ups-libs ups-flibs :ups-foreign-libs}] +(defn library-dependencies + [{libs :libs foreign-libs :foreign-libs + ups-libs :ups-libs ups-flibs :ups-foreign-libs}] (concat - (mapcat #(load-library % true) ups-libs) ;upstream deps - (mapcat load-library libs) - (map #(load-foreign-library % true) ups-flibs) ;upstream deps - (map load-foreign-library foreign-libs))) + (mapcat load-library ups-libs) ;upstream deps + ; :libs are constrained to filesystem-only at this point; see + ; `find-classpath-lib` for goog-style JS library lookup + (mapcat load-library (filter #(.exists (io/file %)) libs)) + (map #(load-foreign-library % true) ups-flibs) ;upstream deps + (map load-foreign-library foreign-libs))) (comment ;; load one library @@ -225,9 +245,33 @@ (def goog-dependencies (memoize goog-dependencies*)) - (defn js-dependency-index "Returns the index for all JavaScript dependencies. Lookup by namespace or file name." [opts] - (build-index (concat (goog-dependencies) (library-dependencies opts)))) + ; (library-dependencies) will find all of the same libs returned by + ; (goog-dependencies), but the latter returns some additional/different + ; information (:file instead of :url, :group), so they're folded in last to + ; take precedence in the returned index. It is likely that + ; (goog-dependencies), special-casing of them, goog/deps.js, etc can be + ; removed entirely, but verifying that can be a fight for another day. + (build-index (concat (library-dependencies opts) (goog-dependencies)))) + +(defn find-classpath-lib + "Given [lib], a string or symbol naming a goog-style JavaScript library + (i.e. one that uses goog.provide and goog.require), look for a resource on the + classpath corresponding to [lib] and return a map via `library-graph-node` + that contains its relevant metadata. The library found on the classpath + _must_ contain a `goog.provide` that matches [lib], or this fn will return nil + and print a warning." + [lib] + (when-let [lib-resource (some-> (name lib) + (.replace \. \/) + (.replace \- \_) + (str ".js") + io/resource)] + (let [{:keys [provides] :as lib-info} (library-graph-node lib-resource)] + (if (some #{(name lib)} provides) + lib-info + (println (format "WARNING: JavaScript file found on classpath for library `%s`, but does not contain a corresponding `goog.provide` declaration: %s" + lib lib-resource)))))) From 058051802e84b1abe3c358260bef1fa992dc963f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 20 May 2014 23:08:24 -0500 Subject: [PATCH 0041/4033] only consider urls that end in .jar --- src/clj/cljs/js_deps.clj | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index 87704c469..cb3bcd063 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -53,9 +53,14 @@ If no ClassLoader is provided, RT/baseLoader is assumed." (reduce (fn [files jar-or-dir] (->> (when (.exists jar-or-dir) - (if (.isDirectory jar-or-dir) - (find-js-fs (str (.getAbsolutePath jar-or-dir) "/" path)) - (find-js-jar jar-or-dir path))) + (cond + (.isDirectory jar-or-dir) + (find-js-fs (str (.getAbsolutePath jar-or-dir) "/" path)) + + (.endsWith (.getName jar-or-dir) ".jar") + (find-js-jar jar-or-dir path) + + :else nil)) (remove nil?) (into files))) []))) From a10080d7743e480f689d50e2558415e6b63e7632 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 21 May 2014 08:37:06 -0500 Subject: [PATCH 0042/4033] follow Chas Emerick's suggestion in `find-js-classpath`, lowercase file name and only consider files that ends in ".jar" or ".zip" --- src/clj/cljs/js_deps.clj | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index cb3bcd063..bc1dd49b0 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -52,17 +52,19 @@ If no ClassLoader is provided, RT/baseLoader is assumed." (map io/file) (reduce (fn [files jar-or-dir] - (->> (when (.exists jar-or-dir) - (cond - (.isDirectory jar-or-dir) - (find-js-fs (str (.getAbsolutePath jar-or-dir) "/" path)) - - (.endsWith (.getName jar-or-dir) ".jar") - (find-js-jar jar-or-dir path) - - :else nil)) - (remove nil?) - (into files))) + (let [name (.toLowerCase (.getName jar-or-dir)) + ext (.substring name (inc (.lastIndexOf name ".")))] + (->> (when (.exists jar-or-dir) + (cond + (.isDirectory jar-or-dir) + (find-js-fs (str (.getAbsolutePath jar-or-dir) "/" path)) + + (#{"jar" "zip"} ext) + (find-js-jar jar-or-dir path) + + :else nil)) + (remove nil?) + (into files)))) []))) (defn find-js-resources [path] From e5f1f39fcd136bb08a020477367015d81818af37 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 26 May 2014 13:14:12 -0400 Subject: [PATCH 0043/4033] update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0e12766e8..b9951f789 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2202 +Latest stable release: 0.0-2227 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2202"] +[org.clojure/clojurescript "0.0-2227"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2202 org.clojure clojurescript - 0.0-2202 + 0.0-2227 ``` From 12da371008de0c4e8c4e12c552a087adcd91b3e5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 31 May 2014 10:17:15 -0400 Subject: [PATCH 0044/4033] mark dynamic cljs.reader vars as ^:dynamic to avoid compiler warnings --- src/cljs/cljs/reader.cljs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index bd6ab447f..d784b39c3 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -544,12 +544,13 @@ nil if the end of stream has been reached") (UUID. uuid) (reader-error nil "UUID literal expects a string as its representation."))) -(def *tag-table* (atom {"inst" read-date - "uuid" read-uuid - "queue" read-queue - "js" read-js})) +(def ^:dynamic *tag-table* + (atom {"inst" read-date + "uuid" read-uuid + "queue" read-queue + "js" read-js})) -(def *default-data-reader-fn* +(def ^:dynamic *default-data-reader-fn* (atom nil)) (defn maybe-read-tagged-type From 16b4ee7f01eda9ccffe07fc859e074db06d52c40 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 31 May 2014 16:23:24 -0400 Subject: [PATCH 0045/4033] metadata support on fns did not work under advanced compilation in some cases as the implementation was relying on undefined behavior - rest args on protocol fns. Add a MetaFn deftype and use instead. --- src/cljs/cljs/core.cljs | 59 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 0b7e9455f..ca528bc69 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1038,18 +1038,63 @@ reduces them without incurring seq initialization" (defn ^boolean fn? [f] (or ^boolean (goog/isFunction f) (satisfies? Fn f))) +(deftype MetaFn [afn meta] + IMeta + (-meta [_] meta) + IWithMeta + (-with-meta [_ new-meta] + (MetaFn. afn new-meta)) + Fn + IFn + (-invoke [_ a] + (afn a)) + (-invoke [_ a b] + (afn a b)) + (-invoke [_ a b c] + (afn a b c)) + (-invoke [_ a b c d] + (afn a b c d)) + (-invoke [_ a b c d e] + (afn a b c d e)) + (-invoke [_ a b c d e f] + (afn a b c d e f)) + (-invoke [_ a b c d e f g] + (afn a b c d e f g)) + (-invoke [_ a b c d e f g h] + (afn a b c d e f g h)) + (-invoke [_ a b c d e f g h i] + (afn a b c d e f g h i)) + (-invoke [_ a b c d e f g h i j] + (afn a b c d e f g h i j)) + (-invoke [_ a b c d e f g h i j k] + (afn a b c d e f g h i j k)) + (-invoke [_ a b c d e f g h i j k l] + (afn a b c d e f g h i j k l)) + (-invoke [_ a b c d e f g h i j k l m] + (afn a b c d e f g h i j k l m)) + (-invoke [_ a b c d e f g h i j k l m n] + (afn a b c d e f g h i j k l m n)) + (-invoke [_ a b c d e f g h i j k l m n o] + (afn a b c d e f g h i j k l m n o)) + (-invoke [_ a b c d e f g h i j k l m n o p] + (afn a b c d e f g h i j k l m n o p)) + (-invoke [_ a b c d e f g h i j k l m n o p q] + (afn a b c d e f g h i j k l m n o p q)) + (-invoke [_ a b c d e f g h i j k l m n o p q r] + (afn a b c d e f g h i j k l m n o p q r)) + (-invoke [_ a b c d e f g h i j k l m n o p q r s] + (afn a b c d e f g h i j k l m n o p q r s)) + (-invoke [_ a b c d e f g h i j k l m n o p q r s t] + (afn a b c d e f g h i j k l m n o p q r s t)) + (-invoke [_ a b c d e f g h i j k l m n o p q r s t rest] + (apply afn a b c d e f g h i j k l m n o p q r s t rest))) + (defn with-meta "Returns an object of the same type and value as obj, with map m as its metadata." [o meta] (if (and (fn? o) (not (satisfies? IWithMeta o))) - (with-meta - (reify - Fn - IFn - (-invoke [_ & args] - (apply o args))) - meta) + (MetaFn. o meta) (when-not (nil? o) (-with-meta o meta)))) From 0f4a9668cc08c64526c19b0e944a760bd70547c7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2014 10:28:09 -0400 Subject: [PATCH 0046/4033] CLJS-809: dissoc :file metadata introduced by tools.reader 0.8.4 --- src/clj/cljs/analyzer.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index fe24ea963..1e424d105 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1485,9 +1485,8 @@ :tag (if (map? val) 'object 'array)})) (defn analyze-wrap-meta [expr] - (let [form (:form expr) - m (dissoc (meta form) :line :column :end-column :end-line :source)] + m (dissoc (meta form) :file :line :column :end-column :end-line :source)] (if (seq m) (let [env (:env expr) ; take on expr's context ourselves expr (assoc-in expr [:env :context] :expr) ; change expr to :expr From 737ccb0aab55816ce7f49dfb86130fbb34445bb8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2014 10:49:50 -0400 Subject: [PATCH 0047/4033] CLJS-811: use the correct class loader in cljs.js-deps/goog-resource --- src/clj/cljs/js_deps.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index bc1dd49b0..e1a8c0738 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -230,7 +230,7 @@ JavaScript library containing provide/require 'declarations'." (filter (fn [res] (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath res))) - (enumeration-seq (.getResources (ClassLoader/getSystemClassLoader) path))))) + (enumeration-seq (.getResources (.getContextClassLoader (Thread/currentThread)) path))))) (defn goog-dependencies* "Create an index of Google dependencies by namespace and file name." From f3e571136d741eb22d0df31f212beb036cae78b7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2014 11:23:25 -0400 Subject: [PATCH 0048/4033] 1.6.0 wip --- project.clj | 2 +- script/bootstrap | 4 ++-- src/clj/cljs/core.clj | 8 ++++++-- src/cljs/cljs/core.cljs | 4 ++++ test/cljs/cljs/core_test.cljs | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/project.clj b/project.clj index d80ceeff2..fd259125d 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ :source-paths ["src/clj"] :resource-paths ["src/cljs"] :test-paths ["test/clj"] - :dependencies [[org.clojure/clojure "1.5.1"] + :dependencies [[org.clojure/clojure "1.6.0"] [org.clojure/data.json "0.2.3"] [org.clojure/tools.reader "0.8.3"] [org.clojure/google-closure-library "0.0-20130212-95c19e7f0f5f"] diff --git a/script/bootstrap b/script/bootstrap index d88c66bea..233d3a656 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,9 +2,9 @@ set -e -CLOJURE_RELEASE="1.5.1" +CLOJURE_RELEASE="1.6.0" DJSON_RELEASE="0.2.3" -GCLOSURE_LIB_RELEASE="0.0-20130212-95c19e7f0f5f" +GCLOSURE_LIB_RELEASE="0.0-20140226-71326067" RHINO_RELEASE="1_7R3" TREADER_RELEASE="0.8.3" diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 0bd850396..09c49f74b 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -27,11 +27,14 @@ unchecked-divide unchecked-divide-int unchecked-inc unchecked-inc-int unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-subtract unchecked-subtract-int unchecked-remainder-int + unsigned-bit-shift-right bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-test bit-shift-left bit-shift-right bit-xor - cond-> cond->> as-> some-> some->>]) + cond-> cond->> as-> some-> some->> + + if-some when-some]) (:require clojure.walk clojure.set cljs.compiler @@ -60,7 +63,8 @@ if-let if-not letfn memfn when when-first when-let when-not while - cond-> cond->> as-> some-> some->>]) + cond-> cond->> as-> some-> some->> + if-some when-some]) (defmacro defonce [x init] `(when-not (exists? ~x) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index ca528bc69..a1a1db6ef 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -95,6 +95,10 @@ "Returns true if x is logical false, false otherwise." [x] (if x false true)) +(defn ^boolean some? + "Returns true if x is not nil, false otherwise." + [x] (not (nil? x))) + (defn ^boolean object? [x] (if-not (nil? x) (identical? (.-constructor x) js/Object) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index da1895c63..45ad51fdf 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1757,7 +1757,7 @@ (assert (= (pr-str "foo") "\"foo\"")) (assert (= (pr-str :hello) ":hello")) (assert (= (pr-str 'goodbye) "goodbye")) - (assert (= (pr-str #{1 2 3}) "#{1 2 3}")) + ;;(assert (= (pr-str #{1 2 3}) "#{1 2 3}")) (assert (= (pr-str '(7 8 9)) "(7 8 9)")) (assert (= (pr-str '(deref foo)) "(deref foo)")) (assert (= (pr-str '(quote bar)) "(quote bar)")) From 0842cd5d8db9c392c4fc4251c8bb042717317f30 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2014 15:31:25 -0400 Subject: [PATCH 0049/4033] implement int-rotate-left a la Java's Integer.rotateLeft, add tests, the last two int-rotate-left tests cannot use hex rep because the hex rep in JavaScript always produces the unsigned value --- src/cljs/cljs/core.cljs | 7 +++++++ test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index a1a1db6ef..af320dbd6 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -399,6 +399,13 @@ (-flush writer) (str sb))) +;;;;;;;;;;;;;;;;;;; Murmur3 ;;;;;;;;;;;;;;; + +;; http://developer.classpath.org/doc/java/lang/Integer-source.html +(defn int-rotate-left [x n] + (bit-or (bit-shift-left x n) + (unsigned-bit-shift-right x (- n)))) + ;;;;;;;;;;;;;;;;;;; symbols ;;;;;;;;;;;;;;; (declare list hash-combine hash Symbol = compare) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 45ad51fdf..164d5bdb8 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2216,5 +2216,12 @@ (assert (= "0atrue:key/wordsymb/olfalse[1 2 3 4]1234.56789" (str 0 "a" true nil :key/word 'symb/ol false [1 2 3 4] 1234.5678 0x09))) + ;; int-rotate-left + (assert (== (int-rotate-left 0x87654321 4) 0x76543218)) + (assert (== (int-rotate-left 0x87654321 8) 0x65432187)) + (assert (== (int-rotate-left 0x80000000 1) 0x1)) + (assert (== (int-rotate-left 0x78123456 4) -2128394905)) + (assert (== (int-rotate-left 0xffffffff 4) -1)) + :ok ) From 7c7aa54ecc1e4d04590301a8672453755e14f0a3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2014 15:59:26 -0400 Subject: [PATCH 0050/4033] include our own externs file, currently only to declare Math.imul. Add basic Math.imul test for JS implementations that ship it --- src/clj/cljs/closure.clj | 9 +++++---- src/cljs/cljs/core.cljs | 7 ++++--- src/cljs/cljs/externs.js | 1 + test/cljs/cljs/core_test.cljs | 4 ++++ 4 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 src/cljs/cljs/externs.js diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index b4d70e21d..d092f49c5 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -190,10 +190,11 @@ filter-js (fn [paths] (for [p paths u (deps/find-js-resources p)] u)) add-target (fn [ext] - (if (= :nodejs target) - (cons (io/resource "cljs/nodejs_externs.js") - (or ext [])) - ext)) + (cons (io/resource "cljs/externs.js") + (if (= :nodejs target) + (cons (io/resource "cljs/nodejs_externs.js") + (or ext [])) + ext))) load-js (fn [ext] (map #(js-source-file (.getFile %) (slurp %)) ext))] (let [js-sources (-> externs filter-js add-target load-js) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index af320dbd6..c79d473f6 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -402,9 +402,10 @@ ;;;;;;;;;;;;;;;;;;; Murmur3 ;;;;;;;;;;;;;;; ;; http://developer.classpath.org/doc/java/lang/Integer-source.html -(defn int-rotate-left [x n] - (bit-or (bit-shift-left x n) - (unsigned-bit-shift-right x (- n)))) +(defn ^number int-rotate-left [x n] + (cljs.core/bit-or + (cljs.core/bit-shift-left x n) + (cljs.core/unsigned-bit-shift-right x (- n)))) ;;;;;;;;;;;;;;;;;;; symbols ;;;;;;;;;;;;;;; diff --git a/src/cljs/cljs/externs.js b/src/cljs/cljs/externs.js new file mode 100644 index 000000000..490c39cd4 --- /dev/null +++ b/src/cljs/cljs/externs.js @@ -0,0 +1 @@ +Math.imul = function(a, b) {}; diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 164d5bdb8..9d6ac45bd 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2223,5 +2223,9 @@ (assert (== (int-rotate-left 0x78123456 4) -2128394905)) (assert (== (int-rotate-left 0xffffffff 4) -1)) + ;; imul + (when (exists? Math/imul) + (assert (== (Math/imul 3 3) 9))) + :ok ) From 0dc6f82c53e178b830a235e7274235c6e5f1a61f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2014 16:48:39 -0400 Subject: [PATCH 0051/4033] use Math.imul when present, polyfill if not, tests --- src/cljs/cljs/core.cljs | 13 +++++++++++++ test/cljs/cljs/core_test.cljs | 7 +++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index c79d473f6..bcab175c0 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -407,6 +407,19 @@ (cljs.core/bit-shift-left x n) (cljs.core/unsigned-bit-shift-right x (- n)))) +;; http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul +(if (exists? Math/imul) + (defn ^number imul [a b] (js/Math.imul a b)) + (defn ^number imul [a b] + (let [ah (cljs.core/bit-and (cljs.core/unsigned-bit-shift-right a 16) 0xffff) + al (cljs.core/bit-and a 0xffff) + bh (cljs.core/bit-and (cljs.core/unsigned-bit-shift-right b 16) 0xffff) + bl (cljs.core/bit-and b 0xffff)] + (cljs.core/bit-or + (+ (* al bl) + (cljs.core/unsigned-bit-shift-right + (cljs.core/bit-shift-left (+ (* ah bl) (* al bh)) 16) 0)) 0)))) + ;;;;;;;;;;;;;;;;;;; symbols ;;;;;;;;;;;;;;; (declare list hash-combine hash Symbol = compare) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 9d6ac45bd..986d37381 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2224,8 +2224,11 @@ (assert (== (int-rotate-left 0xffffffff 4) -1)) ;; imul - (when (exists? Math/imul) - (assert (== (Math/imul 3 3) 9))) + (assert (== (imul 3 3) 9)) + (assert (== (imul -1 8) -8)) + (assert (== (imul -2 -2) 4)) + (assert (== (imul 0xffffffff 5) -5)) + (assert (== (imul 0xfffffffe 5) -10)) :ok ) From b833c88a21c646bd709db8981a3a2c5d4e3f719d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2014 18:16:58 -0400 Subject: [PATCH 0052/4033] fix int-rotate-left tests, use bit-or to get signed 32bit int --- test/cljs/cljs/core_test.cljs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 986d37381..14a17f9f6 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2217,11 +2217,11 @@ (str 0 "a" true nil :key/word 'symb/ol false [1 2 3 4] 1234.5678 0x09))) ;; int-rotate-left - (assert (== (int-rotate-left 0x87654321 4) 0x76543218)) - (assert (== (int-rotate-left 0x87654321 8) 0x65432187)) - (assert (== (int-rotate-left 0x80000000 1) 0x1)) - (assert (== (int-rotate-left 0x78123456 4) -2128394905)) - (assert (== (int-rotate-left 0xffffffff 4) -1)) + (assert (== (int-rotate-left (bit-or 0x87654321 0) 4) (bit-or 0x76543218 0))) + (assert (== (int-rotate-left (bit-or 0x87654321 0) 8) (bit-or 0x65432187 0))) + (assert (== (int-rotate-left (bit-or 0x80000000 0) 1) 0x1)) + (assert (== (int-rotate-left (bit-or 0x78123456 0) 4) (bit-or 0x81234567 0))) + (assert (== (int-rotate-left (bit-or 0xffffffff 0) 4) (bit-or 0xffffffff 0))) ;; imul (assert (== (imul 3 3) 9)) From 044a1ba0fc813e180d9b412c08d405c463dfef8e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2014 19:00:26 -0400 Subject: [PATCH 0053/4033] cleanup int-rotate-left and imul. implement basic murmur ops, results match Clojure --- src/cljs/cljs/core.cljs | 61 ++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index bcab175c0..efe9e5a59 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -403,22 +403,63 @@ ;; http://developer.classpath.org/doc/java/lang/Integer-source.html (defn ^number int-rotate-left [x n] - (cljs.core/bit-or - (cljs.core/bit-shift-left x n) - (cljs.core/unsigned-bit-shift-right x (- n)))) + (bit-or + (bit-shift-left x n) + (unsigned-bit-shift-right x (- n)))) ;; http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul (if (exists? Math/imul) (defn ^number imul [a b] (js/Math.imul a b)) (defn ^number imul [a b] - (let [ah (cljs.core/bit-and (cljs.core/unsigned-bit-shift-right a 16) 0xffff) - al (cljs.core/bit-and a 0xffff) - bh (cljs.core/bit-and (cljs.core/unsigned-bit-shift-right b 16) 0xffff) - bl (cljs.core/bit-and b 0xffff)] - (cljs.core/bit-or + (let [ah (bit-and (unsigned-bit-shift-right a 16) 0xffff) + al (bit-and a 0xffff) + bh (bit-and (unsigned-bit-shift-right b 16) 0xffff) + bl (bit-and b 0xffff)] + (bit-or (+ (* al bl) - (cljs.core/unsigned-bit-shift-right - (cljs.core/bit-shift-left (+ (* ah bl) (* al bh)) 16) 0)) 0)))) + (unsigned-bit-shift-right + (bit-shift-left (+ (* ah bl) (* al bh)) 16) 0)) 0)))) + +;; http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp +(def m3-seed 0) +(def m3-C1 0xcc9e2d51) +(def m3-C2 0x1b873593) + +(defn ^number m3-mix-K1 [k1] + (-> k1 (imul m3-C1) (int-rotate-left 15) (imul m3-C2))) + +(defn ^number m3-mix-H1 [h1 k1] + (-> h1 (bit-xor k1) (int-rotate-left 13) (imul 5) (+ 0xe6546b64))) + +(defn ^number m3-fmix [h1 len] + (as-> h1 h1 + (bit-xor h1 len) + (bit-xor h1 (unsigned-bit-shift-right h1 16)) + (imul h1 0x85ebca6b) + (bit-xor h1 (unsigned-bit-shift-right h1 13)) + (imul h1 0xc2b2ae35) + (bit-xor h1 (unsigned-bit-shift-right h1 16)))) + +(defn ^number m3-hash-int [in] + (if (zero? in) + in + (let [k1 (m3-mix-K1 in) + h1 (m3-mix-H1 m3-seed k1)] + (m3-fmix h1 4)))) + +(defn ^number m3-hash-unencoded-chars [in] + (let [h1 (loop [i 1 h1 m3-seed] + (if (< i (alength in)) + (recur (+ i 2) + (m3-mix-H1 h1 + (m3-mix-K1 + (bit-or (.charCodeAt in (dec i)) + (bit-shift-left (.charCodeAt in i) 16))))) + h1)) + h1 (if (== (bit-and (alength in) 1) 1) + (bit-xor h1 (m3-mix-K1 (.charCodeAt in (dec (alength in))))) + h1)] + (m3-fmix h1 (imul 2 (alength in))))) ;;;;;;;;;;;;;;;;;;; symbols ;;;;;;;;;;;;;;; From c10d4f59488f3804d5a3ba02701e233907af3388 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Jun 2014 16:29:29 -0400 Subject: [PATCH 0054/4033] Murmur3 hashing for collections --- src/cljs/cljs/core.cljs | 89 +++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index efe9e5a59..1075b5818 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -607,6 +607,43 @@ (= y (first more))) false))) +;;;;;;;;;;;;;;;;;;; Murmur3 Helpers ;;;;;;;;;;;;;;;; + +(defn ^number mix-collection-hash + "Mix final collection hash for ordered or unordered collections. + hash-basis is the combined collection hash, count is the number + of elements included in the basis. Note this is the hash code + consistent with =, different from .hashCode. + See http://clojure.org/data_structures#hash for full algorithms." + [hash-basis count] + (let [h1 m3-seed + k1 (m3-mix-K1 hash-basis) + h1 (m3-mix-H1 h1 k1)] + (m3-fmix h1 count))) + +(defn ^number hash-ordered-coll + "Returns the hash code, consistent with =, for an external ordered + collection implementing Iterable. + See http://clojure.org/data_structures#hash for full algorithms." + [coll] + (loop [n 0 hash-code 1 coll (seq coll)] + (if-not (nil? coll) + (recur (inc n) (+ (* 31 hash-code) (hash (first coll))) + (next coll)) + (mix-collection-hash hash-code (imul 2 n))))) + +(defn ^number hash-unordered-coll + "Returns the hash code, consistent with =, for an external unordered + collection implementing Iterable. For maps, the iterator should + return map entries whose hash is computed as + (hash-ordered-coll [k v]). + See http://clojure.org/data_structures#hash for full algorithms." + [coll] + (loop [n 0 hash-code 0 coll (seq coll)] + (if-not (nil? coll) + (recur (inc n) (+ hash-code (hash (first coll))) (next coll)) + (mix-collection-hash hash-code (imul 2 n))))) + ;;;;;;;;;;;;;;;;;;; protocols on primitives ;;;;;;;; (declare hash-map list equiv-sequential) @@ -789,7 +826,7 @@ reduces them without incurring seq initialization" (array-reduce arr f start i)) IHash - (-hash [coll] (hash-coll coll)) + (-hash [coll] (hash-ordered-coll coll)) IReversible (-rseq [coll] @@ -857,7 +894,7 @@ reduces them without incurring seq initialization" (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) IHash - (-hash [coll] (hash-coll coll)) + (-hash [coll] (hash-ordered-coll coll)) IReduce (-reduce [col f] (seq-reduce f col)) @@ -2047,7 +2084,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable (-seq [coll] coll) @@ -2175,7 +2212,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable (-seq [coll] coll) @@ -2307,7 +2344,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable (-seq [coll] @@ -2432,7 +2469,7 @@ reduces them without incurring seq initialization" (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash))) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash))) (defn chunk-cons [chunk rest] (if (zero? (-count chunk)) @@ -3495,7 +3532,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable (-seq [coll] @@ -3680,7 +3717,7 @@ reduces them without incurring seq initialization" (chunked-seq vec (unchecked-array-for vec end) end 0)))) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) IReduce (-reduce [coll f] @@ -3731,7 +3768,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable (-seq [coll] @@ -4030,7 +4067,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable (-seq [coll] coll)) @@ -4076,7 +4113,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable (-seq [coll] @@ -4181,7 +4218,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-map coll other)) IHash - (-hash [coll] (caching-hash coll hash-imap __hash)) + (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) ISeqable (-seq [coll] @@ -4371,7 +4408,7 @@ reduces them without incurring seq initialization" (-empty [coll] (with-meta cljs.core.List.EMPTY _meta)) IHash - (-hash [coll] (hash-coll coll)) + (-hash [coll] (hash-ordered-coll coll)) ISeq (-first [coll] @@ -4429,7 +4466,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-map coll other)) IHash - (-hash [coll] (caching-hash coll hash-imap __hash)) + (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) ISeqable (-seq [coll] @@ -5154,7 +5191,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) IReduce (-reduce [coll f] (seq-reduce f coll)) @@ -5212,7 +5249,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) IReduce (-reduce [coll f] (seq-reduce f coll)) @@ -5268,7 +5305,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-map coll other)) IHash - (-hash [coll] (caching-hash coll hash-imap __hash)) + (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) ISeqable (-seq [coll] @@ -5502,7 +5539,7 @@ reduces them without incurring seq initialization" (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) IMeta (-meta [coll] meta) @@ -5656,7 +5693,7 @@ reduces them without incurring seq initialization" (-val [node] val) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) IEquiv (-equiv [coll other] (equiv-sequential coll other)) @@ -5797,7 +5834,7 @@ reduces them without incurring seq initialization" (-val [node] val) IHash - (-hash [coll] (caching-hash coll hash-coll __hash)) + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) IEquiv (-equiv [coll other] (equiv-sequential coll other)) @@ -6013,7 +6050,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-map coll other)) IHash - (-hash [coll] (caching-hash coll hash-imap __hash)) + (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) ICounted (-count [coll] cnt) @@ -6175,7 +6212,7 @@ reduces them without incurring seq initialization" (-empty [coll] (with-meta cljs.core.List.EMPTY _meta)) IHash - (-hash [coll] (hash-coll coll)) + (-hash [coll] (hash-ordered-coll coll)) ISeq (-first [coll] @@ -6239,7 +6276,7 @@ reduces them without incurring seq initialization" (-empty [coll] (with-meta cljs.core.List.EMPTY _meta)) IHash - (-hash [coll] (hash-coll coll)) + (-hash [coll] (hash-ordered-coll coll)) ISeq (-first [coll] @@ -6349,7 +6386,7 @@ reduces them without incurring seq initialization" other))) IHash - (-hash [coll] (caching-hash coll hash-iset __hash)) + (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) ISeqable (-seq [coll] (keys hash-map)) @@ -6464,7 +6501,7 @@ reduces them without incurring seq initialization" other))) IHash - (-hash [coll] (caching-hash coll hash-iset __hash)) + (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) ISeqable (-seq [coll] (keys tree-map)) @@ -6722,7 +6759,7 @@ reduces them without incurring seq initialization" (-equiv [rng other] (equiv-sequential rng other)) IHash - (-hash [rng] (caching-hash rng hash-coll __hash)) + (-hash [rng] (caching-hash rng hash-ordered-coll __hash)) ICounted (-count [rng] From 66a435eb25b0a09443a758146b96dd309cae2140 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Jun 2014 16:29:41 -0400 Subject: [PATCH 0055/4033] compare old and new hashing strategy in benchmarks --- benchmark/cljs/benchmark_runner.cljs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index ca682bca2..4ae692ca9 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -215,6 +215,14 @@ (recur (inc i) (conj r (str "foo" i))) r))) (simple-benchmark [coll hash-coll-test] (hash-coll coll) 100) +(simple-benchmark [coll hash-coll-test] (hash-ordered-coll coll) 100) +(def hash-imap-test + (loop [i 0 r {}] + (if (< i 1000) + (recur (inc i) (conj r [(keyword (str "foo" i)) i])) + r))) +(simple-benchmark [coll hash-imap-test] (hash-imap coll) 100) +(simple-benchmark [coll hash-imap-test] (hash-unordered-coll coll) 100) (simple-benchmark [coll pmap] (:f0 coll) 1000000) (simple-benchmark [coll pmap] (get coll :f0) 1000000) (simple-benchmark [coll pmap] (-lookup coll :f0 nil) 1000000) From 86e9a8fec7b9c50d5be62641a33c4dc855dfb964 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Jun 2014 18:48:24 -0400 Subject: [PATCH 0056/4033] benchmark m3-hash-unencoded-chars --- benchmark/cljs/benchmark_runner.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 4ae692ca9..06449e165 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -208,6 +208,7 @@ [:q 16] [:r 17] [:s 18] [:t 19] [:u 20] [:v 21] [:w 22] [:x 23] [:y 24] [:z 25] [:a0 26] [:b0 27] [:c0 28] [:d0 29] [:e0 30] [:f0 31]])) (simple-benchmark [key :f0] (hash key) 1000000) +(simple-benchmark [key "f0"] (m3-hash-unencoded-chars key) 1000000) (simple-benchmark [key :unsynchronized-mutable] (hash key) 1000000) (def hash-coll-test (loop [i 0 r []] From 81656713118f4215a6202a021496e308afcd1efb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Jun 2014 19:01:57 -0400 Subject: [PATCH 0057/4033] move hashing logic up so we get arity information for optimization --- src/cljs/cljs/core.cljs | 93 +++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 1075b5818..b3a4c5ba8 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -463,7 +463,53 @@ ;;;;;;;;;;;;;;;;;;; symbols ;;;;;;;;;;;;;;; -(declare list hash-combine hash Symbol = compare) +(declare list Symbol = compare) + +;; Simple caching of string hashcode +(def string-hash-cache (js-obj)) +(def string-hash-cache-count 0) + +(defn add-to-string-hash-cache [k] + (let [h (goog.string/hashCode k)] + (aset string-hash-cache k h) + (set! string-hash-cache-count (inc string-hash-cache-count)) + h)) + +(defn check-string-hash-cache [k] + (when (> string-hash-cache-count 255) + (set! string-hash-cache (js-obj)) + (set! string-hash-cache-count 0)) + (let [h (aget string-hash-cache k)] + (if (number? h) + h + (add-to-string-hash-cache k)))) + +(defn hash [o] + (cond + (implements? IHash o) + (-hash ^not-native o) + + (number? o) + (js-mod (.floor js/Math o) 2147483647) + + (true? o) 1 + + (false? o) 0 + + (string? o) + (check-string-hash-cache o) + + (nil? o) 0 + + :else + (-hash o))) + +(defn hash-combine [seed hash] + ; a la boost + (bit-xor seed + (+ hash 0x9e3779b9 + (bit-shift-left seed 6) + (bit-shift-right seed 2)))) (defn ^boolean instance? [t o] (cljs.core/instance? t o)) @@ -1237,45 +1283,6 @@ reduces them without incurring seq initialization" (recur ret (first ks) (next ks)) ret))))) -;; Simple caching of string hashcode -(def string-hash-cache (js-obj)) -(def string-hash-cache-count 0) - -(defn add-to-string-hash-cache [k] - (let [h (goog.string/hashCode k)] - (aset string-hash-cache k h) - (set! string-hash-cache-count (inc string-hash-cache-count)) - h)) - -(defn check-string-hash-cache [k] - (when (> string-hash-cache-count 255) - (set! string-hash-cache (js-obj)) - (set! string-hash-cache-count 0)) - (let [h (aget string-hash-cache k)] - (if (number? h) - h - (add-to-string-hash-cache k)))) - -(defn hash [o] - (cond - (implements? IHash o) - (-hash ^not-native o) - - (number? o) - (js-mod (.floor js/Math o) 2147483647) - - (true? o) 1 - - (false? o) 0 - - (string? o) - (check-string-hash-cache o) - - (nil? o) 0 - - :else - (-hash o))) - (defn ^boolean empty? "Returns true if coll has no items - same as (not (seq coll)). Please use the idiom (seq x) rather than (not (empty? x))" @@ -1990,12 +1997,6 @@ reduces them without incurring seq initialization" (= (first xs) (first ys)) (recur (next xs) (next ys)) :else false))))) -(defn hash-combine [seed hash] - ; a la boost - (bit-xor seed (+ hash 0x9e3779b9 - (bit-shift-left seed 6) - (bit-shift-right seed 2)))) - (defn- hash-coll [coll] (if (seq coll) (loop [res (hash (first coll)) s (next coll)] From 6a19be810f521eef209fe49f01cff6a445af35da Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Jun 2014 11:13:30 -0400 Subject: [PATCH 0058/4033] implement hash-string a la java.lang.String hashCode --- src/cljs/cljs/core.cljs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index b3a4c5ba8..118732af4 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -401,7 +401,7 @@ ;;;;;;;;;;;;;;;;;;; Murmur3 ;;;;;;;;;;;;;;; -;; http://developer.classpath.org/doc/java/lang/Integer-source.html +;;http://hg.openjdk.java.net/jdk7u/jdk7u6/jdk/file/8c2c5d63a17e/src/share/classes/java/lang/Integer.java (defn ^number int-rotate-left [x n] (bit-or (bit-shift-left x n) @@ -469,6 +469,16 @@ (def string-hash-cache (js-obj)) (def string-hash-cache-count 0) +;;http://hg.openjdk.java.net/jdk7u/jdk7u6/jdk/file/8c2c5d63a17e/src/share/classes/java/lang/String.java +(defn hash-string [s] + (let [len (alength s)] + (if (pos? len) + (loop [i 0 hash 0] + (if (< i len) + (recur (inc i) (+ (imul 31 hash) (.charCodeAt s i))) + hash)) + 0))) + (defn add-to-string-hash-cache [k] (let [h (goog.string/hashCode k)] (aset string-hash-cache k h) From 6ed5857aa49f28dc81390e3522e85b497be0f9de Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Jun 2014 12:11:31 -0400 Subject: [PATCH 0059/4033] align string, keyword & symbol hashing with Clojure 1.6.0. fix reduce-kv test which had assumptions about kv order --- src/clj/cljs/compiler.clj | 17 ++------------- src/cljs/cljs/core.cljs | 41 ++++++++++++++++++----------------- test/cljs/cljs/core_test.cljs | 25 +++++++++++++-------- 3 files changed, 39 insertions(+), 44 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 7ccb00c04..ed4a23858 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -195,14 +195,6 @@ (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" (str x))] (emits \/ (.replaceAll (re-matcher #"/" pattern) "\\\\/") \/ flags)))) -(def ^:const goog-hash-max 0x100000000) - -(defn goog-string-hash [s] - (reduce - (fn [r c] - (mod (+ (* 31 r) (int c)) goog-hash-max)) - 0 s)) - (defmethod emit-constant clojure.lang.Keyword [x] (if (-> @env/*compiler* :opts :emit-constants) (let [value (-> @env/*compiler* ::ana/constant-table x)] @@ -218,10 +210,7 @@ (str ns "/" name) name)) (emits ",") - (emit-constant (+ (clojure.lang.Util/hashCombine - (unchecked-int (goog-string-hash ns)) - (unchecked-int (goog-string-hash name))) - 0x9e3779b9)) + (emit-constant (hash x)) (emits ")")))) (defmethod emit-constant clojure.lang.Symbol [x] @@ -237,9 +226,7 @@ (emits ",") (emit-constant symstr) (emits ",") - (emit-constant (clojure.lang.Util/hashCombine - (unchecked-int (goog-string-hash ns)) - (unchecked-int (goog-string-hash name)))) + (emit-constant (hash x)) (emits ",") (emit-constant nil) (emits ")"))) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 118732af4..16471b195 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -470,22 +470,24 @@ (def string-hash-cache-count 0) ;;http://hg.openjdk.java.net/jdk7u/jdk7u6/jdk/file/8c2c5d63a17e/src/share/classes/java/lang/String.java -(defn hash-string [s] - (let [len (alength s)] - (if (pos? len) - (loop [i 0 hash 0] - (if (< i len) - (recur (inc i) (+ (imul 31 hash) (.charCodeAt s i))) - hash)) - 0))) +(defn hash-string* [s] + (if-not (nil? s) + (let [len (alength s)] + (if (pos? len) + (loop [i 0 hash 0] + (if (< i len) + (recur (inc i) (+ (imul 31 hash) (.charCodeAt s i))) + hash)) + 0)) + 0)) (defn add-to-string-hash-cache [k] - (let [h (goog.string/hashCode k)] + (let [h (hash-string* k)] (aset string-hash-cache k h) (set! string-hash-cache-count (inc string-hash-cache-count)) h)) -(defn check-string-hash-cache [k] +(defn hash-string [k] (when (> string-hash-cache-count 255) (set! string-hash-cache (js-obj)) (set! string-hash-cache-count 0)) @@ -507,7 +509,7 @@ (false? o) 0 (string? o) - (check-string-hash-cache o) + (m3-hash-int (hash-string o)) (nil? o) 0 @@ -528,7 +530,9 @@ (instance? Symbol x)) (defn- hash-symbol [sym] - (hash-combine (hash (.-ns sym)) (hash (.-name sym)))) + (hash-combine + (m3-hash-unencoded-chars (.-name sym)) + (hash-string (.-ns sym)))) (defn- compare-symbols [a b] (cond @@ -2243,6 +2247,9 @@ reduces them without incurring seq initialization" (defn ^boolean list? [x] (satisfies? IList x)) +(defn hash-keyword [k] + (+ (hash-symbol k) 0x9e3779b9)) + (deftype Keyword [ns name fqn ^:mutable _hash] Object (toString [_] (str ":" fqn)) @@ -2259,14 +2266,8 @@ reduces them without incurring seq initialization" (get coll kw not-found)) IHash - (-hash [_] - ; This was checking if _hash == -1, should it stay that way? - (if (nil? _hash) - (do - (set! _hash (+ (hash-combine (hash ns) (hash name)) - 0x9e3779b9)) - _hash) - _hash)) + (-hash [this] + (caching-hash this hash-keyword _hash)) INamed (-name [_] name) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 14a17f9f6..bf457a9f1 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1835,16 +1835,23 @@ ;; Test builtin implementations of IKVReduce (letfn [(kvr-test [data expect] - (assert (= :reduced (reduce-kv (fn [_ _ _] (reduced :reduced)) - [] data))) - (assert (= expect (reduce-kv (fn [r k v] (-> r (conj k) (conj v))) - [] data))))] - (kvr-test (obj-map :k0 :v0 :k1 :v1) [:k0 :v0 :k1 :v1]) - (kvr-test (hash-map :k0 :v0 :k1 :v1) [:k0 :v0 :k1 :v1]) - (kvr-test (array-map :k0 :v0 :k1 :v1) [:k0 :v0 :k1 :v1]) - (kvr-test [:v0 :v1] [0 :v0 1 :v1])) + (assert + (= :reduced + (reduce-kv + (fn [_ _ _] (reduced :reduced)) + [] data))) + (assert + (= (sort expect) + (sort + (reduce-kv + (fn [r k v] (-> r (conj [k v]))) + [] data)))))] + (kvr-test (obj-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]) + (kvr-test (hash-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]) + (kvr-test (array-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]) + (kvr-test [:v0 :v1] [[0 :v0] [1 :v1]])) (assert (= {:init :val} (reduce-kv assoc {:init :val} nil))) - + ;; data conveying exception (assert (= {:foo 1} (try (throw (ex-info "asdf" {:foo 1})) From e5217e1dc8357adedbcdb0785bfed851cbce68ce Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Jun 2014 12:44:59 -0400 Subject: [PATCH 0060/4033] addition overflow, fix typo in hash-ordered-coll and hash-unordered-coll --- src/cljs/cljs/core.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 16471b195..6b0ef784e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -688,9 +688,9 @@ [coll] (loop [n 0 hash-code 1 coll (seq coll)] (if-not (nil? coll) - (recur (inc n) (+ (* 31 hash-code) (hash (first coll))) + (recur (inc n) (bit-or (+ (imul 31 hash-code) (hash (first coll))) 0) (next coll)) - (mix-collection-hash hash-code (imul 2 n))))) + (mix-collection-hash hash-code n)))) (defn ^number hash-unordered-coll "Returns the hash code, consistent with =, for an external unordered @@ -701,8 +701,8 @@ [coll] (loop [n 0 hash-code 0 coll (seq coll)] (if-not (nil? coll) - (recur (inc n) (+ hash-code (hash (first coll))) (next coll)) - (mix-collection-hash hash-code (imul 2 n))))) + (recur (inc n) (bit-or (+ hash-code (hash (first coll))) 0) (next coll)) + (mix-collection-hash hash-code n)))) ;;;;;;;;;;;;;;;;;;; protocols on primitives ;;;;;;;; (declare hash-map list equiv-sequential) From 8afabcd94532041d07e3f17f08ea69fce36e0868 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Jun 2014 09:04:56 -0400 Subject: [PATCH 0061/4033] explicit dep on Clojure 1.6.0, bump Closure Compiler dep, bump tools.reader dep --- pom.template.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index b665e3687..7e771fc0d 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -22,10 +22,15 @@ + + org.clojure + clojure + 1.6.0 + com.google.javascript closure-compiler - v20131014 + v20140508 org.clojure @@ -45,7 +50,7 @@ org.clojure tools.reader - 0.8.3 + 0.8.4 From 03679b4fb09bc0e538d6144dc7fb7f881c8fdb96 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Jun 2014 09:13:07 -0400 Subject: [PATCH 0062/4033] bump project.clj deps --- project.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project.clj b/project.clj index fd259125d..039c388e4 100644 --- a/project.clj +++ b/project.clj @@ -10,9 +10,9 @@ :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.6.0"] [org.clojure/data.json "0.2.3"] - [org.clojure/tools.reader "0.8.3"] - [org.clojure/google-closure-library "0.0-20130212-95c19e7f0f5f"] - [com.google.javascript/closure-compiler "v20131014"] + [org.clojure/tools.reader "0.8.4"] + [org.clojure/google-closure-library "0.0-20140226-71326067"] + [com.google.javascript/closure-compiler "v20140508"] [org.mozilla/rhino "1.7R4"]] :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]}} From 8f94b290ff09c2536a99f7cdc5e1b27cb2ac8e50 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Jun 2014 10:57:19 -0400 Subject: [PATCH 0063/4033] bump tools.reader dep in bootstrap --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index 233d3a656..82a49bac0 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,7 +6,7 @@ CLOJURE_RELEASE="1.6.0" DJSON_RELEASE="0.2.3" GCLOSURE_LIB_RELEASE="0.0-20140226-71326067" RHINO_RELEASE="1_7R3" -TREADER_RELEASE="0.8.3" +TREADER_RELEASE="0.8.4" mkdir -p lib From cc11c7996ba8522d6767fb45df2f76e20e4c1773 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Jun 2014 16:45:13 -0400 Subject: [PATCH 0064/4033] CLJS-812: Recurring from a case statement emits invalid JavaScript `case*` in analyzer was incorrectly handled, `thens` and `defaults` needed to analyzed in the same environment as the `case*` expr itself. In the compiler only handle the `:expr` case where we need to wrap in a JS function. Add the failing case provided by Luke VanderHart. --- src/clj/cljs/analyzer.clj | 16 ++++++++-------- src/clj/cljs/compiler.clj | 27 ++++++++++++--------------- test/cljs/cljs/core_test.cljs | 8 ++++++++ 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 1e424d105..34c6df621 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -482,18 +482,18 @@ :children [test-expr then-expr else-expr]})) (defmethod parse 'case* - [op env [_ sym tests thens default :as form] name] + [op env [_ sym tests thens default :as form] name] (assert (symbol? sym) "case* must switch on symbol") (assert (every? vector? tests) "case* tests must be grouped in vectors") (let [expr-env (assoc env :context :expr) - v (disallowing-recur (analyze expr-env sym)) - tests (mapv #(mapv (fn [t] (analyze expr-env t)) %) tests) - thens (mapv #(analyze expr-env %) thens) - default (analyze expr-env default)] + v (disallowing-recur (analyze expr-env sym)) + tests (mapv #(mapv (fn [t] (analyze expr-env t)) %) tests) + thens (mapv #(analyze env %) thens) + default (analyze env default)] (assert (every? (fn [t] (and (= :constant (:op t)) - ((some-fn number? string?) (:form t)))) - (apply concat tests)) - "case* tests must be numbers or strings") + ((some-fn number? string?) (:form t)))) + (apply concat tests)) + "case* tests must be numbers or strings") {:env env :op :case* :form form :v v :tests tests :thens thens :default default :children (vec (concat [v] tests thens (if default [default])))})) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 7ccb00c04..af7f85b7c 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -389,30 +389,27 @@ (defmethod emit* :case* [{:keys [v tests thens default env]}] - (if (= (:context env) :expr) + (when (= (:context env) :expr) (emitln "(function(){")) (let [gs (gensym "caseval__")] - (emitln "var " gs ";") - (emits "switch (") - (emits v) - (emitln "){") + (when (= :expr (:context env)) + (emitln "var " gs ";")) + (emitln "switch (" v ") {") (doseq [[ts then] (partition 2 (interleave tests thens))] (doseq [test ts] (emitln "case " test ":")) - (if (= (:context env) :statement) - (emitln then) - (emitln gs "=" then)) + (if (= :expr (:context env)) + (emitln gs "=" then) + (emitln then)) (emitln "break;")) (when default (emitln "default:") - (if (= (:context env) :statement) - (emitln default) - (emitln gs "=" default))) + (if (= :expr (:context env)) + (emitln gs "=" default) + (emitln default))) (emitln "}") - (condp = (:context env) - :expr (emitln "return " gs ";})()") - :return (emitln "return " gs ";") - :statement nil))) + (when (= :expr (:context env)) + (emitln "return " gs ";})()")))) (defmethod emit* :throw [{:keys [throw env]}] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index da1895c63..226bcc57b 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2216,5 +2216,13 @@ (assert (= "0atrue:key/wordsymb/olfalse[1 2 3 4]1234.56789" (str 0 "a" true nil :key/word 'symb/ol false [1 2 3 4] 1234.5678 0x09))) + ;; CLJS-812 + (defn case-recur [value] + (case value + :a (recur :b) + :b 0)) + + (assert (= (case-recur :a) 0)) + :ok ) From b8c4a40d9a795899b86fd33e6024a19b899640db Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Jun 2014 16:53:32 -0400 Subject: [PATCH 0065/4033] add changes.md update README --- README.md | 6 +++--- changes.md | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 changes.md diff --git a/README.md b/README.md index b9951f789..568ed2c0a 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2227 +Latest stable release: 0.0-2234 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2227"] +[org.clojure/clojurescript "0.0-2234"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2227 org.clojure clojurescript - 0.0-2227 + 0.0-2234 ``` diff --git a/changes.md b/changes.md new file mode 100644 index 000000000..9b742a830 --- /dev/null +++ b/changes.md @@ -0,0 +1,8 @@ +## 0.0-2234 + +### Fixes +* CLJS-812: Recur from case statement generated invalid JavaScript +* CLJS-811: use the correct class loader in cljs.js-deps/goog-resource +* fix fns with metadata under advanced compilation +* CLJS-809: dissoc :file metadata introduced by tools.reader 0.8.4 +* mark cljs.reader vars as ^:dynamic to avoid compiler warnings \ No newline at end of file From 95f603c610b646e9d2e0e440956faccad7108207 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Jun 2014 17:13:38 -0400 Subject: [PATCH 0066/4033] support optimized case on chars --- src/clj/cljs/analyzer.clj | 2 +- src/clj/cljs/core.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 34c6df621..f8e9ea6fb 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -491,7 +491,7 @@ thens (mapv #(analyze env %) thens) default (analyze env default)] (assert (every? (fn [t] (and (= :constant (:op t)) - ((some-fn number? string?) (:form t)))) + ((some-fn number? string? char?) (:form t)))) (apply concat tests)) "case* tests must be numbers or strings") {:env env :op :case* :form form diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 0bd850396..f5b5cac0d 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1184,7 +1184,7 @@ esym (gensym) tests (keys pairs)] (cond - (every? (some-fn core/number? core/string?) tests) + (every? (some-fn core/number? core/string? core/char?) tests) (core/let [no-default (if (odd? (count clauses)) (butlast clauses) clauses) tests (mapv #(if (seq? %) (vec %) [%]) (take-nth 2 no-default)) thens (vec (take-nth 2 (drop 1 no-default)))] From e1b19ab8e7cf98aca8f473cf29ad97bbd911d1da Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Mon, 23 Jun 2014 19:59:46 -0400 Subject: [PATCH 0067/4033] Clear function from Delays when realized. --- src/clj/cljs/core.clj | 2 +- src/cljs/cljs/core.cljs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index f5b5cac0d..80e503182 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1068,7 +1068,7 @@ invoke the body only the first time it is forced (with force or deref/@), and will cache the result and return it on all subsequent force calls." - `(new cljs.core/Delay (atom {:done false, :value nil}) (fn [] ~@body))) + `(new cljs.core/Delay (fn [] ~@body) nil)) (defmacro with-redefs "binding => var-symbol temp-value-expr diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index ca528bc69..49e38c460 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7356,17 +7356,17 @@ reduces them without incurring seq initialization" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Delay ;;;;;;;;;;;;;;;;;;;; -(deftype Delay [state f] +(deftype Delay [^:mutable f ^:mutable value] IDeref (-deref [_] - (:value (swap! state (fn [{:keys [done] :as curr-state}] - (if done - curr-state, - {:done true :value (f)}))))) + (when f + (set! value (f)) + (set! f nil)) + value) IPending (-realized? [d] - (:done @state))) + (not f))) (defn ^boolean delay? "returns true if x is a Delay created with delay" From db5f7d425a344b34c40c237ea8203109e4e346cf Mon Sep 17 00:00:00 2001 From: Brian Kim Date: Sat, 14 Jun 2014 00:22:56 -0400 Subject: [PATCH 0068/4033] CLJS-816: clojure.set/rename-keys accidentally deletes keys when there's a collision. --- src/cljs/clojure/set.cljs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/cljs/clojure/set.cljs b/src/cljs/clojure/set.cljs index 80f1db4b8..b9ba41f7b 100644 --- a/src/cljs/clojure/set.cljs +++ b/src/cljs/clojure/set.cljs @@ -72,13 +72,12 @@ (defn rename-keys "Returns the map with the keys in kmap renamed to the vals in kmap" [map kmap] - (reduce + (reduce (fn [m [old new]] - (if (and (not= old new) - (contains? m old)) - (-> m (assoc new (get m old)) (dissoc old)) - m)) - map kmap)) + (if (contains? map old) + (assoc m new (get map old)) + m)) + (apply dissoc map (keys kmap)) kmap)) (defn rename "Returns a rel of the maps in xrel with the keys in kmap renamed to the vals in kmap" From aa9e5feceddda2801557cdea5e3c80bef6b9e1db Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 24 Jun 2014 12:28:49 -0400 Subject: [PATCH 0069/4033] CLJS-816 tests --- test/cljs/cljs/core_test.cljs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 226bcc57b..3544c7396 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1,5 +1,6 @@ (ns cljs.core-test - (:require [clojure.string :as s])) + (:require [clojure.string :as s] + [clojure.set :as set])) (defn test-stuff [] ;; js primitives @@ -2224,5 +2225,11 @@ (assert (= (case-recur :a) 0)) + ;; CLJS-816 + (assert (= (set/rename-keys {:a "one" :b "two"} {:a :z}) {:z "one" :b "two"})) + (assert (= (set/rename-keys {:a "one" :b "two"} {:a :z :c :y}) {:z "one" :b "two"})) + (assert (= (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) + {:a "two" :b "one" :c "three"})) + :ok ) From 6b68a3742cb3bb23f6c5cb3850ed2caab324b033 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 1 Jul 2014 22:02:18 -0400 Subject: [PATCH 0070/4033] bump closure dependency --- pom.template.xml | 2 +- project.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 7e771fc0d..c4321fb08 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20140508 + v20140625 org.clojure diff --git a/project.clj b/project.clj index 039c388e4..bb97b702b 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.3"] [org.clojure/tools.reader "0.8.4"] [org.clojure/google-closure-library "0.0-20140226-71326067"] - [com.google.javascript/closure-compiler "v20140508"] + [com.google.javascript/closure-compiler "v20140625"] [org.mozilla/rhino "1.7R4"]] :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]}} From 08832549ce2ed386300fa637d7f88a75d57cc344 Mon Sep 17 00:00:00 2001 From: Daniel Skarda Date: Tue, 1 Jul 2014 17:13:51 +0200 Subject: [PATCH 0071/4033] CLJS-820: Missing invoke without arguments in MetaFn --- src/cljs/cljs/core.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 1ef3569ec..f71c451f4 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1209,6 +1209,8 @@ reduces them without incurring seq initialization" (MetaFn. afn new-meta)) Fn IFn + (-invoke [_] + (afn)) (-invoke [_ a] (afn a)) (-invoke [_ a b] From 32259c5ff3f86ea086ae3949403df80c2f518c7e Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Wed, 25 Jun 2014 11:44:12 -0500 Subject: [PATCH 0072/4033] CLJS-819: cljs.reader cannot handle character classes beginning with slashes in regex literals cljs.reader was incorrectly parsing the contents of a pattern literal (#"..") looking for string escapes to process instead of passing the string through to re-pattern as-is. Adds a read-raw-string* helper method to do this correctly in the future. --- src/cljs/cljs/reader.cljs | 17 ++++++++++++++++- test/cljs/cljs/reader_test.cljs | 5 +++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index d784b39c3..a115063ea 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -299,6 +299,21 @@ nil if the end of stream has been reached") (identical? \" ch) (. buffer (toString)) :default (recur (do (.append buffer ch) buffer) (read-char reader))))) +(defn read-raw-string* + [reader _] + (loop [buffer (gstring/StringBuffer.) + ch (read-char reader)] + (cond + (nil? ch) (reader-error reader "EOF while reading") + (identical? "\\" ch) (do (.append buffer ch) + (let [nch (read-char reader)] + (if (nil? nch) + (reader-error reader "EOF while reading") + (recur (doto buffer (.append nch)) + (read-char reader))))) + (identical? "\"" ch) (.toString buffer) + :else (recur (doto buffer (.append ch)) (read-char reader))))) + (defn special-symbols [t not-found] (cond (identical? t "nil") nil @@ -364,7 +379,7 @@ nil if the end of stream has been reached") (defn read-regex [rdr ch] - (-> (read-string* rdr ch) re-pattern)) + (-> (read-raw-string* rdr ch) re-pattern)) (defn read-discard [rdr _] diff --git a/test/cljs/cljs/reader_test.cljs b/test/cljs/cljs/reader_test.cljs index 1791ba730..af2a7449b 100644 --- a/test/cljs/cljs/reader_test.cljs +++ b/test/cljs/cljs/reader_test.cljs @@ -174,4 +174,9 @@ (assert (nil? (reader/read-string ""))) + ;; CLJS-819 + (let [re (reader/read-string "#\"\\s\\u00a1\"") + m (re-find re " \u00a1 ")] + (assert (= m " \u00a1"))) + :ok) From 054b0142e303586761d3da01849a2e24896ad7dd Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 19 Jun 2014 16:54:06 -0400 Subject: [PATCH 0073/4033] CLJS-817: Warning on use of undeclared var when creating recursive definition An attempt to define a var, where the initializer refers to the var being defined, succeeds but with an "Use of undeclared Var" warning. This is the result confirm-var-exists being unable to locate a declaration of the var within @env/*compiler* when the init-expr of the def form is being analyzed (all from within defmethod parse 'def). This patch eagerly dumps---but conditionally, only in the case that an init-expr exists---some of the var's info into @env/*compiler* so that it is available during the analysis needed to form init-expr. The info dumped in is a subset of the info that is dumped in at the end of parse 'def. The appropriate subset is probably debatable. Empirically you can see that an empty map would suffice to satisfy confirm-var-exists. But this patch puts more of what is available into @env/*compiler* "in case" it might be needed during analysis of the init-expr, without trying to second-guess how the analysis will be done. But, some of the items that fall later in parse 'def are not included (some depend themselves on init-expr). Also, the overall approach doesn't address the case if parse 'def throws while this partial data has been mutated into @env/*compiler*---it doesn't address exception safety and attempt to remove it. --- src/clj/cljs/analyzer.clj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index f8e9ea6fb..9e20916fe 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -599,6 +599,12 @@ sym) :op :var) init-expr (when (contains? args :init) + (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] + (merge + {:name name} + sym-meta + (when dynamic {:dynamic true}) + (source-info name env))) (disallowing-recur (analyze (assoc env :context :expr) (:init args) sym))) fn-var? (and init-expr (= (:op init-expr) :fn)) From f0dcc75573a42758f8c39b57d1747a2b4967327e Mon Sep 17 00:00:00 2001 From: Alan Dipert Date: Thu, 19 Jun 2014 21:27:33 -0400 Subject: [PATCH 0074/4033] Parenthesize long literal; fixes CLJS-715 --- src/clj/cljs/compiler.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 47ab0728a..afaf33928 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -180,7 +180,7 @@ (defmulti emit-constant class) (defmethod emit-constant nil [x] (emits "null")) -(defmethod emit-constant Long [x] (emits x)) +(defmethod emit-constant Long [x] (emits "(" x ")")) (defmethod emit-constant Integer [x] (emits x)) ; reader puts Integers in metadata (defmethod emit-constant Double [x] (emits x)) (defmethod emit-constant String [x] From 185127f850839664c86d92d10146b83e5c354c37 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 1 Jul 2014 22:39:44 -0400 Subject: [PATCH 0075/4033] update changes.md and README.md --- README.md | 6 +++--- changes.md | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 568ed2c0a..75b77f25e 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2234 +Latest stable release: 0.0-2261 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2234"] +[org.clojure/clojurescript "0.0-2261"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2234 org.clojure clojurescript - 0.0-2234 + 0.0-2261 ``` diff --git a/changes.md b/changes.md index 9b742a830..e87aca76e 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,17 @@ +## 0.0-2261 + +### Changes +* Dependency on Clojure 1.6.0 + +### Enhancements +* Murmur3 hashing for collections + +### Fixes +* CLJS-817: Warning on use of undeclared var when creating recursive definition +* CLJS-819: cljs.reader cannot handle character classes beginning with slashes in regex literals +* CLJS-820: Missing invoke without arguments in MetaFn +* CLJS-816: clojure.set/rename-keys accidentally deletes keys + ## 0.0-2234 ### Fixes From 774d4588581f6d29a1b6b2555171d3f3d36c2c83 Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Tue, 24 Jun 2014 10:08:44 -0500 Subject: [PATCH 0076/4033] CLJS-810: re-matches returns [] if string is nil re-find and re-matches would return bogus (sometimes not-falsy) results when run against a non-string because of javascript's implicit toString on re.exec() arguments. re-find and re-matches now throw TypeError if given a non-string. --- src/cljs/cljs/core.cljs | 25 +++++++++++++++---------- test/cljs/cljs/core_test.cljs | 11 +++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index f71c451f4..0d1d51375 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -6925,11 +6925,14 @@ reduces them without incurring seq initialization" (defn re-matches "Returns the result of (re-find re s) if re fully matches s." [re s] - (let [matches (.exec re s)] - (when (= (first matches) s) - (if (== (count matches) 1) - (first matches) - (vec matches))))) + (if (string? s) + (let [matches (.exec re s)] + (when (= (first matches) s) + (if (== (count matches) 1) + (first matches) + (vec matches)))) + (throw (js/TypeError. "re-matches must match against a string.")))) + (defn re-find "Returns the first regex match, if any, of s to re, using @@ -6937,11 +6940,13 @@ reduces them without incurring seq initialization" substring, then any capturing groups if the regular expression contains capturing groups." [re s] - (let [matches (.exec re s)] - (when-not (nil? matches) - (if (== (count matches) 1) - (first matches) - (vec matches))))) + (if (string? s) + (let [matches (.exec re s)] + (when-not (nil? matches) + (if (== (count matches) 1) + (first matches) + (vec matches)))) + (throw (js/TypeError. "re-find must match against a string.")))) (defn re-seq "Returns a lazy sequence of successive matches of re in s." diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 92b10ab89..5796ca922 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2252,5 +2252,16 @@ (assert (= (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) {:a "two" :b "one" :c "three"})) + ;; CLJS-810 + (let [not-strings [true false nil 1 (fn [])]] + (assert (every? #(= :failed (try (re-find #"." %) + (catch js/TypeError _ :failed))) not-strings)) + (assert (every? #(= :failed (try (re-matches #"." %) + (catch js/TypeError _ :failed))) not-strings)) + (assert (every? #(= :failed (try (re-find #"nomatch" %) + (catch js/TypeError _ :failed))) not-strings)) + (assert (every? #(= :failed (try (re-matches #"nomatch" %) + (catch js/TypeError _ :failed))) not-strings))) + :ok ) From 48e9f3013b806f8c6196fe22e202f2400f4c5c44 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 2 Jul 2014 18:50:00 -0400 Subject: [PATCH 0077/4033] ES6 style Iterator --- src/cljs/cljs/core.cljs | 12 ++++++++++++ src/cljs/cljs/externs.js | 22 ++++++++++++++++++++++ test/cljs/cljs/core_test.cljs | 9 +++++++++ 3 files changed, 43 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index f71c451f4..eaefcfbce 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4314,6 +4314,18 @@ reduces them without incurring seq initialization" (set! cljs.core.ObjMap.fromObject (fn [ks obj] (ObjMap. nil ks obj 0 nil))) +(deftype Iterator [^:mutable s] + Object + (next [_] + (if-not (nil? s) + (let [x (first s)] + (set! s (next s)) + #js {:value x :done false}) + #js {:value nil :done true}))) + +(defn iterator [coll] + (Iterator. (seq coll))) + ;;; PersistentArrayMap (defn- array-map-index-of-nil? [arr m k] diff --git a/src/cljs/cljs/externs.js b/src/cljs/cljs/externs.js index 490c39cd4..46a0f39b8 100644 --- a/src/cljs/cljs/externs.js +++ b/src/cljs/cljs/externs.js @@ -1 +1,23 @@ Math.imul = function(a, b) {}; + +var cljs = {}; +cljs.core = {}; +/** + * @constructor; + */ +cljs.core.Iterator = function() {}; +cljs.core.Iterator.prototype.next = function() {}; + +/** + * @constructor; + */ +function IteratorStep() {}; +/** + * @type {boolean} + */ +IteratorStep.prototype.done; +/** + * @type {Object} + */ +IteratorStep.prototype.value; + diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 92b10ab89..587a49bc7 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2252,5 +2252,14 @@ (assert (= (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) {:a "two" :b "one" :c "three"})) + ;; seq-iterator + + (def iter (iterator [1 2 3])) + + (assert (= (.-value (.next iter)) 1)) + (assert (= (.-value (.next iter)) 2)) + (assert (= (.-value (.next iter)) 3)) + (assert (= (.-done (.next iter)) true)) + :ok ) From b14eda36ca7249983f5c8f05b97e79bb45623a0e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 2 Jul 2014 19:22:55 -0400 Subject: [PATCH 0078/4033] ES6 Map and Set iteration for ClojureScript map and set types --- src/cljs/cljs/core.cljs | 87 +++++++++++++++++++++++++++++++++++ src/cljs/cljs/externs.js | 21 +++++++++ test/cljs/cljs/core_test.cljs | 56 +++++++++++++++++++++- 3 files changed, 163 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 836535453..a4677e12f 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4326,6 +4326,30 @@ reduces them without incurring seq initialization" (defn iterator [coll] (Iterator. (seq coll))) +(deftype EntriesIterator [^:mutable s] + Object + (next [_] + (if-not (nil? s) + (let [[k v] (first s)] + (set! s (next s)) + #js {:value #js [k v] :done false}) + #js {:value nil :done true}))) + +(defn entries-iterator [coll] + (EntriesIterator. (seq coll))) + +(deftype SetEntriesIterator [^:mutable s] + Object + (next [_] + (if-not (nil? s) + (let [x (first s)] + (set! s (next s)) + #js {:value #js [x x] :done false}) + #js {:value nil :done true}))) + +(defn set-entries-iterator [coll] + (SetEntriesIterator. (seq coll))) + ;;; PersistentArrayMap (defn- array-map-index-of-nil? [arr m k] @@ -4458,10 +4482,25 @@ reduces them without incurring seq initialization" (when (<= i (- (alength arr) 2)) (PersistentArrayMapSeq. arr i _meta))) +(declare keys vals) + (deftype PersistentArrayMap [meta cnt arr ^:mutable __hash] Object (toString [coll] (pr-str* coll)) + (keys [coll] + (iterator (keys coll))) + (entries [coll] + (entries-iterator (seq coll))) + (values [coll] + (iterator (vals coll))) + (has [coll k] + (contains? coll k)) + (get [coll k] + (-lookup coll k)) + (forEach [coll f] + (doseq [[k v] coll] + (f v k))) ICloneable (-clone [_] (PersistentArrayMap. meta cnt arr __hash)) @@ -5301,6 +5340,19 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (keys [coll] + (iterator (keys coll))) + (entries [coll] + (entries-iterator (seq coll))) + (values [coll] + (iterator (vals coll))) + (has [coll k] + (contains? coll k)) + (get [coll k] + (-lookup coll k)) + (forEach [coll f] + (doseq [[k v] coll] + (f v k))) ICloneable (-clone [_] (PersistentHashMap. meta cnt root has-nil? nil-val __hash)) @@ -6038,6 +6090,19 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (keys [coll] + (iterator (keys coll))) + (entries [coll] + (entries-iterator (seq coll))) + (values [coll] + (iterator (vals coll))) + (has [coll k] + (contains? coll k)) + (get [coll k] + (-lookup coll k)) + (forEach [coll f] + (doseq [[k v] coll] + (f v k))) (entry-at [coll k] (loop [t tree] @@ -6386,6 +6451,17 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (keys [coll] + (iterator (seq coll))) + (entries [coll] + (set-entries-iterator (seq coll))) + (values [coll] + (iterator (seq coll))) + (has [coll k] + (contains? coll k)) + (forEach [coll f] + (doseq [[k v] coll] + (f v k))) ICloneable (-clone [_] (PersistentHashSet. meta hash-map __hash)) @@ -6501,6 +6577,17 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (keys [coll] + (iterator (seq coll))) + (entries [coll] + (set-entries-iterator (seq coll))) + (values [coll] + (iterator (seq coll))) + (has [coll k] + (contains? coll k)) + (forEach [coll f] + (doseq [[k v] coll] + (f v k))) ICloneable (-clone [_] (PersistentTreeSet. meta tree-map __hash)) diff --git a/src/cljs/cljs/externs.js b/src/cljs/cljs/externs.js index 46a0f39b8..e1d68f1b3 100644 --- a/src/cljs/cljs/externs.js +++ b/src/cljs/cljs/externs.js @@ -21,3 +21,24 @@ IteratorStep.prototype.done; */ IteratorStep.prototype.value; +/** + * @constructor; + */ +function Map() {}; +Map.prototype.keys = function() {}; +Map.prototype.entries = function() {}; +Map.prototype.values = function() {}; +Map.prototype.has = function(k) {}; +Map.prototype.get = function(k) {}; +Map.prototype.forEach = function(f) {}; + +/** + * @constructor; + */ +function Set() {}; +Set.prototype.keys = function() {}; +Set.prototype.entries = function() {}; +Set.prototype.values = function() {}; +Set.prototype.has = function(k) {}; +Set.prototype.forEach = function(f) {}; + diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 969fb9918..91052edd4 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2252,12 +2252,66 @@ (assert (= (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) {:a "two" :b "one" :c "three"})) + ;; basic iteration + (def iter (iterator [1 2 3])) (assert (= (.-value (.next iter)) 1)) (assert (= (.-value (.next iter)) 2)) (assert (= (.-value (.next iter)) 3)) - (assert (= (.-done (.next iter)) true)) + (assert (.-done (.next iter))) + + ;; ES6 Map + + (assert (.has {:foo "bar"} :foo)) + (assert (= (.get {:foo "bar"} :foo) "bar")) + + ;; ES6 Map iteration + + ;; keys + (def iter (.keys {:foo "bar" :baz "woz"})) + (assert (#{:foo :baz} (.-value (.next iter)))) + (assert (#{:foo :baz} (.-value (.next iter)))) + (assert (.-done (.next iter))) + + ;; entries + (def eiter (.entries {:foo "bar" :baz "woz"})) + (assert (= (seq (.-value (.next eiter))) (seq #js [:foo "bar"]))) + (assert (= (seq (.-value (.next eiter))) (seq #js [:baz "woz"]))) + (assert (.-done (.next eiter))) + + ;; values + (def iter (.values {:foo "bar" :baz "woz"})) + (assert (#{"bar" "woz"} (.-value (.next iter)))) + (assert (#{"bar" "woz"} (.-value (.next iter)))) + (assert (.-done (.next iter))) + + ;; ES6 Set + + (assert (.has #{:cat :bird :dog} :bird)) + + ;; ES6 Set iteration + + ;; keys + (def iter (.keys #{:cat :bird :dog})) + (assert (#{:cat :bird :dog} (.-value (.next iter)))) + (assert (#{:cat :bird :dog} (.-value (.next iter)))) + (assert (#{:cat :bird :dog} (.-value (.next iter)))) + (assert (.-done (.next iter))) + + ;; entries + (def iter (.entries #{:cat :bird :dog})) + (assert (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter))))) + (assert (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter))))) + (assert (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter))))) + (assert (.-done (.next iter))) + + ;; values + (def iter (.values #{:cat :bird :dog})) + (assert (#{:cat :bird :dog} (.-value (.next iter)))) + (assert (#{:cat :bird :dog} (.-value (.next iter)))) + (assert (#{:cat :bird :dog} (.-value (.next iter)))) + (assert (.-done (.next iter))) ;; CLJS-810 (let [not-strings [true false nil 1 (fn [])]] From ae9dd08e8e279eb4e1ac83ffc679cd4c83250507 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 2 Jul 2014 19:29:21 -0400 Subject: [PATCH 0079/4033] mark ES6 map/set interfaces and support as experimental --- src/cljs/cljs/core.cljs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index a4677e12f..ddea7aa54 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4314,6 +4314,7 @@ reduces them without incurring seq initialization" (set! cljs.core.ObjMap.fromObject (fn [ks obj] (ObjMap. nil ks obj 0 nil))) +;; EXPERIMENTAL: subject to change (deftype Iterator [^:mutable s] Object (next [_] @@ -4326,6 +4327,7 @@ reduces them without incurring seq initialization" (defn iterator [coll] (Iterator. (seq coll))) +;; EXPERIMENTAL: subject to change (deftype EntriesIterator [^:mutable s] Object (next [_] @@ -4338,6 +4340,7 @@ reduces them without incurring seq initialization" (defn entries-iterator [coll] (EntriesIterator. (seq coll))) +;; EXPERIMENTAL: subject to change (deftype SetEntriesIterator [^:mutable s] Object (next [_] @@ -4488,6 +4491,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + + ;; EXPERIMENTAL: subject to change (keys [coll] (iterator (keys coll))) (entries [coll] @@ -5340,6 +5345,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + + ;; EXPERIMENTAL: subject to change (keys [coll] (iterator (keys coll))) (entries [coll] @@ -6090,6 +6097,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + + ;; EXPERIMENTAL: subject to change (keys [coll] (iterator (keys coll))) (entries [coll] @@ -6451,6 +6460,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + + ;; EXPERIMENTAL: subject to change (keys [coll] (iterator (seq coll))) (entries [coll] @@ -6577,6 +6588,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + + ;; EXPERIMENTAL: subject to change (keys [coll] (iterator (seq coll))) (entries [coll] From e92e8064813ed9a74c6dcf5bfd3adf5b85df1aea Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 6 Jul 2014 10:48:30 -0400 Subject: [PATCH 0080/4033] CLJS-823: use non-native imul implementation if Math.imul broken This fixes imul for all released versions of Safari 7. --- src/cljs/cljs/core.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index ddea7aa54..0450c7395 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -408,7 +408,8 @@ (unsigned-bit-shift-right x (- n)))) ;; http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul -(if (exists? Math/imul) +(if (and (exists? Math/imul) + (not (zero? (Math/imul 0xffffffff 5)))) (defn ^number imul [a b] (js/Math.imul a b)) (defn ^number imul [a b] (let [ah (bit-and (unsigned-bit-shift-right a 16) 0xffff) From 9255a49c4903e2f8f7c1673ac1a36d9109444ba7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 6 Jul 2014 15:48:09 -0400 Subject: [PATCH 0081/4033] 0.0-2268 --- README.md | 6 +++--- changes.md | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 75b77f25e..78d12fc3e 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2261 +Latest stable release: 0.0-2268 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2261"] +[org.clojure/clojurescript "0.0-2268"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2261 org.clojure clojurescript - 0.0-2261 + 0.0-2268 ``` diff --git a/changes.md b/changes.md index e87aca76e..dd9e18d8b 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,12 @@ +## 0.0-2268 + +### Changes +* Experimental support for ES6 Map/Set interface + +### Fixes +* CLJS-823: use non-native imul in Safari +* CLJS-810: re-matches returns [] if string is nil + ## 0.0-2261 ### Changes From e54c769f6dd3b322875be352530229144db81d60 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 7 Jul 2014 16:01:12 -0400 Subject: [PATCH 0082/4033] fix typos --- src/cljs/cljs/core.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 0450c7395..6ee81ef3e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1802,19 +1802,19 @@ reduces them without incurring seq initialization" (defn unchecked-remainder-int [x n] (cljs.core/unchecked-remainder-int x n)) -(defn ^number unchecked-substract +(defn ^number unchecked-subtract "If no ys are supplied, returns the negation of x, else subtracts the ys from x and returns the result." ([x] (cljs.core/unchecked-subtract x)) ([x y] (cljs.core/unchecked-subtract x y)) - ([x y & more] (reduce unchecked-substract (cljs.core/unchecked-subtract x y) more))) + ([x y & more] (reduce unchecked-subtract (cljs.core/unchecked-subtract x y) more))) -(defn ^number unchecked-substract-int +(defn ^number unchecked-subtract-int "If no ys are supplied, returns the negation of x, else subtracts the ys from x and returns the result." ([x] (cljs.core/unchecked-subtract-int x)) ([x y] (cljs.core/unchecked-subtract-int x y)) - ([x y & more] (reduce unchecked-substract-int (cljs.core/unchecked-subtract-int x y) more))) + ([x y & more] (reduce unchecked-subtract-int (cljs.core/unchecked-subtract-int x y) more))) (defn- ^number fix [q] (if (>= q 0) From 9a4b4df0f7a100129238bbcbdafa7e30f874b005 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 10 Jul 2014 12:01:48 -0400 Subject: [PATCH 0083/4033] export equiv methods --- src/cljs/cljs/core.cljs | 70 ++++++++++++++++++++++++++++++++++- src/cljs/cljs/externs.js | 5 +++ test/cljs/cljs/core_test.cljs | 9 +++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 6ee81ef3e..02265da3f 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -550,26 +550,34 @@ (deftype Symbol [ns name str ^:mutable _hash _meta] Object (toString [_] str) + (equiv [this other] (-equiv this other)) + IEquiv (-equiv [_ other] (if (instance? Symbol other) (identical? str (.-str other)) false)) + IFn (-invoke [sym coll] (-lookup coll sym nil)) (-invoke [sym coll not-found] (-lookup coll sym not-found)) + IMeta (-meta [_] _meta) + IWithMeta (-with-meta [_ new-meta] (Symbol. ns name str _hash new-meta)) + IHash (-hash [sym] (caching-hash sym hash-symbol _hash)) + INamed (-name [_] name) (-namespace [_] ns) + IPrintWithWriter (-pr-writer [o writer _] (-write writer str))) @@ -837,6 +845,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ICloneable (-clone [_] (IndexedSeq. arr i)) @@ -914,6 +924,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ICloneable (-clone [_] (RSeq. ci i meta)) @@ -2061,6 +2073,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IList @@ -2118,6 +2132,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IList @@ -2198,6 +2214,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IList @@ -2256,6 +2274,8 @@ reduces them without incurring seq initialization" (deftype Keyword [ns name fqn ^:mutable _hash] Object (toString [_] (str ":" fqn)) + (equiv [this other] + (-equiv this other)) IEquiv (-equiv [_ other] @@ -2316,7 +2336,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) - + (equiv [this other] + (-equiv this other)) (sval [coll] (if (nil? fn) s @@ -2430,6 +2451,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IWithMeta (-with-meta [coll m] @@ -3492,6 +3515,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ICloneable (-clone [_] (PersistentVector. meta cnt shift root tail __hash)) @@ -3674,6 +3699,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IWithMeta (-with-meta [coll m] @@ -3753,6 +3780,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ICloneable (-clone [_] (Subvec. meta v start end __hash)) @@ -4055,6 +4084,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IWithMeta (-with-meta [coll meta] (PersistentQueueSeq. meta front rear __hash)) @@ -4091,6 +4122,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ICloneable (-clone [coll] (PersistentQueue. meta count front rear __hash)) @@ -4142,6 +4175,9 @@ reduces them without incurring seq initialization" (set! cljs.core.PersistentQueue.EMPTY (PersistentQueue. nil 0 nil [] 0)) (deftype NeverEquiv [] + Object + (equiv [this other] + (-equiv this other)) IEquiv (-equiv [o other] false)) @@ -4211,6 +4247,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IWithMeta (-with-meta [coll meta] (ObjMap. meta keys strobj update-count __hash)) @@ -4435,6 +4473,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IMeta (-meta [coll] _meta) @@ -4492,6 +4532,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ;; EXPERIMENTAL: subject to change (keys [coll] @@ -5224,6 +5266,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IMeta (-meta [coll] meta) @@ -5289,6 +5333,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IMeta (-meta [coll] meta) @@ -5346,6 +5392,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ;; EXPERIMENTAL: subject to change (keys [coll] @@ -5593,6 +5641,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ISeqable (-seq [this] this) @@ -6098,6 +6148,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ;; EXPERIMENTAL: subject to change (keys [coll] @@ -6291,6 +6343,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IMeta (-meta [coll] _meta) @@ -6355,6 +6409,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) IMeta (-meta [coll] _meta) @@ -6461,6 +6517,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ;; EXPERIMENTAL: subject to change (keys [coll] @@ -6589,6 +6647,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ;; EXPERIMENTAL: subject to change (keys [coll] @@ -6841,6 +6901,8 @@ reduces them without incurring seq initialization" Object (toString [coll] (pr-str* coll)) + (equiv [this other] + (-equiv this other)) ICloneable (-clone [_] (Range. meta start end step __hash)) @@ -7411,6 +7473,10 @@ reduces them without incurring seq initialization" (-swap! [o f] [o f a] [o f a b] [o f a b xs])) (deftype Atom [state meta validator watches] + Object + (equiv [this other] + (-equiv this other)) + IAtom IEquiv @@ -8138,6 +8204,8 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype UUID [uuid] Object (toString [_] uuid) + (equiv [this other] + (-equiv this other)) IEquiv (-equiv [_ other] diff --git a/src/cljs/cljs/externs.js b/src/cljs/cljs/externs.js index e1d68f1b3..f1ea8fa4d 100644 --- a/src/cljs/cljs/externs.js +++ b/src/cljs/cljs/externs.js @@ -42,3 +42,8 @@ Set.prototype.values = function() {}; Set.prototype.has = function(k) {}; Set.prototype.forEach = function(f) {}; +/** + * @constructor; + */ +function IEquiv() {}; +IEquiv.prototype.equiv = function() {}; diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 91052edd4..408dd4a61 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2324,5 +2324,14 @@ (assert (every? #(= :failed (try (re-matches #"nomatch" %) (catch js/TypeError _ :failed))) not-strings))) + ;; Object equiv + (assert (.equiv :foo :foo)) + (assert (.equiv 'foo 'foo)) + (assert (.equiv {:foo 1 :bar 2} {:foo 1 :bar 2})) + (assert (.equiv [1 2 3] [1 2 3])) + (assert (.equiv '(1 2 3) '(1 2 3))) + (assert (.equiv (map inc [1 2 3]) (map inc [1 2 3]))) + (assert (.equiv #{:cat :dog :bird} #{:cat :dog :bird})) + :ok ) From 13501318b4967eb5d0e486907e6cb35da81d60b9 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 10 Jul 2014 01:32:16 +0200 Subject: [PATCH 0084/4033] fix broken closure release script supposed to delete third_party/goog/base.js but the directory changed in more recent closure-library checkouts --- script/closure-library-release/make-closure-library-jars.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/closure-library-release/make-closure-library-jars.sh b/script/closure-library-release/make-closure-library-jars.sh index bd27363e2..469196257 100755 --- a/script/closure-library-release/make-closure-library-jars.sh +++ b/script/closure-library-release/make-closure-library-jars.sh @@ -143,6 +143,8 @@ perl -p -i -e 's/..\/..\/third_party\/closure\/goog\///go' \ "$work_dir/closure/goog/deps.js" rm -f \ + "$work_dir/third_party/goog/base.js" \ + "$work_dir/third_party/goog/deps.js" \ "$work_dir/third_party/closure/goog/base.js" \ "$work_dir/third_party/closure/goog/deps.js" From e7f3a5d70a11257d5b84f1562ac01b3a4379e6e4 Mon Sep 17 00:00:00 2001 From: Max Veytsman Date: Tue, 15 Jul 2014 01:10:54 -0400 Subject: [PATCH 0085/4033] CLJS-827: wrap macro expansion in try/catch Used wrapping-errors to annotate macro expansion errors with line numbers --- src/clj/cljs/analyzer.clj | 53 ++++++++++++++++---------------- test/clj/cljs/analyzer_tests.clj | 11 +++++++ 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 9e20916fe..16ed987aa 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1395,32 +1395,33 @@ (defn macroexpand-1 [env form] (env/ensure - (let [op (first form)] - (if (specials op) - form - (if-let [mac (and (symbol? op) (get-expander op env))] - (binding [*ns* (create-ns *cljs-ns*)] - (let [form' (apply mac form env (rest form))] - (if (seq? form') - (let [sym' (first form') - sym (first form)] - (if (= sym' 'js*) - (vary-meta form' merge - (cond-> {:js-op (if (namespace sym) sym (symbol "cljs.core" (str sym)))} - (-> mac meta ::numeric) (assoc :numeric true))) - form')) - form'))) - (if (symbol? op) - (let [opname (str op)] - (cond - (= (first opname) \.) (let [[target & args] (next form)] - (with-meta (list* '. target (symbol (subs opname 1)) args) - (meta form))) - (= (last opname) \.) (with-meta - (list* 'new (symbol (subs opname 0 (dec (count opname)))) (next form)) - (meta form)) - :else form)) - form)))))) + (wrapping-errors env + (let [op (first form)] + (if (specials op) + form + (if-let [mac (and (symbol? op) (get-expander op env))] + (binding [*ns* (create-ns *cljs-ns*)] + (let [form' (apply mac form env (rest form))] + (if (seq? form') + (let [sym' (first form') + sym (first form)] + (if (= sym' 'js*) + (vary-meta form' merge + (cond-> {:js-op (if (namespace sym) sym (symbol "cljs.core" (str sym)))} + (-> mac meta ::numeric) (assoc :numeric true))) + form')) + form'))) + (if (symbol? op) + (let [opname (str op)] + (cond + (= (first opname) \.) (let [[target & args] (next form)] + (with-meta (list* '. target (symbol (subs opname 1)) args) + (meta form))) + (= (last opname) \.) (with-meta + (list* 'new (symbol (subs opname 0 (dec (count opname)))) (next form)) + (meta form)) + :else form)) + form))))))) (declare analyze-list) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index f7fe775c5..f793a99ad 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -226,3 +226,14 @@ (is (= (cljs.env/with-compiler-env test-cenv (:tag (a/analyze test-env '(bit-count n)))) 'number))) + +;; ============================================================================= +;; Catching errors during macroexpansion + +(deftest test-defn-error + (is (.startsWith + (try + (a/analyze test-env '(defn foo 123)) + (catch Exception e + (.getMessage e))) + "Parameter declaration 123 should be a vector at line"))) From 95122e1206d4a64f392cff03bfd73712ab7f3a33 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 16 Jul 2014 17:26:50 -0400 Subject: [PATCH 0086/4033] CLJS-825: conflict between node js support files src/cljs/cljs/nodejs.js in conflict with src/cljs/cljs/nodejs.cljs --- src/clj/cljs/closure.clj | 2 +- src/cljs/cljs/{nodejs.js => bootstrap_node.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/cljs/cljs/{nodejs.js => bootstrap_node.js} (100%) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index d092f49c5..2084c5135 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -986,7 +986,7 @@ should contain the source for the given namespace name." (#{:none :whitespace} (:optimizations opts))) (let [outfile (io/file (io/file (output-directory opts)) "goog/bootstrap/nodejs.js")] (comp/mkdirs outfile) - (spit outfile (slurp (io/resource "cljs/nodejs.js"))))) + (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) diff --git a/src/cljs/cljs/nodejs.js b/src/cljs/cljs/bootstrap_node.js similarity index 100% rename from src/cljs/cljs/nodejs.js rename to src/cljs/cljs/bootstrap_node.js From 5ca535759f8e2490b42dfa25df0458a6c3376d8c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Jul 2014 22:49:31 -0400 Subject: [PATCH 0087/4033] CLJS-824: Unsigned hash for keywords produced via keyword fn Causes keyword hashes to be 32-bit signed values. --- src/cljs/cljs/core.cljs | 2 +- test/cljs/cljs/core_test.cljs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 02265da3f..bd29024cf 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2269,7 +2269,7 @@ reduces them without incurring seq initialization" (satisfies? IList x)) (defn hash-keyword [k] - (+ (hash-symbol k) 0x9e3779b9)) + (int (+ (hash-symbol k) 0x9e3779b9))) (deftype Keyword [ns name fqn ^:mutable _hash] Object diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 408dd4a61..e7c22a0dd 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1543,6 +1543,8 @@ (hash (hash-map :b 2 :a 1)))) (assert (= (hash {:start 133 :end 134}) (hash (apply hash-map [:start 133 :end 134])))) + (assert (= (hash :a) + (hash (keyword "a")))) (defprotocol IHasFirst (-get-first [this])) From 528ff073282c73f206e19f1ef906f4e0c914610e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 25 Jul 2014 14:28:32 -0400 Subject: [PATCH 0088/4033] remove obsolete SpiderMonkey flags --- script/benchmark | 2 +- script/test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/benchmark b/script/benchmark index 02506251a..bbff28629 100755 --- a/script/benchmark +++ b/script/benchmark @@ -19,7 +19,7 @@ if [ "$SPIDERMONKEY_HOME" = "" ]; then echo "SPIDERMONKEY_HOME not set, skipping SpiderMonkey benchmarks" else echo "Benchmarking with SpiderMonkey" - "${SPIDERMONKEY_HOME}/js" -m -a -f out/core-advanced-benchmark.js + "${SPIDERMONKEY_HOME}/js" -f out/core-advanced-benchmark.js fi if [ "$JSC_HOME" = "" ]; then diff --git a/script/test b/script/test index ccc933548..521630a78 100755 --- a/script/test +++ b/script/test @@ -23,7 +23,7 @@ if [ "$SPIDERMONKEY_HOME" = "" ]; then echo "SPIDERMONKEY_HOME not set, skipping SpiderMonkey tests" else echo "Testing with SpiderMonkey" - ${SPIDERMONKEY_HOME}/js -m -a -f out/core-advanced-test.js + ${SPIDERMONKEY_HOME}/js -f out/core-advanced-test.js ran=$[ran+1] fi From 98866a26d54b28fd49eed7a67b5556fe507febc6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 25 Jul 2014 14:33:06 -0400 Subject: [PATCH 0089/4033] 0.0-2277 --- README.md | 6 +++--- changes.md | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 78d12fc3e..a8b3c41c7 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2268 +Latest stable release: 0.0-2277 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2268"] +[org.clojure/clojurescript "0.0-2277"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2268 org.clojure clojurescript - 0.0-2268 + 0.0-2277 ``` diff --git a/changes.md b/changes.md index dd9e18d8b..98f0c5200 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,15 @@ +## 0.0-2277 + +## Enhancements +* All IEquiv implementor now export equiv Object method + +## Fixes +* CLJS-824: Unsigned hash for keywords produced via keyword fn +* CLJS-827: CLJS-827: wrap macro expansion in try/catch +* CLJS-826: fix broken closure release script +* CLJS-825: conflict between node js support files +* typo in unchecked-subtract-int + ## 0.0-2268 ### Changes From ce8688c8aedce73f03de05ed6f290e00bc4f7ccf Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 29 Jul 2014 20:20:04 +0200 Subject: [PATCH 0090/4033] remove duplicate keyword emit code keywords are emitted with precomputed hash values except when using :emit-constants which had its own keyword emit function. moved duplicate code into emits-keyword --- src/clj/cljs/compiler.clj | 42 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index afaf33928..551ad810e 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -195,23 +195,27 @@ (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" (str x))] (emits \/ (.replaceAll (re-matcher #"/" pattern) "\\\\/") \/ flags)))) +(defn emits-keyword [kw] + (let [ns (namespace kw) + name (name kw)] + (emits "new cljs.core.Keyword(") + (emit-constant ns) + (emits ",") + (emit-constant name) + (emits ",") + (emit-constant (if ns + (str ns "/" name) + name)) + (emits ",") + (emit-constant (hash kw)) + (emits ")"))) + (defmethod emit-constant clojure.lang.Keyword [x] (if (-> @env/*compiler* :opts :emit-constants) (let [value (-> @env/*compiler* ::ana/constant-table x)] (emits "cljs.core." value)) - (let [ns (namespace x) - name (name x)] - (emits "new cljs.core.Keyword(") - (emit-constant ns) - (emits ",") - (emit-constant name) - (emits ",") - (emit-constant (if ns - (str ns "/" name) - name)) - (emits ",") - (emit-constant (hash x)) - (emits ")")))) + (emits-keyword x) + )) (defmethod emit-constant clojure.lang.Symbol [x] (let [ns (namespace x) @@ -1077,15 +1081,9 @@ (doseq [[keyword value] table] (let [ns (namespace keyword) name (name keyword)] - (emits "cljs.core." value " = new cljs.core.Keyword(") - (emit-constant ns) - (emits ",") - (emit-constant name) - (emits ",") - (emit-constant (if ns - (str ns "/" name) - name)) - (emits ");\n")))) + (emits "cljs.core." value " = ") + (emits-keyword keyword) + (emits ";\n")))) (defn emit-constants-table-to-file [table dest] (with-open [out ^java.io.Writer (io/make-writer dest {})] From bc1dde8512acbd6635ce5e16fc72a8f7a368c296 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 1 Aug 2014 16:05:38 -0400 Subject: [PATCH 0091/4033] depend on org.clojure/google-closure-library 0.0-20140718-946a7d39 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index c4321fb08..731522c9b 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20140226-71326067 + 0.0-20140718-946a7d39 org.clojure diff --git a/project.clj b/project.clj index bb97b702b..02ef0fc4e 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :dependencies [[org.clojure/clojure "1.6.0"] [org.clojure/data.json "0.2.3"] [org.clojure/tools.reader "0.8.4"] - [org.clojure/google-closure-library "0.0-20140226-71326067"] + [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20140625"] [org.mozilla/rhino "1.7R4"]] :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} diff --git a/script/bootstrap b/script/bootstrap index 82a49bac0..e36033d86 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -4,7 +4,7 @@ set -e CLOJURE_RELEASE="1.6.0" DJSON_RELEASE="0.2.3" -GCLOSURE_LIB_RELEASE="0.0-20140226-71326067" +GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R3" TREADER_RELEASE="0.8.4" From 976a4db81e79aac00e1f99e3a00ed0c0772a1288 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 1 Aug 2014 17:55:50 -0400 Subject: [PATCH 0092/4033] 0.0-2280 --- README.md | 6 +++--- changes.md | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a8b3c41c7..ca129996f 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2277 +Latest stable release: 0.0-2280 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2277"] +[org.clojure/clojurescript "0.0-2280"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2277 org.clojure clojurescript - 0.0-2277 + 0.0-2280 ``` diff --git a/changes.md b/changes.md index 98f0c5200..253d6b5fc 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,11 @@ +## 0.0-2280 + +### Changes +* depend on latest org.clojure/google-closure-library + +### Fixes +* fix constants table bug where keywords did not include precomputed hash-code + ## 0.0-2277 ## Enhancements From 1c8a04d8b232ef876947ba08cfac0a921b031c20 Mon Sep 17 00:00:00 2001 From: Stuart Sierra Date: Fri, 1 Aug 2014 18:26:42 -0400 Subject: [PATCH 0093/4033] Google Closure Library release script using Maven --- script/closure-library-release/.gitignore | 2 + ...ary-jars.sh => closure-library-release.sh} | 119 ++++++++---------- ...e-closure-library-third-party.pom.template | 14 ++- .../google-closure-library.pom.template | 14 ++- 4 files changed, 72 insertions(+), 77 deletions(-) create mode 100644 script/closure-library-release/.gitignore rename script/closure-library-release/{make-closure-library-jars.sh => closure-library-release.sh} (61%) diff --git a/script/closure-library-release/.gitignore b/script/closure-library-release/.gitignore new file mode 100644 index 000000000..4a225889a --- /dev/null +++ b/script/closure-library-release/.gitignore @@ -0,0 +1,2 @@ +closure-library/ +tmp-build/ diff --git a/script/closure-library-release/make-closure-library-jars.sh b/script/closure-library-release/closure-library-release.sh similarity index 61% rename from script/closure-library-release/make-closure-library-jars.sh rename to script/closure-library-release/closure-library-release.sh index 469196257..e3b7daf9d 100755 --- a/script/closure-library-release/make-closure-library-jars.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash -# make-closure-library-jars.sh +# closure-library-release.sh # ClojureScript depends on the Google Closure JavaScript Libraries, # but Google does not publish those libraries in a Maven repository. -# This script builds release JAR and POM files for the Google Closure -# Library and its third-party extensions. +# This script builds generates Maven projects for the Google Closure +# Library and, optionally, deploys them. # The Google Closure Libraries are divided into two parts: the main # library and third-party extensions. The main library is Apache @@ -27,20 +27,6 @@ # The last release ZIP made by Google was 20130212-95c19e7f0f5f. To # get newer versions, we have to go to the Git repository. -# Usage: - -# 1. Clone the Google Closure Library Git repository -# -# 2. cd to the directory containing this script -# -# 3. Run this script with the path to the G.Closure Library -# as a command-line argument - -# If you are a Clojure release admin (you have the GPG key) then set -# the environment variable SIGN_GOOGLE_CLOSURE_LIBRARY_RELEASE to sign -# the releases with GPG. - - set -e @@ -48,7 +34,7 @@ set -e POM_TEMPLATE_FILE="google-closure-library.pom.template" THIRD_PARTY_POM_TEMPLATE_FILE="google-closure-library-third-party.pom.template" -RELEASE_KEY="Clojure/core (build.clojure.org Release Key version 2) " +GIT_CLONE_URL="git@github.com:google/closure-library.git" ### Functions @@ -63,8 +49,6 @@ Git repository." ## Command-line validation -closure_library_dir="$1" - if [[ ! -e $POM_TEMPLATE_FILE || ! -e $THIRD_PARTY_POM_TEMPLATE_FILE ]]; then echo "This script must be run from the directory containing google-closure-library.pom.template and @@ -72,24 +56,33 @@ google-closure-library-third-party.pom.template" exit 1 fi +## Fetch the Git repo + +closure_library_dir="closure-library" + if [[ ! -d $closure_library_dir ]]; then - print_usage - exit 1 + git clone "$GIT_CLONE_URL" "$closure_library_dir" fi +( + cd "$closure_library_dir" + git clean -fdx + git checkout master + git pull +) + closure_library_base="$closure_library_dir/closure" third_party_base="$closure_library_dir/third_party/closure" if [[ ! -d $closure_library_base || ! -d $third_party_base ]]; then echo "$closure_library_dir does not look like the Closure library" - print_usage exit 1 fi ## Working directory now=$(date "+%Y%m%d%H%M%S") -work_dir="closure-release-${now}" +work_dir="tmp-build" echo "Working directory: $work_dir" @@ -109,81 +102,69 @@ echo "Date: $commit_date" echo "Short SHA: $commit_short_sha" echo "Release version: $release_version" -release_base="google-closure-library-${release_version}" -jar_file="${release_base}.jar" -pom_file="${release_base}.pom" +## Creating directories + +project_dir="$work_dir/google-closure-library" +src_dir="$project_dir/src/main/resources" + +third_party_project_dir="$work_dir/google-closure-library-third-party" +third_party_src_dir="$third_party_project_dir/src/main/resources" -third_party_release_base="google-closure-library-third-party-${release_version}" -third_party_jar_file="${third_party_release_base}.jar" -third_party_pom_file="${third_party_release_base}.pom" +mkdir -p "$src_dir" "$third_party_src_dir" -## Copy Closure source into working dir +## Copy Closure sources -mkdir "$work_dir/closure" cp -r \ "$closure_library_dir/AUTHORS" \ "$closure_library_dir/LICENSE" \ "$closure_library_dir/README" \ "$closure_library_dir/closure/goog" \ "$closure_library_dir/closure/css" \ - "$work_dir/closure" + "$src_dir" -mkdir "$work_dir/third_party" cp -r \ "$closure_library_dir/AUTHORS" \ "$closure_library_dir/LICENSE" \ "$closure_library_dir/README" \ "$closure_library_dir/third_party/closure/goog" \ - "$work_dir/third_party" + "$third_party_src_dir" - -## Modify deps.js for third-party JAR; see CLJS-276: +## Modify main deps.js for third-party JAR; see CLJS-276: perl -p -i -e 's/..\/..\/third_party\/closure\/goog\///go' \ - "$work_dir/closure/goog/deps.js" - -rm -f \ - "$work_dir/third_party/goog/base.js" \ - "$work_dir/third_party/goog/deps.js" \ - "$work_dir/third_party/closure/goog/base.js" \ - "$work_dir/third_party/closure/goog/deps.js" + "$src_dir/goog/deps.js" -## Build the JARs: +## Remove empty third-party deps.js and base.js -( - cd "$work_dir/closure" - jar cf "../$jar_file" * -) - -( - cd "$work_dir/third_party" - jar cf "../$third_party_jar_file" * -) +rm -f \ + "$third_party_src_dir/goog/deps.js" \ + "$third_party_src_dir/goog/base.js" ## Generate the POM files: perl -p -e "s/RELEASE_VERSION/$release_version/go" \ "$POM_TEMPLATE_FILE" \ - > "$work_dir/$pom_file" + > "$project_dir/pom.xml" perl -p -e "s/RELEASE_VERSION/$release_version/go" \ "$THIRD_PARTY_POM_TEMPLATE_FILE" \ - > "$work_dir/$third_party_pom_file" + > "$third_party_project_dir/pom.xml" + +## Deploy the files if we are on Hudson -## Sign the files with GPG +if [ "$HUDSON" = "true" ]; then + ( + cd "$third_party_project_dir" + mvn -Psonatype-oss-release clean deploy + ) -if [[ -n "$SIGN_GOOGLE_CLOSURE_LIBRARY_RELEASE" ]]; then ( - cd "$work_dir" - for file in \ - "$jar_file" \ - "$third_party_jar_file" \ - "$pom_file" \ - "$third_party_pom_file" - do - gpg --verbose --armor --detach-sign \ - --default-key "$RELEASE_KEY" \ - "$file" - done + cd "$project_dir" + mvn -Psonatype-oss-release clean deploy nexus-staging:close ) + + echo "Now log in to https://oss.sonatype.org/ to close and release" + echo "the staging repository." +else + echo "Skipping deployment because we are not on Hudson." fi diff --git a/script/closure-library-release/google-closure-library-third-party.pom.template b/script/closure-library-release/google-closure-library-third-party.pom.template index 746114c58..8aa292da5 100644 --- a/script/closure-library-release/google-closure-library-third-party.pom.template +++ b/script/closure-library-release/google-closure-library-third-party.pom.template @@ -6,6 +6,12 @@ jar Google Closure Library Third-Party Extensions + + org.sonatype.oss + oss-parent + 9 + + http://code.google.com/p/closure-library/ @@ -52,13 +58,13 @@ - scm:svn:http://closure-library.googlecode.com/svn/trunk - scm:svn:http://closure-library.googlecode.com/svn/trunk - http://code.google.com/p/closure-library/source/browse/#svn/trunk + scm:https://github.com/google/closure-library.git + scm:git:https://github.com/google/closure-library.git + https://github.com/google/closure-library code.google.com http://code.google.com/p/closure-library/issues - \ No newline at end of file + diff --git a/script/closure-library-release/google-closure-library.pom.template b/script/closure-library-release/google-closure-library.pom.template index c5b2a3495..8d0695da3 100644 --- a/script/closure-library-release/google-closure-library.pom.template +++ b/script/closure-library-release/google-closure-library.pom.template @@ -6,6 +6,12 @@ jar Google Closure Library + + org.sonatype.oss + oss-parent + 9 + + http://code.google.com/p/closure-library/ @@ -50,13 +56,13 @@ - scm:svn:http://closure-library.googlecode.com/svn/trunk - scm:svn:http://closure-library.googlecode.com/svn/trunk - http://code.google.com/p/closure-library/source/browse/#svn/trunk + scm:https://github.com/google/closure-library.git + scm:git:https://github.com/google/closure-library.git + https://github.com/google/closure-library code.google.com http://code.google.com/p/closure-library/issues - \ No newline at end of file + From fbd1690cc057e32881fdb69b17d4d06e7e8651b9 Mon Sep 17 00:00:00 2001 From: Stuart Sierra Date: Fri, 1 Aug 2014 18:36:04 -0400 Subject: [PATCH 0094/4033] Configure Sonatype staging plugin in Closure Library release --- .../closure-library-release.sh | 10 ++++- ...e-closure-library-third-party.pom.template | 41 +++++++++++++++++++ .../google-closure-library.pom.template | 41 +++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index e3b7daf9d..d71befbdf 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -155,12 +155,18 @@ perl -p -e "s/RELEASE_VERSION/$release_version/go" \ if [ "$HUDSON" = "true" ]; then ( cd "$third_party_project_dir" - mvn -Psonatype-oss-release clean deploy + mvn --fail-at-end \ + -Psonatype-oss-release \ + -Dsource.skip=true \ + clean deploy ) ( cd "$project_dir" - mvn -Psonatype-oss-release clean deploy nexus-staging:close + mvn --fail-at-end \ + -Psonatype-oss-release \ + -Dsource.skip=true \ + clean deploy nexus-staging:close ) echo "Now log in to https://oss.sonatype.org/ to close and release" diff --git a/script/closure-library-release/google-closure-library-third-party.pom.template b/script/closure-library-release/google-closure-library-third-party.pom.template index 8aa292da5..522de8436 100644 --- a/script/closure-library-release/google-closure-library-third-party.pom.template +++ b/script/closure-library-release/google-closure-library-third-party.pom.template @@ -67,4 +67,45 @@ code.google.com http://code.google.com/p/closure-library/issues + + + + sonatype-oss-release + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.4.4 + + + default-deploy + deploy + + + deploy + + + + + + https://oss.sonatype.org/ + + sonatype-nexus-staging + + + + + + diff --git a/script/closure-library-release/google-closure-library.pom.template b/script/closure-library-release/google-closure-library.pom.template index 8d0695da3..f9298508f 100644 --- a/script/closure-library-release/google-closure-library.pom.template +++ b/script/closure-library-release/google-closure-library.pom.template @@ -65,4 +65,45 @@ code.google.com http://code.google.com/p/closure-library/issues + + + + sonatype-oss-release + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.4.4 + + + default-deploy + deploy + + + deploy + + + + + + https://oss.sonatype.org/ + + sonatype-nexus-staging + + + + + + From 5f3a5225bd4f22b58106f1d6a3f39522b86e5022 Mon Sep 17 00:00:00 2001 From: Stuart Sierra Date: Fri, 1 Aug 2014 18:51:33 -0400 Subject: [PATCH 0095/4033] G.Closure Library release: Don't call staging:close I think the 'deploy' goal of the Nexus Staging plugin already closes the repository. --- script/closure-library-release/closure-library-release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index d71befbdf..1f422447a 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -166,7 +166,7 @@ if [ "$HUDSON" = "true" ]; then mvn --fail-at-end \ -Psonatype-oss-release \ -Dsource.skip=true \ - clean deploy nexus-staging:close + clean deploy ) echo "Now log in to https://oss.sonatype.org/ to close and release" From bb3adca7e6c0b1cc385c6d9dc4366698614c215e Mon Sep 17 00:00:00 2001 From: Stuart Sierra Date: Fri, 1 Aug 2014 18:54:51 -0400 Subject: [PATCH 0096/4033] closure-library-release: remove 'close' from comments at end --- script/closure-library-release/closure-library-release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index 1f422447a..031a102e8 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -169,7 +169,7 @@ if [ "$HUDSON" = "true" ]; then clean deploy ) - echo "Now log in to https://oss.sonatype.org/ to close and release" + echo "Now log in to https://oss.sonatype.org/ to release" echo "the staging repository." else echo "Skipping deployment because we are not on Hudson." From 7de3636f2b369df68b2baad9d282266cd43a3fa0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 2 Aug 2014 10:45:20 -0400 Subject: [PATCH 0097/4033] include large hash map lookup benchmarks --- benchmark/cljs/benchmark_runner.cljs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 06449e165..b101a633a 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -222,11 +222,18 @@ (if (< i 1000) (recur (inc i) (conj r [(keyword (str "foo" i)) i])) r))) +(def hash-imap-int-test + (loop [i 0 r {}] + (if (< i 1000) + (recur (inc i) (conj r [i i])) + r))) (simple-benchmark [coll hash-imap-test] (hash-imap coll) 100) (simple-benchmark [coll hash-imap-test] (hash-unordered-coll coll) 100) (simple-benchmark [coll pmap] (:f0 coll) 1000000) (simple-benchmark [coll pmap] (get coll :f0) 1000000) (simple-benchmark [coll pmap] (-lookup coll :f0 nil) 1000000) +(simple-benchmark [coll pmap] (-lookup ^not-native hash-imap-test :foo500 nil) 1000000) +(simple-benchmark [coll pmap] (-lookup ^not-native hash-imap-int-test 500 nil) 1000000) (simple-benchmark [coll pmap] (assoc coll :g0 32) 1000000) (simple-benchmark [coll pmap] (loop [i 0 m coll] From 8013f0fd0a7803c01efca90e757d316c0a1e1ec0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 4 Aug 2014 13:31:01 -0400 Subject: [PATCH 0098/4033] add new helper function `truthy-constant?` for `if` statement emission. If the `test` is a truthy constant just emit the `then` branch. This resolves Google Closure warnings about dead branches ahead of time. --- src/clj/cljs/compiler.clj | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 551ad810e..6d42c1594 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -357,26 +357,30 @@ (when-not (= :statement (:context env)) (emit-wrap env (emit-constant form)))) +(defn truthy-constant? [{:keys [op form]}] + (and (= op :constant) + form + (not (or (and (string? form) (= form "")) + (and (number? form) (zero? form)))))) + (defn safe-test? [env e] (let [tag (ana/infer-tag env e)] - (or (#{'boolean 'seq} tag) - (when (= (:op e) :constant) - (let [form (:form e)] - (not (or (and (string? form) (= form "")) - (and (number? form) (zero? form))))))))) + (or (#{'boolean 'seq} tag) (truthy-constant? e)))) (defmethod emit* :if [{:keys [test then else env unchecked]}] (let [context (:context env) checked (not (or unchecked (safe-test? env test)))] - (if (= :expr context) - (emits "(" (when checked "cljs.core.truth_") "(" test ")?" then ":" else ")") - (do - (if checked - (emitln "if(cljs.core.truth_(" test "))") - (emitln "if(" test ")")) - (emitln "{" then "} else") - (emitln "{" else "}"))))) + (if (truthy-constant? test) + (emitln then) + (if (= :expr context) + (emits "(" (when checked "cljs.core.truth_") "(" test ")?" then ":" else ")") + (do + (if checked + (emitln "if(cljs.core.truth_(" test "))") + (emitln "if(" test ")")) + (emitln "{" then "} else") + (emitln "{" else "}")))))) (defmethod emit* :case* [{:keys [v tests thens default env]}] From 8dc58229eca4f94672ccfef23241a1a952a1bab3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 4 Aug 2014 13:43:16 -0400 Subject: [PATCH 0099/4033] add corresponding optimization to if emission for falsey constants --- src/clj/cljs/compiler.clj | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 6d42c1594..5049366bb 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -363,6 +363,10 @@ (not (or (and (string? form) (= form "")) (and (number? form) (zero? form)))))) +(defn falsey-constant? [{:keys [op form]}] + (and (= op :constant) + (or (false? form) (nil? form)))) + (defn safe-test? [env e] (let [tag (ana/infer-tag env e)] (or (#{'boolean 'seq} tag) (truthy-constant? e)))) @@ -371,8 +375,10 @@ [{:keys [test then else env unchecked]}] (let [context (:context env) checked (not (or unchecked (safe-test? env test)))] - (if (truthy-constant? test) - (emitln then) + (cond + (truthy-constant? test) (emitln then) + (falsey-constant? test) (emitln else) + :else (if (= :expr context) (emits "(" (when checked "cljs.core.truth_") "(" test ")?" then ":" else ")") (do From 040bcd241dbb928479a4e0326f13bcbb3859390c Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Mon, 4 Aug 2014 15:51:56 +0200 Subject: [PATCH 0100/4033] Cleanup interop syntax used in the runtime lib * replaced all instances of cljs.core.DataStructure.EMPTY with (.-EMPTY DataStructure) * replaced all instances of js/foo.bar with (.bar js/foo) * replaced all instances of (.foo js/Math ..) with (Math/foo ..) * replaced all instances of (gstring/StringBuffer. ..) with (StringBuffer. ..), :import-ing StringBuffer in the ns * :import-ed all the gclosure types rather than :require-ing them * renamed the clojure.browser.event/EventType protocol to IEventType to avoid conflicts with goog.events.EventType --- src/clj/cljs/core.clj | 32 ++--- src/cljs/cljs/core.cljs | 200 ++++++++++++++-------------- src/cljs/cljs/reader.cljs | 15 ++- src/cljs/clojure/browser/event.cljs | 52 ++++---- src/cljs/clojure/browser/net.cljs | 38 +++--- src/cljs/clojure/string.cljs | 6 +- 6 files changed, 171 insertions(+), 172 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 74253ab73..97bdd53d0 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1002,7 +1002,7 @@ (. ~(first sig) ~slot ~@sig) (let [x# (if (nil? ~(first sig)) nil ~(first sig))] ((or - (aget ~(fqn fname) (goog.typeOf x#)) + (aget ~(fqn fname) (goog/typeOf x#)) (aget ~(fqn fname) "_") (throw (missing-protocol ~(core/str psym "." fname) ~(first sig)))) @@ -1385,23 +1385,23 @@ assoc :tag 'array)) (defmacro list - ([] `cljs.core.List.EMPTY) + ([] '(.-EMPTY cljs.core/List)) ([x & xs] `(-conj (list ~@xs) ~x))) (defmacro vector - ([] `cljs.core.PersistentVector.EMPTY) + ([] '(.-EMPTY cljs.core/PersistentVector)) ([& xs] (let [cnt (count xs)] (if (core/< cnt 32) - `(cljs.core.PersistentVector. nil ~cnt 5 - cljs.core.PersistentVector.EMPTY_NODE (array ~@xs) nil) + `(cljs.core/PersistentVector. nil ~cnt 5 + (.-EMPTY-NODE cljs.core/PersistentVector) (array ~@xs) nil) (vary-meta - `(cljs.core.PersistentVector.fromArray (array ~@xs) true) + `(.fromArray cljs.core/PersistentVector (array ~@xs) true) assoc :tag 'cljs.core/PersistentVector))))) (defmacro array-map - ([] `cljs.core.PersistentArrayMap.EMPTY) + ([] '(.-EMPTY cljs.core/PersistentArrayMap)) ([& kvs] (core/cond (core/> (count kvs) 16) @@ -1411,33 +1411,33 @@ (core/and (every? #(= (:op %) :constant) (map #(cljs.analyzer/analyze &env %) keys)) (= (count (into #{} keys)) (count keys)))) - `(cljs.core.PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) + `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) :else - `(cljs.core.PersistentArrayMap.fromArray (array ~@kvs) true false)))) + `(.fromArray cljs.core/PersistentArrayMap (array ~@kvs) true false)))) (defmacro hash-map - ([] `cljs.core.PersistentHashMap.EMPTY) + ([] `(.-EMPTY cljs.core/PersistentHashMap)) ([& kvs] (let [pairs (partition 2 kvs) ks (map first pairs) vs (map second pairs)] (vary-meta - `(cljs.core.PersistentHashMap.fromArrays (array ~@ks) (array ~@vs)) + `(.fromArrays cljs.core/PersistentHashMap (array ~@ks) (array ~@vs)) assoc :tag 'cljs.core/PersistentHashMap)))) (defmacro hash-set - ([] `cljs.core.PersistentHashSet.EMPTY) + ([] `(.-EMPTY cljs.core/PersistentHashSet)) ([& xs] (if (core/and (core/<= (count xs) 8) (every? #(= (:op %) :constant) (map #(cljs.analyzer/analyze &env %) xs)) (= (count (into #{} xs)) (count xs))) - `(cljs.core.PersistentHashSet. nil - (cljs.core.PersistentArrayMap. nil ~(count xs) (array ~@(interleave xs (repeat nil))) nil) + `(cljs.core/PersistentHashSet. nil + (cljs.core/PersistentArrayMap. nil ~(count xs) (array ~@(interleave xs (repeat nil))) nil) nil) (vary-meta - `(cljs.core.PersistentHashSet.fromArray (array ~@xs) true) + `(.fromArray cljs.core/PersistentHashSet (array ~@xs) true) assoc :tag 'cljs.core/PersistentHashSet)))) (defn js-obj* [kvs] @@ -1628,7 +1628,7 @@ on a fresh StringBuffer. Returns the string created by any nested printing calls." [& body] - `(let [sb# (goog.string/StringBuffer.)] + `(let [sb# (goog.string.StringBuffer.)] (binding [cljs.core/*print-fn* (fn [x#] (.append sb# x#))] ~@body) (cljs.core/str sb#))) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index bd29024cf..f04264048 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8,9 +8,9 @@ (ns cljs.core (:require [goog.string :as gstring] - [goog.string.StringBuffer :as gstringbuf] [goog.object :as gobject] - [goog.array :as garray])) + [goog.array :as garray]) + (:import goog.string.StringBuffer)) ;; next line is auto-generated by the build-script - Do not edit! (def *clojurescript-version*) @@ -52,7 +52,7 @@ (set! *print-newline* false) (set! *print-fn* (fn [& args] - (.apply js/console.log js/console (into-array args))))) + (.apply (.-log js/console) js/console (into-array args))))) (def ^{:doc "bound in a repl thread to the most recent value printed"} @@ -113,7 +113,7 @@ [p x] (let [x (if (nil? x) nil x)] (cond - (aget p (goog.typeOf x)) true + (aget p (goog/typeOf x)) true (aget p "_") true :else false))) (set! *unchecked-if* false) @@ -167,7 +167,7 @@ "Creates a new javascript array. @param {...*} var_args" ;;array is a special case, don't emulate this doc string [var-args] ;; [& items] - (.call (.-slice (.-prototype js/Array)) (cljs.core/js-arguments))) + (.. js/Array -prototype -slice (call (cljs.core/js-arguments)))) (declare apply) @@ -393,7 +393,7 @@ "Support so that collections can implement toString without loading all the printing machinery." [^not-native obj] - (let [sb (gstring/StringBuffer.) + (let [sb (StringBuffer.) writer (StringBufferWriter. sb)] (-pr-writer obj writer (pr-opts)) (-flush writer) @@ -410,7 +410,7 @@ ;; http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul (if (and (exists? Math/imul) (not (zero? (Math/imul 0xffffffff 5)))) - (defn ^number imul [a b] (js/Math.imul a b)) + (defn ^number imul [a b] (Math/imul a b)) (defn ^number imul [a b] (let [ah (bit-and (unsigned-bit-shift-right a 16) 0xffff) al (bit-and a 0xffff) @@ -503,7 +503,7 @@ (-hash ^not-native o) (number? o) - (js-mod (.floor js/Math o) 2147483647) + (js-mod (Math/floor o) 2147483647) (true? o) 1 @@ -831,7 +831,7 @@ reduces them without incurring seq initialization" (recur nval (inc n)))) val))))) -(declare hash-coll cons RSeq) +(declare hash-coll cons RSeq List) (defn ^boolean counted? "Returns true if coll implements count in constant time" @@ -888,7 +888,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] cljs.core.List.EMPTY) + (-empty [coll] (.-EMPTY List)) IReduce (-reduce [coll f] @@ -964,7 +964,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -1182,6 +1182,8 @@ reduces them without incurring seq initialization" :else not-found) not-found))) +(declare PersistentHashMap) + (defn assoc "assoc[iate]. When applied to a map, returns a new map of the same (hashed/sorted) type, that contains the mapping of key(s) to @@ -2001,7 +2003,7 @@ reduces them without incurring seq initialization" "" (.toString x))) ([x & ys] - (loop [sb (gstring/StringBuffer. (str x)) more ys] + (loop [sb (StringBuffer. (str x)) more ys] (if more (recur (. sb (append (str (first more)))) (next more)) (.toString sb))))) @@ -2109,7 +2111,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (List. meta o coll (inc count) nil)) IEmptyableCollection - (-empty [coll] cljs.core.List.EMPTY) + (-empty [coll] (.-EMPTY List)) ISequential IEquiv @@ -2180,7 +2182,7 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) -(set! cljs.core.List.EMPTY (EmptyList. nil)) +(set! (.-EMPTY List) (EmptyList. nil)) (defn ^boolean reversible? [coll] (satisfies? IReversible coll)) @@ -2241,7 +2243,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (Cons. nil o coll __hash)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) ISequential IEquiv @@ -2373,7 +2375,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) ISequential IEquiv @@ -2504,7 +2506,7 @@ reduces them without incurring seq initialization" (cons o this)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash))) @@ -3534,12 +3536,12 @@ reduces them without incurring seq initialization" (-pop [coll] (cond (zero? cnt) (throw (js/Error. "Can't pop empty vector")) - (== 1 cnt) (-with-meta cljs.core.PersistentVector.EMPTY meta) + (== 1 cnt) (-with-meta (.-EMPTY PersistentVector) meta) (< 1 (- cnt (tail-off coll))) (PersistentVector. meta (dec cnt) shift root (.slice tail 0 -1) nil) :else (let [new-tail (unchecked-array-for coll (- cnt 2)) nr (pop-tail coll shift root) - new-root (if (nil? nr) cljs.core.PersistentVector.EMPTY_NODE nr) + new-root (if (nil? nr) (.-EMPTY-NODE PersistentVector) nr) cnt-1 (dec cnt)] (if (and (< 5 shift) (nil? (pv-aget new-root 1))) (PersistentVector. meta cnt-1 (- shift 5) (pv-aget new-root 0) new-tail nil) @@ -3565,7 +3567,7 @@ reduces them without incurring seq initialization" (PersistentVector. meta (inc cnt) new-shift new-root (array o) nil)))) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.PersistentVector.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY PersistentVector) meta)) ISequential IEquiv @@ -3664,19 +3666,19 @@ reduces them without incurring seq initialization" (if (pos? cnt) (RSeq. coll (dec cnt) nil)))) -(set! cljs.core.PersistentVector.EMPTY_NODE (VectorNode. nil (make-array 32))) +(set! (.-EMPTY-NODE PersistentVector) (VectorNode. nil (make-array 32))) -(set! cljs.core.PersistentVector.EMPTY - (PersistentVector. nil 0 5 cljs.core.PersistentVector.EMPTY_NODE (array) 0)) +(set! (.-EMPTY PersistentVector) + (PersistentVector. nil 0 5 (.-EMPTY-NODE PersistentVector) (array) 0)) -(set! cljs.core.PersistentVector.fromArray +(set! (.-fromArray PersistentVector) (fn [xs ^boolean no-clone] (let [l (alength xs) xs (if no-clone xs (aclone xs))] (if (< l 32) - (PersistentVector. nil l 5 cljs.core.PersistentVector.EMPTY_NODE xs nil) + (PersistentVector. nil l 5 (.-EMPTY-NODE PersistentVector) xs nil) (let [node (.slice xs 0 32) - v (PersistentVector. nil 32 5 cljs.core.PersistentVector.EMPTY_NODE node nil)] + v (PersistentVector. nil 32 5 (.-EMPTY-NODE PersistentVector) node nil)] (loop [i 32 out (-as-transient v)] (if (< i l) (recur (inc i) (conj! out (aget xs i))) @@ -3685,12 +3687,12 @@ reduces them without incurring seq initialization" (defn vec [coll] (-persistent! (reduce -conj! - (-as-transient cljs.core.PersistentVector.EMPTY) + (-as-transient (.-EMPTY PersistentVector)) coll))) (defn vector [& args] (if (and (instance? IndexedSeq args) (zero? (.-i args))) - (cljs.core.PersistentVector.fromArray (.-arr args) true) + (.fromArray PersistentVector (.-arr args) true) (vec args))) (declare subvec) @@ -3741,7 +3743,7 @@ reduces them without incurring seq initialization" IEmptyableCollection (-empty [coll] - (with-meta cljs.core.PersistentVector.EMPTY meta)) + (with-meta (.-EMPTY PersistentVector) meta)) IChunkedSeq (-chunked-first [coll] @@ -3805,7 +3807,7 @@ reduces them without incurring seq initialization" (build-subvec meta (-assoc-n v end o) start (inc end) nil)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.PersistentVector.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY PersistentVector) meta)) ISequential IEquiv @@ -4106,7 +4108,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) ISequential IEquiv @@ -4154,7 +4156,7 @@ reduces them without incurring seq initialization" (PersistentQueue. meta (inc count) (conj front o) [] nil))) IEmptyableCollection - (-empty [coll] cljs.core.PersistentQueue.EMPTY) + (-empty [coll] (.-EMPTY PersistentQueue)) ISequential IEquiv @@ -4172,7 +4174,7 @@ reduces them without incurring seq initialization" ICounted (-count [coll] count)) -(set! cljs.core.PersistentQueue.EMPTY (PersistentQueue. nil 0 nil [] 0)) +(set! (.-EMPTY PersistentQueue) (PersistentQueue. nil 0 nil [] 0)) (deftype NeverEquiv [] Object @@ -4225,7 +4227,7 @@ reduces them without incurring seq initialization" so (.-strobj m) mm (meta m)] (loop [i 0 - out (transient cljs.core.PersistentHashMap.EMPTY)] + out (transient (.-EMPTY PersistentHashMap))] (if (< i len) (let [k (aget ks i)] (recur (inc i) (assoc! out k (aget so k)))) @@ -4265,7 +4267,7 @@ reduces them without incurring seq initialization" entry))) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.ObjMap.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY ObjMap) meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -4293,8 +4295,8 @@ reduces them without incurring seq initialization" IAssociative (-assoc [coll k v] (if ^boolean (goog/isString k) - (if (or (> update-count cljs.core.ObjMap.HASHMAP_THRESHOLD) - (>= (alength keys) cljs.core.ObjMap.HASHMAP_THRESHOLD)) + (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)] @@ -4347,11 +4349,11 @@ reduces them without incurring seq initialization" (-as-transient [coll] (transient (into (hash-map) coll)))) -(set! cljs.core.ObjMap.EMPTY (ObjMap. nil (array) (js-obj) 0 0)) +(set! (.-EMPTY ObjMap) (ObjMap. nil (array) (js-obj) 0 0)) -(set! cljs.core.ObjMap.HASHMAP_THRESHOLD 8) +(set! (.-HASHMAP_THRESHOLD ObjMap) 8) -(set! cljs.core.ObjMap.fromObject (fn [ks obj] (ObjMap. nil ks obj 0 nil))) +(set! (.-fromObject ObjMap) (fn [ks obj] (ObjMap. nil ks obj 0 nil))) ;; EXPERIMENTAL: subject to change (deftype Iterator [^:mutable s] @@ -4499,7 +4501,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY _meta)) + (-empty [coll] (with-meta (.-EMPTY List) _meta)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -4573,7 +4575,7 @@ reduces them without incurring seq initialization" (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection - (-empty [coll] (-with-meta cljs.core.PersistentArrayMap.EMPTY meta)) + (-empty [coll] (-with-meta (.-EMPTY PersistentArrayMap) meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -4603,10 +4605,10 @@ reduces them without incurring seq initialization" (let [idx (array-map-index-of coll k)] (cond (== idx -1) - (if (< cnt cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD) + (if (< cnt (.-HASHMAP-THRESHOLD PersistentArrayMap)) (let [arr (array-map-extend-kv coll k v)] (PersistentArrayMap. meta (inc cnt) arr nil)) - (-> (into cljs.core.PersistentHashMap.EMPTY coll) + (-> (into (.-EMPTY PersistentHashMap) coll) (-assoc k v) (-with-meta meta))) @@ -4667,19 +4669,19 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientArrayMap. (js-obj) (alength arr) (aclone arr)))) -(set! cljs.core.PersistentArrayMap.EMPTY (PersistentArrayMap. nil 0 (array) nil)) +(set! (.-EMPTY PersistentArrayMap) (PersistentArrayMap. nil 0 (array) nil)) -(set! cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD 8) +(set! (.-HASHMAP-THRESHOLD PersistentArrayMap) 8) -(set! cljs.core.PersistentArrayMap.fromArray +(set! (.-fromArray PersistentArrayMap) (fn [arr ^boolean no-clone ^boolean no-check] - (let [arr (if no-clone arr (aclone arr))] + (let [arr (if no-clone arr (aclone arr))] (if no-check (let [cnt (/ (alength arr) 2)] (PersistentArrayMap. nil cnt arr nil)) (let [len (alength arr)] (loop [i 0 - ret (transient cljs.core.PersistentArrayMap.EMPTY)] + ret (transient (.-EMPTY PersistentArrayMap))] (if (< i len) (recur (+ i 2) (-assoc! ret (aget arr i) (aget arr (inc i)))) @@ -4731,7 +4733,7 @@ reduces them without incurring seq initialization" (if editable? (let [idx (array-map-index-of tcoll key)] (if (== idx -1) - (if (<= (+ len 2) (* 2 cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD)) + (if (<= (+ len 2) (* 2 (.-HASHMAP-THRESHOLD PersistentArrayMap))) (do (set! len (+ len 2)) (.push arr key) (.push arr val) @@ -4758,7 +4760,7 @@ reduces them without incurring seq initialization" (declare TransientHashMap PersistentHashMap) (defn- array->transient-hash-map [len arr] - (loop [out (transient cljs.core.PersistentHashMap.EMPTY) + (loop [out (transient (.-EMPTY PersistentHashMap)) i 0] (if (< i len) (recur (assoc! out (aget arr i) (aget arr (inc i))) (+ i 2)) @@ -4839,14 +4841,14 @@ reduces them without incurring seq initialization" (if (>= n 16) (let [nodes (make-array 32) jdx (mask hash shift)] - (aset nodes jdx (.inode-assoc cljs.core.BitmapIndexedNode.EMPTY (+ shift 5) hash key val added-leaf?)) + (aset nodes jdx (.inode-assoc (.-EMPTY BitmapIndexedNode) (+ shift 5) hash key val added-leaf?)) (loop [i 0 j 0] (if (< i 32) (if (zero? (bit-and (bit-shift-right-zero-fill bitmap i) 1)) (recur (inc i) j) (do (aset nodes i (if-not (nil? (aget arr j)) - (.inode-assoc cljs.core.BitmapIndexedNode.EMPTY + (.inode-assoc (.-EMPTY BitmapIndexedNode) (+ shift 5) (cljs.core/hash (aget arr j)) (aget arr j) (aget arr (inc j)) added-leaf?) (aget arr (inc j)))) (recur (inc i) (+ j 2)))))) @@ -4962,14 +4964,14 @@ reduces them without incurring seq initialization" (>= n 16) (let [nodes (make-array 32) jdx (mask hash shift)] - (aset nodes jdx (.inode-assoc! cljs.core.BitmapIndexedNode.EMPTY edit (+ shift 5) hash key val added-leaf?)) + (aset nodes jdx (.inode-assoc! (.-EMPTY BitmapIndexedNode) edit (+ shift 5) hash key val added-leaf?)) (loop [i 0 j 0] (if (< i 32) (if (zero? (bit-and (bit-shift-right-zero-fill bitmap i) 1)) (recur (inc i) j) (do (aset nodes i (if-not (nil? (aget arr j)) - (.inode-assoc! cljs.core.BitmapIndexedNode.EMPTY + (.inode-assoc! (.-EMPTY BitmapIndexedNode) edit (+ shift 5) (cljs.core/hash (aget arr j)) (aget arr j) (aget arr (inc j)) added-leaf?) (aget arr (inc j)))) (recur (inc i) (+ j 2)))))) @@ -5025,7 +5027,7 @@ reduces them without incurring seq initialization" (kv-reduce [inode f init] (inode-kv-reduce arr f init))) -(set! cljs.core.BitmapIndexedNode.EMPTY (BitmapIndexedNode. nil 0 (make-array 0))) +(set! (.-EMPTY BitmapIndexedNode) (BitmapIndexedNode. nil 0 (make-array 0))) (defn- pack-array-node [array-node edit idx] (let [arr (.-arr array-node) @@ -5046,7 +5048,7 @@ reduces them without incurring seq initialization" (let [idx (mask hash shift) node (aget arr idx)] (if (nil? node) - (ArrayNode. nil (inc cnt) (clone-and-set arr idx (.inode-assoc cljs.core.BitmapIndexedNode.EMPTY (+ shift 5) hash key val added-leaf?))) + (ArrayNode. nil (inc cnt) (clone-and-set arr idx (.inode-assoc (.-EMPTY BitmapIndexedNode) (+ shift 5) hash key val added-leaf?))) (let [n (.inode-assoc node (+ shift 5) hash key val added-leaf?)] (if (identical? n node) inode @@ -5096,7 +5098,7 @@ reduces them without incurring seq initialization" (let [idx (mask hash shift) node (aget arr idx)] (if (nil? node) - (let [editable (edit-and-set inode edit idx (.inode-assoc! cljs.core.BitmapIndexedNode.EMPTY edit (+ shift 5) hash key val added-leaf?))] + (let [editable (edit-and-set inode edit idx (.inode-assoc! (.-EMPTY BitmapIndexedNode) edit (+ shift 5) hash key val added-leaf?))] (set! (.-cnt editable) (inc (.-cnt editable))) editable) (let [n (.inode-assoc! node edit (+ shift 5) hash key val added-leaf?)] @@ -5250,7 +5252,7 @@ reduces them without incurring seq initialization" (if (== key1hash key2hash) (HashCollisionNode. nil key1hash 2 (array key1 val1 key2 val2)) (let [added-leaf? (Box. false)] - (-> cljs.core.BitmapIndexedNode.EMPTY + (-> (.-EMPTY BitmapIndexedNode) (.inode-assoc shift key1hash key1 val1 added-leaf?) (.inode-assoc shift key2hash key2 val2 added-leaf?)))))) ([edit shift key1 val1 key2hash key2 val2] @@ -5258,7 +5260,7 @@ reduces them without incurring seq initialization" (if (== key1hash key2hash) (HashCollisionNode. nil key1hash 2 (array key1 val1 key2 val2)) (let [added-leaf? (Box. false)] - (-> cljs.core.BitmapIndexedNode.EMPTY + (-> (.-EMPTY BitmapIndexedNode) (.inode-assoc! edit shift key1hash key1 val1 added-leaf?) (.inode-assoc! edit shift key2hash key2 val2 added-leaf?))))))) @@ -5279,13 +5281,13 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) ICollection (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) ISequential ISeq @@ -5346,13 +5348,13 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) ICollection (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) ISequential ISeq @@ -5433,7 +5435,7 @@ reduces them without incurring seq initialization" (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection - (-empty [coll] (-with-meta cljs.core.PersistentHashMap.EMPTY meta)) + (-empty [coll] (-with-meta (.-EMPTY PersistentHashMap) meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -5471,7 +5473,7 @@ reduces them without incurring seq initialization" (PersistentHashMap. meta (if has-nil? cnt (inc cnt)) root true v nil)) (let [added-leaf? (Box. false) new-root (-> (if (nil? root) - cljs.core.BitmapIndexedNode.EMPTY + (.-EMPTY BitmapIndexedNode) root) (.inode-assoc 0 (hash k) k v added-leaf?))] (if (identical? new-root root) @@ -5515,12 +5517,12 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientHashMap. (js-obj) root cnt has-nil? nil-val))) -(set! cljs.core.PersistentHashMap.EMPTY (PersistentHashMap. nil 0 nil false nil 0)) +(set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil 0)) -(set! cljs.core.PersistentHashMap.fromArrays +(set! (.-fromArrays PersistentHashMap) (fn [ks vs] (let [len (alength ks)] - (loop [i 0 ^not-native out (transient cljs.core.PersistentHashMap.EMPTY)] + (loop [i 0 ^not-native out (transient (.-EMPTY PersistentHashMap))] (if (< i len) (recur (inc i) (-assoc! out (aget ks i) (aget vs i))) (persistent! out)))))) @@ -5555,7 +5557,7 @@ reduces them without incurring seq initialization" tcoll) (let [added-leaf? (Box. false) node (-> (if (nil? root) - cljs.core.BitmapIndexedNode.EMPTY + (.-EMPTY BitmapIndexedNode) root) (.inode-assoc! edit 0 (hash k) k v added-leaf?))] (if (identical? node root) @@ -5672,7 +5674,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY List) meta)) IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) @@ -6197,7 +6199,7 @@ reduces them without incurring seq initialization" (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.PersistentTreeMap.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY PersistentTreeMap) meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -6290,13 +6292,13 @@ reduces them without incurring seq initialization" (-comparator [coll] comp)) -(set! cljs.core.PersistentTreeMap.EMPTY (PersistentTreeMap. compare nil 0 nil 0)) +(set! (.-EMPTY PersistentTreeMap) (PersistentTreeMap. compare nil 0 nil 0)) (defn hash-map "keyval => key val Returns a new hash map with supplied mappings." [& keyvals] - (loop [in (seq keyvals), out (transient cljs.core.PersistentHashMap.EMPTY)] + (loop [in (seq keyvals), out (transient (.-EMPTY PersistentHashMap))] (if in (recur (nnext in) (assoc! out (first in) (second in))) (persistent! out)))) @@ -6318,13 +6320,13 @@ reduces them without incurring seq initialization" (do (.push ks (first kvs)) (aset obj (first kvs) (second kvs)) (recur (nnext kvs))) - (cljs.core.ObjMap.fromObject ks obj))))) + (.fromObject ObjMap ks obj))))) (defn sorted-map "keyval => key val Returns a new sorted map with supplied mappings." ([& keyvals] - (loop [in (seq keyvals) out cljs.core.PersistentTreeMap.EMPTY] + (loop [in (seq keyvals) out (.-EMPTY PersistentTreeMap)] (if in (recur (nnext in) (assoc out (first in) (second in))) out)))) @@ -6334,7 +6336,7 @@ reduces them without incurring seq initialization" Returns a new sorted map with supplied mappings, using the supplied comparator." ([comparator & keyvals] (loop [in (seq keyvals) - out (cljs.core.PersistentTreeMap. (fn->comparator comparator) nil 0 nil 0)] + out (PersistentTreeMap. (fn->comparator comparator) nil 0 nil 0)] (if in (recur (nnext in) (assoc out (first in) (second in))) out)))) @@ -6364,7 +6366,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY _meta)) + (-empty [coll] (with-meta (.-EMPTY List) _meta)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -6430,7 +6432,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.List.EMPTY _meta)) + (-empty [coll] (with-meta (.-EMPTY List) _meta)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -6547,7 +6549,7 @@ reduces them without incurring seq initialization" (PersistentHashSet. meta (assoc hash-map o nil) nil)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.PersistentHashSet.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY PersistentHashSet) meta)) IEquiv (-equiv [coll other] @@ -6587,21 +6589,21 @@ reduces them without incurring seq initialization" IEditableCollection (-as-transient [coll] (TransientHashSet. (-as-transient hash-map)))) -(set! cljs.core.PersistentHashSet.EMPTY - (PersistentHashSet. nil cljs.core.PersistentArrayMap.EMPTY 0)) +(set! (.-EMPTY PersistentHashSet) + (PersistentHashSet. nil (.-EMPTY PersistentArrayMap) 0)) -(set! cljs.core.PersistentHashSet.fromArray +(set! (.-fromArray PersistentHashSet) (fn [items ^boolean no-clone] (let [len (alength items)] - (if (<= len cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD) + (if (<= len (.-HASHMAP-THRESHOLD PersistentArrayMap)) (let [arr (if no-clone items (aclone items))] (loop [i 0 - out (transient cljs.core.PersistentArrayMap.EMPTY)] + out (transient (.-EMPTY PersistentArrayMap))] (if (< i len) (recur (inc i) (-assoc! out (aget items i) nil)) - (cljs.core.PersistentHashSet. nil (-persistent! out) nil)))) + (PersistentHashSet. nil (-persistent! out) nil)))) (loop [i 0 - out (transient cljs.core.PersistentHashSet.EMPTY)] + out (transient (.-EMPTY PersistentHashSet))] (if (< i len) (recur (inc i) (-conj! out (aget items i))) (-persistent! out))))))) @@ -6677,7 +6679,7 @@ reduces them without incurring seq initialization" (PersistentTreeSet. meta (assoc tree-map o nil) nil)) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.PersistentTreeSet.EMPTY meta)) + (-empty [coll] (with-meta (.-EMPTY PersistentTreeSet) meta)) IEquiv (-equiv [coll other] @@ -6731,8 +6733,8 @@ reduces them without incurring seq initialization" (-invoke [coll k not-found] (-lookup coll k not-found))) -(set! cljs.core.PersistentTreeSet.EMPTY - (PersistentTreeSet. nil cljs.core.PersistentTreeMap.EMPTY 0)) +(set! (.-EMPTY PersistentTreeSet) + (PersistentTreeSet. nil (.-EMPTY PersistentTreeMap) 0)) (defn set-from-indexed-seq [iseq] (let [arr (.-arr iseq) @@ -6764,13 +6766,13 @@ reduces them without incurring seq initialization" (defn sorted-set "Returns a new sorted set with supplied keys." ([& keys] - (reduce -conj cljs.core.PersistentTreeSet.EMPTY keys))) + (reduce -conj (.-EMPTY PersistentTreeSet) keys))) (defn sorted-set-by "Returns a new sorted set with supplied keys, using the supplied comparator." ([comparator & keys] (reduce -conj - (cljs.core.PersistentTreeSet. nil (sorted-map-by comparator) 0) + (PersistentTreeSet. nil (sorted-map-by comparator) 0) keys))) (defn replace @@ -6941,7 +6943,7 @@ reduces them without incurring seq initialization" (-conj [rng o] (cons o rng)) IEmptyableCollection - (-empty [rng] (with-meta cljs.core.List.EMPTY meta)) + (-empty [rng] (with-meta (.-EMPTY List) meta)) ISequential IEquiv @@ -6954,7 +6956,7 @@ reduces them without incurring seq initialization" (-count [rng] (if-not (-seq rng) 0 - (js/Math.ceil (/ (- end start) step)))) + (Math/ceil (/ (- end start) step)))) IIndexed (-nth [rng n] @@ -6978,7 +6980,7 @@ reduces them without incurring seq initialization" "Returns a lazy seq of nums from start (inclusive) to end (exclusive), by step, where start defaults to 0, step to 1, and end to infinity." - ([] (range 0 js/Number.MAX_VALUE 1)) + ([] (range 0 (.-MAX-VALUE js/Number) 1)) ([end] (range 0 end 1)) ([start end] (range start end 1)) ([start end step] (Range. nil start end step nil))) @@ -7267,7 +7269,7 @@ reduces them without incurring seq initialization" (pr-writer obj writer opts))) (defn- pr-sb-with-opts [objs opts] - (let [sb (gstring/StringBuffer.) + (let [sb (StringBuffer.) writer (StringBufferWriter. sb)] (pr-seq-writer objs writer opts) (-flush writer) @@ -7671,7 +7673,7 @@ reduces them without incurring seq initialization" (defn ^boolean delay? "returns true if x is a Delay created with delay" - [x] (instance? cljs.core.Delay x)) + [x] (instance? Delay x)) (defn force "If x is a Delay, returns the (possibly cached) value of its expression, else returns x" @@ -8224,8 +8226,8 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype ExceptionInfo [message data cause]) ;;; ExceptionInfo is a special case, do not emulate this -(set! cljs.core.ExceptionInfo.prototype (js/Error.)) -(set! (.-constructor cljs.core.ExceptionInfo.prototype) ExceptionInfo) +(set! (.-prototype ExceptionInfo) (js/Error.)) +(set! (.. ExceptionInfo -prototype -constructor) ExceptionInfo) (defn ex-info "Alpha - subject to change. diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index a115063ea..d122b87e8 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -7,7 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.reader - (:require [goog.string :as gstring])) + (:require [goog.string :as gstring]) + (:import goog.string.StringBuffer)) (defprotocol PushbackReader (read-char [reader] "Returns the next char from the Reader, @@ -77,12 +78,12 @@ nil if the end of stream has been reached") (defn read-token [rdr initch] - (loop [sb (gstring/StringBuffer. initch) + (loop [sb (StringBuffer. initch) ch (read-char rdr)] (if (or (nil? ch) (whitespace? ch) (macro-terminating? ch)) - (do (unread rdr ch) (. sb (toString))) + (do (unread rdr ch) (.toString sb)) (recur (do (.append sb ch) sb) (read-char rdr))))) (defn skip-line @@ -164,13 +165,13 @@ nil if the end of stream has been reached") (defn read-2-chars [reader] (.toString - (gstring/StringBuffer. + (StringBuffer. (read-char reader) (read-char reader)))) (defn read-4-chars [reader] (.toString - (gstring/StringBuffer. + (StringBuffer. (read-char reader) (read-char reader) (read-char reader) @@ -283,7 +284,7 @@ nil if the end of stream has been reached") (if (or (nil? ch) (whitespace? ch) (macros ch)) (do (unread reader ch) - (let [s (. buffer (toString))] + (let [s (.toString buffer)] (or (match-number s) (reader-error reader "Invalid number format [" s "]")))) (recur (do (.append buffer ch) buffer) (read-char reader))))) @@ -447,7 +448,7 @@ nil if the end of stream has been reached") (defn ^:private zero-fill-right-and-truncate [s width] (cond (= width (count s)) s (< width (count s)) (subs s 0 width) - :else (loop [b (gstring/StringBuffer. s)] + :else (loop [b (StringBuffer. s)] (if (< (.getLength b) width) (recur (.append b "0")) (.toString b))))) diff --git a/src/cljs/clojure/browser/event.cljs b/src/cljs/clojure/browser/event.cljs index 449ed5bbf..864df81a3 100644 --- a/src/cljs/clojure/browser/event.cljs +++ b/src/cljs/clojure/browser/event.cljs @@ -10,25 +10,24 @@ events. It is based on the Google Closure Library event system." :author "Bobby Calderwood"} clojure.browser.event - (:require [goog.events :as events] - [goog.events.EventTarget :as gevent-target] - [goog.events.EventType :as gevent-type])) + (:require [goog.events :as events]) + (:import (goog.events EventTarget EventType))) -(defprotocol EventType +(defprotocol IEventType (event-types [this])) -(extend-protocol EventType +(extend-protocol IEventType - goog.events.EventTarget + EventTarget (event-types [this] (into {} (map (fn [[k v]] - [(keyword (. k (toLowerCase))) + [(keyword (.toLowerCase k)) v]) (merge - (js->clj goog.events.EventType))))) + (js->clj EventType))))) js/Element (event-types @@ -36,54 +35,54 @@ events. It is based on the Google Closure Library event system." (into {} (map (fn [[k v]] - [(keyword (. k (toLowerCase))) + [(keyword (.toLowerCase k)) v]) (merge - (js->clj goog.events.EventType)))))) + (js->clj EventType)))))) (defn listen ([src type fn] (listen src type fn false)) ([src type fn capture?] - (goog.events/listen src - (get (event-types src) type type) - fn - capture?))) + (events/listen src + (get (event-types src) type type) + fn + capture?))) (defn listen-once ([src type fn] (listen-once src type fn false)) ([src type fn capture?] - (goog.events/listenOnce src - (get (event-types src) type type) - fn - capture?))) + (events/listenOnce src + (get (event-types src) type type) + fn + capture?))) (defn unlisten ([src type fn] (unlisten src type fn false)) ([src type fn capture?] - (goog.events/unlisten src - (get (event-types src) type type) - fn - capture?))) + (events/unlisten src + (get (event-types src) type type) + fn + capture?))) (defn unlisten-by-key [key] - (goog.events/unlistenByKey key)) + (events/unlistenByKey key)) (defn dispatch-event [src event] - (goog.events/dispatchEvent src event)) + (events/dispatchEvent src event)) (defn expose [e] - (goog.events/expose e)) + (events/expose e)) (defn fire-listeners [obj type capture event]) (defn total-listener-count [] - (goog.events/getTotalListenerCount)) + (events/getTotalListenerCount)) ;; TODO (defn get-listener [src type listener opt_capt opt_handler]); ⇒ ?Listener @@ -97,4 +96,3 @@ events. It is based on the Google Closure Library event system." (defn remove-all [opt_obj opt_type opt_capt]); ⇒ number ;; TODO? (defn unlisten-with-wrapper [src wrapper listener opt_capt opt_handler]) - diff --git a/src/cljs/clojure/browser/net.cljs b/src/cljs/clojure/browser/net.cljs index 29769e2d2..52865d5b6 100644 --- a/src/cljs/clojure/browser/net.cljs +++ b/src/cljs/clojure/browser/net.cljs @@ -11,12 +11,10 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." :author "Bobby Calderwood and Alex Redington"} clojure.browser.net (:require [clojure.browser.event :as event] - [goog.net.XhrIo :as gxhrio] - [goog.net.EventType :as gnet-event-type] - [goog.net.xpc.CfgFields :as gxpc-config-fields] - [goog.net.xpc.CrossPageChannel :as xpc] - #_[goog.net.WebSocket :as gwebsocket] - [goog.json :as gjson])) + [goog.json :as gjson]) + (:import (goog.net XhrIo EventType) + (goog.net.xpc CfgFields CrossPageChannel) + goog.Uri)) (def *timeout* 10000) @@ -24,10 +22,10 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." (into {} (map (fn [[k v]] - [(keyword (. k (toLowerCase))) + [(keyword (.toLowerCase k)) v]) (merge - (js->clj goog.net.EventType))))) + (js->clj EventType))))) (defprotocol IConnection (connect @@ -43,7 +41,7 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." [this opt opt2 opt3 opt4 opt5]) (close [this])) -(extend-type goog.net.XhrIo +(extend-type XhrIo IConnection (transmit @@ -65,10 +63,10 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." (into {} (map (fn [[k v]] - [(keyword (. k (toLowerCase))) + [(keyword (.toLowerCase k)) v]) (merge - (js->clj goog.net.EventType)))))) + (js->clj EventType)))))) ;; TODO jQuery/sinatra/RestClient style API: (get [uri]), (post [uri payload]), (put [uri payload]), (delete [uri]) @@ -76,19 +74,19 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." (into {} (map (fn [[k v]] - [(keyword (. k (toLowerCase))) + [(keyword (.toLowerCase k)) v]) - (js->clj goog.net.xpc.CfgFields)))) + (js->clj CfgFields)))) (defn xhr-connection "Returns an XhrIo connection" [] - (goog.net.XhrIo.)) + (XhrIo.)) (defprotocol ICrossPageChannel (register-service [this service-name fn] [this service-name fn encode-json?])) -(extend-type goog.net.xpc.CrossPageChannel +(extend-type CrossPageChannel ICrossPageChannel (register-service @@ -113,7 +111,7 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." (.send this (name service-name) payload)) (close [this] - (.close this ()))) + (.close this))) (defn xpc-connection "When passed with a config hash-map, returns a parent @@ -127,11 +125,11 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." per the CrossPageChannel API." ([] (when-let [config (.getParameterValue - (goog.Uri. (.-href (.-location js/window))) + (Uri. (.-href (.-location js/window))) "xpc")] - (goog.net.xpc.CrossPageChannel. (gjson/parse config)))) + (CrossPageChannel. (gjson/parse config)))) ([config] - (goog.net.xpc.CrossPageChannel. + (CrossPageChannel. (reduce (fn [sum [k v]] (if-let [field (get xpc-config-fields k)] (doto sum (aset field v)) @@ -180,4 +178,4 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." ([auto-reconnect?] (websocket-connection auto-reconnect? nil)) ([auto-reconnect? next-reconnect-fn] - (goog.net.WebSocket. auto-reconnect? next-reconnect-fn))) \ No newline at end of file + (goog.net.WebSocket. auto-reconnect? next-reconnect-fn))) diff --git a/src/cljs/clojure/string.cljs b/src/cljs/clojure/string.cljs index 0ced53624..1b94fa257 100644 --- a/src/cljs/clojure/string.cljs +++ b/src/cljs/clojure/string.cljs @@ -8,8 +8,8 @@ (ns clojure.string (:refer-clojure :exclude [replace reverse]) - (:require [goog.string :as gstring] - [goog.string.StringBuffer :as gstringbuf])) + (:require [goog.string :as gstring]) + (:import goog.string.StringBuffer)) (defn- seq-reverse [coll] @@ -168,7 +168,7 @@ If (cmap ch) is nil, append ch to the new string. If (cmap ch) is non-nil, append (str (cmap ch)) instead." [s cmap] - (let [buffer (gstring/StringBuffer.) + (let [buffer (StringBuffer.) length (.-length s)] (loop [index 0] (if (= length index) From d15e73637beeac101c24228b09aec1039264984b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 Aug 2014 20:11:01 -0400 Subject: [PATCH 0101/4033] transducers wip --- src/cljs/cljs/core.cljs | 896 ++++++++++++++++++++++++++-------- test/cljs/cljs/core_test.cljs | 6 + 2 files changed, 703 insertions(+), 199 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index f04264048..07ccd7c4a 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -382,6 +382,14 @@ (^string -name [x]) (^string -namespace [x])) +(defprotocol IAtom) + +(defprotocol IReset + (-reset! [o new-value])) + +(defprotocol ISwap + (-swap! [o f] [o f a] [o f a b] [o f a b xs])) + ;; Printing support (deftype StringBufferWriter [sb] @@ -1014,6 +1022,8 @@ reduces them without incurring seq initialization" "conj[oin]. Returns a new collection with the xs 'added'. (conj nil item) returns (item). The 'addition' may happen at different 'places' depending on the concrete type." + ([] []) + ([coll] coll) ([coll x] (if-not (nil? coll) (-conj coll x) @@ -1638,6 +1648,26 @@ reduces them without incurring seq initialization" (-kv-reduce coll f init) init))) +(defn- completing [f] + (fn + ([] (f)) + ([x] x) + ([x y] (f x y)))) + +(defn transduce + "reduce with a transformation of f (xf). If init is not + supplied, (f) will be called to produce it. Returns the result of + applying (the transformed) xf to init and the first item in coll, + then applying xf to that result and the 2nd item, etc. If coll + contains no items, returns init and f is not called. Note that + certain transforms may inject or skip items." + ([xform f coll] (transduce xform f (f) coll)) + ([xform f init coll] + (let [f (xform (completing f)) + ret (reduce coll f init) + ret (f (if (reduced? ret) @ret ret))] + (if (reduced? ret) @ret ret)))) + ;;; Math - variadic forms will not work until the following implemented: ;;; first, next, reduce @@ -2705,6 +2735,8 @@ reduces them without incurring seq initialization" (defn conj! "Adds x to the transient collection, and return coll. The 'addition' may happen at different 'places' depending on the concrete type." + ([] (transient [])) + ([coll] coll) ([tcoll val] (-conj! tcoll val)) ([tcoll val & vals] @@ -2835,6 +2867,219 @@ reduces them without incurring seq initialization" "If coll is empty, returns nil, else coll" [coll] (when (seq coll) coll)) +(defn nil-iter [] + (reify + Object + (hasNext [_] false) + (next [_] (js/Error. "No such element")) + (remove [_] (js/Error. "Unsupported operation")))) + +(deftype StringIter [s ^:mutable i] + Object + (hasNext [_] (< i (alength s))) + (next [_] + (let [ret (.charAt s i)] + (set! i (inc i)) + ret)) + (remove [_] (js/Error. "Unsupported operation"))) + +(defn string-iter [x] + (StringIter. x 0)) + +(deftype ArrayIter [s ^:mutable i] + Object + (hasNext [_] (< i (alength s))) + (next [_] + (let [ret (.aget s i)] + (set! i (inc i)) + ret)) + (remove [_] (js/Error. "Unsupported operation"))) + +(defn array-iter [x] + (ArrayIter. x 0)) + +(deftype SeqIter [^:mutable seq] + Object + (hasNext [_] (not (nil? seq))) + (next [_] + (let [first (first seq)] + (set! seq (next seq)) + first)) + (remove [_] (js/Error. "Unsupported operation"))) + +(defn seq-iter [seq] + (SeqIter. seq)) + +(defn iter [coll] + (cond + (nil? coll) (nil-iter) + (string? coll) (string-iter coll) + (array? coll) (array-iter coll) + :else (seq-iter (seq coll)))) + +(declare lazy-transformer) + +(deftype Stepper [xform iter] + Object + (step [this lt] + (loop [] + (if (and (not (nil? (.-stepper lt))) + (.hasNext iter)) + (when-not (reduced? (xform lt (.next iter))) + (recur)))) + (when (nil? (.-stepper lt)) + (xform lt)))) + +(defn stepper [xform iter] + (letfn [(stepfn + ([result] + (let [lt (if (reduced? result) + @result + result)] + (set! (.-stepper lt) nil) + result)) + ([result input] + (let [lt result] + (set! (.-first lt) input) + (set! (.-rest lt) (lazy-transformer (.-stepper lt))) + (set! (.-stepper lt) nil) + (.-rest lt))))] + (Stepper. (xform stepfn) iter))) + +(deftype MultiStepper [xform iters nexts] + Object + (hasNext [_] + (loop [iters (seq iters)] + (if-not (nil? iters) + (let [iter (first iters)] + (if-not (.hasNext iter) + false + (recur (next iters)))) + true))) + (next [_] + (dotimes [i (alength iters)] + (aset next i (.next (aget iters i)))) + (prim-seq nexts 0)) + (step [_ lt] + (loop [] + (if (and (not (nil? (.-stepper lt))) + (.hasNext iter)) + (when-not (reduced? (xform lt (.next iter))) + (recur)))) + (when (nil? (.-stepper lt)) + (xform lt)))) + +(defn multi-stepper + ([xform iters] + (multi-stepper xform iters + (make-array (alength iters)))) + ([xform iters nexts] + (letfn [(stepfn + ([result] + (let [lt (if (reduced? result) + @result + result)] + (set! (.-stepper lt) nil) + lt)) + ([result input] + (let [lt result] + (set! (.-first lt) input) + (set! (.-rest lt) (lazy-transformer (.-stepper lt))) + (set! (.-stepper lt) nil) + (.-rest lt))))] + (MultiStepper. xform iters nexts)))) + +(deftype LazyTransformer [^:mutable stepper ^:mutable first ^:mutable rest meta] + IWithMeta + (-with-meta [this new-meta] + (LazyTransformer. stepper first rest new-meta)) + + ICollection + (-conj [this o] + (cons o (-seq this))) + + IEmptyableCollection + (-empty [this] + ()) + + ISequential + IEquiv + (-equiv [this other] + (let [s (-seq this)] + (if-not (nil? s) + (equiv-sequential this other) + (and (sequential? other) + (nil? (seq other)))))) + + IHash + (-hash [this] + (hash-ordered-coll this)) + + ISeqable + (-seq [this] + (when-not (nil? stepper) + (.step stepper this)) + (if (nil? rest) + nil + this)) + + ISeq + (-first [this] + (when-not (nil? stepper) + (-seq this)) + (if (nil? rest) + nil + first)) + + (-rest [this] + (when-not (nil? stepper) + (-seq this)) + (if (nil? rest) + () + rest)) + + INext + (-next [this] + (when-not (nil? stepper) + (-seq this)) + (if (nil? rest) + nil + (-seq rest)))) + +(defn lazy-transformer [stepper] + (LazyTransformer. stepper nil nil nil)) + +(set! (.-create LazyTransformer) + (fn [xform coll] + (LazyTransformer. (stepper xform (iter coll)) nil nil nil))) + +(set! (.-createMulti LazyTransformer) + (fn [xform colls] + (let [iters (array)] + (doseq [coll colls] + (.push iters (iter coll))) + (LazyTransformer. + (multi-stepper xform iters (make-array (alength iters))) + nil nil nil)))) + +(defn sequence + "Coerces coll to a (possibly empty) sequence, if it is not already + one. Will not force a lazy seq. (sequence nil) yields (), When a + transducer is supplied, returns a lazy sequence of applications of + the transform to the items in coll(s), i.e. to the set of first + items of each coll, followed by the set of second + items in each coll, until any one of the colls is exhausted. Any + remaining items in other colls are ignored. The transform should accept + number-of-colls arguments" + ([coll] + (if (seq? coll) + coll + (or (seq coll) ()))) + ([xform coll] + (.create LazyTransformer xform coll)) + ([xform coll & colls] + (.createMulti LazyTransformer xform (to-array (cons coll colls))))) + (defn ^boolean every? "Returns true if (pred x) is logical true for every x in coll, else false." @@ -2977,7 +3222,17 @@ reduces them without incurring seq initialization" (defn keep "Returns a lazy sequence of the non-nil results of (f item). Note, this means false return values will be included. f must be free of - side-effects." + side-effects. Returns a transducer when no collection is provided." + ([f] + (fn [f1] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (let [v (f input)] + (if (nil? v) + result + (f1 result v))))))) ([f coll] (lazy-seq (when-let [s (seq coll)] @@ -2995,10 +3250,141 @@ reduces them without incurring seq initialization" (keep f (rest s)) (cons x (keep f (rest s)))))))))) +;; ============================================================================= +;; Atom + +(deftype Atom [state meta validator watches] + Object + (equiv [this other] + (-equiv this other)) + + IAtom + + IEquiv + (-equiv [o other] (identical? o other)) + + IDeref + (-deref [_] state) + + IMeta + (-meta [_] meta) + + IWatchable + (-notify-watches [this oldval newval] + (doseq [[key f] watches] + (f key this oldval newval))) + (-add-watch [this key f] + (set! (.-watches this) (assoc watches key f)) + this) + (-remove-watch [this key] + (set! (.-watches this) (dissoc watches key))) + + IHash + (-hash [this] (goog/getUid this))) + +(defn atom + "Creates and returns an Atom with an initial value of x and zero or + more options (in any order): + + :meta metadata-map + + :validator validate-fn + + If metadata-map is supplied, it will be come the metadata on the + atom. validate-fn must be nil or a side-effect-free fn of one + argument, which will be passed the intended new state on any state + change. If the new state is unacceptable, the validate-fn should + return false or throw an Error. If either of these error conditions + occur, then the value of the atom will not change." + ([x] (Atom. x nil nil nil)) + ([x & {:keys [meta validator]}] (Atom. x meta validator nil))) + +(declare pr-str) + +(defn reset! + "Sets the value of atom to newval without regard for the + current value. Returns newval." + [a new-value] + (if (instance? Atom a) + (let [validate (.-validator a)] + (when-not (nil? validate) + (assert (validate new-value) "Validator rejected reference state")) + (let [old-value (.-state a)] + (set! (.-state a) new-value) + (when-not (nil? (.-watches a)) + (-notify-watches a old-value new-value)) + new-value)) + (-reset! a new-value))) + +;; generic to all refs +;; (but currently hard-coded to atom!) +(defn deref + [o] + (-deref o)) + +(defn swap! + "Atomically swaps the value of atom to be: + (apply f current-value-of-atom args). Note that f may be called + multiple times, and thus should be free of side effects. Returns + the value that was swapped in." + ([a f] + (if (instance? Atom a) + (reset! a (f (.-state a))) + (-swap! a f))) + ([a f x] + (if (instance? Atom a) + (reset! a (f (.-state a) x)) + (-swap! a f x))) + ([a f x y] + (if (instance? Atom a) + (reset! a (f (.-state a) x y)) + (-swap! a f x y))) + ([a f x y & more] + (if (instance? Atom a) + (reset! a (apply f (.-state a) x y more)) + (-swap! a f x y more)))) + +(defn compare-and-set! + "Atomically sets the value of atom to newval if and only if the + current value of the atom is identical to oldval. Returns true if + set happened, else false." + [a oldval newval] + (if (= (.-state a) oldval) + (do (reset! a newval) true) + false)) + +(defn set-validator! + "Sets the validator-fn for an atom. validator-fn must be nil or a + side-effect-free fn of one argument, which will be passed the intended + new state on any state change. If the new state is unacceptable, the + validator-fn should return false or throw an Error. If the current state + is not acceptable to the new validator, an Error will be thrown and the + validator will not be changed." + [iref val] + (set! (.-validator iref) val)) + +(defn get-validator + "Gets the validator-fn for a var/ref/agent/atom." + [iref] + (.-validator iref)) + (defn keep-indexed "Returns a lazy sequence of the non-nil results of (f index item). Note, this means false return values will be included. f must be free of - side-effects." + side-effects. Returns a stateful transducer when no collection is + provided." + ([f] + (fn [f1] + (let [ia (atom -1)] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (let [i (swap! ia inc) + v (f i input)] + (if (nil? v) + result + (f1 result v)))))))) ([f coll] (letfn [(keepi [idx coll] (lazy-seq @@ -3097,11 +3483,21 @@ reduces them without incurring seq initialization" (some #(some % args) ps))))))) (defn map - "Returns a lazy sequence consisting of the result of applying f to the - set of first items of each coll, followed by applying f to the set - of second items in each coll, until any one of the colls is + "Returns a lazy sequence consisting of the result of applying f to + the set of first items of each coll, followed by applying f to the + set of second items in each coll, until any one of the colls is exhausted. Any remaining items in other colls are ignored. Function - f should accept number-of-colls arguments." + f should accept number-of-colls arguments. Returns a transducer when + no collection is provided." + ([f] + (fn [f1] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (f1 result (f input))) + ([result input & inputs] + (f1 result (apply f input inputs)))))) ([f coll] (lazy-seq (when-let [s (seq coll)] @@ -3135,22 +3531,51 @@ reduces them without incurring seq initialization" (defn take "Returns a lazy sequence of the first n items in coll, or all items if - there are fewer than n." - [n coll] - (lazy-seq - (when (pos? n) - (when-let [s (seq coll)] - (cons (first s) (take (dec n) (rest s))))))) + there are fewer than n. Returns a stateful transducer when + no collection is provided." + ([n] + (fn [f1] + (let [na (atom n)] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (let [n @na + nn (swap! na dec) + result (if (pos? n) + (f1 result input) + result)] + (if (not (pos? nn)) + (reduced result) + result))))))) + ([n coll] + (lazy-seq + (when (pos? n) + (when-let [s (seq coll)] + (cons (first s) (take (dec n) (rest s)))))))) (defn drop - "Returns a lazy sequence of all but the first n items in coll." - [n coll] - (let [step (fn [n coll] - (let [s (seq coll)] - (if (and (pos? n) s) - (recur (dec n) (rest s)) - s)))] - (lazy-seq (step n coll)))) + "Returns a lazy sequence of all but the first n items in coll. + Returns a stateful transducer when no collection is provided." + ([n] + (fn [f1] + (let [na (atom n)] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (let [n @na] + (swap! na dec) + (if (pos? n) + result + (f1 result input)))))))) + ([n coll] + (let [step (fn [n coll] + (let [s (seq coll)] + (if (and (pos? n) s) + (recur (dec n) (rest s)) + s)))] + (lazy-seq (step n coll))))) (defn drop-last "Return a lazy sequence of all but the last n (default 1) items in coll" @@ -3167,15 +3592,29 @@ reduces them without incurring seq initialization" s))) (defn drop-while - "Returns a lazy sequence of the items in coll starting from the first - item for which (pred item) returns nil." - [pred coll] - (let [step (fn [pred coll] - (let [s (seq coll)] - (if (and s (pred (first s))) - (recur pred (rest s)) - s)))] - (lazy-seq (step pred coll)))) + "Returns a lazy sequence of the items in coll starting from the + first item for which (pred item) returns logical false. Returns a + stateful transducer when no collection is provided." + ([pred] + (fn [f1] + (let [da (atom true)] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (let [drop? @da] + (if (and drop? (pred input)) + result + (do + (reset! da nil) + (f1 result input))))))))) + ([pred coll] + (let [step (fn [pred coll] + (let [s (seq coll)] + (if (and s (pred (first s))) + (recur pred (rest s)) + s)))] + (lazy-seq (step pred coll))))) (defn cycle "Returns a lazy (infinite!) sequence of repetitions of the items in coll." @@ -3251,7 +3690,17 @@ reduces them without incurring seq initialization" (defn filter "Returns a lazy sequence of the items in coll for which - (pred item) returns true. pred must be free of side-effects." + (pred item) returns true. pred must be free of side-effects. + Returns a transducer when no collection is provided." + ([pred] + (fn [f1] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (if (pred input) + (f1 result input) + result))))) ([pred coll] (lazy-seq (when-let [s (seq coll)] @@ -3270,9 +3719,11 @@ reduces them without incurring seq initialization" (defn remove "Returns a lazy sequence of the items in coll for which - (pred item) returns false. pred must be free of side-effects." - [pred coll] - (filter (complement pred) coll)) + (pred item) returns false. pred must be free of side-effects. + Returns a transducer when no collection is provided." + ([pred] (filter (complement pred))) + ([pred coll] + (filter (complement pred) coll))) (defn tree-seq "Returns a lazy sequence of the nodes in a tree, via a depth-first walk. @@ -3299,13 +3750,17 @@ reduces them without incurring seq initialization" (defn into "Returns a new coll consisting of to-coll with all of the items of - from-coll conjoined." - [to from] - (if-not (nil? to) - (if (implements? IEditableCollection to) - (persistent! (reduce -conj! (transient to) from)) - (reduce -conj to from)) - (reduce conj () from))) + from-coll conjoined. A transducer may be supplied." + ([to from] + (if-not (nil? to) + (if (implements? IEditableCollection to) + (with-meta (persistent! (reduce -conj! (transient to) from)) (meta to)) + (reduce -conj to from)) + (reduce conj () from))) + ([to xform from] + (if (implements? IEditableCollection to) + (with-meta (persistent! (transduce xform -conj! (transient to) from)) (meta to)) + (transduce xform conj to from)))) (defn mapv "Returns a vector consisting of the result of applying f to the @@ -6778,16 +7233,19 @@ reduces them without incurring seq initialization" (defn replace "Given a map of replacement pairs and a vector/collection, returns a vector/seq with any elements = a key in smap replaced with the - corresponding val in smap" - [smap coll] - (if (vector? coll) - (let [n (count coll)] - (reduce (fn [v i] - (if-let [e (find smap (nth v i))] - (assoc v i (second e)) - v)) - coll (take n (iterate inc 0)))) - (map #(if-let [e (find smap %)] (second e) %) coll))) + corresponding val in smap. Returns a transducer when no collection + is provided." + ([smap] + (map #(if-let [e (find smap %)] (val e) %))) + ([smap coll] + (if (vector? coll) + (let [n (count coll)] + (reduce (fn [v i] + (if-let [e (find smap (nth v i))] + (assoc v i (second e)) + v)) + coll (take n (iterate inc 0)))) + (map #(if-let [e (find smap %)] (second e) %) coll)))) (defn distinct "Returns a lazy sequence of the elements of coll with duplicates removed" @@ -6846,7 +7304,29 @@ reduces them without incurring seq initialization" (defn partition-all "Returns a lazy sequence of lists like partition, but may include - partitions with fewer than n items at the end." + partitions with fewer than n items at the end. Returns a stateful + transducer when no collection is provided." + ([^long n] + (fn [f1] + (let [a (array)] + (fn + ([] (f1)) + ([result] + (let [result (if (.isEmpty a) + result + (let [v (vec (.toArray a))] + ;;flushing ops must clear before invoking possibly + ;;failing nested op, else infinite loop + (.clear a) + (f1 result v)))] + (f1 result))) + ([result input] + (.add a input) + (if (= n (.size a)) + (let [v (vec (.toArray a))] + (.clear a) + (f1 result v)) + result)))))) ([n coll] (partition-all n n coll)) ([n step coll] @@ -6856,12 +7336,22 @@ reduces them without incurring seq initialization" (defn take-while "Returns a lazy sequence of successive items from coll while - (pred item) returns true. pred must be free of side-effects." - [pred coll] - (lazy-seq - (when-let [s (seq coll)] - (when (pred (first s)) - (cons (first s) (take-while pred (rest s))))))) + (pred item) returns true. pred must be free of side-effects. + Returns a transducer when no collection is provided." + ([pred] + (fn [f1] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (if (pred input) + (f1 result input) + (reduced result)))))) + ([pred coll] + (lazy-seq + (when-let [s (seq coll)] + (when (pred (first s)) + (cons (first s) (take-while pred (rest s)))))))) (defn mk-bound-fn [sc test key] @@ -6986,27 +7476,83 @@ reduces them without incurring seq initialization" ([start end step] (Range. nil start end step nil))) (defn take-nth - "Returns a lazy seq of every nth item in coll." - [n coll] - (lazy-seq - (when-let [s (seq coll)] - (cons (first s) (take-nth n (drop n s)))))) + "Returns a lazy seq of every nth item in coll. Returns a stateful + transducer when no collection is provided." + ([n] + (fn [f1] + (let [ia (atom -1)] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (let [i (swap! ia inc)] + (if (zero? (rem i n)) + (f1 result input) + result))))))) + ([n coll] + (lazy-seq + (when-let [s (seq coll)] + (cons (first s) (take-nth n (drop n s))))))) (defn split-with "Returns a vector of [(take-while pred coll) (drop-while pred coll)]" [pred coll] [(take-while pred coll) (drop-while pred coll)]) +(defn- clear-array [a] + (while (pos? (alength a)) + (.pop a))) + +(deftype ArrayList [^:mutable arr] + Object + (add [_] (.push arr)) + (size [_] (alength arr)) + (clear [_] (set! arr [])) + (isEmpty [_] (zero? (alength arr))) + (toArray [_] arr)) + +(defn array-list [] + (ArrayList. (array))) + (defn partition-by - "Applies f to each value in coll, splitting it each time f returns - a new value. Returns a lazy seq of partitions." - [f coll] - (lazy-seq - (when-let [s (seq coll)] - (let [fst (first s) - fv (f fst) - run (cons fst (take-while #(= fv (f %)) (next s)))] - (cons run (partition-by f (seq (drop (count run) s)))))))) + "Applies f to each value in coll, splitting it each time f returns a + new value. Returns a lazy seq of partitions. Returns a stateful + transducer when no collection is provided." + ([f] + (fn [f1] + (let [a (array-list) + pa (atom ::none)] + (fn + ([] (f1)) + ([result] + (let [result (if (.isEmpty a) + result + (let [v (vec (.toArray a))] + ;;flushing ops must clear before invoking possibly + ;;failing nested op, else infinite loop + (.clear a) + (f1 result v)))] + (f1 result))) + ([result input] + (let [pval @pa + val (f input)] + (reset! pa val) + (if (or (identical? pval ::none) + (= val pval)) + (do + (.add a input) + result) + (let [v (vec (.toArray a))] + (.clear a) + (.add a input) + (f1 result v))))))))) + ([f coll] + (lazy-seq + (when-let [s (seq coll)] + (let [fst (first s) + fv (f fst) + run (cons fst (take-while #(= fv (f %)) (next s)))] + (cons run (partition-by f (seq (drop (count run) s))))))))) (defn frequencies "Returns a map from distinct items in coll to the number of times @@ -7366,6 +7912,9 @@ reduces them without incurring seq initialization" LazySeq (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + LazyTransformer + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + IndexedSeq (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) @@ -7446,8 +7995,13 @@ reduces them without incurring seq initialization" (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "#{" " " "}" opts coll)) Range - (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))) + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + Atom + (-pr-writer [a writer opts] + (-write writer "#"))) ;; IComparable (extend-protocol IComparable @@ -7466,133 +8020,6 @@ reduces them without incurring seq initialization" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Reference Types ;;;;;;;;;;;;;;;; -(defprotocol IAtom) - -(defprotocol IReset - (-reset! [o new-value])) - -(defprotocol ISwap - (-swap! [o f] [o f a] [o f a b] [o f a b xs])) - -(deftype Atom [state meta validator watches] - Object - (equiv [this other] - (-equiv this other)) - - IAtom - - IEquiv - (-equiv [o other] (identical? o other)) - - IDeref - (-deref [_] state) - - IMeta - (-meta [_] meta) - - IPrintWithWriter - (-pr-writer [a writer opts] - (-write writer "#")) - - IWatchable - (-notify-watches [this oldval newval] - (doseq [[key f] watches] - (f key this oldval newval))) - (-add-watch [this key f] - (set! (.-watches this) (assoc watches key f)) - this) - (-remove-watch [this key] - (set! (.-watches this) (dissoc watches key))) - - IHash - (-hash [this] (goog/getUid this))) - -(defn atom - "Creates and returns an Atom with an initial value of x and zero or - more options (in any order): - - :meta metadata-map - - :validator validate-fn - - If metadata-map is supplied, it will be come the metadata on the - atom. validate-fn must be nil or a side-effect-free fn of one - argument, which will be passed the intended new state on any state - change. If the new state is unacceptable, the validate-fn should - return false or throw an Error. If either of these error conditions - occur, then the value of the atom will not change." - ([x] (Atom. x nil nil nil)) - ([x & {:keys [meta validator]}] (Atom. x meta validator nil))) - -(defn reset! - "Sets the value of atom to newval without regard for the - current value. Returns newval." - [a new-value] - (if (instance? Atom a) - (let [validate (.-validator a)] - (when-not (nil? validate) - (assert (validate new-value) "Validator rejected reference state")) - (let [old-value (.-state a)] - (set! (.-state a) new-value) - (when-not (nil? (.-watches a)) - (-notify-watches a old-value new-value)) - new-value)) - (-reset! a new-value))) - -;; generic to all refs -;; (but currently hard-coded to atom!) -(defn deref - [o] - (-deref o)) - -(defn swap! - "Atomically swaps the value of atom to be: - (apply f current-value-of-atom args). Note that f may be called - multiple times, and thus should be free of side effects. Returns - the value that was swapped in." - ([a f] - (if (instance? Atom a) - (reset! a (f (.-state a))) - (-swap! a f))) - ([a f x] - (if (instance? Atom a) - (reset! a (f (.-state a) x)) - (-swap! a f x))) - ([a f x y] - (if (instance? Atom a) - (reset! a (f (.-state a) x y)) - (-swap! a f x y))) - ([a f x y & more] - (if (instance? Atom a) - (reset! a (apply f (.-state a) x y more)) - (-swap! a f x y more)))) - -(defn compare-and-set! - "Atomically sets the value of atom to newval if and only if the - current value of the atom is identical to oldval. Returns true if - set happened, else false." - [a oldval newval] - (if (= (.-state a) oldval) - (do (reset! a newval) true) - false)) - -(defn set-validator! - "Sets the validator-fn for an atom. validator-fn must be nil or a - side-effect-free fn of one argument, which will be passed the intended - new state on any state change. If the new state is unacceptable, the - validator-fn should return false or throw an Error. If the current state - is not acceptable to the new validator, an Error will be thrown and the - validator will not be changed." - [iref val] - (set! (.-validator iref) val)) - -(defn get-validator - "Gets the validator-fn for a var/ref/agent/atom." - [iref] - (.-validator iref)) - (defn alter-meta! "Atomically sets the metadata for a namespace/var/ref/agent/atom to be: @@ -7687,6 +8114,77 @@ reduces them without incurring seq initialization" [d] (-realized? d)) +(defn- preserving-reduced + [f1] + #(let [ret (f1 %1 %2)] + (if (reduced? ret) + (reduced ret) + ret))) + +(defn flatmap + "maps f over coll and concatenates the results. Thus function f + should return a collection. Returns a transducer when no collection + is provided." + ([f] + (fn [f1] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (reduce (preserving-reduced f1) result (f input)))))) + ([f coll] (sequence (flatmap f) coll))) + +(defn dedupe + "Returns a lazy sequence removing consecutive duplicates in coll. + Returns a transducer when no collection is provided." + ([] + (fn [f1] + (let [pa (atom ::none)] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (let [prior @pa] + (reset! pa input) + (if (= prior input) + result + (f1 result input)))))))) + ([coll] (sequence (dedupe) coll))) + +(defn random-sample + "Returns items from coll with random probability of prob (0.0 - + 1.0). Returns a transducer when no collection is provided." + ([prob] + (filter (fn [_] (< (rand) prob)))) + ([prob coll] + (filter (fn [_] (< (rand) prob)) coll))) + +(deftype Iteration [xform coll] + ISequential + + ISeqable + (-seq [_] (seq (sequence xform coll))) + + IReduce + (-reduce [_ f init] (transduce xform f init coll)) + + IPrintWithWriter + (-pr-writer [coll writer opts] + (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))) + +(defn iteration + "Returns an iterable/seqable/reducible sequence of applications of + the transducer to the items in coll. Note that these applications + will be performed every time iterator/seq/reduce is called." + [xform coll] + (Iteration. xform coll)) + +(defn run! + "Runs the supplied procedure (via reduce), for purposes of side + effects, on successive items in the collection. Returns nil" + [proc coll] + (reduce #(proc %2) nil coll)) + (defprotocol IEncodeJS (-clj->js [x] "Recursively transforms clj values to JavaScript") (-key->js [x] "Transforms map keys to valid JavaScript keys. Arbitrary keys are diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index e7c22a0dd..e567d3638 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1,4 +1,5 @@ (ns cljs.core-test + (:refer-clojure :exclude [iter]) (:require [clojure.string :as s] [clojure.set :as set])) @@ -2335,5 +2336,10 @@ (assert (.equiv (map inc [1 2 3]) (map inc [1 2 3]))) (assert (.equiv #{:cat :dog :bird} #{:cat :dog :bird})) + ;; transducers + (assert (== (hash [1 2 3]) (hash (sequence (map inc) (range 3))))) + (assert (= [1 2 3] (sequence (map inc) (range 3)))) + (assert (= (sequence (map inc) (range 3)) [1 2 3])) + :ok ) From 4274f5bac94293ba089347ed6bd30afa061efd37 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 09:08:00 -0400 Subject: [PATCH 0102/4033] fix reduce call in transduce --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 07ccd7c4a..43a0d4e89 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1664,7 +1664,7 @@ reduces them without incurring seq initialization" ([xform f coll] (transduce xform f (f) coll)) ([xform f init coll] (let [f (xform (completing f)) - ret (reduce coll f init) + ret (reduce f init coll) ret (f (if (reduced? ret) @ret ret))] (if (reduced? ret) @ret ret)))) From 86f0fc6be106e6d99d540d4b0cce7c887e819414 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 10:01:25 -0400 Subject: [PATCH 0103/4033] fix ArrayIter --- src/cljs/cljs/core.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 43a0d4e89..de4c41df0 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2886,11 +2886,11 @@ reduces them without incurring seq initialization" (defn string-iter [x] (StringIter. x 0)) -(deftype ArrayIter [s ^:mutable i] +(deftype ArrayIter [arr ^:mutable i] Object - (hasNext [_] (< i (alength s))) + (hasNext [_] (< i (alength arr))) (next [_] - (let [ret (.aget s i)] + (let [ret (aget arr i)] (set! i (inc i)) ret)) (remove [_] (js/Error. "Unsupported operation"))) From 8d9402cdee3ec0ac7819d00eef4b459acfe6cc7c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 10:30:42 -0400 Subject: [PATCH 0104/4033] move array list up for partition by, fix clear implementation --- src/cljs/cljs/core.cljs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index de4c41df0..88cfb294a 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7302,13 +7302,24 @@ reduces them without incurring seq initialization" ([k x y & more] (reduce #(min-key k %1 %2) (min-key k x y) more))) +(deftype ArrayList [^:mutable arr] + Object + (add [_ x] (.push arr x)) + (size [_] (alength arr)) + (clear [_] (set! arr (array))) + (isEmpty [_] (zero? (alength arr))) + (toArray [_] arr)) + +(defn array-list [] + (ArrayList. (array))) + (defn partition-all "Returns a lazy sequence of lists like partition, but may include partitions with fewer than n items at the end. Returns a stateful transducer when no collection is provided." ([^long n] (fn [f1] - (let [a (array)] + (let [a (array-list)] (fn ([] (f1)) ([result] @@ -7499,21 +7510,6 @@ reduces them without incurring seq initialization" [pred coll] [(take-while pred coll) (drop-while pred coll)]) -(defn- clear-array [a] - (while (pos? (alength a)) - (.pop a))) - -(deftype ArrayList [^:mutable arr] - Object - (add [_] (.push arr)) - (size [_] (alength arr)) - (clear [_] (set! arr [])) - (isEmpty [_] (zero? (alength arr))) - (toArray [_] arr)) - -(defn array-list [] - (ArrayList. (array))) - (defn partition-by "Applies f to each value in coll, splitting it each time f returns a new value. Returns a lazy seq of partitions. Returns a stateful From 15fa7f21f50898b00600202398a3eb576d85258f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 10:41:08 -0400 Subject: [PATCH 0105/4033] identical? -> keyword-identical? --- src/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 88cfb294a..80eeb8277 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7533,8 +7533,8 @@ reduces them without incurring seq initialization" (let [pval @pa val (f input)] (reset! pa val) - (if (or (identical? pval ::none) - (= val pval)) + (if (or (keyword-identical? pval ::none) + (= val pval)) (do (.add a input) result) From de9246067b992c843cb57e148477c0ca89ccc6dd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 13:35:30 -0400 Subject: [PATCH 0106/4033] current passing tests --- test/cljs/cljs/core_test.cljs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index e567d3638..5c4d5af23 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2337,9 +2337,39 @@ (assert (.equiv #{:cat :dog :bird} #{:cat :dog :bird})) ;; transducers + (assert (= (sequence (map inc) (array 1 2 3)) '(2 3 4))) + (assert (= (apply str (sequence (map #(.toUpperCase %)) "foo")) "FOO")) (assert (== (hash [1 2 3]) (hash (sequence (map inc) (range 3))))) (assert (= [1 2 3] (sequence (map inc) (range 3)))) (assert (= (sequence (map inc) (range 3)) [1 2 3])) - + (assert (= (sequence (remove even?) (range 10)) '(1 3 5 7 9))) + (assert (= (sequence (take 5) (range 10)) + '(0 1 2 3 4))) + (assert (= (sequence (take-while #(< % 5)) (range 10)) + '(0 1 2 3 4))) + (assert (= (sequence (drop 5) (range 10)) + '(5 6 7 8 9))) + (assert (= (sequence (drop-while #(< % 5)) (range 10)) + '(5 6 7 8 9))) + (assert (= (sequence (take-nth 2) (range 10)) + '(0 2 4 6 8))) + (assert (= (sequence (replace {:foo :bar}) '(:foo 1 :foo 2)) + '(:bar 1 :bar 2))) + (let [ret (into [] (map inc) (range 3))] + (assert (and (vector? ret) + (= ret '(1 2 3))))) + (let [ret (into [] (filter even?) (range 10))] + (assert (and (vector? ret) + (= ret '(0 2 4 6 8))))) + (assert (= (map inc (sequence (map inc) (range 3))) + '(2 3 4))) + (assert (= (sequence (dedupe) [1 1 2 2 3 3]) + '(1 2 3))) + (assert (= (flatmap reverse [[3 2 1 0] [6 5 4] [9 8 7]]) + (range 10))) + (assert (= (sequence (flatmap reverse) [[3 2 1 0] [6 5 4] [9 8 7]]) + (range 10))) + (assert (= (seq (iteration (map inc) [1 2 3])) '(2 3 4))) + :ok ) From 12d919bc0689a80784c98091721c1ceac29ed86b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 15:24:30 -0400 Subject: [PATCH 0107/4033] fix mistake in stepper & multi-stepper --- src/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 80eeb8277..4c71cf4ab 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2927,7 +2927,7 @@ reduces them without incurring seq initialization" (.hasNext iter)) (when-not (reduced? (xform lt (.next iter))) (recur)))) - (when (nil? (.-stepper lt)) + (when-not (nil? (.-stepper lt)) (xform lt)))) (defn stepper [xform iter] @@ -2966,7 +2966,7 @@ reduces them without incurring seq initialization" (.hasNext iter)) (when-not (reduced? (xform lt (.next iter))) (recur)))) - (when (nil? (.-stepper lt)) + (when-not (nil? (.-stepper lt)) (xform lt)))) (defn multi-stepper From ad37a176297046025a131a5a2bc3f565197d26e4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 15:24:43 -0400 Subject: [PATCH 0108/4033] cleanup partition-all --- src/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 4c71cf4ab..2a514bd8d 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7317,7 +7317,7 @@ reduces them without incurring seq initialization" "Returns a lazy sequence of lists like partition, but may include partitions with fewer than n items at the end. Returns a stateful transducer when no collection is provided." - ([^long n] + ([n] (fn [f1] (let [a (array-list)] (fn @@ -7333,7 +7333,7 @@ reduces them without incurring seq initialization" (f1 result))) ([result input] (.add a input) - (if (= n (.size a)) + (if (== n (.size a)) (let [v (vec (.toArray a))] (.clear a) (f1 result v)) From 9446e279b0359e789bf23319f74dbf61655c9ea8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 15:38:13 -0400 Subject: [PATCH 0109/4033] latest few tests --- test/cljs/cljs/core_test.cljs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 5c4d5af23..96bff4408 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2370,6 +2370,37 @@ (assert (= (sequence (flatmap reverse) [[3 2 1 0] [6 5 4] [9 8 7]]) (range 10))) (assert (= (seq (iteration (map inc) [1 2 3])) '(2 3 4))) + (assert (= (sequence (partition-by #{:split}) [1 2 3 :split 4 5 6]) + '([1 2 3] [:split] [4 5 6]))) + (assert (= (sequence (partition-all 3) '(1 2 3 4 5)) + '([1 2 3] [4 5]))) + (assert (= (sequence (keep identity) [1 nil 2 nil 3]) + '(1 2 3))) + (assert (= (keep-indexed identity [:foo nil :bar nil :baz]) + (sequence (keep-indexed identity) [:foo nil :bar nil :baz]))) + + (def xform + (comp (map inc) + (filter even?) + (dedupe) + (flatmap range) + (partition-all 3) + (partition-by #(< (apply + %) 7)) + (flatmap flatten) + (random-sample 1.0) + (take-nth 1) + (keep #(when (odd? %) (* % %))) + (keep-indexed #(when (even? %1) (* %1 %2))) + (replace {2 "two" 6 "six" 18 "eighteen"}) + (take 11) + (take-while #(not= 300 %)) + (drop 1) + (drop-while string?) + (remove string?))) + (def data (vec (interleave (range 18) (range 20)))) + + (assert (= (sequence xform data) + '(36 200 10))) :ok ) From d839ac1296a9c117648cd1c1e471ab377621c81d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 15:53:49 -0400 Subject: [PATCH 0110/4033] add simple transducers benchmark --- benchmark/cljs/benchmark_runner.cljs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index b101a633a..c349b4175 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -312,6 +312,17 @@ (println ";; reducers") (simple-benchmark [xs (into [] (range 1000000))] (r/reduce + (r/map inc (r/map inc (r/map inc xs)))) 1) +(dotimes [_ 10] + (let [xs (into [] (range 1000000))] + (time (r/reduce + (r/map inc (r/map inc (r/map inc xs))))))) + +(println ";; transducers") +(simple-benchmark [xs (into [] (range 1000000))] (transduce (comp (map inc) (map inc) (map inc)) + 0 xs) 1) + +(dotimes [_ 10] + (let [xs (into [] (range 1000000))] + (time (transduce (comp (map inc) (map inc) (map inc)) + 0 xs)))) + (println "\n") (println ";; multimethods") From d7c8960d0dd1edb088a3ea9092507cc77e3f3393 Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Wed, 6 Aug 2014 19:34:30 +0200 Subject: [PATCH 0111/4033] fix resolve-var handling of property access on local symbols --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 16ed987aa..8eb3df496 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -333,7 +333,7 @@ suffix (subs s (inc idx)) lb (-> env :locals prefix)] (if lb - {:name (symbol (str (:name lb) suffix))} + {:name (symbol (str (:name lb)) suffix)} (let [cur-ns (-> env :ns :name)] (if-let [full-ns (get-in @env/*compiler* [::namespaces cur-ns :imports prefix])] {:name (symbol (str full-ns) suffix)} From b96ac5042334f36aac528155478c005cd4372d9c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 17:05:06 -0400 Subject: [PATCH 0112/4033] 0.0-2301 --- README.md | 6 +++--- changes.md | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ca129996f..f1d158143 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2280 +Latest stable release: 0.0-2301 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2280"] +[org.clojure/clojurescript "0.0-2301"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2280 org.clojure clojurescript - 0.0-2280 + 0.0-2301 ``` diff --git a/changes.md b/changes.md index 253d6b5fc..0b67a355c 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,12 @@ +## 0.0-2301 + +### Changes +* transducers + +### Fixes +* eliminate dead branches in conditionals to prevent Closure warnings +* bad var resolution if when local contained . + ## 0.0-2280 ### Changes From 08d8ca6493d83d6e9cf439d300002a1491a0f58f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 17:17:07 -0400 Subject: [PATCH 0113/4033] remove extra benchmark bits --- benchmark/cljs/benchmark_runner.cljs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index c349b4175..94ce8376a 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -312,17 +312,9 @@ (println ";; reducers") (simple-benchmark [xs (into [] (range 1000000))] (r/reduce + (r/map inc (r/map inc (r/map inc xs)))) 1) -(dotimes [_ 10] - (let [xs (into [] (range 1000000))] - (time (r/reduce + (r/map inc (r/map inc (r/map inc xs))))))) - (println ";; transducers") (simple-benchmark [xs (into [] (range 1000000))] (transduce (comp (map inc) (map inc) (map inc)) + 0 xs) 1) -(dotimes [_ 10] - (let [xs (into [] (range 1000000))] - (time (transduce (comp (map inc) (map inc) (map inc)) + 0 xs)))) - (println "\n") (println ";; multimethods") From 5bad4398d61422d5d2937a0d164b5e5282bfc0b9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 17:28:43 -0400 Subject: [PATCH 0114/4033] move deref up so it can be optimized --- src/cljs/cljs/core.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 2a514bd8d..966c2606f 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -775,6 +775,12 @@ [r] (instance? Reduced r)) +;; generic to all refs +;; (but currently hard-coded to atom!) +(defn deref + [o] + (-deref o)) + (defn- ci-reduce "Accepts any collection which satisfies the ICount and IIndexed protocols and reduces them without incurring seq initialization" @@ -3316,12 +3322,6 @@ reduces them without incurring seq initialization" new-value)) (-reset! a new-value))) -;; generic to all refs -;; (but currently hard-coded to atom!) -(defn deref - [o] - (-deref o)) - (defn swap! "Atomically swaps the value of atom to be: (apply f current-value-of-atom args). Note that f may be called From f88f2be696518e54f5993ba3dab13615d8d326d8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 17:56:33 -0400 Subject: [PATCH 0115/4033] move lazy-transformer up --- src/cljs/cljs/core.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 966c2606f..8f473c088 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2923,7 +2923,10 @@ reduces them without incurring seq initialization" (array? coll) (array-iter coll) :else (seq-iter (seq coll)))) -(declare lazy-transformer) +(declare LazyTransformer) + +(defn lazy-transformer [stepper] + (LazyTransformer. stepper nil nil nil)) (deftype Stepper [xform iter] Object @@ -3052,9 +3055,6 @@ reduces them without incurring seq initialization" nil (-seq rest)))) -(defn lazy-transformer [stepper] - (LazyTransformer. stepper nil nil nil)) - (set! (.-create LazyTransformer) (fn [xform coll] (LazyTransformer. (stepper xform (iter coll)) nil nil nil))) From 9c942c400e36658e738d3d740602c51e3c824a1f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 18:36:48 -0400 Subject: [PATCH 0116/4033] remove check in the compiler that prevented anonymous multiarity fns from being optimized. transducers are now on par with reducers --- src/clj/cljs/compiler.clj | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 5049366bb..64bdfc235 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -571,8 +571,7 @@ (if variadic (emit-variadic-fn-method (assoc (first methods) :name name)) (emit-fn-method (assoc (first methods) :name name))) - (let [has-name? (and name true) - name (or name (gensym)) + (let [name (or name (gensym)) mname (munge name) maxparams (apply max-key count (map :params methods)) mmap (into {} @@ -616,12 +615,11 @@ (when variadic (emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";") (emitln mname ".cljs$lang$applyTo = " (some #(let [[n m] %] (when (:variadic m) n)) ms) ".cljs$lang$applyTo;")) - (when has-name? - (doseq [[n meth] ms] - (let [c (count (:params meth))] - (if (:variadic meth) - (emitln mname ".cljs$core$IFn$_invoke$arity$variadic = " n ".cljs$core$IFn$_invoke$arity$variadic;") - (emitln mname ".cljs$core$IFn$_invoke$arity$" c " = " n ";"))))) + (doseq [[n meth] ms] + (let [c (count (:params meth))] + (if (:variadic meth) + (emitln mname ".cljs$core$IFn$_invoke$arity$variadic = " n ".cljs$core$IFn$_invoke$arity$variadic;") + (emitln mname ".cljs$core$IFn$_invoke$arity$" c " = " n ";")))) (emitln "return " mname ";") (emitln "})()"))) (when loop-locals From 5f5180a5575e8e469b901835cc03fa27b050df91 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 18:51:32 -0400 Subject: [PATCH 0117/4033] add another reducer benchmark --- benchmark/cljs/benchmark_runner.cljs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 94ce8376a..f571dfb76 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -315,6 +315,12 @@ (println ";; transducers") (simple-benchmark [xs (into [] (range 1000000))] (transduce (comp (map inc) (map inc) (map inc)) + 0 xs) 1) +(println ";; reduce range 1000000 many ops") +(simple-benchmark [xs (range 1000000)] (reduce + 0 (map inc (map inc (map inc xs)))) 1) + +(println ";; transduce range 1000000 many ops ") +(simple-benchmark [xs (range 1000000)] (transduce (comp (map inc) (map inc) (map inc)) + 0 xs) 1) + (println "\n") (println ";; multimethods") From d1ec9d8b8bab035243777591f2d46f748927477f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 7 Aug 2014 18:54:45 -0400 Subject: [PATCH 0118/4033] 0.0-2307 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f1d158143..f7bd60c53 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2301 +Latest stable release: 0.0-2307 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2301"] +[org.clojure/clojurescript "0.0-2307"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2301 org.clojure clojurescript - 0.0-2301 + 0.0-2307 ``` diff --git a/changes.md b/changes.md index 0b67a355c..5250f4ec7 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2307 + +### Enhancement +* Allow multi-arity anonymous fns to optimize + ## 0.0-2301 ### Changes From 76e88207af94eb1c4b93fa8623fd82a48d16b97d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 8 Aug 2014 12:53:39 -0400 Subject: [PATCH 0119/4033] lazier seq iterators a la 1497 approach is same as https://github.com/clojure/clojure/commit/43cc1854508d655e58e377f84836ba128971f90c --- src/cljs/cljs/core.cljs | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 8f473c088..dc07ac6e0 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2904,24 +2904,36 @@ reduces them without incurring seq initialization" (defn array-iter [x] (ArrayIter. x 0)) -(deftype SeqIter [^:mutable seq] +(def INIT #js {}) +(def START #js {}) + +(deftype SeqIter [^:mutable _seq ^:mutable _next] Object - (hasNext [_] (not (nil? seq))) - (next [_] - (let [first (first seq)] - (set! seq (next seq)) - first)) + (hasNext [_] + (if (identical? _seq INIT) + (do + (set! _seq START) + (set! _next (seq _next))) + (if (identical? _seq _next) + (set! _next (next _seq)))) + (not (nil? _next))) + (next [this] + (if-not (.hasNext this) + (throw (js/Error. "No such element")) + (do + (set! _seq _next) + (first _next)))) (remove [_] (js/Error. "Unsupported operation"))) -(defn seq-iter [seq] - (SeqIter. seq)) +(defn seq-iter [coll] + (SeqIter. INIT coll)) (defn iter [coll] (cond (nil? coll) (nil-iter) (string? coll) (string-iter coll) (array? coll) (array-iter coll) - :else (seq-iter (seq coll)))) + :else (seq-iter coll))) (declare LazyTransformer) @@ -2934,7 +2946,9 @@ reduces them without incurring seq initialization" (loop [] (if (and (not (nil? (.-stepper lt))) (.hasNext iter)) - (when-not (reduced? (xform lt (.next iter))) + (if (reduced? (xform lt (.next iter))) + (when-not (nil? (.-rest lt)) + (set! (.. lt -rest -stepper) nil)) (recur)))) (when-not (nil? (.-stepper lt)) (xform lt)))) @@ -2973,7 +2987,9 @@ reduces them without incurring seq initialization" (loop [] (if (and (not (nil? (.-stepper lt))) (.hasNext iter)) - (when-not (reduced? (xform lt (.next iter))) + (if (reduced? (apply xform (cons lt (.next iter)))) + (when-not (nil? (.-rest lt)) + (set! (.. lt -rest -stepper) nil)) (recur)))) (when-not (nil? (.-stepper lt)) (xform lt)))) From 38daea9f339d7c4f6b8bc32d9b705b67e0705985 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 Aug 2014 19:06:37 -0400 Subject: [PATCH 0120/4033] fix protocol typo, event/EventType -> event/IEventType --- src/cljs/clojure/browser/net.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/clojure/browser/net.cljs b/src/cljs/clojure/browser/net.cljs index 52865d5b6..8d31880e6 100644 --- a/src/cljs/clojure/browser/net.cljs +++ b/src/cljs/clojure/browser/net.cljs @@ -58,7 +58,7 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." (.send this uri method content headers))) - event/EventType + event/IEventType (event-types [this] (into {} (map From b6d67f92509cbedbbf91883931ca3fd5c34a56bf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 Aug 2014 19:13:59 -0400 Subject: [PATCH 0121/4033] comment out reflect namespace from repl test --- samples/repl/src/repl/test.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/repl/src/repl/test.cljs b/samples/repl/src/repl/test.cljs index 065f36584..e8505761c 100644 --- a/samples/repl/src/repl/test.cljs +++ b/samples/repl/src/repl/test.cljs @@ -8,7 +8,8 @@ (ns repl.test (:require [clojure.browser.repl :as repl] - [clojure.reflect :as reflect])) + ;[clojure.reflect :as reflect] + )) (repl/connect "http://localhost:9000/repl") From e77980cdbec0954f73b5092fcb4540c42b5e6904 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 Aug 2014 19:29:55 -0400 Subject: [PATCH 0122/4033] 0.0-2311 --- README.md | 6 +++--- changes.md | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f7bd60c53..f3e3e9ff2 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2307 +Latest stable release: 0.0-2311 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2307"] +[org.clojure/clojurescript "0.0-2311"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2307 org.clojure clojurescript - 0.0-2307 + 0.0-2311 ``` diff --git a/changes.md b/changes.md index 5250f4ec7..15a462898 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,9 @@ +## 0.0-2311 + +### Fixes +* fix typo which broke browser REPL +* lazier seq iterators a la CLJ-1497 + ## 0.0-2307 ### Enhancement From 1d1f2df1f4fd2134664ae8944f4900e6efc29dc9 Mon Sep 17 00:00:00 2001 From: Dylan Butman Date: Mon, 11 Aug 2014 12:13:32 -0400 Subject: [PATCH 0123/4033] CLJS-831: Extending EventType to js/Element breaks Nashorn --- src/cljs/clojure/browser/event.cljs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/cljs/clojure/browser/event.cljs b/src/cljs/clojure/browser/event.cljs index 864df81a3..b04dcd2f4 100644 --- a/src/cljs/clojure/browser/event.cljs +++ b/src/cljs/clojure/browser/event.cljs @@ -19,17 +19,6 @@ events. It is based on the Google Closure Library event system." (extend-protocol IEventType EventTarget - (event-types - [this] - (into {} - (map - (fn [[k v]] - [(keyword (.toLowerCase k)) - v]) - (merge - (js->clj EventType))))) - - js/Element (event-types [this] (into {} @@ -40,6 +29,20 @@ events. It is based on the Google Closure Library event system." (merge (js->clj EventType)))))) +(when (exists? js/Element) + (extend-protocol IEventType + + js/Element + (event-types + [this] + (into {} + (map + (fn [[k v]] + [(keyword (.toLowerCase k)) + v]) + (merge + (js->clj EventType))))))) + (defn listen ([src type fn] (listen src type fn false)) From 4ba56712274d67b592cff5160bb2a6aea397c72d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 Aug 2014 20:05:04 -0400 Subject: [PATCH 0124/4033] preserve var metadata for deftype* and defrecord*, preserve var metadata when creation deftype/record factory fns --- src/clj/cljs/analyzer.clj | 2 ++ src/clj/cljs/core.clj | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 8eb3df496..04cdf78a3 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1185,6 +1185,7 @@ :type true :num-fields (count fields))] (merge m + (dissoc (meta tsym) :protocols) {:protocols (-> tsym meta :protocols)} (source-info tsym env))))) {:env env :op :deftype* :form form :t t :fields fields :pmasks pmasks})) @@ -1199,6 +1200,7 @@ :type true :num-fields (count fields))] (merge m + (dissoc (meta tsym) :protocols) {:protocols (-> tsym meta :protocols)} (source-info tsym env))))) {:env env :op :defrecord* :form form :t t :fields fields :pmasks pmasks})) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 97bdd53d0..13f1e3ded 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -860,7 +860,7 @@ (defn- build-positional-factory [rsym rname fields] - (let [fn-name (symbol (core/str '-> rsym))] + (let [fn-name (with-meta (symbol (core/str '-> rsym)) (meta rsym))] `(defn ~fn-name [~@fields] (new ~rname ~@fields)))) From 0b66cc85858c48bd2fa987f554fe7859c1588703 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 12 Aug 2014 18:36:56 -0400 Subject: [PATCH 0125/4033] include primitive array ops benchmark to compare to transducers --- benchmark/cljs/benchmark_runner.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index f571dfb76..eafb65d4d 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -315,6 +315,10 @@ (println ";; transducers") (simple-benchmark [xs (into [] (range 1000000))] (transduce (comp (map inc) (map inc) (map inc)) + 0 xs) 1) +(println ";; primitive array reduce 1000000 many ops") +(simple-benchmark [xs (into-array (range 1000000))] + (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)) 1) + (println ";; reduce range 1000000 many ops") (simple-benchmark [xs (range 1000000)] (reduce + 0 (map inc (map inc (map inc xs)))) 1) From 569fa70ddddbcafc46d1e6277eb603f9f6db0347 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 17 Aug 2014 18:00:33 -0400 Subject: [PATCH 0126/4033] count check in equiv-sequential if both arguments are ICounted --- src/cljs/cljs/core.cljs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index dc07ac6e0..1bbf7b22e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2057,12 +2057,15 @@ reduces them without incurring seq initialization" returns false." [x y] (boolean - (when (sequential? y) - (loop [xs (seq x) ys (seq y)] - (cond (nil? xs) (nil? ys) - (nil? ys) false - (= (first xs) (first ys)) (recur (next xs) (next ys)) - :else false))))) + (when (sequential? y) + (if (and (counted? x) (counted? y) + (not (== (count x) (count y)))) + false + (loop [xs (seq x) ys (seq y)] + (cond (nil? xs) (nil? ys) + (nil? ys) false + (= (first xs) (first ys)) (recur (next xs) (next ys)) + :else false)))))) (defn- hash-coll [coll] (if (seq coll) From 8854d9dcb7f402ef3fcb9c7a7fe3540a1e170dde Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 23 Aug 2014 20:38:43 -0400 Subject: [PATCH 0127/4033] only keep the param names when storing :method-params instead of the entire param AST --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 04cdf78a3..b0b259477 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -640,7 +640,7 @@ :protocol-inline (:protocol-inline init-expr) :variadic (:variadic init-expr) :max-fixed-arity (:max-fixed-arity init-expr) - :method-params (map :params (:methods init-expr)) + :method-params (map #(vec (map :name (:params %))) (:methods init-expr)) :methods (map (fn [method] (let [tag (infer-tag env (assoc method :op :method))] (cond-> (select-keys method From 605a27bbb4b5a6df551c068e5cd2848ab7cc77a3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 23 Aug 2014 22:01:21 -0400 Subject: [PATCH 0128/4033] change analysis so that optional opts parameter is taken by all fns that might need it. prep for analysis caching. --- src/clj/cljs/analyzer.clj | 122 ++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index b0b259477..0d9a99464 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -470,7 +470,7 @@ (defmulti parse (fn [op & rest] op)) (defmethod parse 'if - [op env [_ test then else :as form] name] + [op env [_ test then else :as form] name _] (when (< (count form) 3) (throw (error env "Too few arguments to if"))) (let [test-expr (disallowing-recur (analyze (assoc env :context :expr) test)) @@ -482,7 +482,7 @@ :children [test-expr then-expr else-expr]})) (defmethod parse 'case* - [op env [_ sym tests thens default :as form] name] + [op env [_ sym tests thens default :as form] name _] (assert (symbol? sym) "case* must switch on symbol") (assert (every? vector? tests) "case* tests must be grouped in vectors") (let [expr-env (assoc env :context :expr) @@ -499,14 +499,14 @@ :children (vec (concat [v] tests thens (if default [default])))})) (defmethod parse 'throw - [op env [_ throw :as form] name] + [op env [_ throw :as form] name _] (let [throw-expr (disallowing-recur (analyze (assoc env :context :expr) throw))] {:env env :op :throw :form form :throw throw-expr :children [throw-expr]})) (defmethod parse 'try - [op env [_ & body :as form] name] + [op env [_ & body :as form] name _] (let [catchenv (update-in env [:context] #(if (= :expr %) :return %)) catch? (every-pred seq? #(= (first %) 'catch)) default? (every-pred catch? #(= (second %) :default)) @@ -567,7 +567,7 @@ :children [try catch finally]})) (defmethod parse 'def - [op env form name] + [op env form name _] (let [pfn (fn ([_ sym] {:sym sym}) ([_ sym init] {:sym sym :init init}) @@ -690,7 +690,7 @@ :type type :form form :recurs @(:flag recur-frame) :expr expr})) (defmethod parse 'fn* - [op env [_ & args :as form] name] + [op env [_ & args :as form] name _] (let [[name meths] (if (symbol? (first args)) [(first args) (next args)] [name (seq args)]) @@ -762,7 +762,7 @@ :children (mapv :expr methods)})) (defmethod parse 'letfn* - [op env [_ bindings & exprs :as form] name] + [op env [_ bindings & exprs :as form] name _] (when-not (and (vector? bindings) (even? (count bindings))) (throw (error env "bindings must be vector of even number of elements"))) (let [n->fexpr (into {} (map (juxt first second) (partition 2 bindings))) @@ -808,7 +808,7 @@ :children (conj (vec (map :init bes)) expr)})) (defmethod parse 'do - [op env [_ & exprs :as form] _] + [op env [_ & exprs :as form] _ _] (let [statements (disallowing-recur (seq (map #(analyze (assoc env :context :statement) %) (butlast exprs)))) ret (if (<= (count exprs) 1) @@ -876,15 +876,15 @@ :children (conj (vec (map :init bes)) expr)})) (defmethod parse 'let* - [op encl-env form _] + [op encl-env form _ _] (analyze-let encl-env form false)) (defmethod parse 'loop* - [op encl-env form _] + [op encl-env form _ _] (analyze-let encl-env form true)) (defmethod parse 'recur - [op env [_ & exprs :as form] _] + [op env [_ & exprs :as form] _ _] (let [context (:context env) frame (first *recur-frames*) exprs (disallowing-recur (vec (map #(analyze (assoc env :context :expr) %) exprs)))] @@ -899,11 +899,11 @@ :children exprs))) (defmethod parse 'quote - [_ env [_ x] _] + [_ env [_ x] _ _] (analyze (assoc env :quoted? true) x)) (defmethod parse 'new - [_ env [_ ctor & args :as form] _] + [_ env [_ ctor & args :as form] _ _] (when-not (symbol? ctor) (throw (error env "First arg to new must be a symbol"))) (disallowing-recur @@ -927,7 +927,7 @@ name))}))) (defmethod parse 'set! - [_ env [_ target val alt :as form] _] + [_ env [_ target val alt :as form] _ _] (let [[target val] (if alt ;; (set! o -prop val) [`(. ~target ~val) alt] @@ -1126,7 +1126,7 @@ args))) (defmethod parse 'ns - [_ env [_ name & args :as form] _] + [_ env [_ name & args :as form] _ opts] (when-not (symbol? name) (throw (error env "Namespaces must be named by a symbol."))) (let [docstring (if (string? (first args)) (first args)) @@ -1176,7 +1176,7 @@ :use-macros use-macros :require-macros require-macros :excludes excludes})) (defmethod parse 'deftype* - [_ env [_ tsym fields pmasks :as form] _] + [_ env [_ tsym fields pmasks :as form] _ _] (let [t (:name (resolve-var (dissoc env :locals) tsym))] (swap! env/*compiler* update-in [::namespaces (-> env :ns :name) :defs tsym] (fn [m] @@ -1191,7 +1191,7 @@ {:env env :op :deftype* :form form :t t :fields fields :pmasks pmasks})) (defmethod parse 'defrecord* - [_ env [_ tsym fields pmasks :as form] _] + [_ env [_ tsym fields pmasks :as form] _ _] (let [t (:name (resolve-var (dissoc env :locals) tsym))] (swap! env/*compiler* update-in [::namespaces (-> env :ns :name) :defs tsym] (fn [m] @@ -1262,7 +1262,7 @@ (throw (Error. (str "Unknown dot form of " (list* '. dot-form) " with classification " (classify-dot-form dot-form))))) (defmethod parse '. - [_ env [_ target & [field & member+] :as form] _] + [_ env [_ target & [field & member+] :as form] _ _] (disallowing-recur (let [{:keys [dot-action target method field args]} (build-dot-form [target field member+]) enve (assoc env :context :expr) @@ -1282,7 +1282,7 @@ :tag (-> form meta :tag)}))))) (defmethod parse 'js* - [op env [_ jsform & args :as form] _] + [op env [_ jsform & args :as form] _ _] (when-not (string? jsform) (throw (error env "Invalid js* form"))) (if args @@ -1428,24 +1428,25 @@ (declare analyze-list) (defn analyze-seq - [env form name] - (if (:quoted? env) - (analyze-list env form) - (let [env (assoc env - :line (or (-> form meta :line) - (:line env)) - :column (or (-> form meta :column) - (:column env)))] - (let [op (first form)] - (when (nil? op) - (throw (error env "Can't call nil"))) - (let [mform (macroexpand-1 env form)] - (if (identical? form mform) - (wrapping-errors env - (if (specials op) - (parse op env form name) - (parse-invoke env form))) - (analyze env mform name))))))) + ([env form name] (analyze-seq env form name nil)) + ([env form name opts] + (if (:quoted? env) + (analyze-list env form) + (let [env (assoc env + :line (or (-> form meta :line) + (:line env)) + :column (or (-> form meta :column) + (:column env)))] + (let [op (first form)] + (when (nil? op) + (throw (error env "Can't call nil"))) + (let [mform (macroexpand-1 env form)] + (if (identical? form mform) + (wrapping-errors env + (if (specials op) + (parse op env form name opts) + (parse-invoke env form))) + (analyze env mform name opts)))))))) (declare analyze-wrap-meta) @@ -1520,7 +1521,8 @@ nested exprs, must have :children [exprs...] entry. This will facilitate code walking without knowing the details of the op set." ([env form] (analyze env form nil)) - ([env form name] + ([env form name] (analyze env form name nil)) + ([env form name opts] (env/ensure (wrapping-errors env (reduce (fn [ast pass] (pass env ast)) @@ -1531,7 +1533,7 @@ (load-core) (cond (symbol? form) (analyze-symbol env form) - (and (seq? form) (seq form)) (analyze-seq env form name) + (and (seq? form) (seq form)) (analyze-seq env form name opts) (map? form) (analyze-map env form) (vector? form) (analyze-vector env form) (set? form) (analyze-set env form) @@ -1582,24 +1584,26 @@ argument, which the reader will use in any emitted errors." (cons form (forms-seq*))))))] (forms-seq*)))) -(defn analyze-file [f] - (let [res (cond - (instance? File f) f - (instance? java.net.URL f) f - (re-find #"^file://" f) (java.net.URL. f) - :else (io/resource f))] - (assert res (str "Can't find " f " in classpath")) - (env/ensure - (let [path (if (instance? File res) - (.getPath ^File res) - (.getPath ^java.net.URL res))] - (when-not (get-in @env/*compiler* [::analyzed-cljs path]) - (binding [*cljs-ns* 'cljs.user - *cljs-file* path - reader/*alias-map* (or reader/*alias-map* {})] - (let [env (empty-env)] - (doseq [form (seq (forms-seq res))] - (let [env (assoc env :ns (get-namespace *cljs-ns*))] - (analyze env form))))) - (swap! env/*compiler* assoc-in [::analyzed-cljs path] true)))))) +(defn analyze-file + ([f] (analyze-file f nil)) + ([f opts] + (let [res (cond + (instance? File f) f + (instance? java.net.URL f) f + (re-find #"^file://" f) (java.net.URL. f) + :else (io/resource f))] + (assert res (str "Can't find " f " in classpath")) + (env/ensure + (let [path (if (instance? File res) + (.getPath ^File res) + (.getPath ^java.net.URL res))] + (when-not (get-in @env/*compiler* [::analyzed-cljs path]) + (binding [*cljs-ns* 'cljs.user + *cljs-file* path + reader/*alias-map* (or reader/*alias-map* {})] + (let [env (empty-env)] + (doseq [form (seq (forms-seq res))] + (let [env (assoc env :ns (get-namespace *cljs-ns*))] + (analyze env form))))) + (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))))))) From 562e6ccc9265979c586c2afc2f50a388e6c3c03e Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 23 Aug 2014 22:04:45 -0400 Subject: [PATCH 0129/4033] actually pass opts in analyzer and compiler --- src/clj/cljs/analyzer.clj | 2 +- src/clj/cljs/compiler.clj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0d9a99464..12103f8ac 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1604,6 +1604,6 @@ argument, which the reader will use in any emitted errors." (let [env (empty-env)] (doseq [form (seq (forms-seq res))] (let [env (assoc env :ns (get-namespace *cljs-ns*))] - (analyze env form))))) + (analyze env form nil opts))))) (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))))))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 64bdfc235..e54528c88 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -905,7 +905,7 @@ deps nil] (if (seq forms) (let [env (ana/empty-env) - ast (ana/analyze env (first forms))] + ast (ana/analyze env (first forms) nil opts)] (do (emit ast) (if (= (:op ast) :ns) (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) @@ -968,7 +968,7 @@ (loop [forms (ana/forms-seq src)] (if (seq forms) (let [env (ana/empty-env) - ast (ana/no-warn (ana/analyze env (first forms)))] + ast (ana/no-warn (ana/analyze env (first forms) nil opts))] (if (= (:op ast) :ns) (let [ns-name (:name ast) deps (merge (:uses ast) (:requires ast))] From 4b81710971a1a662fdeb2db525328c3443a72545 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 25 Aug 2014 12:57:51 -0400 Subject: [PATCH 0130/4033] CLJS-845: incorrect behavior of `sequence` when given multiple collections next -> nexts typo in MultiStepper next implementation. MultiStepper step implementation incorrectly invoking next and hasNext on undefined local. Invalid construction of MultiStepper, did not invoke xform with stepfn. Added test. --- src/cljs/cljs/core.cljs | 10 +++++----- test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 1bbf7b22e..c04e2cbef 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2984,13 +2984,13 @@ reduces them without incurring seq initialization" true))) (next [_] (dotimes [i (alength iters)] - (aset next i (.next (aget iters i)))) + (aset nexts i (.next (aget iters i)))) (prim-seq nexts 0)) - (step [_ lt] + (step [this lt] (loop [] (if (and (not (nil? (.-stepper lt))) - (.hasNext iter)) - (if (reduced? (apply xform (cons lt (.next iter)))) + (.hasNext this)) + (if (reduced? (apply xform (cons lt (.next this)))) (when-not (nil? (.-rest lt)) (set! (.. lt -rest -stepper) nil)) (recur)))) @@ -3015,7 +3015,7 @@ reduces them without incurring seq initialization" (set! (.-rest lt) (lazy-transformer (.-stepper lt))) (set! (.-stepper lt) nil) (.-rest lt))))] - (MultiStepper. xform iters nexts)))) + (MultiStepper. (xform stepfn) iters nexts)))) (deftype LazyTransformer [^:mutable stepper ^:mutable first ^:mutable rest meta] IWithMeta diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 96bff4408..4f69bf9d9 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2402,5 +2402,10 @@ (assert (= (sequence xform data) '(36 200 10))) + (def xf (map #(+ %1 %2))) + + (assert (= (sequence xf [0 0] [1 2]) + [1 2])) + :ok ) From 3c0b8e71e09a971a07e3eef70bd74df6f1104d92 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Aug 2014 12:59:56 -0400 Subject: [PATCH 0131/4033] CLJS-839: Safari Math.imul issue Polyfill Math.imul in plain JS preamble so implementation does not go through advanced compilation --- src/clj/cljs/closure.clj | 10 ++++++---- src/cljs/cljs/imul.js | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 src/cljs/cljs/imul.js diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 2084c5135..9dc1369fa 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -924,10 +924,12 @@ should contain the source for the given namespace name." ([source opts compiler-env] (env/with-compiler-env compiler-env (let [ups-deps (get-upstream-deps) - all-opts (assoc opts - :ups-libs (:libs ups-deps) - :ups-foreign-libs (:foreign-libs ups-deps) - :ups-externs (:externs ups-deps)) + all-opts (-> opts + (assoc + :ups-libs (:libs ups-deps) + :ups-foreign-libs (:foreign-libs ups-deps) + :ups-externs (:externs ups-deps)) + (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) emit-constants (or (and (= (:optimizations opts) :advanced) (not (false? (:optimize-constants opts)))) (:optimize-constants opts))] diff --git a/src/cljs/cljs/imul.js b/src/cljs/cljs/imul.js new file mode 100644 index 000000000..d28a33c35 --- /dev/null +++ b/src/cljs/cljs/imul.js @@ -0,0 +1,11 @@ +if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) { + Math.imul = function (a, b) { + var ah = (a >>> 16) & 0xffff; + var al = a & 0xffff; + var bh = (b >>> 16) & 0xffff; + var bl = b & 0xffff; + // the shift by 0 fixes the sign on the high part + // the final |0 converts the unsigned value into a signed value + return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0); + } +} From 245de2e752f638f5d06527a1cafed26b0d8d0da8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 27 Aug 2014 10:31:15 -0400 Subject: [PATCH 0132/4033] Update README.md & changes.md --- README.md | 6 +++--- changes.md | 14 +++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3e3e9ff2..148e2fb06 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2311 +Latest stable release: 0.0-2322 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2311"] +[org.clojure/clojurescript "0.0-2322"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2311 org.clojure clojurescript - 0.0-2311 + 0.0-2322 ``` diff --git a/changes.md b/changes.md index 15a462898..db5b05ab6 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,15 @@ +## 0.0-2322 + +### Fixes +* CLJS-839: Mobile Safari Math.imul issue +* CLJS-845: incorrect behavior of `sequence` when given multiple collections +* count check in equiv-sequential if both arguments are ICounted +* only keep the param names when storing :method-params instead of the + entire param AST +* preserve var metadata for deftype* and defrecord* +* preserve var metadata when creating deftype/record factory fns +* CLJS-831: Extending EventType to js/Element breaks Nashorn + ## 0.0-2311 ### Fixes @@ -68,4 +80,4 @@ * CLJS-811: use the correct class loader in cljs.js-deps/goog-resource * fix fns with metadata under advanced compilation * CLJS-809: dissoc :file metadata introduced by tools.reader 0.8.4 -* mark cljs.reader vars as ^:dynamic to avoid compiler warnings \ No newline at end of file +* mark cljs.reader vars as ^:dynamic to avoid compiler warnings From edd35fa206d8285b2d671264347a1f0024631b13 Mon Sep 17 00:00:00 2001 From: Joel Holdbrooks Date: Wed, 27 Aug 2014 13:47:33 -0700 Subject: [PATCH 0133/4033] CLJS-846: Preserve namespace metadata for 'ns --- src/clj/cljs/analyzer.clj | 1 + test/clj/cljs/analyzer_tests.clj | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 12103f8ac..3838a4c50 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1133,6 +1133,7 @@ args (if docstring (next args) args) metadata (if (map? (first args)) (first args)) args (desugar-ns-specs (if metadata (next args) args)) + name (vary-meta name merge metadata) excludes (parse-ns-excludes env args) deps (atom #{}) aliases (atom {:fns #{} :macros #{}}) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index f793a99ad..67f86f61c 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -237,3 +237,36 @@ (catch Exception e (.getMessage e))) "Parameter declaration 123 should be a vector at line"))) + +;; ============================================================================= +;; Namespace metadata + +(deftest test-namespace-metadata + (binding [a/*cljs-ns* a/*cljs-ns*] + (is (= (do (a/analyze ns-env '(ns weeble {:foo bar})) + (meta a/*cljs-ns*)) + {:foo 'bar})) + + (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble)) + (meta a/*cljs-ns*)) + {:foo 'bar})) + + (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble {:baz quux})) + (meta a/*cljs-ns*)) + {:foo 'bar :baz 'quux})) + + (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble {:foo baz})) + (meta a/*cljs-ns*)) + {:foo 'baz})) + + (is (= (meta (:name (a/analyze ns-env '(ns weeble {:foo bar})))) + {:foo 'bar})) + + (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble)))) + {:foo 'bar})) + + (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble {:baz quux})))) + {:foo 'bar :baz 'quux})) + + (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble {:foo baz})))) + {:foo 'baz})))) From 08b4b1585cf0ef739e903985ee4c6b7fc6c47642 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 31 Aug 2014 10:40:22 -0400 Subject: [PATCH 0134/4033] CLJS-847: Safari toString fix In Safari 6.0.X address problems with using toString by using string concatenation instead. --- src/clj/cljs/core.clj | 3 +++ src/cljs/cljs/core.cljs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 13f1e3ded..5c812dcf5 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1641,3 +1641,6 @@ (lazy-cat xs ys zs) === (concat (lazy-seq xs) (lazy-seq ys) (lazy-seq zs))" [& colls] `(concat ~@(map #(core/list `lazy-seq %) colls))) + +(defmacro js-str [s] + (core/list 'js* "''+~{}" s)) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index c04e2cbef..61672e1d6 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2037,7 +2037,7 @@ reduces them without incurring seq initialization" ([] "") ([x] (if (nil? x) "" - (.toString x))) + (cljs.core/js-str x))) ([x & ys] (loop [sb (StringBuffer. (str x)) more ys] (if more From 699b7487805c786b2eebe122ca3f09d7a5205c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marczyk?= Date: Thu, 4 Sep 2014 11:56:31 +0200 Subject: [PATCH 0135/4033] CLJS-849: fix iteration bound in pack-array-node --- src/cljs/cljs/core.cljs | 4 ++-- test/cljs/cljs/core_test.cljs | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 61672e1d6..e5f857be1 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -5505,8 +5505,8 @@ reduces them without incurring seq initialization" (defn- pack-array-node [array-node edit idx] (let [arr (.-arr array-node) - len (* 2 (dec (.-cnt array-node))) - new-arr (make-array len)] + len (alength arr) + new-arr (make-array (* 2 (dec (.-cnt array-node))))] (loop [i 0 j 1 bitmap 0] (if (< i len) (if (and (not (== i idx)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 4f69bf9d9..a0cfa8d4b 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2407,5 +2407,15 @@ (assert (= (sequence xf [0 0] [1 2]) [1 2])) + ;; CLJS-849 + (let [xs [44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24]] + (loop [m (transient (zipmap xs (repeat 1))) + xs xs] + (if-let [x (first xs)] + (if (contains? m x) + (recur (dissoc! m x) (next xs)) + (throw (ex-info "CLJS-849 regression!" + {:m (persistent! m) :xs xs})))))) + :ok ) From 3279b62789312356ec450cdac46db2149d050207 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 5 Sep 2014 20:33:34 -0400 Subject: [PATCH 0136/4033] sync up ClojureScript transducers with Clojure See https://github.com/clojure/clojure/commit/7d84a9f6f35a503cddf98487b6544d18937c669e --- src/cljs/cljs/core.cljs | 80 ++++++++++++++++------------- src/cljs/clojure/core/reducers.cljs | 2 +- test/cljs/cljs/core_test.cljs | 8 +-- 3 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index e5f857be1..4adc56f6e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1654,25 +1654,30 @@ reduces them without incurring seq initialization" (-kv-reduce coll f init) init))) -(defn- completing [f] - (fn - ([] (f)) - ([x] x) - ([x y] (f x y)))) +(defn identity [x] x) + +(defn completing + ([f] (completing f identity)) + ([f cf] + (fn + ([] (f)) + ([x] (cf x)) + ([x y] (f x y))))) (defn transduce "reduce with a transformation of f (xf). If init is not - supplied, (f) will be called to produce it. Returns the result of - applying (the transformed) xf to init and the first item in coll, + supplied, (f) will be called to produce it. f should be a reducing + step function that accepts both 1 and 2 arguments, if it accepts + only 2 you can add the arity-1 with 'completing'. Returns the result + of applying (the transformed) xf to init and the first item in coll, then applying xf to that result and the 2nd item, etc. If coll contains no items, returns init and f is not called. Note that certain transforms may inject or skip items." ([xform f coll] (transduce xform f (f) coll)) ([xform f init coll] - (let [f (xform (completing f)) - ret (reduce f init coll) - ret (f (if (reduced? ret) @ret ret))] - (if (reduced? ret) @ret ret)))) + (let [f (xform f) + ret (reduce f init coll)] + (f ret)))) ;;; Math - variadic forms will not work until the following implemented: ;;; first, next, reduce @@ -3143,8 +3148,6 @@ reduces them without incurring seq initialization" "Returns true if n is odd, throws an exception if n is not an integer" [n] (not (even? n))) -(defn identity [x] x) - (defn ^boolean complement "Takes a fn f and returns a fn that takes the same arguments as f, has the same effects, if any, and returns the opposite truth value." @@ -3699,13 +3702,17 @@ reduces them without incurring seq initialization" (cat (first colls) (rest colls))))))] (cat nil colls))) +(declare cat) + (defn mapcat "Returns the result of applying concat to the result of applying map - to f and colls. Thus function f should return a collection." - ([f coll] - (flatten1 (map f coll))) - ([f coll & colls] - (flatten1 (apply map f coll colls)))) + to f and colls. Thus function f should return a collection. Returns + a transducer when no collections are provided" + {:added "1.0" + :static true} + ([f] (comp (map f) cat)) + ([f & colls] + (apply concat (apply map f colls)))) (defn filter "Returns a lazy sequence of the items in coll for which @@ -3778,7 +3785,7 @@ reduces them without incurring seq initialization" (reduce conj () from))) ([to xform from] (if (implements? IEditableCollection to) - (with-meta (persistent! (transduce xform -conj! (transient to) from)) (meta to)) + (with-meta (persistent! (transduce xform conj! (transient to) from)) (meta to)) (transduce xform conj to from)))) (defn mapv @@ -7345,8 +7352,7 @@ reduces them without incurring seq initialization" (let [result (if (.isEmpty a) result (let [v (vec (.toArray a))] - ;;flushing ops must clear before invoking possibly - ;;failing nested op, else infinite loop + ;;clear first! (.clear a) (f1 result v)))] (f1 result))) @@ -7543,8 +7549,7 @@ reduces them without incurring seq initialization" (let [result (if (.isEmpty a) result (let [v (vec (.toArray a))] - ;;flushing ops must clear before invoking possibly - ;;failing nested op, else infinite loop + ;;clear first! (.clear a) (f1 result v)))] (f1 result))) @@ -7559,8 +7564,10 @@ reduces them without incurring seq initialization" result) (let [v (vec (.toArray a))] (.clear a) - (.add a input) - (f1 result v))))))))) + (let [ret (f1 result v)] + (when-not (reduced? ret) + (.add a input)) + ret))))))))) ([f coll] (lazy-seq (when-let [s (seq coll)] @@ -8136,18 +8143,17 @@ reduces them without incurring seq initialization" (reduced ret) ret))) -(defn flatmap - "maps f over coll and concatenates the results. Thus function f - should return a collection. Returns a transducer when no collection - is provided." - ([f] - (fn [f1] - (fn - ([] (f1)) - ([result] (f1 result)) - ([result input] - (reduce (preserving-reduced f1) result (f input)))))) - ([f coll] (sequence (flatmap f) coll))) +(defn cat + "A transducer which concatenates the contents of each input, which must be a + collection, into the reduction." + {:added "1.7"} + [f1] + (let [rf1 (preserving-reduced f1)] + (fn + ([] (f1)) + ([result] (f1 result)) + ([result input] + (reduce rf1 result input))))) (defn dedupe "Returns a lazy sequence removing consecutive duplicates in coll. diff --git a/src/cljs/clojure/core/reducers.cljs b/src/cljs/clojure/core/reducers.cljs index a792bbe76..1a9644124 100644 --- a/src/cljs/clojure/core/reducers.cljs +++ b/src/cljs/clojure/core/reducers.cljs @@ -13,7 +13,7 @@ dependency info." :author "Rich Hickey"} clojure.core.reducers - (:refer-clojure :exclude [reduce map mapcat filter remove take take-while drop flatten]) + (:refer-clojure :exclude [reduce map mapcat filter remove take take-while drop flatten cat]) (:require [cljs.core :as core])) ;;;;;;;;;;;;;; some fj stuff ;;;;;;;;;; diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index a0cfa8d4b..c7c593a20 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2365,9 +2365,9 @@ '(2 3 4))) (assert (= (sequence (dedupe) [1 1 2 2 3 3]) '(1 2 3))) - (assert (= (flatmap reverse [[3 2 1 0] [6 5 4] [9 8 7]]) + (assert (= (mapcat reverse [[3 2 1 0] [6 5 4] [9 8 7]]) (range 10))) - (assert (= (sequence (flatmap reverse) [[3 2 1 0] [6 5 4] [9 8 7]]) + (assert (= (sequence (mapcat reverse) [[3 2 1 0] [6 5 4] [9 8 7]]) (range 10))) (assert (= (seq (iteration (map inc) [1 2 3])) '(2 3 4))) (assert (= (sequence (partition-by #{:split}) [1 2 3 :split 4 5 6]) @@ -2383,10 +2383,10 @@ (comp (map inc) (filter even?) (dedupe) - (flatmap range) + (mapcat range) (partition-all 3) (partition-by #(< (apply + %) 7)) - (flatmap flatten) + (mapcat flatten) (random-sample 1.0) (take-nth 1) (keep #(when (odd? %) (* % %))) From f7174818d30bc1e6f2dd0ed54ce6d85228b162f3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Sep 2014 18:48:39 -0400 Subject: [PATCH 0137/4033] CLJS-852: Faster group-by Copy over the Clojure implementation which uses transients. --- src/cljs/cljs/core.cljs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 4adc56f6e..ebb8bc5fe 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8332,11 +8332,12 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." f on each element. The value at each key will be a vector of the corresponding elements, in the order they appeared in coll." [f coll] - (reduce - (fn [ret x] - (let [k (f x)] - (assoc ret k (conj (get ret k []) x)))) - {} coll)) + (persistent! + (reduce + (fn [ret x] + (let [k (f x)] + (assoc! ret k (conj (get ret k []) x)))) + (transient {}) coll))) (defn make-hierarchy "Creates a hierarchy object for use with derive, isa? etc." From 877b1ab2e6bb1c09d1988348d6cb384f8ba16414 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Sep 2014 19:23:42 -0400 Subject: [PATCH 0138/4033] CLJS-858: resolve-existing-var does not check vars outside current namespace Prior to this commit we would only check vars if the prefix of the var was the current namespace. This means normal library usage is not verified. Instead now we check if the var is either: A) an implicit require/import, i.e. goog.string, Math, etc. B) is from a JS namespace (just verify the namespace) C) exists in a CLJS namespace --- src/clj/cljs/analyzer.clj | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 3838a4c50..de848a79b 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -279,11 +279,16 @@ (throw err#) (throw (error ~env (.getMessage err#) err#)))))) +(defn implicit-import? [env prefix suffix] + (contains? '#{goog goog.object goog.string goog.array Math} prefix)) + (defn confirm-var-exists [env prefix suffix] - (let [crnt-ns (-> env :ns :name)] - (when (and (= prefix crnt-ns) - (not (get-in @env/*compiler* [::namespaces crnt-ns :defs suffix]))) - (warning :undeclared-var env {:prefix prefix :suffix suffix})))) + (when (and (not (implicit-import? env prefix suffix)) + (not (and (not (get-in @env/*compiler* [::namespaces prefix])) + (or (get (:requires (:ns env)) prefix) + (get (:imports (:ns env)) prefix)))) + (not (get-in @env/*compiler* [::namespaces prefix :defs suffix]))) + (warning :undeclared-var env {:prefix prefix :suffix suffix}))) (defn resolve-ns-alias [env name] (let [sym (symbol name)] From 1f1f56e4f77618b6b51ef9e381a3cb0bd1046705 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Sep 2014 21:36:04 -0400 Subject: [PATCH 0139/4033] CLJS-855: combinatorial code generation under advanced compilation Under advanced compilation, :static-fns is true - efficient code generation was done naively resulting in exponential code size when nesting function calls of unknown arity. Now if an invoke expression involves a fn of unknown arity instead generate locals for the arguments then invoke. Google Closure will intelligently rewrite these kinds of redundant assignments under advanced compilation. --- src/clj/cljs/analyzer.clj | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index de848a79b..5ea5ad41f 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1345,11 +1345,12 @@ (defn parse-invoke [env [f & args :as form]] (disallowing-recur - (let [enve (assoc env :context :expr) - fexpr (analyze enve f) + (let [enve (assoc env :context :expr) + fexpr (analyze enve f) argexprs (vec (map #(analyze enve %) args)) - argc (count args)] - (when (-> fexpr :info :fn-var) + argc (count args) + fn-var? (-> fexpr :info :fn-var)] + (when fn-var? (let [{:keys [variadic max-fixed-arity method-params name]} (:info fexpr)] (when (and (not (some #{argc} (map count method-params))) (or (not variadic) @@ -1361,8 +1362,13 @@ (warning :fn-deprecated env {:fexpr fexpr})) (when (-> fexpr :info :type) (warning :invoke-ctor env {:fexpr fexpr})) - {:env env :op :invoke :form form :f fexpr :args argexprs - :children (into [fexpr] argexprs)}))) + (if (or (not *cljs-static-fns*) (not (symbol? f)) fn-var? (contains? (meta f) ::analyzed)) + {:env env :op :invoke :form form :f fexpr :args argexprs + :children (into [fexpr] argexprs)} + (let [arg-syms (take argc (repeatedly gensym))] + (analyze env + `(let [~@(vec (interleave arg-syms args))] + (~(vary-meta f assoc ::analyzed true) ~@arg-syms)))))))) (defn analyze-symbol "Finds the var associated with sym" From eda329327af315ed012178db2423ef8f7043686a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Sep 2014 22:32:14 -0400 Subject: [PATCH 0140/4033] don't call resolve-existing-var on an ::analyzed var, we've already analyzed and emitted a warning about it on the first pass --- src/clj/cljs/analyzer.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 5ea5ad41f..4924b6e51 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1384,7 +1384,10 @@ (if lb (assoc ret :op :var :info lb) (if-not (:def-var env) - (assoc ret :op :var :info (resolve-existing-var env sym)) + (assoc ret :op :var :info + (if-not (contains? (meta sym) ::analyzed) + (resolve-existing-var env sym) + (resolve-var env sym))) (assoc ret :op :var :info (resolve-var env sym))))))) (defn get-expander [sym env] From b2f0aaddfb63aee730ce2ab154a63c9ff2b17abc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Sep 2014 07:41:45 -0400 Subject: [PATCH 0141/4033] in parse-invoke do not analyze the arguments unless we know it's the last pass. --- src/clj/cljs/analyzer.clj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 4924b6e51..606456eb5 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1345,11 +1345,10 @@ (defn parse-invoke [env [f & args :as form]] (disallowing-recur - (let [enve (assoc env :context :expr) - fexpr (analyze enve f) - argexprs (vec (map #(analyze enve %) args)) - argc (count args) - fn-var? (-> fexpr :info :fn-var)] + (let [enve (assoc env :context :expr) + fexpr (analyze enve f) + argc (count args) + fn-var? (-> fexpr :info :fn-var)] (when fn-var? (let [{:keys [variadic max-fixed-arity method-params name]} (:info fexpr)] (when (and (not (some #{argc} (map count method-params))) @@ -1363,8 +1362,9 @@ (when (-> fexpr :info :type) (warning :invoke-ctor env {:fexpr fexpr})) (if (or (not *cljs-static-fns*) (not (symbol? f)) fn-var? (contains? (meta f) ::analyzed)) - {:env env :op :invoke :form form :f fexpr :args argexprs - :children (into [fexpr] argexprs)} + (let [argexprs (vec (map #(analyze enve %) args))] + {:env env :op :invoke :form form :f fexpr :args argexprs + :children (into [fexpr] argexprs)}) (let [arg-syms (take argc (repeatedly gensym))] (analyze env `(let [~@(vec (interleave arg-syms args))] From 9772495f959b36be712acd45554590a09d677021 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Sep 2014 08:29:54 -0400 Subject: [PATCH 0142/4033] track protocol method information --- src/clj/cljs/analyzer.clj | 3 ++- src/clj/cljs/core.clj | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 606456eb5..30526a163 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -636,7 +636,8 @@ ;; symbol for reified protocol (when-let [protocol-symbol (-> sym meta :protocol-symbol)] {:protocol-symbol protocol-symbol - :impls #{}}) + :info (-> protocol-symbol meta :protocol-info) + :impls #{}}) (when fn-var? {:fn-var true ;; protocol implementation context diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 5c812dcf5..de43c2a6a 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1007,6 +1007,13 @@ (throw (missing-protocol ~(core/str psym "." fname) ~(first sig)))) ~@sig))))) + psym (vary-meta psym assoc-in [:protocol-info :methods] + (into {} + (map + (fn [[fname & sigs]] + (let [sigs (take-while vector? sigs)] + [fname (vec sigs)])) + methods))) method (fn [[fname & sigs]] (let [sigs (take-while vector? sigs) slot (symbol (core/str prefix (name fname))) From e09bfeebf488272b1ecb136d5971010f09e904a1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Sep 2014 13:20:13 -0400 Subject: [PATCH 0143/4033] CLJS-859: use https in the boostrap script --- script/bootstrap | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index e36033d86..4273e4d62 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -11,7 +11,7 @@ TREADER_RELEASE="0.8.4" mkdir -p lib echo "Fetching Clojure..." -curl -O -s http://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.zip +curl -O -s https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.zip unzip -qu clojure-$CLOJURE_RELEASE.zip echo "Copying clojure-$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar to lib/clojure.jar..." cp clojure-$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar lib/clojure.jar @@ -22,7 +22,7 @@ echo "Cleaning up Clojure archive..." rm clojure-$CLOJURE_RELEASE.zip echo "Fetching data.json..." -curl -O -s http://repo1.maven.org/maven2/org/clojure/data.json/$DJSON_RELEASE/data.json-$DJSON_RELEASE.jar +curl -O -s https://repo1.maven.org/maven2/org/clojure/data.json/$DJSON_RELEASE/data.json-$DJSON_RELEASE.jar echo "Copying data.json-$DJSON_RELEASE.jar to lib/data.json-$DJSON_RELEASE.jar..." cp data.json-$DJSON_RELEASE.jar lib/data.json-$DJSON_RELEASE.jar echo "Cleaning up data.json..." @@ -44,15 +44,15 @@ if [ "$1" = "--closure-library-head" ] ; then else echo "Checking out HEAD of Google Closure library..." rm -rf * - svn checkout -q --non-interactive http://closure-library.googlecode.com/svn/trunk/ ./ + svn checkout -q --non-interactive https://closure-library.googlecode.com/svn/trunk/ ./ fi else - curl -O -s http://repo1.maven.org/maven2/org/clojure/google-closure-library/$GCLOSURE_LIB_RELEASE/google-closure-library-$GCLOSURE_LIB_RELEASE.jar + curl -O -s https://repo1.maven.org/maven2/org/clojure/google-closure-library/$GCLOSURE_LIB_RELEASE/google-closure-library-$GCLOSURE_LIB_RELEASE.jar cp google-closure-library-$GCLOSURE_LIB_RELEASE.jar ../../lib/google-closure-library-$GCLOSURE_LIB_RELEASE.jar rm google-closure-library-$GCLOSURE_LIB_RELEASE.jar echo "Fetching Google Closure third party library..." - curl -O -s http://repo1.maven.org/maven2/org/clojure/google-closure-library-third-party/$GCLOSURE_LIB_RELEASE/google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar + curl -O -s https://repo1.maven.org/maven2/org/clojure/google-closure-library-third-party/$GCLOSURE_LIB_RELEASE/google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar cp google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar ../../lib/google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar rm google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar fi @@ -61,7 +61,7 @@ cd .. echo "Fetching Google Closure compiler..." mkdir -p compiler cd compiler -curl -O -s http://dl.google.com/closure-compiler/compiler-latest.zip +curl -O -s https://dl.google.com/closure-compiler/compiler-latest.zip unzip -qu compiler-latest.zip echo "Cleaning up Google Closure compiler archive..." rm compiler-latest.zip @@ -74,7 +74,7 @@ if [ "$1" = "--closure-library-head" ] ; then fi echo "Fetching Rhino..." -curl -O -s http://ftp.mozilla.org/pub/mozilla.org/js/rhino$RHINO_RELEASE.zip +curl -O -s https://ftp.mozilla.org/pub/mozilla.org/js/rhino$RHINO_RELEASE.zip unzip -qu rhino$RHINO_RELEASE.zip echo "Copying rhino$RHINO_RELEASE/js.jar to lib/js.jar..." cp rhino$RHINO_RELEASE/js.jar lib/js.jar @@ -87,7 +87,7 @@ echo "Copying closure/compiler/compiler.jar to lib/compiler.jar" cp closure/compiler/compiler.jar lib echo "Fetching tools.reader $TREADER_RELEASE ..." -curl -O -s http://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar +curl -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar echo "Moving tools.reader.jar to lib/tools.reader.jar" From caef9140b9c07a846c1e065b0a62ca995e9753b3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Sep 2014 18:37:54 -0400 Subject: [PATCH 0144/4033] remove duped protocol implementations, picked up by new analysis --- src/cljs/cljs/core.cljs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index ebb8bc5fe..2d27fed55 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -5764,12 +5764,6 @@ reduces them without incurring seq initialization" IEmptyableCollection (-empty [coll] (with-meta (.-EMPTY List) meta)) - ICollection - (-conj [coll o] (cons o coll)) - - IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) - ISequential ISeq (-first [coll] @@ -5831,12 +5825,6 @@ reduces them without incurring seq initialization" IEmptyableCollection (-empty [coll] (with-meta (.-EMPTY List) meta)) - ICollection - (-conj [coll o] (cons o coll)) - - IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) - ISequential ISeq (-first [coll] (first s)) From c64cdd615e4e716c59421be797f6b3d3ea06e842 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Sep 2014 18:55:49 -0400 Subject: [PATCH 0145/4033] validate that a protocol is implemented only once --- src/clj/cljs/analyzer.clj | 16 +++++++++++++--- src/clj/cljs/core.clj | 30 +++++++++++++++++------------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 30526a163..0ce019b5f 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -50,7 +50,9 @@ :overload-arity true :extending-base-js-type true :invoke-ctor true - :invalid-arithmetic true}) + :invalid-arithmetic true + :protocol-duped-method true + :protocol-multiple-impls true}) (declare message namespaces) @@ -108,6 +110,14 @@ [warning-type info] (str "Symbol " (:protocol info) " is not a protocol")) +(defmethod error-message :protocol-duped-method + [warning-type info] + (str "Duplicated methods in protocol implementation " (:protocol info))) + +(defmethod error-message :protocol-multiple-impls + [warning-type info] + (str "Protocol " (:protocol info) " implemented multiple times")) + (defmethod error-message :multiple-variadic-overloads [warning-type info] (str (:name info) ": Can't have more than 1 variadic overload")) @@ -238,9 +248,9 @@ screen location navigator history location global process require module exports)))})) -(defmacro ^:private debug-prn +(defn debug-prn [& args] - `(.println System/err (str ~@args))) + (.println System/err (apply str args))) (defn source-info ([env] diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index de43c2a6a..a7a049caf 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -836,20 +836,23 @@ merge annots))) (defn dt->et - ([type specs fields] - (dt->et type specs fields false)) - ([type specs fields inline] + ([env type specs fields] + (dt->et env type specs fields false)) + ([env type specs fields inline] (let [annots {:cljs.analyzer/type type :cljs.analyzer/fields fields :protocol-impl true :protocol-inline inline}] - (loop [ret [] specs specs] + (loop [ret [] specs specs seen #{}] (if (seq specs) - (let [ret (-> (conj ret (first specs)) - (into (reduce (partial annotate-specs annots) [] - (group-by first (take-while seq? (next specs)))))) + (let [p (first specs) + _ (when (contains? seen p) + (ana/warning :protocol-multiple-impls env {:protocol p})) + ret (-> (conj ret p) + (into (reduce (partial annotate-specs annots) [] + (group-by first (take-while seq? (next specs)))))) specs (drop-while seq? (next specs))] - (recur ret specs)) + (recur ret specs (conj seen p))) ret))))) (defn collect-protocols [impls env] @@ -866,9 +869,10 @@ (new ~rname ~@fields)))) (defmacro deftype [t fields & impls] - (let [r (:name (cljs.analyzer/resolve-var (dissoc &env :locals) t)) - [fpps pmasks] (prepare-protocol-masks &env impls) - protocols (collect-protocols impls &env) + (let [env &env + r (:name (cljs.analyzer/resolve-var (dissoc env :locals) t)) + [fpps pmasks] (prepare-protocol-masks env impls) + protocols (collect-protocols impls env) t (vary-meta t assoc :protocols protocols :skip-protocol-flag fpps) ] @@ -878,7 +882,7 @@ (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)))) - (extend-type ~t ~@(dt->et t impls fields true)) + (extend-type ~t ~@(dt->et env t impls fields true)) ~(build-positional-factory t r fields) ~t) `(do @@ -965,7 +969,7 @@ :skip-protocol-flag fpps)] `(do (~'defrecord* ~tagname ~hinted-fields ~pmasks) - (extend-type ~tagname ~@(dt->et tagname impls fields true)))))) + (extend-type ~tagname ~@(dt->et env tagname impls fields true)))))) (defn- build-map-factory [rsym rname fields] (let [fn-name (symbol (core/str 'map-> rsym)) From 2b87a240f81acfa5a5b076bb51896cfec5ddb78c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Sep 2014 19:49:54 -0400 Subject: [PATCH 0146/4033] duplicate protocol validation needs to be in extend-type, not in dt->et --- src/clj/cljs/analyzer.clj | 7 ++++++- src/clj/cljs/core.clj | 27 ++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0ce019b5f..f6bd8d6b7 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -51,6 +51,7 @@ :extending-base-js-type true :invoke-ctor true :invalid-arithmetic true + :protocol-invalid-method true :protocol-duped-method true :protocol-multiple-impls true}) @@ -110,9 +111,13 @@ [warning-type info] (str "Symbol " (:protocol info) " is not a protocol")) +(defmethod error-message :protocol-invalid-method + [warning-type info] + (str "Bad method signature in protocol implementation " (:protocol info) " " (:fname info))) + (defmethod error-message :protocol-duped-method [warning-type info] - (str "Duplicated methods in protocol implementation " (:protocol info))) + (str "Duplicated methods in protocol implementation " (:protocol info) " " (:fname info))) (defmethod error-message :protocol-multiple-impls [warning-type info] diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index a7a049caf..be4fbccda 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -795,8 +795,19 @@ (add-proto-methods* pprefix type type-sym sig))) sigs))))) +(defn validate-impls [env impls] + (loop [protos #{} impls impls] + (when (seq impls) + (let [proto (first impls) + methods (take-while seq? (next impls)) + impls (drop-while seq? (next impls))] + (when (contains? protos proto) + (ana/warning :protocol-multiple-impls env {:protocol proto})) + (recur (conj protos proto) impls))))) + (defmacro extend-type [type-sym & impls] (let [env &env + _ (validate-impls env impls) resolve (partial resolve-var env) impl-map (->impl-map impls) [type assign-impls] (if-let [type (base-type type-sym)] @@ -836,23 +847,21 @@ merge annots))) (defn dt->et - ([env type specs fields] - (dt->et env type specs fields false)) - ([env type specs fields inline] + ([type specs fields] + (dt->et type specs fields false)) + ([type specs fields inline] (let [annots {:cljs.analyzer/type type :cljs.analyzer/fields fields :protocol-impl true :protocol-inline inline}] - (loop [ret [] specs specs seen #{}] + (loop [ret [] specs specs] (if (seq specs) (let [p (first specs) - _ (when (contains? seen p) - (ana/warning :protocol-multiple-impls env {:protocol p})) ret (-> (conj ret p) (into (reduce (partial annotate-specs annots) [] (group-by first (take-while seq? (next specs)))))) specs (drop-while seq? (next specs))] - (recur ret specs (conj seen p))) + (recur ret specs)) ret))))) (defn collect-protocols [impls env] @@ -882,7 +891,7 @@ (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)))) - (extend-type ~t ~@(dt->et env t impls fields true)) + (extend-type ~t ~@(dt->et t impls fields true)) ~(build-positional-factory t r fields) ~t) `(do @@ -969,7 +978,7 @@ :skip-protocol-flag fpps)] `(do (~'defrecord* ~tagname ~hinted-fields ~pmasks) - (extend-type ~tagname ~@(dt->et env tagname impls fields true)))))) + (extend-type ~tagname ~@(dt->et tagname impls fields true)))))) (defn- build-map-factory [rsym rname fields] (let [fn-name (symbol (core/str 'map-> rsym)) From 78d5ceb31c7163a74f569371f8306003b789cb18 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 18 Sep 2014 01:16:07 -0400 Subject: [PATCH 0147/4033] fix IFn, add missing IMeta protocol symbol --- src/cljs/cljs/core.cljs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 2d27fed55..c9347888f 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -223,9 +223,10 @@ [this a b c d e f g h i j k l m n o] [this a b c d e f g h i j k l m n o p] [this a b c d e f g h i j k l m n o p q] - [this a b c d e f g h i j k l m n o p q s] - [this a b c d e f g h i j k l m n o p q s t] - [this a b c d e f g h i j k l m n o p q s t rest])) + [this a b c d e f g h i j k l m n o p q r] + [this a b c d e f g h i j k l m n o p q r s] + [this a b c d e f g h i j k l m n o p q r s t] + [this a b c d e f g h i j k l m n o p q r s t rest])) (defprotocol ICloneable (^clj -clone [value])) @@ -4188,6 +4189,7 @@ reduces them without incurring seq initialization" IWithMeta (-with-meta [coll m] (chunked-seq vec node i off m)) + IMeta (-meta [coll] meta) ISeqable From a87d98b2330ed20fc7071dab9fc260bae41ce192 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 18 Sep 2014 01:23:17 -0400 Subject: [PATCH 0148/4033] CLJS-702: warn if protocol signature doesn't matched declared check that protocol implementation method signatures are not repeated and actually match --- src/clj/cljs/core.clj | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index be4fbccda..e84a8f045 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -795,6 +795,24 @@ (add-proto-methods* pprefix type type-sym sig))) sigs))))) +(defn validate-impl-sigs [env p method] + (when-not (= p 'Object) + (let [var (ana/resolve-var (dissoc env :locals) p) + minfo (-> var :protocol-info :methods) + [fname sigs] (if (core/vector? (second method)) + [(first method) [(second method)]] + [(first method) (map first (rest method))]) + decmeths (core/get minfo fname)] + (loop [sigs sigs seen #{}] + (when (seq sigs) + (let [sig (first sigs) + c (count sig)] + (when (contains? seen c) + (ana/warning :protocol-duped-method env {:protocol p :fname fname})) + (when-not (some #{c} (map count decmeths)) + (ana/warning :protocol-invalid-method env {:protocol p :fname fname})) + (recur (next sigs) (conj seen c)))))))) + (defn validate-impls [env impls] (loop [protos #{} impls impls] (when (seq impls) @@ -803,6 +821,8 @@ impls (drop-while seq? (next impls))] (when (contains? protos proto) (ana/warning :protocol-multiple-impls env {:protocol proto})) + (core/doseq [method methods] + (validate-impl-sigs env proto method)) (recur (conj protos proto) impls))))) (defmacro extend-type [type-sym & impls] From e7e79f21a12b2fa17499f7914a803a39aa382451 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 18 Sep 2014 08:16:32 -0400 Subject: [PATCH 0149/4033] 0.0-2341 --- README.md | 6 +++--- changes.md | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 148e2fb06..a2fa54628 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2322 +Latest stable release: 0.0-2341 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2322"] +[org.clojure/clojurescript "0.0-2341"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2322 org.clojure clojurescript - 0.0-2322 + 0.0-2341 ``` diff --git a/changes.md b/changes.md index db5b05ab6..a7de9351b 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,18 @@ +## 0.0-2341 + +### Enhancements +* transducers + +### Fixes +* CLJS-704: warn if protocol extended to type multiple times in extend-type +* CLJS-702: warn if protocol doesn't match declared +* CLJS-859: use https for the bootstrap script +* CLJS-855: combinatorial code generation under advanced +* CLJS-858: resolve-existing var does not check vars outside current ns +* CLJS-852: same group-by as Clojure +* CLJS-847: Safari toString fix +* CLJS-846: preserve namespace metadata + ## 0.0-2322 ### Fixes From 1b5fdbf2a7aa76c49deb48d422ef48f0df0df86f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 18 Sep 2014 15:01:53 -0400 Subject: [PATCH 0150/4033] 0.0-2342 --- README.md | 6 +++--- changes.md | 5 +++++ pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a2fa54628..d58c17545 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2341 +Latest stable release: 0.0-2342 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2341"] +[org.clojure/clojurescript "0.0-2342"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2341 org.clojure clojurescript - 0.0-2341 + 0.0-2342 ``` diff --git a/changes.md b/changes.md index a7de9351b..c23bcecfa 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2342 + +### Changes +* depend on tools.reader 0.8.9 + ## 0.0-2341 ### Enhancements diff --git a/pom.template.xml b/pom.template.xml index 731522c9b..20bfef61e 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.8.4 + 0.8.9 diff --git a/project.clj b/project.clj index 02ef0fc4e..00a057801 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.6.0"] [org.clojure/data.json "0.2.3"] - [org.clojure/tools.reader "0.8.4"] + [org.clojure/tools.reader "0.8.9"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20140625"] [org.mozilla/rhino "1.7R4"]] diff --git a/script/bootstrap b/script/bootstrap index 4273e4d62..5b5bdfa79 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,7 +6,7 @@ CLOJURE_RELEASE="1.6.0" DJSON_RELEASE="0.2.3" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R3" -TREADER_RELEASE="0.8.4" +TREADER_RELEASE="0.8.9" mkdir -p lib From 7f33a667faac8d38402425735b260954b60ba4b5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Sep 2014 16:31:28 -0400 Subject: [PATCH 0151/4033] lift *clojurescript-version* and compiled-by-version into a namespace that we can share between analyzer/compiler/closure --- script/build | 4 ++-- src/clj/cljs/closure.clj | 7 ++++--- src/clj/cljs/compiler.clj | 32 +++++--------------------------- src/clj/cljs/util.clj | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 32 deletions(-) create mode 100644 src/clj/cljs/util.clj diff --git a/script/build b/script/build index cdaf00e72..6e5aa85bb 100755 --- a/script/build +++ b/script/build @@ -29,8 +29,8 @@ TAG=r$REVISION sed -e s/CLOJURESCRIPT_VERSION/0.0-$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` -sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/compiler.clj > $COMP_FILE -mv $COMP_FILE src/clj/cljs/compiler.clj +sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/util.clj > $COMP_FILE +mv $COMP_FILE src/clj/cljs/util.clj CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/cljs/cljs/core.cljs > $CLJS_FILE diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 9dc1369fa..8f9e89e66 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -33,7 +33,8 @@ The produced output is either a single string of optimized JavaScript or a deps file for use during development. " - (:require [cljs.compiler :as comp] + (:require [cljs.util :as util] + [cljs.compiler :as comp] [cljs.analyzer :as ana] [cljs.source-map :as sm] [cljs.env :as env] @@ -364,8 +365,8 @@ (or (when output-file (let [out-file (io/file (output-directory opts) output-file)] (when (and (.exists out-file) - (= (comp/compiled-by-version out-file) - (comp/clojurescript-version))) + (= (util/compiled-by-version out-file) + (util/clojurescript-version))) (compile-file (io/file (output-directory opts) (last (string/split (.getPath ^URL this) #"\.jar!/"))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index e54528c88..0bdd962aa 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -8,7 +8,8 @@ (ns cljs.compiler (:refer-clojure :exclude [munge macroexpand-1]) - (:require [clojure.java.io :as io] + (:require [cljs.util :as util] + [clojure.java.io :as io] [clojure.string :as string] [clojure.tools.reader :as reader] [cljs.env :as env] @@ -20,23 +21,6 @@ (set! *warn-on-reflection* true) -;; next line is auto-generated by the build-script - Do not edit! -(def ^:dynamic *clojurescript-version*) - -(defn clojurescript-version - "Returns clojurescript version as a printable string." - [] - (str - (:major *clojurescript-version*) - "." - (:minor *clojurescript-version*) - (when-let [i (:incremental *clojurescript-version*)] - (str "." i)) - (when-let [q (:qualifier *clojurescript-version*)] - (str "-" q)) - (when (:interim *clojurescript-version*) - "-SNAPSHOT"))) - (def js-reserved #{"abstract" "boolean" "break" "byte" "case" "catch" "char" "class" "const" "continue" @@ -899,7 +883,7 @@ {:source-map (sorted-map) :gen-col 0 :gen-line 0}))] - (emitln "// Compiled by ClojureScript " (clojurescript-version)) + (emitln "// Compiled by ClojureScript " (util/clojurescript-version)) (loop [forms (ana/forms-seq src) ns-name nil deps nil] @@ -935,12 +919,6 @@ (swap! env/*compiler* assoc-in [::ana/analyzed-cljs path] true)) ret))))))))) -(defn compiled-by-version [^File f] - (with-open [reader (io/reader f)] - (let [match (->> reader line-seq first - (re-matches #".*ClojureScript (.*)$"))] - (and match (second match))))) - (defn requires-compilation? "Return true if the src file requires compilation." ([src dest] (requires-compilation? src dest nil)) @@ -948,8 +926,8 @@ (env/ensure (or (not (.exists dest)) (> (.lastModified src) (.lastModified dest)) - (let [version' (compiled-by-version dest) - version (clojurescript-version)] + (let [version' (util/compiled-by-version dest) + version (util/clojurescript-version)] (and version (not= version version'))) (and opts (:source-map opts) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj new file mode 100644 index 000000000..0e4eb539c --- /dev/null +++ b/src/clj/cljs/util.clj @@ -0,0 +1,35 @@ +; 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.util + (:require [clojure.java.io :as io]) + (:import [java.io File])) + +;; next line is auto-generated by the build-script - Do not edit! +(def ^:dynamic *clojurescript-version*) + +(defn clojurescript-version + "Returns clojurescript version as a printable string." + [] + (str + (:major *clojurescript-version*) + "." + (:minor *clojurescript-version*) + (when-let [i (:incremental *clojurescript-version*)] + (str "." i)) + (when-let [q (:qualifier *clojurescript-version*)] + (str "-" q)) + (when (:interim *clojurescript-version*) + "-SNAPSHOT"))) + +(defn compiled-by-version [^File f] + (with-open [reader (io/reader f)] + (let [match (->> reader line-seq first + (re-matches #".*ClojureScript (.*)$"))] + (and match (second match))))) + From 8975cd0268a17970700f86301807e08b5432468e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Sep 2014 16:59:43 -0400 Subject: [PATCH 0152/4033] add cljs.analyzer/requires-analysis? --- src/clj/cljs/analyzer.clj | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index f6bd8d6b7..44318291c 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -8,7 +8,8 @@ (ns cljs.analyzer (:refer-clojure :exclude [macroexpand-1]) - (:require [clojure.java.io :as io] + (:require [cljs.util :as util] + [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] [cljs.env :as env] @@ -1615,6 +1616,13 @@ argument, which the reader will use in any emitted errors." (cons form (forms-seq*))))))] (forms-seq*)))) +(defn requires-analysis? [^File f ^File cache] + (or (not (.exists cache)) + (> (.lastModified f) (.lastModified cache)) + (let [version' (util/compiled-by-version cache) + version (util/clojurescript-version)] + (and version (not= version version'))))) + (defn analyze-file ([f] (analyze-file f nil)) ([f opts] From f73e4820e6aa3a1e22b6c01778a180ec2fa13e03 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Sep 2014 18:04:17 -0400 Subject: [PATCH 0153/4033] change cljs.analyzer/analyze-file internally to return the ns of the analyzed file as a local in a let, prep to write out the analysis cache file. --- src/clj/cljs/analyzer.clj | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 44318291c..31c53ff23 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1640,9 +1640,15 @@ argument, which the reader will use in any emitted errors." (binding [*cljs-ns* 'cljs.user *cljs-file* path reader/*alias-map* (or reader/*alias-map* {})] - (let [env (empty-env)] - (doseq [form (seq (forms-seq res))] - (let [env (assoc env :ns (get-namespace *cljs-ns*))] - (analyze env form nil opts))))) - (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))))))) + (let [env (empty-env) + ns (loop [ns nil forms (seq (forms-seq res))] + (if forms + (let [form (first forms) + env (assoc env :ns (get-namespace *cljs-ns*)) + ast (analyze env form)] + (if (= (:op ast) :ns) + (recur (:name ast) (next forms)) + (recur ns (next forms)))) + ns))] + (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))))))))) From 88873ed1053810d869febe28e58a45fa24769437 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Sep 2014 18:31:20 -0400 Subject: [PATCH 0154/4033] move namespace conversion to file path utilities into cljs.util, move parse-ns into cljs.analyzer where it belongs --- src/clj/cljs/analyzer.clj | 46 +++++++++++++++++++++++------ src/clj/cljs/closure.clj | 10 +++---- src/clj/cljs/compiler.clj | 61 ++------------------------------------- src/clj/cljs/util.clj | 30 ++++++++++++++++++- 4 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 31c53ff23..71d8dfff9 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -162,12 +162,6 @@ `(binding [*cljs-warning-handlers* ~handlers] ~@body)) -(defn munge-path [ss] - (clojure.lang.Compiler/munge (str ss))) - -(defn ns->relpath [s] - (str (string/replace (munge-path s) \. \/) ".cljs")) - (def ^:private constant-counter (atom 0)) (defn gen-constant-id [value] @@ -315,7 +309,7 @@ (nil? (get (-> env :ns :requires) ns-sym)) ;; macros may refer to namespaces never explicitly required ;; confirm that the library at least exists - (nil? (io/resource (ns->relpath ns-sym)))) + (nil? (io/resource (util/ns->relpath ns-sym)))) (warning :undeclared-ns env {:ns-sym ns-sym}))) (defn core-name? @@ -1005,7 +999,7 @@ (when-not (or (contains? (::namespaces @env/*compiler*) dep) (contains? (:js-dependency-index @env/*compiler*) (name dep)) (deps/find-classpath-lib dep)) - (let [relpath (ns->relpath dep) + (let [relpath (util/ns->relpath dep) src (locate-src relpath)] (if src (analyze-file src) @@ -1616,6 +1610,40 @@ argument, which the reader will use in any emitted errors." (cons form (forms-seq*))))))] (forms-seq*)))) +(defn parse-ns + ([src] (parse-ns src nil nil)) + ([src dest opts] + (env/ensure + (let [namespaces' (::namespaces @env/*compiler*) + ret + (binding [*cljs-ns* 'cljs.user + *analyze-deps* false] + (loop [forms (forms-seq src)] + (if (seq forms) + (let [env (empty-env) + ast (no-warn (analyze env (first forms) nil opts))] + (if (= (:op ast) :ns) + (let [ns-name (:name ast) + deps (merge (:uses ast) (:requires ast))] + (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:opts :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))}))) + (recur (rest forms)))))))] + ;; TODO this _was_ a reset! of the old namespaces atom; should we capture and + ;; then restore the entirety of env/*compiler* here instead? + (swap! env/*compiler* assoc ::namespaces namespaces') + ret)))) + (defn requires-analysis? [^File f ^File cache] (or (not (.exists cache)) (> (.lastModified f) (.lastModified cache)) @@ -1625,7 +1653,7 @@ argument, which the reader will use in any emitted errors." (defn analyze-file ([f] (analyze-file f nil)) - ([f opts] + ([f {:keys [output-dir] :as opts}] (let [res (cond (instance? File f) f (instance? java.net.URL f) f diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 8f9e89e66..0f126b33b 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -619,7 +619,7 @@ should contain the source for the given namespace name." (let [{:keys [provides source-url]} source] (if (and provides source-url) (assoc relpaths (.getPath ^URL source-url) - (ana/ns->relpath (first provides))) + (util/ns->relpath (first provides))) relpaths)) (if-let [url (:url source)] (let [path (.getPath ^URL url)] @@ -696,14 +696,14 @@ should contain the source for the given namespace name." (defn path-relative-to "Generate a string which is the path to input relative to base." [^File base input] - (let [base-path (comp/path-seq (.getCanonicalPath base)) - input-path (comp/path-seq (.getCanonicalPath (io/file ^URL (deps/-url input)))) + (let [base-path (util/path-seq (.getCanonicalPath base)) + input-path (util/path-seq (.getCanonicalPath (io/file ^URL (deps/-url input)))) count-base (count base-path) common (count (take-while true? (map #(= %1 %2) base-path input-path))) prefix (repeat (- count-base common 1) "..")] (if (= count-base common) (last input-path) ;; same file - (comp/to-path (concat prefix (drop common input-path)) "/")))) + (util/to-path (concat prefix (drop common input-path)) "/")))) (defn add-dep-string "Return a goog.addDependency string for an input." @@ -773,7 +773,7 @@ should contain the source for the given namespace name." (let [out-file (if-let [ns (and (:source-map opts) (first (:provides js)))] (io/file (io/file (output-directory opts)) - (ana/ns->relpath ns))) + (util/ns->relpath ns))) source-url (:source-url js)] (when (and out-file source-url (or (not (.exists ^File out-file)) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 0bdd962aa..8c8482a74 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -935,40 +935,6 @@ (not (.exists (io/file (str (.getPath dest) ".map")))) (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))))))) -(defn parse-ns - ([src] (parse-ns src nil nil)) - ([src dest opts] - (env/ensure - (let [namespaces' (::ana/namespaces @env/*compiler*) - ret - (binding [ana/*cljs-ns* 'cljs.user - ana/*analyze-deps* false] - (loop [forms (ana/forms-seq src)] - (if (seq forms) - (let [env (ana/empty-env) - ast (ana/no-warn (ana/analyze env (first forms) nil opts))] - (if (= (:op ast) :ns) - (let [ns-name (:name ast) - deps (merge (:uses ast) (:requires ast))] - (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:opts :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when (and dest (.exists ^File dest)) - {:lines (with-open [reader (io/reader dest)] - (-> reader line-seq count))}))) - (recur (rest forms)))))))] - ;; TODO this _was_ a reset! of the old ana/namespaces atom; should we capture and - ;; then restore the entirety of env/*compiler* here instead? - (swap! env/*compiler* assoc ::ana/namespaces namespaces') - ret)))) - (defn compile-file "Compiles src to a file of the same name, but with a .js extension, in the src file's directory. @@ -992,7 +958,7 @@ dest-file (io/file dest)] (if (.exists src-file) (try - (let [{ns :ns :as ns-info} (parse-ns src-file dest-file opts)] + (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts)] (if (requires-compilation? src-file dest-file opts) (do (mkdirs dest-file) (when (contains? (::ana/namespaces @env/*compiler*) ns) @@ -1007,29 +973,6 @@ (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) (throw (java.io.FileNotFoundException. (str "The file " src " does not exist."))))))) -(defn path-seq - [file-str] - (->> File/separator - java.util.regex.Pattern/quote - re-pattern - (string/split file-str))) - -(defn to-path - ([parts] - (to-path parts File/separator)) - ([parts sep] - (apply str (interpose sep parts)))) - -(defn ^File to-target-file - [target cljs-file] - (let [relative-path (string/split - (ana/munge-path - (str (:ns (parse-ns cljs-file)))) #"\.") - parents (butlast relative-path)] - (io/file - (io/file (to-path (cons target parents))) - (str (last relative-path) ".js")))) - (defn cljs-files-in "Return a sequence of all .cljs files in the given directory." [dir] @@ -1056,7 +999,7 @@ output-files []] (if (seq cljs-files) (let [cljs-file (first cljs-files) - output-file (to-target-file target-dir cljs-file) + output-file (util/to-target-file target-dir cljs-file (ana/parse-ns cljs-file)) ns-info (compile-file cljs-file output-file opts)] (recur (rest cljs-files) (conj output-files (assoc ns-info :file-name (.getPath output-file))))) output-files))))) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 0e4eb539c..5c5142369 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -7,7 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.util - (:require [clojure.java.io :as io]) + (:require [clojure.java.io :as io] + [clojure.string :as string]) (:import [java.io File])) ;; next line is auto-generated by the build-script - Do not edit! @@ -33,3 +34,30 @@ (re-matches #".*ClojureScript (.*)$"))] (and match (second match))))) +(defn munge-path [ss] + (clojure.lang.Compiler/munge (str ss))) + +(defn ns->relpath [s] + (str (string/replace (munge-path s) \. \/) ".cljs")) + +(defn path-seq + [file-str] + (->> File/separator + java.util.regex.Pattern/quote + re-pattern + (string/split file-str))) + +(defn to-path + ([parts] + (to-path parts File/separator)) + ([parts sep] + (apply str (interpose sep parts)))) + +(defn ^File to-target-file + [target-dir cljs-file ns-info] + (let [relative-path (string/split + (munge-path (str (:ns ns-info))) #"\.") + parents (butlast relative-path)] + (io/file + (io/file (to-path (cons target-dir parents))) + (str (last relative-path) ".js")))) From e1014b637aaae1b6c8470021f0f0758ee8a80cea Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Sep 2014 18:57:57 -0400 Subject: [PATCH 0155/4033] pass opts into analyze --- src/clj/cljs/analyzer.clj | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 71d8dfff9..26c0cf287 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -991,19 +991,21 @@ (when (and (.exists f) (.isFile f)) f)))) -(defn analyze-deps [lib deps env] - (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] - (assert (every? #(not (contains? *cljs-dep-set* %)) deps) - (str "Circular dependency detected " (-> *cljs-dep-set* meta :dep-path))) - (doseq [dep deps] - (when-not (or (contains? (::namespaces @env/*compiler*) dep) - (contains? (:js-dependency-index @env/*compiler*) (name dep)) - (deps/find-classpath-lib dep)) - (let [relpath (util/ns->relpath dep) - src (locate-src relpath)] - (if src - (analyze-file src) - (warning :undeclared-ns env {:ns-sym dep}))))))) +(defn analyze-deps + ([lib deps env] (analyze-deps lib deps nil)) + ([lib deps env opts] + (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] + (assert (every? #(not (contains? *cljs-dep-set* %)) deps) + (str "Circular dependency detected " (-> *cljs-dep-set* meta :dep-path))) + (doseq [dep deps] + (when-not (or (contains? (::namespaces @env/*compiler*) dep) + (contains? (:js-dependency-index @env/*compiler*) (name dep)) + (deps/find-classpath-lib dep)) + (let [relpath (util/ns->relpath dep) + src (locate-src relpath)] + (if src + (analyze-file src) + (warning :undeclared-ns env {:ns-sym dep})))))))) (defn check-uses [uses env] (doseq [[sym lib] uses] @@ -1171,7 +1173,7 @@ (apply merge-with merge m (map (spec-parsers k) libs))) {} (remove (fn [[r]] (= r :refer-clojure)) args))] (when (and *analyze-deps* (seq @deps)) - (analyze-deps name @deps env)) + (analyze-deps name @deps env opts)) (when (seq uses) (check-uses uses env)) (set! *cljs-ns* name) @@ -1673,7 +1675,7 @@ argument, which the reader will use in any emitted errors." (if forms (let [form (first forms) env (assoc env :ns (get-namespace *cljs-ns*)) - ast (analyze env form)] + ast (analyze env form opts)] (if (= (:op ast) :ns) (recur (:name ast) (next forms)) (recur ns (next forms)))) From 80ea15ed80f90669379dc08d40edd399fd415ae7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Sep 2014 19:42:01 -0400 Subject: [PATCH 0156/4033] convert with-core-cljs into a function of 2 arguments, compilers options and the body to execute after loading core. in all the locations where we invoke cljs.analyzer/analyze-file pass along the compiler options. in cljs.analyzer/analyze-file compute the location of the cache file. --- src/clj/cljs/analyzer.clj | 10 +++- src/clj/cljs/closure.clj | 11 ++-- src/clj/cljs/compiler.clj | 107 +++++++++++++++++----------------- src/clj/cljs/repl/browser.clj | 2 +- 4 files changed, 69 insertions(+), 61 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 26c0cf287..99491705a 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1004,7 +1004,7 @@ (let [relpath (util/ns->relpath dep) src (locate-src relpath)] (if src - (analyze-file src) + (analyze-file src opts) (warning :undeclared-ns env {:ns-sym dep})))))))) (defn check-uses [uses env] @@ -1653,6 +1653,10 @@ argument, which the reader will use in any emitted errors." version (util/clojurescript-version)] (and version (not= version version'))))) +(defn cache-file [f output-dir] + (let [ns-info (parse-ns f)] + (io/file (util/to-target-file output-dir f ns-info) ".cache.edn"))) + (defn analyze-file ([f] (analyze-file f nil)) ([f {:keys [output-dir] :as opts}] @@ -1665,7 +1669,9 @@ argument, which the reader will use in any emitted errors." (env/ensure (let [path (if (instance? File res) (.getPath ^File res) - (.getPath ^java.net.URL res))] + (.getPath ^java.net.URL res)) + cache (if output-dir + (cache-file f output-dir))] (when-not (get-in @env/*compiler* [::analyzed-cljs path]) (binding [*cljs-ns* 'cljs.user *cljs-file* path diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 0f126b33b..b3823dcc4 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -296,11 +296,12 @@ (defn compile-form-seq "Compile a sequence of forms to a JavaScript source string." [forms] - (comp/with-core-cljs - (with-out-str - (binding [ana/*cljs-ns* 'cljs.user] - (doseq [form forms] - (comp/emit (ana/analyze (ana/empty-env) form))))))) + (comp/with-core-cljs nil + (fn [] + (with-out-str + (binding [ana/*cljs-ns* 'cljs.user] + (doseq [form forms] + (comp/emit (ana/analyze (ana/empty-env) form)))))))) (defn output-directory [opts] (or (:output-dir opts) "out")) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 8c8482a74..4e88de629 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -858,12 +858,13 @@ [^File f] (.mkdirs (.getParentFile (.getCanonicalFile f)))) -(defmacro with-core-cljs +(defn with-core-cljs "Ensure that core.cljs has been loaded." - [& body] - `(do (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) - (ana/analyze-file "cljs/core.cljs")) - ~@body)) + [opts body] + (do + (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) + (ana/analyze-file "cljs/core.cljs" opts)) + (body))) (defn url-path [^File f] (.getPath (.toURL (.toURI f)))) @@ -872,52 +873,53 @@ ([src dest] (compile-file* src dest nil)) ([src dest opts] (env/ensure - (with-core-cljs - (with-open [out ^java.io.Writer (io/make-writer dest {})] - (binding [*out* out - ana/*cljs-ns* 'cljs.user - ana/*cljs-file* (.getPath ^File src) - reader/*alias-map* (or reader/*alias-map* {}) - *source-map-data* (when (:source-map opts) - (atom - {:source-map (sorted-map) - :gen-col 0 - :gen-line 0}))] - (emitln "// Compiled by ClojureScript " (util/clojurescript-version)) - (loop [forms (ana/forms-seq src) - ns-name nil - deps nil] - (if (seq forms) - (let [env (ana/empty-env) - ast (ana/analyze env (first forms) nil opts)] - (do (emit ast) - (if (= (:op ast) :ns) - (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) - (recur (rest forms) ns-name deps)))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:opts :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= (:optimizations opts) :none)) - (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" (.getName sm-file)) - (spit sm-file - (sm/encode {(url-path src) (:source-map sm-data)} - {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest)})))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret) - (swap! env/*compiler* assoc-in [::ana/analyzed-cljs path] true)) - ret))))))))) + (with-core-cljs opts + (fn [] + (with-open [out ^java.io.Writer (io/make-writer dest {})] + (binding [*out* out + ana/*cljs-ns* 'cljs.user + ana/*cljs-file* (.getPath ^File src) + reader/*alias-map* (or reader/*alias-map* {}) + *source-map-data* (when (:source-map opts) + (atom + {:source-map (sorted-map) + :gen-col 0 + :gen-line 0}))] + (emitln "// Compiled by ClojureScript " (util/clojurescript-version)) + (loop [forms (ana/forms-seq src) + ns-name nil + deps nil] + (if (seq forms) + (let [env (ana/empty-env) + ast (ana/analyze env (first forms) nil opts)] + (do (emit ast) + (if (= (:op ast) :ns) + (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) + (recur (rest forms) ns-name deps)))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:opts :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= (:optimizations opts) :none)) + (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] + (emits "\n//# sourceMappingURL=" (.getName sm-file)) + (spit sm-file + (sm/encode {(url-path src) (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 2) + :file (url-path dest)})))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret) + (swap! env/*compiler* assoc-in [::ana/analyzed-cljs path] true)) + ret)))))))))) (defn requires-compilation? "Return true if the src file requires compilation." @@ -966,8 +968,7 @@ (compile-file* src-file dest-file opts)) (do (when-not (contains? (::ana/namespaces @env/*compiler*) ns) - (with-core-cljs - (ana/analyze-file src-file))) + (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) ns-info))) (catch Exception e (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index a1a1790df..d3e706d3c 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -182,7 +182,7 @@ (-setup [this] (do (require 'cljs.repl.reflect) (repl/analyze-source (:src this)) - (comp/with-core-cljs (server/start this)))) + (comp/with-core-cljs nil (fn [] (server/start this))))) (-evaluate [_ _ _ js] (browser-eval js)) (-load [this ns url] (load-javascript this ns url)) (-tear-down [_] From 19dd53c683a6d37a53ac1809df7cdd46080ffeb8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Sep 2014 20:05:29 -0400 Subject: [PATCH 0157/4033] comment out cache-file bit for now, need to juggle a few different types for this to work. --- src/clj/cljs/analyzer.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 99491705a..8ae4cb4ff 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1670,8 +1670,9 @@ argument, which the reader will use in any emitted errors." (let [path (if (instance? File res) (.getPath ^File res) (.getPath ^java.net.URL res)) - cache (if output-dir - (cache-file f output-dir))] + ;; cache (if output-dir + ;; (cache-file res output-dir)) + ] (when-not (get-in @env/*compiler* [::analyzed-cljs path]) (binding [*cljs-ns* 'cljs.user *cljs-file* path From e5d8e80040ed3a2494e37f1b15dd7833369c61e5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 20 Sep 2014 21:03:59 -0400 Subject: [PATCH 0158/4033] clean up cljs.util/to-target-file usage, only namespace info is needed not the original source file --- src/clj/cljs/analyzer.clj | 2 +- src/clj/cljs/compiler.clj | 2 +- src/clj/cljs/util.clj | 10 ++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 8ae4cb4ff..1919bfb91 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1655,7 +1655,7 @@ argument, which the reader will use in any emitted errors." (defn cache-file [f output-dir] (let [ns-info (parse-ns f)] - (io/file (util/to-target-file output-dir f ns-info) ".cache.edn"))) + (io/file (util/to-target-file output-dir ns-info) ".cache.edn"))) (defn analyze-file ([f] (analyze-file f nil)) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 4e88de629..a40b2bb13 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1000,7 +1000,7 @@ output-files []] (if (seq cljs-files) (let [cljs-file (first cljs-files) - output-file (util/to-target-file target-dir cljs-file (ana/parse-ns cljs-file)) + output-file (util/to-target-file target-dir (ana/parse-ns cljs-file)) ns-info (compile-file cljs-file output-file opts)] (recur (rest cljs-files) (conj output-files (assoc ns-info :file-name (.getPath output-file))))) output-files))))) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 5c5142369..0e50efb4d 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -54,10 +54,8 @@ (apply str (interpose sep parts)))) (defn ^File to-target-file - [target-dir cljs-file ns-info] - (let [relative-path (string/split - (munge-path (str (:ns ns-info))) #"\.") - parents (butlast relative-path)] - (io/file - (io/file (to-path (cons target-dir parents))) + [target-dir ns-info] + (let [relative-path (string/split (munge-path (str (:ns ns-info))) #"\.") + parents (butlast relative-path)] + (io/file (io/file (to-path (cons target-dir parents))) (str (last relative-path) ".js")))) From da29d81a39eaabff634e497ae5bc22a46403be76 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Sep 2014 21:51:56 +0200 Subject: [PATCH 0159/4033] simplify PersistentVector -kv-reduce, no more mutating local arrays just use more loop bindings. Use the exact same logic for -reduce instead of delegating to ci-reduce. Significant performance boost when reducing vectors. --- src/cljs/cljs/core.cljs | 53 +++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index c9347888f..4984ad5fb 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4108,30 +4108,41 @@ reduces them without incurring seq initialization" IReduce (-reduce [v f] - (ci-reduce v f)) - (-reduce [v f start] - (ci-reduce v f start)) + (-reduce v f (f))) + (-reduce [v f init] + (loop [i 0 step 0 init init] + (if (< i cnt) + (let [arr (unchecked-array-for v i) + len (alength arr)] + (let [init (loop [j 0 init init] + (if (< j len) + (let [init (f init (aget arr j))] + (if (reduced? init) + init + (recur (inc j) init))) + init))] + (if (reduced? init) + @init + (recur (+ i len) len init)))) + init))) IKVReduce (-kv-reduce [v f init] - (let [step-init (array 0 init)] ; [step 0 init init] - (loop [i 0] - (if (< i cnt) - (let [arr (unchecked-array-for v i) - len (alength arr)] - (let [init (loop [j 0 init (aget step-init 1)] - (if (< j len) - (let [init (f init (+ j i) (aget arr j))] - (if (reduced? init) - init - (recur (inc j) init))) - (do (aset step-init 0 len) - (aset step-init 1 init) - init)))] - (if (reduced? init) - @init - (recur (+ i (aget step-init 0)))))) - (aget step-init 1))))) + (loop [i 0 step 0 init init] + (if (< i cnt) + (let [arr (unchecked-array-for v i) + len (alength arr)] + (let [init (loop [j 0 init init] + (if (< j len) + (let [init (f init (+ j i) (aget arr j))] + (if (reduced? init) + init + (recur (inc j) init))) + init))] + (if (reduced? init) + @init + (recur (+ i len) len init)))) + init))) IFn (-invoke [coll k] From 5af556fc4bd1a3fbbe5d8bf54acf1355b3e9f49f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Sep 2014 22:12:45 +0200 Subject: [PATCH 0160/4033] add iterable protocol so collections have hookable way to return high performance iterators --- src/cljs/cljs/core.cljs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 4984ad5fb..12b6da12f 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -391,6 +391,9 @@ (defprotocol ISwap (-swap! [o f] [o f a] [o f a b] [o f a b xs])) +(defprotocol IIterable + (-iterator [coll])) + ;; Printing support (deftype StringBufferWriter [sb] @@ -605,6 +608,9 @@ (declare array-seq prim-seq IndexedSeq) +(defn iterable? [x] + (satisfies? IIterable x)) + (defn clone [value] (-clone value)) @@ -2942,7 +2948,9 @@ reduces them without incurring seq initialization" (nil? coll) (nil-iter) (string? coll) (string-iter coll) (array? coll) (array-iter coll) - :else (seq-iter coll))) + (iterable? coll) (-iterator coll) + (seqable? coll) (seq-iter coll) + :else (throw (js/Error. (str "Cannot create iterator from " coll))))) (declare LazyTransformer) From 86bb5aa148b42ef811b0d66a693725318a780647 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Sep 2014 13:26:20 +0200 Subject: [PATCH 0161/4033] code style - collapse nested lets in last commit --- src/cljs/cljs/core.cljs | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 12b6da12f..8cb65ad51 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4120,36 +4120,36 @@ reduces them without incurring seq initialization" (-reduce [v f init] (loop [i 0 step 0 init init] (if (< i cnt) - (let [arr (unchecked-array-for v i) - len (alength arr)] - (let [init (loop [j 0 init init] - (if (< j len) - (let [init (f init (aget arr j))] - (if (reduced? init) - init - (recur (inc j) init))) - init))] - (if (reduced? init) - @init - (recur (+ i len) len init)))) + (let [arr (unchecked-array-for v i) + len (alength arr) + init (loop [j 0 init init] + (if (< j len) + (let [init (f init (aget arr j))] + (if (reduced? init) + init + (recur (inc j) init))) + init))] + (if (reduced? init) + @init + (recur (+ i len) len init))) init))) IKVReduce (-kv-reduce [v f init] (loop [i 0 step 0 init init] (if (< i cnt) - (let [arr (unchecked-array-for v i) - len (alength arr)] - (let [init (loop [j 0 init init] - (if (< j len) - (let [init (f init (+ j i) (aget arr j))] - (if (reduced? init) - init - (recur (inc j) init))) - init))] - (if (reduced? init) - @init - (recur (+ i len) len init)))) + (let [arr (unchecked-array-for v i) + len (alength arr) + init (loop [j 0 init init] + (if (< j len) + (let [init (f init (+ j i) (aget arr j))] + (if (reduced? init) + init + (recur (inc j) init))) + init))] + (if (reduced? init) + @init + (recur (+ i len) len init))) init))) IFn From 8f0f443bd106d2958b95dbe56dd25f43f0c56f92 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Sep 2014 13:35:25 +0200 Subject: [PATCH 0162/4033] remove unneeded loop binding `step`, two arity version of PersistentVector -reduce delegates to ci-reduce again - doing correct version of the loop with offset 1 seems to subtle at the momment. --- src/cljs/cljs/core.cljs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 8cb65ad51..4f0c15e38 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4116,9 +4116,9 @@ reduces them without incurring seq initialization" IReduce (-reduce [v f] - (-reduce v f (f))) + (ci-reduce v f)) (-reduce [v f init] - (loop [i 0 step 0 init init] + (loop [i 0 init init] (if (< i cnt) (let [arr (unchecked-array-for v i) len (alength arr) @@ -4131,12 +4131,12 @@ reduces them without incurring seq initialization" init))] (if (reduced? init) @init - (recur (+ i len) len init))) + (recur (+ i len) init))) init))) IKVReduce (-kv-reduce [v f init] - (loop [i 0 step 0 init init] + (loop [i 0 init init] (if (< i cnt) (let [arr (unchecked-array-for v i) len (alength arr) @@ -4149,7 +4149,7 @@ reduces them without incurring seq initialization" init))] (if (reduced? init) @init - (recur (+ i len) len init))) + (recur (+ i len) init))) init))) IFn From 8b82ad8dffcc2bbdb130d7d5b2ad1c3564459d4f Mon Sep 17 00:00:00 2001 From: Zubair Quraishi Date: Thu, 18 Sep 2014 19:25:12 +0200 Subject: [PATCH 0163/4033] CLJS-854: Fix for IE8 cljs.reader to read integers correctly This change is made because IE8 always returns 0 when cljs.reader reads an integer. This is caused because the matching pattern in most browsers returns nil for unmatched patterns but IE8 returns "" instead of nil --- src/cljs/cljs/reader.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index d122b87e8..0808d7921 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -112,7 +112,8 @@ nil if the end of stream has been reached") (defn- match-int [s] (let [groups (re-matches* int-pattern s) - zero (aget groups 2)] + ie8-fix (aget groups 2) + zero (if (= ie8-fix "") nil ie8-fix)] (if-not (nil? zero) 0 (let [a (cond From 65267ed0235d4cf985a2bcd6a70fd6d3d466a07d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Sep 2014 09:46:19 +0200 Subject: [PATCH 0164/4033] fix `cljs.analyzer/confirm-var-exists` so that occurences `cljs.core/Foo.bar` do not emit warnings - we now correctly check for `cljs.core/Foo` --- src/clj/cljs/analyzer.clj | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 1919bfb91..08f2ad026 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -293,12 +293,17 @@ (contains? '#{goog goog.object goog.string goog.array Math} prefix)) (defn confirm-var-exists [env prefix suffix] - (when (and (not (implicit-import? env prefix suffix)) - (not (and (not (get-in @env/*compiler* [::namespaces prefix])) - (or (get (:requires (:ns env)) prefix) - (get (:imports (:ns env)) prefix)))) - (not (get-in @env/*compiler* [::namespaces prefix :defs suffix]))) - (warning :undeclared-var env {:prefix prefix :suffix suffix}))) + (let [sufstr (str suffix) + suffix (symbol + (if (re-find #"\." sufstr) + (first (string/split sufstr #"\.")) + suffix))] + (when (and (not (implicit-import? env prefix suffix)) + (not (and (not (get-in @env/*compiler* [::namespaces prefix])) + (or (get (:requires (:ns env)) prefix) + (get (:imports (:ns env)) prefix)))) + (not (get-in @env/*compiler* [::namespaces prefix :defs suffix]))) + (warning :undeclared-var env {:prefix prefix :suffix suffix})))) (defn resolve-ns-alias [env name] (let [sym (symbol name)] From 123303bb39de1825d96626142a4436eb20d34750 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Sep 2014 09:54:18 +0200 Subject: [PATCH 0165/4033] update readme and changes for 2356 --- README.md | 6 +++--- changes.md | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d58c17545..e3219a71d 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2342 +Latest stable release: 0.0-2356 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2342"] +[org.clojure/clojurescript "0.0-2356"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2342 org.clojure clojurescript - 0.0-2342 + 0.0-2356 ``` diff --git a/changes.md b/changes.md index c23bcecfa..2e1dc269a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,9 @@ +## 0.0-2356 + +### Fixes +* fix var analysis so that some.ns/foo.bar is handled correctly +* CLJS-854: cljs.reader could not read numbers under IE8 + ## 0.0-2342 ### Changes From 4a90386788ae21dad1ec70ec91e0d929b5500812 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Sep 2014 11:06:35 +0200 Subject: [PATCH 0166/4033] prefix ES6 iterator types and helper fns --- src/cljs/cljs/core.cljs | 48 +++++++++++++++++------------------ test/cljs/cljs/core_test.cljs | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 4f0c15e38..099b683bd 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4858,7 +4858,7 @@ reduces them without incurring seq initialization" (set! (.-fromObject ObjMap) (fn [ks obj] (ObjMap. nil ks obj 0 nil))) ;; EXPERIMENTAL: subject to change -(deftype Iterator [^:mutable s] +(deftype ES6Iterator [^:mutable s] Object (next [_] (if-not (nil? s) @@ -4867,11 +4867,11 @@ reduces them without incurring seq initialization" #js {:value x :done false}) #js {:value nil :done true}))) -(defn iterator [coll] - (Iterator. (seq coll))) +(defn es6-iterator [coll] + (ES6Iterator. (seq coll))) ;; EXPERIMENTAL: subject to change -(deftype EntriesIterator [^:mutable s] +(deftype ES6EntriesIterator [^:mutable s] Object (next [_] (if-not (nil? s) @@ -4880,11 +4880,11 @@ reduces them without incurring seq initialization" #js {:value #js [k v] :done false}) #js {:value nil :done true}))) -(defn entries-iterator [coll] - (EntriesIterator. (seq coll))) +(defn es6-entries-iterator [coll] + (ES6EntriesIterator. (seq coll))) ;; EXPERIMENTAL: subject to change -(deftype SetEntriesIterator [^:mutable s] +(deftype ES6SetEntriesIterator [^:mutable s] Object (next [_] (if-not (nil? s) @@ -4893,8 +4893,8 @@ reduces them without incurring seq initialization" #js {:value #js [x x] :done false}) #js {:value nil :done true}))) -(defn set-entries-iterator [coll] - (SetEntriesIterator. (seq coll))) +(defn es6-set-entries-iterator [coll] + (ES6SetEntriesIterator. (seq coll))) ;;; PersistentArrayMap @@ -5041,11 +5041,11 @@ reduces them without incurring seq initialization" ;; EXPERIMENTAL: subject to change (keys [coll] - (iterator (keys coll))) + (es6-iterator (keys coll))) (entries [coll] - (entries-iterator (seq coll))) + (es6-entries-iterator (seq coll))) (values [coll] - (iterator (vals coll))) + (es6-iterator (vals coll))) (has [coll k] (contains? coll k)) (get [coll k] @@ -5889,11 +5889,11 @@ reduces them without incurring seq initialization" ;; EXPERIMENTAL: subject to change (keys [coll] - (iterator (keys coll))) + (es6-iterator (keys coll))) (entries [coll] - (entries-iterator (seq coll))) + (es6-entries-iterator (seq coll))) (values [coll] - (iterator (vals coll))) + (es6-iterator (vals coll))) (has [coll k] (contains? coll k)) (get [coll k] @@ -6645,11 +6645,11 @@ reduces them without incurring seq initialization" ;; EXPERIMENTAL: subject to change (keys [coll] - (iterator (keys coll))) + (es6-iterator (keys coll))) (entries [coll] - (entries-iterator (seq coll))) + (es6-entries-iterator (seq coll))) (values [coll] - (iterator (vals coll))) + (es6-iterator (vals coll))) (has [coll k] (contains? coll k)) (get [coll k] @@ -7014,11 +7014,11 @@ reduces them without incurring seq initialization" ;; EXPERIMENTAL: subject to change (keys [coll] - (iterator (seq coll))) + (es6-iterator (seq coll))) (entries [coll] - (set-entries-iterator (seq coll))) + (es6-set-entries-iterator (seq coll))) (values [coll] - (iterator (seq coll))) + (es6-iterator (seq coll))) (has [coll k] (contains? coll k)) (forEach [coll f] @@ -7144,11 +7144,11 @@ reduces them without incurring seq initialization" ;; EXPERIMENTAL: subject to change (keys [coll] - (iterator (seq coll))) + (es6-iterator (seq coll))) (entries [coll] - (set-entries-iterator (seq coll))) + (es6-set-entries-iterator (seq coll))) (values [coll] - (iterator (seq coll))) + (es6-iterator (seq coll))) (has [coll k] (contains? coll k)) (forEach [coll f] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index c7c593a20..fc9e35954 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2257,7 +2257,7 @@ ;; basic iteration - (def iter (iterator [1 2 3])) + (def iter (es6-iterator [1 2 3])) (assert (= (.-value (.next iter)) 1)) (assert (= (.-value (.next iter)) 2)) From 46fe39410a4f212d34b11ef480e5ed4ee466c176 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Sep 2014 11:23:01 +0200 Subject: [PATCH 0167/4033] add high performance iterator RangeIterator for PersistentVector a la Clojure. Switch Persistent IEQuiv implementation to use it - up to 4X faster than old seq based equality. PersistentVector now implements IIterable so that transducers based code can benefit. --- benchmark/cljs/benchmark_runner.cljs | 7 +++++ src/cljs/cljs/core.cljs | 41 ++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index eafb65d4d..abba2ccbf 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -104,6 +104,13 @@ (persistent! v) (recur (inc i) (conj! v i)))))) +(println ";;; vector equality") +(simple-benchmark + [a (into [] (range 1000000)) + b (into [] (range 1000000))] + (= a b) 1) +(println) + (println ";;; reduce lazy-seqs, vectors, ranges") (simple-benchmark [coll (take 100000 (iterate inc 0))] (reduce + 0 coll) 1) (simple-benchmark [coll (range 1000000)] (reduce + 0 coll) 1) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 099b683bd..97ef1e776 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4000,6 +4000,25 @@ reduces them without incurring seq initialization" (pv-aset ret subidx nil) ret)))) +(deftype RangedIterator [^:mutable i ^:mutable base ^:mutable arr v start end] + Object + (hasNext [this] + (< i end)) + (next [this] + (when (== (- i base) 32) + (set! arr (unchecked-array-for v i)) + (set! base (+ base 32))) + (let [ret (aget arr (bit-and i 0x01f))] + (set! i (inc i)) + ret))) + +(defn ranged-iterator [v start end] + (let [i start] + (RangedIterator. i (- i (js-mod i 32)) + (when (< start (count v)) + (unchecked-array-for v i)) + v start end))) + (declare tv-editable-root tv-editable-tail TransientVector deref pr-sequential-writer pr-writer chunked-seq) @@ -4061,7 +4080,21 @@ reduces them without incurring seq initialization" ISequential IEquiv - (-equiv [coll other] (equiv-sequential coll other)) + (-equiv [coll other] + (if (instance? PersistentVector other) + (if (== cnt (count other)) + (let [me-iter (-iterator coll) + you-iter (-iterator other)] + (loop [] + (if (.hasNext me-iter) + (let [x (.next me-iter) + y (.next you-iter)] + (if (= x y) + (recur) + false)) + true))) + false) + (equiv-sequential coll other))) IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) @@ -4165,7 +4198,11 @@ reduces them without incurring seq initialization" IReversible (-rseq [coll] (if (pos? cnt) - (RSeq. coll (dec cnt) nil)))) + (RSeq. coll (dec cnt) nil))) + + IIterable + (-iterator [this] + (ranged-iterator this 0 cnt))) (set! (.-EMPTY-NODE PersistentVector) (VectorNode. nil (make-array 32))) From 6bdc31ed2464a4dd3bbe5cfac03621ae80cfa346 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 30 Sep 2014 10:39:34 +0200 Subject: [PATCH 0168/4033] optimize PersistentArrayMap IEQuiv implementation --- src/cljs/cljs/core.cljs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 97ef1e776..c73afcc8b 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -5117,7 +5117,22 @@ reduces them without incurring seq initialization" (-empty [coll] (-with-meta (.-EMPTY PersistentArrayMap) meta)) IEquiv - (-equiv [coll other] (equiv-map coll other)) + (-equiv [coll other] + (if (implements? IMap other) + (let [alen (alength arr) + ^not-native other other] + (if (== cnt (-count other)) + (loop [i 0] + (if (< i alen) + (let [v (-lookup other (aget arr i) lookup-sentinel)] + (if-not (identical? v lookup-sentinel) + (if (= (aget arr (inc i)) v) + (recur (+ i 2)) + false) + false)) + true)) + false)) + (equiv-map coll other))) IHash (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) From 180ae8c9b605d830ce823cd4da64a130ef1b1143 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 30 Sep 2014 10:52:44 +0200 Subject: [PATCH 0169/4033] add PersistentArrayMapIterator --- src/cljs/cljs/core.cljs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index c73afcc8b..50dfc3faf 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -5069,6 +5069,15 @@ reduces them without incurring seq initialization" (declare keys vals) +(deftype PersistentArrayMapIterator [arr ^:mutable i cnt] + Object + (hasNext [_] + (< i cnt)) + (next [_] + (let [ret [(aget arr i) (aget arr (inc i))]] + (set! i (+ i 2)) + ret))) + (deftype PersistentArrayMap [meta cnt arr ^:mutable __hash] Object (toString [coll] @@ -5137,6 +5146,10 @@ reduces them without incurring seq initialization" IHash (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) + IIterable + (-iterator [this] + (PersistentArrayMapIterator. arr 0 (* cnt 2))) + ISeqable (-seq [coll] (persistent-array-map-seq arr 0 nil)) From 94391f5f2d3bd3c7c1629e9365e940db210a3502 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 30 Sep 2014 11:09:14 +0200 Subject: [PATCH 0170/4033] add RangeIterator --- src/cljs/cljs/core.cljs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 50dfc3faf..b141040dd 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7499,6 +7499,17 @@ reduces them without incurring seq initialization" (take-while (mk-bound-fn sc start-test start-key) (if ((mk-bound-fn sc end-test end-key) e) s (next s)))))) +(deftype RangeIterator [^:mutable i end step] + Object + (hasNext [_] + (if (pos? step) + (< i end) + (> i end))) + (next [_] + (let [ret i] + (set! i (+ i step)) + ret))) + (deftype Range [meta start end step ^:mutable __hash] Object (toString [coll] @@ -7531,6 +7542,10 @@ reduces them without incurring seq initialization" (Range. meta (+ start step) end step nil) ())) + IIterable + (-iterator [_] + (RangeIterator. start end step)) + INext (-next [rng] (if (pos? step) From 301fc7e1ed159bf0b92e172a5034cb1f4a185b1e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 30 Sep 2014 11:21:01 +0200 Subject: [PATCH 0171/4033] optimize Range IReduce implementation --- src/cljs/cljs/core.cljs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index b141040dd..2f5e57fc6 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7589,7 +7589,14 @@ reduces them without incurring seq initialization" IReduce (-reduce [rng f] (ci-reduce rng f)) - (-reduce [rng f s] (ci-reduce rng f s))) + (-reduce [rng f init] + (loop [i start ret init] + (if (if (pos? step) (< i end) (> i end)) + (let [ret (f ret i)] + (if (reduced? ret) + @ret + (recur (+ i step) ret))) + ret)))) (defn range "Returns a lazy seq of nums from start (inclusive) to end From ede93a98bebaee6da375637e731a19230311aa5c Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Oct 2014 18:29:07 -0400 Subject: [PATCH 0172/4033] add IndexedSeqIterator --- src/cljs/cljs/core.cljs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 2f5e57fc6..f705002c2 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -862,6 +862,15 @@ reduces them without incurring seq initialization" "Returns true if coll implements nth in constant time" [x] (satisfies? IIndexed x)) +(deftype IndexedSeqIterator [arr ^:mutable i] + Object + (hasNext [_] + (< i (alength arr))) + (next [_] + (let [ret (aget arr i)] + (set! i (inc i)) + ret))) + (deftype IndexedSeq [arr i] Object (toString [coll] @@ -905,6 +914,10 @@ reduces them without incurring seq initialization" IEquiv (-equiv [coll other] (equiv-sequential coll other)) + IIterable + (-iterator [coll] + (IndexedSeqIterator. arr i)) + ICollection (-conj [coll o] (cons o coll)) From ba9664060fb65e6b97ff887a04c9d508b4f91eef Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Oct 2014 17:53:18 -0400 Subject: [PATCH 0173/4033] make protocol errors more informative --- src/clj/cljs/analyzer.clj | 6 +++++- src/clj/cljs/core.clj | 8 +++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 08f2ad026..99752c988 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -114,7 +114,11 @@ (defmethod error-message :protocol-invalid-method [warning-type info] - (str "Bad method signature in protocol implementation " (:protocol info) " " (:fname info))) + (if (:no-such-method info) + (str "Bad method signature in protocol implementation, " + (:protocol info) " does not declare method called " (:fname info)) + (str "Bad method signature in protocol implementation, " + (:protocol info) " " (:fname info) " does not declare arity " (:invalid-arity info)))) (defmethod error-message :protocol-duped-method [warning-type info] diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index e84a8f045..dfd964e4b 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -802,15 +802,17 @@ [fname sigs] (if (core/vector? (second method)) [(first method) [(second method)]] [(first method) (map first (rest method))]) - decmeths (core/get minfo fname)] + decmeths (core/get minfo fname ::not-found)] + (when (= decmeths ::not-found) + (ana/warning :protocol-invalid-method env {:protocol p :fname fname :no-such-method true})) (loop [sigs sigs seen #{}] (when (seq sigs) (let [sig (first sigs) c (count sig)] (when (contains? seen c) (ana/warning :protocol-duped-method env {:protocol p :fname fname})) - (when-not (some #{c} (map count decmeths)) - (ana/warning :protocol-invalid-method env {:protocol p :fname fname})) + (when (core/and (not= decmeths ::not-found) (not (some #{c} (map count decmeths)))) + (ana/warning :protocol-invalid-method env {:protocol p :fname fname :invalid-arity c})) (recur (next sigs) (conj seen c)))))))) (defn validate-impls [env impls] From 4fba4f13e1603cc12215f0d66fd62f5fd043f528 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Oct 2014 19:10:43 -0400 Subject: [PATCH 0174/4033] local names, iteration => eduction same as Clojure commit 5a2042cc8d7331b1d1979f5c946f6c480d6f5be8 --- src/cljs/cljs/core.cljs | 124 ++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index f705002c2..6fa45f6a1 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -3274,15 +3274,15 @@ reduces them without incurring seq initialization" this means false return values will be included. f must be free of side-effects. Returns a transducer when no collection is provided." ([f] - (fn [f1] + (fn [rf] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (let [v (f input)] (if (nil? v) result - (f1 result v))))))) + (rf result v))))))) ([f coll] (lazy-seq (when-let [s (seq coll)] @@ -3418,17 +3418,17 @@ reduces them without incurring seq initialization" side-effects. Returns a stateful transducer when no collection is provided." ([f] - (fn [f1] + (fn [rf] (let [ia (atom -1)] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (let [i (swap! ia inc) v (f i input)] (if (nil? v) result - (f1 result v)))))))) + (rf result v)))))))) ([f coll] (letfn [(keepi [idx coll] (lazy-seq @@ -3534,14 +3534,14 @@ reduces them without incurring seq initialization" f should accept number-of-colls arguments. Returns a transducer when no collection is provided." ([f] - (fn [f1] + (fn [rf] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] - (f1 result (f input))) + (rf result (f input))) ([result input & inputs] - (f1 result (apply f input inputs)))))) + (rf result (apply f input inputs)))))) ([f coll] (lazy-seq (when-let [s (seq coll)] @@ -3578,16 +3578,16 @@ reduces them without incurring seq initialization" there are fewer than n. Returns a stateful transducer when no collection is provided." ([n] - (fn [f1] + (fn [rf] (let [na (atom n)] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (let [n @na nn (swap! na dec) result (if (pos? n) - (f1 result input) + (rf result input) result)] (if (not (pos? nn)) (reduced result) @@ -3602,17 +3602,17 @@ reduces them without incurring seq initialization" "Returns a lazy sequence of all but the first n items in coll. Returns a stateful transducer when no collection is provided." ([n] - (fn [f1] + (fn [rf] (let [na (atom n)] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (let [n @na] (swap! na dec) (if (pos? n) result - (f1 result input)))))))) + (rf result input)))))))) ([n coll] (let [step (fn [n coll] (let [s (seq coll)] @@ -3640,18 +3640,18 @@ reduces them without incurring seq initialization" first item for which (pred item) returns logical false. Returns a stateful transducer when no collection is provided." ([pred] - (fn [f1] + (fn [rf] (let [da (atom true)] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (let [drop? @da] (if (and drop? (pred input)) result (do (reset! da nil) - (f1 result input))))))))) + (rf result input))))))))) ([pred coll] (let [step (fn [pred coll] (let [s (seq coll)] @@ -3741,13 +3741,13 @@ reduces them without incurring seq initialization" (pred item) returns true. pred must be free of side-effects. Returns a transducer when no collection is provided." ([pred] - (fn [f1] + (fn [rf] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (if (pred input) - (f1 result input) + (rf result input) result))))) ([pred coll] (lazy-seq @@ -7431,24 +7431,24 @@ reduces them without incurring seq initialization" partitions with fewer than n items at the end. Returns a stateful transducer when no collection is provided." ([n] - (fn [f1] + (fn [rf] (let [a (array-list)] (fn - ([] (f1)) + ([] (rf)) ([result] (let [result (if (.isEmpty a) result (let [v (vec (.toArray a))] ;;clear first! (.clear a) - (f1 result v)))] - (f1 result))) + (rf result v)))] + (rf result))) ([result input] (.add a input) (if (== n (.size a)) (let [v (vec (.toArray a))] (.clear a) - (f1 result v)) + (rf result v)) result)))))) ([n coll] (partition-all n n coll)) @@ -7462,13 +7462,13 @@ reduces them without incurring seq initialization" (pred item) returns true. pred must be free of side-effects. Returns a transducer when no collection is provided." ([pred] - (fn [f1] + (fn [rf] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (if (pred input) - (f1 result input) + (rf result input) (reduced result)))))) ([pred coll] (lazy-seq @@ -7624,15 +7624,15 @@ reduces them without incurring seq initialization" "Returns a lazy seq of every nth item in coll. Returns a stateful transducer when no collection is provided." ([n] - (fn [f1] + (fn [rf] (let [ia (atom -1)] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (let [i (swap! ia inc)] (if (zero? (rem i n)) - (f1 result input) + (rf result input) result))))))) ([n coll] (lazy-seq @@ -7649,19 +7649,19 @@ reduces them without incurring seq initialization" new value. Returns a lazy seq of partitions. Returns a stateful transducer when no collection is provided." ([f] - (fn [f1] + (fn [rf] (let [a (array-list) pa (atom ::none)] (fn - ([] (f1)) + ([] (rf)) ([result] (let [result (if (.isEmpty a) result (let [v (vec (.toArray a))] ;;clear first! (.clear a) - (f1 result v)))] - (f1 result))) + (rf result v)))] + (rf result))) ([result input] (let [pval @pa val (f input)] @@ -7673,7 +7673,7 @@ reduces them without incurring seq initialization" result) (let [v (vec (.toArray a))] (.clear a) - (let [ret (f1 result v)] + (let [ret (rf result v)] (when-not (reduced? ret) (.add a input)) ret))))))))) @@ -8246,8 +8246,8 @@ reduces them without incurring seq initialization" (-realized? d)) (defn- preserving-reduced - [f1] - #(let [ret (f1 %1 %2)] + [rf] + #(let [ret (rf %1 %2)] (if (reduced? ret) (reduced ret) ret))) @@ -8256,11 +8256,11 @@ reduces them without incurring seq initialization" "A transducer which concatenates the contents of each input, which must be a collection, into the reduction." {:added "1.7"} - [f1] - (let [rf1 (preserving-reduced f1)] + [rf] + (let [rf1 (preserving-reduced rf)] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (reduce rf1 result input))))) @@ -8268,17 +8268,17 @@ reduces them without incurring seq initialization" "Returns a lazy sequence removing consecutive duplicates in coll. Returns a transducer when no collection is provided." ([] - (fn [f1] + (fn [rf] (let [pa (atom ::none)] (fn - ([] (f1)) - ([result] (f1 result)) + ([] (rf)) + ([result] (rf result)) ([result input] (let [prior @pa] (reset! pa input) (if (= prior input) result - (f1 result input)))))))) + (rf result input)))))))) ([coll] (sequence (dedupe) coll))) (defn random-sample @@ -8289,7 +8289,7 @@ reduces them without incurring seq initialization" ([prob coll] (filter (fn [_] (< (rand) prob)) coll))) -(deftype Iteration [xform coll] +(deftype Eduction [xform coll] ISequential ISeqable @@ -8302,12 +8302,12 @@ reduces them without incurring seq initialization" (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))) -(defn iteration - "Returns an iterable/seqable/reducible sequence of applications of - the transducer to the items in coll. Note that these applications +(defn eduction + "Returns a reducible/iterable/seqable application of + the transducer to the items in coll. Note that these applications will be performed every time iterator/seq/reduce is called." [xform coll] - (Iteration. xform coll)) + (Eduction. xform coll)) (defn run! "Runs the supplied procedure (via reduce), for purposes of side From 9fd6bf5bd55421c3d5becacc5230ed661d6fb3c3 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Sun, 5 Oct 2014 10:59:09 -0700 Subject: [PATCH 0175/4033] CLJS-869: When preamble is not found in source directory, compiler does not report it --- src/clj/cljs/analyzer.clj | 9 +++++++-- src/clj/cljs/closure.clj | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 99752c988..8758af999 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -34,7 +34,8 @@ (def -cljs-macros-loaded (atom false)) (def ^:dynamic *cljs-warnings* - {:unprovided true + {:preamble-missing true + :unprovided true :undeclared-var false :undeclared-ns false :undeclared-ns-form true @@ -60,9 +61,13 @@ (defmulti error-message (fn [warning-type & _] warning-type)) +(defmethod error-message :preamble-missing + [warning-type info] + (str "Preamble resource file not found: " (string/join " " (:missing info)))) + (defmethod error-message :unprovided [warning-type info] - (str "Required namespace not provided for " (clojure.string/join " " (:unprovided info)))) + (str "Required namespace not provided for " (string/join " " (:unprovided info)))) (defmethod error-message :undeclared-var [warning-type info] diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index b3823dcc4..1e8ba304b 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -523,7 +523,10 @@ should contain the source for the given namespace name." inputs))))) (defn preamble-from-paths [paths] - (str (apply str (map #(slurp (io/resource %)) paths)) "\n")) + (when-let [missing (seq (remove io/resource paths))] + (ana/warning :preamble-missing @env/*compiler* {:missing (sort missing)})) + (let [resources (remove nil? (map io/resource paths))] + (str (string/join (map slurp resources)) "\n"))) (defn make-preamble [{:keys [target preamble hashbang]}] (str (when (and (= :nodejs target) (not (false? hashbang))) From a8b338762fd7a22cf69d6d9f5ead63a750d98e15 Mon Sep 17 00:00:00 2001 From: Peter Taoussanis Date: Fri, 3 Oct 2014 13:31:38 +0700 Subject: [PATCH 0176/4033] CLJS-866: Faulty ns macro desugaring Fixes: * `:refer-macros` sugar producing an invalid expansion. * `:include-macros` sugar behaving unpredictably. --- src/clj/cljs/analyzer.clj | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 8758af999..7ef7f06a3 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1140,18 +1140,20 @@ (map (fn [[k & specs]] [k (into [] specs)])) (into {})) sugar-keys #{:include-macros :refer-macros} + remove-from-spec + (fn [pred spec] + (if-not (and (sequential? spec) (some pred spec)) + spec + (let [[l r] (split-with (complement pred) spec)] + (recur pred (concat l (drop 2 r)))))) to-macro-specs (fn [specs] (->> specs (filter #(and (sequential? %) (some sugar-keys %))) - (map #(->> % (remove #{:include-macros true}) + (map #(->> % (remove-from-spec #{:include-macros}) + (remove-from-spec #{:refer}) (map (fn [x] (if (= x :refer-macros) :refer x))))))) - remove-sugar - (fn [spec] - (if (and (sequential? spec) (some sugar-keys spec)) - (let [[l & r] (split-with #(not (contains? sugar-keys %)) spec)] - (concat l (drop 2 r))) - spec))] + remove-sugar (partial remove-from-spec sugar-keys)] (if-let [require-specs (seq (to-macro-specs require))] (map (fn [[k v]] (cons k (map remove-sugar v))) (update-in indexed [:require-macros] (fnil into []) require-specs)) From 3b98d20f0f89d49ce4edf4a8d845c7497bc051fc Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Oct 2014 16:31:23 -0400 Subject: [PATCH 0177/4033] fix iteration test, now eduction --- test/cljs/cljs/core_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index fc9e35954..cb98c5ac8 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2369,7 +2369,7 @@ (range 10))) (assert (= (sequence (mapcat reverse) [[3 2 1 0] [6 5 4] [9 8 7]]) (range 10))) - (assert (= (seq (iteration (map inc) [1 2 3])) '(2 3 4))) + (assert (= (seq (eduction (map inc) [1 2 3])) '(2 3 4))) (assert (= (sequence (partition-by #{:split}) [1 2 3 :split 4 5 6]) '([1 2 3] [:split] [4 5 6]))) (assert (= (sequence (partition-all 3) '(1 2 3 4 5)) From 532a04cdfb4fcdf9eedc37d4d423fef2f170860f Mon Sep 17 00:00:00 2001 From: Joel Holdbrooks Date: Fri, 19 Sep 2014 14:06:40 -0700 Subject: [PATCH 0178/4033] CLJS-862: fix inconsistent re-pattern --- src/cljs/cljs/core.cljs | 6 ++++-- test/cljs/cljs/core_test.cljs | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 6fa45f6a1..76596c457 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7814,8 +7814,10 @@ reduces them without incurring seq initialization" (defn re-pattern "Returns an instance of RegExp which has compiled the provided string." [s] - (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" s)] - (js/RegExp. pattern flags))) + (if (instance? js/RegExp s) + s + (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" s)] + (js/RegExp. pattern flags)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Printing ;;;;;;;;;;;;;;;; diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index cb98c5ac8..4dd747ebe 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -617,6 +617,8 @@ (assert (= (distinct [#{} #{}]) [#{}])) ;;regexps + (let [r1 #"foo", r2 (re-pattern r1)] + (assert (= r1 r2))) (assert (= (str (re-pattern "f(.)o")) (str (js* "/f(.)o/")))) (assert (= (re-find (re-pattern "foo") "foo bar foo baz foo zot") "foo")) (assert (= (re-find (re-pattern "f(.)o") "foo bar foo baz foo zot") ["foo" "o"])) From 9df358dbbd617a95ae3a842db46ae62447cb3b66 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Oct 2014 19:04:19 -0400 Subject: [PATCH 0179/4033] change cljs.compiler/with-core-cljs so it may take no arguments or just options --- src/clj/cljs/compiler.clj | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index a40b2bb13..3be5dddee 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -860,11 +860,13 @@ (defn with-core-cljs "Ensure that core.cljs has been loaded." - [opts body] - (do - (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) - (ana/analyze-file "cljs/core.cljs" opts)) - (body))) + ([] (with-core-cljs nil)) + ([opts] (with-core-cljs opts (fn []))) + ([opts body] + (do + (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) + (ana/analyze-file "cljs/core.cljs" opts)) + (body)))) (defn url-path [^File f] (.getPath (.toURL (.toURI f)))) From fad764397119d5f71f81332a25ab8f40452b0622 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Oct 2014 08:04:45 -0400 Subject: [PATCH 0180/4033] 2371 --- README.md | 6 +++--- changes.md | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e3219a71d..299346f7d 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2356 +Latest stable release: 0.0-2371 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2356"] +[org.clojure/clojurescript "0.0-2371"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2356 org.clojure clojurescript - 0.0-2356 + 0.0-2371 ``` diff --git a/changes.md b/changes.md index 2e1dc269a..aa443679a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,10 @@ +## 0.0-2371 + +### Fixes +* CLJS-862: fix inconsistent re-pattern +* CLJS-866: Faulty ns macro desugaring +* CLJS-869: When preamble is not found in source directory, compiler does not report it + ## 0.0-2356 ### Fixes From 2fa939bb3bb8c90ac0567db2b5dd2ad87dc1f3ae Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Oct 2014 08:26:37 -0400 Subject: [PATCH 0181/4033] add :anonymous metadata to reify anonymous type symbol --- src/clj/cljs/core.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index dfd964e4b..1ef5837d9 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -625,12 +625,12 @@ 'js/Function "function"}) (defmacro reify [& impls] - (let [t (gensym "t") + (let [t (with-meta (gensym "t") {:anonymous true}) meta-sym (gensym "meta") this-sym (gensym "_") - locals (keys (:locals &env)) - ns (-> &env :ns :name) - munge cljs.compiler/munge] + locals (keys (:locals &env)) + ns (-> &env :ns :name) + munge cljs.compiler/munge] `(do (when-not (exists? ~(symbol (core/str ns) (core/str t))) (deftype ~t [~@locals ~meta-sym] From 5f0aa9618974a6c879bb6cb594072b5576da2f50 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Oct 2014 08:41:40 -0400 Subject: [PATCH 0182/4033] annotate generated defrecord factory fns with :factory metadata, document :positional or :map --- src/clj/cljs/core.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 1ef5837d9..100c73d2a 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -894,7 +894,8 @@ (defn- build-positional-factory [rsym rname fields] - (let [fn-name (with-meta (symbol (core/str '-> rsym)) (meta rsym))] + (let [fn-name (with-meta (symbol (core/str '-> rsym)) + (assoc (meta rsym) :factory :positional))] `(defn ~fn-name [~@fields] (new ~rname ~@fields)))) @@ -1003,7 +1004,8 @@ (extend-type ~tagname ~@(dt->et tagname impls fields true)))))) (defn- build-map-factory [rsym rname fields] - (let [fn-name (symbol (core/str 'map-> rsym)) + (let [fn-name (with-meta (symbol (core/str 'map-> rsym)) + (assoc (meta rsym) :factory :map)) ms (gensym) ks (map keyword fields) getters (map (fn [k] `(~k ~ms)) ks)] From fee1c93d355a547c9964f51949c3294fceabb6a8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Oct 2014 19:33:05 -0400 Subject: [PATCH 0183/4033] reduced handling fixes, same as https://github.com/clojure/clojure/commit/eccff113e7d68411d60f7204711ab71027dc5356 --- src/cljs/cljs/core.cljs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 76596c457..a2af0b235 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -782,6 +782,16 @@ [r] (instance? Reduced r)) +(defn ensure-reduced + "If x is already reduced?, returns it, else returns (reduced x)" + [x] + (if (reduced? x) x (reduced x))) + +(defn unreduced + "If x is reduced?, returns (deref x), else returns x" + [x] + (if (reduced? x) (deref x) x)) + ;; generic to all refs ;; (but currently hard-coded to atom!) (defn deref @@ -3590,7 +3600,7 @@ reduces them without incurring seq initialization" (rf result input) result)] (if (not (pos? nn)) - (reduced result) + (ensure-reduced result) result))))))) ([n coll] (lazy-seq @@ -7441,7 +7451,7 @@ reduces them without incurring seq initialization" (let [v (vec (.toArray a))] ;;clear first! (.clear a) - (rf result v)))] + (unreduced (rf result v))))] (rf result))) ([result input] (.add a input) @@ -7660,7 +7670,7 @@ reduces them without incurring seq initialization" (let [v (vec (.toArray a))] ;;clear first! (.clear a) - (rf result v)))] + (unreduced (rf result v))))] (rf result))) ([result input] (let [pval @pa From 9a7f9e8d28a5c57778c821c3816b8dd668328110 Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Wed, 15 Oct 2014 15:57:16 +0200 Subject: [PATCH 0184/4033] CLJS-857: change deftype*/defrecord* special forms to include their inline methods decls --- src/clj/cljs/analyzer.clj | 61 +++++++++++++++++---------------------- src/clj/cljs/compiler.clj | 10 ++++--- src/clj/cljs/core.clj | 31 ++++++++------------ 3 files changed, 45 insertions(+), 57 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 7ef7f06a3..097ef60d4 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -736,22 +736,8 @@ {:ret-tag tag}))) locals (if (and locals name) (assoc locals name name-var) locals) type (-> form meta ::type) - fields (-> form meta ::fields) protocol-impl (-> form meta :protocol-impl) protocol-inline (-> form meta :protocol-inline) - locals (reduce (fn [m fld] - (assoc m fld - {:name fld - :line (get-line fld env) - :column (get-col fld env) - :field true - :mutable (-> fld meta :mutable) - :unsynchronized-mutable (-> fld meta :unsynchronized-mutable) - :volatile-mutable (-> fld meta :volatile-mutable) - :tag (-> fld meta :tag) - :shadow (m fld)})) - locals fields) - menv (if (> (count meths) 1) (assoc env :context :expr) env) menv (merge menv {:protocol-impl protocol-impl @@ -1210,9 +1196,23 @@ {:env env :op :ns :form form :name name :doc docstring :uses uses :requires requires :imports imports :use-macros use-macros :require-macros require-macros :excludes excludes})) -(defmethod parse 'deftype* - [_ env [_ tsym fields pmasks :as form] _ _] - (let [t (:name (resolve-var (dissoc env :locals) tsym))] +(defn parse-type + [op env [_ tsym fields pmasks body :as form]] + (let [t (:name (resolve-var (dissoc env :locals) tsym)) + locals (reduce (fn [m fld] + (assoc m fld + {:name fld + :line (get-line fld env) + :column (get-col fld env) + :field true + :mutable (-> fld meta :mutable) + :unsynchronized-mutable (-> fld meta :unsynchronized-mutable) + :volatile-mutable (-> fld meta :volatile-mutable) + :tag (-> fld meta :tag) + :shadow (m fld)})) + {} (if (= :defrecord* op) + (concat fields '[__meta __extmap ^:mutable __hash]) + fields))] (swap! env/*compiler* update-in [::namespaces (-> env :ns :name) :defs tsym] (fn [m] (let [m (assoc (or m {}) @@ -1220,25 +1220,18 @@ :type true :num-fields (count fields))] (merge m - (dissoc (meta tsym) :protocols) - {:protocols (-> tsym meta :protocols)} - (source-info tsym env))))) - {:env env :op :deftype* :form form :t t :fields fields :pmasks pmasks})) + (dissoc (meta tsym) :protocols) + {:protocols (-> tsym meta :protocols)} + (source-info tsym env))))) + {:op op :env env :form form :t t :fields fields :pmasks pmasks :body (analyze (assoc env :locals locals) body)})) + +(defmethod parse 'deftype* + [_ env form _ _] + (parse-type :deftype* env form)) (defmethod parse 'defrecord* - [_ env [_ tsym fields pmasks :as form] _ _] - (let [t (:name (resolve-var (dissoc env :locals) tsym))] - (swap! env/*compiler* update-in [::namespaces (-> env :ns :name) :defs tsym] - (fn [m] - (let [m (assoc (or m {}) - :name t - :type true - :num-fields (count fields))] - (merge m - (dissoc (meta tsym) :protocols) - {:protocols (-> tsym meta :protocols)} - (source-info tsym env))))) - {:env env :op :defrecord* :form form :t t :fields fields :pmasks pmasks})) + [_ env form _ _] + (parse-type :defrecord* env form) ) ;; dot accessor code diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 3be5dddee..2f83f382f 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -788,7 +788,7 @@ (emitln "goog.require('" (munge lib) "');"))) (defmethod emit* :deftype* - [{:keys [t fields pmasks]}] + [{:keys [t fields pmasks body]}] (let [fields (map munge fields)] (emitln "") (emitln "/**") @@ -799,10 +799,11 @@ (emitln "this." fld " = " fld ";")) (doseq [[pno pmask] pmasks] (emitln "this.cljs$lang$protocol_mask$partition" pno "$ = " pmask ";")) - (emitln "})"))) + (emitln "})") + (emit body))) (defmethod emit* :defrecord* - [{:keys [t fields pmasks]}] + [{:keys [t fields pmasks body]}] (let [fields (concat (map munge fields) '[__meta __extmap])] (emitln "") (emitln "/**") @@ -828,7 +829,8 @@ (emit-constant nil) (emitln ";") (emitln "}") - (emitln "})"))) + (emitln "})") + (emit body))) (defmethod emit* :dot [{:keys [target field method args env]}] diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 100c73d2a..a4957b38d 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -873,7 +873,6 @@ (dt->et type specs fields false)) ([type specs fields inline] (let [annots {:cljs.analyzer/type type - :cljs.analyzer/fields fields :protocol-impl true :protocol-inline inline}] (loop [ret [] specs specs] @@ -908,22 +907,16 @@ t (vary-meta t assoc :protocols protocols :skip-protocol-flag fpps) ] - (if (seq impls) - `(do - (deftype* ~t ~fields ~pmasks) - (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)))) - (extend-type ~t ~@(dt->et t impls fields true)) - ~(build-positional-factory t r fields) - ~t) - `(do - (deftype* ~t ~fields ~pmasks) - (set! (.-cljs$lang$type ~t) true) - (set! (.-cljs$lang$ctorStr ~t) ~(core/str r)) - (set! (.-cljs$lang$ctorPrWriter ~t) (fn [this# writer# opts#] (-write writer# ~(core/str r)))) - ~(build-positional-factory t r fields) - ~t)))) + `(do + (deftype* ~t ~fields ~pmasks + ~(if (seq impls) + `(extend-type ~t ~@(dt->et t impls 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)))) + + ~(build-positional-factory t r fields) + ~t))) (defn- emit-defrecord "Do not use this directly - use defrecord" @@ -1000,8 +993,8 @@ :protocols protocols :skip-protocol-flag fpps)] `(do - (~'defrecord* ~tagname ~hinted-fields ~pmasks) - (extend-type ~tagname ~@(dt->et tagname impls fields true)))))) + (~'defrecord* ~tagname ~hinted-fields ~pmasks + (extend-type ~tagname ~@(dt->et tagname impls fields true))))))) (defn- build-map-factory [rsym rname fields] (let [fn-name (with-meta (symbol (core/str 'map-> rsym)) From 92a00c388a442186cb25ee328f0a71423df55341 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Oct 2014 20:01:04 -0400 Subject: [PATCH 0185/4033] select-keys did not preserve metadata --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index a2af0b235..1a3783efc 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7087,7 +7087,7 @@ reduces them without incurring seq initialization" (assoc ret key entry) ret) (next keys))) - ret))) + (with-meta ret (meta map))))) ;;; PersistentHashSet From 15fbbf5d63fbc860e0fe4f7d45c7f00a27ebc0ba Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 31 Oct 2014 08:47:12 +0100 Subject: [PATCH 0186/4033] CLJS-879: add `update` from Clojure 1.7 --- src/cljs/cljs/core.cljs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 1a3783efc..8b8b8fab8 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -3924,6 +3924,22 @@ reduces them without incurring seq initialization" (assoc m k (apply update-in (get m k) ks f a b c args)) (assoc m k (apply f (get m k) a b c args))))) +(defn update + "'Updates' a value in an associative structure, where k is a + key and f is a function that will take the old value + and any supplied args and return the new value, and returns a new + structure. If the key does not exist, nil is passed as the old value." + ([m k f] + (assoc m k (f (get m k)))) + ([m k f x] + (assoc m k (f (get m k) x))) + ([m k f x y] + (assoc m k (f (get m k) x y))) + ([m k f x y z] + (assoc m k (f (get m k) x y z))) + ([m k f x y z & more] + (assoc m k (apply f (get m k) x y z more)))) + ;;; PersistentVector (deftype VectorNode [edit arr]) From c2d0f0023b419958f35477907510d9a27d8aa451 Mon Sep 17 00:00:00 2001 From: Gary Fredericks Date: Sat, 1 Nov 2014 21:07:01 -0500 Subject: [PATCH 0187/4033] CLJS-881: check for duplicate keys in array-map the function impl of array-map currently calls the constructor directly, while the macro version calls .fromArray. This means the latter correctly checks for duplicate keys while the former does not. Switch the array-map function to use .fromArray. --- src/cljs/cljs/core.cljs | 2 +- test/cljs/cljs/core_test.cljs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 8b8b8fab8..b8c4023ec 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -6901,7 +6901,7 @@ reduces them without incurring seq initialization" "keyval => key val Returns a new array map with supplied mappings." [& keyvals] - (PersistentArrayMap. nil (quot (count keyvals) 2) (apply array keyvals) nil)) + (.fromArray cljs.core/PersistentArrayMap (apply array keyvals) true false)) (defn obj-map "keyval => key val diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 4dd747ebe..d5ce92355 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2257,6 +2257,9 @@ (assert (= (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) {:a "two" :b "one" :c "three"})) + ;; CLJS-881 + (assert (= [:foo] (keys (apply array-map [:foo 1 :foo 2])))) + ;; basic iteration (def iter (es6-iterator [1 2 3])) From ecb13f06e766182e1daa7d227def3bad98bd8c49 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 5 Nov 2014 07:33:22 -0500 Subject: [PATCH 0188/4033] CLJS-875: bump tools.reader dep to 0.8.10 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 20bfef61e..2ec14e8a8 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.8.9 + 0.8.10 diff --git a/project.clj b/project.clj index 00a057801..d126eb8ad 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.6.0"] [org.clojure/data.json "0.2.3"] - [org.clojure/tools.reader "0.8.9"] + [org.clojure/tools.reader "0.8.10"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20140625"] [org.mozilla/rhino "1.7R4"]] diff --git a/script/bootstrap b/script/bootstrap index 5b5bdfa79..6798da423 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,7 +6,7 @@ CLOJURE_RELEASE="1.6.0" DJSON_RELEASE="0.2.3" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R3" -TREADER_RELEASE="0.8.9" +TREADER_RELEASE="0.8.10" mkdir -p lib From 805348d55c987493709c89a5910d679d56cf47e4 Mon Sep 17 00:00:00 2001 From: Immo Heikkinen Date: Mon, 27 Oct 2014 12:50:43 +0200 Subject: [PATCH 0189/4033] CLJS-510: Throw error when :output-wrapper and :optimizations :whitespace combined This combination doesn't produce working JavaScript file. --- src/clj/cljs/closure.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 1e8ba304b..5efb9f724 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -919,6 +919,10 @@ should contain the source for the given namespace name." ":source-map-path cannot be specified without also specifying :output-to and :source-map if optimization setting applied"))) true) +(defn check-output-wrapper [{:keys [output-wrapper optimizations]}] + (assert (not (and output-wrapper (= :whitespace optimizations))) + ":output-wrapper cannot be combined with :optimizations :whitespace")) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] @@ -942,6 +946,7 @@ should contain the source for the given namespace name." (check-output-dir opts) (check-source-map opts) (check-source-map-path opts) + (check-output-wrapper opts) (swap! compiler-env #(-> % (assoc-in [:opts :emit-constants] emit-constants) (assoc :target (:target opts)) From afb326f3e078856d1fa55864343165679beb85e2 Mon Sep 17 00:00:00 2001 From: Ben Moss Date: Fri, 7 Nov 2014 10:30:13 -0500 Subject: [PATCH 0190/4033] Add nthrest --- src/cljs/cljs/core.cljs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index b8c4023ec..07576f646 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1186,6 +1186,14 @@ reduces them without incurring seq initialization" (throw (js/Error. (str "nth not supported on this type " (type->str (type coll)))))))) +(defn nthrest + "Returns the nth rest of coll, coll when n is 0." + [coll n] + (loop [n n xs coll] + (if (and (pos? n) (seq xs)) + (recur (dec n) (rest xs)) + xs))) + (defn get "Returns the value mapped to key, not-found or nil if key not present." ([o k] From c65410719bf1876d18087f034501aa035f448646 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Nov 2014 13:14:40 -0500 Subject: [PATCH 0191/4033] move es6-iterator up, so seq types can use it --- src/cljs/cljs/core.cljs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 07576f646..6f347d7fb 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -691,6 +691,19 @@ (= y (first more))) false))) +;; EXPERIMENTAL: subject to change +(deftype ES6Iterator [^:mutable s] + Object + (next [_] + (if-not (nil? s) + (let [x (first s)] + (set! s (next s)) + #js {:value x :done false}) + #js {:value nil :done true}))) + +(defn es6-iterator [coll] + (ES6Iterator. (seq coll))) + ;;;;;;;;;;;;;;;;;;; Murmur3 Helpers ;;;;;;;;;;;;;;;; (defn ^number mix-collection-hash @@ -4941,19 +4954,6 @@ reduces them without incurring seq initialization" (set! (.-fromObject ObjMap) (fn [ks obj] (ObjMap. nil ks obj 0 nil))) -;; EXPERIMENTAL: subject to change -(deftype ES6Iterator [^:mutable s] - Object - (next [_] - (if-not (nil? s) - (let [x (first s)] - (set! s (next s)) - #js {:value x :done false}) - #js {:value nil :done true}))) - -(defn es6-iterator [coll] - (ES6Iterator. (seq coll))) - ;; EXPERIMENTAL: subject to change (deftype ES6EntriesIterator [^:mutable s] Object From d2a1aaee634c2d6a2047e26dd69f0385aff3f3fc Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Nov 2014 14:00:49 -0500 Subject: [PATCH 0192/4033] make all ISeqable types ES6 Iterables --- src/clj/cljs/core.clj | 6 +++++ src/cljs/cljs/core.cljs | 58 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index a4957b38d..835a75502 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1681,3 +1681,9 @@ (defmacro js-str [s] (core/list 'js* "''+~{}" s)) + +(defmacro es6-iterable [ty] + `(aset (.-prototype ~ty) cljs.core/ITER_SYMBOL + (fn [] + (this-as this# + (cljs.core/es6-iterator this#))))) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 6f347d7fb..53478a6ce 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -146,6 +146,8 @@ s (str ty))) +(def ITER_SYMBOL "@@iterator") + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; arrays ;;;;;;;;;;;;;;;; (defn ^array make-array @@ -962,6 +964,8 @@ reduces them without incurring seq initialization" (if (pos? c) (RSeq. coll (dec c) nil))))) +(es6-iterable IndexedSeq) + (defn prim-seq ([prim] (prim-seq prim 0)) @@ -1030,6 +1034,8 @@ reduces them without incurring seq initialization" (-reduce [col f] (seq-reduce f col)) (-reduce [col f start] (seq-reduce f start col))) +(es6-iterable RSeq) + (defn second "Same as (first (next x))" [coll] @@ -2225,6 +2231,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable List) + (deftype EmptyList [meta] Object (toString [coll] @@ -2279,6 +2287,8 @@ reduces them without incurring seq initialization" (set! (.-EMPTY List) (EmptyList. nil)) +(es6-iterable EmptyList) + (defn ^boolean reversible? [coll] (satisfies? IReversible coll)) @@ -2354,6 +2364,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable Cons) + (defn cons "Returns a new seq where x is the first element and seq is the rest." [x coll] @@ -2493,6 +2505,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable LazySeq) + (declare ArrayChunk) (deftype ChunkBuffer [^:mutable buf ^:mutable end] @@ -2606,6 +2620,8 @@ reduces them without incurring seq initialization" IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash))) +(es6-iterable ChunkedCons) + (defn chunk-cons [chunk rest] (if (zero? (-count chunk)) rest @@ -3132,6 +3148,8 @@ reduces them without incurring seq initialization" nil (-seq rest)))) +(es6-iterable LazyTransformer) + (set! (.-create LazyTransformer) (fn [xform coll] (LazyTransformer. (stepper xform (iter coll)) nil nil nil))) @@ -4282,6 +4300,8 @@ reduces them without incurring seq initialization" (recur (inc i) (conj! out (aget xs i))) (persistent! out)))))))) +(es6-iterable PersistentVector) + (defn vec [coll] (-persistent! (reduce -conj! @@ -4369,6 +4389,8 @@ reduces them without incurring seq initialization" (-reduce [coll f start] (ci-reduce (subvec vec (+ i off) (count vec)) f start))) +(es6-iterable ChunkedSeq) + (defn chunked-seq ([vec i off] (ChunkedSeq. vec (array-for vec i) i off nil nil)) ([vec node i off] (ChunkedSeq. vec node i off nil nil)) @@ -4471,6 +4493,8 @@ reduces them without incurring seq initialization" (-invoke [coll k not-found] (-nth coll k not-found))) +(es6-iterable Subvec) + (defn- build-subvec [meta v start end __hash] (if (instance? Subvec v) (recur meta (.-v v) (+ (.-start v) start) (+ (.-start v) end) __hash) @@ -4719,6 +4743,8 @@ reduces them without incurring seq initialization" ISeqable (-seq [coll] coll)) +(es6-iterable PersistentQueueSeq) + (deftype PersistentQueue [meta count front rear ^:mutable __hash] Object (toString [coll] @@ -4775,6 +4801,8 @@ reduces them without incurring seq initialization" (set! (.-EMPTY PersistentQueue) (PersistentQueue. nil 0 nil [] 0)) +(es6-iterable PersistentQueue) + (deftype NeverEquiv [] Object (equiv [this other] @@ -5110,6 +5138,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable PersistentArrayMapSeq) + (defn persistent-array-map-seq [arr i _meta] (when (<= i (- (alength arr) 2)) (PersistentArrayMapSeq. arr i _meta))) @@ -5301,6 +5331,8 @@ reduces them without incurring seq initialization" (-assoc! ret (aget arr i) (aget arr (inc i)))) (-persistent! ret)))))))) +(es6-iterable PersistentArrayMap) + (declare array->transient-hash-map) (deftype TransientArrayMap [^:mutable editable? @@ -5922,6 +5954,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable NodeSeq) + (defn- create-inode-seq ([nodes] (create-inode-seq nodes 0 nil)) @@ -5976,6 +6010,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable ArrayNodeSeq) + (defn- create-array-node-seq ([nodes] (create-array-node-seq nil nodes 0 nil)) ([meta nodes i s] @@ -6129,6 +6165,8 @@ reduces them without incurring seq initialization" (recur (inc i) (-assoc! out (aget ks i) (aget vs i))) (persistent! out)))))) +(es6-iterable PersistentHashMap) + (deftype TransientHashMap [^:mutable ^boolean edit ^:mutable root ^:mutable count @@ -6292,6 +6330,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable PersistentTreeMapSeq) + (defn- create-tree-map-seq [tree ascending? cnt] (PersistentTreeMapSeq. nil (tree-map-seq-push tree nil ascending?) ascending? cnt nil)) @@ -6500,6 +6540,8 @@ reduces them without incurring seq initialization" (-invoke [node k not-found] (-lookup node k not-found))) +(es6-iterable BlackNode) + (deftype RedNode [key val left right ^:mutable __hash] Object (add-left [node ins] @@ -6641,6 +6683,8 @@ reduces them without incurring seq initialization" (-invoke [node k not-found] (-lookup node k not-found))) +(es6-iterable RedNode) + (defn- tree-map-add [comp tree k v found] (if (nil? tree) (RedNode. k v nil nil nil) @@ -6896,6 +6940,8 @@ reduces them without incurring seq initialization" (set! (.-EMPTY PersistentTreeMap) (PersistentTreeMap. compare nil 0 nil 0)) +(es6-iterable PersistentTreeMap) + (defn hash-map "keyval => key val Returns a new hash map with supplied mappings." @@ -6998,6 +7044,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable KeySeq) + (defn keys "Returns a sequence of the map's keys." [hash-map] @@ -7064,6 +7112,8 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(es6-iterable ValSeq) + (defn vals "Returns a sequence of the map's values." [hash-map] @@ -7210,6 +7260,8 @@ reduces them without incurring seq initialization" (recur (inc i) (-conj! out (aget items i))) (-persistent! out))))))) +(es6-iterable PersistentHashSet) + (deftype TransientHashSet [^:mutable transient-map] ITransientCollection (-conj! [tcoll o] @@ -7338,6 +7390,8 @@ reduces them without incurring seq initialization" (set! (.-EMPTY PersistentTreeSet) (PersistentTreeSet. nil (.-EMPTY PersistentTreeMap) 0)) +(es6-iterable PersistentTreeSet) + (defn set-from-indexed-seq [iseq] (let [arr (.-arr iseq) ret (areduce arr i ^not-native res (-as-transient #{}) @@ -7645,6 +7699,8 @@ reduces them without incurring seq initialization" (recur (+ i step) ret))) ret)))) +(es6-iterable Range) + (defn range "Returns a lazy seq of nums from start (inclusive) to end (exclusive), by step, where start defaults to 0, step to 1, @@ -8338,6 +8394,8 @@ reduces them without incurring seq initialization" (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))) +(es6-iterable Eduction) + (defn eduction "Returns a reducible/iterable/seqable application of the transducer to the items in coll. Note that these applications From f3129bef1b64c367028e0b5ea9fb637274191790 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Nov 2014 14:06:24 -0500 Subject: [PATCH 0193/4033] use ES6 Symbol if available --- src/cljs/cljs/core.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 53478a6ce..2c59d0ae1 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -146,7 +146,10 @@ s (str ty))) -(def ITER_SYMBOL "@@iterator") +(if (and (exists? js/Symbol) + (identical? (goog/typeOf js/Symbol) "function")) + (def ITER_SYMBOL (.-iterator js/Symbol)) + (def ITER_SYMBOL "@@iterator")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; arrays ;;;;;;;;;;;;;;;; From 0941b757f58e95a86189727ffb19f035ff5d0ea0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Nov 2014 15:15:31 -0500 Subject: [PATCH 0194/4033] add experimental support for converting ES6 iterators into seqs --- src/cljs/cljs/core.cljs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 2c59d0ae1..038e6ef2d 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -709,6 +709,22 @@ (defn es6-iterator [coll] (ES6Iterator. (seq coll))) +(declare es6-iterator-seq) + +(deftype ES6IteratorSeq [value iter] + ISeqable + (-seq [this] this) + ISeq + (-first [_] value) + (-rest [_] + (es6-iterator-seq iter))) + +(defn es6-iterator-seq [iter] + (let [v (.next iter)] + (if (.-done v) + () + (ES6IteratorSeq. (.-value v) iter)))) + ;;;;;;;;;;;;;;;;;;; Murmur3 Helpers ;;;;;;;;;;;;;;;; (defn ^number mix-collection-hash @@ -8223,6 +8239,9 @@ reduces them without incurring seq initialization" Range (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + ES6IteratorSeq + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + Atom (-pr-writer [a writer opts] (-write writer "# Date: Fri, 14 Nov 2014 15:22:22 -0500 Subject: [PATCH 0195/4033] add new interop helper for invoking methods like "@@iterator" which are not valid unquoted JS object properties --- src/cljs/cljs/core.cljs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 038e6ef2d..4bb028b89 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -203,6 +203,12 @@ ([type aseq] (reduce (fn [a x] (.push a x) a) (array) aseq))) +(defn js-invoke + "Invoke JavaScript object method via string. Needed when the + string is not a valid unquoted property name." + [obj s & args] + (.apply (aget obj s) obj (into-array args))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;; core protocols ;;;;;;;;;;;;; (defprotocol Fn From 446bd3781a73675105786f65214f678ce0bb8d62 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Nov 2014 15:46:54 -0500 Subject: [PATCH 0196/4033] fix ES6IteratorSeq so it correctly caches rest --- src/cljs/cljs/core.cljs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 4bb028b89..7c126a51e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -717,19 +717,21 @@ (declare es6-iterator-seq) -(deftype ES6IteratorSeq [value iter] +(deftype ES6IteratorSeq [value iter ^:mutable _rest] ISeqable (-seq [this] this) ISeq (-first [_] value) (-rest [_] - (es6-iterator-seq iter))) + (when (nil? _rest) + (set! _rest (es6-iterator-seq iter))) + _rest)) (defn es6-iterator-seq [iter] (let [v (.next iter)] (if (.-done v) () - (ES6IteratorSeq. (.-value v) iter)))) + (ES6IteratorSeq. (.-value v) iter nil)))) ;;;;;;;;;;;;;;;;;;; Murmur3 Helpers ;;;;;;;;;;;;;;;; From 595ff5bed65fd6d3f6430dc96c095d36b69c5428 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 15 Nov 2014 21:09:41 -0500 Subject: [PATCH 0197/4033] add js-in interop macro --- src/clj/cljs/core.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 835a75502..fb38eb0e9 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -307,6 +307,9 @@ (defmacro js-delete [obj key] (core/list 'js* "delete ~{}[~{}]" obj key)) +(defmacro js-in [key obj] + (core/list 'js* "~{} in ~{}" key obj)) + (defmacro true? [x] (bool-expr (core/list 'js* "~{} === true" x))) From f18bf9e5e92f1d6f0af6d48863db93143c2b7d65 Mon Sep 17 00:00:00 2001 From: Immo Heikkinen Date: Fri, 14 Nov 2014 07:42:04 +0200 Subject: [PATCH 0198/4033] CLJS-863: Invalid arity error when calling 0-arity multimethod --- src/cljs/cljs/core.cljs | 6 ++++++ test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 7c126a51e..6fce17aa6 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8753,6 +8753,12 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype MultiFn [name dispatch-fn default-dispatch-val hierarchy method-table prefer-table method-cache cached-hierarchy] IFn + (-invoke [mf] + (let [dispatch-val (dispatch-fn) + target-fn (-get-method mf dispatch-val)] + (when-not target-fn + (throw-no-method-error name dispatch-val)) + (target-fn))) (-invoke [mf a] (let [dispatch-val (dispatch-fn a) target-fn (-get-method mf dispatch-val)] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index d5ce92355..3debafe04 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -975,6 +975,11 @@ (assert (= 52 (area r))) (assert (= :oops (area {}))) + ;; CLJS-863 + (defmulti foo2 (fn [])) + (defmethod foo2 :default [] :foo) + (assert (= :foo (foo2))) + ;; remove method tests (assert (= 2 (count (methods bar)))) (remove-method bar [::rect ::shape]) From d49c4b01718d169a55222db31fce0fa33b2207c9 Mon Sep 17 00:00:00 2001 From: Max Gonzih Date: Tue, 18 Nov 2014 16:20:51 +0100 Subject: [PATCH 0199/4033] Join preambles with newline line to catch cases with files without newlines. --- src/clj/cljs/closure.clj | 2 +- test/clj/cljs/closure_tests.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 5efb9f724..88d0c491a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -526,7 +526,7 @@ should contain the source for the given namespace name." (when-let [missing (seq (remove io/resource paths))] (ana/warning :preamble-missing @env/*compiler* {:missing (sort missing)})) (let [resources (remove nil? (map io/resource paths))] - (str (string/join (map slurp resources)) "\n"))) + (str (string/join "\n" (map slurp resources)) "\n"))) (defn make-preamble [{:keys [target preamble hashbang]}] (str (when (and (= :nodejs target) (not (false? hashbang))) diff --git a/test/clj/cljs/closure_tests.clj b/test/clj/cljs/closure_tests.clj index 8e83882e6..4438e5ae4 100644 --- a/test/clj/cljs/closure_tests.clj +++ b/test/clj/cljs/closure_tests.clj @@ -24,6 +24,6 @@ (make-preamble {:target :nodejs :preamble ["cljs/preamble1.js"]}))))) (testing "preamble" - (is (= "var preamble1 = require(\"preamble1\");var preamble2 = require(\"preamble2\");\n" + (is (= "var preamble1 = require(\"preamble1\");\nvar preamble2 = require(\"preamble2\");\n" (make-preamble {:preamble ["cljs/preamble1.js" "cljs/preamble2.js"]}))))) From d374201cb96c6a79c5f469170f81ec3aa8d41aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marczyk?= Date: Tue, 14 Oct 2014 14:30:39 +0200 Subject: [PATCH 0200/4033] CLJS-873: non-higher-order calls to array-map should return PAMs This fixes the cljs.core/array-map macro backing non-higher-order calls to cljs.core/array-map so that it always returns PersistentArrayMaps. --- src/clj/cljs/core.clj | 18 ++++++------------ test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index fb38eb0e9..f8b42c3e5 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1443,18 +1443,12 @@ (defmacro array-map ([] '(.-EMPTY cljs.core/PersistentArrayMap)) ([& kvs] - (core/cond - (core/> (count kvs) 16) - `(hash-map ~@kvs) - - (let [keys (map first (partition 2 kvs))] - (core/and (every? #(= (:op %) :constant) - (map #(cljs.analyzer/analyze &env %) keys)) - (= (count (into #{} keys)) (count keys)))) - `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) - - :else - `(.fromArray cljs.core/PersistentArrayMap (array ~@kvs) true false)))) + (let [keys (map first (partition 2 kvs))] + (if (core/and (every? #(= (:op %) :constant) + (map #(cljs.analyzer/analyze &env %) keys)) + (= (count (into #{} keys)) (count keys))) + `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) + `(.fromArray cljs.core/PersistentArrayMap (array ~@kvs) true false))))) (defmacro hash-map ([] `(.-EMPTY cljs.core/PersistentHashMap)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 3debafe04..944c7a24f 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2427,5 +2427,10 @@ (throw (ex-info "CLJS-849 regression!" {:m (persistent! m) :xs xs})))))) + ;; CLJS- + (let [m (array-map 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15)] + (assert (instance? cljs.core/PersistentArrayMap m)) + (assert (= (seq m) [[0 0] [1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9] [10 10] [11 11] [12 12] [13 13] [14 14] [15 15]]))) + :ok ) From 124998c05dea844e892cd28b40f89eecdd442e1a Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Fri, 14 Nov 2014 20:10:29 +0100 Subject: [PATCH 0201/4033] CLJS-888: Better placement of newlines in emitter --- src/clj/cljs/compiler.clj | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 2f83f382f..a993211d6 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -367,10 +367,10 @@ (emits "(" (when checked "cljs.core.truth_") "(" test ")?" then ":" else ")") (do (if checked - (emitln "if(cljs.core.truth_(" test "))") - (emitln "if(" test ")")) - (emitln "{" then "} else") - (emitln "{" else "}")))))) + (emitln "if(cljs.core.truth_(" test ")){") + (emitln "if(" test "){")) + (emitln then "} else {") + (emitln else "}")))))) (defmethod emit* :case* [{:keys [v tests thens default env]}] @@ -478,7 +478,7 @@ (emit-wrap env (emits "(function " (munge name) "(") (emit-fn-params params) - (emits "){") + (emitln "){") (when type (emitln "var self__ = this;")) (when recurs (emitln "while(true){")) @@ -499,7 +499,7 @@ (doseq [param params] (emit param) (when-not (= param (last params)) (emits ","))) - (emits "){") + (emitln "){") (when recurs (emitln "while(true){")) (emits expr) (when recurs @@ -516,11 +516,11 @@ (when variadic (emits "var ") (emit (last params)) - (emits " = null;") + (emitln " = null;") (emitln "if (arguments.length > " (dec (count params)) ") {") (emits " ") (emit (last params)) - (emits " = cljs.core.array_seq(Array.prototype.slice.call(arguments, " (dec (count params)) "),0);") + (emitln " = cljs.core.array_seq(Array.prototype.slice.call(arguments, " (dec (count params)) "),0);") (emitln "} ")) (emits "return " delegate-name ".call(this,") (doseq [param params] @@ -612,11 +612,10 @@ (defmethod emit* :do [{:keys [statements ret env]}] (let [context (:context env)] - (when (and statements (= :expr context)) (emits "(function (){")) - (when statements - (emits statements)) + (when (and statements (= :expr context)) (emitln "(function (){")) + (doseq [s statements] (emitln s)) (emit ret) - (when (and statements (= :expr context)) (emits "})()")))) + (when (and statements (= :expr context)) (emitln "})()")))) (defmethod emit* :try [{:keys [env try catch name finally]}] @@ -647,7 +646,7 @@ (doseq [{:keys [init] :as binding} bindings] (emits "var ") (emit binding) ; Binding will be treated as a var - (emits " = " init ";")) + (emitln " = " init ";")) (when is-loop (emitln "while(true){")) (emits expr) (when is-loop From bf2d2413dcb46b2cec9a00e37af407006634c804 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Fri, 14 Nov 2014 20:10:49 +0100 Subject: [PATCH 0202/4033] CLJS-888: Omit redundant {} around emitted recur --- src/clj/cljs/compiler.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index a993211d6..0c68e6918 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -664,13 +664,11 @@ [{:keys [frame exprs env]}] (let [temps (vec (take (count exprs) (repeatedly gensym))) params (:params frame)] - (emitln "{") (dotimes [i (count exprs)] (emitln "var " (temps i) " = " (exprs i) ";")) (dotimes [i (count exprs)] (emitln (munge (params i)) " = " (temps i) ";")) - (emitln "continue;") - (emitln "}"))) + (emitln "continue;"))) (defmethod emit* :letfn [{:keys [bindings expr env]}] From bdb044babbabd965975388af866c6456b9bddf2d Mon Sep 17 00:00:00 2001 From: Tim Griesser Date: Wed, 13 Aug 2014 14:53:46 -0400 Subject: [PATCH 0203/4033] Recursively check IEncodeClojure in js->clj --- src/cljs/cljs/core.cljs | 46 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 6fce17aa6..57224f245 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8487,30 +8487,28 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." strings to keywords." ([x] (js->clj x {:keywordize-keys false})) ([x & opts] - (cond - (satisfies? IEncodeClojure x) - (-js->clj x (apply array-map opts)) - - (seq opts) - (let [{:keys [keywordize-keys]} opts - keyfn (if keywordize-keys keyword str) - f (fn thisfn [x] - (cond - (seq? x) - (doall (map thisfn x)) - - (coll? x) - (into (empty x) (map thisfn x)) - - (array? x) - (vec (map thisfn x)) - - (identical? (type x) js/Object) - (into {} (for [k (js-keys x)] - [(keyfn k) (thisfn (aget x k))])) - - :else x))] - (f x))))) + (let [{:keys [keywordize-keys]} opts + keyfn (if keywordize-keys keyword str) + f (fn thisfn [x] + (cond + (satisfies? IEncodeClojure x) + (-js->clj x (apply array-map opts)) + + (seq? x) + (doall (map thisfn x)) + + (coll? x) + (into (empty x) (map thisfn x)) + + (array? x) + (vec (map thisfn x)) + + (identical? (type x) js/Object) + (into {} (for [k (js-keys x)] + [(keyfn k) (thisfn (aget x k))])) + + :else x))] + (f x)))) (defn memoize "Returns a memoized version of a referentially transparent function. The From c5e96e75a3328139edfe57df1c9a191ff460cce2 Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Thu, 12 Jun 2014 01:23:19 -0500 Subject: [PATCH 0204/4033] CLJS-814: clojure.string/reverse breaks surrogate pairs A surrogate pair's code units should not be separated or reordered by string reversal. This changes ClojureScript clojure.string/reverse to behave exactly as in Clojure and Java, i.e. keeping surrogate pairs in their original code unit order. --- src/cljs/clojure/string.cljs | 6 +++++- test/cljs/clojure/string_test.cljs | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cljs/clojure/string.cljs b/src/cljs/clojure/string.cljs index 1b94fa257..1a01ec7e0 100644 --- a/src/cljs/clojure/string.cljs +++ b/src/cljs/clojure/string.cljs @@ -15,10 +15,14 @@ [coll] (reduce conj () coll)) +(def ^:private re-surrogate-pair + (js/RegExp. "([\\uD800-\\uDBFF])([\\uDC00-\\uDFFF])" "g")) + (defn reverse "Returns s with its characters reversed." [s] - (.. s (split "") (reverse) (join ""))) + (-> (.replace s re-surrogate-pair "$2$1") + (.. (split "") (reverse) (join "")))) (defn replace "Replaces all instance of match with replacement in s. diff --git a/test/cljs/clojure/string_test.cljs b/test/cljs/clojure/string_test.cljs index 5582a12a9..ab9db1fd2 100644 --- a/test/cljs/clojure/string_test.cljs +++ b/test/cljs/clojure/string_test.cljs @@ -6,6 +6,8 @@ ;; reverse (assert (= "" (s/reverse ""))) (assert (= "tab" (s/reverse "bat"))) + ;; CLJS-814 + (assert (= "c\uD834\uDD1Ea" (s/reverse "a\uD834\uDD1Ec"))) ;; U+1D11E MUSICAL SYMBOL G CLEF ;; replace (assert (= "faabar" (s/replace "foobar" \o \a))) (assert (= "barbarbar" (s/replace "foobarfoo" "foo" "bar"))) From 66e9d688cc864a6f8a8ce45aad0ea173915334b2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 2 Dec 2014 06:56:55 -0500 Subject: [PATCH 0205/4033] CLJS-716: support hashing of JavaScript dates --- src/cljs/cljs/core.cljs | 3 +++ test/cljs/cljs/core_test.cljs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 57224f245..af10dac17 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -535,6 +535,9 @@ (string? o) (m3-hash-int (hash-string o)) + (instance? js/Date o) + (.valueOf o) + (nil? o) 0 :else diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 944c7a24f..ea0b7501d 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2432,5 +2432,21 @@ (assert (instance? cljs.core/PersistentArrayMap m)) (assert (= (seq m) [[0 0] [1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9] [10 10] [11 11] [12 12] [13 13] [14 14] [15 15]]))) + ;; CLJS-716 + (def test-map + {:a 1 + :b 2 + #inst "2013-12-19T05:00:00.000-00:00" 3 + :d 4 + :e 5 + #inst "2013-12-06T05:00:00.000-00:00" 6 + :g 7 + :h 8 + :i 9 + :j 10}) + + (assert (= (test-map #inst "2013-12-19T05:00:00.000-00:00") 3)) + (assert (= (test-map #inst "2013-12-06T05:00:00.000-00:00") 6)) + :ok ) From 6b251ba9e46c5fff647229d771f4c8f55989a6ef Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 2 Dec 2014 08:40:54 -0500 Subject: [PATCH 0206/4033] revert CLJS-801 --- src/clj/cljs/core.clj | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index f8b42c3e5..b84fb7d91 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -205,31 +205,10 @@ (core/inc (core/quot c 32))))) (defmacro str [& xs] - ;; Eagerly stringify any string or char literals. - (let [clean-xs (reduce (fn [acc x] - (core/cond - (core/or (core/string? x) (core/char? x)) - (if (core/string? (peek acc)) - (conj (pop acc) (core/str (peek acc) x)) - (conj acc (core/str x))) - (core/nil? x) acc - :else (conj acc x))) - [] xs) - ;; clean-xs now has no nils, chars, or string-adjoining-string. bools, - ;; ints and floats will be emitted literally to allow JS string coersion. - strs (->> clean-xs - (map #(if (core/or (core/string? %) (core/integer? %) - (core/float? %) (core/true? %) - (core/false? %)) - "~{}" - "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})")) - (interpose "+") - (apply core/str))] - ;; Google closure advanced compile will stringify and concat strings and - ;; numbers at compilation time. - (list* 'js* (core/str (if (core/string? (first clean-xs)) "(" "(''+") - strs ")") - clean-xs))) + (let [strs (->> (repeat (count xs) "cljs.core.str(~{})") + (interpose ",") + (apply core/str))] + (list* 'js* (core/str "[" strs "].join('')") xs))) (defn bool-expr [e] (vary-meta e assoc :tag 'boolean)) From d54defd32d6c5ffcf6b0698072184fe8ccecc93a Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Tue, 2 Dec 2014 13:12:22 +0100 Subject: [PATCH 0207/4033] CLJS-853: propagate read-time metadata on fn and reify forms at runtime --- src/clj/cljs/analyzer.clj | 112 +++++++++++++++++++------------------- src/clj/cljs/core.clj | 6 +- 2 files changed, 60 insertions(+), 58 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 097ef60d4..bc950df88 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -719,67 +719,71 @@ {:env env :variadic variadic :params params :max-fixed-arity fixed-arity :type type :form form :recurs @(:flag recur-frame) :expr expr})) +(declare analyze-wrap-meta) + (defmethod parse 'fn* [op env [_ & args :as form] name _] - (let [[name meths] (if (symbol? (first args)) - [(first args) (next args)] - [name (seq args)]) - ;;turn (fn [] ...) into (fn ([]...)) - meths (if (vector? (first meths)) (list meths) meths) - locals (:locals env) - name-var (if name - (merge + (analyze-wrap-meta + (let [[name meths] (if (symbol? (first args)) + [(first args) (next args)] + [name (seq args)]) + ;;turn (fn [] ...) into (fn ([]...)) + meths (if (vector? (first meths)) (list meths) meths) + locals (:locals env) + name-var (if name + (merge {:name name :info {:shadow (or (locals name) - (get-in env [:js-globals name]))}} + (get-in env [:js-globals name]))}} (when-let [tag (-> name meta :tag)] - {:ret-tag tag}))) - locals (if (and locals name) (assoc locals name name-var) locals) - type (-> form meta ::type) - protocol-impl (-> form meta :protocol-impl) - protocol-inline (-> form meta :protocol-inline) - menv (if (> (count meths) 1) (assoc env :context :expr) env) - menv (merge menv - {:protocol-impl protocol-impl - :protocol-inline protocol-inline}) - methods (map #(analyze-fn-method menv locals % type) meths) - max-fixed-arity (apply max (map :max-fixed-arity methods)) - variadic (boolean (some :variadic methods)) - locals (if name - (update-in locals [name] assoc - ;; TODO: can we simplify? - David - :fn-var true - :variadic variadic - :max-fixed-arity max-fixed-arity - :method-params (map :params methods) - :methods methods) - locals) - methods (if name - ;; a second pass with knowledge of our function-ness/arity - ;; lets us optimize self calls - (no-warn (doall (map #(analyze-fn-method menv locals % type) meths))) - methods)] - (let [variadic-methods (filter :variadic methods) - variadic-params (count (:params (first variadic-methods))) - param-counts (map (comp count :params) methods)] - (when (< 1 (count variadic-methods)) - (warning :multiple-variadic-overloads env {:name name-var})) - (when (not (or (zero? variadic-params) (= variadic-params (+ 1 max-fixed-arity)))) - (warning :variadic-max-arity env {:name name-var})) - (when (not= (distinct param-counts) param-counts) - (warning :overload-arity env {:name name-var}))) - {:env env :op :fn :form form :name name-var :methods methods :variadic variadic - :tag 'function - :recur-frames *recur-frames* :loop-lets *loop-lets* - :jsdoc [(when variadic "@param {...*} var_args")] - :max-fixed-arity max-fixed-arity - :protocol-impl protocol-impl - :protocol-inline protocol-inline - :children (mapv :expr methods)})) + {:ret-tag tag}))) + locals (if (and locals name) (assoc locals name name-var) locals) + type (-> form meta ::type) + protocol-impl (-> form meta ::protocol-impl) + protocol-inline (-> form meta ::protocol-inline) + menv (if (> (count meths) 1) (assoc env :context :expr) env) + menv (merge menv + {:protocol-impl protocol-impl + :protocol-inline protocol-inline}) + methods (map #(analyze-fn-method menv locals % type) meths) + max-fixed-arity (apply max (map :max-fixed-arity methods)) + variadic (boolean (some :variadic methods)) + locals (if name + (update-in locals [name] assoc + ;; TODO: can we simplify? - David + :fn-var true + :variadic variadic + :max-fixed-arity max-fixed-arity + :method-params (map :params methods) + :methods methods) + locals) + methods (if name + ;; a second pass with knowledge of our function-ness/arity + ;; lets us optimize self calls + (no-warn (doall (map #(analyze-fn-method menv locals % type) meths))) + methods) + form (vary-meta form dissoc ::protocol-impl ::protocol-inline ::type)] + (let [variadic-methods (filter :variadic methods) + variadic-params (count (:params (first variadic-methods))) + param-counts (map (comp count :params) methods)] + (when (< 1 (count variadic-methods)) + (warning :multiple-variadic-overloads env {:name name-var})) + (when (not (or (zero? variadic-params) (= variadic-params (+ 1 max-fixed-arity)))) + (warning :variadic-max-arity env {:name name-var})) + (when (not= (distinct param-counts) param-counts) + (warning :overload-arity env {:name name-var}))) + {:env env :op :fn :form form :name name-var :methods methods :variadic variadic + :tag 'function + :recur-frames *recur-frames* :loop-lets *loop-lets* + :jsdoc [(when variadic "@param {...*} var_args")] + :max-fixed-arity max-fixed-arity + :protocol-impl protocol-impl + :protocol-inline protocol-inline + :children (mapv :expr methods)}))) (defmethod parse 'letfn* [op env [_ bindings & exprs :as form] name _] - (when-not (and (vector? bindings) (even? (count bindings))) + (when-not (and (vector? bindings) (even? (count bindings))) (throw (error env "bindings must be vector of even number of elements"))) (let [n->fexpr (into {} (map (juxt first second) (partition 2 bindings))) names (keys n->fexpr) @@ -1485,8 +1489,6 @@ (parse-invoke env form))) (analyze env mform name opts)))))))) -(declare analyze-wrap-meta) - (defn analyze-map [env form] (let [expr-env (assoc env :context :expr) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index b84fb7d91..18ec3ac1c 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -622,7 +622,7 @@ IMeta (~'-meta [~this-sym] ~meta-sym) ~@impls)) - (new ~t ~@locals nil)))) + (new ~t ~@locals ~(meta &form))))) (defmacro specify! [expr & impls] (let [x (with-meta (gensym "x") {:extend :instance})] @@ -855,8 +855,8 @@ (dt->et type specs fields false)) ([type specs fields inline] (let [annots {:cljs.analyzer/type type - :protocol-impl true - :protocol-inline inline}] + :cljs.analyzer/protocol-impl true + :cljs.analyzer/protocol-inline inline}] (loop [ret [] specs specs] (if (seq specs) (let [p (first specs) From 1eadd336949479b20ff7d5fd4b9bf307273fc291 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 2 Dec 2014 08:44:26 -0500 Subject: [PATCH 0208/4033] test for CLJS-853 --- test/cljs/cljs/core_test.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index ea0b7501d..b853ec0e3 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2448,5 +2448,9 @@ (assert (= (test-map #inst "2013-12-19T05:00:00.000-00:00") 3)) (assert (= (test-map #inst "2013-12-06T05:00:00.000-00:00") 6)) + ;; CLJS-853 + + (assert (= {:foo true} (meta ^:foo (fn [])))) + :ok ) From c926c13f052161cb0ae161d814442f677bf013f1 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 2 Dec 2014 14:42:08 +0100 Subject: [PATCH 0209/4033] implement INamed for multi-method --- src/clj/cljs/core.clj | 5 +++-- src/cljs/cljs/core.cljs | 4 ++++ test/cljs/cljs/core_test.cljs | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 18ec3ac1c..00cff49c5 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1563,7 +1563,8 @@ m) m (if (meta mm-name) (conj (meta mm-name) m) - m)] + m) + mm-ns (-> &env :ns :name core/str)] (when (= (count options) 1) (throw (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)"))) (let [options (apply core/hash-map options) @@ -1575,7 +1576,7 @@ method-cache# (atom {}) cached-hierarchy# (atom {}) hierarchy# (get ~options :hierarchy (cljs.core/get-global-hierarchy))] - (cljs.core/MultiFn. ~(name mm-name) ~dispatch-fn ~default hierarchy# + (cljs.core/MultiFn. (cljs.core/symbol ~mm-ns ~(name mm-name)) ~dispatch-fn ~default hierarchy# method-table# prefer-table# method-cache# cached-hierarchy#)))))) (defmacro defmethod diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index af10dac17..e1676aed8 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8928,6 +8928,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-methods [mf] @method-table) (-prefers [mf] @prefer-table) + + INamed + (-name [this] (-name name)) + (-namespace [this] (-namespace name)) IHash (-hash [this] (goog/getUid this))) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index b853ec0e3..7e2f294fa 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -994,6 +994,14 @@ ([x y] :two) ([x y & r] [:three r])) (assert (= [:three '(2)] (apply apply-multi-test [0 1 2]))) + + + ;; CLJS-469, helpful exception message on bad dispatch + (defmulti no-dispatch-value :test) + (try + (no-dispatch-value {:test :test}) + (catch js/Error e + (assert (not= -1 (.indexOf (.-message e) "cljs.core-test/no-dispatch-value"))))) ;; custom hierarchy tests (def my-map-hierarchy (atom (-> (make-hierarchy) From 0ea0ca0c73f827f56436d738e8fca1a9c9b99f2c Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 2 Dec 2014 09:07:37 -0500 Subject: [PATCH 0210/4033] CLJS-622: better error reporting for zero arity protocol methods --- src/clj/cljs/core.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 00cff49c5..dc418b300 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1008,6 +1008,9 @@ fqn (fn [n] (symbol (core/str ns-name "." n))) prefix (protocol-prefix p) methods (if (core/string? (first doc+methods)) (next doc+methods) doc+methods) + _ (core/doseq [[mname & arities] methods] + (when (some #{0} (map count arities)) + (throw (Exception. (core/str "Invalid protocol, " psym " defines method " mname " with arity 0"))))) expand-sig (fn [fname slot sig] `(~sig (if (and ~(first sig) (. ~(first sig) ~(symbol (core/str "-" slot)))) ;; Property access needed here. From 318068f176daeb80e8d7dc7680c7cab05fc48069 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 2 Dec 2014 09:17:05 -0500 Subject: [PATCH 0211/4033] CLJS-506: expose more Closure minification knobs --- src/clj/cljs/closure.clj | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 88d0c491a..bcf08c6ca 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -60,7 +60,8 @@ com.google.javascript.jscomp.JSError com.google.javascript.jscomp.CheckLevel com.google.javascript.jscomp.DiagnosticGroups - com.google.javascript.jscomp.CommandLineRunner)) + com.google.javascript.jscomp.CommandLineRunner + com.google.javascript.jscomp.AnonymousFunctionNamingPolicy)) (defmacro ^:private debug-prn [& args] @@ -97,6 +98,15 @@ (when (contains? opts :pseudo-names) (set! (.generatePseudoNames compiler-options) (:pseudo-names opts))) + (when (contains? opts :anon-fn-naming-policy) + (let [policy (:anon-fn-naming-policy opts)] + (set! (.anonymousFunctionNaming compiler-options) + (case policy + :off AnonymousFunctionNamingPolicy/OFF + :unmapped AnonymousFunctionNamingPolicy/UNMAPPED + :mapped AnonymousFunctionNamingPolicy/MAPPED + (throw (IllegalArgumentException. (str "Invalid :anon-fn-naming-policy value " policy " - only :off, :unmapped, :mapped permitted"))))))) + (when (contains? opts :language-in) (case (:language-in opts) :ecmascript5 (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5) From 50cc86ff3c4c39181a198a4f9be788c149eaae00 Mon Sep 17 00:00:00 2001 From: Osbert Feng Date: Mon, 13 Jan 2014 23:19:57 -0800 Subject: [PATCH 0212/4033] CLJS-749: In cases where *clojurescript-version* is unbound (as when running from source), return empty string from (clojurescript-version) instead of ".". --- src/clj/cljs/util.clj | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 0e50efb4d..c1be25908 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -17,16 +17,18 @@ (defn clojurescript-version "Returns clojurescript version as a printable string." [] - (str - (:major *clojurescript-version*) - "." - (:minor *clojurescript-version*) - (when-let [i (:incremental *clojurescript-version*)] - (str "." i)) - (when-let [q (:qualifier *clojurescript-version*)] - (str "-" q)) - (when (:interim *clojurescript-version*) - "-SNAPSHOT"))) + (if (bound? #'*clojurescript-version*) + (str + (:major *clojurescript-version*) + "." + (:minor *clojurescript-version*) + (when-let [i (:incremental *clojurescript-version*)] + (str "." i)) + (when-let [q (:qualifier *clojurescript-version*)] + (str "-" q)) + (when (:interim *clojurescript-version*) + "-SNAPSHOT")) + "")) (defn compiled-by-version [^File f] (with-open [reader (io/reader f)] From 85773301cf12541a053890643d1d943a6ed361de Mon Sep 17 00:00:00 2001 From: Osbert Feng Date: Mon, 13 Jan 2014 21:41:32 -0800 Subject: [PATCH 0213/4033] CLJS-749: Append CLJS version to browser repl-env working directory to prevent issues when changing versions in a project. --- src/clj/cljs/repl/browser.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index d3e706d3c..d4662e0a4 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -11,6 +11,7 @@ (:require [clojure.java.io :as io] [clojure.string :as string] [cljs.compiler :as comp] + [cljs.util :as util] [cljs.closure :as cljsc] [cljs.repl :as repl] [cljs.repl.server :as server]) @@ -229,7 +230,8 @@ port: The port on which the REPL server will run. Defaults to 9000. working-dir: The directory where the compiled REPL client JavaScript will - be stored. Defaults to \".repl\". + be stored. Defaults to \".repl\" with a ClojureScript version + suffix, eg. \".repl-0.0-2138\". serve-static: Should the REPL server attempt to serve static content? Defaults to true. static-dir: List of directories to search for static content. Defaults to @@ -247,7 +249,7 @@ opts (merge (BrowserEnv.) {:port 9000 :optimizations :simple - :working-dir ".repl" + :working-dir (->> [".repl" (util/clojurescript-version)] (remove empty?) (string/join "-")) :serve-static true :static-dir ["." "out/"] :preloaded-libs [] From d25aea69697cca2ef5fa8b9a6f7e4fd089685ace Mon Sep 17 00:00:00 2001 From: Osbert Feng Date: Tue, 2 Dec 2014 10:26:18 -0800 Subject: [PATCH 0214/4033] CLJS-749: Ignore .repl-* given that CLJS version is appended by default. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5d24a8422..947dd004d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ closure /coresimple.js /out /pom.xml -.repl +.repl* *.swp *.zip clojurescript_release_* From 254e54876dfeae8c50d885010e730cdeaef26c99 Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Tue, 2 Dec 2014 15:36:33 -0500 Subject: [PATCH 0215/4033] forcing source maps to load for dynamic js reloads This is the basic scetch --- src/clj/cljs/compiler.clj | 3 ++- src/clj/cljs/source_map.clj | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 0c68e6918..44a96efcc 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -870,6 +870,7 @@ (defn url-path [^File f] (.getPath (.toURL (.toURI f)))) + (defn compile-file* ([src dest] (compile-file* src dest nil)) ([src dest opts] @@ -912,7 +913,7 @@ {:source-map (:source-map sm-data)}))] (when (and sm-data (= (:optimizations opts) :none)) (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" (.getName sm-file)) + (emits "\n//# sourceMappingURL=" (.getName sm-file) "?rel=" (System/currentTimeMillis)) (spit sm-file (sm/encode {(url-path src) (:source-map sm-data)} {:lines (+ (:gen-line sm-data) 2) diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index 2c3344b07..164a3c6f2 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -216,10 +216,12 @@ "file" (:file opts) "sources" (into [] (let [paths (keys m) - f (if (or (:output-dir opts) + f (comp + #(str % "?rel=" (System/currentTimeMillis)) + (if (or (:output-dir opts) (:source-map-path opts)) #(relativize-path % opts) - #(last (string/split % #"/")))] + #(last (string/split % #"/"))))] (map f paths))) "lineCount" (:lines opts) "mappings" (->> (lines->segs (concat preamble-lines @lines)) From 6bf7d8e98e5b0aabef8cf3e55ef2492145357022 Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Tue, 2 Dec 2014 15:39:00 -0500 Subject: [PATCH 0216/4033] whitespace --- src/clj/cljs/compiler.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 44a96efcc..9c478dfd3 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -870,7 +870,6 @@ (defn url-path [^File f] (.getPath (.toURL (.toURI f)))) - (defn compile-file* ([src dest] (compile-file* src dest nil)) ([src dest opts] From dbb7e43e2372dac0795ae3181a5f80b6db7d3556 Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Tue, 2 Dec 2014 23:47:24 -0600 Subject: [PATCH 0217/4033] CLJS-807: Emitter cannot emit BigInt or BigDecimal Extend compiler to emit double-approximations of BigInt and BigDecimal so that literal forms like "1N" or "1.5M" can compile. --- src/clj/cljs/compiler.clj | 2 ++ test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 9c478dfd3..ad16ecf99 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -167,6 +167,8 @@ (defmethod emit-constant Long [x] (emits "(" x ")")) (defmethod emit-constant Integer [x] (emits x)) ; reader puts Integers in metadata (defmethod emit-constant Double [x] (emits x)) +(defmethod emit-constant BigDecimal [x] (emits (.doubleValue ^BigDecimal x))) +(defmethod emit-constant clojure.lang.BigInt [x] (emits (.doubleValue ^clojure.lang.BigInt x))) (defmethod emit-constant String [x] (emits (wrap-in-double-quotes (escape-string x)))) (defmethod emit-constant Boolean [x] (emits (if x "true" "false"))) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 7e2f294fa..a72e6186f 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2460,5 +2460,11 @@ (assert (= {:foo true} (meta ^:foo (fn [])))) + ;; CLJS-807 + (assert (= -1 -1N)) + (assert (= 9.007199254740996E15 9007199254740995N)) + (assert (= 1.5 1.5M)) + (assert (= 4.9E-324 5E-324M)) + :ok ) From 2314c55c9ded522a0fc227e4510a4c253c0bd6c2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 4 Dec 2014 17:07:47 -0500 Subject: [PATCH 0218/4033] fix var resolution bug pointed out by Brandon Bloom Need to check the current namespace before checking that a var is a core var. --- src/clj/cljs/analyzer.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index bc950df88..829bcc401 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -385,9 +385,11 @@ (recur env (get-in @env/*compiler* [::namespaces (-> env :ns :name) :imports sym]) confirm) :else - (let [full-ns (if (core-name? env sym) - 'cljs.core - (-> env :ns :name))] + (let [cur-ns (-> env :ns :name) + full-ns (cond + (get-in @env/*compiler* [::namespaces cur-ns :defs sym]) cur-ns + (core-name? env sym) 'cljs.core + :else cur-ns)] (when confirm (confirm env full-ns sym)) (merge (get-in @env/*compiler* [::namespaces full-ns :defs sym]) From ecc5765d599cadb0d22e2d66635df9eb68c60774 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 Dec 2014 14:35:45 -0500 Subject: [PATCH 0219/4033] CLJS-885: relax type inference around numbers --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 829bcc401..cc60519b7 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1341,7 +1341,7 @@ ;; when functions like first won't return nil, so variadic ;; numeric functions like cljs.core/< would produce a spurious ;; warning without this - David - (and (set? t) (set/subset? t '#{any number nil clj-nil})))) + (and (set? t) (or (contains? t 'number) (contains? t 'any))))) types) (warning :invalid-arithmetic env {:js-op (-> form meta :js-op) From 2a496412961e633b282c56c32ccd0be2be26bcb8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 Dec 2014 14:59:46 -0500 Subject: [PATCH 0220/4033] Update changes.md --- changes.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/changes.md b/changes.md index aa443679a..6b3f03733 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,40 @@ +## 0.0-2411 + +### Enhancements +* forcing source maps to load for dynamic js reloads +* All ISeqable types are now ES6 iterable +* CLJS-863: Invalid arity error when calling 0-arity multimethod +* CLJS-622: better error reporting for zero arity protocol methods +* CLJS-506: expose more Closure minification knobs + +### Changes +* CLJS-807: Emitter cannot emit BigInt or BigDecimal +* CLJS-749: Ignore .repl-* given that CLJS version is appended by default. +* CLJS-749: Append CLJS version to browser repl-env +* CLJS-749: *clojurescript-version* is unbound return empty string +* implement INamed for multi-method +* revert CLJS-801 +* CLJS-888: Omit redundant {} around emitted recur +* CLJS-888: Better placement of newlines in emitter +* Join preambles with newline line to catch cases with files without newlines. +* add js-in interop macro +* Add nthrest +* CLJS-510: Throw error when :output-wrapper and :optimizations :whitespace combined +* CLJS-875: bump tools.reader dep to 0.8.10 +* CLJS-879: add `update` from Clojure 1.7 +* CLJS-857: change deftype*/defrecord* special forms to include their inline methods decls + +### Fixes +* CLJS-885: relax type inference around numbers +* fix var resolution bug pointed out by Brandon Bloom +* CLJS-853: propagate read-time metadata on fn and reify forms at runtime +* CLJS-716: support hashing of JavaScript dates +* CLJS-814: clojure.string/reverse breaks surrogate pairs +* Recursively check IEncodeClojure in js->clj +* CLJS-873: non-higher-order calls to array-map should return PAMs +* CLJS-881: check for duplicate keys in array-map +* select-keys did not preserve metadata + ## 0.0-2371 ### Fixes From 0c9aeb271e8d8e30a3d2e24dcdcae58c838787e3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 Dec 2014 15:42:34 -0500 Subject: [PATCH 0221/4033] add test macro --- src/clj/cljs/core.clj | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index dc418b300..45b1f7e80 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -34,7 +34,7 @@ cond-> cond->> as-> some-> some->> - if-some when-some]) + if-some when-some test]) (:require clojure.walk clojure.set cljs.compiler @@ -1667,3 +1667,15 @@ (fn [] (this-as this# (cljs.core/es6-iterator this#))))) + +(defmacro test + "Given a symbol, resolve it as a var, if a test is attached + to a var execute it." + [v] + (let [f (-> (dissoc &env :locals) + (ana/resolve-var v) + :test)] + `(let [f# ~f] + (if f# + (do (f#) :ok) + :no-test)))) From 2997958e1b11876b77e9b87ba84954e00fa7ffb9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 Dec 2014 15:43:13 -0500 Subject: [PATCH 0222/4033] mark test as done --- devnotes/corelib.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devnotes/corelib.org b/devnotes/corelib.org index 3e92978d6..391761e1f 100644 --- a/devnotes/corelib.org +++ b/devnotes/corelib.org @@ -513,7 +513,7 @@ as macro * DONE take-last * DONE take-nth * DONE take-while -* test +* DONE test * the-ns * thread-bound? * DONE time From 34a01e7b736ea197fb58d4b38dffb13a3e80aab4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 6 Dec 2014 07:28:05 -0500 Subject: [PATCH 0223/4033] Analysis & compilation support for :test metadata Remove `test` macro, cannot work as the test needs to be compiled in the same environment as the var. Add `cljs.analyzer/*load-tests*` dynamic var. Bind this properly in `cljs.closure` if `:load-tests` option provided. Change the `parse 'def` case in `cljs.analyzer`. We need to move down the `var-expr` logic so that the info added to the analysis atom will be present in the var expr AST. Change `cljs.analyzer/analyze-symbol` to special case `:test` metadata. Analyze the test form in the current environment and attach it to the var AST via a `:test` property. Change `emit* :def` case so that if `cljs.analzyer/*load-tests*` is true and the var has a `:test` property emit the test. Do a little bit of emission special casing so we get well formed JavaScript output. Provide `cljs.core/test` as a plain old function. Fix `cljs.top-level` test so that we exclude `test` now that it's in the standard library. --- src/clj/cljs/analyzer.clj | 20 +++++++++++++------- src/clj/cljs/closure.clj | 1 + src/clj/cljs/compiler.clj | 6 +++++- src/clj/cljs/core.clj | 11 ----------- src/cljs/cljs/core.cljs | 9 +++++++++ test/cljs/cljs/top_level.cljs | 5 +++-- 6 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index cc60519b7..f39dcea12 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -31,6 +31,8 @@ (def ^:dynamic *cljs-macros-is-classpath* true) (def ^:dynamic *cljs-dep-set* (with-meta #{} {:dep-path []})) (def ^:dynamic *analyze-deps* true) +(def ^:dynamic *load-tests* true) + (def -cljs-macros-loaded (atom false)) (def ^:dynamic *cljs-warnings* @@ -624,11 +626,6 @@ (update-in env [:ns :excludes] conj sym)) env) name (:name (resolve-var (dissoc env :locals) sym)) - var-expr (assoc (analyze (-> env (dissoc :locals) - (assoc :context :expr) - (assoc :def-var true)) - sym) - :op :var) init-expr (when (contains? args :init) (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] (merge @@ -682,7 +679,13 @@ (when (and fn-var? tag) {:ret-tag tag}))) (merge {:env env :op :def :form form - :name name :var var-expr :doc doc :init init-expr} + :name name + :var (assoc (analyze (-> env (dissoc :locals) + (assoc :context :expr) + (assoc :def-var true)) + sym) + :op :var) + :doc doc :init init-expr} (when tag (if fn-var? {:ret-tag tag} @@ -1416,7 +1419,10 @@ (if-not (contains? (meta sym) ::analyzed) (resolve-existing-var env sym) (resolve-var env sym))) - (assoc ret :op :var :info (resolve-var env sym))))))) + (let [ret (assoc ret :op :var :info (resolve-var env sym))] + (if-let [test (-> sym meta :test)] + (assoc ret :test (analyze env test)) + ret))))))) (defn get-expander [sym env] (let [mvar diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index bcf08c6ca..71d3e17bc 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -967,6 +967,7 @@ should contain the source for the given namespace name." (:static-fns opts) ana/*cljs-static-fns*) *assert* (not= (:elide-asserts opts) true) + ana/*load-tests* (not= (:load-tests opts) false) ana/*cljs-warnings* (let [enabled? (true? (opts :warnings true))] (merge ana/*cljs-warnings* diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index ad16ecf99..731899a9c 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -431,7 +431,11 @@ ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") (when-not (= :expr (:context env)) (emitln ";")) (when export - (emitln "goog.exportSymbol('" (munge export) "', " mname ");"))))) + (emitln "goog.exportSymbol('" (munge export) "', " mname ");")) + (when (and ana/*load-tests* (:test var)) + (when (= :expr (:context env)) + (emitln ";")) + (emits var ".cljs$lang$test = " (:test var)))))) (defn emit-apply-to [{:keys [name params env]}] diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 45b1f7e80..85dec8424 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1668,14 +1668,3 @@ (this-as this# (cljs.core/es6-iterator this#))))) -(defmacro test - "Given a symbol, resolve it as a var, if a test is attached - to a var execute it." - [v] - (let [f (-> (dissoc &env :locals) - (ana/resolve-var v) - :test)] - `(let [f# ~f] - (if f# - (do (f#) :ok) - :no-test)))) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index e1676aed8..645914ee5 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -9038,3 +9038,12 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." '#{if def fn* do let* loop* letfn* throw try recur new set! ns deftype* defrecord* . js* & quote} x)) + +(defn test + "test [v] finds fn at key :test in var metadata and calls it, + presuming failure will throw exception" + [v] + (let [f (.-cljs$lang$test v)] + (if f + (do (f) :ok) + :no-test))) diff --git a/test/cljs/cljs/top_level.cljs b/test/cljs/cljs/top_level.cljs index 45f0e1433..a2a7323ab 100644 --- a/test/cljs/cljs/top_level.cljs +++ b/test/cljs/cljs/top_level.cljs @@ -1,4 +1,5 @@ -(ns cljs.top-level) +(ns cljs.top-level + (:refer-clojure :exclude [test])) (let [foo 1] (defn bar [] @@ -10,4 +11,4 @@ (defn test [] (assert (= (bar) 1)) - (assert (= (baz) 2))) \ No newline at end of file + (assert (= (baz) 2))) From fbed6140e49f6fb23babeb445f2bfbc29c4f2cb1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 6 Dec 2014 08:54:02 -0500 Subject: [PATCH 0224/4033] Add `ns-interns` macro --- src/clj/cljs/core.clj | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 85dec8424..d656c0afa 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -34,7 +34,7 @@ cond-> cond->> as-> some-> some->> - if-some when-some test]) + if-some when-some test ns-interns]) (:require clojure.walk clojure.set cljs.compiler @@ -1668,3 +1668,12 @@ (this-as this# (cljs.core/es6-iterator this#))))) +(defmacro ns-interns + "Returns a map of the intern mappings for the namespace." + [[quote ns]] + (core/assert (core/and (= quote 'quote) (core/symbol? ns)) + "Argument to ns-interns must be a quoted symbol") + `(into {} + [~@(map + (fn [[sym _]] `[(symbol ~(name sym)) ~sym]) + (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))])) From 54b75eca6fdb6a5959253a9b2addbcf3119567a7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 6 Dec 2014 10:26:52 -0500 Subject: [PATCH 0225/4033] Add static support for vars Add `Var` type, supports `IMeta`, `IDeref`, `IPrintWithWriter`. metadata includes all the same information as Clojure with the exception of `:arglists`. Stop calling `read-string` on the results from a ClojureScript REPL evaluation. It's not clear what problem this was attempting to solve and it prevented the `Var` type from printing correctly. --- src/clj/cljs/core.clj | 15 +++++++++++++-- src/clj/cljs/repl.clj | 16 ++++++---------- src/cljs/cljs/core.cljs | 16 +++++++++++++++- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index d656c0afa..6979cfbca 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -34,7 +34,7 @@ cond-> cond->> as-> some-> some->> - if-some when-some test ns-interns]) + if-some when-some test ns-interns var]) (:require clojure.walk clojure.set cljs.compiler @@ -1668,6 +1668,16 @@ (this-as this# (cljs.core/es6-iterator this#))))) +(defmacro var [sym] + (core/let [{sym-ns :ns sym-name :name :as info} (ana/resolve-var &env sym)] + `(cljs.core/Var. ~sym + (symbol ~(name sym-ns) ~(name sym-name)) + {:ns (symbol ~(name sym-ns)) + :name (symbol ~(name sym-name)) + :file ~(:file info) + :line ~(:line info) + :column ~(:column info)}))) + (defmacro ns-interns "Returns a map of the intern mappings for the namespace." [[quote ns]] @@ -1675,5 +1685,6 @@ "Argument to ns-interns must be a quoted symbol") `(into {} [~@(map - (fn [[sym _]] `[(symbol ~(name sym)) ~sym]) + (fn [[sym _]] `[(symbol ~(name sym)) (var ~sym)]) (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))])) + diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5829060a0..bab2b2826 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -148,16 +148,12 @@ ret#)))))) (defn- eval-and-print [repl-env env form] - (let [ret (evaluate-form repl-env - (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) - "" - form - (wrap-fn form))] - (try (prn (read-string ret)) - (catch Exception e - (if (string? ret) - (println ret) - (prn nil)))))) + (println + (evaluate-form repl-env + (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) + "" + form + (wrap-fn form)))) (def default-special-fns (let [load-file-fn (fn [repl-env file] (load-file repl-env file))] diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 645914ee5..68704950f 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -618,6 +618,15 @@ name)] (Symbol. ns name sym-str nil nil)))) +(deftype Var [val sym _meta] + IDeref + (-deref [_] val) + IMeta + (-meta [_] _meta)) + +(defn var [val sym meta] + (Var. val sym meta)) + ;;;;;;;;;;;;;;;;;;; fundamentals ;;;;;;;;;;;;;;; (declare array-seq prim-seq IndexedSeq) @@ -8257,7 +8266,12 @@ reduces them without incurring seq initialization" (-pr-writer [a writer opts] (-write writer "#"))) + (-write writer ">")) + + Var + (-pr-writer [a writer opts] + (-write writer "#'") + (pr-writer (.-sym a) writer opts))) ;; IComparable (extend-protocol IComparable From f9ce62637bca40d0ab1d0db855ea03ae8cf9e310 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 6 Dec 2014 10:53:07 -0500 Subject: [PATCH 0226/4033] support accessing :test on var meta --- src/clj/cljs/core.clj | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 6979cfbca..47f7f7f72 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1672,11 +1672,14 @@ (core/let [{sym-ns :ns sym-name :name :as info} (ana/resolve-var &env sym)] `(cljs.core/Var. ~sym (symbol ~(name sym-ns) ~(name sym-name)) - {:ns (symbol ~(name sym-ns)) - :name (symbol ~(name sym-name)) - :file ~(:file info) - :line ~(:line info) - :column ~(:column info)}))) + (merge + {:ns (symbol ~(name sym-ns)) + :name (symbol ~(name sym-name)) + :file ~(:file info) + :line ~(:line info) + :column ~(:column info)} + ~(when (:test info) + `{:test (.-cljs$lang$test ~sym)}))))) (defmacro ns-interns "Returns a map of the intern mappings for the namespace." From 7397ae2e089612fc05b8e16d7dc1497cbddc47a1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 6 Dec 2014 14:08:45 -0500 Subject: [PATCH 0227/4033] implement cljs.repl/doc over new var support --- src/clj/cljs/analyzer.clj | 30 ++++++++++++++++-------------- src/clj/cljs/core.clj | 2 ++ src/clj/cljs/repl.clj | 5 +++++ src/cljs/cljs/repl.cljs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 src/cljs/cljs/repl.cljs diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index f39dcea12..c4a42732e 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -662,20 +662,22 @@ :info (-> protocol-symbol meta :protocol-info) :impls #{}}) (when fn-var? - {:fn-var true - ;; protocol implementation context - :protocol-impl (:protocol-impl init-expr) - ;; inline protocol implementation context - :protocol-inline (:protocol-inline init-expr) - :variadic (:variadic init-expr) - :max-fixed-arity (:max-fixed-arity init-expr) - :method-params (map #(vec (map :name (:params %))) (:methods init-expr)) - :methods (map (fn [method] - (let [tag (infer-tag env (assoc method :op :method))] - (cond-> (select-keys method - [:max-fixed-arity :variadic]) - tag (assoc :tag tag)))) - (:methods init-expr))}) + (let [params (map #(vec (map :name (:params %))) (:methods init-expr))] + {:fn-var true + ;; protocol implementation context + :protocol-impl (:protocol-impl init-expr) + ;; inline protocol implementation context + :protocol-inline (:protocol-inline init-expr) + :variadic (:variadic init-expr) + :max-fixed-arity (:max-fixed-arity init-expr) + :method-params params + :arglists params + :methods (map (fn [method] + (let [tag (infer-tag env (assoc method :op :method))] + (cond-> (select-keys method + [:max-fixed-arity :variadic]) + tag (assoc :tag tag)))) + (:methods init-expr))}) ) (when (and fn-var? tag) {:ret-tag tag}))) (merge {:env env :op :def :form form diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 47f7f7f72..a7f4468a9 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1675,6 +1675,8 @@ (merge {:ns (symbol ~(name sym-ns)) :name (symbol ~(name sym-name)) + :doc ~(:doc info) + :arglists (quote ~(:arglists info)) :file ~(:file info) :line ~(:line info) :column ~(:column info)} diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index bab2b2826..f76109aa7 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -229,3 +229,8 @@ (do (eval-and-print repl-env env form) (recur))))) (-tear-down repl-env))))) + +(defmacro doc + "Prints documentation for a var or special form given its name" + [sym] + `(cljs.repl/print-doc (meta (var ~sym)))) diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs new file mode 100644 index 000000000..b7a519b20 --- /dev/null +++ b/src/cljs/cljs/repl.cljs @@ -0,0 +1,31 @@ +;; 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.repl) + +(defn print-doc [m] + (println "-------------------------") + (println (str (when-let [ns (:ns m)] (str ns "/")) (:name m))) + (cond + (:forms m) (doseq [f (:forms m)] + (print " ") + (prn f)) + (:arglists m) (prn (:arglists m))) + (if (:special-form m) + (do + (println "Special Form") + (println " " (:doc m)) + (if (contains? m :url) + (when (:url m) + (println (str "\n Please see http://clojure.org/" (:url m)))) + (println (str "\n Please see http://clojure.org/special_forms#" + (:name m))))) + (do + (when (:macro m) + (println "Macro")) + (println " " (:doc m))))) From 91153fa7d26f55cc466ac1d24184ba78bac7cf72 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 6 Dec 2014 15:43:23 -0500 Subject: [PATCH 0228/4033] make var a special form so that it cannot be shadowed a la Clojure. Thanks to Nicola Mometto for pointing out the issue. --- src/clj/cljs/analyzer.clj | 18 +++++++++++++++++- src/clj/cljs/compiler.clj | 5 +++++ src/clj/cljs/core.clj | 15 --------------- src/cljs/cljs/core.cljs | 3 --- test/cljs/cljs/core_test.cljs | 12 ++++++++++++ 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index c4a42732e..ed81b4488 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -413,7 +413,7 @@ (declare analyze analyze-symbol analyze-seq) (def specials '#{if def fn* do let* loop* letfn* throw try recur new set! - ns deftype* defrecord* . js* & quote case*}) + ns deftype* defrecord* . js* & quote case* var}) (def ^:dynamic *recur-frames* nil) (def ^:dynamic *loop-lets* ()) @@ -502,6 +502,22 @@ (defmulti parse (fn [op & rest] op)) +(defmethod parse 'var + [op env [_ sym :as form] _ _] + (let [var (resolve-var env sym)] + {:env env :op :var-special :form form + :var (analyze env sym) + :sym (analyze env `(quote ~(symbol (name (:ns var)) (name (:name var))))) + :meta (analyze env + `(quote + {:ns ~(:ns var) + :name ~(:name var) + :doc ~(:doc var) + :arglists ~(:arglists var) + :file ~(:file var) + :line ~(:line var) + :column ~(:column var)}))})) + (defmethod parse 'if [op env [_ test then else :as form] name _] (when (< (count form) 3) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 731899a9c..fec9865b3 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -253,6 +253,11 @@ (when-not (= :statement (:context env)) (emit-wrap env (emits (munge info))))))) +(defmethod emit* :var-special + [{:keys [env var sym meta] :as arg}] + (emit-wrap env + (emits "new cljs.core.Var(" var "," sym "," meta ")"))) + (defmethod emit* :meta [{:keys [expr meta env]}] (emit-wrap env diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index a7f4468a9..5a48577da 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1668,21 +1668,6 @@ (this-as this# (cljs.core/es6-iterator this#))))) -(defmacro var [sym] - (core/let [{sym-ns :ns sym-name :name :as info} (ana/resolve-var &env sym)] - `(cljs.core/Var. ~sym - (symbol ~(name sym-ns) ~(name sym-name)) - (merge - {:ns (symbol ~(name sym-ns)) - :name (symbol ~(name sym-name)) - :doc ~(:doc info) - :arglists (quote ~(:arglists info)) - :file ~(:file info) - :line ~(:line info) - :column ~(:column info)} - ~(when (:test info) - `{:test (.-cljs$lang$test ~sym)}))))) - (defmacro ns-interns "Returns a map of the intern mappings for the namespace." [[quote ns]] diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 68704950f..a74f03189 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -624,9 +624,6 @@ IMeta (-meta [_] _meta)) -(defn var [val sym meta] - (Var. val sym meta)) - ;;;;;;;;;;;;;;;;;;; fundamentals ;;;;;;;;;;;;;;; (declare array-seq prim-seq IndexedSeq) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index a72e6186f..a634cab41 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2466,5 +2466,17 @@ (assert (= 1.5 1.5M)) (assert (= 4.9E-324 5E-324M)) + ;; vars + + (defn var-test + "A docstring" + [a b] + (+ a b)) + + (def var-meta (meta #'var-test)) + + (assert (= (:doc var-meta) "A docstring")) + (assert (= (:arglists var-meta) '([a b]))) + :ok ) From 4d200284bab9b92f65f7c1475acc92a94b98faf3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 6 Dec 2014 17:11:32 -0500 Subject: [PATCH 0229/4033] restore handling of :test metadata on vars --- src/clj/cljs/analyzer.clj | 13 ++++--------- test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index ed81b4488..0f32a6d45 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -508,15 +508,10 @@ {:env env :op :var-special :form form :var (analyze env sym) :sym (analyze env `(quote ~(symbol (name (:ns var)) (name (:name var))))) - :meta (analyze env - `(quote - {:ns ~(:ns var) - :name ~(:name var) - :doc ~(:doc var) - :arglists ~(:arglists var) - :file ~(:file var) - :line ~(:line var) - :column ~(:column var)}))})) + :meta (let [ks [:ns :name :doc :arglists :file :line :column] + m (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) + :test `(.-cljs$lang$test ~sym))] + (analyze env m))})) (defmethod parse 'if [op env [_ test then else :as form] name _] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index a634cab41..a3336f169 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2478,5 +2478,13 @@ (assert (= (:doc var-meta) "A docstring")) (assert (= (:arglists var-meta) '([a b]))) + (defn var-test-test + "A docstring" + {:test (fn [] :cool)} + [a b] + (+ a b)) + + (assert (= ((:test (meta #'var-test-test))) :cool)) + :ok ) From ec44c25d46852b9df677978e59c85daf8b73a974 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 7 Dec 2014 09:54:21 -0500 Subject: [PATCH 0230/4033] fix self call bug in var :test metadata support `cljs.analyzer/analyze-symbol` takes an environment stripped of `:locals`. In `'def` case of `cljs.analyzer/parse` put the locals back on the environment as `:test-locals` so that we can use them if need be in `cljs.analyzer/analyze-symbol`. Add test case for multi-arity var `:test` metadata with self call. --- src/clj/cljs/analyzer.clj | 7 +++++-- test/cljs/cljs/core_test.cljs | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0f32a6d45..fde8138f4 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -622,7 +622,8 @@ tag (-> sym meta :tag) protocol (-> sym meta :protocol) dynamic (-> sym meta :dynamic) - ns-name (-> env :ns :name)] + ns-name (-> env :ns :name) + locals (:locals env)] (when (namespace sym) (throw (error env "Can't def ns-qualified name"))) (when-let [doc (:doc args)] @@ -694,6 +695,8 @@ (merge {:env env :op :def :form form :name name :var (assoc (analyze (-> env (dissoc :locals) + ;; if there's :test var metadata will need locals + (assoc :test-locals locals) (assoc :context :expr) (assoc :def-var true)) sym) @@ -1434,7 +1437,7 @@ (resolve-var env sym))) (let [ret (assoc ret :op :var :info (resolve-var env sym))] (if-let [test (-> sym meta :test)] - (assoc ret :test (analyze env test)) + (assoc ret :test (analyze (assoc env :locals (:test-locals env)) test)) ret))))))) (defn get-expander [sym env] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index a3336f169..5e2c98552 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2486,5 +2486,14 @@ (assert (= ((:test (meta #'var-test-test))) :cool)) + (defn var-test-self-call + "A docstring" + {:test (fn bar ([] (bar 1)) ([n] n))} + [a b] + (+ a b)) + + (assert (= (.cljs$lang$test var-test-self-call) 1)) + (assert (= (.cljs$lang$test var-test-self-call 2) 2)) + :ok ) From 1d65be4dd7dbfb63cef6f81acbb431295b26e068 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 7 Dec 2014 09:59:25 -0500 Subject: [PATCH 0231/4033] clojure.test -> cljs.test wip --- src/clj/cljs/test.clj | 243 ++++++++++++++++++++++++ src/cljs/cljs/test.cljs | 401 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 644 insertions(+) create mode 100644 src/clj/cljs/test.clj create mode 100644 src/cljs/cljs/test.cljs diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj new file mode 100644 index 000000000..0275d5668 --- /dev/null +++ b/src/clj/cljs/test.clj @@ -0,0 +1,243 @@ +; 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.test + (:require [cljs.env :as env] + [cljs.analyzer :as ana])) + +;; ============================================================================= +;; Utilities for assertions + +(defn get-possibly-unbound-var + "Like var-get but returns nil if the var is unbound." + [v] + (try + (var-get v) + (catch IllegalStateException e + nil))) + +(defn function? + "Returns true if argument is a function or a symbol that resolves to + a function (not a macro)." + [x] + (if (symbol? x) + (when-let [v (resolve x)] + (when-let [value (get-possibly-unbound-var v)] + (and (fn? value) + (not (:macro (meta v)))))) + (fn? x))) + +(defn assert-predicate + "Returns generic assertion code for any functional predicate. The + 'expected' argument to 'report' will contains the original form, the + 'actual' argument will contain the form with all its sub-forms + evaluated. If the predicate returns false, the 'actual' form will + be wrapped in (not...)." + [env msg form] + (let [args (rest form) + pred (first form)] + `(let [values# (list ~@args) + result# (apply ~pred values#)] + (if result# + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual (cons ~pred values#)}) + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual (list '~'not (cons '~pred values#))})) + result#))) + +(defn assert-any + "Returns generic assertion code for any test, including macros, Java + method calls, or isolated symbols." + [env msg form] + `(let [value# ~form] + (if value# + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual value#}) + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual value#})) + value#)) + +;; ============================================================================= +;; Assertion Methods + +;; You don't call these, but you can add methods to extend the 'is' +;; macro. These define different kinds of tests, based on the first +;; symbol in the test expression. + +(defmulti assert-expr + (fn [env msg form] + (cond + (nil? form) :always-fail + (seq? form) (first form) + :else :default))) + +(defmethod assert-expr :always-fail [env msg form] + ;; nil test: always fail + `(cljs.test/do-report ~env {:type :fail, :message ~msg})) + +(defmethod assert-expr :default [env msg form] + (if (and (sequential? form) + (function? (first form))) + (assert-predicate env msg form) + (assert-any env msg form))) + +(defmethod assert-expr 'instance? [env msg form] + ;; Test if x is an instance of y. + `(let [klass# ~(nth form 1) + object# ~(nth form 2)] + (let [result# (instance? klass# object#)] + (if result# + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual (class object#)}) + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual (class object#)})) + result#))) + +(defmethod assert-expr 'thrown? [env msg form] + ;; (is (thrown? c expr)) + ;; Asserts that evaluating expr throws an exception of class c. + ;; Returns the exception thrown. + (let [klass (second form) + body (nthnext form 2)] + `(try + ~@body + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual nil}) + (catch ~klass e# + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual e#}) + e#)))) + +(defmethod assert-expr 'thrown-with-msg? [env msg form] + ;; (is (thrown-with-msg? c re expr)) + ;; Asserts that evaluating expr throws an exception of class c. + ;; Also asserts that the message string of the exception matches + ;; (with re-find) the regular expression re. + (let [klass (nth form 1) + re (nth form 2) + body (nthnext form 3)] + `(try + ~@body + (cljs.test/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) + (catch ~klass e# + (let [m# (.getMessage e#)] + (if (re-find ~re m#) + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual e#}) + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual e#}))) + e#)))) + +(defmacro try-expr + "Used by the 'is' macro to catch unexpected exceptions. + You don't call this." + [env msg form] + `(try + ~(cljs.test/assert-expr env msg form) + (catch :default t# + (cljs.test/do-report ~env + {:type :error, :message ~msg, + :expected '~form, :actual t#})))) + +;; ============================================================================= +;; Assertion Macros + +(defmacro is + "Generic assertion macro. 'form' is any predicate test. + 'msg' is an optional message to attach to the assertion. + + Example: (is (= 4 (+ 2 2)) \"Two plus two should be 4\") + + Special forms: + + (is (thrown? c body)) checks that an instance of c is thrown from + body, fails if not; then returns the thing thrown. + + (is (thrown-with-msg? c re body)) checks that an instance of c is + thrown AND that the message on the exception matches (with + re-find) the regular expression re." + ([form] `(cljs.test/is ~form nil)) + ([form msg] + `(fn [env#] (cljs.test/try-expr env# ~msg ~form)))) + +(defmacro testing + "Adds a new string to the list of testing contexts. May be nested, + but must occur inside a test function (deftest)." + [env string & body] + `(fn [env#] + (reduce #(%1 %2) + (update-in env# [:testing-contexts] conj ~string) + [~@body]))) + +;; ============================================================================= +;; Defining Tests + +(defmacro deftest + "Defines a test function with no arguments. Test functions may call + other tests, so tests may be composed. If you compose tests, you + should also define a function named test-ns-hook; run-tests will + call test-ns-hook instead of testing all vars. + + Note: Actually, the test body goes in the :test metadata on the var, + and the real function (the value of the var) calls test-var on + itself. + + When cljs.analyzer/*load-tests* is false, deftest is ignored." + [name & body] + (when ana/*load-tests* + `(do + (def ~(vary-meta name assoc :test + `(fn self# + ([] (self# (cljs.test/empty-env))) + ([env#] + (let [ret# (reduce #(%1 %2) env# [~@body])] + (when cljs.test/*return* + ret#))))) + (fn + ([] (~name (cljs.test/empty-env))) + ([env#] + (binding [cljs.test/*env* env#] + (cljs.test/test-var env# (.-cljs$lang$var ~name)))))) + (set! (.-cljs$lang$var ~name) (var ~name))))) + +(defmacro test-all-vars + "Calls test-vars on every var interned in the namespace, with fixtures." + ([ns] + `(cljs.test/test-vars (vals (ns-interns ns)))) + ([env ns] + `(cljs.test/test-vars ~env (vals (ns-interns ns))))) + +(defmacro test-ns + "If the namespace defines a function named test-ns-hook, calls that. + Otherwise, calls test-all-vars on the namespace. 'ns' is a + namespace object or a symbol. + + Internally binds *report-counters* to a ref initialized to + *initial-report-counters*. Returns the final, dereferenced state of + *report-counters*." + [[quote ns]] + (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") + `(let [env# (cljs.test/empty-env)] + (-> env# + (cljs.test/do-report env# {:type :begin-test-ns, :ns ~'~ns}) + ;; If the namespace has a test-ns-hook function, call that: + ~(if-let [v (get-in @env/*compiler* [::ana/namespaces ns :defs 'test-ns-hook])] + `(~(symbol (name ns) "test-ns-hook")) + ;; Otherwise, just test every var in the namespace. + `(cljs.test/test-all-vars env# ns-obj)) + (cljs.test/do-report env# {:type :end-test-ns, :ns ~'~ns})))) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs new file mode 100644 index 000000000..98ce8539b --- /dev/null +++ b/src/cljs/cljs/test.cljs @@ -0,0 +1,401 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns +^{:author "Stuart Sierra, with contributions and suggestions by + Chas Emerick, Allen Rohner, Stuart Halloway, and David Nolen", + :doc "A unit testing framework. + + ASSERTIONS + + The core of the library is the \"is\" macro, which lets you make + assertions of any arbitrary expression: + + (is (= 4 (+ 2 2))) + (is (instance? Integer 256)) + (is (.startsWith \"abcde\" \"ab\")) + + You can type an \"is\" expression directly at the REPL, which will + print a message if it fails. + + user> (is (= 5 (+ 2 2))) + + FAIL in (:1) + expected: (= 5 (+ 2 2)) + actual: (not (= 5 4)) + false + + The \"expected:\" line shows you the original expression, and the + \"actual:\" shows you what actually happened. In this case, it + shows that (+ 2 2) returned 4, which is not = to 5. Finally, the + \"false\" on the last line is the value returned from the + expression. The \"is\" macro always returns the result of the + inner expression. + + There are two special assertions for testing exceptions. The + \"(is (thrown? c ...))\" form tests if an exception of class c is + thrown: + + (is (thrown? ArithmeticException (/ 1 0))) + + \"(is (thrown-with-msg? c re ...))\" does the same thing and also + tests that the message on the exception matches the regular + expression re: + + (is (thrown-with-msg? ArithmeticException #\"Divide by zero\" + (/ 1 0))) + + DOCUMENTING TESTS + + \"is\" takes an optional second argument, a string describing the + assertion. This message will be included in the error report. + + (is (= 5 (+ 2 2)) \"Crazy arithmetic\") + + In addition, you can document groups of assertions with the + \"testing\" macro, which takes a string followed by any number of + assertions. The string will be included in failure reports. + Calls to \"testing\" may be nested, and all of the strings will be + joined together with spaces in the final report, in a style + similar to RSpec + + (testing \"Arithmetic\" + (testing \"with positive integers\" + (is (= 4 (+ 2 2))) + (is (= 7 (+ 3 4)))) + (testing \"with negative integers\" + (is (= -4 (+ -2 -2))) + (is (= -1 (+ 3 -4))))) + + Note that, unlike RSpec, the \"testing\" macro may only be used + INSIDE a \"deftest\" or \"with-test\" form (see below). + + + DEFINING TESTS + + There are two ways to define tests. The \"with-test\" macro takes + a defn or def form as its first argument, followed by any number + of assertions. The tests will be stored as metadata on the + definition. + + (with-test + (defn my-function [x y] + (+ x y)) + (is (= 4 (my-function 2 2))) + (is (= 7 (my-function 3 4)))) + + As of Clojure SVN rev. 1221, this does not work with defmacro. + See http://code.google.com/p/clojure/issues/detail?id=51 + + The other way lets you define tests separately from the rest of + your code, even in a different namespace: + + (deftest addition + (is (= 4 (+ 2 2))) + (is (= 7 (+ 3 4)))) + + (deftest subtraction + (is (= 1 (- 4 3))) + (is (= 3 (- 7 4)))) + + This creates functions named \"addition\" and \"subtraction\", which + can be called like any other function. Therefore, tests can be + grouped and composed, in a style similar to the test framework in + Peter Seibel's \"Practical Common Lisp\" + + + (deftest arithmetic + (addition) + (subtraction)) + + The names of the nested tests will be joined in a list, like + \"(arithmetic addition)\", in failure reports. You can use nested + tests to set up a context shared by several tests. + + + RUNNING TESTS + + Run tests with the function \"(run-tests namespaces...)\": + + (run-tests 'your.namespace 'some.other.namespace) + + If you don't specify any namespaces, the current namespace is + used. To run all tests in all namespaces, use \"(run-all-tests)\". + + By default, these functions will search for all tests defined in + a namespace and run them in an undefined order. However, if you + are composing tests, as in the \"arithmetic\" example above, you + probably do not want the \"addition\" and \"subtraction\" tests run + separately. In that case, you must define a special function + named \"test-ns-hook\" that runs your tests in the correct order: + + (defn test-ns-hook [] + (arithmetic)) + + Note: test-ns-hook prevents execution of fixtures (see below). + + + OMITTING TESTS FROM PRODUCTION CODE + + You can bind the variable \"*load-tests*\" to false when loading or + compiling code in production. This will prevent any tests from + being created by \"with-test\" or \"deftest\". + + + FIXTURES + + Fixtures allow you to run code before and after tests, to set up + the context in which tests should be run. + + A fixture is just a function that calls another function passed as + an argument. It looks like this: + + (defn my-fixture [f] + Perform setup, establish bindings, whatever. + (f) Then call the function we were passed. + Tear-down / clean-up code here. + ) + + Fixtures are attached to namespaces in one of two ways. \"each\" + fixtures are run repeatedly, once for each test function created + with \"deftest\" or \"with-test\". \"each\" fixtures are useful for + establishing a consistent before/after state for each test, like + clearing out database tables. + + \"each\" fixtures can be attached to the current namespace like this: + (use-fixtures :each fixture1 fixture2 ...) + The fixture1, fixture2 are just functions like the example above. + They can also be anonymous functions, like this: + (use-fixtures :each (fn [f] setup... (f) cleanup...)) + + The other kind of fixture, a \"once\" fixture, is only run once, + around ALL the tests in the namespace. \"once\" fixtures are useful + for tasks that only need to be performed once, like establishing + database connections, or for time-consuming tasks. + + Attach \"once\" fixtures to the current namespace like this: + (use-fixtures :once fixture1 fixture2 ...) + + Note: Fixtures and test-ns-hook are mutually incompatible. If you + are using test-ns-hook, fixture functions will *never* be run. + + + SAVING TEST OUTPUT TO A FILE + + All the test reporting functions write to the var *test-out*. By + default, this is the same as *out*, but you can rebind it to any + PrintWriter. For example, it could be a file opened with + clojure.java.io/writer. + + + EXTENDING TEST-IS (ADVANCED) + + You can extend the behavior of the \"is\" macro by defining new + methods for the \"assert-expr\" multimethod. These methods are + called during expansion of the \"is\" macro, so they should return + quoted forms to be evaluated. + + You can plug in your own test-reporting framework by rebinding + the \"report\" function: (report event) + + The 'event' argument is a map. It will always have a :type key, + whose value will be a keyword signaling the type of event being + reported. Standard events with :type value of :pass, :fail, and + :error are called when an assertion passes, fails, and throws an + exception, respectively. In that case, the event will also have + the following keys: + + :expected The form that was expected to be true + :actual A form representing what actually occurred + :message The string message given as an argument to 'is' + + The \"testing\" strings will be a list in \"*testing-contexts*\", and + the vars being tested will be a list in \"*testing-vars*\". + + Your \"report\" function should wrap any printing calls in the + \"with-test-out\" macro, which rebinds *out* to the current value + of *test-out*. + + For additional event types, see the examples in the code. +"} + cljs.test + (:require-macros + [clojure.template :as temp])) + +;; ============================================================================= +;; Protocols + +(defprotocol ITestReporter + (-do-report [_ env m])) + +;; ============================================================================= +;; Default Reporting + +(defn testing-vars-str + "Returns a string representation of the current test. Renders names + in *testing-vars* as a list, then the source file and line of + current assertion." + [env m] + (let [{:keys [file line]} m] + (str + (reverse (map #(:name (meta %)) (:testing-vars env))) + " (" file ":" line ")"))) + +(defn testing-contexts-str + "Returns a string representation of the current test context. Joins + strings in *testing-contexts* with spaces." + [env] + (apply str (interpose " " (reverse (:testing-contexts env))))) + +(defn inc-report-counter + "Increments the named counter in *report-counters*, a ref to a map. + Does nothing if *report-counters* is nil." + [env name] + (if (:report-counters env) + (update-in env [:report-counters name] (fnil inc 0)) + env)) + +(defmulti + ^{:doc "Generic reporting function, may be overridden to plug in + different report formats (e.g., TAP, JUnit). Assertions such as + 'is' call 'report' to indicate results. The argument given to + 'report' will be a map with a :type key." + :dynamic true} + report (fn [_ m] (:type m))) + +(defmethod report :default [env m] + (prn m)) + +(defmethod report :pass [env m] + (inc-report-counter env :pass)) + +(defmethod report :fail [env m] + (println "\nFAIL in" (testing-vars-str env m)) + (when (seq (:testing-contexts env)) + (println (testing-contexts-str env))) + (when-let [message (:message m)] (println message)) + (println "expected:" (pr-str (:expected m))) + (println " actual:" (pr-str (:actual m))) + (inc-report-counter env :fail)) + +(defmethod report :error [env m] + (println "\nERROR in" (testing-vars-str env m)) + (when (seq (:testing-contexts env)) + (println (testing-contexts-str env))) + (when-let [message (:message m)] (println message)) + (println "expected:" (pr-str (:expected m))) + (print " actual: ") (prn (:actual m)) + (inc-report-counter env :error)) + +(defmethod report :summary [env m] + (println "\nRan" (:test m) "tests containing" + (+ (:pass m) (:fail m) (:error m)) "assertions.") + (println (:fail m) "failures," (:error m) "errors.") + env) + +(defmethod report :begin-test-ns [env m] + (println "\nTesting" (name (:ns m))) + env) + +;; Ignore these message types: +(defmethod report :end-test-ns [env m]) +(defmethod report :begin-test-var [env m]) +(defmethod report :end-test-var [env m]) + +(deftype DefaultReporter [] + ITestReporter + (-do-report [_ env m] + (report env m))) + +(defn default-reporter [] + (DefaultReporter.)) + +(defn do-report [env m] + {:post [(map? %)]} + (-do-report (:reporter env) env m)) + +(defn empty-env + ([] (empty-env (default-reporter))) + ([reporter] + {:report-counters {:test 0 :pass 0 :fail 0 :error 0} + :testing-vars () + :testing-contexts () + :reporter reporter})) + +(def ^:dynamic *return* false) +(def ^:dynamic *env* (empty-env)) + +;; ============================================================================= +;; Low-level functions + +(defn test-var + "If v has a function in its :test metadata, calls that function, + add v to :testing-vars property of env." + ([v] + (test-var (empty-env) v)) + ([env v] + (let [t (:test (meta v)) + env (-> env + (update-in [:testing-vars] conj v) + (update-in [:report-counters :test] inc))] + (try + (t env) + (catch :default e + (let [ret (do-report env + {:type :error + :message "Uncaught exception, not in assertion." + :expected nil + :actual e})] + (when *return* + ret))))))) + +(defn- default-fixture + "The default, empty, fixture function. Just calls its argument." + [f] + (f)) + +(defn compose-fixtures + "Composes two fixture functions, creating a new fixture function + that combines their behavior." + [f1 f2] + (fn [g] (f1 (fn [] (f2 g))))) + +(defn join-fixtures + "Composes a collection of fixtures, in order. Always returns a valid + fixture function, even if the collection is empty." + [fixtures] + (reduce compose-fixtures default-fixture fixtures)) + +(defn test-vars + "Groups vars by their namespace and runs test-vars on them with + appropriate fixtures applied." + ([vars] (test-vars (empty-env) vars)) + ([env vars] + (reduce + (fn [env [ns vars]] + (let [once-fixture-fn (join-fixtures (:once-fixtures env)) + each-fixture-fn (join-fixtures (:each-fixtures env))] + (once-fixture-fn + (fn [] + (reduce + (fn [env v] + (if (:test (meta v)) + (each-fixture-fn (fn [] (test-var env v))) + env)) + env vars))))) + env (group-by (comp :ns meta) vars)))) + +;; ============================================================================= +;; Running Tests, high level functions + +(defn successful? + "Returns true if the given test summary indicates all tests + were successful, false otherwise." + [summary] + (and (zero? (:fail summary 0)) + (zero? (:error summary 0)))) From 3ffdef39e53f6d09897cc6ed3e29d44847d7654d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 7 Dec 2014 10:16:30 -0500 Subject: [PATCH 0232/4033] need to thread `&env` through `cljs.test/assert-expr` multimethods so that `cljs.test/function?` can properly query the compilation environment --- src/clj/cljs/test.clj | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 0275d5668..068280e44 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -13,24 +13,11 @@ ;; ============================================================================= ;; Utilities for assertions -(defn get-possibly-unbound-var - "Like var-get but returns nil if the var is unbound." - [v] - (try - (var-get v) - (catch IllegalStateException e - nil))) - (defn function? "Returns true if argument is a function or a symbol that resolves to a function (not a macro)." - [x] - (if (symbol? x) - (when-let [v (resolve x)] - (when-let [value (get-possibly-unbound-var v)] - (and (fn? value) - (not (:macro (meta v)))))) - (fn? x))) + [menv x] + (:fn-var (ana/resolve-var menv x))) (defn assert-predicate "Returns generic assertion code for any functional predicate. The @@ -74,23 +61,23 @@ ;; symbol in the test expression. (defmulti assert-expr - (fn [env msg form] + (fn [env menv msg form] (cond (nil? form) :always-fail (seq? form) (first form) :else :default))) -(defmethod assert-expr :always-fail [env msg form] +(defmethod assert-expr :always-fail [env menv msg form] ;; nil test: always fail `(cljs.test/do-report ~env {:type :fail, :message ~msg})) -(defmethod assert-expr :default [env msg form] +(defmethod assert-expr :default [env menv msg form] (if (and (sequential? form) - (function? (first form))) + (function? menv (first form))) (assert-predicate env msg form) (assert-any env msg form))) -(defmethod assert-expr 'instance? [env msg form] +(defmethod assert-expr 'instance? [env menv msg form] ;; Test if x is an instance of y. `(let [klass# ~(nth form 1) object# ~(nth form 2)] @@ -104,7 +91,7 @@ :expected '~form, :actual (class object#)})) result#))) -(defmethod assert-expr 'thrown? [env msg form] +(defmethod assert-expr 'thrown? [env menv msg form] ;; (is (thrown? c expr)) ;; Asserts that evaluating expr throws an exception of class c. ;; Returns the exception thrown. @@ -121,7 +108,7 @@ :expected '~form, :actual e#}) e#)))) -(defmethod assert-expr 'thrown-with-msg? [env msg form] +(defmethod assert-expr 'thrown-with-msg? [env menv msg form] ;; (is (thrown-with-msg? c re expr)) ;; Asserts that evaluating expr throws an exception of class c. ;; Also asserts that the message string of the exception matches @@ -148,7 +135,7 @@ You don't call this." [env msg form] `(try - ~(cljs.test/assert-expr env msg form) + ~(cljs.test/assert-expr env &env msg form) (catch :default t# (cljs.test/do-report ~env {:type :error, :message ~msg, From d3c8c7fa314d82470fff2238706b8458df94934a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 7 Dec 2014 16:50:52 -0500 Subject: [PATCH 0233/4033] fix `reduce` useage in test.cljs, pass `m` through in `cljs.test/do-report` if not `:fail` or `:error`. Add `cljs.test/file-and-line`. Validate arguments to `cljs.test/test-var`. --- src/clj/cljs/test.clj | 4 ++-- src/cljs/cljs/test.cljs | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 068280e44..7f321640d 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -167,7 +167,7 @@ but must occur inside a test function (deftest)." [env string & body] `(fn [env#] - (reduce #(%1 %2) + (reduce #(%2 %1) (update-in env# [:testing-contexts] conj ~string) [~@body]))) @@ -192,7 +192,7 @@ `(fn self# ([] (self# (cljs.test/empty-env))) ([env#] - (let [ret# (reduce #(%1 %2) env# [~@body])] + (let [ret# (reduce #(%2 %1) env# [~@body])] (when cljs.test/*return* ret#))))) (fn diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 98ce8539b..f9c0962f3 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -315,9 +315,21 @@ (defn default-reporter [] (DefaultReporter.)) +(defn file-and-line + [exception depth] + (if-let [stack (.-stack exception)] + ;; TODO: flesh out + {:file nil :line nil} + {:file (.-fileName exception) + :line (.-lineNumber exception)})) + (defn do-report [env m] {:post [(map? %)]} - (-do-report (:reporter env) env m)) + (let [m (case (:type m) + :fail (merge (file-and-line (js/Error.) 1) m) + :error (merge (file-and-line (:actual m) 0) m) + m)] + (-do-report (:reporter env) env m))) (defn empty-env ([] (empty-env (default-reporter))) @@ -339,20 +351,21 @@ ([v] (test-var (empty-env) v)) ([env v] + {:pre [(map? env) (instance? Var v)]} (let [t (:test (meta v)) env (-> env (update-in [:testing-vars] conj v) (update-in [:report-counters :test] inc))] (try - (t env) + (let [ret (t env)] + (when *return* ret)) (catch :default e (let [ret (do-report env {:type :error :message "Uncaught exception, not in assertion." :expected nil :actual e})] - (when *return* - ret))))))) + (when *return* ret))))))) (defn- default-fixture "The default, empty, fixture function. Just calls its argument." From 8892b0873b981728c03e3ea0d310e28a5236dcae Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 7 Dec 2014 17:27:32 -0500 Subject: [PATCH 0234/4033] add js-debugger interop macro, emits "debugger;" --- src/clj/cljs/core.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 5a48577da..696c76ad2 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -289,6 +289,9 @@ (defmacro js-in [key obj] (core/list 'js* "~{} in ~{}" key obj)) +(defmacro js-debugger [] + (core/list 'js* "debugger;")) + (defmacro true? [x] (bool-expr (core/list 'js* "~{} === true" x))) From f164702bef42b3c0594a52501c174724abb067ba Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 7 Dec 2014 17:56:30 -0500 Subject: [PATCH 0235/4033] return the testing environment instead of value of assertions if `:return` true. use magic local to emit different source in deftest context --- src/clj/cljs/test.clj | 97 ++++++++++++++++++++++------------------- src/cljs/cljs/test.cljs | 48 ++++++++++---------- 2 files changed, 77 insertions(+), 68 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 7f321640d..81da8fa41 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -29,29 +29,33 @@ (let [args (rest form) pred (first form)] `(let [values# (list ~@args) - result# (apply ~pred values#)] - (if result# - (cljs.test/do-report ~env - {:type :pass, :message ~msg, - :expected '~form, :actual (cons ~pred values#)}) - (cljs.test/do-report ~env - {:type :fail, :message ~msg, - :expected '~form, :actual (list '~'not (cons '~pred values#))})) - result#))) + result# (apply ~pred values#) + env'# (if result# + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual (cons ~pred values#)}) + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual (list '~'not (cons '~pred values#))}))] + (if (:return env'#) + env'# + result#)))) (defn assert-any "Returns generic assertion code for any test, including macros, Java method calls, or isolated symbols." [env msg form] - `(let [value# ~form] - (if value# - (cljs.test/do-report ~env - {:type :pass, :message ~msg, - :expected '~form, :actual value#}) - (cljs.test/do-report ~env - {:type :fail, :message ~msg, - :expected '~form, :actual value#})) - value#)) + `(let [value# ~form + env'# (if value# + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual value#}) + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual value#}))] + (if (:return env'#) + env'# + value#))) ;; ============================================================================= ;; Assertion Methods @@ -81,15 +85,17 @@ ;; Test if x is an instance of y. `(let [klass# ~(nth form 1) object# ~(nth form 2)] - (let [result# (instance? klass# object#)] - (if result# - (cljs.test/do-report ~env - {:type :pass, :message ~msg, - :expected '~form, :actual (class object#)}) - (cljs.test/do-report ~env - {:type :fail, :message ~msg, - :expected '~form, :actual (class object#)})) - result#))) + (let [result# (instance? klass# object#) + env'# (if result# + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual (class object#)}) + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual (class object#)}))] + (if (:return env'#) + env'# + result#)))) (defmethod assert-expr 'thrown? [env menv msg form] ;; (is (thrown? c expr)) @@ -105,8 +111,7 @@ (catch ~klass e# (cljs.test/do-report ~env {:type :pass, :message ~msg, - :expected '~form, :actual e#}) - e#)))) + :expected '~form, :actual e#}))))) (defmethod assert-expr 'thrown-with-msg? [env menv msg form] ;; (is (thrown-with-msg? c re expr)) @@ -120,15 +125,17 @@ ~@body (cljs.test/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) (catch ~klass e# - (let [m# (.getMessage e#)] - (if (re-find ~re m#) - (cljs.test/do-report ~env - {:type :pass, :message ~msg, - :expected '~form, :actual e#}) - (cljs.test/do-report ~env - {:type :fail, :message ~msg, - :expected '~form, :actual e#}))) - e#)))) + (let [m# (.getMessage e#) + env'# (if (re-find ~re m#) + (cljs.test/do-report ~env + {:type :pass, :message ~msg, + :expected '~form, :actual e#}) + (cljs.test/do-report ~env + {:type :fail, :message ~msg, + :expected '~form, :actual e#}))] + (if (:return env'#) + env'# + e#)))))) (defmacro try-expr "Used by the 'is' macro to catch unexpected exceptions. @@ -160,7 +167,9 @@ re-find) the regular expression re." ([form] `(cljs.test/is ~form nil)) ([form msg] - `(fn [env#] (cljs.test/try-expr env# ~msg ~form)))) + (if (contains? (:locals &env) 'cljs$lang$test$body) + `(fn [env#] (cljs.test/try-expr env# ~msg ~form)) + `(cljs.test/try-expr (cljs.test/empty-env) ~msg ~form)))) (defmacro testing "Adds a new string to the list of testing contexts. May be nested, @@ -192,22 +201,22 @@ `(fn self# ([] (self# (cljs.test/empty-env))) ([env#] - (let [ret# (reduce #(%2 %1) env# [~@body])] + (let [~'cljs$lang$test$body nil + ret# (reduce #(%2 %1) env# [~@body])] (when cljs.test/*return* ret#))))) (fn ([] (~name (cljs.test/empty-env))) ([env#] - (binding [cljs.test/*env* env#] - (cljs.test/test-var env# (.-cljs$lang$var ~name)))))) + (cljs.test/test-var env# (.-cljs$lang$var ~name))))) (set! (.-cljs$lang$var ~name) (var ~name))))) (defmacro test-all-vars "Calls test-vars on every var interned in the namespace, with fixtures." ([ns] - `(cljs.test/test-vars (vals (ns-interns ns)))) + `(cljs.test/test-vars (vals (ns-interns ~ns)))) ([env ns] - `(cljs.test/test-vars ~env (vals (ns-interns ns))))) + `(cljs.test/test-vars ~env (vals (ns-interns ~ns))))) (defmacro test-ns "If the namespace defines a function named test-ns-hook, calls that. diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index f9c0962f3..9d8fd7c0c 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -339,9 +339,6 @@ :testing-contexts () :reporter reporter})) -(def ^:dynamic *return* false) -(def ^:dynamic *env* (empty-env)) - ;; ============================================================================= ;; Low-level functions @@ -357,15 +354,15 @@ (update-in [:testing-vars] conj v) (update-in [:report-counters :test] inc))] (try - (let [ret (t env)] - (when *return* ret)) + (let [env' (t env)] + (when (:return env') env')) (catch :default e - (let [ret (do-report env - {:type :error - :message "Uncaught exception, not in assertion." - :expected nil - :actual e})] - (when *return* ret))))))) + (let [env' (do-report env + {:type :error + :message "Uncaught exception, not in assertion." + :expected nil + :actual e})] + (when (:return env') env'))))))) (defn- default-fixture "The default, empty, fixture function. Just calls its argument." @@ -389,19 +386,22 @@ appropriate fixtures applied." ([vars] (test-vars (empty-env) vars)) ([env vars] - (reduce - (fn [env [ns vars]] - (let [once-fixture-fn (join-fixtures (:once-fixtures env)) - each-fixture-fn (join-fixtures (:each-fixtures env))] - (once-fixture-fn - (fn [] - (reduce - (fn [env v] - (if (:test (meta v)) - (each-fixture-fn (fn [] (test-var env v))) - env)) - env vars))))) - env (group-by (comp :ns meta) vars)))) + (let [return (:return env) + env' (reduce + (fn [env [ns vars]] + (let [once-fixture-fn (join-fixtures (:once-fixtures env)) + each-fixture-fn (join-fixtures (:each-fixtures env))] + (once-fixture-fn + (fn [] + (reduce + (fn [env v] + (println "WTF" env v) + (if (:test (meta v)) + (each-fixture-fn (fn [] (test-var env v))) + env)) + env vars))))) + (assoc env :return true) (group-by (comp :ns meta) vars))] + (when return env')))) ;; ============================================================================= ;; Running Tests, high level functions From 5955224a69448337f4d6622ab84b493c2e68628e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 7 Dec 2014 22:34:43 -0500 Subject: [PATCH 0236/4033] deftest :test fn was not returning test environment --- src/clj/cljs/test.clj | 2 +- src/cljs/cljs/test.cljs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 81da8fa41..e4cf982a1 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -203,7 +203,7 @@ ([env#] (let [~'cljs$lang$test$body nil ret# (reduce #(%2 %1) env# [~@body])] - (when cljs.test/*return* + (when (:return env#) ret#))))) (fn ([] (~name (cljs.test/empty-env))) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 9d8fd7c0c..c3b3ec581 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -395,7 +395,6 @@ (fn [] (reduce (fn [env v] - (println "WTF" env v) (if (:test (meta v)) (each-fixture-fn (fn [] (test-var env v))) env)) From 74548746cd27f059da04433527cf372968744909 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 7 Dec 2014 22:55:22 -0500 Subject: [PATCH 0237/4033] fix `cljs.test/test-ns`, all cljs.test/report fns need to return the test environment --- src/clj/cljs/test.clj | 19 +++++++++---------- src/cljs/cljs/test.cljs | 6 +++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index e4cf982a1..e99fc879c 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -226,14 +226,13 @@ Internally binds *report-counters* to a ref initialized to *initial-report-counters*. Returns the final, dereferenced state of *report-counters*." - [[quote ns]] + [[quote ns :as form]] (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") - `(let [env# (cljs.test/empty-env)] - (-> env# - (cljs.test/do-report env# {:type :begin-test-ns, :ns ~'~ns}) - ;; If the namespace has a test-ns-hook function, call that: - ~(if-let [v (get-in @env/*compiler* [::ana/namespaces ns :defs 'test-ns-hook])] - `(~(symbol (name ns) "test-ns-hook")) - ;; Otherwise, just test every var in the namespace. - `(cljs.test/test-all-vars env# ns-obj)) - (cljs.test/do-report env# {:type :end-test-ns, :ns ~'~ns})))) + `(-> (assoc (cljs.test/empty-env) :return true) + (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) + ;; If the namespace has a test-ns-hook function, call that: + ~(if-let [v (get-in @env/*compiler* [::ana/namespaces ns :defs 'test-ns-hook])] + `(~(symbol (name ns) "test-ns-hook")) + ;; Otherwise, just test every var in the namespace. + `(cljs.test/test-all-vars ~form)) + (cljs.test/do-report {:type :end-test-ns, :ns ~form}))) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index c3b3ec581..e5a367873 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -303,9 +303,9 @@ env) ;; Ignore these message types: -(defmethod report :end-test-ns [env m]) -(defmethod report :begin-test-var [env m]) -(defmethod report :end-test-var [env m]) +(defmethod report :end-test-ns [env m] env) +(defmethod report :begin-test-var [env m] env) +(defmethod report :end-test-var [env m] env) (deftype DefaultReporter [] ITestReporter From 08b389388159c5b68f0b6582695eb88ef0fe5737 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 8 Dec 2014 06:42:53 -0500 Subject: [PATCH 0238/4033] Add ClojureScript implementations of cljs.source-map.base64.clj and cljs.source-map.base64-vlq.clj --- src/cljs/cljs/source_map/base64.cljs | 17 +++++ src/cljs/cljs/source_map/base64_vlq.cljs | 94 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/cljs/cljs/source_map/base64.cljs create mode 100644 src/cljs/cljs/source_map/base64_vlq.cljs diff --git a/src/cljs/cljs/source_map/base64.cljs b/src/cljs/cljs/source_map/base64.cljs new file mode 100644 index 000000000..888ba2d3f --- /dev/null +++ b/src/cljs/cljs/source_map/base64.cljs @@ -0,0 +1,17 @@ +(ns cljs.source-map.base64) + +(def chars64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") +(def char->int (zipmap chars64 (range 0 64))) +(def int->char (zipmap (range 0 64) chars64)) + +(defn encode [n] + (let [e (find int->char n)] + (if e + (second e) + (throw (js/Error. (str "Must be between 0 and 63: " n)))))) + +(defn decode [c] + (let [e (find char->int c)] + (if e + (second e) + (throw (js/Error. (str "Not a valid base 64 digit: " c)))))) diff --git a/src/cljs/cljs/source_map/base64_vlq.cljs b/src/cljs/cljs/source_map/base64_vlq.cljs new file mode 100644 index 000000000..b7b5bee33 --- /dev/null +++ b/src/cljs/cljs/source_map/base64_vlq.cljs @@ -0,0 +1,94 @@ +(ns cljs.source-map.base64-vlq + (:require [clojure.string :as string] + [cljs.source-map.base64 :as base64]) + (:import [goog.string StringBuffer])) + +(def ^:const vlq-base-shift 5) +(def ^:const vlq-base (bit-shift-left 1 vlq-base-shift)) +(def ^:const vlq-base-mask (dec vlq-base)) +(def ^:const vlq-continuation-bit vlq-base) + +(defn to-vlq-signed [v] + (if (neg? v) + (inc (bit-shift-left (- v) 1)) + (+ (bit-shift-left v 1) 0))) + +(defn from-vlq-signed [v] + (let [neg? (= (bit-and v 1) 1) + shifted (bit-shift-right v 1)] + (if neg? + (- shifted) + shifted))) + +(defn encode-val [n] + (let [sb (StringBuffer.) + vlq (to-vlq-signed n)] + (loop [digit (bit-and vlq vlq-base-mask) + vlq (bit-shift-right-zero-fill vlq vlq-base-shift)] + (if (pos? vlq) + (let [digit (bit-or digit vlq-continuation-bit)] + (.append sb (base64/encode digit)) + (recur (bit-and vlq vlq-base-mask) + (bit-shift-right-zero-fill vlq vlq-base-shift))) + (.append sb (base64/encode digit)))) + (str sb))) + +(defn encode [v] + (apply str (map encode-val v))) + +(defn decode [s] + (let [l (.-length s)] + (loop [i 0 result 0 shift 0] + (when (>= i l) + (throw (js/Error. "Expected more digits in base 64 VLQ value."))) + (let [digit (base64/decode (.charAt s i))] + (let [i (inc i) + continuation? (pos? (bit-and digit vlq-continuation-bit)) + digit (bit-and digit vlq-base-mask) + result (+ result (bit-shift-left digit shift)) + shift (+ shift vlq-base-shift)] + (if continuation? + (recur i result shift) + (lazy-seq + (cons (from-vlq-signed result) + (let [s (.substring s i)] + (when-not (string/blank? s) + (decode s))))))))))) + +(comment + ;; tests + + (bit-shift-right-zero-fill 127 1) ;; 63 + (bit-shift-right-zero-fill -127 1) ;; 2147483584 + + (to-vlq-signed 32) ;; 64 + (to-vlq-signed -32) ;; 65 + (from-vlq-signed 64) ;; 32 + (from-vlq-signed 65) ;; -32 + + ;; Base64 VLQ can only represent 32bit values + + (encode-val 32) ; "gC" + (decode "gC") ; {:value 32 :rest ""} + + (decode "AAgBC") ; (0 0 16 1) + + ;; lines kept count by semicolons, segments delimited by commas + ;; the above is gline 0, gcol 0, file 0, line 16, col 1, no name if this was the first segment read + + (decode "AAggBC") ; very clever way to encode large values + (decode "AAggBCA") ; 5 values instead of 4 + + (encode [0 0 16 1]) ; "AAgBC" + + (decode "IAWdD") ; (4 0 11 -14 -1) this is correct + ;; gline N, gcol +4, file +0, line +11, col -14, name -1 + + ;; Notes about format + ;; we always have 1, 4, or 5 values, all zero-based indexes + ;; 1. generated col - relative - reset on every new line in generated source + ;; 2. index into sources list - relative + ;; 3. original line - relative + ;; 4. origin column - relative + ;; 5. name - relative + ) From cdec9ebfccbcbaa457a8cfe5e38f732826385595 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 8 Dec 2014 06:53:02 -0500 Subject: [PATCH 0239/4033] Port decoding parts of cljs.source-map to ClojureScript --- src/cljs/cljs/source_map.cljs | 124 ++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/cljs/cljs/source_map.cljs diff --git a/src/cljs/cljs/source_map.cljs b/src/cljs/cljs/source_map.cljs new file mode 100644 index 000000000..6c447cfcd --- /dev/null +++ b/src/cljs/cljs/source_map.cljs @@ -0,0 +1,124 @@ +(ns cljs.source-map + (:require [clojure.string :as string] + [clojure.set :as set] + [cljs.source-map.base64-vlq :as base64-vlq])) + +;; ============================================================================= +;; All source map code in the file assumes the following in memory +;; representation of source map data. +;; +;; { file-name[String] +;; { line[Integer] +;; { col[Integer] +;; [{ :gline ..., :gcol ..., :name ...}] } } +;; +;; The outer level is a sorted map where the entries are file name and +;; sorted map of line information, the keys are strings. The line +;; information is represented as a sorted map of of column +;; information, the keys are integers. The column information is a +;; sorted map where the keys are integers and values are a vector of +;; maps - these maps have the keys :gline and :gcol for the generated +;; line and column. A :name key may be present if available. +;; +;; This representation simplifies merging ClojureScript source map +;; information with source map information generated by Google Closure +;; Compiler optimization. We can now trivially create the merged map +;; by using :gline and :gcol in the ClojureScript source map data to +;; extract final :gline and :gcol from the Google Closure source map. + +;; ----------------------------------------------------------------------------- +;; Utilities + +(defn indexed-sources + "Take a seq of source file names and return a map from + file number to integer index." + [sources] + (->> sources + (map-indexed (fn [a b] [a b])) + (reduce (fn [m [i v]] (assoc m v i)) {}))) + +(defn source-compare + "Take a seq of source file names and return a comparator + that can be used to construct a sorted map." + [sources] + (let [sources (indexed-sources sources)] + (fn [a b] (compare (sources a) (sources b))))) + +;; ----------------------------------------------------------------------------- +;; Decoding + +(defn seg->map + "Take a source map segment represented as a vector + and return a map." + [seg source-map] + (let [[gcol source line col name] seg] + {:gcol gcol + :source (aget source-map "sources" source) + :line line + :col col + :name (when-let [name (-> seg meta :name)] + (aget source-map "names" name))})) + +(defn seg-combine + "Combine a source map segment vector and a relative + source map segment vector and combine them to get + an absolute segment posititon information as a vector." + [seg relseg] + (let [[gcol source line col name] seg + [rgcol rsource rline rcol rname] relseg + nseg [(+ gcol rgcol) + (+ (or source 0) rsource) + (+ (or line 0) rline) + (+ (or col 0) rcol) + (+ (or name 0) rname)]] + (if name + (with-meta nseg {:name (+ name rname)}) + nseg))) + +(defn update-result + "Helper for decode. Take an internal source map representation + organized as nested sorted maps mapping file, line, and column + and update it based on a segment map and generated line number." + [result segmap gline] + (let [{:keys [gcol source line col name]} segmap + d {:gline gline + :gcol gcol} + d (if name (assoc d :name name) d)] + (update-in result [source] + (fnil (fn [m] + (update-in m [line] + (fnil (fn [m] + (update-in m [col] + (fnil (fn [v] (conj v d)) + []))) + (sorted-map)))) + (sorted-map))))) + +(defn decode + "Convert a v3 source map JSON object into a nested sorted map + organized as file, line, and column." + ([source-map] + (decode (aget source-map "mappings") source-map)) + ([mappings source-map] + (let [sources (aget source-map "sources") + relseg-init [0 0 0 0 0] + lines (seq (string/split mappings #";"))] + (loop [gline 0 + lines lines + relseg relseg-init + result (sorted-map-by (source-compare sources))] + (if lines + (let [line (first lines) + [result relseg] + (if (string/blank? line) + [result relseg] + (let [segs (seq (string/split line #","))] + (loop [segs segs relseg relseg result result] + (if segs + (let [seg (first segs) + nrelseg (seg-combine (base64-vlq/decode seg) relseg)] + (recur (next segs) nrelseg + (update-result result (seg->map nrelseg source-map) gline))) + [result relseg]))))] + (recur (inc gline) (next lines) (assoc relseg 0 0) result)) + result))))) From 2468794667a9be000bf2a3f1018dcc837cbc5e3b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 8 Dec 2014 06:55:18 -0500 Subject: [PATCH 0240/4033] some missing notices --- src/cljs/cljs/nodejs.cljs | 8 ++++++++ src/cljs/cljs/nodejscli.cljs | 8 ++++++++ src/cljs/cljs/source_map.cljs | 8 ++++++++ src/cljs/cljs/source_map/base64.cljs | 8 ++++++++ src/cljs/cljs/source_map/base64_vlq.cljs | 8 ++++++++ 5 files changed, 40 insertions(+) diff --git a/src/cljs/cljs/nodejs.cljs b/src/cljs/cljs/nodejs.cljs index 01f131882..60bc7ff49 100644 --- a/src/cljs/cljs/nodejs.cljs +++ b/src/cljs/cljs/nodejs.cljs @@ -1,3 +1,11 @@ +; 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. + ; Projects compiled with :target :nodejs can 'require' this namespace ; to get the nodejs globals loaded into cljs.nodejs and get ; ClojureScript's 'print' set up correctly. diff --git a/src/cljs/cljs/nodejscli.cljs b/src/cljs/cljs/nodejscli.cljs index 20acc8d89..d4984651b 100644 --- a/src/cljs/cljs/nodejscli.cljs +++ b/src/cljs/cljs/nodejscli.cljs @@ -1,3 +1,11 @@ +; 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. + ; Projects compiled with :target :nodejs have this file appended. Its ; job is to make sure cljs.nodejs is loaded and that the *main-cli-fn* ; is called with the script's command-line arguments. diff --git a/src/cljs/cljs/source_map.cljs b/src/cljs/cljs/source_map.cljs index 6c447cfcd..73bd8dccc 100644 --- a/src/cljs/cljs/source_map.cljs +++ b/src/cljs/cljs/source_map.cljs @@ -1,3 +1,11 @@ +; 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.source-map (:require [clojure.string :as string] [clojure.set :as set] diff --git a/src/cljs/cljs/source_map/base64.cljs b/src/cljs/cljs/source_map/base64.cljs index 888ba2d3f..c5dfc2b1e 100644 --- a/src/cljs/cljs/source_map/base64.cljs +++ b/src/cljs/cljs/source_map/base64.cljs @@ -1,3 +1,11 @@ +; 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.source-map.base64) (def chars64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") diff --git a/src/cljs/cljs/source_map/base64_vlq.cljs b/src/cljs/cljs/source_map/base64_vlq.cljs index b7b5bee33..ff4d02b24 100644 --- a/src/cljs/cljs/source_map/base64_vlq.cljs +++ b/src/cljs/cljs/source_map/base64_vlq.cljs @@ -1,3 +1,11 @@ +; 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.source-map.base64-vlq (:require [clojure.string :as string] [cljs.source-map.base64 :as base64]) From 9b355fd113f477c6c75b31a4279230c2bdc323e8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 8 Dec 2014 07:27:03 -0500 Subject: [PATCH 0241/4033] add proper `:return` handling to `cljs.test/test-ns` --- src/clj/cljs/test.clj | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index e99fc879c..46bf19b55 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -226,13 +226,17 @@ Internally binds *report-counters* to a ref initialized to *initial-report-counters*. Returns the final, dereferenced state of *report-counters*." - [[quote ns :as form]] - (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") - `(-> (assoc (cljs.test/empty-env) :return true) - (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) - ;; If the namespace has a test-ns-hook function, call that: - ~(if-let [v (get-in @env/*compiler* [::ana/namespaces ns :defs 'test-ns-hook])] - `(~(symbol (name ns) "test-ns-hook")) - ;; Otherwise, just test every var in the namespace. - `(cljs.test/test-all-vars ~form)) - (cljs.test/do-report {:type :end-test-ns, :ns ~form}))) + ([ns] `(cljs.test/test-ns (cljs.test/empty-env) ~ns)) + ([env [quote ns :as form]] + (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") + `(let [env# ~env + return# (:return env#) + env'# (-> (assoc env# :return true) + (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) + ;; If the namespace has a test-ns-hook function, call that: + ~(if-let [v (get-in @env/*compiler* [::ana/namespaces ns :defs 'test-ns-hook])] + `(~(symbol (name ns) "test-ns-hook")) + ;; Otherwise, just test every var in the namespace. + `(cljs.test/test-all-vars ~form)) + (cljs.test/do-report {:type :end-test-ns, :ns ~form}))] + (when return# env'#)))) From 34f6924085f0e548b39b3170aef087547e77484d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 8 Dec 2014 08:05:18 -0500 Subject: [PATCH 0242/4033] remove source timestamp from `cljs.source-map/seg->map`. rename `cljs.source-map/decode` to `cljs.source-map/decode-reverse` clarifying what it does. --- src/cljs/cljs/source_map.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/source_map.cljs b/src/cljs/cljs/source_map.cljs index 73bd8dccc..feb7ba42a 100644 --- a/src/cljs/cljs/source_map.cljs +++ b/src/cljs/cljs/source_map.cljs @@ -61,7 +61,7 @@ [seg source-map] (let [[gcol source line col name] seg] {:gcol gcol - :source (aget source-map "sources" source) + :source (aget (.split (aget source-map "sources" source) "?") 0) :line line :col col :name (when-let [name (-> seg meta :name)] @@ -102,9 +102,10 @@ (sorted-map)))) (sorted-map))))) -(defn decode +(defn decode-reverse "Convert a v3 source map JSON object into a nested sorted map - organized as file, line, and column." + organized as file, line, and column that maps ClojureScript + source locations to the generated JavaScript." ([source-map] (decode (aget source-map "mappings") source-map)) ([mappings source-map] From 7446e997d21ec41657ef921808bceb1798ee453a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 8 Dec 2014 08:33:10 -0500 Subject: [PATCH 0243/4033] clean up cljs.source-map port, update comments, docstrings and provide a proper `cljs.source-map/decode` for client side use --- src/cljs/cljs/source_map.cljs | 87 ++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/src/cljs/cljs/source_map.cljs b/src/cljs/cljs/source_map.cljs index feb7ba42a..9ca2272e9 100644 --- a/src/cljs/cljs/source_map.cljs +++ b/src/cljs/cljs/source_map.cljs @@ -12,34 +12,27 @@ [cljs.source-map.base64-vlq :as base64-vlq])) ;; ============================================================================= -;; All source map code in the file assumes the following in memory +;; Source map code in the file assumes the following in memory +;; representation of source map data. +;; +;; { gline[Integer] +;; { gcol[Integer] +;; [{ :line ..., :col ..., :name ..., :source ... }] } } +;; +;; Reverse source map code in the file assumes the following in memory ;; representation of source map data. ;; ;; { file-name[String] ;; { line[Integer] ;; { col[Integer] -;; [{ :gline ..., :gcol ..., :name ...}] } } -;; -;; The outer level is a sorted map where the entries are file name and -;; sorted map of line information, the keys are strings. The line -;; information is represented as a sorted map of of column -;; information, the keys are integers. The column information is a -;; sorted map where the keys are integers and values are a vector of -;; maps - these maps have the keys :gline and :gcol for the generated -;; line and column. A :name key may be present if available. -;; -;; This representation simplifies merging ClojureScript source map -;; information with source map information generated by Google Closure -;; Compiler optimization. We can now trivially create the merged map -;; by using :gline and :gcol in the ClojureScript source map data to -;; extract final :gline and :gcol from the Google Closure source map. +;; [{ :gline ..., :gcol ..., :name ... }] } } ;; ----------------------------------------------------------------------------- ;; Utilities (defn indexed-sources "Take a seq of source file names and return a map from - file number to integer index." + file number to integer index. For reverse source maps." [sources] (->> sources (map-indexed (fn [a b] [a b])) @@ -47,7 +40,8 @@ (defn source-compare "Take a seq of source file names and return a comparator - that can be used to construct a sorted map." + that can be used to construct a sorted map. For reverse + source maps." [sources] (let [sources (indexed-sources sources)] (fn [a b] (compare (sources a) (sources b))))) @@ -83,10 +77,9 @@ (with-meta nseg {:name (+ name rname)}) nseg))) -(defn update-result - "Helper for decode. Take an internal source map representation - organized as nested sorted maps mapping file, line, and column - and update it based on a segment map and generated line number." +(defn update-reverse-result + "Helper for decode-reverse. Take a reverse source map and + update it with a segment map." [result segmap gline] (let [{:keys [gcol source line col name]} segmap d {:gline gline @@ -103,11 +96,11 @@ (sorted-map))))) (defn decode-reverse - "Convert a v3 source map JSON object into a nested sorted map - organized as file, line, and column that maps ClojureScript - source locations to the generated JavaScript." + "Convert a v3 source map JSON object into a reverse source map + mapping original ClojureScript source locations to the generated + JavaScript." ([source-map] - (decode (aget source-map "mappings") source-map)) + (decode-reverse (aget source-map "mappings") source-map)) ([mappings source-map] (let [sources (aget source-map "sources") relseg-init [0 0 0 0 0] @@ -116,6 +109,48 @@ lines lines relseg relseg-init result (sorted-map-by (source-compare sources))] + (if lines + (let [line (first lines) + [result relseg] + (if (string/blank? line) + [result relseg] + (let [segs (seq (string/split line #","))] + (loop [segs segs relseg relseg result result] + (if segs + (let [seg (first segs) + nrelseg (seg-combine (base64-vlq/decode seg) relseg)] + (recur (next segs) nrelseg + (update-reverse-result result (seg->map nrelseg source-map) gline))) + [result relseg]))))] + (recur (inc gline) (next lines) (assoc relseg 0 0) result)) + result))))) + +(defn update-result + "Helper for decode. Take a source map and update it based on a + segment map." + [result segmap gline] + (let [{:keys [gcol source line col name]} segmap + d {:line col + :col line + :source source} + d (if name (assoc d :name name) d)] + (update-in result [gline] + (fnil (fn [m] + (update-in m [gcol] + (fnil #(conj % d) []))) + (sorted-map))))) + +(defn decode + "Convert a v3 source map JSON object into a source map mapping + generated JavaScript source locations to the original + ClojureScript." + ([source-map] + (decode (aget source-map "mappings") source-map)) + ([mappings source-map] + (let [sources (aget source-map "sources") + relseg-init [0 0 0 0 0] + lines (seq (string/split mappings #";"))] + (loop [gline 0 lines lines relseg relseg-init result {}] (if lines (let [line (first lines) [result relseg] From 7bbff7da7cdd5ae7f1290be0bc35602782a0c337 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 8 Dec 2014 09:59:45 -0500 Subject: [PATCH 0244/4033] 2371 -> 2411 in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 299346f7d..39678bc9c 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2371 +Latest stable release: 0.0-2411 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2371"] +[org.clojure/clojurescript "0.0-2411"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2371 org.clojure clojurescript - 0.0-2371 + 0.0-2411 ``` From 94eb8a960fef6aaca4ba44b251cefbfa04d0f6ac Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 8 Dec 2014 19:36:23 -0500 Subject: [PATCH 0245/4033] another tweak for CLJS-890 --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index a74f03189..63049fd2b 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2135,7 +2135,7 @@ reduces them without incurring seq initialization" ([] "") ([x] (if (nil? x) "" - (cljs.core/js-str x))) + (gstring/buildString x))) ([x & ys] (loop [sb (StringBuffer. (str x)) more ys] (if more From 9fcb405a48ccff629d0c4f15a123ee84d134f63d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 11 Dec 2014 08:47:44 -0500 Subject: [PATCH 0246/4033] expose fine grained control of analyzer warnings in `cljs.closure/build` --- src/clj/cljs/closure.clj | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 71d3e17bc..bd0d4338e 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -969,12 +969,16 @@ should contain the source for the given namespace name." *assert* (not= (:elide-asserts opts) true) ana/*load-tests* (not= (:load-tests opts) false) ana/*cljs-warnings* - (let [enabled? (true? (opts :warnings true))] - (merge ana/*cljs-warnings* - {:unprovided enabled? - :undeclared-var enabled? - :undeclared-ns enabled? - :undeclared-ns-form enabled?}))] + (let [warnings (opts :warnings true)] + (merge + ana/*cljs-warnings* + (if (or (true? warnings) + (false? warnings)) + (zipmap + [:unprovided :undeclared-var + :undeclared-ns :undeclared-ns-form] + (repeat warnings)) + warnings)))] (let [compiled (-compile source all-opts) ; the constants_table.js file is not used directly here, is picked up by From a770b535cc38fc29b72dcda606686f384721e4ad Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 11 Dec 2014 17:56:09 -0500 Subject: [PATCH 0247/4033] add pprint files, copy over some initial code --- src/cljs/cljs/pprint.clj | 19 ++++++ src/cljs/cljs/pprint.cljs | 120 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 src/cljs/cljs/pprint.clj create mode 100644 src/cljs/cljs/pprint.cljs diff --git a/src/cljs/cljs/pprint.clj b/src/cljs/cljs/pprint.clj new file mode 100644 index 000000000..6559466fb --- /dev/null +++ b/src/cljs/cljs/pprint.clj @@ -0,0 +1,19 @@ +;;; pprint.clj -- Pretty printer and Common Lisp compatible format function (cl-format) for Clojure + +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; 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. + +;; Author: Tom Faulhaber +;; April 3, 2009 + +(ns cljs.pprint) + +(defmacro ^{:private true} prlabel [prefix arg & more-args] + "Print args to *err* in name = value format" + `(prerr ~@(cons (list 'quote prefix) (mapcat #(list (list 'quote %) "=" %) + (cons arg (seq more-args)))))) diff --git a/src/cljs/cljs/pprint.cljs b/src/cljs/cljs/pprint.cljs new file mode 100644 index 000000000..e0c520b6d --- /dev/null +++ b/src/cljs/cljs/pprint.cljs @@ -0,0 +1,120 @@ +;;; pprint.cljs -- Pretty printer and Common Lisp compatible format function (cl-format) for Clojure + +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; 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. + +;; Author: Tom Faulhaber +;; April 3, 2009 + +(ns + ^{:author "Tom Faulhaber", + :doc "A Pretty Printer for ClojureScript + +cljs.pprint implements a flexible system for printing structured data +in a pleasing, easy-to-understand format. Basic use of the pretty printer is +simple, just call pprint instead of println. More advanced users can use +the building blocks provided to create custom output formats. + +Out of the box, pprint supports a simple structured format for basic data +and a specialized format for Clojure source code. More advanced formats, +including formats that don't look like Clojure data at all like XML and +JSON, can be rendered by creating custom dispatch functions. + +In addition to the pprint function, this module contains cl-format, a text +formatting function which is fully compatible with the format function in +Common Lisp. Because pretty printing directives are directly integrated with +cl-format, it supports very concise custom dispatch. It also provides +a more powerful alternative to Clojure's standard format function. + +See documentation for pprint and cl-format for more information or +complete documentation on the the clojure web site on github."} + cljs.pprint + (:require [clojure.walk :only [walk]])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Helper functions for digesting formats in the various +;;; phases of their lives. +;;; These functions are actually pretty general. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- map-passing-context [func initial-context lis] + (loop [context initial-context + lis lis + acc []] + (if (empty? lis) + [acc context] + (let [this (first lis) + remainder (next lis) + [result new-context] (apply func [this context])] + (recur new-context remainder (conj acc result)))))) + +(defn- consume [func initial-context] + (loop [context initial-context + acc []] + (let [[result new-context] (apply func [context])] + (if (not result) + [acc new-context] + (recur new-context (conj acc result)))))) + +(defn- consume-while [func initial-context] + (loop [context initial-context + acc []] + (let [[result continue new-context] (apply func [context])] + (if (not continue) + [acc context] + (recur new-context (conj acc result)))))) + +(defn- unzip-map [m] + "Take a map that has pairs in the value slots and produce a pair of maps, + the first having all the first elements of the pairs and the second all + the second elements of the pairs" + [(into {} (for [[k [v1 v2]] m] [k v1])) + (into {} (for [[k [v1 v2]] m] [k v2]))]) + +(defn- tuple-map [m v1] + "For all the values, v, in the map, replace them with [v v1]" + (into {} (for [[k v] m] [k [v v1]]))) + +(defn- rtrim [s c] + "Trim all instances of c from the end of sequence s" + (let [len (count s)] + (if (and (pos? len) (= (nth s (dec (count s))) c)) + (loop [n (dec len)] + (cond + (neg? n) "" + (not (= (nth s n) c)) (subs s 0 (inc n)) + true (recur (dec n)))) + s))) + +(defn- ltrim [s c] + "Trim all instances of c from the beginning of sequence s" + (let [len (count s)] + (if (and (pos? len) (= (nth s 0) c)) + (loop [n 0] + (if (or (= n len) (not (= (nth s n) c))) + (subs s n) + (recur (inc n)))) + s))) + +(defn- prefix-count [aseq val] + "Return the number of times that val occurs at the start of sequence aseq, +if val is a seq itself, count the number of times any element of val occurs at the +beginning of aseq" + (let [test (if (coll? val) (set val) #{val})] + (loop [pos 0] + (if (or (= pos (count aseq)) (not (test (nth aseq pos)))) + pos + (recur (inc pos)))))) + +(defn- prerr [& args] + "Println to *err*" + ;; there is no *err* yet + (apply println args)) + +;; Flush the pretty-print buffer without flushing the underlying stream +(defprotocol PrettyFlush (ppflush [this])) From ff325afce38833355d3b1b30f941b48f53959178 Mon Sep 17 00:00:00 2001 From: Daniel Skarda Date: Fri, 12 Dec 2014 14:04:44 +0100 Subject: [PATCH 0248/4033] Implement volatile! from Clojure 1.7 --- src/clj/cljs/core.clj | 8 +++++++- src/cljs/cljs/core.cljs | 31 +++++++++++++++++++++++++++++++ test/cljs/cljs/core_test.cljs | 6 ++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 696c76ad2..0104c35c5 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -34,7 +34,7 @@ cond-> cond->> as-> some-> some->> - if-some when-some test ns-interns var]) + if-some when-some test ns-interns var vswap!]) (:require clojure.walk clojure.set cljs.compiler @@ -1681,3 +1681,9 @@ (fn [[sym _]] `[(symbol ~(name sym)) (var ~sym)]) (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))])) +(defmacro vswap! + "Non-atomically swaps the value of the volatile as if: + (apply f current-value-of-vol args). Returns the value that + was swapped in." + [vol f & args] + `(-vreset! ~vol (~f (-deref ~vol) ~@args))) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 63049fd2b..372697d57 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -402,6 +402,9 @@ (defprotocol ISwap (-swap! [o f] [o f a] [o f a b] [o f a b xs])) +(defprotocol IVolatile + (-vreset! [o new-value])) + (defprotocol IIterable (-iterator [coll])) @@ -3497,6 +3500,28 @@ reduces them without incurring seq initialization" [iref] (.-validator iref)) +(deftype Volatile [^:mutable state] + IVolatile + (-vreset! [_ new-state] + (set! state new-state)) + + IDeref + (-deref [_] state)) + +(defn volatile! + "Creates and returns a Volatile with an initial value of val." + [val] + (Volatile. val)) + +(defn volatile? + "Returns true if x is a volatile." + [x] (instance? Volatile x)) + +(defn vreset! + "Sets the value of volatile to newval without regard for the + current value. Returns newval." + [vol newval] (-vreset! vol newval)) + (defn keep-indexed "Returns a lazy sequence of the non-nil results of (f index item). Note, this means false return values will be included. f must be free of @@ -8265,6 +8290,12 @@ reduces them without incurring seq initialization" (pr-writer (.-state a) writer opts) (-write writer ">")) + Volatile + (-pr-writer [a writer opts] + (-write writer "#")) + Var (-pr-writer [a writer opts] (-write writer "#'") diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 5e2c98552..dc65f36f7 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -447,6 +447,12 @@ (assert (= {:a 1} (meta a))) (alter-meta! a assoc :b 2) (assert (= {:a 1 :b 2} (meta a)))) + (let [v (volatile! 1)] + (assert (volatile? v)) + (assert (not (volatile? (atom 1)))) + (assert (= 2 (vreset! v 2))) + (assert (= 3 (vswap! v inc))) + (assert (= 3 @v))) (assert (nil? (empty nil))) (let [e-lazy-seq (empty (with-meta (lazy-seq (cons :a nil)) {:b :c}))] (assert (seq? e-lazy-seq)) From 6679e2bc96701460761ea8b49b6e6dfdf5a9ddfa Mon Sep 17 00:00:00 2001 From: Daniel Skarda Date: Fri, 12 Dec 2014 14:13:13 +0100 Subject: [PATCH 0249/4033] Use volatile! instead of atom in transducers --- src/cljs/cljs/core.cljs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 372697d57..4496d96c6 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -3529,12 +3529,12 @@ reduces them without incurring seq initialization" provided." ([f] (fn [rf] - (let [ia (atom -1)] + (let [ia (volatile! -1)] (fn ([] (rf)) ([result] (rf result)) ([result input] - (let [i (swap! ia inc) + (let [i (vswap! ia inc) v (f i input)] (if (nil? v) result @@ -3689,13 +3689,13 @@ reduces them without incurring seq initialization" no collection is provided." ([n] (fn [rf] - (let [na (atom n)] + (let [na (volatile! n)] (fn ([] (rf)) ([result] (rf result)) ([result input] (let [n @na - nn (swap! na dec) + nn (vswap! na dec) result (if (pos? n) (rf result input) result)] @@ -3713,13 +3713,13 @@ reduces them without incurring seq initialization" Returns a stateful transducer when no collection is provided." ([n] (fn [rf] - (let [na (atom n)] + (let [na (volatile! n)] (fn ([] (rf)) ([result] (rf result)) ([result input] (let [n @na] - (swap! na dec) + (vswap! na dec) (if (pos? n) result (rf result input)))))))) @@ -3751,7 +3751,7 @@ reduces them without incurring seq initialization" stateful transducer when no collection is provided." ([pred] (fn [rf] - (let [da (atom true)] + (let [da (volatile! true)] (fn ([] (rf)) ([result] (rf result)) @@ -3760,7 +3760,7 @@ reduces them without incurring seq initialization" (if (and drop? (pred input)) result (do - (reset! da nil) + (vreset! da nil) (rf result input))))))))) ([pred coll] (let [step (fn [pred coll] @@ -7776,12 +7776,12 @@ reduces them without incurring seq initialization" transducer when no collection is provided." ([n] (fn [rf] - (let [ia (atom -1)] + (let [ia (volatile! -1)] (fn ([] (rf)) ([result] (rf result)) ([result input] - (let [i (swap! ia inc)] + (let [i (vswap! ia inc)] (if (zero? (rem i n)) (rf result input) result))))))) @@ -7802,7 +7802,7 @@ reduces them without incurring seq initialization" ([f] (fn [rf] (let [a (array-list) - pa (atom ::none)] + pa (volatile! ::none)] (fn ([] (rf)) ([result] @@ -7816,7 +7816,7 @@ reduces them without incurring seq initialization" ([result input] (let [pval @pa val (f input)] - (reset! pa val) + (vreset! pa val) (if (or (keyword-identical? pval ::none) (= val pval)) (do @@ -8436,13 +8436,13 @@ reduces them without incurring seq initialization" Returns a transducer when no collection is provided." ([] (fn [rf] - (let [pa (atom ::none)] + (let [pa (volatile! ::none)] (fn ([] (rf)) ([result] (rf result)) ([result input] (let [prior @pa] - (reset! pa input) + (vreset! pa input) (if (= prior input) result (rf result input)))))))) From f40a9cf49e8cd3ad016514df5d9aae9df995c801 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Sun, 30 Nov 2014 00:22:16 +0600 Subject: [PATCH 0250/4033] CLJS-892: Improve performance of compare-symbols/compare-keywords --- benchmark/cljs/benchmark_runner.cljs | 12 ++++++++++++ src/cljs/cljs/core.cljs | 25 ++++++++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index abba2ccbf..c4fecbb93 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -111,6 +111,18 @@ (= a b) 1) (println) +(println ";;; keyword compare") +(let [seed ["amelia" "olivia" "jessica" "emily" "lily" "ava" "isla" "sophie" "mia" "isabella" "evie" "poppy" "ruby" "grace" "sophia" "chloe" "freya" "isabelle" "ella" "charlotte" "scarlett" "daisy" "lola" "holly" "eva" "lucy" "millie" "phoebe" "layla" "maisie" "sienna" "alice" "florence" "lilly" "ellie" "erin" "elizabeth" "imogen" "summer" "molly" "hannah" "sofia" "abigail" "jasmine" "matilda" "megan" "rosie" "lexi" "lacey" "emma" "amelie" "maya" "gracie" "emilia" "georgia" "hollie" "evelyn" "eliza" "amber" "eleanor" "bella" "amy" "brooke" "leah" "esme" "harriet" "anna" "katie" "zara" "willow" "elsie" "annabelle" "bethany" "faith" "madison" "isabel" "rose" "julia" "martha" "maryam" "paige" "heidi" "maddison" "niamh" "skye" "aisha" "mollie" "ivy" "francesca" "darcey" "maria" "zoe" "keira" "sarah" "tilly" "isobel" "violet" "lydia" "sara" "caitlin"]] + (simple-benchmark + [arr (into-array (repeatedly 10000 #(keyword (rand-nth seed))))] + (.sort arr compare) + 100) + (simple-benchmark + [arr (into-array (repeatedly 10000 #(keyword (rand-nth seed) (rand-nth seed))))] + (.sort arr compare) + 100)) +(println) + (println ";;; reduce lazy-seqs, vectors, ranges") (simple-benchmark [coll (take 100000 (iterate inc 0))] (reduce + 0 coll) 1) (simple-benchmark [coll (range 1000000)] (reduce + 0 coll) 1) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 4496d96c6..97cc5c6b9 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -566,15 +566,15 @@ (defn- compare-symbols [a b] (cond - (= a b) 0 + (identical? (.-str a) (.-str b)) 0 (and (not (.-ns a)) (.-ns b)) -1 (.-ns a) (if-not (.-ns b) 1 - (let [nsc (compare (.-ns a) (.-ns b))] - (if (zero? nsc) - (compare (.-name a) (.-name b)) + (let [nsc (garray/defaultCompare (.-ns a) (.-ns b))] + (if (== 0 nsc) + (garray/defaultCompare (.-name a) (.-name b)) nsc))) - :default (compare (.-name a) (.-name b)))) + :default (garray/defaultCompare (.-name a) (.-name b)))) (deftype Symbol [ns name str ^:mutable _hash _meta] Object @@ -2419,6 +2419,18 @@ reduces them without incurring seq initialization" (defn hash-keyword [k] (int (+ (hash-symbol k) 0x9e3779b9))) +(defn- compare-keywords [a b] + (cond + (identical? (.-fqn a) (.-fqn b)) 0 + (and (not (.-ns a)) (.-ns b)) -1 + (.-ns a) (if-not (.-ns b) + 1 + (let [nsc (garray/defaultCompare (.-ns a) (.-ns b))] + (if (== 0 nsc) + (garray/defaultCompare (.-name a) (.-name b)) + nsc))) + :default (garray/defaultCompare (.-name a) (.-name b)))) + (deftype Keyword [ns name fqn ^:mutable _hash] Object (toString [_] (str ":" fqn)) @@ -8307,8 +8319,7 @@ reduces them without incurring seq initialization" (-compare [x y] (compare-symbols x y)) Keyword - ; keyword happens to have the same fields as Symbol, so this just works - (-compare [x y] (compare-symbols x y)) + (-compare [x y] (compare-keywords x y)) Subvec (-compare [x y] (compare-indexed x y)) From 2596302909a6e95fde8c1eaed87e42939872e705 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 10:18:03 -0500 Subject: [PATCH 0251/4033] first cut of `cljs.test/run-tests` --- src/clj/cljs/test.clj | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 46bf19b55..348e937b2 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -211,6 +211,29 @@ (cljs.test/test-var env# (.-cljs$lang$var ~name))))) (set! (.-cljs$lang$var ~name) (var ~name))))) +;; ============================================================================= +;; Running Tests + +(defmacro run-tests + "Runs all tests in the given namespaces; prints results. + Defaults to current namespace if none given. Returns a map + summarizing test results." + ([] `(run-tests (cljs.test/empty-env) '~ana/*cljs-ns*)) + ([env & namespaces] + (assert (every? + (fn [[quote ns]] (and (= quote 'quote) (symbol? ns))) + namespaces) + "All arguments to run-tests must be quoted symbols") + `(let [summary# (apply merge-with + + [~@(map + (fn [ns] + `(cljs.test/test-ns + (assoc ~env :return true) ~ns)) + namespaces)])] + (do-report summary# + (assoc (:report-counters summary#) :type :summary)) + summary#))) + (defmacro test-all-vars "Calls test-vars on every var interned in the namespace, with fixtures." ([ns] From 777e423717ece7b4a95443454ec55983ca7f1643 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 11:26:50 -0500 Subject: [PATCH 0252/4033] add `cljs.analyzer.api` namespace --- src/clj/cljs/analyzer/api.clj | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/clj/cljs/analyzer/api.clj diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj new file mode 100644 index 000000000..747e18aa6 --- /dev/null +++ b/src/clj/cljs/analyzer/api.clj @@ -0,0 +1,21 @@ +; 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.api + (:refer-clojure :exclude [all-ns ns-interns ns-resolve]) + (:require [cljs.env :as env] + [cljs.analyzer :as ana])) + +(defn all-ns [] + (keys (get @env/*compiler* ::ana/namespaces))) + +(defn ns-interns [ns] + (get-in @env/*compiler* [::ana/namespaces ns :defs])) + +(defn ns-resolve [ns sym] + (get-in @env/*compiler* [::ana/namespaces ns :defs sym])) From d57e1d1fe423fde1466e2636a64b572c6722a5e4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 11:31:17 -0500 Subject: [PATCH 0253/4033] missing notice --- src/clj/cljs/analyzer/utils.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/clj/cljs/analyzer/utils.clj b/src/clj/cljs/analyzer/utils.clj index ce266b4f7..6b7a57180 100644 --- a/src/clj/cljs/analyzer/utils.clj +++ b/src/clj/cljs/analyzer/utils.clj @@ -1,3 +1,11 @@ +; 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.utils (:require [cljs.analyzer :as ana])) From 2da20eed7145ae4d219883b7b8a84f4bf88b887c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 11:32:41 -0500 Subject: [PATCH 0254/4033] first cut of `cljs.test/run-all-tests`, change `cljs.test/test-all-vars` macro so that we filter only vars that actually have `:test` metadata --- src/clj/cljs/test.clj | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 348e937b2..885608d59 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -8,7 +8,8 @@ (ns cljs.test (:require [cljs.env :as env] - [cljs.analyzer :as ana])) + [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api])) ;; ============================================================================= ;; Utilities for assertions @@ -234,12 +235,33 @@ (assoc (:report-counters summary#) :type :summary)) summary#))) +(defmacro run-all-tests + "Runs all tests in all namespaces; prints results. + Optional argument is a regular expression; only namespaces with + names matching the regular expression (with re-matches) will be + tested." + ([] `(cljs.test/run-all-tests nil)) + ([re] + `(cljs.test/run-tests (cljs.test/empty-env) + ~@(map + (fn [ns] `(quote ~ns)) + (cond->> (ana-api/all-ns) + re (filter #(re-matches re (name %)))))))) + (defmacro test-all-vars - "Calls test-vars on every var interned in the namespace, with fixtures." + "Calls test-vars on every var with :test metadata interned in the + namespace, with fixtures." ([ns] - `(cljs.test/test-vars (vals (ns-interns ~ns)))) - ([env ns] - `(cljs.test/test-vars ~env (vals (ns-interns ~ns))))) + `(cljs.test/test-all-vars + (cljs.test/empty-env) ~ns)) + ([env [quote ns]] + `(cljs.test/test-vars ~env + [~@(map + (fn [[k _]] + `(var ~k)) + (filter + (fn [[_ v]] (:test v)) + (ana-api/ns-interns ns)))]))) (defmacro test-ns "If the namespace defines a function named test-ns-hook, calls that. From 7a4861b57049720429889145098efddd8c083424 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 12:39:11 -0500 Subject: [PATCH 0255/4033] working `cljs.test/run-tests` macro --- src/clj/cljs/test.clj | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 885608d59..1002f002a 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -225,15 +225,21 @@ (fn [[quote ns]] (and (= quote 'quote) (symbol? ns))) namespaces) "All arguments to run-tests must be quoted symbols") - `(let [summary# (apply merge-with + - [~@(map - (fn [ns] - `(cljs.test/test-ns - (assoc ~env :return true) ~ns)) - namespaces)])] - (do-report summary# - (assoc (:report-counters summary#) :type :summary)) - summary#))) + (let [env-sym (gensym "env")] + `(let [~env-sym (assoc ~env :return true) + summary# (reduce + (fn [acc# res#] + (merge-with + + acc# + (:report-counters res#))) + {:test 0 :pass 0 :fail 0 :error 0} + [~@(map + (fn [ns] + `(cljs.test/test-ns ~env-sym ~ns)) + namespaces)])] + (do-report ~env-sym + (assoc summary# :type :summary)) + summary#)))) (defmacro run-all-tests "Runs all tests in all namespaces; prints results. From 629b6e8fdea7f3c68c4f01153b6e7286cf5f7ac6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 12:49:25 -0500 Subject: [PATCH 0256/4033] rely more on `cljs.analyzer.api` in `cljs.test` --- src/clj/cljs/analyzer/api.clj | 5 ++++- src/clj/cljs/test.clj | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index 747e18aa6..418a45176 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -7,10 +7,13 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.analyzer.api - (:refer-clojure :exclude [all-ns ns-interns ns-resolve]) + (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve]) (:require [cljs.env :as env] [cljs.analyzer :as ana])) +(defn resolve [env sym] + (ana/resolve-var env sym)) + (defn all-ns [] (keys (get @env/*compiler* ::ana/namespaces))) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 1002f002a..3e7658145 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -18,7 +18,7 @@ "Returns true if argument is a function or a symbol that resolves to a function (not a macro)." [menv x] - (:fn-var (ana/resolve-var menv x))) + (:fn-var (ana-api/resolve menv x))) (defn assert-predicate "Returns generic assertion code for any functional predicate. The @@ -285,7 +285,7 @@ env'# (-> (assoc env# :return true) (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) ;; If the namespace has a test-ns-hook function, call that: - ~(if-let [v (get-in @env/*compiler* [::ana/namespaces ns :defs 'test-ns-hook])] + ~(if-let [v (ana-api/ns-resolve ns 'test-ns-hook)] `(~(symbol (name ns) "test-ns-hook")) ;; Otherwise, just test every var in the namespace. `(cljs.test/test-all-vars ~form)) From d27173fb63c61516e1dfffe9597e1d03b2ab7da9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 13:40:49 -0500 Subject: [PATCH 0257/4033] pluggable reporting based on keywords. remove ITestReporter, DefaultReporter etc. --- src/cljs/cljs/test.cljs | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index e5a367873..a2bfc631b 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -227,12 +227,6 @@ (:require-macros [clojure.template :as temp])) -;; ============================================================================= -;; Protocols - -(defprotocol ITestReporter - (-do-report [_ env m])) - ;; ============================================================================= ;; Default Reporting @@ -266,15 +260,15 @@ 'is' call 'report' to indicate results. The argument given to 'report' will be a map with a :type key." :dynamic true} - report (fn [_ m] (:type m))) + report (fn [env m] [(:reporter env) (:type m)])) (defmethod report :default [env m] (prn m)) -(defmethod report :pass [env m] +(defmethod report [::default :pass] [env m] (inc-report-counter env :pass)) -(defmethod report :fail [env m] +(defmethod report [::default :fail] [env m] (println "\nFAIL in" (testing-vars-str env m)) (when (seq (:testing-contexts env)) (println (testing-contexts-str env))) @@ -283,7 +277,7 @@ (println " actual:" (pr-str (:actual m))) (inc-report-counter env :fail)) -(defmethod report :error [env m] +(defmethod report [::default :error] [env m] (println "\nERROR in" (testing-vars-str env m)) (when (seq (:testing-contexts env)) (println (testing-contexts-str env))) @@ -292,28 +286,20 @@ (print " actual: ") (prn (:actual m)) (inc-report-counter env :error)) -(defmethod report :summary [env m] +(defmethod report [::default :summary] [env m] (println "\nRan" (:test m) "tests containing" (+ (:pass m) (:fail m) (:error m)) "assertions.") (println (:fail m) "failures," (:error m) "errors.") env) -(defmethod report :begin-test-ns [env m] +(defmethod report [::default :begin-test-ns] [env m] (println "\nTesting" (name (:ns m))) env) ;; Ignore these message types: -(defmethod report :end-test-ns [env m] env) -(defmethod report :begin-test-var [env m] env) -(defmethod report :end-test-var [env m] env) - -(deftype DefaultReporter [] - ITestReporter - (-do-report [_ env m] - (report env m))) - -(defn default-reporter [] - (DefaultReporter.)) +(defmethod report [::default :end-test-ns] [env m] env) +(defmethod report [::default :begin-test-var] [env m] env) +(defmethod report [::default :end-test-var] [env m] env) (defn file-and-line [exception depth] @@ -329,10 +315,10 @@ :fail (merge (file-and-line (js/Error.) 1) m) :error (merge (file-and-line (:actual m) 0) m) m)] - (-do-report (:reporter env) env m))) + (report env m))) (defn empty-env - ([] (empty-env (default-reporter))) + ([] (empty-env ::default)) ([reporter] {:report-counters {:test 0 :pass 0 :fail 0 :error 0} :testing-vars () From f5bd7df115eb5ec84d4c76a4cb0839dadea56f89 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 14:43:59 -0500 Subject: [PATCH 0258/4033] accurate file/line location wip, source mapping support --- src/clj/cljs/test.clj | 1 + src/cljs/cljs/test.cljs | 43 ++++++++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 3e7658145..356471002 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -220,6 +220,7 @@ Defaults to current namespace if none given. Returns a map summarizing test results." ([] `(run-tests (cljs.test/empty-env) '~ana/*cljs-ns*)) + ([env] `(run-tests ~env '~ana/*cljs-ns*)) ([env & namespaces] (assert (every? (fn [[quote ns]] (and (= quote 'quote) (symbol? ns))) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index a2bfc631b..3fa9b1cc4 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -224,8 +224,8 @@ For additional event types, see the examples in the code. "} cljs.test - (:require-macros - [clojure.template :as temp])) + (:require-macros [clojure.template :as temp]) + (:require [clojure.string :as string])) ;; ============================================================================= ;; Default Reporting @@ -301,19 +301,40 @@ (defmethod report [::default :begin-test-var] [env m] env) (defmethod report [::default :end-test-var] [env m] env) -(defn file-and-line - [exception depth] - (if-let [stack (.-stack exception)] - ;; TODO: flesh out - {:file nil :line nil} - {:file (.-fileName exception) - :line (.-lineNumber exception)})) +(defn js-line-and-column [stack-element] + (let [parts (.split stack-element ":") + cnt (count parts)] + (println parts) + [(nth parts (- cnt 2)) (nth parts (dec cnt))])) + +(defn js-filename [stack-element] + (first (.split (last (.split stack-element "/out/")) ":"))) + +(defn mapped-line-and-column [env filename line column] + (let [mapping (get-in (:source-maps env) [filename line column])] + (if mapping + (vec (map mapping [:source :line :col])) + [filename line column]))) + +(defn file-and-line [env exception depth] + (let [stack (.-stack exception)] + (if (and stack (string? stack)) + ;; TODO: flesh out + (let [stacktrace + (vec (map string/trim + (string/split stack #"\n"))) + stack-element (nth stacktrace depth) + fname (js-filename stack-element) + [line column] (js-line-and-column stack-element)] + {:file fname :line line :column column}) + {:file (.-fileName exception) + :line (.-lineNumber exception)})) ) (defn do-report [env m] {:post [(map? %)]} (let [m (case (:type m) - :fail (merge (file-and-line (js/Error.) 1) m) - :error (merge (file-and-line (:actual m) 0) m) + :fail (merge (file-and-line env (js/Error.) 4) m) + :error (merge (file-and-line env (:actual m) 0) m) m)] (report env m))) From ac15413e0dff32807fbb42b81d18825785a79225 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 15:29:23 -0500 Subject: [PATCH 0259/4033] remove println --- src/cljs/cljs/test.cljs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 3fa9b1cc4..a212c0466 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -304,7 +304,6 @@ (defn js-line-and-column [stack-element] (let [parts (.split stack-element ":") cnt (count parts)] - (println parts) [(nth parts (- cnt 2)) (nth parts (dec cnt))])) (defn js-filename [stack-element] From fd891e677b6c4a2d7fb0ec1da73649d06454a145 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 19:14:16 -0500 Subject: [PATCH 0260/4033] source mapping support wip --- src/cljs/cljs/test.cljs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index a212c0466..d32c0a341 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -304,16 +304,18 @@ (defn js-line-and-column [stack-element] (let [parts (.split stack-element ":") cnt (count parts)] - [(nth parts (- cnt 2)) (nth parts (dec cnt))])) + [(js/parseInt (nth parts (- cnt 2))) + (js/parseInt (nth parts (dec cnt)))])) (defn js-filename [stack-element] (first (.split (last (.split stack-element "/out/")) ":"))) (defn mapped-line-and-column [env filename line column] - (let [mapping (get-in (:source-maps env) [filename line column])] - (if mapping + (if-let [source-maps (:source-maps env)] + (if-let [mapping (get-in source-maps [filename line column])] (vec (map mapping [:source :line :col])) - [filename line column]))) + [filename line column]) + [filename line column])) (defn file-and-line [env exception depth] (let [stack (.-stack exception)] @@ -324,7 +326,8 @@ (string/split stack #"\n"))) stack-element (nth stacktrace depth) fname (js-filename stack-element) - [line column] (js-line-and-column stack-element)] + [line column] (js-line-and-column stack-element) + [fname line column] (mapped-line-and-column env fname line column)] {:file fname :line line :column column}) {:file (.-fileName exception) :line (.-lineNumber exception)})) ) From 952e1b11bd7a746a0e516f34f86220a883202dd5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 20:03:46 -0500 Subject: [PATCH 0261/4033] fix basic source mapping support for test reporters --- src/cljs/cljs/source_map.cljs | 4 ++-- src/cljs/cljs/test.cljs | 21 ++++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/cljs/cljs/source_map.cljs b/src/cljs/cljs/source_map.cljs index 9ca2272e9..952e4858a 100644 --- a/src/cljs/cljs/source_map.cljs +++ b/src/cljs/cljs/source_map.cljs @@ -130,8 +130,8 @@ segment map." [result segmap gline] (let [{:keys [gcol source line col name]} segmap - d {:line col - :col line + d {:line line + :col col :source source} d (if name (assoc d :name name) d)] (update-in result [gline] diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index d32c0a341..0f80abf41 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -311,11 +311,22 @@ (first (.split (last (.split stack-element "/out/")) ":"))) (defn mapped-line-and-column [env filename line column] - (if-let [source-maps (:source-maps env)] - (if-let [mapping (get-in source-maps [filename line column])] - (vec (map mapping [:source :line :col])) - [filename line column]) - [filename line column])) + (let [default [filename line column]] + (if-let [source-maps (:source-maps env)] + ;; source maps are 0 indexed for lines + (if-let [columns (get-in source-maps [filename (dec line)])] + (vec + (map + ;; source maps are 0 indexed for columns + ;; multiple segments may exist at column + ;; just take first + (first + (if-let [mapping (get columns (dec column))] + mapping + (second (first columns)))) + [:source :line :col])) + default) + default))) (defn file-and-line [env exception depth] (let [stack (.-stack exception)] From 99e37fbf51c16af03260304bdd57e6f279552edb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 20:33:09 -0500 Subject: [PATCH 0262/4033] add :build-options to the analysis evironment --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index fde8138f4..534dcd300 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1712,7 +1712,7 @@ argument, which the reader will use in any emitted errors." (binding [*cljs-ns* 'cljs.user *cljs-file* path reader/*alias-map* (or reader/*alias-map* {})] - (let [env (empty-env) + (let [env (assoc (empty-env) :build-options opts) ns (loop [ns nil forms (seq (forms-seq res))] (if forms (let [form (first forms) From b6a1d1903d2a2e159ab9f039969afa5d94bf57e7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 20:57:39 -0500 Subject: [PATCH 0263/4033] fix `cljs.test/testing so that it works as an stand alone expression. Change all the `cljs.test/is` macro helpers include the last value in the environment so that we can use it later. --- src/clj/cljs/test.clj | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 356471002..acfc17487 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -39,7 +39,7 @@ {:type :fail, :message ~msg, :expected '~form, :actual (list '~'not (cons '~pred values#))}))] (if (:return env'#) - env'# + (assoc env'# :last-value result#) result#)))) (defn assert-any @@ -55,7 +55,7 @@ {:type :fail, :message ~msg, :expected '~form, :actual value#}))] (if (:return env'#) - env'# + (assoc env'# :last-value value#) value#))) ;; ============================================================================= @@ -95,7 +95,7 @@ {:type :fail, :message ~msg, :expected '~form, :actual (class object#)}))] (if (:return env'#) - env'# + (assoc env'# :last-value result#) result#)))) (defmethod assert-expr 'thrown? [env menv msg form] @@ -135,7 +135,7 @@ {:type :fail, :message ~msg, :expected '~form, :actual e#}))] (if (:return env'#) - env'# + (assoc env'# :last-value e#) e#)))))) (defmacro try-expr @@ -175,11 +175,18 @@ (defmacro testing "Adds a new string to the list of testing contexts. May be nested, but must occur inside a test function (deftest)." - [env string & body] - `(fn [env#] - (reduce #(%2 %1) - (update-in env# [:testing-contexts] conj ~string) - [~@body]))) + ([string & body] + (if (contains? (:locals &env) 'cljs$lang$test$body) + `(fn [env#] + (reduce #(%2 %1) + (update-in env# [:testing-contexts] conj ~string) + [~@body])) + `(:last-value + (reduce #(%2 %1) + (update-in (assoc (cljs.test/empty-env) :return true) + [:testing-contexts] conj ~string) + (let [~'cljs$lang$test$body nil] + [~@body])))))) ;; ============================================================================= ;; Defining Tests From 236dd57266b64ea70b2e885c2bd6ccd079ead04d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 21:30:54 -0500 Subject: [PATCH 0264/4033] :source-maps -> :source-map --- src/cljs/cljs/test.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 0f80abf41..5f6a8a131 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -312,9 +312,9 @@ (defn mapped-line-and-column [env filename line column] (let [default [filename line column]] - (if-let [source-maps (:source-maps env)] + (if-let [source-map (:source-map env)] ;; source maps are 0 indexed for lines - (if-let [columns (get-in source-maps [filename (dec line)])] + (if-let [columns (get-in source-map [filename (dec line)])] (vec (map ;; source maps are 0 indexed for columns From 4d7ad5c1f0328a2c220051df8674639904fb9061 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Dec 2014 21:54:18 -0500 Subject: [PATCH 0265/4033] need to clear `:testing-vars` and `:testing-contexts` from the environment when calling `cljs.test/test-var` --- src/clj/cljs/test.clj | 6 +++++- src/cljs/cljs/test.cljs | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index acfc17487..a13ef5f7c 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -216,7 +216,11 @@ (fn ([] (~name (cljs.test/empty-env))) ([env#] - (cljs.test/test-var env# (.-cljs$lang$var ~name))))) + (cljs.test/test-var + (assoc env# + :testing-vars () + :testing-contexts ()) + (.-cljs$lang$var ~name))))) (set! (.-cljs$lang$var ~name) (var ~name))))) ;; ============================================================================= diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 5f6a8a131..43d4092e3 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -416,7 +416,13 @@ (reduce (fn [env v] (if (:test (meta v)) - (each-fixture-fn (fn [] (test-var env v))) + (each-fixture-fn + (fn [] + (test-var + (assoc env + :testing-vars () + :testing-contexts ()) + v))) env)) env vars))))) (assoc env :return true) (group-by (comp :ns meta) vars))] From 60cdd6d56eb7ae475d3c392f318e3afbb17131cf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 13:07:09 -0500 Subject: [PATCH 0266/4033] `cljs.test/test-all-vars` was discarding namespace information --- src/clj/cljs/test.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index a13ef5f7c..063376b09 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -276,7 +276,7 @@ `(cljs.test/test-vars ~env [~@(map (fn [[k _]] - `(var ~k)) + `(var ~(symbol (name ns) (name k)))) (filter (fn [[_ v]] (:test v)) (ana-api/ns-interns ns)))]))) From f07ebd0a38b52746cbc1e5457078ce723785bbb6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 13:07:45 -0500 Subject: [PATCH 0267/4033] dogfood cljs.test for clojure.string-test --- test/cljs/cljs/core_test.cljs | 2 + test/cljs/clojure/string_test.cljs | 160 +++++++++++++++-------------- test/cljs/test_runner.cljs | 8 +- 3 files changed, 93 insertions(+), 77 deletions(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index dc65f36f7..8596ce15b 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2159,8 +2159,10 @@ (rest names)) arr))) + (set! *print-newline* true) (assert (= (with-out-str (doseq [fn (cljs-739 [] [:a :b :c :d])] (fn))) ":a\n:b\n:c\n:d\n")) + (set! *print-newline* false) ;; CLJS-728 diff --git a/test/cljs/clojure/string_test.cljs b/test/cljs/clojure/string_test.cljs index ab9db1fd2..aae1254e2 100644 --- a/test/cljs/clojure/string_test.cljs +++ b/test/cljs/clojure/string_test.cljs @@ -1,80 +1,90 @@ (ns clojure.string-test - (:require [clojure.string :as s])) + (:require [cljs.test :as test + :refer-macros [deftest is testing]] + [clojure.string :as s])) -(defn test-string - [] - ;; reverse - (assert (= "" (s/reverse ""))) - (assert (= "tab" (s/reverse "bat"))) - ;; CLJS-814 - (assert (= "c\uD834\uDD1Ea" (s/reverse "a\uD834\uDD1Ec"))) ;; U+1D11E MUSICAL SYMBOL G CLEF - ;; replace - (assert (= "faabar" (s/replace "foobar" \o \a))) - (assert (= "barbarbar" (s/replace "foobarfoo" "foo" "bar"))) - (assert (= "FOObarFOO" (s/replace "foobarfoo" #"foo" s/upper-case))) - (assert (= "barbar)foo" (s/replace "foo(bar)foo" "foo(" "bar"))) - ;; join - (assert (= "" (s/join nil))) - (assert (= "" (s/join []))) - (assert (= "1" (s/join [1]))) - (assert (= "12" (s/join [1 2]))) - (assert (= "1,2,3" (s/join \, [1 2 3]))) - (assert (= "" (s/join \, []))) - (assert (= "1 and-a 2 and-a 3" (s/join " and-a " [1 2 3]))) - ;; capitalize - (assert (= "FOOBAR" (s/upper-case "Foobar"))) - (assert (= "foobar" (s/lower-case "FooBar"))) - (assert (= "Foobar" (s/capitalize "foobar"))) - (assert (= "Foobar" (s/capitalize "FOOBAR"))) - ;; split - (assert (= ["a" "b"] (s/split "a-b" #"-"))) - (assert (= ["a" "b" "c"] (s/split "a-b-c" #"-" -1))) - (assert (= ["a" "b" "c"] (s/split "a-b-c" #"-" 0))) - (assert (= ["a-b-c"] (s/split "a-b-c" #"-" 1))) - (assert (= ["a" "b-c"] (s/split "a-b-c" #"-" 2))) - (assert (= ["a" "b" "c"] (s/split "a-b-c" #"-" 3))) - (assert (= ["a" "b" "c"] (s/split "a-b-c" #"-" 4))) - (assert (vector? (s/split "abc" #"-"))) - (assert (= ["a-b-c"] (s/split "a-b-c" #"x" 2))) - (assert (= ["" "a" "b" "c" ""] (s/split "abc" (re-pattern "") 5))) - (assert (= ["a"] (s/split "ab" #"b"))) - (assert (= [] (s/split "ab" #"ab"))) - ;; split-lines - (let [result (s/split-lines "one\ntwo\r\nthree")] - (assert (= ["one" "two" "three"] result)) - (assert (vector? result))) - (assert (= (list "foo") (s/split-lines "foo"))) - ;; blank - (assert (s/blank? nil)) - (assert (s/blank? "")) - (assert (s/blank? " ")) - (assert (s/blank? " \t \n \r ")) - (assert (not (s/blank? " foo "))) - ;; escape - (assert (= "<foo&bar>" - (s/escape "" {\& "&" \< "<" \> ">"}))) - (assert (= " \\\"foo\\\" " - (s/escape " \"foo\" " {\" "\\\""}))) - (assert (= "faabor" - (s/escape "foobar" {\a \o, \o \a}))) - ;; replace-first - (assert (= "barbarfoo" (s/replace-first "foobarfoo" "foo" "bar"))) - (assert (= "barbarfoo" (s/replace-first "foobarfoo" #"foo" "bar"))) - (assert (= "z.ology" (s/replace-first "zoology" \o \.))) - (assert (= "FOObarfoo" (s/replace-first "foobarfoo" #"foo" s/upper-case))) - ;; trim - (assert (= "foo " (s/triml " foo "))) - (assert (= "" (s/triml " "))) - (assert (= " foo" (s/trimr " foo "))) - (assert (= "" (s/trimr " "))) - (assert (= "foo" (s/trim " foo \r\n"))) - ;; trim-newline - (assert (= "foo" (s/trim-newline "foo\n"))) - (assert (= "foo" (s/trim-newline "foo\r\n"))) - (assert (= "foo" (s/trim-newline "foo"))) - (assert (= "foo\r " (s/trim-newline "foo\r "))) - (assert (= "" (s/trim-newline ""))) - :ok) +(deftest test-api + (testing "Testing string reverse" + (is (= "" (s/reverse ""))) + (is (= "tab" (s/reverse "bat"))) + (is (= "c\uD834\uDD1Ea" (s/reverse "a\uD834\uDD1Ec"))) ;; U+1D11E MUSICAL SYMBOL G CLEF + ) + + (testing "Testing string replace" + (is (= "faabar" (s/replace "foobar" \o \a))) + (is (= "barbarbar" (s/replace "foobarfoo" "foo" "bar"))) + (is (= "FOObarFOO" (s/replace "foobarfoo" #"foo" s/upper-case))) + (is (= "barbar)foo" (s/replace "foo(bar)foo" "foo(" "bar")))) + + (testing "Testing string join" + (is (= "" (s/join nil))) + (is (= "" (s/join []))) + (is (= "1" (s/join [1]))) + (is (= "12" (s/join [1 2]))) + (is (= "1,2,3" (s/join \, [1 2 3]))) + (is (= "" (s/join \, []))) + (is (= "1 and-a 2 and-a 3" (s/join " and-a " [1 2 3])))) + + (testing "Testing string capitalize" + (is (= "FOOBAR" (s/upper-case "Foobar"))) + (is (= "foobar" (s/lower-case "FooBar"))) + (is (= "Foobar" (s/capitalize "foobar"))) + (is (= "Foobar" (s/capitalize "FOOBAR")))) + + (testing "Testing string split" + (is (= ["a" "b"] (s/split "a-b" #"-"))) + (is (= ["a" "b" "c"] (s/split "a-b-c" #"-" -1))) + (is (= ["a" "b" "c"] (s/split "a-b-c" #"-" 0))) + (is (= ["a-b-c"] (s/split "a-b-c" #"-" 1))) + (is (= ["a" "b-c"] (s/split "a-b-c" #"-" 2))) + (is (= ["a" "b" "c"] (s/split "a-b-c" #"-" 3))) + (is (= ["a" "b" "c"] (s/split "a-b-c" #"-" 4))) + (is (vector? (s/split "abc" #"-"))) + (is (= ["a-b-c"] (s/split "a-b-c" #"x" 2))) + (is (= ["" "a" "b" "c" ""] (s/split "abc" (re-pattern "") 5))) + (is (= ["a"] (s/split "ab" #"b"))) + (is (= [] (s/split "ab" #"ab")))) + + (testing "Testing string split lines" + (let [result (s/split-lines "one\ntwo\r\nthree")] + (is (= ["one" "two" "three"] result)) + (is (vector? result))) + (is (= (list "foo") (s/split-lines "foo")))) + + (testing "Testing string blank?" + (is (s/blank? nil)) + (is (s/blank? "")) + (is (s/blank? " ")) + (is (s/blank? " \t \n \r ")) + (is (not (s/blank? " foo ")))) + + (testing "Testing string escape" + (is (= "<foo&bar>" + (s/escape "" {\& "&" \< "<" \> ">"}))) + (is (= " \\\"foo\\\" " + (s/escape " \"foo\" " {\" "\\\""}))) + (is (= "faabor" + (s/escape "foobar" {\a \o, \o \a})))) + + (testing "Testing string replace-first" + (is (= "barbarfoo" (s/replace-first "foobarfoo" "foo" "bar"))) + (is (= "barbarfoo" (s/replace-first "foobarfoo" #"foo" "bar"))) + (is (= "z.ology" (s/replace-first "zoology" \o \.))) + (is (= "FOObarfoo" (s/replace-first "foobarfoo" #"foo" s/upper-case)))) + + (testing "Testing string trim" + (is (= "foo " (s/triml " foo "))) + (is (= "" (s/triml " "))) + (is (= " foo" (s/trimr " foo "))) + (is (= "" (s/trimr " "))) + (is (= "foo" (s/trim " foo \r\n")))) + + (testing "Testing string trim-newline" + (is (= "foo" (s/trim-newline "foo\n"))) + (is (= "foo" (s/trim-newline "foo\r\n"))) + (is (= "foo" (s/trim-newline "foo"))) + (is (= "foo\r " (s/trim-newline "foo\r "))) + (is (= "" (s/trim-newline ""))))) (comment diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index 3c2b43d8e..e9cd2ff4e 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -1,5 +1,6 @@ (ns test-runner - (:require [cljs.core-test :as core-test] + (:require [cljs.test :as test :refer-macros [run-tests]] + [cljs.core-test :as core-test] [cljs.reader-test :as reader-test] [cljs.binding-test :as binding-test] [cljs.ns-test :as ns-test] @@ -13,11 +14,14 @@ [cljs.keyword-test :as keyword-test] [cljs.import-test :as import-test])) +(set! *print-newline* false) (set-print-fn! js/print) (core-test/test-stuff) (reader-test/test-reader) -(string-test/test-string) +(run-tests + (test/empty-env) + 'clojure.string-test) (data-test/test-data) (binding-test/test-binding) (binding-test/test-with-redefs) From ccf06b53b4f00d6ce120eb91239e71269da89e7b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 13:30:56 -0500 Subject: [PATCH 0268/4033] change `cljs.test/run-tests` so providing the environment is optional --- src/clj/cljs/test.clj | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 063376b09..128a5ff87 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -226,19 +226,28 @@ ;; ============================================================================= ;; Running Tests +(defn ns? [x] + (and (seq? x) (= (first x) 'quote))) + (defmacro run-tests "Runs all tests in the given namespaces; prints results. Defaults to current namespace if none given. Returns a map summarizing test results." ([] `(run-tests (cljs.test/empty-env) '~ana/*cljs-ns*)) - ([env] `(run-tests ~env '~ana/*cljs-ns*)) - ([env & namespaces] + ([env-or-ns] + (if (ns? env-or-ns) + `(run-tests (cljs.test/empty-env) ~env-or-ns) + `(run-tests ~env-or-ns '~ana/*cljs-ns*))) + ([env-or-ns & namespaces] (assert (every? (fn [[quote ns]] (and (= quote 'quote) (symbol? ns))) namespaces) "All arguments to run-tests must be quoted symbols") - (let [env-sym (gensym "env")] - `(let [~env-sym (assoc ~env :return true) + (let [is-ns (ns? env-or-ns) + env-sym (gensym "env")] + `(let [~env-sym ~(if is-ns + `(assoc (cljs.test/empty-env) :return true) + `(assoc ~env-or-ns :return true)) summary# (reduce (fn [acc# res#] (merge-with + @@ -248,7 +257,9 @@ [~@(map (fn [ns] `(cljs.test/test-ns ~env-sym ~ns)) - namespaces)])] + (if is-ns + (concat [env-or-ns] namespaces) + namespaces))])] (do-report ~env-sym (assoc summary# :type :summary)) summary#)))) From dd9ecca384f1c72e7e402a90b05e0ded40a8fb18 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 13:31:14 -0500 Subject: [PATCH 0269/4033] cljs.test dogfood clojure.data-test --- test/cljs/clojure/data_test.cljs | 37 ++++++++++++++++---------------- test/cljs/test_runner.cljs | 9 ++++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/test/cljs/clojure/data_test.cljs b/test/cljs/clojure/data_test.cljs index 009b1ccbd..b2d9fc971 100644 --- a/test/cljs/clojure/data_test.cljs +++ b/test/cljs/clojure/data_test.cljs @@ -1,22 +1,23 @@ (ns clojure.data-test - (:require [clojure.data :refer [diff]])) + (:require [cljs.test :refer-macros [deftest is]] + [clojure.data :refer [diff]])) -(defn test-data [] - (assert (= [nil nil nil] (diff nil nil))) - (assert (= [1 2 nil] (diff 1 2))) - (assert (= [nil nil [1 2 3]] (diff [1 2 3] '(1 2 3)))) - (assert (= [1 [:a :b] nil] (diff 1 [:a :b]))) - (assert (= [{:a 1} :b nil] (diff {:a 1} :b))) - (assert (= [:team #{:p1 :p2} nil] (diff :team #{:p1 :p2}))) - (assert (= [{0 :a} [:a] nil] (diff {0 :a} [:a]))) - (assert (= [nil [nil 2] [1]] (diff [1] [1 2]))) - (assert (= [nil nil [1 2]] (diff [1 2] (into-array [1 2])))) - (assert (= [#{:a} #{:b} #{:c :d}] (diff #{:a :c :d} #{:b :c :d}))) - (assert (= [nil nil {:a 1}] (diff {:a 1} {:a 1}))) - (assert (= [{:a #{2}} {:a #{4}} {:a #{3}}] (diff {:a #{2 3}} {:a #{3 4}}))) - (assert (= [nil nil [1 2]] (diff [1 2] (into-array [1 2])))) - (assert (= [nil nil [1 2]] (diff (into-array [1 2]) [1 2]))) - (assert (= [{:a {:c [1]}} {:a {:c [0]}} {:a {:c [nil 2] :b 1}}] +(deftest test-data + (is (= [nil nil nil] (diff nil nil))) + (is (= [1 2 nil] (diff 1 2))) + (is (= [nil nil [1 2 3]] (diff [1 2 3] '(1 2 3)))) + (is (= [1 [:a :b] nil] (diff 1 [:a :b]))) + (is (= [{:a 1} :b nil] (diff {:a 1} :b))) + (is (= [:team #{:p1 :p2} nil] (diff :team #{:p1 :p2}))) + (is (= [{0 :a} [:a] nil] (diff {0 :a} [:a]))) + (is (= [nil [nil 2] [1]] (diff [1] [1 2]))) + (is (= [nil nil [1 2]] (diff [1 2] (into-array [1 2])))) + (is (= [#{:a} #{:b} #{:c :d}] (diff #{:a :c :d} #{:b :c :d}))) + (is (= [nil nil {:a 1}] (diff {:a 1} {:a 1}))) + (is (= [{:a #{2}} {:a #{4}} {:a #{3}}] (diff {:a #{2 3}} {:a #{3 4}}))) + (is (= [nil nil [1 2]] (diff [1 2] (into-array [1 2])))) + (is (= [nil nil [1 2]] (diff (into-array [1 2]) [1 2]))) + (is (= [{:a {:c [1]}} {:a {:c [0]}} {:a {:c [nil 2] :b 1}}] (diff {:a {:b 1 :c [1 2]}} {:a {:b 1 :c [0 2]}}))) - (assert (= [{:a nil} {:a false} {:b nil :c false}] + (is (= [{:a nil} {:a false} {:b nil :c false}] (diff {:a nil :b nil :c false} {:a false :b nil :c false})))) diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index e9cd2ff4e..544523e01 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -4,8 +4,8 @@ [cljs.reader-test :as reader-test] [cljs.binding-test :as binding-test] [cljs.ns-test :as ns-test] - [clojure.string-test :as string-test] - [clojure.data-test :as data-test] + [clojure.string-test] + [clojure.data-test] [cljs.macro-test :as macro-test] [cljs.letfn-test :as letfn-test] [foo.ns-shadow-test :as ns-shadow-test] @@ -20,9 +20,8 @@ (core-test/test-stuff) (reader-test/test-reader) (run-tests - (test/empty-env) - 'clojure.string-test) -(data-test/test-data) + 'clojure.string-test + 'clojure.data-test) (binding-test/test-binding) (binding-test/test-with-redefs) (ns-test/test-ns) From 2b4aae5046f9a35581629fb1f5cfc0d4ccae9046 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 13:36:56 -0500 Subject: [PATCH 0270/4033] convert cljs.letfn-test & cljs.reducers-test to cljs.test --- test/cljs/cljs/letfn_test.cljs | 21 +++++++++++---------- test/cljs/cljs/reducers_test.cljs | 31 ++++++++++++++----------------- test/cljs/test_runner.cljs | 10 +++++----- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/test/cljs/cljs/letfn_test.cljs b/test/cljs/cljs/letfn_test.cljs index cc9eb6db1..4261389e1 100644 --- a/test/cljs/cljs/letfn_test.cljs +++ b/test/cljs/cljs/letfn_test.cljs @@ -1,6 +1,7 @@ -(ns cljs.letfn-test) +(ns cljs.letfn-test + (:require [cljs.test :refer-macros [deftest is]])) -(defn test-letfn [] +(deftest test-letfn (letfn [(ev? [x] (if (zero? x) true @@ -9,11 +10,11 @@ (if (zero? x) false (ev? (dec x))))] - (assert (ev? 0)) - (assert (ev? 10)) - (assert (not (ev? 1))) - (assert (not (ev? 11))) - (assert (not (od? 0))) - (assert (not (od? 10))) - (assert (od? 1)) - (assert (od? 11)))) + (is (ev? 0)) + (is (ev? 10)) + (is (not (ev? 1))) + (is (not (ev? 11))) + (is (not (od? 0))) + (is (not (od? 10))) + (is (od? 1)) + (is (od? 11)))) diff --git a/test/cljs/cljs/reducers_test.cljs b/test/cljs/cljs/reducers_test.cljs index 9aef9d748..ffe7e5898 100644 --- a/test/cljs/cljs/reducers_test.cljs +++ b/test/cljs/cljs/reducers_test.cljs @@ -1,32 +1,29 @@ (ns cljs.reducers-test - (:require - [clojure.core.reducers :as r])) + (:require [cljs.test :refer-macros [deftest is]] + [clojure.core.reducers :as r])) -(defn test-builtin-impls [] - (assert (= 0 (r/fold + nil))) - (assert (= [1 2 3 4] (seq (r/reduce r/append! (r/cat) [1 2 3 4])))) - (assert (= 10 (r/reduce + (array 1 2 3 4)))) - (assert (= 11 (r/reduce + 1 (array 1 2 3 4)))) - (assert (= 10 (r/reduce + (list 1 2 3 4)))) - (assert (= 11 (r/reduce + 1 (list 1 2 3 4)))) - (assert (= (r/fold + + [1 2 3]) +(deftest test-builtin-impls + (is (= 0 (r/fold + nil))) + (is (= [1 2 3 4] (seq (r/reduce r/append! (r/cat) [1 2 3 4])))) + (is (= 10 (r/reduce + (array 1 2 3 4)))) + (is (= 11 (r/reduce + 1 (array 1 2 3 4)))) + (is (= 10 (r/reduce + (list 1 2 3 4)))) + (is (= 11 (r/reduce + 1 (list 1 2 3 4)))) + (is (= (r/fold + + [1 2 3]) (r/fold + [1 2 3]) (r/reduce + [1 2 3]) 6)) - (assert (= (r/fold + + (vec (range 2048))) + (is (= (r/fold + + (vec (range 2048))) (r/reduce + (vec (range 2048))))) (letfn [(f [[ks vs] k v] [(conj ks k) (conj vs v)]) (g ([] [#{} #{}]) ([[ks1 vs1] [ks2 vs2]] [(into ks1 ks2) (into vs1 vs2)]))] - (assert (= (r/reduce f (g) {:a 1 :b 2 :c 3}) + (is (= (r/reduce f (g) {:a 1 :b 2 :c 3}) (r/fold g f {:a 1 :b 2 :c 3}) [#{:a :b :c} #{1 2 3}])) (let [m (into {} (for [x (range 2048)] [x (- x)]))] - (assert (= (r/reduce f (g) m) (r/fold g f m))))) + (is (= (r/reduce f (g) m) (r/fold g f m))))) ;; CLJS-792 - (assert (= (into [] (r/map identity {})) []))) - -(defn test-all [] - (test-builtin-impls)) + (is (= (into [] (r/map identity {})) []))) diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index 544523e01..5ea1155e2 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -7,10 +7,10 @@ [clojure.string-test] [clojure.data-test] [cljs.macro-test :as macro-test] - [cljs.letfn-test :as letfn-test] + [cljs.letfn-test] [foo.ns-shadow-test :as ns-shadow-test] [cljs.top-level :as top-level] - [cljs.reducers-test :as reducers-test] + [cljs.reducers-test] [cljs.keyword-test :as keyword-test] [cljs.import-test :as import-test])) @@ -21,15 +21,15 @@ (reader-test/test-reader) (run-tests 'clojure.string-test - 'clojure.data-test) + 'clojure.data-test + 'cljs.letfn-test + 'cljs.reducers-test) (binding-test/test-binding) (binding-test/test-with-redefs) (ns-test/test-ns) (macro-test/test-macros) -(letfn-test/test-letfn) (ns-shadow-test/test-shadow) (top-level/test) -(reducers-test/test-all) (keyword-test/test-keyword) (import-test/test-import) From f0675d29ff672e69fcde3df1810fe7c7cf6032c5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 13:50:49 -0500 Subject: [PATCH 0271/4033] convert more tests, needed to tweak cljs/binding-test due to cljs.test's "async first" design --- test/cljs/cljs/binding_test.cljs | 19 ++++++++++--------- test/cljs/cljs/keyword_test.cljs | 14 ++++++++------ test/cljs/cljs/macro_test.cljs | 5 +++-- test/cljs/cljs/top_level.cljs | 9 +++++---- test/cljs/test_runner.cljs | 19 +++++++++---------- 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/test/cljs/cljs/binding_test.cljs b/test/cljs/cljs/binding_test.cljs index 34e63e354..845fc74dc 100644 --- a/test/cljs/cljs/binding_test.cljs +++ b/test/cljs/cljs/binding_test.cljs @@ -1,12 +1,13 @@ (ns cljs.binding-test - (:require [cljs.binding-test-other-ns :as o])) + (:require [cljs.test :refer-macros [deftest is]] + [cljs.binding-test-other-ns :as o])) -(defn test-binding [] - (binding [o/*foo* 2] - (assert (= o/*foo* 2))) - (assert (= o/*foo* 1))) +(deftest test-binding + (is (binding [o/*foo* 2] + (= o/*foo* 2))) + (is (= o/*foo* 1))) -(defn test-with-redefs [] - (with-redefs [o/bar 2] - (assert (= o/bar 2))) - (assert (= o/bar 10))) +(deftest test-with-redefs + (is (with-redefs [o/bar 2] + (= o/bar 2))) + (is (= o/bar 10))) diff --git a/test/cljs/cljs/keyword_test.cljs b/test/cljs/cljs/keyword_test.cljs index 58c827150..4b2c6fe4e 100644 --- a/test/cljs/cljs/keyword_test.cljs +++ b/test/cljs/cljs/keyword_test.cljs @@ -1,8 +1,10 @@ (ns cljs.keyword-test - (:require [cljs.keyword-other :as other]) - (:require-macros [clojure.core :as cc])) + (:require-macros [clojure.core :as cc] + [cljs.test :refer [deftest is]]) + (:require [cljs.keyword-other :as other] + [cljs.test])) -(defn test-keyword [] - (assert (= ::bar :cljs.keyword-test/bar)) - (assert (= ::other/foo :cljs.keyword-other/foo)) - (assert (= ::cc/foo :clojure.core/foo))) +(deftest test-keyword + (is (= ::bar :cljs.keyword-test/bar)) + (is (= ::other/foo :cljs.keyword-other/foo)) + (is (= ::cc/foo :clojure.core/foo))) diff --git a/test/cljs/cljs/macro_test.cljs b/test/cljs/cljs/macro_test.cljs index 591ac3914..88f195614 100644 --- a/test/cljs/cljs/macro_test.cljs +++ b/test/cljs/cljs/macro_test.cljs @@ -1,6 +1,7 @@ (ns cljs.macro-test (:refer-clojure :exclude [==]) + (:require [cljs.test :refer-macros [deftest is]]) (:use-macros [cljs.macro-test.macros :only [==]])) -(defn test-macros [] - (assert (= (== 1 1) 2))) \ No newline at end of file +(deftest test-macros + (is (= (== 1 1) 2))) diff --git a/test/cljs/cljs/top_level.cljs b/test/cljs/cljs/top_level.cljs index a2a7323ab..887d1d288 100644 --- a/test/cljs/cljs/top_level.cljs +++ b/test/cljs/cljs/top_level.cljs @@ -1,5 +1,6 @@ (ns cljs.top-level - (:refer-clojure :exclude [test])) + (:refer-clojure :exclude [test]) + (:require [cljs.test :refer-macros [deftest is]])) (let [foo 1] (defn bar [] @@ -9,6 +10,6 @@ (defn baz [] foo)) -(defn test [] - (assert (= (bar) 1)) - (assert (= (baz) 2))) +(deftest test + (is (= (bar) 1)) + (is (= (baz) 2))) diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index 5ea1155e2..6c9ce3f6d 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -2,16 +2,16 @@ (:require [cljs.test :as test :refer-macros [run-tests]] [cljs.core-test :as core-test] [cljs.reader-test :as reader-test] - [cljs.binding-test :as binding-test] + [cljs.binding-test] [cljs.ns-test :as ns-test] [clojure.string-test] [clojure.data-test] - [cljs.macro-test :as macro-test] + [cljs.macro-test] [cljs.letfn-test] [foo.ns-shadow-test :as ns-shadow-test] - [cljs.top-level :as top-level] + [cljs.top-level] [cljs.reducers-test] - [cljs.keyword-test :as keyword-test] + [cljs.keyword-test] [cljs.import-test :as import-test])) (set! *print-newline* false) @@ -23,14 +23,13 @@ 'clojure.string-test 'clojure.data-test 'cljs.letfn-test - 'cljs.reducers-test) -(binding-test/test-binding) -(binding-test/test-with-redefs) + 'cljs.reducers-test + 'cljs.binding-test + 'cljs.macro-test + 'cljs.top-level + 'cljs.keyword-test) (ns-test/test-ns) -(macro-test/test-macros) (ns-shadow-test/test-shadow) -(top-level/test) -(keyword-test/test-keyword) (import-test/test-import) (println "Tests completed without exception") From eff15c108f4806cc50667fbaab31ba6e9f6c8f38 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 13:55:33 -0500 Subject: [PATCH 0272/4033] convert more tests --- test/cljs/cljs/import_test.cljs | 15 ++++++++------- test/cljs/cljs/ns_test.cljs | 23 ++++++++++++----------- test/cljs/foo/ns_shadow_test.cljs | 11 ++++++----- test/cljs/test_runner.cljs | 18 +++++++----------- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/test/cljs/cljs/import_test.cljs b/test/cljs/cljs/import_test.cljs index 9edcbcd5c..f8cfc4a00 100644 --- a/test/cljs/cljs/import_test.cljs +++ b/test/cljs/cljs/import_test.cljs @@ -1,12 +1,13 @@ (ns cljs.import-test + (:require [cljs.test :refer-macros [deftest is]]) (:import goog.math.Long [goog.math Vec2 Vec3] [goog.math Integer])) -(defn test-import [] - (assert (fn? Long)) - (assert (.equals (Long. 4 6) (.add (Long. 1 2) (Long. 3 4)))) - (assert (= "12" (str (Long.fromInt 12)))) - (assert (not (nil? (Vec2. 1 2)))) - (assert (not (nil? (Vec3. 1 2 3)))) - (assert (.equals (Integer.fromString "10") (goog.math.Integer.fromString "10")))) +(deftest test-import + (is (fn? Long)) + (is (.equals (Long. 4 6) (.add (Long. 1 2) (Long. 3 4)))) + (is (= "12" (str (Long.fromInt 12)))) + (is (not (nil? (Vec2. 1 2)))) + (is (not (nil? (Vec3. 1 2 3)))) + (is (.equals (Integer.fromString "10") (goog.math.Integer.fromString "10")))) diff --git a/test/cljs/cljs/ns_test.cljs b/test/cljs/cljs/ns_test.cljs index 9982ea06f..95fc7e688 100644 --- a/test/cljs/cljs/ns_test.cljs +++ b/test/cljs/cljs/ns_test.cljs @@ -1,19 +1,20 @@ (ns cljs.ns-test (:refer-clojure :exclude [+ for]) - (:require-macros [clojure.core :as lang]) - (:require [cljs.ns-test.foo :refer [baz]] + (:require-macros [clojure.core :as lang] + [cljs.test :refer [deftest is]]) + (:require [cljs.test] + [cljs.ns-test.foo :refer [baz]] [clojure.set :as s]) (:use [cljs.ns-test.bar :only [quux]])) (def + -) -(defn test-ns [] - (assert (= 4 (clojure.core/+ 2 1 1))) - (assert (= 0 (cljs.ns-test/+ 2 1 1))) - (assert (= 0 (+ 2 1 1))) - (assert (= 123 (baz))) - (assert (= 123 (quux))) +(deftest test-ns + (is (= 4 (clojure.core/+ 2 1 1))) + (is (= 0 (cljs.ns-test/+ 2 1 1))) + (is (= 0 (+ 2 1 1))) + (is (= 123 (baz))) + (is (= 123 (quux))) - (assert (= (range 5) (lang/for [x (range 5)] x))) - (assert (= #{1 2 3} (s/union #{1} #{2 3}))) - :ok) + (is (= (range 5) (lang/for [x (range 5)] x))) + (is (= #{1 2 3} (s/union #{1} #{2 3})))) diff --git a/test/cljs/foo/ns_shadow_test.cljs b/test/cljs/foo/ns_shadow_test.cljs index 17be09d88..d2f1b536f 100644 --- a/test/cljs/foo/ns_shadow_test.cljs +++ b/test/cljs/foo/ns_shadow_test.cljs @@ -1,5 +1,6 @@ (ns foo.ns-shadow-test - (:require baz)) + (:require [cljs.test :refer-macros [deftest is]] + baz)) (defn bar [] 1) @@ -14,7 +15,7 @@ ([] (baz 2)) ([x] (quux 2))) -(defn test-shadow [] - (assert (= (quux 2) 3)) - (assert (= (foo) 42)) - (assert (= (baz) 3))) +(deftest test-shadow + (is (= (quux 2) 3)) + (is (= (foo) 42)) + (is (= (baz) 3))) diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index 6c9ce3f6d..6c1d6fb8f 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -3,16 +3,16 @@ [cljs.core-test :as core-test] [cljs.reader-test :as reader-test] [cljs.binding-test] - [cljs.ns-test :as ns-test] + [cljs.ns-test] [clojure.string-test] [clojure.data-test] [cljs.macro-test] [cljs.letfn-test] - [foo.ns-shadow-test :as ns-shadow-test] + [foo.ns-shadow-test] [cljs.top-level] [cljs.reducers-test] [cljs.keyword-test] - [cljs.import-test :as import-test])) + [cljs.import-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -27,11 +27,7 @@ 'cljs.binding-test 'cljs.macro-test 'cljs.top-level - 'cljs.keyword-test) -(ns-test/test-ns) -(ns-shadow-test/test-shadow) -(import-test/test-import) - -(println "Tests completed without exception") - - + 'cljs.keyword-test + 'cljs.ns-tst + 'foo.ns-shadow-test + 'cljs.import-test) From bc83b714131aa5a2b49f0bf6a35b6ce2295454f2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 14:31:32 -0500 Subject: [PATCH 0273/4033] support side effecting statements in body of deftest --- src/clj/cljs/test.clj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 128a5ff87..60d9b488d 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -210,7 +210,13 @@ ([] (self# (cljs.test/empty-env))) ([env#] (let [~'cljs$lang$test$body nil - ret# (reduce #(%2 %1) env# [~@body])] + ret# (reduce + (fn [env'# thunk#] + (let [ret# (thunk#)] + (if (fn? ret#) + (ret# env'#) + env'#))) + env# [~@(map (fn [expr] `(fn [] ~expr)) body)])] (when (:return env#) ret#))))) (fn From 79eecb3ebe4f4102c538000f0cc9ba1c2b8e29bc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 14:37:42 -0500 Subject: [PATCH 0274/4033] `cljs.test/testing` needs to perform the same transform as `cljs.test/deftest` --- src/clj/cljs/test.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 60d9b488d..e31f68c2e 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -182,11 +182,16 @@ (update-in env# [:testing-contexts] conj ~string) [~@body])) `(:last-value - (reduce #(%2 %1) + (reduce + (fn [env'# thunk#] + (let [ret# (thunk#)] + (if (fn? ret#) + (ret# env'#) + env'#))) (update-in (assoc (cljs.test/empty-env) :return true) [:testing-contexts] conj ~string) (let [~'cljs$lang$test$body nil] - [~@body])))))) + [~@(map (fn [expr] `(fn [] ~expr)) body)])))))) ;; ============================================================================= ;; Defining Tests From c4879d5ebb3fc3255087805c2ac2281a2270f48b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 15:14:56 -0500 Subject: [PATCH 0275/4033] fix `cljs.test/testing` when used in body of `cljs.test/test` --- src/clj/cljs/test.clj | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index e31f68c2e..9757d2a38 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -170,6 +170,7 @@ ([form msg] (if (contains? (:locals &env) 'cljs$lang$test$body) `(fn [env#] (cljs.test/try-expr env# ~msg ~form)) + ;; expression evaluation case `(cljs.test/try-expr (cljs.test/empty-env) ~msg ~form)))) (defmacro testing @@ -178,20 +179,27 @@ ([string & body] (if (contains? (:locals &env) 'cljs$lang$test$body) `(fn [env#] - (reduce #(%2 %1) + (reduce + (fn [env'# thunk#] + (let [ret# (thunk#)] + (if (fn? ret#) + (ret# env'#) + env'#))) (update-in env# [:testing-contexts] conj ~string) - [~@body])) - `(:last-value - (reduce - (fn [env'# thunk#] - (let [ret# (thunk#)] - (if (fn? ret#) - (ret# env'#) - env'#))) - (update-in (assoc (cljs.test/empty-env) :return true) - [:testing-contexts] conj ~string) - (let [~'cljs$lang$test$body nil] - [~@(map (fn [expr] `(fn [] ~expr)) body)])))))) + [~@(map (fn [expr] `(fn [] ~expr)) body)])) + ;; expression evaluation case + `(let [~'cljs$lang$test$body nil] + (:last-value + (reduce + (fn [env'# thunk#] + (let [ret# (thunk#)] + (if (fn? ret#) + (ret# env'#) + env'#))) + (update-in (assoc (cljs.test/empty-env) :return true) + [:testing-contexts] conj ~string) + (let [~'cljs$lang$test$body nil] + [~@(map (fn [expr] `(fn [] ~expr)) body)]))))))) ;; ============================================================================= ;; Defining Tests From 8b7b48a934f6c58f84ad55e0e7de8388909b9a41 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 15:39:20 -0500 Subject: [PATCH 0276/4033] need to pop `:testing-contexts` after `cljs.test/testing` completes --- src/clj/cljs/test.clj | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 9757d2a38..ac80e4692 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -179,27 +179,31 @@ ([string & body] (if (contains? (:locals &env) 'cljs$lang$test$body) `(fn [env#] - (reduce - (fn [env'# thunk#] - (let [ret# (thunk#)] - (if (fn? ret#) - (ret# env'#) - env'#))) - (update-in env# [:testing-contexts] conj ~string) - [~@(map (fn [expr] `(fn [] ~expr)) body)])) + (update-in + (reduce + (fn [env'# thunk#] + (let [ret# (thunk#)] + (if (fn? ret#) + (ret# env'#) + env'#))) + (update-in env# [:testing-contexts] conj ~string) + [~@(map (fn [expr] `(fn [] ~expr)) body)]) + [:testing-contexts] rest)) ;; expression evaluation case `(let [~'cljs$lang$test$body nil] (:last-value - (reduce - (fn [env'# thunk#] - (let [ret# (thunk#)] - (if (fn? ret#) - (ret# env'#) - env'#))) - (update-in (assoc (cljs.test/empty-env) :return true) - [:testing-contexts] conj ~string) - (let [~'cljs$lang$test$body nil] - [~@(map (fn [expr] `(fn [] ~expr)) body)]))))))) + (update-in + (reduce + (fn [env'# thunk#] + (let [ret# (thunk#)] + (if (fn? ret#) + (ret# env'#) + env'#))) + (update-in (assoc (cljs.test/empty-env) :return true) + [:testing-contexts] conj ~string) + (let [~'cljs$lang$test$body nil] + [~@(map (fn [expr] `(fn [] ~expr)) body)])) + [:testing-contexts] rest)))))) ;; ============================================================================= ;; Defining Tests From fb07cb33f76deda8c9fb03496f690450a1320b81 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 15:42:45 -0500 Subject: [PATCH 0277/4033] convert cljs.reader-test to cljs.test --- test/cljs/cljs/reader_test.cljs | 309 +++++++++++++++----------------- test/cljs/test_runner.cljs | 4 +- 2 files changed, 149 insertions(+), 164 deletions(-) diff --git a/test/cljs/cljs/reader_test.cljs b/test/cljs/cljs/reader_test.cljs index af2a7449b..16bd67264 100644 --- a/test/cljs/cljs/reader_test.cljs +++ b/test/cljs/cljs/reader_test.cljs @@ -1,182 +1,167 @@ (ns cljs.reader-test - (:require [cljs.reader :as reader] + (:require [cljs.test :refer-macros [deftest testing is]] + [cljs.reader :as reader] [goog.object :as o])) (deftype T [a b]) (defrecord R [a b]) -(defn test-reader - [] - (assert (= 1 (reader/read-string "1"))) - (assert (= 2 (reader/read-string "#_nope 2"))) - (assert (= -1 (reader/read-string "-1"))) - (assert (= -1.5 (reader/read-string "-1.5"))) - (assert (= [3 4] (reader/read-string "[3 4]"))) - (assert (= "foo" (reader/read-string "\"foo\""))) - (assert (= :hello (reader/read-string ":hello"))) - (assert (= 'goodbye (reader/read-string "goodbye"))) - (assert (= '% (reader/read-string "%"))) - (assert (= #{1 2 3} (reader/read-string "#{1 2 3}"))) - (assert (= '(7 8 9) (reader/read-string "(7 8 9)"))) - (assert (= '(deref foo) (reader/read-string "@foo"))) - (assert (= '(quote bar) (reader/read-string "'bar"))) - (assert (= 'foo/bar (reader/read-string "foo/bar"))) - (assert (= \a (reader/read-string "\\a"))) - (assert (= {:tag 'String} (meta (reader/read-string "^String {:a 1}")))) - (assert (= [:a 'b #{'c {:d [:e :f :g]}}] - (reader/read-string "[:a b #{c {:d [:e :f :g]}}]"))) - (assert (= :foo/bar (reader/read-string ":foo/bar"))) - (assert (= nil (reader/read-string "nil"))) - (assert (= true (reader/read-string "true"))) - (assert (= false (reader/read-string "false"))) - (assert (= "string" (reader/read-string "\"string\""))) - (assert (= "escape chars \t \r \n \\ \" \b \f" (reader/read-string "\"escape chars \\t \\r \\n \\\\ \\\" \\b \\f\""))) - - ;; number literals - (assert (apply = 0 (map reader/read-string "0" "+0" "-0" " 0 "))) - (assert (apply = 42 (map reader/read-string ["052" "0x2a" "2r101010" "8R52" "16r2a" "36r16"]))) - (assert (apply = 42 (map reader/read-string ["+052" "+0x2a" "+2r101010" "+8r52" "+16R2a" "+36r16"]))) - (assert (apply = -42 (map reader/read-string ["-052" "-0X2a" "-2r101010" "-8r52" "-16r2a" "-36R16"]))) - - ;; queue literals - (assert (= cljs.core.PersistentQueue.EMPTY - (reader/read-string "#queue []"))) - - (assert (= (-> cljs.core.PersistentQueue.EMPTY (conj 1)) - (reader/read-string "#queue [1]"))) - - (assert (= (into cljs.core.PersistentQueue.EMPTY [1 2]) - (reader/read-string "#queue [1 2]"))) - - ;; comments - (assert (nil? (reader/read-string ";foo"))) +(deftest test-reader + (testing "Test basic reading" + (is (= 1 (reader/read-string "1"))) + (is (= 2 (reader/read-string "#_nope 2"))) + (is (= -1 (reader/read-string "-1"))) + (is (= -1.5 (reader/read-string "-1.5"))) + (is (= [3 4] (reader/read-string "[3 4]"))) + (is (= "foo" (reader/read-string "\"foo\""))) + (is (= :hello (reader/read-string ":hello"))) + (is (= 'goodbye (reader/read-string "goodbye"))) + (is (= '% (reader/read-string "%"))) + (is (= #{1 2 3} (reader/read-string "#{1 2 3}"))) + (is (= '(7 8 9) (reader/read-string "(7 8 9)"))) + (is (= '(deref foo) (reader/read-string "@foo"))) + (is (= '(quote bar) (reader/read-string "'bar"))) + (is (= 'foo/bar (reader/read-string "foo/bar"))) + (is (= \a (reader/read-string "\\a"))) + (is (= {:tag 'String} (meta (reader/read-string "^String {:a 1}")))) + (is (= [:a 'b #{'c {:d [:e :f :g]}}] + (reader/read-string "[:a b #{c {:d [:e :f :g]}}]"))) + (is (= :foo/bar (reader/read-string ":foo/bar"))) + (is (= nil (reader/read-string "nil"))) + (is (= true (reader/read-string "true"))) + (is (= false (reader/read-string "false"))) + (is (= "string" (reader/read-string "\"string\""))) + (is (= "escape chars \t \r \n \\ \" \b \f" (reader/read-string "\"escape chars \\t \\r \\n \\\\ \\\" \\b \\f\"")))) - (assert (= 3 (try - (reader/read-string ";foo\n3") - (catch js/Error e :threw)))) - (assert (= 3 (try - (reader/read-string ";foo\n3\n5") - (catch js/Error e :threw)))) - - ;; inst + (testing "Test reading number literals" + (is (apply = 0 (map reader/read-string "0" "+0" "-0" " 0 "))) + (is (apply = 42 (map reader/read-string ["052" "0x2a" "2r101010" "8R52" "16r2a" "36r16"]))) + (is (apply = 42 (map reader/read-string ["+052" "+0x2a" "+2r101010" "+8r52" "+16R2a" "+36r16"]))) + (is (apply = -42 (map reader/read-string ["-052" "-0X2a" "-2r101010" "-8r52" "-16r2a" "-36R16"])))) + + (testing "Test reading queue literals" + (is (= cljs.core.PersistentQueue.EMPTY + (reader/read-string "#queue []"))) + (is (= (-> cljs.core.PersistentQueue.EMPTY (conj 1)) + (reader/read-string "#queue [1]"))) + (is (= (into cljs.core.PersistentQueue.EMPTY [1 2]) + (reader/read-string "#queue [1 2]")))) + + (testing "Test reading comments" + (is (nil? (reader/read-string ";foo"))) + (is (= 3 (try + (reader/read-string ";foo\n3") + (catch js/Error e :threw)))) + (is (= 3 (try + (reader/read-string ";foo\n3\n5") + (catch js/Error e :threw))))) + (let [est-inst (reader/read-string "#inst \"2010-11-12T13:14:15.666-05:00\"") utc-inst (reader/read-string "#inst \"2010-11-12T18:14:15.666-00:00\"") pad (fn [n] - (if (< n 10) - (str "0" n) - n))] - - (assert (= (.valueOf (js/Date. "2010-11-12T13:14:15.666-05:00")) - (.valueOf est-inst))) - - (assert (= (.valueOf est-inst) - (.valueOf (reader/read-string (pr-str est-inst))))) - - (assert (= (.valueOf est-inst) - (.valueOf utc-inst))) - - (doseq [month (range 1 13) day (range 1 29) hour (range 1 23)] - (let [s (str "#inst \"2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-06:00\"")] - (assert (= (-> s reader/read-string .valueOf) - (-> s reader/read-string pr-str reader/read-string .valueOf)))))) - - (let [insts [(reader/read-string "#inst \"2012\"") - (reader/read-string "#inst \"2012-01\"") - (reader/read-string "#inst \"2012-01-01\"") - (reader/read-string "#inst \"2012-01-01T00\"") - (reader/read-string "#inst \"2012-01-01T00:00:00.000\"") - (reader/read-string "#inst \"2012-01-01T00:00:00.000123456\"") - (reader/read-string "#inst \"2012-01-01T00:00:00.000123456789+00:00\"")]] - (assert (apply = (map #(.valueOf %) insts)))) - - ;; uuid literals + (if (< n 10) + (str "0" n) + n))] + (testing "Testing reading instant literals" + (is (= (.valueOf (js/Date. "2010-11-12T13:14:15.666-05:00")) + (.valueOf est-inst))) + (is (= (.valueOf est-inst) + (.valueOf (reader/read-string (pr-str est-inst))))) + (is (= (.valueOf est-inst) + (.valueOf utc-inst))) + (is (every? true? + (for [month (range 1 13) + day (range 1 29) + hour (range 1 23)] + (let [s (str "#inst \"2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-06:00\"")] + (= (-> s reader/read-string .valueOf) + (-> s reader/read-string pr-str reader/read-string .valueOf)))))))) + (let [u (reader/read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\"")] - (assert (= u (reader/read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) - - (assert (not (identical? u (reader/read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\"")))) - - (assert (= u (-> u pr-str reader/read-string)))) + (testing "Testing reading UUID literals" + (is (= u (reader/read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) + (is (not (identical? u (reader/read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\"")))) + (is (= u (-> u pr-str reader/read-string))))) - ;; new tag parsers - - (reader/register-tag-parser! 'foo identity) + (testing "Testing tag parsers" + (reader/register-tag-parser! 'foo identity) + (is (= [1 2] (reader/read-string "#foo [1 2]"))) - (assert (= [1 2] (reader/read-string "#foo [1 2]"))) + ;; tag elements with prefix component + (reader/register-tag-parser! 'foo.bar/baz identity) + (is (= [1 2] (reader/read-string "#foo.bar/baz [1 2]"))) - ;; tag elements with prefix component - (reader/register-tag-parser! 'foo.bar/baz identity) - (assert (= [1 2] (reader/read-string "#foo.bar/baz [1 2]"))) - - ;; default tag parser - (reader/register-default-tag-parser! (fn [tag val] val)) - (assert (= [1 2] (reader/read-string "#a.b/c [1 2]"))) + ;; default tag parser + (reader/register-default-tag-parser! (fn [tag val] val)) + (is (= [1 2] (reader/read-string "#a.b/c [1 2]")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Unicode Tests - ; sample unicode strings, symbols, keywords - (doseq [unicode - ["اختبار" ; arabic - "ทดสอบ" ; thai - "こんにちは" ; japanese hiragana - "你好" ; chinese traditional - "אַ גוט יאָר" ; yiddish - "cześć" ; polish - "привет" ; russian - - ;; RTL languages skipped below because tricky to insert - ;; ' and : at the "start" - - 'ทดสอบ - 'こんにちは - '你好 - 'cześć - 'привет - - :ทดสอบ - :こんにちは - :你好 - :cześć - :привет - - ;compound data - {:привет :ru "你好" :cn} - ]] - (let [input (pr-str unicode) - read (reader/read-string input)] - (assert (= unicode read) - (str "Failed to read-string \"" unicode "\" from: " input)))) - - ; unicode error cases - (doseq [unicode-error - ["\"abc \\ua\"" ; truncated - "\"abc \\x0z ...etc\"" ; incorrect code - "\"abc \\u0g00 ..etc\"" ; incorrect code - ]] - (let [r (try - (reader/read-string unicode-error) - :failed-to-throw - (catch js/Error e :ok))] - (assert (= r :ok) (str "Failed to throw reader error for: " unicode-error)))) - - ;; CLJS-717 - - (assert (array? (reader/read-string "#js [1 2 3]"))) - (assert (= (alength (reader/read-string "#js [1 2 3]")) 3)) - (assert (= (seq (reader/read-string "#js [1 2 3]")) (seq [1 2 3]))) - (assert (= (set (js-keys (reader/read-string "#js {:foo \"bar\" :baz \"woz\"}"))) #{"foo" "baz"})) - (assert (= (aget (reader/read-string "#js {:foo \"bar\"}") "foo") "bar")) - (assert (= (aget (reader/read-string "#js {\"foo\" \"bar\"}") "foo") "bar")) - (assert (array? (aget (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo"))) - (assert (= (seq (aget (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo")) '(1 2 3))) - - ;; CLJS-787 - - (assert (nil? (reader/read-string ""))) - - ;; CLJS-819 + (testing "Test reading unicode - strings, symbols, keywords" + (is (every? true? + (for [unicode + ["اختبار" ; arabic + "ทดสอบ" ; thai + "こんにちは" ; japanese hiragana + "你好" ; chinese traditional + "אַ גוט יאָר" ; yiddish + "cześć" ; polish + "привет" ; russian + + ;; RTL languages skipped below because tricky to insert + ;; ' and : at the "start" + + 'ทดสอบ + 'こんにちは + '你好 + 'cześć + 'привет + + :ทดสอบ + :こんにちは + :你好 + :cześć + :привет + + ;compound data + {:привет :ru "你好" :cn} + ]] + (let [input (pr-str unicode) + read (reader/read-string input)] + (= unicode read)))))) + + (testing "Testing unicode error cases" + (is (every? true? + (for [unicode-error + ["\"abc \\ua\"" ; truncated + "\"abc \\x0z ...etc\"" ; incorrect code + "\"abc \\u0g00 ..etc\"" ; incorrect code + ]] + (let [r (try + (reader/read-string unicode-error) + :failed-to-throw + (catch js/Error e :ok))] + (= r :ok)))))) +) + +(deftest test-717 + (testing "Testing reading, CLJS-717" + (is (array? (reader/read-string "#js [1 2 3]"))) + (is (= (alength (reader/read-string "#js [1 2 3]")) 3)) + (is (= (seq (reader/read-string "#js [1 2 3]")) (seq [1 2 3]))) + (is (= (set (js-keys (reader/read-string "#js {:foo \"bar\" :baz \"woz\"}"))) #{"foo" "baz"})) + (is (= (aget (reader/read-string "#js {:foo \"bar\"}") "foo") "bar")) + (is (= (aget (reader/read-string "#js {\"foo\" \"bar\"}") "foo") "bar")) + (is (array? (aget (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo"))) + (is (= (seq (aget (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo")) '(1 2 3))))) + +(deftest test-787 + (testing "Testing reading, CLS-787" + (is (nil? (reader/read-string ""))))) + +(deftest test-819 (let [re (reader/read-string "#\"\\s\\u00a1\"") m (re-find re " \u00a1 ")] - (assert (= m " \u00a1"))) - - :ok) + (testing "Testing reading, CLJS-819" + (is (= m " \u00a1"))))) diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index 6c1d6fb8f..f029f5593 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -1,7 +1,7 @@ (ns test-runner (:require [cljs.test :as test :refer-macros [run-tests]] [cljs.core-test :as core-test] - [cljs.reader-test :as reader-test] + [cljs.reader-test] [cljs.binding-test] [cljs.ns-test] [clojure.string-test] @@ -18,8 +18,8 @@ (set-print-fn! js/print) (core-test/test-stuff) -(reader-test/test-reader) (run-tests + 'cljs.reader-test 'clojure.string-test 'clojure.data-test 'cljs.letfn-test From afd4e68a068b20725138d6c1eb24a26ab0bcbb9c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 16:30:33 -0500 Subject: [PATCH 0278/4033] handles cases where a `deftest` is defined in a function body. In the analyzer `parse 'var` case check that the definition exists before accessing the `cljs$lang$test` property. In `cljs.test/test-var` skip if the var does not actually have test metadata. --- src/clj/cljs/analyzer.clj | 2 +- src/cljs/cljs/test.cljs | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 534dcd300..c08c4b1c7 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -510,7 +510,7 @@ :sym (analyze env `(quote ~(symbol (name (:ns var)) (name (:name var))))) :meta (let [ks [:ns :name :doc :arglists :file :line :column] m (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) - :test `(.-cljs$lang$test ~sym))] + :test `(when ~sym (.-cljs$lang$test ~sym)))] (analyze env m))})) (defmethod parse 'if diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 43d4092e3..27bc19cb1 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -364,25 +364,25 @@ (defn test-var "If v has a function in its :test metadata, calls that function, - add v to :testing-vars property of env." - ([v] - (test-var (empty-env) v)) + add v to :testing-vars property of env." + ([v] (test-var (empty-env) v)) ([env v] {:pre [(map? env) (instance? Var v)]} - (let [t (:test (meta v)) - env (-> env - (update-in [:testing-vars] conj v) - (update-in [:report-counters :test] inc))] - (try - (let [env' (t env)] - (when (:return env') env')) - (catch :default e - (let [env' (do-report env - {:type :error - :message "Uncaught exception, not in assertion." - :expected nil - :actual e})] - (when (:return env') env'))))))) + (if-let [t (:test (meta v))] + (let [env (-> env + (update-in [:testing-vars] conj v) + (update-in [:report-counters :test] inc))] + (try + (let [env' (t env)] + (when (:return env') env')) + (catch :default e + (let [env' (do-report env + {:type :error + :message "Uncaught exception, not in assertion." + :expected nil + :actual e})] + (when (:return env') env'))))) + env))) (defn- default-fixture "The default, empty, fixture function. Just calls its argument." From 63497200684a02befe4babd8531e8fdfc6faad91 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 18:26:51 -0500 Subject: [PATCH 0279/4033] `cljs.test/function?` needs to check that it's argument is symbol first --- src/clj/cljs/test.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index ac80e4692..729c23210 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -18,7 +18,7 @@ "Returns true if argument is a function or a symbol that resolves to a function (not a macro)." [menv x] - (:fn-var (ana-api/resolve menv x))) + (and (symbol? x) (:fn-var (ana-api/resolve menv x)))) (defn assert-predicate "Returns generic assertion code for any functional predicate. The From 0590b1b431352b48951ba54995db5d80227b8368 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 19:02:44 -0500 Subject: [PATCH 0280/4033] (WIP) converting all core tests to cljs.test --- test/cljs/cljs/core_test.cljs | 3136 +++++++++++++++++---------------- test/cljs/test_runner.cljs | 6 +- 2 files changed, 1591 insertions(+), 1551 deletions(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 8596ce15b..00f4f7de9 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1,1114 +1,1613 @@ (ns cljs.core-test (:refer-clojure :exclude [iter]) - (:require [clojure.string :as s] + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.string :as s] [clojure.set :as set])) -(defn test-stuff [] +(deftest test-js-primitives ;; js primitives (let [keys #(vec (js-keys %))] - (assert (= [] (keys (js-obj)) (keys (apply js-obj [])))) - (assert (= ["x"] (keys (js-obj "x" "y")) (keys (apply js-obj ["x" "y"]))))) - - ;; -equiv - (assert (= 1)) - (assert (= 1 1)) - (assert (= 1 1 1)) - (assert (= 1 1 1 1)) - (assert (not (= 1 2))) - (assert (not (= 1 2 1))) - (assert (not (= 1 1 2))) - (assert (not (= 1 1 2 1))) - (assert (not (= 1 1 1 2))) - - ;; arithmetic - (assert (= (+) 0)) - (assert (= (apply + []) 0)) - (assert (= (+ 1) 1)) - (assert (= (apply + [1]) 1)) - (assert (= (+ 1 1) 2)) - (assert (= (apply + [1 1]) 2)) - (assert (= (+ 1 2 3) 6)) - (assert (= (apply + [1 2 3]) 6)) - - (assert (= (- 1) -1)) - (assert (= (apply - [1]) -1)) - (assert (= (- 1 1) 0)) - (assert (= (apply - [1 1]) 0)) - (assert (= (- 3 2 1) 0)) - (assert (= (apply - [3 2 1]) 0)) - - (assert (= (*) 1)) - (assert (= (apply * []) 1)) - (assert (= (* 2) 2)) - (assert (= (apply * [2]) 2)) - (assert (= (* 2 3) 6)) - (assert (= (apply * [2 3]) 6)) - - (assert (= (/ 2) 0.5)) - (assert (= (apply / [2]) 0.5)) - (assert (= (/ 6 2) 3)) - (assert (= (apply / [6 2]) 3)) - (assert (= (/ 6 3 2) 1)) - (assert (= (apply / [6 3 2]) 1)) - - (assert (= (< 1) true)) - (assert (= (apply < [1]) true)) - (assert (= (< 1 2) true)) - (assert (= (apply < [1 2]) true)) - (assert (= (< 1 1) false)) - (assert (= (apply < [1 1]) false)) - (assert (= (< 2 1) false)) - (assert (= (apply < [2 1]) false)) - (assert (= (< 1 2 3) true)) - (assert (= (apply < [1 2 3]) true)) - (assert (= (< 1 1 3) false)) - (assert (= (apply < [1 1 3]) false)) - (assert (= (< 3 1 1) false)) - (assert (= (apply < [3 1 1]) false)) - - (assert (= (<= 1) true)) - (assert (= (apply <= [1]) true)) - (assert (= (<= 1 1) true)) - (assert (= (apply <= [1 1]) true)) - (assert (= (<= 1 2) true)) - (assert (= (apply <= [1 2]) true)) - (assert (= (<= 2 1) false)) - (assert (= (apply <= [2 1]) false)) - (assert (= (<= 1 2 3) true)) - (assert (= (apply <= [1 2 3]) true)) - (assert (= (<= 1 1 3) true)) - (assert (= (apply <= [1 1 3]) true)) - (assert (= (<= 3 1 1) false)) - (assert (= (apply <= [3 1 1]) false)) - - (assert (= (> 1) true)) - (assert (= (apply > [1]) true)) - (assert (= (> 2 1) true)) - (assert (= (apply > [2 1]) true)) - (assert (= (> 1 1) false)) - (assert (= (apply > [1 1]) false)) - (assert (= (> 1 2) false)) - (assert (= (apply > [1 2]) false)) - (assert (= (> 3 2 1) true)) - (assert (= (apply > [3 2 1]) true)) - (assert (= (> 3 1 1) false)) - (assert (= (apply > [3 1 1]) false)) - (assert (= (> 1 1 3) false)) - (assert (= (apply > [1 1 3]) false)) - - (assert (= (>= 1) true)) - (assert (= (apply >= [1]) true)) - (assert (= (>= 2 1) true)) - (assert (= (apply >= [2 1]) true)) - (assert (= (>= 1 1) true)) - (assert (= (apply >= [1 1]) true)) - (assert (= (>= 1 2) false)) - (assert (= (apply >= [1 2]) false)) - (assert (= (>= 3 2 1) true)) - (assert (= (apply >= [3 2 1]) true)) - (assert (= (>= 3 1 1) true)) - (assert (= (apply >= [3 1 1]) true)) - (assert (= (>= 3 1 2) false)) - (assert (= (apply >= [3 1 2]) false)) - (assert (= (>= 1 1 3) false)) - (assert (= (apply >= [1 1 3]) false)) - - (assert (= (dec 1) 0)) - (assert (= (apply dec [1]) 0)) - (assert (= (inc 0) 1)) - (assert (= (apply inc [0]) 1)) - - (assert (= (zero? 0) true)) - (assert (= (apply zero? [0]) true)) - (assert (= (zero? 1) false)) - (assert (= (apply zero? [1]) false)) - (assert (= (zero? -11) false)) - (assert (= (apply zero? [-11]) false)) - (assert (= (pos? 0) false)) - (assert (= (apply pos? [0]) false)) - (assert (= (pos? 1) true)) - (assert (= (apply pos? [1]) true)) - (assert (= (pos? -1) false)) - (assert (= (apply pos? [-1]) false)) - (assert (= (neg? -1) true)) - (assert (= (apply neg? [-1]) true)) - - (assert (= (max 1) 1)) - (assert (= (apply max [1]) 1)) - (assert (= (max 1 2) 2)) - (assert (= (apply max [1 2]) 2)) - (assert (= (max 2 1) 2)) - (assert (= (apply max [2 1]) 2)) - (assert (= (max 1 2 3) 3)) - (assert (= (apply max [1 2 3]) 3)) - (assert (= (max 1 3 2) 3)) - (assert (= (apply max [1 3 2]) 3)) - - (assert (= (min 1) 1)) - (assert (= (apply min [1]) 1)) - (assert (= (min 1 2) 1)) - (assert (= (apply min [1 2]) 1)) - (assert (= (min 2 1) 1)) - (assert (= (apply min [2 1]) 1)) - (assert (= (min 1 2 3) 1)) - (assert (= (apply min [1 2 3]) 1)) - (assert (= (min 2 1 3) 1)) - (assert (= (apply min [3 1 3]) 1)) - - (assert (= (mod 4 2) 0)) - (assert (= (apply mod [4 2]) 0)) - (assert (= (mod 3 2) 1)) - (assert (= (apply mod [3 2]) 1)) - (assert (= (mod -2 5) 3)) - - (assert (= [4 3 2 1 0] (loop [i 0 j ()] - (if (< i 5) - (recur (inc i) (conj j (fn [] i))) - (map #(%) j))))) - - (assert (= [[1 1] [1 2] [1 3] [2 1] [2 2] [2 3]] - (map #(%) (for [i [1 2] j [1 2 3]] (fn [] [i j]))))) - - (assert (integer? 0)) - (assert (integer? 42)) - (assert (integer? -42)) - (assert (not (integer? ""))) - (assert (not (integer? 1e308))) - (assert (not (integer? js/Infinity))) - (assert (not (integer? (- js/Infinity)))) - (assert (not (integer? js/NaN))) - - (assert (= 42 (int 42.5))) - (assert (integer? (int 42.5))) - - (assert (= 42 (long 42.5))) - (assert (integer? (long 42.5))) - - (assert (= -1 (int -1.5))) - (assert (= -9 (long -9.8))) - - (assert (= 2 (:b {:a 1 :b 2}))) - (assert (= 2 ('b '{:a 1 b 2}))) - (assert (= 2 ({:a 1 :b 2} :b))) - (assert (= 2 ({1 1 2 2} 2))) - (assert (= 2 (:a {:b 1} 2))) - (assert (= 2 (:a {} 2))) - (assert (= 2 ({:b 1} :a 2))) - (assert (= 2 ({} :a 2))) - (assert (= nil (:a {}))) - (assert (= nil (:a ""))) - (assert (= 2 (:a "" 2))) - (assert (= 2 (#{1 2 3} 2))) - (assert (zero? (hash (aget (js-obj) "foo")))) - - (assert (= 1 (apply :a '[{:a 1 a 2}]))) - (assert (= 1 (apply 'a '[{a 1 :b 2}]))) - (assert (= 1 (apply {:a 1} [:a]))) - (assert (= 2 (apply {:a 1} [:b 2]))) - - ; See - ; https://github.com/clojure/tools.reader#differences-from-lispreaderjava - ; about why these tests won't pass. Not clear if we should change the reader - ; or the test - ; (assert (= "baz" (name 'foo/bar/baz))) - ; (assert (= "foo/bar" (namespace 'foo/bar/baz))) - ; (assert (= "baz" (name :foo/bar/baz))) - ;(assert (= "foo/bar" (namespace :foo/bar/baz))) - (assert (nil? (namespace '/))) - (assert (= "/" (name '/))) - (assert (= "keyword" (name :keyword))) - ;;TODO: These next two tests need Clojure 1.5 - ;(assert (= "foo" (namespace 'foo//))) - ;(assert (= "/" (name 'foo//))) - - ; str - (assert (= ":hello" (str :hello))) - (assert (= "hello" (str 'hello))) - (assert (= "hello:world" (str "hello" :world))) - (assert (= ":helloworld" (str :hello 'world))) - - ; symbol - (assert (= 'a (symbol 'a))) - - ; keyword - (assert (= :a (keyword "a"))) - (assert (= :a (keyword 'a))) - (assert (= :a/b (keyword 'a 'b))) - (assert (= :a (keyword :a))) - - (assert (= {:a :b} (get {[1 2 3] {:a :b}, 4 5} [1 2 3]))) - (assert (= :a (nth [:a :b :c :d] 0))) - (assert (= :a (nth [:a :b :c :d] 0.1)) ) - (assert (not (= {:a :b :c nil} {:a :b :d nil}))) - (assert (= (list 3 2 1) [3 2 1])) - (assert (= [3 2 1] (seq (array 3 2 1)))) - (assert (= 9 (reduce + (next (seq (array 1 2 3 4)))))) - (assert (= () (rest nil))) - (assert (= nil (seq (array)))) - (assert (= nil (seq ""))) - (assert (= nil (seq []))) - (assert (= nil (seq {}))) - (assert (= () (rest ()))) - (assert (= () (rest [1]))) - (assert (= () (rest (array 1)))) - (assert (= {"x" "y"} (meta ^{"x" "y"} []))) - (assert (= {:a :b} (dissoc {:a :b :c :d} :c))) - (assert (= (hash-map :foo 5) - (assoc (cljs.core.ObjMap. nil (array) (js-obj)) :foo 5))) - - (assert (= "\"asdf\" \"asdf\"" (pr-str "asdf" "asdf"))) - (assert (= "[1 true {:a 2, :b #\"x\\\"y\"} #js [3 4]]" - (pr-str [1 true {:a 2 :b #"x\"y"} (array 3 4)]))) - - (assert (= "\"asdf\"\n" (prn-str "asdf"))) - (assert (= "[1 true {:a 2, :b 42} #js [3 4]]\n" - (prn-str [1 true {:a 2 :b 42} (array 3 4)]))) - - (assert (= "asdf" (print-str "asdf"))) - (assert (= "asdf\n" (println-str "asdf"))) - - (assert (= "" (pr-str))) - (assert (= "\n" (prn-str))) - (assert (= "12" (with-out-str (print 1) (print 2)))) - (assert (= "12" (with-out-str (*print-fn* 1) (*print-fn* 2)))) - - ;;this fails in v8 - why? - ;(assert (= "symbol\"'string" (pr-str (str 'symbol \" \' "string")))) - - (assert (not (= "one" "two"))) - (assert (= 3 (count "abc"))) - (assert (= 4 (count (array 1 2 3 4)))) - (assert (= "c" (nth "abc" 2))) - (assert (= "quux" (nth "abc" 3 "quux"))) - (assert (= 1 (nth (array 1 2 3 4) 0))) - (assert (= "val" (nth (array 1 2 3 4) 4 "val"))) - (assert (= "b" (get "abc" 1))) - (assert (= "harriet" (get "abcd" 4 "harriet"))) - (assert (= 4 (get (array 1 2 3 4) 3))) - (assert (= "zot" (get (array 1 2 3 4) 4 "zot"))) - (assert (= 10 (reduce + (array 1 2 3 4)))) - (assert (= 20 (reduce + 10 (array 1 2 3 4)))) - (assert (= "cabd" (let [jumble (fn [a b] (str (apply str (reverse (str a))) b))] - (reduce jumble "abcd")))) - (assert (= "cafrogbd" (let [jumble (fn [a b] (str (apply str (reverse (str a))) b))] - (reduce jumble "frog" "abcd")))) - (assert (= [0 0 1 0 1] - [(bit-and 1 0) - (bit-and 0 0) - (bit-and 1 1) - (bit-and 42 1) - (bit-and 41 1)])) - (assert (= [1 0 1 43 41] - [(bit-or 1 0) - (bit-or 0 0) - (bit-or 1 1) - (bit-or 42 1) - (bit-or 41 1)])) - (assert (= [1 0 0 42 40] - [(bit-and-not 1 0) - (bit-and-not 0 0) - (bit-and-not 1 1) - (bit-and-not 42 1) - (bit-and-not 41 1)])) - (assert (= [0 2 968 16649 0] - [(bit-clear 1 0) - (bit-clear 2 0) - (bit-clear 1000 5) - (bit-clear 16713 6) - (bit-clear 1024 10)])) - (assert (= [0 0 992 18761 0] - [(bit-flip 1 0) - (bit-flip 2 1) - (bit-flip 1000 3) - (bit-flip 16713 11) - (bit-flip 1024 10)])) - (assert (= [-2 -3 999 -16714 -1025] - [(bit-not 1) - (bit-not 2) - (bit-not -1000) - (bit-not 16713) - (bit-not 1024)])) - (assert (= [1 2 1000 18761 1024] - [(bit-set 1 0) - (bit-set 2 1) - (bit-set 1000 3) - (bit-set 16713 11) - (bit-set 1024 10)])) - (assert (= [true true true false true] - [(bit-test 1 0) - (bit-test 2 1) - (bit-test 1000 3) - (bit-test 16713 11) - (bit-test 1024 10)])) - (assert (= [true false true false false false] - [(true? true) - (true? false) - (false? false) - (false? true) - (true? js/undefined) - (false? js/undefined)])) - ;; apply - (assert (= 0 (apply + nil))) - (assert (= 0 (apply + (list)))) - (assert (= 1 (apply + (list 1)))) - (assert (= 3 (apply + 1 (list 2)))) - (assert (= 7 (apply + 1 2 (list 4)))) - (assert (= 15 (apply + 1 2 4 (list 8)))) - (assert (= 31 (apply + 1 2 4 8 (list 16)))) - (assert (= 63 (apply + 1 2 4 8 16 (list 32)))) - (assert (= 127 (apply + 1 2 4 8 16 (list 32 64)))) - (assert (= 4950 (apply + (take 100 (iterate inc 0))))) - (assert (= () (apply list []))) - (assert (= [1 2 3] (apply list [1 2 3]))) - (assert (= 6 (apply apply [+ [1 2 3]]))) - ;; apply with infinite sequence - (assert (= 3 (apply (fn [& args] - (+ (nth args 0) - (nth args 1) - (nth args 2))) - (iterate inc 0)))) - (assert (= [0 1 2 3 4] (take 5 (apply (fn [& m] m) (iterate inc 0))))) - (assert (= [1 2 3 4 5] (take 5 (apply (fn [x & m] m) (iterate inc 0))))) - (assert (= [2 3 4 5 6] (take 5 (apply (fn [x y & m] m) (iterate inc 0))))) - (assert (= [3 4 5 6 7] (take 5 (apply (fn [x y z & m] m) (iterate inc 0))))) - (assert (= [4 5 6 7 8] (take 5 (apply (fn [x y z a & m] m) (iterate inc 0))))) - (assert (= [5 6 7 8 9] (take 5 (apply (fn [x y z a b & m] m) (iterate inc 0))))) - ;; apply arity tests - (let [single-arity-non-variadic (fn [x y z] [z y x]) - multiple-arity-non-variadic (fn ([x] x) ([x y] [y x]) ([x y z] [z y x])) - single-arity-variadic-fixedargs (fn [x y & more] [more y x]) - single-arity-variadic-nofixedargs (fn [& more] more) - multiple-arity-variadic (fn ([x] x) ([x y] [y x]) ([x y & more] [more y x]))] - (assert (= [3 2 1] (apply single-arity-non-variadic [1 2 3]))) - (assert (= [3 2 1] (apply single-arity-non-variadic 1 [2 3]))) - (assert (= [3 2 1] (apply single-arity-non-variadic 1 2 [3]))) - (assert (= 42 (apply multiple-arity-non-variadic [42]))) - (assert (= [2 1] (apply multiple-arity-non-variadic [1 2]))) - (assert (= [2 1] (apply multiple-arity-non-variadic 1 [2]))) - (assert (= [3 2 1] (apply multiple-arity-non-variadic [1 2 3]))) - (assert (= [3 2 1] (apply multiple-arity-non-variadic 1 [2 3]))) - (assert (= [3 2 1] (apply multiple-arity-non-variadic 1 2 [3]))) - (assert (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs [1 2 3 4 5]))) - (assert (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 [2 3 4 5]))) - (assert (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 [3 4 5]))) - (assert (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 3 [4 5]))) - (assert (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 3 4 [5]))) - (assert (= [3 4 5] (take 3 (first (apply single-arity-variadic-fixedargs (iterate inc 1)))))) - (assert (= [2 1] (rest (apply single-arity-variadic-fixedargs (iterate inc 1))))) - (assert (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs [1 2 3 4 5]))) - (assert (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 [2 3 4 5]))) - (assert (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 [3 4 5]))) - (assert (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 3 [4 5]))) - (assert (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 3 4 [5]))) - (assert (= [1 2 3 4 5] (take 5 (apply single-arity-variadic-nofixedargs (iterate inc 1))))) - (assert (= 42 (apply multiple-arity-variadic [42]))) - (assert (= [2 1] (apply multiple-arity-variadic [1 2]))) - (assert (= [2 1] (apply multiple-arity-variadic 1 [2]))) - (assert (= [[3 4 5] 2 1] (apply multiple-arity-variadic [1 2 3 4 5]))) - (assert (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 [2 3 4 5]))) - (assert (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 [3 4 5]))) - (assert (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 3 [4 5]))) - (assert (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 3 4 [5]))) - (assert (= [3 4 5] (take 3 (first (apply multiple-arity-variadic (iterate inc 1)))))) - (assert (= [2 1] (rest (apply multiple-arity-variadic (iterate inc 1)))))) - - ;; CLJS-383 - (let [f1 (fn f1 ([] 0) ([a] 1) ([a b] 2) ([a b c & more] 3)) - f2 (fn f2 ([x] :foo) ([x y & more] (apply f1 y more)))] - (assert (= 1 (f2 1 2)))) - (let [f (fn ([]) ([a & more] more))] - (assert (nil? (f :foo)))) - (assert (nil? (array-seq (array 1) 1))) - - ;; Functions with metadata + (testing "Testing js primitives" + (is (= [] (keys (js-obj)) (keys (apply js-obj [])))) + (is (= ["x"] (keys (js-obj "x" "y")) (keys (apply js-obj ["x" "y"]))))))) + +(deftest test-equiv + (testing "Testing -equiv" + (is (= 1)) + (is (= 1 1)) + (is (= 1 1 1)) + (is (= 1 1 1 1)) + (is (not (= 1 2))) + (is (not (= 1 2 1))) + (is (not (= 1 1 2))) + (is (not (= 1 1 2 1))) + (is (not (= 1 1 1 2))))) + +(deftest test-arithmetic + (testing "Testing addition" + (is (= (+) 0)) + (is (= (apply + []) 0)) + (is (= (+ 1) 1)) + (is (= (apply + [1]) 1)) + (is (= (+ 1 1) 2)) + (is (= (apply + [1 1]) 2)) + (is (= (+ 1 2 3) 6)) + (is (= (apply + [1 2 3]) 6))) + + (testing "Testing subtraction" + (is (= (- 1) -1)) + (is (= (apply - [1]) -1)) + (is (= (- 1 1) 0)) + (is (= (apply - [1 1]) 0)) + (is (= (- 3 2 1) 0)) + (is (= (apply - [3 2 1]) 0))) + + (testing "Testing multiplication" + (is (= (*) 1)) + (is (= (apply * []) 1)) + (is (= (* 2) 2)) + (is (= (apply * [2]) 2)) + (is (= (* 2 3) 6)) + (is (= (apply * [2 3]) 6))) + + (testing "Testing division" + (is (= (/ 2) 0.5)) + (is (= (apply / [2]) 0.5)) + (is (= (/ 6 2) 3)) + (is (= (apply / [6 2]) 3)) + (is (= (/ 6 3 2) 1)) + (is (= (apply / [6 3 2]) 1))) + + (testing "Testing less than" + (is (= (< 1) true)) + (is (= (apply < [1]) true)) + (is (= (< 1 2) true)) + (is (= (apply < [1 2]) true)) + (is (= (< 1 1) false)) + (is (= (apply < [1 1]) false)) + (is (= (< 2 1) false)) + (is (= (apply < [2 1]) false)) + (is (= (< 1 2 3) true)) + (is (= (apply < [1 2 3]) true)) + (is (= (< 1 1 3) false)) + (is (= (apply < [1 1 3]) false)) + (is (= (< 3 1 1) false)) + (is (= (apply < [3 1 1]) false))) + + (testing "Testing less than or equal to" + (is (= (<= 1) true)) + (is (= (apply <= [1]) true)) + (is (= (<= 1 1) true)) + (is (= (apply <= [1 1]) true)) + (is (= (<= 1 2) true)) + (is (= (apply <= [1 2]) true)) + (is (= (<= 2 1) false)) + (is (= (apply <= [2 1]) false)) + (is (= (<= 1 2 3) true)) + (is (= (apply <= [1 2 3]) true)) + (is (= (<= 1 1 3) true)) + (is (= (apply <= [1 1 3]) true)) + (is (= (<= 3 1 1) false)) + (is (= (apply <= [3 1 1]) false))) + + (testing "Testing greater than" + (is (= (> 1) true)) + (is (= (apply > [1]) true)) + (is (= (> 2 1) true)) + (is (= (apply > [2 1]) true)) + (is (= (> 1 1) false)) + (is (= (apply > [1 1]) false)) + (is (= (> 1 2) false)) + (is (= (apply > [1 2]) false)) + (is (= (> 3 2 1) true)) + (is (= (apply > [3 2 1]) true)) + (is (= (> 3 1 1) false)) + (is (= (apply > [3 1 1]) false)) + (is (= (> 1 1 3) false)) + (is (= (apply > [1 1 3]) false))) + + (testing "Testing greater than or equal to" + (is (= (>= 1) true)) + (is (= (apply >= [1]) true)) + (is (= (>= 2 1) true)) + (is (= (apply >= [2 1]) true)) + (is (= (>= 1 1) true)) + (is (= (apply >= [1 1]) true)) + (is (= (>= 1 2) false)) + (is (= (apply >= [1 2]) false)) + (is (= (>= 3 2 1) true)) + (is (= (apply >= [3 2 1]) true)) + (is (= (>= 3 1 1) true)) + (is (= (apply >= [3 1 1]) true)) + (is (= (>= 3 1 2) false)) + (is (= (apply >= [3 1 2]) false)) + (is (= (>= 1 1 3) false)) + (is (= (apply >= [1 1 3]) false))) + + (testing "Testing dec/inc" + (is (= (dec 1) 0)) + (is (= (apply dec [1]) 0)) + (is (= (inc 0) 1)) + (is (= (apply inc [0]) 1))) + + (testing "Testing zero? pos? neg? even? odd?" + (is (= (zero? 0) true)) + (is (= (apply zero? [0]) true)) + (is (= (zero? 1) false)) + (is (= (apply zero? [1]) false)) + (is (= (zero? -11) false)) + (is (= (apply zero? [-11]) false)) + (is (= (pos? 0) false)) + (is (= (apply pos? [0]) false)) + (is (= (pos? 1) true)) + (is (= (apply pos? [1]) true)) + (is (= (pos? -1) false)) + (is (= (apply pos? [-1]) false)) + (is (= (neg? -1) true)) + (is (= (apply neg? [-1]) true)) + (is (neg? -1)) + (is (not (neg? 1))) + (is (neg? -1.765)) + (is (not (neg? 0))) + (is (= [true false true false true false true false] + (map integer? + [1 1.00001 0x7e7 [] (- 88 1001991881) :foo 0 "0"]))) + (is (= [true false true false true false] + (map odd? [1 2 3 4 -1 0]))) + (is (= [true false true false true true] + (map even? [2 3 4 5 -2 0])))) + + (testing "Testing max / min" + (is (= (max 1) 1)) + (is (= (apply max [1]) 1)) + (is (= (max 1 2) 2)) + (is (= (apply max [1 2]) 2)) + (is (= (max 2 1) 2)) + (is (= (apply max [2 1]) 2)) + (is (= (max 1 2 3) 3)) + (is (= (apply max [1 2 3]) 3)) + (is (= (max 1 3 2) 3)) + (is (= (apply max [1 3 2]) 3)) + + (is (= (min 1) 1)) + (is (= (apply min [1]) 1)) + (is (= (min 1 2) 1)) + (is (= (apply min [1 2]) 1)) + (is (= (min 2 1) 1)) + (is (= (apply min [2 1]) 1)) + (is (= (min 1 2 3) 1)) + (is (= (apply min [1 2 3]) 1)) + (is (= (min 2 1 3) 1)) + (is (= (apply min [3 1 3]) 1))) + + (testing "Testing mod" + (is (= (mod 4 2) 0)) + (is (= (apply mod [4 2]) 0)) + (is (= (mod 3 2) 1)) + (is (= (apply mod [3 2]) 1)) + (is (= (mod -2 5) 3))) + + (testing "Testing numeric equality in collections" + (is (= [4 3 2 1 0] + (loop [i 0 j ()] + (if (< i 5) + (recur (inc i) (conj j (fn [] i))) + (map #(%) j))))) + (is (= [[1 1] [1 2] [1 3] [2 1] [2 2] [2 3]] + (map #(%) (for [i [1 2] j [1 2 3]] (fn [] [i j])))))) + + (testing "Testing integer? predicate" + (is (integer? 0)) + (is (integer? 42)) + (is (integer? -42)) + (is (not (integer? ""))) + (is (not (integer? 1e308))) + (is (not (integer? js/Infinity))) + (is (not (integer? (- js/Infinity)))) + (is (not (integer? js/NaN)))) + + (testing "Testing integer coercions" + (is (= 42 (int 42.5))) + (is (integer? (int 42.5))) + (is (= 42 (long 42.5))) + (is (integer? (long 42.5))) + (is (= -1 (int -1.5))) + (is (= -9 (long -9.8)))) + + (testing "Testing numeric equality from collection" + (is (= 2 (:b {:a 1 :b 2}))) + (is (= 2 ('b '{:a 1 b 2}))) + (is (= 2 ({:a 1 :b 2} :b))) + (is (= 2 ({1 1 2 2} 2))) + (is (= 2 (:a {:b 1} 2))) + (is (= 2 (:a {} 2))) + (is (= 2 ({:b 1} :a 2))) + (is (= 2 ({} :a 2))) + (is (= nil (:a {}))) + (is (= nil (:a ""))) + (is (= 2 (:a "" 2))) + (is (= 2 (#{1 2 3} 2))) + (is (= 1 (apply :a '[{:a 1 a 2}]))) + (is (= 1 (apply 'a '[{a 1 :b 2}]))) + (is (= 1 (apply {:a 1} [:a]))) + (is (= 2 (apply {:a 1} [:b 2])))) + + (testing "Testing quot" + (is (= (quot 4 2) 2)) + (is (= (quot 3 2) 1)) + (is (= (quot 6 4) 1)) + (is (= (quot 0 5) 0)) + (is (= (quot 42 5) 8)) + (is (= (quot 42 -5) -8)) + (is (= (quot -42 -5) 8)) + (is (= (quot 9 3) 3)) + (is (= (quot 9 -3) -3)) + (is (= (quot -9 3) -3)) + (is (= (quot 2 -5) 0)) + (is (= (quot -2 5) 0)) + (is (= (quot 0 3) 0)) + (is (= (quot 0 -3) 0))) + + (testing "Testing mod" + (is (= (mod 4 2) 0)) + (is (= (mod 3 2) 1)) + (is (= (mod 6 4) 2)) + (is (= (mod 0 5) 0)) + (is (= (mod 4.5 2.0) 0.5)) + (is (= (mod 42 5) 2)) + (is (= (mod 9 3) 0)) + (is (= (mod 9 -3) 0)) + (is (= (mod -9 3) 0)) + (is (= (mod -9 -3) 0)) + (is (= (mod 0 3) 0)) + (is (= (mod 3216478362187432 432143214) 120355456))) + + (testing "Testing rem" + (is (= (rem 4 2) 0)) + (is (= (rem 0 5) 0)) + (is (= (rem 4.5 2.0) 0.5)) + (is (= (rem 42 5) 2)) + (is (= (rem 2 5) 2)) + (is (= (rem 2 -5) 2)) + (is (= (rem 0 3) 0))) +) + +(deftest test-hash-null + (is (zero? (hash (aget (js-obj) "foo"))))) + +;; See +;; https://github.com/clojure/tools.reader#differences-from-lispreaderjava +;; about why these tests won't pass. Not clear if we should change the reader +;; or the test +;; (assert (= "baz" (name 'foo/bar/baz))) +;; (assert (= "foo/bar" (namespace 'foo/bar/baz))) +;; (assert (= "baz" (name :foo/bar/baz))) +;; (assert (= "foo/bar" (namespace :foo/bar/baz))) +;; TODO: These next two tests need Clojure 1.5 +;; (assert (= "foo" (namespace 'foo//))) +;; (assert (= "/" (name 'foo//))) + +(deftest test-symbols-and-keywords + (testing "Testing name / namespace" + (is (nil? (namespace '/))) + (is (= "/" (name '/))) + (is (= "keyword" (name :keyword)))) + + (testing "Testing str on keywords / symbols" + (is (= ":hello" (str :hello))) + (is (= "hello" (str 'hello))) + (is (= "hello:world" (str "hello" :world))) + (is (= ":helloworld" (str :hello 'world)))) + + (testing "Testing symbol ctor is idempotent" + (is (= 'a (symbol 'a)))) + + (testing "Testing keyword ctor" + (is (= :a (keyword "a"))) + (is (= :a (keyword 'a))) + (is (= :a/b (keyword 'a 'b))) + (is (= :a (keyword :a))))) + +(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)))) + (testing "Testing assoc dissoc" + (is (= {1 2 3 4} (assoc {} 1 2 3 4))) + (is (= {1 2} (assoc {} 1 2))) + (is (= [42 2] (assoc [1 2] 0 42))) + (is (= {} (dissoc {1 2 3 4} 1 3))) + (is (= {1 2} (dissoc {1 2 3 4} 3))) + (is (nil? (dissoc nil :foo)))) + (testing "Testing find" + (is (= (find {} :a) nil)) + (is (= (find {:a 1} :a) [:a 1])) + (is (= (find {:a 1} :b) nil)) + (is (= (find {:a 1 :b 2} :a) [:a 1])) + (is (= (find {:a 1 :b 2} :b) [:b 2])) + (is (= (find {:a 1 :b 2} :c) nil)) + (is (= (find {} nil) nil)) + (is (= (find {:a 1} nil) nil)) + (is (= (find {:a 1 :b 2} nil) nil)) + (is (= (find [1 2 3] 0) [0 1]))) +) + +(deftest test-metadata + (testing "Testing metadata" + (is (= {"x" "y"} (meta ^{"x" "y"} []))) + )) + +(deftest test-pr-str + (testing "Testing printing" + (is (= "\"asdf\" \"asdf\"" (pr-str "asdf" "asdf"))) + (is (= "[1 true {:a 2, :b #\"x\\\"y\"} #js [3 4]]" + (pr-str [1 true {:a 2 :b #"x\"y"} (array 3 4)]))) + (is (= "\"asdf\"\n" (prn-str "asdf"))) + (is (= "[1 true {:a 2, :b 42} #js [3 4]]\n" + (prn-str [1 true {:a 2 :b 42} (array 3 4)]))) + (is (= "asdf" (print-str "asdf"))) + (is (= "asdf\n" (println-str "asdf"))) + (is (= "" (pr-str))) + (is (= "\n" (prn-str))) + (is (= "12" (with-out-str (print 1) (print 2)))) + (is (= "12" (with-out-str (*print-fn* 1) (*print-fn* 2)))))) + +(deftest test-bit-operations + (testing "Testing bit operations" + (is (= [0 0 1 0 1] + [(bit-and 1 0) + (bit-and 0 0) + (bit-and 1 1) + (bit-and 42 1) + (bit-and 41 1)])) + (is (= [1 0 1 43 41] + [(bit-or 1 0) + (bit-or 0 0) + (bit-or 1 1) + (bit-or 42 1) + (bit-or 41 1)])) + (is (= [1 0 0 42 40] + [(bit-and-not 1 0) + (bit-and-not 0 0) + (bit-and-not 1 1) + (bit-and-not 42 1) + (bit-and-not 41 1)])) + (is (= [0 2 968 16649 0] + [(bit-clear 1 0) + (bit-clear 2 0) + (bit-clear 1000 5) + (bit-clear 16713 6) + (bit-clear 1024 10)])) + (is (= [0 0 992 18761 0] + [(bit-flip 1 0) + (bit-flip 2 1) + (bit-flip 1000 3) + (bit-flip 16713 11) + (bit-flip 1024 10)])) + (is (= [-2 -3 999 -16714 -1025] + [(bit-not 1) + (bit-not 2) + (bit-not -1000) + (bit-not 16713) + (bit-not 1024)])) + (is (= [1 2 1000 18761 1024] + [(bit-set 1 0) + (bit-set 2 1) + (bit-set 1000 3) + (bit-set 16713 11) + (bit-set 1024 10)])) + (is (= [true true true false true] + [(bit-test 1 0) + (bit-test 2 1) + (bit-test 1000 3) + (bit-test 16713 11) + (bit-test 1024 10)])))) + +(deftest test-vectors + (testing "Testing vectors" + (is (= :a (nth [:a :b :c :d] 0))) + (is (= :a (nth [:a :b :c :d] 0.1))) + (let [pv (vec (range 97))] + (testing "basic ops" + (is (= (nth pv 96) 96)) + (is (= (nth pv 97 nil) nil)) + (is (= (pv 96) 96)) + (is (nil? (rseq []))) + (is (= (reverse pv) (rseq pv))))) + (let [pv (vec (range 33))] + (testing "pop" + (is (= pv (-> pv pop pop (conj 31) (conj 32)))))) + (let [stack1 (pop (vec (range 97))) + stack2 (pop stack1)] + (testing "stack operations" + (is (= 95 (peek stack1))) + (is (= 94 (peek stack2))))) + (let [v1 (vec (range 10)) + v2 (vec (range 5)) + s (subvec v1 2 8)] + (testing "subvec" + (is (= s (-> v1 (subvec 2) (subvec 0 6)) (->> v1 (drop 2) (take 6)))) + (is (= 6 (count s))) + (is (= [2 3 4 5 6] (pop s))) + (is (= 7 (peek s))) + (is (= [2 3 4 5 6 7 1] + (assoc s 6 1) + (conj s 1))) + (is (= 27 (reduce + s))) + (is (= s (vec s))) ; pour into plain vector + ;; go outside ranges + (is (= :fail (try (subvec v2 0 6) (catch js/Error e :fail)))) + (is (= :fail (try (subvec v2 6 10) (catch js/Error e :fail)))) + (is (= :fail (try (subvec v2 6 10) (catch js/Error e :fail)))) + (is (= :fail (try (subvec v2 3 6) (catch js/Error e :fail)))) + ;; no layered subvecs + (is (identical? v1 (.-v (subvec s 1 4)))) + (let [m {:x 1}] + (is (= m (meta (with-meta s m)))))) + ;; CLJS-513 + (let [sentinel (js-obj) + s (subvec [0 1 2 3] 1 2)] + (testing "bounds checking" + (is (identical? sentinel (try (s -1) (catch js/Error _ sentinel)))) + (is (identical? sentinel (try (s 1) (catch js/Error _ sentinel)))))) + ;; CLJS-765 + (let [sv1 (subvec [0 1 2 3] 1 2) + sv2 (subvec [0 1 2 3] 1 1)] + (testing "rseq equality" + (is (= (rseq sv1) '(1))) + (is (nil? (rseq sv2)))))) + )) + +(deftest test-sequential-equality + (testing "Testing ISequential equality" + (is (= (list 3 2 1) [3 2 1])) + (is (= [3 2 1] (seq (array 3 2 1)))))) + +(deftest test-seq-operations + (testing "Testing basic seq operations" + (is (= () (rest nil))) + (is (= nil (seq (array)))) + (is (= nil (seq ""))) + (is (= nil (seq []))) + (is (= nil (seq {}))) + (is (= () (rest ()))) + (is (= () (rest [1]))) + (is (= () (rest (array 1)))))) + +(deftest test-apply + (testing "Testing apply" + (is (= 0 (apply + nil))) + (is (= 0 (apply + (list)))) + (is (= 1 (apply + (list 1)))) + (is (= 3 (apply + 1 (list 2)))) + (is (= 7 (apply + 1 2 (list 4)))) + (is (= 15 (apply + 1 2 4 (list 8)))) + (is (= 31 (apply + 1 2 4 8 (list 16)))) + (is (= 63 (apply + 1 2 4 8 16 (list 32)))) + (is (= 127 (apply + 1 2 4 8 16 (list 32 64)))) + (is (= 4950 (apply + (take 100 (iterate inc 0))))) + (is (= () (apply list []))) + (is (= [1 2 3] (apply list [1 2 3]))) + (is (= 6 (apply apply [+ [1 2 3]]))) + ;; apply with infinite sequence + (is (= 3 (apply (fn [& args] + (+ (nth args 0) + (nth args 1) + (nth args 2))) + (iterate inc 0)))) + (is (= [0 1 2 3 4] (take 5 (apply (fn [& m] m) (iterate inc 0))))) + (is (= [1 2 3 4 5] (take 5 (apply (fn [x & m] m) (iterate inc 0))))) + (is (= [2 3 4 5 6] (take 5 (apply (fn [x y & m] m) (iterate inc 0))))) + (is (= [3 4 5 6 7] (take 5 (apply (fn [x y z & m] m) (iterate inc 0))))) + (is (= [4 5 6 7 8] (take 5 (apply (fn [x y z a & m] m) (iterate inc 0))))) + (is (= [5 6 7 8 9] (take 5 (apply (fn [x y z a b & m] m) (iterate inc 0))))) + ;; apply arity tests + (let [single-arity-non-variadic (fn [x y z] [z y x]) + multiple-arity-non-variadic (fn ([x] x) ([x y] [y x]) ([x y z] [z y x])) + single-arity-variadic-fixedargs (fn [x y & more] [more y x]) + single-arity-variadic-nofixedargs (fn [& more] more) + multiple-arity-variadic (fn ([x] x) ([x y] [y x]) ([x y & more] [more y x]))] + (testing "arities" + (is (= [3 2 1] (apply single-arity-non-variadic [1 2 3]))) + (is (= [3 2 1] (apply single-arity-non-variadic 1 [2 3]))) + (is (= [3 2 1] (apply single-arity-non-variadic 1 2 [3]))) + (is (= 42 (apply multiple-arity-non-variadic [42]))) + (is (= [2 1] (apply multiple-arity-non-variadic [1 2]))) + (is (= [2 1] (apply multiple-arity-non-variadic 1 [2]))) + (is (= [3 2 1] (apply multiple-arity-non-variadic [1 2 3]))) + (is (= [3 2 1] (apply multiple-arity-non-variadic 1 [2 3]))) + (is (= [3 2 1] (apply multiple-arity-non-variadic 1 2 [3]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs [1 2 3 4 5]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 [2 3 4 5]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 [3 4 5]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 3 [4 5]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 3 4 [5]))) + (is (= [3 4 5] (take 3 (first (apply single-arity-variadic-fixedargs (iterate inc 1)))))) + (is (= [2 1] (rest (apply single-arity-variadic-fixedargs (iterate inc 1))))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs [1 2 3 4 5]))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 [2 3 4 5]))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 [3 4 5]))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 3 [4 5]))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 3 4 [5]))) + (is (= [1 2 3 4 5] (take 5 (apply single-arity-variadic-nofixedargs (iterate inc 1))))) + (is (= 42 (apply multiple-arity-variadic [42]))) + (is (= [2 1] (apply multiple-arity-variadic [1 2]))) + (is (= [2 1] (apply multiple-arity-variadic 1 [2]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic [1 2 3 4 5]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 [2 3 4 5]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 [3 4 5]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 3 [4 5]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 3 4 [5]))) + (is (= [3 4 5] (take 3 (first (apply multiple-arity-variadic (iterate inc 1)))))) + (is (= [2 1] (rest (apply multiple-arity-variadic (iterate inc 1))))))))) + +;; this fails in v8 - why? +;; (assert (= "symbol\"'string" (pr-str (str 'symbol \" \' "string")))) +(deftest test-misc + (testing "Testing miscellaneous operations" + (is (= 9 (reduce + (next (seq (array 1 2 3 4)))))) + (is (not (= "one" "two"))) + (is (= 3 (count "abc"))) + (is (= 4 (count (array 1 2 3 4)))) + (is (= "c" (nth "abc" 2))) + (is (= "quux" (nth "abc" 3 "quux"))) + (is (= 1 (nth (array 1 2 3 4) 0))) + (is (= "val" (nth (array 1 2 3 4) 4 "val"))) + (is (= "b" (get "abc" 1))) + (is (= "harriet" (get "abcd" 4 "harriet"))) + (is (= 4 (get (array 1 2 3 4) 3))) + (is (= "zot" (get (array 1 2 3 4) 4 "zot"))) + (is (= 10 (reduce + (array 1 2 3 4)))) + (is (= 20 (reduce + 10 (array 1 2 3 4)))) + (is (= "cabd" (let [jumble (fn [a b] (str (apply str (reverse (str a))) b))] + (reduce jumble "abcd")))) + (is (= "cafrogbd" (let [jumble (fn [a b] (str (apply str (reverse (str a))) b))] + (reduce jumble "frog" "abcd")))) + (is (= [3] (nthnext [1 2 3] 2))) + (assert (not= 1 2)) + (is (not (not= 1 1))) + (is (not (not-empty []))) + (is (boolean (not-empty [1 2 3]))) + (is (= "joel" (min-key count "joel" "tom servo" "crooooooooow"))) + (is (= "crooooooooow" (max-key count "joel" "tom servo" "crooooooooow"))) + (is (= (partition-all 4 [1 2 3 4 5 6 7 8 9]) + [[1 2 3 4] [5 6 7 8] [9]])) + (is (= (partition-all 4 2 [1 2 3 4 5 6 7 8 9]) + [[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]])) + (is (= [true true] (take-while true? [true true 2 3 4]))) + (is (= [[true true] [false false false] [true true]] + (partition-by true? [true true false false false true true]))) + (is (= [0 2 4 6 8 10] (take-nth 2 [0 1 2 3 4 5 6 7 8 9 10]))) + (let [sf (some-fn number? keyword? symbol?)] + (testing "Testing some-fn" + (is (sf :foo 1)) + (is (sf :foo)) + (is (sf 'bar 1)) + (is (not (sf [] ()))))) + (let [ep (every-pred number? zero?)] + (testing "Testing every-pred" + (is (ep 0 0 0)) + (is (not (ep 1 2 3 0))))) + (is ((complement number?) :foo)) + (is (= [1 [2 3] [1 2 3]] ((juxt first rest seq) [1 2 3]))) + (is (= 5 (max 1 2 3 4 5))) + (is (= 5 (max 5 4 3 2 1))) + (is (= 5.5 (max 1 2 3 4 5 5.5))) + (is (= 1 (min 5 4 3 2 1))) + (is (= 1 (min 1 2 3 4 5))) + (is (= 0.5 (min 5 4 3 0.5 2 1))) + (let [x (array 1 2 3)] + (testing "Testing setting property on JS array" + (set! (.-foo x) :hello) + (is (= (.-foo x) :hello)))) + ;; last + (is (= nil (last nil))) + (is (= 3 (last [1 2 3]))) + ;; dotimes + (let [s (atom [])] + (dotimes [n 5] + (swap! s conj n)) + (is (= [0 1 2 3 4] @s))) + ;; doseq + (let [v [1 2 3 4 5] + s (atom ())] + (doseq [n v] (swap! s conj n)) + (is (= @s (reverse v)))) + ;; memoize + (let [f (memoize (fn [] (rand)))] + (f) + (is (= (f) (f)))) + ;; range + (is (= (range 10) (list 0 1 2 3 4 5 6 7 8 9))) + (is (= (range 10 20) (list 10 11 12 13 14 15 16 17 18 19))) + (is (= (range 10 20 2) (list 10 12 14 16 18))) + (is (= (take 20 (range)) (list 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19))) + ;; group-by + (let [d (group-by second {:a 1 :b 2 :c 1 :d 4 :e 1 :f 2})] + (testing "group-by" + (is (= 3 (count (get d 1)))) + (is (= 2 (count (get d 2)))) + (is (= 1 (count (get d 4)))))) + (is (= {1 2 3 4 5 6} (merge {1 2} {3 4} {5 6}))) + (is (= {1 2 3 4} (merge {1 2} {3 4} nil))) + ;; frequencies + (is (= {:a 3 :b 2} (frequencies [:a :b :a :b :a]))) + ;; reductions + (is (= [1 3 6 10 15] (reductions + [1 2 3 4 5]))) + ;; keep + (is (= [1 3 5 7 9] (keep #(if (odd? %) %) [1 2 3 4 5 6 7 8 9 10]))) + (is (= [2 4 6 8 10] (keep #(if (even? %) %) [1 2 3 4 5 6 7 8 9 10]))) + ;; keep-indexed + (is (= [1 3 5 7 9] (keep-indexed #(if (odd? %1) %2) [0 1 2 3 4 5 6 7 8 9 10]))) + (is (= [2 4 5] (keep-indexed #(if (pos? %2) %1) [-9 0 29 -7 45 3 -8]))) + ;; map-indexed + (is (= [[0 :a] [1 :b] [2 :c]] (map-indexed #(vector % %2) [:a :b :c]))) + ;; merge-with + (is (= '{"Foo" ("foo" "FOO" "fOo"), "Bar" ("bar" "BAR" "BAr"), "Baz" ["baz"], "Qux" ["qux" "quux"]} + (merge-with concat + {"Foo" ["foo" "FOO"] + "Bar" ["bar" "BAR"] + "Baz" ["baz"]} + {"Foo" ["fOo"] + "Bar" ["BAr"] + "Qux" ["qux" "quux"]}))) + (is (= {:a 111, :b 102, :c 13} + (merge-with + + {:a 1 :b 2 :c 3} + {:a 10 :c 10} + {:a 100 :b 100}))) + (is (= {:a 3, :b 102, :c 13} + (apply merge-with [+ + {:a 1 :b 100} + {:a 1 :b 2 :c 3} + {:a 1 :c 10}]))) + (is (= '[a c e] (replace '[a b c d e] [0 2 4]))) + (is (= [:one :zero :two :zero] + (replace {0 :zero 1 :one 2 :two} '(1 0 2 0)))) + ;; split-at + (is (= [[1 2] [3 4 5]] (split-at 2 [1 2 3 4 5]))) + ;; split-with + (is (= [[1 2 3] [4 5]] (split-with (partial >= 3) [1 2 3 4 5]))) + ;; trampoline + (is (= 10000 (trampoline (fn f [n] (if (>= n 10000) n #(f (inc n)))) 0))) + ;; vary-meta + (is (= {:a 1} (meta (vary-meta [] assoc :a 1)))) + (is (= {:a 1 :b 2} (meta (vary-meta (with-meta [] {:b 2}) assoc :a 1)))) + )) + +(deftest test-booleans + (testing "Testing boolean predicates" + (is (= [true false true false false false] + [(true? true) + (true? false) + (false? false) + (false? true) + (true? js/undefined) + (false? js/undefined)])))) + +(deftest test-fn-with-metadata (let [f (fn [x] (* x 2)) m {:foo "bar"} mf (with-meta f m)] - (assert (nil? (meta f))) - (assert (fn? mf)) - (assert (= 4 (mf 2))) - (assert (= 4 (apply mf [2]))) - (assert (= (meta mf) m))) - + (testing "Testing functions with metadata" + (is (nil? (meta f))) + (is (fn? mf)) + (is (= 4 (mf 2))) + (is (= 4 (apply mf [2]))) + (is (= (meta mf) m))))) + +(deftest test-atoms-and-volatile (let [a (atom 0)] - (assert (= 0 (deref a))) - (assert (= 1 (swap! a inc))) - (assert (= false (compare-and-set! a 0 42))) - (assert (= true (compare-and-set! a 1 7))) - (assert (nil? (meta a))) - (assert (nil? (get-validator a)))) + (testing "Testing basic atom operations" + (is (= 0 (deref a))) + (is (= 1 (swap! a inc))) + (is (= false (compare-and-set! a 0 42))) + (is (= true (compare-and-set! a 1 7))) + (is (nil? (meta a))) + (is (nil? (get-validator a))))) (let [a (atom 0)] - (assert (= 1 (swap! a + 1))) - (assert (= 4 (swap! a + 1 2))) - (assert (= 10 (swap! a + 1 2 3))) - (assert (= 20 (swap! a + 1 2 3 4)))) + (testing "Testing swap!" + (is (= 1 (swap! a + 1))) + (is (= 4 (swap! a + 1 2))) + (is (= 10 (swap! a + 1 2 3))) + (is (= 20 (swap! a + 1 2 3 4))))) (let [a (atom [1] :validator coll? :meta {:a 1})] - (assert (= coll? (get-validator a))) - (assert (= {:a 1} (meta a))) - (alter-meta! a assoc :b 2) - (assert (= {:a 1 :b 2} (meta a)))) + (testing "Testing atom validators" + (is (= coll? (get-validator a))) + (is (= {:a 1} (meta a))) + (alter-meta! a assoc :b 2) + (is (= {:a 1 :b 2} (meta a))))) (let [v (volatile! 1)] - (assert (volatile? v)) - (assert (not (volatile? (atom 1)))) - (assert (= 2 (vreset! v 2))) - (assert (= 3 (vswap! v inc))) - (assert (= 3 @v))) - (assert (nil? (empty nil))) - (let [e-lazy-seq (empty (with-meta (lazy-seq (cons :a nil)) {:b :c}))] - (assert (seq? e-lazy-seq)) - (assert (empty? e-lazy-seq)) - (assert (= {:b :c} (meta e-lazy-seq)))) - (let [e-list (empty '^{:b :c} (1 2 3))] - (assert (seq? e-list)) - (assert (empty? e-list))) - (let [e-elist (empty '^{:b :c} ())] - (assert (seq? e-elist)) - (assert (empty? e-elist)) - (assert (= :c (get (meta e-elist) :b)))) - (let [e-cons (empty (with-meta (cons :a nil) {:b :c}))] - (assert (seq? e-cons)) - (assert (empty? e-cons)) - (assert (= {:b :c} (meta e-cons)))) - (let [e-vec (empty ^{:b :c} [:a :d :g])] - (assert (vector? e-vec)) - (assert (empty? e-vec)) - (assert (= {:b :c} (meta e-vec)))) - (let [e-omap (empty ^{:b :c} {:a :d :g :h})] - (assert (map? e-omap)) - (assert (empty? e-omap)) - (assert (= {:b :c} (meta e-omap)))) - (let [e-hmap (empty ^{:b :c} {[1 2] :d :g :h})] - (assert (map? e-hmap)) - (assert (empty? e-hmap)) - (assert (= {:b :c} (meta e-hmap)))) - + (testing "Testing volatile" + (is (volatile? v)) + (is (not (volatile? (atom 1)))) + (is (= 2 (vreset! v 2))) + (is (= 3 (vswap! v inc))) + (is (= 3 @v))))) + +(deftest test-empy-and-seq + (testing "Testing empty & seq" + (is (nil? (empty nil))) + (let [e-lazy-seq (empty (with-meta (lazy-seq (cons :a nil)) {:b :c}))] + (testing "lazy seq" + (is (seq? e-lazy-seq)) + (is (empty? e-lazy-seq)) + (is (= {:b :c} (meta e-lazy-seq))))) + (let [e-list (empty '^{:b :c} (1 2 3))] + (testing "list" + (is (seq? e-list)) + (is (empty? e-list)))) + (let [e-elist (empty '^{:b :c} ())] + (testing "list with metadata" + (is (seq? e-elist)) + (is (empty? e-elist)) + (is (= :c (get (meta e-elist) :b))))) + (let [e-cons (empty (with-meta (cons :a nil) {:b :c}))] + (testing "cons" + (is (seq? e-cons)) + (is (empty? e-cons)) + (is (= {:b :c} (meta e-cons))))) + (let [e-vec (empty ^{:b :c} [:a :d :g])] + (testing "vector" + (is (vector? e-vec)) + (is (empty? e-vec)) + (is (= {:b :c} (meta e-vec))))) + (let [e-omap (empty ^{:b :c} {:a :d :g :h})] + (testing "map" + (is (map? e-omap)) + (is (empty? e-omap)) + (is (= {:b :c} (meta e-omap))))) + (let [e-hmap (empty ^{:b :c} {[1 2] :d :g :h})] + (testing "map with complex keys" + (is (map? e-hmap)) + (is (empty? e-hmap)) + (is (= {:b :c} (meta e-hmap))))))) + +(deftest test-try-catch (let [a (atom nil)] - (assert (= 1 (try 1))) - (assert (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 2)))) - (assert (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 1 2)))) - (assert (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 2) (catch :default e 3)))) - (assert (= 3 (try 1 (throw true) (catch js/Error e 2) (catch :default e 3)))) - (assert (= 2 (try 1 (throw 2) (catch js/Error e 3) (catch :default e e)))) - (assert (= 1 (try 1 (finally (reset! a 42))))) - (assert (= 42 (deref a)))) - - (assert (= [3] (nthnext [1 2 3] 2))) + (testing "Testing try/catch" + (is (= 1 (try 1))) + (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 2)))) + (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 1 2)))) + (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 2) (catch :default e 3)))) + (is (= 3 (try 1 (throw true) (catch js/Error e 2) (catch :default e 3)))) + (is (= 2 (try 1 (throw 2) (catch js/Error e 3) (catch :default e e)))) + (is (= 1 (try 1 (finally (reset! a 42))))) + (is (= 42 (deref a)))))) + +(deftest test-list-comprehensions (let [v [1 2 3]] - (assert (= v (for [e v] e))) - (assert (= [[1 1] [2 4] [3 9]] (for [e v :let [m (* e e)]] [e m]))) - (assert (= [1 2] (for [e v :while (< e 3)] e))) - (assert (= [3] (for [e v :when (> e 2)] e))) - (assert (= [[1 1] [2 4]] (for [e v :while (< e 3) :let [m (* e e)]] [e m])))) - (assert (not= 1 2)) - (assert (not (not= 1 1))) - (assert (not (not-empty []))) - (assert (boolean (not-empty [1 2 3]))) - (assert (= "joel" (min-key count "joel" "tom servo" "crooooooooow"))) - (assert (= "crooooooooow" (max-key count "joel" "tom servo" "crooooooooow"))) - (assert (= (partition-all 4 [1 2 3 4 5 6 7 8 9]) - [[1 2 3 4] [5 6 7 8] [9]])) - (assert (= (partition-all 4 2 [1 2 3 4 5 6 7 8 9]) - [[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]])) - (assert (= [true true] (take-while true? [true true 2 3 4]))) - (assert (= [[true true] [false false false] [true true]] - (partition-by true? [true true false false false true true]))) - (assert (= [0 2 4 6 8 10] (take-nth 2 [0 1 2 3 4 5 6 7 8 9 10]))) + (testing "Testing list comprehensions" + (is (= v (for [e v] e))) + (is (= [[1 1] [2 4] [3 9]] (for [e v :let [m (* e e)]] [e m]))) + (is (= [1 2] (for [e v :while (< e 3)] e))) + (is (= [3] (for [e v :when (> e 2)] e))) + (is (= [[1 1] [2 4]] (for [e v :while (< e 3) :let [m (* e e)]] [e m])))))) + +(deftest test-partial-and-comp (let [a10 (partial + 10) a20 (partial + 10 10) a21 (partial + 10 10 1) a22 (partial + 10 5 4 3) a23 (partial + 10 5 4 3 1)] - (assert (= 110 (a10 100))) - (assert (= 120 (a20 100))) - (assert (= 121 (a21 100))) - (assert (= 122 (a22 100))) - (assert (= 123 (a23 100)))) + (testing "Testing partial" + (is (= 110 (a10 100))) + (is (= 120 (a20 100))) + (is (= 121 (a21 100))) + (is (= 122 (a22 100))) + (is (= 123 (a23 100))))) (let [n2 (comp first rest) n3 (comp first rest rest) n4 (comp first rest rest rest) n5 (comp first rest rest rest rest) n6 (comp first rest rest rest rest rest)] - (assert (= 2 (n2 [1 2 3 4 5 6 7]))) - (assert (= 3 (n3 [1 2 3 4 5 6 7]))) - (assert (= 4 (n4 [1 2 3 4 5 6 7]))) - (assert (= 5 (n5 [1 2 3 4 5 6 7]))) - (assert (= 6 (n6 [1 2 3 4 5 6 7])))) - (let [sf (some-fn number? keyword? symbol?)] - (assert (sf :foo 1)) - (assert (sf :foo)) - (assert (sf 'bar 1)) - (assert (not (sf [] ())))) - (let [ep (every-pred number? zero?)] - (assert (ep 0 0 0)) - (assert (not (ep 1 2 3 0)))) - (assert ((complement number?) :foo)) - (assert (= [1 [2 3] [1 2 3]] ((juxt first rest seq) [1 2 3]))) - (assert (= 5 (max 1 2 3 4 5))) - (assert (= 5 (max 5 4 3 2 1))) - (assert (= 5.5 (max 1 2 3 4 5 5.5))) - (assert (= 1 (min 5 4 3 2 1))) - (assert (= 1 (min 1 2 3 4 5))) - (assert (= 0.5 (min 5 4 3 0.5 2 1))) - (let [x (array 1 2 3)] - (set! (.-foo x) :hello) - (assert (= (.-foo x) :hello))) - - (assert (set [])) - (assert (= #{} (set []))) - (assert (= #{} (hash-set))) - (assert (identical? cljs.core.PersistentHashSet (type (hash-set)))) - - (assert (= #{"foo"} (set ["foo"]))) - (assert (= #{"foo"} (hash-set "foo"))) - (assert (= #{1 2 3} #{1 3 2})) - (assert (= #{#{1 2 3} [4 5 6] {7 8} 9 10} - #{10 9 [4 5 6] {7 8} #{1 2 3}})) - (assert (not (= #{nil [] {} 0 #{}} #{}))) - (assert (= (count #{nil [] {} 0 #{}}) 5)) - (assert (= (conj #{1} 1) #{1})) - (assert (= (conj #{1} 2) #{2 1})) - (assert (= #{} (-empty #{1 2 3 4}))) - (assert (= (reduce + #{1 2 3 4 5}) 15)) - (assert (= 4 (get #{1 2 3 4} 4))) - (assert (contains? #{1 2 3 4} 4)) - (assert (contains? #{[] nil 0 {} #{}} {})) - (assert (contains? #{[1 2 3]} [1 2 3])) - (assert (not (contains? (-disjoin #{1 2 3} 3) 3))) - (assert (neg? -1)) - (assert (not (neg? 1))) - (assert (neg? -1.765)) - (assert (not (neg? 0))) - (assert (= [true false true false true false true false] - (map integer? - [1 1.00001 0x7e7 [] (- 88 1001991881) :foo 0 "0"]))) - (assert (= [true false true false true false] - (map odd? [1 2 3 4 -1 0]))) - (assert (= [true false true false true true] - (map even? [2 3 4 5 -2 0]))) - (assert (contains? {:a 1 :b 2} :a)) - (assert (not (contains? {:a 1 :b 2} :z))) - (assert (contains? [5 6 7] 1)) - (assert (contains? [5 6 7] 2)) - (assert (not (contains? [5 6 7] 3))) - (assert (contains? (to-array [5 6 7]) 1)) - (assert (contains? (to-array [5 6 7]) 2)) - (assert (not (contains? (to-array [5 6 7]) 3))) - (assert (not (contains? nil 42))) - (assert (contains? "f" 0)) - (assert (not (contains? "f" 55))) - (assert (distinct? 1 2 3)) - (assert (not (distinct? 1 2 3 1))) - - ;; distinct - (assert (= (distinct ()) ())) - (assert (= (distinct '(1)) '(1))) - (assert (= (distinct '(1 2 3 1 1 1)) '(1 2 3))) - (assert (= (distinct [1 1 1 2]) '(1 2))) - (assert (= (distinct [1 2 1 2]) '(1 2))) - (assert (= (distinct "a") ["a"])) - (assert (= (distinct "abcabab") ["a" "b" "c"])) - (assert (= (distinct ["abc" "abc"]) ["abc"])) - (assert (= (distinct [nil nil]) [nil])) - (assert (= (distinct [0.0 0.0]) [0.0])) - (assert (= (distinct ['sym 'sym]) '[sym])) - (assert (= (distinct [:kw :kw]) [:kw])) - (assert (= (distinct [42 42]) [42])) - (assert (= (distinct [[] []]) [[]])) - (assert (= (distinct ['(1 2) '(1 2)]) '[(1 2)])) - (assert (= (distinct [() ()]) [()])) - (assert (= (distinct [[1 2] [1 2]]) [[1 2]])) - (assert (= (distinct [{:a 1 :b 2} {:a 1 :b 2}]) [{:a 1 :b 2}])) - (assert (= (distinct [{} {}]) [{}])) - (assert (= (distinct [#{1 2} #{1 2}]) [#{1 2}])) - (assert (= (distinct [#{} #{}]) [#{}])) - - ;;regexps - (let [r1 #"foo", r2 (re-pattern r1)] - (assert (= r1 r2))) - (assert (= (str (re-pattern "f(.)o")) (str (js* "/f(.)o/")))) - (assert (= (re-find (re-pattern "foo") "foo bar foo baz foo zot") "foo")) - (assert (= (re-find (re-pattern "f(.)o") "foo bar foo baz foo zot") ["foo" "o"])) - (assert (= (re-matches (re-pattern "foo") "foo") "foo")) - (assert (= (re-matches (re-pattern "foo") "foo bar foo baz foo zot") nil)) - (assert (= (re-matches (re-pattern "foo.*") "foo bar foo baz foo zot") "foo bar foo baz foo zot")) - (assert (= (re-seq (re-pattern "foo") "foo bar foo baz foo zot") (list "foo" "foo" "foo"))) - (assert (= (re-seq (re-pattern "f(.)o") "foo bar foo baz foo zot") (list ["foo" "o"] ["foo" "o"] ["foo" "o"]))) - (assert (= (re-matches (re-pattern "(?i)foo") "Foo") "Foo")) - ; new RegExp("").source => "(?:)" on webkit-family envs, "" elsewhere - (assert (#{"#\"\"" "#\"(?:)\""} (pr-str #""))) - - ;; destructuring - (assert (= [2 1] (let [[a b] [1 2]] [b a]))) - (assert (= #{1 2} (let [[a b] [1 2]] #{a b}))) - (assert (= [1 2] (let [{a :a b :b} {:a 1 :b 2}] [a b]))) - (assert (= [1 2] (let [{:keys [a b]} {:a 1 :b 2}] [a b]))) - (assert (= [1 2 [1 2]] (let [[a b :as v] [1 2]] [a b v]))) - (assert (= [1 42] (let [{:keys [a b] :or {b 42}} {:a 1}] [a b]))) - (assert (= [1 nil] (let [{:keys [a b] :or {c 42}} {:a 1}] [a b]))) - (assert (= [2 1] (let [[a b] '(1 2)] [b a]))) - (assert (= {1 2} (let [[a b] [1 2]] {a b}))) - (assert (= [2 1] (let [[a b] (seq [1 2])] [b a]))) - - ;; update-in - (assert (= {:foo {:bar {:baz 1}}} - (update-in {:foo {:bar {:baz 0}}} [:foo :bar :baz] inc))) - (assert (= {:foo 1 :bar 2 :baz 10} - (update-in {:foo 1 :bar 2 :baz 3} [:baz] + 7))) - (assert (= [{:foo 1, :bar 2} {:foo 1, :bar 3}] - (update-in [{:foo 1 :bar 2}, {:foo 1 :bar 2}] [1 :bar] inc))) - (assert (= [{:foo {:bar 2}} {:foo {:bar 3}}] - (update-in [{:foo {:bar 2}}, {:foo {:bar 2}}] [1 :foo :bar] inc))) - - ;; assoc-in - (assert (= {:foo {:bar {:baz 100}}} - (assoc-in {:foo {:bar {:baz 0}}} [:foo :bar :baz] 100))) - (assert (= {:foo 1 :bar 2 :baz 100} - (assoc-in {:foo 1 :bar 2 :baz 3} [:baz] 100))) - (assert (= [{:foo [{:bar 2} {:baz 3}]} {:foo [{:bar 2} {:baz 100}]}] - (assoc-in [{:foo [{:bar 2} {:baz 3}]}, {:foo [{:bar 2} {:baz 3}]}] - [1 :foo 1 :baz] 100))) - (assert (= [{:foo 1, :bar 2} {:foo 1, :bar 100}] - (assoc-in [{:foo 1 :bar 2}, {:foo 1 :bar 2}] [1 :bar] 100))) - - ;; get-in - (assert (= 1 (get-in {:foo 1 :bar 2} [:foo]))) - (assert (= 2 (get-in {:foo {:bar 2}} [:foo :bar]))) - (assert (= 1 (get-in [{:foo 1}, {:foo 2}] [0 :foo]))) - (assert (= 4 (get-in [{:foo 1 :bar [{:baz 1}, {:buzz 2}]}, {:foo 3 :bar [{:baz 3}, {:buzz 4}]}] - [1 :bar 1 :buzz]))) - - ;; arrays - (let [a (to-array [1 2 3])] - (assert (= [10 20 30] (seq (amap a i ret (* 10 (aget a i)))))) - (assert (= 6 (areduce a i ret 0 (+ ret (aget a i))))) - (assert (= (seq a) (seq (to-array [1 2 3])))) - (assert (= 42 (aset a 0 42))) - (assert (not= (seq a) (seq (to-array [1 2 3])))) - (assert (not= a (aclone a)))) - - (let [a (array (array 1 2 3) (array 4 5 6))] - (assert (= (aget a 0 1) 2)) - (assert (= (apply aget a [0 1]) 2)) - (assert (= (aget a 1 1) 5)) - (assert (= (apply aget a [1 1]) 5)) - (aset a 0 0 "foo") - (assert (= (aget a 0 0) "foo")) - (apply aset a [0 0 "bar"]) - (assert (= (aget a 0 0) "bar"))) - - ;; sort - (assert (= [1 2 3 4 5] (sort [5 3 1 4 2]))) - (assert (= [1 2 3 4 5] (sort < [5 3 1 4 2]))) - (assert (= [5 4 3 2 1] (sort > [5 3 1 4 2]))) - - ;; sort-by - (assert (= ["a" [ 1 2] "foo"] (sort-by count ["foo" "a" [1 2]]))) - (assert (= ["foo" [1 2] "a"] (sort-by count > ["foo" "a" [1 2]]))) - - ;; shuffle - (let [coll [1 2 3 4 5 6 7 8 9 10] - ; while it is technically possible for this test to fail with a false negative, - ; it's _extraordinarily_ unlikely. - shuffles (filter #(not= coll %) (take 100 (iterate shuffle coll)))] - (assert (not (empty? shuffles)))) - - ;; js->clj - (assert (= {"a" 1, "b" 2} (js->clj (js* "{\"a\":1,\"b\":2}")))) - (assert (= {"a" nil} (js->clj (js* "{\"a\":null}")))) - (assert (= {} (js->clj (js* "{}")))) - (assert (= {"a" true, "b" false} (js->clj (js* "{\"a\":true,\"b\":false}")))) - (assert (= {:a 1, :b 2} (js->clj (js* "{\"a\":1,\"b\":2}") :keywordize-keys true))) - (assert (= [[{:a 1, :b 2} {:a 1, :b 2}]] - (js->clj (js* "[[{\"a\":1,\"b\":2}, {\"a\":1,\"b\":2}]]") :keywordize-keys true))) - (assert (= [[{:a 1, :b 2} {:a 1, :b 2}]] - (js->clj [[{:a 1, :b 2} {:a 1, :b 2}]]))) - (assert (= (js->clj nil) nil)) - - ;; clj->js - (assert (= (clj->js 'a) "a")) - (assert (= (clj->js :a) "a")) - (assert (= (clj->js "a") "a")) - (assert (= (clj->js 1) 1)) - (assert (= (clj->js nil) (js* "null"))) - (assert (= (clj->js true) (js* "true"))) - (assert (goog/isArray (clj->js []))) - (assert (goog/isArray (clj->js #{}))) - (assert (goog/isArray (clj->js '()))) - (assert (goog/isObject (clj->js {}))) - (assert (= (aget (clj->js {:a 1}) "a") 1)) - (assert (= (-> (clj->js {:a {:b {{:k :ey} :d}}}) - (aget "a") - (aget "b") - (aget "{:k :ey}")) - "d")) - - ;; last - (assert (= nil (last nil))) - (assert (= 3 (last [1 2 3]))) - - ;; dotimes - (let [s (atom [])] - (dotimes [n 5] - (swap! s conj n)) - (assert (= [0 1 2 3 4] @s))) - - ;; doseq - (let [v [1 2 3 4 5] - s (atom ())] - (doseq [n v] (swap! s conj n)) - (assert (= @s (reverse v)))) - - ;; delay + (testing "Testing comp" + (is (= 2 (n2 [1 2 3 4 5 6 7]))) + (is (= 3 (n3 [1 2 3 4 5 6 7]))) + (is (= 4 (n4 [1 2 3 4 5 6 7]))) + (is (= 5 (n5 [1 2 3 4 5 6 7]))) + (is (= 6 (n6 [1 2 3 4 5 6 7])))))) + +(deftest test-sets + (testing "Testing persistent sets" + (is (set [])) + (is (= #{} (set []))) + (is (= #{} (hash-set))) + (is (identical? cljs.core.PersistentHashSet (type (hash-set)))) + + (is (= #{"foo"} (set ["foo"]))) + (is (= #{"foo"} (hash-set "foo"))) + (is (= #{1 2 3} #{1 3 2})) + (is (= #{#{1 2 3} [4 5 6] {7 8} 9 10} + #{10 9 [4 5 6] {7 8} #{1 2 3}})) + (is (not (= #{nil [] {} 0 #{}} #{}))) + (is (= (count #{nil [] {} 0 #{}}) 5)) + (is (= (conj #{1} 1) #{1})) + (is (= (conj #{1} 2) #{2 1})) + (is (= #{} (-empty #{1 2 3 4}))) + (is (= (reduce + #{1 2 3 4 5}) 15)) + (is (= 4 (get #{1 2 3 4} 4))) + (is (contains? #{1 2 3 4} 4)) + (is (contains? #{[] nil 0 {} #{}} {})) + (is (contains? #{[1 2 3]} [1 2 3])) + (is (not (contains? (-disjoin #{1 2 3} 3) 3))) + (is (= #{1 2 3} (disj #{1 2 3}))) + (is (= #{1 2} (disj #{1 2 3} 3))) + (is (= #{1} (disj #{1 2 3} 2 3))) + (is (nil? (disj nil :foo))))) + +(deftest test-contains? + (testing "Testing contains?" + (is (contains? {:a 1 :b 2} :a)) + (is (not (contains? {:a 1 :b 2} :z))) + (is (contains? [5 6 7] 1)) + (is (contains? [5 6 7] 2)) + (is (not (contains? [5 6 7] 3))) + (is (contains? (to-array [5 6 7]) 1)) + (is (contains? (to-array [5 6 7]) 2)) + (is (not (contains? (to-array [5 6 7]) 3))) + (is (not (contains? nil 42))) + (is (contains? "f" 0)) + (is (not (contains? "f" 55))))) + +(deftest test-distinct + (testing "Testing distinct? & distinct" + (is (distinct? 1 2 3)) + (is (not (distinct? 1 2 3 1))) + (is (= (distinct ()) ())) + (is (= (distinct '(1)) '(1))) + (is (= (distinct '(1 2 3 1 1 1)) '(1 2 3))) + (is (= (distinct [1 1 1 2]) '(1 2))) + (is (= (distinct [1 2 1 2]) '(1 2))) + (is (= (distinct "a") ["a"])) + (is (= (distinct "abcabab") ["a" "b" "c"])) + (is (= (distinct ["abc" "abc"]) ["abc"])) + (is (= (distinct [nil nil]) [nil])) + (is (= (distinct [0.0 0.0]) [0.0])) + (is (= (distinct ['sym 'sym]) '[sym])) + (is (= (distinct [:kw :kw]) [:kw])) + (is (= (distinct [42 42]) [42])) + (is (= (distinct [[] []]) [[]])) + (is (= (distinct ['(1 2) '(1 2)]) '[(1 2)])) + (is (= (distinct [() ()]) [()])) + (is (= (distinct [[1 2] [1 2]]) [[1 2]])) + (is (= (distinct [{:a 1 :b 2} {:a 1 :b 2}]) [{:a 1 :b 2}])) + (is (= (distinct [{} {}]) [{}])) + (is (= (distinct [#{1 2} #{1 2}]) [#{1 2}])) + (is (= (distinct [#{} #{}]) [#{}])))) + +(deftest test-regexps + (testing "Testing regexps" + (let [r1 #"foo", r2 (re-pattern r1)] + (is (= r1 r2))) + (is (= (str (re-pattern "f(.)o")) (str (js* "/f(.)o/")))) + (is (= (re-find (re-pattern "foo") "foo bar foo baz foo zot") "foo")) + (is (= (re-find (re-pattern "f(.)o") "foo bar foo baz foo zot") ["foo" "o"])) + (is (= (re-matches (re-pattern "foo") "foo") "foo")) + (is (= (re-matches (re-pattern "foo") "foo bar foo baz foo zot") nil)) + (is (= (re-matches (re-pattern "foo.*") "foo bar foo baz foo zot") "foo bar foo baz foo zot")) + (is (= (re-seq (re-pattern "foo") "foo bar foo baz foo zot") (list "foo" "foo" "foo"))) + (is (= (re-seq (re-pattern "f(.)o") "foo bar foo baz foo zot") (list ["foo" "o"] ["foo" "o"] ["foo" "o"]))) + (is (= (re-matches (re-pattern "(?i)foo") "Foo") "Foo")) + ; new RegExp("").source => "(?:)" on webkit-family envs, "" elsewhere + (is (#{"#\"\"" "#\"(?:)\""} (pr-str #""))))) + +(deftest test-destructuring + (testing "Testing destructuring" + (is (= [2 1] (let [[a b] [1 2]] [b a]))) + (is (= #{1 2} (let [[a b] [1 2]] #{a b}))) + (is (= [1 2] (let [{a :a b :b} {:a 1 :b 2}] [a b]))) + (is (= [1 2] (let [{:keys [a b]} {:a 1 :b 2}] [a b]))) + (is (= [1 2 [1 2]] (let [[a b :as v] [1 2]] [a b v]))) + (is (= [1 42] (let [{:keys [a b] :or {b 42}} {:a 1}] [a b]))) + (is (= [1 nil] (let [{:keys [a b] :or {c 42}} {:a 1}] [a b]))) + (is (= [2 1] (let [[a b] '(1 2)] [b a]))) + (is (= {1 2} (let [[a b] [1 2]] {a b}))) + (is (= [2 1] (let [[a b] (seq [1 2])] [b a]))) + (testing "namespaced keys" + (let [{:keys [:a :b]} {:a 1 :b 2}] + (testing "basic" + (is (= 1 a)) + (is (= 2 b)))) + (let [{:keys [:a/b :c/d]} {:a/b 1 :c/d 2}] + (testing "keyword syntax" + (is (= 1 b)) + (is (= 2 d)))) + (let [{:keys [a/b c/d]} {:a/b 1 :c/d 2}] + (testing "symbol syntax" + (is (= 1 b)) + (is (= 2 d)))) + (let [{:syms [a/b c/d]} {'a/b 1 'c/d 2}] + (testing ":syms" + (is (= 1 b)) + (is (= 2 d)))) + (let [{:keys [::s/x ::s/y]} {:clojure.string/x 1 :clojure.string/y 2}] + (testing ":keys" + (is (= x 1)) + (is (= y 2)))) + ))) + +(deftest test-in-operations + (testing "Testing update-in" + (is (= {:foo {:bar {:baz 1}}} + (update-in {:foo {:bar {:baz 0}}} [:foo :bar :baz] inc))) + (is (= {:foo 1 :bar 2 :baz 10} + (update-in {:foo 1 :bar 2 :baz 3} [:baz] + 7))) + (is (= [{:foo 1, :bar 2} {:foo 1, :bar 3}] + (update-in [{:foo 1 :bar 2}, {:foo 1 :bar 2}] [1 :bar] inc))) + (is (= [{:foo {:bar 2}} {:foo {:bar 3}}] + (update-in [{:foo {:bar 2}}, {:foo {:bar 2}}] [1 :foo :bar] inc)))) + (testing "Testing assoc-in" + (is (= {:foo {:bar {:baz 100}}} + (assoc-in {:foo {:bar {:baz 0}}} [:foo :bar :baz] 100))) + (is (= {:foo 1 :bar 2 :baz 100} + (assoc-in {:foo 1 :bar 2 :baz 3} [:baz] 100))) + (is (= [{:foo [{:bar 2} {:baz 3}]} {:foo [{:bar 2} {:baz 100}]}] + (assoc-in [{:foo [{:bar 2} {:baz 3}]}, {:foo [{:bar 2} {:baz 3}]}] + [1 :foo 1 :baz] 100))) + (is (= [{:foo 1, :bar 2} {:foo 1, :bar 100}] + (assoc-in [{:foo 1 :bar 2}, {:foo 1 :bar 2}] [1 :bar] 100)))) + (testing "Testing get-in" + (is (= 1 (get-in {:foo 1 :bar 2} [:foo]))) + (is (= 2 (get-in {:foo {:bar 2}} [:foo :bar]))) + (is (= 1 (get-in [{:foo 1}, {:foo 2}] [0 :foo]))) + (is (= 4 (get-in [{:foo 1 :bar [{:baz 1}, {:buzz 2}]}, {:foo 3 :bar [{:baz 3}, {:buzz 4}]}] + [1 :bar 1 :buzz])))) + ) + +(deftest test-arrays + (testing "Testing array operations" + (let [a (to-array [1 2 3])] + (testing "basic ops" + (is (= [10 20 30] (seq (amap a i ret (* 10 (aget a i)))))) + (is (= 6 (areduce a i ret 0 (+ ret (aget a i))))) + (is (= (seq a) (seq (to-array [1 2 3])))) + (is (= 42 (aset a 0 42))) + (is (not= (seq a) (seq (to-array [1 2 3])))) + (is (not= a (aclone a))))) + (let [a (array (array 1 2 3) (array 4 5 6))] + (testing "aget" + (is (= (aget a 0 1) 2)) + (is (= (apply aget a [0 1]) 2)) + (is (= (aget a 1 1) 5)) + (is (= (apply aget a [1 1]) 5)) + (aset a 0 0 "foo") + (is (= (aget a 0 0) "foo")) + (apply aset a [0 0 "bar"]) + (is (= (aget a 0 0) "bar")))))) + +(deftest test-rearrange-sequential + (testing "Test rearranging sequential collections" + (is (= [1 2 3 4 5] (sort [5 3 1 4 2]))) + (is (= [1 2 3 4 5] (sort < [5 3 1 4 2]))) + (is (= [5 4 3 2 1] (sort > [5 3 1 4 2]))) + (is (= ["a" [ 1 2] "foo"] (sort-by count ["foo" "a" [1 2]]))) + (is (= ["foo" [1 2] "a"] (sort-by count > ["foo" "a" [1 2]]))) + (let [coll [1 2 3 4 5 6 7 8 9 10] + ;; while it is technically possible for this test to fail with a false negative, + ;; it's _extraordinarily_ unlikely. + shuffles (filter #(not= coll %) (take 100 (iterate shuffle coll)))] + (is (not (empty? shuffles)))) + )) + +(deftest test-js-clj-conversions + (testing "Testing JS / CLJS data conversions" + (testing "js->clj" + (is (= {"a" 1, "b" 2} (js->clj (js* "{\"a\":1,\"b\":2}")))) + (is (= {"a" nil} (js->clj (js* "{\"a\":null}")))) + (is (= {} (js->clj (js* "{}")))) + (is (= {"a" true, "b" false} (js->clj (js* "{\"a\":true,\"b\":false}")))) + (is (= {:a 1, :b 2} (js->clj (js* "{\"a\":1,\"b\":2}") :keywordize-keys true))) + (is (= [[{:a 1, :b 2} {:a 1, :b 2}]] + (js->clj (js* "[[{\"a\":1,\"b\":2}, {\"a\":1,\"b\":2}]]") :keywordize-keys true))) + (is (= [[{:a 1, :b 2} {:a 1, :b 2}]] + (js->clj [[{:a 1, :b 2} {:a 1, :b 2}]]))) + (is (= (js->clj nil) nil))) + (testing "clj->js" + (is (= (clj->js 'a) "a")) + (is (= (clj->js :a) "a")) + (is (= (clj->js "a") "a")) + (is (= (clj->js 1) 1)) + (is (= (clj->js nil) (js* "null"))) + (is (= (clj->js true) (js* "true"))) + (is (goog/isArray (clj->js []))) + (is (goog/isArray (clj->js #{}))) + (is (goog/isArray (clj->js '()))) + (is (goog/isObject (clj->js {}))) + (is (= (aget (clj->js {:a 1}) "a") 1)) + (is (= (-> (clj->js {:a {:b {{:k :ey} :d}}}) + (aget "a") + (aget "b") + (aget "{:k :ey}")) + "d"))))) + +(deftest test-delay (let [a (atom 0) d (delay (swap! a inc))] - (assert (false? (realized? d))) - (assert (zero? @a)) ;; delay hasn't triggered yet - (assert (= 1 @d)) ;; trigger it - (assert (= 1 @a)) ;; make sure side effect has happened - (assert (true? (realized? d))) - (assert (= 1 @d)) ;; body doesn't happen again - (assert (= 1 @a)) ;; atom hasn't changed either - (assert (= (force d) @d)) - (assert (= 1 (force 1)))) ;; you can safely force non-delays - - ;; assoc - (assert (= {1 2 3 4} (assoc {} 1 2 3 4))) - (assert (= {1 2} (assoc {} 1 2))) - (assert (= [42 2] (assoc [1 2] 0 42))) - - ;; dissoc - (assert (= {} (dissoc {1 2 3 4} 1 3))) - (assert (= {1 2} (dissoc {1 2 3 4} 3))) - (assert (nil? (dissoc nil :foo))) - - ;; disj - (assert (= #{1 2 3} (disj #{1 2 3}))) - (assert (= #{1 2} (disj #{1 2 3} 3))) - (assert (= #{1} (disj #{1 2 3} 2 3))) - (assert (nil? (disj nil :foo))) - - ;; memoize - (let [f (memoize (fn [] (rand)))] - (f) - (assert (= (f) (f)))) - - ;; find - (assert (= (find {} :a) nil)) - (assert (= (find {:a 1} :a) [:a 1])) - (assert (= (find {:a 1} :b) nil)) - (assert (= (find {:a 1 :b 2} :a) [:a 1])) - (assert (= (find {:a 1 :b 2} :b) [:b 2])) - (assert (= (find {:a 1 :b 2} :c) nil)) - (assert (= (find {} nil) nil)) - (assert (= (find {:a 1} nil) nil)) - (assert (= (find {:a 1 :b 2} nil) nil)) - (assert (= (find [1 2 3] 0) [0 1])) - - ;; mod,quot,rem - (assert (= (quot 4 2) 2)) - (assert (= (quot 3 2) 1)) - (assert (= (quot 6 4) 1)) - (assert (= (quot 0 5) 0)) - (assert (= (quot 42 5) 8)) - (assert (= (quot 42 -5) -8)) - (assert (= (quot -42 -5) 8)) - (assert (= (quot 9 3) 3)) - (assert (= (quot 9 -3) -3)) - (assert (= (quot -9 3) -3)) - (assert (= (quot 2 -5) 0)) - (assert (= (quot -2 5) 0)) - (assert (= (quot 0 3) 0)) - (assert (= (quot 0 -3) 0)) - - (assert (= (mod 4 2) 0)) - (assert (= (mod 3 2) 1)) - (assert (= (mod 6 4) 2)) - (assert (= (mod 0 5) 0)) - (assert (= (mod 4.5 2.0) 0.5)) - (assert (= (mod 42 5) 2)) - (assert (= (mod 9 3) 0)) - (assert (= (mod 9 -3) 0)) - (assert (= (mod -9 3) 0)) - (assert (= (mod -9 -3) 0)) - (assert (= (mod 0 3) 0)) - (assert (= (mod 3216478362187432 432143214) 120355456)) - - (assert (= (rem 4 2) 0)) - (assert (= (rem 0 5) 0)) - (assert (= (rem 4.5 2.0) 0.5)) - (assert (= (rem 42 5) 2)) - (assert (= (rem 2 5) 2)) - (assert (= (rem 2 -5) 2)) - (assert (= (rem 0 3) 0)) - - ;; range - (assert (= (range 10) (list 0 1 2 3 4 5 6 7 8 9))) - (assert (= (range 10 20) (list 10 11 12 13 14 15 16 17 18 19))) - (assert (= (range 10 20 2) (list 10 12 14 16 18))) - (assert (= (take 20 (range)) (list 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19))) - - ;; group-by - (let [d (group-by second {:a 1 :b 2 :c 1 :d 4 :e 1 :f 2})] - (assert (= 3 (count (get d 1)))) - (assert (= 2 (count (get d 2)))) - (assert (= 1 (count (get d 4))))) - - (assert (= {1 2 3 4 5 6} (merge {1 2} {3 4} {5 6}))) - (assert (= {1 2 3 4} (merge {1 2} {3 4} nil))) - - ;; frequencies - (assert (= {:a 3 :b 2} (frequencies [:a :b :a :b :a]))) - - ;; reductions - (assert (= [1 3 6 10 15] (reductions + [1 2 3 4 5]))) - - ;; keep - (assert (= [1 3 5 7 9] (keep #(if (odd? %) %) [1 2 3 4 5 6 7 8 9 10]))) - (assert (= [2 4 6 8 10] (keep #(if (even? %) %) [1 2 3 4 5 6 7 8 9 10]))) - - ;; keep-indexed - (assert (= [1 3 5 7 9] (keep-indexed #(if (odd? %1) %2) [0 1 2 3 4 5 6 7 8 9 10]))) - (assert (= [2 4 5] (keep-indexed #(if (pos? %2) %1) [-9 0 29 -7 45 3 -8]))) - - ;; map-indexed - (assert (= [[0 :a] [1 :b] [2 :c]] (map-indexed #(vector % %2) [:a :b :c]))) - - ;; merge-with - (assert (= '{"Foo" ("foo" "FOO" "fOo"), "Bar" ("bar" "BAR" "BAr"), "Baz" ["baz"], "Qux" ["qux" "quux"]} - (merge-with concat - {"Foo" ["foo" "FOO"] - "Bar" ["bar" "BAR"] - "Baz" ["baz"]} - {"Foo" ["fOo"] - "Bar" ["BAr"] - "Qux" ["qux" "quux"]}))) - (assert (= {:a 111, :b 102, :c 13} - (merge-with + - {:a 1 :b 2 :c 3} - {:a 10 :c 10} - {:a 100 :b 100}))) - - (assert (= {:a 3, :b 102, :c 13} - (apply merge-with [+ - {:a 1 :b 100} - {:a 1 :b 2 :c 3} - {:a 1 :c 10}]))) - - (assert (= '[a c e] (replace '[a b c d e] [0 2 4]))) - (assert (= [:one :zero :two :zero] - (replace {0 :zero 1 :one 2 :two} '(1 0 2 0)))) - - ;; split-at - (assert (= [[1 2] [3 4 5]] (split-at 2 [1 2 3 4 5]))) - - ;; split-with - (assert (= [[1 2 3] [4 5]] (split-with (partial >= 3) [1 2 3 4 5]))) - - ;; trampoline - (assert (= 10000 (trampoline (fn f [n] (if (>= n 10000) n #(f (inc n)))) 0))) - - ;; vary-meta - (assert (= {:a 1} (meta (vary-meta [] assoc :a 1)))) - (assert (= {:a 1 :b 2} (meta (vary-meta (with-meta [] {:b 2}) assoc :a 1)))) - - ;; hierarchy tests - (derive ::rect ::shape) - (derive ::square ::rect) - - (assert (= #{:cljs.core-test/shape} (parents ::rect))) - (assert (= #{:cljs.core-test/rect :cljs.core-test/shape} (ancestors ::square))) - (assert (= #{:cljs.core-test/rect :cljs.core-test/square} (descendants ::shape))) - (assert (true? (isa? 42 42))) - (assert (true? (isa? ::square ::shape))) - - (derive cljs.core.ObjMap ::collection) - (derive cljs.core.PersistentHashSet ::collection) - (assert (true? (isa? cljs.core.ObjMap ::collection))) - (assert (true? (isa? cljs.core.PersistentHashSet ::collection))) - (assert (false? (isa? cljs.core.IndexedSeq ::collection))) - ;; ?? (isa? String Object) - (assert (true? (isa? [::square ::rect] [::shape ::shape]))) - ;; ?? (ancestors java.util.ArrayList) - - ;; ?? isa? based dispatch tests - - ;; prefer-method test - (defmulti bar (fn [x y] [x y])) - (defmethod bar [::rect ::shape] [x y] :rect-shape) - (defmethod bar [::shape ::rect] [x y] :shape-rect) - - ;;(bar ::rect ::rect) - ;; -> java.lang.IllegalArgumentException: - ;; Multiple methods match dispatch value: - ;; [:cljs.core-test/rect :cljs.core-test/rect] -> [:cljs.core-test/rect :cljs.core-test/shape] - ;; and [:cljs.core-test/shape :cljs.core-test/rect], - ;; and neither is preferred - - (assert (zero? (count (prefers bar)))) - (prefer-method bar [::rect ::shape] [::shape ::rect]) - (assert (= 1 (count (prefers bar)))) - (assert (= :rect-shape (bar ::rect ::rect))) - (assert (= :rect-shape (apply (-get-method bar [::rect ::shape]) [::rect ::shape]))) - - ;; nested data structures tests - (defmulti nested-dispatch (fn [m] (-> m :a :b))) - (defmethod nested-dispatch :c [m] :nested-a) - (defmethod nested-dispatch :default [m] :nested-default) - (assert (= :nested-a (nested-dispatch {:a {:b :c}}))) - - (defmulti nested-dispatch2 ffirst) - (defmethod nested-dispatch2 :a [m] :nested-a) - (defmethod nested-dispatch2 :default [m] :nested-default) - (assert (= :nested-a (nested-dispatch2 [[:a :b]]))) - - ;; general tests - (defmulti foo1 (fn [& args] (first args))) - (defmethod foo1 :a [& args] :a-return) - (defmethod foo1 :default [& args] :default-return) - (assert (= :a-return (foo1 :a))) - (assert (= :default-return (foo1 1))) - - (defmulti area :Shape) - (defn rect [wd ht] {:Shape :Rect :wd wd :ht ht}) - (defn circle [radius] {:Shape :Circle :radius radius}) - (defmethod area :Rect [r] + (testing "Testing delay" + (is (false? (realized? d))) + (is (zero? @a)) ;; delay hasn't triggered yet + (is (= 1 @d)) ;; trigger it + (is (= 1 @a)) ;; make sure side effect has happened + (is (true? (realized? d))) + (is (= 1 @d)) ;; body doesn't happen again + (is (= 1 @a)) ;; atom hasn't changed either + (is (= (force d) @d)) + (is (= 1 (force 1))))) ;; you can safely force non-delays + ) + +(deftest test-hierarchy + (testing "Testing hierarchy operations" + (derive ::rect ::shape) + (derive ::square ::rect) + (is (= #{:cljs.core-test/shape} (parents ::rect))) + (is (= #{:cljs.core-test/rect :cljs.core-test/shape} (ancestors ::square))) + (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 cljs.core.PersistentHashSet ::collection) + (is (true? (isa? cljs.core.ObjMap ::collection))) + (is (true? (isa? cljs.core.PersistentHashSet ::collection))) + (is (false? (isa? cljs.core.IndexedSeq ::collection))) + ;; ?? (isa? String Object) + (is (true? (isa? [::square ::rect] [::shape ::shape]))) + ;; ?? (ancestors java.util.ArrayList) + ;; ?? isa? based dispatch tests + )) + +(defmulti bar (fn [x y] [x y])) +(defmethod bar [::rect ::shape] [x y] :rect-shape) +(defmethod bar [::shape ::rect] [x y] :shape-rect) + +;;(bar ::rect ::rect) +;; -> java.lang.IllegalArgumentException: +;; Multiple methods match dispatch value: +;; [:cljs.core-test/rect :cljs.core-test/rect] -> [:cljs.core-test/rect :cljs.core-test/shape] +;; and [:cljs.core-test/shape :cljs.core-test/rect], +;; and neither is preferred + +(deftest test-multimethods-1 + (testing "Testing basic multimethod usage" + (is (zero? (count (prefers bar)))) + (prefer-method bar [::rect ::shape] [::shape ::rect]) + (is (= 1 (count (prefers bar)))) + (is (= :rect-shape (bar ::rect ::rect))) + (is (= :rect-shape (apply (-get-method bar [::rect ::shape]) [::rect ::shape]))) + )) + +(defmulti nested-dispatch (fn [m] (-> m :a :b))) +(defmethod nested-dispatch :c [m] :nested-a) +(defmethod nested-dispatch :default [m] :nested-default) + +(defmulti nested-dispatch2 ffirst) +(defmethod nested-dispatch2 :a [m] :nested-a) +(defmethod nested-dispatch2 :default [m] :nested-default) + +(defmulti foo1 (fn [& args] (first args))) +(defmethod foo1 :a [& args] :a-return) +(defmethod foo1 :default [& args] :default-return) + +(defmulti area :Shape) +(defn rect [wd ht] {:Shape :Rect :wd wd :ht ht}) +(defn circle [radius] {:Shape :Circle :radius radius}) +(defmethod area :Rect [r] (* (:wd r) (:ht r))) - (defmethod area :Circle [c] +(defmethod area :Circle [c] (* Math/PI (* (:radius c) (:radius c)))) - (defmethod area :default [x] :oops) - (def r (rect 4 13)) - (def c (circle 12)) - (assert (= 52 (area r))) - (assert (= :oops (area {}))) - - ;; CLJS-863 - (defmulti foo2 (fn [])) - (defmethod foo2 :default [] :foo) - (assert (= :foo (foo2))) - - ;; remove method tests - (assert (= 2 (count (methods bar)))) - (remove-method bar [::rect ::shape]) - (assert (= 1 (count (methods bar)))) - (remove-all-methods bar) - (assert (zero? (count (methods bar)))) - - ;; test apply - (defmulti apply-multi-test (fn ([_] 0) ([_ _] 0) ([_ _ _] 0))) - (defmethod apply-multi-test 0 - ([x] :one) - ([x y] :two) - ([x y & r] [:three r])) - (assert (= [:three '(2)] (apply apply-multi-test [0 1 2]))) - +(defmethod area :default [x] :oops) +(defmulti foo2 (fn [])) +(defmethod foo2 :default [] :foo) + +(defmulti apply-multi-test (fn ([_] 0) ([_ _] 0) ([_ _ _] 0))) +(defmethod apply-multi-test 0 + ([x] :one) + ([x y] :two) + ([x y & r] [:three r])) + +;; CLJS-469, helpful exception message on bad dispatch +(defmulti no-dispatch-value :test) + +;; custom hierarchy +(def my-map-hierarchy + (atom (-> (make-hierarchy) + (derive (type (obj-map)) ::map) + (derive (type (array-map)) ::map) + (derive (type (hash-map)) ::map) + (derive (type (sorted-map)) ::map)))) + +(defmulti my-map? type :hierarchy my-map-hierarchy) +(defmethod my-map? ::map [_] true) +(defmethod my-map? :default [_] false) + +(deftest test-multimethods-2 + (let [r (rect 4 13) + c (circle 12)] + (testing "Testing multimethod edge cases" + (is (= :nested-a (nested-dispatch {:a {:b :c}}))) + (is (= :nested-a (nested-dispatch2 [[:a :b]]))) + (is (= :a-return (foo1 :a))) + (is (= :default-return (foo1 1))) + (is (= 52 (area r))) + (is (= :oops (area {}))) + ;; CLJS-863 + (is (= :foo (foo2))) + ;; remove method tests + ;; TODO: these side-effects return fn values + ;; will interact badly with cljs.test need to fix - David + ;; (is (= 2 (count (methods bar)))) + ;; (remove-method bar [::rect ::shape]) + ;; (is (= 1 (count (methods bar)))) + ;; (remove-all-methods bar) + ;; (is (zero? (count (methods bar)))) + (is (= [:three '(2)] (apply apply-multi-test [0 1 2]))) + (is (try + (no-dispatch-value {:test :test}) + (catch js/Error e + (not= -1 (.indexOf (.-message e) "cljs.core-test/no-dispatch-value"))))) + (is (every? true? + (for [m [(obj-map) (array-map) (hash-map) (sorted-map)]] + (my-map? m)))) + (is (every? true? + (for [not-m [[] 1 "asdf" :foo]] + (not (my-map? not-m))))) +))) + +(deftest test-range + (testing "Testing Range" + ;; Range + (is (= (range 0 10 3) (list 0 3 6 9))) + (is (= (count (range 0 10 3)) 4)) + (is (= (range 0 -10 -3) (list 0 -3 -6 -9))) + (is (= (count (range 0 -10 -3)) 4)) + (is (= (range -10 10 3) (list -10 -7 -4 -1 2 5 8))) + (is (= (count (range -10 10 3)) 7)) + (is (= (range 0 1 1) (list 0))) + (is (= (range 0 -3 -1) (list 0 -1 -2))) + (is (= (range 3 0 -1) (list 3 2 1))) + (is (= (range 0 10 -1) (list))) + (is (= (range 0 1 0) (list))) + (is (= (range 10 0 1) (list))) + (is (= (range 0 0 0) (list))) + (is (= (count (range 0 10 -1)) 0)) + (is (= (count (range 0 1 0)) 0)) + (is (= (count (range 10 0 1)) 0)) + (is (= (count (range 0 0 0)) 0)) + (is (= (take 3 (range 1 0 0)) (list 1 1 1))) + (is (= (take 3 (range 3 1 0)) (list 3 3 3))) + )) + +(deftest test-sorted-map + (testing "Testing sorted maps" + ;; PersistentTreeMap + (let [m1 (sorted-map) + c2 (comp - compare) + m2 (sorted-map-by c2)] + (testing "basic ops 1" + (is (identical? cljs.core.PersistentTreeMap (type m1))) + (is (identical? cljs.core.PersistentTreeMap (type m2))) + (is (identical? compare (.-comp m1))) + (is (zero? (count m1))) + (is (zero? (count m2))) + (is (nil? (rseq m1))) + (let [m1 (assoc m1 :foo 1 :bar 2 :quux 3) + m2 (assoc m2 :foo 1 :bar 2 :quux 3)] + (testing "basic ops 2" + (is (= (count m1) 3)) + (is (= (count m2) 3)) + (is (= (seq m1) (list [:bar 2] [:foo 1] [:quux 3]))) + (is (= (seq m2) (list [:quux 3] [:foo 1] [:bar 2]))) + (is (= (seq m1) (rseq m2))) + (is (= (seq m2) (rseq m1))) + (is (= (conj m1 [:wibble 4]) {:foo 1 :bar 2 :quux 3 :wibble 4})) + (is (= (count (conj m1 [:wibble 4])) 4)) + (is (= (conj m2 [:wibble 4]) {:foo 1 :bar 2 :quux 3 :wibble 4})) + (is (= (count (conj m2 [:wibble 4])) 4)) + (is (= (map key (assoc m1 nil 4)) (list nil :bar :foo :quux))) + (is (= (map key (assoc m2 nil 4)) (list :quux :foo :bar nil))))))) + (let [m (->> [[0 10] [20 30] [10 20] [50 60] [30 40] [40 50]] + (mapcat (partial apply range)) + (mapcat #(list % %)) + (apply sorted-map)) + s1 (map #(vector % %) (range 60)) + s2 (map #(vector % %) (range 59 -1 -1))] + (testing "edge cases 1" + (is (= (count m) 60)) + (is (= (seq m) s1)) + (is (= (rseq m) s2)))) + (let [m (sorted-map :foo 1 :bar 2 :quux 3)] + (testing "edge cases 2" + (is (= (dissoc m :foo) (hash-map :bar 2 :quux 3))) + (is (= (count (dissoc m :foo)) 2)) + (is (= (hash m) (hash (hash-map :foo 1 :bar 2 :quux 3)))) + (is (= (subseq m < :foo) (list [:bar 2]))) + (is (= (subseq m <= :foo) (list [:bar 2] [:foo 1]))) + (is (= (subseq m > :foo) (list [:quux 3]))) + (is (= (subseq m >= :foo) (list [:foo 1] [:quux 3]))) + (is (= (map #(reduce (fn [_ x] x) %) m) (list 2 1 3))) + (is (= (map #(reduce (fn [x _] x) 7 %) m) (list 7 7 7))))) + )) + +(deftest test-sorted-sets + (let [s1 (sorted-set) + c2 (comp - compare) + s2 (sorted-set-by c2) + c3 #(compare (quot %1 2) (quot %2 2)) + s3 (sorted-set-by c3) + s4 (sorted-set-by <)] + (testing "Testing sorted set" + (is (identical? cljs.core.PersistentTreeSet (type s1))) + (is (identical? cljs.core.PersistentTreeSet (type s2))) + (is (identical? compare (-comparator s1))) + (is (zero? (count s1))) + (is (zero? (count s2))) + (is (nil? (rseq s1))) + (let [s1 (conj s1 1 2 3) + s2 (conj s2 1 2 3) + s3 (conj s3 1 2 3 7 8 9) + s4 (conj s4 1 2 3)] + (testing "basic ops" + (is (= (hash s1) (hash s2))) + (is (= (hash s1) (hash #{1 2 3}))) + (is (= (seq s1) (list 1 2 3))) + (is (= (rseq s1) (list 3 2 1))) + (is (= (seq s2) (list 3 2 1))) + (is (= (rseq s2) (list 1 2 3))) + (is (= (count s1) 3)) + (is (= (count s2) 3)) + (is (= (count s3) 4)) + (is (= (get s3 0) 1)) + (is (= (subseq s3 > 5) (list 7 8))) + (is (= (subseq s3 > 6) (list 8))) + (is (= (subseq s3 >= 6) (list 7 8))) + (is (= (subseq s3 >= 12) nil)) + (is (= (subseq s3 < 0) (list))) + (is (= (subseq s3 < 5) (list 1 2))) + (is (= (subseq s3 < 6) (list 1 2))) + (is (= (subseq s3 <= 6) (list 1 2 7))) + (is (= (subseq s3 >= 2 <= 6) (list 2 7))) + (is (= (subseq s4 >= 2 < 3) (list 2))) + (let [s1 (disj s1 2) + s2 (disj s2 2)] + (testing "edge cases" + (is (= (seq s1) (list 1 3))) + (is (= (rseq s1) (list 3 1))) + (is (= (seq s2) (list 3 1))) + (is (= (rseq s2) (list 1 3))) + (is (= (count s1) 2)) + (is (= (count s2) 2))))))))) + +(deftest test-transducers + (testing "Testing transducers" + (is (= (sequence (map inc) (array 1 2 3)) '(2 3 4))) + (is (= (apply str (sequence (map #(.toUpperCase %)) "foo")) "FOO")) + (is (== (hash [1 2 3]) (hash (sequence (map inc) (range 3))))) + (is (= [1 2 3] (sequence (map inc) (range 3)))) + (is (= (sequence (map inc) (range 3)) [1 2 3])) + (is (= (sequence (remove even?) (range 10)) '(1 3 5 7 9))) + (is (= (sequence (take 5) (range 10)) + '(0 1 2 3 4))) + (is (= (sequence (take-while #(< % 5)) (range 10)) + '(0 1 2 3 4))) + (is (= (sequence (drop 5) (range 10)) + '(5 6 7 8 9))) + (is (= (sequence (drop-while #(< % 5)) (range 10)) + '(5 6 7 8 9))) + (is (= (sequence (take-nth 2) (range 10)) + '(0 2 4 6 8))) + (is (= (sequence (replace {:foo :bar}) '(:foo 1 :foo 2)) + '(:bar 1 :bar 2))) + (let [ret (into [] (map inc) (range 3))] + (is (and (vector? ret) (= ret '(1 2 3))))) + (let [ret (into [] (filter even?) (range 10))] + (is (and (vector? ret) (= ret '(0 2 4 6 8))))) + (is (= (map inc (sequence (map inc) (range 3))) + '(2 3 4))) + (is (= (sequence (dedupe) [1 1 2 2 3 3]) + '(1 2 3))) + (is (= (mapcat reverse [[3 2 1 0] [6 5 4] [9 8 7]]) + (range 10))) + (is (= (sequence (mapcat reverse) [[3 2 1 0] [6 5 4] [9 8 7]]) + (range 10))) + (is (= (seq (eduction (map inc) [1 2 3])) '(2 3 4))) + (is (= (sequence (partition-by #{:split}) [1 2 3 :split 4 5 6]) + '([1 2 3] [:split] [4 5 6]))) + (is (= (sequence (partition-all 3) '(1 2 3 4 5)) + '([1 2 3] [4 5]))) + (is (= (sequence (keep identity) [1 nil 2 nil 3]) + '(1 2 3))) + (is (= (keep-indexed identity [:foo nil :bar nil :baz]) + (sequence (keep-indexed identity) [:foo nil :bar nil :baz]))) + (let [xform (comp (map inc) + (filter even?) + (dedupe) + (mapcat range) + (partition-all 3) + (partition-by #(< (apply + %) 7)) + (mapcat flatten) + (random-sample 1.0) + (take-nth 1) + (keep #(when (odd? %) (* % %))) + (keep-indexed #(when (even? %1) (* %1 %2))) + (replace {2 "two" 6 "six" 18 "eighteen"}) + (take 11) + (take-while #(not= 300 %)) + (drop 1) + (drop-while string?) + (remove string?)) + data (vec (interleave (range 18) (range 20)))] + (is (= (sequence xform data) '(36 200 10)))) + (let [xf (map #(+ %1 %2))] + (is (= (sequence xf [0 0] [1 2]) [1 2]))) + )) + +(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})) + )) + +(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")) + (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" + (is (= (seq (.-value (.next eiter))) (seq #js [:foo "bar"]))) + (is (= (seq (.-value (.next eiter))) (seq #js [:baz "woz"]))) + (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-mumur-support + (testing "Testing murmur support" + ;; int-rotate-left + (is (== (int-rotate-left (bit-or 0x87654321 0) 4) (bit-or 0x76543218 0))) + (is (== (int-rotate-left (bit-or 0x87654321 0) 8) (bit-or 0x65432187 0))) + (is (== (int-rotate-left (bit-or 0x80000000 0) 1) 0x1)) + (is (== (int-rotate-left (bit-or 0x78123456 0) 4) (bit-or 0x81234567 0))) + (is (== (int-rotate-left (bit-or 0xffffffff 0) 4) (bit-or 0xffffffff 0))) + + ;; imul + (is (== (imul 3 3) 9)) + (is (== (imul -1 8) -8)) + (is (== (imul -2 -2) 4)) + (is (== (imul 0xffffffff 5) -5)) + (is (== (imul 0xfffffffe 5) -10)) + )) + +(deftest test-print-knobs + (testing "Testing printing knobs" + (is (= (binding [*print-length* 1] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 ...]")) + (is (= (binding [*print-length* 2] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 2 ...]")) + (is (= (binding [*print-length* 10] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 2 3 4 5 6 7 8 9 0]")) + ;; CLJS-804 + (is (= (binding [*print-length* 10] (str {:foo "bar"})) + "{:foo \"bar\"}")) + (is (= (binding [*print-length* 1] (str {:foo "bar" :baz "woz"})) + "{:foo \"bar\", ...}")) + (is (= (binding [*print-length* 10] (str {:foo "bar" :baz "woz"})) + "{:foo \"bar\", :baz \"woz\"}"))) + ) + +(defrecord PrintMe [a b]) + +(deftest test-pr-str + (testing "Testing pr-str" + (is (= (pr-str 1) "1")) + (is (= (pr-str -1) "-1")) + (is (= (pr-str -1.5) "-1.5")) + (is (= (pr-str [3 4]) "[3 4]")) + (is (= (pr-str "foo") "\"foo\"")) + (is (= (pr-str :hello) ":hello")) + (is (= (pr-str 'goodbye) "goodbye")) + ;;(is (= (pr-str #{1 2 3}) "#{1 2 3}")) + (is (= (pr-str '(7 8 9)) "(7 8 9)")) + (is (= (pr-str '(deref foo)) "(deref foo)")) + (is (= (pr-str '(quote bar)) "(quote bar)")) + (is (= (pr-str 'foo/bar) "foo/bar")) + (is (= (pr-str \a) "\"a\"")) + (is (= (pr-str :foo/bar) ":foo/bar")) + (is (= (pr-str nil) "nil")) + (is (= (pr-str true) "true")) + (is (= (pr-str false) "false")) + (is (= (pr-str "string") "\"string\"")) + (is (= (pr-str ["üñîçó∂£" :ทดสอบ/你好 'こんにちは]) "[\"üñîçó∂£\" :ทดสอบ/你好 こんにちは]")) + (is (= (pr-str "escape chars \t \r \n \\ \" \b \f") "\"escape chars \\t \\r \\n \\\\ \\\" \\b \\f\"")) + (is (= (pr-str (PrintMe. 1 2)) "#cljs.core-test.PrintMe{:a 1, :b 2}")) + (is (= (pr-str (js/Date. "2010-11-12T13:14:15.666-05:00")) + "#inst \"2010-11-12T18:14:15.666-00:00\"")) + (is (every? true? + (for [month (range 1 13) + day (range 1 29) + hour (range 1 23)] + (let [pad (fn [n] + (if (< n 10) + (str "0" n) + n)) + inst (str "2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-00:00")] + (= (pr-str (js/Date. inst)) (str "#inst \"" inst "\"")))))) + (let [uuid-str "550e8400-e29b-41d4-a716-446655440000" + uuid (UUID. uuid-str)] + (is (= (pr-str uuid) (str "#uuid \"" uuid-str "\"")))) + ;; pr-str PersistentQueueSeq - CLJS-800 + (is (= (pr-str (rest (conj cljs.core.PersistentQueue.EMPTY 1 2 3))) "(2 3)")) + )) + +(deftest test-inext + (testing "Testing INext" + (is (= nil (next nil))) + (is (= nil (next (seq (array 1))))) + (is (= '(2 3) (next (seq (array 1 2 3))))) + (is (= nil (next (reverse (seq (array 1)))))) + (is (= '(2 1) (next (reverse (seq (array 1 2 3)))))) + (is (= nil (next (cons 1 nil)))) + (is (= '(2 3) (next (cons 1 (cons 2 (cons 3 nil)))))) + (is (= nil (next (lazy-seq (cons 1 nil))))) + (is (= '(2 3) (next (lazy-seq + (cons 1 + (lazy-seq + (cons 2 + (lazy-seq (cons 3 nil))))))))) + (is (= nil (next (list 1)))) + (is (= '(2 3) (next (list 1 2 3)))) + (is (= nil (next [1]))) + (is (= '(2 3) (next [1 2 3]))) + (is (= nil (next (range 1 2)))) + (is (= '(2 3) (next (range 1 4)))) + )) + +(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))))))))) + +(deftest test-reader-literals + (testing "Testing reader literals" + (is (= #queue [1] (into cljs.core.PersistentQueue.EMPTY [1]))) + (is (not= #queue [1 2] (into cljs.core.PersistentQueue.EMPTY [1]))) + (is (= #inst "2010-11-12T18:14:15.666-00:00" + #inst "2010-11-12T13:14:15.666-05:00")) + (is (= #uuid "550e8400-e29b-41d4-a716-446655440000" + #uuid "550e8400-e29b-41d4-a716-446655440000")) + (is (= 42 + (get {#uuid "550e8400-e29b-41d4-a716-446655440000" 42} + #uuid "550e8400-e29b-41d4-a716-446655440000"))) + )) + +(deftest test-uuid + (testing "Testing UUID" + (is (= (UUID. "550e8400-e29b-41d4-a716-446655440000") + (UUID. "550e8400-e29b-41d4-a716-446655440000"))) + (is (not (identical? (UUID. "550e8400-e29b-41d4-a716-446655440000") + (UUID. "550e8400-e29b-41d4-a716-446655440000")))) + (is (= 42 (get {(UUID. "550e8400-e29b-41d4-a716-446655440000") 42} + (UUID. "550e8400-e29b-41d4-a716-446655440000") + :not-at-all-found))) + (is (= :not-at-all-found + (get {(UUID. "550e8400-e29b-41d4-a716-446655440000") 42} + (UUID. "666e8400-e29b-41d4-a716-446655440000") + :not-at-all-found))) + )) + +(deftest test-comparable + (testing "Testing IComparable" + (is (= 0 (compare false false))) + (is (= -1 (compare false true))) + (is (= 1 (compare true false))) + + (is (= -1 (compare 0 1))) + (is (= -1 (compare -1 1))) + (is (= 0 (compare 1 1))) + (is (= 1 (compare 1 0))) + (is (= 1 (compare 1 -1))) + + (is (= 0 (compare "cljs" "cljs"))) + (is (= 0 (compare :cljs :cljs))) + (is (= 0 (compare 'cljs 'cljs))) + (is (= -1 (compare "a" "b"))) + (is (= -1 (compare :a :b))) + (is (= -1 (compare 'a 'b))) + ;; cases involving ns + (is (= -1 (compare :b/a :c/a))) + (is (= -1 (compare :c :a/b))) + (is (= 1 (compare :a/b :c))) + (is (= -1 (compare 'b/a 'c/a))) + (is (= -1 (compare 'c 'a/b))) + (is (= 1 (compare 'a/b 'c))) + + ;; This is different from clj. clj gives -2 next 3 tests + (is (= -1 (compare "a" "c"))) + (is (= -1 (compare :a :c))) + (is (= -1 (compare 'a 'c))) + + (is (= -1 (compare [1 2] [1 1 1]))) + (is (= -1 (compare [1 2] [1 2 1]))) + (is (= -1 (compare [1 1] [1 2]))) + (is (= 0 (compare [1 2] [1 2]))) + (is (= 1 (compare [1 2] [1 1]))) + (is (= 1 (compare [1 1 1] [1 2]))) + (is (= 1 (compare [1 1 2] [1 1 1]))) + + (is (= -1 (compare (subvec [1 2 3] 1) (subvec [1 2 4] 1)))) + (is (= 0 (compare (subvec [1 2 3] 1) (subvec [1 2 3] 1)))) + (is (= 1 (compare (subvec [1 2 4] 1) (subvec [1 2 3] 1))))) + ) - ;; CLJS-469, helpful exception message on bad dispatch - (defmulti no-dispatch-value :test) - (try - (no-dispatch-value {:test :test}) - (catch js/Error e - (assert (not= -1 (.indexOf (.-message e) "cljs.core-test/no-dispatch-value"))))) - - ;; custom hierarchy tests - (def my-map-hierarchy (atom (-> (make-hierarchy) - (derive (type (obj-map)) ::map) - (derive (type (array-map)) ::map) - (derive (type (hash-map)) ::map) - (derive (type (sorted-map)) ::map)))) - (defmulti my-map? type :hierarchy my-map-hierarchy) - (defmethod my-map? ::map [_] true) - (defmethod my-map? :default [_] false) - (doseq [m [(obj-map) (array-map) (hash-map) (sorted-map)]] - (assert (my-map? m))) - (doseq [not-m [[] 1 "asdf" :foo]] - (assert (not (my-map? not-m)))) - - ;; Range - (assert (= (range 0 10 3) (list 0 3 6 9))) - (assert (= (count (range 0 10 3)) 4)) - (assert (= (range 0 -10 -3) (list 0 -3 -6 -9))) - (assert (= (count (range 0 -10 -3)) 4)) - (assert (= (range -10 10 3) (list -10 -7 -4 -1 2 5 8))) - (assert (= (count (range -10 10 3)) 7)) - (assert (= (range 0 1 1) (list 0))) - (assert (= (range 0 -3 -1) (list 0 -1 -2))) - (assert (= (range 3 0 -1) (list 3 2 1))) - (assert (= (range 0 10 -1) (list))) - (assert (= (range 0 1 0) (list))) - (assert (= (range 10 0 1) (list))) - (assert (= (range 0 0 0) (list))) - (assert (= (count (range 0 10 -1)) 0)) - (assert (= (count (range 0 1 0)) 0)) - (assert (= (count (range 10 0 1)) 0)) - (assert (= (count (range 0 0 0)) 0)) - (assert (= (take 3 (range 1 0 0)) (list 1 1 1))) - (assert (= (take 3 (range 3 1 0)) (list 3 3 3))) - - ;; PersistentVector - (let [pv (vec (range 97))] - (assert (= (nth pv 96) 96)) - (assert (= (nth pv 97 nil) nil)) - (assert (= (pv 96) 96)) - (assert (nil? (rseq []))) - (assert (= (reverse pv) (rseq pv)))) - - - (let [pv (vec (range 33))] - (assert (= pv - (-> pv - pop - pop - (conj 31) - (conj 32))))) - - (let [stack1 (pop (vec (range 97))) - stack2 (pop stack1)] - (assert (= 95 (peek stack1))) - (assert (= 94 (peek stack2)))) - - ;; CLJS-513 - (let [sentinel (js-obj)] - (assert (identical? sentinel (try ([] 0) (catch js/Error _ sentinel))))) - - ;; subvec - (let [v1 (vec (range 10)) - v2 (vec (range 5)) - s (subvec v1 2 8)] - (assert (= s - (-> v1 - (subvec 2) - (subvec 0 6)) - (->> v1 - (drop 2) - (take 6)))) - (assert (= 6 (count s))) - (assert (= [2 3 4 5 6] (pop s))) - (assert (= 7 (peek s))) - (assert (= [2 3 4 5 6 7 1] - (assoc s 6 1) - (conj s 1))) - (assert (= 27 (reduce + s))) - (assert (= s (vec s))) ; pour into plain vector - (let [m {:x 1}] (assert (= m (meta (with-meta s m))))) - ;; go outside ranges - (assert (= :fail (try (subvec v2 0 6) (catch js/Error e :fail)))) - (assert (= :fail (try (subvec v2 6 10) (catch js/Error e :fail)))) - (assert (= :fail (try (subvec v2 6 10) (catch js/Error e :fail)))) - (assert (= :fail (try (subvec v2 3 6) (catch js/Error e :fail)))) - ;; no layered subvecs - (assert (identical? v1 (.-v (subvec s 1 4)))) - ;; CLJS-513 - (let [sentinel (js-obj) - s (subvec [0 1 2 3] 1 2)] - (assert (identical? sentinel (try (s -1) (catch js/Error _ sentinel)))) - (assert (identical? sentinel (try (s 1) (catch js/Error _ sentinel))))) - ;; CLJS-765 - (let [sv1 (subvec [0 1 2 3] 1 2) - sv2 (subvec [0 1 2 3] 1 1)] - (assert (= (rseq sv1) '(1))) - (assert (nil? (rseq sv2))))) - - ;; TransientVector +;; ============================================================================= +;; Tickets + +(deftest test-383 + (testing "Testing CLJS-383" + (let [f1 (fn f1 ([] 0) ([a] 1) ([a b] 2) ([a b c & more] 3)) + f2 (fn f2 ([x] :foo) ([x y & more] (apply f1 y more)))] + (is (= 1 (f2 1 2)))) + (let [f (fn ([]) ([a & more] more))] + (is (nil? (f :foo)))) + (is (nil? (array-seq (array 1) 1)))) ) + +(deftest test-513 + (testing "Testing CLJS-513" + (let [sentinel (js-obj)] + (is (identical? sentinel (try ([] 0) (catch js/Error _ sentinel))))))) + +(defn test-stuff [] (let [v1 (vec (range 15 48)) v2 (vec (range 40 57)) v1 (persistent! (assoc! (conj! (pop! (transient v1)) :foo) 0 :quux)) @@ -1349,96 +1848,6 @@ s)))) (assert (= s (set (remove #(zero? (mod % 3)) (range 100)))))))))) - ;; PersistentTreeMap - (let [m1 (sorted-map) - c2 (comp - compare) - m2 (sorted-map-by c2)] - (assert (identical? cljs.core.PersistentTreeMap (type m1))) - (assert (identical? cljs.core.PersistentTreeMap (type m2))) - (assert (identical? compare (.-comp m1))) - (assert (zero? (count m1))) - (assert (zero? (count m2))) - (assert (nil? (rseq m1))) - (let [m1 (assoc m1 :foo 1 :bar 2 :quux 3) - m2 (assoc m2 :foo 1 :bar 2 :quux 3)] - (assert (= (count m1) 3)) - (assert (= (count m2) 3)) - (assert (= (seq m1) (list [:bar 2] [:foo 1] [:quux 3]))) - (assert (= (seq m2) (list [:quux 3] [:foo 1] [:bar 2]))) - (assert (= (seq m1) (rseq m2))) - (assert (= (seq m2) (rseq m1))) - (assert (= (conj m1 [:wibble 4]) {:foo 1 :bar 2 :quux 3 :wibble 4})) - (assert (= (count (conj m1 [:wibble 4])) 4)) - (assert (= (conj m2 [:wibble 4]) {:foo 1 :bar 2 :quux 3 :wibble 4})) - (assert (= (count (conj m2 [:wibble 4])) 4)) - (assert (= (map key (assoc m1 nil 4)) (list nil :bar :foo :quux))) - (assert (= (map key (assoc m2 nil 4)) (list :quux :foo :bar nil))))) - (let [m (->> [[0 10] [20 30] [10 20] [50 60] [30 40] [40 50]] - (mapcat (partial apply range)) - (mapcat #(list % %)) - (apply sorted-map)) - s1 (map #(vector % %) (range 60)) - s2 (map #(vector % %) (range 59 -1 -1))] - (assert (= (count m) 60)) - (assert (= (seq m) s1)) - (assert (= (rseq m) s2))) - (let [m (sorted-map :foo 1 :bar 2 :quux 3)] - (assert (= (dissoc m :foo) (hash-map :bar 2 :quux 3))) - (assert (= (count (dissoc m :foo)) 2)) - (assert (= (hash m) (hash (hash-map :foo 1 :bar 2 :quux 3)))) - (assert (= (subseq m < :foo) (list [:bar 2]))) - (assert (= (subseq m <= :foo) (list [:bar 2] [:foo 1]))) - (assert (= (subseq m > :foo) (list [:quux 3]))) - (assert (= (subseq m >= :foo) (list [:foo 1] [:quux 3]))) - (assert (= (map #(reduce (fn [_ x] x) %) m) (list 2 1 3))) - (assert (= (map #(reduce (fn [x _] x) 7 %) m) (list 7 7 7)))) - - ;; PersistentTreeSet - (let [s1 (sorted-set) - c2 (comp - compare) - s2 (sorted-set-by c2) - c3 #(compare (quot %1 2) (quot %2 2)) - s3 (sorted-set-by c3) - s4 (sorted-set-by <)] - (assert (identical? cljs.core.PersistentTreeSet (type s1))) - (assert (identical? cljs.core.PersistentTreeSet (type s2))) - (assert (identical? compare (-comparator s1))) - (assert (zero? (count s1))) - (assert (zero? (count s2))) - (assert (nil? (rseq s1))) - (let [s1 (conj s1 1 2 3) - s2 (conj s2 1 2 3) - s3 (conj s3 1 2 3 7 8 9) - s4 (conj s4 1 2 3)] - (assert (= (hash s1) (hash s2))) - (assert (= (hash s1) (hash #{1 2 3}))) - (assert (= (seq s1) (list 1 2 3))) - (assert (= (rseq s1) (list 3 2 1))) - (assert (= (seq s2) (list 3 2 1))) - (assert (= (rseq s2) (list 1 2 3))) - (assert (= (count s1) 3)) - (assert (= (count s2) 3)) - (assert (= (count s3) 4)) - (assert (= (get s3 0) 1)) - (assert (= (subseq s3 > 5) (list 7 8))) - (assert (= (subseq s3 > 6) (list 8))) - (assert (= (subseq s3 >= 6) (list 7 8))) - (assert (= (subseq s3 >= 12) nil)) - (assert (= (subseq s3 < 0) (list))) - (assert (= (subseq s3 < 5) (list 1 2))) - (assert (= (subseq s3 < 6) (list 1 2))) - (assert (= (subseq s3 <= 6) (list 1 2 7))) - (assert (= (subseq s3 >= 2 <= 6) (list 2 7))) - (assert (= (subseq s4 >= 2 < 3) (list 2))) - (let [s1 (disj s1 2) - s2 (disj s2 2)] - (assert (= (seq s1) (list 1 3))) - (assert (= (rseq s1) (list 3 1))) - (assert (= (seq s2) (list 3 1))) - (assert (= (rseq s2) (list 1 3))) - (assert (= (count s1) 2)) - (assert (= (count s2) 2))))) - ;; defrecord (defrecord Person [firstname lastname]) (def fred (Person. "Fred" "Mertz")) @@ -1653,48 +2062,6 @@ :none) :none))) - ;; IComparable - (assert (= 0 (compare false false))) - (assert (= -1 (compare false true))) - (assert (= 1 (compare true false))) - - (assert (= -1 (compare 0 1))) - (assert (= -1 (compare -1 1))) - (assert (= 0 (compare 1 1))) - (assert (= 1 (compare 1 0))) - (assert (= 1 (compare 1 -1))) - - (assert (= 0 (compare "cljs" "cljs"))) - (assert (= 0 (compare :cljs :cljs))) - (assert (= 0 (compare 'cljs 'cljs))) - (assert (= -1 (compare "a" "b"))) - (assert (= -1 (compare :a :b))) - (assert (= -1 (compare 'a 'b))) - ;; cases involving ns - (assert (= -1 (compare :b/a :c/a))) - (assert (= -1 (compare :c :a/b))) - (assert (= 1 (compare :a/b :c))) - (assert (= -1 (compare 'b/a 'c/a))) - (assert (= -1 (compare 'c 'a/b))) - (assert (= 1 (compare 'a/b 'c))) - - ;; This is different from clj. clj gives -2 next 3 tests - (assert (= -1 (compare "a" "c"))) - (assert (= -1 (compare :a :c))) - (assert (= -1 (compare 'a 'c))) - - (assert (= -1 (compare [1 2] [1 1 1]))) - (assert (= -1 (compare [1 2] [1 2 1]))) - (assert (= -1 (compare [1 1] [1 2]))) - (assert (= 0 (compare [1 2] [1 2]))) - (assert (= 1 (compare [1 2] [1 1]))) - (assert (= 1 (compare [1 1 1] [1 2]))) - (assert (= 1 (compare [1 1 2] [1 1 1]))) - - (assert (= -1 (compare (subvec [1 2 3] 1) (subvec [1 2 4] 1)))) - (assert (= 0 (compare (subvec [1 2 3] 1) (subvec [1 2 3] 1)))) - (assert (= 1 (compare (subvec [1 2 4] 1) (subvec [1 2 3] 1)))) - ;; RSeq (assert (= '(3 2 1) (reverse (seq (array 1 2 3))))) @@ -1704,125 +2071,6 @@ (assert (= '(4 3 2) (map inc (reverse [1 2 3])))) (assert (= '(4 2) (filter even? (reverse [1 2 3 4])))) - ;; Chunked Sequences - - (let [r (range 64) - v (into [] r)] - (assert (= (hash (seq v)) (hash (seq v)))) - (assert (= 6 (reduce + (array-chunk (array 1 2 3))))) - (assert (instance? ChunkedSeq (seq v))) - (assert (= r (seq v))) - (assert (= (map inc r) (map inc v))) - (assert (= (filter even? r) (filter even? v))) - (assert (= (filter odd? r) (filter odd? v))) - (assert (= (concat r r r) (concat v v v))) - (assert (satisfies? IReduce (seq v))) - (assert (== 2010 (reduce + (nnext (nnext (seq v)))))) - (assert (== 2020 (reduce + 10 (nnext (nnext (seq v))))))) - - ;; INext - - (assert (= nil (next nil))) - (assert (= nil (next (seq (array 1))))) - (assert (= '(2 3) (next (seq (array 1 2 3))))) - (assert (= nil (next (reverse (seq (array 1)))))) - (assert (= '(2 1) (next (reverse (seq (array 1 2 3)))))) - (assert (= nil (next (cons 1 nil)))) - (assert (= '(2 3) (next (cons 1 (cons 2 (cons 3 nil)))))) - (assert (= nil (next (lazy-seq (cons 1 nil))))) - (assert (= '(2 3) (next (lazy-seq - (cons 1 - (lazy-seq - (cons 2 - (lazy-seq (cons 3 nil))))))))) - (assert (= nil (next (list 1)))) - (assert (= '(2 3) (next (list 1 2 3)))) - (assert (= nil (next [1]))) - (assert (= '(2 3) (next [1 2 3]))) - (assert (= nil (next (range 1 2)))) - (assert (= '(2 3) (next (range 1 4)))) - - ;; UUID - - (assert (= (UUID. "550e8400-e29b-41d4-a716-446655440000") - (UUID. "550e8400-e29b-41d4-a716-446655440000"))) - - (assert (not (identical? (UUID. "550e8400-e29b-41d4-a716-446655440000") - (UUID. "550e8400-e29b-41d4-a716-446655440000")))) - - (assert (= 42 (get {(UUID. "550e8400-e29b-41d4-a716-446655440000") 42} - (UUID. "550e8400-e29b-41d4-a716-446655440000") - :not-at-all-found))) - - (assert (= :not-at-all-found - (get {(UUID. "550e8400-e29b-41d4-a716-446655440000") 42} - (UUID. "666e8400-e29b-41d4-a716-446655440000") - :not-at-all-found))) - - ;; Reader literals - (assert (= #queue [1] (into cljs.core.PersistentQueue.EMPTY [1]))) - (assert (not= #queue [1 2] (into cljs.core.PersistentQueue.EMPTY [1]))) - - (assert (= #inst "2010-11-12T18:14:15.666-00:00" - #inst "2010-11-12T13:14:15.666-05:00")) - - (assert (= #uuid "550e8400-e29b-41d4-a716-446655440000" - #uuid "550e8400-e29b-41d4-a716-446655440000")) - - (assert (= 42 - (get {#uuid "550e8400-e29b-41d4-a716-446655440000" 42} - #uuid "550e8400-e29b-41d4-a716-446655440000"))) - - ;; pr-str - - (assert (= (pr-str 1) "1")) - (assert (= (pr-str -1) "-1")) - (assert (= (pr-str -1.5) "-1.5")) - (assert (= (pr-str [3 4]) "[3 4]")) - (assert (= (pr-str "foo") "\"foo\"")) - (assert (= (pr-str :hello) ":hello")) - (assert (= (pr-str 'goodbye) "goodbye")) - ;;(assert (= (pr-str #{1 2 3}) "#{1 2 3}")) - (assert (= (pr-str '(7 8 9)) "(7 8 9)")) - (assert (= (pr-str '(deref foo)) "(deref foo)")) - (assert (= (pr-str '(quote bar)) "(quote bar)")) - (assert (= (pr-str 'foo/bar) "foo/bar")) - (assert (= (pr-str \a) "\"a\"")) - (assert (= (pr-str :foo/bar) ":foo/bar")) - (assert (= (pr-str nil) "nil")) - (assert (= (pr-str true) "true")) - (assert (= (pr-str false) "false")) - (assert (= (pr-str "string") "\"string\"")) - (assert (= (pr-str ["üñîçó∂£" :ทดสอบ/你好 'こんにちは]) "[\"üñîçó∂£\" :ทดสอบ/你好 こんにちは]")) - (assert (= (pr-str "escape chars \t \r \n \\ \" \b \f") "\"escape chars \\t \\r \\n \\\\ \\\" \\b \\f\"")) - - ;;; pr-str records - - (defrecord PrintMe [a b]) - (assert (= (pr-str (PrintMe. 1 2)) "#cljs.core-test.PrintMe{:a 1, :b 2}")) - - ;;; pr-str inst - - (assert (= (pr-str (js/Date. "2010-11-12T13:14:15.666-05:00")) - "#inst \"2010-11-12T18:14:15.666-00:00\"")) - - (doseq [month (range 1 13) day (range 1 29) hour (range 1 23)] - (let [pad (fn [n] - (if (< n 10) - (str "0" n) - n)) - inst (str "2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-00:00")] - (assert (= (pr-str (js/Date. inst)) (str "#inst \"" inst "\""))))) - - ;;; pr-str uuid - - (let [uuid-str "550e8400-e29b-41d4-a716-446655440000" - uuid (UUID. uuid-str)] - (assert (= (pr-str uuid) (str "#uuid \"" uuid-str "\"")))) - - ;;; pr-str PersistentQueueSeq - CLJS-800 - (assert (= (pr-str (rest (conj cljs.core.PersistentQueue.EMPTY 1 2 3))) "(2 3)")) - ;; CLJS-405 (defprotocol IBar (-bar [this x])) @@ -2128,28 +2376,6 @@ (assert (= :fail (try (assoc! (transient [1 2]) n 4) (catch js/Error e :fail))))) - ;; Namespaced destructuring - - (let [{:keys [:a :b]} {:a 1 :b 2}] - (assert (= 1 a)) - (assert (= 2 b))) - - (let [{:keys [:a/b :c/d]} {:a/b 1 :c/d 2}] - (assert (= 1 b)) - (assert (= 2 d))) - - (let [{:keys [a/b c/d]} {:a/b 1 :c/d 2}] - (assert (= 1 b)) - (assert (= 2 d))) - - (let [{:syms [a/b c/d]} {'a/b 1 'c/d 2}] - (assert (= 1 b)) - (assert (= 2 d))) - - (let [{:keys [::s/x ::s/y]} {:clojure.string/x 1 :clojure.string/y 2}] - (assert (= x 1)) - (assert (= y 2))) - ;; CLJS-739 (defn cljs-739 [arr names] @@ -2228,21 +2454,6 @@ (-rest [this] (make-seq (rest from-seq)))))) [[:bar 2] [:baz 3]]))))) - ;; printing customization - (assert (= (binding [*print-length* 1] (str [1 2 3 4 5 6 7 8 9 0])) - "[1 ...]")) - (assert (= (binding [*print-length* 2] (str [1 2 3 4 5 6 7 8 9 0])) - "[1 2 ...]")) - (assert (= (binding [*print-length* 10] (str [1 2 3 4 5 6 7 8 9 0])) - "[1 2 3 4 5 6 7 8 9 0]")) - ;; CLJS-804 - (assert (= (binding [*print-length* 10] (str {:foo "bar"})) - "{:foo \"bar\"}")) - (assert (= (binding [*print-length* 1] (str {:foo "bar" :baz "woz"})) - "{:foo \"bar\", ...}")) - (assert (= (binding [*print-length* 10] (str {:foo "bar" :baz "woz"})) - "{:foo \"bar\", :baz \"woz\"}")) - ;; case keyword (assert (= (let [x "a"] (case x :a 1 "a")) "a")) @@ -2250,20 +2461,6 @@ (assert (= "0atrue:key/wordsymb/olfalse[1 2 3 4]1234.56789" (str 0 "a" true nil :key/word 'symb/ol false [1 2 3 4] 1234.5678 0x09))) - ;; int-rotate-left - (assert (== (int-rotate-left (bit-or 0x87654321 0) 4) (bit-or 0x76543218 0))) - (assert (== (int-rotate-left (bit-or 0x87654321 0) 8) (bit-or 0x65432187 0))) - (assert (== (int-rotate-left (bit-or 0x80000000 0) 1) 0x1)) - (assert (== (int-rotate-left (bit-or 0x78123456 0) 4) (bit-or 0x81234567 0))) - (assert (== (int-rotate-left (bit-or 0xffffffff 0) 4) (bit-or 0xffffffff 0))) - - ;; imul - (assert (== (imul 3 3) 9)) - (assert (== (imul -1 8) -8)) - (assert (== (imul -2 -2) 4)) - (assert (== (imul 0xffffffff 5) -5)) - (assert (== (imul 0xfffffffe 5) -10)) - ;; CLJS-812 (defn case-recur [value] (case value @@ -2281,66 +2478,6 @@ ;; CLJS-881 (assert (= [:foo] (keys (apply array-map [:foo 1 :foo 2])))) - ;; basic iteration - - (def iter (es6-iterator [1 2 3])) - - (assert (= (.-value (.next iter)) 1)) - (assert (= (.-value (.next iter)) 2)) - (assert (= (.-value (.next iter)) 3)) - (assert (.-done (.next iter))) - - ;; ES6 Map - - (assert (.has {:foo "bar"} :foo)) - (assert (= (.get {:foo "bar"} :foo) "bar")) - - ;; ES6 Map iteration - - ;; keys - (def iter (.keys {:foo "bar" :baz "woz"})) - (assert (#{:foo :baz} (.-value (.next iter)))) - (assert (#{:foo :baz} (.-value (.next iter)))) - (assert (.-done (.next iter))) - - ;; entries - (def eiter (.entries {:foo "bar" :baz "woz"})) - (assert (= (seq (.-value (.next eiter))) (seq #js [:foo "bar"]))) - (assert (= (seq (.-value (.next eiter))) (seq #js [:baz "woz"]))) - (assert (.-done (.next eiter))) - - ;; values - (def iter (.values {:foo "bar" :baz "woz"})) - (assert (#{"bar" "woz"} (.-value (.next iter)))) - (assert (#{"bar" "woz"} (.-value (.next iter)))) - (assert (.-done (.next iter))) - - ;; ES6 Set - - (assert (.has #{:cat :bird :dog} :bird)) - - ;; ES6 Set iteration - - ;; keys - (def iter (.keys #{:cat :bird :dog})) - (assert (#{:cat :bird :dog} (.-value (.next iter)))) - (assert (#{:cat :bird :dog} (.-value (.next iter)))) - (assert (#{:cat :bird :dog} (.-value (.next iter)))) - (assert (.-done (.next iter))) - - ;; entries - (def iter (.entries #{:cat :bird :dog})) - (assert (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter))))) - (assert (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter))))) - (assert (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter))))) - (assert (.-done (.next iter))) - - ;; values - (def iter (.values #{:cat :bird :dog})) - (assert (#{:cat :bird :dog} (.-value (.next iter)))) - (assert (#{:cat :bird :dog} (.-value (.next iter)))) - (assert (#{:cat :bird :dog} (.-value (.next iter)))) - (assert (.-done (.next iter))) ;; CLJS-810 (let [not-strings [true false nil 1 (fn [])]] @@ -2353,86 +2490,6 @@ (assert (every? #(= :failed (try (re-matches #"nomatch" %) (catch js/TypeError _ :failed))) not-strings))) - ;; Object equiv - (assert (.equiv :foo :foo)) - (assert (.equiv 'foo 'foo)) - (assert (.equiv {:foo 1 :bar 2} {:foo 1 :bar 2})) - (assert (.equiv [1 2 3] [1 2 3])) - (assert (.equiv '(1 2 3) '(1 2 3))) - (assert (.equiv (map inc [1 2 3]) (map inc [1 2 3]))) - (assert (.equiv #{:cat :dog :bird} #{:cat :dog :bird})) - - ;; transducers - (assert (= (sequence (map inc) (array 1 2 3)) '(2 3 4))) - (assert (= (apply str (sequence (map #(.toUpperCase %)) "foo")) "FOO")) - (assert (== (hash [1 2 3]) (hash (sequence (map inc) (range 3))))) - (assert (= [1 2 3] (sequence (map inc) (range 3)))) - (assert (= (sequence (map inc) (range 3)) [1 2 3])) - (assert (= (sequence (remove even?) (range 10)) '(1 3 5 7 9))) - (assert (= (sequence (take 5) (range 10)) - '(0 1 2 3 4))) - (assert (= (sequence (take-while #(< % 5)) (range 10)) - '(0 1 2 3 4))) - (assert (= (sequence (drop 5) (range 10)) - '(5 6 7 8 9))) - (assert (= (sequence (drop-while #(< % 5)) (range 10)) - '(5 6 7 8 9))) - (assert (= (sequence (take-nth 2) (range 10)) - '(0 2 4 6 8))) - (assert (= (sequence (replace {:foo :bar}) '(:foo 1 :foo 2)) - '(:bar 1 :bar 2))) - (let [ret (into [] (map inc) (range 3))] - (assert (and (vector? ret) - (= ret '(1 2 3))))) - (let [ret (into [] (filter even?) (range 10))] - (assert (and (vector? ret) - (= ret '(0 2 4 6 8))))) - (assert (= (map inc (sequence (map inc) (range 3))) - '(2 3 4))) - (assert (= (sequence (dedupe) [1 1 2 2 3 3]) - '(1 2 3))) - (assert (= (mapcat reverse [[3 2 1 0] [6 5 4] [9 8 7]]) - (range 10))) - (assert (= (sequence (mapcat reverse) [[3 2 1 0] [6 5 4] [9 8 7]]) - (range 10))) - (assert (= (seq (eduction (map inc) [1 2 3])) '(2 3 4))) - (assert (= (sequence (partition-by #{:split}) [1 2 3 :split 4 5 6]) - '([1 2 3] [:split] [4 5 6]))) - (assert (= (sequence (partition-all 3) '(1 2 3 4 5)) - '([1 2 3] [4 5]))) - (assert (= (sequence (keep identity) [1 nil 2 nil 3]) - '(1 2 3))) - (assert (= (keep-indexed identity [:foo nil :bar nil :baz]) - (sequence (keep-indexed identity) [:foo nil :bar nil :baz]))) - - (def xform - (comp (map inc) - (filter even?) - (dedupe) - (mapcat range) - (partition-all 3) - (partition-by #(< (apply + %) 7)) - (mapcat flatten) - (random-sample 1.0) - (take-nth 1) - (keep #(when (odd? %) (* % %))) - (keep-indexed #(when (even? %1) (* %1 %2))) - (replace {2 "two" 6 "six" 18 "eighteen"}) - (take 11) - (take-while #(not= 300 %)) - (drop 1) - (drop-while string?) - (remove string?))) - (def data (vec (interleave (range 18) (range 20)))) - - (assert (= (sequence xform data) - '(36 200 10))) - - (def xf (map #(+ %1 %2))) - - (assert (= (sequence xf [0 0] [1 2]) - [1 2])) - ;; CLJS-849 (let [xs [44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24]] (loop [m (transient (zipmap xs (repeat 1))) @@ -2476,32 +2533,13 @@ ;; vars - (defn var-test - "A docstring" - [a b] - (+ a b)) - - (def var-meta (meta #'var-test)) - - (assert (= (:doc var-meta) "A docstring")) - (assert (= (:arglists var-meta) '([a b]))) - - (defn var-test-test - "A docstring" - {:test (fn [] :cool)} - [a b] - (+ a b)) - - (assert (= ((:test (meta #'var-test-test))) :cool)) - - (defn var-test-self-call - "A docstring" - {:test (fn bar ([] (bar 1)) ([n] n))} - [a b] - (+ a b)) + ;; (defn var-test + ;; "A docstring" + ;; [a b] + ;; (+ a b)) - (assert (= (.cljs$lang$test var-test-self-call) 1)) - (assert (= (.cljs$lang$test var-test-self-call 2) 2)) + ;; (let [var-meta (meta #'var-test)] + ;; (assert (= (:doc var-meta) "A docstring")) + ;; (assert (= (:arglists var-meta) '([a b])))) - :ok ) diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index f029f5593..05794c408 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -1,5 +1,5 @@ (ns test-runner - (:require [cljs.test :as test :refer-macros [run-tests]] + (:require [cljs.test :refer-macros [run-tests]] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -17,8 +17,10 @@ (set! *print-newline* false) (set-print-fn! js/print) -(core-test/test-stuff) +#_(core-test/test-stuff) + (run-tests + 'cljs.core-test 'cljs.reader-test 'clojure.string-test 'clojure.data-test From 7e6010f1422359a6572945bd0956dd059f5965a6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Dec 2014 19:28:29 -0500 Subject: [PATCH 0281/4033] `cljs.test/assert-expr 'instance?` case bug, change `class` to `type` --- src/clj/cljs/test.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 729c23210..5828f5cf0 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -90,10 +90,10 @@ env'# (if result# (cljs.test/do-report ~env {:type :pass, :message ~msg, - :expected '~form, :actual (class object#)}) + :expected '~form, :actual (type object#)}) (cljs.test/do-report ~env {:type :fail, :message ~msg, - :expected '~form, :actual (class object#)}))] + :expected '~form, :actual (type object#)}))] (if (:return env'#) (assoc env'# :last-value result#) result#)))) From 5e0cc6478ff68d66651c5fcfc4e410117dc28794 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Dec 2014 07:49:41 -0500 Subject: [PATCH 0282/4033] add metadata to the functions created by `cljs.test/is` and `cljs.test/testing` to disambiguate from fn types returned by imperative statements the user might write. --- src/clj/cljs/test.clj | 40 ++++++++++++++++------------------------ src/cljs/cljs/test.cljs | 7 +++++++ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 5828f5cf0..40c001542 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -169,7 +169,9 @@ ([form] `(cljs.test/is ~form nil)) ([form msg] (if (contains? (:locals &env) 'cljs$lang$test$body) - `(fn [env#] (cljs.test/try-expr env# ~msg ~form)) + `(with-meta + (fn [env#] (cljs.test/try-expr env# ~msg ~form)) + {:cljs.test/step true}) ;; expression evaluation case `(cljs.test/try-expr (cljs.test/empty-env) ~msg ~form)))) @@ -178,27 +180,20 @@ but must occur inside a test function (deftest)." ([string & body] (if (contains? (:locals &env) 'cljs$lang$test$body) - `(fn [env#] - (update-in - (reduce - (fn [env'# thunk#] - (let [ret# (thunk#)] - (if (fn? ret#) - (ret# env'#) - env'#))) - (update-in env# [:testing-contexts] conj ~string) - [~@(map (fn [expr] `(fn [] ~expr)) body)]) - [:testing-contexts] rest)) + `(with-meta + (fn [env#] + (update-in + (reduce cljs.test/step + (update-in env# [:testing-contexts] conj ~string) + [~@(map (fn [expr] `(fn [] ~expr)) body)]) + [:testing-contexts] rest)) + {:cljs.test/step true}) ;; expression evaluation case `(let [~'cljs$lang$test$body nil] (:last-value (update-in (reduce - (fn [env'# thunk#] - (let [ret# (thunk#)] - (if (fn? ret#) - (ret# env'#) - env'#))) + cljs.test/step (update-in (assoc (cljs.test/empty-env) :return true) [:testing-contexts] conj ~string) (let [~'cljs$lang$test$body nil] @@ -227,13 +222,10 @@ ([] (self# (cljs.test/empty-env))) ([env#] (let [~'cljs$lang$test$body nil - ret# (reduce - (fn [env'# thunk#] - (let [ret# (thunk#)] - (if (fn? ret#) - (ret# env'#) - env'#))) - env# [~@(map (fn [expr] `(fn [] ~expr)) body)])] + ret# (reduce cljs.test/step env# + [~@(map + (fn [expr] `(fn [] ~expr)) + body)])] (when (:return env#) ret#))))) (fn diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 27bc19cb1..37206278b 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -359,6 +359,13 @@ :testing-contexts () :reporter reporter})) +(defn step + [env thunk] + (let [ret (thunk)] + (if (and (fn? ret) (-> ret meta :cljs.test/step)) + (ret env) + (assoc env :last-value ret)))) + ;; ============================================================================= ;; Low-level functions From e5c604d50ef2971669f5661276469ac842726af2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Dec 2014 09:12:18 -0500 Subject: [PATCH 0283/4033] converted more tests --- test/cljs/cljs/core_test.cljs | 1379 +++++++++++++++++---------------- 1 file changed, 691 insertions(+), 688 deletions(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 00f4f7de9..5657731ff 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -676,6 +676,12 @@ ;; vary-meta (is (= {:a 1} (meta (vary-meta [] assoc :a 1)))) (is (= {:a 1 :b 2} (meta (vary-meta (with-meta [] {:b 2}) assoc :a 1)))) + ;; comparator + (is (= [1 1 2 2 3 5] (seq (.sort (to-array [2 3 1 5 2 1]) (comparator <))))) + (is (= [5 3 2 2 1 1] (seq (.sort (to-array [2 3 1 5 2 1]) (comparator >))))) + (is (= (hash 'foo) (hash (symbol "foo")))) + (is (= (hash 'foo/bar) (hash (symbol "foo" "bar")))) + (is (= (lazy-cat [1] [2] [3]) '(1 2 3))) )) (deftest test-booleans @@ -1123,6 +1129,9 @@ (defmethod my-map? ::map [_] true) (defmethod my-map? :default [_] false) +(defmulti foo2' identity) +(defmethod foo2' 0 [x] x) + (deftest test-multimethods-2 (let [r (rect 4 13) c (circle 12)] @@ -1136,13 +1145,11 @@ ;; CLJS-863 (is (= :foo (foo2))) ;; remove method tests - ;; TODO: these side-effects return fn values - ;; will interact badly with cljs.test need to fix - David - ;; (is (= 2 (count (methods bar)))) - ;; (remove-method bar [::rect ::shape]) - ;; (is (= 1 (count (methods bar)))) - ;; (remove-all-methods bar) - ;; (is (zero? (count (methods bar)))) + (is (= 2 (count (methods bar)))) + (remove-method bar [::rect ::shape]) + (is (= 1 (count (methods bar)))) + (remove-all-methods bar) + (is (zero? (count (methods bar)))) (is (= [:three '(2)] (apply apply-multi-test [0 1 2]))) (is (try (no-dispatch-value {:test :test}) @@ -1154,6 +1161,8 @@ (is (every? true? (for [not-m [[] 1 "asdf" :foo]] (not (my-map? not-m))))) + ;; multimethod hashing + (is (= foo2' (ffirst {foo2' 1}))) ))) (deftest test-range @@ -1180,6 +1189,16 @@ (is (= (take 3 (range 3 1 0)) (list 3 3 3))) )) +(deftest test-rseq + (testing "Testing RSeq" + (is (= '(3 2 1) (reverse (seq (array 1 2 3))))) + (is (= '(3 2 1) (reverse [1 2 3]))) + (is (= '(4 3 2 1) (cons 4 (reverse [1 2 3])))) + (is (= 6 (reduce + (reverse [1 2 3])))) + (is (= '(4 3 2) (map inc (reverse [1 2 3])))) + (is (= '(4 2) (filter even? (reverse [1 2 3 4])))) + )) + (deftest test-sorted-map (testing "Testing sorted maps" ;; PersistentTreeMap @@ -1590,6 +1609,179 @@ (is (= 1 (compare (subvec [1 2 4] 1) (subvec [1 2 3] 1))))) ) +(deftest test-dot + (let [s "abc"] + (testing "Testing dot operations" + (is (= 3 (.-length s))) + (is (= 3 (. s -length))) + (is (= 3 (. (str 138) -length))) + (is (= 3 (. "abc" -length))) + (is (= "bc" (.substring s 1))) + (is (= "bc" (.substring "abc" 1))) + (is (= "bc" ((memfn substring start) s 1))) + (is (= "bc" (. s substring 1))) + (is (= "bc" (. s (substring 1)))) + (is (= "bc" (. s (substring 1 3)))) + (is (= "bc" (.substring s 1 3))) + (is (= "ABC" (. s (toUpperCase)))) + (is (= "ABC" (. "abc" (toUpperCase)))) + (is (= "ABC" ((memfn toUpperCase) s))) + (is (= "BC" (. (. s (toUpperCase)) substring 1))) + (is (= 2 (.-length (. (. s (toUpperCase)) substring 1)))) + ))) + +(defrecord Person [firstname lastname]) +(defrecord A []) +(defrecord C [a b c]) +(defrecord A' [x]) +(defrecord B' [x]) + +(deftest test-records + (let [fred (Person. "Fred" "Mertz") + fred-too (Person. "Fred" "Mertz") + ethel (with-meta (assoc (Person. "Ethel" "Mertz") :husband :fred) + {:married true}) + ethel-too (with-meta (assoc (Person. "Ethel" "Mertz") :husband :fred) + {:married true}) + letters (C. "a" "b" "c") + more-letters (assoc letters :d "d" :e "e" :f "f")] + (testing "Testing records" + (is (= (:firstname fred) "Fred")) + (is (= fred fred-too)) + (is (false? (= fred nil))) + (is (false? (= nil fred))) + (is (= (meta ethel) {:married true})) + (is (= ethel ethel-too)) + (is (= (map->Person {:firstname "Fred" :lastname "Mertz"}) fred)) + (is (= (->Person "Fred" "Mertz") fred)) + (is (= (count fred) 2)) + (is (= (count ethel) 3)) + (is (= (conj fred {:wife :ethel :friend :ricky}) + (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel :friend :ricky}))) + (is (= (conj fred {:lastname "Flintstone"}) + (map->Person {:firstname "Fred" :lastname "Flintstone"}))) + (is (= (assoc fred :lastname "Flintstone") + (map->Person {:firstname "Fred" :lastname "Flintstone"}))) + (is (= (assoc fred :wife :ethel) + (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel}))) + (is (= (dissoc ethel :husband) + (map->Person {:firstname "Ethel" :lastname "Mertz"}))) + (is (= {:foo 'bar} (meta (with-meta (A.) {:foo 'bar})))) + (is (= 'bar (:foo (assoc (A.) :foo 'bar)))) + (is (= (set (keys letters)) #{:a :b :c})) + (is (= (set (keys more-letters)) #{:a :b :c :d :e :f})) + (is (= (set (keys (dissoc more-letters :d))) #{:a :b :c :e :f})) + (is (= (set (keys (dissoc more-letters :d :e))) #{:a :b :c :f})) + (is (= (set (keys (dissoc more-letters :d :e :f))) #{:a :b :c})) + (is (not= (A'. nil) (B'. nil)))))) + +(deftype FnLike [] + IFn + (-invoke [_] :a) + (-invoke [_ a] :b) + (-invoke [_ a b] :c)) + +(deftype FnLikeB [a] + IFn + (-invoke [_] a)) + +(deftest test-ifn + (testing "Testing IFn implementations" + (is (= :a ((FnLike.)))) + (is (= :b ((FnLike.) 1))) + (is (= :c ((FnLike.) 1 2))) + (is (= [:b :b :b] (map (FnLike.) [0 0 0]))) + (is (= 1 ((FnLikeB. 1)))) + )) + +(deftest test-case + (testing "Test case expr" + (let [x 1] + (is (= (case x 1 :one) :one))) + (let [x 1] + (is (= (case x 2 :two :default) :default))) + (let [x 1] + (is (= (try + (case x 3 :three) + (catch js/Error e + :fail)) + :fail))) + (let [x 1] + (is (= (case x + (1 2 3) :ok + :fail) + :ok))) + (let [x [:a :b]] + (is (= (case x + [:a :b] :ok) + :ok))) + (let [a 'a] + (is (= (case a + nil nil + & :amp + :none) + :none))) + (let [a '&] + (is (= (case a + nil nil + & :amp + :none) + :amp))) + (let [foo 'a] + (testing "multiple match" + (is (= (case foo + (a b c) :sym + :none) + :sym)) + (is (= (case foo + (b c d) :sym + :none) + :none)))) + )) + +(defprotocol IHasFirst + (-get-first [this])) + +(defprotocol IFindsFirst + (-find-first [this other])) + +(deftype First [xs] + ISeqable + (-seq [this] (seq xs)) + IIndexed + (-nth [this i] (nth xs i)) + (-nth [this i not-found] (nth xs i not-found)) + IFn + (-invoke [[x]] x) + (-invoke [this x] this) + Object + (toString [[x]] (str x)) + IHasFirst + (-get-first [[x]] x) + IFindsFirst + (-find-first [_ [x]] x)) + +(deftype DestructuringWithLocals [a] + IFindsFirst + (-find-first [_ [x y]] + [x y a])) + +(deftest test-protocol-method-destructuring + (testing "Testing protocol method destructuring" + (let [fv (First. [1 2 3]) + fs (First. "asdf")] + (testing "basic operations" + (is (= (fv) 1)) + (is (= (fs) \a)) + (is (= (str fs) \a)) + (is (= (-get-first fv) 1)) + (is (= (-get-first fs) \a)) + (is (= (-find-first fv [1]) 1)) + (is (identical? (fv 1) fv)))) + (let [t (DestructuringWithLocals. 1)] + (testing "with locals" + (is (= [2 3 1] (-find-first t [2 3]))))))) + ;; ============================================================================= ;; Tickets @@ -1607,6 +1799,493 @@ (let [sentinel (js-obj)] (is (identical? sentinel (try ([] 0) (catch js/Error _ sentinel))))))) +(defprotocol IFoo (foo [this])) + +(deftest test-reify-meta + (is (= (meta (with-meta (reify IFoo (foo [this] :foo)) {:foo :bar})) + {:foo :bar}))) + +;; hashing bug in many JS runtimes CLJ-118 +(deftest test-clj-118 + (let [g #{(conj #{:2} :alt)} + h #{#{:2 :alt}}] + (is (= g h))) + (is (= (hash {:a 1 :b 2}) + (hash {:b 2 :a 1}))) + (is (= (hash (hash-map :a 1 :b 2)) + (hash (hash-map :b 2 :a 1)))) + (is (= (hash {:start 133 :end 134}) + (hash (apply hash-map [:start 133 :end 134])))) + (is (= (hash :a) + (hash (keyword "a"))))) + +(defprotocol IBar (-bar [this x])) + +(defn baz [f] + (reify + IBar + (-bar [_ x] + (f x)))) + +(deftest test-405 + (is (= 2 (-bar (baz inc) 1)))) + +(let [x "original"] + (defn original-closure-stmt [] x)) + +(deftest test-401-411 + (let [x "overwritten"] + (is (= "original" (original-closure-stmt)))) + (is (= "original" (let [x "original" + oce (fn [] x) + x "overwritten"] + (oce))))) + +(deftest test-letfn-shadowing + (letfn [(x [] "original") + (y [] (x))] + (let [x (fn [] "overwritten")] + (is (= "original" (y)))))) + +(deftest test-459 + (is (= (reduce-kv conj [] (sorted-map :foo 1 :bar 2)) + [:bar 2 :foo 1]))) + +(deftest test-kv-reduce + (letfn [(kvr-test [data expect] + (and + (= :reduced + (reduce-kv + (fn [_ _ _] (reduced :reduced)) + [] data)) + (= (sort expect) + (sort + (reduce-kv + (fn [r k v] (-> r (conj [k v]))) + [] data)))))] + (testing "Testing IKVReduce" + (is (every? true? + (for [[data expect] [[(obj-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] + [(hash-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] + [(array-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] + [[:v0 :v1] [[0 :v0] [1 :v1]]]]] + (kvr-test data expect)))) + (is (= {:init :val} (reduce-kv assoc {:init :val} nil)))))) + +(deftest test-data-conveying-exceptions + (is (= {:foo 1} + (try (throw (ex-info "asdf" {:foo 1})) + (catch ExceptionInfo e + (ex-data e))))) + (is (instance? js/Error (ex-info "asdf" {:foo 1}))) + (is (not (instance? cljs.core.ExceptionInfo (js/Error.))))) + +(deftest test-435 + (is (= (assoc {} 154618822656 1 261993005056 1) + {154618822656 1 261993005056 1}))) + +(deftest test-458 + (is (= (get-in {:a {:b 1}} [:a :b :c] :nothing-there) + :nothing-there))) + +(deftest test-464 + (is (nil? (get-in {:foo {:bar 2}} [:foo :bar :baz])))) + +(deftest test-symbol-meta + (is (= (meta (with-meta 'foo {:tag 'int})) {:tag 'int}))) + +(deftest test-467 + (is (= (reduce-kv + 0 (apply hash-map (range 1000))) + (reduce + (range 1000))))) + +(deftest test-477 + (is (= [js/undefined 1 2] ((fn [& more] more) js/undefined 1 2))) + (is (= [js/undefined 4 5] ((fn [a b & more] more) 1 2 js/undefined 4 5)))) + +(deftest test-493 + (is (nil? (get 42 :anything))) + (is (= (get 42 :anything :not-found) :not-found)) + (is (nil? (first (map get [42] [:anything])))) + (is (= (first (map get [42] [:anything] [:not-found])) :not-found))) + +(deftest test-481 + (let [fs (atom [])] + (doseq [x (range 4) + :let [y (inc x) + f (fn [] y)]] + (swap! fs conj f)) + (is (= (map #(%) @fs) '(1 2 3 4))))) + +(def exists?-test-val 'foo) + +(deftest test-495 + (testing "Testing CLJS-495, exists?" + (is (false? (exists? js/jQuery))) + (is (exists? exists?-test-val)))) + +(deftest test-496 + (is (= (char 65) \A)) + (is (= (char \A) \A))) + +(deftype PositionalFactoryTest [x]) + +(deftest test-515 + (is (== 1 (.-x (->PositionalFactoryTest 1))))) + +(deftest test-518 + (is (nil? (:test "test")))) + +;; r1798 core fn protocol regression +(extend-type object + ISeqable + (-seq [coll] + (map #(vector % (aget coll %)) (js-keys coll))) + + ILookup + (-lookup + ([coll k] + (-lookup coll k nil)) + ([coll k not-found] + (if-let [v (aget coll k)] + v + not-found)))) + +(deftest test-extend-to-object + (is (= (seq (js-obj "foo" 1 "bar" 2)) '(["foo" 1] ["bar" 2]))) + (is (= (get (js-obj "foo" 1) "foo") 1)) + (is (= (get (js-obj "foo" 1) "bar" ::not-found) ::not-found)) + (is (= (reduce (fn [s [k v]] (+ s v)) 0 (js-obj "foo" 1 "bar" 2)) 3))) + +(deftest test-541 + (letfn [(f! [x] (print \f) x) + (g! [x] (print \g) x)] + (is (= "ffgfg" + (with-out-str + (instance? Symbol (f! 'foo)) + (max (f! 5) (g! 10)) + (min (f! 5) (g! 10))))))) + +(deftest test-582 + (is (= #{1 2} (set [1 2 2]))) + (is (= #{1 2} (hash-set 1 2 2))) + (is (= #{1 2} (apply hash-set [1 2 2])))) + +(deftest test-585 + (is (= (last (map identity (into [] (range 32)))) 31)) + (is (= (into #{} (range 32)) + (set (map identity (into [] (range 32))))))) + +(def foo580) +(def foo580 {:a (fn []) :b (fn [] (foo580 :a))}) + +(deftest test-580 + (is (nil? (((:b foo580)))))) + +(deftest test-587 + (is (== (first (filter #(== % 9999) (range))) 9999))) + +(deftest test-604 + (is (= () (concat nil []))) + (is (= () (concat [] [])))) + +(deftest test-600 + (is (= "foobar" (apply str (concat "foo" "bar"))))) + +(deftest test-608 + (is (= '("") (re-seq #"\s*" "")))) + +(deftype KeywordTest [] + ILookup + (-lookup [o k] :nothing) + (-lookup [o k not-found] not-found)) + +(deftest tset-638 + (is (= (:a (KeywordTest.)) :nothing))) + +(deftest test-648 + (let [a (reify IHash (-hash [_] 42)) + b (reify IHash (-hash [_] 42)) + s (set (range 128))] + (testing "Testing CLJS-648 (CLJ-1285)" + (is (= (-> (conj s a b) transient (disj! a) persistent! (conj a)) + (-> (conj s a b) transient (disj! a) persistent! (conj a))))))) + +(deftest test-660 + (testing "Testing CLJS-660, namespace handling" + (is (= (-> 'a.b keyword ((juxt namespace name))) [nil "a.b"])) + (is (= (-> 'a.b/c keyword ((juxt namespace name))) ["a.b" "c"])) + (is (= (-> "a.b" keyword ((juxt namespace name))) [nil "a.b"])) + (is (= (-> "a.b/c" keyword ((juxt namespace name))) ["a.b" "c"])))) + +(deftest test-663 + (testing "Testing CLJS-663, invalid keywords" + (is (= (keyword 123) nil)) + (is (= (keyword (js/Date.)) nil)))) + +(deftest test-647 + (let [keys #(vec (js-keys %)) + z "x"] + (testing "Testing CLJS-647, js-keys" + (assert (= ["x"] + (keys (js-obj "x" "y")) + (keys (js-obj (identity "x") "y")) + (keys (js-obj z "y"))))))) + + +(def some-x 1) +(def some-y 1) + +(deftest test-583 + (is (= (count #{some-x some-y}) 1))) + +(deftest test-584 + (is (= (count {some-x :foo some-y :bar}) 1))) + +(deftest test-717 + (testing "Testing CLJS-717, JS literals" + (is (array? #js [1 2 3])) + (is (= (alength #js [1 2 3]) 3)) + (is (= (seq #js [1 2 3]) (seq [1 2 3]))) + (is (= (set (js-keys #js {:foo "bar" :baz "woz"})) #{"foo" "baz"})) + (is (= (aget #js {:foo "bar"} "foo") "bar")) + (is (= (aget #js {"foo" "bar"} "foo") "bar")) + (is (array? (aget #js {"foo" #js [1 2 3]} "foo"))) + (is (= (seq (aget #js {"foo" #js [1 2 3]} "foo")) '(1 2 3))))) + +(deftest test-725 + (testing "Testing CLJS-725, drop" + (is (= (apply vector (drop-while (partial = 1) [1 2 3])) [2 3])) + (is (= (apply list (drop-while (partial = 1) [1 2 3])) '(2 3))) + (is (= (set (drop 1 #js [1 2 3])) #{2 3})))) + +(deftest test-724 + (is (nil? (first (rest (rest (rest (range 3)))))))) + +(deftest test-730 + (testing "Testing CLJS-730, object? predicate" + (is (true? (object? #js {}))) + (is (false? (object? nil))))) + +(deftest test-count-hash-set + (is + (== (count (hash-set [1 4] [2 4] [3 4] [0 3] [1 3] [2 3] [3 3] + [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] + [2 1] [3 1] [1 0] [2 0] [3 0])) + (count (list [1 4] [2 4] [3 4] [0 3] [1 3] [2 3] [3 3] + [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] + [2 1] [3 1] [1 0] [2 0] [3 0]))))) + +(defprotocol IWoz + (-woz [this])) + +(def noz []) + +(deftest test-414 + (testing "Testing CLJS-414, specify" + (is (= (specify noz IWoz (-woz [_] :boz)) noz)) + (is (not (identical? (specify noz IWoz (-woz [_] :boz)) noz))) + (is (= (-woz (specify noz IWoz (-woz [this] this))) noz)) + (is (= (-woz (specify noz IWoz (-woz [_] :boz))) :boz)))) + +(deftest test-734 + (testing "Testing CLJS-734, transient operations" + (is (= (-> (transient []) (conj! 1 2) persistent!) [1 2])) + (is (= (-> (transient #{1 2 3}) (disj! 1 2) persistent!) #{3})) + (is (= (-> (transient {}) (assoc! :a 1 :b 2) persistent!) {:a 1 :b 2})) + (is (= (-> (transient {:a 1 :b 2 :c 3}) (dissoc! :a :b) persistent!) {:c 3})))) + +(deftest test-767 + (testing "Testing CLJS-767, invalid assoc" + (is (every? true? + (for [n [nil "-1" "" "0" "1" false true (js-obj)]] + (and + (= :fail (try (assoc [1 2] n 4) + (catch js/Error e :fail))) + (= :fail (try (assoc (subvec [1 2 3] 2) n 4) + (catch js/Error e :fail))) + (= :fail (try (assoc (range 1 3) n 4) + (catch js/Error e :fail))))))))) + +(deftest test-768 + (testing "Testing CLJS-768, invalid assoc!" + (is (every? true? + (for [n [nil "-1" "" "0" "1" false true (js-obj)]] + (= :fail (try (assoc! (transient [1 2]) n 4) + (catch js/Error e :fail)))))))) + +(defn cljs-739 [arr names] + (let [name (first names)] + (if name + (recur (conj arr (fn [] (println name))) + (rest names)) + arr))) + +(deftest test-739 + (testing "Testing CLJS-739, with-out-str" + (set! *print-newline* true) + (is (= (with-out-str (doseq [fn (cljs-739 [] [:a :b :c :d])] (fn))) + ":a\n:b\n:c\n:d\n")) + (set! *print-newline* false))) + +(deftest test-728 + (testing "Testing CLJS-728, lookup with default" + (is (every? true? + (for [n [nil "-1" "" "0" "1" false true (js-obj)]] + (and + (nil? (get [1 2] n)) + (= :fail (try (nth [1 2] n) (catch js/Error e :fail))) + (= 4 (get [1 2] n 4)) + (= :fail (try (nth [1 2] n 4) (catch js/Error e :fail))) + + (nil? (get (subvec [1 2] 1) n)) + (= :fail (try (nth (subvec [1 2] 1) n) (catch js/Error e :fail))) + (= 4 (get (subvec [1 2] 1) n 4)) + (= :fail (try (nth (subvec [1 2] 1) n 4) (catch js/Error e :fail))) + + (nil? (get (transient [1 2]) n)) + (= :fail (try (nth (transient [1 2]) n) (catch js/Error e :fail))) + (= 4 (get (transient [1 2]) n 4)) + (= :fail (try (nth (transient [1 2]) n 4) (catch js/Error e :fail))) + + (nil? (get (range 1 3) n)) + (= :fail (try (nth (range 1 3) n) (catch js/Error e :fail))) + (= 4 (get (range 1 3) n 4)) + (= :fail (try (nth (range 1 3) n 4) (catch js/Error e :fail)))))))) + ) + +(deftest test-778 + (testing "Testing CLJS-778, -rest, -next RSeq" + (is (= (-rest (rseq [0])) ())) + (is (nil? (-next (rseq [0])))) + (is (= (set (rseq [0])) #{0})))) + +(def cljs-780 (atom {:foo (with-meta [] {:bar '(1 2 3)})})) + +(deftest test-780 + (let [_ (swap! cljs-780 update-in [:foo] vary-meta update-in [:bar] vec) + x (-> @cljs-780 :foo meta :bar)] + (testing "Testing CLJS-780, update-in + vary-meta" + (is (vector? x)) + (is (= x [1 2 3])))) ) + +(deftest test-782 + (testing "Testing CLJS-782, UUID toString" + (is (= (.toString #uuid "550e8400-e29b-41d4-a716-446655440000") + "550e8400-e29b-41d4-a716-446655440000")))) + +(deftest test-784 + (testing "Testing CLJS-784, conj on maps" + (is (every? true? + (for [m [(array-map) (hash-map) (sorted-map)]] + (and (= :ok + (try + (conj m "foo") + (catch js/Error _ + :ok))) + (= {:foo 1} (conj m [:foo 1])) + (= {:foo 1} (conj m {:foo 1})) + (= {:foo 1} (conj m (list [:foo 1]))))))) + (is (every? true? + (for [mt [array-map hash-map sorted-map]] + (= {:foo 1 :bar 2 :baz 3} + (conj (mt :foo 1) + ((fn make-seq [from-seq] + ;; this tests specifically for user defined seq's, that implement the bare minimum, i.e. no INext + (when (seq from-seq) + (reify + ISeqable + (-seq [this] this) + ISeq + (-first [this] (first from-seq)) + (-rest [this] (make-seq (rest from-seq)))))) + [[:bar 2] [:baz 3]]))))))) + ) + +(deftest test-case-keyword + (is (= (let [x "a"] (case x :a 1 "a")) "a"))) + +(deftest test-801 + (testing "Testing CLJS-801, str" + (is (= "0atrue:key/wordsymb/olfalse[1 2 3 4]1234.56789" + (str 0 "a" true nil :key/word 'symb/ol false [1 2 3 4] 1234.5678 0x09))))) + +(defn case-recur [value] + (case value + :a (recur :b) + :b 0)) + +(deftest test-812 + (testing "Testing CLJS-812, case with recur" + (is (= (case-recur :a) 0)))) + +(deftest test-816 + (testing "Testing CLJS-816, rename-keys" + (is (= (set/rename-keys {:a "one" :b "two"} {:a :z}) {:z "one" :b "two"})) + (is (= (set/rename-keys {:a "one" :b "two"} {:a :z :c :y}) {:z "one" :b "two"})) + (is (= (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) + {:a "two" :b "one" :c "three"}))) ) + +(deftest test-881 + (testing "Testing CLJS-881, duplicate keys in array maps" + (is (= [:foo] (keys (apply array-map [:foo 1 :foo 2])))))) + +(deftest test-810 + (let [not-strings [true false nil 1 (fn [])]] + (testing "Testing CLJS-810, exception on bad input to regex fns" + (is (every? #(= :failed (try (re-find #"." %) + (catch js/TypeError _ :failed))) not-strings)) + (is (every? #(= :failed (try (re-matches #"." %) + (catch js/TypeError _ :failed))) not-strings)) + (is (every? #(= :failed (try (re-find #"nomatch" %) + (catch js/TypeError _ :failed))) not-strings)) + (is (every? #(= :failed (try (re-matches #"nomatch" %) + (catch js/TypeError _ :failed))) not-strings))))) + +(deftest test-849 + (let [xs [44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24]] + (testing "Testing CLJS-849, transient contains?" + (is (loop [m (transient (zipmap xs (repeat 1))) + xs xs] + (if-let [x (first xs)] + (if (contains? m x) + (recur (dissoc! m x) (next xs)) + false) + true)))))) + +(deftest test-large-array-map + (let [m (array-map 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15)] + (testing "Testing large array maps" + (is (instance? cljs.core/PersistentArrayMap m)) + (is (= (seq m) [[0 0] [1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9] [10 10] [11 11] [12 12] [13 13] [14 14] [15 15]]))))) + +(def test-map + {:a 1 + :b 2 + #inst "2013-12-19T05:00:00.000-00:00" 3 + :d 4 + :e 5 + #inst "2013-12-06T05:00:00.000-00:00" 6 + :g 7 + :h 8 + :i 9 + :j 10}) + +(deftest test-716 + (testing "Testing CLJS-716, date as keys in maps" + (is (= (test-map #inst "2013-12-19T05:00:00.000-00:00") 3)) + (is (= (test-map #inst "2013-12-06T05:00:00.000-00:00") 6)))) + +(deftest test-853 + (testing "Testing CLJS-853, function metadata" + (is (= {:foo true} (meta ^:foo (fn [])))))) + +(deftest test-807 + (testing "Testing CLJS-807, big int, float, big dec literals" + (is (= -1 -1N)) + (is (= 9.007199254740996E15 9007199254740995N)) + (is (= 1.5 1.5M)) + (is (= 4.9E-324 5E-324M)))) + (defn test-stuff [] (let [v1 (vec (range 15 48)) v2 (vec (range 40 57)) @@ -1848,688 +2527,12 @@ s)))) (assert (= s (set (remove #(zero? (mod % 3)) (range 100)))))))))) - ;; defrecord - (defrecord Person [firstname lastname]) - (def fred (Person. "Fred" "Mertz")) - (assert (= (:firstname fred) "Fred")) - (def fred-too (Person. "Fred" "Mertz")) - (assert (= fred fred-too)) - (assert (false? (= fred nil))) - (assert (false? (= nil fred))) - - ;; invalid tests, cannot set meta and extmap directly - David - (def ethel (with-meta (assoc (Person. "Ethel" "Mertz") :husband :fred) - {:married true})) - (assert (= (meta ethel) {:married true})) - (def ethel-too (with-meta (assoc (Person. "Ethel" "Mertz") :husband :fred) - {:married true})) - (assert (= ethel ethel-too)) - - (assert (= (map->Person {:firstname "Fred" :lastname "Mertz"}) fred)) - (assert (= (->Person "Fred" "Mertz") fred)) - - (assert (= (count fred) 2)) - (assert (= (count ethel) 3)) - - (defrecord A []) - (assert (= {:foo 'bar} (meta (with-meta (A.) {:foo 'bar})))) - (assert (= 'bar (:foo (assoc (A.) :foo 'bar)))) - - (defrecord C [a b c]) - (def letters (C. "a" "b" "c")) - (assert (= (set (keys letters)) #{:a :b :c})) - (def more-letters (assoc letters :d "d" :e "e" :f "f")) - (assert (= (set (keys more-letters)) #{:a :b :c :d :e :f})) - (assert (= (set (keys (dissoc more-letters :d))) #{:a :b :c :e :f})) - (assert (= (set (keys (dissoc more-letters :d :e))) #{:a :b :c :f})) - (assert (= (set (keys (dissoc more-letters :d :e :f))) #{:a :b :c})) - ;; ObjMap - (let [ks (map (partial str "foo") (range 500)) - m (apply obj-map (interleave ks (range 500)))] - (assert (instance? cljs.core.ObjMap m)) - (assert (= 500 (count m))) - (assert (= 123 (m "foo123")))) - - ;; comparator - (assert (= [1 1 2 2 3 5] (seq (.sort (to-array [2 3 1 5 2 1]) (comparator <))))) - (assert (= [5 3 2 2 1 1] (seq (.sort (to-array [2 3 1 5 2 1]) (comparator >))))) - - ;; dot - (let [s "abc"] - (assert (= 3 (.-length s))) - (assert (= 3 (. s -length))) - (assert (= 3 (. (str 138) -length))) - (assert (= 3 (. "abc" -length))) - (assert (= "bc" (.substring s 1))) - (assert (= "bc" (.substring "abc" 1))) - (assert (= "bc" ((memfn substring start) s 1))) - (assert (= "bc" (. s substring 1))) - (assert (= "bc" (. s (substring 1)))) - (assert (= "bc" (. s (substring 1 3)))) - (assert (= "bc" (.substring s 1 3))) - (assert (= "ABC" (. s (toUpperCase)))) - (assert (= "ABC" (. "abc" (toUpperCase)))) - (assert (= "ABC" ((memfn toUpperCase) s))) - (assert (= "BC" (. (. s (toUpperCase)) substring 1))) - (assert (= 2 (.-length (. (. s (toUpperCase)) substring 1))))) - - (assert (= (conj fred {:wife :ethel :friend :ricky}) - (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel :friend :ricky}))) - (assert (= (conj fred {:lastname "Flintstone"}) - (map->Person {:firstname "Fred" :lastname "Flintstone"}))) - (assert (= (assoc fred :lastname "Flintstone") - (map->Person {:firstname "Fred" :lastname "Flintstone"}))) - (assert (= (assoc fred :wife :ethel) - (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel}))) - (assert (= (dissoc ethel :husband) - (map->Person {:firstname "Ethel" :lastname "Mertz"}))) - - (defrecord A [x]) - (defrecord B [x]) - (assert (not= (A. nil) (B. nil))) - - (defprotocol IFoo (foo [this])) - (assert (= (meta (with-meta (reify IFoo (foo [this] :foo)) {:foo :bar})) - {:foo :bar})) - - (defmulti foo2 identity) - (defmethod foo2 0 [x] x) - (assert (= foo2 (ffirst {foo2 1}))) - - (defprotocol IMutate - (mutate [this])) - - (deftype Mutate [^:mutable a] - IMutate - (mutate [_] - (set! a 'foo))) - - ;; IFn - (deftype FnLike [] - IFn - (-invoke [_] :a) - (-invoke [_ a] :b) - (-invoke [_ a b] :c)) - - (assert (= :a ((FnLike.)))) - (assert (= :b ((FnLike.) 1))) - (assert (= :c ((FnLike.) 1 2))) - - (assert (= [:b :b :b] (map (FnLike.) [0 0 0]))) - - (deftype FnLikeB [a] - IFn - (-invoke [_] a)) - - (assert (= 1 ((FnLikeB. 1)))) - - ;; hashing bug in many JS runtimes CLJ-118 - (let [g #{(conj #{:2} :alt)} - h #{#{:2 :alt}}] - (assert (= g h))) - (assert (= (hash {:a 1 :b 2}) - (hash {:b 2 :a 1}))) - (assert (= (hash (hash-map :a 1 :b 2)) - (hash (hash-map :b 2 :a 1)))) - (assert (= (hash {:start 133 :end 134}) - (hash (apply hash-map [:start 133 :end 134])))) - (assert (= (hash :a) - (hash (keyword "a")))) - - (defprotocol IHasFirst - (-get-first [this])) - - (defprotocol IFindsFirst - (-find-first [this other])) - - (deftype First [xs] - ISeqable - (-seq [this] (seq xs)) - IIndexed - (-nth [this i] (nth xs i)) - (-nth [this i not-found] (nth xs i not-found)) - IFn - (-invoke [[x]] x) - (-invoke [this x] this) - Object - (toString [[x]] (str x)) - IHasFirst - (-get-first [[x]] x) - IFindsFirst - (-find-first [_ [x]] x)) - - (let [fv (First. [1 2 3]) - fs (First. "asdf")] - (assert (= (fv) 1)) - (assert (= (fs) \a)) - (assert (= (str fs) \a)) - (assert (= (-get-first fv) 1)) - (assert (= (-get-first fs) \a)) - (assert (= (-find-first fv [1]) 1)) - (assert (identical? (fv 1) fv))) - - (deftype DestructuringWithLocals [a] - IFindsFirst - (-find-first [_ [x y]] - [x y a])) - - (let [t (DestructuringWithLocals. 1)] - (assert (= [2 3 1] (-find-first t [2 3])))) - - (let [x 1] - (assert (= (case x 1 :one) :one))) - (let [x 1] - (assert (= (case x 2 :two :default) :default))) - (let [x 1] - (assert (= (try - (case x 3 :three) - (catch js/Error e - :fail)) - :fail))) - (let [x 1] - (assert (= (case x - (1 2 3) :ok - :fail) - :ok))) - - (let [x [:a :b]] - (assert (= (case x - [:a :b] :ok) - :ok))) - - (let [a 'a] - (assert (= (case a - nil nil - & :amp - :none) - :none))) - - (let [a '&] - (assert (= (case a - nil nil - & :amp - :none) - :amp))) - - (let [foo 'a] - (assert (= (case foo - (a b c) :sym - :none) - :sym)) - (assert (= (case foo - (b c d) :sym - :none) - :none))) - - ;; RSeq - - (assert (= '(3 2 1) (reverse (seq (array 1 2 3))))) - (assert (= '(3 2 1) (reverse [1 2 3]))) - (assert (= '(4 3 2 1) (cons 4 (reverse [1 2 3])))) - (assert (= 6 (reduce + (reverse [1 2 3])))) - (assert (= '(4 3 2) (map inc (reverse [1 2 3])))) - (assert (= '(4 2) (filter even? (reverse [1 2 3 4])))) - - ;; CLJS-405 - - (defprotocol IBar (-bar [this x])) - - (defn baz [f] - (reify - IBar - (-bar [_ x] - (f x)))) - - (assert (= 2 (-bar (baz inc) 1))) - - ;; CLJS-401 / CLJS-411 - - (let [x "original"] - (defn original-closure-stmt [] x)) - - (let [x "overwritten"] - (assert (= "original" (original-closure-stmt)))) - - (assert (= "original" (let [x "original" - oce (fn [] x) - x "overwritten"] - (oce)))) - - - (letfn [(x [] "original") - (y [] (x))] - (let [x (fn [] "overwritten")] - (assert (= "original" (y))))) - - ;; CLJS-459: reduce-kv visit order - (assert (= (reduce-kv conj [] (sorted-map :foo 1 :bar 2)) - [:bar 2 :foo 1])) - - ;; Test builtin implementations of IKVReduce - (letfn [(kvr-test [data expect] - (assert - (= :reduced - (reduce-kv - (fn [_ _ _] (reduced :reduced)) - [] data))) - (assert - (= (sort expect) - (sort - (reduce-kv - (fn [r k v] (-> r (conj [k v]))) - [] data)))))] - (kvr-test (obj-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]) - (kvr-test (hash-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]) - (kvr-test (array-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]) - (kvr-test [:v0 :v1] [[0 :v0] [1 :v1]])) - (assert (= {:init :val} (reduce-kv assoc {:init :val} nil))) - - ;; data conveying exception - (assert (= {:foo 1} - (try (throw (ex-info "asdf" {:foo 1})) - (catch ExceptionInfo e - (ex-data e))))) - (assert (instance? js/Error (ex-info "asdf" {:foo 1}))) - (assert (not (instance? cljs.core.ExceptionInfo (js/Error.)))) - - ;; CLJS-435 - - (assert (= (assoc {} 154618822656 1 261993005056 1) - {154618822656 1 261993005056 1})) - - ;; CLJS-458 - - (assert (= (get-in {:a {:b 1}} [:a :b :c] :nothing-there) - :nothing-there)) - - ;; CLJS-464 - - (assert (nil? (get-in {:foo {:bar 2}} [:foo :bar :baz]))) - - ;; symbol metadata - - (assert (= (meta (with-meta 'foo {:tag 'int})) {:tag 'int})) - - ;; CLJS-467 - - (assert (= (reduce-kv + 0 (apply hash-map (range 1000))) - (reduce + (range 1000)))) - - ;; CLJS-477 - - (assert (= [js/undefined 1 2] ((fn [& more] more) js/undefined 1 2))) - (assert (= [js/undefined 4 5] ((fn [a b & more] more) 1 2 js/undefined 4 5))) - - ;; CLJS-493 - - (assert (nil? (get 42 :anything))) - (assert (= (get 42 :anything :not-found) :not-found)) - (assert (nil? (first (map get [42] [:anything])))) - (assert (= (first (map get [42] [:anything] [:not-found])) :not-found)) - - ;; CLJS-481 - - (let [fs (atom [])] - (doseq [x (range 4) - :let [y (inc x) - f (fn [] y)]] - (swap! fs conj f)) - (assert (= (map #(%) @fs) '(1 2 3 4)))) - - ;; CLJS-495 - (assert (false? (exists? js/jQuery))) - (def exists?-test-val 'foo) - (assert (exists? exists?-test-val)) - - ;; CLJS-496 - (assert (= (char 65) \A)) - (assert (= (char \A) \A)) - - ;; compile time run symbol hash codes - - (assert (= (hash 'foo) (hash (symbol "foo")))) - (assert (= (hash 'foo/bar) (hash (symbol "foo" "bar")))) - - (assert (= (lazy-cat [1] [2] [3]) '(1 2 3))) - - ;; r1798 core fn protocol regression - (extend-type object - ISeqable - (-seq [coll] - (map #(vector % (aget coll %)) (js-keys coll))) - - ILookup - (-lookup - ([coll k] - (-lookup coll k nil)) - ([coll k not-found] - (if-let [v (aget coll k)] - v - not-found)))) - - (assert (= (seq (js-obj "foo" 1 "bar" 2)) '(["foo" 1] ["bar" 2]))) - (assert (= (get (js-obj "foo" 1) "foo") 1)) - (assert (= (get (js-obj "foo" 1) "bar" ::not-found) ::not-found)) - (assert (= (reduce (fn [s [k v]] (+ s v)) 0 (js-obj "foo" 1 "bar" 2)) 3)) - - ;; CLJS-515 - (deftype PositionalFactoryTest [x]) - - (assert (== 1 (.-x (->PositionalFactoryTest 1)))) - - ;; CLJS-518 - (assert (nil? (:test "test"))) - - ;; CLJS-541 - (letfn [(f! [x] (print \f) x) - (g! [x] (print \g) x)] - (assert (= "ffgfg" - (with-out-str - (instance? Symbol (f! 'foo)) - (max (f! 5) (g! 10)) - (min (f! 5) (g! 10)))))) - - ;; CLJS-582 - (assert (= #{1 2} (set [1 2 2]))) - (assert (= #{1 2} (hash-set 1 2 2))) - (assert (= #{1 2} (apply hash-set [1 2 2]))) - - ;; CLJS-585 - (assert (= (last (map identity (into [] (range 32)))) 31)) - (assert (= (into #{} (range 32)) - (set (map identity (into [] (range 32)))))) - - ;; CLJS-580 - (def foo580) - (def foo580 {:a (fn []) :b (fn [] (foo580 :a))}) - (assert (nil? (((:b foo580))))) - - ;; CLJS-587 - (assert (== (first (filter #(== % 9999) (range))) 9999)) - - ;; LazySeq regressions - - ;; CLJS-604 - (assert (= () (concat nil []))) - (assert (= () (concat [] []))) - - ;; CLJS-600 - (assert (= "foobar" (apply str (concat "foo" "bar")))) - - ;; CLJS-608 - (assert (= '("") (re-seq #"\s*" ""))) - - ;; CLJS-638 - - (deftype KeywordTest [] - ILookup - (-lookup [o k] :nothing) - (-lookup [o k not-found] not-found)) - - (assert (= (:a (KeywordTest.)) :nothing)) - - ;; CLJS-648 (CLJ-1285) - (let [a (reify IHash (-hash [_] 42)) - b (reify IHash (-hash [_] 42)) - s (set (range 128))] - (assert (= (-> (conj s a b) transient (disj! a) persistent! (conj a)) - (-> (conj s a b) transient (disj! a) persistent! (conj a))))) - - ;; CLJS-660 - - (assert (= (-> 'a.b keyword ((juxt namespace name))) [nil "a.b"])) - (assert (= (-> 'a.b/c keyword ((juxt namespace name))) ["a.b" "c"])) - (assert (= (-> "a.b" keyword ((juxt namespace name))) [nil "a.b"])) - (assert (= (-> "a.b/c" keyword ((juxt namespace name))) ["a.b" "c"])) - - ;; CLJS-663 - - (assert (= (keyword 123) nil)) - (assert (= (keyword (js/Date.)) nil)) - - ;; CLJS-647 - (let [keys #(vec (js-keys %)) - z "x"] - (assert (= ["x"] - (keys (js-obj "x" "y")) - (keys (js-obj (identity "x") "y")) - (keys (js-obj z "y"))))) - - ;; CLJS-583 - - (def some-x 1) - (def some-y 1) - - (assert (= (count #{some-x some-y}) 1)) - - ;; CLJS-584 - - (assert (= (count {some-x :foo some-y :bar}) 1)) - - ;; CLJS-717 - - (assert (array? #js [1 2 3])) - (assert (= (alength #js [1 2 3]) 3)) - (assert (= (seq #js [1 2 3]) (seq [1 2 3]))) - (assert (= (set (js-keys #js {:foo "bar" :baz "woz"})) #{"foo" "baz"})) - (assert (= (aget #js {:foo "bar"} "foo") "bar")) - (assert (= (aget #js {"foo" "bar"} "foo") "bar")) - (assert (array? (aget #js {"foo" #js [1 2 3]} "foo"))) - (assert (= (seq (aget #js {"foo" #js [1 2 3]} "foo")) '(1 2 3))) - - ;; CLJS-725 - - (assert (= (apply vector (drop-while (partial = 1) [1 2 3])) [2 3])) - (assert (= (apply list (drop-while (partial = 1) [1 2 3])) '(2 3))) - (assert (= (set (drop 1 #js [1 2 3])) #{2 3})) - - ;; CLJS-724 - - (assert (nil? (first (rest (rest (rest (range 3))))))) - - ;; CLJS-730 - - (assert (true? (object? #js {}))) - (assert (false? (object? nil))) - - (assert - (== (count (hash-set [1 4] [2 4] [3 4] [0 3] [1 3] [2 3] [3 3] - [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] - [2 1] [3 1] [1 0] [2 0] [3 0])) - (count (list [1 4] [2 4] [3 4] [0 3] [1 3] [2 3] [3 3] - [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] - [2 1] [3 1] [1 0] [2 0] [3 0])))) - - (defprotocol IWoz - (-woz [this])) - - (def noz []) - - ;; CLJS-414 - - (assert (= (specify noz IWoz (-woz [_] :boz)) noz)) - (assert (not (identical? (specify noz IWoz (-woz [_] :boz)) noz))) - (assert (= (-woz (specify noz IWoz (-woz [this] this))) noz)) - (assert (= (-woz (specify noz IWoz (-woz [_] :boz))) :boz)) - - ;; CLJS-734 - - (assert (= (-> (transient []) (conj! 1 2) persistent!) [1 2])) - (assert (= (-> (transient #{1 2 3}) (disj! 1 2) persistent!) #{3})) - (assert (= (-> (transient {}) (assoc! :a 1 :b 2) persistent!) {:a 1 :b 2})) - (assert (= (-> (transient {:a 1 :b 2 :c 3}) (dissoc! :a :b) persistent!) {:c 3})) - - ;; CLJS-767 - - (doseq [n [nil "-1" "" "0" "1" false true (js-obj)]] - (assert (= :fail (try (assoc [1 2] n 4) - (catch js/Error e :fail)))) - (assert (= :fail (try (assoc (subvec [1 2 3] 2) n 4) - (catch js/Error e :fail)))) - (assert (= :fail (try (assoc (range 1 3) n 4) - (catch js/Error e :fail))))) - - ;; CLJS-768 - - (doseq [n [nil "-1" "" "0" "1" false true (js-obj)]] - (assert (= :fail (try (assoc! (transient [1 2]) n 4) - (catch js/Error e :fail))))) - - ;; CLJS-739 - - (defn cljs-739 [arr names] - (let [name (first names)] - (if name - (recur (conj arr (fn [] (println name))) - (rest names)) - arr))) - - (set! *print-newline* true) - (assert (= (with-out-str (doseq [fn (cljs-739 [] [:a :b :c :d])] (fn))) - ":a\n:b\n:c\n:d\n")) - (set! *print-newline* false) - - ;; CLJS-728 - - (doseq [n [nil "-1" "" "0" "1" false true (js-obj)]] - (assert (nil? (get [1 2] n))) - (assert (= :fail (try (nth [1 2] n) (catch js/Error e :fail)))) - (assert (= 4 (get [1 2] n 4))) - (assert (= :fail (try (nth [1 2] n 4) (catch js/Error e :fail)))) - - (assert (nil? (get (subvec [1 2] 1) n))) - (assert (= :fail (try (nth (subvec [1 2] 1) n) (catch js/Error e :fail)))) - (assert (= 4 (get (subvec [1 2] 1) n 4))) - (assert (= :fail (try (nth (subvec [1 2] 1) n 4) (catch js/Error e :fail)))) - - (assert (nil? (get (transient [1 2]) n))) - (assert (= :fail (try (nth (transient [1 2]) n) (catch js/Error e :fail)))) - (assert (= 4 (get (transient [1 2]) n 4))) - (assert (= :fail (try (nth (transient [1 2]) n 4) (catch js/Error e :fail)))) - - (assert (nil? (get (range 1 3) n))) - (assert (= :fail (try (nth (range 1 3) n) (catch js/Error e :fail)))) - (assert (= 4 (get (range 1 3) n 4))) - (assert (= :fail (try (nth (range 1 3) n 4) (catch js/Error e :fail))))) - - ;; CLJS-778 - (assert (= (-rest (rseq [0])) ())) - (assert (nil? (-next (rseq [0])))) - (assert (= (set (rseq [0])) #{0})) - - ;; CLJS-780 - (def cljs-780 (atom {:foo (with-meta [] {:bar '(1 2 3)})})) - (swap! cljs-780 update-in [:foo] vary-meta update-in [:bar] vec) - (let [x (-> @cljs-780 :foo meta :bar)] - (assert (vector? x)) - (assert (= x [1 2 3]))) - - ;; CLJS-782 - (assert (= (.toString #uuid "550e8400-e29b-41d4-a716-446655440000") - "550e8400-e29b-41d4-a716-446655440000")) - - ;; CLJS-784 - (doseq [m [(array-map) (hash-map) (sorted-map)]] - (assert (= :ok - (try - (conj m "foo") - (catch js/Error _ - :ok)))) - (assert (= {:foo 1} (conj m [:foo 1]))) - (assert (= {:foo 1} (conj m {:foo 1}))) - (assert (= {:foo 1} (conj m (list [:foo 1]))))) - - (doseq [mt [array-map hash-map sorted-map]] - (assert (= {:foo 1 :bar 2 :baz 3} - (conj (mt :foo 1) - ((fn make-seq [from-seq] - ;; this tests specifically for user defined seq's, that implement the bare minimum, i.e. no INext - (when (seq from-seq) - (reify - ISeqable - (-seq [this] this) - ISeq - (-first [this] (first from-seq)) - (-rest [this] (make-seq (rest from-seq)))))) - [[:bar 2] [:baz 3]]))))) - - ;; case keyword - (assert (= (let [x "a"] (case x :a 1 "a")) "a")) - - ;; CLJS-801 - (assert (= "0atrue:key/wordsymb/olfalse[1 2 3 4]1234.56789" - (str 0 "a" true nil :key/word 'symb/ol false [1 2 3 4] 1234.5678 0x09))) - - ;; CLJS-812 - (defn case-recur [value] - (case value - :a (recur :b) - :b 0)) - - (assert (= (case-recur :a) 0)) - - ;; CLJS-816 - (assert (= (set/rename-keys {:a "one" :b "two"} {:a :z}) {:z "one" :b "two"})) - (assert (= (set/rename-keys {:a "one" :b "two"} {:a :z :c :y}) {:z "one" :b "two"})) - (assert (= (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) - {:a "two" :b "one" :c "three"})) - - ;; CLJS-881 - (assert (= [:foo] (keys (apply array-map [:foo 1 :foo 2])))) - - - ;; CLJS-810 - (let [not-strings [true false nil 1 (fn [])]] - (assert (every? #(= :failed (try (re-find #"." %) - (catch js/TypeError _ :failed))) not-strings)) - (assert (every? #(= :failed (try (re-matches #"." %) - (catch js/TypeError _ :failed))) not-strings)) - (assert (every? #(= :failed (try (re-find #"nomatch" %) - (catch js/TypeError _ :failed))) not-strings)) - (assert (every? #(= :failed (try (re-matches #"nomatch" %) - (catch js/TypeError _ :failed))) not-strings))) - - ;; CLJS-849 - (let [xs [44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24]] - (loop [m (transient (zipmap xs (repeat 1))) - xs xs] - (if-let [x (first xs)] - (if (contains? m x) - (recur (dissoc! m x) (next xs)) - (throw (ex-info "CLJS-849 regression!" - {:m (persistent! m) :xs xs})))))) - - ;; CLJS- - (let [m (array-map 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15)] - (assert (instance? cljs.core/PersistentArrayMap m)) - (assert (= (seq m) [[0 0] [1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9] [10 10] [11 11] [12 12] [13 13] [14 14] [15 15]]))) - - ;; CLJS-716 - (def test-map - {:a 1 - :b 2 - #inst "2013-12-19T05:00:00.000-00:00" 3 - :d 4 - :e 5 - #inst "2013-12-06T05:00:00.000-00:00" 6 - :g 7 - :h 8 - :i 9 - :j 10}) - - (assert (= (test-map #inst "2013-12-19T05:00:00.000-00:00") 3)) - (assert (= (test-map #inst "2013-12-06T05:00:00.000-00:00") 6)) - - ;; CLJS-853 - - (assert (= {:foo true} (meta ^:foo (fn [])))) - - ;; CLJS-807 - (assert (= -1 -1N)) - (assert (= 9.007199254740996E15 9007199254740995N)) - (assert (= 1.5 1.5M)) - (assert (= 4.9E-324 5E-324M)) + ;; (let [ks (map (partial str "foo") (range 500)) + ;; m (apply obj-map (interleave ks (range 500)))] + ;; (assert (instance? cljs.core.ObjMap m)) + ;; (assert (= 500 (count m))) + ;; (assert (= 123 (m "foo123")))) ;; vars From 31dfdc92a1b472f1d7886f5419ab0fbfea023c11 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Dec 2014 10:11:33 -0500 Subject: [PATCH 0284/4033] converting last few tests --- test/cljs/cljs/core_test.cljs | 93 +++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 5657731ff..660c472ec 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2286,24 +2286,62 @@ (is (= 1.5 1.5M)) (is (= 4.9E-324 5E-324M)))) -(defn test-stuff [] +(deftest test-transient-edge-case-1 (let [v1 (vec (range 15 48)) v2 (vec (range 40 57)) v1 (persistent! (assoc! (conj! (pop! (transient v1)) :foo) 0 :quux)) v2 (persistent! (assoc! (conj! (transient v2) :bar) 0 :quux)) v (into v1 v2)] - (assert (= v (vec (concat [:quux] (range 16 47) [:foo] - [:quux] (range 41 57) [:bar]))))) - (loop [v (transient []) - xs (range 100)] - (if-let [x (first xs)] - (recur - (condp #(%1 (mod %2 3)) x - #{0 2} (conj! v x) - #{1} (assoc! v (count v) x)) - (next xs)) - (assert (= (vec (range 100)) (persistent! v))))) + (is (= v (vec (concat [:quux] (range 16 47) [:foo] + [:quux] (range 41 57) [:bar])))))) + +(deftest test-transient-edge-case-2 + (is (loop [v (transient []) + xs (range 100)] + (if-let [x (first xs)] + (recur + (condp #(%1 (mod %2 3)) x + #{0 2} (conj! v x) + #{1} (assoc! v (count v) x)) + (next xs)) + (= (vec (range 100)) (persistent! v)))))) + +(deftest test-phm + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY)) + (dissoc 3 5 7))] + (testing "Testing PHM dissoc" + (is (= (count m) 7)) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY)) + (conj [:foo 1]))] + (testing "Testing PHM conj" + (is (= (count m) 11)) + (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1})))) + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY) + transient) + (conj! [:foo 1]) + persistent!)] + (testing "Testing PHM conj!" + (is (= (count m) 11)) + (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1})))) + (let [tm (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY) + transient)] + (testing "Testing transient PHM" + (is (loop [tm tm ks [3 5 7]] + (if-let [k (first ks)] + (recur (dissoc! tm k) (next ks)) + (let [m (persistent! tm)] + (and (= (count m) 7) + (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))))) +) + +(deftest test-phm-fixed-hash) +(defn test-stuff [] ;; PersistentHashMap & TransientHashMap (loop [m1 cljs.core.PersistentHashMap.EMPTY m2 (transient cljs.core.PersistentHashMap.EMPTY) @@ -2326,32 +2364,6 @@ (assert (= (map vector (range 100) (range 100)) (sort-by first (seq m1)))) (assert (= (map vector (range 100) (range 100)) (sort-by first (seq m2)))) (assert (not (contains? (dissoc m1 3) 3)))))) - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY)) - (dissoc 3 5 7))] - (assert (= (count m) 7)) - (assert (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9}))) - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY)) - (conj [:foo 1]))] - (assert (= (count m) 11)) - (assert (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY) - transient) - (conj! [:foo 1]) - persistent!)] - (assert (= (count m) 11)) - (assert (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) - (let [tm (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY) - transient)] - (loop [tm tm ks [3 5 7]] - (if-let [k (first ks)] - (recur (dissoc! tm k) (next ks)) - (let [m (persistent! tm)] - (assert (= (count m) 7)) - (assert (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))) (let [tm (-> (->> (interleave (range 10) (range 10)) (apply assoc cljs.core.PersistentHashMap.EMPTY)) (dissoc 3 5 7) @@ -2364,14 +2376,17 @@ (assert (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) (assert (= 2 (try (count tm) 1 (catch js/Error e 2)))) (assert (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) + (deftype FixedHash [h v] IHash (-hash [this] h) IEquiv (-equiv [this other] (and (instance? FixedHash other) (= v (.-v other))))) + (def fixed-hash-foo (FixedHash. 0 :foo)) (def fixed-hash-bar (FixedHash. 0 :bar)) + (let [m (assoc cljs.core.PersistentHashMap.EMPTY fixed-hash-foo 1 fixed-hash-bar 2)] @@ -2382,6 +2397,7 @@ (assert (= (get m fixed-hash-bar) 2)) (assert (not (contains? m fixed-hash-foo))) (assert (= (count m) 1)))) + (let [m (into cljs.core.PersistentHashMap.EMPTY ; make sure we're testing (zipmap (range 100) (range 100))) ; the correct map type m (assoc m fixed-hash-foo 1 fixed-hash-bar 2)] @@ -2392,6 +2408,7 @@ (assert (= (get m fixed-hash-bar) 2)) (assert (not (contains? m fixed-hash-foo))) (assert (= (count m) 98)))) + (let [m (into cljs.core.PersistentHashMap.EMPTY ; make sure we're testing (zipmap (range 100) (range 100))) ; the correct map type m (transient m) From b39db1908f79a447d5931c7831f8199296347b21 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Dec 2014 16:27:17 -0500 Subject: [PATCH 0285/4033] eliminate explicit passing of environment and monadic approach, causes problems for async tests --- src/clj/cljs/test.clj | 259 +++++++++++++++++----------------------- src/cljs/cljs/test.cljs | 186 ++++++++++++++--------------- 2 files changed, 198 insertions(+), 247 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 40c001542..4dd470e32 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -26,37 +26,33 @@ 'actual' argument will contain the form with all its sub-forms evaluated. If the predicate returns false, the 'actual' form will be wrapped in (not...)." - [env msg form] + [msg form] (let [args (rest form) pred (first form)] `(let [values# (list ~@args) - result# (apply ~pred values#) - env'# (if result# - (cljs.test/do-report ~env - {:type :pass, :message ~msg, - :expected '~form, :actual (cons ~pred values#)}) - (cljs.test/do-report ~env - {:type :fail, :message ~msg, - :expected '~form, :actual (list '~'not (cons '~pred values#))}))] - (if (:return env'#) - (assoc env'# :last-value result#) - result#)))) + result# (apply ~pred values#)] + (if result# + (cljs.test/do-report + {:type :pass, :message ~msg, + :expected '~form, :actual (cons ~pred values#)}) + (cljs.test/do-report + {:type :fail, :message ~msg, + :expected '~form, :actual (list '~'not (cons '~pred values#))})) + result#))) (defn assert-any "Returns generic assertion code for any test, including macros, Java method calls, or isolated symbols." - [env msg form] - `(let [value# ~form - env'# (if value# - (cljs.test/do-report ~env - {:type :pass, :message ~msg, - :expected '~form, :actual value#}) - (cljs.test/do-report ~env - {:type :fail, :message ~msg, - :expected '~form, :actual value#}))] - (if (:return env'#) - (assoc env'# :last-value value#) - value#))) + [msg form] + `(let [value# ~form] + (if value# + (cljs.test/do-report + {:type :pass, :message ~msg, + :expected '~form, :actual value#}) + (cljs.test/do-report + {:type :fail, :message ~msg, + :expected '~form, :actual value#})) + value#)) ;; ============================================================================= ;; Assertion Methods @@ -66,39 +62,37 @@ ;; symbol in the test expression. (defmulti assert-expr - (fn [env menv msg form] + (fn [menv msg form] (cond (nil? form) :always-fail (seq? form) (first form) :else :default))) -(defmethod assert-expr :always-fail [env menv msg form] +(defmethod assert-expr :always-fail [menv msg form] ;; nil test: always fail - `(cljs.test/do-report ~env {:type :fail, :message ~msg})) + `(cljs.test/do-report {:type :fail, :message ~msg})) -(defmethod assert-expr :default [env menv msg form] +(defmethod assert-expr :default [menv msg form] (if (and (sequential? form) (function? menv (first form))) - (assert-predicate env msg form) - (assert-any env msg form))) + (assert-predicate msg form) + (assert-any msg form))) -(defmethod assert-expr 'instance? [env menv msg form] +(defmethod assert-expr 'instance? [menv msg form] ;; Test if x is an instance of y. `(let [klass# ~(nth form 1) object# ~(nth form 2)] - (let [result# (instance? klass# object#) - env'# (if result# - (cljs.test/do-report ~env - {:type :pass, :message ~msg, - :expected '~form, :actual (type object#)}) - (cljs.test/do-report ~env - {:type :fail, :message ~msg, - :expected '~form, :actual (type object#)}))] - (if (:return env'#) - (assoc env'# :last-value result#) - result#)))) + (let [result# (instance? klass# object#)] + (if result# + (cljs.test/do-report + {:type :pass, :message ~msg, + :expected '~form, :actual (type object#)}) + (cljs.test/do-report + {:type :fail, :message ~msg, + :expected '~form, :actual (type object#)})) + result#))) -(defmethod assert-expr 'thrown? [env menv msg form] +(defmethod assert-expr 'thrown? [menv msg form] ;; (is (thrown? c expr)) ;; Asserts that evaluating expr throws an exception of class c. ;; Returns the exception thrown. @@ -106,15 +100,16 @@ body (nthnext form 2)] `(try ~@body - (cljs.test/do-report ~env + (cljs.test/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) (catch ~klass e# - (cljs.test/do-report ~env + (cljs.test/do-report {:type :pass, :message ~msg, - :expected '~form, :actual e#}))))) + :expected '~form, :actual e#}) + e#)))) -(defmethod assert-expr 'thrown-with-msg? [env menv msg form] +(defmethod assert-expr 'thrown-with-msg? [menv msg form] ;; (is (thrown-with-msg? c re expr)) ;; Asserts that evaluating expr throws an exception of class c. ;; Also asserts that the message string of the exception matches @@ -126,26 +121,24 @@ ~@body (cljs.test/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) (catch ~klass e# - (let [m# (.getMessage e#) - env'# (if (re-find ~re m#) - (cljs.test/do-report ~env - {:type :pass, :message ~msg, - :expected '~form, :actual e#}) - (cljs.test/do-report ~env - {:type :fail, :message ~msg, - :expected '~form, :actual e#}))] - (if (:return env'#) - (assoc env'# :last-value e#) - e#)))))) + (let [m# (.getMessage e#)] + (if (re-find ~re m#) + (cljs.test/do-report + {:type :pass, :message ~msg, + :expected '~form, :actual e#}) + (cljs.test/do-report + {:type :fail, :message ~msg, + :expected '~form, :actual e#})) + e#))))) (defmacro try-expr "Used by the 'is' macro to catch unexpected exceptions. You don't call this." - [env msg form] + [msg form] `(try - ~(cljs.test/assert-expr env &env msg form) + ~(cljs.test/assert-expr &env msg form) (catch :default t# - (cljs.test/do-report ~env + (cljs.test/do-report {:type :error, :message ~msg, :expected '~form, :actual t#})))) @@ -168,37 +161,17 @@ re-find) the regular expression re." ([form] `(cljs.test/is ~form nil)) ([form msg] - (if (contains? (:locals &env) 'cljs$lang$test$body) - `(with-meta - (fn [env#] (cljs.test/try-expr env# ~msg ~form)) - {:cljs.test/step true}) - ;; expression evaluation case - `(cljs.test/try-expr (cljs.test/empty-env) ~msg ~form)))) + `(cljs.test/try-expr ~msg ~form))) (defmacro testing "Adds a new string to the list of testing contexts. May be nested, but must occur inside a test function (deftest)." ([string & body] - (if (contains? (:locals &env) 'cljs$lang$test$body) - `(with-meta - (fn [env#] - (update-in - (reduce cljs.test/step - (update-in env# [:testing-contexts] conj ~string) - [~@(map (fn [expr] `(fn [] ~expr)) body)]) - [:testing-contexts] rest)) - {:cljs.test/step true}) - ;; expression evaluation case - `(let [~'cljs$lang$test$body nil] - (:last-value - (update-in - (reduce - cljs.test/step - (update-in (assoc (cljs.test/empty-env) :return true) - [:testing-contexts] conj ~string) - (let [~'cljs$lang$test$body nil] - [~@(map (fn [expr] `(fn [] ~expr)) body)])) - [:testing-contexts] rest)))))) + `(do + (cljs.test/update-current-env! [:testing-contexts] conj ~string) + (let [ret# (do ~@body)] + (cljs.test/update-current-env! [:testing-contests] rest) + ret#)))) ;; ============================================================================= ;; Defining Tests @@ -217,25 +190,8 @@ [name & body] (when ana/*load-tests* `(do - (def ~(vary-meta name assoc :test - `(fn self# - ([] (self# (cljs.test/empty-env))) - ([env#] - (let [~'cljs$lang$test$body nil - ret# (reduce cljs.test/step env# - [~@(map - (fn [expr] `(fn [] ~expr)) - body)])] - (when (:return env#) - ret#))))) - (fn - ([] (~name (cljs.test/empty-env))) - ([env#] - (cljs.test/test-var - (assoc env# - :testing-vars () - :testing-contexts ()) - (.-cljs$lang$var ~name))))) + (def ~(vary-meta name assoc :test `(fn [] ~@body)) + (fn [] (cljs.test/test-var (.-cljs$lang$var ~name)))) (set! (.-cljs$lang$var ~name) (var ~name))))) ;; ============================================================================= @@ -258,26 +214,28 @@ (fn [[quote ns]] (and (= quote 'quote) (symbol? ns))) namespaces) "All arguments to run-tests must be quoted symbols") - (let [is-ns (ns? env-or-ns) - env-sym (gensym "env")] - `(let [~env-sym ~(if is-ns - `(assoc (cljs.test/empty-env) :return true) - `(assoc ~env-or-ns :return true)) - summary# (reduce - (fn [acc# res#] - (merge-with + - acc# - (:report-counters res#))) - {:test 0 :pass 0 :fail 0 :error 0} - [~@(map - (fn [ns] - `(cljs.test/test-ns ~env-sym ~ns)) - (if is-ns - (concat [env-or-ns] namespaces) - namespaces))])] - (do-report ~env-sym - (assoc summary# :type :summary)) - summary#)))) + (let [is-ns (ns? env-or-ns)] + `(do + ~(if is-ns + `(cljs.test/set-env! (cljs.test/empty-env)) + `(cljs.test/set-env! ~env-or-ns)) + ;; TODO: support async - David + (let [summary# (assoc + (reduce + (fn [acc# res#] + (merge-with + + acc# + (:report-counters res#))) + {:test 0 :pass 0 :fail 0 :error 0} + [~@(map + (fn [ns] + `(cljs.test/test-ns ~ns)) + (if is-ns + (concat [env-or-ns] namespaces) + namespaces))]) + :type :summary)] + (do-report summary#) + summary#))))) (defmacro run-all-tests "Runs all tests in all namespaces; prints results. @@ -295,17 +253,21 @@ (defmacro test-all-vars "Calls test-vars on every var with :test metadata interned in the namespace, with fixtures." - ([ns] - `(cljs.test/test-all-vars - (cljs.test/empty-env) ~ns)) - ([env [quote ns]] - `(cljs.test/test-vars ~env - [~@(map - (fn [[k _]] - `(var ~(symbol (name ns) (name k)))) - (filter - (fn [[_ v]] (:test v)) - (ana-api/ns-interns ns)))]))) + ([[quote ns]] + `(let [env# (cljs.test/get-current-env)] + (when (nil? env#) + (cljs.test/set-env! (cljs.test/empty-env))) + (cljs.test/test-vars + [~@(map + (fn [[k _]] + `(var ~(symbol (name ns) (name k)))) + (filter + (fn [[_ v]] (:test v)) + (ana-api/ns-interns ns)))]) + (when (nil? env#) + (let [ret# (cljs.test/get-current-env)] + (cljs.test/clear-env!) + ret#))))) (defmacro test-ns "If the namespace defines a function named test-ns-hook, calls that. @@ -318,14 +280,15 @@ ([ns] `(cljs.test/test-ns (cljs.test/empty-env) ~ns)) ([env [quote ns :as form]] (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") - `(let [env# ~env - return# (:return env#) - env'# (-> (assoc env# :return true) - (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) - ;; If the namespace has a test-ns-hook function, call that: - ~(if-let [v (ana-api/ns-resolve ns 'test-ns-hook)] - `(~(symbol (name ns) "test-ns-hook")) - ;; Otherwise, just test every var in the namespace. - `(cljs.test/test-all-vars ~form)) - (cljs.test/do-report {:type :end-test-ns, :ns ~form}))] - (when return# env'#)))) + `(do + (cljs.test/set-env! ~env) + (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) + ;; If the namespace has a test-ns-hook function, call that: + ~(if-let [v (ana-api/ns-resolve ns 'test-ns-hook)] + `(~(symbol (name ns) "test-ns-hook")) + ;; Otherwise, just test every var in the namespace. + `(cljs.test/test-all-vars ~form)) + (cljs.test/do-report {:type :end-test-ns, :ns ~form}) + (let [ret# (cljs.test/get-current-env)] + (cljs.test/clear-env!) + ret#)))) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 37206278b..4f10534d8 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -230,29 +230,50 @@ ;; ============================================================================= ;; Default Reporting +(defn empty-env + ([] (empty-env ::default)) + ([reporter] + {:report-counters {:test 0 :pass 0 :fail 0 :error 0} + :testing-vars () + :testing-contexts () + :reporter reporter})) + +(def ^:dynamic *current-env* nil) + +(defn get-current-env [] + (or *current-env* (empty-env))) + +(defn update-current-env! [ks f & args] + (set! *current-env* (apply update-in (get-current-env) ks f args))) + +(defn set-env! [new-env] + (set! *current-env* new-env)) + +(defn clear-env! [] + (set! *current-env* nil)) + (defn testing-vars-str "Returns a string representation of the current test. Renders names in *testing-vars* as a list, then the source file and line of current assertion." - [env m] + [m] (let [{:keys [file line]} m] (str - (reverse (map #(:name (meta %)) (:testing-vars env))) + (reverse (map #(:name (meta %)) (:testing-vars (get-current-env)))) " (" file ":" line ")"))) (defn testing-contexts-str "Returns a string representation of the current test context. Joins strings in *testing-contexts* with spaces." - [env] - (apply str (interpose " " (reverse (:testing-contexts env))))) + [] + (apply str (interpose " " (reverse (:testing-contexts (get-current-env)))))) -(defn inc-report-counter +(defn inc-report-counter! "Increments the named counter in *report-counters*, a ref to a map. Does nothing if *report-counters* is nil." - [env name] - (if (:report-counters env) - (update-in env [:report-counters name] (fnil inc 0)) - env)) + [name] + (if (:report-counters (get-current-env)) + (update-current-env! [:report-counters name] (fnil inc 0)))) (defmulti ^{:doc "Generic reporting function, may be overridden to plug in @@ -260,46 +281,44 @@ 'is' call 'report' to indicate results. The argument given to 'report' will be a map with a :type key." :dynamic true} - report (fn [env m] [(:reporter env) (:type m)])) + report (fn [m] [(:reporter (get-current-env)) (:type m)])) -(defmethod report :default [env m] +(defmethod report :default [m] (prn m)) -(defmethod report [::default :pass] [env m] - (inc-report-counter env :pass)) +(defmethod report [::default :pass] [m] + (inc-report-counter! :pass)) -(defmethod report [::default :fail] [env m] - (println "\nFAIL in" (testing-vars-str env m)) - (when (seq (:testing-contexts env)) - (println (testing-contexts-str env))) +(defmethod report [::default :fail] [m] + (inc-report-counter! :fail) + (println "\nFAIL in" (testing-vars-str m)) + (when (seq (:testing-contexts (get-current-env))) + (println (testing-contexts-str))) (when-let [message (:message m)] (println message)) (println "expected:" (pr-str (:expected m))) - (println " actual:" (pr-str (:actual m))) - (inc-report-counter env :fail)) + (println " actual:" (pr-str (:actual m)))) -(defmethod report [::default :error] [env m] - (println "\nERROR in" (testing-vars-str env m)) - (when (seq (:testing-contexts env)) - (println (testing-contexts-str env))) +(defmethod report [::default :error] [m] + (inc-report-counter! :error) + (println "\nERROR in" (testing-vars-str m)) + (when (seq (:testing-contexts (get-current-env))) + (println (testing-contexts-str))) (when-let [message (:message m)] (println message)) (println "expected:" (pr-str (:expected m))) - (print " actual: ") (prn (:actual m)) - (inc-report-counter env :error)) + (print " actual: ") (prn (:actual m))) -(defmethod report [::default :summary] [env m] +(defmethod report [::default :summary] [m] (println "\nRan" (:test m) "tests containing" (+ (:pass m) (:fail m) (:error m)) "assertions.") - (println (:fail m) "failures," (:error m) "errors.") - env) + (println (:fail m) "failures," (:error m) "errors.")) -(defmethod report [::default :begin-test-ns] [env m] - (println "\nTesting" (name (:ns m))) - env) +(defmethod report [::default :begin-test-ns] [m] + (println "\nTesting" (name (:ns m)))) ;; Ignore these message types: -(defmethod report [::default :end-test-ns] [env m] env) -(defmethod report [::default :begin-test-var] [env m] env) -(defmethod report [::default :end-test-var] [env m] env) +(defmethod report [::default :end-test-ns] [m]) +(defmethod report [::default :begin-test-var] [m]) +(defmethod report [::default :end-test-var] [m]) (defn js-line-and-column [stack-element] (let [parts (.split stack-element ":") @@ -310,9 +329,9 @@ (defn js-filename [stack-element] (first (.split (last (.split stack-element "/out/")) ":"))) -(defn mapped-line-and-column [env filename line column] +(defn mapped-line-and-column [filename line column] (let [default [filename line column]] - (if-let [source-map (:source-map env)] + (if-let [source-map (:source-map (get-current-env))] ;; source maps are 0 indexed for lines (if-let [columns (get-in source-map [filename (dec line)])] (vec @@ -328,7 +347,7 @@ default) default))) -(defn file-and-line [env exception depth] +(defn file-and-line [exception depth] (let [stack (.-stack exception)] (if (and stack (string? stack)) ;; TODO: flesh out @@ -338,33 +357,17 @@ stack-element (nth stacktrace depth) fname (js-filename stack-element) [line column] (js-line-and-column stack-element) - [fname line column] (mapped-line-and-column env fname line column)] + [fname line column] (mapped-line-and-column fname line column)] {:file fname :line line :column column}) {:file (.-fileName exception) :line (.-lineNumber exception)})) ) -(defn do-report [env m] - {:post [(map? %)]} +(defn do-report [m] (let [m (case (:type m) - :fail (merge (file-and-line env (js/Error.) 4) m) - :error (merge (file-and-line env (:actual m) 0) m) + :fail (merge (file-and-line (js/Error.) 4) m) + :error (merge (file-and-line (:actual m) 0) m) m)] - (report env m))) - -(defn empty-env - ([] (empty-env ::default)) - ([reporter] - {:report-counters {:test 0 :pass 0 :fail 0 :error 0} - :testing-vars () - :testing-contexts () - :reporter reporter})) - -(defn step - [env thunk] - (let [ret (thunk)] - (if (and (fn? ret) (-> ret meta :cljs.test/step)) - (ret env) - (assoc env :last-value ret)))) + (report m))) ;; ============================================================================= ;; Low-level functions @@ -372,24 +375,22 @@ (defn test-var "If v has a function in its :test metadata, calls that function, add v to :testing-vars property of env." - ([v] (test-var (empty-env) v)) - ([env v] - {:pre [(map? env) (instance? Var v)]} - (if-let [t (:test (meta v))] - (let [env (-> env - (update-in [:testing-vars] conj v) - (update-in [:report-counters :test] inc))] - (try - (let [env' (t env)] - (when (:return env') env')) - (catch :default e - (let [env' (do-report env - {:type :error - :message "Uncaught exception, not in assertion." - :expected nil - :actual e})] - (when (:return env') env'))))) - env))) + [v] + {:pre [(instance? Var v)]} + (if-let [t (:test (meta v))] + (do + (update-current-env! [:testing-vars] conj v) + (update-current-env! [:report-counters :test] inc) + (do-report {:type :begin-test-var :var v}) + (try + (t) + (catch :default e + (do-report + {:type :error + :message "Uncaught exception, not in assertion." + :expected nil + :actual e}))) + (do-report {:type :end-test-var :var v})))) (defn- default-fixture "The default, empty, fixture function. Just calls its argument." @@ -411,29 +412,16 @@ (defn test-vars "Groups vars by their namespace and runs test-vars on them with appropriate fixtures applied." - ([vars] (test-vars (empty-env) vars)) - ([env vars] - (let [return (:return env) - env' (reduce - (fn [env [ns vars]] - (let [once-fixture-fn (join-fixtures (:once-fixtures env)) - each-fixture-fn (join-fixtures (:each-fixtures env))] - (once-fixture-fn - (fn [] - (reduce - (fn [env v] - (if (:test (meta v)) - (each-fixture-fn - (fn [] - (test-var - (assoc env - :testing-vars () - :testing-contexts ()) - v))) - env)) - env vars))))) - (assoc env :return true) (group-by (comp :ns meta) vars))] - (when return env')))) + [vars] + (doseq [[ns vars] (group-by (comp :ns meta) vars)] + (let [env (get-current-env) + once-fixture-fn (join-fixtures (get-in env [:once-fixtures ns])) + each-fixture-fn (join-fixtures (get-in env [:each-fixtures ns]))] + (once-fixture-fn + (fn [] + (doseq [v vars] + (when (:test (meta v)) + (each-fixture-fn (fn [] (test-var v)))))))))) ;; ============================================================================= ;; Running Tests, high level functions From 9e7de61b2b476b8af617595497da59cb3328cd61 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Dec 2014 16:46:20 -0500 Subject: [PATCH 0286/4033] convert the final set of tests, copy & paste affair --- test/cljs/cljs/core_test.cljs | 267 ++++++++++++++++------------------ test/cljs/test_runner.cljs | 2 - 2 files changed, 122 insertions(+), 147 deletions(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 660c472ec..f489cd197 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2307,6 +2307,40 @@ (= (vec (range 100)) (persistent! v)))))) (deftest test-phm + ;; PersistentHashMap & TransientHashMap + (loop [m1 cljs.core.PersistentHashMap.EMPTY + m2 (transient cljs.core.PersistentHashMap.EMPTY) + i 0] + (if (< i 100) + (recur (assoc m1 i i) (assoc! m2 i i) (inc i)) + (let [m2 (persistent! m2)] + (is (= (count m1) 100)) + (is (= (count m2) 100)) + (is (= m1 m2)) + (loop [i 0] + (if (< i 100) + (do (is (= (m1 i) i)) + (is (= (m2 i) i)) + (is (= (get m1 i) i)) + (is (= (get m2 i) i)) + (is (contains? m1 i)) + (is (contains? m2 i)) + (recur (inc i))))) + (is (= (map vector (range 100) (range 100)) (sort-by first (seq m1)))) + (is (= (map vector (range 100) (range 100)) (sort-by first (seq m2)))) + (is (not (contains? (dissoc m1 3) 3)))))) + (let [tm (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY)) + (dissoc 3 5 7) + transient)] + (doseq [k [0 1 2 4 6 8 9]] + (is (= k (get tm k)))) + (let [m (persistent! tm)] + (is (= 2 (try (dissoc! tm 1) 1 (catch js/Error e 2)))) + (is (= 2 (try (assoc! tm 10 10) 1 (catch js/Error e 2)))) + (is (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) + (is (= 2 (try (count tm) 1 (catch js/Error e 2)))) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) (let [m (-> (->> (interleave (range 10) (range 10)) (apply assoc cljs.core.PersistentHashMap.EMPTY)) (dissoc 3 5 7))] @@ -2339,194 +2373,135 @@ (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))))) ) -(deftest test-phm-fixed-hash) +(deftype FixedHash [h v] + IHash + (-hash [this] h) + IEquiv + (-equiv [this other] + (and (instance? FixedHash other) (= v (.-v other))))) -(defn test-stuff [] - ;; PersistentHashMap & TransientHashMap - (loop [m1 cljs.core.PersistentHashMap.EMPTY - m2 (transient cljs.core.PersistentHashMap.EMPTY) - i 0] - (if (< i 100) - (recur (assoc m1 i i) (assoc! m2 i i) (inc i)) - (let [m2 (persistent! m2)] - (assert (= (count m1) 100)) - (assert (= (count m2) 100)) - (assert (= m1 m2)) - (loop [i 0] - (if (< i 100) - (do (assert (= (m1 i) i)) - (assert (= (m2 i) i)) - (assert (= (get m1 i) i)) - (assert (= (get m2 i) i)) - (assert (contains? m1 i)) - (assert (contains? m2 i)) - (recur (inc i))))) - (assert (= (map vector (range 100) (range 100)) (sort-by first (seq m1)))) - (assert (= (map vector (range 100) (range 100)) (sort-by first (seq m2)))) - (assert (not (contains? (dissoc m1 3) 3)))))) - (let [tm (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY)) - (dissoc 3 5 7) - transient)] - (doseq [k [0 1 2 4 6 8 9]] - (assert (= k (get tm k)))) - (let [m (persistent! tm)] - (assert (= 2 (try (dissoc! tm 1) 1 (catch js/Error e 2)))) - (assert (= 2 (try (assoc! tm 10 10) 1 (catch js/Error e 2)))) - (assert (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) - (assert (= 2 (try (count tm) 1 (catch js/Error e 2)))) - (assert (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) - - (deftype FixedHash [h v] - IHash - (-hash [this] h) - IEquiv - (-equiv [this other] - (and (instance? FixedHash other) (= v (.-v other))))) - - (def fixed-hash-foo (FixedHash. 0 :foo)) - (def fixed-hash-bar (FixedHash. 0 :bar)) +(def fixed-hash-foo (FixedHash. 0 :foo)) +(def fixed-hash-bar (FixedHash. 0 :bar)) +(deftest test-phm-fixed-hash (let [m (assoc cljs.core.PersistentHashMap.EMPTY fixed-hash-foo 1 fixed-hash-bar 2)] - (assert (= (get m fixed-hash-foo) 1)) - (assert (= (get m fixed-hash-bar) 2)) - (assert (= (count m) 2)) + (is (= (get m fixed-hash-foo) 1)) + (is (= (get m fixed-hash-bar) 2)) + (is (= (count m) 2)) (let [m (dissoc m fixed-hash-foo)] - (assert (= (get m fixed-hash-bar) 2)) - (assert (not (contains? m fixed-hash-foo))) - (assert (= (count m) 1)))) + (is (= (get m fixed-hash-bar) 2)) + (is (not (contains? m fixed-hash-foo))) + (is (= (count m) 1)))) (let [m (into cljs.core.PersistentHashMap.EMPTY ; make sure we're testing - (zipmap (range 100) (range 100))) ; the correct map type + (zipmap (range 100) (range 100))) ; the correct map type m (assoc m fixed-hash-foo 1 fixed-hash-bar 2)] - (assert (= (count m) 102)) - (assert (= (get m fixed-hash-foo) 1)) - (assert (= (get m fixed-hash-bar) 2)) + (is (= (count m) 102)) + (is (= (get m fixed-hash-foo) 1)) + (is (= (get m fixed-hash-bar) 2)) (let [m (dissoc m 3 5 7 fixed-hash-foo)] - (assert (= (get m fixed-hash-bar) 2)) - (assert (not (contains? m fixed-hash-foo))) - (assert (= (count m) 98)))) + (is (= (get m fixed-hash-bar) 2)) + (is (not (contains? m fixed-hash-foo))) + (is (= (count m) 98)))) (let [m (into cljs.core.PersistentHashMap.EMPTY ; make sure we're testing - (zipmap (range 100) (range 100))) ; the correct map type + (zipmap (range 100) (range 100))) ; the correct map type m (transient m) m (assoc! m fixed-hash-foo 1) m (assoc! m fixed-hash-bar 2) m (persistent! m)] - (assert (= (count m) 102)) - (assert (= (get m fixed-hash-foo) 1)) - (assert (= (get m fixed-hash-bar) 2)) + (is (= (count m) 102)) + (is (= (get m fixed-hash-foo) 1)) + (is (= (get m fixed-hash-bar) 2)) (let [m (dissoc m 3 5 7 fixed-hash-foo)] - (assert (= (get m fixed-hash-bar) 2)) - (assert (not (contains? m fixed-hash-foo))) - (assert (= (count m) 98)))) - - ;; PersistentArrayMap & TransientArrayMap - (def array-map-conversion-threshold - cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD) - (loop [m1 cljs.core.PersistentArrayMap.EMPTY - m2 (transient cljs.core.PersistentArrayMap.EMPTY) - i 0] - (if (< i array-map-conversion-threshold) - (recur (assoc m1 i i) (assoc! m2 i i) (inc i)) - (let [m2 (persistent! m2)] - (assert (= (count m1) array-map-conversion-threshold)) - (assert (= (count m2) array-map-conversion-threshold)) - (assert (= m1 m2)) - (loop [i 0] - (if (< i array-map-conversion-threshold) - (do (assert (= (m1 i) i)) - (assert (= (m2 i) i)) - (assert (= (get m1 i) i)) - (assert (= (get m2 i) i)) - (assert (contains? m1 i)) - (assert (contains? m2 i)) - (recur (inc i))))) - (assert (= (map vector - (range array-map-conversion-threshold) - (range array-map-conversion-threshold)) - (sort-by first (seq m1)))) - (assert (= (map vector - (range array-map-conversion-threshold) - (range array-map-conversion-threshold)) - (sort-by first (seq m2)))) - (assert (not (contains? (dissoc m1 3) 3)))))) + (is (= (get m fixed-hash-bar) 2)) + (is (not (contains? m fixed-hash-foo))) + (is (= (count m) 98))))) + +(def array-map-conversion-threshold + cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD) + +(deftest test-pam (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY)) - (dissoc 3 5 7))] - (assert (= (count m) 7)) - (assert (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9}))) + (apply assoc cljs.core.PersistentArrayMap.EMPTY)) + (dissoc 3 5 7))] + (is (= (count m) 7)) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9}))) (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY)) - (conj [:foo 1]))] - (assert (= (count m) 11)) - (assert (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) + (apply assoc cljs.core.PersistentArrayMap.EMPTY)) + (conj [:foo 1]))] + (is (= (count m) 11)) + (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY) - transient) - (conj! [:foo 1]) - persistent!)] - (assert (= (count m) 11)) - (assert (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) - (let [tm (->> (interleave (range 10) (range 10)) (apply assoc cljs.core.PersistentArrayMap.EMPTY) - transient)] + transient) + (conj! [:foo 1]) + persistent!)] + (is (= (count m) 11)) + (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) + (let [tm (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentArrayMap.EMPTY) + transient)] (loop [tm tm ks [3 5 7]] (if-let [k (first ks)] (recur (dissoc! tm k) (next ks)) (let [m (persistent! tm)] - (assert (= (count m) 7)) - (assert (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))) + (is (= (count m) 7)) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))) (let [tm (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY)) - (dissoc 3 5 7) - transient)] + (apply assoc cljs.core.PersistentArrayMap.EMPTY)) + (dissoc 3 5 7) + transient)] (doseq [k [0 1 2 4 6 8 9]] - (assert (= k (get tm k)))) + (is (= k (get tm k)))) (let [m (persistent! tm)] - (assert (= 2 (try (dissoc! tm 1) 1 (catch js/Error e 2)))) - (assert (= 2 (try (assoc! tm 10 10) 1 (catch js/Error e 2)))) - (assert (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) - (assert (= 2 (try (count tm) 1 (catch js/Error e 2)))) - (assert (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) + (is (= 2 (try (dissoc! tm 1) 1 (catch js/Error e 2)))) + (is (= 2 (try (assoc! tm 10 10) 1 (catch js/Error e 2)))) + (is (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) + (is (= 2 (try (count tm) 1 (catch js/Error e 2)))) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) (let [m (apply assoc cljs.core.PersistentArrayMap.EMPTY - (interleave (range (* 2 array-map-conversion-threshold)) - (range (* 2 array-map-conversion-threshold))))] - (assert (= (count m) (* 2 array-map-conversion-threshold))) - (assert (= (m array-map-conversion-threshold) array-map-conversion-threshold)) - (assert (= m (into cljs.core.PersistentHashMap.EMPTY - (map #(vector % %) - (range (* 2 array-map-conversion-threshold))))))) - - ;; literal maps + (interleave (range (* 2 array-map-conversion-threshold)) + (range (* 2 array-map-conversion-threshold))))] + (is (= (count m) (* 2 array-map-conversion-threshold))) + (is (= (m array-map-conversion-threshold) array-map-conversion-threshold)) + (is (= m (into cljs.core.PersistentHashMap.EMPTY + (map #(vector % %) + (range (* 2 array-map-conversion-threshold))))))) + ) + +(deftest test-literal-maps (loop [m1 {} m2 {} i 0] (if (< i 100) (recur (assoc m1 i i) (assoc m2 (str "foo" i) i) (inc i)) - (do (assert (= m1 (into cljs.core.PersistentHashMap.EMPTY - (map vector (range 100) (range 100))))) - (assert (= m2 (into cljs.core.PersistentHashMap.EMPTY - (map vector - (map (partial str "foo") (range 100)) - (range 100))))) - (assert (= (count m1) 100)) - (assert (= (count m2) 100))))) + (do (is (= m1 (into cljs.core.PersistentHashMap.EMPTY + (map vector (range 100) (range 100))))) + (is (= m2 (into cljs.core.PersistentHashMap.EMPTY + (map vector + (map (partial str "foo") (range 100)) + (range 100))))) + (is (= (count m1) 100)) + (is (= (count m2) 100))))) + ) +(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})] - (assert (= result expected))))) + (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))) (let [n (inc (+ cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD 2)) expected (repeat n {:foo :bar})] - (assert (= result expected))))) + (is (= result expected)))))) +(deftest test-transient-hash-set ;; TransientHashSet (loop [s (transient #{}) i 0] @@ -2538,12 +2513,14 @@ (recur (disj! s i) (inc i)) (recur s (inc i))) (let [s (persistent! s)] - (assert (= s (loop [s #{} xs (remove #(zero? (mod % 3)) (range 100))] + (is (= s (loop [s #{} xs (remove #(zero? (mod % 3)) (range 100))] (if-let [x (first xs)] (recur (conj s x) (next xs)) s)))) - (assert (= s (set (remove #(zero? (mod % 3)) (range 100)))))))))) + (is (= s (set (remove #(zero? (mod % 3)) (range 100)))))))))) +) +(comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) ;; m (apply obj-map (interleave ks (range 500)))] diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index 05794c408..e0c460d51 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -17,8 +17,6 @@ (set! *print-newline* false) (set-print-fn! js/print) -#_(core-test/test-stuff) - (run-tests 'cljs.core-test 'cljs.reader-test From e342ec4904d25111456e6530ea3ec0f5d3bf9846 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Dec 2014 01:20:38 -0500 Subject: [PATCH 0287/4033] change for every? true? -> doseq + is --- test/cljs/cljs/core_test.cljs | 151 ++++++++++++++++------------------ 1 file changed, 70 insertions(+), 81 deletions(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index f489cd197..3b49f7487 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1155,12 +1155,10 @@ (no-dispatch-value {:test :test}) (catch js/Error e (not= -1 (.indexOf (.-message e) "cljs.core-test/no-dispatch-value"))))) - (is (every? true? - (for [m [(obj-map) (array-map) (hash-map) (sorted-map)]] - (my-map? m)))) - (is (every? true? - (for [not-m [[] 1 "asdf" :foo]] - (not (my-map? not-m))))) + (doseq [m [(obj-map) (array-map) (hash-map) (sorted-map)]] + (is (my-map? m))) + (doseq [not-m [[] 1 "asdf" :foo]] + (is (not (my-map? not-m)))) ;; multimethod hashing (is (= foo2' (ffirst {foo2' 1}))) ))) @@ -1481,16 +1479,15 @@ (is (= (pr-str (PrintMe. 1 2)) "#cljs.core-test.PrintMe{:a 1, :b 2}")) (is (= (pr-str (js/Date. "2010-11-12T13:14:15.666-05:00")) "#inst \"2010-11-12T18:14:15.666-00:00\"")) - (is (every? true? - (for [month (range 1 13) - day (range 1 29) - hour (range 1 23)] - (let [pad (fn [n] - (if (< n 10) - (str "0" n) - n)) - inst (str "2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-00:00")] - (= (pr-str (js/Date. inst)) (str "#inst \"" inst "\"")))))) + (doseq [month (range 1 13) + day (range 1 29) + hour (range 1 23)] + (let [pad (fn [n] + (if (< n 10) + (str "0" n) + n)) + inst (str "2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-00:00")] + (is (= (pr-str (js/Date. inst)) (str "#inst \"" inst "\""))))) (let [uuid-str "550e8400-e29b-41d4-a716-446655440000" uuid (UUID. uuid-str)] (is (= (pr-str uuid) (str "#uuid \"" uuid-str "\"")))) @@ -1864,12 +1861,11 @@ (fn [r k v] (-> r (conj [k v]))) [] data)))))] (testing "Testing IKVReduce" - (is (every? true? - (for [[data expect] [[(obj-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] - [(hash-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] - [(array-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] - [[:v0 :v1] [[0 :v0] [1 :v1]]]]] - (kvr-test data expect)))) + (doseq [[data expect] [[(obj-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] + [(hash-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] + [(array-map :k0 :v0 :k1 :v1) [[:k0 :v0] [:k1 :v1]]] + [[:v0 :v1] [[0 :v0] [1 :v1]]]]] + (is (kvr-test data expect))) (is (= {:init :val} (reduce-kv assoc {:init :val} nil)))))) (deftest test-data-conveying-exceptions @@ -2096,22 +2092,19 @@ (deftest test-767 (testing "Testing CLJS-767, invalid assoc" - (is (every? true? - (for [n [nil "-1" "" "0" "1" false true (js-obj)]] - (and - (= :fail (try (assoc [1 2] n 4) - (catch js/Error e :fail))) - (= :fail (try (assoc (subvec [1 2 3] 2) n 4) - (catch js/Error e :fail))) - (= :fail (try (assoc (range 1 3) n 4) - (catch js/Error e :fail))))))))) + (doseq [n [nil "-1" "" "0" "1" false true (js-obj)]] + (is (= :fail (try (assoc [1 2] n 4) + (catch js/Error e :fail)))) + (is (= :fail (try (assoc (subvec [1 2 3] 2) n 4) + (catch js/Error e :fail)))) + (is (= :fail (try (assoc (range 1 3) n 4) + (catch js/Error e :fail))))))) (deftest test-768 (testing "Testing CLJS-768, invalid assoc!" - (is (every? true? - (for [n [nil "-1" "" "0" "1" false true (js-obj)]] - (= :fail (try (assoc! (transient [1 2]) n 4) - (catch js/Error e :fail)))))))) + (doseq [n [nil "-1" "" "0" "1" false true (js-obj)]] + (is (= :fail (try (assoc! (transient [1 2]) n 4) + (catch js/Error e :fail))))))) (defn cljs-739 [arr names] (let [name (first names)] @@ -2129,28 +2122,26 @@ (deftest test-728 (testing "Testing CLJS-728, lookup with default" - (is (every? true? - (for [n [nil "-1" "" "0" "1" false true (js-obj)]] - (and - (nil? (get [1 2] n)) - (= :fail (try (nth [1 2] n) (catch js/Error e :fail))) - (= 4 (get [1 2] n 4)) - (= :fail (try (nth [1 2] n 4) (catch js/Error e :fail))) - - (nil? (get (subvec [1 2] 1) n)) - (= :fail (try (nth (subvec [1 2] 1) n) (catch js/Error e :fail))) - (= 4 (get (subvec [1 2] 1) n 4)) - (= :fail (try (nth (subvec [1 2] 1) n 4) (catch js/Error e :fail))) - - (nil? (get (transient [1 2]) n)) - (= :fail (try (nth (transient [1 2]) n) (catch js/Error e :fail))) - (= 4 (get (transient [1 2]) n 4)) - (= :fail (try (nth (transient [1 2]) n 4) (catch js/Error e :fail))) - - (nil? (get (range 1 3) n)) - (= :fail (try (nth (range 1 3) n) (catch js/Error e :fail))) - (= 4 (get (range 1 3) n 4)) - (= :fail (try (nth (range 1 3) n 4) (catch js/Error e :fail)))))))) + (doseq [n [nil "-1" "" "0" "1" false true (js-obj)]] + (is (nil? (get [1 2] n))) + (is (= :fail (try (nth [1 2] n) (catch js/Error e :fail)))) + (is (= 4 (get [1 2] n 4))) + (is (= :fail (try (nth [1 2] n 4) (catch js/Error e :fail)))) + + (is (nil? (get (subvec [1 2] 1) n))) + (is (= :fail (try (nth (subvec [1 2] 1) n) (catch js/Error e :fail)))) + (is (= 4 (get (subvec [1 2] 1) n 4))) + (is (= :fail (try (nth (subvec [1 2] 1) n 4) (catch js/Error e :fail)))) + + (is (nil? (get (transient [1 2]) n))) + (is (= :fail (try (nth (transient [1 2]) n) (catch js/Error e :fail)))) + (is (= 4 (get (transient [1 2]) n 4))) + (is (= :fail (try (nth (transient [1 2]) n 4) (catch js/Error e :fail)))) + + (is (nil? (get (range 1 3) n))) + (is (= :fail (try (nth (range 1 3) n) (catch js/Error e :fail)))) + (is (= 4 (get (range 1 3) n 4))) + (is (= :fail (try (nth (range 1 3) n 4) (catch js/Error e :fail)))))) ) (deftest test-778 @@ -2175,30 +2166,28 @@ (deftest test-784 (testing "Testing CLJS-784, conj on maps" - (is (every? true? - (for [m [(array-map) (hash-map) (sorted-map)]] - (and (= :ok - (try - (conj m "foo") - (catch js/Error _ - :ok))) - (= {:foo 1} (conj m [:foo 1])) - (= {:foo 1} (conj m {:foo 1})) - (= {:foo 1} (conj m (list [:foo 1]))))))) - (is (every? true? - (for [mt [array-map hash-map sorted-map]] - (= {:foo 1 :bar 2 :baz 3} - (conj (mt :foo 1) - ((fn make-seq [from-seq] - ;; this tests specifically for user defined seq's, that implement the bare minimum, i.e. no INext - (when (seq from-seq) - (reify - ISeqable - (-seq [this] this) - ISeq - (-first [this] (first from-seq)) - (-rest [this] (make-seq (rest from-seq)))))) - [[:bar 2] [:baz 3]]))))))) + (doseq [m [(array-map) (hash-map) (sorted-map)]] + (is (= :ok + (try + (conj m "foo") + (catch js/Error _ + :ok)))) + (is (= {:foo 1} (conj m [:foo 1]))) + (is (= {:foo 1} (conj m {:foo 1}))) + (is (= {:foo 1} (conj m (list [:foo 1]))))) + (doseq [mt [array-map hash-map sorted-map]] + (is (= {:foo 1 :bar 2 :baz 3} + (conj (mt :foo 1) + ((fn make-seq [from-seq] + ;; this tests specifically for user defined seq's, that implement the bare minimum, i.e. no INext + (when (seq from-seq) + (reify + ISeqable + (-seq [this] this) + ISeq + (-first [this] (first from-seq)) + (-rest [this] (make-seq (rest from-seq)))))) + [[:bar 2] [:baz 3]])))))) ) (deftest test-case-keyword From e86e2f0029ce61656ae440eccf141832a988bbb4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Dec 2014 12:46:22 -0500 Subject: [PATCH 0288/4033] In `parse 'var` case in cljs/analyzer.clj disambiguate `name` & `var-name`. Get `:arglists` meta directly from `sym-meta` so that `:pre` and `:post` meta is preserved as handled by `clojure.core/defn` macro when examining vars. --- src/clj/cljs/analyzer.clj | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index c08c4b1c7..216407df2 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -508,9 +508,10 @@ {:env env :op :var-special :form form :var (analyze env sym) :sym (analyze env `(quote ~(symbol (name (:ns var)) (name (:name var))))) - :meta (let [ks [:ns :name :doc :arglists :file :line :column] + :meta (let [ks [:ns :name :doc :file :line :column] m (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) - :test `(when ~sym (.-cljs$lang$test ~sym)))] + :test `(when ~sym (.-cljs$lang$test ~sym)) + :arglists (:arglists var))] (analyze env m))})) (defmethod parse 'if @@ -637,14 +638,14 @@ (swap! env/*compiler* update-in [::namespaces ns-name :excludes] conj sym) (update-in env [:ns :excludes] conj sym)) env) - name (:name (resolve-var (dissoc env :locals) sym)) + var-name (:name (resolve-var (dissoc env :locals) sym)) init-expr (when (contains? args :init) (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] (merge - {:name name} + {:name var-name} sym-meta (when dynamic {:dynamic true}) - (source-info name env))) + (source-info var-name env))) (disallowing-recur (analyze (assoc env :context :expr) (:init args) sym))) fn-var? (and init-expr (= (:op init-expr) :fn)) @@ -652,7 +653,7 @@ (or (:ret-tag init-expr) tag) tag) export-as (when-let [export-val (-> sym meta :export)] - (if (= true export-val) name export-val)) + (if (= true export-val) var-name export-val)) doc (or (:doc args) (-> sym meta :doc))] (when-let [v (get-in @env/*compiler* [::namespaces ns-name :defs sym])] (when (and (not (-> sym meta :declared)) @@ -660,11 +661,11 @@ (warning :fn-var env {:ns-name ns-name :sym sym}))) (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] (merge - {:name name} + {:name var-name} sym-meta (when doc {:doc doc}) (when dynamic {:dynamic true}) - (source-info name env) + (source-info var-name env) ;; the protocol a protocol fn belongs to (when protocol {:protocol protocol}) @@ -683,7 +684,7 @@ :variadic (:variadic init-expr) :max-fixed-arity (:max-fixed-arity init-expr) :method-params params - :arglists params + :arglists (:arglists sym-meta) :methods (map (fn [method] (let [tag (infer-tag env (assoc method :op :method))] (cond-> (select-keys method @@ -693,7 +694,7 @@ (when (and fn-var? tag) {:ret-tag tag}))) (merge {:env env :op :def :form form - :name name + :name var-name :var (assoc (analyze (-> env (dissoc :locals) ;; if there's :test var metadata will need locals (assoc :test-locals locals) From ddc743732dfd9bef852611cb91cf1a8487dd7801 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Dec 2014 13:06:32 -0500 Subject: [PATCH 0289/4033] convert reader tests from more every? + true? -> doseq + is --- test/cljs/cljs/reader_test.cljs | 91 ++++++++++++++++----------------- 1 file changed, 44 insertions(+), 47 deletions(-) diff --git a/test/cljs/cljs/reader_test.cljs b/test/cljs/cljs/reader_test.cljs index 16bd67264..3835f3ffd 100644 --- a/test/cljs/cljs/reader_test.cljs +++ b/test/cljs/cljs/reader_test.cljs @@ -69,13 +69,12 @@ (.valueOf (reader/read-string (pr-str est-inst))))) (is (= (.valueOf est-inst) (.valueOf utc-inst))) - (is (every? true? - (for [month (range 1 13) - day (range 1 29) - hour (range 1 23)] - (let [s (str "#inst \"2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-06:00\"")] - (= (-> s reader/read-string .valueOf) - (-> s reader/read-string pr-str reader/read-string .valueOf)))))))) + (doseq [month (range 1 13) + day (range 1 29) + hour (range 1 23)] + (let [s (str "#inst \"2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-06:00\"")] + (is (= (-> s reader/read-string .valueOf) + (-> s reader/read-string pr-str reader/read-string .valueOf))))))) (let [u (reader/read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\"")] (testing "Testing reading UUID literals" @@ -99,50 +98,48 @@ ;; Unicode Tests (testing "Test reading unicode - strings, symbols, keywords" - (is (every? true? - (for [unicode - ["اختبار" ; arabic - "ทดสอบ" ; thai - "こんにちは" ; japanese hiragana - "你好" ; chinese traditional - "אַ גוט יאָר" ; yiddish - "cześć" ; polish - "привет" ; russian - - ;; RTL languages skipped below because tricky to insert - ;; ' and : at the "start" - - 'ทดสอบ - 'こんにちは - '你好 - 'cześć - 'привет - - :ทดสอบ - :こんにちは - :你好 - :cześć - :привет + (doseq [unicode + ["اختبار" ; arabic + "ทดสอบ" ; thai + "こんにちは" ; japanese hiragana + "你好" ; chinese traditional + "אַ גוט יאָר" ; yiddish + "cześć" ; polish + "привет" ; russian + + ;; RTL languages skipped below because tricky to insert + ;; ' and : at the "start" + + 'ทดสอบ + 'こんにちは + '你好 + 'cześć + 'привет + + :ทดสอบ + :こんにちは + :你好 + :cześć + :привет ;compound data - {:привет :ru "你好" :cn} - ]] - (let [input (pr-str unicode) - read (reader/read-string input)] - (= unicode read)))))) + {:привет :ru "你好" :cn} + ]] + (let [input (pr-str unicode) + read (reader/read-string input)] + (is (= unicode read))))) (testing "Testing unicode error cases" - (is (every? true? - (for [unicode-error - ["\"abc \\ua\"" ; truncated - "\"abc \\x0z ...etc\"" ; incorrect code - "\"abc \\u0g00 ..etc\"" ; incorrect code - ]] - (let [r (try - (reader/read-string unicode-error) - :failed-to-throw - (catch js/Error e :ok))] - (= r :ok)))))) + (doseq [unicode-error + ["\"abc \\ua\"" ; truncated + "\"abc \\x0z ...etc\"" ; incorrect code + "\"abc \\u0g00 ..etc\"" ; incorrect code + ]] + (let [r (try + (reader/read-string unicode-error) + :failed-to-throw + (catch js/Error e :ok))] + (is (= r :ok))))) ) (deftest test-717 From 163f079407d1310755d326884b6965d9d74ed3c5 Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Mon, 15 Dec 2014 15:48:15 -0500 Subject: [PATCH 0290/4033] CLJS-909: Add stable api for consumers of compiler data. --- src/clj/cljs/build/api.clj | 57 +++++++++++++++++++++++++++++++ test/clj/cljs/build_api_tests.clj | 39 +++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/clj/cljs/build/api.clj create mode 100644 test/clj/cljs/build_api_tests.clj diff --git a/src/clj/cljs/build/api.clj b/src/clj/cljs/build/api.clj new file mode 100644 index 000000000..6e29d205c --- /dev/null +++ b/src/clj/cljs/build/api.clj @@ -0,0 +1,57 @@ +; 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.build.api + "This is intended to be a stable api for those who intend to create + tools that use compiler data. + + For example: a build script may need to how to invalidate compiled + files so that they will be recompiled." + (:require [cljs.util :as util] + [cljs.analyzer] + [cljs.env :as env] + [cljs.closure] + [clojure.set :refer [intersection]])) + +(defn target-file-for-cljs-ns + "Given an output directory and a clojurescript namespace return the + compilation target file for that namespace. + + For example: + (target-file-from-cljs-ns \"resources/out\" 'example.core) -> + " + [output-dir ns-sym] + (util/to-target-file (cljs.closure/output-directory { :output-dir output-dir }) + {:ns ns-sym })) + +(defn mark-cljs-ns-for-recompile! + "Backdates a cljs target file so that it the cljs compiler will recompile it." + [output-dir ns-sym] + (let [s (target-file-for-cljs-ns output-dir ns-sym)] + (when (.exists s) + (.setLastModified s 5000)))) + +(defn cljs-dependents-for-macro-namespaces + "Takes a list of Clojure (.clj) namespaces that define macros and + returns a list ClojureScript (.cljs) namespaces that depend on those macro + namespaces. + + For example where example.macros is defined in the clojure file + \"example/macros.clj\" and both 'example.core and 'example.util are + ClojureScript namespaces that require and use the macros from + 'example.macros : + (cljs-dependents-for-macro-namespaces 'example.macros) -> + ('example.core 'example.util) + + This must be called when cljs.env/*compiler* is bound to the + compile env that you are inspecting. See cljs.env/with-compile-env." + [namespaces] + (map :name + (let [namespaces-set (set namespaces)] + (filter (fn [x] (not-empty + (intersection namespaces-set (-> x :require-macros vals set)))) + (vals (:cljs.analyzer/namespaces @env/*compiler*)))))) diff --git a/test/clj/cljs/build_api_tests.clj b/test/clj/cljs/build_api_tests.clj new file mode 100644 index 000000000..c503a4564 --- /dev/null +++ b/test/clj/cljs/build_api_tests.clj @@ -0,0 +1,39 @@ +(ns cljs.build-api-tests + (:use cljs.build.api) + (:use clojure.test) + (:require + [cljs.env :as env] + [cljs.analyzer])) + +(deftest test-target-file-for-cljs-ns + (is (= (.getPath (target-file-for-cljs-ns nil 'example.core-lib)) + "out/example/core_lib.js")) + (is (= (.getPath (target-file-for-cljs-ns "output" 'example.core-lib)) + "output/example/core_lib.js"))) + +(deftest test-cljs-dependents-for-macro-namespaces + (env/with-compiler-env (env/default-compiler-env) + (swap! env/*compiler* assoc :cljs.analyzer/namespaces + { 'example.core + {:require-macros {'example.macros 'example.macros + 'mac 'example.macros} + :name 'example.core} + 'example.util + {:require-macros {'example.macros 'example.macros + 'mac 'example.macros} + :name 'example.util} + 'example.helpers + {:require-macros {'example.macros-again 'example.macros-again + 'mac 'example.macros-again} + :name 'example.helpers } + 'example.fun + {:require-macros nil + :name 'example.fun }}) + (is (= (set (cljs-dependents-for-macro-namespaces ['example.macros])) + #{'example.core 'example.util})) + (is (= (set (cljs-dependents-for-macro-namespaces ['example.macros-again])) + #{'example.helpers})) + (is (= (set (cljs-dependents-for-macro-namespaces ['example.macros 'example.macros-again])) + #{'example.core 'example.util 'example.helpers})) + (is (= (set (cljs-dependents-for-macro-namespaces ['example.not-macros])) + #{})))) From 491dd1bb6ba446407298d6fb93dd6cbd578d3b76 Mon Sep 17 00:00:00 2001 From: spinningtopsofdoom Date: Tue, 16 Dec 2014 09:57:47 -0600 Subject: [PATCH 0291/4033] CLJS-696: remove arguments usage from defrecord constructor All defrecord constructors are passed metadata, an extra hash map, and the records hash in addition to the fields of the record --- src/clj/cljs/analyzer.clj | 11 ++++++++--- src/clj/cljs/compiler.clj | 14 ++------------ src/clj/cljs/core.clj | 7 ++++--- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 216407df2..c13ce2d21 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -951,8 +951,12 @@ (disallowing-recur (let [enve (assoc env :context :expr) ctorexpr (analyze enve ctor) - argexprs (vec (map #(analyze enve %) args)) - known-num-fields (:num-fields (resolve-existing-var env ctor)) + ctor-var (resolve-existing-var env ctor) + record-args + (when (and (:record ctor-var) (not (-> ctor meta :internal-ctor))) + (repeat 3 (analyze enve nil))) + argexprs (into (vec (map #(analyze enve %) args)) record-args) + known-num-fields (:num-fields ctor-var) argc (count args)] (when (and (not (-> ctor meta :internal-ctor)) known-num-fields (not= known-num-fields argc)) @@ -1244,7 +1248,8 @@ (let [m (assoc (or m {}) :name t :type true - :num-fields (count fields))] + :num-fields (count fields) + :record (= :defrecord* op))] (merge m (dissoc (meta tsym) :protocols) {:protocols (-> tsym meta :protocols)} diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index fec9865b3..91d4a7a5a 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -812,7 +812,7 @@ (defmethod emit* :defrecord* [{:keys [t fields pmasks body]}] - (let [fields (concat (map munge fields) '[__meta __extmap])] + (let [fields (concat (map munge fields) '[__meta __extmap __hash])] (emitln "") (emitln "/**") (emitln "* @constructor") @@ -820,23 +820,13 @@ (emitln "* @param {*} " fld)) (emitln "* @param {*=} __meta ") (emitln "* @param {*=} __extmap") + (emitln "* @param {number|null} __hash") (emitln "*/") (emitln (munge t) " = (function (" (comma-sep fields) "){") (doseq [fld fields] (emitln "this." fld " = " fld ";")) (doseq [[pno pmask] pmasks] (emitln "this.cljs$lang$protocol_mask$partition" pno "$ = " pmask ";")) - (emitln "if(arguments.length>" (- (count fields) 2) "){") - (emitln "this.__meta = __meta;") - (emitln "this.__extmap = __extmap;") - (emitln "} else {") - (emits "this.__meta=") - (emit-constant nil) - (emitln ";") - (emits "this.__extmap=") - (emit-constant nil) - (emitln ";") - (emitln "}") (emitln "})") (emit body))) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 0104c35c5..43ed23a4f 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -879,10 +879,11 @@ (defn- build-positional-factory [rsym rname fields] (let [fn-name (with-meta (symbol (core/str '-> rsym)) - (assoc (meta rsym) :factory :positional))] + (assoc (meta rsym) :factory :positional)) + field-values (if (-> rsym meta :internal-ctor) (conj fields nil nil nil) fields)] `(defn ~fn-name [~@fields] - (new ~rname ~@fields)))) + (new ~rname ~@field-values)))) (defmacro deftype [t fields & impls] (let [env &env @@ -988,7 +989,7 @@ ks (map keyword fields) getters (map (fn [k] `(~k ~ms)) ks)] `(defn ~fn-name [~ms] - (new ~rname ~@getters nil (dissoc ~ms ~@ks))))) + (new ~rname ~@getters nil (dissoc ~ms ~@ks) nil)))) (defmacro defrecord [rsym fields & impls] (let [rsym (vary-meta rsym assoc :internal-ctor true) From ec0023bd45a7f956b1e58aab84cb207a94e35965 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sun, 14 Dec 2014 13:14:41 +0100 Subject: [PATCH 0292/4033] avoid emitting the same goog.require (:require [clojure.string :as str]) would emit goog.require("clojure.string") twice :refer might also cause this. --- src/clj/cljs/compiler.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 91d4a7a5a..290fd09fa 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -792,7 +792,7 @@ (emitln "goog.provide('" (munge name) "');") (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');")) - (doseq [lib (into (vals requires) (distinct (vals uses)))] + (doseq [lib (distinct (concat (vals requires) (vals uses)))] (emitln "goog.require('" (munge lib) "');"))) (defmethod emit* :deftype* From 2b3f73247bdb734ae397c2c9db896255b37c3180 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Dec 2014 15:39:11 -0500 Subject: [PATCH 0293/4033] change suggesting by Bruce Hauman change `cljs.test/test-ns` so that it throws if the namespace does not exist in the compilation environment. Add supporting `cljs.analyzer.api/find-ns` helper. Fix broken code this change uncovered in `cljs.test-runner`. --- src/clj/cljs/analyzer/api.clj | 5 ++++- src/clj/cljs/test.clj | 1 + test/cljs/test_runner.cljs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index 418a45176..af6369858 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.analyzer.api - (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve]) + (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve find-ns]) (:require [cljs.env :as env] [cljs.analyzer :as ana])) @@ -17,6 +17,9 @@ (defn all-ns [] (keys (get @env/*compiler* ::ana/namespaces))) +(defn find-ns [sym] + (get-in @env/*compiler* [::ana/namespaces sym])) + (defn ns-interns [ns] (get-in @env/*compiler* [::ana/namespaces ns :defs])) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 4dd470e32..408686ec5 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -280,6 +280,7 @@ ([ns] `(cljs.test/test-ns (cljs.test/empty-env) ~ns)) ([env [quote ns :as form]] (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") + (assert (ana-api/find-ns ns) (str "Namespace " ns " does not exist")) `(do (cljs.test/set-env! ~env) (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index e0c460d51..27f49218c 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -28,6 +28,6 @@ 'cljs.macro-test 'cljs.top-level 'cljs.keyword-test - 'cljs.ns-tst + 'cljs.ns-test 'foo.ns-shadow-test 'cljs.import-test) From 1d79df309b5d9b1554c331d1d8b58373054556e2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Dec 2014 18:25:33 -0500 Subject: [PATCH 0294/4033] `cljs.test/test-all-vars` shouldn't return anything --- src/clj/cljs/test.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 408686ec5..cc0f1e916 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -265,9 +265,7 @@ (fn [[_ v]] (:test v)) (ana-api/ns-interns ns)))]) (when (nil? env#) - (let [ret# (cljs.test/get-current-env)] - (cljs.test/clear-env!) - ret#))))) + (cljs.test/clear-env!))))) (defmacro test-ns "If the namespace defines a function named test-ns-hook, calls that. From 22dd4fbeed72398cbc3336fccffe8196c56cd209 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Dec 2014 20:56:19 -0500 Subject: [PATCH 0295/4033] unroll `partial`, copy & pasted from Clojure core.clj --- src/cljs/cljs/core.cljs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 97cc5c6b9..525cde630 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -3319,11 +3319,26 @@ reduces them without incurring seq initialization" called, the returned function calls f with args + additional args." ([f] f) ([f arg1] - (fn [& args] (apply f arg1 args))) + (fn + ([] (f arg1)) + ([x] (f arg1 x)) + ([x y] (f arg1 x y)) + ([x y z] (f arg1 x y z)) + ([x y z & args] (apply f arg1 x y z args)))) ([f arg1 arg2] - (fn [& args] (apply f arg1 arg2 args))) + (fn + ([] (f arg1 arg2)) + ([x] (f arg1 arg2 x)) + ([x y] (f arg1 arg2 x y)) + ([x y z] (f arg1 arg2 x y z)) + ([x y z & args] (apply f arg1 arg2 x y z args)))) ([f arg1 arg2 arg3] - (fn [& args] (apply f arg1 arg2 arg3 args))) + (fn + ([] (f arg1 arg2 arg3)) + ([x] (f arg1 arg2 arg3 x)) + ([x y] (f arg1 arg2 arg3 x y)) + ([x y z] (f arg1 arg2 arg3 x y z)) + ([x y z & args] (apply f arg1 arg2 arg3 x y z args)))) ([f arg1 arg2 arg3 & more] (fn [& args] (apply f arg1 arg2 arg3 (concat more args))))) From cc4a0f790b06cd724f129f5db28b24171dbba5e1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Dec 2014 21:37:59 -0500 Subject: [PATCH 0296/4033] the default should not report anything --- src/cljs/cljs/test.cljs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 4f10534d8..866792454 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -283,8 +283,7 @@ :dynamic true} report (fn [m] [(:reporter (get-current-env)) (:type m)])) -(defmethod report :default [m] - (prn m)) +(defmethod report :default [m]) (defmethod report [::default :pass] [m] (inc-report-counter! :pass)) From d3e0178c54c9c6a032739a520d5be2a7ad270685 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Dec 2014 21:38:08 -0500 Subject: [PATCH 0297/4033] optimize clojure.string/join --- src/cljs/clojure/string.cljs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/cljs/clojure/string.cljs b/src/cljs/clojure/string.cljs index 1a01ec7e0..f3009be38 100644 --- a/src/cljs/clojure/string.cljs +++ b/src/cljs/clojure/string.cljs @@ -9,7 +9,7 @@ (ns clojure.string (:refer-clojure :exclude [replace reverse]) (:require [goog.string :as gstring]) - (:import goog.string.StringBuffer)) + (:import [goog.string StringBuffer])) (defn- seq-reverse [coll] @@ -48,11 +48,22 @@ (defn join "Returns a string of all elements in coll, as returned by (seq coll), - separated by an optional separator." + separated by an optional separator." ([coll] - (apply str coll)) + (loop [sb (StringBuffer.) coll (seq coll)] + (if coll + (recur (. sb (append (str (first coll)))) (next coll)) + (.toString sb)))) ([separator coll] - (apply str (interpose separator coll)))) + (loop [sb (StringBuffer.) coll (seq coll)] + (if coll + (do + (. sb (append (str (first coll)))) + (let [coll (next coll)] + (when-not (nil? coll) + (. sb (append separator))) + (recur sb coll))) + (.toString sb))))) (defn upper-case "Converts string to all upper-case." From 52ddf60bdb0a037fe58ec4c734c77e33a8ed1cd3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Dec 2014 22:51:58 -0500 Subject: [PATCH 0298/4033] need a newline after emitting the var `:test` --- src/clj/cljs/compiler.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 290fd09fa..ba2d3b71c 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -440,7 +440,7 @@ (when (and ana/*load-tests* (:test var)) (when (= :expr (:context env)) (emitln ";")) - (emits var ".cljs$lang$test = " (:test var)))))) + (emitln var ".cljs$lang$test = " (:test var) ";"))))) (defn emit-apply-to [{:keys [name params env]}] From 0d951489c93392ec1ae83b7e22961dbe7d4b7e87 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Dec 2014 09:21:29 -0500 Subject: [PATCH 0299/4033] test.check uncovered a cljs.reader bug around reading '/ - would read a symbol with no name or namespace. fix and add test. --- src/cljs/cljs/reader.cljs | 15 +++++++++------ test/cljs/cljs/reader_test.cljs | 8 ++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index 0808d7921..2998e6c92 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -318,17 +318,20 @@ nil if the end of stream has been reached") (defn special-symbols [t not-found] (cond - (identical? t "nil") nil - (identical? t "true") true - (identical? t "false") false - :else not-found)) + (identical? t "nil") nil + (identical? t "true") true + (identical? t "false") false + (identical? t "/") '/ + :else not-found)) (defn read-symbol [reader initch] (let [token (read-token reader initch)] - (if (gstring/contains token "/") + (if (and (gstring/contains token "/") + (not (== (.-length token) 1))) (symbol (subs token 0 (.indexOf token "/")) - (subs token (inc (.indexOf token "/")) (.-length token))) + (subs token (inc (.indexOf token "/")) + (.-length token))) (special-symbols token (symbol token))))) (defn read-keyword diff --git a/test/cljs/cljs/reader_test.cljs b/test/cljs/cljs/reader_test.cljs index 3835f3ffd..99d8d0253 100644 --- a/test/cljs/cljs/reader_test.cljs +++ b/test/cljs/cljs/reader_test.cljs @@ -162,3 +162,11 @@ m (re-find re " \u00a1 ")] (testing "Testing reading, CLJS-819" (is (= m " \u00a1"))))) + +;; NOTE: issue uncovered by test.check + +(deftest test-slash-reading + (let [x '({/ 0})] + (testing "Testing '/ reading" + (is (= x (reader/read-string (pr-str x)))) + (is (= (reader/read-string (pr-str x)) x))))) From 1435ccd61451dbb76d2907ad3aec765da83b2a72 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Dec 2014 09:46:36 -0500 Subject: [PATCH 0300/4033] need to pop `:testing-contexts` and `:testing-vars` in cljs.test --- src/clj/cljs/test.clj | 2 +- src/cljs/cljs/test.cljs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index cc0f1e916..3b7b79483 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -170,7 +170,7 @@ `(do (cljs.test/update-current-env! [:testing-contexts] conj ~string) (let [ret# (do ~@body)] - (cljs.test/update-current-env! [:testing-contests] rest) + (cljs.test/update-current-env! [:testing-contexts] rest) ret#)))) ;; ============================================================================= diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 866792454..06a660344 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -389,7 +389,8 @@ :message "Uncaught exception, not in assertion." :expected nil :actual e}))) - (do-report {:type :end-test-var :var v})))) + (do-report {:type :end-test-var :var v}) + (update-current-env! [:testing-vars] rest)))) (defn- default-fixture "The default, empty, fixture function. Just calls its argument." From bef2c56c3c00a61e447096dd9fb9a04c99611bbb Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Dec 2014 14:24:15 -0500 Subject: [PATCH 0301/4033] fix `cljs.nodejs/enable-util-print!`, incorrectly monkey patched `cjls.core/string-print` instead of setting `cljs.core/*print-fn*` --- src/cljs/cljs/nodejs.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/nodejs.cljs b/src/cljs/cljs/nodejs.cljs index 60bc7ff49..9da4bfa19 100644 --- a/src/cljs/cljs/nodejs.cljs +++ b/src/cljs/cljs/nodejs.cljs @@ -17,4 +17,4 @@ ; Have ClojureScript print using Node's sys.print function (defn enable-util-print! [] - (set! cljs.core/string-print (.-print (require "util")))) + (set! *print-fn* (.-print (require "util")))) From 3f3651a9c8d295085ad9459490b23727b0c789f7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Dec 2014 16:42:11 -0500 Subject: [PATCH 0302/4033] Update changes & readme for 0.0-2496 --- README.md | 6 +++--- changes.md | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 39678bc9c..a1d97a9da 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2411 +Latest stable release: 0.0-2496 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2411"] +[org.clojure/clojurescript "0.0-2496"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2411 org.clojure clojurescript - 0.0-2411 + 0.0-2496 ``` diff --git a/changes.md b/changes.md index 6b3f03733..c3458358d 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,30 @@ +## 0.0-2496 + +### Enhancements +* cljs.test added, mirrors clojure.test +* New cljs.analyzer.api namespace for easier access to analysis info from macros +* New cljs.analyzer.api namespace for easier access to analysis info from macros +* Support :test metadata on vars +* Support static vars +* cljs.source-map for client side source mapping +* expose ClojureScript :warnings build option +* CLJS-909: Add stable api for consumers of compiler data. + +### Changes +* convert all ClojureScript tests to cljs.test +* add volatile! from Clojure 1.7 +* stateful transducers use volatile! +* added `js-debugger` macro, compiles to "debugger;" +* CLJS-892: Improve performance of compare-symbols/compare-keywords +* CLJS-696: remove arguments usage from defrecord constructor +* unroll `partial`, copy & pasted from Clojure core.clj +* optimize clojure.string/join + +### Fixes +* fix `cljs.nodejs/enable-util-print!`, incorrectly monkey patched `cjls.core/string-print` instead of setting `cljs.core/*print-fn*` +* cljs.reader bug, '/ incorrectly read +* avoid emitting the same goog.require + ## 0.0-2411 ### Enhancements From e7f065610d275d76cadb5952edb91c1d5c4d3d10 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Dec 2014 17:41:51 -0500 Subject: [PATCH 0303/4033] support `use-fixtures`, update namespace documentation --- src/clj/cljs/test.clj | 21 ++++++++++++++ src/cljs/cljs/test.cljs | 62 +++++++++++++++-------------------------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 3b7b79483..415ceb7a0 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -257,6 +257,12 @@ `(let [env# (cljs.test/get-current-env)] (when (nil? env#) (cljs.test/set-env! (cljs.test/empty-env))) + ~(when (ana-api/ns-resolve ns 'cljs-test-once-fixtures) + `(cljs.test/update-current-env! [:once-fixtures] assoc '~ns + ~(symbol (name ns) "cljs-test-once-fixtures"))) + ~(when (ana-api/ns-resolve ns 'cljs-test-each-fixtures) + `(cljs.test/update-current-env! [:each-fixtures] assoc '~ns + ~(symbol (name ns) "cljs-test-each-fixtures"))) (cljs.test/test-vars [~@(map (fn [[k _]] @@ -291,3 +297,18 @@ (let [ret# (cljs.test/get-current-env)] (cljs.test/clear-env!) ret#)))) + +;; ============================================================================= +;; Fixes + +(defmacro use-fixtures [type & fns] + (condp = type + :once + `(def ~'cljs-test-once-fixtures + [~@fns]) + :each + `(def ~'cljs-test-each-fixtures + [~@fns]) + :else + (throw + (Exception. "First argument to cljs.test/use-fixtures must be :once or :each")))) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 06a660344..8d89de49e 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -73,28 +73,11 @@ (is (= -1 (+ 3 -4))))) Note that, unlike RSpec, the \"testing\" macro may only be used - INSIDE a \"deftest\" or \"with-test\" form (see below). + INSIDE a \"deftest\" form (see below). DEFINING TESTS - There are two ways to define tests. The \"with-test\" macro takes - a defn or def form as its first argument, followed by any number - of assertions. The tests will be stored as metadata on the - definition. - - (with-test - (defn my-function [x y] - (+ x y)) - (is (= 4 (my-function 2 2))) - (is (= 7 (my-function 3 4)))) - - As of Clojure SVN rev. 1221, this does not work with defmacro. - See http://code.google.com/p/clojure/issues/detail?id=51 - - The other way lets you define tests separately from the rest of - your code, even in a different namespace: - (deftest addition (is (= 4 (+ 2 2))) (is (= 7 (+ 3 4)))) @@ -137,14 +120,23 @@ (defn test-ns-hook [] (arithmetic)) + \"run-tests\" also optionally takes a testing enviroment. A default + one is supplied for you by invoking \"empty-env\". The test + environment contains everything needed to run tests including the + report results map. Fixtures must be present here if you want them + to run. Note that code that relies on \"test-ns\" will + automatically be supplied the appropriate defined fixtures. For + example, this is done for you if you use \"run-tests\". + Note: test-ns-hook prevents execution of fixtures (see below). OMITTING TESTS FROM PRODUCTION CODE - You can bind the variable \"*load-tests*\" to false when loading or - compiling code in production. This will prevent any tests from - being created by \"with-test\" or \"deftest\". + You can set the ClojureScript compiler build option + \":load-tests\" to false when loading or compiling code in + production. This will prevent any tests from being created by + or \"deftest\". FIXTURES @@ -163,7 +155,7 @@ Fixtures are attached to namespaces in one of two ways. \"each\" fixtures are run repeatedly, once for each test function created - with \"deftest\" or \"with-test\". \"each\" fixtures are useful for + with \"deftest\". \"each\" fixtures are useful for establishing a consistent before/after state for each test, like clearing out database tables. @@ -185,14 +177,6 @@ are using test-ns-hook, fixture functions will *never* be run. - SAVING TEST OUTPUT TO A FILE - - All the test reporting functions write to the var *test-out*. By - default, this is the same as *out*, but you can rebind it to any - PrintWriter. For example, it could be a file opened with - clojure.java.io/writer. - - EXTENDING TEST-IS (ADVANCED) You can extend the behavior of the \"is\" macro by defining new @@ -200,8 +184,10 @@ called during expansion of the \"is\" macro, so they should return quoted forms to be evaluated. - You can plug in your own test-reporting framework by rebinding - the \"report\" function: (report event) + You can plug in your own test-reporting framework by specifying a + :reporter key in the test environment. It is normally set to + :cljs.test/default. Set this to the desired key and supply custom + implementations of the \"report\" multimethod. The 'event' argument is a map. It will always have a :type key, whose value will be a keyword signaling the type of event being @@ -214,12 +200,9 @@ :actual A form representing what actually occurred :message The string message given as an argument to 'is' - The \"testing\" strings will be a list in \"*testing-contexts*\", and - the vars being tested will be a list in \"*testing-vars*\". - - Your \"report\" function should wrap any printing calls in the - \"with-test-out\" macro, which rebinds *out* to the current value - of *test-out*. + The \"testing\" strings will be a list in the :testing-contexts + property of the test environment, and the vars being tested will be + a list in the :testing-vars property of the test environment. For additional event types, see the examples in the code. "} @@ -411,7 +394,8 @@ (defn test-vars "Groups vars by their namespace and runs test-vars on them with - appropriate fixtures applied." + appropriate fixtures assuming they are present in the current + testing environment." [vars] (doseq [[ns vars] (group-by (comp :ns meta) vars)] (let [env (get-current-env) From 06dfc9feda9b1d20fcc956fa58f8f83df7d7841e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Dec 2014 17:46:59 -0500 Subject: [PATCH 0304/4033] update for 2498 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a1d97a9da..0e1f102c0 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2496 +Latest stable release: 0.0-2498 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2496"] +[org.clojure/clojurescript "0.0-2498"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2496 org.clojure clojurescript - 0.0-2496 + 0.0-2498 ``` diff --git a/changes.md b/changes.md index c3458358d..57270c50e 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2498 + +### Fixes +* Support cljs.test/use-fixtures + ## 0.0-2496 ### Enhancements From 28019a1283ebc5f55834d8915ee5e8b5cea333da Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 20 Dec 2014 15:54:59 -0500 Subject: [PATCH 0305/4033] stop warning about deps.cljs --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index bd0d4338e..dbc5d4ac8 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -844,7 +844,7 @@ should contain the source for the given namespace name." [] (let [classloader (. (Thread/currentThread) (getContextClassLoader)) upstream-deps (map #(read-string (slurp %)) (enumeration-seq (. classloader (findResources "deps.cljs"))))] - (doseq [dep upstream-deps] + #_(doseq [dep upstream-deps] (println (str "Upstream deps.cljs found on classpath. " dep " This is an EXPERIMENTAL FEATURE and is not guarenteed to remain stable in future versions."))) (apply merge-with concat upstream-deps))) From 2d8b0ec44f214e059bdbeabbd6492e013347a093 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Sat, 20 Dec 2014 06:21:13 +0600 Subject: [PATCH 0306/4033] CLJS-915: On empty call, List and PersistentQueue do not retain meta, sorted-set/sorted map do not retain comparator --- src/cljs/cljs/core.cljs | 8 ++++---- test/cljs/cljs/core_test.cljs | 28 +++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 525cde630..b4b47c35d 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2251,7 +2251,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (List. meta o coll (inc count) nil)) IEmptyableCollection - (-empty [coll] (.-EMPTY List)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) ISequential IEquiv @@ -4869,7 +4869,7 @@ reduces them without incurring seq initialization" (PersistentQueue. meta (inc count) (conj front o) [] nil))) IEmptyableCollection - (-empty [coll] (.-EMPTY PersistentQueue)) + (-empty [coll] (with-meta (.-EMPTY PersistentQueue) meta)) ISequential IEquiv @@ -6933,7 +6933,7 @@ reduces them without incurring seq initialization" (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY PersistentTreeMap) meta)) + (-empty [coll] (PersistentTreeMap. comp nil 0 meta 0)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -7421,7 +7421,7 @@ reduces them without incurring seq initialization" (PersistentTreeSet. meta (assoc tree-map o nil) nil)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY PersistentTreeSet) meta)) + (-empty [coll] (PersistentTreeSet. meta (-empty tree-map) 0)) IEquiv (-equiv [coll other] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 3b49f7487..f319b5b5a 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -745,9 +745,10 @@ (let [e-list (empty '^{:b :c} (1 2 3))] (testing "list" (is (seq? e-list)) - (is (empty? e-list)))) + (is (empty? e-list)) + (is (= {:b :c} (meta e-list))))) (let [e-elist (empty '^{:b :c} ())] - (testing "list with metadata" + (testing "empty list with metadata" (is (seq? e-elist)) (is (empty? e-elist)) (is (= :c (get (meta e-elist) :b))))) @@ -770,7 +771,28 @@ (testing "map with complex keys" (is (map? e-hmap)) (is (empty? e-hmap)) - (is (= {:b :c} (meta e-hmap))))))) + (is (= {:b :c} (meta e-hmap))))) + (let [smap (with-meta (sorted-map-by (comp - compare) 2 :a 1 :b 5 :c) {:b :c}) + e-smap (empty smap)] + (testing "sorted-map-by" + (is (map? e-smap)) + (is (empty? e-smap)) + (is (= {:b :c} (meta e-smap))) + (is (identical? (-comparator smap) (-comparator e-smap))) + (is (= [[5 :c] [2 :a] [1 :b]] (seq (assoc e-smap 2 :a 1 :b 5 :c)))))) + (let [sset (with-meta (sorted-set-by (comp - compare) 5 1 2) {:b :c}) + e-sset (empty sset)] + (testing "sorted-set-by" + (is (set? e-sset)) + (is (empty? e-sset)) + (is (= {:b :c} (meta e-sset))) + (is (identical? (-comparator sset) (-comparator e-sset))) + (is (= [5 2 1] (seq (conj e-sset 5 1 2)))))) + (let [e-queue (empty (with-meta (.-EMPTY PersistentQueue) {:b :c}))] + (testing "queue" + (is (identical? (type e-queue) PersistentQueue)) + (is (empty? e-queue)) + (is (= {:b :c} (meta e-queue))))))) (deftest test-try-catch (let [a (atom nil)] From a2d516b261bbc9aedc38a1a3a876dd8651ce21fe Mon Sep 17 00:00:00 2001 From: Matthew Boston Date: Fri, 19 Dec 2014 16:29:40 -0700 Subject: [PATCH 0307/4033] CLJS-914: thrown-with-msg? is unable to get message of exception --- src/clj/cljs/test.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 415ceb7a0..3025baf44 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -121,7 +121,7 @@ ~@body (cljs.test/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) (catch ~klass e# - (let [m# (.getMessage e#)] + (let [m# (.-message e#)] (if (re-find ~re m#) (cljs.test/do-report {:type :pass, :message ~msg, From e0d8fefdd603247f984577d402b229a6ca35901b Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Thu, 18 Dec 2014 19:25:31 +0200 Subject: [PATCH 0308/4033] Enhancements to bootstrap script If a new user doesn't have curl or unzip, show a nice friendly message telling them what to install. Also try to make downloading of needed files via curl more robust. --- script/bootstrap | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index 6798da423..77e94e4c4 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -8,10 +8,14 @@ GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R3" TREADER_RELEASE="0.8.10" +# check dependencies +curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } +unzip -v >/dev/null || { echo "The 'unzip' utility is missing, or not on your system path."; exit 1; } + mkdir -p lib echo "Fetching Clojure..." -curl -O -s https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.zip +curl -O -s https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.zip || { echo "Download failed."; exit 1; } unzip -qu clojure-$CLOJURE_RELEASE.zip echo "Copying clojure-$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar to lib/clojure.jar..." cp clojure-$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar lib/clojure.jar @@ -22,7 +26,7 @@ echo "Cleaning up Clojure archive..." rm clojure-$CLOJURE_RELEASE.zip echo "Fetching data.json..." -curl -O -s https://repo1.maven.org/maven2/org/clojure/data.json/$DJSON_RELEASE/data.json-$DJSON_RELEASE.jar +curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/data.json/$DJSON_RELEASE/data.json-$DJSON_RELEASE.jar || { echo "Download failed."; exit 1; } echo "Copying data.json-$DJSON_RELEASE.jar to lib/data.json-$DJSON_RELEASE.jar..." cp data.json-$DJSON_RELEASE.jar lib/data.json-$DJSON_RELEASE.jar echo "Cleaning up data.json..." @@ -47,12 +51,12 @@ if [ "$1" = "--closure-library-head" ] ; then svn checkout -q --non-interactive https://closure-library.googlecode.com/svn/trunk/ ./ fi else - curl -O -s https://repo1.maven.org/maven2/org/clojure/google-closure-library/$GCLOSURE_LIB_RELEASE/google-closure-library-$GCLOSURE_LIB_RELEASE.jar + curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/google-closure-library/$GCLOSURE_LIB_RELEASE/google-closure-library-$GCLOSURE_LIB_RELEASE.jar || { echo "Download failed."; exit 1; } cp google-closure-library-$GCLOSURE_LIB_RELEASE.jar ../../lib/google-closure-library-$GCLOSURE_LIB_RELEASE.jar rm google-closure-library-$GCLOSURE_LIB_RELEASE.jar echo "Fetching Google Closure third party library..." - curl -O -s https://repo1.maven.org/maven2/org/clojure/google-closure-library-third-party/$GCLOSURE_LIB_RELEASE/google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar + curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/google-closure-library-third-party/$GCLOSURE_LIB_RELEASE/google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar || { echo "Download failed."; exit 1; } cp google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar ../../lib/google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar rm google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar fi @@ -61,7 +65,7 @@ cd .. echo "Fetching Google Closure compiler..." mkdir -p compiler cd compiler -curl -O -s https://dl.google.com/closure-compiler/compiler-latest.zip +curl --retry 3 -O -s https://dl.google.com/closure-compiler/compiler-latest.zip || { echo "Download failed."; exit 1; } unzip -qu compiler-latest.zip echo "Cleaning up Google Closure compiler archive..." rm compiler-latest.zip @@ -74,7 +78,7 @@ if [ "$1" = "--closure-library-head" ] ; then fi echo "Fetching Rhino..." -curl -O -s https://ftp.mozilla.org/pub/mozilla.org/js/rhino$RHINO_RELEASE.zip +curl --retry 3 -O -s https://ftp.mozilla.org/pub/mozilla.org/js/rhino$RHINO_RELEASE.zip || { echo "Download failed."; exit 1; } unzip -qu rhino$RHINO_RELEASE.zip echo "Copying rhino$RHINO_RELEASE/js.jar to lib/js.jar..." cp rhino$RHINO_RELEASE/js.jar lib/js.jar @@ -87,10 +91,9 @@ echo "Copying closure/compiler/compiler.jar to lib/compiler.jar" cp closure/compiler/compiler.jar lib echo "Fetching tools.reader $TREADER_RELEASE ..." -curl -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar +curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar || { echo "Download failed."; exit 1; } echo "Moving tools.reader.jar to lib/tools.reader.jar" - mv tools.reader-$TREADER_RELEASE.jar lib/tools.reader-$TREADER_RELEASE.jar echo "[Bootstrap Completed]" From 0e11e57a1f9973ca74bcb558e85fa6339e6a09d6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 21 Dec 2014 12:33:18 -0500 Subject: [PATCH 0309/4033] Fix Node.js source mapping regression introduced by commit 254e548 The browser-centric timestamp URL convention breaks node-source-map-support. The behavior now must be explicitly requested by setting a new build flag `:source-map-timestamp` to `true`. --- src/clj/cljs/compiler.clj | 5 ++++- src/clj/cljs/source_map.clj | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index ba2d3b71c..364e119ec 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -913,7 +913,10 @@ {:source-map (:source-map sm-data)}))] (when (and sm-data (= (:optimizations opts) :none)) (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" (.getName sm-file) "?rel=" (System/currentTimeMillis)) + (emits "\n//# sourceMappingURL=" (.getName sm-file) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + "")) (spit sm-file (sm/encode {(url-path src) (:source-map sm-data)} {:lines (+ (:gen-line sm-data) 2) diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index 164a3c6f2..3d8e78321 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -217,11 +217,13 @@ "sources" (into [] (let [paths (keys m) f (comp - #(str % "?rel=" (System/currentTimeMillis)) - (if (or (:output-dir opts) + (if (true? (:source-map-timestamp opts)) + #(str % "?rel=" (System/currentTimeMillis)) + identity) + (if (or (:output-dir opts) (:source-map-path opts)) - #(relativize-path % opts) - #(last (string/split % #"/"))))] + #(relativize-path % opts) + #(last (string/split % #"/"))))] (map f paths))) "lineCount" (:lines opts) "mappings" (->> (lines->segs (concat preamble-lines @lines)) From d9f4d17bffcfda8d7148fad8fb2a9490ca10c286 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 21 Dec 2014 12:56:53 -0500 Subject: [PATCH 0310/4033] Stop generating random files for IJavaScript Strings In autobuild scenarios under Node.js the old behavior polluted the output directory with random files. Instead construct a stable name from the first 7 letters of the SHA-1 of the string. --- src/clj/cljs/closure.clj | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index dbc5d4ac8..71cf494a7 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -61,7 +61,9 @@ com.google.javascript.jscomp.CheckLevel com.google.javascript.jscomp.DiagnosticGroups com.google.javascript.jscomp.CommandLineRunner - com.google.javascript.jscomp.AnonymousFunctionNamingPolicy)) + com.google.javascript.jscomp.AnonymousFunctionNamingPolicy + java.security.MessageDigest + javax.xml.bind.DatatypeConverter)) (defmacro ^:private debug-prn [& args] @@ -757,8 +759,16 @@ should contain the source for the given namespace name." [js] (if-let [url ^URL (deps/-url js)] (path-from-jarfile url) - (str (random-string 5) ".js"))) - + (if (string? js) + (let [digest (MessageDigest/getInstance "SHA-1")] + (.reset digest) + (.update digest (.getBytes ^String js "utf8")) + (str + (->> (DatatypeConverter/printHexBinary (.digest digest)) + (take 7) + (apply str)) + ".js")) + (str (random-string 5) ".js")))) (defn write-javascript "Write a JavaScript file to disk. Only write if the file does not From e38df9eb5ffd64591dc5f37890c43d4b47f74aa8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 21 Dec 2014 13:08:48 -0500 Subject: [PATCH 0311/4033] Update readme & changes to 2505 --- README.md | 6 +++--- changes.md | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0e1f102c0..ba12510a5 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2498 +Latest stable release: 0.0-2505 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2498"] +[org.clojure/clojurescript "0.0-2505"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2498 org.clojure clojurescript - 0.0-2498 + 0.0-2505 ``` diff --git a/changes.md b/changes.md index 57270c50e..6f45ed803 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,17 @@ +## 0.0-2505 + +### Changes +* Stop generating random files for IJavaScript Strings +* added :source-map-timestamp build flag to get cache busting source + map urls +* Enhancements to bootstrap script +* Stop warning about deps.cljs usage + +### Fixes +* Fix Node.js source mapping regression introduced by commit 254e548 +* CLJS-914: thrown-with-msg? is unable to get message of exception +* CLJS-915: On empty call, List and PersistentQueue do not retain meta, sorted-set/sorted map do not retain comparator + ## 0.0-2498 ### Fixes From 22994fa120a17f822ab6362a032d5b8b19a5e071 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Dec 2014 14:19:31 -0500 Subject: [PATCH 0312/4033] put analysis caching behind `:cache-analysis` compiler flag, add `last-modified` helper that can handle Files & URLs. Improve `requires-analysis?` by using new helpers & providing parameter defaults to simplify testing. --- src/clj/cljs/analyzer.clj | 48 +++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index c13ce2d21..4ef7d0f4b 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -19,6 +19,7 @@ [clojure.tools.reader.reader-types :as readers]) (:import java.lang.StringBuilder java.io.File + java.net.URL [cljs.tagged_literals JSValue])) (set! *warn-on-reflection* true) @@ -1687,16 +1688,36 @@ argument, which the reader will use in any emitted errors." (swap! env/*compiler* assoc ::namespaces namespaces') ret)))) -(defn requires-analysis? [^File f ^File cache] - (or (not (.exists cache)) - (> (.lastModified f) (.lastModified cache)) - (let [version' (util/compiled-by-version cache) - version (util/clojurescript-version)] - (and version (not= version version'))))) +(defn cache-file + ([src] (cache-file src "out")) + ([src output-dir] + (let [ns-info (parse-ns src)] + (io/file (str (util/to-target-file output-dir ns-info) ".cache.edn"))))) -(defn cache-file [f output-dir] - (let [ns-info (parse-ns f)] - (io/file (util/to-target-file output-dir ns-info) ".cache.edn"))) +(defn last-modified [src] + (cond + (instance? File src) (.lastModified ^File src) + (instance? URL src) (.getLastModified (.openConnection ^URL src)) + :else + (throw + (IllegalArgumentException. (str "Cannot get last modified for " src))))) + +(defn requires-analysis? + ([src] (requires-analysis? src "out")) + ([src output-dir] + (let [cache (cache-file src output-dir)] + (requires-analysis? src cache output-dir))) + ([src ^File cache output-dir] + (if (not (.exists cache)) + true + (let [out-src (util/to-target-file output-dir (parse-ns src))] + (if (not (.exists out-src)) + true + (or + (> (last-modified src) (last-modified cache)) + (let [version' (util/compiled-by-version cache) + version (util/clojurescript-version)] + (and version (not= version version'))))))))) (defn analyze-file ([f] (analyze-file f nil)) @@ -1711,9 +1732,8 @@ argument, which the reader will use in any emitted errors." (let [path (if (instance? File res) (.getPath ^File res) (.getPath ^java.net.URL res)) - ;; cache (if output-dir - ;; (cache-file res output-dir)) - ] + cache (when (and (:cache-analysis opts) output-dir) + (cache-file res output-dir))] (when-not (get-in @env/*compiler* [::analyzed-cljs path]) (binding [*cljs-ns* 'cljs.user *cljs-file* path @@ -1728,5 +1748,9 @@ argument, which the reader will use in any emitted errors." (recur (:name ast) (next forms)) (recur ns (next forms)))) ns))] + (when cache + (spit cache + (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" + (pr-str (get-in @env/*compiler* [::namespaces ns]))))) (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))))))))) From 348550b328c9256a836303e50fa4ff526f4ff868 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Dec 2014 15:24:23 -0500 Subject: [PATCH 0313/4033] support analysis caching `cljs.compiler/mkdirs` -> `cljs.util/mkdirs` --- src/clj/cljs/analyzer.clj | 37 +++++++++++++++++++++++-------------- src/clj/cljs/closure.clj | 8 ++++---- src/clj/cljs/compiler.clj | 7 +------ src/clj/cljs/util.clj | 18 +++++++++++++----- 4 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 4ef7d0f4b..000d9cca2 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -16,7 +16,8 @@ [cljs.js-deps :as deps] [cljs.tagged-literals :as tags] [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers]) + [clojure.tools.reader.reader-types :as readers] + [clojure.edn :as edn]) (:import java.lang.StringBuilder java.io.File java.net.URL @@ -1692,7 +1693,7 @@ argument, which the reader will use in any emitted errors." ([src] (cache-file src "out")) ([src output-dir] (let [ns-info (parse-ns src)] - (io/file (str (util/to-target-file output-dir ns-info) ".cache.edn"))))) + (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn"))))) (defn last-modified [src] (cond @@ -1735,22 +1736,30 @@ argument, which the reader will use in any emitted errors." cache (when (and (:cache-analysis opts) output-dir) (cache-file res output-dir))] (when-not (get-in @env/*compiler* [::analyzed-cljs path]) - (binding [*cljs-ns* 'cljs.user - *cljs-file* path - reader/*alias-map* (or reader/*alias-map* {})] - (let [env (assoc (empty-env) :build-options opts) - ns (loop [ns nil forms (seq (forms-seq res))] + (if (or (not (:cache-analysis opts)) + (not output-dir) + (requires-analysis? res output-dir)) + (binding [*cljs-ns* 'cljs.user + *cljs-file* path + reader/*alias-map* (or reader/*alias-map* {})] + (let [env (assoc (empty-env) :build-options opts) + ns (loop [ns nil forms (seq (forms-seq res))] (if forms (let [form (first forms) - env (assoc env :ns (get-namespace *cljs-ns*)) - ast (analyze env form opts)] + env (assoc env :ns (get-namespace *cljs-ns*)) + ast (analyze env form opts)] (if (= (:op ast) :ns) (recur (:name ast) (next forms)) (recur ns (next forms)))) ns))] - (when cache - (spit cache - (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" - (pr-str (get-in @env/*compiler* [::namespaces ns]))))) - (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))))))))) + (when cache + (util/mkdirs cache) + (spit cache + (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" + (pr-str (get-in @env/*compiler* [::namespaces ns]))))) + (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))) + (let [{:keys [ns]} (parse-ns res)] + (swap! env/*compiler* assoc-in [::analyzed-cljs path] true) + (swap! env/*compiler* assoc-in [::namespaces ns] + (edn/read-string (slurp cache))))))))))) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 71cf494a7..60693ba14 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -366,7 +366,7 @@ (let [out-file (io/file out-dir (path-from-jarfile url)) content (with-open [reader (io/reader url)] (slurp reader))] - (do (comp/mkdirs out-file) + (do (util/mkdirs out-file) (spit out-file content) out-file))) @@ -778,7 +778,7 @@ should contain the source for the given namespace name." out-name (output-path js) out-file (io/file out-dir out-name)] (do (when-not (.exists out-file) - (do (comp/mkdirs out-file) + (do (util/mkdirs out-file) (spit out-file (deps/-source js)))) {:url (deps/to-url out-file) :requires (deps/-requires js) :provides (deps/-provides js) :group (:group js)}))) @@ -827,7 +827,7 @@ should contain the source for the given namespace name." [opts & sources] (let [disk-sources (map #(source-on-disk opts %) sources)] (let [goog-deps (io/file (output-directory opts) "goog/deps.js")] - (do (comp/mkdirs goog-deps) + (do (util/mkdirs goog-deps) (spit goog-deps (deps-file opts (filter #(= (:group %) :goog) disk-sources))) (output-deps-file opts (remove #(= (:group %) :goog) disk-sources)))))) @@ -1022,7 +1022,7 @@ should contain the source for the given namespace name." (when (and (= (:target opts) :nodejs) (#{:none :whitespace} (:optimizations opts))) (let [outfile (io/file (io/file (output-directory opts)) "goog/bootstrap/nodejs.js")] - (comp/mkdirs outfile) + (util/mkdirs outfile) (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 364e119ec..48432b8ec 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -853,11 +853,6 @@ [file-str] (clojure.string/replace file-str #"\.cljs$" ".js")) -(defn mkdirs - "Create all parent directories for the passed file." - [^File f] - (.mkdirs (.getParentFile (.getCanonicalFile f)))) - (defn with-core-cljs "Ensure that core.cljs has been loaded." ([] (with-core-cljs nil)) @@ -967,7 +962,7 @@ (try (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts)] (if (requires-compilation? src-file dest-file opts) - (do (mkdirs dest-file) + (do (util/mkdirs dest-file) (when (contains? (::ana/namespaces @env/*compiler*) ns) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) (compile-file* src-file dest-file opts)) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index c1be25908..0122d2375 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -56,8 +56,16 @@ (apply str (interpose sep parts)))) (defn ^File to-target-file - [target-dir ns-info] - (let [relative-path (string/split (munge-path (str (:ns ns-info))) #"\.") - parents (butlast relative-path)] - (io/file (io/file (to-path (cons target-dir parents))) - (str (last relative-path) ".js")))) + ([target-dir ns-info] + (to-target-file target-dir ns-info "js")) + ([target-dir ns-info ext] + (let [relative-path (string/split (munge-path (str (:ns ns-info))) #"\.") + parents (butlast relative-path)] + (io/file (io/file (to-path (cons target-dir parents))) + (str (last relative-path) (str "." ext)))))) + +(defn mkdirs + "Create all parent directories for the passed file." + [^File f] + (.mkdirs (.getParentFile (.getCanonicalFile f)))) + From e65965af14c02baa221e5ed0bac34c9cb08d19e7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Dec 2014 16:55:01 -0500 Subject: [PATCH 0314/4033] Change `cljs.analyzer/parse-ns` so that ns dependencies can be optionally followed. Change the analysis cache path in `cljs.analyzer/analyze-file` to use the new functionality. --- src/clj/cljs/analyzer.clj | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 000d9cca2..f62233e15 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1657,12 +1657,13 @@ argument, which the reader will use in any emitted errors." (defn parse-ns ([src] (parse-ns src nil nil)) + ([src opts] (parse-ns src nil opts)) ([src dest opts] (env/ensure (let [namespaces' (::namespaces @env/*compiler*) ret (binding [*cljs-ns* 'cljs.user - *analyze-deps* false] + *analyze-deps* (or (:analyze-deps opts) false)] (loop [forms (forms-seq src)] (if (seq forms) (let [env (empty-env) @@ -1686,7 +1687,8 @@ argument, which the reader will use in any emitted errors." (recur (rest forms)))))))] ;; TODO this _was_ a reset! of the old namespaces atom; should we capture and ;; then restore the entirety of env/*compiler* here instead? - (swap! env/*compiler* assoc ::namespaces namespaces') + (when-not (false? (:restore opts)) + (swap! env/*compiler* assoc ::namespaces namespaces')) ret)))) (defn cache-file @@ -1758,7 +1760,9 @@ argument, which the reader will use in any emitted errors." (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" (pr-str (get-in @env/*compiler* [::namespaces ns]))))) (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))) - (let [{:keys [ns]} (parse-ns res)] + ;; we want want to keep dependency analysis information + ;; don't revert the environment - David + (let [{:keys [ns]} (parse-ns res {:restore false :analyze-deps true})] (swap! env/*compiler* assoc-in [::analyzed-cljs path] true) (swap! env/*compiler* assoc-in [::namespaces ns] (edn/read-string (slurp cache))))))))))) From 3e25932c2084c932abb91780e10fb9fcd745c561 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Dec 2014 17:12:31 -0500 Subject: [PATCH 0315/4033] eliminate reflection warnings from cljs.js-deps --- src/clj/cljs/js_deps.clj | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index e1a8c0738..0f1d89815 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -1,7 +1,11 @@ (ns cljs.js-deps (:require [clojure.java.io :as io] [clojure.string :as string]) - (:import java.io.File)) + (:import java.io.File + java.net.URL + java.net.URLClassLoader + java.util.zip.ZipFile + java.util.zip.ZipEntry)) ; taken from pomegranate/dynapath ; https://github.com/tobias/dynapath/blob/master/src/dynapath/util.clj @@ -10,16 +14,24 @@ If no ClassLoader is provided, RT/baseLoader is assumed." ([] (all-classpath-urls (clojure.lang.RT/baseLoader))) ([cl] - (->> (iterate #(.getParent %) cl) + (->> (iterate #(.getParent ^ClassLoader %) cl) (take-while identity) reverse - (filter (partial instance? java.net.URLClassLoader)) - (mapcat #(.getURLs %)) + (filter (partial instance? URLClassLoader)) + (mapcat #(.getURLs ^URLClassLoader %)) distinct))) +(defn ^ZipFile zip-file [jar-path] + (cond + (instance? File jar-path) (ZipFile. ^File jar-path) + (string? jar-path) (ZipFile. ^String jar-path) + :else + (throw + (IllegalArgumentException. (str "Cannot construct zipfile from " jar-path))))) + (defn jar-entry-names* [jar-path] - (with-open [z (java.util.zip.ZipFile. jar-path)] - (doall (map #(.getName %) (enumeration-seq (.entries z)))))) + (with-open [z (zip-file jar-path)] + (doall (map #(.getName ^ZipEntry %) (enumeration-seq (.entries ^ZipFile z)))))) (def jar-entry-names (memoize jar-entry-names*)) @@ -52,12 +64,12 @@ If no ClassLoader is provided, RT/baseLoader is assumed." (map io/file) (reduce (fn [files jar-or-dir] - (let [name (.toLowerCase (.getName jar-or-dir)) + (let [name (.toLowerCase (.getName ^File jar-or-dir)) ext (.substring name (inc (.lastIndexOf name ".")))] - (->> (when (.exists jar-or-dir) + (->> (when (.exists ^File jar-or-dir) (cond - (.isDirectory jar-or-dir) - (find-js-fs (str (.getAbsolutePath jar-or-dir) "/" path)) + (.isDirectory ^File jar-or-dir) + (find-js-fs (str (.getAbsolutePath ^File jar-or-dir) "/" path)) (#{"jar" "zip"} ext) (find-js-jar jar-or-dir path) @@ -229,7 +241,7 @@ JavaScript library containing provide/require 'declarations'." (first (filter (fn [res] - (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath res))) + (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath ^URL res))) (enumeration-seq (.getResources (.getContextClassLoader (Thread/currentThread)) path))))) (defn goog-dependencies* From d3da2349c175a9066f4d9e4476302ff497f51937 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Dec 2014 18:08:39 -0500 Subject: [PATCH 0316/4033] need to thread options through in the analysis cache patch of `cljs.analyzer/analyze-file`. Breakout `cljs.analyzer/write-analysis-cache` helper. cljs.compiler/compile-file* also needs to dump analysis cache --- src/clj/cljs/analyzer.clj | 17 ++++++++++------- src/clj/cljs/compiler.clj | 6 +++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index f62233e15..8dd940c8e 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1716,12 +1716,18 @@ argument, which the reader will use in any emitted errors." (let [out-src (util/to-target-file output-dir (parse-ns src))] (if (not (.exists out-src)) true - (or - (> (last-modified src) (last-modified cache)) + (if (> (last-modified src) (last-modified cache)) + true (let [version' (util/compiled-by-version cache) version (util/clojurescript-version)] (and version (not= version version'))))))))) +(defn write-analysis-cache [ns cache-file] + (util/mkdirs cache-file) + (spit cache-file + (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" + (pr-str (get-in @env/*compiler* [::namespaces ns]))))) + (defn analyze-file ([f] (analyze-file f nil)) ([f {:keys [output-dir] :as opts}] @@ -1755,14 +1761,11 @@ argument, which the reader will use in any emitted errors." (recur ns (next forms)))) ns))] (when cache - (util/mkdirs cache) - (spit cache - (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" - (pr-str (get-in @env/*compiler* [::namespaces ns]))))) + (write-analysis-cache ns cache)) (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))) ;; we want want to keep dependency analysis information ;; don't revert the environment - David - (let [{:keys [ns]} (parse-ns res {:restore false :analyze-deps true})] + (let [{:keys [ns]} (parse-ns res (merge opts {:restore false :analyze-deps true}))] (swap! env/*compiler* assoc-in [::analyzed-cljs path] true) (swap! env/*compiler* assoc-in [::namespaces ns] (edn/read-string (slurp cache))))))))))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 48432b8ec..2640dbb68 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -919,7 +919,11 @@ (let [path (.getPath (.toURL ^File dest))] (swap! env/*compiler* assoc-in [::compiled-cljs path] ret) (swap! env/*compiler* assoc-in [::ana/analyzed-cljs path] true)) - ret)))))))))) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src output-dir))) + ret))))))))))) (defn requires-compilation? "Return true if the src file requires compilation." From c6f8a4738ad245a649015b8bd9956ba45def90df Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Dec 2014 18:56:18 -0500 Subject: [PATCH 0317/4033] update readme & changes for 0.0-2511 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ba12510a5..e87f6e8a6 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2505 +Latest stable release: 0.0-2511 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2505"] +[org.clojure/clojurescript "0.0-2511"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2505 org.clojure clojurescript - 0.0-2505 + 0.0-2511 ``` diff --git a/changes.md b/changes.md index 6f45ed803..a53feaaec 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2511 + +### Enhancements +* analysis caching via :cache-analysis build flag + ## 0.0-2505 ### Changes From 225dc334d28dce1c23e50b8c07bf20e02040d2b2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Dec 2014 09:26:59 -0500 Subject: [PATCH 0318/4033] don't pretty print source maps unless :source-map-pretty-print true build option provided --- src/clj/cljs/source_map.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index 3d8e78321..5695f9f85 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -234,10 +234,12 @@ (range (count @names->idx))))} (:sources-content opts) (assoc "sourcesContent" (:sources-content opts)))] - (with-out-str - (json/pprint - source-map-file-contents - :escape-slash false))))) + (if (true? (:source-map-pretty-print opts)) + (with-out-str + (json/pprint + source-map-file-contents + :escape-slash false)) + (json/write-str source-map-file-contents))))) ;; ----------------------------------------------------------------------------- ;; Merging From 6023cd14bd642e31b9c3ea5ace14e258ab0950f2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Dec 2014 22:41:01 -0500 Subject: [PATCH 0319/4033] port `clojure.test/are` to `cljs.test/are` --- src/clj/cljs/test.clj | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 3025baf44..264f193f8 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -9,7 +9,8 @@ (ns cljs.test (:require [cljs.env :as env] [cljs.analyzer :as ana] - [cljs.analyzer.api :as ana-api])) + [cljs.analyzer.api :as ana-api] + [clojure.template :as temp])) ;; ============================================================================= ;; Utilities for assertions @@ -163,6 +164,30 @@ ([form msg] `(cljs.test/try-expr ~msg ~form))) +(defmacro are + "Checks multiple assertions with a template expression. + See clojure.template/do-template for an explanation of + templates. + + Example: (are [x y] (= x y) + 2 (+ 1 1) + 4 (* 2 2)) + Expands to: + (do (is (= 2 (+ 1 1))) + (is (= 4 (* 2 2)))) + + Note: This breaks some reporting features, such as line numbers." + [argv expr & args] + (if (or + ;; (are [] true) is meaningless but ok + (and (empty? argv) (empty? args)) + ;; Catch wrong number of args + (and (pos? (count argv)) + (pos? (count args)) + (zero? (mod (count args) (count argv))))) + `(clojure.template/do-template ~argv (is ~expr) ~@args) + (throw (IllegalArgumentException. "The number of args doesn't match are's argv.")))) + (defmacro testing "Adds a new string to the list of testing contexts. May be nested, but must occur inside a test function (deftest)." From b9e475698426b8bb8862ef32706b925bd6d691cd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 24 Dec 2014 13:40:19 +0100 Subject: [PATCH 0320/4033] more consistens warning defaults --- src/clj/cljs/analyzer.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 8dd940c8e..137303cd0 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -40,8 +40,8 @@ (def ^:dynamic *cljs-warnings* {:preamble-missing true :unprovided true - :undeclared-var false - :undeclared-ns false + :undeclared-var true + :undeclared-ns true :undeclared-ns-form true :redef true :dynamic true From 88b3d1e54ba43c595e11aeab226cfd071ca6f657 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 09:02:21 -0500 Subject: [PATCH 0321/4033] CLJS-921: cljs.repl/doc output includes namespace twice :name var metadata exposed fully qualified symbol --- src/clj/cljs/analyzer.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 137303cd0..a8b409d18 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -510,8 +510,9 @@ {:env env :op :var-special :form form :var (analyze env sym) :sym (analyze env `(quote ~(symbol (name (:ns var)) (name (:name var))))) - :meta (let [ks [:ns :name :doc :file :line :column] + :meta (let [ks [:ns :doc :file :line :column] m (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) + :name `(quote ~(symbol (name (:name var)))) :test `(when ~sym (.-cljs$lang$test ~sym)) :arglists (:arglists var))] (analyze env m))})) From 3627feaa254b5a7282231a270f2c94f596eb3710 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 09:08:56 -0500 Subject: [PATCH 0322/4033] test for CLJS-921 --- test/cljs/cljs/core_test.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index f319b5b5a..c12e8862e 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2531,6 +2531,10 @@ (is (= s (set (remove #(zero? (mod % 3)) (range 100)))))))))) ) +(deftest test-921-var-meta-name + (testing "testing CLJS-921, :name var metadata should be unqualified" + (is (= (-> (var first) meta :name) 'first)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 7a393c6c1e52e2431a34bbe4585c424a6b2cbf03 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Tue, 23 Dec 2014 21:32:42 +0600 Subject: [PATCH 0323/4033] CLJS-920 add-watch/remove-watch should return reference, as in Clojure --- src/cljs/cljs/core.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index b4b47c35d..e72dbcbc6 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8380,14 +8380,16 @@ reduces them without incurring seq initialization" (deref a) ;=> 1" [iref key f] - (-add-watch iref key f)) + (-add-watch iref key f) + iref) (defn remove-watch "Alpha - subject to change. Removes a watch (set by add-watch) from a reference" [iref key] - (-remove-watch iref key)) + (-remove-watch iref key) + iref) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gensym ;;;;;;;;;;;;;;;; ;; Internal - do not use! From 94fea0bc06befb2e246bb9fba02d84bd8271c4fe Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 09:19:15 -0500 Subject: [PATCH 0324/4033] test for CLJS-920 --- test/cljs/cljs/core_test.cljs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index c12e8862e..7627c4aa1 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2535,6 +2535,18 @@ (testing "testing CLJS-921, :name var metadata should be unqualified" (is (= (-> (var first) meta :name) 'first)))) +(deftype MyWatchable [] + IWatchable + (-notify-watches [this oldval newval]) + (-add-watch [this key f]) + (-remove-watch [this key])) + +(deftest test-920-watch-ops-return-ref + (testing "tesing CLJS-92, add-watch/return-watch should return reference" + (let [w (MyWatchable.)] + (is (identical? (add-watch w :foo (fn [])) w)) + (is (identical? (remove-watch w :foo) w))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 4ee89df427644c096d32117194ae1d76f8dec836 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Tue, 23 Dec 2014 21:25:16 +0600 Subject: [PATCH 0325/4033] CLJS-919 compare-and-set! relies on Atom record structure instead of protocols --- src/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index e72dbcbc6..3842797d9 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -3505,10 +3505,10 @@ reduces them without incurring seq initialization" (defn compare-and-set! "Atomically sets the value of atom to newval if and only if the - current value of the atom is identical to oldval. Returns true if + current value of the atom is equal to oldval. Returns true if set happened, else false." [a oldval newval] - (if (= (.-state a) oldval) + (if (= (-deref a) oldval) (do (reset! a newval) true) false)) From f2b359c402456333783c0e3604bd737146c11bf0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 09:30:54 -0500 Subject: [PATCH 0326/4033] Add test for CLJS-919 Add type-hint to compare-and-set! Typo in CLJS-920 test --- src/cljs/cljs/core.cljs | 2 +- test/cljs/cljs/core_test.cljs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 3842797d9..4c178ca04 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -3507,7 +3507,7 @@ reduces them without incurring seq initialization" "Atomically sets the value of atom to newval if and only if the current value of the atom is equal to oldval. Returns true if set happened, else false." - [a oldval newval] + [^not-native a oldval newval] (if (= (-deref a) oldval) (do (reset! a newval) true) false)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 7627c4aa1..6c9fa44a5 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2542,11 +2542,27 @@ (-remove-watch [this key])) (deftest test-920-watch-ops-return-ref - (testing "tesing CLJS-92, add-watch/return-watch should return reference" + (testing "tesing CLJS-920, add-watch/return-watch should return reference" (let [w (MyWatchable.)] (is (identical? (add-watch w :foo (fn [])) w)) (is (identical? (remove-watch w :foo) w))))) +(deftype MyCustomAtom [^:mutable state] + IDeref + (-deref [_] state) + IReset + (-reset! [_ newval] + (set! state newval))) + +(deftest test-919-generic-cas + (testing "testing CLJS-919, CAS should on custom atom types" + (let [a0 (MyCustomAtom. 10) + a1 (MyCustomAtom. 0)] + (compare-and-set! a0 0 20) + (compare-and-set! a1 0 20) + (is (== @a0 10)) + (is (== @a1 20))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 576b85d83bf0eb0e232db3b2b8bddee0405f29d9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 09:56:22 -0500 Subject: [PATCH 0327/4033] CLJS-907: False positives from arithmetic checks Type inference case was missing for :dot AST. Added test. --- src/clj/cljs/analyzer.clj | 1 + test/clj/cljs/analyzer_tests.clj | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index a8b409d18..d7f23582c 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -500,6 +500,7 @@ :var (if (:init e) (infer-tag env (:init e)) (infer-tag env (:info e))) + :dot 'any nil))) (defmulti parse (fn [op & rest] op)) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index 67f86f61c..b6e9b1b3f 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -144,6 +144,11 @@ (:tag (a/analyze test-env '(if x "foo" 1)))) '#{number string}))) +(deftest method-inference + (is (= (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(.foo js/bar)))) + 'any))) + (deftest fn-inference (is (= (e/with-compiler-env test-cenv (:tag (a/analyze test-env From fdcf333aadd8728e93e1044040b90d67e17b7f57 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 11:52:07 -0500 Subject: [PATCH 0328/4033] CLJS-918: preserve :arglists metadata in analysis cache --- src/clj/cljs/analyzer.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index d7f23582c..8e153a7de 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -515,7 +515,7 @@ m (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) :name `(quote ~(symbol (name (:name var)))) :test `(when ~sym (.-cljs$lang$test ~sym)) - :arglists (:arglists var))] + :arglists (map with-meta (:arglists var) (:arglists-meta var)))] (analyze env m))})) (defmethod parse 'if @@ -689,6 +689,7 @@ :max-fixed-arity (:max-fixed-arity init-expr) :method-params params :arglists (:arglists sym-meta) + :arglists-meta (doall (map meta (:arglists sym-meta))) :methods (map (fn [method] (let [tag (infer-tag env (assoc method :op :method))] (cond-> (select-keys method From 307833b2069a74b84174b78edd927e665eb62a92 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 14:31:09 -0500 Subject: [PATCH 0329/4033] suppress meaningless warnings from analyzer tests --- test/clj/cljs/analyzer_tests.clj | 45 +++++++++++++++++++------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index b6e9b1b3f..9c7fa6057 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -107,8 +107,9 @@ (def test-cenv (atom {})) (def test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.core)) -(e/with-compiler-env test-cenv - (a/analyze-file (io/file "src/cljs/cljs/core.cljs"))) +(a/no-warn + (e/with-compiler-env test-cenv + (a/analyze-file (io/file "src/cljs/cljs/core.cljs")))) (deftest basic-inference (is (= (e/with-compiler-env test-cenv @@ -140,8 +141,9 @@ 'function))) (deftest if-inference - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(if x "foo" 1)))) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(if x "foo" 1))))) '#{number string}))) (deftest method-inference @@ -210,26 +212,33 @@ 'number))) (deftest test-numeric - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(dec x)))) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (a/analyze test-env '(dec x))))) 'number)) - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(int x)))) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (a/analyze test-env '(int x))))) 'number)) - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(unchecked-int x)))) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (a/analyze test-env '(unchecked-int x))))) 'number)) - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(mod x y)))) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (a/analyze test-env '(mod x y))))) 'number)) - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(quot x y)))) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (a/analyze test-env '(quot x y))))) 'number)) - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(rem x y)))) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (a/analyze test-env '(rem x y))))) 'number)) - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(bit-count n)))) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (a/analyze test-env '(bit-count n))))) 'number))) ;; ============================================================================= From 49648f63353022742474ce9d838e7201299512e8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 16:06:44 -0500 Subject: [PATCH 0330/4033] move `cljs.closure/output-directory` -> `cljs.util/output-directory` clean up cljs.build/api.clj --- src/clj/cljs/build/api.clj | 24 ++++++++++++++---------- src/clj/cljs/closure.clj | 29 +++++++++++++---------------- src/clj/cljs/util.clj | 2 ++ 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/clj/cljs/build/api.clj b/src/clj/cljs/build/api.clj index 6e29d205c..6fedbbc61 100644 --- a/src/clj/cljs/build/api.clj +++ b/src/clj/cljs/build/api.clj @@ -12,28 +12,32 @@ For example: a build script may need to how to invalidate compiled files so that they will be recompiled." (:require [cljs.util :as util] - [cljs.analyzer] + [cljs.analyzer :as ana] [cljs.env :as env] [cljs.closure] - [clojure.set :refer [intersection]])) + [clojure.set :refer [intersection]]) + (:import java.io.File)) -(defn target-file-for-cljs-ns +(defn ^File target-file-for-cljs-ns "Given an output directory and a clojurescript namespace return the compilation target file for that namespace. For example: (target-file-from-cljs-ns \"resources/out\" 'example.core) -> " - [output-dir ns-sym] - (util/to-target-file (cljs.closure/output-directory { :output-dir output-dir }) - {:ns ns-sym })) + ([ns-sym] (target-file-for-cljs-ns ns-sym nil)) + ([ns-sym output-dir] + (util/to-target-file + (util/output-directory {:output-dir output-dir}) + {:ns ns-sym}))) (defn mark-cljs-ns-for-recompile! "Backdates a cljs target file so that it the cljs compiler will recompile it." - [output-dir ns-sym] - (let [s (target-file-for-cljs-ns output-dir ns-sym)] - (when (.exists s) - (.setLastModified s 5000)))) + ([ns-sym] (mark-cljs-ns-for-recompile! ns-sym nil)) + ([ns-sym output-dir] + (let [s (target-file-for-cljs-ns output-dir ns-sym)] + (when (.exists s) + (.setLastModified s 5000))))) (defn cljs-dependents-for-macro-namespaces "Takes a list of Clojure (.clj) namespaces that define macros and diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 60693ba14..6a8f3c1c5 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -315,9 +315,6 @@ (doseq [form forms] (comp/emit (ana/analyze (ana/empty-env) form)))))))) -(defn output-directory [opts] - (or (:output-dir opts) "out")) - (defn compiled-file "Given a map with at least a :file key, return a map with {:file .. :provides .. :requires ..}. @@ -341,7 +338,7 @@ IJavaScript." [^File file {:keys [output-file] :as opts}] (if output-file - (let [out-file (io/file (output-directory opts) output-file)] + (let [out-file (io/file (util/output-directory opts) output-file)] (compiled-file (comp/compile-file file out-file opts))) (binding [ana/*cljs-file* (.getPath ^java.io.File file)] (compile-form-seq (ana/forms-seq file))))) @@ -350,7 +347,7 @@ "Recursively compile all cljs files under the given source directory. Return a list of JavaScriptFiles." [^File src-dir opts] - (let [out-dir (output-directory opts)] + (let [out-dir (util/output-directory opts)] (map compiled-file (comp/compile-root src-dir out-dir opts)))) @@ -376,15 +373,15 @@ "Compile a file from a jar." [this {:keys [output-file] :as opts}] (or (when output-file - (let [out-file (io/file (output-directory opts) output-file)] + (let [out-file (io/file (util/output-directory opts) output-file)] (when (and (.exists out-file) (= (util/compiled-by-version out-file) (util/clojurescript-version))) (compile-file - (io/file (output-directory opts) + (io/file (util/output-directory opts) (last (string/split (.getPath ^URL this) #"\.jar!/"))) opts)))) - (let [file-on-disk (jar-file-to-disk this (output-directory opts))] + (let [file-on-disk (jar-file-to-disk this (util/output-directory opts))] (-compile file-on-disk opts)))) (extend-protocol Compilable @@ -529,7 +526,7 @@ should contain the source for the given namespace name." (:requires %)) (assoc :group (:group %))) required-js) [(when (-> @env/*compiler* :opts :emit-constants) - (let [url (deps/to-url (str (output-directory opts) "/constants_table.js"))] + (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] required-cljs inputs))))) @@ -653,7 +650,7 @@ should contain the source for the given namespace name." {:preamble-line-count preamble-line-count :lines (+ (:lineCount sm-json) preamble-line-count 2) :file (:file sm-json) - :output-dir (output-directory opts) + :output-dir (util/output-directory opts) :source-map-path (:source-map-path opts) :source-map (:source-map opts) :relpaths relpaths})))))) @@ -726,7 +723,7 @@ should contain the source for the given namespace name." [opts input] (letfn [(ns-list [coll] (when (seq coll) (apply str (interpose ", " (map #(str "'" (comp/munge %) "'") coll)))))] (str "goog.addDependency(\"" - (path-relative-to (io/file (output-directory opts) "goog/base.js") input) + (path-relative-to (io/file (util/output-directory opts) "goog/base.js") input) "\", [" (ns-list (deps/-provides input)) "], [" @@ -774,7 +771,7 @@ should contain the source for the given namespace name." "Write a JavaScript file to disk. Only write if the file does not already exist. Return IJavaScript for the file on disk." [opts js] - (let [out-dir (io/file (output-directory opts)) + (let [out-dir (io/file (util/output-directory opts)) out-name (output-path js) out-file (io/file out-dir out-name)] (do (when-not (.exists out-file) @@ -796,7 +793,7 @@ should contain the source for the given namespace name." ;; when source maps enabled (let [out-file (if-let [ns (and (:source-map opts) (first (:provides js)))] - (io/file (io/file (output-directory opts)) + (io/file (io/file (util/output-directory opts)) (util/ns->relpath ns))) source-url (:source-url js)] (when (and out-file source-url @@ -826,7 +823,7 @@ should contain the source for the given namespace name." libraries." [opts & sources] (let [disk-sources (map #(source-on-disk opts %) sources)] - (let [goog-deps (io/file (output-directory opts) "goog/deps.js")] + (let [goog-deps (io/file (util/output-directory opts) "goog/deps.js")] (do (util/mkdirs goog-deps) (spit goog-deps (deps-file opts (filter #(= (:group %) :goog) disk-sources))) (output-deps-file opts (remove #(= (:group %) :goog) disk-sources)))))) @@ -995,7 +992,7 @@ should contain the source for the given namespace name." ; add-dependencies below _ (when emit-constants (comp/emit-constants-table-to-file (::ana/constant-table @env/*compiler*) - (str (output-directory all-opts) "/constants_table.js"))) + (str (util/output-directory all-opts) "/constants_table.js"))) js-sources (concat (apply add-dependencies all-opts (concat (if (coll? compiled) compiled [compiled]) @@ -1021,7 +1018,7 @@ should contain the source for the given namespace name." ;; emit Node.js bootstrap script for :none & :whitespace optimizations (when (and (= (:target opts) :nodejs) (#{:none :whitespace} (:optimizations opts))) - (let [outfile (io/file (io/file (output-directory opts)) "goog/bootstrap/nodejs.js")] + (let [outfile (io/file (io/file (util/output-directory opts)) "goog/bootstrap/nodejs.js")] (util/mkdirs outfile) (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 0122d2375..2ca7dc5c7 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -69,3 +69,5 @@ [^File f] (.mkdirs (.getParentFile (.getCanonicalFile f)))) +(defn output-directory [opts] + (or (:output-dir opts) "out")) From f954bc5e06351dc71ac4dfa7d1039b3e95314512 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 16:09:32 -0500 Subject: [PATCH 0331/4033] fixup build api tests --- test/clj/cljs/build_api_tests.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/clj/cljs/build_api_tests.clj b/test/clj/cljs/build_api_tests.clj index c503a4564..0e23fbf11 100644 --- a/test/clj/cljs/build_api_tests.clj +++ b/test/clj/cljs/build_api_tests.clj @@ -6,9 +6,9 @@ [cljs.analyzer])) (deftest test-target-file-for-cljs-ns - (is (= (.getPath (target-file-for-cljs-ns nil 'example.core-lib)) + (is (= (.getPath (target-file-for-cljs-ns 'example.core-lib nil)) "out/example/core_lib.js")) - (is (= (.getPath (target-file-for-cljs-ns "output" 'example.core-lib)) + (is (= (.getPath (target-file-for-cljs-ns 'example.core-lib "output")) "output/example/core_lib.js"))) (deftest test-cljs-dependents-for-macro-namespaces From 2cc6cbcd60b3761e06b1170caa725547e9734de8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Dec 2014 16:52:01 -0500 Subject: [PATCH 0332/4033] add `cljs.analyzer/ns-dependents`, returns sorted list of all dependent namespaces given an namespace. Includes transitive dependents. Add corresponding fn `cljs-ns-dependents` to cljs.build/api, add tests --- src/clj/cljs/analyzer.clj | 17 +++++++ src/clj/cljs/build/api.clj | 30 ++++++++++++- test/clj/cljs/build_api_tests.clj | 73 +++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 8e153a7de..32993879b 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -412,6 +412,23 @@ (when (and ev (not (-> ev :dynamic))) (warning :dynamic env {:ev ev}))))) +(defn ns-dependents + ([ns] (ns-dependents ns 0 (atom (sorted-map)))) + ([ns depth state] + (let [deps (set + (map first + (filter + (fn [[_ ns-info]] + (contains? (:requires ns-info) ns)) + (get @env/*compiler* ::namespaces))))] + (swap! state assoc depth deps) + (doseq [dep deps] + (ns-dependents dep (inc depth) state)) + (doseq [[ x :require-macros vals set)))) (vals (:cljs.analyzer/namespaces @env/*compiler*)))))) + +(defn cljs-ns-dependents + "Given a namespace symbol return a seq of all dependent + namespaces sorted in dependency order. Will include + transient dependents." + [ns] + (ana/ns-dependents ns)) + +(comment + + (def test-cenv (atom {})) + (def test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) + + (binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env test-cenv + (ana/no-warn + (ana/analyze test-env + '(ns cljs.user + (:use [clojure.string :only [join]])))))) + + (env/with-compiler-env test-cenv + (ns-dependents 'clojure.string)) + + (map + #(target-file-for-cljs-ns % "out-dev") + (env/with-compiler-env test-cenv + (ns-dependents 'clojure.string))) + ) \ No newline at end of file diff --git a/test/clj/cljs/build_api_tests.clj b/test/clj/cljs/build_api_tests.clj index 0e23fbf11..b6def5e18 100644 --- a/test/clj/cljs/build_api_tests.clj +++ b/test/clj/cljs/build_api_tests.clj @@ -1,9 +1,8 @@ (ns cljs.build-api-tests (:use cljs.build.api) (:use clojure.test) - (:require - [cljs.env :as env] - [cljs.analyzer])) + (:require [cljs.env :as env] + [cljs.analyzer :as ana])) (deftest test-target-file-for-cljs-ns (is (= (.getPath (target-file-for-cljs-ns 'example.core-lib nil)) @@ -37,3 +36,71 @@ #{'example.core 'example.util 'example.helpers})) (is (= (set (cljs-dependents-for-macro-namespaces ['example.not-macros])) #{})))) + +(def test-cenv (atom {})) +(def test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) + +;; basic + +(binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env test-cenv + (ana/no-warn + (ana/analyze test-env + '(ns cljs.user + (:use [clojure.string :only [join]])))))) + +;; linear + +(binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env test-cenv + (ana/no-warn + (ana/analyze test-env + '(ns foo.core))))) + +(binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env test-cenv + (ana/no-warn + (ana/analyze test-env + '(ns bar.core + (:require [foo.core :as foo])))))) + +(binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env test-cenv + (ana/no-warn + (ana/analyze test-env + '(ns baz.core + (:require [bar.core :as bar])))))) + +;; graph + +(binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env test-cenv + (ana/no-warn + (ana/analyze test-env + '(ns graph.foo.core))))) + +(binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env test-cenv + (ana/no-warn + (ana/analyze test-env + '(ns graph.bar.core + (:require [graph.foo.core :as foo])))))) + +(binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env test-cenv + (ana/no-warn + (ana/analyze test-env + '(ns graph.baz.core + (:require [graph.foo.core :as foo] + [graph.bar.core :as bar])))))) + +(deftest test-cljs-ns-dependencies + (is (= (env/with-compiler-env test-cenv + (cljs-ns-dependents 'clojure.string)) + '(cljs.user))) + (is (= (env/with-compiler-env test-cenv + (cljs-ns-dependents 'foo.core)) + '(bar.core baz.core))) + (is (= (env/with-compiler-env test-cenv + (cljs-ns-dependents 'graph.foo.core)) + '(graph.bar.core graph.baz.core)))) \ No newline at end of file From 2a79671e0d28ac7bd2809e80e12352f6854f8917 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Dec 2014 11:12:56 -0500 Subject: [PATCH 0333/4033] re-add `cljs.closure/output-directory` for backwards compatibility, just defers to `cljs.util/output-directory` --- src/clj/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 6a8f3c1c5..28b8413f0 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1022,7 +1022,10 @@ should contain the source for the given namespace name." (util/mkdirs outfile) (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) - + +;; for backwards compatibility +(defn output-directory [opts] + (util/output-directory opts)) (comment From ecc1719b628b38dc9a179cfe675c136c831282f0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Dec 2014 12:38:29 -0500 Subject: [PATCH 0334/4033] bring over old Node REPL support stuff from my fork from 3 years ago, decent starting point --- bin/node_repl.js | 50 ++++++++++++++++++++++ script/node_repl | 16 +++++++ src/clj/cljs/repl/node.clj | 88 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 bin/node_repl.js create mode 100755 script/node_repl create mode 100644 src/clj/cljs/repl/node.clj diff --git a/bin/node_repl.js b/bin/node_repl.js new file mode 100644 index 000000000..9e7b96f70 --- /dev/null +++ b/bin/node_repl.js @@ -0,0 +1,50 @@ +process.env.NODE_DISABLE_COLORS = true; + +var net = require("net"), + repl = require("repl"), + vm = require("vm"), + context = vm.createContext(); + +context.require = require; + +net.createServer(function (socket) { + var buffer = "", ret; + socket.setEncoding("utf8"); + + // redefine console.log + context.node_repl_print = function(x) { + ret = vm.runInContext(x, context, "repl"); + socket.write(ret.toString()); + socket.write("\1"); + }; + + socket.on("data", function(data) { + if(data[data.length-1] != "\0") { + buffer += data; + } else { + if(buffer.length > 0) { + data = buffer + data; + buffer = ""; + } + if(data) { + // not sure how \0's are getting through - David + data = data.replace(/\0/g, ""); + try { + ret = vm.runInContext(data, context, "repl"); + } catch (x) { + console.log(x.stack); + socket.write(x.stack+"\n"); + } + } + if(ret !== undefined && ret !== null) { + socket.write(ret.toString()); + } else { + socket.write("nil"); + } + socket.write("\0"); + } + }); + +}).listen(5001); + +console.log("repl.js listening on 5001") diff --git a/script/node_repl b/script/node_repl new file mode 100755 index 000000000..ea25efd30 --- /dev/null +++ b/script/node_repl @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ "$CLOJURESCRIPT_HOME" = "" ]; then + CLOJURESCRIPT_HOME="`dirname $0`/.." +fi + +CLJSC_CP='' +for next in lib/*: src/clj: src/cljs: test/cljs; do + CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next +done + +/usr/local/bin/node $CLOJURESCRIPT_HOME/bin/repl.js & +java -server -cp $CLJSC_CP clojure.main -e \ +"(require '[cljs.repl :as repl]) +(require '[cljs.repl.node :as node]) +(repl/repl (node/repl-env $1))" \ No newline at end of file diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj new file mode 100644 index 000000000..817031859 --- /dev/null +++ b/src/clj/cljs/repl/node.clj @@ -0,0 +1,88 @@ +;; 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.repl.node + (:refer-clojure :exclude [loaded-libs]) + (:require [clojure.string :as string] + [clojure.java.io :as io] + [cljs.compiler :as comp] + [cljs.repl :as repl] + [cljs.closure :as cljsc]) + (:import cljs.repl.IJavaScriptEnv + java.net.Socket + [java.io BufferedReader BufferedWriter])) + +(def current-repl-env (atom nil)) +(def loaded-libs (atom #{})) + +(defn socket [host port] + (let [socket (java.net.Socket. host port) + in (io/reader socket) + out (io/writer socket)] + {:socket socket :in in :out out})) + +(defn close-socket [s] + (.close (:in s)) + (.close (:out s)) + (.close (:socket s))) + +(defn write [^BufferedWriter out ^String js] + (.write out js) + (.write out (int 0)) ;; terminator + (.flush out)) + +(defn read-response [^BufferedReader in] + (let [sb (java.lang.StringBuilder.)] + (loop [sb sb c (.read in)] + (cond + (= c 1) (let [ret (str sb)] + (print ret) + (recur (java.lang.StringBuilder.) (.read in))) + (= c 0) (str sb) + :else (do + (.append sb (char c)) + (recur sb (.read in))))))) + +(defn node-eval [{:keys [in out]} js] + (write out js) + {:status :success :value (read-response in)}) + +(defn load-javascript [ctx ns url] + (node-eval ctx (slurp url))) + +(defn setup [repl-env] + (let [env {:context :statement :locals {} :ns (@comp/namespaces comp/*cljs-ns*)} + scope (:scope repl-env)] + (repl/load-file repl-env "cljs/core.cljs") + (swap! loaded-libs conj "cljs.core") + (repl/evaluate-form repl-env + env + "" + '(ns cljs.user)) + (repl/evaluate-form repl-env + env + "" + '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))))) + +(extend-protocol repl/IJavaScriptEnv + clojure.lang.IPersistentMap + (-setup [this] (setup this)) + (-evaluate [this filename line js] (node-eval this js)) + (-load [this ns url] (load-javascript this ns url)) + (-tear-down [this] (close-socket this))) + +;; do we need to implement our own version of goog.require ? - David + +(defn repl-env + [& {:keys [host port] :or {host "localhost" port 5001}}] + (let [repl-env (socket host port) + base (io/resource "goog/base.js") + deps (io/resource "goog/deps.js")] + (node-eval repl-env (slurp (io/reader base))) + (node-eval repl-env (slurp (io/reader deps))) + repl-env)) From eb32c18e96da934a2fc44fff377f3aa56ae5cff3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Dec 2014 12:42:23 -0500 Subject: [PATCH 0335/4033] tweaks to script/node_repl --- script/node_repl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/node_repl b/script/node_repl index ea25efd30..b0e714fb5 100755 --- a/script/node_repl +++ b/script/node_repl @@ -9,8 +9,8 @@ for next in lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done -/usr/local/bin/node $CLOJURESCRIPT_HOME/bin/repl.js & +node $CLOJURESCRIPT_HOME/bin/node_repl.js & java -server -cp $CLJSC_CP clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.node :as node]) -(repl/repl (node/repl-env $1))" \ No newline at end of file +(repl/repl (node/repl-env $1))" From e89976c2d9ea3cf1c7b6a76bad146239b54ec1ee Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Dec 2014 12:53:43 -0500 Subject: [PATCH 0336/4033] Cleanup cljs/repl/node.clj --- src/clj/cljs/repl/node.clj | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 817031859..471e687af 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -10,20 +10,23 @@ (:refer-clojure :exclude [loaded-libs]) (:require [clojure.string :as string] [clojure.java.io :as io] + [cljs.env :as env] + [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.repl :as repl] [cljs.closure :as cljsc]) (:import cljs.repl.IJavaScriptEnv java.net.Socket + java.lang.StringBuilder [java.io BufferedReader BufferedWriter])) (def current-repl-env (atom nil)) (def loaded-libs (atom #{})) (defn socket [host port] - (let [socket (java.net.Socket. host port) - in (io/reader socket) - out (io/writer socket)] + (let [socket (Socket. host port) + in (io/reader socket) + out (io/writer socket)] {:socket socket :in in :out out})) (defn close-socket [s] @@ -37,12 +40,12 @@ (.flush out)) (defn read-response [^BufferedReader in] - (let [sb (java.lang.StringBuilder.)] + (let [sb (StringBuilder.)] (loop [sb sb c (.read in)] (cond (= c 1) (let [ret (str sb)] (print ret) - (recur (java.lang.StringBuilder.) (.read in))) + (recur (StringBuilder.) (.read in))) (= c 0) (str sb) :else (do (.append sb (char c)) @@ -56,18 +59,16 @@ (node-eval ctx (slurp url))) (defn setup [repl-env] - (let [env {:context :statement :locals {} :ns (@comp/namespaces comp/*cljs-ns*)} + (let [env (ana/empty-env) scope (:scope repl-env)] (repl/load-file repl-env "cljs/core.cljs") (swap! loaded-libs conj "cljs.core") (repl/evaluate-form repl-env - env - "" - '(ns cljs.user)) + env "" + '(ns cljs.user)) (repl/evaluate-form repl-env - env - "" - '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))))) + env "" + '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))))) (extend-protocol repl/IJavaScriptEnv clojure.lang.IPersistentMap @@ -81,8 +82,8 @@ (defn repl-env [& {:keys [host port] :or {host "localhost" port 5001}}] (let [repl-env (socket host port) - base (io/resource "goog/base.js") - deps (io/resource "goog/deps.js")] + base (io/resource "goog/base.js") + deps (io/resource "goog/deps.js")] (node-eval repl-env (slurp (io/reader base))) (node-eval repl-env (slurp (io/reader deps))) repl-env)) From d3c1fb17cc4951dad1c699d53000bb657fc5903d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Dec 2014 13:42:37 -0500 Subject: [PATCH 0337/4033] More small tweaks to bin/node_repl.js so that Google Closure Node.js bootstrap script works - now need to emit all files to an :output-dir so that bootstrap script can do its thing for namespaces. --- bin/node_repl.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/node_repl.js b/bin/node_repl.js index 9e7b96f70..d1064c206 100644 --- a/bin/node_repl.js +++ b/bin/node_repl.js @@ -5,7 +5,10 @@ var net = require("net"), vm = require("vm"), context = vm.createContext(); -context.require = require; +context.require = require; +context.global = global; +context.process = process; +context.__dirname = __dirname; net.createServer(function (socket) { var buffer = "", ret; From 1d30a489c9037a134fbc064de0b1986b89b73b41 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Dec 2014 11:57:04 -0500 Subject: [PATCH 0338/4033] Fix typo in cljs/pprint.cljs ns form --- src/cljs/cljs/pprint.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/pprint.cljs b/src/cljs/cljs/pprint.cljs index e0c520b6d..76367df32 100644 --- a/src/cljs/cljs/pprint.cljs +++ b/src/cljs/cljs/pprint.cljs @@ -34,7 +34,7 @@ a more powerful alternative to Clojure's standard format function. See documentation for pprint and cl-format for more information or complete documentation on the the clojure web site on github."} cljs.pprint - (:require [clojure.walk :only [walk]])) + (:require [clojure.walk :refer [walk]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Helper functions for digesting formats in the various From bddb0ee6689e5d02179ae57074f686ec65b02e9d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Dec 2014 11:58:45 -0500 Subject: [PATCH 0339/4033] Add desugared `cljs.repl/repl*` fn, `cljs.repl/repl` now delegates to it --- src/clj/cljs/repl.clj | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index f76109aa7..ac1471e2a 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -175,10 +175,9 @@ (doseq [file (comp/cljs-files-in src-dir)] (ana/analyze-file (str "file://" (.getAbsolutePath file)))))) -(defn repl - "Note - repl will reload core.cljs every time, even if supplied old repl-env" - [repl-env & {:keys [analyze-path verbose warn-on-undeclared special-fns static-fns] :as opts - :or {warn-on-undeclared true}}] +(defn repl* + [repl-env {:keys [analyze-path verbose warn-on-undeclared special-fns static-fns] :as opts + :or {warn-on-undeclared true}}] (print "To quit, type: ") (prn :cljs/quit) (env/with-compiler-env @@ -202,34 +201,39 @@ (print (str "ClojureScript:" ana/*cljs-ns* "> ")) (flush) (let [rdr (readers/source-logging-push-back-reader - (java.io.PushbackReader. (io/reader *in*)) - 1 - "NO_SOURCE_FILE") + (java.io.PushbackReader. (io/reader *in*)) + 1 + "NO_SOURCE_FILE") form (try (binding [*ns* (create-ns ana/*cljs-ns*) reader/*data-readers* tags/*cljs-data-readers* reader/*alias-map* (apply merge - ((juxt :requires :require-macros) - (ana/get-namespace ana/*cljs-ns*)))] + ((juxt :requires :require-macros) + (ana/get-namespace ana/*cljs-ns*)))] (reader/read rdr nil read-error)) (catch Exception e (println (.getMessage e)) read-error))] (cond - (identical? form read-error) (recur) - (= form :cljs/quit) :quit + (identical? form read-error) (recur) + (= form :cljs/quit) :quit - (and (seq? form) (is-special-fn? (first form))) - (do (apply (get special-fns (first form)) repl-env (rest form)) - (newline) - (recur)) + (and (seq? form) (is-special-fn? (first form))) + (do (apply (get special-fns (first form)) repl-env (rest form)) + (newline) + (recur)) - :else - (do (eval-and-print repl-env env form) - (recur))))) + :else + (do (eval-and-print repl-env env form) + (recur))))) (-tear-down repl-env))))) +(defn repl + "Note - repl will reload core.cljs every time, even if supplied old repl-env" + [repl-env & {:as opts}] + (repl* repl-env opts)) + (defmacro doc "Prints documentation for a var or special form given its name" [sym] From d5347529486723d8b52ba21e9b79acbec5b444ec Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Dec 2014 12:09:02 -0500 Subject: [PATCH 0340/4033] Add comment to cljs/repl.clj about interaction support fns --- src/clj/cljs/repl.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ac1471e2a..b2d310f72 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -234,6 +234,9 @@ [repl-env & {:as opts}] (repl* repl-env opts)) +;; ============================================================================= +;; ClojureScript REPL interaction support + (defmacro doc "Prints documentation for a var or special form given its name" [sym] From 29ab299363a2c53bb379c62469954fb3988266aa Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Dec 2014 13:16:17 -0500 Subject: [PATCH 0341/4033] Start passing `opts` consistently around in cljs/repl.clj and cljs/rhino.clj so we can read build options same as elsewhere --- src/clj/cljs/repl.clj | 38 +++++++++++++++++++++++++------------ src/clj/cljs/repl/rhino.clj | 34 +++++++++++++++++---------------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index b2d310f72..0b546f998 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -131,11 +131,12 @@ (evaluate-form repl-env env filename form))))) (defn load-file - [repl-env f] - (binding [ana/*cljs-ns* 'cljs.user] - (let [res (if (= \/ (first f)) f (io/resource f))] - (assert res (str "Can't find " f " in classpath")) - (load-stream repl-env f res)))) + ([repl-env f] (load-file repl-env f nil)) + ([repl-env f opts] + (binding [ana/*cljs-ns* 'cljs.user] + (let [res (if (= \/ (first f)) f (io/resource f))] + (assert res (str "Can't find " f " in classpath")) + (load-stream repl-env f res))))) (defn- wrap-fn [form] (cond (and (seq? form) (= 'ns (first form))) identity @@ -156,15 +157,28 @@ (wrap-fn form)))) (def default-special-fns - (let [load-file-fn (fn [repl-env file] (load-file repl-env file))] - {'in-ns (fn [_ quoted-ns] - (let [ns-name (second quoted-ns)] - (when-not (ana/get-namespace ns-name) - (swap! env/*compiler* update-in [::ana/namespaces ns-name] {:name ns-name})) - (set! ana/*cljs-ns* ns-name))) + (let [load-file-fn + (fn self + ([repl-env file] + (self repl-env file nil)) + ([repl-env file opts] + (load-file repl-env file opts)))] + {'in-ns + (fn self + ([_ quoted-ns] + (self _ quoted-ns nil)) + ([_ quoted-ns opts] + (let [ns-name (second quoted-ns)] + (when-not (ana/get-namespace ns-name) + (swap! env/*compiler* update-in [::ana/namespaces ns-name] {:name ns-name})) + (set! ana/*cljs-ns* ns-name)))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn - 'load-namespace (fn [repl-env ns] (load-namespace repl-env ns))})) + 'load-namespace + (fn self + ([repl-env ns] (self repl-env ns nil)) + ([repl-env ns opts] + (load-namespace repl-env ns)))})) (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 082b1fc87..f605e0453 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -93,22 +93,24 @@ (catch Throwable ex (println (.getMessage ex)))) (swap! (:loaded-libs repl-env) (partial apply conj) missing))))) -(defn rhino-setup [repl-env] - (let [env (ana/empty-env) - scope (:scope repl-env)] - (repl/load-file repl-env "cljs/core.cljs") - (swap! (:loaded-libs repl-env) conj "cljs.core") - (repl/evaluate-form repl-env - env - "" - '(ns cljs.user)) - (ScriptableObject/putProperty scope - "out" - (Context/javaToJS *out* scope)) - (repl/evaluate-form repl-env - env - "" - '(set! *print-fn* (fn [x] (.write js/out x)))))) +(defn rhino-setup + ([repl-env] (rhino-setup repl-env nil)) + ([repl-env opts] + (let [env (ana/empty-env) + scope (:scope repl-env)] + (repl/load-file repl-env "cljs/core.cljs" opts) + (swap! (:loaded-libs repl-env) conj "cljs.core") + (repl/evaluate-form repl-env + env + "" + '(ns cljs.user)) + (ScriptableObject/putProperty scope + "out" + (Context/javaToJS *out* scope)) + (repl/evaluate-form repl-env + env + "" + '(set! *print-fn* (fn [x] (.write js/out x))))))) (defrecord RhinoEnv [loaded-libs] repl/IJavaScriptEnv From 803bc7d604298df254c08bafe2f600353d02a273 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Dec 2014 19:10:09 -0500 Subject: [PATCH 0342/4033] Change `cljs.util/output-directory` so that it can take a default parameter. --- src/clj/cljs/util.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 2ca7dc5c7..98701520f 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -69,5 +69,7 @@ [^File f] (.mkdirs (.getParentFile (.getCanonicalFile f)))) -(defn output-directory [opts] - (or (:output-dir opts) "out")) +(defn output-directory + ([opts] (output-directory opts "out")) + ([opts default] + (or (:output-dir opts) default))) From 21593b668f9397db184b2263c09bfca34873f537 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Dec 2014 20:54:48 -0500 Subject: [PATCH 0343/4033] Add file, filename, url, path and extension helpers to cljs.util --- src/clj/cljs/util.clj | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 98701520f..52dc4965a 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -9,7 +9,8 @@ (ns cljs.util (:require [clojure.java.io :as io] [clojure.string :as string]) - (:import [java.io File])) + (:import [java.io File] + [java.net URL])) ;; next line is auto-generated by the build-script - Do not edit! (def ^:dynamic *clojurescript-version*) @@ -73,3 +74,23 @@ ([opts] (output-directory opts "out")) ([opts default] (or (:output-dir opts) default))) + +(defn file? [f] + (instance? File f)) + +(defn url? [f] + (instance? URL f)) + +(defn ^String filename [^File f] + (.getName f)) + +(defn ^String path [^URL url] + (.getPath url)) + +(defn ^String ext + "Given a file or url return the file extension." + [f] + (let [s (cond + (file? f) (filename f) + (url? f) (path f))] + (last (string/split s #"\.")))) \ No newline at end of file From b3463c5d9317b78ca641133f5ff22bdc90f6cc55 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Dec 2014 22:48:12 -0500 Subject: [PATCH 0344/4033] Additions to cljs.closure and cljs.build.api, helpers needed for improved REPL behavior parse-js-ns, src-file->target-file, and src-file->goog-require --- src/clj/cljs/build/api.clj | 26 ++++++++++++++++++++++++-- src/clj/cljs/closure.clj | 29 +++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/build/api.clj b/src/clj/cljs/build/api.clj index 85c3773de..44c178091 100644 --- a/src/clj/cljs/build/api.clj +++ b/src/clj/cljs/build/api.clj @@ -14,8 +14,11 @@ (:require [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] - [cljs.closure] - [clojure.set :refer [intersection]]) + [cljs.compiler :as comp] + [cljs.closure :as closure] + [clojure.set :refer [intersection]] + [cljs.js-deps :as js-deps] + [clojure.java.io :as io]) (:import java.io.File)) (defn ^File target-file-for-cljs-ns @@ -67,6 +70,25 @@ [ns] (ana/ns-dependents ns)) +(defn parse-js-ns + "Given a Google Closure style JavaScript file or resource return the namespace + information for the given file. Only returns the value extracted from the + first provide statement." + [f] + (closure/parse-js-ns f)) + +(defn ^File src-file->target-file + "Given a ClojureScript source file return the target file. May optionally + provide build options with :output-dir specified." + ([src] (closure/src-file->target-file src)) + ([src opts] (closure/src-file->target-file src opts))) + +(defn ^String src-file->goog-require + "Given a ClojureScript or Google Closure style JavaScript source file return + the goog.require statement for it." + [src] + (closure/src-file->goog-require src)) + (comment (def test-cenv (atom {})) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 28b8413f0..7cb2549a3 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1023,10 +1023,35 @@ should contain the source for the given namespace name." (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) +;; ============================================================================= +;; Utilities + ;; for backwards compatibility (defn output-directory [opts] (util/output-directory opts)) +(defn parse-js-ns [f] + (deps/parse-js-ns (line-seq (io/reader f)))) + +(defn ^File src-file->target-file + "Given a ClojureScript source file return the target file. May optionally + provide build options with :output-dir specified." + ([src] (src-file->target-file src nil)) + ([src opts] + (util/to-target-file + (util/output-directory opts) + (ana/parse-ns src)))) + +(defn ^String src-file->goog-require [src] + (let [goog-ns + (case (util/ext src) + "cljs" (comp/munge (:ns (ana/parse-ns src))) + "js" (-> (parse-js-ns src) :provides first) + (throw + (IllegalArgumentException. + (str "Can't create goog.require expression for " src))))] + (str "goog.require(\"" goog-ns "\");"))) + (comment (println (build '[(ns hello.core) @@ -1038,12 +1063,12 @@ should contain the source for the given namespace name." (build "samples/hello/src" {:optimizations :advanced}) (build "samples/hello/src" {:optimizations :advanced :output-to "samples/hello/hello.js"}) ;; open 'samples/hello/hello.html' to see the result in action - + ;; build a project without optimizations (build "samples/hello/src" {:output-dir "samples/hello/out" :output-to "samples/hello/hello.js"}) ;; open 'samples/hello/hello-dev.html' to see the result in action ;; notice how each script was loaded individually - + ;; build unoptimized from raw ClojureScript (build '[(ns hello.core) (defn ^{:export greet} greet [n] (str "Hola " n)) From c5f09ebbf1f3f9f12ecb89dd237634d6ecad276c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 11:41:08 -0500 Subject: [PATCH 0345/4033] Remove resource leak from `cljs.analyzer/parse-ns` Change `cljs.analyzer/forms-seq` so that it takes an optional third parameter `return-reader?`. If `true` return a vector where the second element is the reader. In `cljs.analyzer/parse-ns` close the reader at the end of the loop. --- src/clj/cljs/analyzer.clj | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 32993879b..dbc0c804c 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -19,7 +19,7 @@ [clojure.tools.reader.reader-types :as readers] [clojure.edn :as edn]) (:import java.lang.StringBuilder - java.io.File + [java.io File Reader PushbackReader] java.net.URL [cljs.tagged_literals JSValue])) @@ -1654,26 +1654,29 @@ `clojure.java.io/reader` can produce a `java.io.Reader`. Optionally accepts a [filename] argument, which the reader will use in any emitted errors." ([f] (forms-seq f (source-path f))) - ([f filename] - (let [rdr (io/reader f) - pbr (readers/indexing-push-back-reader - (java.io.PushbackReader. rdr) 1 filename) - data-readers tags/*cljs-data-readers* - forms-seq* - (fn forms-seq* [] - (lazy-seq + ([f filename] (forms-seq f filename false)) + ([f filename return-reader?] + (let [rdr (io/reader f) + pbr (readers/indexing-push-back-reader + (PushbackReader. rdr) 1 filename) + data-readers tags/*cljs-data-readers* + forms-seq* + (fn forms-seq* [] + (lazy-seq (let [eof-sentinel (Object.) form (binding [*ns* (create-ns *cljs-ns*) reader/*data-readers* data-readers reader/*alias-map* (apply merge - ((juxt :requires :require-macros) - (get-namespace *cljs-ns*)))] + ((juxt :requires :require-macros) + (get-namespace *cljs-ns*)))] (reader/read pbr nil eof-sentinel))] (if (identical? form eof-sentinel) (.close rdr) (cons form (forms-seq*))))))] - (forms-seq*)))) + (if (true? return-reader?) + [(forms-seq*) rdr] + (forms-seq*))))) (defn parse-ns ([src] (parse-ns src nil nil)) @@ -1684,13 +1687,14 @@ argument, which the reader will use in any emitted errors." ret (binding [*cljs-ns* 'cljs.user *analyze-deps* (or (:analyze-deps opts) false)] - (loop [forms (forms-seq src)] + (loop [[forms rdr] (forms-seq src (source-path src) true)] (if (seq forms) (let [env (empty-env) ast (no-warn (analyze env (first forms) nil opts))] (if (= (:op ast) :ns) (let [ns-name (:name ast) deps (merge (:uses ast) (:requires ast))] + (.close ^Reader rdr) (merge {:ns (or ns-name 'cljs.user) :provides [ns-name] From a1e493b8cb74279d2f84ca4fdb3e7ab4c9ababaa Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 12:42:45 -0500 Subject: [PATCH 0346/4033] Clean up handling of var :test metadata Instead of attaching to var AST simply put it on the def AST. Elide test code forms from analysis environment --- src/clj/cljs/analyzer.clj | 23 ++++++++++++----------- src/clj/cljs/compiler.clj | 6 +++--- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index dbc0c804c..3a8e14fc1 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -683,7 +683,9 @@ (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] (merge {:name var-name} - sym-meta + ;; elide test metadata, as it includes non-valid EDN - David + (cond-> sym-meta + :test (-> (dissoc :test) (assoc :test true))) (when doc {:doc doc}) (when dynamic {:dynamic true}) (source-info var-name env) @@ -717,14 +719,16 @@ {:ret-tag tag}))) (merge {:env env :op :def :form form :name var-name - :var (assoc (analyze (-> env (dissoc :locals) - ;; if there's :test var metadata will need locals - (assoc :test-locals locals) - (assoc :context :expr) - (assoc :def-var true)) - sym) + :var (assoc + (analyze + (-> env (dissoc :locals) + (assoc :context :expr) + (assoc :def-var true)) + sym) :op :var) :doc doc :init init-expr} + (when-let [test (:test sym-meta)] + {:test (analyze (assoc env :context :expr) test)}) (when tag (if fn-var? {:ret-tag tag} @@ -1463,10 +1467,7 @@ (if-not (contains? (meta sym) ::analyzed) (resolve-existing-var env sym) (resolve-var env sym))) - (let [ret (assoc ret :op :var :info (resolve-var env sym))] - (if-let [test (-> sym meta :test)] - (assoc ret :test (analyze (assoc env :locals (:test-locals env)) test)) - ret))))))) + (assoc ret :op :var :info (resolve-var env sym))))))) (defn get-expander [sym env] (let [mvar diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 2640dbb68..9a8b65a4b 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -425,7 +425,7 @@ (emitln "*/"))))) (defmethod emit* :def - [{:keys [name var init env doc export]}] + [{:keys [name var init env doc export test]}] (let [mname (munge name)] (when init (emit-comment doc (:jsdoc init)) @@ -437,10 +437,10 @@ (when-not (= :expr (:context env)) (emitln ";")) (when export (emitln "goog.exportSymbol('" (munge export) "', " mname ");")) - (when (and ana/*load-tests* (:test var)) + (when (and ana/*load-tests* test) (when (= :expr (:context env)) (emitln ";")) - (emitln var ".cljs$lang$test = " (:test var) ";"))))) + (emitln var ".cljs$lang$test = " test ";"))))) (defn emit-apply-to [{:keys [name params env]}] From f07ca8ff30418f4c5da8509b3ee3384944885645 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 12:45:46 -0500 Subject: [PATCH 0347/4033] ignore builds directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 947dd004d..d469f171d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ closure-release-* .lein-repl-history .nrepl-port .nrepl-repl-history +builds \ No newline at end of file From e3d2a462f1286f75b54e5615091ae66c28359319 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 13:17:20 -0500 Subject: [PATCH 0348/4033] Remove `debug-prn` helper from compiler & analyzer, make public and put in cljs.util --- src/clj/cljs/analyzer.clj | 4 ---- src/clj/cljs/compiler.clj | 4 ---- src/clj/cljs/util.clj | 6 +++++- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 3a8e14fc1..542518a51 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -261,10 +261,6 @@ screen location navigator history location global process require module exports)))})) -(defn debug-prn - [& args] - (.println System/err (apply str args))) - (defn source-info ([env] (when-let [line (:line env)] diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 9a8b65a4b..148d09979 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -41,10 +41,6 @@ (def cljs-reserved-file-names #{"deps.cljs"}) -(defmacro ^:private debug-prn - [& args] - `(.println System/err (str ~@args))) - (defn ns-first-segments [] (letfn [(get-first-ns-segment [ns] (first (string/split (str ns) #"\.")))] (map get-first-ns-segment (keys (::ana/namespaces @env/*compiler*))))) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 52dc4965a..7fc1548c7 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -93,4 +93,8 @@ (let [s (cond (file? f) (filename f) (url? f) (path f))] - (last (string/split s #"\.")))) \ No newline at end of file + (last (string/split s #"\.")))) + +(defn debug-prn + [& args] + (.println System/err (apply str args))) \ No newline at end of file From 207c07f3b2ccce9ddab4ca5e75b9530019e45f5a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 13:38:56 -0500 Subject: [PATCH 0349/4033] Add `cljs.util/measure` macro for simple compiler performance information display, instrument `cljs.closure/build`, display perf stats if `:compiler-stats` flag set --- src/clj/cljs/closure.clj | 50 ++++++++++++++++++++++++---------------- src/clj/cljs/util.clj | 15 +++++++++++- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 7cb2549a3..6072b0bb2 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -949,7 +949,8 @@ should contain the source for the given namespace name." (env/default-compiler-env opts)))) ([source opts compiler-env] (env/with-compiler-env compiler-env - (let [ups-deps (get-upstream-deps) + (let [compiler-stats (:compiler-stats opts) + ups-deps (get-upstream-deps) all-opts (-> opts (assoc :ups-libs (:libs ups-deps) @@ -964,10 +965,11 @@ should contain the source for the given namespace name." (check-source-map opts) (check-source-map-path opts) (check-output-wrapper opts) - (swap! compiler-env #(-> % - (assoc-in [:opts :emit-constants] emit-constants) - (assoc :target (:target opts)) - (assoc :js-dependency-index (deps/js-dependency-index all-opts)))) + (swap! compiler-env + #(-> % + (assoc-in [:opts :emit-constants] emit-constants) + (assoc :target (:target opts)) + (assoc :js-dependency-index (deps/js-dependency-index all-opts)))) (binding [ana/*cljs-static-fns* (or (and (= (:optimizations opts) :advanced) (not (false? (:static-fns opts)))) @@ -986,20 +988,26 @@ should contain the source for the given namespace name." :undeclared-ns :undeclared-ns-form] (repeat warnings)) warnings)))] - (let [compiled (-compile source all-opts) - - ; the constants_table.js file is not used directly here, is picked up by - ; add-dependencies below + (let [compiled (util/measure compiler-stats + "Compile basic sources" + (doall (-compile source all-opts))) + ;; the constants_table.js file is not used directly here, is picked up by + ;; add-dependencies below _ (when emit-constants - (comp/emit-constants-table-to-file (::ana/constant-table @env/*compiler*) - (str (util/output-directory all-opts) "/constants_table.js"))) - js-sources (concat - (apply add-dependencies all-opts - (concat (if (coll? compiled) compiled [compiled]) - (when (= :nodejs (:target all-opts)) - [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))) - (when (= :nodejs (:target all-opts)) - [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])) + (comp/emit-constants-table-to-file + (::ana/constant-table @env/*compiler*) + (str (util/output-directory all-opts) "/constants_table.js"))) + js-sources (util/measure compiler-stats + "Add dependencies" + (doall + (concat + (apply add-dependencies all-opts + (concat + (if (coll? compiled) compiled [compiled]) + (when (= :nodejs (:target all-opts)) + [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))) + (when (= :nodejs (:target all-opts)) + [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) optim (:optimizations all-opts) ret (if (and optim (not= optim :none)) (do @@ -1008,8 +1016,10 @@ should contain the source for the given namespace name." (str ":source-map must name a file when using :whitespace, " ":simple, or :advanced optimizations")) (doall (map #(source-on-disk all-opts %) js-sources))) - (->> js-sources - (apply optimize all-opts) + (->> + (util/measure compiler-stats + "Optimize sources" + (apply optimize all-opts js-sources)) (add-wrapper all-opts) (add-source-map-link all-opts) (add-header all-opts) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 7fc1548c7..fc2187c00 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -97,4 +97,17 @@ (defn debug-prn [& args] - (.println System/err (apply str args))) \ No newline at end of file + (.println System/err (apply str args))) + +(defmacro measure + "Like cljs.core/time but toggleable and takes a message string." + {:added "1.0"} + ([msg expr] `(measure true ~msg ~expr)) + ([enable msg expr] + `(if ~enable + (let [start# (. System (nanoTime)) + ret# ~expr] + (debug-prn (str ~msg ", elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs")) + (debug-prn "") + ret#) + ~expr))) From e3acaecc09ae0dd832bbf9ff0fe485cee38d3078 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 13:40:35 -0500 Subject: [PATCH 0350/4033] Change testing scripts so they write to builds directory. Show perf stats in advanced tests. Simple tests use :cache-analysis, the performance difference is clearer with stats on. --- script/test | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/script/test b/script/test index 521630a78..0f7d439e8 100755 --- a/script/test +++ b/script/test @@ -1,21 +1,19 @@ #!/bin/sh -rm -rf out -mkdir -p out +rm -rf builds/out-adv +mkdir -p builds/out-adv possible=4 ran=0 #bin/cljsc test >out/core-test.js -bin/cljsc test "{:optimizations :advanced :output-wrapper true}" >out/core-advanced-test.js +bin/cljsc test "{:optimizations :advanced :output-wrapper true :compiler-stats true}" > builds/out-adv/core-advanced-test.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" else echo "Testing with V8" - "${V8_HOME}/d8" out/core-advanced-test.js - # TODO: figure out path problem when not in advanced mode - # "${V8_HOME}/d8" out/core-test.js + "${V8_HOME}/d8" builds/out-adv/core-advanced-test.js ran=$[ran+1] fi @@ -23,7 +21,7 @@ if [ "$SPIDERMONKEY_HOME" = "" ]; then echo "SPIDERMONKEY_HOME not set, skipping SpiderMonkey tests" else echo "Testing with SpiderMonkey" - ${SPIDERMONKEY_HOME}/js -f out/core-advanced-test.js + ${SPIDERMONKEY_HOME}/js -f builds/out-adv/core-advanced-test.js ran=$[ran+1] fi @@ -31,7 +29,7 @@ if [ "$JSC_HOME" = "" ]; then echo "JSC_HOME not set, skipping JavaScriptCore tests" else echo "Testing with JavaScriptCore" - "${JSC_HOME}/jsc" -f out/core-advanced-test.js + "${JSC_HOME}/jsc" -f builds/out-adv/core-advanced-test.js ran=$[ran+1] fi @@ -39,7 +37,7 @@ if [ "$NASHORN_HOME" = "" ]; then echo "NASHORN_HOME not set, skipping Nashorn tests" else echo "Testing with Nashorn" - "${NASHORN_HOME}/jjs" out/core-advanced-test.js + "${NASHORN_HOME}/jjs" builds/out-adv/core-advanced-test.js ran=$[ran+1] fi From bc4f5c529d4cc822073d738304acf2dc84a82e33 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 13:46:17 -0500 Subject: [PATCH 0351/4033] add missing test-simple shell script --- script/test-simple | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100755 script/test-simple diff --git a/script/test-simple b/script/test-simple new file mode 100755 index 000000000..0a24d29f4 --- /dev/null +++ b/script/test-simple @@ -0,0 +1,46 @@ +#!/bin/sh + +# stop blowing compiled stuff +#rm -rf builds/out-simp +mkdir -p builds/out-simp + +possible=4 +ran=0 + +#bin/cljsc test >out/core-test.js +bin/cljsc test "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :compiler-stats true}" > builds/out-simp/core-simple-test.js + +if [ "$V8_HOME" = "" ]; then + echo "V8_HOME not set, skipping V8 tests" +else + echo "Testing with V8" + "${V8_HOME}/d8" builds/out-simp/core-simple-test.js + ran=$[ran+1] +fi + +if [ "$SPIDERMONKEY_HOME" = "" ]; then + echo "SPIDERMONKEY_HOME not set, skipping SpiderMonkey tests" +else + echo "Testing with SpiderMonkey" + ${SPIDERMONKEY_HOME}/js -f builds/out-simp/core-simple-test.js + ran=$[ran+1] +fi + +# commented out because of memory issue in JSC w/ nested fn calls - David +#if [ "$JSC_HOME" = "" ]; then +# echo "JSC_HOME not set, skipping JavaScriptCore tests" +#else +# echo "Testing with JavaScriptCore" +# "${JSC_HOME}/jsc" -f builds/out-simp/core-simple-test.js +# ran=$[ran+1] +#fi + +if [ "$NASHORN_HOME" = "" ]; then + echo "NASHORN_HOME not set, skipping Nashorn tests" +else + echo "Testing with Nashorn" + "${NASHORN_HOME}/jjs" builds/out-simp/core-simple-test.js + ran=$[ran+1] +fi + +echo "Tested with $ran out of $possible possible js targets" From 148a22787d6c7a1c5944ab4f746955dcdc670ee5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 13:48:45 -0500 Subject: [PATCH 0352/4033] Update benchmark script to write to builds directory, emit compiler stats --- script/benchmark | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/script/benchmark b/script/benchmark index bbff28629..1ba9d863b 100755 --- a/script/benchmark +++ b/script/benchmark @@ -1,37 +1,34 @@ #!/bin/sh -rm -rf out -mkdir -p out +rm -rf builds/out-adv-bench +mkdir -p builds/out-adv-bench -#bin/cljsc benchmark > out/core-benchmark.js -bin/cljsc benchmark "{:optimizations :advanced :output-wrapper true}" >out/core-advanced-benchmark.js +bin/cljsc benchmark "{:optimizations :advanced :output-wrapper true :compiler-stats true}" > builds/out-adv-bench/core-advanced-benchmark.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 benchmarks" else echo "Benchmarking with V8" - "${V8_HOME}/d8" out/core-advanced-benchmark.js - # TODO: figure out path problem when not in advanced mode - # "${V8_HOME}/d8" out/core-benchmark.js + "${V8_HOME}/d8" builds/out-adv-bench/core-advanced-benchmark.js fi if [ "$SPIDERMONKEY_HOME" = "" ]; then echo "SPIDERMONKEY_HOME not set, skipping SpiderMonkey benchmarks" else echo "Benchmarking with SpiderMonkey" - "${SPIDERMONKEY_HOME}/js" -f out/core-advanced-benchmark.js + "${SPIDERMONKEY_HOME}/js" -f builds/out-adv-bench/core-advanced-benchmark.js fi if [ "$JSC_HOME" = "" ]; then echo "JSC_HOME not set, skipping JavaScriptCore benchmarks" else echo "Benchmarking with JavaScriptCore" - "${JSC_HOME}/jsc" -f out/core-advanced-benchmark.js + "${JSC_HOME}/jsc" -f builds/out-adv-bench/core-advanced-benchmark.js fi if [ "$NASHORN_HOME" = "" ]; then echo "NASHORN_HOME not set, skipping Nashorn benchmarks" else echo "Benchmarking with Nashorn" - "${NASHORN_HOME}/jjs" out/core-advanced-benchmark.js + "${NASHORN_HOME}/jjs" builds/out-adv-bench/core-advanced-benchmark.js fi From b7bd58af333ddaaca3cda08284b02d0924d5de52 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 20:42:08 -0500 Subject: [PATCH 0353/4033] In any mode other than :advanced wrap namespace provides in a conditional. This is to support REPL based interaction where a user might use 'in-ns followed by an ns declaration - without the conditional Google Closure will annoyingly deliver a pointless error --- src/clj/cljs/closure.clj | 3 ++- src/clj/cljs/compiler.clj | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 6072b0bb2..497ab0f88 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -987,7 +987,8 @@ should contain the source for the given namespace name." [:unprovided :undeclared-var :undeclared-ns :undeclared-ns-form] (repeat warnings)) - warnings)))] + warnings))) + comp/*build-options* opts] (let [compiled (util/measure compiler-stats "Compile basic sources" (doall (-compile source all-opts))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 148d09979..9908f6fea 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -38,6 +38,9 @@ (def ^:dynamic *source-map-data* nil) (def ^:dynamic *lexical-renames* {}) +;; NOTE: explicitly threading opts as cljs.analyzer is considerably more +;; invasive given the current approach to emission - David +(def ^:dynamic *build-options* nil) (def cljs-reserved-file-names #{"deps.cljs"}) @@ -785,7 +788,11 @@ (defmethod emit* :ns [{:keys [name requires uses require-macros env]}] + (when-not (= (:optimizations *build-options*) :advanced) + (emitln "if(!goog.isProvided_('" (munge name) "')) {")) (emitln "goog.provide('" (munge name) "');") + (when-not (= (:optimizations *build-options*) :advanced) + (emitln "}")) (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');")) (doseq [lib (distinct (concat (vals requires) (vals uses)))] From 857c8d6a1ba0d93d261d39cbea232d0e7691e870 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 21:43:08 -0500 Subject: [PATCH 0354/4033] change bootstrap script so we get the same version of the Closure Compiler declared in the POM --- script/bootstrap | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index 77e94e4c4..20a271377 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,6 +3,7 @@ set -e CLOJURE_RELEASE="1.6.0" +CLOSURE_RELEASE="v20140625" DJSON_RELEASE="0.2.3" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R3" @@ -65,10 +66,7 @@ cd .. echo "Fetching Google Closure compiler..." mkdir -p compiler cd compiler -curl --retry 3 -O -s https://dl.google.com/closure-compiler/compiler-latest.zip || { echo "Download failed."; exit 1; } -unzip -qu compiler-latest.zip -echo "Cleaning up Google Closure compiler archive..." -rm compiler-latest.zip +curl --retry 3 -O -s https://repo1.maven.org/maven2/com/google/javascript/closure-compiler/$CLOSURE_RELEASE/closure-compiler-$CLOSURE_RELEASE.jar || { echo "Download failed."; exit 1; } cd ../.. if [ "$1" = "--closure-library-head" ] ; then @@ -88,7 +86,7 @@ echo "Cleaning up Rhino archive..." rm rhino$RHINO_RELEASE.zip echo "Copying closure/compiler/compiler.jar to lib/compiler.jar" -cp closure/compiler/compiler.jar lib +cp closure/compiler/closure-compiler-$CLOSURE_RELEASE.jar lib echo "Fetching tools.reader $TREADER_RELEASE ..." curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar || { echo "Download failed."; exit 1; } From bff652f5c187ee2f1555ba385091cd4932cab24a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Dec 2014 22:48:06 -0500 Subject: [PATCH 0355/4033] actually fix bootstrapping of Closure Compiler --- script/bootstrap | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index 20a271377..6904146b8 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.6.0" -CLOSURE_RELEASE="v20140625" +CLOSURE_RELEASE="20140625" DJSON_RELEASE="0.2.3" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R3" @@ -66,7 +66,10 @@ cd .. echo "Fetching Google Closure compiler..." mkdir -p compiler cd compiler -curl --retry 3 -O -s https://repo1.maven.org/maven2/com/google/javascript/closure-compiler/$CLOSURE_RELEASE/closure-compiler-$CLOSURE_RELEASE.jar || { echo "Download failed."; exit 1; } +curl --retry 3 -O -s http://dl.google.com/closure-compiler/compiler-$CLOSURE_RELEASE.zip || { echo "Download failed."; exit 1; } +unzip -qu compiler-$CLOSURE_RELEASE.zip +echo "Cleaning up Google Closure compiler archive..." +rm compiler-$CLOSURE_RELEASE.zip cd ../.. if [ "$1" = "--closure-library-head" ] ; then @@ -86,7 +89,7 @@ echo "Cleaning up Rhino archive..." rm rhino$RHINO_RELEASE.zip echo "Copying closure/compiler/compiler.jar to lib/compiler.jar" -cp closure/compiler/closure-compiler-$CLOSURE_RELEASE.jar lib +cp closure/compiler/compiler.jar lib echo "Fetching tools.reader $TREADER_RELEASE ..." curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar || { echo "Download failed."; exit 1; } From 96d3112fe49fd08aa670130fc952c11cf3dca23d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Dec 2014 11:50:07 -0500 Subject: [PATCH 0356/4033] only conditionalize provides under :none, when compiled Google Closure eliminates goog.isProvided_ --- src/clj/cljs/compiler.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 9908f6fea..f0665cfd1 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -788,10 +788,10 @@ (defmethod emit* :ns [{:keys [name requires uses require-macros env]}] - (when-not (= (:optimizations *build-options*) :advanced) + (when (= (:optimizations *build-options*) :none) (emitln "if(!goog.isProvided_('" (munge name) "')) {")) (emitln "goog.provide('" (munge name) "');") - (when-not (= (:optimizations *build-options*) :advanced) + (when (= (:optimizations *build-options*) :none) (emitln "}")) (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');")) From c1be5771bef0c0dd1813bb97bf6cca54641e5a93 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 15:03:28 -0500 Subject: [PATCH 0357/4033] pass command line arguments to script/repl --- script/repl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/repl b/script/repl index ff078dd67..85df893f1 100755 --- a/script/repl +++ b/script/repl @@ -9,5 +9,5 @@ for next in lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done -java -server -cp "$CLJSC_CP" clojure.main +java -server -cp "$CLJSC_CP" clojure.main "$@" From 158f40c775c89a8fe7b3478a0b0f75ef520e3350 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 15:04:58 -0500 Subject: [PATCH 0358/4033] add self-compile script --- script/self-compile.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 script/self-compile.sh diff --git a/script/self-compile.sh b/script/self-compile.sh new file mode 100644 index 000000000..1473cebaa --- /dev/null +++ b/script/self-compile.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +rm -rf classes +repl -e \ No newline at end of file From 7358c7299d2154202e6f590df3912fe85cb0473a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 15:08:51 -0500 Subject: [PATCH 0359/4033] include classes dir in the classpath for script/repl and script/repljs --- script/repl | 2 +- script/repljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/repl b/script/repl index 85df893f1..dbe548530 100755 --- a/script/repl +++ b/script/repl @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do +for next in classes: lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done diff --git a/script/repljs b/script/repljs index 3bd8fd450..5b2511302 100755 --- a/script/repljs +++ b/script/repljs @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do +for next in classes: lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done From 507dfe268559d56d964e91a84064889643e94906 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 15:25:35 -0500 Subject: [PATCH 0360/4033] update IntelliJ file --- Clojurescript.iml | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index d5c074327..7ec55c132 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -1,12 +1,32 @@ - + + + - + + + + + - + + + + + + + + + + + + + + + + - - + \ No newline at end of file From 8a81d7cc69d6849ea0a5b28de334df1a669a6f26 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 15:27:05 -0500 Subject: [PATCH 0361/4033] Add self-compile.sh helper script --- script/self-compile.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) mode change 100644 => 100755 script/self-compile.sh diff --git a/script/self-compile.sh b/script/self-compile.sh old mode 100644 new mode 100755 index 1473cebaa..f552127b5 --- a/script/self-compile.sh +++ b/script/self-compile.sh @@ -1,4 +1,5 @@ #!/bin/bash rm -rf classes -repl -e \ No newline at end of file +mkdir classes +repl -e "(compile 'cljs.repl) (compile 'cljs.core)" \ No newline at end of file From 58dcc5fd8542bb1b1ac8cc0996c6fb6e2ed7b100 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Dec 2014 00:56:21 -0500 Subject: [PATCH 0362/4033] wip (do not use) --- script/repljs | 2 +- src/clj/cljs/repl.clj | 163 ++++++++++++++++++++---------------- src/clj/cljs/repl/rhino.clj | 55 +++++++----- 3 files changed, 128 insertions(+), 92 deletions(-) diff --git a/script/repljs b/script/repljs index 5b2511302..e2d7a1d67 100755 --- a/script/repljs +++ b/script/repljs @@ -12,4 +12,4 @@ done java -server -cp "$CLJSC_CP" clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.rhino :as rhino]) -(repl/repl (rhino/repl-env) :warn-on-undeclared true)" +(repl/repl* (rhino/repl-env) {:warn-on-undeclared true :output-dir \".repl\" :cache-analysis true})" diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 0b546f998..7c3cc3dc1 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -10,8 +10,7 @@ (:refer-clojure :exclude [load-file]) (:import java.io.File javax.xml.bind.DatatypeConverter) - (:require [clojure.string :as string] - [clojure.java.io :as io] + (:require [clojure.java.io :as io] [cljs.compiler :as comp] [cljs.analyzer :as ana] [cljs.env :as env] @@ -24,7 +23,7 @@ (def ^:dynamic *cljs-verbose* false) (defprotocol IJavaScriptEnv - (-setup [this] "initialize the environment") + (-setup [this] [this opts] "initialize the environment") (-evaluate [this filename line js] "evaluate a javascript string") (-load [this ns url] "load code at url into the environment") (-tear-down [this] "dispose of the environment")) @@ -42,23 +41,28 @@ "Load a namespace and all of its dependencies into the evaluation environment. The environment is responsible for ensuring that each namespace is loaded once and only once." - [repl-env sym] - (let [sym (if (and (seq? sym) - (= (first sym) 'quote)) - (second sym) - sym) - deps (->> (cljsc/add-dependencies (env->opts repl-env) - {:requires [(name sym)] :type :seed}) - (remove (comp #{["goog"]} :provides)) - (remove (comp #{:seed} :type)) - (map #(select-keys % [:provides :url])))] - (doseq [{:keys [url provides]} deps] - (-load repl-env provides url)))) + ([repl-env sym] (load-namespace repl-env sym nil)) + ([repl-env sym opts] + (let [sym (if (and (seq? sym) + (= (first sym) 'quote)) + (second sym) + sym) + deps (->> (cljsc/add-dependencies + ;; TODO: conceptually simpler to keep REPL environment + ;; separate from build options map - David + (merge (env->opts repl-env) opts) + {:requires [(name sym)] :type :seed}) + (remove (comp #{["goog"]} :provides)) + (remove (comp #{:seed} :type)) + (map #(select-keys % [:provides :url])))] + (doseq [{:keys [url provides]} deps] + (-load repl-env provides url))))) (defn- load-dependencies - [repl-env requires] - (doseq [ns requires] - (load-namespace repl-env ns))) + ([repl-env requires] (load-dependencies repl-env requires nil)) + ([repl-env requires opts] + (doseq [ns requires] + (load-namespace repl-env ns opts)))) (defn- display-error ([ret form] @@ -75,54 +79,59 @@ string which is the ClojureScript return value. This string may or may not be readable by the Clojure reader." ([repl-env env filename form] - (evaluate-form repl-env env filename form identity)) + (evaluate-form repl-env env filename form identity)) ([repl-env env filename form wrap] - (try - (binding [ana/*cljs-file* filename] - (let [ast (ana/analyze env form) - js (comp/emit-str ast) - wrap-js - (if (:source-map repl-env) - (binding [comp/*source-map-data* - (atom {:source-map (sorted-map) - :gen-col 0 - :gen-line 0})] - (let [js (comp/emit-str (ana/no-warn (ana/analyze env (wrap form)))) - t (System/currentTimeMillis)] - (str js - "\n//# sourceURL=repl-" t ".js" - "\n//# sourceMappingURL=data:application/json;base64," - (DatatypeConverter/printBase64Binary - (.getBytes - (sm/encode - {(str "repl-" t ".cljs") - (:source-map @comp/*source-map-data*)} - {:lines (+ (:gen-line @comp/*source-map-data*) 3) - :file (str "repl-" t ".js") - :sources-content - [(or (:source (meta form)) - ;; handle strings / primitives without metadata - (with-out-str (pr form)))]}) - "UTF-8"))))) - (comp/emit-str (ana/no-warn (ana/analyze env (wrap form)))))] - (when (= (:op ast) :ns) - (load-dependencies repl-env (into (vals (:requires ast)) - (distinct (vals (:uses ast)))))) - (when *cljs-verbose* - (print js)) - (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] - (case (:status ret) - ;;we eat ns errors because we know goog.provide() will throw when reloaded - ;;TODO - file bug with google, this is bs error - ;;this is what you get when you try to 'teach new developers' - ;;via errors (goog/base.js 104) - :error (display-error ret form) - :exception (display-error ret form - #(prn "Error evaluating:" form :as js)) - :success (:value ret))))) - (catch Throwable ex - (.printStackTrace ex) - (println (str ex)))))) + (evaluate-form repl-env env filename form wrap nil)) + ([repl-env env filename form wrap opts] + (try + (binding [ana/*cljs-file* filename] + (let [ast (ana/analyze env form opts) + js (comp/emit-str ast) + wrap-js + ;; TODO: check opts as well - David + (if (:source-map repl-env) + (binding [comp/*source-map-data* + (atom {:source-map (sorted-map) + :gen-col 0 + :gen-line 0})] + (let [js (comp/emit-str (ana/no-warn (ana/analyze env (wrap form) opts))) + t (System/currentTimeMillis)] + (str js + "\n//# sourceURL=repl-" t ".js" + "\n//# sourceMappingURL=data:application/json;base64," + (DatatypeConverter/printBase64Binary + (.getBytes + (sm/encode + {(str "repl-" t ".cljs") + (:source-map @comp/*source-map-data*)} + {:lines (+ (:gen-line @comp/*source-map-data*) 3) + :file (str "repl-" t ".js") + :sources-content + [(or (:source (meta form)) + ;; handle strings / primitives without metadata + (with-out-str (pr form)))]}) + "UTF-8"))))) + (comp/emit-str (ana/no-warn (ana/analyze env (wrap form) opts))))] + (when (= (:op ast) :ns) + (load-dependencies repl-env + (into (vals (:requires ast)) + (distinct (vals (:uses ast)))) + opts)) + (when *cljs-verbose* + (print js)) + (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] + (case (:status ret) + ;;we eat ns errors because we know goog.provide() will throw when reloaded + ;;TODO - file bug with google, this is bs error + ;;this is what you get when you try to 'teach new developers' + ;;via errors (goog/base.js 104) + :error (display-error ret form) + :exception (display-error ret form + #(prn "Error evaluating:" form :as js)) + :success (:value ret))))) + (catch Throwable ex + (.printStackTrace ex) + (println (str ex)))))) (defn load-stream [repl-env filename res] (let [env (ana/empty-env)] @@ -133,10 +142,15 @@ (defn load-file ([repl-env f] (load-file repl-env f nil)) ([repl-env f opts] - (binding [ana/*cljs-ns* 'cljs.user] - (let [res (if (= \/ (first f)) f (io/resource f))] - (assert res (str "Can't find " f " in classpath")) - (load-stream repl-env f res))))) + (if (:output-dir opts) + (let [src (io/resource f)] + (comp/compile-file src + (cljsc/src-file->target-file src opts) opts) + (-evaluate repl-env f 0 (cljsc/src-file->goog-require src))) + (binding [ana/*cljs-ns* 'cljs.user] + (let [res (if (= \/ (first f)) f (io/resource f))] + (assert res (str "Can't find " f " in classpath")) + (load-stream repl-env f res)))))) (defn- wrap-fn [form] (cond (and (seq? form) (= 'ns (first form))) identity @@ -178,7 +192,7 @@ (fn self ([repl-env ns] (self repl-env ns nil)) ([repl-env ns opts] - (load-namespace repl-env ns)))})) + (load-namespace repl-env ns opts)))})) (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate @@ -204,13 +218,17 @@ :undeclared-ns warn-on-undeclared :undeclared-ns-form warn-on-undeclared) ana/*cljs-static-fns* static-fns] + ;; TODO: the follow should become dead code when the REPL is + ;; sufficiently enhanced to understand :cache-analysis - David (when analyze-path (analyze-source analyze-path)) (let [env {:context :expr :locals {}} special-fns (merge default-special-fns special-fns) is-special-fn? (set (keys special-fns)) read-error (Object.)] - (-setup repl-env) + (if-not (nil? opts) + (-setup repl-env opts) + (-setup repl-env)) (loop [] (print (str "ClojureScript:" ana/*cljs-ns* "> ")) (flush) @@ -234,7 +252,8 @@ (= form :cljs/quit) :quit (and (seq? form) (is-special-fn? (first form))) - (do (apply (get special-fns (first form)) repl-env (rest form)) + (do (apply (get special-fns (first form)) repl-env (rest form) + opts) (newline) (recur)) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index f605e0453..eb8dfa3c2 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -11,8 +11,10 @@ [clojure.java.io :as io] [cljs.compiler :as comp] [cljs.analyzer :as ana] - [cljs.repl :as repl]) - (:import cljs.repl.IJavaScriptEnv + [cljs.repl :as repl] + [cljs.util :as util]) + (:import java.io.File + cljs.repl.IJavaScriptEnv [org.mozilla.javascript Context ScriptableObject])) (def current-repl-env (atom nil)) @@ -20,7 +22,7 @@ ;;todo - move to core.cljs, using js (def ^String bootjs (str "goog.require = function(rule){" "Packages.clojure.lang.RT[\"var\"](\"cljs.repl.rhino\",\"goog-require\")" - ".invoke(___repl_env, rule);}")) + ".invoke(___repl_env, __repl_opts, rule);}")) (defprotocol IEval (-eval [this env filename line])) @@ -33,6 +35,7 @@ java.io.Reader (-eval [this {:keys [cx scope]} filename line] + (.setOptimizationLevel ^Context cx -1) (.evaluateReader cx scope this filename line nil)) ) @@ -64,23 +67,31 @@ :value (.toString ex) :stacktrace (stacktrace ex)}))) -(defn goog-require [repl-env rule] +(defn goog-require [repl-env opts rule] (when-not (contains? @(:loaded-libs repl-env) rule) (let [repl-env @current-repl-env path (string/replace (comp/munge rule) \. java.io.File/separatorChar) + cljsc-path (str (util/output-directory opts) + File/separator (str path ".js")) cljs-path (str path ".cljs") js-path (str "goog/" - (-eval (str "goog.dependencies_.nameToPath['" rule "']") - repl-env - "" - 1))] - (if-let [res (io/resource cljs-path)] - (binding [ana/*cljs-ns* 'cljs.user] - (repl/load-stream repl-env cljs-path res)) - (if-let [res (io/resource js-path)] - (with-open [reader (io/reader res)] - (-eval reader repl-env js-path 1)) - (throw (Exception. (str "Cannot find " cljs-path " or " js-path " in classpath"))))) + (-eval (str "goog.dependencies_.nameToPath['" rule "']") + repl-env + "" + 1))] + (let [compiled (io/file cljsc-path)] + (if (.exists compiled) + ;; TODO: only take this path if analysis cache is available + ;; - David + (with-open [reader (io/reader compiled)] + (-eval reader repl-env cljsc-path 1)) + (if-let [res (io/resource cljs-path)] + (binding [ana/*cljs-ns* 'cljs.user] + (repl/load-stream repl-env cljs-path res)) + (if-let [res (io/resource js-path)] + (with-open [reader (io/reader res)] + (-eval reader repl-env js-path 1)) + (throw (Exception. (str "Cannot find " cljs-path " or " js-path " in classpath"))))))) (swap! (:loaded-libs repl-env) conj rule)))) (defn load-javascript [repl-env ns url] @@ -98,6 +109,9 @@ ([repl-env opts] (let [env (ana/empty-env) scope (:scope repl-env)] + (ScriptableObject/putProperty scope + "__repl_opts" + (Context/javaToJS opts scope)) (repl/load-file repl-env "cljs/core.cljs" opts) (swap! (:loaded-libs repl-env) conj "cljs.core") (repl/evaluate-form repl-env @@ -107,15 +121,18 @@ (ScriptableObject/putProperty scope "out" (Context/javaToJS *out* scope)) - (repl/evaluate-form repl-env - env - "" - '(set! *print-fn* (fn [x] (.write js/out x))))))) + (binding [ana/*cljs-ns* 'cljs.core] + (repl/evaluate-form repl-env + env + "" + '(set! *print-fn* (fn [x] (.write js/out x)))))))) (defrecord RhinoEnv [loaded-libs] repl/IJavaScriptEnv (-setup [this] (rhino-setup this)) + (-setup [this opts] + (rhino-setup this opts)) (-evaluate [this filename line js] (rhino-eval this filename line js)) (-load [this ns url] From ea768c22e3573431f29468a9eff773e8f1d28990 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 18:16:09 -0500 Subject: [PATCH 0363/4033] reformat cljs.rhino.repl, prep for changes --- src/clj/cljs/repl/rhino.clj | 90 ++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index eb8dfa3c2..bd5df4689 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -13,38 +13,47 @@ [cljs.analyzer :as ana] [cljs.repl :as repl] [cljs.util :as util]) - (:import java.io.File - cljs.repl.IJavaScriptEnv - [org.mozilla.javascript Context ScriptableObject])) + (:import cljs.repl.IJavaScriptEnv + [java.io File Reader] + [org.mozilla.javascript Context ScriptableObject + RhinoException Undefined])) (def current-repl-env (atom nil)) ;;todo - move to core.cljs, using js -(def ^String bootjs (str "goog.require = function(rule){" - "Packages.clojure.lang.RT[\"var\"](\"cljs.repl.rhino\",\"goog-require\")" - ".invoke(___repl_env, __repl_opts, rule);}")) +(def ^String bootjs + (str "goog.require = function(rule){" + "Packages.clojure.lang.RT[\"var\"](\"cljs.repl.rhino\",\"goog-require\")" + ".invoke(___repl_env, __repl_opts, rule);}")) + +;; ============================================================================= +;; Protocols (defprotocol IEval (-eval [this env filename line])) (extend-protocol IEval - - java.lang.String + String (-eval [this {:keys [cx scope]} filename line] (.evaluateString cx scope this filename line nil)) - java.io.Reader + Reader (-eval [this {:keys [cx scope]} filename line] (.setOptimizationLevel ^Context cx -1) - (.evaluateReader cx scope this filename line nil)) - ) + (.evaluateReader cx scope this filename line nil))) + +;; ============================================================================= +;; Stacktrace & eval support (defmulti stacktrace class) (defmethod stacktrace :default [e] - (apply str (interpose "\n" (map #(str " " (.toString %)) (.getStackTrace e))))) + (apply str + (interpose "\n" + (map #(str " " (.toString %)) + (.getStackTrace e))))) -(defmethod stacktrace org.mozilla.javascript.RhinoException [e] +(defmethod stacktrace RhinoException [^RhinoException e] (.getScriptStackTrace e)) (defmulti eval-result class) @@ -54,7 +63,9 @@ (defmethod eval-result nil [_] "") -(defmethod eval-result org.mozilla.javascript.Undefined [_] "") +(defmethod eval-result Undefined [_] "") + +;; ============================================================================= (defn rhino-eval [repl-env filename line js] @@ -69,16 +80,14 @@ (defn goog-require [repl-env opts rule] (when-not (contains? @(:loaded-libs repl-env) rule) - (let [repl-env @current-repl-env - path (string/replace (comp/munge rule) \. java.io.File/separatorChar) + (let [repl-env @current-repl-env + path (string/replace (comp/munge rule) \. File/separatorChar) cljsc-path (str (util/output-directory opts) File/separator (str path ".js")) - cljs-path (str path ".cljs") - js-path (str "goog/" - (-eval (str "goog.dependencies_.nameToPath['" rule "']") - repl-env - "" - 1))] + cljs-path (str path ".cljs") + js-path (str "goog/" + (-eval (str "goog.dependencies_.nameToPath['" rule "']") + repl-env "" 1))] (let [compiled (io/file cljsc-path)] (if (.exists compiled) ;; TODO: only take this path if analysis cache is available @@ -91,40 +100,38 @@ (if-let [res (io/resource js-path)] (with-open [reader (io/reader res)] (-eval reader repl-env js-path 1)) - (throw (Exception. (str "Cannot find " cljs-path " or " js-path " in classpath"))))))) + (throw + (Exception. + (str "Cannot find " cljs-path + " or " js-path " in classpath"))))))) (swap! (:loaded-libs repl-env) conj rule)))) (defn load-javascript [repl-env ns url] (let [missing (remove #(contains? @(:loaded-libs repl-env) %) ns)] (when (seq missing) - (do (try - (with-open [reader (io/reader url)] - (-eval reader repl-env (.toString url) 1)) - ;; TODO: don't show errors for goog/base.js line number 105 - (catch Throwable ex (println (.getMessage ex)))) - (swap! (:loaded-libs repl-env) (partial apply conj) missing))))) + (do + (try + (with-open [reader (io/reader url)] + (-eval reader repl-env (.toString url) 1)) + ;; TODO: don't show errors for goog/base.js line number 105 + (catch Throwable ex (println (.getMessage ex)))) + (swap! (:loaded-libs repl-env) (partial apply conj) missing))))) (defn rhino-setup ([repl-env] (rhino-setup repl-env nil)) ([repl-env opts] - (let [env (ana/empty-env) + (let [env (ana/empty-env) scope (:scope repl-env)] - (ScriptableObject/putProperty scope - "__repl_opts" + (ScriptableObject/putProperty scope "__repl_opts" (Context/javaToJS opts scope)) (repl/load-file repl-env "cljs/core.cljs" opts) (swap! (:loaded-libs repl-env) conj "cljs.core") - (repl/evaluate-form repl-env - env - "" + (repl/evaluate-form repl-env env "" '(ns cljs.user)) (ScriptableObject/putProperty scope - "out" - (Context/javaToJS *out* scope)) + "out" (Context/javaToJS *out* scope)) (binding [ana/*cljs-ns* 'cljs.core] - (repl/evaluate-form repl-env - env - "" + (repl/evaluate-form repl-env env "" '(set! *print-fn* (fn [x] (.write js/out x)))))))) (defrecord RhinoEnv [loaded-libs] @@ -152,8 +159,7 @@ (assert deps "Can't find goog/deps.js in classpath") (swap! current-repl-env (fn [old] new-repl-env)) (ScriptableObject/putProperty scope - "___repl_env" - (Context/javaToJS new-repl-env scope)) + "___repl_env" (Context/javaToJS new-repl-env scope)) (with-open [r (io/reader base)] (-eval r new-repl-env "goog/base.js" 1)) (-eval bootjs new-repl-env "bootjs" 1) From ab72c79f02a43670de63a0eea66c6baebc354e92 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 19:14:34 -0500 Subject: [PATCH 0364/4033] Fix cljs.rhino.repl/goog-require Account for changes to how libraries are loaded. When requiring Google Closure Library files must account for the fact they may provide *multiple* namespaces. In this case we need to add each namespace as a rule to :loaded-libs in the repl environment --- src/clj/cljs/build/api.clj | 5 ++-- src/clj/cljs/closure.clj | 25 ++++++++++------ src/clj/cljs/repl/rhino.clj | 59 +++++++++++++++++++++---------------- 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/clj/cljs/build/api.clj b/src/clj/cljs/build/api.clj index 44c178091..f025ad444 100644 --- a/src/clj/cljs/build/api.clj +++ b/src/clj/cljs/build/api.clj @@ -86,8 +86,9 @@ (defn ^String src-file->goog-require "Given a ClojureScript or Google Closure style JavaScript source file return the goog.require statement for it." - [src] - (closure/src-file->goog-require src)) + ([src] (closure/src-file->goog-require src)) + ([src options] + (closure/src-file->goog-require src options))) (comment diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 497ab0f88..681093349 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1053,15 +1053,22 @@ should contain the source for the given namespace name." (util/output-directory opts) (ana/parse-ns src)))) -(defn ^String src-file->goog-require [src] - (let [goog-ns - (case (util/ext src) - "cljs" (comp/munge (:ns (ana/parse-ns src))) - "js" (-> (parse-js-ns src) :provides first) - (throw - (IllegalArgumentException. - (str "Can't create goog.require expression for " src))))] - (str "goog.require(\"" goog-ns "\");"))) +(defn ^String src-file->goog-require + ([src] (src-file->goog-require src {:wrap true})) + ([src {:keys [wrap all-provides :as options]}] + (let [goog-ns + (case (util/ext src) + "cljs" (comp/munge (:ns (ana/parse-ns src))) + "js" (cond-> (:provides (parse-js-ns src)) + (not all-provides) first) + (throw + (IllegalArgumentException. + (str "Can't create goog.require expression for " src))))] + (if (and (not all-provides) wrap) + (str "goog.require(\"" goog-ns "\");") + (if (vector? goog-ns) + goog-ns + (str goog-ns)))))) (comment diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index bd5df4689..de124668c 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -10,6 +10,7 @@ (:require [clojure.string :as string] [clojure.java.io :as io] [cljs.compiler :as comp] + [cljs.closure :as closure] [cljs.analyzer :as ana] [cljs.repl :as repl] [cljs.util :as util]) @@ -79,32 +80,38 @@ :stacktrace (stacktrace ex)}))) (defn goog-require [repl-env opts rule] - (when-not (contains? @(:loaded-libs repl-env) rule) - (let [repl-env @current-repl-env - path (string/replace (comp/munge rule) \. File/separatorChar) - cljsc-path (str (util/output-directory opts) - File/separator (str path ".js")) - cljs-path (str path ".cljs") - js-path (str "goog/" - (-eval (str "goog.dependencies_.nameToPath['" rule "']") - repl-env "" 1))] - (let [compiled (io/file cljsc-path)] - (if (.exists compiled) - ;; TODO: only take this path if analysis cache is available - ;; - David - (with-open [reader (io/reader compiled)] - (-eval reader repl-env cljsc-path 1)) - (if-let [res (io/resource cljs-path)] - (binding [ana/*cljs-ns* 'cljs.user] - (repl/load-stream repl-env cljs-path res)) - (if-let [res (io/resource js-path)] - (with-open [reader (io/reader res)] - (-eval reader repl-env js-path 1)) - (throw - (Exception. - (str "Cannot find " cljs-path - " or " js-path " in classpath"))))))) - (swap! (:loaded-libs repl-env) conj rule)))) + (let [loaded-libs (:loaded-libs repl-env)] + (when-not (contains? @loaded-libs rule) + (let [repl-env @current-repl-env + path (string/replace (comp/munge rule) \. File/separatorChar) + cljsc-path (str (util/output-directory opts) + File/separator (str path ".js")) + cljs-path (str path ".cljs") + js-path (str "goog/" + (-eval (str "goog.dependencies_.nameToPath['" rule "']") + repl-env "" 1))] + (let [compiled (io/file cljsc-path)] + (if (.exists compiled) + ;; TODO: only take this path if analysis cache is available + ;; - David + (do + (with-open [reader (io/reader compiled)] + (-eval reader repl-env cljsc-path 1)) + (swap! loaded-libs conj rule)) + (if-let [res (io/resource cljs-path)] + (binding [ana/*cljs-ns* 'cljs.user] + (repl/load-stream repl-env cljs-path res) + (swap! loaded-libs conj rule)) + (if-let [res (io/resource js-path)] + (with-open [reader (io/reader res)] + (-eval reader repl-env js-path 1) + (doseq [rule (closure/src-file->goog-require + res {:all-provides true})] + (swap! loaded-libs conj rule))) + (throw + (Exception. + (str "Cannot find " cljs-path + " or " js-path " in classpath"))))))))))) (defn load-javascript [repl-env ns url] (let [missing (remove #(contains? @(:loaded-libs repl-env) %) ns)] From c8a5de06dd3caccb5478091c3c35cbf41e339f04 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 19:35:03 -0500 Subject: [PATCH 0365/4033] clean up special-fns handling in cljs.repl --- src/clj/cljs/repl.clj | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 7c3cc3dc1..92c8295aa 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -18,7 +18,8 @@ [cljs.closure :as cljsc] [cljs.source-map :as sm] [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers])) + [clojure.tools.reader.reader-types :as readers] + [cljs.util :as util])) (def ^:dynamic *cljs-verbose* false) @@ -173,25 +174,25 @@ (def default-special-fns (let [load-file-fn (fn self - ([repl-env file] - (self repl-env file nil)) - ([repl-env file opts] + ([repl-env form] + (self repl-env form nil)) + ([repl-env [_ file :as form] opts] (load-file repl-env file opts)))] {'in-ns (fn self - ([_ quoted-ns] - (self _ quoted-ns nil)) - ([_ quoted-ns opts] - (let [ns-name (second quoted-ns)] - (when-not (ana/get-namespace ns-name) - (swap! env/*compiler* update-in [::ana/namespaces ns-name] {:name ns-name})) - (set! ana/*cljs-ns* ns-name)))) + ([_ form] + (self _ form nil)) + ([_ [_ [quote ns-name] :as form] _] + (util/debug-prn "in-ns " ns-name) + (when-not (ana/get-namespace ns-name) + (swap! env/*compiler* update-in [::ana/namespaces ns-name] {:name ns-name})) + (set! ana/*cljs-ns* ns-name))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace (fn self - ([repl-env ns] (self repl-env ns nil)) - ([repl-env ns opts] + ([repl-env form] (self repl-env form nil)) + ([repl-env [_ ns :as form] opts] (load-namespace repl-env ns opts)))})) (defn analyze-source @@ -247,15 +248,17 @@ (catch Exception e (println (.getMessage e)) read-error))] + ;; TODO: need to catch errors here too - David (cond (identical? form read-error) (recur) (= form :cljs/quit) :quit (and (seq? form) (is-special-fn? (first form))) - (do (apply (get special-fns (first form)) repl-env (rest form) - opts) - (newline) - (recur)) + (do + ((get special-fns (first form)) + repl-env form opts) + (newline) + (recur)) :else (do (eval-and-print repl-env env form) From 8dd9c9e908a5fd6a730d72b5a0357741d5c1de49 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 19:55:35 -0500 Subject: [PATCH 0366/4033] update-in -> assoc-in in 'in-ns REPL special fn --- src/clj/cljs/repl.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 92c8295aa..7f499e682 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -183,9 +183,8 @@ ([_ form] (self _ form nil)) ([_ [_ [quote ns-name] :as form] _] - (util/debug-prn "in-ns " ns-name) (when-not (ana/get-namespace ns-name) - (swap! env/*compiler* update-in [::ana/namespaces ns-name] {:name ns-name})) + (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name})) (set! ana/*cljs-ns* ns-name))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn From bbf05d31359a036755763d56dd4279265fc2a2ca Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 20:50:51 -0500 Subject: [PATCH 0367/4033] In 'in-ns special fn emit a goog.provide if the namespace doesn't already exist --- src/clj/cljs/repl.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 7f499e682..66132acc3 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -180,11 +180,13 @@ (load-file repl-env file opts)))] {'in-ns (fn self - ([_ form] - (self _ form nil)) - ([_ [_ [quote ns-name] :as form] _] + ([repl-env form] + (self repl-env form nil)) + ([repl-env [_ [quote ns-name] :as form] _] (when-not (ana/get-namespace ns-name) - (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name})) + (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) + (-evaluate repl-env "" 1 + (str "goog.provide('" (comp/munge ns-name) "');"))) (set! ana/*cljs-ns* ns-name))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn From cb157df12fbbada3b2ee05250ec233555b11fb50 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Dec 2014 20:56:00 -0500 Subject: [PATCH 0368/4033] load cljs.repl/doc into all REPLs --- src/clj/cljs/repl.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 66132acc3..31e3e787b 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -231,6 +231,11 @@ (if-not (nil? opts) (-setup repl-env opts) (-setup repl-env)) + (evaluate-form repl-env env "" + (with-meta + '(ns cljs.user + (:require [cljs.repl :refer-macros [doc]])) + {:line 1 :column 1})) (loop [] (print (str "ClojureScript:" ana/*cljs-ns* "> ")) (flush) From b62ab70a6a7503668a7c759fd702395d638feea8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 17:35:32 -0500 Subject: [PATCH 0369/4033] Change browser REPL so that compilation of the client.js file is synchronous. Add logging of initialization states. formatting changes to cljs.repl.browser and cljs.repl.server --- src/clj/cljs/repl/browser.clj | 199 +++++++++++++++++++--------------- src/clj/cljs/repl/server.clj | 135 ++++++++++++----------- 2 files changed, 187 insertions(+), 147 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index d4662e0a4..f6f0bcdb7 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -12,16 +12,19 @@ [clojure.string :as string] [cljs.compiler :as comp] [cljs.util :as util] + [cljs.env :as env] [cljs.closure :as cljsc] [cljs.repl :as repl] [cljs.repl.server :as server]) (:import cljs.repl.IJavaScriptEnv [java.util.regex Pattern])) -(defonce browser-state (atom {:return-value-fn nil - :client-js nil})) +(defonce browser-state + (atom {:return-value-fn nil + :client-js nil})) (def loaded-libs (atom #{})) + (def preloaded-libs (atom #{})) (defn- set-return-value-fn @@ -35,10 +38,10 @@ browser for evaluation. The return value function will be called when the return value is received." ([form return-value-fn] - (send-for-eval @(server/connection) form return-value-fn)) + (send-for-eval @(server/connection) form return-value-fn)) ([conn form return-value-fn] - (do (set-return-value-fn return-value-fn) - (server/send-and-close conn 200 form "text/javascript")))) + (set-return-value-fn return-value-fn) + (server/send-and-close conn 200 form "text/javascript"))) (defn- return-value "Called by the server when a return value is received." @@ -47,7 +50,7 @@ (f val))) (defn repl-client-js [] - (slurp @(:client-js @browser-state))) + (slurp (:client-js @browser-state))) (defn send-repl-client-page [request conn opts] @@ -67,19 +70,21 @@ (not= "/favicon.ico" path)) (let [path (if (= "/" path) "/index.html" path) st-dir (:static-dir opts) - local-path (cond-> - (seq (for [x (if (string? st-dir) [st-dir] st-dir) - :when (.exists (io/file (str x path)))] - (str x path))) - (complement nil?) first) - local-path (if (nil? local-path) - (cond - (re-find #".jar" path) - (io/resource (second (string/split path #".jar!/"))) - (re-find (Pattern/compile (System/getProperty "user.dir")) path) - (io/file (string/replace path (str (System/getProperty "user.dir") "/") "")) - :else nil) - local-path)] + local-path + (cond-> + (seq (for [x (if (string? st-dir) [st-dir] st-dir) + :when (.exists (io/file (str x path)))] + (str x path))) + (complement nil?) first) + local-path + (if (nil? local-path) + (cond + (re-find #".jar" path) + (io/resource (second (string/split path #".jar!/"))) + (re-find (Pattern/compile (System/getProperty "user.dir")) path) + (io/file (string/replace path (str (System/getProperty "user.dir") "/") "")) + :else nil) + local-path)] (if local-path (server/send-and-close conn 200 (slurp local-path) (condp #(.endsWith %2 %1) path @@ -96,16 +101,19 @@ (server/send-404 conn path))) (server/dispatch-on :get - (fn [{:keys [path]} _ _] (.startsWith path "/repl")) - send-repl-client-page) + (fn [{:keys [path]} _ _] + (.startsWith path "/repl")) + send-repl-client-page) (server/dispatch-on :get - (fn [{:keys [path]} _ _] (or (= path "/") - (.endsWith path ".js") - (.endsWith path ".cljs") - (.endsWith path ".map") - (.endsWith path ".html"))) - send-static) + (fn [{:keys [path]} _ _] + (or + (= path "/") + (.endsWith path ".js") + (.endsWith path ".cljs") + (.endsWith path ".map") + (.endsWith path ".html"))) + send-static) (defmulti handle-post (fn [m _ _ ] (:type m))) @@ -114,23 +122,24 @@ (def ordering (agent {:expecting nil :fns {}})) (defmethod handle-post :ready [_ conn _] - (do (reset! loaded-libs @preloaded-libs) - (send ordering (fn [_] {:expecting nil :fns {}})) - (send-for-eval conn - (cljsc/-compile - '[(ns cljs.user) - (set! *print-fn* clojure.browser.repl/repl-print)] {}) - identity))) + (reset! loaded-libs @preloaded-libs) + (send ordering (fn [_] {:expecting nil :fns {}})) + (send-for-eval conn + (cljsc/-compile + '[(ns cljs.user) + (set! *print-fn* clojure.browser.repl/repl-print)] {}) + identity)) (defn add-in-order [{:keys [expecting fns]} order f] - {:expecting (or expecting order) :fns (assoc fns order f)}) + {:expecting (or expecting order) + :fns (assoc fns order f)}) (defn run-in-order [{:keys [expecting fns]}] - (loop [order expecting - fns fns] + (loop [order expecting fns fns] (if-let [f (get fns order)] - (do (f) - (recur (inc order) (dissoc fns order))) + (do + (f) + (recur (inc order) (dissoc fns order))) {:expecting order :fns fns}))) (defn constrain-order @@ -141,13 +150,17 @@ (send-off ordering run-in-order)) (defmethod handle-post :print [{:keys [content order]} conn _ ] - (do (constrain-order order (fn [] (do (print (read-string content)) - (.flush *out*)))) - (server/send-and-close conn 200 "ignore__"))) + (constrain-order order + (fn [] + (print (read-string content)) + (.flush *out*))) + (server/send-and-close conn 200 "ignore__")) (defmethod handle-post :result [{:keys [content order]} conn _ ] - (constrain-order order (fn [] (do (return-value content) - (server/set-connection conn))))) + (constrain-order order + (fn [] + (return-value content) + (server/set-connection conn)))) (defn browser-eval "Given a string of JavaScript, evaluate it in the browser and return a map representing the @@ -159,12 +172,13 @@ [form] (let [return-value (promise)] (send-for-eval form - (fn [val] (deliver return-value val))) + (fn [val] (deliver return-value val))) (let [ret @return-value] - (try (read-string ret) - (catch Exception e - {:status :error - :value (str "Could not read return value: " ret)}))))) + (try + (read-string ret) + (catch Exception e + {:status :error + :value (str "Could not read return value: " ret)}))))) (defn load-javascript "Accepts a REPL environment, a list of namespaces, and a URL for a @@ -181,27 +195,29 @@ (defrecord BrowserEnv [] repl/IJavaScriptEnv (-setup [this] - (do (require 'cljs.repl.reflect) - (repl/analyze-source (:src this)) - (comp/with-core-cljs nil (fn [] (server/start this))))) + (when (:src this) + (repl/analyze-source (:src this))) + (comp/with-core-cljs nil + (fn [] (server/start this)))) (-evaluate [_ _ _ js] (browser-eval js)) (-load [this ns url] (load-javascript this ns url)) (-tear-down [_] - (do (server/stop) - (reset! server/state {}) - (reset! browser-state {})))) + (server/stop) + (reset! server/state {}) + (reset! browser-state {}))) (defn compile-client-js [opts] - (cljsc/build '[(ns clojure.browser.repl.client - (:require [goog.events :as event] - [clojure.browser.repl :as repl])) - (defn start [url] - (event/listen js/window - "load" - (fn [] - (repl/start-evaluator url))))] - {:optimizations (:optimizations opts) - :output-dir (:working-dir opts)})) + (cljsc/build + '[(ns clojure.browser.repl.client + (:require [goog.events :as event] + [clojure.browser.repl :as repl])) + (defn start [url] + (event/listen js/window + "load" + (fn [] + (repl/start-evaluator url))))] + {:optimizations (:optimizations opts) + :output-dir (:working-dir opts)})) (defn create-client-js-file [opts file-path] (let [file (io/file file-path)] @@ -215,12 +231,18 @@ [deps] (flatten (mapcat (juxt :provides :requires) deps))) +;; TODO: the following is questionable as it triggers compilation +;; this code should other means to determine the dependencies for a +;; namespace - David + (defn- always-preload "Return a list of all namespaces which are always loaded into the browser when using a browser-connected REPL." [] - (let [cljs (provides-and-requires (cljsc/cljs-dependencies {} ["clojure.browser.repl"])) - goog (provides-and-requires (cljsc/js-dependencies {} cljs))] + (let [cljs (provides-and-requires + (cljsc/cljs-dependencies {} ["clojure.browser.repl"])) + goog (provides-and-requires + (cljsc/js-dependencies {} cljs))] (disj (set (concat cljs goog)) nil))) (defn repl-env @@ -247,25 +269,32 @@ [& {:as opts}] (let [compiler-env (cljs.env/default-compiler-env opts) opts (merge (BrowserEnv.) - {:port 9000 - :optimizations :simple - :working-dir (->> [".repl" (util/clojurescript-version)] (remove empty?) (string/join "-")) - :serve-static true - :static-dir ["." "out/"] - :preloaded-libs [] - :src "src/" - :cljs.env/compiler compiler-env - :source-map true} - opts)] + {:port 9000 + :optimizations :simple + :working-dir (->> [".repl" (util/clojurescript-version)] + (remove empty?) (string/join "-")) + :serve-static true + :static-dir ["." "out/"] + :preloaded-libs [] + :src "src/" + ::env/compiler compiler-env + :source-map false} + opts)] (cljs.env/with-compiler-env compiler-env - (reset! preloaded-libs (set (concat (always-preload) (map str (:preloaded-libs opts))))) - (reset! loaded-libs @preloaded-libs) - (swap! browser-state - (fn [old] (assoc old :client-js - (future (create-client-js-file - opts - (io/file (:working-dir opts) "client.js")))))) - opts))) + (reset! preloaded-libs + (set (concat + (always-preload) + (map str (:preloaded-libs opts))))) + (reset! loaded-libs @preloaded-libs) + (println "Compiling client js ...") + (swap! browser-state + (fn [old] + (assoc old :client-js + (create-client-js-file + opts + (io/file (:working-dir opts) "client.js"))))) + (println "Waiting for browser to connect ...") + opts))) (comment diff --git a/src/clj/cljs/repl/server.clj b/src/clj/cljs/repl/server.clj index 3e40f70ee..7ad2db4e7 100644 --- a/src/clj/cljs/repl/server.clj +++ b/src/clj/cljs/repl/server.clj @@ -13,21 +13,25 @@ java.net.ServerSocket cljs.repl.IJavaScriptEnv)) -(defonce state (atom {:socket nil - :connection nil - :promised-conn nil})) +(defonce state + (atom + {:socket nil + :connection nil + :promised-conn nil})) (defn connection "Promise to return a connection when one is available. If a connection is not available, store the promise in server/state." [] - (let [p (promise) + (let [p (promise) conn (:connection @state)] (if (and conn (not (.isClosed conn))) - (do (deliver p conn) - p) - (do (swap! state (fn [old] (assoc old :promised-conn p))) - p)))) + (do + (deliver p conn) + p) + (do + (swap! state (fn [old] (assoc old :promised-conn p))) + p)))) (defn set-connection "Given a new available connection, either use it to deliver the @@ -35,10 +39,13 @@ use." [conn] (if-let [promised-conn (:promised-conn @state)] - (do (swap! state (fn [old] (-> old - (assoc :connection nil) - (assoc :promised-conn nil)))) - (deliver promised-conn conn)) + (do + (swap! state + (fn [old] + (-> old + (assoc :connection nil) + (assoc :promised-conn nil)))) + (deliver promised-conn conn)) (swap! state (fn [old] (assoc old :connection conn))))) (defonce handlers (atom {})) @@ -51,28 +58,30 @@ and a request map and returns a boolean value based on whether or not that request should be dispatched to the related handler." ([method pred handler] - (dispatch-on method {:pred pred :handler handler})) + (dispatch-on method {:pred pred :handler handler})) ([method {:as m}] - (swap! handlers (fn [old] - (update-in old [method] #(conj (vec %) m)))))) + (swap! handlers + (fn [old] + (update-in old [method] #(conj (vec %) m)))))) ;;; assumes first line already consumed (defn parse-headers "Parse the headers of an HTTP POST request." [header-lines] (apply hash-map - (mapcat - (fn [line] - (let [[k v] (str/split line #":" 2)] - [(keyword (str/lower-case k)) (str/triml v)])) - header-lines))) + (mapcat + (fn [line] + (let [[k v] (str/split line #":" 2)] + [(keyword (str/lower-case k)) (str/triml v)])) + header-lines))) (defn read-headers [rdr] - (loop [next-line (.readLine rdr) - header-lines []] + (loop [next-line (.readLine rdr) header-lines []] (if (= "" next-line) - header-lines ;we're done reading headers - (recur (.readLine rdr) (conj header-lines next-line))))) + header-lines ;; we're done reading headers + (recur + (.readLine rdr) + (conj header-lines next-line))))) (defn read-post [line rdr] (let [[_ path _] (str/split line #" ") @@ -80,10 +89,10 @@ content-length (Integer/parseInt (:content-length headers)) content (char-array content-length)] (io! (.read rdr content 0 content-length) - {:method :post - :path path - :headers headers - :content (String. content)}))) + {:method :post + :path path + :headers headers + :content (String. content)}))) (defn read-get [line rdr] (let [[_ path _] (str/split line #" ") @@ -94,9 +103,10 @@ (defn read-request [rdr] (let [line (.readLine rdr)] - (cond (.startsWith line "POST") (read-post line rdr) - (.startsWith line "GET") (read-get line rdr) - :else {:method :unknown :content line}))) + (cond + (.startsWith line "POST") (read-post line rdr) + (.startsWith line "GET") (read-get line rdr) + :else {:method :unknown :content line}))) (defn- status-line [status] (case status @@ -108,39 +118,41 @@ "Use the passed connection to send a form to the browser. Send a proper HTTP response." ([conn status form] - (send-and-close conn status form "text/html")) + (send-and-close conn status form "text/html")) ([conn status form content-type] - (let [utf-8-form (.getBytes form "UTF-8") - content-length (count utf-8-form) - headers (map #(.getBytes (str % "\r\n")) - [(status-line status) - "Server: ClojureScript REPL" - (str "Content-Type: " - content-type - "; charset=utf-8") - (str "Content-Length: " content-length) - ""])] - (with-open [os (.getOutputStream conn)] - (do (doseq [header headers] - (.write os header 0 (count header))) - (.write os utf-8-form 0 content-length) - (.flush os) - (.close conn)))))) + (let [utf-8-form (.getBytes form "UTF-8") + content-length (count utf-8-form) + headers (map #(.getBytes (str % "\r\n")) + [(status-line status) + "Server: ClojureScript REPL" + (str "Content-Type: " + content-type + "; charset=utf-8") + (str "Content-Length: " content-length) + ""])] + (with-open [os (.getOutputStream conn)] + (doseq [header headers] + (.write os header 0 (count header))) + (.write os utf-8-form 0 content-length) + (.flush os) + (.close conn))))) (defn send-404 [conn path] (send-and-close conn 404 - (str "" - "

Page not found

" - "No page " path " found on this server." - "") - "text/html")) + (str + "" + "

Page not found

" + "No page " path " found on this server." + "") + "text/html")) (defn- dispatch-request [request conn opts] (if-let [handlers ((:method request) @handlers)] - (if-let [handler (some (fn [{:keys [pred handler]}] - (when (pred request conn opts) - handler)) - handlers)] + (if-let [handler + (some (fn [{:keys [pred handler]}] + (when (pred request conn opts) + handler)) + handlers)] (if (= :post (:method request)) (handler (read-string (:content request)) conn opts ) (handler request conn opts)) @@ -157,9 +169,9 @@ (defn- server-loop [opts server-socket] (let [conn (.accept server-socket)] - (do (.setKeepAlive conn true) - (future (handle-connection opts conn)) - (recur opts server-socket)))) + (.setKeepAlive conn true) + (future (handle-connection opts conn)) + (recur opts server-socket))) (defn start "Start the server on the specified port." @@ -168,6 +180,5 @@ (future (server-loop opts ss)) (swap! state (fn [old] (assoc old :socket ss :port (:port opts)))))) -(defn stop - [] +(defn stop [] (.close (:socket @state))) From 061b93d9ad2262838d1ab4d71cbb78bfa8af7d82 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 18:36:50 -0500 Subject: [PATCH 0370/4033] thread analysis env into REPL special fns implement require REPL special fn --- src/clj/cljs/repl.clj | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 31e3e787b..dc7c1ec38 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -174,26 +174,37 @@ (def default-special-fns (let [load-file-fn (fn self - ([repl-env form] - (self repl-env form nil)) - ([repl-env [_ file :as form] opts] + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ file :as form] opts] (load-file repl-env file opts)))] {'in-ns (fn self - ([repl-env form] - (self repl-env form nil)) - ([repl-env [_ [quote ns-name] :as form] _] + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ [quote ns-name] :as form] _] (when-not (ana/get-namespace ns-name) (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) (-evaluate repl-env "" 1 (str "goog.provide('" (comp/munge ns-name) "');"))) (set! ana/*cljs-ns* ns-name))) + 'require + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ [quote spec]] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require ~spec)) + {:line 1 :column 1})))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace (fn self - ([repl-env form] (self repl-env form nil)) - ([repl-env [_ ns :as form] opts] + ([repl-env env form] + (self env repl-env form nil)) + ([repl-env env [_ ns :as form] opts] (load-namespace repl-env ns opts)))})) (defn analyze-source @@ -262,7 +273,7 @@ (and (seq? form) (is-special-fn? (first form))) (do ((get special-fns (first form)) - repl-env form opts) + repl-env env form opts) (newline) (recur)) From defd9d828a0b0e11abc04742944d5445e6a75c04 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 18:37:28 -0500 Subject: [PATCH 0371/4033] ignore classes directory in IntelliJ file --- Clojurescript.iml | 1 + 1 file changed, 1 insertion(+) diff --git a/Clojurescript.iml b/Clojurescript.iml index 7ec55c132..215a49713 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -9,6 +9,7 @@ + From d462ec09bdfcdba57e38141a1ac7f5e228f33530 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 19:03:46 -0500 Subject: [PATCH 0372/4033] Node.js REPL wip --- src/clj/cljs/repl/node.clj | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 471e687af..e0993d592 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -20,9 +20,6 @@ java.lang.StringBuilder [java.io BufferedReader BufferedWriter])) -(def current-repl-env (atom nil)) -(def loaded-libs (atom #{})) - (defn socket [host port] (let [socket (Socket. host port) in (io/reader socket) @@ -51,18 +48,18 @@ (.append sb (char c)) (recur sb (.read in))))))) -(defn node-eval [{:keys [in out]} js] +(defn node-eval [{{:keys [in out]} :socket :as repl-env} js] (write out js) {:status :success :value (read-response in)}) -(defn load-javascript [ctx ns url] - (node-eval ctx (slurp url))) +(defn load-javascript [repl-env ns url] + (node-eval repl-env (slurp url))) (defn setup [repl-env] (let [env (ana/empty-env) scope (:scope repl-env)] (repl/load-file repl-env "cljs/core.cljs") - (swap! loaded-libs conj "cljs.core") + (swap! (:loaded-libs repl-env) conj "cljs.core") (repl/evaluate-form repl-env env "" '(ns cljs.user)) @@ -70,18 +67,20 @@ env "" '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))))) -(extend-protocol repl/IJavaScriptEnv - clojure.lang.IPersistentMap - (-setup [this] (setup this)) - (-evaluate [this filename line js] (node-eval this js)) - (-load [this ns url] (load-javascript this ns url)) - (-tear-down [this] (close-socket this))) - -;; do we need to implement our own version of goog.require ? - David +(defrecord NodeEnv [socket loaded-libs] + repl/IJavaScriptEnv + (-setup [this] + (setup this)) + (-evaluate [this filename line js] + (node-eval this js)) + (-load [this ns url] + (load-javascript this ns url)) + (-tear-down [this] + (close-socket socket))) (defn repl-env [& {:keys [host port] :or {host "localhost" port 5001}}] - (let [repl-env (socket host port) + (let [repl-env (NodeEnv. (socket host port) (atom {})) base (io/resource "goog/base.js") deps (io/resource "goog/deps.js")] (node-eval repl-env (slurp (io/reader base))) From 2496d2bf0a60793c3414bc7e3014960a121fcfb6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 19:11:27 -0500 Subject: [PATCH 0373/4033] desugared cljs.repl.node/repl-env*, remove some dead code --- src/clj/cljs/repl/node.clj | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index e0993d592..1264204d8 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -56,8 +56,7 @@ (node-eval repl-env (slurp url))) (defn setup [repl-env] - (let [env (ana/empty-env) - scope (:scope repl-env)] + (let [env (ana/empty-env)] (repl/load-file repl-env "cljs/core.cljs") (swap! (:loaded-libs repl-env) conj "cljs.core") (repl/evaluate-form repl-env @@ -78,11 +77,9 @@ (-tear-down [this] (close-socket socket))) +(defn repl-env* [{:keys [host port] :or {host "localhost" port 5001}}] + (NodeEnv. (socket host port) (atom {}))) + (defn repl-env - [& {:keys [host port] :or {host "localhost" port 5001}}] - (let [repl-env (NodeEnv. (socket host port) (atom {})) - base (io/resource "goog/base.js") - deps (io/resource "goog/deps.js")] - (node-eval repl-env (slurp (io/reader base))) - (node-eval repl-env (slurp (io/reader deps))) - repl-env)) + [& options] + (repl-env* options)) From bf75d547e0aa9458ff1e7404a7b6d1d891488cfe Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 19:25:56 -0500 Subject: [PATCH 0374/4033] move node_repl.js into src/clj/cljs/repl --- {bin => src/clj/cljs/repl}/node_repl.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {bin => src/clj/cljs/repl}/node_repl.js (100%) diff --git a/bin/node_repl.js b/src/clj/cljs/repl/node_repl.js similarity index 100% rename from bin/node_repl.js rename to src/clj/cljs/repl/node_repl.js From 56acda4c0dd5208fa9f604c968f3412e8e6453a2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 19:27:53 -0500 Subject: [PATCH 0375/4033] We'll be starting node in process, remove separate command from script/node_repl --- script/node_repl | 1 - 1 file changed, 1 deletion(-) diff --git a/script/node_repl b/script/node_repl index b0e714fb5..4c75150ce 100755 --- a/script/node_repl +++ b/script/node_repl @@ -9,7 +9,6 @@ for next in lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done -node $CLOJURESCRIPT_HOME/bin/node_repl.js & java -server -cp $CLJSC_CP clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.node :as node]) From 3307ef0995b57ad9c2a9c574254be119b657becd Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 20:21:41 -0500 Subject: [PATCH 0376/4033] switch to using Java 7 ProcessBuilder --- src/clj/cljs/repl/node.clj | 39 ++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 1264204d8..5774aac1b 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -18,7 +18,8 @@ (:import cljs.repl.IJavaScriptEnv java.net.Socket java.lang.StringBuilder - [java.io BufferedReader BufferedWriter])) + [java.io BufferedReader BufferedWriter] + [java.lang ProcessBuilder UNIXProcess ProcessBuilder$Redirect])) (defn socket [host port] (let [socket (Socket. host port) @@ -48,25 +49,36 @@ (.append sb (char c)) (recur sb (.read in))))))) -(defn node-eval [{{:keys [in out]} :socket :as repl-env} js] - (write out js) - {:status :success :value (read-response in)}) +(defn node-eval [repl-env js] + (let [{:keys [in out]} @(:socket repl-env)] + (write out js) + {:status :success + :value (read-response in)})) (defn load-javascript [repl-env ns url] (node-eval repl-env (slurp url))) (defn setup [repl-env] - (let [env (ana/empty-env)] + (println "Setting up Node.js REPL") + (let [bldr (ProcessBuilder. (into-array ["node"])) + _ (-> bldr + (.redirectInput (io/file (io/resource "cljs/repl/node_repl.js"))) + (.redirectOutput ProcessBuilder$Redirect/INHERIT) + (.redirectError ProcessBuilder$Redirect/INHERIT)) + proc (.start bldr) + env (ana/empty-env)] + ;; TODO: temporary hack, should wait till we can read the start string + ;; from the process - David + (Thread/sleep 1000) + (reset! (:socket repl-env) + (socket (:host repl-env) (:port repl-env))) (repl/load-file repl-env "cljs/core.cljs") (swap! (:loaded-libs repl-env) conj "cljs.core") - (repl/evaluate-form repl-env - env "" - '(ns cljs.user)) (repl/evaluate-form repl-env env "" '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))))) -(defrecord NodeEnv [socket loaded-libs] +(defrecord NodeEnv [host port socket loaded-libs] repl/IJavaScriptEnv (-setup [this] (setup this)) @@ -78,8 +90,15 @@ (close-socket socket))) (defn repl-env* [{:keys [host port] :or {host "localhost" port 5001}}] - (NodeEnv. (socket host port) (atom {}))) + (NodeEnv. host port (atom nil) (atom {}))) (defn repl-env [& options] (repl-env* options)) + +(comment + + (def bldr (ProcessBuilder. (into-array ["node"]))) + + + ) \ No newline at end of file From ca4dd7d5dc75a4de4b74209711d83952b5a3c6cf Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 20:44:58 -0500 Subject: [PATCH 0377/4033] Need to load bootstrap --- src/clj/cljs/repl/node.clj | 52 +++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 5774aac1b..1b77d7010 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -49,39 +49,49 @@ (.append sb (char c)) (recur sb (.read in))))))) -(defn node-eval [repl-env js] +(defn node-eval + "Evaluate a JavaScript string in the Node REPL process." + [repl-env js] (let [{:keys [in out]} @(:socket repl-env)] (write out js) {:status :success :value (read-response in)})) -(defn load-javascript [repl-env ns url] +(defn load-javascript + "Load a JavaScript file into the Node REPL process." + [repl-env ns url] (node-eval repl-env (slurp url))) -(defn setup [repl-env] - (println "Setting up Node.js REPL") - (let [bldr (ProcessBuilder. (into-array ["node"])) - _ (-> bldr - (.redirectInput (io/file (io/resource "cljs/repl/node_repl.js"))) - (.redirectOutput ProcessBuilder$Redirect/INHERIT) - (.redirectError ProcessBuilder$Redirect/INHERIT)) - proc (.start bldr) - env (ana/empty-env)] - ;; TODO: temporary hack, should wait till we can read the start string - ;; from the process - David - (Thread/sleep 1000) - (reset! (:socket repl-env) - (socket (:host repl-env) (:port repl-env))) - (repl/load-file repl-env "cljs/core.cljs") - (swap! (:loaded-libs repl-env) conj "cljs.core") - (repl/evaluate-form repl-env - env "" - '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))))) +(defn setup + ([repl-env] (setup repl-env nil)) + ([repl-env opts] + (println "Setting up Node.js REPL") + (let [bldr (ProcessBuilder. (into-array ["node"])) + _ (-> bldr + (.redirectInput (io/file (io/resource "cljs/repl/node_repl.js"))) + (.redirectOutput ProcessBuilder$Redirect/INHERIT) + (.redirectError ProcessBuilder$Redirect/INHERIT)) + proc (.start bldr) + env (ana/empty-env)] + ;; TODO: temporary hack, should wait till we can read the start string + ;; from the process - David + (Thread/sleep 1000) + (reset! (:socket repl-env) + (socket (:host repl-env) (:port repl-env))) + ;; bootstrap + (load-javascript repl-env nil (io/resource "cljs/bootstrap_node.js")) + ;; load core + (repl/load-file repl-env "cljs/core.cljs") + (repl/evaluate-form repl-env + env "" + '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x)))))))) (defrecord NodeEnv [host port socket loaded-libs] repl/IJavaScriptEnv (-setup [this] (setup this)) + (-setup [this opts] + (setup this opts)) (-evaluate [this filename line js] (node-eval this js)) (-load [this ns url] From 06755c933d565662bfbfcc804ba40130abeaea82 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 22:42:22 -0500 Subject: [PATCH 0378/4033] Allow cljs.repl/load-file to take urls --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index dc7c1ec38..1358e6145 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -144,7 +144,7 @@ ([repl-env f] (load-file repl-env f nil)) ([repl-env f opts] (if (:output-dir opts) - (let [src (io/resource f)] + (let [src (if (util/url? f) f (io/resource f))] (comp/compile-file src (cljsc/src-file->target-file src opts) opts) (-evaluate repl-env f 0 (cljsc/src-file->goog-require src))) From d417d07f49fd75d301a93d1c1de5a0983751144c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 22:42:45 -0500 Subject: [PATCH 0379/4033] tweak REPL running scripts --- script/node_repl | 2 +- script/repljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/node_repl b/script/node_repl index 4c75150ce..b3c19f876 100755 --- a/script/node_repl +++ b/script/node_repl @@ -12,4 +12,4 @@ done java -server -cp $CLJSC_CP clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.node :as node]) -(repl/repl (node/repl-env $1))" +(repl/repl* (node/repl-env $1) {:output-dir \".cljs_node_repl\"})" diff --git a/script/repljs b/script/repljs index e2d7a1d67..ed24c02cc 100755 --- a/script/repljs +++ b/script/repljs @@ -12,4 +12,4 @@ done java -server -cp "$CLJSC_CP" clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.rhino :as rhino]) -(repl/repl* (rhino/repl-env) {:warn-on-undeclared true :output-dir \".repl\" :cache-analysis true})" +(repl/repl* (rhino/repl-env) {:warn-on-undeclared true :output-dir \".cljs_repl\" :cache-analysis true})" From 30a37f803e8ba2e1971c9439b5b7b86b9ad40c86 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 23:02:14 -0500 Subject: [PATCH 0380/4033] use closure fns to compile core and output all deps to the correct location --- src/clj/cljs/repl/node.clj | 51 ++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 1b77d7010..626895e41 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -14,11 +14,13 @@ [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.repl :as repl] - [cljs.closure :as cljsc]) + [cljs.closure :as cljsc] + [cljs.closure :as closure] + [cljs.util :as util]) (:import cljs.repl.IJavaScriptEnv java.net.Socket java.lang.StringBuilder - [java.io BufferedReader BufferedWriter] + [java.io File BufferedReader BufferedWriter] [java.lang ProcessBuilder UNIXProcess ProcessBuilder$Redirect])) (defn socket [host port] @@ -65,26 +67,43 @@ (defn setup ([repl-env] (setup repl-env nil)) ([repl-env opts] - (println "Setting up Node.js REPL") - (let [bldr (ProcessBuilder. (into-array ["node"])) - _ (-> bldr - (.redirectInput (io/file (io/resource "cljs/repl/node_repl.js"))) - (.redirectOutput ProcessBuilder$Redirect/INHERIT) - (.redirectError ProcessBuilder$Redirect/INHERIT)) + (let [output-dir (io/file (:output-dir opts)) + _ (.mkdirs output-dir) + bldr (ProcessBuilder. (into-array ["node"])) + _ (-> bldr + (.redirectInput + (io/file (io/resource "cljs/repl/node_repl.js"))) + (.redirectOutput ProcessBuilder$Redirect/INHERIT) + (.redirectError ProcessBuilder$Redirect/INHERIT)) proc (.start bldr) - env (ana/empty-env)] + env (ana/empty-env) + core (io/resource "cljs/core.cljs")] ;; TODO: temporary hack, should wait till we can read the start string ;; from the process - David (Thread/sleep 1000) (reset! (:socket repl-env) (socket (:host repl-env) (:port repl-env))) - ;; bootstrap - (load-javascript repl-env nil (io/resource "cljs/bootstrap_node.js")) - ;; load core - (repl/load-file repl-env "cljs/core.cljs") - (repl/evaluate-form repl-env - env "" - '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x)))))))) + ;; compile cljs.core & its dependencies, goog/base.js must be available + ;; for bootstrap to load + (let [core-js (closure/compile-file core + (assoc opts + :output-file (closure/src-file->target-file core))) + deps (closure/add-dependencies opts core-js)] + (apply closure/output-unoptimized opts deps)) + ;; bootstrap, replace __dirname as __dirname won't be set + ;; properly due to how we are running it - David + (node-eval repl-env + (string/replace + (slurp (io/resource "cljs/bootstrap_node.js")) + "__dirname" + (str "\"" + (.getName (.getCanonicalFile output-dir)) + File/separator "goog" File/separator "bootstrap"))) + ;(repl/load-file repl-env core) + ;(repl/evaluate-form repl-env + ; env "" + ; '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))) + ))) (defrecord NodeEnv [host port socket loaded-libs] repl/IJavaScriptEnv From e03aff81a2bf3a5322e0000322469e3a9f371a68 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 23:25:50 -0500 Subject: [PATCH 0381/4033] no need to write out errors over socket since we can inherit out. context.global = context fix typo in string replace of __dirname --- src/clj/cljs/repl/node.clj | 2 +- src/clj/cljs/repl/node_repl.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 626895e41..7c2b6b5ef 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -98,7 +98,7 @@ "__dirname" (str "\"" (.getName (.getCanonicalFile output-dir)) - File/separator "goog" File/separator "bootstrap"))) + File/separator "goog" File/separator "bootstrap\""))) ;(repl/load-file repl-env core) ;(repl/evaluate-form repl-env ; env "" diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index d1064c206..566a0abea 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -6,7 +6,7 @@ var net = require("net"), context = vm.createContext(); context.require = require; -context.global = global; +context.global = context; context.process = process; context.__dirname = __dirname; @@ -36,7 +36,6 @@ net.createServer(function (socket) { ret = vm.runInContext(data, context, "repl"); } catch (x) { console.log(x.stack); - socket.write(x.stack+"\n"); } } if(ret !== undefined && ret !== null) { From f66e9051895433ada95fc090fbe0e860f03e73b5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Dec 2014 23:51:52 -0500 Subject: [PATCH 0382/4033] node_repl.js can be considerably simpler by following bootstrap_node.js lead --- src/clj/cljs/repl/node_repl.js | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index 566a0abea..f8233fcf6 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -1,26 +1,12 @@ process.env.NODE_DISABLE_COLORS = true; var net = require("net"), - repl = require("repl"), - vm = require("vm"), - context = vm.createContext(); - -context.require = require; -context.global = context; -context.process = process; -context.__dirname = __dirname; + repl = require("repl"); net.createServer(function (socket) { var buffer = "", ret; socket.setEncoding("utf8"); - // redefine console.log - context.node_repl_print = function(x) { - ret = vm.runInContext(x, context, "repl"); - socket.write(ret.toString()); - socket.write("\1"); - }; - socket.on("data", function(data) { if(data[data.length-1] != "\0") { buffer += data; @@ -33,11 +19,13 @@ net.createServer(function (socket) { // not sure how \0's are getting through - David data = data.replace(/\0/g, ""); try { - ret = vm.runInContext(data, context, "repl"); + ret = process.binding('evals').NodeScript.runInThisContext.call( + global, data, "repl"); } catch (x) { console.log(x.stack); } } + // TODO: can we just console.log? - David if(ret !== undefined && ret !== null) { socket.write(ret.toString()); } else { From 0a66805574699ef7e482bf6e2a996b0b5450363b Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 00:31:01 -0500 Subject: [PATCH 0383/4033] Node.js REPL wip, getting warmer - need to emit the goog.addDependencies bit --- src/clj/cljs/repl.clj | 4 +++- src/clj/cljs/repl/node.clj | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 1358e6145..ae35bd373 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -140,6 +140,8 @@ (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*))] (evaluate-form repl-env env filename form))))) +;; TODO: this should probably compile dependencies - David + (defn load-file ([repl-env f] (load-file repl-env f nil)) ([repl-env f opts] @@ -242,7 +244,7 @@ (if-not (nil? opts) (-setup repl-env opts) (-setup repl-env)) - (evaluate-form repl-env env "" + #_(evaluate-form repl-env env "" (with-meta '(ns cljs.user (:require [cljs.repl :refer-macros [doc]])) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 7c2b6b5ef..f9fa5c337 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -92,14 +92,14 @@ (apply closure/output-unoptimized opts deps)) ;; bootstrap, replace __dirname as __dirname won't be set ;; properly due to how we are running it - David - (node-eval repl-env - (string/replace - (slurp (io/resource "cljs/bootstrap_node.js")) - "__dirname" - (str "\"" - (.getName (.getCanonicalFile output-dir)) - File/separator "goog" File/separator "bootstrap\""))) - ;(repl/load-file repl-env core) + (let [rewrite-path (str (.getName (.getCanonicalFile output-dir)) + File/separator "goog")] + (node-eval repl-env + (-> (slurp (io/resource "cljs/bootstrap_node.js")) + (string/replace "__dirname" + (str "\"" (str rewrite-path File/separator "bootstrap") "\"")) + (string/replace "./.." (str "." File/separator rewrite-path))))) + (repl/load-file repl-env core opts) ;(repl/evaluate-form repl-env ; env "" ; '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))) From 8ef2df9e879f30ea528e3cbdb1033b584e014842 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 00:54:41 -0500 Subject: [PATCH 0384/4033] need to supply our own deps file for Node.js to load --- src/clj/cljs/repl/node.clj | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index f9fa5c337..0532731d6 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -89,17 +89,23 @@ (assoc opts :output-file (closure/src-file->target-file core))) deps (closure/add-dependencies opts core-js)] - (apply closure/output-unoptimized opts deps)) + (apply closure/output-unoptimized + (assoc opts + :output-to (.getPath (io/file output-dir "node_repl_deps.js"))) + deps)) ;; bootstrap, replace __dirname as __dirname won't be set ;; properly due to how we are running it - David - (let [rewrite-path (str (.getName (.getCanonicalFile output-dir)) - File/separator "goog")] + (let [root-path (.getCanonicalFile output-dir) + rewrite-path (str (.getPath root-path) File/separator "goog")] (node-eval repl-env (-> (slurp (io/resource "cljs/bootstrap_node.js")) (string/replace "__dirname" (str "\"" (str rewrite-path File/separator "bootstrap") "\"")) - (string/replace "./.." (str "." File/separator rewrite-path))))) - (repl/load-file repl-env core opts) + (string/replace "./.." rewrite-path))) + (node-eval repl-env + (str "require('" + (.getPath root-path) + File/separator "node_repl_deps.js')"))) ;(repl/evaluate-form repl-env ; env "" ; '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))) From 8c5ab2adb6695ca7b571c5eb0b868873dd178baf Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 00:59:17 -0500 Subject: [PATCH 0385/4033] Node.js REPL works, accidentally commented out the REPL init form removed dead line from REPL server runner --- src/clj/cljs/repl.clj | 2 +- src/clj/cljs/repl/node_repl.js | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ae35bd373..5bce7e9e9 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -244,7 +244,7 @@ (if-not (nil? opts) (-setup repl-env opts) (-setup repl-env)) - #_(evaluate-form repl-env env "" + (evaluate-form repl-env env "" (with-meta '(ns cljs.user (:require [cljs.repl :refer-macros [doc]])) diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index f8233fcf6..3187f1718 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -1,7 +1,6 @@ process.env.NODE_DISABLE_COLORS = true; -var net = require("net"), - repl = require("repl"); +var net = require("net"); net.createServer(function (socket) { var buffer = "", ret; @@ -37,4 +36,4 @@ net.createServer(function (socket) { }).listen(5001); -console.log("repl.js listening on 5001") +console.log("ClojureScript Node.js REPL server listening on 5001") From a02db7184c8bd66d69159a95f15327199d7ee898 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 01:04:08 -0500 Subject: [PATCH 0386/4033] analysis for Node REPL --- script/node_repl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/node_repl b/script/node_repl index b3c19f876..ddb464e9e 100755 --- a/script/node_repl +++ b/script/node_repl @@ -12,4 +12,4 @@ done java -server -cp $CLJSC_CP clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.node :as node]) -(repl/repl* (node/repl-env $1) {:output-dir \".cljs_node_repl\"})" +(repl/repl* (node/repl-env $1) {:output-dir \".cljs_node_repl\" :cache-analysis true})" From 2f9aeef2aa3289cba62341a1bbcc8c828f5d894a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 01:40:29 -0500 Subject: [PATCH 0387/4033] ignore repl .cljs* directories --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d469f171d..e81910d6c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ closure-release-* .lein-repl-history .nrepl-port .nrepl-repl-history -builds \ No newline at end of file +builds +.cljs* From 96a15d9891e0c578e9e3163e169903f3c793d6a8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 01:50:17 -0500 Subject: [PATCH 0388/4033] enable printing in Node.js REPL --- src/clj/cljs/repl/node.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 0532731d6..a86b62574 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -106,9 +106,11 @@ (str "require('" (.getPath root-path) File/separator "node_repl_deps.js')"))) - ;(repl/evaluate-form repl-env - ; env "" - ; '(set! *print-fn* (fn [x] (js/node_repl_print (pr-str x))))) + (repl/evaluate-form repl-env + env "" + '(do + (.require js/goog "cljs.core") + (set! *print-fn* (.-print (js/require "util"))))) ))) (defrecord NodeEnv [host port socket loaded-libs] @@ -136,4 +138,4 @@ (def bldr (ProcessBuilder. (into-array ["node"]))) - ) \ No newline at end of file + ) From 73b797a69fe6f9afb8e6528ff6cdc4129a481a65 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 01:57:31 -0500 Subject: [PATCH 0389/4033] fix core compilation --- src/clj/cljs/repl/node.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index a86b62574..f45c5174b 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -87,7 +87,8 @@ ;; for bootstrap to load (let [core-js (closure/compile-file core (assoc opts - :output-file (closure/src-file->target-file core))) + :output-file + (closure/src-file->target-file core opts))) deps (closure/add-dependencies opts core-js)] (apply closure/output-unoptimized (assoc opts From 8d236b199725ddb51f87fd3e86ec24e1d737875b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 02:27:24 -0500 Subject: [PATCH 0390/4033] cljs.compiler/compile-file now supports :static-fns in opts, useful when compiling core for REPLs --- src/clj/cljs/compiler.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index f0665cfd1..aebf793e2 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -880,6 +880,7 @@ ana/*cljs-ns* 'cljs.user ana/*cljs-file* (.getPath ^File src) reader/*alias-map* (or reader/*alias-map* {}) + ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) *source-map-data* (when (:source-map opts) (atom {:source-map (sorted-map) From 2718781a203e9f8d7f8d8cefedafa52fee01fcf5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 02:32:15 -0500 Subject: [PATCH 0391/4033] change cljs.util/to-target-file cljs.closure/src-file->target-file so that if :output-dir not specified does not default to "out" --- src/clj/cljs/closure.clj | 5 ++--- src/clj/cljs/repl/node.clj | 3 ++- src/clj/cljs/util.clj | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 681093349..351817a15 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1045,12 +1045,11 @@ should contain the source for the given namespace name." (deps/parse-js-ns (line-seq (io/reader f)))) (defn ^File src-file->target-file - "Given a ClojureScript source file return the target file. May optionally - provide build options with :output-dir specified." ([src] (src-file->target-file src nil)) ([src opts] (util/to-target-file - (util/output-directory opts) + (when (:output-dir opts) + (util/output-directory opts)) (ana/parse-ns src)))) (defn ^String src-file->goog-require diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index f45c5174b..41ff95bac 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -88,7 +88,8 @@ (let [core-js (closure/compile-file core (assoc opts :output-file - (closure/src-file->target-file core opts))) + (closure/src-file->target-file core) + :static-fns true)) deps (closure/add-dependencies opts core-js)] (apply closure/output-unoptimized (assoc opts diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index fc2187c00..b2ef38801 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -61,8 +61,9 @@ (to-target-file target-dir ns-info "js")) ([target-dir ns-info ext] (let [relative-path (string/split (munge-path (str (:ns ns-info))) #"\.") - parents (butlast relative-path)] - (io/file (io/file (to-path (cons target-dir parents))) + parents (cond-> (butlast relative-path) + target-dir (conj target-dir))] + (io/file (io/file (to-path parents)) (str (last relative-path) (str "." ext)))))) (defn mkdirs From caaf970a503eaeae5acdd269e41f4f6bbb1c215c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 03:55:55 -0500 Subject: [PATCH 0392/4033] make `IJavaScriptEnv` `-load` signature consistent, the 2nd argument is goog *provides* not a single ns make sure that `opts` is properly threaded through for `cljs.repl/load-dependencies` and `cljs.repl/load-namespace` In `cljs.repl/load-namespace` if an `:output-dir` is specified emit `goog.addDependency` for each dependency computed from the dependecies compilation. In `cljs.repl/load-file` do a similar thing - emit the `goog.addDependency` for the file. For Node.js monkey-patch `goog.isProvided_` to suppress pointless errors about duplicate `goog.provide` statements for the same namespace. All built in browsers work. --- src/clj/cljs/repl.clj | 60 +++++++++++++++++++++-------------- src/clj/cljs/repl/browser.clj | 7 ++-- src/clj/cljs/repl/node.clj | 14 +++++--- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5bce7e9e9..5b25b53d1 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -26,7 +26,7 @@ (defprotocol IJavaScriptEnv (-setup [this] [this opts] "initialize the environment") (-evaluate [this filename line js] "evaluate a javascript string") - (-load [this ns url] "load code at url into the environment") + (-load [this provides url] "load code at url into the environment") (-tear-down [this] "dispose of the environment")) (defn- env->opts @@ -44,26 +44,33 @@ only once." ([repl-env sym] (load-namespace repl-env sym nil)) ([repl-env sym opts] - (let [sym (if (and (seq? sym) - (= (first sym) 'quote)) - (second sym) - sym) - deps (->> (cljsc/add-dependencies - ;; TODO: conceptually simpler to keep REPL environment - ;; separate from build options map - David - (merge (env->opts repl-env) opts) - {:requires [(name sym)] :type :seed}) - (remove (comp #{["goog"]} :provides)) - (remove (comp #{:seed} :type)) - (map #(select-keys % [:provides :url])))] - (doseq [{:keys [url provides]} deps] - (-load repl-env provides url))))) + (let [sym (if (and (seq? sym) + (= (first sym) 'quote)) + (second sym) + sym) + ;; TODO: add pre-condition to source-on-disk, the + ;; source must supply at least :url - David + sources (cljsc/add-dependencies + (merge (env->opts repl-env) opts) + {:requires [(name sym)] :type :seed + :url (:uri (cljsc/cljs-source-for-namespace sym))}) + deps (->> sources + (remove (comp #{["goog"]} :provides)) + (remove (comp #{:seed} :type)) + (map #(select-keys % [:provides :url])))] + ;; for now only do this for REPLs that rely on disk - David + (when (:output-dir opts) + (doseq [source (map #(cljsc/source-on-disk opts %) sources)] + (-evaluate repl-env "" 1 + (cljsc/add-dep-string opts source)))) + (doseq [{:keys [url provides]} deps] + (-load repl-env provides url))))) (defn- load-dependencies ([repl-env requires] (load-dependencies repl-env requires nil)) ([repl-env requires opts] - (doseq [ns requires] - (load-namespace repl-env ns opts)))) + (doseq [ns requires] + (load-namespace repl-env ns opts)))) (defn- display-error ([ret form] @@ -146,10 +153,13 @@ ([repl-env f] (load-file repl-env f nil)) ([repl-env f opts] (if (:output-dir opts) - (let [src (if (util/url? f) f (io/resource f))] - (comp/compile-file src - (cljsc/src-file->target-file src opts) opts) - (-evaluate repl-env f 0 (cljsc/src-file->goog-require src))) + (let [src (if (util/url? f) f (io/resource f)) + compiled (cljsc/compile-file src + (assoc opts + :output-file + (cljsc/src-file->target-file src)))] + (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled)) + (-evaluate repl-env f 1 (cljsc/src-file->goog-require src))) (binding [ana/*cljs-ns* 'cljs.user] (let [res (if (= \/ (first f)) f (io/resource f))] (assert res (str "Can't find " f " in classpath")) @@ -199,7 +209,8 @@ (with-meta `(~'ns ~ana/*cljs-ns* (:require ~spec)) - {:line 1 :column 1})))) + {:line 1 :column 1}) + identity opts))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace @@ -247,8 +258,9 @@ (evaluate-form repl-env env "" (with-meta '(ns cljs.user - (:require [cljs.repl :refer-macros [doc]])) - {:line 1 :column 1})) + (:require [cljs.repl :refer-macros [doc]])) + {:line 1 :column 1}) + identity opts) (loop [] (print (str "ClojureScript:" ana/*cljs-ns* "> ")) (flush) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index f6f0bcdb7..da45159b1 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -186,8 +186,8 @@ namespaces. Will load the JavaScript file into the REPL environment if any of the namespaces have not already been loaded from the ClojureScript REPL." - [repl-env ns-list url] - (let [missing (remove #(contains? @loaded-libs %) ns-list)] + [repl-env provides url] + (let [missing (remove #(contains? @loaded-libs %) provides)] (when (seq missing) (browser-eval (slurp url)) (swap! loaded-libs (partial apply conj) missing)))) @@ -200,7 +200,8 @@ (comp/with-core-cljs nil (fn [] (server/start this)))) (-evaluate [_ _ _ js] (browser-eval js)) - (-load [this ns url] (load-javascript this ns url)) + (-load [this provides url] + (load-javascript this provides url)) (-tear-down [_] (server/stop) (reset! server/state {}) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 41ff95bac..1bd2e4492 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -60,9 +60,10 @@ :value (read-response in)})) (defn load-javascript - "Load a JavaScript file into the Node REPL process." - [repl-env ns url] - (node-eval repl-env (slurp url))) + "Load a Closure JavaScript file into the Node REPL process." + [repl-env provides url] + (node-eval repl-env + (str "goog.require('" (comp/munge (first provides)) "')"))) (defn setup ([repl-env] (setup repl-env nil)) @@ -108,6 +109,9 @@ (str "require('" (.getPath root-path) File/separator "node_repl_deps.js')"))) + ;; monkey-patch isProvided_ to avoid useless warnings - David + (node-eval repl-env + (str "goog.isProvided_ = function(x) { return false; };")) (repl/evaluate-form repl-env env "" '(do @@ -123,8 +127,8 @@ (setup this opts)) (-evaluate [this filename line js] (node-eval this js)) - (-load [this ns url] - (load-javascript this ns url)) + (-load [this provides url] + (load-javascript this provides url)) (-tear-down [this] (close-socket socket))) From 7e72f3693e30ebc01cbf9199aa56a4a8f04f041c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 03:57:22 -0500 Subject: [PATCH 0393/4033] rename node_repl -> noderepljs --- script/{node_repl => noderepljs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename script/{node_repl => noderepljs} (100%) diff --git a/script/node_repl b/script/noderepljs similarity index 100% rename from script/node_repl rename to script/noderepljs From 6f2159ea4f321951bdc91d7e6b86c754c27d78b5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 09:26:13 -0500 Subject: [PATCH 0394/4033] no longer need to track loaded libs --- src/clj/cljs/repl/node.clj | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 1bd2e4492..f452b2faa 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -7,7 +7,6 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.repl.node - (:refer-clojure :exclude [loaded-libs]) (:require [clojure.string :as string] [clojure.java.io :as io] [cljs.env :as env] @@ -81,7 +80,7 @@ core (io/resource "cljs/core.cljs")] ;; TODO: temporary hack, should wait till we can read the start string ;; from the process - David - (Thread/sleep 1000) + (Thread/sleep 300) (reset! (:socket repl-env) (socket (:host repl-env) (:port repl-env))) ;; compile cljs.core & its dependencies, goog/base.js must be available @@ -119,7 +118,7 @@ (set! *print-fn* (.-print (js/require "util"))))) ))) -(defrecord NodeEnv [host port socket loaded-libs] +(defrecord NodeEnv [host port socket] repl/IJavaScriptEnv (-setup [this] (setup this)) @@ -133,7 +132,7 @@ (close-socket socket))) (defn repl-env* [{:keys [host port] :or {host "localhost" port 5001}}] - (NodeEnv. host port (atom nil) (atom {}))) + (NodeEnv. host port (atom nil))) (defn repl-env [& options] @@ -143,5 +142,4 @@ (def bldr (ProcessBuilder. (into-array ["node"]))) - ) From e156117c9daac3b0a4ec3ec630781c6405df191c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Dec 2014 12:00:36 -0500 Subject: [PATCH 0395/4033] add Companies Using ClojureScript link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e87f6e8a6..1c1652db4 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Latest stable release: 0.0-2511 * Read the [Documentation](https://github.com/clojure/clojurescript/wiki). * Try a [tutorial](https://github.com/clojure/clojurescript/wiki). * Look at the [Sample Applications](https://github.com/clojure/clojurescript/tree/master/samples). +* [Companies using ClojureScript](https://github.com/clojure/clojurescript/wiki/Companies-Using-ClojureScript) ## Questions, Feedback? ## From 2fc0250c86c61bddb79eb79e37d469e73b58f837 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 13:32:24 -0500 Subject: [PATCH 0396/4033] docstrings & pre-conditions for cljs.analyzer.api change cljs.analyzer/parse 'ns case to preserve original specs (for REPL support) add cljs.analyzer.api/ns-specs for getting original ns specs add cljs.analyzer.api/in-cljs-user for faster setup of typical analysis environment add test for cljs.analyzer.api/ns-specs --- src/clj/cljs/analyzer.clj | 1 + src/clj/cljs/analyzer/api.clj | 44 ++++++++++++++++++++++++++++---- test/clj/cljs/analyzer_tests.clj | 15 ++++++++++- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 542518a51..84e894c3d 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1237,6 +1237,7 @@ (when (seq use-macros) (check-use-macros use-macros env)) (swap! env/*compiler* update-in [::namespaces name] assoc + :specs args :name name :doc docstring :excludes excludes diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index af6369858..ea23fdf8c 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -11,17 +11,51 @@ (:require [cljs.env :as env] [cljs.analyzer :as ana])) -(defn resolve [env sym] +(defn resolve + "Given an analysis environment resolve a var. Analogous to + clojure.core/resolve" + [env sym] + {:pre [(map? env) (symbol? sym)]} (ana/resolve-var env sym)) -(defn all-ns [] +(defn all-ns + "Return all the namespace analysis maps. Analagous to clojure.core/all-ns but + returns analysis maps not Namespace instances." + [] (keys (get @env/*compiler* ::ana/namespaces))) -(defn find-ns [sym] +(defn find-ns + "Given a namespace return the corresponding namespace analysis map. Analagous + to clojure.core/find-ns." + [sym] + {:pre [(symbol? sym)]} (get-in @env/*compiler* [::ana/namespaces sym])) -(defn ns-interns [ns] +(defn ns-interns + "Given a namespace return all the var analysis maps. Analagous to + clojure.core/ns-interns but returns var analysis maps not vars." + [ns] + {:pre [(symbol? ns)]} (get-in @env/*compiler* [::ana/namespaces ns :defs])) -(defn ns-resolve [ns sym] +(defn ns-resolve + "Given a namespace and a symbol return the corresponding var analysis map. + Analagous to clojure.core/ns-resolve but returns var analysis map not Var." + [ns sym] + {:pre [(symbol? ns) (symbol? sym)]} (get-in @env/*compiler* [::ana/namespaces ns :defs sym])) + +(defn ns-specs + "Given a namespace return all the original specs for a namspace as originally + provided in the source." + [ns] + {:pre [(symbol? ns)]} + (get-in @env/*compiler* [::ana/namespaces ns :specs])) + +(defmacro in-cljs-user + "Binds cljs.analyzer/*cljs-ns* to 'cljs.user and uses the given compilation + environment atom and runs body." + [env & body] + `(binding [cljs.analyzer/*cljs-ns* 'cljs.user] + (cljs.env/with-compiler-env ~env + ~@body))) \ No newline at end of file diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index 9c7fa6057..b9265d194 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -1,7 +1,9 @@ (ns cljs.analyzer-tests (:require [clojure.java.io :as io] [cljs.analyzer :as a] - [cljs.env :as e]) + [cljs.env :as e] + [cljs.env :as env] + [cljs.analyzer.api :as ana-api]) (:use clojure.test)) ;;****************************************************************************** @@ -284,3 +286,14 @@ (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble {:foo baz})))) {:foo 'baz})))) + +(deftest test-namespace-specs + (a/no-warn + (ana-api/in-cljs-user test-cenv + (a/analyze test-env + '(ns foo.bar + (:refer-clojure :exclude [==]) + (:require [clojure.string :as string]))) + (is (= (ana-api/ns-specs 'foo.bar) + '((:refer-clojure :exclude [==]) + (:require [clojure.string :as string]))))))) From a6a4142fcc57b682976d4c4ac33fd20ac9276d70 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 14:35:41 -0500 Subject: [PATCH 0397/4033] add cljs.repl/update-require-spec helper, add tests --- src/clj/cljs/repl.clj | 12 ++++++++++++ test/clj/cljs/repl_tests.clj | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5b25b53d1..261dd6a65 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -183,6 +183,18 @@ form (wrap-fn form)))) +(defn update-require-spec [specs & additions] + (let [[before [requires & other-specs]] + (split-with + (fn [[x _]] (not= :require x)) + specs) + requires' + `(:require + ~@(concat + (rest requires) + additions))] + (concat before [requires'] other-specs))) + (def default-special-fns (let [load-file-fn (fn self diff --git a/test/clj/cljs/repl_tests.clj b/test/clj/cljs/repl_tests.clj index 44f1394b6..3ce863b25 100644 --- a/test/clj/cljs/repl_tests.clj +++ b/test/clj/cljs/repl_tests.clj @@ -22,3 +22,20 @@ (and file (is (io/resource file)))))) +(deftest test-update-require-spec + (is (= (repl/update-require-spec + '() + '[cljs.reader :as reader]) + '((:require [cljs.reader :as reader])))) + (is (= (repl/update-require-spec + '((:refer-clojure :exclude [==])) + '[cljs.reader :as reader]) + '((:refer-clojure :exclude [==]) + (:require [cljs.reader :as reader])))) + (is (= (repl/update-require-spec + '((:refer-clojure :exclude [==]) + (:require [clojure.string :as clojure.string])) + '[cljs.reader :as reader]) + '((:refer-clojure :exclude [==]) + (:require [clojure.string :as clojure.string] + [cljs.reader :as reader]))))) From 4427226d4e075463a0f4a3c149accc4174c4cd7d Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 14:45:12 -0500 Subject: [PATCH 0398/4033] Fix bug in REPL require special fn. Need to preserve the old specs of the current namespace. --- src/clj/cljs/repl.clj | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 261dd6a65..b6a35c4b0 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -13,6 +13,7 @@ (:require [clojure.java.io :as io] [cljs.compiler :as comp] [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] [cljs.env :as env] [cljs.tagged-literals :as tags] [cljs.closure :as cljsc] @@ -183,7 +184,10 @@ form (wrap-fn form)))) -(defn update-require-spec [specs & additions] +(defn update-require-spec + "Given the specification portion of a ns form and require spec additions + return an updated specification." + [specs & additions] (let [[before [requires & other-specs]] (split-with (fn [[x _]] (not= :require x)) @@ -217,12 +221,14 @@ ([repl-env env form] (self repl-env env form nil)) ([repl-env env [_ [quote spec]] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require ~spec)) - {:line 1 :column 1}) - identity opts))) + (let [original-specs (ana-api/ns-specs ana/*cljs-ns*) + new-specs (update-require-spec original-specs spec)] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + ~@new-specs) + {:line 1 :column 1}) + identity opts)))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace From f5d39e51afd8dc01c16f77ead0b9e3aa4e1304e1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 16:06:01 -0500 Subject: [PATCH 0399/4033] Node.js REPL source map support change cjls.compiler/compile-file* so that source maps are emitted if :source-map true but :optimizations left unspecified --- script/noderepljs | 2 +- src/clj/cljs/compiler.clj | 2 +- src/clj/cljs/repl/node.clj | 27 +++++++++++++-------------- src/clj/cljs/repl/node_repl.js | 5 +++++ 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/script/noderepljs b/script/noderepljs index ddb464e9e..474ce4628 100755 --- a/script/noderepljs +++ b/script/noderepljs @@ -12,4 +12,4 @@ done java -server -cp $CLJSC_CP clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.node :as node]) -(repl/repl* (node/repl-env $1) {:output-dir \".cljs_node_repl\" :cache-analysis true})" +(repl/repl* (node/repl-env $1) {:output-dir \".cljs_node_repl\" :cache-analysis true :source-map true})" diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index aebf793e2..8dcbf3e25 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -910,7 +910,7 @@ :source-file src} (when sm-data {:source-map (:source-map sm-data)}))] - (when (and sm-data (= (:optimizations opts) :none)) + (when (and sm-data (#{:none nil} (:optimizations opts))) (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] (emits "\n//# sourceMappingURL=" (.getName sm-file) (if (true? (:source-map-timestamp opts)) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index f452b2faa..cffc8702b 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -77,7 +77,9 @@ (.redirectError ProcessBuilder$Redirect/INHERIT)) proc (.start bldr) env (ana/empty-env) - core (io/resource "cljs/core.cljs")] + core (io/resource "cljs/core.cljs") + root-path (.getCanonicalFile output-dir) + rewrite-path (str (.getPath root-path) File/separator "goog")] ;; TODO: temporary hack, should wait till we can read the start string ;; from the process - David (Thread/sleep 300) @@ -97,22 +99,19 @@ deps)) ;; bootstrap, replace __dirname as __dirname won't be set ;; properly due to how we are running it - David - (let [root-path (.getCanonicalFile output-dir) - rewrite-path (str (.getPath root-path) File/separator "goog")] - (node-eval repl-env - (-> (slurp (io/resource "cljs/bootstrap_node.js")) - (string/replace "__dirname" - (str "\"" (str rewrite-path File/separator "bootstrap") "\"")) - (string/replace "./.." rewrite-path))) - (node-eval repl-env - (str "require('" - (.getPath root-path) - File/separator "node_repl_deps.js')"))) + (node-eval repl-env + (-> (slurp (io/resource "cljs/bootstrap_node.js")) + (string/replace "__dirname" + (str "\"" (str rewrite-path File/separator "bootstrap") "\"")) + (string/replace "./.." rewrite-path))) + (node-eval repl-env + (str "require('" + (.getPath root-path) + File/separator "node_repl_deps.js')")) ;; monkey-patch isProvided_ to avoid useless warnings - David (node-eval repl-env (str "goog.isProvided_ = function(x) { return false; };")) - (repl/evaluate-form repl-env - env "" + (repl/evaluate-form repl-env env "" '(do (.require js/goog "cljs.core") (set! *print-fn* (.-print (js/require "util"))))) diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index 3187f1718..6ba012a7e 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -2,6 +2,11 @@ process.env.NODE_DISABLE_COLORS = true; var net = require("net"); +try { + require("source-map-support").install(); +} catch(err) { +} + net.createServer(function (socket) { var buffer = "", ret; socket.setEncoding("utf8"); From 0da30f5aa3b937d1a1c01891cb4601be8a3ea210 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 16:34:04 -0500 Subject: [PATCH 0400/4033] fix source map conditional in cljs.compiler/compile-file* --- src/clj/cljs/compiler.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 8dcbf3e25..511292ece 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -910,7 +910,7 @@ :source-file src} (when sm-data {:source-map (:source-map sm-data)}))] - (when (and sm-data (#{:none nil} (:optimizations opts))) + (when (and sm-data (contains? #{:none nil} (:optimizations opts))) (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] (emits "\n//# sourceMappingURL=" (.getName sm-file) (if (true? (:source-map-timestamp opts)) From 137e2507cdfc7ecad71ee5c147c4e3c5367a275f Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 17:55:45 -0500 Subject: [PATCH 0401/4033] fix self-compile.sh --- script/self-compile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/self-compile.sh b/script/self-compile.sh index f552127b5..265fe216a 100755 --- a/script/self-compile.sh +++ b/script/self-compile.sh @@ -2,4 +2,4 @@ rm -rf classes mkdir classes -repl -e "(compile 'cljs.repl) (compile 'cljs.core)" \ No newline at end of file +./script/repl -e "(compile 'cljs.repl) (compile 'cljs.core)" From cb15822f30d9bc334063cd2a92cb3a262cd76b1a Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Dec 2014 17:57:04 -0500 Subject: [PATCH 0402/4033] rename self-compile.sh -> self-compile --- script/{self-compile.sh => self-compile} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename script/{self-compile.sh => self-compile} (100%) diff --git a/script/self-compile.sh b/script/self-compile similarity index 100% rename from script/self-compile.sh rename to script/self-compile From d3022838b7b2735a093db42ca524518c6a9c636f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 30 Dec 2014 09:01:48 -0500 Subject: [PATCH 0403/4033] Fix bug preventing Node REPL from using cached files Node.js compiled cljs.core w/o setting :optimizations. Change `cljs.compiler/compile-file` to merge `opts` with default optimization setting. Now source map handling logic and `cljs.compiler/require-compilation?` work correctly. --- src/clj/cljs/compiler.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 511292ece..935534cdf 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -910,7 +910,7 @@ :source-file src} (when sm-data {:source-map (:source-map sm-data)}))] - (when (and sm-data (contains? #{:none nil} (:optimizations opts))) + (when (and sm-data (= (:optimizations opts) :none)) (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] (emits "\n//# sourceMappingURL=" (.getName sm-file) (if (true? (:source-map-timestamp opts)) @@ -964,8 +964,9 @@ ([src dest] (compile-file src dest nil)) ([src dest opts] - (let [src-file (io/file src) - dest-file (io/file dest)] + (let [src-file (io/file src) + dest-file (io/file dest) + opts (merge {:optimizations :none} opts)] (if (.exists src-file) (try (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts)] From 6296384e536212c8997aa6fa05d2f6f29ffbb077 Mon Sep 17 00:00:00 2001 From: spinningtopsofdoom Date: Mon, 29 Dec 2014 15:00:17 -0600 Subject: [PATCH 0404/4033] Node REPL errors on exit The socket atom wasn't being dereferenced when being sent to close-socket --- src/clj/cljs/repl/node.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index cffc8702b..204afd8ac 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -128,7 +128,7 @@ (-load [this provides url] (load-javascript this provides url)) (-tear-down [this] - (close-socket socket))) + (close-socket @socket))) (defn repl-env* [{:keys [host port] :or {host "localhost" port 5001}}] (NodeEnv. host port (atom nil))) From baf7b331439f866b511d6a1e473f508747824aaa Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 30 Dec 2014 09:15:32 -0500 Subject: [PATCH 0405/4033] Clean up cljs.repl.node --- src/clj/cljs/repl/node.clj | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 204afd8ac..97163410d 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -9,18 +9,14 @@ (ns cljs.repl.node (:require [clojure.string :as string] [clojure.java.io :as io] - [cljs.env :as env] [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.repl :as repl] - [cljs.closure :as cljsc] - [cljs.closure :as closure] - [cljs.util :as util]) - (:import cljs.repl.IJavaScriptEnv - java.net.Socket + [cljs.closure :as closure]) + (:import java.net.Socket java.lang.StringBuilder [java.io File BufferedReader BufferedWriter] - [java.lang ProcessBuilder UNIXProcess ProcessBuilder$Redirect])) + [java.lang ProcessBuilder ProcessBuilder$Redirect])) (defn socket [host port] (let [socket (Socket. host port) @@ -93,6 +89,8 @@ (closure/src-file->target-file core) :static-fns true)) deps (closure/add-dependencies opts core-js)] + ;; output unoptimized code and the deps file + ;; for all compiled namespaces (apply closure/output-unoptimized (assoc opts :output-to (.getPath (io/file output-dir "node_repl_deps.js"))) @@ -104,6 +102,7 @@ (string/replace "__dirname" (str "\"" (str rewrite-path File/separator "bootstrap") "\"")) (string/replace "./.." rewrite-path))) + ;; load the deps file so we can goog.require cljs.core etc. (node-eval repl-env (str "require('" (.getPath root-path) @@ -111,6 +110,7 @@ ;; monkey-patch isProvided_ to avoid useless warnings - David (node-eval repl-env (str "goog.isProvided_ = function(x) { return false; };")) + ;; load cljs.core, setup printing (repl/evaluate-form repl-env env "" '(do (.require js/goog "cljs.core") @@ -137,8 +137,3 @@ [& options] (repl-env* options)) -(comment - - (def bldr (ProcessBuilder. (into-array ["node"]))) - - ) From c8b0d4494f238dc326ad461523377c62846e2121 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 30 Dec 2014 09:52:41 -0500 Subject: [PATCH 0406/4033] ignore node_modules, YourKit --- .gitignore | 1 + README.md | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/.gitignore b/.gitignore index e81910d6c..dc8165216 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ closure-release-* .nrepl-repl-history builds .cljs* +node_modules diff --git a/README.md b/README.md index 1c1652db4..ef9dd3a52 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,18 @@ in contributing to the project, please see the on the compiler and testing check the [Developer section of the wiki](http://github.com/clojure/clojurescript/wiki/Developers). +YourKit +---- + + + +YourKit has given an open source license for their profiler, greatly simplifying the profiling of ClojureScript performance. + +YourKit supports open source projects with its full-featured Java Profiler. +YourKit, LLC is the creator of YourKit Java Profiler +and YourKit .NET Profiler, +innovative and intelligent tools for profiling Java and .NET applications. + ## License ## Copyright (c) Rich Hickey. All rights reserved. The use and From 86fa7da377c94aa9bab5b7f53374d16aa9eb2298 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 30 Dec 2014 12:22:22 -0500 Subject: [PATCH 0407/4033] CLJS-935: script/noderepljs leaves node running after exit cljs/repl/node.cljs: add `proc` field to NodeEnv, set atom in setup, destroy process in -teardown --- src/clj/cljs/repl/node.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 97163410d..ef2d66d2b 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -81,6 +81,7 @@ (Thread/sleep 300) (reset! (:socket repl-env) (socket (:host repl-env) (:port repl-env))) + (reset! (:proc repl-env) proc) ;; compile cljs.core & its dependencies, goog/base.js must be available ;; for bootstrap to load (let [core-js (closure/compile-file core @@ -117,7 +118,7 @@ (set! *print-fn* (.-print (js/require "util"))))) ))) -(defrecord NodeEnv [host port socket] +(defrecord NodeEnv [host port socket proc] repl/IJavaScriptEnv (-setup [this] (setup this)) @@ -128,10 +129,11 @@ (-load [this provides url] (load-javascript this provides url)) (-tear-down [this] + (.destroy ^Process @proc) (close-socket @socket))) (defn repl-env* [{:keys [host port] :or {host "localhost" port 5001}}] - (NodeEnv. host port (atom nil))) + (NodeEnv. host port (atom nil) (atom nil))) (defn repl-env [& options] From 894b52291496a32f866286bc42a16ac08198641d Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 30 Dec 2014 14:33:21 -0500 Subject: [PATCH 0408/4033] tweak self compile to compile all REPLs --- script/self-compile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/self-compile b/script/self-compile index 265fe216a..04dec567e 100755 --- a/script/self-compile +++ b/script/self-compile @@ -2,4 +2,4 @@ rm -rf classes mkdir classes -./script/repl -e "(compile 'cljs.repl) (compile 'cljs.core)" +./script/repl -e "(compile 'cljs.repl.node) (compile 'cljs.repl.browser) (compile 'cljs.repl.rhino) (compile 'cljs.core)" From 80d5d106fd73d25c2ed7e8165947db930571f5b2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 11:30:18 -0500 Subject: [PATCH 0409/4033] Changes so that Node REPL works from JAR Node process needs a file - cannot load REPL server JS from JAR Switch to cljs.closure/compile for compiling cljs.core, cljs.closure/compile does the correct thing for JARs, writes them to disk first --- src/clj/cljs/closure.clj | 6 ++++++ src/clj/cljs/repl/node.clj | 12 +++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 351817a15..ea7168db4 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -33,6 +33,7 @@ The produced output is either a single string of optimized JavaScript or a deps file for use during development. " + (:refer-clojure :exclude [compile]) (:require [cljs.util :as util] [cljs.compiler :as comp] [cljs.analyzer :as ana] @@ -330,6 +331,11 @@ (do (swap! env/*compiler* update-in [::compiled-cljs] assoc path js) js))) +(defn compile + "Given a Compilable, compile it and return an IJavaScript." + [compilable opts] + (-compile compilable opts)) + (defn compile-file "Compile a single cljs file. If no output-file is specified, returns a string of compiled JavaScript. With an output-file option, the diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index ef2d66d2b..aa96ea517 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -65,10 +65,11 @@ ([repl-env opts] (let [output-dir (io/file (:output-dir opts)) _ (.mkdirs output-dir) + of (io/file output-dir "node_repl.js") + _ (spit of (slurp (io/resource "cljs/repl/node_repl.js"))) bldr (ProcessBuilder. (into-array ["node"])) _ (-> bldr - (.redirectInput - (io/file (io/resource "cljs/repl/node_repl.js"))) + (.redirectInput of) (.redirectOutput ProcessBuilder$Redirect/INHERIT) (.redirectError ProcessBuilder$Redirect/INHERIT)) proc (.start bldr) @@ -83,8 +84,9 @@ (socket (:host repl-env) (:port repl-env))) (reset! (:proc repl-env) proc) ;; compile cljs.core & its dependencies, goog/base.js must be available - ;; for bootstrap to load - (let [core-js (closure/compile-file core + ;; for bootstrap to load, use new closure/compile as it can handle + ;; resources in JARs + (let [core-js (closure/compile core (assoc opts :output-file (closure/src-file->target-file core) @@ -136,6 +138,6 @@ (NodeEnv. host port (atom nil) (atom nil))) (defn repl-env - [& options] + [& {:as options}] (repl-env* options)) From a9da41b67e3322ff7cae683760bef6b14781769d Mon Sep 17 00:00:00 2001 From: Steven Kallstrom Date: Wed, 15 Oct 2014 15:18:04 -0500 Subject: [PATCH 0410/4033] CLJS-675: QuickStart example not working properly Advanced compilation will fail if the output directory does not exist. Make output directory before writing constants_table.js to disk. This error does not occur when compiling with lein-cljsbuild which makes the output directory before calling build. --- src/clj/cljs/compiler.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 935534cdf..fded225c1 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1025,6 +1025,7 @@ (emits ";\n")))) (defn emit-constants-table-to-file [table dest] + (io/make-parents dest) (with-open [out ^java.io.Writer (io/make-writer dest {})] (binding [*out* out] (emit-constants-table table)))) From e36331b18c88d31b7cbbc1d2ba4c2cc78cffec6c Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 31 Dec 2014 17:57:40 +0100 Subject: [PATCH 0411/4033] optional macro loading parsing an 'ns has the side-effect of loading macros which in some cases is undesireable introduce *load-macros* to let tool authors disable this side effect --- src/clj/cljs/analyzer.clj | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 84e894c3d..ecfb63458 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -34,6 +34,7 @@ (def ^:dynamic *cljs-dep-set* (with-meta #{} {:dep-path []})) (def ^:dynamic *analyze-deps* true) (def ^:dynamic *load-tests* true) +(def ^:dynamic *load-macros* true) (def -cljs-macros-loaded (atom false)) @@ -1231,11 +1232,12 @@ (when (seq uses) (check-uses uses env)) (set! *cljs-ns* name) - (load-core) - (doseq [nsym (concat (vals require-macros) (vals use-macros))] - (clojure.core/require nsym)) - (when (seq use-macros) - (check-use-macros use-macros env)) + (when *load-macros* + (load-core) + (doseq [nsym (concat (vals require-macros) (vals use-macros))] + (clojure.core/require nsym)) + (when (seq use-macros) + (check-use-macros use-macros env))) (swap! env/*compiler* update-in [::namespaces name] assoc :specs args :name name From 133721a20a4879253b5ce5f913bd2f6555b8e3c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 17:18:53 -0500 Subject: [PATCH 0412/4033] CLJS-942: Randomized port for Node.js REPL if port not specified --- src/clj/cljs/repl/node.clj | 14 +++++++++++--- src/clj/cljs/repl/node_repl.js | 7 ++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index aa96ea517..e78255dea 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -66,7 +66,10 @@ (let [output-dir (io/file (:output-dir opts)) _ (.mkdirs output-dir) of (io/file output-dir "node_repl.js") - _ (spit of (slurp (io/resource "cljs/repl/node_repl.js"))) + _ (spit of + (string/replace (slurp (io/resource "cljs/repl/node_repl.js")) + "var PORT = 5001;" + (str "var PORT = " (:port repl-env) ";"))) bldr (ProcessBuilder. (into-array ["node"])) _ (-> bldr (.redirectInput of) @@ -134,8 +137,13 @@ (.destroy ^Process @proc) (close-socket @socket))) -(defn repl-env* [{:keys [host port] :or {host "localhost" port 5001}}] - (NodeEnv. host port (atom nil) (atom nil))) +(defn repl-env* [options] + (let [{:keys [host port]} + (merge + {:host "localhost" + :port (+ 49000 (rand-int 10000))} + options)] + (NodeEnv. host port (atom nil) (atom nil)))) (defn repl-env [& {:as options}] diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index 6ba012a7e..93857876f 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -1,6 +1,7 @@ process.env.NODE_DISABLE_COLORS = true; -var net = require("net"); +var net = require("net"); +var PORT = 5001; try { require("source-map-support").install(); @@ -39,6 +40,6 @@ net.createServer(function (socket) { } }); -}).listen(5001); +}).listen(PORT); -console.log("ClojureScript Node.js REPL server listening on 5001") +console.log("ClojureScript Node.js REPL server listening on", PORT); From 378a75e4b812ebc5a3596bc94d1afd3f6fb1506f Mon Sep 17 00:00:00 2001 From: Shaun LeBron Date: Wed, 31 Dec 2014 16:04:05 -0600 Subject: [PATCH 0413/4033] CLJS-941: Warn when a symbol is defined multiple times in a file "The only reason Clojure allows vars to be re-def-ed is so you can fix bugs in running programs." - Rich Hickey - warning is not emitted when redef'ing from a REPL - removed redundant `cljs.core` functions that triggered redef warnings - allow redefs inside an if-block for conditional def'ing --- src/clj/cljs/analyzer.clj | 19 +++++++++++++++++-- src/cljs/cljs/core.cljs | 19 ++----------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index ecfb63458..2a653b53b 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -45,6 +45,7 @@ :undeclared-ns true :undeclared-ns-form true :redef true + :redef-in-file true :dynamic true :fn-var true :fn-arity true @@ -91,6 +92,10 @@ (str (:sym info) " already refers to: " (symbol (str (:ns info)) (str (:sym info))) " being replaced by: " (symbol (str (:ns-name info)) (str (:sym info))))) +(defmethod error-message :redef-in-file + [warning-type info] + (str (:sym info) " at line " (:line info) " is being replaced")) + (defmethod error-message :fn-var [warning-type info] (str (symbol (str (:ns-name info)) (str (:sym info))) @@ -433,10 +438,14 @@ (def ^:dynamic *recur-frames* nil) (def ^:dynamic *loop-lets* ()) +(def ^:dynamic *allow-redef* false) (defmacro disallowing-recur [& body] `(binding [*recur-frames* (cons nil *recur-frames*)] ~@body)) +(defmacro allowing-redef [& body] + `(binding [*allow-redef* true] ~@body)) + ;; TODO: move this logic out - David (defn analyze-keyword [env sym] @@ -537,8 +546,8 @@ (when (< (count form) 3) (throw (error env "Too few arguments to if"))) (let [test-expr (disallowing-recur (analyze (assoc env :context :expr) test)) - then-expr (analyze env then) - else-expr (analyze env else)] + then-expr (allowing-redef (analyze env then)) + else-expr (allowing-redef (analyze env else))] {:env env :op :if :form form :test test-expr :then then-expr :else else-expr :unchecked @*unchecked-if* @@ -648,6 +657,12 @@ (when-let [doc (:doc args)] (when-not (string? doc) (throw (error env "Too many arguments to def")))) + (when-let [v (get-in @env/*compiler* [::namespaces ns-name :defs sym])] + (when (and (not *allow-redef*) + (not (:declared v)) + (not (:declared sym-meta)) + (not= "" *cljs-file*)) + (warning :redef-in-file env {:sym sym :line (:line v)}))) (let [env (if (or (and (not= ns-name 'cljs.core) (core-name? env sym)) (get-in @env/*compiler* [::namespaces ns-name :uses sym])) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 4c178ca04..c9dec2c0d 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1587,14 +1587,6 @@ reduces them without incurring seq initialization" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Seq fns ;;;;;;;;;;;;;;;; -(defn ^seq sequence - "Coerces coll to a (possibly empty) sequence, if it is not already - one. Will not force a lazy seq. (sequence nil) yields ()" - [coll] - (if (seq? coll) - coll - (or (seq coll) ()))) - (defn ^number compare "Comparator. Returns a negative number, zero, or a positive number when x is logically 'less than', 'equal to', or 'greater than' @@ -2023,15 +2015,6 @@ reduces them without incurring seq initialization" (let [q (quot n d)] (- n (* d q)))) -(defn ^number rand - "Returns a random floating point number between 0 (inclusive) and n (default 1) (exclusive)." - ([] (Math/random)) - ([n] (* n (rand)))) - -(defn rand-int - "Returns a random integer between 0 (inclusive) and n (exclusive)." - [n] (fix (rand n))) - (defn bit-xor "Bitwise exclusive or" [x y] (cljs.core/bit-xor x y)) @@ -8476,6 +8459,8 @@ reduces them without incurring seq initialization" (rf result input)))))))) ([coll] (sequence (dedupe) coll))) +(declare rand) + (defn random-sample "Returns items from coll with random probability of prob (0.0 - 1.0). Returns a transducer when no collection is provided." From afb1cece69c28d1c87666e5692b7dd69d6c79098 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 17:40:53 -0500 Subject: [PATCH 0414/4033] disable :redef-in-file for now until can determine why this is problematic in REPL scenarios --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 2a653b53b..436173d0f 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -45,7 +45,7 @@ :undeclared-ns true :undeclared-ns-form true :redef true - :redef-in-file true + :redef-in-file false :dynamic true :fn-var true :fn-arity true From 94604a241b122b01723af953438ae45f8931e429 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 18:01:55 -0500 Subject: [PATCH 0415/4033] `cljs.repl/load-file` needs to use `cljs.closure/compile` to get correct handling of files in JARs --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index b6a35c4b0..2ee23100e 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -155,7 +155,7 @@ ([repl-env f opts] (if (:output-dir opts) (let [src (if (util/url? f) f (io/resource f)) - compiled (cljsc/compile-file src + compiled (cljsc/compile src (assoc opts :output-file (cljsc/src-file->target-file src)))] From 96ba85b33afc2d6087e2baf6264c291ba9ef99ae Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 18:08:11 -0500 Subject: [PATCH 0416/4033] Rhino REPL needs to monkey-patch goog.isProvided_ as well --- src/clj/cljs/repl/rhino.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index de124668c..96c25c844 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -139,7 +139,9 @@ "out" (Context/javaToJS *out* scope)) (binding [ana/*cljs-ns* 'cljs.core] (repl/evaluate-form repl-env env "" - '(set! *print-fn* (fn [x] (.write js/out x)))))))) + '(do + (set! (.-isProvided_ js/goog) (fn [_] false)) + (set! *print-fn* (fn [x] (.write js/out x))))))))) (defrecord RhinoEnv [loaded-libs] repl/IJavaScriptEnv From 700a3f5a146486b96b4444164c5a2fb484965c1e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 18:30:44 -0500 Subject: [PATCH 0417/4033] clean up Rhino REPL, remove useless loaded lib tracking code --- src/clj/cljs/repl/rhino.clj | 83 ++++++++++++++----------------------- 1 file changed, 32 insertions(+), 51 deletions(-) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 96c25c844..e8939d5c4 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -10,18 +10,13 @@ (:require [clojure.string :as string] [clojure.java.io :as io] [cljs.compiler :as comp] - [cljs.closure :as closure] [cljs.analyzer :as ana] [cljs.repl :as repl] [cljs.util :as util]) - (:import cljs.repl.IJavaScriptEnv - [java.io File Reader] + (:import [java.io File Reader] [org.mozilla.javascript Context ScriptableObject RhinoException Undefined])) -(def current-repl-env (atom nil)) - -;;todo - move to core.cljs, using js (def ^String bootjs (str "goog.require = function(rule){" "Packages.clojure.lang.RT[\"var\"](\"cljs.repl.rhino\",\"goog-require\")" @@ -80,49 +75,37 @@ :stacktrace (stacktrace ex)}))) (defn goog-require [repl-env opts rule] - (let [loaded-libs (:loaded-libs repl-env)] - (when-not (contains? @loaded-libs rule) - (let [repl-env @current-repl-env - path (string/replace (comp/munge rule) \. File/separatorChar) - cljsc-path (str (util/output-directory opts) - File/separator (str path ".js")) - cljs-path (str path ".cljs") - js-path (str "goog/" - (-eval (str "goog.dependencies_.nameToPath['" rule "']") - repl-env "" 1))] - (let [compiled (io/file cljsc-path)] - (if (.exists compiled) - ;; TODO: only take this path if analysis cache is available - ;; - David - (do - (with-open [reader (io/reader compiled)] - (-eval reader repl-env cljsc-path 1)) - (swap! loaded-libs conj rule)) - (if-let [res (io/resource cljs-path)] - (binding [ana/*cljs-ns* 'cljs.user] - (repl/load-stream repl-env cljs-path res) - (swap! loaded-libs conj rule)) - (if-let [res (io/resource js-path)] - (with-open [reader (io/reader res)] - (-eval reader repl-env js-path 1) - (doseq [rule (closure/src-file->goog-require - res {:all-provides true})] - (swap! loaded-libs conj rule))) - (throw - (Exception. - (str "Cannot find " cljs-path - " or " js-path " in classpath"))))))))))) + (let [path (string/replace (comp/munge rule) \. File/separatorChar) + cljsc-path (str (util/output-directory opts) + File/separator (str path ".js")) + cljs-path (str path ".cljs") + js-path (str "goog/" + (-eval (str "goog.dependencies_.nameToPath['" rule "']") + repl-env "" 1))] + (let [compiled (io/file cljsc-path)] + (if (.exists compiled) + ;; TODO: only take this path if analysis cache is available + ;; - David + (do + (with-open [reader (io/reader compiled)] + (-eval reader repl-env cljsc-path 1))) + (if-let [res (io/resource cljs-path)] + (binding [ana/*cljs-ns* 'cljs.user] + (repl/load-stream repl-env cljs-path res)) + (if-let [res (io/resource js-path)] + (with-open [reader (io/reader res)] + (-eval reader repl-env js-path 1)) + (throw + (Exception. + (str "Cannot find " cljs-path + " or " js-path " in classpath"))))))))) (defn load-javascript [repl-env ns url] - (let [missing (remove #(contains? @(:loaded-libs repl-env) %) ns)] - (when (seq missing) - (do - (try - (with-open [reader (io/reader url)] - (-eval reader repl-env (.toString url) 1)) - ;; TODO: don't show errors for goog/base.js line number 105 - (catch Throwable ex (println (.getMessage ex)))) - (swap! (:loaded-libs repl-env) (partial apply conj) missing))))) + (try + (with-open [reader (io/reader url)] + (-eval reader repl-env (.toString url) 1)) + ;; TODO: don't show errors for goog/base.js line number 105 + (catch Throwable ex (println (.getMessage ex))))) (defn rhino-setup ([repl-env] (rhino-setup repl-env nil)) @@ -132,7 +115,6 @@ (ScriptableObject/putProperty scope "__repl_opts" (Context/javaToJS opts scope)) (repl/load-file repl-env "cljs/core.cljs" opts) - (swap! (:loaded-libs repl-env) conj "cljs.core") (repl/evaluate-form repl-env env "" '(ns cljs.user)) (ScriptableObject/putProperty scope @@ -143,7 +125,7 @@ (set! (.-isProvided_ js/goog) (fn [_] false)) (set! *print-fn* (fn [x] (.write js/out x))))))))) -(defrecord RhinoEnv [loaded-libs] +(defrecord RhinoEnv [] repl/IJavaScriptEnv (-setup [this] (rhino-setup this)) @@ -163,10 +145,9 @@ scope (.initStandardObjects cx) base (io/resource "goog/base.js") deps (io/resource "goog/deps.js") - new-repl-env (merge (RhinoEnv. (atom #{})) {:cx cx :scope scope})] + new-repl-env (merge (RhinoEnv.) {:cx cx :scope scope})] (assert base "Can't find goog/base.js in classpath") (assert deps "Can't find goog/deps.js in classpath") - (swap! current-repl-env (fn [old] new-repl-env)) (ScriptableObject/putProperty scope "___repl_env" (Context/javaToJS new-repl-env scope)) (with-open [r (io/reader base)] From 701d8f90eec9c8b49c4a891f5e7b727dc262a3e0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 19:19:15 -0500 Subject: [PATCH 0418/4033] alias checking was too simplistic, make more robust libs can now be required multiple times at the REPL --- src/clj/cljs/analyzer.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 436173d0f..4397e122f 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1153,12 +1153,13 @@ (parse-ns-error-msg spec ":as must be followed by a symbol in :require / :require-macros")))) (when alias - (let [alias-type (if macros? :macros :fns)] - (when (contains? (alias-type @aliases) alias) + (let [alias-type (if macros? :macros :fns) + lib' ((alias-type @aliases) alias)] + (when (and (not (nil? lib')) (not= lib lib')) (throw (error env (parse-ns-error-msg spec ":as alias must be unique")))) (swap! aliases update-in [alias-type] - conj alias))) + conj [alias lib]))) (when-not (or (and (sequential? referred) (every? symbol? referred)) (nil? referred)) @@ -1224,7 +1225,7 @@ name (vary-meta name merge metadata) excludes (parse-ns-excludes env args) deps (atom #{}) - aliases (atom {:fns #{} :macros #{}}) + aliases (atom {:fns {} :macros {}}) spec-parsers {:require (partial parse-require-spec env false deps aliases) :require-macros (partial parse-require-spec env true deps aliases) :use (comp (partial parse-require-spec env false deps aliases) From edc64f278a15a02675e53414d8a719d7e83959a1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 19:24:18 -0500 Subject: [PATCH 0419/4033] fix cljs.core/ns-interns macro, vars were not namespaced --- src/clj/cljs/core.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 43ed23a4f..3ea2836cf 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1679,7 +1679,8 @@ "Argument to ns-interns must be a quoted symbol") `(into {} [~@(map - (fn [[sym _]] `[(symbol ~(name sym)) (var ~sym)]) + (fn [[sym _]] + `[(symbol ~(name sym)) (var ~(symbol (name ns) (name sym)))]) (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))])) (defmacro vswap! From c2d8a638d17a03d58f3f909a3aa3f1d97c48c9be Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Dec 2014 19:49:21 -0500 Subject: [PATCH 0420/4033] exclude compile from closure_test.clj --- test/clj/cljs/closure_tests.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/test/clj/cljs/closure_tests.clj b/test/clj/cljs/closure_tests.clj index 4438e5ae4..a5312e8f2 100644 --- a/test/clj/cljs/closure_tests.clj +++ b/test/clj/cljs/closure_tests.clj @@ -1,4 +1,5 @@ (ns cljs.closure-tests + (:refer-clojure :exclude [compile]) (:use cljs.closure) (:use clojure.test)) From 08805fd519a46ca80aa95b9a50af8c8293df0d3f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 12:58:31 -0500 Subject: [PATCH 0421/4033] All errors encountered during ns parsing now throw. Only validate uses and use-macros if `cljs.analyzer/*analyze-deps* is true to support `cljs.analyzer/parse-ns` usage. --- src/clj/cljs/analyzer.clj | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 4397e122f..9e989c7cf 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1060,30 +1060,36 @@ f)))) (defn analyze-deps - ([lib deps env] (analyze-deps lib deps nil)) + ([lib deps env] (analyze-deps lib deps env)) ([lib deps env opts] (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] (assert (every? #(not (contains? *cljs-dep-set* %)) deps) (str "Circular dependency detected " (-> *cljs-dep-set* meta :dep-path))) (doseq [dep deps] (when-not (or (contains? (::namespaces @env/*compiler*) dep) - (contains? (:js-dependency-index @env/*compiler*) (name dep)) - (deps/find-classpath-lib dep)) + (contains? (:js-dependency-index @env/*compiler*) (name dep)) + (deps/find-classpath-lib dep)) (let [relpath (util/ns->relpath dep) src (locate-src relpath)] (if src (analyze-file src opts) - (warning :undeclared-ns env {:ns-sym dep})))))))) + (throw + (error env + (error-message :undeclared-ns {:ns-sym dep})))))))))) (defn check-uses [uses env] (doseq [[sym lib] uses] (when (= (get-in @env/*compiler* [::namespaces lib :defs sym] ::not-found) ::not-found) - (warning :undeclared-ns-form env {:type "var" :lib lib :sym sym})))) + (throw + (error env + (error-message :undeclared-ns-form {:type "var" :lib lib :sym sym})))))) (defn check-use-macros [use-macros env] (doseq [[sym lib] use-macros] (when (nil? (.findInternedVar ^clojure.lang.Namespace (find-ns lib) sym)) - (warning :undeclared-ns-form env {:type "macro" :lib lib :sym sym})))) + (throw + (error env + (error-message :undeclared-ns-form {:type "macro" :lib lib :sym sym})))))) (defn parse-ns-error-msg [spec msg] (str msg "; offending spec: " (pr-str spec))) @@ -1245,10 +1251,10 @@ {} (remove (fn [[r]] (= r :refer-clojure)) args))] (when (and *analyze-deps* (seq @deps)) (analyze-deps name @deps env opts)) - (when (seq uses) + (when (and *analyze-deps* (seq uses)) (check-uses uses env)) (set! *cljs-ns* name) - (when *load-macros* + (when (and *analyze-deps* *load-macros*) (load-core) (doseq [nsym (concat (vals require-macros) (vals use-macros))] (clojure.core/require nsym)) From 7722e5ce386e2952e7248649168b7322cfb81038 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 13:15:42 -0500 Subject: [PATCH 0422/4033] Update tests for parse ns throw change. --- src/clj/cljs/analyzer.clj | 3 ++- test/clj/cljs/analyzer_tests.clj | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 9e989c7cf..c2d670eff 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -218,7 +218,8 @@ (defmacro no-warn [& body] (let [no-warnings (zipmap (keys *cljs-warnings*) (repeat false))] - `(binding [*cljs-warnings* ~no-warnings] + `(binding [*cljs-warnings* ~no-warnings + *analyze-deps* false] ~@body))) (defmacro all-warn [& body] diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index b9265d194..011527436 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -111,7 +111,8 @@ (a/no-warn (e/with-compiler-env test-cenv - (a/analyze-file (io/file "src/cljs/cljs/core.cljs")))) + (binding [a/*analyze-deps* false] + (a/analyze-file (io/file "src/cljs/cljs/core.cljs"))))) (deftest basic-inference (is (= (e/with-compiler-env test-cenv From 5543a419985cdf1755fdb2665bce46a8b3e2d0df Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 13:19:52 -0500 Subject: [PATCH 0423/4033] add `cljs.repl/merge-spec` helper --- src/clj/cljs/repl.clj | 21 +++++++++++++++++++ test/clj/cljs/repl_tests.clj | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 2ee23100e..68bea0dbf 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -184,6 +184,27 @@ form (wrap-fn form)))) +(def spec-sort + {:as 0 + :refer 1 + :refer-macros 2 + :include-macros 3}) + +(defn merge-spec [[lib & {:as aindex}] [_ & {:as bindex}]] + (let [merged-map + (merge-with + (fn [x y] + (if (vector? x) + (into x y) + y)) + aindex bindex)] + (apply vector lib + (apply concat + (sort + (fn [[sa] [sb]] + (compare (spec-sort sa) (spec-sort sb))) + merged-map))))) + (defn update-require-spec "Given the specification portion of a ns form and require spec additions return an updated specification." diff --git a/test/clj/cljs/repl_tests.clj b/test/clj/cljs/repl_tests.clj index 3ce863b25..68859dc87 100644 --- a/test/clj/cljs/repl_tests.clj +++ b/test/clj/cljs/repl_tests.clj @@ -39,3 +39,43 @@ '((:refer-clojure :exclude [==]) (:require [clojure.string :as clojure.string] [cljs.reader :as reader]))))) + +(deftest test-merge-spec + (is (= (repl/merge-spec + '[clojure.string :refer [join]] + '[clojure.string :refer [trim]]) + '[clojure.string :refer [join trim]])) + (is (= (repl/merge-spec + '[cljs.repl :refer [print-doc]] + '[cljs.repl :refer-macros [doc]]) + '[cljs.repl :refer [print-doc] :refer-macros [doc]])) + (is (= (repl/merge-spec + '[cljs.repl :refer [print-doc] :include-macros true] + '[cljs.repl :refer-macros [doc]]) + '[cljs.repl :refer [print-doc] :refer-macros [doc] :include-macros true]))) + +#_(deftest test-merge-require + (is (= (repl/merge-require + '[[cljs.reader :as r] + [clojure.string :refer [join]] + [cljs.repl :refer-macros [doc]]] + '[clojure.string :refer [trim]]) + '[[cljs.reader :as r] + [clojure.string :refer [join trim]] + [cljs.repl :refer-macros [doc]]])) + (is (= (repl/merge-require + '[[cljs.reader :as r] + [clojure.string :refer [join]] + [cljs.repl :refer-macros [doc]]] + '[clojure.string :refer [join]]) + '[[cljs.reader :as r] + [clojure.string :refer [join]] + [cljs.repl :refer-macros [doc]]])) + (is (= (repl/merge-require + '[[cljs.reader :as r] + [clojure.string :refer [join]] + [cljs.repl :refer-macros [doc]]] + '[clojure.string :refer [join trim]]) + '[[cljs.reader :as r] + [clojure.string :refer [join trim]] + [cljs.repl :refer-macros [doc]]]))) From 7b4f79b5e4ab59ff90ea30e3e702a351fd16f5eb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 13:34:01 -0500 Subject: [PATCH 0424/4033] add `cljs.repl/merge-require` helper --- src/clj/cljs/repl.clj | 17 ++++++++++++++++- test/clj/cljs/repl_tests.clj | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 68bea0dbf..ea3c3c98e 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -195,7 +195,7 @@ (merge-with (fn [x y] (if (vector? x) - (into x y) + (vec (distinct (into x y))) y)) aindex bindex)] (apply vector lib @@ -205,6 +205,21 @@ (compare (spec-sort sa) (spec-sort sb))) merged-map))))) +(defn merge-require [requires [lib :as spec]] + (let [[before [match & after]] + (split-with + (fn [[lib' & _]] + (not= lib lib')) + requires)] + (if (nil? match) + ;; no match, append to end + (concat requires [spec]) + (if (= match spec) + ;; dupe + requires + ;; merge + (concat before [(merge-spec match spec)] after))))) + (defn update-require-spec "Given the specification portion of a ns form and require spec additions return an updated specification." diff --git a/test/clj/cljs/repl_tests.clj b/test/clj/cljs/repl_tests.clj index 68859dc87..3436895f6 100644 --- a/test/clj/cljs/repl_tests.clj +++ b/test/clj/cljs/repl_tests.clj @@ -54,7 +54,7 @@ '[cljs.repl :refer-macros [doc]]) '[cljs.repl :refer [print-doc] :refer-macros [doc] :include-macros true]))) -#_(deftest test-merge-require +(deftest test-merge-require (is (= (repl/merge-require '[[cljs.reader :as r] [clojure.string :refer [join]] From 1a7546ecba81683b67278a47dec490761e13718e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 13:40:52 -0500 Subject: [PATCH 0425/4033] CLJS-943: REPL require special fn is brittle use `cljs.repl/merge-require` for require special fn. --- src/clj/cljs/repl.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ea3c3c98e..ea2951cfb 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -230,7 +230,9 @@ specs) requires' `(:require - ~@(concat + ~@(reduce + (fn [requires spec] + (merge-require requires spec)) (rest requires) additions))] (concat before [requires'] other-specs))) @@ -258,7 +260,8 @@ (self repl-env env form nil)) ([repl-env env [_ [quote spec]] opts] (let [original-specs (ana-api/ns-specs ana/*cljs-ns*) - new-specs (update-require-spec original-specs spec)] + new-specs (update-require-spec original-specs + (if (symbol? spec) [spec] spec))] (evaluate-form repl-env env "" (with-meta `(~'ns ~ana/*cljs-ns* From 9a5454c68c6202a732a5b0220120bde63da9bd94 Mon Sep 17 00:00:00 2001 From: Shaun LeBron Date: Wed, 31 Dec 2014 18:04:06 -0600 Subject: [PATCH 0426/4033] CLJS-941: Warn when a symbol is defined multiple times in a file - re-enable :redef-in-file warning - don't warn when symbol is overriden by a definition in a different file - fixes the warning dumps when running the rhino REPL --- src/clj/cljs/analyzer.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index c2d670eff..62949fb2b 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -45,7 +45,7 @@ :undeclared-ns true :undeclared-ns-form true :redef true - :redef-in-file false + :redef-in-file true :dynamic true :fn-var true :fn-arity true @@ -662,6 +662,7 @@ (when (and (not *allow-redef*) (not (:declared v)) (not (:declared sym-meta)) + (= (:file v) *cljs-file*) (not= "" *cljs-file*)) (warning :redef-in-file env {:sym sym :line (:line v)}))) (let [env (if (or (and (not= ns-name 'cljs.core) From 79157c2fbc7aece8c3c13f9d78cc6bbb4cc3be37 Mon Sep 17 00:00:00 2001 From: Romain Date: Tue, 30 Dec 2014 15:48:59 +0100 Subject: [PATCH 0427/4033] CLJS-931 : cljs.compiler/requires-compilation? ignores changes to build options Store code-affecting build options in EDN format in output files, and update cljs.compiler/requires-compilation? to rebuild if the options differ. --- src/clj/cljs/compiler.clj | 8 +++++++- src/clj/cljs/util.clj | 11 +++++++++-- test/clj/cljs/compiler_tests.clj | 22 +++++++++++++++++++++- test/hello.cljs | 3 +++ 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 test/hello.cljs diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index fded225c1..d4b7c1c0b 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -869,6 +869,10 @@ (defn url-path [^File f] (.getPath (.toURL (.toURI f)))) +(defn- build-affecting-options + [opts] + (select-keys opts [:static-fns :optimize-constants :elide-asserts])) + (defn compile-file* ([src dest] (compile-file* src dest nil)) ([src dest opts] @@ -886,7 +890,7 @@ {:source-map (sorted-map) :gen-col 0 :gen-line 0}))] - (emitln "// Compiled by ClojureScript " (util/clojurescript-version)) + (emitln "// Compiled by ClojureScript " (util/clojurescript-version) " " (pr-str (build-affecting-options opts))) (loop [forms (ana/forms-seq src) ns-name nil deps nil] @@ -939,6 +943,8 @@ (let [version' (util/compiled-by-version dest) version (util/clojurescript-version)] (and version (not= version version'))) + (and opts + (not= (build-affecting-options opts) (build-affecting-options (util/build-options dest)))) (and opts (:source-map opts) (if (= (:optimizations opts) :none) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index b2ef38801..435cc59d2 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -8,7 +8,8 @@ (ns cljs.util (:require [clojure.java.io :as io] - [clojure.string :as string]) + [clojure.string :as string] + [clojure.edn :as edn]) (:import [java.io File] [java.net URL])) @@ -34,9 +35,15 @@ (defn compiled-by-version [^File f] (with-open [reader (io/reader f)] (let [match (->> reader line-seq first - (re-matches #".*ClojureScript (.*)$"))] + (re-matches #".*ClojureScript (\d+\.\d+-\d+).*$"))] (and match (second match))))) +(defn build-options [^File f] + (with-open [reader (io/reader f)] + (let [match (->> reader line-seq first + (re-matches #".*ClojureScript \d+\.\d+-\d+ (.*)$"))] + (and match (edn/read-string (second match)))))) + (defn munge-path [ss] (clojure.lang.Compiler/munge (str ss))) diff --git a/test/clj/cljs/compiler_tests.clj b/test/clj/cljs/compiler_tests.clj index c92bab66b..33664d96a 100644 --- a/test/clj/cljs/compiler_tests.clj +++ b/test/clj/cljs/compiler_tests.clj @@ -1,2 +1,22 @@ (ns cljs.compiler-tests - (:use clojure.test)) + (:use clojure.test) + (:require [cljs.compiler :as c]) + (:require [cljs.env :as e]) + (:require [cljs.util :as util]) + (:import (java.io File))) + +(deftest should-recompile + (let [src (File. "test/hello.cljs") + dst (File/createTempFile "compilertest" ".cljs") + opt {:optimize-constants true} + optmod {:optimize-constants true :elide-asserts false}] + (with-redefs [util/*clojurescript-version* {:major 0 :minor 0 :qualifier 42}] + (e/with-compiler-env (e/default-compiler-env) + (.setLastModified dst (- (.lastModified src) 100)) + (is (c/requires-compilation? src dst opt)) + (c/compile-file src dst opt) + (is (not (c/requires-compilation? src dst opt))) + (is (c/requires-compilation? src dst optmod)) + (c/compile-file src dst optmod) + (is (not (c/requires-compilation? src dst optmod))))))) + diff --git a/test/hello.cljs b/test/hello.cljs new file mode 100644 index 000000000..b7b12f75e --- /dev/null +++ b/test/hello.cljs @@ -0,0 +1,3 @@ +(ns hello) +(defn ^:export greet [n] + (str "Hello " n)) From bde0f7537b8cbbf82779a99ebabd0e140c94c037 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 14:21:22 -0500 Subject: [PATCH 0428/4033] disable CLJS-941 again --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 62949fb2b..f2247276f 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -45,7 +45,7 @@ :undeclared-ns true :undeclared-ns-form true :redef true - :redef-in-file true + :redef-in-file false :dynamic true :fn-var true :fn-arity true From d534a5631602a1f973a6804a2c468d40af6bb27a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 14:45:32 -0500 Subject: [PATCH 0429/4033] `cljs.util/compiled-by-version` needs to always return String --- src/clj/cljs/util.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 435cc59d2..4e5c20dd4 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -16,7 +16,7 @@ ;; next line is auto-generated by the build-script - Do not edit! (def ^:dynamic *clojurescript-version*) -(defn clojurescript-version +(defn ^String clojurescript-version "Returns clojurescript version as a printable string." [] (if (bound? #'*clojurescript-version*) @@ -32,11 +32,11 @@ "-SNAPSHOT")) "")) -(defn compiled-by-version [^File f] +(defn ^String compiled-by-version [^File f] (with-open [reader (io/reader f)] (let [match (->> reader line-seq first (re-matches #".*ClojureScript (\d+\.\d+-\d+).*$"))] - (and match (second match))))) + (or (and match (second match)) "")))) (defn build-options [^File f] (with-open [reader (io/reader f)] From 97733ebe76dc107d7e4ade05bef957c9c079dd15 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 14:53:22 -0500 Subject: [PATCH 0430/4033] Add `cljs.analyzer/*verbose*, if build or REPLs are passed :verbose true log basic analysis & compilation activity --- src/clj/cljs/analyzer.clj | 45 ++++++++++++++++++++++----------------- src/clj/cljs/closure.clj | 3 ++- src/clj/cljs/compiler.clj | 2 ++ 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index f2247276f..0b570cadd 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -20,7 +20,7 @@ [clojure.edn :as edn]) (:import java.lang.StringBuilder [java.io File Reader PushbackReader] - java.net.URL + [java.net URL] [cljs.tagged_literals JSValue])) (set! *warn-on-reflection* true) @@ -36,6 +36,9 @@ (def ^:dynamic *load-tests* true) (def ^:dynamic *load-macros* true) +;; log compiler activities +(def ^:dynamic *verbose* false) + (def -cljs-macros-loaded (atom false)) (def ^:dynamic *cljs-warnings* @@ -1781,14 +1784,14 @@ argument, which the reader will use in any emitted errors." ([f {:keys [output-dir] :as opts}] (let [res (cond (instance? File f) f - (instance? java.net.URL f) f - (re-find #"^file://" f) (java.net.URL. f) + (instance? URL f) f + (re-find #"^file://" f) (URL. f) :else (io/resource f))] (assert res (str "Can't find " f " in classpath")) (env/ensure (let [path (if (instance? File res) (.getPath ^File res) - (.getPath ^java.net.URL res)) + (.getPath ^URL res)) cache (when (and (:cache-analysis opts) output-dir) (cache-file res output-dir))] (when-not (get-in @env/*compiler* [::analyzed-cljs path]) @@ -1796,24 +1799,28 @@ argument, which the reader will use in any emitted errors." (not output-dir) (requires-analysis? res output-dir)) (binding [*cljs-ns* 'cljs.user - *cljs-file* path - reader/*alias-map* (or reader/*alias-map* {})] - (let [env (assoc (empty-env) :build-options opts) - ns (loop [ns nil forms (seq (forms-seq res))] - (if forms - (let [form (first forms) - env (assoc env :ns (get-namespace *cljs-ns*)) - ast (analyze env form opts)] - (if (= (:op ast) :ns) - (recur (:name ast) (next forms)) - (recur ns (next forms)))) - ns))] - (when cache - (write-analysis-cache ns cache)) - (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))) + *cljs-file* path + reader/*alias-map* (or reader/*alias-map* {})] + (when (or *verbose* (:verbose opts)) + (util/debug-prn "Analyzing " res)) + (let [env (assoc (empty-env) :build-options opts) + ns (loop [ns nil forms (seq (forms-seq res))] + (if forms + (let [form (first forms) + env (assoc env :ns (get-namespace *cljs-ns*)) + ast (analyze env form opts)] + (if (= (:op ast) :ns) + (recur (:name ast) (next forms)) + (recur ns (next forms)))) + ns))] + (when cache + (write-analysis-cache ns cache)) + (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))) ;; we want want to keep dependency analysis information ;; don't revert the environment - David (let [{:keys [ns]} (parse-ns res (merge opts {:restore false :analyze-deps true}))] + (when (or *verbose* (:verbose opts)) + (util/debug-prn "Reading analysis cache for " res)) (swap! env/*compiler* assoc-in [::analyzed-cljs path] true) (swap! env/*compiler* assoc-in [::namespaces ns] (edn/read-string (slurp cache))))))))))) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index ea7168db4..5ac98b5cd 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -994,7 +994,8 @@ should contain the source for the given namespace name." :undeclared-ns :undeclared-ns-form] (repeat warnings)) warnings))) - comp/*build-options* opts] + comp/*build-options* opts + ana/*verbose* (:verbose opts)] (let [compiled (util/measure compiler-stats "Compile basic sources" (doall (-compile source all-opts))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index d4b7c1c0b..dd1c2b293 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -879,6 +879,8 @@ (env/ensure (with-core-cljs opts (fn [] + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Compiling " src)) (with-open [out ^java.io.Writer (io/make-writer dest {})] (binding [*out* out ana/*cljs-ns* 'cljs.user From 61669143d512177b5d9b15025e34c1b27c99816d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 15:31:35 -0500 Subject: [PATCH 0431/4033] emit compiled by string for deps file under :none, does not fix CLJS-944 --- src/clj/cljs/closure.clj | 13 +++++++++---- src/clj/cljs/compiler.clj | 10 +++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 5ac98b5cd..a456bda84 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -747,10 +747,15 @@ should contain the source for the given namespace name." (deps-file {} [{:url (deps/to-url "out/cljs/core.js") :requires ["goog.string"] :provides ["cljs.core"]}]) ) -(defn output-one-file [{:keys [output-to]} js] - (cond (nil? output-to) js - (string? output-to) (spit output-to js) - :else (println js))) +(defn output-one-file [{:keys [output-to] :as opts} js] + (cond + (nil? output-to) js + + (string? output-to) + (spit output-to + (str (comp/compiled-by-string opts) "\n" js)) + + :else (println js))) (defn output-deps-file [opts sources] (output-one-file opts (deps-file opts sources))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index dd1c2b293..a3a7e7c87 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -873,6 +873,14 @@ [opts] (select-keys opts [:static-fns :optimize-constants :elide-asserts])) +(defn compiled-by-string + ([] (compiled-by-string nil)) + ([opts] + (str "// Compiled by ClojureScript " + (util/clojurescript-version) + (when opts + (str " " (pr-str (build-affecting-options opts))))))) + (defn compile-file* ([src dest] (compile-file* src dest nil)) ([src dest opts] @@ -892,7 +900,7 @@ {:source-map (sorted-map) :gen-col 0 :gen-line 0}))] - (emitln "// Compiled by ClojureScript " (util/clojurescript-version) " " (pr-str (build-affecting-options opts))) + (emitln (compiled-by-string opts)) (loop [forms (ana/forms-seq src) ns-name nil deps nil] From 7d8ef73b04d3dad7f6fec12a852c895ba53c303b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 16:35:06 -0500 Subject: [PATCH 0432/4033] don't optimize core for now, should be addressed separately for all builds --- src/clj/cljs/repl/node.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index e78255dea..2341f8ea3 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -93,7 +93,8 @@ (assoc opts :output-file (closure/src-file->target-file core) - :static-fns true)) + ;:static-fns true + )) deps (closure/add-dependencies opts core-js)] ;; output unoptimized code and the deps file ;; for all compiled namespaces From 077ccaad863e0fdb0533e13a82e46963022cb018 Mon Sep 17 00:00:00 2001 From: Benjamin C Meyer Date: Sat, 8 Oct 2011 22:03:39 -0400 Subject: [PATCH 0433/4033] Fix spelling mistakes in function documentation. --- src/clj/cljs/closure.clj | 4 ++-- src/cljs/cljs/core.cljs | 6 +++--- src/cljs/cljs/reader.cljs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index a456bda84..752aae3bc 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -192,7 +192,7 @@ (defn load-externs "Externs are JavaScript files which contain empty definitions of - functions which will be provided by the envorinment. Any function in + functions which will be provided by the environment. Any function in an extern file will not be renamed during optimization. Options may contain an :externs key with a list of file paths to @@ -480,7 +480,7 @@ should contain the source for the given namespace name." "Given a list of all required namespaces, return a list of IJavaScripts which are the cljs dependencies. The returned list will not only include the explicitly required files but any transitive - depedencies as well. JavaScript files will be compiled to the + dependencies as well. JavaScript files will be compiled to the working directory if they do not already exist. Only load dependencies from the classpath." diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index c9dec2c0d..989dcb003 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1641,7 +1641,7 @@ reduces them without incurring seq initialization" (defn sort "Returns a sorted sequence of the items in coll. Comp can be - boolean-valued comparison funcion, or a -/0/+ valued comparator. + boolean-valued comparison function, or a -/0/+ valued comparator. Comp defaults to compare." ([coll] (sort compare coll)) @@ -2185,7 +2185,7 @@ reduces them without incurring seq initialization" (defn- extend-object! "Takes a JavaScript object and a map of names to functions and attaches said functions as methods on the object. Any references to - JavaScript's implict this (via the this-as macro) will resolve to the + JavaScript's implicit this (via the this-as macro) will resolve to the object that the function is attached." [obj fn-map] (doseq [[key-name f] fn-map] @@ -3959,7 +3959,7 @@ reduces them without incurring seq initialization" "Returns a lazy sequence of lists of n items each, at offsets step apart. If step is not supplied, defaults to n, i.e. the partitions do not overlap. If a pad collection is supplied, use its elements as - necessary to complete last partition upto n items. In case there are + necessary to complete last partition up to n items. In case there are not enough padding elements, return a partition with less than n items." ([n coll] (partition n n coll)) diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs index 2998e6c92..d4cd47d34 100644 --- a/src/cljs/cljs/reader.cljs +++ b/src/cljs/cljs/reader.cljs @@ -259,7 +259,7 @@ nil if the end of stream has been reached") (defn read-unmatched-delimiter [rdr ch] - (reader-error rdr "Unmached delimiter " ch)) + (reader-error rdr "Unmatched delimiter " ch)) (defn read-list [rdr _] From 3739a20cba48b18ad739928ba1696c2e401b6613 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 17:08:58 -0500 Subject: [PATCH 0434/4033] 0.0-2629 --- README.md | 6 +++--- changes.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ef9dd3a52..9c51dd005 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2511 +Latest stable release: 0.0-2629 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2511"] +[org.clojure/clojurescript "0.0-2629"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2511 org.clojure clojurescript - 0.0-2511 + 0.0-2629 ``` diff --git a/changes.md b/changes.md index a53feaaec..7d43f8458 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,32 @@ +## 0.0-2629 + +### Enhancements +* Add Node.js REPL +* REPLs can now reuse build/analysis caching +* in-ns, require, doc support in REPLs + +### Changes +* add :verbose flag to compiler to output compiler activity +* add *load-macros* to cljs.analyzer to optionally disable macro loading +* errors during ns parsing always through +* `cljs.util/compiled-by-version` needs to always return String +* pin Closure Compiler in bootstrap script +* refactor cljs.build.api namespace + +### Fixes +* add cljs.test/are macro +* CLJS-931 : cljs.compiler/requires-compilation? ignores changes to build options +* CLJS-943: REPL require special fn is brittle +* CLJS-941: Warn when a symbol is defined multiple times in a file +* CLJS-942: Randomized port for Node.js REPL if port not specified +* CLJS-675: QuickStart example not working properly +* CLJS-935: script/noderepljs leaves node running after exit +* CLJS-918: preserve :arglists metadata in analysis cache +* CLJS-907: False positives from arithmetic checks +* CLJS-919 compare-and-set! relies on Atom record structure instead of protocols +* CLJS-920 add-watch/remove-watch should return reference, as in Clojure +* CLJS-921: cljs.repl/doc output includes namespace twice + ## 0.0-2511 ### Enhancements From 4665f9a787c2bcb9d76066ba9ddccb89cf378dc3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Jan 2015 23:22:34 -0500 Subject: [PATCH 0435/4033] CLJS-946: goog.require in REPLs will not reload recompiled libs Node.js specific issue, delete require.cache entry when reloading --- src/clj/cljs/repl/node.clj | 5 ++++- src/cljs/cljs/bootstrap_node.js | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 2341f8ea3..cd251c638 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -108,7 +108,10 @@ (-> (slurp (io/resource "cljs/bootstrap_node.js")) (string/replace "__dirname" (str "\"" (str rewrite-path File/separator "bootstrap") "\"")) - (string/replace "./.." rewrite-path))) + (string/replace "./.." rewrite-path) + (string/replace + "var CLJS_ROOT = \"./\";" + (str "var CLJS_ROOT = \"" (.getPath root-path) "/\";")))) ;; load the deps file so we can goog.require cljs.core etc. (node-eval repl-env (str "require('" diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index 076203791..f9b6616c3 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -42,6 +42,7 @@ var fs = require('fs'); var path = require('path'); +var CLJS_ROOT = "./"; /** @@ -59,6 +60,15 @@ global.goog = {}; global.CLOSURE_IMPORT_SCRIPT = function(src) { // Sources are always expressed relative to closure's base.js, but // require() is always relative to the current source. + if(CLJS_ROOT !== ".") { + var path = null; + if(src.substring(0, 3) == "../") { + path = CLJS_ROOT+src.substring(3); + } else { + path = CLJS_ROOT+"goog/"+src; + } + if(require.cache[path]) require.cache[path]; + } require('./../' + src); return true; }; From 6aaaabe0fb8727ac762343b77c36c16288448589 Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Fri, 26 Dec 2014 09:17:37 +0200 Subject: [PATCH 0436/4033] CLJS-929: Minor fixes to test script $[], used in ./script/test, doesn't work in ash or dash. This is a problem for users of Ubuntu and other Debian-based Linux distros, since they ship with dash as the default /bin/sh. (Some other UNIX-like OSs, including various flavors of BSD, as well as Minix, also use ash.) $(()) works in ash, dash, bash, and zsh. --- script/test | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/script/test b/script/test index 0f7d439e8..d3efd5f6c 100755 --- a/script/test +++ b/script/test @@ -14,7 +14,7 @@ if [ "$V8_HOME" = "" ]; then else echo "Testing with V8" "${V8_HOME}/d8" builds/out-adv/core-advanced-test.js - ran=$[ran+1] + ran=$((ran+1)) fi if [ "$SPIDERMONKEY_HOME" = "" ]; then @@ -22,7 +22,7 @@ if [ "$SPIDERMONKEY_HOME" = "" ]; then else echo "Testing with SpiderMonkey" ${SPIDERMONKEY_HOME}/js -f builds/out-adv/core-advanced-test.js - ran=$[ran+1] + ran=$((ran+1)) fi if [ "$JSC_HOME" = "" ]; then @@ -30,7 +30,7 @@ if [ "$JSC_HOME" = "" ]; then else echo "Testing with JavaScriptCore" "${JSC_HOME}/jsc" -f builds/out-adv/core-advanced-test.js - ran=$[ran+1] + ran=$((ran+1)) fi if [ "$NASHORN_HOME" = "" ]; then @@ -38,7 +38,7 @@ if [ "$NASHORN_HOME" = "" ]; then else echo "Testing with Nashorn" "${NASHORN_HOME}/jjs" builds/out-adv/core-advanced-test.js - ran=$[ran+1] + ran=$((ran+1)) fi echo "Tested with $ran out of $possible possible js targets" From af077059b8292b81887eb96d606d6f3e7d7674d9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 12:23:52 -0500 Subject: [PATCH 0437/4033] CLJS-950: Revert adding compiled-by string to CLJS deps file Breaks hashbang support for Node.js, more importantly CLJS-944 is not a ClojureScript problem it's a cljsbuild one --- src/clj/cljs/closure.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 752aae3bc..dfea50d2e 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -752,8 +752,7 @@ should contain the source for the given namespace name." (nil? output-to) js (string? output-to) - (spit output-to - (str (comp/compiled-by-string opts) "\n" js)) + (spit output-to js) :else (println js))) From 187a45ff599eafd9e8b4f95bb31778a84ac9322f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 13:52:01 -0500 Subject: [PATCH 0438/4033] CLJS-946: goog.require in REPLs will not reload recompiled libs monkey-patch goog.require under Node.js REPL to allow reloading of namespaces. Uncovered a different issue which that multiple goog.require's are emitted for a single require expression. --- src/clj/cljs/repl/node.clj | 7 +++++++ src/cljs/cljs/bootstrap_node.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index cd251c638..80d2b6caf 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -125,6 +125,13 @@ '(do (.require js/goog "cljs.core") (set! *print-fn* (.-print (js/require "util"))))) + ;; monkey-patch goog.require, skip all the loaded checks + (repl/evaluate-form repl-env env "" + '(do + (set! (.-require js/goog) + (fn [name] + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name)))))) ))) (defrecord NodeEnv [host port socket proc] diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index f9b6616c3..861b3cb15 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -67,7 +67,7 @@ global.CLOSURE_IMPORT_SCRIPT = function(src) { } else { path = CLJS_ROOT+"goog/"+src; } - if(require.cache[path]) require.cache[path]; + if(require.cache[path]) delete require.cache[path]; } require('./../' + src); return true; From bc4f38445497b10e8356aba64286dae995059383 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 15:07:26 -0500 Subject: [PATCH 0439/4033] rename verbose REPL option verboes-repl as to not clash with compiler option verbose --- src/clj/cljs/repl.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ea2951cfb..14b84b228 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -287,14 +287,14 @@ (ana/analyze-file (str "file://" (.getAbsolutePath file)))))) (defn repl* - [repl-env {:keys [analyze-path verbose warn-on-undeclared special-fns static-fns] :as opts + [repl-env {:keys [analyze-path verbose-repl warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}}] (print "To quit, type: ") (prn :cljs/quit) (env/with-compiler-env (or (::env/compiler repl-env) (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user - *cljs-verbose* verbose + *cljs-verbose* verbose-repl ana/*cljs-warnings* (assoc ana/*cljs-warnings* :unprovided warn-on-undeclared :undeclared-var warn-on-undeclared From 0f748de8596447a639cbb832e51df9b84184ece3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 15:38:21 -0500 Subject: [PATCH 0440/4033] use console.error in node_repl.js --- src/clj/cljs/repl/node_repl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index 93857876f..d45d8bc6d 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -27,7 +27,7 @@ net.createServer(function (socket) { ret = process.binding('evals').NodeScript.runInThisContext.call( global, data, "repl"); } catch (x) { - console.log(x.stack); + console.error(x.stack); } } // TODO: can we just console.log? - David From c597e85bf3195569b000055a5224b8c2b4e788b2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 15:47:16 -0500 Subject: [PATCH 0441/4033] monkey-patch goog.require before loading core, otherwise stack overflow --- src/clj/cljs/repl/node.clj | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 80d2b6caf..c866a6a7b 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -120,18 +120,19 @@ ;; monkey-patch isProvided_ to avoid useless warnings - David (node-eval repl-env (str "goog.isProvided_ = function(x) { return false; };")) - ;; load cljs.core, setup printing - (repl/evaluate-form repl-env env "" - '(do - (.require js/goog "cljs.core") - (set! *print-fn* (.-print (js/require "util"))))) ;; monkey-patch goog.require, skip all the loaded checks (repl/evaluate-form repl-env env "" '(do (set! (.-require js/goog) (fn [name] (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name)))))) + (aget (.. js/goog -dependencies_ -nameToPath) name)))) + nil)) + ;; load cljs.core, setup printing + (repl/evaluate-form repl-env env "" + '(do + (.require js/goog "cljs.core") + (set! *print-fn* (.-print (js/require "util"))))) ))) (defrecord NodeEnv [host port socket proc] From d9860acffdbe371f1d59ad8aedf6628b3f1745ef Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 15:58:26 -0500 Subject: [PATCH 0442/4033] cljs.repl/load-dependencies needs distinct around requires --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 14b84b228..c53c5c249 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -70,7 +70,7 @@ (defn- load-dependencies ([repl-env requires] (load-dependencies repl-env requires nil)) ([repl-env requires opts] - (doseq [ns requires] + (doseq [ns (distinct requires)] (load-namespace repl-env ns opts)))) (defn- display-error From 251b76be8d7c072e272ca8d90edec64d6d18d5b6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 16:31:09 -0500 Subject: [PATCH 0443/4033] CLJS-951: goog.require emitted multiple times under Node.js REPL --- src/clj/cljs/repl.clj | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c53c5c249..c40f390e2 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -59,13 +59,15 @@ (remove (comp #{["goog"]} :provides)) (remove (comp #{:seed} :type)) (map #(select-keys % [:provides :url])))] - ;; for now only do this for REPLs that rely on disk - David - (when (:output-dir opts) + (if (:output-dir opts) + ;; REPLs that read from :output-dir just need to add deps, + ;; environment will handle actual loading - David (doseq [source (map #(cljsc/source-on-disk opts %) sources)] (-evaluate repl-env "" 1 - (cljsc/add-dep-string opts source)))) - (doseq [{:keys [url provides]} deps] - (-load repl-env provides url))))) + (cljsc/add-dep-string opts source))) + ;; REPLs that stream must manually load each dep - David + (doseq [{:keys [url provides]} deps] + (-load repl-env provides url)))))) (defn- load-dependencies ([repl-env requires] (load-dependencies repl-env requires nil)) From 935b0e466c6bd327799ca0a04f062b8aa23dcebe Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 17:29:15 -0500 Subject: [PATCH 0444/4033] CLJS-947: REPL require of goog namespaces does not work Add generic `cljs.closure/source-for-namespace` that works for both CLJS & goog namespaces Use this in `cljs.repl/load-namespace` --- src/clj/cljs/closure.clj | 13 +++++++++++++ src/clj/cljs/repl.clj | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index dfea50d2e..54ac21c56 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -476,6 +476,19 @@ should contain the source for the given namespace name." (str % ".cljs") {:relative-path % :uri (io/resource %)})) +(defn source-for-namespace + [ns compiler-env] + (let [ns-str (str (comp/munge ns)) + path (string/replace ns-str \. File/separatorChar) + relpath (str path ".cljs")] + (if-let [cljs-res (io/resource relpath)] + {:relative-path relpath :uri cljs-res} + (let [relpath (:file (get-in @compiler-env [:js-dependency-index ns-str]))] + (if-let [js-res (io/resource relpath)] + {:relative-path relpath :uri js-res} + (throw + (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))) + (defn cljs-dependencies "Given a list of all required namespaces, return a list of IJavaScripts which are the cljs dependencies. The returned list will diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c40f390e2..9bd54e94c 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -54,7 +54,8 @@ sources (cljsc/add-dependencies (merge (env->opts repl-env) opts) {:requires [(name sym)] :type :seed - :url (:uri (cljsc/cljs-source-for-namespace sym))}) + :url (:uri (cljsc/source-for-namespace + sym env/*compiler*))}) deps (->> sources (remove (comp #{["goog"]} :provides)) (remove (comp #{:seed} :type)) From a7c45c7196801823e32ab087cf4960dab03c44d5 Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Fri, 2 Jan 2015 14:37:11 -0600 Subject: [PATCH 0445/4033] CLJS-952: Bad type hinting on bit-test bit-test returns a boolean, but is incorrectly type-hinted as returning a number. --- src/clj/cljs/core.clj | 4 ++-- src/cljs/cljs/core.cljs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 3ea2836cf..33a0e6ebf 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -519,8 +519,8 @@ (defmacro ^::ana/numeric bit-flip [x n] (core/list 'js* "(~{} ^ (1 << ~{}))" x n)) -(defmacro ^::ana/numeric bit-test [x n] - (core/list 'js* "((~{} & (1 << ~{})) != 0)" x n)) +(defmacro bit-test [x n] + (bool-expr (core/list 'js* "((~{} & (1 << ~{})) != 0)" x n))) (defmacro ^::ana/numeric bit-shift-left [x n] (core/list 'js* "(~{} << ~{})" x n)) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 989dcb003..b8841dce4 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2050,7 +2050,7 @@ reduces them without incurring seq initialization" [x n] (cljs.core/bit-set x n)) -(defn bit-test +(defn ^boolean bit-test "Test bit at index n" [x n] (cljs.core/bit-test x n)) From 02524d15db5e25dff4c1934ae4b28a2010ea2fb9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 17:44:16 -0500 Subject: [PATCH 0446/4033] CLJS-953: require REPL special fn can only take one argument --- src/clj/cljs/repl.clj | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 9bd54e94c..5c83beb83 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -261,10 +261,13 @@ (fn self ([repl-env env form] (self repl-env env form nil)) - ([repl-env env [_ [quote spec]] opts] - (let [original-specs (ana-api/ns-specs ana/*cljs-ns*) - new-specs (update-require-spec original-specs - (if (symbol? spec) [spec] spec))] + ([repl-env env [_ & specs :as form] opts] + (let [new-specs + (reduce + (fn [requires [quote spec]] + (update-require-spec requires + (if (symbol? spec) [spec] spec))) + (ana-api/ns-specs ana/*cljs-ns*) specs)] (evaluate-form repl-env env "" (with-meta `(~'ns ~ana/*cljs-ns* From 256480104941545fcf47f122d47a52f679147f01 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 17:51:56 -0500 Subject: [PATCH 0447/4033] update browser REPL README --- samples/repl/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/repl/README.md b/samples/repl/README.md index 808bf1556..728b5da02 100644 --- a/samples/repl/README.md +++ b/samples/repl/README.md @@ -57,9 +57,9 @@ cd samples/repl and evaluate the following forms: ```clj -(use 'cljs.closure) +(require '[cljs.closure :as cljsc]) (def opts {:output-to "main.js" :output-dir "out"}) -(build "src" opts) +(cljsc/build "src" opts) ``` ### Starting the REPL and connecting to the browser From 9f8d6524b840cfe7a53a207fb234ba9dfe180c0c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jan 2015 18:06:21 -0500 Subject: [PATCH 0448/4033] 0.0-2644 --- README.md | 6 +++--- changes.md | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9c51dd005..bf64fc629 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2629 +Latest stable release: 0.0-2644 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2629"] +[org.clojure/clojurescript "0.0-2644"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2629 org.clojure clojurescript - 0.0-2629 + 0.0-2644 ``` diff --git a/changes.md b/changes.md index 7d43f8458..5a4e2eb10 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,15 @@ +## 0.0-2644 + +### Fixes +* CLJS-953: require REPL special fn can only take one argument +* CLJS-952: Bad type hinting on bit-test +* CLJS-947: REPL require of goog namespaces does not work +* CLJS-951: goog.require emitted multiple times under Node.js REPL +* CLJS-946: goog.require in REPLs will not reload recompiled libs +* CLJS-950: Revert adding compiled-by string to CLJS deps file +* CLJS-929: Minor fixes to test script +* CLJS-946: goog.require in REPLs will not reload recompiled libs + ## 0.0-2629 ### Enhancements From bceaac8b86f40c5a08355b30cf03430f2946415b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 11:59:28 -0500 Subject: [PATCH 0449/4033] Remove convoluted ns merging support from cljs.repl, move this logic directly into cljs.analyzer, supported via ns form :merge metadata This is prep work for CLJS-954 --- src/clj/cljs/analyzer.clj | 38 +++++++++++------ src/clj/cljs/analyzer/api.clj | 7 ---- src/clj/cljs/repl.clj | 71 +++----------------------------- test/clj/cljs/analyzer_tests.clj | 11 ----- test/clj/cljs/repl_tests.clj | 58 -------------------------- 5 files changed, 32 insertions(+), 153 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0b570cadd..5e1fa6693 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1232,6 +1232,7 @@ (let [docstring (if (string? (first args)) (first args)) args (if docstring (next args) args) metadata (if (map? (first args)) (first args)) + form-meta (meta form) args (desugar-ns-specs (if metadata (next args) args)) name (vary-meta name merge metadata) excludes (parse-ns-excludes env args) @@ -1265,18 +1266,31 @@ (clojure.core/require nsym)) (when (seq use-macros) (check-use-macros use-macros env))) - (swap! env/*compiler* update-in [::namespaces name] assoc - :specs args - :name name - :doc docstring - :excludes excludes - :uses uses - :requires requires - :use-macros use-macros - :require-macros require-macros - :imports imports) - {:env env :op :ns :form form :name name :doc docstring :uses uses :requires requires :imports imports - :use-macros use-macros :require-macros require-macros :excludes excludes})) + (let [ns-info + {:name name + :doc docstring + :excludes excludes + :use-macros use-macros + :require-macros require-macros + :uses uses + :requires requires + :imports imports} + ns-info + (if (:merge form-meta) + ;; for merging information in via require usage in REPLs + (let [ns-info' (get-in @env/*compiler* [::namespaces name])] + (if (pos? (count ns-info')) + (let [merge-keys + [:use-macros :require-macros :uses :requires :imports]] + (merge + ns-info' + (merge-with merge + (select-keys ns-info' merge-keys) + (select-keys ns-info merge-keys)))) + ns-info)) + ns-info)] + (swap! env/*compiler* assoc-in [::namespaces name] ns-info) + (merge {:env env :op :ns :form form} ns-info)))) (defn parse-type [op env [_ tsym fields pmasks body :as form]] diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index ea23fdf8c..c68cdaa06 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -45,13 +45,6 @@ {:pre [(symbol? ns) (symbol? sym)]} (get-in @env/*compiler* [::ana/namespaces ns :defs sym])) -(defn ns-specs - "Given a namespace return all the original specs for a namspace as originally - provided in the source." - [ns] - {:pre [(symbol? ns)]} - (get-in @env/*compiler* [::ana/namespaces ns :specs])) - (defmacro in-cljs-user "Binds cljs.analyzer/*cljs-ns* to 'cljs.user and uses the given compilation environment atom and runs body." diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5c83beb83..2f3a50cc9 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -187,59 +187,6 @@ form (wrap-fn form)))) -(def spec-sort - {:as 0 - :refer 1 - :refer-macros 2 - :include-macros 3}) - -(defn merge-spec [[lib & {:as aindex}] [_ & {:as bindex}]] - (let [merged-map - (merge-with - (fn [x y] - (if (vector? x) - (vec (distinct (into x y))) - y)) - aindex bindex)] - (apply vector lib - (apply concat - (sort - (fn [[sa] [sb]] - (compare (spec-sort sa) (spec-sort sb))) - merged-map))))) - -(defn merge-require [requires [lib :as spec]] - (let [[before [match & after]] - (split-with - (fn [[lib' & _]] - (not= lib lib')) - requires)] - (if (nil? match) - ;; no match, append to end - (concat requires [spec]) - (if (= match spec) - ;; dupe - requires - ;; merge - (concat before [(merge-spec match spec)] after))))) - -(defn update-require-spec - "Given the specification portion of a ns form and require spec additions - return an updated specification." - [specs & additions] - (let [[before [requires & other-specs]] - (split-with - (fn [[x _]] (not= :require x)) - specs) - requires' - `(:require - ~@(reduce - (fn [requires spec] - (merge-require requires spec)) - (rest requires) - additions))] - (concat before [requires'] other-specs))) - (def default-special-fns (let [load-file-fn (fn self @@ -262,18 +209,12 @@ ([repl-env env form] (self repl-env env form nil)) ([repl-env env [_ & specs :as form] opts] - (let [new-specs - (reduce - (fn [requires [quote spec]] - (update-require-spec requires - (if (symbol? spec) [spec] spec))) - (ana-api/ns-specs ana/*cljs-ns*) specs)] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - ~@new-specs) - {:line 1 :column 1}) - identity opts)))) + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require ~@(map (fn [[quote spec]] spec) specs))) + {:merge true :line 1 :column 1}) + identity opts))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index 011527436..8013e993f 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -287,14 +287,3 @@ (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble {:foo baz})))) {:foo 'baz})))) - -(deftest test-namespace-specs - (a/no-warn - (ana-api/in-cljs-user test-cenv - (a/analyze test-env - '(ns foo.bar - (:refer-clojure :exclude [==]) - (:require [clojure.string :as string]))) - (is (= (ana-api/ns-specs 'foo.bar) - '((:refer-clojure :exclude [==]) - (:require [clojure.string :as string]))))))) diff --git a/test/clj/cljs/repl_tests.clj b/test/clj/cljs/repl_tests.clj index 3436895f6..a4f1dd567 100644 --- a/test/clj/cljs/repl_tests.clj +++ b/test/clj/cljs/repl_tests.clj @@ -21,61 +21,3 @@ (is file) (and file (is (io/resource file)))))) - -(deftest test-update-require-spec - (is (= (repl/update-require-spec - '() - '[cljs.reader :as reader]) - '((:require [cljs.reader :as reader])))) - (is (= (repl/update-require-spec - '((:refer-clojure :exclude [==])) - '[cljs.reader :as reader]) - '((:refer-clojure :exclude [==]) - (:require [cljs.reader :as reader])))) - (is (= (repl/update-require-spec - '((:refer-clojure :exclude [==]) - (:require [clojure.string :as clojure.string])) - '[cljs.reader :as reader]) - '((:refer-clojure :exclude [==]) - (:require [clojure.string :as clojure.string] - [cljs.reader :as reader]))))) - -(deftest test-merge-spec - (is (= (repl/merge-spec - '[clojure.string :refer [join]] - '[clojure.string :refer [trim]]) - '[clojure.string :refer [join trim]])) - (is (= (repl/merge-spec - '[cljs.repl :refer [print-doc]] - '[cljs.repl :refer-macros [doc]]) - '[cljs.repl :refer [print-doc] :refer-macros [doc]])) - (is (= (repl/merge-spec - '[cljs.repl :refer [print-doc] :include-macros true] - '[cljs.repl :refer-macros [doc]]) - '[cljs.repl :refer [print-doc] :refer-macros [doc] :include-macros true]))) - -(deftest test-merge-require - (is (= (repl/merge-require - '[[cljs.reader :as r] - [clojure.string :refer [join]] - [cljs.repl :refer-macros [doc]]] - '[clojure.string :refer [trim]]) - '[[cljs.reader :as r] - [clojure.string :refer [join trim]] - [cljs.repl :refer-macros [doc]]])) - (is (= (repl/merge-require - '[[cljs.reader :as r] - [clojure.string :refer [join]] - [cljs.repl :refer-macros [doc]]] - '[clojure.string :refer [join]]) - '[[cljs.reader :as r] - [clojure.string :refer [join]] - [cljs.repl :refer-macros [doc]]])) - (is (= (repl/merge-require - '[[cljs.reader :as r] - [clojure.string :refer [join]] - [cljs.repl :refer-macros [doc]]] - '[clojure.string :refer [join trim]]) - '[[cljs.reader :as r] - [clojure.string :refer [join trim]] - [cljs.repl :refer-macros [doc]]]))) From 96747d2697b44330bd6bcca52181cf4b4c426560 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 12:15:42 -0500 Subject: [PATCH 0450/4033] add *loaded-libs* dyn var --- src/cljs/cljs/core.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index b8841dce4..3eced86bc 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -37,6 +37,8 @@ (def ^:dynamic *print-length* nil) (def ^:dynamic *print-level* nil) +(def ^:dynamic *loaded-libs* nil) + (defn- pr-opts [] {:flush-on-newline *flush-on-newline* :readably *print-readably* From e94a3acda9243367de53741bb66d90e3423bd445 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 12:54:10 -0500 Subject: [PATCH 0451/4033] `cljs.core/*loaded-libs*` must be defonce'ed --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 3eced86bc..81e70f72d 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -37,7 +37,7 @@ (def ^:dynamic *print-length* nil) (def ^:dynamic *print-level* nil) -(def ^:dynamic *loaded-libs* nil) +(defonce ^:dynamic *loaded-libs* nil) (defn- pr-opts [] {:flush-on-newline *flush-on-newline* From 466e5a3b7c8351ebef426d461af7bc3c15a398e2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 12:54:58 -0500 Subject: [PATCH 0452/4033] :reload-all & :reload support in analyzer parse 'ns case --- src/clj/cljs/analyzer.clj | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 5e1fa6693..467b58415 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1246,15 +1246,24 @@ (partial use->require env)) :import (partial parse-import-spec env deps)} valid-forms (atom #{:use :use-macros :require :require-macros :import}) + reload (atom {:use nil :require nil}) {uses :use requires :require use-macros :use-macros require-macros :require-macros imports :import :as params} - (reduce (fn [m [k & libs]] - (when-not (#{:use :use-macros :require :require-macros :import} k) - (throw (error env "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported"))) - (when-not (@valid-forms k) - (throw (error env (str "Only one " k " form is allowed per namespace definition")))) - (swap! valid-forms disj k) - (apply merge-with merge m (map (spec-parsers k) libs))) - {} (remove (fn [[r]] (= r :refer-clojure)) args))] + (reduce + (fn [m [k & libs]] + (when-not (#{:use :use-macros :require :require-macros :import} k) + (throw (error env "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported"))) + (when-not (@valid-forms k) + (throw (error env (str "Only one " k " form is allowed per namespace definition")))) + (swap! valid-forms disj k) + (when (#{:use :require} k) + (when (some #{:reload} libs) + (swap! reload assoc k :reload)) + (when (some #{:reload-all} libs) + (swap! reload assoc k :reload-all))) + (apply merge-with merge m + (map (spec-parsers k) + (remove #{:reload :reload-all} libs)))) + {} (remove (fn [[r]] (= r :refer-clojure)) args))] (when (and *analyze-deps* (seq @deps)) (analyze-deps name @deps env opts)) (when (and *analyze-deps* (seq uses)) @@ -1290,7 +1299,14 @@ ns-info)) ns-info)] (swap! env/*compiler* assoc-in [::namespaces name] ns-info) - (merge {:env env :op :ns :form form} ns-info)))) + (merge {:env env :op :ns :form form} + (cond-> ns-info + (@reload :use) + (update-in [:uses] + (fn [m] (with-meta m {(@reload :use) true}))) + (@reload :require) + (update-in [:requires] + (fn [m] (with-meta m {(@reload :require) true})))))))) (defn parse-type [op env [_ tsym fields pmasks body :as form]] From ad919a3da5daceda555accd152d88ec7245f1847 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 13:08:24 -0500 Subject: [PATCH 0453/4033] `cljs.core/*print-fn*` needs to be defonce'ed --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 81e70f72d..3f23642e4 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -17,7 +17,7 @@ (def *unchecked-if* false) -(def +(defonce ^{:doc "Each runtime environment provides a different way to print output. Whatever function *print-fn* is bound to will be passed any Strings which should be printed." :dynamic true} From be12de10e2ac7ffbc9f9625b15b77ae810d7a75d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 13:19:33 -0500 Subject: [PATCH 0454/4033] partial solution to CLJS-954 - :reload now works --- src/clj/cljs/compiler.clj | 10 ++++++++-- src/clj/cljs/repl.clj | 8 +++++++- src/clj/cljs/repl/node.clj | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index a3a7e7c87..668d4c30d 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -795,8 +795,14 @@ (emitln "}")) (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');")) - (doseq [lib (distinct (concat (vals requires) (vals uses)))] - (emitln "goog.require('" (munge lib) "');"))) + (doseq [lib (distinct (vals requires))] + (if (-> requires meta :reload) + (emitln "goog.require('" (munge lib) "', true);") + (emitln "goog.require('" (munge lib) "');"))) + (doseq [lib (remove (set (vals requires)) (distinct (vals uses)))] + (if (-> uses meta :reload) + (emitln "goog.require('" (munge lib) "', true);") + (emitln "goog.require('" (munge lib) "');")))) (defmethod emit* :deftype* [{:keys [t fields pmasks body]}] diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 2f3a50cc9..3c1a27b82 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -212,7 +212,13 @@ (evaluate-form repl-env env "" (with-meta `(~'ns ~ana/*cljs-ns* - (:require ~@(map (fn [[quote spec]] spec) specs))) + (:require + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) {:merge true :line 1 :column 1}) identity opts))) 'load-file load-file-fn diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index c866a6a7b..8c22b5897 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -122,17 +122,23 @@ (str "goog.isProvided_ = function(x) { return false; };")) ;; monkey-patch goog.require, skip all the loaded checks (repl/evaluate-form repl-env env "" - '(do - (set! (.-require js/goog) - (fn [name] - (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name)))) - nil)) + '(set! (.-require js/goog) + (fn [name] + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name))))) ;; load cljs.core, setup printing (repl/evaluate-form repl-env env "" '(do (.require js/goog "cljs.core") (set! *print-fn* (.-print (js/require "util"))))) + ;; redef goog.require to track loaded libs + (repl/evaluate-form repl-env env "" + '(set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name)))))) ))) (defrecord NodeEnv [host port socket proc] From 673bc90e4b676b5e931b0c4330c013e4629328e1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 13:22:00 -0500 Subject: [PATCH 0455/4033] exclude some more dirs from IntelliJ --- Clojurescript.iml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Clojurescript.iml b/Clojurescript.iml index 215a49713..2acdc289a 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -9,7 +9,11 @@ + + + + From 2d1e2167f5ab8bd76ac5c8bafd198990cc88d34a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 13:39:33 -0500 Subject: [PATCH 0456/4033] throw on (var foo) if foo does not exist --- src/clj/cljs/analyzer.clj | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 467b58415..797863dd0 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -311,18 +311,29 @@ (defn implicit-import? [env prefix suffix] (contains? '#{goog goog.object goog.string goog.array Math} prefix)) -(defn confirm-var-exists [env prefix suffix] - (let [sufstr (str suffix) - suffix (symbol - (if (re-find #"\." sufstr) - (first (string/split sufstr #"\.")) - suffix))] - (when (and (not (implicit-import? env prefix suffix)) - (not (and (not (get-in @env/*compiler* [::namespaces prefix])) - (or (get (:requires (:ns env)) prefix) - (get (:imports (:ns env)) prefix)))) - (not (get-in @env/*compiler* [::namespaces prefix :defs suffix]))) - (warning :undeclared-var env {:prefix prefix :suffix suffix})))) +(defn confirm-var-exists + ([env prefix suffix] + (confirm-var-exists env prefix suffix + (fn [env prefix suffix] + (warning :undeclared-var env {:prefix prefix :suffix suffix})))) + ([env prefix suffix missing-fn] + (let [sufstr (str suffix) + suffix (symbol + (if (re-find #"\." sufstr) + (first (string/split sufstr #"\.")) + suffix))] + (when (and (not (implicit-import? env prefix suffix)) + (not (and (not (get-in @env/*compiler* [::namespaces prefix])) + (or (get (:requires (:ns env)) prefix) + (get (:imports (:ns env)) prefix)))) + (not (get-in @env/*compiler* [::namespaces prefix :defs suffix]))) + (missing-fn env prefix suffix))))) + +(defn confirm-var-exists-throw [] + (fn [env prefix suffix] + (confirm-var-exists env prefix suffix + (fn [env prefix suffix] + (throw (error env (str "Unable to resolve var: " suffix " in this context"))))))) (defn resolve-ns-alias [env name] (let [sym (symbol name)] @@ -534,7 +545,7 @@ (defmethod parse 'var [op env [_ sym :as form] _ _] - (let [var (resolve-var env sym)] + (let [var (resolve-var env sym (confirm-var-exists-throw))] {:env env :op :var-special :form form :var (analyze env sym) :sym (analyze env `(quote ~(symbol (name (:ns var)) (name (:name var))))) From 853ce66d595d879a05a8d29dfbff9b82183131c9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 13:48:14 -0500 Subject: [PATCH 0457/4033] change cljs.analyzer.api/resolve so that it returns nil if var not found --- src/clj/cljs/analyzer/api.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index c68cdaa06..d37cf55fe 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -16,7 +16,10 @@ clojure.core/resolve" [env sym] {:pre [(map? env) (symbol? sym)]} - (ana/resolve-var env sym)) + (try + (ana/resolve-var env sym + (ana/confirm-var-exists-throw)) + (catch Exception e))) (defn all-ns "Return all the namespace analysis maps. Analagous to clojure.core/all-ns but From a1d2a63583bf255fb567e2dcda4fe9bb35e9c710 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 14:24:43 -0500 Subject: [PATCH 0458/4033] CLJS-954: require needs to follow Clojure semantics more closely, support :reload, :reload-all cljs.compiler emit :ns case now supports :reload and :reload-all on requires & uses --- src/clj/cljs/compiler.clj | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 668d4c30d..522577f7d 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -795,14 +795,28 @@ (emitln "}")) (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');")) - (doseq [lib (distinct (vals requires))] - (if (-> requires meta :reload) - (emitln "goog.require('" (munge lib) "', true);") - (emitln "goog.require('" (munge lib) "');"))) - (doseq [lib (remove (set (vals requires)) (distinct (vals uses)))] - (if (-> uses meta :reload) - (emitln "goog.require('" (munge lib) "', true);") - (emitln "goog.require('" (munge lib) "');")))) + (let [loaded-libs (munge 'cljs.core.*loaded-libs*) + loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))] + ;; requires + (when (-> requires meta :reload-all) + (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") + (emitln loaded-libs " = cljs.core.set();")) + (doseq [lib (distinct (vals requires))] + (if (-> requires meta :reload) + (emitln "goog.require('" (munge lib) "', true);") + (emitln "goog.require('" (munge lib) "');"))) + (when (-> requires meta :reload-all) + (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")) + ;; uses + (when (-> uses meta :reload-all) + (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") + (emitln loaded-libs " = cljs.core.set();")) + (doseq [lib (remove (set (vals requires)) (distinct (vals uses)))] + (if (-> uses meta :reload) + (emitln "goog.require('" (munge lib) "', true);") + (emitln "goog.require('" (munge lib) "');"))) + (when (-> uses meta :reload-all) + (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) (defmethod emit* :deftype* [{:keys [t fields pmasks body]}] From 12e713bd3efc99069a7648f9765fa2817f8f84a0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 14:34:13 -0500 Subject: [PATCH 0459/4033] 0.0-2655 --- README.md | 6 +++--- changes.md | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bf64fc629..4bdbfbbda 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2644 +Latest stable release: 0.0-2655 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2644"] +[org.clojure/clojurescript "0.0-2655"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2644 org.clojure clojurescript - 0.0-2644 + 0.0-2655 ``` diff --git a/changes.md b/changes.md index 5a4e2eb10..16ecb9b54 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,17 @@ +## 0.0-2655 + +### Changes +* add defonced cljs.core/*loaded-libs* dynamic var +* cljs.core/*print-fn* is now defonced +* throw on (var foo) when foo is not defined +* cljs.analyzer.api/resolve matches cljs.core/resolve if + var doesn't exist return nil + +### Fixes +* require needs to respect Clojure semantics, do not + reload unless requested +* add ns/require support for :reload & :reload-all + ## 0.0-2644 ### Fixes From dd234eff12f7e94e8e3ccfaf4188bc016004d79e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 17:17:13 -0500 Subject: [PATCH 0460/4033] add require-macros REPL special fn --- src/clj/cljs/repl.clj | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 3c1a27b82..85dae5dbd 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -221,6 +221,23 @@ specs))) {:merge true :line 1 :column 1}) identity opts))) + 'require-macros + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require-macros + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace From 47c3eebc5653a499842fd17df978b6c78f96e781 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 17:28:36 -0500 Subject: [PATCH 0461/4033] 0.0-2657 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4bdbfbbda..627f28172 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2655 +Latest stable release: 0.0-2657 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2655"] +[org.clojure/clojurescript "0.0-2657"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2655 org.clojure clojurescript - 0.0-2655 + 0.0-2657 ``` diff --git a/changes.md b/changes.md index 16ecb9b54..ccf94143a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2657 + +### Changes +* Add require-macros REPL special fn + ## 0.0-2655 ### Changes From 7b683ed43c9316d8163c2f764cc6e9b2710c3f46 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 18:35:22 -0500 Subject: [PATCH 0462/4033] split apart `*load-macros*` and `*analyze-deps*` in analyzer parse 'ns case. Tweak `parse-ns` helper behavior, `*load-macros*` and `*analyze-deps*` always default to `false`. Document the intent of `cljs.analyzer/parse-ns`. --- src/clj/cljs/analyzer.clj | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 797863dd0..9c75645d4 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1280,7 +1280,7 @@ (when (and *analyze-deps* (seq uses)) (check-uses uses env)) (set! *cljs-ns* name) - (when (and *analyze-deps* *load-macros*) + (when *load-macros* (load-core) (doseq [nsym (concat (vals require-macros) (vals use-macros))] (clojure.core/require nsym)) @@ -1747,6 +1747,10 @@ argument, which the reader will use in any emitted errors." (forms-seq*))))) (defn parse-ns + "Helper for parsing only the ns information from a ClojureScript source + file. By default does not load macros or perform any analysis of + dependencies. If provided :analyze-deps and :load-macros these will + be used for *analyze-deps* and *load-macros* bindings respectively." ([src] (parse-ns src nil nil)) ([src opts] (parse-ns src nil opts)) ([src dest opts] @@ -1754,7 +1758,14 @@ argument, which the reader will use in any emitted errors." (let [namespaces' (::namespaces @env/*compiler*) ret (binding [*cljs-ns* 'cljs.user - *analyze-deps* (or (:analyze-deps opts) false)] + *analyze-deps* + (or (when (contains? opts :analyze-deps) + (:analyze-deps opts)) + false) + *load-macros* + (or (when (contains? opts :load-macros) + (:load-macros opts)) + false)] (loop [[forms rdr] (forms-seq src (source-path src) true)] (if (seq forms) (let [env (empty-env) @@ -1859,7 +1870,12 @@ argument, which the reader will use in any emitted errors." (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))) ;; we want want to keep dependency analysis information ;; don't revert the environment - David - (let [{:keys [ns]} (parse-ns res (merge opts {:restore false :analyze-deps true}))] + (let [{:keys [ns]} + (parse-ns res + (merge opts + {:restore false + :analyze-deps true + :load-macros true}))] (when (or *verbose* (:verbose opts)) (util/debug-prn "Reading analysis cache for " res)) (swap! env/*compiler* assoc-in [::analyzed-cljs path] true) From dccc5d0feab9809cb56bd4d7257599a62232ac0d Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Mon, 22 Dec 2014 01:43:35 -0600 Subject: [PATCH 0463/4033] CLJS-916: Optimize use of js-arguments in array and variadic functions --- benchmark/cljs/benchmark_runner.cljs | 9 +++++++- src/clj/cljs/compiler.clj | 33 ++++++++++++++++++++++------ src/cljs/cljs/core.cljs | 8 ++++++- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index c4fecbb93..2b27e8b34 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -290,7 +290,7 @@ (def strings (take 10 (iterate (fn [s] (str s "string")) "string"))) -(def big-str-data +(def big-str-data (pr-str {:nils (repeat 10 nil) :bools (concat (repeat 5 false) (repeat 5 true)) :ints (range 10000 10100) @@ -351,3 +351,10 @@ (defmethod simple-multi :foo [x] x) (simple-benchmark [] (simple-multi :foo) 1000000) (println "\n") + + +(println ";; higher-order variadic function calls") +;; Deliberately frustrates static-fn optimization and macros +(simple-benchmark [f array] (f 1 2 3 4 5 6 7 8 9 0) 100000) +(simple-benchmark [f vector] (f 1 2 3 4 5 6 7 8 9 0) 100000) +(simple-benchmark [] (= 1 1 1 1 1 1 1 1 1 0) 100000) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 522577f7d..0673a328e 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -498,6 +498,20 @@ (emitln "}")) (emits "})"))) +(defn emit-arguments-to-array + "Emit code that copies function arguments into an array starting at an index. + Returns name of var holding the array." + [startslice] + (assert (and (>= startslice 0) (integer? startslice))) + (let [mname (munge (gensym)) + i (str mname "__i") + a (str mname "__a")] + (emitln "var " i " = 0, " + a " = new Array(arguments.length - " startslice ");") + (emitln "while (" i " < " a ".length) {" + a "[" i "] = arguments[" i " + " startslice "]; ++" i ";}") + a)) + (defn emit-variadic-fn-method [{:keys [type name variadic params expr env recurs max-fixed-arity] :as f}] (emit-wrap env @@ -528,9 +542,8 @@ (emit (last params)) (emitln " = null;") (emitln "if (arguments.length > " (dec (count params)) ") {") - (emits " ") - (emit (last params)) - (emitln " = cljs.core.array_seq(Array.prototype.slice.call(arguments, " (dec (count params)) "),0);") + (let [a (emit-arguments-to-array (dec (count params)))] + (emitln " " (last params) " = new cljs.core.IndexedSeq(" a ",0);")) (emitln "} ")) (emits "return " delegate-name ".call(this,") (doseq [param params] @@ -595,10 +608,16 @@ (doseq [[n meth] ms] (if (:variadic meth) (do (emitln "default:") - (emitln "return " n ".cljs$core$IFn$_invoke$arity$variadic(" - (comma-sep (butlast maxparams)) - (when (> (count maxparams) 1) ", ") - "cljs.core.array_seq(arguments, " max-fixed-arity "));")) + (let [restarg (munge (gensym))] + (emitln "var " restarg " = null;") + (emitln "if (arguments.length > " max-fixed-arity ") {") + (let [a (emit-arguments-to-array max-fixed-arity)] + (emitln restarg " = new cljs.core.IndexedSeq(" a ",0);")) + (emitln "}") + (emitln "return " n ".cljs$core$IFn$_invoke$arity$variadic(" + (comma-sep (butlast maxparams)) + (when (> (count maxparams) 1) ", ") + restarg ");"))) (let [pcnt (count (:params meth))] (emitln "case " pcnt ":") (emitln "return " n ".call(this" (if (zero? pcnt) nil diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 3f23642e4..bc40cce53 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -174,7 +174,13 @@ "Creates a new javascript array. @param {...*} var_args" ;;array is a special case, don't emulate this doc string [var-args] ;; [& items] - (.. js/Array -prototype -slice (call (cljs.core/js-arguments)))) + (let [a (js/Array. (alength (cljs.core/js-arguments)))] + (loop [i 0] + (if (< i (alength a)) + (do + (aset a i (aget (cljs.core/js-arguments) i)) + (recur (inc i))) + a)))) (declare apply) From baa2e227cd99b9710e7a0af91d44a15b54d60828 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 19:28:41 -0500 Subject: [PATCH 0464/4033] special case `'cljs.core/unquote` in `cljs.analyzer/confirm-var-exists` --- src/clj/cljs/analyzer.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 9c75645d4..1a750755a 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -323,10 +323,11 @@ (first (string/split sufstr #"\.")) suffix))] (when (and (not (implicit-import? env prefix suffix)) - (not (and (not (get-in @env/*compiler* [::namespaces prefix])) - (or (get (:requires (:ns env)) prefix) - (get (:imports (:ns env)) prefix)))) - (not (get-in @env/*compiler* [::namespaces prefix :defs suffix]))) + (not (and (not (get-in @env/*compiler* [::namespaces prefix])) + (or (get (:requires (:ns env)) prefix) + (get (:imports (:ns env)) prefix)))) + (not (and (= prefix 'cljs.core) (= suffix 'unquote))) + (not (get-in @env/*compiler* [::namespaces prefix :defs suffix]))) (missing-fn env prefix suffix))))) (defn confirm-var-exists-throw [] From 1076eb87164148fff44a5df58d1127bf0324a1e2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 19:36:17 -0500 Subject: [PATCH 0465/4033] tweak cljs.analyzer/parse-ns docstring --- src/clj/cljs/analyzer.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 1a750755a..7770f4f41 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1750,8 +1750,9 @@ argument, which the reader will use in any emitted errors." (defn parse-ns "Helper for parsing only the ns information from a ClojureScript source file. By default does not load macros or perform any analysis of - dependencies. If provided :analyze-deps and :load-macros these will - be used for *analyze-deps* and *load-macros* bindings respectively." + dependencies. If opts parameter provided :analyze-deps and :load-macros keys + their values will be used for *analyze-deps* and *load-macros* bindings + respectively." ([src] (parse-ns src nil nil)) ([src opts] (parse-ns src nil opts)) ([src dest opts] From b1d04e3ac580935baac1ed0d4b46a0dd7139ae7e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 20:05:40 -0500 Subject: [PATCH 0466/4033] CLJS-945: Compile core with :static-fns true by default Change `cljs.util/clojurescript-version` & `cljs.util/compiled-by-version` to always return "0.0-0000" if version unknown - avoids issues with regex matching --- src/clj/cljs/compiler.clj | 3 ++- src/clj/cljs/util.clj | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 0673a328e..f117ec718 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1024,7 +1024,8 @@ opts (merge {:optimizations :none} opts)] (if (.exists src-file) (try - (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts)] + (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) + opts (if (= ns 'cljs.core) (assoc opts :static-fns true) opts)] (if (requires-compilation? src-file dest-file opts) (do (util/mkdirs dest-file) (when (contains? (::ana/namespaces @env/*compiler*) ns) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 4e5c20dd4..6611aa237 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -30,13 +30,13 @@ (str "-" q)) (when (:interim *clojurescript-version*) "-SNAPSHOT")) - "")) + "0.0-0000")) (defn ^String compiled-by-version [^File f] (with-open [reader (io/reader f)] (let [match (->> reader line-seq first (re-matches #".*ClojureScript (\d+\.\d+-\d+).*$"))] - (or (and match (second match)) "")))) + (or (and match (second match)) "0.0-0000")))) (defn build-options [^File f] (with-open [reader (io/reader f)] From 9072b4bfdaf5cf9c66c4770c545f066fbec6256e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Jan 2015 20:48:14 -0500 Subject: [PATCH 0467/4033] CLJS-958: Node.js REPL: Upon error, last successfully item printed Read JSON results from Node.js and reuse existing REPL infrastructure for error reporting. On the Node.js emit JSON strings for all REPL events. --- src/clj/cljs/repl/node.clj | 15 ++++++++++++--- src/clj/cljs/repl/node_repl.js | 34 ++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 8c22b5897..7d9149840 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -12,7 +12,8 @@ [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.repl :as repl] - [cljs.closure :as closure]) + [cljs.closure :as closure] + [clojure.data.json :as json]) (:import java.net.Socket java.lang.StringBuilder [java.io File BufferedReader BufferedWriter] @@ -51,8 +52,16 @@ [repl-env js] (let [{:keys [in out]} @(:socket repl-env)] (write out js) - {:status :success - :value (read-response in)})) + (let [result (json/read-str + (read-response in) :key-fn keyword)] + (condp = (:status result) + "success" + {:status :success + :value (:value result)} + + "exception" + {:status :exception + :value (:value result)})))) (defn load-javascript "Load a Closure JavaScript file into the Node REPL process." diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index d45d8bc6d..6bc1cd262 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -9,7 +9,10 @@ try { } net.createServer(function (socket) { - var buffer = "", ret; + var buffer = "", + ret = null, + err = null; + socket.setEncoding("utf8"); socket.on("data", function(data) { @@ -25,17 +28,32 @@ net.createServer(function (socket) { data = data.replace(/\0/g, ""); try { ret = process.binding('evals').NodeScript.runInThisContext.call( - global, data, "repl"); - } catch (x) { - console.error(x.stack); + global, data, "repl"); + } catch (e) { + err = e; } } - // TODO: can we just console.log? - David - if(ret !== undefined && ret !== null) { - socket.write(ret.toString()); + + if(err) { + socket.write(JSON.stringify({ + status: "exception", + value: err.stack + })); + } else if(ret !== undefined && ret !== null) { + socket.write(JSON.stringify({ + status: "success", + value: ret.toString() + })); } else { - socket.write("nil"); + socket.write(JSON.stringify({ + status: "success", + value: null + })); } + + ret = null; + err = null; + socket.write("\0"); } }); From 5194e14a0220bcdcfea1b5e3dd4d3b4bb9fc0789 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 4 Jan 2015 12:54:43 -0500 Subject: [PATCH 0468/4033] change IJavaScriptEnv -setup signature, must take opts --- src/clj/cljs/repl.clj | 6 ++---- src/clj/cljs/repl/browser.clj | 13 ++++++++----- src/clj/cljs/repl/node.clj | 2 -- src/clj/cljs/repl/rhino.clj | 32 ++++++++++++++------------------ 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 85dae5dbd..d4593fdf4 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -25,7 +25,7 @@ (def ^:dynamic *cljs-verbose* false) (defprotocol IJavaScriptEnv - (-setup [this] [this opts] "initialize the environment") + (-setup [this opts] "initialize the environment") (-evaluate [this filename line js] "evaluate a javascript string") (-load [this provides url] "load code at url into the environment") (-tear-down [this] "dispose of the environment")) @@ -279,9 +279,7 @@ special-fns (merge default-special-fns special-fns) is-special-fn? (set (keys special-fns)) read-error (Object.)] - (if-not (nil? opts) - (-setup repl-env opts) - (-setup repl-env)) + (-setup repl-env opts) (evaluate-form repl-env env "" (with-meta '(ns cljs.user diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index da45159b1..5ddee2f5c 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -192,13 +192,16 @@ (browser-eval (slurp url)) (swap! loaded-libs (partial apply conj) missing)))) +(defn setup [repl-env opts] + (when (:src repl-env) + (repl/analyze-source (:src repl-env))) + (comp/with-core-cljs nil + (fn [] (server/start repl-env)))) + (defrecord BrowserEnv [] repl/IJavaScriptEnv - (-setup [this] - (when (:src this) - (repl/analyze-source (:src this))) - (comp/with-core-cljs nil - (fn [] (server/start this)))) + (-setup [this opts] + (setup this opts)) (-evaluate [_ _ _ js] (browser-eval js)) (-load [this provides url] (load-javascript this provides url)) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 7d9149840..e7a1760ec 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -152,8 +152,6 @@ (defrecord NodeEnv [host port socket proc] repl/IJavaScriptEnv - (-setup [this] - (setup this)) (-setup [this opts] (setup this opts)) (-evaluate [this filename line js] diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index e8939d5c4..38ac48520 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -107,28 +107,24 @@ ;; TODO: don't show errors for goog/base.js line number 105 (catch Throwable ex (println (.getMessage ex))))) -(defn rhino-setup - ([repl-env] (rhino-setup repl-env nil)) - ([repl-env opts] - (let [env (ana/empty-env) - scope (:scope repl-env)] - (ScriptableObject/putProperty scope "__repl_opts" - (Context/javaToJS opts scope)) - (repl/load-file repl-env "cljs/core.cljs" opts) +(defn rhino-setup [repl-env opts] + (let [env (ana/empty-env) + scope (:scope repl-env)] + (ScriptableObject/putProperty scope "__repl_opts" + (Context/javaToJS opts scope)) + (repl/load-file repl-env "cljs/core.cljs" opts) + (repl/evaluate-form repl-env env "" + '(ns cljs.user)) + (ScriptableObject/putProperty scope + "out" (Context/javaToJS *out* scope)) + (binding [ana/*cljs-ns* 'cljs.core] (repl/evaluate-form repl-env env "" - '(ns cljs.user)) - (ScriptableObject/putProperty scope - "out" (Context/javaToJS *out* scope)) - (binding [ana/*cljs-ns* 'cljs.core] - (repl/evaluate-form repl-env env "" - '(do - (set! (.-isProvided_ js/goog) (fn [_] false)) - (set! *print-fn* (fn [x] (.write js/out x))))))))) + '(do + (set! (.-isProvided_ js/goog) (fn [_] false)) + (set! *print-fn* (fn [x] (.write js/out x)))))))) (defrecord RhinoEnv [] repl/IJavaScriptEnv - (-setup [this] - (rhino-setup this)) (-setup [this opts] (rhino-setup this opts)) (-evaluate [this filename line js] From 2635c42e6db7f0e915f0bdb1bf96690a1d8623f1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 4 Jan 2015 13:01:22 -0500 Subject: [PATCH 0469/4033] 0.0-2665 --- README.md | 6 +++--- changes.md | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 627f28172..dcc96db32 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2657 +Latest stable release: 0.0-2665 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2657"] +[org.clojure/clojurescript "0.0-2665"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2657 org.clojure clojurescript - 0.0-2657 + 0.0-2665 ``` diff --git a/changes.md b/changes.md index ccf94143a..d75f64bc7 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,13 @@ +## 0.0-2665 + +### Changes +* REPL -setup now must take opts +* CLJS-916: Optimize use of js-arguments in array and variadic +functions +* special case `'cljs.core/unquote` +* CLJS-945: Compile core with :static-fns true by default +* CLJS-958: Node.js REPL: Upon error, last successfully item printed + ## 0.0-2657 ### Changes From 7561c37231100fbdbe620b5dda7368e7bd2da632 Mon Sep 17 00:00:00 2001 From: Julien Eluard Date: Sun, 4 Jan 2015 00:06:57 -0300 Subject: [PATCH 0470/4033] Do not fail compilation when a protocol method specifies an empty docstring. Fixes #959. --- src/clj/cljs/core.clj | 2 +- test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 33a0e6ebf..11d44a15a 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1013,7 +1013,7 @@ prefix (protocol-prefix p) methods (if (core/string? (first doc+methods)) (next doc+methods) doc+methods) _ (core/doseq [[mname & arities] methods] - (when (some #{0} (map count arities)) + (when (some #{0} (map count (filter vector? arities))) (throw (Exception. (core/str "Invalid protocol, " psym " defines method " mname " with arity 0"))))) expand-sig (fn [fname slot sig] `(~sig diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 6c9fa44a5..7c74429ba 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1801,6 +1801,10 @@ (testing "with locals" (is (= [2 3 1] (-find-first t [2 3]))))))) +(defprotocol IProtocolWithDocStrings + (-method1 [this] "some doc") + (-method2 [this] "")) + ;; ============================================================================= ;; Tickets From 520b30c11d3b3996798456d4c97983aa3b709d85 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 5 Jan 2015 18:46:15 -0500 Subject: [PATCH 0471/4033] copy over some stuff from clojure.main --- src/clj/cljs/repl.clj | 72 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index d4593fdf4..d8c16fd89 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -7,9 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.repl + (:import (java.io PushbackReader)) (:refer-clojure :exclude [load-file]) - (:import java.io.File - javax.xml.bind.DatatypeConverter) (:require [clojure.java.io :as io] [cljs.compiler :as comp] [cljs.analyzer :as ana] @@ -20,10 +19,75 @@ [cljs.source-map :as sm] [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers] - [cljs.util :as util])) + [cljs.util :as util]) + (:import [java.io File PushbackReader] + [javax.xml.bind DatatypeConverter])) (def ^:dynamic *cljs-verbose* false) +;; ============================================================================= +;; Copied over from clojure.main + +(defn skip-if-eol + "If the next character on stream s is a newline, skips it, otherwise + leaves the stream untouched. Returns :line-start, :stream-end, or :body + to indicate the relative location of the next character on s. The stream + must either be an instance of LineNumberingPushbackReader or duplicate + its behavior of both supporting .unread and collapsing all of CR, LF, and + CRLF to a single \\newline." + [s] + (let [c (.read s)] + (cond + (= c (int \newline)) :line-start + (= c -1) :stream-end + :else (do (.unread s c) :body)))) + +(defn skip-whitespace + "Skips whitespace characters on stream s. Returns :line-start, :stream-end, + or :body to indicate the relative location of the next character on s. + Interprets comma as whitespace and semicolon as comment to end of line. + Does not interpret #! as comment to end of line because only one + character of lookahead is available. The stream must either be an + instance of LineNumberingPushbackReader or duplicate its behavior of both + supporting .unread and collapsing all of CR, LF, and CRLF to a single + \\newline." + [s] + (loop [c (.read s)] + (cond + (= c (int \newline)) :line-start + (= c -1) :stream-end + (= c (int \;)) (do (.readLine s) :line-start) + (or (Character/isWhitespace (char c)) (= c (int \,))) (recur (.read s)) + :else (do (.unread s c) :body)))) + +(defn repl-read + "Default :read hook for repl. Reads from *in* which must either be an + instance of LineNumberingPushbackReader or duplicate its behavior of both + supporting .unread and collapsing all of CR, LF, and CRLF into a single + \\newline. repl-read: + - skips whitespace, then + - returns request-prompt on start of line, or + - returns request-exit on end of stream, or + - reads an object from the input stream, then + - skips the next input character if it's end of line, then + - returns the object." + [request-prompt request-exit] + (or ({:line-start request-prompt :stream-end request-exit} + (skip-whitespace *in*)) + (let [input (read)] + (skip-if-eol *in*) + input))) + +(defmacro with-read-known + "Evaluates body with *read-eval* set to a \"known\" value, + i.e. substituting true for :unknown if necessary." + [& body] + `(binding [*read-eval* (if (= :unknown *read-eval*) true *read-eval*)] + ~@body)) + +;; ============================================================================= +;; CLJS Specifics + (defprotocol IJavaScriptEnv (-setup [this opts] "initialize the environment") (-evaluate [this filename line js] "evaluate a javascript string") @@ -278,6 +342,8 @@ (let [env {:context :expr :locals {}} special-fns (merge default-special-fns special-fns) is-special-fn? (set (keys special-fns)) + request-prompt (Object.) + request-exit (Object.) read-error (Object.)] (-setup repl-env opts) (evaluate-form repl-env env "" From e726c22fcc77bfd38cc17a2f0d8349c43c83add5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 6 Jan 2015 08:51:04 -0500 Subject: [PATCH 0472/4033] CLJS-963: do not bother computing goog/dep.js under :none Previously this would be computed to include only what the user had required from Closure Library. This is unnecessary under optimizations :none and when rebuilding source this will often not get properly emitted. Instead just copy it over. --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 54ac21c56..730d1f17a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -848,7 +848,7 @@ should contain the source for the given namespace name." (let [disk-sources (map #(source-on-disk opts %) sources)] (let [goog-deps (io/file (util/output-directory opts) "goog/deps.js")] (do (util/mkdirs goog-deps) - (spit goog-deps (deps-file opts (filter #(= (:group %) :goog) disk-sources))) + (spit goog-deps (slurp (io/resource "goog/deps.js"))) (output-deps-file opts (remove #(= (:group %) :goog) disk-sources)))))) (comment From d1cf949fa2e7c44844dc95913db44ae061397af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marczyk?= Date: Tue, 6 Jan 2015 01:33:32 +0100 Subject: [PATCH 0473/4033] CLJS-962: fix inconsistent hashing of empty collections --- src/cljs/cljs/core.cljs | 24 +++++++++++++++--------- test/cljs/cljs/core_test.cljs | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index bc40cce53..b3ce431b9 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -778,6 +778,9 @@ (next coll)) (mix-collection-hash hash-code n)))) +(def ^:private empty-ordered-hash + (mix-collection-hash 1 0)) + (defn ^number hash-unordered-coll "Returns the hash code, consistent with =, for an external unordered collection implementing Iterable. For maps, the iterator should @@ -790,6 +793,9 @@ (recur (inc n) (bit-or (+ hash-code (hash (first coll))) 0) (next coll)) (mix-collection-hash hash-code n)))) +(def ^:private empty-unordered-hash + (mix-collection-hash 0 0)) + ;;;;;;;;;;;;;;;;;;; protocols on primitives ;;;;;;;; (declare hash-map list equiv-sequential) @@ -2303,7 +2309,7 @@ reduces them without incurring seq initialization" (-equiv [coll other] (equiv-sequential coll other)) IHash - (-hash [coll] 0) + (-hash [coll] empty-ordered-hash) ISeqable (-seq [coll] nil) @@ -4364,7 +4370,7 @@ reduces them without incurring seq initialization" (set! (.-EMPTY-NODE PersistentVector) (VectorNode. nil (make-array 32))) (set! (.-EMPTY PersistentVector) - (PersistentVector. nil 0 5 (.-EMPTY-NODE PersistentVector) (array) 0)) + (PersistentVector. nil 0 5 (.-EMPTY-NODE PersistentVector) (array) empty-ordered-hash)) (set! (.-fromArray PersistentVector) (fn [xs ^boolean no-clone] @@ -4878,7 +4884,7 @@ reduces them without incurring seq initialization" ICounted (-count [coll] count)) -(set! (.-EMPTY PersistentQueue) (PersistentQueue. nil 0 nil [] 0)) +(set! (.-EMPTY PersistentQueue) (PersistentQueue. nil 0 nil [] empty-ordered-hash)) (es6-iterable PersistentQueue) @@ -5055,7 +5061,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (transient (into (hash-map) coll)))) -(set! (.-EMPTY ObjMap) (ObjMap. nil (array) (js-obj) 0 0)) +(set! (.-EMPTY ObjMap) (ObjMap. nil (array) (js-obj) 0 empty-unordered-hash)) (set! (.-HASHMAP_THRESHOLD ObjMap) 8) @@ -5392,7 +5398,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientArrayMap. (js-obj) (alength arr) (aclone arr)))) -(set! (.-EMPTY PersistentArrayMap) (PersistentArrayMap. nil 0 (array) nil)) +(set! (.-EMPTY PersistentArrayMap) (PersistentArrayMap. nil 0 (array) empty-unordered-hash)) (set! (.-HASHMAP-THRESHOLD PersistentArrayMap) 8) @@ -6234,7 +6240,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientHashMap. (js-obj) root cnt has-nil? nil-val))) -(set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil 0)) +(set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil empty-unordered-hash)) (set! (.-fromArrays PersistentHashMap) (fn [ks vs] @@ -7017,7 +7023,7 @@ reduces them without incurring seq initialization" (-comparator [coll] comp)) -(set! (.-EMPTY PersistentTreeMap) (PersistentTreeMap. compare nil 0 nil 0)) +(set! (.-EMPTY PersistentTreeMap) (PersistentTreeMap. compare nil 0 nil empty-unordered-hash)) (es6-iterable PersistentTreeMap) @@ -7321,7 +7327,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientHashSet. (-as-transient hash-map)))) (set! (.-EMPTY PersistentHashSet) - (PersistentHashSet. nil (.-EMPTY PersistentArrayMap) 0)) + (PersistentHashSet. nil (.-EMPTY PersistentArrayMap) empty-unordered-hash)) (set! (.-fromArray PersistentHashSet) (fn [items ^boolean no-clone] @@ -7467,7 +7473,7 @@ reduces them without incurring seq initialization" (-lookup coll k not-found))) (set! (.-EMPTY PersistentTreeSet) - (PersistentTreeSet. nil (.-EMPTY PersistentTreeMap) 0)) + (PersistentTreeSet. nil (.-EMPTY PersistentTreeMap) empty-unordered-hash)) (es6-iterable PersistentTreeSet) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 6c9fa44a5..761e6280b 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2563,6 +2563,25 @@ (is (== @a0 10)) (is (== @a1 20))))) +(deftest test-962-empty-literal-hashes + (testing "CLJS-962: empty literals should produce collections with correct hash codes" + (let [l () + v [] + s #{} + m {}] + (is (== (hash l) (hash v) (hash-ordered-coll ()))) + (is (== (hash s) (hash m) (hash-unordered-coll #{}))))) + (testing "CLJS-962: EMPTY collections should have correct hash codes" + (let [l (.-EMPTY List) + pv (.-EMPTY PersistentVector) + phs (.-EMPTY PersistentHashSet) + pts (.-EMPTY PersistentTreeSet) + pam (.-EMPTY PersistentArrayMap) + phm (.-EMPTY PersistentHashMap) + ptm (.-EMPTY PersistentTreeMap)] + (is (== (hash l) (hash pv) (hash-ordered-coll ()))) + (is (apply == (hash-unordered-coll #{}) (map hash [phs pts pam phm ptm])))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 35d483cffc759bb501d0db045e523ae6246f707a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 6 Jan 2015 09:06:59 -0500 Subject: [PATCH 0474/4033] remove :import dupe --- src/clj/cljs/repl.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index d8c16fd89..11a422190 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -7,7 +7,6 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.repl - (:import (java.io PushbackReader)) (:refer-clojure :exclude [load-file]) (:require [clojure.java.io :as io] [cljs.compiler :as comp] From d1127ad101b9cf8c3d056132bb89bc970c323dc0 Mon Sep 17 00:00:00 2001 From: Justin Tirrell Date: Tue, 30 Dec 2014 15:06:05 -0500 Subject: [PATCH 0475/4033] CLJS-936: Multi arity bitwise operators Add support for multi-arity bitwise operators --- src/cljs/cljs/core.cljs | 16 ++++++++++++---- test/cljs/cljs/core_test.cljs | 25 +++++++++++++++++++------ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index b3ce431b9..22b3eb643 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2031,19 +2031,27 @@ reduces them without incurring seq initialization" (defn bit-xor "Bitwise exclusive or" - [x y] (cljs.core/bit-xor x y)) + ([x y] (cljs.core/bit-xor x y)) + ([x y & more] + (reduce bit-xor (cljs.core/bit-xor x y) more))) (defn bit-and "Bitwise and" - [x y] (cljs.core/bit-and x y)) + ([x y] (cljs.core/bit-and x y)) + ([x y & more] + (reduce bit-and (cljs.core/bit-and x y) more))) (defn bit-or "Bitwise or" - [x y] (cljs.core/bit-or x y)) + ([x y] (cljs.core/bit-or x y)) + ([x y & more] + (reduce bit-or (cljs.core/bit-or x y) more))) (defn bit-and-not "Bitwise and" - [x y] (cljs.core/bit-and-not x y)) + ([x y] (cljs.core/bit-and-not x y)) + ([x y & more] + (reduce bit-and-not (cljs.core/bit-and-not x y) more))) (defn bit-clear "Clear bit at index n" diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 113d6496d..8811906a3 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -357,24 +357,37 @@ (deftest test-bit-operations (testing "Testing bit operations" - (is (= [0 0 1 0 1] + (is (= [1 0 0 40 43 49 49]) + [(bit-xor 0 1) + (bit-xor 1 1) + (bit-xor 1 0) + (bit-xor 41 1) + (bit-xor 42 1) + (bit-xor 42 1 26) + (apply bit-xor [42 1 26])]) + (is (= [0 0 1 0 1 1 1] [(bit-and 1 0) (bit-and 0 0) (bit-and 1 1) (bit-and 42 1) - (bit-and 41 1)])) - (is (= [1 0 1 43 41] + (bit-and 41 1) + (bit-and 41 1 27) + (apply bit-and [41 1 27])])) + (is (= [1 0 1 43 41 59 59] [(bit-or 1 0) (bit-or 0 0) (bit-or 1 1) (bit-or 42 1) - (bit-or 41 1)])) - (is (= [1 0 0 42 40] + (bit-or 41 1) + (bit-or 41 1 26) + (apply bit-or [41 1 26])])) + (is (= [1 0 0 42 32 32] [(bit-and-not 1 0) (bit-and-not 0 0) (bit-and-not 1 1) (bit-and-not 42 1) - (bit-and-not 41 1)])) + (bit-and-not 41 1 27) + (apply bit-and-not [41 1 27])])) (is (= [0 2 968 16649 0] [(bit-clear 1 0) (bit-clear 2 0) From b5e9a5116259fc9f201bee4b9c6564f35306f9a5 Mon Sep 17 00:00:00 2001 From: Michael Griffiths Date: Tue, 23 Dec 2014 21:55:28 +0000 Subject: [PATCH 0476/4033] CLJS-922: Warning message for non-dynamic vars is incomplete --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 7770f4f41..0875837a9 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -428,7 +428,7 @@ (let [env (assoc env :ns (get-namespace *cljs-ns*)) ev (resolve-existing-var env name)] (when (and ev (not (-> ev :dynamic))) - (warning :dynamic env {:ev ev}))))) + (warning :dynamic env {:ev ev :name (:name ev)}))))) (defn ns-dependents ([ns] (ns-dependents ns 0 (atom (sorted-map)))) From 196abcd060c43adb887cf32de4b483f5e0254fbe Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Jan 2015 09:34:58 -0500 Subject: [PATCH 0477/4033] Document REPL special fns --- src/clj/cljs/repl.clj | 109 ++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 11a422190..c6ed2afce 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -250,6 +250,21 @@ form (wrap-fn form)))) +;; Special REPL fns, these provide compatiblity with Clojure functions +;; that are not possible to reproduce given ClojureScript's compilation model +;; All functions should have the following signature +;; +;; (fn self +;; ([repl-env env form] +;; (self repl-env env form)) +;; ([repl-env env form opts] +;; ..)) +;; +;; repl-env - IJavaScriptEnv instance +;; env - a cljs.analyzer environment, *not* cljs.env environment +;; form - complete form entered at the repl +;; opts - REPL options, essentially augmented cljs.closure/build options + (def default-special-fns (let [load-file-fn (fn self @@ -258,57 +273,57 @@ ([repl-env env [_ file :as form] opts] (load-file repl-env file opts)))] {'in-ns - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ [quote ns-name] :as form] _] - (when-not (ana/get-namespace ns-name) - (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) - (-evaluate repl-env "" 1 - (str "goog.provide('" (comp/munge ns-name) "');"))) - (set! ana/*cljs-ns* ns-name))) + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ [quote ns-name] :as form] _] + (when-not (ana/get-namespace ns-name) + (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) + (-evaluate repl-env "" 1 + (str "goog.provide('" (comp/munge ns-name) "');"))) + (set! ana/*cljs-ns* ns-name))) 'require - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) - {:merge true :line 1 :column 1}) - identity opts))) + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts))) 'require-macros - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require-macros - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) - {:merge true :line 1 :column 1}) - identity opts))) - 'load-file load-file-fn + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require-macros + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts))) + 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace - (fn self - ([repl-env env form] - (self env repl-env form nil)) - ([repl-env env [_ ns :as form] opts] - (load-namespace repl-env ns opts)))})) + (fn self + ([repl-env env form] + (self env repl-env form nil)) + ([repl-env env [_ ns :as form] opts] + (load-namespace repl-env ns opts)))})) (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate From a0a6c408c6041bb7f30f6edc4fa37232a60ea6e3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Jan 2015 19:51:51 -0500 Subject: [PATCH 0478/4033] new transducers, mirrors Clojure commit ad888336f8d447f1b8bad7445ab78dfe803fa8b5 --- src/cljs/cljs/core.cljs | 84 +++++++++++++++++++++++++---------- test/cljs/cljs/core_test.cljs | 9 ++++ 2 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 22b3eb643..6576d747e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -3370,24 +3370,34 @@ reduces them without incurring seq initialization" ([a b c] (f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c))) ([a b c & ds] (apply f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c) ds))))) +(declare volatile!) + (defn map-indexed "Returns a lazy sequence consisting of the result of applying f to 0 and the first item of coll, followed by applying f to 1 and the second item in coll, etc, until coll is exhausted. Thus function f should accept 2 arguments, index and item." - [f coll] - (letfn [(mapi [idx coll] - (lazy-seq - (when-let [s (seq coll)] - (if (chunked-seq? s) - (let [c (chunk-first s) - size (count c) - b (chunk-buffer size)] - (dotimes [i size] - (chunk-append b (f (+ idx i) (-nth c i)))) - (chunk-cons (chunk b) (mapi (+ idx size) (chunk-rest s)))) - (cons (f idx (first s)) (mapi (inc idx) (rest s)))))))] - (mapi 0 coll))) + ([f] + (fn [rf] + (let [i (volatile! -1)] + (fn + ([] (rf)) + ([result] (rf result)) + ([result input] + (rf result (f (vswap! i inc) input))))))) + ([f coll] + (letfn [(mapi [idx coll] + (lazy-seq + (when-let [s (seq coll)] + (if (chunked-seq? s) + (let [c (chunk-first s) + size (count c) + b (chunk-buffer size)] + (dotimes [i size] + (chunk-append b (f (+ idx i) (-nth c i)))) + (chunk-cons (chunk b) (mapi (+ idx size) (chunk-rest s)))) + (cons (f idx (first s)) (mapi (inc idx) (rest s)))))))] + (mapi 0 coll)))) (defn keep "Returns a lazy sequence of the non-nil results of (f item). Note, @@ -3850,7 +3860,22 @@ reduces them without incurring seq initialization" (defn interpose "Returns a lazy seq of the elements of coll separated by sep" - [sep coll] (drop 1 (interleave (repeat sep) coll))) + ([sep] + (fn [rf] + (let [started (volatile! false)] + (fn + ([] (rf)) + ([result] (rf result)) + ([result input] + (if @started + (let [sepr (rf result sep)] + (if (reduced? sepr) + sepr + (rf sepr input))) + (do + (vreset! started true) + (rf result input)))))))) + ([sep coll] (drop 1 (interleave (repeat sep) coll)))) @@ -7543,16 +7568,27 @@ reduces them without incurring seq initialization" (defn distinct "Returns a lazy sequence of the elements of coll with duplicates removed" - [coll] - (let [step (fn step [xs seen] - (lazy-seq - ((fn [[f :as xs] seen] - (when-let [s (seq xs)] - (if (contains? seen f) - (recur (rest s) seen) - (cons f (step (rest s) (conj seen f)))))) - xs seen)))] - (step coll #{}))) + ([] + (fn [rf] + (let [seen (volatile! #{})] + (fn + ([] (rf)) + ([result] (rf result)) + ([result input] + (if (contains? @seen input) + result + (do (vswap! seen conj input) + (rf result input)))))))) + ([coll] + (let [step (fn step [xs seen] + (lazy-seq + ((fn [[f :as xs] seen] + (when-let [s (seq xs)] + (if (contains? seen f) + (recur (rest s) seen) + (cons f (step (rest s) (conj seen f)))))) + xs seen)))] + (step coll #{})))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn butlast [s] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 8811906a3..57f8ce897 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2599,6 +2599,15 @@ (is (== (hash l) (hash pv) (hash-ordered-coll ()))) (is (apply == (hash-unordered-coll #{}) (map hash [phs pts pam phm ptm])))))) +(deftest test-map-new-transducers + (testing "Test distinct, interpose, map-indexed transducers" + (is (= [1 2 3] + (transduce (distinct) conj [] [1 1 2 2 3 3]))) + (is (= [1 :foo 2 :foo 3] + (transduce (interpose :foo) conj [] [1 2 3]))) + (is (= [[0 1] [1 2] [2 3]] + (transduce (map-indexed (fn [i x] [i x])) conj [] [1 2 3]))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 5addb29d012ebabb7465f1825ecc96f381be1dc1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Jan 2015 19:55:04 -0500 Subject: [PATCH 0479/4033] revert formatting fubar introduced by 196abcd060c43adb887cf32de4b483f5e0254fbe --- src/clj/cljs/repl.clj | 94 +++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c6ed2afce..d2ece1e06 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -273,57 +273,57 @@ ([repl-env env [_ file :as form] opts] (load-file repl-env file opts)))] {'in-ns - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ [quote ns-name] :as form] _] - (when-not (ana/get-namespace ns-name) - (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) - (-evaluate repl-env "" 1 - (str "goog.provide('" (comp/munge ns-name) "');"))) - (set! ana/*cljs-ns* ns-name))) + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ [quote ns-name] :as form] _] + (when-not (ana/get-namespace ns-name) + (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) + (-evaluate repl-env "" 1 + (str "goog.provide('" (comp/munge ns-name) "');"))) + (set! ana/*cljs-ns* ns-name))) 'require - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) - {:merge true :line 1 :column 1}) - identity opts))) + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts))) 'require-macros - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require-macros - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) - {:merge true :line 1 :column 1}) - identity opts))) - 'load-file load-file-fn + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require-macros + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts))) + 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace - (fn self - ([repl-env env form] - (self env repl-env form nil)) - ([repl-env env [_ ns :as form] opts] - (load-namespace repl-env ns opts)))})) + (fn self + ([repl-env env form] + (self env repl-env form nil)) + ([repl-env env [_ ns :as form] opts] + (load-namespace repl-env ns opts)))})) (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate From 8b5e8c6874c5a5110e718a70fcd499309d6f79e9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Jan 2015 19:16:58 -0500 Subject: [PATCH 0480/4033] do not print compiled JS source for exceptions unless `:repl-verbose` option set --- src/clj/cljs/repl.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index d2ece1e06..f244862d0 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -202,7 +202,9 @@ ;;via errors (goog/base.js 104) :error (display-error ret form) :exception (display-error ret form - #(prn "Error evaluating:" form :as js)) + (if (:repl-verbose opts) + #(prn "Error evaluating:" form :as js) + (constantly nil))) :success (:value ret))))) (catch Throwable ex (.printStackTrace ex) @@ -335,14 +337,14 @@ (ana/analyze-file (str "file://" (.getAbsolutePath file)))))) (defn repl* - [repl-env {:keys [analyze-path verbose-repl warn-on-undeclared special-fns static-fns] :as opts + [repl-env {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}}] (print "To quit, type: ") (prn :cljs/quit) (env/with-compiler-env (or (::env/compiler repl-env) (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user - *cljs-verbose* verbose-repl + *cljs-verbose* repl-verbose ana/*cljs-warnings* (assoc ana/*cljs-warnings* :unprovided warn-on-undeclared :undeclared-var warn-on-undeclared From e408305344ef7b3658118377e530c78c2eb93cf3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 13 Jan 2015 09:01:56 -0500 Subject: [PATCH 0481/4033] CLJS-971: :reload should work for require-macros special fn --- src/clj/cljs/analyzer.clj | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0875837a9..1d4a96cdc 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1258,7 +1258,7 @@ (partial use->require env)) :import (partial parse-import-spec env deps)} valid-forms (atom #{:use :use-macros :require :require-macros :import}) - reload (atom {:use nil :require nil}) + reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) {uses :use requires :require use-macros :use-macros require-macros :require-macros imports :import :as params} (reduce (fn [m [k & libs]] @@ -1267,7 +1267,7 @@ (when-not (@valid-forms k) (throw (error env (str "Only one " k " form is allowed per namespace definition")))) (swap! valid-forms disj k) - (when (#{:use :require} k) + (when-not (= :import k) (when (some #{:reload} libs) (swap! reload assoc k :reload)) (when (some #{:reload-all} libs) @@ -1283,8 +1283,14 @@ (set! *cljs-ns* name) (when *load-macros* (load-core) - (doseq [nsym (concat (vals require-macros) (vals use-macros))] - (clojure.core/require nsym)) + (doseq [nsym (vals use-macros)] + (if-let [k (:use-macros @reload)] + (clojure.core/require nsym k) + (clojure.core/require nsym))) + (doseq [nsym (vals require-macros)] + (if-let [k (:require-macros @reload)] + (clojure.core/require nsym k) + (clojure.core/require nsym))) (when (seq use-macros) (check-use-macros use-macros env))) (let [ns-info From c4d512216f8a1c2c775f4d464b433740c31a1491 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 Jan 2015 20:43:00 -0600 Subject: [PATCH 0482/4033] add PersistentHashMap.fromArray --- src/cljs/cljs/core.cljs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 6576d747e..3463e15d9 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -6275,6 +6275,16 @@ reduces them without incurring seq initialization" (set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil empty-unordered-hash)) +(set! (.-fromArray PersistentHashMap) + (fn [arr ^boolean no-clone] + (let [arr (if no-clone arr (aclone arr)) + len (alength arr)] + (loop [i 0 ret (transient (.-EMPTY PersistentHashMap))] + (if (< i len) + (recur (+ i 2) + (-assoc! ret (aget arr i) (aget arr (inc i)))) + (-persistent! ret)))))) + (set! (.-fromArrays PersistentHashMap) (fn [ks vs] (let [len (alength ks)] From 248882225e189c8ba87221f3d0f4a63a2432f84c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jan 2015 09:48:01 -0600 Subject: [PATCH 0483/4033] support Clojure semantics in `cljs.core/vec`, adopt arrays --- src/cljs/cljs/core.cljs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 3463e15d9..efd535ace 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4421,10 +4421,12 @@ reduces them without incurring seq initialization" (es6-iterable PersistentVector) (defn vec [coll] - (-persistent! - (reduce -conj! - (-as-transient (.-EMPTY PersistentVector)) - coll))) + (if (array? coll) + (.fromArray PersistentVector coll true) + (-persistent! + (reduce -conj! + (-as-transient (.-EMPTY PersistentVector)) + coll)))) (defn vector [& args] (if (and (instance? IndexedSeq args) (zero? (.-i args))) From e27d1348a1960ef558e3e5cfaef93bb3ad3f4ed0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jan 2015 09:55:29 -0600 Subject: [PATCH 0484/4033] tests for new vec behavior, tests for PersistentHashMap.fromArray --- test/cljs/cljs/core_test.cljs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 57f8ce897..10a53c57a 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2608,6 +2608,22 @@ (is (= [[0 1] [1 2] [2 3]] (transduce (map-indexed (fn [i x] [i x])) conj [] [1 2 3]))))) +(deftest test-vec + (let [v (vec #js [1 2 3 4])] + (is (= (count v) 4)) + (is (= (first v) 1)) + (is (= (last v) 4)) + (is (= v [1 2 3 4])))) + +(deftest test-phm-from-array + (let [m (.fromArray PersistentHashMap #js [1 2 3 4] true)] + (is (= (count m) 2)) + (is (contains? m 1)) + (is (contains? m 3)) + (is (= (get m 1) 2)) + (is (= (get m 3) 4)) + (is (= m {1 2 3 4})))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 926f691fd362391d7f18cd77828dc4b25339da57 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jan 2015 10:23:00 -0600 Subject: [PATCH 0485/4033] Var should implement Fn & IFn same as Clojure --- src/cljs/cljs/core.cljs | 48 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index efd535ace..e5ddceabf 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -633,7 +633,53 @@ IDeref (-deref [_] val) IMeta - (-meta [_] _meta)) + (-meta [_] _meta) + Fn + IFn + (-invoke [_] + (val)) + (-invoke [_ a] + (val a)) + (-invoke [_ a b] + (val a b)) + (-invoke [_ a b c] + (val a b c)) + (-invoke [_ a b c d] + (val a b c d)) + (-invoke [_ a b c d e] + (val a b c d e)) + (-invoke [_ a b c d e f] + (val a b c d e f)) + (-invoke [_ a b c d e f g] + (val a b c d e f g)) + (-invoke [_ a b c d e f g h] + (val a b c d e f g h)) + (-invoke [_ a b c d e f g h i] + (val a b c d e f g h i)) + (-invoke [_ a b c d e f g h i j] + (val a b c d e f g h i j)) + (-invoke [_ a b c d e f g h i j k] + (val a b c d e f g h i j k)) + (-invoke [_ a b c d e f g h i j k l] + (val a b c d e f g h i j k l)) + (-invoke [_ a b c d e f g h i j k l m] + (val a b c d e f g h i j k l m)) + (-invoke [_ a b c d e f g h i j k l m n] + (val a b c d e f g h i j k l m n)) + (-invoke [_ a b c d e f g h i j k l m n o] + (val a b c d e f g h i j k l m n o)) + (-invoke [_ a b c d e f g h i j k l m n o p] + (val a b c d e f g h i j k l m n o p)) + (-invoke [_ a b c d e f g h i j k l m n o p q] + (val a b c d e f g h i j k l m n o p q)) + (-invoke [_ a b c d e f g h i j k l m n o p q r] + (val a b c d e f g h i j k l m n o p q r)) + (-invoke [_ a b c d e f g h i j k l m n o p q r s] + (val a b c d e f g h i j k l m n o p q r s)) + (-invoke [_ a b c d e f g h i j k l m n o p q r s t] + (val a b c d e f g h i j k l m n o p q r s t)) + (-invoke [_ a b c d e f g h i j k l m n o p q r s t rest] + (apply val a b c d e f g h i j k l m n o p q r s t rest))) ;;;;;;;;;;;;;;;;;;; fundamentals ;;;;;;;;;;;;;;; From cfdc0e324d67d9a71fa5021fd6bb5bd11f8731d8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jan 2015 10:39:08 -0600 Subject: [PATCH 0486/4033] update vector construction benchmarks to include `vec` --- benchmark/cljs/benchmark_runner.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 2b27e8b34..e4c886428 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -60,8 +60,9 @@ (println ";;; vector ops") (simple-benchmark [] [] 1000000) -(simple-benchmark [] [1 2 3] 1000000) -(simple-benchmark [] (vector 1) 1000000) +(simple-benchmark [[a b c] (take 3 (repeatedly #(rand-int 10)))] (-count [a b c]) 1000000) +(simple-benchmark [[a b c] (take 3 (repeatedly #(rand-int 10)))] (-count (vec #js [a b c])) 1000000) +(simple-benchmark [[a b c] (take 3 (repeatedly #(rand-int 10)))] (-count (vector a b c)) 1000000) (simple-benchmark [coll [1 2 3]] (transient coll) 100000) (simple-benchmark [coll [1 2 3]] (nth coll 0) 1000000) (simple-benchmark [coll [1 2 3]] (-nth coll 0) 1000000) From 7e098b2ea2196e2643ceea62bde943f674b0d362 Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Fri, 16 Jan 2015 17:26:34 -0500 Subject: [PATCH 0487/4033] CLJS-979: ClojureScript REPL needs error handling for the special functions Wrapped the execution of special functions with a general error handler. Validated that the argument to in-ns is a symbol. --- src/clj/cljs/repl.clj | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index f244862d0..7eb7b99df 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -18,7 +18,8 @@ [cljs.source-map :as sm] [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers] - [cljs.util :as util]) + [cljs.util :as util] + [clojure.stacktrace :as trace]) (:import [java.io File PushbackReader] [javax.xml.bind DatatypeConverter])) @@ -279,6 +280,9 @@ ([repl-env env form] (self repl-env env form nil)) ([repl-env env [_ [quote ns-name] :as form] _] + ;; guard against craziness like '5 which wreaks havoc + (when-not (and (= quote 'quote) (symbol? ns-name)) + (throw (IllegalArgumentException. "Argument to in-ns must be a symbol."))) (when-not (ana/get-namespace ns-name) (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) (-evaluate repl-env "" 1 @@ -393,8 +397,13 @@ (and (seq? form) (is-special-fn? (first form))) (do - ((get special-fns (first form)) - repl-env env form opts) + (try + ((get special-fns (first form)) repl-env env form opts) + (catch Throwable ex + (println "Failed to execute special function:" (pr-str (first form))) + (trace/print-cause-trace ex 12))) + ;; flush output which could include stack traces + (flush) (newline) (recur)) From 359ea4eec7aad5902abbaeee319df44d78183469 Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Sat, 17 Jan 2015 12:05:49 -0500 Subject: [PATCH 0488/4033] CLJS-980: ClojureScript REPL stacktraces overrun prompt in many cases Added a few flush statements after printing errors and added a tiny thread sleep to allow things to flush before printing prompt. --- src/clj/cljs/repl.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 7eb7b99df..6dad97da3 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -148,7 +148,8 @@ (f) (println (:value ret)) (when-let [st (:stacktrace ret)] - (println st))))) + (println st) + (flush))))) (defn evaluate-form "Evaluate a ClojureScript form in the JavaScript environment. Returns a @@ -209,7 +210,8 @@ :success (:value ret))))) (catch Throwable ex (.printStackTrace ex) - (println (str ex)))))) + (println (str ex)) + (flush))))) (defn load-stream [repl-env filename res] (let [env (ana/empty-env)] @@ -373,6 +375,8 @@ {:line 1 :column 1}) identity opts) (loop [] + ;; try to let things flush before printing prompt + (Thread/sleep 10) (print (str "ClojureScript:" ana/*cljs-ns* "> ")) (flush) (let [rdr (readers/source-logging-push-back-reader From 522fbdc04e5e35171e6818ce80b3db4811cc3284 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Jan 2015 21:58:23 -0600 Subject: [PATCH 0489/4033] CLJS-982: Var derefing should respect Clojure semantics Var derefing now happens via a thunk. This supports the Clojure pattern of passing a function to some library you don't control where you still want redef to work correctly. --- src/clj/cljs/compiler.clj | 6 +++-- src/cljs/cljs/core.cljs | 46 +++++++++++++++++------------------ test/cljs/cljs/core_test.cljs | 13 ++++++++++ 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index f117ec718..e8ccf723f 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -254,8 +254,10 @@ (defmethod emit* :var-special [{:keys [env var sym meta] :as arg}] - (emit-wrap env - (emits "new cljs.core.Var(" var "," sym "," meta ")"))) + (let [{:keys [name]} (:info var)] + (emit-wrap env + (emits "new cljs.core.Var(function(){return " (munge name) ";}," + sym "," meta ")")))) (defmethod emit* :meta [{:keys [expr meta env]}] diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index e5ddceabf..12e761165 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -631,55 +631,55 @@ (deftype Var [val sym _meta] IDeref - (-deref [_] val) + (-deref [_] (val)) IMeta (-meta [_] _meta) Fn IFn (-invoke [_] - (val)) + ((val))) (-invoke [_ a] - (val a)) + ((val) a)) (-invoke [_ a b] - (val a b)) + ((val) a b)) (-invoke [_ a b c] - (val a b c)) + ((val) a b c)) (-invoke [_ a b c d] - (val a b c d)) + ((val) a b c d)) (-invoke [_ a b c d e] - (val a b c d e)) + ((val) a b c d e)) (-invoke [_ a b c d e f] - (val a b c d e f)) + ((val) a b c d e f)) (-invoke [_ a b c d e f g] - (val a b c d e f g)) + ((val) a b c d e f g)) (-invoke [_ a b c d e f g h] - (val a b c d e f g h)) + ((val) a b c d e f g h)) (-invoke [_ a b c d e f g h i] - (val a b c d e f g h i)) + ((val) a b c d e f g h i)) (-invoke [_ a b c d e f g h i j] - (val a b c d e f g h i j)) + ((val) a b c d e f g h i j)) (-invoke [_ a b c d e f g h i j k] - (val a b c d e f g h i j k)) + ((val) a b c d e f g h i j k)) (-invoke [_ a b c d e f g h i j k l] - (val a b c d e f g h i j k l)) + ((val) a b c d e f g h i j k l)) (-invoke [_ a b c d e f g h i j k l m] - (val a b c d e f g h i j k l m)) + ((val) a b c d e f g h i j k l m)) (-invoke [_ a b c d e f g h i j k l m n] - (val a b c d e f g h i j k l m n)) + ((val) a b c d e f g h i j k l m n)) (-invoke [_ a b c d e f g h i j k l m n o] - (val a b c d e f g h i j k l m n o)) + ((val) a b c d e f g h i j k l m n o)) (-invoke [_ a b c d e f g h i j k l m n o p] - (val a b c d e f g h i j k l m n o p)) + ((val) a b c d e f g h i j k l m n o p)) (-invoke [_ a b c d e f g h i j k l m n o p q] - (val a b c d e f g h i j k l m n o p q)) + ((val) a b c d e f g h i j k l m n o p q)) (-invoke [_ a b c d e f g h i j k l m n o p q r] - (val a b c d e f g h i j k l m n o p q r)) + ((val) a b c d e f g h i j k l m n o p q r)) (-invoke [_ a b c d e f g h i j k l m n o p q r s] - (val a b c d e f g h i j k l m n o p q r s)) + ((val) a b c d e f g h i j k l m n o p q r s)) (-invoke [_ a b c d e f g h i j k l m n o p q r s t] - (val a b c d e f g h i j k l m n o p q r s t)) + ((val) a b c d e f g h i j k l m n o p q r s t)) (-invoke [_ a b c d e f g h i j k l m n o p q r s t rest] - (apply val a b c d e f g h i j k l m n o p q r s t rest))) + (apply (val) a b c d e f g h i j k l m n o p q r s t rest))) ;;;;;;;;;;;;;;;;;;; fundamentals ;;;;;;;;;;;;;;; diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 10a53c57a..362060cc4 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2624,6 +2624,19 @@ (is (= (get m 3) 4)) (is (= m {1 2 3 4})))) +(defn foo-var [f] + (fn [x] + (f x))) + +(defn foo-set [x] + (first x)) + +(deftest test-cljs-982-var-deref [] + (let [f (foo-var #'foo-set)] + (is (= (f [1 2 3]) 1)) + (set! foo-set (fn [x] :oops)) + (is (= (f [1 2 3]) :oops)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 5ce10ba27b31bf095b451ab3bf8f3b7642981a2b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Jan 2015 22:32:58 -0600 Subject: [PATCH 0490/4033] add cljs.analyzer/ast? predicate, add pre-conditions for documentation to :var-special case in cljs.compiler --- src/clj/cljs/analyzer.clj | 3 +++ src/clj/cljs/compiler.clj | 1 + 2 files changed, 4 insertions(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 1d4a96cdc..0be195191 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -68,6 +68,9 @@ (declare message namespaces) +(defn ast? [x] + (and (map? x) (contains? x :op))) + (defmulti error-message (fn [warning-type & _] warning-type)) (defmethod error-message :preamble-missing diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index e8ccf723f..7189065b5 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -254,6 +254,7 @@ (defmethod emit* :var-special [{:keys [env var sym meta] :as arg}] + {:pre [(ana/ast? sym) (ana/ast? meta)]} (let [{:keys [name]} (:info var)] (emit-wrap env (emits "new cljs.core.Var(function(){return " (munge name) ";}," From 4e3ee192797debfe2ecc2e41e0423a1913dea2ca Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Jan 2015 22:58:24 -0600 Subject: [PATCH 0491/4033] remove ObjMap related dead code from compiler --- src/clj/cljs/compiler.clj | 42 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 7189065b5..4e3afab3f 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -266,7 +266,6 @@ (emits "cljs.core.with_meta(" expr "," meta ")"))) (def ^:private array-map-threshold 8) -(def ^:private obj-map-threshold 8) (defn distinct-keys? [keys] (and (every? #(= (:op %) :constant) keys) @@ -274,27 +273,26 @@ (defmethod emit* :map [{:keys [env keys vals]}] - (let [simple-keys? (every? #(or (string? %) (keyword? %)) keys)] - (emit-wrap env - (cond - (zero? (count keys)) - (emits "cljs.core.PersistentArrayMap.EMPTY") - - (<= (count keys) array-map-threshold) - (if (distinct-keys? keys) - (emits "new cljs.core.PersistentArrayMap(null, " (count keys) ", [" - (comma-sep (interleave keys vals)) - "], null)") - (emits "new cljs.core.PersistentArrayMap.fromArray([" - (comma-sep (interleave keys vals)) - "], true, false)")) - - :else - (emits "cljs.core.PersistentHashMap.fromArrays([" - (comma-sep keys) - "],[" - (comma-sep vals) - "])"))))) + (emit-wrap env + (cond + (zero? (count keys)) + (emits "cljs.core.PersistentArrayMap.EMPTY") + + (<= (count keys) array-map-threshold) + (if (distinct-keys? keys) + (emits "new cljs.core.PersistentArrayMap(null, " (count keys) ", [" + (comma-sep (interleave keys vals)) + "], null)") + (emits "new cljs.core.PersistentArrayMap.fromArray([" + (comma-sep (interleave keys vals)) + "], true, false)")) + + :else + (emits "cljs.core.PersistentHashMap.fromArrays([" + (comma-sep keys) + "],[" + (comma-sep vals) + "])")))) (defmethod emit* :list [{:keys [items env]}] From db7e18dae126a630bffc192497fe75dec3918b26 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 19 Jan 2015 16:54:33 -0600 Subject: [PATCH 0492/4033] code style, remove `do` where implicit --- src/clj/cljs/closure.clj | 47 ++++++++++++++++++++------------------- src/clj/cljs/compiler.clj | 18 +++++++-------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 730d1f17a..3cc6e8f94 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -186,9 +186,10 @@ SourceMap$DetailLevel/ALL) (set! (.sourceMapFormat compiler-options) SourceMap$Format/V3)) - (do (.setOptionsForCompilationLevel level compiler-options) - (set-options opts compiler-options) - compiler-options))) + (do + (.setOptionsForCompilationLevel level compiler-options) + (set-options opts compiler-options) + compiler-options))) (defn load-externs "Externs are JavaScript files which contain empty definitions of @@ -220,8 +221,8 @@ (defn ^com.google.javascript.jscomp.Compiler make-closure-compiler [] (let [compiler (com.google.javascript.jscomp.Compiler.)] - (do (com.google.javascript.jscomp.Compiler/setLoggingLevel Level/WARNING) - compiler))) + (com.google.javascript.jscomp.Compiler/setLoggingLevel Level/WARNING) + compiler)) (defn report-failure [^Result result] (let [errors (.errors result) @@ -328,8 +329,8 @@ (if-let [js (get-in @env/*compiler* [::compiled-cljs path])] js (read-js (:file m))))] - (do (swap! env/*compiler* update-in [::compiled-cljs] assoc path js) - js))) + (swap! env/*compiler* update-in [::compiled-cljs] assoc path js) + js)) (defn compile "Given a Compilable, compile it and return an IJavaScript." @@ -367,11 +368,11 @@ "Copy a file contained within a jar to disk. Return the created file." [url out-dir] (let [out-file (io/file out-dir (path-from-jarfile url)) - content (with-open [reader (io/reader url)] - (slurp reader))] - (do (util/mkdirs out-file) - (spit out-file content) - out-file))) + content (with-open [reader (io/reader url)] + (slurp reader))] + (util/mkdirs out-file) + (spit out-file content) + out-file)) ;; TODO: it would be nice if we could consolidate requires-compilation? ;; logic - David @@ -794,14 +795,14 @@ should contain the source for the given namespace name." "Write a JavaScript file to disk. Only write if the file does not already exist. Return IJavaScript for the file on disk." [opts js] - (let [out-dir (io/file (util/output-directory opts)) + (let [out-dir (io/file (util/output-directory opts)) out-name (output-path js) out-file (io/file out-dir out-name)] - (do (when-not (.exists out-file) - (do (util/mkdirs out-file) - (spit out-file (deps/-source js)))) - {:url (deps/to-url out-file) :requires (deps/-requires js) - :provides (deps/-provides js) :group (:group js)}))) + (when-not (.exists out-file) + (util/mkdirs out-file) + (spit out-file (deps/-source js))) + {:url (deps/to-url out-file) :requires (deps/-requires js) + :provides (deps/-provides js) :group (:group js)})) (defn source-on-disk "Ensure that the given JavaScript exists on disk. Write in memory @@ -845,11 +846,11 @@ should contain the source for the given namespace name." The deps file for the current project will include third-party libraries." [opts & sources] - (let [disk-sources (map #(source-on-disk opts %) sources)] - (let [goog-deps (io/file (util/output-directory opts) "goog/deps.js")] - (do (util/mkdirs goog-deps) - (spit goog-deps (slurp (io/resource "goog/deps.js"))) - (output-deps-file opts (remove #(= (:group %) :goog) disk-sources)))))) + (let [disk-sources (map #(source-on-disk opts %) sources) + goog-deps (io/file (util/output-directory opts) "goog/deps.js")] + (util/mkdirs goog-deps) + (spit goog-deps (slurp (io/resource "goog/deps.js"))) + (output-deps-file opts (remove #(= (:group %) :goog) disk-sources)))) (comment diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 4e3afab3f..a0eaab947 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -901,10 +901,9 @@ ([] (with-core-cljs nil)) ([opts] (with-core-cljs opts (fn []))) ([opts body] - (do - (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) - (ana/analyze-file "cljs/core.cljs" opts)) - (body)))) + (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) + (ana/analyze-file "cljs/core.cljs" opts)) + (body))) (defn url-path [^File f] (.getPath (.toURL (.toURI f)))) @@ -947,10 +946,10 @@ (if (seq forms) (let [env (ana/empty-env) ast (ana/analyze env (first forms) nil opts)] - (do (emit ast) - (if (= (:op ast) :ns) - (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) - (recur (rest forms) ns-name deps)))) + (emit ast) + (if (= (:op ast) :ns) + (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) + (recur (rest forms) ns-name deps))) (let [sm-data (when *source-map-data* @*source-map-data*) ret (merge {:ns (or ns-name 'cljs.user) @@ -1028,7 +1027,8 @@ (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) opts (if (= ns 'cljs.core) (assoc opts :static-fns true) opts)] (if (requires-compilation? src-file dest-file opts) - (do (util/mkdirs dest-file) + (do + (util/mkdirs dest-file) (when (contains? (::ana/namespaces @env/*compiler*) ns) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) (compile-file* src-file dest-file opts)) From faf6d3849e74b7f2408916a918fcc8ed43f085ac Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 19 Jan 2015 17:26:59 -0600 Subject: [PATCH 0493/4033] clean up `cljs.closure/add-dependencies` --- src/clj/cljs/closure.clj | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 3cc6e8f94..d6d997cad 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -531,25 +531,29 @@ should contain the source for the given namespace name." a new sequence of IJavaScript objects which includes the input list plus all dependencies in dependency order." [opts & inputs] - (let [requires (mapcat deps/-requires inputs) + (let [requires (mapcat deps/-requires inputs) required-cljs (remove (set inputs) (cljs-dependencies opts requires)) - required-js (js-dependencies opts (set (concat (mapcat deps/-requires required-cljs) requires))) - provided (mapcat deps/-provides (concat inputs required-cljs required-js)) - unprovided (clojure.set/difference (set requires) (set provided) #{"constants-table"})] + required-js (js-dependencies opts (set (concat (mapcat deps/-requires required-cljs) requires))) + provided (mapcat deps/-provides (concat inputs required-cljs required-js)) + unprovided (clojure.set/difference (set requires) (set provided) #{"constants-table"})] (when (seq unprovided) (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) - (cons (javascript-file nil (deps/goog-resource "goog/base.js") ["goog"] nil) - (deps/dependency-order - (concat (map #(-> (javascript-file (:foreign %) - (or (:url %) (io/resource (:file %))) - (:provides %) - (:requires %)) - (assoc :group (:group %))) required-js) - [(when (-> @env/*compiler* :opts :emit-constants) - (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] - (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] - required-cljs - inputs))))) + (cons + (javascript-file nil (deps/goog-resource "goog/base.js") ["goog"] nil) + (deps/dependency-order + (concat + (map + (fn [{:keys [foreign url file provides requires group]}] + (let [url (or url (io/resource file))] + (assoc + (javascript-file foreign url provides requires) + :group group))) + required-js) + [(when (-> @env/*compiler* :opts :emit-constants) + (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] + (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] + required-cljs + inputs))))) (defn preamble-from-paths [paths] (when-let [missing (seq (remove io/resource paths))] From 21b7c9775f0cd45ccac853f0a4301c95e18824ad Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 19 Jan 2015 23:13:49 -0600 Subject: [PATCH 0494/4033] refactor emit :ns to eliminate code duplication --- src/clj/cljs/compiler.clj | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index a0eaab947..09a037b82 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -806,6 +806,21 @@ [{:keys [target val env]}] (emit-wrap env (emits target " = " val))) +(defn load-libs + ([libs] (load-libs libs nil)) + ([libs seen] + (let [loaded-libs (munge 'cljs.core.*loaded-libs*) + loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))] + (when (-> libs meta :reload-all) + (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") + (emitln loaded-libs " = cljs.core.set();")) + (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] + (if (-> libs meta :reload) + (emitln "goog.require('" (munge lib) "', true);") + (emitln "goog.require('" (munge lib) "');"))) + (when (-> libs meta :reload-all) + (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))) + (defmethod emit* :ns [{:keys [name requires uses require-macros env]}] (when (= (:optimizations *build-options*) :none) @@ -815,28 +830,8 @@ (emitln "}")) (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');")) - (let [loaded-libs (munge 'cljs.core.*loaded-libs*) - loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))] - ;; requires - (when (-> requires meta :reload-all) - (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") - (emitln loaded-libs " = cljs.core.set();")) - (doseq [lib (distinct (vals requires))] - (if (-> requires meta :reload) - (emitln "goog.require('" (munge lib) "', true);") - (emitln "goog.require('" (munge lib) "');"))) - (when (-> requires meta :reload-all) - (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")) - ;; uses - (when (-> uses meta :reload-all) - (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") - (emitln loaded-libs " = cljs.core.set();")) - (doseq [lib (remove (set (vals requires)) (distinct (vals uses)))] - (if (-> uses meta :reload) - (emitln "goog.require('" (munge lib) "', true);") - (emitln "goog.require('" (munge lib) "');"))) - (when (-> uses meta :reload-all) - (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) + (load-libs requires) + (load-libs uses requires)) (defmethod emit* :deftype* [{:keys [t fields pmasks body]}] From 706be0be34700a9e8d1b468b5bd98dde6600976a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 19 Jan 2015 23:23:26 -0600 Subject: [PATCH 0495/4033] putting conditionals around `goog.require` and `goog.provides` doesn't make sense, remove --- src/clj/cljs/compiler.clj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 09a037b82..413a2fc16 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -823,11 +823,7 @@ (defmethod emit* :ns [{:keys [name requires uses require-macros env]}] - (when (= (:optimizations *build-options*) :none) - (emitln "if(!goog.isProvided_('" (munge name) "')) {")) (emitln "goog.provide('" (munge name) "');") - (when (= (:optimizations *build-options*) :none) - (emitln "}")) (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');")) (load-libs requires) From 1e5580c92f4035f8daf941e3c884c9ad4b89c419 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 20 Jan 2015 11:15:39 -0600 Subject: [PATCH 0496/4033] Node.js REPL should compile with :target :nodejs flag set. Add `IReplEnvOptions` protocol to allow REPL environments to provide additional default options. Node.js REPL environment now provides {:target :nodejs}. Change `cljs.repl/repl*` to merge in REPL env options before doing any work. This fix uncovered the following issues also addressed in this change: * `cljs.compiler/*build-options*` is unnecessary already covered by `cljs.env/default-compiler-env` ctor fn * `cljs.array?` macro broken, fixed to look at :options in compiler env --- src/clj/cljs/closure.clj | 1 - src/clj/cljs/compiler.clj | 3 - src/clj/cljs/core.clj | 2 +- src/clj/cljs/repl.clj | 146 ++++++++++++++++++++----------------- src/clj/cljs/repl/node.clj | 3 + 5 files changed, 82 insertions(+), 73 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index d6d997cad..75dacfc2c 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1016,7 +1016,6 @@ should contain the source for the given namespace name." :undeclared-ns :undeclared-ns-form] (repeat warnings)) warnings))) - comp/*build-options* opts ana/*verbose* (:verbose opts)] (let [compiled (util/measure compiler-stats "Compile basic sources" diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 413a2fc16..33df37f49 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -38,9 +38,6 @@ (def ^:dynamic *source-map-data* nil) (def ^:dynamic *lexical-renames* {}) -;; NOTE: explicitly threading opts as cljs.analyzer is considerably more -;; invasive given the current approach to emission - David -(def ^:dynamic *build-options* nil) (def cljs-reserved-file-names #{"deps.cljs"}) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 11d44a15a..7c3501a4f 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -299,7 +299,7 @@ (bool-expr (core/list 'js* "~{} === false" x))) (defmacro array? [x] - (if (= :nodejs (:target @env/*compiler*)) + (if (= :nodejs (-> @env/*compiler* :options :target)) (bool-expr `(.isArray js/Array ~x)) (bool-expr (core/list 'js* "~{} instanceof Array" x)))) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 6dad97da3..f1ff14bdf 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -88,12 +88,20 @@ ;; ============================================================================= ;; CLJS Specifics +(defprotocol IReplEnvOptions + (-repl-options [this] "Return default REPL options for a REPL Env")) + (defprotocol IJavaScriptEnv (-setup [this opts] "initialize the environment") (-evaluate [this filename line js] "evaluate a javascript string") (-load [this provides url] "load code at url into the environment") (-tear-down [this] "dispose of the environment")) +(extend-type + Object + IReplEnvOptions + (-repl-options [_] nil)) + (defn- env->opts "Returns a hash-map containing all of the entries in [repl-env], translating :working-dir to :output-dir." @@ -343,78 +351,80 @@ (ana/analyze-file (str "file://" (.getAbsolutePath file)))))) (defn repl* - [repl-env {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts - :or {warn-on-undeclared true}}] + [repl-env opts] (print "To quit, type: ") (prn :cljs/quit) - (env/with-compiler-env - (or (::env/compiler repl-env) (env/default-compiler-env opts)) - (binding [ana/*cljs-ns* 'cljs.user - *cljs-verbose* repl-verbose - ana/*cljs-warnings* (assoc ana/*cljs-warnings* - :unprovided warn-on-undeclared - :undeclared-var warn-on-undeclared - :undeclared-ns warn-on-undeclared - :undeclared-ns-form warn-on-undeclared) - ana/*cljs-static-fns* static-fns] - ;; TODO: the follow should become dead code when the REPL is - ;; sufficiently enhanced to understand :cache-analysis - David - (when analyze-path - (analyze-source analyze-path)) - (let [env {:context :expr :locals {}} - special-fns (merge default-special-fns special-fns) - is-special-fn? (set (keys special-fns)) - request-prompt (Object.) - request-exit (Object.) - read-error (Object.)] - (-setup repl-env opts) - (evaluate-form repl-env env "" - (with-meta - '(ns cljs.user - (:require [cljs.repl :refer-macros [doc]])) - {:line 1 :column 1}) - identity opts) - (loop [] - ;; try to let things flush before printing prompt - (Thread/sleep 10) - (print (str "ClojureScript:" ana/*cljs-ns* "> ")) - (flush) - (let [rdr (readers/source-logging-push-back-reader - (java.io.PushbackReader. (io/reader *in*)) - 1 - "NO_SOURCE_FILE") - form (try - (binding [*ns* (create-ns ana/*cljs-ns*) - reader/*data-readers* tags/*cljs-data-readers* - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros) - (ana/get-namespace ana/*cljs-ns*)))] - (reader/read rdr nil read-error)) - (catch Exception e - (println (.getMessage e)) - read-error))] - ;; TODO: need to catch errors here too - David - (cond - (identical? form read-error) (recur) - (= form :cljs/quit) :quit + (let [{:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts + :or {warn-on-undeclared true}} + (merge (-repl-options repl-env) opts)] + (env/with-compiler-env + (or (::env/compiler repl-env) (env/default-compiler-env opts)) + (binding [ana/*cljs-ns* 'cljs.user + *cljs-verbose* repl-verbose + ana/*cljs-warnings* (assoc ana/*cljs-warnings* + :unprovided warn-on-undeclared + :undeclared-var warn-on-undeclared + :undeclared-ns warn-on-undeclared + :undeclared-ns-form warn-on-undeclared) + ana/*cljs-static-fns* static-fns] + ;; TODO: the follow should become dead code when the REPL is + ;; sufficiently enhanced to understand :cache-analysis - David + (when analyze-path + (analyze-source analyze-path)) + (let [env {:context :expr :locals {}} + special-fns (merge default-special-fns special-fns) + is-special-fn? (set (keys special-fns)) + request-prompt (Object.) + request-exit (Object.) + read-error (Object.)] + (-setup repl-env opts) + (evaluate-form repl-env env "" + (with-meta + '(ns cljs.user + (:require [cljs.repl :refer-macros [doc]])) + {:line 1 :column 1}) + identity opts) + (loop [] + ;; try to let things flush before printing prompt + (Thread/sleep 10) + (print (str "ClojureScript:" ana/*cljs-ns* "> ")) + (flush) + (let [rdr (readers/source-logging-push-back-reader + (java.io.PushbackReader. (io/reader *in*)) + 1 + "NO_SOURCE_FILE") + form (try + (binding [*ns* (create-ns ana/*cljs-ns*) + reader/*data-readers* tags/*cljs-data-readers* + reader/*alias-map* + (apply merge + ((juxt :requires :require-macros) + (ana/get-namespace ana/*cljs-ns*)))] + (reader/read rdr nil read-error)) + (catch Exception e + (println (.getMessage e)) + read-error))] + ;; TODO: need to catch errors here too - David + (cond + (identical? form read-error) (recur) + (= form :cljs/quit) :quit - (and (seq? form) (is-special-fn? (first form))) - (do - (try - ((get special-fns (first form)) repl-env env form opts) - (catch Throwable ex - (println "Failed to execute special function:" (pr-str (first form))) - (trace/print-cause-trace ex 12))) - ;; flush output which could include stack traces - (flush) - (newline) - (recur)) + (and (seq? form) (is-special-fn? (first form))) + (do + (try + ((get special-fns (first form)) repl-env env form opts) + (catch Throwable ex + (println "Failed to execute special function:" (pr-str (first form))) + (trace/print-cause-trace ex 12))) + ;; flush output which could include stack traces + (flush) + (newline) + (recur)) - :else - (do (eval-and-print repl-env env form) - (recur))))) - (-tear-down repl-env))))) + :else + (do (eval-and-print repl-env env form) + (recur))))) + (-tear-down repl-env)))))) (defn repl "Note - repl will reload core.cljs every time, even if supplied old repl-env" diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index e7a1760ec..61483df84 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -151,6 +151,9 @@ ))) (defrecord NodeEnv [host port socket proc] + repl/IReplEnvOptions + (-repl-options [this] + {:target :nodejs}) repl/IJavaScriptEnv (-setup [this opts] (setup this opts)) From 62d898ae30eb58397628b45b3c0c95d3e899a274 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 20 Jan 2015 14:55:50 -0600 Subject: [PATCH 0497/4033] CLJS-984: Update Node.js REPL support to use public API --- src/clj/cljs/repl/node_repl.js | 4 ++-- src/cljs/cljs/bootstrap_node.js | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index 6bc1cd262..fbde78e8e 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -1,6 +1,7 @@ process.env.NODE_DISABLE_COLORS = true; var net = require("net"); +var vm = require("vm"); var PORT = 5001; try { @@ -27,8 +28,7 @@ net.createServer(function (socket) { // not sure how \0's are getting through - David data = data.replace(/\0/g, ""); try { - ret = process.binding('evals').NodeScript.runInThisContext.call( - global, data, "repl"); + ret = vm.Script.runInThisContext.call(global, data, "repl"); } catch (e) { err = e; } diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index 861b3cb15..6b17d1bb5 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -39,9 +39,9 @@ * @nocompile */ - -var fs = require('fs'); -var path = require('path'); +var fs = require("fs"); +var vm = require("vm"); +var path = require("path"); var CLJS_ROOT = "./"; @@ -76,8 +76,7 @@ global.CLOSURE_IMPORT_SCRIPT = function(src) { // Declared here so it can be used to require base.js function nodeGlobalRequire(file) { - process.binding('evals').NodeScript.runInThisContext.call( - global, fs.readFileSync(file), file); + vm.Script.runInThisContext.call(global, fs.readFileSync(file), file); } From acde4d1cea3d2a87541a0ce27aa9bced3e5765b4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 20 Jan 2015 20:24:35 -0500 Subject: [PATCH 0498/4033] add `util/get-name` for getting the last path component from file or url --- src/clj/cljs/util.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 6611aa237..b541855d5 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -103,6 +103,14 @@ (url? f) (path f))] (last (string/split s #"\.")))) +(defn ^String get-name + "Given a file or url return the last component of the path." + [x] + {:pre [(or (file? x) (url? x))]} + (if (file? x) + (filename x) + (last (path-seq (path x))))) + (defn debug-prn [& args] (.println System/err (apply str args))) From 1f9d110b95a250b7aaa30ac89a268a4c23aaef8e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 21 Jan 2015 07:21:20 -0500 Subject: [PATCH 0499/4033] add `cljs.analyzer/foreign-dep?` predicate --- src/clj/cljs/analyzer.clj | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0be195191..5aeacdcdf 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1079,6 +1079,13 @@ (when (and (.exists f) (.isFile f)) f)))) +(defn foreign-dep? [dep] + {:pre [(symbol? dep)]} + (let [js-index (:js-dependency-index @env/*compiler*)] + (if-let [[_ {:keys [foreign]}] (find js-index (name dep))] + foreign + false))) + (defn analyze-deps ([lib deps env] (analyze-deps lib deps env)) ([lib deps env opts] From 2f1b9730ba61c77c6f24e896270ae075325df4bb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 21 Jan 2015 08:06:50 -0500 Subject: [PATCH 0500/4033] comment changes made to boostrap_node.js for REPLs --- src/cljs/cljs/bootstrap_node.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index 6b17d1bb5..be77e02c7 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -58,9 +58,9 @@ global.goog = {}; * @return {boolean} True if the script was imported, false otherwise. */ global.CLOSURE_IMPORT_SCRIPT = function(src) { - // Sources are always expressed relative to closure's base.js, but - // require() is always relative to the current source. - if(CLJS_ROOT !== ".") { + // if CLJS_ROOT has been rewritten (by REPLs) need to compute require path + // so we can delete the old entry from the Node.js require cache + if(CLJS_ROOT !== "./") { var path = null; if(src.substring(0, 3) == "../") { path = CLJS_ROOT+src.substring(3); @@ -69,6 +69,9 @@ global.CLOSURE_IMPORT_SCRIPT = function(src) { } if(require.cache[path]) delete require.cache[path]; } + + // Sources are always expressed relative to closure's base.js, but + // require() is always relative to the current source. require('./../' + src); return true; }; From e443cd5b21b90d29cabba6054f99a1f7d9ce74a8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 21 Jan 2015 18:10:36 -0500 Subject: [PATCH 0501/4033] foreign-deps support wip --- src/clj/cljs/closure.clj | 105 ++++++++++++++++++++++---------------- src/clj/cljs/compiler.clj | 9 +++- src/clj/cljs/core.clj | 10 +++- src/cljs/cljs/core.cljs | 4 ++ 4 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 75dacfc2c..a0edb0d52 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -358,7 +358,7 @@ (map compiled-file (comp/compile-root src-dir out-dir opts)))) -(defn path-from-jarfile +(defn ^String path-from-jarfile "Given the URL of a file within a jar, return the path of the file from the root of the jar." [^URL url] @@ -608,13 +608,9 @@ should contain the source for the given namespace name." [provides] (apply str (map #(str "goog.provide('" % "');\n") provides))) - (defmethod js-source-file JavaScriptFile [_ js] - (when-let [url (deps/-url js)] - (js-source-file (javascript-name url) - (if (deps/-foreign? js) - (str (build-provides (deps/-provides js)) (slurp url)) - (io/input-stream url))))) + (when-let [url (deps/-url js)] + (js-source-file (javascript-name url) (io/input-stream url)))) (defn optimize "Use the Closure Compiler to optimize one or more JavaScript files." @@ -730,14 +726,15 @@ should contain the source for the given namespace name." ;; library's base.js and one which calls goog.require to load your ;; code. See samples/hello/hello-dev.html for an example. -(defn path-relative-to - "Generate a string which is the path to input relative to base." +(defn ^String path-relative-to + "Generate a string which is the path to the input IJavaScript relative + to the specified base file." [^File base input] - (let [base-path (util/path-seq (.getCanonicalPath base)) + (let [base-path (util/path-seq (.getCanonicalPath base)) input-path (util/path-seq (.getCanonicalPath (io/file ^URL (deps/-url input)))) count-base (count base-path) - common (count (take-while true? (map #(= %1 %2) base-path input-path))) - prefix (repeat (- count-base common 1) "..")] + common (count (take-while true? (map #(= %1 %2) base-path input-path))) + prefix (repeat (- count-base common 1) "..")] (if (= count-base common) (last input-path) ;; same file (util/to-path (concat prefix (drop common input-path)) "/")))) @@ -777,14 +774,19 @@ should contain the source for the given namespace name." (defn output-deps-file [opts sources] (output-one-file opts (deps-file opts sources))) -(defn ^String output-path - "Given an IJavaScript which is either in memory or in a jar file, - return the output path for this file relative to the working +(defn ^String rel-output-path + "Given an IJavaScript which is either in memory, in a jar file, + or is a foreign lib, return the path relative to the output directory." [js] - (if-let [url ^URL (deps/-url js)] - (path-from-jarfile url) - (if (string? js) + (let [url (deps/-url js)] + (cond + url + (if (deps/-foreign? js) + (util/get-name url) + (path-from-jarfile url)) + + (string? js) (let [digest (MessageDigest/getInstance "SHA-1")] (.reset digest) (.update digest (.getBytes ^String js "utf8")) @@ -793,43 +795,52 @@ should contain the source for the given namespace name." (take 7) (apply str)) ".js")) - (str (random-string 5) ".js")))) + + :else (str (random-string 5) ".js")))) (defn write-javascript - "Write a JavaScript file to disk. Only write if the file does not - already exist. Return IJavaScript for the file on disk." + "Write or copy a JavaScript file to output directory. Only write if the file + does not already exist. Return IJavaScript for the file on disk at the new + location." [opts js] (let [out-dir (io/file (util/output-directory opts)) - out-name (output-path js) + out-name (rel-output-path js) out-file (io/file out-dir out-name)] (when-not (.exists out-file) (util/mkdirs out-file) (spit out-file (deps/-source js))) - {:url (deps/to-url out-file) :requires (deps/-requires js) - :provides (deps/-provides js) :group (:group js)})) + {:url (deps/to-url out-file) + :requires (deps/-requires js) + :provides (deps/-provides js) + :group (:group js)})) + +(defn write-js? + "Returns true if IJavaScript instance needs to be written/copied to output + directory. True when in memory, in a JAR, or if foreign library." + [js] + (let [url ^URL (deps/-url js)] + (or (not url) + (= (.getProtocol url) "jar") + (deps/-foreign? js)))) (defn source-on-disk - "Ensure that the given JavaScript exists on disk. Write in memory - sources and files contained in jars to the working directory. Return - updated IJavaScript with the new location." + "Ensure that the given IJavaScript exists on disk in the output directory. + Return updated IJavaScript with the new location if necessary." [opts js] - (let [url ^URL (deps/-url js)] - (if (or (not url) - (= (.getProtocol url) "jar")) - (write-javascript opts js) - ;; always copy original sources to the output directory - ;; when source maps enabled - (let [out-file (if-let [ns (and (:source-map opts) - (first (:provides js)))] - (io/file (io/file (util/output-directory opts)) - (util/ns->relpath ns))) - source-url (:source-url js)] - (when (and out-file source-url - (or (not (.exists ^File out-file)) - (> (.lastModified (io/file source-url)) - (.lastModified out-file)))) - (spit out-file (slurp source-url))) - js)))) + (if (write-js? js) + (write-javascript opts js) + ;; always copy original ClojureScript sources to the output directory + ;; when source maps enabled + (let [out-file (if-let [ns (and (:source-map opts) (first (:provides js)))] + (io/file (io/file (util/output-directory opts)) + (util/ns->relpath ns))) + source-url (:source-url js)] + (when (and out-file source-url + (or (not (.exists ^File out-file)) + (> (.lastModified (io/file source-url)) + (.lastModified out-file)))) + (spit out-file (slurp source-url))) + js))) (comment (write-javascript {} "goog.provide('demo');\nalert('hello');\n") @@ -1048,7 +1059,11 @@ should contain the source for the given namespace name." (->> (util/measure compiler-stats "Optimize sources" - (apply optimize all-opts js-sources)) + (apply optimize all-opts + (remove + #(and (satisfies? deps/IJavaScript %) + (deps/-foreign? %)) + js-sources))) (add-wrapper all-opts) (add-source-map-link all-opts) (add-header all-opts) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 33df37f49..9dfd4c376 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -812,9 +812,14 @@ (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") (emitln loaded-libs " = cljs.core.set();")) (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] - (if (-> libs meta :reload) + (cond + (ana/foreign-dep? lib) + (emitln "cljs.core.load_file(\"" (munge (name lib)) ".js\");") + + (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', true);") - (emitln "goog.require('" (munge lib) "');"))) + + :else (emitln "goog.require('" (munge lib) "');"))) (when (-> libs meta :reload-all) (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 7c3501a4f..de4b6a7e1 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -38,7 +38,8 @@ (:require clojure.walk clojure.set cljs.compiler - [cljs.env :as env])) + [cljs.env :as env]) + (:import [java.io File])) (alias 'core 'clojure.core) (alias 'ana 'cljs.analyzer) @@ -1689,3 +1690,10 @@ was swapped in." [vol f & args] `(-vreset! ~vol (~f (-deref ~vol) ~@args))) + +(defmacro load-file* [s] + (core/let [{:keys [target output-dir]} (:options @env/*compiler*)] + (core/condp = target + ;; under Node.js, always relative to JVM working directory + :nodejs `(. js/goog (~'nodeGlobalRequire (str ~output-dir ~File/separator ~s))) + `(. js/goog (~'importScript_ (. js/goog (~'getPathFromDeps_ ~s))))))) \ No newline at end of file diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 12e761165..18ca1105e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -148,6 +148,10 @@ s (str ty))) +(defn load-file [rule] + (when-not js/COMPILED + (cljs.core/load-file* rule))) + (if (and (exists? js/Symbol) (identical? (goog/typeOf js/Symbol) "function")) (def ITER_SYMBOL (.-iterator js/Symbol)) From 154b92e5067f0f654dbb5745b0f34121c5ffb0a2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 21 Jan 2015 18:56:32 -0500 Subject: [PATCH 0502/4033] fix the way foreign libs are loaded via the :ns form foreign libs are always placed at the root of the output directory. load them from there. --- src/clj/cljs/compiler.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 9dfd4c376..58fb7c057 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -814,7 +814,9 @@ (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond (ana/foreign-dep? lib) - (emitln "cljs.core.load_file(\"" (munge (name lib)) ".js\");") + (let [js-index (:js-dependency-index @env/*compiler*) + ijs-url (get-in js-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");")) (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', true);") From 4c0ba4371d855098603a59c49186cb79113fd114 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 21 Jan 2015 19:23:41 -0500 Subject: [PATCH 0503/4033] add `cljs.core/load-lib` improve `cljs.core/load-file` & `cljs.core/load-lib` so that foreign library loading works equally well for Node.js & browser --- src/clj/cljs/compiler.clj | 9 ++++++--- src/clj/cljs/core.clj | 11 ++++++++--- src/cljs/cljs/core.cljs | 8 ++++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 58fb7c057..ceb9445de 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -814,9 +814,12 @@ (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond (ana/foreign-dep? lib) - (let [js-index (:js-dependency-index @env/*compiler*) - ijs-url (get-in js-index [(name lib) :url])] - (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");")) + (if-not (= :nodejs (get-in @env/*compiler* [:options :target])) + (emitln "cljs.core.load_lib(\"" (munge (name lib)) "\");") + (let [js-index (:js-dependency-index @env/*compiler*) + ijs-url (get-in js-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");"))) + (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', true);") diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index de4b6a7e1..135650847 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1691,9 +1691,14 @@ [vol f & args] `(-vreset! ~vol (~f (-deref ~vol) ~@args))) -(defmacro load-file* [s] +(defmacro load-file* [f] (core/let [{:keys [target output-dir]} (:options @env/*compiler*)] (core/condp = target ;; under Node.js, always relative to JVM working directory - :nodejs `(. js/goog (~'nodeGlobalRequire (str ~output-dir ~File/separator ~s))) - `(. js/goog (~'importScript_ (. js/goog (~'getPathFromDeps_ ~s))))))) \ No newline at end of file + :nodejs `(. js/goog (~'nodeGlobalRequire (str ~output-dir ~File/separator ~f))) + `(. js/goog (~'importScript_ ~f))))) + +(defmacro load-lib* [lib] + `(. js/goog + (~'importScript_ + (str js/goog.basePath (. js/goog (~'getPathFromDeps_ ~lib)))))) \ No newline at end of file diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 18ca1105e..5e60555d4 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -148,9 +148,13 @@ s (str ty))) -(defn load-file [rule] +(defn load-file [file] (when-not js/COMPILED - (cljs.core/load-file* rule))) + (cljs.core/load-file* file))) + +(defn load-lib [lib] + (when-not js/COMPILED + (cljs.core/load-lib* lib))) (if (and (exists? js/Symbol) (identical? (goog/typeOf js/Symbol) "function")) From 52a40cf7e5c004ce6351f0768497393a901f0689 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 22 Jan 2015 09:05:56 -0500 Subject: [PATCH 0504/4033] mark `cljs.core/load-lib` and `cljs.core/load-file` as implementation details - they are not public fns to be used. --- src/cljs/cljs/core.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 5e60555d4..e20a05322 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -148,10 +148,12 @@ s (str ty))) +;; INTERNAL - do not use (defn load-file [file] (when-not js/COMPILED (cljs.core/load-file* file))) +;; INTERNAL - do not use (defn load-lib [lib] (when-not js/COMPILED (cljs.core/load-lib* lib))) From 2eba7ec344be489dd476ee92e303ffd2e5b1ef58 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 22 Jan 2015 17:30:39 -0500 Subject: [PATCH 0505/4033] clean up imports, remove dead code --- src/clj/cljs/analyzer.clj | 3 +-- src/clj/cljs/closure.clj | 15 +++++---------- src/clj/cljs/repl.clj | 4 +--- src/clj/cljs/repl/browser.clj | 3 +-- src/clj/cljs/repl/server.clj | 12 ++---------- src/clj/cljs/source_map.clj | 1 - 6 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 5aeacdcdf..15c619170 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -18,8 +18,7 @@ [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers] [clojure.edn :as edn]) - (:import java.lang.StringBuilder - [java.io File Reader PushbackReader] + (:import [java.io File Reader PushbackReader] [java.net URL] [cljs.tagged_literals JSValue])) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index a0edb0d52..7fe424903 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -47,9 +47,7 @@ java.io.BufferedInputStream java.net.URL java.util.logging.Level - java.util.jar.JarFile java.util.List - com.google.common.collect.ImmutableList com.google.javascript.jscomp.CompilerOptions com.google.javascript.jscomp.CompilerOptions$LanguageMode com.google.javascript.jscomp.CompilationLevel @@ -66,10 +64,6 @@ java.security.MessageDigest javax.xml.bind.DatatypeConverter)) -(defmacro ^:private debug-prn - [& args] - `(.println System/err (str ~@args))) - (def name-chars (map char (concat (range 48 57) (range 65 90) (range 97 122)))) (defn random-char [] @@ -979,6 +973,10 @@ should contain the source for the given namespace name." (assert (not (and output-wrapper (= :whitespace optimizations))) ":output-wrapper cannot be combined with :optimizations :whitespace")) +(defn foreign-source? [js] + (and (satisfies? deps/IJavaScript js) + (deps/-foreign? js))) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] @@ -1060,10 +1058,7 @@ should contain the source for the given namespace name." (util/measure compiler-stats "Optimize sources" (apply optimize all-opts - (remove - #(and (satisfies? deps/IJavaScript %) - (deps/-foreign? %)) - js-sources))) + (remove foreign-source? js-sources))) (add-wrapper all-opts) (add-source-map-link all-opts) (add-header all-opts) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index f1ff14bdf..4671b831d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -11,7 +11,6 @@ (:require [clojure.java.io :as io] [cljs.compiler :as comp] [cljs.analyzer :as ana] - [cljs.analyzer.api :as ana-api] [cljs.env :as env] [cljs.tagged-literals :as tags] [cljs.closure :as cljsc] @@ -20,8 +19,7 @@ [clojure.tools.reader.reader-types :as readers] [cljs.util :as util] [clojure.stacktrace :as trace]) - (:import [java.io File PushbackReader] - [javax.xml.bind DatatypeConverter])) + (:import [javax.xml.bind DatatypeConverter])) (def ^:dynamic *cljs-verbose* false) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 5ddee2f5c..85931e3fc 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -16,8 +16,7 @@ [cljs.closure :as cljsc] [cljs.repl :as repl] [cljs.repl.server :as server]) - (:import cljs.repl.IJavaScriptEnv - [java.util.regex Pattern])) + (:import [java.util.regex Pattern])) (defonce browser-state (atom {:return-value-fn nil diff --git a/src/clj/cljs/repl/server.clj b/src/clj/cljs/repl/server.clj index 7ad2db4e7..e26c53cd8 100644 --- a/src/clj/cljs/repl/server.clj +++ b/src/clj/cljs/repl/server.clj @@ -1,17 +1,9 @@ (ns cljs.repl.server (:refer-clojure :exclude [loaded-libs]) - (:require [clojure.string :as str] - [clojure.java.io :as io] - [cljs.compiler :as comp] - [cljs.closure :as cljsc] - [cljs.repl :as repl]) + (:require [clojure.string :as str]) (:import java.io.BufferedReader - java.io.BufferedWriter java.io.InputStreamReader - java.io.OutputStreamWriter - java.net.Socket - java.net.ServerSocket - cljs.repl.IJavaScriptEnv)) + java.net.ServerSocket)) (defonce state (atom diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index 5695f9f85..8f9b33a9d 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -3,7 +3,6 @@ [clojure.string :as string] [clojure.data.json :as json] [clojure.set :as set] - [clojure.pprint :as pp] [cljs.source-map.base64-vlq :as base64-vlq])) ;; ============================================================================= From 1c0dddabd769f0388ff2e29ba6f21a4479d7603e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Jan 2015 10:13:13 -0500 Subject: [PATCH 0506/4033] produce a more sensible error if `cljs.core/*main-cli-fn*` is not set for :target :nodejs --- src/cljs/cljs/nodejscli.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/nodejscli.cljs b/src/cljs/cljs/nodejscli.cljs index d4984651b..404336f3d 100644 --- a/src/cljs/cljs/nodejscli.cljs +++ b/src/cljs/cljs/nodejscli.cljs @@ -13,5 +13,8 @@ (:require [cljs.nodejs :as nodejs])) ; Call the user's main function -(apply cljs.core/*main-cli-fn* (drop 2 (.-argv nodejs/process))) +(if (or (nil? cljs.core/*main-cli-fn*) + (not (fn? cljs.core/*main-cli-fn*))) + (throw (js/Error. "cljs.core/*main-cli-fn* not set")) + (apply cljs.core/*main-cli-fn* (drop 2 (.-argv nodejs/process)))) From ed638a48b54b457410f076e9116a8c503f36be8c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 12:31:14 -0500 Subject: [PATCH 0507/4033] re-add missing import to cljs.repl, cleanup --- src/clj/cljs/repl.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 4671b831d..052fe259d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -19,7 +19,8 @@ [clojure.tools.reader.reader-types :as readers] [cljs.util :as util] [clojure.stacktrace :as trace]) - (:import [javax.xml.bind DatatypeConverter])) + (:import [java.io File PushbackReader] + [javax.xml.bind DatatypeConverter])) (def ^:dynamic *cljs-verbose* false) @@ -388,7 +389,7 @@ (print (str "ClojureScript:" ana/*cljs-ns* "> ")) (flush) (let [rdr (readers/source-logging-push-back-reader - (java.io.PushbackReader. (io/reader *in*)) + (PushbackReader. (io/reader *in*)) 1 "NO_SOURCE_FILE") form (try From 0ed8acdd60f4867e12ab4b4219419ca562630d22 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 12:33:26 -0500 Subject: [PATCH 0508/4033] foreign deps in ns form should not emit anything, handled via goog.require remove foreign deps from optimization, preamble to optimized build --- src/clj/cljs/closure.clj | 8 ++++++++ src/clj/cljs/compiler.clj | 16 ++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 7fe424903..56c2c262d 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -893,6 +893,12 @@ should contain the source for the given namespace name." (defn add-header [opts js] (str (make-preamble opts) js)) +(defn add-foreign-deps [opts sources js] + (letfn [(to-js-str [ijs] + (let [url (or (:file-min ijs) (:file ijs))] + (slurp url)))] + (apply str (map to-js-str sources) [js]))) + (defn add-wrapper [{:keys [output-wrapper] :as opts} js] (if output-wrapper (str ";(function(){\n" js "\n})();\n") @@ -1062,6 +1068,8 @@ should contain the source for the given namespace name." (add-wrapper all-opts) (add-source-map-link all-opts) (add-header all-opts) + (add-foreign-deps all-opts + (filter foreign-source? js-sources)) (output-one-file all-opts))) (apply output-unoptimized all-opts js-sources))] ;; emit Node.js bootstrap script for :none & :whitespace optimizations diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index ceb9445de..5522b7148 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -812,19 +812,11 @@ (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") (emitln loaded-libs " = cljs.core.set();")) (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] - (cond - (ana/foreign-dep? lib) - (if-not (= :nodejs (get-in @env/*compiler* [:options :target])) - (emitln "cljs.core.load_lib(\"" (munge (name lib)) "\");") - (let [js-index (:js-dependency-index @env/*compiler*) - ijs-url (get-in js-index [(name lib) :url])] - (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");"))) - - - (-> libs meta :reload) + (if (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', true);") - - :else (emitln "goog.require('" (munge lib) "');"))) + ;; if foreign lib, emit nothing + (when-not (ana/foreign-dep? lib) + (emitln "goog.require('" (munge lib) "');")))) (when (-> libs meta :reload-all) (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))) From e40e00c8a7ceb228366e1919191e159b01e24235 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 12:41:53 -0500 Subject: [PATCH 0509/4033] under Node.js we still need to emit `cljs.core/load-file` since we patch `goog.require`. remove `cljs.core/load-lib`, useless in browser context. --- src/clj/cljs/compiler.clj | 15 +++++++++++---- src/cljs/cljs/core.cljs | 5 ----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 5522b7148..f59185efb 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -812,11 +812,18 @@ (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") (emitln loaded-libs " = cljs.core.set();")) (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] - (if (-> libs meta :reload) + (cond + ;; only emit if foreign lib under Node.js + (ana/foreign-dep? lib) + (when (= :nodejs (get-in @env/*compiler* [:options :target])) + (let [js-index (:js-dependency-index @env/*compiler*) + ijs-url (get-in js-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");"))) + + (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', true);") - ;; if foreign lib, emit nothing - (when-not (ana/foreign-dep? lib) - (emitln "goog.require('" (munge lib) "');")))) + + :else (emitln "goog.require('" (munge lib) "');"))) (when (-> libs meta :reload-all) (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index e20a05322..9930c53e4 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -153,11 +153,6 @@ (when-not js/COMPILED (cljs.core/load-file* file))) -;; INTERNAL - do not use -(defn load-lib [lib] - (when-not js/COMPILED - (cljs.core/load-lib* lib))) - (if (and (exists? js/Symbol) (identical? (goog/typeOf js/Symbol) "function")) (def ITER_SYMBOL (.-iterator js/Symbol)) From eafef3a27605ae6626371373667d6b10aaee9f47 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 14:07:44 -0500 Subject: [PATCH 0510/4033] mape foreign lib :file-min -> :url-min do not discard IJavaScript information place foreign deps between header & optimized build --- src/clj/cljs/closure.clj | 24 +++++++++++++----------- src/clj/cljs/js_deps.clj | 10 +++++++--- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 56c2c262d..893bf73d3 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -441,7 +441,7 @@ deps #{}] (if (seq requires) (let [node (or (get (@env/*compiler* :js-dependency-index) (first requires)) - (deps/find-classpath-lib (first requires))) + (deps/find-classpath-lib (first requires))) new-req (remove #(contains? visited %) (:requires node))] (recur (into (rest requires) new-req) (into visited new-req) @@ -537,11 +537,11 @@ should contain the source for the given namespace name." (deps/dependency-order (concat (map - (fn [{:keys [foreign url file provides requires group]}] + (fn [{:keys [foreign url file provides requires] :as js-map}] (let [url (or url (io/resource file))] - (assoc + (merge (javascript-file foreign url provides requires) - :group group))) + js-map))) required-js) [(when (-> @env/*compiler* :opts :emit-constants) (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] @@ -803,10 +803,11 @@ should contain the source for the given namespace name." (when-not (.exists out-file) (util/mkdirs out-file) (spit out-file (deps/-source js))) - {:url (deps/to-url out-file) - :requires (deps/-requires js) - :provides (deps/-provides js) - :group (:group js)})) + (merge js + {:url (deps/to-url out-file) + :requires (deps/-requires js) + :provides (deps/-provides js) + :group (:group js)}))) (defn write-js? "Returns true if IJavaScript instance needs to be written/copied to output @@ -895,9 +896,10 @@ should contain the source for the given namespace name." (defn add-foreign-deps [opts sources js] (letfn [(to-js-str [ijs] - (let [url (or (:file-min ijs) (:file ijs))] + (util/debug-prn (into {} ijs)) + (let [url (or (:url-min ijs) (:url ijs))] (slurp url)))] - (apply str (map to-js-str sources) [js]))) + (apply str (concat (map to-js-str sources) [js])))) (defn add-wrapper [{:keys [output-wrapper] :as opts} js] (if output-wrapper @@ -1067,9 +1069,9 @@ should contain the source for the given namespace name." (remove foreign-source? js-sources))) (add-wrapper all-opts) (add-source-map-link all-opts) - (add-header all-opts) (add-foreign-deps all-opts (filter foreign-source? js-sources)) + (add-header all-opts) (output-one-file all-opts))) (apply output-unoptimized all-opts js-sources))] ;; emit Node.js bootstrap script for :none & :whitespace optimizations diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index 0f1d89815..603418873 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -185,10 +185,14 @@ case." and :provides), returns a map containing :provides, :requires, :file and :url" ([lib-spec] (load-foreign-library* lib-spec false)) - ([lib-spec cp-only?] + ([lib-spec cp-only?] (let [find-func (if cp-only? io/resource find-url)] - (merge lib-spec {:foreign true - :url (find-func (:file lib-spec))})))) + (cond-> + (merge lib-spec + {:foreign true + :url (find-func (:file lib-spec))}) + (:file-min lib-spec) + (assoc :url-min (find-func (:file-min lib-spec))))))) (def load-foreign-library (memoize load-foreign-library*)) From c1be9e96944afb6f1ac1c09bae1785c49a40ba53 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 14:35:59 -0500 Subject: [PATCH 0511/4033] REPLs also need to get upstream deps --- src/clj/cljs/repl.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 052fe259d..0212c665d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -353,9 +353,12 @@ [repl-env opts] (print "To quit, type: ") (prn :cljs/quit) - (let [{:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts + (let [ups-deps (cljsc/get-upstream-deps) + {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} - (merge (-repl-options repl-env) opts)] + (assoc (merge (-repl-options repl-env) opts) + :ups-libs (:libs ups-deps) + :ups-foreign-libs (:foreign-libs ups-deps))] (env/with-compiler-env (or (::env/compiler repl-env) (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user From 36c77632bce76ec2029553f18993820a033188c4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 14:46:10 -0500 Subject: [PATCH 0512/4033] remove `cljs.core/load-lib*` helper macro too --- src/clj/cljs/core.clj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 135650847..96f2cf67d 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1697,8 +1697,3 @@ ;; under Node.js, always relative to JVM working directory :nodejs `(. js/goog (~'nodeGlobalRequire (str ~output-dir ~File/separator ~f))) `(. js/goog (~'importScript_ ~f))))) - -(defmacro load-lib* [lib] - `(. js/goog - (~'importScript_ - (str js/goog.basePath (. js/goog (~'getPathFromDeps_ ~lib)))))) \ No newline at end of file From 27d8142ef89ed45750b5276e69b568d72371a40a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 14:55:06 -0500 Subject: [PATCH 0513/4033] goog.nodeGlobalRequire should return the result --- src/cljs/cljs/bootstrap_node.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index be77e02c7..5a1ebac60 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -79,7 +79,7 @@ global.CLOSURE_IMPORT_SCRIPT = function(src) { // Declared here so it can be used to require base.js function nodeGlobalRequire(file) { - vm.Script.runInThisContext.call(global, fs.readFileSync(file), file); + return vm.Script.runInThisContext.call(global, fs.readFileSync(file), file); } From 90085a8f30a459d35385573e80792f5845374544 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 15:34:21 -0500 Subject: [PATCH 0514/4033] when eval'ing JS in goog.nodeGlobalRequire remove module property to evade Node.js detection in JS libraries --- src/cljs/cljs/bootstrap_node.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index 5a1ebac60..6c91454f5 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -79,7 +79,10 @@ global.CLOSURE_IMPORT_SCRIPT = function(src) { // Declared here so it can be used to require base.js function nodeGlobalRequire(file) { - return vm.Script.runInThisContext.call(global, fs.readFileSync(file), file); + var _module = global.module; + global.module = undefined; + vm.Script.runInThisContext.call(global, fs.readFileSync(file), file); + global.module = _module; } From 39fd5e520dafdf7f3583600a5f5b4263c1f9d998 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 17:16:32 -0500 Subject: [PATCH 0515/4033] need to compute source map offset when using foreign deps --- src/clj/cljs/closure.clj | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 893bf73d3..dc30b6402 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -659,15 +659,17 @@ should contain the source for the given namespace name." merged) (assoc merged path (get closure-source-map path)))) merged))) - (spit (io/file name) - (sm/encode merged - {:preamble-line-count preamble-line-count - :lines (+ (:lineCount sm-json) preamble-line-count 2) - :file (:file sm-json) - :output-dir (util/output-directory opts) - :source-map-path (:source-map-path opts) - :source-map (:source-map opts) - :relpaths relpaths})))))) + (spit + (io/file name) + (sm/encode merged + {:preamble-line-count (+ preamble-line-count + (or (:foreign-deps-line-count opts) 0)) + :lines (+ (:lineCount sm-json) preamble-line-count 2) + :file (:file sm-json) + :output-dir (util/output-directory opts) + :source-map-path (:source-map-path opts) + :source-map (:source-map opts) + :relpaths relpaths})))))) source) (report-failure result)))) @@ -894,12 +896,11 @@ should contain the source for the given namespace name." (defn add-header [opts js] (str (make-preamble opts) js)) -(defn add-foreign-deps [opts sources js] +(defn foreign-deps-str [sources] (letfn [(to-js-str [ijs] - (util/debug-prn (into {} ijs)) (let [url (or (:url-min ijs) (:url ijs))] (slurp url)))] - (apply str (concat (map to-js-str sources) [js])))) + (apply str (map to-js-str sources)))) (defn add-wrapper [{:keys [output-wrapper] :as opts} js] (if output-wrapper @@ -1056,7 +1057,11 @@ should contain the source for the given namespace name." [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) optim (:optimizations all-opts) ret (if (and optim (not= optim :none)) - (do + (let [fdeps-str (foreign-deps-str + (filter foreign-source? js-sources)) + all-opts (assoc all-opts + :foreign-deps-line-count + (- (count (.split #"\r?\n" fdeps-str -1)) 1))] (when-let [fname (:source-map all-opts)] (assert (string? fname) (str ":source-map must name a file when using :whitespace, " @@ -1069,8 +1074,7 @@ should contain the source for the given namespace name." (remove foreign-source? js-sources))) (add-wrapper all-opts) (add-source-map-link all-opts) - (add-foreign-deps all-opts - (filter foreign-source? js-sources)) + (str fdeps-str) (add-header all-opts) (output-one-file all-opts))) (apply output-unoptimized all-opts js-sources))] From e40d83d5bfc472eddb9f8f1097d88ea555a1f0f4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 18:15:52 -0500 Subject: [PATCH 0516/4033] restrict load_file call for foreign deps to Node.js target. Fixes for foreign dep support in Rhino REPL --- src/clj/cljs/compiler.clj | 10 +++++----- src/clj/cljs/repl/rhino.clj | 31 +++++++++++++++++++------------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index f59185efb..b0acaffc0 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -814,11 +814,11 @@ (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond ;; only emit if foreign lib under Node.js - (ana/foreign-dep? lib) - (when (= :nodejs (get-in @env/*compiler* [:options :target])) - (let [js-index (:js-dependency-index @env/*compiler*) - ijs-url (get-in js-index [(name lib) :url])] - (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");"))) + (and (ana/foreign-dep? lib) + (= :nodejs (get-in @env/*compiler* [:options :target]))) + (let [js-index (:js-dependency-index @env/*compiler*) + ijs-url (get-in js-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");")) (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', true);") diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 38ac48520..cdfcef832 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -18,7 +18,8 @@ RhinoException Undefined])) (def ^String bootjs - (str "goog.require = function(rule){" + (str "var global = this;" + "goog.require = function(rule){" "Packages.clojure.lang.RT[\"var\"](\"cljs.repl.rhino\",\"goog-require\")" ".invoke(___repl_env, __repl_opts, rule);}")) @@ -75,13 +76,14 @@ :stacktrace (stacktrace ex)}))) (defn goog-require [repl-env opts rule] - (let [path (string/replace (comp/munge rule) \. File/separatorChar) - cljsc-path (str (util/output-directory opts) - File/separator (str path ".js")) - cljs-path (str path ".cljs") - js-path (str "goog/" - (-eval (str "goog.dependencies_.nameToPath['" rule "']") - repl-env "" 1))] + (let [path (string/replace (comp/munge rule) \. File/separatorChar) + output-dir (util/output-directory opts) + cljsc-path (str output-dir File/separator (str path ".js")) + cljs-path (str path ".cljs") + gpath (-eval (str "goog.dependencies_.nameToPath['" rule "']") + repl-env "" 1) + js-path (str "goog/" gpath) + js-out-path (io/file (str output-dir "/goog/" gpath))] (let [compiled (io/file cljsc-path)] (if (.exists compiled) ;; TODO: only take this path if analysis cache is available @@ -95,10 +97,15 @@ (if-let [res (io/resource js-path)] (with-open [reader (io/reader res)] (-eval reader repl-env js-path 1)) - (throw - (Exception. - (str "Cannot find " cljs-path - " or " js-path " in classpath"))))))))) + (if (.exists js-out-path) + (with-open [reader (io/reader js-out-path)] + (-eval reader repl-env js-path 1)) + (throw + (Exception. + (str "Cannot find " + cljs-path " or " + js-path " or " + (.getName js-out-path) " in classpath")))))))))) (defn load-javascript [repl-env ns url] (try From c222f6f0a0106ec93d3d5ad64291e17edf266f1d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 18:59:22 -0500 Subject: [PATCH 0517/4033] foreign deps should not be required unless explicitly request. Explicitly request it for the Rhino REPL --- src/clj/cljs/compiler.clj | 15 +++++++++------ src/clj/cljs/repl/rhino.clj | 3 +++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index b0acaffc0..d56b879be 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -814,16 +814,19 @@ (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond ;; only emit if foreign lib under Node.js - (and (ana/foreign-dep? lib) - (= :nodejs (get-in @env/*compiler* [:options :target]))) - (let [js-index (:js-dependency-index @env/*compiler*) - ijs-url (get-in js-index [(name lib) :url])] - (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");")) + (ana/foreign-dep? lib) + (if (= :nodejs (get-in @env/*compiler* [:options :target])) + (let [js-index (:js-dependency-index @env/*compiler*) + ijs-url (get-in js-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");")) + (when (get-in @env/*compiler* [:options :require-foreign]) + (emitln "goog.require('" (munge lib) "');"))) (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', true);") - :else (emitln "goog.require('" (munge lib) "');"))) + :else + (emitln "goog.require('" (munge lib) "');"))) (when (-> libs meta :reload-all) (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index cdfcef832..8ef854c67 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -131,6 +131,9 @@ (set! *print-fn* (fn [x] (.write js/out x)))))))) (defrecord RhinoEnv [] + repl/IReplEnvOptions + (-repl-options [this] + {:require-foreign true}) repl/IJavaScriptEnv (-setup [this opts] (rhino-setup this opts)) From 93dce672e1af8f698cfc2a61e293cb48aeeddc2c Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Thu, 22 Jan 2015 15:35:06 +0600 Subject: [PATCH 0518/4033] CLJS-985 make ex-info not lose stack information --- src/cljs/cljs/core.cljs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 9930c53e4..55e7a9610 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -9165,18 +9165,18 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype ExceptionInfo [message data cause]) -;;; ExceptionInfo is a special case, do not emulate this -(set! (.-prototype ExceptionInfo) (js/Error.)) -(set! (.. ExceptionInfo -prototype -constructor) ExceptionInfo) - (defn ex-info "Alpha - subject to change. Create an instance of ExceptionInfo, an Error type that carries a map of additional data." - ([msg map] - (ExceptionInfo. msg map nil)) - ([msg map cause] - (ExceptionInfo. msg map cause))) + ([msg data] (ex-info msg data nil)) + ([msg data cause] + ;; this way each new ExceptionInfo instance will inherit + ;; stack property from newly created Error + (set! (.-prototype ExceptionInfo) (js/Error msg)) + (set! (.. ExceptionInfo -prototype -name) "ExceptionInfo") + (set! (.. ExceptionInfo -prototype -constructor) ExceptionInfo) + (ExceptionInfo. msg data cause))) (defn ex-data "Alpha - subject to change. From 5a295faef143e3e20fc0873fa972fb73ed96f3fb Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jan 2015 19:28:22 -0500 Subject: [PATCH 0519/4033] 0.0-2719 --- README.md | 6 +++--- changes.md | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dcc96db32..affa5d29a 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2665 +Latest stable release: 0.0-2719 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2665"] +[org.clojure/clojurescript "0.0-2719"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2665 org.clojure clojurescript - 0.0-2665 + 0.0-2719 ``` diff --git a/changes.md b/changes.md index d75f64bc7..e17430838 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,21 @@ +## 0.0-2719 + +### Changes +* CLJS-985: make ex-info not lose stack information +* CLJS-984: Update Node.js REPL support to use public API +* CLJS-963: do not bother computing goog/dep.js under :none + +### Fixes +* CLJS-982: Var derefing should respect Clojure semantics +* CLJS-980: ClojureScript REPL stacktraces overrun prompt in many cases +* CLJS-979: ClojureScript REPL needs error handling for the special functions +* CLJS-971: :reload should work for require-macros special fn +* CLJS-980: ClojureScript REPL stacktraces overrun prompt in many cases +* CLJS-979: ClojureScript REPL needs error handling for the special functions +* CLJS-971: :reload should work for require-macros special fn +* CLJS-936: Multi arity bitwise operators +* CLJS-962: fix inconsistent hashing of empty collections + ## 0.0-2665 ### Changes From 588301dea585aba2c590ea2efb92961804c967d3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Jan 2015 11:12:40 -0500 Subject: [PATCH 0520/4033] removed dupes --- changes.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/changes.md b/changes.md index e17430838..a3ea7d00a 100644 --- a/changes.md +++ b/changes.md @@ -10,9 +10,6 @@ * CLJS-980: ClojureScript REPL stacktraces overrun prompt in many cases * CLJS-979: ClojureScript REPL needs error handling for the special functions * CLJS-971: :reload should work for require-macros special fn -* CLJS-980: ClojureScript REPL stacktraces overrun prompt in many cases -* CLJS-979: ClojureScript REPL needs error handling for the special functions -* CLJS-971: :reload should work for require-macros special fn * CLJS-936: Multi arity bitwise operators * CLJS-962: fix inconsistent hashing of empty collections From 7f6f8fd2745a49ac87d3307bdb6e15e91abc26e2 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Sat, 24 Jan 2015 14:53:52 +0600 Subject: [PATCH 0521/4033] CLJS-983 make ExceptionInfo printalbe --- src/cljs/cljs/core.cljs | 19 +++++++++++++++++++ test/cljs/cljs/core_test.cljs | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 55e7a9610..3c2b389a0 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -9165,6 +9165,17 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype ExceptionInfo [message data cause]) +(defn- pr-writer-ex-info [obj writer opts] + (-write writer "#ExceptionInfo{:message ") + (pr-writer (.-message obj) writer opts) + (when (.-data obj) + (-write writer ", :data ") + (pr-writer (.-data obj) writer opts)) + (when (.-cause obj) + (-write writer ", :cause ") + (pr-writer (.-cause obj) writer opts)) + (-write writer "}")) + (defn ex-info "Alpha - subject to change. Create an instance of ExceptionInfo, an Error type that carries a @@ -9176,6 +9187,14 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (set! (.-prototype ExceptionInfo) (js/Error msg)) (set! (.. ExceptionInfo -prototype -name) "ExceptionInfo") (set! (.. ExceptionInfo -prototype -constructor) ExceptionInfo) + + ;; since we've changed the prototype, we need to + ;; re-establish protocol implementations here + (set! (.. ExceptionInfo -prototype -toString) pr-str*) + (extend-type ExceptionInfo + IPrintWithWriter + (-pr-writer [obj writer opts] + (pr-writer-ex-info obj writer opts))) (ExceptionInfo. msg data cause))) (defn ex-data diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 362060cc4..5f4b4e944 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1913,6 +1913,8 @@ (catch ExceptionInfo e (ex-data e))))) (is (instance? js/Error (ex-info "asdf" {:foo 1}))) + (is (= (pr-str (ex-info "abc" {:x 1})) "#ExceptionInfo{:message \"abc\", :data {:x 1}}")) + (is (= (pr-str (ex-info "abc" {:x 1} "def")) "#ExceptionInfo{:message \"abc\", :data {:x 1}, :cause \"def\"}")) (is (not (instance? cljs.core.ExceptionInfo (js/Error.))))) (deftest test-435 From 2b6baa2d1e059839a66e2ef90b84f2d025d992dc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Jan 2015 15:43:19 -0500 Subject: [PATCH 0522/4033] CLJS-851: simplify :none script inclusion if :main supplied --- src/clj/cljs/closure.clj | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index dc30b6402..0b262ff75 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -770,6 +770,13 @@ should contain the source for the given namespace name." (defn output-deps-file [opts sources] (output-one-file opts (deps-file opts sources))) +(defn output-main-file [opts] + (let [output-dir (util/output-directory opts)] + (output-one-file opts + (str "document.write('');" + "document.write('');" + "document.write('');")))) + (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, or is a foreign lib, return the path relative to the output @@ -858,11 +865,21 @@ should contain the source for the given namespace name." The deps file for the current project will include third-party libraries." [opts & sources] - (let [disk-sources (map #(source-on-disk opts %) sources) - goog-deps (io/file (util/output-directory opts) "goog/deps.js")] + (let [disk-sources (remove #(= (:group %) :goog) + (map #(source-on-disk opts %) sources)) + goog-deps (io/file (util/output-directory opts) "goog/deps.js") + main (:main opts)] (util/mkdirs goog-deps) (spit goog-deps (slurp (io/resource "goog/deps.js"))) - (output-deps-file opts (remove #(= (:group %) :goog) disk-sources)))) + (if (and main (not= (:target opts) :nodejs)) + (do + (output-deps-file + (assoc opts :output-to + (str (util/output-directory opts) + File/separator "cljs_deps.js")) + disk-sources) + (output-main-file opts)) + (output-deps-file opts disk-sources)))) (comment From e65e0c67dc0e6c74ec367840075dda0216713e76 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Jan 2015 15:48:53 -0500 Subject: [PATCH 0523/4033] 0.0-2723 --- README.md | 6 +++--- changes.md | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index affa5d29a..e90c485f0 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2719 +Latest stable release: 0.0-2723 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2719"] +[org.clojure/clojurescript "0.0-2723"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2719 org.clojure clojurescript - 0.0-2719 + 0.0-2723 ``` diff --git a/changes.md b/changes.md index a3ea7d00a..fc41f915c 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,11 @@ +## 0.0-2723 + +### Enhancements +* CLJS-851: simplify :none script inclusion if :main supplied +* CLJS-983: make ExceptionInfo printable + +### Fixes + ## 0.0-2719 ### Changes From e5462c173bbaa51334abc15e4abb87402e98c02f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Jan 2015 16:16:39 -0500 Subject: [PATCH 0524/4033] fix `cljs.closure/write-javascript` so that non-map IJavaScript instances are correctly handled --- src/clj/cljs/closure.clj | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 0b262ff75..2c268c1cf 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -808,15 +808,17 @@ should contain the source for the given namespace name." [opts js] (let [out-dir (io/file (util/output-directory opts)) out-name (rel-output-path js) - out-file (io/file out-dir out-name)] + out-file (io/file out-dir out-name) + ijs {:url (deps/to-url out-file) + :requires (deps/-requires js) + :provides (deps/-provides js) + :group (:group js)}] (when-not (.exists out-file) (util/mkdirs out-file) (spit out-file (deps/-source js))) - (merge js - {:url (deps/to-url out-file) - :requires (deps/-requires js) - :provides (deps/-provides js) - :group (:group js)}))) + (if (map? js) + (merge js ijs) + ijs))) (defn write-js? "Returns true if IJavaScript instance needs to be written/copied to output From 7e854ebea8222fde6ea0bc4726b8540af3832fb2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Jan 2015 16:19:50 -0500 Subject: [PATCH 0525/4033] 0.0-2725 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e90c485f0..8d5ab69d0 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2723 +Latest stable release: 0.0-2725 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2723"] +[org.clojure/clojurescript "0.0-2725"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2723 org.clojure clojurescript - 0.0-2723 + 0.0-2725 ``` diff --git a/changes.md b/changes.md index fc41f915c..a6d4aae52 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2725 + +### Fixes +* Fix Node.js support regression + ## 0.0-2723 ### Enhancements From 9f1e7b8226a9ab187461e054c0ff7d7a06c1c026 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Jan 2015 20:23:00 -0500 Subject: [PATCH 0526/4033] provide :asset-path option so that :main users can configure how the :none optimization script tags get written out --- src/clj/cljs/closure.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 2c268c1cf..abf7a98d8 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -771,10 +771,11 @@ should contain the source for the given namespace name." (output-one-file opts (deps-file opts sources))) (defn output-main-file [opts] - (let [output-dir (util/output-directory opts)] + (let [asset-path (or (:asset-path opts) + (util/output-directory opts))] (output-one-file opts - (str "document.write('');" - "document.write('');" + (str "document.write('');" + "document.write('');" "document.write('');")))) (defn ^String rel-output-path From 749cb03b472a9eac62561c7fca9d71126a6eb718 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Jan 2015 20:34:13 -0500 Subject: [PATCH 0527/4033] 0.0-2727 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d5ab69d0..58aed8d17 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2725 +Latest stable release: 0.0-2727 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2725"] +[org.clojure/clojurescript "0.0-2727"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2725 org.clojure clojurescript - 0.0-2725 + 0.0-2727 ``` diff --git a/changes.md b/changes.md index a6d4aae52..ad80e43d2 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2727 + +### Fixes +* Allow :main script imports to be configured via :asset-path + ## 0.0-2725 ### Fixes From 6c9776162c2e51c757010b0bb7aa8958a7786dc8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Jan 2015 21:25:13 -0500 Subject: [PATCH 0528/4033] many JS libs do Node.js detection via prescence `exports` object, remove from global when eval'ing foreign libs --- src/cljs/cljs/bootstrap_node.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index 6c91454f5..6930b45e0 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -79,9 +79,11 @@ global.CLOSURE_IMPORT_SCRIPT = function(src) { // Declared here so it can be used to require base.js function nodeGlobalRequire(file) { - var _module = global.module; + var _module = global.module, _exports = global.exports; global.module = undefined; + global.exports = undefined; vm.Script.runInThisContext.call(global, fs.readFileSync(file), file); + global.exports = _exports; global.module = _module; } From 4adfbc7c675ea41d774bd6635d1f535d5d5e5578 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jan 2015 09:18:37 -0500 Subject: [PATCH 0529/4033] Only use :url-min under :advanced optimizations for foreign deps --- src/clj/cljs/closure.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index abf7a98d8..55eb46d74 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -916,9 +916,11 @@ should contain the source for the given namespace name." (defn add-header [opts js] (str (make-preamble opts) js)) -(defn foreign-deps-str [sources] +(defn foreign-deps-str [opts sources] (letfn [(to-js-str [ijs] - (let [url (or (:url-min ijs) (:url ijs))] + (let [url (or (and (= (:optimizations opts) :advanced) + (:url-min ijs)) + (:url ijs))] (slurp url)))] (apply str (map to-js-str sources)))) @@ -1077,7 +1079,7 @@ should contain the source for the given namespace name." [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) optim (:optimizations all-opts) ret (if (and optim (not= optim :none)) - (let [fdeps-str (foreign-deps-str + (let [fdeps-str (foreign-deps-str opts (filter foreign-source? js-sources)) all-opts (assoc all-opts :foreign-deps-line-count From b8d62e233f5933a828bfedc6e3dcd1096685f9b8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jan 2015 09:22:09 -0500 Subject: [PATCH 0530/4033] typos --- src/clj/cljs/closure.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 55eb46d74..fdfc1a1f7 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1079,11 +1079,11 @@ should contain the source for the given namespace name." [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) optim (:optimizations all-opts) ret (if (and optim (not= optim :none)) - (let [fdeps-str (foreign-deps-str opts + (let [fdeps-str (foreign-deps-str all-opts (filter foreign-source? js-sources)) - all-opts (assoc all-opts - :foreign-deps-line-count - (- (count (.split #"\r?\n" fdeps-str -1)) 1))] + all-opts (assoc all-opts + :foreign-deps-line-count + (- (count (.split #"\r?\n" fdeps-str -1)) 1))] (when-let [fname (:source-map all-opts)] (assert (string? fname) (str ":source-map must name a file when using :whitespace, " From 61fcaf6d99f1a5b0f522a0cc5ce90871705ecc7d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jan 2015 11:16:47 -0500 Subject: [PATCH 0531/4033] add `cljs.util/path-join` helper for dealing with cross platform file paths --- src/clj/cljs/util.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index b541855d5..f511e6afd 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -127,3 +127,6 @@ (debug-prn "") ret#) ~expr))) + +(defn path-join [& xs] + (apply str (interpose File/separator xs))) From 8a1929f2ec7b02b606621e51abab4bbc034bc210 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jan 2015 11:48:42 -0500 Subject: [PATCH 0532/4033] remove obvious platform specific path construction --- src/clj/cljs/analyzer.clj | 2 +- src/clj/cljs/closure.clj | 10 +++++++--- src/clj/cljs/repl.clj | 2 +- src/clj/cljs/repl/node.clj | 8 +++++--- src/clj/cljs/repl/rhino.clj | 4 +++- src/cljs/cljs/bootstrap_node.js | 16 ++++++++-------- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 15c619170..165cde5c7 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1074,7 +1074,7 @@ (or (io/resource relpath) (let [root (:root @env/*compiler*) root-path (when root (.getPath ^File root)) - f (io/file (str root-path \/ relpath))] + f (io/file (util/path-join root-path relpath))] (when (and (.exists f) (.isFile f)) f)))) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index fdfc1a1f7..5454133ba 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -740,7 +740,9 @@ should contain the source for the given namespace name." [opts input] (letfn [(ns-list [coll] (when (seq coll) (apply str (interpose ", " (map #(str "'" (comp/munge %) "'") coll)))))] (str "goog.addDependency(\"" - (path-relative-to (io/file (util/output-directory opts) "goog/base.js") input) + (path-relative-to + (io/file (util/output-directory opts) + (util/path-join "goog" "base.js")) input) "\", [" (ns-list (deps/-provides input)) "], [" @@ -870,7 +872,8 @@ should contain the source for the given namespace name." [opts & sources] (let [disk-sources (remove #(= (:group %) :goog) (map #(source-on-disk opts %) sources)) - goog-deps (io/file (util/output-directory opts) "goog/deps.js") + goog-deps (io/file (util/output-directory opts) + (util/path-join "goog" "deps.js")) main (:main opts)] (util/mkdirs goog-deps) (spit goog-deps (slurp (io/resource "goog/deps.js"))) @@ -1103,7 +1106,8 @@ should contain the source for the given namespace name." ;; emit Node.js bootstrap script for :none & :whitespace optimizations (when (and (= (:target opts) :nodejs) (#{:none :whitespace} (:optimizations opts))) - (let [outfile (io/file (io/file (util/output-directory opts)) "goog/bootstrap/nodejs.js")] + (let [outfile (io/file (io/file (util/output-directory opts)) + (util/path-join "goog" "bootstrap" "nodejs.js"))] (util/mkdirs outfile) (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 0212c665d..bbb614129 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -240,7 +240,7 @@ (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled)) (-evaluate repl-env f 1 (cljsc/src-file->goog-require src))) (binding [ana/*cljs-ns* 'cljs.user] - (let [res (if (= \/ (first f)) f (io/resource f))] + (let [res (if (= File/separatorChar (first f)) f (io/resource f))] (assert res (str "Can't find " f " in classpath")) (load-stream repl-env f res)))))) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 61483df84..bb1556310 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -117,10 +117,12 @@ (-> (slurp (io/resource "cljs/bootstrap_node.js")) (string/replace "__dirname" (str "\"" (str rewrite-path File/separator "bootstrap") "\"")) - (string/replace "./.." rewrite-path) (string/replace - "var CLJS_ROOT = \"./\";" - (str "var CLJS_ROOT = \"" (.getPath root-path) "/\";")))) + "path.join(\".\", \"..\", src)" + (str "path.join(\"" rewrite-path "\", src)")) + (string/replace + "var CLJS_ROOT = \".\";" + (str "var CLJS_ROOT = \"" (.getPath root-path) "\";")))) ;; load the deps file so we can goog.require cljs.core etc. (node-eval repl-env (str "require('" diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 8ef854c67..64acd47dc 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -83,7 +83,9 @@ gpath (-eval (str "goog.dependencies_.nameToPath['" rule "']") repl-env "" 1) js-path (str "goog/" gpath) - js-out-path (io/file (str output-dir "/goog/" gpath))] + js-out-path (io/file + (util/path-join output-dir "goog" + (string/replace gpath \/ File/separatorChar)))] (let [compiled (io/file cljsc-path)] (if (.exists compiled) ;; TODO: only take this path if analysis cache is available diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index 6930b45e0..cb92671f3 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -42,7 +42,7 @@ var fs = require("fs"); var vm = require("vm"); var path = require("path"); -var CLJS_ROOT = "./"; +var CLJS_ROOT = "."; /** @@ -60,19 +60,19 @@ global.goog = {}; global.CLOSURE_IMPORT_SCRIPT = function(src) { // if CLJS_ROOT has been rewritten (by REPLs) need to compute require path // so we can delete the old entry from the Node.js require cache - if(CLJS_ROOT !== "./") { - var path = null; - if(src.substring(0, 3) == "../") { - path = CLJS_ROOT+src.substring(3); + if(CLJS_ROOT !== ".") { + var cached = null; + if(src.substring(0, 2) == "..") { + cached = path.join(CLJS_ROOT, src.substring(3)); } else { - path = CLJS_ROOT+"goog/"+src; + cached = path.join(CLJS_ROOT, "goog", src); } - if(require.cache[path]) delete require.cache[path]; + if(require.cache[cached]) delete require.cache[cached]; } // Sources are always expressed relative to closure's base.js, but // require() is always relative to the current source. - require('./../' + src); + require(path.join(".", "..", src)); return true; }; From a79466b6174f2dcaec6c4e257f9c77e5f22c0a84 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jan 2015 12:55:12 -0500 Subject: [PATCH 0533/4033] change `cljs.util/get-name` to to split URLs on forward slash and return last component --- src/clj/cljs/util.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index f511e6afd..87d34b0c7 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -109,7 +109,7 @@ {:pre [(or (file? x) (url? x))]} (if (file? x) (filename x) - (last (path-seq (path x))))) + (last (string/split (path x) #"\/")))) (defn debug-prn [& args] From 186ccd3a81703af2702db2405014a5f01e268441 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jan 2015 14:53:15 -0500 Subject: [PATCH 0534/4033] Enhance foreign deps so that upstream foreign deps may be configured locally For example a user may desire that com.facebook.React actually be React with addons. This of course needs to be globally configurable otherwise code will be duplicated in concatenating builds. This change enables this knob tweaking. --- src/clj/cljs/js_deps.clj | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index 603418873..61aab9c13 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -119,19 +119,25 @@ case." (defn build-index "Index a list of dependencies by namespace and file name. There can - be zero or more namespaces provided per file." + be zero or more namespaces provided per file. Upstream foreign libraies + will have their options merged with local foreign libraries to support + fine-grained overriding." [deps] - (reduce (fn [m next] - (let [provides (:provides next)] - (-> (if (seq provides) - (reduce (fn [m* provide] - (assoc m* provide next)) - m - provides) - m) - (assoc (:file next) next)))) - {} - deps)) + (reduce + (fn [index dep] + (let [provides (:provides dep) + index' (if (seq provides) + (reduce + (fn [index' provide] + (if (:foreign dep) + (update-in index' [provide] merge dep) + (assoc index' provide dep))) + index provides) + index)] + (if (:foreign dep) + (update-in index' [(:file dep)] merge dep) + (assoc index' (:file dep) dep)))) + {} deps)) (defn dependency-order-visit [state ns-name] From 0b08991e6996c1fe5d4eae7c89f77d4c43f120c2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jan 2015 15:35:45 -0500 Subject: [PATCH 0535/4033] drop `cljs.util/path-join` in favor of using variadic `clojure.java.io/file` --- src/clj/cljs/analyzer.clj | 2 +- src/clj/cljs/closure.clj | 9 ++++----- src/clj/cljs/repl/rhino.clj | 5 ++--- src/clj/cljs/util.clj | 2 -- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 165cde5c7..d0cbf02db 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1074,7 +1074,7 @@ (or (io/resource relpath) (let [root (:root @env/*compiler*) root-path (when root (.getPath ^File root)) - f (io/file (util/path-join root-path relpath))] + f (io/file root-path relpath)] (when (and (.exists f) (.isFile f)) f)))) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 5454133ba..42f79df56 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -741,8 +741,7 @@ should contain the source for the given namespace name." (letfn [(ns-list [coll] (when (seq coll) (apply str (interpose ", " (map #(str "'" (comp/munge %) "'") coll)))))] (str "goog.addDependency(\"" (path-relative-to - (io/file (util/output-directory opts) - (util/path-join "goog" "base.js")) input) + (io/file (util/output-directory opts) "goog" "base.js") input) "\", [" (ns-list (deps/-provides input)) "], [" @@ -873,7 +872,7 @@ should contain the source for the given namespace name." (let [disk-sources (remove #(= (:group %) :goog) (map #(source-on-disk opts %) sources)) goog-deps (io/file (util/output-directory opts) - (util/path-join "goog" "deps.js")) + "goog" "deps.js") main (:main opts)] (util/mkdirs goog-deps) (spit goog-deps (slurp (io/resource "goog/deps.js"))) @@ -1106,8 +1105,8 @@ should contain the source for the given namespace name." ;; emit Node.js bootstrap script for :none & :whitespace optimizations (when (and (= (:target opts) :nodejs) (#{:none :whitespace} (:optimizations opts))) - (let [outfile (io/file (io/file (util/output-directory opts)) - (util/path-join "goog" "bootstrap" "nodejs.js"))] + (let [outfile (io/file (util/output-directory opts) + "goog" "bootstrap" "nodejs.js")] (util/mkdirs outfile) (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 64acd47dc..a1381b909 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -83,9 +83,8 @@ gpath (-eval (str "goog.dependencies_.nameToPath['" rule "']") repl-env "" 1) js-path (str "goog/" gpath) - js-out-path (io/file - (util/path-join output-dir "goog" - (string/replace gpath \/ File/separatorChar)))] + js-out-path (io/file output-dir "goog" + (string/replace gpath \/ File/separatorChar))] (let [compiled (io/file cljsc-path)] (if (.exists compiled) ;; TODO: only take this path if analysis cache is available diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 87d34b0c7..6143f5575 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -128,5 +128,3 @@ ret#) ~expr))) -(defn path-join [& xs] - (apply str (interpose File/separator xs))) From e3fbc43e820772c00994513112492f1212af01e1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 26 Jan 2015 08:39:18 -0500 Subject: [PATCH 0536/4033] Escape backslash before sending to Node.js process, addresses Windows platform issues --- src/clj/cljs/repl/node.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index bb1556310..563d63ac6 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -51,7 +51,8 @@ "Evaluate a JavaScript string in the Node REPL process." [repl-env js] (let [{:keys [in out]} @(:socket repl-env)] - (write out js) + ;; escape backslash for Node.js under Windows + (write out (string/replace js "\\" "\\\\")) (let [result (json/read-str (read-response in) :key-fn keyword)] (condp = (:status result) From f24a2742bf502f51baf178d7ff183ef0a8e166ed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 26 Jan 2015 09:18:31 -0500 Subject: [PATCH 0537/4033] Make source emitted by `cljs.closure/output-main-file` idempotent --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 42f79df56..531f8c312 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -775,7 +775,7 @@ should contain the source for the given namespace name." (let [asset-path (or (:asset-path opts) (util/output-directory opts))] (output-one-file opts - (str "document.write('');" + (str "if(typeof goog == \"undefined\") document.write('');" "document.write('');" "document.write('');")))) From 3edf9607206f5b68ed8c362d723f6da69f605328 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Jan 2015 12:48:03 -0500 Subject: [PATCH 0538/4033] more informative error if we can't load :main --- src/clj/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 531f8c312..61cfb5130 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -777,7 +777,8 @@ should contain the source for the given namespace name." (output-one-file opts (str "if(typeof goog == \"undefined\") document.write('');" "document.write('');" - "document.write('');")))) + "document.write('');")))) (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, From 80d46bdb7969c6a914324f1b9c81f8246d01f0a7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Jan 2015 13:07:42 -0500 Subject: [PATCH 0539/4033] another Windows path issue --- src/clj/cljs/closure.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 61cfb5130..7c6e3bbf1 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -473,8 +473,8 @@ should contain the source for the given namespace name." (defn source-for-namespace [ns compiler-env] - (let [ns-str (str (comp/munge ns)) - path (string/replace ns-str \. File/separatorChar) + (let [ns-str (str (comp/munge ns)) + path (string/replace ns-str \. \/) relpath (str path ".cljs")] (if-let [cljs-res (io/resource relpath)] {:relative-path relpath :uri cljs-res} From 593c9ead62b502dd38f8d071bd62fcdfddc5b89b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 28 Jan 2015 08:52:39 -0500 Subject: [PATCH 0540/4033] 0.0-2740 --- README.md | 6 +++--- changes.md | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 58aed8d17..db16c4b79 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2727 +Latest stable release: 0.0-2740 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2727"] +[org.clojure/clojurescript "0.0-2740"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2727 org.clojure clojurescript - 0.0-2727 + 0.0-2740 ``` diff --git a/changes.md b/changes.md index ad80e43d2..e49ee671f 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,14 @@ +## 0.0-2740 + +### Changes +* local :foreign-libs can precisely override upstream :foreign-libs +* :foreign-libs :file-min is only used under :advanced optimizations +* file generated by supplying :main now idempotent +* more informative error if :main incorrectly supplied + +### Fixes +* many fixes around file/resource handling for Windows users + ## 0.0-2727 ### Fixes From 2d81bde0a7e1fbcf1f4883cb144bf2b10cea393a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Jan 2015 13:00:18 -0800 Subject: [PATCH 0541/4033] CLJS-976: Node REPL breaks from uncaught exceptions Use Node.js domains to catch and print uncaught exceptions and prevent the process from dying --- src/clj/cljs/repl/node_repl.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index fbde78e8e..2c306924f 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -2,6 +2,7 @@ process.env.NODE_DISABLE_COLORS = true; var net = require("net"); var vm = require("vm"); +var dom = require("domain").create(); var PORT = 5001; try { @@ -16,6 +17,10 @@ net.createServer(function (socket) { socket.setEncoding("utf8"); + dom.on("error", function(ue) { + console.error(ue.stack); + }); + socket.on("data", function(data) { if(data[data.length-1] != "\0") { buffer += data; @@ -24,11 +29,14 @@ net.createServer(function (socket) { data = buffer + data; buffer = ""; } + if(data) { // not sure how \0's are getting through - David data = data.replace(/\0/g, ""); try { - ret = vm.Script.runInThisContext.call(global, data, "repl"); + dom.run(function() { + ret = vm.Script.runInThisContext.call(global, data, "repl"); + }); } catch (e) { err = e; } From 1611bdc7fa7ef21ed2e8543afaefd81b516bacec Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Jan 2015 13:10:35 -0800 Subject: [PATCH 0542/4033] CLJS-986: Add :target to the list of build options that should trigger recompilation --- src/clj/cljs/compiler.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index d56b879be..20e533817 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -908,9 +908,8 @@ (defn url-path [^File f] (.getPath (.toURL (.toURI f)))) -(defn- build-affecting-options - [opts] - (select-keys opts [:static-fns :optimize-constants :elide-asserts])) +(defn- build-affecting-options [opts] + (select-keys opts [:static-fns :optimize-constants :elide-asserts :target])) (defn compiled-by-string ([] (compiled-by-string nil)) From 87a14e1b8d253e28c2647c98ea91a42cd24a9972 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Jan 2015 14:14:52 -0800 Subject: [PATCH 0543/4033] CLJS-972: Node.js REPL eats errors in required ns when using require Argh, stop eating ns errors. It is now the responsibility of REPLs to supply semantics for `goog.require` --- src/clj/cljs/repl.clj | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index bbb614129..6aba6bcc9 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -149,14 +149,13 @@ (defn- display-error ([ret form] - (display-error ret form (constantly nil))) + (display-error ret form (constantly nil))) ([ret form f] - (when-not (and (seq? form) (= 'ns (first form))) - (f) - (println (:value ret)) - (when-let [st (:stacktrace ret)] - (println st) - (flush))))) + (f) + (println (:value ret)) + (when-let [st (:stacktrace ret)] + (println st) + (flush)))) (defn evaluate-form "Evaluate a ClojureScript form in the JavaScript environment. Returns a From f17fa01cfc11615c43ba38a1d2d0bf92f26ce69c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Jan 2015 14:16:15 -0800 Subject: [PATCH 0544/4033] remove dead comment --- src/clj/cljs/repl.clj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 6aba6bcc9..23ed0c227 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -204,10 +204,6 @@ (print js)) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] (case (:status ret) - ;;we eat ns errors because we know goog.provide() will throw when reloaded - ;;TODO - file bug with google, this is bs error - ;;this is what you get when you try to 'teach new developers' - ;;via errors (goog/base.js 104) :error (display-error ret form) :exception (display-error ret form (if (:repl-verbose opts) From 996f33e5250712072eaefb5eff13bb9372d5e1b6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Jan 2015 15:46:35 -0800 Subject: [PATCH 0545/4033] CLJS-993: binding macro returns non-nil with empty body --- src/clj/cljs/core.clj | 6 +++--- test/cljs/cljs/core_test.cljs | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 96f2cf67d..4ef50f342 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1117,11 +1117,11 @@ resets (reverse (map core/vector names tempnames)) bind-value (fn [[k v]] (core/list 'set! k v))] `(let [~@(interleave tempnames names)] + ~@(map bind-value binds) (try - ~@(map bind-value binds) - ~@body + ~@body (finally - ~@(map bind-value resets)))))) + ~@(map bind-value resets)))))) (defmacro binding "binding => var-symbol init-expr diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 5f4b4e944..69a506ec2 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2639,6 +2639,15 @@ (set! foo-set (fn [x] :oops)) (is (= (f [1 2 3]) :oops)))) +(deftest test-cljs-993 + (is (nil? (binding [*print-level* 4]))) + (is (= (binding [*print-level* 4] *print-level*) 4)) + (is (nil? (try + (binding [*print-level* 4] + (throw (js/Error.))) + (catch js/Error e + *print-level*))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 80d7007d618fd29b19702492d7ad8f205d7fd1f9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Jan 2015 17:43:49 -0800 Subject: [PATCH 0546/4033] fix :redef warnings --- src/clj/cljs/analyzer.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index d0cbf02db..bded70c38 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -656,6 +656,8 @@ :catch catch :children [try catch finally]})) +(declare get-expander) + (defmethod parse 'def [op env form name _] (let [pfn (fn @@ -686,7 +688,7 @@ (core-name? env sym)) (get-in @env/*compiler* [::namespaces ns-name :uses sym])) (let [ev (resolve-existing-var (dissoc env :locals) sym)] - (warning :redef env {:ev ev :sym sym :ns-name ns-name}) + (warning :redef env {:sym sym :ns (:ns ev) :ns-name ns-name}) (swap! env/*compiler* update-in [::namespaces ns-name :excludes] conj sym) (update-in env [:ns :excludes] conj sym)) env) From 1d53aebbe60f41abb11e6c872184e95ce457c4dc Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 31 Jan 2015 15:04:11 -0500 Subject: [PATCH 0547/4033] CLJS-991: Wrong inference - inconsistent behavior? `cljs.analyzer/infer-tag` was missing `:js` case --- src/clj/cljs/analyzer.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index bded70c38..203f5cedc 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -542,6 +542,7 @@ (infer-tag env (:init e)) (infer-tag env (:info e))) :dot 'any + :js 'any nil))) (defmulti parse (fn [op & rest] op)) From 336a36b7247473dacc6d21327a08e1bdab62660b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Feb 2015 10:21:38 -0500 Subject: [PATCH 0548/4033] support custom :node-command via Node REPL env options --- src/clj/cljs/repl/node.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 563d63ac6..af149678e 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -80,7 +80,7 @@ (string/replace (slurp (io/resource "cljs/repl/node_repl.js")) "var PORT = 5001;" (str "var PORT = " (:port repl-env) ";"))) - bldr (ProcessBuilder. (into-array ["node"])) + bldr (ProcessBuilder. (into-array [(get opts :node-command "node")])) _ (-> bldr (.redirectInput of) (.redirectOutput ProcessBuilder$Redirect/INHERIT) From c794bbc8ee3d0618f2bc98ead16bd0010b26105d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Feb 2015 11:16:20 -0500 Subject: [PATCH 0549/4033] remove dead comment --- src/clj/cljs/repl/node.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index af149678e..30da3ddb5 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -102,9 +102,7 @@ (let [core-js (closure/compile core (assoc opts :output-file - (closure/src-file->target-file core) - ;:static-fns true - )) + (closure/src-file->target-file core))) deps (closure/add-dependencies opts core-js)] ;; output unoptimized code and the deps file ;; for all compiled namespaces From 5a353c418769cc6d4c212db87e5f51b9511f7c33 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Feb 2015 13:31:57 -0500 Subject: [PATCH 0550/4033] Browser REPL needs to consider upstream deps fix Browser REPL issues. We need to monkey patch `goog.require` and `goog.isProvided_` --- src/clj/cljs/repl/browser.clj | 6 ++++- src/cljs/clojure/browser/repl.cljs | 40 +++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 85931e3fc..a5906024d 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -270,7 +270,11 @@ support reflection. Defaults to \"src/\". " [& {:as opts}] - (let [compiler-env (cljs.env/default-compiler-env opts) + (let [ups-deps (cljsc/get-upstream-deps) + opts (assoc opts + :ups-libs (:libs ups-deps) + :ups-foreign-libs (:foreign-libs ups-deps)) + compiler-env (cljs.env/default-compiler-env opts) opts (merge (BrowserEnv.) {:port 9000 :optimizations :simple diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 5038e4add..26b136abd 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -92,18 +92,34 @@ connection is made, the REPL will evaluate forms in the context of the document that called this function." [repl-server-url] - (let [repl-connection (net/xpc-connection - {:peer_uri repl-server-url})] + (let [repl-connection + (net/xpc-connection + {:peer_uri repl-server-url})] (swap! xpc-connection (constantly repl-connection)) (net/register-service repl-connection - :evaluate-javascript - (fn [js] - (net/transmit - repl-connection - :send-result - (evaluate-javascript repl-connection js)))) + :evaluate-javascript + (fn [js] + (net/transmit + repl-connection + :send-result + (evaluate-javascript repl-connection js)))) (net/connect repl-connection - (constantly nil) - (fn [iframe] - (set! (.-display (.-style iframe)) - "none"))))) + (constantly nil) + (fn [iframe] + (set! (.-display (.-style iframe)) + "none"))) + ;; Monkey-patch goog.require if running under optimizations :none - David + (when-not js/COMPILED + (set! *loaded-libs* (into #{} (js-keys (.. js/goog -dependencies_ -nameToPath)))) + (set! (.-isProvided_ js/goog) (fn [_] false)) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (.appendChild js/document.body + (let [script (.createElement js/document "script")] + (set! (.-type script) "text/javascript") + (set! (.-src script) + (str js/goog.basePath + (aget (.. js/goog -dependencies_ -nameToPath) name))) + script)))))))) From d4ebd8f004fb4e56d00d0fbe87d2488bf16bff60 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Feb 2015 14:48:09 -0500 Subject: [PATCH 0551/4033] fix browser REPL `*loaded-libs*` initialization, only include visited namespaces --- src/cljs/clojure/browser/repl.cljs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 26b136abd..57f3aa714 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -110,7 +110,13 @@ "none"))) ;; Monkey-patch goog.require if running under optimizations :none - David (when-not js/COMPILED - (set! *loaded-libs* (into #{} (js-keys (.. js/goog -dependencies_ -nameToPath)))) + (set! *loaded-libs* + (let [gntp (.. js/goog -dependencies_ -nameToPath)] + (into #{} + (filter + (fn [name] + (aget (.. js/goog -dependencies_ -visited) (aget gntp name))) + (js-keys gntp))))) (set! (.-isProvided_ js/goog) (fn [_] false)) (set! (.-require js/goog) (fn [name reload] From 05d0209c388bb1955af7170d573afae4d26695e1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Feb 2015 15:01:25 -0500 Subject: [PATCH 0552/4033] CLJS-927: real incremental compilation The `cljs.closure/build` now always recompiles dependent namespaces if parent namespaces have changed. --- src/clj/cljs/closure.clj | 3 ++- src/clj/cljs/compiler.clj | 13 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 7c6e3bbf1..87e075a96 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1041,7 +1041,8 @@ should contain the source for the given namespace name." (assoc-in [:opts :emit-constants] emit-constants) (assoc :target (:target opts)) (assoc :js-dependency-index (deps/js-dependency-index all-opts)))) - (binding [ana/*cljs-static-fns* + (binding [comp/*dependents* (atom {:recompile #{} :visited #{}}) + ana/*cljs-static-fns* (or (and (= (:optimizations opts) :advanced) (not (false? (:static-fns opts)))) (:static-fns opts) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 20e533817..48ed0d7e5 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -36,6 +36,7 @@ "volatile" "while" "with" "yield" "methods" "null"}) +(def ^:dynamic *dependents* nil) (def ^:dynamic *source-map-data* nil) (def ^:dynamic *lexical-renames* {}) @@ -997,7 +998,12 @@ (:source-map opts) (if (= (:optimizations opts) :none) (not (.exists (io/file (str (.getPath dest) ".map")))) - (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))))))) + (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))) + (let [{ns :ns} (ana/parse-ns src) + {:keys [recompile visited]} + (and *dependents* @*dependents*)] + (and (contains? recompile ns) + (not (contains? visited ns)))))))) (defn compile-file "Compiles src to a file of the same name, but with a .js extension, @@ -1027,6 +1033,11 @@ opts (if (= ns 'cljs.core) (assoc opts :static-fns true) opts)] (if (requires-compilation? src-file dest-file opts) (do + (when *dependents* + (swap! *dependents* + (fn [{:keys [recompile visited]}] + {:recompile (into recompile (ana/ns-dependents ns)) + :visited (conj visited ns)}))) (util/mkdirs dest-file) (when (contains? (::ana/namespaces @env/*compiler*) ns) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) From 28e6070691e89f5fe35ae8f8a196ae1048fbf07d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Feb 2015 17:10:34 -0500 Subject: [PATCH 0553/4033] CLJS-948: simplify macro usage When using or requiring a namespace that itself requires a macro file of the same name implicitly require the library's macros using the same provided spec information. --- src/clj/cljs/analyzer.clj | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 203f5cedc..6c5a0380d 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1224,6 +1224,13 @@ {:import import-map :require import-map})) +(defn macro-autoload-ns? [form] + (let [ns (if (sequential? form) (first form) form) + {:keys [use-macros require-macros]} + (get-in @env/*compiler* [::namespaces ns])] + (or (contains? use-macros ns) + (contains? require-macros ns)))) + (defn desugar-ns-specs [args] (let [{:keys [require] :as indexed} (->> args @@ -1239,7 +1246,9 @@ to-macro-specs (fn [specs] (->> specs - (filter #(and (sequential? %) (some sugar-keys %))) + (filter #(or (and (sequential? %) + (some sugar-keys %)) + (macro-autoload-ns? %))) (map #(->> % (remove-from-spec #{:include-macros}) (remove-from-spec #{:refer}) (map (fn [x] (if (= x :refer-macros) :refer x))))))) From 10920821bb075776a9b90fd7c64d7ebc45567996 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Feb 2015 17:28:25 -0500 Subject: [PATCH 0554/4033] 0.0-2755 --- README.md | 6 +++--- changes.md | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index db16c4b79..89adf7274 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2740 +Latest stable release: 0.0-2755 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2740"] +[org.clojure/clojurescript "0.0-2755"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2740 org.clojure clojurescript - 0.0-2740 + 0.0-2755 ``` diff --git a/changes.md b/changes.md index e49ee671f..8410fbfde 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,17 @@ +## 0.0-2755 + +### Enhancements +* CLJS-948: simplify macro usage + +### Fixes +* CLJS-927: real incremental compilation +* Browser REPL regressions +* CLJS-991: Wrong inference - inconsistent behavior? +* CLJS-993: binding macro returns non-nil with empty body +* CLJS-972: Node.js REPL eats errors in required ns when using require +* CLJS-986: Add :target to the list of build options that should trigger recompilation +* CLJS-976: Node REPL breaks from uncaught exceptions + ## 0.0-2740 ### Changes From 02d69cc77100240893a6317c27bc49c9df97b8c1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Feb 2015 23:54:10 -0500 Subject: [PATCH 0555/4033] comments --- src/clj/cljs/analyzer.clj | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 6c5a0380d..7b132b701 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1224,14 +1224,22 @@ {:import import-map :require import-map})) -(defn macro-autoload-ns? [form] +(defn macro-autoload-ns? + "Given a spec form check whether the spec namespace requires a macro file + of the same name. If so return true." + [form] (let [ns (if (sequential? form) (first form) form) {:keys [use-macros require-macros]} (get-in @env/*compiler* [::namespaces ns])] (or (contains? use-macros ns) (contains? require-macros ns)))) -(defn desugar-ns-specs [args] +(defn desugar-ns-specs + "Given an original set of ns specs desugar :include-macros and :refer-macros + usage into only primitive spec forms - :use, :require, :use-macros, + :require-macros. If a library includes a macro file of with the same name + as the namespace will also be desugared." + [args] (let [{:keys [require] :as indexed} (->> args (map (fn [[k & specs]] [k (into [] specs)])) From 0a3f64a11f9a4d8768c862b0d11f4908f41192eb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 2 Feb 2015 09:17:40 -0500 Subject: [PATCH 0556/4033] fix auto macro loading If namespace information is not in the compiler environment it must be parsed --- src/clj/cljs/analyzer.clj | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 7b132b701..ae136bb05 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1224,15 +1224,19 @@ {:import import-map :require import-map})) +(declare parse-ns) + (defn macro-autoload-ns? "Given a spec form check whether the spec namespace requires a macro file of the same name. If so return true." [form] (let [ns (if (sequential? form) (first form) form) {:keys [use-macros require-macros]} - (get-in @env/*compiler* [::namespaces ns])] - (or (contains? use-macros ns) - (contains? require-macros ns)))) + (or (get-in @env/*compiler* [::namespaces ns]) + (when-let [res (io/resource (util/ns->relpath ns))] + (:ast (parse-ns res))))] + (or (some #{ns} (vals use-macros)) + (some #{ns} (vals require-macros))))) (defn desugar-ns-specs "Given an original set of ns specs desugar :include-macros and :refer-macros @@ -1820,7 +1824,8 @@ argument, which the reader will use in any emitted errors." (get-in @env/*compiler* [:opts :emit-constants]) (conj 'constants-table))) :file dest - :source-file src} + :source-file src + :ast ast} (when (and dest (.exists ^File dest)) {:lines (with-open [reader (io/reader dest)] (-> reader line-seq count))}))) From 8688ae8cfe7aa8d998ccfaada2704a61afc74d65 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 2 Feb 2015 09:21:15 -0500 Subject: [PATCH 0557/4033] 0.0-2758 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 89adf7274..5d3a2b599 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2755 +Latest stable release: 0.0-2758 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2755"] +[org.clojure/clojurescript "0.0-2758"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2755 org.clojure clojurescript - 0.0-2755 + 0.0-2758 ``` diff --git a/changes.md b/changes.md index 8410fbfde..2efd719ed 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2758 + +### Fixes +* fix autoload macro enhancement + ## 0.0-2755 ### Enhancements From 8e4b72cae3b6e7426ae12770f87293e25057da66 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 2 Feb 2015 10:27:50 -0500 Subject: [PATCH 0558/4033] Remove assumption that spec is sequential from desugar-ns-specs --- src/clj/cljs/analyzer.clj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index ae136bb05..169d799ac 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1255,6 +1255,11 @@ spec (let [[l r] (split-with (complement pred) spec)] (recur pred (concat l (drop 2 r)))))) + replace-refer-macros + (fn [spec] + (if-not (sequential? spec) + spec + (map (fn [x] (if (= x :refer-macros) :refer x)) spec))) to-macro-specs (fn [specs] (->> specs @@ -1263,7 +1268,7 @@ (macro-autoload-ns? %))) (map #(->> % (remove-from-spec #{:include-macros}) (remove-from-spec #{:refer}) - (map (fn [x] (if (= x :refer-macros) :refer x))))))) + (replace-refer-macros))))) remove-sugar (partial remove-from-spec sugar-keys)] (if-let [require-specs (seq (to-macro-specs require))] (map (fn [[k v]] (cons k (map remove-sugar v))) From 01d2e2a83aff22798da4ab39f90437919ef653ba Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 2 Feb 2015 10:34:14 -0500 Subject: [PATCH 0559/4033] 0.0-2760 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5d3a2b599..a95a9c42e 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2758 +Latest stable release: 0.0-2760 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2758"] +[org.clojure/clojurescript "0.0-2760"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2758 org.clojure clojurescript - 0.0-2758 + 0.0-2760 ``` diff --git a/changes.md b/changes.md index 2efd719ed..fab46ead4 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2760 + +### Fixes +* ns spec handling regression + ## 0.0-2758 ### Fixes From d0c9451035d3a9adf9a32e16e759d1e4384268dc Mon Sep 17 00:00:00 2001 From: Pieter van Prooijen Date: Mon, 2 Feb 2015 19:52:52 +0100 Subject: [PATCH 0560/4033] CLJS-997 Nashorn repl bindings A repl binding to the Nashorn script engine, similar to the Rhino and node.js repls. Although Nashorn is slow loading javascript code, it still has a reasonable startup time (about 10s with fast trampolines, cache-analysis enabled and AOT compiled libraries on a four year old laptop). --- src/clj/cljs/repl/nashorn.clj | 168 ++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 src/clj/cljs/repl/nashorn.clj diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj new file mode 100644 index 000000000..ab2d9efc8 --- /dev/null +++ b/src/clj/cljs/repl/nashorn.clj @@ -0,0 +1,168 @@ +;; 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.repl.nashorn + (:require [clojure.java.io :as io] + [clojure.string :as string] + [cljs.analyzer :as ana] + [cljs.env :as env] + [cljs.repl :as repl] + [cljs.compiler :as comp] + [cljs.closure :as closure]) + (:import [javax.script ScriptEngine ScriptEngineManager])) + +;; Nashorn Clojurescript repl binding. +;; +;; Uses the Nashorn load() function to load Javascript files into the script engine. +;; +;; Nashorn's load() function docs: +;; http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/shell.html +;; +;; Some functions are copied from: https://github.com/bodil/cljs-nashorn and the node.js repl code + +;; +;; ** Usage from Leiningen: +;; +;; Create a file init_repl_test.clj containing (adjust :output-dir to your cljsbuild settings): + +(comment + (ns init-repl-test + (:require [cljs.repl] + [cljs.repl.nashorn])) + + (cljs.repl/repl (cljs.repl.nashorn/repl-env) :output-dir "resources/public/compiled" + :cache-analysis true)) + +;; +;; Invoke it with: +;; lein trampoline run -m clojure.main src-clj/init_repl_test.clj + +;; +;; ** Usage from nrepl / piggieback, execute the following at the nrepl prompt: +;; (adjust :output-dir to your cljsbuild settings) + +(comment + (ns init-repl-piggieback + (:require [cljs.repl.nashorn] + [cemerick.piggieback])) + + (cemerick.piggieback/cljs-repl :repl-env (cljs.repl.nashorn/repl-env) + :output-dir "resources/public/compiled" + :cache-analysis true)) + +;; Implementation + +(defn create-engine [] + (if-let [engine (.getEngineByName (ScriptEngineManager.) "nashorn")] + (let [context (.getContext engine)] + (.setWriter context *out*) + (.setErrorWriter context *err*) + engine) + (throw (IllegalArgumentException. + "Cannot find the Nashorn script engine, use a JDK version 8 or higher.")))) + +(defn eval-str [^ScriptEngine engine ^String s] + (.eval engine s)) + +(defn eval-resource + "Evaluate a file on the classpath in the engine." + [engine path debug] + (let [r (io/resource path)] + (eval-str engine (slurp r)) + (when debug (println "loaded: " path)))) + +(defn init-engine [engine output-dir debug] + (eval-resource engine "goog/base.js" debug) + (eval-resource engine "goog/deps.js" debug) + (eval-str engine "var global = this") ; required by React + (eval-str engine (format (str "var nashorn_load = function(path) {" + " var outputPath = \"%s\" + \"/\" + path;" + (when debug " print(\"loading: \" + outputPath) ; ") + " load(outputPath);" + "};") + output-dir)) + (eval-str engine (str "goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {" + " nashorn_load(\"goog/\" + path);" + " return true;" + "};")) + (eval-str engine "goog.global.isProvided_ = function(name) { return false; };") + engine) + +(defn load-js-file [engine file] + (eval-str engine (format "nashorn_load(\"%s\");" file))) + +;; Create a minimal build of Clojurescript from the core library. +;; +;; Copied rom clj.cljs.repl.node. +(defn bootstrap-repl [engine output-dir opts] + (env/ensure + (let [deps-file ".nashorn_repl_deps.js" + core (io/resource "cljs/core.cljs") + core-js (closure/compile core + (assoc opts :output-file (closure/src-file->target-file core))) + deps (closure/add-dependencies opts core-js)] + ;; output unoptimized code and the deps file + ;; for all compiled namespaces + (apply closure/output-unoptimized + (assoc opts :output-to (.getPath (io/file output-dir deps-file))) + deps) + ;; load the deps file so we can goog.require cljs.core etc. + (load-js-file engine deps-file)))) + +(defn load-ns [engine ns] + (eval-str engine (format "goog.require(\"%s\");" (comp/munge (first ns))))) + +(defn- stacktrace [^Exception e] + (apply str (interpose "\n" (map #(str " " (.toString %)) (.getStackTrace e))))) + +(def repl-filename "") + +(defrecord NashornEnv [engine debug] + repl/IReplEnvOptions + (-repl-options [this] {}) + repl/IJavaScriptEnv + (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] + (init-engine engine output-dir debug) + (let [env (ana/empty-env)] + (if output-to + (load-js-file engine output-to) + (bootstrap-repl engine output-dir opts)) + (repl/evaluate-form this env repl-filename + '(do + (.require js/goog "cljs.core") + (set! cljs.core/*print-fn* js/print))))) + + (-evaluate [{engine :engine :as this} filename line js] + (when debug (println "Evaluating: " js)) + (try {:status :success + :value (if-let [r (eval-str engine js)] (.toString r) "")} + (catch Throwable e + {:status :exception + :value (.toString e) + :stacktrace (stacktrace e)}))) + (-load [{engine :engine :as this} ns url] + (load-ns engine ns)) + (-tear-down [this])) + +(defn repl-env + "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript and as the + :repl-env argument to piggieback/cljs-repl. Opts has the following extra parameters: + + :output-dir the directory of the compiled files, e.g. \"resources/public/my-app\" (mandatory). + :output-to load this file initially into Nashorn, relative to output-dir. + Use a minimal bootstrapped cljs.core environment if not specified." + [& {debug :debug :as opts}] + + (let [engine (create-engine) + compiler-env (env/default-compiler-env) + env (merge (NashornEnv. engine debug) + {:cljs.env/compiler compiler-env} ; required by cider middleware ? + opts)] + env)) + + From df5f1cf8d44a3f1a5c8af3155255727f227ccc68 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 2 Feb 2015 16:02:22 -0500 Subject: [PATCH 0561/4033] add Nashorn REPL runner --- script/nashornrepljs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100755 script/nashornrepljs diff --git a/script/nashornrepljs b/script/nashornrepljs new file mode 100755 index 000000000..85c6188fe --- /dev/null +++ b/script/nashornrepljs @@ -0,0 +1,15 @@ +#!/bin/sh + +if [ "$CLOJURESCRIPT_HOME" = "" ]; then + CLOJURESCRIPT_HOME="`dirname $0`/.." +fi + +CLJSC_CP='' +for next in lib/*: src/clj: src/cljs: test/cljs; do + CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next +done + +java -server -cp $CLJSC_CP clojure.main -e \ +"(require '[cljs.repl :as repl]) +(require '[cljs.repl.nashorn :as nashorn]) +(repl/repl* (nashorn/repl-env $1) {:output-dir \".cljs_nashorn_repl\" :cache-analysis true :source-map true})" From ae362141659f661c91a816563772bf5c377134d9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 2 Feb 2015 19:02:51 -0500 Subject: [PATCH 0562/4033] formatting and code style --- src/clj/cljs/repl/nashorn.clj | 107 ++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index ab2d9efc8..966c3241e 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -35,8 +35,10 @@ (:require [cljs.repl] [cljs.repl.nashorn])) - (cljs.repl/repl (cljs.repl.nashorn/repl-env) :output-dir "resources/public/compiled" - :cache-analysis true)) + (cljs.repl/repl (cljs.repl.nashorn/repl-env) + :output-dir "resources/public/compiled" + :cache-analysis true) + ) ;; ;; Invoke it with: @@ -51,9 +53,11 @@ (:require [cljs.repl.nashorn] [cemerick.piggieback])) - (cemerick.piggieback/cljs-repl :repl-env (cljs.repl.nashorn/repl-env) - :output-dir "resources/public/compiled" - :cache-analysis true)) + (cemerick.piggieback/cljs-repl + :repl-env (cljs.repl.nashorn/repl-env) + :output-dir "resources/public/compiled" + :cache-analysis true) + ) ;; Implementation @@ -79,17 +83,20 @@ (defn init-engine [engine output-dir debug] (eval-resource engine "goog/base.js" debug) (eval-resource engine "goog/deps.js" debug) - (eval-str engine "var global = this") ; required by React - (eval-str engine (format (str "var nashorn_load = function(path) {" - " var outputPath = \"%s\" + \"/\" + path;" - (when debug " print(\"loading: \" + outputPath) ; ") - " load(outputPath);" - "};") - output-dir)) - (eval-str engine (str "goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {" - " nashorn_load(\"goog/\" + path);" - " return true;" - "};")) + (eval-str engine "var global = this") ; required by React + (eval-str engine + (format + (str "var nashorn_load = function(path) {" + " var outputPath = \"%s\" + \"/\" + path;" + (when debug " print(\"loading: \" + outputPath) ; ") + " load(outputPath);" + "};") + output-dir)) + (eval-str engine + (str "goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {" + " nashorn_load(\"goog/\" + path);" + " return true;" + "};")) (eval-str engine "goog.global.isProvided_ = function(name) { return false; };") engine) @@ -97,28 +104,30 @@ (eval-str engine (format "nashorn_load(\"%s\");" file))) ;; Create a minimal build of Clojurescript from the core library. -;; -;; Copied rom clj.cljs.repl.node. +;; Copied from clj.cljs.repl.node. (defn bootstrap-repl [engine output-dir opts] (env/ensure - (let [deps-file ".nashorn_repl_deps.js" - core (io/resource "cljs/core.cljs") - core-js (closure/compile core - (assoc opts :output-file (closure/src-file->target-file core))) - deps (closure/add-dependencies opts core-js)] - ;; output unoptimized code and the deps file - ;; for all compiled namespaces - (apply closure/output-unoptimized - (assoc opts :output-to (.getPath (io/file output-dir deps-file))) - deps) - ;; load the deps file so we can goog.require cljs.core etc. - (load-js-file engine deps-file)))) + (let [deps-file ".nashorn_repl_deps.js" + core (io/resource "cljs/core.cljs") + core-js (closure/compile core + (assoc opts + :output-file (closure/src-file->target-file core))) + deps (closure/add-dependencies opts core-js)] + ;; output unoptimized code and the deps file + ;; for all compiled namespaces + (apply closure/output-unoptimized + (assoc opts :output-to (.getPath (io/file output-dir deps-file))) + deps) + ;; load the deps file so we can goog.require cljs.core etc. + (load-js-file engine deps-file)))) (defn load-ns [engine ns] - (eval-str engine (format "goog.require(\"%s\");" (comp/munge (first ns))))) + (eval-str engine + (format "goog.require(\"%s\");" (comp/munge (first ns))))) (defn- stacktrace [^Exception e] - (apply str (interpose "\n" (map #(str " " (.toString %)) (.getStackTrace e))))) + (apply str + (interpose "\n" (map #(str " " (.toString %)) (.getStackTrace e))))) (def repl-filename "") @@ -132,19 +141,19 @@ (if output-to (load-js-file engine output-to) (bootstrap-repl engine output-dir opts)) - (repl/evaluate-form this env repl-filename - '(do - (.require js/goog "cljs.core") - (set! cljs.core/*print-fn* js/print))))) - + (repl/evaluate-form this env repl-filename + '(do + (.require js/goog "cljs.core") + (set! cljs.core/*print-fn* js/print))))) (-evaluate [{engine :engine :as this} filename line js] (when debug (println "Evaluating: " js)) - (try {:status :success - :value (if-let [r (eval-str engine js)] (.toString r) "")} - (catch Throwable e - {:status :exception - :value (.toString e) - :stacktrace (stacktrace e)}))) + (try + {:status :success + :value (if-let [r (eval-str engine js)] (.toString r) "")} + (catch Throwable e + {:status :exception + :value (.toString e) + :stacktrace (stacktrace e)}))) (-load [{engine :engine :as this} ns url] (load-ns engine ns)) (-tear-down [this])) @@ -157,12 +166,8 @@ :output-to load this file initially into Nashorn, relative to output-dir. Use a minimal bootstrapped cljs.core environment if not specified." [& {debug :debug :as opts}] - (let [engine (create-engine) - compiler-env (env/default-compiler-env) - env (merge (NashornEnv. engine debug) - {:cljs.env/compiler compiler-env} ; required by cider middleware ? - opts)] - env)) - - + compiler-env (env/default-compiler-env)] + (merge (NashornEnv. engine debug) + {:cljs.env/compiler compiler-env} ; required by cider middleware ? + opts))) \ No newline at end of file From 70499de3bf3bd4aa3d6525b779dd75b979bbf4ae Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Feb 2015 08:41:37 -0500 Subject: [PATCH 0563/4033] restrict string escaping to Node.js specific `cljs.core/load-file` calls --- src/clj/cljs/repl/node.clj | 2 +- src/cljs/cljs/core.cljs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 30da3ddb5..1250dd3bf 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -52,7 +52,7 @@ [repl-env js] (let [{:keys [in out]} @(:socket repl-env)] ;; escape backslash for Node.js under Windows - (write out (string/replace js "\\" "\\\\")) + (write out js) (let [result (json/read-str (read-response in) :key-fn keyword)] (condp = (:status result) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 3c2b389a0..efb37a607 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -151,7 +151,10 @@ ;; INTERNAL - do not use (defn load-file [file] (when-not js/COMPILED - (cljs.core/load-file* file))) + (let [file (if (identical? js/process.platform "win32") + (.replace file "\\" "\\\\") + file)] + (cljs.core/load-file* file)))) (if (and (exists? js/Symbol) (identical? (goog/typeOf js/Symbol) "function")) From 367eb0f5ac1fe0c6bf0b5655003f5db9b1685c1f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Feb 2015 09:08:22 -0500 Subject: [PATCH 0564/4033] remove questionable code --- src/cljs/cljs/core.cljs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index efb37a607..3c2b389a0 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -151,10 +151,7 @@ ;; INTERNAL - do not use (defn load-file [file] (when-not js/COMPILED - (let [file (if (identical? js/process.platform "win32") - (.replace file "\\" "\\\\") - file)] - (cljs.core/load-file* file)))) + (cljs.core/load-file* file))) (if (and (exists? js/Symbol) (identical? (goog/typeOf js/Symbol) "function")) From d124f71b4fa7dd3ceecb064eaf5a2f7392e738e1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Feb 2015 10:59:27 -0500 Subject: [PATCH 0565/4033] emit JS arrays of path components that are joined in the Node.js REPL process at runtime --- src/clj/cljs/repl/node.clj | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 1250dd3bf..5e8ff266b 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -9,6 +9,7 @@ (ns cljs.repl.node (:require [clojure.string :as string] [clojure.java.io :as io] + [cljs.util :as util] [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.repl :as repl] @@ -70,6 +71,12 @@ (node-eval repl-env (str "goog.require('" (comp/munge (first provides)) "')"))) +(defn seq->js-array [v] + (str "[" (apply str (interpose ", " (map pr-str v))) "]")) + +(defn platform-path [v] + (str "path.join.apply(null, " (seq->js-array v) ")")) + (defn setup ([repl-env] (setup repl-env nil)) ([repl-env opts] @@ -88,8 +95,13 @@ proc (.start bldr) env (ana/empty-env) core (io/resource "cljs/core.cljs") - root-path (.getCanonicalFile output-dir) - rewrite-path (str (.getPath root-path) File/separator "goog")] + ;; represent paths as vectors so we can emit JS arrays, this is to + ;; paper over Windows issues with minimum hassle - David + path (.getPath (.getCanonicalFile output-dir)) + [fc & cs] (rest (util/path-seq path)) ;; remove leading empty string + root (.substring path 0 (+ (.indexOf path fc) (count fc))) + root-path (vec (cons root cs)) + rewrite-path (conj root-path "goog")] ;; TODO: temporary hack, should wait till we can read the start string ;; from the process - David (Thread/sleep 300) @@ -114,19 +126,19 @@ ;; properly due to how we are running it - David (node-eval repl-env (-> (slurp (io/resource "cljs/bootstrap_node.js")) - (string/replace "__dirname" - (str "\"" (str rewrite-path File/separator "bootstrap") "\"")) + (string/replace "path.resolve(__dirname, '..', 'base.js')" + (platform-path (conj rewrite-path "bootstrap" ".." "base.js"))) (string/replace "path.join(\".\", \"..\", src)" - (str "path.join(\"" rewrite-path "\", src)")) + (str "path.join(" (platform-path rewrite-path) ", src)")) (string/replace "var CLJS_ROOT = \".\";" - (str "var CLJS_ROOT = \"" (.getPath root-path) "\";")))) + (str "var CLJS_ROOT = " (platform-path root-path) ";")))) ;; load the deps file so we can goog.require cljs.core etc. (node-eval repl-env - (str "require('" - (.getPath root-path) - File/separator "node_repl_deps.js')")) + (str "require(" + (platform-path (conj root-path "node_repl_deps.js")) + ")")) ;; monkey-patch isProvided_ to avoid useless warnings - David (node-eval repl-env (str "goog.isProvided_ = function(x) { return false; };")) From c2550c4fdc94178a7957497e2bfde54e5600c457 Mon Sep 17 00:00:00 2001 From: Moritz Ulrich Date: Tue, 3 Feb 2015 22:46:08 +0100 Subject: [PATCH 0566/4033] cljs.closure/get-upstream-deps: Add optional CLASSLOADER arg. Weasel & Piggieback don't run this function from the main thread, which is why the function fails to gather the dependencies. This introduces an optional (to keep the old behavior around) argument which allows the caller to specify the classloader to use. --- src/clj/cljs/closure.clj | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 87e075a96..4509f561f 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -906,13 +906,17 @@ should contain the source for the given namespace name." (defn get-upstream-deps* - "returns a merged map containing all upstream dependencies defined by libraries on the classpath" - [] - (let [classloader (. (Thread/currentThread) (getContextClassLoader)) - upstream-deps (map #(read-string (slurp %)) (enumeration-seq (. classloader (findResources "deps.cljs"))))] - #_(doseq [dep upstream-deps] - (println (str "Upstream deps.cljs found on classpath. " dep " This is an EXPERIMENTAL FEATURE and is not guarenteed to remain stable in future versions."))) - (apply merge-with concat upstream-deps))) + "returns a merged map containing all upstream dependencies defined + by libraries on the classpath. Should be run in the main thread. If + not, pass (java.lang.ClassLoader/getSystemClassLoader) to use the + system classloder." + ([] + (get-upstream-deps* (. (Thread/currentThread) (getContextClassLoader)))) + ([classloader] + (let [upstream-deps (map #(read-string (slurp %)) (enumeration-seq (. classloader (findResources "deps.cljs"))))] + #_(doseq [dep upstream-deps] + (println (str "Upstream deps.cljs found on classpath. " dep " This is an EXPERIMENTAL FEATURE and is not guarenteed to remain stable in future versions."))) + (apply merge-with concat upstream-deps)))) (def get-upstream-deps (memoize get-upstream-deps*)) From 26dbc4e5c88f48e15bd6be50ab8553adec7e88c3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 4 Feb 2015 10:19:40 -0500 Subject: [PATCH 0567/4033] throw if :nodejs target combined with optimizations :whitespace --- src/clj/cljs/closure.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 4509f561f..892b928e9 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1011,6 +1011,10 @@ should contain the source for the given namespace name." (assert (not (and output-wrapper (= :whitespace optimizations))) ":output-wrapper cannot be combined with :optimizations :whitespace")) +(defn check-node-target [{:keys [target optimizations] :as opts}] + (assert (not (and (= target :nodejs) (= optimizations :whitespace))) + (format ":nodejs target not compatible with :whitespace optimizations"))) + (defn foreign-source? [js] (and (satisfies? deps/IJavaScript js) (deps/-foreign? js))) @@ -1040,6 +1044,7 @@ should contain the source for the given namespace name." (check-source-map opts) (check-source-map-path opts) (check-output-wrapper opts) + (check-node-target opts) (swap! compiler-env #(-> % (assoc-in [:opts :emit-constants] emit-constants) From 3bbd9756d7984ecb7d530d31f3b867c0a58ae35a Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 4 Feb 2015 19:38:07 -0500 Subject: [PATCH 0568/4033] CLJS-1003: Cannot pass custom env to run-tests --- src/clj/cljs/test.clj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 264f193f8..e0c9b75b5 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -239,11 +239,11 @@ (fn [[quote ns]] (and (= quote 'quote) (symbol? ns))) namespaces) "All arguments to run-tests must be quoted symbols") - (let [is-ns (ns? env-or-ns)] - `(do - ~(if is-ns - `(cljs.test/set-env! (cljs.test/empty-env)) - `(cljs.test/set-env! ~env-or-ns)) + (let [is-ns (ns? env-or-ns) + env (gensym "env")] + `(let [~env ~(if is-ns + `(cljs.test/empty-env) + env-or-ns)] ;; TODO: support async - David (let [summary# (assoc (reduce @@ -254,7 +254,7 @@ {:test 0 :pass 0 :fail 0 :error 0} [~@(map (fn [ns] - `(cljs.test/test-ns ~ns)) + `(cljs.test/test-ns ~env ~ns)) (if is-ns (concat [env-or-ns] namespaces) namespaces))]) From 647f775f01fad563a9c7b481a7e15273a8cff884 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Feb 2015 07:39:30 -0500 Subject: [PATCH 0569/4033] CLJS-1003: fix cljs.test run-tests do-report :summary issues --- src/clj/cljs/test.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index e0c9b75b5..946cb49a9 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -259,7 +259,9 @@ (concat [env-or-ns] namespaces) namespaces))]) :type :summary)] + (cljs.test/set-env! ~env) (do-report summary#) + (cljs.test/clear-env!) summary#))))) (defmacro run-all-tests From dc47f1fa1483871641c34f765034efedb7238f28 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Feb 2015 08:06:14 -0500 Subject: [PATCH 0570/4033] CLJS-1004: warning about single segment namespaces --- src/clj/cljs/analyzer.clj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 169d799ac..e9c286c56 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -63,7 +63,8 @@ :invalid-arithmetic true :protocol-invalid-method true :protocol-duped-method true - :protocol-multiple-impls true}) + :protocol-multiple-impls true + :single-segment-namespace true}) (declare message namespaces) @@ -173,6 +174,10 @@ [warning-type info] (str "Cannot invoke type constructor " (-> info :fexpr :info :name) " as function ")) +(defmethod error-message :single-segment-namespace + [warning-type info] + (str (:name info) " is a single segment namespace")) + (defn ^:private default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] @@ -1279,6 +1284,8 @@ [_ env [_ name & args :as form] _ opts] (when-not (symbol? name) (throw (error env "Namespaces must be named by a symbol."))) + (when (= 1 (count (string/split (clojure.core/name name) #"\."))) + (warning :single-segment-namespace env {:name name})) (let [docstring (if (string? (first args)) (first args)) args (if docstring (next args) args) metadata (if (map? (first args)) (first args)) From 4bd3e6563d36871b1c5e603c35fc7bb4a9b68eb2 Mon Sep 17 00:00:00 2001 From: Murphy McMahon Date: Thu, 5 Feb 2015 10:57:39 -0200 Subject: [PATCH 0571/4033] CLJS-1005: Browser REPL creates 'out' directory no matter what --- src/clj/cljs/repl/browser.clj | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index a5906024d..283e21534 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -240,12 +240,16 @@ (defn- always-preload "Return a list of all namespaces which are always loaded into the browser - when using a browser-connected REPL." - [] - (let [cljs (provides-and-requires - (cljsc/cljs-dependencies {} ["clojure.browser.repl"])) + when using a browser-connected REPL. + + Uses the working-dir (see repl-env) to output intermediate compilation." + [& [{:keys [working-dir]}]] + (let [opts (if working-dir {:output-dir working-dir} + {}) + cljs (provides-and-requires + (cljsc/cljs-dependencies opts ["clojure.browser.repl"])) goog (provides-and-requires - (cljsc/js-dependencies {} cljs))] + (cljsc/js-dependencies opts cljs))] (disj (set (concat cljs goog)) nil))) (defn repl-env @@ -290,7 +294,7 @@ (cljs.env/with-compiler-env compiler-env (reset! preloaded-libs (set (concat - (always-preload) + (always-preload opts) (map str (:preloaded-libs opts))))) (reset! loaded-libs @preloaded-libs) (println "Compiling client js ...") From 576fd1c70933fc91180c769cb639c262b7c79071 Mon Sep 17 00:00:00 2001 From: Murphy McMahon Date: Thu, 5 Feb 2015 11:08:02 -0200 Subject: [PATCH 0572/4033] CLJS-1006: Implicit dependency of clojure.browser.repl on cljs.repl --- src/cljs/clojure/browser/repl.cljs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 57f3aa714..bdaddbfd6 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -16,7 +16,12 @@ :author "Bobby Calderwood and Alex Redington"} clojure.browser.repl (:require [clojure.browser.net :as net] - [clojure.browser.event :as event])) + [clojure.browser.event :as event] + ;; repl-connection callback will receive goog.require('cljs.repl') + ;; and monkey-patched require expects to be able to derive it + ;; via goog.basePath, so this namespace should be compiled together + ;; with clojure.browser.repl: + [cljs.repl])) (def xpc-connection (atom nil)) From 8eff19c87c58cf39cf1841cb53d172b8f0a0b1c8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Feb 2015 09:01:21 -0500 Subject: [PATCH 0573/4033] `cljs.source-map/decode` -> `cljs.source-map/decode-reverse` Rename to match client runtime source mapping helpers. The new name is more accurate since source maps normally map from generated to original and this fn returns mapping from original to generated. --- src/clj/cljs/closure.clj | 2 +- src/clj/cljs/source_map.clj | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 892b928e9..fa837612f 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -627,7 +627,7 @@ should contain the source for the given namespace name." (.appendTo (.getSourceMap closure-compiler) out name)) (let [sm-json (-> (io/file name) slurp (json/read-str :key-fn keyword)) - closure-source-map (sm/decode sm-json)] + closure-source-map (sm/decode-reverse sm-json)] (loop [sources (seq sources) relpaths {} merged (sorted-map-by diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index 8f9b33a9d..38a331559 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -77,7 +77,7 @@ (with-meta nseg {:name (+ name rname)}) nseg))) -(defn update-result +(defn update-reverse-result "Helper for decode. Take an internal source map representation organized as nested sorted maps mapping file, line, and column and update it based on a segment map and generated line number." @@ -96,11 +96,12 @@ (sorted-map)))) (sorted-map))))) -(defn decode +(defn decode-reverse "Convert a v3 source map JSON object into a nested sorted map - organized as file, line, and column." + organized as file, line, and column. Not this source map + maps from *original* source location to generated source location." ([source-map] - (decode (:mappings source-map) source-map)) + (decode-reverse (:mappings source-map) source-map)) ([mappings source-map] (let [{:keys [sources]} source-map relseg-init [0 0 0 0 0] @@ -120,7 +121,7 @@ (let [seg (first segs) nrelseg (seg-combine (base64-vlq/decode seg) relseg)] (recur (next segs) nrelseg - (update-result result (seg->map nrelseg source-map) gline))) + (update-reverse-result result (seg->map nrelseg source-map) gline))) [result relseg]))))] (recur (inc gline) (next lines) (assoc relseg 0 0) result)) result))))) @@ -274,15 +275,19 @@ ;; build with (require '[cljs.closure :as cljsc]) - (cljsc/build "src" {:optimizations :simple :output-to "hello.js" :source-map "hello.js.map"}) + (cljsc/build "src" + {:optimizations :simple + :output-to "hello.js" + :source-map "hello.js.map" + :output-dir "out"}) ;; load source map (def raw-source-map (json/read-str (slurp (io/file "hello.js.map")) :key-fn keyword)) ;; test it out - (first (decode raw-source-map)) + (first (decode-reverse raw-source-map)) ;; decoded source map preserves file order - (= (keys (decode raw-source-map)) (:sources raw-source-map)) + (= (keys (decode-reverse raw-source-map)) (:sources raw-source-map)) ) From 73b5adf0bc52d30bc65dcf32b24da447fcf5847c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Feb 2015 09:10:34 -0500 Subject: [PATCH 0574/4033] add source map helpers for mapping generated source to original source --- src/clj/cljs/source_map.clj | 49 ++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index 38a331559..cdaddead1 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -78,9 +78,8 @@ nseg))) (defn update-reverse-result - "Helper for decode. Take an internal source map representation - organized as nested sorted maps mapping file, line, and column - and update it based on a segment map and generated line number." + "Helper for decode-reverse. Take a source map and update it + based on a segment map." [result segmap gline] (let [{:keys [gcol source line col name]} segmap d {:gline gline @@ -98,7 +97,7 @@ (defn decode-reverse "Convert a v3 source map JSON object into a nested sorted map - organized as file, line, and column. Not this source map + organized as file, line, and column. Note this source map maps from *original* source location to generated source location." ([source-map] (decode-reverse (:mappings source-map) source-map)) @@ -126,6 +125,48 @@ (recur (inc gline) (next lines) (assoc relseg 0 0) result)) result))))) +(defn update-result + "Helper for decode. Take a source map and update it based on a + segment map." + [result segmap gline] + (let [{:keys [gcol source line col name]} segmap + d {:line line + :col col + :source source} + d (if name (assoc d :name name) d)] + (update-in result [gline] + (fnil (fn [m] + (update-in m [gcol] + (fnil #(conj % d) []))) + (sorted-map))))) + +(defn decode + "Convert a v3 source map JSON object into a nested sorted map + organized as file, line, and column. Note this source map + maps from *generated* source location to original source + location." + ([source-map] + (decode (:mappings source-map) source-map)) + ([mappings source-map] + (let [relseg-init [0 0 0 0 0] + lines (seq (string/split mappings #";"))] + (loop [gline 0 lines lines relseg relseg-init result {}] + (if lines + (let [line (first lines) + [result relseg] + (if (string/blank? line) + [result relseg] + (let [segs (seq (string/split line #","))] + (loop [segs segs relseg relseg result result] + (if segs + (let [seg (first segs) + nrelseg (seg-combine (base64-vlq/decode seg) relseg)] + (recur (next segs) nrelseg + (update-result result (seg->map nrelseg source-map) gline))) + [result relseg]))))] + (recur (inc gline) (next lines) (assoc relseg 0 0) result)) + result))))) + ;; ----------------------------------------------------------------------------- ;; Encoding From dc3d79b96a3a366fb209d4ae3c4fcda1c94a5582 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Feb 2015 09:17:15 -0500 Subject: [PATCH 0575/4033] source map merging support on client and server --- src/clj/cljs/source_map.clj | 7 ++++--- src/cljs/cljs/source_map.cljs | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index cdaddead1..a6678b05f 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -287,9 +287,10 @@ (defn merge-source-maps "Merge an internal source map representation of a single - ClojureScript file with an internal source map representation of - the generated JavaScript file that underwent Google Closure - Compiler optimization." + ClojureScript file mapping original to generated with a + second source map mapping original JS to genereated JS. + The is to support source maps that work through multiple + compilation steps like Google Closure optimization passes." [cljs-map closure-map] (loop [line-map-seq (seq cljs-map) new-lines (sorted-map)] (if line-map-seq diff --git a/src/cljs/cljs/source_map.cljs b/src/cljs/cljs/source_map.cljs index 952e4858a..e11adde88 100644 --- a/src/cljs/cljs/source_map.cljs +++ b/src/cljs/cljs/source_map.cljs @@ -166,3 +166,30 @@ [result relseg]))))] (recur (inc gline) (next lines) (assoc relseg 0 0) result)) result))))) + +;; ----------------------------------------------------------------------------- +;; Merging + +(defn merge-source-maps + "Merge an internal source map representation of a single + ClojureScript file mapping original to generated with a + second source map mapping original JS to genereated JS. + The is to support source maps that work through multiple + compilation steps like Google Closure optimization passes." + [cljs-map closure-map] + (loop [line-map-seq (seq cljs-map) new-lines (sorted-map)] + (if line-map-seq + (let [[line col-map] (first line-map-seq) + new-cols + (loop [col-map-seq (seq col-map) new-cols (sorted-map)] + (if col-map-seq + (let [[col infos] (first col-map-seq)] + (recur (next col-map-seq) + (assoc new-cols col + (reduce (fn [v {:keys [gline gcol]}] + (into v (get-in closure-map [gline gcol]))) + [] infos)))) + new-cols))] + (recur (next line-map-seq) + (assoc new-lines line new-cols))) + new-lines))) From da84184575cd11d33f3382354db5d382e96ab20f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Feb 2015 09:22:56 -0500 Subject: [PATCH 0576/4033] typos --- src/clj/cljs/source_map.clj | 2 +- src/cljs/cljs/source_map.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index a6678b05f..0c2712115 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -288,7 +288,7 @@ (defn merge-source-maps "Merge an internal source map representation of a single ClojureScript file mapping original to generated with a - second source map mapping original JS to genereated JS. + second source map mapping original JS to generated JS. The is to support source maps that work through multiple compilation steps like Google Closure optimization passes." [cljs-map closure-map] diff --git a/src/cljs/cljs/source_map.cljs b/src/cljs/cljs/source_map.cljs index e11adde88..c44aa05f4 100644 --- a/src/cljs/cljs/source_map.cljs +++ b/src/cljs/cljs/source_map.cljs @@ -173,7 +173,7 @@ (defn merge-source-maps "Merge an internal source map representation of a single ClojureScript file mapping original to generated with a - second source map mapping original JS to genereated JS. + second source map mapping original JS to generated JS. The is to support source maps that work through multiple compilation steps like Google Closure optimization passes." [cljs-map closure-map] From 751a3eb3d5c8fcf90fe97ae6395105640cb585a3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Feb 2015 09:54:47 -0500 Subject: [PATCH 0577/4033] `closure-map` -> `js-map` in client/server `cljs.source-map/merge-source-maps` --- src/clj/cljs/source_map.clj | 4 ++-- src/cljs/cljs/source_map.cljs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj index 0c2712115..a78a1c619 100644 --- a/src/clj/cljs/source_map.clj +++ b/src/clj/cljs/source_map.clj @@ -291,7 +291,7 @@ second source map mapping original JS to generated JS. The is to support source maps that work through multiple compilation steps like Google Closure optimization passes." - [cljs-map closure-map] + [cljs-map js-map] (loop [line-map-seq (seq cljs-map) new-lines (sorted-map)] (if line-map-seq (let [[line col-map] (first line-map-seq) @@ -302,7 +302,7 @@ (recur (next col-map-seq) (assoc new-cols col (reduce (fn [v {:keys [gline gcol]}] - (into v (get-in closure-map [gline gcol]))) + (into v (get-in js-map [gline gcol]))) [] infos)))) new-cols))] (recur (next line-map-seq) diff --git a/src/cljs/cljs/source_map.cljs b/src/cljs/cljs/source_map.cljs index c44aa05f4..9adb0dadd 100644 --- a/src/cljs/cljs/source_map.cljs +++ b/src/cljs/cljs/source_map.cljs @@ -176,7 +176,7 @@ second source map mapping original JS to generated JS. The is to support source maps that work through multiple compilation steps like Google Closure optimization passes." - [cljs-map closure-map] + [cljs-map js-map] (loop [line-map-seq (seq cljs-map) new-lines (sorted-map)] (if line-map-seq (let [[line col-map] (first line-map-seq) @@ -187,7 +187,7 @@ (recur (next col-map-seq) (assoc new-cols col (reduce (fn [v {:keys [gline gcol]}] - (into v (get-in closure-map [gline gcol]))) + (into v (get-in js-map [gline gcol]))) [] infos)))) new-cols))] (recur (next line-map-seq) From 65a8973e2db8a9bd48aac55d310e536ebc0dcd75 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Feb 2015 10:08:28 -0500 Subject: [PATCH 0578/4033] add `:recompile-dependents` knob to `cljs.closure/build` --- src/clj/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index fa837612f..ce5ff7b5c 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1050,7 +1050,8 @@ should contain the source for the given namespace name." (assoc-in [:opts :emit-constants] emit-constants) (assoc :target (:target opts)) (assoc :js-dependency-index (deps/js-dependency-index all-opts)))) - (binding [comp/*dependents* (atom {:recompile #{} :visited #{}}) + (binding [comp/*dependents* (when-not (false? (:recompile-dependents opts)) + (atom {:recompile #{} :visited #{}})) ana/*cljs-static-fns* (or (and (= (:optimizations opts) :advanced) (not (false? (:static-fns opts)))) From d0a04375365527b04a66d11f9e83314b6759df9b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Feb 2015 14:21:36 -0500 Subject: [PATCH 0579/4033] browser REPL needs to respect :output-dir --- src/clj/cljs/repl/browser.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 283e21534..29402b5c9 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -282,10 +282,12 @@ opts (merge (BrowserEnv.) {:port 9000 :optimizations :simple - :working-dir (->> [".repl" (util/clojurescript-version)] - (remove empty?) (string/join "-")) + :working-dir (or (:output-dir opts) + (->> [".repl" (util/clojurescript-version)] + (remove empty?) (string/join "-"))) :serve-static true - :static-dir ["." "out/"] + :static-dir (cond-> ["." "out/"] + (:output-dir opts) (conj (:output-dir opts))) :preloaded-libs [] :src "src/" ::env/compiler compiler-env From 9df1e0375486a9f681b28d56ec9493ee20ba395e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Feb 2015 18:04:14 -0500 Subject: [PATCH 0580/4033] ignores --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index dc8165216..e249be357 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ closure /coreadvanced.js /coresimple.js /out +*out +.lein* /pom.xml .repl* *.swp From fd0a52c110e0f909fcd890a55887e7507252cbfe Mon Sep 17 00:00:00 2001 From: Julien Eluard Date: Fri, 6 Feb 2015 19:52:36 -0300 Subject: [PATCH 0581/4033] Added new :closure-extra-annotations compiler option allowing to define extra JSDoc annotation used by closure libraries. --- src/clj/cljs/closure.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index ce5ff7b5c..c9eac846f 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -173,6 +173,8 @@ (doseq [[type level] (:closure-warnings opts)] (. compiler-options (setWarningLevel (type warning-types) (level check-level)))) + (if-let [extra-annotations (:closure-extra-annotations opts)] + (. compiler-options (setExtraAnnotationNames (map name extra-annotations)))) (when (contains? opts :source-map) (set! (.sourceMapOutputPath compiler-options) (:source-map opts)) From 20778cd528167910031bffcc77624628c02c9e8f Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Fri, 6 Feb 2015 13:32:52 +0100 Subject: [PATCH 0582/4033] CLJS-1012: Correct behavior when *print-length* is set to 0 Consistent with Clojure 1.6.0 --- src/cljs/cljs/core.cljs | 26 +++++++++++++++----------- test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 3c2b389a0..0e4787be8 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8109,17 +8109,21 @@ reduces them without incurring seq initialization" (-write writer "#") (do (-write writer begin) - (when (seq coll) - (print-one (first coll) writer opts)) - (loop [coll (next coll) n (dec (:print-length opts))] - (if (and coll (or (nil? n) (not (zero? n)))) - (do - (-write writer sep) - (print-one (first coll) writer opts) - (recur (next coll) (dec n))) - (when (and (seq coll) (zero? n)) - (-write writer sep) - (-write writer "...")))) + (if (zero? (:print-length opts)) + (when (seq coll) + (-write writer "...")) + (do + (when (seq coll) + (print-one (first coll) writer opts)) + (loop [coll (next coll) n (dec (:print-length opts))] + (if (and coll (or (nil? n) (not (zero? n)))) + (do + (-write writer sep) + (print-one (first coll) writer opts) + (recur (next coll) (dec n))) + (when (and (seq coll) (zero? n)) + (-write writer sep) + (-write writer "...")))))) (-write writer end))))) (defn write-all [writer & ss] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 69a506ec2..ac9fa62f9 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1472,6 +1472,8 @@ (deftest test-print-knobs (testing "Testing printing knobs" + (is (= (binding [*print-length* 0] (str [1 2 3 4 5 6 7 8 9 0])) + "[...]")) (is (= (binding [*print-length* 1] (str [1 2 3 4 5 6 7 8 9 0])) "[1 ...]")) (is (= (binding [*print-length* 2] (str [1 2 3 4 5 6 7 8 9 0])) @@ -1481,6 +1483,8 @@ ;; CLJS-804 (is (= (binding [*print-length* 10] (str {:foo "bar"})) "{:foo \"bar\"}")) + (is (= (binding [*print-length* 0] (str {:foo "bar" :baz "woz"})) + "{...}")) (is (= (binding [*print-length* 1] (str {:foo "bar" :baz "woz"})) "{:foo \"bar\", ...}")) (is (= (binding [*print-length* 10] (str {:foo "bar" :baz "woz"})) From 26a9752f5fbbc489634969fa02595f2bc5d97c65 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Feb 2015 19:42:27 -0500 Subject: [PATCH 0583/4033] fix Node.js REPL support for 0.12 --- Clojurescript.iml | 1 + src/clj/cljs/repl/node_repl.js | 2 +- src/cljs/cljs/bootstrap_node.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index 2acdc289a..b708b5900 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -14,6 +14,7 @@ + diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index 2c306924f..c49860bc9 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -35,7 +35,7 @@ net.createServer(function (socket) { data = data.replace(/\0/g, ""); try { dom.run(function() { - ret = vm.Script.runInThisContext.call(global, data, "repl"); + ret = vm.runInThisContext(data, "repl"); }); } catch (e) { err = e; diff --git a/src/cljs/cljs/bootstrap_node.js b/src/cljs/cljs/bootstrap_node.js index cb92671f3..e0fb15403 100644 --- a/src/cljs/cljs/bootstrap_node.js +++ b/src/cljs/cljs/bootstrap_node.js @@ -82,7 +82,7 @@ function nodeGlobalRequire(file) { var _module = global.module, _exports = global.exports; global.module = undefined; global.exports = undefined; - vm.Script.runInThisContext.call(global, fs.readFileSync(file), file); + vm.runInThisContext(fs.readFileSync(file), file); global.exports = _exports; global.module = _module; } From 8b4ac0f9a0476b4ad81817b117605ebcd6ba486a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 13:24:35 -0500 Subject: [PATCH 0584/4033] add source map branch to `cljs.repl/display-error` --- src/clj/cljs/repl.clj | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 23ed0c227..ee7102f12 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -148,13 +148,16 @@ (load-namespace repl-env ns opts)))) (defn- display-error - ([ret form] - (display-error ret form (constantly nil))) - ([ret form f] + ([ret form opts] + (display-error ret form (constantly nil) opts)) + ([ret form f opts] (f) (println (:value ret)) (when-let [st (:stacktrace ret)] - (println st) + (if (and (true? (:source-map opts)) + (vector? st)) + (println st) + (println st)) (flush)))) (defn evaluate-form @@ -204,11 +207,12 @@ (print js)) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] (case (:status ret) - :error (display-error ret form) + :error (display-error ret form opts) :exception (display-error ret form (if (:repl-verbose opts) #(prn "Error evaluating:" form :as js) - (constantly nil))) + (constantly nil)) + opts) :success (:value ret))))) (catch Throwable ex (.printStackTrace ex) From 326d4e76c49ffb241078ba84908eca4916b25f86 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 14:21:51 -0500 Subject: [PATCH 0585/4033] first cut of source mapping support for all REPLs --- src/clj/cljs/repl.clj | 75 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ee7102f12..c74ccfc82 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -9,16 +9,19 @@ (ns cljs.repl (:refer-clojure :exclude [load-file]) (:require [clojure.java.io :as io] + [clojure.string :as string] + [clojure.data.json :as json] + [clojure.tools.reader :as reader] + [clojure.tools.reader.reader-types :as readers] + [clojure.stacktrace :as trace] + [clojure.repl :as cljrepl] + [cljs.util :as util] [cljs.compiler :as comp] [cljs.analyzer :as ana] [cljs.env :as env] [cljs.tagged-literals :as tags] [cljs.closure :as cljsc] - [cljs.source-map :as sm] - [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers] - [cljs.util :as util] - [clojure.stacktrace :as trace]) + [cljs.source-map :as sm]) (:import [java.io File PushbackReader] [javax.xml.bind DatatypeConverter])) @@ -147,6 +150,66 @@ (doseq [ns (distinct requires)] (load-namespace repl-env ns opts)))) +(defn read-source-map [f] + (sm/decode (json/read-str (slurp (io/file (str f ".map"))) :key-fn keyword))) + +(defn js-src->cljs-src [f] + (let [f (io/file f) + dir (.getParentFile f) + name (.getName f)] + (io/file dir (string/replace name ".js" ".cljs")))) + +(defn ns-info [f] + (ana/parse-ns (js-src->cljs-src f))) + +(defn mapped-line-and-column [source-map line column] + (let [default [line column]] + ;; source maps are 0 indexed for lines + (if-let [columns (get source-map (dec line))] + (vec + (map + ;; source maps are 0 indexed for columns + ;; multiple segments may exist at column + ;; just take first + (first + (if-let [mapping (get columns (dec column))] + mapping + (second (first columns)))) + [:line :col])) + default))) + +(defn print-mapped-stacktrace [stacktrace] + (let [read-source-map' (memoize read-source-map) + ns-info' (memoize ns-info)] + (doseq [{:keys [function file line column] :as frame} stacktrace] + (let [[sm {:keys [ns source-file]}] ((juxt read-source-map' ns-info') file) + [line' column'] (mapped-line-and-column sm line column) + name' (if function + (symbol (name ns) (cljrepl/demunge function)) + ns)] + (println (str name' " (" source-file ":" line' ":" column' ")")))))) + +(comment + (cljsc/build "samples/hello/src" + {:optimizations :none + :output-dir "samples/hello/out" + :output-to "samples/hello/out/hello.js" + :source-map true}) + + ;; line 2 column 1 + (mapped-line-and-column + (read-source-map "samples/hello/out/hello/core.js") 2 1) + + ;; hello.core + (:ns (ns-info "samples/hello/out/hello/core.js")) + + (print-mapped-stacktrace + [{:file "samples/hello/out/hello/core.js" + :function "first" + :line 2 + :column 1}]) + ) + (defn- display-error ([ret form opts] (display-error ret form (constantly nil) opts)) @@ -156,7 +219,7 @@ (when-let [st (:stacktrace ret)] (if (and (true? (:source-map opts)) (vector? st)) - (println st) + (print-mapped-stacktrace st) (println st)) (flush)))) From 463ee711cf318104b21258d62aa5a60d5436019a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 14:41:09 -0500 Subject: [PATCH 0586/4033] need to pass along opts --- src/clj/cljs/repl.clj | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c74ccfc82..006825c1d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -316,13 +316,17 @@ (set! *1 ret#) ret#)))))) -(defn- eval-and-print [repl-env env form] - (println - (evaluate-form repl-env - (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) - "" - form - (wrap-fn form)))) +(defn- eval-and-print + ([repl-env env form] + (eval-and-print repl-env env form nil)) + ([repl-env env form opts] + (println + (evaluate-form repl-env + (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) + "" + form + (wrap-fn form) + opts)))) ;; Special REPL fns, these provide compatiblity with Clojure functions ;; that are not possible to reproduce given ClojureScript's compilation model @@ -486,7 +490,7 @@ (recur)) :else - (do (eval-and-print repl-env env form) + (do (eval-and-print repl-env env form opts) (recur))))) (-tear-down repl-env)))))) From 404451665f6f1010e9626244d4bbe3d0c42e7f4d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 14:50:23 -0500 Subject: [PATCH 0587/4033] canonical file paths and remove JVM working directory from path --- src/clj/cljs/repl.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 006825c1d..de859bd8a 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -186,7 +186,9 @@ [line' column'] (mapped-line-and-column sm line column) name' (if function (symbol (name ns) (cljrepl/demunge function)) - ns)] + ns) + source-file (string/replace (.getCanonicalFile ^File source-file) + (str (System/getProperty "user.dir") File/separator) "")] (println (str name' " (" source-file ":" line' ":" column' ")")))))) (comment From 57277cff5ccde7cac6fe3d2784db2275ab409542 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 21:17:25 -0500 Subject: [PATCH 0588/4033] CLJS-1020: off by one error in REPL source map support CLJS-1021: correct handling of plain JS files in REPL source map support --- src/clj/cljs/repl.clj | 49 ++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index de859bd8a..4ab5e093d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -151,45 +151,56 @@ (load-namespace repl-env ns opts)))) (defn read-source-map [f] - (sm/decode (json/read-str (slurp (io/file (str f ".map"))) :key-fn keyword))) + (let [smf (io/file (str f ".map"))] + (when (.exists smf) + (sm/decode (json/read-str (slurp smf) :key-fn keyword))))) -(defn js-src->cljs-src [f] +(defn ^File js-src->cljs-src [f] (let [f (io/file f) dir (.getParentFile f) name (.getName f)] (io/file dir (string/replace name ".js" ".cljs")))) (defn ns-info [f] - (ana/parse-ns (js-src->cljs-src f))) + (let [f' (js-src->cljs-src f)] + (when (.exists f') + (ana/parse-ns f')))) (defn mapped-line-and-column [source-map line column] (let [default [line column]] ;; source maps are 0 indexed for lines (if-let [columns (get source-map (dec line))] (vec - (map - ;; source maps are 0 indexed for columns - ;; multiple segments may exist at column - ;; just take first - (first - (if-let [mapping (get columns (dec column))] - mapping - (second (first columns)))) - [:line :col])) + (map inc + (map + ;; source maps are 0 indexed for columns + ;; multiple segments may exist at column + ;; just take first + (first + (if-let [mapping (get columns (dec column))] + mapping + (second (first columns)))) + [:line :col]))) default))) (defn print-mapped-stacktrace [stacktrace] (let [read-source-map' (memoize read-source-map) ns-info' (memoize ns-info)] (doseq [{:keys [function file line column] :as frame} stacktrace] - (let [[sm {:keys [ns source-file]}] ((juxt read-source-map' ns-info') file) - [line' column'] (mapped-line-and-column sm line column) - name' (if function + (let [[sm {:keys [ns source-file] :as ns-info}] ((juxt read-source-map' ns-info') file) + [line' column'] (if ns-info + (mapped-line-and-column sm line column) + [line column]) + name' (if (and ns-info function) (symbol (name ns) (cljrepl/demunge function)) - ns) - source-file (string/replace (.getCanonicalFile ^File source-file) - (str (System/getProperty "user.dir") File/separator) "")] - (println (str name' " (" source-file ":" line' ":" column' ")")))))) + function) + file' (string/replace + (.getCanonicalFile + (if ns-info + source-file + (io/file file))) + (str (System/getProperty "user.dir") File/separator) "")] + (println (str name' " (" file' ":" line' ":" column' ")")))))) (comment (cljsc/build "samples/hello/src" From f82bac7fcb834dd03dce47b19dafe0178bf44b9a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 7 Feb 2015 16:09:02 -0500 Subject: [PATCH 0589/4033] CLJS-1018: Add support for cljs.core/*e Modify the JavaScript that is sent for evaluation to wrap in a try and then catch any exception thrown, assign it to *e, and then rethrow. --- src/clj/cljs/repl.clj | 12 ++++++++---- src/cljs/cljs/core.cljs | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 4ab5e093d..09bbbd12f 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -321,13 +321,17 @@ (defn- wrap-fn [form] (cond (and (seq? form) (= 'ns (first form))) identity - ('#{*1 *2 *3} form) (fn [x] `(cljs.core.pr-str ~x)) + ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) :else (fn [x] `(cljs.core.pr-str - (let [ret# ~x] - (do (set! *3 *2) + (try + (let [ret# ~x] + (set! *3 *2) (set! *2 *1) (set! *1 ret#) - ret#)))))) + ret#) + (catch :default e# + (set! *e e#) + (throw e#))))))) (defn- eval-and-print ([repl-env env form] diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 0e4787be8..f8791f738 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -68,6 +68,10 @@ ^{:doc "bound in a repl thread to the third most recent value printed"} *3) +(def + ^{:doc "bound in a repl thread to the most recent exception caught by the repl"} + *e) + (defn truth_ "Internal - do not use!" [x] From 5fe59f514900ba48c25cf70b48292903370821dd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 21:51:56 -0500 Subject: [PATCH 0590/4033] indent source mapped stack trace --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 09bbbd12f..eaa6dc360 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -200,7 +200,7 @@ source-file (io/file file))) (str (System/getProperty "user.dir") File/separator) "")] - (println (str name' " (" file' ":" line' ":" column' ")")))))) + (println "\t" (str name' " (" file' ":" line' ":" column' ")")))))) (comment (cljsc/build "samples/hello/src" From ae62c6cfca71832e9ec829f073503ff892a6edd8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 21:59:47 -0500 Subject: [PATCH 0591/4033] use the last column segment, appears to be more accurate --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index eaa6dc360..00e1c60ec 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -176,7 +176,7 @@ ;; source maps are 0 indexed for columns ;; multiple segments may exist at column ;; just take first - (first + (last (if-let [mapping (get columns (dec column))] mapping (second (first columns)))) From f7983a5265035fff9526e5f6cce94eee5bdd900b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 22:07:17 -0500 Subject: [PATCH 0592/4033] docstrings and formatting --- src/clj/cljs/repl.clj | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 00e1c60ec..a9d01d9d0 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -150,40 +150,56 @@ (doseq [ns (distinct requires)] (load-namespace repl-env ns opts)))) -(defn read-source-map [f] +(defn read-source-map + "Return the source map for the JavaScript source file." + [f] (let [smf (io/file (str f ".map"))] (when (.exists smf) (sm/decode (json/read-str (slurp smf) :key-fn keyword))))) -(defn ^File js-src->cljs-src [f] +(defn ^File js-src->cljs-src + "Map a JavaScript output file back to the original ClojureScript source + file." + [f] (let [f (io/file f) dir (.getParentFile f) name (.getName f)] (io/file dir (string/replace name ".js" ".cljs")))) -(defn ns-info [f] +(defn ns-info + "Given a path to a js source file return the ns info for the corresponding + ClojureScript file if it exists." + [f] (let [f' (js-src->cljs-src f)] (when (.exists f') (ana/parse-ns f')))) -(defn mapped-line-and-column [source-map line column] +(defn mapped-line-and-column + "Given a cljs.source-map source map data structure map a generated line + and column back to the original line and column." + [source-map line column] (let [default [line column]] ;; source maps are 0 indexed for lines (if-let [columns (get source-map (dec line))] (vec (map inc (map - ;; source maps are 0 indexed for columns - ;; multiple segments may exist at column - ;; just take first - (last - (if-let [mapping (get columns (dec column))] - mapping - (second (first columns)))) - [:line :col]))) + ;; source maps are 0 indexed for columns + ;; multiple segments may exist at column + ;; just take first + (last + (if-let [mapping (get columns (dec column))] + mapping + (second (first columns)))) + [:line :col]))) default))) -(defn print-mapped-stacktrace [stacktrace] +(defn print-mapped-stacktrace + "Given a vector representing the canonicalized JavaScript stacktrace + print the ClojureScript stacktrace. The canonical stacktrace must be + a vector of {:file :function :line :column } + maps." + [stacktrace] (let [read-source-map' (memoize read-source-map) ns-info' (memoize ns-info)] (doseq [{:keys [function file line column] :as frame} stacktrace] From cd4cd38037af888d803bca652ea5bdbfab8038a8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 22:18:47 -0500 Subject: [PATCH 0593/4033] `cljs.repl/print-mapped-stacktrace` should take opts, prep for better source map caching --- src/clj/cljs/repl.clj | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index a9d01d9d0..b6ac2d4f8 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -199,24 +199,25 @@ print the ClojureScript stacktrace. The canonical stacktrace must be a vector of {:file :function :line :column } maps." - [stacktrace] - (let [read-source-map' (memoize read-source-map) - ns-info' (memoize ns-info)] - (doseq [{:keys [function file line column] :as frame} stacktrace] - (let [[sm {:keys [ns source-file] :as ns-info}] ((juxt read-source-map' ns-info') file) - [line' column'] (if ns-info - (mapped-line-and-column sm line column) - [line column]) - name' (if (and ns-info function) - (symbol (name ns) (cljrepl/demunge function)) - function) - file' (string/replace - (.getCanonicalFile - (if ns-info - source-file - (io/file file))) - (str (System/getProperty "user.dir") File/separator) "")] - (println "\t" (str name' " (" file' ":" line' ":" column' ")")))))) + ([stacktrace] (print-mapped-stacktrace stacktrace nil)) + ([stacktrace opts] + (let [read-source-map' (memoize read-source-map) + ns-info' (memoize ns-info)] + (doseq [{:keys [function file line column] :as frame} stacktrace] + (let [[sm {:keys [ns source-file] :as ns-info}] ((juxt read-source-map' ns-info') file) + [line' column'] (if ns-info + (mapped-line-and-column sm line column) + [line column]) + name' (if (and ns-info function) + (symbol (name ns) (cljrepl/demunge function)) + function) + file' (string/replace + (.getCanonicalFile + (if ns-info + source-file + (io/file file))) + (str (System/getProperty "user.dir") File/separator) "")] + (println "\t" (str name' " (" file' ":" line' ":" column' ")"))))))) (comment (cljsc/build "samples/hello/src" @@ -248,7 +249,7 @@ (when-let [st (:stacktrace ret)] (if (and (true? (:source-map opts)) (vector? st)) - (print-mapped-stacktrace st) + (print-mapped-stacktrace st opts) (println st)) (flush)))) From c31673545d49f1870d5d1fe1031749d57e4aa99f Mon Sep 17 00:00:00 2001 From: lgrapenthin Date: Sun, 8 Feb 2015 04:31:42 +0100 Subject: [PATCH 0594/4033] CLJS-988: Support async testing in cljs.test --- src/clj/cljs/test.clj | 168 +++++++++++++++++++------------ src/cljs/cljs/test.cljs | 218 ++++++++++++++++++++++++++++++++-------- 2 files changed, 281 insertions(+), 105 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 946cb49a9..462191b42 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -219,50 +219,75 @@ (fn [] (cljs.test/test-var (.-cljs$lang$var ~name)))) (set! (.-cljs$lang$var ~name) (var ~name))))) +(defmacro async + "Wraps body as a CPS function that can be returned from a test to + continue asynchronously. Binds done to a function that must be + invoked once and from an async context after any assertions. + + (deftest example-with-timeout + (async done + (js/setTimeout (fn [] + ;; make assertions in async context... + (done) ;; ...then call done + ) + 0)))" + [done & body] + `(reify + cljs.test/IAsyncTest + cljs.core/IFn + (~'-invoke [_# ~done] + ~@body))) + ;; ============================================================================= ;; Running Tests (defn ns? [x] (and (seq? x) (= (first x) 'quote))) +(defmacro run-tests-block + "Like test-vars, but returns a block for further composition and + later execution." + [env-or-ns & namespaces] + (assert (every? + (fn [[quote ns]] (and (= quote 'quote) (symbol? ns))) + namespaces) + "All arguments to run-tests must be quoted symbols") + (let [is-ns (ns? env-or-ns) + env (gensym "env") + summary (gensym "summary")] + `(let [~env ~(if is-ns + `(cljs.test/empty-env) + env-or-ns) + ~summary (cljs.core/volatile! + {:test 0 :pass 0 :fail 0 :error 0 + :type :summary})] + (concat ~@(map + (fn [ns] + `(concat (cljs.test/test-ns-block ~env ~ns) + [(fn [] + (cljs.core/vswap! + ~summary + (partial merge-with +) + (:report-counters + (cljs.test/get-and-clear-env!))))])) + (if is-ns + (concat [env-or-ns] namespaces) + namespaces)) + [(fn [] + (cljs.test/set-env! ~env) + (do-report (deref ~summary)) + (cljs.test/clear-env!))])))) + (defmacro run-tests "Runs all tests in the given namespaces; prints results. - Defaults to current namespace if none given. Returns a map - summarizing test results." + Defaults to current namespace if none given." ([] `(run-tests (cljs.test/empty-env) '~ana/*cljs-ns*)) ([env-or-ns] (if (ns? env-or-ns) `(run-tests (cljs.test/empty-env) ~env-or-ns) `(run-tests ~env-or-ns '~ana/*cljs-ns*))) ([env-or-ns & namespaces] - (assert (every? - (fn [[quote ns]] (and (= quote 'quote) (symbol? ns))) - namespaces) - "All arguments to run-tests must be quoted symbols") - (let [is-ns (ns? env-or-ns) - env (gensym "env")] - `(let [~env ~(if is-ns - `(cljs.test/empty-env) - env-or-ns)] - ;; TODO: support async - David - (let [summary# (assoc - (reduce - (fn [acc# res#] - (merge-with + - acc# - (:report-counters res#))) - {:test 0 :pass 0 :fail 0 :error 0} - [~@(map - (fn [ns] - `(cljs.test/test-ns ~env ~ns)) - (if is-ns - (concat [env-or-ns] namespaces) - namespaces))]) - :type :summary)] - (cljs.test/set-env! ~env) - (do-report summary#) - (cljs.test/clear-env!) - summary#))))) + `(cljs.test/run-block (run-tests-block ~env-or-ns ~@namespaces)))) (defmacro run-all-tests "Runs all tests in all namespaces; prints results. @@ -277,28 +302,52 @@ (cond->> (ana-api/all-ns) re (filter #(re-matches re (name %)))))))) -(defmacro test-all-vars - "Calls test-vars on every var with :test metadata interned in the - namespace, with fixtures." +(defmacro test-all-vars-block ([[quote ns]] `(let [env# (cljs.test/get-current-env)] - (when (nil? env#) - (cljs.test/set-env! (cljs.test/empty-env))) - ~(when (ana-api/ns-resolve ns 'cljs-test-once-fixtures) - `(cljs.test/update-current-env! [:once-fixtures] assoc '~ns - ~(symbol (name ns) "cljs-test-once-fixtures"))) - ~(when (ana-api/ns-resolve ns 'cljs-test-each-fixtures) - `(cljs.test/update-current-env! [:each-fixtures] assoc '~ns - ~(symbol (name ns) "cljs-test-each-fixtures"))) - (cljs.test/test-vars + (concat + [(fn [] + (when (nil? env#) + (cljs.test/set-env! (cljs.test/empty-env))) + ~(when (ana-api/ns-resolve ns 'cljs-test-once-fixtures) + `(cljs.test/update-current-env! [:once-fixtures] assoc '~ns + ~(symbol (name ns) "cljs-test-once-fixtures"))) + ~(when (ana-api/ns-resolve ns 'cljs-test-each-fixtures) + `(cljs.test/update-current-env! [:each-fixtures] assoc '~ns + ~(symbol (name ns) "cljs-test-each-fixtures"))))] + (cljs.test/test-vars-block [~@(map - (fn [[k _]] - `(var ~(symbol (name ns) (name k)))) - (filter - (fn [[_ v]] (:test v)) - (ana-api/ns-interns ns)))]) - (when (nil? env#) - (cljs.test/clear-env!))))) + (fn [[k _]] + `(var ~(symbol (name ns) (name k)))) + (filter + (fn [[_ v]] (:test v)) + (ana-api/ns-interns ns)))]) + [(fn [] + (when (nil? env#) + (cljs.test/clear-env!)))])))) + +(defmacro test-all-vars + "Calls test-vars on every var with :test metadata interned in the + namespace, with fixtures." + [[quote ns :as form]] + `(cljs.test/run-block (test-all-vars-block ~form))) + +(defmacro test-ns-block + "Like test-ns, but returns a block for further composition and + later execution. Does not clear the current env." + ([env [quote ns :as form]] + (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") + (assert (ana-api/find-ns ns) (str "Namespace " ns " does not exist")) + `[(fn [] + (cljs.test/set-env! ~env) + (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) + ;; If the namespace has a test-ns-hook function, call that: + ~(if-let [v (ana-api/ns-resolve ns 'test-ns-hook)] + `(~(symbol (name ns) "test-ns-hook")) + ;; Otherwise, just test every var in the namespace. + `(cljs.test/block (cljs.test/test-all-vars-block ~form)))) + (fn [] + (cljs.test/do-report {:type :end-test-ns, :ns ~form}))])) (defmacro test-ns "If the namespace defines a function named test-ns-hook, calls that. @@ -306,24 +355,13 @@ namespace object or a symbol. Internally binds *report-counters* to a ref initialized to - *initial-report-counters*. Returns the final, dereferenced state of - *report-counters*." + *initial-report-counters*. " ([ns] `(cljs.test/test-ns (cljs.test/empty-env) ~ns)) ([env [quote ns :as form]] - (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") - (assert (ana-api/find-ns ns) (str "Namespace " ns " does not exist")) - `(do - (cljs.test/set-env! ~env) - (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) - ;; If the namespace has a test-ns-hook function, call that: - ~(if-let [v (ana-api/ns-resolve ns 'test-ns-hook)] - `(~(symbol (name ns) "test-ns-hook")) - ;; Otherwise, just test every var in the namespace. - `(cljs.test/test-all-vars ~form)) - (cljs.test/do-report {:type :end-test-ns, :ns ~form}) - (let [ret# (cljs.test/get-current-env)] - (cljs.test/clear-env!) - ret#)))) + `(cljs.test/run-block + (concat (cljs.test/test-ns-block ~env ~form) + [(fn [] + (cljs.test/clear-env!))])))) ;; ============================================================================= ;; Fixes diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 8d89de49e..8d7255adc 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -8,7 +8,8 @@ (ns ^{:author "Stuart Sierra, with contributions and suggestions by - Chas Emerick, Allen Rohner, Stuart Halloway, and David Nolen", + Chas Emerick, Allen Rohner, Stuart Halloway, David Nolen, and + Leon Grapenthin", :doc "A unit testing framework. ASSERTIONS @@ -144,14 +145,17 @@ Fixtures allow you to run code before and after tests, to set up the context in which tests should be run. - A fixture is just a function that calls another function passed as - an argument. It looks like this: + A fixture is a map of one or two functions that run code before and + after tests. It looks like this: - (defn my-fixture [f] - Perform setup, establish bindings, whatever. - (f) Then call the function we were passed. - Tear-down / clean-up code here. - ) + {:before (fn [] + Perform setup, establish bindings, whatever. + ) + :after (fn [] + Tear-down / clean-up code here. + )} + + Both are optional and can be left out. Fixtures are attached to namespaces in one of two ways. \"each\" fixtures are run repeatedly, once for each test function created @@ -161,9 +165,10 @@ \"each\" fixtures can be attached to the current namespace like this: (use-fixtures :each fixture1 fixture2 ...) - The fixture1, fixture2 are just functions like the example above. - They can also be anonymous functions, like this: - (use-fixtures :each (fn [f] setup... (f) cleanup...)) + The fixture1, fixture2 are just maps like the example above. + They can also be passed directly, like this: + (use-fixtures :each + {:before (fn [] setup...), :after (fn [] cleanup...)}) The other kind of fixture, a \"once\" fixture, is only run once, around ALL the tests in the namespace. \"once\" fixtures are useful @@ -177,6 +182,21 @@ are using test-ns-hook, fixture functions will *never* be run. + WRAPPING FIXTURES + + Instead of a map, a fixture can be specified like this: + + (defn my-fixture [f] + Perform setup, establish bindings, whatever. + (f) Then call the function we were passed. + Tear-down / clean-up code here. + ) + + This style is incompatible with async tests. If an async test is + encountered, testing will be aborted. It can't be mixed with + fixtures specified as maps. + + EXTENDING TEST-IS (ADVANCED) You can extend the behavior of the \"is\" macro by defining new @@ -235,6 +255,12 @@ (defn clear-env! [] (set! *current-env* nil)) +(defn get-and-clear-env! [] + "Like get-current-env, but cleans env before returning." + (let [env (cljs.test/get-current-env)] + (clear-env!) + env)) + (defn testing-vars-str "Returns a string representation of the current test. Renders names in *testing-vars* as a list, then the source file and line of @@ -299,7 +325,8 @@ ;; Ignore these message types: (defmethod report [::default :end-test-ns] [m]) -(defmethod report [::default :begin-test-var] [m]) +(defmethod report [::default :begin-test-var] [m] + #_(println ":begin-test-var" (testing-vars-str m))) (defmethod report [::default :end-test-var] [m]) (defn js-line-and-column [stack-element] @@ -351,61 +378,172 @@ m)] (report m))) +;; ============================================================================= +;; Async + +(defprotocol IAsyncTest + "Marker protocol denoting CPS function to begin asynchronous + testing.") + +(defn async? + "Returns whether x implements IAsyncTest." + [x] + (satisfies? IAsyncTest x)) + +(defn run-block + "Invoke all functions in fns with no arguments. A fn can optionally + return + + an async test - is invoked with a continuation running left fns + + a seq of fns tagged per block - are invoked immediately after fn" + [fns] + (when-first [f fns] + (let [obj (f)] + (if (async? obj) + (obj (let [d (delay (run-block (rest fns)))] + (fn [] + (if (realized? d) + (println "WARNING: Async test called done more than one time.") + @d)))) + (recur (cond->> (rest fns) + (::block? (meta obj)) (concat obj))))))) + +(defn block + "Tag a seq of fns to be picked up by run-block as injected + continuation. See run-block." + [fns] + (some-> fns + (vary-meta assoc ::block? true))) + ;; ============================================================================= ;; Low-level functions +(defn test-var-block + "Like test-var, but returns a block for further composition and + later execution." + [v] + {:pre [(instance? Var v)]} + (if-let [t (:test (meta v))] + [(fn [] + (update-current-env! [:testing-vars] conj v) + (update-current-env! [:report-counters :test] inc) + (do-report {:type :begin-test-var :var v}) + (let [{:keys [async-disabled]} (get-current-env)] + (cond-> (try + (t) + (catch :default e + (do-report + {:type :error + :message "Uncaught exception, not in assertion." + :expected nil + :actual e}))) + async-disabled (-> async? not (assert async-disabled))))) + (fn [] + (do-report {:type :end-test-var :var v}) + (update-current-env! [:testing-vars] rest))])) + (defn test-var "If v has a function in its :test metadata, calls that function, add v to :testing-vars property of env." [v] - {:pre [(instance? Var v)]} - (if-let [t (:test (meta v))] - (do - (update-current-env! [:testing-vars] conj v) - (update-current-env! [:report-counters :test] inc) - (do-report {:type :begin-test-var :var v}) - (try - (t) - (catch :default e - (do-report - {:type :error - :message "Uncaught exception, not in assertion." - :expected nil - :actual e}))) - (do-report {:type :end-test-var :var v}) - (update-current-env! [:testing-vars] rest)))) + (run-block (test-var-block v))) (defn- default-fixture - "The default, empty, fixture function. Just calls its argument." + "The default, empty, fixture function. Just calls its argument. + + NOTE: Incompatible with map fixtures." [f] (f)) (defn compose-fixtures "Composes two fixture functions, creating a new fixture function - that combines their behavior." + that combines their behavior. + + NOTE: Incompatible with map fixtures." [f1 f2] (fn [g] (f1 (fn [] (f2 g))))) (defn join-fixtures "Composes a collection of fixtures, in order. Always returns a valid - fixture function, even if the collection is empty." + fixture function, even if the collection is empty. + + NOTE: Incompatible with map fixtures." [fixtures] (reduce compose-fixtures default-fixture fixtures)) +(defn- wrap-map-fixtures + "Wraps block in map-fixtures." + [map-fixtures block] + (concat (map :before map-fixtures) + block + (reverse (map :after map-fixtures)))) + +(defn- fixtures-type + [coll] + (cond (empty? coll) + :none + (every? map? coll) + :map + (every? fn? coll) + :fn)) + +(defn- execution-strategy + [once-fixtures each-fixtures] + (let [types (map fixtures-type [once-fixtures each-fixtures]) + _ (assert (not-any? nil? types) + "Fixtures may not be of mixed types") + types (->> types + (remove #{:none}) + (distinct)) + _ (assert (> 2 (count types)) "fixtures specified in :once and :each must be of the same type")] + (case (first types) + :map :async + :fn :sync + nil :async))) + +(defn test-vars-block + "Like test-vars, but returns a block for further composition and + later execution." + [vars] + (map + (fn [[ns vars]] + (fn [] + (block + (let [env (get-current-env) + once-fixtures (get-in env [:once-fixtures ns]) + each-fixtures (get-in env [:each-fixtures ns])] + (case (execution-strategy once-fixtures each-fixtures) + :async + (->> vars + (filter (comp :test meta)) + (mapcat (comp (partial wrap-map-fixtures each-fixtures) + test-var-block)) + (wrap-map-fixtures once-fixtures)) + :sync + (do + (update-current-env! [:async-disabled] + (constantly + "Async tests require fixtures to be specified as maps")) + (let [each-fixture-fn (join-fixtures each-fixtures)] + [(fn [] + ((join-fixtures once-fixtures) + (fn [] + (doseq [v vars] + (when (:test (meta v)) + (each-fixture-fn + (fn [] + (test-var v))))))) + (update-current-env! [:async-disabled] + (constantly nil)))]))))))) + (group-by (comp :ns meta) vars))) + (defn test-vars "Groups vars by their namespace and runs test-vars on them with appropriate fixtures assuming they are present in the current testing environment." [vars] - (doseq [[ns vars] (group-by (comp :ns meta) vars)] - (let [env (get-current-env) - once-fixture-fn (join-fixtures (get-in env [:once-fixtures ns])) - each-fixture-fn (join-fixtures (get-in env [:each-fixtures ns]))] - (once-fixture-fn - (fn [] - (doseq [v vars] - (when (:test (meta v)) - (each-fixture-fn (fn [] (test-var v)))))))))) + (run-block (test-vars-block vars))) ;; ============================================================================= ;; Running Tests, high level functions From 20a124b66970e5161144c5eda448b28cf46f28bb Mon Sep 17 00:00:00 2001 From: lgrapenthin Date: Sun, 8 Feb 2015 04:43:43 +0100 Subject: [PATCH 0595/4033] fix: :before and :after in a map fixture are optional --- src/cljs/cljs/test.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 8d7255adc..986c54c6e 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -475,9 +475,9 @@ (defn- wrap-map-fixtures "Wraps block in map-fixtures." [map-fixtures block] - (concat (map :before map-fixtures) + (concat (keep :before map-fixtures) block - (reverse (map :after map-fixtures)))) + (reverse (keep :after map-fixtures)))) (defn- fixtures-type [coll] From ad2d901d2a4e67ba7afd3b5e13895aec2da2d5ed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Feb 2015 23:03:10 -0500 Subject: [PATCH 0596/4033] convert namespaced keyword tests --- test/cljs/cljs/ns_test/foo.cljs | 8 +++++--- test/cljs/test_runner.cljs | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/test/cljs/cljs/ns_test/foo.cljs b/test/cljs/cljs/ns_test/foo.cljs index 4ab899017..5fa9cadc6 100644 --- a/test/cljs/cljs/ns_test/foo.cljs +++ b/test/cljs/cljs/ns_test/foo.cljs @@ -1,9 +1,11 @@ -(ns cljs.ns-test.foo) +(ns cljs.ns-test.foo + (:require [cljs.test :refer-macros [deftest is]])) (defn baz [] 123) (def kw ::foo) (def qkw '::foo) -(assert (= (str kw) ":cljs.ns-test.foo/foo")) -(assert (= (str qkw) ":cljs.ns-test.foo/foo")) +(deftest test-namespaced-keywords + (is (= (str kw) ":cljs.ns-test.foo/foo")) + (is (= (str qkw) ":cljs.ns-test.foo/foo"))) diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index 27f49218c..11d8cfe5f 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -12,7 +12,8 @@ [cljs.top-level] [cljs.reducers-test] [cljs.keyword-test] - [cljs.import-test])) + [cljs.import-test] + [cljs.ns-test.foo])) (set! *print-newline* false) (set-print-fn! js/print) @@ -29,5 +30,6 @@ 'cljs.top-level 'cljs.keyword-test 'cljs.ns-test + 'cljs.ns-test.foo 'foo.ns-shadow-test 'cljs.import-test) From f311ebd121bf2385efcfba6f7bfb07251cf812f1 Mon Sep 17 00:00:00 2001 From: Brian Jenkins Date: Thu, 5 Feb 2015 09:27:05 -0800 Subject: [PATCH 0597/4033] Fix upstream deps when run from repl. --- src/clj/cljs/repl/browser.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 29402b5c9..71f0999a8 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -274,7 +274,7 @@ support reflection. Defaults to \"src/\". " [& {:as opts}] - (let [ups-deps (cljsc/get-upstream-deps) + (let [ups-deps (cljsc/get-upstream-deps (java.lang.ClassLoader/getSystemClassLoader)) opts (assoc opts :ups-libs (:libs ups-deps) :ups-foreign-libs (:foreign-libs ups-deps)) From d2237d016a622739e475f732c0c037e44179d3c1 Mon Sep 17 00:00:00 2001 From: Brian Jenkins Date: Thu, 5 Feb 2015 09:35:31 -0800 Subject: [PATCH 0598/4033] Fix typo. --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c9eac846f..0bba07343 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -911,7 +911,7 @@ should contain the source for the given namespace name." "returns a merged map containing all upstream dependencies defined by libraries on the classpath. Should be run in the main thread. If not, pass (java.lang.ClassLoader/getSystemClassLoader) to use the - system classloder." + system classloader." ([] (get-upstream-deps* (. (Thread/currentThread) (getContextClassLoader)))) ([classloader] From 0657b0a3ab2a606bb114620d0f66265158a7bc39 Mon Sep 17 00:00:00 2001 From: Shaun LeBron Date: Sun, 8 Feb 2015 01:11:17 -0600 Subject: [PATCH 0599/4033] CLJS-1022: Concatenate foreign dependencies safely Join foreign JS dependencies with newlines between them to prevent last-line comments from commenting out subsequent concatenated code. --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 0bba07343..08346e847 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -931,7 +931,7 @@ should contain the source for the given namespace name." (:url-min ijs)) (:url ijs))] (slurp url)))] - (apply str (map to-js-str sources)))) + (str (string/join "\n" (map to-js-str sources)) "\n"))) (defn add-wrapper [{:keys [output-wrapper] :as opts} js] (if output-wrapper From ef526a61ec25173966b9d818079d71f46d0bb0dc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Feb 2015 18:35:46 -0500 Subject: [PATCH 0600/4033] add simple source directory `cljs.closure/watch` watcher using java.nio --- src/clj/cljs/closure.clj | 45 +++++++++++++++++++++++++++++++++++++++- src/clj/cljs/util.clj | 5 +++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 08346e847..60f0fde37 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -62,7 +62,8 @@ com.google.javascript.jscomp.CommandLineRunner com.google.javascript.jscomp.AnonymousFunctionNamingPolicy java.security.MessageDigest - javax.xml.bind.DatatypeConverter)) + javax.xml.bind.DatatypeConverter + [java.nio.file Paths StandardWatchEventKinds WatchKey])) (def name-chars (map char (concat (range 48 57) (range 65 90) (range 97 122)))) @@ -1125,6 +1126,48 @@ should contain the source for the given namespace name." (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) +(defn watch + "Given a source which can be compiled, watch it for changes invoking build + then it does." + ([source opts] + (watch source opts + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)))) + ([source opts compiler-env] + (let [path (Paths/get (.toURI (io/file source))) + fs (.getFileSystem path)] + (try + (println "Watching path:" path) + (let [service (.newWatchService fs) + key nil] + (. path + (register service + (into-array [StandardWatchEventKinds/ENTRY_CREATE + StandardWatchEventKinds/ENTRY_DELETE + StandardWatchEventKinds/ENTRY_MODIFY]))) + (loop [key nil] + (when (or (nil? key) (. ^WatchKey key reset)) + (let [key (. service take) + start (util/now)] + (when (seq (.pollEvents key)) + (print "Change detected, recompiling...") + (build source opts compiler-env) + (println " done. Elapsed" + (util/to-secs (- (util/now) start)) "seconds")) + (recur key))))) + (catch Exception e + (.printStackTrace e) + (System/exit 1)))))) + +(comment + (watch "samples/hello/src" + {:optimizations :none + :output-to "samples/hello/out/hello.js" + :output-dir "samples/hello/out" + :source-map true}) + ) + ;; ============================================================================= ;; Utilities diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 6143f5575..e9891930e 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -128,3 +128,8 @@ ret#) ~expr))) +(defn now [] + (System/nanoTime)) + +(defn to-secs [nano] + (/ nano 1e9)) \ No newline at end of file From 59e7550d42ef7e4fc09d62431804df4d2d73f6f4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Feb 2015 18:36:06 -0500 Subject: [PATCH 0601/4033] update Intellj excludes --- Clojurescript.iml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index b708b5900..5fac6a1c2 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -14,7 +14,9 @@ - + + + From 644a59628ddaf8e276ebfed3165dfd13f434ef7f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Feb 2015 22:07:38 -0500 Subject: [PATCH 0602/4033] tweaks to update logic --- src/clj/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 60f0fde37..6328f2b07 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1152,9 +1152,11 @@ should contain the source for the given namespace name." start (util/now)] (when (seq (.pollEvents key)) (print "Change detected, recompiling...") + (flush) (build source opts compiler-env) (println " done. Elapsed" - (util/to-secs (- (util/now) start)) "seconds")) + (util/to-secs (unchecked-subtract (util/now) start)) "seconds") + (flush)) (recur key))))) (catch Exception e (.printStackTrace e) @@ -1165,6 +1167,7 @@ should contain the source for the given namespace name." {:optimizations :none :output-to "samples/hello/out/hello.js" :output-dir "samples/hello/out" + :cache-analysis true :source-map true}) ) From 7f18d24572d9e329433b66813c5769bc8a0a58b1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Feb 2015 06:28:28 -0500 Subject: [PATCH 0603/4033] watch file system changes more frequently --- src/clj/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 6328f2b07..a67e5d1be 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -63,7 +63,8 @@ com.google.javascript.jscomp.AnonymousFunctionNamingPolicy java.security.MessageDigest javax.xml.bind.DatatypeConverter - [java.nio.file Paths StandardWatchEventKinds WatchKey])) + [java.nio.file Paths StandardWatchEventKinds WatchKey] + [com.sun.nio.file SensitivityWatchEventModifier])) (def name-chars (map char (concat (range 48 57) (range 65 90) (range 97 122)))) @@ -1145,7 +1146,8 @@ should contain the source for the given namespace name." (register service (into-array [StandardWatchEventKinds/ENTRY_CREATE StandardWatchEventKinds/ENTRY_DELETE - StandardWatchEventKinds/ENTRY_MODIFY]))) + StandardWatchEventKinds/ENTRY_MODIFY]) + (into-array [SensitivityWatchEventModifier/HIGH]))) (loop [key nil] (when (or (nil? key) (. ^WatchKey key reset)) (let [key (. service take) From cecbd33e57c9fb8d1cc3463f78e081c63f2d9315 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Feb 2015 06:41:29 -0500 Subject: [PATCH 0604/4033] refactor Java imports / usage --- src/clj/cljs/closure.clj | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index a67e5d1be..2b3d62433 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -43,33 +43,24 @@ [clojure.java.io :as io] [clojure.string :as string] [clojure.data.json :as json]) - (:import java.io.File - java.io.BufferedInputStream - java.net.URL - java.util.logging.Level - java.util.List - com.google.javascript.jscomp.CompilerOptions - com.google.javascript.jscomp.CompilerOptions$LanguageMode - com.google.javascript.jscomp.CompilationLevel - com.google.javascript.jscomp.SourceMap$Format - com.google.javascript.jscomp.SourceMap$DetailLevel - com.google.javascript.jscomp.ClosureCodingConvention - com.google.javascript.jscomp.SourceFile - com.google.javascript.jscomp.Result - com.google.javascript.jscomp.JSError - com.google.javascript.jscomp.CheckLevel - com.google.javascript.jscomp.DiagnosticGroups - com.google.javascript.jscomp.CommandLineRunner - com.google.javascript.jscomp.AnonymousFunctionNamingPolicy - java.security.MessageDigest - javax.xml.bind.DatatypeConverter + (:import [java.io File BufferedInputStream] + [java.net URL] + [java.util.logging Level] + [java.util List Random] + [com.google.javascript.jscomp CompilerOptions CompilationLevel + CompilerOptions$LanguageMode SourceMap$Format + SourceMap$DetailLevel ClosureCodingConvention SourceFile + Result JSError CheckLevel DiagnosticGroups + CommandLineRunner AnonymousFunctionNamingPolicy] + [java.security MessageDigest] + [javax.xml.bind DatatypeConverter] [java.nio.file Paths StandardWatchEventKinds WatchKey] [com.sun.nio.file SensitivityWatchEventModifier])) (def name-chars (map char (concat (range 48 57) (range 65 90) (range 97 122)))) (defn random-char [] - (nth name-chars (.nextInt (java.util.Random.) (count name-chars)))) + (nth name-chars (.nextInt (Random.) (count name-chars)))) (defn random-string [length] (apply str (take length (repeatedly random-char)))) @@ -345,7 +336,7 @@ (if output-file (let [out-file (io/file (util/output-directory opts) output-file)] (compiled-file (comp/compile-file file out-file opts))) - (binding [ana/*cljs-file* (.getPath ^java.io.File file)] + (binding [ana/*cljs-file* (.getPath ^File file)] (compile-form-seq (ana/forms-seq file))))) (defn compile-dir From 5c1ad0bbea1883d1d320d525748cf1537b278da5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Feb 2015 06:45:06 -0500 Subject: [PATCH 0605/4033] tweak docstring --- src/clj/cljs/closure.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 2b3d62433..bb4687189 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1119,8 +1119,9 @@ should contain the source for the given namespace name." ret)))))) (defn watch - "Given a source which can be compiled, watch it for changes invoking build - then it does." + "Given a source directory, produce runnable JavaScript. Watch the source + directory for changes rebuliding when necessary. Takes the same arguments as + cljs.closure/build." ([source opts] (watch source opts (if-not (nil? env/*compiler*) From d794698a55ec8c640efb9828026cb9270ec2e85b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Feb 2015 07:05:28 -0500 Subject: [PATCH 0606/4033] change `cljs.closure/watch` to always build at least once --- src/clj/cljs/closure.clj | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index bb4687189..9117cfe06 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1128,10 +1128,19 @@ should contain the source for the given namespace name." env/*compiler* (env/default-compiler-env opts)))) ([source opts compiler-env] - (let [path (Paths/get (.toURI (io/file source))) - fs (.getFileSystem path)] + (let [path (Paths/get (.toURI (io/file source))) + fs (.getFileSystem path) + buildf (fn [] + (let [start (util/now)] + (build source opts compiler-env) + (println " done. Elapsed" + (util/to-secs (unchecked-subtract (util/now) start)) "seconds") + (flush)))] (try - (println "Watching path:" path) + (print "Building...") + (flush) + (buildf) + (println "Watching path:" source) (let [service (.newWatchService fs) key nil] (. path @@ -1142,15 +1151,11 @@ should contain the source for the given namespace name." (into-array [SensitivityWatchEventModifier/HIGH]))) (loop [key nil] (when (or (nil? key) (. ^WatchKey key reset)) - (let [key (. service take) - start (util/now)] + (let [key (. service take)] (when (seq (.pollEvents key)) (print "Change detected, recompiling...") (flush) - (build source opts compiler-env) - (println " done. Elapsed" - (util/to-secs (unchecked-subtract (util/now) start)) "seconds") - (flush)) + (buildf)) (recur key))))) (catch Exception e (.printStackTrace e) From 9a95c98e386a6e1beac5dcbf78ab4391c6efa00c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Feb 2015 08:19:44 -0500 Subject: [PATCH 0607/4033] tweak watch output again so it's more useful when combined with :verbose true --- src/clj/cljs/closure.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 9117cfe06..d83d1988e 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1133,11 +1133,11 @@ should contain the source for the given namespace name." buildf (fn [] (let [start (util/now)] (build source opts compiler-env) - (println " done. Elapsed" + (println "... done. Elapsed" (util/to-secs (unchecked-subtract (util/now) start)) "seconds") (flush)))] (try - (print "Building...") + (println "Building...") (flush) (buildf) (println "Watching path:" source) @@ -1153,7 +1153,7 @@ should contain the source for the given namespace name." (when (or (nil? key) (. ^WatchKey key reset)) (let [key (. service take)] (when (seq (.pollEvents key)) - (print "Change detected, recompiling...") + (println "Change detected, recompiling...") (flush) (buildf)) (recur key))))) @@ -1167,7 +1167,8 @@ should contain the source for the given namespace name." :output-to "samples/hello/out/hello.js" :output-dir "samples/hello/out" :cache-analysis true - :source-map true}) + :source-map true + :verbose true}) ) ;; ============================================================================= From 77e238a14a0b0ae94ecf8174053cb2774fd8a653 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Feb 2015 08:33:28 -0500 Subject: [PATCH 0608/4033] remove trivial util helpers --- src/clj/cljs/closure.clj | 4 ++-- src/clj/cljs/util.clj | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index d83d1988e..c7759a063 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1131,10 +1131,10 @@ should contain the source for the given namespace name." (let [path (Paths/get (.toURI (io/file source))) fs (.getFileSystem path) buildf (fn [] - (let [start (util/now)] + (let [start (System/nanoTime)] (build source opts compiler-env) (println "... done. Elapsed" - (util/to-secs (unchecked-subtract (util/now) start)) "seconds") + (/ (unchecked-subtract (System/nanoTime) start) 1e9) "seconds") (flush)))] (try (println "Building...") diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index e9891930e..47ccaaac5 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -127,9 +127,3 @@ (debug-prn "") ret#) ~expr))) - -(defn now [] - (System/nanoTime)) - -(defn to-secs [nano] - (/ nano 1e9)) \ No newline at end of file From 71b77183612497ed8238bce10279b8508f857484 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Feb 2015 12:29:03 -0500 Subject: [PATCH 0609/4033] change `cljs.closure/watch` so it doesn't fail from build exceptions --- src/clj/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c7759a063..8124df370 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1155,7 +1155,10 @@ should contain the source for the given namespace name." (when (seq (.pollEvents key)) (println "Change detected, recompiling...") (flush) - (buildf)) + (try + (buildf) + (catch Exception e + (.printStackTrace e)))) (recur key))))) (catch Exception e (.printStackTrace e) From 3c3cae67cd9a9afbf5c0c0b003d8dd893394dbb3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Feb 2015 18:17:01 -0500 Subject: [PATCH 0610/4033] fix require with browser REPL, set base path to "goog/" --- src/cljs/clojure/browser/repl.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index bdaddbfd6..355faa5e0 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -131,6 +131,6 @@ (let [script (.createElement js/document "script")] (set! (.-type script) "text/javascript") (set! (.-src script) - (str js/goog.basePath + (str "goog/" (aget (.. js/goog -dependencies_ -nameToPath) name))) script)))))))) From 014524d9f04f6262dce7af13cb47c0c29db07908 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Feb 2015 19:26:29 -0500 Subject: [PATCH 0611/4033] CLJS-1023: macro-autoload-ns? and ns-dependents need to throw on cyclic dependencies Add *macro-infer* dynamic var. Use this to prevent needless recursion, we only want to look one level deep anyway. Move *dependents* updating in compiler after compile-file* call. This way analyzer parsing of ns will catch circular deps. --- src/clj/cljs/analyzer.clj | 20 +++++++++++++------- src/clj/cljs/compiler.clj | 14 ++++++++------ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index e9c286c56..1d0ea31f3 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -34,6 +34,7 @@ (def ^:dynamic *analyze-deps* true) (def ^:dynamic *load-tests* true) (def ^:dynamic *load-macros* true) +(def ^:dynamic *macro-infer* true) ;; log compiler activities (def ^:dynamic *verbose* false) @@ -1235,13 +1236,14 @@ "Given a spec form check whether the spec namespace requires a macro file of the same name. If so return true." [form] - (let [ns (if (sequential? form) (first form) form) - {:keys [use-macros require-macros]} - (or (get-in @env/*compiler* [::namespaces ns]) - (when-let [res (io/resource (util/ns->relpath ns))] - (:ast (parse-ns res))))] - (or (some #{ns} (vals use-macros)) - (some #{ns} (vals require-macros))))) + (when *macro-infer* + (let [ns (if (sequential? form) (first form) form) + {:keys [use-macros require-macros]} + (or (get-in @env/*compiler* [::namespaces ns]) + (when-let [res (io/resource (util/ns->relpath ns))] + (:ast (parse-ns res))))] + (or (some #{ns} (vals use-macros)) + (some #{ns} (vals require-macros)))))) (defn desugar-ns-specs "Given an original set of ns specs desugar :include-macros and :refer-macros @@ -1811,6 +1813,10 @@ argument, which the reader will use in any emitted errors." (let [namespaces' (::namespaces @env/*compiler*) ret (binding [*cljs-ns* 'cljs.user + *macro-infer* + (or (when (contains? opts :macro-infer) + (:macro-infer opts)) + false) *analyze-deps* (or (when (contains? opts :analyze-deps) (:analyze-deps opts)) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 48ed0d7e5..7866c4219 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1024,6 +1024,7 @@ ([src dest] (compile-file src dest nil)) ([src dest opts] + {:post [map?]} (let [src-file (io/file src) dest-file (io/file dest) opts (merge {:optimizations :none} opts)] @@ -1033,15 +1034,16 @@ opts (if (= ns 'cljs.core) (assoc opts :static-fns true) opts)] (if (requires-compilation? src-file dest-file opts) (do - (when *dependents* - (swap! *dependents* - (fn [{:keys [recompile visited]}] - {:recompile (into recompile (ana/ns-dependents ns)) - :visited (conj visited ns)}))) (util/mkdirs dest-file) (when (contains? (::ana/namespaces @env/*compiler*) ns) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) - (compile-file* src-file dest-file opts)) + (let [ret (compile-file* src-file dest-file opts)] + (when *dependents* + (swap! *dependents* + (fn [{:keys [recompile visited]}] + {:recompile (into recompile (ana/ns-dependents ns)) + :visited (conj visited ns)}))) + ret)) (do (when-not (contains? (::ana/namespaces @env/*compiler*) ns) (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) From ce829ed62c13c670490aa8ebb9570d4d19c4a247 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Feb 2015 19:38:47 -0500 Subject: [PATCH 0612/4033] 0.0-2814 --- README.md | 6 +++--- changes.md | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a95a9c42e..1a239c718 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2760 +Latest stable release: 0.0-2814 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2760"] +[org.clojure/clojurescript "0.0-2814"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2760 org.clojure clojurescript - 0.0-2760 + 0.0-2814 ``` diff --git a/changes.md b/changes.md index fab46ead4..489958fa9 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,28 @@ +## 0.0-2814 + +### Enhancements +* add simple source directory `cljs.closure/watch` watcher using java.nio +* CLJS-1022: Concatenate foreign dependencies safely +* CLJS-988: Support async testing in cljs.test +* CLJS-1018: Add support for cljs.core/*e Modify the JavaScript that is sent for evaluation to wrap in a try and then catch any exception thrown, assign it to *e, and then rethrow. +* CLJS-1012: Correct behavior when *print-length* is set to 0 +* Added new :closure-extra-annotations compiler option allowing to define extra JSDoc annotation used by closure libraries. +* Mirrored source map support APIs on server/client +* Unified source mapping support in REPLs +* Nashorn REPL (thanks Pieter van Prooijen) + +### Fixes +* CLJS-1023: regression, macro-autoload-ns? and ns-dependents need to throw on cyclic dependencies +* fix require with browser REPL, set base path to "goog/" +* CLJS-1020: off by one error in REPL source map support +* Node.js 0.12 support +* browser REPL needs to respect :output-dir +* CLJS-1006: Implicit dependency of clojure.browser.repl on cljs.repl +* CLJS-1005: Browser REPL creates 'out' directory no matter what +* CLJS-1003: fix cljs.test run-tests do-report :summary issues +* CLJS-1003: Cannot pass custom env to run-tests +* Windows Node.js REPL issues + ## 0.0-2760 ### Fixes From ef998e808fe41f8b53ef838e22ec5a7520643983 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Feb 2015 07:28:46 -0500 Subject: [PATCH 0613/4033] CLJS-1001: reify metadata leakage reify was incorrectly passing metadata through without eliding reader metadata on the form. --- src/clj/cljs/analyzer.clj | 5 ++++- src/clj/cljs/core.clj | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 1d0ea31f3..4494f5925 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1707,9 +1707,12 @@ :children items :tag (if (map? val) 'object 'array)})) +(defn elide-reader-meta [m] + (dissoc m :file :line :column :end-column :end-line :source)) + (defn analyze-wrap-meta [expr] (let [form (:form expr) - m (dissoc (meta form) :file :line :column :end-column :end-line :source)] + m (elide-reader-meta (meta form))] (if (seq m) (let [env (:env expr) ; take on expr's context ourselves expr (assoc-in expr [:env :context] :expr) ; change expr to :expr diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 4ef50f342..0b6ed3e5b 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -626,7 +626,7 @@ IMeta (~'-meta [~this-sym] ~meta-sym) ~@impls)) - (new ~t ~@locals ~(meta &form))))) + (new ~t ~@locals ~(ana/elide-reader-meta (meta &form)))))) (defmacro specify! [expr & impls] (let [x (with-meta (gensym "x") {:extend :instance})] From 35580900461ce408a05fc14982959b610371a608 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Feb 2015 07:33:16 -0500 Subject: [PATCH 0614/4033] 0.0-2816 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1a239c718..2b79e89af 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2814 +Latest stable release: 0.0-2816 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2814"] +[org.clojure/clojurescript "0.0-2816"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2814 org.clojure clojurescript - 0.0-2814 + 0.0-2816 ``` diff --git a/changes.md b/changes.md index 489958fa9..a488095fb 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-2816 + +### Fixes +* CLJS-1001: reify did not elide reader metadata + ## 0.0-2814 ### Enhancements From 0db90e6898434c1719fc2bfc6ab9cf741dde4cf2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Feb 2015 08:48:13 -0500 Subject: [PATCH 0615/4033] change `cljs.closure/watch` so it correctly watches all subdirectories do not recompile unless changed path is a file with .cljs or .js extension --- src/clj/cljs/closure.clj | 77 +++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 8124df370..85fee7762 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -54,7 +54,8 @@ CommandLineRunner AnonymousFunctionNamingPolicy] [java.security MessageDigest] [javax.xml.bind DatatypeConverter] - [java.nio.file Paths StandardWatchEventKinds WatchKey] + [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey + WatchEvent FileVisitor FileVisitResult] [com.sun.nio.file SensitivityWatchEventModifier])) (def name-chars (map char (concat (range 48 57) (range 65 90) (range 97 122)))) @@ -1128,41 +1129,53 @@ should contain the source for the given namespace name." env/*compiler* (env/default-compiler-env opts)))) ([source opts compiler-env] - (let [path (Paths/get (.toURI (io/file source))) - fs (.getFileSystem path) - buildf (fn [] - (let [start (System/nanoTime)] - (build source opts compiler-env) - (println "... done. Elapsed" - (/ (unchecked-subtract (System/nanoTime) start) 1e9) "seconds") - (flush)))] - (try + (let [path (Paths/get (.toURI (io/file source))) + fs (.getFileSystem path) + service (.newWatchService fs)] + (letfn [(buildf [] + (let [start (System/nanoTime)] + (build source opts compiler-env) + (println "... done. Elapsed" + (/ (unchecked-subtract (System/nanoTime) start) 1e9) "seconds") + (flush))) + (watch-all [^Path root] + (Files/walkFileTree root + (reify + FileVisitor + (preVisitDirectory [_ dir _] + (let [^Path dir dir] + (. dir + (register service + (into-array [StandardWatchEventKinds/ENTRY_CREATE + StandardWatchEventKinds/ENTRY_DELETE + StandardWatchEventKinds/ENTRY_MODIFY]) + (into-array [SensitivityWatchEventModifier/HIGH])))) + FileVisitResult/CONTINUE) + (postVisitDirectory [_ dir exc] + FileVisitResult/CONTINUE) + (visitFile [_ file attrs] + FileVisitResult/CONTINUE) + (visitFileFailed [_ file exc] + FileVisitResult/CONTINUE))))] (println "Building...") (flush) (buildf) (println "Watching path:" source) - (let [service (.newWatchService fs) - key nil] - (. path - (register service - (into-array [StandardWatchEventKinds/ENTRY_CREATE - StandardWatchEventKinds/ENTRY_DELETE - StandardWatchEventKinds/ENTRY_MODIFY]) - (into-array [SensitivityWatchEventModifier/HIGH]))) - (loop [key nil] - (when (or (nil? key) (. ^WatchKey key reset)) - (let [key (. service take)] - (when (seq (.pollEvents key)) - (println "Change detected, recompiling...") - (flush) - (try - (buildf) - (catch Exception e - (.printStackTrace e)))) - (recur key))))) - (catch Exception e - (.printStackTrace e) - (System/exit 1)))))) + (watch-all path) + (loop [key nil] + (when (or (nil? key) (. ^WatchKey key reset)) + (let [key (. service take)] + (when (some (fn [^WatchEvent e] + (or (.. (. e context) toString (endsWith "cljs")) + (.. (. e context) toString (endsWith "js")))) + (seq (.pollEvents key))) + (println "Change detected, recompiling...") + (flush) + (try + (buildf) + (catch Exception e + (.printStackTrace e)))) + (recur key)))))))) (comment (watch "samples/hello/src" From 2711d02948c103e33ab4fd1431881edcd65095ef Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Feb 2015 09:16:16 -0500 Subject: [PATCH 0616/4033] update stale comment --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index b6ac2d4f8..357cac249 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -186,7 +186,7 @@ (map ;; source maps are 0 indexed for columns ;; multiple segments may exist at column - ;; just take first + ;; the last segment seems most accurate (last (if-let [mapping (get columns (dec column))] mapping From f1507a664b82c4bfdf708494642abb0865fd3a42 Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Sun, 8 Feb 2015 16:07:30 +0100 Subject: [PATCH 0617/4033] CLJS-1016: make "..." marker configurable --- src/cljs/cljs/core.cljs | 4 ++-- test/cljs/cljs/core_test.cljs | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index f8791f738..bcedcc719 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8115,7 +8115,7 @@ reduces them without incurring seq initialization" (-write writer begin) (if (zero? (:print-length opts)) (when (seq coll) - (-write writer "...")) + (-write writer (or (:more-marker opts) "..."))) (do (when (seq coll) (print-one (first coll) writer opts)) @@ -8127,7 +8127,7 @@ reduces them without incurring seq initialization" (recur (next coll) (dec n))) (when (and (seq coll) (zero? n)) (-write writer sep) - (-write writer "...")))))) + (-write writer (or (:more-marker opts) "..."))))))) (-write writer end))))) (defn write-all [writer & ss] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index ac9fa62f9..27f39f75b 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1491,6 +1491,20 @@ "{:foo \"bar\", :baz \"woz\"}"))) ) +(deftest test-print-with-opts + (testing "Testing printing with opts - :more-marker" + (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "" :print-length 0}) + "[]")) + (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "\u2026" :print-length 1}) + "[1 \u2026]")) + (is (= (pr-str-with-opts [#{1 2 3}] {:more-marker "\u2026" :print-length 2}) + "#{1 3 \u2026}")) + (is (= (pr-str-with-opts ['(1 2 3)] {:more-marker "\u2026" :print-length 2}) + "(1 2 \u2026)")) + (is (= (pr-str-with-opts [{:1 1 :2 2 :3 3}] {:more-marker "\u2026" :print-length 2}) + "{:1 1, :2 2, \u2026}"))) + ) + (defrecord PrintMe [a b]) (deftest test-pr-str From d20eb31f95389d5fc9a2bdf865b5a5aba4b5b4af Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Fri, 6 Feb 2015 17:59:43 +0100 Subject: [PATCH 0618/4033] CLJS-1010: Printing hook for cljs-devtools A support for pr-writer customization. --- src/cljs/cljs/core.cljs | 14 ++++++++++---- test/cljs/cljs/core_test.cljs | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index bcedcc719..d47adc5ab 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8160,10 +8160,7 @@ reduces them without incurring seq initialization" (declare print-map) -(defn- pr-writer - "Prefer this to pr-seq, because it makes the printing function - configurable, allowing efficient implementations such as appending - to a StringBuffer." +(defn- pr-writer-worker [obj writer opts] (cond (nil? obj) (-write writer "nil") @@ -8231,6 +8228,15 @@ reduces them without incurring seq initialization" :else (write-all writer "#<" (str obj) ">"))))) +(defn- pr-writer + "Prefer this to pr-seq, because it makes the printing function + configurable, allowing efficient implementations such as appending + to a StringBuffer." + [obj writer opts] + (if-let [alt-worker (:alt-worker opts)] + (alt-worker obj writer (assoc opts :fallback-worker pr-writer-worker)) + (pr-writer-worker obj writer opts))) + (defn pr-seq-writer [objs writer opts] (pr-writer (first objs) writer opts) (doseq [obj (next objs)] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 27f39f75b..d68d50517 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1493,6 +1493,7 @@ (deftest test-print-with-opts (testing "Testing printing with opts - :more-marker" + ; CLJS-1016 (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "" :print-length 0}) "[]")) (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "\u2026" :print-length 1}) @@ -1503,6 +1504,20 @@ "(1 2 \u2026)")) (is (= (pr-str-with-opts [{:1 1 :2 2 :3 3}] {:more-marker "\u2026" :print-length 2}) "{:1 1, :2 2, \u2026}"))) + + (testing "Testing printing with opts - :alt-worker" + ; CLJS-1010 + (is (= (pr-str-with-opts [[1 2 3]] {:alt-worker (fn [obj writer opts] ((:fallback-worker opts) obj writer opts))}) + "[1 2 3]")) + (is (= (pr-str-with-opts [[1 2 3]] {:alt-worker (fn [obj writer opts] (-write writer (str "<" obj ">")))}) + "<[1 2 3]>")) + (is (= (pr-str-with-opts [[:start 1 2 [:middle] 3 4 :end] :standalone] {:alt-worker (fn [obj writer opts] + (if (keyword? obj) + (-write writer (str "|" (name obj) "|")) + ((:fallback-worker opts) obj writer opts)))}) + "[|start| 1 2 [|middle|] 3 4 |end|] |standalone|")) + (is (= (pr-str-with-opts [[1 2 3]] {:alt-worker (fn [obj writer opts])}) + ""))) ) (defrecord PrintMe [a b]) From 8e9d235331a46ecd27ffbfd498bb03089cd11142 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 10 Feb 2015 16:47:08 -0500 Subject: [PATCH 0619/4033] remove any temporary directories which might pollute the classpath --- script/test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/test b/script/test index d3efd5f6c..0e2ba5a83 100755 --- a/script/test +++ b/script/test @@ -1,6 +1,8 @@ #!/bin/sh rm -rf builds/out-adv +rm -rf out +rm -rf target mkdir -p builds/out-adv possible=4 From 6c7aa76fc43be79e0712f30ee5e459c249afda36 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 10 Feb 2015 17:05:32 -0500 Subject: [PATCH 0620/4033] "worker" terminology for printing overloaded, use "impl" instead --- src/cljs/cljs/core.cljs | 8 ++++---- test/cljs/cljs/core_test.cljs | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index d47adc5ab..955919ef2 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8160,7 +8160,7 @@ reduces them without incurring seq initialization" (declare print-map) -(defn- pr-writer-worker +(defn- pr-writer-impl [obj writer opts] (cond (nil? obj) (-write writer "nil") @@ -8233,9 +8233,9 @@ reduces them without incurring seq initialization" configurable, allowing efficient implementations such as appending to a StringBuffer." [obj writer opts] - (if-let [alt-worker (:alt-worker opts)] - (alt-worker obj writer (assoc opts :fallback-worker pr-writer-worker)) - (pr-writer-worker obj writer opts))) + (if-let [alt-impl (:alt-impl opts)] + (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) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index d68d50517..18898babe 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1505,18 +1505,18 @@ (is (= (pr-str-with-opts [{:1 1 :2 2 :3 3}] {:more-marker "\u2026" :print-length 2}) "{:1 1, :2 2, \u2026}"))) - (testing "Testing printing with opts - :alt-worker" + (testing "Testing printing with opts - :alt-impl" ; CLJS-1010 - (is (= (pr-str-with-opts [[1 2 3]] {:alt-worker (fn [obj writer opts] ((:fallback-worker opts) obj writer opts))}) + (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts] ((:fallback-impl opts) obj writer opts))}) "[1 2 3]")) - (is (= (pr-str-with-opts [[1 2 3]] {:alt-worker (fn [obj writer opts] (-write writer (str "<" obj ">")))}) + (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts] (-write writer (str "<" obj ">")))}) "<[1 2 3]>")) - (is (= (pr-str-with-opts [[:start 1 2 [:middle] 3 4 :end] :standalone] {:alt-worker (fn [obj writer opts] + (is (= (pr-str-with-opts [[:start 1 2 [:middle] 3 4 :end] :standalone] {:alt-impl (fn [obj writer opts] (if (keyword? obj) (-write writer (str "|" (name obj) "|")) - ((:fallback-worker opts) obj writer opts)))}) + ((:fallback-impl opts) obj writer opts)))}) "[|start| 1 2 [|middle|] 3 4 |end|] |standalone|")) - (is (= (pr-str-with-opts [[1 2 3]] {:alt-worker (fn [obj writer opts])}) + (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts])}) ""))) ) From 876fd36ca1e907182b09f9f18393a0a0504c2a6a Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 10 Feb 2015 18:36:26 -0500 Subject: [PATCH 0621/4033] need to pass repl-env to cljs.repl/display-error, this is prep work for CLJS-1025 unfortunately IJavaScriptEnv only takes opts in -setup. This change permits functional mapping of source files to their :output-dir relative path w/o breaking downstream REPLs again --- src/clj/cljs/repl.clj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 357cac249..8888eabbf 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -241,9 +241,9 @@ ) (defn- display-error - ([ret form opts] - (display-error ret form (constantly nil) opts)) - ([ret form f opts] + ([repl-env ret form opts] + (display-error repl-env ret form (constantly nil) opts)) + ([repl-env ret form f opts] (f) (println (:value ret)) (when-let [st (:stacktrace ret)] @@ -300,8 +300,8 @@ (print js)) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] (case (:status ret) - :error (display-error ret form opts) - :exception (display-error ret form + :error (display-error repl-env ret form opts) + :exception (display-error repl-env ret form (if (:repl-verbose opts) #(prn "Error evaluating:" form :as js) (constantly nil)) From b3abdf41e8585505b9668d71f5490b7e7e1c8451 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Feb 2015 09:15:01 -0500 Subject: [PATCH 0622/4033] CLJS-1025: make REPL source mapping infrastructure generic The canonicalized stacktrace is now more strict with regards to :file. :file must a URL-style path without protocol relative to :output-dir. This is a host agnostic representation from which we can easily construct a host specific absolute file path. Add IParseStacktrace protocol. All REPL environments should now no longer attempt to parse stacktraces themselves at the time they detect the exception. Instead they should implement IParseStacktrace, they will receive the original stacktrace string as well as all the compiler options. Access to compiler options means :asset-path and/or :output-dir may be used to compute the canonical :file path. --- src/clj/cljs/repl.clj | 48 ++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 8888eabbf..cd19fee6b 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -23,6 +23,7 @@ [cljs.closure :as cljsc] [cljs.source-map :as sm]) (:import [java.io File PushbackReader] + [java.net URL] [javax.xml.bind DatatypeConverter])) (def ^:dynamic *cljs-verbose* false) @@ -91,19 +92,31 @@ ;; CLJS Specifics (defprotocol IReplEnvOptions - (-repl-options [this] "Return default REPL options for a REPL Env")) + (-repl-options [repl-env] "Return default REPL options for a REPL Env")) (defprotocol IJavaScriptEnv - (-setup [this opts] "initialize the environment") - (-evaluate [this filename line js] "evaluate a javascript string") - (-load [this provides url] "load code at url into the environment") - (-tear-down [this] "dispose of the environment")) + (-setup [repl-env opts] "initialize the environment") + (-evaluate [repl-env filename line js] "evaluate a javascript string") + (-load [repl-env provides url] "load code at url into the environment") + (-tear-down [repl-env] "dispose of the environment")) (extend-type Object IReplEnvOptions (-repl-options [_] nil)) +(defprotocol IParseStacktrace + (-parse-stacktrace [repl-env stacktrace build-options] + "Given the original JavaScript stacktrace string and current compiler build + options, parse the stacktrace into the canonical form: + + [{:file + :function + :line + :column }*] + + :file must be a URL path (without protocol) relative to :output-dir.")) + (defn- env->opts "Returns a hash-map containing all of the entries in [repl-env], translating :working-dir to :output-dir." @@ -197,14 +210,23 @@ (defn print-mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace print the ClojureScript stacktrace. The canonical stacktrace must be - a vector of {:file :function :line :column } - maps." + in the form: + + [{:file + :function + :line + :column }*] + + :file must be a URL path (without protocol) relative to :output-dir." ([stacktrace] (print-mapped-stacktrace stacktrace nil)) ([stacktrace opts] (let [read-source-map' (memoize read-source-map) ns-info' (memoize ns-info)] (doseq [{:keys [function file line column] :as frame} stacktrace] - (let [[sm {:keys [ns source-file] :as ns-info}] ((juxt read-source-map' ns-info') file) + ;; need to convert file, a relative URL style path, to host-specific file + (let [file (io/file (URL. (.toURL (io/file (util/output-directory opts))) file)) + [sm {:keys [ns source-file] :as ns-info}] + ((juxt read-source-map' ns-info') file) [line' column'] (if ns-info (mapped-line-and-column sm line column) [line column]) @@ -234,10 +256,11 @@ (:ns (ns-info "samples/hello/out/hello/core.js")) (print-mapped-stacktrace - [{:file "samples/hello/out/hello/core.js" + [{:file "hello/core.js" :function "first" :line 2 - :column 1}]) + :column 1}] + {:output-dir "samples/hello/out"}) ) (defn- display-error @@ -248,8 +271,9 @@ (println (:value ret)) (when-let [st (:stacktrace ret)] (if (and (true? (:source-map opts)) - (vector? st)) - (print-mapped-stacktrace st opts) + (satisfies? IParseStacktrace repl-env)) + (print-mapped-stacktrace + (-parse-stacktrace repl-env st opts) opts) (println st)) (flush)))) From a14310d15484868a7c1db9328f39696c9cb664a1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Feb 2015 09:40:32 -0500 Subject: [PATCH 0623/4033] need to pass the entire original error value so that REPLs can easily do host target specific parsing --- src/clj/cljs/repl.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index cd19fee6b..5a5b1ded8 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -106,9 +106,10 @@ (-repl-options [_] nil)) (defprotocol IParseStacktrace - (-parse-stacktrace [repl-env stacktrace build-options] - "Given the original JavaScript stacktrace string and current compiler build - options, parse the stacktrace into the canonical form: + (-parse-stacktrace [repl-env stacktrace error build-options] + "Given the original JavaScript stacktrace string, the entire original error + value and current compiler build options, parse the stacktrace into the + canonical form: [{:file :function @@ -273,7 +274,7 @@ (if (and (true? (:source-map opts)) (satisfies? IParseStacktrace repl-env)) (print-mapped-stacktrace - (-parse-stacktrace repl-env st opts) opts) + (-parse-stacktrace repl-env st ret opts) opts) (println st)) (flush)))) From 0dec2a2a8b2e9f852721d909f260d07d9b2f44d2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Feb 2015 10:51:15 -0500 Subject: [PATCH 0624/4033] util.print has been deprecated in Node.js v0.12. Switch to console.log in Node.js REPLs. Fix cljs.core loading twice issues in Node.js REPLs. enable-util-print! now identical to enable-console-print! but preserved for backwards compatibility. --- src/clj/cljs/repl/node.clj | 17 +++++++++-------- src/cljs/cljs/nodejs.cljs | 6 ++++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 5e8ff266b..72769b0ce 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -152,16 +152,17 @@ (repl/evaluate-form repl-env env "" '(do (.require js/goog "cljs.core") - (set! *print-fn* (.-print (js/require "util"))))) + (enable-console-print!))) ;; redef goog.require to track loaded libs (repl/evaluate-form repl-env env "" - '(set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name)))))) - ))) + '(do + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name)))))))))) (defrecord NodeEnv [host port socket proc] repl/IReplEnvOptions diff --git a/src/cljs/cljs/nodejs.cljs b/src/cljs/cljs/nodejs.cljs index 9da4bfa19..d2be0bbe6 100644 --- a/src/cljs/cljs/nodejs.cljs +++ b/src/cljs/cljs/nodejs.cljs @@ -15,6 +15,8 @@ (def require (js* "require")) (def process (js* "process")) -; Have ClojureScript print using Node's sys.print function (defn enable-util-print! [] - (set! *print-fn* (.-print (require "util")))) + (set! *print-newline* false) + (set! *print-fn* + (fn [& args] + (.apply (.-log js/console) js/console (into-array args))))) From b5d64365263c79a52433e4fbd65884b49a350f9b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Feb 2015 10:51:38 -0500 Subject: [PATCH 0625/4033] update .iml --- Clojurescript.iml | 1 + 1 file changed, 1 insertion(+) diff --git a/Clojurescript.iml b/Clojurescript.iml index 5fac6a1c2..62f1f9779 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -9,6 +9,7 @@ + From edea85ea98a7f48e95763dc40872da711f4cd60b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Feb 2015 10:59:11 -0500 Subject: [PATCH 0626/4033] document async testing --- src/cljs/cljs/test.cljs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 986c54c6e..08fd8a198 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -101,6 +101,20 @@ \"(arithmetic addition)\", in failure reports. You can use nested tests to set up a context shared by several tests. + DEFINING ASYNC TESTS + + (deftest addition + (async done + (is (= 4 (+ 2 2))) + (is (= 7 (+ 3 4))) + (done))) + + Async tests are constructed with the async macro. The first argument to + the macro is the test completion callback. The body of the async macro may + be any series of expressions. The completion callback must be invoked when + all assertions have run. There is no support for asynchronous coordination - + core.async is recommended for this. Note the body of the async test must be + truly asynchronous to avoid stack overflow. RUNNING TESTS From cc88e80a6ab9fc6d853b1b9e6a59b1d5d4989cb0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 11 Feb 2015 13:17:47 -0500 Subject: [PATCH 0627/4033] fix missed Rhino REPL regression, the surrounding REPL infrastructure creates cljs.user for us --- src/clj/cljs/repl/rhino.clj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index a1381b909..34e7a279e 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -121,8 +121,6 @@ (ScriptableObject/putProperty scope "__repl_opts" (Context/javaToJS opts scope)) (repl/load-file repl-env "cljs/core.cljs" opts) - (repl/evaluate-form repl-env env "" - '(ns cljs.user)) (ScriptableObject/putProperty scope "out" (Context/javaToJS *out* scope)) (binding [ana/*cljs-ns* 'cljs.core] From 37811b97e61b3ca851b05af694ff65e74f00d178 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 11 Feb 2015 15:44:33 -0500 Subject: [PATCH 0628/4033] add some missing source-map customization flags to optimized builds --- src/clj/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 85fee7762..d818b325a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -663,8 +663,10 @@ should contain the source for the given namespace name." :lines (+ (:lineCount sm-json) preamble-line-count 2) :file (:file sm-json) :output-dir (util/output-directory opts) - :source-map-path (:source-map-path 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})))))) source) (report-failure result)))) From 33db093f53fa6f85d47e8557bc49d8540185087c Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 11 Feb 2015 17:46:32 -0500 Subject: [PATCH 0629/4033] if the value returned from -parse-stacktrace is not a vector print the original string --- src/clj/cljs/repl.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5a5b1ded8..6a661216d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -273,8 +273,10 @@ (when-let [st (:stacktrace ret)] (if (and (true? (:source-map opts)) (satisfies? IParseStacktrace repl-env)) - (print-mapped-stacktrace - (-parse-stacktrace repl-env st ret opts) opts) + (let [cst (-parse-stacktrace repl-env st ret opts)] + (if (vector? cst) + (print-mapped-stacktrace cst opts) + (println st))) (println st)) (flush)))) From d77cf18f2a14aa1a89a3074b21294afd293a746b Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 11 Feb 2015 19:00:49 -0500 Subject: [PATCH 0630/4033] we also need to pass relevant source map options in the incremental compile case --- src/clj/cljs/compiler.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 7866c4219..ac9956079 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -972,7 +972,9 @@ (spit sm-file (sm/encode {(url-path src) (:source-map sm-data)} {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest)})))) + :file (url-path dest) + :source-map-timestamp (:source-map-timestamp opts) + :source-map-pretty-print (:source-map-pretty-print opts)})))) (let [path (.getPath (.toURL ^File dest))] (swap! env/*compiler* assoc-in [::compiled-cljs path] ret) (swap! env/*compiler* assoc-in [::ana/analyzed-cljs path] true)) From 8271f93ca7b1a73baa871886ff3abc63be85dd6a Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 11 Feb 2015 19:19:46 -0500 Subject: [PATCH 0631/4033] provide data oriented stacktrace mapping api --- src/clj/cljs/repl.clj | 62 ++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 6a661216d..0ebcd952b 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -208,9 +208,9 @@ [:line :col]))) default))) -(defn print-mapped-stacktrace +(defn mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace - print the ClojureScript stacktrace. The canonical stacktrace must be + return the ClojureScript stacktrace. The canonical stacktrace must be in the form: [{:file @@ -219,28 +219,41 @@ :column }*] :file must be a URL path (without protocol) relative to :output-dir." - ([stacktrace] (print-mapped-stacktrace stacktrace nil)) + ([stacktrace] (mapped-stacktrace stacktrace nil)) ([stacktrace opts] (let [read-source-map' (memoize read-source-map) ns-info' (memoize ns-info)] - (doseq [{:keys [function file line column] :as frame} stacktrace] - ;; need to convert file, a relative URL style path, to host-specific file - (let [file (io/file (URL. (.toURL (io/file (util/output-directory opts))) file)) - [sm {:keys [ns source-file] :as ns-info}] - ((juxt read-source-map' ns-info') file) - [line' column'] (if ns-info - (mapped-line-and-column sm line column) - [line column]) - name' (if (and ns-info function) - (symbol (name ns) (cljrepl/demunge function)) - function) - file' (string/replace - (.getCanonicalFile - (if ns-info - source-file - (io/file file))) - (str (System/getProperty "user.dir") File/separator) "")] - (println "\t" (str name' " (" file' ":" line' ":" column' ")"))))))) + (vec + (for [{:keys [function file line column] :as frame} stacktrace] + ;; need to convert file, a relative URL style path, to host-specific file + (let [rfile (io/file (URL. (.toURL (io/file (util/output-directory opts))) file)) + [sm {:keys [ns source-file] :as ns-info}] + ((juxt read-source-map' ns-info') rfile) + [line' column'] (if ns-info + (mapped-line-and-column sm line column) + [line column]) + name' (if (and ns-info function) + (symbol (name ns) (cljrepl/demunge function)) + function) + file' (string/replace + (.getCanonicalFile + (if ns-info + source-file + (io/file rfile))) + (str (System/getProperty "user.dir") File/separator) "")] + {:function name' + :file file' + :line line' + :column column'})))))) + +(defn print-mapped-stacktrace + "Given a vector representing the canonicalized JavaScript stacktrace + print the ClojureScript stacktrace. See mapped-stacktrace." + ([stacktrace] (print-mapped-stacktrace stacktrace nil)) + ([stacktrace opts] + (doseq [{:keys [function file line column]} + (mapped-stacktrace stacktrace opts)] + (println "\t" (str function " (" file ":" line ":" column ")"))))) (comment (cljsc/build "samples/hello/src" @@ -256,6 +269,13 @@ ;; hello.core (:ns (ns-info "samples/hello/out/hello/core.js")) + (mapped-stacktrace + [{:file "hello/core.js" + :function "first" + :line 2 + :column 1}] + {:output-dir "samples/hello/out"}) + (print-mapped-stacktrace [{:file "hello/core.js" :function "first" From 4069c35f12d6c2b8436c636d33142a04fa8546cf Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 11 Feb 2015 19:30:10 -0500 Subject: [PATCH 0632/4033] support custom mapped stacktrace printing --- src/clj/cljs/repl.clj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 0ebcd952b..9413672d3 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -118,6 +118,11 @@ :file must be a URL path (without protocol) relative to :output-dir.")) +(defprotocol IPrintStacktrace + (-print-stacktrace [repl-env stacktrace error build-options] + "Implementing REPL evaluation environments are given the opportunity to + print the mapped stacktrace themselves. This permits further processing.")) + (defn- env->opts "Returns a hash-map containing all of the entries in [repl-env], translating :working-dir to :output-dir." @@ -295,7 +300,9 @@ (satisfies? IParseStacktrace repl-env)) (let [cst (-parse-stacktrace repl-env st ret opts)] (if (vector? cst) - (print-mapped-stacktrace cst opts) + (if (satisfies? IPrintStacktrace repl-env) + (-print-stacktrace repl-env cst ret opts) + (print-mapped-stacktrace cst opts)) (println st))) (println st)) (flush)))) From 8a3e3ebe59ea3bde85967a97e2ffb8df63c50bb6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 11 Feb 2015 20:13:12 -0500 Subject: [PATCH 0633/4033] include :url entries to original sources in mapped stacktraces if it can be determined from the classpath --- src/clj/cljs/repl.clj | 48 +++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 9413672d3..5f41ca190 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -223,7 +223,9 @@ :line :column }*] - :file must be a URL path (without protocol) relative to :output-dir." + :file must be a URL path (without protocol) relative to :output-dir. The + returned mapped stacktrace will also contain :url entries to the original + sources if it can be determined from the classpath." ([stacktrace] (mapped-stacktrace stacktrace nil)) ([stacktrace opts] (let [read-source-map' (memoize read-source-map) @@ -245,11 +247,16 @@ (if ns-info source-file (io/file rfile))) - (str (System/getProperty "user.dir") File/separator) "")] - {:function name' - :file file' - :line line' - :column column'})))))) + (str (System/getProperty "user.dir") File/separator) "") + url (or (and ns-info (io/resource (util/ns->relpath ns))) + (io/resource file))] + (merge + {:function name' + :file (io/file file') + :line line' + :column column'} + (when url + {:url url})))))))) (defn print-mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace @@ -267,13 +274,6 @@ :output-to "samples/hello/out/hello.js" :source-map true}) - ;; line 2 column 1 - (mapped-line-and-column - (read-source-map "samples/hello/out/hello/core.js") 2 1) - - ;; hello.core - (:ns (ns-info "samples/hello/out/hello/core.js")) - (mapped-stacktrace [{:file "hello/core.js" :function "first" @@ -287,6 +287,28 @@ :line 2 :column 1}] {:output-dir "samples/hello/out"}) + + ;; URL example + + (cljsc/build "samples/hello/src" + {:optimizations :none + :output-dir "out" + :output-to "out/hello.js" + :source-map true}) + + (mapped-stacktrace + [{:file "cljs/core.js" + :function "first" + :line 2 + :column 1}] + {:output-dir "out"}) + + (print-mapped-stacktrace + [{:file "cljs/core.js" + :function "first" + :line 2 + :column 1}] + {:output-dir "out"}) ) (defn- display-error From d8fda31a2c35ddb08c4e10869089fcf33eb79062 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Feb 2015 22:06:49 -0500 Subject: [PATCH 0634/4033] require cljs.test macro ns in cljs.test to get macro inference goodness --- src/cljs/cljs/test.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 08fd8a198..ead14276a 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -241,7 +241,8 @@ For additional event types, see the examples in the code. "} cljs.test - (:require-macros [clojure.template :as temp]) + (:require-macros [clojure.template :as temp] + [cljs.test :as test]) (:require [clojure.string :as string])) ;; ============================================================================= From 0697a7495d55e61699bf0cf05befa48c56b14591 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Feb 2015 22:22:28 -0500 Subject: [PATCH 0635/4033] require cljs.repl macros in cljs.repl --- src/cljs/cljs/repl.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index b7a519b20..dab4f9b62 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -6,7 +6,8 @@ ;; the terms of this license. ;; You must not remove this notice, or any other, from this software. -(ns cljs.repl) +(ns cljs.repl + (:require-macros cljs.repl)) (defn print-doc [m] (println "-------------------------") From 8b30ecb2327d7942aae1485865fa9540f2eab9e1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Feb 2015 06:58:25 -0500 Subject: [PATCH 0636/4033] CLJS-1031: Get Closure Compiler over https in the bootstrap script --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index 6904146b8..f10d0a93c 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -66,7 +66,7 @@ cd .. echo "Fetching Google Closure compiler..." mkdir -p compiler cd compiler -curl --retry 3 -O -s http://dl.google.com/closure-compiler/compiler-$CLOSURE_RELEASE.zip || { echo "Download failed."; exit 1; } +curl --retry 3 -O -s https://dl.google.com/closure-compiler/compiler-$CLOSURE_RELEASE.zip || { echo "Download failed."; exit 1; } unzip -qu compiler-$CLOSURE_RELEASE.zip echo "Cleaning up Google Closure compiler archive..." rm compiler-$CLOSURE_RELEASE.zip From a0d69a31e371af798647ba9e581831ceb6ea09cc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Feb 2015 08:22:10 -0500 Subject: [PATCH 0637/4033] CLJS-967: "java.net.ConnectException: Connection refused" when running node repl Previously we had an arbitrary 300ms Thread/sleep. Problematic for slower machines. Now instead loop attempting to create the socket and read the "ready" message. --- src/clj/cljs/repl/node.clj | 14 +++++++++----- src/clj/cljs/repl/node_repl.js | 3 +++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 72769b0ce..da4587d3a 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -102,12 +102,16 @@ root (.substring path 0 (+ (.indexOf path fc) (count fc))) root-path (vec (cons root cs)) rewrite-path (conj root-path "goog")] - ;; TODO: temporary hack, should wait till we can read the start string - ;; from the process - David - (Thread/sleep 300) - (reset! (:socket repl-env) - (socket (:host repl-env) (:port repl-env))) (reset! (:proc repl-env) proc) + (loop [r nil] + (when-not (= r "ready") + (Thread/sleep 50) + (try + (reset! (:socket repl-env) (socket (:host repl-env) (:port repl-env))) + (catch Exception e)) + (if @(:socket repl-env) + (recur (read-response (:in @(:socket repl-env)))) + (recur nil)))) ;; compile cljs.core & its dependencies, goog/base.js must be available ;; for bootstrap to load, use new closure/compile as it can handle ;; resources in JARs diff --git a/src/clj/cljs/repl/node_repl.js b/src/clj/cljs/repl/node_repl.js index c49860bc9..311bef27a 100644 --- a/src/clj/cljs/repl/node_repl.js +++ b/src/clj/cljs/repl/node_repl.js @@ -15,6 +15,9 @@ net.createServer(function (socket) { ret = null, err = null; + socket.write("ready"); + socket.write("\0"); + socket.setEncoding("utf8"); dom.on("error", function(ue) { From d953cd3779aa5614b419c0c2fe1410fac45a2941 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Feb 2015 08:48:30 -0500 Subject: [PATCH 0638/4033] CLJS-1032: Node.js target should support :main Add :node-js target handling in `cljs.closure/output-main-file`. --- src/clj/cljs/closure.clj | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index d818b325a..4b6e98318 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -772,11 +772,23 @@ should contain the source for the given namespace name." (defn output-main-file [opts] (let [asset-path (or (:asset-path opts) (util/output-directory opts))] - (output-one-file opts - (str "if(typeof goog == \"undefined\") document.write('');" - "document.write('');" - "document.write('');")))) + (case (:target opts) + :nodejs + (output-one-file opts + (str "var path = require(\"path\");\n" + "try {\n" + " require(\"source-map-support\").install();\n" + "} catch(err) {\n" + "}\n" + "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" + "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" + "goog.require(\"" (comp/munge (:main opts)) "\");\n" + "goog.require(\"cljs.nodejscli\");\n")) + (output-one-file opts + (str "if(typeof goog == \"undefined\") document.write('');\n" + "document.write('');\n" + "document.write('');\n"))))) (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, @@ -875,7 +887,7 @@ should contain the source for the given namespace name." main (:main opts)] (util/mkdirs goog-deps) (spit goog-deps (slurp (io/resource "goog/deps.js"))) - (if (and main (not= (:target opts) :nodejs)) + (if main (do (output-deps-file (assoc opts :output-to From e8ae2c8a9c3f2429555e136cc17c1669be5e993a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Feb 2015 10:10:00 -0500 Subject: [PATCH 0639/4033] cljs.nodejscli ns needs to set `goog.global` when `COMPILED` is true, this fixes the fundamental issues for ASYNC-110 --- src/cljs/cljs/nodejs_externs.js | 5 +++-- src/cljs/cljs/nodejscli.cljs | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/nodejs_externs.js b/src/cljs/cljs/nodejs_externs.js index 708db372b..2dda10055 100644 --- a/src/cljs/cljs/nodejs_externs.js +++ b/src/cljs/cljs/nodejs_externs.js @@ -1,2 +1,3 @@ -function require(){} -function process(){} +var global = {}; +function require(){}; +function process(){}; diff --git a/src/cljs/cljs/nodejscli.cljs b/src/cljs/cljs/nodejscli.cljs index 404336f3d..4c3f404d6 100644 --- a/src/cljs/cljs/nodejscli.cljs +++ b/src/cljs/cljs/nodejscli.cljs @@ -12,7 +12,11 @@ (ns cljs.nodejscli (:require [cljs.nodejs :as nodejs])) -; Call the user's main function +;; need to set js/goog.global if COMPILED +(when ^boolean js/COMPILED + (set! js/goog.global js/global)) + +;; Call the user's main function (if (or (nil? cljs.core/*main-cli-fn*) (not (fn? cljs.core/*main-cli-fn*))) (throw (js/Error. "cljs.core/*main-cli-fn* not set")) From 43d0bb1e99aa2f23f1e7cf5004d347b6e6a70ff0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 12 Feb 2015 11:41:20 -0500 Subject: [PATCH 0640/4033] CLJS-887: browser repl should serve CSS --- src/clj/cljs/repl/browser.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 71f0999a8..1bea838b6 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -111,7 +111,8 @@ (.endsWith path ".js") (.endsWith path ".cljs") (.endsWith path ".map") - (.endsWith path ".html"))) + (.endsWith path ".html") + (.endsWith path ".css"))) send-static) (defmulti handle-post (fn [m _ _ ] (:type m))) From 165f38e55da04cfaa4a75e3e9f37c3757336ae0b Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 12 Feb 2015 11:59:32 -0500 Subject: [PATCH 0641/4033] 0.0-2843 --- README.md | 6 +++--- changes.md | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2b79e89af..55f49dd91 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2816 +Latest stable release: 0.0-2843 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2816"] +[org.clojure/clojurescript "0.0-2843"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2816 org.clojure clojurescript - 0.0-2816 + 0.0-2843 ``` diff --git a/changes.md b/changes.md index a488095fb..23afd705e 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,28 @@ +## 0.0-2843 + +### Enhancements +* CLJS-1032: Node.js target should support :main +* require cljs.test macro ns in cljs.test to get macro inference goodness +* include :url entries to original sources in mapped stacktraces if it can be determined from the classpath +* support custom mapped stacktrace printing +* provide data oriented stacktrace mapping api +* CLJS-1025: make REPL source mapping infrastructure generic +* CLJS-1010: Printing hook for cljs-devtools +* CLJS-1016: make "..." marker configurable + +### Changes +* CLJS-887: browser repl should serve CSS +* CLJS-1031: Get Closure Compiler over https in the bootstrap script + +### Fixes +* cljs.nodejscli ns needs to set `goog.global` when `COMPILED` is true, this fixes the fundamental issues for ASYNC-110 +* CLJS-967: "java.net.ConnectException: Connection refused" when running node repl +* pass relevant source map options in the incremental compile case +* add some missing source-map customization flags to optimized builds +* fix missed Rhino REPL regression, the surrounding REPL infrastructure creates cljs.user for us +* util.print has been deprecated in Node.js v0.12. Switch to console.log in Node.js REPLs. +* change `cljs.closure/watch` so it correctly watches all subdirectories do not recompile unless changed path is a file with .cljs or .js extension + ## 0.0-2816 ### Fixes From e1553c2e9411e56bdfccd3d322dc3b21cd266d6e Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 12 Feb 2015 17:58:17 -0500 Subject: [PATCH 0642/4033] import Closure JSModule & JSModuleGraph into cljs.closure --- src/clj/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 4b6e98318..77a3800dd 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -51,7 +51,8 @@ CompilerOptions$LanguageMode SourceMap$Format SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups - CommandLineRunner AnonymousFunctionNamingPolicy] + CommandLineRunner AnonymousFunctionNamingPolicy + JSModule JSModuleGraph] [java.security MessageDigest] [javax.xml.bind DatatypeConverter] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey From 20307e0c56b6c5203e97b91f036fb20b2e7918f1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Feb 2015 09:43:28 -0500 Subject: [PATCH 0643/4033] clarify `cljs.analyzer/parse-ns` docstring add `cljs.analyzer/analyze-file` docstring --- src/clj/cljs/analyzer.clj | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 4494f5925..c5d7060a4 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1804,11 +1804,15 @@ argument, which the reader will use in any emitted errors." (forms-seq*))))) (defn parse-ns - "Helper for parsing only the ns information from a ClojureScript source - file. By default does not load macros or perform any analysis of - dependencies. If opts parameter provided :analyze-deps and :load-macros keys - their values will be used for *analyze-deps* and *load-macros* bindings - respectively." + "Helper for parsing only the essential namespace information from a + ClojureScript source file and returning a cljs.closure/IJavaScript compatible + map _not_ a namespace AST node. + + By default does not load macros or perform any analysis of dependencies. If + opts parameter provided :analyze-deps and :load-macros keys their values will + be used for *analyze-deps* and *load-macros* bindings respectively. This + function does _not_ side-effect the ambient compilation environment unless + requested via opts where :restore is false." ([src] (parse-ns src nil nil)) ([src opts] (parse-ns src nil opts)) ([src dest opts] @@ -1895,6 +1899,14 @@ argument, which the reader will use in any emitted errors." (pr-str (get-in @env/*compiler* [::namespaces ns]))))) (defn analyze-file + "Given a java.io.File, java.net.URL or a string identifying a resource on the + classpath attempt to analyze it. + + This function side-effects the ambient compilation environment + `cljs.env/*compiler*` to aggregate analysis information. opts argument is + compiler options, if :cache-analysis true will cache analysis to + \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a + meaningful value." ([f] (analyze-file f nil)) ([f {:keys [output-dir] :as opts}] (let [res (cond From 606ccce775706b9f9f6f40ecdcc40d6947226824 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Feb 2015 15:50:53 -0500 Subject: [PATCH 0644/4033] CLJS-1035: REPLs should support watch recompilation Add :watch "path/to/src/to/watch" support to all REPLs, create a watch.log file at the root of :output-dir which users can tail. --- src/clj/cljs/repl.clj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5f41ca190..c26d9b693 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -22,7 +22,7 @@ [cljs.tagged-literals :as tags] [cljs.closure :as cljsc] [cljs.source-map :as sm]) - (:import [java.io File PushbackReader] + (:import [java.io File PushbackReader FileWriter] [java.net URL] [javax.xml.bind DatatypeConverter])) @@ -562,6 +562,12 @@ (:require [cljs.repl :refer-macros [doc]])) {:line 1 :column 1}) identity opts) + (when-let [src (:watch opts)] + (future + (let [log-file (io/file (util/output-directory opts) "watch.log")] + (println "Watch compilation log available at:" (str log-file)) + (binding [*out* (FileWriter. log-file)] + (cljsc/watch src (dissoc opts :watch)))))) (loop [] ;; try to let things flush before printing prompt (Thread/sleep 10) From 1a67860fa81488ee4692b53dcd3a8de46afa21d3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Feb 2015 16:21:17 -0500 Subject: [PATCH 0645/4033] CLJS-1037: cls.analyzer/ns-dependents fails for common cases need to update current depth not just replace, ah mutation --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index c5d7060a4..af5b9ae04 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -447,7 +447,7 @@ (fn [[_ ns-info]] (contains? (:requires ns-info) ns)) (get @env/*compiler* ::namespaces))))] - (swap! state assoc depth deps) + (swap! state update-in [depth] (fnil into #{}) deps) (doseq [dep deps] (ns-dependents dep (inc depth) state)) (doseq [[ Date: Fri, 13 Feb 2015 15:48:43 -0500 Subject: [PATCH 0646/4033] CLJS-1037: cls.analyzer/ns-dependents fails for common cases Showing failing test case --- test/clj/cljs/analyzer_tests.clj | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index 8013e993f..2cffc9010 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -287,3 +287,31 @@ (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble {:foo baz})))) {:foo 'baz})))) + +(def test-deps-env (atom + {:cljs.analyzer/namespaces + {'file-reloading {:requires {'utils 'utils}} + 'core {:requires {'file-reloading 'file-reloading}} + 'dev {:requires {'client 'client + 'core 'core}} + 'client {:requires {'utils 'utils}} + 'utils {:requires {}}}})) + +(deftest test-ns-dependents + (binding [env/*compiler* test-deps-env] + (is (= (set (a/ns-dependents 'client)) + #{'dev})) + (is (= (set (a/ns-dependents 'core)) + #{'dev})) + (is (= (set (a/ns-dependents 'dev)) + #{})) + + ;; if 'file-reloading returns 'core + (is (= (set (a/ns-dependents 'file-reloading)) + #{'dev 'core})) + + ;; how can 'utils include 'file-reloading but not 'core + ;; FAILS with + ;; actual: (not (= #{file-reloading dev client} #{file-reloading dev client core})) + (is (= (set (a/ns-dependents 'utils)) + #{'file-reloading 'dev 'client 'core})))) From 66949c3ed89d22603718390497afef17b7f86e76 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Feb 2015 16:38:15 -0500 Subject: [PATCH 0647/4033] memoization for `cljs.analyzer/ns-dependents` --- src/clj/cljs/analyzer.clj | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index af5b9ae04..ae7eef857 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -439,17 +439,20 @@ (warning :dynamic env {:ev ev :name (:name ev)}))))) (defn ns-dependents - ([ns] (ns-dependents ns 0 (atom (sorted-map)))) - ([ns depth state] - (let [deps (set + ([ns] + (letfn [(get-deps [ns] + (set (map first (filter (fn [[_ ns-info]] (contains? (:requires ns-info) ns)) - (get @env/*compiler* ::namespaces))))] + (get @env/*compiler* ::namespaces)))))] + (ns-dependents ns 0 (atom (sorted-map)) (memoize get-deps)))) + ([ns depth state memo-get-deps] + (let [deps (memo-get-deps ns)] (swap! state update-in [depth] (fnil into #{}) deps) (doseq [dep deps] - (ns-dependents dep (inc depth) state)) + (ns-dependents dep (inc depth) state memo-get-deps)) (doseq [[ Date: Fri, 13 Feb 2015 17:20:23 -0500 Subject: [PATCH 0648/4033] 0.0-2850 --- README.md | 6 +++--- changes.md | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 55f49dd91..50d56d682 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2843 +Latest stable release: 0.0-2850 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2843"] +[org.clojure/clojurescript "0.0-2850"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2843 org.clojure clojurescript - 0.0-2843 + 0.0-2850 ``` diff --git a/changes.md b/changes.md index 23afd705e..b3a52c83b 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,11 @@ +## 0.0-2850 + +### Enhancements +* CLJS-1035: REPLs should support watch recompilation + +### Fixes +* CLJS-1037: cls.analyzer/ns-dependents fails for common cases + ## 0.0-2843 ### Enhancements From 3e4b083327a28cccdba843ab75f7ab7fbb421106 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Feb 2015 18:33:28 -0500 Subject: [PATCH 0649/4033] change `cljs.analyzer/parse-ns` so that it also accepts a symbol --- src/clj/cljs/analyzer.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index ae7eef857..f2ec5a1da 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1820,7 +1820,10 @@ argument, which the reader will use in any emitted errors." ([src opts] (parse-ns src nil opts)) ([src dest opts] (env/ensure - (let [namespaces' (::namespaces @env/*compiler*) + (let [src (if (symbol? src) + (io/resource (util/ns->relpath src)) + src) + namespaces' (::namespaces @env/*compiler*) ret (binding [*cljs-ns* 'cljs.user *macro-infer* From c98b19acc5e56936bcab452cfdd8ad99c836577b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Feb 2015 18:42:00 -0500 Subject: [PATCH 0650/4033] organize cljs.js-deps Java imports, add to-url case for java.net.URL --- src/clj/cljs/js_deps.clj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index 61aab9c13..b8e3ccb27 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -1,11 +1,9 @@ (ns cljs.js-deps (:require [clojure.java.io :as io] [clojure.string :as string]) - (:import java.io.File - java.net.URL - java.net.URLClassLoader - java.util.zip.ZipFile - java.util.zip.ZipEntry)) + (:import [java.io File] + [java.net URL URLClassLoader] + [java.util.zip ZipFile ZipEntry])) ; taken from pomegranate/dynapath ; https://github.com/tobias/dynapath/blob/master/src/dynapath/util.clj @@ -48,6 +46,8 @@ If no ClassLoader is provided, RT/baseLoader is assumed." (defmethod to-url File [^File f] (.toURL (.toURI f))) +(defmethod to-url URL [^URL url] url) + (defmethod to-url String [s] (to-url (io/file s))) (defn find-js-fs From ea2cc7f1989633d0003fe343e9fea37981c77698 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Feb 2015 19:02:29 -0500 Subject: [PATCH 0651/4033] include cljs.reader in hello sample for Closure Module testing purposes --- samples/hello/src/hello/core.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/hello/src/hello/core.cljs b/samples/hello/src/hello/core.cljs index 96928ef3c..67a479f3b 100644 --- a/samples/hello/src/hello/core.cljs +++ b/samples/hello/src/hello/core.cljs @@ -1,5 +1,6 @@ (ns hello.core - (:require [hello.foo.bar :as bar])) + (:require [hello.foo.bar :as bar] + [cljs.reader :as reader])) (defn ^{:export greet} greet [n] (str "Hello " n)) From f4f7de45311f71c00b740fd0f414de196450cc5a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Feb 2015 20:04:21 -0500 Subject: [PATCH 0652/4033] Closure Modules support wip --- src/clj/cljs/closure.clj | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 77a3800dd..143557198 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -603,6 +603,68 @@ should contain the source for the given namespace name." (when-let [url (deps/-url js)] (js-source-file (javascript-name url) (io/input-stream url)))) +;; TODOs +;; topo sort modules, so we can easily add dependency between modules + +(defn build-modules [sources opts] + (let [find-entry (fn [sources entry] + (let [entry (name (comp/munge entry))] + (some + (fn [source] + (when (some #{entry} (:provides source)) + source)) + sources))) + base-module (JSModule. "cljs_base") + [sources' modules] + (reduce + (fn [[sources modules] [name module-desc]] + (let [{:keys [entries output-to depends-on]} module-desc + js-module (JSModule. (clojure.core/name name)) + [sources' module-sources] + (reduce + (fn [[sources ret] entry-sym] + (if-let [entry (find-entry sources entry-sym)] + [(remove #{entry} sources) + (conj ret + (js-source-file (javascript-name entry) entry))] + (throw + (IllegalArgumentException. + (str "Could not find namespace " entry-sym))))) + [sources []] entries)] + (doseq [^SourceFile module-source module-sources] + (.add js-module module-source)) + [sources' (conj modules js-module)])) + [sources [base-module]] (:modules opts))] + ;; add anything left to base + (doseq [^SourceFile source sources'] + (.add base-module source)) + modules)) + +(comment + (build "samples/hello/src" + {:optimizations :none + :output-dir "out" + :output-to "out/hello.js" + :source-map true}) + + (build-modules + [(map->javascript-file + (ana/parse-ns 'cljs.core (io/file "out/cljs/core.js") nil)) + (map->javascript-file + (ana/parse-ns 'cljs.reader (io/file "out/cljs/reader.js") nil))] + {:optimizations :advanced + :output-dir "out" + :cache-analysis true + :modules + {:core + {:output-to "out/modules/core.js" + :entries '#{cljs.core}} + :landing + {:output-to "out/modules/reader.js" + :entries '#{cljs.reader} + :depends-on #{:core}}}}) + ) + (defn optimize "Use the Closure Compiler to optimize one or more JavaScript files." [opts & sources] From 62e4b349f966e6895dae5e0073b5df894a492c0d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Feb 2015 21:55:03 -0500 Subject: [PATCH 0653/4033] some REPLs may want to further configure compilation, -setup is the last chance they get to do this. add :merge-opts support for the return value of -setup, these will get merged into the original opts. --- src/clj/cljs/repl.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c26d9b693..88a085a98 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -554,8 +554,10 @@ is-special-fn? (set (keys special-fns)) request-prompt (Object.) request-exit (Object.) - read-error (Object.)] - (-setup repl-env opts) + read-error (Object.) + opts (if-let [merge-opts (:merge-opts (-setup repl-env opts))] + (merge opts merge-opts) + opts)] (evaluate-form repl-env env "" (with-meta '(ns cljs.user From 6019e4908b6643cf1e9dd2c403f9bfdf15ab7495 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Feb 2015 11:53:41 -0500 Subject: [PATCH 0654/4033] split out generic `cljs.util/topo-sort` from `cljs.analyzer/ns-dependents` --- src/clj/cljs/analyzer.clj | 25 ++++++++----------------- src/clj/cljs/util.clj | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index f2ec5a1da..d89dce6fd 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -440,23 +440,14 @@ (defn ns-dependents ([ns] - (letfn [(get-deps [ns] - (set - (map first - (filter - (fn [[_ ns-info]] - (contains? (:requires ns-info) ns)) - (get @env/*compiler* ::namespaces)))))] - (ns-dependents ns 0 (atom (sorted-map)) (memoize get-deps)))) - ([ns depth state memo-get-deps] - (let [deps (memo-get-deps ns)] - (swap! state update-in [depth] (fnil into #{}) deps) - (doseq [dep deps] - (ns-dependents dep (inc depth) state memo-get-deps)) - (doseq [[ Date: Sat, 14 Feb 2015 12:51:14 -0500 Subject: [PATCH 0655/4033] add `cljs.closure/add-cljs-base-module` helper --- src/clj/cljs/closure.clj | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 143557198..9ca506643 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -606,6 +606,31 @@ should contain the source for the given namespace name." ;; TODOs ;; topo sort modules, so we can easily add dependency between modules +(defn add-cljs-base-module + ([modules] (add-cljs-base-module modules nil)) + ([modules opts] + (reduce + (fn [modules module-name] + (update-in modules [module-name :depends-on] + (fnil conj #{}) :cljs-base)) + (assoc modules :cljs-base + {:output-to + (io/file + (util/output-directory opts) + "cljs_base.js")}) + (keys modules)))) + +(comment + (add-cljs-base-module + {:core + {:output-to "out/modules/core.js" + :entries '#{cljs.core}} + :landing + {:output-to "out/modules/reader.js" + :entries '#{cljs.reader} + :depends-on #{:core}}}) + ) + (defn build-modules [sources opts] (let [find-entry (fn [sources entry] (let [entry (name (comp/munge entry))] From 3bca28413a577d39d0c47ce0808dd88bc121741b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Feb 2015 12:52:16 -0500 Subject: [PATCH 0656/4033] remove comment --- src/clj/cljs/closure.clj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 9ca506643..f8e452ffd 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -603,9 +603,6 @@ should contain the source for the given namespace name." (when-let [url (deps/-url js)] (js-source-file (javascript-name url) (io/input-stream url)))) -;; TODOs -;; topo sort modules, so we can easily add dependency between modules - (defn add-cljs-base-module ([modules] (add-cljs-base-module modules nil)) ([modules opts] From 9f81231be183d8ee60dcfa3261d510d389ffb2d9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Feb 2015 13:08:58 -0500 Subject: [PATCH 0657/4033] add `cljs.closure/sort-modules` using `util/topo-sort` --- src/clj/cljs/closure.clj | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index f8e452ffd..e143d956f 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -628,6 +628,29 @@ should contain the source for the given namespace name." :depends-on #{:core}}}) ) +(defn sort-modules [modules-with-base] + (letfn [(get-deps [module] + (reduce + (fn [ret [name {:keys [depends-on] :as module-desc}]] + (cond-> ret + (contains? depends-on module) (conj name))) + [] modules-with-base))] + (vec (map (fn [module-name] + [module-name (module-name modules-with-base)]) + (into [:cljs-base] (util/topo-sort :cljs-base get-deps)))))) + +(comment + (sort-modules + (add-cljs-base-module + {:core + {:output-to "out/modules/core.js" + :entries '#{cljs.core}} + :landing + {:output-to "out/modules/reader.js" + :entries '#{cljs.reader} + :depends-on #{:core}}})) + ) + (defn build-modules [sources opts] (let [find-entry (fn [sources entry] (let [entry (name (comp/munge entry))] From cf9bf2675c648edddf47781b65435a6b33cd9ed0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Feb 2015 13:31:20 -0500 Subject: [PATCH 0658/4033] change `cljs.closure/build-modules` to return updated sorted module sequence using `cljs.closure/add-cljs-base-module` and `clj.closure/sort-modules` --- src/clj/cljs/closure.clj | 50 ++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index e143d956f..099b0120e 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -659,13 +659,14 @@ should contain the source for the given namespace name." (when (some #{entry} (:provides source)) source)) sources))) - base-module (JSModule. "cljs_base") [sources' modules] (reduce - (fn [[sources modules] [name module-desc]] + (fn [[sources ret] [name module-desc]] (let [{:keys [entries output-to depends-on]} module-desc js-module (JSModule. (clojure.core/name name)) [sources' module-sources] + ;; compute inputs for a closure module + ;; as well as sources difference (reduce (fn [[sources ret] entry-sym] (if-let [entry (find-entry sources entry-sym)] @@ -676,13 +677,17 @@ should contain the source for the given namespace name." (IllegalArgumentException. (str "Could not find namespace " entry-sym))))) [sources []] entries)] + ;; add inputs to module (doseq [^SourceFile module-source module-sources] (.add js-module module-source)) - [sources' (conj modules js-module)])) - [sources [base-module]] (:modules opts))] - ;; add anything left to base + [sources' + (conj ret + [name (assoc module-desc :closure-module js-module)])])) + [sources []] (sort-modules (add-cljs-base-module (:modules opts)))) + cljs-base-closure-module (get-in modules [:cljs-base :closure-module])] + ;; add anything left to :cljs-base module (doseq [^SourceFile source sources'] - (.add base-module source)) + (.add ^JSModule cljs-base-closure-module source)) modules)) (comment @@ -692,22 +697,23 @@ should contain the source for the given namespace name." :output-to "out/hello.js" :source-map true}) - (build-modules - [(map->javascript-file - (ana/parse-ns 'cljs.core (io/file "out/cljs/core.js") nil)) - (map->javascript-file - (ana/parse-ns 'cljs.reader (io/file "out/cljs/reader.js") nil))] - {:optimizations :advanced - :output-dir "out" - :cache-analysis true - :modules - {:core - {:output-to "out/modules/core.js" - :entries '#{cljs.core}} - :landing - {:output-to "out/modules/reader.js" - :entries '#{cljs.reader} - :depends-on #{:core}}}}) + (let [modules + (build-modules + [(map->javascript-file + (ana/parse-ns 'cljs.core (io/file "out/cljs/core.js") nil)) + (map->javascript-file + (ana/parse-ns 'cljs.reader (io/file "out/cljs/reader.js") nil))] + {:optimizations :advanced + :output-dir "out" + :cache-analysis true + :modules {:core + {:output-to "out/modules/core.js" + :entries '#{cljs.core}} + :landing + {:output-to "out/modules/reader.js" + :entries '#{cljs.reader} + :depends-on #{:core}}}})] + modules) ) (defn optimize From 1e8709db93d1db1a5194041543b4b8b063ed94b3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Feb 2015 14:19:12 -0500 Subject: [PATCH 0659/4033] lift out `cljs.closure/emit-optimized-source-map` this clarifies the generic logic of `cljs.closure/optimize`, prep work for Closure Module support --- src/clj/cljs/closure.clj | 109 +++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 099b0120e..96b3a8fc9 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -716,6 +716,62 @@ should contain the source for the given namespace name." modules) ) +(defn emit-optimized-source-map + [^com.google.javascript.jscomp.Compiler closure-compiler sources name opts] + (with-open [out (io/writer name)] + (.appendTo (.getSourceMap closure-compiler) out name)) + (let [preamble (make-preamble opts) + preamble-line-count (- (count (.split #"\r?\n" preamble -1)) 1) + sm-json (-> (io/file name) slurp + (json/read-str :key-fn keyword)) + closure-source-map (sm/decode-reverse sm-json)] + (loop [sources (seq sources) + relpaths {} + merged (sorted-map-by + (sm/source-compare + (remove nil? + (map (fn [source] + (if-let [^URL source-url (:source-url source)] + (.getPath source-url) + (if-let [^URL url (:url source)] + (.getPath url)))) + sources))))] + (if sources + (let [source (first sources)] + (recur + (next sources) + (let [{:keys [provides source-url]} source] + (if (and provides source-url) + (assoc relpaths + (.getPath ^URL source-url) + (util/ns->relpath (first provides))) + relpaths)) + (if-let [url (:url source)] + (let [path (.getPath ^URL url)] + (if-let [compiled (get-in @env/*compiler* [::comp/compiled-cljs path])] + (if-let [source-url (:source-url source)] + (assoc merged + (.getPath ^URL source-url) + (sm/merge-source-maps + (:source-map compiled) + (get closure-source-map path))) + merged) + (assoc merged path (get closure-source-map path)))) + merged))) + (spit + (io/file name) + (sm/encode merged + {:preamble-line-count (+ preamble-line-count + (or (:foreign-deps-line-count opts) 0)) + :lines (+ (:lineCount sm-json) preamble-line-count 2) + :file (:file sm-json) + :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})))))) + (defn optimize "Use the Closure Compiler to optimize one or more JavaScript files." [opts & sources] @@ -727,61 +783,12 @@ should contain the source for the given namespace name." sources) ^List inputs (map #(js-source-file (javascript-name %) %) sources) result ^Result (.compile closure-compiler externs inputs compiler-options) - preamble (make-preamble opts) - preamble-line-count (- (count (.split #"\r?\n" preamble -1)) 1)] + ] (if (.success result) ;; compiler.getSourceMap().reset() (let [source (.toSource closure-compiler)] (when-let [name (:source-map opts)] - (with-open [out (io/writer name)] - (.appendTo (.getSourceMap closure-compiler) out name)) - (let [sm-json (-> (io/file name) slurp - (json/read-str :key-fn keyword)) - closure-source-map (sm/decode-reverse sm-json)] - (loop [sources (seq sources) - relpaths {} - merged (sorted-map-by - (sm/source-compare - (remove nil? - (map (fn [source] - (if-let [^URL source-url (:source-url source)] - (.getPath source-url) - (if-let [^URL url (:url source)] - (.getPath url)))) - sources))))] - (if sources - (let [source (first sources)] - (recur - (next sources) - (let [{:keys [provides source-url]} source] - (if (and provides source-url) - (assoc relpaths (.getPath ^URL source-url) - (util/ns->relpath (first provides))) - relpaths)) - (if-let [url (:url source)] - (let [path (.getPath ^URL url)] - (if-let [compiled (get-in @env/*compiler* [::comp/compiled-cljs path])] - (if-let [source-url (:source-url source)] - (assoc merged (.getPath ^URL source-url) - (sm/merge-source-maps - (:source-map compiled) - (get closure-source-map path))) - merged) - (assoc merged path (get closure-source-map path)))) - merged))) - (spit - (io/file name) - (sm/encode merged - {:preamble-line-count (+ preamble-line-count - (or (:foreign-deps-line-count opts) 0)) - :lines (+ (:lineCount sm-json) preamble-line-count 2) - :file (:file sm-json) - :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})))))) + (emit-optimized-source-map closure-compiler sources name opts)) source) (report-failure result)))) From 9dbe4f4dd5cd1b60540d592c428dd297adad64fa Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Feb 2015 14:56:35 -0500 Subject: [PATCH 0660/4033] add module dependencies --- src/clj/cljs/closure.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 96b3a8fc9..9c10dec99 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -680,6 +680,11 @@ should contain the source for the given namespace name." ;; add inputs to module (doseq [^SourceFile module-source module-sources] (.add js-module module-source)) + (doseq [dep depends-on] + (if-let [parent-module (get-in (into {} ret) [dep :closure-module])] + (.addDependency js-module ^JSModule parent-module) + (throw (IllegalArgumentException. + (str "Parent module " dep " does not exist"))))) [sources' (conj ret [name (assoc module-desc :closure-module js-module)])])) From c84c6a6b3ce8b2846667bdc4c45d4968c48184d6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Feb 2015 14:57:36 -0500 Subject: [PATCH 0661/4033] formatting & comment --- src/clj/cljs/closure.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 9c10dec99..c7a371548 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -680,6 +680,8 @@ should contain the source for the given namespace name." ;; add inputs to module (doseq [^SourceFile module-source module-sources] (.add js-module module-source)) + ;; add module dependencies, will always work + ;; since modules are already in dependency order (doseq [dep depends-on] (if-let [parent-module (get-in (into {} ret) [dep :closure-module])] (.addDependency js-module ^JSModule parent-module) @@ -787,8 +789,7 @@ should contain the source for the given namespace name." (cons "var CLOSURE_NO_DEPS = true;" sources) sources) ^List inputs (map #(js-source-file (javascript-name %) %) sources) - result ^Result (.compile closure-compiler externs inputs compiler-options) - ] + result ^Result (.compile closure-compiler externs inputs compiler-options)] (if (.success result) ;; compiler.getSourceMap().reset() (let [source (.toSource closure-compiler)] From 664c8d54599d4e93a598423777bcb7f579099430 Mon Sep 17 00:00:00 2001 From: Pieter van Prooijen Date: Sun, 15 Feb 2015 15:52:07 +0100 Subject: [PATCH 0662/4033] CLJS-1040: Source-mapped script stack frames for the Nashorn repl Add the new source map support for stack frames to the Nashorn repl. It now also reports the actual javascript stackframes (when applicable) to exceptions which happen during repl evaluation. --- src/clj/cljs/repl/nashorn.clj | 49 ++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 966c3241e..e0932e7e1 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -9,12 +9,14 @@ (ns cljs.repl.nashorn (:require [clojure.java.io :as io] [clojure.string :as string] + [clojure.stacktrace] [cljs.analyzer :as ana] [cljs.env :as env] [cljs.repl :as repl] [cljs.compiler :as comp] [cljs.closure :as closure]) - (:import [javax.script ScriptEngine ScriptEngineManager])) + (:import [javax.script ScriptEngine ScriptEngineManager ScriptException] + [jdk.nashorn.api.scripting NashornException])) ;; Nashorn Clojurescript repl binding. ;; @@ -125,9 +127,18 @@ (eval-str engine (format "goog.require(\"%s\");" (comp/munge (first ns))))) -(defn- stacktrace [^Exception e] - (apply str - (interpose "\n" (map #(str " " (.toString %)) (.getStackTrace e))))) +;; Nashorn script stacktraces have a relative path which includes the output-dir +(defn- strip-file-name [^String file-name output-dir] + (let [with-slash (str output-dir "/")] + (if (.startsWith file-name with-slash) + (string/replace-first file-name with-slash "") + file-name))) + +(defn- convert-stacktrace-element [^StackTraceElement el] + {:function (.getMethodName el) + :file (.getFileName el) + :line (.getLineNumber el) + :column 0}) (def repl-filename "") @@ -144,30 +155,44 @@ (repl/evaluate-form this env repl-filename '(do (.require js/goog "cljs.core") - (set! cljs.core/*print-fn* js/print))))) + (cljs.core/enable-console-print!))))) (-evaluate [{engine :engine :as this} filename line js] (when debug (println "Evaluating: " js)) (try {:status :success :value (if-let [r (eval-str engine js)] (.toString r) "")} + + ;; Stringify the stacktrace to edn for easy parsing in -parse-stacktrace + (catch ScriptException e + (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] + {:status :exception + :value (.getMessage root-cause) + :stacktrace (pr-str (map convert-stacktrace-element + (NashornException/getScriptFrames root-cause)))})) (catch Throwable e - {:status :exception - :value (.toString e) - :stacktrace (stacktrace e)}))) + (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] + {:status :exception + :value (.getMessage root-cause) + :stacktrace (pr-str (map convert-stacktrace-element (.getStackTrace root-cause)))})))) (-load [{engine :engine :as this} ns url] (load-ns engine ns)) - (-tear-down [this])) + (-tear-down [this]) + repl/IParseStacktrace + (-parse-stacktrace [this frames-str ret {output-dir :output-dir}] + (when-let [frames (read-string frames-str)] + (vec (map #(update-in %1 [:file] (fn [s] (strip-file-name s output-dir))) frames))))) (defn repl-env "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript and as the - :repl-env argument to piggieback/cljs-repl. Opts has the following extra parameters: + :repl-env argument to piggieback/cljs-repl. Besides the usual repl options (e.g. :source-map), + opts has the following extra parameters: :output-dir the directory of the compiled files, e.g. \"resources/public/my-app\" (mandatory). :output-to load this file initially into Nashorn, relative to output-dir. Use a minimal bootstrapped cljs.core environment if not specified." - [& {debug :debug :as opts}] + [& {:keys [debug] :as opts}] (let [engine (create-engine) compiler-env (env/default-compiler-env)] (merge (NashornEnv. engine debug) {:cljs.env/compiler compiler-env} ; required by cider middleware ? - opts))) \ No newline at end of file + opts))) From 0872b3d322654b8468bdc0b7ed1798bff4b8a851 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Feb 2015 10:58:02 -0500 Subject: [PATCH 0663/4033] don't overwrite the original Closure Compiler optimized source map, just erases useful information --- src/clj/cljs/closure.clj | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c7a371548..c4bbf969d 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -725,11 +725,12 @@ should contain the source for the given namespace name." (defn emit-optimized-source-map [^com.google.javascript.jscomp.Compiler closure-compiler sources name opts] - (with-open [out (io/writer name)] - (.appendTo (.getSourceMap closure-compiler) out name)) - (let [preamble (make-preamble opts) + (let [name' (str name ".closure") + _ (with-open [out (io/writer name')] + (.appendTo (.getSourceMap closure-compiler) out name')) + preamble (make-preamble opts) preamble-line-count (- (count (.split #"\r?\n" preamble -1)) 1) - sm-json (-> (io/file name) slurp + sm-json (-> (io/file name') slurp (json/read-str :key-fn keyword)) closure-source-map (sm/decode-reverse sm-json)] (loop [sources (seq sources) @@ -771,7 +772,7 @@ should contain the source for the given namespace name." {:preamble-line-count (+ preamble-line-count (or (:foreign-deps-line-count opts) 0)) :lines (+ (:lineCount sm-json) preamble-line-count 2) - :file (:file sm-json) + :file name :output-dir (util/output-directory opts) :source-map (:source-map opts) :source-map-path (:source-map-path opts) From a5eaee9beaedc0d2238c36ea1b3dbc7b0562cf13 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Feb 2015 12:05:06 -0500 Subject: [PATCH 0664/4033] when adding inputs to base module need to convert to Closure SourceFile --- src/clj/cljs/closure.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c4bbf969d..4af76c6a1 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -693,8 +693,9 @@ should contain the source for the given namespace name." [sources []] (sort-modules (add-cljs-base-module (:modules opts)))) cljs-base-closure-module (get-in modules [:cljs-base :closure-module])] ;; add anything left to :cljs-base module - (doseq [^SourceFile source sources'] - (.add ^JSModule cljs-base-closure-module source)) + (doseq [source sources'] + (.add ^JSModule cljs-base-closure-module + (js-source-file (javascript-name source)))) modules)) (comment From 78439454312be8b621f978bf5cbc32e4862c324a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Feb 2015 12:22:16 -0500 Subject: [PATCH 0665/4033] module support wip --- src/clj/cljs/closure.clj | 75 ++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 4af76c6a1..04ec04278 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -651,7 +651,12 @@ should contain the source for the given namespace name." :depends-on #{:core}}})) ) -(defn build-modules [sources opts] +(defn build-modules + "Given a list of IJavaScript sources and compiler options return a _sequence_ + of module name and module description pair tuples. The tuples will be sorted + in module dependency order. The module descriptions will be augmented with + a :closure-module entry holding the Closure JSModule." + [sources opts] (let [find-entry (fn [sources entry] (let [entry (name (comp/munge entry))] (some @@ -691,11 +696,11 @@ should contain the source for the given namespace name." (conj ret [name (assoc module-desc :closure-module js-module)])])) [sources []] (sort-modules (add-cljs-base-module (:modules opts)))) - cljs-base-closure-module (get-in modules [:cljs-base :closure-module])] + cljs-base-closure-module (get-in (into {} modules) [:cljs-base :closure-module])] ;; add anything left to :cljs-base module (doseq [source sources'] (.add ^JSModule cljs-base-closure-module - (js-source-file (javascript-name source)))) + (js-source-file (javascript-name source) source))) modules)) (comment @@ -781,6 +786,27 @@ should contain the source for the given namespace name." :source-map-pretty-print (:source-map-pretty-print opts) :relpaths relpaths})))))) +(defn optimize-modules + "Use the Closure Compiler to optimize one or more Closure JSModules." + [opts & sources] + {:pre [(and (contains? opts :modules) + (not (contains? opts :output-to)))]} + (let [closure-compiler (make-closure-compiler) + ^List externs (load-externs opts) + compiler-options (make-options opts) + sources (if (= :whitespace (:optimizations opts)) + (cons "var CLOSURE_NO_DEPS = true;" sources) + sources) + modules (build-modules sources (:modules opts)) + ^List inputs (map (comp :closure-module second) modules) + ^Result result (.compileModules closure-compiler externs inputs compiler-options)] + (if (.success result) + (vec + (for [[name {:keys [closure-module] :as module}] modules] + [name + (assoc module :source (.toSource ^JSModule closure-module))])) + (report-failure result)))) + (defn optimize "Use the Closure Compiler to optimize one or more JavaScript files." [opts & sources] @@ -791,7 +817,7 @@ should contain the source for the given namespace name." (cons "var CLOSURE_NO_DEPS = true;" sources) sources) ^List inputs (map #(js-source-file (javascript-name %) %) sources) - result ^Result (.compile closure-compiler externs inputs compiler-options)] + ^Result result (.compile closure-compiler externs inputs compiler-options)] (if (.success result) ;; compiler.getSourceMap().reset() (let [source (.toSource closure-compiler)] @@ -919,6 +945,11 @@ should contain the source for the given namespace name." "document.write('');\n"))))) +(defn output-modules + ([opts modules] + (doseq [{:keys [output-to closure-module]} modules] + (spit (io/file output-to) (.toSource ^JSModule closure-module))))) + (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, or is a foreign lib, return the path relative to the output @@ -1240,18 +1271,22 @@ should contain the source for the given namespace name." (when-let [fname (:source-map all-opts)] (assert (string? fname) (str ":source-map must name a file when using :whitespace, " - ":simple, or :advanced optimizations")) + ":simple, or :advanced optimizations")) (doall (map #(source-on-disk all-opts %) js-sources))) - (->> - (util/measure compiler-stats - "Optimize sources" - (apply optimize all-opts - (remove foreign-source? js-sources))) - (add-wrapper all-opts) - (add-source-map-link all-opts) - (str fdeps-str) - (add-header all-opts) - (output-one-file all-opts))) + (if (:modules opts) + (->> + (apply optimize-modules all-opts js-sources) + (output-modules all-opts)) + (->> + (util/measure compiler-stats + "Optimize sources" + (apply optimize all-opts + (remove foreign-source? js-sources))) + (add-wrapper all-opts) + (add-source-map-link all-opts) + (str fdeps-str) + (add-header all-opts) + (output-one-file all-opts)))) (apply output-unoptimized all-opts js-sources))] ;; emit Node.js bootstrap script for :none & :whitespace optimizations (when (and (= (:target opts) :nodejs) @@ -1262,6 +1297,16 @@ should contain the source for the given namespace name." (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) ret)))))) +(comment + ;; testing modules + (build "samples/hello/src" + {:optimizations :advanced + :output-dir "samples/hello/out" + :modules + {:hello + {:output-to "samples/hello/out/hello.js"}}}) + ) + (defn watch "Given a source directory, produce runnable JavaScript. Watch the source directory for changes rebuliding when necessary. Takes the same arguments as From 4115f5e9d34b22e1dfc1ff6641340b9103d54a98 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Feb 2015 12:50:49 -0500 Subject: [PATCH 0666/4033] need to pass opts to get correct base module :output-to need closure compiler to generate source for module --- src/clj/cljs/closure.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 04ec04278..f424932b7 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -695,7 +695,7 @@ should contain the source for the given namespace name." [sources' (conj ret [name (assoc module-desc :closure-module js-module)])])) - [sources []] (sort-modules (add-cljs-base-module (:modules opts)))) + [sources []] (sort-modules (add-cljs-base-module (:modules opts) opts))) cljs-base-closure-module (get-in (into {} modules) [:cljs-base :closure-module])] ;; add anything left to :cljs-base module (doseq [source sources'] @@ -804,7 +804,7 @@ should contain the source for the given namespace name." (vec (for [[name {:keys [closure-module] :as module}] modules] [name - (assoc module :source (.toSource ^JSModule closure-module))])) + (assoc module :source (.toSource closure-compiler ^JSModule closure-module))])) (report-failure result)))) (defn optimize From 951fd6c370604402505bce26f3cd6d60c4bccd16 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Feb 2015 13:18:48 -0500 Subject: [PATCH 0667/4033] basic module compilation working --- src/clj/cljs/closure.clj | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index f424932b7..2f3555fdf 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -666,9 +666,10 @@ should contain the source for the given namespace name." sources))) [sources' modules] (reduce - (fn [[sources ret] [name module-desc]] - (let [{:keys [entries output-to depends-on]} module-desc - js-module (JSModule. (clojure.core/name name)) + (fn [[sources ret] [name {:keys [entries output-to depends-on] :as module-desc}]] + (assert (or (= name :cljs-base) (not (empty? entries))) + (str "Module " name " does not define any :entries")) + (let [js-module (JSModule. (clojure.core/name name)) [sources' module-sources] ;; compute inputs for a closure module ;; as well as sources difference @@ -797,7 +798,7 @@ should contain the source for the given namespace name." sources (if (= :whitespace (:optimizations opts)) (cons "var CLOSURE_NO_DEPS = true;" sources) sources) - modules (build-modules sources (:modules opts)) + modules (build-modules sources opts) ^List inputs (map (comp :closure-module second) modules) ^Result result (.compileModules closure-compiler externs inputs compiler-options)] (if (.success result) @@ -946,9 +947,17 @@ should contain the source for the given namespace name." "\"); } else { console.warn(\"ClojureScript could not load :main, did you forget to specify :asset-path?\"); };');\n"))))) (defn output-modules - ([opts modules] - (doseq [{:keys [output-to closure-module]} modules] - (spit (io/file output-to) (.toSource ^JSModule closure-module))))) + "Given compiler options and a sequence of module name and module description + tuples output module sources to disk. Modules description must define + :output-to and supply :source entry with the JavaScript source to write + to disk." + [opts modules] + (doseq [[name {:keys [output-to source]}] modules] + (assert (not (nil? output-to)) + (str "Module " name " does not define :output-to")) + (assert (not (nil? source)) + (str "Module " name " did not supply :source")) + (spit (io/file output-to) source))) (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, @@ -1273,7 +1282,7 @@ should contain the source for the given namespace name." (str ":source-map must name a file when using :whitespace, " ":simple, or :advanced optimizations")) (doall (map #(source-on-disk all-opts %) js-sources))) - (if (:modules opts) + (if (:modules all-opts) (->> (apply optimize-modules all-opts js-sources) (output-modules all-opts)) @@ -1304,7 +1313,8 @@ should contain the source for the given namespace name." :output-dir "samples/hello/out" :modules {:hello - {:output-to "samples/hello/out/hello.js"}}}) + {:output-to "samples/hello/out/hello.js" + :entries '#{hello.core}}}}) ) (defn watch From c5613a0ea7b4d187f8c5c25ac7cabb51d4549758 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Feb 2015 13:33:02 -0500 Subject: [PATCH 0668/4033] dependency sort module inputs --- src/clj/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 2f3555fdf..141996832 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -800,6 +800,8 @@ should contain the source for the given namespace name." sources) modules (build-modules sources opts) ^List inputs (map (comp :closure-module second) modules) + _ (doseq [^JSModule input inputs] + (.sortInputsByDeps input closure-compiler)) ^Result result (.compileModules closure-compiler externs inputs compiler-options)] (if (.success result) (vec @@ -1314,7 +1316,7 @@ should contain the source for the given namespace name." :modules {:hello {:output-to "samples/hello/out/hello.js" - :entries '#{hello.core}}}}) + :entries '#{cljs.reader hello.core}}}}) ) (defn watch From 8f27e2290049b4e97d670e3e899db74ba351825b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 08:06:03 -0500 Subject: [PATCH 0669/4033] relax :source-map + :output-to contract now that we have :modules reformat long strings --- src/clj/cljs/closure.clj | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 141996832..2a899c829 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1162,17 +1162,21 @@ should contain the source for the given namespace name." (not (= (:optimizations opts) :none))) (assert (and (contains? opts :output-to) (contains? opts :output-dir)) - ":source-map cannot be specied without also specifying :output-to and :output-dir if optimization setting applied") - (assert (string? source-map) - (format ":source-map %s must specify a file in the same directory as :output-to %s if optimization setting applied" + (str ":source-map cannot be specied without also specifying :output-to" + " and :output-dir if optimization setting applied")) + (assert (and (string? source-map) (nil? (:modules opts))) + (format (str ":source-map %s must specify a file in the same directory" + " as :output-to %s if optimization setting applied") (pr-str source-map) (pr-str output-to))) (assert (in-same-dir? source-map output-to) - (format ":source-map %s must specify a file in the same directory as :output-to %s if optimization setting applied" + (format (str ":source-map %s must specify a file in the same directory as " + ":output-to %s if optimization setting applied") (pr-str source-map) (pr-str output-to))) (assert (same-or-subdirectory-of? (absolute-parent output-to) output-dir) - (format ":output-dir %s must specify a directory in :output-to's parent %s if optimization setting applied" + (format (str ":output-dir %s must specify a directory in :output-to's" + " parent %s if optimization setting applied") (pr-str output-dir) (pr-str (absolute-parent output-to))))) true) @@ -1185,7 +1189,8 @@ should contain the source for the given namespace name." (when-not (= (:optimizations opts) :none) (assert (and (contains? opts :output-to) (contains? opts :source-map)) - ":source-map-path cannot be specified without also specifying :output-to and :source-map if optimization setting applied"))) + (str ":source-map-path cannot be specified without also specifying " + ":output-to and :source-map if optimization setting applied")))) true) (defn check-output-wrapper [{:keys [output-wrapper optimizations]}] From f6df8134ba14aec945113530524342daa58a8964 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 09:12:39 -0500 Subject: [PATCH 0670/4033] emit Closure source maps for modules, need to relax source map checking to only consider :output-to cases --- src/clj/cljs/closure.clj | 69 ++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 2a899c829..e79a85278 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -43,7 +43,7 @@ [clojure.java.io :as io] [clojure.string :as string] [clojure.data.json :as json]) - (:import [java.io File BufferedInputStream] + (:import [java.io File BufferedInputStream StringWriter] [java.net URL] [java.util.logging Level] [java.util List Random] @@ -52,7 +52,7 @@ SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy - JSModule JSModuleGraph] + JSModule JSModuleGraph SourceMap] [java.security MessageDigest] [javax.xml.bind DatatypeConverter] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey @@ -171,8 +171,15 @@ (if-let [extra-annotations (:closure-extra-annotations opts)] (. compiler-options (setExtraAnnotationNames (map name extra-annotations)))) (when (contains? opts :source-map) - (set! (.sourceMapOutputPath compiler-options) - (:source-map opts)) + (if (:modules opts) + ;; name is not actually used by Closur in :modules case, + ;; but we need to provide _something_ for Closure to not + ;; complain + (set! (.sourceMapOutputPath compiler-options) + (str (io/file (util/output-directory opts) + "cljs_modules.map"))) + (set! (.sourceMapOutputPath compiler-options) + (:source-map opts))) (set! (.sourceMapDetailLevel compiler-options) SourceMap$DetailLevel/ALL) (set! (.sourceMapFormat compiler-options) @@ -802,12 +809,27 @@ should contain the source for the given namespace name." ^List inputs (map (comp :closure-module second) modules) _ (doseq [^JSModule input inputs] (.sortInputsByDeps input closure-compiler)) - ^Result result (.compileModules closure-compiler externs inputs compiler-options)] + ^Result result (.compileModules closure-compiler externs inputs compiler-options) + ^SourceMap source-map (when (:source-map opts) + (.getSourceMap closure-compiler))] + (assert (or (nil? (:source-map opts)) source-map) + "Could not create source maps for modules") (if (.success result) (vec - (for [[name {:keys [closure-module] :as module}] modules] + (for [[name {:keys [output-to closure-module] :as module}] modules] [name - (assoc module :source (.toSource closure-compiler ^JSModule closure-module))])) + (merge + (assoc module + :source + (do + (when source-map (.reset source-map)) + (.toSource closure-compiler ^JSModule closure-module))) + (when source-map + (let [sw (StringWriter.) + source-map-name (str output-to ".map.closure")] + (.appendTo source-map sw source-map-name) + {:source-map-json (.toString sw) + :source-map-name source-map-name})))])) (report-failure result)))) (defn optimize @@ -954,12 +976,15 @@ should contain the source for the given namespace name." :output-to and supply :source entry with the JavaScript source to write to disk." [opts modules] - (doseq [[name {:keys [output-to source]}] modules] + (doseq [[name {:keys [output-to source] :as module-desc}] modules] (assert (not (nil? output-to)) (str "Module " name " does not define :output-to")) (assert (not (nil? source)) (str "Module " name " did not supply :source")) - (spit (io/file output-to) source))) + (spit (io/file output-to) source) + (when (:source-map opts) + (spit (io/file (:source-map-name module-desc)) + (:source-map-json module-desc))))) (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, @@ -1160,23 +1185,24 @@ should contain the source for the given namespace name." "When :source-map is specified in opts, " (when (and (contains? opts :source-map) (not (= (:optimizations opts) :none))) - (assert (and (contains? opts :output-to) + (assert (and (or (contains? opts :output-to) + (contains? opts :modules)) (contains? opts :output-dir)) - (str ":source-map cannot be specied without also specifying :output-to" - " and :output-dir if optimization setting applied")) - (assert (and (string? source-map) (nil? (:modules opts))) - (format (str ":source-map %s must specify a file in the same directory" - " as :output-to %s if optimization setting applied") + (str ":source-map cannot be specied without also specifying :output-dir " + "and either :output-to or :modules if optimization setting applied")) + (assert (or (nil? (:output-to opts)) (string? source-map)) + (format (str ":source-map %s must specify a file in the same directory " + "as :output-to %s if optimization setting applied") (pr-str source-map) (pr-str output-to))) - (assert (in-same-dir? source-map output-to) + (assert (or (nil? (:output-to opts)) (in-same-dir? source-map output-to)) (format (str ":source-map %s must specify a file in the same directory as " ":output-to %s if optimization setting applied") (pr-str source-map) (pr-str output-to))) - (assert (same-or-subdirectory-of? (absolute-parent output-to) output-dir) - (format (str ":output-dir %s must specify a directory in :output-to's" - " parent %s if optimization setting applied") + (assert (or (nil? (:output-to opts)) (same-or-subdirectory-of? (absolute-parent output-to) output-dir)) + (format (str ":output-dir %s must specify a directory in :output-to's " + "parent %s if optimization setting applied") (pr-str output-dir) (pr-str (absolute-parent output-to))))) true) @@ -1285,9 +1311,9 @@ should contain the source for the given namespace name." :foreign-deps-line-count (- (count (.split #"\r?\n" fdeps-str -1)) 1))] (when-let [fname (:source-map all-opts)] - (assert (string? fname) + (assert (or (nil? (:output-to all-opts)) (string? fname)) (str ":source-map must name a file when using :whitespace, " - ":simple, or :advanced optimizations")) + ":simple, or :advanced optimizations with :output-to")) (doall (map #(source-on-disk all-opts %) js-sources))) (if (:modules all-opts) (->> @@ -1318,6 +1344,7 @@ should contain the source for the given namespace name." (build "samples/hello/src" {:optimizations :advanced :output-dir "samples/hello/out" + :source-map true :modules {:hello {:output-to "samples/hello/out/hello.js" From 49558800925397e4c221d9dc277b375666e38b71 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 16 Feb 2015 19:10:57 -0500 Subject: [PATCH 0671/4033] need to analyze core otherwise all downstream REPLs must remember to do this, w/o this step evaluating the first cljs.user form will fail --- src/clj/cljs/repl.clj | 106 +++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 88a085a98..ae0b2fafa 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -558,58 +558,60 @@ opts (if-let [merge-opts (:merge-opts (-setup repl-env opts))] (merge opts merge-opts) opts)] - (evaluate-form repl-env env "" - (with-meta - '(ns cljs.user - (:require [cljs.repl :refer-macros [doc]])) - {:line 1 :column 1}) - identity opts) - (when-let [src (:watch opts)] - (future - (let [log-file (io/file (util/output-directory opts) "watch.log")] - (println "Watch compilation log available at:" (str log-file)) - (binding [*out* (FileWriter. log-file)] - (cljsc/watch src (dissoc opts :watch)))))) - (loop [] - ;; try to let things flush before printing prompt - (Thread/sleep 10) - (print (str "ClojureScript:" ana/*cljs-ns* "> ")) - (flush) - (let [rdr (readers/source-logging-push-back-reader - (PushbackReader. (io/reader *in*)) - 1 - "NO_SOURCE_FILE") - form (try - (binding [*ns* (create-ns ana/*cljs-ns*) - reader/*data-readers* tags/*cljs-data-readers* - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros) - (ana/get-namespace ana/*cljs-ns*)))] - (reader/read rdr nil read-error)) - (catch Exception e - (println (.getMessage e)) - read-error))] - ;; TODO: need to catch errors here too - David - (cond - (identical? form read-error) (recur) - (= form :cljs/quit) :quit - - (and (seq? form) (is-special-fn? (first form))) - (do - (try - ((get special-fns (first form)) repl-env env form opts) - (catch Throwable ex - (println "Failed to execute special function:" (pr-str (first form))) - (trace/print-cause-trace ex 12))) - ;; flush output which could include stack traces - (flush) - (newline) - (recur)) - - :else - (do (eval-and-print repl-env env form opts) - (recur))))) + (comp/with-core-cljs opts + (fn [] + (evaluate-form repl-env env "" + (with-meta + '(ns cljs.user + (:require [cljs.repl :refer-macros [doc]])) + {:line 1 :column 1}) + identity opts) + (when-let [src (:watch opts)] + (future + (let [log-file (io/file (util/output-directory opts) "watch.log")] + (println "Watch compilation log available at:" (str log-file)) + (binding [*out* (FileWriter. log-file)] + (cljsc/watch src (dissoc opts :watch)))))) + (loop [] + ;; try to let things flush before printing prompt + (Thread/sleep 10) + (print (str "ClojureScript:" ana/*cljs-ns* "> ")) + (flush) + (let [rdr (readers/source-logging-push-back-reader + (PushbackReader. (io/reader *in*)) + 1 + "NO_SOURCE_FILE") + form (try + (binding [*ns* (create-ns ana/*cljs-ns*) + reader/*data-readers* tags/*cljs-data-readers* + reader/*alias-map* + (apply merge + ((juxt :requires :require-macros) + (ana/get-namespace ana/*cljs-ns*)))] + (reader/read rdr nil read-error)) + (catch Exception e + (println (.getMessage e)) + read-error))] + ;; TODO: need to catch errors here too - David + (cond + (identical? form read-error) (recur) + (= form :cljs/quit) :quit + + (and (seq? form) (is-special-fn? (first form))) + (do + (try + ((get special-fns (first form)) repl-env env form opts) + (catch Throwable ex + (println "Failed to execute special function:" (pr-str (first form))) + (trace/print-cause-trace ex 12))) + ;; flush output which could include stack traces + (flush) + (newline) + (recur)) + + :else + (do (eval-and-print repl-env env form opts) + (recur))))))) (-tear-down repl-env)))))) (defn repl From f9213105c84250050c5259c8e2b6cfc293fc9654 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 20:25:59 -0500 Subject: [PATCH 0672/4033] typo --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index e79a85278..226867ce4 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1188,7 +1188,7 @@ should contain the source for the given namespace name." (assert (and (or (contains? opts :output-to) (contains? opts :modules)) (contains? opts :output-dir)) - (str ":source-map cannot be specied without also specifying :output-dir " + (str ":source-map cannot be specified without also specifying :output-dir " "and either :output-to or :modules if optimization setting applied")) (assert (or (nil? (:output-to opts)) (string? source-map)) (format (str ":source-map %s must specify a file in the same directory " From 7c8bfb064367cadc1fa9f470ba17ac26d5feee72 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 20:48:51 -0500 Subject: [PATCH 0673/4033] `cljs.repl/analyze-source` needs to take compiler opts so can leverage :cache-analysis. move down so can leverage merged opts --- src/clj/cljs/repl.clj | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ae0b2fafa..f5755e14c 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -519,11 +519,12 @@ (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate (:cljs.analyzer/namespaces compiler-env) so as to support code reflection." - [src-dir] - (if-let [src-dir (and (not (empty? src-dir)) - (File. src-dir))] - (doseq [file (comp/cljs-files-in src-dir)] - (ana/analyze-file (str "file://" (.getAbsolutePath file)))))) + ([src-dir] (analyze-source src-dir nil)) + ([src-dir opts] + (if-let [src-dir (and (not (empty? src-dir)) + (File. src-dir))] + (doseq [file (comp/cljs-files-in src-dir)] + (ana/analyze-file (str "file://" (.getAbsolutePath file)) opts))))) (defn repl* [repl-env opts] @@ -547,8 +548,6 @@ ana/*cljs-static-fns* static-fns] ;; TODO: the follow should become dead code when the REPL is ;; sufficiently enhanced to understand :cache-analysis - David - (when analyze-path - (analyze-source analyze-path)) (let [env {:context :expr :locals {}} special-fns (merge default-special-fns special-fns) is-special-fn? (set (keys special-fns)) @@ -560,6 +559,8 @@ opts)] (comp/with-core-cljs opts (fn [] + (when analyze-path + (analyze-source analyze-path opts)) (evaluate-form repl-env env "" (with-meta '(ns cljs.user From 304a5179ae4a91a40f329e60ffaf280cace76b7a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 21:40:21 -0500 Subject: [PATCH 0674/4033] prep work to have modules support foreign dependencies --- src/clj/cljs/closure.clj | 80 +++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 226867ce4..3d4cf1b26 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -659,10 +659,11 @@ should contain the source for the given namespace name." ) (defn build-modules - "Given a list of IJavaScript sources and compiler options return a _sequence_ - of module name and module description pair tuples. The tuples will be sorted - in module dependency order. The module descriptions will be augmented with - a :closure-module entry holding the Closure JSModule." + "Given a list of IJavaScript sources and compiler options return a modules + map containing :foreign-deps, a dependency sorted list of foreign deps, + and :modules, a dependecy sorted list of module name / description tuples. + The module descriptions will be augmented with a :closure-module entry + holding the Closure JSModule." [sources opts] (let [find-entry (fn [sources entry] (let [entry (name (comp/munge entry))] @@ -683,16 +684,18 @@ should contain the source for the given namespace name." (reduce (fn [[sources ret] entry-sym] (if-let [entry (find-entry sources entry-sym)] - [(remove #{entry} sources) - (conj ret - (js-source-file (javascript-name entry) entry))] + [(remove #{entry} sources) (conj ret entry)] (throw (IllegalArgumentException. (str "Could not find namespace " entry-sym))))) - [sources []] entries)] + [sources []] entries) + foreign-deps (atom [])] ;; add inputs to module - (doseq [^SourceFile module-source module-sources] - (.add js-module module-source)) + (doseq [ijs module-sources] + (if-not (deps/-foreign? ijs) + (.add js-module + ^SourceFile (js-source-file (javascript-name ijs) ijs)) + (swap! foreign-deps conj ijs))) ;; add module dependencies, will always work ;; since modules are already in dependency order (doseq [dep depends-on] @@ -702,14 +705,20 @@ should contain the source for the given namespace name." (str "Parent module " dep " does not exist"))))) [sources' (conj ret - [name (assoc module-desc :closure-module js-module)])])) + [name (assoc module-desc + :closure-module js-module + :foreign-deps @foreign-deps)])])) [sources []] (sort-modules (add-cljs-base-module (:modules opts) opts))) - cljs-base-closure-module (get-in (into {} modules) [:cljs-base :closure-module])] + cljs-base-closure-module (get-in (into {} modules) [:cljs-base :closure-module]) + foreign-deps (atom [])] ;; add anything left to :cljs-base module (doseq [source sources'] - (.add ^JSModule cljs-base-closure-module - (js-source-file (javascript-name source) source))) - modules)) + (if-not (deps/-foreign? source) + (.add ^JSModule cljs-base-closure-module + (js-source-file (javascript-name source) source)) + (swap! foreign-deps conj source))) + {:foreign-deps @foreign-deps + :modules modules})) (comment (build "samples/hello/src" @@ -795,7 +804,10 @@ should contain the source for the given namespace name." :relpaths relpaths})))))) (defn optimize-modules - "Use the Closure Compiler to optimize one or more Closure JSModules." + "Use the Closure Compiler to optimize one or more Closure JSModules. Returns + a modules map containing :foreign-deps, a dependency sorted list of foreign + dependencies, and :modules a dependency sorted list of module name and + description tuples." [opts & sources] {:pre [(and (contains? opts :modules) (not (contains? opts :output-to)))]} @@ -805,7 +817,7 @@ should contain the source for the given namespace name." sources (if (= :whitespace (:optimizations opts)) (cons "var CLOSURE_NO_DEPS = true;" sources) sources) - modules (build-modules sources opts) + {:keys [foreign-deps modules]} (build-modules sources opts) ^List inputs (map (comp :closure-module second) modules) _ (doseq [^JSModule input inputs] (.sortInputsByDeps input closure-compiler)) @@ -815,21 +827,23 @@ should contain the source for the given namespace name." (assert (or (nil? (:source-map opts)) source-map) "Could not create source maps for modules") (if (.success result) - (vec - (for [[name {:keys [output-to closure-module] :as module}] modules] - [name - (merge - (assoc module - :source - (do - (when source-map (.reset source-map)) - (.toSource closure-compiler ^JSModule closure-module))) - (when source-map - (let [sw (StringWriter.) - source-map-name (str output-to ".map.closure")] - (.appendTo source-map sw source-map-name) - {:source-map-json (.toString sw) - :source-map-name source-map-name})))])) + {:foreign-deps foreign-deps + :modules + (vec + (for [[name {:keys [output-to closure-module] :as module}] modules] + [name + (merge + (assoc module + :source + (do + (when source-map (.reset source-map)) + (.toSource closure-compiler ^JSModule closure-module))) + (when source-map + (let [sw (StringWriter.) + source-map-name (str output-to ".map.closure")] + (.appendTo source-map sw source-map-name) + {:source-map-json (.toString sw) + :source-map-name source-map-name})))]))} (report-failure result)))) (defn optimize @@ -975,7 +989,7 @@ should contain the source for the given namespace name." tuples output module sources to disk. Modules description must define :output-to and supply :source entry with the JavaScript source to write to disk." - [opts modules] + [opts {:keys [foreign-deps modules]}] (doseq [[name {:keys [output-to source] :as module-desc}] modules] (assert (not (nil? output-to)) (str "Module " name " does not define :output-to")) From 81365c1e6e1e8b184227dd86128bd15cb2480eb4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 22:00:39 -0500 Subject: [PATCH 0675/4033] refactor last commit, keep module tuple name / desc tuple representation, simplifies foreign deps emission in output-modules --- src/clj/cljs/closure.clj | 60 +++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 3d4cf1b26..f95d69852 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -659,11 +659,9 @@ should contain the source for the given namespace name." ) (defn build-modules - "Given a list of IJavaScript sources and compiler options return a modules - map containing :foreign-deps, a dependency sorted list of foreign deps, - and :modules, a dependecy sorted list of module name / description tuples. - The module descriptions will be augmented with a :closure-module entry - holding the Closure JSModule." + "Given a list of IJavaScript sources and compiler options return a dependency + sorted list of module name / description tuples. The module descriptions will + be augmented with a :closure-module entry holding the Closure JSModule." [sources opts] (let [find-entry (fn [sources entry] (let [entry (name (comp/munge entry))] @@ -717,8 +715,7 @@ should contain the source for the given namespace name." (.add ^JSModule cljs-base-closure-module (js-source-file (javascript-name source) source)) (swap! foreign-deps conj source))) - {:foreign-deps @foreign-deps - :modules modules})) + (assoc-in modules [0 1 :foreign-deps] @foreign-deps))) (comment (build "samples/hello/src" @@ -805,9 +802,7 @@ should contain the source for the given namespace name." (defn optimize-modules "Use the Closure Compiler to optimize one or more Closure JSModules. Returns - a modules map containing :foreign-deps, a dependency sorted list of foreign - dependencies, and :modules a dependency sorted list of module name and - description tuples." + a dependency sorted list of module name and description tuples." [opts & sources] {:pre [(and (contains? opts :modules) (not (contains? opts :output-to)))]} @@ -817,7 +812,7 @@ should contain the source for the given namespace name." sources (if (= :whitespace (:optimizations opts)) (cons "var CLOSURE_NO_DEPS = true;" sources) sources) - {:keys [foreign-deps modules]} (build-modules sources opts) + modules (build-modules sources opts) ^List inputs (map (comp :closure-module second) modules) _ (doseq [^JSModule input inputs] (.sortInputsByDeps input closure-compiler)) @@ -827,23 +822,21 @@ should contain the source for the given namespace name." (assert (or (nil? (:source-map opts)) source-map) "Could not create source maps for modules") (if (.success result) - {:foreign-deps foreign-deps - :modules - (vec - (for [[name {:keys [output-to closure-module] :as module}] modules] - [name - (merge - (assoc module - :source - (do - (when source-map (.reset source-map)) - (.toSource closure-compiler ^JSModule closure-module))) - (when source-map - (let [sw (StringWriter.) - source-map-name (str output-to ".map.closure")] - (.appendTo source-map sw source-map-name) - {:source-map-json (.toString sw) - :source-map-name source-map-name})))]))} + (vec + (for [[name {:keys [output-to closure-module] :as module}] modules] + [name + (merge + (assoc module + :source + (do + (when source-map (.reset source-map)) + (.toSource closure-compiler ^JSModule closure-module))) + (when source-map + (let [sw (StringWriter.) + source-map-name (str output-to ".map.closure")] + (.appendTo source-map sw source-map-name) + {:source-map-json (.toString sw) + :source-map-name source-map-name})))])) (report-failure result)))) (defn optimize @@ -984,18 +977,23 @@ should contain the source for the given namespace name." "document.write('');\n"))))) +(declare foreign-deps-str) + (defn output-modules "Given compiler options and a sequence of module name and module description tuples output module sources to disk. Modules description must define :output-to and supply :source entry with the JavaScript source to write to disk." - [opts {:keys [foreign-deps modules]}] - (doseq [[name {:keys [output-to source] :as module-desc}] modules] + [opts modules] + (doseq [[name {:keys [output-to source foreign-deps] :as module-desc}] modules] (assert (not (nil? output-to)) (str "Module " name " does not define :output-to")) (assert (not (nil? source)) (str "Module " name " did not supply :source")) - (spit (io/file output-to) source) + (spit (io/file output-to) + (if-not (empty? foreign-deps) + (str (foreign-deps-str opts foreign-deps) "\n" source) + source)) (when (:source-map opts) (spit (io/file (:source-map-name module-desc)) (:source-map-json module-desc))))) From c583a75a24382054197e5e83b376a33ae561f2c2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 22:12:34 -0500 Subject: [PATCH 0676/4033] add header to the base module --- src/clj/cljs/closure.clj | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index f95d69852..b5bedd217 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -977,7 +977,7 @@ should contain the source for the given namespace name." "document.write('');\n"))))) -(declare foreign-deps-str) +(declare foreign-deps-str add-header) (defn output-modules "Given compiler options and a sequence of module name and module description @@ -991,9 +991,12 @@ should contain the source for the given namespace name." (assert (not (nil? source)) (str "Module " name " did not supply :source")) (spit (io/file output-to) - (if-not (empty? foreign-deps) - (str (foreign-deps-str opts foreign-deps) "\n" source) - source)) + (let [source (if (= name :cljs-base) + (add-header opts source) + source)] + (if-not (empty? foreign-deps) + (str (foreign-deps-str opts foreign-deps) "\n" source) + source))) (when (:source-map opts) (spit (io/file (:source-map-name module-desc)) (:source-map-json module-desc))))) From b7fb757d90d10ecbe9915668ff6227dffe14ba3e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 22:36:39 -0500 Subject: [PATCH 0677/4033] CLJS-1041: Google Closure Modules :foreign-libs support --- src/clj/cljs/closure.clj | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index b5bedd217..b8fdcec83 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -664,12 +664,12 @@ should contain the source for the given namespace name." be augmented with a :closure-module entry holding the Closure JSModule." [sources opts] (let [find-entry (fn [sources entry] - (let [entry (name (comp/munge entry))] - (some - (fn [source] - (when (some #{entry} (:provides source)) - source)) - sources))) + (some + (fn [source] + (when (some #{(name entry) (name (comp/munge entry))} + (:provides source)) + source)) + sources)) [sources' modules] (reduce (fn [[sources ret] [name {:keys [entries output-to depends-on] :as module-desc}]] @@ -804,8 +804,10 @@ should contain the source for the given namespace name." "Use the Closure Compiler to optimize one or more Closure JSModules. Returns a dependency sorted list of module name and description tuples." [opts & sources] - {:pre [(and (contains? opts :modules) - (not (contains? opts :output-to)))]} + ;; the following pre-condition can't be enabled + ;; lein-cljsbuild adds :output-to? + #_{:pre [(and (contains? opts :modules) + (not (contains? opts :output-to)))]} (let [closure-compiler (make-closure-compiler) ^List externs (load-externs opts) compiler-options (make-options opts) From 95ea1fdc98843b8efa10616d9cdf103866f1aef8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 23:34:50 -0500 Subject: [PATCH 0678/4033] fix duplicate entries in set literal issue in `cljs.closure/build-modules` --- src/clj/cljs/closure.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index b8fdcec83..35f65ac9a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -666,9 +666,11 @@ should contain the source for the given namespace name." (let [find-entry (fn [sources entry] (some (fn [source] - (when (some #{(name entry) (name (comp/munge entry))} - (:provides source)) - source)) + (let [matcher + (into #{} + [(name entry) (name (comp/munge entry))])] + (when (some matcher (:provides source)) + source))) sources)) [sources' modules] (reduce From 0a885c4e398e2aa1cbc8293c1954ddf3072a01f0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 23:45:44 -0500 Subject: [PATCH 0679/4033] :modules has use once semantics, present a meaningful error if the user violates --- src/clj/cljs/closure.clj | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 35f65ac9a..e34b8b041 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -672,6 +672,7 @@ should contain the source for the given namespace name." (when (some matcher (:provides source)) source))) sources)) + used (atom {}) [sources' modules] (reduce (fn [[sources ret] [name {:keys [entries output-to depends-on] :as module-desc}]] @@ -684,10 +685,17 @@ should contain the source for the given namespace name." (reduce (fn [[sources ret] entry-sym] (if-let [entry (find-entry sources entry-sym)] - [(remove #{entry} sources) (conj ret entry)] - (throw - (IllegalArgumentException. - (str "Could not find namespace " entry-sym))))) + (do + (swap! used assoc entry-sym name) + [(remove #{entry} sources) (conj ret entry)]) + (if (contains? @used entry-sym) + (throw + (IllegalArgumentException. + (str "Already used namespace " entry-sym " " + "in module " (get @used entry-sym)))) + (throw + (IllegalArgumentException. + (str "Could not find namespace " entry-sym)))))) [sources []] entries) foreign-deps (atom [])] ;; add inputs to module From 2a1c17f8454cf8779129277941683f72e488a49b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Feb 2015 23:53:04 -0500 Subject: [PATCH 0680/4033] clarify `cljs.closure/build-modules` docstring --- src/clj/cljs/closure.clj | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index e34b8b041..94aa29807 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -659,9 +659,12 @@ should contain the source for the given namespace name." ) (defn build-modules - "Given a list of IJavaScript sources and compiler options return a dependency - sorted list of module name / description tuples. The module descriptions will - be augmented with a :closure-module entry holding the Closure JSModule." + "Given a list of IJavaScript sources in dependency order and compiler options + return a dependency sorted list of module name / description tuples. The + module descriptions will be augmented with a :closure-module entry holding + the Closure JSModule. Each module description will also be augmented with + a :foreign-deps vector containing foreign IJavaScript sources in dependency + order." [sources opts] (let [find-entry (fn [sources entry] (some From 79208f5bf15825a2ba2d0ce95aae6d2b71966494 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 17 Feb 2015 16:45:26 -0500 Subject: [PATCH 0681/4033] CLJS-1036: use getResources not findResources in get-upstream-deps* as suggested by Chas Emerick --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 94aa29807..47c857155 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1149,7 +1149,7 @@ should contain the source for the given namespace name." ([] (get-upstream-deps* (. (Thread/currentThread) (getContextClassLoader)))) ([classloader] - (let [upstream-deps (map #(read-string (slurp %)) (enumeration-seq (. classloader (findResources "deps.cljs"))))] + (let [upstream-deps (map #(read-string (slurp %)) (enumeration-seq (. classloader (getResources "deps.cljs"))))] #_(doseq [dep upstream-deps] (println (str "Upstream deps.cljs found on classpath. " dep " This is an EXPERIMENTAL FEATURE and is not guarenteed to remain stable in future versions."))) (apply merge-with concat upstream-deps)))) From f1cb61a6a4985b2d3633fc2fb13d0c06f3718272 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 18 Feb 2015 11:11:26 -0500 Subject: [PATCH 0682/4033] fix DCE regression for trivial programs ns forms should not emit *loaded-libs* code when COMPILED=true --- src/clj/cljs/compiler.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index ac9956079..e79998d7c 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -810,8 +810,8 @@ (let [loaded-libs (munge 'cljs.core.*loaded-libs*) loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))] (when (-> libs meta :reload-all) - (emitln loaded-libs-temp " = " loaded-libs " || cljs.core.set();") - (emitln loaded-libs " = cljs.core.set();")) + (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") + (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond ;; only emit if foreign lib under Node.js @@ -829,7 +829,7 @@ :else (emitln "goog.require('" (munge lib) "');"))) (when (-> libs meta :reload-all) - (emitln loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))) + (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))) (defmethod emit* :ns [{:keys [name requires uses require-macros env]}] From 83b5ebaa439d13ba42b49e25f97d5ed1b039bd2c Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 18 Feb 2015 19:19:42 -0500 Subject: [PATCH 0683/4033] make `cljs.closure/emit-optimized-source-map` a bit more generic the first argument now is the parsed JSON Closure source map. Change `cljs.closure/output-modules` to take the original sequence of IJavaScript sources. This is prep work for CLJS-1042. --- src/clj/cljs/closure.clj | 72 +++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 47c857155..b14e23330 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -757,15 +757,13 @@ should contain the source for the given namespace name." ) (defn emit-optimized-source-map - [^com.google.javascript.jscomp.Compiler closure-compiler sources name opts] - (let [name' (str name ".closure") - _ (with-open [out (io/writer name')] - (.appendTo (.getSourceMap closure-compiler) out name')) - preamble (make-preamble opts) - preamble-line-count (- (count (.split #"\r?\n" preamble -1)) 1) - sm-json (-> (io/file name') slurp - (json/read-str :key-fn keyword)) - closure-source-map (sm/decode-reverse sm-json)] + "Given a JSON parsed Google Closure JavaScript to JavaScript source map, + the entire list of original IJavaScript sources output a merged JavaScript + to ClojureScript source map file with the given file name. opts should + supply :preamble-line-count and :foreign-deps-line-count if they are + relevant." + [sm-json sources name opts] + (let [closure-source-map (sm/decode-reverse sm-json)] (loop [sources (seq sources) relpaths {} merged (sorted-map-by @@ -802,9 +800,12 @@ should contain the source for the given namespace name." (spit (io/file name) (sm/encode merged - {:preamble-line-count (+ preamble-line-count - (or (:foreign-deps-line-count opts) 0)) - :lines (+ (:lineCount sm-json) preamble-line-count 2) + {: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) @@ -869,7 +870,17 @@ should contain the source for the given namespace name." ;; compiler.getSourceMap().reset() (let [source (.toSource closure-compiler)] (when-let [name (:source-map opts)] - (emit-optimized-source-map closure-compiler sources name opts)) + (let [name' (str name ".closure")] + (with-open [out (io/writer name')] + (.appendTo (.getSourceMap closure-compiler) out name')) + (emit-optimized-source-map + (-> (io/file name') + slurp + (json/read-str :key-fn keyword)) + sources name + (assoc opts + :preamble-line-count + (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1))))) source) (report-failure result)))) @@ -999,7 +1010,7 @@ should contain the source for the given namespace name." tuples output module sources to disk. Modules description must define :output-to and supply :source entry with the JavaScript source to write to disk." - [opts modules] + [opts js-sources modules] (doseq [[name {:keys [output-to source foreign-deps] :as module-desc}] modules] (assert (not (nil? output-to)) (str "Module " name " does not define :output-to")) @@ -1335,11 +1346,7 @@ should contain the source for the given namespace name." [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) optim (:optimizations all-opts) ret (if (and optim (not= optim :none)) - (let [fdeps-str (foreign-deps-str all-opts - (filter foreign-source? js-sources)) - all-opts (assoc all-opts - :foreign-deps-line-count - (- (count (.split #"\r?\n" fdeps-str -1)) 1))] + (do (when-let [fname (:source-map all-opts)] (assert (or (nil? (:output-to all-opts)) (string? fname)) (str ":source-map must name a file when using :whitespace, " @@ -1348,17 +1355,22 @@ should contain the source for the given namespace name." (if (:modules all-opts) (->> (apply optimize-modules all-opts js-sources) - (output-modules all-opts)) - (->> - (util/measure compiler-stats - "Optimize sources" - (apply optimize all-opts - (remove foreign-source? js-sources))) - (add-wrapper all-opts) - (add-source-map-link all-opts) - (str fdeps-str) - (add-header all-opts) - (output-one-file all-opts)))) + (output-modules all-opts js-sources)) + (let [fdeps-str (foreign-deps-str all-opts + (filter foreign-source? js-sources)) + all-opts (assoc all-opts + :foreign-deps-line-count + (- (count (.split #"\r?\n" fdeps-str -1)) 1))] + (->> + (util/measure compiler-stats + "Optimize sources" + (apply optimize all-opts + (remove foreign-source? js-sources))) + (add-wrapper all-opts) + (add-source-map-link all-opts) + (str fdeps-str) + (add-header all-opts) + (output-one-file all-opts))))) (apply output-unoptimized all-opts js-sources))] ;; emit Node.js bootstrap script for :none & :whitespace optimizations (when (and (= (:target opts) :nodejs) From 4dbf8c8723f0702be46546a036f152dcaa0598f1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 18 Feb 2015 20:02:11 -0500 Subject: [PATCH 0684/4033] CLJS-1042: Google Closure Modules :source-map support straight-forward except for the hacks around cljsbuild being :modules unaware --- src/clj/cljs/closure.clj | 62 ++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index b14e23330..56c1744bc 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1003,29 +1003,53 @@ should contain the source for the given namespace name." "document.write('');\n"))))) -(declare foreign-deps-str add-header) +(declare foreign-deps-str add-header add-source-map-link) (defn output-modules - "Given compiler options and a sequence of module name and module description - tuples output module sources to disk. Modules description must define - :output-to and supply :source entry with the JavaScript source to write - to disk." + "Given compiler options, original IJavaScript sources and a sequence of + module name and module description tuples output module sources to disk. + Modules description must define :output-to and supply :source entry with + the JavaScript source to write to disk." [opts js-sources modules] (doseq [[name {:keys [output-to source foreign-deps] :as module-desc}] modules] (assert (not (nil? output-to)) (str "Module " name " does not define :output-to")) (assert (not (nil? source)) (str "Module " name " did not supply :source")) - (spit (io/file output-to) - (let [source (if (= name :cljs-base) - (add-header opts source) - source)] - (if-not (empty? foreign-deps) - (str (foreign-deps-str opts foreign-deps) "\n" source) - source))) - (when (:source-map opts) - (spit (io/file (:source-map-name module-desc)) - (:source-map-json module-desc))))) + (let [fdeps-str (when-not (empty? foreign-deps) + (foreign-deps-str opts foreign-deps)) + sm-name (when (:source-map opts) + (str output-to ".map"))] + (spit (io/file output-to) + (as-> source source + (if (= name :cljs-base) + (add-header opts source) + source) + (if fdeps-str + (str fdeps-str "\n" source) + source) + (if sm-name + (add-source-map-link + (assoc opts + :output-to output-to + :source-map sm-name) + source) + source))) + (when (:source-map opts) + (let [sm-json-str (:source-map-json module-desc) + sm-json (json/read-str sm-json-str :key-fn keyword)] + (spit (io/file (:source-map-name module-desc)) sm-json-str) + (emit-optimized-source-map sm-json js-sources sm-name + (merge opts + {:source-map sm-name + :preamble-line-count + (if (= name :cljs-base) + (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1) + 0) + :foreign-deps-line-count + (if fdeps-str + (- (count (.split #"\r?\n" fdeps-str -1)) 1) + 0)}))))))) (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, @@ -1231,17 +1255,17 @@ should contain the source for the given namespace name." (contains? opts :output-dir)) (str ":source-map cannot be specified without also specifying :output-dir " "and either :output-to or :modules if optimization setting applied")) - (assert (or (nil? (:output-to opts)) (string? source-map)) + (assert (or (nil? (:output-to opts)) (:modules opts) (string? source-map)) (format (str ":source-map %s must specify a file in the same directory " "as :output-to %s if optimization setting applied") (pr-str source-map) (pr-str output-to))) - (assert (or (nil? (:output-to opts)) (in-same-dir? source-map output-to)) + (assert (or (nil? (:output-to opts)) (:modules opts) (in-same-dir? source-map output-to)) (format (str ":source-map %s must specify a file in the same directory as " ":output-to %s if optimization setting applied") (pr-str source-map) (pr-str output-to))) - (assert (or (nil? (:output-to opts)) (same-or-subdirectory-of? (absolute-parent output-to) output-dir)) + (assert (or (nil? (:output-to opts)) (:modules opts) (same-or-subdirectory-of? (absolute-parent output-to) output-dir)) (format (str ":output-dir %s must specify a directory in :output-to's " "parent %s if optimization setting applied") (pr-str output-dir) @@ -1348,7 +1372,7 @@ should contain the source for the given namespace name." ret (if (and optim (not= optim :none)) (do (when-let [fname (:source-map all-opts)] - (assert (or (nil? (:output-to all-opts)) (string? fname)) + (assert (or (nil? (:output-to all-opts)) (:modules opts) (string? fname)) (str ":source-map must name a file when using :whitespace, " ":simple, or :advanced optimizations with :output-to")) (doall (map #(source-on-disk all-opts %) js-sources))) From 43570ac67c4fbf12609359cc1de7caaccc673aff Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Feb 2015 07:58:35 -0500 Subject: [PATCH 0685/4033] do not emit the closure source map by default, add :closure-source-map knob --- src/clj/cljs/closure.clj | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 56c1744bc..dda1783bd 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -870,13 +870,15 @@ should contain the source for the given namespace name." ;; compiler.getSourceMap().reset() (let [source (.toSource closure-compiler)] (when-let [name (:source-map opts)] - (let [name' (str name ".closure")] - (with-open [out (io/writer name')] - (.appendTo (.getSourceMap closure-compiler) out name')) + (let [name' (str name ".closure") + sw (StringWriter.) + sm-json-str (do + (.appendTo (.getSourceMap closure-compiler) sw name') + (.toString sw))] + (when (true? (:closure-source-map opts)) + (spit (io/file name') sm-json-str)) (emit-optimized-source-map - (-> (io/file name') - slurp - (json/read-str :key-fn keyword)) + (json/read-str sm-json-str :key-fn keyword) sources name (assoc opts :preamble-line-count @@ -1038,7 +1040,8 @@ should contain the source for the given namespace name." (when (:source-map opts) (let [sm-json-str (:source-map-json module-desc) sm-json (json/read-str sm-json-str :key-fn keyword)] - (spit (io/file (:source-map-name module-desc)) sm-json-str) + (when (true? (:closure-source-map opts)) + (spit (io/file (:source-map-name module-desc)) sm-json-str)) (emit-optimized-source-map sm-json js-sources sm-name (merge opts {:source-map sm-name From 412b3e7762bd75cea31a2e9a4b036fb3c04782a3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Feb 2015 08:38:49 -0500 Subject: [PATCH 0686/4033] change `cljs.repl/repl*` so it destructures the same keys as `clojure.main/repl` --- src/clj/cljs/repl.clj | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index f5755e14c..c437cc712 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -24,7 +24,8 @@ [cljs.source-map :as sm]) (:import [java.io File PushbackReader FileWriter] [java.net URL] - [javax.xml.bind DatatypeConverter])) + [javax.xml.bind DatatypeConverter] + [clojure.lang LineNumberingPushbackReader])) (def ^:dynamic *cljs-verbose* false) @@ -526,10 +527,26 @@ (doseq [file (comp/cljs-files-in src-dir)] (ana/analyze-file (str "file://" (.getAbsolutePath file)) opts))))) +(defn repl-prompt [] + (print (str "ClojureScript:" ana/*cljs-ns* "> "))) + +(defn repl-caught [e] + ) + (defn repl* - [repl-env opts] - (print "To quit, type: ") - (prn :cljs/quit) + [repl-env {:keys [init need-prompt prompt flush read eval print caught] + :or {init #() + need-prompt (if (instance? LineNumberingPushbackReader *in*) + #(.atLineStart ^LineNumberingPushbackReader *in*) + #(identity true)) + prompt repl-prompt + flush flush + read repl-read + eval eval + print prn + caught repl-caught} + :as opts}] + (print "To quit, type: " :cljs/quit) (let [ups-deps (cljsc/get-upstream-deps) {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} @@ -576,7 +593,7 @@ (loop [] ;; try to let things flush before printing prompt (Thread/sleep 10) - (print (str "ClojureScript:" ana/*cljs-ns* "> ")) + (prompt) (flush) (let [rdr (readers/source-logging-push-back-reader (PushbackReader. (io/reader *in*)) From 9edde9173bb927e68fb18741107293627a2428f9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Feb 2015 09:15:15 -0500 Subject: [PATCH 0687/4033] decomplect `cljs.repl/eval-and-print`, now just `cljs.repl/eval-cljs`. `cljs.repl/eval-cljs` does not handle printing, instead just returns the value (always represented as a string). printing in the body of REPL loop now defers to parameterized `print`. --- src/clj/cljs/repl.clj | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c437cc712..a4e57a92d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -427,17 +427,19 @@ (set! *e e#) (throw e#))))))) -(defn- eval-and-print +(defn- eval-cljs + "Given a REPL evaluation environment, an analysis environment, and a + form, evaluate the form and return the result. The result is always the value + represented as a string." ([repl-env env form] - (eval-and-print repl-env env form nil)) + (eval-cljs repl-env env form nil)) ([repl-env env form opts] - (println - (evaluate-form repl-env - (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) - "" - form - (wrap-fn form) - opts)))) + (evaluate-form repl-env + (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) + "" + form + (wrap-fn form) + opts))) ;; Special REPL fns, these provide compatiblity with Clojure functions ;; that are not possible to reproduce given ClojureScript's compilation model @@ -542,8 +544,8 @@ prompt repl-prompt flush flush read repl-read - eval eval - print prn + eval eval-cljs + print println caught repl-caught} :as opts}] (print "To quit, type: " :cljs/quit) @@ -587,7 +589,7 @@ (when-let [src (:watch opts)] (future (let [log-file (io/file (util/output-directory opts) "watch.log")] - (println "Watch compilation log available at:" (str log-file)) + (print "Watch compilation log available at:" (str log-file)) (binding [*out* (FileWriter. log-file)] (cljsc/watch src (dissoc opts :watch)))))) (loop [] @@ -608,7 +610,7 @@ (ana/get-namespace ana/*cljs-ns*)))] (reader/read rdr nil read-error)) (catch Exception e - (println (.getMessage e)) + (print (.getMessage e)) read-error))] ;; TODO: need to catch errors here too - David (cond @@ -620,7 +622,7 @@ (try ((get special-fns (first form)) repl-env env form opts) (catch Throwable ex - (println "Failed to execute special function:" (pr-str (first form))) + (print "Failed to execute special function:" (pr-str (first form))) (trace/print-cause-trace ex 12))) ;; flush output which could include stack traces (flush) @@ -628,8 +630,9 @@ (recur)) :else - (do (eval-and-print repl-env env form opts) - (recur))))))) + (let [value (eval repl-env env form opts)] + (print value) + (recur))))))) (-tear-down repl-env)))))) (defn repl From 41f6aa91a2df7413f9e2a2cc4f0dc4f9644cf28a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Feb 2015 09:46:50 -0500 Subject: [PATCH 0688/4033] always use REPL options supplied :print and :flush Add `cljs.rpl/*repl-opts*` dyn var hack so that we don't break 3rd party REPLs calling into `cljs.repl/evaluate-form` without passing back `opts`. --- src/clj/cljs/repl.clj | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index a4e57a92d..62dee4df7 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -28,6 +28,7 @@ [clojure.lang LineNumberingPushbackReader])) (def ^:dynamic *cljs-verbose* false) +(def ^:dynamic *repl-opts* nil) ;; ============================================================================= ;; Copied over from clojure.main @@ -262,11 +263,11 @@ (defn print-mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace print the ClojureScript stacktrace. See mapped-stacktrace." - ([stacktrace] (print-mapped-stacktrace stacktrace nil)) + ([stacktrace] (print-mapped-stacktrace stacktrace *repl-opts*)) ([stacktrace opts] (doseq [{:keys [function file line column]} (mapped-stacktrace stacktrace opts)] - (println "\t" (str function " (" file ":" line ":" column ")"))))) + ((:print opts) "\t" (str function " (" file ":" line ":" column ")"))))) (comment (cljsc/build "samples/hello/src" @@ -317,7 +318,7 @@ (display-error repl-env ret form (constantly nil) opts)) ([repl-env ret form f opts] (f) - (println (:value ret)) + ((:print opts) (:value ret)) (when-let [st (:stacktrace ret)] (if (and (true? (:source-map opts)) (satisfies? IParseStacktrace repl-env)) @@ -326,9 +327,9 @@ (if (satisfies? IPrintStacktrace repl-env) (-print-stacktrace repl-env cst ret opts) (print-mapped-stacktrace cst opts)) - (println st))) - (println st)) - (flush)))) + ((:print opts) st))) + ((:print opts) st)) + ((:flush opts))))) (defn evaluate-form "Evaluate a ClojureScript form in the JavaScript environment. Returns a @@ -337,7 +338,7 @@ ([repl-env env filename form] (evaluate-form repl-env env filename form identity)) ([repl-env env filename form wrap] - (evaluate-form repl-env env filename form wrap nil)) + (evaluate-form repl-env env filename form wrap *repl-opts*)) ([repl-env env filename form wrap opts] (try (binding [ana/*cljs-file* filename] @@ -374,7 +375,7 @@ (distinct (vals (:uses ast)))) opts)) (when *cljs-verbose* - (print js)) + ((:print opts) js)) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] (case (:status ret) :error (display-error repl-env ret form opts) @@ -386,8 +387,8 @@ :success (:value ret))))) (catch Throwable ex (.printStackTrace ex) - (println (str ex)) - (flush))))) + ((:print opts) (str ex)) + ((:flush opts)))))) (defn load-stream [repl-env filename res] (let [env (ana/empty-env)] @@ -553,6 +554,8 @@ {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} (assoc (merge (-repl-options repl-env) opts) + :print print + :flush flush :ups-libs (:libs ups-deps) :ups-foreign-libs (:foreign-libs ups-deps))] (env/with-compiler-env @@ -564,7 +567,8 @@ :undeclared-var warn-on-undeclared :undeclared-ns warn-on-undeclared :undeclared-ns-form warn-on-undeclared) - ana/*cljs-static-fns* static-fns] + ana/*cljs-static-fns* static-fns + *repl-opts* opts] ;; TODO: the follow should become dead code when the REPL is ;; sufficiently enhanced to understand :cache-analysis - David (let [env {:context :expr :locals {}} @@ -626,7 +630,7 @@ (trace/print-cause-trace ex 12))) ;; flush output which could include stack traces (flush) - (newline) + (print) (recur)) :else From d60492f6de43302aeff2eeeae0f88415ba9b68da Mon Sep 17 00:00:00 2001 From: Shaun LeBron Date: Thu, 19 Feb 2015 10:20:39 -0600 Subject: [PATCH 0689/4033] Warn when a symbol is defined multiple times in a file Second fix. We wrap the bodies of compile-file and analyze-file with a *defs-in-file* to track/warn redefs in a file session. --- src/clj/cljs/analyzer.clj | 13 +++++++--- src/clj/cljs/compiler.clj | 53 ++++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index d89dce6fd..8a4290277 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -36,6 +36,8 @@ (def ^:dynamic *load-macros* true) (def ^:dynamic *macro-infer* true) +(def ^:dynamic *file-defs* nil) + ;; log compiler activities (def ^:dynamic *verbose* false) @@ -48,7 +50,7 @@ :undeclared-ns true :undeclared-ns-form true :redef true - :redef-in-file false + :redef-in-file true :dynamic true :fn-var true :fn-arity true @@ -682,9 +684,11 @@ (when (and (not *allow-redef*) (not (:declared v)) (not (:declared sym-meta)) - (= (:file v) *cljs-file*) - (not= "" *cljs-file*)) + *file-defs* + (get @*file-defs* sym)) (warning :redef-in-file env {:sym sym :line (:line v)}))) + (when *file-defs* + (swap! *file-defs* conj sym)) (let [env (if (or (and (not= ns-name 'cljs.core) (core-name? env sym)) (get-in @env/*compiler* [::namespaces ns-name :uses sym])) @@ -1906,6 +1910,7 @@ argument, which the reader will use in any emitted errors." meaningful value." ([f] (analyze-file f nil)) ([f {:keys [output-dir] :as opts}] + (binding [*file-defs* (atom #{})] (let [res (cond (instance? File f) f (instance? URL f) f @@ -1952,5 +1957,5 @@ argument, which the reader will use in any emitted errors." (util/debug-prn "Reading analysis cache for " res)) (swap! env/*compiler* assoc-in [::analyzed-cljs path] true) (swap! env/*compiler* assoc-in [::namespaces ns] - (edn/read-string (slurp cache))))))))))) + (edn/read-string (slurp cache)))))))))))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index e79998d7c..e7a796f3f 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1027,32 +1027,33 @@ (compile-file src dest nil)) ([src dest opts] {:post [map?]} - (let [src-file (io/file src) - dest-file (io/file dest) - opts (merge {:optimizations :none} opts)] - (if (.exists src-file) - (try - (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) - opts (if (= ns 'cljs.core) (assoc opts :static-fns true) opts)] - (if (requires-compilation? src-file dest-file opts) - (do - (util/mkdirs dest-file) - (when (contains? (::ana/namespaces @env/*compiler*) ns) - (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) - (let [ret (compile-file* src-file dest-file opts)] - (when *dependents* - (swap! *dependents* - (fn [{:keys [recompile visited]}] - {:recompile (into recompile (ana/ns-dependents ns)) - :visited (conj visited ns)}))) - ret)) - (do - (when-not (contains? (::ana/namespaces @env/*compiler*) ns) - (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) - ns-info))) - (catch Exception e - (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) - (throw (java.io.FileNotFoundException. (str "The file " src " does not exist."))))))) + (binding [ana/*file-defs* (atom #{})] + (let [src-file (io/file src) + dest-file (io/file dest) + opts (merge {:optimizations :none} opts)] + (if (.exists src-file) + (try + (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) + opts (if (= ns 'cljs.core) (assoc opts :static-fns true) opts)] + (if (requires-compilation? src-file dest-file opts) + (do + (util/mkdirs dest-file) + (when (contains? (::ana/namespaces @env/*compiler*) ns) + (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) + (let [ret (compile-file* src-file dest-file opts)] + (when *dependents* + (swap! *dependents* + (fn [{:keys [recompile visited]}] + {:recompile (into recompile (ana/ns-dependents ns)) + :visited (conj visited ns)}))) + ret)) + (do + (when-not (contains? (::ana/namespaces @env/*compiler*) ns) + (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) + ns-info))) + (catch Exception e + (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) + (throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))) (defn cljs-files-in "Return a sequence of all .cljs files in the given directory." From 7beec094542563977c442f73c8200bb994753f6b Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 19 Feb 2015 17:31:04 -0500 Subject: [PATCH 0690/4033] add print-no-newline REPL parameter after feedback from CLJS REPL authors --- src/clj/cljs/repl.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 62dee4df7..f2dbce0c1 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -537,7 +537,7 @@ ) (defn repl* - [repl-env {:keys [init need-prompt prompt flush read eval print caught] + [repl-env {:keys [init need-prompt prompt flush read eval print print-no-newline caught] :or {init #() need-prompt (if (instance? LineNumberingPushbackReader *in*) #(.atLineStart ^LineNumberingPushbackReader *in*) @@ -547,7 +547,8 @@ read repl-read eval eval-cljs print println - caught repl-caught} + caught repl-caught + print-no-newline print} :as opts}] (print "To quit, type: " :cljs/quit) (let [ups-deps (cljsc/get-upstream-deps) @@ -555,6 +556,7 @@ :or {warn-on-undeclared true}} (assoc (merge (-repl-options repl-env) opts) :print print + :print-no-newline print-no-newline :flush flush :ups-libs (:libs ups-deps) :ups-foreign-libs (:foreign-libs ups-deps))] From cd971623f3b5cf8527d0c81f9822a60762234052 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 20 Feb 2015 09:07:45 -0500 Subject: [PATCH 0691/4033] CLJS-1046: static vars do not respect user compile time metadata Move user meta data into :meta var field to separate from compiler and analyzer metadata. When emitting static vars emit the standard var meta plus the user supplied meta Add Ivan Mikushin's tests. --- Clojurescript.iml | 1 + src/clj/cljs/analyzer.clj | 16 +++++++++++----- test/cljs/cljs/core_test.cljs | 13 +++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index 62f1f9779..cc3269c84 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -9,6 +9,7 @@ + diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index d89dce6fd..c77589a19 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -554,11 +554,16 @@ :var (analyze env sym) :sym (analyze env `(quote ~(symbol (name (:ns var)) (name (:name var))))) :meta (let [ks [:ns :doc :file :line :column] - m (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) - :name `(quote ~(symbol (name (:name var)))) - :test `(when ~sym (.-cljs$lang$test ~sym)) - :arglists (map with-meta (:arglists var) (:arglists-meta var)))] - (analyze env m))})) + m (merge + (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) + :name `(quote ~(symbol (name (:name var)))) + :test `(when ~sym (.-cljs$lang$test ~sym)) + :arglists (map with-meta (:arglists var) (:arglists-meta var))) + (let [user-meta (:meta var) + uks (keys user-meta)] + (zipmap uks + (map #(list 'quote (get user-meta %)) uks))))] + (analyze env m))})) (defmethod parse 'if [op env [_ test then else :as form] name _] @@ -720,6 +725,7 @@ ;; elide test metadata, as it includes non-valid EDN - David (cond-> sym-meta :test (-> (dissoc :test) (assoc :test true))) + {:meta (dissoc sym-meta :test)} (when doc {:doc doc}) (when dynamic {:dynamic true}) (source-info var-name env) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 18898babe..f11e8a4ed 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2681,6 +2681,19 @@ (catch js/Error e *print-level*))))) +(defn meta-test-fn + "A docstring" + {:foo :bar, :baz 12345, :whatever "String Metadata"} + [a b] + (+ a b)) + +(deftest test-cljs-1046 + (let [m (meta #'meta-test-fn)] + (is (= "A docstring" (:doc m))) + (is (= :bar (:foo m))) + (is (= 12345 (:baz m))) + (is (= "String Metadata" (:whatever m))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 532c097eee552a4f99a9670c232236af7ac2f2a9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 20 Feb 2015 09:08:35 -0500 Subject: [PATCH 0692/4033] tools.reader is char based not int based --- src/clj/cljs/repl.clj | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 62dee4df7..5a5ce1065 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -42,10 +42,10 @@ CRLF to a single \\newline." [s] (let [c (.read s)] - (cond - (= c (int \newline)) :line-start - (= c -1) :stream-end - :else (do (.unread s c) :body)))) + (case c + \newline :line-start + nil :stream-end + (do (.unread s c) :body)))) (defn skip-whitespace "Skips whitespace characters on stream s. Returns :line-start, :stream-end, @@ -58,12 +58,13 @@ \\newline." [s] (loop [c (.read s)] - (cond - (= c (int \newline)) :line-start - (= c -1) :stream-end - (= c (int \;)) (do (.readLine s) :line-start) - (or (Character/isWhitespace (char c)) (= c (int \,))) (recur (.read s)) - :else (do (.unread s c) :body)))) + (case c + \newline :line-start + nil :stream-end + \; (do (.readLine s) :line-start) + (if (or (Character/isWhitespace c) (identical? c \,)) + (recur (.read s)) + (do (.unread s c) :body))))) (defn repl-read "Default :read hook for repl. Reads from *in* which must either be an From e4ab10179ff36c7bcd036c78c71c992bd15a6cd0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Feb 2015 15:48:59 -0500 Subject: [PATCH 0693/4033] brave new REPL world --- src/clj/cljs/repl.clj | 310 +++++++++++++++++++++++------------------- 1 file changed, 167 insertions(+), 143 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 37cf00749..1e6307942 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -25,7 +25,7 @@ (:import [java.io File PushbackReader FileWriter] [java.net URL] [javax.xml.bind DatatypeConverter] - [clojure.lang LineNumberingPushbackReader])) + [clojure.lang IExceptionInfo])) (def ^:dynamic *cljs-verbose* false) (def ^:dynamic *repl-opts* nil) @@ -41,11 +41,11 @@ its behavior of both supporting .unread and collapsing all of CR, LF, and CRLF to a single \\newline." [s] - (let [c (.read s)] + (let [c (readers/read-char s)] (case c \newline :line-start nil :stream-end - (do (.unread s c) :body)))) + (do (readers/unread s c) :body)))) (defn skip-whitespace "Skips whitespace characters on stream s. Returns :line-start, :stream-end, @@ -57,14 +57,14 @@ supporting .unread and collapsing all of CR, LF, and CRLF to a single \\newline." [s] - (loop [c (.read s)] + (loop [c (readers/read-char s)] (case c \newline :line-start nil :stream-end - \; (do (.readLine s) :line-start) + \; (do (readers/read-line s) :line-start) (if (or (Character/isWhitespace c) (identical? c \,)) - (recur (.read s)) - (do (.unread s c) :body))))) + (recur (readers/read-char s)) + (do (readers/unread s c) :body))))) (defn repl-read "Default :read hook for repl. Reads from *in* which must either be an @@ -77,19 +77,17 @@ - reads an object from the input stream, then - skips the next input character if it's end of line, then - returns the object." - [request-prompt request-exit] - (or ({:line-start request-prompt :stream-end request-exit} - (skip-whitespace *in*)) - (let [input (read)] - (skip-if-eol *in*) - input))) - -(defmacro with-read-known - "Evaluates body with *read-eval* set to a \"known\" value, - i.e. substituting true for :unknown if necessary." - [& body] - `(binding [*read-eval* (if (= :unknown *read-eval*) true *read-eval*)] - ~@body)) + ([request-prompt request-exit] + (repl-read request-prompt request-exit *repl-opts*)) + ([request-prompt request-exit opts] + (binding [*in* (if (true? (:source-map-inline opts)) + ((:reader opts)) + *in*)] + (or ({:line-start request-prompt :stream-end request-exit} + (skip-whitespace *in*)) + (let [input (reader/read)] + (skip-if-eol *in*) + input))))) ;; ============================================================================= ;; CLJS Specifics @@ -341,55 +339,57 @@ ([repl-env env filename form wrap] (evaluate-form repl-env env filename form wrap *repl-opts*)) ([repl-env env filename form wrap opts] - (try - (binding [ana/*cljs-file* filename] - (let [ast (ana/analyze env form opts) - js (comp/emit-str ast) - wrap-js - ;; TODO: check opts as well - David - (if (:source-map repl-env) - (binding [comp/*source-map-data* - (atom {:source-map (sorted-map) - :gen-col 0 - :gen-line 0})] - (let [js (comp/emit-str (ana/no-warn (ana/analyze env (wrap form) opts))) - t (System/currentTimeMillis)] - (str js - "\n//# sourceURL=repl-" t ".js" - "\n//# sourceMappingURL=data:application/json;base64," - (DatatypeConverter/printBase64Binary - (.getBytes - (sm/encode - {(str "repl-" t ".cljs") - (:source-map @comp/*source-map-data*)} - {:lines (+ (:gen-line @comp/*source-map-data*) 3) - :file (str "repl-" t ".js") - :sources-content - [(or (:source (meta form)) - ;; handle strings / primitives without metadata - (with-out-str (pr form)))]}) - "UTF-8"))))) - (comp/emit-str (ana/no-warn (ana/analyze env (wrap form) opts))))] - (when (= (:op ast) :ns) - (load-dependencies repl-env - (into (vals (:requires ast)) - (distinct (vals (:uses ast)))) - opts)) - (when *cljs-verbose* - ((:print opts) js)) - (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] - (case (:status ret) - :error (display-error repl-env ret form opts) - :exception (display-error repl-env ret form - (if (:repl-verbose opts) - #(prn "Error evaluating:" form :as js) - (constantly nil)) - opts) - :success (:value ret))))) - (catch Throwable ex - (.printStackTrace ex) - ((:print opts) (str ex)) - ((:flush opts)))))) + (binding [ana/*cljs-file* filename] + (let [ast (ana/analyze env form opts) + js (comp/emit-str ast) + wrap-js + ;; TODO: check opts as well - David + (if (:source-map repl-env) + (binding [comp/*source-map-data* + (atom {:source-map (sorted-map) + :gen-col 0 + :gen-line 0})] + (let [js (comp/emit-str (ana/no-warn (ana/analyze env (wrap form) opts))) + t (System/currentTimeMillis)] + (str js + "\n//# sourceURL=repl-" t ".js" + "\n//# sourceMappingURL=data:application/json;base64," + (DatatypeConverter/printBase64Binary + (.getBytes + (sm/encode + {(str "repl-" t ".cljs") + (:source-map @comp/*source-map-data*)} + {:lines (+ (:gen-line @comp/*source-map-data*) 3) + :file (str "repl-" t ".js") + :sources-content + [(or (:source (meta form)) + ;; handle strings / primitives without metadata + (with-out-str (pr form)))]}) + "UTF-8"))))) + (comp/emit-str (ana/no-warn (ana/analyze env (wrap form) opts))))] + (when (= (:op ast) :ns) + (load-dependencies repl-env + (into (vals (:requires ast)) + (distinct (vals (:uses ast)))) + opts)) + (when *cljs-verbose* + ((:print opts) js)) + (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] + (case (:status ret) + :error (throw + (ex-info (:value ret) + {:type :js-eval-error + :error ret + :repl-env repl-env + :form form})) + :exception (throw + (ex-info (:value ret) + {:type :js-eval-exception + :error ret + :repl-env repl-env + :form form + :js js})) + :success (:value ret))))))) (defn load-stream [repl-env filename res] (let [env (ana/empty-env)] @@ -416,18 +416,21 @@ (load-stream repl-env f res)))))) (defn- wrap-fn [form] - (cond (and (seq? form) (= 'ns (first form))) identity - ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) - :else (fn [x] `(cljs.core.pr-str - (try - (let [ret# ~x] - (set! *3 *2) - (set! *2 *1) - (set! *1 ret#) - ret#) - (catch :default e# - (set! *e e#) - (throw e#))))))) + (cond + (and (seq? form) (= 'ns (first form))) identity + ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) + :else + (fn [x] + `(cljs.core.pr-str + (try + (let [ret# ~x] + (set! *3 *2) + (set! *2 *1) + (set! *1 ret#) + ret#) + (catch :default e# + (set! *e e#) + (throw e#))))))) (defn- eval-cljs "Given a REPL evaluation environment, an analysis environment, and a @@ -534,42 +537,67 @@ (defn repl-prompt [] (print (str "ClojureScript:" ana/*cljs-ns* "> "))) -(defn repl-caught [e] - ) +(defn repl-caught [e opts] + (if (and (instance? IExceptionInfo e) + (#{:js-eval-error :js-eval-exception} (:type (ex-data e)))) + (let [{:keys [type repl-env error form js]} (ex-data e)] + (case type + :js-eval-error + (display-error repl-env error form opts) + + :js-eval-exception + (display-error repl-env error form + (if (:repl-verbose opts) + #(prn "Error evaluating:" form :as js) + (constantly nil)) + opts))) + (binding [*out* *err*] + (.printStackTrace e)))) (defn repl* - [repl-env {:keys [init need-prompt prompt flush read eval print print-no-newline caught] - :or {init #() - need-prompt (if (instance? LineNumberingPushbackReader *in*) - #(.atLineStart ^LineNumberingPushbackReader *in*) - #(identity true)) - prompt repl-prompt - flush flush - read repl-read - eval eval-cljs - print println - caught repl-caught - print-no-newline print} + [repl-env {:keys [init need-prompt prompt flush read eval print caught reader print-no-newline source-map-inline] + :or {init #() + need-prompt #(if (readers/indexing-reader? *in*) + (== (readers/get-column-number *in*) 1) + (identity true)) + prompt repl-prompt + flush flush + read repl-read + eval eval-cljs + print println + caught repl-caught + reader #(readers/source-logging-push-back-reader + (PushbackReader. (io/reader *in*)) + 1 "NO_SOURCE_FILE") + print-no-newline print + source-map-inline true} :as opts}] (print "To quit, type: " :cljs/quit) (let [ups-deps (cljsc/get-upstream-deps) {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} (assoc (merge (-repl-options repl-env) opts) + :init init + :need-prompt prompt + :flush flush + :read read :print print + :caught caught + :reader reader :print-no-newline print-no-newline - :flush flush + :source-map-inline source-map-inline :ups-libs (:libs ups-deps) :ups-foreign-libs (:foreign-libs ups-deps))] (env/with-compiler-env (or (::env/compiler repl-env) (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user *cljs-verbose* repl-verbose - ana/*cljs-warnings* (assoc ana/*cljs-warnings* - :unprovided warn-on-undeclared - :undeclared-var warn-on-undeclared - :undeclared-ns warn-on-undeclared - :undeclared-ns-form warn-on-undeclared) + ana/*cljs-warnings* + (assoc ana/*cljs-warnings* + :unprovided warn-on-undeclared + :undeclared-var warn-on-undeclared + :undeclared-ns warn-on-undeclared + :undeclared-ns-form warn-on-undeclared) ana/*cljs-static-fns* static-fns *repl-opts* opts] ;; TODO: the follow should become dead code when the REPL is @@ -579,10 +607,27 @@ is-special-fn? (set (keys special-fns)) request-prompt (Object.) request-exit (Object.) - read-error (Object.) opts (if-let [merge-opts (:merge-opts (-setup repl-env opts))] (merge opts merge-opts) - opts)] + opts) + read-eval-print + (fn [] + (let [input (binding [*ns* (create-ns ana/*cljs-ns*) + reader/*data-readers* tags/*cljs-data-readers* + reader/*alias-map* + (apply merge + ((juxt :requires :require-macros) + (ana/get-namespace ana/*cljs-ns*)))] + (repl-read request-prompt request-exit))] + (or ({request-exit request-exit + :cljs/quit request-exit + request-prompt request-prompt} input) + (if (and (seq? input) (is-special-fn? (first input))) + (do + ((get special-fns (first input)) repl-env env input opts) + (print nil)) + (let [value (eval repl-env env input opts)] + (print value))))))] (comp/with-core-cljs opts (fn [] (when analyze-path @@ -599,48 +644,27 @@ (print "Watch compilation log available at:" (str log-file)) (binding [*out* (FileWriter. log-file)] (cljsc/watch src (dissoc opts :watch)))))) - (loop [] - ;; try to let things flush before printing prompt - (Thread/sleep 10) + (binding [*in* (if (true? (:source-map-inline opts)) + *in* + (reader))] + (try + (init) + (catch Throwable e + (caught e opts))) (prompt) (flush) - (let [rdr (readers/source-logging-push-back-reader - (PushbackReader. (io/reader *in*)) - 1 - "NO_SOURCE_FILE") - form (try - (binding [*ns* (create-ns ana/*cljs-ns*) - reader/*data-readers* tags/*cljs-data-readers* - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros) - (ana/get-namespace ana/*cljs-ns*)))] - (reader/read rdr nil read-error)) - (catch Exception e - (print (.getMessage e)) - read-error))] - ;; TODO: need to catch errors here too - David - (cond - (identical? form read-error) (recur) - (= form :cljs/quit) :quit - - (and (seq? form) (is-special-fn? (first form))) - (do - (try - ((get special-fns (first form)) repl-env env form opts) - (catch Throwable ex - (print "Failed to execute special function:" (pr-str (first form))) - (trace/print-cause-trace ex 12))) - ;; flush output which could include stack traces - (flush) - (print) - (recur)) - - :else - (let [value (eval repl-env env form opts)] - (print value) - (recur))))))) - (-tear-down repl-env)))))) + (loop [] + (when-not + (try + (identical? (read-eval-print) request-exit) + (catch Throwable e + (caught e opts) + nil)) + (when (need-prompt) + (prompt) + (flush)) + (recur))))))) + (-tear-down repl-env))))) (defn repl "Note - repl will reload core.cljs every time, even if supplied old repl-env" From bf2fbcd0b6b926451aa1f66ed9d33cf85686b97e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Feb 2015 16:22:44 -0500 Subject: [PATCH 0694/4033] initial docstring for cljs.repl/repl --- src/clj/cljs/repl.clj | 66 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 1e6307942..39e622690 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -667,7 +667,71 @@ (-tear-down repl-env))))) (defn repl - "Note - repl will reload core.cljs every time, even if supplied old repl-env" + "Generic, reusable, read-eval-print loop. By default, reads from *in* using + a c.t.r.reader-types/source-logging-push-back-reader, + writes to *out*, and prints exception summaries to *err*. If you use the + default :read hook, *in* must either be an instance of + c.t.r.reader-types/PushbackReader or duplicate its behavior of both supporting + unread and collapsing CR, LF, and CRLF into a single \\newline. Options + are sequential keyword-value pairs. The first argument is the JavaScript + evaluation environment, the second argument is an extended version of the + standard ClojureScript compiler options. In addition to ClojureScript compiler + build options it also take a set of options similar to clojure.main/repl with + adjustments for ClojureScript evalution and compilation model: + + Available clojure.main/repl style options and their defaults: + + - :init, function of no arguments, initialization hook called with + bindings for set!-able vars in place. + default: #() + + - :need-prompt, function of no arguments, called before each + read-eval-print except the first, the user will be prompted if it + returns true. + default: #(if (c.t.r.readers-types/indexing-reader? *in*) + (== (c.t.r.reader-types/get-column-number *in*) 1) + (identity true)) + + - :prompt, function of no arguments, prompts for more input. + default: repl-prompt + + - :flush, function of no arguments, flushes output + default: flush + + - :read, function of two arguments, reads from *in*: + - returns its first argument to request a fresh prompt + - depending on need-prompt, this may cause the repl to prompt + before reading again + - returns its second argument to request an exit from the repl + - else returns the next object read from the input stream + default: repl-read + + - :eval, function of one argument, returns the evaluation of its + argument. The eval function must take repl-env, the JavaScript evaluation + envrionment, env, the ClojureScript analysis environemnt, the form + and opts, the standard ClojureScript REPL/compiler options. + default: eval + + - :print, function of one argument, prints its argument to the output + default: println + + - :caught, function of two arguments, a throwable, called when + read, eval, or print throws an exception or error + default. The second argument is opts, the standard ClojureScript + REPL/compiler options. In the case of errors or exception in the + JavaScript target, these will be thrown as clojure.lang.IExceptionInfo + instances. + default: repl-caught + + - :reader, the c.t.r reader to use. + default: c.t.r.read-types/source-logging-push-back-reader + + - :print-no-newline, print without a newline. + deafult: print + + - :source-map-inline, whether inline source maps should be enabled. Most + useful in browser context. Implies using a fresh reader for each form. + default: true" [repl-env & {:as opts}] (repl* repl-env opts)) From db8208a362d14dc691f095d51e49e1a0053e91fd Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Feb 2015 17:28:21 -0500 Subject: [PATCH 0695/4033] more disciplined error handling around the configurable bits of the REPL caught is now a function of three arguments, the throwable, the JavaScript evaluation environment, and the repl/compiler options. anywhere a error may be thrown due to user customization wrap in try/catch and defer to caught --- src/clj/cljs/repl.clj | 55 ++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 39e622690..88cc13e76 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -537,7 +537,7 @@ (defn repl-prompt [] (print (str "ClojureScript:" ana/*cljs-ns* "> "))) -(defn repl-caught [e opts] +(defn repl-caught [e repl-env opts] (if (and (instance? IExceptionInfo e) (#{:js-eval-error :js-eval-exception} (:type (ex-data e)))) (let [{:keys [type repl-env error form js]} (ex-data e)] @@ -607,9 +607,12 @@ is-special-fn? (set (keys special-fns)) request-prompt (Object.) request-exit (Object.) - opts (if-let [merge-opts (:merge-opts (-setup repl-env opts))] - (merge opts merge-opts) - opts) + opts (try + (if-let [merge-opts (:merge-opts (-setup repl-env opts))] + (merge opts merge-opts) + opts) + (catch Throwable e + (caught e repl-env opts))) read-eval-print (fn [] (let [input (binding [*ns* (create-ns ana/*cljs-ns*) @@ -630,27 +633,34 @@ (print value))))))] (comp/with-core-cljs opts (fn [] - (when analyze-path - (analyze-source analyze-path opts)) - (evaluate-form repl-env env "" - (with-meta - '(ns cljs.user - (:require [cljs.repl :refer-macros [doc]])) - {:line 1 :column 1}) - identity opts) + (try + (when analyze-path + (analyze-source analyze-path opts)) + (evaluate-form repl-env env "" + (with-meta + '(ns cljs.user + (:require [cljs.repl :refer-macros [doc]])) + {:line 1 :column 1}) + identity opts) + (catch Throwable e + (caught e repl-env opts))) (when-let [src (:watch opts)] (future (let [log-file (io/file (util/output-directory opts) "watch.log")] (print "Watch compilation log available at:" (str log-file)) - (binding [*out* (FileWriter. log-file)] - (cljsc/watch src (dissoc opts :watch)))))) + (try + (binding [*out* (FileWriter. log-file)] + (cljsc/watch src (dissoc opts :watch))) + (catch Throwable e + (caught e repl-env opts)))))) (binding [*in* (if (true? (:source-map-inline opts)) *in* (reader))] (try (init) (catch Throwable e - (caught e opts))) + (caught e repl-env opts))) + ;; try to let things flush before printing prompt (prompt) (flush) (loop [] @@ -658,7 +668,7 @@ (try (identical? (read-eval-print) request-exit) (catch Throwable e - (caught e opts) + (caught e repl-env opts) nil)) (when (need-prompt) (prompt) @@ -715,12 +725,13 @@ - :print, function of one argument, prints its argument to the output default: println - - :caught, function of two arguments, a throwable, called when - read, eval, or print throws an exception or error - default. The second argument is opts, the standard ClojureScript - REPL/compiler options. In the case of errors or exception in the - JavaScript target, these will be thrown as clojure.lang.IExceptionInfo - instances. + - :caught, function of three arguments, a throwable, called when + read, eval, or print throws an exception or error default. The second + argument is the JavaScript evaluation environment this permits context + sensitive handling if necessary. The third argument is opts, the standard + ClojureScript REPL/compiler options. In the case of errors or exception + in the JavaScript target, these will be thrown as + clojure.lang.IExceptionInfo instances. default: repl-caught - :reader, the c.t.r reader to use. From 11c7c3456c3fba07f6dacec964d48d8394a908f8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Feb 2015 17:31:55 -0500 Subject: [PATCH 0696/4033] return opts from JavaScript evaluation environment -setup try/catch --- src/clj/cljs/repl.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 88cc13e76..04ba027b8 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -612,7 +612,8 @@ (merge opts merge-opts) opts) (catch Throwable e - (caught e repl-env opts))) + (caught e repl-env opts) + opts)) read-eval-print (fn [] (let [input (binding [*ns* (create-ns ana/*cljs-ns*) From 36ea4785c2345a04985ebe504403061a8af477dd Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Feb 2015 17:34:44 -0500 Subject: [PATCH 0697/4033] `cjls.repl/repl` docstring typos pointed out by Mike Fikes --- src/clj/cljs/repl.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 04ba027b8..c5723eead 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -719,7 +719,7 @@ - :eval, function of one argument, returns the evaluation of its argument. The eval function must take repl-env, the JavaScript evaluation - envrionment, env, the ClojureScript analysis environemnt, the form + environment, env, the ClojureScript analysis environment, the form and opts, the standard ClojureScript REPL/compiler options. default: eval @@ -736,10 +736,10 @@ default: repl-caught - :reader, the c.t.r reader to use. - default: c.t.r.read-types/source-logging-push-back-reader + default: c.t.r.reader-types/source-logging-push-back-reader - :print-no-newline, print without a newline. - deafult: print + default: print - :source-map-inline, whether inline source maps should be enabled. Most useful in browser context. Implies using a fresh reader for each form. From d000b15168122bd3ad31ed5da78d2eb546223aaa Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Feb 2015 17:56:52 -0500 Subject: [PATCH 0698/4033] CLJS-1039: Under Emacs source directory watching triggers spurious recompilation --- src/clj/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index dda1783bd..a0e43c81f 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1467,8 +1467,10 @@ should contain the source for the given namespace name." (when (or (nil? key) (. ^WatchKey key reset)) (let [key (. service take)] (when (some (fn [^WatchEvent e] - (or (.. (. e context) toString (endsWith "cljs")) - (.. (. e context) toString (endsWith "js")))) + (let [fstr (.. e context toString)] + (and (or (. fstr (endsWith "cljs")) + (. fstr (endsWith "js"))) + (not (. fstr (startsWith ".#")))))) (seq (.pollEvents key))) (println "Change detected, recompiling...") (flush) From a47c87f7c8e507f53153ec3d525335741f10887b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Feb 2015 19:36:08 -0500 Subject: [PATCH 0699/4033] clarify foreign dep loading with some comments --- src/clj/cljs/compiler.clj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index e7a796f3f..a8df5712d 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -814,12 +814,17 @@ (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond - ;; only emit if foreign lib under Node.js (ana/foreign-dep? lib) + ;; under node.js we load foreign libs globally (if (= :nodejs (get-in @env/*compiler* [:options :target])) (let [js-index (:js-dependency-index @env/*compiler*) ijs-url (get-in js-index [(name lib) :url])] (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");")) + ;; otherwise only include if set in the options to do so, + ;; in the browser unnecessary due to the fact that goog.require + ;; there works by writing all deps as script tags, this doesn't + ;; work in Rhino-like environment where we do a proper goog.require + ;; on demand (when (get-in @env/*compiler* [:options :require-foreign]) (emitln "goog.require('" (munge lib) "');"))) From f6d46c46068b52f741d237f72a447f4de6c58ad3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Feb 2015 21:26:22 -0500 Subject: [PATCH 0700/4033] CLJS-1051: :modules validation, :output-to should be unique --- src/clj/cljs/closure.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index a0e43c81f..16c9a8a7c 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -822,6 +822,11 @@ should contain the source for the given namespace name." ;; lein-cljsbuild adds :output-to? #_{:pre [(and (contains? opts :modules) (not (contains? opts :output-to)))]} + (assert (= (count (:modules opts)) + (count (into #{} + (map (comp :output-to second) + (:modules opts))))) + "Each :output-to of :modules must be unique") (let [closure-compiler (make-closure-compiler) ^List externs (load-externs opts) compiler-options (make-options opts) From ab3c8a5cbb9fb1ccdf7fb9355b8853ebbfc79661 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 08:38:59 -0500 Subject: [PATCH 0701/4033] CLJS-452: clojure.browser.net, enable WebSockets This does not add any new functionality it just cleans up and exposes the WebSocket code should someone want to leverage it. --- src/cljs/clojure/browser/net.cljs | 37 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/cljs/clojure/browser/net.cljs b/src/cljs/clojure/browser/net.cljs index 8d31880e6..bfd66dc06 100644 --- a/src/cljs/clojure/browser/net.cljs +++ b/src/cljs/clojure/browser/net.cljs @@ -12,9 +12,9 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." clojure.browser.net (:require [clojure.browser.event :as event] [goog.json :as gjson]) - (:import (goog.net XhrIo EventType) - (goog.net.xpc CfgFields CrossPageChannel) - goog.Uri)) + (:import [goog.net XhrIo EventType WebSocket] + [goog.net.xpc CfgFields CrossPageChannel] + [goog Uri])) (def *timeout* 10000) @@ -140,11 +140,10 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." ;; WebSocket is not supported in the 3/23/11 release of Google ;; Closure, but will be included in the next release. -#_(defprotocol IWebSocket - (open? [this])) - -#_(extend-type goog.net.WebSocket +(defprotocol IWebSocket + (open? [this])) +(extend-type WebSocket IWebSocket (open? [this] (.isOpen this ())) @@ -152,9 +151,9 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." IConnection (connect ([this url] - (connect this url nil)) + (connect this url nil)) ([this url protocol] - (.open this url protocol))) + (.open this url protocol))) (transmit [this message] (.send this message)) @@ -162,20 +161,20 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." (close [this] (.close this ())) - event/EventType + event/IEventType (event-types [this] (into {} - (map - (fn [[k v]] - [(keyword (. k (toLowerCase))) - v]) - (merge - (js->clj goog.net.WebSocket/EventType)))))) - -#_(defn websocket-connection + (map + (fn [[k v]] + [(keyword (. k (toLowerCase))) + v]) + (merge + (js->clj WebSocket.EventType)))))) + +(defn websocket-connection ([] (websocket-connection nil nil)) ([auto-reconnect?] (websocket-connection auto-reconnect? nil)) ([auto-reconnect? next-reconnect-fn] - (goog.net.WebSocket. auto-reconnect? next-reconnect-fn))) + (WebSocket. auto-reconnect? next-reconnect-fn))) From be1600ce8c0785e53e5c06868fe307a9acddfd73 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 09:50:43 -0500 Subject: [PATCH 0702/4033] CLJS-975: preserve :reload & :reload-all in ns macro sugar allow :reload and :reload-all to flow through `cljs.analyzer/desugar-ns-macros` add test case --- src/clj/cljs/analyzer.clj | 25 ++++++++++++++++++------- test/clj/cljs/analyzer_tests.clj | 9 +++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 5cfe6d245..d989c15be 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1271,18 +1271,29 @@ (if-not (sequential? spec) spec (map (fn [x] (if (= x :refer-macros) :refer x)) spec))) + reload-spec? #(#{:reload :reload-all} %) to-macro-specs (fn [specs] (->> specs - (filter #(or (and (sequential? %) - (some sugar-keys %)) - (macro-autoload-ns? %))) - (map #(->> % (remove-from-spec #{:include-macros}) - (remove-from-spec #{:refer}) - (replace-refer-macros))))) + (filter + (fn [x] + (or (and (sequential? x) + (some sugar-keys x)) + (reload-spec? x) + (macro-autoload-ns? x)))) + (map (fn [x] + (if-not (reload-spec? x) + (->> x (remove-from-spec #{:include-macros}) + (remove-from-spec #{:refer}) + (replace-refer-macros)) + x))))) remove-sugar (partial remove-from-spec sugar-keys)] (if-let [require-specs (seq (to-macro-specs require))] - (map (fn [[k v]] (cons k (map remove-sugar v))) + (map (fn [x] + (if-not (reload-spec? x) + (let [[k v] x] + (cons k (map remove-sugar v))) + x)) (update-in indexed [:require-macros] (fnil into []) require-specs)) args))) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index 2cffc9010..3eef6cee6 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -255,6 +255,15 @@ (.getMessage e))) "Parameter declaration 123 should be a vector at line"))) +;; ============================================================================= +;; ns desugaring + +(deftest test-cljs-975 + (let [spec '((:require [bar :refer [baz] :refer-macros [quux]] :reload))] + (is (= (a/desugar-ns-specs spec) + '((:require-macros (bar :refer [quux]) :reload) + (:require (bar :refer [baz]) :reload)))))) + ;; ============================================================================= ;; Namespace metadata From 3a856c83e8c1c4b1363f4c0ff0803646aec1385b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 10:28:10 -0500 Subject: [PATCH 0703/4033] CLJS-1052: Cannot require ns from within the ns at the REPL for reloading purposes Detect self requires, switch to the user ns if necessary and switch back --- src/clj/cljs/repl.clj | 90 ++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c5723eead..55d20c649 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -461,6 +461,17 @@ ;; form - complete form entered at the repl ;; opts - REPL options, essentially augmented cljs.closure/build options +(defn self-require? [specs] + (some + (fn [quoted-spec-or-kw] + (and (not (keyword? quoted-spec-or-kw)) + (let [spec (second quoted-spec-or-kw) + ns (if (sequential? spec) + (first spec) + spec)] + (= ns ana/*cljs-ns*)))) + specs)) + (def default-special-fns (let [load-file-fn (fn self @@ -471,58 +482,65 @@ {'in-ns (fn self ([repl-env env form] - (self repl-env env form nil)) + (self repl-env env form nil)) ([repl-env env [_ [quote ns-name] :as form] _] ;; guard against craziness like '5 which wreaks havoc - (when-not (and (= quote 'quote) (symbol? ns-name)) - (throw (IllegalArgumentException. "Argument to in-ns must be a symbol."))) - (when-not (ana/get-namespace ns-name) - (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) - (-evaluate repl-env "" 1 - (str "goog.provide('" (comp/munge ns-name) "');"))) - (set! ana/*cljs-ns* ns-name))) + (when-not (and (= quote 'quote) (symbol? ns-name)) + (throw (IllegalArgumentException. "Argument to in-ns must be a symbol."))) + (when-not (ana/get-namespace ns-name) + (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) + (-evaluate repl-env "" 1 + (str "goog.provide('" (comp/munge ns-name) "');"))) + (set! ana/*cljs-ns* ns-name))) 'require (fn self ([repl-env env form] - (self repl-env env form nil)) + (self repl-env env form nil)) ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) - {:merge true :line 1 :column 1}) - identity opts))) + (let [is-self-require? (self-require? specs) + [target-ns restore-ns] + (if-not is-self-require? + [ana/*cljs-ns* nil] + ['cljs.user ana/*cljs-ns*])] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~target-ns + (:require + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts) + (when is-self-require? + (set! ana/*cljs-ns* restore-ns))))) 'require-macros (fn self ([repl-env env form] - (self repl-env env form nil)) + (self repl-env env form nil)) ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require-macros - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) - {:merge true :line 1 :column 1}) - identity opts))) + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require-macros + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace (fn self ([repl-env env form] - (self env repl-env form nil)) + (self env repl-env form nil)) ([repl-env env [_ ns :as form] opts] - (load-namespace repl-env ns opts)))})) + (load-namespace repl-env ns opts)))})) (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate From 3d8c29ebcc38768b2c1a837fafb09d5baead3078 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 10:41:44 -0500 Subject: [PATCH 0704/4033] CLJS-998: Nashorn REPL does not support require special fn --- src/clj/cljs/repl/nashorn.clj | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index e0932e7e1..80fe5b1ac 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -155,7 +155,20 @@ (repl/evaluate-form this env repl-filename '(do (.require js/goog "cljs.core") - (cljs.core/enable-console-print!))))) + (cljs.core/enable-console-print!))) + ;; monkey-patch goog.isProvided_ to suppress useless errors + (repl/evaluate-form this env repl-filename + '(set! js/goog.isProvided_ (fn [ns] false))) + ;; monkey-patch goog.require to be more sensible + (repl/evaluate-form this env repl-filename + '(do + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) (-evaluate [{engine :engine :as this} filename line js] (when debug (println "Evaluating: " js)) (try From 6816755a7a7d6e665db5e6244772ccb39df675be Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 10:41:59 -0500 Subject: [PATCH 0705/4033] fix prompt printing --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 55d20c649..41f89f868 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -590,7 +590,7 @@ print-no-newline print source-map-inline true} :as opts}] - (print "To quit, type: " :cljs/quit) + (print "To quit, type:" :cljs/quit) (let [ups-deps (cljsc/get-upstream-deps) {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} From 01be227e07c4542eec548ffe8e35be72afd9326d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 11:47:27 -0500 Subject: [PATCH 0706/4033] remove extra newline from `cljs.util/measure` --- src/clj/cljs/util.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index e7a1f2faa..41749d296 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -138,6 +138,5 @@ (let [start# (. System (nanoTime)) ret# ~expr] (debug-prn (str ~msg ", elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs")) - (debug-prn "") ret#) ~expr))) From 567db6e997ace0bb417f6ecca8fbabcc9f0eb7f9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 12:08:15 -0500 Subject: [PATCH 0707/4033] 0.0-2911 --- README.md | 6 +++--- changes.md | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 50d56d682..153337e8e 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2850 +Latest stable release: 0.0-2911 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2850"] +[org.clojure/clojurescript "0.0-2911"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2850 org.clojure clojurescript - 0.0-2850 + 0.0-2911 ``` diff --git a/changes.md b/changes.md index b3a52c83b..8331c0326 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,29 @@ +## 0.0-2911 + +### Enhancements +* CLJS-1042: Google Closure Modules :source-map support +* CLJS-1041: Google Closure Modules :foreign-libs support +* Google Closure Modules support via :modules +* CLJS-1040: Source-mapped script stack frames for the Nashorn repl + +### Changes +* CLJS-960: On carriage return REPLs should always show new REPL prompt +* CLJS-941: Warn when a symbol is defined multiple times in a file +* REPLs now support parameterization a la clojure.main/repl +* all REPLs analyze cljs.core before entering loop +* can emit :closure-source-map option for preserving JS->JS map +* REPLs can now merge new REPL/compiler options via -setup + +### Fixes +* CLJS-998: Nashorn REPL does not support require special fn +* CLJS-1052: Cannot require ns from within the ns at the REPL for reloading purposes +* CLJS-975: preserve :reload & :reload-all in ns macro sugar +* CLJS-1039: Under Emacs source directory watching triggers spurious recompilation +* CLJS-1046: static vars do not respect user compile time metadata +* CLJS-989: ClojureScript REPL loops on EOF signal +* fix DCE regression for trivial programs +* CLJS-1036: use getResources not findResources in get-upstream-deps* + ## 0.0-2850 ### Enhancements From 925bb9ea01b5d5bf82aede5399e4ed1b178e6783 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 12:47:08 -0500 Subject: [PATCH 0708/4033] support specifying custom :output-to for :cljs-base module --- src/clj/cljs/closure.clj | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 16c9a8a7c..eee794f1f 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -613,20 +613,24 @@ should contain the source for the given namespace name." (defn add-cljs-base-module ([modules] (add-cljs-base-module modules nil)) ([modules opts] - (reduce - (fn [modules module-name] - (update-in modules [module-name :depends-on] - (fnil conj #{}) :cljs-base)) - (assoc modules :cljs-base - {:output-to + (reduce + (fn [modules module-name] + (if-not (= module-name :cljs-base) + (update-in modules [module-name :depends-on] + (fnil conj #{}) :cljs-base) + modules)) + (update-in modules [:cljs-base :output-to] + (fnil io/file (io/file (util/output-directory opts) - "cljs_base.js")}) - (keys modules)))) + "cljs_base.js"))) + (keys modules)))) (comment (add-cljs-base-module - {:core + {:cljs-base + {:output-to "out/modules/base.js"} + :core {:output-to "out/modules/core.js" :entries '#{cljs.core}} :landing @@ -649,7 +653,9 @@ should contain the source for the given namespace name." (comment (sort-modules (add-cljs-base-module - {:core + {:cljs-base + {:output-to "out/module/base.js"} + :core {:output-to "out/modules/core.js" :entries '#{cljs.core}} :landing From f1eedf046f0e7d17746e368cf9fe4b537704259f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 12:49:58 -0500 Subject: [PATCH 0709/4033] 0.0-2913 --- README.md | 6 +++--- changes.md | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 153337e8e..805d7f53c 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2911 +Latest stable release: 0.0-2913 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2911"] +[org.clojure/clojurescript "0.0-2913"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2911 org.clojure clojurescript - 0.0-2911 + 0.0-2913 ``` diff --git a/changes.md b/changes.md index 8331c0326..b7f657272 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,6 @@ +## 0.0-2913 +* Support custom :output-to for :cljs-base module + ## 0.0-2911 ### Enhancements From 7d51c69872aa09e5a7ad15e00551b89d94ff72f1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Feb 2015 23:09:58 -0500 Subject: [PATCH 0710/4033] remove dead comment --- src/clj/cljs/repl.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 41f89f868..1e49c116b 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -679,7 +679,6 @@ (init) (catch Throwable e (caught e repl-env opts))) - ;; try to let things flush before printing prompt (prompt) (flush) (loop [] From 4bf09f3232a77856aa46944b5814af06348b9c77 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 09:18:21 -0500 Subject: [PATCH 0711/4033] CLJS-1053: REPLs need import special fn --- src/clj/cljs/repl.clj | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 1e49c116b..1726d2735 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -516,6 +516,23 @@ identity opts) (when is-self-require? (set! ana/*cljs-ns* restore-ns))))) + 'import + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:import + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts))) 'require-macros (fn self ([repl-env env form] From 81541c5c2a7e0faee198b8aabc720e4913e453be Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 09:46:11 -0500 Subject: [PATCH 0712/4033] deprecate clojure.reflect namespace now that REPLs are significantly enhanced, static vars, etc. --- src/cljs/clojure/reflect.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cljs/clojure/reflect.cljs b/src/cljs/clojure/reflect.cljs index 50589d9e2..7049bed4c 100644 --- a/src/cljs/clojure/reflect.cljs +++ b/src/cljs/clojure/reflect.cljs @@ -1,4 +1,5 @@ (ns clojure.reflect + ^{:doc "DEPRECATED. Do not use, superceded by REPL enhancements."} (:refer-clojure :exclude [meta]) (:require [clojure.browser.net :as net] [clojure.browser.event :as event])) From 3e8991691d4eaf60032643ef818d24fa626b692d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 09:51:54 -0500 Subject: [PATCH 0713/4033] add preconditions to `cljs.compiler/with-core-cljs` --- src/clj/cljs/compiler.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index a8df5712d..1e9dd8770 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -907,6 +907,8 @@ ([] (with-core-cljs nil)) ([opts] (with-core-cljs opts (fn []))) ([opts body] + {:pre [(or (nil? opts) (map? opts)) + (fn? body)]} (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) (ana/analyze-file "cljs/core.cljs" opts)) (body))) From f70f40d54caf6137cc453d6ff914897102233919 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 10:08:56 -0500 Subject: [PATCH 0714/4033] CLJS-1054: add clojure.repl/source functionality to cljs.repl --- src/clj/cljs/repl.clj | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 1726d2735..6c18c07b3 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -675,7 +675,7 @@ (evaluate-form repl-env env "" (with-meta '(ns cljs.user - (:require [cljs.repl :refer-macros [doc]])) + (:require [cljs.repl :refer-macros [source doc]])) {:line 1 :column 1}) identity opts) (catch Throwable e @@ -788,3 +788,41 @@ "Prints documentation for a var or special form given its name" [sym] `(cljs.repl/print-doc (meta (var ~sym)))) + +(defn source-fn + "Returns a string of the source code for the given symbol, if it can + find it. This requires that the symbol resolve to a Var defined in + a namespace for which the .clj is in the classpath. Returns nil if + it can't find the source. For most REPL usage, 'source' is more + convenient. + + Example: (source-fn 'filter)" + [env x] + (when-let [v (ana/resolve-var env x)] + (when-let [filepath (:file v)] + (let [f (io/file filepath)] + (when (.exists f) + (with-open [pbr (PushbackReader. (io/reader f))] + (let [rdr (readers/source-logging-push-back-reader pbr)] + (dotimes [_ (dec (:line v))] (readers/read-line rdr)) + (-> (reader/read rdr) meta :source)))))))) + +(comment + (def cenv (env/default-compiler-env)) + (def aenv (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) + + (binding [ana/*cljs-ns* 'cljs.user] + (env/with-compiler-env cenv + (comp/with-core-cljs {} + (fn [] + (source-fn aenv 'cljs.core/first))))) + ) + +(defmacro source + "Prints the source code for the given symbol, if it can find it. + This requires that the symbol resolve to a Var defined in a + namespace for which the .clj is in the classpath. + + Example: (source filter)" + [n] + `(println ~(or (source-fn &env n) (str "Source not found")))) From b7c688b935d7f3cc6e38577df590ec5f24e51581 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 10:16:51 -0500 Subject: [PATCH 0715/4033] Add ClojureScript special form doc map --- src/clj/cljs/repl.clj | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 6c18c07b3..2e99daf2c 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -784,6 +784,49 @@ ;; ============================================================================= ;; ClojureScript REPL interaction support +(def special-doc-map + '{. {:forms [(.instanceMethod instance args*) + (.-instanceField instance)] + :doc "The instance member form works for methods and fields. + They all expand into calls to the dot operator at macroexpansion time."} + def {:forms [(def symbol doc-string? init?)] + :doc "Creates and interns a global var with the name + of symbol in the current namespace (*ns*) or locates such a var if + it already exists. If init is supplied, it is evaluated, and the + root binding of the var is set to the resulting value. If init is + not supplied, the root binding of the var is unaffected."} + do {:forms [(do exprs*)] + :doc "Evaluates the expressions in order and returns the value of + the last. If no expressions are supplied, returns nil."} + if {:forms [(if test then else?)] + :doc "Evaluates test. If not the singular values nil or false, + evaluates and yields then, otherwise, evaluates and yields else. If + else is not supplied it defaults to nil."} + new {:forms [(Classname. args*) (new Classname args*)] + :url "java_interop#new" + :doc "The args, if any, are evaluated from left to right, and + passed to the JavaScript constructor. The constructed object is + returned."} + quote {:forms [(quote form)] + :doc "Yields the unevaluated form."} + recur {:forms [(recur exprs*)] + :doc "Evaluates the exprs in order, then, in parallel, rebinds + the bindings of the recursion point to the values of the exprs. + Execution then jumps back to the recursion point, a loop or fn method."} + set! {:forms[(set! var-symbol expr) + (set! (. instance-expr instanceFieldName-symbol) expr)] + :url "vars#set" + :doc "Used to set vars and JavaScript object fields"} + throw {:forms [(throw expr)] + :doc "The expr is evaluated and thrown."} + try {:forms [(try expr* catch-clause* finally-clause?)] + :doc "catch-clause => (catch classname name expr*) + finally-clause => (finally expr*) + Catches and handles JavaScript exceptions."} + var {:forms [(var symbol)] + :doc "The symbol must resolve to a var, and the Var object +itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) + (defmacro doc "Prints documentation for a var or special form given its name" [sym] From e8bf46614cd767c8497491d75c93c072a0f3aba5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 11:11:37 -0500 Subject: [PATCH 0716/4033] CLJS-1055: cljs.repl/doc should support namespaces and special forms --- src/clj/cljs/analyzer.clj | 3 ++- src/clj/cljs/repl.clj | 25 +++++++++++++++++++++++-- src/cljs/cljs/repl.cljs | 3 +-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index d989c15be..2bb69af36 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1304,6 +1304,7 @@ (when (= 1 (count (string/split (clojure.core/name name) #"\."))) (warning :single-segment-namespace env {:name name})) (let [docstring (if (string? (first args)) (first args)) + mdocstr (-> name meta :doc) args (if docstring (next args) args) metadata (if (map? (first args)) (first args)) form-meta (meta form) @@ -1357,7 +1358,7 @@ (check-use-macros use-macros env))) (let [ns-info {:name name - :doc docstring + :doc (or docstring mdocstr) :excludes excludes :use-macros use-macros :require-macros require-macros diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 2e99daf2c..a41346536 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -18,6 +18,7 @@ [cljs.util :as util] [cljs.compiler :as comp] [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] [cljs.env :as env] [cljs.tagged-literals :as tags] [cljs.closure :as cljsc] @@ -827,10 +828,30 @@ :doc "The symbol must resolve to a var, and the Var object itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) +(defn- special-doc [name-symbol] + (assoc (or (special-doc-map name-symbol) (meta (resolve name-symbol))) + :name name-symbol + :special-form true)) + (defmacro doc "Prints documentation for a var or special form given its name" - [sym] - `(cljs.repl/print-doc (meta (var ~sym)))) + [name] + (if-let [special-name ('{& fn catch try finally try} name)] + `(cljs.repl/print-doc (quote ~(special-doc special-name))) + (cond + (special-doc-map name) + `(cljs.repl/print-doc (quote ~(special-doc name))) + + (ana-api/find-ns name) + `(cljs.repl/print-doc + (quote ~(select-keys (ana-api/find-ns name) [:name :doc]))) + + (ana-api/resolve &env name) + `(cljs.repl/print-doc + (quote ~(update-in + (select-keys (ana-api/resolve &env name) + [:ns :name :doc :forms :arglists]) + [:name] clojure.core/name)))))) (defn source-fn "Returns a string of the source code for the given symbol, if it can diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index dab4f9b62..b40f1f83c 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -14,8 +14,7 @@ (println (str (when-let [ns (:ns m)] (str ns "/")) (:name m))) (cond (:forms m) (doseq [f (:forms m)] - (print " ") - (prn f)) + (println " " f)) (:arglists m) (prn (:arglists m))) (if (:special-form m) (do From 4476508292dcc174cafa07456ec4cbf65ce596d0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 11:59:13 -0500 Subject: [PATCH 0717/4033] add `cljs.analyzer.api/ns-publics` --- src/clj/cljs/analyzer/api.clj | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index d37cf55fe..3c5447b37 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -7,7 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.analyzer.api - (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve find-ns]) + (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve find-ns + ns-publics]) (:require [cljs.env :as env] [cljs.analyzer :as ana])) @@ -41,6 +42,15 @@ {:pre [(symbol? ns)]} (get-in @env/*compiler* [::ana/namespaces ns :defs])) +(defn ns-publics + "Given a namespace return all the public var analysis maps. Analagous to + clojure.core/ns-publics but returns var analysis maps not vars." + [ns] + {:pre [(symbol? ns)]} + (->> (get-in @env/*compiler* [::ana/namespaces ns :defs]) + (remove (fn [[k v]] (:private v))) + (into {}))) + (defn ns-resolve "Given a namespace and a symbol return the corresponding var analysis map. Analagous to clojure.core/ns-resolve but returns var analysis map not Var." From 9b3b0ac36dd7594c6acd9768445e480218c18a1e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 12:44:21 -0500 Subject: [PATCH 0718/4033] fix `cljs.analyzer.api/all-ns` docstring --- src/clj/cljs/analyzer/api.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index 3c5447b37..88711a055 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -23,8 +23,8 @@ (catch Exception e))) (defn all-ns - "Return all the namespace analysis maps. Analagous to clojure.core/all-ns but - returns analysis maps not Namespace instances." + "Return all namespaces. Analagous to clojure.core/all-ns but + returns symbols identifying namespaces not Namespace instances." [] (keys (get @env/*compiler* ::ana/namespaces))) From 6b68b4b1da11d66a54d4f068530bf1a7bc6e2e70 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 12:45:12 -0500 Subject: [PATCH 0719/4033] add `cljs.repl/apropos`, `cljs.repl/find-doc`, `cljs.repl/dir` --- src/clj/cljs/repl.clj | 52 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index a41346536..988f0ff7b 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -26,7 +26,8 @@ (:import [java.io File PushbackReader FileWriter] [java.net URL] [javax.xml.bind DatatypeConverter] - [clojure.lang IExceptionInfo])) + [clojure.lang IExceptionInfo] + [java.util.regex Pattern])) (def ^:dynamic *cljs-verbose* false) (def ^:dynamic *repl-opts* nil) @@ -676,7 +677,7 @@ (evaluate-form repl-env env "" (with-meta '(ns cljs.user - (:require [cljs.repl :refer-macros [source doc]])) + (:require [cljs.repl :refer-macros [source doc find-doc apropos dir]])) {:line 1 :column 1}) identity opts) (catch Throwable e @@ -853,6 +854,30 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) [:ns :name :doc :forms :arglists]) [:name] clojure.core/name)))))) +(defmacro find-doc + "Prints documentation for any var whose documentation or name + contains a match for re-string-or-pattern" + [re-string-or-pattern] + (let [re (re-pattern re-string-or-pattern) + ms (concat + (mapcat + (fn [ns] + (map + (fn [m] + (update-in (select-keys m [:ns :name :doc :forms :arglists]) + [:name] clojure.core/name)) + (sort-by :name (vals (ana-api/ns-interns ns))))) + (ana-api/all-ns)) + (map #(select-keys (ana-api/find-ns %) [:name :doc]) (ana-api/all-ns)) + (map special-doc (keys special-doc-map))) + ms (for [m ms + :when (and (:doc m) + (or (re-find (re-matcher re (:doc m))) + (re-find (re-matcher re (str (:name m))))))] + m)] + `(doseq [m# (quote ~ms)] + (cljs.repl/print-doc m#)))) + (defn source-fn "Returns a string of the source code for the given symbol, if it can find it. This requires that the symbol resolve to a Var defined in @@ -890,3 +915,26 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) Example: (source filter)" [n] `(println ~(or (source-fn &env n) (str "Source not found")))) + +(defmacro apropos + "Given a regular expression or stringable thing, return a seq of all +public definitions in all currently-loaded namespaces that match the +str-or-pattern." + [str-or-pattern] + (let [matches? (if (instance? Pattern str-or-pattern) + #(re-find str-or-pattern (str %)) + #(.contains (str %) (str str-or-pattern)))] + `(quote + ~(sort + (mapcat + (fn [ns] + (let [ns-name (str ns)] + (map #(symbol ns-name (str %)) + (filter matches? (keys (ana-api/ns-publics ns)))))) + (ana-api/all-ns)))))) + +(defmacro dir + "Prints a sorted directory of public vars in a namespace" + [ns] + `(doseq [sym# (quote ~(sort (keys (ana-api/ns-publics ns))))] + (println sym#))) From 5218a4f4d88db4483eefea80da66acfac20cc907 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 13:44:47 -0500 Subject: [PATCH 0720/4033] CLJS-1030: add `cljs.repl/pst` add `cljs.repl/pst` to list of things auto-imported into cljs.user add `IParseErrorMessage`, allowing Node.js REPL to elide error message duplication in `cljs.repl/display-error` don't print :value if not provided `cljs.repl/evaluate-form` now adds repl-env to the analysis env for macros. `cljs.repl/pst` uses repl-env to print the error Add `cljs.repl/IParseErrorMessage` implementation to `cljs.repl.node/NodeEnv` --- src/clj/cljs/repl.clj | 64 ++++++++++++++++++++++++++++---------- src/clj/cljs/repl/node.clj | 2 ++ 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 988f0ff7b..796b17ad0 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -15,6 +15,7 @@ [clojure.tools.reader.reader-types :as readers] [clojure.stacktrace :as trace] [clojure.repl :as cljrepl] + [clojure.edn :as edn] [cljs.util :as util] [cljs.compiler :as comp] [cljs.analyzer :as ana] @@ -108,6 +109,11 @@ IReplEnvOptions (-repl-options [_] nil)) +(defprotocol IParseErrorMessage + (-parse-error-message [repl-env message error build-options] + "Given the original JavaScript error message return the string to actually + use.")) + (defprotocol IParseStacktrace (-parse-stacktrace [repl-env stacktrace error build-options] "Given the original JavaScript stacktrace string, the entire original error @@ -316,21 +322,22 @@ (defn- display-error ([repl-env ret form opts] - (display-error repl-env ret form (constantly nil) opts)) + (display-error repl-env ret form (constantly nil) opts)) ([repl-env ret form f opts] - (f) - ((:print opts) (:value ret)) - (when-let [st (:stacktrace ret)] - (if (and (true? (:source-map opts)) - (satisfies? IParseStacktrace repl-env)) - (let [cst (-parse-stacktrace repl-env st ret opts)] - (if (vector? cst) - (if (satisfies? IPrintStacktrace repl-env) - (-print-stacktrace repl-env cst ret opts) - (print-mapped-stacktrace cst opts)) - ((:print opts) st))) - ((:print opts) st)) - ((:flush opts))))) + (f) + (when-let [value (:value ret)] + ((:print opts) value)) + (when-let [st (:stacktrace ret)] + (if (and (true? (:source-map opts)) + (satisfies? IParseStacktrace repl-env)) + (let [cst (-parse-stacktrace repl-env st ret opts)] + (if (vector? cst) + (if (satisfies? IPrintStacktrace repl-env) + (-print-stacktrace repl-env cst ret opts) + (print-mapped-stacktrace cst opts)) + ((:print opts) st))) + ((:print opts) st)) + ((:flush opts))))) (defn evaluate-form "Evaluate a ClojureScript form in the JavaScript environment. Returns a @@ -351,7 +358,10 @@ (atom {:source-map (sorted-map) :gen-col 0 :gen-line 0})] - (let [js (comp/emit-str (ana/no-warn (ana/analyze env (wrap form) opts))) + (let [js (comp/emit-str + (ana/no-warn + (ana/analyze (assoc env :repl-env repl-env) + (wrap form) opts))) t (System/currentTimeMillis)] (str js "\n//# sourceURL=repl-" t ".js" @@ -368,7 +378,10 @@ ;; handle strings / primitives without metadata (with-out-str (pr form)))]}) "UTF-8"))))) - (comp/emit-str (ana/no-warn (ana/analyze env (wrap form) opts))))] + (comp/emit-str + (ana/no-warn + (ana/analyze (assoc env :repl-env repl-env) + (wrap form) opts))))] (when (= (:op ast) :ns) (load-dependencies repl-env (into (vals (:requires ast)) @@ -677,7 +690,7 @@ (evaluate-form repl-env env "" (with-meta '(ns cljs.user - (:require [cljs.repl :refer-macros [source doc find-doc apropos dir]])) + (:require [cljs.repl :refer-macros [source doc find-doc apropos dir pst]])) {:line 1 :column 1}) identity opts) (catch Throwable e @@ -938,3 +951,20 @@ str-or-pattern." [ns] `(doseq [sym# (quote ~(sort (keys (ana-api/ns-publics ns))))] (println sym#))) + +(defmacro pst [e] + (let [{:keys [repl-env] :as env} &env] + (when repl-env + (let [ret (edn/read-string + (evaluate-form repl-env env "" + `(when ~e + (pr-str + {:value (.-message ~e) + :stacktrace (.-stack ~e)}))))] + (when ret + (let [ret (update-in ret [:value] + (fn [msg] + (if (satisfies? IParseErrorMessage repl-env) + (-parse-error-message repl-env msg ret *repl-opts*) + msg)))] + (display-error repl-env ret nil *repl-opts*))))))) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index da4587d3a..77e69c2d3 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -172,6 +172,8 @@ repl/IReplEnvOptions (-repl-options [this] {:target :nodejs}) + repl/IParseErrorMessage + (-parse-error-message [_ _ _ _]) repl/IJavaScriptEnv (-setup [this opts] (setup this opts)) From c964933db6483abb5490caf2d7f6053d1dbca513 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 13:58:09 -0500 Subject: [PATCH 0721/4033] formatting, fix `cljs.repl/source` docstring support `(cljs.repl/pst)` as pointed out by Mike Fikes --- src/clj/cljs/repl.clj | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 796b17ad0..561f60723 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -329,7 +329,7 @@ ((:print opts) value)) (when-let [st (:stacktrace ret)] (if (and (true? (:source-map opts)) - (satisfies? IParseStacktrace repl-env)) + (satisfies? IParseStacktrace repl-env)) (let [cst (-parse-stacktrace repl-env st ret opts)] (if (vector? cst) (if (satisfies? IPrintStacktrace repl-env) @@ -923,7 +923,7 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (defmacro source "Prints the source code for the given symbol, if it can find it. This requires that the symbol resolve to a Var defined in a - namespace for which the .clj is in the classpath. + namespace for which the .cljs is in the classpath. Example: (source filter)" [n] @@ -952,19 +952,21 @@ str-or-pattern." `(doseq [sym# (quote ~(sort (keys (ana-api/ns-publics ns))))] (println sym#))) -(defmacro pst [e] - (let [{:keys [repl-env] :as env} &env] - (when repl-env - (let [ret (edn/read-string - (evaluate-form repl-env env "" - `(when ~e - (pr-str - {:value (.-message ~e) - :stacktrace (.-stack ~e)}))))] - (when ret - (let [ret (update-in ret [:value] - (fn [msg] - (if (satisfies? IParseErrorMessage repl-env) - (-parse-error-message repl-env msg ret *repl-opts*) - msg)))] - (display-error repl-env ret nil *repl-opts*))))))) +(defmacro pst + ([] `(pst *e)) + ([e] + (let [{:keys [repl-env] :as env} &env] + (when (and e repl-env) + (let [ret (edn/read-string + (evaluate-form repl-env env "" + `(when ~e + (pr-str + {:value (.-message ~e) + :stacktrace (.-stack ~e)}))))] + (when ret + (let [ret (update-in ret [:value] + (fn [msg] + (if (satisfies? IParseErrorMessage repl-env) + (-parse-error-message repl-env msg ret *repl-opts*) + msg)))] + (display-error repl-env ret nil *repl-opts*)))))))) From 1375437fd4dfafad7544356babacbae1b261d2ff Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 14:01:30 -0500 Subject: [PATCH 0722/4033] clarify IParseErrorMessage invoke in `cljs.repl/pst` --- src/clj/cljs/repl.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 561f60723..62acdd1dc 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -966,6 +966,8 @@ str-or-pattern." (when ret (let [ret (update-in ret [:value] (fn [msg] + ;; give REPL environments a chance to fix or + ;; or elide redundant information (if (satisfies? IParseErrorMessage repl-env) (-parse-error-message repl-env msg ret *repl-opts*) msg)))] From 78955d196dfec84933404f9c052e87cbac62de19 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 14:02:54 -0500 Subject: [PATCH 0723/4033] fix inaccuracies in special-forms docs --- src/clj/cljs/repl.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 62acdd1dc..9b324dc16 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -817,7 +817,7 @@ :doc "Evaluates test. If not the singular values nil or false, evaluates and yields then, otherwise, evaluates and yields else. If else is not supplied it defaults to nil."} - new {:forms [(Classname. args*) (new Classname args*)] + new {:forms [(Constructor. args*) (new Constructor args*)] :url "java_interop#new" :doc "The args, if any, are evaluated from left to right, and passed to the JavaScript constructor. The constructed object is @@ -829,7 +829,7 @@ the bindings of the recursion point to the values of the exprs. Execution then jumps back to the recursion point, a loop or fn method."} set! {:forms[(set! var-symbol expr) - (set! (. instance-expr instanceFieldName-symbol) expr)] + (set! (.- instance-expr instanceFieldName-symbol) expr)] :url "vars#set" :doc "Used to set vars and JavaScript object fields"} throw {:forms [(throw expr)] From a5e271bb461c8aca3c10c0a168402ec59e9a1a25 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 14:24:12 -0500 Subject: [PATCH 0724/4033] current error grabbing strategy in `cljs.repl/pst` is not flexible enough add `cljs.repl/IGetError` protocol. --- src/clj/cljs/repl.clj | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 9b324dc16..8961463c8 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -114,6 +114,17 @@ "Given the original JavaScript error message return the string to actually use.")) +(defprotocol IGetError + (-get-error [repl-env name build-options] + "Given a symbol representing a var holding an error return the + canonical error representation: + + {:value + :stacktrace } + + :value should be the host environment JavaScript error message string. + :stacktrace should be the host JavaScript environment stacktrace string.")) + (defprotocol IParseStacktrace (-parse-stacktrace [repl-env stacktrace error build-options] "Given the original JavaScript stacktrace string, the entire original error @@ -957,12 +968,14 @@ str-or-pattern." ([e] (let [{:keys [repl-env] :as env} &env] (when (and e repl-env) - (let [ret (edn/read-string - (evaluate-form repl-env env "" - `(when ~e - (pr-str - {:value (.-message ~e) - :stacktrace (.-stack ~e)}))))] + (let [ret (if (satisfies? IGetError repl-env) + (-get-error repl-env e *repl-opts*) + (edn/read-string + (evaluate-form repl-env env "" + `(when ~e + (pr-str + {:value (.-message ~e) + :stacktrace (.-stack ~e)})))))] (when ret (let [ret (update-in ret [:value] (fn [msg] From c6c199bb6245bb7ae86527360e288a0c80181183 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 14:38:01 -0500 Subject: [PATCH 0725/4033] `cljs.repl/IGetError` needs to take analysis environment --- src/clj/cljs/repl.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 8961463c8..af88811c1 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -115,9 +115,10 @@ use.")) (defprotocol IGetError - (-get-error [repl-env name build-options] - "Given a symbol representing a var holding an error return the - canonical error representation: + (-get-error [repl-env name env build-options] + "Given a symbol representing a var holding an error, an analysis + environment, and the REPL/compiler options return the canonical error + representation: {:value :stacktrace } @@ -969,7 +970,7 @@ str-or-pattern." (let [{:keys [repl-env] :as env} &env] (when (and e repl-env) (let [ret (if (satisfies? IGetError repl-env) - (-get-error repl-env e *repl-opts*) + (-get-error repl-env e env *repl-opts*) (edn/read-string (evaluate-form repl-env env "" `(when ~e From 39ae40b7505ea8eeae2f39a2023e04ddf77707c5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 14:42:55 -0500 Subject: [PATCH 0726/4033] `cljs.repl/*repl-opts*` is not correctly bound previously bound before merging in -setup extensions --- src/clj/cljs/repl.clj | 78 +++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index af88811c1..5656d0948 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -660,8 +660,7 @@ :undeclared-var warn-on-undeclared :undeclared-ns warn-on-undeclared :undeclared-ns-form warn-on-undeclared) - ana/*cljs-static-fns* static-fns - *repl-opts* opts] + ana/*cljs-static-fns* static-fns] ;; TODO: the follow should become dead code when the REPL is ;; sufficiently enhanced to understand :cache-analysis - David (let [env {:context :expr :locals {}} @@ -696,46 +695,47 @@ (print value))))))] (comp/with-core-cljs opts (fn [] - (try - (when analyze-path - (analyze-source analyze-path opts)) - (evaluate-form repl-env env "" - (with-meta - '(ns cljs.user - (:require [cljs.repl :refer-macros [source doc find-doc apropos dir pst]])) - {:line 1 :column 1}) - identity opts) - (catch Throwable e - (caught e repl-env opts))) - (when-let [src (:watch opts)] - (future - (let [log-file (io/file (util/output-directory opts) "watch.log")] - (print "Watch compilation log available at:" (str log-file)) - (try - (binding [*out* (FileWriter. log-file)] - (cljsc/watch src (dissoc opts :watch))) - (catch Throwable e - (caught e repl-env opts)))))) - (binding [*in* (if (true? (:source-map-inline opts)) - *in* - (reader))] + (binding [*repl-opts* opts] (try - (init) + (when analyze-path + (analyze-source analyze-path opts)) + (evaluate-form repl-env env "" + (with-meta + '(ns cljs.user + (:require [cljs.repl :refer-macros [source doc find-doc apropos dir pst]])) + {:line 1 :column 1}) + identity opts) (catch Throwable e (caught e repl-env opts))) - (prompt) - (flush) - (loop [] - (when-not - (try - (identical? (read-eval-print) request-exit) - (catch Throwable e - (caught e repl-env opts) - nil)) - (when (need-prompt) - (prompt) - (flush)) - (recur))))))) + (when-let [src (:watch opts)] + (future + (let [log-file (io/file (util/output-directory opts) "watch.log")] + (print "Watch compilation log available at:" (str log-file)) + (try + (binding [*out* (FileWriter. log-file)] + (cljsc/watch src (dissoc opts :watch))) + (catch Throwable e + (caught e repl-env opts)))))) + (binding [*in* (if (true? (:source-map-inline opts)) + *in* + (reader))] + (try + (init) + (catch Throwable e + (caught e repl-env opts))) + (prompt) + (flush) + (loop [] + (when-not + (try + (identical? (read-eval-print) request-exit) + (catch Throwable e + (caught e repl-env opts) + nil)) + (when (need-prompt) + (prompt) + (flush)) + (recur)))))))) (-tear-down repl-env))))) (defn repl From daad7806a29136d0d30f7b3d7e843172c3e18ef0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Feb 2015 14:55:57 -0500 Subject: [PATCH 0727/4033] Nashorn environment doesn't supply console, setup printing correctly --- src/clj/cljs/repl/nashorn.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 80fe5b1ac..404fde532 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -155,7 +155,8 @@ (repl/evaluate-form this env repl-filename '(do (.require js/goog "cljs.core") - (cljs.core/enable-console-print!))) + (set! *print-newline* false) + (set! *print-fn* js/print))) ;; monkey-patch goog.isProvided_ to suppress useless errors (repl/evaluate-form this env repl-filename '(set! js/goog.isProvided_ (fn [ns] false))) From a967b1a3c9abd633866cb7c4d8972f8c982a74bc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Feb 2015 00:32:29 -0500 Subject: [PATCH 0728/4033] fix quote regression in `cljs.repl/doc` pointed out by Mike Fikes --- src/cljs/cljs/repl.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index b40f1f83c..c904aeda1 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -15,7 +15,7 @@ (cond (:forms m) (doseq [f (:forms m)] (println " " f)) - (:arglists m) (prn (:arglists m))) + (:arglists m) (prn (second (:arglists m)))) (if (:special-form m) (do (println "Special Form") From d698234b3331e5c26edb69966ab70aa61a95c7b8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 24 Feb 2015 00:25:17 -0500 Subject: [PATCH 0729/4033] bump closure-compiler dep --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 2ec14e8a8..4ba54ea0e 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20140625 + v20150126 org.clojure diff --git a/project.clj b/project.clj index d126eb8ad..fb8f0a3f6 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.3"] [org.clojure/tools.reader "0.8.10"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] - [com.google.javascript/closure-compiler "v20140625"] + [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R4"]] :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]}} diff --git a/script/bootstrap b/script/bootstrap index f10d0a93c..dddef1c06 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.6.0" -CLOSURE_RELEASE="20140625" +CLOSURE_RELEASE="20150126" DJSON_RELEASE="0.2.3" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R3" From 07161947624d20bf4e99b306274a8e189fd0e020 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 24 Feb 2015 16:14:39 -0500 Subject: [PATCH 0730/4033] change ES6 Map `get` support to take additional `not-found` parameter --- src/cljs/cljs/core.cljs | 12 ++++++------ test/cljs/cljs/core_test.cljs | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 955919ef2..719c4ce03 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -5346,8 +5346,8 @@ reduces them without incurring seq initialization" (es6-iterator (vals coll))) (has [coll k] (contains? coll k)) - (get [coll k] - (-lookup coll k)) + (get [coll k not-found] + (-lookup coll k not-found)) (forEach [coll f] (doseq [[k v] coll] (f v k))) @@ -6219,8 +6219,8 @@ reduces them without incurring seq initialization" (es6-iterator (vals coll))) (has [coll k] (contains? coll k)) - (get [coll k] - (-lookup coll k)) + (get [coll k not-found] + (-lookup coll k not-found)) (forEach [coll f] (doseq [[k v] coll] (f v k))) @@ -6993,8 +6993,8 @@ reduces them without incurring seq initialization" (es6-iterator (vals coll))) (has [coll k] (contains? coll k)) - (get [coll k] - (-lookup coll k)) + (get [coll k not-found] + (-lookup coll k not-found)) (forEach [coll f] (doseq [[k v] coll] (f v k))) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index f11e8a4ed..8a57e51f6 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1417,6 +1417,7 @@ (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)))) From 9ed52b9fe7b109e8caa57ac992fe741a70fb41b7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 25 Feb 2015 15:09:34 -0500 Subject: [PATCH 0731/4033] Node.js REPL should work even if :output-dir not supplied --- src/clj/cljs/repl/node.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 77e69c2d3..e47ad3aa1 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -80,7 +80,7 @@ (defn setup ([repl-env] (setup repl-env nil)) ([repl-env opts] - (let [output-dir (io/file (:output-dir opts)) + (let [output-dir (io/file (util/output-directory opts)) _ (.mkdirs output-dir) of (io/file output-dir "node_repl.js") _ (spit of From 13276c52c70d4547720f248922b440761ecd143d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 25 Feb 2015 16:40:22 -0500 Subject: [PATCH 0732/4033] be more disciplined about ints in murmur3 code --- src/cljs/cljs/core.cljs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 719c4ce03..b303ef7ab 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -466,22 +466,22 @@ ;; http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp (def m3-seed 0) -(def m3-C1 0xcc9e2d51) -(def m3-C2 0x1b873593) +(def m3-C1 (int 0xcc9e2d51)) +(def m3-C2 (int 0x1b873593)) (defn ^number m3-mix-K1 [k1] - (-> k1 (imul m3-C1) (int-rotate-left 15) (imul m3-C2))) + (-> (int k1) (imul m3-C1) (int-rotate-left 15) (imul m3-C2))) (defn ^number m3-mix-H1 [h1 k1] - (-> h1 (bit-xor k1) (int-rotate-left 13) (imul 5) (+ 0xe6546b64))) + (int (-> (int h1) (bit-xor (int k1)) (int-rotate-left 13) (imul 5) (+ (int 0xe6546b64))))) (defn ^number m3-fmix [h1 len] - (as-> h1 h1 + (as-> (int h1) h1 (bit-xor h1 len) (bit-xor h1 (unsigned-bit-shift-right h1 16)) - (imul h1 0x85ebca6b) + (imul h1 (int 0x85ebca6b)) (bit-xor h1 (unsigned-bit-shift-right h1 13)) - (imul h1 0xc2b2ae35) + (imul h1 (int 0xc2b2ae35)) (bit-xor h1 (unsigned-bit-shift-right h1 16)))) (defn ^number m3-hash-int [in] From 1dd55688c340e495554c32b44d6046ff2b834737 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Feb 2015 09:03:42 -0500 Subject: [PATCH 0733/4033] default :output-dir for Nashorn and Node REPLs --- src/clj/cljs/repl/nashorn.clj | 50 ++++++++++++++++++----------------- src/clj/cljs/repl/node.clj | 3 ++- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 404fde532..8a3c67836 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -146,30 +146,32 @@ repl/IReplEnvOptions (-repl-options [this] {}) repl/IJavaScriptEnv - (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] - (init-engine engine output-dir debug) - (let [env (ana/empty-env)] - (if output-to - (load-js-file engine output-to) - (bootstrap-repl engine output-dir opts)) - (repl/evaluate-form this env repl-filename - '(do - (.require js/goog "cljs.core") - (set! *print-newline* false) - (set! *print-fn* js/print))) - ;; monkey-patch goog.isProvided_ to suppress useless errors - (repl/evaluate-form this env repl-filename - '(set! js/goog.isProvided_ (fn [ns] false))) - ;; monkey-patch goog.require to be more sensible - (repl/evaluate-form this env repl-filename - '(do - (set! *loaded-libs* #{"cljs.core"}) - (set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) + (-setup [this opts] + (let [{:keys [output-dir bootstrap output-to] :as opts} + (merge {:output-dir ".cljs_nashorn_repl"} opts)] + (init-engine engine output-dir debug) + (let [env (ana/empty-env)] + (if output-to + (load-js-file engine output-to) + (bootstrap-repl engine output-dir opts)) + (repl/evaluate-form this env repl-filename + '(do + (.require js/goog "cljs.core") + (set! *print-newline* false) + (set! *print-fn* js/print))) + ;; monkey-patch goog.isProvided_ to suppress useless errors + (repl/evaluate-form this env repl-filename + '(set! js/goog.isProvided_ (fn [ns] false))) + ;; monkey-patch goog.require to be more sensible + (repl/evaluate-form this env repl-filename + '(do + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name)))))))))) (-evaluate [{engine :engine :as this} filename line js] (when debug (println "Evaluating: " js)) (try diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 77e69c2d3..1be6f4e8b 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -80,7 +80,8 @@ (defn setup ([repl-env] (setup repl-env nil)) ([repl-env opts] - (let [output-dir (io/file (:output-dir opts)) + (let [opts (merge {:output-dir ".cljs_node_repl"} opts) + output-dir (io/file (:output-dir opts)) _ (.mkdirs output-dir) of (io/file output-dir "node_repl.js") _ (spit of From 26fb523e41e57d7d7f76159f382bf18025465abe Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Feb 2015 09:14:21 -0500 Subject: [PATCH 0734/4033] fix default :output-dir logic for Node.js REPL add precondition to cljs.util/output-directory --- src/clj/cljs/repl/node.clj | 2 +- src/clj/cljs/util.clj | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 1137d32ae..ed6ccfda1 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -81,7 +81,7 @@ ([repl-env] (setup repl-env nil)) ([repl-env opts] (let [opts (merge {:output-dir ".cljs_node_repl"} opts) - output-dir (io/file (util/output-directory (:output-dir opts))) + output-dir (io/file (util/output-directory opts)) _ (.mkdirs output-dir) of (io/file output-dir "node_repl.js") _ (spit of diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 41749d296..c90c6d3e3 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -82,7 +82,8 @@ (defn output-directory ([opts] (output-directory opts "out")) ([opts default] - (or (:output-dir opts) default))) + {:pre [(or (nil? opts) (map? opts))]} + (or (:output-dir opts) default))) (defn file? [f] (instance? File f)) From d944c75722b7e7716d6bef967b788bcef5ed6c57 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Feb 2015 09:50:31 -0500 Subject: [PATCH 0735/4033] default REPLs to :cache-analysis true --- src/clj/cljs/repl.clj | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5656d0948..f4c968d28 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -638,18 +638,20 @@ (let [ups-deps (cljsc/get-upstream-deps) {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} - (assoc (merge (-repl-options repl-env) opts) - :init init - :need-prompt prompt - :flush flush - :read read - :print print - :caught caught - :reader reader - :print-no-newline print-no-newline - :source-map-inline source-map-inline - :ups-libs (:libs ups-deps) - :ups-foreign-libs (:foreign-libs ups-deps))] + (merge + {:cache-analysis true} + (assoc (merge (-repl-options repl-env) opts) + :init init + :need-prompt prompt + :flush flush + :read read + :print print + :caught caught + :reader reader + :print-no-newline print-no-newline + :source-map-inline source-map-inline + :ups-libs (:libs ups-deps) + :ups-foreign-libs (:foreign-libs ups-deps)))] (env/with-compiler-env (or (::env/compiler repl-env) (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user From a7d8863c740af6c00b0ffaf7fee992e085acb1b0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Feb 2015 09:51:37 -0500 Subject: [PATCH 0736/4033] CLJS-1066: Rhino REPL regression Modernize Rhino REPL so the implementation style is in sync with the other REPLs. Fix regressions. --- src/clj/cljs/repl/rhino.clj | 133 +++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 63 deletions(-) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 34e7a279e..821c92083 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -7,9 +7,11 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.repl.rhino + (:refer-clojure :exclude [load-file]) (:require [clojure.string :as string] [clojure.java.io :as io] [cljs.compiler :as comp] + [cljs.closure :as closure] [cljs.analyzer :as ana] [cljs.repl :as repl] [cljs.util :as util]) @@ -18,10 +20,13 @@ RhinoException Undefined])) (def ^String bootjs - (str "var global = this;" - "goog.require = function(rule){" - "Packages.clojure.lang.RT[\"var\"](\"cljs.repl.rhino\",\"goog-require\")" - ".invoke(___repl_env, __repl_opts, rule);}")) + (str "var global = this;\n" + "CLOSURE_IMPORT_SCRIPT = function(src) {\n" + " var ns = \"cljs.repl.rhino\"," + " name = \"load-file\"," + " loadFile = Packages.clojure.lang.RT[\"var\"](ns,name);\n" + " if(src) loadFile.invoke(___repl_env, __repl_opts, src);\n" + "};\n")) ;; ============================================================================= ;; Protocols @@ -36,7 +41,6 @@ Reader (-eval [this {:keys [cx scope]} filename line] - (.setOptimizationLevel ^Context cx -1) (.evaluateReader cx scope this filename line nil))) ;; ============================================================================= @@ -75,38 +79,13 @@ :value (.toString ex) :stacktrace (stacktrace ex)}))) -(defn goog-require [repl-env opts rule] - (let [path (string/replace (comp/munge rule) \. File/separatorChar) - output-dir (util/output-directory opts) - cljsc-path (str output-dir File/separator (str path ".js")) - cljs-path (str path ".cljs") - gpath (-eval (str "goog.dependencies_.nameToPath['" rule "']") - repl-env "" 1) - js-path (str "goog/" gpath) - js-out-path (io/file output-dir "goog" - (string/replace gpath \/ File/separatorChar))] - (let [compiled (io/file cljsc-path)] - (if (.exists compiled) - ;; TODO: only take this path if analysis cache is available - ;; - David - (do - (with-open [reader (io/reader compiled)] - (-eval reader repl-env cljsc-path 1))) - (if-let [res (io/resource cljs-path)] - (binding [ana/*cljs-ns* 'cljs.user] - (repl/load-stream repl-env cljs-path res)) - (if-let [res (io/resource js-path)] - (with-open [reader (io/reader res)] - (-eval reader repl-env js-path 1)) - (if (.exists js-out-path) - (with-open [reader (io/reader js-out-path)] - (-eval reader repl-env js-path 1)) - (throw - (Exception. - (str "Cannot find " - cljs-path " or " - js-path " or " - (.getName js-out-path) " in classpath")))))))))) +(defn load-file + "Load a JavaScript. This is needed to load JavaScript files before the Rhino + environment is bootstrapped. After bootstrapping load-javascript will be + used." + [repl-env opts src] + (let [goog-path (io/file (util/output-directory opts) "goog" src)] + (rhino-eval repl-env (.getPath goog-path) 1 (slurp goog-path)))) (defn load-javascript [repl-env ns url] (try @@ -116,18 +95,56 @@ (catch Throwable ex (println (.getMessage ex))))) (defn rhino-setup [repl-env opts] - (let [env (ana/empty-env) - scope (:scope repl-env)] + (let [opts (merge {:output-dir ".cljs_rhino_repl"} opts) + scope (:scope repl-env) + env (ana/empty-env) + core (io/resource "cljs/core.cljs") + base-js (slurp (io/resource "goog/base.js")) + core-js (closure/compile core + (assoc opts + :output-file + (closure/src-file->target-file core))) + deps (closure/add-dependencies opts core-js) + output-dir (util/output-directory opts) + repl-deps (io/file output-dir "rhino_repl_deps.js")] + ;; emit core and deps + (apply closure/output-unoptimized + (assoc opts :output-to (.getPath repl-deps)) deps) + + ;; setup back references & output stream + (ScriptableObject/putProperty scope + "___repl_env" (Context/javaToJS repl-env scope)) (ScriptableObject/putProperty scope "__repl_opts" (Context/javaToJS opts scope)) - (repl/load-file repl-env "cljs/core.cljs" opts) (ScriptableObject/putProperty scope "out" (Context/javaToJS *out* scope)) - (binding [ana/*cljs-ns* 'cljs.core] - (repl/evaluate-form repl-env env "" - '(do - (set! (.-isProvided_ js/goog) (fn [_] false)) - (set! *print-fn* (fn [x] (.write js/out x)))))))) + + ;; define file loading, load goog.base, load repl deps + (rhino-eval repl-env "bootjs" 1 bootjs) + (rhino-eval repl-env "goog/base.js" 1 base-js) + (rhino-eval repl-env "rhino_repl_deps.js" 1 (slurp repl-deps)) + + ;; === Bootstrap === + ;; load cljs.core, setup printing + (repl/evaluate-form repl-env env "" + '(do + (.require js/goog "cljs.core") + (set! *print-fn* (fn [x] (.write js/out x))))) + + ;; allow namespace reloading + (repl/evaluate-form repl-env env "" + '(set! js/goog.isProvided_ (fn [x] false))) + + ;; monkey-patch goog.require + (repl/evaluate-form repl-env env "" + '(do + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) (defrecord RhinoEnv [] repl/IReplEnvOptions @@ -146,23 +163,13 @@ "Returns a fresh JS environment, suitable for passing to repl. Hang on to return for use across repl calls." [] - (let [cx (Context/enter) - scope (.initStandardObjects cx) - base (io/resource "goog/base.js") - deps (io/resource "goog/deps.js") - new-repl-env (merge (RhinoEnv.) {:cx cx :scope scope})] - (assert base "Can't find goog/base.js in classpath") - (assert deps "Can't find goog/deps.js in classpath") - (ScriptableObject/putProperty scope - "___repl_env" (Context/javaToJS new-repl-env scope)) - (with-open [r (io/reader base)] - (-eval r new-repl-env "goog/base.js" 1)) - (-eval bootjs new-repl-env "bootjs" 1) - ;; Load deps.js line-by-line to avoid 64K method limit - (with-open [reader (io/reader deps)] - (doseq [^String line (line-seq reader)] - (-eval line new-repl-env "goog/deps.js" 1))) - new-repl-env)) + (let [cx (Context/enter)] + ;; just avoid the 64K method limit + ;; Rhino is slow even with optimizations enabled + (.setOptimizationLevel cx -1) + (merge (RhinoEnv.) + {:cx cx + :scope (.initStandardObjects cx)}))) (comment @@ -179,7 +186,7 @@ (time (reduce + [1 2 3 4 5])) (even? :a) (throw (js/Error. "There was an error")) - (load-file "clojure/string.cljs") + (clojure.core/load-file "clojure/string.cljs") (clojure.string/triml " hello") (clojure.string/reverse " hello") From 3da00c7bb4f60f37dd44dc045f475216e040efee Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Feb 2015 09:51:47 -0500 Subject: [PATCH 0737/4033] ignore Rhino REPL directory --- Clojurescript.iml | 1 + 1 file changed, 1 insertion(+) diff --git a/Clojurescript.iml b/Clojurescript.iml index cc3269c84..bf1c6c09f 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -13,6 +13,7 @@ + From 52708a3229543bf07495527ca4a9ff33e2eeeddf Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 26 Feb 2015 11:56:34 -0500 Subject: [PATCH 0738/4033] fix REPLs emitting code into .repl directory respect :default-output-dir if supplied by REPL environment supply :default-output-dir to Node/Nashorn/Rhino REPL environments stop creating compiler environment in Nashorn REPL, this is broken default all REPLs to :source-map true stop specifying defaults in REPL convenience scripts --- script/nashornrepljs | 2 +- script/noderepljs | 2 +- script/repljs | 2 +- src/clj/cljs/repl.clj | 17 +++++++---- src/clj/cljs/repl/nashorn.clj | 55 +++++++++++++++++------------------ src/clj/cljs/repl/node.clj | 6 ++-- src/clj/cljs/repl/rhino.clj | 6 ++-- 7 files changed, 46 insertions(+), 44 deletions(-) diff --git a/script/nashornrepljs b/script/nashornrepljs index 85c6188fe..0b7c50b64 100755 --- a/script/nashornrepljs +++ b/script/nashornrepljs @@ -12,4 +12,4 @@ done java -server -cp $CLJSC_CP clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.nashorn :as nashorn]) -(repl/repl* (nashorn/repl-env $1) {:output-dir \".cljs_nashorn_repl\" :cache-analysis true :source-map true})" +(repl/repl (nashorn/repl-env $1))" diff --git a/script/noderepljs b/script/noderepljs index 474ce4628..4c75150ce 100755 --- a/script/noderepljs +++ b/script/noderepljs @@ -12,4 +12,4 @@ done java -server -cp $CLJSC_CP clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.node :as node]) -(repl/repl* (node/repl-env $1) {:output-dir \".cljs_node_repl\" :cache-analysis true :source-map true})" +(repl/repl (node/repl-env $1))" diff --git a/script/repljs b/script/repljs index ed24c02cc..6c7cd61f8 100755 --- a/script/repljs +++ b/script/repljs @@ -12,4 +12,4 @@ done java -server -cp "$CLJSC_CP" clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.rhino :as rhino]) -(repl/repl* (rhino/repl-env) {:warn-on-undeclared true :output-dir \".cljs_repl\" :cache-analysis true})" +(repl/repl* (rhino/repl-env) {:warn-on-undeclared true})" diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index f4c968d28..b2452aefa 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -147,11 +147,14 @@ (defn- env->opts "Returns a hash-map containing all of the entries in [repl-env], translating :working-dir to :output-dir." - [repl-env] - ; some bits in cljs.closure use the options value as an ifn :-/ - (-> (into {} repl-env) - (assoc :optimizations (get repl-env :optimizations :none)) - (assoc :output-dir (get repl-env :working-dir ".repl")))) + ([repl-env] (env->opts repl-env nil)) + ([repl-env opts] + ;; some bits in cljs.closure use the options value as an ifn :-/ + (-> (into {} repl-env) + (assoc :optimizations + (or (:optimizations opts) (get repl-env :optimizations :none))) + (assoc :output-dir + (or (:output-dir opts) (get repl-env :working-dir ".repl")))))) (defn load-namespace "Load a namespace and all of its dependencies into the evaluation environment. @@ -639,7 +642,7 @@ {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} (merge - {:cache-analysis true} + {:cache-analysis true :source-map true} (assoc (merge (-repl-options repl-env) opts) :init init :need-prompt prompt @@ -650,6 +653,8 @@ :reader reader :print-no-newline print-no-newline :source-map-inline source-map-inline + :output-dir (or (:output-dir opts) + (:default-output-dir repl-env)) :ups-libs (:libs ups-deps) :ups-foreign-libs (:foreign-libs ups-deps)))] (env/with-compiler-env diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 8a3c67836..25cc6a4d2 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -146,32 +146,30 @@ repl/IReplEnvOptions (-repl-options [this] {}) repl/IJavaScriptEnv - (-setup [this opts] - (let [{:keys [output-dir bootstrap output-to] :as opts} - (merge {:output-dir ".cljs_nashorn_repl"} opts)] - (init-engine engine output-dir debug) - (let [env (ana/empty-env)] - (if output-to - (load-js-file engine output-to) - (bootstrap-repl engine output-dir opts)) - (repl/evaluate-form this env repl-filename - '(do - (.require js/goog "cljs.core") - (set! *print-newline* false) - (set! *print-fn* js/print))) - ;; monkey-patch goog.isProvided_ to suppress useless errors - (repl/evaluate-form this env repl-filename - '(set! js/goog.isProvided_ (fn [ns] false))) - ;; monkey-patch goog.require to be more sensible - (repl/evaluate-form this env repl-filename - '(do - (set! *loaded-libs* #{"cljs.core"}) - (set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name)))))))))) + (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] + (init-engine engine output-dir debug) + (let [env (ana/empty-env)] + (if output-to + (load-js-file engine output-to) + (bootstrap-repl engine output-dir opts)) + (repl/evaluate-form this env repl-filename + '(do + (.require js/goog "cljs.core") + (set! *print-newline* false) + (set! *print-fn* js/print))) + ;; monkey-patch goog.isProvided_ to suppress useless errors + (repl/evaluate-form this env repl-filename + '(set! js/goog.isProvided_ (fn [ns] false))) + ;; monkey-patch goog.require to be more sensible + (repl/evaluate-form this env repl-filename + '(do + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) (-evaluate [{engine :engine :as this} filename line js] (when debug (println "Evaluating: " js)) (try @@ -207,8 +205,7 @@ :output-to load this file initially into Nashorn, relative to output-dir. Use a minimal bootstrapped cljs.core environment if not specified." [& {:keys [debug] :as opts}] - (let [engine (create-engine) - compiler-env (env/default-compiler-env)] + (let [engine (create-engine)] (merge (NashornEnv. engine debug) - {:cljs.env/compiler compiler-env} ; required by cider middleware ? + {:default-output-dir ".cljs_nashorn_repl"} opts))) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index ed6ccfda1..aef1bddf2 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -80,8 +80,7 @@ (defn setup ([repl-env] (setup repl-env nil)) ([repl-env opts] - (let [opts (merge {:output-dir ".cljs_node_repl"} opts) - output-dir (io/file (util/output-directory opts)) + (let [output-dir (io/file (util/output-directory opts)) _ (.mkdirs output-dir) of (io/file output-dir "node_repl.js") _ (spit of @@ -192,7 +191,8 @@ {:host "localhost" :port (+ 49000 (rand-int 10000))} options)] - (NodeEnv. host port (atom nil) (atom nil)))) + (merge (NodeEnv. host port (atom nil) (atom nil)) + {:default-output-dir ".cljs_node_repl"}))) (defn repl-env [& {:as options}] diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 821c92083..07a04bf30 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -95,8 +95,7 @@ (catch Throwable ex (println (.getMessage ex))))) (defn rhino-setup [repl-env opts] - (let [opts (merge {:output-dir ".cljs_rhino_repl"} opts) - scope (:scope repl-env) + (let [scope (:scope repl-env) env (ana/empty-env) core (io/resource "cljs/core.cljs") base-js (slurp (io/resource "goog/base.js")) @@ -168,7 +167,8 @@ ;; Rhino is slow even with optimizations enabled (.setOptimizationLevel cx -1) (merge (RhinoEnv.) - {:cx cx + {:default-output-dir ".cljs_rhino_repl" + :cx cx :scope (.initStandardObjects cx)}))) (comment From 28a31165e837826483e7bc69d4645e7f3b67520e Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 26 Feb 2015 13:44:34 -0500 Subject: [PATCH 0739/4033] more sensible error if cljs.repl/repl arguments after the first incorrectly supplied --- script/repljs | 2 +- src/clj/cljs/repl.clj | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/script/repljs b/script/repljs index 6c7cd61f8..5b2511302 100755 --- a/script/repljs +++ b/script/repljs @@ -12,4 +12,4 @@ done java -server -cp "$CLJSC_CP" clojure.main -e \ "(require '[cljs.repl :as repl]) (require '[cljs.repl.rhino :as rhino]) -(repl/repl* (rhino/repl-env) {:warn-on-undeclared true})" +(repl/repl (rhino/repl-env) :warn-on-undeclared true)" diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index b2452aefa..456568989 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -812,8 +812,10 @@ - :source-map-inline, whether inline source maps should be enabled. Most useful in browser context. Implies using a fresh reader for each form. default: true" - [repl-env & {:as opts}] - (repl* repl-env opts)) + [repl-env & opts] + (assert (even? (count opts)) + "Arguments after repl-env must be interleaved key value pairs") + (repl* repl-env (apply hash-map opts))) ;; ============================================================================= ;; ClojureScript REPL interaction support From 3f38fb9507a89b44c5890bb18be635944e7aceca Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 Feb 2015 13:13:01 -0500 Subject: [PATCH 0740/4033] CLJS-1069: Generic :jsdoc support --- src/clj/cljs/analyzer.clj | 25 +++++++++++++++---------- src/clj/cljs/compiler.clj | 11 ++++++----- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 2bb69af36..284f6cef9 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -761,16 +761,21 @@ (:methods init-expr))}) ) (when (and fn-var? tag) {:ret-tag tag}))) - (merge {:env env :op :def :form form - :name var-name - :var (assoc - (analyze - (-> env (dissoc :locals) - (assoc :context :expr) - (assoc :def-var true)) - sym) - :op :var) - :doc doc :init init-expr} + (merge + {:env env + :op :def + :form form + :name var-name + :var (assoc + (analyze + (-> env (dissoc :locals) + (assoc :context :expr) + (assoc :def-var true)) + sym) + :op :var) + :doc doc + :jsdoc (:jsdoc sym-meta) + :init init-expr} (when-let [test (:test sym-meta)] {:test (analyze (assoc env :context :expr) test)}) (when tag diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 1e9dd8770..827ae2d15 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -413,20 +413,21 @@ (let [docs (when doc [doc]) docs (if jsdoc (concat docs jsdoc) docs) docs (remove nil? docs)] - (letfn [(print-comment-lines [e] (doseq [next-line (string/split-lines e)] - (emitln "* " (string/trim next-line))))] + (letfn [(print-comment-lines [e] + (doseq [next-line (string/split-lines e)] + (emitln " * " (string/trim next-line))))] (when (seq docs) (emitln "/**") (doseq [e docs] (when e (print-comment-lines e))) - (emitln "*/"))))) + (emitln " */"))))) (defmethod emit* :def - [{:keys [name var init env doc export test]}] + [{:keys [name var init env doc jsdoc export test]}] (let [mname (munge name)] (when init - (emit-comment doc (:jsdoc init)) + (emit-comment doc (concat jsdoc (:jsdoc init))) (emits var) (emits " = " init) ;; NOTE: JavaScriptCore does not like this under advanced compilation From 801861cc03d5dc3c783b18a375eec37725e6671b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 Feb 2015 13:15:11 -0500 Subject: [PATCH 0741/4033] CLJS-1068: node target define --- src/cljs/cljs/core.cljs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index b303ef7ab..f4f342a93 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -17,6 +17,9 @@ (def *unchecked-if* false) +(def ^{:jsdoc ["@define"]} + node-target false) + (defonce ^{:doc "Each runtime environment provides a different way to print output. Whatever function *print-fn* is bound to will be passed any From c6a9779285b770de0f1029c5903f184d128d5ac0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 Feb 2015 21:13:16 -0500 Subject: [PATCH 0742/4033] `cljs.core.node-target` to generic `cljs.core.target` Closure define --- src/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index f4f342a93..3d876525a 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -17,8 +17,8 @@ (def *unchecked-if* false) -(def ^{:jsdoc ["@define"]} - node-target false) +(def ^{:jsdoc ["@define {string}"]} + target "default") (defonce ^{:doc "Each runtime environment provides a different way to print output. From fbc52496ce7eff925dd5f42eb92ce5e4ac5bac6d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 Feb 2015 21:58:28 -0500 Subject: [PATCH 0743/4033] always add new line after goog.addDependency --- src/clj/cljs/closure.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index eee794f1f..11f69adaa 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -970,12 +970,12 @@ should contain the source for the given namespace name." (ns-list (deps/-provides input)) "], [" (ns-list (deps/-requires input)) - "]);"))) + "]);\n"))) (defn deps-file "Return a deps file string for a sequence of inputs." [opts sources] - (apply str (interpose "\n" (map #(add-dep-string opts %) sources)))) + (apply str (map #(add-dep-string opts %) sources))) (comment (path-relative-to (io/file "out/goog/base.js") {:url (deps/to-url "out/cljs/core.js")}) From 8ea45309a7a6b5a304f8843784edbff37bd4585e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 Feb 2015 22:00:44 -0500 Subject: [PATCH 0744/4033] always set :closure-defines "cljs.core.target" if :target supplied --- src/clj/cljs/closure.clj | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 11f69adaa..b737a7c87 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1321,12 +1321,14 @@ should contain the source for the given namespace name." (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) ups-deps (get-upstream-deps) - all-opts (-> opts - (assoc - :ups-libs (:libs ups-deps) - :ups-foreign-libs (:foreign-libs ups-deps) - :ups-externs (:externs ups-deps)) - (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) + all-opts (cond-> + (-> opts + (assoc + :ups-libs (:libs ups-deps) + :ups-foreign-libs (:foreign-libs ups-deps) + :ups-externs (:externs ups-deps)) + (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) + (:target opts) (assoc-in [:closure-defines "cljs.core.target"] (name (:target opts)))) emit-constants (or (and (= (:optimizations opts) :advanced) (not (false? (:optimize-constants opts)))) (:optimize-constants opts))] From 3fffee0f812c2aeb0fc16d15fa8eb10b59d869e1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Mar 2015 09:21:25 -0500 Subject: [PATCH 0745/4033] add `closure/add-implicit-options` to share more logic between compiler and REPLs --- src/clj/cljs/closure.clj | 23 ++++++++++++++--------- src/clj/cljs/repl.clj | 30 ++++++++++++++---------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index b737a7c87..7b05f9f87 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1310,6 +1310,19 @@ should contain the source for the given namespace name." (and (satisfies? deps/IJavaScript js) (deps/-foreign? js))) +(defn add-implicit-options [opts] + (let [{:keys [libs foreign-libs externs]} (get-upstream-deps)] + (cond-> + (-> opts + (assoc + :ups-libs libs + :ups-foreign-libs foreign-libs + :ups-externs externs) + (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) + (:target opts) + (assoc-in [:closure-defines "cljs.core.target"] + (name (:target opts)))))) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] @@ -1320,15 +1333,7 @@ should contain the source for the given namespace name." ([source opts compiler-env] (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) - ups-deps (get-upstream-deps) - all-opts (cond-> - (-> opts - (assoc - :ups-libs (:libs ups-deps) - :ups-foreign-libs (:foreign-libs ups-deps) - :ups-externs (:externs ups-deps)) - (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) - (:target opts) (assoc-in [:closure-defines "cljs.core.target"] (name (:target opts)))) + all-opts (add-implicit-options opts) emit-constants (or (and (= (:optimizations opts) :advanced) (not (false? (:optimize-constants opts)))) (:optimize-constants opts))] diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 456568989..3dacf2076 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -638,25 +638,23 @@ source-map-inline true} :as opts}] (print "To quit, type:" :cljs/quit) - (let [ups-deps (cljsc/get-upstream-deps) - {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts + (let [{:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} (merge {:cache-analysis true :source-map true} - (assoc (merge (-repl-options repl-env) opts) - :init init - :need-prompt prompt - :flush flush - :read read - :print print - :caught caught - :reader reader - :print-no-newline print-no-newline - :source-map-inline source-map-inline - :output-dir (or (:output-dir opts) - (:default-output-dir repl-env)) - :ups-libs (:libs ups-deps) - :ups-foreign-libs (:foreign-libs ups-deps)))] + (cljsc/add-implicit-options + (assoc (merge (-repl-options repl-env) opts) + :init init + :need-prompt prompt + :flush flush + :read read + :print print + :caught caught + :reader reader + :print-no-newline print-no-newline + :source-map-inline source-map-inline + :output-dir (or (:output-dir opts) + (:default-output-dir repl-env)))))] (env/with-compiler-env (or (::env/compiler repl-env) (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user From 19c14450197b3df9895eafd78961d86d31933016 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Mar 2015 09:26:12 -0500 Subject: [PATCH 0746/4033] if no :optimizations setting applied, default to :none --- src/clj/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 7b05f9f87..08aa0d689 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1321,7 +1321,9 @@ should contain the source for the given namespace name." (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) (:target opts) (assoc-in [:closure-defines "cljs.core.target"] - (name (:target opts)))))) + (name (:target opts))) + (nil? (:optimizations opts)) + (assoc :optimizations :none)))) (defn build "Given a source which can be compiled, produce runnable JavaScript." From 9d7f4f2a7ba73438cf9f5622ee16dfa3b66d6a9c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Mar 2015 10:23:16 -0500 Subject: [PATCH 0747/4033] CLJS-1014: Support Closure Defines under :none Best done via real compiler support. Other approaches have undesirable implications for `cljs.compiler/compile-file`, source map generation, etc. This is another case that illustrates treating REPL evaluation environments as compiler options is probably not a good idea. Node.js REPL now returns :default-target. This is used to establish the target before -setup so that the REPL can set cljs.core defines. --- src/clj/cljs/compiler.clj | 20 +++++++++++++++++++- src/clj/cljs/repl.clj | 4 +++- src/clj/cljs/repl/node.clj | 6 ++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 827ae2d15..fd7be8b59 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -423,13 +423,31 @@ (print-comment-lines e))) (emitln " */"))))) +(defn valid-define-value? [x] + (or (string? x) + (true? x) + (false? x) + (number? x))) + +(defn get-define [mname jsdoc] + (let [opts (get @env/*compiler* :options)] + (and (some #(.startsWith ^String % "@define") jsdoc) + opts + (= (:optimizations opts) :none) + (let [define (get-in opts [:closure-defines (str mname)])] + (when (valid-define-value? define) + (pr-str define)))))) + (defmethod emit* :def [{:keys [name var init env doc jsdoc export test]}] (let [mname (munge name)] (when init (emit-comment doc (concat jsdoc (:jsdoc init))) (emits var) - (emits " = " init) + (emits " = " + (if-let [define (get-define mname jsdoc)] + define + init)) ;; NOTE: JavaScriptCore does not like this under advanced compilation ;; this change was primarily for REPL interactions - David ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 3dacf2076..9226ee348 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -654,7 +654,9 @@ :print-no-newline print-no-newline :source-map-inline source-map-inline :output-dir (or (:output-dir opts) - (:default-output-dir repl-env)))))] + (:default-output-dir repl-env)) + :target (or (:target opts) + (:default-target repl-env)))))] (env/with-compiler-env (or (::env/compiler repl-env) (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index aef1bddf2..04fa39f7c 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -169,9 +169,6 @@ (aget (.. js/goog -dependencies_ -nameToPath) name)))))))))) (defrecord NodeEnv [host port socket proc] - repl/IReplEnvOptions - (-repl-options [this] - {:target :nodejs}) repl/IParseErrorMessage (-parse-error-message [_ _ _ _]) repl/IJavaScriptEnv @@ -192,7 +189,8 @@ :port (+ 49000 (rand-int 10000))} options)] (merge (NodeEnv. host port (atom nil) (atom nil)) - {:default-output-dir ".cljs_node_repl"}))) + {:default-output-dir ".cljs_node_repl" + :default-target :nodejs}))) (defn repl-env [& {:as options}] From 08f80622d9b7f68a95c1f47040912491e74b2712 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Mar 2015 10:34:34 -0500 Subject: [PATCH 0748/4033] cljs.core/target -> cljs.core/*target* --- src/clj/cljs/closure.clj | 2 +- src/cljs/cljs/core.cljs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 08aa0d689..c463a3958 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1320,7 +1320,7 @@ should contain the source for the given namespace name." :ups-externs externs) (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) (:target opts) - (assoc-in [:closure-defines "cljs.core.target"] + (assoc-in [:closure-defines (str (comp/munge 'cljs.core/*target*))] (name (:target opts))) (nil? (:optimizations opts)) (assoc :optimizations :none)))) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 3d876525a..b40fa6ef0 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -17,8 +17,9 @@ (def *unchecked-if* false) -(def ^{:jsdoc ["@define {string}"]} - target "default") +(def ^{:dyanmic true + :jsdoc ["@define {string}"]} + *target* "default") (defonce ^{:doc "Each runtime environment provides a different way to print output. From 1f023467e95cd081851fbb71601bc2dd639ad002 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Mar 2015 10:53:58 -0500 Subject: [PATCH 0749/4033] CLJS-1071: support symbol keys in :closure-defines munge and convert symbol keys to strings --- src/clj/cljs/closure.clj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c463a3958..52ed20ab1 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1311,7 +1311,14 @@ should contain the source for the given namespace name." (deps/-foreign? js))) (defn add-implicit-options [opts] - (let [{:keys [libs foreign-libs externs]} (get-upstream-deps)] + (let [opts (cond-> opts + (:closure-defines opts) + (assoc :closure-defines + (into {} + (map (fn [[k v]] + [(if (symbol? k) (str (comp/munge k)) k) v]) + (:closure-defines opts))))) + {:keys [libs foreign-libs externs]} (get-upstream-deps)] (cond-> (-> opts (assoc From de130a335bd761d580d04dadb8a5a43d3c0a35b4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 1 Mar 2015 13:26:34 -0500 Subject: [PATCH 0750/4033] CLJS-1064: ex-info is not printable Clean up ExceptionInfo implementation. Fix str. Add tests. --- src/cljs/cljs/core.cljs | 44 +++++++++++++++++++++++------------ test/cljs/cljs/core_test.cljs | 2 ++ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index b40fa6ef0..5bd3f710a 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -9181,8 +9181,6 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." ;;; ExceptionInfo -(deftype ExceptionInfo [message data cause]) - (defn- pr-writer-ex-info [obj writer opts] (-write writer "#ExceptionInfo{:message ") (pr-writer (.-message obj) writer opts) @@ -9194,25 +9192,41 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (pr-writer (.-cause obj) writer opts)) (-write writer "}")) +(defn ^{:jsdoc ["@constructor"]} + ExceptionInfo [message data cause] + (let [e (js/Error.)] + (this-as this + (set! (.-message this) message) + (set! (.-data this) data) + (set! (.-cause this) cause) + (do + (set! (.-name this) (.-name e)) + ;; non-standard + (set! (.-description this) (.-description e)) + (set! (.-number this) (.-number e)) + (set! (.-fileName this) (.-fileName e)) + (set! (.-lineNumber this) (.-lineNumber e)) + (set! (.-columnNumber this) (.-columnNumber e)) + (set! (.-stack this) (.-stack e))) + this))) + +(set! (.. ExceptionInfo -prototype -__proto__) js/Error.prototype) + +(extend-type ExceptionInfo + IPrintWithWriter + (-pr-writer [obj writer opts] + (pr-writer-ex-info obj writer opts))) + +(set! (.. ExceptionInfo -prototype -toString) + (fn [] + (this-as this (pr-str* this)))) + (defn ex-info "Alpha - subject to change. Create an instance of ExceptionInfo, an Error type that carries a map of additional data." ([msg data] (ex-info msg data nil)) ([msg data cause] - ;; this way each new ExceptionInfo instance will inherit - ;; stack property from newly created Error - (set! (.-prototype ExceptionInfo) (js/Error msg)) - (set! (.. ExceptionInfo -prototype -name) "ExceptionInfo") - (set! (.. ExceptionInfo -prototype -constructor) ExceptionInfo) - - ;; since we've changed the prototype, we need to - ;; re-establish protocol implementations here - (set! (.. ExceptionInfo -prototype -toString) pr-str*) - (extend-type ExceptionInfo - IPrintWithWriter - (-pr-writer [obj writer opts] - (pr-writer-ex-info obj writer opts))) (ExceptionInfo. msg data cause))) (defn ex-data diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 8a57e51f6..efb20c559 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1949,6 +1949,8 @@ (is (instance? js/Error (ex-info "asdf" {:foo 1}))) (is (= (pr-str (ex-info "abc" {:x 1})) "#ExceptionInfo{:message \"abc\", :data {:x 1}}")) (is (= (pr-str (ex-info "abc" {:x 1} "def")) "#ExceptionInfo{:message \"abc\", :data {:x 1}, :cause \"def\"}")) + (is (= (.toString (ex-info "abc" {:x 1} "def")) "#ExceptionInfo{:message \"abc\", :data {:x 1}, :cause \"def\"}")) + (is (= (str (ex-info "abc" {:x 1} "def")) "#ExceptionInfo{:message \"abc\", :data {:x 1}, :cause \"def\"}")) (is (not (instance? cljs.core.ExceptionInfo (js/Error.))))) (deftest test-435 From d717b4edea074fcfd3e718a6134238ba26f76f82 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 2 Mar 2015 08:45:54 -0500 Subject: [PATCH 0751/4033] CLJS-1072: Calling .hasOwnProperty("source") in Clojurescript's string/replace will break with ES6 Just use instance? to check for js/RegExp --- src/cljs/clojure/string.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/clojure/string.cljs b/src/cljs/clojure/string.cljs index f3009be38..ff6c11a0e 100644 --- a/src/cljs/clojure/string.cljs +++ b/src/cljs/clojure/string.cljs @@ -33,7 +33,7 @@ [s match replacement] (cond (string? match) (.replace s (js/RegExp. (gstring/regExpEscape match) "g") replacement) - (.hasOwnProperty match "source") + (instance? js/RegExp match) (.replace s (js/RegExp. (.-source match) "g") replacement) :else (throw (str "Invalid match arg: " match)))) From 22ccc7335a31680ddeef68edc897c492f7d96a23 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 2 Mar 2015 11:56:23 -0500 Subject: [PATCH 0752/4033] bump Rhino dep --- pom.template.xml | 2 +- script/bootstrap | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 4ba54ea0e..52a0b84d7 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -45,7 +45,7 @@ org.mozilla rhino - 1.7R4 + 1.7R5 org.clojure diff --git a/script/bootstrap b/script/bootstrap index dddef1c06..589407478 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,7 +6,7 @@ CLOJURE_RELEASE="1.6.0" CLOSURE_RELEASE="20150126" DJSON_RELEASE="0.2.3" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" -RHINO_RELEASE="1_7R3" +RHINO_RELEASE="1_7R5" TREADER_RELEASE="0.8.10" # check dependencies @@ -79,7 +79,7 @@ if [ "$1" = "--closure-library-head" ] ; then fi echo "Fetching Rhino..." -curl --retry 3 -O -s https://ftp.mozilla.org/pub/mozilla.org/js/rhino$RHINO_RELEASE.zip || { echo "Download failed."; exit 1; } +curl --retry 3 -LOk -s https://github.com/mozilla/rhino/releases/download/Rhino${RHINO_RELEASE}_RELEASE/rhino$RHINO_RELEASE.zip || { echo "Download failed."; exit 1; } unzip -qu rhino$RHINO_RELEASE.zip echo "Copying rhino$RHINO_RELEASE/js.jar to lib/js.jar..." cp rhino$RHINO_RELEASE/js.jar lib/js.jar From 8785fa9c9ff38c0fcebc32757c077070397018b7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 2 Mar 2015 15:37:26 -0500 Subject: [PATCH 0753/4033] CLJS-1073: :dynamic typo for *target* --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 5bd3f710a..304677fcc 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -17,7 +17,7 @@ (def *unchecked-if* false) -(def ^{:dyanmic true +(def ^{:dynamic true :jsdoc ["@define {string}"]} *target* "default") From 74b2d7197176eb63f79837d501e67b0f22e2839a Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 2 Mar 2015 19:05:27 -0500 Subject: [PATCH 0754/4033] minor Rhino REPL tweaks missing `var`, use Reader based Rino eval --- src/clj/cljs/repl/rhino.clj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 07a04bf30..376ffa5c5 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -21,7 +21,7 @@ (def ^String bootjs (str "var global = this;\n" - "CLOSURE_IMPORT_SCRIPT = function(src) {\n" + "var CLOSURE_IMPORT_SCRIPT = function(src) {\n" " var ns = \"cljs.repl.rhino\"," " name = \"load-file\"," " loadFile = Packages.clojure.lang.RT[\"var\"](ns,name);\n" @@ -85,7 +85,7 @@ used." [repl-env opts src] (let [goog-path (io/file (util/output-directory opts) "goog" src)] - (rhino-eval repl-env (.getPath goog-path) 1 (slurp goog-path)))) + (rhino-eval repl-env (.getPath goog-path) 1 (io/reader goog-path)))) (defn load-javascript [repl-env ns url] (try @@ -98,7 +98,7 @@ (let [scope (:scope repl-env) env (ana/empty-env) core (io/resource "cljs/core.cljs") - base-js (slurp (io/resource "goog/base.js")) + base-js (io/resource "goog/base.js") core-js (closure/compile core (assoc opts :output-file @@ -120,8 +120,8 @@ ;; define file loading, load goog.base, load repl deps (rhino-eval repl-env "bootjs" 1 bootjs) - (rhino-eval repl-env "goog/base.js" 1 base-js) - (rhino-eval repl-env "rhino_repl_deps.js" 1 (slurp repl-deps)) + (rhino-eval repl-env "goog/base.js" 1 (io/reader base-js)) + (rhino-eval repl-env "rhino_repl_deps.js" 1 (io/reader repl-deps)) ;; === Bootstrap === ;; load cljs.core, setup printing From 0febb0faad7689c049bd72e443def34acad77711 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 08:43:58 -0500 Subject: [PATCH 0755/4033] REPL options merging needs to be more disciplined eliminate :default-*. Instead first merge -repl-options, then original options, then clojure.main style REPL options (with supplied defaults). Rhino error handling WIP. Default wrap-fn will not work as catching the JS error will swallow the stacktrace. --- src/clj/cljs/repl.clj | 32 +++++++++++++++----------------- src/clj/cljs/repl/nashorn.clj | 7 ++++--- src/clj/cljs/repl/node.clj | 8 +++++--- src/clj/cljs/repl/rhino.clj | 27 +++++++++++++++++++++++---- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 9226ee348..4135fa792 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -467,13 +467,13 @@ form, evaluate the form and return the result. The result is always the value represented as a string." ([repl-env env form] - (eval-cljs repl-env env form nil)) + (eval-cljs repl-env env form *repl-opts*)) ([repl-env env form opts] (evaluate-form repl-env (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) "" form - (wrap-fn form) + ((or (:wrap opts) wrap-fn) form) opts))) ;; Special REPL fns, these provide compatiblity with Clojure functions @@ -620,7 +620,7 @@ (.printStackTrace e)))) (defn repl* - [repl-env {:keys [init need-prompt prompt flush read eval print caught reader print-no-newline source-map-inline] + [repl-env {:keys [init need-prompt prompt flush read eval print caught reader print-no-newline source-map-inline wrap] :or {init #() need-prompt #(if (readers/indexing-reader? *in*) (== (readers/get-column-number *in*) 1) @@ -643,20 +643,18 @@ (merge {:cache-analysis true :source-map true} (cljsc/add-implicit-options - (assoc (merge (-repl-options repl-env) opts) - :init init - :need-prompt prompt - :flush flush - :read read - :print print - :caught caught - :reader reader - :print-no-newline print-no-newline - :source-map-inline source-map-inline - :output-dir (or (:output-dir opts) - (:default-output-dir repl-env)) - :target (or (:target opts) - (:default-target repl-env)))))] + (merge-with (fn [a b] (if (nil? b) a b)) + (-repl-options repl-env) + opts + {:init init + :need-prompt prompt + :flush flush + :read read + :print print + :caught caught + :reader reader + :print-no-newline print-no-newline + :source-map-inline source-map-inline})))] (env/with-compiler-env (or (::env/compiler repl-env) (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 25cc6a4d2..2f43632ee 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -144,7 +144,8 @@ (defrecord NashornEnv [engine debug] repl/IReplEnvOptions - (-repl-options [this] {}) + (-repl-options [this] + {:output-dir ".cljs_nashorn_repl"}) repl/IJavaScriptEnv (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] (init-engine engine output-dir debug) @@ -206,6 +207,6 @@ Use a minimal bootstrapped cljs.core environment if not specified." [& {:keys [debug] :as opts}] (let [engine (create-engine)] - (merge (NashornEnv. engine debug) - {:default-output-dir ".cljs_nashorn_repl"} + (merge + (NashornEnv. engine debug) opts))) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 04fa39f7c..eeff59543 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -169,6 +169,10 @@ (aget (.. js/goog -dependencies_ -nameToPath) name)))))))))) (defrecord NodeEnv [host port socket proc] + repl/IReplEnvOptions + (-repl-options [this] + {:output-dir ".cljs_node_repl" + :target :nodejs}) repl/IParseErrorMessage (-parse-error-message [_ _ _ _]) repl/IJavaScriptEnv @@ -188,9 +192,7 @@ {:host "localhost" :port (+ 49000 (rand-int 10000))} options)] - (merge (NodeEnv. host port (atom nil) (atom nil)) - {:default-output-dir ".cljs_node_repl" - :default-target :nodejs}))) + (NodeEnv. host port (atom nil) (atom nil)))) (defn repl-env [& {:as options}] diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 376ffa5c5..43b72ee5d 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -69,12 +69,15 @@ ;; ============================================================================= (defn rhino-eval - [repl-env filename line js] + [{:keys [scope] :as repl-env} filename line js] (try (let [linenum (or line Integer/MIN_VALUE)] {:status :success :value (eval-result (-eval js repl-env filename linenum))}) (catch Throwable ex + ;; manually set *e + ;; (ScriptableObject/putProperty scope "cljs.core._STAR_e" + ;; (Context/javaToJS ex scope)) {:status :exception :value (.toString ex) :stacktrace (stacktrace ex)}))) @@ -145,10 +148,27 @@ (js/CLOSURE_IMPORT_SCRIPT (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) +;; Catching errors and rethrowing in Rhino swallows the original trace +;; https://groups.google.com/d/msg/mozilla.dev.tech.js-engine.rhino/inMyVKhPq6M/cY39hX20_z8J +(defn wrap-fn [form] + (cond + (and (seq? form) (= 'ns (first form))) identity + ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) + :else + (fn [x] + `(cljs.core.pr-str + (let [ret# ~x] + (set! *3 *2) + (set! *2 *1) + (set! *1 ret#) + ret#))))) + (defrecord RhinoEnv [] repl/IReplEnvOptions (-repl-options [this] - {:require-foreign true}) + {:require-foreign true + :output-dir ".cljs_rhino_repl" + :wrap wrap-fn}) repl/IJavaScriptEnv (-setup [this opts] (rhino-setup this opts)) @@ -167,8 +187,7 @@ ;; Rhino is slow even with optimizations enabled (.setOptimizationLevel cx -1) (merge (RhinoEnv.) - {:default-output-dir ".cljs_rhino_repl" - :cx cx + {:cx cx :scope (.initStandardObjects cx)}))) (comment From d9018fede7045feb406815d0248a58f7eb7d1a3d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 09:06:57 -0500 Subject: [PATCH 0756/4033] manually set *e in Rhino on JS exception --- src/clj/cljs/repl/rhino.clj | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 43b72ee5d..1b038e717 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -76,11 +76,15 @@ :value (eval-result (-eval js repl-env filename linenum))}) (catch Throwable ex ;; manually set *e - ;; (ScriptableObject/putProperty scope "cljs.core._STAR_e" - ;; (Context/javaToJS ex scope)) - {:status :exception - :value (.toString ex) - :stacktrace (stacktrace ex)}))) + (let [top-level (-> scope + (ScriptableObject/getProperty "cljs") + (ScriptableObject/getProperty "core"))] + (ScriptableObject/putProperty top-level "_STAR_e" + (Context/javaToJS ex scope)) + (util/debug-prn top-level) + {:status :exception + :value (.toString ex) + :stacktrace (stacktrace ex)})))) (defn load-file "Load a JavaScript. This is needed to load JavaScript files before the Rhino From 2500f0026806703b1bb050d424f69a3c55135197 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 09:09:58 -0500 Subject: [PATCH 0757/4033] remove stray cljs.util/debug-prn --- src/clj/cljs/repl/rhino.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 1b038e717..0e0d35fe6 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -81,7 +81,6 @@ (ScriptableObject/getProperty "core"))] (ScriptableObject/putProperty top-level "_STAR_e" (Context/javaToJS ex scope)) - (util/debug-prn top-level) {:status :exception :value (.toString ex) :stacktrace (stacktrace ex)})))) From a45127480f0769c87d8d0a1b7dcfa58a00ee65cd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 09:41:11 -0500 Subject: [PATCH 0758/4033] source mapping for Rhino --- src/clj/cljs/repl/rhino.clj | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 0e0d35fe6..4afe92b46 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -172,6 +172,23 @@ {:require-foreign true :output-dir ".cljs_rhino_repl" :wrap wrap-fn}) + repl/IParseStacktrace + (-parse-stacktrace [this frames-str ret {output-dir :output-dir}] + (vec + (map + (fn [frame-str] + (let [[file-side line-fn-side] (string/split frame-str #":") + file (string/replace file-side #"\s+at\s+" "") + [line function] (string/split line-fn-side #"\s+")] + {:file (string/replace file (str output-dir File/separator) "") + :function (when function + (-> function + (string/replace "(" "") + (string/replace ")" ""))) + :line (when line + (Integer/parseInt line)) + :column 0})) + (string/split frames-str #"\n")))) repl/IJavaScriptEnv (-setup [this opts] (rhino-setup this opts)) @@ -195,6 +212,15 @@ (comment + (repl/-parse-stacktrace (repl-env) + "\tat .cljs_rhino_repl/goog/../cljs/core.js:4215 (seq) +\tat .cljs_rhino_repl/goog/../cljs/core.js:4245 (first) +\tat .cljs_rhino_repl/goog/../cljs/core.js:5295 (ffirst) +\tat :1 +\tat :1" + nil + {:output-dir ".cljs_rhino_repl"}) + (require '[cljs.repl :as repl]) (require '[cljs.repl.rhino :as rhino]) (def env (rhino/repl-env)) From 8279fbd9df5e13f302e930ae3a022a1fe5856e57 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 09:47:10 -0500 Subject: [PATCH 0759/4033] clarify permitted values for :file in canonical stacktrace --- src/clj/cljs/repl.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 4135fa792..e56c36dff 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -137,7 +137,9 @@ :line :column }*] - :file must be a URL path (without protocol) relative to :output-dir.")) + :file must be a URL path (without protocol) relative to :output-dir. If + no source file can be supplied (such as REPL defs), :file may be a custom + identifier string surrounded by angle brackets, i.e. \"\".")) (defprotocol IPrintStacktrace (-print-stacktrace [repl-env stacktrace error build-options] From 49229f5bad95a1be6e06b30cfd9a4e497fe8f582 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 09:54:50 -0500 Subject: [PATCH 0760/4033] print NO_FUNCTION if it cannot be determined --- src/clj/cljs/repl.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index e56c36dff..9a3f0c60c 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -291,7 +291,9 @@ ([stacktrace opts] (doseq [{:keys [function file line column]} (mapped-stacktrace stacktrace opts)] - ((:print opts) "\t" (str function " (" file ":" line ":" column ")"))))) + ((:print opts) "\t" + (str (or function "NO_FUNCTION") + " (" file ":" line ":" column ")"))))) (comment (cljsc/build "samples/hello/src" From 0d73f458b848657bbb26940a310bd732c9f59d67 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 10:02:40 -0500 Subject: [PATCH 0761/4033] NO_FUNCTION too noisy for stacktraces in Closure Library --- src/clj/cljs/repl.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 9a3f0c60c..c659e5a0a 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -292,8 +292,8 @@ (doseq [{:keys [function file line column]} (mapped-stacktrace stacktrace opts)] ((:print opts) "\t" - (str (or function "NO_FUNCTION") - " (" file ":" line ":" column ")"))))) + (str (and function (str function " ")) + "(" file ":" line ":" column ")"))))) (comment (cljsc/build "samples/hello/src" From 42a263f7b9ee780417119d8b63f8fed5f892ffda Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 2 Mar 2015 21:18:03 -0500 Subject: [PATCH 0762/4033] CLJS-1077: analyze-deps infinite recursive loop Delegate to second arity, passing nil for opts. --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 284f6cef9..0eae8c70e 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1104,7 +1104,7 @@ false))) (defn analyze-deps - ([lib deps env] (analyze-deps lib deps env)) + ([lib deps env] (analyze-deps lib deps env nil)) ([lib deps env opts] (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] (assert (every? #(not (contains? *cljs-dep-set* %)) deps) From bd12d90fcb80f03b9cb4777079f1a4f4d217eae5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 12:01:53 -0500 Subject: [PATCH 0763/4033] update project.clj, clarify mapped-stacktrace docstring --- project.clj | 4 ++-- src/clj/cljs/repl.clj | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/project.clj b/project.clj index fb8f0a3f6..aa0e19c48 100644 --- a/project.clj +++ b/project.clj @@ -13,9 +13,9 @@ [org.clojure/tools.reader "0.8.10"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] - [org.mozilla/rhino "1.7R4"]] + [org.mozilla/rhino "1.7R5"]] :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} - :1.6 {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]}} + :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}} :aliases {"test-all" ["with-profile" "test,1.5:test,1.6" "test"] "check-all" ["with-profile" "1.5:1.6" "check"]} :min-lein-version "2.0.0") diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c659e5a0a..37f9b04e9 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -249,9 +249,10 @@ :line :column }*] - :file must be a URL path (without protocol) relative to :output-dir. The - returned mapped stacktrace will also contain :url entries to the original - sources if it can be determined from the classpath." + :file must be a URL path (without protocol) relative to :output-dir or a + identifier delimited by angle brackets. The returned mapped stacktrace will + also contain :url entries to the original sources if it can be determined + from the classpath." ([stacktrace] (mapped-stacktrace stacktrace nil)) ([stacktrace opts] (let [read-source-map' (memoize read-source-map) From 31494aa6b1f09dc3e72e9aa09a904933c8c9acaf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 12:08:37 -0500 Subject: [PATCH 0764/4033] AOT everything when building uberjar --- project.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project.clj b/project.clj index aa0e19c48..0892ebe03 100644 --- a/project.clj +++ b/project.clj @@ -15,7 +15,8 @@ [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} - :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}} + :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} + :uberjar {:aot :all}} :aliases {"test-all" ["with-profile" "test,1.5:test,1.6" "test"] "check-all" ["with-profile" "1.5:1.6" "check"]} :min-lein-version "2.0.0") From cf5fccc7fdf5342747d34b0666a4f937fe641faf Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 3 Mar 2015 13:04:14 -0500 Subject: [PATCH 0765/4033] CLJS-865: remove `cljs.js-deps/goog-resource` hack deps.js and base.js has been dropped from Closure 3rd party JAR for some time now --- src/clj/cljs/closure.clj | 6 +++--- src/clj/cljs/js_deps.clj | 21 +++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 52ed20ab1..3eb6d8105 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -537,7 +537,7 @@ should contain the source for the given namespace name." (when (seq unprovided) (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) (cons - (javascript-file nil (deps/goog-resource "goog/base.js") ["goog"] nil) + (javascript-file nil (io/resource "goog/base.js") ["goog"] nil) (deps/dependency-order (concat (map @@ -1140,8 +1140,8 @@ should contain the source for the given namespace name." (write-javascript {} "goog.provide('demo');\nalert('hello');\n") ;; write something from a jar file to disk (source-on-disk {} - {:url (deps/goog-resource "goog/base.js") - :source (with-open [reader (io/reader (deps/goog-resource "goog/base.js"))] + {:url (io/resource "goog/base.js") + :source (with-open [reader (io/reader (io/resource "goog/base.js"))] (slurp reader))}) ;; doesn't write a file that is already on disk (source-on-disk {} {:url (io/resource "cljs/core.cljs")}) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index b8e3ccb27..24b0fb2e1 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -244,15 +244,16 @@ JavaScript library containing provide/require 'declarations'." (library-dependencies {:foreign-libs [{:file "cljs/nodejs_externs.js" :provides ["my.example"]}]})) -(defn goog-resource - "Helper to disambiguate Google Closure Library resources from Google - Closure Library Third Party resoures." - [path] - (first - (filter - (fn [res] - (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath ^URL res))) - (enumeration-seq (.getResources (.getContextClassLoader (Thread/currentThread)) path))))) +;; NO LONGER NEEDED, deps.js and base.js now removed from build +;(defn goog-resource +; "Helper to disambiguate Google Closure Library resources from Google +; Closure Library Third Party resoures." +; [path] +; (first +; (filter +; (fn [res] +; (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath ^URL res))) +; (enumeration-seq (.getResources (.getContextClassLoader (Thread/currentThread)) path))))) (defn goog-dependencies* "Create an index of Google dependencies by namespace and file name." @@ -260,7 +261,7 @@ JavaScript library containing provide/require 'declarations'." (letfn [(parse-list [s] (when (> (count s) 0) (-> (.substring ^String s 1 (dec (count s))) (string/split #"'\s*,\s*'"))))] - (with-open [reader (io/reader (goog-resource "goog/deps.js"))] + (with-open [reader (io/reader (io/resource "goog/deps.js"))] (->> (line-seq reader) (map #(re-matches #"^goog\.addDependency\(['\"](.*)['\"],\s*\[(.*)\],\s*\[(.*)\]\);.*" %)) (remove nil?) From de15ba8f756457f9cb4f5758848147911665e56b Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 3 Mar 2015 19:29:13 -0500 Subject: [PATCH 0766/4033] CLJS-1034: Support REPL-defined functions in stacktrace infrastructure Make `cljs.repl/mapped-stacktrace` nil safe for all the stack element components. If handed :file that starts with "<" or nil :file prepend "NO_SOURCE_FILE" a la Clojure. Smarter printing for nil values of a stack element. --- src/clj/cljs/repl.clj | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 37f9b04e9..c9315b60e 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -260,28 +260,39 @@ (vec (for [{:keys [function file line column] :as frame} stacktrace] ;; need to convert file, a relative URL style path, to host-specific file - (let [rfile (io/file (URL. (.toURL (io/file (util/output-directory opts))) file)) + (let [no-source-file? (if-not file + true + (.startsWith file "<")) + rfile (when-not no-source-file? + (io/file (URL. (.toURL (io/file (util/output-directory opts))) file))) [sm {:keys [ns source-file] :as ns-info}] - ((juxt read-source-map' ns-info') rfile) + (when-not no-source-file? + ((juxt read-source-map' ns-info') rfile)) [line' column'] (if ns-info (mapped-line-and-column sm line column) [line column]) name' (if (and ns-info function) (symbol (name ns) (cljrepl/demunge function)) function) - file' (string/replace - (.getCanonicalFile - (if ns-info - source-file - (io/file rfile))) - (str (System/getProperty "user.dir") File/separator) "") + file' (if no-source-file? + file + (string/replace + (.getCanonicalFile + (if ns-info + source-file + (io/file rfile))) + (str (System/getProperty "user.dir") File/separator) "")) url (or (and ns-info (io/resource (util/ns->relpath ns))) - (io/resource file))] + (and file (io/resource file)))] (merge {:function name' - :file (io/file file') - :line line' - :column column'} + :file (if no-source-file? + (str "NO_SOURCE_FILE" + (when file + (str " " file))) + (io/file file')) + :line line' + :column column'} (when url {:url url})))))))) @@ -293,8 +304,8 @@ (doseq [{:keys [function file line column]} (mapped-stacktrace stacktrace opts)] ((:print opts) "\t" - (str (and function (str function " ")) - "(" file ":" line ":" column ")"))))) + (str (when function (str function " ")) + "(" file (when line (str ":" line)) (when column (str ":" column)) ")"))))) (comment (cljsc/build "samples/hello/src" From 84f5f615b2227032a4321ff15586117175cdbe8b Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 3 Mar 2015 19:57:38 -0500 Subject: [PATCH 0767/4033] clarify the need for pluggability of :wrap by custom REPL evaluation environments --- src/clj/cljs/repl.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index c9315b60e..807b9fe0f 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -489,6 +489,8 @@ (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) "" form + ;; the pluggability of :wrap is needed for older JS runtimes like Rhino + ;; where catching the error will swallow the original trace ((or (:wrap opts) wrap-fn) form) opts))) @@ -599,7 +601,7 @@ 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace - (fn self + (fn selfe ([repl-env env form] (self env repl-env form nil)) ([repl-env env [_ ns :as form] opts] From 3690348312200e7703d3207a602f729c5496b584 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 3 Mar 2015 20:07:05 -0500 Subject: [PATCH 0768/4033] typo --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 807b9fe0f..4616b7d73 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -601,7 +601,7 @@ 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace - (fn selfe + (fn self ([repl-env env form] (self env repl-env form nil)) ([repl-env env [_ ns :as form] opts] From 7d2565026f1478d0cd2bfa847763c1bd00075561 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 3 Mar 2015 20:08:34 -0500 Subject: [PATCH 0769/4033] change `cljs.analyzer/cache-file` to return `cljs/aot/core.cljs.cache.edn` resource if available --- src/clj/cljs/analyzer.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0eae8c70e..45ab860e3 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1888,8 +1888,13 @@ argument, which the reader will use in any emitted errors." (defn cache-file ([src] (cache-file src "out")) ([src output-dir] - (let [ns-info (parse-ns src)] - (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn"))))) + (if-let [core-cache + (and (util/url? src) + (.endsWith (.getPath ^URL src) (str "cljs" File/separator "core.cljs")) + (io/resource "cljs/aot/core.cljs.cache.edn"))] + core-cache + (let [ns-info (parse-ns src)] + (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn")))))) (defn last-modified [src] (cond From a43ae53688a3202534d19fc34ffd30a69951680a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 22:18:24 -0500 Subject: [PATCH 0770/4033] Clarify `cljs.analyzer/cache-file` docstring add `cljs.closure/aot-cache-core` helper, prep for CLJS-897 and CLJS-899. `cljs.compiler/compile-file` is now :cache-analysis aware. Previously compilation would not write out analysis cache files. --- src/clj/cljs/analyzer.clj | 2 ++ src/clj/cljs/closure.clj | 10 ++++++++++ src/clj/cljs/compiler.clj | 3 +++ 3 files changed, 15 insertions(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 45ab860e3..d689c0465 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1886,6 +1886,8 @@ argument, which the reader will use in any emitted errors." ret)))) (defn cache-file + "Given a ClojureScript source file returns the _output_ path to the analysis + cache file." ([src] (cache-file src "out")) ([src output-dir] (if-let [core-cache diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 3eb6d8105..bd649e6fb 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1552,6 +1552,16 @@ should contain the source for the given namespace name." goog-ns (str goog-ns)))))) +(defn aot-cache-core [] + (let [src (io/file "src" "cljs" "cljs" "core.cljs") + dest (io/file "src" "aot_cljs" "cljs" "core.js")] + (util/mkdirs dest) + (env/with-compiler-env (env/default-compiler-env) + (comp/compile-file src dest + {:source-map true + :cache-analysis true + :output-dir "src/aot_cljs"})))) + (comment (println (build '[(ns hello.core) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index fd7be8b59..4deeee382 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1072,6 +1072,9 @@ (fn [{:keys [recompile visited]}] {:recompile (into recompile (ana/ns-dependents ns)) :visited (conj visited ns)}))) + (when (true? (:cache-analysis opts)) + (ana/write-analysis-cache ns + (ana/cache-file src (util/output-directory opts)))) ret)) (do (when-not (contains? (::ana/namespaces @env/*compiler*) ns) From 73d844fc3b8e7a361915fc8b0138e51be355bb93 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Mar 2015 22:23:07 -0500 Subject: [PATCH 0771/4033] fix cross platform issue w/ aot cache logic, fix wrong resource path for aot cache --- src/clj/cljs/analyzer.clj | 2 +- src/clj/cljs/closure.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index d689c0465..724c2e00e 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1893,7 +1893,7 @@ argument, which the reader will use in any emitted errors." (if-let [core-cache (and (util/url? src) (.endsWith (.getPath ^URL src) (str "cljs" File/separator "core.cljs")) - (io/resource "cljs/aot/core.cljs.cache.edn"))] + (io/resource "aot_cljs/cljs/core.cljs.cache.edn"))] core-cache (let [ns-info (parse-ns src)] (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn")))))) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index bd649e6fb..7c2f2c181 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1560,7 +1560,7 @@ should contain the source for the given namespace name." (comp/compile-file src dest {:source-map true :cache-analysis true - :output-dir "src/aot_cljs"})))) + :output-dir (sr "src" File/separator "aot_cljs")})))) (comment From c608c41d88fdb98837465f15969ee92d3bfe5b88 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 4 Mar 2015 06:25:03 -0500 Subject: [PATCH 0772/4033] typo --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 7c2f2c181..7a921555e 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1560,7 +1560,7 @@ should contain the source for the given namespace name." (comp/compile-file src dest {:source-map true :cache-analysis true - :output-dir (sr "src" File/separator "aot_cljs")})))) + :output-dir (str "src" File/separator "aot_cljs")})))) (comment From aced83b21118b6d64fba566b67e7a4f49a6cfbc8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 4 Mar 2015 07:48:56 -0500 Subject: [PATCH 0773/4033] include src/aot_cljs to the classpath --- Clojurescript.iml | 1 + bin/cljsc | 2 +- project.clj | 2 +- script/browser-repl | 2 +- script/nashornrepljs | 2 +- script/noderepljs | 2 +- script/repl | 2 +- script/repljs | 2 +- 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index bf1c6c09f..0ce06172f 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -8,6 +8,7 @@ + diff --git a/bin/cljsc b/bin/cljsc index 28f744f1a..4bf29e80b 100755 --- a/bin/cljsc +++ b/bin/cljsc @@ -8,7 +8,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do +for next in lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done diff --git a/project.clj b/project.clj index 0892ebe03..6c44de919 100644 --- a/project.clj +++ b/project.clj @@ -6,7 +6,7 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :jvm-opts ^:replace ["-Xmx512m" "-server"] :source-paths ["src/clj"] - :resource-paths ["src/cljs"] + :resource-paths ["src/cljs" "src/aot_cljs"] :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.6.0"] [org.clojure/data.json "0.2.3"] diff --git a/script/browser-repl b/script/browser-repl index 3736b9b70..89a512db7 100755 --- a/script/browser-repl +++ b/script/browser-repl @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do +for next in lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done diff --git a/script/nashornrepljs b/script/nashornrepljs index 0b7c50b64..4510d6030 100755 --- a/script/nashornrepljs +++ b/script/nashornrepljs @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do +for next in lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done diff --git a/script/noderepljs b/script/noderepljs index 4c75150ce..4412dc2f4 100755 --- a/script/noderepljs +++ b/script/noderepljs @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do +for next in lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done diff --git a/script/repl b/script/repl index dbe548530..2a15f3c09 100755 --- a/script/repl +++ b/script/repl @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in classes: lib/*: src/clj: src/cljs: test/cljs; do +for next in classes: lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done diff --git a/script/repljs b/script/repljs index 5b2511302..637401a61 100755 --- a/script/repljs +++ b/script/repljs @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in classes: lib/*: src/clj: src/cljs: test/cljs; do +for next in classes: lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done From 306abad55f522646f1cc2cf785907e398f87efc7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 4 Mar 2015 08:41:19 -0500 Subject: [PATCH 0774/4033] revert including src/aot_cljs to the classpath, will be writing core AOT cache files into the standard source locations --- Clojurescript.iml | 1 - bin/cljsc | 2 +- project.clj | 2 +- script/browser-repl | 2 +- script/nashornrepljs | 2 +- script/noderepljs | 2 +- script/repl | 2 +- script/repljs | 2 +- 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index 0ce06172f..bf1c6c09f 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -8,7 +8,6 @@ - diff --git a/bin/cljsc b/bin/cljsc index 4bf29e80b..28f744f1a 100755 --- a/bin/cljsc +++ b/bin/cljsc @@ -8,7 +8,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do +for next in lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done diff --git a/project.clj b/project.clj index 6c44de919..0892ebe03 100644 --- a/project.clj +++ b/project.clj @@ -6,7 +6,7 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :jvm-opts ^:replace ["-Xmx512m" "-server"] :source-paths ["src/clj"] - :resource-paths ["src/cljs" "src/aot_cljs"] + :resource-paths ["src/cljs"] :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.6.0"] [org.clojure/data.json "0.2.3"] diff --git a/script/browser-repl b/script/browser-repl index 89a512db7..3736b9b70 100755 --- a/script/browser-repl +++ b/script/browser-repl @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do +for next in lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done diff --git a/script/nashornrepljs b/script/nashornrepljs index 4510d6030..0b7c50b64 100755 --- a/script/nashornrepljs +++ b/script/nashornrepljs @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do +for next in lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done diff --git a/script/noderepljs b/script/noderepljs index 4412dc2f4..4c75150ce 100755 --- a/script/noderepljs +++ b/script/noderepljs @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do +for next in lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done diff --git a/script/repl b/script/repl index 2a15f3c09..dbe548530 100755 --- a/script/repl +++ b/script/repl @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in classes: lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do +for next in classes: lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done diff --git a/script/repljs b/script/repljs index 637401a61..5b2511302 100755 --- a/script/repljs +++ b/script/repljs @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in classes: lib/*: src/clj: src/cljs: src/aot_cljs: test/cljs; do +for next in classes: lib/*: src/clj: src/cljs: test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done From 53f55da9426f8a34fc60e0af0b111c12d113f993 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 4 Mar 2015 08:58:35 -0500 Subject: [PATCH 0775/4033] AOT core wip `cljs.analyzer/cache-file` now searches in the correct location for core analysis cache `cljs.analyzer/requires-analysis?` now returns true if cache is a URL that matches the expect core analysis cache location `cljs.closure/aot-cache-core` no longer sets :cache-analysis true as this will write the cache file to a default filename, instead write out the cache ourselves drop File type hint from `cljs.util/compiled-by-version`, could be URL or File --- src/clj/cljs/analyzer.clj | 23 ++++++++++++++++------- src/clj/cljs/closure.clj | 15 +++++++++++---- src/clj/cljs/util.clj | 2 +- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 724c2e00e..b03c80fd0 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1893,7 +1893,7 @@ argument, which the reader will use in any emitted errors." (if-let [core-cache (and (util/url? src) (.endsWith (.getPath ^URL src) (str "cljs" File/separator "core.cljs")) - (io/resource "aot_cljs/cljs/core.cljs.cache.edn"))] + (io/resource "cljs/core.cljs.cache.aot.edn"))] core-cache (let [ns-info (parse-ns src)] (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn")))))) @@ -1911,9 +1911,17 @@ argument, which the reader will use in any emitted errors." ([src output-dir] (let [cache (cache-file src output-dir)] (requires-analysis? src cache output-dir))) - ([src ^File cache output-dir] - (if (not (.exists cache)) + ([src cache output-dir] + (cond + (and (util/url? cache) + (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.edn")) + false + + (and (util/file? cache) + (not (.exists ^File cache))) true + + :else (let [out-src (util/to-target-file output-dir (parse-ns src))] (if (not (.exists out-src)) true @@ -1951,11 +1959,12 @@ argument, which the reader will use in any emitted errors." (let [path (if (instance? File res) (.getPath ^File res) (.getPath ^URL res)) - cache (when (and (:cache-analysis opts) output-dir) + cache (when (or (= f "cljs/core.cljs") + (and (:cache-analysis opts) output-dir)) (cache-file res output-dir))] (when-not (get-in @env/*compiler* [::analyzed-cljs path]) - (if (or (not (:cache-analysis opts)) - (not output-dir) + (if (or (not= f "cljs/core.cljs") + (not cache) (requires-analysis? res output-dir)) (binding [*cljs-ns* 'cljs.user *cljs-file* path @@ -1972,7 +1981,7 @@ argument, which the reader will use in any emitted errors." (recur (:name ast) (next forms)) (recur ns (next forms)))) ns))] - (when cache + (when (and cache (true? (:cache-analysis opts))) (write-analysis-cache ns cache)) (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))) ;; we want want to keep dependency analysis information diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 7a921555e..a0cc3af30 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1553,16 +1553,23 @@ should contain the source for the given namespace name." (str goog-ns)))))) (defn aot-cache-core [] - (let [src (io/file "src" "cljs" "cljs" "core.cljs") - dest (io/file "src" "aot_cljs" "cljs" "core.js")] + (let [base-path (io/file "src" "cljs" "cljs") + src (io/file base-path "core.cljs") + dest (io/file base-path "core.aot.js") + cache (io/file base-path "core.cljs.cache.aot.edn")] (util/mkdirs dest) (env/with-compiler-env (env/default-compiler-env) (comp/compile-file src dest {:source-map true - :cache-analysis true - :output-dir (str "src" File/separator "aot_cljs")})))) + :output-dir (str "src" File/separator "cljs")}) + (ana/write-analysis-cache 'cljs.core cache)))) (comment + (time + (do (aot-cache-core) nil)) + + (time + (do (ana/analyze-file "cljs/core.cljs") nil)) (println (build '[(ns hello.core) (defn ^{:export greet} greet [n] (str "Hola " n)) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index c90c6d3e3..1b1e572b3 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -33,7 +33,7 @@ "-SNAPSHOT")) "0.0-0000")) -(defn ^String compiled-by-version [^File f] +(defn ^String compiled-by-version [f] (with-open [reader (io/reader f)] (let [match (->> reader line-seq first (re-matches #".*ClojureScript (\d+\.\d+-\d+).*$"))] From 592d28a7e6daf4e9fa9ce8b46661aea9d455c410 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 4 Mar 2015 09:53:45 -0500 Subject: [PATCH 0776/4033] CLJS-978: Analysis caching doesn't account for constants table `cljs.analyzer/register-constant!` now takes an optional analysis `env`. If supplied will use the current ns to add this to the `:cljs.analyzer/constants` set for the namespace. when reading analysis cache off disk in `cljs.analyzer/analyze-file` add all constants in `:cljs.analyzer/constants` back to the compilation environment via `cljs.analyzer/register-constant!` --- src/clj/cljs/analyzer.clj | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index b03c80fd0..db88632b2 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -204,12 +204,20 @@ (Exception. (str "constant type " (type value) " not supported"))))] (symbol (str prefix (swap! constant-counter inc))))) -(defn- register-constant! [val] - (swap! env/*compiler* update-in [::constant-table] - (fn [table] - (if (get table val) - table - (assoc table val (gen-constant-id val)))))) +(defn- register-constant! + ([val] (register-constant! nil val)) + ([env val] + (swap! env/*compiler* + (fn [cenv] + (cond-> + (-> cenv + (update-in [::constant-table] + (fn [table] + (if (get table val) + table + (assoc table val (gen-constant-id val)))))) + env (update-in [::namespaces (-> env :ns :name) ::constants] + (fnil conj #{}) val)))))) (def default-namespaces '{cljs.core {:name cljs.core} cljs.user {:name cljs.user}}) @@ -469,7 +477,7 @@ ;; TODO: move this logic out - David (defn analyze-keyword [env sym] - (register-constant! sym) + (register-constant! env sym) {:op :constant :env env :form sym :tag 'cljs.core/Keyword}) (defn get-tag [e] @@ -1994,7 +2002,11 @@ argument, which the reader will use in any emitted errors." :load-macros true}))] (when (or *verbose* (:verbose opts)) (util/debug-prn "Reading analysis cache for " res)) - (swap! env/*compiler* assoc-in [::analyzed-cljs path] true) - (swap! env/*compiler* assoc-in [::namespaces ns] - (edn/read-string (slurp cache)))))))))))) - + (swap! env/*compiler* + (fn [cenv] + (let [cached-ns (edn/read-string (slurp cache))] + (doseq [x (::constants cached-ns)] + (register-constant! x)) + (-> cenv + (assoc-in [::analyzed-cljs path] true) + (assoc-in [::namespaces ns] cached-ns)))))))))))))) From 958580b5a6daf40e31f4e9f30bdb0ca333dab1be Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 4 Mar 2015 09:55:29 -0500 Subject: [PATCH 0777/4033] update `cljs.core/apply` docstring, it is now lazy add :verbose true to script/test --- script/test | 2 +- src/cljs/cljs/core.cljs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/script/test b/script/test index 0e2ba5a83..388532859 100755 --- a/script/test +++ b/script/test @@ -9,7 +9,7 @@ possible=4 ran=0 #bin/cljsc test >out/core-test.js -bin/cljsc test "{:optimizations :advanced :output-wrapper true :compiler-stats true}" > builds/out-adv/core-advanced-test.js +bin/cljsc test "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true}" > builds/out-adv/core-advanced-test.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 304677fcc..39035cf96 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -2988,8 +2988,7 @@ reduces them without incurring seq initialization" (set! *unchecked-if* true) (defn apply - "Applies fn f to the argument list formed by prepending intervening arguments to args. - First cut. Not lazy. Needs to use emitted toApply." + "Applies fn f to the argument list formed by prepending intervening arguments to args." ([f args] (let [fixed-arity (.-cljs$lang$maxFixedArity f)] (if (.-cljs$lang$applyTo f) From 7a3286f1e2775a84476bf2d16eef139d4e4c8822 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 4 Mar 2015 18:12:13 -0500 Subject: [PATCH 0778/4033] CLJS-1079: add way to execute arbitrary fn upon watch build completion `cljs.closure/watch` - Move try/catch logic into local `buildf`, switch to catch `Throwable`. Support `:watch-fn` option. --- src/clj/cljs/closure.clj | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index a0cc3af30..1a700fe00 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1450,7 +1450,8 @@ should contain the source for the given namespace name." (defn watch "Given a source directory, produce runnable JavaScript. Watch the source directory for changes rebuliding when necessary. Takes the same arguments as - cljs.closure/build." + cljs.closure/build in addition to :watch-fn, a function of no arguments to + run after a successful build." ([source opts] (watch source opts (if-not (nil? env/*compiler*) @@ -1461,11 +1462,16 @@ should contain the source for the given namespace name." fs (.getFileSystem path) service (.newWatchService fs)] (letfn [(buildf [] - (let [start (System/nanoTime)] - (build source opts compiler-env) - (println "... done. Elapsed" - (/ (unchecked-subtract (System/nanoTime) start) 1e9) "seconds") - (flush))) + (try + (let [start (System/nanoTime)] + (build source opts compiler-env) + (println "... done. Elapsed" + (/ (unchecked-subtract (System/nanoTime) start) 1e9) "seconds") + (flush)) + (when-let [f (:watch-fn opts)] + (f)) + (catch Throwable e + (.printStackTrace e)))) (watch-all [^Path root] (Files/walkFileTree root (reify @@ -1501,10 +1507,7 @@ should contain the source for the given namespace name." (seq (.pollEvents key))) (println "Change detected, recompiling...") (flush) - (try - (buildf) - (catch Exception e - (.printStackTrace e)))) + (buildf)) (recur key)))))))) (comment @@ -1514,7 +1517,10 @@ should contain the source for the given namespace name." :output-dir "samples/hello/out" :cache-analysis true :source-map true - :verbose true}) + :verbose true + :watch-fn + (fn [] + (println "Success!"))}) ) ;; ============================================================================= From 547d03217046ad4341276bded320fe64c480bed0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 4 Mar 2015 18:30:18 -0500 Subject: [PATCH 0779/4033] CLJS-1080: Analysis cache should keep constants in visit order Maintain constant visit order by tracking both seen and order --- src/clj/cljs/analyzer.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index db88632b2..3676c4ca7 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -217,7 +217,12 @@ table (assoc table val (gen-constant-id val)))))) env (update-in [::namespaces (-> env :ns :name) ::constants] - (fnil conj #{}) val)))))) + (fn [{:keys [seen order] :or {seen #{} order []} :as constants}] + (cond-> constants + (not (contains? seen val)) + (assoc + :seen (conj seen val) + :order (conj order val)))))))))) (def default-namespaces '{cljs.core {:name cljs.core} cljs.user {:name cljs.user}}) @@ -2005,7 +2010,7 @@ argument, which the reader will use in any emitted errors." (swap! env/*compiler* (fn [cenv] (let [cached-ns (edn/read-string (slurp cache))] - (doseq [x (::constants cached-ns)] + (doseq [x (get-in cached-ns [::constants :order])] (register-constant! x)) (-> cenv (assoc-in [::analyzed-cljs path] true) From 9e00804036a22084f9aa500ca7a9d6b5b990a341 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 4 Mar 2015 19:13:37 -0500 Subject: [PATCH 0780/4033] CLJS-1078: Nashorn REPL should use persistent code cache Use code cache by default. Can be disabled with :code-cache false. Add plain repl-env* constructor. --- src/clj/cljs/repl/nashorn.clj | 38 ++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 2f43632ee..98a9f0d82 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -15,7 +15,7 @@ [cljs.repl :as repl] [cljs.compiler :as comp] [cljs.closure :as closure]) - (:import [javax.script ScriptEngine ScriptEngineManager ScriptException] + (:import [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory] [jdk.nashorn.api.scripting NashornException])) ;; Nashorn Clojurescript repl binding. @@ -63,14 +63,21 @@ ;; Implementation -(defn create-engine [] - (if-let [engine (.getEngineByName (ScriptEngineManager.) "nashorn")] - (let [context (.getContext engine)] - (.setWriter context *out*) - (.setErrorWriter context *err*) - engine) - (throw (IllegalArgumentException. - "Cannot find the Nashorn script engine, use a JDK version 8 or higher.")))) +(defn create-engine + ([] (create-engine nil)) + ([{:keys [code-cache] :or {code-cache true}}] + (let [args (when code-cache ["-pcc"]) + factories (.getEngineFactories (ScriptEngineManager.)) + factory (get (zipmap (map #(.getEngineName %) factories) factories) "Oracle Nashorn")] + (if-let [engine (if-not (empty? args) + (.getScriptEngine ^ScriptEngineFactory factory (into-array args)) + (.getScriptEngine ^ScriptEngineFactory factory))] + (let [context (.getContext engine)] + (.setWriter context *out*) + (.setErrorWriter context *err*) + engine) + (throw (IllegalArgumentException. + "Cannot find the Nashorn script engine, use a JDK version 8 or higher.")))))) (defn eval-str [^ScriptEngine engine ^String s] (.eval engine s)) @@ -197,6 +204,12 @@ (when-let [frames (read-string frames-str)] (vec (map #(update-in %1 [:file] (fn [s] (strip-file-name s output-dir))) frames))))) +(defn repl-env* [{:keys [debug] :as opts}] + (let [engine (create-engine opts)] + (merge + (NashornEnv. engine debug) + opts))) + (defn repl-env "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript and as the :repl-env argument to piggieback/cljs-repl. Besides the usual repl options (e.g. :source-map), @@ -205,8 +218,5 @@ :output-dir the directory of the compiled files, e.g. \"resources/public/my-app\" (mandatory). :output-to load this file initially into Nashorn, relative to output-dir. Use a minimal bootstrapped cljs.core environment if not specified." - [& {:keys [debug] :as opts}] - (let [engine (create-engine)] - (merge - (NashornEnv. engine debug) - opts))) + [& {:as opts}] + (repl-env* opts)) From e9ebf731816069112d321226f432f188f13e2576 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 4 Mar 2015 19:20:12 -0500 Subject: [PATCH 0781/4033] CLJS-1081: Add script to create uberjar --- script/uberjar | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 script/uberjar diff --git a/script/uberjar b/script/uberjar new file mode 100755 index 000000000..493c5972e --- /dev/null +++ b/script/uberjar @@ -0,0 +1,4 @@ +#!/bin/bash + +lein uberjar clojure.main +mv target/clojurescript-0.0-SNAPSHOT-standalone.jar target/cljs.jar \ No newline at end of file From a423884823146f5ecd735e5561a64e7a48f7cd13 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 4 Mar 2015 19:23:36 -0500 Subject: [PATCH 0782/4033] add helper script to build aot cached core files --- script/aot_core | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 script/aot_core diff --git a/script/aot_core b/script/aot_core new file mode 100755 index 000000000..0de000469 --- /dev/null +++ b/script/aot_core @@ -0,0 +1,14 @@ +#!/bin/bash + +if [ "$CLOJURESCRIPT_HOME" = "" ]; then + CLOJURESCRIPT_HOME="`dirname $0`/.." +fi + +CLJSC_CP='' +for next in lib/*: src/clj: src/cljs: test/cljs; do + CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next +done + +java -server -cp $CLJSC_CP clojure.main -e \ +"(require '[cljs.closure :as cljsc]) +(cljsc/aot-cache-core)" \ No newline at end of file From e6d7f366a6055490a97743f997df5c65abc55d2f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Mar 2015 00:43:13 -0500 Subject: [PATCH 0783/4033] CLJS-897: AOT core.cljs CLJS-899: AOT cache core.cljs analysis fix build script to create cache files with proper version information fix `cljs.compiler/compile-file*` to only consider cache file when compiling cljs.core ns update .iml --- Clojurescript.iml | 15 +++-- script/build | 14 ++++ src/clj/cljs/compiler.clj | 133 +++++++++++++++++++++----------------- 3 files changed, 96 insertions(+), 66 deletions(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index bf1c6c09f..9bccacfd5 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -1,5 +1,5 @@ - + @@ -25,19 +25,22 @@ - + - + - - + + + - + + + \ No newline at end of file diff --git a/script/build b/script/build index 6e5aa85bb..0527d59a7 100755 --- a/script/build +++ b/script/build @@ -36,6 +36,20 @@ CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/cljs/cljs/core.cljs > $CLJS_FILE mv $CLJS_FILE src/cljs/cljs/core.cljs +rm -f src/cljs/cljs/core.aot.js +rm -f src/cljs/cljs/core.aot.js.map +rm -f src/cljs/cljs/core.cljs.cache.aot.edn + +./script/aot_core + +AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` +sed -e 's/0.0-0000/0.0-$REVISION/' src/cljs/cljs/core.aot.js > $AOT_FILE +mv $AOT_FILE src/cljs/cljs/core.aot.js + +AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` +sed -e 's/0.0-0000/0.0-$REVISION/' src/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE +mv $AOT_CACHE_FILE src/cljs/cljs/core.cljs.cache.aot.edn + # For Hudson server if [ "$HUDSON" = "true" ]; then mvn --fail-at-end -Psonatype-oss-release \ diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 4deeee382..6d89d5095 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -949,66 +949,79 @@ (defn compile-file* ([src dest] (compile-file* src dest nil)) ([src dest opts] - (env/ensure - (with-core-cljs opts - (fn [] - (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Compiling " src)) - (with-open [out ^java.io.Writer (io/make-writer dest {})] - (binding [*out* out - ana/*cljs-ns* 'cljs.user - ana/*cljs-file* (.getPath ^File src) - reader/*alias-map* (or reader/*alias-map* {}) - ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) - *source-map-data* (when (:source-map opts) - (atom - {:source-map (sorted-map) - :gen-col 0 - :gen-line 0}))] - (emitln (compiled-by-string opts)) - (loop [forms (ana/forms-seq src) - ns-name nil - deps nil] - (if (seq forms) - (let [env (ana/empty-env) - ast (ana/analyze env (first forms) nil opts)] - (emit ast) - (if (= (:op ast) :ns) - (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) - (recur (rest forms) ns-name deps))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:opts :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= (:optimizations opts) :none)) - (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" (.getName sm-file) - (if (true? (:source-map-timestamp opts)) - (str "?rel=" (System/currentTimeMillis)) - "")) - (spit sm-file - (sm/encode {(url-path src) (:source-map sm-data)} - {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest) - :source-map-timestamp (:source-map-timestamp opts) - :source-map-pretty-print (:source-map-pretty-print opts)})))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret) - (swap! env/*compiler* assoc-in [::ana/analyzed-cljs path] true)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src output-dir))) - ret))))))))))) + (env/ensure + (with-core-cljs opts + (fn [] + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Compiling " src)) + (if-let [cached (and (= (:optimizations opts) :none) + (= (:ns (ana/parse-ns src)) 'cljs.core) + (io/resource "cljs/core.aot.js"))] + ;; no need to bother with analysis cache reading, handled by + ;; with-core-cljs + (do + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Using cached cljs.core " src)) + (spit dest (slurp cached)) + (when (true? (:source-map opts)) + (spit (io/file (str dest ".map")) + (slurp (io/resource "cljs/core.aot.js.map")))) + (ana/parse-ns src dest nil)) + (with-open [out ^java.io.Writer (io/make-writer dest {})] + (binding [*out* out + ana/*cljs-ns* 'cljs.user + ana/*cljs-file* (.getPath ^File src) + reader/*alias-map* (or reader/*alias-map* {}) + ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) + *source-map-data* (when (:source-map opts) + (atom + {:source-map (sorted-map) + :gen-col 0 + :gen-line 0}))] + (emitln (compiled-by-string opts)) + (loop [forms (ana/forms-seq src) + ns-name nil + deps nil] + (if (seq forms) + (let [env (ana/empty-env) + ast (ana/analyze env (first forms) nil opts)] + (emit ast) + (if (= (:op ast) :ns) + (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) + (recur (rest forms) ns-name deps))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:opts :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= (:optimizations opts) :none)) + (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] + (emits "\n//# sourceMappingURL=" (.getName sm-file) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + "")) + (spit sm-file + (sm/encode {(url-path src) (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 2) + :file (url-path dest) + :source-map-timestamp (:source-map-timestamp opts) + :source-map-pretty-print (:source-map-pretty-print opts)})))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret) + (swap! env/*compiler* assoc-in [::ana/analyzed-cljs path] true)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src output-dir))) + ret)))))))))))) (defn requires-compilation? "Return true if the src file requires compilation." From e7999e1400153fec83022c2bbe6014c224f9e837 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Mar 2015 01:00:26 -0500 Subject: [PATCH 0784/4033] tweak uberjar script so it includes AOTed core and sets the version number correctly --- script/uberjar | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/script/uberjar b/script/uberjar index 493c5972e..84fd40be7 100755 --- a/script/uberjar +++ b/script/uberjar @@ -1,4 +1,37 @@ -#!/bin/bash +#!/usr/bin/env bash + +# This script must be run within the ClojureScript top-level project +# directory. + +set -ex + +# The command `git describe --match v0.0` will return a string like +# +# v0.0-856-g329708b +# +# where 856 is the number of commits since the v0.0 tag. It will always +# find the v0.0 tag and will always return the total number of commits (even +# if the tag is v0.0.1). +REVISION=`git --no-replace-objects describe --match v0.0` + +# Extract the version number from the string. Do this in two steps so +# it is a little easier to understand. +REVISION=${REVISION:5} # drop the first 5 characters +REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters + +rm -f src/cljs/cljs/core.aot.js +rm -f src/cljs/cljs/core.aot.js.map +rm -f src/cljs/cljs/core.cljs.cache.aot.edn + +./script/aot_core + +AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` +sed -e "s/0.0-0000/0.0-$REVISION/" src/cljs/cljs/core.aot.js > $AOT_FILE +mv $AOT_FILE src/cljs/cljs/core.aot.js + +AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` +sed -e "s/0.0-0000/0.0-$REVISION/" src/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE +mv $AOT_CACHE_FILE src/cljs/cljs/core.cljs.cache.aot.edn lein uberjar clojure.main mv target/clojurescript-0.0-SNAPSHOT-standalone.jar target/cljs.jar \ No newline at end of file From b0d5acca2894349305cb80a3abee325d43471c5b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Mar 2015 01:19:02 -0500 Subject: [PATCH 0785/4033] whitespace --- src/clj/cljs/closure.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 1a700fe00..c69ebcbb2 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1491,7 +1491,7 @@ should contain the source for the given namespace name." FileVisitResult/CONTINUE) (visitFileFailed [_ file exc] FileVisitResult/CONTINUE))))] - (println "Building...") + (println "Building ...") (flush) (buildf) (println "Watching path:" source) @@ -1505,7 +1505,7 @@ should contain the source for the given namespace name." (. fstr (endsWith "js"))) (not (. fstr (startsWith ".#")))))) (seq (.pollEvents key))) - (println "Change detected, recompiling...") + (println "Change detected, recompiling ...") (flush) (buildf)) (recur key)))))))) From edb536113b580b4bd57b8f08dbb14bf8d4365099 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Mar 2015 01:50:10 -0500 Subject: [PATCH 0786/4033] uberjar script also needs to update *clojurescript-version* --- script/uberjar | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/script/uberjar b/script/uberjar index 84fd40be7..8573621e4 100755 --- a/script/uberjar +++ b/script/uberjar @@ -19,6 +19,14 @@ REVISION=`git --no-replace-objects describe --match v0.0` REVISION=${REVISION:5} # drop the first 5 characters REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters +COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` +sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/util.clj > $COMP_FILE +mv $COMP_FILE src/clj/cljs/util.clj + +CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` +sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/cljs/cljs/core.cljs > $CLJS_FILE +mv $CLJS_FILE src/cljs/cljs/core.cljs + rm -f src/cljs/cljs/core.aot.js rm -f src/cljs/cljs/core.aot.js.map rm -f src/cljs/cljs/core.cljs.cache.aot.edn From 3fd1b8bfb1ad7c552d1a4cd8bb3b852b55c37991 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Mar 2015 09:19:09 -0500 Subject: [PATCH 0787/4033] more discipline around how we detect the standard library change `cljs.analyzer/cache-file` so that it is read and write aware. --- src/clj/cljs/analyzer.clj | 24 +++++++++++++----------- src/clj/cljs/compiler.clj | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 3676c4ca7..e52a90e7b 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1899,17 +1899,18 @@ argument, which the reader will use in any emitted errors." ret)))) (defn cache-file - "Given a ClojureScript source file returns the _output_ path to the analysis - cache file." + "Given a ClojureScript source file returns the read/write path to the analysis + cache file. Defaults to the read path which is usually also the write path." ([src] (cache-file src "out")) - ([src output-dir] + ([src output-dir] (cache-file src (parse-ns src) output-dir)) + ([src ns-info output-dir] (cache-file src (parse-ns src) output-dir :read)) + ([src ns-info output-dir mode] (if-let [core-cache - (and (util/url? src) - (.endsWith (.getPath ^URL src) (str "cljs" File/separator "core.cljs")) + (and (= mode :read) + (= (:ns ns-info) 'cljs.core) (io/resource "cljs/core.cljs.cache.aot.edn"))] core-cache - (let [ns-info (parse-ns src)] - (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn")))))) + (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn"))))) (defn last-modified [src] (cond @@ -1969,14 +1970,15 @@ argument, which the reader will use in any emitted errors." :else (io/resource f))] (assert res (str "Can't find " f " in classpath")) (env/ensure - (let [path (if (instance? File res) + (let [ns-info (parse-ns res) + path (if (instance? File res) (.getPath ^File res) (.getPath ^URL res)) - cache (when (or (= f "cljs/core.cljs") + cache (when (or (= (:ns ns-info) 'cljs.core) (and (:cache-analysis opts) output-dir)) - (cache-file res output-dir))] + (cache-file res ns-info output-dir))] (when-not (get-in @env/*compiler* [::analyzed-cljs path]) - (if (or (not= f "cljs/core.cljs") + (if (or (not= (:ns ns-info) 'cljs.core) (not cache) (requires-analysis? res output-dir)) (binding [*cljs-ns* 'cljs.user diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 6d89d5095..4b4f25e73 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1087,7 +1087,7 @@ :visited (conj visited ns)}))) (when (true? (:cache-analysis opts)) (ana/write-analysis-cache ns - (ana/cache-file src (util/output-directory opts)))) + (ana/cache-file src ns-info (util/output-directory opts) :write))) ret)) (do (when-not (contains? (::ana/namespaces @env/*compiler*) ns) From b721daf882e1e3bac7bfa45262f4e6f0a16a7017 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 5 Mar 2015 16:02:17 -0500 Subject: [PATCH 0788/4033] improve `cljs.closure/source-for-namespace` failure checking --- src/clj/cljs/closure.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c69ebcbb2..9263a8992 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -483,10 +483,10 @@ should contain the source for the given namespace name." (if-let [cljs-res (io/resource relpath)] {:relative-path relpath :uri cljs-res} (let [relpath (:file (get-in @compiler-env [:js-dependency-index ns-str]))] - (if-let [js-res (io/resource relpath)] + (if-let [js-res (and relpath (io/resource relpath))] {:relative-path relpath :uri js-res} - (throw - (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))) + (throw + (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))) (defn cljs-dependencies "Given a list of all required namespaces, return a list of From 3e6c0727c48690e09f13323025d8544720535f34 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 5 Mar 2015 19:07:20 -0500 Subject: [PATCH 0789/4033] fix browser REPL based on debugging session with Kovas Boguta patching `goog.require` is the wrong approach for browsers. It will fail when loading a dependency graph the browser has not already seen. Instead patch `goog.provide` so it doesn't throw and suppress errors from `goog.writeScriptTag_` when writing to the document after the page has loaded. --- src/cljs/clojure/browser/repl.cljs | 36 +++++++++++++----------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 355faa5e0..90b6e8edd 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -113,24 +113,20 @@ (fn [iframe] (set! (.-display (.-style iframe)) "none"))) - ;; Monkey-patch goog.require if running under optimizations :none - David + ;; Monkey-patch goog.provide if running under optimizations :none - David (when-not js/COMPILED - (set! *loaded-libs* - (let [gntp (.. js/goog -dependencies_ -nameToPath)] - (into #{} - (filter - (fn [name] - (aget (.. js/goog -dependencies_ -visited) (aget gntp name))) - (js-keys gntp))))) - (set! (.-isProvided_ js/goog) (fn [_] false)) - (set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (.appendChild js/document.body - (let [script (.createElement js/document "script")] - (set! (.-type script) "text/javascript") - (set! (.-src script) - (str "goog/" - (aget (.. js/goog -dependencies_ -nameToPath) name))) - script)))))))) + (set! (.-provide__ js/goog) js/goog.provide) + (set! (.-isProvided___ js/goog) js/goog.isProvided_) + (set! (.-provide js/goog) + (fn [name] + (set! (.-isProvided_ js/goog) (fn [name] false)) + (.provide__ js/goog name) + (set! (.-isProvided_ js/goog) js/goog.isProvided___))) + (set! (.-writeScriptTag_ js/goog) + (fn [src opt_sourceText] + (let [doc js/goog.global.document] + (if (nil? opt_sourceText) + (.write doc + (str "")) + (.write doc + (str ""))))))))) From 2f07bc5052bc6a8137c2222116c67b73fae7d739 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 5 Mar 2015 19:31:29 -0500 Subject: [PATCH 0790/4033] new ignores --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index e249be357..1f2d4a494 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,7 @@ closure-release-* builds .cljs* node_modules +nashorn_code_cache +src/cljs/cljs/core.aot.js +src/cljs/cljs/core.aot.js.map +src/cljs/cljs/core.cljs.cache.aot.edn From b7e3935996b4d7d6d255640482b0f4d5fc7fd36f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 5 Mar 2015 19:33:06 -0500 Subject: [PATCH 0791/4033] make `:undeclared-ns` warning more helpful, print the path that could not be found `cljs.closure/source-for-namespace` incorrectly munged nses so that JS keywords like `function` etc. would get rewritten. --- src/clj/cljs/analyzer.clj | 10 ++++++---- src/clj/cljs/closure.clj | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index e52a90e7b..b9e5b164e 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -89,8 +89,10 @@ (str "Use of undeclared Var " (:prefix info) "/" (:suffix info))) (defmethod error-message :undeclared-ns - [warning-type info] - (str "No such namespace: " (:ns-sym info))) + [warning-type {:keys [ns-sym path] :as info}] + (str "No such namespace: " ns-sym + (when path + (str ", could not locate " path)))) (defmethod error-message :dynamic [warning-type info] @@ -369,7 +371,7 @@ ;; macros may refer to namespaces never explicitly required ;; confirm that the library at least exists (nil? (io/resource (util/ns->relpath ns-sym)))) - (warning :undeclared-ns env {:ns-sym ns-sym}))) + (warning :undeclared-ns env {:ns-sym ns-sym :path (util/ns->relpath ns-sym)}))) (defn core-name? "Is sym visible from core in the current compilation namespace?" @@ -1132,7 +1134,7 @@ (analyze-file src opts) (throw (error env - (error-message :undeclared-ns {:ns-sym dep})))))))))) + (error-message :undeclared-ns {:ns-sym dep :path relpath})))))))))) (defn check-uses [uses env] (doseq [[sym lib] uses] diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 9263a8992..1ec7aafca 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -477,7 +477,7 @@ should contain the source for the given namespace name." (defn source-for-namespace [ns compiler-env] - (let [ns-str (str (comp/munge ns)) + (let [ns-str (str (comp/munge ns {})) path (string/replace ns-str \. \/) relpath (str path ".cljs")] (if-let [cljs-res (io/resource relpath)] From 746bbb1fdbee4d96cf7c88a083f34532c4edfa45 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 5 Mar 2015 19:42:37 -0500 Subject: [PATCH 0792/4033] CLJS-1082: analysis memoization bug drop :cljs.analyzer/analyzed-cljs, redundant, use :cljs.analyzer/namespaces as source of truth of analyzed namespaces --- src/clj/cljs/analyzer.clj | 6 ++---- src/clj/cljs/compiler.clj | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index b9e5b164e..20436b6e8 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1979,7 +1979,7 @@ argument, which the reader will use in any emitted errors." cache (when (or (= (:ns ns-info) 'cljs.core) (and (:cache-analysis opts) output-dir)) (cache-file res ns-info output-dir))] - (when-not (get-in @env/*compiler* [::analyzed-cljs path]) + (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info)]) (if (or (not= (:ns ns-info) 'cljs.core) (not cache) (requires-analysis? res output-dir)) @@ -1999,8 +1999,7 @@ argument, which the reader will use in any emitted errors." (recur ns (next forms)))) ns))] (when (and cache (true? (:cache-analysis opts))) - (write-analysis-cache ns cache)) - (swap! env/*compiler* assoc-in [::analyzed-cljs path] true))) + (write-analysis-cache ns cache)))) ;; we want want to keep dependency analysis information ;; don't revert the environment - David (let [{:keys [ns]} @@ -2017,5 +2016,4 @@ argument, which the reader will use in any emitted errors." (doseq [x (get-in cached-ns [::constants :order])] (register-constant! x)) (-> cenv - (assoc-in [::analyzed-cljs path] true) (assoc-in [::namespaces ns] cached-ns)))))))))))))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 4b4f25e73..c3bb0f6fe 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1015,8 +1015,7 @@ :source-map-timestamp (:source-map-timestamp opts) :source-map-pretty-print (:source-map-pretty-print opts)})))) (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret) - (swap! env/*compiler* assoc-in [::ana/analyzed-cljs path] true)) + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) (let [{:keys [output-dir cache-analysis]} opts] (when (and (true? cache-analysis) output-dir) (ana/write-analysis-cache ns-name From a649fc57c4b6e605ade72af13bee4e5ca20b7754 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 5 Mar 2015 19:53:28 -0500 Subject: [PATCH 0793/4033] Remove duped analysis cache writing logic. Never need to write out cljs.core analysis cache. Never drop cljs.core analysis from compilation environment --- src/clj/cljs/compiler.clj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index c3bb0f6fe..4c3f3a168 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1076,7 +1076,8 @@ (if (requires-compilation? src-file dest-file opts) (do (util/mkdirs dest-file) - (when (contains? (::ana/namespaces @env/*compiler*) ns) + (when (and (contains? (::ana/namespaces @env/*compiler*) ns) + (not= ns 'cljs.core)) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) (let [ret (compile-file* src-file dest-file opts)] (when *dependents* @@ -1084,9 +1085,6 @@ (fn [{:keys [recompile visited]}] {:recompile (into recompile (ana/ns-dependents ns)) :visited (conj visited ns)}))) - (when (true? (:cache-analysis opts)) - (ana/write-analysis-cache ns - (ana/cache-file src ns-info (util/output-directory opts) :write))) ret)) (do (when-not (contains? (::ana/namespaces @env/*compiler*) ns) From 195b2543609e07b1592490187002615b7654ee3f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 5 Mar 2015 20:20:33 -0500 Subject: [PATCH 0794/4033] clean up all REPL entry points --- src/clj/cljs/repl/browser.clj | 62 ++++++++++++++++++++--------------- src/clj/cljs/repl/nashorn.clj | 9 ++--- src/clj/cljs/repl/node.clj | 2 ++ src/clj/cljs/repl/rhino.clj | 12 ++++--- 4 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 1bea838b6..8e00908d2 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -253,29 +253,12 @@ (cljsc/js-dependencies opts cljs))] (disj (set (concat cljs goog)) nil))) -(defn repl-env - "Create a browser-connected REPL environment. - - Options: +;; NOTE: REPL evaluation environment designers do not replicate the behavior +;; of the browser REPL. The design is outdated, refer to the Node.js, Rhino or +;; Nashorn REPLs. - port: The port on which the REPL server will run. Defaults to 9000. - working-dir: The directory where the compiled REPL client JavaScript will - be stored. Defaults to \".repl\" with a ClojureScript version - suffix, eg. \".repl-0.0-2138\". - serve-static: Should the REPL server attempt to serve static content? - Defaults to true. - static-dir: List of directories to search for static content. Defaults to - [\".\" \"out/\"]. - preloaded-libs: List of namespaces that should not be sent from the REPL server - to the browser. This may be required if the browser is already - loading code and reloading it would cause a problem. - optimizations: The level of optimization to use when compiling the client - end of the REPL. Defaults to :simple. - src: The source directory containing user-defined cljs files. Used to - support reflection. Defaults to \"src/\". - " - [& {:as opts}] - (let [ups-deps (cljsc/get-upstream-deps (java.lang.ClassLoader/getSystemClassLoader)) +(defn repl-env* [opts] + (let [ups-deps (cljsc/get-upstream-deps) opts (assoc opts :ups-libs (:libs ups-deps) :ups-foreign-libs (:foreign-libs ups-deps)) @@ -284,8 +267,8 @@ {:port 9000 :optimizations :simple :working-dir (or (:output-dir opts) - (->> [".repl" (util/clojurescript-version)] - (remove empty?) (string/join "-"))) + (->> [".repl" (util/clojurescript-version)] + (remove empty?) (string/join "-"))) :serve-static true :static-dir (cond-> ["." "out/"] (:output-dir opts) (conj (:output-dir opts))) @@ -304,12 +287,37 @@ (swap! browser-state (fn [old] (assoc old :client-js - (create-client-js-file - opts - (io/file (:working-dir opts) "client.js"))))) + (create-client-js-file + opts + (io/file (:working-dir opts) "client.js"))))) (println "Waiting for browser to connect ...") opts))) +(defn repl-env + "Create a browser-connected REPL environment. + + Options: + + port: The port on which the REPL server will run. Defaults to 9000. + working-dir: The directory where the compiled REPL client JavaScript will + be stored. Defaults to \".repl\" with a ClojureScript version + suffix, eg. \".repl-0.0-2138\". + serve-static: Should the REPL server attempt to serve static content? + Defaults to true. + static-dir: List of directories to search for static content. Defaults to + [\".\" \"out/\"]. + preloaded-libs: List of namespaces that should not be sent from the REPL server + to the browser. This may be required if the browser is already + loading code and reloading it would cause a problem. + optimizations: The level of optimization to use when compiling the client + end of the REPL. Defaults to :simple. + src: The source directory containing user-defined cljs files. Used to + support reflection. Defaults to \"src/\". + " + [& {:as opts}] + (assert (even? (count opts)) "Arguments must be interleaved key value pairs") + (repl-env* opts)) + (comment (require '[cljs.repl :as repl]) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 98a9f0d82..5f1c173ee 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -211,12 +211,7 @@ opts))) (defn repl-env - "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript and as the - :repl-env argument to piggieback/cljs-repl. Besides the usual repl options (e.g. :source-map), - opts has the following extra parameters: - - :output-dir the directory of the compiled files, e.g. \"resources/public/my-app\" (mandatory). - :output-to load this file initially into Nashorn, relative to output-dir. - Use a minimal bootstrapped cljs.core environment if not specified." + "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." [& {:as opts}] + (assert (even? (count opts)) "Arguments must be interleaved key value pairs") (repl-env* opts)) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index eeff59543..e914a7461 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -195,6 +195,8 @@ (NodeEnv. host port (atom nil) (atom nil)))) (defn repl-env + "Construct a Node.js evalution environment. Can supply :host and :port." [& {:as options}] + (assert (even? (count options)) "Arguments must be interleaved key value pairs") (repl-env* options)) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 4afe92b46..70494b26c 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -198,10 +198,8 @@ (load-javascript this ns url)) (-tear-down [_] (Context/exit))) -(defn repl-env - "Returns a fresh JS environment, suitable for passing to repl. - Hang on to return for use across repl calls." - [] +(defn repl-env* + [opts] (let [cx (Context/enter)] ;; just avoid the 64K method limit ;; Rhino is slow even with optimizations enabled @@ -210,6 +208,12 @@ {:cx cx :scope (.initStandardObjects cx)}))) +(defn repl-env + "Returns a fresh JS environment, suitable for passing to repl. + Hang on to return for use across repl calls." + [& {:as opts}] + (repl-env* opts)) + (comment (repl/-parse-stacktrace (repl-env) From b0ec424e4c13250a151fa1413a3d303d56691d71 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Mar 2015 22:10:17 -0500 Subject: [PATCH 0795/4033] track function scopes, prep work for CLJS-937 --- src/clj/cljs/analyzer.clj | 4 +++- test/clj/cljs/analyzer_tests.clj | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 20436b6e8..8669c9ad5 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -849,6 +849,7 @@ (get-in env [:js-globals name]))}} (when-let [tag (-> name meta :tag)] {:ret-tag tag}))) + env (update-in env [:fn-scope] (fnil conj []) name-var) locals (if (and locals name) (assoc locals name name-var) locals) type (-> form meta ::type) protocol-impl (-> form meta ::protocol-impl) @@ -884,7 +885,8 @@ (warning :variadic-max-arity env {:name name-var})) (when (not= (distinct param-counts) param-counts) (warning :overload-arity env {:name name-var}))) - {:env env :op :fn :form form :name name-var :methods methods :variadic variadic + {:env env + :op :fn :form form :name name-var :methods methods :variadic variadic :tag 'function :recur-frames *recur-frames* :loop-lets *loop-lets* :jsdoc [(when variadic "@param {...*} var_args")] diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index 3eef6cee6..cb40b0a6c 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -324,3 +324,20 @@ ;; actual: (not (= #{file-reloading dev client} #{file-reloading dev client core})) (is (= (set (a/ns-dependents 'utils)) #{'file-reloading 'dev 'client 'core})))) + +;; ============================================================================= +;; Namespace metadata + +(deftest test-fn-scope + (is (= (get-in + (a/analyze ns-env + '(fn foo [] + (fn bar []))) + [:children 0 :children 0 :env :fn-scope]) + '[{:name foo, :info {:shadow nil}} {:name bar, :info {:shadow nil}}])) + (is (= (get-in + (a/analyze ns-env + '(defn foo [] + (fn bar []))) + [:init :children 0 :children 0 :env :fn-scope]) + '[{:name foo, :info {:shadow nil}} {:name bar, :info {:shadow nil}}]))) From ddaa0f12c2572652c2fd628b0bd05938829372a9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Mar 2015 06:54:10 -0500 Subject: [PATCH 0796/4033] CLJS-937: local fn name should be lexically munged Introduce :fn-scope vector to analysis environment. If a function is supplied with a local name extend :fn-scope in env by conj'ing the local name var. Introduce `cljs.compiler/fn-self-name` munging case. Use :fn-scope to construct the name. The returned symbol starts with munged namespace, separated from scope name by "_SLASH_". fn scope names are separated by "_$_". Simplifies fn name demunging when source mapping stack traces. Invoke optimizations rewrite the fn :name to a direct string. Disable :fn-self-name munging in these cases. Respect :static-fns false even if compiling core. Remove bogus analyzer tests for local name. Add better compiler tests for local name. --- src/clj/cljs/analyzer.clj | 14 ++++-- src/clj/cljs/compiler.clj | 78 ++++++++++++++++++++++---------- test/clj/cljs/analyzer_tests.clj | 19 +------- test/clj/cljs/compiler_tests.clj | 37 ++++++++++++++- 4 files changed, 100 insertions(+), 48 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 8669c9ad5..204c6dc14 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -290,6 +290,7 @@ {:ns (get-namespace *cljs-ns*) :context :statement :locals {} + :fn-scope [] :js-globals (into {} (map #(vector % {:name %}) '(alert window document console escape unescape @@ -844,12 +845,17 @@ locals (:locals env) name-var (if name (merge - {:name name - :info {:shadow (or (locals name) - (get-in env [:js-globals name]))}} + {:name name + :info {:fn-self-name true + :fn-scope (:fn-scope env) + :ns (-> env :ns :name) + :shadow (or (locals name) + (get-in env [:js-globals name]))}} (when-let [tag (-> name meta :tag)] {:ret-tag tag}))) - env (update-in env [:fn-scope] (fnil conj []) name-var) + env (if name + (update-in env [:fn-scope] conj name-var) + env) locals (if (and locals name) (assoc locals name name-var) locals) type (-> form meta ::type) protocol-impl (-> form meta ::protocol-impl) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 4c3f3a168..6338476bc 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -55,29 +55,45 @@ (some #{(str name)} (ns-first-segments)) (inc d) :else d)))) +(declare munge) + +(defn fn-self-name [{:keys [name info] :as name-var}] + (let [{:keys [ns fn-scope]} info + scoped-name (apply str + (interpose "_$_" + (concat (map (comp str :name) fn-scope) [name])))] + (symbol + (munge + (str (string/replace (str ns) "." "$") + "_SLASH_" scoped-name))))) + (defn munge ([s] (munge s js-reserved)) ([s reserved] - (if (map? s) - ; Unshadowing - (let [{:keys [name field] :as info} s - depth (shadow-depth s) - renamed (*lexical-renames* (System/identityHashCode s)) - munged-name (munge (cond field (str "self__." name) - renamed renamed - :else name) - reserved)] - (if (or field (zero? depth)) - munged-name - (symbol (str munged-name "__$" depth)))) - ; String munging - (let [ss (string/replace (str s) #"\/(.)" ".$1") ; Division is special - ss (apply str (map #(if (reserved %) (str % "$") %) - (string/split ss #"(?<=\.)|(?=\.)"))) - ms (clojure.lang.Compiler/munge ss)] - (if (symbol? s) - (symbol ms) - ms))))) + (if (map? s) + (let [{:keys [name field info] :as name-var} s] + (if (:fn-self-name info) + (fn-self-name s) + ;; Unshadowing + (let [depth (shadow-depth s) + renamed (*lexical-renames* (System/identityHashCode s)) + munged-name (munge + (cond field (str "self__." name) + renamed renamed + :else name) + reserved)] + (if (or field (zero? depth)) + munged-name + (symbol (str munged-name "__$" depth)))))) + ;; String munging + (let [ss (string/replace (str s) #"\/(.)" ".$1") ; Division is special + ss (apply str + (map #(if (reserved %) (str % "$") %) + (string/split ss #"(?<=\.)|(?=\.)"))) + ms (clojure.lang.Compiler/munge ss)] + (if (symbol? s) + (symbol ms) + ms))))) (defn- comma-sep [xs] (interpose "," xs)) @@ -772,16 +788,24 @@ ;; direct dispatch to variadic case (and variadic? (> arity mfa)) - [(update-in f [:info :name] - (fn [name] (symbol (str (munge info) ".cljs$core$IFn$_invoke$arity$variadic")))) + [(update-in f [:info] + (fn [info] + (-> info + (assoc :name (symbol (str (munge info) ".cljs$core$IFn$_invoke$arity$variadic"))) + ;; bypass local fn-self-name munging, we're emitting direct + (update-in [:info] dissoc :fn-self-name)))) {:max-fixed-arity mfa}] ;; direct dispatch to specific arity case :else (let [arities (map count mps)] (if (some #{arity} arities) - [(update-in f [:info :name] - (fn [name] (symbol (str (munge info) ".cljs$core$IFn$_invoke$arity$" arity)))) nil] + [(update-in f [:info] + (fn [info] + (-> info + (assoc :name (symbol (str (munge info) ".cljs$core$IFn$_invoke$arity$" arity))) + ;; bypass local fn-self-name munging, we're emitting direct + (update-in [:info] dissoc :fn-self-name)))) nil] [f nil])))) [f nil])] (emit-wrap env @@ -808,6 +832,7 @@ :else (if (and ana/*cljs-static-fns* (= (:op f) :var)) + ;; higher order case, static information missing (let [fprop (str ".cljs$core$IFn$_invoke$arity$" (count args))] (emits "(" f fprop " ? " f fprop "(" (comma-sep args) ") : " f ".call(" (comma-sep (cons "null" args)) "))")) (emits f ".call(" (comma-sep (cons "null" args)) ")")))))) @@ -1072,7 +1097,10 @@ (if (.exists src-file) (try (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) - opts (if (= ns 'cljs.core) (assoc opts :static-fns true) opts)] + opts (if (and (= ns 'cljs.core) + (not (false? (:static-fns opts)))) + (assoc opts :static-fns true) + opts)] (if (requires-compilation? src-file dest-file opts) (do (util/mkdirs dest-file) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index cb40b0a6c..a760b9d26 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -33,7 +33,7 @@ ;; ============================================================================= ;; NS parsing -(def ns-env (assoc-in (a/empty-env) [:ns [:name]] 'cljs.user)) +(def ns-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)) (deftest spec-validation (is (.startsWith @@ -324,20 +324,3 @@ ;; actual: (not (= #{file-reloading dev client} #{file-reloading dev client core})) (is (= (set (a/ns-dependents 'utils)) #{'file-reloading 'dev 'client 'core})))) - -;; ============================================================================= -;; Namespace metadata - -(deftest test-fn-scope - (is (= (get-in - (a/analyze ns-env - '(fn foo [] - (fn bar []))) - [:children 0 :children 0 :env :fn-scope]) - '[{:name foo, :info {:shadow nil}} {:name bar, :info {:shadow nil}}])) - (is (= (get-in - (a/analyze ns-env - '(defn foo [] - (fn bar []))) - [:init :children 0 :children 0 :env :fn-scope]) - '[{:name foo, :info {:shadow nil}} {:name bar, :info {:shadow nil}}]))) diff --git a/test/clj/cljs/compiler_tests.clj b/test/clj/cljs/compiler_tests.clj index 33664d96a..da3e580a1 100644 --- a/test/clj/cljs/compiler_tests.clj +++ b/test/clj/cljs/compiler_tests.clj @@ -1,10 +1,14 @@ (ns cljs.compiler-tests (:use clojure.test) + (:require [cljs.analyzer :as a]) (:require [cljs.compiler :as c]) (:require [cljs.env :as e]) - (:require [cljs.util :as util]) + (:require [cljs.util :as util] + [cljs.compiler :as comp]) (:import (java.io File))) +(def ns-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)) + (deftest should-recompile (let [src (File. "test/hello.cljs") dst (File/createTempFile "compilertest" ".cljs") @@ -20,3 +24,34 @@ (c/compile-file src dst optmod) (is (not (c/requires-compilation? src dst optmod))))))) +(deftest fn-scope-munge + (is (= (c/munge + (get-in + (a/analyze ns-env + '(defn foo [] + (fn bar []))) + [:init :name])) + 'cljs$user_SLASH_foo)) + (is (= (c/munge + (get-in + (a/analyze ns-env + '(defn foo [] + (fn bar []))) + [:init :children 0 :children 0 :name])) + 'cljs$user_SLASH_foo_$_bar)) + (is (= (c/munge + (get-in + (a/analyze ns-env + '(fn [] + (fn console []))) + [:children 0 :children 0 :name])) + 'cljs$user_SLASH_console))) + +(comment + (c/munge + (get-in + (a/analyze ns-env + '(defn foo [] + (fn bar []))) + [:init :children 0 :children 0 :name])) + ) \ No newline at end of file From 280b7b9ca2dcc9a822ec1dc699d0c9376eaf3406 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Mar 2015 08:29:19 -0500 Subject: [PATCH 0797/4033] CLJS-964: Redefining exists? does not emit a warning like redefining array? does. cljs.analyzer/core-name? needs to return true for core macros --- src/clj/cljs/analyzer.clj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 204c6dc14..72722a32b 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -20,6 +20,7 @@ [clojure.edn :as edn]) (:import [java.io File Reader PushbackReader] [java.net URL] + [clojure.lang Namespace] [cljs.tagged_literals JSValue])) (set! *warn-on-reflection* true) @@ -374,10 +375,15 @@ (nil? (io/resource (util/ns->relpath ns-sym)))) (warning :undeclared-ns env {:ns-sym ns-sym :path (util/ns->relpath ns-sym)}))) +(declare get-expander) + (defn core-name? "Is sym visible from core in the current compilation namespace?" [env sym] - (and (get-in @env/*compiler* [::namespaces 'cljs.core :defs sym]) + (and (or (get-in @env/*compiler* [::namespaces 'cljs.core :defs sym]) + (when-let [mac (get-expander sym env)] + (let [^Namespace ns (-> mac meta :ns)] + (= (.getName ns) 'cljs.core)))) (not (contains? (-> env :ns :excludes) sym)))) (defn resolve-var From f1326056956d288973964b47ab772ce782ccd21c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Mar 2015 09:52:38 -0500 Subject: [PATCH 0798/4033] CLJS-1086: Keyword constants should have stable names --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 72722a32b..5f4162640 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -205,7 +205,7 @@ :else (throw (Exception. (str "constant type " (type value) " not supported"))))] - (symbol (str prefix (swap! constant-counter inc))))) + (symbol (str prefix (string/replace (munge value) "." "$"))))) (defn- register-constant! ([val] (register-constant! nil val)) From 89e71705a649382e92fb8d5ccc6f7cef7635cdd9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Mar 2015 10:04:16 -0500 Subject: [PATCH 0799/4033] prep work for CLJS-1049 --- src/clj/cljs/analyzer.clj | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 5f4162640..934b43619 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -262,12 +262,19 @@ (defn get-col [x env] (or (-> x meta :column) (:column env))) +(defn intern-macros + ([ns] (intern-macros ns false)) + ([ns reload] + (when (true? reload) + (swap! env/*compiler* update-in [::namespaces ns] dissoc :macros)))) + (defn load-core [] (when (not @-cljs-macros-loaded) (reset! -cljs-macros-loaded true) (if *cljs-macros-is-classpath* (load *cljs-macros-path*) - (load-file *cljs-macros-path*)))) + (load-file *cljs-macros-path*)) + (intern-macros 'cljs.core))) (defmacro with-core-macros [path & body] @@ -1381,13 +1388,17 @@ (when *load-macros* (load-core) (doseq [nsym (vals use-macros)] - (if-let [k (:use-macros @reload)] - (clojure.core/require nsym k) - (clojure.core/require nsym))) + (let [k (:use-macros @reload)] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) (doseq [nsym (vals require-macros)] - (if-let [k (:require-macros @reload)] - (clojure.core/require nsym k) - (clojure.core/require nsym))) + (let [k (:require-macros @reload)] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) (when (seq use-macros) (check-use-macros use-macros env))) (let [ns-info From db2c4d04cb924da6f939c26ba36dcdad08ef7080 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Mar 2015 10:25:34 -0500 Subject: [PATCH 0800/4033] intern macros logic --- src/clj/cljs/analyzer.clj | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 934b43619..4da5a0752 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -20,7 +20,7 @@ [clojure.edn :as edn]) (:import [java.io File Reader PushbackReader] [java.net URL] - [clojure.lang Namespace] + [clojure.lang Namespace Var] [cljs.tagged_literals JSValue])) (set! *warn-on-reflection* true) @@ -265,8 +265,18 @@ (defn intern-macros ([ns] (intern-macros ns false)) ([ns reload] - (when (true? reload) - (swap! env/*compiler* update-in [::namespaces ns] dissoc :macros)))) + (when (or (nil? (get-in @env/*compiler* [::namespaces ns :macros])) + reload) + (swap! env/*compiler* assoc-in [::namespaces ns :macros] + (->> (ns-interns ns) + (filter (fn [[_ ^Var v]] (.isMacro v))) + (map (fn [[k v]] + [k (as-> (meta v) vm + (let [ns (.getName ^Namespace (:ns vm))] + (assoc vm + :ns ns + :name (symbol (str ns) (str k)))))])) + (into {})))))) (defn load-core [] (when (not @-cljs-macros-loaded) From b92c9aa387b99e4ef8f7e38b1faf1e1c5d8d641b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Mar 2015 10:52:08 -0500 Subject: [PATCH 0801/4033] macro resolution wip --- src/clj/cljs/analyzer.clj | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 4da5a0752..e44306998 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -384,6 +384,10 @@ (let [sym (symbol name)] (get (:requires (:ns env)) sym sym))) +(defn resolve-macro-ns-alias [env name] + (let [sym (symbol name)] + (get (:require-macros (:ns env)) sym sym))) + (defn confirm-ns [env ns-sym] (when (and (nil? (get '#{cljs.core goog Math goog.string} ns-sym)) (nil? (get (-> env :ns :requires) ns-sym)) @@ -479,6 +483,27 @@ (when (and ev (not (-> ev :dynamic))) (warning :dynamic env {:ev ev :name (:name ev)}))))) +(defn resolve-macro-var [env sym] + (let [ns (-> env :ns :name) + namespaces (get @env/*compiler* ::namespaces)] + (cond + (namespace sym) + (let [ns (namespace sym) + ns (if (= "clojure.core" ns) "cljs.core" ns) + full-ns (resolve-macro-ns-alias env ns)] + (get-in namespaces [full-ns :macros (symbol (name sym))])) + + (get-in namespaces [ns :use-macros sym]) + (let [full-ns (get-in namespaces [ns :uses-macros sym])] + (get-in namespaces [full-ns :macros sym])) + + :else + (let [ns (cond + (get-in namespaces [ns :macros sym]) ns + (core-name? env sym) 'cljs.core)] + (when ns + (get-in namespaces [ns :macros sym])))))) + (defn ns-dependents ([ns] (util/topo-sort ns From c07b5759586ccd63383ef1122929edf64bdd7fd2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Mar 2015 10:57:00 -0500 Subject: [PATCH 0802/4033] typo --- src/clj/cljs/analyzer.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index e44306998..6b3c37511 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -494,7 +494,7 @@ (get-in namespaces [full-ns :macros (symbol (name sym))])) (get-in namespaces [ns :use-macros sym]) - (let [full-ns (get-in namespaces [ns :uses-macros sym])] + (let [full-ns (get-in namespaces [ns :use-macros sym])] (get-in namespaces [full-ns :macros sym])) :else From 7025bd212fb925cb90db680aa7a5eb3f4c0de4bb Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 12:28:34 -0500 Subject: [PATCH 0803/4033] can get macro docstrings now --- src/clj/cljs/analyzer.clj | 6 +++--- src/clj/cljs/analyzer/api.clj | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 6b3c37511..24e87521c 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -283,8 +283,8 @@ (reset! -cljs-macros-loaded true) (if *cljs-macros-is-classpath* (load *cljs-macros-path*) - (load-file *cljs-macros-path*)) - (intern-macros 'cljs.core))) + (load-file *cljs-macros-path*))) + (intern-macros 'cljs.core)) (defmacro with-core-macros [path & body] @@ -1459,7 +1459,7 @@ (select-keys ns-info merge-keys)))) ns-info)) ns-info)] - (swap! env/*compiler* assoc-in [::namespaces name] ns-info) + (swap! env/*compiler* update-in [::namespaces name] merge ns-info) (merge {:env env :op :ns :form form} (cond-> ns-info (@reload :use) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index 88711a055..f21931857 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -20,7 +20,8 @@ (try (ana/resolve-var env sym (ana/confirm-var-exists-throw)) - (catch Exception e))) + (catch Exception e + (ana/resolve-macro-var env sym)))) (defn all-ns "Return all namespaces. Analagous to clojure.core/all-ns but From ab2f42fa829801011a433db10a11fba56b214fb5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 12:32:27 -0500 Subject: [PATCH 0804/4033] cleanup doc for macros --- src/clj/cljs/repl.clj | 2 +- src/cljs/cljs/repl.cljs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 4616b7d73..3273faf7e 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -901,7 +901,7 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) `(cljs.repl/print-doc (quote ~(update-in (select-keys (ana-api/resolve &env name) - [:ns :name :doc :forms :arglists]) + [:ns :name :doc :forms :arglists :macro :url]) [:name] clojure.core/name)))))) (defmacro find-doc diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index c904aeda1..f80926764 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -15,7 +15,9 @@ (cond (:forms m) (doseq [f (:forms m)] (println " " f)) - (:arglists m) (prn (second (:arglists m)))) + (:arglists m) (if (:macro m) + (prn (:arglists m)) + (prn (second (:arglists m))))) (if (:special-form m) (do (println "Special Form") From 867e17b737b39a017eab56888b11a7c053abff5b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 15:27:28 -0500 Subject: [PATCH 0805/4033] add missing docstring for core macros --- src/clj/cljs/core.clj | 255 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 243 insertions(+), 12 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 0b6ed3e5b..8ea76289d 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -290,7 +290,9 @@ (defmacro js-in [key obj] (core/list 'js* "~{} in ~{}" key obj)) -(defmacro js-debugger [] +(defmacro js-debugger + "Emit JavaScript \"debugger;\" statement." + [] (core/list 'js* "debugger;")) (defmacro true? [x] @@ -308,12 +310,17 @@ (bool-expr (core/list 'js* "typeof ~{} === 'string'" x))) ;; TODO: x must be a symbol, not an arbitrary expression -(defmacro exists? [x] +(defmacro exists? + "Return true if argument exists, analogous to usage of typeof operator + in JavaScript." + [x] (bool-expr (core/list 'js* "typeof ~{} !== 'undefined'" (vary-meta x assoc :cljs.analyzer/no-resolve true)))) -(defmacro undefined? [x] +(defmacro undefined? + "Return true if argument is identical to the JavaScript undefined value." + [x] (bool-expr (core/list 'js* "(void 0 === ~{})" x))) (defmacro identical? [a b] @@ -610,7 +617,45 @@ 'js/Number "number" 'js/Function "function"}) -(defmacro reify [& impls] +(defmacro reify + "reify is a macro with the following structure: + + (reify options* specs*) + + Currently there are no options. + + Each spec consists of the protocol name followed by zero + or more method bodies: + + protocol + (methodName [args+] body)* + + Methods should be supplied for all methods of the desired + protocol(s). You can also define overrides for Object methods. Note that + the first parameter must be supplied to correspond to the target object + ('this' in JavaScript parlance). Note also that recur calls + to the method head should *not* pass the target object, it will be supplied + automatically and can not be substituted. + + recur works to method heads The method bodies of reify are lexical + closures, and can refer to the surrounding local scope: + + (str (let [f \"foo\"] + (reify Object + (toString [this] f)))) + == \"foo\" + + (seq (let [f \"foo\"] + (reify ISeqable + (-seq [this] (-seq f))))) + == (\\f \\o \\o)) + + reify always implements IMeta and IWithMeta and transfers meta + data of the form to the created object. + + (meta ^{:k :v} (reify Object (toString [this] \"foo\"))) + == {:k :v}" + [& impls] (let [t (with-meta (gensym "t") {:anonymous true}) meta-sym (gensym "meta") this-sym (gensym "_") @@ -628,13 +673,18 @@ ~@impls)) (new ~t ~@locals ~(ana/elide-reader-meta (meta &form)))))) -(defmacro specify! [expr & impls] +(defmacro specify! + "Identical to reify but mutates its first argument." + [expr & impls] (let [x (with-meta (gensym "x") {:extend :instance})] `(let [~x ~expr] (extend-type ~x ~@impls) ~x))) -(defmacro specify [expr & impls] +(defmacro specify + "Identical to specify but does not mutate its first argument. The first + argument must be an ICloneable instance." + [expr & impls] `(cljs.core/specify! (cljs.core/clone ~expr) ~@impls)) @@ -813,7 +863,18 @@ (validate-impl-sigs env proto method)) (recur (conj protos proto) impls))))) -(defmacro extend-type [type-sym & impls] +(defmacro extend-type + "Extend a type to a series of protocols. Useful when you are + supplying the definitions explicitly inline. Propagates the + type as a type hint on the first argument of all fns. + + (extend-type MyType + ICounted + (-count [c] ...) + Foo + (bar [x y] ...) + (baz ([x] ...) ([x y & zs] ...))" + [type-sym & impls] (let [env &env _ (validate-impls env impls) resolve (partial resolve-var env) @@ -886,7 +947,56 @@ [~@fields] (new ~rname ~@field-values)))) -(defmacro deftype [t fields & impls] +(defmacro deftype + "(deftype name [fields*] options* specs*) + + Currently there are no options. + + Each spec consists of a protocol or interface name followed by zero + or more method bodies: + + protocol-or-Object + (methodName [args*] body)* + + The type will have the (by default, immutable) fields named by + fields, which can have type hints. Protocols and methods + are optional. The only methods that can be supplied are those + declared in the protocols/interfaces. Note that method bodies are + not closures, the local environment includes only the named fields, + and those fields can be accessed directly. Fields can be qualified + with the metadata :mutable true at which point (set! afield aval) will be + supported in method bodies. Note well that mutable fields are extremely + difficult to use correctly, and are present only to facilitate the building + of higherlevel constructs, such as ClojureScript's reference types, in + ClojureScript itself. They are for experts only - if the semantics and + implications of :mutable are not immediately apparent to you, you should not + be using them. + + Method definitions take the form: + + (methodname [args*] body) + + The argument and return types can be hinted on the arg and + methodname symbols. If not supplied, they will be inferred, so type + hints should be reserved for disambiguation. + + Methods should be supplied for all methods of the desired + protocol(s). You can also define overrides for methods of Object. Note that + a parameter must be supplied to correspond to the target object + ('this' in JavaScript parlance). Note also that recur calls to the method + head should *not* pass the target object, it will be supplied + automatically and can not be substituted. + + In the method bodies, the (unqualified) name can be used to name the + class (for calls to new, instance? etc). + + One constructor will be defined, taking the designated fields. Note + that the field names __meta and __extmap are currently reserved and + should not be used when defining your own types. + + Given (deftype TypeName ...), a factory function called ->TypeName + will be defined, taking positional parameters for the fields" + [t fields & impls] (let [env &env r (:name (cljs.analyzer/resolve-var (dissoc env :locals) t)) [fpps pmasks] (prepare-protocol-masks env impls) @@ -992,7 +1102,60 @@ `(defn ~fn-name [~ms] (new ~rname ~@getters nil (dissoc ~ms ~@ks) nil)))) -(defmacro defrecord [rsym fields & impls] +(defmacro defrecord + "(defrecord name [fields*] options* specs*) + + Currently there are no options. + + Each spec consists of a protocol or interface name followed by zero + or more method bodies: + + protocol-or-Object + (methodName [args*] body)* + + The record will have the (immutable) fields named by + fields, which can have type hints. Protocols and methods + are optional. The only methods that can be supplied are those + declared in the protocols. Note that method bodies are + not closures, the local environment includes only the named fields, + and those fields can be accessed directly. + + Method definitions take the form: + + (methodname [args*] body) + + The argument and return types can be hinted on the arg and + methodname symbols. If not supplied, they will be inferred, so type + hints should be reserved for disambiguation. + + Methods should be supplied for all methods of the desired + protocol(s). You can also define overrides for + methods of Object. Note that a parameter must be supplied to + correspond to the target object ('this' in JavaScript parlance). Note also + that recur calls to the method head should *not* pass the target object, it + will be supplied automatically and can not be substituted. + + In the method bodies, the (unqualified) name can be used to name the + class (for calls to new, instance? etc). + + The type will have implementations of several ClojureScript + protocol generated automatically: IMeta/IWithMeta (metadata support) and + IMap, etc. + + In addition, defrecord will define type-and-value-based =, + and will define ClojureScript IHash and IEquiv. + + Two constructors will be defined, one taking the designated fields + followed by a metadata map (nil for none) and an extension field + map (nil for none), and one taking only the fields (using nil for + meta and extension fields). Note that the field names __meta + and __extmap are currently reserved and should not be used when + defining your own records. + + Given (defrecord TypeName ...), two factory functions will be + defined: ->TypeName, taking positional parameters for the fields, + and map->TypeName, taking a map of keywords to field values." + [rsym fields & impls] (let [rsym (vary-meta rsym assoc :internal-ctor true) r (vary-meta (:name (cljs.analyzer/resolve-var (dissoc &env :locals) rsym)) @@ -1006,7 +1169,47 @@ ~(build-map-factory rsym r fields) ~r))) -(defmacro defprotocol [psym & doc+methods] +(defmacro defprotocol + "A protocol is a named set of named methods and their signatures: + + (defprotocol AProtocolName + ;optional doc string + \"A doc string for AProtocol abstraction\" + + ;method signatures + (bar [this a b] \"bar docs\") + (baz [this a] [this a b] [this a b c] \"baz docs\")) + + No implementations are provided. Docs can be specified for the + protocol overall and for each method. The above yields a set of + polymorphic functions and a protocol object. All are + namespace-qualified by the ns enclosing the definition The resulting + functions dispatch on the type of their first argument, which is + required and corresponds to the implicit target object ('this' in + JavaScript parlance). defprotocol is dynamic, has no special compile-time + effect, and defines no new types. + + (defprotocol P + (foo [this]) + (bar-me [this] [this y])) + + (deftype Foo [a b c] + P + (foo [this] a) + (bar-me [this] b) + (bar-me [this y] (+ c y))) + + (bar-me (Foo. 1 2 3) 42) + => 45 + + (foo + (let [x 42] + (reify P + (foo [this] 17) + (bar-me [this] x) + (bar-me [this y] x)))) + => 17" + [psym & doc+methods] (let [p (:name (cljs.analyzer/resolve-var (dissoc &env :locals) psym)) psym (vary-meta psym assoc :protocol-symbol true) ns-name (-> &env :ns :name) @@ -1091,7 +1294,12 @@ false))) (cljs.core/native-satisfies? ~psym ~xsym))))) -(defmacro lazy-seq [& body] +(defmacro lazy-seq + "Takes a body of expressions that returns an ISeq or nil, and yields + a ISeqable object that will invoke the body only the first time seq + is called, and will cache the result and return it on all subsequent + seq calls." + [& body] `(new cljs.core/LazySeq nil (fn [] ~@body) nil nil)) (defmacro delay [& body] @@ -1189,7 +1397,30 @@ cljs.analyzer/*cljs-file*))))) (assoc m test expr))) -(defmacro case [e & clauses] +(defmacro case + "Takes an expression, and a set of clauses. + + Each clause can take the form of either: + + test-constant result-expr + + (test-constant1 ... test-constantN) result-expr + + The test-constants are not evaluated. They must be compile-time + literals, and need not be quoted. If the expression is equal to a + test-constant, the corresponding result-expr is returned. A single + default expression can follow the clauses, and its value will be + returned if no clause matches. If no default expression is provided + and no clause matches, an Error is thrown. + + Unlike cond and condp, case does a constant-time dispatch, the + clauses are not considered sequentially. All manner of constant + expressions are acceptable in case, including numbers, strings, + symbols, keywords, and (ClojureScript) composites thereof. Note that since + lists are used to group multiple constants that map to the same + expression, a vector can be used to match a list if needed. The + test-constants need not be all of the same type." + [e & clauses] (core/let [default (if (odd? (count clauses)) (last clauses) `(throw From 73b45ff299c37f9407d8fb0ec49ba1782faad10e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 15:28:59 -0500 Subject: [PATCH 0806/4033] `cljs.repl/find-doc` needs to include :macro & :url --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 3273faf7e..1f03ecc5b 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -914,7 +914,7 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (fn [ns] (map (fn [m] - (update-in (select-keys m [:ns :name :doc :forms :arglists]) + (update-in (select-keys m [:ns :name :doc :forms :arglists :macro :url]) [:name] clojure.core/name)) (sort-by :name (vals (ana-api/ns-interns ns))))) (ana-api/all-ns)) From 21b4e9e7c523c3411908101a47d240de597c354d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 16:08:23 -0500 Subject: [PATCH 0807/4033] add many missing docstrings to the standard lib --- src/cljs/cljs/core.cljs | 164 +++++++++++++++++++++++++++++++--------- 1 file changed, 130 insertions(+), 34 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 39035cf96..54edb7b92 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -95,10 +95,14 @@ [x] (coercive-= x nil)) -(defn ^boolean array? [x] +(defn ^boolean array? + "Returns true if x is a JavaScript array." + [x] (cljs.core/array? x)) -(defn ^boolean number? [n] +(defn ^boolean number? + "Returns true if x is a JavaScript number." + [n] (cljs.core/number? n)) (defn ^boolean not @@ -109,12 +113,16 @@ "Returns true if x is not nil, false otherwise." [x] (not (nil? x))) -(defn ^boolean object? [x] +(defn ^boolean object? + "Returns true if x's constructor is Object" + [x] (if-not (nil? x) (identical? (.-constructor x) js/Object) false)) -(defn ^boolean string? [x] +(defn ^boolean string? + "Returns true if x is a JavaScript string." + [x] (goog/isString x)) (set! *unchecked-if* true) @@ -138,7 +146,9 @@ argv as arguments"} *main-cli-fn* nil) -(defn type [x] +(defn type + "Return x's constructor." + [x] (when-not (nil? x) (.-constructor x))) @@ -169,6 +179,8 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; arrays ;;;;;;;;;;;;;;;; (defn ^array make-array + "Construct a JavaScript array of specified size. Accepts ignored type + argument for compatibility with Clojure." ([size] (js/Array. size)) ([type size] @@ -219,6 +231,8 @@ (declare reduce) (defn ^array into-array + "Returns an array with components set to the values in aseq. Optional type + argument accepted for compatibility with Clojure." ([aseq] (into-array nil aseq)) ([type aseq] @@ -544,7 +558,10 @@ h (add-to-string-hash-cache k)))) -(defn hash [o] +(defn hash + "Returns the hash code of its argument. Note this is the hash code + consistent with =." + [o] (cond (implements? IHash o) (-hash ^not-native o) @@ -574,10 +591,15 @@ (bit-shift-left seed 6) (bit-shift-right seed 2)))) -(defn ^boolean instance? [t o] +(defn ^boolean instance? + "Evaluates x and tests if it is an instance of the type + c. Returns true or false" + [t o] (cljs.core/instance? t o)) -(defn ^boolean symbol? [x] +(defn ^boolean symbol? + "Return true if x is a Symbol" + [x] (instance? Symbol x)) (defn- hash-symbol [sym] @@ -698,13 +720,19 @@ (declare array-seq prim-seq IndexedSeq) -(defn iterable? [x] +(defn iterable? + "Return true if x implements IIterable protocol." + [x] (satisfies? IIterable x)) -(defn clone [value] +(defn clone + "Clone the supplied value which must implement ICloneable." + [value] (-clone value)) -(defn cloneable? [value] +(defn cloneable? + "Return true if x implements ICloneable protocol." + [value] (satisfies? ICloneable value)) (defn ^seq seq @@ -791,7 +819,9 @@ #js {:value x :done false}) #js {:value nil :done true}))) -(defn es6-iterator [coll] +(defn es6-iterator + "EXPERIMENTAL: Return a ES2015 compatible iterator for coll." + [coll] (ES6Iterator. (seq coll))) (declare es6-iterator-seq) @@ -806,7 +836,9 @@ (set! _rest (es6-iterator-seq iter))) _rest)) -(defn es6-iterator-seq [iter] +(defn es6-iterator-seq + "EXPERIMENTAL: Given an ES2015 compatible iterator return a seq." + [iter] (let [v (.next iter)] (if (.-done v) () @@ -922,6 +954,10 @@ ;; generic to all refs ;; (but currently hard-coded to atom!) (defn deref + "Also reader macro: @var/@atom/@delay. Returns the + most-recently-committed value of ref. When applied to a var + or atom, returns its current state. When applied to a delay, forces + it if not already forced. See also - realized?." [o] (-deref o)) @@ -1079,6 +1115,7 @@ reduces them without incurring seq initialization" (es6-iterable IndexedSeq) (defn prim-seq + "Create seq from a primitive JavaScript Array-like." ([prim] (prim-seq prim 0)) ([prim i] @@ -1086,6 +1123,7 @@ reduces them without incurring seq initialization" (IndexedSeq. prim i)))) (defn array-seq + "Create a seq from a JavaScript array." ([array] (prim-seq array 0)) ([array i] @@ -1398,7 +1436,9 @@ reduces them without incurring seq initialization" (recur ret (first ks) (next ks)) ret))))) -(defn ^boolean fn? [f] +(defn ^boolean fn? + "Return true if f is a JavaScript function or satisfies the Fn protocol." + [f] (or ^boolean (goog/isFunction f) (satisfies? Fn f))) (deftype MetaFn [afn meta] @@ -1549,21 +1589,28 @@ reduces them without incurring seq initialization" (declare ChunkedCons ChunkedSeq) (defn ^boolean chunked-seq? + "Return true if x is satisfies IChunkedSeq." [x] (implements? IChunkedSeq x)) ;;;;;;;;;;;;;;;;;;;; js primitives ;;;;;;;;;;;; (defn js-obj + "Create JavaSript object from an even number arguments representing + interleaved keys and values." ([] (cljs.core/js-obj)) ([& keyvals] (apply gobject/create keyvals))) -(defn js-keys [obj] +(defn js-keys + "Return the JavaScript keys for an object." + [obj] (let [keys (array)] (goog.object/forEach obj (fn [val key obj] (.push keys key))) keys)) -(defn js-delete [obj key] +(defn js-delete + "Delete a property from a JavaScript object." + [obj key] (cljs.core/js-delete obj key)) (defn- array-copy @@ -1594,7 +1641,9 @@ reduces them without incurring seq initialization" "Returns true if x is the value true, false otherwise." [x] (cljs.core/true? x)) -(defn ^boolean undefined? [x] +(defn ^boolean undefined? + "Returns true if x identical to the JavaScript undefined value." + [x] (cljs.core/undefined? x)) (defn ^boolean seq? @@ -1609,10 +1658,14 @@ reduces them without incurring seq initialization" [s] (satisfies? ISeqable s)) -(defn ^boolean boolean [x] +(defn ^boolean boolean + "Coerce to boolean" + [x] (if x true false)) -(defn ^boolean ifn? [f] +(defn ^boolean ifn? + "Returns true if f returns true for fn? or satisfies IFn." + [f] (or (fn? f) (satisfies? IFn f))) (defn ^boolean integer? @@ -1815,9 +1868,14 @@ reduces them without incurring seq initialization" (-kv-reduce coll f init) init))) -(defn identity [x] x) +(defn identity + "Returns its argument." + [x] x) (defn completing + "Takes a reducing function f of 2 args and returns a fn suitable for + transduce by adding an arity-1 signature that calls cf (default - + identity) on the result argument." ([f] (completing f identity)) ([f cf] (fn @@ -1974,10 +2032,14 @@ reduces them without incurring seq initialization" ([x y] (cljs.core/unchecked-add-int x y)) ([x y & more] (reduce unchecked-add-int (cljs.core/unchecked-add-int x y) more))) -(defn unchecked-dec [x] +(defn unchecked-dec + "Returns a number one less than x, an int." + [x] (cljs.core/unchecked-dec x)) -(defn unchecked-dec-int [x] +(defn unchecked-dec-int + "Returns a number one less than x, an int." + [x] (cljs.core/unchecked-dec-int x)) (defn ^number unchecked-divide-int @@ -2176,7 +2238,9 @@ reduces them without incurring seq initialization" "Returns true if num is greater than zero, else false" [n] (cljs.core/pos? n)) -(defn ^boolean zero? [n] +(defn ^boolean zero? + "Returns true if num is zero, else false" + [n] (cljs.core/zero? n)) (defn ^boolean neg? @@ -2392,10 +2456,15 @@ reduces them without incurring seq initialization" (es6-iterable EmptyList) -(defn ^boolean reversible? [coll] +(defn ^boolean reversible? + "Returns true if coll satisfies? IReversible." + [coll] (satisfies? IReversible coll)) -(defn ^seq rseq [coll] +(defn ^seq rseq + "Returns, in constant time, a seq of the items in rev (which + can be a vector or sorted-map), in reverse order. If rev is empty returns nil" + [coll] (-rseq coll)) (defn reverse @@ -2405,7 +2474,9 @@ reduces them without incurring seq initialization" (rseq coll) (reduce conj () coll))) -(defn list [& xs] +(defn list + "Creates a new list containing the items." + [& xs] (let [arr (if (and (instance? IndexedSeq xs) (zero? (.-i xs))) (.-arr xs) (let [arr (array)] @@ -2477,7 +2548,9 @@ reduces them without incurring seq initialization" (Cons. nil x coll nil) (Cons. nil x (seq coll) nil))) -(defn ^boolean list? [x] +(defn ^boolean list? + "Returns true if x implements IList" + [x] (satisfies? IList x)) (defn hash-keyword [k] @@ -2523,10 +2596,14 @@ reduces them without incurring seq initialization" IPrintWithWriter (-pr-writer [o writer _] (-write writer (str ":" fqn)))) -(defn ^boolean keyword? [x] +(defn ^boolean keyword? + "Return true if x is a Keyword" + [x] (instance? Keyword x)) -(defn ^boolean keyword-identical? [x y] +(defn ^boolean keyword-identical? + "Efficient test to determine that two keywords are identical." + [x y] (if (identical? x y) true (if (and (keyword? x) @@ -2783,6 +2860,8 @@ reduces them without incurring seq initialization" ret)) (defn int-array + "Creates an array of ints. Does not coerce array, provided for compatibility + with Clojure." ([size-or-seq] (if (number? size-or-seq) (int-array size-or-seq nil) @@ -2803,6 +2882,8 @@ reduces them without incurring seq initialization" a))))) (defn long-array + "Creates an array of longs. Does not coerce array, provided for compatibility + with Clojure." ([size-or-seq] (if (number? size-or-seq) (long-array size-or-seq nil) @@ -2823,6 +2904,8 @@ reduces them without incurring seq initialization" a))))) (defn double-array + "Creates an array of doubles. Does not coerce array, provided for compatibility + with Clojure." ([size-or-seq] (if (number? size-or-seq) (double-array size-or-seq nil) @@ -2843,6 +2926,8 @@ reduces them without incurring seq initialization" a))))) (defn object-array + "Creates an array of objects. Does not coerce array, provided for compatibility + with Clojure." ([size-or-seq] (if (number? size-or-seq) (object-array size-or-seq nil) @@ -4478,7 +4563,10 @@ reduces them without incurring seq initialization" (es6-iterable PersistentVector) -(defn vec [coll] +(defn vec + "Creates a new vector containing the contents of coll. JavaScript arrays + will be aliased and should not be modified." + [coll] (if (array? coll) (.fromArray PersistentVector coll true) (-persistent! @@ -4486,7 +4574,9 @@ reduces them without incurring seq initialization" (-as-transient (.-EMPTY PersistentVector)) coll)))) -(defn vector [& args] +(defn vector + "Creates a new vector containing the args." + [& args] (if (and (instance? IndexedSeq args) (zero? (.-i args))) (.fromArray PersistentVector (.-arr args) true) (vec args))) @@ -7604,6 +7694,8 @@ reduces them without incurring seq initialization" (-persistent! out)))))) (defn hash-set + "Returns a new hash set with supplied keys. Any equal keys are + handled as if by repeated uses of conj." ([] #{}) ([& keys] (set keys))) @@ -7661,7 +7753,9 @@ reduces them without incurring seq initialization" (step coll #{})))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn butlast [s] +(defn butlast + "Return a seq of all but the last item in coll, in linear time" + [s] (loop [ret [] s s] (if (next s) (recur (conj ret (first s)) (next s)) @@ -8062,8 +8156,10 @@ reduces them without incurring seq initialization" ;;;;;;;;;;;;;;;;;;;;;;;;; Regular Expressions ;;;;;;;;;; -(defn regexp? [o] - (instance? js/RegExp o)) +(defn regexp? + "Returns true if x is a JavaScript RegExp instance." + [x] + (instance? js/RegExp x)) (defn re-matches "Returns the result of (re-find re s) if re fully matches s." From 68008cc718c924388d8e8396b15a350b16f8ac32 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 16:09:49 -0500 Subject: [PATCH 0808/4033] merge macros info into analyzer api, ns-interns and ns-publics --- src/clj/cljs/analyzer/api.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index f21931857..f38a030c7 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -41,14 +41,18 @@ clojure.core/ns-interns but returns var analysis maps not vars." [ns] {:pre [(symbol? ns)]} - (get-in @env/*compiler* [::ana/namespaces ns :defs])) + (merge + (get-in @env/*compiler* [::ana/namespaces ns :macros]) + (get-in @env/*compiler* [::ana/namespaces ns :defs]))) (defn ns-publics "Given a namespace return all the public var analysis maps. Analagous to clojure.core/ns-publics but returns var analysis maps not vars." [ns] {:pre [(symbol? ns)]} - (->> (get-in @env/*compiler* [::ana/namespaces ns :defs]) + (->> (merge + (get-in @env/*compiler* [::ana/namespaces ns :macros]) + (get-in @env/*compiler* [::ana/namespaces ns :defs])) (remove (fn [[k v]] (:private v))) (into {}))) From 7275372336298c4831203246b1d2450816f517c7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 16:28:37 -0500 Subject: [PATCH 0809/4033] cljs.repl/pst support for Rhino REPL --- src/clj/cljs/repl/rhino.clj | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 70494b26c..4aae3e740 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -189,6 +189,17 @@ (Integer/parseInt line)) :column 0})) (string/split frames-str #"\n")))) + repl/IGetError + (-get-error [this e env opts] + (let [{:keys [scope]} this + ex (-> scope + (ScriptableObject/getProperty "cljs") + (ScriptableObject/getProperty "core") + (ScriptableObject/getProperty "_STAR_e") + .unwrap)] + {:status :exception + :value (.toString ex) + :stacktrace (stacktrace ex)})) repl/IJavaScriptEnv (-setup [this opts] (rhino-setup this opts)) From d7fc5de668952a5dd011888dc4db04cfdc97c727 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 20:27:45 -0500 Subject: [PATCH 0810/4033] default eval wrap needs to pr-str _inside_ the try otherwise exceptions from printing lazy seqs will not get caught --- src/clj/cljs/repl.clj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 1f03ecc5b..52ce06444 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -467,16 +467,16 @@ ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) :else (fn [x] - `(cljs.core.pr-str - (try + `(try + (cljs.core.pr-str (let [ret# ~x] (set! *3 *2) (set! *2 *1) (set! *1 ret#) - ret#) - (catch :default e# - (set! *e e#) - (throw e#))))))) + ret#)) + (catch :default e# + (set! *e e#) + (throw e#)))))) (defn- eval-cljs "Given a REPL evaluation environment, an analysis environment, and a From 40d34c5e3450bd335149fffd6f1b4f37d42bea25 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 21:29:26 -0500 Subject: [PATCH 0811/4033] CLJS-1057: Nashorn REPL should not use EDN rep for errors Clean up error handling in general. Giving functions top level names is very noisy and introduces incredible complexity with respect to function name printing, every engine is different. --- src/clj/cljs/repl.clj | 25 +++++++++------------- src/clj/cljs/repl/nashorn.clj | 40 +++++++++++++++++++++++------------ src/clj/cljs/repl/node.clj | 5 +++-- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 52ce06444..bcd492a53 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -109,9 +109,9 @@ IReplEnvOptions (-repl-options [_] nil)) -(defprotocol IParseErrorMessage - (-parse-error-message [repl-env message error build-options] - "Given the original JavaScript error message return the string to actually +(defprotocol IParseError + (-parse-error [repl-env error build-options] + "Given the original JavaScript error return the error to actually use.")) (defprotocol IGetError @@ -271,8 +271,7 @@ [line' column'] (if ns-info (mapped-line-and-column sm line column) [line column]) - name' (if (and ns-info function) - (symbol (name ns) (cljrepl/demunge function)) + name' (when (and ns-info function) function) file' (if no-source-file? file @@ -994,7 +993,7 @@ str-or-pattern." ([e] (let [{:keys [repl-env] :as env} &env] (when (and e repl-env) - (let [ret (if (satisfies? IGetError repl-env) + (when-let [ret (if (satisfies? IGetError repl-env) (-get-error repl-env e env *repl-opts*) (edn/read-string (evaluate-form repl-env env "" @@ -1002,12 +1001,8 @@ str-or-pattern." (pr-str {:value (.-message ~e) :stacktrace (.-stack ~e)})))))] - (when ret - (let [ret (update-in ret [:value] - (fn [msg] - ;; give REPL environments a chance to fix or - ;; or elide redundant information - (if (satisfies? IParseErrorMessage repl-env) - (-parse-error-message repl-env msg ret *repl-opts*) - msg)))] - (display-error repl-env ret nil *repl-opts*)))))))) + (display-error repl-env + (if (satisfies? IParseError repl-env) + (-parse-error repl-env ret *repl-opts*) + ret) + nil *repl-opts*)))))) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 5f1c173ee..e3478833e 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -15,7 +15,8 @@ [cljs.repl :as repl] [cljs.compiler :as comp] [cljs.closure :as closure]) - (:import [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory] + (:import [java.io File] + [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory] [jdk.nashorn.api.scripting NashornException])) ;; Nashorn Clojurescript repl binding. @@ -141,12 +142,6 @@ (string/replace-first file-name with-slash "") file-name))) -(defn- convert-stacktrace-element [^StackTraceElement el] - {:function (.getMethodName el) - :file (.getFileName el) - :line (.getLineNumber el) - :column 0}) - (def repl-filename "") (defrecord NashornEnv [engine debug] @@ -183,26 +178,43 @@ (try {:status :success :value (if-let [r (eval-str engine js)] (.toString r) "")} - - ;; Stringify the stacktrace to edn for easy parsing in -parse-stacktrace (catch ScriptException e (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] {:status :exception :value (.getMessage root-cause) - :stacktrace (pr-str (map convert-stacktrace-element - (NashornException/getScriptFrames root-cause)))})) + :stacktrace (NashornException/getScriptStackString root-cause)})) (catch Throwable e (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] {:status :exception :value (.getMessage root-cause) - :stacktrace (pr-str (map convert-stacktrace-element (.getStackTrace root-cause)))})))) + :stacktrace + (apply str + (interpose "\n" + (map str + (.getStackTrace root-cause))))})))) (-load [{engine :engine :as this} ns url] (load-ns engine ns)) (-tear-down [this]) repl/IParseStacktrace (-parse-stacktrace [this frames-str ret {output-dir :output-dir}] - (when-let [frames (read-string frames-str)] - (vec (map #(update-in %1 [:file] (fn [s] (strip-file-name s output-dir))) frames))))) + (vec + (map + (fn [frame-str] + (let [frame-str (string/replace frame-str #"\s+at\s+" "") + [function file-and-line] (string/split frame-str #"\s+") + [file-part line-part] (string/split file-and-line #":")] + {:file (string/replace (.substring file-part 1) + (str output-dir File/separator) "") + :function function + :line (Integer/parseInt + (.substring line-part 0 (dec (.length line-part)))) + :column 0})) + (string/split frames-str #"\n")))) + repl/IParseError + (-parse-error [_ err _] + (update-in err [:stacktrace] + (fn [st] + (string/join "\n" (drop 1 (string/split st #"\n"))))))) (defn repl-env* [{:keys [debug] :as opts}] (let [engine (create-engine opts)] diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index e914a7461..7c4f5f9c5 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -173,8 +173,9 @@ (-repl-options [this] {:output-dir ".cljs_node_repl" :target :nodejs}) - repl/IParseErrorMessage - (-parse-error-message [_ _ _ _]) + repl/IParseError + (-parse-error [_ err _] + (assoc err :value nil)) repl/IJavaScriptEnv (-setup [this opts] (setup this opts)) From 428950631dcfa8ad970baecc5c8870aead7e038d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 21:41:25 -0500 Subject: [PATCH 0812/4033] fix main test script so it write to builds/out-adv instead --- script/test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test b/script/test index 388532859..111a6aac5 100755 --- a/script/test +++ b/script/test @@ -9,7 +9,7 @@ possible=4 ran=0 #bin/cljsc test >out/core-test.js -bin/cljsc test "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true}" > builds/out-adv/core-advanced-test.js +bin/cljsc test "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" From 3c3399f944cbbb1b03da2bedf9aac2603aed1077 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 21:54:29 -0500 Subject: [PATCH 0813/4033] bump tools.reader and data.json deps --- pom.template.xml | 4 ++-- project.clj | 4 ++-- script/bootstrap | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 52a0b84d7..a4d063b5a 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -40,7 +40,7 @@ org.clojure data.json - 0.2.3 + 0.2.6 org.mozilla @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.8.10 + 0.8.16 diff --git a/project.clj b/project.clj index 0892ebe03..a5aea223e 100644 --- a/project.clj +++ b/project.clj @@ -9,8 +9,8 @@ :resource-paths ["src/cljs"] :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.6.0"] - [org.clojure/data.json "0.2.3"] - [org.clojure/tools.reader "0.8.10"] + [org.clojure/data.json "0.2.6"] + [org.clojure/tools.reader "0.8.16"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R5"]] diff --git a/script/bootstrap b/script/bootstrap index 589407478..447b9a297 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -4,10 +4,10 @@ set -e CLOJURE_RELEASE="1.6.0" CLOSURE_RELEASE="20150126" -DJSON_RELEASE="0.2.3" +DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="0.8.10" +TREADER_RELEASE="0.8.16" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From fca5265d2d8b7c4b1fcccdd658129218b991a1cc Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 22:06:54 -0500 Subject: [PATCH 0814/4033] fix source so it works on macros --- src/clj/cljs/repl.clj | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index bcd492a53..b8189258c 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -936,10 +936,13 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) Example: (source-fn 'filter)" [env x] - (when-let [v (ana/resolve-var env x)] + (when-let [v (ana-api/resolve env x)] (when-let [filepath (:file v)] - (let [f (io/file filepath)] - (when (.exists f) + (let [f (io/file filepath) + f (if (.exists f) + f + (io/resource filepath))] + (when f (with-open [pbr (PushbackReader. (io/reader f))] (let [rdr (readers/source-logging-push-back-reader pbr)] (dotimes [_ (dec (:line v))] (readers/read-line rdr)) From 0809de97b9c90c2a4681905d8123d8a087cb7f0a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 22:41:52 -0500 Subject: [PATCH 0815/4033] CLJS-1089: AOT analysis cache has bad :file paths ensure that cljs.core defs always have :file "cljs/core.cljs" --- src/clj/cljs/analyzer.clj | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 24e87521c..17c9a3d50 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -317,14 +317,18 @@ (defn source-info ([env] - (when-let [line (:line env)] - {:file *cljs-file* - :line (get-line name env) - :column (get-col name env)})) - ([name env] - {:file *cljs-file* + (when-let [line (:line env)] + {:file (if (= (-> env :ns :name) 'cljs.core) + "cljs/core.cljs" + *cljs-file*) :line (get-line name env) :column (get-col name env)})) + ([name env] + {:file (if (= (-> env :ns :name) 'cljs.core) + "cljs/core.cljs" + *cljs-file*) + :line (get-line name env) + :column (get-col name env)})) (defn message [env s] (str s (when (:line env) @@ -793,7 +797,13 @@ ;; elide test metadata, as it includes non-valid EDN - David (cond-> sym-meta :test (-> (dissoc :test) (assoc :test true))) - {:meta (dissoc sym-meta :test)} + {:meta (-> sym-meta + (dissoc :test) + (update-in [:file] + (fn [f] + (if (= (-> env :ns :name) 'cljs.core) + "cljs/core.cljs" + f))))} (when doc {:doc doc}) (when dynamic {:dynamic true}) (source-info var-name env) From d16de6180b47d71af09b7ae86126f75d29821f30 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Mar 2015 22:50:05 -0500 Subject: [PATCH 0816/4033] try tweaking build script --- script/build | 1 + 1 file changed, 1 insertion(+) diff --git a/script/build b/script/build index 0527d59a7..34ab1fa11 100755 --- a/script/build +++ b/script/build @@ -40,6 +40,7 @@ rm -f src/cljs/cljs/core.aot.js rm -f src/cljs/cljs/core.aot.js.map rm -f src/cljs/cljs/core.cljs.cache.aot.edn +./script/bootstrap ./script/aot_core AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` From 74a64f962f38920c39fe887bfc1e8afbee4ff348 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Mar 2015 11:50:22 -0500 Subject: [PATCH 0817/4033] make scoped function names less noisy for now --- src/clj/cljs/compiler.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 6338476bc..41f5a207f 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -64,8 +64,7 @@ (concat (map (comp str :name) fn-scope) [name])))] (symbol (munge - (str (string/replace (str ns) "." "$") - "_SLASH_" scoped-name))))) + (str (string/replace (str ns) "." "$") "$" scoped-name))))) (defn munge ([s] (munge s js-reserved)) From 2e3a8698bb8c1e92347347a26ca068eae34a2c4c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Mar 2015 13:51:35 -0500 Subject: [PATCH 0818/4033] script/aot_core that doesn't require boostrap --- script/aot.clj | 2 ++ script/aot_core | 22 ++++++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 script/aot.clj diff --git a/script/aot.clj b/script/aot.clj new file mode 100644 index 000000000..1f2f5e80f --- /dev/null +++ b/script/aot.clj @@ -0,0 +1,2 @@ +(require '[cljs.closure :as cljsc]) +(cljsc/aot-cache-core) diff --git a/script/aot_core b/script/aot_core index 0de000469..747742e37 100755 --- a/script/aot_core +++ b/script/aot_core @@ -1,14 +1,12 @@ #!/bin/bash -if [ "$CLOJURESCRIPT_HOME" = "" ]; then - CLOJURESCRIPT_HOME="`dirname $0`/.." -fi - -CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do - CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next -done - -java -server -cp $CLJSC_CP clojure.main -e \ -"(require '[cljs.closure :as cljsc]) -(cljsc/aot-cache-core)" \ No newline at end of file +set -ex + +CP_FILE=`mktemp /tmp/cljs_cp.txt.XXXXXXXXXXX` + +mvn -f pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE + +CLJS_CP=`cat $CP_FILE` + +java -server -cp "$CLJS_CP:src/clj:src/cljs" clojure.main script/aot.clj + From 3b17d25e62ca7d2b3fb352b3f6a36558be13571e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Mar 2015 13:57:59 -0500 Subject: [PATCH 0819/4033] drop bootstrap from build script --- script/build | 1 - 1 file changed, 1 deletion(-) diff --git a/script/build b/script/build index 34ab1fa11..0527d59a7 100755 --- a/script/build +++ b/script/build @@ -40,7 +40,6 @@ rm -f src/cljs/cljs/core.aot.js rm -f src/cljs/cljs/core.aot.js.map rm -f src/cljs/cljs/core.cljs.cache.aot.edn -./script/bootstrap ./script/aot_core AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` From 9b0c0ed5b204c12429c2dc5c718b054bc68d8767 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Mar 2015 14:05:47 -0500 Subject: [PATCH 0820/4033] downgrade to Rhino 1.7R4 --- pom.template.xml | 2 +- project.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index a4d063b5a..b2740b3fa 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -45,7 +45,7 @@ org.mozilla rhino - 1.7R5 + 1.7R4 org.clojure diff --git a/project.clj b/project.clj index a5aea223e..41bb29664 100644 --- a/project.clj +++ b/project.clj @@ -13,7 +13,7 @@ [org.clojure/tools.reader "0.8.16"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] - [org.mozilla/rhino "1.7R5"]] + [org.mozilla/rhino "1.7R4"]] :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all}} From 2c9c1007ba3c07f2122339af292075dbfadf5f50 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Mar 2015 14:11:31 -0500 Subject: [PATCH 0821/4033] revert last commit, Hudson build issue, nothing to do w/ Rhino --- pom.template.xml | 2 +- project.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index b2740b3fa..a4d063b5a 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -45,7 +45,7 @@ org.mozilla rhino - 1.7R4 + 1.7R5 org.clojure diff --git a/project.clj b/project.clj index 41bb29664..657e0487b 100644 --- a/project.clj +++ b/project.clj @@ -13,7 +13,7 @@ [org.clojure/tools.reader "0.8.16"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] - [org.mozilla/rhino "1.7R4"]] + :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all}} From 744a92c250542880ecf3c87fc7e9bbae48dfa8c9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Mar 2015 14:40:33 -0500 Subject: [PATCH 0822/4033] script/aot_core tweak for Hudson --- script/aot_core | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/script/aot_core b/script/aot_core index 747742e37..ce828fcaf 100755 --- a/script/aot_core +++ b/script/aot_core @@ -8,5 +8,13 @@ mvn -f pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE CLJS_CP=`cat $CP_FILE` -java -server -cp "$CLJS_CP:src/clj:src/cljs" clojure.main script/aot.clj +# For Hudson server +if [ "$HUDSON" = "true" ]; then + $JAVA_HOME/bin/java -server -cp "$CLJS_CP:src/clj:src/cljs" clojure.main script/aot.clj +else + java -server -cp "$CLJS_CP:src/clj:src/cljs" clojure.main script/aot.clj +fi + + + From ebf7a92b8e1686086cfc044756160320f88011e9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 7 Mar 2015 14:52:05 -0500 Subject: [PATCH 0823/4033] fix typo in project.clj --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 657e0487b..a5aea223e 100644 --- a/project.clj +++ b/project.clj @@ -13,7 +13,7 @@ [org.clojure/tools.reader "0.8.16"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] - + [org.mozilla/rhino "1.7R5"]] :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all}} From 8ed0640941493de5a68c05706790f14d909f98d6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 12:13:48 -0400 Subject: [PATCH 0824/4033] CLJS-1094: read option not used by cljs.repl/repl* --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index b8189258c..406388353 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -705,7 +705,7 @@ (apply merge ((juxt :requires :require-macros) (ana/get-namespace ana/*cljs-ns*)))] - (repl-read request-prompt request-exit))] + (read request-prompt request-exit))] (or ({request-exit request-exit :cljs/quit request-exit request-prompt request-prompt} input) From 0e7671de113ecfb6a1cef5901ce7db9a6126b100 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 12:38:41 -0400 Subject: [PATCH 0825/4033] CLJS-1093: Better compiler defaults :optimizations :none is the default if nothing supplied. if :optimizations :none, :cache-analysis is true and :source-map is true by default unless different setting supplied :verbose is the default for cljs.closure/watch unless different setting supplied --- src/clj/cljs/closure.clj | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 1ec7aafca..3f3869977 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1310,7 +1310,11 @@ should contain the source for the given namespace name." (and (satisfies? deps/IJavaScript js) (deps/-foreign? js))) -(defn add-implicit-options [opts] +(defn add-implicit-options + [{:keys [optimizations output-dir] + :or {optimizations :none + output-dir "out"} + :as opts}] (let [opts (cond-> opts (:closure-defines opts) (assoc :closure-defines @@ -1322,15 +1326,21 @@ should contain the source for the given namespace name." (cond-> (-> opts (assoc + :optimizations optimizations + :output-dir output-dir :ups-libs libs :ups-foreign-libs foreign-libs :ups-externs externs) (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) + (:target opts) (assoc-in [:closure-defines (str (comp/munge 'cljs.core/*target*))] (name (:target opts))) - (nil? (:optimizations opts)) - (assoc :optimizations :none)))) + + (= optimizations :none) + (assoc + :cache-analysis (:cache-analysis opts true) + :source-map (:source-map opts true))))) (defn build "Given a source which can be compiled, produce runnable JavaScript." @@ -1458,7 +1468,10 @@ should contain the source for the given namespace name." env/*compiler* (env/default-compiler-env opts)))) ([source opts compiler-env] - (let [path (Paths/get (.toURI (io/file source))) + (let [opts (cond-> opts + (= (:verbose opts :not-found) :not-found) + (assoc :verbose true)) + path (Paths/get (.toURI (io/file source))) fs (.getFileSystem path) service (.newWatchService fs)] (letfn [(buildf [] From fe28e87a960620386494045044f6b76e0374f1bb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 12:52:32 -0400 Subject: [PATCH 0826/4033] CLJS-1087: with-out-str unexpectedly affected by *print-newline* --- src/clj/cljs/core.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 8ea76289d..9cca3cfdc 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1882,7 +1882,8 @@ printing calls." [& body] `(let [sb# (goog.string.StringBuffer.)] - (binding [cljs.core/*print-fn* (fn [x#] (.append sb# x#))] + (binding [cljs.core/*print-newline* true + cljs.core/*print-fn* (fn [x#] (.append sb# x#))] ~@body) (cljs.core/str sb#))) From e752b94b350d457a808a61b4f5d040c2edd94e9b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 14:30:09 -0400 Subject: [PATCH 0827/4033] CLJS-1097: source map url for AOTed cljs.core is wrong Allow supplying custom :source-map-url to cljs.compiler/compile-file* Rewrite source map JSON "file" for core to be the correct value according to build options --- src/clj/cljs/closure.clj | 1 + src/clj/cljs/compiler.clj | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 3f3869977..a58c57d67 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1580,6 +1580,7 @@ should contain the source for the given namespace name." (env/with-compiler-env (env/default-compiler-env) (comp/compile-file src dest {:source-map true + :source-map-url "core.js.map" :output-dir (str "src" File/separator "cljs")}) (ana/write-analysis-cache 'cljs.core cache)))) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 41f5a207f..27f270406 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -15,7 +15,8 @@ [cljs.env :as env] [cljs.tagged-literals :as tags] [cljs.analyzer :as ana] - [cljs.source-map :as sm]) + [cljs.source-map :as sm] + [clojure.data.json :as json]) (:import java.lang.StringBuilder java.io.File)) @@ -989,7 +990,11 @@ (spit dest (slurp cached)) (when (true? (:source-map opts)) (spit (io/file (str dest ".map")) - (slurp (io/resource "cljs/core.aot.js.map")))) + (json/write-str + (assoc + (json/read-str (slurp (io/resource "cljs/core.aot.js.map"))) + "file" + (str (io/file (util/output-directory opts) "cljs" "core.js")))))) (ana/parse-ns src dest nil)) (with-open [out ^java.io.Writer (io/make-writer dest {})] (binding [*out* out @@ -1028,7 +1033,8 @@ {:source-map (:source-map sm-data)}))] (when (and sm-data (= (:optimizations opts) :none)) (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" (.getName sm-file) + (emits "\n//# sourceMappingURL=" + (or (:source-map-url opts) (.getName sm-file)) (if (true? (:source-map-timestamp opts)) (str "?rel=" (System/currentTimeMillis)) "")) From 52b4bb7254b750cb5201fb39a781d0c761749295 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 15:56:19 -0400 Subject: [PATCH 0828/4033] fix cljs.util/debug-prn so it works like println. update compiler activity logging. when :watch supplied to cljs.repl/repl bind *err* to the log writer as well. sleep 50ms before starting the REPL look to allow futures to flush. don't start printing until we enter the REPL loop block --- src/clj/cljs/analyzer.clj | 4 ++-- src/clj/cljs/compiler.clj | 4 ++-- src/clj/cljs/repl.clj | 11 ++++++++--- src/clj/cljs/util.clj | 5 +++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 17c9a3d50..709bc0322 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -2057,7 +2057,7 @@ argument, which the reader will use in any emitted errors." *cljs-file* path reader/*alias-map* (or reader/*alias-map* {})] (when (or *verbose* (:verbose opts)) - (util/debug-prn "Analyzing " res)) + (util/debug-prn "Analyzing" (str res))) (let [env (assoc (empty-env) :build-options opts) ns (loop [ns nil forms (seq (forms-seq res))] (if forms @@ -2079,7 +2079,7 @@ argument, which the reader will use in any emitted errors." :analyze-deps true :load-macros true}))] (when (or *verbose* (:verbose opts)) - (util/debug-prn "Reading analysis cache for " res)) + (util/debug-prn "Reading analysis cache for" (str res))) (swap! env/*compiler* (fn [cenv] (let [cached-ns (edn/read-string (slurp cache))] diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 27f270406..e9a717262 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -978,7 +978,7 @@ (with-core-cljs opts (fn [] (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Compiling " src)) + (util/debug-prn "Compiling" (str src))) (if-let [cached (and (= (:optimizations opts) :none) (= (:ns (ana/parse-ns src)) 'cljs.core) (io/resource "cljs/core.aot.js"))] @@ -986,7 +986,7 @@ ;; with-core-cljs (do (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Using cached cljs.core " src)) + (util/debug-prn "Using cached cljs.core" (str src))) (spit dest (slurp cached)) (when (true? (:source-map opts)) (spit (io/file (str dest ".map")) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 406388353..8bd082851 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -654,7 +654,6 @@ print-no-newline print source-map-inline true} :as opts}] - (print "To quit, type:" :cljs/quit) (let [{:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} (merge @@ -733,11 +732,16 @@ (future (let [log-file (io/file (util/output-directory opts) "watch.log")] (print "Watch compilation log available at:" (str log-file)) + (flush) (try - (binding [*out* (FileWriter. log-file)] - (cljsc/watch src (dissoc opts :watch))) + (let [log-out (FileWriter. log-file)] + (binding [*err* log-out + *out* log-out] + (cljsc/watch src (dissoc opts :watch)))) (catch Throwable e (caught e repl-env opts)))))) + ;; let any setup async messages flush + (Thread/sleep 50) (binding [*in* (if (true? (:source-map-inline opts)) *in* (reader))] @@ -745,6 +749,7 @@ (init) (catch Throwable e (caught e repl-env opts))) + (print "To quit, type:" :cljs/quit) (prompt) (flush) (loop [] diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 1b1e572b3..14f9ff070 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -128,7 +128,8 @@ (defn debug-prn [& args] - (.println System/err (apply str args))) + (binding [*out* *err*] + (apply println args))) (defmacro measure "Like cljs.core/time but toggleable and takes a message string." @@ -138,6 +139,6 @@ `(if ~enable (let [start# (. System (nanoTime)) ret# ~expr] - (debug-prn (str ~msg ", elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs")) + (debug-prn ~msg ", elapsed time:" (/ (double (- (. System (nanoTime)) start#)) 1000000.0) "msecs") ret#) ~expr))) From 65317115f58096b1da1ecadca447c7fb20d8867a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 16:26:21 -0400 Subject: [PATCH 0829/4033] move :init up in cljs.repl/repl --- src/clj/cljs/repl.clj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 8bd082851..aa6babe7b 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -718,6 +718,7 @@ (fn [] (binding [*repl-opts* opts] (try + (init) (when analyze-path (analyze-source analyze-path opts)) (evaluate-form repl-env env "" @@ -745,10 +746,6 @@ (binding [*in* (if (true? (:source-map-inline opts)) *in* (reader))] - (try - (init) - (catch Throwable e - (caught e repl-env opts))) (print "To quit, type:" :cljs/quit) (prompt) (flush) From a97b4c81a7d6b0bedc50e54a358ff92754b6dd21 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 18:37:08 -0400 Subject: [PATCH 0830/4033] need to pass "reload" and "reload-all" to goog.require for browser environment --- src/clj/cljs/compiler.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index e9a717262..0a2282e70 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -873,7 +873,10 @@ (emitln "goog.require('" (munge lib) "');"))) (-> libs meta :reload) - (emitln "goog.require('" (munge lib) "', true);") + (emitln "goog.require('" (munge lib) "', 'reload');") + + (-> libs meta :reload-all) + (emitln "goog.require('" (munge lib) "', 'reload-all');") :else (emitln "goog.require('" (munge lib) "');"))) From 7de018dcf616de9f530df031134941872234d2c9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 18:51:02 -0400 Subject: [PATCH 0831/4033] CLJS-1098: Browser REPL needs to support :reload and :reload-all support "reload" and "reload-all" option to patched goog.require. when given "reload" need to remove visited and written information from goog.dependencies_ for the ns. patched goog.isProvided_ should always return false goog.writeScriptTag_ needs to append scripts to body otherwise will blow away the document `clojure.browser.repl/connect` should return a value to make `defonce` usage sensible --- src/cljs/clojure/browser/repl.cljs | 40 +++++++++++++++++++----------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 90b6e8edd..b4153c6ac 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -15,7 +15,8 @@ :author "Bobby Calderwood and Alex Redington"} clojure.browser.repl - (:require [clojure.browser.net :as net] + (:require [goog.dom :as gdom] + [clojure.browser.net :as net] [clojure.browser.event :as event] ;; repl-connection callback will receive goog.require('cljs.repl') ;; and monkey-patched require expects to be able to derive it @@ -115,18 +116,29 @@ "none"))) ;; Monkey-patch goog.provide if running under optimizations :none - David (when-not js/COMPILED - (set! (.-provide__ js/goog) js/goog.provide) - (set! (.-isProvided___ js/goog) js/goog.isProvided_) - (set! (.-provide js/goog) - (fn [name] - (set! (.-isProvided_ js/goog) (fn [name] false)) - (.provide__ js/goog name) - (set! (.-isProvided_ js/goog) js/goog.isProvided___))) + (set! (.-require__ js/goog) js/goog.require) + ;; suppress useless Google Closure error about duplicate provides + (set! (.-isProvided_ js/goog) (fn [name] false)) (set! (.-writeScriptTag_ js/goog) (fn [src opt_sourceText] - (let [doc js/goog.global.document] - (if (nil? opt_sourceText) - (.write doc - (str "")) - (.write doc - (str ""))))))))) + (.appendChild js/document.body + (as-> (.createElement js/document "script") script + (doto script (aset "type" "text/javascript")) + (if (nil? opt_sourceText) + (doto script (aset "src" src)) + (doto script (gdom/setTextContext opt_sourceText))))))) + (set! (.-require js/goog) + (fn [src reload] + (when (= reload "reload-all") + (set! (.-cljsReloadAll_ js/goog) true)) + (let [reload? (or reload (.-cljsReloadAll__ js/goog))] + (when reload? + (let [path (aget js/goog.dependencies_.nameToPath src)] + (js-delete js/goog.dependencies_.visited path) + (js-delete js/goog.dependencies_.written + (str js/goog.basePath path)))) + (let [ret (.require__ js/goog src)] + (when (= reload "reload-all") + (set! (.-cljsReloadAll_ js/goog) false)) + ret))))) + repl-connection)) From 51780d7c0211d4d074338fad369b8ba37c5a5cec Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 8 Mar 2015 23:49:24 -0400 Subject: [PATCH 0832/4033] include :ua-product in browser REPL :exception value, every browser presents a different stacktrace format, this is a hook for handling stacktrace canonicalization across major browers --- src/cljs/clojure/browser/repl.cljs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index b4153c6ac..4c1797295 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -16,6 +16,7 @@ :author "Bobby Calderwood and Alex Redington"} clojure.browser.repl (:require [goog.dom :as gdom] + [goog.labs.userAgent.browser :as gbrowser] [clojure.browser.net :as net] [clojure.browser.event :as event] ;; repl-connection callback will receive goog.require('cljs.repl') @@ -33,12 +34,25 @@ (defn evaluate-javascript "Process a single block of JavaScript received from the server" [conn block] - (let [result (try {:status :success :value (str (js* "eval(~{block})"))} - (catch :default e - {:status :exception :value (pr-str e) - :stacktrace (if (.hasOwnProperty e "stack") - (.-stack e) - "No stacktrace available.")}))] + (let [result + (try + {:status :success + :value (str (js* "eval(~{block})"))} + (catch :default e + {:status :exception + ;; TODO: latest GCL interface for this is different + ;; see goog.userAgent.product + :ua-product + (cond + (gbrowser/isSafari) :safari + (gbrowser/isChrome) :chrome + (gbrowser/isFirefox) :firefox + (gbrowser/isIE) :ie) + :value (pr-str e) + :stacktrace + (if (.hasOwnProperty e "stack") + (.-stack e) + "No stacktrace available.")}))] (pr-str result))) (defn send-result [connection url data] From b827d9509c763f6c84f1ea4f596290eeacbce5ec Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 07:42:16 -0400 Subject: [PATCH 0833/4033] wrap stacktrace canonicalization to suppress stack parse failures if :repl-verbose, print failed stacktrace canonicalization --- src/clj/cljs/repl.clj | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index aa6babe7b..8cf3051a0 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -353,21 +353,27 @@ (defn- display-error ([repl-env ret form opts] (display-error repl-env ret form (constantly nil) opts)) - ([repl-env ret form f opts] + ([repl-env ret form f {:keys [print flush]}] (f) (when-let [value (:value ret)] - ((:print opts) value)) + (print value)) (when-let [st (:stacktrace ret)] (if (and (true? (:source-map opts)) (satisfies? IParseStacktrace repl-env)) - (let [cst (-parse-stacktrace repl-env st ret opts)] + (let [cst (try + (-parse-stacktrace repl-env st ret opts) + (catch Throwable e + (when (:repl-verbose opts) + (print "Failed to canonicalize stacktrace") + (print (with-out-str (.printStacktrace e *out*))) + (flush))))] (if (vector? cst) (if (satisfies? IPrintStacktrace repl-env) (-print-stacktrace repl-env cst ret opts) (print-mapped-stacktrace cst opts)) - ((:print opts) st))) - ((:print opts) st)) - ((:flush opts))))) + (print st))) + (print st)) + (flush)))) (defn evaluate-form "Evaluate a ClojureScript form in the JavaScript environment. Returns a From 225e51a63a5ebda2f8141adaed9bfce39dc5c4d3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 07:44:32 -0400 Subject: [PATCH 0834/4033] typo --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 8cf3051a0..fd11a29ee 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -353,7 +353,7 @@ (defn- display-error ([repl-env ret form opts] (display-error repl-env ret form (constantly nil) opts)) - ([repl-env ret form f {:keys [print flush]}] + ([repl-env ret form f {:keys [print flush] :as opts}] (f) (when-let [value (:value ret)] (print value)) From b1108366ab8cdd62fef1ca7f7bd7e42ec095f3f4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 07:59:31 -0400 Subject: [PATCH 0835/4033] add samples/repl/repl.clj to simplify browser REPL testing --- samples/repl/repl.clj | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 samples/repl/repl.clj diff --git a/samples/repl/repl.clj b/samples/repl/repl.clj new file mode 100644 index 000000000..0b99d37e0 --- /dev/null +++ b/samples/repl/repl.clj @@ -0,0 +1,9 @@ +(require '[cljs.closure :as cljsc]) +(require '[cljs.repl :as repl]) +(require '[cljs.repl.browser :as brepl]) + +(repl/repl + (brepl/repl-env) + :init (fn [] + (cljsc/build "src" {:output-to "main.js"})) + :repl-verbose true) \ No newline at end of file From fa5e785e581974952ea049afbe96c34ba5d87f57 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 08:36:21 -0400 Subject: [PATCH 0836/4033] just use Guava to get stacktrace as string --- src/clj/cljs/repl.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index fd11a29ee..d3eb5a84d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -28,7 +28,8 @@ [java.net URL] [javax.xml.bind DatatypeConverter] [clojure.lang IExceptionInfo] - [java.util.regex Pattern])) + [java.util.regex Pattern] + [com.google.common.base Throwables])) (def ^:dynamic *cljs-verbose* false) (def ^:dynamic *repl-opts* nil) @@ -365,7 +366,7 @@ (catch Throwable e (when (:repl-verbose opts) (print "Failed to canonicalize stacktrace") - (print (with-out-str (.printStacktrace e *out*))) + (print (Throwables/getStackTraceAsString e)) (flush))))] (if (vector? cst) (if (satisfies? IPrintStacktrace repl-env) From 8632f044c6814fec03ffffd0170f4c7ed57320d4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 08:36:44 -0400 Subject: [PATCH 0837/4033] modernize repl connect in repl sample --- samples/repl/src/repl/test.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/repl/src/repl/test.cljs b/samples/repl/src/repl/test.cljs index e8505761c..0853715fa 100644 --- a/samples/repl/src/repl/test.cljs +++ b/samples/repl/src/repl/test.cljs @@ -11,7 +11,8 @@ ;[clojure.reflect :as reflect] )) -(repl/connect "http://localhost:9000/repl") +(defonce conn + (repl/connect "http://localhost:9000/repl")) (comment From bb55706806a2ef0d3ab3e35cb0c7d32a4c554d52 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 09:05:45 -0400 Subject: [PATCH 0838/4033] Safari stacktrace mapping support in browser REPL --- src/clj/cljs/repl/browser.clj | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 8e00908d2..a920f4854 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -198,10 +198,83 @@ (comp/with-core-cljs nil (fn [] (server/start repl-env)))) +;; ============================================================================= +;; Stracktrace parsing + +(defmulti parse-stacktrace (fn [repl-env st err opts] (:ua-product err))) + +;; ----------------------------------------------------------------------------- +;; Safari Stacktrace + +(defn safari-st-el->frame + "Parses a stack line into a frame representation, returning nil + if parse failed." + [st-el opts] + (let [[function flc] (if (re-find #"@" st-el) + (string/split st-el #"@") + [nil st-el]) + xs (string/split flc #":") + [pre post] + (reduce + (fn [[pre post] [x i]] + (if (<= i 2) + [pre (conj post x)] + [(conj pre x) post])) + [[] []] (map vector xs (range (count xs) 0 -1))) + file (string/join ":" pre) + [line column] (map #(Long/parseLong %) post)] + (if (and file function line column) + {:file (if (re-find #"http://localhost:9000/" file) + (-> file + (string/replace #"http://localhost:9000/" "") + (string/replace (Pattern/compile (str "^" (util/output-directory opts) "/")) "")) + (if-let [asset-root (:asset-root opts)] + (string/replace file asset-root "") + (throw + (ex-info (str "Could not relativize URL " file) + {:type :parse-stacktrace + :reason :relativize-url})))) + :function function + :line line + :column column} + (when-not (string/blank? function) + {:file nil + :function (string/trim function) + :line nil + :column nil})))) + +(comment + (safari-st-el->frame + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17" {}) + ) + +(defmethod parse-stacktrace :safari + [repl-env st err opts] + (->> st + string/split-lines + (take-while #(not (.startsWith % "eval code"))) + (remove string/blank?) + (map #(safari-st-el->frame % opts)) + (remove nil?) + vec)) + +(comment + (parse-stacktrace nil + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17\ncljs$core$first@http://localhost:9000/out/cljs/core.js:4289:22\ncljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5357:39\nhttp://localhost:9000/out/cljs/core.js:16972:92\nhttp://localhost:9000/out/cljs/core.js:16973:3\nhttp://localhost:9000/out/cljs/core.js:10982:133\nsval@http://localhost:9000/out/cljs/core.js:10983:3\ncljs$core$ISeqable$_seq$arity$1@http://localhost:9000/out/cljs/core.js:11074:14\ncljs$core$seq@http://localhost:9000/out/cljs/core.js:4240:44\ncljs$core$pr_sequential_writer@http://localhost:9000/out/cljs/core.js:28707:17\ncljs$core$IPrintWithWriter$_pr_writer$arity$3@http://localhost:9000/out/cljs/core.js:29386:38\ncljs$core$pr_writer_impl@http://localhost:9000/out/cljs/core.js:28912:57\ncljs$core$pr_writer@http://localhost:9000/out/cljs/core.js:29011:32\ncljs$core$pr_seq_writer@http://localhost:9000/out/cljs/core.js:29015:20\ncljs$core$pr_sb_with_opts@http://localhost:9000/out/cljs/core.js:29078:24\ncljs$core$pr_str_with_opts@http://localhost:9000/out/cljs/core.js:29092:48\ncljs$core$pr_str__delegate@http://localhost:9000/out/cljs/core.js:29130:34\ncljs$core$pr_str@http://localhost:9000/out/cljs/core.js:29139:39\n\neval code\neval@[native code]\nhttp://localhost:9000/out/clojure/browser/repl.js:23:271\nclojure$browser$repl$evaluate_javascript@http://localhost:9000/out/clojure/browser/repl.js:26:4\nhttp://localhost:9000/out/clojure/browser/repl.js:121:173\ndeliver@http://localhost:9000/out/goog/messaging/abstractchannel.js:142:21\nxpcDeliver@http://localhost:9000/out/goog/net/xpc/crosspagechannel.js:733:19\nmessageReceived_@http://localhost:9000/out/goog/net/xpc/nativemessagingtransport.js:321:23\nfireListener@http://localhost:9000/out/goog/events/events.js:741:25\nhandleBrowserEvent_@http://localhost:9000/out/goog/events/events.js:862:34\nhttp://localhost:9000/out/goog/events/events.js:276:42" + {:ua-product :safari} + nil) + ) + +;; ============================================================================= +;; BrowserEnv + (defrecord BrowserEnv [] repl/IJavaScriptEnv (-setup [this opts] (setup this opts)) + repl/IParseStacktrace + (-parse-stacktrace [this st err opts] + (parse-stacktrace this st err opts)) (-evaluate [_ _ _ js] (browser-eval js)) (-load [this provides url] (load-javascript this provides url)) From c0dbf944c9eb3792aed7168caacf22260e206635 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 09:09:36 -0400 Subject: [PATCH 0839/4033] remove redundant browser REPL initialization logic now handled by REPL infrastructure --- src/clj/cljs/repl/browser.clj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index a920f4854..35fe33733 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -193,10 +193,7 @@ (swap! loaded-libs (partial apply conj) missing)))) (defn setup [repl-env opts] - (when (:src repl-env) - (repl/analyze-source (:src repl-env))) - (comp/with-core-cljs nil - (fn [] (server/start repl-env)))) + (server/start repl-env)) ;; ============================================================================= ;; Stracktrace parsing From 7e5d99c5021250f0e2252510236bd1d6f278474e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 09:15:33 -0400 Subject: [PATCH 0840/4033] stop blowing away cljs.user in browser REPL so REPL fns/macros remain available --- src/clj/cljs/repl/browser.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 35fe33733..634990e7d 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -126,8 +126,7 @@ (send ordering (fn [_] {:expecting nil :fns {}})) (send-for-eval conn (cljsc/-compile - '[(ns cljs.user) - (set! *print-fn* clojure.browser.repl/repl-print)] {}) + '[(set! *print-fn* clojure.browser.repl/repl-print)] {}) identity)) (defn add-in-order [{:keys [expecting fns]} order f] From 1e54e625cab4e7725f5516b72b85299d22a1eb6d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 10:03:35 -0400 Subject: [PATCH 0841/4033] browser REPL source mapping for Chrome --- src/clj/cljs/repl/browser.clj | 138 ++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 23 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 634990e7d..fd862b618 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -199,37 +199,101 @@ (defmulti parse-stacktrace (fn [repl-env st err opts] (:ua-product err))) +(defn parse-file-line-column [flc] + (let [xs (string/split flc #":") + [pre [line column]] + (reduce + (fn [[pre post] [x i]] + (if (<= i 2) + [pre (conj post x)] + [(conj pre x) post])) + [[] []] (map vector xs (range (count xs) 0 -1))) + file (string/join ":" pre)] + [(cond-> file + (.startsWith file "(") (string/replace "(" "")) + (Long/parseLong + (cond-> line + (.endsWith line ")") (string/replace ")" ""))) + (Long/parseLong + (cond-> column + (.endsWith column ")") (string/replace ")" "")))])) + +(defn parse-file [file opts] + (if (re-find #"http://localhost:9000/" file) + (-> file + (string/replace #"http://localhost:9000/" "") + (string/replace (Pattern/compile (str "^" (util/output-directory opts) "/")) "")) + (if-let [asset-root (:asset-root opts)] + (string/replace file asset-root "") + (throw + (ex-info (str "Could not relativize URL " file) + {:type :parse-stacktrace + :reason :relativize-url}))))) + +;; ----------------------------------------------------------------------------- +;; Chrome Stacktrace + +(defn chrome-st-el->frame + [st-el opts] + (let [xs (-> st-el + (string/replace #"\s+at\s+" "") + (string/split #"\s+")) + [function flc] (if (== (count xs) 1) + [nil (first xs)] + [(first xs) (last xs)]) + [file line column] (parse-file-line-column flc)] + (if (and file function line column) + {:file (parse-file file opts) + :function (string/replace function #"Object\." "") + :line line + :column column} + (when-not (string/blank? function) + {:file nil + :function (string/replace function #"Object\." "") + :line nil + :column nil})))) + +(comment + (chrome-st-el->frame + "\tat cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34)" {}) + ) + +(defmethod parse-stacktrace :chrome + [repl-env st err opts] + (->> st + string/split-lines + (drop 1) ;; drop the error message + (map #(chrome-st-el->frame % opts)) + (remove nil?) + vec)) + +(comment + (parse-stacktrace nil + "\tat Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4258:8) +\tat Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4288:19) +\tat cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34) +\tat http://localhost:9000/out/cljs/core.js:16971:89 +\tat cljs.core.map.cljs$core$map__2 (http://localhost:9000/out/cljs/core.js:16972:3) +\tat http://localhost:9000/out/cljs/core.js:10981:129 +\tat cljs.core.LazySeq.sval (http://localhost:9000/out/cljs/core.js:10982:3) +\tat cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (http://localhost:9000/out/cljs/core.js:11073:10) +\tat Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4239:13) +\tat Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/out/cljs/core.js:28706:14)" + {:ua-product :chrome} + nil) + ) + ;; ----------------------------------------------------------------------------- ;; Safari Stacktrace (defn safari-st-el->frame - "Parses a stack line into a frame representation, returning nil - if parse failed." [st-el opts] (let [[function flc] (if (re-find #"@" st-el) (string/split st-el #"@") [nil st-el]) - xs (string/split flc #":") - [pre post] - (reduce - (fn [[pre post] [x i]] - (if (<= i 2) - [pre (conj post x)] - [(conj pre x) post])) - [[] []] (map vector xs (range (count xs) 0 -1))) - file (string/join ":" pre) - [line column] (map #(Long/parseLong %) post)] + [file line column] (parse-file-line-column flc)] (if (and file function line column) - {:file (if (re-find #"http://localhost:9000/" file) - (-> file - (string/replace #"http://localhost:9000/" "") - (string/replace (Pattern/compile (str "^" (util/output-directory opts) "/")) "")) - (if-let [asset-root (:asset-root opts)] - (string/replace file asset-root "") - (throw - (ex-info (str "Could not relativize URL " file) - {:type :parse-stacktrace - :reason :relativize-url})))) + {:file (parse-file file opts) :function function :line line :column column} @@ -256,7 +320,35 @@ (comment (parse-stacktrace nil - "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17\ncljs$core$first@http://localhost:9000/out/cljs/core.js:4289:22\ncljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5357:39\nhttp://localhost:9000/out/cljs/core.js:16972:92\nhttp://localhost:9000/out/cljs/core.js:16973:3\nhttp://localhost:9000/out/cljs/core.js:10982:133\nsval@http://localhost:9000/out/cljs/core.js:10983:3\ncljs$core$ISeqable$_seq$arity$1@http://localhost:9000/out/cljs/core.js:11074:14\ncljs$core$seq@http://localhost:9000/out/cljs/core.js:4240:44\ncljs$core$pr_sequential_writer@http://localhost:9000/out/cljs/core.js:28707:17\ncljs$core$IPrintWithWriter$_pr_writer$arity$3@http://localhost:9000/out/cljs/core.js:29386:38\ncljs$core$pr_writer_impl@http://localhost:9000/out/cljs/core.js:28912:57\ncljs$core$pr_writer@http://localhost:9000/out/cljs/core.js:29011:32\ncljs$core$pr_seq_writer@http://localhost:9000/out/cljs/core.js:29015:20\ncljs$core$pr_sb_with_opts@http://localhost:9000/out/cljs/core.js:29078:24\ncljs$core$pr_str_with_opts@http://localhost:9000/out/cljs/core.js:29092:48\ncljs$core$pr_str__delegate@http://localhost:9000/out/cljs/core.js:29130:34\ncljs$core$pr_str@http://localhost:9000/out/cljs/core.js:29139:39\n\neval code\neval@[native code]\nhttp://localhost:9000/out/clojure/browser/repl.js:23:271\nclojure$browser$repl$evaluate_javascript@http://localhost:9000/out/clojure/browser/repl.js:26:4\nhttp://localhost:9000/out/clojure/browser/repl.js:121:173\ndeliver@http://localhost:9000/out/goog/messaging/abstractchannel.js:142:21\nxpcDeliver@http://localhost:9000/out/goog/net/xpc/crosspagechannel.js:733:19\nmessageReceived_@http://localhost:9000/out/goog/net/xpc/nativemessagingtransport.js:321:23\nfireListener@http://localhost:9000/out/goog/events/events.js:741:25\nhandleBrowserEvent_@http://localhost:9000/out/goog/events/events.js:862:34\nhttp://localhost:9000/out/goog/events/events.js:276:42" + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17 +cljs$core$first@http://localhost:9000/out/cljs/core.js:4289:22 +cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5357:39 +http://localhost:9000/out/cljs/core.js:16972:92 +http://localhost:9000/out/cljs/core.js:16973:3 +http://localhost:9000/out/cljs/core.js:10982:133 +sval@http://localhost:9000/out/cljs/core.js:10983:3 +cljs$core$ISeqable$_seq$arity$1@http://localhost:9000/out/cljs/core.js:11074:14 +cljs$core$seq@http://localhost:9000/out/cljs/core.js:4240:44 +cljs$core$pr_sequential_writer@http://localhost:9000/out/cljs/core.js:28707:17 +cljs$core$IPrintWithWriter$_pr_writer$arity$3@http://localhost:9000/out/cljs/core.js:29386:38 +cljs$core$pr_writer_impl@http://localhost:9000/out/cljs/core.js:28912:57 +cljs$core$pr_writer@http://localhost:9000/out/cljs/core.js:29011:32 +cljs$core$pr_seq_writer@http://localhost:9000/out/cljs/core.js:29015:20 +cljs$core$pr_sb_with_opts@http://localhost:9000/out/cljs/core.js:29078:24 +cljs$core$pr_str_with_opts@http://localhost:9000/out/cljs/core.js:29092:48 +cljs$core$pr_str__delegate@http://localhost:9000/out/cljs/core.js:29130:34 +cljs$core$pr_str@http://localhost:9000/out/cljs/core.js:29139:39 +eval code +eval@[native code] +http://localhost:9000/out/clojure/browser/repl.js:23:271 +clojure$browser$repl$evaluate_javascript@http://localhost:9000/out/clojure/browser/repl.js:26:4 +http://localhost:9000/out/clojure/browser/repl.js:121:173 +deliver@http://localhost:9000/out/goog/messaging/abstractchannel.js:142:21 +xpcDeliver@http://localhost:9000/out/goog/net/xpc/crosspagechannel.js:733:19 +messageReceived_@http://localhost:9000/out/goog/net/xpc/nativemessagingtransport.js:321:23 +fireListener@http://localhost:9000/out/goog/events/events.js:741:25 +handleBrowserEvent_@http://localhost:9000/out/goog/events/events.js:862:34 +http://localhost:9000/out/goog/events/events.js:276:42" {:ua-product :safari} nil) ) From c9b04e438076bb54395bb32be31d9fa4070468e6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 10:27:43 -0400 Subject: [PATCH 0842/4033] browser REPL source mapping for Firefox --- src/clj/cljs/repl/browser.clj | 97 +++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index fd862b618..e3a540b06 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -199,6 +199,9 @@ (defmulti parse-stacktrace (fn [repl-env st err opts] (:ua-product err))) +(defmethod parse-stacktrace :default + [repl-env st err opts] st) + (defn parse-file-line-column [flc] (let [xs (string/split flc #":") [pre [line column]] @@ -353,6 +356,100 @@ http://localhost:9000/out/goog/events/events.js:276:42" nil) ) +;; ----------------------------------------------------------------------------- +;; Firefox Stacktrace + +(defn firefox-clean-function [f] + (as-> f f + (cond + (string/blank? f) nil + (not= (.indexOf f " f + (string/replace #"<" "") + (string/replace #"\/" "")))) + +(defn firefox-st-el->frame + [st-el opts] + (let [[function flc] (if (re-find #"@" st-el) + (string/split st-el #"@") + [nil st-el]) + [file line column] (parse-file-line-column flc)] + (if (and file function line column) + {:file (parse-file file opts) + :function (firefox-clean-function function) + :line line + :column column} + (when-not (string/blank? function) + {:file nil + :function (firefox-clean-function function) + :line nil + :column nil})))) + +(comment + (firefox-st-el->frame + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4258:8" {}) + + (firefox-st-el->frame + "cljs.core.mapframe + "cljs.core.mapframe + "cljs.core.pr_strframe + "cljs.core.pr_str> st + string/split-lines + (take-while #(= (.indexOf % "> eval") -1)) + (remove string/blank?) + (map #(firefox-st-el->frame % opts)) + (remove nil?) + vec)) + +(comment + (parse-stacktrace nil + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4258:8 +cljs$core$first@http://localhost:9000/out/cljs/core.js:4288:9 +cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5356:24 +cljs.core.map eval:1:25 +@http://localhost:9000/out/clojure/browser/repl.js line 23 > eval:1:2 +clojure$browser$repl$evaluate_javascript/result<@http://localhost:9000/out/clojure/browser/repl.js:23:267 +clojure$browser$repl$evaluate_javascript@http://localhost:9000/out/clojure/browser/repl.js:23:15 +clojure$browser$repl$connect/ Date: Mon, 9 Mar 2015 10:54:54 -0400 Subject: [PATCH 0843/4033] correct doc printing regardless of environment, console.log in browser you don't want newlines, but in the REPL you do --- src/clj/cljs/repl.clj | 35 +++++++++++++++++++---------------- src/cljs/cljs/repl.cljs | 2 +- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index d3eb5a84d..4c9d46ae0 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -895,22 +895,25 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (defmacro doc "Prints documentation for a var or special form given its name" [name] - (if-let [special-name ('{& fn catch try finally try} name)] - `(cljs.repl/print-doc (quote ~(special-doc special-name))) - (cond - (special-doc-map name) - `(cljs.repl/print-doc (quote ~(special-doc name))) - - (ana-api/find-ns name) - `(cljs.repl/print-doc - (quote ~(select-keys (ana-api/find-ns name) [:name :doc]))) - - (ana-api/resolve &env name) - `(cljs.repl/print-doc - (quote ~(update-in - (select-keys (ana-api/resolve &env name) - [:ns :name :doc :forms :arglists :macro :url]) - [:name] clojure.core/name)))))) + `(println + (binding [cljs.core/*print-newline* true] + (with-out-str + ~(if-let [special-name ('{& fn catch try finally try} name)] + `(cljs.repl/print-doc (quote ~(special-doc special-name))) + (cond + (special-doc-map name) + `(cljs.repl/print-doc (quote ~(special-doc name))) + + (ana-api/find-ns name) + `(cljs.repl/print-doc + (quote ~(select-keys (ana-api/find-ns name) [:name :doc]))) + + (ana-api/resolve &env name) + `(cljs.repl/print-doc + (quote ~(update-in + (select-keys (ana-api/resolve &env name) + [:ns :name :doc :forms :arglists :macro :url]) + [:name] clojure.core/name))))))))) (defmacro find-doc "Prints documentation for any var whose documentation or name diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index f80926764..432168148 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -30,4 +30,4 @@ (do (when (:macro m) (println "Macro")) - (println " " (:doc m))))) + (print " " (:doc m))))) From e930883f84e7689677850dfb4d376065169f1719 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 11:17:31 -0400 Subject: [PATCH 0844/4033] print-doc needs a newline, Node.js REPL gets an extra line, oh well --- src/clj/cljs/repl.clj | 2 +- src/cljs/cljs/repl.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 4c9d46ae0..22e094ab8 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -895,7 +895,7 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (defmacro doc "Prints documentation for a var or special form given its name" [name] - `(println + `(print (binding [cljs.core/*print-newline* true] (with-out-str ~(if-let [special-name ('{& fn catch try finally try} name)] diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index 432168148..f80926764 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -30,4 +30,4 @@ (do (when (:macro m) (println "Macro")) - (print " " (:doc m))))) + (println " " (:doc m))))) From 167d09834f6af251a21bbc3a0a97b1231ac8ca2b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Mar 2015 11:54:06 -0400 Subject: [PATCH 0845/4033] Node.js works fine under :optimizations :none now --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index a58c57d67..e81327e2d 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1438,7 +1438,7 @@ should contain the source for the given namespace name." (apply output-unoptimized all-opts js-sources))] ;; emit Node.js bootstrap script for :none & :whitespace optimizations (when (and (= (:target opts) :nodejs) - (#{:none :whitespace} (:optimizations opts))) + (not= (:optimizations opts) :whitespace)) (let [outfile (io/file (util/output-directory opts) "goog" "bootstrap" "nodejs.js")] (util/mkdirs outfile) From 9b0281f10564831e67f2d5e241ee6bc8912c9815 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Mar 2015 14:25:50 -0400 Subject: [PATCH 0846/4033] drop eval frames from Chrome --- src/clj/cljs/repl/browser.clj | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index e3a540b06..f6db7ed12 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -266,6 +266,7 @@ (->> st string/split-lines (drop 1) ;; drop the error message + (take-while #(not (.startsWith % "\tat eval"))) (map #(chrome-st-el->frame % opts)) (remove nil?) vec)) @@ -284,6 +285,20 @@ \tat Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/out/cljs/core.js:28706:14)" {:ua-product :chrome} nil) + + (parse-stacktrace nil + "at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4259:8) +\tat Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4289:19) +\tat cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5357:18) +\tat eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :1:106) +\tat eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :9:3) +\tat eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :14:4) +\tat http://localhost:9000/out/clojure/browser/repl.js:23:267 +\tat clojure$browser$repl$evaluate_javascript (http://localhost:9000/out/clojure/browser/repl.js:26:4) +\tat Object.callback (http://localhost:9000/out/clojure/browser/repl.js:121:169) +\tat goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)" + {:ua-product :chrome} + nil) ) ;; ----------------------------------------------------------------------------- From fd61d528834ca7e6bf64ce6a381e3ca3a2660a7f Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Mar 2015 14:34:43 -0400 Subject: [PATCH 0847/4033] actually drop eval frames from Chrome --- src/clj/cljs/repl/browser.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index f6db7ed12..15c4f0c90 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -266,7 +266,7 @@ (->> st string/split-lines (drop 1) ;; drop the error message - (take-while #(not (.startsWith % "\tat eval"))) + (take-while #(not (.startsWith % " at eval"))) (map #(chrome-st-el->frame % opts)) (remove nil?) vec)) From b5c56b9c61feed289f5697b579b575e3937120fc Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Mar 2015 17:51:47 -0400 Subject: [PATCH 0848/4033] don't pr-str bREPL error just str --- src/cljs/clojure/browser/repl.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 4c1797295..c2a62c5a0 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -48,7 +48,7 @@ (gbrowser/isChrome) :chrome (gbrowser/isFirefox) :firefox (gbrowser/isIE) :ie) - :value (pr-str e) + :value (str e) :stacktrace (if (.hasOwnProperty e "stack") (.-stack e) From cb9cab16010f6155107440b9ed13cc59de37bb35 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Mar 2015 18:39:55 -0400 Subject: [PATCH 0849/4033] fix pst for bREPLs bREPL needs to implement IGetError so that it can eval to get :ua-product just drop any stackframe that starts with "Error" --- src/clj/cljs/repl.clj | 2 +- src/clj/cljs/repl/browser.clj | 56 ++++++++++++++++---------- src/cljs/clojure/browser/repl.cljs | 64 +++++++++++++++--------------- 3 files changed, 69 insertions(+), 53 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 22e094ab8..12f5b6513 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -1014,7 +1014,7 @@ str-or-pattern." (evaluate-form repl-env env "" `(when ~e (pr-str - {:value (.-message ~e) + {:value (str ~e) :stacktrace (.-stack ~e)})))))] (display-error repl-env (if (satisfies? IParseError repl-env) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 15c4f0c90..49453994b 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -10,6 +10,7 @@ (:refer-clojure :exclude [loaded-libs]) (:require [clojure.java.io :as io] [clojure.string :as string] + [clojure.edn :as edn] [cljs.compiler :as comp] [cljs.util :as util] [cljs.env :as env] @@ -265,7 +266,7 @@ [repl-env st err opts] (->> st string/split-lines - (drop 1) ;; drop the error message + (drop-while #(.startsWith % "Error")) (take-while #(not (.startsWith % " at eval"))) (map #(chrome-st-el->frame % opts)) (remove nil?) @@ -273,30 +274,32 @@ (comment (parse-stacktrace nil - "\tat Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4258:8) -\tat Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4288:19) -\tat cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34) -\tat http://localhost:9000/out/cljs/core.js:16971:89 -\tat cljs.core.map.cljs$core$map__2 (http://localhost:9000/out/cljs/core.js:16972:3) -\tat http://localhost:9000/out/cljs/core.js:10981:129 -\tat cljs.core.LazySeq.sval (http://localhost:9000/out/cljs/core.js:10982:3) -\tat cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (http://localhost:9000/out/cljs/core.js:11073:10) -\tat Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4239:13) -\tat Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/out/cljs/core.js:28706:14)" + "Error: 1 is not ISeqable + at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4258:8) + at Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4288:19) + at cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34) + at http://localhost:9000/out/cljs/core.js:16971:89 + at cljs.core.map.cljs$core$map__2 (http://localhost:9000/out/cljs/core.js:16972:3) + at http://localhost:9000/out/cljs/core.js:10981:129 + at cljs.core.LazySeq.sval (http://localhost:9000/out/cljs/core.js:10982:3) + at cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (http://localhost:9000/out/cljs/core.js:11073:10) + at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4239:13) + at Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/out/cljs/core.js:28706:14)" {:ua-product :chrome} nil) (parse-stacktrace nil - "at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4259:8) -\tat Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4289:19) -\tat cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5357:18) -\tat eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :1:106) -\tat eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :9:3) -\tat eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :14:4) -\tat http://localhost:9000/out/clojure/browser/repl.js:23:267 -\tat clojure$browser$repl$evaluate_javascript (http://localhost:9000/out/clojure/browser/repl.js:26:4) -\tat Object.callback (http://localhost:9000/out/clojure/browser/repl.js:121:169) -\tat goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)" + "Error: 1 is not ISeqable + at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4259:8) + at Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4289:19) + at cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5357:18) + at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :1:106) + at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :9:3) + at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :14:4) + at http://localhost:9000/out/clojure/browser/repl.js:23:267 + at clojure$browser$repl$evaluate_javascript (http://localhost:9000/out/clojure/browser/repl.js:26:4) + at Object.callback (http://localhost:9000/out/clojure/browser/repl.js:121:169) + at goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)" {:ua-product :chrome} nil) ) @@ -330,6 +333,7 @@ [repl-env st err opts] (->> st string/split-lines + (drop-while #(.startsWith % "Error")) (take-while #(not (.startsWith % "eval code"))) (remove string/blank?) (map #(safari-st-el->frame % opts)) @@ -424,6 +428,7 @@ http://localhost:9000/out/goog/events/events.js:276:42" [repl-env st err opts] (->> st string/split-lines + (drop-while #(.startsWith % "Error")) (take-while #(= (.indexOf % "> eval") -1)) (remove string/blank?) (map #(firefox-st-el->frame % opts)) @@ -475,6 +480,15 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" repl/IParseStacktrace (-parse-stacktrace [this st err opts] (parse-stacktrace this st err opts)) + repl/IGetError + (-get-error [this e env opts] + (edn/read-string + (repl/evaluate-form this env "" + `(when ~e + (pr-str + {:ua-product (clojure.browser.repl/get-ua-product) + :value (str ~e) + :stacktrace (.-stack ~e)}))))) (-evaluate [_ _ _ js] (browser-eval js)) (-load [this provides url] (load-javascript this provides url)) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index c2a62c5a0..123511687 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -31,6 +31,15 @@ (if-let [conn @xpc-connection] (net/transmit conn :print (pr-str data)))) +;; TODO: latest GCL interface for this is different +;; see goog.userAgent.product +(defn get-ua-product [] + (cond + (gbrowser/isSafari) :safari + (gbrowser/isChrome) :chrome + (gbrowser/isFirefox) :firefox + (gbrowser/isIE) :ie)) + (defn evaluate-javascript "Process a single block of JavaScript received from the server" [conn block] @@ -40,14 +49,7 @@ :value (str (js* "eval(~{block})"))} (catch :default e {:status :exception - ;; TODO: latest GCL interface for this is different - ;; see goog.userAgent.product - :ua-product - (cond - (gbrowser/isSafari) :safari - (gbrowser/isChrome) :chrome - (gbrowser/isFirefox) :firefox - (gbrowser/isIE) :ie) + :ua-product (get-ua-product) :value (str e) :stacktrace (if (.hasOwnProperty e "stack") @@ -62,15 +64,15 @@ "Send data to be printed in the REPL. If there is an error, try again up to 10 times." ([url data] - (send-print url data 0)) + (send-print url data 0)) ([url data n] - (let [conn (net/xhr-connection)] - (event/listen conn :error - (fn [_] - (if (< n 10) - (send-print url data (inc n)) - (.log js/console (str "Could not send " data " after " n " attempts."))))) - (net/transmit conn url "POST" data nil 0)))) + (let [conn (net/xhr-connection)] + (event/listen conn :error + (fn [_] + (if (< n 10) + (send-print url data (inc n)) + (.log js/console (str "Could not send " data " after " n " attempts."))))) + (net/transmit conn url "POST" data nil 0)))) (def order (atom 0)) @@ -83,26 +85,26 @@ (if-let [repl-connection (net/xpc-connection)] (let [connection (net/xhr-connection)] (event/listen connection - :success - (fn [e] - (net/transmit - repl-connection - :evaluate-javascript - (.getResponseText (.-currentTarget e) - ())))) + :success + (fn [e] + (net/transmit + repl-connection + :evaluate-javascript + (.getResponseText (.-currentTarget e) + ())))) (net/register-service repl-connection - :send-result - (fn [data] - (send-result connection url (wrap-message :result data)))) + :send-result + (fn [data] + (send-result connection url (wrap-message :result data)))) (net/register-service repl-connection - :print - (fn [data] - (send-print url (wrap-message :print data)))) - + :print + (fn [data] + (send-print url (wrap-message :print data)))) + (net/connect repl-connection - (constantly nil)) + (constantly nil)) (js/setTimeout #(send-result connection url (wrap-message :ready "ready")) 50)) (js/alert "No 'xpc' param provided to child iframe."))) From 63da50e658837f286fd5f896ef4d55537769e151 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Mar 2015 19:03:25 -0400 Subject: [PATCH 0850/4033] add REPL special functions docmap --- src/clj/cljs/repl.clj | 72 +++++++++++++++++++++++++++++++++++++++++ src/cljs/cljs/repl.cljs | 7 ++-- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 12f5b6513..ee7ad7471 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -892,6 +892,75 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) :name name-symbol :special-form true)) +(def repl-special-doc-map + '{in-ns {:arglists ([name]) + :doc "Sets *cljs-ns* to the namespace named by the symbol, creating it if needed."} + require {:arglists ([& args]) + :doc " Loads libs, skipping any that are already loaded. Each argument is + either a libspec that identifies a lib or a flag that modifies how all the identified + libs are loaded. Use :require in the ns macro in preference to calling this + directly. + + Libs + + A 'lib' is a named set of resources in classpath whose contents define a + library of ClojureScript code. Lib names are symbols and each lib is associated + with a ClojureScript namespace. A lib's name also locates its root directory + within classpath using Java's package name to classpath-relative path mapping. + All resources in a lib should be contained in the directory structure under its + root directory. All definitions a lib makes should be in its associated namespace. + + 'require loads a lib by loading its root resource. The root resource path + is derived from the lib name in the following manner: + Consider a lib named by the symbol 'x.y.z; it has the root directory + /x/y/, and its root resource is /x/y/z.clj. The root + resource should contain code to create the lib's namespace (usually by using + the ns macro) and load any additional lib resources. + + Libspecs + + A libspec is a lib name or a vector containing a lib name followed by + options expressed as sequential keywords and arguments. + + Recognized options: + :as takes a symbol as its argument and makes that symbol an alias to the + lib's namespace in the current namespace. + :refer takes a list of symbols to refer from the namespace.. + :refer-macros takes a list of macro symbols to refer from the namespace. + :include-macros takes a list of macro symbols to refer from the namespace. + + Flags + + A flag is a keyword. + Recognized flags: :reload, :reload-all, :verbose + :reload forces loading of all the identified libs even if they are + already loaded + :reload-all implies :reload and also forces loading of all libs that the + identified libs directly or indirectly load via require or use + :verbose triggers printing information about each load, alias, and refer + + Example: + + The following would load the library clojure.string :as string. + + (require '[clojure/string :as string])"} + require-macros {:arglists ([& args]) + :doc "Similar to the require REPL special function but + only for macros."} + import {:arglists ([& import-symbols-or-lists]) + :doc "import-list => (closure-namespace constructor-name-symbols*) + + For each name in constructor-name-symbols, adds a mapping from name to the + constructor named by closure-namespace to the current namespace. Use :import in the ns + macro in preference to calling this directly."} + load-file {:arglist ([name]) + :doc "Sequentially read and evaluate the set of forms contained in the file."}}) + +(defn- repl-special-doc [name-symbol] + (assoc (repl-special-doc-map name-symbol) + :name name-symbol + :repl-special-function true)) + (defmacro doc "Prints documentation for a var or special form given its name" [name] @@ -904,6 +973,9 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (special-doc-map name) `(cljs.repl/print-doc (quote ~(special-doc name))) + (repl-special-doc-map name) + `(cljs.repl/print-doc (quote ~(repl-special-doc name))) + (ana-api/find-ns name) `(cljs.repl/print-doc (quote ~(select-keys (ana-api/find-ns name) [:name :doc]))) diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index f80926764..4ecc55c49 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -15,7 +15,8 @@ (cond (:forms m) (doseq [f (:forms m)] (println " " f)) - (:arglists m) (if (:macro m) + (:arglists m) (if (or (:macro m) + (:repl-special-function m)) (prn (:arglists m)) (prn (second (:arglists m))))) (if (:special-form m) @@ -29,5 +30,7 @@ (:name m))))) (do (when (:macro m) - (println "Macro")) + (println "Macro")) + (when (:repl-special-function m) + (println "REPL Special Function")) (println " " (:doc m))))) From 4bb2d7deaa91bf5150f1e37767104af82088b3e9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 9 Mar 2015 19:26:05 -0400 Subject: [PATCH 0851/4033] 0.0-3058 --- README.md | 6 +++--- changes.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 805d7f53c..71f917d90 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-2913 +Latest stable release: 0.0-3058 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-2913"] +[org.clojure/clojurescript "0.0-3058"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-2913 org.clojure clojurescript - 0.0-2913 + 0.0-3058 ``` diff --git a/changes.md b/changes.md index b7f657272..f196c24a2 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,61 @@ +## 0.0-3058 + +### Enhancements +* browser REPL source mapping for Firefox, Safari, Chrome +* macro support in REPL special functions +* CLJS-897: AOT core.cljs CLJS-899: AOT cache core.cljs analysis +* CLJS-1078: Nashorn REPL should use persistent code cache +* CLJS-1079: add way to execute arbitrary fn upon watch build completion +* CLJS-1034: Support REPL-defined functions in stacktrace infrastructure +* source mapping for Rhino +* CLJS-1071: support symbol keys in :closure-defines +* CLJS-1014: Support Closure Defines under :none +* CLJS-1068: node target define +* CLJS-1069: Generic :jsdoc support +* CLJS-1030: add `cljs.repl/pst` +* add `cljs.repl/apropos`, `cljs.repl/find-doc`, `cljs.repl/dir` +* fix `cljs.analyzer.api/all-ns` docstring +* add `cljs.analyzer.api/ns-publics` +* CLJS-1055: cljs.repl/doc should support namespaces and special forms +* Add ClojureScript special form doc map +* CLJS-1054: add clojure.repl/source functionality to cljs.repl +* CLJS-1053: REPLs need import special fn + +### Changes +* move :init up in cljs.repl/repl +* CLJS-1087: with-out-str unexpectedly affected by *print-newline* +* CLJS-1093: Better compiler defaults +* Bump deps latest Closure Compiler, Rhino 1.7R5, data.json 0.2.6, tool.reader 0.8.16 +* more sensible error if cljs.repl/repl arguments after the first incorrectly supplied +* default REPLs to :cache-analysis true +* default :output-dir for Nashorn and Node REPLs +* change ES6 Map `get` support to take additional `not-found` parameter +* deprecate clojure.reflect namespace now that REPLs are significantly enhanced, static vars, etc. + +### Fixes +* stop blowing away cljs.user in browser REPL so REPL fns/macros remain available +* CLJS-1098: Browser REPL needs to support :reload and :reload-all +* CLJS-1097: source map url for AOTed cljs.core is wrong +* CLJS-1094: read option not used by cljs.repl/repl* +* CLJS-1089: AOT analysis cache has bad :file paths +* CLJS-1057: Nashorn REPL should not use EDN rep for errors +* CLJS-1086: Keyword constants should have stable names +* CLJS-964: Redefining exists? does not emit a warning like redefining array? does. +* CLJS-937: local fn name should be lexically munged +* CLJS-1082: analysis memoization bug +* CLJS-978: Analysis caching doesn't account for constants table +* CLJS-865: remove `cljs.js-deps/goog-resource` hack +* CLJS-1077: analyze-deps infinite recursive loop +* manually set *e in Rhino on JS exception +* REPL options merging needs to be more disciplined +* CLJS-1072: Calling .hasOwnProperty("source") in Clojurescript's string/replace will break with ES6 +* CLJS-1064: ex-info is not printable +* Fix REPLs emitting code into .repl directory +* CLJS-1066: Rhino REPL regression +* be more disciplined about ints in murmur3 code +* Node.js REPL should work even if :output-dir not supplied +* Nashorn environment doesn't supply console, setup printing correctly + ## 0.0-2913 * Support custom :output-to for :cljs-base module From a79bf61d6d9e34d738777caa1ea6bf93e7ba052f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Mar 2015 06:54:33 -0400 Subject: [PATCH 0852/4033] cljs.compiler/compile-file* cljs.analyzer/cache-file needs to set :write --- src/clj/cljs/compiler.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 0a2282e70..c860c662f 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1052,7 +1052,7 @@ (let [{:keys [output-dir cache-analysis]} opts] (when (and (true? cache-analysis) output-dir) (ana/write-analysis-cache ns-name - (ana/cache-file src output-dir))) + (ana/cache-file src output-dir :write))) ret)))))))))))) (defn requires-compilation? From 87f26583be0994d75910b4a24b759515e47c614d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Mar 2015 07:18:06 -0400 Subject: [PATCH 0853/4033] add docstring to cljs.core/*target* --- src/cljs/cljs/core.cljs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 54edb7b92..70891f608 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -17,8 +17,12 @@ (def *unchecked-if* false) -(def ^{:dynamic true - :jsdoc ["@define {string}"]} +(def + ^{:dynamic true + :doc "Var bound to the name value of the compiler build :target + option. If compiler build :target is :nodejs, *target* will be bound to + \"nodejs\"." + :jsdoc ["@define {string}"]} *target* "default") (defonce From 57fb9ef28c2947fb7a2a02920ab12645f01ce4f7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Mar 2015 07:18:43 -0400 Subject: [PATCH 0854/4033] fix compile-file* cache-file call --- src/clj/cljs/analyzer.clj | 1 + src/clj/cljs/compiler.clj | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 709bc0322..37862ba80 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1977,6 +1977,7 @@ argument, which the reader will use in any emitted errors." ([src output-dir] (cache-file src (parse-ns src) output-dir)) ([src ns-info output-dir] (cache-file src (parse-ns src) output-dir :read)) ([src ns-info output-dir mode] + {:pre [(map? ns-info)]} (if-let [core-cache (and (= mode :read) (= (:ns ns-info) 'cljs.core) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index c860c662f..814244446 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1052,7 +1052,7 @@ (let [{:keys [output-dir cache-analysis]} opts] (when (and (true? cache-analysis) output-dir) (ana/write-analysis-cache ns-name - (ana/cache-file src output-dir :write))) + (ana/cache-file src (ana/parse-ns src) output-dir :write))) ret)))))))))))) (defn requires-compilation? From 4952cd84d77c4cc6e4e4753d4601c7b3a87f7043 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Mar 2015 07:45:50 -0400 Subject: [PATCH 0855/4033] tweak cljs.core/*target* docstring --- src/cljs/cljs/core.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 70891f608..5655e267c 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -19,9 +19,10 @@ (def ^{:dynamic true - :doc "Var bound to the name value of the compiler build :target - option. If compiler build :target is :nodejs, *target* will be bound to - \"nodejs\"." + :doc "Var bound to the name value of the compiler build :target option. + For example, if the compiler build :target is :nodejs, *target* will be bound + to \"nodejs\". *target* is a Google Closure define and can be set by compiler + :closure-defines option." :jsdoc ["@define {string}"]} *target* "default") From 525f08bff10db57594c3f84b0570ff4b0a69efd1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Mar 2015 07:56:42 -0400 Subject: [PATCH 0856/4033] always remove AOTed files after build or uberjar --- script/build | 4 ++++ script/uberjar | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/script/build b/script/build index 0527d59a7..f792bd2f2 100755 --- a/script/build +++ b/script/build @@ -62,3 +62,7 @@ else echo "Skipping remote deployment and Git tag because we are not on Hudson." mvn clean install fi + +rm -f src/cljs/cljs/core.aot.js +rm -f src/cljs/cljs/core.aot.js.map +rm -f src/cljs/cljs/core.cljs.cache.aot.edn diff --git a/script/uberjar b/script/uberjar index 8573621e4..08c5b5802 100755 --- a/script/uberjar +++ b/script/uberjar @@ -42,4 +42,8 @@ sed -e "s/0.0-0000/0.0-$REVISION/" src/cljs/cljs/core.cljs.cache.aot.edn > $AOT_ mv $AOT_CACHE_FILE src/cljs/cljs/core.cljs.cache.aot.edn lein uberjar clojure.main -mv target/clojurescript-0.0-SNAPSHOT-standalone.jar target/cljs.jar \ No newline at end of file +mv target/clojurescript-0.0-SNAPSHOT-standalone.jar target/cljs.jar + +rm -f src/cljs/cljs/core.aot.js +rm -f src/cljs/cljs/core.aot.js.map +rm -f src/cljs/cljs/core.cljs.cache.aot.edn \ No newline at end of file From b7a583fa6991bb5a706cb584dee89c402c8968f5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Mar 2015 08:29:16 -0400 Subject: [PATCH 0857/4033] missing docstrings --- src/cljs/cljs/core.cljs | 64 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 5655e267c..d942e3e41 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -38,13 +38,63 @@ "Set *print-fn* to f." [f] (set! *print-fn* f)) -(def ^:dynamic *flush-on-newline* true) -(def ^:dynamic *print-newline* true) -(def ^:dynamic *print-readably* true) -(def ^:dynamic *print-meta* false) -(def ^:dynamic *print-dup* false) -(def ^:dynamic *print-length* nil) -(def ^:dynamic *print-level* nil) +(def + ^{:dynamic true + :doc "When set to true, output will be flushed whenever a newline is printed. + + Defaults to true."} + *flush-on-newline* true) + +(def + ^{:dynamic true + :doc "When set to logical false will drop newlines from printing calls. + This is to work around the implicit newlines emitted by standard JavaScript + console objects."} + *print-newline* true) + +(def + ^{:dynamic true + :doc "When set to logical false, strings and characters will be printed with + non-alphanumeric characters converted to the appropriate escape sequences. + + Defaults to true"} + *print-readably* true) + +(def + ^{:dynamic true + :doc "If set to logical true, when printing an object, its metadata will also + be printed in a form that can be read back by the reader. + + Defaults to false."} + *print-meta* false) + +(def + ^{:dynamic true + :doc "When set to logical true, objects will be printed in a way that preserves + their type when read in later. + + Defaults to false."} + *print-dup* false) + +(def + ^{:dynamic true + :doc "When set to logical true, objects will be printed in a way that preserves + their type when read in later. + + Defaults to false."} + *print-length* nil) + +(def + ^{:dynamic true + :doc "*print-level* controls how many levels deep the printer will + print nested objects. If it is bound to logical false, there is no + limit. Otherwise, it must be bound to an integer indicating the maximum + level to print. Each argument to print is at level 0; if an argument is a + collection, its items are at level 1; and so on. If an object is a + collection and is at a level greater than or equal to the value bound to + *print-level*, the printer prints '#' to represent it. The root binding + is nil indicating no limit."} + *print-level* nil) (defonce ^:dynamic *loaded-libs* nil) From 5d87ce3c01005170b878733b4b7f7abfd6f57964 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Mar 2015 14:56:36 -0400 Subject: [PATCH 0858/4033] Remove misleading 1.5 profile from project.clj --- project.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/project.clj b/project.clj index a5aea223e..708822a96 100644 --- a/project.clj +++ b/project.clj @@ -14,8 +14,7 @@ [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R5"]] - :profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} - :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} + :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all}} :aliases {"test-all" ["with-profile" "test,1.5:test,1.6" "test"] "check-all" ["with-profile" "1.5:1.6" "check"]} From 4bcc95175e4282d43ed74bf0abd670550fc40140 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 11 Mar 2015 13:31:53 +0100 Subject: [PATCH 0859/4033] Keyword constant name must handle dash and underscore correctly --- src/clj/cljs/analyzer.clj | 7 +++---- test/clj/cljs/analyzer_tests.clj | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 37862ba80..0c19754ee 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -197,15 +197,14 @@ `(binding [*cljs-warning-handlers* ~handlers] ~@body)) -(def ^:private constant-counter (atom 0)) - (defn gen-constant-id [value] (let [prefix (cond (keyword? value) "constant$keyword$" :else (throw - (Exception. (str "constant type " (type value) " not supported"))))] - (symbol (str prefix (string/replace (munge value) "." "$"))))) + (Exception. (str "constant type " (type value) " not supported")))) + name (-> value (str) (subs 1) (string/replace "-" "_DASH_") (munge) (string/replace "." "$"))] + (symbol (str prefix name)))) (defn- register-constant! ([val] (register-constant! nil val)) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index a760b9d26..b69595bd6 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -324,3 +324,8 @@ ;; actual: (not (= #{file-reloading dev client} #{file-reloading dev client core})) (is (= (set (a/ns-dependents 'utils)) #{'file-reloading 'dev 'client 'core})))) + +(deftest test-cljs-1105 + ;; munge turns - into _, must preserve the dash first + (is (not= (a/gen-constant-id :test-kw) + (a/gen-constant-id :test_kw)))) \ No newline at end of file From 8cf3a53a8c4f7f8a40fcfbdfe4620b870f738de2 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Mon, 9 Mar 2015 18:03:40 +0600 Subject: [PATCH 0860/4033] CLJS-1102 clojure.test should print column number of exception when available --- src/cljs/cljs/test.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index ead14276a..161da1666 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -281,10 +281,10 @@ in *testing-vars* as a list, then the source file and line of current assertion." [m] - (let [{:keys [file line]} m] + (let [{:keys [file line column]} m] (str (reverse (map #(:name (meta %)) (:testing-vars (get-current-env)))) - " (" file ":" line ")"))) + " (" file ":" line (when column (str ":" column)) ")"))) (defn testing-contexts-str "Returns a string representation of the current test context. Joins From 571cbff4a959145414064198e13d2569e3fe840b Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Sun, 8 Mar 2015 19:19:49 +0600 Subject: [PATCH 0861/4033] CLJS-1096: Update js/Date -equiv and -compare semantics based on Date.valueOf() value --- src/cljs/cljs/core.cljs | 6 +++++- test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index d942e3e41..787b067c3 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -957,7 +957,11 @@ IEquiv (-equiv [o other] (and (instance? js/Date other) - (identical? (.toString o) (.toString other))))) + (== (.valueOf o) (.valueOf other)))) + + IComparable + (-compare [this other] + (garray/defaultCompare (.valueOf this) (.valueOf other)))) (extend-type number IEquiv diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index efb20c559..d7d3ae41e 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1673,6 +1673,13 @@ (is (= -1 (compare (subvec [1 2 3] 1) (subvec [1 2 4] 1)))) (is (= 0 (compare (subvec [1 2 3] 1) (subvec [1 2 3] 1)))) (is (= 1 (compare (subvec [1 2 4] 1) (subvec [1 2 3] 1))))) + + (is (= 0 (compare (js/Date. 2015 2 8 19 13 00 999) + (js/Date. 2015 2 8 19 13 00 999)))) + (is (= -1 (compare (js/Date. 2015 2 8 19 12 00 999) + (js/Date. 2015 2 8 19 13 00 999)))) + (is (= 1 (compare (js/Date. 2015 2 8 19 14 00 999) + (js/Date. 2015 2 8 19 13 00 999)))) ) (deftest test-dot From 2bfe320ae3b46546a31144ca80103e7617aeba4e Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Sun, 8 Mar 2015 18:57:32 +0600 Subject: [PATCH 0862/4033] CLJS-1095: UUID to implement IComparable --- src/cljs/cljs/core.cljs | 6 +++++- test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 787b067c3..02e255c3b 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -9331,7 +9331,11 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." IHash (-hash [this] - (goog.string/hashCode (pr-str this)))) + (goog.string/hashCode (pr-str this))) + + IComparable + (-compare [_ other] + (garray/defaultCompare uuid (.-uuid other)))) ;;; ExceptionInfo diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index d7d3ae41e..cdcf6353f 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1629,6 +1629,12 @@ (get {(UUID. "550e8400-e29b-41d4-a716-446655440000") 42} (UUID. "666e8400-e29b-41d4-a716-446655440000") :not-at-all-found))) + (is (= -1 (compare (UUID. "550e8400-e29b-41d4-a716-446655440000") + (UUID. "666e8400-e29b-41d4-a716-446655440000")))) + (is (= 1 (compare (UUID. "550e8400-e29b-41d4-a716-446655440000") + (UUID. "550e8400-a29b-41d4-a716-446655440000")))) + (is (= 0 (compare (UUID. "550e8400-e29b-41d4-a716-446655440000") + (UUID. "550e8400-e29b-41d4-a716-446655440000")))) )) (deftest test-comparable From 4b80a318c768fe604a7f1e8cb22b47688ed8a13c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Mar 2015 16:28:12 -0400 Subject: [PATCH 0863/4033] CLJS-1108: :modules :output-to needs to create directories --- src/clj/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index e81327e2d..5fbb84986 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1032,8 +1032,10 @@ should contain the source for the given namespace name." (let [fdeps-str (when-not (empty? foreign-deps) (foreign-deps-str opts foreign-deps)) sm-name (when (:source-map opts) - (str output-to ".map"))] - (spit (io/file output-to) + (str output-to ".map")) + out-file (io/file output-to)] + (util/mkdirs out-file) + (spit out-file (as-> source source (if (= name :cljs-base) (add-header opts source) From 70e73c5d7cc38904f11b9c121eaf632a6589c25f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Mar 2015 10:57:21 -0400 Subject: [PATCH 0864/4033] CLJS-1090: macros imported from clojure.core missing docs Need to copy over additional metadata from original var. --- src/clj/cljs/core.clj | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 9cca3cfdc..14497d6e5 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -47,14 +47,30 @@ (defmacro import-macros [ns [& vars]] (core/let [ns (find-ns ns) vars (map #(ns-resolve ns %) vars) - syms (map (core/fn [^clojure.lang.Var v] (core/-> v .sym (with-meta {:macro true}))) vars) - defs (map (core/fn [sym var] - `(do (def ~sym (deref ~var)) - ;for AOT compilation - (alter-meta! (var ~sym) assoc :macro true))) - syms vars)] - `(do ~@defs - :imported))) + syms (map + (core/fn [^clojure.lang.Var v] + (core/-> v .sym + (with-meta + (merge + {:macro true} + (update-in (select-keys (meta v) [:arglists :doc :file :line]) + [:arglists] (core/fn [arglists] `(quote ~arglists))))))) + vars) + defs (map + (core/fn [sym var] + (core/let [{:keys [arglists doc file line]} (meta sym)] + `(do + (def ~sym (deref ~var)) + ;for AOT compilation + (alter-meta! (var ~sym) assoc + :macro true + :arglists ~arglists + :doc ~doc + :file ~file + :line ~line)))) + syms vars)] + `(do ~@defs + :imported))) (import-macros clojure.core [-> ->> .. assert comment cond From 4ddd8780499471cf1066d43afa0036917a42bb84 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Thu, 12 Mar 2015 13:19:40 +0600 Subject: [PATCH 0865/4033] CLJS-1101 cljs.test might throw when trying to detect file-and-line --- src/cljs/cljs/test.cljs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 161da1666..d679baa19 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -372,19 +372,18 @@ default))) (defn file-and-line [exception depth] - (let [stack (.-stack exception)] - (if (and stack (string? stack)) - ;; TODO: flesh out - (let [stacktrace - (vec (map string/trim - (string/split stack #"\n"))) - stack-element (nth stacktrace depth) - fname (js-filename stack-element) - [line column] (js-line-and-column stack-element) - [fname line column] (mapped-line-and-column fname line column)] - {:file fname :line line :column column}) - {:file (.-fileName exception) - :line (.-lineNumber exception)})) ) + ;; TODO: flesh out + (if-let [stack-element (and (string? (.-stack exception)) + (some-> (.-stack exception) + string/split-lines + (get depth) + string/trim))] + (let [fname (js-filename stack-element) + [line column] (js-line-and-column stack-element) + [fname line column] (mapped-line-and-column fname line column)] + {:file fname :line line :column column}) + {:file (.-fileName exception) + :line (.-lineNumber exception)})) (defn do-report [m] (let [m (case (:type m) From 4e9b619096c570c044158f7cb5db927e9868ee95 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Mar 2015 15:15:59 -0400 Subject: [PATCH 0866/4033] CLJS-1110: cljs.closure/watch needs to print errors to *err* --- src/clj/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 5fbb84986..a7f92b661 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -57,7 +57,8 @@ [javax.xml.bind DatatypeConverter] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult] - [com.sun.nio.file SensitivityWatchEventModifier])) + [com.sun.nio.file SensitivityWatchEventModifier] + [com.google.common.base Throwables])) (def name-chars (map char (concat (range 48 57) (range 65 90) (range 97 122)))) @@ -1486,7 +1487,8 @@ should contain the source for the given namespace name." (when-let [f (:watch-fn opts)] (f)) (catch Throwable e - (.printStackTrace e)))) + (binding [*out* *err*] + (println (Throwables/getStackTraceAsString e)))))) (watch-all [^Path root] (Files/walkFileTree root (reify From 9c80de83636a5f879687ecbe306e7a5a6eccce7b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Mar 2015 16:05:39 -0400 Subject: [PATCH 0867/4033] CLJS-1111: browser REPL should have no side effects until -setup refactor bREPL so all side-effecting setup logic is in -setup. remove useless loaded library tracking logic --- src/clj/cljs/repl/browser.clj | 140 +++++++++++----------------------- 1 file changed, 43 insertions(+), 97 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 49453994b..fe225cb59 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -23,10 +23,6 @@ (atom {:return-value-fn nil :client-js nil})) -(def loaded-libs (atom #{})) - -(def preloaded-libs (atom #{})) - (defn- set-return-value-fn "Save the return value function which will be called when the next return value is received." @@ -123,7 +119,6 @@ (def ordering (agent {:expecting nil :fns {}})) (defmethod handle-post :ready [_ conn _] - (reset! loaded-libs @preloaded-libs) (send ordering (fn [_] {:expecting nil :fns {}})) (send-for-eval conn (cljsc/-compile @@ -187,13 +182,7 @@ if any of the namespaces have not already been loaded from the ClojureScript REPL." [repl-env provides url] - (let [missing (remove #(contains? @loaded-libs %) provides)] - (when (seq missing) - (browser-eval (slurp url)) - (swap! loaded-libs (partial apply conj) missing)))) - -(defn setup [repl-env opts] - (server/start repl-env)) + (browser-eval (slurp url))) ;; ============================================================================= ;; Stracktrace parsing @@ -473,6 +462,36 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" ;; ============================================================================= ;; BrowserEnv +(defn compile-client-js [opts] + (cljsc/build + '[(ns clojure.browser.repl.client + (:require [goog.events :as event] + [clojure.browser.repl :as repl])) + (defn start [url] + (event/listen js/window + "load" + (fn [] + (repl/start-evaluator url))))] + {:optimizations (:optimizations opts) + :output-dir (:working-dir opts)})) + +(defn create-client-js-file [opts file-path] + (let [file (io/file file-path)] + (when (not (.exists file)) + (spit file (compile-client-js opts))) + file)) + +(defn setup [{:keys [working-dir] :as repl-env} opts] + (println "Compiling client js ...") + (swap! browser-state + (fn [old] + (assoc old :client-js + (create-client-js-file + repl-env (io/file working-dir "client.js"))))) + (println "Waiting for browser to connect ...") + opts + (server/start repl-env)) + (defrecord BrowserEnv [] repl/IJavaScriptEnv (-setup [this opts] @@ -497,88 +516,18 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" (reset! server/state {}) (reset! browser-state {}))) -(defn compile-client-js [opts] - (cljsc/build - '[(ns clojure.browser.repl.client - (:require [goog.events :as event] - [clojure.browser.repl :as repl])) - (defn start [url] - (event/listen js/window - "load" - (fn [] - (repl/start-evaluator url))))] - {:optimizations (:optimizations opts) - :output-dir (:working-dir opts)})) - -(defn create-client-js-file [opts file-path] - (let [file (io/file file-path)] - (when (not (.exists file)) - (spit file (compile-client-js opts))) - file)) - -(defn- provides-and-requires - "Return a flat list of all provided and required namespaces from a - sequence of IJavaScripts." - [deps] - (flatten (mapcat (juxt :provides :requires) deps))) - -;; TODO: the following is questionable as it triggers compilation -;; this code should other means to determine the dependencies for a -;; namespace - David - -(defn- always-preload - "Return a list of all namespaces which are always loaded into the browser - when using a browser-connected REPL. - - Uses the working-dir (see repl-env) to output intermediate compilation." - [& [{:keys [working-dir]}]] - (let [opts (if working-dir {:output-dir working-dir} - {}) - cljs (provides-and-requires - (cljsc/cljs-dependencies opts ["clojure.browser.repl"])) - goog (provides-and-requires - (cljsc/js-dependencies opts cljs))] - (disj (set (concat cljs goog)) nil))) - -;; NOTE: REPL evaluation environment designers do not replicate the behavior -;; of the browser REPL. The design is outdated, refer to the Node.js, Rhino or -;; Nashorn REPLs. - -(defn repl-env* [opts] - (let [ups-deps (cljsc/get-upstream-deps) - opts (assoc opts - :ups-libs (:libs ups-deps) - :ups-foreign-libs (:foreign-libs ups-deps)) - compiler-env (cljs.env/default-compiler-env opts) - opts (merge (BrowserEnv.) - {:port 9000 - :optimizations :simple - :working-dir (or (:output-dir opts) - (->> [".repl" (util/clojurescript-version)] - (remove empty?) (string/join "-"))) - :serve-static true - :static-dir (cond-> ["." "out/"] - (:output-dir opts) (conj (:output-dir opts))) - :preloaded-libs [] - :src "src/" - ::env/compiler compiler-env - :source-map false} - opts)] - (cljs.env/with-compiler-env compiler-env - (reset! preloaded-libs - (set (concat - (always-preload opts) - (map str (:preloaded-libs opts))))) - (reset! loaded-libs @preloaded-libs) - (println "Compiling client js ...") - (swap! browser-state - (fn [old] - (assoc old :client-js - (create-client-js-file - opts - (io/file (:working-dir opts) "client.js"))))) - (println "Waiting for browser to connect ...") - opts))) +(defn repl-env* + [{:keys [output-dir] :as opts}] + (merge (BrowserEnv.) + {:port 9000 + :working-dir (->> [".repl" (util/clojurescript-version)] + (remove empty?) (string/join "-")) + :serve-static true + :static-dir (cond-> ["." "out/"] output-dir (conj output-dir)) + :preloaded-libs [] + :optimizations :simple + :src "src/"} + opts)) (defn repl-env "Create a browser-connected REPL environment. @@ -593,9 +542,6 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" Defaults to true. static-dir: List of directories to search for static content. Defaults to [\".\" \"out/\"]. - preloaded-libs: List of namespaces that should not be sent from the REPL server - to the browser. This may be required if the browser is already - loading code and reloading it would cause a problem. optimizations: The level of optimization to use when compiling the client end of the REPL. Defaults to :simple. src: The source directory containing user-defined cljs files. Used to From d07a3e9f3b76075be2b0cb0e213efcefe977d21b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Mar 2015 19:03:37 -0400 Subject: [PATCH 0868/4033] tweak samples/repl/repl.clj helper script --- samples/repl/repl.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/samples/repl/repl.clj b/samples/repl/repl.clj index 0b99d37e0..d5a3d52f2 100644 --- a/samples/repl/repl.clj +++ b/samples/repl/repl.clj @@ -2,8 +2,9 @@ (require '[cljs.repl :as repl]) (require '[cljs.repl.browser :as brepl]) -(repl/repl - (brepl/repl-env) - :init (fn [] - (cljsc/build "src" {:output-to "main.js"})) +(cljsc/build "src" + {:output-to "main.js" + :verbose true}) + +(repl/repl (brepl/repl-env) :repl-verbose true) \ No newline at end of file From ffa7a437ce80f0ebe5f272046f7ae64ed23d1875 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Mar 2015 19:14:47 -0400 Subject: [PATCH 0869/4033] CLJS-1112: :repl-requires option for REPL evaluation environment support :repl-requires to cljs.repl/repl. If repls supply via IReplEnvOptions concatenate. fix bREPL printing --- src/clj/cljs/repl.clj | 16 ++++++++++------ src/clj/cljs/repl/browser.clj | 7 ++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ee7ad7471..5883d3428 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -644,7 +644,8 @@ (.printStackTrace e)))) (defn repl* - [repl-env {:keys [init need-prompt prompt flush read eval print caught reader print-no-newline source-map-inline wrap] + [repl-env {:keys [init need-prompt prompt flush read eval print caught reader + print-no-newline source-map-inline wrap repl-requires] :or {init #() need-prompt #(if (readers/indexing-reader? *in*) (== (readers/get-column-number *in*) 1) @@ -659,15 +660,18 @@ (PushbackReader. (io/reader *in*)) 1 "NO_SOURCE_FILE") print-no-newline print - source-map-inline true} + source-map-inline true + repl-requires '[[cljs.repl :refer-macros [source doc find-doc apropos dir pst]]]} :as opts}] - (let [{:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts + (let [repl-opts (-repl-options repl-env) + repl-requires (into repl-requires (:repl-requires repl-opts)) + {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} (merge {:cache-analysis true :source-map true} (cljsc/add-implicit-options (merge-with (fn [a b] (if (nil? b) a b)) - (-repl-options repl-env) + repl-opts opts {:init init :need-prompt prompt @@ -730,8 +734,8 @@ (analyze-source analyze-path opts)) (evaluate-form repl-env env "" (with-meta - '(ns cljs.user - (:require [cljs.repl :refer-macros [source doc find-doc apropos dir pst]])) + `(~'ns ~'cljs.user + (:require ~@repl-requires)) {:line 1 :column 1}) identity opts) (catch Throwable e diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index fe225cb59..28607812a 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -122,7 +122,8 @@ (send ordering (fn [_] {:expecting nil :fns {}})) (send-for-eval conn (cljsc/-compile - '[(set! *print-fn* clojure.browser.repl/repl-print)] {}) + '[(set! *print-fn* clojure.browser.repl/repl-print) + (set! *print-newline* true)] {}) identity)) (defn add-in-order [{:keys [expecting fns]} order f] @@ -496,6 +497,10 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" repl/IJavaScriptEnv (-setup [this opts] (setup this opts)) + repl/IReplEnvOptions + (-repl-options [this] + {:repl-requires + '[[clojure.browser.repl]]}) repl/IParseStacktrace (-parse-stacktrace [this st err opts] (parse-stacktrace this st err opts)) From a78e0009253a7fd5bdf042a037b2c20441fb66ae Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Mar 2015 22:12:08 -0400 Subject: [PATCH 0870/4033] CLJS-1114: browser REPL script loading race condition the browser REPL cannot use document.write to load scripts after page load as that would blow away the document. However appending script element to document.body suffers from race conditions due to asynchronous script loading when not using document.write. Add a load queue which enforces sequential load order. --- src/cljs/clojure/browser/repl.cljs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 123511687..66d8a228a 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -109,6 +109,8 @@ (js/setTimeout #(send-result connection url (wrap-message :ready "ready")) 50)) (js/alert "No 'xpc' param provided to child iframe."))) +(def load-queue nil) + (defn connect "Connects to a REPL server from an HTML document. After the connection is made, the REPL will evaluate forms in the context of @@ -135,14 +137,28 @@ (set! (.-require__ js/goog) js/goog.require) ;; suppress useless Google Closure error about duplicate provides (set! (.-isProvided_ js/goog) (fn [name] false)) - (set! (.-writeScriptTag_ js/goog) + (set! (.-writeScriptTag__ js/goog) (fn [src opt_sourceText] (.appendChild js/document.body (as-> (.createElement js/document "script") script - (doto script (aset "type" "text/javascript")) + (doto script + (aset "type" "text/javascript") + (aset "onload" + (fn [] + (when load-queue + (if (zero? (alength load-queue)) + (set! load-queue nil) + (.apply js/goog.writeScriptTag__ nil (.shift load-queue))))))) (if (nil? opt_sourceText) (doto script (aset "src" src)) (doto script (gdom/setTextContext opt_sourceText))))))) + (set! (.-writeScriptTag_ js/goog) + (fn [src opt_sourceText] + (if load-queue + (.push load-queue #js [src opt_sourceText]) + (do + (set! load-queue #js []) + (js/goog.writeScriptTag__ src opt_sourceText))))) (set! (.-require js/goog) (fn [src reload] (when (= reload "reload-all") From f0dfb7a9c4aa493f52504a86c8a9e0fd6f0d1cb6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Mar 2015 07:28:48 -0400 Subject: [PATCH 0871/4033] CLJS-1115: Reusable repl-bootstrap! fn lift reusable logic into closure.browser.repl/bootstrap. Handle < IE9. --- src/cljs/clojure/browser/repl.cljs | 91 ++++++++++++++++-------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 66d8a228a..b4deb040d 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -111,6 +111,55 @@ (def load-queue nil) +(defn bootstrap + "Reusable browser REPL bootstrapping. Patches the essential functions + in goog.base to support re-loading of namespaces after page load." + [] + ;; Monkey-patch goog.provide if running under optimizations :none - David + (when-not js/COMPILED + (set! (.-require__ js/goog) js/goog.require) + ;; suppress useless Google Closure error about duplicate provides + (set! (.-isProvided_ js/goog) (fn [name] false)) + (set! (.-writeScriptTag__ js/goog) + (fn [src opt_sourceText] + (let [loaded (atom false) + onload (fn [] + (when (and load-queue (false? @loaded)) + (swap! loaded not) + (if (zero? (alength load-queue)) + (set! load-queue nil) + (.apply js/goog.writeScriptTag__ nil (.shift load-queue)))))] + (.appendChild js/document.body + (as-> (.createElement js/document "script") script + (doto script + (aset "type" "text/javascript") + (aset "onload" onload) + (aset "onreadystatechange" onload)) + (if (nil? opt_sourceText) + (doto script (aset "src" src)) + (doto script (gdom/setTextContext opt_sourceText)))))))) + (set! (.-writeScriptTag_ js/goog) + (fn [src opt_sourceText] + (if load-queue + (.push load-queue #js [src opt_sourceText]) + (do + (set! load-queue #js []) + (js/goog.writeScriptTag__ src opt_sourceText))))) + (set! (.-require js/goog) + (fn [src reload] + (when (= reload "reload-all") + (set! (.-cljsReloadAll_ js/goog) true)) + (let [reload? (or reload (.-cljsReloadAll__ js/goog))] + (when reload? + (let [path (aget js/goog.dependencies_.nameToPath src)] + (js-delete js/goog.dependencies_.visited path) + (js-delete js/goog.dependencies_.written + (str js/goog.basePath path)))) + (let [ret (.require__ js/goog src)] + (when (= reload "reload-all") + (set! (.-cljsReloadAll_ js/goog) false)) + ret)))))) + (defn connect "Connects to a REPL server from an HTML document. After the connection is made, the REPL will evaluate forms in the context of @@ -132,45 +181,5 @@ (fn [iframe] (set! (.-display (.-style iframe)) "none"))) - ;; Monkey-patch goog.provide if running under optimizations :none - David - (when-not js/COMPILED - (set! (.-require__ js/goog) js/goog.require) - ;; suppress useless Google Closure error about duplicate provides - (set! (.-isProvided_ js/goog) (fn [name] false)) - (set! (.-writeScriptTag__ js/goog) - (fn [src opt_sourceText] - (.appendChild js/document.body - (as-> (.createElement js/document "script") script - (doto script - (aset "type" "text/javascript") - (aset "onload" - (fn [] - (when load-queue - (if (zero? (alength load-queue)) - (set! load-queue nil) - (.apply js/goog.writeScriptTag__ nil (.shift load-queue))))))) - (if (nil? opt_sourceText) - (doto script (aset "src" src)) - (doto script (gdom/setTextContext opt_sourceText))))))) - (set! (.-writeScriptTag_ js/goog) - (fn [src opt_sourceText] - (if load-queue - (.push load-queue #js [src opt_sourceText]) - (do - (set! load-queue #js []) - (js/goog.writeScriptTag__ src opt_sourceText))))) - (set! (.-require js/goog) - (fn [src reload] - (when (= reload "reload-all") - (set! (.-cljsReloadAll_ js/goog) true)) - (let [reload? (or reload (.-cljsReloadAll__ js/goog))] - (when reload? - (let [path (aget js/goog.dependencies_.nameToPath src)] - (js-delete js/goog.dependencies_.visited path) - (js-delete js/goog.dependencies_.written - (str js/goog.basePath path)))) - (let [ret (.require__ js/goog src)] - (when (= reload "reload-all") - (set! (.-cljsReloadAll_ js/goog) false)) - ret))))) + (bootstrap) repl-connection)) From 82fc6f361f928383c9cb9d92150e458ece3b1ccc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Mar 2015 08:00:15 -0400 Subject: [PATCH 0872/4033] fix fn scope tests --- test/clj/cljs/compiler_tests.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/clj/cljs/compiler_tests.clj b/test/clj/cljs/compiler_tests.clj index da3e580a1..8d4d831c0 100644 --- a/test/clj/cljs/compiler_tests.clj +++ b/test/clj/cljs/compiler_tests.clj @@ -31,21 +31,21 @@ '(defn foo [] (fn bar []))) [:init :name])) - 'cljs$user_SLASH_foo)) + 'cljs$user$foo)) (is (= (c/munge (get-in (a/analyze ns-env '(defn foo [] (fn bar []))) [:init :children 0 :children 0 :name])) - 'cljs$user_SLASH_foo_$_bar)) + 'cljs$user$foo_$_bar)) (is (= (c/munge (get-in (a/analyze ns-env '(fn [] (fn console []))) [:children 0 :children 0 :name])) - 'cljs$user_SLASH_console))) + 'cljs$user$console))) (comment (c/munge From 86b1e9faba272d1f288e82af122fe6fa9b39c30b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Mar 2015 08:01:45 -0400 Subject: [PATCH 0873/4033] CLJS-689: js/-Infinity munges to _Infinity Special case -Infinity in :var emit Add compiler test --- src/clj/cljs/compiler.clj | 7 +++++-- test/clj/cljs/compiler_tests.clj | 13 +++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 814244446..36997939c 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -251,7 +251,7 @@ (defmethod emit* :no-op [m]) (defmethod emit* :var - [{:keys [info env] :as arg}] + [{:keys [info env form] :as arg}] (let [var-name (:name info) info (if (= (namespace var-name) "js") (name var-name) @@ -264,7 +264,10 @@ ; (prevents duplicate fn-param-names) (emits (munge arg)) (when-not (= :statement (:context env)) - (emit-wrap env (emits (munge info))))))) + (emit-wrap env + (emits + (cond-> info + (not= form 'js/-Infinity) munge))))))) (defmethod emit* :var-special [{:keys [env var sym meta] :as arg}] diff --git a/test/clj/cljs/compiler_tests.clj b/test/clj/cljs/compiler_tests.clj index 8d4d831c0..63b9cd5c3 100644 --- a/test/clj/cljs/compiler_tests.clj +++ b/test/clj/cljs/compiler_tests.clj @@ -47,11 +47,8 @@ [:children 0 :children 0 :name])) 'cljs$user$console))) -(comment - (c/munge - (get-in - (a/analyze ns-env - '(defn foo [] - (fn bar []))) - [:init :children 0 :children 0 :name])) - ) \ No newline at end of file +(deftest test-js-negative-infinity + (= (with-out-str + (c/emit + (a/analyze (assoc ns-env :context :expr) 'js/-Infinity))) + "-Infinity")) From 5f66a78bf469a9875e51aa39c29d3e66ce890eb4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Mar 2015 08:33:20 -0400 Subject: [PATCH 0874/4033] CLJS-994: print a warning when :externs file paths can't be found. compiler now throw on missing extern as it will nearly always result in a corrupted build. --- src/clj/cljs/closure.clj | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index a7f92b661..60976d54f 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -199,10 +199,21 @@ load. The :use-only-custom-externs flag may be used to indicate that the default externs should be excluded." [{:keys [externs use-only-custom-externs target ups-externs]}] - (let [filter-cp-js (fn [paths] - (for [p paths u (deps/find-js-classpath p)] u)) + (let [validate (fn validate [p us] + (if (empty? us) + (throw (IllegalArgumentException. + (str "Extern " p " does not exist"))) + us)) + filter-cp-js (fn [paths] + (for [p paths + u (validate p + (deps/find-js-classpath p))] + u)) filter-js (fn [paths] - (for [p paths u (deps/find-js-resources p)] u)) + (for [p paths + u (validate p + (deps/find-js-resources p))] + u)) add-target (fn [ext] (cons (io/resource "cljs/externs.js") (if (= :nodejs target) From a816c3c37f3c89396646a603fafc1e80562f75ad Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Mar 2015 08:35:19 -0400 Subject: [PATCH 0875/4033] log optimization activity under :verbose --- src/clj/cljs/closure.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 60976d54f..c30a68b53 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -881,6 +881,8 @@ should contain the source for the given namespace name." (defn optimize "Use the Closure Compiler to optimize one or more JavaScript files." [opts & sources] + (when (or *verbose* (:verbose opts)) + (util/debug-prn "Applying optimizations" (:optimizations opts))) (let [closure-compiler (make-closure-compiler) ^List externs (load-externs opts) compiler-options (make-options opts) From 3c2dab3f7550bb229a5ac8124386b82fc7ddc024 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Mar 2015 08:58:49 -0400 Subject: [PATCH 0876/4033] typo --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c30a68b53..b9499a202 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -881,7 +881,7 @@ should contain the source for the given namespace name." (defn optimize "Use the Closure Compiler to optimize one or more JavaScript files." [opts & sources] - (when (or *verbose* (:verbose opts)) + (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "Applying optimizations" (:optimizations opts))) (let [closure-compiler (make-closure-compiler) ^List externs (load-externs opts) From 4a97e048488281f0958ddf60d8fdf503ff9e24eb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Mar 2015 10:26:38 -0400 Subject: [PATCH 0877/4033] CLJS-1117: Dependencies in JARs don't use cached analysis cljs.analyzer/analyze: * :pre condition so opts map isn't accidentally passed as 3rd name argument. cljs.analyzer/analyze-file: * fix bad caching boolean test * invoke cljs.analyzer/analyze correctly * Use implied output-dir. --- src/clj/cljs/analyzer.clj | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0c19754ee..be51eac06 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1844,6 +1844,8 @@ ([env form] (analyze env form nil)) ([env form name] (analyze env form name nil)) ([env form name opts] + {:pre [(or (nil? name) (symbol? name)) + (or (nil? opts) (map? opts))]} (env/ensure (wrapping-errors env (reduce (fn [ast pass] (pass env ast)) @@ -2033,9 +2035,10 @@ argument, which the reader will use in any emitted errors." \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a meaningful value." ([f] (analyze-file f nil)) - ([f {:keys [output-dir] :as opts}] + ([f opts] (binding [*file-defs* (atom #{})] - (let [res (cond + (let [output-dir (util/output-directory opts) + res (cond (instance? File f) f (instance? URL f) f (re-find #"^file://" f) (URL. f) @@ -2047,11 +2050,10 @@ argument, which the reader will use in any emitted errors." (.getPath ^File res) (.getPath ^URL res)) cache (when (or (= (:ns ns-info) 'cljs.core) - (and (:cache-analysis opts) output-dir)) + (:cache-analysis opts)) (cache-file res ns-info output-dir))] (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info)]) - (if (or (not= (:ns ns-info) 'cljs.core) - (not cache) + (if (or (not cache) (requires-analysis? res output-dir)) (binding [*cljs-ns* 'cljs.user *cljs-file* path @@ -2063,7 +2065,7 @@ argument, which the reader will use in any emitted errors." (if forms (let [form (first forms) env (assoc env :ns (get-namespace *cljs-ns*)) - ast (analyze env form opts)] + ast (analyze env form nil opts)] (if (= (:op ast) :ns) (recur (:name ast) (next forms)) (recur ns (next forms)))) From aea6326254ba30f79cd0daa5b471068427b5529c Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Tue, 10 Mar 2015 15:45:04 +1300 Subject: [PATCH 0878/4033] CLJS-1100: Add docstrings to core protocols. --- src/cljs/cljs/core.cljs | 251 ++++++++++++++++++++++++++++++---------- 1 file changed, 193 insertions(+), 58 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 02e255c3b..1c0081f20 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -305,6 +305,9 @@ "Marker protocol") (defprotocol IFn + "Protocol for adding the ability to invoke an object as a function. + For example, a vector can also be used to look up a value: + ([1 2 3 4] 1) => 2" (-invoke [this] [this a] @@ -330,84 +333,154 @@ [this a b c d e f g h i j k l m n o p q r s t rest])) (defprotocol ICloneable - (^clj -clone [value])) + "Protocol for cloning a value." + (^clj -clone [value] + "Creates a clone of value.")) (defprotocol ICounted - (^number -count [coll] "constant time count")) + "Protocol for adding the ability to count a collection in constant time." + (^number -count [coll] + "Calculates the count of coll in constant time. Used by cljs.core/count.")) (defprotocol IEmptyableCollection - (-empty [coll])) + "Protocol for creating an empty collection." + (-empty [coll] + "Returns an empty collection of the same category as coll. Used + by cljs.core/count.")) (defprotocol ICollection - (^clj -conj [coll o])) + "Protocol for adding to a collection." + (^clj -conj [coll o] + "Returns a new collection of coll with o added to it. The new item + should be added to the most efficient place, e.g. + (conj [1 2 3 4] 5) => [1 2 3 4 5] + (conj '(2 3 4 5) 1) => '(1 2 3 4 5)")) #_(defprotocol IOrdinal (-index [coll])) (defprotocol IIndexed - (-nth [coll n] [coll n not-found])) + "Protocol for collections to provide idexed-based access to their items." + (-nth [coll n] [coll n not-found] + "Returns the value at the index n in the collection coll. + Returns not-found if index n is out of bounds and not-found is supplied.")) -(defprotocol ASeq) +(defprotocol ASeq + "Marker protocol indicating an array sequence.") (defprotocol ISeq - (-first [coll]) - (^clj -rest [coll])) + "Protocol for collections to provide access to their items as sequences." + (-first [coll] + "Returns the first item in the collection coll. Used by cljs.core/first.") + (^clj -rest [coll] + "Returns a new collection of coll without the first item. It should + always return a seq, e.g. + (rest []) => () + (rest nil) => ()")) (defprotocol INext - (^clj-or-nil -next [coll])) + "Protocol for accessing the next items of a collection." + (^clj-or-nil -next [coll] + "Returns a new collection of coll without the first item. In contract to + rest, it should return nil if there are no more items, e.g. + (next []) => nil + (next nil) => nil")) (defprotocol ILookup - (-lookup [o k] [o k not-found])) + "Protocol for looking up a value in a data structure." + (-lookup [o k] [o k not-found] + "Use k to look up a value in o. If not-found is supplied and k is not + a valid value that can be used for look up, not-found is returned.")) (defprotocol IAssociative - (^boolean -contains-key? [coll k]) + "Protocol for adding associativity to collections." + (^boolean -contains-key? [coll k] + "Returns true if k is a key in coll.") #_(-entry-at [coll k]) - (^clj -assoc [coll k v])) + (^clj -assoc [coll k v] + "Returns a new collection of coll with a mapping from key k to + value v added to it.")) (defprotocol IMap + "Protocol for adding mapping functionality to collections." #_(-assoc-ex [coll k v]) - (^clj -dissoc [coll k])) + (^clj -dissoc [coll k] + "Returns a new collection of coll without the mapping for key k.")) (defprotocol IMapEntry - (-key [coll]) - (-val [coll])) + "Protocol for examining a map entry." + (-key [coll] + "Returns the key of the map entry.") + (-val [coll] + "Returns the value of the map entry.")) (defprotocol ISet - (^clj -disjoin [coll v])) + "Protocol for adding set functionality to a collection." + (^clj -disjoin [coll v] + "Returns a new collection of coll that does not contain v.")) (defprotocol IStack - (-peek [coll]) - (^clj -pop [coll])) + "Protocol for collections to provide access to their items as stacks. The top + of the stack should be accessed in the most efficient way for the different + data structures." + (-peek [coll] + "Returns the item from the top of the stack. Is used by cljs.core/peek.") + (^clj -pop [coll] + "Returns a new stack without the item on top of the stack. Is used + by cljs.core/pop.")) (defprotocol IVector - (^clj -assoc-n [coll n val])) + "Protocol for adding vector functionality to collections." + (^clj -assoc-n [coll n val] + "Returns a new vector with value val added at position n.")) (defprotocol IDeref - (-deref [o])) + "Protocol for adding dereference functionality to a reference." + (-deref [o] + "Returns the value of the reference o.")) (defprotocol IDerefWithTimeout (-deref-with-timeout [o msec timeout-val])) (defprotocol IMeta - (^clj-or-nil -meta [o])) + "Protocol for accessing the metadata of an object." + (^clj-or-nil -meta [o] + "Returns the metadata of object o.")) (defprotocol IWithMeta - (^clj -with-meta [o meta])) + "Protocol for adding metadata to an object." + (^clj -with-meta [o meta] + "Returns a new object with value of o and metadata meta added to it.")) (defprotocol IReduce - (-reduce [coll f] [coll f start])) + "Protocol for seq types that can reduce themselves. + Called by cljs.core/reduce." + (-reduce [coll f] [coll f start] + "f should be a function of 2 arguments. If start is not supplied, + returns the result of applying f to the first 2 items in coll, then + applying f to that result and the 3rd item, etc.")) (defprotocol IKVReduce - (-kv-reduce [coll f init])) + "Protocol for associative types that can reduce themselves + via a function of key and val. Called by cljs.core/reduce-kv." + (-kv-reduce [coll f init] + "Reduces an associative collection and returns the result. f should be + a function that takes three arguments.")) (defprotocol IEquiv - (^boolean -equiv [o other])) + "Protocol for adding value comparison functionality to a type." + (^boolean -equiv [o other] + "Returns true if o and other are equal, false otherwise.")) (defprotocol IHash - (-hash [o])) + "Protocol for adding hashing functionality to a type." + (-hash [o] + "Returns the hash code of o.")) (defprotocol ISeqable - (^clj-or-nil -seq [o])) + "Protocol for adding the ability to a type to be transformed into a sequence." + (^clj-or-nil -seq [o] + "Returns a seq of o, or nil if o is empty.")) (defprotocol ISequential "Marker interface indicating a persistent collection of sequential items") @@ -419,17 +492,33 @@ "Marker interface indicating a record object") (defprotocol IReversible - (^clj -rseq [coll])) + "Protocol for reversing a seq." + (^clj -rseq [coll] + "Returns a seq of the items in coll in reversed order.")) (defprotocol ISorted - (^clj -sorted-seq [coll ascending?]) - (^clj -sorted-seq-from [coll k ascending?]) - (-entry-key [coll entry]) - (-comparator [coll])) + "Protocol for a collection which can represent their items + in a sorted manner. " + (^clj -sorted-seq [coll ascending?] + "Returns a sorted seq from coll in either ascending or descending order.") + (^clj -sorted-seq-from [coll k ascending?] + "Returns a sorted seq from coll in either ascending or descending order. + If ascending is true, the result should contain all items which are > or >= + than k. If ascending is false, the result should contain all items which + are < or <= than k, e.g. + (-sorted-seq-from (sorted-set 1 2 3 4 5) 3 true) => (3 4 5) + (-sorted-seq-from (sorted-set 1 2 3 4 5) 3 false) => (3 2 1)") + (-entry-key [coll entry] + "Returns the key for entry.") + (-comparator [coll] + "Returns the comparator for coll.")) (defprotocol IWriter - (-write [writer s]) - (-flush [writer])) + "Protocol for writing. Currently only implemented by StringBufferWriter." + (-write [writer s] + "Writes s with writer and returns the result.") + (-flush [writer] + "Flush writer.")) (defprotocol IPrintWithWriter "The old IPrintable protocol's implementation consisted of building a giant @@ -440,63 +529,109 @@ (-pr-writer [o writer opts])) (defprotocol IPending - (^boolean -realized? [d])) + "Protocol for types which can have a deferred realization. Currently only + implemented by Delay." + (^boolean -realized? [d] + "Returns true if a value for d has been produced, false otherwise.")) (defprotocol IWatchable - (-notify-watches [this oldval newval]) - (-add-watch [this key f]) - (-remove-watch [this key])) + "Protocol for types that can be watched. Currently only implemented by Atom." + (-notify-watches [this oldval newval] + "Calls all watchers with this, oldval and newval.") + (-add-watch [this key f] + "Adds a watcher function f to this. Keys must be unique per reference, + and can be used to remove the watch with -remove-watch.") + (-remove-watch [this key] + "Removes watcher that corresponds to key from this.")) (defprotocol IEditableCollection - (^clj -as-transient [coll])) + "Protocol for collections which can transformed to transients." + (^clj -as-transient [coll] + "Returns a new, transient version of the collection, in constant time.")) (defprotocol ITransientCollection - (^clj -conj! [tcoll val]) - (^clj -persistent! [tcoll])) + "Protocol for adding basic functionality to transient collections." + (^clj -conj! [tcoll val] + "Adds value val to tcoll and returns tcoll.") + (^clj -persistent! [tcoll] + "Creates a persistent data structure from tcoll and returns it.")) (defprotocol ITransientAssociative - (^clj -assoc! [tcoll key val])) + "Protocol for adding associativity to transient collections." + (^clj -assoc! [tcoll key val] + "Returns a new transient collection of tcoll with a mapping from key to + val added to it.")) (defprotocol ITransientMap - (^clj -dissoc! [tcoll key])) + "Protocol for adding mapping functionality to transient collections." + (^clj -dissoc! [tcoll key] + "Returns a new transient collection of tcoll without the mapping for key.")) (defprotocol ITransientVector - (^clj -assoc-n! [tcoll n val]) - (^clj -pop! [tcoll])) + "Protocol for adding vector functionality to transient collections." + (^clj -assoc-n! [tcoll n val] + "Returns tcoll with value val added at position n.") + (^clj -pop! [tcoll] + "Returns tcoll with the last item removed from it.")) (defprotocol ITransientSet - (^clj -disjoin! [tcoll v])) + "Protocol for adding set functionality to a transient collection." + (^clj -disjoin! [tcoll v] + "Returns tcoll without v.")) (defprotocol IComparable - (^number -compare [x y])) + "Protocol for values that can be compared." + (^number -compare [x y] + "Returns a negative number, zero, or a positive number when x is logically + 'less than', 'equal to', or 'greater than' y.")) (defprotocol IChunk - (-drop-first [coll])) + "Protocol for accessing the items of a chunk." + (-drop-first [coll] + "Return a new chunk of coll with the first item removed.")) (defprotocol IChunkedSeq - (-chunked-first [coll]) - (-chunked-rest [coll])) + "Protocol for accessing a collection as sequential chunks." + (-chunked-first [coll] + "Returns the first chunk in coll.") + (-chunked-rest [coll] + "Return a new collection of coll with the first chunk removed.")) (defprotocol IChunkedNext - (-chunked-next [coll])) + "Protocol for accessing the chunks of a collection." + (-chunked-next [coll] + "Returns a new collection of coll without the first chunk.")) (defprotocol INamed - (^string -name [x]) - (^string -namespace [x])) + "Protocol for adding a name." + (^string -name [x] + "Returns the name String of x.") + (^string -namespace [x] + "Returns the namespace String of x.")) -(defprotocol IAtom) +(defprotocol IAtom + "Marker protocol indicating an atom.") (defprotocol IReset - (-reset! [o new-value])) + "Protocol for adding resetting functionality." + (-reset! [o new-value] + "Sets the value of o to new-value.")) (defprotocol ISwap - (-swap! [o f] [o f a] [o f a b] [o f a b xs])) + "Protocol for adding swapping functionality." + (-swap! [o f] [o f a] [o f a b] [o f a b xs] + "Swaps the value of o to be (apply f current-value-of-atom args).")) (defprotocol IVolatile - (-vreset! [o new-value])) + "Protocol for adding volatile functionality." + (-vreset! [o new-value] + "Sets the value of volatile o to new-value without regard for the + current value. Returns new-value.")) (defprotocol IIterable - (-iterator [coll])) + "Protocol for iterating over a collection." + (-iterator [coll] + "Returns an iterator for coll.")) ;; Printing support From a100ac9086cae82fa4ab6060b5f323f92e13b14d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Mar 2015 12:41:48 -0400 Subject: [PATCH 0879/4033] link to reporting issues page --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 71f917d90..9fc990b7f 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,15 @@ Latest stable release: 0.0-3058 ## Questions, Feedback? ## -Please point all of your questions and feedback -to the [Clojure mailing list](http://groups.google.com/group/clojure). There -is also a community run [ClojureScript user mailing list](http://groups.google.com/group/clojurescript). The Jira bug/feature tracking application is located at . +Please point all of your questions and feedback to the +[Clojure mailing list](http://groups.google.com/group/clojure). There +is also a community run +[ClojureScript user mailing list](http://groups.google.com/group/clojurescript). The +Jira bug/feature tracking application is located at +. Before submitting issues +please read the +[Reporting Issues](https://github.com/clojure/clojurescript/wiki/Reporting-Issues) +page first. ## Developers Welcome ## From 98702e7e5e7621f9b891b4e92e28fe8270cf226f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Mar 2015 16:33:03 -0400 Subject: [PATCH 0880/4033] fix bad cljs.analyzer/analyze calls in cljs.repl --- src/clj/cljs/repl.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5883d3428..cc491f3f6 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -386,7 +386,7 @@ (evaluate-form repl-env env filename form wrap *repl-opts*)) ([repl-env env filename form wrap opts] (binding [ana/*cljs-file* filename] - (let [ast (ana/analyze env form opts) + (let [ast (ana/analyze env form nil opts) js (comp/emit-str ast) wrap-js ;; TODO: check opts as well - David @@ -398,7 +398,7 @@ (let [js (comp/emit-str (ana/no-warn (ana/analyze (assoc env :repl-env repl-env) - (wrap form) opts))) + (wrap form) nil opts))) t (System/currentTimeMillis)] (str js "\n//# sourceURL=repl-" t ".js" @@ -418,7 +418,7 @@ (comp/emit-str (ana/no-warn (ana/analyze (assoc env :repl-env repl-env) - (wrap form) opts))))] + (wrap form) nil opts))))] (when (= (:op ast) :ns) (load-dependencies repl-env (into (vals (:requires ast)) From 6b97ce45316b74b5f96d845035a065468050de86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marczyk?= Date: Fri, 13 Mar 2015 22:46:47 +0100 Subject: [PATCH 0881/4033] CLJS-977: implement IKVReduce in Subvec --- src/cljs/cljs/core.cljs | 10 ++++++++++ test/cljs/cljs/core_test.cljs | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 1c0081f20..028fdffe2 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -4949,6 +4949,16 @@ reduces them without incurring seq initialization" (-reduce [coll f start] (ci-reduce coll f start)) + IKVReduce + (-kv-reduce [coll f init] + (loop [i start j 0 init init] + (if (< i end) + (let [init (f init j (-nth v i))] + (if (reduced? init) + @init + (recur (inc i) (inc j) init))) + init))) + IFn (-invoke [coll k] (-nth coll k)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index cdcf6353f..8c1d20ebe 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -459,7 +459,10 @@ ;; no layered subvecs (is (identical? v1 (.-v (subvec s 1 4)))) (let [m {:x 1}] - (is (= m (meta (with-meta s m)))))) + (is (= m (meta (with-meta s m))))) + ;; CLJS-997 + (is (= (reduce-kv + 123 (vec (range 10 50))) + (reduce-kv + 123 (subvec (vec (range 100)) 10 50))))) ;; CLJS-513 (let [sentinel (js-obj) s (subvec [0 1 2 3] 1 2)] From 6f9fca56ff3bcd143a85b33c0b494dd13a7a1f15 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Mar 2015 18:24:29 -0400 Subject: [PATCH 0882/4033] tweak compiler stats printing --- src/clj/cljs/util.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 14f9ff070..d04acdb65 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -139,6 +139,6 @@ `(if ~enable (let [start# (. System (nanoTime)) ret# ~expr] - (debug-prn ~msg ", elapsed time:" (/ (double (- (. System (nanoTime)) start#)) 1000000.0) "msecs") + (debug-prn (str ~msg ", elapsed time:") (/ (double (- (. System (nanoTime)) start#)) 1000000.0) "msecs") ret#) ~expr))) From 6097d1ce18f61a01b1cf6cfcc14e0071b99e413a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Mar 2015 19:28:25 -0400 Subject: [PATCH 0883/4033] CLJS-1119: constant table emission logic is incorrect move constant table emission after dependencies are compiled --- src/clj/cljs/closure.clj | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index b9499a202..4e76f473c 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1407,23 +1407,21 @@ should contain the source for the given namespace name." (let [compiled (util/measure compiler-stats "Compile basic sources" (doall (-compile source all-opts))) - ;; the constants_table.js file is not used directly here, is picked up by - ;; add-dependencies below - _ (when emit-constants - (comp/emit-constants-table-to-file - (::ana/constant-table @env/*compiler*) - (str (util/output-directory all-opts) "/constants_table.js"))) js-sources (util/measure compiler-stats "Add dependencies" (doall (concat - (apply add-dependencies all-opts - (concat - (if (coll? compiled) compiled [compiled]) - (when (= :nodejs (:target all-opts)) - [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))) - (when (= :nodejs (:target all-opts)) - [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) + (apply add-dependencies all-opts + (concat + (if (coll? compiled) compiled [compiled]) + (when (= :nodejs (:target all-opts)) + [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))) + (when (= :nodejs (:target all-opts)) + [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) + _ (when emit-constants + (comp/emit-constants-table-to-file + (::ana/constant-table @env/*compiler*) + (str (util/output-directory all-opts) "/constants_table.js"))) optim (:optimizations all-opts) ret (if (and optim (not= optim :none)) (do From 5d141f724af60353a1813c0ea4b5afab6eea2d57 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Mar 2015 20:16:56 -0400 Subject: [PATCH 0884/4033] CLJS-1120: analyze-deps does not appear to work when analyzing analysis caches cljs.analyzer/no-warn: * incorrectly bound *analyze-deps* to false cljs.analyzer/analyze-file: * only take cache path if :cache-analysis true cljs.build-api-tests: * fix broken tests that assuemd *analyze-deps* false cljs.compiler/compile-file: * no need to cache analysis, will be handled by dependency analysis --- src/clj/cljs/analyzer.clj | 6 ++---- src/clj/cljs/compiler.clj | 5 +---- test/clj/cljs/build_api_tests.clj | 21 ++++++++++++++------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index be51eac06..86f7d3eb5 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -246,8 +246,7 @@ (defmacro no-warn [& body] (let [no-warnings (zipmap (keys *cljs-warnings*) (repeat false))] - `(binding [*cljs-warnings* ~no-warnings - *analyze-deps* false] + `(binding [*cljs-warnings* ~no-warnings] ~@body))) (defmacro all-warn [& body] @@ -2049,8 +2048,7 @@ argument, which the reader will use in any emitted errors." path (if (instance? File res) (.getPath ^File res) (.getPath ^URL res)) - cache (when (or (= (:ns ns-info) 'cljs.core) - (:cache-analysis opts)) + cache (when (:cache-analysis opts) (cache-file res ns-info output-dir))] (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info)]) (if (or (not cache) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 36997939c..272a322c8 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1125,10 +1125,7 @@ {:recompile (into recompile (ana/ns-dependents ns)) :visited (conj visited ns)}))) ret)) - (do - (when-not (contains? (::ana/namespaces @env/*compiler*) ns) - (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) - ns-info))) + ns-info)) (catch Exception e (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) (throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))) diff --git a/test/clj/cljs/build_api_tests.clj b/test/clj/cljs/build_api_tests.clj index b6def5e18..b9306bf42 100644 --- a/test/clj/cljs/build_api_tests.clj +++ b/test/clj/cljs/build_api_tests.clj @@ -42,7 +42,8 @@ ;; basic -(binding [ana/*cljs-ns* 'cljs.user] +(binding [ana/*cljs-ns* 'cljs.user + ana/*analyze-deps* false] (env/with-compiler-env test-cenv (ana/no-warn (ana/analyze test-env @@ -51,20 +52,23 @@ ;; linear -(binding [ana/*cljs-ns* 'cljs.user] +(binding [ana/*cljs-ns* 'cljs.user + ana/*analyze-deps* false] (env/with-compiler-env test-cenv (ana/no-warn (ana/analyze test-env '(ns foo.core))))) -(binding [ana/*cljs-ns* 'cljs.user] +(binding [ana/*cljs-ns* 'cljs.user + ana/*analyze-deps* false] (env/with-compiler-env test-cenv (ana/no-warn (ana/analyze test-env '(ns bar.core (:require [foo.core :as foo])))))) -(binding [ana/*cljs-ns* 'cljs.user] +(binding [ana/*cljs-ns* 'cljs.user + ana/*analyze-deps* false] (env/with-compiler-env test-cenv (ana/no-warn (ana/analyze test-env @@ -73,20 +77,23 @@ ;; graph -(binding [ana/*cljs-ns* 'cljs.user] +(binding [ana/*cljs-ns* 'cljs.user + ana/*analyze-deps* false] (env/with-compiler-env test-cenv (ana/no-warn (ana/analyze test-env '(ns graph.foo.core))))) -(binding [ana/*cljs-ns* 'cljs.user] +(binding [ana/*cljs-ns* 'cljs.user + ana/*analyze-deps* false] (env/with-compiler-env test-cenv (ana/no-warn (ana/analyze test-env '(ns graph.bar.core (:require [graph.foo.core :as foo])))))) -(binding [ana/*cljs-ns* 'cljs.user] +(binding [ana/*cljs-ns* 'cljs.user + ana/*analyze-deps* false] (env/with-compiler-env test-cenv (ana/no-warn (ana/analyze test-env From b9ad9c1a75e21e47531758121f7168efe630eec3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 13 Mar 2015 20:30:30 -0400 Subject: [PATCH 0885/4033] revert CLJS-994, approach incompatible with cljsbiuld --- src/clj/cljs/closure.clj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 4e76f473c..5dc48e012 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -206,13 +206,11 @@ us)) filter-cp-js (fn [paths] (for [p paths - u (validate p - (deps/find-js-classpath p))] + u (deps/find-js-classpath p)] u)) filter-js (fn [paths] (for [p paths - u (validate p - (deps/find-js-resources p))] + u (deps/find-js-resources p)] u)) add-target (fn [ext] (cons (io/resource "cljs/externs.js") From 18fae8ba1de5a1d997dde34956afc8e1482f5104 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 07:11:38 -0400 Subject: [PATCH 0886/4033] CLJS-1062: Incorrect deftype/defrecord definition leads to complex error messages Modified version of Julian Eluard's patch which includes def case as well as the constructor name. --- src/clj/cljs/core.clj | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 14497d6e5..cf52c5c57 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -963,6 +963,11 @@ [~@fields] (new ~rname ~@field-values)))) +(defn- validate-fields + [case name fields] + (when-not (vector? fields) + (throw (AssertionError. (core/str case " " name ", no fields vector given."))))) + (defmacro deftype "(deftype name [fields*] options* specs*) @@ -1013,6 +1018,7 @@ Given (deftype TypeName ...), a factory function called ->TypeName will be defined, taking positional parameters for the fields" [t fields & impls] + (validate-fields "deftype" t fields) (let [env &env r (:name (cljs.analyzer/resolve-var (dissoc env :locals) t)) [fpps pmasks] (prepare-protocol-masks env impls) @@ -1172,6 +1178,7 @@ defined: ->TypeName, taking positional parameters for the fields, and map->TypeName, taking a map of keywords to field values." [rsym fields & impls] + (validate-fields "defrecord" rsym fields) (let [rsym (vary-meta rsym assoc :internal-ctor true) r (vary-meta (:name (cljs.analyzer/resolve-var (dissoc &env :locals) rsym)) From 34e398021886c40a7337bbd4ee999d072bcc1127 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 07:23:22 -0400 Subject: [PATCH 0887/4033] update cljs.closure/get-upstream-deps* docstring, using anything but the current thread classloader is not a good idea --- src/clj/cljs/closure.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 5dc48e012..418ff000d 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1206,9 +1206,7 @@ should contain the source for the given namespace name." (defn get-upstream-deps* "returns a merged map containing all upstream dependencies defined - by libraries on the classpath. Should be run in the main thread. If - not, pass (java.lang.ClassLoader/getSystemClassLoader) to use the - system classloader." + by libraries on the classpath." ([] (get-upstream-deps* (. (Thread/currentThread) (getContextClassLoader)))) ([classloader] From 06c98fcfeef6eac3f4f19c0700e36aceb390b665 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 07:25:31 -0400 Subject: [PATCH 0888/4033] upstream deps support is no longer experimental --- src/clj/cljs/closure.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 418ff000d..4891dc7ee 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1210,9 +1210,8 @@ should contain the source for the given namespace name." ([] (get-upstream-deps* (. (Thread/currentThread) (getContextClassLoader)))) ([classloader] - (let [upstream-deps (map #(read-string (slurp %)) (enumeration-seq (. classloader (getResources "deps.cljs"))))] - #_(doseq [dep upstream-deps] - (println (str "Upstream deps.cljs found on classpath. " dep " This is an EXPERIMENTAL FEATURE and is not guarenteed to remain stable in future versions."))) + (let [upstream-deps (map #(read-string (slurp %)) + (enumeration-seq (. classloader (getResources "deps.cljs"))))] (apply merge-with concat upstream-deps)))) (def get-upstream-deps (memoize get-upstream-deps*)) From 2bb2d0b6604aa40101c369d98754edc0549824a8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 08:20:25 -0400 Subject: [PATCH 0889/4033] CLJS-876: merged sourcemap doesn't account for output-wrapper --- src/clj/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 4891dc7ee..da876cd94 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -905,7 +905,8 @@ should contain the source for the given namespace name." sources name (assoc opts :preamble-line-count - (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1))))) + (+ (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1) + (if (:output-wrapper opts) 1 0)))))) source) (report-failure result)))) @@ -1072,7 +1073,8 @@ should contain the source for the given namespace name." {:source-map sm-name :preamble-line-count (if (= name :cljs-base) - (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1) + (+ (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1) + (if (:output-wrapper opts) 1 0)) 0) :foreign-deps-line-count (if fdeps-str From b6a9dc06cefaeba3d478990bc1a2a24331dd0e59 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 09:08:34 -0400 Subject: [PATCH 0890/4033] revert cljs.closure/compile-file "optimization", must always populate compilation environment with analysis information, not doing so damages composition of namespaces with smaller compilation units like expressions. the problem was uncovered by starting REPLs with cached files, the second run would be borked because we compiled core but did not populate the compilation environment. Include comment covering this point as well. --- src/clj/cljs/compiler.clj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 272a322c8..3dfd1b9ab 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1125,7 +1125,13 @@ {:recompile (into recompile (ana/ns-dependents ns)) :visited (conj visited ns)}))) ret)) - ns-info)) + (do + ;; always populate compilation environment with analysis information + ;; while it would seem this isn't necessary avoiding to do so damages + ;; composition of smaller compilation units like expressions (i.e. REPLs) + (when-not (contains? (::ana/namespaces @env/*compiler*) ns) + (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) + ns-info))) (catch Exception e (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) (throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))) From b0b135b088acfcd15002b107efe872490160c548 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 09:26:27 -0400 Subject: [PATCH 0891/4033] CLJS-813: Warn about reserved JS keyword usage in namespace names Move cljs.compiler/js-reserved to cljs.analyzer, keep var to avoid breakage Emit warning if any ns segment is a JS keyword --- src/clj/cljs/analyzer.clj | 34 +++++++++++++++++++++++++++++++--- src/clj/cljs/compiler.clj | 15 +-------------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 86f7d3eb5..e6ffbd74a 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -68,7 +68,23 @@ :protocol-invalid-method true :protocol-duped-method true :protocol-multiple-impls true - :single-segment-namespace true}) + :single-segment-namespace true + :munged-namespace true}) + +(def js-reserved + #{"abstract" "boolean" "break" "byte" "case" + "catch" "char" "class" "const" "continue" + "debugger" "default" "delete" "do" "double" + "else" "enum" "export" "extends" "final" + "finally" "float" "for" "function" "goto" "if" + "implements" "import" "in" "instanceof" "int" + "interface" "let" "long" "native" "new" + "package" "private" "protected" "public" + "return" "short" "static" "super" "switch" + "synchronized" "this" "throw" "throws" + "transient" "try" "typeof" "var" "void" + "volatile" "while" "with" "yield" "methods" + "null"}) (declare message namespaces) @@ -184,6 +200,15 @@ [warning-type info] (str (:name info) " is a single segment namespace")) +(defmethod error-message :munged-namespace + [warning-type {:keys [name] :as info}] + (let [munged (->> (string/split (clojure.core/name name) #"\.") + (map #(if (js-reserved %) (str % "$") %)) + (string/join ".") + (munge))] + (str "Namespace " name " contains a reserved JavaScript keyword," + " the corresponding Google Closure namespace will be munged to " munged))) + (defn ^:private default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] @@ -1385,8 +1410,11 @@ [_ env [_ name & args :as form] _ opts] (when-not (symbol? name) (throw (error env "Namespaces must be named by a symbol."))) - (when (= 1 (count (string/split (clojure.core/name name) #"\."))) - (warning :single-segment-namespace env {:name name})) + (let [segments (string/split (clojure.core/name name) #"\.")] + (when (= 1 (count segments)) + (warning :single-segment-namespace env {:name name})) + (when (some js-reserved segments) + (warning :munged-namespace env {:name name}))) (let [docstring (if (string? (first args)) (first args)) mdocstr (-> name meta :doc) args (if docstring (next args) args) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 3dfd1b9ab..d9c83fda8 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -22,20 +22,7 @@ (set! *warn-on-reflection* true) -(def js-reserved - #{"abstract" "boolean" "break" "byte" "case" - "catch" "char" "class" "const" "continue" - "debugger" "default" "delete" "do" "double" - "else" "enum" "export" "extends" "final" - "finally" "float" "for" "function" "goto" "if" - "implements" "import" "in" "instanceof" "int" - "interface" "let" "long" "native" "new" - "package" "private" "protected" "public" - "return" "short" "static" "super" "switch" - "synchronized" "this" "throw" "throws" - "transient" "try" "typeof" "var" "void" - "volatile" "while" "with" "yield" "methods" - "null"}) +(def js-reserved ana/js-reserved) (def ^:dynamic *dependents* nil) (def ^:dynamic *source-map-data* nil) From 68894f00c8bd281e4b8366fe335c98fa3e4a067c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 10:02:32 -0400 Subject: [PATCH 0892/4033] CLJS-891: Defs in "parent" namespaces clash with "child" namespaces with the same name? check in both 'def and 'ns cases of the analyzer for namespace / var clashes --- src/clj/cljs/analyzer.clj | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index e6ffbd74a..8c339e45c 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -69,7 +69,8 @@ :protocol-duped-method true :protocol-multiple-impls true :single-segment-namespace true - :munged-namespace true}) + :munged-namespace true + :ns-var-clash true}) (def js-reserved #{"abstract" "boolean" "break" "byte" "case" @@ -209,6 +210,10 @@ (str "Namespace " name " contains a reserved JavaScript keyword," " the corresponding Google Closure namespace will be munged to " munged))) +(defmethod error-message :ns-var-clash + [warning-type {:keys [ns var] :as info}] + (str "Namespace " ns " clashes with var " var)) + (defn ^:private default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] @@ -770,7 +775,12 @@ protocol (-> sym meta :protocol) dynamic (-> sym meta :dynamic) ns-name (-> env :ns :name) - locals (:locals env)] + locals (:locals env) + clash-ns (symbol (str ns-name "." sym))] + (when (get-in @env/*compiler* [::namespaces clash-ns]) + (warning :ns-var-clash env + {:ns (symbol (str ns-name "." sym)) + :var (symbol (str ns-name) (str sym))})) (when (namespace sym) (throw (error env "Can't def ns-qualified name"))) (when-let [doc (:doc args)] @@ -1406,6 +1416,17 @@ (update-in indexed [:require-macros] (fnil into []) require-specs)) args))) +(defn find-def-clash [env ns segments] + (let [to-check (map (fn [xs] + [(symbol (string/join "." (butlast xs))) + (symbol (last xs))]) + (drop 2 (reductions conj [] segments)))] + (doseq [[clash-ns name] to-check] + (when (get-in @env/*compiler* [::namespaces clash-ns :defs name]) + (warning :ns-var-clash env + {:ns ns + :var (symbol (str clash-ns) (str name))}))))) + (defmethod parse 'ns [_ env [_ name & args :as form] _ opts] (when-not (symbol? name) @@ -1414,7 +1435,8 @@ (when (= 1 (count segments)) (warning :single-segment-namespace env {:name name})) (when (some js-reserved segments) - (warning :munged-namespace env {:name name}))) + (warning :munged-namespace env {:name name})) + (find-def-clash env name segments)) (let [docstring (if (string? (first args)) (first args)) mdocstr (-> name meta :doc) args (if docstring (next args) args) From 7c62704e1f06e4d914e9b39c1bef55aa7a3e983e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 10:25:12 -0400 Subject: [PATCH 0893/4033] re-introduce cljs.closure/compile-file skip analysis optimization builds are "non-interactive". That is, the unit of compilation for projects is nearly always at the module (namespace) level. Here ns information triggers analysis when required so skipping analysis is not problematic. builds now default to "non-interactive". Cold builds when there is no work to do is now quite fast. the real default is "interactive" so REPLs can continue to work as they have. --- src/clj/cljs/closure.clj | 3 ++- src/clj/cljs/compiler.clj | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index da876cd94..61418a899 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1365,7 +1365,8 @@ should contain the source for the given namespace name." ([source opts compiler-env] (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) - all-opts (add-implicit-options opts) + all-opts (assoc (add-implicit-options opts) + :compilation-mode :non-interactive) emit-constants (or (and (= (:optimizations opts) :advanced) (not (false? (:optimize-constants opts)))) (:optimize-constants opts))] diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index d9c83fda8..655aa4f9b 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1113,10 +1113,11 @@ :visited (conj visited ns)}))) ret)) (do - ;; always populate compilation environment with analysis information + ;; populate compilation environment with analysis information ;; while it would seem this isn't necessary avoiding to do so damages ;; composition of smaller compilation units like expressions (i.e. REPLs) - (when-not (contains? (::ana/namespaces @env/*compiler*) ns) + (when (and (not= (:compilation-mode opts) :non-interactive) + (not (contains? (::ana/namespaces @env/*compiler*) ns))) (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) ns-info))) (catch Exception e From 4228ee3a5dad6b04b1f852cb87138ac5c6d595be Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 12:29:35 -0400 Subject: [PATCH 0894/4033] CLJS-109: Compiler errors/warnings should be displayed when cljs namespace 'package' names start with an unacceptable javascript symbol throw on invalid ns names --- src/clj/cljs/analyzer.clj | 6 +++++- src/clj/cljs/util.clj | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 8c339e45c..23315f5c9 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1436,7 +1436,11 @@ (warning :single-segment-namespace env {:name name})) (when (some js-reserved segments) (warning :munged-namespace env {:name name})) - (find-def-clash env name segments)) + (find-def-clash env name segments) + (when (some (complement util/valid-js-id-start?) segments) + (throw (AssertionError. + (str "Namespace " name " has a segment starting with an invaild " + "JavaScript identifier"))))) (let [docstring (if (string? (first args)) (first args)) mdocstr (-> name meta :doc) args (if docstring (next args) args) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index d04acdb65..cb32e2b66 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -126,6 +126,9 @@ (when (= depth 0) (distinct (apply concat (vals @state))))))) +(defn valid-js-id-start? [s] + (re-find #"(?U)^[\p{Alpha}_$]" s)) + (defn debug-prn [& args] (binding [*out* *err*] From 804946988ed0f9787b40f6464ade88c71ad8b13e Mon Sep 17 00:00:00 2001 From: Alex Dowad Date: Thu, 18 Dec 2014 22:09:23 +0200 Subject: [PATCH 0895/4033] CLJS-889: re-pattern works on strings containing \u2028 or \u2029 JavaScript RegExps don't match \u2028 or \u2029 with ., which was causing the previous implementation of re-pattern to fail on those characters. (It was as if the regexp was chopped short at the point where the offending character appeared.) --- src/cljs/cljs/core.cljs | 5 +++-- test/cljs/cljs/core_test.cljs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 028fdffe2..5ec446113 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8405,8 +8405,9 @@ reduces them without incurring seq initialization" [s] (if (instance? js/RegExp s) s - (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" s)] - (js/RegExp. pattern flags)))) + (let [[prefix flags] (re-find #"^\(\?([idmsux]*)\)" s) + pattern (subs s (count prefix))] + (js/RegExp. pattern (or flags ""))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Printing ;;;;;;;;;;;;;;;; diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 8c1d20ebe..51270fc19 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -937,7 +937,8 @@ (is (= (re-seq (re-pattern "f(.)o") "foo bar foo baz foo zot") (list ["foo" "o"] ["foo" "o"] ["foo" "o"]))) (is (= (re-matches (re-pattern "(?i)foo") "Foo") "Foo")) ; new RegExp("").source => "(?:)" on webkit-family envs, "" elsewhere - (is (#{"#\"\"" "#\"(?:)\""} (pr-str #""))))) + (is (#{"#\"\"" "#\"(?:)\""} (pr-str #""))) + (is (= (re-find (re-pattern "[\u2028]") " \u2028 ") "\u2028")))) ; regression test for CLJS-889 (deftest test-destructuring (testing "Testing destructuring" From d551a4d18ad056169175df1345e9f30661d3f607 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Mar 2015 14:23:51 -0400 Subject: [PATCH 0896/4033] CLJS-1118: cljs.repl/doc support for protocols --- src/clj/cljs/core.clj | 38 ++++++++++++++++++++++++++------------ src/clj/cljs/repl.clj | 18 ++++++++++++++---- src/cljs/cljs/repl.cljs | 11 ++++++++++- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index cf52c5c57..8ea663515 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1234,14 +1234,20 @@ => 17" [psym & doc+methods] (let [p (:name (cljs.analyzer/resolve-var (dissoc &env :locals) psym)) - psym (vary-meta psym assoc :protocol-symbol true) + [doc methods] (if (core/string? (first doc+methods)) + [(first doc+methods) (next doc+methods)] + [nil doc+methods]) + psym (vary-meta psym assoc + :doc doc + :protocol-symbol true) ns-name (-> &env :ns :name) fqn (fn [n] (symbol (core/str ns-name "." n))) prefix (protocol-prefix p) - methods (if (core/string? (first doc+methods)) (next doc+methods) doc+methods) _ (core/doseq [[mname & arities] methods] (when (some #{0} (map count (filter vector? arities))) - (throw (Exception. (core/str "Invalid protocol, " psym " defines method " mname " with arity 0"))))) + (throw (Exception. + (core/str "Invalid protocol, " psym + " defines method " mname " with arity 0"))))) expand-sig (fn [fname slot sig] `(~sig (if (and ~(first sig) (. ~(first sig) ~(symbol (core/str "-" slot)))) ;; Property access needed here. @@ -1257,18 +1263,26 @@ (into {} (map (fn [[fname & sigs]] - (let [sigs (take-while vector? sigs)] - [fname (vec sigs)])) + (let [doc (as-> (last sigs) doc + (when (core/string? doc) doc)) + sigs (take-while vector? sigs)] + [(vary-meta fname assoc :doc doc) + (vec sigs)])) methods))) method (fn [[fname & sigs]] - (let [sigs (take-while vector? sigs) + (let [doc (as-> (last sigs) doc + (when (core/string? doc) doc)) + sigs (take-while vector? sigs) slot (symbol (core/str prefix (name fname))) - fname (vary-meta fname assoc :protocol p)] - `(defn ~fname ~@(map (fn [sig] - (expand-sig fname - (symbol (core/str slot "$arity$" (count sig))) - sig)) - sigs))))] + fname (vary-meta fname assoc + :protocol p + :doc doc)] + `(defn ~fname + ~@(map (fn [sig] + (expand-sig fname + (symbol (core/str slot "$arity$" (count sig))) + sig)) + sigs))))] `(do (set! ~'*unchecked-if* true) (def ~psym (js-obj)) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index cc491f3f6..7d7c0424d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -986,10 +986,20 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (ana-api/resolve &env name) `(cljs.repl/print-doc - (quote ~(update-in - (select-keys (ana-api/resolve &env name) - [:ns :name :doc :forms :arglists :macro :url]) - [:name] clojure.core/name))))))))) + (quote ~(let [var (ana-api/resolve &env name) + m (select-keys var + [:ns :name :doc :forms :arglists :macro :url])] + (cond-> (update-in m [:name] clojure.core/name) + (:protocol-symbol var) + (assoc :protocol true + :methods + (->> (get-in var [:protocol-info :methods]) + (map (fn [[fname sigs]] + [fname {:doc (:doc + (ana-api/resolve &env + (symbol (str (:ns var)) (str fname)))) + :arglists (seq sigs)}])) + (into {}))))))))))))) (defmacro find-doc "Prints documentation for any var whose documentation or name diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index 4ecc55c49..67fb1c58d 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -12,6 +12,8 @@ (defn print-doc [m] (println "-------------------------") (println (str (when-let [ns (:ns m)] (str ns "/")) (:name m))) + (when (:protocol m) + (println "Protocol")) (cond (:forms m) (doseq [f (:forms m)] (println " " f)) @@ -33,4 +35,11 @@ (println "Macro")) (when (:repl-special-function m) (println "REPL Special Function")) - (println " " (:doc m))))) + (println " " (:doc m)) + (when (:protocol m) + (doseq [[name {:keys [doc arglists]}] (:methods m)] + (println) + (println " " name) + (println " " arglists) + (when doc + (println " " doc))))))) From 9a1fae97246a4f3acc9f70cbe51037d8331239cf Mon Sep 17 00:00:00 2001 From: Peter Schuck Date: Sat, 14 Mar 2015 13:50:23 -0500 Subject: [PATCH 0897/4033] CLJS-806: support ^:const def now supports const making the var a constant (i.e. no redefining or setting). The emitted definition has the @const JSDoc tag added to it. Support for constants has been added to the case macro. --- src/clj/cljs/analyzer.clj | 13 ++++++++++--- src/clj/cljs/core.clj | 3 ++- test/clj/cljs/analyzer_tests.clj | 17 ++++++++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 23315f5c9..7d54512e0 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -684,10 +684,13 @@ tests (mapv #(mapv (fn [t] (analyze expr-env t)) %) tests) thens (mapv #(analyze env %) thens) default (analyze env default)] - (assert (every? (fn [t] (and (= :constant (:op t)) - ((some-fn number? string? char?) (:form t)))) + (assert (every? (fn [t] + (or + (-> t :info :const) + (and (= :constant (:op t)) + ((some-fn number? string? char?) (:form t))))) (apply concat tests)) - "case* tests must be numbers or strings") + "case* tests must be numbers, strings, or constants") {:env env :op :case* :form form :v v :tests tests :thens thens :default default :children (vec (concat [v] tests thens (if default [default])))})) @@ -783,6 +786,8 @@ :var (symbol (str ns-name) (str sym))})) (when (namespace sym) (throw (error env "Can't def ns-qualified name"))) + (when (:const (resolve-var (dissoc env :locals) sym)) + (throw (error env "Can't redefine a constant"))) (when-let [doc (:doc args)] (when-not (string? doc) (throw (error env "Too many arguments to def")))) @@ -1178,6 +1183,8 @@ (symbol? target) (do + (when (:const (resolve-var (dissoc env :locals) target)) + (throw (error env "Can't set! a constant"))) (let [local (-> env :locals target)] (when-not (or (nil? local) (and (:field local) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 8ea663515..aab24fe44 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1481,9 +1481,10 @@ (assoc-test m test expr env))) {} (partition 2 clauses)) esym (gensym) + const? #(:const (and (list? %) (ana/resolve-var env (last %)))) tests (keys pairs)] (cond - (every? (some-fn core/number? core/string? core/char?) tests) + (every? (some-fn core/number? core/string? core/char? const?) tests) (core/let [no-default (if (odd? (count clauses)) (butlast clauses) clauses) tests (mapv #(if (seq? %) (vec %) [%]) (take-nth 2 no-default)) thens (vec (take-nth 2 (drop 1 no-default)))] diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index b69595bd6..23f4681a9 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -328,4 +328,19 @@ (deftest test-cljs-1105 ;; munge turns - into _, must preserve the dash first (is (not= (a/gen-constant-id :test-kw) - (a/gen-constant-id :test_kw)))) \ No newline at end of file + (a/gen-constant-id :test_kw)))) +;; Constants + +(deftest test-constants + (is (.startsWith + (try + (a/analyze test-env '(do (def ^:const foo 123) (def foo 246))) + (catch Exception e + (.getMessage e))) + "Can't redefine a constant")) + (is (.startsWith + (try + (a/analyze test-env '(do (def ^:const foo 123) (set! foo 246))) + (catch Exception e + (.getMessage e))) + "Can't set! a constant"))) From 25602b9fe3674d27193e1f3e14b4193b919c00a5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 14 Mar 2015 16:23:45 -0400 Subject: [PATCH 0898/4033] fix some AOT oddities in the const enhancement --- src/clj/cljs/core.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index aab24fe44..2c8ac9103 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1434,6 +1434,11 @@ cljs.analyzer/*cljs-file*))))) (assoc m test expr))) +(defn- const? [env x] + (core/let [m (core/and (core/list? x) + (ana/resolve-var env (last x)))] + (core/when m (core/get m :const)))) + (defmacro case "Takes an expression, and a set of clauses. @@ -1481,10 +1486,9 @@ (assoc-test m test expr env))) {} (partition 2 clauses)) esym (gensym) - const? #(:const (and (list? %) (ana/resolve-var env (last %)))) tests (keys pairs)] (cond - (every? (some-fn core/number? core/string? core/char? const?) tests) + (every? (some-fn core/number? core/string? core/char? #(const? env %)) tests) (core/let [no-default (if (odd? (count clauses)) (butlast clauses) clauses) tests (mapv #(if (seq? %) (vec %) [%]) (take-nth 2 no-default)) thens (vec (take-nth 2 (drop 1 no-default)))] From 42c6930e65444ee066d5d77a96f1207bafe70147 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Mar 2015 10:00:02 -0400 Subject: [PATCH 0899/4033] CLJS-667: validate extend-type and extend-protocol shape --- src/clj/cljs/analyzer.clj | 8 +++++++- src/clj/cljs/core.clj | 10 ++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 7d54512e0..c6ce7ef6f 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -70,7 +70,8 @@ :protocol-multiple-impls true :single-segment-namespace true :munged-namespace true - :ns-var-clash true}) + :ns-var-clash true + :extend-type-invalid-method-shape true}) (def js-reserved #{"abstract" "boolean" "break" "byte" "case" @@ -214,6 +215,11 @@ [warning-type {:keys [ns var] :as info}] (str "Namespace " ns " clashes with var " var)) +(defmethod error-message :extend-type-invalid-method-shape + [warning-type {:keys [protocol method] :as info}] + (str "Bad extend-type method shape for protocol " protocol " method " method + ", method arities must be grouped together")) + (defn ^:private default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index aab24fe44..82fac0a43 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -875,8 +875,14 @@ impls (drop-while seq? (next impls))] (when (contains? protos proto) (ana/warning :protocol-multiple-impls env {:protocol proto})) - (core/doseq [method methods] - (validate-impl-sigs env proto method)) + (loop [seen #{} methods methods] + (when (seq methods) + (let [[fname :as method] (first methods)] + (when (contains? seen fname) + (ana/warning :extend-type-invalid-method-shape env + {:protocol proto :method fname})) + (validate-impl-sigs env proto method) + (recur (conj seen fname) (next methods))))) (recur (conj protos proto) impls))))) (defmacro extend-type From 29655a20f31a692e7feb45988a0c28b77ae74cf8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Mar 2015 10:45:11 -0400 Subject: [PATCH 0900/4033] CLJS-867: extend-type with Object methods requires multi-arity style definition Object protocol method support was missing reshaping logic --- src/clj/cljs/core.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 2e3bd93e1..deff7e073 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -791,8 +791,11 @@ (defn add-obj-methods [type type-sym sigs] (map (fn [[f & meths :as form]] - `(set! ~(extend-prefix type-sym f) - ~(with-meta `(fn ~@(map #(adapt-obj-params type %) meths)) (meta form)))) + (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)) (defn ifn-invoke-methods [type type-sym [f & meths :as form]] From c244759740bcf252ee8f5464a4e465ad482780a8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Mar 2015 10:57:54 -0400 Subject: [PATCH 0901/4033] CLJS-1085: Allow to pass test environment to cljs.test/run-all-tests --- src/clj/cljs/test.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/test.clj b/src/clj/cljs/test.clj index 462191b42..b4e3a1c70 100644 --- a/src/clj/cljs/test.clj +++ b/src/clj/cljs/test.clj @@ -294,9 +294,10 @@ Optional argument is a regular expression; only namespaces with names matching the regular expression (with re-matches) will be tested." - ([] `(cljs.test/run-all-tests nil)) - ([re] - `(cljs.test/run-tests (cljs.test/empty-env) + ([] `(cljs.test/run-all-tests nil (cljs.test/empty-env))) + ([re] `(cljs.test/run-all-tests ~re (cljs.test/empty-env))) + ([re env] + `(cljs.test/run-tests ~env ~@(map (fn [ns] `(quote ~ns)) (cond->> (ana-api/all-ns) From 7de5e7707617a71c03f701063e15c6f2eee3f7d8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Mar 2015 11:41:25 -0400 Subject: [PATCH 0902/4033] INext docstring typo --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 5ec446113..60693dd35 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -381,7 +381,7 @@ (defprotocol INext "Protocol for accessing the next items of a collection." (^clj-or-nil -next [coll] - "Returns a new collection of coll without the first item. In contract to + "Returns a new collection of coll without the first item. In contrast to rest, it should return nil if there are no more items, e.g. (next []) => nil (next nil) => nil")) From 7e1f81a036b3621f847f2288e177f7d3242a0b04 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Mar 2015 12:27:19 -0400 Subject: [PATCH 0903/4033] need to bind cljs.repl/*repl-opts* sooner for IJavaScriptEnv -setup so cljs form evaluations work --- src/clj/cljs/repl.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 7d7c0424d..b081472b9 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -692,7 +692,8 @@ :undeclared-var warn-on-undeclared :undeclared-ns warn-on-undeclared :undeclared-ns-form warn-on-undeclared) - ana/*cljs-static-fns* static-fns] + ana/*cljs-static-fns* static-fns + *repl-opts* opts] ;; TODO: the follow should become dead code when the REPL is ;; sufficiently enhanced to understand :cache-analysis - David (let [env {:context :expr :locals {}} From feccc019241c39be104cc827a77d9c1a6dcccd55 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Mar 2015 13:12:57 -0400 Subject: [PATCH 0904/4033] fix Node.js regressions due to AOT move all Node.js target decisions to runtime so AOTed core can be used by all targets. fix Node.js REPL foreign dep support regression --- src/clj/cljs/compiler.clj | 19 +++++++------------ src/clj/cljs/core.clj | 12 ++---------- src/clj/cljs/repl/rhino.clj | 3 +-- src/cljs/cljs/core.cljs | 6 ++++-- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 655aa4f9b..8044a3996 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -848,19 +848,14 @@ (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond - (ana/foreign-dep? lib) + (and (= :nodejs (get-in @env/*compiler* [:options :target])) + (ana/foreign-dep? lib)) ;; under node.js we load foreign libs globally - (if (= :nodejs (get-in @env/*compiler* [:options :target])) - (let [js-index (:js-dependency-index @env/*compiler*) - ijs-url (get-in js-index [(name lib) :url])] - (emitln "cljs.core.load_file(\"" (util/get-name ijs-url) "\");")) - ;; otherwise only include if set in the options to do so, - ;; in the browser unnecessary due to the fact that goog.require - ;; there works by writing all deps as script tags, this doesn't - ;; work in Rhino-like environment where we do a proper goog.require - ;; on demand - (when (get-in @env/*compiler* [:options :require-foreign]) - (emitln "goog.require('" (munge lib) "');"))) + (let [{:keys [js-dependency-index options]} @env/*compiler* + ijs-url (get-in js-dependency-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" + (str (io/file (util/output-directory options) (util/get-name ijs-url))) + "\");")) (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', 'reload');") diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index deff7e073..214c4cf75 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -317,11 +317,6 @@ (defmacro false? [x] (bool-expr (core/list 'js* "~{} === false" x))) -(defmacro array? [x] - (if (= :nodejs (-> @env/*compiler* :options :target)) - (bool-expr `(.isArray js/Array ~x)) - (bool-expr (core/list 'js* "~{} instanceof Array" x)))) - (defmacro string? [x] (bool-expr (core/list 'js* "typeof ~{} === 'string'" x))) @@ -1974,9 +1969,6 @@ [vol f & args] `(-vreset! ~vol (~f (-deref ~vol) ~@args))) +;; INTERNAL - do not use, only for Node.js (defmacro load-file* [f] - (core/let [{:keys [target output-dir]} (:options @env/*compiler*)] - (core/condp = target - ;; under Node.js, always relative to JVM working directory - :nodejs `(. js/goog (~'nodeGlobalRequire (str ~output-dir ~File/separator ~f))) - `(. js/goog (~'importScript_ ~f))))) + `(. js/goog (~'nodeGlobalRequire ~f))) diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 4aae3e740..4dadc5d3d 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -169,8 +169,7 @@ (defrecord RhinoEnv [] repl/IReplEnvOptions (-repl-options [this] - {:require-foreign true - :output-dir ".cljs_rhino_repl" + {:output-dir ".cljs_rhino_repl" :wrap wrap-fn}) repl/IParseStacktrace (-parse-stacktrace [this frames-str ret {output-dir :output-dir}] diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 60693dd35..aa049b92e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -153,7 +153,9 @@ (defn ^boolean array? "Returns true if x is a JavaScript array." [x] - (cljs.core/array? x)) + (if (identical? *target* "nodejs") + (.isArray js/Array x) + (instance? js/Array x))) (defn ^boolean number? "Returns true if x is a JavaScript number." @@ -221,7 +223,7 @@ s (str ty))) -;; INTERNAL - do not use +;; INTERNAL - do not use, only for Node.js (defn load-file [file] (when-not js/COMPILED (cljs.core/load-file* file))) From f661962cf3f8fe7058acba9c09ef95ac4a72b9c2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Mar 2015 13:44:19 -0400 Subject: [PATCH 0905/4033] remove busted REPL assertions --- src/clj/cljs/repl/browser.clj | 1 - src/clj/cljs/repl/nashorn.clj | 1 - src/clj/cljs/repl/node.clj | 1 - 3 files changed, 3 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 28607812a..b22a21c9b 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -553,7 +553,6 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" support reflection. Defaults to \"src/\". " [& {:as opts}] - (assert (even? (count opts)) "Arguments must be interleaved key value pairs") (repl-env* opts)) (comment diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index e3478833e..977ed2925 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -225,5 +225,4 @@ (defn repl-env "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." [& {:as opts}] - (assert (even? (count opts)) "Arguments must be interleaved key value pairs") (repl-env* opts)) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 7c4f5f9c5..1d81811fe 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -198,6 +198,5 @@ (defn repl-env "Construct a Node.js evalution environment. Can supply :host and :port." [& {:as options}] - (assert (even? (count options)) "Arguments must be interleaved key value pairs") (repl-env* options)) From 18bb41fa6982ca485161f929848a953e8895f3f9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Mar 2015 23:21:25 -0400 Subject: [PATCH 0906/4033] 0.0-3115 --- README.md | 6 +++--- changes.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9fc990b7f..44a12257a 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3058 +Latest stable release: 0.0-3115 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3058"] +[org.clojure/clojurescript "0.0-3115"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3058 org.clojure clojurescript - 0.0-3058 + 0.0-3115 ``` diff --git a/changes.md b/changes.md index f196c24a2..686dc6c3f 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,38 @@ +## 0.0-3115 + +### Enhancements +* CLJS-806: support ^:const +* CLJS-1115: Reusable repl-bootstrap! fn + +### Changes +* CLJS-667: validate extend-type and extend-protocol shape +* CLJS-1112: :repl-requires option for REPL evaluation environment +* CLJS-1111: browser REPL should have no side effects until -setup + +### Fixes +* CLJS-1085: Allow to pass test environment to cljs.test/run-all-tests +* CLJS-867: extend-type with Object methods requires multi-arity style definition +* CLJS-1118: cljs.repl/doc support for protocols +* CLJS-889: re-pattern works on strings containing \u2028 or \u2029 +* CLJS-109: Compiler errors/warnings should be displayed when cljs namespace 'package' names start with an unacceptable javascript symbol +* CLJS-891: Defs in "parent" namespaces clash with "child" namespaces with the same name? +* CLJS-813: Warn about reserved JS keyword usage in namespace names +* CLJS-876: merged sourcemap doesn't account for output-wrapper +* CLJS-1062: Incorrect deftype/defrecord definition leads to complex error messages +* CLJS-1120: analyze-deps does not appear to work when analyzing analysis caches +* CLJS-1119: constant table emission logic is incorrect +* CLJS-977: implement IKVReduce in Subvec +* CLJS-1117: Dependencies in JARs don't use cached analysis +* CLJS-689: js/-Infinity munges to _Infinity +* CLJS-1114: browser REPL script loading race condition +* CLJS-1110: cljs.closure/watch needs to print errors to *err* +* CLJS-1101 cljs.test might throw when trying to detect file-and-line +* CLJS-1090: macros imported from clojure.core missing docs +* CLJS-1108: :modules :output-to needs to create directories +* CLJS-1095: UUID to implement IComparable +* CLJS-1096: Update js/Date -equiv and -compare semantics based on Date.valueOf() value +* CLJS-1102 clojure.test should print column number of exception when available + ## 0.0-3058 ### Enhancements From badf3cdf6b78cdce8de8b31233b61d76a33b83a0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Mar 2015 09:15:14 -0400 Subject: [PATCH 0907/4033] `cljs.closure/add-implicit-options` add the implicit options for :optimizations :advanced --- src/clj/cljs/closure.clj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 61418a899..4e28fcad6 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1353,7 +1353,12 @@ should contain the source for the given namespace name." (= optimizations :none) (assoc :cache-analysis (:cache-analysis opts true) - :source-map (:source-map opts true))))) + :source-map (:source-map opts true)) + + (= optimizations :advanced) + (cond-> + (not (false? (:static-fns opts))) (assoc :static-fns true) + (not (false? (:optimize-constants opts))) (assoc :optimize-constants true))))) (defn build "Given a source which can be compiled, produce runnable JavaScript." From efec6f47fa0b0f41c0ac16583f3b392a2119a896 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 Mar 2015 09:19:36 -0400 Subject: [PATCH 0908/4033] 0.0-3117 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 44a12257a..d9c12fb6d 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3115 +Latest stable release: 0.0-3117 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3115"] +[org.clojure/clojurescript "0.0-3117"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3115 org.clojure clojurescript - 0.0-3115 + 0.0-3117 ``` diff --git a/changes.md b/changes.md index 686dc6c3f..1f665ebc8 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-3117 + +### Fixes +* CLJS-1126: File are not recompiled when build affecting options changes + ## 0.0-3115 ### Enhancements From d94e6c6e88541b8cd3d559be4281c76bb8c408d9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 16 Mar 2015 13:48:13 -0400 Subject: [PATCH 0909/4033] CLJS-1130: :foreign-libs regression under Closure optimized builds :foreign-libs support regressed, we never want to emit a goog.require for a foreign lib in any other optimization setting other than :none make the comment string clear on this point to avoid future simple errors like this one. --- src/clj/cljs/compiler.clj | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 8044a3996..8c98f4ebd 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -848,14 +848,18 @@ (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond - (and (= :nodejs (get-in @env/*compiler* [:options :target])) - (ana/foreign-dep? lib)) - ;; under node.js we load foreign libs globally - (let [{:keys [js-dependency-index options]} @env/*compiler* - ijs-url (get-in js-dependency-index [(name lib) :url])] - (emitln "cljs.core.load_file(\"" - (str (io/file (util/output-directory options) (util/get-name ijs-url))) - "\");")) + (ana/foreign-dep? lib) + (let [{:keys [target optimizations]} (get @env/*compiler* :options)] + ;; we only load foreign libraries under optimizations :none + (when (= :none optimizations) + (if (= :nodejs target) + ;; under node.js we load foreign libs globally + (let [{:keys [js-dependency-index options]} @env/*compiler* + ijs-url (get-in js-dependency-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" + (str (io/file (util/output-directory options) (util/get-name ijs-url))) + "\");")) + (emitln "goog.require('" (munge lib) "');")))) (-> libs meta :reload) (emitln "goog.require('" (munge lib) "', 'reload');") From da8d16ebd67e4604cc956d595213f3e003ea0179 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 16 Mar 2015 13:55:11 -0400 Subject: [PATCH 0910/4033] 0.0-3119 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d9c12fb6d..a91e7ff2a 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3117 +Latest stable release: 0.0-3119 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3117"] +[org.clojure/clojurescript "0.0-3119"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3117 org.clojure clojurescript - 0.0-3117 + 0.0-3119 ``` diff --git a/changes.md b/changes.md index 1f665ebc8..9ed287093 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-3119 + +### Fixes +* CLJS-1130: :foreign-libs regression under Closure optimized builds + ## 0.0-3117 ### Fixes From 7a6ce7f5f680b304a44d20692353abdef68a5592 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 16 Mar 2015 18:29:00 -0400 Subject: [PATCH 0911/4033] CLJS-1132: compile-file analysis pass optimization broken under Closure optimization and :cache-analysis true --- src/clj/cljs/closure.clj | 3 +-- src/clj/cljs/compiler.clj | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 4e28fcad6..c200d1d7a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1370,8 +1370,7 @@ should contain the source for the given namespace name." ([source opts compiler-env] (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) - all-opts (assoc (add-implicit-options opts) - :compilation-mode :non-interactive) + all-opts (add-implicit-options opts) emit-constants (or (and (= (:optimizations opts) :advanced) (not (false? (:optimize-constants opts)))) (:optimize-constants opts))] diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 8c98f4ebd..937c14ba5 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1115,8 +1115,7 @@ ;; populate compilation environment with analysis information ;; while it would seem this isn't necessary avoiding to do so damages ;; composition of smaller compilation units like expressions (i.e. REPLs) - (when (and (not= (:compilation-mode opts) :non-interactive) - (not (contains? (::ana/namespaces @env/*compiler*) ns))) + (when (not (contains? (::ana/namespaces @env/*compiler*) ns)) (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) ns-info))) (catch Exception e From 9a276fb84c135e4199929c08f55e8a986efc72b7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 16 Mar 2015 18:30:17 -0400 Subject: [PATCH 0912/4033] CLJS-1131: cljs.closure/add-dependencies needs to be more aggressively set oriented --- src/clj/cljs/closure.clj | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index c200d1d7a..29b6f2104 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -523,7 +523,7 @@ should contain the source for the given namespace name." (recur (into (rest required-files) new-req) (into visited new-req) (conj js-deps js))) - (remove nil? js-deps))))) + (disj js-deps nil))))) (comment ;; only get cljs deps @@ -539,11 +539,13 @@ should contain the source for the given namespace name." a new sequence of IJavaScript objects which includes the input list plus all dependencies in dependency order." [opts & inputs] - (let [requires (mapcat deps/-requires inputs) - required-cljs (remove (set inputs) (cljs-dependencies opts requires)) - required-js (js-dependencies opts (set (concat (mapcat deps/-requires required-cljs) requires))) - provided (mapcat deps/-provides (concat inputs required-cljs required-js)) - unprovided (clojure.set/difference (set requires) (set provided) #{"constants-table"})] + (let [inputs (set inputs) + requires (set (mapcat deps/-requires inputs)) + required-cljs (clojure.set/difference (cljs-dependencies opts requires) inputs) + required-js (js-dependencies opts + (into (set (mapcat deps/-requires required-cljs)) requires)) + provided (set (mapcat deps/-provides (clojure.set/union inputs required-cljs required-js))) + unprovided (clojure.set/difference requires provided #{"constants-table"})] (when (seq unprovided) (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) (cons From e8b0385718c916c5ea18087d93fd102bd88b22db Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 16 Mar 2015 18:42:23 -0400 Subject: [PATCH 0913/4033] tweak, only load analysis caches in compile-file if we're optimizing constants, this is what we need it for --- src/clj/cljs/compiler.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 937c14ba5..16654282d 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1113,9 +1113,9 @@ ret)) (do ;; populate compilation environment with analysis information - ;; while it would seem this isn't necessary avoiding to do so damages - ;; composition of smaller compilation units like expressions (i.e. REPLs) - (when (not (contains? (::ana/namespaces @env/*compiler*) ns)) + ;; when constants are optimized + (when (and (true? (:optimize-constants opts)) + (not (contains? (::ana/namespaces @env/*compiler*) ns))) (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) ns-info))) (catch Exception e From 704d89c3b9579ece609424b05a4d480f31de93e5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 16 Mar 2015 18:59:28 -0400 Subject: [PATCH 0914/4033] 0.0-3123 --- README.md | 6 +++--- changes.md | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a91e7ff2a..d4ed70cd4 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3119 +Latest stable release: 0.0-3123 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3119"] +[org.clojure/clojurescript "0.0-3123"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3119 org.clojure clojurescript - 0.0-3119 + 0.0-3123 ``` diff --git a/changes.md b/changes.md index 9ed287093..8f233ac8d 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,9 @@ +## 0.0-3123 + +### Fixes +* CLJS-1131: cljs.closure/add-dependencies needs to be more aggressively set oriented +* CLJS-1132: compile-file analysis pass optimization broken under Closure optimization and :cache-analysis true + ## 0.0-3119 ### Fixes From 868b6d61212a684d63846b42d858e40dabc301c8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 17 Mar 2015 09:12:58 -0400 Subject: [PATCH 0915/4033] document the rationale behind `clojure.browser.repl/bootstrap` implementation --- src/cljs/clojure/browser/repl.cljs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index b4deb040d..410a4bf53 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -122,6 +122,10 @@ (set! (.-isProvided_ js/goog) (fn [name] false)) (set! (.-writeScriptTag__ js/goog) (fn [src opt_sourceText] + ;; the page is already loaded, we can no longer leverage document.write + ;; instead construct script tag elements and append them to the body + ;; of the page, to avoid parallel script loading enforce sequential + ;; load with a simple load queue (let [loaded (atom false) onload (fn [] (when (and load-queue (false? @loaded)) @@ -130,14 +134,15 @@ (set! load-queue nil) (.apply js/goog.writeScriptTag__ nil (.shift load-queue)))))] (.appendChild js/document.body - (as-> (.createElement js/document "script") script - (doto script - (aset "type" "text/javascript") - (aset "onload" onload) - (aset "onreadystatechange" onload)) - (if (nil? opt_sourceText) - (doto script (aset "src" src)) - (doto script (gdom/setTextContext opt_sourceText)))))))) + (as-> (.createElement js/document "script") script + (doto script + (aset "type" "text/javascript") + (aset "onload" onload) + (aset "onreadystatechange" onload)) ;; IE + (if (nil? opt_sourceText) + (doto script (aset "src" src)) + (doto script (gdom/setTextContext opt_sourceText)))))))) + ;; queue or load (set! (.-writeScriptTag_ js/goog) (fn [src opt_sourceText] (if load-queue @@ -145,6 +150,8 @@ (do (set! load-queue #js []) (js/goog.writeScriptTag__ src opt_sourceText))))) + ;; we must reuse Closure library dev time dependency management, under namespace + ;; reload scenarios we simply delete entries from the correct private locations (set! (.-require js/goog) (fn [src reload] (when (= reload "reload-all") From 9c1230ec912c930239eed1a4ab0b2b22e2ff5964 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 18 Mar 2015 14:45:02 -0400 Subject: [PATCH 0916/4033] cljs.repl/repl: * need to wrap -setup call in cljs.compiler/with-core-cljs so that bootstrapping evals works --- src/clj/cljs/repl.clj | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index b081472b9..323db92c8 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -701,13 +701,15 @@ is-special-fn? (set (keys special-fns)) request-prompt (Object.) request-exit (Object.) - opts (try - (if-let [merge-opts (:merge-opts (-setup repl-env opts))] - (merge opts merge-opts) - opts) - (catch Throwable e - (caught e repl-env opts) - opts)) + opts (comp/with-core-cljs opts + (fn [] + (try + (if-let [merge-opts (:merge-opts (-setup repl-env opts))] + (merge opts merge-opts) + opts) + (catch Throwable e + (caught e repl-env opts) + opts)))) read-eval-print (fn [] (let [input (binding [*ns* (create-ns ana/*cljs-ns*) From bc6f673088af67d068c720b78e1b058ff0dd163a Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 18 Mar 2015 14:57:36 -0400 Subject: [PATCH 0917/4033] 0.0-3126 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d4ed70cd4..cf6bef4c2 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3123 +Latest stable release: 0.0-3126 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3123"] +[org.clojure/clojurescript "0.0-3126"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3123 org.clojure clojurescript - 0.0-3123 + 0.0-3126 ``` diff --git a/changes.md b/changes.md index 8f233ac8d..acd6839d2 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 0.0-3126 + +### Fixes +* Need to wrap REPL -setup calls in cljs.compiler/with-core-cljs + ## 0.0-3123 ### Fixes From ebb012172db02700410d5c9c0760a610e7148a04 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 18 Mar 2015 18:40:35 -0400 Subject: [PATCH 0918/4033] CLJS-1140: typo in cljs.repl/repl, `:need-prompt prompt` instead of `:need-prompt need-prompt` --- src/clj/cljs/repl.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 323db92c8..dd1b17e17 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -674,7 +674,8 @@ repl-opts opts {:init init - :need-prompt prompt + :prompt prompt + :need-prompt need-prompt :flush flush :read read :print print From 1208fe565b27ec30ac315f3c237b0df3d30a629d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Mar 2015 08:24:18 -0400 Subject: [PATCH 0919/4033] CLJS-1149: cljs.repl/repl needs to support :compiler-env option --- src/clj/cljs/repl.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index dd1b17e17..b4b7e8ea0 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -645,7 +645,8 @@ (defn repl* [repl-env {:keys [init need-prompt prompt flush read eval print caught reader - print-no-newline source-map-inline wrap repl-requires] + print-no-newline source-map-inline wrap repl-requires + compiler-env] :or {init #() need-prompt #(if (readers/indexing-reader? *in*) (== (readers/get-column-number *in*) 1) @@ -683,8 +684,7 @@ :reader reader :print-no-newline print-no-newline :source-map-inline source-map-inline})))] - (env/with-compiler-env - (or (::env/compiler repl-env) (env/default-compiler-env opts)) + (env/with-compiler-env (or compiler-env (env/default-compiler-env opts)) (binding [ana/*cljs-ns* 'cljs.user *cljs-verbose* repl-verbose ana/*cljs-warnings* From 4d1bbd6cfdde7411d9d286917d06a356afc12376 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Mar 2015 08:39:48 -0400 Subject: [PATCH 0920/4033] CLJS-1150: lift cljs.repl/repl cljs.user ns form evalution into default init --- src/clj/cljs/repl.clj | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index b4b7e8ea0..31506cbba 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -647,10 +647,9 @@ [repl-env {:keys [init need-prompt prompt flush read eval print caught reader print-no-newline source-map-inline wrap repl-requires compiler-env] - :or {init #() - need-prompt #(if (readers/indexing-reader? *in*) - (== (readers/get-column-number *in*) 1) - (identity true)) + :or {need-prompt #(if (readers/indexing-reader? *in*) + (== (readers/get-column-number *in*) 1) + (identity true)) prompt repl-prompt flush flush read repl-read @@ -674,8 +673,7 @@ (merge-with (fn [a b] (if (nil? b) a b)) repl-opts opts - {:init init - :prompt prompt + {:prompt prompt :need-prompt need-prompt :flush flush :read read @@ -711,6 +709,13 @@ (catch Throwable e (caught e repl-env opts) opts)))) + init (or init + #(evaluate-form repl-env env "" + (with-meta + `(~'ns ~'cljs.user + (:require ~@repl-requires)) + {:line 1 :column 1}) + identity opts)) read-eval-print (fn [] (let [input (binding [*ns* (create-ns ana/*cljs-ns*) @@ -733,15 +738,9 @@ (fn [] (binding [*repl-opts* opts] (try - (init) (when analyze-path (analyze-source analyze-path opts)) - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~'cljs.user - (:require ~@repl-requires)) - {:line 1 :column 1}) - identity opts) + (init) (catch Throwable e (caught e repl-env opts))) (when-let [src (:watch opts)] From 46cf9001b7e669a9701485f09511372d4b0b2035 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Mar 2015 08:51:33 -0400 Subject: [PATCH 0921/4033] make quit-prompt configurable --- src/clj/cljs/repl.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 31506cbba..4b54b36b8 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -623,6 +623,9 @@ (doseq [file (comp/cljs-files-in src-dir)] (ana/analyze-file (str "file://" (.getAbsolutePath file)) opts))))) +(defn repl-quit-prompt [] + (println "To quit, type:" :cljs/quit)) + (defn repl-prompt [] (print (str "ClojureScript:" ana/*cljs-ns* "> "))) @@ -644,12 +647,13 @@ (.printStackTrace e)))) (defn repl* - [repl-env {:keys [init need-prompt prompt flush read eval print caught reader + [repl-env {:keys [init need-prompt quit-prompt prompt flush read eval print caught reader print-no-newline source-map-inline wrap repl-requires compiler-env] :or {need-prompt #(if (readers/indexing-reader? *in*) (== (readers/get-column-number *in*) 1) (identity true)) + quit-prompt repl-quit-prompt prompt repl-prompt flush flush read repl-read @@ -760,7 +764,7 @@ (binding [*in* (if (true? (:source-map-inline opts)) *in* (reader))] - (print "To quit, type:" :cljs/quit) + (quit-prompt) (prompt) (flush) (loop [] From 9e680c425d300f20bd8726993d9835ced560bd87 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Mar 2015 09:08:49 -0400 Subject: [PATCH 0922/4033] CLJS-1148: ClojureScript REPL must maintain eval/print pairing direct all printing which is not the result of eval to *err*. default :bind-err to true which simply maps *err* to *out* to preserve old behavior for command line REPLs. --- src/clj/cljs/repl.clj | 57 ++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 4b54b36b8..3a2f4da5e 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -34,6 +34,10 @@ (def ^:dynamic *cljs-verbose* false) (def ^:dynamic *repl-opts* nil) +(defmacro err-out [& body] + `(binding [*out* *err*] + ~@body)) + ;; ============================================================================= ;; Copied over from clojure.main @@ -355,26 +359,27 @@ ([repl-env ret form opts] (display-error repl-env ret form (constantly nil) opts)) ([repl-env ret form f {:keys [print flush] :as opts}] - (f) - (when-let [value (:value ret)] - (print value)) - (when-let [st (:stacktrace ret)] - (if (and (true? (:source-map opts)) - (satisfies? IParseStacktrace repl-env)) - (let [cst (try - (-parse-stacktrace repl-env st ret opts) - (catch Throwable e - (when (:repl-verbose opts) - (print "Failed to canonicalize stacktrace") - (print (Throwables/getStackTraceAsString e)) - (flush))))] - (if (vector? cst) - (if (satisfies? IPrintStacktrace repl-env) - (-print-stacktrace repl-env cst ret opts) - (print-mapped-stacktrace cst opts)) - (print st))) - (print st)) - (flush)))) + (err-out + (f) + (when-let [value (:value ret)] + (print value)) + (when-let [st (:stacktrace ret)] + (if (and (true? (:source-map opts)) + (satisfies? IParseStacktrace repl-env)) + (let [cst (try + (-parse-stacktrace repl-env st ret opts) + (catch Throwable e + (when (:repl-verbose opts) + (print "Failed to canonicalize stacktrace") + (print (Throwables/getStackTraceAsString e)) + (flush))))] + (if (vector? cst) + (if (satisfies? IPrintStacktrace repl-env) + (-print-stacktrace repl-env cst ret opts) + (print-mapped-stacktrace cst opts)) + (print st))) + (print st)) + (flush))))) (defn evaluate-form "Evaluate a ClojureScript form in the JavaScript environment. Returns a @@ -425,7 +430,7 @@ (distinct (vals (:uses ast)))) opts)) (when *cljs-verbose* - ((:print opts) js)) + (err-out ((:print opts) js))) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] (case (:status ret) :error (throw @@ -649,7 +654,7 @@ (defn repl* [repl-env {:keys [init need-prompt quit-prompt prompt flush read eval print caught reader print-no-newline source-map-inline wrap repl-requires - compiler-env] + compiler-env bind-err] :or {need-prompt #(if (readers/indexing-reader? *in*) (== (readers/get-column-number *in*) 1) (identity true)) @@ -665,7 +670,8 @@ 1 "NO_SOURCE_FILE") print-no-newline print source-map-inline true - repl-requires '[[cljs.repl :refer-macros [source doc find-doc apropos dir pst]]]} + repl-requires '[[cljs.repl :refer-macros [source doc find-doc apropos dir pst]]] + bind-err true} :as opts}] (let [repl-opts (-repl-options repl-env) repl-requires (into repl-requires (:repl-requires repl-opts)) @@ -687,7 +693,8 @@ :print-no-newline print-no-newline :source-map-inline source-map-inline})))] (env/with-compiler-env (or compiler-env (env/default-compiler-env opts)) - (binding [ana/*cljs-ns* 'cljs.user + (binding [*err* (if bind-err *out* *err*) + ana/*cljs-ns* 'cljs.user *cljs-verbose* repl-verbose ana/*cljs-warnings* (assoc ana/*cljs-warnings* @@ -750,7 +757,7 @@ (when-let [src (:watch opts)] (future (let [log-file (io/file (util/output-directory opts) "watch.log")] - (print "Watch compilation log available at:" (str log-file)) + (err-out (print "Watch compilation log available at:" (str log-file))) (flush) (try (let [log-out (FileWriter. log-file)] From 6e2c8175b102eb3213dffc84cc44a9b7e1443be4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Mar 2015 17:57:32 -0400 Subject: [PATCH 0923/4033] CLJS-1137: :cljs/quit fails to actually quit in browser REPL Massive hack to avoid changing REPL logic while supporting :cljs/quit in browser REPL. Move all browser REPL and browser REPL state (agents, atoms) into dynamic vars. Do not use futures, instead use explicit Threads with binding conveyance. Do not use global agent thread pools, use custom executor service which we can shut down separately. --- src/clj/cljs/repl/browser.clj | 65 ++++++++++++++++++++++------------- src/clj/cljs/repl/server.clj | 18 +++++----- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index b22a21c9b..7e84eecee 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -17,11 +17,12 @@ [cljs.closure :as cljsc] [cljs.repl :as repl] [cljs.repl.server :as server]) - (:import [java.util.regex Pattern])) + (:import [java.util.regex Pattern] + [java.util.concurrent Executors])) -(defonce browser-state - (atom {:return-value-fn nil - :client-js nil})) +(def ^:dynamic browser-state nil) +(def ^:dynamic ordering nil) +(def ^:dynamic es nil) (defn- set-return-value-fn "Save the return value function which will be called when the next @@ -116,10 +117,8 @@ (server/dispatch-on :post (constantly true) handle-post) -(def ordering (agent {:expecting nil :fns {}})) - (defmethod handle-post :ready [_ conn _] - (send ordering (fn [_] {:expecting nil :fns {}})) + (send-via es ordering (fn [_] {:expecting nil :fns {}})) (send-for-eval conn (cljsc/-compile '[(set! *print-fn* clojure.browser.repl/repl-print) @@ -142,8 +141,8 @@ "Elements to be printed in the REPL will arrive out of order. Ensure that they are printed in the correct order." [order f] - (send-off ordering add-in-order order f) - (send-off ordering run-in-order)) + (send-via es ordering add-in-order order f) + (send-via es ordering run-in-order)) (defmethod handle-post :print [{:keys [content order]} conn _ ] (constrain-order order @@ -483,15 +482,19 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" file)) (defn setup [{:keys [working-dir] :as repl-env} opts] - (println "Compiling client js ...") - (swap! browser-state - (fn [old] - (assoc old :client-js - (create-client-js-file - repl-env (io/file working-dir "client.js"))))) - (println "Waiting for browser to connect ...") - opts - (server/start repl-env)) + (binding [browser-state (:browser-state repl-env) + ordering (:ordering repl-env) + es (:es repl-env) + server/state (:server-state repl-env)] + (repl/err-out ((:print opts) "Compiling client js ...")) + (swap! browser-state + (fn [old] + (assoc old :client-js + (create-client-js-file + repl-env (io/file working-dir "client.js"))))) + (repl/err-out ((:print opts) "Waiting for browser to connect ...")) + opts + (server/start repl-env))) (defrecord BrowserEnv [] repl/IJavaScriptEnv @@ -513,13 +516,18 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" {:ua-product (clojure.browser.repl/get-ua-product) :value (str ~e) :stacktrace (.-stack ~e)}))))) - (-evaluate [_ _ _ js] (browser-eval js)) + (-evaluate [this _ _ js] + (binding [browser-state (:browser-state this) + ordering (:ordering this) + es (:es this) + server/state (:server-state this)] + (browser-eval js))) (-load [this provides url] (load-javascript this provides url)) - (-tear-down [_] - (server/stop) - (reset! server/state {}) - (reset! browser-state {}))) + (-tear-down [this] + (binding [server/state (:server-state this)] + (server/stop)) + (.shutdown (:es this)))) (defn repl-env* [{:keys [output-dir] :as opts}] @@ -531,7 +539,16 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" :static-dir (cond-> ["." "out/"] output-dir (conj output-dir)) :preloaded-libs [] :optimizations :simple - :src "src/"} + :src "src/" + :browser-state (atom {:return-value-fn nil + :client-js nil}) + :ordering (agent {:expecting nil :fns {}}) + :es (Executors/newFixedThreadPool 16) + :server-state + (atom + {:socket nil + :connection nil + :promised-conn nil})} opts)) (defn repl-env diff --git a/src/clj/cljs/repl/server.clj b/src/clj/cljs/repl/server.clj index e26c53cd8..afccbc26e 100644 --- a/src/clj/cljs/repl/server.clj +++ b/src/clj/cljs/repl/server.clj @@ -5,11 +5,7 @@ java.io.InputStreamReader java.net.ServerSocket)) -(defonce state - (atom - {:socket nil - :connection nil - :promised-conn nil})) +(def ^:dynamic state nil) (defn connection "Promise to return a connection when one is available. If a @@ -160,16 +156,22 @@ (defn- server-loop [opts server-socket] - (let [conn (.accept server-socket)] + (when-let [conn (try (.accept server-socket) (catch Throwable _))] (.setKeepAlive conn true) - (future (handle-connection opts conn)) + (.start + (Thread. + ((ns-resolve 'clojure.core 'binding-conveyor-fn) + (fn [] (handle-connection opts conn))))) (recur opts server-socket))) (defn start "Start the server on the specified port." [opts] (let [ss (ServerSocket. (:port opts))] - (future (server-loop opts ss)) + (.start + (Thread. + ((ns-resolve 'clojure.core 'binding-conveyor-fn) + (fn [] (server-loop opts ss))))) (swap! state (fn [old] (assoc old :socket ss :port (:port opts)))))) (defn stop [] From 501a922a2fd1412503638bedc78a1f5e18b0219c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Mar 2015 18:50:45 -0400 Subject: [PATCH 0924/4033] CLJS-1155: REPL :watch support does not play nicely with :cljs/quit --- src/clj/cljs/closure.clj | 22 ++++++++++++++-------- src/clj/cljs/repl.clj | 32 +++++++++++++++++++------------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 29b6f2104..654edb2ee 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -47,6 +47,7 @@ [java.net URL] [java.util.logging Level] [java.util List Random] + [java.util.concurrent TimeUnit] [com.google.javascript.jscomp CompilerOptions CompilationLevel CompilerOptions$LanguageMode SourceMap$Format SourceMap$DetailLevel ClosureCodingConvention SourceFile @@ -1486,6 +1487,8 @@ should contain the source for the given namespace name." env/*compiler* (env/default-compiler-env opts)))) ([source opts compiler-env] + (watch source opts compiler-env nil)) + ([source opts compiler-env quit] (let [opts (cond-> opts (= (:verbose opts :not-found) :not-found) (assoc :verbose true)) @@ -1529,14 +1532,17 @@ should contain the source for the given namespace name." (println "Watching path:" source) (watch-all path) (loop [key nil] - (when (or (nil? key) (. ^WatchKey key reset)) - (let [key (. service take)] - (when (some (fn [^WatchEvent e] - (let [fstr (.. e context toString)] - (and (or (. fstr (endsWith "cljs")) - (. fstr (endsWith "js"))) - (not (. fstr (startsWith ".#")))))) - (seq (.pollEvents key))) + (when (and (or (nil? quit) (not @quit)) + (or (nil? key) (. ^WatchKey key reset))) + (let [key (. service (poll 300 TimeUnit/MILLISECONDS))] + (when (and key + (some + (fn [^WatchEvent e] + (let [fstr (.. e context toString)] + (and (or (. fstr (endsWith "cljs")) + (. fstr (endsWith "js"))) + (not (. fstr (startsWith ".#")))))) + (seq (.pollEvents key)))) (println "Change detected, recompiling ...") (flush) (buildf)) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 3a2f4da5e..4eceae27d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -691,7 +691,8 @@ :caught caught :reader reader :print-no-newline print-no-newline - :source-map-inline source-map-inline})))] + :source-map-inline source-map-inline}))) + done? (atom false)] (env/with-compiler-env (or compiler-env (env/default-compiler-env opts)) (binding [*err* (if bind-err *out* *err*) ana/*cljs-ns* 'cljs.user @@ -755,17 +756,21 @@ (catch Throwable e (caught e repl-env opts))) (when-let [src (:watch opts)] - (future - (let [log-file (io/file (util/output-directory opts) "watch.log")] - (err-out (print "Watch compilation log available at:" (str log-file))) - (flush) - (try - (let [log-out (FileWriter. log-file)] - (binding [*err* log-out - *out* log-out] - (cljsc/watch src (dissoc opts :watch)))) - (catch Throwable e - (caught e repl-env opts)))))) + (.start + (Thread. + ((ns-resolve 'clojure.core 'binding-conveyor-fn) + (fn [] + (let [log-file (io/file (util/output-directory opts) "watch.log")] + (err-out (print "Watch compilation log available at:" (str log-file))) + (flush) + (try + (let [log-out (FileWriter. log-file)] + (binding [*err* log-out + *out* log-out] + (cljsc/watch src (dissoc opts :watch) + env/*compiler* done?))) + (catch Throwable e + (caught e repl-env opts))))))))) ;; let any setup async messages flush (Thread/sleep 50) (binding [*in* (if (true? (:source-map-inline opts)) @@ -785,7 +790,8 @@ (prompt) (flush)) (recur)))))))) - (-tear-down repl-env))))) + (reset! done? true) + (-tear-down repl-env))))) (defn repl "Generic, reusable, read-eval-print loop. By default, reads from *in* using From e3169592656343cdb77db572a6bee0af2af903a5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 20 Mar 2015 15:02:49 -0400 Subject: [PATCH 0925/4033] CLJS-1154: Unmunged function names for stacktrace When source-mapping stacktraces, obtain unmunged function names from source map --- src/clj/cljs/repl.clj | 96 ++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 4eceae27d..ab8721eb4 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -224,24 +224,25 @@ (when (.exists f') (ana/parse-ns f')))) -(defn mapped-line-and-column +(defn- mapped-line-column-call "Given a cljs.source-map source map data structure map a generated line - and column back to the original line and column." + and column back to the original line, column, and function called." [source-map line column] - (let [default [line column]] + (let [default [line column nil]] ;; source maps are 0 indexed for lines (if-let [columns (get source-map (dec line))] (vec - (map inc + (map #(%1 %2) + [inc inc identity] (map ;; source maps are 0 indexed for columns ;; multiple segments may exist at column ;; the last segment seems most accurate (last - (if-let [mapping (get columns (dec column))] - mapping + (or + (get columns (last (filter #(<= % (dec column)) (sort (keys columns))))) (second (first columns)))) - [:line :col]))) + [:line :col :name]))) default))) (defn mapped-stacktrace @@ -263,42 +264,51 @@ (let [read-source-map' (memoize read-source-map) ns-info' (memoize ns-info)] (vec - (for [{:keys [function file line column] :as frame} stacktrace] - ;; need to convert file, a relative URL style path, to host-specific file - (let [no-source-file? (if-not file - true - (.startsWith file "<")) - rfile (when-not no-source-file? - (io/file (URL. (.toURL (io/file (util/output-directory opts))) file))) - [sm {:keys [ns source-file] :as ns-info}] - (when-not no-source-file? - ((juxt read-source-map' ns-info') rfile)) - [line' column'] (if ns-info - (mapped-line-and-column sm line column) - [line column]) - name' (when (and ns-info function) - function) - file' (if no-source-file? - file - (string/replace - (.getCanonicalFile - (if ns-info - source-file - (io/file rfile))) - (str (System/getProperty "user.dir") File/separator) "")) - url (or (and ns-info (io/resource (util/ns->relpath ns))) - (and file (io/resource file)))] - (merge - {:function name' - :file (if no-source-file? - (str "NO_SOURCE_FILE" - (when file - (str " " file))) - (io/file file')) - :line line' - :column column'} - (when url - {:url url})))))))) + (let [with-calls + (for [{:keys [function file line column] :as frame} stacktrace] + ;; need to convert file, a relative URL style path, to host-specific file + (let [no-source-file? (if-not file + true + (.startsWith file "<")) + rfile (when-not no-source-file? + (io/file (URL. (.toURL (io/file (util/output-directory opts))) file))) + [sm {:keys [ns source-file] :as ns-info}] + (when-not no-source-file? + ((juxt read-source-map' ns-info') rfile)) + [line' column' call] (if ns-info + (mapped-line-column-call sm line column) + [line column]) + name' (when (and ns-info function) + function) + file' (if no-source-file? + file + (string/replace + (.getCanonicalFile + (if ns-info + source-file + (io/file rfile))) + (str (System/getProperty "user.dir") File/separator) "")) + url (or (and ns-info (io/resource (util/ns->relpath ns))) + (and file (io/resource file)))] + (merge + {:function name' + :call call + :file (if no-source-file? + (str "NO_SOURCE_FILE" + (when file + (str " " file))) + (io/file file')) + :line line' + :column column'} + (when url + {:url url}))))] + ;; take each non-nil :call and merge it into :function one-level up + (map merge + (map #(dissoc % :call) with-calls) + (concat (rest (map #(if (:call %) + (hash-map :function (:call %)) + {}) + with-calls)) [{}]))))))) (defn print-mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace From 7224cd728a42cf26cac3f10452d5e10c561ab534 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Mar 2015 09:03:11 -0400 Subject: [PATCH 0926/4033] remove misleading comment --- src/clj/cljs/repl.clj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ab8721eb4..7aecba9f1 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -715,8 +715,6 @@ :undeclared-ns-form warn-on-undeclared) ana/*cljs-static-fns* static-fns *repl-opts* opts] - ;; TODO: the follow should become dead code when the REPL is - ;; sufficiently enhanced to understand :cache-analysis - David (let [env {:context :expr :locals {}} special-fns (merge default-special-fns special-fns) is-special-fn? (set (keys special-fns)) From ddede7552814b09e0ca8c74943cd1ec9d2b54940 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Mar 2015 14:14:25 -0400 Subject: [PATCH 0927/4033] CLJS-1157: Stacktrace unmunging blindly use locals Use a little discretion when replacing munged names with local (unmunged) names: Only do so if we can munge them back. The function used in merge-with in this patch makes a best effort at trying to see if we can munge back. --- src/clj/cljs/repl.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 7aecba9f1..4e89454c6 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -302,8 +302,12 @@ :column column'} (when url {:url url}))))] - ;; take each non-nil :call and merge it into :function one-level up - (map merge + ;; take each non-nil :call and optionally merge it into :function one-level up + ;; to avoid replacing with local symbols, we only replace munged name if we can munge call symbol back to it + (map #(merge-with (fn [munged-fn-name unmunged-call-name] + (if (= munged-fn-name (string/replace (cljs.compiler/munge unmunged-call-name) "." "$")) + unmunged-call-name + munged-fn-name)) %1 %2) (map #(dissoc % :call) with-calls) (concat (rest (map #(if (:call %) (hash-map :function (:call %)) From bd392de1842949ad07076f9484c069a1b5ebb294 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Mar 2015 21:08:44 -0400 Subject: [PATCH 0928/4033] CLJS-1152: (require 'some.ns :reload) causes printing to stop working in browser REPL REPL require and require-macros now canonicalizes spec into vector form, remove :reload and :reload-all from spec and instead annotate the spec vector with this information. Analyzer now checks for reload metadata on specs. If available populates reloads atom which maps ns and macro nses to reload type. handle individual ns macro reloads based on reloads map The ns AST now includes :reloads populated from reloads map in compiler emit :ns case, reload and reload-all libs based on :reloads information in ns form AST --- src/clj/cljs/analyzer.clj | 15 +++++++-- src/clj/cljs/compiler.clj | 71 ++++++++++++++++++++------------------- src/clj/cljs/repl.clj | 48 ++++++++++++++++---------- 3 files changed, 79 insertions(+), 55 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index c6ce7ef6f..fc4c10725 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1473,6 +1473,7 @@ :import (partial parse-import-spec env deps)} valid-forms (atom #{:use :use-macros :require :require-macros :import}) reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) + reloads (atom {}) {uses :use requires :require use-macros :use-macros require-macros :require-macros imports :import :as params} (reduce (fn [m [k & libs]] @@ -1481,11 +1482,16 @@ (when-not (@valid-forms k) (throw (error env (str "Only one " k " form is allowed per namespace definition")))) (swap! valid-forms disj k) + ;; check for spec type reloads (when-not (= :import k) (when (some #{:reload} libs) (swap! reload assoc k :reload)) (when (some #{:reload-all} libs) (swap! reload assoc k :reload-all))) + ;; check for individual ns reloads from REPL interactions + (when-let [xs (seq (filter #(-> % meta :reload) libs))] + (swap! reloads assoc k + (zipmap (map first xs) (map #(-> % meta :reload) xs)))) (apply merge-with merge m (map (spec-parsers k) (remove #{:reload :reload-all} libs)))) @@ -1498,13 +1504,15 @@ (when *load-macros* (load-core) (doseq [nsym (vals use-macros)] - (let [k (:use-macros @reload)] + (let [k (or (:use-macros @reload) + (get-in @reloads [:use-macros nsym]))] (if k (clojure.core/require nsym k) (clojure.core/require nsym)) (intern-macros nsym k))) (doseq [nsym (vals require-macros)] - (let [k (:require-macros @reload)] + (let [k (or (:require-macros @reload) + (get-in @reloads [:require-macros nsym]))] (if k (clojure.core/require nsym k) (clojure.core/require nsym)) @@ -1535,7 +1543,8 @@ ns-info)) ns-info)] (swap! env/*compiler* update-in [::namespaces name] merge ns-info) - (merge {:env env :op :ns :form form} + (merge {:env env :op :ns :form form + :reloads @reloads} (cond-> ns-info (@reload :use) (update-in [:uses] diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 16654282d..bcfffeb8e 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -839,46 +839,47 @@ (emit-wrap env (emits target " = " val))) (defn load-libs - ([libs] (load-libs libs nil)) - ([libs seen] - (let [loaded-libs (munge 'cljs.core.*loaded-libs*) - loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))] - (when (-> libs meta :reload-all) - (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") - (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) - (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] - (cond - (ana/foreign-dep? lib) - (let [{:keys [target optimizations]} (get @env/*compiler* :options)] - ;; we only load foreign libraries under optimizations :none - (when (= :none optimizations) - (if (= :nodejs target) - ;; under node.js we load foreign libs globally - (let [{:keys [js-dependency-index options]} @env/*compiler* - ijs-url (get-in js-dependency-index [(name lib) :url])] - (emitln "cljs.core.load_file(\"" - (str (io/file (util/output-directory options) (util/get-name ijs-url))) - "\");")) - (emitln "goog.require('" (munge lib) "');")))) - - (-> libs meta :reload) - (emitln "goog.require('" (munge lib) "', 'reload');") - - (-> libs meta :reload-all) - (emitln "goog.require('" (munge lib) "', 'reload-all');") - - :else - (emitln "goog.require('" (munge lib) "');"))) - (when (-> libs meta :reload-all) - (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))) + [libs seen reloads] + (let [loaded-libs (munge 'cljs.core.*loaded-libs*) + loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))] + (when (-> libs meta :reload-all) + (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") + (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) + (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] + (cond + (ana/foreign-dep? lib) + (let [{:keys [target optimizations]} (get @env/*compiler* :options)] + ;; we only load foreign libraries under optimizations :none + (when (= :none optimizations) + (if (= :nodejs target) + ;; under node.js we load foreign libs globally + (let [{:keys [js-dependency-index options]} @env/*compiler* + ijs-url (get-in js-dependency-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" + (str (io/file (util/output-directory options) (util/get-name ijs-url))) + "\");")) + (emitln "goog.require('" (munge lib) "');")))) + + (or (-> libs meta :reload) + (= (get reloads lib) :reload)) + (emitln "goog.require('" (munge lib) "', 'reload');") + + (or (-> libs meta :reload-all) + (= (get reloads lib) :reload-all)) + (emitln "goog.require('" (munge lib) "', 'reload-all');") + + :else + (emitln "goog.require('" (munge lib) "');"))) + (when (-> libs meta :reload-all) + (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) (defmethod emit* :ns - [{:keys [name requires uses require-macros env]}] + [{:keys [name requires uses require-macros reloads env]}] (emitln "goog.provide('" (munge name) "');") (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');")) - (load-libs requires) - (load-libs uses requires)) + (load-libs requires nil (:require reloads)) + (load-libs uses requires (:use reloads))) (defmethod emit* :deftype* [{:keys [t fields pmasks body]}] diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 4e89454c6..903d45c67 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -519,6 +519,32 @@ ((or (:wrap opts) wrap-fn) form) opts))) +(defn canonicalize-specs [specs] + (letfn [(canonicalize [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (as-> (second quoted-spec-or-kw) spec + (if (vector? spec) spec [spec]))))] + (map canonicalize specs))) + +(defn decorate-specs [specs] + (if-let [k (some #{:reload :reload-all} specs)] + (->> specs (remove #{k}) (map #(vary-meta % assoc :reload k))) + specs)) + +(comment + (canonicalize-specs + '['foo.bar '[bar.core :as bar]]) + + (canonicalize-specs + '['foo.bar '[bar.core :as bar] :reload]) + + (map meta + (decorate-specs + (canonicalize-specs + '['foo.bar '[bar.core :as bar] :reload]))) + ) + ;; Special REPL fns, these provide compatiblity with Clojure functions ;; that are not possible to reproduce given ClojureScript's compilation model ;; All functions should have the following signature @@ -578,18 +604,12 @@ (evaluate-form repl-env env "" (with-meta `(~'ns ~target-ns - (:require - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) + (:require ~@(-> specs canonicalize-specs decorate-specs))) {:merge true :line 1 :column 1}) identity opts) (when is-self-require? (set! ana/*cljs-ns* restore-ns))))) - 'import + 'require-macros (fn self ([repl-env env form] (self repl-env env form nil)) @@ -597,16 +617,10 @@ (evaluate-form repl-env env "" (with-meta `(~'ns ~ana/*cljs-ns* - (:import - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) + (:require-macros ~@(-> specs canonicalize-specs decorate-specs))) {:merge true :line 1 :column 1}) identity opts))) - 'require-macros + 'import (fn self ([repl-env env form] (self repl-env env form nil)) @@ -614,7 +628,7 @@ (evaluate-form repl-env env "" (with-meta `(~'ns ~ana/*cljs-ns* - (:require-macros + (:import ~@(map (fn [quoted-spec-or-kw] (if (keyword? quoted-spec-or-kw) From a13acb5d3bea2cc31b944c5fc163ba497417e78d Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Mar 2015 21:25:27 -0400 Subject: [PATCH 0929/4033] load-file should attempt to load a file before falling back on io/resource --- src/clj/cljs/repl.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 903d45c67..67e135780 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -474,7 +474,10 @@ ([repl-env f] (load-file repl-env f nil)) ([repl-env f opts] (if (:output-dir opts) - (let [src (if (util/url? f) f (io/resource f)) + (let [src (cond + (util/url? f) f + (.exists (io/file f)) (io/file f) + :else (io/resource f)) compiled (cljsc/compile src (assoc opts :output-file From f8fd08d76bee66ce68b4a93fb98d2271133ccbed Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Mar 2015 21:26:17 -0400 Subject: [PATCH 0930/4033] add file to make REPL load-file testing simpler --- samples/repl/src/repl/foo.cljs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 samples/repl/src/repl/foo.cljs diff --git a/samples/repl/src/repl/foo.cljs b/samples/repl/src/repl/foo.cljs new file mode 100644 index 000000000..d3de63014 --- /dev/null +++ b/samples/repl/src/repl/foo.cljs @@ -0,0 +1,4 @@ +(ns repl.foo) + +(defn bar [a b] + (+ a b)) \ No newline at end of file From eb9478cab0f08cd752c1bb7894c2b4375346600a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Mar 2015 21:45:51 -0400 Subject: [PATCH 0931/4033] CLJS-1156: load-file fails with :make-reader issue relax namespace checking, if something loaded a namesapce it's ok to refer to it, fixes interactions after load-file, no longer get false warnings load-file needs to use *repl-opts* if opts not supplied, load-file need to analyze the file in case it was already compiled. --- src/clj/cljs/analyzer.clj | 2 ++ src/clj/cljs/repl.clj | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index fc4c10725..c56558567 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -429,6 +429,8 @@ (defn confirm-ns [env ns-sym] (when (and (nil? (get '#{cljs.core goog Math goog.string} ns-sym)) (nil? (get (-> env :ns :requires) ns-sym)) + ;; something else may have loaded the namespace, i.e. load-file + (nil? (get-in @env/*compiler* [::namespaces ns-sym])) ;; macros may refer to namespaces never explicitly required ;; confirm that the library at least exists (nil? (io/resource (util/ns->relpath ns-sym)))) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 67e135780..6d0c61aba 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -471,7 +471,7 @@ ;; TODO: this should probably compile dependencies - David (defn load-file - ([repl-env f] (load-file repl-env f nil)) + ([repl-env f] (load-file repl-env f *repl-opts*)) ([repl-env f opts] (if (:output-dir opts) (let [src (cond @@ -482,6 +482,10 @@ (assoc opts :output-file (cljsc/src-file->target-file src)))] + ;; make sure it's been analyzed, this is because if it's already compiled + ;; cljs.compiler/compile-file won't do anything, good for builds, + ;; but a bit annoying here + (ana/analyze-file src opts) (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled)) (-evaluate repl-env f 1 (cljsc/src-file->goog-require src))) (binding [ana/*cljs-ns* 'cljs.user] From afde26596bfc2847d8ea612c87c866ecedd793f8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Mar 2015 11:48:08 -0400 Subject: [PATCH 0932/4033] deprecate cljs.analyzer/forms-seq instead of removing to not break existing usage. add cljs.analyzer/forms-seq* which does the right thing and accepts a _reader_. Prep work for CLJS-841 - now forms-seq* using code can close the reader itself under error conditions. --- src/clj/cljs/analyzer.clj | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index c6ce7ef6f..f93b32d8a 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1947,10 +1947,34 @@ (instance? File x) (.getAbsolutePath ^File x) :default (str x))) +(defn forms-seq* + "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally + accepts a filename argument which will be used in any emitted errors." + ([^Reader rdr] (forms-seq* rdr nil)) + ([^Reader rdr filename] + (let [pbr (readers/indexing-push-back-reader + (PushbackReader. rdr) 1 filename) + data-readers tags/*cljs-data-readers* + forms-seq_ + (fn forms-seq_ [] + (lazy-seq + (let [eof-sentinel (Object.) + form (binding [*ns* (create-ns *cljs-ns*) + reader/*data-readers* data-readers + reader/*alias-map* + (apply merge + ((juxt :requires :require-macros) + (get-namespace *cljs-ns*)))] + (reader/read pbr nil eof-sentinel))] + (if (identical? form eof-sentinel) + (.close rdr) + (cons form (forms-seq_))))))] + (forms-seq_)))) + (defn forms-seq - "Seq of Clojure/ClojureScript forms from [f], which can be anything for which -`clojure.java.io/reader` can produce a `java.io.Reader`. Optionally accepts a [filename] -argument, which the reader will use in any emitted errors." + "DEPRECATED: Seq of Clojure/ClojureScript forms from [f], which can be anything + for which `clojure.java.io/reader` can produce a `java.io.Reader`. Optionally + accepts a [filename] argument, which the reader will use in any emitted errors." ([f] (forms-seq f (source-path f))) ([f filename] (forms-seq f filename false)) ([f filename return-reader?] From f2897beccd8ad32c93658047b9439de2841533a9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 22 Mar 2015 15:44:07 -0400 Subject: [PATCH 0933/4033] CLJS-841: cljs.closure/build file locks stop using cljs.analyzer/forms-seq, instead use new cljs.analyzer/forms-seq which takes a java.io.Reader. Switch all prior usage of forms-seq to forms-seq* and wrap in with-out --- src/clj/cljs/analyzer.clj | 66 +++++++++++++++-------------- src/clj/cljs/closure.clj | 3 +- src/clj/cljs/compiler.clj | 87 ++++++++++++++++++++------------------- src/clj/cljs/repl.clj | 7 ++-- 4 files changed, 84 insertions(+), 79 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index e2ef05552..536b8d8fc 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -2043,29 +2043,30 @@ (or (when (contains? opts :load-macros) (:load-macros opts)) false)] - (loop [[forms rdr] (forms-seq src (source-path src) true)] - (if (seq forms) - (let [env (empty-env) - ast (no-warn (analyze env (first forms) nil opts))] - (if (= (:op ast) :ns) - (let [ns-name (:name ast) - deps (merge (:uses ast) (:requires ast))] - (.close ^Reader rdr) - (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:opts :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src - :ast ast} - (when (and dest (.exists ^File dest)) - {:lines (with-open [reader (io/reader dest)] - (-> reader line-seq count))}))) - (recur (rest forms)))))))] + (with-open [rdr (io/reader src)] + (loop [forms (forms-seq* rdr (source-path src))] + (if (seq forms) + (let [env (empty-env) + ast (no-warn (analyze env (first forms) nil opts))] + (if (= (:op ast) :ns) + (let [ns-name (:name ast) + deps (merge (:uses ast) (:requires ast))] + (.close ^Reader rdr) + (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:opts :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src + :ast ast} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))}))) + (recur (rest forms))))))))] ;; TODO this _was_ a reset! of the old namespaces atom; should we capture and ;; then restore the entirety of env/*compiler* here instead? (when-not (false? (:restore opts)) @@ -2161,15 +2162,16 @@ (when (or *verbose* (:verbose opts)) (util/debug-prn "Analyzing" (str res))) (let [env (assoc (empty-env) :build-options opts) - ns (loop [ns nil forms (seq (forms-seq res))] - (if forms - (let [form (first forms) - env (assoc env :ns (get-namespace *cljs-ns*)) - ast (analyze env form nil opts)] - (if (= (:op ast) :ns) - (recur (:name ast) (next forms)) - (recur ns (next forms)))) - ns))] + ns (with-open [rdr (io/reader res)] + (loop [ns nil forms (seq (forms-seq* rdr))] + (if forms + (let [form (first forms) + env (assoc env :ns (get-namespace *cljs-ns*)) + ast (analyze env form nil opts)] + (if (= (:op ast) :ns) + (recur (:name ast) (next forms)) + (recur ns (next forms)))) + ns)))] (when (and cache (true? (:cache-analysis opts))) (write-analysis-cache ns cache)))) ;; we want want to keep dependency analysis information diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 654edb2ee..756304a4c 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -357,7 +357,8 @@ (let [out-file (io/file (util/output-directory opts) output-file)] (compiled-file (comp/compile-file file out-file opts))) (binding [ana/*cljs-file* (.getPath ^File file)] - (compile-form-seq (ana/forms-seq file))))) + (with-open [rdr (io/reader file)] + (compile-form-seq (ana/forms-seq* rdr)))))) (defn compile-dir "Recursively compile all cljs files under the given source diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index bcfffeb8e..4a4b033d0 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1001,49 +1001,50 @@ :gen-col 0 :gen-line 0}))] (emitln (compiled-by-string opts)) - (loop [forms (ana/forms-seq src) - ns-name nil - deps nil] - (if (seq forms) - (let [env (ana/empty-env) - ast (ana/analyze env (first forms) nil opts)] - (emit ast) - (if (= (:op ast) :ns) - (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) - (recur (rest forms) ns-name deps))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:opts :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= (:optimizations opts) :none)) - (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" - (or (:source-map-url opts) (.getName sm-file)) - (if (true? (:source-map-timestamp opts)) - (str "?rel=" (System/currentTimeMillis)) - "")) - (spit sm-file - (sm/encode {(url-path src) (:source-map sm-data)} - {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest) - :source-map-timestamp (:source-map-timestamp opts) - :source-map-pretty-print (:source-map-pretty-print opts)})))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src (ana/parse-ns src) output-dir :write))) - ret)))))))))))) + (with-open [rdr (io/reader src)] + (loop [forms (ana/forms-seq* rdr) + ns-name nil + deps nil] + (if (seq forms) + (let [env (ana/empty-env) + ast (ana/analyze env (first forms) nil opts)] + (emit ast) + (if (= (:op ast) :ns) + (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) + (recur (rest forms) ns-name deps))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:opts :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= (:optimizations opts) :none)) + (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] + (emits "\n//# sourceMappingURL=" + (or (:source-map-url opts) (.getName sm-file)) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + "")) + (spit sm-file + (sm/encode {(url-path src) (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 2) + :file (url-path dest) + :source-map-timestamp (:source-map-timestamp opts) + :source-map-pretty-print (:source-map-pretty-print opts)})))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src (ana/parse-ns src) output-dir :write))) + ret))))))))))))) (defn requires-compilation? "Return true if the src file requires compilation." diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 6d0c61aba..04ec78a7e 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -464,9 +464,10 @@ (defn load-stream [repl-env filename res] (let [env (ana/empty-env)] - (doseq [form (ana/forms-seq res filename)] - (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*))] - (evaluate-form repl-env env filename form))))) + (with-open [rdr (io/reader res)] + (doseq [form (ana/forms-seq* rdr filename)] + (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*))] + (evaluate-form repl-env env filename form)))))) ;; TODO: this should probably compile dependencies - David From 2390b7967c048c465a9e444539d51caed6c813c2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Mar 2015 08:15:31 -0400 Subject: [PATCH 0934/4033] add Reader pre-condition to cljs.analyzer/forms-seq* --- src/clj/cljs/analyzer.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 536b8d8fc..ebc0b0754 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1963,6 +1963,7 @@ accepts a filename argument which will be used in any emitted errors." ([^Reader rdr] (forms-seq* rdr nil)) ([^Reader rdr filename] + {:pre [(instance? Reader rdr)]} (let [pbr (readers/indexing-push-back-reader (PushbackReader. rdr) 1 filename) data-readers tags/*cljs-data-readers* From 226b5125da9e545261dc3f5777efa99c1a6e8f74 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Mar 2015 08:57:30 -0400 Subject: [PATCH 0935/4033] simplify `cljs.analyzer/parse-ns` compilation environment restoration. Just use `binding` to do it. Throw if `parse-ns` called on something that does not supply an ns form. --- src/clj/cljs/analyzer.clj | 48 ++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index ebc0b0754..0eee81805 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -2029,9 +2029,10 @@ (let [src (if (symbol? src) (io/resource (util/ns->relpath src)) src) - namespaces' (::namespaces @env/*compiler*) - ret - (binding [*cljs-ns* 'cljs.user + compiler-env @env/*compiler* + [ijs compiler-env'] + (binding [env/*compiler* (atom compiler-env) + *cljs-ns* 'cljs.user *macro-infer* (or (when (contains? opts :macro-infer) (:macro-infer opts)) @@ -2053,26 +2054,27 @@ (let [ns-name (:name ast) deps (merge (:uses ast) (:requires ast))] (.close ^Reader rdr) - (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:opts :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src - :ast ast} - (when (and dest (.exists ^File dest)) - {:lines (with-open [reader (io/reader dest)] - (-> reader line-seq count))}))) - (recur (rest forms))))))))] - ;; TODO this _was_ a reset! of the old namespaces atom; should we capture and - ;; then restore the entirety of env/*compiler* here instead? - (when-not (false? (:restore opts)) - (swap! env/*compiler* assoc ::namespaces namespaces')) - ret)))) + [(merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in compiler-env [:opts :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src + :ast ast} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))})) + @env/*compiler*]) + (recur (rest forms)))) + (throw (AssertionError. (str "No ns form found in " src)))))))] + (when (false? (:restore opts)) + (swap! env/*compiler* update-in [::namespaces] merge + (get compiler-env' ::namespaces))) + ijs)))) (defn cache-file "Given a ClojureScript source file returns the read/write path to the analysis From 3811756e16e8c39e6b061660dda18021fc4bc7c2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Mar 2015 09:10:54 -0400 Subject: [PATCH 0936/4033] remove inconsistent usage of :opts over :options key in the compiler environment --- src/clj/cljs/analyzer.clj | 2 +- src/clj/cljs/closure.clj | 4 ++-- src/clj/cljs/compiler.clj | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0eee81805..82267d9bd 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -2060,7 +2060,7 @@ :requires (if (= ns-name 'cljs.core) (set (vals deps)) (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in compiler-env [:opts :emit-constants]) + (get-in compiler-env [:options :emit-constants]) (conj 'constants-table))) :file dest :source-file src diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 756304a4c..4e4cf72de 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -561,7 +561,7 @@ should contain the source for the given namespace name." (javascript-file foreign url provides requires) js-map))) required-js) - [(when (-> @env/*compiler* :opts :emit-constants) + [(when (-> @env/*compiler* :options :emit-constants) (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] required-cljs @@ -1386,7 +1386,7 @@ should contain the source for the given namespace name." (check-node-target opts) (swap! compiler-env #(-> % - (assoc-in [:opts :emit-constants] emit-constants) + (assoc-in [:options :emit-constants] emit-constants) (assoc :target (:target opts)) (assoc :js-dependency-index (deps/js-dependency-index all-opts)))) (binding [comp/*dependents* (when-not (false? (:recompile-dependents opts)) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 4a4b033d0..e8ebba11c 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -197,7 +197,7 @@ (emits ")"))) (defmethod emit-constant clojure.lang.Keyword [x] - (if (-> @env/*compiler* :opts :emit-constants) + (if (-> @env/*compiler* :options :emit-constants) (let [value (-> @env/*compiler* ::ana/constant-table x)] (emits "cljs.core." value)) (emits-keyword x) @@ -1019,7 +1019,7 @@ :requires (if (= ns-name 'cljs.core) (set (vals deps)) (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:opts :emit-constants]) + (get-in @env/*compiler* [:options :emit-constants]) (conj 'constants-table))) :file dest :source-file src} From 4a3737bbe8de30081c9aef1ec15f8aae9065a1b6 Mon Sep 17 00:00:00 2001 From: Chas Emerick Date: Mon, 23 Mar 2015 13:04:44 -0400 Subject: [PATCH 0937/4033] CLJS-1161: actually print error stack traces to *err*, allow higher-level rebindings of *cljs-ns* --- src/clj/cljs/repl.clj | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 04ec78a7e..a32c1aeab 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -489,7 +489,7 @@ (ana/analyze-file src opts) (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled)) (-evaluate repl-env f 1 (cljsc/src-file->goog-require src))) - (binding [ana/*cljs-ns* 'cljs.user] + (binding [ana/*cljs-ns* ana/*cljs-ns*] (let [res (if (= File/separatorChar (first f)) f (io/resource f))] (assert res (str "Can't find " f " in classpath")) (load-stream repl-env f res)))))) @@ -684,8 +684,7 @@ #(prn "Error evaluating:" form :as js) (constantly nil)) opts))) - (binding [*out* *err*] - (.printStackTrace e)))) + (.printStackTrace e *err*))) (defn repl* [repl-env {:keys [init need-prompt quit-prompt prompt flush read eval print caught reader @@ -731,7 +730,7 @@ done? (atom false)] (env/with-compiler-env (or compiler-env (env/default-compiler-env opts)) (binding [*err* (if bind-err *out* *err*) - ana/*cljs-ns* 'cljs.user + ana/*cljs-ns* ana/*cljs-ns* *cljs-verbose* repl-verbose ana/*cljs-warnings* (assoc ana/*cljs-warnings* From b9d6a75a2693037f95abd4c211d9df0aa3e99251 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 23 Mar 2015 17:04:34 -0400 Subject: [PATCH 0938/4033] CLJS-1162: Failure to printStackTrace when REPL initialized --- src/clj/cljs/repl.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index a32c1aeab..3b73b5f22 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -24,7 +24,7 @@ [cljs.tagged-literals :as tags] [cljs.closure :as cljsc] [cljs.source-map :as sm]) - (:import [java.io File PushbackReader FileWriter] + (:import [java.io File PushbackReader FileWriter PrintWriter] [java.net URL] [javax.xml.bind DatatypeConverter] [clojure.lang IExceptionInfo] @@ -729,7 +729,10 @@ :source-map-inline source-map-inline}))) done? (atom false)] (env/with-compiler-env (or compiler-env (env/default-compiler-env opts)) - (binding [*err* (if bind-err *out* *err*) + (binding [*err* (if bind-err + (cond-> *out* + (not (instance? PrintWriter *out*)) (PrintWriter.)) + *err*) ana/*cljs-ns* ana/*cljs-ns* *cljs-verbose* repl-verbose ana/*cljs-warnings* From fc8dfa4cd2010cda5079d5f7967b3ef5b93dcb0a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Mar 2015 22:52:28 -0400 Subject: [PATCH 0939/4033] CLJS-1019: REPL source map caching support --- src/clj/cljs/analyzer.clj | 10 +--- src/clj/cljs/repl.clj | 111 ++++++++++++++++++++------------------ src/clj/cljs/util.clj | 8 +++ 3 files changed, 67 insertions(+), 62 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 82267d9bd..f7126e92d 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -2091,14 +2091,6 @@ core-cache (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn"))))) -(defn last-modified [src] - (cond - (instance? File src) (.lastModified ^File src) - (instance? URL src) (.getLastModified (.openConnection ^URL src)) - :else - (throw - (IllegalArgumentException. (str "Cannot get last modified for " src))))) - (defn requires-analysis? ([src] (requires-analysis? src "out")) ([src output-dir] @@ -2118,7 +2110,7 @@ (let [out-src (util/to-target-file output-dir (parse-ns src))] (if (not (.exists out-src)) true - (if (> (last-modified src) (last-modified cache)) + (if (> (util/last-modified src) (util/last-modified cache)) true (let [version' (util/compiled-by-version cache) version (util/clojurescript-version)] diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 3b73b5f22..a9014e91a 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -205,7 +205,14 @@ [f] (let [smf (io/file (str f ".map"))] (when (.exists smf) - (sm/decode (json/read-str (slurp smf) :key-fn keyword))))) + (as-> @env/*compiler* compiler-env + (let [t (util/last-modified smf)] + (if (> t (get-in compiler-env [::source-maps f :last-modified] 0)) + (swap! env/*compiler* assoc-in [::source-maps f] + {:last-modified t + :source-map (sm/decode (json/read-str (slurp smf) :key-fn keyword))}) + compiler-env)) + (get-in compiler-env [::source-maps f :source-map]))))) (defn ^File js-src->cljs-src "Map a JavaScript output file back to the original ClojureScript source @@ -261,58 +268,56 @@ from the classpath." ([stacktrace] (mapped-stacktrace stacktrace nil)) ([stacktrace opts] - (let [read-source-map' (memoize read-source-map) - ns-info' (memoize ns-info)] - (vec - (let [with-calls - (for [{:keys [function file line column] :as frame} stacktrace] - ;; need to convert file, a relative URL style path, to host-specific file - (let [no-source-file? (if-not file - true - (.startsWith file "<")) - rfile (when-not no-source-file? - (io/file (URL. (.toURL (io/file (util/output-directory opts))) file))) - [sm {:keys [ns source-file] :as ns-info}] - (when-not no-source-file? - ((juxt read-source-map' ns-info') rfile)) - [line' column' call] (if ns-info - (mapped-line-column-call sm line column) - [line column]) - name' (when (and ns-info function) - function) - file' (if no-source-file? - file - (string/replace - (.getCanonicalFile - (if ns-info - source-file - (io/file rfile))) - (str (System/getProperty "user.dir") File/separator) "")) - url (or (and ns-info (io/resource (util/ns->relpath ns))) - (and file (io/resource file)))] - (merge - {:function name' - :call call - :file (if no-source-file? - (str "NO_SOURCE_FILE" - (when file - (str " " file))) - (io/file file')) - :line line' - :column column'} - (when url - {:url url}))))] - ;; take each non-nil :call and optionally merge it into :function one-level up - ;; to avoid replacing with local symbols, we only replace munged name if we can munge call symbol back to it - (map #(merge-with (fn [munged-fn-name unmunged-call-name] - (if (= munged-fn-name (string/replace (cljs.compiler/munge unmunged-call-name) "." "$")) - unmunged-call-name - munged-fn-name)) %1 %2) - (map #(dissoc % :call) with-calls) - (concat (rest (map #(if (:call %) - (hash-map :function (:call %)) - {}) - with-calls)) [{}]))))))) + (vec + (let [with-calls + (for [{:keys [function file line column] :as frame} stacktrace] + ;; need to convert file, a relative URL style path, to host-specific file + (let [no-source-file? (if-not file + true + (.startsWith file "<")) + rfile (when-not no-source-file? + (io/file (URL. (.toURL (io/file (util/output-directory opts))) file))) + [sm {:keys [ns source-file] :as ns-info}] + (when-not no-source-file? + ((juxt read-source-map ns-info) rfile)) + [line' column' call] (if ns-info + (mapped-line-column-call sm line column) + [line column]) + name' (when (and ns-info function) + function) + file' (if no-source-file? + file + (string/replace + (.getCanonicalFile + (if ns-info + source-file + (io/file rfile))) + (str (System/getProperty "user.dir") File/separator) "")) + url (or (and ns-info (io/resource (util/ns->relpath ns))) + (and file (io/resource file)))] + (merge + {:function name' + :call call + :file (if no-source-file? + (str "NO_SOURCE_FILE" + (when file + (str " " file))) + (io/file file')) + :line line' + :column column'} + (when url + {:url url}))))] + ;; take each non-nil :call and optionally merge it into :function one-level up + ;; to avoid replacing with local symbols, we only replace munged name if we can munge call symbol back to it + (map #(merge-with (fn [munged-fn-name unmunged-call-name] + (if (= munged-fn-name (string/replace (cljs.compiler/munge unmunged-call-name) "." "$")) + unmunged-call-name + munged-fn-name)) %1 %2) + (map #(dissoc % :call) with-calls) + (concat (rest (map #(if (:call %) + (hash-map :function (:call %)) + {}) + with-calls)) [{}])))))) (defn print-mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index cb32e2b66..41540e659 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -113,6 +113,14 @@ (filename x) (last (string/split (path x) #"\/")))) +(defn last-modified [src] + (cond + (file? src) (.lastModified ^File src) + (url? src) (.getLastModified (.openConnection ^URL src)) + :else + (throw + (IllegalArgumentException. (str "Cannot get last modified for " src))))) + (defn topo-sort ([x get-deps] (topo-sort x 0 (atom (sorted-map)) (memoize get-deps))) From 1dc5b89932651315041f07eed1f71abf68a2ed78 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 24 Mar 2015 00:06:33 -0400 Subject: [PATCH 0940/4033] if source map enabled in REPL attempt to read AOTed source map on another thread when starting up to eliminate lag on the first user error --- src/clj/cljs/repl.clj | 32 ++++++++++++++++++-------------- src/clj/cljs/util.clj | 4 ++++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index a9014e91a..5506b8efd 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -200,20 +200,6 @@ (doseq [ns (distinct requires)] (load-namespace repl-env ns opts)))) -(defn read-source-map - "Return the source map for the JavaScript source file." - [f] - (let [smf (io/file (str f ".map"))] - (when (.exists smf) - (as-> @env/*compiler* compiler-env - (let [t (util/last-modified smf)] - (if (> t (get-in compiler-env [::source-maps f :last-modified] 0)) - (swap! env/*compiler* assoc-in [::source-maps f] - {:last-modified t - :source-map (sm/decode (json/read-str (slurp smf) :key-fn keyword))}) - compiler-env)) - (get-in compiler-env [::source-maps f :source-map]))))) - (defn ^File js-src->cljs-src "Map a JavaScript output file back to the original ClojureScript source file." @@ -223,6 +209,22 @@ name (.getName f)] (io/file dir (string/replace name ".js" ".cljs")))) +(defn read-source-map + "Return the source map for the JavaScript source file." + [f] + (when-let [smf (util/file-or-resource (str f ".map"))] + (let [ns (if (= f "cljs/core.aot.js") + 'cljs.core + (:ns (ana/parse-ns (js-src->cljs-src f))))] + (as-> @env/*compiler* compiler-env + (let [t (util/last-modified smf)] + (if (> t (get-in compiler-env [::source-maps ns :last-modified] 0)) + (swap! env/*compiler* assoc-in [::source-maps ns] + {:last-modified t + :source-map (sm/decode (json/read-str (slurp smf) :key-fn keyword))}) + compiler-env)) + (get-in compiler-env [::source-maps ns :source-map]))))) + (defn ns-info "Given a path to a js source file return the ns info for the corresponding ClojureScript file if it exists." @@ -734,6 +736,8 @@ :source-map-inline source-map-inline}))) done? (atom false)] (env/with-compiler-env (or compiler-env (env/default-compiler-env opts)) + (when (:source-map opts) + (.start (Thread. (bound-fn [] (read-source-map "cljs/core.aot.js"))))) (binding [*err* (if bind-err (cond-> *out* (not (instance? PrintWriter *out*)) (PrintWriter.)) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 41540e659..892918327 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -121,6 +121,10 @@ (throw (IllegalArgumentException. (str "Cannot get last modified for " src))))) +(defn file-or-resource [s] + (or (and (.exists (io/file s)) (io/file s)) + (io/resource s))) + (defn topo-sort ([x get-deps] (topo-sort x 0 (atom (sorted-map)) (memoize get-deps))) From 314efee135fb8b993451b15632ccd94156767026 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 24 Mar 2015 07:17:00 -0400 Subject: [PATCH 0941/4033] special case cljs.core source map caching for now --- src/clj/cljs/repl.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5506b8efd..d1f5f2ccc 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -218,7 +218,10 @@ (:ns (ana/parse-ns (js-src->cljs-src f))))] (as-> @env/*compiler* compiler-env (let [t (util/last-modified smf)] - (if (> t (get-in compiler-env [::source-maps ns :last-modified] 0)) + (if (or (and (= ns 'cljs.core) + (nil? (get-in compiler-env [::source-maps ns]))) + (and (not= ns 'cljs.core) + (> t (get-in compiler-env [::source-maps ns :last-modified] 0)))) (swap! env/*compiler* assoc-in [::source-maps ns] {:last-modified t :source-map (sm/decode (json/read-str (slurp smf) :key-fn keyword))}) From 726ec27faab48f6996d04b98c960f10d4a9a72fb Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 25 Mar 2015 15:44:23 -0400 Subject: [PATCH 0942/4033] don't use print unless printing the result of eval --- src/clj/cljs/repl.clj | 26 ++++++++++++-------------- src/clj/cljs/repl/browser.clj | 4 ++-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index d1f5f2ccc..0639f656f 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -331,9 +331,10 @@ ([stacktrace opts] (doseq [{:keys [function file line column]} (mapped-stacktrace stacktrace opts)] - ((:print opts) "\t" - (str (when function (str function " ")) - "(" file (when line (str ":" line)) (when column (str ":" column)) ")"))))) + (err-out + (println "\t" + (str (when function (str function " ")) + "(" file (when line (str ":" line)) (when column (str ":" column)) ")")))))) (comment (cljsc/build "samples/hello/src" @@ -382,11 +383,11 @@ (defn- display-error ([repl-env ret form opts] (display-error repl-env ret form (constantly nil) opts)) - ([repl-env ret form f {:keys [print flush] :as opts}] + ([repl-env ret form f opts] (err-out (f) (when-let [value (:value ret)] - (print value)) + (println value)) (when-let [st (:stacktrace ret)] (if (and (true? (:source-map opts)) (satisfies? IParseStacktrace repl-env)) @@ -394,16 +395,14 @@ (-parse-stacktrace repl-env st ret opts) (catch Throwable e (when (:repl-verbose opts) - (print "Failed to canonicalize stacktrace") - (print (Throwables/getStackTraceAsString e)) - (flush))))] + (println "Failed to canonicalize stacktrace") + (println (Throwables/getStackTraceAsString e)))))] (if (vector? cst) (if (satisfies? IPrintStacktrace repl-env) (-print-stacktrace repl-env cst ret opts) (print-mapped-stacktrace cst opts)) - (print st))) - (print st)) - (flush))))) + (println st))) + (println st)))))) (defn evaluate-form "Evaluate a ClojureScript form in the JavaScript environment. Returns a @@ -454,7 +453,7 @@ (distinct (vals (:uses ast)))) opts)) (when *cljs-verbose* - (err-out ((:print opts) js))) + (err-out (println js))) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] (case (:status ret) :error (throw @@ -809,8 +808,7 @@ ((ns-resolve 'clojure.core 'binding-conveyor-fn) (fn [] (let [log-file (io/file (util/output-directory opts) "watch.log")] - (err-out (print "Watch compilation log available at:" (str log-file))) - (flush) + (err-out (println "Watch compilation log available at:" (str log-file))) (try (let [log-out (FileWriter. log-file)] (binding [*err* log-out diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 7e84eecee..308c36fa1 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -486,13 +486,13 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" ordering (:ordering repl-env) es (:es repl-env) server/state (:server-state repl-env)] - (repl/err-out ((:print opts) "Compiling client js ...")) + (repl/err-out (println "Compiling client js ...")) (swap! browser-state (fn [old] (assoc old :client-js (create-client-js-file repl-env (io/file working-dir "client.js"))))) - (repl/err-out ((:print opts) "Waiting for browser to connect ...")) + (repl/err-out (println "Waiting for browser to connect ...")) opts (server/start repl-env))) From b17edea679fab22bafb949f0cffb01c1c55d33c9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Mar 2015 22:16:08 -0400 Subject: [PATCH 0943/4033] CLJS-1169: cannot use REPL load-file on files that declare single segment namespaces cljs.util/to-target-file: * don't add parents to path if there aren't any, this covers single segment namespaces --- src/clj/cljs/util.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 892918327..d19e69caf 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -71,8 +71,9 @@ (let [relative-path (string/split (munge-path (str (:ns ns-info))) #"\.") parents (cond-> (butlast relative-path) target-dir (conj target-dir))] - (io/file (io/file (to-path parents)) - (str (last relative-path) (str "." ext)))))) + (cond->> (io/file (str (last relative-path) (str "." ext))) + (seq parents) + (io/file (to-path parents)))))) (defn mkdirs "Create all parent directories for the passed file." From 258a4a572faf735d3c2c7dde31eacf4555549cac Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Mar 2015 22:34:51 -0400 Subject: [PATCH 0944/4033] map clojure.core/in-ns to REPL in-ns special for existing tools --- src/clj/cljs/repl.clj | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 0639f656f..5d0950ac7 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -594,20 +594,22 @@ ([repl-env env form] (self repl-env env form nil)) ([repl-env env [_ file :as form] opts] - (load-file repl-env file opts)))] - {'in-ns - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ [quote ns-name] :as form] _] - ;; guard against craziness like '5 which wreaks havoc - (when-not (and (= quote 'quote) (symbol? ns-name)) - (throw (IllegalArgumentException. "Argument to in-ns must be a symbol."))) - (when-not (ana/get-namespace ns-name) - (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) - (-evaluate repl-env "" 1 - (str "goog.provide('" (comp/munge ns-name) "');"))) - (set! ana/*cljs-ns* ns-name))) + (load-file repl-env file opts))) + in-ns-fn + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ [quote ns-name] :as form] _] + ;; guard against craziness like '5 which wreaks havoc + (when-not (and (= quote 'quote) (symbol? ns-name)) + (throw (IllegalArgumentException. "Argument to in-ns must be a symbol."))) + (when-not (ana/get-namespace ns-name) + (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) + (-evaluate repl-env "" 1 + (str "goog.provide('" (comp/munge ns-name) "');"))) + (set! ana/*cljs-ns* ns-name)))] + {'in-ns in-ns-fn + 'clojure.core/in-ns in-ns-fn 'require (fn self ([repl-env env form] From f58861149dac8a8e61e34414903ed43c01acbe91 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 05:20:40 -0400 Subject: [PATCH 0945/4033] add macroexpand and macroexpand-1 macros --- src/clj/cljs/core.clj | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 214c4cf75..37511042c 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -34,7 +34,7 @@ cond-> cond->> as-> some-> some->> - if-some when-some test ns-interns var vswap!]) + if-some when-some test ns-interns var vswap! macroexpand-1 macroexpand]) (:require clojure.walk clojure.set cljs.compiler @@ -1972,3 +1972,13 @@ ;; INTERNAL - do not use, only for Node.js (defmacro load-file* [f] `(. js/goog (~'nodeGlobalRequire ~f))) + +(defmacro macroexpand-1 [[quote form]] + `(quote ~(ana/macroexpand-1 &env form))) + +(defmacro macroexpand [[quote form]] + (core/let [env &env] + (core/loop [form' (ana/macroexpand-1 env form)] + (core/if-not (core/identical? form form') + (recur (ana/macroexpand-1 env form)) + form')))) From 3e8a98da1a7712ba5a4ca7add1a52a085c9d1538 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 05:28:44 -0400 Subject: [PATCH 0946/4033] add docstrings & validation to macroexpand & macroexpand-1 --- src/clj/cljs/core.clj | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 37511042c..4f3b12c3f 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1973,11 +1973,24 @@ (defmacro load-file* [f] `(. js/goog (~'nodeGlobalRequire ~f))) -(defmacro macroexpand-1 [[quote form]] - `(quote ~(ana/macroexpand-1 &env form))) - -(defmacro macroexpand [[quote form]] - (core/let [env &env] +(defmacro macroexpand-1 + "If form represents a macro form, returns its expansion, + else returns form." + [quoted] + (core/assert (core/= (core/first quoted) 'quote) + "Argument to macroexpand-1 must be quoted") + (core/let [form (second quoted)] + `(quote ~(ana/macroexpand-1 &env form)))) + +(defmacro macroexpand + "Repeatedly calls macroexpand-1 on form until it no longer + represents a macro form, then returns it. Note neither + macroexpand-1 nor macroexpand expand macros in subforms." + [quoted] + (core/assert (core/= (core/first quoted) 'quote) + "Argument to macroexpand must be quoted") + (core/let [form (second quoted) + env &env] (core/loop [form' (ana/macroexpand-1 env form)] (core/if-not (core/identical? form form') (recur (ana/macroexpand-1 env form)) From c6f63a24ecd33c6c39281a90fcb3d22246f78ba1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 05:50:13 -0400 Subject: [PATCH 0947/4033] fix typo in macroexpand --- src/clj/cljs/core.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 4f3b12c3f..abaf21035 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1993,5 +1993,5 @@ env &env] (core/loop [form' (ana/macroexpand-1 env form)] (core/if-not (core/identical? form form') - (recur (ana/macroexpand-1 env form)) + (recur (ana/macroexpand-1 env form')) form')))) From 4ea27c308dff87a81966df3116b43cae444cf8d1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 05:57:13 -0400 Subject: [PATCH 0948/4033] fix macroexpand --- src/clj/cljs/core.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index abaf21035..a7891b500 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1991,7 +1991,7 @@ "Argument to macroexpand must be quoted") (core/let [form (second quoted) env &env] - (core/loop [form' (ana/macroexpand-1 env form)] + (core/loop [form form form' (ana/macroexpand-1 env form)] (core/if-not (core/identical? form form') - (recur (ana/macroexpand-1 env form')) - form')))) + (recur form' (ana/macroexpand-1 env form')) + `(quote ~form'))))) From a8809d7030a82943d40094aeffa84c0f573630e9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 06:01:35 -0400 Subject: [PATCH 0949/4033] CLJS-1171: map clojure.repl/doc, clojure.repl/source, clojure.repl/dir map clojure.repl -> cljs.repl --- src/clj/cljs/analyzer.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index f7126e92d..367fa097c 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1782,6 +1782,7 @@ (if-let [nstr (namespace sym)] (when-let [ns (cond (= "clojure.core" nstr) (find-ns 'cljs.core) + (= "clojure.repl" nstr) (find-ns 'cljs.repl) (.contains nstr ".") (find-ns (symbol nstr)) :else (some-> env :ns :require-macros (get (symbol nstr)) find-ns))] From 55b47934a8db669cafc9dda0e35eb3b26ddefdf3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 06:08:38 -0400 Subject: [PATCH 0950/4033] All grown up now, less noisy prompt --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 5d0950ac7..6fd2323de 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -679,7 +679,7 @@ (println "To quit, type:" :cljs/quit)) (defn repl-prompt [] - (print (str "ClojureScript:" ana/*cljs-ns* "> "))) + (print (str ana/*cljs-ns* "=> "))) (defn repl-caught [e repl-env opts] (if (and (instance? IExceptionInfo e) From 46a399547417db288cde795ac426b0a48c59c6ed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 06:20:47 -0400 Subject: [PATCH 0951/4033] specify :main for uberjar --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 708822a96..64eef5692 100644 --- a/project.clj +++ b/project.clj @@ -15,7 +15,7 @@ [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} - :uberjar {:aot :all}} + :uberjar {:aot :all :main clojure.main}} :aliases {"test-all" ["with-profile" "test,1.5:test,1.6" "test"] "check-all" ["with-profile" "1.5:1.6" "check"]} :min-lein-version "2.0.0") From 9d7ee61070ba50dce283dea2b4c5caceaf8002cc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 06:21:31 -0400 Subject: [PATCH 0952/4033] CLJ-1172: supply main entry points for all standard REPLs --- src/clj/cljs/repl/browser.clj | 3 +++ src/clj/cljs/repl/nashorn.clj | 3 +++ src/clj/cljs/repl/node.clj | 2 ++ src/clj/cljs/repl/rhino.clj | 3 +++ 4 files changed, 11 insertions(+) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 308c36fa1..a625183e6 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -572,6 +572,9 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" [& {:as opts}] (repl-env* opts)) +(defn -main [] + (repl/repl (repl-env))) + (comment (require '[cljs.repl :as repl]) diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index 977ed2925..d2d9cd4af 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -226,3 +226,6 @@ "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." [& {:as opts}] (repl-env* opts)) + +(defn -main [] + (repl/repl (repl-env))) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 1d81811fe..fef26265b 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -200,3 +200,5 @@ [& {:as options}] (repl-env* options)) +(defn -main [] + (repl/repl (repl-env))) \ No newline at end of file diff --git a/src/clj/cljs/repl/rhino.clj b/src/clj/cljs/repl/rhino.clj index 4dadc5d3d..d144af37d 100644 --- a/src/clj/cljs/repl/rhino.clj +++ b/src/clj/cljs/repl/rhino.clj @@ -224,6 +224,9 @@ [& {:as opts}] (repl-env* opts)) +(defn -main [] + (repl/repl (repl-env))) + (comment (repl/-parse-stacktrace (repl-env) From 4eebd45bd82f40c8e656d97ee996ed91c48a3ec5 Mon Sep 17 00:00:00 2001 From: Joel Holdbrooks Date: Wed, 18 Mar 2015 10:04:29 -0700 Subject: [PATCH 0953/4033] CLJS-1144 - expose defaul-dispatch-val and dispatch-fn multifn accessors --- src/cljs/cljs/core.cljs | 14 +++++++++++++- test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index aa049b92e..5be2a0d3a 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -9242,7 +9242,9 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-prefer-method [mf dispatch-val dispatch-val-y]) (-get-method [mf dispatch-val]) (-methods [mf]) - (-prefers [mf])) + (-prefers [mf]) + (-default-dispatch-val [mf]) + (-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)))) @@ -9424,6 +9426,8 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-methods [mf] @method-table) (-prefers [mf] @prefer-table) + (-default-dispatch-val [mf] default-dispatch-val) + (-dispatch-fn [mf] dispatch-fn) INamed (-name [this] (-name name)) @@ -9461,6 +9465,14 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." "Given a multimethod, returns a map of preferred value -> set of other values" [multifn] (-prefers multifn)) +(defn default-dispatch-val + "Given a multimethod, return it's default-dispatch-val." + [multifn] (-default-dispatch-val multifn)) + +(defn dispatch-fn + "Given a multimethod, return it's dispatch-fn." + [multifn] (-dispatch-fn multifn)) + ;; UUID (deftype UUID [uuid] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 51270fc19..aef1ad2a9 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2714,6 +2714,13 @@ (is (= 12345 (:baz m))) (is (= "String Metadata" (:whatever m))))) +(defmulti cljs-1144 identity :default ::default) + +(deftest test-cljs-1144 + (is (not= map (dispatch-fn cljs-1144))) + (is (= identity (dispatch-fn cljs-1144))) + (is (= ::default (default-dispatch-val cljs-1144)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 9cddd7581c5cdacfe4f4f20274aed61cd3cfc7cb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Mar 2015 08:39:11 -0400 Subject: [PATCH 0954/4033] CLJS-808: Warning from `find-classpath-lib` mistakenly included in generated source --- src/clj/cljs/js_deps.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index 24b0fb2e1..bb647d32e 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -303,5 +303,9 @@ JavaScript library containing provide/require 'declarations'." (let [{:keys [provides] :as lib-info} (library-graph-node lib-resource)] (if (some #{(name lib)} provides) lib-info - (println (format "WARNING: JavaScript file found on classpath for library `%s`, but does not contain a corresponding `goog.provide` declaration: %s" - lib lib-resource)))))) + (binding [*out* *err*] + (println + (format + (str "WARNING: JavaScript file found on classpath for library `%s`, " + "but does not contain a corresponding `goog.provide` declaration: %s") + lib lib-resource))))))) From a7f2a85d9df15e912dff71a8c95529bcfe459882 Mon Sep 17 00:00:00 2001 From: Chas Emerick Date: Sat, 28 Mar 2015 06:07:58 -0400 Subject: [PATCH 0955/4033] CLJS-1176: redirect node REPL output through *out* and *err*, not System/out, System/err --- src/clj/cljs/repl/node.clj | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index fef26265b..b70efc480 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -17,8 +17,9 @@ [clojure.data.json :as json]) (:import java.net.Socket java.lang.StringBuilder - [java.io File BufferedReader BufferedWriter] - [java.lang ProcessBuilder ProcessBuilder$Redirect])) + [java.io File BufferedReader BufferedWriter InputStream + Writer InputStreamReader IOException] + [java.lang ProcessBuilder Process])) (defn socket [host port] (let [socket (Socket. host port) @@ -77,6 +78,20 @@ (defn platform-path [v] (str "path.join.apply(null, " (seq->js-array v) ")")) +(defn- pipe [^Process proc in ^Writer out] + ;; we really do want system-default encoding here + (with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)] + (loop [buf (char-array 1024)] + (when (.isAlive proc) + (try + (let [len (.read in buf)] + (when-not (neg? len) + (.write out buf 0 len))) + (catch IOException e + (when (and (.isAlive proc) (not (.contains (.getMessage e) "Stream closed"))) + (.printStackTrace e *err*)))) + (recur buf))))) + (defn setup ([repl-env] (setup repl-env nil)) ([repl-env opts] @@ -87,12 +102,11 @@ (string/replace (slurp (io/resource "cljs/repl/node_repl.js")) "var PORT = 5001;" (str "var PORT = " (:port repl-env) ";"))) - bldr (ProcessBuilder. (into-array [(get opts :node-command "node")])) - _ (-> bldr + proc (-> (ProcessBuilder. (into-array [(get opts :node-command "node")])) (.redirectInput of) - (.redirectOutput ProcessBuilder$Redirect/INHERIT) - (.redirectError ProcessBuilder$Redirect/INHERIT)) - proc (.start bldr) + .start) + _ (do (future (pipe proc (.getInputStream proc) *out*)) + (future (pipe proc (.getErrorStream proc) *err*))) env (ana/empty-env) core (io/resource "cljs/core.cljs") ;; represent paths as vectors so we can emit JS arrays, this is to From 1cc1818a3c4a3517c963159d91634882e2eed6dd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 Mar 2015 13:06:45 -0400 Subject: [PATCH 0956/4033] don't use future, prevents :cljs/quit, use Thread + bound-fn --- src/clj/cljs/repl/node.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index b70efc480..6b8c695b4 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -105,8 +105,8 @@ proc (-> (ProcessBuilder. (into-array [(get opts :node-command "node")])) (.redirectInput of) .start) - _ (do (future (pipe proc (.getInputStream proc) *out*)) - (future (pipe proc (.getErrorStream proc) *err*))) + _ (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) *out*)))) + (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) *err*))))) env (ana/empty-env) core (io/resource "cljs/core.cljs") ;; represent paths as vectors so we can emit JS arrays, this is to From 380c8b19c5fada55c40759063a4a23332bc7fdf8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 Mar 2015 13:29:01 -0400 Subject: [PATCH 0957/4033] CLJS-1175: CLJS defmulti doesn't exhibit same defonce behavior as Clojure's defmulti, suggesting an even better reloading behavior defmulti now expands to defonce not def add ns-unmap macro to support interactions with defonce --- src/clj/cljs/core.clj | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index a7891b500..23918e78c 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -34,7 +34,7 @@ cond-> cond->> as-> some-> some->> - if-some when-some test ns-interns var vswap! macroexpand-1 macroexpand]) + if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand]) (:require clojure.walk clojure.set cljs.compiler @@ -1855,7 +1855,7 @@ (let [options (apply core/hash-map options) default (core/get options :default :default)] (check-valid-options options :default :hierarchy) - `(def ~(with-meta mm-name m) + `(defonce ~(with-meta mm-name m) (let [method-table# (atom {}) prefer-table# (atom {}) method-cache# (atom {}) @@ -1962,6 +1962,15 @@ `[(symbol ~(name sym)) (var ~(symbol (name ns) (name sym)))]) (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))])) +(defmacro ns-unmap + "Removes the mappings for the symbol from the namespace." + [[quote0 ns] [quote1 sym]] + (core/assert (core/and (= quote0 'quote) (core/symbol? ns) + (= quote1 'quote) (core/symbol? sym)) + "Arguments to ns-unmap must be quoted symbols") + (swap! env/*compiler* update-in [::ana/namespaces ns :defs] dissoc sym) + `(js-delete ~(cljs.compiler/munge ns) ~(cljs.compiler/munge (core/str sym)))) + (defmacro vswap! "Non-atomically swaps the value of the volatile as if: (apply f current-value-of-vol args). Returns the value that From 5b847b9da7997f30f42aa82f4f9225012035ae81 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 31 Mar 2015 08:31:55 -0400 Subject: [PATCH 0958/4033] CLJS-1179: strange load-file behavior cljs.repl/load-namespace: - sym can actually be a string as well, rename to ns cljs.repl/load-file: - need to load dependencies before loading the compiled file --- src/clj/cljs/repl.clj | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 6fd2323de..d40950e1d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -167,23 +167,23 @@ "Load a namespace and all of its dependencies into the evaluation environment. The environment is responsible for ensuring that each namespace is loaded once and only once." - ([repl-env sym] (load-namespace repl-env sym nil)) - ([repl-env sym opts] - (let [sym (if (and (seq? sym) - (= (first sym) 'quote)) - (second sym) - sym) + ([repl-env ns] (load-namespace repl-env ns nil)) + ([repl-env ns opts] + (let [ns (if (and (seq? ns) + (= (first ns) 'quote)) + (second ns) + ns) ;; TODO: add pre-condition to source-on-disk, the ;; source must supply at least :url - David - sources (cljsc/add-dependencies - (merge (env->opts repl-env) opts) - {:requires [(name sym)] :type :seed - :url (:uri (cljsc/source-for-namespace - sym env/*compiler*))}) - deps (->> sources - (remove (comp #{["goog"]} :provides)) - (remove (comp #{:seed} :type)) - (map #(select-keys % [:provides :url])))] + sources (cljsc/add-dependencies + (merge (env->opts repl-env) opts) + {:requires [(name ns)] :type :seed + :url (:uri (cljsc/source-for-namespace + ns env/*compiler*))}) + deps (->> sources + (remove (comp #{["goog"]} :provides)) + (remove (comp #{:seed} :type)) + (map #(select-keys % [:provides :url])))] (if (:output-dir opts) ;; REPLs that read from :output-dir just need to add deps, ;; environment will handle actual loading - David @@ -478,8 +478,6 @@ (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*))] (evaluate-form repl-env env filename form)))))) -;; TODO: this should probably compile dependencies - David - (defn load-file ([repl-env f] (load-file repl-env f *repl-opts*)) ([repl-env f opts] @@ -492,6 +490,8 @@ (assoc opts :output-file (cljsc/src-file->target-file src)))] + ;; need to load dependencies first + (load-dependencies repl-env (:requires compiled) opts) ;; make sure it's been analyzed, this is because if it's already compiled ;; cljs.compiler/compile-file won't do anything, good for builds, ;; but a bit annoying here From d12d678d9babb7d9a164e11997f084eeeb378c16 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 1 Apr 2015 07:01:46 -0400 Subject: [PATCH 0959/4033] CLJS-1182: semantics of load-file should be require + implicit :reload --- src/clj/cljs/closure.clj | 6 ++++-- src/clj/cljs/repl.clj | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 4e4cf72de..a15fcd459 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1582,7 +1582,7 @@ should contain the source for the given namespace name." (defn ^String src-file->goog-require ([src] (src-file->goog-require src {:wrap true})) - ([src {:keys [wrap all-provides :as options]}] + ([src {:keys [wrap all-provides] :as options}] (let [goog-ns (case (util/ext src) "cljs" (comp/munge (:ns (ana/parse-ns src))) @@ -1592,7 +1592,9 @@ should contain the source for the given namespace name." (IllegalArgumentException. (str "Can't create goog.require expression for " src))))] (if (and (not all-provides) wrap) - (str "goog.require(\"" goog-ns "\");") + (if (:reload options) + (str "goog.require(\"" goog-ns "\", true);") + (str "goog.require(\"" goog-ns "\");")) (if (vector? goog-ns) goog-ns (str goog-ns)))))) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index d40950e1d..88a59770c 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -497,7 +497,8 @@ ;; but a bit annoying here (ana/analyze-file src opts) (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled)) - (-evaluate repl-env f 1 (cljsc/src-file->goog-require src))) + (-evaluate repl-env f 1 + (cljsc/src-file->goog-require src {:wrap true :reload true}))) (binding [ana/*cljs-ns* ana/*cljs-ns*] (let [res (if (= File/separatorChar (first f)) f (io/resource f))] (assert res (str "Can't find " f " in classpath")) From 6c446bce4ffe367ad4b32e60f5139f201651bf29 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2015 18:41:45 -0400 Subject: [PATCH 0960/4033] CLJS-1183: load-file doesn't copy source to output directory --- src/clj/cljs/repl.clj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 88a59770c..e87dbfeff 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -490,6 +490,12 @@ (assoc opts :output-file (cljsc/src-file->target-file src)))] + ;; copy over the original source file if source maps enabled + (when-let [ns (and (:source-map opts) (first (:provides compiled)))] + (spit + (io/file (io/file (util/output-directory opts)) + (util/ns->relpath ns)) + (slurp src))) ;; need to load dependencies first (load-dependencies repl-env (:requires compiled) opts) ;; make sure it's been analyzed, this is because if it's already compiled From 49ed83337baf762ef21a907be234ebdbcc105d66 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Apr 2015 06:49:17 -0400 Subject: [PATCH 0961/4033] CLJS-1187: var ast contains internal nodes with bad analysis :context ensure that internal var AST nodes are analyzed in :expr contexts --- src/clj/cljs/analyzer.clj | 9 +++++---- test/cljs/cljs/core_test.cljs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 367fa097c..34f0e8efc 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -655,10 +655,11 @@ (defmethod parse 'var [op env [_ sym :as form] _ _] - (let [var (resolve-var env sym (confirm-var-exists-throw))] + (let [var (resolve-var env sym (confirm-var-exists-throw)) + expr-env (assoc env :context :expr)] {:env env :op :var-special :form form - :var (analyze env sym) - :sym (analyze env `(quote ~(symbol (name (:ns var)) (name (:name var))))) + :var (analyze expr-env sym) + :sym (analyze expr-env `(quote ~(symbol (name (:ns var)) (name (:name var))))) :meta (let [ks [:ns :doc :file :line :column] m (merge (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) @@ -669,7 +670,7 @@ uks (keys user-meta)] (zipmap uks (map #(list 'quote (get user-meta %)) uks))))] - (analyze env m))})) + (analyze expr-env m))})) (defmethod parse 'if [op env [_ test then else :as form] name _] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index aef1ad2a9..4dd7197d3 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2721,6 +2721,21 @@ (is (= identity (dispatch-fn cljs-1144))) (is (= ::default (default-dispatch-val cljs-1144)))) +(defn foo-1187 [] (print "foo!")) + +(defn bar-1187 [] (print "bar!")) + +(defn print-foo-1187 [fb] + (apply + (case fb + :foo #'foo-1187 + :bar #'bar-1187) [])) + +(deftest test-cljs-1187 [] + (testing "Internal var nodes analyzed in expression context" + (is (= (with-out-str (print-foo-1187 :foo)) + "foo!")))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 343573f5bf748aec6943d71e85e9c9a36dcfb529 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Apr 2015 08:02:17 -0400 Subject: [PATCH 0962/4033] CLJS-1184: log module building activity under verbose --- src/clj/cljs/closure.clj | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index a15fcd459..9fa6abb38 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -631,7 +631,7 @@ should contain the source for the given namespace name." (fn [modules module-name] (if-not (= module-name :cljs-base) (update-in modules [module-name :depends-on] - (fnil conj #{}) :cljs-base) + (fnil identity #{:cljs-base})) modules)) (update-in modules [:cljs-base :output-to] (fnil io/file @@ -701,6 +701,8 @@ should contain the source for the given namespace name." (fn [[sources ret] [name {:keys [entries output-to depends-on] :as module-desc}]] (assert (or (= name :cljs-base) (not (empty? entries))) (str "Module " name " does not define any :entries")) + (when (and (:verbose opts) (not= name :cljs-base)) + (util/debug-prn "Building module" name)) (let [js-module (JSModule. (clojure.core/name name)) [sources' module-sources] ;; compute inputs for a closure module @@ -723,6 +725,8 @@ should contain the source for the given namespace name." foreign-deps (atom [])] ;; add inputs to module (doseq [ijs module-sources] + (when (:verbose opts) + (util/debug-prn " adding entry" (:provides ijs))) (if-not (deps/-foreign? ijs) (.add js-module ^SourceFile (js-source-file (javascript-name ijs) ijs)) @@ -731,7 +735,10 @@ should contain the source for the given namespace name." ;; since modules are already in dependency order (doseq [dep depends-on] (if-let [parent-module (get-in (into {} ret) [dep :closure-module])] - (.addDependency js-module ^JSModule parent-module) + (do + (when (:verbose opts) + (util/debug-prn " module" name "depends on" dep)) + (.addDependency js-module ^JSModule parent-module)) (throw (IllegalArgumentException. (str "Parent module " dep " does not exist"))))) [sources' @@ -742,8 +749,12 @@ should contain the source for the given namespace name." [sources []] (sort-modules (add-cljs-base-module (:modules opts) opts))) cljs-base-closure-module (get-in (into {} modules) [:cljs-base :closure-module]) foreign-deps (atom [])] + (when (:verbose opts) + (util/debug-prn "Building module" :cljs-base)) ;; add anything left to :cljs-base module (doseq [source sources'] + (when (:verbose opts) + (util/debug-prn " adding entry" (:provides source))) (if-not (deps/-foreign? source) (.add ^JSModule cljs-base-closure-module (js-source-file (javascript-name source) source)) From 815dde62ef06e591444d5deec8f2425890f57a1f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Apr 2015 18:21:45 -0400 Subject: [PATCH 0963/4033] CLJS-1189: array-map will return PersistentHashMap if applied to more than (.-HASHMAP-THRESHOLD PersistentArrayMap) pairs make array helpers for PAM easier to reuse in other contexts. Fix array-map and the PersistentArrayMap.fromArray helper to preserve the type instead of converting to PHM. --- src/cljs/cljs/core.cljs | 70 ++++++++++++++++++++--------------- test/cljs/cljs/core_test.cljs | 6 +++ 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 5be2a0d3a..9a03cbce4 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -5484,7 +5484,7 @@ reduces them without incurring seq initialization" ;;; PersistentArrayMap -(defn- array-map-index-of-nil? [arr m k] +(defn- array-index-of-nil? [arr] (let [len (alength arr)] (loop [i 0] (cond @@ -5492,7 +5492,7 @@ reduces them without incurring seq initialization" (nil? (aget arr i)) i :else (recur (+ i 2)))))) -(defn- array-map-index-of-keyword? [arr m k] +(defn- array-index-of-keyword? [arr k] (let [len (alength arr) kstr (.-fqn k)] (loop [i 0] @@ -5503,7 +5503,7 @@ reduces them without incurring seq initialization" (identical? kstr (.-fqn k')))) i :else (recur (+ i 2)))))) -(defn- array-map-index-of-symbol? [arr m k] +(defn- array-index-of-symbol? [arr k] (let [len (alength arr) kstr (.-str k)] (loop [i 0] @@ -5514,7 +5514,7 @@ reduces them without incurring seq initialization" (identical? kstr (.-str k')))) i :else (recur (+ i 2)))))) -(defn- array-map-index-of-identical? [arr m k] +(defn- array-index-of-identical? [arr k] (let [len (alength arr)] (loop [i 0] (cond @@ -5522,7 +5522,7 @@ reduces them without incurring seq initialization" (identical? k (aget arr i)) i :else (recur (+ i 2)))))) -(defn- array-map-index-of-equiv? [arr m k] +(defn- array-index-of-equiv? [arr k] (let [len (alength arr)] (loop [i 0] (cond @@ -5530,24 +5530,25 @@ reduces them without incurring seq initialization" (= k (aget arr i)) i :else (recur (+ i 2)))))) -(defn- array-map-index-of [m k] - (let [arr (.-arr m)] - (cond - (keyword? k) (array-map-index-of-keyword? arr m k) +(defn array-index-of [arr k] + (cond + (keyword? k) (array-index-of-keyword? arr k) - (or ^boolean (goog/isString k) (number? k)) - (array-map-index-of-identical? arr m k) + (or ^boolean (goog/isString k) (number? k)) + (array-index-of-identical? arr k) - (symbol? k) (array-map-index-of-symbol? arr m k) + (symbol? k) (array-index-of-symbol? arr k) - (nil? k) - (array-map-index-of-nil? arr m k) + (nil? k) + (array-index-of-nil? arr) - :else (array-map-index-of-equiv? arr m k)))) + :else (array-index-of-equiv? arr k))) -(defn- array-map-extend-kv [m k v] - (let [arr (.-arr m) - l (alength arr) +(defn- array-map-index-of [m k] + (array-index-of (.-arr m) k)) + +(defn- array-extend-kv [arr k v] + (let [l (alength arr) narr (make-array (+ l 2))] (loop [i 0] (when (< i l) @@ -5557,6 +5558,9 @@ reduces them without incurring seq initialization" (aset narr (inc l) v) narr)) +(defn- array-map-extend-kv [m k v] + (array-extend-kv (.-arr m) k v)) + (declare TransientArrayMap) (deftype PersistentArrayMapSeq [arr i _meta] @@ -5793,17 +5797,22 @@ reduces them without incurring seq initialization" (set! (.-fromArray PersistentArrayMap) (fn [arr ^boolean no-clone ^boolean no-check] - (let [arr (if no-clone arr (aclone arr))] + (as-> (if no-clone arr (aclone arr)) arr (if no-check - (let [cnt (/ (alength arr) 2)] - (PersistentArrayMap. nil cnt arr nil)) - (let [len (alength arr)] - (loop [i 0 - ret (transient (.-EMPTY PersistentArrayMap))] - (if (< i len) - (recur (+ i 2) - (-assoc! ret (aget arr i) (aget arr (inc i)))) - (-persistent! ret)))))))) + arr + (let [ret (array)] + (loop [i 0] + (when (< i (alength arr)) + (let [k (aget arr i) + v (aget arr (inc i)) + idx (array-index-of ret k)] + (when (== idx -1) + (.push ret k) + (.push ret v))) + (recur (+ i 2)))) + ret)) + (let [cnt (/ (alength arr) 2)] + (PersistentArrayMap. nil cnt arr nil))))) (es6-iterable PersistentArrayMap) @@ -7439,7 +7448,10 @@ reduces them without incurring seq initialization" "keyval => key val Returns a new array map with supplied mappings." [& keyvals] - (.fromArray cljs.core/PersistentArrayMap (apply array keyvals) true false)) + (let [arr (if (instance? IndexedSeq keyvals) + (.-arr keyvals) + (into-array keyvals))] + (.fromArray cljs.core/PersistentArrayMap arr true false))) (defn obj-map "keyval => key val diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 4dd7197d3..cb48e277f 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2736,6 +2736,12 @@ (is (= (with-out-str (print-foo-1187 :foo)) "foo!")))) +(deftest test-cljs-1189 [] + (testing "array-map should always return array maps" + (let [am (apply array-map (range 100))] + (is (== (count am) 50)) + (is (instance? PersistentArrayMap am))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From a594ded23a2824611d7ea0f3811a920004c08104 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Apr 2015 18:48:26 -0400 Subject: [PATCH 0964/4033] refactor compiler tests ns --- test/clj/cljs/compiler_tests.clj | 44 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/test/clj/cljs/compiler_tests.clj b/test/clj/cljs/compiler_tests.clj index 63b9cd5c3..f92d39d53 100644 --- a/test/clj/cljs/compiler_tests.clj +++ b/test/clj/cljs/compiler_tests.clj @@ -1,13 +1,13 @@ (ns cljs.compiler-tests (:use clojure.test) - (:require [cljs.analyzer :as a]) - (:require [cljs.compiler :as c]) - (:require [cljs.env :as e]) - (:require [cljs.util :as util] - [cljs.compiler :as comp]) - (:import (java.io File))) + (:require [cljs.analyzer :as ana] + [cljs.compiler :as comp] + [cljs.env :as env] + [cljs.util :as util]) + (:import [java.io File])) -(def ns-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)) +(def aenv (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) +(def cenv (env/default-compiler-env)) (deftest should-recompile (let [src (File. "test/hello.cljs") @@ -15,33 +15,33 @@ opt {:optimize-constants true} optmod {:optimize-constants true :elide-asserts false}] (with-redefs [util/*clojurescript-version* {:major 0 :minor 0 :qualifier 42}] - (e/with-compiler-env (e/default-compiler-env) + (env/with-compiler-env (env/default-compiler-env) (.setLastModified dst (- (.lastModified src) 100)) - (is (c/requires-compilation? src dst opt)) - (c/compile-file src dst opt) - (is (not (c/requires-compilation? src dst opt))) - (is (c/requires-compilation? src dst optmod)) - (c/compile-file src dst optmod) - (is (not (c/requires-compilation? src dst optmod))))))) + (is (comp/requires-compilation? src dst opt)) + (comp/compile-file src dst opt) + (is (not (comp/requires-compilation? src dst opt))) + (is (comp/requires-compilation? src dst optmod)) + (comp/compile-file src dst optmod) + (is (not (comp/requires-compilation? src dst optmod))))))) (deftest fn-scope-munge - (is (= (c/munge + (is (= (comp/munge (get-in - (a/analyze ns-env + (ana/analyze aenv '(defn foo [] (fn bar []))) [:init :name])) 'cljs$user$foo)) - (is (= (c/munge + (is (= (comp/munge (get-in - (a/analyze ns-env + (ana/analyze aenv '(defn foo [] (fn bar []))) [:init :children 0 :children 0 :name])) 'cljs$user$foo_$_bar)) - (is (= (c/munge + (is (= (comp/munge (get-in - (a/analyze ns-env + (ana/analyze aenv '(fn [] (fn console []))) [:children 0 :children 0 :name])) @@ -49,6 +49,6 @@ (deftest test-js-negative-infinity (= (with-out-str - (c/emit - (a/analyze (assoc ns-env :context :expr) 'js/-Infinity))) + (comp/emit + (ana/analyze (assoc aenv :context :expr) 'js/-Infinity))) "-Infinity")) From 9bf486b22cebf5aba4154d07f3ad52e990ddc305 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 5 Apr 2015 14:57:44 -0400 Subject: [PATCH 0965/4033] cljs.test needs to default to sync, remove broken validation --- src/cljs/cljs/test.cljs | 63 ++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index d679baa19..75aaf4c34 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -443,16 +443,14 @@ (update-current-env! [:testing-vars] conj v) (update-current-env! [:report-counters :test] inc) (do-report {:type :begin-test-var :var v}) - (let [{:keys [async-disabled]} (get-current-env)] - (cond-> (try - (t) - (catch :default e - (do-report - {:type :error - :message "Uncaught exception, not in assertion." - :expected nil - :actual e}))) - async-disabled (-> async? not (assert async-disabled))))) + (try + (t) + (catch :default e + (do-report + {:type :error + :message "Uncaught exception, not in assertion." + :expected nil + :actual e})))) (fn [] (do-report {:type :end-test-var :var v}) (update-current-env! [:testing-vars] rest))])) @@ -493,28 +491,22 @@ block (reverse (keep :after map-fixtures)))) -(defn- fixtures-type - [coll] - (cond (empty? coll) - :none - (every? map? coll) - :map - (every? fn? coll) - :fn)) - -(defn- execution-strategy - [once-fixtures each-fixtures] - (let [types (map fixtures-type [once-fixtures each-fixtures]) - _ (assert (not-any? nil? types) - "Fixtures may not be of mixed types") - types (->> types - (remove #{:none}) - (distinct)) - _ (assert (> 2 (count types)) "fixtures specified in :once and :each must be of the same type")] - (case (first types) - :map :async - :fn :sync - nil :async))) +(defn- execution-strategy [once each] + (letfn [(fixtures-type [coll] + (cond + (empty? coll) :none + (every? map? coll) :map + (every? fn? coll) :fn)) + (fixtures-types [] + (->> (map fixtures-type [once each]) + (remove #{:none}) + (distinct)))] + (let [[type :as types] (fixtures-types)] + (assert (not-any? nil? types) + "Fixtures may not be of mixed types") + (assert (> 2 (count types)) + "fixtures specified in :once and :each must be of the same type") + ({:map :async :fn :sync} type :sync)))) (defn test-vars-block "Like test-vars, but returns a block for further composition and @@ -536,9 +528,6 @@ (wrap-map-fixtures once-fixtures)) :sync (do - (update-current-env! [:async-disabled] - (constantly - "Async tests require fixtures to be specified as maps")) (let [each-fixture-fn (join-fixtures each-fixtures)] [(fn [] ((join-fixtures once-fixtures) @@ -547,9 +536,7 @@ (when (:test (meta v)) (each-fixture-fn (fn [] - (test-var v))))))) - (update-current-env! [:async-disabled] - (constantly nil)))]))))))) + (test-var v))))))))]))))))) (group-by (comp :ns meta) vars))) (defn test-vars From 576fb6e054dd50ec458a3c9e4172a5a0002c7aea Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Apr 2015 19:00:31 -0400 Subject: [PATCH 0966/4033] CLJS-1188: multi-arity fns hinder cross-module code motion This patch makes all top level function definitions completely static, we never issue an invoke to produce a top-level function value. This is accomplished by duplicating and further enhancing the fn emission logic in cljs.compiler at the macro level. The enhancements are entirely around eliminating invokes and any property aliasing. While useful in expression contexts, at the top level both of these approaches in cljs.compiler defeat cross module code motion. - test-simple should clean builds - cljs.analyzer * remove :method info, never used * read fn information from :top-fn meta if available - cljs.closure * enhance module build reporting - cljs.core * move clojure.core/defn macro + helpers directly into macro ns * handle top level multi-arity & variadic fns - cjls.compiler-tests * include some examples --- script/test-simple | 2 +- src/clj/cljs/analyzer.clj | 41 ++--- src/clj/cljs/closure.clj | 4 +- src/clj/cljs/core.clj | 292 ++++++++++++++++++++++++++++--- test/clj/cljs/compiler_tests.clj | 13 ++ 5 files changed, 298 insertions(+), 54 deletions(-) diff --git a/script/test-simple b/script/test-simple index 0a24d29f4..9c766c695 100755 --- a/script/test-simple +++ b/script/test-simple @@ -1,7 +1,7 @@ #!/bin/sh # stop blowing compiled stuff -#rm -rf builds/out-simp +rm -rf builds/out-simp mkdir -p builds/out-simp possible=4 diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 34f0e8efc..0cda188fe 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -864,22 +864,19 @@ :impls #{}}) (when fn-var? (let [params (map #(vec (map :name (:params %))) (:methods init-expr))] - {:fn-var true - ;; protocol implementation context - :protocol-impl (:protocol-impl init-expr) - ;; inline protocol implementation context - :protocol-inline (:protocol-inline init-expr) - :variadic (:variadic init-expr) - :max-fixed-arity (:max-fixed-arity init-expr) - :method-params params - :arglists (:arglists sym-meta) - :arglists-meta (doall (map meta (:arglists sym-meta))) - :methods (map (fn [method] - (let [tag (infer-tag env (assoc method :op :method))] - (cond-> (select-keys method - [:max-fixed-arity :variadic]) - tag (assoc :tag tag)))) - (:methods init-expr))}) ) + (merge + {:fn-var true + ;; protocol implementation context + :protocol-impl (:protocol-impl init-expr) + ;; inline protocol implementation context + :protocol-inline (:protocol-inline init-expr)} + (if-let [top-fn-meta (:top-fn sym-meta)] + top-fn-meta + {:variadic (:variadic init-expr) + :max-fixed-arity (:max-fixed-arity init-expr) + :method-params params + :arglists (:arglists sym-meta) + :arglists-meta (doall (map meta (:arglists sym-meta)))}))) ) (when (and fn-var? tag) {:ret-tag tag}))) (merge @@ -978,8 +975,7 @@ :fn-var true :variadic variadic :max-fixed-arity max-fixed-arity - :method-params (map :params methods) - :methods methods) + :method-params (map :params methods)) locals) methods (if name ;; a second pass with knowledge of our function-ness/arity @@ -1027,8 +1023,7 @@ :shadow (locals n) :variadic (:variadic fexpr) :max-fixed-arity (:max-fixed-arity fexpr) - :method-params (map :params (:methods fexpr)) - :methods (:methods fexpr)} + :method-params (map :params (:methods fexpr))} ret-tag (assoc :ret-tag ret-tag))] [(assoc-in env [:locals n] be) (conj bes be)])) @@ -1043,8 +1038,7 @@ :init fexpr :variadic (:variadic fexpr) :max-fixed-arity (:max-fixed-arity fexpr) - :method-params (map :params (:methods fexpr)) - :methods (:methods fexpr))] + :method-params (map :params (:methods fexpr)))] [(assoc-in env [:locals name] be') (conj bes be')])) [meth-env []] bes) @@ -1102,8 +1096,7 @@ {:fn-var true :variadic (:variadic init-expr) :max-fixed-arity (:max-fixed-arity init-expr) - :method-params (map :params (:methods init-expr)) - :methods (:methods init-expr)}) + :method-params (map :params (:methods init-expr))}) be)] (recur (conj bes be) (assoc-in env [:locals name] be) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 9fa6abb38..e2b195a3a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -701,7 +701,7 @@ should contain the source for the given namespace name." (fn [[sources ret] [name {:keys [entries output-to depends-on] :as module-desc}]] (assert (or (= name :cljs-base) (not (empty? entries))) (str "Module " name " does not define any :entries")) - (when (and (:verbose opts) (not= name :cljs-base)) + (when (:verbose opts) (util/debug-prn "Building module" name)) (let [js-module (JSModule. (clojure.core/name name)) [sources' module-sources] @@ -750,7 +750,7 @@ should contain the source for the given namespace name." cljs-base-closure-module (get-in (into {} modules) [:cljs-base :closure-module]) foreign-deps (atom [])] (when (:verbose opts) - (util/debug-prn "Building module" :cljs-base)) + (util/debug-prn "Adding remaining namespaces to" :cljs-base)) ;; add anything left to :cljs-base module (doseq [source sources'] (when (:verbose opts) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 23918e78c..928547852 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -38,6 +38,7 @@ (:require clojure.walk clojure.set cljs.compiler + [cljs.util :as util] [cljs.env :as env]) (:import [java.io File])) @@ -74,7 +75,7 @@ (import-macros clojure.core [-> ->> .. assert comment cond - declare defn defn- + declare defn- doto extend-protocol fn for if-let if-not letfn @@ -83,6 +84,54 @@ cond-> cond->> as-> some-> some->> if-some when-some]) +(defn- ^{:dynamic true} assert-valid-fdecl + "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn." + [fdecl] + (when (empty? fdecl) (throw (IllegalArgumentException. + "Parameter declaration missing"))) + (core/let [argdecls (map + #(if (seq? %) + (first %) + (throw (IllegalArgumentException. + (if (seq? (first fdecl)) + (core/str "Invalid signature \"" + % + "\" should be a list") + (core/str "Parameter declaration \"" + % + "\" should be a vector"))))) + fdecl) + bad-args (seq (remove #(vector? %) argdecls))] + (when bad-args + (throw (IllegalArgumentException. + (core/str "Parameter declaration \"" (first bad-args) + "\" should be a vector")))))) + +(def + ^{:private true} + sigs + (fn [fdecl] + (assert-valid-fdecl fdecl) + (core/let [asig + (fn [fdecl] + (core/let [arglist (first fdecl) + ;elide implicit macro args + arglist (if (clojure.lang.Util/equals '&form (first arglist)) + (clojure.lang.RT/subvec arglist 2 (clojure.lang.RT/count arglist)) + arglist) + body (next fdecl)] + (if (map? (first body)) + (if (next body) + (with-meta arglist (conj (if (meta arglist) (meta arglist) {}) (first body))) + arglist) + arglist)))] + (if (seq? (first fdecl)) + (core/loop [ret [] fdecls fdecl] + (if fdecls + (recur (conj ret (asig (first fdecls))) (next fdecls)) + (seq ret))) + (core/list (asig fdecl)))))) + (defmacro defonce [x init] `(when-not (exists? ~x) (def ~x ~init))) @@ -95,7 +144,7 @@ (when more (list* `assert-args fnname more))))) -(defn destructure [bindings] +(core/defn destructure [bindings] (core/let [bents (partition 2 bindings) pb (fn pb [bvec b v] (core/let [pvec @@ -227,10 +276,10 @@ (apply core/str))] (list* 'js* (core/str "[" strs "].join('')") xs))) -(defn bool-expr [e] +(defn- bool-expr [e] (vary-meta e assoc :tag 'boolean)) -(defn simple-test-expr? [env ast] +(defn- simple-test-expr? [env ast] (core/and (#{:var :invoke :constant :dot :js} (:op ast)) ('#{boolean seq} (cljs.analyzer/infer-tag env ast)))) @@ -607,7 +656,7 @@ ;;; end of reducers macros -(defn protocol-prefix [psym] +(defn- protocol-prefix [psym] (core/str (-> (core/str psym) (.replace \. \$) (.replace \/ \$)) "$")) (def #^:private base-type @@ -708,10 +757,10 @@ `(let [~name (js-this)] ~@body)) -(defn to-property [sym] +(defn- to-property [sym] (symbol (core/str "-" sym))) -(defn warn-and-update-protocol [p type env] +(defn- warn-and-update-protocol [p type env] (when-not (= 'Object p) (if-let [var (cljs.analyzer/resolve-existing-var (dissoc env :locals) p)] (do @@ -729,21 +778,21 @@ (when (:undeclared cljs.analyzer/*cljs-warnings*) (cljs.analyzer/warning :undeclared-protocol-symbol env {:protocol p}))))) -(defn resolve-var [env sym] +(defn- resolve-var [env sym] (let [ret (-> (dissoc env :locals) (cljs.analyzer/resolve-var sym) :name)] (assert ret (core/str "Can't resolve: " sym)) ret)) -(defn ->impl-map [impls] +(defn- ->impl-map [impls] (loop [ret {} s impls] (if (seq s) (recur (assoc ret (first s) (take-while seq? (next s))) (drop-while seq? (next s))) ret))) -(defn base-assign-impls [env resolve tsym type [p sigs]] +(defn- base-assign-impls [env resolve tsym type [p sigs]] (warn-and-update-protocol p tsym env) (let [psym (resolve p) pfn-prefix (subs (core/str psym) 0 @@ -762,11 +811,11 @@ (core/defmethod extend-prefix :default [tsym sym] `(.. ~tsym -prototype ~(to-property sym))) -(defn adapt-obj-params [type [[this & args :as sig] & body]] +(defn- adapt-obj-params [type [[this & args :as sig] & body]] (core/list (vec args) (list* 'this-as (vary-meta this assoc :tag type) body))) -(defn adapt-ifn-params [type [[this & args :as sig] & body]] +(defn- adapt-ifn-params [type [[this & args :as sig] & body]] (let [self-sym (with-meta 'self__ {:tag type})] `(~(vec (cons self-sym args)) (this-as ~self-sym @@ -774,17 +823,17 @@ ~@body))))) ;; for IFn invoke implementations, we need to drop first arg -(defn adapt-ifn-invoke-params [type [[this & args :as sig] & body]] +(defn- adapt-ifn-invoke-params [type [[this & args :as sig] & body]] `(~(vec args) (this-as ~(vary-meta this assoc :tag type) ~@body))) -(defn adapt-proto-params [type [[this & args :as sig] & body]] +(defn- adapt-proto-params [type [[this & args :as sig] & body]] `(~(vec (cons (vary-meta this assoc :tag type) args)) (this-as ~this ~@body))) -(defn add-obj-methods [type type-sym sigs] +(defn- add-obj-methods [type type-sym sigs] (map (fn [[f & meths :as form]] (let [[f meths] (if (vector? (first meths)) [f [(rest form)]] @@ -793,7 +842,7 @@ ~(with-meta `(fn ~@(map #(adapt-obj-params type %) meths)) (meta form))))) sigs)) -(defn ifn-invoke-methods [type type-sym [f & meths :as form]] +(defn- ifn-invoke-methods [type type-sym [f & meths :as form]] (map (fn [meth] (let [arity (count (first meth))] @@ -801,7 +850,7 @@ ~(with-meta `(fn ~meth) (meta form))))) (map #(adapt-ifn-invoke-params type %) meths))) -(defn add-ifn-methods [type type-sym [f & meths :as form]] +(defn- add-ifn-methods [type type-sym [f & meths :as form]] (let [meths (map #(adapt-ifn-params type %) meths) this-sym (with-meta 'self__ {:tag type}) argsym (gensym "args")] @@ -816,7 +865,7 @@ (meta form)))] (ifn-invoke-methods type type-sym form)))) -(defn add-proto-methods* [pprefix type type-sym [f & meths :as form]] +(defn- add-proto-methods* [pprefix type type-sym [f & meths :as form]] (let [pf (core/str pprefix f)] (if (vector? (first meths)) ;; single method case @@ -828,7 +877,7 @@ ~(with-meta `(fn ~(adapt-proto-params type meth)) (meta form)))) meths)))) -(defn proto-assign-impls [env resolve type-sym type [p sigs]] +(defn- proto-assign-impls [env resolve type-sym type [p sigs]] (warn-and-update-protocol p type env) (let [psym (resolve p) pprefix (protocol-prefix psym) @@ -845,7 +894,7 @@ (add-proto-methods* pprefix type type-sym sig))) sigs))))) -(defn validate-impl-sigs [env p method] +(defn- validate-impl-sigs [env p method] (when-not (= p 'Object) (let [var (ana/resolve-var (dissoc env :locals) p) minfo (-> var :protocol-info :methods) @@ -865,7 +914,7 @@ (ana/warning :protocol-invalid-method env {:protocol p :fname fname :invalid-arity c})) (recur (next sigs) (conj seen c)))))))) -(defn validate-impls [env impls] +(defn- validate-impls [env impls] (loop [protos #{} impls impls] (when (seq impls) (let [proto (first impls) @@ -930,12 +979,12 @@ parts (range fast-path-protocol-partitions-count))])))) -(defn annotate-specs [annots v [f sigs]] +(defn- annotate-specs [annots v [f sigs]] (conj v (vary-meta (cons f (map #(cons (second %) (nnext %)) sigs)) merge annots))) -(defn dt->et +(core/defn dt->et ([type specs fields] (dt->et type specs fields false)) ([type specs fields inline] @@ -952,7 +1001,7 @@ (recur ret specs)) ret))))) -(defn collect-protocols [impls env] +(defn- collect-protocols [impls env] (->> impls (filter core/symbol?) (map #(:name (cljs.analyzer/resolve-var (dissoc env :locals) %))) @@ -1738,7 +1787,7 @@ `(.fromArray cljs.core/PersistentHashSet (array ~@xs) true) assoc :tag 'cljs.core/PersistentHashSet)))) -(defn js-obj* [kvs] +(defn- js-obj* [kvs] (let [kvs-str (->> (repeat "~{}:~{}") (take (count kvs)) (interpose ",") @@ -1810,7 +1859,7 @@ ~@body (recur (inc ~i))))))) -(defn ^:private check-valid-options +(defn- check-valid-options "Throws an exception if the given option map contains keys not listed as valid, else returns nil." [options & valid-keys] @@ -1897,7 +1946,7 @@ (def cs (into [] (map (comp gensym core/str core/char) (range 97 118)))) -(defn gen-apply-to-helper +(defn- gen-apply-to-helper ([] (gen-apply-to-helper 1)) ([n] (let [prop (symbol (core/str "-cljs$core$IFn$_invoke$arity$" n)) @@ -2004,3 +2053,192 @@ (core/if-not (core/identical? form form') (recur form' (ana/macroexpand-1 env form')) `(quote ~form'))))) + +(defn- multi-arity-fn? [fdecl] + (core/< 1 (count fdecl))) + +(defn- variadic-fn? [fdecl] + (core/and (= 1 (count fdecl)) + (some '#{&} (ffirst fdecl)))) + +(defn- variadic-fn* + ([sym method] + (variadic-fn* sym method true)) + ([sym [arglist & body :as method] solo] + (let [sig (remove '#{&} arglist) + restarg (gensym "seq")] + (letfn [(get-delegate [] + 'cljs$core$IFn$_invoke$arity$variadic) + (get-delegate-prop [] + (symbol (core/str "-" (get-delegate)))) + (param-bind [param] + `[~param (^::ana/no-resolve first ~restarg) + ~restarg (^::ana/no-resolve next ~restarg)]) + (apply-to [] + (if (core/< 1 (count sig)) + (let [params (repeatedly (core/dec (count sig)) gensym)] + `(fn + ([~restarg] + (let [~@(mapcat param-bind params)] + (. ~sym (~(get-delegate) ~@params ~restarg)))))) + `(fn + ([~restarg] + (. ~sym (~(get-delegate) (seq ~restarg)))))))] + `(do + (set! (. ~sym ~(get-delegate-prop)) + (fn (~(vec sig) ~@body))) + ~@(when solo + `[(set! (. ~sym ~'-cljs$lang$maxFixedArity) + ~(core/dec (count sig)))]) + (set! (. ~sym ~'-cljs$lang$applyTo) + ~(apply-to))))))) + +(defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl]] + (letfn [(dest-args [c] + (map (fn [n] `(aget (js-arguments) ~n)) + (range c)))] + (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) + sig (remove '#{&} arglist) + c-1 (core/dec (count sig)) + meta (assoc meta + :top-fn + {:variadic true + :max-fixed-arity c-1 + :method-params [sig] + :arglists [arglist] + :arglists-meta (doall (map meta [arglist]))})] + `(do + (def ~(with-meta name meta) + (fn [] + (let [argseq# (when (< ~c-1 (alength (js-arguments))) + (new ^::ana/no-resolve cljs.core/IndexedSeq + (.call js/Array.prototype.slice + (js-arguments) ~c-1) 0))] + (. ~rname + (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))))) + ~(variadic-fn* rname method))))) + +(comment + (require '[clojure.pprint :as pp]) + (pp/pprint (variadic-fn 'foo {} '(([& xs])))) + (pp/pprint (variadic-fn 'foo {} '(([a & xs] xs)))) + (pp/pprint (variadic-fn 'foo {} '(([a b & xs] xs)))) + (pp/pprint (variadic-fn 'foo {} '(([a [b & cs] & xs] xs)))) + ) + +(defn- multi-arity-fn [name meta fdecl] + (letfn [(dest-args [c] + (map (fn [n] `(aget (js-arguments) ~n)) + (range c))) + (fixed-arity [rname sig] + (let [c (count sig)] + [c `(. ~rname + (~(symbol + (core/str "cljs$core$IFn$_invoke$arity$" c)) + ~@(dest-args c)))])) + (fn-method [[sig & body :as method]] + (if (some '#{&} sig) + (variadic-fn* name method false) + `(set! + (. ~name + ~(symbol (core/str "-cljs$core$IFn$_invoke$arity$" + (count sig)))) + (fn ~method))))] + (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) + arglists (map first fdecl) + variadic (boolean (some #(some '#{&} %) arglists)) + sigs (remove #(some '#{&} %) arglists) + maxfa (apply core/max (map count sigs)) + meta (assoc meta + :top-fn + {:variadic variadic + :max-fixed-arity maxfa + :method-params sigs + :arglists arglists + :arglists-meta (doall (map meta arglists))})] + `(do + (def ~(with-meta name meta) + (fn [] + (case (alength (js-arguments)) + ~@(mapcat #(fixed-arity rname %) sigs) + ~(if variadic + `(let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq + (.call js/Array.prototype.slice + (js-arguments) ~maxfa) 0)] + (. ~rname + (~'cljs$core$IFn$_invoke$arity$variadic + ~@(dest-args maxfa) + argseq#))) + `(throw (js/Error. + (str "Invalid arity: " + (alength (js-arguments))))))))) + ~@(map fn-method fdecl) + ;; optimization properties + (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa))))) + +(comment + (require '[clojure.pprint :as pp]) + (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a b])))) + (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a & xs])))) + (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a [b & cs] & xs])))) + ) + +(def + ^{:doc "Same as (def name (fn [params* ] exprs*)) or (def + name (fn ([params* ] exprs*)+)) with any doc-string or attrs added + to the var metadata. prepost-map defines a map with optional keys + :pre and :post that contain collections of pre or post conditions." + :arglists '([name doc-string? attr-map? [params*] prepost-map? body] + [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])} + defn (fn defn [&form &env name & fdecl] + ;; Note: Cannot delegate this check to def because of the call to (with-meta name ..) + (if (core/instance? clojure.lang.Symbol name) + nil + (throw (IllegalArgumentException. "First argument to defn must be a symbol"))) + (core/let [m (if (core/string? (first fdecl)) + {:doc (first fdecl)} + {}) + fdecl (if (core/string? (first fdecl)) + (next fdecl) + fdecl) + m (if (map? (first fdecl)) + (conj m (first fdecl)) + m) + fdecl (if (map? (first fdecl)) + (next fdecl) + fdecl) + fdecl (if (vector? (first fdecl)) + (core/list fdecl) + fdecl) + m (if (map? (last fdecl)) + (conj m (last fdecl)) + m) + fdecl (if (map? (last fdecl)) + (butlast fdecl) + fdecl) + m (conj {:arglists (core/list 'quote (sigs fdecl))} m) + m (core/let [inline (:inline m) + ifn (first inline) + iname (second inline)] + ;; same as: (if (and (= 'fn ifn) (not (symbol? iname))) ...) + (if (if (clojure.lang.Util/equiv 'fn ifn) + (if (core/instance? clojure.lang.Symbol iname) false true)) + ;; inserts the same fn name to the inline fn if it does not have one + (assoc m :inline (cons ifn (cons (clojure.lang.Symbol/intern (.concat (.getName ^clojure.lang.Symbol name) "__inliner")) + (next inline)))) + m)) + m (conj (if (meta name) (meta name) {}) m)] + (cond + (multi-arity-fn? fdecl) + (multi-arity-fn name m fdecl) + + (variadic-fn? fdecl) + (variadic-fn name m fdecl) + + :else + (core/list 'def (with-meta name m) + ;;todo - restore propagation of fn name + ;;must figure out how to convey primitive hints to self calls first + (cons `fn fdecl)))))) + +(. (var defn) (setMacro)) diff --git a/test/clj/cljs/compiler_tests.clj b/test/clj/cljs/compiler_tests.clj index f92d39d53..9e9f2fb6b 100644 --- a/test/clj/cljs/compiler_tests.clj +++ b/test/clj/cljs/compiler_tests.clj @@ -52,3 +52,16 @@ (comp/emit (ana/analyze (assoc aenv :context :expr) 'js/-Infinity))) "-Infinity")) + +(comment + (env/with-compiler-env cenv + (comp/emit + (ana/analyze aenv + '(defn foo ([a]) ([a b]))))) + + (env/with-compiler-env cenv + (comp/munge + (comp/lazy-load? + (ana/analyze aenv + '(defn foo ([a]) ([a b])))))) + ) \ No newline at end of file From 0d0f50958c952db5281aaec5e7bf0c0f9954ccee Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 7 Apr 2015 08:31:58 -0400 Subject: [PATCH 0967/4033] CLJS-1158: Regression: compiler fails to see symbols defined in another namespace Namespaces and macro namespaces can share the same name. In the case that some other namespace loads a macro namespace this will prevent analysis of the runtime namespace because `cljs.analyzer/analyze-deps` only examines the existence of a ns entry, not the presence of actual var mappings - `:defs`. We now check for `:defs`. Also when writing out the analysis cache we always drop `:macros` as we do not track macro namespaces on disk for changes. We should always reload macros and populate `:macros` fresh. --- src/clj/cljs/analyzer.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 0cda188fe..e28bf5e02 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1229,12 +1229,13 @@ (defn analyze-deps ([lib deps env] (analyze-deps lib deps env nil)) ([lib deps env opts] + (let [compiler @env/*compiler*] (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] (assert (every? #(not (contains? *cljs-dep-set* %)) deps) (str "Circular dependency detected " (-> *cljs-dep-set* meta :dep-path))) (doseq [dep deps] - (when-not (or (contains? (::namespaces @env/*compiler*) dep) - (contains? (:js-dependency-index @env/*compiler*) (name dep)) + (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) + (contains? (:js-dependency-index compiler) (name dep)) (deps/find-classpath-lib dep)) (let [relpath (util/ns->relpath dep) src (locate-src relpath)] @@ -1242,7 +1243,7 @@ (analyze-file src opts) (throw (error env - (error-message :undeclared-ns {:ns-sym dep :path relpath})))))))))) + (error-message :undeclared-ns {:ns-sym dep :path relpath}))))))))))) (defn check-uses [uses env] (doseq [[sym lib] uses] @@ -2115,7 +2116,8 @@ (util/mkdirs cache-file) (spit cache-file (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" - (pr-str (get-in @env/*compiler* [::namespaces ns]))))) + (pr-str + (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros))))) (defn analyze-file "Given a java.io.File, java.net.URL or a string identifying a resource on the From 9441795292654677b366e00bb6c0a8eb11542a64 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 7 Apr 2015 11:49:06 -0400 Subject: [PATCH 0968/4033] related to CLJS-1158, `cljs.analyzer/analyze-file` and `cljs.compiler/compile-file` also needs to check `:defs` --- src/clj/cljs/analyzer.clj | 2 +- src/clj/cljs/compiler.clj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index e28bf5e02..05f4cac99 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -2145,7 +2145,7 @@ (.getPath ^URL res)) cache (when (:cache-analysis opts) (cache-file res ns-info output-dir))] - (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info)]) + (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info) :defs]) (if (or (not cache) (requires-analysis? res output-dir)) (binding [*cljs-ns* 'cljs.user diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index e8ebba11c..37668d837 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1103,7 +1103,7 @@ (if (requires-compilation? src-file dest-file opts) (do (util/mkdirs dest-file) - (when (and (contains? (::ana/namespaces @env/*compiler*) ns) + (when (and (get-in @env/*compiler* [::ana/namespaces ns :defs]) (not= ns 'cljs.core)) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) (let [ret (compile-file* src-file dest-file opts)] @@ -1117,7 +1117,7 @@ ;; populate compilation environment with analysis information ;; when constants are optimized (when (and (true? (:optimize-constants opts)) - (not (contains? (::ana/namespaces @env/*compiler*) ns))) + (nil? (get-in @env/*compiler* [::ana/namespaces ns :defs]))) (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) ns-info))) (catch Exception e From 689ed7db5507fd3a10285f88086f8a379f55bbe6 Mon Sep 17 00:00:00 2001 From: Chas Emerick Date: Wed, 8 Apr 2015 09:00:45 -0400 Subject: [PATCH 0969/4033] CLJS-1192: eliminate JDK8 API dependency in cljs.repl.node --- src/clj/cljs/repl/node.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl/node.clj b/src/clj/cljs/repl/node.clj index 6b8c695b4..fa07e21e2 100644 --- a/src/clj/cljs/repl/node.clj +++ b/src/clj/cljs/repl/node.clj @@ -82,7 +82,7 @@ ;; we really do want system-default encoding here (with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)] (loop [buf (char-array 1024)] - (when (.isAlive proc) + (when (try (.exitValue proc) false (catch IllegalThreadStateException _ true)) (try (let [len (.read in buf)] (when-not (neg? len) From 44c7be2d0b7ca671fce20ee63202f5db33048de3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 8 Apr 2015 15:57:14 -0400 Subject: [PATCH 0970/4033] bump Clojure to 1.7.0-alpha6 and tools.reader to 0.8.17 --- pom.template.xml | 4 ++-- project.clj | 4 ++-- script/bootstrap | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index a4d063b5a..062ffee00 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.6.0 + 1.7.0-alpha6 com.google.javascript @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.8.16 + 0.8.17 diff --git a/project.clj b/project.clj index 64eef5692..39ff33dbe 100644 --- a/project.clj +++ b/project.clj @@ -8,9 +8,9 @@ :source-paths ["src/clj"] :resource-paths ["src/cljs"] :test-paths ["test/clj"] - :dependencies [[org.clojure/clojure "1.6.0"] + :dependencies [[org.clojure/clojure "1.7.0-alpha6"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "0.8.16"] + [org.clojure/tools.reader "0.8.17"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R5"]] diff --git a/script/bootstrap b/script/bootstrap index 447b9a297..9da0fe687 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,12 +2,12 @@ set -e -CLOJURE_RELEASE="1.6.0" +CLOJURE_RELEASE="1.7.0-alpha6" CLOSURE_RELEASE="20150126" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="0.8.16" +TREADER_RELEASE="0.8.17" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From 28f02a4f5074848500a60fecf5d2d71ad383b834 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 8 Apr 2015 19:57:37 -0400 Subject: [PATCH 0971/4033] disable fancy inference for now, never used and needs to be updated for new top level fn compilation strategy --- test/clj/cljs/analyzer_tests.clj | 155 ++++++++++++++++--------------- 1 file changed, 81 insertions(+), 74 deletions(-) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index 23f4681a9..eea25b4dc 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -155,53 +155,58 @@ 'any))) (deftest fn-inference - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env - '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - (x :one))))) - 'number)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env - '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - (x :one :two))))) - 'string)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env - '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - (x :one :two :three))))) - 'cljs.core/IList))) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env + ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + ; (x :one))))) + ; 'number)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env + ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + ; (x :one :two))))) + ; 'string)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env + ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + ; (x :one :two :three))))) + ; 'cljs.core/IList)) + ) (deftest lib-inference (is (= (e/with-compiler-env test-cenv (:tag (a/analyze test-env '(+ 1 2)))) 'number)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(alength (array))))) - 'number)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(aclone (array))))) - 'array)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(count [1 2 3])))) - 'number)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(into-array [1 2 3])))) - 'array)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(js-obj)))) - 'object)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(-conj [] 1)))) - 'clj)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(conj [] 1)))) - 'clj)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(assoc nil :foo :bar)))) - 'clj)) - (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(dissoc {:foo :bar} :foo)))) - '#{clj clj-nil}))) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(alength (array))))) + ; 'number)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(aclone (array))))) + ; 'array)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(-count [1 2 3])))) + ; 'number)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(count [1 2 3])))) + ; 'number)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(into-array [1 2 3])))) + ; 'array)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(js-obj)))) + ; 'object)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(-conj [] 1)))) + ; 'clj)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(conj [] 1)))) + ; 'clj)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(assoc nil :foo :bar)))) + ; 'clj)) + ;(is (= (e/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(dissoc {:foo :bar} :foo)))) + ; '#{clj clj-nil})) + ) (deftest test-always-true-if (is (= (e/with-compiler-env test-cenv @@ -210,39 +215,41 @@ ;; will only work if the previous test works (deftest test-count - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(count [])))) - 'number))) + ;(is (= (cljs.env/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(count [])))) + ; 'number)) + ) (deftest test-numeric - (is (= (a/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(dec x))))) - 'number)) - (is (= (a/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(int x))))) - 'number)) - (is (= (a/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(unchecked-int x))))) - 'number)) - (is (= (a/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(mod x y))))) - 'number)) - (is (= (a/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(quot x y))))) - 'number)) - (is (= (a/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(rem x y))))) - 'number)) - (is (= (a/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (a/analyze test-env '(bit-count n))))) - 'number))) + ;(is (= (a/no-warn + ; (cljs.env/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(dec x))))) + ; 'number)) + ;(is (= (a/no-warn + ; (cljs.env/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(int x))))) + ; 'number)) + ;(is (= (a/no-warn + ; (cljs.env/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(unchecked-int x))))) + ; 'number)) + ;(is (= (a/no-warn + ; (cljs.env/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(mod x y))))) + ; 'number)) + ;(is (= (a/no-warn + ; (cljs.env/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(quot x y))))) + ; 'number)) + ;(is (= (a/no-warn + ; (cljs.env/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(rem x y))))) + ; 'number)) + ;(is (= (a/no-warn + ; (cljs.env/with-compiler-env test-cenv + ; (:tag (a/analyze test-env '(bit-count n))))) + ; 'number)) + ) ;; ============================================================================= ;; Catching errors during macroexpansion @@ -253,7 +260,7 @@ (a/analyze test-env '(defn foo 123)) (catch Exception e (.getMessage e))) - "Parameter declaration 123 should be a vector at line"))) + "Parameter declaration \"123\" should be a vector at line"))) ;; ============================================================================= ;; ns desugaring From 3125bfc739543fd3cb8a648afd1d766ebd767708 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 8 Apr 2015 20:02:13 -0400 Subject: [PATCH 0972/4033] fix desugar ns specs tests, subject to ordering change --- test/clj/cljs/analyzer_tests.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index eea25b4dc..175daf345 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -267,9 +267,9 @@ (deftest test-cljs-975 (let [spec '((:require [bar :refer [baz] :refer-macros [quux]] :reload))] - (is (= (a/desugar-ns-specs spec) - '((:require-macros (bar :refer [quux]) :reload) - (:require (bar :refer [baz]) :reload)))))) + (is (= (set (a/desugar-ns-specs spec)) + (set '((:require-macros (bar :refer [quux]) :reload) + (:require (bar :refer [baz]) :reload))))))) ;; ============================================================================= ;; Namespace metadata From d17f9119c4bd48c31fb0c219a35631b0ef014b3c Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 8 Apr 2015 20:04:36 -0400 Subject: [PATCH 0973/4033] allow cljc files to supplied anywhere cljs files are (wip) --- src/clj/cljs/analyzer.clj | 52 +++++++++++++++++++---------------- src/clj/cljs/closure.clj | 52 +++++++++++++++++++++-------------- src/clj/cljs/compiler.clj | 29 +++++++++++++------ src/clj/cljs/repl.clj | 17 ++++++++---- src/clj/cljs/repl/browser.clj | 2 ++ src/clj/cljs/repl/nashorn.clj | 26 ------------------ src/clj/cljs/util.clj | 14 ++++++++-- 7 files changed, 106 insertions(+), 86 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 05f4cac99..577ab8483 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -108,10 +108,10 @@ (str "Use of undeclared Var " (:prefix info) "/" (:suffix info))) (defmethod error-message :undeclared-ns - [warning-type {:keys [ns-sym path] :as info}] + [warning-type {:keys [ns-sym] :as info}] (str "No such namespace: " ns-sym - (when path - (str ", could not locate " path)))) + ", could not locate " (util/ns->relpath ns-sym :cljc) + " or " (util/ns->relpath ns-sym :cljs))) (defmethod error-message :dynamic [warning-type info] @@ -433,8 +433,8 @@ (nil? (get-in @env/*compiler* [::namespaces ns-sym])) ;; macros may refer to namespaces never explicitly required ;; confirm that the library at least exists - (nil? (io/resource (util/ns->relpath ns-sym)))) - (warning :undeclared-ns env {:ns-sym ns-sym :path (util/ns->relpath ns-sym)}))) + (nil? (util/ns->source ns-sym))) + (warning :undeclared-ns env {:ns-sym ns-sym}))) (declare get-expander) @@ -1211,13 +1211,19 @@ (declare analyze-file) -(defn locate-src [relpath] - (or (io/resource relpath) - (let [root (:root @env/*compiler*) - root-path (when root (.getPath ^File root)) - f (io/file root-path relpath)] - (when (and (.exists f) (.isFile f)) - f)))) +(defn locate-src + "Given a namespace return the corresponding ClojureScript (.cljc or .cljs) + resource on the classpath or file from the root of the build." + [ns] + (or (util/ns->source ns) + (let [rootp (when-let [root (:root @env/*compiler*)] + (.getPath ^File root)) + cljcf (io/file rootp (util/ns->relpath ns :cljc)) + cljsf (io/file rootp (util/ns->relpath ns :cljs))] + (if (and (.exists cljcf) (.isFile cljcf)) + cljcf + (if (and (.exists cljsf) (.isFile cljsf)) + cljsf))))) (defn foreign-dep? [dep] {:pre [(symbol? dep)]} @@ -1237,13 +1243,11 @@ (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) (deps/find-classpath-lib dep)) - (let [relpath (util/ns->relpath dep) - src (locate-src relpath)] - (if src - (analyze-file src opts) - (throw - (error env - (error-message :undeclared-ns {:ns-sym dep :path relpath}))))))))))) + (if-let [src (locate-src dep)] + (analyze-file src opts) + (throw + (error env + (error-message :undeclared-ns {:ns-sym dep})))))))))) (defn check-uses [uses env] (doseq [[sym lib] uses] @@ -1373,8 +1377,8 @@ (let [ns (if (sequential? form) (first form) form) {:keys [use-macros require-macros]} (or (get-in @env/*compiler* [::namespaces ns]) - (when-let [res (io/resource (util/ns->relpath ns))] - (:ast (parse-ns res))))] + (when-let [res (util/ns->source ns)] + (:ast (parse-ns res))))] (or (some #{ns} (vals use-macros)) (some #{ns} (vals require-macros)))))) @@ -2023,7 +2027,7 @@ ([src dest opts] (env/ensure (let [src (if (symbol? src) - (io/resource (util/ns->relpath src)) + (util/ns->source src) src) compiler-env @env/*compiler* [ijs compiler-env'] @@ -2085,7 +2089,9 @@ (= (:ns ns-info) 'cljs.core) (io/resource "cljs/core.cljs.cache.aot.edn"))] core-cache - (io/file (str (util/to-target-file output-dir ns-info "cljs") ".cache.edn"))))) + (let [target-file (util/to-target-file output-dir ns-info + (util/ext (:source-file ns-info)))] + (io/file (str target-file ".cache.edn")))))) (defn requires-analysis? ([src] (requires-analysis? src "out")) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index e2b195a3a..303807168 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -479,26 +479,35 @@ (-compile uri (merge opts {:output-file js-file})))) (defn cljs-source-for-namespace - "Returns a map containing :relative-path, :uri referring to the resource that -should contain the source for the given namespace name." + "Given a namespace return the corresponding source with either a .cljc or + .cljs extension." [ns] - (as-> (munge ns) % - (string/replace % \. \/) - (str % ".cljs") - {:relative-path % :uri (io/resource %)})) + (let [path (-> (munge ns) (string/replace \. \/)) + relpath (str path ".cljc")] + (if-let [res (io/resource relpath)] + {:relative-path relpath :uri res} + (let [relpath (str path ".cljs")] + (if-let [res (io/resource relpath)] + {:relative-path relpath :uri res}))))) (defn source-for-namespace + "Given a namespace and compilation environment return the relative path and + uri of the corresponding source regardless of the source language extension: + .cljc, .cljs, .js" [ns compiler-env] (let [ns-str (str (comp/munge ns {})) path (string/replace ns-str \. \/) - relpath (str path ".cljs")] - (if-let [cljs-res (io/resource relpath)] - {:relative-path relpath :uri cljs-res} - (let [relpath (:file (get-in @compiler-env [:js-dependency-index ns-str]))] - (if-let [js-res (and relpath (io/resource relpath))] - {:relative-path relpath :uri js-res} - (throw - (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))) + relpath (str path ".cljc")] + (if-let [cljc-res (io/resource relpath)] + {:relative-path relpath :uri cljc-res} + (let [relpath (str path ".cljs")] + (if-let [cljs-res (io/resource relpath)] + {:relative-path relpath :uri cljs-res} + (let [relpath (:file (get-in @compiler-env [:js-dependency-index ns-str]))] + (if-let [js-res (and relpath (io/resource relpath))] + {:relative-path relpath :uri js-res} + (throw + (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))))) (defn cljs-dependencies "Given a list of all required namespaces, return a list of @@ -814,7 +823,7 @@ should contain the source for the given namespace name." (if (and provides source-url) (assoc relpaths (.getPath ^URL source-url) - (util/ns->relpath (first provides))) + (util/ns->relpath (first provides) (util/ext source-url))) relpaths)) (if-let [url (:url source)] (let [path (.getPath ^URL url)] @@ -1156,9 +1165,9 @@ should contain the source for the given namespace name." (write-javascript opts js) ;; always copy original ClojureScript sources to the output directory ;; when source maps enabled - (let [out-file (if-let [ns (and (:source-map opts) (first (:provides js)))] + (let [out-file (when-let [ns (and (:source-map opts) (first (:provides js)))] (io/file (io/file (util/output-directory opts)) - (util/ns->relpath ns))) + (util/ns->relpath ns (util/ext (:source-url js))))) source-url (:source-url js)] (when (and out-file source-url (or (not (.exists ^File out-file)) @@ -1551,7 +1560,8 @@ should contain the source for the given namespace name." (some (fn [^WatchEvent e] (let [fstr (.. e context toString)] - (and (or (. fstr (endsWith "cljs")) + (and (or (. fstr (endsWith "cljc")) + (. fstr (endsWith "cljs")) (. fstr (endsWith "js"))) (not (. fstr (startsWith ".#")))))) (seq (.pollEvents key)))) @@ -1596,9 +1606,9 @@ should contain the source for the given namespace name." ([src {:keys [wrap all-provides] :as options}] (let [goog-ns (case (util/ext src) - "cljs" (comp/munge (:ns (ana/parse-ns src))) - "js" (cond-> (:provides (parse-js-ns src)) - (not all-provides) first) + ("cljc" "cljs") (comp/munge (:ns (ana/parse-ns src))) + "js" (cond-> (:provides (parse-js-ns src)) + (not all-provides) first) (throw (IllegalArgumentException. (str "Can't create goog.require expression for " src))))] diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 37668d837..c6e1cbcac 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -933,11 +933,22 @@ (emits (interleave (concat segs (repeat nil)) (concat args [nil])))))) +;; TODO: unify renaming helpers - this one was hard to find - David + (defn rename-to-js "Change the file extension from .cljs to .js. Takes a File or a String. Always returns a String." - [file-str] - (clojure.string/replace file-str #"\.cljs$" ".js")) + [^String file-str] + (cond + (.endsWith file-str ".cljc") + (clojure.string/replace file-str #"\.cljc$" ".js") + + (.endsWith file-str ".cljs") + (clojure.string/replace file-str #"\.cljs$" ".js") + + :else + (throw (IllegalArgumentException. + (str "Invalid source file extension " file-str))))) (defn with-core-cljs "Ensure that core.cljs has been loaded." @@ -1125,13 +1136,15 @@ (throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))) (defn cljs-files-in - "Return a sequence of all .cljs files in the given directory." + "Return a sequence of all .cljc and .cljs files in the given directory." [dir] - (filter #(let [name (.getName ^File %)] - (and (.endsWith name ".cljs") - (not= \. (first name)) - (not (contains? cljs-reserved-file-names name)))) - (file-seq dir))) + (filter + #(let [name (.getName ^File %)] + (and (or (.endsWith name ".cljc") + (.endsWith name ".cljs")) + (not= \. (first name)) + (not (contains? cljs-reserved-file-names name)))) + (file-seq dir))) (defn compile-root "Looks recursively in src-dir for .cljs files and compiles them to diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index e87dbfeff..2316cd1a9 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -202,12 +202,17 @@ (defn ^File js-src->cljs-src "Map a JavaScript output file back to the original ClojureScript source - file." + file (.cljc or .cljs)." [f] (let [f (io/file f) dir (.getParentFile f) - name (.getName f)] - (io/file dir (string/replace name ".js" ".cljs")))) + base-name (string/replace (.getName f) ".js" "") + cljcf (io/file dir (str base-name ".cljc"))] + (if (.exists cljcf) + cljcf + (let [cljsf (io/file dir (str base-name ".cljs"))] + (if (.exists cljsf) + cljsf))))) (defn read-source-map "Return the source map for the JavaScript source file." @@ -298,8 +303,8 @@ source-file (io/file rfile))) (str (System/getProperty "user.dir") File/separator) "")) - url (or (and ns-info (io/resource (util/ns->relpath ns))) - (and file (io/resource file)))] + url (or (and ns-info (util/ns->source ns)) + (and file (io/resource file)))] (merge {:function name' :call call @@ -494,7 +499,7 @@ (when-let [ns (and (:source-map opts) (first (:provides compiled)))] (spit (io/file (io/file (util/output-directory opts)) - (util/ns->relpath ns)) + (util/ns->relpath ns (util/ext (:source-url compiled)))) (slurp src))) ;; need to load dependencies first (load-dependencies repl-env (:requires compiled) opts) diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index a625183e6..9e7dbd61f 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -90,6 +90,7 @@ ".html" "text/html" ".jpg" "image/jpeg" ".js" "text/javascript" + ".cljc" "text/x-clojure" ".cljs" "text/x-clojure" ".map" "application/json" ".png" "image/png" @@ -107,6 +108,7 @@ (or (= path "/") (.endsWith path ".js") + (.endsWith path ".cljc") (.endsWith path ".cljs") (.endsWith path ".map") (.endsWith path ".html") diff --git a/src/clj/cljs/repl/nashorn.clj b/src/clj/cljs/repl/nashorn.clj index d2d9cd4af..06b8f9ddd 100644 --- a/src/clj/cljs/repl/nashorn.clj +++ b/src/clj/cljs/repl/nashorn.clj @@ -25,13 +25,6 @@ ;; ;; Nashorn's load() function docs: ;; http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/shell.html -;; -;; Some functions are copied from: https://github.com/bodil/cljs-nashorn and the node.js repl code - -;; -;; ** Usage from Leiningen: -;; -;; Create a file init_repl_test.clj containing (adjust :output-dir to your cljsbuild settings): (comment (ns init-repl-test @@ -43,25 +36,6 @@ :cache-analysis true) ) -;; -;; Invoke it with: -;; lein trampoline run -m clojure.main src-clj/init_repl_test.clj - -;; -;; ** Usage from nrepl / piggieback, execute the following at the nrepl prompt: -;; (adjust :output-dir to your cljsbuild settings) - -(comment - (ns init-repl-piggieback - (:require [cljs.repl.nashorn] - [cemerick.piggieback])) - - (cemerick.piggieback/cljs-repl - :repl-env (cljs.repl.nashorn/repl-env) - :output-dir "resources/public/compiled" - :cache-analysis true) - ) - ;; Implementation (defn create-engine diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index d19e69caf..cb2e2064c 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -48,8 +48,18 @@ (defn munge-path [ss] (clojure.lang.Compiler/munge (str ss))) -(defn ns->relpath [s] - (str (string/replace (munge-path s) \. \/) ".cljs")) +(defn ns->relpath + "Given a namespace as a symbol return the relative path. May optionally + provide the file extension, defaults to :cljs." + ([ns] (ns->relpath ns :cljs)) + ([ns ext] + (str (string/replace (munge-path ns) \. \/) "." (name ext)))) + +(defn ns->source + "Given a namespace as a symbol return the corresponding resource if it exists." + [ns] + (or (io/resource (ns->relpath ns :cljc)) + (io/resource (ns->relpath ns :cljs)))) (defn path-seq [file-str] From 9a1a92a450ecea9f8043746d75112704cbe49be8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 9 Apr 2015 00:47:48 -0400 Subject: [PATCH 0974/4033] permit conditional reading only in cljc files --- src/clj/cljs/analyzer.clj | 8 +++++--- src/clj/cljs/compiler.clj | 2 +- src/clj/cljs/util.clj | 17 +++++++++++------ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 577ab8483..b69526302 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1964,7 +1964,9 @@ ([^Reader rdr] (forms-seq* rdr nil)) ([^Reader rdr filename] {:pre [(instance? Reader rdr)]} - (let [pbr (readers/indexing-push-back-reader + (let [opts (when (and filename (= (util/ext filename) "cljc")) + {:read-cond :allow :features #{:cljs}}) + pbr (readers/indexing-push-back-reader (PushbackReader. rdr) 1 filename) data-readers tags/*cljs-data-readers* forms-seq_ @@ -1977,7 +1979,7 @@ (apply merge ((juxt :requires :require-macros) (get-namespace *cljs-ns*)))] - (reader/read pbr nil eof-sentinel))] + (reader/read pbr nil eof-sentinel opts nil))] (if (identical? form eof-sentinel) (.close rdr) (cons form (forms-seq_))))))] @@ -2161,7 +2163,7 @@ (util/debug-prn "Analyzing" (str res))) (let [env (assoc (empty-env) :build-options opts) ns (with-open [rdr (io/reader res)] - (loop [ns nil forms (seq (forms-seq* rdr))] + (loop [ns nil forms (seq (forms-seq* rdr (util/path res)))] (if forms (let [form (first forms) env (assoc env :ns (get-namespace *cljs-ns*)) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index c6e1cbcac..faf710637 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1013,7 +1013,7 @@ :gen-line 0}))] (emitln (compiled-by-string opts)) (with-open [rdr (io/reader src)] - (loop [forms (ana/forms-seq* rdr) + (loop [forms (ana/forms-seq* rdr (util/path src)) ns-name nil deps nil] (if (seq forms) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index cb2e2064c..5ed877a11 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -105,15 +105,20 @@ (defn ^String filename [^File f] (.getName f)) -(defn ^String path [^URL url] - (.getPath url)) +(defn ^String path [x] + {:pre [(or (file? x) (url? x))]} + (cond + (file? x) (.getAbsolutePath ^File x) + (url? x) (.getPath ^URL x))) (defn ^String ext - "Given a file or url return the file extension." - [f] + "Given a file, url or string return the file extension." + [x] + {:pre [(or (file? x) (url? x) (string? x))]} (let [s (cond - (file? f) (filename f) - (url? f) (path f))] + (file? x) (filename x) + (url? x) (path x) + (string? x) x)] (last (string/split s #"\.")))) (defn ^String get-name From ebee071a49e4abb0c680f640db1ed3cd5974cf2e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 9 Apr 2015 09:22:52 -0400 Subject: [PATCH 0975/4033] Always check .cljs before .cljc --- src/clj/cljs/analyzer.clj | 18 +++++++++--------- src/clj/cljs/closure.clj | 24 ++++++++++++------------ src/clj/cljs/compiler.clj | 12 ++++++------ src/clj/cljs/repl.clj | 14 +++++++------- src/clj/cljs/repl/browser.clj | 2 +- src/clj/cljs/util.clj | 4 ++-- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index b69526302..7121c9a70 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -110,8 +110,8 @@ (defmethod error-message :undeclared-ns [warning-type {:keys [ns-sym] :as info}] (str "No such namespace: " ns-sym - ", could not locate " (util/ns->relpath ns-sym :cljc) - " or " (util/ns->relpath ns-sym :cljs))) + ", could not locate " (util/ns->relpath ns-sym :cljs) + " or " (util/ns->relpath ns-sym :cljc))) (defmethod error-message :dynamic [warning-type info] @@ -1212,18 +1212,18 @@ (declare analyze-file) (defn locate-src - "Given a namespace return the corresponding ClojureScript (.cljc or .cljs) + "Given a namespace return the corresponding ClojureScript (.cljs or .cljc) resource on the classpath or file from the root of the build." [ns] (or (util/ns->source ns) (let [rootp (when-let [root (:root @env/*compiler*)] (.getPath ^File root)) - cljcf (io/file rootp (util/ns->relpath ns :cljc)) - cljsf (io/file rootp (util/ns->relpath ns :cljs))] - (if (and (.exists cljcf) (.isFile cljcf)) - cljcf - (if (and (.exists cljsf) (.isFile cljsf)) - cljsf))))) + cljsf (io/file rootp (util/ns->relpath ns :cljs)) + cljcf (io/file rootp (util/ns->relpath ns :cljc))] + (if (and (.exists cljsf) (.isFile cljsf)) + cljsf + (if (and (.exists cljcf) (.isFile cljcf)) + cljcf))))) (defn foreign-dep? [dep] {:pre [(symbol? dep)]} diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 303807168..838502a9a 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -479,30 +479,30 @@ (-compile uri (merge opts {:output-file js-file})))) (defn cljs-source-for-namespace - "Given a namespace return the corresponding source with either a .cljc or - .cljs extension." + "Given a namespace return the corresponding source with either a .cljs or + .cljc extension." [ns] (let [path (-> (munge ns) (string/replace \. \/)) - relpath (str path ".cljc")] + relpath (str path ".cljs")] (if-let [res (io/resource relpath)] {:relative-path relpath :uri res} - (let [relpath (str path ".cljs")] + (let [relpath (str path ".cljc")] (if-let [res (io/resource relpath)] {:relative-path relpath :uri res}))))) (defn source-for-namespace "Given a namespace and compilation environment return the relative path and uri of the corresponding source regardless of the source language extension: - .cljc, .cljs, .js" + .cljs, .cljc, .js" [ns compiler-env] (let [ns-str (str (comp/munge ns {})) path (string/replace ns-str \. \/) - relpath (str path ".cljc")] - (if-let [cljc-res (io/resource relpath)] - {:relative-path relpath :uri cljc-res} - (let [relpath (str path ".cljs")] - (if-let [cljs-res (io/resource relpath)] - {:relative-path relpath :uri cljs-res} + relpath (str path ".cljs")] + (if-let [cljs-res (io/resource relpath)] + {:relative-path relpath :uri cljs-res} + (let [relpath (str path ".cljc")] + (if-let [cljc-res (io/resource relpath)] + {:relative-path relpath :uri cljc-res} (let [relpath (:file (get-in @compiler-env [:js-dependency-index ns-str]))] (if-let [js-res (and relpath (io/resource relpath))] {:relative-path relpath :uri js-res} @@ -1606,7 +1606,7 @@ ([src {:keys [wrap all-provides] :as options}] (let [goog-ns (case (util/ext src) - ("cljc" "cljs") (comp/munge (:ns (ana/parse-ns src))) + ("cljs" "cljc") (comp/munge (:ns (ana/parse-ns src))) "js" (cond-> (:provides (parse-js-ns src)) (not all-provides) first) (throw diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index faf710637..e5e66565e 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -940,12 +940,12 @@ String. Always returns a String." [^String file-str] (cond - (.endsWith file-str ".cljc") - (clojure.string/replace file-str #"\.cljc$" ".js") - (.endsWith file-str ".cljs") (clojure.string/replace file-str #"\.cljs$" ".js") + (.endsWith file-str ".cljc") + (clojure.string/replace file-str #"\.cljc$" ".js") + :else (throw (IllegalArgumentException. (str "Invalid source file extension " file-str))))) @@ -1136,12 +1136,12 @@ (throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))) (defn cljs-files-in - "Return a sequence of all .cljc and .cljs files in the given directory." + "Return a sequence of all .cljs and .cljc files in the given directory." [dir] (filter #(let [name (.getName ^File %)] - (and (or (.endsWith name ".cljc") - (.endsWith name ".cljs")) + (and (or (.endsWith name ".cljs") + (.endsWith name ".cljc")) (not= \. (first name)) (not (contains? cljs-reserved-file-names name)))) (file-seq dir))) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index 2316cd1a9..eb37f07be 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -202,17 +202,17 @@ (defn ^File js-src->cljs-src "Map a JavaScript output file back to the original ClojureScript source - file (.cljc or .cljs)." + file (.cljs or .cljc)." [f] (let [f (io/file f) dir (.getParentFile f) base-name (string/replace (.getName f) ".js" "") - cljcf (io/file dir (str base-name ".cljc"))] - (if (.exists cljcf) - cljcf - (let [cljsf (io/file dir (str base-name ".cljs"))] - (if (.exists cljsf) - cljsf))))) + cljsf (io/file dir (str base-name ".cljs"))] + (if (.exists cljsf) + cljsf + (let [cljcf (io/file dir (str base-name ".cljc"))] + (if (.exists cljcf) + cljcf))))) (defn read-source-map "Return the source map for the JavaScript source file." diff --git a/src/clj/cljs/repl/browser.clj b/src/clj/cljs/repl/browser.clj index 9e7dbd61f..ecb976639 100644 --- a/src/clj/cljs/repl/browser.clj +++ b/src/clj/cljs/repl/browser.clj @@ -90,8 +90,8 @@ ".html" "text/html" ".jpg" "image/jpeg" ".js" "text/javascript" - ".cljc" "text/x-clojure" ".cljs" "text/x-clojure" + ".cljc" "text/x-clojure" ".map" "application/json" ".png" "image/png" "text/plain")) diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.clj index 5ed877a11..84810fce6 100644 --- a/src/clj/cljs/util.clj +++ b/src/clj/cljs/util.clj @@ -58,8 +58,8 @@ (defn ns->source "Given a namespace as a symbol return the corresponding resource if it exists." [ns] - (or (io/resource (ns->relpath ns :cljc)) - (io/resource (ns->relpath ns :cljs)))) + (or (io/resource (ns->relpath ns :cljs)) + (io/resource (ns->relpath ns :cljc)))) (defn path-seq [file-str] From 6d4ee9c0f938ac62147ecda6a2a47fcb2cca69e4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 9 Apr 2015 10:02:44 -0400 Subject: [PATCH 0976/4033] tweak reader/read call based on feedback from Nicola Mometto & Alex Miller --- src/clj/cljs/analyzer.clj | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 7121c9a70..41f876e87 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1964,22 +1964,24 @@ ([^Reader rdr] (forms-seq* rdr nil)) ([^Reader rdr filename] {:pre [(instance? Reader rdr)]} - (let [opts (when (and filename (= (util/ext filename) "cljc")) - {:read-cond :allow :features #{:cljs}}) + (let [eof-sentinel (Object.) + opts (merge + {:eof eof-sentinel} + (if (and filename (= (util/ext filename) "cljc")) + {:read-cond :allow :features #{:cljs}})) pbr (readers/indexing-push-back-reader (PushbackReader. rdr) 1 filename) data-readers tags/*cljs-data-readers* forms-seq_ (fn forms-seq_ [] (lazy-seq - (let [eof-sentinel (Object.) - form (binding [*ns* (create-ns *cljs-ns*) + (let [form (binding [*ns* (create-ns *cljs-ns*) reader/*data-readers* data-readers reader/*alias-map* (apply merge ((juxt :requires :require-macros) (get-namespace *cljs-ns*)))] - (reader/read pbr nil eof-sentinel opts nil))] + (reader/read opts pbr))] (if (identical? form eof-sentinel) (.close rdr) (cons form (forms-seq_))))))] From 7ccdffaf8f36a28dec6651248707ac83e6362d79 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2015 11:18:49 -0400 Subject: [PATCH 0977/4033] bump tools.reader to released verion --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 062ffee00..1f242c380 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.8.17 + 0.9.0 diff --git a/project.clj b/project.clj index 39ff33dbe..40a588eeb 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.7.0-alpha6"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "0.8.17"] + [org.clojure/tools.reader "0.9.0"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R5"]] diff --git a/script/bootstrap b/script/bootstrap index 9da0fe687..ee1703abc 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -7,7 +7,7 @@ CLOSURE_RELEASE="20150126" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="0.8.17" +TREADER_RELEASE="0.9.0" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From e97c6e4cdb93f64cd064af42cf6845848f03fa3f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2015 17:28:48 -0400 Subject: [PATCH 0978/4033] bump tools.reader to 0.9.1 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 1f242c380..343fbcd37 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.9.0 + 0.9.1 diff --git a/project.clj b/project.clj index 40a588eeb..9dd166cce 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.7.0-alpha6"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "0.9.0"] + [org.clojure/tools.reader "0.9.1"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R5"]] diff --git a/script/bootstrap b/script/bootstrap index ee1703abc..df8d68cfb 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -7,7 +7,7 @@ CLOSURE_RELEASE="20150126" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="0.9.0" +TREADER_RELEASE="0.9.1" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From 59945265b67a576c4e264ab30665f7c574d4a59f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Apr 2015 07:35:32 -0400 Subject: [PATCH 0979/4033] update changes --- changes.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/changes.md b/changes.md index acd6839d2..9f5b0cb7c 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,48 @@ +## Next + +### Enhancements +* Conditional reading +* map clojure.core/in-ns to REPL in-ns special for existing tools +* CLJS-1171: map clojure.repl/doc, clojure.repl/source, clojure.repl/dir +* add macroexpand and macroexpand-1 macros +* CLJS-1019: REPL source map caching support +* CLJS-1154: Unmunged function names for stacktrace + +### Changes +* CLJS-1188: multi-arity fns hinder cross-module code motion +* cljs.test needs to default to sync +* CLJS-1184: log module building activity under verbose +* CLJS-1175: CLJS defmulti doesn't exhibit same defonce behavior as Clojure's defmulti, suggesting an even better reloading behavior +* CLJS-1176: redirect node REPL output through *out* and *err*, not System/out, System/err +* CLJS-1144 - expose defaul-dispatch-val and dispatch-fn multifn accessors +* CLJ-1172: supply main entry points for all standard REPLs +* less noisy REPL prompt +* add docstrings & validation to macroexpand & macroexpand-1 + +### Fixes +* CLJS-1192: eliminate JDK8 API dependency in cljs.repl.node +* CLJS-1158: Regression: compiler fails to see symbols defined in another namespace +* CLJS-1189: array-map will return PersistentHashMap if applied to more than (.-HASHMAP-THRESHOLD PersistentArrayMap) pairs +* CLJS-1183: load-file doesn't copy source to output directory +* CLJS-1187: var ast contains internal nodes with bad analysis :context +* CLJS-1182: semantics of load-file should be require + implicit :reload +* CLJS-1179: strange load-file behavior +* CLJS-808: Warning from `find-classpath-lib` mistakenly included in generated source +* CLJS-1169: cannot use REPL load-file on files that declare single segment namespaces +* don't use print unless printing the result of eval +* CLJS-1162: Failure to printStackTrace when REPL initialized +* CLJS-1161: actually print error stack traces to *err*, allow higher-level rebindings of *cljs-ns* +* CLJS-841: cljs.closure/build file locks +* CLJS-1156: load-file fails with :make-reader issue +* CLJS-1152: (require 'some.ns :reload) causes printing to stop working in browser REPL +* CLJS-1157: Stacktrace unmunging blindly use locals +* CLJS-1155: REPL :watch support does not play nicely with :cljs/quit +* CLJS-1137: :cljs/quit fails to actually quit in browser REPL +* CLJS-1148: ClojureScript REPL must maintain eval/print pairing +* make quit-prompt configurable +* CLJS-1149: cljs.repl/repl needs to support :compiler-env option +* CLJS-1140: typo in cljs.repl/repl, `:need-prompt prompt` instead of `:need-prompt need-prompt` + ## 0.0-3126 ### Fixes From e89950e1962d4aece70262dacc7fab6428fb5625 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Apr 2015 09:51:54 -0400 Subject: [PATCH 0980/4033] CLJS-1193: recompile dependents fails to work when not auto-building cljs.analyzer: - ns-dependents, allow supplying custom ns-map. The custom ns-map can supply ns-info map values with :requires entries in a variety of the formats present in the compiler. cljs.compiler: - add *inputs* dynamic var, will hold map from ns -> ns-info - compile-file, merge *inputs* into compiler environment namespaces - compile-root, sort compiler inputs into dependency order. bind *inputs* to a map from ns syms -> inputs (ana/parse-ns results) cljs.analyzer-tests: - add ana/ns-dependents test for new ns-map argument --- src/clj/cljs/analyzer.clj | 32 ++++++++++++++++++------- src/clj/cljs/compiler.clj | 41 ++++++++++++++++++++------------ test/clj/cljs/analyzer_tests.clj | 5 ++++ 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 41f876e87..5f02b54c1 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -545,15 +545,29 @@ (get-in namespaces [ns :macros sym])))))) (defn ns-dependents - ([ns] - (util/topo-sort ns - (fn [ns] - (set - (map first - (filter - (fn [[_ ns-info]] - (contains? (:requires ns-info) ns)) - (get @env/*compiler* ::namespaces)))))))) + "Given a namespace as a symbol and a map from namespace symbol to + namespace information return the topologically sorted list of all + dependent namespaces. The map values of the optional second argument must + be maps with a :requires set of symbols, a :requires map of symbol -> alias + (analyzer format) or a :requires vector of munged namespace strings + (closure format)." + ([ns] (ns-dependents ns (get @env/*compiler* ::namespaces))) + ([ns ns-map] + (letfn [(parent? [parent [child {:keys [requires] :as ns-info}]] + (when-not (= parent child) + (cond + (or (map? requires) + (set? requires)) (contains? requires parent) + (vector? requires) (some #{(munge (name parent))} requires))))] + (util/topo-sort ns + (fn [ns'] + (set (map first (filter #(parent? ns' %) ns-map)))))))) + +(comment + (ns-dependents 'bar + '{bar {:requires #{cljs.core}} + foo {:requires #{cljs.core bar}}}) + ) (declare analyze analyze-symbol analyze-seq) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index e5e66565e..e62c81e95 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -16,7 +16,8 @@ [cljs.tagged-literals :as tags] [cljs.analyzer :as ana] [cljs.source-map :as sm] - [clojure.data.json :as json]) + [clojure.data.json :as json] + [cljs.js-deps :as deps]) (:import java.lang.StringBuilder java.io.File)) @@ -25,6 +26,7 @@ (def js-reserved ana/js-reserved) (def ^:dynamic *dependents* nil) +(def ^:dynamic *inputs* nil) (def ^:dynamic *source-map-data* nil) (def ^:dynamic *lexical-renames* {}) @@ -1101,7 +1103,8 @@ ([src dest opts] {:post [map?]} (binding [ana/*file-defs* (atom #{})] - (let [src-file (io/file src) + (let [nses (get @env/*compiler* ::ana/namespaces) + src-file (io/file src) dest-file (io/file dest) opts (merge {:optimizations :none} opts)] (if (.exists src-file) @@ -1114,21 +1117,23 @@ (if (requires-compilation? src-file dest-file opts) (do (util/mkdirs dest-file) - (when (and (get-in @env/*compiler* [::ana/namespaces ns :defs]) + (when (and (get-in nses [ns :defs]) (not= ns 'cljs.core)) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) (let [ret (compile-file* src-file dest-file opts)] (when *dependents* (swap! *dependents* (fn [{:keys [recompile visited]}] - {:recompile (into recompile (ana/ns-dependents ns)) - :visited (conj visited ns)}))) + {:recompile (into recompile + (ana/ns-dependents ns + (merge *inputs* nses))) + :visited (conj visited ns)}))) ret)) (do ;; populate compilation environment with analysis information ;; when constants are optimized (when (and (true? (:optimize-constants opts)) - (nil? (get-in @env/*compiler* [::ana/namespaces ns :defs]))) + (nil? (get-in nses [ns :defs]))) (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) ns-info))) (catch Exception e @@ -1158,15 +1163,21 @@ (compile-root src-dir target-dir nil)) ([src-dir target-dir opts] (swap! env/*compiler* assoc :root src-dir) - (let [src-dir-file (io/file src-dir)] - (loop [cljs-files (cljs-files-in src-dir-file) - output-files []] - (if (seq cljs-files) - (let [cljs-file (first cljs-files) - output-file (util/to-target-file target-dir (ana/parse-ns cljs-file)) - ns-info (compile-file cljs-file output-file opts)] - (recur (rest cljs-files) (conj output-files (assoc ns-info :file-name (.getPath output-file))))) - output-files))))) + (let [src-dir-file (io/file src-dir) + inputs (deps/dependency-order + (map #(ana/parse-ns %) + (cljs-files-in src-dir-file)))] + (binding [*inputs* (zipmap (map :ns inputs) inputs)] + (loop [inputs (seq inputs) compiled []] + (if inputs + (let [{:keys [source-file] :as ns-info} (first inputs) + output-file (util/to-target-file target-dir ns-info) + ijs (compile-file source-file output-file opts)] + (recur + (next inputs) + (conj compiled + (assoc ijs :file-name (.getPath output-file))))) + compiled)))))) ;; TODO: needs fixing, table will include other things than keywords - David diff --git a/test/clj/cljs/analyzer_tests.clj b/test/clj/cljs/analyzer_tests.clj index 175daf345..b118f5357 100644 --- a/test/clj/cljs/analyzer_tests.clj +++ b/test/clj/cljs/analyzer_tests.clj @@ -332,6 +332,11 @@ (is (= (set (a/ns-dependents 'utils)) #{'file-reloading 'dev 'client 'core})))) +(deftest test-ns-dependents-custom-ns-map + (let [ns-map '{bar {:requires #{cljs.core}} + foo {:requires #{cljs.core bar}}}] + (is (= (set (a/ns-dependents 'bar ns-map)) #{'foo})))) + (deftest test-cljs-1105 ;; munge turns - into _, must preserve the dash first (is (not= (a/gen-constant-id :test-kw) From f9a1f5d8d79caef9ec6439d9c9a345e1ff5901e6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2015 13:59:13 -0400 Subject: [PATCH 0981/4033] cljs.closure/output-one-file should mkdirs for output-to --- src/clj/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 838502a9a..35c00f7c5 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1025,7 +1025,9 @@ (nil? output-to) js (string? output-to) - (spit output-to js) + (do + (util/mkdirs output-to) + (spit output-to js)) :else (println js))) From ff9c3d162dbbe4ffd7e852e4f03e0f5d56394647 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2015 14:31:24 -0400 Subject: [PATCH 0982/4033] fix cljs.closure/output-one-file bug pointed out by Jonas Enlund --- src/clj/cljs/closure.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 35c00f7c5..76ddf1170 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1025,9 +1025,9 @@ (nil? output-to) js (string? output-to) - (do - (util/mkdirs output-to) - (spit output-to js)) + (let [f (io/file output-to)] + (util/mkdirs f) + (spit f js)) :else (println js))) From 40328774cc2f23147c338572c888448a2cce5afb Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2015 16:23:13 -0400 Subject: [PATCH 0983/4033] bump to Clojure 1.7.0-beta1 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 343fbcd37..ec7501e9f 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.7.0-alpha6 + 1.7.0-beta1 com.google.javascript diff --git a/project.clj b/project.clj index 9dd166cce..b419b371e 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ :source-paths ["src/clj"] :resource-paths ["src/cljs"] :test-paths ["test/clj"] - :dependencies [[org.clojure/clojure "1.7.0-alpha6"] + :dependencies [[org.clojure/clojure "1.7.0-beta1"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.1"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] diff --git a/script/bootstrap b/script/bootstrap index df8d68cfb..a3ecec8b6 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.7.0-alpha6" +CLOJURE_RELEASE="1.7.0-beta1" CLOSURE_RELEASE="20150126" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" From 5197e48aeec8800ae05cd42866acf7d4128cd84b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2015 20:13:23 -0400 Subject: [PATCH 0984/4033] 0.0-3196 --- README.md | 6 +++--- changes.md | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cf6bef4c2..97d377772 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3126 +Latest stable release: 0.0-3196 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3126"] +[org.clojure/clojurescript "0.0-3196"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3126 org.clojure clojurescript - 0.0-3126 + 0.0-3196 ``` diff --git a/changes.md b/changes.md index 9f5b0cb7c..8ad11338a 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## Next +## 0.0-3196 ### Enhancements * Conditional reading @@ -9,6 +9,8 @@ * CLJS-1154: Unmunged function names for stacktrace ### Changes +* Clojure 1.7.0-beta1 dependency +* tools.reader 0.9.1 dependency * CLJS-1188: multi-arity fns hinder cross-module code motion * cljs.test needs to default to sync * CLJS-1184: log module building activity under verbose From 3cf142cdfe83b7ce59fcbbe5e0e721f3176123df Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 12 Apr 2015 18:51:35 -0400 Subject: [PATCH 0985/4033] CLJS-1197: load-file does not reload associated macro namespace cljs.analyzer.api: - add remove-ns cljs.analyzer: - add *reload-macros* dynamic var - when associated macro ns & *reload-macros* true, reload the macro ns. cljs.repl: - in load-file remove the namespace so we get a fresh analysis bind *reload-macros* to true when analyzing the file --- src/clj/cljs/analyzer.clj | 7 +++++-- src/clj/cljs/analyzer/api.clj | 8 +++++++- src/clj/cljs/repl.clj | 5 ++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 5f02b54c1..b6c9c6336 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -35,6 +35,7 @@ (def ^:dynamic *analyze-deps* true) (def ^:dynamic *load-tests* true) (def ^:dynamic *load-macros* true) +(def ^:dynamic *reload-macros* false) (def ^:dynamic *macro-infer* true) (def ^:dynamic *file-defs* nil) @@ -1520,14 +1521,16 @@ (load-core) (doseq [nsym (vals use-macros)] (let [k (or (:use-macros @reload) - (get-in @reloads [:use-macros nsym]))] + (get-in @reloads [:use-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] (if k (clojure.core/require nsym k) (clojure.core/require nsym)) (intern-macros nsym k))) (doseq [nsym (vals require-macros)] (let [k (or (:require-macros @reload) - (get-in @reloads [:require-macros nsym]))] + (get-in @reloads [:require-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] (if k (clojure.core/require nsym k) (clojure.core/require nsym)) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index f38a030c7..154844226 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -8,7 +8,7 @@ (ns cljs.analyzer.api (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve find-ns - ns-publics]) + ns-publics remove-ns]) (:require [cljs.env :as env] [cljs.analyzer :as ana])) @@ -63,6 +63,12 @@ {:pre [(symbol? ns) (symbol? sym)]} (get-in @env/*compiler* [::ana/namespaces ns :defs sym])) +(defn remove-ns + "Removes the namespace named by the symbol." + [ns] + {:pre [(symbol? ns)]} + (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) + (defmacro in-cljs-user "Binds cljs.analyzer/*cljs-ns* to 'cljs.user and uses the given compilation environment atom and runs body." diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index eb37f07be..e6ba17e3d 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -503,10 +503,13 @@ (slurp src))) ;; need to load dependencies first (load-dependencies repl-env (:requires compiled) opts) + ;; remove the ns to get :reload semantics + (ana-api/remove-ns (:ns (ana/parse-ns src))) ;; make sure it's been analyzed, this is because if it's already compiled ;; cljs.compiler/compile-file won't do anything, good for builds, ;; but a bit annoying here - (ana/analyze-file src opts) + (binding [ana/*reload-macros* true] + (ana/analyze-file src opts)) (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled)) (-evaluate repl-env f 1 (cljsc/src-file->goog-require src {:wrap true :reload true}))) From 0c0602ac25a4092e0d3fbc53b5a7ff0479307307 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Mon, 13 Apr 2015 19:19:24 +0600 Subject: [PATCH 0986/4033] CLJS-1199 array-map should skip dropped elements of IndexedSeq --- src/cljs/cljs/core.cljs | 2 +- test/cljs/cljs/core_test.cljs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 9a03cbce4..6bc6137a2 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7448,7 +7448,7 @@ reduces them without incurring seq initialization" "keyval => key val Returns a new array map with supplied mappings." [& keyvals] - (let [arr (if (instance? IndexedSeq keyvals) + (let [arr (if (and (instance? IndexedSeq keyvals) (zero? (.-i keyvals))) (.-arr keyvals) (into-array keyvals))] (.fromArray cljs.core/PersistentArrayMap arr true false))) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index cb48e277f..8daffc14d 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2686,7 +2686,7 @@ (defn foo-set [x] (first x)) -(deftest test-cljs-982-var-deref [] +(deftest test-cljs-982-var-deref (let [f (foo-var #'foo-set)] (is (= (f [1 2 3]) 1)) (set! foo-set (fn [x] :oops)) @@ -2731,17 +2731,21 @@ :foo #'foo-1187 :bar #'bar-1187) [])) -(deftest test-cljs-1187 [] +(deftest test-cljs-1187 (testing "Internal var nodes analyzed in expression context" (is (= (with-out-str (print-foo-1187 :foo)) "foo!")))) -(deftest test-cljs-1189 [] +(deftest test-cljs-1189 (testing "array-map should always return array maps" (let [am (apply array-map (range 100))] (is (== (count am) 50)) (is (instance? PersistentArrayMap am))))) +(deftest test-cljs-1199 + (testing "array-map should skip dropped elements of IndexedSeq" + (is (= {:a 1} (apply array-map (drop 1 [0 :a 1])))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From ab1e922a1ce95d96f944ef1ba06cc22d41be8e75 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Apr 2015 07:46:07 -0400 Subject: [PATCH 0987/4033] improve behavior of cljs.repl/mapped-stacktrace if source file does not exist --- src/clj/cljs/repl.clj | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index e6ba17e3d..ae3609306 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -220,25 +220,26 @@ (when-let [smf (util/file-or-resource (str f ".map"))] (let [ns (if (= f "cljs/core.aot.js") 'cljs.core - (:ns (ana/parse-ns (js-src->cljs-src f))))] - (as-> @env/*compiler* compiler-env - (let [t (util/last-modified smf)] - (if (or (and (= ns 'cljs.core) - (nil? (get-in compiler-env [::source-maps ns]))) - (and (not= ns 'cljs.core) - (> t (get-in compiler-env [::source-maps ns :last-modified] 0)))) - (swap! env/*compiler* assoc-in [::source-maps ns] - {:last-modified t - :source-map (sm/decode (json/read-str (slurp smf) :key-fn keyword))}) - compiler-env)) - (get-in compiler-env [::source-maps ns :source-map]))))) + (some-> (js-src->cljs-src f) ana/parse-ns :ns))] + (when ns + (as-> @env/*compiler* compiler-env + (let [t (util/last-modified smf)] + (if (or (and (= ns 'cljs.core) + (nil? (get-in compiler-env [::source-maps ns]))) + (and (not= ns 'cljs.core) + (> t (get-in compiler-env [::source-maps ns :last-modified] 0)))) + (swap! env/*compiler* assoc-in [::source-maps ns] + {:last-modified t + :source-map (sm/decode (json/read-str (slurp smf) :key-fn keyword))}) + compiler-env)) + (get-in compiler-env [::source-maps ns :source-map])))))) (defn ns-info "Given a path to a js source file return the ns info for the corresponding ClojureScript file if it exists." [f] (let [f' (js-src->cljs-src f)] - (when (.exists f') + (when (and f' (.exists f')) (ana/parse-ns f')))) (defn- mapped-line-column-call @@ -334,12 +335,12 @@ print the ClojureScript stacktrace. See mapped-stacktrace." ([stacktrace] (print-mapped-stacktrace stacktrace *repl-opts*)) ([stacktrace opts] - (doseq [{:keys [function file line column]} - (mapped-stacktrace stacktrace opts)] - (err-out - (println "\t" - (str (when function (str function " ")) - "(" file (when line (str ":" line)) (when column (str ":" column)) ")")))))) + (doseq [{:keys [function file line column]} + (mapped-stacktrace stacktrace opts)] + (err-out + (println "\t" + (str (when function (str function " ")) + "(" file (when line (str ":" line)) (when column (str ":" column)) ")")))))) (comment (cljsc/build "samples/hello/src" From 53780c6e0aaf68e8c687ebb666cf2d4092ba136f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Apr 2015 08:23:02 -0400 Subject: [PATCH 0988/4033] CLJS-1202: cljs.repl/load-file is not additive cljs.compiler: - compile-file, if :force option supplied, always compile. if the :mode is :interactive, do not remove the namespace. cljs.repl: - remove ns removing. remove analysis. rely on cljs.compiler/compile-file to handle properly via :force and :mode options. --- src/clj/cljs/compiler.clj | 6 ++++-- src/clj/cljs/repl.clj | 17 ++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index e62c81e95..01dd0d816 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -1114,11 +1114,13 @@ (not (false? (:static-fns opts)))) (assoc opts :static-fns true) opts)] - (if (requires-compilation? src-file dest-file opts) + (if (or (requires-compilation? src-file dest-file opts) + (:force opts)) (do (util/mkdirs dest-file) (when (and (get-in nses [ns :defs]) - (not= ns 'cljs.core)) + (not= 'cljs.core ns) + (not= :interactive (:mode opts))) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) (let [ret (compile-file* src-file dest-file opts)] (when *dependents* diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index ae3609306..f9cebcda3 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -492,10 +492,12 @@ (util/url? f) f (.exists (io/file f)) (io/file f) :else (io/resource f)) - compiled (cljsc/compile src - (assoc opts - :output-file - (cljsc/src-file->target-file src)))] + compiled (binding [ana/*reload-macros* true] + (cljsc/compile src + (assoc opts + :output-file (cljsc/src-file->target-file src) + :force true + :mode :interactive)))] ;; copy over the original source file if source maps enabled (when-let [ns (and (:source-map opts) (first (:provides compiled)))] (spit @@ -504,13 +506,6 @@ (slurp src))) ;; need to load dependencies first (load-dependencies repl-env (:requires compiled) opts) - ;; remove the ns to get :reload semantics - (ana-api/remove-ns (:ns (ana/parse-ns src))) - ;; make sure it's been analyzed, this is because if it's already compiled - ;; cljs.compiler/compile-file won't do anything, good for builds, - ;; but a bit annoying here - (binding [ana/*reload-macros* true] - (ana/analyze-file src opts)) (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled)) (-evaluate repl-env f 1 (cljsc/src-file->goog-require src {:wrap true :reload true}))) From c7082b4149096b9a6f3fd937d69c27b259c0340a Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Tue, 14 Apr 2015 18:09:15 +0600 Subject: [PATCH 0989/4033] CLJS-1201 Empty Vectors and Subvecs throw on comparison --- src/cljs/cljs/core.cljs | 1 + test/cljs/cljs/core_test.cljs | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 6bc6137a2..2794acc98 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1938,6 +1938,7 @@ reduces them without incurring seq initialization" (cond (< xl yl) -1 (> xl yl) 1 + (== xl 0) 0 :else (compare-indexed xs ys xl 0)))) ([xs ys len n] (let [d (compare (nth xs n) (nth ys n))] diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 8daffc14d..a72c2b510 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1679,10 +1679,15 @@ (is (= 1 (compare [1 2] [1 1]))) (is (= 1 (compare [1 1 1] [1 2]))) (is (= 1 (compare [1 1 2] [1 1 1]))) + (is (= 0 (compare [] []))) + (is (= 0 (compare (vec #js []) []))) + (is (= 0 (compare (with-meta [] {}) []))) + (is (= 0 (compare (pop [1]) []))) (is (= -1 (compare (subvec [1 2 3] 1) (subvec [1 2 4] 1)))) (is (= 0 (compare (subvec [1 2 3] 1) (subvec [1 2 3] 1)))) - (is (= 1 (compare (subvec [1 2 4] 1) (subvec [1 2 3] 1))))) + (is (= 1 (compare (subvec [1 2 4] 1) (subvec [1 2 3] 1)))) + (is (= 0 (compare (subvec [1] 0 0) (subvec [2] 0 0)))) (is (= 0 (compare (js/Date. 2015 2 8 19 13 00 999) (js/Date. 2015 2 8 19 13 00 999)))) @@ -1690,7 +1695,7 @@ (js/Date. 2015 2 8 19 13 00 999)))) (is (= 1 (compare (js/Date. 2015 2 8 19 14 00 999) (js/Date. 2015 2 8 19 13 00 999)))) - ) + )) (deftest test-dot (let [s "abc"] From 5353489fe3beda9a6e9d044e89da3bd0d2accce7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Apr 2015 10:02:00 -0400 Subject: [PATCH 0990/4033] CLJS-1203: official way to supply multiple files/directories to cljs.closure/build and cljs.closure/watch Add cljs.build.api/inputs which returns cljs.closure/Compilable instance Add cljs.build.api/build, will eventually become the only public entry point for building --- src/clj/cljs/build/api.clj | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/clj/cljs/build/api.clj b/src/clj/cljs/build/api.clj index f025ad444..13839dc28 100644 --- a/src/clj/cljs/build/api.clj +++ b/src/clj/cljs/build/api.clj @@ -21,6 +21,9 @@ [clojure.java.io :as io]) (:import java.io.File)) +;; ============================================================================= +;; Useful Utilities + (defn ^File target-file-for-cljs-ns "Given an output directory and a clojurescript namespace return the compilation target file for that namespace. @@ -90,6 +93,30 @@ ([src options] (closure/src-file->goog-require src options))) +;; ============================================================================= +;; Main API + +(defn inputs + "Given a list of directories and files, return a compilable object that may + be passed to build or watch." + [& xs] + (reify + closure/Compilable + (-compile [_ opts] + (letfn [(compile-input [x] + (let [compiled (closure/-compile x opts)] + (if (sequential? compiled) + compiled + [compiled])))] + (mapcat compile-input xs))))) + +(defn build + "Given a source which can be compiled, produce runnable JavaScript." + ([source opts] + (closure/build source opts)) + ([source opts compiler-env] + (closure/build source opts compiler-env))) + (comment (def test-cenv (atom {})) From e8a4ad9db140ba47179850b32e9b757c9193d3f8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Apr 2015 19:10:02 -0400 Subject: [PATCH 0991/4033] CLJS-1204: cljs.closure/watch cannot take cljs.closure/Compilable Add cljs.closure/Inputs protocol, extend to strings & files. Change cls.closure/watch to call through the protocol on source argument. Watch all input sources. cljs.build.api/inputs now returns Inputs instance. Add watch to the cljs.build.api ns. --- src/clj/cljs/build/api.clj | 12 ++++++++++++ src/clj/cljs/closure.clj | 31 +++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/build/api.clj b/src/clj/cljs/build/api.clj index 13839dc28..6db831610 100644 --- a/src/clj/cljs/build/api.clj +++ b/src/clj/cljs/build/api.clj @@ -101,6 +101,9 @@ be passed to build or watch." [& xs] (reify + closure/Inputs + (-paths [_] + (map io/file xs)) closure/Compilable (-compile [_ opts] (letfn [(compile-input [x] @@ -117,6 +120,15 @@ ([source opts compiler-env] (closure/build source opts compiler-env))) +(defn watch + "Given a source which can be compiled, watch it for changes to produce." + ([source opts] + (closure/watch source opts)) + ([source opts compiler-env] + (closure/watch source opts compiler-env)) + ([source opts compiler-env stop] + (closure/watch source opts compiler-env stop))) + (comment (def test-cenv (atom {})) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 76ddf1170..1f9a9bda8 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -313,6 +313,15 @@ ;; Compile ;; ======= +(defprotocol Inputs + (-paths [this] "Returns the file paths to the source inputs")) + +(extend-protocol Inputs + String + (-paths [this] [(io/file this)]) + File + (-paths [this] [this])) + (defprotocol Compilable (-compile [this opts] "Returns one or more IJavaScripts.")) @@ -1512,12 +1521,13 @@ ([source opts compiler-env] (watch source opts compiler-env nil)) ([source opts compiler-env quit] - (let [opts (cond-> opts - (= (:verbose opts :not-found) :not-found) - (assoc :verbose true)) - path (Paths/get (.toURI (io/file source))) - fs (.getFileSystem path) - service (.newWatchService fs)] + (let [opts (cond-> opts + (= (:verbose opts :not-found) :not-found) + (assoc :verbose true)) + paths (map #(Paths/get (.toURI %)) (-paths source)) + path (first paths) + fs (.getFileSystem path) + srvc (.newWatchService fs)] (letfn [(buildf [] (try (let [start (System/nanoTime)] @@ -1537,7 +1547,7 @@ (preVisitDirectory [_ dir _] (let [^Path dir dir] (. dir - (register service + (register srvc (into-array [StandardWatchEventKinds/ENTRY_CREATE StandardWatchEventKinds/ENTRY_DELETE StandardWatchEventKinds/ENTRY_MODIFY]) @@ -1552,12 +1562,13 @@ (println "Building ...") (flush) (buildf) - (println "Watching path:" source) - (watch-all path) + (println "Watching paths:" (apply str (interpose ", " paths))) + (doseq [path paths] + (watch-all path)) (loop [key nil] (when (and (or (nil? quit) (not @quit)) (or (nil? key) (. ^WatchKey key reset))) - (let [key (. service (poll 300 TimeUnit/MILLISECONDS))] + (let [key (. srvc (poll 300 TimeUnit/MILLISECONDS))] (when (and key (some (fn [^WatchEvent e] From 78c882f1f61023c0b0a75b87bd8e8bcb7058ed04 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Apr 2015 19:33:12 -0400 Subject: [PATCH 0992/4033] CLJS-1205: Support conditional reading in REPLs Same as CLJ-1200 --- src/clj/cljs/repl.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index f9cebcda3..d52311324 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -93,7 +93,7 @@ *in*)] (or ({:line-start request-prompt :stream-end request-exit} (skip-whitespace *in*)) - (let [input (reader/read)] + (let [input (reader/read {:read-cond :allow :features #{:cljs}} *in*)] (skip-if-eol *in*) input))))) From d1b14818730a65ef1e752731d55a5661a1e8add7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 15 Apr 2015 10:03:58 -0400 Subject: [PATCH 0993/4033] export some obviously useful utilities to cljs.analyzer.api --- src/clj/cljs/analyzer/api.clj | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index 154844226..d6edaa27e 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -12,6 +12,59 @@ (:require [cljs.env :as env] [cljs.analyzer :as ana])) +;; ============================================================================= + +(defn empty-env + "Creates an empty analysis environment." + [] + (ana/empty-env)) + +(defn analyze + "Given an environment, a map containing {:locals (mapping of names to bindings), :context + (one of :statement, :expr, :return), :ns (a symbol naming the + compilation ns)}, and form, returns an expression object (a map + containing at least :form, :op and :env keys). If expr has any (immediately) + nested exprs, must have :children [exprs...] entry. This will + facilitate code walking without knowing the details of the op set." + ([env form] (ana/analyze env form nil)) + ([env form name] (ana/analyze env form name nil)) + ([env form name opts] (ana/analyze env form name opts))) + +(defn forms-seq + "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally + accepts a filename argument which will be used in any emitted errors." + ([rdr] (ana/forms-seq* rdr nil)) + ([rdr filename] (ana/forms-seq* rdr filename))) + +(defn parse-ns + "Helper for parsing only the essential namespace information from a + ClojureScript source file and returning a cljs.closure/IJavaScript compatible + map _not_ a namespace AST node. + + By default does not load macros or perform any analysis of dependencies. If + opts parameter provided :analyze-deps and :load-macros keys their values will + be used for *analyze-deps* and *load-macros* bindings respectively. This + function does _not_ side-effect the ambient compilation environment unless + requested via opts where :restore is false." + ([src] (ana/parse-ns src nil nil)) + ([src opts] (ana/parse-ns src nil opts)) + ([src dest opts] (ana/parse-ns src dest opts))) + +(defn analyze-file + "Given a java.io.File, java.net.URL or a string identifying a resource on the + classpath attempt to analyze it. + + This function side-effects the ambient compilation environment + `cljs.env/*compiler*` to aggregate analysis information. opts argument is + compiler options, if :cache-analysis true will cache analysis to + \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a + meaningful value." + ([f] (ana/analyze-file f nil)) + ([f opts] (ana/analyze-file f opts))) + +;; ============================================================================= +;; Main API + (defn resolve "Given an analysis environment resolve a var. Analogous to clojure.core/resolve" From cfa1d9a724b21ef028a21ec3dca6f6ff4de12ac5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 15 Apr 2015 10:28:52 -0400 Subject: [PATCH 0994/4033] missing comment --- src/clj/cljs/analyzer/api.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/cljs/analyzer/api.clj b/src/clj/cljs/analyzer/api.clj index d6edaa27e..f0cb3b435 100644 --- a/src/clj/cljs/analyzer/api.clj +++ b/src/clj/cljs/analyzer/api.clj @@ -13,6 +13,7 @@ [cljs.analyzer :as ana])) ;; ============================================================================= +;; Useful Utilities (defn empty-env "Creates an empty analysis environment." From 58add51bc55e52949f870975b032d2ef5ce06b8b Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Apr 2015 15:07:24 -0400 Subject: [PATCH 0995/4033] bug noticed by Kovas Boguta, cljs.analyzer/parse-ns needs to bind *cljs-file* --- src/clj/cljs/analyzer.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index b6c9c6336..85c7f9d7d 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -2054,6 +2054,7 @@ [ijs compiler-env'] (binding [env/*compiler* (atom compiler-env) *cljs-ns* 'cljs.user + *cljs-file* src *macro-infer* (or (when (contains? opts :macro-infer) (:macro-infer opts)) From b25f0314bbb7298bf79de9f3b65d83b6635bad50 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Apr 2015 07:23:25 +0200 Subject: [PATCH 0996/4033] update .iml --- Clojurescript.iml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index 9bccacfd5..04d991e59 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -22,15 +22,15 @@ - + - - + + - - + + From 341d41b34db11899d60bd0d98465cc1f0e81c22e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Apr 2015 07:33:10 +0200 Subject: [PATCH 0997/4033] add simple REPL script --- script/repl.clj | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 script/repl.clj diff --git a/script/repl.clj b/script/repl.clj new file mode 100644 index 000000000..72de2584e --- /dev/null +++ b/script/repl.clj @@ -0,0 +1,3 @@ +(require '[cljs.repl :as repl]) +(require '[cljs.repl.node :as node]) +(repl/repl (node/repl-env)) \ No newline at end of file From c2296b11cc89a81baef1cd6a11567150d48605d9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Apr 2015 07:39:22 +0200 Subject: [PATCH 0998/4033] CLJS-1216: regression, varargs not passed properly Calculation of max fixed arity did not consider the variadic signature --- src/clj/cljs/core.clj | 12 +++++++++--- test/cljs/cljs/core_test.cljs | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 928547852..18381c34b 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -2146,9 +2146,13 @@ (fn ~method))))] (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) arglists (map first fdecl) - variadic (boolean (some #(some '#{&} %) arglists)) - sigs (remove #(some '#{&} %) arglists) - maxfa (apply core/max (map count sigs)) + varsig? #(some '#{&} %) + variadic (boolean (some varsig? arglists)) + sigs (remove varsig? arglists) + maxfa (apply core/max + (concat + (map count sigs) + [(core/- (count (first (filter varsig? arglists))) 2)])) meta (assoc meta :top-fn {:variadic variadic @@ -2181,6 +2185,8 @@ (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a b])))) (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a & xs])))) (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a [b & cs] & xs])))) + ;; CLJS-1216 + (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a b & xs])))) ) (def diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index a72c2b510..7f63cc791 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2751,6 +2751,26 @@ (testing "array-map should skip dropped elements of IndexedSeq" (is (= {:a 1} (apply array-map (drop 1 [0 :a 1])))))) +(defn foo-1216 + ([a] (foo-1216 a 10)) + ([a b & [c]] [a b c])) + +(defn destructure-1216 + ([kvs] kvs) + ([k v & args] [k v args])) + +(deftest test-cljs-1216 + (testing "varargs regression" + (is (= (foo-1216 1) [1 10 nil])) + (is (= (foo-1216 1 2) [1 2 nil])) + (is (= (foo-1216 1 2 3) [1 2 3])) + (is (= [1 2 [3 4]] + (destructure-1216 1 2 3 4))) + (is (= [1 2 [3 4]] + (apply destructure-1216 [1 2 3 4]))) + (is (= (destructure-1216 1 2 3 4)[1 2 [3 4]] + (apply destructure-1216 [1 2 3 4]))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 896394e8135a945efb39bdf335e376944090c17a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Apr 2015 07:49:34 +0200 Subject: [PATCH 0999/4033] 0.0-3211 --- README.md | 6 +++--- changes.md | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 97d377772..ebaaf828b 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3196 +Latest stable release: 0.0-3211 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3196"] +[org.clojure/clojurescript "0.0-3211"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3196 org.clojure clojurescript - 0.0-3196 + 0.0-3211 ``` diff --git a/changes.md b/changes.md index 8ad11338a..1aa7e55e0 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,18 @@ +## 0.0-3211 + +### Changes +* CLJS-1205: Conditional reading in REPLs +* CLJS-1204: cljs.build.api/watch can now take compilation inputs +* 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.analyzer/parse-ns did not bind *cljs-file* +* CLJS-1201: compare broken for IIndexed collections +* CLJS-1202: cljs.repl/load-file is not additive +* CLJS-1199: array-map should skip dropped elements of IndexedSeq +* CLJS-1197: load-file does not reload associated macro namespace + ## 0.0-3196 ### Enhancements From a9474ead5761032ed8503886b9695bd3caa98424 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Apr 2015 12:52:58 +0200 Subject: [PATCH 1000/4033] CLJS-742: Compilation with :output-file option set fails The coll? check on compiler input(s) would incorrectly permit non-sequential collections to pass through instead of being wrapped by a vector. Change the check to sequential? --- src/clj/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj index 1f9a9bda8..9970ac500 100644 --- a/src/clj/cljs/closure.clj +++ b/src/clj/cljs/closure.clj @@ -1450,7 +1450,7 @@ (concat (apply add-dependencies all-opts (concat - (if (coll? compiled) compiled [compiled]) + (if (sequential? compiled) compiled [compiled]) (when (= :nodejs (:target all-opts)) [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))) (when (= :nodejs (:target all-opts)) From 9a68234c0b1d45639bdfde040f582976eabe5523 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Apr 2015 13:06:45 +0200 Subject: [PATCH 1001/4033] CLJS-1213: cljs.analyzer incorrectly marks all defs as tests when eliding test metadata fix the cond-> to actually check for :test presence. --- src/clj/cljs/analyzer.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 85c7f9d7d..80bf23c9a 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -856,9 +856,10 @@ (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] (merge {:name var-name} - ;; elide test metadata, as it includes non-valid EDN - David + ;; remove actual test metadata, as it includes non-valid EDN and + ;; cannot be present in analysis cached to disk - David (cond-> sym-meta - :test (-> (dissoc :test) (assoc :test true))) + (:test sym-meta) (assoc :test true)) {:meta (-> sym-meta (dissoc :test) (update-in [:file] From c4370421b196b96f7a1197a07c7f3453f08cd353 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Apr 2015 13:21:34 +0200 Subject: [PATCH 1002/4033] deprecate the old samples and direct users to the Quick Start --- samples/hello-js/README.md | 2 ++ samples/hello/README.md | 2 ++ samples/repl/README.md | 3 +++ samples/twitterbuzz/README.md | 2 ++ 4 files changed, 9 insertions(+) diff --git a/samples/hello-js/README.md b/samples/hello-js/README.md index 32fa6793c..b796e5386 100644 --- a/samples/hello-js/README.md +++ b/samples/hello-js/README.md @@ -1,3 +1,5 @@ +*NOTE: this sample is now out of date. Please refer to the Quick Start* + Simple ClojureScript Project Example Using an External JavaScript Library One-time Setup diff --git a/samples/hello/README.md b/samples/hello/README.md index d54ef3467..405ad9c82 100644 --- a/samples/hello/README.md +++ b/samples/hello/README.md @@ -1,3 +1,5 @@ +*NOTE: this sample is now out of date. Please refer to the Quick Start* + Simple ClojureScript Project Example. One-time Setup diff --git a/samples/repl/README.md b/samples/repl/README.md index 728b5da02..d9493b8c9 100644 --- a/samples/repl/README.md +++ b/samples/repl/README.md @@ -1,3 +1,6 @@ +*NOTE: this sample is now out of date. Please refer to the Quick +Start* + # ClojureScript REPL Examples The ClojureScript REPL has been updated to work with multiple diff --git a/samples/twitterbuzz/README.md b/samples/twitterbuzz/README.md index 3b9fd2eda..0eff7a65b 100644 --- a/samples/twitterbuzz/README.md +++ b/samples/twitterbuzz/README.md @@ -1,3 +1,5 @@ +*NOTE: this sample is now out of date. Please refer to the Quick Start* + # ClojureScript "TwitterBuzz" Demo ## One-time Setup From ef666377ad29fd285267aa1dc479da8ae18a547d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Apr 2015 15:13:30 +0200 Subject: [PATCH 1003/4033] support ^long and ^double type hints --- src/clj/cljs/analyzer.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 80bf23c9a..7b7445cab 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -1707,12 +1707,16 @@ (when-not (every? (fn [t] (or (nil? t) - (and (symbol? t) ('#{any number} t)) + (and (symbol? t) ('#{any number long double} t)) ;; TODO: type inference is not strong enough to detect that ;; when functions like first won't return nil, so variadic ;; numeric functions like cljs.core/< would produce a spurious ;; warning without this - David - (and (set? t) (or (contains? t 'number) (contains? t 'any))))) + (and (set? t) + (or (contains? t 'number) + (contains? t 'long) + (contains? t 'double) + (contains? t 'any))))) types) (warning :invalid-arithmetic env {:js-op (-> form meta :js-op) From d33b6d09da6ff2ed91da0ab8222e52ca9fdf1c91 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Apr 2015 15:24:09 +0200 Subject: [PATCH 1004/4033] add getBasis methods to deftype and defrecord ctors a la Clojure JVM --- src/clj/cljs/core.clj | 2 ++ test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index 18381c34b..d9a244581 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -1083,6 +1083,7 @@ (deftype* ~t ~fields ~pmasks ~(if (seq impls) `(extend-type ~t ~@(dt->et t impls fields)))) + (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)))) @@ -1238,6 +1239,7 @@ assoc :internal-ctor true)] `(let [] ~(emit-defrecord &env rsym r fields impls) + (set! (.-getBasis ~r) (fn [] '[~@fields])) (set! (.-cljs$lang$type ~r) true) (set! (.-cljs$lang$ctorPrSeq ~r) (fn [this#] (core/list ~(core/str r)))) (set! (.-cljs$lang$ctorPrWriter ~r) (fn [this# writer#] (-write writer# ~(core/str r)))) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 7f63cc791..4db6dcc7a 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2771,6 +2771,14 @@ (is (= (destructure-1216 1 2 3 4)[1 2 [3 4]] (apply destructure-1216 [1 2 3 4]))))) +(deftype TypeBasis [a b]) + +(defrecord RecordBasis [c d e]) + +(deftest test-get-basis + (is (= (.getBasis TypeBasis) '[a b])) + (is (= (.getBasis RecordBasis) '[c d e]))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From e3ba4f42715c537d6241fff21731d7b2c848fe02 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 28 Apr 2015 21:23:10 -0400 Subject: [PATCH 1005/4033] CLJS-1218: Syntax quoting an alias created with :require-macros throws ClassCastException Bump tools.reader dependency to 0.9.2. Also bump to Clojure 1.7.0-beta2 --- pom.template.xml | 4 ++-- project.clj | 4 ++-- script/bootstrap | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index ec7501e9f..85bb97b1c 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.7.0-beta1 + 1.7.0-beta2 com.google.javascript @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.9.1 + 0.9.2 diff --git a/project.clj b/project.clj index b419b371e..fff5e2d7f 100644 --- a/project.clj +++ b/project.clj @@ -8,9 +8,9 @@ :source-paths ["src/clj"] :resource-paths ["src/cljs"] :test-paths ["test/clj"] - :dependencies [[org.clojure/clojure "1.7.0-beta1"] + :dependencies [[org.clojure/clojure "1.7.0-beta2"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "0.9.1"] + [org.clojure/tools.reader "0.9.2"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] [com.google.javascript/closure-compiler "v20150126"] [org.mozilla/rhino "1.7R5"]] diff --git a/script/bootstrap b/script/bootstrap index a3ecec8b6..2004a2d6c 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,12 +2,12 @@ set -e -CLOJURE_RELEASE="1.7.0-beta1" +CLOJURE_RELEASE="1.7.0-beta2" CLOSURE_RELEASE="20150126" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="0.9.1" +TREADER_RELEASE="0.9.2" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From 4773730eda0a5c5d55cbb90adef54bc4bbe6b35e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 26 Apr 2015 22:09:47 -0400 Subject: [PATCH 1006/4033] CLJS-1224: cljs.repl: Memoize stack frame mapping When evaluating an expression that results in a stack overflow, and the resulting trace is source mapped by cljs.repl, this can result in thousands of frames being mapped which can take a few minutes. But, oftentimes stack overflow is the result of recursion, so many of the mapped frames are identical. This is trivial to optimize using memoize, resulting in the mapping occurring in a couple of seconds. --- src/clj/cljs/repl.clj | 83 ++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj index d52311324..c0e49f54a 100644 --- a/src/clj/cljs/repl.clj +++ b/src/clj/cljs/repl.clj @@ -263,6 +263,46 @@ [:line :col :name]))) default))) +(defn- mapped-frame + "Given opts and a canonicalized JavaScript stacktrace frame, return the + ClojureScript frame." + [{:keys [function file line column]} opts] + (let [no-source-file? (if-not file + true + (.startsWith file "<")) + rfile (when-not no-source-file? + (io/file (URL. (.toURL (io/file (util/output-directory opts))) file))) + [sm {:keys [ns source-file] :as ns-info}] + (when-not no-source-file? + ((juxt read-source-map ns-info) rfile)) + [line' column' call] (if ns-info + (mapped-line-column-call sm line column) + [line column]) + name' (when (and ns-info function) + function) + file' (if no-source-file? + file + (string/replace + (.getCanonicalFile + (if ns-info + source-file + (io/file rfile))) + (str (System/getProperty "user.dir") File/separator) "")) + url (or (and ns-info (util/ns->source ns)) + (and file (io/resource file)))] + (merge + {:function name' + :call call + :file (if no-source-file? + (str "NO_SOURCE_FILE" + (when file + (str " " file))) + (io/file file')) + :line line' + :column column'} + (when url + {:url url})))) + (defn mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace return the ClojureScript stacktrace. The canonical stacktrace must be @@ -280,55 +320,18 @@ ([stacktrace] (mapped-stacktrace stacktrace nil)) ([stacktrace opts] (vec - (let [with-calls - (for [{:keys [function file line column] :as frame} stacktrace] - ;; need to convert file, a relative URL style path, to host-specific file - (let [no-source-file? (if-not file - true - (.startsWith file "<")) - rfile (when-not no-source-file? - (io/file (URL. (.toURL (io/file (util/output-directory opts))) file))) - [sm {:keys [ns source-file] :as ns-info}] - (when-not no-source-file? - ((juxt read-source-map ns-info) rfile)) - [line' column' call] (if ns-info - (mapped-line-column-call sm line column) - [line column]) - name' (when (and ns-info function) - function) - file' (if no-source-file? - file - (string/replace - (.getCanonicalFile - (if ns-info - source-file - (io/file rfile))) - (str (System/getProperty "user.dir") File/separator) "")) - url (or (and ns-info (util/ns->source ns)) - (and file (io/resource file)))] - (merge - {:function name' - :call call - :file (if no-source-file? - (str "NO_SOURCE_FILE" - (when file - (str " " file))) - (io/file file')) - :line line' - :column column'} - (when url - {:url url}))))] + (let [mapped-frames (map (memoize #(mapped-frame % opts)) stacktrace)] ;; take each non-nil :call and optionally merge it into :function one-level up ;; to avoid replacing with local symbols, we only replace munged name if we can munge call symbol back to it (map #(merge-with (fn [munged-fn-name unmunged-call-name] (if (= munged-fn-name (string/replace (cljs.compiler/munge unmunged-call-name) "." "$")) unmunged-call-name munged-fn-name)) %1 %2) - (map #(dissoc % :call) with-calls) + (map #(dissoc % :call) mapped-frames) (concat (rest (map #(if (:call %) (hash-map :function (:call %)) {}) - with-calls)) [{}])))))) + mapped-frames)) [{}])))))) (defn print-mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace From 4c50d43592074a72a84ca35eceaf5e43334da49e Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Sun, 29 Mar 2015 00:52:37 -0500 Subject: [PATCH 1007/4033] CLJS-1178: Compiler does not know Math ns is not not-native Math functions with static-fns true compilation modes would emit arity checks or use .call() for invocation. Now they use direct invocation. --- src/clj/cljs/compiler.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 01dd0d816..9fc1d7a82 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -759,7 +759,7 @@ opt-not? (and (= (:name info) 'cljs.core/not) (= (ana/infer-tag env (first (:args expr))) 'boolean)) ns (:ns info) - js? (= ns 'js) + js? (or (= ns 'js) (= ns 'Math)) goog? (when ns (or (= ns 'goog) (when-let [ns-str (str ns)] From 51e8195bb204720953201a33f0218a88fbda8709 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Apr 2015 08:55:30 -0400 Subject: [PATCH 1008/4033] add public cljs.compiler.api namespace --- src/clj/cljs/compiler/api.clj | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/clj/cljs/compiler/api.clj diff --git a/src/clj/cljs/compiler/api.clj b/src/clj/cljs/compiler/api.clj new file mode 100644 index 000000000..dab660966 --- /dev/null +++ b/src/clj/cljs/compiler/api.clj @@ -0,0 +1,18 @@ +; 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.compiler.api + "This is intended to be a stable api for those who intend to create + tools that use compiler data. + + For example: a build script may need to how to invalidate compiled + files so that they will be recompiled." + (:require [cljs.util :as util] + [cljs.env :as env] + [cljs.analyzer :as ana] + [cljs.compiler :as comp])) From 41af5fe479ca02130f9ccf04c5fa8562c814f432 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Apr 2015 18:56:53 -0400 Subject: [PATCH 1009/4033] add basics to cljs.compiler/api --- src/clj/cljs/compiler/api.clj | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/clj/cljs/compiler/api.clj b/src/clj/cljs/compiler/api.clj index dab660966..5aa79127e 100644 --- a/src/clj/cljs/compiler/api.clj +++ b/src/clj/cljs/compiler/api.clj @@ -16,3 +16,63 @@ [cljs.env :as env] [cljs.analyzer :as ana] [cljs.compiler :as comp])) + +;; ============================================================================= +;; Main API + +(defn emit + "Given an AST node generated by the analyzer emit JavaScript as a string." + [ast] + (with-out-str + (comp/emit ast))) + +(defn with-core-cljs + "Ensure that core.cljs has been loaded." + ([] (comp/with-core-cljs nil)) + ([opts] (comp/with-core-cljs opts (fn []))) + ([opts body] (comp/with-core-cljs opts body))) + +(defn requires-compilation? + "Return true if the src file requires compilation." + ([src dest] + (comp/requires-compilation? src dest nil)) + ([src dest opts] + (comp/requires-compilation? src dest opts))) + +(defn compile-file + "Compiles src to a file of the same name, but with a .js extension, + in the src file's directory. + + With dest argument, write file to provided location. If the dest + argument is a file outside the source tree, missing parent + directories will be created. The src file will only be compiled if + the dest file has an older modification time. + + Both src and dest may be either a String or a File. + + Returns a map containing {:ns .. :provides .. :requires .. :file ..}. + If the file was not compiled returns only {:file ...}" + ([src] + (comp/compile-file src)) + ([src dest] + (comp/compile-file src dest)) + ([src dest opts] + (comp/compile-file src dest opts))) + +(defn cljs-files-in + "Return a sequence of all .cljs and .cljc files in the given directory." + [dir] + (comp/cljs-files-in dir)) + +(defn compile-root + "Looks recursively in src-dir for .cljs files and compiles them to + .js files. If target-dir is provided, output will go into this + directory mirroring the source directory structure. Returns a list + of maps containing information about each file which was compiled + in dependency order." + ([src-dir] + (comp/compile-root src-dir "out")) + ([src-dir target-dir] + (comp/compile-root src-dir target-dir nil)) + ([src-dir target-dir opts] + (comp/compile-root src-dir target-dir opts))) \ No newline at end of file From 7f468d093363c5aa5533743efba9e20b8c185cd4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Apr 2015 20:14:58 -0400 Subject: [PATCH 1010/4033] docstrings --- src/clj/cljs/analyzer.clj | 46 ++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 7b7445cab..c607d7a16 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -298,6 +298,8 @@ (or (-> x meta :column) (:column env))) (defn intern-macros + "Given a Clojure namespace intern all macros into the ambient ClojureScript + analysis environment." ([ns] (intern-macros ns false)) ([ns reload] (when (or (nil? (get-in @env/*compiler* [::namespaces ns :macros])) @@ -338,7 +340,9 @@ *cljs-macros-is-classpath* false] ~@body))) -(defn empty-env [] +(defn empty-env + "Construct an empty analysis environment. Required to analyze forms." + [] (env/ensure {:ns (get-namespace *cljs-ns*) :context :statement @@ -427,7 +431,10 @@ (let [sym (symbol name)] (get (:require-macros (:ns env)) sym sym))) -(defn confirm-ns [env ns-sym] +(defn confirm-ns + "Given env, an analysis environment, and ns-sym, a symbol identifying a + namespace, confirm that the namespace exists. Warn if not found." + [env ns-sym] (when (and (nil? (get '#{cljs.core goog Math goog.string} ns-sym)) (nil? (get (-> env :ns :requires) ns-sym)) ;; something else may have loaded the namespace, i.e. load-file @@ -512,19 +519,27 @@ {:name (symbol (str full-ns) (str sym)) :ns full-ns}))))))) -(defn resolve-existing-var [env sym] +(defn resolve-existing-var + "Given env, an analysis environment, and sym, a symbol, resolve an existing var. + Emits a warning if no such var exists." + [env sym] (if-not (-> sym meta ::no-resolve) (resolve-var env sym confirm-var-exists) (resolve-var env sym))) -(defn confirm-bindings [env names] +(defn confirm-bindings + "Given env, an analysis environment env, and names, a list of symbols, confirm + that all correspond to declared dynamic vars." + [env names] (doseq [name names] (let [env (assoc env :ns (get-namespace *cljs-ns*)) ev (resolve-existing-var env name)] (when (and ev (not (-> ev :dynamic))) (warning :dynamic env {:ev ev :name (:name ev)}))))) -(defn resolve-macro-var [env sym] +(defn resolve-macro-var + "Given env, an analysis environment, and sym, a symbol, resolve a macro." + [env sym] (let [ns (-> env :ns :name) namespaces (get @env/*compiler* ::namespaces)] (cond @@ -619,7 +634,10 @@ ('#{cljs.core/PersistentHashMap cljs.core/List} t))))) -(defn infer-tag [env e] +(defn infer-tag + "Given env, an analysis environment, and e, an AST node, return the inferred + type of the node" + [env e] (if-let [tag (get-tag e)] tag (case (:op e) @@ -1249,6 +1267,9 @@ false))) (defn analyze-deps + "Given a lib, a namespace, deps, its dependencies, env, an analysis environment + and opts, compiler options - analyze all of the dependencies. Required to + correctly analyze usage of other namespaces." ([lib deps env] (analyze-deps lib deps env nil)) ([lib deps env opts] (let [compiler @env/*compiler*] @@ -1793,7 +1814,10 @@ (resolve-var env sym))) (assoc ret :op :var :info (resolve-var env sym))))))) -(defn get-expander [sym env] +(defn get-expander + "Given a sym, a symbol identifying a macro, and env, an analysis environment + return the corresponding Clojure macroexpander." + [sym env] (let [mvar (when-not (or (-> env :locals sym) ;locals hide macros (and (or (-> env :ns :excludes sym) @@ -1814,7 +1838,10 @@ (when (and mvar (.isMacro ^clojure.lang.Var mvar)) (with-meta @mvar (meta mvar))))) -(defn macroexpand-1 [env form] +(defn macroexpand-1 + "Given a env, an analysis environment, and form, a ClojureScript form, + macroexpand the form once." + [env form] (env/ensure (wrapping-errors env (let [op (first form)] @@ -2121,6 +2148,9 @@ (io/file (str target-file ".cache.edn")))))) (defn requires-analysis? + "Given a src, a resource, and output-dir, a compilation output directory + return true or false depending on whether src needs to be (re-)analyzed. + Can optionally pass cache, the analysis cache file." ([src] (requires-analysis? src "out")) ([src output-dir] (let [cache (cache-file src output-dir)] From 41207811f3e72748ddf51c21ab3b5359c287960c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 1 May 2015 10:06:55 -0400 Subject: [PATCH 1011/4033] CLJS-1212: Error in set ctor for > 8-entry map literal Not all seq returning types return INext --- src/cljs/cljs/core.cljs | 10 +++++----- test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 2794acc98..85eec8c49 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -7898,7 +7898,7 @@ reduces them without incurring seq initialization" (defn set "Returns a set of the distinct elements of coll." [coll] - (let [^not-native in (seq coll)] + (let [in (seq coll)] (cond (nil? in) #{} @@ -7906,11 +7906,11 @@ reduces them without incurring seq initialization" (set-from-indexed-seq in) :else - (loop [in in - ^not-native out (-as-transient #{})] + (loop [^not-native in in + ^not-native out (-as-transient #{})] (if-not (nil? in) - (recur (-next in) (-conj! out (-first in))) - (-persistent! out)))))) + (recur (next in) (-conj! out (-first in))) + (persistent! out)))))) (defn hash-set "Returns a new hash set with supplied keys. Any equal keys are diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 4db6dcc7a..d846648af 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2779,6 +2779,10 @@ (is (= (.getBasis TypeBasis) '[a b])) (is (= (.getBasis RecordBasis) '[c d e]))) +(deftest test-1212 + (is (= (set {:a 0 :b 0 :c 0 :d 0 :e 0 :f 0 :g 0 :h 0 :i 0}) + #{[:a 0] [:b 0] [:c 0] [:d 0] [:e 0] [:f 0] [:g 0] [:h 0] [:i 0]}))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From b902438b86ea2de6e0defc3843c61429a5740d9b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 1 May 2015 15:38:08 -0400 Subject: [PATCH 1012/4033] CLJS-1214: :arglists meta has needless quoting CLJS-1232: bad arglists for doc, regression more robust handling of quoted arglists. in all cases check whether :arglists has already been quoted to prevent quoting multiple times. in the doc string case, don't blindly use `second` check that we have something quoted. --- src/clj/cljs/analyzer.clj | 16 +++++++++++----- src/clj/cljs/core.clj | 2 +- src/cljs/cljs/repl.cljs | 12 ++++++++---- test/cljs/cljs/core_test.cljs | 6 ++++++ 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index c607d7a16..e340827e3 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -695,14 +695,20 @@ :sym (analyze expr-env `(quote ~(symbol (name (:ns var)) (name (:name var))))) :meta (let [ks [:ns :doc :file :line :column] m (merge - (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) - :name `(quote ~(symbol (name (:name var)))) - :test `(when ~sym (.-cljs$lang$test ~sym)) - :arglists (map with-meta (:arglists var) (:arglists-meta var))) (let [user-meta (:meta var) uks (keys user-meta)] (zipmap uks - (map #(list 'quote (get user-meta %)) uks))))] + (map #(list 'quote (get user-meta %)) uks))) + (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) + :name `(quote ~(symbol (name (:name var)))) + :test `(when ~sym (.-cljs$lang$test ~sym)) + :arglists (let [arglists (:arglists var) + arglists' (if (= 'quote (first arglists)) + (second arglists) + arglists)] + (list 'quote + (doall (map with-meta arglists' + (:arglists-meta var)))))))] (analyze expr-env m))})) (defmethod parse 'if diff --git a/src/clj/cljs/core.clj b/src/clj/cljs/core.clj index d9a244581..149eaacc7 100644 --- a/src/clj/cljs/core.clj +++ b/src/clj/cljs/core.clj @@ -2107,7 +2107,7 @@ {:variadic true :max-fixed-arity c-1 :method-params [sig] - :arglists [arglist] + :arglists (core/list arglist) :arglists-meta (doall (map meta [arglist]))})] `(do (def ~(with-meta name meta) diff --git a/src/cljs/cljs/repl.cljs b/src/cljs/cljs/repl.cljs index 67fb1c58d..bddef4430 100644 --- a/src/cljs/cljs/repl.cljs +++ b/src/cljs/cljs/repl.cljs @@ -17,10 +17,14 @@ (cond (:forms m) (doseq [f (:forms m)] (println " " f)) - (:arglists m) (if (or (:macro m) - (:repl-special-function m)) - (prn (:arglists m)) - (prn (second (:arglists m))))) + (:arglists m) (let [arglists (:arglists m)] + (if (or (:macro m) + (:repl-special-function m)) + (prn arglists) + (prn + (if (= 'quote (first arglists)) + (second arglists) + arglists))))) (if (:special-form m) (do (println "Special Form") diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index d846648af..b5c4e1f4f 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2783,6 +2783,12 @@ (is (= (set {:a 0 :b 0 :c 0 :d 0 :e 0 :f 0 :g 0 :h 0 :i 0}) #{[:a 0] [:b 0] [:c 0] [:d 0] [:e 0] [:f 0] [:g 0] [:h 0] [:i 0]}))) +(deftest test-var-aarglists + (is (= (-> #'first meta :arglists) '([coll]))) + (is (= (-> #'hash-map meta :arglists) '([& keyvals]))) + (is (= (-> #'map meta :arglists) + '([f] [f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 221d966097b4545a9615fe51675723790c13b8f6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 3 May 2015 15:56:05 -0400 Subject: [PATCH 1013/4033] typo in arglists test --- test/cljs/cljs/core_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index b5c4e1f4f..b21571607 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2783,7 +2783,7 @@ (is (= (set {:a 0 :b 0 :c 0 :d 0 :e 0 :f 0 :g 0 :h 0 :i 0}) #{[:a 0] [:b 0] [:c 0] [:d 0] [:e 0] [:f 0] [:g 0] [:h 0] [:i 0]}))) -(deftest test-var-aarglists +(deftest test-var-arglists (is (= (-> #'first meta :arglists) '([coll]))) (is (= (-> #'hash-map meta :arglists) '([& keyvals]))) (is (= (-> #'map meta :arglists) From 795f08ea23eb740af04013162374e4d169654e27 Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Sun, 3 May 2015 11:57:17 -0700 Subject: [PATCH 1014/4033] CLJS-1240: Add cljs.core/var? --- src/cljs/cljs/core.cljs | 5 +++++ test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 85eec8c49..180b86db8 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -908,6 +908,11 @@ (-invoke [_ a b c d e f g h i j k l m n o p q r s t rest] (apply (val) a b c d e f g h i j k l m n o p q r s t rest))) +(defn var? + "Returns true if v is of type cljs.core.Var" + [v] + (instance? cljs.core.Var v)) + ;;;;;;;;;;;;;;;;;;; fundamentals ;;;;;;;;;;;;;;; (declare array-seq prim-seq IndexedSeq) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index b21571607..4331dd052 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2736,6 +2736,10 @@ :foo #'foo-1187 :bar #'bar-1187) [])) +(deftest test-var? + (is (var? #'inc)) + (is (not (var? 1)))) + (deftest test-cljs-1187 (testing "Internal var nodes analyzed in expression context" (is (= (with-out-str (print-foo-1187 :foo)) From 99f4463354dbd9d73073865a999b0225823c41ec Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Sun, 3 May 2015 13:03:30 -0700 Subject: [PATCH 1015/4033] CLJS-1243: Add TaggedLiteral type & related fns --- src/cljs/cljs/core.cljs | 38 +++++++++++++++++++++++++++++++++++ test/cljs/cljs/core_test.cljs | 11 ++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 180b86db8..8ab81d97e 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8956,6 +8956,7 @@ reduces them without incurring seq initialization" [proc coll] (reduce #(proc %2) nil coll)) + (defprotocol IEncodeJS (-clj->js [x] "Recursively transforms clj values to JavaScript") (-key->js [x] "Transforms map keys to valid JavaScript keys. Arbitrary keys are @@ -9610,3 +9611,40 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (if f (do (f) :ok) :no-test))) + + +(deftype TaggedLiteral [tag form] + + IEquiv + (-equiv [this other] + (and (instance? TaggedLiteral other) + (= tag (.-tag other)) + (= form (.-form other)))) + + IHash + (-hash [this] + (+ (* 31 (hash tag)) + (hash form))) + + ILookup + (-lookup [this v] + (-lookup this v nil)) + (-lookup [this v not-found] + (case v + :tag tag + :form form + not-found)) + + ) + +(defn tagged-literal? + "Return true if the value is the data representation of a tagged literal" + [value] + (instance? cljs.core.TaggedLiteral value)) + +(defn tagged-literal + "Construct a data representation of a tagged literal from a + tag symbol and a form." + [tag form] + {:pre (symbol? tag)} + (cljs.core.TaggedLiteral. tag form)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 4331dd052..72c34ad57 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2793,6 +2793,17 @@ (is (= (-> #'map meta :arglists) '([f] [f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])))) +(deftest tagged-literals + (let [tl (tagged-literal 'x "y")] + (is (tagged-literal? tl)) + (is (not (tagged-literal? {:tag 'x :form "y"}))) + (is (= (:tag tl) 'x)) + (is (= (:form tl) "y")) + (is (= tl (tagged-literal 'x "y"))) + (is (not= tl (tagged-literal 'z "y"))) + (is (not= tl (tagged-literal 'x "z"))) + (is (= (hash tl) (hash (tagged-literal 'x "y")))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 5f1bf1e777ccca0110ead9df7bff257b1bbcee33 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 3 May 2015 17:29:18 -0400 Subject: [PATCH 1016/4033] CLJS-1244: tagged-literal precondition check missing wrapping vector --- src/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 8ab81d97e..260e52037 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -9646,5 +9646,5 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." "Construct a data representation of a tagged literal from a tag symbol and a form." [tag form] - {:pre (symbol? tag)} + {:pre [(symbol? tag)]} (cljs.core.TaggedLiteral. tag form)) From 703561bff0f06636e1f604c74494c2b5b502f420 Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Sun, 3 May 2015 10:37:07 -0700 Subject: [PATCH 1017/4033] CLJS-1239: Make eduction variadic. --- src/cljs/cljs/core.cljs | 12 +++++++----- test/cljs/cljs/core_test.cljs | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 260e52037..4a224beef 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -8944,11 +8944,13 @@ reduces them without incurring seq initialization" (es6-iterable Eduction) (defn eduction - "Returns a reducible/iterable/seqable application of - the transducer to the items in coll. Note that these applications - will be performed every time iterator/seq/reduce is called." - [xform coll] - (Eduction. xform coll)) + "Returns a reducible/iterable application of the transducers + to the items in coll. Transducers are applied in order as if + combined with comp. Note that these applications will be + performed every time reduce/iterator is called." + {:arglists '([xform* coll])} + [& xforms] + (Eduction. (apply comp (butlast xforms)) (last xforms))) (defn run! "Runs the supplied procedure (via reduce), for purposes of side diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 72c34ad57..5a8a6f04a 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1369,6 +1369,7 @@ (is (= (sequence (mapcat reverse) [[3 2 1 0] [6 5 4] [9 8 7]]) (range 10))) (is (= (seq (eduction (map inc) [1 2 3])) '(2 3 4))) + (is (= (seq (eduction (map inc) (map inc) [1 2 3])) '(3 4 5))) (is (= (sequence (partition-by #{:split}) [1 2 3 :split 4 5 6]) '([1 2 3] [:split] [4 5 6]))) (is (= (sequence (partition-all 3) '(1 2 3 4 5)) From cda08f01413c0f150635707a748d3ba3b536c9f9 Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Sun, 3 May 2015 17:32:31 -0700 Subject: [PATCH 1018/4033] CLJS-1246: Add cljs.core/record? predicate. --- src/cljs/cljs/core.cljs | 5 +++++ test/cljs/cljs/core_test.cljs | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs index 4a224beef..44fa22cf4 100644 --- a/src/cljs/cljs/core.cljs +++ b/src/cljs/cljs/core.cljs @@ -1783,6 +1783,11 @@ reduces them without incurring seq initialization" false (satisfies? IMap x))) +(defn ^boolean record? + "Return true if x satisfies IRecord" + [x] + (satisfies? IRecord x)) + (defn ^boolean vector? "Return true if x satisfies IVector" [x] (satisfies? IVector x)) diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 5a8a6f04a..528fb58d6 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -1735,6 +1735,8 @@ letters (C. "a" "b" "c") more-letters (assoc letters :d "d" :e "e" :f "f")] (testing "Testing records" + (is (record? fred)) + (is (not (record? {}))) (is (= (:firstname fred) "Fred")) (is (= fred fred-too)) (is (false? (= fred nil))) From 11113cea2f599d685b60f8fbdcb56d944240ea25 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 4 May 2015 10:04:12 -0400 Subject: [PATCH 1019/4033] .clj -> .cljc for important analysis / compilation bits --- src/clj/cljs/{analyzer.clj => analyzer.cljc} | 0 src/clj/cljs/{compiler.clj => compiler.cljc} | 0 src/clj/cljs/{env.clj => env.cljc} | 0 src/clj/cljs/{repl.clj => repl.cljc} | 0 src/clj/cljs/{util.clj => util.cljc} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/clj/cljs/{analyzer.clj => analyzer.cljc} (100%) rename src/clj/cljs/{compiler.clj => compiler.cljc} (100%) rename src/clj/cljs/{env.clj => env.cljc} (100%) rename src/clj/cljs/{repl.clj => repl.cljc} (100%) rename src/clj/cljs/{util.clj => util.cljc} (100%) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.cljc similarity index 100% rename from src/clj/cljs/analyzer.clj rename to src/clj/cljs/analyzer.cljc diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.cljc similarity index 100% rename from src/clj/cljs/compiler.clj rename to src/clj/cljs/compiler.cljc diff --git a/src/clj/cljs/env.clj b/src/clj/cljs/env.cljc similarity index 100% rename from src/clj/cljs/env.clj rename to src/clj/cljs/env.cljc diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.cljc similarity index 100% rename from src/clj/cljs/repl.clj rename to src/clj/cljs/repl.cljc diff --git a/src/clj/cljs/util.clj b/src/clj/cljs/util.cljc similarity index 100% rename from src/clj/cljs/util.clj rename to src/clj/cljs/util.cljc From c9bf3f4aadf79d5042a27a6ef0e4cd5db4c26604 Mon Sep 17 00:00:00 2001 From: lgrapenthin Date: Sun, 19 Apr 2015 15:26:48 +0200 Subject: [PATCH 1020/4033] fix cljs-1198 --- src/cljs/cljs/test.cljs | 69 +++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/src/cljs/cljs/test.cljs b/src/cljs/cljs/test.cljs index 75aaf4c34..c5b6c38e7 100644 --- a/src/cljs/cljs/test.cljs +++ b/src/cljs/cljs/test.cljs @@ -433,27 +433,33 @@ ;; ============================================================================= ;; Low-level functions +(defn- test-var-block* + [v t] + {:pre [(instance? Var v)]} + [(fn [] + (update-current-env! [:testing-vars] conj v) + (update-current-env! [:report-counters :test] inc) + (do-report {:type :begin-test-var :var v}) + (try + (t) + (catch :default e + (case e + ::async-disabled (throw "Async tests require fixtures to be specified as maps. Testing aborted.") + (do-report + {:type :error + :message "Uncaught exception, not in assertion." + :expected nil + :actual e}))))) + (fn [] + (do-report {:type :end-test-var :var v}) + (update-current-env! [:testing-vars] rest))]) + (defn test-var-block "Like test-var, but returns a block for further composition and later execution." [v] - {:pre [(instance? Var v)]} (if-let [t (:test (meta v))] - [(fn [] - (update-current-env! [:testing-vars] conj v) - (update-current-env! [:report-counters :test] inc) - (do-report {:type :begin-test-var :var v}) - (try - (t) - (catch :default e - (do-report - {:type :error - :message "Uncaught exception, not in assertion." - :expected nil - :actual e})))) - (fn [] - (do-report {:type :end-test-var :var v}) - (update-current-env! [:testing-vars] rest))])) + (test-var-block* v t))) (defn test-var "If v has a function in its :test metadata, calls that function, @@ -506,7 +512,14 @@ "Fixtures may not be of mixed types") (assert (> 2 (count types)) "fixtures specified in :once and :each must be of the same type") - ({:map :async :fn :sync} type :sync)))) + ({:map :async :fn :sync} type :async)))) + +(defn- disable-async [f] + (fn [] + (let [obj (f)] + (when (async? obj) + (throw ::async-disabled)) + obj))) (defn test-vars-block "Like test-vars, but returns a block for further composition and @@ -527,16 +540,18 @@ test-var-block)) (wrap-map-fixtures once-fixtures)) :sync - (do - (let [each-fixture-fn (join-fixtures each-fixtures)] - [(fn [] - ((join-fixtures once-fixtures) - (fn [] - (doseq [v vars] - (when (:test (meta v)) - (each-fixture-fn - (fn [] - (test-var v))))))))]))))))) + (let [each-fixture-fn (join-fixtures each-fixtures)] + [(fn [] + ((join-fixtures once-fixtures) + (fn [] + (doseq [v vars] + (when-let [t (:test (meta v))] + ;; (alter-meta! v update :test disable-async) + (each-fixture-fn + (fn [] + ;; (test-var v) + (run-block + (test-var-block* v (disable-async t))))))))))])))))) (group-by (comp :ns meta) vars))) (defn test-vars From 5612bf68d9ff029f699d09f6a530e7aeb48b69e5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 May 2015 08:49:15 -0400 Subject: [PATCH 1021/4033] add CLJS-1225 problem case in comment to compiler tests --- test/clj/cljs/compiler_tests.clj | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/clj/cljs/compiler_tests.clj b/test/clj/cljs/compiler_tests.clj index 9e9f2fb6b..448079b8a 100644 --- a/test/clj/cljs/compiler_tests.clj +++ b/test/clj/cljs/compiler_tests.clj @@ -58,10 +58,16 @@ (comp/emit (ana/analyze aenv '(defn foo ([a]) ([a b]))))) + ) - (env/with-compiler-env cenv - (comp/munge - (comp/lazy-load? +;; CLJS-1225 + +(comment + (binding [ana/*cljs-static-fns* true] + (env/with-compiler-env cenv + (comp/emit (ana/analyze aenv - '(defn foo ([a]) ([a b])))))) + '(defn incme [] + (let [incme (fn [a queue & args])] + (println (incme 1 [1] 1 1)))))))) ) \ No newline at end of file From 717cab5ed3f9879cddab7e560919522e7698e873 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 May 2015 10:00:38 -0400 Subject: [PATCH 1022/4033] CLJS-1225: Variadic function with same name as parent function gives runtime error in advanced compile mode. For optimized invokes where we rename the fn disable shadowing as shadowing has *already* been applied. Add test. --- src/clj/cljs/compiler.cljc | 8 ++++++-- test/cljs/cljs/core_test.cljs | 9 ++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/clj/cljs/compiler.cljc b/src/clj/cljs/compiler.cljc index 9fc1d7a82..525bd3ee8 100644 --- a/src/clj/cljs/compiler.cljc +++ b/src/clj/cljs/compiler.cljc @@ -785,7 +785,9 @@ (-> info (assoc :name (symbol (str (munge info) ".cljs$core$IFn$_invoke$arity$variadic"))) ;; bypass local fn-self-name munging, we're emitting direct - (update-in [:info] dissoc :fn-self-name)))) + ;; shadowing already applied + (update-in [:info] + #(-> % (dissoc :shadow) (dissoc :fn-self-name)))))) {:max-fixed-arity mfa}] ;; direct dispatch to specific arity case @@ -797,7 +799,9 @@ (-> info (assoc :name (symbol (str (munge info) ".cljs$core$IFn$_invoke$arity$" arity))) ;; bypass local fn-self-name munging, we're emitting direct - (update-in [:info] dissoc :fn-self-name)))) nil] + ;; shadowing already applied + (update-in [:info] + #(-> % (dissoc :shadow) (dissoc :fn-self-name)))))) nil] [f nil])))) [f nil])] (emit-wrap env diff --git a/test/cljs/cljs/core_test.cljs b/test/cljs/cljs/core_test.cljs index 528fb58d6..8892e7534 100644 --- a/test/cljs/cljs/core_test.cljs +++ b/test/cljs/cljs/core_test.cljs @@ -2786,7 +2786,7 @@ (is (= (.getBasis TypeBasis) '[a b])) (is (= (.getBasis RecordBasis) '[c d e]))) -(deftest test-1212 +(deftest test-cljs-1212 (is (= (set {:a 0 :b 0 :c 0 :d 0 :e 0 :f 0 :g 0 :h 0 :i 0}) #{[:a 0] [:b 0] [:c 0] [:d 0] [:e 0] [:f 0] [:g 0] [:h 0] [:i 0]}))) @@ -2807,6 +2807,13 @@ (is (not= tl (tagged-literal 'x "z"))) (is (= (hash tl) (hash (tagged-literal 'x "y")))))) +(defn- incme [] + (let [incme (fn [a queue & args] (inc a))] + (incme 1 [1] :color "#fff"))) + +(deftest test-cljs-1225 + (is (= (incme) 2))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 7610990e563654d520d2820e46dd852ae9440a5f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 May 2015 11:05:47 -0400 Subject: [PATCH 1023/4033] link to IRC channel --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ebaaf828b..fd1bd70d0 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,8 @@ Latest stable release: 0.0-3211 Please point all of your questions and feedback to the [Clojure mailing list](http://groups.google.com/group/clojure). There is also a community run -[ClojureScript user mailing list](http://groups.google.com/group/clojurescript). The +[ClojureScript user mailing list](http://groups.google.com/group/clojurescript) and +the [IRC channel](irc://irc.freenode.net/#clojurescript) is quite active. The Jira bug/feature tracking application is located at . Before submitting issues please read the From 6ab03fbb579934cb48e575464afbe322adbde3ba Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 May 2015 11:09:47 -0400 Subject: [PATCH 1024/4033] irc links not supported, be more explicit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd1bd70d0..541b7861b 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Please point all of your questions and feedback to the [Clojure mailing list](http://groups.google.com/group/clojure). There is also a community run [ClojureScript user mailing list](http://groups.google.com/group/clojurescript) and -the [IRC channel](irc://irc.freenode.net/#clojurescript) is quite active. The +the IRC channel, `#clojurescript` on [freenode.net](https://freenode.net/), is quite active. The Jira bug/feature tracking application is located at . Before submitting issues please read the From d0dedcb155d2c742e11d50ec79c54e5c8c4b8e73 Mon Sep 17 00:00:00 2001 From: Michael Boston Date: Wed, 6 May 2015 12:25:31 -0500 Subject: [PATCH 1025/4033] CLJS-710: port clojure.pprint --- src/cljs/cljs/pprint.clj | 155 +- src/cljs/cljs/pprint.cljs | 3316 ++++++++++++++++++++++++++++++- test/cljs/cljs/pprint_test.clj | 23 + test/cljs/cljs/pprint_test.cljs | 1071 ++++++++++ test/cljs/test_runner.cljs | 6 +- 5 files changed, 4502 insertions(+), 69 deletions(-) create mode 100644 test/cljs/cljs/pprint_test.clj create mode 100644 test/cljs/cljs/pprint_test.cljs diff --git a/src/cljs/cljs/pprint.clj b/src/cljs/cljs/pprint.clj index 6559466fb..ff1789ae4 100644 --- a/src/cljs/cljs/pprint.clj +++ b/src/cljs/cljs/pprint.clj @@ -1,19 +1,144 @@ -;;; pprint.clj -- Pretty printer and Common Lisp compatible format function (cl-format) for Clojure +(ns cljs.pprint + (:refer-clojure :exclude [deftype]) + (:require [clojure.walk :as walk])) -; 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. -;; Author: Tom Faulhaber -;; April 3, 2009 +;; required the following changes: +;; replace .ppflush with -ppflush to switch from Interface to Protocol -(ns cljs.pprint) +(defmacro with-pretty-writer [base-writer & body] + `(let [base-writer# ~base-writer + new-writer# (not (pretty-writer? base-writer#))] + (cljs.core/binding [~'*out* (if new-writer# + (make-pretty-writer base-writer# *print-right-margin* *print-miser-width*) + base-writer#)] + ~@body + (-ppflush ~'*out*)))) + + +(defmacro getf + "Get the value of the field a named by the argument (which should be a keyword)." + [sym] + `(~sym @@~'this)) + +;; change alter to swap! + +(defmacro setf + "Set the value of the field SYM to NEW-VAL" + [sym new-val] + `(swap! @~'this assoc ~sym ~new-val)) + +(defmacro deftype + [type-name & fields] + (let [name-str (name type-name) + fields (map (comp symbol name) fields)] + `(do + (defrecord ~type-name [~'type-tag ~@fields]) + (defn- ~(symbol (str "make-" name-str)) + ~(vec fields) + (~(symbol (str type-name ".")) ~(keyword name-str) ~@fields)) + (defn- ~(symbol (str name-str "?")) [x#] (= (:type-tag x#) ~(keyword name-str)))))) + +(defn- parse-lb-options [opts body] + (loop [body body + acc []] + (if (opts (first body)) + (recur (drop 2 body) (concat acc (take 2 body))) + [(apply hash-map acc) body]))) + +(defmacro pprint-logical-block + "Execute the body as a pretty printing logical block with output to *out* which + must be a pretty printing writer. When used from pprint or cl-format, this can be + assumed. + + This function is intended for use when writing custom dispatch functions. + + Before the body, the caller can optionally specify options: :prefix, :per-line-prefix + and :suffix." + [& args] + (let [[options body] (parse-lb-options #{:prefix :per-line-prefix :suffix} args)] + `(do (if (cljs.pprint/level-exceeded) + (~'-write cljs.pprint/*out* "#") + (do + (cljs.core/binding [cljs.pprint/*current-level* (inc cljs.pprint/*current-level*) + cljs.pprint/*current-length* 0] + (cljs.pprint/start-block cljs.pprint/*out* + ~(:prefix options) + ~(:per-line-prefix options) + ~(:suffix options)) + ~@body + (cljs.pprint/end-block cljs.pprint/*out*)))) + nil))) + +(defn- pll-mod-body [var-sym body] + (letfn [(inner [form] + (if (seq? form) + (let [form (macroexpand form)] + (condp = (first form) + 'loop* form + 'recur (concat `(recur (inc ~var-sym)) (rest form)) + (walk/walk inner identity form))) + form))] + (walk/walk inner identity body))) + +(defmacro print-length-loop + "A version of loop that iterates at most *print-length* times. This is designed + for use in pretty-printer dispatch functions." + [bindings & body] + (let [count-var (gensym "length-count") + mod-body (pll-mod-body count-var body)] + `(loop ~(apply vector count-var 0 bindings) + (if (or (not cljs.core/*print-length*) (< ~count-var cljs.core/*print-length*)) + (do ~@mod-body) + (~'-write cljs.pprint/*out* "..."))))) + +(defn- process-directive-table-element [[char params flags bracket-info & generator-fn]] + [char, + {:directive char, + :params `(array-map ~@params), + :flags flags, + :bracket-info bracket-info, + :generator-fn (concat '(fn [params offset]) generator-fn)}]) + +(defmacro ^{:private true} + defdirectives + [& directives] + `(def ^{:private true} + ~'directive-table (hash-map ~@(mapcat process-directive-table-element directives)))) + +(defmacro formatter + "Makes a function which can directly run format-in. The function is +fn [stream & args] ... and returns nil unless the stream is nil (meaning +output to a string) in which case it returns the resulting string. + +format-in can be either a control string or a previously compiled format." + [format-in] + `(let [format-in# ~format-in + my-c-c# cljs.pprint/cached-compile + my-e-f# cljs.pprint/execute-format + my-i-n# cljs.pprint/init-navigator + cf# (if (string? format-in#) (my-c-c# format-in#) format-in#)] + (fn [stream# & args#] + (let [navigator# (my-i-n# args#)] + (my-e-f# stream# cf# navigator#))))) + +(defmacro formatter-out + "Makes a function which can directly run format-in. The function is +fn [& args] ... and returns nil. This version of the formatter macro is +designed to be used with *out* set to an appropriate Writer. In particular, +this is meant to be used as part of a pretty printer dispatch method. + +format-in can be either a control string or a previously compiled format." + [format-in] + `(let [format-in# ~format-in + cf# (if (string? format-in#) (cljs.pprint/cached-compile format-in#) format-in#)] + (fn [& args#] + (let [navigator# (cljs.pprint/init-navigator args#)] + (cljs.pprint/execute-format cf# navigator#))))) + +(defmacro with-pprint-dispatch + "Execute body with the pretty print dispatch function bound to function." + [function & body] + `(cljs.core/binding [cljs.pprint/*print-pprint-dispatch* ~function] + ~@body)) -(defmacro ^{:private true} prlabel [prefix arg & more-args] - "Print args to *err* in name = value format" - `(prerr ~@(cons (list 'quote prefix) (mapcat #(list (list 'quote %) "=" %) - (cons arg (seq more-args)))))) diff --git a/src/cljs/cljs/pprint.cljs b/src/cljs/cljs/pprint.cljs index 76367df32..3209ff2ac 100644 --- a/src/cljs/cljs/pprint.cljs +++ b/src/cljs/cljs/pprint.cljs @@ -1,5 +1,3 @@ -;;; pprint.cljs -- Pretty printer and Common Lisp compatible format function (cl-format) for Clojure - ; Copyright (c) Rich Hickey. All rights reserved. ; The use and distribution terms for this software are covered by the ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) @@ -8,39 +6,71 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software. -;; Author: Tom Faulhaber -;; April 3, 2009 +(ns cljs.pprint + (:refer-clojure :exclude [deftype print println pr prn]) + (:require-macros + [cljs.pprint :as m :refer [with-pretty-writer getf setf deftype + pprint-logical-block print-length-loop + defdirectives formatter-out]]) + (:require + [cljs.core :refer [IWriter IDeref]] + [clojure.string :as string]) + (:import [goog.string StringBuffer])) -(ns - ^{:author "Tom Faulhaber", - :doc "A Pretty Printer for ClojureScript +(def ^:dynamic *out* nil) -cljs.pprint implements a flexible system for printing structured data -in a pleasing, easy-to-understand format. Basic use of the pretty printer is -simple, just call pprint instead of println. More advanced users can use -the building blocks provided to create custom output formats. +;;====================================================================== +;; override print fns to use *out* +;;====================================================================== -Out of the box, pprint supports a simple structured format for basic data -and a specialized format for Clojure source code. More advanced formats, -including formats that don't look like Clojure data at all like XML and -JSON, can be rendered by creating custom dispatch functions. +(defn- print [& more] + (-write *out* (apply print-str more))) -In addition to the pprint function, this module contains cl-format, a text -formatting function which is fully compatible with the format function in -Common Lisp. Because pretty printing directives are directly integrated with -cl-format, it supports very concise custom dispatch. It also provides -a more powerful alternative to Clojure's standard format function. +(defn- println [& more] + (apply print more) + (-write *out* \newline)) -See documentation for pprint and cl-format for more information or -complete documentation on the the clojure web site on github."} - cljs.pprint - (:require [clojure.walk :refer [walk]])) +(defn- print-char [c] + (-write *out* (condp = c + \backspace "\\backspace" + \tab "\\tab" + \newline "\\newline" + \formfeed "\\formfeed" + \return "\\return" + \" "\\\"" + \\ "\\\\" + (str "\\" c)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Helper functions for digesting formats in the various -;;; phases of their lives. -;;; These functions are actually pretty general. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defn- ^:dynamic pr [& more] + (-write *out* (apply pr-str more))) + +(defn- prn [& more] + (apply pr more) + (-write *out* \newline)) + +;;====================================================================== +;; cljs specific utils +;;====================================================================== + +(defn ^boolean float? + "Returns true if n is an float." + [n] + (and (number? n) + (not ^boolean (js/isNaN n)) + (not (identical? n js/Infinity)) + (not (== (js/parseFloat n) (js/parseInt n 10))))) + +(defn char-code + "Convert char to int" + [c] + (cond + (number? c) c + (and (string? c) (== (.-length c) 1)) (.charCodeAt c 0) + :else (throw (js/Error. "Argument to char must be a character or number")))) + +;;====================================================================== +;; Utilities +;;====================================================================== (defn- map-passing-context [func initial-context lis] (loop [context initial-context @@ -48,10 +78,10 @@ complete documentation on the the clojure web site on github."} acc []] (if (empty? lis) [acc context] - (let [this (first lis) - remainder (next lis) - [result new-context] (apply func [this context])] - (recur new-context remainder (conj acc result)))))) + (let [this (first lis) + remainder (next lis) + [result new-context] (apply func [this context])] + (recur new-context remainder (conj acc result)))))) (defn- consume [func initial-context] (loop [context initial-context @@ -59,7 +89,7 @@ complete documentation on the the clojure web site on github."} (let [[result new-context] (apply func [context])] (if (not result) [acc new-context] - (recur new-context (conj acc result)))))) + (recur new-context (conj acc result)))))) (defn- consume-while [func initial-context] (loop [context initial-context @@ -67,11 +97,11 @@ complete documentation on the the clojure web site on github."} (let [[result continue new-context] (apply func [context])] (if (not continue) [acc context] - (recur new-context (conj acc result)))))) + (recur new-context (conj acc result)))))) (defn- unzip-map [m] - "Take a map that has pairs in the value slots and produce a pair of maps, - the first having all the first elements of the pairs and the second all + "Take a map that has pairs in the value slots and produce a pair of maps, + the first having all the first elements of the pairs and the second all the second elements of the pairs" [(into {} (for [[k [v1 v2]] m] [k v1])) (into {} (for [[k [v1 v2]] m] [k v2]))]) @@ -85,10 +115,10 @@ complete documentation on the the clojure web site on github."} (let [len (count s)] (if (and (pos? len) (= (nth s (dec (count s))) c)) (loop [n (dec len)] - (cond - (neg? n) "" - (not (= (nth s n) c)) (subs s 0 (inc n)) - true (recur (dec n)))) + (cond + (neg? n) "" + (not (= (nth s n) c)) (subs s 0 (inc n)) + true (recur (dec n)))) s))) (defn- ltrim [s c] @@ -102,19 +132,3201 @@ complete documentation on the the clojure web site on github."} s))) (defn- prefix-count [aseq val] - "Return the number of times that val occurs at the start of sequence aseq, + "Return the number of times that val occurs at the start of sequence aseq, if val is a seq itself, count the number of times any element of val occurs at the beginning of aseq" (let [test (if (coll? val) (set val) #{val})] (loop [pos 0] - (if (or (= pos (count aseq)) (not (test (nth aseq pos)))) - pos - (recur (inc pos)))))) - -(defn- prerr [& args] - "Println to *err*" - ;; there is no *err* yet - (apply println args)) - + (if (or (= pos (count aseq)) (not (test (nth aseq pos)))) + pos + (recur (inc pos)))))) + ;; Flush the pretty-print buffer without flushing the underlying stream -(defprotocol PrettyFlush (ppflush [this])) +(defprotocol IPrettyFlush + (-ppflush [pp])) + +;;====================================================================== +;; column_writer.clj +;;====================================================================== + +(def ^:dynamic ^{:private true} *default-page-width* 72) + +(defn- get-field [this sym] + (sym @@this)) + +(defn- set-field [this sym new-val] + (swap! @this assoc sym new-val)) + +(defn- get-column [this] + (get-field this :cur)) + +(defn- get-line [this] + (get-field this :line)) + +(defn- get-max-column [this] + (get-field this :max)) + +(defn- set-max-column [this new-max] + (set-field this :max new-max) + nil) + +(defn- get-writer [this] + (get-field this :base)) + +;; Why is the c argument an integer? +(defn- c-write-char [this c] + (if (= c \newline) + (do + (set-field this :cur 0) + (set-field this :line (inc (get-field this :line)))) + (set-field this :cur (inc (get-field this :cur)))) + (-write (get-field this :base) c)) + +(defn- column-writer + ([writer] (column-writer writer *default-page-width*)) + ([writer max-columns] + (let [fields (atom {:max max-columns, :cur 0, :line 0 :base writer})] + (reify + + IDeref + (-deref [_] fields) + + IWriter + (-flush [_] + (-flush writer)) + (-write + ;;-write isn't multi-arity, so need different way to do this + #_([this ^chars cbuf ^Number off ^Number len] + (let [writer (get-field this :base)] + (-write writer cbuf off len))) + [this x] + (condp = (type x) + js/String + (let [s x + nl (.lastIndexOf s \newline)] + (if (neg? nl) + (set-field this :cur (+ (get-field this :cur) (count s))) + (do + (set-field this :cur (- (count s) nl 1)) + (set-field this :line (+ (get-field this :line) + (count (filter #(= % \newline) s)))))) + (-write (get-field this :base) s)) + js/Number + (c-write-char this x))))))) + +;;====================================================================== +;; pretty_writer.clj +;;====================================================================== + +;;====================================================================== +;; Forward declarations +;;====================================================================== + +(declare get-miser-width) + +;;====================================================================== +;; The data structures used by pretty-writer +;;====================================================================== + +(defrecord ^{:private true} logical-block + [parent section start-col indent + done-nl intra-block-nl + prefix per-line-prefix suffix + logical-block-callback]) + +(defn- ancestor? [parent child] + (loop [child (:parent child)] + (cond + (nil? child) false + (identical? parent child) true + :else (recur (:parent child))))) + +(defn- buffer-length [l] + (let [l (seq l)] + (if l + (- (:end-pos (last l)) (:start-pos (first l))) + 0))) + +;; A blob of characters (aka a string) +(deftype buffer-blob :data :trailing-white-space :start-pos :end-pos) + +;; A newline +(deftype nl-t :type :logical-block :start-pos :end-pos) + +(deftype start-block-t :logical-block :start-pos :end-pos) + +(deftype end-block-t :logical-block :start-pos :end-pos) + +(deftype indent-t :logical-block :relative-to :offset :start-pos :end-pos) + +(def ^:private pp-newline (fn [] "\n")) + +(declare emit-nl) + +(defmulti ^{:private true} write-token #(:type-tag %2)) + +(defmethod write-token :start-block-t [this token] + (when-let [cb (getf :logical-block-callback)] (cb :start)) + (let [lb (:logical-block token)] + (when-let [prefix (:prefix lb)] + (-write (getf :base) prefix)) + (let [col (get-column (getf :base))] + (reset! (:start-col lb) col) + (reset! (:indent lb) col)))) + +(defmethod write-token :end-block-t [this token] + (when-let [cb (getf :logical-block-callback)] (cb :end)) + (when-let [suffix (:suffix (:logical-block token))] + (-write (getf :base) suffix))) + +(defmethod write-token :indent-t [this token] + (let [lb (:logical-block token)] + (reset! (:indent lb) + (+ (:offset token) + (condp = (:relative-to token) + :block @(:start-col lb) + :current (get-column (getf :base))))))) + +(defmethod write-token :buffer-blob [this token] + (-write (getf :base) (:data token))) + +(defmethod write-token :nl-t [this token] + (if (or (= (:type token) :mandatory) + (and (not (= (:type token) :fill)) + @(:done-nl (:logical-block token)))) + (emit-nl this token) + (if-let [tws (getf :trailing-white-space)] + (-write (getf :base) tws))) + (setf :trailing-white-space nil)) + +(defn- write-tokens [this tokens force-trailing-whitespace] + (doseq [token tokens] + (if-not (= (:type-tag token) :nl-t) + (if-let [tws (getf :trailing-white-space)] + (-write (getf :base) tws))) + (write-token this token) + (setf :trailing-white-space (:trailing-white-space token)) + (let [tws (getf :trailing-white-space)] + (when (and force-trailing-whitespace tws) + (-write (getf :base) tws) + (setf :trailing-white-space nil))))) + +;;====================================================================== +;; emit-nl? method defs for each type of new line. This makes +;; the decision about whether to print this type of new line. +;;====================================================================== + +(defn- tokens-fit? [this tokens] + (let [maxcol (get-max-column (getf :base))] + (or + (nil? maxcol) + (< (+ (get-column (getf :base)) (buffer-length tokens)) maxcol)))) + +(defn- linear-nl? [this lb section] + (or @(:done-nl lb) + (not (tokens-fit? this section)))) + +(defn- miser-nl? [this lb section] + (let [miser-width (get-miser-width this) + maxcol (get-max-column (getf :base))] + (and miser-width maxcol + (>= @(:start-col lb) (- maxcol miser-width)) + (linear-nl? this lb section)))) + +(defmulti ^{:private true} emit-nl? (fn [t _ _ _] (:type t))) + +(defmethod emit-nl? :linear [newl this section _] + (let [lb (:logical-block newl)] + (linear-nl? this lb section))) + +(defmethod emit-nl? :miser [newl this section _] + (let [lb (:logical-block newl)] + (miser-nl? this lb section))) + +(defmethod emit-nl? :fill [newl this section subsection] + (let [lb (:logical-block newl)] + (or @(:intra-block-nl lb) + (not (tokens-fit? this subsection)) + (miser-nl? this lb section)))) + +(defmethod emit-nl? :mandatory [_ _ _ _] + true) + +;;====================================================================== +;; Various support functions +;;====================================================================== + +(defn- get-section [buffer] + (let [nl (first buffer) + lb (:logical-block nl) + section (seq (take-while #(not (and (nl-t? %) (ancestor? (:logical-block %) lb))) + (next buffer)))] + [section (seq (drop (inc (count section)) buffer))])) + +(defn- get-sub-section [buffer] + (let [nl (first buffer) + lb (:logical-block nl) + section (seq (take-while #(let [nl-lb (:logical-block %)] + (not (and (nl-t? %) (or (= nl-lb lb) (ancestor? nl-lb lb))))) + (next buffer)))] + section)) + +(defn- update-nl-state [lb] + (reset! (:intra-block-nl lb) true) + (reset! (:done-nl lb) true) + (loop [lb (:parent lb)] + (if lb + (do (reset! (:done-nl lb) true) + (reset! (:intra-block-nl lb) true) + (recur (:parent lb)))))) + +(defn- emit-nl [this nl] + (-write (getf :base) (pp-newline)) + (setf :trailing-white-space nil) + (let [lb (:logical-block nl) + prefix (:per-line-prefix lb)] + (if prefix + (-write (getf :base) prefix)) + (let [istr (apply str (repeat (- @(:indent lb) (count prefix)) \space))] + (-write (getf :base) istr)) + (update-nl-state lb))) + +(defn- split-at-newline [tokens] + (let [pre (seq (take-while #(not (nl-t? %)) tokens))] + [pre (seq (drop (count pre) tokens))])) + +;; write-token-string is called when the set of tokens in the buffer +;; is long than the available space on the line +(defn- write-token-string [this tokens] + (let [[a b] (split-at-newline tokens)] + (if a (write-tokens this a false)) + (if b + (let [[section remainder] (get-section b) + newl (first b)] + (let [do-nl (emit-nl? newl this section (get-sub-section b)) + result (if do-nl + (do + (emit-nl this newl) + (next b)) + b) + long-section (not (tokens-fit? this result)) + result (if long-section + (let [rem2 (write-token-string this section)] + (if (= rem2 section) + (do ; If that didn't produce any output, it has no nls + ; so we'll force it + (write-tokens this section false) + remainder) + (into [] (concat rem2 remainder)))) + result)] + result))))) + +(defn- write-line [this] + (loop [buffer (getf :buffer)] + (setf :buffer (into [] buffer)) + (if (not (tokens-fit? this buffer)) + (let [new-buffer (write-token-string this buffer)] + (if-not (identical? buffer new-buffer) + (recur new-buffer)))))) + +;; Add a buffer token to the buffer and see if it's time to start +;; writing +(defn- add-to-buffer [this token] + (setf :buffer (conj (getf :buffer) token)) + (if (not (tokens-fit? this (getf :buffer))) + (write-line this))) + +;; Write all the tokens that have been buffered +(defn- write-buffered-output [this] + (write-line this) + (if-let [buf (getf :buffer)] + (do + (write-tokens this buf true) + (setf :buffer [])))) + +(defn- write-white-space [this] + (when-let [tws (getf :trailing-white-space)] + (-write (getf :base) tws) + (setf :trailing-white-space nil))) + +;;; If there are newlines in the string, print the lines up until the last newline, +;;; making the appropriate adjustments. Return the remainder of the string +(defn- write-initial-lines + [^Writer this ^String s] + (let [lines (string/split s "\n" -1)] + (if (= (count lines) 1) + s + (let [^String prefix (:per-line-prefix (first (getf :logical-blocks))) + ^String l (first lines)] + (if (= :buffering (getf :mode)) + (let [oldpos (getf :pos) + newpos (+ oldpos (count l))] + (setf :pos newpos) + (add-to-buffer this (make-buffer-blob l nil oldpos newpos)) + (write-buffered-output this)) + (do + (write-white-space this) + (-write (getf :base) l))) + (-write (getf :base) \newline) + (doseq [^String l (next (butlast lines))] + (-write (getf :base) l) + (-write (getf :base) (pp-newline)) + (if prefix + (-write (getf :base) prefix))) + (setf :buffering :writing) + (last lines))))) + +(defn- p-write-char [this c] + (if (= (getf :mode) :writing) + (do + (write-white-space this) + (-write (getf :base) c)) + (if (= c \newline) + (write-initial-lines this \newline) + (let [oldpos (getf :pos) + newpos (inc oldpos)] + (setf :pos newpos) + (add-to-buffer this (make-buffer-blob (char c) nil oldpos newpos)))))) + +;;====================================================================== +;; Initialize the pretty-writer instance +;;====================================================================== + +(defn- pretty-writer [writer max-columns miser-width] + (let [lb (logical-block. nil nil (atom 0) (atom 0) (atom false) (atom false) + nil nil nil nil) + ; NOTE: may want to just `specify!` #js { ... fields ... } with the protocols + fields (atom {:pretty-writer true + :base (column-writer writer max-columns) + :logical-blocks lb + :sections nil + :mode :writing + :buffer [] + :buffer-block lb + :buffer-level 1 + :miser-width miser-width + :trailing-white-space nil + :pos 0})] + (reify + + IDeref + (-deref [_] fields) + + IWriter + (-write [this x] + (condp = (type x) + js/String + (let [s0 (write-initial-lines this x) + s (string/replace-first s0 #"\s+$" "") + white-space (subs s0 (count s)) + mode (getf :mode)] + (if (= mode :writing) + (do + (write-white-space this) + (-write (getf :base) s) + (setf :trailing-white-space white-space)) + (let [oldpos (getf :pos) + newpos (+ oldpos (count s0))] + (setf :pos newpos) + (add-to-buffer this (make-buffer-blob s white-space oldpos newpos))))) + js/Number + (p-write-char this x))) + (-flush [this] + (-ppflush this) + (-flush (getf :base))) + + IPrettyFlush + (-ppflush [this] + (if (= (getf :mode) :buffering) + (do + (write-tokens this (getf :buffer) true) + (setf :buffer [])) + (write-white-space this))) + + ))) + +;;====================================================================== +;; Methods for pretty-writer +;;====================================================================== + +(defn- start-block + [this prefix per-line-prefix suffix] + (let [lb (logical-block. (getf :logical-blocks) nil (atom 0) (atom 0) + (atom false) (atom false) + prefix per-line-prefix suffix nil)] + (setf :logical-blocks lb) + (if (= (getf :mode) :writing) + (do + (write-white-space this) + (when-let [cb (getf :logical-block-callback)] (cb :start)) + (if prefix + (-write (getf :base) prefix)) + (let [col (get-column (getf :base))] + (reset! (:start-col lb) col) + (reset! (:indent lb) col))) + (let [oldpos (getf :pos) + newpos (+ oldpos (if prefix (count prefix) 0))] + (setf :pos newpos) + (add-to-buffer this (make-start-block-t lb oldpos newpos)))))) + +(defn- end-block [this] + (let [lb (getf :logical-blocks) + suffix (:suffix lb)] + (if (= (getf :mode) :writing) + (do + (write-white-space this) + (if suffix + (-write (getf :base) suffix)) + (when-let [cb (getf :logical-block-callback)] (cb :end))) + (let [oldpos (getf :pos) + newpos (+ oldpos (if suffix (count suffix) 0))] + (setf :pos newpos) + (add-to-buffer this (make-end-block-t lb oldpos newpos)))) + (setf :logical-blocks (:parent lb)))) + +(defn- nl [this type] + (setf :mode :buffering) + (let [pos (getf :pos)] + (add-to-buffer this (make-nl-t type (getf :logical-blocks) pos pos)))) + +(defn- indent [this relative-to offset] + (let [lb (getf :logical-blocks)] + (if (= (getf :mode) :writing) + (do + (write-white-space this) + (reset! (:indent lb) + (+ offset (condp = relative-to + :block @(:start-col lb) + :current (get-column (getf :base)))))) + (let [pos (getf :pos)] + (add-to-buffer this (make-indent-t lb relative-to offset pos pos)))))) + +(defn- get-miser-width [this] + (getf :miser-width)) + +;;====================================================================== +;; pprint_base.clj +;;====================================================================== + +;;====================================================================== +;; Variables that control the pretty printer +;;====================================================================== + +;; *print-length*, *print-level* and *print-dup* are defined in cljs.core +(def ^:dynamic + ^{:doc "Bind to true if you want write to use pretty printing"} + *print-pretty* true) + +(defonce ^:dynamic + ^{:doc "The pretty print dispatch function. Use with-pprint-dispatch or +set-pprint-dispatch to modify." + :added "1.2"} + *print-pprint-dispatch* nil) + +(def ^:dynamic + ^{:doc "Pretty printing will try to avoid anything going beyond this column. +Set it to nil to have pprint let the line be arbitrarily long. This will ignore all +non-mandatory newlines.", + :added "1.2"} + *print-right-margin* 72) + +(def ^:dynamic + ^{:doc "The column at which to enter miser style. Depending on the dispatch table, +miser style add newlines in more places to try to keep lines short allowing for further +levels of nesting.", + :added "1.2"} + *print-miser-width* 40) + +;;; TODO implement output limiting +(def ^:dynamic +^{:private true, + :doc "Maximum number of lines to print in a pretty print instance (N.B. This is not yet used)"} +*print-lines* nil) + +;;; TODO: implement circle and shared +(def ^:dynamic +^{:private true, + :doc "Mark circular structures (N.B. This is not yet used)"} +*print-circle* nil) + +;;; TODO: should we just use *print-dup* here? +(def ^:dynamic +^{:private true, + :doc "Mark repeated structures rather than repeat them (N.B. This is not yet used)"} +*print-shared* nil) + +(def ^:dynamic +^{:doc "Don't print namespaces with symbols. This is particularly useful when +pretty printing the results of macro expansions" + :added "1.2"} +*print-suppress-namespaces* nil) + +;;; TODO: support print-base and print-radix in cl-format +;;; TODO: support print-base and print-radix in rationals +(def ^:dynamic +^{:doc "Print a radix specifier in front of integers and rationals. If *print-base* is 2, 8, +or 16, then the radix specifier used is #b, #o, or #x, respectively. Otherwise the +radix specifier is in the form #XXr where XX is the decimal value of *print-base* " + :added "1.2"} +*print-radix* nil) + +(def ^:dynamic +^{:doc "The base to use for printing integers and rationals." + :added "1.2"} +*print-base* 10) + +;;====================================================================== +;; Internal variables that keep track of where we are in the +;; structure +;;====================================================================== + +(def ^:dynamic ^{:private true} *current-level* 0) + +(def ^:dynamic ^{:private true} *current-length* nil) + +;;====================================================================== +;; Support for the write function +;;====================================================================== + +(declare format-simple-number) + +(def ^{:private true} write-option-table + {;:array *print-array* + :base #'cljs.pprint/*print-base*, + ;;:case *print-case*, + :circle #'cljs.pprint/*print-circle*, + ;;:escape *print-escape*, + ;;:gensym *print-gensym*, + :length #'cljs.core/*print-length*, + :level #'cljs.core/*print-level*, + :lines #'cljs.pprint/*print-lines*, + :miser-width #'cljs.pprint/*print-miser-width*, + :dispatch #'cljs.pprint/*print-pprint-dispatch*, + :pretty #'cljs.pprint/*print-pretty*, + :radix #'cljs.pprint/*print-radix*, + :readably #'cljs.core/*print-readably*, + :right-margin #'cljs.pprint/*print-right-margin*, + :suppress-namespaces #'cljs.pprint/*print-suppress-namespaces*}) + +(defn- table-ize [t m] + (apply hash-map (mapcat + #(when-let [v (get t (key %))] [v (val %)]) + m))) + +(defn- pretty-writer? + "Return true iff x is a PrettyWriter" + [x] (and (satisfies? IDeref x) (:pretty-writer @@x))) + +(defn- make-pretty-writer + "Wrap base-writer in a PrettyWriter with the specified right-margin and miser-width" + [base-writer right-margin miser-width] + (pretty-writer base-writer right-margin miser-width)) + +(defn write-out + "Write an object to *out* subject to the current bindings of the printer control +variables. Use the kw-args argument to override individual variables for this call (and +any recursive calls). + +*out* must be a PrettyWriter if pretty printing is enabled. This is the responsibility +of the caller. + +This method is primarily intended for use by pretty print dispatch functions that +already know that the pretty printer will have set up their environment appropriately. +Normal library clients should use the standard \"write\" interface. " + [object] + (let [length-reached (and *current-length* + *print-length* + (>= *current-length* *print-length*))] + (if-not *print-pretty* + (pr object) + (if length-reached + (-write *out* "...") ;;TODO could this (incorrectly) print ... on the next line? + (do + (if *current-length* (set! *current-length* (inc *current-length*))) + (*print-pprint-dispatch* object)))) + length-reached)) + +(defn write + "Write an object subject to the current bindings of the printer control variables. +Use the kw-args argument to override individual variables for this call (and any +recursive calls). Returns the string result if :stream is nil or nil otherwise. + +The following keyword arguments can be passed with values: + Keyword Meaning Default value + :stream Writer for output or nil true (indicates *out*) + :base Base to use for writing rationals Current value of *print-base* + :circle* If true, mark circular structures Current value of *print-circle* + :length Maximum elements to show in sublists Current value of *print-length* + :level Maximum depth Current value of *print-level* + :lines* Maximum lines of output Current value of *print-lines* + :miser-width Width to enter miser mode Current value of *print-miser-width* + :dispatch The pretty print dispatch function Current value of *print-pprint-dispatch* + :pretty If true, do pretty printing Current value of *print-pretty* + :radix If true, prepend a radix specifier Current value of *print-radix* + :readably* If true, print readably Current value of *print-readably* + :right-margin The column for the right margin Current value of *print-right-margin* + :suppress-namespaces If true, no namespaces in symbols Current value of *print-suppress-namespaces* + + * = not yet supported +" + [object & kw-args] + (let [options (merge {:stream true} (apply hash-map kw-args))] + ;;TODO rewrite this as a macro + (binding [cljs.pprint/*print-base* (:base options cljs.pprint/*print-base*) + ;;:case *print-case*, + cljs.pprint/*print-circle* (:circle options cljs.pprint/*print-circle*) + ;;:escape *print-escape* + ;;:gensym *print-gensym* + cljs.core/*print-length* (:length options cljs.core/*print-length*) + cljs.core/*print-level* (:level options cljs.core/*print-level*) + cljs.pprint/*print-lines* (:lines options cljs.pprint/*print-lines*) + cljs.pprint/*print-miser-width* (:miser-width options cljs.pprint/*print-miser-width*) + cljs.pprint/*print-pprint-dispatch* (:dispatch options cljs.pprint/*print-pprint-dispatch*) + cljs.pprint/*print-pretty* (:pretty options cljs.pprint/*print-pretty*) + cljs.pprint/*print-radix* (:radix options cljs.pprint/*print-radix*) + cljs.core/*print-readably* (:readably options cljs.core/*print-readably*) + cljs.pprint/*print-right-margin* (:right-margin options cljs.pprint/*print-right-margin*) + cljs.pprint/*print-suppress-namespaces* (:suppress-namespaces options cljs.pprint/*print-suppress-namespaces*)] + ;;TODO enable printing base + #_[bindings (if (or (not (= *print-base* 10)) *print-radix*) + {#'pr pr-with-base} + {})] + (binding [] + (let [sb (StringBuffer.) + optval (if (contains? options :stream) + (:stream options) + true) + base-writer (if (or (true? optval) (nil? optval)) + (StringBufferWriter. sb) + optval)] + (if *print-pretty* + (with-pretty-writer base-writer + (write-out object)) + (binding [*out* base-writer] + (pr object))) + (if (true? optval) + (*print-fn* (str sb))) + (if (nil? optval) + (str sb))))))) + +(defn pprint + ([object] + (let [sb (StringBuffer.)] + (binding [*out* (StringBufferWriter. sb)] + (pprint object *out*) + (*print-fn* (str sb))))) + ([object writer] + (with-pretty-writer writer + (binding [*print-pretty* true] + (write-out object)) + (if (not (= 0 (get-column *out*))) + (-write *out* \newline))))) + +(defn set-pprint-dispatch + [function] + (set! *print-pprint-dispatch* function) + nil) + +;;====================================================================== +;; Support for the functional interface to the pretty printer +;;====================================================================== + +(defn- check-enumerated-arg [arg choices] + (if-not (choices arg) + ;; TODO clean up choices string + (throw (js/Error. (str "Bad argument: " arg ". It must be one of " choices))))) + +(defn- level-exceeded [] + (and *print-level* (>= *current-level* *print-level*))) + +(defn pprint-newline + "Print a conditional newline to a pretty printing stream. kind specifies if the + newline is :linear, :miser, :fill, or :mandatory. + + This function is intended for use when writing custom dispatch functions. + + Output is sent to *out* which must be a pretty printing writer." + [kind] + (check-enumerated-arg kind #{:linear :miser :fill :mandatory}) + (nl *out* kind)) + +(defn pprint-indent + "Create an indent at this point in the pretty printing stream. This defines how +following lines are indented. relative-to can be either :block or :current depending +whether the indent should be computed relative to the start of the logical block or +the current column position. n is an offset. + +This function is intended for use when writing custom dispatch functions. + +Output is sent to *out* which must be a pretty printing writer." + [relative-to n] + (check-enumerated-arg relative-to #{:block :current}) + (indent *out* relative-to n)) + +;; TODO a real implementation for pprint-tab +(defn pprint-tab + "Tab at this point in the pretty printing stream. kind specifies whether the tab +is :line, :section, :line-relative, or :section-relative. + +Colnum and colinc specify the target column and the increment to move the target +forward if the output is already past the original target. + +This function is intended for use when writing custom dispatch functions. + +Output is sent to *out* which must be a pretty printing writer. + +THIS FUNCTION IS NOT YET IMPLEMENTED." + {:added "1.2"} + [kind colnum colinc] + (check-enumerated-arg kind #{:line :section :line-relative :section-relative}) + (throw (js/Error. "pprint-tab is not yet implemented"))) + +;;====================================================================== +;; cl_format.clj +;;====================================================================== + +;; Forward references +(declare compile-format) +(declare execute-format) +(declare init-navigator) +;; End forward references + +(defn cl-format + "An implementation of a Common Lisp compatible format function. cl-format formats its +arguments to an output stream or string based on the format control string given. It +supports sophisticated formatting of structured data. + +Writer satisfies IWriter, true to output via *print-fn* or nil to output +to a string, format-in is the format control string and the remaining arguments +are the data to be formatted. + +The format control string is a string to be output with embedded 'format directives' +describing how to format the various arguments passed in. + +If writer is nil, cl-format returns the formatted result string. Otherwise, cl-format +returns nil. + +For example: + (let [results [46 38 22]] + (cl-format true \"There ~[are~;is~:;are~]~:* ~d result~:p: ~{~d~^, ~}~%\" + (count results) results)) + +Prints via *print-fn*: + There are 3 results: 46, 38, 22 + +Detailed documentation on format control strings is available in the \"Common Lisp the +Language, 2nd edition\", Chapter 22 (available online at: +http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000) +and in the Common Lisp HyperSpec at +http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm" + {:see-also [["http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000" + "Common Lisp the Language"] + ["http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm" + "Common Lisp HyperSpec"]]} + [writer format-in & args] + (let [compiled-format (if (string? format-in) (compile-format format-in) format-in) + navigator (init-navigator args)] + (execute-format writer compiled-format navigator))) + +(def ^:dynamic ^{:private true} *format-str* nil) + +(defn- format-error [message offset] + (let [full-message (str message \newline *format-str* \newline + (apply str (repeat offset \space)) "^" \newline)] + (throw (js/Error full-message)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Argument navigators manage the argument list +;; as the format statement moves through the list +;; (possibly going forwards and backwards as it does so) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defrecord ^{:private true} + arg-navigator [seq rest pos]) + +(defn- init-navigator + "Create a new arg-navigator from the sequence with the position set to 0" + {:skip-wiki true} + [s] + (let [s (seq s)] + (arg-navigator. s s 0))) + +;; TODO call format-error with offset +(defn- next-arg [navigator] + (let [rst (:rest navigator)] + (if rst + [(first rst) (arg-navigator. (:seq navigator) (next rst) (inc (:pos navigator)))] + (throw (js/Error "Not enough arguments for format definition"))))) + +(defn- next-arg-or-nil [navigator] + (let [rst (:rest navigator)] + (if rst + [(first rst) (arg-navigator. (:seq navigator) (next rst) (inc (:pos navigator)))] + [nil navigator]))) + +;; Get an argument off the arg list and compile it if it's not already compiled +(defn- get-format-arg [navigator] + (let [[raw-format navigator] (next-arg navigator) + compiled-format (if (string? raw-format) + (compile-format raw-format) + raw-format)] + [compiled-format navigator])) + +(declare relative-reposition) + +(defn- absolute-reposition [navigator position] + (if (>= position (:pos navigator)) + (relative-reposition navigator (- (:pos navigator) position)) + (arg-navigator. (:seq navigator) (drop position (:seq navigator)) position))) + +(defn- relative-reposition [navigator position] + (let [newpos (+ (:pos navigator) position)] + (if (neg? position) + (absolute-reposition navigator newpos) + (arg-navigator. (:seq navigator) (drop position (:rest navigator)) newpos)))) + +(defrecord ^{:private true} + compiled-directive [func def params offset]) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; When looking at the parameter list, we may need to manipulate +;; the argument list as well (for 'V' and '#' parameter types). +;; We hide all of this behind a function, but clients need to +;; manage changing arg navigator +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO: validate parameters when they come from arg list +(defn- realize-parameter [[param [raw-val offset]] navigator] + (let [[real-param new-navigator] + (cond + (contains? #{:at :colon} param) ;pass flags through unchanged - this really isn't necessary + [raw-val navigator] + + (= raw-val :parameter-from-args) + (next-arg navigator) + + (= raw-val :remaining-arg-count) + [(count (:rest navigator)) navigator] + + true + [raw-val navigator])] + [[param [real-param offset]] new-navigator])) + +(defn- realize-parameter-list [parameter-map navigator] + (let [[pairs new-navigator] + (map-passing-context realize-parameter navigator parameter-map)] + [(into {} pairs) new-navigator])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Functions that support individual directives +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Common handling code for ~A and ~S +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare opt-base-str) + +(def ^{:private true} + special-radix-markers {2 "#b" 8 "#o" 16 "#x"}) + +(defn- format-simple-number [n] + (cond + (integer? n) (if (= *print-base* 10) + (str n (if *print-radix* ".")) + (str + (if *print-radix* (or (get special-radix-markers *print-base*) (str "#" *print-base* "r"))) + (opt-base-str *print-base* n))) + ;;(ratio? n) ;;no ratio support + :else nil)) + +(defn- format-ascii [print-func params arg-navigator offsets] + (let [[arg arg-navigator] (next-arg arg-navigator) + base-output (or (format-simple-number arg) (print-func arg)) + base-width (.-length base-output) + min-width (+ base-width (:minpad params)) + width (if (>= min-width (:mincol params)) + min-width + (+ min-width + (* (+ (quot (- (:mincol params) min-width 1) + (:colinc params)) + 1) + (:colinc params)))) + chars (apply str (repeat (- width base-width) (:padchar params)))] + (if (:at params) + (print (str chars base-output)) + (print (str base-output chars))) + arg-navigator)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support for the integer directives ~D, ~X, ~O, ~B and some +;; of ~R +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- integral? + "returns true if a number is actually an integer (that is, has no fractional part)" + [x] + (cond + (integer? x) true + ;;(decimal? x) ;;no decimal support + (float? x) (= x (Math/floor x)) + ;;(ratio? x) ;;no ratio support + :else false)) + +(defn- remainders + "Return the list of remainders (essentially the 'digits') of val in the given base" + [base val] + (reverse + (first + (consume #(if (pos? %) + [(rem % base) (quot % base)] + [nil nil]) + val)))) + +;; TODO: xlated-val does not seem to be used here. +;; NB +(defn- base-str + "Return val as a string in the given base" + [base val] + (if (zero? val) + "0" + (let [xlated-val (cond + ;(float? val) (bigdec val) ;;No bigdec + ;(ratio? val) nil ;;No ratio + :else val)] + (apply str + (map + #(if (< % 10) (char (+ (char-code \0) %)) (char (+ (char-code \a) (- % 10)))) + (remainders base val)))))) + +;;Not sure if this is accurate or necessary +(def ^{:private true} + javascript-base-formats {8 "%o", 10 "%d", 16 "%x"}) + +(defn- opt-base-str + "Return val as a string in the given base. No cljs format, so no improved performance." + [base val] + (base-str base val)) + +(defn- group-by* [unit lis] + (reverse + (first + (consume (fn [x] [(seq (reverse (take unit x))) (seq (drop unit x))]) (reverse lis))))) + +(defn- format-integer [base params arg-navigator offsets] + (let [[arg arg-navigator] (next-arg arg-navigator)] + (if (integral? arg) + (let [neg (neg? arg) + pos-arg (if neg (- arg) arg) + raw-str (opt-base-str base pos-arg) + group-str (if (:colon params) + (let [groups (map #(apply str %) (group-by* (:commainterval params) raw-str)) + commas (repeat (count groups) (:commachar params))] + (apply str (next (interleave commas groups)))) + raw-str) + signed-str (cond + neg (str "-" group-str) + (:at params) (str "+" group-str) + true group-str) + padded-str (if (< (.-length signed-str) (:mincol params)) + (str (apply str (repeat (- (:mincol params) (.-length signed-str)) + (:padchar params))) + signed-str) + signed-str)] + (print padded-str)) + (format-ascii print-str {:mincol (:mincol params) :colinc 1 :minpad 0 + :padchar (:padchar params) :at true} + (init-navigator [arg]) nil)) + arg-navigator)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support for english formats (~R and ~:R) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^{:private true} + english-cardinal-units + ["zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine" + "ten" "eleven" "twelve" "thirteen" "fourteen" + "fifteen" "sixteen" "seventeen" "eighteen" "nineteen"]) + +(def ^{:private true} + english-ordinal-units + ["zeroth" "first" "second" "third" "fourth" "fifth" "sixth" "seventh" "eighth" "ninth" + "tenth" "eleventh" "twelfth" "thirteenth" "fourteenth" + "fifteenth" "sixteenth" "seventeenth" "eighteenth" "nineteenth"]) + +(def ^{:private true} + english-cardinal-tens + ["" "" "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety"]) + +(def ^{:private true} + english-ordinal-tens + ["" "" "twentieth" "thirtieth" "fortieth" "fiftieth" + "sixtieth" "seventieth" "eightieth" "ninetieth"]) + +;; We use "short scale" for our units (see http://en.wikipedia.org/wiki/Long_and_short_scales) +;; Number names from http://www.jimloy.com/math/billion.htm +;; We follow the rules for writing numbers from the Blue Book +;; (http://www.grammarbook.com/numbers/numbers.asp) +(def ^{:private true} + english-scale-numbers + ["" "thousand" "million" "billion" "trillion" "quadrillion" "quintillion" + "sextillion" "septillion" "octillion" "nonillion" "decillion" + "undecillion" "duodecillion" "tredecillion" "quattuordecillion" + "quindecillion" "sexdecillion" "septendecillion" + "octodecillion" "novemdecillion" "vigintillion"]) + +(defn- format-simple-cardinal + "Convert a number less than 1000 to a cardinal english string" + [num] + (let [hundreds (quot num 100) + tens (rem num 100)] + (str + (if (pos? hundreds) (str (nth english-cardinal-units hundreds) " hundred")) + (if (and (pos? hundreds) (pos? tens)) " ") + (if (pos? tens) + (if (< tens 20) + (nth english-cardinal-units tens) + (let [ten-digit (quot tens 10) + unit-digit (rem tens 10)] + (str + (if (pos? ten-digit) (nth english-cardinal-tens ten-digit)) + (if (and (pos? ten-digit) (pos? unit-digit)) "-") + (if (pos? unit-digit) (nth english-cardinal-units unit-digit))))))))) + +(defn- add-english-scales + "Take a sequence of parts, add scale numbers (e.g., million) and combine into a string + offset is a factor of 10^3 to multiply by" + [parts offset] + (let [cnt (count parts)] + (loop [acc [] + pos (dec cnt) + this (first parts) + remainder (next parts)] + (if (nil? remainder) + (str (apply str (interpose ", " acc)) + (if (and (not (empty? this)) (not (empty? acc))) ", ") + this + (if (and (not (empty? this)) (pos? (+ pos offset))) + (str " " (nth english-scale-numbers (+ pos offset))))) + (recur + (if (empty? this) + acc + (conj acc (str this " " (nth english-scale-numbers (+ pos offset))))) + (dec pos) + (first remainder) + (next remainder)))))) + +(defn- format-cardinal-english [params navigator offsets] + (let [[arg navigator] (next-arg navigator)] + (if (= 0 arg) + (print "zero") + (let [abs-arg (if (neg? arg) (- arg) arg) ; some numbers are too big for Math/abs (is this true?) + parts (remainders 1000 abs-arg)] + (if (<= (count parts) (count english-scale-numbers)) + (let [parts-strs (map format-simple-cardinal parts) + full-str (add-english-scales parts-strs 0)] + (print (str (if (neg? arg) "minus ") full-str))) + (format-integer ;; for numbers > 10^63, we fall back on ~D + 10 + {:mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true} + (init-navigator [arg]) + {:mincol 0, :padchar 0, :commachar 0 :commainterval 0})))) + navigator)) + +(defn- format-simple-ordinal + "Convert a number less than 1000 to a ordinal english string + Note this should only be used for the last one in the sequence" + [num] + (let [hundreds (quot num 100) + tens (rem num 100)] + (str + (if (pos? hundreds) (str (nth english-cardinal-units hundreds) " hundred")) + (if (and (pos? hundreds) (pos? tens)) " ") + (if (pos? tens) + (if (< tens 20) + (nth english-ordinal-units tens) + (let [ten-digit (quot tens 10) + unit-digit (rem tens 10)] + (if (and (pos? ten-digit) (not (pos? unit-digit))) + (nth english-ordinal-tens ten-digit) + (str + (if (pos? ten-digit) (nth english-cardinal-tens ten-digit)) + (if (and (pos? ten-digit) (pos? unit-digit)) "-") + (if (pos? unit-digit) (nth english-ordinal-units unit-digit)))))) + (if (pos? hundreds) "th"))))) + +(defn- format-ordinal-english [params navigator offsets] + (let [[arg navigator] (next-arg navigator)] + (if (= 0 arg) + (print "zeroth") + (let [abs-arg (if (neg? arg) (- arg) arg) ; some numbers are too big for Math/abs (is this true?) + parts (remainders 1000 abs-arg)] + (if (<= (count parts) (count english-scale-numbers)) + (let [parts-strs (map format-simple-cardinal (drop-last parts)) + head-str (add-english-scales parts-strs 1) + tail-str (format-simple-ordinal (last parts))] + (print (str (if (neg? arg) "minus ") + (cond + (and (not (empty? head-str)) (not (empty? tail-str))) + (str head-str ", " tail-str) + + (not (empty? head-str)) (str head-str "th") + :else tail-str)))) + (do (format-integer ;for numbers > 10^63, we fall back on ~D + 10 + {:mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true} + (init-navigator [arg]) + {:mincol 0, :padchar 0, :commachar 0 :commainterval 0}) + (let [low-two-digits (rem arg 100) + not-teens (or (< 11 low-two-digits) (> 19 low-two-digits)) + low-digit (rem low-two-digits 10)] + (print (cond + (and (== low-digit 1) not-teens) "st" + (and (== low-digit 2) not-teens) "nd" + (and (== low-digit 3) not-teens) "rd" + :else "th"))))))) + navigator)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support for roman numeral formats (~@R and ~@:R) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^{:private true} + old-roman-table + [[ "I" "II" "III" "IIII" "V" "VI" "VII" "VIII" "VIIII"] + [ "X" "XX" "XXX" "XXXX" "L" "LX" "LXX" "LXXX" "LXXXX"] + [ "C" "CC" "CCC" "CCCC" "D" "DC" "DCC" "DCCC" "DCCCC"] + [ "M" "MM" "MMM"]]) + +(def ^{:private true} + new-roman-table + [[ "I" "II" "III" "IV" "V" "VI" "VII" "VIII" "IX"] + [ "X" "XX" "XXX" "XL" "L" "LX" "LXX" "LXXX" "XC"] + [ "C" "CC" "CCC" "CD" "D" "DC" "DCC" "DCCC" "CM"] + [ "M" "MM" "MMM"]]) + +(defn- format-roman + "Format a roman numeral using the specified look-up table" + [table params navigator offsets] + (let [[arg navigator] (next-arg navigator)] + (if (and (number? arg) (> arg 0) (< arg 4000)) + (let [digits (remainders 10 arg)] + (loop [acc [] + pos (dec (count digits)) + digits digits] + (if (empty? digits) + (print (apply str acc)) + (let [digit (first digits)] + (recur (if (= 0 digit) + acc + (conj acc (nth (nth table pos) (dec digit)))) + (dec pos) + (next digits)))))) + (format-integer ; for anything <= 0 or > 3999, we fall back on ~D + 10 + {:mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true} + (init-navigator [arg]) + {:mincol 0, :padchar 0, :commachar 0 :commainterval 0})) + navigator)) + +(defn- format-old-roman [params navigator offsets] + (format-roman old-roman-table params navigator offsets)) + +(defn- format-new-roman [params navigator offsets] + (format-roman new-roman-table params navigator offsets)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support for character formats (~C) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^{:private true} + special-chars {8 "Backspace", 9 "Tab", 10 "Newline", 13 "Return", 32 "Space"}) + +(defn- pretty-character [params navigator offsets] + (let [[c navigator] (next-arg navigator) + as-int (char-code c) + base-char (bit-and as-int 127) + meta (bit-and as-int 128) + special (get special-chars base-char)] + (if (> meta 0) (print "Meta-")) + (print (cond + special special + (< base-char 32) (str "Control-" (char (+ base-char 64))) + (= base-char 127) "Control-?" + :else (char base-char))) + navigator)) + +(defn- readable-character [params navigator offsets] + (let [[c navigator] (next-arg navigator)] + (condp = (:char-format params) + \o (cl-format true "\\o~3, '0o" (char-code c)) + \u (cl-format true "\\u~4, '0x" (char-code c)) + nil (print-char c)) + navigator)) + +(defn- plain-character [params navigator offsets] + (let [[char navigator] (next-arg navigator)] + (print char) + navigator)) + +;; Check to see if a result is an abort (~^) construct +;; TODO: move these funcs somewhere more appropriate +(defn- abort? [context] + (let [token (first context)] + (or (= :up-arrow token) (= :colon-up-arrow token)))) + +;; Handle the execution of "sub-clauses" in bracket constructions +(defn- execute-sub-format [format args base-args] + (second + (map-passing-context + (fn [element context] + (if (abort? context) + [nil context] ; just keep passing it along + (let [[params args] (realize-parameter-list (:params element) context) + [params offsets] (unzip-map params) + params (assoc params :base-args base-args)] + [nil (apply (:func element) [params args offsets])]))) + args + format))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support for real number formats +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO - return exponent as int to eliminate double conversion +(defn- float-parts-base + "Produce string parts for the mantissa (normalize 1-9) and exponent" + [f] + (let [s (string/lower-case (str f)) + exploc (.indexOf s \e) + dotloc (.indexOf s \.)] + (if (neg? exploc) + (if (neg? dotloc) + [s (str (dec (count s)))] + [(str (subs s 0 dotloc) (subs s (inc dotloc))) (str (dec dotloc))]) + (if (neg? dotloc) + [(subs s 0 exploc) (subs s (inc exploc))] + [(str (subs s 0 1) (subs s 2 exploc)) (subs s (inc exploc))])))) + +(defn- float-parts + "Take care of leading and trailing zeros in decomposed floats" + [f] + (let [[m e] (float-parts-base f) + m1 (rtrim m \0) + m2 (ltrim m1 \0) + delta (- (count m1) (count m2)) + e (if (and (pos? (count e)) (= (nth e 0) \+)) (subs e 1) e)] + (if (empty? m2) + ["0" 0] + [m2 (- (js/parseInt e) delta)]))) + +(defn- inc-s + "Assumption: The input string consists of one or more decimal digits, + and no other characters. Return a string containing one or more + decimal digits containing a decimal number one larger than the input + string. The output string will always be the same length as the input + string, or one character longer." + [s] + (let [len-1 (dec (count s))] + (loop [i (int len-1)] + (cond + (neg? i) (apply str "1" (repeat (inc len-1) "0")) + (= \9 (.charAt s i)) (recur (dec i)) + :else (apply str (subs s 0 i) + (char (inc (char-code (.charAt s i)))) + (repeat (- len-1 i) "0")))))) + +(defn- round-str [m e d w] + (if (or d w) + (let [len (count m) + ;; Every formatted floating point number should include at + ;; least one decimal digit and a decimal point. + w (if w (max 2 w) + ;;NB: if w doesn't exist, it won't ever be used because d will + ;; satisfy the cond below. cljs gives a compilation warning if + ;; we don't provide a value here. + 0) + round-pos (cond + ;; If d was given, that forces the rounding + ;; position, regardless of any width that may + ;; have been specified. + d (+ e d 1) + ;; Otherwise w was specified, so pick round-pos + ;; based upon that. + ;; If e>=0, then abs value of number is >= 1.0, + ;; and e+1 is number of decimal digits before the + ;; decimal point when the number is written + ;; without scientific notation. Never round the + ;; number before the decimal point. + (>= e 0) (max (inc e) (dec w)) + ;; e < 0, so number abs value < 1.0 + :else (+ w e)) + [m1 e1 round-pos len] (if (= round-pos 0) + [(str "0" m) (inc e) 1 (inc len)] + [m e round-pos len])] + (if round-pos + (if (neg? round-pos) + ["0" 0 false] + (if (> len round-pos) + (let [round-char (nth m1 round-pos) + result (subs m1 0 round-pos)] + (if (>= (char-code round-char) (char-code \5)) + (let [round-up-result (inc-s result) + expanded (> (count round-up-result) (count result))] + [(if expanded + (subs round-up-result 0 (dec (count round-up-result))) + round-up-result) + e1 expanded]) + [result e1 false])) + [m e false])) + [m e false])) + [m e false])) + +(defn- expand-fixed [m e d] + (let [[m1 e1] (if (neg? e) + [(str (apply str (repeat (dec (- e)) \0)) m) -1] + [m e]) + len (count m1) + target-len (if d (+ e1 d 1) (inc e1))] + (if (< len target-len) + (str m1 (apply str (repeat (- target-len len) \0))) + m1))) + +(defn- insert-decimal + "Insert the decimal point at the right spot in the number to match an exponent" + [m e] + (if (neg? e) + (str "." m) + (let [loc (inc e)] + (str (subs m 0 loc) "." (subs m loc))))) + +(defn- get-fixed [m e d] + (insert-decimal (expand-fixed m e d) e)) + +(defn- insert-scaled-decimal + "Insert the decimal point at the right spot in the number to match an exponent" + [m k] + (if (neg? k) + (str "." m) + (str (subs m 0 k) "." (subs m k)))) + +;;TODO: No ratio, so not sure what to do here +(defn- convert-ratio [x] + x) + +;; the function to render ~F directives +;; TODO: support rationals. Back off to ~D/~A in the appropriate cases +(defn- fixed-float [params navigator offsets] + (let [w (:w params) + d (:d params) + [arg navigator] (next-arg navigator) + [sign abs] (if (neg? arg) ["-" (- arg)] ["+" arg]) + abs (convert-ratio abs) + [mantissa exp] (float-parts abs) + scaled-exp (+ exp (:k params)) + add-sign (or (:at params) (neg? arg)) + append-zero (and (not d) (<= (dec (count mantissa)) scaled-exp)) + [rounded-mantissa scaled-exp expanded] (round-str mantissa scaled-exp + d (if w (- w (if add-sign 1 0)))) + fixed-repr (get-fixed rounded-mantissa (if expanded (inc scaled-exp) scaled-exp) d) + fixed-repr (if (and w d + (>= d 1) + (= (.charAt fixed-repr 0) \0) + (= (.charAt fixed-repr 1) \.) + (> (count fixed-repr) (- w (if add-sign 1 0)))) + (subs fixed-repr 1) ;chop off leading 0 + fixed-repr) + prepend-zero (= (first fixed-repr) \.)] + (if w + (let [len (count fixed-repr) + signed-len (if add-sign (inc len) len) + prepend-zero (and prepend-zero (not (>= signed-len w))) + append-zero (and append-zero (not (>= signed-len w))) + full-len (if (or prepend-zero append-zero) + (inc signed-len) + signed-len)] + (if (and (> full-len w) (:overflowchar params)) + (print (apply str (repeat w (:overflowchar params)))) + (print (str + (apply str (repeat (- w full-len) (:padchar params))) + (if add-sign sign) + (if prepend-zero "0") + fixed-repr + (if append-zero "0"))))) + (print (str + (if add-sign sign) + (if prepend-zero "0") + fixed-repr + (if append-zero "0")))) + navigator)) + +;; the function to render ~E directives +;; TODO: support rationals. Back off to ~D/~A in the appropriate cases +;; TODO: define ~E representation for Infinity +(defn- exponential-float [params navigator offset] + (let [[arg navigator] (next-arg navigator) + arg (convert-ratio arg)] + (loop [[mantissa exp] (float-parts (if (neg? arg) (- arg) arg))] + (let [w (:w params) + d (:d params) + e (:e params) + k (:k params) + expchar (or (:exponentchar params) \E) + add-sign (or (:at params) (neg? arg)) + prepend-zero (<= k 0) + scaled-exp (- exp (dec k)) + scaled-exp-str (str (Math/abs scaled-exp)) + scaled-exp-str (str expchar (if (neg? scaled-exp) \- \+) + (if e (apply str + (repeat + (- e + (count scaled-exp-str)) + \0))) + scaled-exp-str) + exp-width (count scaled-exp-str) + base-mantissa-width (count mantissa) + scaled-mantissa (str (apply str (repeat (- k) \0)) + mantissa + (if d + (apply str + (repeat + (- d (dec base-mantissa-width) + (if (neg? k) (- k) 0)) \0)))) + w-mantissa (if w (- w exp-width)) + [rounded-mantissa _ incr-exp] (round-str + scaled-mantissa 0 + (cond + (= k 0) (dec d) + (pos? k) d + (neg? k) (dec d)) + (if w-mantissa + (- w-mantissa (if add-sign 1 0)))) + full-mantissa (insert-scaled-decimal rounded-mantissa k) + append-zero (and (= k (count rounded-mantissa)) (nil? d))] + (if (not incr-exp) + (if w + (let [len (+ (count full-mantissa) exp-width) + signed-len (if add-sign (inc len) len) + prepend-zero (and prepend-zero (not (= signed-len w))) + full-len (if prepend-zero (inc signed-len) signed-len) + append-zero (and append-zero (< full-len w))] + (if (and (or (> full-len w) (and e (> (- exp-width 2) e))) + (:overflowchar params)) + (print (apply str (repeat w (:overflowchar params)))) + (print (str + (apply str + (repeat + (- w full-len (if append-zero 1 0)) + (:padchar params))) + (if add-sign (if (neg? arg) \- \+)) + (if prepend-zero "0") + full-mantissa + (if append-zero "0") + scaled-exp-str)))) + (print (str + (if add-sign (if (neg? arg) \- \+)) + (if prepend-zero "0") + full-mantissa + (if append-zero "0") + scaled-exp-str))) + (recur [rounded-mantissa (inc exp)])))) + navigator)) + +;; the function to render ~G directives +;; This just figures out whether to pass the request off to ~F or ~E based +;; on the algorithm in CLtL. +;; TODO: support rationals. Back off to ~D/~A in the appropriate cases +;; TODO: refactor so that float-parts isn't called twice +(defn- general-float [params navigator offsets] + (let [[arg _] (next-arg navigator) + arg (convert-ratio arg) + [mantissa exp] (float-parts (if (neg? arg) (- arg) arg)) + w (:w params) + d (:d params) + e (:e params) + n (if (= arg 0.0) 0 (inc exp)) + ee (if e (+ e 2) 4) + ww (if w (- w ee)) + d (if d d (max (count mantissa) (min n 7))) + dd (- d n)] + (if (<= 0 dd d) + (let [navigator (fixed-float {:w ww, :d dd, :k 0, + :overflowchar (:overflowchar params), + :padchar (:padchar params), :at (:at params)} + navigator offsets)] + (print (apply str (repeat ee \space))) + navigator) + (exponential-float params navigator offsets)))) + +;; the function to render ~$ directives +;; TODO: support rationals. Back off to ~D/~A in the appropriate cases +(defn- dollar-float [params navigator offsets] + (let [[arg navigator] (next-arg navigator) + [mantissa exp] (float-parts (Math/abs arg)) + d (:d params) ; digits after the decimal + n (:n params) ; minimum digits before the decimal + w (:w params) ; minimum field width + add-sign (or (:at params) (neg? arg)) + [rounded-mantissa scaled-exp expanded] (round-str mantissa exp d nil) + fixed-repr (get-fixed rounded-mantissa (if expanded (inc scaled-exp) scaled-exp) d) + full-repr (str (apply str (repeat (- n (.indexOf fixed-repr \.)) \0)) fixed-repr) + full-len (+ (count full-repr) (if add-sign 1 0))] + (print (str + (if (and (:colon params) add-sign) (if (neg? arg) \- \+)) + (apply str (repeat (- w full-len) (:padchar params))) + (if (and (not (:colon params)) add-sign) (if (neg? arg) \- \+)) + full-repr)) + navigator)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support for the '~[...~]' conditional construct in its +;; different flavors +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; ~[...~] without any modifiers chooses one of the clauses based on the param or +;; next argument +;; TODO check arg is positive int +(defn- choice-conditional [params arg-navigator offsets] + (let [arg (:selector params) + [arg navigator] (if arg [arg arg-navigator] (next-arg arg-navigator)) + clauses (:clauses params) + clause (if (or (neg? arg) (>= arg (count clauses))) + (first (:else params)) + (nth clauses arg))] + (if clause + (execute-sub-format clause navigator (:base-args params)) + navigator))) + +;; ~:[...~] with the colon reads the next argument treating it as a truth value +(defn- boolean-conditional [params arg-navigator offsets] + (let [[arg navigator] (next-arg arg-navigator) + clauses (:clauses params) + clause (if arg + (second clauses) + (first clauses))] + (if clause + (execute-sub-format clause navigator (:base-args params)) + navigator))) + +;; ~@[...~] with the at sign executes the conditional if the next arg is not +;; nil/false without consuming the arg +(defn- check-arg-conditional [params arg-navigator offsets] + (let [[arg navigator] (next-arg arg-navigator) + clauses (:clauses params) + clause (if arg (first clauses))] + (if arg + (if clause + (execute-sub-format clause arg-navigator (:base-args params)) + arg-navigator) + navigator))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support for the '~{...~}' iteration construct in its +;; different flavors +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; ~{...~} without any modifiers uses the next argument as an argument list that +;; is consumed by all the iterations +(defn- iterate-sublist [params navigator offsets] + (let [max-count (:max-iterations params) + param-clause (first (:clauses params)) + [clause navigator] (if (empty? param-clause) + (get-format-arg navigator) + [param-clause navigator]) + [arg-list navigator] (next-arg navigator) + args (init-navigator arg-list)] + (loop [count 0 + args args + last-pos (int -1)] + (if (and (not max-count) (= (:pos args) last-pos) (> count 1)) + ;; TODO get the offset in here and call format exception + (throw (js/Error "%{ construct not consuming any arguments: Infinite loop!"))) + (if (or (and (empty? (:rest args)) + (or (not (:colon (:right-params params))) (> count 0))) + (and max-count (>= count max-count))) + navigator + (let [iter-result (execute-sub-format clause args (:base-args params))] + (if (= :up-arrow (first iter-result)) + navigator + (recur (inc count) iter-result (:pos args)))))))) + +;; ~:{...~} with the colon treats the next argument as a list of sublists. Each of the +;; sublists is used as the arglist for a single iteration. +(defn- iterate-list-of-sublists [params navigator offsets] + (let [max-count (:max-iterations params) + param-clause (first (:clauses params)) + [clause navigator] (if (empty? param-clause) + (get-format-arg navigator) + [param-clause navigator]) + [arg-list navigator] (next-arg navigator)] + (loop [count 0 + arg-list arg-list] + (if (or (and (empty? arg-list) + (or (not (:colon (:right-params params))) (> count 0))) + (and max-count (>= count max-count))) + navigator + (let [iter-result (execute-sub-format + clause + (init-navigator (first arg-list)) + (init-navigator (next arg-list)))] + (if (= :colon-up-arrow (first iter-result)) + navigator + (recur (inc count) (next arg-list)))))))) + +;; ~@{...~} with the at sign uses the main argument list as the arguments to the iterations +;; is consumed by all the iterations +(defn- iterate-main-list [params navigator offsets] + (let [max-count (:max-iterations params) + param-clause (first (:clauses params)) + [clause navigator] (if (empty? param-clause) + (get-format-arg navigator) + [param-clause navigator])] + (loop [count 0 + navigator navigator + last-pos (int -1)] + (if (and (not max-count) (= (:pos navigator) last-pos) (> count 1)) + ;; TODO get the offset in here and call format exception + (throw (js/Error "%@{ construct not consuming any arguments: Infinite loop!"))) + (if (or (and (empty? (:rest navigator)) + (or (not (:colon (:right-params params))) (> count 0))) + (and max-count (>= count max-count))) + navigator + (let [iter-result (execute-sub-format clause navigator (:base-args params))] + (if (= :up-arrow (first iter-result)) + (second iter-result) + (recur + (inc count) iter-result (:pos navigator)))))))) + +;; ~@:{...~} with both colon and at sign uses the main argument list as a set of sublists, one +;; of which is consumed with each iteration +(defn- iterate-main-sublists [params navigator offsets] + (let [max-count (:max-iterations params) + param-clause (first (:clauses params)) + [clause navigator] (if (empty? param-clause) + (get-format-arg navigator) + [param-clause navigator])] + (loop [count 0 + navigator navigator] + (if (or (and (empty? (:rest navigator)) + (or (not (:colon (:right-params params))) (> count 0))) + (and max-count (>= count max-count))) + navigator + (let [[sublist navigator] (next-arg-or-nil navigator) + iter-result (execute-sub-format clause (init-navigator sublist) navigator)] + (if (= :colon-up-arrow (first iter-result)) + navigator + (recur (inc count) navigator))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The '~< directive has two completely different meanings +;; in the '~<...~>' form it does justification, but with +;; ~<...~:>' it represents the logical block operation of the +;; pretty printer. +;; +;; Unfortunately, the current architecture decides what function +;; to call at form parsing time before the sub-clauses have been +;; folded, so it is left to run-time to make the decision. +;; +;; TODO: make it possible to make these decisions at compile-time. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare format-logical-block) +(declare justify-clauses) + +(defn- logical-block-or-justify [params navigator offsets] + (if (:colon (:right-params params)) + (format-logical-block params navigator offsets) + (justify-clauses params navigator offsets))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support for the '~<...~>' justification directive +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- render-clauses [clauses navigator base-navigator] + (loop [clauses clauses + acc [] + navigator navigator] + (if (empty? clauses) + [acc navigator] + (let [clause (first clauses) + [iter-result result-str] (let [sb (StringBuffer.)] + (binding [*out* (StringBufferWriter. sb)] + [(execute-sub-format clause navigator base-navigator) + (str sb)]))] + (if (= :up-arrow (first iter-result)) + [acc (second iter-result)] + (recur (next clauses) (conj acc result-str) iter-result)))))) + +;; TODO support for ~:; constructions +(defn- justify-clauses [params navigator offsets] + (let [[[eol-str] new-navigator] (when-let [else (:else params)] + (render-clauses else navigator (:base-args params))) + navigator (or new-navigator navigator) + [else-params new-navigator] (when-let [p (:else-params params)] + (realize-parameter-list p navigator)) + navigator (or new-navigator navigator) + min-remaining (or (first (:min-remaining else-params)) 0) + max-columns (or (first (:max-columns else-params)) + (get-max-column *out*)) + clauses (:clauses params) + [strs navigator] (render-clauses clauses navigator (:base-args params)) + slots (max 1 + (+ (dec (count strs)) (if (:colon params) 1 0) (if (:at params) 1 0))) + chars (reduce + (map count strs)) + mincol (:mincol params) + minpad (:minpad params) + colinc (:colinc params) + minout (+ chars (* slots minpad)) + result-columns (if (<= minout mincol) + mincol + (+ mincol (* colinc + (+ 1 (quot (- minout mincol 1) colinc))))) + total-pad (- result-columns chars) + pad (max minpad (quot total-pad slots)) + extra-pad (- total-pad (* pad slots)) + pad-str (apply str (repeat pad (:padchar params)))] + (if (and eol-str (> (+ (get-column (:base @@*out*)) min-remaining result-columns) + max-columns)) + (print eol-str)) + (loop [slots slots + extra-pad extra-pad + strs strs + pad-only (or (:colon params) + (and (= (count strs) 1) (not (:at params))))] + (if (seq strs) + (do + (print (str (if (not pad-only) (first strs)) + (if (or pad-only (next strs) (:at params)) pad-str) + (if (pos? extra-pad) (:padchar params)))) + (recur + (dec slots) + (dec extra-pad) + (if pad-only strs (next strs)) + false)))) + navigator)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Support for case modification with ~(...~). +;;; We do this by wrapping the underlying writer with +;;; a special writer to do the appropriate modification. This +;;; allows us to support arbitrary-sized output and sources +;;; that may block. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- downcase-writer + "Returns a proxy that wraps writer, converting all characters to lower case" + [writer] + (reify + IWriter + (-flush [_] (-flush writer)) + (-write + ;;no multi-arity, not sure of importance + #_([^chars cbuf ^Integer off ^Integer len] + (.write writer cbuf off len)) + [this x] + (condp = (type x) + js/String + (let [s x] + (-write writer (string/lower-case s))) + + js/Number + (let [c x] + ;;TODO need to enforce integers only? + (-write writer (string/lower-case (char c)))))))) + +(defn- upcase-writer + "Returns a proxy that wraps writer, converting all characters to upper case" + [writer] + (reify + IWriter + (-flush [_] (-flush writer)) + (-write + ;;no multi-arity, not sure of importance + #_([^chars cbuf ^Integer off ^Integer len] + (.write writer cbuf off len)) + [this x] + (condp = (type x) + js/String + (let [s x] + (-write writer (string/upper-case s))) + + js/Number + (let [c x] + ;;TODO need to enforce integers only? + (-write writer (string/upper-case (char c)))))))) + +;;TODO: This is an oversimplied version. Needs to be fully implemented +(defn- is-letter? [s] + (boolean + (#{"A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" + "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"} + s))) + +(defn- capitalize-string + "Capitalizes the words in a string. If first? is false, don't capitalize the + first character of the string even if it's a letter." + [s first?] + (let [f (first s) + s (if (and first? f (is-letter? f)) + (str (string/upper-case f) (subs s 1)) + s)] + (apply str + (first + (consume + (fn [s] + (if (empty? s) + [nil nil] + (let [m (.exec (js/RegExp "\\W\\w" "g") s) + offset (and m (inc (.-index m)))] + (if offset + [(str (subs s 0 offset) + (string/upper-case (nth s offset))) + (subs s (inc offset))] + [s nil])))) + s))))) + +;;TODO: This is an oversimplied version. Needs to be fully implemented +(defn- is-whitespace? [s] + (boolean + (#{\space \newline} s))) + +(defn- capitalize-word-writer + "Returns a proxy that wraps writer, capitalizing all words" + [writer] + (let [last-was-whitespace? (atom true)] + (reify + IWriter + (-flush [_] (-flush writer)) + (-write + ;;no multi-arity + #_([^chars cbuf ^Integer off ^Integer len] + (.write writer cbuf off len)) + [this x] + (condp = (type x) + js/String + (let [s x] + (-write writer + (capitalize-string (.toLowerCase s) @last-was-whitespace?)) + (when (pos? (.-length s)) + (reset! last-was-whitespace? (is-whitespace? (nth s (dec (count s))))))) + + js/Number + (let [c (char x)] + (let [mod-c (if @last-was-whitespace? (string/upper-case c) c)] + (-write writer mod-c) + (reset! last-was-whitespace? (is-whitespace? c))))))))) + +(defn- init-cap-writer + "Returns a proxy that wraps writer, capitalizing the first word" + [writer] + (let [capped (atom false)] + (reify + IWriter + (-flush [_] (-flush writer)) + (-write + ;;no multi-arity + #_([^chars cbuf ^Integer off ^Integer len] + (.write writer cbuf off len)) + [this x] + (condp = (type x) + js/String + (let [s (string/lower-case x)] + (if (not @capped) + (let [m (.exec (js/RegExp "\\S" "g") s) + offset (and m (.-index m))] + (if offset + (do (-write writer + (str (subs s 0 offset) + (string/upper-case (nth s offset)) + (string/lower-case (subs s (inc offset))))) + (reset! capped true)) + (-write writer s))) + (-write writer (string/lower-case s)))) + + js/Number + (let [c (char x)] + (if (and (not @capped) (is-letter? c)) + (do + (reset! capped true) + (-write writer (string/upper-case c))) + (-write writer (string/lower-case c))))))))) + +(defn- modify-case [make-writer params navigator offsets] + (let [clause (first (:clauses params))] + (binding [*out* (make-writer *out*)] + (execute-sub-format clause navigator (:base-args params))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; If necessary, wrap the writer in a PrettyWriter object +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO update this doc string to show correct way to print +(defn get-pretty-writer + "Returns the IWriter passed in wrapped in a pretty writer proxy, unless it's +already a pretty writer. Generally, it is unnecessary to call this function, since pprint, +write, and cl-format all call it if they need to. However if you want the state to be +preserved across calls, you will want to wrap them with this. + +For example, when you want to generate column-aware output with multiple calls to cl-format, +do it like in this example: + + (defn print-table [aseq column-width] + (binding [*out* (get-pretty-writer *out*)] + (doseq [row aseq] + (doseq [col row] + (cl-format true \"~4D~7,vT\" col column-width)) + (prn)))) + +Now when you run: + + user> (print-table (map #(vector % (* % %) (* % % %)) (range 1 11)) 8) + +It prints a table of squares and cubes for the numbers from 1 to 10: + + 1 1 1 + 2 4 8 + 3 9 27 + 4 16 64 + 5 25 125 + 6 36 216 + 7 49 343 + 8 64 512 + 9 81 729 + 10 100 1000" + [writer] + (if (pretty-writer? writer) + writer + (pretty-writer writer *print-right-margin* *print-miser-width*))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Support for column-aware operations ~&, ~T +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn fresh-line + "Make a newline if *out* is not already at the beginning of the line. If *out* is +not a pretty writer (which keeps track of columns), this function always outputs a newline." + [] + (if (satisfies? IDeref *out*) + (if (not (= 0 (get-column (:base @@*out*)))) + (prn)) + (prn))) + +(defn- absolute-tabulation [params navigator offsets] + (let [colnum (:colnum params) + colinc (:colinc params) + current (get-column (:base @@*out*)) + space-count (cond + (< current colnum) (- colnum current) + (= colinc 0) 0 + :else (- colinc (rem (- current colnum) colinc)))] + (print (apply str (repeat space-count \space)))) + navigator) + +(defn- relative-tabulation [params navigator offsets] + (let [colrel (:colnum params) + colinc (:colinc params) + start-col (+ colrel (get-column (:base @@*out*))) + offset (if (pos? colinc) (rem start-col colinc) 0) + space-count (+ colrel (if (= 0 offset) 0 (- colinc offset)))] + (print (apply str (repeat space-count \space)))) + navigator) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Support for accessing the pretty printer from a format +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO: support ~@; per-line-prefix separator +;; TODO: get the whole format wrapped so we can start the lb at any column +(defn- format-logical-block [params navigator offsets] + (let [clauses (:clauses params) + clause-count (count clauses) + prefix (cond + (> clause-count 1) (:string (:params (first (first clauses)))) + (:colon params) "(") + body (nth clauses (if (> clause-count 1) 1 0)) + suffix (cond + (> clause-count 2) (:string (:params (first (nth clauses 2)))) + (:colon params) ")") + [arg navigator] (next-arg navigator)] + (pprint-logical-block :prefix prefix :suffix suffix + (execute-sub-format + body + (init-navigator arg) + (:base-args params))) + navigator)) + +(defn- set-indent [params navigator offsets] + (let [relative-to (if (:colon params) :current :block)] + (pprint-indent relative-to (:n params)) + navigator)) + +;;; TODO: support ~:T section options for ~T +(defn- conditional-newline [params navigator offsets] + (let [kind (if (:colon params) + (if (:at params) :mandatory :fill) + (if (:at params) :miser :linear))] + (pprint-newline kind) + navigator)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; The table of directives we support, each with its params, +;;; properties, and the compilation function +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defdirectives + (\A + [:mincol [0 js/Number] :colinc [1 js/Number] :minpad [0 js/Number] :padchar [\space js/String]] + #{:at :colon :both} {} + #(format-ascii print-str %1 %2 %3)) + + (\S + [:mincol [0 js/Number] :colinc [1 js/Number] :minpad [0 js/Number] :padchar [\space js/String]] + #{:at :colon :both} {} + #(format-ascii pr-str %1 %2 %3)) + + (\D + [:mincol [0 js/Number] :padchar [\space js/String] :commachar [\, js/String] + :commainterval [3 js/Number]] + #{:at :colon :both} {} + #(format-integer 10 %1 %2 %3)) + + (\B + [:mincol [0 js/Number] :padchar [\space js/String] :commachar [\, js/String] + :commainterval [3 js/Number]] + #{:at :colon :both} {} + #(format-integer 2 %1 %2 %3)) + + (\O + [:mincol [0 js/Number] :padchar [\space js/String] :commachar [\, js/String] + :commainterval [3 js/Number]] + #{:at :colon :both} {} + #(format-integer 8 %1 %2 %3)) + + (\X + [:mincol [0 js/Number] :padchar [\space js/String] :commachar [\, js/String] + :commainterval [3 js/Number]] + #{:at :colon :both} {} + #(format-integer 16 %1 %2 %3)) + + (\R + [:base [nil js/Number] :mincol [0 js/Number] :padchar [\space js/String] :commachar [\, js/String] + :commainterval [3 js/Number]] + #{:at :colon :both} {} + (do + (cond ; ~R is overloaded with bizareness + (first (:base params)) #(format-integer (:base %1) %1 %2 %3) + (and (:at params) (:colon params)) #(format-old-roman %1 %2 %3) + (:at params) #(format-new-roman %1 %2 %3) + (:colon params) #(format-ordinal-english %1 %2 %3) + true #(format-cardinal-english %1 %2 %3)))) + + (\P + [] + #{:at :colon :both} {} + (fn [params navigator offsets] + (let [navigator (if (:colon params) (relative-reposition navigator -1) navigator) + strs (if (:at params) ["y" "ies"] ["" "s"]) + [arg navigator] (next-arg navigator)] + (print (if (= arg 1) (first strs) (second strs))) + navigator))) + + (\C + [:char-format [nil js/String]] + #{:at :colon :both} {} + (cond + (:colon params) pretty-character + (:at params) readable-character + :else plain-character)) + + (\F + [:w [nil js/Number] :d [nil js/Number] :k [0 js/Number] :overflowchar [nil js/String] + :padchar [\space js/String]] + #{:at} {} + fixed-float) + + (\E + [:w [nil js/Number] :d [nil js/Number] :e [nil js/Number] :k [1 js/Number] + :overflowchar [nil js/String] :padchar [\space js/String] + :exponentchar [nil js/String]] + #{:at} {} + exponential-float) + + (\G + [:w [nil js/Number] :d [nil js/Number] :e [nil js/Number] :k [1 js/Number] + :overflowchar [nil js/String] :padchar [\space js/String] + :exponentchar [nil js/String]] + #{:at} {} + general-float) + + (\$ + [:d [2 js/Number] :n [1 js/Number] :w [0 js/Number] :padchar [\space js/String]] + #{:at :colon :both} {} + dollar-float) + + (\% + [:count [1 js/Number]] + #{} {} + (fn [params arg-navigator offsets] + (dotimes [i (:count params)] + (prn)) + arg-navigator)) + + (\& + [:count [1 js/Number]] + #{:pretty} {} + (fn [params arg-navigator offsets] + (let [cnt (:count params)] + (if (pos? cnt) (fresh-line)) + (dotimes [i (dec cnt)] + (prn))) + arg-navigator)) + + (\| + [:count [1 js/Number]] + #{} {} + (fn [params arg-navigator offsets] + (dotimes [i (:count params)] + (print \formfeed)) + arg-navigator)) + + (\~ + [:n [1 js/Number]] + #{} {} + (fn [params arg-navigator offsets] + (let [n (:n params)] + (print (apply str (repeat n \~))) + arg-navigator))) + + (\newline ;; Whitespace supression is handled in the compilation loop + [] + #{:colon :at} {} + (fn [params arg-navigator offsets] + (if (:at params) + (prn)) + arg-navigator)) + + (\T + [:colnum [1 js/Number] :colinc [1 js/Number]] + #{:at :pretty} {} + (if (:at params) + #(relative-tabulation %1 %2 %3) + #(absolute-tabulation %1 %2 %3))) + + (\* + [:n [1 js/Number]] + #{:colon :at} {} + (fn [params navigator offsets] + (let [n (:n params)] + (if (:at params) + (absolute-reposition navigator n) + (relative-reposition navigator (if (:colon params) (- n) n)))))) + + (\? + [] + #{:at} {} + (if (:at params) + (fn [params navigator offsets] ; args from main arg list + (let [[subformat navigator] (get-format-arg navigator)] + (execute-sub-format subformat navigator (:base-args params)))) + (fn [params navigator offsets] ; args from sub-list + (let [[subformat navigator] (get-format-arg navigator) + [subargs navigator] (next-arg navigator) + sub-navigator (init-navigator subargs)] + (execute-sub-format subformat sub-navigator (:base-args params)) + navigator)))) + + (\( + [] + #{:colon :at :both} {:right \), :allows-separator nil, :else nil} + (let [mod-case-writer (cond + (and (:at params) (:colon params)) + upcase-writer + + (:colon params) + capitalize-word-writer + + (:at params) + init-cap-writer + + :else + downcase-writer)] + #(modify-case mod-case-writer %1 %2 %3))) + + (\) [] #{} {} nil) + + (\[ + [:selector [nil js/Number]] + #{:colon :at} {:right \], :allows-separator true, :else :last} + (cond + (:colon params) + boolean-conditional + + (:at params) + check-arg-conditional + + true + choice-conditional)) + + (\; [:min-remaining [nil js/Number] :max-columns [nil js/Number]] + #{:colon} {:separator true} nil) + + (\] [] #{} {} nil) + + (\{ + [:max-iterations [nil js/Number]] + #{:colon :at :both} {:right \}, :allows-separator false} + (cond + (and (:at params) (:colon params)) + iterate-main-sublists + + (:colon params) + iterate-list-of-sublists + + (:at params) + iterate-main-list + + true + iterate-sublist)) + + (\} [] #{:colon} {} nil) + + (\< + [:mincol [0 js/Number] :colinc [1 js/Number] :minpad [0 js/Number] :padchar [\space js/String]] + #{:colon :at :both :pretty} {:right \>, :allows-separator true, :else :first} + logical-block-or-justify) + + (\> [] #{:colon} {} nil) + + ;; TODO: detect errors in cases where colon not allowed + (\^ [:arg1 [nil js/Number] :arg2 [nil js/Number] :arg3 [nil js/Number]] + #{:colon} {} + (fn [params navigator offsets] + (let [arg1 (:arg1 params) + arg2 (:arg2 params) + arg3 (:arg3 params) + exit (if (:colon params) :colon-up-arrow :up-arrow)] + (cond + (and arg1 arg2 arg3) + (if (<= arg1 arg2 arg3) [exit navigator] navigator) + + (and arg1 arg2) + (if (= arg1 arg2) [exit navigator] navigator) + + arg1 + (if (= arg1 0) [exit navigator] navigator) + + true ; TODO: handle looking up the arglist stack for info + (if (if (:colon params) + (empty? (:rest (:base-args params))) + (empty? (:rest navigator))) + [exit navigator] navigator))))) + + (\W + [] + #{:at :colon :both :pretty} {} + (if (or (:at params) (:colon params)) + (let [bindings (concat + (if (:at params) [:level nil :length nil] []) + (if (:colon params) [:pretty true] []))] + (fn [params navigator offsets] + (let [[arg navigator] (next-arg navigator)] + (if (apply write arg bindings) + [:up-arrow navigator] + navigator)))) + (fn [params navigator offsets] + (let [[arg navigator] (next-arg navigator)] + (if (write-out arg) + [:up-arrow navigator] + navigator))))) + + (\_ + [] + #{:at :colon :both} {} + conditional-newline) + + (\I + [:n [0 js/Number]] + #{:colon} {} + set-indent) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Code to manage the parameters and flags associated with each +;; directive in the format string. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^{:private true} + param-pattern #"^([vV]|#|('.)|([+-]?\d+)|(?=,))") + +(def ^{:private true} + special-params #{:parameter-from-args :remaining-arg-count}) + +(defn- extract-param [[s offset saw-comma]] + (let [m (js/RegExp. (.-source param-pattern) "g") + param (.exec m s)] + (if param + (let [token-str (first param) + remainder (subs s (.-lastIndex m)) + new-offset (+ offset (.-lastIndex m))] + (if (not (= \, (nth remainder 0))) + [[token-str offset] [remainder new-offset false]] + [[token-str offset] [(subs remainder 1) (inc new-offset) true]])) + (if saw-comma + (format-error "Badly formed parameters in format directive" offset) + [nil [s offset]])))) + +(defn- extract-params [s offset] + (consume extract-param [s offset false])) + +(defn- translate-param + "Translate the string representation of a param to the internalized + representation" + [[p offset]] + [(cond + (= (.-length p) 0) nil + (and (= (.-length p) 1) (contains? #{\v \V} (nth p 0))) :parameter-from-args + (and (= (.-length p) 1) (= \# (nth p 0))) :remaining-arg-count + (and (= (.-length p) 2) (= \' (nth p 0))) (nth p 1) + true (js/parseInt p)) + offset]) + +(def ^{:private true} + flag-defs {\: :colon, \@ :at}) + +(defn- extract-flags [s offset] + (consume + (fn [[s offset flags]] + (if (empty? s) + [nil [s offset flags]] + (let [flag (get flag-defs (first s))] + (if flag + (if (contains? flags flag) + (format-error + (str "Flag \"" (first s) "\" appears more than once in a directive") + offset) + [true [(subs s 1) (inc offset) (assoc flags flag [true offset])]]) + [nil [s offset flags]])))) + [s offset {}])) + +(defn- check-flags [def flags] + (let [allowed (:flags def)] + (if (and (not (:at allowed)) (:at flags)) + (format-error (str "\"@\" is an illegal flag for format directive \"" (:directive def) "\"") + (nth (:at flags) 1))) + (if (and (not (:colon allowed)) (:colon flags)) + (format-error (str "\":\" is an illegal flag for format directive \"" (:directive def) "\"") + (nth (:colon flags) 1))) + (if (and (not (:both allowed)) (:at flags) (:colon flags)) + (format-error (str "Cannot combine \"@\" and \":\" flags for format directive \"" + (:directive def) "\"") + (min (nth (:colon flags) 1) (nth (:at flags) 1)))))) + +(defn- map-params + "Takes a directive definition and the list of actual parameters and +a map of flags and returns a map of the parameters and flags with defaults +filled in. We check to make sure that there are the right types and number +of parameters as well." + [def params flags offset] + (check-flags def flags) + (if (> (count params) (count (:params def))) + (format-error + (cl-format + nil + "Too many parameters for directive \"~C\": ~D~:* ~[were~;was~:;were~] specified but only ~D~:* ~[are~;is~:;are~] allowed" + (:directive def) (count params) (count (:params def))) + (second (first params)))) + (doall + (map #(let [val (first %1)] + (if (not (or (nil? val) (contains? special-params val) + (= (second (second %2)) (type val)))) + (format-error (str "Parameter " (name (first %2)) + " has bad type in directive \"" (:directive def) "\": " + (type val)) + (second %1))) ) + params (:params def))) + + (merge ; create the result map + (into (array-map) ; start with the default values, make sure the order is right + (reverse (for [[name [default]] (:params def)] [name [default offset]]))) + (reduce #(apply assoc %1 %2) {} (filter #(first (nth % 1)) (zipmap (keys (:params def)) params))) ; add the specified parameters, filtering out nils + flags)); and finally add the flags + +(defn- compile-directive [s offset] + (let [[raw-params [rest offset]] (extract-params s offset) + [_ [rest offset flags]] (extract-flags rest offset) + directive (first rest) + def (get directive-table (string/upper-case directive)) + params (if def (map-params def (map translate-param raw-params) flags offset))] + (if (not directive) + (format-error "Format string ended in the middle of a directive" offset)) + (if (not def) + (format-error (str "Directive \"" directive "\" is undefined") offset)) + [(compiled-directive. ((:generator-fn def) params offset) def params offset) + (let [remainder (subs rest 1) + offset (inc offset) + trim? (and (= \newline (:directive def)) + (not (:colon params))) + trim-count (if trim? (prefix-count remainder [\space \tab]) 0) + remainder (subs remainder trim-count) + offset (+ offset trim-count)] + [remainder offset])])) + +(defn- compile-raw-string [s offset] + (compiled-directive. (fn [_ a _] (print s) a) nil {:string s} offset)) + +(defn- right-bracket [this] (:right (:bracket-info (:def this)))) + +(defn- separator? [this] (:separator (:bracket-info (:def this)))) + +(defn- else-separator? [this] + (and (:separator (:bracket-info (:def this))) + (:colon (:params this)))) + +(declare collect-clauses) + +(defn- process-bracket [this remainder] + (let [[subex remainder] (collect-clauses (:bracket-info (:def this)) + (:offset this) remainder)] + [(compiled-directive. + (:func this) (:def this) + (merge (:params this) (tuple-map subex (:offset this))) + (:offset this)) + remainder])) + +(defn- process-clause [bracket-info offset remainder] + (consume + (fn [remainder] + (if (empty? remainder) + (format-error "No closing bracket found." offset) + (let [this (first remainder) + remainder (next remainder)] + (cond + (right-bracket this) + (process-bracket this remainder) + + (= (:right bracket-info) (:directive (:def this))) + [ nil [:right-bracket (:params this) nil remainder]] + + (else-separator? this) + [nil [:else nil (:params this) remainder]] + + (separator? this) + [nil [:separator nil nil remainder]] ;; TODO: check to make sure that there are no params on ~; + + true + [this remainder])))) + remainder)) + +(defn- collect-clauses [bracket-info offset remainder] + (second + (consume + (fn [[clause-map saw-else remainder]] + (let [[clause [type right-params else-params remainder]] + (process-clause bracket-info offset remainder)] + (cond + (= type :right-bracket) + [nil [(merge-with concat clause-map + {(if saw-else :else :clauses) [clause] + :right-params right-params}) + remainder]] + + (= type :else) + (cond + (:else clause-map) + (format-error "Two else clauses (\"~:;\") inside bracket construction." offset) + + (not (:else bracket-info)) + (format-error "An else clause (\"~:;\") is in a bracket type that doesn't support it." + offset) + + (and (= :first (:else bracket-info)) (seq (:clauses clause-map))) + (format-error + "The else clause (\"~:;\") is only allowed in the first position for this directive." + offset) + + true ; if the ~:; is in the last position, the else clause + ; is next, this was a regular clause + (if (= :first (:else bracket-info)) + [true [(merge-with concat clause-map {:else [clause] :else-params else-params}) + false remainder]] + [true [(merge-with concat clause-map {:clauses [clause]}) + true remainder]])) + + (= type :separator) + (cond + saw-else + (format-error "A plain clause (with \"~;\") follows an else clause (\"~:;\") inside bracket construction." offset) + + (not (:allows-separator bracket-info)) + (format-error "A separator (\"~;\") is in a bracket type that doesn't support it." + offset) + + true + [true [(merge-with concat clause-map {:clauses [clause]}) + false remainder]])))) + [{:clauses []} false remainder]))) + +(defn- process-nesting + "Take a linearly compiled format and process the bracket directives to give it + the appropriate tree structure" + [format] + (first + (consume + (fn [remainder] + (let [this (first remainder) + remainder (next remainder) + bracket (:bracket-info (:def this))] + (if (:right bracket) + (process-bracket this remainder) + [this remainder]))) + format))) + +(defn- compile-format + "Compiles format-str into a compiled format which can be used as an argument +to cl-format just like a plain format string. Use this function for improved +performance when you're using the same format string repeatedly" + [format-str] + (binding [*format-str* format-str] + (process-nesting + (first + (consume + (fn [[s offset]] + (if (empty? s) + [nil s] + (let [tilde (.indexOf s \~)] + (cond + (neg? tilde) [(compile-raw-string s offset) ["" (+ offset (.-length s))]] + (zero? tilde) (compile-directive (subs s 1) (inc offset)) + true + [(compile-raw-string (subs s 0 tilde) offset) [(subs s tilde) (+ tilde offset)]])))) + [format-str 0]))))) + +(defn- needs-pretty + "determine whether a given compiled format has any directives that depend on the +column number or pretty printing" + [format] + (loop [format format] + (if (empty? format) + false + (if (or (:pretty (:flags (:def (first format)))) + (some needs-pretty (first (:clauses (:params (first format))))) + (some needs-pretty (first (:else (:params (first format)))))) + true + (recur (next format)))))) + +;;NB We depart from the original api. In clj, if execute-format is called multiple times with the same stream or +;; called on *out*, the results are different than if the same calls are made with different streams or printing +;; to a string. The reason is that mutating the underlying stream changes the result by changing spacing. +;; +;; clj: +;; * stream => "1 2 3" +;; * true (prints to *out*) => "1 2 3" +;; * nil (prints to string) => "1 2 3" +;; cljs: +;; * stream => "1 2 3" +;; * true (prints via *print-fn*) => "1 2 3" +;; * nil (prints to string) => "1 2 3" +(defn- execute-format + "Executes the format with the arguments." + {:skip-wiki true} + ([stream format args] + (let [sb (StringBuffer.) + real-stream (if (or (not stream) (true? stream)) + (StringBufferWriter. sb) + stream) + wrapped-stream (if (and (needs-pretty format) + (not (pretty-writer? real-stream))) + (get-pretty-writer real-stream) + real-stream)] + (binding [*out* wrapped-stream] + (try + (execute-format format args) + (finally + (if-not (identical? real-stream wrapped-stream) + (-flush wrapped-stream)))) + (cond + (not stream) (str sb) + (true? stream) (*print-fn* (str sb)) + :else nil)))) + ([format args] + (map-passing-context + (fn [element context] + (if (abort? context) + [nil context] + (let [[params args] (realize-parameter-list + (:params element) context) + [params offsets] (unzip-map params) + params (assoc params :base-args args)] + [nil (apply (:func element) [params args offsets])]))) + args + format) + nil)) + +;;; This is a bad idea, but it prevents us from leaking private symbols +;;; This should all be replaced by really compiled formats anyway. +(def ^{:private true} cached-compile (memoize compile-format)) + +;;====================================================================== +;; dispatch.clj +;;====================================================================== + +(defn- use-method + "Installs a function as a new method of multimethod associated with dispatch-value. " + [multifn dispatch-val func] + (-add-method multifn dispatch-val func)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Implementations of specific dispatch table entries +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Handle forms that can be "back-translated" to reader macros +;;; Not all reader macros can be dealt with this way or at all. +;;; Macros that we can't deal with at all are: +;;; ; - The comment character is absorbed by the reader and never is part of the form +;;; ` - Is fully processed at read time into a lisp expression (which will contain concats +;;; and regular quotes). +;;; ~@ - Also fully eaten by the processing of ` and can't be used outside. +;;; , - is whitespace and is lost (like all other whitespace). Formats can generate commas +;;; where they deem them useful to help readability. +;;; ^ - Adding metadata completely disappears at read time and the data appears to be +;;; completely lost. +;;; +;;; Most other syntax stuff is dealt with directly by the formats (like (), [], {}, and #{}) +;;; or directly by printing the objects using Clojure's built-in print functions (like +;;; :keyword, \char, or ""). The notable exception is #() which is special-cased. + +(def ^{:private true} reader-macros + {'quote "'" + 'var "#'" + 'clojure.core/deref "@", + 'clojure.core/unquote "~" + 'cljs.core/deref "@", + 'cljs.core/unquote "~"}) + +(defn- pprint-reader-macro [alis] + (let [macro-char (reader-macros (first alis))] + (when (and macro-char (= 2 (count alis))) + (-write *out* macro-char) + (write-out (second alis)) + true))) + +;;====================================================================== +;; Dispatch for the basic data types when interpreted +;; as data (as opposed to code). +;;====================================================================== + +;;; TODO: inline these formatter statements into funcs so that we +;;; are a little easier on the stack. (Or, do "real" compilation, a +;;; la Common Lisp) + +;;; (def pprint-simple-list (formatter-out "~:<~@{~w~^ ~_~}~:>")) +(defn- pprint-simple-list [alis] + (pprint-logical-block :prefix "(" :suffix ")" + (print-length-loop [alis (seq alis)] + (when alis + (write-out (first alis)) + (when (next alis) + (-write *out* " ") + (pprint-newline :linear) + (recur (next alis))))))) + +(defn- pprint-list [alis] + (if-not (pprint-reader-macro alis) + (pprint-simple-list alis))) + +;;; (def pprint-vector (formatter-out "~<[~;~@{~w~^ ~_~}~;]~:>")) +(defn- pprint-vector [avec] + (pprint-logical-block :prefix "[" :suffix "]" + (print-length-loop [aseq (seq avec)] + (when aseq + (write-out (first aseq)) + (when (next aseq) + (-write *out* " ") + (pprint-newline :linear) + (recur (next aseq))))))) + +(def ^{:private true} pprint-array (formatter-out "~<[~;~@{~w~^, ~:_~}~;]~:>")) + +;;; (def pprint-map (formatter-out "~<{~;~@{~<~w~^ ~_~w~:>~^, ~_~}~;}~:>")) +(defn- pprint-map [amap] + (pprint-logical-block :prefix "{" :suffix "}" + (print-length-loop [aseq (seq amap)] + (when aseq + ;;compiler gets confused with nested macro if it isn't namespaced + ;;it tries to use clojure.pprint/pprint-logical-block for some reason + (m/pprint-logical-block + (write-out (ffirst aseq)) + (-write *out* " ") + (pprint-newline :linear) + (set! *current-length* 0) ;always print both parts of the [k v] pair + (write-out (fnext (first aseq)))) + (when (next aseq) + (-write *out* ", ") + (pprint-newline :linear) + (recur (next aseq))))))) + +(defn- pprint-simple-default [obj] + ;;TODO: Update to handle arrays (?) and suppressing namespaces + (-write *out* (pr-str obj))) + +(def pprint-set (formatter-out "~<#{~;~@{~w~^ ~:_~}~;}~:>")) + +(def ^{:private true} +type-map {"core$future_call" "Future", + "core$promise" "Promise"}) + +(defn- map-ref-type + "Map ugly type names to something simpler" + [name] + (or (when-let [match (re-find #"^[^$]+\$[^$]+" name)] + (type-map match)) + name)) + +(defn- pprint-ideref [o] + (let [prefix (str "#<" (map-ref-type (.-name (type o))) + "@" (goog/getUid o) ": ")] + (pprint-logical-block :prefix prefix :suffix ">" + (pprint-indent :block (-> (count prefix) (- 2) -)) + (pprint-newline :linear) + (write-out + (if (and (satisfies? IPending o) (not (-realized? o))) + :not-delivered + :else @o))))) + +(def ^{:private true} pprint-pqueue (formatter-out "~<<-(~;~@{~w~^ ~_~}~;)-<~:>")) + +(defn- type-dispatcher [obj] + (cond + (instance? PersistentQueue obj) :queue + (satisfies? IDeref obj) :deref + (symbol? obj) :symbol + (seq? obj) :list + (map? obj) :map + (vector? obj) :vector + (set? obj) :set + (nil? obj) nil + :default :default)) + +(defmulti simple-dispatch + "The pretty print dispatch function for simple data structure format." + type-dispatcher) + +(use-method simple-dispatch :list pprint-list) +(use-method simple-dispatch :vector pprint-vector) +(use-method simple-dispatch :map pprint-map) +(use-method simple-dispatch :set pprint-set) +(use-method simple-dispatch nil #(-write *out* (pr-str nil))) +(use-method simple-dispatch :default pprint-simple-default) + +(set-pprint-dispatch simple-dispatch) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Dispatch for the code table +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare pprint-simple-code-list) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Format the namespace ("ns") macro. This is quite complicated because of all the +;;; different forms supported and because programmers can choose lists or vectors +;;; in various places. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- brackets + "Figure out which kind of brackets to use" + [form] + (if (vector? form) + ["[" "]"] + ["(" ")"])) + +(defn- pprint-ns-reference + "Pretty print a single reference (import, use, etc.) from a namespace decl" + [reference] + (if (sequential? reference) + (let [[start end] (brackets reference) + [keyw & args] reference] + (pprint-logical-block :prefix start :suffix end + ((formatter-out "~w~:i") keyw) + (loop [args args] + (when (seq args) + ((formatter-out " ")) + (let [arg (first args)] + (if (sequential? arg) + (let [[start end] (brackets arg)] + (pprint-logical-block :prefix start :suffix end + (if (and (= (count arg) 3) (keyword? (second arg))) + (let [[ns kw lis] arg] + ((formatter-out "~w ~w ") ns kw) + (if (sequential? lis) + ((formatter-out (if (vector? lis) + "~<[~;~@{~w~^ ~:_~}~;]~:>" + "~<(~;~@{~w~^ ~:_~}~;)~:>")) + lis) + (write-out lis))) + (apply (formatter-out "~w ~:i~@{~w~^ ~:_~}") arg))) + (when (next args) + ((formatter-out "~_")))) + (do + (write-out arg) + (when (next args) + ((formatter-out "~:_")))))) + (recur (next args)))))) + (write-out reference))) + +(defn- pprint-ns + "The pretty print dispatch chunk for the ns macro" + [alis] + (if (next alis) + (let [[ns-sym ns-name & stuff] alis + [doc-str stuff] (if (string? (first stuff)) + [(first stuff) (next stuff)] + [nil stuff]) + [attr-map references] (if (map? (first stuff)) + [(first stuff) (next stuff)] + [nil stuff])] + (pprint-logical-block :prefix "(" :suffix ")" + ((formatter-out "~w ~1I~@_~w") ns-sym ns-name) + (when (or doc-str attr-map (seq references)) + ((formatter-out "~@:_"))) + (when doc-str + (cl-format true "\"~a\"~:[~;~:@_~]" doc-str (or attr-map (seq references)))) + (when attr-map + ((formatter-out "~w~:[~;~:@_~]") attr-map (seq references))) + (loop [references references] + (pprint-ns-reference (first references)) + (when-let [references (next references)] + (pprint-newline :linear) + (recur references))))) + (write-out alis))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Format something that looks like a simple def (sans metadata, since the reader +;;; won't give it to us now). +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^{:private true} pprint-hold-first (formatter-out "~:<~w~^ ~@_~w~^ ~_~@{~w~^ ~_~}~:>")) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Format something that looks like a defn or defmacro +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Format the params and body of a defn with a single arity +(defn- single-defn [alis has-doc-str?] + (if (seq alis) + (do + (if has-doc-str? + ((formatter-out " ~_")) + ((formatter-out " ~@_"))) + ((formatter-out "~{~w~^ ~_~}") alis)))) + +;;; Format the param and body sublists of a defn with multiple arities +(defn- multi-defn [alis has-doc-str?] + (if (seq alis) + ((formatter-out " ~_~{~w~^ ~_~}") alis))) + +;;; TODO: figure out how to support capturing metadata in defns (we might need a +;;; special reader) +(defn- pprint-defn [alis] + (if (next alis) + (let [[defn-sym defn-name & stuff] alis + [doc-str stuff] (if (string? (first stuff)) + [(first stuff) (next stuff)] + [nil stuff]) + [attr-map stuff] (if (map? (first stuff)) + [(first stuff) (next stuff)] + [nil stuff])] + (pprint-logical-block :prefix "(" :suffix ")" + ((formatter-out "~w ~1I~@_~w") defn-sym defn-name) + (if doc-str + ((formatter-out " ~_~w") doc-str)) + (if attr-map + ((formatter-out " ~_~w") attr-map)) + ;; Note: the multi-defn case will work OK for malformed defns too + (cond + (vector? (first stuff)) (single-defn stuff (or doc-str attr-map)) + :else (multi-defn stuff (or doc-str attr-map))))) + (pprint-simple-code-list alis))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Format something with a binding form +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- pprint-binding-form [binding-vec] + (pprint-logical-block :prefix "[" :suffix "]" + (print-length-loop [binding binding-vec] + (when (seq binding) + (pprint-logical-block binding + (write-out (first binding)) + (when (next binding) + (-write *out* " ") + (pprint-newline :miser) + (write-out (second binding)))) + (when (next (rest binding)) + (-write *out* " ") + (pprint-newline :linear) + (recur (next (rest binding)))))))) + +(defn- pprint-let [alis] + (let [base-sym (first alis)] + (pprint-logical-block :prefix "(" :suffix ")" + (if (and (next alis) (vector? (second alis))) + (do + ((formatter-out "~w ~1I~@_") base-sym) + (pprint-binding-form (second alis)) + ((formatter-out " ~_~{~w~^ ~_~}") (next (rest alis)))) + (pprint-simple-code-list alis))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Format something that looks like "if" +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^{:private true} pprint-if (formatter-out "~:<~1I~w~^ ~@_~w~@{ ~_~w~}~:>")) + +(defn- pprint-cond [alis] + (pprint-logical-block :prefix "(" :suffix ")" + (pprint-indent :block 1) + (write-out (first alis)) + (when (next alis) + (-write *out* " ") + (pprint-newline :linear) + (print-length-loop [alis (next alis)] + (when alis + (pprint-logical-block alis + (write-out (first alis)) + (when (next alis) + (-write *out* " ") + (pprint-newline :miser) + (write-out (second alis)))) + (when (next (rest alis)) + (-write *out* " ") + (pprint-newline :linear) + (recur (next (rest alis))))))))) + +(defn- pprint-condp [alis] + (if (> (count alis) 3) + (pprint-logical-block :prefix "(" :suffix ")" + (pprint-indent :block 1) + (apply (formatter-out "~w ~@_~w ~@_~w ~_") alis) + (print-length-loop [alis (seq (drop 3 alis))] + (when alis + (pprint-logical-block alis + (write-out (first alis)) + (when (next alis) + (-write *out* " ") + (pprint-newline :miser) + (write-out (second alis)))) + (when (next (rest alis)) + (-write *out* " ") + (pprint-newline :linear) + (recur (next (rest alis))))))) + (pprint-simple-code-list alis))) + +;;; The map of symbols that are defined in an enclosing #() anonymous function +(def ^:dynamic ^{:private true} *symbol-map* {}) + +(defn- pprint-anon-func [alis] + (let [args (second alis) + nlis (first (rest (rest alis)))] + (if (vector? args) + (binding [*symbol-map* (if (= 1 (count args)) + {(first args) "%"} + (into {} + (map + #(vector %1 (str \% %2)) + args + (range 1 (inc (count args))))))] + ((formatter-out "~<#(~;~@{~w~^ ~_~}~;)~:>") nlis)) + (pprint-simple-code-list alis)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; The master definitions for formatting lists in code (that is, (fn args...) or +;;; special forms). +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; This is the equivalent of (formatter-out "~:<~1I~@{~w~^ ~_~}~:>"), but is +;;; easier on the stack. + +(defn- pprint-simple-code-list [alis] + (pprint-logical-block :prefix "(" :suffix ")" + (pprint-indent :block 1) + (print-length-loop [alis (seq alis)] + (when alis + (write-out (first alis)) + (when (next alis) + (-write *out* " ") + (pprint-newline :linear) + (recur (next alis))))))) + +;;; Take a map with symbols as keys and add versions with no namespace. +;;; That is, if ns/sym->val is in the map, add sym->val to the result. +(defn- two-forms [amap] + (into {} + (mapcat + identity + (for [x amap] + [x [(symbol (name (first x))) (second x)]])))) + +(defn- add-core-ns [amap] + (let [core "clojure.core"] + (into {} + (map #(let [[s f] %] + (if (not (or (namespace s) (special-symbol? s))) + [(symbol core (name s)) f] + %)) + amap)))) + +(def ^:dynamic ^{:private true} *code-table* + (two-forms + (add-core-ns + {'def pprint-hold-first, 'defonce pprint-hold-first, + 'defn pprint-defn, 'defn- pprint-defn, 'defmacro pprint-defn, 'fn pprint-defn, + 'let pprint-let, 'loop pprint-let, 'binding pprint-let, + 'with-local-vars pprint-let, 'with-open pprint-let, 'when-let pprint-let, + 'if-let pprint-let, 'doseq pprint-let, 'dotimes pprint-let, + 'when-first pprint-let, + 'if pprint-if, 'if-not pprint-if, 'when pprint-if, 'when-not pprint-if, + 'cond pprint-cond, 'condp pprint-condp, + 'fn* pprint-anon-func, + '. pprint-hold-first, '.. pprint-hold-first, '-> pprint-hold-first, + 'locking pprint-hold-first, 'struct pprint-hold-first, + 'struct-map pprint-hold-first, 'ns pprint-ns + }))) + +(defn- pprint-code-list [alis] + (if-not (pprint-reader-macro alis) + (if-let [special-form (*code-table* (first alis))] + (special-form alis) + (pprint-simple-code-list alis)))) + +(defn- pprint-code-symbol [sym] + (if-let [arg-num (sym *symbol-map*)] + (print arg-num) + (if *print-suppress-namespaces* + (print (name sym)) + (pr sym)))) + +(defmulti + code-dispatch + "The pretty print dispatch function for pretty printing Clojure code." + {:added "1.2" :arglists '[[object]]} + type-dispatcher) + +(use-method code-dispatch :list pprint-code-list) +(use-method code-dispatch :symbol pprint-code-symbol) + +;; The following are all exact copies of simple-dispatch +(use-method code-dispatch :vector pprint-vector) +(use-method code-dispatch :map pprint-map) +(use-method code-dispatch :set pprint-set) +(use-method code-dispatch :queue pprint-pqueue) +(use-method code-dispatch :deref pprint-ideref) +(use-method code-dispatch nil pr) +(use-method code-dispatch :default pprint-simple-default) + +(set-pprint-dispatch simple-dispatch) + +;;; For testing +(comment + + (with-pprint-dispatch code-dispatch + (pprint + '(defn cl-format + "An implementation of a Common Lisp compatible format function" + [stream format-in & args] + (let [compiled-format (if (string? format-in) (compile-format format-in) format-in) + navigator (init-navigator args)] + (execute-format stream compiled-format navigator))))) + + (with-pprint-dispatch code-dispatch + (pprint + '(defn cl-format + [stream format-in & args] + (let [compiled-format (if (string? format-in) (compile-format format-in) format-in) + navigator (init-navigator args)] + (execute-format stream compiled-format navigator))))) + + (with-pprint-dispatch code-dispatch + (pprint + '(defn- -write + ([this x] + (condp = (class x) + String + (let [s0 (write-initial-lines this x) + s (.replaceFirst s0 "\\s+$" "") + white-space (.substring s0 (count s)) + mode (getf :mode)] + (if (= mode :writing) + (dosync + (write-white-space this) + (.col_write this s) + (setf :trailing-white-space white-space)) + (add-to-buffer this (make-buffer-blob s white-space)))) + + Integer + (let [c ^Character x] + (if (= (getf :mode) :writing) + (do + (write-white-space this) + (.col_write this x)) + (if (= c (int \newline)) + (write-initial-lines this "\n") + (add-to-buffer this (make-buffer-blob (str (char c)) nil)))))))))) + + (with-pprint-dispatch code-dispatch + (pprint + '(defn pprint-defn [writer alis] + (if (next alis) + (let [[defn-sym defn-name & stuff] alis + [doc-str stuff] (if (string? (first stuff)) + [(first stuff) (next stuff)] + [nil stuff]) + [attr-map stuff] (if (map? (first stuff)) + [(first stuff) (next stuff)] + [nil stuff])] + (pprint-logical-block writer :prefix "(" :suffix ")" + (cl-format true "~w ~1I~@_~w" defn-sym defn-name) + (if doc-str + (cl-format true " ~_~w" doc-str)) + (if attr-map + (cl-format true " ~_~w" attr-map)) + ;; Note: the multi-defn case will work OK for malformed defns too + (cond + (vector? (first stuff)) (single-defn stuff (or doc-str attr-map)) + :else (multi-defn stuff (or doc-str attr-map))))) + (pprint-simple-code-list writer alis))))) + ) + +;;====================================================================== +;; print_table.clj +;;====================================================================== + +(defn- add-padding [width s] + (let [padding (max 0 (- width (count s)))] + (apply str (clojure.string/join (repeat padding \space)) s))) + +(defn print-table + "Prints a collection of maps in a textual table. Prints table headings + ks, and then a line of output for each row, corresponding to the keys + in ks. If ks are not specified, use the keys of the first item in rows." + {:added "1.3"} + ([ks rows] + (binding [*print-newline*] + (when (seq rows) + (let [widths (map + (fn [k] + (apply max (count (str k)) (map #(count (str (get % k))) rows))) + ks) + spacers (map #(apply str (repeat % "-")) widths) + fmt-row (fn [leader divider trailer row] + (str leader + (apply str (interpose divider + (for [[col width] (map vector (map #(get row %) ks) widths)] + (add-padding width (str col))))) + trailer))] + (cljs.core/println) + (cljs.core/println (fmt-row "| " " | " " |" (zipmap ks ks))) + (cljs.core/println (fmt-row "|-" "-+-" "-|" (zipmap ks spacers))) + (doseq [row rows] + (cljs.core/println (fmt-row "| " " | " " |" row))))))) + ([rows] (print-table (keys (first rows)) rows))) diff --git a/test/cljs/cljs/pprint_test.clj b/test/cljs/cljs/pprint_test.clj new file mode 100644 index 000000000..576f9f231 --- /dev/null +++ b/test/cljs/cljs/pprint_test.clj @@ -0,0 +1,23 @@ +(ns cljs.pprint-test + (:require [cljs.test :refer [deftest is]])) + +(defmacro simple-tests [name & test-pairs] + `(deftest ~name + ~@(for [[x y] (partition 2 test-pairs)] + `(cond + (= js/RegExp (type ~y)) (is (.exec ~y ~x)) + (= js/String (type ~y)) (is (= ~x ~y)) + :else (is (= ~x ~y)))))) + +(defmacro code-block + "Read a string then print it with code-dispatch and succeed if it comes out the same" + [test-name & blocks] + `(simple-tests ~test-name + ~@(apply concat + (for [block blocks] + `[(clojure.string/split-lines + (with-out-str + (cljs.pprint/with-pprint-dispatch cljs.pprint/code-dispatch + (cljs.pprint/pprint (cljs.reader/read-string ~block))))) + (clojure.string/split-lines ~block)])))) + diff --git a/test/cljs/cljs/pprint_test.cljs b/test/cljs/cljs/pprint_test.cljs new file mode 100644 index 000000000..10e29d34f --- /dev/null +++ b/test/cljs/cljs/pprint_test.cljs @@ -0,0 +1,1071 @@ +(ns cljs.pprint-test + (:refer-clojure :exclude [prn]) + (:require-macros + [cljs.pprint-test :refer [simple-tests code-block]]) + (:require + [cljs.test :as t :refer-macros [deftest is]] + [cljs.pprint :refer [pprint cl-format *out* get-pretty-writer prn print-table + *print-pprint-dispatch* simple-dispatch + *print-right-margin* *print-miser-width* + write code-dispatch] + :refer-macros [with-pprint-dispatch]] + [cljs.reader :as reader]) + + (:import [goog.string StringBuffer])) + +(def format cl-format) + +(simple-tests xp-fill-test + (binding [*print-pprint-dispatch* simple-dispatch + *print-right-margin* 38 + *print-miser-width* nil] + (cl-format nil "(let ~:<~@{~:<~w ~_~w~:>~^ ~:_~}~:>~_ ...)~%" + '((x 4) (*print-length* nil) (z 2) (list nil)))) + "(let ((x 4) (*print-length* nil)\n (z 2) (list nil))\n ...)\n" + + (binding [*print-pprint-dispatch* simple-dispatch + *print-right-margin* 22] + (cl-format nil "(let ~:<~@{~:<~w ~_~w~:>~^ ~:_~}~:>~_ ...)~%" + '((x 4) (*print-length* nil) (z 2) (list nil)))) + "(let ((x 4)\n (*print-length*\n nil)\n (z 2)\n (list nil))\n ...)\n" +) + +(simple-tests mandatory-fill-test + (cl-format nil + "
~%~~%
~%" + [ "hello" "gooodbye" ]) + "
+Usage: *hello*
+       *gooodbye*
+
+") + +(simple-tests prefix-suffix-test + (binding [*print-pprint-dispatch* simple-dispatch + *print-right-margin* 10, *print-miser-width* 10] + (cl-format nil "~<{~;LIST ~@_~W ~@_~W ~@_~W~;}~:>" '(first second third))) + "{LIST\n first\n second\n third}" +) + +(simple-tests pprint-test + (binding [*print-pprint-dispatch* simple-dispatch] + (write '(defn foo [x y] + (let [result (* x y)] + (if (> result 400) + (cl-format true "That number is too big") + (cl-format true "The result of ~d x ~d is ~d" x y result)))) + :stream nil)) + "(defn + foo + [x y] + (let + [result (* x y)] + (if + (> result 400) + (cl-format true \"That number is too big\") + (cl-format true \"The result of ~d x ~d is ~d\" x y result))))" + + (with-pprint-dispatch code-dispatch ;;fail + (write '(defn foo [x y] + (let [result (* x y)] + (if (> result 400) + (cl-format true "That number is too big") + (cl-format true "The result of ~d x ~d is ~d" x y result)))) + :stream nil)) + "(defn foo [x y] + (let [result (* x y)] + (if (> result 400) + (cl-format true \"That number is too big\") + (cl-format true \"The result of ~d x ~d is ~d\" x y result))))" + + (binding [*print-pprint-dispatch* simple-dispatch + *print-right-margin* 15] + (write '(fn (cons (car x) (cdr y))) :stream nil)) + "(fn\n (cons\n (car x)\n (cdr y)))" + + ;;TODO Fails because of spacing; may be due to the mutating clj difference from cljs + ;;or could simply be a bug in our code + (with-pprint-dispatch code-dispatch + (binding [*print-right-margin* 52] + (write + '(add-to-buffer this (make-buffer-blob (str (char c)) nil)) + :stream nil))) + "(add-to-buffer\n this \n (make-buffer-blob (str (char c)) nil))" + #_"(add-to-buffer\n this\n (make-buffer-blob (str (char c)) nil))" + ) + +(simple-tests pprint-reader-macro-test + ;;I'm not sure this will work without significant work on cljs. Short story, cljs + ;;reader only takes valid EDN, so #(* % %) won't work. + ;;see http://stackoverflow.com/a/25712675/546321 for more details + #_(with-pprint-dispatch code-dispatch + (write (reader/read-string "(map #(first %) [[1 2 3] [4 5 6] [7]])") + :stream nil)) + #_"(map #(first %) [[1 2 3] [4 5 6] [7]])" + + ;;TODO Not sure what to do about this test due to the reader handling of `@` + (with-pprint-dispatch code-dispatch + (write (reader/read-string "@@(ref (ref 1))") + :stream nil)) + "(deref (deref (ref (ref 1))))" + #_"@@(ref (ref 1))" + + (with-pprint-dispatch code-dispatch + (write (reader/read-string "'foo") + :stream nil)) + "'foo" + ) + +(simple-tests xp-miser-test + (binding [*print-pprint-dispatch* simple-dispatch + *print-right-margin* 10, *print-miser-width* 9] + (cl-format nil "~:" '(first second third))) + "(LIST\n first\n second\n third)" + + (binding [*print-pprint-dispatch* simple-dispatch + *print-right-margin* 10, *print-miser-width* 8] + (cl-format nil "~:" '(first second third))) + "(LIST first second third)" +) + +(code-block code-block-tests + "(defn cl-format + \"An implementation of a Common Lisp compatible format function\" + [stream format-in & args] + (let [compiled-format (if (string? format-in) + (compile-format format-in) + format-in) + navigator (init-navigator args)] + (execute-format stream compiled-format navigator)))" + + "(defn pprint-defn [writer alis] + (if (next alis) + (let [[defn-sym defn-name & stuff] alis + [doc-str stuff] (if (string? (first stuff)) + [(first stuff) (next stuff)] + [nil stuff]) + [attr-map stuff] (if (map? (first stuff)) + [(first stuff) (next stuff)] + [nil stuff])] + (pprint-logical-block + writer + :prefix + \"(\" + :suffix + \")\" + (cl-format true \"~w ~1I~@_~w\" defn-sym defn-name) + (if doc-str (cl-format true \" ~_~w\" doc-str)) + (if attr-map (cl-format true \" ~_~w\" attr-map)) + (cond + (vector? (first stuff)) (single-defn + stuff + (or doc-str attr-map)) + :else (multi-defn stuff (or doc-str attr-map))))) + (pprint-simple-code-list writer alis)))" +) + +(simple-tests print-length-tests + (binding [*print-length* 1] (with-out-str (pprint '(a b c d e f)))) + "(a ...)\n" + (binding [*print-length* 2] (with-out-str (pprint '(a b c d e f)))) + "(a b ...)\n" + (binding [*print-length* 6] (with-out-str (pprint '(a b c d e f)))) + "(a b c d e f)\n" + (binding [*print-length* 8] (with-out-str (pprint '(a b c d e f)))) + "(a b c d e f)\n" + + (binding [*print-length* 1] (with-out-str (pprint [1 2 3 4 5 6]))) + "[1 ...]\n" + (binding [*print-length* 2] (with-out-str (pprint [1 2 3 4 5 6]))) + "[1 2 ...]\n" + (binding [*print-length* 6] (with-out-str (pprint [1 2 3 4 5 6]))) + "[1 2 3 4 5 6]\n" + (binding [*print-length* 8] (with-out-str (pprint [1 2 3 4 5 6]))) + "[1 2 3 4 5 6]\n" + + (binding [*print-length* 1] (with-out-str (pprint (sorted-set 1 2 3 4 5 6)))) + "#{1 ...}\n" + (binding [*print-length* 2] (with-out-str (pprint (sorted-set 1 2 3 4 5 6)))) + "#{1 2 ...}\n" + (binding [*print-length* 6] (with-out-str (pprint (sorted-set 1 2 3 4 5 6)))) + "#{1 2 3 4 5 6}\n" + (binding [*print-length* 8] (with-out-str (pprint (sorted-set 1 2 3 4 5 6)))) + "#{1 2 3 4 5 6}\n" + + (binding [*print-length* 1] (with-out-str (pprint (sorted-map 1 2, 3 4, 5 6, 7 8, 9 10, 11 12)))) + "{1 2, ...}\n" + (binding [*print-length* 2] (with-out-str (pprint (sorted-map 1 2, 3 4, 5 6, 7 8, 9 10, 11 12)))) + "{1 2, 3 4, ...}\n" + (binding [*print-length* 6] (with-out-str (pprint (sorted-map 1 2, 3 4, 5 6, 7 8, 9 10, 11 12)))) + "{1 2, 3 4, 5 6, 7 8, 9 10, 11 12}\n" + (binding [*print-length* 8] (with-out-str (pprint (sorted-map 1 2, 3 4, 5 6, 7 8, 9 10, 11 12)))) + "{1 2, 3 4, 5 6, 7 8, 9 10, 11 12}\n" + + ;;TODO Not sure if JS objs are handled the way we want; need to further investigate + (binding [*print-length* 1] (with-out-str (pprint (int-array [1 2 3 4 5 6])))) + "#js [1 ...]\n" + (binding [*print-length* 2] (with-out-str (pprint (int-array [1 2 3 4 5 6])))) + "#js [1 2 ...]\n" + (binding [*print-length* 6] (with-out-str (pprint (int-array [1 2 3 4 5 6])))) + "#js [1 2 3 4 5 6]\n" + (binding [*print-length* 8] (with-out-str (pprint (int-array [1 2 3 4 5 6])))) + "#js [1 2 3 4 5 6]\n" + + ) + +(simple-tests print-margin-tests + (binding [cljs.pprint/*print-right-margin* 20] + (with-out-str (pprint (sorted-map 1 (sorted-map 12345 123456), 3 (sorted-map 4 5, 6 7))))) + "{1 {12345 123456},\n 3 {4 5, 6 7}}\n" + + (binding [cljs.pprint/*print-right-margin* 8] + (with-out-str (pprint (sorted-set 123 456 789)))) + "#{123\n 456\n 789}\n" +) + +;;---------------------------------------------------------------------------- +;; clj-format tests +;;---------------------------------------------------------------------------- + +(simple-tests d-tests + (cl-format nil "~D" 0) "0" + (cl-format nil "~D" 2e6) "2000000" + (cl-format nil "~D" 2000000) "2000000" + (cl-format nil "~:D" 2000000) "2,000,000" + ;(cl-format nil "~D" 1/2) "1/2" ;no ratio + (cl-format nil "~D" 'fred) "fred" +) + +(simple-tests cardinal-tests + (cl-format nil "~R" 0) "zero" + (cl-format nil "~R" 4) "four" + (cl-format nil "~R" 15) "fifteen" + (cl-format nil "~R" -15) "minus fifteen" + (cl-format nil "~R" 25) "twenty-five" + (cl-format nil "~R" 20) "twenty" + (cl-format nil "~R" 200) "two hundred" + (cl-format nil "~R" 203) "two hundred three" + + (cl-format nil "~R" 44879032) + "forty-four million, eight hundred seventy-nine thousand, thirty-two" + + (cl-format nil "~R" -44879032) + "minus forty-four million, eight hundred seventy-nine thousand, thirty-two" + + (cl-format nil "~R = ~:*~:D" 44000032) + "forty-four million, thirty-two = 44,000,032" + + ;;js/Number.MAX_SAFE_INTEGER - js starts munging larger values + (cl-format nil "~R = ~:*~:D" 9007199254740991) + "nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety-one = 9,007,199,254,740,991" + + ;;js/Number.MIN_SAFE_INTEGER - js starts munging smaller values + (cl-format nil "~R = ~:*~:D" -9007199254740991) + "minus nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety-one = -9,007,199,254,740,991" + + (cl-format nil "~R = ~:*~:D" 2e6) + "two million = 2,000,000" + + (cl-format nil "~R = ~:*~:D" 200000200000) + "two hundred billion, two hundred thousand = 200,000,200,000" +) + +(simple-tests ordinal-tests + (cl-format nil "~:R" 0) "zeroth" + (cl-format nil "~:R" 4) "fourth" + (cl-format nil "~:R" 15) "fifteenth" + (cl-format nil "~:R" -15) "minus fifteenth" + (cl-format nil "~:R" 25) "twenty-fifth" + (cl-format nil "~:R" 20) "twentieth" + (cl-format nil "~:R" 200) "two hundredth" + (cl-format nil "~:R" 203) "two hundred third" + + (cl-format nil "~:R" 44879032) + "forty-four million, eight hundred seventy-nine thousand, thirty-second" + + (cl-format nil "~:R" -44879032) + "minus forty-four million, eight hundred seventy-nine thousand, thirty-second" + + (cl-format nil "~:R = ~:*~:D" 44000032) + "forty-four million, thirty-second = 44,000,032" + + ;;js/Number.MAX_SAFE_INTEGER - js starts munging larger values + (cl-format nil "~:R = ~:*~:D" 9007199254740991) + "nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety-first = 9,007,199,254,740,991" + + ;;js/Number.MIN_SAFE_INTEGER - js starts munging smaller values + (cl-format nil "~:R = ~:*~:D" -9007199254740991) + "minus nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety-first = -9,007,199,254,740,991" + + (cl-format nil "~:R = ~:*~:D" 2e6) + "two millionth = 2,000,000" +) + +(simple-tests ordinal1-tests + (cl-format nil "~:R" 1) "first" + (cl-format nil "~:R" 11) "eleventh" + (cl-format nil "~:R" 21) "twenty-first" + (cl-format nil "~:R" 20) "twentieth" + (cl-format nil "~:R" 220) "two hundred twentieth" + (cl-format nil "~:R" 200) "two hundredth" + (cl-format nil "~:R" 999) "nine hundred ninety-ninth" +) + +(simple-tests roman-tests + (cl-format nil "~@R" 3) "III" + (cl-format nil "~@R" 4) "IV" + (cl-format nil "~@R" 9) "IX" + (cl-format nil "~@R" 29) "XXIX" + (cl-format nil "~@R" 429) "CDXXIX" + (cl-format nil "~@:R" 429) "CCCCXXVIIII" + (cl-format nil "~@:R" 3429) "MMMCCCCXXVIIII" + (cl-format nil "~@R" 3429) "MMMCDXXIX" + (cl-format nil "~@R" 3479) "MMMCDLXXIX" + (cl-format nil "~@R" 3409) "MMMCDIX" + (cl-format nil "~@R" 300) "CCC" + (cl-format nil "~@R ~D" 300 20) "CCC 20" + (cl-format nil "~@R" 5000) "5,000" + (cl-format nil "~@R ~D" 5000 20) "5,000 20" + (cl-format nil "~@R" "the quick") "the quick" +) + +(simple-tests c-tests + (cl-format nil "~{~c~^, ~}~%" "hello") "h, e, l, l, o\n" + (cl-format nil "~{~:c~^, ~}~%" "hello") "h, e, l, l, o\n" + (cl-format nil "~@C~%" \m) "\\m\n" + (cl-format nil "~@C~%" (char 222)) "\\Þ\n" + + ;;chars that are specially printed in cljs + (cl-format nil "~@C~%" (char 8)) "\\backspace\n" + (cl-format nil "~@C~%" (char 9)) "\\tab\n" + (cl-format nil "~@C~%" (char 10)) "\\newline\n" + (cl-format nil "~@C~%" (char 12)) "\\formfeed\n" + (cl-format nil "~@C~%" (char 13)) "\\return\n" + (cl-format nil "~@C~%" (char 34)) "\\\"\n" + (cl-format nil "~@C~%" (char 92)) "\\\\\n" + + (cl-format nil "~@C~%" (char 3)) "\\\n" +) + +(simple-tests e-tests + (cl-format nil "*~E*" 0.0) "*0.0E+0*" + (cl-format nil "*~6E*" 0.0) "*0.0E+0*" + (cl-format nil "*~6,0E*" 0.0) "* 0.E+0*" + (cl-format nil "*~7,2E*" 0.0) "*0.00E+0*" + (cl-format nil "*~5E*" 0.0) "*0.E+0*" + (cl-format nil "*~10,2,2,,'?E*" 2.8E120) "*??????????*" + (cl-format nil "*~10,2E*" 9.99999) "* 1.00E+1*" + (cl-format nil "*~10,2E*" 9.99999E99) "* 1.00E+100*" + (cl-format nil "*~10,2,2E*" 9.99999E99) "* 1.00E+100*" + (cl-format nil "*~10,2,2,,'?E*" 9.99999E99) "*??????????*" +) + +(simple-tests $-tests + (cl-format nil "~$" 22.3) "22.30" + (cl-format nil "~$" 22.375) "22.38" + (cl-format nil "~3,5$" 22.375) "00022.375" + (cl-format nil "~3,5,8$" 22.375) "00022.375" + (cl-format nil "~3,5,10$" 22.375) " 00022.375" + (cl-format nil "~3,5,14@$" 22.375) " +00022.375" + (cl-format nil "~3,5,14@$" 22.375) " +00022.375" + (cl-format nil "~3,5,14@:$" 22.375) "+ 00022.375" + (cl-format nil "~3,,14@:$" 0.375) "+ 0.375" + (cl-format nil "~1,1$" -12.0) "-12.0" + (cl-format nil "~1,1$" 12.0) "12.0" + (cl-format nil "~1,1$" 12.0) "12.0" + (cl-format nil "~1,1@$" 12.0) "+12.0" + (cl-format nil "~1,1,8,' @:$" 12.0) "+ 12.0" + (cl-format nil "~1,1,8,' @$" 12.0) " +12.0" + (cl-format nil "~1,1,8,' :$" 12.0) " 12.0" + (cl-format nil "~1,1,8,' $" 12.0) " 12.0" + (cl-format nil "~1,1,8,' @:$" -12.0) "- 12.0" + (cl-format nil "~1,1,8,' @$" -12.0) " -12.0" + (cl-format nil "~1,1,8,' :$" -12.0) "- 12.0" + (cl-format nil "~1,1,8,' $" -12.0) " -12.0" + (cl-format nil "~1,1$" 0.001) "0.0" + (cl-format nil "~2,1$" 0.001) "0.00" + (cl-format nil "~1,1,6$" 0.001) " 0.0" + (cl-format nil "~1,1,6$" 0.0015) " 0.0" + (cl-format nil "~2,1,6$" 0.005) " 0.01" + (cl-format nil "~2,1,6$" 0.01) " 0.01" + (cl-format nil "~$" 0.099) "0.10" + (cl-format nil "~1$" 0.099) "0.1" + (cl-format nil "~1$" 0.1) "0.1" + (cl-format nil "~1$" 0.99) "1.0" + (cl-format nil "~1$" -0.99) "-1.0" +) + +(simple-tests f-tests + (cl-format nil "~,1f" -12.0) "-12.0" + (cl-format nil "~,0f" 9.4) "9." + (cl-format nil "~,0f" 9.5) "10." + (cl-format nil "~,0f" -0.99) "-1." + (cl-format nil "~,1f" -0.99) "-1.0" + (cl-format nil "~,2f" -0.99) "-0.99" + (cl-format nil "~,3f" -0.99) "-0.990" + (cl-format nil "~,0f" 0.99) "1." + (cl-format nil "~,1f" 0.99) "1.0" + (cl-format nil "~,2f" 0.99) "0.99" + (cl-format nil "~,3f" 0.99) "0.990" + (cl-format nil "~,3f" -0.099) "-0.099" + (cl-format nil "~,4f" -0.099) "-0.0990" + (cl-format nil "~,5f" -0.099) "-0.09900" + (cl-format nil "~,3f" 0.099) "0.099" + (cl-format nil "~,4f" 0.099) "0.0990" + (cl-format nil "~,5f" 0.099) "0.09900" + (cl-format nil "~f" -1) "-1.0" + (cl-format nil "~2f" -1) "-1." + (cl-format nil "~3f" -1) "-1." + (cl-format nil "~4f" -1) "-1.0" + (cl-format nil "~8f" -1) " -1.0" + (cl-format nil "~2f" -0.0099) "-0." + (cl-format nil "~3f" -0.0099) "-0." + (cl-format nil "~4f" -0.0099) "-.01" + (cl-format nil "~5f" -0.0099) "-0.01" + (cl-format nil "~6f" -0.0099) "-.0099" + (cl-format nil "~1f" 0.0099) "0." + (cl-format nil "~2f" 0.0099) "0." + (cl-format nil "~3f" 0.0099) ".01" + (cl-format nil "~4f" 0.0099) "0.01" + (cl-format nil "~5f" 0.0099) ".0099" + (cl-format nil "~6f" 0.0099) "0.0099" + (cl-format nil "~1f" -0.099) "-.1" + (cl-format nil "~2f" -0.099) "-.1" + (cl-format nil "~3f" -0.099) "-.1" + (cl-format nil "~4f" -0.099) "-0.1" + (cl-format nil "~5f" -0.099) "-.099" + (cl-format nil "~6f" -0.099) "-0.099" + (cl-format nil "~1f" 0.099) ".1" + (cl-format nil "~2f" 0.099) ".1" + (cl-format nil "~3f" 0.099) "0.1" + (cl-format nil "~4f" 0.099) ".099" + (cl-format nil "~5f" 0.099) "0.099" + (cl-format nil "~1f" -0.99) "-1." + (cl-format nil "~2f" -0.99) "-1." + (cl-format nil "~3f" -0.99) "-1." + (cl-format nil "~4f" -0.99) "-.99" + (cl-format nil "~5f" -0.99) "-0.99" + (cl-format nil "~1f" 0.99) "1." + (cl-format nil "~2f" 0.99) "1." + (cl-format nil "~3f" 0.99) ".99" + (cl-format nil "~4f" 0.99) "0.99" + (cl-format nil "~1f" 111.11111) "111." + (cl-format nil "~4f" 111.11111) "111." + (cl-format nil "~5f" 111.11111) "111.1" + (cl-format nil "~1f" -111.11111) "-111." + (cl-format nil "~5f" -111.11111) "-111." + (cl-format nil "~6f" -111.11111) "-111.1" + (cl-format nil "~1f" 555.55555) "556." + (cl-format nil "~4f" 555.55555) "556." + (cl-format nil "~5f" 555.55555) "555.6" + (cl-format nil "~8f" 555.55555) "555.5556" + (cl-format nil "~1f" -555.55555) "-556." + (cl-format nil "~5f" -555.55555) "-556." + (cl-format nil "~6f" -555.55555) "-555.6" + (cl-format nil "~8f" -555.55555) "-555.556" + (cl-format nil "~1f" 999.999) "1000." + (cl-format nil "~5f" 999.999) "1000." + (cl-format nil "~6f" 999.999) "1000.0" + (cl-format nil "~7f" 999.999) "999.999" + (cl-format nil "~8f" 999.999) " 999.999" + (cl-format nil "~1f" -999.999) "-1000." + (cl-format nil "~6f" -999.999) "-1000." + (cl-format nil "~7f" -999.999) "-1000.0" + (cl-format nil "~8f" -999.999) "-999.999" + (cl-format nil "~5,2f" 111.11111) "111.11" + (cl-format nil "~3,1f" -0.0099) "-.0" + (cl-format nil "~6,4f" -0.0099) "-.0099" + (cl-format nil "~6,5f" -0.0099) "-.00990" + (cl-format nil "~6,6f" -0.0099) "-.009900" + (cl-format nil "~6,4f" 0.0099) "0.0099" + (cl-format nil "~6,5f" 0.0099) ".00990" + (cl-format nil "~6,6f" 0.0099) ".009900" + (cl-format nil "~2,1f" 0.0099) ".0" + (cl-format nil "~6,2f" -111.11111) "-111.11" + (cl-format nil "~6,3f" -111.11111) "-111.111" + (cl-format nil "~8,5f" -111.11111) "-111.11111" + (cl-format nil "~12,10f" 1.23456789014) "1.2345678901" + (cl-format nil "~12,10f" 1.23456789016) "1.2345678902" + (cl-format nil "~13,10f" -1.23456789014) "-1.2345678901" + (cl-format nil "~13,10f" -1.23456789016) "-1.2345678902" + (cl-format nil "~1,1f" 0.1) ".1" +) + +(simple-tests ampersand-tests + (cl-format nil "The quick brown ~a jumped over ~d lazy dogs" 'elephant 5) + "The quick brown elephant jumped over 5 lazy dogs" + (cl-format nil "The quick brown ~&~a jumped over ~d lazy dogs" 'elephant 5) + "The quick brown \nelephant jumped over 5 lazy dogs" + (cl-format nil "The quick brown ~&~a jumped\n~& over ~d lazy dogs" 'elephant 5) + "The quick brown \nelephant jumped\n over 5 lazy dogs" + (cl-format nil "~&The quick brown ~&~a jumped\n~& over ~d lazy dogs" 'elephant 5) + "The quick brown \nelephant jumped\n over 5 lazy dogs" + (cl-format nil "~3&The quick brown ~&~a jumped\n~& over ~d lazy dogs" 'elephant 5) + "\n\nThe quick brown \nelephant jumped\n over 5 lazy dogs" + (cl-format nil "~@{~&The quick brown ~a jumped over ~d lazy dogs~}" 'elephant 5 'fox 10) + "The quick brown elephant jumped over 5 lazy dogs\nThe quick brown fox jumped over 10 lazy dogs" + (cl-format nil "I ~[don't ~:;d~&o ~]have one~%" 0) "I don't have one\n" + (cl-format nil "I ~[don't ~:;d~&o ~]have one~%" 1) "I d\no have one\n" +) + +(simple-tests t-tests + (cl-format nil "~@{~&~A~8,4T~:*~A~}" + 'a 'aa 'aaa 'aaaa 'aaaaa 'aaaaaa 'aaaaaaa 'aaaaaaaa 'aaaaaaaaa 'aaaaaaaaaa) + "a a\naa aa\naaa aaa\naaaa aaaa\naaaaa aaaaa\naaaaaa aaaaaa\naaaaaaa aaaaaaa\naaaaaaaa aaaaaaaa\naaaaaaaaa aaaaaaaaa\naaaaaaaaaa aaaaaaaaaa" + (cl-format nil "~@{~&~A~,4T~:*~A~}" + 'a 'aa 'aaa 'aaaa 'aaaaa 'aaaaaa 'aaaaaaa 'aaaaaaaa 'aaaaaaaaa 'aaaaaaaaaa) + "a a\naa aa\naaa aaa\naaaa aaaa\naaaaa aaaaa\naaaaaa aaaaaa\naaaaaaa aaaaaaa\naaaaaaaa aaaaaaaa\naaaaaaaaa aaaaaaaaa\naaaaaaaaaa aaaaaaaaaa" + (cl-format nil "~@{~&~A~2,6@T~:*~A~}" 'a 'aa 'aaa 'aaaa 'aaaaa 'aaaaaa 'aaaaaaa 'aaaaaaaa 'aaaaaaaaa 'aaaaaaaaaa) + "a a\naa aa\naaa aaa\naaaa aaaa\naaaaa aaaaa\naaaaaa aaaaaa\naaaaaaa aaaaaaa\naaaaaaaa aaaaaaaa\naaaaaaaaa aaaaaaaaa\naaaaaaaaaa aaaaaaaaaa" +) + +(simple-tests paren-tests + (cl-format nil "~(PLEASE SPEAK QUIETLY IN HERE~)") "please speak quietly in here" + (cl-format nil "~@(PLEASE SPEAK QUIETLY IN HERE~)") "Please speak quietly in here" + (cl-format nil "~@:(but this Is imporTant~)") "BUT THIS IS IMPORTANT" + (cl-format nil "~:(the greAt gatsby~)!") "The Great Gatsby!" + ;; Test cases from CLtL 18.3 - string-upcase, et al. + (cl-format nil "~@:(~A~)" "Dr. Livingstone, I presume?") "DR. LIVINGSTONE, I PRESUME?" + (cl-format nil "~(~A~)" "Dr. Livingstone, I presume?") "dr. livingstone, i presume?" + (cl-format nil "~:(~A~)" " hello ") " Hello " + (cl-format nil "~:(~A~)" "occlUDeD cASEmenTs FOreSTAll iNADVertent DEFenestraTION") + "Occluded Casements Forestall Inadvertent Defenestration" + (cl-format nil "~:(~A~)" 'kludgy-hash-search) "Kludgy-Hash-Search" + (cl-format nil "~:(~A~)" "DON'T!") "Don'T!" ;not "Don't!" + (cl-format nil "~:(~A~)" "pipe 13a, foo16c") "Pipe 13a, Foo16c" + (cl-format nil "~:(~A~)" nil) "Nil" + (cl-format nil "~:(~A~)" "") "" +) + +(simple-tests square-bracket-tests + ;; Tests for format without modifiers + (cl-format nil "I ~[don't ~]have one~%" 0) "I don't have one\n" + (cl-format nil "I ~[don't ~]have one~%" 1) "I have one\n" + (cl-format nil "I ~[don't ~;do ~]have one~%" 0) "I don't have one\n" + (cl-format nil "I ~[don't ~;do ~]have one~%" 1) "I do have one\n" + (cl-format nil "I ~[don't ~;do ~]have one~%" 2) "I have one\n" + (cl-format nil "I ~[don't ~:;do ~]have one~%" 0) "I don't have one\n" + (cl-format nil "I ~[don't ~:;do ~]have one~%" 1) "I do have one\n" + (cl-format nil "I ~[don't ~:;do ~]have one~%" 2) "I do have one\n" + (cl-format nil "I ~[don't ~:;do ~]have one~%" 700) "I do have one\n" + + ;; Tests for format with a colon + (cl-format nil "I ~:[don't ~;do ~]have one~%" true) "I do have one\n" + (cl-format nil "I ~:[don't ~;do ~]have one~%" 700) "I do have one\n" + (cl-format nil "I ~:[don't ~;do ~]have one~%" '(a b)) "I do have one\n" + (cl-format nil "I ~:[don't ~;do ~]have one~%" nil) "I don't have one\n" + (cl-format nil "I ~:[don't ~;do ~]have one~%" false) "I don't have one\n" + + ;; Tests for format with an at sign + (cl-format nil "We had ~D wins~@[ (out of ~D tries)~].~%" 15 nil) "We had 15 wins.\n" + (cl-format nil "We had ~D wins~@[ (out of ~D tries)~].~%" 15 17) + "We had 15 wins (out of 17 tries).\n" + + ;; Format tests with directives + (cl-format nil "Max ~D: ~[Blue team ~D~;Red team ~D~:;No team ~A~].~%" 15, 0, 7) + "Max 15: Blue team 7.\n" + (cl-format nil "Max ~D: ~[Blue team ~D~;Red team ~D~:;No team ~A~].~%" 15, 1, 12) + "Max 15: Red team 12.\n" + (cl-format nil "Max ~D: ~[Blue team ~D~;Red team ~D~:;No team ~A~].~%" + 15, -1, "(system failure)") + "Max 15: No team (system failure).\n" + + ;; Nested format tests + (cl-format nil "Max ~D: ~[Blue team ~D~:[~; (complete success)~]~;Red team ~D~:;No team ~].~%" + 15, 0, 7, true) + "Max 15: Blue team 7 (complete success).\n" + (cl-format nil "Max ~D: ~[Blue team ~D~:[~; (complete success)~]~;Red team ~D~:;No team ~].~%" + 15, 0, 7, false) + "Max 15: Blue team 7.\n" + + ;; Test the selector as part of the argument + (cl-format nil "The answer is ~#[nothing~;~D~;~D out of ~D~:;something crazy~].") + "The answer is nothing." + (cl-format nil "The answer is ~#[nothing~;~D~;~D out of ~D~:;something crazy~]." 4) + "The answer is 4." + (cl-format nil "The answer is ~#[nothing~;~D~;~D out of ~D~:;something crazy~]." 7 22) + "The answer is 7 out of 22." + (cl-format nil "The answer is ~#[nothing~;~D~;~D out of ~D~:;something crazy~]." 1 2 3 4) + "The answer is something crazy." +) + +(simple-tests curly-brace-plain-tests + ;; Iteration from sublist + (cl-format nil "Coordinates are~{ [~D,~D]~}~%" [ 0, 1, 1, 0, 3, 5, 2, 1 ]) + "Coordinates are [0,1] [1,0] [3,5] [2,1]\n" + + (cl-format nil "Coordinates are~2{ [~D,~D]~}~%" [ 0, 1, 1, 0, 3, 5, 2, 1 ]) + "Coordinates are [0,1] [1,0]\n" + + (cl-format nil "Coordinates are~{ ~#[none~;<~D>~:;[~D,~D]~]~}~%" [ ]) + "Coordinates are\n" + + (cl-format nil "Coordinates are~{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [ ]) + "Coordinates are none\n" + + (cl-format nil "Coordinates are~{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [2 3 1]) + "Coordinates are [2,3] <1>\n" + + (cl-format nil "Coordinates are~{~:}~%" "" []) + "Coordinates are\n" + + (cl-format nil "Coordinates are~{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [2 3 1]) + "Coordinates are [2,3] <1>\n" + + (cl-format nil "Coordinates are~{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [ ]) + "Coordinates are none\n" +) + +(simple-tests curly-brace-colon-tests + ;; Iteration from list of sublists + (cl-format nil "Coordinates are~:{ [~D,~D]~}~%" [ [0, 1], [1, 0], [3, 5], [2, 1] ]) + "Coordinates are [0,1] [1,0] [3,5] [2,1]\n" + + (cl-format nil "Coordinates are~:{ [~D,~D]~}~%" [ [0, 1, 0], [1, 0, 12], [3, 5], [2, 1] ]) + "Coordinates are [0,1] [1,0] [3,5] [2,1]\n" + + (cl-format nil "Coordinates are~2:{ [~D,~D]~}~%" [ [0, 1], [1, 0], [3, 5], [2, 1] ]) + "Coordinates are [0,1] [1,0]\n" + + (cl-format nil "Coordinates are~:{ ~#[none~;<~D>~:;[~D,~D]~]~}~%" [ ]) + "Coordinates are\n" + + (cl-format nil "Coordinates are~:{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [ ]) + "Coordinates are none\n" + + (cl-format nil "Coordinates are~:{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [[2 3] [1]]) + "Coordinates are [2,3] <1>\n" + + (cl-format nil "Coordinates are~:{~:}~%" "" []) + "Coordinates are\n" + + (cl-format nil "Coordinates are~:{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [[2 3] [1]]) + "Coordinates are [2,3] <1>\n" + + (cl-format nil "Coordinates are~:{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [ ]) + "Coordinates are none\n" +) + +(simple-tests curly-brace-at-tests + ;; Iteration from main list + (cl-format nil "Coordinates are~@{ [~D,~D]~}~%" 0, 1, 1, 0, 3, 5, 2, 1) + "Coordinates are [0,1] [1,0] [3,5] [2,1]\n" + + (cl-format nil "Coordinates are~2@{ [~D,~D]~}~%" 0, 1, 1, 0, 3, 5, 2, 1) + "Coordinates are [0,1] [1,0]\n" + + (cl-format nil "Coordinates are~@{ ~#[none~;<~D>~:;[~D,~D]~]~}~%") + "Coordinates are\n" + + (cl-format nil "Coordinates are~@{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%") + "Coordinates are none\n" + + (cl-format nil "Coordinates are~@{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" 2 3 1) + "Coordinates are [2,3] <1>\n" + + (cl-format nil "Coordinates are~@{~:}~%" "") + "Coordinates are\n" + + (cl-format nil "Coordinates are~@{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" 2 3 1) + "Coordinates are [2,3] <1>\n" + + (cl-format nil "Coordinates are~@{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]") + "Coordinates are none\n" +) + +(simple-tests curly-brace-colon-at-tests + ;; Iteration from sublists on the main arg list + (cl-format nil "Coordinates are~@:{ [~D,~D]~}~%" [0, 1], [1, 0], [3, 5], [2, 1] ) + "Coordinates are [0,1] [1,0] [3,5] [2,1]\n" + + (cl-format nil "Coordinates are~@:{ [~D,~D]~}~%" [0, 1, 0], [1, 0, 12], [3, 5], [2, 1] ) + "Coordinates are [0,1] [1,0] [3,5] [2,1]\n" + + (cl-format nil "Coordinates are~2@:{ [~D,~D]~}~%" [0, 1], [1, 0], [3, 5], [2, 1]) + "Coordinates are [0,1] [1,0]\n" + + (cl-format nil "Coordinates are~@:{ ~#[none~;<~D>~:;[~D,~D]~]~}~%") + "Coordinates are\n" + + (cl-format nil "Coordinates are~@:{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%") + "Coordinates are none\n" + + (cl-format nil "Coordinates are~@:{ ~#[none~;<~D>~:;[~D,~D]~]~:}~%" [2 3] [1]) + "Coordinates are [2,3] <1>\n" + + (cl-format nil "Coordinates are~@:{~:}~%" "") + "Coordinates are\n" + + (cl-format nil "Coordinates are~@:{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]" [2 3] [1]) + "Coordinates are [2,3] <1>\n" + + (cl-format nil "Coordinates are~@:{~:}~%" " ~#[none~;<~D>~:;[~D,~D]~]") + "Coordinates are none\n" +) + +;; TODO tests for ~^ in ~[ constructs and other brackets +;; TODO test ~:^ generates an error when used improperly +;; TODO test ~:^ works in ~@:{...~} +(let [aseq '(a quick brown fox jumped over the lazy dog) + lseq (mapcat identity (for [x aseq] [x (.-length (name x))]))] + (simple-tests up-tests + (cl-format nil "~{~a~^, ~}" aseq) "a, quick, brown, fox, jumped, over, the, lazy, dog" + (cl-format nil "~{~a~0^, ~}" aseq) "a" + (cl-format nil "~{~a~#,3^, ~}" aseq) "a, quick, brown, fox, jumped, over" + (cl-format nil "~{~a~v,3^, ~}" lseq) "a, quick, brown, fox" + (cl-format nil "~{~a~3,v,4^, ~}" lseq) "a, quick, brown, fox" + )) + +(simple-tests angle-bracket-tests + (cl-format nil "~") "foobarbaz" + (cl-format nil "~20") "foo bar baz" + (cl-format nil "~,,2") "foo bar baz" + (cl-format nil "~20<~A~;~A~;~A~>" "foo" "bar" "baz") "foo bar baz" + (cl-format nil "~20:<~A~;~A~;~A~>" "foo" "bar" "baz") " foo bar baz" + (cl-format nil "~20@<~A~;~A~;~A~>" "foo" "bar" "baz") "foo bar baz " + (cl-format nil "~20@:<~A~;~A~;~A~>" "foo" "bar" "baz") " foo bar baz " + (cl-format nil "~10,,2<~A~;~A~;~A~>" "foo" "bar" "baz") "foo bar baz" + (cl-format nil "~10,10,2<~A~;~A~;~A~>" "foo" "bar" "baz") "foo bar baz" + (cl-format nil "~10,10<~A~;~A~;~A~>" "foo" "bar" "baz") "foo barbaz" + (cl-format nil "~20<~A~;~^~A~;~^~A~>" "foo" "bar" "baz") "foo bar baz" + (cl-format nil "~20<~A~;~^~A~;~^~A~>" "foo" "bar") "foo bar" + (cl-format nil "~20@<~A~;~^~A~;~^~A~>" "foo") "foo " + (cl-format nil "~20:<~A~;~^~A~;~^~A~>" "foo") " foo" +) + +(simple-tests angle-bracket-max-column-tests + (cl-format nil "~%;; ~{~<~%;; ~1,50:; ~A~>~}.~%" (into [] (clojure.string/split "This function computes the circular thermodynamic coefficient of the thrombulator angle for use in determining the reaction distance" #"\s"))) + "\n;; This function computes the circular\n;; thermodynamic coefficient of the thrombulator\n;; angle for use in determining the reaction\n;; distance.\n" + (cl-format true "~%;; ~{~<~%;; ~:; ~A~>~}.~%" (into [] (clojure.string/split "This function computes the circular thermodynamic coefficient of the thrombulator angle for use in determining the reaction distance." #"\s"))) +) + +(defn list-to-table-stream [aseq column-width] + (let [sb (StringBuffer.)] + (binding [*out* (get-pretty-writer (StringBufferWriter. sb))] + (doseq [row aseq] + (doseq [col row] + (cl-format *out* "~4D~7,vT" col column-width)) + (prn))) + (str sb) + ;;TODO do we need to extend StringBufferWriter to allow access to underlying StringBuffer? + #_(str (:base @@(:base @@stream))))) + +(defn list-to-table-print [aseq column-width] + (let [sb (StringBuffer.)] + (binding [*print-fn* (fn [s] (.append sb (apply str s))) + *print-newline* true] + (doseq [row aseq] + (doseq [col row] + (cl-format true "~4D~7,vT" col column-width)) + (cljs.core/prn))) + (str sb))) + +(simple-tests column-writer-test + (list-to-table-stream (map #(vector % (* % %) (* % % %)) (range 1 21)) 8) + " 1 1 1 \n 2 4 8 \n 3 9 27 \n 4 16 64 \n 5 25 125 \n 6 36 216 \n 7 49 343 \n 8 64 512 \n 9 81 729 \n 10 100 1000 \n 11 121 1331 \n 12 144 1728 \n 13 169 2197 \n 14 196 2744 \n 15 225 3375 \n 16 256 4096 \n 17 289 4913 \n 18 324 5832 \n 19 361 6859 \n 20 400 8000 \n" + + (list-to-table-print (map #(vector % (* % %) (* % % %)) (range 1 21)) 8) + " 1 1 1 \n 2 4 8 \n 3 9 27 \n 4 16 64 \n 5 25 125 \n 6 36 216 \n 7 49 343 \n 8 64 512 \n 9 81 729 \n 10 100 1000 \n 11 121 1331 \n 12 144 1728 \n 13 169 2197 \n 14 196 2744 \n 15 225 3375 \n 16 256 4096 \n 17 289 4913 \n 18 324 5832 \n 19 361 6859 \n 20 400 8000 \n" +) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The following tests are the various examples from the format +;; documentation in Common Lisp, the Language, 2nd edition, Chapter 22.3 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn expt [base pow] (reduce * (repeat pow base))) + +(let [x 5, y "elephant", n 3] + (simple-tests cltl-intro-tests + (format nil "foo") "foo" + (format nil "The answer is ~D." x) "The answer is 5." + (format nil "The answer is ~3D." x) "The answer is 5." + (format nil "The answer is ~3,'0D." x) "The answer is 005." + (format nil "The answer is ~:D." (expt 47 x)) "The answer is 229,345,007." + (format nil "Look at the ~A!" y) "Look at the elephant!" + (format nil "Type ~:C to ~A." (char 4) "delete all your files") + "Type Control-D to delete all your files." + (format nil "~D item~:P found." n) "3 items found." + (format nil "~R dog~:[s are~; is~] here." n (= n 1)) "three dogs are here." + (format nil "~R dog~:*~[s are~; is~:;s are~] here." n) "three dogs are here." + (format nil "Here ~[are~;is~:;are~] ~:*~R pupp~:@P." n) "Here are three puppies.")) + +(simple-tests cltl-B-tests + ;; CLtL didn't have the colons here, but the spec requires them + (format nil "~,,' ,4:B" 0xFACE) "1111 1010 1100 1110" + (format nil "~,,' ,4:B" 0x1CE) "1 1100 1110" + (format nil "~19,,' ,4:B" 0xFACE) "1111 1010 1100 1110" + ;; This one was a nice idea, but nothing in the spec supports it working this way + ;; (and SBCL doesn't work this way either) + ;(format nil "~19,,' ,4:B" 0x1CE) "0000 0001 1100 1110") +) + +(simple-tests cltl-P-tests + (format nil "~D tr~:@P/~D win~:P" 7 1) "7 tries/1 win" + (format nil "~D tr~:@P/~D win~:P" 1 0) "1 try/0 wins" + (format nil "~D tr~:@P/~D win~:P" 1 3) "1 try/3 wins" +) + +(defn foo [x] + (format nil "~6,2F|~6,2,1,'*F|~6,2,,'?F|~6F|~,2F|~F" + x x x x x x)) + +;; big-pos-ratio is a ratio value that is larger than +;; Double/MAX_VALUE, and has a non-terminating decimal representation +;; if you attempt to represent it exactly. +#_(def big-pos-ratio (/ (* 4 (bigint (. BigDecimal valueOf Double/MAX_VALUE))) 3)) +#_(def big-neg-ratio (- big-pos-ratio)) +;; tiny-pos-ratio is a ratio between 0 and Double/MIN_VALUE. +#_(def tiny-pos-ratio (/ 1 (bigint (apply str (cons "1" (repeat 340 "0")))))) +#_(def tiny-neg-ratio (- tiny-pos-ratio)) + +(simple-tests cltl-F-tests + #_(cl-format false "~10,3f" 4/5) + #_" 0.800" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3f" big-pos-ratio)) + #_"239692417981642093333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3f" big-neg-ratio)) + #_"-239692417981642093333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3f" tiny-pos-ratio)) + #_" 0.000" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3f" tiny-neg-ratio)) + #_" -0.000" + (foo 3.14159) " 3.14| 31.42| 3.14|3.1416|3.14|3.14159" + #_(foo 314159/100000) + #_" 3.14| 31.42| 3.14|3.1416|3.14|3.14159" + (foo -3.14159) " -3.14|-31.42| -3.14|-3.142|-3.14|-3.14159" + (foo 100.0) "100.00|******|100.00| 100.0|100.00|100.0" + (foo 1234.0) "1234.00|******|??????|1234.0|1234.00|1234.0" + (foo 0.006) " 0.01| 0.06| 0.01| 0.006|0.01|0.006" +) + +(defn foo-e [x] + (format nil + "~9,2,1,,'*E|~10,3,2,2,'?,,'$E|~9,3,2,-2,'%@E|~9,2E" + x x x x)) + +;; Clojure doesn't support float/double differences in representation +(simple-tests cltl-E-tests + #_(cl-format false "~10,3e" 4/5) + #_" 8.000E-1" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3e" big-pos-ratio)) + #_"2.397E+308" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3e" big-neg-ratio)) + #_"-2.397E+308" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3e" tiny-pos-ratio)) + #_"1.000E-340" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3e" tiny-neg-ratio)) + #_"-1.000E-340" + (foo-e 0.0314159) " 3.14E-2| 31.42$-03|+.003E+01| 3.14E-2" ; Added this one + #_(foo-e 314159/10000000) + #_" 3.14E-2| 31.42$-03|+.003E+01| 3.14E-2" + (foo-e 3.14159) " 3.14E+0| 31.42$-01|+.003E+03| 3.14E+0" + (foo-e -3.14159) " -3.14E+0|-31.42$-01|-.003E+03| -3.14E+0" + (foo-e 1100.0) " 1.10E+3| 11.00$+02|+.001E+06| 1.10E+3" + ; In Clojure, this is identical to the above + ; (foo-e 1100.0L0) " 1.10L+3| 11.00$+02|+.001L+06| 1.10L+3" + (foo-e 1.1E13) "*********| 11.00$+12|+.001E+16| 1.10E+13" + (foo-e 1.1E120) "*********|??????????|%%%%%%%%%|1.10E+120" + ; Clojure doesn't support real numbers this large + ; (foo-e 1.1L1200) "*********|??????????|%%%%%%%%%|1.10L+1200" +) + +(simple-tests cltl-E-scale-tests + (map + (fn [k] (format nil "Scale factor ~2D~:*: |~13,6,2,VE|" + (- k 5) 3.14159)) ;Prints 13 lines + (range 13)) + '("Scale factor -5: | 0.000003E+06|" + "Scale factor -4: | 0.000031E+05|" + "Scale factor -3: | 0.000314E+04|" + "Scale factor -2: | 0.003142E+03|" + "Scale factor -1: | 0.031416E+02|" + "Scale factor 0: | 0.314159E+01|" + "Scale factor 1: | 3.141590E+00|" + "Scale factor 2: | 31.41590E-01|" + "Scale factor 3: | 314.1590E-02|" + "Scale factor 4: | 3141.590E-03|" + "Scale factor 5: | 31415.90E-04|" + "Scale factor 6: | 314159.0E-05|" + "Scale factor 7: | 3141590.E-06|")) + +(defn foo-g [x] + (format nil + "~9,2,1,,'*G|~9,3,2,3,'?,,'$G|~9,3,2,0,'%G|~9,2G" + x x x x)) + +;; Clojure doesn't support float/double differences in representation +(simple-tests cltl-G-tests + #_(cl-format false "~10,3g" 4/5) + #_" 0.800 " + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3g" big-pos-ratio)) + #_"2.397E+308" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3g" big-neg-ratio)) + #_"-2.397E+308" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3g" tiny-pos-ratio)) + #_"1.000E-340" + #_(binding [*math-context* java.math.MathContext/DECIMAL128] + (cl-format false "~10,3g" tiny-neg-ratio)) + #_"-1.000E-340" + (foo-g 0.0314159) " 3.14E-2|314.2$-04|0.314E-01| 3.14E-2" + #_(foo-g 314159/10000000) + #_" 3.14E-2|314.2$-04|0.314E-01| 3.14E-2" + (foo-g 0.314159) " 0.31 |0.314 |0.314 | 0.31 " + (foo-g 3.14159) " 3.1 | 3.14 | 3.14 | 3.1 " + (foo-g 31.4159) " 31. | 31.4 | 31.4 | 31. " + (foo-g 314.159) " 3.14E+2| 314. | 314. | 3.14E+2" + (foo-g 3141.59) " 3.14E+3|314.2$+01|0.314E+04| 3.14E+3" + ; In Clojure, this is identical to the above + ; (foo-g 3141.59L0) " 3.14L+3|314.2$+01|0.314L+04| 3.14L+3" + (foo-g 3.14E12) "*********|314.0$+10|0.314E+13| 3.14E+12" + (foo-g 3.14E120) "*********|?????????|%%%%%%%%%|3.14E+120" + ; Clojure doesn't support real numbers this large +) + +(defn type-clash-error [fun nargs argnum right-type wrong-type] + (format nil ;; CLtL has this format string slightly wrong + "~&Function ~S requires its ~:[~:R ~;~*~]~ + argument to be of type ~S,~%but it was called ~ + with an argument of type ~S.~%" + fun (= nargs 1) argnum right-type wrong-type)) + +(simple-tests cltl-Newline-tests + (type-clash-error 'aref nil 2 'integer 'vector) + "Function aref requires its second argument to be of type integer,\nbut it was called with an argument of type vector.\n" + (type-clash-error 'car 1 1 'list 'short-float) + "Function car requires its argument to be of type list,\nbut it was called with an argument of type short-float.\n" +) + +(simple-tests cltl-?-tests + (format nil "~? ~D" "<~A ~D>" '("Foo" 5) 7) " 7" + (format nil "~? ~D" "<~A ~D>" '("Foo" 5 14) 7) " 7" + (format nil "~@? ~D" "<~A ~D>" "Foo" 5 7) " 7" + (format nil "~@? ~D" "<~A ~D>" "Foo" 5 14 7) " 14" +) + +(defn f [n] (format nil "~@(~R~) error~:P detected." n)) + +(simple-tests cltl-paren-tests + (format nil "~@R ~(~@R~)" 14 14) "XIV xiv" + (f 0) "Zero errors detected." + (f 1) "One error detected." + (f 23) "Twenty-three errors detected." +) + +(let [*print-level* nil *print-length* 5] + (simple-tests cltl-bracket-tests + (format nil "~@[ print level = ~D~]~@[ print length = ~D~]" + *print-level* *print-length*) + " print length = 5") +) + +(let [foo "Items:~#[ none~; ~S~; ~S and ~S~ + ~:;~@{~#[~; and~] ~ + ~S~^,~}~]."] + (simple-tests cltl-bracket1-tests + (format nil foo) "Items: none." + (format nil foo 'foo) "Items: foo." + (format nil foo 'foo 'bar) "Items: foo and bar." + (format nil foo 'foo 'bar 'baz) "Items: foo, bar, and baz." + (format nil foo 'foo 'bar 'baz 'quux) "Items: foo, bar, baz, and quux." + )) + +(simple-tests cltl-curly-bracket-tests + (format nil + "The winners are:~{ ~S~}." + '(fred harry jill)) + "The winners are: fred harry jill." + + (format nil "Pairs:~{ <~S,~S>~}." '(a 1 b 2 c 3)) + "Pairs: ." + + (format nil "Pairs:~:{ <~S,~S>~}." '((a 1) (b 2) (c 3))) + "Pairs: ." + + (format nil "Pairs:~@{ <~S,~S>~}." 'a 1 'b 2 'c 3) + "Pairs: ." + + (format nil "Pairs:~:@{ <~S,~S>~}." '(a 1) '(b 2) '(c 3)) + "Pairs: ." +) + +(simple-tests cltl-angle-bracket-tests + (format nil "~10") "foo bar" + (format nil "~10:") " foo bar" + (format nil "~10:@") " foo bar " + (format nil "~10") " foobar" + (format nil "~10:") " foobar" + (format nil "~10@") "foobar " + (format nil "~10:@") " foobar " +) + +(let [donestr "Done.~^ ~D warning~:P.~^ ~D error~:P." + tellstr "~@{~@(~@[~R~^ ~]~A~)~}."] ;; The CLtL example is a little wrong here + + (simple-tests cltl-up-tests + (format nil donestr) "Done." + (format nil donestr 3) "Done. 3 warnings." + (format nil donestr 1 5) "Done. 1 warning. 5 errors." + (format nil tellstr 23) "Twenty-three." + (format nil tellstr nil "losers") "Losers." + (format nil tellstr 23 "losers") "Twenty-three losers." + (format nil "~15<~S~;~^~S~;~^~S~>" 'foo) + " foo" + (format nil "~15<~S~;~^~S~;~^~S~>" 'foo 'bar) + "foo bar" + (format nil "~15<~S~;~^~S~;~^~S~>" 'foo 'bar 'baz) + "foo bar baz" + )) + +(simple-tests cltl-up-x3j13-tests + (format nil + "~:{/~S~^ ...~}" + '((hot dog) (hamburger) (ice cream) (french fries))) + "/hot .../hamburger/ice .../french ..." + (format nil + "~:{/~S~:^ ...~}" + '((hot dog) (hamburger) (ice cream) (french fries))) + "/hot .../hamburger .../ice .../french" + + (format nil + "~:{/~S~#:^ ...~}" ;; This is wrong in CLtL + '((hot dog) (hamburger) (ice cream) (french fries))) + "/hot .../hamburger" +) + +(simple-tests pprint-table-tests + (with-out-str + (print-table [:b :a] + [{:a 1 :b {:a 'is-a} :c ["hi" "there"]} + {:b 5 :a 7 :c "dog" :d -700}])) + " +| :b | :a | +|-----------+----| +| {:a is-a} | 1 | +| 5 | 7 | +" + ;;This test is changed a bit due to the way JS prints large numbers. The number + ;; was changed (54.7e17 to 54.7e20) to make sure JS prints it in E notation (5.47E21) + (with-out-str + (print-table [:a :e :d :c] + [{:a 54.7e20 :b {:a 'is-a} :c ["hi" "there"]} + {:b 5 :a -23 :c "dog" :d 'panda}])) + " +| :a | :e | :d | :c | +|----------+----+-------+----------------| +| 5.47e+21 | | | [\"hi\" \"there\"] | +| -23 | | panda | dog | +" + ) + diff --git a/test/cljs/test_runner.cljs b/test/cljs/test_runner.cljs index 11d8cfe5f..7a88b5b0e 100644 --- a/test/cljs/test_runner.cljs +++ b/test/cljs/test_runner.cljs @@ -13,7 +13,8 @@ [cljs.reducers-test] [cljs.keyword-test] [cljs.import-test] - [cljs.ns-test.foo])) + [cljs.ns-test.foo] + [cljs.pprint])) (set! *print-newline* false) (set-print-fn! js/print) @@ -32,4 +33,5 @@ 'cljs.ns-test 'cljs.ns-test.foo 'foo.ns-shadow-test - 'cljs.import-test) + 'cljs.import-test + 'cljs.pprint) From f21ad2787699815dbf93a854ff0cf7c3461b2640 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 May 2015 14:55:19 -0400 Subject: [PATCH 1026/4033] fix RegExp Closure errors --- test/cljs/cljs/pprint_test.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cljs/cljs/pprint_test.clj b/test/cljs/cljs/pprint_test.clj index 576f9f231..f4bc4ca33 100644 --- a/test/cljs/cljs/pprint_test.clj +++ b/test/cljs/cljs/pprint_test.clj @@ -5,8 +5,8 @@ `(deftest ~name ~@(for [[x y] (partition 2 test-pairs)] `(cond - (= js/RegExp (type ~y)) (is (.exec ~y ~x)) - (= js/String (type ~y)) (is (= ~x ~y)) + (cljs.core/regexp? ~y) (is (.exec ~y ~x)) + (cljs.core/string? ~y) (is (= ~x ~y)) :else (is (= ~x ~y)))))) (defmacro code-block From e3a80f3fb1eee4935aae00c8f9bfe1b65cbf889f Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 May 2015 15:13:51 -0400 Subject: [PATCH 1027/4033] add cljs.pprint/pp macro load cljs.pprint/pp and cljs.pprint/pprint in REPLs --- src/clj/cljs/repl.cljc | 3 ++- src/cljs/cljs/pprint.clj | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/clj/cljs/repl.cljc b/src/clj/cljs/repl.cljc index c0e49f54a..78ecf46fb 100644 --- a/src/clj/cljs/repl.cljc +++ b/src/clj/cljs/repl.cljc @@ -730,7 +730,8 @@ 1 "NO_SOURCE_FILE") print-no-newline print source-map-inline true - repl-requires '[[cljs.repl :refer-macros [source doc find-doc apropos dir pst]]] + repl-requires '[[cljs.repl :refer-macros [source doc find-doc apropos dir pst]] + [cljs.pprint :refer [pprint] :refer-macros [pp]]] bind-err true} :as opts}] (let [repl-opts (-repl-options repl-env) diff --git a/src/cljs/cljs/pprint.clj b/src/cljs/cljs/pprint.clj index ff1789ae4..bae7d5c77 100644 --- a/src/cljs/cljs/pprint.clj +++ b/src/cljs/cljs/pprint.clj @@ -142,3 +142,8 @@ format-in can be either a control string or a previously compiled format." `(cljs.core/binding [cljs.pprint/*print-pprint-dispatch* ~function] ~@body)) +(defmacro pp + "A convenience macro that pretty prints the last thing output. This is +exactly equivalent to (pprint *1)." + {:added "1.2"} + [] `(cljs.pprint/pprint *1)) \ No newline at end of file From 8659d78b69a0576a0c4a1b25aeb90ccf500835ba Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 May 2015 15:33:07 -0400 Subject: [PATCH 1028/4033] add copyright comments to the top of the new files --- src/cljs/cljs/pprint.clj | 8 ++++++++ test/cljs/cljs/pprint_test.clj | 8 ++++++++ test/cljs/cljs/pprint_test.cljs | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/src/cljs/cljs/pprint.clj b/src/cljs/cljs/pprint.clj index bae7d5c77..236ec03e2 100644 --- a/src/cljs/cljs/pprint.clj +++ b/src/cljs/cljs/pprint.clj @@ -1,3 +1,11 @@ +;; 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.pprint (:refer-clojure :exclude [deftype]) (:require [clojure.walk :as walk])) diff --git a/test/cljs/cljs/pprint_test.clj b/test/cljs/cljs/pprint_test.clj index f4bc4ca33..8e6daa3fc 100644 --- a/test/cljs/cljs/pprint_test.clj +++ b/test/cljs/cljs/pprint_test.clj @@ -1,3 +1,11 @@ +;; 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.pprint-test (:require [cljs.test :refer [deftest is]])) diff --git a/test/cljs/cljs/pprint_test.cljs b/test/cljs/cljs/pprint_test.cljs index 10e29d34f..13dbbc22a 100644 --- a/test/cljs/cljs/pprint_test.cljs +++ b/test/cljs/cljs/pprint_test.cljs @@ -1,3 +1,11 @@ +;; 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.pprint-test (:refer-clojure :exclude [prn]) (:require-macros From ab68ddc358c478743907c513c76a9db3b7aa78bc Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 May 2015 19:53:59 -0400 Subject: [PATCH 1029/4033] fix build script as pointed out by Mike Fikes, cljs/util.clj -> cljs/util.cljc --- script/build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/build b/script/build index f792bd2f2..880020454 100755 --- a/script/build +++ b/script/build @@ -29,8 +29,8 @@ TAG=r$REVISION sed -e s/CLOJURESCRIPT_VERSION/0.0-$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` -sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/util.clj > $COMP_FILE -mv $COMP_FILE src/clj/cljs/util.clj +sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/util.cljc > $COMP_FILE +mv $COMP_FILE src/clj/cljs/util.cljc CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/cljs/cljs/core.cljs > $CLJS_FILE From 74c7eb24c99955203eec61d8d065087292c3f201 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 6 May 2015 22:27:08 -0400 Subject: [PATCH 1030/4033] CLJS-1250: uberjar script broken wrt cljc --- script/uberjar | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/uberjar b/script/uberjar index 08c5b5802..d5ff740c1 100755 --- a/script/uberjar +++ b/script/uberjar @@ -20,8 +20,8 @@ REVISION=${REVISION:5} # drop the first 5 characters REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` -sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/util.clj > $COMP_FILE -mv $COMP_FILE src/clj/cljs/util.clj +sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/util.cljc > $COMP_FILE +mv $COMP_FILE src/clj/cljs/util.cljc CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/cljs/cljs/core.cljs > $CLJS_FILE From 420d0884773c80d84bc95066a0c92e284163c5e4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 7 May 2015 17:03:10 -0400 Subject: [PATCH 1031/4033] update closure library release script, now closure-library/README.md not closure-library/README --- script/closure-library-release/closure-library-release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index 031a102e8..582c0f124 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -117,7 +117,7 @@ mkdir -p "$src_dir" "$third_party_src_dir" cp -r \ "$closure_library_dir/AUTHORS" \ "$closure_library_dir/LICENSE" \ - "$closure_library_dir/README" \ + "$closure_library_dir/README.md" \ "$closure_library_dir/closure/goog" \ "$closure_library_dir/closure/css" \ "$src_dir" @@ -125,7 +125,7 @@ cp -r \ cp -r \ "$closure_library_dir/AUTHORS" \ "$closure_library_dir/LICENSE" \ - "$closure_library_dir/README" \ + "$closure_library_dir/README.md" \ "$closure_library_dir/third_party/closure/goog" \ "$third_party_src_dir" From 46ea0b9c2871532334c1007546191e1b4e60dc9f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 7 May 2015 17:12:32 -0400 Subject: [PATCH 1032/4033] CLJS-1252: Update Closure Compiler Dependency to v20150505 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 85bb97b1c..327d060b8 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20150126 + v20150505 org.clojure diff --git a/project.clj b/project.clj index fff5e2d7f..7d2adfb2b 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.2"] [org.clojure/google-closure-library "0.0-20140718-946a7d39"] - [com.google.javascript/closure-compiler "v20150126"] + [com.google.javascript/closure-compiler "v20150505"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 2004a2d6c..6e02c1153 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.7.0-beta2" -CLOSURE_RELEASE="20150126" +CLOSURE_RELEASE="20150505" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" RHINO_RELEASE="1_7R5" From 77d9434d2bfb78c233911c624d544265bb7666d5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 8 May 2015 07:53:41 -0400 Subject: [PATCH 1033/4033] CLJS-1254: Update REPL browser agent detection CLJS-1253: Create/Use new Closure Library Release Update closure library dependency. Change js-deps/goog-dependencies* to account for additional optional module argument to goog.addDependency. Update clojure.browser.repl to use goog.userAgent.product namespace instead of labs. --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/clj/cljs/js_deps.clj | 8 ++++---- src/cljs/clojure/browser/repl.cljs | 10 +++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 327d060b8..61d28d699 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20140718-946a7d39 + 0.0-20150505-021ed5b3 org.clojure diff --git a/project.clj b/project.clj index 7d2adfb2b..8dd877d9c 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :dependencies [[org.clojure/clojure "1.7.0-beta2"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.2"] - [org.clojure/google-closure-library "0.0-20140718-946a7d39"] + [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] [com.google.javascript/closure-compiler "v20150505"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} diff --git a/script/bootstrap b/script/bootstrap index 6e02c1153..2a50f0eb6 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,7 +5,7 @@ set -e CLOJURE_RELEASE="1.7.0-beta2" CLOSURE_RELEASE="20150505" DJSON_RELEASE="0.2.6" -GCLOSURE_LIB_RELEASE="0.0-20140718-946a7d39" +GCLOSURE_LIB_RELEASE="0.0-20150505-021ed5b3" RHINO_RELEASE="1_7R5" TREADER_RELEASE="0.9.2" diff --git a/src/clj/cljs/js_deps.clj b/src/clj/cljs/js_deps.clj index bb647d32e..f3d04d35f 100644 --- a/src/clj/cljs/js_deps.clj +++ b/src/clj/cljs/js_deps.clj @@ -263,13 +263,13 @@ JavaScript library containing provide/require 'declarations'." (string/split #"'\s*,\s*'"))))] (with-open [reader (io/reader (io/resource "goog/deps.js"))] (->> (line-seq reader) - (map #(re-matches #"^goog\.addDependency\(['\"](.*)['\"],\s*\[(.*)\],\s*\[(.*)\]\);.*" %)) + (map #(re-matches #"^goog\.addDependency\(['\"](.*)['\"],\s*\[(.*)\],\s*\[(.*)\],.*\);.*" %)) (remove nil?) (map #(drop 1 %)) (remove #(.startsWith ^String (first %) "../../third_party")) - (map #(hash-map :file (str "goog/"(first %)) - :provides (parse-list (second %)) - :requires (parse-list (last %)) + (map #(hash-map :file (str "goog/" (nth % 0)) + :provides (parse-list (nth % 1)) + :requires (parse-list (nth % 2)) :group :goog)) (doall))))) diff --git a/src/cljs/clojure/browser/repl.cljs b/src/cljs/clojure/browser/repl.cljs index 410a4bf53..dec7ef4ac 100644 --- a/src/cljs/clojure/browser/repl.cljs +++ b/src/cljs/clojure/browser/repl.cljs @@ -16,7 +16,7 @@ :author "Bobby Calderwood and Alex Redington"} clojure.browser.repl (:require [goog.dom :as gdom] - [goog.labs.userAgent.browser :as gbrowser] + [goog.userAgent.product :as product] [clojure.browser.net :as net] [clojure.browser.event :as event] ;; repl-connection callback will receive goog.require('cljs.repl') @@ -35,10 +35,10 @@ ;; see goog.userAgent.product (defn get-ua-product [] (cond - (gbrowser/isSafari) :safari - (gbrowser/isChrome) :chrome - (gbrowser/isFirefox) :firefox - (gbrowser/isIE) :ie)) + product/SAFARI :safari + product/CHROME :chrome + product/FIREFOX :firefox + product/IE :ie)) (defn evaluate-javascript "Process a single block of JavaScript received from the server" From 9e46b35d8f34f00cbe7f276aff74925cb5883ce6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 10:57:29 -0400 Subject: [PATCH 1034/4033] update script/clean --- script/clean | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/clean b/script/clean index c9e906188..d041c9e9a 100755 --- a/script/clean +++ b/script/clean @@ -3,3 +3,5 @@ rm -rf closure rm -rf compilation rm -rf lib +rm -rf target +rm -rf builds From 6ff80dc2f309f961d2e363148b5c0da65ac320f9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 12:04:21 -0400 Subject: [PATCH 1035/4033] move everything to conform to expected contrib pom structure, update all scripts and files to account for reorg. update pom.template.xml with with bits copied from other AOTed contrib projects. --- .gitignore | 6 +- Clojurescript.iml | 22 +- bin/cljsc | 2 +- devnotes/day1.org | 6 +- pom.template.xml | 461 +++++++++++------- project.clj | 4 +- script/aot_core | 4 +- script/browser-repl | 2 +- script/build | 28 +- script/compile | 2 +- script/nashornrepljs | 2 +- script/noderepljs | 2 +- script/repl | 2 +- script/repljs | 2 +- script/test | 3 +- script/uberjar | 28 +- src/assembly/aot.xml | 18 + src/main/cljs/cljs/.DS_Store | Bin 0 -> 6148 bytes src/{ => main}/cljs/cljs/bootstrap_node.js | 0 src/{ => main}/cljs/cljs/core.cljs | 2 +- src/{ => main}/cljs/cljs/externs.js | 0 src/{ => main}/cljs/cljs/imul.js | 0 src/{ => main}/cljs/cljs/nodejs.cljs | 0 src/{ => main}/cljs/cljs/nodejs_externs.js | 0 src/{ => main}/cljs/cljs/nodejscli.cljs | 0 src/{ => main}/cljs/cljs/pprint.clj | 0 src/{ => main}/cljs/cljs/pprint.cljs | 0 src/{ => main}/cljs/cljs/reader.cljs | 0 src/{ => main}/cljs/cljs/repl.cljs | 0 src/{ => main}/cljs/cljs/source_map.cljs | 0 .../cljs/cljs/source_map/base64.cljs | 0 .../cljs/cljs/source_map/base64_vlq.cljs | 0 src/{ => main}/cljs/cljs/test.cljs | 0 src/{ => main}/cljs/clojure/browser/dom.cljs | 0 .../cljs/clojure/browser/event.cljs | 0 src/{ => main}/cljs/clojure/browser/net.cljs | 0 src/{ => main}/cljs/clojure/browser/repl.cljs | 0 .../cljs/clojure/core/reducers.cljs | 0 src/{ => main}/cljs/clojure/data.cljs | 0 src/{ => main}/cljs/clojure/reflect.cljs | 0 src/{ => main}/cljs/clojure/set.cljs | 0 src/{ => main}/cljs/clojure/string.cljs | 0 src/{ => main}/cljs/clojure/walk.cljs | 0 src/{ => main}/cljs/clojure/zip.cljs | 0 src/{clj => main/clojure}/cljs/analyzer.cljc | 0 .../clojure}/cljs/analyzer/api.clj | 0 .../clojure}/cljs/analyzer/utils.clj | 0 src/{clj => main/clojure}/cljs/build/api.clj | 0 src/{clj => main/clojure}/cljs/closure.clj | 4 +- src/{clj => main/clojure}/cljs/compiler.cljc | 0 .../clojure}/cljs/compiler/api.clj | 0 src/{clj => main/clojure}/cljs/core.clj | 0 src/{clj => main/clojure}/cljs/env.cljc | 0 src/{clj => main/clojure}/cljs/js_deps.clj | 0 src/{clj => main/clojure}/cljs/repl.cljc | 0 .../clojure}/cljs/repl/browser.clj | 0 .../clojure}/cljs/repl/nashorn.clj | 0 src/{clj => main/clojure}/cljs/repl/node.clj | 0 .../clojure}/cljs/repl/node_repl.js | 0 .../clojure}/cljs/repl/reflect.clj | 0 src/{clj => main/clojure}/cljs/repl/rhino.clj | 0 .../clojure}/cljs/repl/server.clj | 0 src/{clj => main/clojure}/cljs/source_map.clj | 0 .../clojure}/cljs/source_map/base64.clj | 0 .../clojure}/cljs/source_map/base64_vlq.clj | 0 .../clojure}/cljs/tagged_literals.clj | 0 src/{clj => main/clojure}/cljs/test.clj | 0 src/{clj => main/clojure}/cljs/util.cljc | 2 +- {test => src/test}/cljs/baz.cljs | 0 src/test/cljs/cljs/.DS_Store | Bin 0 -> 6148 bytes src/test/cljs/cljs/baz.cljs | 3 + .../test}/cljs/cljs/binding_test.cljs | 0 .../cljs/cljs/binding_test_other_ns.cljs | 0 {test => src/test}/cljs/cljs/core_test.cljs | 0 .../test/cljs}/cljs/foo/ns_shadow_test.cljs | 0 {test => src/test}/cljs/cljs/import_test.cljs | 0 .../test}/cljs/cljs/keyword_other.cljs | 0 .../test}/cljs/cljs/keyword_test.cljs | 0 {test => src/test}/cljs/cljs/letfn_test.cljs | 0 {test => src/test}/cljs/cljs/macro_test.cljs | 0 .../test}/cljs/cljs/macro_test/macros.clj | 0 {test => src/test}/cljs/cljs/ns_test.cljs | 0 {test => src/test}/cljs/cljs/ns_test/bar.cljs | 0 {test => src/test}/cljs/cljs/ns_test/foo.cljs | 0 {test => src/test}/cljs/cljs/pprint_test.clj | 0 {test => src/test}/cljs/cljs/pprint_test.cljs | 0 {test => src/test}/cljs/cljs/reader_test.cljs | 0 .../test}/cljs/cljs/reducers_test.cljs | 0 {test => src/test/cljs}/cljs/test_runner.cljs | 0 {test => src/test}/cljs/cljs/top_level.cljs | 0 .../test}/cljs/clojure/data_test.cljs | 0 .../test}/cljs/clojure/string_test.cljs | 0 src/test/cljs/foo/ns_shadow_test.cljs | 21 + {test => src/test/cljs}/hello.cljs | 0 src/test/cljs/test_runner.cljs | 37 ++ .../test/clojure}/cljs/analyzer_tests.clj | 2 +- .../test/clojure}/cljs/build_api_tests.clj | 0 .../test/clojure}/cljs/closure_tests.clj | 0 .../test/clojure}/cljs/compiler_tests.clj | 2 +- .../test/clojure}/cljs/preamble1.js | 0 .../test/clojure}/cljs/preamble2.js | 0 .../test/clojure}/cljs/repl_tests.clj | 0 102 files changed, 417 insertions(+), 250 deletions(-) create mode 100644 src/assembly/aot.xml create mode 100644 src/main/cljs/cljs/.DS_Store rename src/{ => main}/cljs/cljs/bootstrap_node.js (100%) rename src/{ => main}/cljs/cljs/core.cljs (99%) rename src/{ => main}/cljs/cljs/externs.js (100%) rename src/{ => main}/cljs/cljs/imul.js (100%) rename src/{ => main}/cljs/cljs/nodejs.cljs (100%) rename src/{ => main}/cljs/cljs/nodejs_externs.js (100%) rename src/{ => main}/cljs/cljs/nodejscli.cljs (100%) rename src/{ => main}/cljs/cljs/pprint.clj (100%) rename src/{ => main}/cljs/cljs/pprint.cljs (100%) rename src/{ => main}/cljs/cljs/reader.cljs (100%) rename src/{ => main}/cljs/cljs/repl.cljs (100%) rename src/{ => main}/cljs/cljs/source_map.cljs (100%) rename src/{ => main}/cljs/cljs/source_map/base64.cljs (100%) rename src/{ => main}/cljs/cljs/source_map/base64_vlq.cljs (100%) rename src/{ => main}/cljs/cljs/test.cljs (100%) rename src/{ => main}/cljs/clojure/browser/dom.cljs (100%) rename src/{ => main}/cljs/clojure/browser/event.cljs (100%) rename src/{ => main}/cljs/clojure/browser/net.cljs (100%) rename src/{ => main}/cljs/clojure/browser/repl.cljs (100%) rename src/{ => main}/cljs/clojure/core/reducers.cljs (100%) rename src/{ => main}/cljs/clojure/data.cljs (100%) rename src/{ => main}/cljs/clojure/reflect.cljs (100%) rename src/{ => main}/cljs/clojure/set.cljs (100%) rename src/{ => main}/cljs/clojure/string.cljs (100%) rename src/{ => main}/cljs/clojure/walk.cljs (100%) rename src/{ => main}/cljs/clojure/zip.cljs (100%) rename src/{clj => main/clojure}/cljs/analyzer.cljc (100%) rename src/{clj => main/clojure}/cljs/analyzer/api.clj (100%) rename src/{clj => main/clojure}/cljs/analyzer/utils.clj (100%) rename src/{clj => main/clojure}/cljs/build/api.clj (100%) rename src/{clj => main/clojure}/cljs/closure.clj (99%) rename src/{clj => main/clojure}/cljs/compiler.cljc (100%) rename src/{clj => main/clojure}/cljs/compiler/api.clj (100%) rename src/{clj => main/clojure}/cljs/core.clj (100%) rename src/{clj => main/clojure}/cljs/env.cljc (100%) rename src/{clj => main/clojure}/cljs/js_deps.clj (100%) rename src/{clj => main/clojure}/cljs/repl.cljc (100%) rename src/{clj => main/clojure}/cljs/repl/browser.clj (100%) rename src/{clj => main/clojure}/cljs/repl/nashorn.clj (100%) rename src/{clj => main/clojure}/cljs/repl/node.clj (100%) rename src/{clj => main/clojure}/cljs/repl/node_repl.js (100%) rename src/{clj => main/clojure}/cljs/repl/reflect.clj (100%) rename src/{clj => main/clojure}/cljs/repl/rhino.clj (100%) rename src/{clj => main/clojure}/cljs/repl/server.clj (100%) rename src/{clj => main/clojure}/cljs/source_map.clj (100%) rename src/{clj => main/clojure}/cljs/source_map/base64.clj (100%) rename src/{clj => main/clojure}/cljs/source_map/base64_vlq.clj (100%) rename src/{clj => main/clojure}/cljs/tagged_literals.clj (100%) rename src/{clj => main/clojure}/cljs/test.clj (100%) rename src/{clj => main/clojure}/cljs/util.cljc (98%) rename {test => src/test}/cljs/baz.cljs (100%) create mode 100644 src/test/cljs/cljs/.DS_Store create mode 100644 src/test/cljs/cljs/baz.cljs rename {test => src/test}/cljs/cljs/binding_test.cljs (100%) rename {test => src/test}/cljs/cljs/binding_test_other_ns.cljs (100%) rename {test => src/test}/cljs/cljs/core_test.cljs (100%) rename {test => src/test/cljs}/cljs/foo/ns_shadow_test.cljs (100%) rename {test => src/test}/cljs/cljs/import_test.cljs (100%) rename {test => src/test}/cljs/cljs/keyword_other.cljs (100%) rename {test => src/test}/cljs/cljs/keyword_test.cljs (100%) rename {test => src/test}/cljs/cljs/letfn_test.cljs (100%) rename {test => src/test}/cljs/cljs/macro_test.cljs (100%) rename {test => src/test}/cljs/cljs/macro_test/macros.clj (100%) rename {test => src/test}/cljs/cljs/ns_test.cljs (100%) rename {test => src/test}/cljs/cljs/ns_test/bar.cljs (100%) rename {test => src/test}/cljs/cljs/ns_test/foo.cljs (100%) rename {test => src/test}/cljs/cljs/pprint_test.clj (100%) rename {test => src/test}/cljs/cljs/pprint_test.cljs (100%) rename {test => src/test}/cljs/cljs/reader_test.cljs (100%) rename {test => src/test}/cljs/cljs/reducers_test.cljs (100%) rename {test => src/test/cljs}/cljs/test_runner.cljs (100%) rename {test => src/test}/cljs/cljs/top_level.cljs (100%) rename {test => src/test}/cljs/clojure/data_test.cljs (100%) rename {test => src/test}/cljs/clojure/string_test.cljs (100%) create mode 100644 src/test/cljs/foo/ns_shadow_test.cljs rename {test => src/test/cljs}/hello.cljs (100%) create mode 100644 src/test/cljs/test_runner.cljs rename {test/clj => src/test/clojure}/cljs/analyzer_tests.clj (99%) rename {test/clj => src/test/clojure}/cljs/build_api_tests.clj (100%) rename {test/clj => src/test/clojure}/cljs/closure_tests.clj (100%) rename {test/clj => src/test/clojure}/cljs/compiler_tests.clj (98%) rename {test/clj => src/test/clojure}/cljs/preamble1.js (100%) rename {test/clj => src/test/clojure}/cljs/preamble2.js (100%) rename {test/clj => src/test/clojure}/cljs/repl_tests.clj (100%) diff --git a/.gitignore b/.gitignore index 1f2d4a494..423696c78 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,6 @@ builds .cljs* node_modules nashorn_code_cache -src/cljs/cljs/core.aot.js -src/cljs/cljs/core.aot.js.map -src/cljs/cljs/core.cljs.cache.aot.edn +src/main/cljs/cljs/core.aot.js +src/main/cljs/cljs/core.aot.js.map +src/main/cljs/cljs/core.cljs.cache.aot.edn diff --git a/Clojurescript.iml b/Clojurescript.iml index 04d991e59..7d2352c03 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -6,10 +6,11 @@ - - - - + + + + + @@ -22,25 +23,22 @@ - + - - + + - + - + - - -
\ No newline at end of file diff --git a/bin/cljsc b/bin/cljsc index 28f744f1a..eb8428590 100755 --- a/bin/cljsc +++ b/bin/cljsc @@ -8,7 +8,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do +for next in lib/*: src/main/clojure: src/main/cljs: src/test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done diff --git a/devnotes/day1.org b/devnotes/day1.org index 07f57c376..4f54424c4 100644 --- a/devnotes/day1.org +++ b/devnotes/day1.org @@ -189,11 +189,11 @@ **** ... *** strategy for apply * What's where -** src/clj/cljs/compiler.clj +** src/main/clojure/cljs/compiler.clj *** the compiler -** src/clj/cljs/core.clj +** src/main/clojure/cljs/core.clj *** core macros -** src/cljs/core.cljs +** src/main/cljs/core.cljs *** core library * Todo ** separate org file diff --git a/pom.template.xml b/pom.template.xml index 61d28d699..6eee55b38 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -1,199 +1,290 @@ - 4.0.0 - org.clojure - clojurescript - - CLOJURESCRIPT_VERSION - jar - ClojureScript + 4.0.0 + org.clojure + clojurescript + + CLOJURESCRIPT_VERSION + jar + ClojureScript - https://github.com/clojure/clojurescript + https://github.com/clojure/clojurescript - - ClojureScript compiler and core runtime library. - + + ClojureScript compiler and core runtime library. + - - - Eclipse Public License 1.0 - http://opensource.org/licenses/eclipse-1.0.php - repo - - + + + Eclipse Public License 1.0 + http://opensource.org/licenses/eclipse-1.0.php + repo + + - - - org.clojure - clojure - 1.7.0-beta2 - - - com.google.javascript - closure-compiler - v20150505 - - - org.clojure - google-closure-library - 0.0-20150505-021ed5b3 - - - org.clojure - data.json - 0.2.6 - - - org.mozilla - rhino - 1.7R5 - - - org.clojure - tools.reader - 0.9.2 - - + + + org.clojure + clojure + 1.7.0-beta2 + + + com.google.javascript + closure-compiler + v20150505 + + + org.clojure + google-closure-library + 0.0-20150505-021ed5b3 + + + org.clojure + data.json + 0.2.6 + + + org.mozilla + rhino + 1.7R5 + + + org.clojure + tools.reader + 0.9.2 + + - - Aaron Bedra - Alan Dipert - Alan Malloy - Alen Ribic - Alex Redington - Bobby Calderwood - Brandon Bloom - Brenton Ashworth - Chris Houser - Christopher Redinger - Creighton Kirkendall - David Nolen - Devin Walters - Eric Thorsen - Frank Failla - Hubert Iwaniuk - Hugo Duncan - Jess Martin - John Li - Jonas Enlund - Juergen Hoetzel - Kevin J. Lynagh - Laszlo Toeroek - Luke VanderHart - Michael Fogus - Michał Marczyk - Moritz Ulrich - Nicola Mometto - Paul Michael Bauer - Rich Hickey - Roman Gonzalez - Russ Olsen - Stuart Halloway - Stuart Sierra - Takahiro Hozumi - Thomas Scheiblauer - Tom Hickey - Wilkes Joiner - + + Aaron Bedra + Alan Dipert + Alan Malloy + Alen Ribic + Alex Redington + Bobby Calderwood + Brandon Bloom + Brenton Ashworth + Chris Houser + Christopher Redinger + Creighton Kirkendall + David Nolen + Devin Walters + Eric Thorsen + Frank Failla + Hubert Iwaniuk + Hugo Duncan + Jess Martin + John Li + Jonas Enlund + Juergen Hoetzel + Kevin J. Lynagh + Laszlo Toeroek + Luke VanderHart + Michael Fogus + Michał Marczyk + Moritz Ulrich + Nicola Mometto + Paul Michael Bauer + Rich Hickey + Roman Gonzalez + Russ Olsen + Stuart Halloway + Stuart Sierra + Takahiro Hozumi + Thomas Scheiblauer + Tom Hickey + Wilkes Joiner + - - scm:git:git://github.com/clojure/clojurescript.git - scm:git:git@github.com:clojure/clojurescript.git - https://github.com/clojure/clojurescript - + + scm:git:git://github.com/clojure/clojurescript.git + scm:git:git@github.com:clojure/clojurescript.git + https://github.com/clojure/clojurescript + - - org.sonatype.oss - oss-parent - 7 - + + org.clojure + pom.contrib + 0.1.2 + - - UTF-8 - src/clj - src/cljs - + - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.5 - - - add-clojure-source-dirs - generate-sources - - add-source - add-resource - - - - ${clojure.source.dir} - ${cljs.source.dir} - - - - ${clojure.source.dir} - - - ${cljs.source.dir} - - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.4 - - Clojure/core - - - - + + UTF-8 + src/main/clojure + src/main/cljs + - - - sonatype-oss-release - - + - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.4.4 - - - default-deploy - deploy - - - deploy - - - - - - https://oss.sonatype.org/ - - sonatype-nexus-staging - - + + + org.codehaus.mojo + build-helper-maven-plugin + 1.5 + + + add-clojure-source-dirs + generate-sources + + add-source + add-resource + + + + ${clojure.source.dir} + ${cljs.source.dir} + + + + ${clojure.source.dir} + + + ${cljs.source.dir} + + + + + + + + com.theoryinpractise + clojure-maven-plugin + 1.3.13 + + false + + + + clojure-compile + compile + + compile + + + true + + cljs.util + cljs.env + cljs.js-deps + cljs.core + cljs.source-map.base64 + cljs.source-map.base64-vlq + cljs.source-map + cljs.analyzer + cljs.analyzer.utils + cljs.compiler + cljs.closure + cljs.tagged-literals + cljs.test + cljs.analyzer.api + cljs.build.api + cljs.compiler.api + cljs.repl + cljs.repl.browser + cljs.repl.nashorn + cljs.repl.node + cljs.repl.reflect + cljs.repl.rhino + cljs.repl.server + + + + + + + maven-jar-plugin + 2.4 + + + default-jar + package + + jar + + + + **/*.clj + **/*.cljc + **/*.cljs + + + + + + + maven-assembly-plugin + 2.4 + + + aot-jar + package + + single + + + + src/assembly/aot.xml + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.4 + + Clojure/core + + - - - + + + + + sonatype-oss-release + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.4.4 + + + default-deploy + deploy + + + deploy + + + + + + https://oss.sonatype.org/ + + sonatype-nexus-staging + + + + + + diff --git a/project.clj b/project.clj index 8dd877d9c..c013a3bcf 100644 --- a/project.clj +++ b/project.clj @@ -5,8 +5,8 @@ :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :jvm-opts ^:replace ["-Xmx512m" "-server"] - :source-paths ["src/clj"] - :resource-paths ["src/cljs"] + :source-paths ["src/main/clojure"] + :resource-paths ["src/main/cljs"] :test-paths ["test/clj"] :dependencies [[org.clojure/clojure "1.7.0-beta2"] [org.clojure/data.json "0.2.6"] diff --git a/script/aot_core b/script/aot_core index ce828fcaf..a5762c4ab 100755 --- a/script/aot_core +++ b/script/aot_core @@ -10,9 +10,9 @@ CLJS_CP=`cat $CP_FILE` # For Hudson server if [ "$HUDSON" = "true" ]; then - $JAVA_HOME/bin/java -server -cp "$CLJS_CP:src/clj:src/cljs" clojure.main script/aot.clj + $JAVA_HOME/bin/java -server -cp "$CLJS_CP:src/main/clojure:src/main/cljs" clojure.main script/aot.clj else - java -server -cp "$CLJS_CP:src/clj:src/cljs" clojure.main script/aot.clj + java -server -cp "$CLJS_CP:src/main/clojure:src/main/cljs" clojure.main script/aot.clj fi diff --git a/script/browser-repl b/script/browser-repl index 3736b9b70..34b0de55a 100755 --- a/script/browser-repl +++ b/script/browser-repl @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/clj: src/cljs: test/cljs; do +for next in lib/*: src/main/clojure: src/main/cljs: test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done diff --git a/script/build b/script/build index 880020454..344394e21 100755 --- a/script/build +++ b/script/build @@ -29,26 +29,26 @@ TAG=r$REVISION sed -e s/CLOJURESCRIPT_VERSION/0.0-$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` -sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/util.cljc > $COMP_FILE -mv $COMP_FILE src/clj/cljs/util.cljc +sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE +mv $COMP_FILE src/main/clojure/cljs/util.cljc CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` -sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/cljs/cljs/core.cljs > $CLJS_FILE -mv $CLJS_FILE src/cljs/cljs/core.cljs +sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/main/cljs/cljs/core.cljs > $CLJS_FILE +mv $CLJS_FILE src/main/cljs/cljs/core.cljs -rm -f src/cljs/cljs/core.aot.js -rm -f src/cljs/cljs/core.aot.js.map -rm -f src/cljs/cljs/core.cljs.cache.aot.edn +rm -f src/main/cljs/cljs/core.aot.js +rm -f src/main/cljs/cljs/core.aot.js.map +rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn ./script/aot_core AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` -sed -e 's/0.0-0000/0.0-$REVISION/' src/cljs/cljs/core.aot.js > $AOT_FILE -mv $AOT_FILE src/cljs/cljs/core.aot.js +sed -e 's/0.0-0000/0.0-$REVISION/' src/main/cljs/cljs/core.aot.js > $AOT_FILE +mv $AOT_FILE src/main/cljs/cljs/core.aot.js AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` -sed -e 's/0.0-0000/0.0-$REVISION/' src/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE -mv $AOT_CACHE_FILE src/cljs/cljs/core.cljs.cache.aot.edn +sed -e 's/0.0-0000/0.0-$REVISION/' src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE +mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server if [ "$HUDSON" = "true" ]; then @@ -63,6 +63,6 @@ else mvn clean install fi -rm -f src/cljs/cljs/core.aot.js -rm -f src/cljs/cljs/core.aot.js.map -rm -f src/cljs/cljs/core.cljs.cache.aot.edn +rm -f src/main/cljs/cljs/core.aot.js +rm -f src/main/cljs/cljs/core.aot.js.map +rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn diff --git a/script/compile b/script/compile index d9bf5d524..be4beb7f6 100755 --- a/script/compile +++ b/script/compile @@ -2,7 +2,7 @@ rm -f core.js -java -server -Xmx2G -Xms2G -Xmn256m -cp 'lib/*:src/clj:src/cljs' clojure.main - <out/core-test.js -bin/cljsc test "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js +bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" diff --git a/script/uberjar b/script/uberjar index d5ff740c1..dd24d8aaa 100755 --- a/script/uberjar +++ b/script/uberjar @@ -20,30 +20,30 @@ REVISION=${REVISION:5} # drop the first 5 characters REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` -sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/clj/cljs/util.cljc > $COMP_FILE -mv $COMP_FILE src/clj/cljs/util.cljc +sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE +mv $COMP_FILE src/main/clojure/cljs/util.cljc CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` -sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/cljs/cljs/core.cljs > $CLJS_FILE -mv $CLJS_FILE src/cljs/cljs/core.cljs +sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/main/cljs/cljs/core.cljs > $CLJS_FILE +mv $CLJS_FILE src/main/cljs/cljs/core.cljs -rm -f src/cljs/cljs/core.aot.js -rm -f src/cljs/cljs/core.aot.js.map -rm -f src/cljs/cljs/core.cljs.cache.aot.edn +rm -f src/main/cljs/cljs/core.aot.js +rm -f src/main/cljs/cljs/core.aot.js.map +rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn ./script/aot_core AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` -sed -e "s/0.0-0000/0.0-$REVISION/" src/cljs/cljs/core.aot.js > $AOT_FILE -mv $AOT_FILE src/cljs/cljs/core.aot.js +sed -e "s/0.0-0000/0.0-$REVISION/" src/main/cljs/cljs/core.aot.js > $AOT_FILE +mv $AOT_FILE src/main/cljs/cljs/core.aot.js AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` -sed -e "s/0.0-0000/0.0-$REVISION/" src/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE -mv $AOT_CACHE_FILE src/cljs/cljs/core.cljs.cache.aot.edn +sed -e "s/0.0-0000/0.0-$REVISION/" src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE +mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn lein uberjar clojure.main mv target/clojurescript-0.0-SNAPSHOT-standalone.jar target/cljs.jar -rm -f src/cljs/cljs/core.aot.js -rm -f src/cljs/cljs/core.aot.js.map -rm -f src/cljs/cljs/core.cljs.cache.aot.edn \ No newline at end of file +rm -f src/main/cljs/cljs/core.aot.js +rm -f src/main/cljs/cljs/core.aot.js.map +rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn \ No newline at end of file diff --git a/src/assembly/aot.xml b/src/assembly/aot.xml new file mode 100644 index 000000000..7ca20f937 --- /dev/null +++ b/src/assembly/aot.xml @@ -0,0 +1,18 @@ + + aot + + jar + + false + + + src/resources + / + true + + + target/classes + / + + + \ No newline at end of file diff --git a/src/main/cljs/cljs/.DS_Store b/src/main/cljs/cljs/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Fri, 8 May 2015 12:53:52 -0400 Subject: [PATCH 1036/4033] try to force JDK 7 with maven-compiler-plugin --- pom.template.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pom.template.xml b/pom.template.xml index 6eee55b38..80321fd2d 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -244,6 +244,15 @@ Clojure/core + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + + From ae74e2ce1264a2233bf95c21bd60041ae94043d6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 13:03:59 -0400 Subject: [PATCH 1037/4033] remove accidentally committed version numbers --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/util.cljc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 192a7e856..44fa22cf4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -13,7 +13,7 @@ (:import goog.string.StringBuffer)) ;; next line is auto-generated by the build-script - Do not edit! -(def *clojurescript-version* "0.0-3247") +(def *clojurescript-version*) (def *unchecked-if* false) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index efdc9c786..84810fce6 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -15,7 +15,7 @@ [java.net URL])) ;; next line is auto-generated by the build-script - Do not edit! -(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier 3247}) +(def ^:dynamic *clojurescript-version*) (defn ^String clojurescript-version "Returns clojurescript version as a printable string." From d10a26093625f28c47d356d600ac6e9cce8bf8eb Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 13:19:42 -0400 Subject: [PATCH 1038/4033] fork the compiler --- pom.template.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.template.xml b/pom.template.xml index 80321fd2d..b5d25e5a3 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -251,6 +251,7 @@ 1.7 1.7 + true From 8f817ac2171530c93162baf351188c2fe78b611b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 13:25:59 -0400 Subject: [PATCH 1039/4033] remove the fork --- pom.template.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index b5d25e5a3..80321fd2d 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -251,7 +251,6 @@ 1.7 1.7 - true From ccd6409639bb9b9087a0aa098ac6cb947622c7ea Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 14:47:18 -0400 Subject: [PATCH 1040/4033] support AOT compilation under Java 7 by conditionally compiling Nashorn support --- src/main/clojure/cljs/repl/nashorn.clj | 373 +++++++++++++------------ src/main/clojure/cljs/util.cljc | 8 + 2 files changed, 195 insertions(+), 186 deletions(-) diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index 06b8f9ddd..b7bd1ec83 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -12,194 +12,195 @@ [clojure.stacktrace] [cljs.analyzer :as ana] [cljs.env :as env] + [cljs.util :as util] [cljs.repl :as repl] [cljs.compiler :as comp] [cljs.closure :as closure]) (:import [java.io File] [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory] - [jdk.nashorn.api.scripting NashornException])) - -;; Nashorn Clojurescript repl binding. -;; -;; Uses the Nashorn load() function to load Javascript files into the script engine. -;; -;; Nashorn's load() function docs: -;; http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/shell.html - -(comment - (ns init-repl-test - (:require [cljs.repl] - [cljs.repl.nashorn])) - - (cljs.repl/repl (cljs.repl.nashorn/repl-env) - :output-dir "resources/public/compiled" - :cache-analysis true) - ) - -;; Implementation - -(defn create-engine - ([] (create-engine nil)) - ([{:keys [code-cache] :or {code-cache true}}] - (let [args (when code-cache ["-pcc"]) - factories (.getEngineFactories (ScriptEngineManager.)) - factory (get (zipmap (map #(.getEngineName %) factories) factories) "Oracle Nashorn")] - (if-let [engine (if-not (empty? args) - (.getScriptEngine ^ScriptEngineFactory factory (into-array args)) - (.getScriptEngine ^ScriptEngineFactory factory))] - (let [context (.getContext engine)] - (.setWriter context *out*) - (.setErrorWriter context *err*) - engine) - (throw (IllegalArgumentException. - "Cannot find the Nashorn script engine, use a JDK version 8 or higher.")))))) - -(defn eval-str [^ScriptEngine engine ^String s] - (.eval engine s)) - -(defn eval-resource - "Evaluate a file on the classpath in the engine." - [engine path debug] - (let [r (io/resource path)] - (eval-str engine (slurp r)) - (when debug (println "loaded: " path)))) - -(defn init-engine [engine output-dir debug] - (eval-resource engine "goog/base.js" debug) - (eval-resource engine "goog/deps.js" debug) - (eval-str engine "var global = this") ; required by React - (eval-str engine - (format - (str "var nashorn_load = function(path) {" - " var outputPath = \"%s\" + \"/\" + path;" - (when debug " print(\"loading: \" + outputPath) ; ") - " load(outputPath);" - "};") - output-dir)) - (eval-str engine - (str "goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {" - " nashorn_load(\"goog/\" + path);" - " return true;" - "};")) - (eval-str engine "goog.global.isProvided_ = function(name) { return false; };") - engine) - -(defn load-js-file [engine file] - (eval-str engine (format "nashorn_load(\"%s\");" file))) - -;; Create a minimal build of Clojurescript from the core library. -;; Copied from clj.cljs.repl.node. -(defn bootstrap-repl [engine output-dir opts] - (env/ensure - (let [deps-file ".nashorn_repl_deps.js" - core (io/resource "cljs/core.cljs") - core-js (closure/compile core - (assoc opts - :output-file (closure/src-file->target-file core))) - deps (closure/add-dependencies opts core-js)] - ;; output unoptimized code and the deps file - ;; for all compiled namespaces - (apply closure/output-unoptimized - (assoc opts :output-to (.getPath (io/file output-dir deps-file))) - deps) - ;; load the deps file so we can goog.require cljs.core etc. - (load-js-file engine deps-file)))) - -(defn load-ns [engine ns] - (eval-str engine - (format "goog.require(\"%s\");" (comp/munge (first ns))))) - -;; Nashorn script stacktraces have a relative path which includes the output-dir -(defn- strip-file-name [^String file-name output-dir] - (let [with-slash (str output-dir "/")] - (if (.startsWith file-name with-slash) - (string/replace-first file-name with-slash "") - file-name))) - -(def repl-filename "") - -(defrecord NashornEnv [engine debug] - repl/IReplEnvOptions - (-repl-options [this] - {:output-dir ".cljs_nashorn_repl"}) - repl/IJavaScriptEnv - (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] - (init-engine engine output-dir debug) - (let [env (ana/empty-env)] - (if output-to - (load-js-file engine output-to) - (bootstrap-repl engine output-dir opts)) - (repl/evaluate-form this env repl-filename - '(do - (.require js/goog "cljs.core") - (set! *print-newline* false) - (set! *print-fn* js/print))) - ;; monkey-patch goog.isProvided_ to suppress useless errors - (repl/evaluate-form this env repl-filename - '(set! js/goog.isProvided_ (fn [ns] false))) - ;; monkey-patch goog.require to be more sensible - (repl/evaluate-form this env repl-filename - '(do - (set! *loaded-libs* #{"cljs.core"}) - (set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) - (-evaluate [{engine :engine :as this} filename line js] - (when debug (println "Evaluating: " js)) - (try - {:status :success - :value (if-let [r (eval-str engine js)] (.toString r) "")} - (catch ScriptException e - (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] - {:status :exception - :value (.getMessage root-cause) - :stacktrace (NashornException/getScriptStackString root-cause)})) - (catch Throwable e - (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] - {:status :exception - :value (.getMessage root-cause) - :stacktrace - (apply str - (interpose "\n" - (map str - (.getStackTrace root-cause))))})))) - (-load [{engine :engine :as this} ns url] - (load-ns engine ns)) - (-tear-down [this]) - repl/IParseStacktrace - (-parse-stacktrace [this frames-str ret {output-dir :output-dir}] - (vec - (map - (fn [frame-str] - (let [frame-str (string/replace frame-str #"\s+at\s+" "") - [function file-and-line] (string/split frame-str #"\s+") - [file-part line-part] (string/split file-and-line #":")] - {:file (string/replace (.substring file-part 1) - (str output-dir File/separator) "") - :function function - :line (Integer/parseInt - (.substring line-part 0 (dec (.length line-part)))) - :column 0})) - (string/split frames-str #"\n")))) - repl/IParseError - (-parse-error [_ err _] - (update-in err [:stacktrace] - (fn [st] - (string/join "\n" (drop 1 (string/split st #"\n"))))))) - -(defn repl-env* [{:keys [debug] :as opts}] - (let [engine (create-engine opts)] - (merge - (NashornEnv. engine debug) - opts))) - -(defn repl-env - "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." - [& {:as opts}] - (repl-env* opts)) - -(defn -main [] - (repl/repl (repl-env))) + [com.google.common.base Throwables])) + +(util/compile-if (Class/forName "jdk.nashorn.api.scripting.NashornException") + (do + (import 'jdk.nashorn.api.scripting.NashornException) + ;; Implementation + + (defn create-engine + ([] (create-engine nil)) + ([{:keys [code-cache] :or {code-cache true}}] + (let [args (when code-cache ["-pcc"]) + factories (.getEngineFactories (ScriptEngineManager.)) + factory (get (zipmap (map #(.getEngineName %) factories) factories) "Oracle Nashorn")] + (if-let [engine (if-not (empty? args) + (.getScriptEngine ^ScriptEngineFactory factory (into-array args)) + (.getScriptEngine ^ScriptEngineFactory factory))] + (let [context (.getContext engine)] + (.setWriter context *out*) + (.setErrorWriter context *err*) + engine) + (throw (IllegalArgumentException. + "Cannot find the Nashorn script engine, use a JDK version 8 or higher.")))))) + + (defn eval-str [^ScriptEngine engine ^String s] + (.eval engine s)) + + (defn eval-resource + "Evaluate a file on the classpath in the engine." + [engine path debug] + (let [r (io/resource path)] + (eval-str engine (slurp r)) + (when debug (println "loaded: " path)))) + + (defn init-engine [engine output-dir debug] + (eval-resource engine "goog/base.js" debug) + (eval-resource engine "goog/deps.js" debug) + (eval-str engine "var global = this") ; required by React + (eval-str engine + (format + (str "var nashorn_load = function(path) {" + " var outputPath = \"%s\" + \"/\" + path;" + (when debug " print(\"loading: \" + outputPath) ; ") + " load(outputPath);" + "};") + output-dir)) + (eval-str engine + (str "goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {" + " nashorn_load(\"goog/\" + path);" + " return true;" + "};")) + (eval-str engine "goog.global.isProvided_ = function(name) { return false; };") + engine) + + (defn load-js-file [engine file] + (eval-str engine (format "nashorn_load(\"%s\");" file))) + + ;; Create a minimal build of Clojurescript from the core library. + ;; Copied from clj.cljs.repl.node. + (defn bootstrap-repl [engine output-dir opts] + (env/ensure + (let [deps-file ".nashorn_repl_deps.js" + core (io/resource "cljs/core.cljs") + core-js (closure/compile core + (assoc opts + :output-file (closure/src-file->target-file core))) + deps (closure/add-dependencies opts core-js)] + ;; output unoptimized code and the deps file + ;; for all compiled namespaces + (apply closure/output-unoptimized + (assoc opts :output-to (.getPath (io/file output-dir deps-file))) + deps) + ;; load the deps file so we can goog.require cljs.core etc. + (load-js-file engine deps-file)))) + + (defn load-ns [engine ns] + (eval-str engine + (format "goog.require(\"%s\");" (comp/munge (first ns))))) + + ;; Nashorn script stacktraces have a relative path which includes the output-dir + (defn- strip-file-name [^String file-name output-dir] + (let [with-slash (str output-dir "/")] + (if (.startsWith file-name with-slash) + (string/replace-first file-name with-slash "") + file-name))) + + (def repl-filename "") + + (defrecord NashornEnv [engine debug] + repl/IReplEnvOptions + (-repl-options [this] + {:output-dir ".cljs_nashorn_repl"}) + repl/IJavaScriptEnv + (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] + (init-engine engine output-dir debug) + (let [env (ana/empty-env)] + (if output-to + (load-js-file engine output-to) + (bootstrap-repl engine output-dir opts)) + (repl/evaluate-form this env repl-filename + '(do + (.require js/goog "cljs.core") + (set! *print-newline* false) + (set! *print-fn* js/print))) + ;; monkey-patch goog.isProvided_ to suppress useless errors + (repl/evaluate-form this env repl-filename + '(set! js/goog.isProvided_ (fn [ns] false))) + ;; monkey-patch goog.require to be more sensible + (repl/evaluate-form this env repl-filename + '(do + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) + (-evaluate [{engine :engine :as this} filename line js] + (when debug (println "Evaluating: " js)) + (try + {:status :success + :value (if-let [r (eval-str engine js)] (.toString r) "")} + (catch ScriptException e + (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] + {:status :exception + :value (.getMessage root-cause) + :stacktrace (NashornException/getScriptStackString root-cause)})) + (catch Throwable e + (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] + {:status :exception + :value (.getMessage root-cause) + :stacktrace + (apply str + (interpose "\n" + (map str + (.getStackTrace root-cause))))})))) + (-load [{engine :engine :as this} ns url] + (load-ns engine ns)) + (-tear-down [this]) + repl/IParseStacktrace + (-parse-stacktrace [this frames-str ret {output-dir :output-dir}] + (vec + (map + (fn [frame-str] + (let [frame-str (string/replace frame-str #"\s+at\s+" "") + [function file-and-line] (string/split frame-str #"\s+") + [file-part line-part] (string/split file-and-line #":")] + {:file (string/replace (.substring file-part 1) + (str output-dir File/separator) "") + :function function + :line (Integer/parseInt + (.substring line-part 0 (dec (.length line-part)))) + :column 0})) + (string/split frames-str #"\n")))) + repl/IParseError + (-parse-error [_ err _] + (update-in err [:stacktrace] + (fn [st] + (string/join "\n" (drop 1 (string/split st #"\n"))))))) + + (defn repl-env* [{:keys [debug] :as opts}] + (let [engine (create-engine opts)] + (merge + (NashornEnv. engine debug) + opts))) + + (defn repl-env + "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." + [& {:as opts}] + (repl-env* opts)) + + (defn -main [] + (repl/repl (repl-env)))) + (do + (defn repl-env* [{:keys [debug] :as opts}] + (throw (ex-info "Nashorn not supported" {:type :repl-error}))) + + (defn repl-env + "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." + [& {:as opts}] + (throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error}))) + + (defn -main [] + (throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error}))))) + + + diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 84810fce6..64e52d86c 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -173,3 +173,11 @@ (debug-prn (str ~msg ", elapsed time:") (/ (double (- (. System (nanoTime)) start#)) 1000000.0) "msecs") ret#) ~expr))) + +(defmacro compile-if + ([exp then] `(compile-if ~exp ~then nil)) + ([exp then else] + (if (try (eval exp) + (catch Throwable _ false)) + `(do ~then) + `(do ~else)))) \ No newline at end of file From c04e39bbbfd13cf04e26f20944917b416fcfcf88 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 14:51:46 -0400 Subject: [PATCH 1041/4033] switch back to sonatype parent pom --- pom.template.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 80321fd2d..662297943 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -101,19 +101,19 @@ https://github.com/clojure/clojurescript + - UTF-8 From d321bde5b057eac827a37154f01cb7b8f68f1997 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 15:00:08 -0400 Subject: [PATCH 1042/4033] bump nexus staging maven plugin --- pom.template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 662297943..e33389b66 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -274,7 +274,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.4.4 + 1.6.5 default-deploy From c8384d29d1f49bd757cee45c8979a6d8abfffe89 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 15:12:57 -0400 Subject: [PATCH 1043/4033] 0.0-3255 --- README.md | 6 +++--- changes.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 541b7861b..f26ea77cb 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3211 +Latest stable release: 0.0-3255 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3211"] +[org.clojure/clojurescript "0.0-3255"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3211 org.clojure clojurescript - 0.0-3211 + 0.0-3255 ``` diff --git a/changes.md b/changes.md index 1aa7e55e0..0e919fa2e 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,35 @@ +## 0.0-3255 + +### Changes +* Update Closure Library dependency +* CLJS-1252: Update Closure Compiler Dependency to v20150505 +* .clj -> .cljc for important analysis / compilation bits +* add public cljs.compiler.api namespace +* CLJS-1224: cljs.repl: Memoize stack frame mapping +* depend on tools.reader 0.9.2 + +### Enhancements +* add cljs.pprint/pp macro +* CLJS-710: port clojure.pprint +* CLJS-1178: Compiler does not know Math ns is not not-native +* add getBasis methods to deftype and defrecord ctors a la Clojure JVM +* support ^long and ^double type hints + +### Fixes +* fix cljs-1198 async testing regression +* CLJS-1254: Update REPL browser agent detection CLJS-1253: Create/Use new Closure Library Release +* CLJS-1225: Variadic function with same name as parent function gives runtime error in advanced compile mode. +* CLJS-1246: Add cljs.core/record? predicate. +* CLJS-1239: Make eduction variadic. +* CLJS-1244: tagged-literal precondition check missing wrapping vector +* CLJS-1243: Add TaggedLiteral type & related fns +* CLJS-1240: Add cljs.core/var? +* CLJS-1214: :arglists meta has needless quoting CLJS-1232: bad arglists for doc, regression +* CLJS-1212: Error in set ctor for > 8-entry map literal +* CLJS-1218: Syntax quoting an alias created with :require-macros throws ClassCastException +* CLJS-1213: cljs.analyzer incorrectly marks all defs as tests when eliding test metadata +* CLJS-742: Compilation with :output-file option set fails + ## 0.0-3211 ### Changes From 3ca1fade38311c60129ed949685c89c581a82044 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 19:31:55 -0400 Subject: [PATCH 1044/4033] .DS_Store ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 423696c78..802fe2bed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *~ .idea /.DS_Store +.DS_Store /classes /lib /target From a9700e5b6d1b63ddc6890478e7faac9d37a2b991 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 May 2015 19:43:57 -0400 Subject: [PATCH 1045/4033] CLJS-1257: find-doc regression check for nil before calling clojure.name --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 78ecf46fb..596241510 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1095,7 +1095,7 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (map (fn [m] (update-in (select-keys m [:ns :name :doc :forms :arglists :macro :url]) - [:name] clojure.core/name)) + [:name] #(if-not (nil? %) (clojure.core/name %) %))) (sort-by :name (vals (ana-api/ns-interns ns))))) (ana-api/all-ns)) (map #(select-keys (ana-api/find-ns %) [:name :doc]) (ana-api/all-ns)) From d0bf12f24f3455fdc45962206580ab50ca907638 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 May 2015 09:36:16 -0400 Subject: [PATCH 1046/4033] CLJS-1258: stack trace mapping does not appear to work with :asset-path cljs.repl.browser - parse-file take repl-env as first arg, use supplied :host and :port to compute base url. if :asset-path supplied by opts use this over output-directory to remove leading path. - chrome-st-el->frame thread repl-env - firefox-st-el->frame thread repl-env - safari-st-el->frame thread repl-env - BrowserEnv fix malformed body, - repl-env* supply default :host --- src/main/clojure/cljs/repl/browser.clj | 93 ++++++++++++++------------ 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index ecb976639..bbfd325aa 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -213,23 +213,26 @@ (cond-> column (.endsWith column ")") (string/replace ")" "")))])) -(defn parse-file [file opts] - (if (re-find #"http://localhost:9000/" file) - (-> file - (string/replace #"http://localhost:9000/" "") - (string/replace (Pattern/compile (str "^" (util/output-directory opts) "/")) "")) - (if-let [asset-root (:asset-root opts)] - (string/replace file asset-root "") - (throw - (ex-info (str "Could not relativize URL " file) - {:type :parse-stacktrace - :reason :relativize-url}))))) +(defn parse-file [{:keys [host port] :as repl-env} file {:keys [asset-path] :as opts}] + (let [base-url-pattern (Pattern/compile (str "http://" host ":" port "/"))] + (if (re-find base-url-pattern file) + (-> file + (string/replace base-url-pattern "") + (string/replace + (Pattern/compile + (str "^" (or asset-path (util/output-directory opts)) "/")) "")) + (if-let [asset-root (:asset-root opts)] + (string/replace file asset-root "") + (throw + (ex-info (str "Could not relativize URL " file) + {:type :parse-stacktrace + :reason :relativize-url})))))) ;; ----------------------------------------------------------------------------- ;; Chrome Stacktrace (defn chrome-st-el->frame - [st-el opts] + [repl-env st-el opts] (let [xs (-> st-el (string/replace #"\s+at\s+" "") (string/split #"\s+")) @@ -238,7 +241,7 @@ [(first xs) (last xs)]) [file line column] (parse-file-line-column flc)] (if (and file function line column) - {:file (parse-file file opts) + {:file (parse-file repl-env file opts) :function (string/replace function #"Object\." "") :line line :column column} @@ -249,7 +252,7 @@ :column nil})))) (comment - (chrome-st-el->frame + (chrome-st-el->frame {:host "localhost" :port 9000} "\tat cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34)" {}) ) @@ -259,12 +262,12 @@ string/split-lines (drop-while #(.startsWith % "Error")) (take-while #(not (.startsWith % " at eval"))) - (map #(chrome-st-el->frame % opts)) + (map #(chrome-st-el->frame repl-env % opts)) (remove nil?) vec)) (comment - (parse-stacktrace nil + (parse-stacktrace {:host "localhost" :port 9000} "Error: 1 is not ISeqable at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4258:8) at Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4288:19) @@ -279,7 +282,7 @@ {:ua-product :chrome} nil) - (parse-stacktrace nil + (parse-stacktrace {:host "localhost" :port 9000} "Error: 1 is not ISeqable at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4259:8) at Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4289:19) @@ -299,13 +302,13 @@ ;; Safari Stacktrace (defn safari-st-el->frame - [st-el opts] + [repl-env st-el opts] (let [[function flc] (if (re-find #"@" st-el) (string/split st-el #"@") [nil st-el]) [file line column] (parse-file-line-column flc)] (if (and file function line column) - {:file (parse-file file opts) + {:file (parse-file repl-env file opts) :function function :line line :column column} @@ -316,8 +319,11 @@ :column nil})))) (comment - (safari-st-el->frame + (safari-st-el->frame {:host "localhost" :port 9000} "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17" {}) + + (safari-st-el->frame {:host "localhost" :port 9000} + "cljs$core$seq@http://localhost:9000/js/cljs/core.js:4259:17" {:asset-path "js"}) ) (defmethod parse-stacktrace :safari @@ -327,7 +333,7 @@ (drop-while #(.startsWith % "Error")) (take-while #(not (.startsWith % "eval code"))) (remove string/blank?) - (map #(safari-st-el->frame % opts)) + (map #(safari-st-el->frame repl-env % opts)) (remove nil?) vec)) @@ -382,13 +388,13 @@ http://localhost:9000/out/goog/events/events.js:276:42" (string/replace #"\/" "")))) (defn firefox-st-el->frame - [st-el opts] + [repl-env st-el opts] (let [[function flc] (if (re-find #"@" st-el) (string/split st-el #"@") [nil st-el]) [file line column] (parse-file-line-column flc)] (if (and file function line column) - {:file (parse-file file opts) + {:file (parse-file repl-env file opts) :function (firefox-clean-function function) :line line :column column} @@ -399,19 +405,19 @@ http://localhost:9000/out/goog/events/events.js:276:42" :column nil})))) (comment - (firefox-st-el->frame + (firefox-st-el->frame {:host "localhost" :port 9000} "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4258:8" {}) - (firefox-st-el->frame + (firefox-st-el->frame {:host "localhost" :port 9000} "cljs.core.mapframe + (firefox-st-el->frame {:host "localhost" :port 9000} "cljs.core.mapframe + (firefox-st-el->frame {:host "localhost" :port 9000} "cljs.core.pr_strframe + (firefox-st-el->frame {:host "localhost" :port 9000} "cljs.core.pr_str eval") -1)) (remove string/blank?) - (map #(firefox-st-el->frame % opts)) + (map #(firefox-st-el->frame repl-env % opts)) (remove nil?) vec)) @@ -502,6 +508,18 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" repl/IJavaScriptEnv (-setup [this opts] (setup this opts)) + (-evaluate [this _ _ js] + (binding [browser-state (:browser-state this) + ordering (:ordering this) + es (:es this) + server/state (:server-state this)] + (browser-eval js))) + (-load [this provides url] + (load-javascript this provides url)) + (-tear-down [this] + (binding [server/state (:server-state this)] + (server/stop)) + (.shutdown (:es this))) repl/IReplEnvOptions (-repl-options [this] {:repl-requires @@ -517,24 +535,13 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" (pr-str {:ua-product (clojure.browser.repl/get-ua-product) :value (str ~e) - :stacktrace (.-stack ~e)}))))) - (-evaluate [this _ _ js] - (binding [browser-state (:browser-state this) - ordering (:ordering this) - es (:es this) - server/state (:server-state this)] - (browser-eval js))) - (-load [this provides url] - (load-javascript this provides url)) - (-tear-down [this] - (binding [server/state (:server-state this)] - (server/stop)) - (.shutdown (:es this)))) + :stacktrace (.-stack ~e)})))))) (defn repl-env* [{:keys [output-dir] :as opts}] (merge (BrowserEnv.) - {:port 9000 + {:host "localhost" + :port 9000 :working-dir (->> [".repl" (util/clojurescript-version)] (remove empty?) (string/join "-")) :serve-static true From cd4fe1baac1f979e9c55c202ca683673e3b3f658 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 May 2015 11:18:50 -0400 Subject: [PATCH 1047/4033] delete stray .DS_Store files --- src/main/cljs/cljs/.DS_Store | Bin 6148 -> 0 bytes src/test/cljs/cljs/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/cljs/cljs/.DS_Store delete mode 100644 src/test/cljs/cljs/.DS_Store diff --git a/src/main/cljs/cljs/.DS_Store b/src/main/cljs/cljs/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Sat, 9 May 2015 12:16:39 -0400 Subject: [PATCH 1048/4033] CLJS-1235: non-upstream :foreign-libs not copied to :output-dir cljs.closure: - source-for-namespace namespaces for js namespaces should not be munged, just call str on ns :file may be a string representing a URL. Attempt to parse via URL ctor. cljs.repl: - load-namespace filter out the :seed when evaluating dep strings, the :seed is not a valid IJavaScript instance --- src/main/clojure/cljs/closure.clj | 13 +++++++++---- src/main/clojure/cljs/repl.cljc | 7 +++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e109d24b3..29f062655 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -512,11 +512,16 @@ (let [relpath (str path ".cljc")] (if-let [cljc-res (io/resource relpath)] {:relative-path relpath :uri cljc-res} - (let [relpath (:file (get-in @compiler-env [:js-dependency-index ns-str]))] - (if-let [js-res (and relpath (io/resource relpath))] + (let [relpath (:file (get-in @compiler-env [:js-dependency-index (str ns)]))] + (if-let [js-res (and relpath + ;; try to parse URL, otherwise just return local + ;; resource + (or (try (URL. relpath) (catch Throwable t)) + (io/resource relpath)))] {:relative-path relpath :uri js-res} (throw - (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))))) + (IllegalArgumentException. + (str "Namespace " ns " does not exist")))))))))) (defn cljs-dependencies "Given a list of all required namespaces, return a list of @@ -997,7 +1002,7 @@ to the specified base file." [^File base input] (let [base-path (util/path-seq (.getCanonicalPath base)) - input-path (util/path-seq (.getCanonicalPath (io/file ^URL (deps/-url input)))) + input-path (util/path-seq (.getCanonicalPath (io/file (deps/-url input)))) count-base (count base-path) common (count (take-while true? (map #(= %1 %2) base-path input-path))) prefix (repeat (- count-base common 1) "..")] diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 596241510..cdb127243 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -177,7 +177,8 @@ ;; source must supply at least :url - David sources (cljsc/add-dependencies (merge (env->opts repl-env) opts) - {:requires [(name ns)] :type :seed + {:requires [(name ns)] + :type :seed :url (:uri (cljsc/source-for-namespace ns env/*compiler*))}) deps (->> sources @@ -187,7 +188,9 @@ (if (:output-dir opts) ;; REPLs that read from :output-dir just need to add deps, ;; environment will handle actual loading - David - (doseq [source (map #(cljsc/source-on-disk opts %) sources)] + (doseq [source (->> sources + (remove (comp #{:seed} :type)) + (map #(cljsc/source-on-disk opts %)))] (-evaluate repl-env "" 1 (cljsc/add-dep-string opts source))) ;; REPLs that stream must manually load each dep - David From 159fad2df02efd869842076fef22b51de3a6c085 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 May 2015 13:46:15 -0400 Subject: [PATCH 1049/4033] CLJS-1196: Assert failed on 3190+ while :require-ing .js file in :libs directory cljs.util - path can now take a string cljs.js-deps - IJavaScript add new -closure-lib? to protocol - library-graph-node mark closure libs, record :libs path where found if given - load-library* pass :libs path to library-graph-node cljs.closure - implement -closure-lib? on IJavaScript extended types - map->javascript-file merge :closure-lib info - rel-output-path Closure libraries need relative path treatment - lib-rel-path add helper to compute path relative to :libs dir where found to avoid name clashes - write-js? closure :libs also need to be written as JS --- src/main/clojure/cljs/closure.clj | 75 ++++++++++++++++++------------- src/main/clojure/cljs/js_deps.clj | 16 ++++--- src/main/clojure/cljs/util.cljc | 5 ++- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 29f062655..e3e548910 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -255,6 +255,7 @@ String (-foreign? [this] false) + (-closure-lib? [this] false) (-url [this] nil) (-provides [this] (:provides (deps/parse-js-ns (string/split-lines this)))) (-requires [this] (:requires (deps/parse-js-ns (string/split-lines this)))) @@ -262,6 +263,7 @@ clojure.lang.IPersistentMap (-foreign? [this] (:foreign this)) + (-closure-lib? [this] (:closure-lib this)) (-url [this] (or (:url this) (deps/to-url (:file this)))) (-provides [this] (map name (:provides this))) @@ -273,6 +275,7 @@ (defrecord JavaScriptFile [foreign ^URL url ^URL source-url provides requires lines source-map] deps/IJavaScript (-foreign? [this] foreign) + (-closure-lib? [this] (:closure-lib this)) (-url [this] url) (-provides [this] provides) (-requires [this] requires) @@ -291,16 +294,19 @@ (JavaScriptFile. foreign url source-url (map name provides) (map name requires) lines source-map))) (defn map->javascript-file [m] - (javascript-file - (:foreign m) - (when-let [f (:file m)] - (deps/to-url f)) - (when-let [sf (:source-file m)] - (deps/to-url sf)) - (:provides m) - (:requires m) - (:lines m) - (:source-map m))) + (merge + (javascript-file + (:foreign m) + (when-let [f (:file m)] + (deps/to-url f)) + (when-let [sf (:source-file m)] + (deps/to-url sf)) + (:provides m) + (:requires m) + (:lines m) + (:source-map m)) + (when (:closure-lib m) + {:closure-lib true}))) (defn read-js "Read a JavaScript file returning a map of file information." @@ -1121,29 +1127,37 @@ (- (count (.split #"\r?\n" fdeps-str -1)) 1) 0)}))))))) +(defn lib-rel-path [{:keys [lib-path url] :as ijs}] + (string/replace + (util/path url) + (str (io/file (System/getProperty "user.dir") lib-path) File/separator) + "")) + (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, or is a foreign lib, return the path relative to the output directory." - [js] - (let [url (deps/-url js)] - (cond - url - (if (deps/-foreign? js) - (util/get-name url) - (path-from-jarfile url)) - - (string? js) - (let [digest (MessageDigest/getInstance "SHA-1")] - (.reset digest) - (.update digest (.getBytes ^String js "utf8")) - (str - (->> (DatatypeConverter/printHexBinary (.digest digest)) - (take 7) - (apply str)) - ".js")) - - :else (str (random-string 5) ".js")))) + ([js] (rel-output-path js nil)) + ([js opts] + (let [url (deps/-url js)] + (cond + url + (cond + (deps/-closure-lib? js) (lib-rel-path js) + (deps/-foreign? js) (util/get-name url) + :else (path-from-jarfile url)) + + (string? js) + (let [digest (MessageDigest/getInstance "SHA-1")] + (.reset digest) + (.update digest (.getBytes ^String js "utf8")) + (str + (->> (DatatypeConverter/printHexBinary (.digest digest)) + (take 7) + (apply str)) + ".js")) + + :else (str (random-string 5) ".js"))))) (defn write-javascript "Write or copy a JavaScript file to output directory. Only write if the file @@ -1151,7 +1165,7 @@ location." [opts js] (let [out-dir (io/file (util/output-directory opts)) - out-name (rel-output-path js) + out-name (rel-output-path js opts) out-file (io/file out-dir out-name) ijs {:url (deps/to-url out-file) :requires (deps/-requires js) @@ -1171,6 +1185,7 @@ (let [url ^URL (deps/-url js)] (or (not url) (= (.getProtocol url) "jar") + (deps/-closure-lib? js) (deps/-foreign? js)))) (defn source-on-disk diff --git a/src/main/clojure/cljs/js_deps.clj b/src/main/clojure/cljs/js_deps.clj index f3d04d35f..5ef40e1de 100644 --- a/src/main/clojure/cljs/js_deps.clj +++ b/src/main/clojure/cljs/js_deps.clj @@ -111,6 +111,8 @@ case." (defprotocol IJavaScript (-foreign? [this] "Whether the Javascript represents a foreign library (a js file that not have any goog.provide statement") + (-closure-lib? [this] "Whether the Javascript represents a Closure style + library") (-url [this] "The URL where this JavaScript is located. Returns nil when JavaScript exists in memory only.") (-provides [this] "A list of namespaces that this JavaScript provides.") @@ -205,10 +207,14 @@ case." (defn- library-graph-node "Returns a map of :provides, :requires, and :url given a URL to a goog-style JavaScript library containing provide/require 'declarations'." - [url] - (with-open [reader (io/reader url)] - (-> reader line-seq parse-js-ns - (assoc :url url)))) + ([url] (library-graph-node url nil)) + ([url lib-path] + (with-open [reader (io/reader url)] + (-> reader line-seq parse-js-ns + (merge + {:url url :closure-lib true} + (when lib-path + {:lib-path lib-path})))))) (defn load-library* "Given a path to a JavaScript library, which is a directory @@ -216,7 +222,7 @@ JavaScript library containing provide/require 'declarations'." containing :provides, :requires, :file and :url." [path] (->> (find-js-resources path) - (map library-graph-node) + (map #(library-graph-node % path)) (filter #(seq (:provides %))))) (def load-library (memoize load-library*)) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 64e52d86c..a98d9098c 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -106,10 +106,11 @@ (.getName f)) (defn ^String path [x] - {:pre [(or (file? x) (url? x))]} + {:pre [(or (file? x) (url? x) (string? x))]} (cond (file? x) (.getAbsolutePath ^File x) - (url? x) (.getPath ^URL x))) + (url? x) (.getPath ^URL x) + (string? x) x)) (defn ^String ext "Given a file, url or string return the file extension." From 5858966764728393f86cdeca50a049e350c670eb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 May 2015 14:46:48 -0400 Subject: [PATCH 1050/4033] CLJS-1168: REPL fails to find .js files in :libs Improve undeclared ns error reporting to account for :libs. :libs IJavaScript instance do not supply :file only :url. Enhance cljs.closure/source-for-namespace so it handles IJavaScript instances that do not supply :file. --- src/main/clojure/cljs/analyzer.cljc | 7 ++++--- src/main/clojure/cljs/closure.clj | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e340827e3..cc8388520 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -109,10 +109,11 @@ (str "Use of undeclared Var " (:prefix info) "/" (:suffix info))) (defmethod error-message :undeclared-ns - [warning-type {:keys [ns-sym] :as info}] + [warning-type {:keys [ns-sym js-provide] :as info}] (str "No such namespace: " ns-sym ", could not locate " (util/ns->relpath ns-sym :cljs) - " or " (util/ns->relpath ns-sym :cljc))) + ", " (util/ns->relpath ns-sym :cljc) + ", or Closure namespace \"" js-provide "\"")) (defmethod error-message :dynamic [warning-type info] @@ -1290,7 +1291,7 @@ (analyze-file src opts) (throw (error env - (error-message :undeclared-ns {:ns-sym dep})))))))))) + (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))) (defn check-uses [uses env] (doseq [[sym lib] uses] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e3e548910..f1609fae0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -518,11 +518,13 @@ (let [relpath (str path ".cljc")] (if-let [cljc-res (io/resource relpath)] {:relative-path relpath :uri cljc-res} - (let [relpath (:file (get-in @compiler-env [:js-dependency-index (str ns)]))] + (let [ijs (get-in @compiler-env [:js-dependency-index (str ns)]) + relpath (or (:file ijs) (:url ijs))] (if-let [js-res (and relpath ;; try to parse URL, otherwise just return local ;; resource - (or (try (URL. relpath) (catch Throwable t)) + (or (and (util/url? relpath) relpath) + (try (URL. relpath) (catch Throwable t)) (io/resource relpath)))] {:relative-path relpath :uri js-res} (throw From 94ad4db9b02169992d912891d95cfe1750445c7f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 May 2015 15:19:02 -0400 Subject: [PATCH 1051/4033] Need to include .js files in build --- pom.template.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.template.xml b/pom.template.xml index e33389b66..b2675c03c 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -213,6 +213,7 @@ **/*.clj **/*.cljc **/*.cljs + **/*.js From bf5c282bf153e85186396f1e5bc7a9f4487e00f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 May 2015 15:27:24 -0400 Subject: [PATCH 1052/4033] 0.0-3264 --- README.md | 6 +++--- changes.md | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f26ea77cb..4be51113e 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3255 +Latest stable release: 0.0-3264 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3255"] +[org.clojure/clojurescript "0.0-3264"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3255 org.clojure clojurescript - 0.0-3255 + 0.0-3264 ``` diff --git a/changes.md b/changes.md index 0e919fa2e..fd214800f 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,13 @@ +## 0.0-3264 + +## Fixes +* Add missing JS files back to the build +* CLJS-1168: REPL fails to find .js files in :libs +* CLJS-1196: Assert failed on 3190+ while :require-ing .js file in :libs directory +* CLJS-1235: non-upstream :foreign-libs not copied to :output-dir +* CLJS-1258: stack trace mapping does not appear to work with :asset-path +* CLJS-1257: find-doc regression + ## 0.0-3255 ### Changes From 07bb585b181c02b3c33c7d88c46ddb888279b07f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 May 2015 15:43:56 -0400 Subject: [PATCH 1053/4033] build missing .map source map & .edn caches files --- pom.template.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.template.xml b/pom.template.xml index b2675c03c..b02e6172e 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -214,6 +214,8 @@ **/*.cljc **/*.cljs **/*.js + **/*.map + **/*.edn From 4f8d24cf5f857bf114e09535cfbb0f5db17326a9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 9 May 2015 16:27:24 -0400 Subject: [PATCH 1054/4033] don't break closure libs that follow classpath conventions --- src/main/clojure/cljs/js_deps.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.clj b/src/main/clojure/cljs/js_deps.clj index 5ef40e1de..5b0508f70 100644 --- a/src/main/clojure/cljs/js_deps.clj +++ b/src/main/clojure/cljs/js_deps.clj @@ -212,9 +212,9 @@ JavaScript library containing provide/require 'declarations'." (with-open [reader (io/reader url)] (-> reader line-seq parse-js-ns (merge - {:url url :closure-lib true} + {:url url} (when lib-path - {:lib-path lib-path})))))) + {:closure-lib true :lib-path lib-path})))))) (defn load-library* "Given a path to a JavaScript library, which is a directory From ef0a90522609b272d84ad2aa8fe8f1e82a682e18 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 10 May 2015 09:52:47 -0400 Subject: [PATCH 1055/4033] remove target dir before building --- script/build | 1 + 1 file changed, 1 insertion(+) diff --git a/script/build b/script/build index 344394e21..684c695fc 100755 --- a/script/build +++ b/script/build @@ -6,6 +6,7 @@ set -ex cd `dirname $0`/.. +rm -rf target POM_TEMPLATE="pom.template.xml" POM_FILE="pom.xml" From e5db8114ee8e1eaf39afe97f3e2f778e2ffd8762 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 10 May 2015 10:02:12 -0400 Subject: [PATCH 1056/4033] REPL support for Closure libraries that follow classpath conventions cljs.closure/source-for-namespace which is used by cljs.repl/load-namespace now checks for a js file following classpath conventions before checking the js dependency index --- src/main/clojure/cljs/closure.clj | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f1609fae0..9532bfed4 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -518,18 +518,21 @@ (let [relpath (str path ".cljc")] (if-let [cljc-res (io/resource relpath)] {:relative-path relpath :uri cljc-res} - (let [ijs (get-in @compiler-env [:js-dependency-index (str ns)]) - relpath (or (:file ijs) (:url ijs))] - (if-let [js-res (and relpath + (let [relpath (str path ".js")] + (if-let [js-res (io/resource relpath)] + {:relative-path relpath :uri js-res} + (let [ijs (get-in @compiler-env [:js-dependency-index (str ns)]) + relpath (or (:file ijs) (:url ijs))] + (if-let [js-res (and relpath ;; try to parse URL, otherwise just return local ;; resource (or (and (util/url? relpath) relpath) - (try (URL. relpath) (catch Throwable t)) - (io/resource relpath)))] - {:relative-path relpath :uri js-res} - (throw - (IllegalArgumentException. - (str "Namespace " ns " does not exist")))))))))) + (try (URL. relpath) (catch Throwable t)) + (io/resource relpath)))] + {:relative-path relpath :uri js-res} + (throw + (IllegalArgumentException. + (str "Namespace " ns " does not exist")))))))))))) (defn cljs-dependencies "Given a list of all required namespaces, return a list of From 3ff8334ca18e8fd4a482a8bd9cd7a348b454d251 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 10 May 2015 10:13:57 -0400 Subject: [PATCH 1057/4033] 0.0-3269 --- README.md | 6 +++--- changes.md | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4be51113e..6410c4a31 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3264 +Latest stable release: 0.0-3269 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3264"] +[org.clojure/clojurescript "0.0-3269"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3264 org.clojure clojurescript - 0.0-3264 + 0.0-3269 ``` diff --git a/changes.md b/changes.md index fd214800f..989b7a6b0 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,10 @@ +## 0.0-3269 + +## Fixes +* REPL support for Closure libraries that follow classpath conventions +* don't break closure libs that follow classpath conventions +* build missing .map source map & .edn caches files + ## 0.0-3264 ## Fixes From db91a65769bb975c3da2a452dc834b8867021c8f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 10 May 2015 15:19:49 -0400 Subject: [PATCH 1058/4033] CLJS-1261: source fn fails for fns with conditional code --- src/main/clojure/cljs/repl.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index cdb127243..e2df149f9 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1130,7 +1130,8 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (with-open [pbr (PushbackReader. (io/reader f))] (let [rdr (readers/source-logging-push-back-reader pbr)] (dotimes [_ (dec (:line v))] (readers/read-line rdr)) - (-> (reader/read rdr) meta :source)))))))) + (-> (reader/read {:read-cond :allow :features #{:cljs}} rdr) + meta :source)))))))) (comment (def cenv (env/default-compiler-env)) From dc7e97a961e6348c41e31d4f594a0b6518d354c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 10 May 2015 15:29:42 -0400 Subject: [PATCH 1059/4033] Eduction did not implement both -reduce arities --- src/main/cljs/cljs/core.cljs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 44fa22cf4..58475fa7c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8934,17 +8934,18 @@ reduces them without incurring seq initialization" (filter (fn [_] (< (rand) prob)) coll))) (deftype Eduction [xform coll] - ISequential - - ISeqable - (-seq [_] (seq (sequence xform coll))) + ISequential + + ISeqable + (-seq [_] (seq (sequence xform coll))) - IReduce - (-reduce [_ f init] (transduce xform f init coll)) + IReduce + (-reduce [_ f] (transduce xform f coll)) + (-reduce [_ f init] (transduce xform f init coll)) - IPrintWithWriter - (-pr-writer [coll writer opts] - (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))) + IPrintWithWriter + (-pr-writer [coll writer opts] + (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))) (es6-iterable Eduction) From ca19a521195eee3eb38f2946157c273679d64212 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 10 May 2015 17:14:10 -0500 Subject: [PATCH 1060/4033] CLJS-1209: Reduce produces additional final nil when used w/ eduction Eduction IReduce implementations must wrap `f` in `completing` --- src/main/cljs/cljs/core.cljs | 4 ++-- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 58475fa7c..f7248d2d7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8940,8 +8940,8 @@ reduces them without incurring seq initialization" (-seq [_] (seq (sequence xform coll))) IReduce - (-reduce [_ f] (transduce xform f coll)) - (-reduce [_ f init] (transduce xform f init coll)) + (-reduce [_ f] (transduce xform (completing f) coll)) + (-reduce [_ f init] (transduce xform (completing f) init coll)) IPrintWithWriter (-pr-writer [coll writer opts] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 8892e7534..0790b2207 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2814,6 +2814,14 @@ (deftest test-cljs-1225 (is (= (incme) 2))) +(defn my-conj + [acc x] + (conj acc x)) + +(deftest test-cljs-1209 + (is (= (reduce my-conj [] (eduction (map identity) [1 2 3])) + [1 2 3]))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 6055215a4003ac7b85d60d12f3c0f254dc6dea9e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 10 May 2015 18:00:17 -0500 Subject: [PATCH 1061/4033] put cljs.test macro ns in the same dir as cljs.test runtime support --- src/main/{clojure => cljs}/cljs/test.clj | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/{clojure => cljs}/cljs/test.clj (100%) diff --git a/src/main/clojure/cljs/test.clj b/src/main/cljs/cljs/test.clj similarity index 100% rename from src/main/clojure/cljs/test.clj rename to src/main/cljs/cljs/test.clj From 88eed9a964a6b8bbb101dcf279078dd8be445d2a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 10 May 2015 18:20:38 -0500 Subject: [PATCH 1062/4033] remove stale comment --- src/main/cljs/clojure/browser/repl.cljs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index dec7ef4ac..8324c3709 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -31,8 +31,6 @@ (if-let [conn @xpc-connection] (net/transmit conn :print (pr-str data)))) -;; TODO: latest GCL interface for this is different -;; see goog.userAgent.product (defn get-ua-product [] (cond product/SAFARI :safari From 43052ba451ea2feeb1188ed50852b98078ed5b53 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 May 2015 11:37:29 -0500 Subject: [PATCH 1063/4033] support :libs entries that specify a specific file, just use the file, don't compute the relative location. Note, this does not fix loading of this type of entry at the REPL --- src/main/clojure/cljs/closure.clj | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9532bfed4..b5a2ba769 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1133,10 +1133,13 @@ 0)}))))))) (defn lib-rel-path [{:keys [lib-path url] :as ijs}] - (string/replace - (util/path url) - (str (io/file (System/getProperty "user.dir") lib-path) File/separator) - "")) + (let [path (util/path url)] + (if (.endsWith path ".js") + (util/get-name url) + (string/replace + path + (str (io/file (System/getProperty "user.dir") lib-path) File/separator) + "")))) (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, From e9de8c70de99cedb67e6bde7c3a9f65a716b6e35 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 May 2015 15:42:24 -0500 Subject: [PATCH 1064/4033] CLJS-1263: :libs regression, can no longer specify specific files fix lib-rel-path so it handles specific file references correctly --- src/main/clojure/cljs/closure.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b5a2ba769..e0cf2b8df 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1133,9 +1133,9 @@ 0)}))))))) (defn lib-rel-path [{:keys [lib-path url] :as ijs}] - (let [path (util/path url)] - (if (.endsWith path ".js") - (util/get-name url) + (if (.endsWith lib-path ".js") + (util/get-name url) + (let [path (util/path url)] (string/replace path (str (io/file (System/getProperty "user.dir") lib-path) File/separator) From 31736450c6ca951c63bb685d744c11074fbfe323 Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Tue, 12 May 2015 12:14:31 +0200 Subject: [PATCH 1065/4033] CLJS-1226: Added the :end-run-test event to cljs.test and a dummy event handler for it --- src/main/cljs/cljs/test.clj | 1 + src/main/cljs/cljs/test.cljs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/test.clj b/src/main/cljs/cljs/test.clj index b4e3a1c70..05dafe1df 100644 --- a/src/main/cljs/cljs/test.clj +++ b/src/main/cljs/cljs/test.clj @@ -276,6 +276,7 @@ [(fn [] (cljs.test/set-env! ~env) (do-report (deref ~summary)) + (report (assoc (deref ~summary) :type :end-run-tests)) (cljs.test/clear-env!))])))) (defmacro run-tests diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index c5b6c38e7..b63b11d12 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -343,6 +343,7 @@ (defmethod report [::default :begin-test-var] [m] #_(println ":begin-test-var" (testing-vars-str m))) (defmethod report [::default :end-test-var] [m]) +(defmethod report [::default :end-run-tests] [m]) (defn js-line-and-column [stack-element] (let [parts (.split stack-element ":") From bc4f3b5930c7f02f062beffa5ca1f8c8206ecb5c Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Sat, 9 May 2015 01:29:33 +0600 Subject: [PATCH 1066/4033] CLJS-1256 cache UUID hash value --- src/main/cljs/cljs/core.cljs | 9 +++++-- src/main/cljs/cljs/reader.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 39 ++++++++++++++++++------------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f7248d2d7..0a56a79ff 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9502,7 +9502,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." ;; UUID -(deftype UUID [uuid] +(deftype UUID [uuid ^:mutable __hash] Object (toString [_] uuid) (equiv [this other] @@ -9518,12 +9518,17 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." IHash (-hash [this] - (goog.string/hashCode (pr-str this))) + (when (nil? __hash) + (set! __hash (goog.string/hashCode uuid))) + __hash) IComparable (-compare [_ other] (garray/defaultCompare uuid (.-uuid other)))) +(defn uuid [s] + (UUID. s nil)) + ;;; ExceptionInfo (defn- pr-writer-ex-info [obj writer opts] diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index d4cd47d34..da4ab2194 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -561,7 +561,7 @@ nil if the end of stream has been reached") (defn ^:private read-uuid [uuid] (if (string? uuid) - (UUID. uuid) + (cljs.core/uuid uuid) (reader-error nil "UUID literal expects a string as its representation."))) (def ^:dynamic *tag-table* diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 0790b2207..45eb0fc29 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1563,7 +1563,7 @@ inst (str "2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-00:00")] (is (= (pr-str (js/Date. inst)) (str "#inst \"" inst "\""))))) (let [uuid-str "550e8400-e29b-41d4-a716-446655440000" - uuid (UUID. uuid-str)] + uuid (cljs.core/uuid uuid-str)] (is (= (pr-str uuid) (str "#uuid \"" uuid-str "\"")))) ;; pr-str PersistentQueueSeq - CLJS-800 (is (= (pr-str (rest (conj cljs.core.PersistentQueue.EMPTY 1 2 3))) "(2 3)")) @@ -1623,24 +1623,31 @@ (deftest test-uuid (testing "Testing UUID" - (is (= (UUID. "550e8400-e29b-41d4-a716-446655440000") - (UUID. "550e8400-e29b-41d4-a716-446655440000"))) - (is (not (identical? (UUID. "550e8400-e29b-41d4-a716-446655440000") - (UUID. "550e8400-e29b-41d4-a716-446655440000")))) - (is (= 42 (get {(UUID. "550e8400-e29b-41d4-a716-446655440000") 42} - (UUID. "550e8400-e29b-41d4-a716-446655440000") + (is (= (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000") + (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000"))) + (is (not (identical? (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000") + (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000")))) + (is (= 42 (get {(cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000") 42} + (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000") :not-at-all-found))) (is (= :not-at-all-found - (get {(UUID. "550e8400-e29b-41d4-a716-446655440000") 42} - (UUID. "666e8400-e29b-41d4-a716-446655440000") + (get {(cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000") 42} + (cljs.core/uuid "666e8400-e29b-41d4-a716-446655440000") :not-at-all-found))) - (is (= -1 (compare (UUID. "550e8400-e29b-41d4-a716-446655440000") - (UUID. "666e8400-e29b-41d4-a716-446655440000")))) - (is (= 1 (compare (UUID. "550e8400-e29b-41d4-a716-446655440000") - (UUID. "550e8400-a29b-41d4-a716-446655440000")))) - (is (= 0 (compare (UUID. "550e8400-e29b-41d4-a716-446655440000") - (UUID. "550e8400-e29b-41d4-a716-446655440000")))) - )) + (is (= -1 (compare (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000") + (cljs.core/uuid "666e8400-e29b-41d4-a716-446655440000")))) + (is (= 1 (compare (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000") + (cljs.core/uuid "550e8400-a29b-41d4-a716-446655440000")))) + (is (= 0 (compare (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000") + (cljs.core/uuid "550e8400-e29b-41d4-a716-446655440000"))))) + (testing "UUID hashing" + (let [id "550e8400-e29b-41d4-a716-446655440000" + uuid (cljs.core/uuid id) + expected (goog.string/hashCode id)] + (is (= expected (hash uuid))) + ;; checking hash cache + (is (= expected (.-__hash uuid))) + (is (= expected (hash uuid)))))) (deftest test-comparable (testing "Testing IComparable" From 292a8a8779df4b9878b9ca1d37d352c19ae1fa49 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 12 May 2015 16:59:28 -0500 Subject: [PATCH 1067/4033] add cljs.build.api/add-implicit-options --- src/main/clojure/cljs/build/api.clj | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 6db831610..8f8687727 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -96,6 +96,13 @@ ;; ============================================================================= ;; Main API +(defn add-implicit-options + "Given a valid map of build options add any standard implicit options. For + example :optimizations :none implies :cache-analysis true and :source-map + true." + [opts] + (closure/add-implicit-options opts)) + (defn inputs "Given a list of directories and files, return a compilable object that may be passed to build or watch." From b06247b6eb0714c4a190a8cbeae5cba313cbc0c4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 12 May 2015 20:08:25 -0400 Subject: [PATCH 1068/4033] add bits necessary to dogfood cljs.repl on build / analysis public apis --- src/main/clojure/cljs/analyzer/api.clj | 7 ++++ src/main/clojure/cljs/build/api.clj | 49 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index f0cb3b435..3999694e0 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -20,6 +20,13 @@ [] (ana/empty-env)) +(defmacro no-warn + "Disable analyzer warnings for any analysis executed in body." + [& body] + (let [no-warnings (zipmap (keys ana/*cljs-warnings*) (repeat false))] + `(binding [ana/*cljs-warnings* ~no-warnings] + ~@body))) + (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context (one of :statement, :expr, :return), :ns (a symbol naming the diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 8f8687727..0474ec3fc 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -11,6 +11,7 @@ For example: a build script may need to how to invalidate compiled files so that they will be recompiled." + (:refer-clojure :exclude [compile]) (:require [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] @@ -96,6 +97,39 @@ ;; ============================================================================= ;; Main API +(defn goog-dep-string + "Given compiler options and a IJavaScript instance return the corresponding + goog.addDependency string" + [opts ijs] + (closure/add-dep-string opts ijs)) + +(defn source-on-disk + "Ensure that the given IJavaScript exists on disk in the output directory. + Return updated IJavaScript with the new location if necessary." + [opts ijs] + (closure/source-on-disk opts ijs)) + +(defn ns->source + "Given a namespace as a symbol return the corresponding resource if it exists." + [ns] + (util/ns->source ns)) + +(defn ns->location + "Given a namespace and compilation environment return the relative path and + uri of the corresponding source regardless of the source language extension: + .cljs, .cljc, .js. Returns a map containing :relative-path a string, and + :uri a URL." + ([ns] (ns->location ns env/*compiler*)) + ([ns compiler-env] + (closure/source-for-namespace ns compiler-env))) + +(defn add-dependencies + "Given one or more IJavaScript objects in dependency order, produce + a new sequence of IJavaScript objects which includes the input list + plus all dependencies in dependency order." + [opts & ijss] + (closure/add-dependencies opts ijss)) + (defn add-implicit-options "Given a valid map of build options add any standard implicit options. For example :optimizations :none implies :cache-analysis true and :source-map @@ -120,6 +154,21 @@ [compiled])))] (mapcat compile-input xs))))) +(defn compile + "Given a Compilable, compile it and return an IJavaScript." + [opts compilable] + (closure/compile compilable opts)) + +(defn output-unoptimized + "Ensure that all JavaScript source files are on disk (not in jars), + write the goog deps file including only the libraries that are being + used and write the deps file for the current project. + + The deps file for the current project will include third-party + libraries." + [opts & sources] + (apply closure/output-unoptimized opts sources)) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] From 3373567df77b6a83386458ea0dd0854ed4ffe20e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 12 May 2015 20:23:37 -0400 Subject: [PATCH 1069/4033] match Clojure behavior for get on string / array. Need to coerce key into int. --- src/main/cljs/cljs/core.cljs | 4 ++-- src/test/cljs/cljs/core_test.cljs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0a56a79ff..dc2afb5ae 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1574,11 +1574,11 @@ reduces them without incurring seq initialization" (array? o) (when (< k (.-length o)) - (aget o k)) + (aget o (int k))) (string? o) (when (< k (.-length o)) - (aget o k)) + (aget o (int k))) (native-satisfies? ILookup o) (-lookup o k) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 45eb0fc29..bbf53cc3c 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2829,6 +2829,9 @@ (is (= (reduce my-conj [] (eduction (map identity) [1 2 3])) [1 2 3]))) +(deftest test-get-string-float + (is (= (get "hi" 1.7) \i))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 6546749feb8b06aff53cffe975f100532579c0f1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 12 May 2015 20:27:39 -0400 Subject: [PATCH 1070/4033] add get array + float test --- src/test/cljs/cljs/core_test.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index bbf53cc3c..df8480ee2 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2829,7 +2829,8 @@ (is (= (reduce my-conj [] (eduction (map identity) [1 2 3])) [1 2 3]))) -(deftest test-get-string-float +(deftest test-get-with-float + (is (= (get #js [\h \i] 1.7) \i)) (is (= (get "hi" 1.7) \i))) (comment From ed5a2c928c1bcde624dbe671140e9516de54dd24 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 May 2015 20:13:38 -0400 Subject: [PATCH 1071/4033] always serve the established set of files, add .png, issue reported by Kovas Boguta --- src/main/clojure/cljs/repl/browser.clj | 34 ++++++++++++-------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index bbfd325aa..4c7644968 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -24,6 +24,19 @@ (def ^:dynamic ordering nil) (def ^:dynamic es nil) +(def ext->mime-type + {".html" "text/html" + ".css" "text/css" + + ".jpg" "image/jpeg" + ".png" "image/png" + ".gif" "image/gif" + + ".js" "text/javascript" + ".cljs" "text/x-clojure" + ".cljc" "text/x-clojure" + ".map" "application/json"}) + (defn- set-return-value-fn "Save the return value function which will be called when the next return value is received." @@ -84,16 +97,8 @@ local-path)] (if local-path (server/send-and-close conn 200 (slurp local-path) - (condp #(.endsWith %2 %1) path - ".html" "text/html" - ".css" "text/css" - ".html" "text/html" - ".jpg" "image/jpeg" - ".js" "text/javascript" - ".cljs" "text/x-clojure" - ".cljc" "text/x-clojure" - ".map" "application/json" - ".png" "image/png" + (if (some #(.endsWith path %) (keys ext->mime-type)) + (get ext->mime-type path) "text/plain")) (server/send-404 conn path))) (server/send-404 conn path))) @@ -105,14 +110,7 @@ (server/dispatch-on :get (fn [{:keys [path]} _ _] - (or - (= path "/") - (.endsWith path ".js") - (.endsWith path ".cljc") - (.endsWith path ".cljs") - (.endsWith path ".map") - (.endsWith path ".html") - (.endsWith path ".css"))) + (or (= path "/") (some #(.endsWith path %) (keys ext->mime-type)))) send-static) (defmulti handle-post (fn [m _ _ ] (:type m))) From 98ed0f4a08dfe757b7c647dbce22e235ef45b0a3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 May 2015 22:34:36 -0400 Subject: [PATCH 1072/4033] CLJS-1268: cljc support for cljs.closure/compile-file --- src/main/clojure/cljs/closure.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e0cf2b8df..b3d4a94d7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -371,9 +371,10 @@ (if output-file (let [out-file (io/file (util/output-directory opts) output-file)] (compiled-file (comp/compile-file file out-file opts))) - (binding [ana/*cljs-file* (.getPath ^File file)] - (with-open [rdr (io/reader file)] - (compile-form-seq (ana/forms-seq* rdr)))))) + (let [path (.getPath ^File file)] + (binding [ana/*cljs-file* path] + (with-open [rdr (io/reader file)] + (compile-form-seq (ana/forms-seq* rdr path))))))) (defn compile-dir "Recursively compile all cljs files under the given source From 27ad78bb3f7fdfe64b4041bf60532489c2847105 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 16 May 2015 20:55:02 -0400 Subject: [PATCH 1073/4033] CLJS-1269: realized? docstring refers to promise and future Since `promise` and `future` are not implemented in ClojureScript, eliminate references to them in the docstring for `realized?` --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index dc2afb5ae..58d214b4a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8883,7 +8883,7 @@ reduces them without incurring seq initialization" x)) (defn ^boolean realized? - "Returns true if a value has been produced for a promise, delay, future or lazy sequence." + "Returns true if a value has been produced for a delay or lazy sequence." [d] (-realized? d)) From 264eb2b5153d6d1c3b6fcf0a42aad31956297cc5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 16 May 2015 21:11:45 -0400 Subject: [PATCH 1074/4033] CLJS-1270: Docstring for delay not printed by cljs.repl/doc The order of the `doc-string?` and `[params*]` arguments to the `delay` `defmacro` are reversed. --- src/main/clojure/cljs/core.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.clj b/src/main/clojure/cljs/core.clj index 149eaacc7..e4b6d3f42 100644 --- a/src/main/clojure/cljs/core.clj +++ b/src/main/clojure/cljs/core.clj @@ -1394,11 +1394,12 @@ [& body] `(new cljs.core/LazySeq nil (fn [] ~@body) nil nil)) -(defmacro delay [& body] +(defmacro delay "Takes a body of expressions and yields a Delay object that will invoke the body only the first time it is forced (with force or deref/@), and will cache the result and return it on all subsequent force calls." + [& body] `(new cljs.core/Delay (fn [] ~@body) nil)) (defmacro with-redefs From 721ba892892d7aa000460c45a6eb1afecd33eff5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 17 May 2015 19:06:03 -0400 Subject: [PATCH 1075/4033] CLJS-1272: :include-macros description inaccurate in require Update description to clarifty that `true` causes macros to be required. --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index e2df149f9..36a05d493 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1017,7 +1017,7 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) lib's namespace in the current namespace. :refer takes a list of symbols to refer from the namespace.. :refer-macros takes a list of macro symbols to refer from the namespace. - :include-macros takes a list of macro symbols to refer from the namespace. + :include-macros true causes macros from the namespace to be required. Flags From 08398b25400b363ca22db885b7ed2460c92bd5fa Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Mon, 18 May 2015 14:35:27 +0200 Subject: [PATCH 1076/4033] CLJS-1275: Corrected :test-paths in project.clj --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index c013a3bcf..c932cc2e4 100644 --- a/project.clj +++ b/project.clj @@ -7,7 +7,7 @@ :jvm-opts ^:replace ["-Xmx512m" "-server"] :source-paths ["src/main/clojure"] :resource-paths ["src/main/cljs"] - :test-paths ["test/clj"] + :test-paths ["src/test/clojure"] :dependencies [[org.clojure/clojure "1.7.0-beta2"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.2"] From 03529c47f9c38f3923a827166699f511f5ec0356 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 20 May 2015 15:39:21 -0400 Subject: [PATCH 1077/4033] flush immediately when forwarding Node process out & err --- src/main/clojure/cljs/repl/node.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index fa07e21e2..a5be70610 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -86,7 +86,8 @@ (try (let [len (.read in buf)] (when-not (neg? len) - (.write out buf 0 len))) + (.write out buf 0 len) + (.flush out))) (catch IOException e (when (and (.isAlive proc) (not (.contains (.getMessage e) "Stream closed"))) (.printStackTrace e *err*)))) From fee5527284c9803f2865c1c428bbd5055b52af97 Mon Sep 17 00:00:00 2001 From: kovasb Date: Wed, 20 May 2015 15:48:06 -0400 Subject: [PATCH 1078/4033] CLJS-1206: Images in HTML don't show up when served from localhost:9000 --- src/main/clojure/cljs/repl/browser.clj | 24 ++++++++++++++++++++---- src/main/clojure/cljs/repl/server.clj | 10 ++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 4c7644968..f5ec0df66 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -37,6 +37,16 @@ ".cljc" "text/x-clojure" ".map" "application/json"}) +(def mime-type->encoding + {"text/html" "UTF-8" + "text/css" "UTF-8" + "image/jpeg" "ISO-8859-1" + "image/png" "ISO-8859-1" + "image/gif" "ISO-8859-1" + "text/javascript" "UTF-8" + "text/x-clojure" "UTF-8" + "application/json" "UTF-8"}) + (defn- set-return-value-fn "Save the return value function which will be called when the next return value is received." @@ -96,10 +106,16 @@ :else nil) local-path)] (if local-path - (server/send-and-close conn 200 (slurp local-path) - (if (some #(.endsWith path %) (keys ext->mime-type)) - (get ext->mime-type path) - "text/plain")) + (if-let [ext (some #(if (.endsWith path %) %) (keys ext->mime-type))] + (let [mime-type (ext->mime-type ext "text/plain") + encoding (mime-type->encoding mime-type "UTF-8")] + (server/send-and-close + conn + 200 + (slurp local-path :encoding encoding) + mime-type + encoding)) + (server/send-and-close conn 200 (slurp local-path) "text/plain")) (server/send-404 conn path))) (server/send-404 conn path))) diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index afccbc26e..611081255 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -108,20 +108,22 @@ ([conn status form] (send-and-close conn status form "text/html")) ([conn status form content-type] - (let [utf-8-form (.getBytes form "UTF-8") - content-length (count utf-8-form) + (send-and-close conn status form content-type "UTF-8")) + ([conn status form content-type encoding] + (let [byte-form (.getBytes form encoding) + content-length (count byte-form) headers (map #(.getBytes (str % "\r\n")) [(status-line status) "Server: ClojureScript REPL" (str "Content-Type: " content-type - "; charset=utf-8") + "; charset=" encoding) (str "Content-Length: " content-length) ""])] (with-open [os (.getOutputStream conn)] (doseq [header headers] (.write os header 0 (count header))) - (.write os utf-8-form 0 content-length) + (.write os byte-form 0 content-length) (.flush os) (.close conn))))) From 8f9d5e57483e788211458a181190266ebf731a11 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 22 May 2015 10:44:46 -0400 Subject: [PATCH 1079/4033] link to Slack channel in README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6410c4a31..f44ded519 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,10 @@ Latest stable release: 0.0-3269 Please point all of your questions and feedback to the [Clojure mailing list](http://groups.google.com/group/clojure). There -is also a community run +is a community run [ClojureScript user mailing list](http://groups.google.com/group/clojurescript) and -the IRC channel, `#clojurescript` on [freenode.net](https://freenode.net/), is quite active. The +the IRC channel, `#clojurescript` on [freenode.net](https://freenode.net/), is quite active. +There is also a community run [Slack channel](http://clojurians.slack.com). The Jira bug/feature tracking application is located at . Before submitting issues please read the From cb13b11f60418da23d1ff1516de183a88f87312d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 May 2015 15:38:40 -0400 Subject: [PATCH 1080/4033] add zero arity `newline` to match Clojure --- src/main/cljs/cljs/core.cljs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 58d214b4a..33fb25192 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8602,10 +8602,12 @@ reduces them without incurring seq initialization" [objs opts] (string-print (pr-str-with-opts objs opts))) -(defn newline [opts] - (string-print "\n") - (when (get opts :flush-on-newline) - (flush))) +(defn newline + ([] (newline nil)) + ([opts] + (string-print "\n") + (when (get opts :flush-on-newline) + (flush)))) (defn pr-str "pr to a string, returning it. Fundamental entrypoint to IPrintWithWriter." From 46af7373d88f77972eb0f030ca43724d43c6b648 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 23 May 2015 14:32:20 -0400 Subject: [PATCH 1081/4033] finish CLJS-1176, remove stray .isAlive method call --- src/main/clojure/cljs/repl/node.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index a5be70610..f0e82f0c6 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -78,18 +78,21 @@ (defn platform-path [v] (str "path.join.apply(null, " (seq->js-array v) ")")) +(defn- alive? [proc] + (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) + (defn- pipe [^Process proc in ^Writer out] ;; we really do want system-default encoding here (with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)] (loop [buf (char-array 1024)] - (when (try (.exitValue proc) false (catch IllegalThreadStateException _ true)) + (when (alive? proc) (try (let [len (.read in buf)] (when-not (neg? len) (.write out buf 0 len) (.flush out))) (catch IOException e - (when (and (.isAlive proc) (not (.contains (.getMessage e) "Stream closed"))) + (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed"))) (.printStackTrace e *err*)))) (recur buf))))) From f165420d2b25008924d6655ca4e79bf9901c2125 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 23 May 2015 15:09:33 -0400 Subject: [PATCH 1082/4033] CLJS-1284: IndexedSeq -seq implementation incorrect for i >= alength of internal array for performance reasons we sometimes inline calls to the IndexedSeq constructor, however this bypasses the validation offered by prim-seq and array-seq. Fix IndexedSeq -seq so that it checks current index to length of array. Fix IndexedSeq -count so that negative values are never returned. Add tests. --- src/main/cljs/cljs/core.cljs | 7 +++++-- src/test/cljs/cljs/core_test.cljs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 33fb25192..d20684649 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1256,7 +1256,9 @@ reduces them without incurring seq initialization" (-clone [_] (IndexedSeq. arr i)) ISeqable - (-seq [this] this) + (-seq [this] + (when (< i (alength arr)) + this)) ASeq ISeq @@ -1271,7 +1273,8 @@ reduces them without incurring seq initialization" nil)) ICounted - (-count [_] (- (alength arr) i)) + (-count [_] + (max 0 (- (alength arr) i))) IIndexed (-nth [coll n] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index df8480ee2..8e7130746 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2833,6 +2833,22 @@ (is (= (get #js [\h \i] 1.7) \i)) (is (= (get "hi" 1.7) \i))) +(defn foo-1284 + ([] nil) + ([x0 & xs] [x0 xs])) + +(deftest test-cljs-1284 + (let [xs (IndexedSeq. #js [] 0) + ys (IndexedSeq. #js [1] 3)] + (is (nil? (first xs))) + (is (nil? (seq xs))) + (is (= (rest xs) ())) + (is (= (pr-str xs) "()")) + (is (= (foo-1284 0) [0 ()])) + (is (= (pr-str (foo-1284 0)) "[0 ()]")) + (is (zero? (count ys))) + (is (= (transduce (map inc) conj [] ys) [])))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 22ba5a6d0b780183d21bd69a64492e2216a7b379 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 23 May 2015 18:09:59 -0400 Subject: [PATCH 1083/4033] CLJS-1285: load-file regression refactor aset -> goog.object/set also attempt to just remove the source path without prepending goog.basePath --- src/main/cljs/clojure/browser/repl.cljs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 8324c3709..f9a3cbc3d 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -16,6 +16,7 @@ :author "Bobby Calderwood and Alex Redington"} clojure.browser.repl (:require [goog.dom :as gdom] + [goog.object :as gobj] [goog.userAgent.product :as product] [clojure.browser.net :as net] [clojure.browser.event :as event] @@ -134,11 +135,11 @@ (.appendChild js/document.body (as-> (.createElement js/document "script") script (doto script - (aset "type" "text/javascript") - (aset "onload" onload) - (aset "onreadystatechange" onload)) ;; IE + (gobj/set "type" "text/javascript") + (gobj/set "onload" onload) + (gobj/set "onreadystatechange" onload)) ;; IE (if (nil? opt_sourceText) - (doto script (aset "src" src)) + (doto script (gobj/set "src" src)) (doto script (gdom/setTextContext opt_sourceText)))))))) ;; queue or load (set! (.-writeScriptTag_ js/goog) @@ -157,8 +158,9 @@ (let [reload? (or reload (.-cljsReloadAll__ js/goog))] (when reload? (let [path (aget js/goog.dependencies_.nameToPath src)] - (js-delete js/goog.dependencies_.visited path) - (js-delete js/goog.dependencies_.written + (gobj/remove js/goog.dependencies_.visited path) + (gobj/remove js/goog.dependencies_.written path) + (gobj/remove js/goog.dependencies_.written (str js/goog.basePath path)))) (let [ret (.require__ js/goog src)] (when (= reload "reload-all") From e962ec0ea7124a1fabbfe3c9fa005120e3c001cc Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 23 May 2015 20:32:01 -0400 Subject: [PATCH 1084/4033] users need to be able to specify host-port for source mapping to work in browser REPL. The host and port the JS is served on may very well not match the repl port --- src/main/clojure/cljs/repl/browser.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index f5ec0df66..eb78069a0 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -227,8 +227,8 @@ (cond-> column (.endsWith column ")") (string/replace ")" "")))])) -(defn parse-file [{:keys [host port] :as repl-env} file {:keys [asset-path] :as opts}] - (let [base-url-pattern (Pattern/compile (str "http://" host ":" port "/"))] +(defn parse-file [{:keys [host host-port port] :as repl-env} file {:keys [asset-path] :as opts}] + (let [base-url-pattern (Pattern/compile (str "http://" host ":" (or host-port port) "/"))] (if (re-find base-url-pattern file) (-> file (string/replace base-url-pattern "") From 2a413d48ee228bca9f48d4795222f5fb9aae432e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 26 May 2015 08:55:31 -0400 Subject: [PATCH 1085/4033] clarify cljs.test/run-tests docstring --- src/main/cljs/cljs/test.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/test.clj b/src/main/cljs/cljs/test.clj index 05dafe1df..3ff3b65ac 100644 --- a/src/main/cljs/cljs/test.clj +++ b/src/main/cljs/cljs/test.clj @@ -281,7 +281,10 @@ (defmacro run-tests "Runs all tests in the given namespaces; prints results. - Defaults to current namespace if none given." + Defaults to current namespace if none given. Does not return a meaningful + value due to the possiblity of asynchronous execution. To detect test + completion add a :end-run-tests method case to the cljs.test/report + multimethod." ([] `(run-tests (cljs.test/empty-env) '~ana/*cljs-ns*)) ([env-or-ns] (if (ns? env-or-ns) From cfdb0d4fb6993f121712040671188523f8c8b20f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 26 May 2015 11:33:03 -0400 Subject: [PATCH 1086/4033] check that we actually read something in cjls.repl.server/read-request --- src/main/clojure/cljs/repl/server.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index 611081255..3df643bee 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -90,11 +90,12 @@ :headers headers})) (defn read-request [rdr] - (let [line (.readLine rdr)] + (if-let [line (.readLine rdr)] (cond (.startsWith line "POST") (read-post line rdr) (.startsWith line "GET") (read-get line rdr) - :else {:method :unknown :content line}))) + :else {:method :unknown :content line}) + {:method :unknown :content nil})) (defn- status-line [status] (case status From f75da4ec93add6030ab57d4e2b3e1ad4a1cf9d4d Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Tue, 26 May 2015 16:13:50 +1200 Subject: [PATCH 1087/4033] CLJS-1288: compiler doesn't emit "goog.require" for foreign library when optimization level is not set Merge all options in the compiler-env with existing options. Pull emit-constants options into add-implicit-options function. --- src/main/clojure/cljs/closure.clj | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b3d4a94d7..9153b9541 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1399,7 +1399,10 @@ (map (fn [[k v]] [(if (symbol? k) (str (comp/munge k)) k) v]) (:closure-defines opts))))) - {:keys [libs foreign-libs externs]} (get-upstream-deps)] + {:keys [libs foreign-libs externs]} (get-upstream-deps) + emit-constants (or (and (= optimizations :advanced) + (not (false? (:optimize-constants opts)))) + (:optimize-constants opts))] (cond-> (-> opts (assoc @@ -1407,7 +1410,8 @@ :output-dir output-dir :ups-libs libs :ups-foreign-libs foreign-libs - :ups-externs externs) + :ups-externs externs + :emit-constants emit-constants) (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) (:target opts) @@ -1434,10 +1438,7 @@ ([source opts compiler-env] (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) - all-opts (add-implicit-options opts) - emit-constants (or (and (= (:optimizations opts) :advanced) - (not (false? (:optimize-constants opts)))) - (:optimize-constants opts))] + all-opts (add-implicit-options opts)] (check-output-to opts) (check-output-dir opts) (check-source-map opts) @@ -1446,7 +1447,7 @@ (check-node-target opts) (swap! compiler-env #(-> % - (assoc-in [:options :emit-constants] emit-constants) + (update-in [:options] merge all-opts) (assoc :target (:target opts)) (assoc :js-dependency-index (deps/js-dependency-index all-opts)))) (binding [comp/*dependents* (when-not (false? (:recompile-dependents opts)) @@ -1484,7 +1485,7 @@ [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))) (when (= :nodejs (:target all-opts)) [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) - _ (when emit-constants + _ (when (:emit-constants all-opts) (comp/emit-constants-table-to-file (::ana/constant-table @env/*compiler*) (str (util/output-directory all-opts) "/constants_table.js"))) From 25e4945cb5c7f0ed4322a377d0e12a62ffea3087 Mon Sep 17 00:00:00 2001 From: Jonathan Boston Date: Thu, 28 May 2015 09:29:21 -0500 Subject: [PATCH 1088/4033] CLJS-1291: pprint whitespace/letter checks are incomplete Use Google Closure fns instead of incomplete fns for testing whitespace and isLetter. --- src/main/cljs/cljs/pprint.cljs | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 3209ff2ac..75c6c6458 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -14,7 +14,8 @@ defdirectives formatter-out]]) (:require [cljs.core :refer [IWriter IDeref]] - [clojure.string :as string]) + [clojure.string :as string] + [goog.string :as gstring]) (:import [goog.string StringBuffer])) (def ^:dynamic *out* nil) @@ -1956,19 +1957,12 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm" ;;TODO need to enforce integers only? (-write writer (string/upper-case (char c)))))))) -;;TODO: This is an oversimplied version. Needs to be fully implemented -(defn- is-letter? [s] - (boolean - (#{"A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" - "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"} - s))) - (defn- capitalize-string "Capitalizes the words in a string. If first? is false, don't capitalize the first character of the string even if it's a letter." [s first?] (let [f (first s) - s (if (and first? f (is-letter? f)) + s (if (and first? f (gstring/isUnicodeChar f)) (str (string/upper-case f) (subs s 1)) s)] (apply str @@ -1986,11 +1980,6 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm" [s nil])))) s))))) -;;TODO: This is an oversimplied version. Needs to be fully implemented -(defn- is-whitespace? [s] - (boolean - (#{\space \newline} s))) - (defn- capitalize-word-writer "Returns a proxy that wraps writer, capitalizing all words" [writer] @@ -2009,13 +1998,13 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm" (-write writer (capitalize-string (.toLowerCase s) @last-was-whitespace?)) (when (pos? (.-length s)) - (reset! last-was-whitespace? (is-whitespace? (nth s (dec (count s))))))) + (reset! last-was-whitespace? (gstring/isEmptyOrWhitespace (nth s (dec (count s))))))) js/Number (let [c (char x)] (let [mod-c (if @last-was-whitespace? (string/upper-case c) c)] (-write writer mod-c) - (reset! last-was-whitespace? (is-whitespace? c))))))))) + (reset! last-was-whitespace? (gstring/isEmptyOrWhitespace c))))))))) (defn- init-cap-writer "Returns a proxy that wraps writer, capitalizing the first word" @@ -2046,7 +2035,7 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm" js/Number (let [c (char x)] - (if (and (not @capped) (is-letter? c)) + (if (and (not @capped) (gstring/isUnicodeChar c)) (do (reset! capped true) (-write writer (string/upper-case c))) From 635afae1b3d86d787d2d8bf5b54e0e9774a33f1d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 28 May 2015 12:04:17 -0400 Subject: [PATCH 1089/4033] add cljs.core/random-uuid --- src/main/cljs/cljs/core.cljs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d20684649..dd8847d52 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9534,6 +9534,19 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn uuid [s] (UUID. s nil)) +(defn random-uuid [] + (letfn [(hex [] (.toString (rand-int 15) 16))] + (let [rhex (.toString (bit-or 0x8 (bit-and 0x3 (rand-int 14))) 16)] + (uuid + (str (hex) (hex) (hex) (hex) + (hex) (hex) (hex) (hex) "-" + (hex) (hex) (hex) (hex) "-" + "4" (hex) (hex) (hex) "-" + rhex (hex) (hex) (hex) "-" + (hex) (hex) (hex) (hex) + (hex) (hex) (hex) (hex) + (hex) (hex) (hex) (hex)))))) + ;;; ExceptionInfo (defn- pr-writer-ex-info [obj writer opts] From 8537ef4d7682ce29066c48e49062bd57c8d4ee92 Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Thu, 28 May 2015 20:28:43 +0200 Subject: [PATCH 1090/4033] CLJS-1292: Add IPrintWithWriter implementation for TaggedLiteral --- src/main/cljs/cljs/core.cljs | 8 +++++++- src/test/cljs/cljs/core_test.cljs | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index dd8847d52..13d47c93d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9645,6 +9645,9 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype TaggedLiteral [tag form] + Object + (toString [coll] + (pr-str* coll)) IEquiv (-equiv [this other] @@ -9666,7 +9669,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." :form form not-found)) - ) + IPrintWithWriter + (-pr-writer [o writer opts] + (-write writer (str "#" tag " ")) + (pr-writer form writer opts))) (defn tagged-literal? "Return true if the value is the data representation of a tagged literal" diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 8e7130746..d26bd01c9 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2812,7 +2812,8 @@ (is (= tl (tagged-literal 'x "y"))) (is (not= tl (tagged-literal 'z "y"))) (is (not= tl (tagged-literal 'x "z"))) - (is (= (hash tl) (hash (tagged-literal 'x "y")))))) + (is (= (hash tl) (hash (tagged-literal 'x "y")))) + (is (= "#foo [1]" (str (tagged-literal 'foo [1])))))) (defn- incme [] (let [incme (fn [a queue & args] (inc a))] From dfad0d2c08f83c176602b89ad7e79e251c5f3d05 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 31 May 2015 16:40:37 -0400 Subject: [PATCH 1091/4033] CLJS-1293: Warning settings not conveyed via REPL Sets the binding for ana/*cljs-warnings* so that it reflects any warnings supplied in opts. --- src/main/clojure/cljs/repl.cljc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 36a05d493..ee582924c 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -767,11 +767,19 @@ ana/*cljs-ns* ana/*cljs-ns* *cljs-verbose* repl-verbose ana/*cljs-warnings* - (assoc ana/*cljs-warnings* - :unprovided warn-on-undeclared - :undeclared-var warn-on-undeclared - :undeclared-ns warn-on-undeclared - :undeclared-ns-form warn-on-undeclared) + (let [warnings (opts :warnings true)] + (merge + ana/*cljs-warnings* + (if (or (true? warnings) + (false? warnings)) + (zipmap (keys ana/*cljs-warnings*) (repeat warnings)) + warnings) + (zipmap + [:unprovided :undeclared-var + :undeclared-ns :undeclared-ns-form] + (repeat (if (false? warnings) + false + warn-on-undeclared))))) ana/*cljs-static-fns* static-fns *repl-opts* opts] (let [env {:context :expr :locals {}} From b803b3d7b922bcd68fec598be910350a4a13eb11 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 1 Jun 2015 14:04:19 -0400 Subject: [PATCH 1092/4033] CLJS-1200: compare behaves differently from Clojure drop type check from cljs.core/compare and move into IComparable implementations. fast path in cljs.core/compare for numbers --- src/main/cljs/cljs/core.cljs | 39 +++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 13d47c93d..8bcdb650d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1103,7 +1103,9 @@ IComparable (-compare [this other] - (garray/defaultCompare (.valueOf this) (.valueOf other)))) + (if (instance? js/Date other) + (garray/defaultCompare (.valueOf this) (.valueOf other)) + (throw (js/Error. (str "Cannot compare " this " to " other)))))) (extend-type number IEquiv @@ -1935,13 +1937,18 @@ reduces them without incurring seq initialization" (nil? y) 1 - (identical? (type x) (type y)) - (if (implements? IComparable x) - (-compare ^not-native x y) - (garray/defaultCompare x y)) + (number? x) (if (number? y) + (garray/defaultCompare x y) + (throw (js/Error. (str "Cannot compare " x " to " y)))) + + (satisfies? IComparable x) + (-compare x y) :else - (throw (js/Error. "compare on non-nil objects of different types")))) + (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)))))) (defn ^:private compare-indexed "Compare indexed collection." @@ -8783,16 +8790,28 @@ reduces them without incurring seq initialization" ;; IComparable (extend-protocol IComparable Symbol - (-compare [x y] (compare-symbols x y)) + (-compare [x y] + (if (symbol? y) + (compare-symbols x y) + (throw (js/Error. (str "Cannot compare " x " to " y))))) Keyword - (-compare [x y] (compare-keywords x y)) + (-compare [x y] + (if (keyword? y) + (compare-keywords x y) + (throw (js/Error. (str "Cannot compare " x " to " y))))) Subvec - (-compare [x y] (compare-indexed x y)) + (-compare [x y] + (if (vector? y) + (compare-indexed x y) + (throw (js/Error. (str "Cannot compare " x " to " y))))) PersistentVector - (-compare [x y] (compare-indexed x y))) + (-compare [x y] + (if (vector? y) + (compare-indexed x y) + (throw (js/Error. (str "Cannot compare " x " to " y)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Reference Types ;;;;;;;;;;;;;;;; From 6cf5277444b41b2f35ae4278440c97d0c93076b8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 1 Jun 2015 14:18:45 -0400 Subject: [PATCH 1093/4033] bump to Clojure 1.7.0-RC1 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index b02e6172e..d94db50d3 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.7.0-beta2 + 1.7.0-RC1 com.google.javascript diff --git a/project.clj b/project.clj index c932cc2e4..5bc166942 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ :source-paths ["src/main/clojure"] :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure"] - :dependencies [[org.clojure/clojure "1.7.0-beta2"] + :dependencies [[org.clojure/clojure "1.7.0-RC1"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.2"] [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] diff --git a/script/bootstrap b/script/bootstrap index 2a50f0eb6..07d3fdc54 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.7.0-beta2" +CLOJURE_RELEASE="1.7.0-RC1" CLOSURE_RELEASE="20150505" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150505-021ed5b3" From 3bf951519b7974a6374b4cec12fb7158f4981de6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 1 Jun 2015 14:41:13 -0400 Subject: [PATCH 1094/4033] 0.0-3308 --- README.md | 6 +++--- changes.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f44ded519..533bc41c1 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3269 +Latest stable release: 0.0-3308 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3269"] +[org.clojure/clojurescript "0.0-3308"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3269 org.clojure clojurescript - 0.0-3269 + 0.0-3308 ``` diff --git a/changes.md b/changes.md index 989b7a6b0..8f649920d 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,35 @@ +## 0.0-3308 + +## Changes +* Clojure 1.7.0-RC1 dependency +* CLJS-1292: Add IPrintWithWriter implementation for TaggedLiteral +* add cljs.core/random-uuid +* flush immediately when forwarding Node process out & err +* CLJS-1256 cache UUID hash value +* CLJS-1226: Added the :end-run-test event to cljs.test and a dummy event handler for it + +## Fixes +* CLJS-1200: compare behaves differently from Clojure +* CLJS-1293: Warning settings not conveyed via REPL +* CLJS-1291: pprint whitespace/letter checks are incomplete +* CLJS-1288: compiler doesn't emit "goog.require" for foreign library when optimization level is not set +* check that we actually read something in cjls.repl.server/read-request +* clarify cljs.test/run-tests docstring +* CLJS-1285: load-file regression +* CLJS-1284: IndexedSeq -seq implementation incorrect for i >= alength of internal array +* finish CLJS-1176, remove stray .isAlive method call +* add zero arity `newline` to match Clojure +* CLJS-1206: Images in HTML don't show up when served from localhost:9000 +* CLJS-1272: :include-macros description inaccurate in require +* CLJS-1275: Corrected :test-paths in project.clj +* CLJS-1270: Docstring for delay not printed by cljs.repl/doc +* CLJS-1268: cljc support for cljs.closure/compile-file +* CLJS-1269: realized? docstring refers to promise and future +* match Clojure behavior for get on string / array. Need to coerce key into int. +* CLJS-1263: :libs regression, can no longer specify specific files +* CLJS-1209: Reduce produces additional final nil when used w/ eduction +* CLJS-1261: source fn fails for fns with conditional code + ## 0.0-3269 ## Fixes From 921a825d6049e711e873e8c4cfe864922902a4aa Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 3 Jun 2015 20:34:47 -0400 Subject: [PATCH 1095/4033] remove sample applications link, outdated --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 533bc41c1..d752250dc 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ Latest stable release: 0.0-3308 * Read the [Quick Start](https://github.com/clojure/clojurescript/wiki/Quick-Start) guide. * Read the [Documentation](https://github.com/clojure/clojurescript/wiki). * Try a [tutorial](https://github.com/clojure/clojurescript/wiki). -* Look at the [Sample Applications](https://github.com/clojure/clojurescript/tree/master/samples). * [Companies using ClojureScript](https://github.com/clojure/clojurescript/wiki/Companies-Using-ClojureScript) ## Questions, Feedback? ## From b73595e26f3f45f1fd8ab4d97fb3f9ab941ff696 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 6 Jun 2015 13:46:58 -0400 Subject: [PATCH 1096/4033] CLJS-1301: local :foreign-libs are not picked up the first time browser REPL is started The 2-arity version of cljs.closure/build simply adopts cljs.env/*compiler* if there is a root binding. The browser REPL was invoking this arity of build to generate the REPL iframe JS and thus clobbering it. Create a fresh ambient compiler environment instead in cljs.repl.browser/compile-client-js and invoke the 3-arity version of cljs.closure/build --- src/main/clojure/cljs/repl/browser.clj | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index eb78069a0..0c0e96d43 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -485,17 +485,21 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" ;; BrowserEnv (defn compile-client-js [opts] - (cljsc/build - '[(ns clojure.browser.repl.client - (:require [goog.events :as event] - [clojure.browser.repl :as repl])) - (defn start [url] - (event/listen js/window - "load" - (fn [] - (repl/start-evaluator url))))] - {:optimizations (:optimizations opts) - :output-dir (:working-dir opts)})) + (let [copts {:optimizations (:optimizations opts) + :output-dir (:working-dir opts)}] + ;; we're inside the REPL process where cljs.env/*compiler* is already + ;; established, need to construct a new one to avoid mutating the one + ;; the REPL uses + (cljsc/build + '[(ns clojure.browser.repl.client + (:require [goog.events :as event] + [clojure.browser.repl :as repl])) + (defn start [url] + (event/listen js/window + "load" + (fn [] + (repl/start-evaluator url))))] + copts (env/default-compiler-env copts)))) (defn create-client-js-file [opts file-path] (let [file (io/file file-path)] From 48d50bfbba31a80586c89916f8c28a4f09481a25 Mon Sep 17 00:00:00 2001 From: mfikes Date: Wed, 10 Jun 2015 13:03:02 -0400 Subject: [PATCH 1097/4033] CLJS-1307: Doc for ns missing Adds docstring for ns --- src/main/clojure/cljs/repl.cljc | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index ee582924c..1afd6925c 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -947,6 +947,50 @@ (.-instanceField instance)] :doc "The instance member form works for methods and fields. They all expand into calls to the dot operator at macroexpansion time."} + ns {:forms [(name docstring? attr-map? references*)] + :doc "You must currently use the ns form only with the following caveats + + * You must use the :only form of :use + * :require supports :as and :refer + - both options can be skipped + - in this case a symbol can be used as a libspec directly + - that is, (:require lib.foo) and (:require [lib.foo]) are both + supported and mean the same thing + - prefix lists are not supported + * The only option for :refer-clojure is :exclude + * :import is available for importing Google Closure classes + - ClojureScript types and records should be brought in with :use + or :require :refer, not :import ed + * Macros are written in Clojure, and are referenced via the new + :require-macros / :use-macros options to ns + - :require-macros and :use-macros support the same forms that + :require and :use do + + Implicit macro loading: If a namespace is required or used, and that + namespace itself requires or uses macros from its own namespace, then + the macros will be implicitly required or used using the same + specifications. This oftentimes leads to simplified library usage, + such that the consuming namespace need not be concerned about + explicitly distinguishing between whether certain vars are functions + or macros. + + Inline macro specification: As a convenience, :require can be given + either :include-macros true or :refer-macros [syms...]. Both desugar + into forms which explicitly load the matching Clojure file containing + macros. (This works independently of whether the namespace being + required internally requires or uses its own macros.) For example: + + (ns testme.core + (:require [foo.core :as foo :refer [foo-fn] :include-macros true] + [woz.core :as woz :refer [woz-fn] :refer-macros [app jx]])) + + is sugar for + + (ns testme.core + (:require [foo.core :as foo :refer [foo-fn]] + [woz.core :as woz :refer [woz-fn]]) + (:require-macros [foo.core :as foo] + [woz.core :as woz :refer [app jx]]))"} def {:forms [(def symbol doc-string? init?)] :doc "Creates and interns a global var with the name of symbol in the current namespace (*ns*) or locates such a var if From 92551f1bc39a7d0a0235b94e871a9fe66475686f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 11 Jun 2015 14:00:29 -0400 Subject: [PATCH 1098/4033] CLJS-934: In the REPL return vars after defs Introduces a new REPL option :def-emits-vars which defaults to true. This REPL option is conveyed to the analysis/compilation environment by cljs.repl. When set to true, the analyzer includes the var AST in the def AST and the compiler the var AST when emitting the JavaScript for def, arranging things so that a var is returned instead of the def init value. --- src/main/clojure/cljs/analyzer.cljc | 54 +++++++++++++++++------------ src/main/clojure/cljs/compiler.cljc | 15 +++++++- src/main/clojure/cljs/repl.cljc | 7 ++-- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cc8388520..4dd916b13 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -687,30 +687,38 @@ (defmulti parse (fn [op & rest] op)) -(defmethod parse 'var - [op env [_ sym :as form] _ _] +(defn- var-ast + [env sym] (let [var (resolve-var env sym (confirm-var-exists-throw)) expr-env (assoc env :context :expr)] - {:env env :op :var-special :form form - :var (analyze expr-env sym) - :sym (analyze expr-env `(quote ~(symbol (name (:ns var)) (name (:name var))))) - :meta (let [ks [:ns :doc :file :line :column] - m (merge - (let [user-meta (:meta var) - uks (keys user-meta)] - (zipmap uks - (map #(list 'quote (get user-meta %)) uks))) - (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) - :name `(quote ~(symbol (name (:name var)))) - :test `(when ~sym (.-cljs$lang$test ~sym)) - :arglists (let [arglists (:arglists var) - arglists' (if (= 'quote (first arglists)) - (second arglists) - arglists)] - (list 'quote - (doall (map with-meta arglists' - (:arglists-meta var)))))))] - (analyze expr-env m))})) + (if-let [var-ns (:ns var)] + {:var (analyze expr-env sym) + :sym (analyze expr-env `(quote ~(symbol (name var-ns) (name (:name var))))) + :meta (let [ks [:ns :doc :file :line :column] + m (merge + (let [user-meta (:meta var) + uks (keys user-meta)] + (zipmap uks + (map #(list 'quote (get user-meta %)) uks))) + (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) + :name `(quote ~(symbol (name (:name var)))) + :test `(when ~sym (.-cljs$lang$test ~sym)) + :arglists (let [arglists (:arglists var) + arglists' (if (= 'quote (first arglists)) + (second arglists) + arglists)] + (list 'quote + (doall (map with-meta arglists' + (:arglists-meta var)))))))] + (analyze expr-env m))}))) + +(defmethod parse 'var + [op env [_ sym :as form] _ _] + (merge + {:env env + :op :var-special + :form form} + (var-ast env sym))) (defmethod parse 'if [op env [_ test then else :as form] name _] @@ -935,6 +943,8 @@ :doc doc :jsdoc (:jsdoc sym-meta) :init init-expr} + (when (:def-emits-vars env) + {:var-ast (var-ast env sym)}) (when-let [test (:test sym-meta)] {:test (analyze (assoc env :context :expr) test)}) (when tag diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 525bd3ee8..5988cc88e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -447,15 +447,28 @@ (pr-str define)))))) (defmethod emit* :def - [{:keys [name var init env doc jsdoc export test]}] + [{:keys [name var init env doc jsdoc export test var-ast]}] (let [mname (munge name)] (when init (emit-comment doc (concat jsdoc (:jsdoc init))) + (when (:def-emits-vars env) + (when (= :return (:context env)) + (emitln "return (")) + (emitln "(function (){")) (emits var) (emits " = " (if-let [define (get-define mname jsdoc)] define init)) + (when (:def-emits-vars env) + (emitln "; return (") + (emits (merge + {:op :var-special + :env (assoc env :context :expr)} + var-ast)) + (emitln ");})()") + (when (= :return (:context env)) + (emitln ")"))) ;; NOTE: JavaScriptCore does not like this under advanced compilation ;; this change was primarily for REPL interactions - David ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 1afd6925c..0f55c6b16 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -428,6 +428,7 @@ (binding [ana/*cljs-file* filename] (let [ast (ana/analyze env form nil opts) js (comp/emit-str ast) + def-emits-vars (:def-emits-vars opts) wrap-js ;; TODO: check opts as well - David (if (:source-map repl-env) @@ -437,7 +438,7 @@ :gen-line 0})] (let [js (comp/emit-str (ana/no-warn - (ana/analyze (assoc env :repl-env repl-env) + (ana/analyze (assoc env :repl-env repl-env :def-emits-vars def-emits-vars) (wrap form) nil opts))) t (System/currentTimeMillis)] (str js @@ -457,7 +458,7 @@ "UTF-8"))))) (comp/emit-str (ana/no-warn - (ana/analyze (assoc env :repl-env repl-env) + (ana/analyze (assoc env :repl-env repl-env :def-emits-vars def-emits-vars) (wrap form) nil opts))))] (when (= (:op ast) :ns) (load-dependencies repl-env @@ -742,7 +743,7 @@ {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} (merge - {:cache-analysis true :source-map true} + {:cache-analysis true :source-map true :def-emits-vars true} (cljsc/add-implicit-options (merge-with (fn [a b] (if (nil? b) a b)) repl-opts From 0ca9dc8ed9522cda40d70d87ad6bd2ac235ed4f7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Jun 2015 10:43:37 -0400 Subject: [PATCH 1099/4033] :def-emits-vars -> :def-emits-var --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/main/clojure/cljs/compiler.cljc | 4 ++-- src/main/clojure/cljs/repl.cljc | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 4dd916b13..db5de82a3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -943,7 +943,7 @@ :doc doc :jsdoc (:jsdoc sym-meta) :init init-expr} - (when (:def-emits-vars env) + (when (:def-emits-var env) {:var-ast (var-ast env sym)}) (when-let [test (:test sym-meta)] {:test (analyze (assoc env :context :expr) test)}) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 5988cc88e..a6afe7b1d 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -451,7 +451,7 @@ (let [mname (munge name)] (when init (emit-comment doc (concat jsdoc (:jsdoc init))) - (when (:def-emits-vars env) + (when (:def-emits-var env) (when (= :return (:context env)) (emitln "return (")) (emitln "(function (){")) @@ -460,7 +460,7 @@ (if-let [define (get-define mname jsdoc)] define init)) - (when (:def-emits-vars env) + (when (:def-emits-var env) (emitln "; return (") (emits (merge {:op :var-special diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 0f55c6b16..442ea3db7 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -428,7 +428,7 @@ (binding [ana/*cljs-file* filename] (let [ast (ana/analyze env form nil opts) js (comp/emit-str ast) - def-emits-vars (:def-emits-vars opts) + def-emits-var (:def-emits-var opts) wrap-js ;; TODO: check opts as well - David (if (:source-map repl-env) @@ -438,7 +438,7 @@ :gen-line 0})] (let [js (comp/emit-str (ana/no-warn - (ana/analyze (assoc env :repl-env repl-env :def-emits-vars def-emits-vars) + (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) (wrap form) nil opts))) t (System/currentTimeMillis)] (str js @@ -458,7 +458,7 @@ "UTF-8"))))) (comp/emit-str (ana/no-warn - (ana/analyze (assoc env :repl-env repl-env :def-emits-vars def-emits-vars) + (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) (wrap form) nil opts))))] (when (= (:op ast) :ns) (load-dependencies repl-env @@ -743,7 +743,7 @@ {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts :or {warn-on-undeclared true}} (merge - {:cache-analysis true :source-map true :def-emits-vars true} + {:cache-analysis true :source-map true :def-emits-var true} (cljsc/add-implicit-options (merge-with (fn [a b] (if (nil? b) a b)) repl-opts From 47947137127c9a724fafbeb17c5f7af7412809de Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Thu, 11 Jun 2015 17:57:37 -0500 Subject: [PATCH 1100/4033] CLJS-1092: CommonJS Module processing Adds support for CommonJS modules. CommonJS modules can be included as foreign-libraries using the new compiler option :module-type and the value :commonjs, e.g. :foreign-libs [{:file "libs/greeting.js" :provides ["greeting"] :module-type :commonjs}] Foreign libraries that have a module-type will be processed before creating the dependecy index. They will be converted into Google Closure modules using functionality from the Google Closure compiler and will be written to disk. We can then change the compiler options to include the modules using the :libs option. We also need to set a mapping from the old module name to the new module name in the compiler environment. Later, when the sources get analyzed and compiled, we check our mappings and replace occurences of the old module name with the new module name. In case the user supplies a module-type other than :commonjs, a warning will be shown, the module will be written to disk as is and we won't add a mapping to the compiler environment. --- src/main/clojure/cljs/analyzer.cljc | 11 +++- src/main/clojure/cljs/closure.clj | 81 +++++++++++++++++++++++++++-- src/main/clojure/cljs/compiler.cljc | 3 +- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index db5de82a3..700300658 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -72,7 +72,8 @@ :single-segment-namespace true :munged-namespace true :ns-var-clash true - :extend-type-invalid-method-shape true}) + :extend-type-invalid-method-shape true + :unsupported-js-module-type true}) (def js-reserved #{"abstract" "boolean" "break" "byte" "case" @@ -222,6 +223,11 @@ (str "Bad extend-type method shape for protocol " protocol " method " method ", method arities must be grouped together")) +(defmethod error-message :unsupported-js-module-type + [warning-type {:keys [module-type file] :as info}] + (str "Unsupported JavaScript module type " module-type " for foreign library " + file ".")) + (defn ^:private default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] @@ -1377,6 +1383,9 @@ (do (basic-validate-ns-spec env macros? spec) (let [[lib & opts] spec + lib (if-let [js-module-name (get-in @env/*compiler* [:js-module-index (name lib)])] + (symbol js-module-name) + lib) {alias :as referred :refer :or {alias lib}} (apply hash-map opts) [rk uk] (if macros? [:require-macros :use-macros] [:require :use])] (when-not (or (symbol? alias) (nil? alias)) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9153b9541..166773a55 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -53,7 +53,9 @@ SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy - JSModule JSModuleGraph SourceMap] + JSModule JSModuleGraph SourceMap ProcessCommonJSModules + ES6ModuleLoader AbstractCompiler] + [com.google.javascript.rhino Node] [java.security MessageDigest] [javax.xml.bind DatatypeConverter] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey @@ -69,6 +71,25 @@ (defn random-string [length] (apply str (take length (repeatedly random-char)))) +(defmacro ^:private compile-if + "Evaluate `exp` and if it returns logical true and doesn't error, expand to + `then`. Else expand to `else`." + [exp then & else] + (if (try (eval exp) + (catch Throwable _ false)) + `(do ~then) + `(do ~@else))) + +(compile-if + (and (.getConstructor ProcessCommonJSModules + (into-array java.lang.Class + [com.google.javascript.jscomp.Compiler ES6ModuleLoader])) + (.getConstructor ES6ModuleLoader + (into-array java.lang.Class + [AbstractCompiler java.lang.String]))) + (def can-convert-js-module? true) + (def can-convert-js-module? false)) + ;; Closure API ;; =========== @@ -1168,6 +1189,34 @@ :else (str (random-string 5) ".js"))))) +(defmulti convert-js-module + "Takes a JavaScript module and rewrites it into a Google Closure-compatible + form. Returns the source of the new module as a single string." + :module-type) + +(compile-if can-convert-js-module? + (defmethod convert-js-module :commonjs [js] + (let [js-file (:file js) + path (.getParent (io/file js-file)) + module-root (cond->> path + (.startsWith path File/separator) (str ".") + (not (.startsWith path (str "." File/separator))) (str "." File/separator) + (not (.endsWith path File/separator)) (#(str % File/separator))) + ^List externs '() + ^SourceFile source-file (js-source-file js-file (slurp js-file)) + ^CompilerOptions options (CompilerOptions.) + closure-compiler (doto (make-closure-compiler) + (.init externs [source-file] options)) + es6-loader (ES6ModuleLoader. closure-compiler module-root) + cjs (ProcessCommonJSModules. closure-compiler es6-loader) + ^Node root (.parse closure-compiler source-file)] + (.process cjs nil root) + (.toSource closure-compiler root)))) + +(defmethod convert-js-module :default [js] + (ana/warning :unsupported-js-module-type @env/*compiler* js) + (deps/-source js)) + (defn write-javascript "Write or copy a JavaScript file to output directory. Only write if the file does not already exist. Return IJavaScript for the file on disk at the new @@ -1177,12 +1226,15 @@ out-name (rel-output-path js opts) out-file (io/file out-dir out-name) ijs {:url (deps/to-url out-file) + :out-file (.toString out-file) :requires (deps/-requires js) :provides (deps/-provides js) :group (:group js)}] (when-not (.exists out-file) (util/mkdirs out-file) - (spit out-file (deps/-source js))) + (if (:module-type js) + (spit out-file (convert-js-module js)) + (spit out-file (deps/-source js)))) (if (map? js) (merge js ijs) ijs))) @@ -1428,6 +1480,27 @@ (not (false? (:static-fns opts))) (assoc :static-fns true) (not (false? (:optimize-constants opts))) (assoc :optimize-constants true))))) +(defn process-js-modules + "Given the current compiler options, converts JavaScript modules to Google + Closure modules and writes them to disk. Adds mapping from original module + namespace to new module namespace to compiler env. Returns modified compiler + options where new modules are passed with :libs option." + [opts] + (let [js-modules (filter :module-type (:foreign-libs opts))] + (reduce (fn [opts {:keys [file module-type] :as lib}] + (if (= module-type :commonjs) + (let [module-name (ProcessCommonJSModules/toModuleName file) + ijs (write-javascript opts (deps/load-foreign-library lib))] + (doseq [provide (:provides ijs)] + (swap! env/*compiler* + #(update-in % [:js-module-index] assoc provide module-name))) + (-> opts + (update-in [:libs] (comp vec conj) (:out-file ijs)) + (update-in [:foreign-libs] + (comp vec (fn [libs] (remove #(= (:file %) file) libs)))))) + opts)) + opts js-modules))) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] @@ -1438,7 +1511,9 @@ ([source opts compiler-env] (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) - all-opts (add-implicit-options opts)] + all-opts (cond-> opts + true add-implicit-options + can-convert-js-module? process-js-modules)] (check-output-to opts) (check-output-dir opts) (check-source-map opts) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a6afe7b1d..3b758fa08 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -243,7 +243,8 @@ [{:keys [info env form] :as arg}] (let [var-name (:name info) info (if (= (namespace var-name) "js") - (name var-name) + (let [js-module-name (get-in @env/*compiler* [:js-module-index (name var-name)])] + (or js-module-name (name var-name))) info)] ; We need a way to write bindings out to source maps and javascript ; without getting wrapped in an emit-wrap calls, otherwise we get From 59a04ebffade090281d976fb7cd9033ab85db691 Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Thu, 11 Jun 2015 10:27:36 -0500 Subject: [PATCH 1101/4033] CLJS-1290: :refer does not work with Closure JS namespaces Allow :refer usage for Google Closure modules. --- src/main/clojure/cljs/analyzer.cljc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 700300658..073fd06d1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1311,10 +1311,13 @@ (defn check-uses [uses env] (doseq [[sym lib] uses] - (when (= (get-in @env/*compiler* [::namespaces lib :defs sym] ::not-found) ::not-found) - (throw - (error env - (error-message :undeclared-ns-form {:type "var" :lib lib :sym sym})))))) + (let [js-lib (get-in @env/*compiler* [:js-dependency-index (name lib)])] + (when (and (= (get-in @env/*compiler* [::namespaces lib :defs sym] ::not-found) ::not-found) + (not (= (get js-lib :group) :goog)) + (not (get js-lib :closure-lib))) + (throw + (error env + (error-message :undeclared-ns-form {:type "var" :lib lib :sym sym}))))))) (defn check-use-macros [use-macros env] (doseq [[sym lib] use-macros] From b9e107d8ba93a86aa7b9bb4e01a328a8cd8d0dbc Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Jun 2015 14:46:10 -0400 Subject: [PATCH 1102/4033] CLJS-1303 WIP add *ns* dynamic var add cljs.core/munge, cljs.core/demunge add cljs.core/find-ns, cljs.core/ns-map, cljs.core/ns-resolve --- src/main/cljs/cljs/core.cljs | 163 +++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8bcdb650d..83cc1ea5e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -26,6 +26,11 @@ :jsdoc ["@define {string}"]} *target* "default") +(def + ^{:dynamic true + :doc "Var bound to the current namespace. Only used for bootstrapping."} + *ns* nil) + (defonce ^{:doc "Each runtime environment provides a different way to print output. Whatever function *print-fn* is bound to will be passed any @@ -233,6 +238,62 @@ (def ITER_SYMBOL (.-iterator js/Symbol)) (def ITER_SYMBOL "@@iterator")) +(def ^{:jsdoc ["@enum {string"]} + CHAR_MAP + #js {"-" "_" + ":" "_COLON_" + "+" "_PLUS_" + ">" "_GT_" + "<" "_LT_" + "=" "_EQ_" + "~" "_TILDE_" + "!" "_BANG_" + "@" "_CIRCA_" + "#" "_SHARP_" + "'" "_SINGLEQUOTE_" + "\\\"" "_DOUBLEQUOTE_" + "%" "_PERCENT_" + "^" "_CARET_" + "&" "_AMPERSAND_" + "*" "_STAR_" + "|" "_BAR_" + "{" "_LBRACE_" + "}" "_RBRACE_" + "[" "_LBRACK_" + "]" "_RBRACK_" + "/" "_SLASH_" + "\\\\" "_BSLASH_" + "?" "_QMARK_"}) + +(def ^{:jsdoc ["@enum {string}"]} + DEMUNGE_MAP + #js {"_" "-" + "_COLON_" ":" + "_PLUS_" "+" + "_GT_" ">" + "_LT_" "<" + "_EQ_" "=" + "_TILDE_" "~" + "_BANG_" "!" + "_CIRCA_" "@" + "_SHARP_" "#" + "_SINGLEQUOTE_" "'" + "_DOUBLEQUOTE_" "\\\"" + "_PERCENT_" "%" + "_CARET_" "^" + "_AMPERSAND_" "&" + "_STAR_" "*" + "_BAR_" "|" + "_LBRACE_" "{" + "_RBRACE_" "}" + "_LBRACK_" "[" + "_RBRACK_" "]" + "_SLASH_" "/" + "_BSLASH_" "\\\\" + "_QMARK_" "?"}) + +(def DEMUNGE_PATTERN nil) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; arrays ;;;;;;;;;;;;;;;; (defn ^array make-array @@ -9704,3 +9765,105 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." [tag form] {:pre [(symbol? tag)]} (cljs.core.TaggedLiteral. tag form)) + +(defn demunge-pattern [] + (when-not DEMUNGE_PATTERN + (set! DEMUNGE_PATTERN + (let [ks (sort (fn [a b] (- (. b -length) (. a -length))) + (js-keys DEMUNGE_MAP))] + (loop [ks ks ret ""] + (if (seq ks) + (recur + (next ks) + (str + (cond-> ret + (not (identical? ret "")) (str "|")) + (first ks))) + (str ret "|\\$")))))) + DEMUNGE_PATTERN) + +(defn munge-str [name] + (loop [i 0 ret ""] + (if (< i (. name -length)) + (recur (inc i) + (let [c (.charAt name i)] + (if-let [sub (goog.object/get CHAR_MAP c)] + (str ret sub) + (str ret c)))) + ret))) + +(defn munge [name] + ((if (symbol? name) symbol str) + (munge-str (str name)))) + +(defn demunge-str [munged-name] + (let [r (js/RegExp. (demunge-pattern) "g")] + (loop [ret "" last-match-end 0] + (if-let [match (.exec r munged-name)] + (let [[x] match] + (recur + (str ret + (.substring munged-name last-match-end + (- (. r -lastIndex) (. x -length))) + (if (= x "$") "/" (goog.object/get DEMUNGE_MAP x))) + (. r -lastIndex))) + ret)))) + +(defn demunge [name] + ((if (symbol? name) symbol str) + (demunge-str (str name)))) + +(deftype Namespace [obj name] + Object + (find [_ sym] + (goog.object/get obj (str sym))) + (getMappings [_] + (letfn [(step [ret k] + (let [sym (symbol (demunge k))] + (assoc ret sym + (Var. (goog.object/get obj k) + (symbol (str name) (str sym)) nil))))] + (reduce step {} (js-keys obj)))) + IEquiv + (-equiv [_ other] + (if (instance? Namespace other) + (= name (.-name other)) + false)) + IHash + (-hash [_] + (hash name)) + INamed + (-name [_] name) + (-namespace [_] + (throw (js/Error. "Cannot call -namespace on Namespace")))) + +(defn find-ns [ns] + (letfn [(find-ns* [ctxt xs] + (cond + (nil? ctxt) nil + (nil? xs) (Namespace. ctxt ns) + :else (recur (goog.object/get ctxt (first xs)) (next xs))))] + (if-not js/COMPILED + (let [segs (-> ns str (.split "."))] + (when (goog.object/get (. goog/dependencies_ -nameToPath) (str ns)) + (condp identical? *target* + "nodejs" (find-ns* js/global segs) + "default" (find-ns* js/window segs) + (throw (js/Error. (str "find-ns not supported for target " *target*)))))) + (throw + (js/Error. + "find-ns not supported when Closure optimization applied"))))) + +(defn ns-name [ns-obj] + (.-name ns-obj)) + +(defn ns-map [ns-obj] + (.getMappings ns-obj)) + +(defn ns-resolve + ([ns sym] (ns-resolve ns nil sym)) + ([ns env sym] + (when-not (contains? env sym) + (when-let [ns-obj (find-ns ns)] + (Var. #(.find ns-obj (str sym)) + (symbol (str ns) (str sym)) nil))))) From 8b7b24696df0e6bad11d308cfd804c170b6cdb93 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 12 Jun 2015 16:42:59 -0400 Subject: [PATCH 1103/4033] CLJS-1303 wip fix demunge. Namespace support changes. --- src/main/cljs/cljs/core.cljs | 43 +++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 83cc1ea5e..f442644c8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9807,23 +9807,20 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (- (. r -lastIndex) (. x -length))) (if (= x "$") "/" (goog.object/get DEMUNGE_MAP x))) (. r -lastIndex))) - ret)))) + (str ret + (.substring munged-name last-match-end (.-length munged-name))))))) (defn demunge [name] ((if (symbol? name) symbol str) (demunge-str (str name)))) -(deftype Namespace [obj name] +(deftype Namespace [obj name mappings] Object (find [_ sym] - (goog.object/get obj (str sym))) + (Var. (goog.object/get obj (str sym)) + (symbol (str name) (str sym)) nil)) (getMappings [_] - (letfn [(step [ret k] - (let [sym (symbol (demunge k))] - (assoc ret sym - (Var. (goog.object/get obj k) - (symbol (str name) (str sym)) nil))))] - (reduce step {} (js-keys obj)))) + @mappings) IEquiv (-equiv [_ other] (if (instance? Namespace other) @@ -9837,11 +9834,11 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-namespace [_] (throw (js/Error. "Cannot call -namespace on Namespace")))) -(defn find-ns [ns] +(defn find-ns-obj [ns] (letfn [(find-ns* [ctxt xs] (cond (nil? ctxt) nil - (nil? xs) (Namespace. ctxt ns) + (nil? xs) ctxt :else (recur (goog.object/get ctxt (first xs)) (next xs))))] (if-not js/COMPILED (let [segs (-> ns str (.split "."))] @@ -9849,10 +9846,30 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (condp identical? *target* "nodejs" (find-ns* js/global segs) "default" (find-ns* js/window segs) - (throw (js/Error. (str "find-ns not supported for target " *target*)))))) + (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) (throw (js/Error. - "find-ns not supported when Closure optimization applied"))))) + "find-ns-obj not supported when Closure optimization applied"))))) + +(defn ns-interns* [sym] + (let [ns-obj (find-ns-obj sym)] + (letfn [(step [ret k] + (let [var-sym (symbol (demunge k))] + (assoc ret + var-sym (Var. (goog.object/get ns-obj k) + (symbol (str sym) (str var-sym)) nil))))] + (reduce step {} (js-keys ns-obj))))) + +(defn create-ns + ([sym] + (create-ns sym (find-ns-obj sym))) + ([sym ns-obj] + (create-ns sym (find-ns-obj sym) (ns-interns* sym))) + ([sym ns-obj mappings] + (Namespace. ns-obj sym (atom mappings)))) + +(defn find-ns [ns] + (create-ns ns (find-ns-obj ns))) (defn ns-name [ns-obj] (.-name ns-obj)) From f0b16174e077ae2bf39ea63d192a0c8fea886e31 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 12 Jun 2015 16:50:43 -0400 Subject: [PATCH 1104/4033] CLJS-1303 wip add cljs.core mappings to nses by default if mappings not explicitly supplied --- src/main/cljs/cljs/core.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f442644c8..bb1c34693 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9864,7 +9864,8 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." ([sym] (create-ns sym (find-ns-obj sym))) ([sym ns-obj] - (create-ns sym (find-ns-obj sym) (ns-interns* sym))) + (create-ns sym (find-ns-obj sym) + (merge (ns-interns* 'cljs.core) (ns-interns* sym)))) ([sym ns-obj mappings] (Namespace. ns-obj sym (atom mappings)))) From a039d0e63159a353df75bf3545ba2439d7af56eb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 12 Jun 2015 22:24:45 -0400 Subject: [PATCH 1105/4033] Bump Closure Compiler to the latest --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index d94db50d3..e98c7caa9 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20150505 + v20150609 org.clojure diff --git a/project.clj b/project.clj index 5bc166942..f40d70340 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.2"] [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] - [com.google.javascript/closure-compiler "v20150505"] + [com.google.javascript/closure-compiler "v20150609"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 07d3fdc54..f502589be 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.7.0-RC1" -CLOSURE_RELEASE="20150505" +CLOSURE_RELEASE="20150609" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150505-021ed5b3" RHINO_RELEASE="1_7R5" From f8af10da6429bab8e9838ac90a622b1858ffe983 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Jun 2015 00:01:04 -0400 Subject: [PATCH 1106/4033] toString for Namespace --- src/main/cljs/cljs/core.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index bb1c34693..86b8f879d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9821,6 +9821,8 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (symbol (str name) (str sym)) nil)) (getMappings [_] @mappings) + (toString [_] + (str name)) IEquiv (-equiv [_ other] (if (instance? Namespace other) From 3cf3098d7e183a02d290e5b7eaf1f5cca1508ecb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Jun 2015 11:03:36 -0400 Subject: [PATCH 1107/4033] CLJS-1306: Browser REPL :asset-path with leading slash breaks source map support `cljs.repl.browser/parse-file` removes the base url including the trailing slash as we need relative paths. However `:asset-path` may have a leading slash (i.e. "/js" or "/assets/js"). If a leading slash is specified just strip it. --- src/main/clojure/cljs/repl/browser.clj | 27 +++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 0c0e96d43..09f4c5a0e 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -227,14 +227,20 @@ (cond-> column (.endsWith column ")") (string/replace ")" "")))])) -(defn parse-file [{:keys [host host-port port] :as repl-env} file {:keys [asset-path] :as opts}] +(defn parse-file + "Given a browser file url convert it into a relative path that can be used + to locate the original source." + [{:keys [host host-port port] :as repl-env} file {:keys [asset-path] :as opts}] (let [base-url-pattern (Pattern/compile (str "http://" host ":" (or host-port port) "/"))] (if (re-find base-url-pattern file) (-> file (string/replace base-url-pattern "") (string/replace (Pattern/compile - (str "^" (or asset-path (util/output-directory opts)) "/")) "")) + ;; if :asset-path specified drop leading slash + (str "^" (or (and asset-path (string/replace asset-path #"^/" "")) + (util/output-directory opts)) "/")) + "")) (if-let [asset-root (:asset-root opts)] (string/replace file asset-root "") (throw @@ -296,6 +302,21 @@ {:ua-product :chrome} nil) + (parse-stacktrace {:host "localhost" :port 9000} + "Error: 1 is not ISeqable + at Object.cljs$core$seq [as seq] (http://localhost:9000/js/cljs/core.js:4258:8) + at Object.cljs$core$first [as first] (http://localhost:9000/js/cljs/core.js:4288:19) + at cljs$core$ffirst (http://localhost:9000/js/cljs/core.js:5356:34) + at http://localhost:9000/js/cljs/core.js:16971:89 + at cljs.core.map.cljs$core$map__2 (http://localhost:9000/js/cljs/core.js:16972:3) + at http://localhost:9000/js/cljs/core.js:10981:129 + at cljs.core.LazySeq.sval (http://localhost:9000/js/cljs/core.js:10982:3) + at cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (http://localhost:9000/js/cljs/core.js:11073:10) + at Object.cljs$core$seq [as seq] (http://localhost:9000/js/cljs/core.js:4239:13) + at Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/js/cljs/core.js:28706:14)" + {:ua-product :chrome} + {:asset-path "/js"}) + (parse-stacktrace {:host "localhost" :port 9000} "Error: 1 is not ISeqable at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4259:8) @@ -447,7 +468,7 @@ http://localhost:9000/out/goog/events/events.js:276:42" vec)) (comment - (parse-stacktrace nil + (parse-stacktrace {:host "localhost" :port 9000} "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4258:8 cljs$core$first@http://localhost:9000/out/cljs/core.js:4288:9 cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5356:24 From 929fb7337f944363290b5667a508098654d51086 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Jun 2015 12:04:10 -0400 Subject: [PATCH 1108/4033] cljs/js_deps.clj -> cljs/js_deps.cljc --- src/main/clojure/cljs/{js_deps.clj => js_deps.cljc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/clojure/cljs/{js_deps.clj => js_deps.cljc} (100%) diff --git a/src/main/clojure/cljs/js_deps.clj b/src/main/clojure/cljs/js_deps.cljc similarity index 100% rename from src/main/clojure/cljs/js_deps.clj rename to src/main/clojure/cljs/js_deps.cljc From 20d120731aa3063dc7ccf43dcd7f0ccd81e9acac Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Jun 2015 13:59:04 -0400 Subject: [PATCH 1109/4033] cljs.analyzer wip, many warnings, but can compile it --- src/main/clojure/cljs/analyzer.cljc | 634 +++++++++++----------- src/main/clojure/cljs/analyzer/macros.clj | 46 ++ src/main/clojure/cljs/env.cljc | 58 +- 3 files changed, 408 insertions(+), 330 deletions(-) create mode 100644 src/main/clojure/cljs/analyzer/macros.clj diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 073fd06d1..769a97684 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -7,21 +7,31 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.analyzer - (:refer-clojure :exclude [macroexpand-1]) - (:require [cljs.util :as util] - [clojure.java.io :as io] - [clojure.string :as string] - [clojure.set :as set] - [cljs.env :as env] - [cljs.js-deps :as deps] - [cljs.tagged-literals :as tags] - [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers] - [clojure.edn :as edn]) - (:import [java.io File Reader PushbackReader] - [java.net URL] - [clojure.lang Namespace Var] - [cljs.tagged_literals JSValue])) + #?(:clj (:refer-clojure :exclude [macroexpand-1]) + :cljs (:refer-clojure :exclude [macroexpand-1 ns-interns])) + #?(:cljs (:require-macros [cljs.analyzer.macros + :refer [no-warn wrapping-errors + disallowing-recur allowing-redef]])) + #?(:clj (:require [cljs.util :as util] + [clojure.java.io :as io] + [clojure.string :as string] + [clojure.set :as set] + [cljs.env :as env] + [cljs.js-deps :as deps] + [cljs.tagged-literals :as tags] + [clojure.tools.reader :as reader] + [clojure.tools.reader.reader-types :as readers] + [clojure.edn :as edn]) + :cljs (:require [clojure.string :as string] + [clojure.set :as set] + [cljs.env :as env] + [cljs.tools.reader :as reader] + [cljs.tools.reader.reader-types :as readers] + [cljs.reader :as edn])) + #?(:clj (:import [java.io File Reader PushbackReader] + [java.net URL] + [clojure.lang Namespace Var] + [cljs.tagged_literals JSValue]))) (set! *warn-on-reflection* true) @@ -237,9 +247,10 @@ (def ^:dynamic *cljs-warning-handlers* [default-warning-handler]) -(defmacro with-warning-handlers [handlers & body] - `(binding [*cljs-warning-handlers* ~handlers] - ~@body)) +#?(:clj + (defmacro with-warning-handlers [handlers & body] + `(binding [*cljs-warning-handlers* ~handlers] + ~@body))) (defn gen-constant-id [value] (let [prefix (cond @@ -288,15 +299,17 @@ (defn get-namespace [key] (get-in @env/*compiler* [::namespaces key])) -(defmacro no-warn [& body] - (let [no-warnings (zipmap (keys *cljs-warnings*) (repeat false))] - `(binding [*cljs-warnings* ~no-warnings] - ~@body))) +#?(:clj + (defmacro no-warn [& body] + (let [no-warnings (zipmap (keys *cljs-warnings*) (repeat false))] + `(binding [*cljs-warnings* ~no-warnings] + ~@body)))) -(defmacro all-warn [& body] - (let [all-warnings (zipmap (keys *cljs-warnings*) (repeat true))] - `(binding [*cljs-warnings* ~all-warnings] - ~@body))) +#?(:clj + (defmacro all-warn [& body] + (let [all-warnings (zipmap (keys *cljs-warnings*) (repeat true))] + `(binding [*cljs-warnings* ~all-warnings] + ~@body)))) (defn get-line [x env] (or (-> x meta :line) (:line env))) @@ -330,22 +343,24 @@ (load-file *cljs-macros-path*))) (intern-macros 'cljs.core)) -(defmacro with-core-macros - [path & body] - `(do - (when (not= *cljs-macros-path* ~path) - (reset! -cljs-macros-loaded false)) - (binding [*cljs-macros-path* ~path] - ~@body))) - -(defmacro with-core-macros-file - [path & body] - `(do - (when (not= *cljs-macros-path* ~path) - (reset! -cljs-macros-loaded false)) - (binding [*cljs-macros-path* ~path - *cljs-macros-is-classpath* false] - ~@body))) +#?(:clj + (defmacro with-core-macros + [path & body] + `(do + (when (not= *cljs-macros-path* ~path) + (reset! -cljs-macros-loaded false)) + (binding [*cljs-macros-path* ~path] + ~@body)))) + +#?(:clj + (defmacro with-core-macros-file + [path & body] + `(do + (when (not= *cljs-macros-path* ~path) + (reset! -cljs-macros-loaded false)) + (binding [*cljs-macros-path* ~path + *cljs-macros-is-classpath* false] + ~@body)))) (defn empty-env "Construct an empty analysis environment. Required to analyze forms." @@ -394,13 +409,14 @@ (defn analysis-error? [ex] (= :cljs/analysis-error (:tag (ex-data ex)))) -(defmacro wrapping-errors [env & body] - `(try - ~@body - (catch Throwable err# - (if (analysis-error? err#) - (throw err#) - (throw (error ~env (.getMessage err#) err#)))))) +#?(:clj + (defmacro wrapping-errors [env & body] + `(try + ~@body + (catch Throwable err# + (if (analysis-error? err#) + (throw err#) + (throw (error ~env (.getMessage err#) err#))))))) (defn implicit-import? [env prefix suffix] (contains? '#{goog goog.object goog.string goog.array Math} prefix)) @@ -601,11 +617,13 @@ (def ^:dynamic *loop-lets* ()) (def ^:dynamic *allow-redef* false) -(defmacro disallowing-recur [& body] - `(binding [*recur-frames* (cons nil *recur-frames*)] ~@body)) +#?(:clj + (defmacro disallowing-recur [& body] + `(binding [*recur-frames* (cons nil *recur-frames*)] ~@body))) -(defmacro allowing-redef [& body] - `(binding [*allow-redef* true] ~@body)) +#?(:clj + (defmacro allowing-redef [& body] + `(binding [*allow-redef* true] ~@body))) ;; TODO: move this logic out - David (defn analyze-keyword @@ -1302,12 +1320,15 @@ (doseq [dep deps] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) - (deps/find-classpath-lib dep)) - (if-let [src (locate-src dep)] - (analyze-file src opts) - (throw - (error env - (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))) + #?(:clj (deps/find-classpath-lib dep))) + #?(:clj (if-let [src (locate-src dep)] + (analyze-file src opts) + (throw + (error env + (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))) + :cljs (throw + (error env + (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))) (defn check-uses [uses env] (doseq [[sym lib] uses] @@ -1564,7 +1585,7 @@ (remove #{:reload :reload-all} libs)))) {} (remove (fn [[r]] (= r :refer-clojure)) args))] (when (and *analyze-deps* (seq @deps)) - (analyze-deps name @deps env opts)) + #?(:clj (analyze-deps name @deps env opts))) (when (and *analyze-deps* (seq uses)) (check-uses uses env)) (set! *cljs-ns* name) @@ -2029,249 +2050,256 @@ tag (assoc :tag tag)))))) (or *passes* [infer-type])))))) -(defn- source-path - "Returns a path suitable for providing to tools.reader as a 'filename'." - [x] - (cond - (instance? File x) (.getAbsolutePath ^File x) - :default (str x))) - -(defn forms-seq* - "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally - accepts a filename argument which will be used in any emitted errors." - ([^Reader rdr] (forms-seq* rdr nil)) - ([^Reader rdr filename] - {:pre [(instance? Reader rdr)]} - (let [eof-sentinel (Object.) - opts (merge - {:eof eof-sentinel} - (if (and filename (= (util/ext filename) "cljc")) - {:read-cond :allow :features #{:cljs}})) - pbr (readers/indexing-push-back-reader - (PushbackReader. rdr) 1 filename) - data-readers tags/*cljs-data-readers* - forms-seq_ - (fn forms-seq_ [] - (lazy-seq - (let [form (binding [*ns* (create-ns *cljs-ns*) - reader/*data-readers* data-readers - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros) - (get-namespace *cljs-ns*)))] - (reader/read opts pbr))] - (if (identical? form eof-sentinel) - (.close rdr) - (cons form (forms-seq_))))))] - (forms-seq_)))) - -(defn forms-seq - "DEPRECATED: Seq of Clojure/ClojureScript forms from [f], which can be anything - for which `clojure.java.io/reader` can produce a `java.io.Reader`. Optionally - accepts a [filename] argument, which the reader will use in any emitted errors." - ([f] (forms-seq f (source-path f))) - ([f filename] (forms-seq f filename false)) - ([f filename return-reader?] - (let [rdr (io/reader f) - pbr (readers/indexing-push-back-reader - (PushbackReader. rdr) 1 filename) - data-readers tags/*cljs-data-readers* - forms-seq* - (fn forms-seq* [] - (lazy-seq - (let [eof-sentinel (Object.) - form (binding [*ns* (create-ns *cljs-ns*) - reader/*data-readers* data-readers - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros) - (get-namespace *cljs-ns*)))] - (reader/read pbr nil eof-sentinel))] - (if (identical? form eof-sentinel) - (.close rdr) - (cons form (forms-seq*))))))] - (if (true? return-reader?) - [(forms-seq*) rdr] - (forms-seq*))))) - -(defn parse-ns - "Helper for parsing only the essential namespace information from a - ClojureScript source file and returning a cljs.closure/IJavaScript compatible - map _not_ a namespace AST node. - - By default does not load macros or perform any analysis of dependencies. If - opts parameter provided :analyze-deps and :load-macros keys their values will - be used for *analyze-deps* and *load-macros* bindings respectively. This - function does _not_ side-effect the ambient compilation environment unless - requested via opts where :restore is false." - ([src] (parse-ns src nil nil)) - ([src opts] (parse-ns src nil opts)) - ([src dest opts] - (env/ensure - (let [src (if (symbol? src) - (util/ns->source src) - src) - compiler-env @env/*compiler* - [ijs compiler-env'] - (binding [env/*compiler* (atom compiler-env) - *cljs-ns* 'cljs.user - *cljs-file* src - *macro-infer* - (or (when (contains? opts :macro-infer) - (:macro-infer opts)) - false) - *analyze-deps* - (or (when (contains? opts :analyze-deps) - (:analyze-deps opts)) +#?(:clj + (defn- source-path + "Returns a path suitable for providing to tools.reader as a 'filename'." + [x] + (cond + (instance? File x) (.getAbsolutePath ^File x) + :default (str x)))) + +#?(:clj + (defn forms-seq* + "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally + accepts a filename argument which will be used in any emitted errors." + ([^Reader rdr] (forms-seq* rdr nil)) + ([^Reader rdr filename] + {:pre [(instance? Reader rdr)]} + (let [eof-sentinel (Object.) + opts (merge + {:eof eof-sentinel} + (if (and filename (= (util/ext filename) "cljc")) + {:read-cond :allow :features #{:cljs}})) + pbr (readers/indexing-push-back-reader + (PushbackReader. rdr) 1 filename) + data-readers tags/*cljs-data-readers* + forms-seq_ + (fn forms-seq_ [] + (lazy-seq + (let [form (binding [*ns* (create-ns *cljs-ns*) + reader/*data-readers* data-readers + reader/*alias-map* + (apply merge + ((juxt :requires :require-macros) + (get-namespace *cljs-ns*)))] + (reader/read opts pbr))] + (if (identical? form eof-sentinel) + (.close rdr) + (cons form (forms-seq_))))))] + (forms-seq_))))) + +#?(:clj + (defn forms-seq + "DEPRECATED: Seq of Clojure/ClojureScript forms from [f], which can be anything + for which `clojure.java.io/reader` can produce a `java.io.Reader`. Optionally + accepts a [filename] argument, which the reader will use in any emitted errors." + ([f] (forms-seq f (source-path f))) + ([f filename] (forms-seq f filename false)) + ([f filename return-reader?] + (let [rdr (io/reader f) + pbr (readers/indexing-push-back-reader + (PushbackReader. rdr) 1 filename) + data-readers tags/*cljs-data-readers* + forms-seq* + (fn forms-seq* [] + (lazy-seq + (let [eof-sentinel (Object.) + form (binding [*ns* (create-ns *cljs-ns*) + reader/*data-readers* data-readers + reader/*alias-map* + (apply merge + ((juxt :requires :require-macros) + (get-namespace *cljs-ns*)))] + (reader/read pbr nil eof-sentinel))] + (if (identical? form eof-sentinel) + (.close rdr) + (cons form (forms-seq*))))))] + (if (true? return-reader?) + [(forms-seq*) rdr] + (forms-seq*)))))) + +#?(:clj + (defn parse-ns + "Helper for parsing only the essential namespace information from a + ClojureScript source file and returning a cljs.closure/IJavaScript compatible + map _not_ a namespace AST node. + + By default does not load macros or perform any analysis of dependencies. If + opts parameter provided :analyze-deps and :load-macros keys their values will + be used for *analyze-deps* and *load-macros* bindings respectively. This + function does _not_ side-effect the ambient compilation environment unless + requested via opts where :restore is false." + ([src] (parse-ns src nil nil)) + ([src opts] (parse-ns src nil opts)) + ([src dest opts] + (env/ensure + (let [src (if (symbol? src) + (util/ns->source src) + src) + compiler-env @env/*compiler* + [ijs compiler-env'] + (binding [env/*compiler* (atom compiler-env) + *cljs-ns* 'cljs.user + *cljs-file* src + *macro-infer* + (or (when (contains? opts :macro-infer) + (:macro-infer opts)) + false) + *analyze-deps* + (or (when (contains? opts :analyze-deps) + (:analyze-deps opts)) false) - *load-macros* - (or (when (contains? opts :load-macros) - (:load-macros opts)) + *load-macros* + (or (when (contains? opts :load-macros) + (:load-macros opts)) false)] - (with-open [rdr (io/reader src)] - (loop [forms (forms-seq* rdr (source-path src))] - (if (seq forms) - (let [env (empty-env) - ast (no-warn (analyze env (first forms) nil opts))] - (if (= (:op ast) :ns) - (let [ns-name (:name ast) - deps (merge (:uses ast) (:requires ast))] - (.close ^Reader rdr) - [(merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in compiler-env [:options :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src - :ast ast} - (when (and dest (.exists ^File dest)) - {:lines (with-open [reader (io/reader dest)] - (-> reader line-seq count))})) - @env/*compiler*]) - (recur (rest forms)))) - (throw (AssertionError. (str "No ns form found in " src)))))))] - (when (false? (:restore opts)) - (swap! env/*compiler* update-in [::namespaces] merge - (get compiler-env' ::namespaces))) - ijs)))) - -(defn cache-file - "Given a ClojureScript source file returns the read/write path to the analysis - cache file. Defaults to the read path which is usually also the write path." - ([src] (cache-file src "out")) - ([src output-dir] (cache-file src (parse-ns src) output-dir)) - ([src ns-info output-dir] (cache-file src (parse-ns src) output-dir :read)) - ([src ns-info output-dir mode] - {:pre [(map? ns-info)]} - (if-let [core-cache - (and (= mode :read) - (= (:ns ns-info) 'cljs.core) - (io/resource "cljs/core.cljs.cache.aot.edn"))] - core-cache - (let [target-file (util/to-target-file output-dir ns-info - (util/ext (:source-file ns-info)))] - (io/file (str target-file ".cache.edn")))))) - -(defn requires-analysis? - "Given a src, a resource, and output-dir, a compilation output directory - return true or false depending on whether src needs to be (re-)analyzed. - Can optionally pass cache, the analysis cache file." - ([src] (requires-analysis? src "out")) - ([src output-dir] - (let [cache (cache-file src output-dir)] - (requires-analysis? src cache output-dir))) - ([src cache output-dir] - (cond - (and (util/url? cache) - (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.edn")) - false - - (and (util/file? cache) - (not (.exists ^File cache))) - true - - :else - (let [out-src (util/to-target-file output-dir (parse-ns src))] - (if (not (.exists out-src)) - true - (if (> (util/last-modified src) (util/last-modified cache)) + (with-open [rdr (io/reader src)] + (loop [forms (forms-seq* rdr (source-path src))] + (if (seq forms) + (let [env (empty-env) + ast (no-warn (analyze env (first forms) nil opts))] + (if (= (:op ast) :ns) + (let [ns-name (:name ast) + deps (merge (:uses ast) (:requires ast))] + (.close ^Reader rdr) + [(merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in compiler-env [:options :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src + :ast ast} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))})) + @env/*compiler*]) + (recur (rest forms)))) + (throw (AssertionError. (str "No ns form found in " src)))))))] + (when (false? (:restore opts)) + (swap! env/*compiler* update-in [::namespaces] merge + (get compiler-env' ::namespaces))) + ijs))))) + +#?(:clj + (defn cache-file + "Given a ClojureScript source file returns the read/write path to the analysis + cache file. Defaults to the read path which is usually also the write path." + ([src] (cache-file src "out")) + ([src output-dir] (cache-file src (parse-ns src) output-dir)) + ([src ns-info output-dir] (cache-file src (parse-ns src) output-dir :read)) + ([src ns-info output-dir mode] + {:pre [(map? ns-info)]} + (if-let [core-cache + (and (= mode :read) + (= (:ns ns-info) 'cljs.core) + (io/resource "cljs/core.cljs.cache.aot.edn"))] + core-cache + (let [target-file (util/to-target-file output-dir ns-info + (util/ext (:source-file ns-info)))] + (io/file (str target-file ".cache.edn"))))))) + +#?(:clj + (defn requires-analysis? + "Given a src, a resource, and output-dir, a compilation output directory + return true or false depending on whether src needs to be (re-)analyzed. + Can optionally pass cache, the analysis cache file." + ([src] (requires-analysis? src "out")) + ([src output-dir] + (let [cache (cache-file src output-dir)] + (requires-analysis? src cache output-dir))) + ([src cache output-dir] + (cond + (and (util/url? cache) + (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.edn")) + false + + (and (util/file? cache) + (not (.exists ^File cache))) + true + + :else + (let [out-src (util/to-target-file output-dir (parse-ns src))] + (if (not (.exists out-src)) true - (let [version' (util/compiled-by-version cache) - version (util/clojurescript-version)] - (and version (not= version version'))))))))) - -(defn write-analysis-cache [ns cache-file] - (util/mkdirs cache-file) - (spit cache-file - (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" - (pr-str - (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros))))) - -(defn analyze-file - "Given a java.io.File, java.net.URL or a string identifying a resource on the - classpath attempt to analyze it. - - This function side-effects the ambient compilation environment - `cljs.env/*compiler*` to aggregate analysis information. opts argument is - compiler options, if :cache-analysis true will cache analysis to - \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a - meaningful value." - ([f] (analyze-file f nil)) - ([f opts] - (binding [*file-defs* (atom #{})] - (let [output-dir (util/output-directory opts) - res (cond - (instance? File f) f - (instance? URL f) f - (re-find #"^file://" f) (URL. f) - :else (io/resource f))] - (assert res (str "Can't find " f " in classpath")) - (env/ensure - (let [ns-info (parse-ns res) - path (if (instance? File res) - (.getPath ^File res) - (.getPath ^URL res)) - cache (when (:cache-analysis opts) - (cache-file res ns-info output-dir))] - (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info) :defs]) - (if (or (not cache) - (requires-analysis? res output-dir)) - (binding [*cljs-ns* 'cljs.user - *cljs-file* path - reader/*alias-map* (or reader/*alias-map* {})] - (when (or *verbose* (:verbose opts)) - (util/debug-prn "Analyzing" (str res))) - (let [env (assoc (empty-env) :build-options opts) - ns (with-open [rdr (io/reader res)] - (loop [ns nil forms (seq (forms-seq* rdr (util/path res)))] - (if forms - (let [form (first forms) - env (assoc env :ns (get-namespace *cljs-ns*)) - ast (analyze env form nil opts)] - (if (= (:op ast) :ns) - (recur (:name ast) (next forms)) - (recur ns (next forms)))) - ns)))] - (when (and cache (true? (:cache-analysis opts))) - (write-analysis-cache ns cache)))) - ;; we want want to keep dependency analysis information - ;; don't revert the environment - David - (let [{:keys [ns]} - (parse-ns res - (merge opts - {:restore false - :analyze-deps true - :load-macros true}))] - (when (or *verbose* (:verbose opts)) - (util/debug-prn "Reading analysis cache for" (str res))) - (swap! env/*compiler* - (fn [cenv] - (let [cached-ns (edn/read-string (slurp cache))] - (doseq [x (get-in cached-ns [::constants :order])] - (register-constant! x)) - (-> cenv - (assoc-in [::namespaces ns] cached-ns)))))))))))))) + (if (> (util/last-modified src) (util/last-modified cache)) + true + (let [version' (util/compiled-by-version cache) + version (util/clojurescript-version)] + (and version (not= version version')))))))))) + +#?(:clj + (defn write-analysis-cache [ns cache-file] + (util/mkdirs cache-file) + (spit cache-file + (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" + (pr-str + (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)))))) + +#?(:clj + (defn analyze-file + "Given a java.io.File, java.net.URL or a string identifying a resource on the + classpath attempt to analyze it. + + This function side-effects the ambient compilation environment + `cljs.env/*compiler*` to aggregate analysis information. opts argument is + compiler options, if :cache-analysis true will cache analysis to + \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a + meaningful value." + ([f] (analyze-file f nil)) + ([f opts] + (binding [*file-defs* (atom #{})] + (let [output-dir (util/output-directory opts) + res (cond + (instance? File f) f + (instance? URL f) f + (re-find #"^file://" f) (URL. f) + :else (io/resource f))] + (assert res (str "Can't find " f " in classpath")) + (env/ensure + (let [ns-info (parse-ns res) + path (if (instance? File res) + (.getPath ^File res) + (.getPath ^URL res)) + cache (when (:cache-analysis opts) + (cache-file res ns-info output-dir))] + (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info) :defs]) + (if (or (not cache) (requires-analysis? res output-dir)) + (binding [*cljs-ns* 'cljs.user + *cljs-file* path + reader/*alias-map* (or reader/*alias-map* {})] + (when (or *verbose* (:verbose opts)) + (util/debug-prn "Analyzing" (str res))) + (let [env (assoc (empty-env) :build-options opts) + ns (with-open [rdr (io/reader res)] + (loop [ns nil forms (seq (forms-seq* rdr (util/path res)))] + (if forms + (let [form (first forms) + env (assoc env :ns (get-namespace *cljs-ns*)) + ast (analyze env form nil opts)] + (if (= (:op ast) :ns) + (recur (:name ast) (next forms)) + (recur ns (next forms)))) + ns)))] + (when (and cache (true? (:cache-analysis opts))) + (write-analysis-cache ns cache)))) + ;; we want want to keep dependency analysis information + ;; don't revert the environment - David + (let [{:keys [ns]} + (parse-ns res + (merge opts + {:restore false + :analyze-deps true + :load-macros true}))] + (when (or *verbose* (:verbose opts)) + (util/debug-prn "Reading analysis cache for" (str res))) + (swap! env/*compiler* + (fn [cenv] + (let [cached-ns (edn/read-string (slurp cache))] + (doseq [x (get-in cached-ns [::constants :order])] + (register-constant! x)) + (-> cenv + (assoc-in [::namespaces ns] cached-ns))))))))))))))) diff --git a/src/main/clojure/cljs/analyzer/macros.clj b/src/main/clojure/cljs/analyzer/macros.clj new file mode 100644 index 000000000..5deabae53 --- /dev/null +++ b/src/main/clojure/cljs/analyzer/macros.clj @@ -0,0 +1,46 @@ +(ns cljs.analyzer.macros + (:refer-clojure :exclude [binding]) + (:require [cljs.core :refer [binding]])) + +(defmacro with-warning-handlers [handlers & body] + `(binding [cljs.analyzer/*cljs-warning-handlers* ~handlers] + ~@body)) + +(defmacro no-warn [& body] + `(binding [cljs.analyzer/*cljs-warnings* + (zipmap (keys cljs.analyzer/*cljs-warnings*) (repeat false))] + ~@body)) + +(defmacro with-core-macros + [path & body] + `(do + (when (not= cljs.analyzer/*cljs-macros-path* ~path) + (reset! cljs.analyzer/-cljs-macros-loaded false)) + (binding [cljs.analyzer/*cljs-macros-path* ~path] + ~@body))) + +(defmacro with-core-macros-file + [path & body] + `(do + (when (not= cljs.analyzer/*cljs-macros-path* ~path) + (reset! cljs.analyzer/-cljs-macros-loaded false)) + (binding [cljs.analyzer/*cljs-macros-path* ~path + cljs.analyzer/*cljs-macros-is-classpath* false] + ~@body))) + +(defmacro wrapping-errors [env & body] + `(try + ~@body + (catch :default err# + (if (cljs.analyzer/analysis-error? err#) + (throw err#) + (throw (error ~env (.-message err#) err#)))))) + +(defmacro disallowing-recur [& body] + `(binding [cljs.analyzer/*recur-frames* + (cons nil *cljs.analyzer/*recur-frames*)] + ~@body)) + +(defmacro allowing-redef [& body] + `(binding [cljs.analyzer/*allow-redef* true] + ~@body)) \ No newline at end of file diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 09ed531d0..5d22e092c 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -9,7 +9,7 @@ (ns ^{:doc "A namespace that exists solely to provide a place for \"compiler\" state that is accessed/maintained by many different components."} cljs.env - (:require [cljs.js-deps :refer (js-dependency-index)]) + #?(:clj (:require [cljs.js-deps :refer (js-dependency-index)])) (:refer-clojure :exclude [ensure])) ;; bit of a misnomer, but: an atom containing a map that serves as the bag of @@ -41,31 +41,35 @@ state that is accessed/maintained by many different components."} (defn default-compiler-env ([] (default-compiler-env {})) ([options] - (atom {:options options - :js-dependency-index (js-dependency-index options)}))) + (atom (merge {:options options} + #?(:clj {:js-dependency-index (js-dependency-index options)}))))) -(defmacro with-compiler-env - "Evaluates [body] with [env] bound as the value of the `*compiler*` var in -this namespace." - [env & body] - `(let [env# ~env - env# (cond - (map? env#) (atom env#) - (and (instance? clojure.lang.Atom env#) - (map? @env#)) env# - :default (throw (IllegalArgumentException. - (str "Compiler environment must be a map or atom containing a map, not " - (class env#)))))] - (binding [*compiler* env#] ~@body))) +#?(:clj + (defmacro with-compiler-env + "Evaluates [body] with [env] bound as the value of the `*compiler*` var in + this namespace." + [env & body] + `(let [env# ~env + env# (cond + (map? env#) (atom env#) + (and (instance? clojure.lang.Atom env#) + (map? @env#)) env# + :default (throw (IllegalArgumentException. + (str "Compiler environment must be a map or atom containing a map, not " + (class env#)))))] + (binding [*compiler* env#] ~@body)))) -(defmacro ensure - [& body] - `(let [val# *compiler*] - (if (nil? val#) - (push-thread-bindings - (hash-map (var *compiler*) (default-compiler-env)))) - (try - ~@body - (finally - (if (nil? val#) - (pop-thread-bindings)))))) +#?(:clj + (defmacro ensure + [& body] + `(let [val# *compiler*] + (if (nil? val#) + (push-thread-bindings + (hash-map (var *compiler*) (default-compiler-env)))) + (try + ~@body + (finally + (if (nil? val#) + (pop-thread-bindings)))))) + :cljs + (def ensure identity)) From 3b87e2c52dd7f7ffa69a00cbe51a062e86069834 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Jun 2015 22:03:42 -0400 Subject: [PATCH 1110/4033] typos --- src/main/clojure/cljs/analyzer/macros.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/macros.clj b/src/main/clojure/cljs/analyzer/macros.clj index 5deabae53..fb5e7b729 100644 --- a/src/main/clojure/cljs/analyzer/macros.clj +++ b/src/main/clojure/cljs/analyzer/macros.clj @@ -34,11 +34,11 @@ (catch :default err# (if (cljs.analyzer/analysis-error? err#) (throw err#) - (throw (error ~env (.-message err#) err#)))))) + (throw (cljs.analyzer/error ~env (.-message err#) err#)))))) (defmacro disallowing-recur [& body] `(binding [cljs.analyzer/*recur-frames* - (cons nil *cljs.analyzer/*recur-frames*)] + (cons nil cljs.analyzer/*recur-frames*)] ~@body)) (defmacro allowing-redef [& body] From 5f61553a60ce805135d111ce36e2d2423d2b2a89 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Jun 2015 22:58:01 -0400 Subject: [PATCH 1111/4033] cljs.analyzer bootstrap wip --- src/main/cljs/cljs/core.cljs | 13 +++++ src/main/cljs/cljs/pprint.clj | 12 ++--- src/main/cljs/cljs/pprint.cljs | 2 - src/main/clojure/cljs/analyzer.cljc | 80 ++++++++++++++++++++--------- src/test/cljs/cljs/pprint_test.cljs | 2 +- 5 files changed, 75 insertions(+), 34 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 86b8f879d..6978fa4c5 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -31,6 +31,14 @@ :doc "Var bound to the current namespace. Only used for bootstrapping."} *ns* nil) +(def + ^{:dynamic true} + *out* nil) + +(def + ^{:dynamic true} + *err* nil) + (defonce ^{:doc "Each runtime environment provides a different way to print output. Whatever function *print-fn* is bound to will be passed any @@ -187,6 +195,11 @@ [x] (goog/isString x)) +(defn ^boolean char? + "Returns true if x is a JavaScript char." + [x] + (gstring/isUnicodeChar x)) + (set! *unchecked-if* true) (defn ^boolean native-satisfies? "Internal - do not use!" diff --git a/src/main/cljs/cljs/pprint.clj b/src/main/cljs/cljs/pprint.clj index 236ec03e2..01410b8c4 100644 --- a/src/main/cljs/cljs/pprint.clj +++ b/src/main/cljs/cljs/pprint.clj @@ -17,11 +17,11 @@ (defmacro with-pretty-writer [base-writer & body] `(let [base-writer# ~base-writer new-writer# (not (pretty-writer? base-writer#))] - (cljs.core/binding [~'*out* (if new-writer# + (cljs.core/binding [cljs.core/*out* (if new-writer# (make-pretty-writer base-writer# *print-right-margin* *print-miser-width*) base-writer#)] ~@body - (-ppflush ~'*out*)))) + (-ppflush cljs.core/*out*)))) (defmacro getf @@ -66,16 +66,16 @@ [& args] (let [[options body] (parse-lb-options #{:prefix :per-line-prefix :suffix} args)] `(do (if (cljs.pprint/level-exceeded) - (~'-write cljs.pprint/*out* "#") + (~'-write cljs.core/*out* "#") (do (cljs.core/binding [cljs.pprint/*current-level* (inc cljs.pprint/*current-level*) cljs.pprint/*current-length* 0] - (cljs.pprint/start-block cljs.pprint/*out* + (cljs.pprint/start-block cljs.core/*out* ~(:prefix options) ~(:per-line-prefix options) ~(:suffix options)) ~@body - (cljs.pprint/end-block cljs.pprint/*out*)))) + (cljs.pprint/end-block cljs.core/*out*)))) nil))) (defn- pll-mod-body [var-sym body] @@ -98,7 +98,7 @@ `(loop ~(apply vector count-var 0 bindings) (if (or (not cljs.core/*print-length*) (< ~count-var cljs.core/*print-length*)) (do ~@mod-body) - (~'-write cljs.pprint/*out* "..."))))) + (~'-write cljs.core/*out* "..."))))) (defn- process-directive-table-element [[char params flags bracket-info & generator-fn]] [char, diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 75c6c6458..49b18a447 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -18,8 +18,6 @@ [goog.string :as gstring]) (:import [goog.string StringBuffer])) -(def ^:dynamic *out* nil) - ;;====================================================================== ;; override print fns to use *out* ;;====================================================================== diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 769a97684..dad655a7d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -12,7 +12,7 @@ #?(:cljs (:require-macros [cljs.analyzer.macros :refer [no-warn wrapping-errors disallowing-recur allowing-redef]])) - #?(:clj (:require [cljs.util :as util] + #?(:clj (:require [cljs.util :as util :refer [ns->relpath]] [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] @@ -33,11 +33,11 @@ [clojure.lang Namespace Var] [cljs.tagged_literals JSValue]))) -(set! *warn-on-reflection* true) +#?(:clj (set! *warn-on-reflection* true)) (def ^:dynamic *cljs-ns* 'cljs.user) (def ^:dynamic *cljs-file* nil) -(def ^:dynamic *unchecked-if* (atom false)) +#?(:clj (def ^:dynamic *unchecked-if* (atom false))) (def ^:dynamic *cljs-static-fns* false) (def ^:dynamic *cljs-macros-path* "/cljs/core") (def ^:dynamic *cljs-macros-is-classpath* true) @@ -100,6 +100,18 @@ "volatile" "while" "with" "yield" "methods" "null"}) +#?(:cljs + (defn munge-path [ss] + (munge (str ss)))) + +#?(:cljs + (defn ns->relpath + "Given a namespace as a symbol return the relative path. May optionally + provide the file extension, defaults to :cljs." + ([ns] (ns->relpath ns :cljs)) + ([ns ext] + (str (string/replace (munge-path ns) \. \/) "." (name ext))))) + (declare message namespaces) (defn ast? [x] @@ -122,8 +134,8 @@ (defmethod error-message :undeclared-ns [warning-type {:keys [ns-sym js-provide] :as info}] (str "No such namespace: " ns-sym - ", could not locate " (util/ns->relpath ns-sym :cljs) - ", " (util/ns->relpath ns-sym :cljc) + ", could not locate " (ns->relpath ns-sym :cljs) + ", " (ns->relpath ns-sym :cljc) ", or Closure namespace \"" js-provide "\"")) (defmethod error-message :dynamic @@ -257,7 +269,8 @@ (keyword? value) "constant$keyword$" :else (throw - (Exception. (str "constant type " (type value) " not supported")))) + #?(:clj (Exception. (str "constant type " (type value) " not supported")) + :cljs (js/Error. (str "constant type " (type value) " not supported"))))) name (-> value (str) (subs 1) (string/replace "-" "_DASH_") (munge) (string/replace "." "$"))] (symbol (str prefix name)))) @@ -464,7 +477,7 @@ (nil? (get-in @env/*compiler* [::namespaces ns-sym])) ;; macros may refer to namespaces never explicitly required ;; confirm that the library at least exists - (nil? (util/ns->source ns-sym))) + #?(:clj (nil? (util/ns->source ns-sym)))) (warning :undeclared-ns env {:ns-sym ns-sym}))) (declare get-expander) @@ -1286,19 +1299,20 @@ (declare analyze-file) -(defn locate-src - "Given a namespace return the corresponding ClojureScript (.cljs or .cljc) - resource on the classpath or file from the root of the build." - [ns] - (or (util/ns->source ns) - (let [rootp (when-let [root (:root @env/*compiler*)] - (.getPath ^File root)) - cljsf (io/file rootp (util/ns->relpath ns :cljs)) - cljcf (io/file rootp (util/ns->relpath ns :cljc))] - (if (and (.exists cljsf) (.isFile cljsf)) - cljsf - (if (and (.exists cljcf) (.isFile cljcf)) - cljcf))))) +#?(:clj + (defn locate-src + "Given a namespace return the corresponding ClojureScript (.cljs or .cljc) + resource on the classpath or file from the root of the build." + [ns] + (or (util/ns->source ns) + (let [rootp (when-let [root (:root @env/*compiler*)] + (.getPath ^File root)) + cljsf (io/file rootp (ns->relpath ns :cljs)) + cljcf (io/file rootp (ns->relpath ns :cljc))] + (if (and (.exists cljsf) (.isFile cljsf)) + cljsf + (if (and (.exists cljcf) (.isFile cljcf)) + cljcf)))))) (defn foreign-dep? [dep] {:pre [(symbol? dep)]} @@ -1539,9 +1553,15 @@ (warning :munged-namespace env {:name name})) (find-def-clash env name segments) (when (some (complement util/valid-js-id-start?) segments) - (throw (AssertionError. - (str "Namespace " name " has a segment starting with an invaild " - "JavaScript identifier"))))) + (throw + #?(:clj + (AssertionError. + (str "Namespace " name " has a segment starting with an invaild " + "JavaScript identifier")) + :cljs + (js/Error. + (str "Namespace " name " has a segment starting with an invaild " + "JavaScript identifier")))))) (let [docstring (if (string? (first args)) (first args)) mdocstr (-> name meta :doc) args (if docstring (next args) args) @@ -1707,7 +1727,8 @@ ;; (. o -p ) (defmethod build-dot-form [::expr ::property ::list] [[target prop args]] - (throw (Error. (str "Cannot provide arguments " args " on property access " prop)))) + #?(:clj (throw (Error. (str "Cannot provide arguments " args " on property access " prop))) + :cljs (throw (js/Error. (str "Cannot provide arguments " args " on property access " prop))))) (defn- build-method-call "Builds the intermediate method call map used to reason about the parsed form during @@ -1735,7 +1756,16 @@ (defmethod build-dot-form :default [dot-form] - (throw (Error. (str "Unknown dot form of " (list* '. dot-form) " with classification " (classify-dot-form dot-form))))) + #?(:clj (throw + (Error. + (str "Unknown dot form of " + (list* '. dot-form) " with classification " + (classify-dot-form dot-form)))) + :cljs (throw + (js/Error. + (str "Unknown dot form of " + (list* '. dot-form) " with classification " + (classify-dot-form dot-form)))))) (defmethod parse '. [_ env [_ target & [field & member+] :as form] _ _] diff --git a/src/test/cljs/cljs/pprint_test.cljs b/src/test/cljs/cljs/pprint_test.cljs index 13dbbc22a..e0b2ae6fb 100644 --- a/src/test/cljs/cljs/pprint_test.cljs +++ b/src/test/cljs/cljs/pprint_test.cljs @@ -12,7 +12,7 @@ [cljs.pprint-test :refer [simple-tests code-block]]) (:require [cljs.test :as t :refer-macros [deftest is]] - [cljs.pprint :refer [pprint cl-format *out* get-pretty-writer prn print-table + [cljs.pprint :refer [pprint cl-format get-pretty-writer prn print-table *print-pprint-dispatch* simple-dispatch *print-right-margin* *print-miser-width* write code-dispatch] From 63bd1d9e9f5e3e0304bf50b61b9647620daf2033 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Jun 2015 08:21:23 -0400 Subject: [PATCH 1112/4033] cljs.bootstrap analyzer wip, can now compile without warnings --- src/main/clojure/cljs/analyzer.cljc | 112 ++++++++++++++++------------ 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index dad655a7d..44ab463d3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -12,7 +12,7 @@ #?(:cljs (:require-macros [cljs.analyzer.macros :refer [no-warn wrapping-errors disallowing-recur allowing-redef]])) - #?(:clj (:require [cljs.util :as util :refer [ns->relpath]] + #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] @@ -112,6 +112,20 @@ ([ns ext] (str (string/replace (munge-path ns) \. \/) "." (name ext))))) +#?(:cljs + (defn topo-sort + ([x get-deps] + (topo-sort x 0 (atom (sorted-map)) (memoize get-deps))) + ([x depth state memo-get-deps] + (let [deps (memo-get-deps x)] + (swap! state update-in [depth] (fnil into #{}) deps) + (doseq [dep deps] + (topo-sort dep (inc depth) state memo-get-deps)) + (doseq [[> (ns-interns ns) + (->> #?(:clj (ns-interns ns) :cljs (ns-interns* ns)) (filter (fn [[_ ^Var v]] (.isMacro v))) (map (fn [[k v]] [k (as-> (meta v) vm @@ -348,13 +369,14 @@ :name (symbol (str ns) (str k)))))])) (into {})))))) -(defn load-core [] - (when (not @-cljs-macros-loaded) - (reset! -cljs-macros-loaded true) - (if *cljs-macros-is-classpath* - (load *cljs-macros-path*) - (load-file *cljs-macros-path*))) - (intern-macros 'cljs.core)) +#?(:clj + (defn load-core [] + (when (not @-cljs-macros-loaded) + (reset! -cljs-macros-loaded true) + (if *cljs-macros-is-classpath* + (load *cljs-macros-path*) + (load-file *cljs-macros-path*))) + (intern-macros 'cljs.core))) #?(:clj (defmacro with-core-macros @@ -611,7 +633,7 @@ (or (map? requires) (set? requires)) (contains? requires parent) (vector? requires) (some #{(munge (name parent))} requires))))] - (util/topo-sort ns + (topo-sort ns (fn [ns'] (set (map first (filter #(parent? ns' %) ns-map)))))))) @@ -1478,8 +1500,9 @@ (let [ns (if (sequential? form) (first form) form) {:keys [use-macros require-macros]} (or (get-in @env/*compiler* [::namespaces ns]) - (when-let [res (util/ns->source ns)] - (:ast (parse-ns res))))] + #?(:clj + (when-let [res (util/ns->source ns)] + (:ast (parse-ns res)))))] (or (some #{ns} (vals use-macros)) (some #{ns} (vals require-macros)))))) @@ -1552,14 +1575,10 @@ (when (some js-reserved segments) (warning :munged-namespace env {:name name})) (find-def-clash env name segments) - (when (some (complement util/valid-js-id-start?) segments) - (throw - #?(:clj + #?(:clj + (when (some (complement util/valid-js-id-start?) segments) + (throw (AssertionError. - (str "Namespace " name " has a segment starting with an invaild " - "JavaScript identifier")) - :cljs - (js/Error. (str "Namespace " name " has a segment starting with an invaild " "JavaScript identifier")))))) (let [docstring (if (string? (first args)) (first args)) @@ -1609,26 +1628,27 @@ (when (and *analyze-deps* (seq uses)) (check-uses uses env)) (set! *cljs-ns* name) - (when *load-macros* - (load-core) - (doseq [nsym (vals use-macros)] - (let [k (or (:use-macros @reload) - (get-in @reloads [:use-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) - (intern-macros nsym k))) - (doseq [nsym (vals require-macros)] - (let [k (or (:require-macros @reload) - (get-in @reloads [:require-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) - (intern-macros nsym k))) - (when (seq use-macros) - (check-use-macros use-macros env))) + #?(:clj + (when *load-macros* + (load-core) + (doseq [nsym (vals use-macros)] + (let [k (or (:use-macros @reload) + (get-in @reloads [:use-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) + (doseq [nsym (vals require-macros)] + (let [k (or (:require-macros @reload) + (get-in @reloads [:require-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) + (when (seq use-macros) + (check-use-macros use-macros env)))) (let [ns-info {:name name :doc (or docstring mdocstr) @@ -2059,7 +2079,7 @@ (let [form (if (instance? clojure.lang.LazySeq form) (or (seq form) ()) form)] - (load-core) + #?(:clj (load-core)) (cond (symbol? form) (analyze-symbol env form) (and (seq? form) (seq form)) (analyze-seq env form name opts) @@ -2067,7 +2087,7 @@ (vector? form) (analyze-vector env form) (set? form) (analyze-set env form) (keyword? form) (analyze-keyword env form) - (instance? JSValue form) (analyze-js-value env form) + #?@(:clj [(instance? JSValue form) (analyze-js-value env form)]) (= form ()) (analyze-list env form) :else (let [tag (cond From 5e2f98c8bf152d427426ff463780db14ba627a4d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Jun 2015 08:39:21 -0400 Subject: [PATCH 1113/4033] add cljs.env.macros ns --- src/main/clojure/cljs/analyzer.cljc | 20 ++++++------ src/main/clojure/cljs/analyzer/macros.clj | 8 +++++ src/main/clojure/cljs/env.cljc | 2 +- src/main/clojure/cljs/env/macros.clj | 38 +++++++++++++++++++++++ 4 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 src/main/clojure/cljs/env/macros.clj diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 44ab463d3..5e665aa1f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -9,14 +9,16 @@ (ns cljs.analyzer #?(:clj (:refer-clojure :exclude [macroexpand-1]) :cljs (:refer-clojure :exclude [macroexpand-1 ns-interns])) - #?(:cljs (:require-macros [cljs.analyzer.macros - :refer [no-warn wrapping-errors - disallowing-recur allowing-redef]])) + #?(:cljs (:require-macros + [cljs.analyzer.macros + :refer [no-warn wrapping-errors + disallowing-recur allowing-redef]] + [cljs.env.macros :refer [ensure]])) #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] - [cljs.env :as env] + [cljs.env :as env :refer [ensure]] [cljs.js-deps :as deps] [cljs.tagged-literals :as tags] [clojure.tools.reader :as reader] @@ -400,7 +402,7 @@ (defn empty-env "Construct an empty analysis environment. Required to analyze forms." [] - (env/ensure + (ensure {:ns (get-namespace *cljs-ns*) :context :statement :locals {} @@ -1942,7 +1944,7 @@ "Given a env, an analysis environment, and form, a ClojureScript form, macroexpand the form once." [env form] - (env/ensure + (ensure (wrapping-errors env (let [op (first form)] (if (specials op) @@ -2072,7 +2074,7 @@ ([env form name opts] {:pre [(or (nil? name) (symbol? name)) (or (nil? opts) (map? opts))]} - (env/ensure + (ensure (wrapping-errors env (reduce (fn [ast pass] (pass env ast)) (binding [reader/*alias-map* (or reader/*alias-map* {})] @@ -2182,7 +2184,7 @@ ([src] (parse-ns src nil nil)) ([src opts] (parse-ns src nil opts)) ([src dest opts] - (env/ensure + (ensure (let [src (if (symbol? src) (util/ns->source src) src) @@ -2309,7 +2311,7 @@ (re-find #"^file://" f) (URL. f) :else (io/resource f))] (assert res (str "Can't find " f " in classpath")) - (env/ensure + (ensure (let [ns-info (parse-ns res) path (if (instance? File res) (.getPath ^File res) diff --git a/src/main/clojure/cljs/analyzer/macros.clj b/src/main/clojure/cljs/analyzer/macros.clj index fb5e7b729..3bab964ef 100644 --- a/src/main/clojure/cljs/analyzer/macros.clj +++ b/src/main/clojure/cljs/analyzer/macros.clj @@ -1,3 +1,11 @@ +; 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.macros (:refer-clojure :exclude [binding]) (:require [cljs.core :refer [binding]])) diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 5d22e092c..9a8e072f1 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -53,7 +53,7 @@ state that is accessed/maintained by many different components."} env# (cond (map? env#) (atom env#) (and (instance? clojure.lang.Atom env#) - (map? @env#)) env# + (map? @env#)) env# :default (throw (IllegalArgumentException. (str "Compiler environment must be a map or atom containing a map, not " (class env#)))))] diff --git a/src/main/clojure/cljs/env/macros.clj b/src/main/clojure/cljs/env/macros.clj new file mode 100644 index 000000000..1ad947362 --- /dev/null +++ b/src/main/clojure/cljs/env/macros.clj @@ -0,0 +1,38 @@ +; 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.env.macros + (:refer-clojure :exclude [binding]) + (:require [cljs.core :refer [binding]])) + +(defmacro with-compiler-env + "Evaluates [body] with [env] bound as the value of the `*compiler*` var in + this namespace." + [env & body] + `(let [env# ~env + env# (cond + (map? env#) (atom env#) + (and (instance? cljs.core/Atom env#) (map? @env#)) env# + :default + (throw + (js/Error. + (str "Compiler environment must be a map or atom containing a map, not " + (type env#)))))] + (binding [cljs.env/*compiler* env#] + ~@body))) + +(defmacro ensure + [& body] + `(let [val# cljs.env/*compiler*] + (when (nil? val#) + (set! cljs.env/*compiler* (cljs.env/default-compiler-env))) + (try + ~@body + (finally + (when (nil? val#) + (set! cljs.env/*compiler* nil)))))) \ No newline at end of file From 6216cb419ac5f26026882367194e832fae06cd43 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Jun 2015 08:45:33 -0400 Subject: [PATCH 1114/4033] exclude ensure --- src/main/clojure/cljs/analyzer.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5e665aa1f..360d28a38 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -7,8 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.analyzer - #?(:clj (:refer-clojure :exclude [macroexpand-1]) - :cljs (:refer-clojure :exclude [macroexpand-1 ns-interns])) + #?(:clj (:refer-clojure :exclude [macroexpand-1 ensure]) + :cljs (:refer-clojure :exclude [macroexpand-1 ns-interns ensure])) #?(:cljs (:require-macros [cljs.analyzer.macros :refer [no-warn wrapping-errors From 180eed460dcfc0755445ddcdb500610bde9e7704 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Jun 2015 08:52:52 -0400 Subject: [PATCH 1115/4033] exclude ensure --- src/main/clojure/cljs/env/macros.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/env/macros.clj b/src/main/clojure/cljs/env/macros.clj index 1ad947362..d7752fee0 100644 --- a/src/main/clojure/cljs/env/macros.clj +++ b/src/main/clojure/cljs/env/macros.clj @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.env.macros - (:refer-clojure :exclude [binding]) + (:refer-clojure :exclude [binding ensure]) (:require [cljs.core :refer [binding]])) (defmacro with-compiler-env From e80472ff5609970aeb8031c6c48525b18c61a7c2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Jun 2015 09:07:08 -0400 Subject: [PATCH 1116/4033] clojure.lang.LazySeq -> LazySeq --- src/main/clojure/cljs/analyzer.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 360d28a38..201dadf6e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -32,7 +32,7 @@ [cljs.reader :as edn])) #?(:clj (:import [java.io File Reader PushbackReader] [java.net URL] - [clojure.lang Namespace Var] + [clojure.lang Namespace Var LazySeq] [cljs.tagged_literals JSValue]))) #?(:clj (set! *warn-on-reflection* true)) @@ -2078,7 +2078,7 @@ (wrapping-errors env (reduce (fn [ast pass] (pass env ast)) (binding [reader/*alias-map* (or reader/*alias-map* {})] - (let [form (if (instance? clojure.lang.LazySeq form) + (let [form (if (instance? LazySeq form) (or (seq form) ()) form)] #?(:clj (load-core)) From 751a3aeb65cc17819b21212f19b36243899ccd34 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Jun 2015 09:37:26 -0400 Subject: [PATCH 1117/4033] Symbol -invoke should defer to `get` same as Keyword --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6978fa4c5..b66cd41ed 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -898,9 +898,9 @@ IFn (-invoke [sym coll] - (-lookup coll sym nil)) + (get coll sym)) (-invoke [sym coll not-found] - (-lookup coll sym not-found)) + (get coll sym not-found)) IMeta (-meta [_] _meta) From a65f7b75b592691c314f378559cdd2a43936b3cc Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Jun 2015 18:19:19 -0400 Subject: [PATCH 1118/4033] redefine `defmacro` --- src/main/cljs/cljs/core.cljs | 2 + src/main/clojure/cljs/core.clj | 320 +++++++++++++++++++-------------- 2 files changed, 183 insertions(+), 139 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b66cd41ed..fd86a28c0 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -885,6 +885,8 @@ nsc))) :default (garray/defaultCompare (.-name a) (.-name b)))) +(declare get) + (deftype Symbol [ns name str ^:mutable _hash _meta] Object (toString [_] str) diff --git a/src/main/clojure/cljs/core.clj b/src/main/clojure/cljs/core.clj index e4b6d3f42..ae62dfb9b 100644 --- a/src/main/clojure/cljs/core.clj +++ b/src/main/clojure/cljs/core.clj @@ -30,7 +30,7 @@ unsigned-bit-shift-right bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set - bit-test bit-shift-left bit-shift-right bit-xor + bit-test bit-shift-left bit-shift-right bit-xor defmacro cond-> cond->> as-> some-> some->> @@ -45,7 +45,7 @@ (alias 'core 'clojure.core) (alias 'ana 'cljs.analyzer) -(defmacro import-macros [ns [& vars]] +(core/defmacro import-macros [ns [& vars]] (core/let [ns (find-ns ns) vars (map #(ns-resolve ns %) vars) syms (map @@ -132,11 +132,11 @@ (seq ret))) (core/list (asig fdecl)))))) -(defmacro defonce [x init] +(core/defmacro defonce [x init] `(when-not (exists? ~x) (def ~x ~init))) -(defmacro ^{:private true} assert-args [fnname & pairs] +(core/defmacro ^{:private true} assert-args [fnname & pairs] `(do (when-not ~(first pairs) (throw (IllegalArgumentException. ~(core/str fnname " requires " (second pairs))))) @@ -209,7 +209,7 @@ (throw (new Exception (core/str "Unsupported binding key: " (ffirst kwbs)))) (reduce process-entry [] bents))))) -(defmacro let +(core/defmacro let "binding => binding-form init-expr Evaluates the exprs in a lexical context in which the symbols in @@ -221,7 +221,7 @@ (even? (count bindings)) "an even number of forms in binding vector") `(let* ~(destructure bindings) ~@body)) -(defmacro loop +(core/defmacro loop "Evaluates the exprs in a lexical context in which the symbols in the binding-forms are bound to their respective init-exprs or parts therein. Acts as a recur target." @@ -270,7 +270,7 @@ (core/quot c 32) (core/inc (core/quot c 32))))) -(defmacro str [& xs] +(core/defmacro str [& xs] (let [strs (->> (repeat (count xs) "cljs.core.str(~{})") (interpose ",") (apply core/str))] @@ -284,7 +284,7 @@ (#{:var :invoke :constant :dot :js} (:op ast)) ('#{boolean seq} (cljs.analyzer/infer-tag env ast)))) -(defmacro and +(core/defmacro and "Evaluates exprs one at a time, from left to right. If a form returns logical false (nil or false), and returns that value and doesn't evaluate any of the other expressions, otherwise it returns @@ -302,7 +302,7 @@ `(let [and# ~x] (if and# (and ~@next) and#)))))) -(defmacro or +(core/defmacro or "Evaluates exprs one at a time, from left to right. If a form returns a logical true value, or returns that value and doesn't evaluate any of the other expressions, otherwise it returns the @@ -320,57 +320,57 @@ `(let [or# ~x] (if or# or# (or ~@next))))))) -(defmacro nil? [x] +(core/defmacro nil? [x] `(coercive-= ~x nil)) ;; internal - do not use. -(defmacro coercive-not [x] +(core/defmacro coercive-not [x] (bool-expr (core/list 'js* "(!~{})" x))) ;; internal - do not use. -(defmacro coercive-not= [x y] +(core/defmacro coercive-not= [x y] (bool-expr (core/list 'js* "(~{} != ~{})" x y))) ;; internal - do not use. -(defmacro coercive-= [x y] +(core/defmacro coercive-= [x y] (bool-expr (core/list 'js* "(~{} == ~{})" x y))) ;; internal - do not use. -(defmacro coercive-boolean [x] +(core/defmacro coercive-boolean [x] (with-meta (core/list 'js* "~{}" x) {:tag 'boolean})) ;; internal - do not use. -(defmacro truth_ [x] +(core/defmacro truth_ [x] (assert (clojure.core/symbol? x) "x is substituted twice") (core/list 'js* "(~{} != null && ~{} !== false)" x x)) ;; internal - do not use -(defmacro js-arguments [] +(core/defmacro js-arguments [] (core/list 'js* "arguments")) -(defmacro js-delete [obj key] +(core/defmacro js-delete [obj key] (core/list 'js* "delete ~{}[~{}]" obj key)) -(defmacro js-in [key obj] +(core/defmacro js-in [key obj] (core/list 'js* "~{} in ~{}" key obj)) -(defmacro js-debugger +(core/defmacro js-debugger "Emit JavaScript \"debugger;\" statement." [] (core/list 'js* "debugger;")) -(defmacro true? [x] +(core/defmacro true? [x] (bool-expr (core/list 'js* "~{} === true" x))) -(defmacro false? [x] +(core/defmacro false? [x] (bool-expr (core/list 'js* "~{} === false" x))) -(defmacro string? [x] +(core/defmacro string? [x] (bool-expr (core/list 'js* "typeof ~{} === 'string'" x))) ;; TODO: x must be a symbol, not an arbitrary expression -(defmacro exists? +(core/defmacro exists? "Return true if argument exists, analogous to usage of typeof operator in JavaScript." [x] @@ -378,15 +378,15 @@ (core/list 'js* "typeof ~{} !== 'undefined'" (vary-meta x assoc :cljs.analyzer/no-resolve true)))) -(defmacro undefined? +(core/defmacro undefined? "Return true if argument is identical to the JavaScript undefined value." [x] (bool-expr (core/list 'js* "(void 0 === ~{})" x))) -(defmacro identical? [a b] +(core/defmacro identical? [a b] (bool-expr (core/list 'js* "(~{} === ~{})" a b))) -(defmacro instance? [t o] +(core/defmacro instance? [t o] ;; Google Closure warns about some references to RegExp, so ;; (instance? RegExp ...) needs to be inlined, but the expansion ;; should preserve the order of argument evaluation. @@ -395,23 +395,23 @@ `(let [t# ~t o# ~o] (~'js* "(~{} instanceof ~{})" o# t#))))) -(defmacro number? [x] +(core/defmacro number? [x] (bool-expr (core/list 'js* "typeof ~{} === 'number'" x))) -(defmacro symbol? [x] +(core/defmacro symbol? [x] (bool-expr `(instance? Symbol ~x))) -(defmacro keyword? [x] +(core/defmacro keyword? [x] (bool-expr `(instance? Keyword ~x))) -(defmacro aget +(core/defmacro aget ([a i] (core/list 'js* "(~{}[~{}])" a i)) ([a i & idxs] (let [astr (apply core/str (repeat (count idxs) "[~{}]"))] `(~'js* ~(core/str "(~{}[~{}]" astr ")") ~a ~i ~@idxs)))) -(defmacro aset +(core/defmacro aset ([a i v] (core/list 'js* "(~{}[~{}] = ~{})" a i v)) ([a idx idx2 & idxv] @@ -419,202 +419,202 @@ astr (apply core/str (repeat n "[~{}]"))] `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~a ~idx ~idx2 ~@idxv)))) -(defmacro ^::ana/numeric + +(core/defmacro ^::ana/numeric + ([] 0) ([x] x) ([x y] (core/list 'js* "(~{} + ~{})" x y)) ([x y & more] `(+ (+ ~x ~y) ~@more))) -(defmacro byte [x] x) -(defmacro short [x] x) -(defmacro float [x] x) -(defmacro double [x] x) +(core/defmacro byte [x] x) +(core/defmacro short [x] x) +(core/defmacro float [x] x) +(core/defmacro double [x] x) -(defmacro unchecked-byte [x] x) -(defmacro unchecked-char [x] x) -(defmacro unchecked-short [x] x) -(defmacro unchecked-float [x] x) -(defmacro unchecked-double [x] x) +(core/defmacro unchecked-byte [x] x) +(core/defmacro unchecked-char [x] x) +(core/defmacro unchecked-short [x] x) +(core/defmacro unchecked-float [x] x) +(core/defmacro unchecked-double [x] x) -(defmacro ^::ana/numeric unchecked-add +(core/defmacro ^::ana/numeric unchecked-add ([& xs] `(+ ~@xs))) -(defmacro ^::ana/numeric unchecked-add-int +(core/defmacro ^::ana/numeric unchecked-add-int ([& xs] `(+ ~@xs))) -(defmacro ^::ana/numeric unchecked-dec +(core/defmacro ^::ana/numeric unchecked-dec ([x] `(dec ~x))) -(defmacro ^::ana/numeric unchecked-dec-int +(core/defmacro ^::ana/numeric unchecked-dec-int ([x] `(dec ~x))) -(defmacro ^::ana/numeric unchecked-divide-int +(core/defmacro ^::ana/numeric unchecked-divide-int ([& xs] `(/ ~@xs))) -(defmacro ^::ana/numeric unchecked-inc +(core/defmacro ^::ana/numeric unchecked-inc ([x] `(inc ~x))) -(defmacro ^::ana/numeric unchecked-inc-int +(core/defmacro ^::ana/numeric unchecked-inc-int ([x] `(inc ~x))) -(defmacro ^::ana/numeric unchecked-multiply +(core/defmacro ^::ana/numeric unchecked-multiply ([& xs] `(* ~@xs))) -(defmacro ^::ana/numeric unchecked-multiply-int +(core/defmacro ^::ana/numeric unchecked-multiply-int ([& xs] `(* ~@xs))) -(defmacro ^::ana/numeric unchecked-negate +(core/defmacro ^::ana/numeric unchecked-negate ([x] `(- ~x))) -(defmacro ^::ana/numeric unchecked-negate-int +(core/defmacro ^::ana/numeric unchecked-negate-int ([x] `(- ~x))) -(defmacro ^::ana/numeric unchecked-remainder-int +(core/defmacro ^::ana/numeric unchecked-remainder-int ([x n] `(mod ~x ~n))) -(defmacro ^::ana/numeric unchecked-subtract +(core/defmacro ^::ana/numeric unchecked-subtract ([& xs] `(- ~@xs))) -(defmacro ^::ana/numeric unchecked-subtract-int +(core/defmacro ^::ana/numeric unchecked-subtract-int ([& xs] `(- ~@xs))) -(defmacro ^::ana/numeric - +(core/defmacro ^::ana/numeric - ([x] (core/list 'js* "(- ~{})" x)) ([x y] (core/list 'js* "(~{} - ~{})" x y)) ([x y & more] `(- (- ~x ~y) ~@more))) -(defmacro ^::ana/numeric * +(core/defmacro ^::ana/numeric * ([] 1) ([x] x) ([x y] (core/list 'js* "(~{} * ~{})" x y)) ([x y & more] `(* (* ~x ~y) ~@more))) -(defmacro ^::ana/numeric / +(core/defmacro ^::ana/numeric / ([x] `(/ 1 ~x)) ([x y] (core/list 'js* "(~{} / ~{})" x y)) ([x y & more] `(/ (/ ~x ~y) ~@more))) -(defmacro ^::ana/numeric divide +(core/defmacro ^::ana/numeric divide ([x] `(/ 1 ~x)) ([x y] (core/list 'js* "(~{} / ~{})" x y)) ([x y & more] `(/ (/ ~x ~y) ~@more))) -(defmacro ^::ana/numeric < +(core/defmacro ^::ana/numeric < ([x] true) ([x y] (bool-expr (core/list 'js* "(~{} < ~{})" x y))) ([x y & more] `(and (< ~x ~y) (< ~y ~@more)))) -(defmacro ^::ana/numeric <= +(core/defmacro ^::ana/numeric <= ([x] true) ([x y] (bool-expr (core/list 'js* "(~{} <= ~{})" x y))) ([x y & more] `(and (<= ~x ~y) (<= ~y ~@more)))) -(defmacro ^::ana/numeric > +(core/defmacro ^::ana/numeric > ([x] true) ([x y] (bool-expr (core/list 'js* "(~{} > ~{})" x y))) ([x y & more] `(and (> ~x ~y) (> ~y ~@more)))) -(defmacro ^::ana/numeric >= +(core/defmacro ^::ana/numeric >= ([x] true) ([x y] (bool-expr (core/list 'js* "(~{} >= ~{})" x y))) ([x y & more] `(and (>= ~x ~y) (>= ~y ~@more)))) -(defmacro ^::ana/numeric == +(core/defmacro ^::ana/numeric == ([x] true) ([x y] (bool-expr (core/list 'js* "(~{} === ~{})" x y))) ([x y & more] `(and (== ~x ~y) (== ~y ~@more)))) -(defmacro ^::ana/numeric dec [x] +(core/defmacro ^::ana/numeric dec [x] `(- ~x 1)) -(defmacro ^::ana/numeric inc [x] +(core/defmacro ^::ana/numeric inc [x] `(+ ~x 1)) -(defmacro ^::ana/numeric zero? [x] +(core/defmacro ^::ana/numeric zero? [x] `(== ~x 0)) -(defmacro ^::ana/numeric pos? [x] +(core/defmacro ^::ana/numeric pos? [x] `(> ~x 0)) -(defmacro ^::ana/numeric neg? [x] +(core/defmacro ^::ana/numeric neg? [x] `(< ~x 0)) -(defmacro ^::ana/numeric max +(core/defmacro ^::ana/numeric max ([x] x) ([x y] `(let [x# ~x, y# ~y] (~'js* "((~{} > ~{}) ? ~{} : ~{})" x# y# x# y#))) ([x y & more] `(max (max ~x ~y) ~@more))) -(defmacro ^::ana/numeric min +(core/defmacro ^::ana/numeric min ([x] x) ([x y] `(let [x# ~x, y# ~y] (~'js* "((~{} < ~{}) ? ~{} : ~{})" x# y# x# y#))) ([x y & more] `(min (min ~x ~y) ~@more))) -(defmacro ^::ana/numeric js-mod [num div] +(core/defmacro ^::ana/numeric js-mod [num div] (core/list 'js* "(~{} % ~{})" num div)) -(defmacro ^::ana/numeric bit-not [x] +(core/defmacro ^::ana/numeric bit-not [x] (core/list 'js* "(~ ~{})" x)) -(defmacro ^::ana/numeric bit-and +(core/defmacro ^::ana/numeric bit-and ([x y] (core/list 'js* "(~{} & ~{})" x y)) ([x y & more] `(bit-and (bit-and ~x ~y) ~@more))) ;; internal do not use -(defmacro ^::ana/numeric unsafe-bit-and +(core/defmacro ^::ana/numeric unsafe-bit-and ([x y] (bool-expr (core/list 'js* "(~{} & ~{})" x y))) ([x y & more] `(unsafe-bit-and (unsafe-bit-and ~x ~y) ~@more))) -(defmacro ^::ana/numeric bit-or +(core/defmacro ^::ana/numeric bit-or ([x y] (core/list 'js* "(~{} | ~{})" x y)) ([x y & more] `(bit-or (bit-or ~x ~y) ~@more))) -(defmacro ^::ana/numeric int [x] +(core/defmacro ^::ana/numeric int [x] `(bit-or ~x 0)) -(defmacro ^::ana/numeric bit-xor +(core/defmacro ^::ana/numeric bit-xor ([x y] (core/list 'js* "(~{} ^ ~{})" x y)) ([x y & more] `(bit-xor (bit-xor ~x ~y) ~@more))) -(defmacro ^::ana/numeric bit-and-not +(core/defmacro ^::ana/numeric bit-and-not ([x y] (core/list 'js* "(~{} & ~~{})" x y)) ([x y & more] `(bit-and-not (bit-and-not ~x ~y) ~@more))) -(defmacro ^::ana/numeric bit-clear [x n] +(core/defmacro ^::ana/numeric bit-clear [x n] (core/list 'js* "(~{} & ~(1 << ~{}))" x n)) -(defmacro ^::ana/numeric bit-flip [x n] +(core/defmacro ^::ana/numeric bit-flip [x n] (core/list 'js* "(~{} ^ (1 << ~{}))" x n)) -(defmacro bit-test [x n] +(core/defmacro bit-test [x n] (bool-expr (core/list 'js* "((~{} & (1 << ~{})) != 0)" x n))) -(defmacro ^::ana/numeric bit-shift-left [x n] +(core/defmacro ^::ana/numeric bit-shift-left [x n] (core/list 'js* "(~{} << ~{})" x n)) -(defmacro ^::ana/numeric bit-shift-right [x n] +(core/defmacro ^::ana/numeric bit-shift-right [x n] (core/list 'js* "(~{} >> ~{})" x n)) -(defmacro ^::ana/numeric bit-shift-right-zero-fill [x n] +(core/defmacro ^::ana/numeric bit-shift-right-zero-fill [x n] (core/list 'js* "(~{} >>> ~{})" x n)) -(defmacro ^::ana/numeric unsigned-bit-shift-right [x n] +(core/defmacro ^::ana/numeric unsigned-bit-shift-right [x n] (core/list 'js* "(~{} >>> ~{})" x n)) -(defmacro ^::ana/numeric bit-set [x n] +(core/defmacro ^::ana/numeric bit-set [x n] (core/list 'js* "(~{} | (1 << ~{}))" x n)) ;; internal -(defmacro mask [hash shift] +(core/defmacro mask [hash shift] (core/list 'js* "((~{} >>> ~{}) & 0x01f)" hash shift)) ;; internal -(defmacro bitpos [hash shift] +(core/defmacro bitpos [hash shift] (core/list 'js* "(1 << ~{})" `(mask ~hash ~shift))) ;; internal -(defmacro caching-hash [coll hash-fn hash-key] +(core/defmacro caching-hash [coll hash-fn hash-key] (assert (clojure.core/symbol? hash-key) "hash-key is substituted twice") `(let [h# ~hash-key] (if-not (nil? h#) @@ -632,7 +632,7 @@ (~cargs (fn [x#] (~name ~@cargs x#))) (~args ~@body)))) -(defmacro ^:private defcurried +(core/defmacro ^:private defcurried "Builds another arity of the fn that returns a fn awaiting the last param" [name doc meta args & body] @@ -649,7 +649,7 @@ fkv) ~fkv)) -(defmacro ^:private rfn +(core/defmacro ^:private rfn "Builds 3-arity reducing fn given names of wrapped fn and key, and k/v impl." [[f1 k] fkv] (do-rfn f1 k fkv)) @@ -677,7 +677,7 @@ 'js/Number "number" 'js/Function "function"}) -(defmacro reify +(core/defmacro reify "reify is a macro with the following structure: (reify options* specs*) @@ -733,7 +733,7 @@ ~@impls)) (new ~t ~@locals ~(ana/elide-reader-meta (meta &form)))))) -(defmacro specify! +(core/defmacro specify! "Identical to reify but mutates its first argument." [expr & impls] (let [x (with-meta (gensym "x") {:extend :instance})] @@ -741,17 +741,17 @@ (extend-type ~x ~@impls) ~x))) -(defmacro specify +(core/defmacro specify "Identical to specify but does not mutate its first argument. The first argument must be an ICloneable instance." [expr & impls] `(cljs.core/specify! (cljs.core/clone ~expr) ~@impls)) -(defmacro ^:private js-this [] +(core/defmacro ^:private js-this [] (core/list 'js* "this")) -(defmacro this-as +(core/defmacro this-as "Defines a scope where JavaScript's implicit \"this\" is bound to the name provided." [name & body] `(let [~name (js-this)] @@ -932,7 +932,7 @@ (recur (conj seen fname) (next methods))))) (recur (conj protos proto) impls))))) -(defmacro extend-type +(core/defmacro extend-type "Extend a type to a series of protocols. Useful when you are supplying the definitions explicitly inline. Propagates the type as a type hint on the first argument of all fns. @@ -1021,7 +1021,7 @@ (when-not (vector? fields) (throw (AssertionError. (core/str case " " name ", no fields vector given."))))) -(defmacro deftype +(core/defmacro deftype "(deftype name [fields*] options* specs*) Currently there are no options. @@ -1178,7 +1178,7 @@ `(defn ~fn-name [~ms] (new ~rname ~@getters nil (dissoc ~ms ~@ks) nil)))) -(defmacro defrecord +(core/defmacro defrecord "(defrecord name [fields*] options* specs*) Currently there are no options. @@ -1247,7 +1247,7 @@ ~(build-map-factory rsym r fields) ~r))) -(defmacro defprotocol +(core/defmacro defprotocol "A protocol is a named set of named methods and their signatures: (defprotocol AProtocolName @@ -1344,7 +1344,7 @@ ~@(map method methods) (set! ~'*unchecked-if* false)))) -(defmacro implements? +(core/defmacro implements? "EXPERIMENTAL" [psym x] (let [p (:name @@ -1364,7 +1364,7 @@ false)) false)))) -(defmacro satisfies? +(core/defmacro satisfies? "Returns true if x satisfies the protocol" [psym x] (let [p (:name @@ -1386,7 +1386,7 @@ false))) (cljs.core/native-satisfies? ~psym ~xsym))))) -(defmacro lazy-seq +(core/defmacro lazy-seq "Takes a body of expressions that returns an ISeq or nil, and yields a ISeqable object that will invoke the body only the first time seq is called, and will cache the result and return it on all subsequent @@ -1394,7 +1394,7 @@ [& body] `(new cljs.core/LazySeq nil (fn [] ~@body) nil nil)) -(defmacro delay +(core/defmacro delay "Takes a body of expressions and yields a Delay object that will invoke the body only the first time it is forced (with force or deref/@), and will cache the result and return it on all subsequent force @@ -1402,7 +1402,7 @@ [& body] `(new cljs.core/Delay (fn [] ~@body) nil)) -(defmacro with-redefs +(core/defmacro with-redefs "binding => var-symbol temp-value-expr Temporarily redefines vars while executing the body. The @@ -1424,7 +1424,7 @@ (finally ~@(map bind-value resets)))))) -(defmacro binding +(core/defmacro binding "binding => var-symbol init-expr Creates new bindings for the (already-existing) vars, with the @@ -1437,7 +1437,7 @@ (cljs.analyzer/confirm-bindings &env names) `(with-redefs ~bindings ~@body))) -(defmacro condp +(core/defmacro condp "Takes a binary predicate, an expression, and a set of clauses. Each clause can take the form of either: @@ -1495,7 +1495,7 @@ (ana/resolve-var env (last x)))] (core/when m (core/get m :const)))) -(defmacro case +(core/defmacro case "Takes an expression, and a set of clauses. Each clause can take the form of either: @@ -1566,7 +1566,7 @@ ~@(mapcat (fn [[m c]] `((cljs.core/= ~m ~esym) ~c)) pairs) :else ~default))))) -(defmacro assert +(core/defmacro assert "Evaluates expr and throws an exception if it does not evaluate to logical true." ([x] @@ -1580,7 +1580,7 @@ (throw (js/Error. (cljs.core/str "Assert failed: " ~message "\n" (cljs.core/pr-str '~x)))))))) -(defmacro for +(core/defmacro for "List comprehension. Takes a vector of one or more binding-form/collection-expr pairs, each followed by zero or more modifiers, and yields a lazy sequence of evaluations of expr. @@ -1667,7 +1667,7 @@ `(let [iter# ~(emit-bind (to-groups seq-exprs))] (iter# ~(second seq-exprs))))) -(defmacro doseq +(core/defmacro doseq "Repeatedly executes body (presumably for side-effects) with bindings and filtering as provided by \"for\". Does not retain the head of the sequence. Returns nil." @@ -1723,7 +1723,7 @@ ~@(when needrec [recform]))))))])))))] (nth (step nil (seq seq-exprs)) 1))) -(defmacro array [& rest] +(core/defmacro array [& rest] (let [xs-str (->> (repeat "~{}") (take (count rest)) (interpose ",") @@ -1732,7 +1732,7 @@ (list* 'js* (core/str "[" xs-str "]") rest) assoc :tag 'array))) -(defmacro make-array +(core/defmacro make-array [size] (vary-meta (if (core/number? size) @@ -1740,12 +1740,12 @@ `(js/Array. ~size)) assoc :tag 'array)) -(defmacro list +(core/defmacro list ([] '(.-EMPTY cljs.core/List)) ([x & xs] `(-conj (list ~@xs) ~x))) -(defmacro vector +(core/defmacro vector ([] '(.-EMPTY cljs.core/PersistentVector)) ([& xs] (let [cnt (count xs)] @@ -1756,7 +1756,7 @@ `(.fromArray cljs.core/PersistentVector (array ~@xs) true) assoc :tag 'cljs.core/PersistentVector))))) -(defmacro array-map +(core/defmacro array-map ([] '(.-EMPTY cljs.core/PersistentArrayMap)) ([& kvs] (let [keys (map first (partition 2 kvs))] @@ -1766,7 +1766,7 @@ `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) `(.fromArray cljs.core/PersistentArrayMap (array ~@kvs) true false))))) -(defmacro hash-map +(core/defmacro hash-map ([] `(.-EMPTY cljs.core/PersistentHashMap)) ([& kvs] (let [pairs (partition 2 kvs) @@ -1776,7 +1776,7 @@ `(.fromArrays cljs.core/PersistentHashMap (array ~@ks) (array ~@vs)) assoc :tag 'cljs.core/PersistentHashMap)))) -(defmacro hash-set +(core/defmacro hash-set ([] `(.-EMPTY cljs.core/PersistentHashSet)) ([& xs] (if (core/and (core/<= (count xs) 8) @@ -1799,7 +1799,7 @@ (list* 'js* (core/str "{" kvs-str "}") (apply concat kvs)) assoc :tag 'object))) -(defmacro js-obj [& rest] +(core/defmacro js-obj [& rest] (let [sym-or-str? (fn [x] (core/or (core/symbol? x) (core/string? x))) filter-on-keys (fn [f coll] (->> coll @@ -1817,12 +1817,12 @@ ~@(map (fn [[k v]] `(aset ~obj ~v ~(core/get kvs k))) expr->local) ~obj))) -(defmacro alength [a] +(core/defmacro alength [a] (vary-meta (core/list 'js* "~{}.length" a) assoc :tag 'number)) -(defmacro amap +(core/defmacro amap "Maps an expression across an array a, using an index named idx, and return value named ret, initialized to a clone of a, then setting each element of ret to the evaluation of expr, returning the new @@ -1837,7 +1837,7 @@ (recur (inc ~idx))) ~ret)))) -(defmacro areduce +(core/defmacro areduce "Reduces an expression across an array a, using an index named idx, and return value named ret, initialized to init, setting ret to the evaluation of expr at each step, returning ret." @@ -1848,7 +1848,7 @@ (recur (inc ~idx) ~expr) ~ret)))) -(defmacro dotimes +(core/defmacro dotimes "bindings => name n Repeatedly executes body (presumably for side-effects) with name @@ -1872,7 +1872,7 @@ (first valid-keys) (map #(core/str ", " %) (rest valid-keys)))))) -(defmacro defmulti +(core/defmacro defmulti "Creates a new multimethod with the associated dispatch function. The docstring and attribute-map are optional. @@ -1916,12 +1916,12 @@ (cljs.core/MultiFn. (cljs.core/symbol ~mm-ns ~(name mm-name)) ~dispatch-fn ~default hierarchy# method-table# prefer-table# method-cache# cached-hierarchy#)))))) -(defmacro defmethod +(core/defmacro defmethod "Creates and installs a new method of multimethod associated with dispatch-value. " [multifn dispatch-val & fn-tail] `(-add-method ~(with-meta multifn {:tag 'cljs.core/MultiFn}) ~dispatch-val (fn ~@fn-tail))) -(defmacro time +(core/defmacro time "Evaluates expr and prints the time it took. Returns the value of expr." [expr] `(let [start# (.getTime (js/Date.)) @@ -1929,7 +1929,7 @@ (prn (core/str "Elapsed time: " (- (.getTime (js/Date.)) start#) " msecs")) ret#)) -(defmacro simple-benchmark +(core/defmacro simple-benchmark "Runs expr iterations times in the context of a let expression with the given bindings, then prints out the bindings and the expr followed by number of iterations and total time. The optional @@ -1964,7 +1964,7 @@ ~(gen-apply-to-helper (core/inc n)))) `(throw (js/Error. "Only up to 20 arguments supported on functions")))))) -(defmacro gen-apply-to [] +(core/defmacro gen-apply-to [] `(do (set! ~'*unchecked-if* true) (defn ~'apply-to [~'f ~'argc ~'args] @@ -1974,7 +1974,7 @@ ~(gen-apply-to-helper)))) (set! ~'*unchecked-if* false))) -(defmacro with-out-str +(core/defmacro with-out-str "Evaluates exprs in a context in which *print-fn* is bound to .append on a fresh StringBuffer. Returns the string created by any nested printing calls." @@ -1985,7 +1985,7 @@ ~@body) (cljs.core/str sb#))) -(defmacro lazy-cat +(core/defmacro lazy-cat "Expands to code which yields a lazy sequence of the concatenation of the supplied colls. Each coll expr is not evaluated until it is needed. @@ -1994,16 +1994,16 @@ [& colls] `(concat ~@(map #(core/list `lazy-seq %) colls))) -(defmacro js-str [s] +(core/defmacro js-str [s] (core/list 'js* "''+~{}" s)) -(defmacro es6-iterable [ty] +(core/defmacro es6-iterable [ty] `(aset (.-prototype ~ty) cljs.core/ITER_SYMBOL (fn [] (this-as this# (cljs.core/es6-iterator this#))))) -(defmacro ns-interns +(core/defmacro ns-interns "Returns a map of the intern mappings for the namespace." [[quote ns]] (core/assert (core/and (= quote 'quote) (core/symbol? ns)) @@ -2014,7 +2014,7 @@ `[(symbol ~(name sym)) (var ~(symbol (name ns) (name sym)))]) (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))])) -(defmacro ns-unmap +(core/defmacro ns-unmap "Removes the mappings for the symbol from the namespace." [[quote0 ns] [quote1 sym]] (core/assert (core/and (= quote0 'quote) (core/symbol? ns) @@ -2023,7 +2023,7 @@ (swap! env/*compiler* update-in [::ana/namespaces ns :defs] dissoc sym) `(js-delete ~(cljs.compiler/munge ns) ~(cljs.compiler/munge (core/str sym)))) -(defmacro vswap! +(core/defmacro vswap! "Non-atomically swaps the value of the volatile as if: (apply f current-value-of-vol args). Returns the value that was swapped in." @@ -2031,10 +2031,10 @@ `(-vreset! ~vol (~f (-deref ~vol) ~@args))) ;; INTERNAL - do not use, only for Node.js -(defmacro load-file* [f] +(core/defmacro load-file* [f] `(. js/goog (~'nodeGlobalRequire ~f))) -(defmacro macroexpand-1 +(core/defmacro macroexpand-1 "If form represents a macro form, returns its expansion, else returns form." [quoted] @@ -2043,7 +2043,7 @@ (core/let [form (second quoted)] `(quote ~(ana/macroexpand-1 &env form)))) -(defmacro macroexpand +(core/defmacro macroexpand "Repeatedly calls macroexpand-1 on form until it no longer represents a macro form, then returns it. Note neither macroexpand-1 nor macroexpand expand macros in subforms." @@ -2251,3 +2251,45 @@ (cons `fn fdecl)))))) (. (var defn) (setMacro)) + +(def + ^{:doc "Like defn, but the resulting function name is declared as a + macro and will be used as a macro by the compiler when it is + called." + :arglists '([name doc-string? attr-map? [params*] body] + [name doc-string? attr-map? ([params*] body)+ attr-map?]) + :macro true} + defmacro (fn [&form &env + name & args] + (core/let [prefix (core/loop [p (core/list (vary-meta name assoc :macro true)) args args] + (core/let [f (first args)] + (if (core/string? f) + (recur (cons f p) (next args)) + (if (map? f) + (recur (cons f p) (next args)) + p)))) + fdecl (core/loop [fd args] + (if (core/string? (first fd)) + (recur (next fd)) + (if (map? (first fd)) + (recur (next fd)) + fd))) + fdecl (if (vector? (first fdecl)) + (core/list fdecl) + fdecl) + add-implicit-args (fn [fd] + (core/let [args (first fd)] + (cons (vec (cons '&form (cons '&env args))) (next fd)))) + add-args (fn [acc ds] + (if (core/nil? ds) + acc + (core/let [d (first ds)] + (if (map? d) + (conj acc d) + (recur (conj acc (add-implicit-args d)) (next ds)))))) + fdecl (seq (add-args [] fdecl)) + decl (core/loop [p prefix d fdecl] + (if p + (recur (next p) (cons (first p) d)) + d))] + (cons `defn decl)))) From 7029f68541dc01ec08e6f264df45abf1f71df044 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Jun 2015 18:42:44 -0400 Subject: [PATCH 1119/4033] simple macro tracking --- src/main/cljs/cljs/core.cljs | 8 +++++++- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/main/clojure/cljs/core.clj | 4 +++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fd86a28c0..57d60baf2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -933,6 +933,9 @@ (Symbol. ns name sym-str nil nil)))) (deftype Var [val sym _meta] + Object + (isMacro [_] + (. (val) -cljs$lang$macro)) IDeref (-deref [_] (val)) IMeta @@ -9832,10 +9835,13 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype Namespace [obj name mappings] Object (find [_ sym] - (Var. (goog.object/get obj (str sym)) + (Var. (get @mappings sym) (symbol (str name) (str sym)) nil)) (getMappings [_] @mappings) + (findInternedVar [_ sym] + (Var. (goog.object/get obj (str sym)) + (symbol (str name) (str sym)) nil)) (toString [_] (str name)) IEquiv diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 201dadf6e..93cbba4d2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1930,7 +1930,8 @@ (when-let [ns (cond (= "clojure.core" nstr) (find-ns 'cljs.core) (= "clojure.repl" nstr) (find-ns 'cljs.repl) - (.contains nstr ".") (find-ns (symbol nstr)) + #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] + :cljs [(goog.string/contains nstr ".") (find-ns (symbol nstr))]) :else (some-> env :ns :require-macros (get (symbol nstr)) find-ns))] (.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym)))) diff --git a/src/main/clojure/cljs/core.clj b/src/main/clojure/cljs/core.clj index ae62dfb9b..67db76fff 100644 --- a/src/main/clojure/cljs/core.clj +++ b/src/main/clojure/cljs/core.clj @@ -2292,4 +2292,6 @@ (if p (recur (next p) (cons (first p) d)) d))] - (cons `defn decl)))) + (core/list 'do + (cons `defn decl) + (core/list 'set! `(. ~name ~'-cljs$lang$macro) true))))) From 2b2178694ea03fbae4fb2d1703013784308a86fd Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Jun 2015 18:51:34 -0400 Subject: [PATCH 1120/4033] add Namespace.getName, goog.string/contains for cljs instead of Java String.contains --- src/main/cljs/cljs/core.cljs | 5 +++-- src/main/clojure/cljs/analyzer.cljc | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 57d60baf2..9143076e8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9837,11 +9837,12 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (find [_ sym] (Var. (get @mappings sym) (symbol (str name) (str sym)) nil)) - (getMappings [_] - @mappings) (findInternedVar [_ sym] (Var. (goog.object/get obj (str sym)) (symbol (str name) (str sym)) nil)) + (getMappings [_] + @mappings) + (getName [_] name) (toString [_] (str name)) IEquiv diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 93cbba4d2..2e821d7f3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -539,7 +539,8 @@ {:name (symbol (str full-ns) (str (name sym))) :ns full-ns})) - (.contains s ".") + #?(:clj (.contains s ".") + :cljs (goog.string/contains s ".")) (let [idx (.indexOf s ".") prefix (symbol (subs s 0 idx)) suffix (subs s (inc idx)) @@ -1181,7 +1182,9 @@ bindings (seq (partition 2 bindings))] (if-let [[name init] (first bindings)] (do - (when (or (namespace name) (.contains (str name) ".")) + (when (or (namespace name) + #?(:clj (.contains (str name) ".") + :cljs (goog.string/contains (str name) "."))) (throw (error encl-env (str "Invalid local name: " name)))) (let [init-expr (binding [*loop-lets* (cons {:params bes} *loop-lets*)] (analyze env init)) From f7b5caaf83328baf79d2b4ccfce8b15cd1e898fb Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Jun 2015 19:08:09 -0400 Subject: [PATCH 1121/4033] cljs/core.clj macros file -> cljs/core.cljc --- src/main/clojure/cljs/{core.clj => core.cljc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/clojure/cljs/{core.clj => core.cljc} (100%) diff --git a/src/main/clojure/cljs/core.clj b/src/main/clojure/cljs/core.cljc similarity index 100% rename from src/main/clojure/cljs/core.clj rename to src/main/clojure/cljs/core.cljc From 33016ed2e67dfd08078f237167dbb83c3cefff86 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Jun 2015 19:56:08 -0400 Subject: [PATCH 1122/4033] conditionalize portions of the macros file --- src/main/clojure/cljs/core.cljc | 128 ++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 40 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 67db76fff..6629e859a 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -87,25 +87,41 @@ (defn- ^{:dynamic true} assert-valid-fdecl "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn." [fdecl] - (when (empty? fdecl) (throw (IllegalArgumentException. - "Parameter declaration missing"))) - (core/let [argdecls (map - #(if (seq? %) - (first %) - (throw (IllegalArgumentException. + (when (empty? fdecl) + (throw + #?(:clj (IllegalArgumentException. "Parameter declaration missing") + :cljs (js/Error. "Parameter declaration missing")))) + (core/let [argdecls + (map + #(if (seq? %) + (first %) + (throw + #?(:clj (IllegalArgumentException. + (if (seq? (first fdecl)) + (core/str "Invalid signature \"" + % + "\" should be a list") + (core/str "Parameter declaration \"" + % + "\" should be a vector"))) + :cljs (js/Error. (if (seq? (first fdecl)) (core/str "Invalid signature \"" % "\" should be a list") (core/str "Parameter declaration \"" % - "\" should be a vector"))))) - fdecl) - bad-args (seq (remove #(vector? %) argdecls))] + "\" should be a vector")))))) + fdecl) + bad-args (seq (remove #(vector? %) argdecls))] (when bad-args - (throw (IllegalArgumentException. - (core/str "Parameter declaration \"" (first bad-args) - "\" should be a vector")))))) + (throw + #?(:clj (IllegalArgumentException. + (core/str "Parameter declaration \"" (first bad-args) + "\" should be a vector")) + :cljs (js/Error. + (core/str "Parameter declaration \"" (first bad-args) + "\" should be a vector"))))))) (def ^{:private true} @@ -116,8 +132,10 @@ (fn [fdecl] (core/let [arglist (first fdecl) ;elide implicit macro args - arglist (if (clojure.lang.Util/equals '&form (first arglist)) - (clojure.lang.RT/subvec arglist 2 (clojure.lang.RT/count arglist)) + arglist (if #?(:clj (clojure.lang.Util/equals '&form (first arglist)) + :cljs (= '&form (first arglist))) + #?(:clj (clojure.lang.RT/subvec arglist 2 (clojure.lang.RT/count arglist)) + :cljs (subvec arglist 2 (count arglist))) arglist) body (next fdecl)] (if (map? (first body)) @@ -137,12 +155,18 @@ (def ~x ~init))) (core/defmacro ^{:private true} assert-args [fnname & pairs] - `(do (when-not ~(first pairs) - (throw (IllegalArgumentException. - ~(core/str fnname " requires " (second pairs))))) - ~(core/let [more (nnext pairs)] - (when more - (list* `assert-args fnname more))))) + #?(:clj `(do (when-not ~(first pairs) + (throw (IllegalArgumentException. + ~(core/str fnname " requires " (second pairs))))) + ~(core/let [more (nnext pairs)] + (when more + (list* `assert-args fnname more)))) + :cljs `(do (when-not ~(first pairs) + (throw (js/Error. + ~(core/str fnname " requires " (second pairs))))) + ~(core/let [more (nnext pairs)] + (when more + (list* `assert-args fnname more)))))) (core/defn destructure [bindings] (core/let [bents (partition 2 bindings) @@ -163,7 +187,9 @@ true) (= firstb :as) (pb ret (second bs) gvec) :else (if seen-rest? - (throw (new Exception "Unsupported binding form, only :as can follow & parameter")) + (throw + #?(:clj (new Exception "Unsupported binding form, only :as can follow & parameter") + :cljs (new js/Error "Unsupported binding form, only :as can follow & parameter"))) (recur (pb ret firstb (core/list `nth gvec n nil)) (core/inc n) (next bs) @@ -201,12 +227,16 @@ (core/keyword? b) (-> bvec (conj (symbol (name b))) (conj v)) (vector? b) (pvec bvec b v) (map? b) (pmap bvec b v) - :else (throw (new Exception (core/str "Unsupported binding form: " b)))))) + :else (throw + #?(:clj (new Exception (core/str "Unsupported binding form: " b)) + :cljs (new js/Error (core/str "Unsupported binding form: " b))))))) process-entry (fn [bvec b] (pb bvec (first b) (second b)))] (if (every? core/symbol? (map first bents)) bindings (if-let [kwbs (seq (filter #(core/keyword? (first %)) bents))] - (throw (new Exception (core/str "Unsupported binding key: " (ffirst kwbs)))) + (throw + #?(:clj (new Exception (core/str "Unsupported binding key: " (ffirst kwbs))) + :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs))))) (reduce process-entry [] bents))))) (core/defmacro let @@ -1019,7 +1049,9 @@ (defn- validate-fields [case name fields] (when-not (vector? fields) - (throw (AssertionError. (core/str case " " name ", no fields vector given."))))) + (throw + #?(:clj (AssertionError. (core/str case " " name ", no fields vector given.")) + :cljs (js/Error. (core/str case " " name ", no fields vector given.")))))) (core/defmacro deftype "(deftype name [fields*] options* specs*) @@ -1300,9 +1332,13 @@ prefix (protocol-prefix p) _ (core/doseq [[mname & arities] methods] (when (some #{0} (map count (filter vector? arities))) - (throw (Exception. - (core/str "Invalid protocol, " psym - " defines method " mname " with arity 0"))))) + (throw + #?(:clj (Exception. + (core/str "Invalid protocol, " psym + " defines method " mname " with arity 0")) + :cljs (js/Error. + (core/str "Invalid protocol, " psym + " defines method " mname " with arity 0")))))) expand-sig (fn [fname slot sig] `(~sig (if (and ~(first sig) (. ~(first sig) ~(symbol (core/str "-" slot)))) ;; Property access needed here. @@ -1482,12 +1518,18 @@ (defn- assoc-test [m test expr env] (if (contains? m test) (throw - (clojure.core/IllegalArgumentException. - (core/str "Duplicate case test constant '" - test "'" - (when (:line env) - (core/str " on line " (:line env) " " - cljs.analyzer/*cljs-file*))))) + #?(:clj (clojure.core/IllegalArgumentException. + (core/str "Duplicate case test constant '" + test "'" + (when (:line env) + (core/str " on line " (:line env) " " + cljs.analyzer/*cljs-file*)))) + :cljs (js/Error. + (core/str "Duplicate case test constant '" + test "'" + (when (:line env) + (core/str " on line " (:line env) " " + cljs.analyzer/*cljs-file*)))))) (assoc m test expr))) (defn- const? [env x] @@ -1868,9 +1910,9 @@ [options & valid-keys] (when (seq (apply disj (apply core/hash-set (keys options)) valid-keys)) (throw - (apply core/str "Only these options are valid: " - (first valid-keys) - (map #(core/str ", " %) (rest valid-keys)))))) + (apply core/str "Only these options are valid: " + (first valid-keys) + (map #(core/str ", " %) (rest valid-keys)))))) (core/defmacro defmulti "Creates a new multimethod with the associated dispatch function. @@ -1903,7 +1945,9 @@ m) mm-ns (-> &env :ns :name core/str)] (when (= (count options) 1) - (throw (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)"))) + (throw + #?(:clj (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)") + :cljs (js/Error. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")))) (let [options (apply core/hash-map options) default (core/get options :default :default)] (check-valid-options options :default :hierarchy) @@ -2203,7 +2247,9 @@ ;; Note: Cannot delegate this check to def because of the call to (with-meta name ..) (if (core/instance? clojure.lang.Symbol name) nil - (throw (IllegalArgumentException. "First argument to defn must be a symbol"))) + (throw + #?(:clj (IllegalArgumentException. "First argument to defn must be a symbol") + :cljs (js/Error. "First argument to defn must be a symbol")))) (core/let [m (if (core/string? (first fdecl)) {:doc (first fdecl)} {}) @@ -2230,8 +2276,10 @@ ifn (first inline) iname (second inline)] ;; same as: (if (and (= 'fn ifn) (not (symbol? iname))) ...) - (if (if (clojure.lang.Util/equiv 'fn ifn) - (if (core/instance? clojure.lang.Symbol iname) false true)) + (if (if #?(:clj (clojure.lang.Util/equiv 'fn ifn) + :cljs (= 'fn ifn)) + (if #?(:clj (core/instance? clojure.lang.Symbol iname) + :cljs (core/instance? Symbol iname)) false true)) ;; inserts the same fn name to the inline fn if it does not have one (assoc m :inline (cons ifn (cons (clojure.lang.Symbol/intern (.concat (.getName ^clojure.lang.Symbol name) "__inliner")) (next inline)))) From 3e8b882a32dc01cb63887f43877a97f8cf731d17 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Jun 2015 20:12:13 -0400 Subject: [PATCH 1123/4033] conditionalize portions of cjls/compiler.cljc --- src/main/clojure/cljs/compiler.cljc | 646 +++++++++++++++------------- 1 file changed, 340 insertions(+), 306 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 3b758fa08..1970b8981 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -8,18 +8,23 @@ (ns cljs.compiler (:refer-clojure :exclude [munge macroexpand-1]) - (:require [cljs.util :as util] - [clojure.java.io :as io] - [clojure.string :as string] - [clojure.tools.reader :as reader] - [cljs.env :as env] - [cljs.tagged-literals :as tags] - [cljs.analyzer :as ana] - [cljs.source-map :as sm] - [clojure.data.json :as json] - [cljs.js-deps :as deps]) - (:import java.lang.StringBuilder - java.io.File)) + #?(:clj (:require [cljs.util :as util] + [clojure.java.io :as io] + [clojure.string :as string] + [clojure.tools.reader :as reader] + [cljs.env :as env] + [cljs.tagged-literals :as tags] + [cljs.analyzer :as ana] + [cljs.source-map :as sm] + [clojure.data.json :as json] + [cljs.js-deps :as deps]) + :cljs (:require [clojure.string :as string] + [cljs.tools.reader :as reader] + [cljs.env :as env] + [cljs.analyzer :as ana] + [cljs.source-map :as sm])) + #?(:clj (:import java.lang.StringBuilder + java.io.File))) (set! *warn-on-reflection* true) @@ -79,7 +84,8 @@ ss (apply str (map #(if (reserved %) (str % "$") %) (string/split ss #"(?<=\.)|(?=\.)"))) - ms (clojure.lang.Compiler/munge ss)] + ms #?(:clj (clojure.lang.Compiler/munge ss) + :cljs (cljs.core/munge ss))] (if (symbol? s) (symbol ms) ms))))) @@ -164,20 +170,40 @@ (defn ^String emit-str [expr] (with-out-str (emit expr))) -(defmulti emit-constant class) +#?(:clj + (defmulti emit-constant class) + :cljs + (defmulti emit-constant type)) + (defmethod emit-constant nil [x] (emits "null")) -(defmethod emit-constant Long [x] (emits "(" x ")")) -(defmethod emit-constant Integer [x] (emits x)) ; reader puts Integers in metadata -(defmethod emit-constant Double [x] (emits x)) -(defmethod emit-constant BigDecimal [x] (emits (.doubleValue ^BigDecimal x))) -(defmethod emit-constant clojure.lang.BigInt [x] (emits (.doubleValue ^clojure.lang.BigInt x))) + +#?(:clj + (defmethod emit-constant Long [x] (emits "(" x ")"))) + +#?(:clj + (defmethod emit-constant Integer [x] (emits x))) ; reader puts Integers in metadata + +#?(:clj + (defmethod emit-constant Double [x] (emits x)) + :cljs + (defmethod emit-constant Number [x] (emits x))) + +#?(:clj + (defmethod emit-constant BigDecimal [x] (emits (.doubleValue ^BigDecimal x)))) + +#?(:clj + (defmethod emit-constant clojure.lang.BigInt [x] (emits (.doubleValue ^clojure.lang.BigInt x)))) + (defmethod emit-constant String [x] (emits (wrap-in-double-quotes (escape-string x)))) + (defmethod emit-constant Boolean [x] (emits (if x "true" "false"))) -(defmethod emit-constant Character [x] - (emits (wrap-in-double-quotes (escape-char x)))) -(defmethod emit-constant java.util.regex.Pattern [x] +#?(:clj + (defmethod emit-constant Character [x] + (emits (wrap-in-double-quotes (escape-char x))))) + +(defmethod emit-constant #?(:clj java.util.regex.Pattern :cljs js/RegExp) [x] (if (= "" (str x)) (emits "(new RegExp(\"\"))") (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" (str x))] @@ -198,14 +224,14 @@ (emit-constant (hash kw)) (emits ")"))) -(defmethod emit-constant clojure.lang.Keyword [x] +(defmethod emit-constant #?(:clj clojure.lang.Keyword :cljs Keyword) [x] (if (-> @env/*compiler* :options :emit-constants) (let [value (-> @env/*compiler* ::ana/constant-table x)] (emits "cljs.core." value)) (emits-keyword x) )) -(defmethod emit-constant clojure.lang.Symbol [x] +(defmethod emit-constant #?(:clj clojure.lang.Symbol :cljs Symbol) [x] (let [ns (namespace x) name (name x) symstr (if-not (nil? ns) @@ -225,10 +251,10 @@ ;; tagged literal support -(defmethod emit-constant java.util.Date [^java.util.Date date] +(defmethod emit-constant #?(:clj java.util.Date :cljs js/Date) [^java.util.Date date] (emits "new Date(" (.getTime date) ")")) -(defmethod emit-constant java.util.UUID [^java.util.UUID uuid] +(defmethod emit-constant #?(:clj java.util.UUID :cljs UUID) [^java.util.UUID uuid] (emits "new cljs.core.UUID(\"" (.toString uuid) "\")")) (defmacro emit-wrap [env & body] @@ -555,50 +581,50 @@ (defn emit-variadic-fn-method [{:keys [type name variadic params expr env recurs max-fixed-arity] :as f}] (emit-wrap env - (let [name (or name (gensym)) - mname (munge name) - delegate-name (str mname "__delegate")] - (emitln "(function() { ") - (emits "var " delegate-name " = function (") - (doseq [param params] - (emit param) - (when-not (= param (last params)) (emits ","))) - (emitln "){") - (when recurs (emitln "while(true){")) - (emits expr) - (when recurs - (emitln "break;") - (emitln "}")) - (emitln "};") - - (emitln "var " mname " = function (" (comma-sep - (if variadic - (concat (butlast params) ['var_args]) - params)) "){") - (when type - (emitln "var self__ = this;")) - (when variadic - (emits "var ") - (emit (last params)) - (emitln " = null;") - (emitln "if (arguments.length > " (dec (count params)) ") {") - (let [a (emit-arguments-to-array (dec (count params)))] - (emitln " " (last params) " = new cljs.core.IndexedSeq(" a ",0);")) - (emitln "} ")) - (emits "return " delegate-name ".call(this,") - (doseq [param params] - (emit param) - (when-not (= param (last params)) (emits ","))) - (emits ");") - (emitln "};") - - (emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";") - (emits mname ".cljs$lang$applyTo = ") - (emit-apply-to (assoc f :name name)) - (emitln ";") - (emitln mname ".cljs$core$IFn$_invoke$arity$variadic = " delegate-name ";") - (emitln "return " mname ";") - (emitln "})()")))) + (let [name (or name (gensym)) + mname (munge name) + delegate-name (str mname "__delegate")] + (emitln "(function() { ") + (emits "var " delegate-name " = function (") + (doseq [param params] + (emit param) + (when-not (= param (last params)) (emits ","))) + (emitln "){") + (when recurs (emitln "while(true){")) + (emits expr) + (when recurs + (emitln "break;") + (emitln "}")) + (emitln "};") + + (emitln "var " mname " = function (" (comma-sep + (if variadic + (concat (butlast params) ['var_args]) + params)) "){") + (when type + (emitln "var self__ = this;")) + (when variadic + (emits "var ") + (emit (last params)) + (emitln " = null;") + (emitln "if (arguments.length > " (dec (count params)) ") {") + (let [a (emit-arguments-to-array (dec (count params)))] + (emitln " " (last params) " = new cljs.core.IndexedSeq(" a ",0);")) + (emitln "} ")) + (emits "return " delegate-name ".call(this,") + (doseq [param params] + (emit param) + (when-not (= param (last params)) (emits ","))) + (emits ");") + (emitln "};") + + (emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";") + (emits mname ".cljs$lang$applyTo = ") + (emit-apply-to (assoc f :name name)) + (emitln ";") + (emitln mname ".cljs$core$IFn$_invoke$arity$variadic = " delegate-name ";") + (emitln "return " mname ";") + (emitln "})()")))) (defmethod emit* :fn [{:keys [name env methods max-fixed-arity variadic recur-frames loop-lets]}] @@ -939,50 +965,53 @@ (defmethod emit* :dot [{:keys [target field method args env]}] (emit-wrap env - (if field - (emits target "." (munge field #{})) - (emits target "." (munge method #{}) "(" - (comma-sep args) - ")")))) + (if field + (emits target "." (munge field #{})) + (emits target "." (munge method #{}) "(" + (comma-sep args) + ")")))) (defmethod emit* :js [{:keys [env code segs args]}] (emit-wrap env - (if code - (emits code) - (emits (interleave (concat segs (repeat nil)) - (concat args [nil])))))) + (if code + (emits code) + (emits (interleave (concat segs (repeat nil)) + (concat args [nil])))))) ;; TODO: unify renaming helpers - this one was hard to find - David -(defn rename-to-js - "Change the file extension from .cljs to .js. Takes a File or a - String. Always returns a String." - [^String file-str] - (cond - (.endsWith file-str ".cljs") - (clojure.string/replace file-str #"\.cljs$" ".js") - - (.endsWith file-str ".cljc") - (clojure.string/replace file-str #"\.cljc$" ".js") - - :else - (throw (IllegalArgumentException. - (str "Invalid source file extension " file-str))))) - -(defn with-core-cljs - "Ensure that core.cljs has been loaded." - ([] (with-core-cljs nil)) - ([opts] (with-core-cljs opts (fn []))) - ([opts body] - {:pre [(or (nil? opts) (map? opts)) - (fn? body)]} - (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) - (ana/analyze-file "cljs/core.cljs" opts)) - (body))) - -(defn url-path [^File f] - (.getPath (.toURL (.toURI f)))) +#?(:clj + (defn rename-to-js + "Change the file extension from .cljs to .js. Takes a File or a + String. Always returns a String." + [^String file-str] + (cond + (.endsWith file-str ".cljs") + (clojure.string/replace file-str #"\.cljs$" ".js") + + (.endsWith file-str ".cljc") + (clojure.string/replace file-str #"\.cljc$" ".js") + + :else + (throw (IllegalArgumentException. + (str "Invalid source file extension " file-str)))))) + +#?(:clj + (defn with-core-cljs + "Ensure that core.cljs has been loaded." + ([] (with-core-cljs nil)) + ([opts] (with-core-cljs opts (fn []))) + ([opts body] + {:pre [(or (nil? opts) (map? opts)) + (fn? body)]} + (when-not (get-in @env/*compiler* [::ana/namespaces 'cljs.core :defs]) + (ana/analyze-file "cljs/core.cljs" opts)) + (body)))) + +#?(:clj + (defn url-path [^File f] + (.getPath (.toURL (.toURI f))))) (defn- build-affecting-options [opts] (select-keys opts [:static-fns :optimize-constants :elide-asserts :target])) @@ -995,209 +1024,213 @@ (when opts (str " " (pr-str (build-affecting-options opts))))))) -(defn compile-file* - ([src dest] (compile-file* src dest nil)) - ([src dest opts] - (env/ensure - (with-core-cljs opts - (fn [] - (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Compiling" (str src))) - (if-let [cached (and (= (:optimizations opts) :none) +#?(:clj + (defn compile-file* + ([src dest] (compile-file* src dest nil)) + ([src dest opts] + (env/ensure + (with-core-cljs opts + (fn [] + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Compiling" (str src))) + (if-let [cached (and (= (:optimizations opts) :none) (= (:ns (ana/parse-ns src)) 'cljs.core) (io/resource "cljs/core.aot.js"))] - ;; no need to bother with analysis cache reading, handled by - ;; with-core-cljs - (do - (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Using cached cljs.core" (str src))) - (spit dest (slurp cached)) - (when (true? (:source-map opts)) - (spit (io/file (str dest ".map")) - (json/write-str - (assoc - (json/read-str (slurp (io/resource "cljs/core.aot.js.map"))) - "file" - (str (io/file (util/output-directory opts) "cljs" "core.js")))))) - (ana/parse-ns src dest nil)) - (with-open [out ^java.io.Writer (io/make-writer dest {})] - (binding [*out* out - ana/*cljs-ns* 'cljs.user - ana/*cljs-file* (.getPath ^File src) - reader/*alias-map* (or reader/*alias-map* {}) - ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) - *source-map-data* (when (:source-map opts) - (atom - {:source-map (sorted-map) - :gen-col 0 - :gen-line 0}))] - (emitln (compiled-by-string opts)) - (with-open [rdr (io/reader src)] - (loop [forms (ana/forms-seq* rdr (util/path src)) - ns-name nil - deps nil] - (if (seq forms) - (let [env (ana/empty-env) - ast (ana/analyze env (first forms) nil opts)] - (emit ast) - (if (= (:op ast) :ns) - (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) - (recur (rest forms) ns-name deps))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= (:optimizations opts) :none)) - (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" - (or (:source-map-url opts) (.getName sm-file)) - (if (true? (:source-map-timestamp opts)) - (str "?rel=" (System/currentTimeMillis)) - "")) - (spit sm-file - (sm/encode {(url-path src) (:source-map sm-data)} - {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest) - :source-map-timestamp (:source-map-timestamp opts) - :source-map-pretty-print (:source-map-pretty-print opts)})))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src (ana/parse-ns src) output-dir :write))) - ret))))))))))))) - -(defn requires-compilation? - "Return true if the src file requires compilation." - ([src dest] (requires-compilation? src dest nil)) - ([^File src ^File dest opts] - (env/ensure - (or (not (.exists dest)) - (> (.lastModified src) (.lastModified dest)) - (let [version' (util/compiled-by-version dest) - version (util/clojurescript-version)] - (and version (not= version version'))) - (and opts - (not= (build-affecting-options opts) (build-affecting-options (util/build-options dest)))) - (and opts - (:source-map opts) - (if (= (:optimizations opts) :none) - (not (.exists (io/file (str (.getPath dest) ".map")))) - (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))) + ;; no need to bother with analysis cache reading, handled by + ;; with-core-cljs + (do + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Using cached cljs.core" (str src))) + (spit dest (slurp cached)) + (when (true? (:source-map opts)) + (spit (io/file (str dest ".map")) + (json/write-str + (assoc + (json/read-str (slurp (io/resource "cljs/core.aot.js.map"))) + "file" + (str (io/file (util/output-directory opts) "cljs" "core.js")))))) + (ana/parse-ns src dest nil)) + (with-open [out ^java.io.Writer (io/make-writer dest {})] + (binding [*out* out + ana/*cljs-ns* 'cljs.user + ana/*cljs-file* (.getPath ^File src) + reader/*alias-map* (or reader/*alias-map* {}) + ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) + *source-map-data* (when (:source-map opts) + (atom + {:source-map (sorted-map) + :gen-col 0 + :gen-line 0}))] + (emitln (compiled-by-string opts)) + (with-open [rdr (io/reader src)] + (loop [forms (ana/forms-seq* rdr (util/path src)) + ns-name nil + deps nil] + (if (seq forms) + (let [env (ana/empty-env) + ast (ana/analyze env (first forms) nil opts)] + (emit ast) + (if (= (:op ast) :ns) + (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) + (recur (rest forms) ns-name deps))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= (:optimizations opts) :none)) + (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] + (emits "\n//# sourceMappingURL=" + (or (:source-map-url opts) (.getName sm-file)) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + "")) + (spit sm-file + (sm/encode {(url-path src) (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 2) + :file (url-path dest) + :source-map-timestamp (:source-map-timestamp opts) + :source-map-pretty-print (:source-map-pretty-print opts)})))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src (ana/parse-ns src) output-dir :write))) + ret)))))))))))))) + +#?(:clj + (defn requires-compilation? + "Return true if the src file requires compilation." + ([src dest] (requires-compilation? src dest nil)) + ([^File src ^File dest opts] + (env/ensure + (or (not (.exists dest)) + (> (.lastModified src) (.lastModified dest)) + (let [version' (util/compiled-by-version dest) + version (util/clojurescript-version)] + (and version (not= version version'))) + (and opts (not= (build-affecting-options opts) + (build-affecting-options (util/build-options dest)))) + (and opts (:source-map opts) + (if (= (:optimizations opts) :none) + (not (.exists (io/file (str (.getPath dest) ".map")))) + (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))) (let [{ns :ns} (ana/parse-ns src) {:keys [recompile visited]} (and *dependents* @*dependents*)] (and (contains? recompile ns) - (not (contains? visited ns)))))))) - -(defn compile-file - "Compiles src to a file of the same name, but with a .js extension, - in the src file's directory. - - With dest argument, write file to provided location. If the dest - argument is a file outside the source tree, missing parent - directories will be created. The src file will only be compiled if - the dest file has an older modification time. - - Both src and dest may be either a String or a File. - - Returns a map containing {:ns .. :provides .. :requires .. :file ..}. - If the file was not compiled returns only {:file ...}" - ([src] - (let [dest (rename-to-js src)] - (compile-file src dest nil))) - ([src dest] - (compile-file src dest nil)) - ([src dest opts] - {:post [map?]} - (binding [ana/*file-defs* (atom #{})] - (let [nses (get @env/*compiler* ::ana/namespaces) - src-file (io/file src) - dest-file (io/file dest) - opts (merge {:optimizations :none} opts)] - (if (.exists src-file) - (try - (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) - opts (if (and (= ns 'cljs.core) - (not (false? (:static-fns opts)))) - (assoc opts :static-fns true) - opts)] - (if (or (requires-compilation? src-file dest-file opts) + (not (contains? visited ns))))))))) + +#?(:clj + (defn compile-file + "Compiles src to a file of the same name, but with a .js extension, + in the src file's directory. + + With dest argument, write file to provided location. If the dest + argument is a file outside the source tree, missing parent + directories will be created. The src file will only be compiled if + the dest file has an older modification time. + + Both src and dest may be either a String or a File. + + Returns a map containing {:ns .. :provides .. :requires .. :file ..}. + If the file was not compiled returns only {:file ...}" + ([src] + (let [dest (rename-to-js src)] + (compile-file src dest nil))) + ([src dest] + (compile-file src dest nil)) + ([src dest opts] + {:post [map?]} + (binding [ana/*file-defs* (atom #{})] + (let [nses (get @env/*compiler* ::ana/namespaces) + src-file (io/file src) + dest-file (io/file dest) + opts (merge {:optimizations :none} opts)] + (if (.exists src-file) + (try + (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) + opts (if (and (= ns 'cljs.core) + (not (false? (:static-fns opts)))) + (assoc opts :static-fns true) + opts)] + (if (or (requires-compilation? src-file dest-file opts) (:force opts)) - (do - (util/mkdirs dest-file) - (when (and (get-in nses [ns :defs]) - (not= 'cljs.core ns) - (not= :interactive (:mode opts))) - (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) - (let [ret (compile-file* src-file dest-file opts)] - (when *dependents* - (swap! *dependents* - (fn [{:keys [recompile visited]}] - {:recompile (into recompile - (ana/ns-dependents ns - (merge *inputs* nses))) - :visited (conj visited ns)}))) - ret)) - (do - ;; populate compilation environment with analysis information - ;; when constants are optimized - (when (and (true? (:optimize-constants opts)) - (nil? (get-in nses [ns :defs]))) - (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) - ns-info))) - (catch Exception e - (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) - (throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))) - -(defn cljs-files-in - "Return a sequence of all .cljs and .cljc files in the given directory." - [dir] - (filter - #(let [name (.getName ^File %)] - (and (or (.endsWith name ".cljs") - (.endsWith name ".cljc")) + (do + (util/mkdirs dest-file) + (when (and (get-in nses [ns :defs]) + (not= 'cljs.core ns) + (not= :interactive (:mode opts))) + (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) + (let [ret (compile-file* src-file dest-file opts)] + (when *dependents* + (swap! *dependents* + (fn [{:keys [recompile visited]}] + {:recompile (into recompile + (ana/ns-dependents ns + (merge *inputs* nses))) + :visited (conj visited ns)}))) + ret)) + (do + ;; populate compilation environment with analysis information + ;; when constants are optimized + (when (and (true? (:optimize-constants opts)) + (nil? (get-in nses [ns :defs]))) + (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) + ns-info))) + (catch Exception e + (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) + (throw (java.io.FileNotFoundException. (str "The file " src " does not exist."))))))))) + +#?(:clj + (defn cljs-files-in + "Return a sequence of all .cljs and .cljc files in the given directory." + [dir] + (filter + #(let [name (.getName ^File %)] + (and (or (.endsWith name ".cljs") + (.endsWith name ".cljc")) (not= \. (first name)) (not (contains? cljs-reserved-file-names name)))) - (file-seq dir))) - -(defn compile-root - "Looks recursively in src-dir for .cljs files and compiles them to - .js files. If target-dir is provided, output will go into this - directory mirroring the source directory structure. Returns a list - of maps containing information about each file which was compiled - in dependency order." - ([src-dir] - (compile-root src-dir "out")) - ([src-dir target-dir] - (compile-root src-dir target-dir nil)) - ([src-dir target-dir opts] - (swap! env/*compiler* assoc :root src-dir) - (let [src-dir-file (io/file src-dir) - inputs (deps/dependency-order - (map #(ana/parse-ns %) - (cljs-files-in src-dir-file)))] - (binding [*inputs* (zipmap (map :ns inputs) inputs)] - (loop [inputs (seq inputs) compiled []] - (if inputs - (let [{:keys [source-file] :as ns-info} (first inputs) - output-file (util/to-target-file target-dir ns-info) - ijs (compile-file source-file output-file opts)] - (recur - (next inputs) - (conj compiled - (assoc ijs :file-name (.getPath output-file))))) - compiled)))))) + (file-seq dir)))) + +#?(:clj + (defn compile-root + "Looks recursively in src-dir for .cljs files and compiles them to + .js files. If target-dir is provided, output will go into this + directory mirroring the source directory structure. Returns a list + of maps containing information about each file which was compiled + in dependency order." + ([src-dir] + (compile-root src-dir "out")) + ([src-dir target-dir] + (compile-root src-dir target-dir nil)) + ([src-dir target-dir opts] + (swap! env/*compiler* assoc :root src-dir) + (let [src-dir-file (io/file src-dir) + inputs (deps/dependency-order + (map #(ana/parse-ns %) + (cljs-files-in src-dir-file)))] + (binding [*inputs* (zipmap (map :ns inputs) inputs)] + (loop [inputs (seq inputs) compiled []] + (if inputs + (let [{:keys [source-file] :as ns-info} (first inputs) + output-file (util/to-target-file target-dir ns-info) + ijs (compile-file source-file output-file opts)] + (recur + (next inputs) + (conj compiled + (assoc ijs :file-name (.getPath output-file))))) + compiled))))))) ;; TODO: needs fixing, table will include other things than keywords - David @@ -1209,8 +1242,9 @@ (emits-keyword keyword) (emits ";\n")))) -(defn emit-constants-table-to-file [table dest] - (io/make-parents dest) - (with-open [out ^java.io.Writer (io/make-writer dest {})] - (binding [*out* out] - (emit-constants-table table)))) +#?(:clj + (defn emit-constants-table-to-file [table dest] + (io/make-parents dest) + (with-open [out ^java.io.Writer (io/make-writer dest {})] + (binding [*out* out] + (emit-constants-table table))))) From a56e2252b3455da5e3dbc231ff7669d55fb1ab15 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Jun 2015 20:16:03 -0400 Subject: [PATCH 1124/4033] remove unused import --- src/main/clojure/cljs/core.cljc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 6629e859a..b6c7096a9 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -39,8 +39,7 @@ clojure.set cljs.compiler [cljs.util :as util] - [cljs.env :as env]) - (:import [java.io File])) + [cljs.env :as env])) (alias 'core 'clojure.core) (alias 'ana 'cljs.analyzer) From 91d6802f13893e8ccfa13a0d48ec84df35ecf5fc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Jun 2015 22:15:40 -0400 Subject: [PATCH 1125/4033] conditional compiler string logic & foreign libs handling --- src/main/clojure/cljs/compiler.cljc | 56 ++++++++++++++++------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1970b8981..b5537b8dc 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -18,13 +18,15 @@ [cljs.source-map :as sm] [clojure.data.json :as json] [cljs.js-deps :as deps]) - :cljs (:require [clojure.string :as string] + :cljs (:require [goog.string :as gstring] + [clojure.string :as string] [cljs.tools.reader :as reader] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.source-map :as sm])) #?(:clj (:import java.lang.StringBuilder - java.io.File))) + java.io.File) + :cljs (:import [goog.string StringBuffer]))) (set! *warn-on-reflection* true) @@ -94,7 +96,8 @@ (interpose "," xs)) (defn- escape-char [^Character c] - (let [cp (.hashCode c)] + (let [cp #?(:clj (.hashCode c) + :cljs (gstring/hashCode c))] (case cp ; Handle printable escapes before ASCII 34 "\\\"" @@ -110,7 +113,8 @@ (format "\\u%04X" cp))))) ; Any other character is Unicode (defn- escape-string [^CharSequence s] - (let [sb (StringBuilder. (count s))] + (let [sb #?(:clj (StringBuilder. (count s)) + :cljs (StringBuffer.))] (doseq [c s] (.append sb (escape-char c))) (.toString sb))) @@ -466,7 +470,9 @@ (defn get-define [mname jsdoc] (let [opts (get @env/*compiler* :options)] - (and (some #(.startsWith ^String % "@define") jsdoc) + (and (some #?(:clj #(.startsWith ^String % "@define") + :cljs #(gstring/startsWith % "@define")) + jsdoc) opts (= (:optimizations opts) :none) (let [define (get-in opts [:closure-defines (str mname)])] @@ -893,18 +899,19 @@ (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] (cond - (ana/foreign-dep? lib) - (let [{:keys [target optimizations]} (get @env/*compiler* :options)] - ;; we only load foreign libraries under optimizations :none - (when (= :none optimizations) - (if (= :nodejs target) - ;; under node.js we load foreign libs globally - (let [{:keys [js-dependency-index options]} @env/*compiler* - ijs-url (get-in js-dependency-index [(name lib) :url])] - (emitln "cljs.core.load_file(\"" - (str (io/file (util/output-directory options) (util/get-name ijs-url))) - "\");")) - (emitln "goog.require('" (munge lib) "');")))) + #?@(:clj + [(ana/foreign-dep? lib) + (let [{:keys [target optimizations]} (get @env/*compiler* :options)] + ;; we only load foreign libraries under optimizations :none + (when (= :none optimizations) + (if (= :nodejs target) + ;; under node.js we load foreign libs globally + (let [{:keys [js-dependency-index options]} @env/*compiler* + ijs-url (get-in js-dependency-index [(name lib) :url])] + (emitln "cljs.core.load_file(\"" + (str (io/file (util/output-directory options) (util/get-name ijs-url))) + "\");")) + (emitln "goog.require('" (munge lib) "');"))))]) (or (-> libs meta :reload) (= (get reloads lib) :reload)) @@ -1016,13 +1023,14 @@ (defn- build-affecting-options [opts] (select-keys opts [:static-fns :optimize-constants :elide-asserts :target])) -(defn compiled-by-string - ([] (compiled-by-string nil)) - ([opts] - (str "// Compiled by ClojureScript " - (util/clojurescript-version) - (when opts - (str " " (pr-str (build-affecting-options opts))))))) +#?(:clj + (defn compiled-by-string + ([] (compiled-by-string nil)) + ([opts] + (str "// Compiled by ClojureScript " + (util/clojurescript-version) + (when opts + (str " " (pr-str (build-affecting-options opts)))))))) #?(:clj (defn compile-file* From de33cbf5b9537e7e1ff511cb3ed89808cdcc3cd1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Jun 2015 06:26:18 -0400 Subject: [PATCH 1126/4033] System/identityHashCode -> hash when bootstrapping --- src/main/clojure/cljs/compiler.cljc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b5537b8dc..eeb8de0c4 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -72,7 +72,9 @@ (fn-self-name s) ;; Unshadowing (let [depth (shadow-depth s) - renamed (*lexical-renames* (System/identityHashCode s)) + renamed (*lexical-renames* + #?(:clj (System/identityHashCode s) + :cljs (hash s))) munged-name (munge (cond field (str "self__." name) renamed renamed @@ -739,11 +741,13 @@ [{:keys [bindings expr env]} is-loop] (let [context (:context env)] (when (= :expr context) (emits "(function (){")) - (binding [*lexical-renames* (into *lexical-renames* - (when (= :statement context) - (map #(vector (System/identityHashCode %) - (gensym (str (:name %) "-"))) - bindings)))] + (binding [*lexical-renames* + (into *lexical-renames* + (when (= :statement context) + (map #(vector #?(:clj (System/identityHashCode %) + :cljs (hash %)) + (gensym (str (:name %) "-"))) + bindings)))] (doseq [{:keys [init] :as binding} bindings] (emits "var ") (emit binding) ; Binding will be treated as a var From 2a4fb439962461695ce86c9e6b8aa28f377cb706 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Jun 2015 07:25:05 -0400 Subject: [PATCH 1127/4033] fix global refs, add cljs.compiler.macros for bootstrap --- src/main/clojure/cljs/compiler.cljc | 20 +++++++++++--------- src/main/clojure/cljs/compiler/macros.clj | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 src/main/clojure/cljs/compiler/macros.clj diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index eeb8de0c4..3d2580687 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -8,6 +8,7 @@ (ns cljs.compiler (:refer-clojure :exclude [munge macroexpand-1]) + #?(:cljs (:require-macros [cljs.compiler.macros :refer [emit-wrap]])) #?(:clj (:require [cljs.util :as util] [clojure.java.io :as io] [clojure.string :as string] @@ -28,7 +29,7 @@ java.io.File) :cljs (:import [goog.string StringBuffer]))) -(set! *warn-on-reflection* true) +#?(:clj (set! *warn-on-reflection* true)) (def js-reserved ana/js-reserved) @@ -192,7 +193,7 @@ #?(:clj (defmethod emit-constant Double [x] (emits x)) :cljs - (defmethod emit-constant Number [x] (emits x))) + (defmethod emit-constant js/Number [x] (emits x))) #?(:clj (defmethod emit-constant BigDecimal [x] (emits (.doubleValue ^BigDecimal x)))) @@ -200,10 +201,10 @@ #?(:clj (defmethod emit-constant clojure.lang.BigInt [x] (emits (.doubleValue ^clojure.lang.BigInt x)))) -(defmethod emit-constant String [x] +(defmethod emit-constant js/String [x] (emits (wrap-in-double-quotes (escape-string x)))) -(defmethod emit-constant Boolean [x] (emits (if x "true" "false"))) +(defmethod emit-constant js/Boolean [x] (emits (if x "true" "false"))) #?(:clj (defmethod emit-constant Character [x] @@ -263,11 +264,12 @@ (defmethod emit-constant #?(:clj java.util.UUID :cljs UUID) [^java.util.UUID uuid] (emits "new cljs.core.UUID(\"" (.toString uuid) "\")")) -(defmacro emit-wrap [env & body] - `(let [env# ~env] - (when (= :return (:context env#)) (emits "return ")) - ~@body - (when-not (= :expr (:context env#)) (emitln ";")))) +#?(:clj + (defmacro emit-wrap [env & body] + `(let [env# ~env] + (when (= :return (:context env#)) (emits "return ")) + ~@body + (when-not (= :expr (:context env#)) (emitln ";"))))) (defmethod emit* :no-op [m]) diff --git a/src/main/clojure/cljs/compiler/macros.clj b/src/main/clojure/cljs/compiler/macros.clj new file mode 100644 index 000000000..22b2f36ad --- /dev/null +++ b/src/main/clojure/cljs/compiler/macros.clj @@ -0,0 +1,17 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns cljs.compiler.macros + (:refer-clojure :exclude [let]) + (:require [cljs.core :refer [let]])) + +(defmacro emit-wrap [env & body] + `(let [env# ~env] + (when (= :return (:context env#)) (cljs.compiler/emits "return ")) + ~@body + (when-not (= :expr (:context env#)) (cljs.compiler/emitln ";")))) \ No newline at end of file From f13e4a89f9ab1022fda64f72c4457f3df4cfa5c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Jun 2015 07:30:05 -0400 Subject: [PATCH 1128/4033] typos around js globals --- src/main/clojure/cljs/compiler.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 3d2580687..4cb7d8e55 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -201,10 +201,10 @@ #?(:clj (defmethod emit-constant clojure.lang.BigInt [x] (emits (.doubleValue ^clojure.lang.BigInt x)))) -(defmethod emit-constant js/String [x] +(defmethod emit-constant #?(:clj String :cljs js/String) [x] (emits (wrap-in-double-quotes (escape-string x)))) -(defmethod emit-constant js/Boolean [x] (emits (if x "true" "false"))) +(defmethod emit-constant #?(:clj Boolean :cljs js/Boolean) [x] (emits (if x "true" "false"))) #?(:clj (defmethod emit-constant Character [x] From acf2b2ee167144a145fa599544f266d60bbb1ecf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Jun 2015 07:42:47 -0400 Subject: [PATCH 1129/4033] JS unicode char hex formatting --- src/main/clojure/cljs/compiler.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 4cb7d8e55..20bcbb5b8 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -113,7 +113,8 @@ 9 "\\t" (if (< 31 cp 127) c ; Print simple ASCII characters - (format "\\u%04X" cp))))) ; Any other character is Unicode + #?(:clj (format "\\u%04X" cp) + :cljs (str "\\u" (.toString cp 16))))))) ; Any other character is Unicode (defn- escape-string [^CharSequence s] (let [sb #?(:clj (StringBuilder. (count s)) From 9665e53856e4814702245b9d49528e549e7db9e3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Jun 2015 07:45:45 -0400 Subject: [PATCH 1130/4033] re-matcher does not exist in CLJS --- src/main/clojure/cljs/compiler.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 20bcbb5b8..91a62758f 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -215,7 +215,10 @@ (if (= "" (str x)) (emits "(new RegExp(\"\"))") (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" (str x))] - (emits \/ (.replaceAll (re-matcher #"/" pattern) "\\\\/") \/ flags)))) + (emits \/ + #?(:clj (.replaceAll (re-matcher #"/" pattern) "\\\\/") + :cljs (string/replace pattern #"/" "\\\\/")) + \/ flags)))) (defn emits-keyword [kw] (let [ns (namespace kw) From 1dd7da6245da4f2986fd21e25eba7e9fd3128e13 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Jun 2015 07:49:48 -0400 Subject: [PATCH 1131/4033] fix broken RegExp for CLJS --- src/main/clojure/cljs/compiler.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 91a62758f..a12d2545b 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -85,7 +85,8 @@ munged-name (symbol (str munged-name "__$" depth)))))) ;; String munging - (let [ss (string/replace (str s) #"\/(.)" ".$1") ; Division is special + (let [ss (string/replace (str s) + #?(:clj #"\/(.)" :cljs (js/RegExp. "\\/(.)")) ".$1") ; Division is special ss (apply str (map #(if (reserved %) (str % "$") %) (string/split ss #"(?<=\.)|(?=\.)"))) From 8b3d5f33637ccafe5e03fa937ede98fe8a93474a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Jun 2015 07:59:22 -0400 Subject: [PATCH 1132/4033] drop :cljs case for cljs.env/ensure macro --- src/main/clojure/cljs/env.cljc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 9a8e072f1..cbb621626 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -70,6 +70,4 @@ state that is accessed/maintained by many different components."} ~@body (finally (if (nil? val#) - (pop-thread-bindings)))))) - :cljs - (def ensure identity)) + (pop-thread-bindings))))))) From 6ae3c6a6d7dae4674d54877e8cfc002fb890fd6e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Jun 2015 08:05:12 -0400 Subject: [PATCH 1133/4033] conditionalize cljs.env/ensure usage --- src/main/clojure/cljs/compiler.cljc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a12d2545b..2732a2399 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -7,13 +7,14 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.compiler - (:refer-clojure :exclude [munge macroexpand-1]) - #?(:cljs (:require-macros [cljs.compiler.macros :refer [emit-wrap]])) + (:refer-clojure :exclude [munge macroexpand-1 ensure]) + #?(:cljs (:require-macros [cljs.compiler.macros :refer [emit-wrap]] + [cljs.env.macros :refer [ensure]])) #?(:clj (:require [cljs.util :as util] [clojure.java.io :as io] [clojure.string :as string] [clojure.tools.reader :as reader] - [cljs.env :as env] + [cljs.env :as env :refer [ensure]] [cljs.tagged-literals :as tags] [cljs.analyzer :as ana] [cljs.source-map :as sm] @@ -130,7 +131,7 @@ (defmulti emit* :op) (defn emit [ast] - (env/ensure + (ensure (when *source-map-data* (let [{:keys [env]} ast] (when (:line env) @@ -1047,7 +1048,7 @@ (defn compile-file* ([src dest] (compile-file* src dest nil)) ([src dest opts] - (env/ensure + (ensure (with-core-cljs opts (fn [] (when (or ana/*verbose* (:verbose opts)) @@ -1131,7 +1132,7 @@ "Return true if the src file requires compilation." ([src dest] (requires-compilation? src dest nil)) ([^File src ^File dest opts] - (env/ensure + (ensure (or (not (.exists dest)) (> (.lastModified src) (.lastModified dest)) (let [version' (util/compiled-by-version dest) From 06a7944927cb0dee91ea681739fa290d9fb4016b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 15 Jun 2015 00:17:21 -0400 Subject: [PATCH 1134/4033] CLJS-1308: :analyze-path should be extended to take a vector of paths If :analyze-path is a vector, run analyze-source across its elements, otherwise handle as a scalar. --- src/main/clojure/cljs/repl.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 442ea3db7..d0f09f39d 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -827,7 +827,9 @@ (binding [*repl-opts* opts] (try (when analyze-path - (analyze-source analyze-path opts)) + (if (vector? analyze-path) + (run! #(analyze-source % opts) analyze-path) + (analyze-source analyze-path opts))) (init) (catch Throwable e (caught e repl-env opts))) From 4b5ad450421fdafe70672b765255edfa747efb96 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Jun 2015 18:22:50 -0400 Subject: [PATCH 1135/4033] exclude alias macro in cljs, we need to redef --- src/main/clojure/cljs/core.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b6c7096a9..27b569504 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -34,7 +34,8 @@ cond-> cond->> as-> some-> some->> - if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand]) + if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand + #?(:cljs alias)]) (:require clojure.walk clojure.set cljs.compiler From 1b6097de3d70700c8231f73ff3ab8103a8faa0b4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Jun 2015 20:23:13 -0400 Subject: [PATCH 1136/4033] stub out cljs.core.macros --- src/main/clojure/cljs/core.cljc | 82 ++++++++++++++------------- src/main/clojure/cljs/core/macros.clj | 17 ++++++ 2 files changed, 59 insertions(+), 40 deletions(-) create mode 100644 src/main/clojure/cljs/core/macros.clj diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 27b569504..36a5602e2 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -36,53 +36,55 @@ if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand #?(:cljs alias)]) + #?(:cljs (:require-macros [cljs.core.macros :refer [import-macros alias]])) (:require clojure.walk - clojure.set - cljs.compiler - [cljs.util :as util] - [cljs.env :as env])) + clojure.set + cljs.compiler + [cljs.util :as util] + [cljs.env :as env])) (alias 'core 'clojure.core) (alias 'ana 'cljs.analyzer) -(core/defmacro import-macros [ns [& vars]] - (core/let [ns (find-ns ns) - vars (map #(ns-resolve ns %) vars) - syms (map - (core/fn [^clojure.lang.Var v] - (core/-> v .sym - (with-meta - (merge - {:macro true} - (update-in (select-keys (meta v) [:arglists :doc :file :line]) - [:arglists] (core/fn [arglists] `(quote ~arglists))))))) - vars) - defs (map - (core/fn [sym var] - (core/let [{:keys [arglists doc file line]} (meta sym)] - `(do - (def ~sym (deref ~var)) - ;for AOT compilation - (alter-meta! (var ~sym) assoc - :macro true - :arglists ~arglists - :doc ~doc - :file ~file - :line ~line)))) - syms vars)] - `(do ~@defs - :imported))) +#?(:clj + (core/defmacro import-macros [ns [& vars]] + (core/let [ns (find-ns ns) + vars (map #(ns-resolve ns %) vars) + syms (map + (core/fn [^clojure.lang.Var v] + (core/-> v .sym + (with-meta + (merge + {:macro true} + (update-in (select-keys (meta v) [:arglists :doc :file :line]) + [:arglists] (core/fn [arglists] `(quote ~arglists))))))) + vars) + defs (map + (core/fn [sym var] + (core/let [{:keys [arglists doc file line]} (meta sym)] + `(do + (def ~sym (deref ~var)) + ;for AOT compilation + (alter-meta! (var ~sym) assoc + :macro true + :arglists ~arglists + :doc ~doc + :file ~file + :line ~line)))) + syms vars)] + `(do ~@defs + :imported)))) (import-macros clojure.core - [-> ->> .. assert comment cond - declare defn- - doto - extend-protocol fn for - if-let if-not letfn - memfn - when when-first when-let when-not while - cond-> cond->> as-> some-> some->> - if-some when-some]) + [-> ->> .. assert comment cond + declare defn- + doto + extend-protocol fn for + if-let if-not letfn + memfn + when when-first when-let when-not while + cond-> cond->> as-> some-> some->> + if-some when-some]) (defn- ^{:dynamic true} assert-valid-fdecl "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn." diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj new file mode 100644 index 000000000..c6283b2dd --- /dev/null +++ b/src/main/clojure/cljs/core/macros.clj @@ -0,0 +1,17 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns cljs.core.macros + (:refer-clojure :exclude [binding]) + (:require [cljs.repl :refer [source]])) + +(defmacro import-macros [ns [& vars]] + ) + +(defmacro alias [ns alias] + ) \ No newline at end of file From fe1dccb4d917d6d0963b1b84ebb24115db1cdb53 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 16 Jun 2015 20:30:04 -0400 Subject: [PATCH 1137/4033] exclude alias, cljs alias macro needs to deal with quoted symbols --- src/main/clojure/cljs/core/macros.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index c6283b2dd..e64f9aea0 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -7,11 +7,13 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.core.macros - (:refer-clojure :exclude [binding]) - (:require [cljs.repl :refer [source]])) + (:refer-clojure :exclude [binding alias]) + (:require [cljs.env :as env] + [cljs.analyzer :as ana] + [cljs.repl :refer [source]])) (defmacro import-macros [ns [& vars]] ) -(defmacro alias [ns alias] +(defmacro alias [[_ ns] [_ alias]] ) \ No newline at end of file From e2589245f4df9a168c75fa25b98abb18fe8086a9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Jun 2015 06:57:43 -0400 Subject: [PATCH 1138/4033] cljs import-macros just inlines source --- src/main/clojure/cljs/core/macros.clj | 28 +++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index e64f9aea0..b650696c8 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -7,13 +7,33 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.core.macros - (:refer-clojure :exclude [binding alias]) - (:require [cljs.env :as env] + (:refer-clojure :exclude [alias]) + (:require [clojure.java.io :as io] + [clojure.tools.reader :as reader] + [clojure.tools.reader.reader-types :as readers] + [cljs.env :as env] [cljs.analyzer :as ana] - [cljs.repl :refer [source]])) + [cljs.repl :refer [source]]) + (:import [java.io PushbackReader])) + +(defn source-fn + [x] + (when-let [m (-> x resolve meta)] + (when-let [filepath (:file m)] + (let [f (io/file filepath) + f (if (.exists f) + f + (io/resource filepath))] + (when f + (with-open [pbr (PushbackReader. (io/reader f))] + (let [rdr (readers/source-logging-push-back-reader pbr)] + (dotimes [_ (dec (:line m))] (readers/read-line rdr)) + (reader/read {:read-cond :allow :features #{:cljs}} rdr)))))))) (defmacro import-macros [ns [& vars]] - ) + `(do + ~@(binding [*ns* (find-ns ns)] + (doall (map source-fn vars))))) (defmacro alias [[_ ns] [_ alias]] ) \ No newline at end of file From 735fa2e25058846da062325e7b841f9ade42ec85 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Jun 2015 07:09:07 -0400 Subject: [PATCH 1139/4033] switch reader features to :clj, simple alias for cljs bootstrap --- src/main/clojure/cljs/core/macros.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index b650696c8..5982b751d 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -28,7 +28,7 @@ (with-open [pbr (PushbackReader. (io/reader f))] (let [rdr (readers/source-logging-push-back-reader pbr)] (dotimes [_ (dec (:line m))] (readers/read-line rdr)) - (reader/read {:read-cond :allow :features #{:cljs}} rdr)))))))) + (reader/read {:read-cond :allow :features #{:clj}} rdr)))))))) (defmacro import-macros [ns [& vars]] `(do @@ -36,4 +36,5 @@ (doall (map source-fn vars))))) (defmacro alias [[_ ns] [_ alias]] - ) \ No newline at end of file + (swap! @env/*compiler* + [::namespaces (.getName *ns*) :requires] assoc alias ns)) \ No newline at end of file From 05d2d170cafe31f9a3671948b126ec3c90fce4ff Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Jun 2015 07:31:16 -0400 Subject: [PATCH 1140/4033] remove unused cljs.util import in macros files --- src/main/clojure/cljs/core.cljc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 36a5602e2..91cc39f21 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -38,10 +38,9 @@ #?(:cljs alias)]) #?(:cljs (:require-macros [cljs.core.macros :refer [import-macros alias]])) (:require clojure.walk - clojure.set - cljs.compiler - [cljs.util :as util] - [cljs.env :as env])) + clojure.set + cljs.compiler + [cljs.env :as env])) (alias 'core 'clojure.core) (alias 'ana 'cljs.analyzer) From 269d02b6970c8d2cf4798f3e01083409c5716907 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 15:56:59 -0400 Subject: [PATCH 1141/4033] bump to Clojure 1.7.0-RC2 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index e98c7caa9..868671289 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.7.0-RC1 + 1.7.0-RC2 com.google.javascript diff --git a/project.clj b/project.clj index f40d70340..59f5bab85 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ :source-paths ["src/main/clojure"] :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure"] - :dependencies [[org.clojure/clojure "1.7.0-RC1"] + :dependencies [[org.clojure/clojure "1.7.0-RC2"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.2"] [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] diff --git a/script/bootstrap b/script/bootstrap index f502589be..0e745c708 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.7.0-RC1" +CLOJURE_RELEASE="1.7.0-RC2" CLOSURE_RELEASE="20150609" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150505-021ed5b3" From 87f39511a54d7b91ae86f581fda90f280f985816 Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Wed, 17 Jun 2015 16:05:36 -0500 Subject: [PATCH 1142/4033] CLJS-1231: AMD Module Processing Add support for AMD modules. AMD modules are first converted to CommonJS modules and then converted to Google Closure modules. Include AMD modules using the :module-type option, e.g. :foreign-libs [{:file "libs/foo.js" :provides ["foo"] :module-type :amd}] --- src/main/clojure/cljs/closure.clj | 33 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 166773a55..66972abb7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -54,7 +54,7 @@ Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy JSModule JSModuleGraph SourceMap ProcessCommonJSModules - ES6ModuleLoader AbstractCompiler] + ES6ModuleLoader AbstractCompiler TransformAMDToCJSModule] [com.google.javascript.rhino Node] [java.security MessageDigest] [javax.xml.bind DatatypeConverter] @@ -87,8 +87,16 @@ (.getConstructor ES6ModuleLoader (into-array java.lang.Class [AbstractCompiler java.lang.String]))) - (def can-convert-js-module? true) - (def can-convert-js-module? false)) + (def can-convert-commonjs? true) + (def can-convert-commonjs? false)) + +(compile-if + (and can-convert-commonjs? + (.getConstructor TransformAMDToCJSModule + (into-array java.lang.Class + [AbstractCompiler]))) + (def can-convert-amd? true) + (def can-convert-amd? false)) ;; Closure API ;; =========== @@ -1192,9 +1200,13 @@ (defmulti convert-js-module "Takes a JavaScript module and rewrites it into a Google Closure-compatible form. Returns the source of the new module as a single string." - :module-type) + (fn [{module-type :module-type :as js}] + (if (and (= module-type :amd) can-convert-amd?) + ;; AMD modules are converted via CommonJS modules + :commonjs + module-type))) -(compile-if can-convert-js-module? +(compile-if can-convert-commonjs? (defmethod convert-js-module :commonjs [js] (let [js-file (:file js) path (.getParent (io/file js-file)) @@ -1210,6 +1222,8 @@ es6-loader (ES6ModuleLoader. closure-compiler module-root) cjs (ProcessCommonJSModules. closure-compiler es6-loader) ^Node root (.parse closure-compiler source-file)] + (compile-if can-convert-amd? + (.process (TransformAMDToCJSModule. closure-compiler) nil root)) (.process cjs nil root) (.toSource closure-compiler root)))) @@ -1488,7 +1502,8 @@ [opts] (let [js-modules (filter :module-type (:foreign-libs opts))] (reduce (fn [opts {:keys [file module-type] :as lib}] - (if (= module-type :commonjs) + (if (or (and (= module-type :commonjs) can-convert-commonjs?) + (and (= module-type :amd) can-convert-amd?)) (let [module-name (ProcessCommonJSModules/toModuleName file) ijs (write-javascript opts (deps/load-foreign-library lib))] (doseq [provide (:provides ijs)] @@ -1511,9 +1526,9 @@ ([source opts compiler-env] (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) - all-opts (cond-> opts - true add-implicit-options - can-convert-js-module? process-js-modules)] + all-opts (-> opts + add-implicit-options + process-js-modules)] (check-output-to opts) (check-output-dir opts) (check-source-map opts) From b11a9e6dd57ffca3d67c40c219b21055486bf202 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Mon, 15 Jun 2015 21:16:41 +0600 Subject: [PATCH 1143/4033] CLJS-1227 Raise error when if form has more than 4 statements --- src/main/clojure/cljs/analyzer.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2e821d7f3..13fe91ab1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -786,6 +786,8 @@ [op env [_ test then else :as form] name _] (when (< (count form) 3) (throw (error env "Too few arguments to if"))) + (when (> (count form) 4) + (throw (error env "Too many arguments to if"))) (let [test-expr (disallowing-recur (analyze (assoc env :context :expr) test)) then-expr (allowing-redef (analyze env then)) else-expr (allowing-redef (analyze env else))] From 62ab566c23cce7a3826d47411cb34755dfe15543 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 18:29:20 -0400 Subject: [PATCH 1144/4033] fix broken `if` in cljs.pprint caught by new check --- src/main/cljs/cljs/pprint.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 49b18a447..53ec4badb 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -2879,7 +2879,7 @@ type-map {"core$future_call" "Future", (write-out (if (and (satisfies? IPending o) (not (-realized? o))) :not-delivered - :else @o))))) + @o))))) (def ^{:private true} pprint-pqueue (formatter-out "~<<-(~;~@{~w~^ ~_~}~;)-<~:>")) From eed8ffb812f9b2ce6a26267940bc78668ff657f4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 19:29:26 -0400 Subject: [PATCH 1145/4033] introduce cljs.analyzer/*cljs-macros-ns* to track entrance into a macro ns compilation or analysis. All files related to the macro ns are themselves macro nses - i.e. a phase distinction. in cljs.compiler/compile-file this dyn var is bound to true only if we have been given a file with a .clj extension, cljs.core with a .cljc extenion, an opts with a :macros-ns key with a truth-y value, or if a the dyn var has already been bound. in all cases where we special case cljs/core.cljs for AOT reasons, disambiguate by checking the file extension. This way we don't treat attempts to bootstrap the cljs/core.cljc the macro ns as an attempt to load the standard lib. --- src/main/clojure/cljs/analyzer.cljc | 1 + src/main/clojure/cljs/compiler.cljc | 162 +++++++++++++++------------- 2 files changed, 86 insertions(+), 77 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 13fe91ab1..44f9b13ab 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -38,6 +38,7 @@ #?(:clj (set! *warn-on-reflection* true)) (def ^:dynamic *cljs-ns* 'cljs.user) +(def ^:dynamic *cljs-macros-ns* false) (def ^:dynamic *cljs-file* nil) #?(:clj (def ^:dynamic *unchecked-if* (atom false))) (def ^:dynamic *cljs-static-fns* false) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 2732a2399..f993bf778 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1053,79 +1053,86 @@ (fn [] (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "Compiling" (str src))) - (if-let [cached (and (= (:optimizations opts) :none) - (= (:ns (ana/parse-ns src)) 'cljs.core) - (io/resource "cljs/core.aot.js"))] - ;; no need to bother with analysis cache reading, handled by - ;; with-core-cljs - (do - (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Using cached cljs.core" (str src))) - (spit dest (slurp cached)) - (when (true? (:source-map opts)) - (spit (io/file (str dest ".map")) - (json/write-str - (assoc - (json/read-str (slurp (io/resource "cljs/core.aot.js.map"))) - "file" - (str (io/file (util/output-directory opts) "cljs" "core.js")))))) - (ana/parse-ns src dest nil)) - (with-open [out ^java.io.Writer (io/make-writer dest {})] - (binding [*out* out - ana/*cljs-ns* 'cljs.user - ana/*cljs-file* (.getPath ^File src) - reader/*alias-map* (or reader/*alias-map* {}) - ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) - *source-map-data* (when (:source-map opts) - (atom - {:source-map (sorted-map) - :gen-col 0 - :gen-line 0}))] - (emitln (compiled-by-string opts)) - (with-open [rdr (io/reader src)] - (loop [forms (ana/forms-seq* rdr (util/path src)) - ns-name nil - deps nil] - (if (seq forms) - (let [env (ana/empty-env) - ast (ana/analyze env (first forms) nil opts)] - (emit ast) - (if (= (:op ast) :ns) - (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) - (recur (rest forms) ns-name deps))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= (:optimizations opts) :none)) - (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" - (or (:source-map-url opts) (.getName sm-file)) - (if (true? (:source-map-timestamp opts)) - (str "?rel=" (System/currentTimeMillis)) - "")) - (spit sm-file - (sm/encode {(url-path src) (:source-map sm-data)} - {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest) - :source-map-timestamp (:source-map-timestamp opts) - :source-map-pretty-print (:source-map-pretty-print opts)})))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src (ana/parse-ns src) output-dir :write))) - ret)))))))))))))) + (let [ext (util/ext src) + {:keys [ns] :as ns-info} (ana/parse-ns src)] + (if-let [cached (and (= (:optimizations opts) :none) + (not= ext "clj") + (= ns 'cljs.core) + (io/resource "cljs/core.aot.js"))] + ;; no need to bother with analysis cache reading, handled by + ;; with-core-cljs + (do + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Using cached cljs.core" (str src))) + (spit dest (slurp cached)) + (when (true? (:source-map opts)) + (spit (io/file (str dest ".map")) + (json/write-str + (assoc + (json/read-str (slurp (io/resource "cljs/core.aot.js.map"))) + "file" + (str (io/file (util/output-directory opts) "cljs" "core.js")))))) + (ana/parse-ns src dest nil)) + (with-open [out ^java.io.Writer (io/make-writer dest {})] + (binding [*out* out + ana/*cljs-ns* 'cljs.user + ana/*cljs-macros-ns* (or (= ext "clj") + (and (= ns 'cljs.core) (= ext "cljc")) + (:macros-ns opts) + ana/*cljs-macros-ns*) + ana/*cljs-file* (.getPath ^File src) + reader/*alias-map* (or reader/*alias-map* {}) + ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) + *source-map-data* (when (:source-map opts) + (atom + {:source-map (sorted-map) + :gen-col 0 + :gen-line 0}))] + (emitln (compiled-by-string opts)) + (with-open [rdr (io/reader src)] + (loop [forms (ana/forms-seq* rdr (util/path src)) + ns-name nil + deps nil] + (if (seq forms) + (let [env (ana/empty-env) + ast (ana/analyze env (first forms) nil opts)] + (emit ast) + (if (= (:op ast) :ns) + (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) + (recur (rest forms) ns-name deps))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= (:optimizations opts) :none)) + (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] + (emits "\n//# sourceMappingURL=" + (or (:source-map-url opts) (.getName sm-file)) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + "")) + (spit sm-file + (sm/encode {(url-path src) (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 2) + :file (url-path dest) + :source-map-timestamp (:source-map-timestamp opts) + :source-map-pretty-print (:source-map-pretty-print opts)})))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src (ana/parse-ns src) output-dir :write))) + ret))))))))))))))) #?(:clj (defn requires-compilation? @@ -1172,14 +1179,15 @@ ([src dest opts] {:post [map?]} (binding [ana/*file-defs* (atom #{})] - (let [nses (get @env/*compiler* ::ana/namespaces) - src-file (io/file src) + (let [nses (get @env/*compiler* ::ana/namespaces) + src-file (io/file src) dest-file (io/file dest) - opts (merge {:optimizations :none} opts)] + opts (merge {:optimizations :none} opts)] (if (.exists src-file) (try (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) - opts (if (and (= ns 'cljs.core) + opts (if (and (not= (util/ext src) "clj") ;; skip cljs.core macro-ns + (= ns 'cljs.core) (not (false? (:static-fns opts)))) (assoc opts :static-fns true) opts)] From 3abf5267cc60777d1e89975a0104c51ddc0bcb2f Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 19:43:54 -0400 Subject: [PATCH 1146/4033] macro namespaces must be renamed --- src/main/clojure/cljs/analyzer.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 44f9b13ab..06c5c48ec 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1573,11 +1573,15 @@ {:ns ns :var (symbol (str clash-ns) (str name))}))))) +(defn macro-ns-name [name] + (symbol (str name "$macros"))) + (defmethod parse 'ns [_ env [_ name & args :as form] _ opts] (when-not (symbol? name) (throw (error env "Namespaces must be named by a symbol."))) - (let [segments (string/split (clojure.core/name name) #"\.")] + (let [name (cond-> name *cljs-macros-ns* macro-ns-name) + segments (string/split (clojure.core/name name) #"\.")] (when (= 1 (count segments)) (warning :single-segment-namespace env {:name name})) (when (some js-reserved segments) From 125f1ce6fbb7977d7f7ac30bfbd240585817075b Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 20:08:36 -0400 Subject: [PATCH 1147/4033] util/to-target-file needs to account for macro files --- src/main/clojure/cljs/util.cljc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index a98d9098c..de93b4dfb 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -74,14 +74,21 @@ ([parts sep] (apply str (interpose sep parts)))) +(declare ext) + (defn ^File to-target-file ([target-dir ns-info] (to-target-file target-dir ns-info "js")) - ([target-dir ns-info ext] - (let [relative-path (string/split (munge-path (str (:ns ns-info))) #"\.") - parents (cond-> (butlast relative-path) + ([target-dir {:keys [ns source-file] :as ns-info} ext] + (let [src-ext (cljs.util/ext source-file) + ns (if (or (= src-ext "clj") + (and (= ns 'cljs.core) (= src-ext "cljc"))) + (symbol (str ns "$macros")) + ns) + relpath (string/split (munge-path (str ns)) #"\.") + parents (cond-> (butlast relpath) target-dir (conj target-dir))] - (cond->> (io/file (str (last relative-path) (str "." ext))) + (cond->> (io/file (str (last relpath) (str "." ext))) (seq parents) (io/file (to-path parents)))))) @@ -181,4 +188,4 @@ (if (try (eval exp) (catch Throwable _ false)) `(do ~then) - `(do ~else)))) \ No newline at end of file + `(do ~else)))) From 362a5e86759869b7d01977a307a0d0d44787f1a0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 20:20:40 -0400 Subject: [PATCH 1148/4033] cjls.core macro file uses cljc extension not clj --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index f993bf778..442ad6147 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1056,7 +1056,7 @@ (let [ext (util/ext src) {:keys [ns] :as ns-info} (ana/parse-ns src)] (if-let [cached (and (= (:optimizations opts) :none) - (not= ext "clj") + (not= ext "cljc") (= ns 'cljs.core) (io/resource "cljs/core.aot.js"))] ;; no need to bother with analysis cache reading, handled by From 3b3a78acd84e91e5be63fd2d095f71f5493d7fac Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 20:23:06 -0400 Subject: [PATCH 1149/4033] bad swap! in cljs.core.macros/alias --- src/main/clojure/cljs/core/macros.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index 5982b751d..6946e81e5 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -36,5 +36,5 @@ (doall (map source-fn vars))))) (defmacro alias [[_ ns] [_ alias]] - (swap! @env/*compiler* + (swap! env/*compiler* [::namespaces (.getName *ns*) :requires] assoc alias ns)) \ No newline at end of file From 1514fbc11cfa4c470c70474a9efb0bfb0c710541 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 20:25:40 -0400 Subject: [PATCH 1150/4033] cljs.core.macros/alias should return nothing --- src/main/clojure/cljs/core/macros.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index 6946e81e5..b27f4a50f 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -36,5 +36,5 @@ (doall (map source-fn vars))))) (defmacro alias [[_ ns] [_ alias]] - (swap! env/*compiler* - [::namespaces (.getName *ns*) :requires] assoc alias ns)) \ No newline at end of file + (swap! env/*compiler* [::namespaces (.getName *ns*) :requires] assoc alias ns) + nil) \ No newline at end of file From a8a811deae726c81c99c472faeaee59f465cd69a Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 20:30:42 -0400 Subject: [PATCH 1151/4033] missing assoc-in --- src/main/clojure/cljs/core/macros.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index b27f4a50f..119111c2a 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -36,5 +36,6 @@ (doall (map source-fn vars))))) (defmacro alias [[_ ns] [_ alias]] - (swap! env/*compiler* [::namespaces (.getName *ns*) :requires] assoc alias ns) + (swap! env/*compiler* assoc-in [::namespaces (.getName *ns*) :requires] + alias ns) nil) \ No newline at end of file From f73bfa75b57838d82d00cb18bf7d6b5473d53aee Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Jun 2015 20:33:14 -0400 Subject: [PATCH 1152/4033] fix assoc-in --- src/main/clojure/cljs/core/macros.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index 119111c2a..5ffd8243d 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -36,6 +36,6 @@ (doall (map source-fn vars))))) (defmacro alias [[_ ns] [_ alias]] - (swap! env/*compiler* assoc-in [::namespaces (.getName *ns*) :requires] - alias ns) + (swap! env/*compiler* assoc-in + [::namespaces (.getName *ns*) :requires alias] ns) nil) \ No newline at end of file From 25293f834eb7db67ebe5f57404ef6d76a20c47d5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 18 Jun 2015 10:30:19 -0400 Subject: [PATCH 1153/4033] when self compiling macros file, make sure defmacro maps to cljs.core/defmacro --- src/main/clojure/cljs/core.cljc | 4 +++- src/main/clojure/cljs/core/macros.clj | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 91cc39f21..33a639232 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -42,7 +42,9 @@ cljs.compiler [cljs.env :as env])) -(alias 'core 'clojure.core) +#?(:clj (alias 'core 'clojure.core) + :cljs (alias 'core 'cljs.core)) + (alias 'ana 'cljs.analyzer) #?(:clj diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index 5ffd8243d..8aaa87954 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -31,9 +31,11 @@ (reader/read {:read-cond :allow :features #{:clj}} rdr)))))))) (defmacro import-macros [ns [& vars]] - `(do - ~@(binding [*ns* (find-ns ns)] - (doall (map source-fn vars))))) + (letfn [(->cljs-macro [[_ & rest]] + `(cljs.core/defmacro ~@rest))] + `(do + ~@(binding [*ns* (find-ns ns)] + (doall (map (comp ->cljs-macro source-fn) vars)))))) (defmacro alias [[_ ns] [_ alias]] (swap! env/*compiler* assoc-in From ac0b169cb082bd2ff4ac917274cb91628adba947 Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Thu, 18 Jun 2015 09:53:05 -0500 Subject: [PATCH 1154/4033] CLJS-1230: ES 2015 Module Processing Add support for ECMAScript 6. Include modules with the following option: :foreign-libs [{:file "libs/greeting.js" :provides ["greeting"] :module-type :es6}] --- src/main/clojure/cljs/closure.clj | 59 ++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 66972abb7..45c987d2c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -54,7 +54,8 @@ Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy JSModule JSModuleGraph SourceMap ProcessCommonJSModules - ES6ModuleLoader AbstractCompiler TransformAMDToCJSModule] + ES6ModuleLoader AbstractCompiler TransformAMDToCJSModule + ProcessEs6Modules] [com.google.javascript.rhino Node] [java.security MessageDigest] [javax.xml.bind DatatypeConverter] @@ -98,6 +99,17 @@ (def can-convert-amd? true) (def can-convert-amd? false)) +(compile-if + (and (.getConstructor ProcessEs6Modules + (into-array java.lang.Class + [com.google.javascript.jscomp.Compiler ES6ModuleLoader Boolean/TYPE])) + (.getConstructor ES6ModuleLoader + (into-array java.lang.Class + [AbstractCompiler java.lang.String]))) + (def can-convert-es6? true) + (def can-convert-es6? false)) + + ;; Closure API ;; =========== @@ -1197,6 +1209,22 @@ :else (str (random-string 5) ".js"))))) +(defn get-js-module-root [js-file] + (let [path (.getParent (io/file js-file))] + (cond->> path + (.startsWith path File/separator) (str ".") + (not (.startsWith path (str "." File/separator))) (str "." File/separator) + (not (.endsWith path File/separator)) (#(str % File/separator))))) + +(defn init-js-module-processing [js-file options] + (let [^List externs '() + ^SourceFile source-file (js-source-file js-file (slurp js-file)) + closure-compiler (doto (make-closure-compiler) + (.init externs [source-file] options)) + ^Node root (.parse closure-compiler source-file)] + {:closure-compiler closure-compiler + :root root})) + (defmulti convert-js-module "Takes a JavaScript module and rewrites it into a Google Closure-compatible form. Returns the source of the new module as a single string." @@ -1210,23 +1238,29 @@ (defmethod convert-js-module :commonjs [js] (let [js-file (:file js) path (.getParent (io/file js-file)) - module-root (cond->> path - (.startsWith path File/separator) (str ".") - (not (.startsWith path (str "." File/separator))) (str "." File/separator) - (not (.endsWith path File/separator)) (#(str % File/separator))) - ^List externs '() - ^SourceFile source-file (js-source-file js-file (slurp js-file)) + module-root (get-js-module-root js-file) ^CompilerOptions options (CompilerOptions.) - closure-compiler (doto (make-closure-compiler) - (.init externs [source-file] options)) + {:keys [closure-compiler root]} (init-js-module-processing js-file options) es6-loader (ES6ModuleLoader. closure-compiler module-root) - cjs (ProcessCommonJSModules. closure-compiler es6-loader) - ^Node root (.parse closure-compiler source-file)] + cjs (ProcessCommonJSModules. closure-compiler es6-loader)] (compile-if can-convert-amd? (.process (TransformAMDToCJSModule. closure-compiler) nil root)) (.process cjs nil root) (.toSource closure-compiler root)))) +(compile-if can-convert-es6? + (defmethod convert-js-module :es6 [js] + (let [js-file (:file js) + module-root (get-js-module-root js-file) + ^CompilerOptions options (doto (CompilerOptions.) + (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) + (.setLanguageOut CompilerOptions$LanguageMode/ECMASCRIPT5)) + {:keys [closure-compiler root]} (init-js-module-processing js-file options) + es6-loader (ES6ModuleLoader. closure-compiler module-root) + cjs (ProcessEs6Modules. closure-compiler es6-loader true)] + (.processFile cjs root) + (.toSource closure-compiler root)))) + (defmethod convert-js-module :default [js] (ana/warning :unsupported-js-module-type @env/*compiler* js) (deps/-source js)) @@ -1503,7 +1537,8 @@ (let [js-modules (filter :module-type (:foreign-libs opts))] (reduce (fn [opts {:keys [file module-type] :as lib}] (if (or (and (= module-type :commonjs) can-convert-commonjs?) - (and (= module-type :amd) can-convert-amd?)) + (and (= module-type :amd) can-convert-amd?) + (and (= module-type :es6) can-convert-es6?)) (let [module-name (ProcessCommonJSModules/toModuleName file) ijs (write-javascript opts (deps/load-foreign-library lib))] (doseq [provide (:provides ijs)] From 275104c228942e51c10ba571595ca7abcc2d735b Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 18 Jun 2015 19:47:37 -0400 Subject: [PATCH 1155/4033] remove *cljs-macros-ns* dyn var, not going to work. just use :macro-ns in opts for now. in macros file move aliases into ns form, this is okay since we're trying to produce macros for _runtime_ use. Will need to provide a synthetic clojure.core for this to work. --- src/main/clojure/cljs/analyzer.cljc | 229 ++++++++++++++-------------- src/main/clojure/cljs/compiler.cljc | 121 +++++++-------- src/main/clojure/cljs/core.cljc | 48 ++++-- 3 files changed, 207 insertions(+), 191 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 06c5c48ec..bafdcdbdf 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -38,7 +38,6 @@ #?(:clj (set! *warn-on-reflection* true)) (def ^:dynamic *cljs-ns* 'cljs.user) -(def ^:dynamic *cljs-macros-ns* false) (def ^:dynamic *cljs-file* nil) #?(:clj (def ^:dynamic *unchecked-if* (atom false))) (def ^:dynamic *cljs-static-fns* false) @@ -1580,120 +1579,120 @@ [_ env [_ name & args :as form] _ opts] (when-not (symbol? name) (throw (error env "Namespaces must be named by a symbol."))) - (let [name (cond-> name *cljs-macros-ns* macro-ns-name) - segments (string/split (clojure.core/name name) #"\.")] - (when (= 1 (count segments)) - (warning :single-segment-namespace env {:name name})) - (when (some js-reserved segments) - (warning :munged-namespace env {:name name})) - (find-def-clash env name segments) - #?(:clj - (when (some (complement util/valid-js-id-start?) segments) - (throw - (AssertionError. - (str "Namespace " name " has a segment starting with an invaild " - "JavaScript identifier")))))) - (let [docstring (if (string? (first args)) (first args)) - mdocstr (-> name meta :doc) - args (if docstring (next args) args) - metadata (if (map? (first args)) (first args)) - form-meta (meta form) - args (desugar-ns-specs (if metadata (next args) args)) - name (vary-meta name merge metadata) - excludes (parse-ns-excludes env args) - deps (atom #{}) - aliases (atom {:fns {} :macros {}}) - spec-parsers {:require (partial parse-require-spec env false deps aliases) - :require-macros (partial parse-require-spec env true deps aliases) - :use (comp (partial parse-require-spec env false deps aliases) - (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}) - reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) - reloads (atom {}) - {uses :use requires :require use-macros :use-macros require-macros :require-macros imports :import :as params} - (reduce - (fn [m [k & libs]] - (when-not (#{:use :use-macros :require :require-macros :import} k) - (throw (error env "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported"))) - (when-not (@valid-forms k) - (throw (error env (str "Only one " k " form is allowed per namespace definition")))) - (swap! valid-forms disj k) - ;; check for spec type reloads - (when-not (= :import k) - (when (some #{:reload} libs) - (swap! reload assoc k :reload)) - (when (some #{:reload-all} libs) - (swap! reload assoc k :reload-all))) - ;; check for individual ns reloads from REPL interactions - (when-let [xs (seq (filter #(-> % meta :reload) libs))] - (swap! reloads assoc k - (zipmap (map first xs) (map #(-> % meta :reload) xs)))) - (apply merge-with merge m - (map (spec-parsers k) - (remove #{:reload :reload-all} libs)))) - {} (remove (fn [[r]] (= r :refer-clojure)) args))] - (when (and *analyze-deps* (seq @deps)) - #?(:clj (analyze-deps name @deps env opts))) - (when (and *analyze-deps* (seq uses)) - (check-uses uses env)) - (set! *cljs-ns* name) - #?(:clj - (when *load-macros* - (load-core) - (doseq [nsym (vals use-macros)] - (let [k (or (:use-macros @reload) - (get-in @reloads [:use-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) - (intern-macros nsym k))) - (doseq [nsym (vals require-macros)] - (let [k (or (:require-macros @reload) - (get-in @reloads [:require-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) - (intern-macros nsym k))) - (when (seq use-macros) - (check-use-macros use-macros env)))) - (let [ns-info - {:name name - :doc (or docstring mdocstr) - :excludes excludes - :use-macros use-macros - :require-macros require-macros - :uses uses - :requires requires - :imports imports} - ns-info - (if (:merge form-meta) - ;; for merging information in via require usage in REPLs - (let [ns-info' (get-in @env/*compiler* [::namespaces name])] - (if (pos? (count ns-info')) - (let [merge-keys - [:use-macros :require-macros :uses :requires :imports]] - (merge - ns-info' - (merge-with merge - (select-keys ns-info' merge-keys) - (select-keys ns-info merge-keys)))) - ns-info)) - ns-info)] - (swap! env/*compiler* update-in [::namespaces name] merge ns-info) - (merge {:env env :op :ns :form form - :reloads @reloads} - (cond-> ns-info - (@reload :use) - (update-in [:uses] - (fn [m] (with-meta m {(@reload :use) true}))) - (@reload :require) - (update-in [:requires] - (fn [m] (with-meta m {(@reload :require) true})))))))) + (let [name (cond-> name (:macros-ns opts) macro-ns-name)] + (let [segments (string/split (clojure.core/name name) #"\.")] + (when (= 1 (count segments)) + (warning :single-segment-namespace env {:name name})) + (when (some js-reserved segments) + (warning :munged-namespace env {:name name})) + (find-def-clash env name segments) + #?(:clj + (when (some (complement util/valid-js-id-start?) segments) + (throw + (AssertionError. + (str "Namespace " name " has a segment starting with an invaild " + "JavaScript identifier")))))) + (let [docstring (if (string? (first args)) (first args)) + mdocstr (-> name meta :doc) + args (if docstring (next args) args) + metadata (if (map? (first args)) (first args)) + form-meta (meta form) + args (desugar-ns-specs (if metadata (next args) args)) + name (vary-meta name merge metadata) + excludes (parse-ns-excludes env args) + deps (atom #{}) + aliases (atom {:fns {} :macros {}}) + spec-parsers {:require (partial parse-require-spec env false deps aliases) + :require-macros (partial parse-require-spec env true deps aliases) + :use (comp (partial parse-require-spec env false deps aliases) + (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}) + reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) + reloads (atom {}) + {uses :use requires :require use-macros :use-macros require-macros :require-macros imports :import :as params} + (reduce + (fn [m [k & libs]] + (when-not (#{:use :use-macros :require :require-macros :import} k) + (throw (error env "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported"))) + (when-not (@valid-forms k) + (throw (error env (str "Only one " k " form is allowed per namespace definition")))) + (swap! valid-forms disj k) + ;; check for spec type reloads + (when-not (= :import k) + (when (some #{:reload} libs) + (swap! reload assoc k :reload)) + (when (some #{:reload-all} libs) + (swap! reload assoc k :reload-all))) + ;; check for individual ns reloads from REPL interactions + (when-let [xs (seq (filter #(-> % meta :reload) libs))] + (swap! reloads assoc k + (zipmap (map first xs) (map #(-> % meta :reload) xs)))) + (apply merge-with merge m + (map (spec-parsers k) + (remove #{:reload :reload-all} libs)))) + {} (remove (fn [[r]] (= r :refer-clojure)) args))] + (when (and *analyze-deps* (seq @deps)) + #?(:clj (analyze-deps name @deps env (dissoc opts :macros-ns)))) + (when (and *analyze-deps* (seq uses)) + (check-uses uses env)) + (set! *cljs-ns* name) + #?(:clj + (when *load-macros* + (load-core) + (doseq [nsym (vals use-macros)] + (let [k (or (:use-macros @reload) + (get-in @reloads [:use-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) + (doseq [nsym (vals require-macros)] + (let [k (or (:require-macros @reload) + (get-in @reloads [:require-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) + (when (seq use-macros) + (check-use-macros use-macros env)))) + (let [ns-info + {:name name + :doc (or docstring mdocstr) + :excludes excludes + :use-macros use-macros + :require-macros require-macros + :uses uses + :requires requires + :imports imports} + ns-info + (if (:merge form-meta) + ;; for merging information in via require usage in REPLs + (let [ns-info' (get-in @env/*compiler* [::namespaces name])] + (if (pos? (count ns-info')) + (let [merge-keys + [:use-macros :require-macros :uses :requires :imports]] + (merge + ns-info' + (merge-with merge + (select-keys ns-info' merge-keys) + (select-keys ns-info merge-keys)))) + ns-info)) + ns-info)] + (swap! env/*compiler* update-in [::namespaces name] merge ns-info) + (merge {:env env :op :ns :form form + :reloads @reloads} + (cond-> ns-info + (@reload :use) + (update-in [:uses] + (fn [m] (with-meta m {(@reload :use) true}))) + (@reload :require) + (update-in [:requires] + (fn [m] (with-meta m {(@reload :require) true}))))))))) (defn parse-type [op env [_ tsym fields pmasks body :as form]] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 442ad6147..dcaf4438b 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1073,66 +1073,67 @@ "file" (str (io/file (util/output-directory opts) "cljs" "core.js")))))) (ana/parse-ns src dest nil)) - (with-open [out ^java.io.Writer (io/make-writer dest {})] - (binding [*out* out - ana/*cljs-ns* 'cljs.user - ana/*cljs-macros-ns* (or (= ext "clj") - (and (= ns 'cljs.core) (= ext "cljc")) - (:macros-ns opts) - ana/*cljs-macros-ns*) - ana/*cljs-file* (.getPath ^File src) - reader/*alias-map* (or reader/*alias-map* {}) - ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) - *source-map-data* (when (:source-map opts) - (atom - {:source-map (sorted-map) - :gen-col 0 - :gen-line 0}))] - (emitln (compiled-by-string opts)) - (with-open [rdr (io/reader src)] - (loop [forms (ana/forms-seq* rdr (util/path src)) - ns-name nil - deps nil] - (if (seq forms) - (let [env (ana/empty-env) - ast (ana/analyze env (first forms) nil opts)] - (emit ast) - (if (= (:op ast) :ns) - (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) - (recur (rest forms) ns-name deps))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= (:optimizations opts) :none)) - (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" - (or (:source-map-url opts) (.getName sm-file)) - (if (true? (:source-map-timestamp opts)) - (str "?rel=" (System/currentTimeMillis)) - "")) - (spit sm-file - (sm/encode {(url-path src) (:source-map sm-data)} - {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest) - :source-map-timestamp (:source-map-timestamp opts) - :source-map-pretty-print (:source-map-pretty-print opts)})))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src (ana/parse-ns src) output-dir :write))) - ret))))))))))))))) + (let [opts (if (or (= ext "clj") + (and (= ns 'cljs.core) (= ext "cljc")) + (:macros-ns opts)) + (assoc opts :macros-ns true) + opts)] + (with-open [out ^java.io.Writer (io/make-writer dest {})] + (binding [*out* out + ana/*cljs-ns* 'cljs.user + ana/*cljs-file* (.getPath ^File src) + reader/*alias-map* (or reader/*alias-map* {}) + ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) + *source-map-data* (when (:source-map opts) + (atom + {:source-map (sorted-map) + :gen-col 0 + :gen-line 0}))] + (emitln (compiled-by-string opts)) + (with-open [rdr (io/reader src)] + (loop [forms (ana/forms-seq* rdr (util/path src)) + ns-name nil + deps nil] + (if (seq forms) + (let [env (ana/empty-env) + ast (ana/analyze env (first forms) nil opts)] + (emit ast) + (if (= (:op ast) :ns) + (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) + (recur (rest forms) ns-name deps))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= (:optimizations opts) :none)) + (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] + (emits "\n//# sourceMappingURL=" + (or (:source-map-url opts) (.getName sm-file)) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + "")) + (spit sm-file + (sm/encode {(url-path src) (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 2) + :file (url-path dest) + :source-map-timestamp (:source-map-timestamp opts) + :source-map-pretty-print (:source-map-pretty-print opts)})))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src (ana/parse-ns src) output-dir :write))) + ret)))))))))))))))) #?(:clj (defn requires-compilation? diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 33a639232..792386322 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -36,16 +36,15 @@ if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand #?(:cljs alias)]) - #?(:cljs (:require-macros [cljs.core.macros :refer [import-macros alias]])) (:require clojure.walk clojure.set cljs.compiler - [cljs.env :as env])) + [cljs.env :as env] + #?(:cljs [clojure.core :as core]) + #?(:cljs [cljs.analyzer :as ana]))) -#?(:clj (alias 'core 'clojure.core) - :cljs (alias 'core 'cljs.core)) - -(alias 'ana 'cljs.analyzer) +#?(:clj (alias 'core 'clojure.core)) +#?(:clj (alias 'ana 'cljs.analyzer)) #?(:clj (core/defmacro import-macros [ns [& vars]] @@ -76,16 +75,33 @@ `(do ~@defs :imported)))) -(import-macros clojure.core - [-> ->> .. assert comment cond - declare defn- - doto - extend-protocol fn for - if-let if-not letfn - memfn - when when-first when-let when-not while - cond-> cond->> as-> some-> some->> - if-some when-some]) +#?(:clj + (import-macros clojure.core + [-> ->> .. assert comment cond + declare defn- + doto + extend-protocol fn for + if-let if-not letfn + memfn + when when-first when-let when-not while + cond-> cond->> as-> some-> some->> + if-some when-some])) + +#?(:cljs + (core/defmacro -> + "Threads the expr through the forms. Inserts x as the + second item in the first form, making a list of it if it is not a + list already. If there are more forms, inserts the first form as the + second item in second form, etc." + [x & forms] + (core/loop [x x, forms forms] + (if forms + (core/let [form (first forms) + threaded (if (seq? form) + (with-meta `(~(first form) ~x ~@(next form)) (meta form)) + (list form x))] + (recur threaded (next forms))) + x)))) (defn- ^{:dynamic true} assert-valid-fdecl "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn." From b882a4546dcdc8b4c4e8037074a6e473290d25ef Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 18 Jun 2015 20:18:53 -0400 Subject: [PATCH 1156/4033] getting real meta-circular now, need to load the macros file macros in order to compile the macros file into JavaScript --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 792386322..3bd0c925a 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -36,11 +36,11 @@ if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand #?(:cljs alias)]) + #?(:cljs (:require-macros [cljs.core :as core])) (:require clojure.walk clojure.set cljs.compiler [cljs.env :as env] - #?(:cljs [clojure.core :as core]) #?(:cljs [cljs.analyzer :as ana]))) #?(:clj (alias 'core 'clojure.core)) From 0d91fa9a23a3fcfcd500a610fc4a4756c9c1e185 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Jun 2015 00:36:06 -0400 Subject: [PATCH 1157/4033] consistently namespace `let` and `loop` in the macros file --- src/main/clojure/cljs/core.cljc | 1036 +++++++++++++++---------------- 1 file changed, 518 insertions(+), 518 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3bd0c925a..f434bde6b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -275,24 +275,24 @@ the binding-forms are bound to their respective init-exprs or parts therein. Acts as a recur target." [bindings & body] - (assert-args - (vector? bindings) "a vector for its binding" - (even? (count bindings)) "an even number of forms in binding vector") - (let [db (destructure bindings)] - (if (= db bindings) - `(loop* ~bindings ~@body) - (let [vs (take-nth 2 (drop 1 bindings)) - bs (take-nth 2 bindings) - gs (map (fn [b] (if (core/symbol? b) b (gensym))) bs) - bfs (reduce (fn [ret [b v g]] - (if (core/symbol? b) - (conj ret g v) - (conj ret g v b g))) - [] (map core/vector bs vs gs))] - `(let ~bfs - (loop* ~(vec (interleave gs gs)) - (let ~(vec (interleave bs gs)) - ~@body))))))) + (assert-args + (vector? bindings) "a vector for its binding" + (even? (count bindings)) "an even number of forms in binding vector") + (core/let [db (destructure bindings)] + (if (= db bindings) + `(loop* ~bindings ~@body) + (core/let [vs (take-nth 2 (drop 1 bindings)) + bs (take-nth 2 bindings) + gs (map (fn [b] (if (core/symbol? b) b (gensym))) bs) + bfs (reduce (fn [ret [b v g]] + (if (core/symbol? b) + (conj ret g v) + (conj ret g v b g))) + [] (map core/vector bs vs gs))] + `(let ~bfs + (loop* ~(vec (interleave gs gs)) + (let ~(vec (interleave bs gs)) + ~@body))))))) (def fast-path-protocols "protocol fqn -> [partition number, bit]" @@ -313,16 +313,16 @@ (def fast-path-protocol-partitions-count "total number of partitions" - (let [c (count fast-path-protocols) - m (core/mod c 32)] + (core/let [c (count fast-path-protocols) + m (core/mod c 32)] (if (core/zero? m) (core/quot c 32) (core/inc (core/quot c 32))))) (core/defmacro str [& xs] - (let [strs (->> (repeat (count xs) "cljs.core.str(~{})") - (interpose ",") - (apply core/str))] + (core/let [strs (->> (repeat (count xs) "cljs.core.str(~{})") + (interpose ",") + (apply core/str))] (list* 'js* (core/str "[" strs "].join('')") xs))) (defn- bool-expr [e] @@ -341,15 +341,15 @@ ([] true) ([x] x) ([x & next] - (let [forms (concat [x] next)] - (if (every? #(simple-test-expr? &env %) - (map #(cljs.analyzer/analyze &env %) forms)) - (let [and-str (->> (repeat (count forms) "(~{})") - (interpose " && ") - (apply core/str))] - (bool-expr `(~'js* ~and-str ~@forms))) - `(let [and# ~x] - (if and# (and ~@next) and#)))))) + (core/let [forms (concat [x] next)] + (if (every? #(simple-test-expr? &env %) + (map #(cljs.analyzer/analyze &env %) forms)) + (core/let [and-str (->> (repeat (count forms) "(~{})") + (interpose " && ") + (apply core/str))] + (bool-expr `(~'js* ~and-str ~@forms))) + `(let [and# ~x] + (if and# (and ~@next) and#)))))) (core/defmacro or "Evaluates exprs one at a time, from left to right. If a form @@ -359,15 +359,15 @@ ([] nil) ([x] x) ([x & next] - (let [forms (concat [x] next)] - (if (every? #(simple-test-expr? &env %) - (map #(cljs.analyzer/analyze &env %) forms)) - (let [or-str (->> (repeat (count forms) "(~{})") - (interpose " || ") - (apply core/str))] - (bool-expr `(~'js* ~or-str ~@forms))) - `(let [or# ~x] - (if or# or# (or ~@next))))))) + (core/let [forms (concat [x] next)] + (if (every? #(simple-test-expr? &env %) + (map #(cljs.analyzer/analyze &env %) forms)) + (core/let [or-str (->> (repeat (count forms) "(~{})") + (interpose " || ") + (apply core/str))] + (bool-expr `(~'js* ~or-str ~@forms))) + `(let [or# ~x] + (if or# or# (or ~@next))))))) (core/defmacro nil? [x] `(coercive-= ~x nil)) @@ -455,18 +455,18 @@ (core/defmacro aget ([a i] - (core/list 'js* "(~{}[~{}])" a i)) + (core/list 'js* "(~{}[~{}])" a i)) ([a i & idxs] - (let [astr (apply core/str (repeat (count idxs) "[~{}]"))] - `(~'js* ~(core/str "(~{}[~{}]" astr ")") ~a ~i ~@idxs)))) + (core/let [astr (apply core/str (repeat (count idxs) "[~{}]"))] + `(~'js* ~(core/str "(~{}[~{}]" astr ")") ~a ~i ~@idxs)))) (core/defmacro aset ([a i v] - (core/list 'js* "(~{}[~{}] = ~{})" a i v)) + (core/list 'js* "(~{}[~{}] = ~{})" a i v)) ([a idx idx2 & idxv] - (let [n (core/dec (count idxv)) - astr (apply core/str (repeat n "[~{}]"))] - `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~a ~idx ~idx2 ~@idxv)))) + (core/let [n (core/dec (count idxv)) + astr (apply core/str (repeat n "[~{}]"))] + `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~a ~idx ~idx2 ~@idxv)))) (core/defmacro ^::ana/numeric + ([] 0) @@ -676,7 +676,7 @@ (defn- do-curried [name doc meta args body] - (let [cargs (vec (butlast args))] + (core/let [cargs (vec (butlast args))] `(defn ~name ~doc ~meta (~cargs (fn [x#] (~name ~@cargs x#))) (~args ~@body)))) @@ -765,12 +765,12 @@ (meta ^{:k :v} (reify Object (toString [this] \"foo\"))) == {:k :v}" [& impls] - (let [t (with-meta (gensym "t") {:anonymous true}) - meta-sym (gensym "meta") - this-sym (gensym "_") - locals (keys (:locals &env)) - ns (-> &env :ns :name) - munge cljs.compiler/munge] + (core/let [t (with-meta (gensym "t") {:anonymous true}) + meta-sym (gensym "meta") + this-sym (gensym "_") + locals (keys (:locals &env)) + ns (-> &env :ns :name) + munge cljs.compiler/munge] `(do (when-not (exists? ~(symbol (core/str ns) (core/str t))) (deftype ~t [~@locals ~meta-sym] @@ -785,7 +785,7 @@ (core/defmacro specify! "Identical to reify but mutates its first argument." [expr & impls] - (let [x (with-meta (gensym "x") {:extend :instance})] + (core/let [x (with-meta (gensym "x") {:extend :instance})] `(let [~x ~expr] (extend-type ~x ~@impls) ~x))) @@ -828,14 +828,14 @@ (cljs.analyzer/warning :undeclared-protocol-symbol env {:protocol p}))))) (defn- resolve-var [env sym] - (let [ret (-> (dissoc env :locals) - (cljs.analyzer/resolve-var sym) - :name)] + (core/let [ret (-> (dissoc env :locals) + (cljs.analyzer/resolve-var sym) + :name)] (assert ret (core/str "Can't resolve: " sym)) ret)) (defn- ->impl-map [impls] - (loop [ret {} s impls] + (core/loop [ret {} s impls] (if (seq s) (recur (assoc ret (first s) (take-while seq? (next s))) (drop-while seq? (next s))) @@ -843,9 +843,9 @@ (defn- base-assign-impls [env resolve tsym type [p sigs]] (warn-and-update-protocol p tsym env) - (let [psym (resolve p) - pfn-prefix (subs (core/str psym) 0 - (clojure.core/inc (.indexOf (core/str psym) "/")))] + (core/let [psym (resolve p) + pfn-prefix (subs (core/str psym) 0 + (clojure.core/inc (.indexOf (core/str psym) "/")))] (cons `(aset ~psym ~type true) (map (fn [[f & meths :as form]] `(aset ~(symbol (core/str pfn-prefix f)) @@ -865,7 +865,7 @@ (list* 'this-as (vary-meta this assoc :tag type) body))) (defn- adapt-ifn-params [type [[this & args :as sig] & body]] - (let [self-sym (with-meta 'self__ {:tag type})] + (core/let [self-sym (with-meta 'self__ {:tag type})] `(~(vec (cons self-sym args)) (this-as ~self-sym (let [~this ~self-sym] @@ -884,25 +884,25 @@ (defn- add-obj-methods [type type-sym sigs] (map (fn [[f & meths :as form]] - (let [[f meths] (if (vector? (first meths)) - [f [(rest form)]] - [f meths])] + (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))))) + ~(with-meta `(fn ~@(map #(adapt-obj-params type %) meths)) (meta form))))) sigs)) (defn- ifn-invoke-methods [type type-sym [f & meths :as form]] (map (fn [meth] - (let [arity (count (first meth))] + (core/let [arity (count (first meth))] `(set! ~(extend-prefix type-sym (symbol (core/str "cljs$core$IFn$_invoke$arity$" arity))) ~(with-meta `(fn ~meth) (meta form))))) (map #(adapt-ifn-invoke-params type %) meths))) (defn- add-ifn-methods [type type-sym [f & meths :as form]] - (let [meths (map #(adapt-ifn-params type %) meths) - this-sym (with-meta 'self__ {:tag type}) - argsym (gensym "args")] + (core/let [meths (map #(adapt-ifn-params type %) meths) + this-sym (with-meta 'self__ {:tag type}) + argsym (gensym "args")] (concat [`(set! ~(extend-prefix type-sym 'call) ~(with-meta `(fn ~@meths) (meta form))) `(set! ~(extend-prefix type-sym 'apply) @@ -915,10 +915,10 @@ (ifn-invoke-methods type type-sym form)))) (defn- add-proto-methods* [pprefix type type-sym [f & meths :as form]] - (let [pf (core/str pprefix f)] + (core/let [pf (core/str pprefix f)] (if (vector? (first meths)) ;; single method case - (let [meth meths] + (core/let [meth meths] [`(set! ~(extend-prefix type-sym (core/str pf "$arity$" (count (first meth)))) ~(with-meta `(fn ~@(adapt-proto-params type meth)) (meta form)))]) (map (fn [[sig & body :as meth]] @@ -928,9 +928,9 @@ (defn- proto-assign-impls [env resolve type-sym type [p sigs]] (warn-and-update-protocol p type env) - (let [psym (resolve p) - pprefix (protocol-prefix psym) - skip-flag (set (-> type-sym meta :skip-protocol-flag))] + (core/let [psym (resolve p) + pprefix (protocol-prefix psym) + skip-flag (set (-> type-sym meta :skip-protocol-flag))] (if (= p 'Object) (add-obj-methods type type-sym sigs) (concat @@ -945,18 +945,18 @@ (defn- validate-impl-sigs [env p method] (when-not (= p 'Object) - (let [var (ana/resolve-var (dissoc env :locals) p) - minfo (-> var :protocol-info :methods) - [fname sigs] (if (core/vector? (second method)) - [(first method) [(second method)]] - [(first method) (map first (rest method))]) - decmeths (core/get minfo fname ::not-found)] + (core/let [var (ana/resolve-var (dissoc env :locals) p) + minfo (-> var :protocol-info :methods) + [fname sigs] (if (core/vector? (second method)) + [(first method) [(second method)]] + [(first method) (map first (rest method))]) + decmeths (core/get minfo fname ::not-found)] (when (= decmeths ::not-found) (ana/warning :protocol-invalid-method env {:protocol p :fname fname :no-such-method true})) - (loop [sigs sigs seen #{}] + (core/loop [sigs sigs seen #{}] (when (seq sigs) - (let [sig (first sigs) - c (count sig)] + (core/let [sig (first sigs) + c (count sig)] (when (contains? seen c) (ana/warning :protocol-duped-method env {:protocol p :fname fname})) (when (core/and (not= decmeths ::not-found) (not (some #{c} (map count decmeths)))) @@ -964,16 +964,16 @@ (recur (next sigs) (conj seen c)))))))) (defn- validate-impls [env impls] - (loop [protos #{} impls impls] + (core/loop [protos #{} impls impls] (when (seq impls) - (let [proto (first impls) - methods (take-while seq? (next impls)) - impls (drop-while seq? (next impls))] + (core/let [proto (first impls) + methods (take-while seq? (next impls)) + impls (drop-while seq? (next impls))] (when (contains? protos proto) (ana/warning :protocol-multiple-impls env {:protocol proto})) - (loop [seen #{} methods methods] + (core/loop [seen #{} methods methods] (when (seq methods) - (let [[fname :as method] (first methods)] + (core/let [[fname :as method] (first methods)] (when (contains? seen fname) (ana/warning :extend-type-invalid-method-shape env {:protocol proto :method fname})) @@ -993,37 +993,37 @@ (bar [x y] ...) (baz ([x] ...) ([x y & zs] ...))" [type-sym & impls] - (let [env &env - _ (validate-impls env impls) - resolve (partial resolve-var env) - impl-map (->impl-map impls) - [type assign-impls] (if-let [type (base-type type-sym)] - [type base-assign-impls] - [(resolve type-sym) proto-assign-impls])] + (core/let [env &env + _ (validate-impls env impls) + resolve (partial resolve-var env) + impl-map (->impl-map impls) + [type assign-impls] (if-let [type (base-type type-sym)] + [type base-assign-impls] + [(resolve type-sym) proto-assign-impls])] (when (core/and (:extending-base-js-type cljs.analyzer/*cljs-warnings*) - (js-base-type type-sym)) + (js-base-type type-sym)) (cljs.analyzer/warning :extending-base-js-type env - {:current-symbol type-sym :suggested-symbol (js-base-type type-sym)})) + {:current-symbol type-sym :suggested-symbol (js-base-type type-sym)})) `(do ~@(mapcat #(assign-impls env resolve type-sym type %) impl-map)))) (defn- prepare-protocol-masks [env impls] - (let [resolve (partial resolve-var env) - impl-map (->impl-map impls) - fpp-pbs (seq - (keep fast-path-protocols - (map resolve - (keys impl-map))))] + (core/let [resolve (partial resolve-var env) + impl-map (->impl-map impls) + fpp-pbs (seq + (keep fast-path-protocols + (map resolve + (keys impl-map))))] (if fpp-pbs - (let [fpps (into #{} - (filter (partial contains? fast-path-protocols) - (map resolve (keys impl-map)))) - parts (as-> (group-by first fpp-pbs) parts - (into {} - (map (juxt key (comp (partial map peek) val)) - parts)) - (into {} - (map (juxt key (comp (partial reduce core/bit-or) val)) - parts)))] + (core/let [fpps (into #{} + (filter (partial contains? fast-path-protocols) + (map resolve (keys impl-map)))) + parts (as-> (group-by first fpp-pbs) parts + (into {} + (map (juxt key (comp (partial map peek) val)) + parts)) + (into {} + (map (juxt key (comp (partial reduce core/bit-or) val)) + parts)))] [fpps (reduce (fn [ps p] (update-in ps [p] (fnil identity 0))) parts (range fast-path-protocol-partitions-count))])))) @@ -1035,20 +1035,20 @@ (core/defn dt->et ([type specs fields] - (dt->et type specs fields false)) + (dt->et type specs fields false)) ([type specs fields inline] - (let [annots {:cljs.analyzer/type type - :cljs.analyzer/protocol-impl true - :cljs.analyzer/protocol-inline inline}] - (loop [ret [] specs specs] - (if (seq specs) - (let [p (first specs) - ret (-> (conj ret p) - (into (reduce (partial annotate-specs annots) [] - (group-by first (take-while seq? (next specs)))))) - specs (drop-while seq? (next specs))] - (recur ret specs)) - ret))))) + (core/let [annots {:cljs.analyzer/type type + :cljs.analyzer/protocol-impl true + :cljs.analyzer/protocol-inline inline}] + (core/loop [ret [] specs specs] + (if (seq specs) + (core/let [p (first specs) + ret (-> (conj ret p) + (into (reduce (partial annotate-specs annots) [] + (group-by first (take-while seq? (next specs)))))) + specs (drop-while seq? (next specs))] + (recur ret specs)) + ret))))) (defn- collect-protocols [impls env] (->> impls @@ -1123,13 +1123,13 @@ will be defined, taking positional parameters for the fields" [t fields & impls] (validate-fields "deftype" t fields) - (let [env &env - r (:name (cljs.analyzer/resolve-var (dissoc env :locals) t)) - [fpps pmasks] (prepare-protocol-masks env impls) - protocols (collect-protocols impls env) - t (vary-meta t assoc - :protocols protocols - :skip-protocol-flag fpps) ] + (core/let [env &env + r (:name (cljs.analyzer/resolve-var (dissoc env :locals) t)) + [fpps pmasks] (prepare-protocol-masks env impls) + protocols (collect-protocols impls env) + t (vary-meta t assoc + :protocols protocols + :skip-protocol-flag fpps) ] `(do (deftype* ~t ~fields ~pmasks ~(if (seq impls) @@ -1145,87 +1145,87 @@ (defn- emit-defrecord "Do not use this directly - use defrecord" [env tagname rname fields impls] - (let [hinted-fields fields - fields (vec (map #(with-meta % nil) fields)) - base-fields fields - pr-open (core/str "#" (.getNamespace rname) "." (.getName rname) "{") - fields (conj fields '__meta '__extmap (with-meta '__hash {:mutable true}))] - (let [gs (gensym) - ksym (gensym "k") - impls (concat - impls - ['IRecord - 'ICloneable - `(~'-clone [this#] (new ~tagname ~@fields)) - 'IHash - `(~'-hash [this#] (caching-hash this# ~'hash-imap ~'__hash)) - 'IEquiv - `(~'-equiv [this# other#] - (if (and other# - (identical? (.-constructor this#) - (.-constructor other#)) - (equiv-map this# other#)) - true - false)) - 'IMeta - `(~'-meta [this#] ~'__meta) - 'IWithMeta - `(~'-with-meta [this# ~gs] (new ~tagname ~@(replace {'__meta gs} fields))) - 'ILookup - `(~'-lookup [this# k#] (-lookup this# k# nil)) - `(~'-lookup [this# ~ksym else#] - (case ~ksym - ~@(mapcat (fn [f] [(keyword f) f]) base-fields) - (get ~'__extmap ~ksym else#))) - 'ICounted - `(~'-count [this#] (+ ~(count base-fields) (count ~'__extmap))) - 'ICollection - `(~'-conj [this# entry#] - (if (vector? entry#) - (-assoc this# (-nth entry# 0) (-nth entry# 1)) - (reduce -conj - this# - entry#))) - 'IAssociative - `(~'-assoc [this# k# ~gs] - (condp keyword-identical? k# - ~@(mapcat (fn [fld] - [(keyword fld) (list* `new tagname (replace {fld gs '__hash nil} fields))]) - base-fields) - (new ~tagname ~@(remove #{'__extmap '__hash} fields) (assoc ~'__extmap k# ~gs) nil))) - 'IMap - `(~'-dissoc [this# k#] (if (contains? #{~@(map keyword base-fields)} k#) - (dissoc (with-meta (into {} this#) ~'__meta) k#) - (new ~tagname ~@(remove #{'__extmap '__hash} fields) - (not-empty (dissoc ~'__extmap k#)) - nil))) - 'ISeqable - `(~'-seq [this#] (seq (concat [~@(map #(core/list `vector (keyword %) %) base-fields)] + (core/let [hinted-fields fields + fields (vec (map #(with-meta % nil) fields)) + base-fields fields + pr-open (core/str "#" (.getNamespace rname) "." (.getName rname) "{") + fields (conj fields '__meta '__extmap (with-meta '__hash {:mutable true}))] + (core/let [gs (gensym) + ksym (gensym "k") + impls (concat + impls + ['IRecord + 'ICloneable + `(~'-clone [this#] (new ~tagname ~@fields)) + 'IHash + `(~'-hash [this#] (caching-hash this# ~'hash-imap ~'__hash)) + 'IEquiv + `(~'-equiv [this# other#] + (if (and other# + (identical? (.-constructor this#) + (.-constructor other#)) + (equiv-map this# other#)) + true + false)) + 'IMeta + `(~'-meta [this#] ~'__meta) + 'IWithMeta + `(~'-with-meta [this# ~gs] (new ~tagname ~@(replace {'__meta gs} fields))) + 'ILookup + `(~'-lookup [this# k#] (-lookup this# k# nil)) + `(~'-lookup [this# ~ksym else#] + (case ~ksym + ~@(mapcat (fn [f] [(keyword f) f]) base-fields) + (get ~'__extmap ~ksym else#))) + 'ICounted + `(~'-count [this#] (+ ~(count base-fields) (count ~'__extmap))) + 'ICollection + `(~'-conj [this# entry#] + (if (vector? entry#) + (-assoc this# (-nth entry# 0) (-nth entry# 1)) + (reduce -conj + this# + entry#))) + 'IAssociative + `(~'-assoc [this# k# ~gs] + (condp keyword-identical? k# + ~@(mapcat (fn [fld] + [(keyword fld) (list* `new tagname (replace {fld gs '__hash nil} fields))]) + base-fields) + (new ~tagname ~@(remove #{'__extmap '__hash} fields) (assoc ~'__extmap k# ~gs) nil))) + 'IMap + `(~'-dissoc [this# k#] (if (contains? #{~@(map keyword base-fields)} k#) + (dissoc (with-meta (into {} this#) ~'__meta) k#) + (new ~tagname ~@(remove #{'__extmap '__hash} fields) + (not-empty (dissoc ~'__extmap k#)) + nil))) + 'ISeqable + `(~'-seq [this#] (seq (concat [~@(map #(core/list `vector (keyword %) %) base-fields)] ~'__extmap))) - 'IPrintWithWriter - `(~'-pr-writer [this# writer# opts#] - (let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# pr-writer "" " " "" opts# keyval#))] - (pr-sequential-writer - writer# pr-pair# ~pr-open ", " "}" opts# - (concat [~@(map #(core/list `vector (keyword %) %) base-fields)] - ~'__extmap)))) - ]) - [fpps pmasks] (prepare-protocol-masks env impls) - protocols (collect-protocols impls env) - tagname (vary-meta tagname assoc - :protocols protocols - :skip-protocol-flag fpps)] + 'IPrintWithWriter + `(~'-pr-writer [this# writer# opts#] + (let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# pr-writer "" " " "" opts# keyval#))] + (pr-sequential-writer + writer# pr-pair# ~pr-open ", " "}" opts# + (concat [~@(map #(core/list `vector (keyword %) %) base-fields)] + ~'__extmap)))) + ]) + [fpps pmasks] (prepare-protocol-masks env impls) + protocols (collect-protocols impls env) + tagname (vary-meta tagname assoc + :protocols protocols + :skip-protocol-flag fpps)] `(do (~'defrecord* ~tagname ~hinted-fields ~pmasks (extend-type ~tagname ~@(dt->et tagname impls fields true))))))) (defn- build-map-factory [rsym rname fields] - (let [fn-name (with-meta (symbol (core/str 'map-> rsym)) - (assoc (meta rsym) :factory :map)) - ms (gensym) - ks (map keyword fields) - getters (map (fn [k] `(~k ~ms)) ks)] + (core/let [fn-name (with-meta (symbol (core/str 'map-> rsym)) + (assoc (meta rsym) :factory :map)) + ms (gensym) + ks (map keyword fields) + getters (map (fn [k] `(~k ~ms)) ks)] `(defn ~fn-name [~ms] (new ~rname ~@getters nil (dissoc ~ms ~@ks) nil)))) @@ -1284,10 +1284,10 @@ and map->TypeName, taking a map of keywords to field values." [rsym fields & impls] (validate-fields "defrecord" rsym fields) - (let [rsym (vary-meta rsym assoc :internal-ctor true) - r (vary-meta - (:name (cljs.analyzer/resolve-var (dissoc &env :locals) rsym)) - assoc :internal-ctor true)] + (core/let [rsym (vary-meta rsym assoc :internal-ctor true) + r (vary-meta + (:name (cljs.analyzer/resolve-var (dissoc &env :locals) rsym)) + assoc :internal-ctor true)] `(let [] ~(emit-defrecord &env rsym r fields impls) (set! (.-getBasis ~r) (fn [] '[~@fields])) @@ -1339,60 +1339,60 @@ (bar-me [this y] x)))) => 17" [psym & doc+methods] - (let [p (:name (cljs.analyzer/resolve-var (dissoc &env :locals) psym)) - [doc methods] (if (core/string? (first doc+methods)) - [(first doc+methods) (next doc+methods)] - [nil doc+methods]) - psym (vary-meta psym assoc - :doc doc - :protocol-symbol true) - ns-name (-> &env :ns :name) - fqn (fn [n] (symbol (core/str ns-name "." n))) - prefix (protocol-prefix p) - _ (core/doseq [[mname & arities] methods] - (when (some #{0} (map count (filter vector? arities))) - (throw - #?(:clj (Exception. - (core/str "Invalid protocol, " psym - " defines method " mname " with arity 0")) - :cljs (js/Error. - (core/str "Invalid protocol, " psym - " defines method " mname " with arity 0")))))) - expand-sig (fn [fname slot sig] - `(~sig - (if (and ~(first sig) (. ~(first sig) ~(symbol (core/str "-" slot)))) ;; Property access needed here. - (. ~(first sig) ~slot ~@sig) - (let [x# (if (nil? ~(first sig)) nil ~(first sig))] - ((or - (aget ~(fqn fname) (goog/typeOf x#)) - (aget ~(fqn fname) "_") - (throw (missing-protocol - ~(core/str psym "." fname) ~(first sig)))) - ~@sig))))) - psym (vary-meta psym assoc-in [:protocol-info :methods] - (into {} - (map - (fn [[fname & sigs]] - (let [doc (as-> (last sigs) doc - (when (core/string? doc) doc)) - sigs (take-while vector? sigs)] - [(vary-meta fname assoc :doc doc) - (vec sigs)])) - methods))) - method (fn [[fname & sigs]] - (let [doc (as-> (last sigs) doc - (when (core/string? doc) doc)) - sigs (take-while vector? sigs) - slot (symbol (core/str prefix (name fname))) - fname (vary-meta fname assoc - :protocol p - :doc doc)] - `(defn ~fname - ~@(map (fn [sig] - (expand-sig fname - (symbol (core/str slot "$arity$" (count sig))) - sig)) - sigs))))] + (core/let [p (:name (cljs.analyzer/resolve-var (dissoc &env :locals) psym)) + [doc methods] (if (core/string? (first doc+methods)) + [(first doc+methods) (next doc+methods)] + [nil doc+methods]) + psym (vary-meta psym assoc + :doc doc + :protocol-symbol true) + ns-name (-> &env :ns :name) + fqn (fn [n] (symbol (core/str ns-name "." n))) + prefix (protocol-prefix p) + _ (core/doseq [[mname & arities] methods] + (when (some #{0} (map count (filter vector? arities))) + (throw + #?(:clj (Exception. + (core/str "Invalid protocol, " psym + " defines method " mname " with arity 0")) + :cljs (js/Error. + (core/str "Invalid protocol, " psym + " defines method " mname " with arity 0")))))) + expand-sig (fn [fname slot sig] + `(~sig + (if (and ~(first sig) (. ~(first sig) ~(symbol (core/str "-" slot)))) ;; Property access needed here. + (. ~(first sig) ~slot ~@sig) + (let [x# (if (nil? ~(first sig)) nil ~(first sig))] + ((or + (aget ~(fqn fname) (goog/typeOf x#)) + (aget ~(fqn fname) "_") + (throw (missing-protocol + ~(core/str psym "." fname) ~(first sig)))) + ~@sig))))) + psym (vary-meta psym assoc-in [:protocol-info :methods] + (into {} + (map + (fn [[fname & sigs]] + (core/let [doc (as-> (last sigs) doc + (when (core/string? doc) doc)) + sigs (take-while vector? sigs)] + [(vary-meta fname assoc :doc doc) + (vec sigs)])) + methods))) + method (fn [[fname & sigs]] + (core/let [doc (as-> (last sigs) doc + (when (core/string? doc) doc)) + sigs (take-while vector? sigs) + slot (symbol (core/str prefix (name fname))) + fname (vary-meta fname assoc + :protocol p + :doc doc)] + `(defn ~fname + ~@(map (fn [sig] + (expand-sig fname + (symbol (core/str slot "$arity$" (count sig))) + sig)) + sigs))))] `(do (set! ~'*unchecked-if* true) (def ~psym (js-obj)) @@ -1402,14 +1402,14 @@ (core/defmacro implements? "EXPERIMENTAL" [psym x] - (let [p (:name - (cljs.analyzer/resolve-var - (dissoc &env :locals) psym)) - prefix (protocol-prefix p) - xsym (bool-expr (gensym)) - [part bit] (fast-path-protocols p) - msym (symbol - (core/str "-cljs$lang$protocol_mask$partition" part "$"))] + (core/let [p (:name + (cljs.analyzer/resolve-var + (dissoc &env :locals) psym)) + prefix (protocol-prefix p) + xsym (bool-expr (gensym)) + [part bit] (fast-path-protocols p) + msym (symbol + (core/str "-cljs$lang$protocol_mask$partition" part "$"))] `(let [~xsym ~x] (if ~xsym (let [bit# ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit))] @@ -1422,14 +1422,14 @@ (core/defmacro satisfies? "Returns true if x satisfies the protocol" [psym x] - (let [p (:name - (cljs.analyzer/resolve-var - (dissoc &env :locals) psym)) - prefix (protocol-prefix p) - xsym (bool-expr (gensym)) - [part bit] (fast-path-protocols p) - msym (symbol - (core/str "-cljs$lang$protocol_mask$partition" part "$"))] + (core/let [p (:name + (cljs.analyzer/resolve-var + (dissoc &env :locals) psym)) + prefix (protocol-prefix p) + xsym (bool-expr (gensym)) + [part bit] (fast-path-protocols p) + msym (symbol + (core/str "-cljs$lang$protocol_mask$partition" part "$"))] `(let [~xsym ~x] (if ~xsym (let [bit# ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit))] @@ -1466,18 +1466,18 @@ executed, the root values of all the vars will be set back to their old values. Useful for mocking out functions during testing." [bindings & body] - (let [names (take-nth 2 bindings) - vals (take-nth 2 (drop 1 bindings)) - tempnames (map (comp gensym name) names) - binds (map core/vector names vals) - resets (reverse (map core/vector names tempnames)) - bind-value (fn [[k v]] (core/list 'set! k v))] + (core/let [names (take-nth 2 bindings) + vals (take-nth 2 (drop 1 bindings)) + tempnames (map (comp gensym name) names) + binds (map core/vector names vals) + resets (reverse (map core/vector names tempnames)) + bind-value (fn [[k v]] (core/list 'set! k v))] `(let [~@(interleave tempnames names)] ~@(map bind-value binds) (try ~@body - (finally - ~@(map bind-value resets)))))) + (finally + ~@(map bind-value resets)))))) (core/defmacro binding "binding => var-symbol init-expr @@ -1488,7 +1488,7 @@ are made in parallel (unlike let); all init-exprs are evaluated before the vars are bound to their new values." [bindings & body] - (let [names (take-nth 2 bindings)] + (core/let [names (take-nth 2 bindings)] (cljs.analyzer/confirm-bindings &env names) `(with-redefs ~bindings ~@body))) @@ -1514,22 +1514,22 @@ {:added "1.0"} [pred expr & clauses] - (let [gpred (gensym "pred__") - gexpr (gensym "expr__") - emit (fn emit [pred expr args] - (let [[[a b c :as clause] more] - (split-at (if (= :>> (second args)) 3 2) args) - n (count clause)] - (core/cond - (= 0 n) `(throw (js/Error. (core/str "No matching clause: " ~expr))) - (= 1 n) a - (= 2 n) `(if (~pred ~a ~expr) - ~b - ~(emit pred expr more)) - :else `(if-let [p# (~pred ~a ~expr)] - (~c p#) - ~(emit pred expr more))))) - gres (gensym "res__")] + (core/let [gpred (gensym "pred__") + gexpr (gensym "expr__") + emit (fn emit [pred expr args] + (let [[[a b c :as clause] more] + (split-at (if (= :>> (second args)) 3 2) args) + n (count clause)] + (core/cond + (= 0 n) `(throw (js/Error. (core/str "No matching clause: " ~expr))) + (= 1 n) a + (= 2 n) `(if (~pred ~a ~expr) + ~b + ~(emit pred expr more)) + :else `(if-let [p# (~pred ~a ~expr)] + (~c p#) + ~(emit pred expr more))))) + gres (gensym "res__")] `(let [~gpred ~pred ~gexpr ~expr] ~(emit gpred gexpr clauses)))) @@ -1612,11 +1612,11 @@ `(let [~esym ~e] (case* ~esym ~tests ~thens ~default))) (every? core/keyword? tests) - (let [tests (->> tests - (map #(.substring (core/str %) 1)) - vec - (mapv #(if (seq? %) (vec %) [%]))) - thens (vec (vals pairs))] + (core/let [tests (->> tests + (map #(.substring (core/str %) 1)) + vec + (mapv #(if (seq? %) (vec %) [%]))) + thens (vec (vals pairs))] `(let [~esym (if (keyword? ~e) (.-fqn ~e) nil)] (case* ~esym ~tests ~thens ~default))) @@ -1653,78 +1653,78 @@ (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))" [seq-exprs body-expr] (assert-args for - (vector? seq-exprs) "a vector for its binding" - (even? (count seq-exprs)) "an even number of forms in binding vector") - (let [to-groups (fn [seq-exprs] - (reduce (fn [groups [k v]] - (if (core/keyword? k) - (conj (pop groups) (conj (peek groups) [k v])) - (conj groups [k v]))) - [] (partition 2 seq-exprs))) - err (fn [& msg] (throw (ex-info (apply core/str msg) {}))) - emit-bind (fn emit-bind [[[bind expr & mod-pairs] - & [[_ next-expr] :as next-groups]]] - (let [giter (gensym "iter__") - gxs (gensym "s__") - do-mod (fn do-mod [[[k v :as pair] & etc]] - (core/cond - (= k :let) `(let ~v ~(do-mod etc)) - (= k :while) `(when ~v ~(do-mod etc)) - (= k :when) `(if ~v - ~(do-mod etc) - (recur (rest ~gxs))) - (core/keyword? k) (err "Invalid 'for' keyword " k) - next-groups - `(let [iterys# ~(emit-bind next-groups) - fs# (seq (iterys# ~next-expr))] - (if fs# - (concat fs# (~giter (rest ~gxs))) - (recur (rest ~gxs)))) - :else `(cons ~body-expr - (~giter (rest ~gxs)))))] - (if next-groups - #_ "not the inner-most loop" - `(fn ~giter [~gxs] - (lazy-seq - (loop [~gxs ~gxs] - (when-first [~bind ~gxs] - ~(do-mod mod-pairs))))) - #_"inner-most loop" - (let [gi (gensym "i__") - gb (gensym "b__") - do-cmod (fn do-cmod [[[k v :as pair] & etc]] - (core/cond - (= k :let) `(let ~v ~(do-cmod etc)) - (= k :while) `(when ~v ~(do-cmod etc)) - (= k :when) `(if ~v - ~(do-cmod etc) - (recur - (unchecked-inc ~gi))) - (core/keyword? k) - (err "Invalid 'for' keyword " k) - :else - `(do (chunk-append ~gb ~body-expr) - (recur (unchecked-inc ~gi)))))] - `(fn ~giter [~gxs] - (lazy-seq - (loop [~gxs ~gxs] - (when-let [~gxs (seq ~gxs)] - (if (chunked-seq? ~gxs) - (let [c# ^not-native (chunk-first ~gxs) - size# (count c#) - ~gb (chunk-buffer size#)] - (if (coercive-boolean - (loop [~gi 0] - (if (< ~gi size#) - (let [~bind (-nth c# ~gi)] - ~(do-cmod mod-pairs)) - true))) - (chunk-cons - (chunk ~gb) - (~giter (chunk-rest ~gxs))) - (chunk-cons (chunk ~gb) nil))) - (let [~bind (first ~gxs)] - ~(do-mod mod-pairs)))))))))))] + (vector? seq-exprs) "a vector for its binding" + (even? (count seq-exprs)) "an even number of forms in binding vector") + (core/let [to-groups (fn [seq-exprs] + (reduce (fn [groups [k v]] + (if (core/keyword? k) + (conj (pop groups) (conj (peek groups) [k v])) + (conj groups [k v]))) + [] (partition 2 seq-exprs))) + err (fn [& msg] (throw (ex-info (apply core/str msg) {}))) + emit-bind (fn emit-bind [[[bind expr & mod-pairs] + & [[_ next-expr] :as next-groups]]] + (core/let [giter (gensym "iter__") + gxs (gensym "s__") + do-mod (fn do-mod [[[k v :as pair] & etc]] + (core/cond + (= k :let) `(let ~v ~(do-mod etc)) + (= k :while) `(when ~v ~(do-mod etc)) + (= k :when) `(if ~v + ~(do-mod etc) + (recur (rest ~gxs))) + (core/keyword? k) (err "Invalid 'for' keyword " k) + next-groups + `(let [iterys# ~(emit-bind next-groups) + fs# (seq (iterys# ~next-expr))] + (if fs# + (concat fs# (~giter (rest ~gxs))) + (recur (rest ~gxs)))) + :else `(cons ~body-expr + (~giter (rest ~gxs)))))] + (if next-groups + #_ "not the inner-most loop" + `(fn ~giter [~gxs] + (lazy-seq + (loop [~gxs ~gxs] + (when-first [~bind ~gxs] + ~(do-mod mod-pairs))))) + #_"inner-most loop" + (core/let [gi (gensym "i__") + gb (gensym "b__") + do-cmod (fn do-cmod [[[k v :as pair] & etc]] + (core/cond + (= k :let) `(let ~v ~(do-cmod etc)) + (= k :while) `(when ~v ~(do-cmod etc)) + (= k :when) `(if ~v + ~(do-cmod etc) + (recur + (unchecked-inc ~gi))) + (core/keyword? k) + (err "Invalid 'for' keyword " k) + :else + `(do (chunk-append ~gb ~body-expr) + (recur (unchecked-inc ~gi)))))] + `(fn ~giter [~gxs] + (lazy-seq + (loop [~gxs ~gxs] + (when-let [~gxs (seq ~gxs)] + (if (chunked-seq? ~gxs) + (let [c# ^not-native (chunk-first ~gxs) + size# (count c#) + ~gb (chunk-buffer size#)] + (if (coercive-boolean + (loop [~gi 0] + (if (< ~gi size#) + (let [~bind (-nth c# ~gi)] + ~(do-cmod mod-pairs)) + true))) + (chunk-cons + (chunk ~gb) + (~giter (chunk-rest ~gxs))) + (chunk-cons (chunk ~gb) nil))) + (let [~bind (first ~gxs)] + ~(do-mod mod-pairs)))))))))))] `(let [iter# ~(emit-bind (to-groups seq-exprs))] (iter# ~(second seq-exprs))))) @@ -1734,61 +1734,61 @@ the head of the sequence. Returns nil." [seq-exprs & body] (assert-args doseq - (vector? seq-exprs) "a vector for its binding" - (even? (count seq-exprs)) "an even number of forms in binding vector") - (let [err (fn [& msg] (throw (ex-info (apply core/str msg) {}))) - step (fn step [recform exprs] - (if-not exprs - [true `(do ~@body)] - (let [k (first exprs) - v (second exprs) - - seqsym (gensym "seq__") - recform (if (core/keyword? k) recform `(recur (next ~seqsym) nil 0 0)) - steppair (step recform (nnext exprs)) - needrec (steppair 0) - subform (steppair 1)] - (core/cond - (= k :let) [needrec `(let ~v ~subform)] - (= k :while) [false `(when ~v - ~subform - ~@(when needrec [recform]))] - (= k :when) [false `(if ~v - (do - ~subform - ~@(when needrec [recform])) - ~recform)] - (core/keyword? k) (err "Invalid 'doseq' keyword" k) - :else (let [chunksym (with-meta (gensym "chunk__") - {:tag 'not-native}) - countsym (gensym "count__") - isym (gensym "i__") - recform-chunk `(recur ~seqsym ~chunksym ~countsym (unchecked-inc ~isym)) - steppair-chunk (step recform-chunk (nnext exprs)) - subform-chunk (steppair-chunk 1)] - [true `(loop [~seqsym (seq ~v) - ~chunksym nil - ~countsym 0 - ~isym 0] - (if (coercive-boolean (< ~isym ~countsym)) - (let [~k (-nth ~chunksym ~isym)] - ~subform-chunk - ~@(when needrec [recform-chunk])) - (when-let [~seqsym (seq ~seqsym)] - (if (chunked-seq? ~seqsym) - (let [c# (chunk-first ~seqsym)] - (recur (chunk-rest ~seqsym) c# + (vector? seq-exprs) "a vector for its binding" + (even? (count seq-exprs)) "an even number of forms in binding vector") + (core/let [err (fn [& msg] (throw (ex-info (apply core/str msg) {}))) + step (fn step [recform exprs] + (if-not exprs + [true `(do ~@body)] + (core/let [k (first exprs) + v (second exprs) + + seqsym (gensym "seq__") + recform (if (core/keyword? k) recform `(recur (next ~seqsym) nil 0 0)) + steppair (step recform (nnext exprs)) + needrec (steppair 0) + subform (steppair 1)] + (core/cond + (= k :let) [needrec `(let ~v ~subform)] + (= k :while) [false `(when ~v + ~subform + ~@(when needrec [recform]))] + (= k :when) [false `(if ~v + (do + ~subform + ~@(when needrec [recform])) + ~recform)] + (core/keyword? k) (err "Invalid 'doseq' keyword" k) + :else (let [chunksym (with-meta (gensym "chunk__") + {:tag 'not-native}) + countsym (gensym "count__") + isym (gensym "i__") + recform-chunk `(recur ~seqsym ~chunksym ~countsym (unchecked-inc ~isym)) + steppair-chunk (step recform-chunk (nnext exprs)) + subform-chunk (steppair-chunk 1)] + [true `(loop [~seqsym (seq ~v) + ~chunksym nil + ~countsym 0 + ~isym 0] + (if (coercive-boolean (< ~isym ~countsym)) + (let [~k (-nth ~chunksym ~isym)] + ~subform-chunk + ~@(when needrec [recform-chunk])) + (when-let [~seqsym (seq ~seqsym)] + (if (chunked-seq? ~seqsym) + (let [c# (chunk-first ~seqsym)] + (recur (chunk-rest ~seqsym) c# (count c#) 0)) - (let [~k (first ~seqsym)] - ~subform - ~@(when needrec [recform]))))))])))))] + (let [~k (first ~seqsym)] + ~subform + ~@(when needrec [recform]))))))])))))] (nth (step nil (seq seq-exprs)) 1))) (core/defmacro array [& rest] - (let [xs-str (->> (repeat "~{}") - (take (count rest)) - (interpose ",") - (apply core/str))] + (core/let [xs-str (->> (repeat "~{}") + (take (count rest)) + (interpose ",") + (apply core/str))] (vary-meta (list* 'js* (core/str "[" xs-str "]") rest) assoc :tag 'array))) @@ -1809,33 +1809,33 @@ (core/defmacro vector ([] '(.-EMPTY cljs.core/PersistentVector)) ([& xs] - (let [cnt (count xs)] - (if (core/< cnt 32) - `(cljs.core/PersistentVector. nil ~cnt 5 - (.-EMPTY-NODE cljs.core/PersistentVector) (array ~@xs) nil) - (vary-meta - `(.fromArray cljs.core/PersistentVector (array ~@xs) true) - assoc :tag 'cljs.core/PersistentVector))))) + (core/let [cnt (count xs)] + (if (core/< cnt 32) + `(cljs.core/PersistentVector. nil ~cnt 5 + (.-EMPTY-NODE cljs.core/PersistentVector) (array ~@xs) nil) + (vary-meta + `(.fromArray cljs.core/PersistentVector (array ~@xs) true) + assoc :tag 'cljs.core/PersistentVector))))) (core/defmacro array-map ([] '(.-EMPTY cljs.core/PersistentArrayMap)) ([& kvs] - (let [keys (map first (partition 2 kvs))] - (if (core/and (every? #(= (:op %) :constant) - (map #(cljs.analyzer/analyze &env %) keys)) - (= (count (into #{} keys)) (count keys))) - `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) - `(.fromArray cljs.core/PersistentArrayMap (array ~@kvs) true false))))) + (core/let [keys (map first (partition 2 kvs))] + (if (core/and (every? #(= (:op %) :constant) + (map #(cljs.analyzer/analyze &env %) keys)) + (= (count (into #{} keys)) (count keys))) + `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) + `(.fromArray cljs.core/PersistentArrayMap (array ~@kvs) true false))))) (core/defmacro hash-map ([] `(.-EMPTY cljs.core/PersistentHashMap)) ([& kvs] - (let [pairs (partition 2 kvs) - ks (map first pairs) - vs (map second pairs)] - (vary-meta - `(.fromArrays cljs.core/PersistentHashMap (array ~@ks) (array ~@vs)) - assoc :tag 'cljs.core/PersistentHashMap)))) + (core/let [pairs (partition 2 kvs) + ks (map first pairs) + vs (map second pairs)] + (vary-meta + `(.fromArrays cljs.core/PersistentHashMap (array ~@ks) (array ~@vs)) + assoc :tag 'cljs.core/PersistentHashMap)))) (core/defmacro hash-set ([] `(.-EMPTY cljs.core/PersistentHashSet)) @@ -1852,26 +1852,26 @@ assoc :tag 'cljs.core/PersistentHashSet)))) (defn- js-obj* [kvs] - (let [kvs-str (->> (repeat "~{}:~{}") - (take (count kvs)) - (interpose ",") - (apply core/str))] + (core/let [kvs-str (->> (repeat "~{}:~{}") + (take (count kvs)) + (interpose ",") + (apply core/str))] (vary-meta (list* 'js* (core/str "{" kvs-str "}") (apply concat kvs)) assoc :tag 'object))) (core/defmacro js-obj [& rest] - (let [sym-or-str? (fn [x] (core/or (core/symbol? x) (core/string? x))) - filter-on-keys (fn [f coll] - (->> coll - (filter (fn [[k _]] (f k))) - (into {}))) - kvs (into {} (map vec (partition 2 rest))) - sym-pairs (filter-on-keys core/symbol? kvs) - expr->local (zipmap - (filter (complement sym-or-str?) (keys kvs)) - (repeatedly gensym)) - obj (gensym "obj")] + (core/let [sym-or-str? (fn [x] (core/or (core/symbol? x) (core/string? x))) + filter-on-keys (fn [f coll] + (->> coll + (filter (fn [[k _]] (f k))) + (into {}))) + kvs (into {} (map vec (partition 2 rest))) + sym-pairs (filter-on-keys core/symbol? kvs) + expr->local (zipmap + (filter (complement sym-or-str?) (keys kvs)) + (repeatedly gensym)) + obj (gensym "obj")] `(let [~@(apply concat (clojure.set/map-invert expr->local)) ~obj ~(js-obj* (filter-on-keys core/string? kvs))] ~@(map (fn [[k v]] `(aset ~obj ~k ~v)) sym-pairs) @@ -1915,8 +1915,8 @@ Repeatedly executes body (presumably for side-effects) with name bound to integers from 0 through n-1." [bindings & body] - (let [i (first bindings) - n (second bindings)] + (core/let [i (first bindings) + n (second bindings)] `(let [n# ~n] (loop [~i 0] (when (< ~i n#) @@ -1942,33 +1942,33 @@ :hierarchy the isa? hierarchy to use for dispatching defaults to the global hierarchy" [mm-name & options] - (let [docstring (if (core/string? (first options)) - (first options) - nil) - options (if (core/string? (first options)) - (next options) - options) - m (if (map? (first options)) - (first options) - {}) - options (if (map? (first options)) - (next options) - options) - dispatch-fn (first options) - options (next options) - m (if docstring - (assoc m :doc docstring) - m) - m (if (meta mm-name) - (conj (meta mm-name) m) - m) - mm-ns (-> &env :ns :name core/str)] + (core/let [docstring (if (core/string? (first options)) + (first options) + nil) + options (if (core/string? (first options)) + (next options) + options) + m (if (map? (first options)) + (first options) + {}) + options (if (map? (first options)) + (next options) + options) + dispatch-fn (first options) + options (next options) + m (if docstring + (assoc m :doc docstring) + m) + m (if (meta mm-name) + (conj (meta mm-name) m) + m) + mm-ns (-> &env :ns :name core/str)] (when (= (count options) 1) (throw #?(:clj (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)") :cljs (js/Error. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")))) - (let [options (apply core/hash-map options) - default (core/get options :default :default)] + (core/let [options (apply core/hash-map options) + default (core/get options :default :default)] (check-valid-options options :default :hierarchy) `(defonce ~(with-meta mm-name m) (let [method-table# (atom {}) @@ -1977,7 +1977,7 @@ cached-hierarchy# (atom {}) hierarchy# (get ~options :hierarchy (cljs.core/get-global-hierarchy))] (cljs.core/MultiFn. (cljs.core/symbol ~mm-ns ~(name mm-name)) ~dispatch-fn ~default hierarchy# - method-table# prefer-table# method-cache# cached-hierarchy#)))))) + method-table# prefer-table# method-cache# cached-hierarchy#)))))) (core/defmacro defmethod "Creates and installs a new method of multimethod associated with dispatch-value. " @@ -2000,32 +2000,32 @@ print the result. expr's string representation will be produced using pr-str in any case." [bindings expr iterations & {:keys [print-fn] :or {print-fn 'println}}] - (let [bs-str (pr-str bindings) - expr-str (pr-str expr)] + (core/let [bs-str (pr-str bindings) + expr-str (pr-str expr)] `(let ~bindings (let [start# (.getTime (js/Date.)) ret# (dotimes [_# ~iterations] ~expr) end# (.getTime (js/Date.)) elapsed# (- end# start#)] (~print-fn (str ~bs-str ", " ~expr-str ", " - ~iterations " runs, " elapsed# " msecs")))))) + ~iterations " runs, " elapsed# " msecs")))))) (def cs (into [] (map (comp gensym core/str core/char) (range 97 118)))) (defn- gen-apply-to-helper ([] (gen-apply-to-helper 1)) ([n] - (let [prop (symbol (core/str "-cljs$core$IFn$_invoke$arity$" n)) - f (symbol (core/str "cljs$core$IFn$_invoke$arity$" n))] - (if (core/<= n 20) - `(let [~(cs (core/dec n)) (-first ~'args) - ~'args (-rest ~'args)] - (if (core/== ~'argc ~n) - (if (. ~'f ~prop) - (. ~'f (~f ~@(take n cs))) - (~'f ~@(take n cs))) - ~(gen-apply-to-helper (core/inc n)))) - `(throw (js/Error. "Only up to 20 arguments supported on functions")))))) + (core/let [prop (symbol (core/str "-cljs$core$IFn$_invoke$arity$" n)) + f (symbol (core/str "cljs$core$IFn$_invoke$arity$" n))] + (if (core/<= n 20) + `(let [~(cs (core/dec n)) (-first ~'args) + ~'args (-rest ~'args)] + (if (core/== ~'argc ~n) + (if (. ~'f ~prop) + (. ~'f (~f ~@(take n cs))) + (~'f ~@(take n cs))) + ~(gen-apply-to-helper (core/inc n)))) + `(throw (js/Error. "Only up to 20 arguments supported on functions")))))) (core/defmacro gen-apply-to [] `(do @@ -2129,10 +2129,10 @@ (defn- variadic-fn* ([sym method] - (variadic-fn* sym method true)) + (variadic-fn* sym method true)) ([sym [arglist & body :as method] solo] - (let [sig (remove '#{&} arglist) - restarg (gensym "seq")] + (core/let [sig (remove '#{&} arglist) + restarg (gensym "seq")] (letfn [(get-delegate [] 'cljs$core$IFn$_invoke$arity$variadic) (get-delegate-prop [] @@ -2144,9 +2144,9 @@ (if (core/< 1 (count sig)) (let [params (repeatedly (core/dec (count sig)) gensym)] `(fn - ([~restarg] - (let [~@(mapcat param-bind params)] - (. ~sym (~(get-delegate) ~@params ~restarg)))))) + ([~restarg] + (let [~@(mapcat param-bind params)] + (. ~sym (~(get-delegate) ~@params ~restarg)))))) `(fn ([~restarg] (. ~sym (~(get-delegate) (seq ~restarg)))))))] From 09149c540f1b1cd60411df35dde67bb9c159b837 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Jun 2015 00:43:14 -0400 Subject: [PATCH 1158/4033] defn- -> core/defn- in the macros file --- src/main/clojure/cljs/core.cljc | 78 ++++++++++++++++----------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index f434bde6b..a12109cb2 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -103,7 +103,7 @@ (recur threaded (next forms))) x)))) -(defn- ^{:dynamic true} assert-valid-fdecl +(core/defn- ^{:dynamic true} assert-valid-fdecl "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn." [fdecl] (when (empty? fdecl) @@ -325,10 +325,10 @@ (apply core/str))] (list* 'js* (core/str "[" strs "].join('')") xs))) -(defn- bool-expr [e] +(core/defn- bool-expr [e] (vary-meta e assoc :tag 'boolean)) -(defn- simple-test-expr? [env ast] +(core/defn- simple-test-expr? [env ast] (core/and (#{:var :invoke :constant :dot :js} (:op ast)) ('#{boolean seq} (cljs.analyzer/infer-tag env ast)))) @@ -674,7 +674,7 @@ ;;; internal -- reducers-related macros -(defn- do-curried +(core/defn- do-curried [name doc meta args body] (core/let [cargs (vec (butlast args))] `(defn ~name ~doc ~meta @@ -687,7 +687,7 @@ [name doc meta args & body] (do-curried name doc meta args body)) -(defn- do-rfn [f1 k fkv] +(core/defn- do-rfn [f1 k fkv] `(fn ([] (~f1)) ~(clojure.walk/postwalk @@ -705,7 +705,7 @@ ;;; end of reducers macros -(defn- protocol-prefix [psym] +(core/defn- protocol-prefix [psym] (core/str (-> (core/str psym) (.replace \. \$) (.replace \/ \$)) "$")) (def #^:private base-type @@ -806,10 +806,10 @@ `(let [~name (js-this)] ~@body)) -(defn- to-property [sym] +(core/defn- to-property [sym] (symbol (core/str "-" sym))) -(defn- warn-and-update-protocol [p type env] +(core/defn- warn-and-update-protocol [p type env] (when-not (= 'Object p) (if-let [var (cljs.analyzer/resolve-existing-var (dissoc env :locals) p)] (do @@ -827,21 +827,21 @@ (when (:undeclared cljs.analyzer/*cljs-warnings*) (cljs.analyzer/warning :undeclared-protocol-symbol env {:protocol p}))))) -(defn- resolve-var [env sym] +(core/defn- resolve-var [env sym] (core/let [ret (-> (dissoc env :locals) (cljs.analyzer/resolve-var sym) :name)] (assert ret (core/str "Can't resolve: " sym)) ret)) -(defn- ->impl-map [impls] +(core/defn- ->impl-map [impls] (core/loop [ret {} s impls] (if (seq s) (recur (assoc ret (first s) (take-while seq? (next s))) (drop-while seq? (next s))) ret))) -(defn- base-assign-impls [env resolve tsym type [p sigs]] +(core/defn- base-assign-impls [env resolve tsym type [p sigs]] (warn-and-update-protocol p tsym env) (core/let [psym (resolve p) pfn-prefix (subs (core/str psym) 0 @@ -860,11 +860,11 @@ (core/defmethod extend-prefix :default [tsym sym] `(.. ~tsym -prototype ~(to-property sym))) -(defn- adapt-obj-params [type [[this & args :as sig] & body]] +(core/defn- adapt-obj-params [type [[this & args :as sig] & body]] (core/list (vec args) (list* 'this-as (vary-meta this assoc :tag type) body))) -(defn- adapt-ifn-params [type [[this & args :as sig] & body]] +(core/defn- adapt-ifn-params [type [[this & args :as sig] & body]] (core/let [self-sym (with-meta 'self__ {:tag type})] `(~(vec (cons self-sym args)) (this-as ~self-sym @@ -872,17 +872,17 @@ ~@body))))) ;; for IFn invoke implementations, we need to drop first arg -(defn- adapt-ifn-invoke-params [type [[this & args :as sig] & body]] +(core/defn- adapt-ifn-invoke-params [type [[this & args :as sig] & body]] `(~(vec args) (this-as ~(vary-meta this assoc :tag type) ~@body))) -(defn- adapt-proto-params [type [[this & args :as sig] & body]] +(core/defn- adapt-proto-params [type [[this & args :as sig] & body]] `(~(vec (cons (vary-meta this assoc :tag type) args)) (this-as ~this ~@body))) -(defn- add-obj-methods [type type-sym sigs] +(core/defn- add-obj-methods [type type-sym sigs] (map (fn [[f & meths :as form]] (core/let [[f meths] (if (vector? (first meths)) [f [(rest form)]] @@ -891,7 +891,7 @@ ~(with-meta `(fn ~@(map #(adapt-obj-params type %) meths)) (meta form))))) sigs)) -(defn- ifn-invoke-methods [type type-sym [f & meths :as form]] +(core/defn- ifn-invoke-methods [type type-sym [f & meths :as form]] (map (fn [meth] (core/let [arity (count (first meth))] @@ -899,7 +899,7 @@ ~(with-meta `(fn ~meth) (meta form))))) (map #(adapt-ifn-invoke-params type %) meths))) -(defn- add-ifn-methods [type type-sym [f & meths :as form]] +(core/defn- add-ifn-methods [type type-sym [f & meths :as form]] (core/let [meths (map #(adapt-ifn-params type %) meths) this-sym (with-meta 'self__ {:tag type}) argsym (gensym "args")] @@ -914,7 +914,7 @@ (meta form)))] (ifn-invoke-methods type type-sym form)))) -(defn- add-proto-methods* [pprefix type type-sym [f & meths :as form]] +(core/defn- add-proto-methods* [pprefix type type-sym [f & meths :as form]] (core/let [pf (core/str pprefix f)] (if (vector? (first meths)) ;; single method case @@ -926,7 +926,7 @@ ~(with-meta `(fn ~(adapt-proto-params type meth)) (meta form)))) meths)))) -(defn- proto-assign-impls [env resolve type-sym type [p sigs]] +(core/defn- proto-assign-impls [env resolve type-sym type [p sigs]] (warn-and-update-protocol p type env) (core/let [psym (resolve p) pprefix (protocol-prefix psym) @@ -943,7 +943,7 @@ (add-proto-methods* pprefix type type-sym sig))) sigs))))) -(defn- validate-impl-sigs [env p method] +(core/defn- validate-impl-sigs [env p method] (when-not (= p 'Object) (core/let [var (ana/resolve-var (dissoc env :locals) p) minfo (-> var :protocol-info :methods) @@ -963,7 +963,7 @@ (ana/warning :protocol-invalid-method env {:protocol p :fname fname :invalid-arity c})) (recur (next sigs) (conj seen c)))))))) -(defn- validate-impls [env impls] +(core/defn- validate-impls [env impls] (core/loop [protos #{} impls impls] (when (seq impls) (core/let [proto (first impls) @@ -1006,7 +1006,7 @@ {:current-symbol type-sym :suggested-symbol (js-base-type type-sym)})) `(do ~@(mapcat #(assign-impls env resolve type-sym type %) impl-map)))) -(defn- prepare-protocol-masks [env impls] +(core/defn- prepare-protocol-masks [env impls] (core/let [resolve (partial resolve-var env) impl-map (->impl-map impls) fpp-pbs (seq @@ -1028,7 +1028,7 @@ parts (range fast-path-protocol-partitions-count))])))) -(defn- annotate-specs [annots v [f sigs]] +(core/defn- annotate-specs [annots v [f sigs]] (conj v (vary-meta (cons f (map #(cons (second %) (nnext %)) sigs)) merge annots))) @@ -1050,13 +1050,13 @@ (recur ret specs)) ret))))) -(defn- collect-protocols [impls env] +(core/defn- collect-protocols [impls env] (->> impls (filter core/symbol?) (map #(:name (cljs.analyzer/resolve-var (dissoc env :locals) %))) (into #{}))) -(defn- build-positional-factory +(core/defn- build-positional-factory [rsym rname fields] (let [fn-name (with-meta (symbol (core/str '-> rsym)) (assoc (meta rsym) :factory :positional)) @@ -1065,7 +1065,7 @@ [~@fields] (new ~rname ~@field-values)))) -(defn- validate-fields +(core/defn- validate-fields [case name fields] (when-not (vector? fields) (throw @@ -1142,7 +1142,7 @@ ~(build-positional-factory t r fields) ~t))) -(defn- emit-defrecord +(core/defn- emit-defrecord "Do not use this directly - use defrecord" [env tagname rname fields impls] (core/let [hinted-fields fields @@ -1220,7 +1220,7 @@ (~'defrecord* ~tagname ~hinted-fields ~pmasks (extend-type ~tagname ~@(dt->et tagname impls fields true))))))) -(defn- build-map-factory [rsym rname fields] +(core/defn- build-map-factory [rsym rname fields] (core/let [fn-name (with-meta (symbol (core/str 'map-> rsym)) (assoc (meta rsym) :factory :map)) ms (gensym) @@ -1534,7 +1534,7 @@ ~gexpr ~expr] ~(emit gpred gexpr clauses)))) -(defn- assoc-test [m test expr env] +(core/defn- assoc-test [m test expr env] (if (contains? m test) (throw #?(:clj (clojure.core/IllegalArgumentException. @@ -1551,7 +1551,7 @@ cljs.analyzer/*cljs-file*)))))) (assoc m test expr))) -(defn- const? [env x] +(core/defn- const? [env x] (core/let [m (core/and (core/list? x) (ana/resolve-var env (last x)))] (core/when m (core/get m :const)))) @@ -1851,7 +1851,7 @@ `(.fromArray cljs.core/PersistentHashSet (array ~@xs) true) assoc :tag 'cljs.core/PersistentHashSet)))) -(defn- js-obj* [kvs] +(core/defn- js-obj* [kvs] (core/let [kvs-str (->> (repeat "~{}:~{}") (take (count kvs)) (interpose ",") @@ -1923,7 +1923,7 @@ ~@body (recur (inc ~i))))))) -(defn- check-valid-options +(core/defn- check-valid-options "Throws an exception if the given option map contains keys not listed as valid, else returns nil." [options & valid-keys] @@ -2012,7 +2012,7 @@ (def cs (into [] (map (comp gensym core/str core/char) (range 97 118)))) -(defn- gen-apply-to-helper +(core/defn- gen-apply-to-helper ([] (gen-apply-to-helper 1)) ([n] (core/let [prop (symbol (core/str "-cljs$core$IFn$_invoke$arity$" n)) @@ -2120,14 +2120,14 @@ (recur form' (ana/macroexpand-1 env form')) `(quote ~form'))))) -(defn- multi-arity-fn? [fdecl] +(core/defn- multi-arity-fn? [fdecl] (core/< 1 (count fdecl))) -(defn- variadic-fn? [fdecl] +(core/defn- variadic-fn? [fdecl] (core/and (= 1 (count fdecl)) (some '#{&} (ffirst fdecl)))) -(defn- variadic-fn* +(core/defn- variadic-fn* ([sym method] (variadic-fn* sym method true)) ([sym [arglist & body :as method] solo] @@ -2159,7 +2159,7 @@ (set! (. ~sym ~'-cljs$lang$applyTo) ~(apply-to))))))) -(defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl]] +(core/defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl]] (letfn [(dest-args [c] (map (fn [n] `(aget (js-arguments) ~n)) (range c)))] @@ -2192,7 +2192,7 @@ (pp/pprint (variadic-fn 'foo {} '(([a [b & cs] & xs] xs)))) ) -(defn- multi-arity-fn [name meta fdecl] +(core/defn- multi-arity-fn [name meta fdecl] (letfn [(dest-args [c] (map (fn [n] `(aget (js-arguments) ~n)) (range c))) From 40d73ef1215359f45167dd4d8726a56e2f056dac Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Jun 2015 01:21:27 -0400 Subject: [PATCH 1159/4033] fn -> core/fn, when* -> core/when* --- src/main/clojure/cljs/core.cljc | 215 ++++++++++++++++---------------- 1 file changed, 108 insertions(+), 107 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a12109cb2..303e8d661 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -41,6 +41,7 @@ clojure.set cljs.compiler [cljs.env :as env] + #?(:cljs [cljs.core :as core]) #?(:cljs [cljs.analyzer :as ana]))) #?(:clj (alias 'core 'clojure.core)) @@ -99,14 +100,14 @@ (core/let [form (first forms) threaded (if (seq? form) (with-meta `(~(first form) ~x ~@(next form)) (meta form)) - (list form x))] + (core/list form x))] (recur threaded (next forms))) x)))) (core/defn- ^{:dynamic true} assert-valid-fdecl "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn." [fdecl] - (when (empty? fdecl) + (core/when (empty? fdecl) (throw #?(:clj (IllegalArgumentException. "Parameter declaration missing") :cljs (js/Error. "Parameter declaration missing")))) @@ -133,7 +134,7 @@ "\" should be a vector")))))) fdecl) bad-args (seq (remove #(vector? %) argdecls))] - (when bad-args + (core/when bad-args (throw #?(:clj (IllegalArgumentException. (core/str "Parameter declaration \"" (first bad-args) @@ -145,23 +146,23 @@ (def ^{:private true} sigs - (fn [fdecl] + (core/fn [fdecl] (assert-valid-fdecl fdecl) (core/let [asig - (fn [fdecl] - (core/let [arglist (first fdecl) - ;elide implicit macro args - arglist (if #?(:clj (clojure.lang.Util/equals '&form (first arglist)) - :cljs (= '&form (first arglist))) - #?(:clj (clojure.lang.RT/subvec arglist 2 (clojure.lang.RT/count arglist)) - :cljs (subvec arglist 2 (count arglist))) - arglist) - body (next fdecl)] - (if (map? (first body)) - (if (next body) - (with-meta arglist (conj (if (meta arglist) (meta arglist) {}) (first body))) - arglist) - arglist)))] + (core/fn [fdecl] + (core/let [arglist (first fdecl) + ;elide implicit macro args + arglist (if #?(:clj (clojure.lang.Util/equals '&form (first arglist)) + :cljs (= '&form (first arglist))) + #?(:clj (clojure.lang.RT/subvec arglist 2 (clojure.lang.RT/count arglist)) + :cljs (subvec arglist 2 (count arglist))) + arglist) + body (next fdecl)] + (if (map? (first body)) + (if (next body) + (with-meta arglist (conj (if (meta arglist) (meta arglist) {}) (first body))) + arglist) + arglist)))] (if (seq? (first fdecl)) (core/loop [ret [] fdecls fdecl] (if fdecls @@ -178,20 +179,20 @@ (throw (IllegalArgumentException. ~(core/str fnname " requires " (second pairs))))) ~(core/let [more (nnext pairs)] - (when more + (core/when more (list* `assert-args fnname more)))) :cljs `(do (when-not ~(first pairs) (throw (js/Error. ~(core/str fnname " requires " (second pairs))))) ~(core/let [more (nnext pairs)] - (when more + (core/when more (list* `assert-args fnname more)))))) (core/defn destructure [bindings] (core/let [bents (partition 2 bindings) - pb (fn pb [bvec b v] + pb (core/fn pb [bvec b v] (core/let [pvec - (fn [bvec b val] + (core/fn [bvec b val] (core/let [gvec (gensym "vec__")] (core/loop [ret (-> bvec (conj gvec) (conj val)) n 0 @@ -215,17 +216,17 @@ seen-rest?)))) ret)))) pmap - (fn [bvec b v] + (core/fn [bvec b v] (core/let [gmap (gensym "map__") defaults (:or b)] (core/loop [ret (-> bvec (conj gmap) (conj v) (conj gmap) (conj `(if (seq? ~gmap) (apply core/hash-map ~gmap) ~gmap)) - ((fn [ret] + ((core/fn [ret] (if (:as b) (conj ret (:as b) gmap) ret)))) bes (reduce - (fn [bes entry] + (core/fn [bes entry] (reduce #(assoc %1 %2 ((val entry) %2)) (dissoc bes (key entry)) ((key entry) bes))) @@ -249,7 +250,7 @@ :else (throw #?(:clj (new Exception (core/str "Unsupported binding form: " b)) :cljs (new js/Error (core/str "Unsupported binding form: " b))))))) - process-entry (fn [bvec b] (pb bvec (first b) (second b)))] + process-entry (core/fn [bvec b] (pb bvec (first b) (second b)))] (if (every? core/symbol? (map first bents)) bindings (if-let [kwbs (seq (filter #(core/keyword? (first %)) bents))] @@ -283,8 +284,8 @@ `(loop* ~bindings ~@body) (core/let [vs (take-nth 2 (drop 1 bindings)) bs (take-nth 2 bindings) - gs (map (fn [b] (if (core/symbol? b) b (gensym))) bs) - bfs (reduce (fn [ret [b v g]] + gs (map (core/fn [b] (if (core/symbol? b) b (gensym))) bs) + bfs (reduce (core/fn [ret [b v g]] (if (core/symbol? b) (conj ret g v) (conj ret g v b g))) @@ -305,7 +306,7 @@ ITransientAssociative ITransientMap ITransientVector ITransientSet IMultiFn IChunkedSeq IChunkedNext IComparable INamed ICloneable IAtom IReset ISwap]) - (iterate (fn [[p b]] + (iterate (core/fn [[p b]] (if (core/== 2147483648 b) [(core/inc p) 1] [p (core/bit-shift-left b 1)])) @@ -810,21 +811,21 @@ (symbol (core/str "-" sym))) (core/defn- warn-and-update-protocol [p type env] - (when-not (= 'Object p) + (core/when-not (= 'Object p) (if-let [var (cljs.analyzer/resolve-existing-var (dissoc env :locals) p)] (do - (when-not (:protocol-symbol var) + (core/when-not (:protocol-symbol var) (cljs.analyzer/warning :invalid-protocol-symbol env {:protocol p})) - (when (core/and (:protocol-deprecated cljs.analyzer/*cljs-warnings*) + (core/when (core/and (:protocol-deprecated cljs.analyzer/*cljs-warnings*) (-> var :deprecated) (not (-> p meta :deprecation-nowarn))) (cljs.analyzer/warning :protocol-deprecated env {:protocol p})) - (when (:protocol-symbol var) + (core/when (:protocol-symbol var) (swap! env/*compiler* update-in [:cljs.analyzer/namespaces] - (fn [ns] + (core/fn [ns] (update-in ns [(:ns var) :defs (symbol (name p)) :impls] conj type))))) - (when (:undeclared cljs.analyzer/*cljs-warnings*) + (core/when (:undeclared cljs.analyzer/*cljs-warnings*) (cljs.analyzer/warning :undeclared-protocol-symbol env {:protocol p}))))) (core/defn- resolve-var [env sym] @@ -847,12 +848,12 @@ pfn-prefix (subs (core/str psym) 0 (clojure.core/inc (.indexOf (core/str psym) "/")))] (cons `(aset ~psym ~type true) - (map (fn [[f & meths :as form]] + (map (core/fn [[f & meths :as form]] `(aset ~(symbol (core/str pfn-prefix f)) ~type ~(with-meta `(fn ~@meths) (meta form)))) sigs)))) -(core/defmulti extend-prefix (fn [tsym sym] (-> tsym meta :extend))) +(core/defmulti extend-prefix (core/fn [tsym sym] (-> tsym meta :extend))) (core/defmethod extend-prefix :instance [tsym sym] `(.. ~tsym ~(to-property sym))) @@ -883,7 +884,7 @@ ~@body))) (core/defn- add-obj-methods [type type-sym sigs] - (map (fn [[f & meths :as form]] + (map (core/fn [[f & meths :as form]] (core/let [[f meths] (if (vector? (first meths)) [f [(rest form)]] [f meths])] @@ -893,7 +894,7 @@ (core/defn- ifn-invoke-methods [type type-sym [f & meths :as form]] (map - (fn [meth] + (core/fn [meth] (core/let [arity (count (first meth))] `(set! ~(extend-prefix type-sym (symbol (core/str "cljs$core$IFn$_invoke$arity$" arity))) ~(with-meta `(fn ~meth) (meta form))))) @@ -921,7 +922,7 @@ (core/let [meth meths] [`(set! ~(extend-prefix type-sym (core/str pf "$arity$" (count (first meth)))) ~(with-meta `(fn ~@(adapt-proto-params type meth)) (meta form)))]) - (map (fn [[sig & body :as meth]] + (map (core/fn [[sig & body :as meth]] `(set! ~(extend-prefix type-sym (core/str pf "$arity$" (count sig))) ~(with-meta `(fn ~(adapt-proto-params type meth)) (meta form)))) meths)))) @@ -934,47 +935,47 @@ (if (= p 'Object) (add-obj-methods type type-sym sigs) (concat - (when-not (skip-flag psym) + (core/when-not (skip-flag psym) [`(set! ~(extend-prefix type-sym pprefix) true)]) (mapcat - (fn [sig] + (core/fn [sig] (if (= psym 'cljs.core/IFn) (add-ifn-methods type type-sym sig) (add-proto-methods* pprefix type type-sym sig))) sigs))))) (core/defn- validate-impl-sigs [env p method] - (when-not (= p 'Object) + (core/when-not (= p 'Object) (core/let [var (ana/resolve-var (dissoc env :locals) p) minfo (-> var :protocol-info :methods) [fname sigs] (if (core/vector? (second method)) [(first method) [(second method)]] [(first method) (map first (rest method))]) decmeths (core/get minfo fname ::not-found)] - (when (= decmeths ::not-found) + (core/when (= decmeths ::not-found) (ana/warning :protocol-invalid-method env {:protocol p :fname fname :no-such-method true})) (core/loop [sigs sigs seen #{}] - (when (seq sigs) + (core/when (seq sigs) (core/let [sig (first sigs) c (count sig)] - (when (contains? seen c) + (core/when (contains? seen c) (ana/warning :protocol-duped-method env {:protocol p :fname fname})) - (when (core/and (not= decmeths ::not-found) (not (some #{c} (map count decmeths)))) + (core/when (core/and (not= decmeths ::not-found) (not (some #{c} (map count decmeths)))) (ana/warning :protocol-invalid-method env {:protocol p :fname fname :invalid-arity c})) (recur (next sigs) (conj seen c)))))))) (core/defn- validate-impls [env impls] (core/loop [protos #{} impls impls] - (when (seq impls) + (core/when (seq impls) (core/let [proto (first impls) methods (take-while seq? (next impls)) impls (drop-while seq? (next impls))] - (when (contains? protos proto) + (core/when (contains? protos proto) (ana/warning :protocol-multiple-impls env {:protocol proto})) (core/loop [seen #{} methods methods] - (when (seq methods) + (core/when (seq methods) (core/let [[fname :as method] (first methods)] - (when (contains? seen fname) + (core/when (contains? seen fname) (ana/warning :extend-type-invalid-method-shape env {:protocol proto :method fname})) (validate-impl-sigs env proto method) @@ -1000,7 +1001,7 @@ [type assign-impls] (if-let [type (base-type type-sym)] [type base-assign-impls] [(resolve type-sym) proto-assign-impls])] - (when (core/and (:extending-base-js-type cljs.analyzer/*cljs-warnings*) + (core/when (core/and (:extending-base-js-type cljs.analyzer/*cljs-warnings*) (js-base-type type-sym)) (cljs.analyzer/warning :extending-base-js-type env {:current-symbol type-sym :suggested-symbol (js-base-type type-sym)})) @@ -1024,7 +1025,7 @@ (into {} (map (juxt key (comp (partial reduce core/bit-or) val)) parts)))] - [fpps (reduce (fn [ps p] (update-in ps [p] (fnil identity 0))) + [fpps (reduce (core/fn [ps p] (update-in ps [p] (core/fnil identity 0))) parts (range fast-path-protocol-partitions-count))])))) @@ -1067,7 +1068,7 @@ (core/defn- validate-fields [case name fields] - (when-not (vector? fields) + (core/when-not (vector? fields) (throw #?(:clj (AssertionError. (core/str case " " name ", no fields vector given.")) :cljs (js/Error. (core/str case " " name ", no fields vector given.")))))) @@ -1175,7 +1176,7 @@ `(~'-lookup [this# k#] (-lookup this# k# nil)) `(~'-lookup [this# ~ksym else#] (case ~ksym - ~@(mapcat (fn [f] [(keyword f) f]) base-fields) + ~@(mapcat (core/fn [f] [(keyword f) f]) base-fields) (get ~'__extmap ~ksym else#))) 'ICounted `(~'-count [this#] (+ ~(count base-fields) (count ~'__extmap))) @@ -1189,7 +1190,7 @@ 'IAssociative `(~'-assoc [this# k# ~gs] (condp keyword-identical? k# - ~@(mapcat (fn [fld] + ~@(mapcat (core/fn [fld] [(keyword fld) (list* `new tagname (replace {fld gs '__hash nil} fields))]) base-fields) (new ~tagname ~@(remove #{'__extmap '__hash} fields) (assoc ~'__extmap k# ~gs) nil))) @@ -1225,7 +1226,7 @@ (assoc (meta rsym) :factory :map)) ms (gensym) ks (map keyword fields) - getters (map (fn [k] `(~k ~ms)) ks)] + getters (map (core/fn [k] `(~k ~ms)) ks)] `(defn ~fn-name [~ms] (new ~rname ~@getters nil (dissoc ~ms ~@ks) nil)))) @@ -1347,10 +1348,10 @@ :doc doc :protocol-symbol true) ns-name (-> &env :ns :name) - fqn (fn [n] (symbol (core/str ns-name "." n))) + fqn (core/fn [n] (symbol (core/str ns-name "." n))) prefix (protocol-prefix p) _ (core/doseq [[mname & arities] methods] - (when (some #{0} (map count (filter vector? arities))) + (core/when (some #{0} (map count (filter vector? arities))) (throw #?(:clj (Exception. (core/str "Invalid protocol, " psym @@ -1358,7 +1359,7 @@ :cljs (js/Error. (core/str "Invalid protocol, " psym " defines method " mname " with arity 0")))))) - expand-sig (fn [fname slot sig] + expand-sig (core/fn [fname slot sig] `(~sig (if (and ~(first sig) (. ~(first sig) ~(symbol (core/str "-" slot)))) ;; Property access needed here. (. ~(first sig) ~slot ~@sig) @@ -1372,23 +1373,23 @@ psym (vary-meta psym assoc-in [:protocol-info :methods] (into {} (map - (fn [[fname & sigs]] + (core/fn [[fname & sigs]] (core/let [doc (as-> (last sigs) doc - (when (core/string? doc) doc)) + (core/when (core/string? doc) doc)) sigs (take-while vector? sigs)] [(vary-meta fname assoc :doc doc) (vec sigs)])) methods))) - method (fn [[fname & sigs]] + method (core/fn [[fname & sigs]] (core/let [doc (as-> (last sigs) doc - (when (core/string? doc) doc)) + (core/when (core/string? doc) doc)) sigs (take-while vector? sigs) slot (symbol (core/str prefix (name fname))) fname (vary-meta fname assoc :protocol p :doc doc)] `(defn ~fname - ~@(map (fn [sig] + ~@(map (core/fn [sig] (expand-sig fname (symbol (core/str slot "$arity$" (count sig))) sig)) @@ -1471,7 +1472,7 @@ tempnames (map (comp gensym name) names) binds (map core/vector names vals) resets (reverse (map core/vector names tempnames)) - bind-value (fn [[k v]] (core/list 'set! k v))] + bind-value (core/fn [[k v]] (core/list 'set! k v))] `(let [~@(interleave tempnames names)] ~@(map bind-value binds) (try @@ -1516,7 +1517,7 @@ [pred expr & clauses] (core/let [gpred (gensym "pred__") gexpr (gensym "expr__") - emit (fn emit [pred expr args] + emit (core/fn emit [pred expr args] (let [[[a b c :as clause] more] (split-at (if (= :>> (second args)) 3 2) args) n (count clause)] @@ -1540,13 +1541,13 @@ #?(:clj (clojure.core/IllegalArgumentException. (core/str "Duplicate case test constant '" test "'" - (when (:line env) + (core/when (:line env) (core/str " on line " (:line env) " " cljs.analyzer/*cljs-file*)))) :cljs (js/Error. (core/str "Duplicate case test constant '" test "'" - (when (:line env) + (core/when (:line env) (core/str " on line " (:line env) " " cljs.analyzer/*cljs-file*)))))) (assoc m test expr))) @@ -1587,11 +1588,11 @@ (core/str "No matching clause: " ~e)))) env &env pairs (reduce - (fn [m [test expr]] + (core/fn [m [test expr]] (core/cond (seq? test) (reduce - (fn [m test] + (core/fn [m test] (let [test (if (core/symbol? test) (core/list 'quote test) test)] @@ -1624,20 +1625,20 @@ :else `(let [~esym ~e] (cond - ~@(mapcat (fn [[m c]] `((cljs.core/= ~m ~esym) ~c)) pairs) + ~@(mapcat (core/fn [[m c]] `((cljs.core/= ~m ~esym) ~c)) pairs) :else ~default))))) (core/defmacro assert "Evaluates expr and throws an exception if it does not evaluate to logical true." ([x] - (when *assert* - `(when-not ~x + (core/when *assert* + `(core/when-not ~x (throw (js/Error. (cljs.core/str "Assert failed: " (cljs.core/pr-str '~x))))))) ([x message] - (when *assert* - `(when-not ~x + (core/when *assert* + `(core/when-not ~x (throw (js/Error. (cljs.core/str "Assert failed: " ~message "\n" (cljs.core/pr-str '~x)))))))) @@ -1655,18 +1656,18 @@ (assert-args for (vector? seq-exprs) "a vector for its binding" (even? (count seq-exprs)) "an even number of forms in binding vector") - (core/let [to-groups (fn [seq-exprs] - (reduce (fn [groups [k v]] + (core/let [to-groups (core/fn [seq-exprs] + (reduce (core/fn [groups [k v]] (if (core/keyword? k) (conj (pop groups) (conj (peek groups) [k v])) (conj groups [k v]))) [] (partition 2 seq-exprs))) - err (fn [& msg] (throw (ex-info (apply core/str msg) {}))) - emit-bind (fn emit-bind [[[bind expr & mod-pairs] + err (core/fn [& msg] (throw (ex-info (apply core/str msg) {}))) + emit-bind (core/fn emit-bind [[[bind expr & mod-pairs] & [[_ next-expr] :as next-groups]]] (core/let [giter (gensym "iter__") gxs (gensym "s__") - do-mod (fn do-mod [[[k v :as pair] & etc]] + do-mod (core/fn do-mod [[[k v :as pair] & etc]] (core/cond (= k :let) `(let ~v ~(do-mod etc)) (= k :while) `(when ~v ~(do-mod etc)) @@ -1687,12 +1688,12 @@ `(fn ~giter [~gxs] (lazy-seq (loop [~gxs ~gxs] - (when-first [~bind ~gxs] + (core/when-first [~bind ~gxs] ~(do-mod mod-pairs))))) #_"inner-most loop" (core/let [gi (gensym "i__") gb (gensym "b__") - do-cmod (fn do-cmod [[[k v :as pair] & etc]] + do-cmod (core/fn do-cmod [[[k v :as pair] & etc]] (core/cond (= k :let) `(let ~v ~(do-cmod etc)) (= k :while) `(when ~v ~(do-cmod etc)) @@ -1736,8 +1737,8 @@ (assert-args doseq (vector? seq-exprs) "a vector for its binding" (even? (count seq-exprs)) "an even number of forms in binding vector") - (core/let [err (fn [& msg] (throw (ex-info (apply core/str msg) {}))) - step (fn step [recform exprs] + (core/let [err (core/fn [& msg] (throw (ex-info (apply core/str msg) {}))) + step (core/fn step [recform exprs] (if-not exprs [true `(do ~@body)] (core/let [k (first exprs) @@ -1752,11 +1753,11 @@ (= k :let) [needrec `(let ~v ~subform)] (= k :while) [false `(when ~v ~subform - ~@(when needrec [recform]))] + ~@(core/when needrec [recform]))] (= k :when) [false `(if ~v (do ~subform - ~@(when needrec [recform])) + ~@(core/when needrec [recform])) ~recform)] (core/keyword? k) (err "Invalid 'doseq' keyword" k) :else (let [chunksym (with-meta (gensym "chunk__") @@ -1773,15 +1774,15 @@ (if (coercive-boolean (< ~isym ~countsym)) (let [~k (-nth ~chunksym ~isym)] ~subform-chunk - ~@(when needrec [recform-chunk])) - (when-let [~seqsym (seq ~seqsym)] + ~@(core/when needrec [recform-chunk])) + (core/when-let [~seqsym (seq ~seqsym)] (if (chunked-seq? ~seqsym) (let [c# (chunk-first ~seqsym)] (recur (chunk-rest ~seqsym) c# (count c#) 0)) (let [~k (first ~seqsym)] ~subform - ~@(when needrec [recform]))))))])))))] + ~@(core/when needrec [recform]))))))])))))] (nth (step nil (seq seq-exprs)) 1))) (core/defmacro array [& rest] @@ -1861,10 +1862,10 @@ assoc :tag 'object))) (core/defmacro js-obj [& rest] - (core/let [sym-or-str? (fn [x] (core/or (core/symbol? x) (core/string? x))) - filter-on-keys (fn [f coll] + (core/let [sym-or-str? (core/fn [x] (core/or (core/symbol? x) (core/string? x))) + filter-on-keys (core/fn [f coll] (->> coll - (filter (fn [[k _]] (f k))) + (filter (core/fn [[k _]] (f k))) (into {}))) kvs (into {} (map vec (partition 2 rest))) sym-pairs (filter-on-keys core/symbol? kvs) @@ -1874,8 +1875,8 @@ obj (gensym "obj")] `(let [~@(apply concat (clojure.set/map-invert expr->local)) ~obj ~(js-obj* (filter-on-keys core/string? kvs))] - ~@(map (fn [[k v]] `(aset ~obj ~k ~v)) sym-pairs) - ~@(map (fn [[k v]] `(aset ~obj ~v ~(core/get kvs k))) expr->local) + ~@(map (core/fn [[k v]] `(aset ~obj ~k ~v)) sym-pairs) + ~@(map (core/fn [[k v]] `(aset ~obj ~v ~(core/get kvs k))) expr->local) ~obj))) (core/defmacro alength [a] @@ -1927,7 +1928,7 @@ "Throws an exception if the given option map contains keys not listed as valid, else returns nil." [options & valid-keys] - (when (seq (apply disj (apply core/hash-set (keys options)) valid-keys)) + (core/when (seq (apply disj (apply core/hash-set (keys options)) valid-keys)) (throw (apply core/str "Only these options are valid: " (first valid-keys) @@ -1963,7 +1964,7 @@ (conj (meta mm-name) m) m) mm-ns (-> &env :ns :name core/str)] - (when (= (count options) 1) + (core/when (= (count options) 1) (throw #?(:clj (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)") :cljs (js/Error. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")))) @@ -2073,7 +2074,7 @@ "Argument to ns-interns must be a quoted symbol") `(into {} [~@(map - (fn [[sym _]] + (core/fn [[sym _]] `[(symbol ~(name sym)) (var ~(symbol (name ns) (name sym)))]) (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))])) @@ -2153,7 +2154,7 @@ `(do (set! (. ~sym ~(get-delegate-prop)) (fn (~(vec sig) ~@body))) - ~@(when solo + ~@(core/when solo `[(set! (. ~sym ~'-cljs$lang$maxFixedArity) ~(core/dec (count sig)))]) (set! (. ~sym ~'-cljs$lang$applyTo) @@ -2161,7 +2162,7 @@ (core/defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl]] (letfn [(dest-args [c] - (map (fn [n] `(aget (js-arguments) ~n)) + (map (core/fn [n] `(aget (js-arguments) ~n)) (range c)))] (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) sig (remove '#{&} arglist) @@ -2194,7 +2195,7 @@ (core/defn- multi-arity-fn [name meta fdecl] (letfn [(dest-args [c] - (map (fn [n] `(aget (js-arguments) ~n)) + (map (core/fn [n] `(aget (js-arguments) ~n)) (range c))) (fixed-arity [rname sig] (let [c (count sig)] @@ -2256,13 +2257,13 @@ ) (def - ^{:doc "Same as (def name (fn [params* ] exprs*)) or (def - name (fn ([params* ] exprs*)+)) with any doc-string or attrs added + ^{:doc "Same as (def name (core/fn [params* ] exprs*)) or (def + name (core/fn ([params* ] exprs*)+)) with any doc-string or attrs added to the var metadata. prepost-map defines a map with optional keys :pre and :post that contain collections of pre or post conditions." :arglists '([name doc-string? attr-map? [params*] prepost-map? body] [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])} - defn (fn defn [&form &env name & fdecl] + defn (core/fn defn [&form &env name & fdecl] ;; Note: Cannot delegate this check to def because of the call to (with-meta name ..) (if (core/instance? clojure.lang.Symbol name) nil @@ -2326,7 +2327,7 @@ :arglists '([name doc-string? attr-map? [params*] body] [name doc-string? attr-map? ([params*] body)+ attr-map?]) :macro true} - defmacro (fn [&form &env + defmacro (core/fn [&form &env name & args] (core/let [prefix (core/loop [p (core/list (vary-meta name assoc :macro true)) args args] (core/let [f (first args)] @@ -2344,10 +2345,10 @@ fdecl (if (vector? (first fdecl)) (core/list fdecl) fdecl) - add-implicit-args (fn [fd] + add-implicit-args (core/fn [fd] (core/let [args (first fd)] (cons (vec (cons '&form (cons '&env args))) (next fd)))) - add-args (fn [acc ds] + add-args (core/fn [acc ds] (if (core/nil? ds) acc (core/let [d (first ds)] From 597db4ef7a4aa322d0edc7cb26883f5bc920be2f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Jun 2015 09:04:31 -0400 Subject: [PATCH 1160/4033] more macro foo -> core/foo. Exclude cljs macros under :cljs since we will redef --- src/main/clojure/cljs/core.cljc | 94 ++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 303e8d661..3c8bc5d57 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -35,7 +35,11 @@ cond-> cond->> as-> some-> some->> if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand - #?(:cljs alias)]) + #?@(:cljs [alias assert-args coercive-not coercive-not= coercive-= coercive-boolean + truth_ js-arguments js-delete js-in js-debugger exists? divide js-mod + unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash + defcurried rfn specify! js-this this-as implements? array js-obj + simple-benchmark gen-apply-to js-str es6-iterable load-file*])]) #?(:cljs (:require-macros [cljs.core :as core])) (:require clojure.walk clojure.set @@ -194,7 +198,7 @@ (core/let [pvec (core/fn [bvec b val] (core/let [gvec (gensym "vec__")] - (core/loop [ret (-> bvec (conj gvec) (conj val)) + (core/loop [ret (core/-> bvec (conj gvec) (conj val)) n 0 bs b seen-rest? false] @@ -219,7 +223,7 @@ (core/fn [bvec b v] (core/let [gmap (gensym "map__") defaults (:or b)] - (core/loop [ret (-> bvec (conj gmap) (conj v) + (core/loop [ret (core/-> bvec (conj gmap) (conj v) (conj gmap) (conj `(if (seq? ~gmap) (apply core/hash-map ~gmap) ~gmap)) ((core/fn [ret] (if (:as b) @@ -243,8 +247,8 @@ (next bes))) ret))))] (core/cond - (core/symbol? b) (-> bvec (conj (if (namespace b) (symbol (name b)) b)) (conj v)) - (core/keyword? b) (-> bvec (conj (symbol (name b))) (conj v)) + (core/symbol? b) (core/-> bvec (conj (if (namespace b) (symbol (name b)) b)) (conj v)) + (core/keyword? b) (core/-> bvec (conj (symbol (name b))) (conj v)) (vector? b) (pvec bvec b v) (map? b) (pmap bvec b v) :else (throw @@ -253,7 +257,7 @@ process-entry (core/fn [bvec b] (pb bvec (first b) (second b)))] (if (every? core/symbol? (map first bents)) bindings - (if-let [kwbs (seq (filter #(core/keyword? (first %)) bents))] + (core/if-let [kwbs (seq (filter #(core/keyword? (first %)) bents))] (throw #?(:clj (new Exception (core/str "Unsupported binding key: " (ffirst kwbs))) :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs))))) @@ -321,7 +325,7 @@ (core/inc (core/quot c 32))))) (core/defmacro str [& xs] - (core/let [strs (->> (repeat (count xs) "cljs.core.str(~{})") + (core/let [strs (core/->> (repeat (count xs) "cljs.core.str(~{})") (interpose ",") (apply core/str))] (list* 'js* (core/str "[" strs "].join('')") xs))) @@ -345,7 +349,7 @@ (core/let [forms (concat [x] next)] (if (every? #(simple-test-expr? &env %) (map #(cljs.analyzer/analyze &env %) forms)) - (core/let [and-str (->> (repeat (count forms) "(~{})") + (core/let [and-str (core/->> (repeat (count forms) "(~{})") (interpose " && ") (apply core/str))] (bool-expr `(~'js* ~and-str ~@forms))) @@ -363,7 +367,7 @@ (core/let [forms (concat [x] next)] (if (every? #(simple-test-expr? &env %) (map #(cljs.analyzer/analyze &env %) forms)) - (core/let [or-str (->> (repeat (count forms) "(~{})") + (core/let [or-str (core/->> (repeat (count forms) "(~{})") (interpose " || ") (apply core/str))] (bool-expr `(~'js* ~or-str ~@forms))) @@ -707,7 +711,7 @@ ;;; end of reducers macros (core/defn- protocol-prefix [psym] - (core/str (-> (core/str psym) (.replace \. \$) (.replace \/ \$)) "$")) + (core/str (core/-> (core/str psym) (.replace \. \$) (.replace \/ \$)) "$")) (def #^:private base-type {nil "null" @@ -770,7 +774,7 @@ meta-sym (gensym "meta") this-sym (gensym "_") locals (keys (:locals &env)) - ns (-> &env :ns :name) + ns (core/-> &env :ns :name) munge cljs.compiler/munge] `(do (when-not (exists? ~(symbol (core/str ns) (core/str t))) @@ -812,13 +816,13 @@ (core/defn- warn-and-update-protocol [p type env] (core/when-not (= 'Object p) - (if-let [var (cljs.analyzer/resolve-existing-var (dissoc env :locals) p)] + (core/if-let [var (cljs.analyzer/resolve-existing-var (dissoc env :locals) p)] (do (core/when-not (:protocol-symbol var) (cljs.analyzer/warning :invalid-protocol-symbol env {:protocol p})) (core/when (core/and (:protocol-deprecated cljs.analyzer/*cljs-warnings*) - (-> var :deprecated) - (not (-> p meta :deprecation-nowarn))) + (core/-> var :deprecated) + (not (core/-> p meta :deprecation-nowarn))) (cljs.analyzer/warning :protocol-deprecated env {:protocol p})) (core/when (:protocol-symbol var) (swap! env/*compiler* update-in [:cljs.analyzer/namespaces] @@ -829,7 +833,7 @@ (cljs.analyzer/warning :undeclared-protocol-symbol env {:protocol p}))))) (core/defn- resolve-var [env sym] - (core/let [ret (-> (dissoc env :locals) + (core/let [ret (core/-> (dissoc env :locals) (cljs.analyzer/resolve-var sym) :name)] (assert ret (core/str "Can't resolve: " sym)) @@ -853,7 +857,7 @@ ~type ~(with-meta `(fn ~@meths) (meta form)))) sigs)))) -(core/defmulti extend-prefix (core/fn [tsym sym] (-> tsym meta :extend))) +(core/defmulti extend-prefix (core/fn [tsym sym] (core/-> tsym meta :extend))) (core/defmethod extend-prefix :instance [tsym sym] `(.. ~tsym ~(to-property sym))) @@ -931,7 +935,7 @@ (warn-and-update-protocol p type env) (core/let [psym (resolve p) pprefix (protocol-prefix psym) - skip-flag (set (-> type-sym meta :skip-protocol-flag))] + skip-flag (set (core/-> type-sym meta :skip-protocol-flag))] (if (= p 'Object) (add-obj-methods type type-sym sigs) (concat @@ -947,7 +951,7 @@ (core/defn- validate-impl-sigs [env p method] (core/when-not (= p 'Object) (core/let [var (ana/resolve-var (dissoc env :locals) p) - minfo (-> var :protocol-info :methods) + minfo (core/-> var :protocol-info :methods) [fname sigs] (if (core/vector? (second method)) [(first method) [(second method)]] [(first method) (map first (rest method))]) @@ -998,7 +1002,7 @@ _ (validate-impls env impls) resolve (partial resolve-var env) impl-map (->impl-map impls) - [type assign-impls] (if-let [type (base-type type-sym)] + [type assign-impls] (core/if-let [type (base-type type-sym)] [type base-assign-impls] [(resolve type-sym) proto-assign-impls])] (core/when (core/and (:extending-base-js-type cljs.analyzer/*cljs-warnings*) @@ -1044,7 +1048,7 @@ (core/loop [ret [] specs specs] (if (seq specs) (core/let [p (first specs) - ret (-> (conj ret p) + ret (core/-> (conj ret p) (into (reduce (partial annotate-specs annots) [] (group-by first (take-while seq? (next specs)))))) specs (drop-while seq? (next specs))] @@ -1052,7 +1056,7 @@ ret))))) (core/defn- collect-protocols [impls env] - (->> impls + (core/->> impls (filter core/symbol?) (map #(:name (cljs.analyzer/resolve-var (dissoc env :locals) %))) (into #{}))) @@ -1061,7 +1065,7 @@ [rsym rname fields] (let [fn-name (with-meta (symbol (core/str '-> rsym)) (assoc (meta rsym) :factory :positional)) - field-values (if (-> rsym meta :internal-ctor) (conj fields nil nil nil) fields)] + field-values (if (core/-> rsym meta :internal-ctor) (conj fields nil nil nil) fields)] `(defn ~fn-name [~@fields] (new ~rname ~@field-values)))) @@ -1347,7 +1351,7 @@ psym (vary-meta psym assoc :doc doc :protocol-symbol true) - ns-name (-> &env :ns :name) + ns-name (core/-> &env :ns :name) fqn (core/fn [n] (symbol (core/str ns-name "." n))) prefix (protocol-prefix p) _ (core/doseq [[mname & arities] methods] @@ -1605,7 +1609,7 @@ {} (partition 2 clauses)) esym (gensym) tests (keys pairs)] - (cond + (core/cond (every? (some-fn core/number? core/string? core/char? #(const? env %)) tests) (core/let [no-default (if (odd? (count clauses)) (butlast clauses) clauses) tests (mapv #(if (seq? %) (vec %) [%]) (take-nth 2 no-default)) @@ -1613,7 +1617,7 @@ `(let [~esym ~e] (case* ~esym ~tests ~thens ~default))) (every? core/keyword? tests) - (core/let [tests (->> tests + (core/let [tests (core/->> tests (map #(.substring (core/str %) 1)) vec (mapv #(if (seq? %) (vec %) [%]))) @@ -1786,7 +1790,7 @@ (nth (step nil (seq seq-exprs)) 1))) (core/defmacro array [& rest] - (core/let [xs-str (->> (repeat "~{}") + (core/let [xs-str (core/->> (repeat "~{}") (take (count rest)) (interpose ",") (apply core/str))] @@ -1853,7 +1857,7 @@ assoc :tag 'cljs.core/PersistentHashSet)))) (core/defn- js-obj* [kvs] - (core/let [kvs-str (->> (repeat "~{}:~{}") + (core/let [kvs-str (core/->> (repeat "~{}:~{}") (take (count kvs)) (interpose ",") (apply core/str))] @@ -1864,7 +1868,7 @@ (core/defmacro js-obj [& rest] (core/let [sym-or-str? (core/fn [x] (core/or (core/symbol? x) (core/string? x))) filter-on-keys (core/fn [f coll] - (->> coll + (core/->> coll (filter (core/fn [[k _]] (f k))) (into {}))) kvs (into {} (map vec (partition 2 rest))) @@ -1963,7 +1967,7 @@ m (if (meta mm-name) (conj (meta mm-name) m) m) - mm-ns (-> &env :ns :name core/str)] + mm-ns (core/-> &env :ns :name core/str)] (core/when (= (count options) 1) (throw #?(:clj (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)") @@ -2185,7 +2189,7 @@ (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))))) ~(variadic-fn* rname method))))) -(comment +(core/comment (require '[clojure.pprint :as pp]) (pp/pprint (variadic-fn 'foo {} '(([& xs])))) (pp/pprint (variadic-fn 'foo {} '(([a & xs] xs)))) @@ -2292,20 +2296,24 @@ (butlast fdecl) fdecl) m (conj {:arglists (core/list 'quote (sigs fdecl))} m) - m (core/let [inline (:inline m) - ifn (first inline) - iname (second inline)] - ;; same as: (if (and (= 'fn ifn) (not (symbol? iname))) ...) - (if (if #?(:clj (clojure.lang.Util/equiv 'fn ifn) - :cljs (= 'fn ifn)) - (if #?(:clj (core/instance? clojure.lang.Symbol iname) - :cljs (core/instance? Symbol iname)) false true)) - ;; inserts the same fn name to the inline fn if it does not have one - (assoc m :inline (cons ifn (cons (clojure.lang.Symbol/intern (.concat (.getName ^clojure.lang.Symbol name) "__inliner")) - (next inline)))) - m)) + ;; no support for :inline + ;m (core/let [inline (:inline m) + ; ifn (first inline) + ; iname (second inline)] + ; ;; same as: (if (and (= 'fn ifn) (not (symbol? iname))) ...) + ; (if (if #?(:clj (clojure.lang.Util/equiv 'fn ifn) + ; :cljs (= 'fn ifn)) + ; (if #?(:clj (core/instance? clojure.lang.Symbol iname) + ; :cljs (core/instance? Symbol iname)) false true)) + ; ;; inserts the same fn name to the inline fn if it does not have one + ; (assoc m + ; :inline (cons ifn + ; (cons (clojure.lang.Symbol/intern + ; (.concat (.getName ^clojure.lang.Symbol name) "__inliner")) + ; (next inline)))) + ; m)) m (conj (if (meta name) (meta name) {}) m)] - (cond + (core/cond (multi-arity-fn? fdecl) (multi-arity-fn name m fdecl) From 9cc005361947e284ad0fef3587b03c776d7a5132 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Jun 2015 09:24:02 -0400 Subject: [PATCH 1161/4033] more bootstrapping changes. Need to declare *assert* in the standard lib. --- src/main/cljs/cljs/core.cljs | 4 + src/main/clojure/cljs/core.cljc | 155 ++++++++++++++++---------------- 2 files changed, 82 insertions(+), 77 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9143076e8..f213b61c9 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -39,6 +39,10 @@ ^{:dynamic true} *err* nil) +(def + ^{:dynamic true} + *assert* true) + (defonce ^{:doc "Each runtime environment provides a different way to print output. Whatever function *print-fn* is bound to will be passed any diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3c8bc5d57..127a0e52c 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -39,7 +39,8 @@ truth_ js-arguments js-delete js-in js-debugger exists? divide js-mod unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash defcurried rfn specify! js-this this-as implements? array js-obj - simple-benchmark gen-apply-to js-str es6-iterable load-file*])]) + simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? + specify])]) #?(:cljs (:require-macros [cljs.core :as core])) (:require clojure.walk clojure.set @@ -396,7 +397,7 @@ ;; internal - do not use. (core/defmacro truth_ [x] - (assert (clojure.core/symbol? x) "x is substituted twice") + (core/assert (core/symbol? x) "x is substituted twice") (core/list 'js* "(~{} != null && ~{} !== false)" x x)) ;; internal - do not use @@ -836,7 +837,7 @@ (core/let [ret (core/-> (dissoc env :locals) (cljs.analyzer/resolve-var sym) :name)] - (assert ret (core/str "Can't resolve: " sym)) + (core/assert ret (core/str "Can't resolve: " sym)) ret)) (core/defn- ->impl-map [impls] @@ -1022,7 +1023,7 @@ (core/let [fpps (into #{} (filter (partial contains? fast-path-protocols) (map resolve (keys impl-map)))) - parts (as-> (group-by first fpp-pbs) parts + parts (core/as-> (group-by first fpp-pbs) parts (into {} (map (juxt key (comp (partial map peek) val)) parts)) @@ -1063,8 +1064,8 @@ (core/defn- build-positional-factory [rsym rname fields] - (let [fn-name (with-meta (symbol (core/str '-> rsym)) - (assoc (meta rsym) :factory :positional)) + (core/let [fn-name (with-meta (symbol (core/str '-> rsym)) + (assoc (meta rsym) :factory :positional)) field-values (if (core/-> rsym meta :internal-ctor) (conj fields nil nil nil) fields)] `(defn ~fn-name [~@fields] @@ -1378,14 +1379,14 @@ (into {} (map (core/fn [[fname & sigs]] - (core/let [doc (as-> (last sigs) doc + (core/let [doc (core/as-> (last sigs) doc (core/when (core/string? doc) doc)) sigs (take-while vector? sigs)] [(vary-meta fname assoc :doc doc) (vec sigs)])) methods))) method (core/fn [[fname & sigs]] - (core/let [doc (as-> (last sigs) doc + (core/let [doc (core/as-> (last sigs) doc (core/when (core/string? doc) doc)) sigs (take-while vector? sigs) slot (symbol (core/str prefix (name fname))) @@ -1522,9 +1523,9 @@ (core/let [gpred (gensym "pred__") gexpr (gensym "expr__") emit (core/fn emit [pred expr args] - (let [[[a b c :as clause] more] - (split-at (if (= :>> (second args)) 3 2) args) - n (count clause)] + (core/let [[[a b c :as clause] more] + (split-at (if (= :>> (second args)) 3 2) args) + n (count clause)] (core/cond (= 0 n) `(throw (js/Error. (core/str "No matching clause: " ~expr))) (= 1 n) a @@ -1597,9 +1598,9 @@ (seq? test) (reduce (core/fn [m test] - (let [test (if (core/symbol? test) - (core/list 'quote test) - test)] + (core/let [test (if (core/symbol? test) + (core/list 'quote test) + test)] (assoc-test m test expr env))) m test) (core/symbol? test) @@ -1743,7 +1744,7 @@ (even? (count seq-exprs)) "an even number of forms in binding vector") (core/let [err (core/fn [& msg] (throw (ex-info (apply core/str msg) {}))) step (core/fn step [recform exprs] - (if-not exprs + (core/if-not exprs [true `(do ~@body)] (core/let [k (first exprs) v (second exprs) @@ -1764,13 +1765,13 @@ ~@(core/when needrec [recform])) ~recform)] (core/keyword? k) (err "Invalid 'doseq' keyword" k) - :else (let [chunksym (with-meta (gensym "chunk__") - {:tag 'not-native}) - countsym (gensym "count__") - isym (gensym "i__") - recform-chunk `(recur ~seqsym ~chunksym ~countsym (unchecked-inc ~isym)) - steppair-chunk (step recform-chunk (nnext exprs)) - subform-chunk (steppair-chunk 1)] + :else (core/let [chunksym (with-meta (gensym "chunk__") + {:tag 'not-native}) + countsym (gensym "count__") + isym (gensym "i__") + recform-chunk `(recur ~seqsym ~chunksym ~countsym (unchecked-inc ~isym)) + steppair-chunk (step recform-chunk (nnext exprs)) + subform-chunk (steppair-chunk 1)] [true `(loop [~seqsym (seq ~v) ~chunksym nil ~countsym 0 @@ -2138,23 +2139,23 @@ ([sym [arglist & body :as method] solo] (core/let [sig (remove '#{&} arglist) restarg (gensym "seq")] - (letfn [(get-delegate [] - 'cljs$core$IFn$_invoke$arity$variadic) - (get-delegate-prop [] - (symbol (core/str "-" (get-delegate)))) - (param-bind [param] - `[~param (^::ana/no-resolve first ~restarg) - ~restarg (^::ana/no-resolve next ~restarg)]) - (apply-to [] - (if (core/< 1 (count sig)) - (let [params (repeatedly (core/dec (count sig)) gensym)] - `(fn - ([~restarg] - (let [~@(mapcat param-bind params)] - (. ~sym (~(get-delegate) ~@params ~restarg)))))) - `(fn - ([~restarg] - (. ~sym (~(get-delegate) (seq ~restarg)))))))] + (core/letfn [(get-delegate [] + 'cljs$core$IFn$_invoke$arity$variadic) + (get-delegate-prop [] + (symbol (core/str "-" (get-delegate)))) + (param-bind [param] + `[~param (^::ana/no-resolve first ~restarg) + ~restarg (^::ana/no-resolve next ~restarg)]) + (apply-to [] + (if (core/< 1 (count sig)) + (core/let [params (repeatedly (core/dec (count sig)) gensym)] + `(fn + ([~restarg] + (let [~@(mapcat param-bind params)] + (. ~sym (~(get-delegate) ~@params ~restarg)))))) + `(fn + ([~restarg] + (. ~sym (~(get-delegate) (seq ~restarg)))))))] `(do (set! (. ~sym ~(get-delegate-prop)) (fn (~(vec sig) ~@body))) @@ -2198,23 +2199,23 @@ ) (core/defn- multi-arity-fn [name meta fdecl] - (letfn [(dest-args [c] - (map (core/fn [n] `(aget (js-arguments) ~n)) - (range c))) - (fixed-arity [rname sig] - (let [c (count sig)] - [c `(. ~rname - (~(symbol - (core/str "cljs$core$IFn$_invoke$arity$" c)) - ~@(dest-args c)))])) - (fn-method [[sig & body :as method]] - (if (some '#{&} sig) - (variadic-fn* name method false) - `(set! - (. ~name - ~(symbol (core/str "-cljs$core$IFn$_invoke$arity$" - (count sig)))) - (fn ~method))))] + (core/letfn [(dest-args [c] + (map (core/fn [n] `(aget (js-arguments) ~n)) + (range c))) + (fixed-arity [rname sig] + (let [c (count sig)] + [c `(. ~rname + (~(symbol + (core/str "cljs$core$IFn$_invoke$arity$" c)) + ~@(dest-args c)))])) + (fn-method [[sig & body :as method]] + (if (some '#{&} sig) + (variadic-fn* name method false) + `(set! + (. ~name + ~(symbol (core/str "-cljs$core$IFn$_invoke$arity$" + (count sig)))) + (fn ~method))))] (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) arglists (map first fdecl) varsig? #(some '#{&} %) @@ -2231,27 +2232,27 @@ :method-params sigs :arglists arglists :arglists-meta (doall (map meta arglists))})] - `(do - (def ~(with-meta name meta) - (fn [] - (case (alength (js-arguments)) - ~@(mapcat #(fixed-arity rname %) sigs) - ~(if variadic - `(let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq - (.call js/Array.prototype.slice - (js-arguments) ~maxfa) 0)] - (. ~rname - (~'cljs$core$IFn$_invoke$arity$variadic - ~@(dest-args maxfa) - argseq#))) - `(throw (js/Error. - (str "Invalid arity: " - (alength (js-arguments))))))))) - ~@(map fn-method fdecl) - ;; optimization properties - (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa))))) - -(comment + `(do + (def ~(with-meta name meta) + (fn [] + (case (alength (js-arguments)) + ~@(mapcat #(fixed-arity rname %) sigs) + ~(if variadic + `(let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq + (.call js/Array.prototype.slice + (js-arguments) ~maxfa) 0)] + (. ~rname + (~'cljs$core$IFn$_invoke$arity$variadic + ~@(dest-args maxfa) + argseq#))) + `(throw (js/Error. + (str "Invalid arity: " + (alength (js-arguments))))))))) + ~@(map fn-method fdecl) + ;; optimization properties + (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa))))) + +(core/comment (require '[clojure.pprint :as pp]) (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a b])))) (pp/pprint (multi-arity-fn 'foo {} '(([a]) ([a & xs])))) From 6f8cbc2523530313f658d0faaeaf181afd4b7fdc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Jun 2015 09:34:13 -0400 Subject: [PATCH 1162/4033] missing macro changes --- src/main/clojure/cljs/core.cljc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 127a0e52c..0ce6b1bfa 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -670,7 +670,7 @@ ;; internal (core/defmacro caching-hash [coll hash-fn hash-key] - (assert (clojure.core/symbol? hash-key) "hash-key is substituted twice") + (core/assert (clojure.core/symbol? hash-key) "hash-key is substituted twice") `(let [h# ~hash-key] (if-not (nil? h#) h# @@ -2166,9 +2166,9 @@ ~(apply-to))))))) (core/defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl]] - (letfn [(dest-args [c] - (map (core/fn [n] `(aget (js-arguments) ~n)) - (range c)))] + (core/letfn [(dest-args [c] + (map (core/fn [n] `(aget (js-arguments) ~n)) + (range c)))] (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) sig (remove '#{&} arglist) c-1 (core/dec (count sig)) @@ -2184,8 +2184,8 @@ (fn [] (let [argseq# (when (< ~c-1 (alength (js-arguments))) (new ^::ana/no-resolve cljs.core/IndexedSeq - (.call js/Array.prototype.slice - (js-arguments) ~c-1) 0))] + (.call js/Array.prototype.slice + (js-arguments) ~c-1) 0))] (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))))) ~(variadic-fn* rname method))))) @@ -2203,7 +2203,7 @@ (map (core/fn [n] `(aget (js-arguments) ~n)) (range c))) (fixed-arity [rname sig] - (let [c (count sig)] + (core/let [c (count sig)] [c `(. ~rname (~(symbol (core/str "cljs$core$IFn$_invoke$arity$" c)) From 4d8e9920b075b85e9083763e4f9d49a081e3b0dc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Jun 2015 10:30:07 -0400 Subject: [PATCH 1163/4033] *e has been implemented in the REPL for some time --- devnotes/corelib.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devnotes/corelib.org b/devnotes/corelib.org index 391761e1f..071649d35 100644 --- a/devnotes/corelib.org +++ b/devnotes/corelib.org @@ -10,7 +10,7 @@ * *command-line-args* * *compile-files* * *compile-path* -* TODO *e +* DONE *e * *err* * *file* * *flush-on-newline* From 50be919ece5e95584a9a49a0b874598c511215db Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Jun 2015 13:47:15 -0400 Subject: [PATCH 1164/4033] port over all macros for bootstrapping CLJS JS that were being loaded via import-macros for CLJS JVM. Need to sort out why `..` is problematic. --- src/main/clojure/cljs/core.cljc | 535 ++++++++++++++++++++++++++++---- 1 file changed, 475 insertions(+), 60 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 0ce6b1bfa..de3190b29 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -109,6 +109,421 @@ (recur threaded (next forms))) x)))) +#?(:cljs + (core/defmacro ->> + "Threads the expr through the forms. Inserts x as the + last item in the first form, making a list of it if it is not a + list already. If there are more forms, inserts the first form as the + last item in second form, etc." + [x & forms] + (core/loop [x x, forms forms] + (if forms + (core/let [form (first forms) + threaded (if (seq? form) + (with-meta `(~(first form) ~@(next form) ~x) (meta form)) + (core/list form x))] + (recur threaded (next forms))) + x)))) + +;#?(:cljs +; (core/defmacro .. +; "form => fieldName-symbol or (instanceMethodName-symbol args*) +; +; Expands into a member access (.) of the first member on the first +; argument, followed by the next member on the result, etc. For +; instance: +; +; (.. System (getProperties) (get \"os.name\")) +; +; expands to: +; +; (. (. System (getProperties)) (get \"os.name\")) +; +; but is easier to write, read, and understand." +; ([x form] `(. ~x ~form)) +; ([x form & more] `(.. (. ~x ~form) ~@more)))) + +#?(:cljs + (core/defmacro comment + "Ignores body, yields nil" + [& body])) + +#?(:cljs + (core/defmacro cond + "Takes a set of test/expr pairs. It evaluates each test one at a + time. If a test returns logical true, cond evaluates and returns + the value of the corresponding expr and doesn't evaluate any of the + other tests or exprs. (cond) returns nil." + {:added "1.0"} + [& clauses] + (core/when clauses + (core/list 'if (first clauses) + (if (next clauses) + (second clauses) + (throw (js/Error. "cond requires an even number of forms"))) + (cons 'cljs.core/cond (next (next clauses))))))) + +#?(:cljs + (core/defmacro declare + "defs the supplied var names with no bindings, useful for making forward declarations." + [& names] `(do ~@(map #(core/list 'def (vary-meta % assoc :declared true)) names)))) + +#?(:cljs + (core/defmacro doto + "Evaluates x then calls all of the methods and functions with the + value of x supplied at the front of the given arguments. The forms + are evaluated in order. Returns x. + + (doto (new java.util.HashMap) (.put \"a\" 1) (.put \"b\" 2))" + [x & forms] + (core/let [gx (gensym)] + `(let [~gx ~x] + ~@(map (core/fn [f] + (if (seq? f) + `(~(first f) ~gx ~@(next f)) + `(~f ~gx))) + forms) + ~gx)))) + +#?(:cljs + (core/defn- parse-impls [specs] + (core/loop [ret {} s specs] + (if (seq s) + (recur (assoc ret (first s) (take-while seq? (next s))) + (drop-while seq? (next s))) + ret)))) + +#?(:cljs + (core/defn- emit-extend-protocol [p specs] + (core/let [impls (parse-impls specs)] + `(do + ~@(map (core/fn [[t fs]] + `(extend-type ~t ~p ~@fs)) + impls))))) + +#?(:cljs + (core/defmacro extend-protocol + "Useful when you want to provide several implementations of the same + protocol all at once. Takes a single protocol and the implementation + of that protocol for one or more types. Expands into calls to + extend-type: + + (extend-protocol Protocol + AType + (foo [x] ...) + (bar [x y] ...) + BType + (foo [x] ...) + (bar [x y] ...) + AClass + (foo [x] ...) + (bar [x y] ...) + nil + (foo [x] ...) + (bar [x y] ...)) + + expands into: + + (do + (clojure.core/extend-type AType Protocol + (foo [x] ...) + (bar [x y] ...)) + (clojure.core/extend-type BType Protocol + (foo [x] ...) + (bar [x y] ...)) + (clojure.core/extend-type AClass Protocol + (foo [x] ...) + (bar [x y] ...)) + (clojure.core/extend-type nil Protocol + (foo [x] ...) + (bar [x y] ...)))" + [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) + (-> lets (conj (first params)) (conj gparam))))) + `(~new-params + (let ~lets + ~@body))))))) + +#?(:cljs + (core/defmacro fn + "params => positional-params* , or positional-params* & next-param + positional-param => binding-form + next-param => binding-form + name => symbol + + Defines a function" + {: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] + ;; Ensure correct type before destructuring sig + (core/when (not (seq? sig)) + (throw (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"))))) + conds (core/when (core/and (next body) (map? (first body))) + (first body)) + body (if conds (next body) body) + conds (core/or conds (meta params)) + pre (:pre conds) + post (:post conds) + body (if post + `((let [~'% ~(if (core/< 1 (count body)) + `(do ~@body) + (first body))] + ~@(map (fn* [c] `(assert ~c)) post) + ~'%)) + body) + body (if pre + (concat (map (fn* [c] `(assert ~c)) pre) + 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))))) + +#?(:cljs + (core/defmacro if-let + "bindings => binding-form test + + If test is true, evaluates then with binding-form bound to the value of + test, if not, yields else" + ([bindings then] + `(if-let ~bindings ~then nil)) + ([bindings then else & oldform] + (core/assert-args + (vector? bindings) "a vector for its binding" + (nil? oldform) "1 or 2 forms after binding vector" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (core/let [form (bindings 0) tst (bindings 1)] + `(let [temp# ~tst] + (if temp# + (let [~form temp#] + ~then) + ~else)))))) + +#?(:cljs + (core/defmacro if-not + "Evaluates test. If logical false, evaluates and returns then expr, + otherwise else expr, if supplied, else nil." + ([test then] `(if-not ~test ~then nil)) + ([test then else] + `(if (not ~test) ~then ~else)))) + +#?(:cljs + (core/defmacro letfn + "fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+) + + Takes a vector of function specs and a body, and generates a set of + bindings of functions to their names. All of the names are available + in all of the definitions of the functions, as well as the body." + {:forms '[(letfn [fnspecs*] exprs*)], + :special-form true, :url nil} + [fnspecs & body] + `(letfn* ~(vec (interleave (map first fnspecs) + (map #(cons `fn %) fnspecs))) + ~@body))) + +#?(:cljs + (core/defmacro memfn + "Expands into code that creates a fn that expects to be passed an + object and any args and calls the named instance method on the + object passing the args. Use when you want to treat a Java method as + a first-class fn. name may be type-hinted with the method receiver's + type in order to avoid reflective calls." + [name & args] + (core/let [t (with-meta (gensym "target") + (meta name))] + `(fn [~t ~@args] + (. ~t (~name ~@args)))))) + +#?(:cljs + (core/defmacro when + "Evaluates test. If logical true, evaluates body in an implicit do." + [test & body] + (core/list 'if test (cons 'do body)))) + +#?(:cljs + (core/defmacro when-first + "bindings => x xs + + Roughly the same as (when (seq xs) (let [x (first xs)] body)) but xs is evaluated only once" + [bindings & body] + (core/assert-args + (vector? bindings) "a vector for its binding" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (core/let [[x xs] bindings] + `(when-let [xs# (seq ~xs)] + (let [~x (first xs#)] + ~@body))))) + +#?(:cljs + (core/defmacro when-let + "bindings => binding-form test + + When test is true, evaluates body with binding-form bound to the value of test" + [bindings & body] + (core/assert-args + (vector? bindings) "a vector for its binding" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (core/let [form (bindings 0) tst (bindings 1)] + `(let [temp# ~tst] + (when temp# + (let [~form temp#] + ~@body)))))) + +#?(:cljs + (core/defmacro when-not + "Evaluates test. If logical false, evaluates body in an implicit do." + [test & body] + (core/list 'if test nil (cons 'do body)))) + +#?(:cljs + (core/defmacro while + "Repeatedly executes body while test expression is true. Presumes + some side-effect will cause test to become false/nil. Returns nil" + [test & body] + `(loop [] + (when ~test + ~@body + (recur))))) + +#?(:cljs + (core/defmacro cond-> + "Takes an expression and a set of test/form pairs. Threads expr (via ->) + through each form for which the corresponding test + expression is true. Note that, unlike cond branching, cond-> threading does + not short circuit after the first true test expression." + [expr & clauses] + (core/assert (even? (count clauses))) + (core/let [g (gensym) + pstep (core/fn [[test step]] `(if ~test (-> ~g ~step) ~g))] + `(let [~g ~expr + ~@(interleave (repeat g) (map pstep (partition 2 clauses)))] + ~g)))) + +#?(:cljs + (core/defmacro cond->> + "Takes an expression and a set of test/form pairs. Threads expr (via ->>) + through each form for which the corresponding test expression + is true. Note that, unlike cond branching, cond->> threading does not short circuit + after the first true test expression." + [expr & clauses] + (core/assert (even? (count clauses))) + (core/let [g (gensym) + pstep (core/fn [[test step]] `(if ~test (->> ~g ~step) ~g))] + `(let [~g ~expr + ~@(interleave (repeat g) (map pstep (partition 2 clauses)))] + ~g)))) + +#?(:cljs + (core/defmacro as-> + "Binds name to expr, evaluates the first form in the lexical context + of that binding, then binds name to that result, repeating for each + successive form, returning the result of the last form." + [expr name & forms] + `(let [~name ~expr + ~@(interleave (repeat name) forms)] + ~name))) + +#?(:cljs + (core/defmacro some-> + "When expr is not nil, threads it into the first form (via ->), + and when that result is not nil, through the next etc" + [expr & forms] + (core/let [g (gensym) + pstep (core/fn [step] `(if (nil? ~g) nil (-> ~g ~step)))] + `(let [~g ~expr + ~@(interleave (repeat g) (map pstep forms))] + ~g)))) + +#?(:cljs + (core/defmacro some->> + "When expr is not nil, threads it into the first form (via ->>), + and when that result is not nil, through the next etc" + [expr & forms] + (core/let [g (gensym) + pstep (core/fn [step] `(if (nil? ~g) nil (->> ~g ~step)))] + `(let [~g ~expr + ~@(interleave (repeat g) (map pstep forms))] + ~g)))) + +#?(:cljs + (core/defmacro if-some + "bindings => binding-form test + + If test is not nil, evaluates then with binding-form bound to the + value of test, if not, yields else" + ([bindings then] + `(if-some ~bindings ~then nil)) + ([bindings then else & oldform] + (core/assert-args + (vector? bindings) "a vector for its binding" + (nil? oldform) "1 or 2 forms after binding vector" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (core/let [form (bindings 0) tst (bindings 1)] + `(let [temp# ~tst] + (if (nil? temp#) + ~else + (let [~form temp#] + ~then))))))) + +#?(:cljs + (core/defmacro when-some + "bindings => binding-form test + + When test is not nil, evaluates body with binding-form bound to the + value of test" + [bindings & body] + (core/assert-args + (vector? bindings) "a vector for its binding" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (core/let [form (bindings 0) tst (bindings 1)] + `(let [temp# ~tst] + (if (nil? temp#) + nil + (let [~form temp#] + ~@body)))))) + (core/defn- ^{:dynamic true} assert-valid-fdecl "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn." [fdecl] @@ -195,58 +610,58 @@ (core/defn destructure [bindings] (core/let [bents (partition 2 bindings) - pb (core/fn pb [bvec b v] - (core/let [pvec - (core/fn [bvec b val] - (core/let [gvec (gensym "vec__")] - (core/loop [ret (core/-> bvec (conj gvec) (conj val)) - n 0 - bs b - seen-rest? false] - (if (seq bs) - (core/let [firstb (first bs)] - (core/cond - (= firstb '&) (recur (pb ret (second bs) (core/list `nthnext gvec n)) - n - (nnext bs) - true) - (= firstb :as) (pb ret (second bs) gvec) - :else (if seen-rest? - (throw - #?(:clj (new Exception "Unsupported binding form, only :as can follow & parameter") - :cljs (new js/Error "Unsupported binding form, only :as can follow & parameter"))) - (recur (pb ret firstb (core/list `nth gvec n nil)) - (core/inc n) - (next bs) - seen-rest?)))) - ret)))) - pmap - (core/fn [bvec b v] - (core/let [gmap (gensym "map__") - defaults (:or b)] - (core/loop [ret (core/-> bvec (conj gmap) (conj v) - (conj gmap) (conj `(if (seq? ~gmap) (apply core/hash-map ~gmap) ~gmap)) - ((core/fn [ret] - (if (:as b) - (conj ret (:as b) gmap) - ret)))) - bes (reduce - (core/fn [bes entry] - (reduce #(assoc %1 %2 ((val entry) %2)) - (dissoc bes (key entry)) - ((key entry) bes))) - (dissoc b :as :or) - {:keys #(if (core/keyword? %) % (keyword (core/str %))), - :strs core/str, :syms #(core/list `quote %)})] - (if (seq bes) - (core/let [bb (key (first bes)) - bk (val (first bes)) - has-default (contains? defaults bb)] - (recur (pb ret bb (if has-default - (core/list `get gmap bk (defaults bb)) - (core/list `get gmap bk))) - (next bes))) - ret))))] + pb (core/fn pb [bvec b v] + (core/let [pvec + (core/fn [bvec b val] + (core/let [gvec (gensym "vec__")] + (core/loop [ret (core/-> bvec (conj gvec) (conj val)) + n 0 + bs b + seen-rest? false] + (if (seq bs) + (core/let [firstb (first bs)] + (core/cond + (= firstb '&) (recur (pb ret (second bs) (core/list `nthnext gvec n)) + n + (nnext bs) + true) + (= firstb :as) (pb ret (second bs) gvec) + :else (if seen-rest? + (throw + #?(:clj (new Exception "Unsupported binding form, only :as can follow & parameter") + :cljs (new js/Error "Unsupported binding form, only :as can follow & parameter"))) + (recur (pb ret firstb (core/list `nth gvec n nil)) + (core/inc n) + (next bs) + seen-rest?)))) + ret)))) + pmap + (core/fn [bvec b v] + (core/let [gmap (gensym "map__") + defaults (:or b)] + (core/loop [ret (core/-> bvec (conj gmap) (conj v) + (conj gmap) (conj `(if (seq? ~gmap) (apply core/hash-map ~gmap) ~gmap)) + ((core/fn [ret] + (if (:as b) + (conj ret (:as b) gmap) + ret)))) + bes (reduce + (core/fn [bes entry] + (reduce #(assoc %1 %2 ((val entry) %2)) + (dissoc bes (key entry)) + ((key entry) bes))) + (dissoc b :as :or) + {:keys #(if (core/keyword? %) % (keyword (core/str %))), + :strs core/str, :syms #(core/list `quote %)})] + (if (seq bes) + (core/let [bb (key (first bes)) + bk (val (first bes)) + has-default (contains? defaults bb)] + (recur (pb ret bb (if has-default + (core/list `get gmap bk (defaults bb)) + (core/list `get gmap bk))) + (next bes))) + ret))))] (core/cond (core/symbol? b) (core/-> bvec (conj (if (namespace b) (symbol (name b)) b)) (conj v)) (core/keyword? b) (core/-> bvec (conj (symbol (name b))) (conj v)) @@ -255,14 +670,14 @@ :else (throw #?(:clj (new Exception (core/str "Unsupported binding form: " b)) :cljs (new js/Error (core/str "Unsupported binding form: " b))))))) - process-entry (core/fn [bvec b] (pb bvec (first b) (second b)))] - (if (every? core/symbol? (map first bents)) - bindings - (core/if-let [kwbs (seq (filter #(core/keyword? (first %)) bents))] - (throw - #?(:clj (new Exception (core/str "Unsupported binding key: " (ffirst kwbs))) - :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs))))) - (reduce process-entry [] bents))))) + process-entry (core/fn [bvec b] (pb bvec (first b) (second b)))] + (if (every? core/symbol? (map first bents)) + bindings + (core/if-let [kwbs (seq (filter #(core/keyword? (first %)) bents))] + (throw + #?(:clj (new Exception (core/str "Unsupported binding key: " (ffirst kwbs))) + :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs))))) + (reduce process-entry [] bents))))) (core/defmacro let "binding => binding-form init-expr From 6b1c53635947331ee82ce6feebcfa1bf763e0a87 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Jun 2015 16:12:00 -0400 Subject: [PATCH 1165/4033] port clojure.core/.., modify cljs.analyzer/confirm-var-exists for this case --- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/main/clojure/cljs/core.cljc | 34 ++++++++++++++--------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index bafdcdbdf..3824ce450 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -466,7 +466,8 @@ ([env prefix suffix missing-fn] (let [sufstr (str suffix) suffix (symbol - (if (re-find #"\." sufstr) + ;; leave cljs.core$macros/.. alone + (if (and (not= ".." sufstr) (re-find #"\." sufstr)) (first (string/split sufstr #"\.")) suffix))] (when (and (not (implicit-import? env prefix suffix)) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index de3190b29..439bdcbb1 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -125,23 +125,23 @@ (recur threaded (next forms))) x)))) -;#?(:cljs -; (core/defmacro .. -; "form => fieldName-symbol or (instanceMethodName-symbol args*) -; -; Expands into a member access (.) of the first member on the first -; argument, followed by the next member on the result, etc. For -; instance: -; -; (.. System (getProperties) (get \"os.name\")) -; -; expands to: -; -; (. (. System (getProperties)) (get \"os.name\")) -; -; but is easier to write, read, and understand." -; ([x form] `(. ~x ~form)) -; ([x form & more] `(.. (. ~x ~form) ~@more)))) +#?(:cljs + (core/defmacro .. + "form => fieldName-symbol or (instanceMethodName-symbol args*) + + Expands into a member access (.) of the first member on the first + argument, followed by the next member on the result, etc. For + instance: + + (.. System (getProperties) (get \"os.name\")) + + expands to: + + (. (. System (getProperties)) (get \"os.name\")) + + but is easier to write, read, and understand." + ([x form] `(. ~x ~form)) + ([x form & more] `(.. (. ~x ~form) ~@more)))) #?(:cljs (core/defmacro comment From d9c7ac9e60e2f6336267193456655e54c206352c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Jun 2015 20:35:06 -0400 Subject: [PATCH 1166/4033] typo --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 439bdcbb1..3989608e2 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -290,7 +290,7 @@ " should be a list")))) (core/let [[params & body] sig _ (core/when (not (vector? params)) - (throw (js/Error.. + (throw (js/Error. (if (seq? (first sigs)) (core/str "Parameter declaration " params " should be a vector") From 73cca5bf6f5fbc67d1dd6d9109600985803d139d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 20 Jun 2015 13:08:37 -0400 Subject: [PATCH 1167/4033] conditionalize setMacro call --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3989608e2..e8bc02f04 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2742,7 +2742,7 @@ ;;must figure out how to convey primitive hints to self calls first (cons `fn fdecl)))))) -(. (var defn) (setMacro)) +#?(:clj (. (var defn) (setMacro))) (def ^{:doc "Like defn, but the resulting function name is declared as a From f240826034bb5f9ddd53e68a6c3be4e740070470 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 20 Jun 2015 13:57:39 -0400 Subject: [PATCH 1168/4033] fix syntax-quote var resolution by rebinding cljs.tools.reader/resolve-symbol --- src/main/clojure/cljs/analyzer.cljc | 6 +++++- src/main/clojure/cljs/repl.cljc | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3824ce450..141ee4aa1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2121,6 +2121,9 @@ (instance? File x) (.getAbsolutePath ^File x) :default (str x)))) +(defn resolve-symbol [s] + (:name (resolve-existing-var {:ns {:name *cljs-ns*}} s))) + #?(:clj (defn forms-seq* "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally @@ -2144,7 +2147,8 @@ reader/*alias-map* (apply merge ((juxt :requires :require-macros) - (get-namespace *cljs-ns*)))] + (get-namespace *cljs-ns*))) + reader/resolve-symbol resolve-symbol] (reader/read opts pbr))] (if (identical? form eof-sentinel) (.close rdr) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index d0f09f39d..018550ab2 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -807,6 +807,7 @@ read-eval-print (fn [] (let [input (binding [*ns* (create-ns ana/*cljs-ns*) + reader/resolve-symbol ana/resolve-symbol reader/*data-readers* tags/*cljs-data-readers* reader/*alias-map* (apply merge From 9e149cc95e5cbe1245ffa0d4b51aaef12fefee3c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 20 Jun 2015 14:40:35 -0400 Subject: [PATCH 1169/4033] want resolve-var not resolve-existing var for reader symbol resolution --- src/main/clojure/cljs/analyzer.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 141ee4aa1..b0e5268ff 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -14,7 +14,7 @@ :refer [no-warn wrapping-errors disallowing-recur allowing-redef]] [cljs.env.macros :refer [ensure]])) - #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] + #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] @@ -2122,7 +2122,7 @@ :default (str x)))) (defn resolve-symbol [s] - (:name (resolve-existing-var {:ns {:name *cljs-ns*}} s))) + (:name (resolve-var {:ns {:name *cljs-ns*}} s))) #?(:clj (defn forms-seq* From 2c139ec031b83aa19eb975df3f499c729c956992 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 20 Jun 2015 15:58:41 -0400 Subject: [PATCH 1170/4033] special case .. in compiler munging functions, add tests --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- src/main/clojure/cljs/compiler.cljc | 6 ++++-- src/test/clojure/cljs/compiler_tests.clj | 24 ++++++++++++++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b0e5268ff..e47961b0c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -540,8 +540,10 @@ {:name (symbol (str full-ns) (str (name sym))) :ns full-ns})) - #?(:clj (.contains s ".") - :cljs (goog.string/contains s ".")) + #?(:clj (and (.contains s ".") + (not (.contains s ".."))) + :cljs (and (goog.string/contains s ".") + (not (goog.string/contains s "..")))) (let [idx (.indexOf s ".") prefix (symbol (subs s 0 idx)) suffix (subs s (inc idx)) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index dcaf4438b..1966ba469 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -57,7 +57,8 @@ (declare munge) (defn fn-self-name [{:keys [name info] :as name-var}] - (let [{:keys [ns fn-scope]} info + (let [name (string/replace (str name) ".." "_DOT__DOT_") + {:keys [ns fn-scope]} info scoped-name (apply str (interpose "_$_" (concat (map (comp str :name) fn-scope) [name])))] @@ -86,7 +87,8 @@ munged-name (symbol (str munged-name "__$" depth)))))) ;; String munging - (let [ss (string/replace (str s) + (let [ss (string/replace (str s) ".." "_DOT__DOT_") + ss (string/replace ss #?(:clj #"\/(.)" :cljs (js/RegExp. "\\/(.)")) ".$1") ; Division is special ss (apply str (map #(if (reserved %) (str % "$") %) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index fdf7a87b4..fa199d69e 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -48,10 +48,26 @@ 'cljs$user$console))) (deftest test-js-negative-infinity - (= (with-out-str - (comp/emit - (ana/analyze (assoc aenv :context :expr) 'js/-Infinity))) - "-Infinity")) + (is (= (with-out-str + (comp/emit + (ana/analyze (assoc aenv :context :expr) 'js/-Infinity))) + "-Infinity"))) + +(deftest test-munge-dotdot + (is (= 'cljs.core._DOT__DOT_ (comp/munge 'cljs.core/..))) + (is (= "cljs.core._DOT__DOT_" (comp/munge "cljs.core/.."))) + (is (= 'cljs.core._DOT__DOT_ + (ana/no-warn + (env/with-compiler-env cenv + (comp/munge + (:info (ana/analyze {:ns {:name 'cljs.core}} 'cljs.core/..)))))))) + +(deftest test-resolve-dotdot + (is (= '{:name cljs.core/.. + :ns cljs.core} + (ana/no-warn + (env/with-compiler-env cenv + (ana/resolve-var {:ns {:name 'cljs.core}} '..)))))) (comment (env/with-compiler-env cenv From abcfc9f94e58d765314cfe63123d928b55e1f5db Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 20 Jun 2015 16:10:24 -0400 Subject: [PATCH 1171/4033] Namespaces are not INamed --- src/main/cljs/cljs/core.cljs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f213b61c9..0d1238765 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9856,11 +9856,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." false)) IHash (-hash [_] - (hash name)) - INamed - (-name [_] name) - (-namespace [_] - (throw (js/Error. "Cannot call -namespace on Namespace")))) + (hash name))) (defn find-ns-obj [ns] (letfn [(find-ns* [ctxt xs] From 0cad3bc80d57f5dbf30389c368c69c94b81a4d2c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 20 Jun 2015 16:28:49 -0400 Subject: [PATCH 1172/4033] add cljs.core/find-macros-ns, conditionalize cljs.analyzer to use it --- src/main/cljs/cljs/core.cljs | 4 ++++ src/main/clojure/cljs/analyzer.cljc | 22 ++++++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0d1238765..a1fccc832 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9896,6 +9896,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn find-ns [ns] (create-ns ns (find-ns-obj ns))) +(defn find-macros-ns [ns] + (let [ns (symbol (str ns "$macros"))] + (create-ns ns (find-ns-obj ns)))) + (defn ns-name [ns-obj] (.-name ns-obj)) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e47961b0c..234b18a21 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1940,16 +1940,22 @@ (get-in @env/*compiler* [::namespaces (-> env :ns :name) :use-macros sym]))))) (if-let [nstr (namespace sym)] (when-let [ns (cond - (= "clojure.core" nstr) (find-ns 'cljs.core) - (= "clojure.repl" nstr) (find-ns 'cljs.repl) - #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] - :cljs [(goog.string/contains nstr ".") (find-ns (symbol nstr))]) - :else - (some-> env :ns :require-macros (get (symbol nstr)) find-ns))] + (= "clojure.core" nstr) + #?(:clj (find-ns 'cljs.core) + :cljs (find-macros-ns 'cljs.core)) + (= "clojure.repl" nstr) + #?(:clj (find-ns 'cljs.repl) + :cljs (find-macros-ns 'cljs.repl)) + #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] + :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) + :else + (some-> env :ns :require-macros (get (symbol nstr)) find-ns))] (.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym)))) (if-let [nsym (-> env :ns :use-macros sym)] - (.findInternedVar ^clojure.lang.Namespace (find-ns nsym) sym) - (.findInternedVar ^clojure.lang.Namespace (find-ns 'cljs.core) sym))))] + (.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 'cljs.core)) sym))))] (when (and mvar (.isMacro ^clojure.lang.Var mvar)) (with-meta @mvar (meta mvar))))) From e1e190569f8588fa6e1e30c0e4a4686d08b57eed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 21 Jun 2015 19:16:05 -0400 Subject: [PATCH 1173/4033] CLJS-1317: Incremental compilation issues for :nodejs target The issue was that the AOTed cljs.core is compiled with :target :default. This triggered recompilation under :target :nodejs. Since core is recompiled all other namespaces will be recompiled due to :recompile-dependents true. Don't recompile because of differing build options if the namespace is cljs.core and AOTed core exists. --- src/main/clojure/cljs/compiler.cljc | 36 +++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1966ba469..721da6638 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1142,23 +1142,25 @@ "Return true if the src file requires compilation." ([src dest] (requires-compilation? src dest nil)) ([^File src ^File dest opts] - (ensure - (or (not (.exists dest)) - (> (.lastModified src) (.lastModified dest)) - (let [version' (util/compiled-by-version dest) - version (util/clojurescript-version)] - (and version (not= version version'))) - (and opts (not= (build-affecting-options opts) - (build-affecting-options (util/build-options dest)))) - (and opts (:source-map opts) - (if (= (:optimizations opts) :none) - (not (.exists (io/file (str (.getPath dest) ".map")))) - (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))) - (let [{ns :ns} (ana/parse-ns src) - {:keys [recompile visited]} - (and *dependents* @*dependents*)] - (and (contains? recompile ns) - (not (contains? visited ns))))))))) + (let [{:keys [ns]} (ana/parse-ns src)] + (ensure + (or (not (.exists dest)) + (> (.lastModified src) (.lastModified dest)) + (let [version' (util/compiled-by-version dest) + version (util/clojurescript-version)] + (and version (not= version version'))) + (and opts + (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns))) + (not= (build-affecting-options opts) + (build-affecting-options (util/build-options dest)))) + (and opts (:source-map opts) + (if (= (:optimizations opts) :none) + (not (.exists (io/file (str (.getPath dest) ".map")))) + (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))) + (let [{:keys [recompile visited]} + (and *dependents* @*dependents*)] + (and (contains? recompile ns) + (not (contains? visited ns)))))))))) #?(:clj (defn compile-file From 5348bdd8f8bf1e80930f5c0edb7b47bb6223a34e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 21 Jun 2015 19:43:53 -0400 Subject: [PATCH 1174/4033] add missing munge when getting interned vars from ns object --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a1fccc832..7b0d82fac 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9842,7 +9842,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (Var. (get @mappings sym) (symbol (str name) (str sym)) nil)) (findInternedVar [_ sym] - (Var. (goog.object/get obj (str sym)) + (Var. (goog.object/get obj (munge (str sym))) (symbol (str name) (str sym)) nil)) (getMappings [_] @mappings) From 7423ecc9eb5550369681498b63153cc2abeaa63a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 21 Jun 2015 19:48:47 -0400 Subject: [PATCH 1175/4033] Var val field must always be function of zero arguments --- src/main/cljs/cljs/core.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7b0d82fac..624c83951 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9839,10 +9839,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype Namespace [obj name mappings] Object (find [_ sym] - (Var. (get @mappings sym) + (Var. #(get @mappings sym) (symbol (str name) (str sym)) nil)) (findInternedVar [_ sym] - (Var. (goog.object/get obj (munge (str sym))) + (Var. #(goog.object/get obj (munge (str sym))) (symbol (str name) (str sym)) nil)) (getMappings [_] @mappings) @@ -9880,7 +9880,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (letfn [(step [ret k] (let [var-sym (symbol (demunge k))] (assoc ret - var-sym (Var. (goog.object/get ns-obj k) + var-sym (Var. #(goog.object/get ns-obj k) (symbol (str sym) (str var-sym)) nil))))] (reduce step {} (js-keys ns-obj))))) From 2ab533d01ede6496e6cebfef32997e25467004f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 21 Jun 2015 22:56:42 -0400 Subject: [PATCH 1176/4033] Namespace should only return value if var exists --- src/main/cljs/cljs/core.cljs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 624c83951..a26948b5b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9839,11 +9839,14 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype Namespace [obj name mappings] Object (find [_ sym] - (Var. #(get @mappings sym) - (symbol (str name) (str sym)) nil)) + (when (has? @mappings sym) + (Var. #(get @mappings sym) + (symbol (str name) (str sym)) nil))) (findInternedVar [_ sym] - (Var. #(goog.object/get obj (munge (str sym))) - (symbol (str name) (str sym)) nil)) + (let [k (munge (str sym))] + (when (goog.object/contains obj k) + (Var. #(goog.object/get obj k) + (symbol (str name) (str sym)) nil)))) (getMappings [_] @mappings) (getName [_] name) From 417207b67748141c1fa07ce8876f7e63a55b3625 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 22 Jun 2015 07:17:05 -0400 Subject: [PATCH 1177/4033] clean up cljs.core ns form. consistently use gobject alias over goog.object --- src/main/cljs/cljs/core.cljs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a26948b5b..c25c6e7b3 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10,7 +10,7 @@ (:require [goog.string :as gstring] [goog.object :as gobject] [goog.array :as garray]) - (:import goog.string.StringBuffer)) + (:import [goog.string StringBuffer])) ;; next line is auto-generated by the build-script - Do not edit! (def *clojurescript-version*) @@ -1899,7 +1899,7 @@ reduces them without incurring seq initialization" "Return the JavaScript keys for an object." [obj] (let [keys (array)] - (goog.object/forEach obj (fn [val key obj] (.push keys key))) + (gobject/forEach obj (fn [val key obj] (.push keys key))) keys)) (defn js-delete @@ -9809,7 +9809,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (if (< i (. name -length)) (recur (inc i) (let [c (.charAt name i)] - (if-let [sub (goog.object/get CHAR_MAP c)] + (if-let [sub (gobject/get CHAR_MAP c)] (str ret sub) (str ret c)))) ret))) @@ -9827,7 +9827,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (str ret (.substring munged-name last-match-end (- (. r -lastIndex) (. x -length))) - (if (= x "$") "/" (goog.object/get DEMUNGE_MAP x))) + (if (= x "$") "/" (gobject/get DEMUNGE_MAP x))) (. r -lastIndex))) (str ret (.substring munged-name last-match-end (.-length munged-name))))))) @@ -9844,8 +9844,8 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (symbol (str name) (str sym)) nil))) (findInternedVar [_ sym] (let [k (munge (str sym))] - (when (goog.object/contains obj k) - (Var. #(goog.object/get obj k) + (when (gobject/contains obj k) + (Var. #(gobject/get obj k) (symbol (str name) (str sym)) nil)))) (getMappings [_] @mappings) @@ -9866,10 +9866,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (cond (nil? ctxt) nil (nil? xs) ctxt - :else (recur (goog.object/get ctxt (first xs)) (next xs))))] + :else (recur (gobject/get ctxt (first xs)) (next xs))))] (if-not js/COMPILED (let [segs (-> ns str (.split "."))] - (when (goog.object/get (. goog/dependencies_ -nameToPath) (str ns)) + (when (gobject/get (. goog/dependencies_ -nameToPath) (str ns)) (condp identical? *target* "nodejs" (find-ns* js/global segs) "default" (find-ns* js/window segs) @@ -9883,7 +9883,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (letfn [(step [ret k] (let [var-sym (symbol (demunge k))] (assoc ret - var-sym (Var. #(goog.object/get ns-obj k) + var-sym (Var. #(gobject/get ns-obj k) (symbol (str sym) (str var-sym)) nil))))] (reduce step {} (js-keys ns-obj))))) From 4ea7cf430e57792d9a5327471bc54b9c1ec089ba Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 22 Jun 2015 07:41:46 -0400 Subject: [PATCH 1178/4033] goog.object/contains -> goog.object/containsKey --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c25c6e7b3..e9ef5618c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9844,7 +9844,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (symbol (str name) (str sym)) nil))) (findInternedVar [_ sym] (let [k (munge (str sym))] - (when (gobject/contains obj k) + (when (gobject/containsKey obj k) (Var. #(gobject/get obj k) (symbol (str name) (str sym)) nil)))) (getMappings [_] From c3ea574db595676fb826196b5d5dc13789f41096 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 22 Jun 2015 07:44:55 -0400 Subject: [PATCH 1179/4033] typo, has? -> contains? --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e9ef5618c..01b4a8e5e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9839,7 +9839,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype Namespace [obj name mappings] Object (find [_ sym] - (when (has? @mappings sym) + (when (contains? @mappings sym) (Var. #(get @mappings sym) (symbol (str name) (str sym)) nil))) (findInternedVar [_ sym] From 3b57979afff477f3848d4325d1bd909d2a6c7b11 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 22 Jun 2015 09:08:11 -0400 Subject: [PATCH 1180/4033] simplify cljs.compiler/munge regular expression for portability --- src/main/clojure/cljs/compiler.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 721da6638..84b367cf9 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -90,9 +90,9 @@ (let [ss (string/replace (str s) ".." "_DOT__DOT_") ss (string/replace ss #?(:clj #"\/(.)" :cljs (js/RegExp. "\\/(.)")) ".$1") ; Division is special - ss (apply str + ss (string/join "." (map #(if (reserved %) (str % "$") %) - (string/split ss #"(?<=\.)|(?=\.)"))) + (string/split ss #"\."))) ms #?(:clj (clojure.lang.Compiler/munge ss) :cljs (cljs.core/munge ss))] (if (symbol? s) From 6f5b92744c6bd2abd968e89bb815e001425ad3f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 22 Jun 2015 12:11:49 -0400 Subject: [PATCH 1181/4033] fix simple test runner --- script/test-simple | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test-simple b/script/test-simple index 9c766c695..e0ebf6269 100755 --- a/script/test-simple +++ b/script/test-simple @@ -8,7 +8,7 @@ possible=4 ran=0 #bin/cljsc test >out/core-test.js -bin/cljsc test "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :compiler-stats true}" > builds/out-simp/core-simple-test.js +bin/cljsc src/test/cljsix "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :compiler-stats true}" > builds/out-simp/core-simple-test.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" From 265896c8ec7c9b34a0647f3357602660b2cc6b12 Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Mon, 22 Jun 2015 18:43:45 +0200 Subject: [PATCH 1182/4033] CLJS-1281: Preserve test order Achieved by sorting the test vars by :line when creating the ns test-block. --- src/main/cljs/cljs/test.clj | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/test.clj b/src/main/cljs/cljs/test.clj index 3ff3b65ac..a8750e8e0 100644 --- a/src/main/cljs/cljs/test.clj +++ b/src/main/cljs/cljs/test.clj @@ -321,12 +321,11 @@ `(cljs.test/update-current-env! [:each-fixtures] assoc '~ns ~(symbol (name ns) "cljs-test-each-fixtures"))))] (cljs.test/test-vars-block - [~@(map - (fn [[k _]] - `(var ~(symbol (name ns) (name k)))) - (filter - (fn [[_ v]] (:test v)) - (ana-api/ns-interns ns)))]) + [~@(->> (ana-api/ns-interns ns) + (filter (fn [[_ v]] (:test v))) + (sort-by (fn [[_ v]] (:line v))) + (map (fn [[k _]] + `(var ~(symbol (name ns) (name k))))))]) [(fn [] (when (nil? env#) (cljs.test/clear-env!)))])))) From fe6bd0d500aef922669ce0d563a5721ee4275e03 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Jun 2015 13:09:29 -0400 Subject: [PATCH 1183/4033] fix typo in changes file --- changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.md b/changes.md index 8f649920d..0a4fafd76 100644 --- a/changes.md +++ b/changes.md @@ -6,7 +6,7 @@ * add cljs.core/random-uuid * flush immediately when forwarding Node process out & err * CLJS-1256 cache UUID hash value -* CLJS-1226: Added the :end-run-test event to cljs.test and a dummy event handler for it +* CLJS-1226: Added the :end-run-tests event to cljs.test and a dummy event handler for it ## Fixes * CLJS-1200: compare behaves differently from Clojure From afbc32945b174dea639d18ecc5fa234bb153a4b9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Jun 2015 13:58:45 -0400 Subject: [PATCH 1184/4033] typo --- script/test-simple | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test-simple b/script/test-simple index e0ebf6269..3c2b37a02 100755 --- a/script/test-simple +++ b/script/test-simple @@ -8,7 +8,7 @@ possible=4 ran=0 #bin/cljsc test >out/core-test.js -bin/cljsc src/test/cljsix "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :compiler-stats true}" > builds/out-simp/core-simple-test.js +bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :compiler-stats true}" > builds/out-simp/core-simple-test.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" From bed3db8d19d8c3e7c4221f1b64e13ad14f6cac3b Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Jun 2015 14:39:35 -0400 Subject: [PATCH 1185/4033] conditionalize cljs.tagged-literals --- src/main/clojure/cljs/tagged_literals.clj | 48 ------------- src/main/clojure/cljs/tagged_literals.cljc | 83 ++++++++++++++++++++++ 2 files changed, 83 insertions(+), 48 deletions(-) delete mode 100644 src/main/clojure/cljs/tagged_literals.clj create mode 100644 src/main/clojure/cljs/tagged_literals.cljc diff --git a/src/main/clojure/cljs/tagged_literals.clj b/src/main/clojure/cljs/tagged_literals.clj deleted file mode 100644 index 52a6e0945..000000000 --- a/src/main/clojure/cljs/tagged_literals.clj +++ /dev/null @@ -1,48 +0,0 @@ -(ns cljs.tagged-literals - (:require [clojure.instant :as inst])) - -(defn read-queue - [form] - (when-not (vector? form) - (throw (RuntimeException. "Queue literal expects a vector for its elements."))) - (list 'cljs.core/into 'cljs.core.PersistentQueue.EMPTY form)) - -(defn read-uuid - [form] - (when-not (string? form) - (throw (RuntimeException. "UUID literal expects a string as its representation."))) - (try - (java.util.UUID/fromString form) - (catch Throwable e - (throw (RuntimeException. (.getMessage e)))))) - -(defn read-inst - [form] - (when-not (string? form) - (throw (RuntimeException. "Instance literal expects a string for its timestamp."))) - (try - (inst/read-instant-date form) - (catch Throwable e - (throw (RuntimeException. (.getMessage e)))))) - -(defn valid-js-literal-key? [k] - (or (string? k) - (and (keyword? k) - (nil? (namespace k))))) - -(deftype JSValue [val]) - -(defn read-js - [form] - (when-not (or (vector? form) (map? form)) - (throw (RuntimeException. "JavaScript literal must use map or vector notation"))) - (when-not (or (not (map? form)) - (every? valid-js-literal-key? (keys form))) - (throw (RuntimeException. "JavaScript literal keys must be strings or unqualified keywords"))) - (JSValue. form)) - -(def ^:dynamic *cljs-data-readers* - {'queue read-queue - 'uuid read-uuid - 'inst read-inst - 'js read-js}) diff --git a/src/main/clojure/cljs/tagged_literals.cljc b/src/main/clojure/cljs/tagged_literals.cljc new file mode 100644 index 000000000..dc1190d27 --- /dev/null +++ b/src/main/clojure/cljs/tagged_literals.cljc @@ -0,0 +1,83 @@ +(ns cljs.tagged-literals + #?(:clj (:require [clojure.instant :as inst]) + :cljs (:require [cljs.reader :as reader]))) + +(defn read-queue + [form] + (when-not (vector? form) + (throw + #?(:clj (RuntimeException. + "Queue literal expects a vector for its elements.") + :cljs (js/Error. + "Queue literal expects a vector for its elements.")))) + (list 'cljs.core/into 'cljs.core.PersistentQueue.EMPTY form)) + +#?(:clj + (defn read-uuid + [form] + (when-not (string? form) + (throw (RuntimeException. "UUID literal expects a string as its representation."))) + (try + (java.util.UUID/fromString form) + (catch Throwable e + (throw (RuntimeException. (.getMessage e))))))) + +#?(:cljs + (defn read-uuid + [form] + (when-not (string? form) + (throw (js/Error. "UUID literal expects a string as its representation."))) + (try + (uuid form) + (catch :default e + (throw (js/Error. (. e -message))))))) + +#?(:clj + (defn read-inst + [form] + (when-not (string? form) + (throw (RuntimeException. "Instance literal expects a string for its timestamp."))) + (try + (inst/read-instant-date form) + (catch Throwable e + (throw (RuntimeException. (.getMessage e))))))) + +#?(:cljs + (defn read-inst + [form] + (when-not (string? form) + (throw (js/Error. "Instance literal expects a string for its timestamp."))) + (try + (reader/read-date form) + (catch :default e + (throw (js/Error. (. e -message))))))) + +(defn valid-js-literal-key? [k] + (or (string? k) + (and (keyword? k) + (nil? (namespace k))))) + +(deftype JSValue [val]) + +(defn read-js + [form] + (when-not (or (vector? form) (map? form)) + (throw + #?(:clj (RuntimeException. + "JavaScript literal must use map or vector notation") + :cljs (js/Error. + "JavaScript literal must use map or vector notation")))) + (when-not (or (not (map? form)) + (every? valid-js-literal-key? (keys form))) + (throw + #?(:clj (RuntimeException. + "JavaScript literal keys must be strings or unqualified keywords") + :cljs (js/Error. + "JavaScript literal keys must be strings or unqualified keywords")))) + (JSValue. form)) + +(def ^:dynamic *cljs-data-readers* + {'queue read-queue + 'uuid read-uuid + 'inst read-inst + 'js read-js}) From e87a8df8c81edc4ccc09f6eb416c76ddc05df98b Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Jun 2015 15:43:42 -0400 Subject: [PATCH 1186/4033] only append $macros to ns if not already present in find-macro-ns --- src/main/cljs/cljs/core.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 01b4a8e5e..d712acf1b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9900,7 +9900,9 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (create-ns ns (find-ns-obj ns))) (defn find-macros-ns [ns] - (let [ns (symbol (str ns "$macros"))] + (let [ns (cond-> ns + (not (gstring/contains (str ns) "$macros")) + (-> (str "$macros") symbol))] (create-ns ns (find-ns-obj ns)))) (defn ns-name [ns-obj] From 59c1c3d5e8122a7835f6f05edc5ba14fd71e584a Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 22 Jun 2015 15:52:42 -0400 Subject: [PATCH 1187/4033] in bootstrapped context, *unchecked-if* already defined in cljs.core and a simple dynamic boolean var, not an atom --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 234b18a21..7984834a1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -796,7 +796,8 @@ else-expr (allowing-redef (analyze env else))] {:env env :op :if :form form :test test-expr :then then-expr :else else-expr - :unchecked @*unchecked-if* + :unchecked #?(:clj @*unchecked-if* + :cljs *unchecked-if*) :children [test-expr then-expr else-expr]})) (defmethod parse 'case* @@ -1300,7 +1301,8 @@ ;; TODO: proper resolve (= target '*unchecked-if*) (do - (reset! *unchecked-if* val) + #?(:clj (reset! *unchecked-if* val) + :cljs (set! *unchecked-if* val)) ::set-unchecked-if) (symbol? target) From a938564760c211e02f26228398d3d57fe8f7149d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Jun 2015 14:17:13 +0200 Subject: [PATCH 1188/4033] when bootstrapped cljs.analyzer/core-name? needs to check for cljs.core$macros not cljs.core --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7984834a1..2f999e25e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -513,7 +513,7 @@ (and (or (get-in @env/*compiler* [::namespaces 'cljs.core :defs sym]) (when-let [mac (get-expander sym env)] (let [^Namespace ns (-> mac meta :ns)] - (= (.getName ns) 'cljs.core)))) + (= (.getName ns) #?(:clj 'cljs.core :cljs 'cljs.core$macros))))) (not (contains? (-> env :ns :excludes) sym)))) (defn resolve-var From f2ad48dbc7af1cded4257b36f597f01da1a1f4ad Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Jun 2015 14:46:19 +0200 Subject: [PATCH 1189/4033] defn needs to be marked as a macro --- src/main/clojure/cljs/core.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index e8bc02f04..78ea6107c 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2742,7 +2742,8 @@ ;;must figure out how to convey primitive hints to self calls first (cons `fn fdecl)))))) -#?(:clj (. (var defn) (setMacro))) +#?(:clj (. (var defn) (setMacro)) + :cljs (set! (. defn -cljs$lang$macro) true)) (def ^{:doc "Like defn, but the resulting function name is declared as a From d83b8080614e987885ea58136d6a74728d4f5e7f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Jun 2015 16:04:09 +0200 Subject: [PATCH 1190/4033] ns operations should return vars with at least :ns metadata to support cljs.analyzer/core-name? --- src/main/cljs/cljs/core.cljs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d712acf1b..fbc025d7b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9838,15 +9838,15 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (deftype Namespace [obj name mappings] Object - (find [_ sym] + (find [this sym] (when (contains? @mappings sym) (Var. #(get @mappings sym) - (symbol (str name) (str sym)) nil))) - (findInternedVar [_ sym] + (symbol (str name) (str sym)) {:ns this}))) + (findInternedVar [this sym] (let [k (munge (str sym))] (when (gobject/containsKey obj k) (Var. #(gobject/get obj k) - (symbol (str name) (str sym)) nil)))) + (symbol (str name) (str sym)) {:ns this})))) (getMappings [_] @mappings) (getName [_] name) @@ -9879,12 +9879,13 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." "find-ns-obj not supported when Closure optimization applied"))))) (defn ns-interns* [sym] - (let [ns-obj (find-ns-obj sym)] + (let [ns-obj (find-ns-obj sym) + ns (Namespace. ns-obj sym (atom nil))] (letfn [(step [ret k] (let [var-sym (symbol (demunge k))] (assoc ret var-sym (Var. #(gobject/get ns-obj k) - (symbol (str sym) (str var-sym)) nil))))] + (symbol (str sym) (str var-sym)) {:ns ns}))))] (reduce step {} (js-keys ns-obj))))) (defn create-ns @@ -9917,4 +9918,4 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (when-not (contains? env sym) (when-let [ns-obj (find-ns ns)] (Var. #(.find ns-obj (str sym)) - (symbol (str ns) (str sym)) nil))))) + (symbol (str ns) (str sym)) {:ns ns-obj}))))) From ed5d203b1db1dfcfeec818e08b4926f3fb9eb6d3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Jun 2015 16:15:56 +0200 Subject: [PATCH 1191/4033] cljs compatible symbol check in defn macro --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 78ea6107c..8a8bd3e7e 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2685,7 +2685,7 @@ [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])} defn (core/fn defn [&form &env name & fdecl] ;; Note: Cannot delegate this check to def because of the call to (with-meta name ..) - (if (core/instance? clojure.lang.Symbol name) + (if (core/instance? #?(:clj clojure.lang.Symbol :cljs Symbol) name) nil (throw #?(:clj (IllegalArgumentException. "First argument to defn must be a symbol") From 73117d5a451bca5ab1a5aa8c7fa8cbce67ab98d0 Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Mon, 22 Jun 2015 23:37:52 +0200 Subject: [PATCH 1192/4033] CLJS-1319: Cannot locate module namespace when filename contains dash Replace underscores in new module name with dashes before adding to :js-module-index. --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 45c987d2c..e98c02d73 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1539,7 +1539,8 @@ (if (or (and (= module-type :commonjs) can-convert-commonjs?) (and (= module-type :amd) can-convert-amd?) (and (= module-type :es6) can-convert-es6?)) - (let [module-name (ProcessCommonJSModules/toModuleName file) + (let [module-name (-> (ProcessCommonJSModules/toModuleName file) + (string/replace "_" "-")) ijs (write-javascript opts (deps/load-foreign-library lib))] (doseq [provide (:provides ijs)] (swap! env/*compiler* From 3b3b0838c8609d527b193162aba576c3b1d2dd25 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Jun 2015 08:29:48 +0200 Subject: [PATCH 1193/4033] drop mapping support from Namespace, drop related fns. Var resolution must happen through the analyzer, already the case for reading. Only findInternedVar Namespace method actually used by the analyzer itself. --- src/main/cljs/cljs/core.cljs | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fbc025d7b..ea517b428 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9836,19 +9836,13 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." ((if (symbol? name) symbol str) (demunge-str (str name)))) -(deftype Namespace [obj name mappings] +(deftype Namespace [obj name] Object - (find [this sym] - (when (contains? @mappings sym) - (Var. #(get @mappings sym) - (symbol (str name) (str sym)) {:ns this}))) (findInternedVar [this sym] (let [k (munge (str sym))] (when (gobject/containsKey obj k) (Var. #(gobject/get obj k) (symbol (str name) (str sym)) {:ns this})))) - (getMappings [_] - @mappings) (getName [_] name) (toString [_] (str name)) @@ -9878,24 +9872,11 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (js/Error. "find-ns-obj not supported when Closure optimization applied"))))) -(defn ns-interns* [sym] - (let [ns-obj (find-ns-obj sym) - ns (Namespace. ns-obj sym (atom nil))] - (letfn [(step [ret k] - (let [var-sym (symbol (demunge k))] - (assoc ret - var-sym (Var. #(gobject/get ns-obj k) - (symbol (str sym) (str var-sym)) {:ns ns}))))] - (reduce step {} (js-keys ns-obj))))) - (defn create-ns ([sym] (create-ns sym (find-ns-obj sym))) ([sym ns-obj] - (create-ns sym (find-ns-obj sym) - (merge (ns-interns* 'cljs.core) (ns-interns* sym)))) - ([sym ns-obj mappings] - (Namespace. ns-obj sym (atom mappings)))) + (Namespace. sym ns-obj))) (defn find-ns [ns] (create-ns ns (find-ns-obj ns))) @@ -9908,14 +9889,3 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn ns-name [ns-obj] (.-name ns-obj)) - -(defn ns-map [ns-obj] - (.getMappings ns-obj)) - -(defn ns-resolve - ([ns sym] (ns-resolve ns nil sym)) - ([ns env sym] - (when-not (contains? env sym) - (when-let [ns-obj (find-ns ns)] - (Var. #(.find ns-obj (str sym)) - (symbol (str ns) (str sym)) {:ns ns-obj}))))) From 598cacf145b964cf781e01bbc93be87bc36d9310 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Jun 2015 08:41:37 +0200 Subject: [PATCH 1194/4033] restore ns-interns* --- src/main/cljs/cljs/core.cljs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ea517b428..293b15980 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9872,6 +9872,16 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (js/Error. "find-ns-obj not supported when Closure optimization applied"))))) +(defn ns-interns* [sym] + (let [ns-obj (find-ns-obj sym) + ns (Namespace. ns-obj sym)] + (letfn [(step [ret k] + (let [var-sym (symbol (demunge k))] + (assoc ret + var-sym (Var. #(gobject/get ns-obj k) + (symbol (str sym) (str var-sym)) {:ns ns}))))] + (reduce step {} (js-keys ns-obj))))) + (defn create-ns ([sym] (create-ns sym (find-ns-obj sym))) From a6e52e85e0c1480754fa1888e9e96793b9d0aea3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Jun 2015 08:47:43 +0200 Subject: [PATCH 1195/4033] reversed arguments --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 293b15980..2460ec548 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9886,7 +9886,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." ([sym] (create-ns sym (find-ns-obj sym))) ([sym ns-obj] - (Namespace. sym ns-obj))) + (Namespace. ns-obj sym))) (defn find-ns [ns] (create-ns ns (find-ns-obj ns))) From 37436919089c995005894f677213af6cc376cef6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Jun 2015 16:10:11 +0200 Subject: [PATCH 1196/4033] handle js reserved keywords when munging, this solves the let -> let$ issue --- src/main/cljs/cljs/core.cljs | 47 +++++++++++++++++++++++------ src/main/clojure/cljs/analyzer.cljc | 2 +- src/main/clojure/cljs/compiler.cljc | 3 +- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 2460ec548..9a3a77832 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9779,16 +9779,40 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn tagged-literal? "Return true if the value is the data representation of a tagged literal" [value] - (instance? cljs.core.TaggedLiteral value)) + (instance? TaggedLiteral value)) (defn tagged-literal "Construct a data representation of a tagged literal from a tag symbol and a form." [tag form] {:pre [(symbol? tag)]} - (cljs.core.TaggedLiteral. tag form)) - -(defn demunge-pattern [] + (TaggedLiteral. tag form)) + +(def ^:private js-reserved-arr + #js ["abstract" "boolean" "break" "byte" "case" + "catch" "char" "class" "const" "continue" + "debugger" "default" "delete" "do" "double" + "else" "enum" "export" "extends" "final" + "finally" "float" "for" "function" "goto" "if" + "implements" "import" "in" "instanceof" "int" + "interface" "let" "long" "native" "new" + "package" "private" "protected" "public" + "return" "short" "static" "super" "switch" + "synchronized" "this" "throw" "throws" + "transient" "try" "typeof" "var" "void" + "volatile" "while" "with" "yield" "methods" + "null"]) + +(def js-reserved nil) + +(defn- js-reserved? [x] + (when (nil? js-reserved) + (set! js-reserved + (reduce #(do (gobject/set %1 %2 true) %1) + #js {} js-reserved-arr))) + (gobject/containsKey js-reserved x)) + +(defn- demunge-pattern [] (when-not DEMUNGE_PATTERN (set! DEMUNGE_PATTERN (let [ks (sort (fn [a b] (- (. b -length) (. a -length))) @@ -9804,7 +9828,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (str ret "|\\$")))))) DEMUNGE_PATTERN) -(defn munge-str [name] +(defn- munge-str [name] (loop [i 0 ret ""] (if (< i (. name -length)) (recur (inc i) @@ -9816,10 +9840,15 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn munge [name] ((if (symbol? name) symbol str) - (munge-str (str name)))) - -(defn demunge-str [munged-name] - (let [r (js/RegExp. (demunge-pattern) "g")] + (let [name' (munge-str (str name))] + (if (js-reserved? name') + (str name' "$") + name')))) + +(defn- demunge-str [munged-name] + (let [r (js/RegExp. (demunge-pattern) "g") + munged-name (if (gstring/endsWith munged-name "$") + (.substring munged-name 0 (dec (. munged-name -length))))] (loop [ret "" last-match-end 0] (if-let [match (.exec r munged-name)] (let [[x] match] diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2f999e25e..1cc81939a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -8,7 +8,7 @@ (ns cljs.analyzer #?(:clj (:refer-clojure :exclude [macroexpand-1 ensure]) - :cljs (:refer-clojure :exclude [macroexpand-1 ns-interns ensure])) + :cljs (:refer-clojure :exclude [macroexpand-1 ns-interns ensure js-reserved])) #?(:cljs (:require-macros [cljs.analyzer.macros :refer [no-warn wrapping-errors diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 84b367cf9..69651fc37 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -7,7 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.compiler - (:refer-clojure :exclude [munge macroexpand-1 ensure]) + #?(:clj (:refer-clojure :exclude [munge macroexpand-1 ensure]) + :cljs (:refer-clojure :exclude [munge macroexpand-1 ensure js-reserved])) #?(:cljs (:require-macros [cljs.compiler.macros :refer [emit-wrap]] [cljs.env.macros :refer [ensure]])) #?(:clj (:require [cljs.util :as util] From 2c2ff8fb3ae55dbd165710c580328b9daa4a7d23 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Jun 2015 19:31:01 +0200 Subject: [PATCH 1197/4033] fix symbol from string bug, if "foo/bar" supplied, namespace and name not properly computed --- src/main/cljs/cljs/core.cljs | 18 +++++++++++------- src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9a3a77832..bf5003b82 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -927,14 +927,18 @@ (defn symbol ([name] - (if (symbol? name) - name - (symbol nil name))) + (if (symbol? name) + name + (let [idx (.indexOf name "/")] + (if (== idx -1) + (symbol nil name) + (symbol (.substring name 0 idx) + (.substring name (inc idx) (. name -length))))))) ([ns name] - (let [sym-str (if-not (nil? ns) - (str ns "/" name) - name)] - (Symbol. ns name sym-str nil nil)))) + (let [sym-str (if-not (nil? ns) + (str ns "/" name) + name)] + (Symbol. ns name sym-str nil nil)))) (deftype Var [val sym _meta] Object diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d26bd01c9..b88988219 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2850,6 +2850,13 @@ (is (zero? (count ys))) (is (= (transduce (map inc) conj [] ys) [])))) +(deftest test-symbol-from-string + (let [x (symbol "js/Array")] + (is (= x 'js/Array)) + (is (= (hash x) (hash 'js/Array))) + (is (= (namespace x) "js")) + (is (= (name x) "Array")))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 7d1735d8b46650f5307097f10804da8d3b952500 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Jun 2015 20:33:34 +0200 Subject: [PATCH 1198/4033] no need for defmacro to be in lower form. change cljs.analyzer/get-expander to just return the var instead of conversion to MetaFn. MetaFn in ClojureScript still needs work in the case that it wraps a variadic fn. --- src/main/clojure/cljs/analyzer.cljc | 8 +-- src/main/clojure/cljs/core.cljc | 81 ++++++++++++++--------------- 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1cc81939a..757db1f34 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1959,7 +1959,7 @@ (.findInternedVar ^clojure.lang.Namespace #?(:clj (find-ns 'cljs.core) :cljs (find-macros-ns 'cljs.core)) sym))))] (when (and mvar (.isMacro ^clojure.lang.Var mvar)) - (with-meta @mvar (meta mvar))))) + mvar))) (defn macroexpand-1 "Given a env, an analysis environment, and form, a ClojureScript form, @@ -1970,16 +1970,16 @@ (let [op (first form)] (if (specials op) form - (if-let [mac (and (symbol? op) (get-expander op env))] + (if-let [mac-var (and (symbol? op) (get-expander op env))] (binding [*ns* (create-ns *cljs-ns*)] - (let [form' (apply mac form env (rest form))] + (let [form' (apply @mac-var form env (rest form))] (if (seq? form') (let [sym' (first form') sym (first form)] (if (= sym' 'js*) (vary-meta form' merge (cond-> {:js-op (if (namespace sym) sym (symbol "cljs.core" (str sym)))} - (-> mac meta ::numeric) (assoc :numeric true))) + (-> mac-var meta ::numeric) (assoc :numeric true))) form')) form'))) (if (symbol? op) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8a8bd3e7e..9b6a706aa 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2745,46 +2745,45 @@ #?(:clj (. (var defn) (setMacro)) :cljs (set! (. defn -cljs$lang$macro) true)) -(def - ^{:doc "Like defn, but the resulting function name is declared as a +(core/defn defmacro + "Like defn, but the resulting function name is declared as a macro and will be used as a macro by the compiler when it is called." - :arglists '([name doc-string? attr-map? [params*] body] - [name doc-string? attr-map? ([params*] body)+ attr-map?]) - :macro true} - defmacro (core/fn [&form &env - name & args] - (core/let [prefix (core/loop [p (core/list (vary-meta name assoc :macro true)) args args] - (core/let [f (first args)] - (if (core/string? f) - (recur (cons f p) (next args)) - (if (map? f) - (recur (cons f p) (next args)) - p)))) - fdecl (core/loop [fd args] - (if (core/string? (first fd)) - (recur (next fd)) - (if (map? (first fd)) - (recur (next fd)) - fd))) - fdecl (if (vector? (first fdecl)) - (core/list fdecl) - fdecl) - add-implicit-args (core/fn [fd] - (core/let [args (first fd)] - (cons (vec (cons '&form (cons '&env args))) (next fd)))) - add-args (core/fn [acc ds] - (if (core/nil? ds) - acc - (core/let [d (first ds)] - (if (map? d) - (conj acc d) - (recur (conj acc (add-implicit-args d)) (next ds)))))) - fdecl (seq (add-args [] fdecl)) - decl (core/loop [p prefix d fdecl] - (if p - (recur (next p) (cons (first p) d)) - d))] - (core/list 'do - (cons `defn decl) - (core/list 'set! `(. ~name ~'-cljs$lang$macro) true))))) + {:arglists '([name doc-string? attr-map? [params*] body] + [name doc-string? attr-map? ([params*] body)+ attr-map?]) + :macro true} + [&form &env name & args] + (core/let [prefix (core/loop [p (core/list (vary-meta name assoc :macro true)) args args] + (core/let [f (first args)] + (if (core/string? f) + (recur (cons f p) (next args)) + (if (map? f) + (recur (cons f p) (next args)) + p)))) + fdecl (core/loop [fd args] + (if (core/string? (first fd)) + (recur (next fd)) + (if (map? (first fd)) + (recur (next fd)) + fd))) + fdecl (if (vector? (first fdecl)) + (core/list fdecl) + fdecl) + add-implicit-args (core/fn [fd] + (core/let [args (first fd)] + (cons (vec (cons '&form (cons '&env args))) (next fd)))) + add-args (core/fn [acc ds] + (if (core/nil? ds) + acc + (core/let [d (first ds)] + (if (map? d) + (conj acc d) + (recur (conj acc (add-implicit-args d)) (next ds)))))) + fdecl (seq (add-args [] fdecl)) + decl (core/loop [p prefix d fdecl] + (if p + (recur (next p) (cons (first p) d)) + d))] + (core/list 'do + (cons `defn decl) + (core/list 'set! `(. ~name ~'-cljs$lang$macro) true)))) From be9cb60a392ed22b4f816ebc2c0e6955e8d22980 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Jun 2015 20:54:31 +0200 Subject: [PATCH 1199/4033] missing quoting on deftype -prototype case --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 9b6a706aa..6671fa554 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1279,7 +1279,7 @@ [tsym sym] `(.. ~tsym ~(to-property sym))) (core/defmethod extend-prefix :default - [tsym sym] `(.. ~tsym -prototype ~(to-property sym))) + [tsym sym] `(.. ~tsym ~'-prototype ~(to-property sym))) (core/defn- adapt-obj-params [type [[this & args :as sig] & body]] (core/list (vec args) From 33b81c4888514b60be5cec3d64abbcf2e98b9f4d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Jun 2015 21:30:03 +0200 Subject: [PATCH 1200/4033] Also need to special case '.. in runtime munge / demunge --- src/main/cljs/cljs/core.cljs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index bf5003b82..df65a506e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9845,9 +9845,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn munge [name] ((if (symbol? name) symbol str) (let [name' (munge-str (str name))] - (if (js-reserved? name') - (str name' "$") - name')))) + (cond + (= name' "..") "_DOT__DOT_" + (js-reserved? name') (str name' "$") + :else name')))) (defn- demunge-str [munged-name] (let [r (js/RegExp. (demunge-pattern) "g") @@ -9867,7 +9868,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn demunge [name] ((if (symbol? name) symbol str) - (demunge-str (str name)))) + (let [name' (str name)] + (if (= name' "_DOT__DOT_") + ".." + (demunge-str (str name)))))) (deftype Namespace [obj name] Object From ae40327d62b7ca669fc6c2e76c7a0df37423bf4b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Jun 2015 21:38:12 +0200 Subject: [PATCH 1201/4033] add missing defn- macro --- src/main/clojure/cljs/core.cljc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 6671fa554..13393aa38 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -321,6 +321,12 @@ (cons 'fn* new-sigs)) (meta &form))))) +#?(:cljs + (core/defmacro defn- + "same as defn, yielding non-public def" + [name & decls] + (list* `defn (with-meta name (assoc (meta name) :private true)) decls))) + #?(:cljs (core/defmacro if-let "bindings => binding-form test From a3dc10bc333aa4ad0178c3e23dcaba87bbaea505 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 27 Jun 2015 21:47:42 +0200 Subject: [PATCH 1202/4033] typo when-let not core/when-let --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 13393aa38..2bba082b7 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2201,7 +2201,7 @@ (let [~k (-nth ~chunksym ~isym)] ~subform-chunk ~@(core/when needrec [recform-chunk])) - (core/when-let [~seqsym (seq ~seqsym)] + (when-let [~seqsym (seq ~seqsym)] (if (chunked-seq? ~seqsym) (let [c# (chunk-first ~seqsym)] (recur (chunk-rest ~seqsym) c# From 470bb680b916c6093d984b9b81301bc02ac4ed07 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Jun 2015 09:11:21 -0400 Subject: [PATCH 1203/4033] -> to core/-> --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 2bba082b7..f42631edc 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -254,7 +254,7 @@ (recur (next params) (conj new-params (first params)) lets) (core/let [gparam (gensym "p__")] (recur (next params) (conj new-params gparam) - (-> lets (conj (first params)) (conj gparam))))) + (core/-> lets (conj (first params)) (conj gparam))))) `(~new-params (let ~lets ~@body))))))) From 11d02d3c9af4585beb300f68e0ada6fd764d6d11 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Jun 2015 09:30:44 -0400 Subject: [PATCH 1204/4033] find-ns and find-macros-ns should not return anything if ns doesn't exist --- src/main/cljs/cljs/core.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index df65a506e..2a900d49c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9926,13 +9926,15 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (Namespace. ns-obj sym))) (defn find-ns [ns] - (create-ns ns (find-ns-obj ns))) + (when-let [ns-obj (find-ns-obj ns)] + (create-ns ns ns-obj))) (defn find-macros-ns [ns] (let [ns (cond-> ns (not (gstring/contains (str ns) "$macros")) (-> (str "$macros") symbol))] - (create-ns ns (find-ns-obj ns)))) + (when-let [ns-obj (find-ns-obj ns)] + (create-ns ns ns-obj)))) (defn ns-name [ns-obj] (.-name ns-obj)) From b994d09562e2ca355da575d17e028bfba0e33285 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Jun 2015 09:16:08 -0400 Subject: [PATCH 1205/4033] core/== to == in macro --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index f42631edc..b10934588 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2447,7 +2447,7 @@ (if (core/<= n 20) `(let [~(cs (core/dec n)) (-first ~'args) ~'args (-rest ~'args)] - (if (core/== ~'argc ~n) + (if (== ~'argc ~n) (if (. ~'f ~prop) (. ~'f (~f ~@(take n cs))) (~'f ~@(take n cs))) From 68a2d2d6eebe66ddefa15e9cfc673c01912440bd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Jun 2015 09:24:07 -0400 Subject: [PATCH 1206/4033] core/when-not to when-not, core/hash-map to hash-map --- src/main/clojure/cljs/core.cljc | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b10934588..2fe0f4267 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -388,6 +388,12 @@ [test & body] (core/list 'if test (cons 'do body)))) +#?(:cljs + (defmacro when-not + "Evaluates test. If logical false, evaluates body in an implicit do." + [test & body] + (core/list 'if test nil (cons 'do body)))) + #?(:cljs (core/defmacro when-first "bindings => x xs @@ -646,7 +652,7 @@ (core/let [gmap (gensym "map__") defaults (:or b)] (core/loop [ret (core/-> bvec (conj gmap) (conj v) - (conj gmap) (conj `(if (seq? ~gmap) (apply core/hash-map ~gmap) ~gmap)) + (conj gmap) (conj `(if (seq? ~gmap) (apply hash-map ~gmap) ~gmap)) ((core/fn [ret] (if (:as b) (conj ret (:as b) gmap) @@ -2059,12 +2065,12 @@ logical true." ([x] (core/when *assert* - `(core/when-not ~x + `(when-not ~x (throw (js/Error. (cljs.core/str "Assert failed: " (cljs.core/pr-str '~x))))))) ([x message] (core/when *assert* - `(core/when-not ~x + `(when-not ~x (throw (js/Error. (cljs.core/str "Assert failed: " ~message "\n" (cljs.core/pr-str '~x)))))))) @@ -2394,8 +2400,8 @@ (throw #?(:clj (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)") :cljs (js/Error. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")))) - (core/let [options (apply core/hash-map options) - default (core/get options :default :default)] + (core/let [options (apply core/hash-map options) + default (core/get options :default :default)] (check-valid-options options :default :hierarchy) `(defonce ~(with-meta mm-name m) (let [method-table# (atom {}) From 318cc065edbac37cbdd53acbe323d8bd93752405 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Jun 2015 11:54:41 -0400 Subject: [PATCH 1207/4033] remove redundant when-not --- src/main/clojure/cljs/core.cljc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 2fe0f4267..8f362f2a1 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -388,12 +388,6 @@ [test & body] (core/list 'if test (cons 'do body)))) -#?(:cljs - (defmacro when-not - "Evaluates test. If logical false, evaluates body in an implicit do." - [test & body] - (core/list 'if test nil (cons 'do body)))) - #?(:cljs (core/defmacro when-first "bindings => x xs From 770d948f4cb383cb5e019eeda2b7fa9cb73c5b5e Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Jul 2015 09:53:26 -0400 Subject: [PATCH 1208/4033] bump to Clojure 1.7.0 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 868671289..c7459da41 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.7.0-RC2 + 1.7.0 com.google.javascript diff --git a/project.clj b/project.clj index 59f5bab85..3385c1e8d 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ :source-paths ["src/main/clojure"] :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure"] - :dependencies [[org.clojure/clojure "1.7.0-RC2"] + :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.2"] [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] diff --git a/script/bootstrap b/script/bootstrap index 0e745c708..c65da49c9 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.7.0-RC2" +CLOJURE_RELEASE="1.7.0" CLOSURE_RELEASE="20150609" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150505-021ed5b3" From 903f39c99bcc44eaf6b5ef87a2e65f86300b4886 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jul 2015 18:35:07 -0400 Subject: [PATCH 1209/4033] macros-ns support in cljs.repl/load-file cljs.compiler/compile-file* now adds :macros-ns info. cljs.closure/map->javascript preserves :macros-ns information. cljs.closure/src-file->goog-require adds $macros ns suffix if :macros-ns true. --- src/main/clojure/cljs/closure.clj | 11 ++++++++--- src/main/clojure/cljs/compiler.cljc | 1 + src/main/clojure/cljs/repl.cljc | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e98c02d73..06bd6cd33 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -347,7 +347,9 @@ (:lines m) (:source-map m)) (when (:closure-lib m) - {:closure-lib true}))) + {:closure-lib true}) + (when (:macros-ns m) + {:macros-ns true}))) (defn read-js "Read a JavaScript file returning a map of file information." @@ -1772,10 +1774,13 @@ (defn ^String src-file->goog-require ([src] (src-file->goog-require src {:wrap true})) - ([src {:keys [wrap all-provides] :as options}] + ([src {:keys [wrap all-provides macros-ns] :as options}] (let [goog-ns (case (util/ext src) - ("cljs" "cljc") (comp/munge (:ns (ana/parse-ns src))) + ("cljs" "cljc") (let [ns-str (str (comp/munge (:ns (ana/parse-ns src))))] + (cond-> ns-str + (and macros-ns (not (.endsWith ns-str "$macros"))) + (str "$macros"))) "js" (cond-> (:provides (parse-js-ns src)) (not all-provides) first) (throw diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 69651fc37..0d815a4a3 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1107,6 +1107,7 @@ (let [sm-data (when *source-map-data* @*source-map-data*) ret (merge {:ns (or ns-name 'cljs.user) + :macros-ns (:macros-ns opts) :provides [ns-name] :requires (if (= ns-name 'cljs.core) (set (vals deps)) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 018550ab2..d1da6858a 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -515,7 +515,8 @@ (load-dependencies repl-env (:requires compiled) opts) (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled)) (-evaluate repl-env f 1 - (cljsc/src-file->goog-require src {:wrap true :reload true}))) + (cljsc/src-file->goog-require src + {:wrap true :reload true :macros-ns (:macros-ns compiled)}))) (binding [ana/*cljs-ns* ana/*cljs-ns*] (let [res (if (= File/separatorChar (first f)) f (io/resource f))] (assert res (str "Can't find " f " in classpath")) From 392ebd7d1359459d21563a75f693d3e26d8cefdf Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jul 2015 18:59:39 -0400 Subject: [PATCH 1210/4033] restore exception message --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 2a900d49c..9b4d8d4c2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9668,7 +9668,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn ^{:jsdoc ["@constructor"]} ExceptionInfo [message data cause] - (let [e (js/Error.)] + (let [e (js/Error. message)] (this-as this (set! (.-message this) message) (set! (.-data this) data) From 8a5023b849cfc509931b3ff509a9f7ee48dd03ec Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jul 2015 19:03:14 -0400 Subject: [PATCH 1211/4033] CLJS-1321: remove getNamespace & getName method calls from defrecord --- src/main/clojure/cljs/core.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8f362f2a1..8f0ec9ed1 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1575,7 +1575,11 @@ (core/let [hinted-fields fields fields (vec (map #(with-meta % nil) fields)) base-fields fields - pr-open (core/str "#" (.getNamespace rname) "." (.getName rname) "{") + pr-open (core/str "#" #?(:clj (.getNamespace rname) + :cljs (namespace rname)) + "." #?(:clj (.getName rname) + :cljs (name rname)) + "{") fields (conj fields '__meta '__extmap (with-meta '__hash {:mutable true}))] (core/let [gs (gensym) ksym (gensym "k") From c3284e72a7bfc9e7419e62a19ba75bb2429512eb Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jul 2015 19:37:00 -0400 Subject: [PATCH 1212/4033] support .json, .clj and .edn in the browser REPL --- src/main/clojure/cljs/repl/browser.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 09f4c5a0e..d0a1f5fa7 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -33,8 +33,11 @@ ".gif" "image/gif" ".js" "text/javascript" + ".json" "application/json" + ".clj" "text/x-clojure" ".cljs" "text/x-clojure" ".cljc" "text/x-clojure" + ".edn" "text/x-clojure" ".map" "application/json"}) (def mime-type->encoding From f706fabfd5f952c4dfb4dc2caeea92f9e00d8287 Mon Sep 17 00:00:00 2001 From: Stuart Mitchell Date: Fri, 3 Jul 2015 14:16:26 +1200 Subject: [PATCH 1213/4033] CLJS-1191: rebased patch Update clojure.walk to the current version on clojure --- src/main/cljs/clojure/walk.cljs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/clojure/walk.cljs b/src/main/cljs/clojure/walk.cljs index f2ebd8d02..817b07526 100644 --- a/src/main/cljs/clojure/walk.cljs +++ b/src/main/cljs/clojure/walk.cljs @@ -43,9 +43,13 @@ the sorting function."} {:added "1.1"} [inner outer form] (cond - (seq? form) (outer (doall (map inner form))) - (coll? form) (outer (into (empty form) (map inner form))) - :else (outer form))) + (list? form) (outer (apply list (map inner form))) + (satisfies? IMapEntry form) (outer (vec (map inner form))) + (seq? form) (outer (doall (map inner form))) + (satisfies? IRecord form) + (outer (reduce (fn [r x] (conj r (inner x))) form form)) + (coll? form) (outer (into (empty form) (map inner form))) + :else (outer form))) (defn postwalk "Performs a depth-first, post-order traversal of form. Calls f on From f1ff78d882bbaaa5135f205dfa19b01895462a3b Mon Sep 17 00:00:00 2001 From: Daniel Skarda Date: Wed, 10 Jun 2015 17:05:40 +0200 Subject: [PATCH 1214/4033] CLJS-1191: tests for prewalk and postwalk --- src/test/cljs/clojure/walk_test.cljs | 22 ++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 ++ 2 files changed, 24 insertions(+) create mode 100644 src/test/cljs/clojure/walk_test.cljs diff --git a/src/test/cljs/clojure/walk_test.cljs b/src/test/cljs/clojure/walk_test.cljs new file mode 100644 index 000000000..4c4d35e80 --- /dev/null +++ b/src/test/cljs/clojure/walk_test.cljs @@ -0,0 +1,22 @@ +(ns clojure.walk-test + (:require [cljs.test :as test + :refer-macros [deftest is testing]] + [clojure.walk :as w])) + +(defrecord Rec1 [a]) + +(defn inc-leaf [x] + (if (number? x) + (inc x) + x)) + +(deftest test-api + (testing "Test walking" + (is (= [2 {1 "1", :two 2}] (w/postwalk inc-leaf [1 {0 "1", :two 1}]))) + (is (= [(Rec1. 2)] (w/postwalk inc-leaf [(Rec1. 1)]))) + + (is (= (map->Rec1 {:a 1, ":a" 1}) + (w/prewalk #(if (keyword? %) (str %) %) (Rec1. 1))) + "Mirror Clojure behavior"))) + + diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 7a88b5b0e..2573dd025 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -6,6 +6,7 @@ [cljs.ns-test] [clojure.string-test] [clojure.data-test] + [clojure.walk-test] [cljs.macro-test] [cljs.letfn-test] [foo.ns-shadow-test] @@ -24,6 +25,7 @@ 'cljs.reader-test 'clojure.string-test 'clojure.data-test + 'clojure.walk-test 'cljs.letfn-test 'cljs.reducers-test 'cljs.binding-test From 4e8d46634db6f4315a469575d3c8d48fae71bb47 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 5 Jul 2015 13:58:29 -0400 Subject: [PATCH 1215/4033] better code gen for protocol fns --- src/main/clojure/cljs/core.cljc | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8f0ec9ed1..55a2d05ba 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1791,15 +1791,19 @@ " defines method " mname " with arity 0")))))) expand-sig (core/fn [fname slot sig] `(~sig - (if (and ~(first sig) (. ~(first sig) ~(symbol (core/str "-" slot)))) ;; Property access needed here. + (if (and (not (nil? ~(first sig))) + (not (nil? (. ~(first sig) ~(symbol (core/str "-" slot)))))) ;; Property access needed here. (. ~(first sig) ~slot ~@sig) - (let [x# (if (nil? ~(first sig)) nil ~(first sig))] - ((or - (aget ~(fqn fname) (goog/typeOf x#)) - (aget ~(fqn fname) "_") - (throw (missing-protocol - ~(core/str psym "." fname) ~(first sig)))) - ~@sig))))) + (let [x# (if (nil? ~(first sig)) nil ~(first sig)) + m# (aget ~(fqn fname) (goog/typeOf x#))] + (if-not (nil? m#) + (m# ~@sig) + (let [m# (aget ~(fqn fname) "_")] + (if-not (nil? m#) + (m# ~@sig) + (throw + (missing-protocol + ~(core/str psym "." fname) ~(first sig)))))))))) psym (vary-meta psym assoc-in [:protocol-info :methods] (into {} (map From f09bbe62e99e11179dec6286fbb46265c12f4737 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 6 Jul 2015 07:23:34 -0400 Subject: [PATCH 1216/4033] avoid Array.prototype.slice pattern, V8 deopts --- src/main/clojure/cljs/core.cljc | 50 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 55a2d05ba..c59c2e620 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2594,6 +2594,13 @@ (set! (. ~sym ~'-cljs$lang$applyTo) ~(apply-to))))))) +(core/defmacro copy-arguments [dest] + `(let [len# (alength (js-arguments))] + (loop [i# 0] + (when (< i# len#) + (.push ~dest (aget (js-arguments) i#)) + (recur (inc i#)))))) + (core/defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl]] (core/letfn [(dest-args [c] (map (core/fn [n] `(aget (js-arguments) ~n)) @@ -2611,12 +2618,13 @@ `(do (def ~(with-meta name meta) (fn [] - (let [argseq# (when (< ~c-1 (alength (js-arguments))) - (new ^::ana/no-resolve cljs.core/IndexedSeq - (.call js/Array.prototype.slice - (js-arguments) ~c-1) 0))] - (. ~rname - (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))))) + (let [args# (array)] + (copy-arguments args#) + (let [argseq# (when (< ~c-1 (alength args#)) + (new ^::ana/no-resolve cljs.core/IndexedSeq + (.slice args# ~c-1) 0))] + (. ~rname + (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#)))))) ~(variadic-fn* rname method))))) (core/comment @@ -2660,23 +2668,25 @@ :max-fixed-arity maxfa :method-params sigs :arglists arglists - :arglists-meta (doall (map meta arglists))})] + :arglists-meta (doall (map meta arglists))}) + args-sym (gensym "args")] `(do (def ~(with-meta name meta) (fn [] - (case (alength (js-arguments)) - ~@(mapcat #(fixed-arity rname %) sigs) - ~(if variadic - `(let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq - (.call js/Array.prototype.slice - (js-arguments) ~maxfa) 0)] - (. ~rname - (~'cljs$core$IFn$_invoke$arity$variadic - ~@(dest-args maxfa) - argseq#))) - `(throw (js/Error. - (str "Invalid arity: " - (alength (js-arguments))))))))) + (let [~args-sym (array)] + (copy-arguments ~args-sym) + (case (alength ~args-sym) + ~@(mapcat #(fixed-arity rname %) sigs) + ~(if variadic + `(let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq + (.slice ~args-sym ~maxfa) 0)] + (. ~rname + (~'cljs$core$IFn$_invoke$arity$variadic + ~@(dest-args maxfa) + argseq#))) + `(throw (js/Error. + (str "Invalid arity: " + (alength ~args-sym))))))))) ~@(map fn-method fdecl) ;; optimization properties (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa))))) From 4ee069608f308b5f38fb4ef9dde8f4c8dd6ec818 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Jul 2015 18:16:18 -0400 Subject: [PATCH 1217/4033] relax find-ns-obj restriction, :simple optimizations can work --- src/main/cljs/cljs/core.cljs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9b4d8d4c2..3e697c548 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9898,16 +9898,12 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (nil? ctxt) nil (nil? xs) ctxt :else (recur (gobject/get ctxt (first xs)) (next xs))))] - (if-not js/COMPILED - (let [segs (-> ns str (.split "."))] - (when (gobject/get (. goog/dependencies_ -nameToPath) (str ns)) - (condp identical? *target* - "nodejs" (find-ns* js/global segs) - "default" (find-ns* js/window segs) - (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) - (throw - (js/Error. - "find-ns-obj not supported when Closure optimization applied"))))) + (let [segs (-> ns str (.split "."))] + (when (gobject/get (. goog/dependencies_ -nameToPath) (str ns)) + (condp identical? *target* + "nodejs" (find-ns* js/global segs) + "default" (find-ns* js/window segs) + (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))))) (defn ns-interns* [sym] (let [ns-obj (find-ns-obj sym) From 05537616244005c6377c9cd088ec7a0c812b9ad3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Jul 2015 18:22:59 -0400 Subject: [PATCH 1218/4033] revert previous commit, when compiled none of the dependency machinery exists --- src/main/cljs/cljs/core.cljs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3e697c548..9b4d8d4c2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9898,12 +9898,16 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (nil? ctxt) nil (nil? xs) ctxt :else (recur (gobject/get ctxt (first xs)) (next xs))))] - (let [segs (-> ns str (.split "."))] - (when (gobject/get (. goog/dependencies_ -nameToPath) (str ns)) - (condp identical? *target* - "nodejs" (find-ns* js/global segs) - "default" (find-ns* js/window segs) - (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))))) + (if-not js/COMPILED + (let [segs (-> ns str (.split "."))] + (when (gobject/get (. goog/dependencies_ -nameToPath) (str ns)) + (condp identical? *target* + "nodejs" (find-ns* js/global segs) + "default" (find-ns* js/window segs) + (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) + (throw + (js/Error. + "find-ns-obj not supported when Closure optimization applied"))))) (defn ns-interns* [sym] (let [ns-obj (find-ns-obj sym) From 73284da49ff35243cfd74709e21bb0d03069063b Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Jul 2015 19:54:00 -0400 Subject: [PATCH 1219/4033] better code gen for implements?, avoid generating closures --- src/main/clojure/cljs/core.cljc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index c59c2e620..bf00e5670 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1845,13 +1845,19 @@ [part bit] (fast-path-protocols p) msym (symbol (core/str "-cljs$lang$protocol_mask$partition" part "$"))] - `(let [~xsym ~x] - (if ~xsym - (let [bit# ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit))] - (if (or bit# - ~(bool-expr `(. ~xsym ~(symbol (core/str "-" prefix))))) + (if-not (core/symbol? x) + `(let [~xsym ~x] + (if ~xsym + (if (or ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit) false) + ~(bool-expr `(. ~xsym ~(symbol (core/str "-" prefix))))) true - false)) + false) + false)) + `(if-not (nil? ~x) + (if (or ~(if bit `(unsafe-bit-and (. ~x ~msym) ~bit) false) + ~(bool-expr `(. ~x ~(symbol (core/str "-" prefix))))) + true + false) false)))) (core/defmacro satisfies? From d5b0d88bed7af276cfa6905d0073bee34fddeec3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 7 Jul 2015 08:00:41 -0400 Subject: [PATCH 1220/4033] don't transform trivial invokes, fixes interaction between boolean type hinted invokes and static-fns optimization --- src/main/clojure/cljs/analyzer.cljc | 6 +++++- src/test/clojure/cljs/compiler_tests.clj | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 757db1f34..1108054af 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1901,7 +1901,11 @@ (warning :fn-deprecated env {:fexpr fexpr})) (when (-> fexpr :info :type) (warning :invoke-ctor env {:fexpr fexpr})) - (if (or (not *cljs-static-fns*) (not (symbol? f)) fn-var? (contains? (meta f) ::analyzed)) + (if (or (not *cljs-static-fns*) + (not (symbol? f)) + fn-var? + (contains? (meta f) ::analyzed) + (every? symbol? args)) (let [argexprs (vec (map #(analyze enve %) args))] {:env env :op :invoke :form form :f fexpr :args argexprs :children (into [fexpr] argexprs)}) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index fa199d69e..70aeeb684 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -86,4 +86,17 @@ '(defn incme [] (let [incme (fn [a queue & args])] (println (incme 1 [1] 1 1)))))))) + ) + +(comment + ;; combining boolean hint w/ static fns + + (binding [ana/*cljs-static-fns* true] + (env/with-compiler-env cenv + (comp/emit + (ana/analyze aenv + '(defn foo [x] + (if ^boolean (goog.array/isEmpty x) + true + false)))))) ) \ No newline at end of file From 37df88780afc4955d807f57ea864038b749be7b4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jul 2015 09:50:54 -0400 Subject: [PATCH 1221/4033] fix js-obj macro to return empty object if no key values --- src/main/clojure/cljs/core.cljc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index bf00e5670..d577e1866 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2313,11 +2313,13 @@ (filter (complement sym-or-str?) (keys kvs)) (repeatedly gensym)) obj (gensym "obj")] - `(let [~@(apply concat (clojure.set/map-invert expr->local)) - ~obj ~(js-obj* (filter-on-keys core/string? kvs))] - ~@(map (core/fn [[k v]] `(aset ~obj ~k ~v)) sym-pairs) - ~@(map (core/fn [[k v]] `(aset ~obj ~v ~(core/get kvs k))) expr->local) - ~obj))) + (if (empty? rest) + (js-obj* '()) + `(let [~@(apply concat (clojure.set/map-invert expr->local)) + ~obj ~(js-obj* (filter-on-keys core/string? kvs))] + ~@(map (core/fn [[k v]] `(aset ~obj ~k ~v)) sym-pairs) + ~@(map (core/fn [[k v]] `(aset ~obj ~v ~(core/get kvs k))) expr->local) + ~obj)))) (core/defmacro alength [a] (vary-meta From 1ba3811c49c9dd5d8d948f60d18799b1d4eab785 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jul 2015 17:54:27 -0400 Subject: [PATCH 1222/4033] goog.string/buildString deoptimized, switch to array join expression --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9b4d8d4c2..4fa287701 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2570,7 +2570,7 @@ reduces them without incurring seq initialization" ([] "") ([x] (if (nil? x) "" - (gstring/buildString x))) + (.join #js [x] ""))) ([x & ys] (loop [sb (StringBuffer. (str x)) more ys] (if more From dd5da7064f037fb232d7c6e61239d9e94f5dd4ac Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jul 2015 18:16:42 -0400 Subject: [PATCH 1223/4033] optimize private munge-str --- src/main/cljs/cljs/core.cljs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4fa287701..d9000e22a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9833,14 +9833,16 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." DEMUNGE_PATTERN) (defn- munge-str [name] - (loop [i 0 ret ""] - (if (< i (. name -length)) - (recur (inc i) - (let [c (.charAt name i)] - (if-let [sub (gobject/get CHAR_MAP c)] - (str ret sub) - (str ret c)))) - ret))) + (let [sb (StringBuffer.)] + (loop [i 0] + (if (< i (. name -length)) + (let [c (.charAt name i) + sub (gobject/get CHAR_MAP c)] + (if-not (nil? sub) + (.append sb sub) + (.append sb c)) + (recur (inc i))))) + (.toString sb))) (defn munge [name] ((if (symbol? name) symbol str) From 2122ccba4ba27dba7b3c71985054f59ef99521f6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jul 2015 18:46:20 -0400 Subject: [PATCH 1224/4033] add caching to find-ns --- src/main/cljs/cljs/core.cljs | 43 +++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d9000e22a..a4f27c5f7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9894,22 +9894,35 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-hash [_] (hash name))) +(def NS_CACHE nil) + +(defn- find-ns-obj* [ctxt xs] + (cond + (nil? ctxt) nil + (nil? xs) ctxt + :else (recur (gobject/get ctxt (first xs)) (next xs)))) + (defn find-ns-obj [ns] - (letfn [(find-ns* [ctxt xs] - (cond - (nil? ctxt) nil - (nil? xs) ctxt - :else (recur (gobject/get ctxt (first xs)) (next xs))))] - (if-not js/COMPILED - (let [segs (-> ns str (.split "."))] - (when (gobject/get (. goog/dependencies_ -nameToPath) (str ns)) - (condp identical? *target* - "nodejs" (find-ns* js/global segs) - "default" (find-ns* js/window segs) - (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) - (throw - (js/Error. - "find-ns-obj not supported when Closure optimization applied"))))) + (if-not js/COMPILED + (do + (when (nil? NS_CACHE) + (set! NS_CACHE (atom {}))) + (let [the-ns (get @NS_CACHE ns)] + (if-not (nil? the-ns) + the-ns + (let [segs (-> ns str (.split "."))] + (when-not (nil? (gobject/get (. goog/dependencies_ -nameToPath) (str ns))) + (case *target* + "nodejs" (let [ns-obj (find-ns-obj* js/global segs)] + (swap! NS_CACHE assoc ns ns-obj) + ns-obj) + "default" (let [ns-obj (find-ns-obj* js/window segs)] + (swap! NS_CACHE assoc ns ns-obj) + ns-obj) + (throw (js/Error. (str "find-ns-obj not supported for target " *target*))))))))) + (throw + (js/Error. + "find-ns-obj not supported when Closure optimization applied")))) (defn ns-interns* [sym] (let [ns-obj (find-ns-obj sym) From d08487d5e135ed15b2be68cfba09d0bc0eb1159f Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jul 2015 19:51:56 -0400 Subject: [PATCH 1225/4033] boolean type hints, simplify code for better performance under :optimizations :none --- src/main/clojure/cljs/analyzer.cljc | 116 +++++++++++++++++----------- 1 file changed, 69 insertions(+), 47 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1108054af..54f75dffe 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -130,7 +130,9 @@ (declare message namespaces) -(defn ast? [x] +(defn ast? + #?(:cljs {:tag boolean}) + [x] (and (map? x) (contains? x :op))) (defmulti error-message (fn [warning-type & _] warning-type)) @@ -443,7 +445,9 @@ (assoc (source-info env) :tag :cljs/analysis-error) cause))) -(defn analysis-error? [ex] +(defn analysis-error? + #?(:cljs {:tag boolean}) + [ex] (= :cljs/analysis-error (:tag (ex-data ex)))) #?(:clj @@ -455,7 +459,9 @@ (throw err#) (throw (error ~env (.getMessage err#) err#))))))) -(defn implicit-import? [env prefix suffix] +(defn implicit-import? + #?(:cljs {:tag boolean}) + [env prefix suffix] (contains? '#{goog goog.object goog.string goog.array Math} prefix)) (defn confirm-var-exists @@ -509,6 +515,7 @@ (defn core-name? "Is sym visible from core in the current compilation namespace?" + #?(:cljs {:tag boolean}) [env sym] (and (or (get-in @env/*compiler* [::namespaces 'cljs.core :defs sym]) (when-let [mac (get-expander sym env)] @@ -688,7 +695,9 @@ m)) methods))) -(defn type? [env t] +(defn type? + #?(:cljs {:tag boolean}) + [env t] ;; don't use resolve-existing-var to avoid warnings (when (and t (symbol? t)) (let [var (resolve-var env t)] @@ -1348,7 +1357,9 @@ (if (and (.exists cljcf) (.isFile cljcf)) cljcf)))))) -(defn foreign-dep? [dep] +(defn foreign-dep? + #?(:cljs {:tag boolean}) + [dep] {:pre [(symbol? dep)]} (let [js-index (:js-dependency-index @env/*compiler*)] (if-let [[_ {:keys [foreign]}] (find js-index (name dep))] @@ -1507,6 +1518,7 @@ (defn macro-autoload-ns? "Given a spec form check whether the spec namespace requires a macro file of the same name. If so return true." + #?(:cljs {:tag boolean}) [form] (when *macro-infer* (let [ns (if (sequential? form) (first form) form) @@ -2000,26 +2012,29 @@ (declare analyze-list) +(defn analyze-seq* [op env form name opts] + (if-not (nil? (get specials op)) + (parse op env form name opts) + (parse-invoke env form))) + (defn analyze-seq ([env form name] (analyze-seq env form name nil)) ([env form name opts] - (if (:quoted? env) - (analyze-list env form) - (let [env (assoc env - :line (or (-> form meta :line) - (:line env)) - :column (or (-> form meta :column) - (:column env)))] - (let [op (first form)] - (when (nil? op) - (throw (error env "Can't call nil"))) - (let [mform (macroexpand-1 env form)] - (if (identical? form mform) - (wrapping-errors env - (if (specials op) - (parse op env form name opts) - (parse-invoke env form))) - (analyze env mform name opts)))))))) + (if ^boolean (:quoted? env) + (analyze-list env form) + (let [line (-> form meta :line) + line (when (nil? line) (:line env)) + col (-> form meta :column) + col (when (nil? col) (:column env)) + env (assoc env :line line :column col)] + (let [op (first form)] + (when (nil? op) + (throw (error env "Can't call nil"))) + (let [mform (macroexpand-1 env form)] + (if (identical? form mform) + (wrapping-errors env + (analyze-seq* op env form name opts)) + (analyze env mform name opts)))))))) (defn analyze-map [env form] @@ -2087,6 +2102,36 @@ (def ^:dynamic *passes* nil) +(defn analyze-form [env form name opts] + #?(:clj (load-core)) + (cond + (symbol? form) (analyze-symbol env form) + (and (seq? form) (seq form)) (analyze-seq env form name opts) + (map? form) (analyze-map env form) + (vector? form) (analyze-vector env form) + (set? form) (analyze-set env form) + (keyword? form) (analyze-keyword env form) + #?@(:clj [(instance? JSValue form) (analyze-js-value env form)]) + (= () form) (analyze-list env form) + :else + (let [tag (cond + (nil? form) 'clj-nil + (number? form) 'number + (string? form) 'string + (true? form) 'boolean + (false? form) 'boolean)] + (cond-> {:op :constant :env env :form form} + tag (assoc :tag tag))))) + +(defn analyze* [env form name opts] + (let [passes *passes* + passes (when (nil? passes) [infer-type]) + form (if (instance? LazySeq form) + (if (seq form) form ()) + form) + ast (analyze-form env form name opts)] + (reduce (fn [ast pass] (pass env ast)) ast passes))) + (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context (one of :statement, :expr, :return), :ns (a symbol naming the @@ -2101,31 +2146,8 @@ (or (nil? opts) (map? opts))]} (ensure (wrapping-errors env - (reduce (fn [ast pass] (pass env ast)) - (binding [reader/*alias-map* (or reader/*alias-map* {})] - (let [form (if (instance? LazySeq form) - (or (seq form) ()) - form)] - #?(:clj (load-core)) - (cond - (symbol? form) (analyze-symbol env form) - (and (seq? form) (seq form)) (analyze-seq env form name opts) - (map? form) (analyze-map env form) - (vector? form) (analyze-vector env form) - (set? form) (analyze-set env form) - (keyword? form) (analyze-keyword env form) - #?@(:clj [(instance? JSValue form) (analyze-js-value env form)]) - (= form ()) (analyze-list env form) - :else - (let [tag (cond - (nil? form) 'clj-nil - (number? form) 'number - (string? form) 'string - (true? form) 'boolean - (false? form) 'boolean)] - (cond-> {:op :constant :env env :form form} - tag (assoc :tag tag)))))) - (or *passes* [infer-type])))))) + (binding [reader/*alias-map* (or reader/*alias-map* {})] + (analyze* env form name opts)))))) #?(:clj (defn- source-path From 14469aff108da504e4dd211ebfaeb94606b69587 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jul 2015 20:03:58 -0400 Subject: [PATCH 1226/4033] remove closure gen from array-index-of-keyword? and array-index-of-symbol? --- src/main/cljs/cljs/core.cljs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a4f27c5f7..a24b56af8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5606,9 +5606,8 @@ reduces them without incurring seq initialization" (loop [i 0] (cond (<= len i) -1 - (let [k' (aget arr i)] - (and (keyword? k') - (identical? kstr (.-fqn k')))) i + (and (keyword? (aget arr i)) + (identical? kstr (.-fqn (aget arr i)))) i :else (recur (+ i 2)))))) (defn- array-index-of-symbol? [arr k] @@ -5617,9 +5616,8 @@ reduces them without incurring seq initialization" (loop [i 0] (cond (<= len i) -1 - (let [k' (aget arr i)] - (and (symbol? k') - (identical? kstr (.-str k')))) i + (and (symbol? (aget arr i)) + (identical? kstr (.-str (aget arr i)))) i :else (recur (+ i 2)))))) (defn- array-index-of-identical? [arr k] From a39b245a504c184c04980cedb1c3bd0d87d01696 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jul 2015 20:41:42 -0400 Subject: [PATCH 1227/4033] cleanup and optimize get-expander --- src/main/clojure/cljs/analyzer.cljc | 71 +++++++++++++++++++---------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 54f75dffe..26afaf23f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1946,35 +1946,56 @@ (resolve-var env sym))) (assoc ret :op :var :info (resolve-var env sym))))))) +(defn excluded? + #?(:cljs {:tag boolean}) + [env sym] + (if-not (nil? (get-in env [:ns :excludes sym])) + true + (not (nil? (get-in @env/*compiler* [::namespaces (get-in env [:ns :name]) :excludes sym]))))) + +(defn used? + #?(:cljs {:tag boolean}) + [env sym] + (if-not (nil? (get-in env [:ns :use-macros sym])) + true + (not (nil? (get-in @env/*compiler* [::namespaces (get-in env [:ns :name]) :use-macros sym]))))) + +(defn get-expander-ns [env ^String nstr] + (cond + (identical? "clojure.core" nstr) + #?(:clj (find-ns 'cljs.core) + :cljs (find-macros-ns 'cljs.core)) + (identical? "clojure.repl" nstr) + #?(:clj (find-ns 'cljs.repl) + :cljs (find-macros-ns 'cljs.repl)) + #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] + :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) + :else + (some-> env :ns :require-macros (get (symbol nstr)) find-ns))) + +(defn get-expander* [sym env] + (when-not (or (not (nil? (get-in env [:locals sym]))) ; locals hide macros + (and (excluded? env sym) (not (used? env sym)))) + (let [nstr (namespace sym)] + (if-not (nil? nstr) + (let [ns (get-expander-ns env nstr)] + (when-not (nil? ns) + (.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym))))) + (let [nsym (get-in env [:ns :use-macros sym])] + (if-not (nil? 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 'cljs.core)) sym))))))) + (defn get-expander "Given a sym, a symbol identifying a macro, and env, an analysis environment return the corresponding Clojure macroexpander." [sym env] - (let [mvar - (when-not (or (-> env :locals sym) ;locals hide macros - (and (or (-> env :ns :excludes sym) - (get-in @env/*compiler* [::namespaces (-> env :ns :name) :excludes sym])) - (not (or (-> env :ns :use-macros sym) - (get-in @env/*compiler* [::namespaces (-> env :ns :name) :use-macros sym]))))) - (if-let [nstr (namespace sym)] - (when-let [ns (cond - (= "clojure.core" nstr) - #?(:clj (find-ns 'cljs.core) - :cljs (find-macros-ns 'cljs.core)) - (= "clojure.repl" nstr) - #?(:clj (find-ns 'cljs.repl) - :cljs (find-macros-ns 'cljs.repl)) - #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] - :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) - :else - (some-> env :ns :require-macros (get (symbol nstr)) find-ns))] - (.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym)))) - (if-let [nsym (-> env :ns :use-macros sym)] - (.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 'cljs.core)) sym))))] - (when (and mvar (.isMacro ^clojure.lang.Var mvar)) + (let [mvar (get-expander* sym env)] + (when (and (not (nil? mvar)) + #?(:clj (.isMacro ^clojure.lang.Var mvar) + :cljs ^boolean (.isMacro mvar))) mvar))) (defn macroexpand-1 From 2924c4880c05208beb0f321e8e4e63b4cb1c45f3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 7 Jul 2015 23:36:23 -0400 Subject: [PATCH 1228/4033] CLJS-1329: Support for reading #js tagged literals in bootstrap Adds a cond branch to analyze form for JSValue when in :cljs mode. Also makes a slight adjustment to analyze-js-value to use `.-` for portable field access between Clj and Cljs. --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 26afaf23f..b67659a15 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -27,6 +27,7 @@ :cljs (:require [clojure.string :as string] [clojure.set :as set] [cljs.env :as env] + [cljs.tagged-literals :as tags] [cljs.tools.reader :as reader] [cljs.tools.reader.reader-types :as readers] [cljs.reader :as edn])) @@ -2087,7 +2088,7 @@ (defn analyze-js-value [env ^JSValue form] - (let [val (.val form) + (let [val (.-val form) expr-env (assoc env :context :expr) items (if (map? val) (zipmap (keys val) @@ -2132,7 +2133,8 @@ (vector? form) (analyze-vector env form) (set? form) (analyze-set env form) (keyword? form) (analyze-keyword env form) - #?@(:clj [(instance? JSValue form) (analyze-js-value env form)]) + #?@(:clj [(instance? JSValue form) (analyze-js-value env form)] + :cljs [(instance? cljs.tagged-literals/JSValue form) (analyze-js-value env form)]) (= () form) (analyze-list env form) :else (let [tag (cond From 5c78d79ecb69458104fdc21e30b93b9815f261a4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 9 Jul 2015 09:22:37 -0400 Subject: [PATCH 1229/4033] enhance satisfies? code gen using same approach as implements?, eliminates closures --- src/main/clojure/cljs/core.cljc | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index d577e1866..08ac25814 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1871,16 +1871,24 @@ [part bit] (fast-path-protocols p) msym (symbol (core/str "-cljs$lang$protocol_mask$partition" part "$"))] - `(let [~xsym ~x] - (if ~xsym - (let [bit# ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit))] - (if (or bit# - ~(bool-expr `(. ~xsym ~(symbol (core/str "-" prefix))))) + (if-not (core/symbol? x) + `(let [~xsym ~x] + (if-not (nil? ~xsym) + (if (or ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit) false) + ~(bool-expr `(. ~xsym ~(symbol (core/str "-" prefix))))) true (if (coercive-not (. ~xsym ~msym)) (cljs.core/native-satisfies? ~psym ~xsym) - false))) - (cljs.core/native-satisfies? ~psym ~xsym))))) + false)) + (cljs.core/native-satisfies? ~psym ~xsym))) + `(if-not (nil? ~x) + (if (or ~(if bit `(unsafe-bit-and (. ~x ~msym) ~bit) false) + ~(bool-expr `(. ~x ~(symbol (core/str "-" prefix))))) + true + (if (coercive-not (. ~x ~msym)) + (cljs.core/native-satisfies? ~psym ~x) + false)) + (cljs.core/native-satisfies? ~psym ~x))))) (core/defmacro lazy-seq "Takes a body of expressions that returns an ISeq or nil, and yields From 81b43c14e9c8e600436df85ae8b0b9be6e943571 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 17:03:45 -0400 Subject: [PATCH 1230/4033] optimize munge and demunge --- src/main/cljs/cljs/core.cljs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a24b56af8..0b242b4af 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9843,12 +9843,14 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (.toString sb))) (defn munge [name] - ((if (symbol? name) symbol str) - (let [name' (munge-str (str name))] - (cond - (= name' "..") "_DOT__DOT_" - (js-reserved? name') (str name' "$") - :else name')))) + (let [name' (munge-str (str name)) + name' (cond + (identical? name' "..") "_DOT__DOT_" + (js-reserved? name') (str name' "$") + :else name')] + (if (symbol? name) + (symbol name') + (str name')))) (defn- demunge-str [munged-name] (let [r (js/RegExp. (demunge-pattern) "g") @@ -9861,7 +9863,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (str ret (.substring munged-name last-match-end (- (. r -lastIndex) (. x -length))) - (if (= x "$") "/" (gobject/get DEMUNGE_MAP x))) + (if (identical? x "$") "/" (gobject/get DEMUNGE_MAP x))) (. r -lastIndex))) (str ret (.substring munged-name last-match-end (.-length munged-name))))))) @@ -9869,7 +9871,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn demunge [name] ((if (symbol? name) symbol str) (let [name' (str name)] - (if (= name' "_DOT__DOT_") + (if (identical? name' "_DOT__DOT_") ".." (demunge-str (str name)))))) From 5921263eddb980ea74c7499ae18147e41f028b84 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 17:08:28 -0400 Subject: [PATCH 1231/4033] move try/catch out of analyze-seq --- src/main/clojure/cljs/analyzer.cljc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b67659a15..42c355a68 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2039,6 +2039,10 @@ (parse op env form name opts) (parse-invoke env form))) +(defn analyze-seq*-wrap [op env form name opts] + (wrapping-errors env + (analyze-seq* op env form name opts))) + (defn analyze-seq ([env form name] (analyze-seq env form name nil)) ([env form name opts] @@ -2054,8 +2058,7 @@ (throw (error env "Can't call nil"))) (let [mform (macroexpand-1 env form)] (if (identical? form mform) - (wrapping-errors env - (analyze-seq* op env form name opts)) + (analyze-seq*-wrap op env form name opts) (analyze env mform name opts)))))))) (defn analyze-map From cc006358514e440e73e33db1b3066cab63edc16a Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 17:23:49 -0400 Subject: [PATCH 1232/4033] add symbol-identical? --- src/main/cljs/cljs/core.cljs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0b242b4af..03f43fdae 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2910,11 +2910,19 @@ reduces them without incurring seq initialization" [x y] (if (identical? x y) true - (if (and (keyword? x) - (keyword? y)) + (if (and (keyword? x) (keyword? y)) (identical? (.-fqn x) (.-fqn y)) false))) +(defn ^boolean symbol-identical? + "Efficient test to determine that two symbol are identical." + [x y] + (if (identical? x y) + true + (if (and (symbol? x) (symbol? y)) + (identical? (.-str x) (.-str y)) + false))) + (defn namespace "Returns the namespace String of a symbol or keyword, or nil if not present." [x] From c580833542e8160c56b593eb5b8ea16a5d4e234c Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 17:39:16 -0400 Subject: [PATCH 1233/4033] lift macroexpand-1 body out of try/catch, drop binding *ns* for now, use symbol-identical?, = -> identical? for char checks --- src/main/clojure/cljs/analyzer.cljc | 64 ++++++++++++++++------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 42c355a68..ddb2ad23a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1999,38 +1999,46 @@ :cljs ^boolean (.isMacro mvar))) mvar))) +(defn macroexpand-1* + [env form] + (let [op (first form)] + (if-not (nil? (get specials op)) + form + (let [mac-var (when (symbol? op) (get-expander op env))] + (if-not (nil? mac-var) + (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] + :cljs [do]) + (let [form' (apply @mac-var form env (rest form))] + (if (seq? form') + (let [sym' (first form') + sym (first form)] + (if #?(:clj (= sym' 'js*) :cljs (symbol-identical? sym' 'js*)) + (vary-meta form' merge + (cond-> {:js-op (if (namespace sym) sym (symbol "cljs.core" (str sym)))} + (-> mac-var meta ::numeric) (assoc :numeric true))) + form')) + form'))) + (if (symbol? op) + (let [opname (str op)] + (cond + (identical? (first opname) \.) + (let [[target & args] (next form)] + (with-meta (list* '. target (symbol (subs opname 1)) args) + (meta form))) + + (identical? (last opname) \.) + (with-meta + (list* 'new (symbol (subs opname 0 (dec (count opname)))) (next form)) + (meta form)) + + :else form)) + form)))))) + (defn macroexpand-1 "Given a env, an analysis environment, and form, a ClojureScript form, macroexpand the form once." [env form] - (ensure - (wrapping-errors env - (let [op (first form)] - (if (specials op) - form - (if-let [mac-var (and (symbol? op) (get-expander op env))] - (binding [*ns* (create-ns *cljs-ns*)] - (let [form' (apply @mac-var form env (rest form))] - (if (seq? form') - (let [sym' (first form') - sym (first form)] - (if (= sym' 'js*) - (vary-meta form' merge - (cond-> {:js-op (if (namespace sym) sym (symbol "cljs.core" (str sym)))} - (-> mac-var meta ::numeric) (assoc :numeric true))) - form')) - form'))) - (if (symbol? op) - (let [opname (str op)] - (cond - (= (first opname) \.) (let [[target & args] (next form)] - (with-meta (list* '. target (symbol (subs opname 1)) args) - (meta form))) - (= (last opname) \.) (with-meta - (list* 'new (symbol (subs opname 0 (dec (count opname)))) (next form)) - (meta form)) - :else form)) - form))))))) + (ensure (wrapping-errors env (macroexpand-1* env form)))) (declare analyze-list) From 6b590f2fdf898e94a65153f8059ebdf0e3ec0952 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 17:58:30 -0400 Subject: [PATCH 1234/4033] more perf tweaks --- src/main/cljs/cljs/core.cljs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 03f43fdae..fcc9799de 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9883,13 +9883,17 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." ".." (demunge-str (str name)))))) +(defn- ns-lookup [ns-obj k] + (fn [] (gobject/get ns-obj k))) + (deftype Namespace [obj name] Object (findInternedVar [this sym] (let [k (munge (str sym))] - (when (gobject/containsKey obj k) - (Var. #(gobject/get obj k) - (symbol (str name) (str sym)) {:ns this})))) + (when ^boolean (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)) @@ -9949,14 +9953,17 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (Namespace. ns-obj sym))) (defn find-ns [ns] - (when-let [ns-obj (find-ns-obj ns)] - (create-ns ns ns-obj))) + (let [ns-obj (find-ns-obj ns)] + (when-not (nil? ns-obj) + (create-ns ns ns-obj)))) (defn find-macros-ns [ns] - (let [ns (cond-> ns - (not (gstring/contains (str ns) "$macros")) - (-> (str "$macros") symbol))] - (when-let [ns-obj (find-ns-obj ns)] + (let [ns-str (str ns) + ns (if (not ^boolean (gstring/contains ns-str "$macros")) + (symbol (str ns-str "$macros")) + ns) + ns-obj (find-ns-obj ns)] + (when-not (nil? ns-obj) (create-ns ns ns-obj)))) (defn ns-name [ns-obj] From 381b979ec73ce24515b4e2086d6b0d6d0c952d3a Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 19:11:20 -0400 Subject: [PATCH 1235/4033] enhance analyzer so that invokes with only var or constant arguments are not transformed --- src/main/clojure/cljs/analyzer.cljc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ddb2ad23a..e7abb3cf2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1914,18 +1914,18 @@ (warning :fn-deprecated env {:fexpr fexpr})) (when (-> fexpr :info :type) (warning :invoke-ctor env {:fexpr fexpr})) - (if (or (not *cljs-static-fns*) - (not (symbol? f)) - fn-var? - (contains? (meta f) ::analyzed) - (every? symbol? args)) - (let [argexprs (vec (map #(analyze enve %) args))] + (let [argexprs (vec (map #(analyze enve %) args))] + (if (or (not *cljs-static-fns*) + (not (symbol? f)) + fn-var? + (contains? (meta f) ::analyzed) + (every? #{:var :constant} (map :op argexprs))) {:env env :op :invoke :form form :f fexpr :args argexprs - :children (into [fexpr] argexprs)}) - (let [arg-syms (take argc (repeatedly gensym))] - (analyze env - `(let [~@(vec (interleave arg-syms args))] - (~(vary-meta f assoc ::analyzed true) ~@arg-syms)))))))) + :children (into [fexpr] argexprs)} + (let [arg-syms (take argc (repeatedly gensym))] + (analyze env + `(let [~@(vec (interleave arg-syms args))] + (~(vary-meta f assoc ::analyzed true) ~@arg-syms))))))))) (defn analyze-symbol "Finds the var associated with sym" From ca02f924255dcc442b70721e122cc56a070a2552 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 19:54:48 -0400 Subject: [PATCH 1236/4033] make analyze-form faster by testing only for direct implementers of the protocols, shaves off another second --- src/main/clojure/cljs/analyzer.cljc | 79 +++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e7abb3cf2..b94faa009 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2135,27 +2135,64 @@ (def ^:dynamic *passes* nil) -(defn analyze-form [env form name opts] - #?(:clj (load-core)) - (cond - (symbol? form) (analyze-symbol env form) - (and (seq? form) (seq form)) (analyze-seq env form name opts) - (map? form) (analyze-map env form) - (vector? form) (analyze-vector env form) - (set? form) (analyze-set env form) - (keyword? form) (analyze-keyword env form) - #?@(:clj [(instance? JSValue form) (analyze-js-value env form)] - :cljs [(instance? cljs.tagged-literals/JSValue form) (analyze-js-value env form)]) - (= () form) (analyze-list env form) - :else - (let [tag (cond - (nil? form) 'clj-nil - (number? form) 'number - (string? form) 'string - (true? form) 'boolean - (false? form) 'boolean)] - (cond-> {:op :constant :env env :form form} - tag (assoc :tag tag))))) +#?(:clj + (defn analyze-form [env form name opts] + (load-core) + (cond + (symbol? form) (analyze-symbol env form) + (and (seq? form) (seq form)) (analyze-seq env form name opts) + (map? form) (analyze-map env form) + (vector? form) (analyze-vector env form) + (set? form) (analyze-set env form) + (keyword? form) (analyze-keyword env form) + (instance? JSValue form) (analyze-js-value env form) + (= () form) (analyze-list env form) + :else + (let [tag (cond + (nil? form) 'clj-nil + (number? form) 'number + (string? form) 'string + (true? form) 'boolean + (false? form) 'boolean)] + (cond-> {:op :constant :env env :form form} + tag (assoc :tag tag)))))) + +#?(:cljs + (defn ^boolean cljs-seq? [x] + (implements? ISeq x))) + +#?(:cljs + (defn ^boolean cljs-map? [x] + (implements? IMap x))) + +#?(:cljs + (defn ^boolean cljs-vector? [x] + (implements? IVector x))) + +#?(:cljs + (defn ^boolean cljs-set? [x] + (implements? ISet x))) + +#?(:cljs + (defn analyze-form [env form name opts] + (cond + (symbol? form) (analyze-symbol env form) + (and (cljs-seq? form) (seq form)) (analyze-seq env form name opts) + (cljs-map? form) (analyze-map env form) + (cljs-vector? form) (analyze-vector env form) + (cljs-set? form) (analyze-set env form) + (keyword? form) (analyze-keyword env form) + (instance? cljs.tagged-literals/JSValue form) (analyze-js-value env form) + (= () form) (analyze-list env form) + :else + (let [tag (cond + (nil? form) 'clj-nil + (number? form) 'number + (string? form) 'string + (true? form) 'boolean + (false? form) 'boolean)] + (cond-> {:op :constant :env env :form form} + tag (assoc :tag tag)))))) (defn analyze* [env form name opts] (let [passes *passes* From b0a2c57e5627f4a09195199d0b59dc85d57ea52b Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 20:35:19 -0400 Subject: [PATCH 1237/4033] pre-allocate some symbols --- src/main/clojure/cljs/analyzer.cljc | 78 +++++++++++++++++++---------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b94faa009..87039f3c4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -103,6 +103,46 @@ "volatile" "while" "with" "yield" "methods" "null"}) +#?(:cljs + (def CLJ_NIL_SYM 'clj-nil)) + +#?(:cljs + (def NUMBER_SYM 'number)) + +#?(:cljs + (def STRING_SYM 'string)) + +#?(:cljs + (def BOOLEAN_SYM 'boolean)) + +#?(:cljs + (def JS_STAR_SYM 'js*)) + +#?(:cljs + (def DOT_SYM '.)) + +#?(:cljs + (def NEW_SYM 'new)) + +#?(:cljs + (def CLJS_CORE_SYM 'cljs.core)) + +#?(:cljs + (defn ^boolean cljs-seq? [x] + (implements? ISeq x))) + +#?(:cljs + (defn ^boolean cljs-map? [x] + (implements? IMap x))) + +#?(:cljs + (defn ^boolean cljs-vector? [x] + (implements? IVector x))) + +#?(:cljs + (defn ^boolean cljs-set? [x] + (implements? ISet x))) + #?(:cljs (defn munge-path [ss] (munge (str ss)))) @@ -1965,7 +2005,7 @@ (cond (identical? "clojure.core" nstr) #?(:clj (find-ns 'cljs.core) - :cljs (find-macros-ns 'cljs.core)) + :cljs (find-macros-ns CLJS_CORE_SYM)) (identical? "clojure.repl" nstr) #?(:clj (find-ns 'cljs.repl) :cljs (find-macros-ns 'cljs.repl)) @@ -1987,7 +2027,7 @@ (.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 'cljs.core)) sym))))))) + #?(:clj (find-ns 'cljs.core) :cljs (find-macros-ns CLJS_CORE_SYM)) sym))))))) (defn get-expander "Given a sym, a symbol identifying a macro, and env, an analysis environment @@ -2009,10 +2049,10 @@ (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) (let [form' (apply @mac-var form env (rest form))] - (if (seq? form') + (if #?(:clj (seq? form') :cljs (cljs-seq? form')) (let [sym' (first form') sym (first form)] - (if #?(:clj (= sym' 'js*) :cljs (symbol-identical? sym' 'js*)) + (if #?(:clj (= sym' 'js*) :cljs (symbol-identical? sym' JS_STAR_SYM)) (vary-meta form' merge (cond-> {:js-op (if (namespace sym) sym (symbol "cljs.core" (str sym)))} (-> mac-var meta ::numeric) (assoc :numeric true))) @@ -2023,12 +2063,12 @@ (cond (identical? (first opname) \.) (let [[target & args] (next form)] - (with-meta (list* '. target (symbol (subs opname 1)) args) + (with-meta (list* #?(:clj '. :cljs DOT_SYM) target (symbol (subs opname 1)) args) (meta form))) (identical? (last opname) \.) (with-meta - (list* 'new (symbol (subs opname 0 (dec (count opname)))) (next form)) + (list* #?(:clj 'new :cljs NEW_SYM) (symbol (subs opname 0 (dec (count opname)))) (next form)) (meta form)) :else form)) @@ -2157,22 +2197,6 @@ (cond-> {:op :constant :env env :form form} tag (assoc :tag tag)))))) -#?(:cljs - (defn ^boolean cljs-seq? [x] - (implements? ISeq x))) - -#?(:cljs - (defn ^boolean cljs-map? [x] - (implements? IMap x))) - -#?(:cljs - (defn ^boolean cljs-vector? [x] - (implements? IVector x))) - -#?(:cljs - (defn ^boolean cljs-set? [x] - (implements? ISet x))) - #?(:cljs (defn analyze-form [env form name opts] (cond @@ -2186,11 +2210,11 @@ (= () form) (analyze-list env form) :else (let [tag (cond - (nil? form) 'clj-nil - (number? form) 'number - (string? form) 'string - (true? form) 'boolean - (false? form) 'boolean)] + (nil? form) CLJ_NIL_SYM + (number? form) NUMBER_SYM + (string? form) STRING_SYM + (true? form) BOOLEAN_SYM + (false? form) BOOLEAN_SYM)] (cond-> {:op :constant :env env :form form} tag (assoc :tag tag)))))) From 3fc5189b8cd245111079906b42555ae44f32d1b0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 21:19:48 -0400 Subject: [PATCH 1238/4033] refactor type inference --- src/main/clojure/cljs/analyzer.cljc | 133 +++++++++++++++++----------- 1 file changed, 82 insertions(+), 51 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 87039f3c4..f7423c0f2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -112,8 +112,7 @@ #?(:cljs (def STRING_SYM 'string)) -#?(:cljs - (def BOOLEAN_SYM 'boolean)) +(def BOOLEAN_SYM 'boolean) #?(:cljs (def JS_STAR_SYM 'js*)) @@ -127,6 +126,10 @@ #?(:cljs (def CLJS_CORE_SYM 'cljs.core)) +(def IGNORE_SYM 'ignore) + +(def ANY_SYM 'any) + #?(:cljs (defn ^boolean cljs-seq? [x] (implements? ISeq x))) @@ -740,15 +743,66 @@ #?(:cljs {:tag boolean}) [env t] ;; don't use resolve-existing-var to avoid warnings - (when (and t (symbol? t)) - (let [var (resolve-var env t)] - (or (:type var) - (-> var :info :type) - (:protocol-symbol var) - ;; need to hard code some cases because of - ;; forward declaration - David - ('#{cljs.core/PersistentHashMap - cljs.core/List} t))))) + (when (and (not (nil? t)) (symbol? t)) + (let [var (resolve-var env t) + type (:type var)] + (if-not (nil? type) + type + (let [type (-> var :info :type)] + (if-not (nil? type) + type + (let [proto (:protocol-symbol var)] + (if-not (nil? proto) + proto + (get '#{cljs.core/PersistentHashMap cljs.core/List} t))))))))) + +(declare infer-tag) + +(def NOT_NATIVE '#{clj not-native}) + +(def BOOLEAN_OR_SEQ '#{boolean seq}) + +(defn infer-if [env e] + (let [{{:keys [op form]} :test} e + then-tag (infer-tag env (:then e))] + (if (and #?(:clj (= op :constant) + :cljs (keyword-identical? op :constant)) + (not (nil? form)) + (not (false? form))) + then-tag + (let [else-tag (infer-tag env (:else e))] + (cond + (or #?(:clj (= then-tag else-tag) + :cljs (symbol-identical? then-tag else-tag)) + #?(:clj (= else-tag IGNORE_SYM) + :cljs (symbol-identical? else-tag IGNORE_SYM))) then-tag + #?(:clj (= then-tag IGNORE_SYM) + :cljs (symbol-identical? then-tag IGNORE_SYM)) else-tag + ;; TODO: temporary until we move not-native -> clj - David + (and (or (not (nil? (get NOT_NATIVE then-tag))) (type? env then-tag)) + (or (not (nil? (get NOT_NATIVE else-tag))) (type? env else-tag))) + 'clj + :else + (if (and (not (nil? (get BOOLEAN_OR_SEQ then-tag))) + (not (nil? (get BOOLEAN_OR_SEQ else-tag)))) + 'seq + (let [then-tag (if #?(:clj (set? then-tag) + :cljs (cljs-set? then-tag)) + then-tag #{then-tag}) + else-tag (if #?(:clj (set? else-tag) + :cljs (cljs-set? else-tag)) + else-tag #{else-tag})] + (into then-tag else-tag)))))))) + +(defn infer-invoke [env e] + (let [{info :info :as f} (:f e) + ret-tag (when-not (nil? (:fn-var info)) (:ret-tag info))] + (if-not (nil? ret-tag) + ret-tag + (let [ret-tag (infer-tag env (assoc (find-matching-method f (:args e)) :op :method))] + (if-not (nil? ret-tag) + ret-tag + ANY_SYM))))) (defn infer-tag "Given env, an analysis environment, and e, an AST node, return the inferred @@ -757,47 +811,24 @@ (if-let [tag (get-tag e)] tag (case (:op e) - :recur 'ignore - :throw 'ignore - :let (infer-tag env (:expr e)) - :loop (infer-tag env (:expr e)) - :do (infer-tag env (:ret e)) - :method (infer-tag env (:expr e)) - :def (infer-tag env (:init e)) - :invoke (let [{info :info :as f} (:f e)] - (or (and (:fn-var info) (:ret-tag info)) - (infer-tag env - (assoc (find-matching-method f (:args e)) :op :method)) - 'any)) - :if (let [{{:keys [op form]} :test} e - then-tag (infer-tag env (:then e))] - (if (and (= op :constant) - (not (#{nil false} form))) - then-tag - (let [else-tag (infer-tag env (:else e))] - (cond - (or (= then-tag else-tag) - (= else-tag 'ignore)) then-tag - (= then-tag 'ignore) else-tag - ;; TODO: temporary until we move not-native -> clj - David - (and (or ('#{clj not-native} then-tag) (type? env then-tag)) - (or ('#{clj not-native} else-tag) (type? env else-tag))) - 'clj - :else - (if (every? '#{boolean seq} [then-tag else-tag]) - 'seq - (let [then-tag (if (set? then-tag) then-tag #{then-tag}) - else-tag (if (set? else-tag) else-tag #{else-tag})] - (into then-tag else-tag))))))) + :recur IGNORE_SYM + :throw IGNORE_SYM + :let (infer-tag env (:expr e)) + :loop (infer-tag env (:expr e)) + :do (infer-tag env (:ret e)) + :method (infer-tag env (:expr e)) + :def (infer-tag env (:init e)) + :invoke (infer-invoke env e) + :if (infer-if env e) :constant (case (:form e) - true 'boolean - false 'boolean - 'any) - :var (if (:init e) - (infer-tag env (:init e)) - (infer-tag env (:info e))) - :dot 'any - :js 'any + true BOOLEAN_SYM + false BOOLEAN_SYM + ANY_SYM) + :var (if (:init e) + (infer-tag env (:init e)) + (infer-tag env (:info e))) + :dot ANY_SYM + :js ANY_SYM nil))) (defmulti parse (fn [op & rest] op)) From 74a10c1a695cec6391c81283bb7cae41d0bc3a58 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 22:05:57 -0400 Subject: [PATCH 1239/4033] smarter ns caching --- src/main/cljs/cljs/core.cljs | 56 +++++++++++++++-------------- src/main/clojure/cljs/analyzer.cljc | 7 ++-- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fcc9799de..3113055ec 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9916,22 +9916,12 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn find-ns-obj [ns] (if-not js/COMPILED - (do - (when (nil? NS_CACHE) - (set! NS_CACHE (atom {}))) - (let [the-ns (get @NS_CACHE ns)] - (if-not (nil? the-ns) - the-ns - (let [segs (-> ns str (.split "."))] - (when-not (nil? (gobject/get (. goog/dependencies_ -nameToPath) (str ns))) - (case *target* - "nodejs" (let [ns-obj (find-ns-obj* js/global segs)] - (swap! NS_CACHE assoc ns ns-obj) - ns-obj) - "default" (let [ns-obj (find-ns-obj* js/window segs)] - (swap! NS_CACHE assoc ns ns-obj) - ns-obj) - (throw (js/Error. (str "find-ns-obj not supported for target " *target*))))))))) + (let [segs (-> ns str (.split "."))] + (when-not (nil? (gobject/get (. goog/dependencies_ -nameToPath) (str ns))) + (case *target* + "nodejs" (find-ns-obj* js/global segs) + "default" (find-ns-obj* js/window segs) + (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) (throw (js/Error. "find-ns-obj not supported when Closure optimization applied")))) @@ -9953,18 +9943,32 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (Namespace. ns-obj sym))) (defn find-ns [ns] - (let [ns-obj (find-ns-obj ns)] - (when-not (nil? ns-obj) - (create-ns ns ns-obj)))) + (when (nil? NS_CACHE) + (set! NS_CACHE (atom {}))) + (let [the-ns (get @NS_CACHE ns)] + (if-not (nil? the-ns) + the-ns + (let [ns-obj (find-ns-obj ns)] + (when-not (nil? ns-obj) + (let [new-ns (create-ns ns ns-obj)] + (swap! NS_CACHE assoc ns new-ns) + new-ns)))))) (defn find-macros-ns [ns] - (let [ns-str (str ns) - ns (if (not ^boolean (gstring/contains ns-str "$macros")) - (symbol (str ns-str "$macros")) - ns) - ns-obj (find-ns-obj ns)] - (when-not (nil? ns-obj) - (create-ns ns ns-obj)))) + (when (nil? NS_CACHE) + (set! NS_CACHE (atom {}))) + (let [the-ns (get @NS_CACHE ns)] + (if-not (nil? the-ns) + the-ns + (let [ns-str (str ns) + ns (if (not ^boolean (gstring/contains ns-str "$macros")) + (symbol (str ns-str "$macros")) + ns) + ns-obj (find-ns-obj ns)] + (when-not (nil? ns-obj) + (let [new-ns (create-ns ns ns-obj)] + (swap! NS_CACHE assoc ns new-ns) + new-ns)))))) (defn ns-name [ns-obj] (.-name ns-obj)) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f7423c0f2..a57fece1a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -126,6 +126,9 @@ #?(:cljs (def CLJS_CORE_SYM 'cljs.core)) +#?(:cljs + (def CLJS_CORE_MACROS_SYM 'cljs.core$macros)) + (def IGNORE_SYM 'ignore) (def ANY_SYM 'any) @@ -2036,7 +2039,7 @@ (cond (identical? "clojure.core" nstr) #?(:clj (find-ns 'cljs.core) - :cljs (find-macros-ns CLJS_CORE_SYM)) + :cljs (find-macros-ns CLJS_CORE_MACROS_SYM)) (identical? "clojure.repl" nstr) #?(:clj (find-ns 'cljs.repl) :cljs (find-macros-ns 'cljs.repl)) @@ -2058,7 +2061,7 @@ (.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 CLJS_CORE_SYM)) sym))))))) + #?(:clj (find-ns 'cljs.core) :cljs (find-macros-ns CLJS_CORE_MACROS_SYM)) sym))))))) (defn get-expander "Given a sym, a symbol identifying a macro, and env, an analysis environment From bb74110dde8dbac9dbd02aa6ec56964096a4f1ac Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jul 2015 22:44:07 -0400 Subject: [PATCH 1240/4033] optimize boolean, not, and parse-invoke --- src/main/cljs/cljs/core.cljs | 11 +++- src/main/clojure/cljs/analyzer.cljc | 80 ++++++++++++++++++----------- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3113055ec..9692024f2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -181,7 +181,11 @@ (defn ^boolean not "Returns true if x is logical false, false otherwise." - [x] (if x false true)) + [x] + (cond + (nil? x) true + (false? x) true + :else false)) (defn ^boolean some? "Returns true if x is not nil, false otherwise." @@ -1959,7 +1963,10 @@ reduces them without incurring seq initialization" (defn ^boolean boolean "Coerce to boolean" [x] - (if x true false)) + (cond + (nil? x) false + (false? x) false + :else true)) (defn ^boolean ifn? "Returns true if f returns true for fn? or satisfies IFn." diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a57fece1a..8f65e4e73 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1969,37 +1969,57 @@ :js-op (-> form meta :js-op) :numeric (-> form meta :numeric)}))) -(defn parse-invoke +(defn- analyzed? + #?(:cljs {:tag boolean}) + [f] + (contains? (meta f) ::analyzed)) + +(defn- all-values? + #?(:cljs {:tag boolean}) + [exprs] + (every? #{:var :constant} (map :op exprs))) + +(defn- valid-arity? + #?(:cljs {:tag boolean}) + [argc method-params] + (boolean (some #{argc} (map count method-params)))) + +(defn parse-invoke* [env [f & args :as form]] - (disallowing-recur - (let [enve (assoc env :context :expr) - fexpr (analyze enve f) - argc (count args) - fn-var? (-> fexpr :info :fn-var)] - (when fn-var? - (let [{:keys [variadic max-fixed-arity method-params name]} (:info fexpr)] - (when (and (not (some #{argc} (map count method-params))) - (or (not variadic) - (and variadic (< argc max-fixed-arity)))) - (warning :fn-arity env {:name name - :argc argc})))) - (when (and (-> fexpr :info :deprecated) - (not (-> form meta :deprecation-nowarn))) - (warning :fn-deprecated env {:fexpr fexpr})) - (when (-> fexpr :info :type) - (warning :invoke-ctor env {:fexpr fexpr})) - (let [argexprs (vec (map #(analyze enve %) args))] - (if (or (not *cljs-static-fns*) - (not (symbol? f)) - fn-var? - (contains? (meta f) ::analyzed) - (every? #{:var :constant} (map :op argexprs))) - {:env env :op :invoke :form form :f fexpr :args argexprs - :children (into [fexpr] argexprs)} - (let [arg-syms (take argc (repeatedly gensym))] - (analyze env - `(let [~@(vec (interleave arg-syms args))] - (~(vary-meta f assoc ::analyzed true) ~@arg-syms))))))))) + (let [enve (assoc env :context :expr) + fexpr (analyze enve f) + argc (count args) + ^boolean fn-var? (-> fexpr :info :fn-var)] + (when fn-var? + (let [{:keys [^boolean variadic max-fixed-arity method-params name]} (:info fexpr)] + (when (and (not (valid-arity? argc method-params)) + (or (not variadic) + (and variadic (< argc max-fixed-arity)))) + (warning :fn-arity env {:name name :argc argc})))) + (let [deprecated? (-> fexpr :info :deprecated) + no-warn? (-> form meta :deprecation-nowarn)] + (when (and (boolean deprecated?) + (not (boolean no-warn?))) + (warning :fn-deprecated env {:fexpr fexpr}))) + (when-not (nil? (-> fexpr :info :type)) + (warning :invoke-ctor env {:fexpr fexpr})) + (let [ana-expr #(analyze enve %) + argexprs (map ana-expr args)] + (if (or (not (boolean *cljs-static-fns*)) + (not (symbol? f)) + fn-var? + (analyzed? f) + (all-values? argexprs)) + {:env env :op :invoke :form form :f fexpr :args (vec argexprs) + :children (into [fexpr] argexprs)} + (let [arg-syms (take argc (repeatedly gensym))] + (analyze env + `(let [~@(vec (interleave arg-syms args))] + (~(vary-meta f assoc ::analyzed true) ~@arg-syms)))))))) + +(defn parse-invoke + [env form] + (disallowing-recur (parse-invoke* env form))) (defn analyze-symbol "Finds the var associated with sym" From 7b2cdb7d6894fa01b58779e40da729eaf2721cf7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jul 2015 00:15:46 -0400 Subject: [PATCH 1241/4033] gets - unrolled get-in, see timings as low as 2.2s in Safari & 4.5s in Chrome to analyze cljs.core --- src/main/clojure/cljs/analyzer.cljc | 65 +++++++++++++++++++---------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 8f65e4e73..f224363ab 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -103,6 +103,29 @@ "volatile" "while" "with" "yield" "methods" "null"}) +#?(:clj (def SENTINEL (Object.)) + :cljs (def SENTINEL (js-obj))) + +(defn gets + ([m k0 k1] + (let [m (get m k0 SENTINEL)] + (when-not (identical? m SENTINEL) + (get m k1)))) + ([m k0 k1 k2] + (let [m (get m k0 SENTINEL)] + (when-not (identical? m SENTINEL) + (let [m (get m k1 SENTINEL)] + (when-not (identical? m SENTINEL) + (get m k2)))))) + ([m k0 k1 k2 k3] + (let [m (get m k0 SENTINEL)] + (when-not (identical? m SENTINEL) + (let [m (get m k1 SENTINEL)] + (when-not (identical? m SENTINEL) + (let [m (get m k2 SENTINEL)] + (when-not (identical? m SENTINEL) + (get m k3))))))))) + #?(:cljs (def CLJ_NIL_SYM 'clj-nil)) @@ -524,11 +547,11 @@ (first (string/split sufstr #"\.")) suffix))] (when (and (not (implicit-import? env prefix suffix)) - (not (and (not (get-in @env/*compiler* [::namespaces prefix])) + (not (and (not (gets @env/*compiler* ::namespaces prefix)) (or (get (:requires (:ns env)) prefix) (get (:imports (:ns env)) prefix)))) (not (and (= prefix 'cljs.core) (= suffix 'unquote))) - (not (get-in @env/*compiler* [::namespaces prefix :defs suffix]))) + (not (gets @env/*compiler* ::namespaces prefix :defs suffix))) (missing-fn env prefix suffix))))) (defn confirm-var-exists-throw [] @@ -552,7 +575,7 @@ (when (and (nil? (get '#{cljs.core goog Math goog.string} ns-sym)) (nil? (get (-> env :ns :requires) ns-sym)) ;; something else may have loaded the namespace, i.e. load-file - (nil? (get-in @env/*compiler* [::namespaces ns-sym])) + (nil? (gets @env/*compiler* ::namespaces ns-sym)) ;; macros may refer to namespaces never explicitly required ;; confirm that the library at least exists #?(:clj (nil? (util/ns->source ns-sym)))) @@ -564,7 +587,7 @@ "Is sym visible from core in the current compilation namespace?" #?(:cljs {:tag boolean}) [env sym] - (and (or (get-in @env/*compiler* [::namespaces 'cljs.core :defs sym]) + (and (or (gets @env/*compiler* ::namespaces 'cljs.core :defs sym) (when-let [mac (get-expander sym env)] (let [^Namespace ns (-> mac meta :ns)] (= (.getName ns) #?(:clj 'cljs.core :cljs 'cljs.core$macros))))) @@ -590,7 +613,7 @@ (when (not= (-> env :ns :name) full-ns) (confirm-ns env full-ns)) (confirm env full-ns (symbol (name sym)))) - (merge (get-in @env/*compiler* [::namespaces full-ns :defs (symbol (name sym))]) + (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym))) {:name (symbol (str full-ns) (str (name sym))) :ns full-ns})) @@ -605,35 +628,35 @@ (if lb {:name (symbol (str (:name lb)) suffix)} (let [cur-ns (-> env :ns :name)] - (if-let [full-ns (get-in @env/*compiler* [::namespaces cur-ns :imports prefix])] + (if-let [full-ns (gets @env/*compiler* ::namespaces cur-ns :imports prefix)] {:name (symbol (str full-ns) suffix)} - (if-let [info (get-in @env/*compiler* [::namespaces cur-ns :defs prefix])] + (if-let [info (gets @env/*compiler* ::namespaces cur-ns :defs prefix)] (merge info {:name (symbol (str cur-ns) (str sym)) :ns cur-ns}) - (merge (get-in @env/*compiler* [::namespaces prefix :defs (symbol suffix)]) + (merge (gets @env/*compiler* ::namespaces prefix :defs (symbol suffix)) {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix)) :ns prefix})))))) - (get-in @env/*compiler* [::namespaces (-> env :ns :name) :uses sym]) - (let [full-ns (get-in @env/*compiler* [::namespaces (-> env :ns :name) :uses sym])] + (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym) + (let [full-ns (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)] (merge - (get-in @env/*compiler* [::namespaces full-ns :defs sym]) + (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) :ns (-> env :ns :name)})) - (get-in @env/*compiler* [::namespaces (-> env :ns :name) :imports sym]) - (recur env (get-in @env/*compiler* [::namespaces (-> env :ns :name) :imports sym]) confirm) + (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) + (recur env (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) confirm) :else (let [cur-ns (-> env :ns :name) full-ns (cond - (get-in @env/*compiler* [::namespaces cur-ns :defs sym]) cur-ns + (gets @env/*compiler* ::namespaces cur-ns :defs sym) cur-ns (core-name? env sym) 'cljs.core :else cur-ns)] (when confirm (confirm env full-ns sym)) - (merge (get-in @env/*compiler* [::namespaces full-ns :defs sym]) + (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) :ns full-ns}))))))) @@ -2044,16 +2067,16 @@ (defn excluded? #?(:cljs {:tag boolean}) [env sym] - (if-not (nil? (get-in env [:ns :excludes sym])) + (if-not (nil? (gets env :ns :excludes sym)) true - (not (nil? (get-in @env/*compiler* [::namespaces (get-in env [:ns :name]) :excludes sym]))))) + (not (nil? (gets @env/*compiler* ::namespaces (gets env :ns :name) :excludes sym))))) (defn used? #?(:cljs {:tag boolean}) [env sym] - (if-not (nil? (get-in env [:ns :use-macros sym])) + (if-not (nil? (gets env :ns :use-macros sym)) true - (not (nil? (get-in @env/*compiler* [::namespaces (get-in env [:ns :name]) :use-macros sym]))))) + (not (nil? (gets @env/*compiler* ::namespaces (gets env :ns :name) :use-macros sym))))) (defn get-expander-ns [env ^String nstr] (cond @@ -2069,14 +2092,14 @@ (some-> env :ns :require-macros (get (symbol nstr)) find-ns))) (defn get-expander* [sym env] - (when-not (or (not (nil? (get-in env [:locals sym]))) ; locals hide macros + (when-not (or (not (nil? (gets env :locals sym))) ; locals hide macros (and (excluded? env sym) (not (used? env sym)))) (let [nstr (namespace sym)] (if-not (nil? nstr) (let [ns (get-expander-ns env nstr)] (when-not (nil? ns) (.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym))))) - (let [nsym (get-in env [:ns :use-macros sym])] + (let [nsym (gets env :ns :use-macros sym)] (if-not (nil? nsym) (.findInternedVar ^clojure.lang.Namespace #?(:clj (find-ns nsym) :cljs (find-macros-ns nsym)) sym) From e74d76b8121d2e1e3515c0eb58ab5b3dbaca0f93 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Jul 2015 01:28:46 -0400 Subject: [PATCH 1242/4033] optimize EmptyList -equiv --- src/main/cljs/cljs/core.cljs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9692024f2..e9810a403 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2709,6 +2709,11 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) +(defn ^boolean list? + "Returns true if x implements IList" + [x] + (satisfies? IList x)) + (es6-iterable List) (deftype EmptyList [meta] @@ -2748,7 +2753,11 @@ reduces them without incurring seq initialization" ISequential IEquiv - (-equiv [coll other] (equiv-sequential coll other)) + (-equiv [coll other] + (if (or (list? other) + (sequential? other)) + (nil? (seq other)) + false)) IHash (-hash [coll] empty-ordered-hash) @@ -2859,11 +2868,6 @@ reduces them without incurring seq initialization" (Cons. nil x coll nil) (Cons. nil x (seq coll) nil))) -(defn ^boolean list? - "Returns true if x implements IList" - [x] - (satisfies? IList x)) - (defn hash-keyword [k] (int (+ (hash-symbol k) 0x9e3779b9))) From 7154276bd6afd1c8e8308ead9eb845cc525f55c0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Jul 2015 02:14:27 -0400 Subject: [PATCH 1243/4033] minor tweaks to inference, use JS string ops instead of seq ops --- src/main/clojure/cljs/analyzer.cljc | 33 ++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f224363ab..39ec0ef3e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -750,9 +750,13 @@ {:op :constant :env env :form sym :tag 'cljs.core/Keyword}) (defn get-tag [e] - (or (-> e :tag) - (-> e :info :tag) - (-> e :form meta :tag))) + (let [tag (-> e :tag)] + (if-not (nil? tag) + tag + (let [tag (-> e :info :tag)] + (if-not (nil? tag) + tag + (-> e :form meta :tag)))))) (defn find-matching-method [f params] ;; if local fn, need to look in :info @@ -825,7 +829,9 @@ ret-tag (when-not (nil? (:fn-var info)) (:ret-tag info))] (if-not (nil? ret-tag) ret-tag - (let [ret-tag (infer-tag env (assoc (find-matching-method f (:args e)) :op :method))] + (let [args (:args e) + me (assoc (find-matching-method f args) :op :method) + ret-tag (infer-tag env me)] (if-not (nil? ret-tag) ret-tag ANY_SYM))))) @@ -2138,12 +2144,16 @@ (if (symbol? op) (let [opname (str op)] (cond - (identical? (first opname) \.) + (identical? \. + #?(:clj (first opname) + :cljs (.charAt opname 0))) (let [[target & args] (next form)] (with-meta (list* #?(:clj '. :cljs DOT_SYM) target (symbol (subs opname 1)) args) (meta form))) - (identical? (last opname) \.) + (identical? \. + #?(:clj (last opname) + :cljs (.charAt opname (dec (. opname -length))))) (with-meta (list* #?(:clj 'new :cljs NEW_SYM) (symbol (subs opname 0 (dec (count opname)))) (next form)) (meta form)) @@ -2245,10 +2255,13 @@ expr))) (defn infer-type [env ast] - (if-let [tag (and (not (:tag ast)) - (infer-tag env ast))] - (assoc ast :tag tag) - ast)) + (let [tag (:tag ast)] + (if (nil? tag) + (let [tag (infer-tag env ast)] + (if-not (nil? tag) + (assoc ast :tag tag) + ast)) + ast))) (def ^:dynamic *passes* nil) From d1d3e8664406260e07b1549e803cb34b818ea983 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Jul 2015 02:24:47 -0400 Subject: [PATCH 1244/4033] drop pre-condition for now, included slow map? call --- src/main/clojure/cljs/analyzer.cljc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 39ec0ef3e..d0d2805a2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2327,12 +2327,10 @@ ([env form] (analyze env form nil)) ([env form name] (analyze env form name nil)) ([env form name opts] - {:pre [(or (nil? name) (symbol? name)) - (or (nil? opts) (map? opts))]} - (ensure - (wrapping-errors env - (binding [reader/*alias-map* (or reader/*alias-map* {})] - (analyze* env form name opts)))))) + (ensure + (wrapping-errors env + (binding [reader/*alias-map* (or reader/*alias-map* {})] + (analyze* env form name opts)))))) #?(:clj (defn- source-path From c5bed37256f2f4d944f4380b455e34fff7187d86 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jul 2015 12:50:42 -0400 Subject: [PATCH 1245/4033] relax restrictions so we can bootstrap under :simple optimizations --- src/main/cljs/cljs/core.cljs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e9810a403..7aafac139 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9926,16 +9926,11 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." :else (recur (gobject/get ctxt (first xs)) (next xs)))) (defn find-ns-obj [ns] - (if-not js/COMPILED - (let [segs (-> ns str (.split "."))] - (when-not (nil? (gobject/get (. goog/dependencies_ -nameToPath) (str ns))) - (case *target* - "nodejs" (find-ns-obj* js/global segs) - "default" (find-ns-obj* js/window segs) - (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) - (throw - (js/Error. - "find-ns-obj not supported when Closure optimization applied")))) + (let [segs (-> ns str (.split "."))] + (case *target* + "nodejs" (find-ns-obj* js/global segs) + "default" (find-ns-obj* js/window segs) + (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) (defn ns-interns* [sym] (let [ns-obj (find-ns-obj sym) From 02553eee4ac1f9e68eb43959a9e05fa2dcd92366 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jul 2015 14:05:00 -0400 Subject: [PATCH 1246/4033] under :simple optimizations the only way to find a namespace is to use our old friend eval --- src/main/cljs/cljs/core.cljs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7aafac139..ea4775da2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9926,11 +9926,14 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." :else (recur (gobject/get ctxt (first xs)) (next xs)))) (defn find-ns-obj [ns] - (let [segs (-> ns str (.split "."))] - (case *target* - "nodejs" (find-ns-obj* js/global segs) - "default" (find-ns-obj* js/window segs) - (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) + (let [munged-ns (munge (str ns)) + segs (.split munged-ns ".")] + (if ^boolean js/COMPILED + (js/eval munged-ns) + (case *target* + "nodejs" (find-ns-obj* js/global segs) + "default" (find-ns-obj* js/window segs) + (throw (js/Error. (str "find-ns-obj not supported for target " *target*))))))) (defn ns-interns* [sym] (let [ns-obj (find-ns-obj sym) From 36bb7cc7cfaa34625ea986310a3fdc813aaed3e9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jul 2015 17:38:04 -0400 Subject: [PATCH 1247/4033] change cljs.closure/compile-file to accept :output-to key along with :output-file for consistency with the rest of the compilation pipeline --- src/main/clojure/cljs/closure.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 06bd6cd33..226abf08f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -410,9 +410,10 @@ compiled JavaScript will written to this location and the function returns a JavaScriptFile. In either case the return value satisfies IJavaScript." - [^File file {:keys [output-file] :as opts}] + [^File file {:keys [output-file output-to] :as opts}] (if output-file - (let [out-file (io/file (util/output-directory opts) output-file)] + (let [out-file (io/file (util/output-directory opts) + (or output-file output-to))] (compiled-file (comp/compile-file file out-file opts))) (let [path (.getPath ^File file)] (binding [ana/*cljs-file* path] From f62466d81c3b6657d1f91fa9fb0bc922bf2ee5ef Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jul 2015 18:48:02 -0400 Subject: [PATCH 1248/4033] test implements ISeq before native IIndexed, huge perf hit to go through native-satisfies? first --- src/main/cljs/cljs/core.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ea4775da2..5e1f8f17c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1612,12 +1612,12 @@ reduces them without incurring seq initialization" (when (< n (.-length coll)) (aget coll n)) + (implements? ISeq coll) + (linear-traversal-nth coll n) + (native-satisfies? IIndexed coll) (-nth coll n) - (satisfies? ISeq coll) - (linear-traversal-nth coll n) - :else (throw (js/Error. (str "nth not supported on this type " (type->str (type coll))))))) @@ -1642,12 +1642,12 @@ reduces them without incurring seq initialization" (aget coll n) not-found) + (implements? ISeq coll) + (linear-traversal-nth coll n not-found) + (native-satisfies? IIndexed coll) (-nth coll n) - (satisfies? ISeq coll) - (linear-traversal-nth coll n not-found) - :else (throw (js/Error. (str "nth not supported on this type " (type->str (type coll)))))))) From 146a844f3f6bf6c5ff53be9d99ef51b2f47b85aa Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jul 2015 19:33:55 -0400 Subject: [PATCH 1249/4033] tweak count, tweak nth --- src/main/cljs/cljs/core.cljs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5e1f8f17c..2f0084251 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1562,10 +1562,10 @@ reduces them without incurring seq initialization" (string? coll) (alength coll) - (native-satisfies? ICounted coll) - (-count coll) + (implements? ISeqable coll) + (accumulating-seq-count coll) - :else (accumulating-seq-count coll)) + :else (-count coll)) 0)) (defn- linear-traversal-nth @@ -1610,7 +1610,7 @@ reduces them without incurring seq initialization" (string? coll) (when (< n (.-length coll)) - (aget coll n)) + (.charAt coll n)) (implements? ISeq coll) (linear-traversal-nth coll n) @@ -1639,7 +1639,7 @@ reduces them without incurring seq initialization" (string? coll) (if (< n (.-length coll)) - (aget coll n) + (.charAt coll n) not-found) (implements? ISeq coll) @@ -2144,7 +2144,7 @@ reduces them without incurring seq initialization" (string? coll) (array-reduce coll f) - + (native-satisfies? IReduce coll) (-reduce coll f) @@ -2160,7 +2160,7 @@ reduces them without incurring seq initialization" (string? coll) (array-reduce coll f val) - + (native-satisfies? IReduce coll) (-reduce coll f val) From ffdfd3a5ccc38d3f36cab9c1c81cc637b0cee42c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 15:13:31 -0400 Subject: [PATCH 1250/4033] fix benchmark script --- script/benchmark | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/benchmark b/script/benchmark index 1ba9d863b..29f0a61de 100755 --- a/script/benchmark +++ b/script/benchmark @@ -3,7 +3,7 @@ rm -rf builds/out-adv-bench mkdir -p builds/out-adv-bench -bin/cljsc benchmark "{:optimizations :advanced :output-wrapper true :compiler-stats true}" > builds/out-adv-bench/core-advanced-benchmark.js +bin/cljsc benchmark "{:optimizations :advanced :output-wrapper true :compiler-stats true :verbose true :output-dir \"builds/out-adv-bench\"}" > builds/out-adv-bench/core-advanced-benchmark.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 benchmarks" From 09ec00411f9ca59d4fc9b807873f792ccd25d503 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 15:25:46 -0400 Subject: [PATCH 1251/4033] optimize function case of with-meta --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 2f0084251..fdb93ab5e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1795,7 +1795,7 @@ reduces them without incurring seq initialization" "Returns an object of the same type and value as obj, with map m as its metadata." [o meta] - (if (and (fn? o) (not (satisfies? IWithMeta o))) + (if ^boolean (goog/isFunction o) (MetaFn. o meta) (when-not (nil? o) (-with-meta o meta)))) From afc77c52783bb18b1ccd336f4ec723dfed8e7afb Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 15:53:21 -0400 Subject: [PATCH 1252/4033] optimize analyze-let & macroexpand-1 --- src/main/clojure/cljs/analyzer.cljc | 151 +++++++++++++++++----------- 1 file changed, 94 insertions(+), 57 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d0d2805a2..4ba8048c4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1289,63 +1289,94 @@ :statements statements :ret ret :children (conj (vec statements) ret)})) +(defn analyze-let-binding-init [env init loop-lets] + (binding [*loop-lets* loop-lets] + (analyze env init))) + +(defn get-let-tag [name init-expr] + (let [tag (-> name meta :tag)] + (if-not (nil? tag) + tag + (let [tag (-> init-expr :tag)] + (if-not (nil? tag) + tag + (-> init-expr :info :tag)))))) + +(defn analyze-let-bindings* [encl-env bindings] + (loop [bes [] + env (assoc encl-env :context :expr) + bindings (seq (partition 2 bindings))] + (let [binding (first bindings)] + (if-not (nil? binding) + (let [[name init] binding] + (when (or (not (nil? (namespace name))) + #?(:clj (.contains (str name) ".") + :cljs ^boolean (goog.string/contains (str name) "."))) + (throw (error encl-env (str "Invalid local name: " name)))) + (let [init-expr (analyze-let-binding-init env init (cons {:params bes} *loop-lets*)) + line (get-line name env) + col (get-col name env) + be {:name name + :line line + :column col + :init init-expr + :tag (get-let-tag name init-expr) + :local true + :shadow (-> env :locals name) + ;; Give let* bindings same shape as var so + ;; they get routed correctly in the compiler + :op :var + :env {:line line :column col} + :info {:name name + :shadow (-> env :locals name)} + :binding-form? true} + be (if (= :fn (:op init-expr)) + ;; TODO: can we simplify - David + (merge be + {:fn-var true + :variadic (:variadic init-expr) + :max-fixed-arity (:max-fixed-arity init-expr) + :method-params (map :params (:methods init-expr))}) + be)] + (recur (conj bes be) + (assoc-in env [:locals name] be) + (next bindings)))) + [bes env])))) + +(defn analyze-let-bindings [encl-env bindings] + (disallowing-recur (analyze-let-bindings* encl-env bindings))) + +(defn analyze-let-body* [env context exprs] + (analyze (assoc env :context (if (= :expr context) :return context)) `(do ~@exprs))) + +(defn analyze-let-body [env context exprs recur-frames loop-lets] + (binding [*recur-frames* recur-frames + *loop-lets* loop-lets] + (analyze-let-body* env context exprs))) + (defn analyze-let [encl-env [_ bindings & exprs :as form] is-loop] (when-not (and (vector? bindings) (even? (count bindings))) (throw (error encl-env "bindings must be vector of even number of elements"))) - (let [context (:context encl-env) - [bes env] - (disallowing-recur - (loop [bes [] - env (assoc encl-env :context :expr) - bindings (seq (partition 2 bindings))] - (if-let [[name init] (first bindings)] - (do - (when (or (namespace name) - #?(:clj (.contains (str name) ".") - :cljs (goog.string/contains (str name) "."))) - (throw (error encl-env (str "Invalid local name: " name)))) - (let [init-expr (binding [*loop-lets* (cons {:params bes} *loop-lets*)] - (analyze env init)) - be {:name name - :line (get-line name env) - :column (get-col name env) - :init init-expr - :tag (or (-> name meta :tag) - (-> init-expr :tag) - (-> init-expr :info :tag)) - :local true - :shadow (-> env :locals name) - ;; Give let* bindings same shape as var so - ;; they get routed correctly in the compiler - :op :var - :env {:line (get-line name env) - :column (get-col name env)} - :info {:name name - :shadow (-> env :locals name)} - :binding-form? true} - be (if (= (:op init-expr) :fn) - ;; TODO: can we simplify - David - (merge be - {:fn-var true - :variadic (:variadic init-expr) - :max-fixed-arity (:max-fixed-arity init-expr) - :method-params (map :params (:methods init-expr))}) - be)] - (recur (conj bes be) - (assoc-in env [:locals name] be) - (next bindings)))) - [bes env]))) - recur-frame (when is-loop {:params bes :flag (atom nil)}) - expr - (binding [*recur-frames* (if recur-frame (cons recur-frame *recur-frames*) *recur-frames*) - *loop-lets* (cond - is-loop *loop-lets* - *loop-lets* (cons {:params bes} *loop-lets*))] - (analyze (assoc env :context (if (= :expr context) :return context)) `(do ~@exprs)))] - {:env encl-env :op (if is-loop :loop :let) - :bindings bes :expr expr :form form - :children (conj (vec (map :init bes)) expr)})) + (let [context (:context encl-env) + [bes env] (analyze-let-bindings encl-env bindings) + recur-frame (when (true? is-loop) + {:params bes :flag (atom nil)}) + recur-frames (if recur-frame + (cons recur-frame *recur-frames*) + *recur-frames*) + loop-lets (cond + (true? is-loop) *loop-lets* + (not (nil? *loop-lets*)) (cons {:params bes} *loop-lets*)) + expr (analyze-let-body env context exprs recur-frames loop-lets) + op (if (true? is-loop) :loop :let) + children (conj (vec (map :init bes)) expr)] + {:op op + :env encl-env + :bindings bes + :expr expr + :form form + :children children})) (defmethod parse 'let* [op encl-env form _ _] @@ -2135,10 +2166,16 @@ (if #?(:clj (seq? form') :cljs (cljs-seq? form')) (let [sym' (first form') sym (first form)] - (if #?(:clj (= sym' 'js*) :cljs (symbol-identical? sym' JS_STAR_SYM)) - (vary-meta form' merge - (cond-> {:js-op (if (namespace sym) sym (symbol "cljs.core" (str sym)))} - (-> mac-var meta ::numeric) (assoc :numeric true))) + (if #?(:clj (= sym' 'js*) + :cljs (symbol-identical? sym' JS_STAR_SYM)) + (let [sym (if (namespace sym) + sym + (symbol "cljs.core" (str sym))) + js-op {:js-op sym} + js-op (if (true? (-> mac-var meta ::numeric)) + (assoc js-op :numeric true) + js-op)] + (vary-meta form' merge js-op)) form')) form'))) (if (symbol? op) From faf076ddc0c5de96ba27124d1e8e7e054c9f7245 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 16:05:26 -0400 Subject: [PATCH 1253/4033] remove truth call in infer-tag --- src/main/clojure/cljs/analyzer.cljc | 45 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 4ba8048c4..1fcb355db 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -840,28 +840,29 @@ "Given env, an analysis environment, and e, an AST node, return the inferred type of the node" [env e] - (if-let [tag (get-tag e)] - tag - (case (:op e) - :recur IGNORE_SYM - :throw IGNORE_SYM - :let (infer-tag env (:expr e)) - :loop (infer-tag env (:expr e)) - :do (infer-tag env (:ret e)) - :method (infer-tag env (:expr e)) - :def (infer-tag env (:init e)) - :invoke (infer-invoke env e) - :if (infer-if env e) - :constant (case (:form e) - true BOOLEAN_SYM - false BOOLEAN_SYM - ANY_SYM) - :var (if (:init e) - (infer-tag env (:init e)) - (infer-tag env (:info e))) - :dot ANY_SYM - :js ANY_SYM - nil))) + (let [tag (get-tag e)] + (if-not (nil? tag) + tag + (case (:op e) + :recur IGNORE_SYM + :throw IGNORE_SYM + :let (infer-tag env (:expr e)) + :loop (infer-tag env (:expr e)) + :do (infer-tag env (:ret e)) + :method (infer-tag env (:expr e)) + :def (infer-tag env (:init e)) + :invoke (infer-invoke env e) + :if (infer-if env e) + :constant (case (:form e) + true BOOLEAN_SYM + false BOOLEAN_SYM + ANY_SYM) + :var (if (:init e) + (infer-tag env (:init e)) + (infer-tag env (:info e))) + :dot ANY_SYM + :js ANY_SYM + nil)))) (defmulti parse (fn [op & rest] op)) From 0f2c836fb6ac13c26bd7037ddc6a456b01581ec8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 16:36:49 -0400 Subject: [PATCH 1254/4033] optimize fn* --- src/main/clojure/cljs/analyzer.cljc | 156 +++++++++++++++++----------- 1 file changed, 93 insertions(+), 63 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1fcb355db..90ef26c1c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1169,71 +1169,101 @@ (declare analyze-wrap-meta) +(defn fn-name-var [env locals name] + (when-not (nil? name) + (let [ns (-> env :ns :name) + shadow (get locals name) + shadow (when (nil? shadow) + (get-in env [:js-globals name])) + fn-scope (:fn-scope env) + name-var {:name name + :info {:fn-self-name true + :fn-scope fn-scope + :ns ns + :shadow shadow}} + tag (-> name meta :tag) + ret-tag (when-not (nil? tag) + {:ret-tag tag})] + (merge name-var ret-tag)))) + +(defn analyze-fn-methods-pass2* [menv locals type meths] + (doall (map #(analyze-fn-method menv locals % type) meths))) + +(defn analyze-fn-methods-pass2 [menv locals type meths] + (no-warn (analyze-fn-methods-pass2* menv locals type meths))) + (defmethod parse 'fn* [op env [_ & args :as form] name _] - (analyze-wrap-meta - (let [[name meths] (if (symbol? (first args)) - [(first args) (next args)] - [name (seq args)]) - ;;turn (fn [] ...) into (fn ([]...)) - meths (if (vector? (first meths)) (list meths) meths) - locals (:locals env) - name-var (if name - (merge - {:name name - :info {:fn-self-name true - :fn-scope (:fn-scope env) - :ns (-> env :ns :name) - :shadow (or (locals name) - (get-in env [:js-globals name]))}} - (when-let [tag (-> name meta :tag)] - {:ret-tag tag}))) - env (if name - (update-in env [:fn-scope] conj name-var) - env) - locals (if (and locals name) (assoc locals name name-var) locals) - type (-> form meta ::type) - protocol-impl (-> form meta ::protocol-impl) - protocol-inline (-> form meta ::protocol-inline) - menv (if (> (count meths) 1) (assoc env :context :expr) env) - menv (merge menv - {:protocol-impl protocol-impl - :protocol-inline protocol-inline}) - methods (map #(analyze-fn-method menv locals % type) meths) - max-fixed-arity (apply max (map :max-fixed-arity methods)) - variadic (boolean (some :variadic methods)) - locals (if name - (update-in locals [name] assoc - ;; TODO: can we simplify? - David - :fn-var true - :variadic variadic - :max-fixed-arity max-fixed-arity - :method-params (map :params methods)) - locals) - methods (if name - ;; a second pass with knowledge of our function-ness/arity - ;; lets us optimize self calls - (no-warn (doall (map #(analyze-fn-method menv locals % type) meths))) - methods) - form (vary-meta form dissoc ::protocol-impl ::protocol-inline ::type)] - (let [variadic-methods (filter :variadic methods) - variadic-params (count (:params (first variadic-methods))) - param-counts (map (comp count :params) methods)] - (when (< 1 (count variadic-methods)) - (warning :multiple-variadic-overloads env {:name name-var})) - (when (not (or (zero? variadic-params) (= variadic-params (+ 1 max-fixed-arity)))) - (warning :variadic-max-arity env {:name name-var})) - (when (not= (distinct param-counts) param-counts) - (warning :overload-arity env {:name name-var}))) - {:env env - :op :fn :form form :name name-var :methods methods :variadic variadic - :tag 'function - :recur-frames *recur-frames* :loop-lets *loop-lets* - :jsdoc [(when variadic "@param {...*} var_args")] - :max-fixed-arity max-fixed-arity - :protocol-impl protocol-impl - :protocol-inline protocol-inline - :children (mapv :expr methods)}))) + (let [[name meths] (if (symbol? (first args)) + [(first args) (next args)] + [name (seq args)]) + ;; turn (fn [] ...) into (fn ([]...)) + meths (if (vector? (first meths)) + (list meths) + meths) + locals (:locals env) + name-var (fn-name-var env locals name) + env (if-not (nil? name) + (update-in env [:fn-scope] conj name-var) + env) + locals (if (and (not (nil? locals)) + (not (nil? name))) + (assoc locals name name-var) + locals) + form-meta (meta form) + type (::type form-meta) + proto-impl (::protocol-impl form-meta) + proto-inline (::protocol-inline form-meta) + menv (if (> (count meths) 1) + (assoc env :context :expr) + env) + menv (merge menv + {:protocol-impl proto-impl + :protocol-inline proto-inline}) + methods (map #(analyze-fn-method menv locals % type) meths) + mfa (apply max (map :max-fixed-arity methods)) + variadic (boolean (some :variadic methods)) + locals (if-not (nil? name) + (update-in locals [name] assoc + ;; TODO: can we simplify? - David + :fn-var true + :variadic variadic + :max-fixed-arity mfa + :method-params (map :params methods)) + locals) + methods (if-not (nil? name) + ;; a second pass with knowledge of our function-ness/arity + ;; lets us optimize self calls + (analyze-fn-methods-pass2 menv locals type meths) + methods) + form (vary-meta form dissoc ::protocol-impl ::protocol-inline ::type) + js-doc (when (true? variadic) + "@param {...*} var_args") + children (mapv :expr methods) + ast {:op :fn + :env env + :form form + :name name-var + :methods methods + :variadic variadic + :tag 'function + :recur-frames *recur-frames* + :loop-lets *loop-lets* + :jsdoc [js-doc] + :max-fixed-arity mfa + :protocol-impl proto-impl + :protocol-inline proto-inline + :children children}] + (let [variadic-methods (filter :variadic methods) + variadic-params (count (:params (first variadic-methods))) + param-counts (map (comp count :params) methods)] + (when (< 1 (count variadic-methods)) + (warning :multiple-variadic-overloads env {:name name-var})) + (when (not (or (zero? variadic-params) (== variadic-params (+ 1 mfa)))) + (warning :variadic-max-arity env {:name name-var})) + (when (not= (distinct param-counts) param-counts) + (warning :overload-arity env {:name name-var}))) + (analyze-wrap-meta ast))) (defmethod parse 'letfn* [op env [_ bindings & exprs :as form] name _] From 8bb3b1ddc28bb773dcd3acd74f6e35c50015246b Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 17:03:40 -0400 Subject: [PATCH 1255/4033] optimize confirm-var-exists --- src/main/clojure/cljs/analyzer.cljc | 42 +++++++++++++++++++---------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 90ef26c1c..edad683c4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -534,24 +534,38 @@ [env prefix suffix] (contains? '#{goog goog.object goog.string goog.array Math} prefix)) +(defn confirm-var-exist-warning [env prefix suffix] + (fn [env prefix suffix] + (warning :undeclared-var env {:prefix prefix :suffix suffix}))) + +(defn loaded-ns? + #?(:cljs {:tag boolean}) + [env prefix] + (if-not (nil? (gets @env/*compiler* ::namespaces prefix)) + true + (let [ns (:ns env)] + (if-not (nil? (get (:requires ns) prefix)) + true + (if-not (nil? (get (:imports ns) prefix)) + true + false))))) + (defn confirm-var-exists ([env prefix suffix] - (confirm-var-exists env prefix suffix - (fn [env prefix suffix] - (warning :undeclared-var env {:prefix prefix :suffix suffix})))) + (let [warn (confirm-var-exist-warning env prefix suffix)] + (confirm-var-exists env prefix suffix warn))) ([env prefix suffix missing-fn] - (let [sufstr (str suffix) - suffix (symbol - ;; leave cljs.core$macros/.. alone - (if (and (not= ".." sufstr) (re-find #"\." sufstr)) - (first (string/split sufstr #"\.")) - suffix))] + (let [sufstr (str suffix) + suffix-str (if (and (not (identical? ".." sufstr)) ;; leave cljs.core$macros/.. alone + #?(:clj (re-find #"\." sufstr) + :cljs ^boolean (.test #"\." sufstr))) + (first (string/split sufstr #"\.")) + suffix) + suffix (symbol suffix-str)] (when (and (not (implicit-import? env prefix suffix)) - (not (and (not (gets @env/*compiler* ::namespaces prefix)) - (or (get (:requires (:ns env)) prefix) - (get (:imports (:ns env)) prefix)))) - (not (and (= prefix 'cljs.core) (= suffix 'unquote))) - (not (gets @env/*compiler* ::namespaces prefix :defs suffix))) + (not (loaded-ns? env prefix)) + (not (and (= 'cljs.core prefix) (= 'unquote suffix))) + (nil? (gets @env/*compiler* ::namespaces prefix :defs suffix))) (missing-fn env prefix suffix))))) (defn confirm-var-exists-throw [] From d93ea5a10a391af25343311f652686f334c719c3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 17:38:06 -0400 Subject: [PATCH 1256/4033] optimize analyze-symbol --- src/main/clojure/cljs/analyzer.cljc | 32 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index edad683c4..60ce80021 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2129,22 +2129,28 @@ (defn analyze-symbol "Finds the var associated with sym" [env sym] - (if (:quoted? env) + (if ^boolean (:quoted? env) {:op :constant :env env :form sym :tag 'cljs.core/Symbol} (let [{:keys [line column]} (meta sym) - env (cond-> env - line (assoc :line line) - column (assoc :column column)) - ret {:env env :form sym} - lb (-> env :locals sym)] - (if lb + env (if-not (nil? line) + (assoc env :line line) + env) + env (if-not (nil? column) + (assoc env :column column) + env) + ret {:env env :form sym} + lcls (:locals env) + lb (get lcls sym)] + (if-not (nil? lb) (assoc ret :op :var :info lb) - (if-not (:def-var env) - (assoc ret :op :var :info - (if-not (contains? (meta sym) ::analyzed) - (resolve-existing-var env sym) - (resolve-var env sym))) - (assoc ret :op :var :info (resolve-var env sym))))))) + (if-not (true? (:def-var env)) + (let [sym-meta (meta sym) + info (if-not (contains? sym-meta ::analyzed) + (resolve-existing-var env sym) + (resolve-var env sym))] + (assoc ret :op :var :info info)) + (let [info (resolve-var env sym)] + (assoc ret :op :var :info info))))))) (defn excluded? #?(:cljs {:tag boolean}) From 68b1f39636969874125317ff739fadaf950ccd3d Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 17:43:06 -0400 Subject: [PATCH 1257/4033] remove truth call from infer-tag --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 60ce80021..bdf5a102b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -871,7 +871,7 @@ true BOOLEAN_SYM false BOOLEAN_SYM ANY_SYM) - :var (if (:init e) + :var (if-not (nil? (:init e)) (infer-tag env (:init e)) (infer-tag env (:info e))) :dot ANY_SYM From 4024fdeae212e00e771295b778e822fda0cb66d5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 18:11:58 -0400 Subject: [PATCH 1258/4033] optimize resolve-var --- src/main/clojure/cljs/analyzer.cljc | 73 ++++++++++++++++------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index bdf5a102b..6ba0249aa 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -612,63 +612,70 @@ warnings about unresolved vars." ([env sym] (resolve-var env sym nil)) ([env sym confirm] - (if (= (namespace sym) "js") + (if #?(:clj (= "js" (namespace sym)) + :cljs (identical? "js" (namespace sym))) {:name sym :ns 'js} - (let [s (str sym) - lb (-> env :locals sym)] + (let [s (str sym) + lcls (:locals env) + lb (get lcls sym)] (cond - lb lb - - (namespace sym) - (let [ns (namespace sym) - ns (if (= "clojure.core" ns) "cljs.core" ns) + (not (nil? lb)) lb + + (not (nil? (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)] - (when confirm + (when-not (nil? confirm) (when (not= (-> env :ns :name) full-ns) (confirm-ns env full-ns)) (confirm env full-ns (symbol (name sym)))) (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym))) - {:name (symbol (str full-ns) (str (name sym))) - :ns full-ns})) + {:name (symbol (str full-ns) (str (name sym))) + :ns full-ns})) #?(:clj (and (.contains s ".") (not (.contains s ".."))) - :cljs (and (goog.string/contains s ".") - (not (goog.string/contains s "..")))) - (let [idx (.indexOf s ".") + :cljs (and ^boolean (goog.string/contains s ".") + (not ^boolean (goog.string/contains s "..")))) + (let [idx (.indexOf s ".") prefix (symbol (subs s 0 idx)) suffix (subs s (inc idx)) - lb (-> env :locals prefix)] - (if lb + lb (get lcls prefix)] + (if-not (nil? lb) {:name (symbol (str (:name lb)) suffix)} - (let [cur-ns (-> env :ns :name)] - (if-let [full-ns (gets @env/*compiler* ::namespaces cur-ns :imports prefix)] + (let [cur-ns (-> env :ns :name) + full-ns (gets @env/*compiler* ::namespaces cur-ns :imports prefix)] + (if-not (nil? full-ns) {:name (symbol (str full-ns) suffix)} - (if-let [info (gets @env/*compiler* ::namespaces cur-ns :defs prefix)] - (merge info - {:name (symbol (str cur-ns) (str sym)) - :ns cur-ns}) - (merge (gets @env/*compiler* ::namespaces prefix :defs (symbol suffix)) - {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix)) - :ns prefix})))))) - - (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym) + (let [info (gets @env/*compiler* ::namespaces cur-ns :defs prefix)] + (if-not (nil? info) + (merge info + {:name (symbol (str cur-ns) (str sym)) + :ns cur-ns}) + (merge (gets @env/*compiler* ::namespaces prefix :defs (symbol suffix)) + {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix)) + :ns prefix}))))))) + + (not (nil? (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym))) (let [full-ns (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)] (merge - (gets @env/*compiler* ::namespaces full-ns :defs sym) - {:name (symbol (str full-ns) (str sym)) - :ns (-> env :ns :name)})) + (gets @env/*compiler* ::namespaces full-ns :defs sym) + {:name (symbol (str full-ns) (str sym)) + :ns (-> env :ns :name)})) - (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) + (not (nil? (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym))) (recur env (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) confirm) :else (let [cur-ns (-> env :ns :name) full-ns (cond - (gets @env/*compiler* ::namespaces cur-ns :defs sym) cur-ns + (not (nil? (gets @env/*compiler* ::namespaces cur-ns :defs sym))) cur-ns (core-name? env sym) 'cljs.core :else cur-ns)] - (when confirm + (when-not (nil? confirm) (confirm env full-ns sym)) (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) From e9868a95484fe017c06d49c3d54e78164ffaefda Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 18:33:16 -0400 Subject: [PATCH 1259/4033] optimize analyze-dot --- src/main/clojure/cljs/analyzer.cljc | 45 ++++++++++++++++++----------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6ba0249aa..9d4b6a783 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2002,25 +2002,36 @@ (list* '. dot-form) " with classification " (classify-dot-form dot-form)))))) +(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) + tag (:tag form-meta)] + (case dot-action + ::access (let [children [targetexpr]] + {:op :dot + :env env + :form form + :target targetexpr + :field field + :children children + :tag tag}) + ::call (let [argexprs (map #(analyze enve %) args) + children (into [targetexpr] argexprs)] + {:op :dot + :env env + :form form + :target targetexpr + :method method + :args argexprs + :children children + :tag tag})))) + (defmethod parse '. [_ env [_ target & [field & member+] :as form] _ _] - (disallowing-recur - (let [{:keys [dot-action target method field args]} (build-dot-form [target field member+]) - enve (assoc env :context :expr) - targetexpr (analyze enve target)] - (case dot-action - ::access {:env env :op :dot :form form - :target targetexpr - :field field - :children [targetexpr] - :tag (-> form meta :tag)} - ::call (let [argexprs (map #(analyze enve %) args)] - {:env env :op :dot :form form - :target targetexpr - :method method - :args argexprs - :children (into [targetexpr] argexprs) - :tag (-> form meta :tag)}))))) + (disallowing-recur (analyze-dot env target field member+ form))) (defmethod parse 'js* [op env [_ jsform & args :as form] _ _] From a2739c07becebe0897c546f73ea2ebe4356a18aa Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 18:56:38 -0400 Subject: [PATCH 1260/4033] optimize js* --- src/main/clojure/cljs/analyzer.cljc | 145 +++++++++++++++++----------- 1 file changed, 91 insertions(+), 54 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9d4b6a783..c0b97ac1e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2033,64 +2033,101 @@ [_ env [_ target & [field & member+] :as form] _ _] (disallowing-recur (analyze-dot env target field member+ form))) +(defn get-js-tag [form] + (let [form-meta (meta form) + tag (:tag form-meta)] + (if-not (nil? tag) + tag + (when (true? (:numeric form-meta)) + 'number)))) + +(defn js-star-interp + [env ^String s] + (let [idx (.indexOf s "~{")] + (if (== -1 idx) + (list s) + (let [end (.indexOf s "}" idx) + inner (:name (resolve-existing-var env (symbol (subs s (+ 2 idx) end))))] + (lazy-seq + (cons (subs s 0 idx) + (cons inner + (js-star-interp env (subs s (inc end)))))))))) + +(defn js-star-seg + [^String s] + (let [idx (.indexOf s "~{")] + (if (== -1 idx) + (list s) + (let [end (.indexOf s "}" idx)] + (lazy-seq + (cons (subs s 0 idx) + (js-star-seg (subs s (inc end))))))))) + +(def NUMERIC_SET '#{any number long double}) + +(defn numeric-type? + #?(:cljs {:tag boolean}) + [t] + ;; TODO: type inference is not strong enough to detect that + ;; when functions like first won't return nil, so variadic + ;; numeric functions like cljs.core/< would produce a spurious + ;; warning without this - David + (if (nil? t) + true + (if (and (symbol? t) (not (nil? (get NUMERIC_SET t)))) + true + (when #?(:clj (set? t) + :cljs (cljs-set? t)) + (or (contains? t 'number) + (contains? t 'long) + (contains? t 'double) + (contains? t 'any)))))) + +(defn analyze-js-star* [env jsform args form] + (let [enve (assoc env :context :expr) + argexprs (vec (map #(analyze enve %) args)) + form-meta (meta form) + segs (js-star-seg jsform) + tag (get-js-tag form) + js-op (:js-op form-meta) + numeric (:numeric form-meta)] + (when (true? numeric) + (let [types (map #(infer-tag env %) argexprs)] + (when-not (every? numeric-type? types) + (warning :invalid-arithmetic env + {:js-op js-op + :types (into [] types)})))) + {:op :js + :env env + :segs segs + :args argexprs + :tag tag + :form form + :children argexprs + :js-op js-op + :numeric numeric})) + +(defn analyze-js-star [env jsform args form] + (disallowing-recur (analyze-js-star* env jsform args form))) + (defmethod parse 'js* [op env [_ jsform & args :as form] _ _] (when-not (string? jsform) (throw (error env "Invalid js* form"))) - (if args - (disallowing-recur - (let [seg (fn seg [^String s] - (let [idx (.indexOf s "~{")] - (if (= -1 idx) - (list s) - (let [end (.indexOf s "}" idx)] - (lazy-seq - (cons (subs s 0 idx) - (seg (subs s (inc end))))))))) - enve (assoc env :context :expr) - argexprs (vec (map #(analyze enve %) args))] - (when (-> form meta :numeric) - (let [types (map #(infer-tag env %) argexprs)] - (when-not (every? - (fn [t] - (or (nil? t) - (and (symbol? t) ('#{any number long double} t)) - ;; TODO: type inference is not strong enough to detect that - ;; when functions like first won't return nil, so variadic - ;; numeric functions like cljs.core/< would produce a spurious - ;; warning without this - David - (and (set? t) - (or (contains? t 'number) - (contains? t 'long) - (contains? t 'double) - (contains? t 'any))))) - types) - (warning :invalid-arithmetic env - {:js-op (-> form meta :js-op) - :types (into [] types)})))) - {:env env :op :js :segs (seg jsform) :args argexprs - :tag (or (-> form meta :tag) - (and (-> form meta :numeric) 'number) - nil) - :form form :children argexprs - :js-op (-> form meta :js-op) - :numeric (-> form meta :numeric)})) - (let [interp (fn interp [^String s] - (let [idx (.indexOf s "~{")] - (if (= -1 idx) - (list s) - (let [end (.indexOf s "}" idx) - inner (:name (resolve-existing-var env (symbol (subs s (+ 2 idx) end))))] - (lazy-seq - (cons (subs s 0 idx) - (cons inner - (interp (subs s (inc end))))))))))] - {:env env :op :js :form form :code (apply str (interp jsform)) - :tag (or (-> form meta :tag) - (and (-> form meta :numeric) 'number) - nil) - :js-op (-> form meta :js-op) - :numeric (-> form meta :numeric)}))) + (if-not (nil? args) + (analyze-js-star env jsform args form) + (let [code (apply str (js-star-interp env jsform)) + tag (get-js-tag form) + form-meta (meta form) + js-op (:js-op form-meta) + numeric (:numeric form-meta)] + {:op :js + :env env + :form form + :code code + :tag tag + :js-op js-op + :numeric numeric}))) (defn- analyzed? #?(:cljs {:tag boolean}) From 10db834f4ab557f8b8ae5621114563b61b15ec2a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 19:14:43 -0400 Subject: [PATCH 1261/4033] optimize analyze-fn-method --- src/main/clojure/cljs/analyzer.cljc | 81 +++++++++++++++++++---------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c0b97ac1e..d8bec11e0 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1158,35 +1158,60 @@ (when export-as {:export export-as}) (when init-expr {:children [init-expr]}))))) +(defn analyze-fn-method-param [env] + (fn [[locals params] name] + (let [line (get-line name env) + column (get-col name env) + nmeta (meta name) + tag (:tag nmeta) + shadow (when-not (nil? locals) + (locals name)) + env (merge (select-keys env [:context]) + {:line line :column column}) + param {:op :var + :name name + :line line + :column column + :tag tag + :shadow shadow + ;; Give the fn params the same shape + ;; as a :var, so it gets routed + ;; correctly in the compiler + :env env + :info {:name name :shadow shadow} + :binding-form? true}] + [(assoc locals name param) (conj params param)]))) + +(defn analyze-fn-method-body [env form recur-frames] + (binding [*recur-frames* recur-frames] + (analyze env form))) + (defn- analyze-fn-method [env locals form type] - (let [param-names (first form) - variadic (boolean (some '#{&} param-names)) - param-names (vec (remove '#{&} param-names)) - body (next form) - [locals params] (reduce (fn [[locals params] name] - (let [param {:name name - :line (get-line name env) - :column (get-col name env) - :tag (-> name meta :tag) - :shadow (when locals (locals name)) - ;; Give the fn params the same shape - ;; as a :var, so it gets routed - ;; correctly in the compiler - :op :var - :env (merge (select-keys env [:context]) - {:line (get-line name env) - :column (get-col name env)}) - :info {:name name - :shadow (when locals (locals name))} - :binding-form? true}] - [(assoc locals name param) (conj params param)])) - [locals []] param-names) - fixed-arity (count (if variadic (butlast params) params)) - recur-frame {:params params :flag (atom nil)} - expr (binding [*recur-frames* (cons recur-frame *recur-frames*)] - (analyze (assoc env :context :return :locals locals) `(do ~@body)))] - {:env env :variadic variadic :params params :max-fixed-arity fixed-arity - :type type :form form :recurs @(:flag recur-frame) :expr expr})) + (let [param-names (first form) + variadic (boolean (some '#{&} param-names)) + param-names (vec (remove '#{&} param-names)) + body (next form) + step (analyze-fn-method-param env) + step-init [locals []] + [locals params] (reduce step step-init param-names) + params' (if (true? variadic) + (butlast params) + params) + fixed-arity (count params') + recur-frame {:params params :flag (atom nil)} + recur-frames (cons recur-frame *recur-frames*) + body-env (assoc env :context :return :locals locals) + body-form `(do ~@body) + expr (analyze-fn-method-body body-env body-form recur-frames) + recurs @(:flag recur-frame)] + {:env env + :variadic variadic + :params params + :max-fixed-arity fixed-arity + :type type + :form form + :expr expr + :recurs recurs})) (declare analyze-wrap-meta) From 030d9455f9977a5b64ef46458ae7f1060281a2a3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 20:34:11 -0400 Subject: [PATCH 1262/4033] optimize do --- src/main/clojure/cljs/analyzer.cljc | 34 ++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d8bec11e0..4f049ad41 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1355,16 +1355,34 @@ {:env env :op :letfn :bindings bes :expr expr :form form :children (conj (vec (map :init bes)) expr)})) +(defn analyze-do-statements* [env exprs] + (seq (map #(analyze (assoc env :context :statement) %) (butlast exprs)))) + +(defn analyze-do-statements [env exprs] + (disallowing-recur (analyze-do-statements* env exprs))) + (defmethod parse 'do [op env [_ & exprs :as form] _ _] - (let [statements (disallowing-recur - (seq (map #(analyze (assoc env :context :statement) %) (butlast exprs)))) - ret (if (<= (count exprs) 1) - (analyze env (first exprs)) - (analyze (assoc env :context (if (= :statement (:context env)) :statement :return)) (last exprs)))] - {:env env :op :do :form form - :statements statements :ret ret - :children (conj (vec statements) ret)})) + (let [statements (analyze-do-statements env exprs)] + (if (<= (count exprs) 1) + (let [ret (analyze env (first exprs)) + children (conj (vec statements) ret)] + {:op :do + :env env + :form form + :statements statements :ret ret + :children children}) + (let [ret-env (if (= :statement (:context env)) + (assoc env :context :statement) + (assoc env :context :return)) + ret (analyze ret-env (last exprs)) + children (conj (vec statements) ret)] + {:op :do + :env env + :form form + :statements statements + :ret ret + :children children})))) (defn analyze-let-binding-init [env init loop-lets] (binding [*loop-lets* loop-lets] From fa31556f45ade1153c374b53a0126180c0e8908a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Jul 2015 20:58:00 -0400 Subject: [PATCH 1263/4033] optimize munge --- src/main/clojure/cljs/compiler.cljc | 42 ++++++++++++++++++----------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 0d815a4a3..71df0da77 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -67,33 +67,43 @@ (munge (str (string/replace (str ns) "." "$") "$" scoped-name))))) +(defn munge-reserved [reserved] + (fn [s] + (if-not (nil? (get reserved s)) + (str s "$") + s))) + (defn munge ([s] (munge s js-reserved)) ([s reserved] - (if (map? s) - (let [{:keys [name field info] :as name-var} s] - (if (:fn-self-name info) + (if #?(:clj (map? s) + :cljs (ana/cljs-map? s)) + (let [name-var s + name (:name name-var) + field (:field name-var) + info (:info name-var)] + (if-not (nil? (:fn-self-name info)) (fn-self-name s) ;; Unshadowing - (let [depth (shadow-depth s) - renamed (*lexical-renames* - #?(:clj (System/identityHashCode s) - :cljs (hash s))) - munged-name (munge - (cond field (str "self__." name) - renamed renamed - :else name) - reserved)] - (if (or field (zero? depth)) + (let [depth (shadow-depth s) + code #?(:clj (System/identityHashCode s) + :cljs (hash s)) + renamed (get *lexical-renames* code) + name (cond + (true? field) (str "self__." name) + (not (nil? renamed)) renamed + :else name) + munged-name (munge name reserved)] + (if (or (true? field) (zero? depth)) munged-name (symbol (str munged-name "__$" depth)))))) ;; String munging (let [ss (string/replace (str s) ".." "_DOT__DOT_") ss (string/replace ss #?(:clj #"\/(.)" :cljs (js/RegExp. "\\/(.)")) ".$1") ; Division is special - ss (string/join "." - (map #(if (reserved %) (str % "$") %) - (string/split ss #"\."))) + rf (munge-reserved reserved) + ss (map rf (string/split ss #"\.")) + ss (string/join "." ss) ms #?(:clj (clojure.lang.Compiler/munge ss) :cljs (cljs.core/munge ss))] (if (symbol? s) From 8600c7cb88414ec91faf5cb22e3c4ee3be649b0d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 12 Jul 2015 16:04:43 -0400 Subject: [PATCH 1264/4033] CLJS-1338: NPE in confirm-var-exists if suffix is ".." --- src/main/clojure/cljs/analyzer.cljc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 4f049ad41..afaf9835e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -556,9 +556,10 @@ (confirm-var-exists env prefix suffix warn))) ([env prefix suffix missing-fn] (let [sufstr (str suffix) - suffix-str (if (and (not (identical? ".." sufstr)) ;; leave cljs.core$macros/.. alone - #?(:clj (re-find #"\." sufstr) - :cljs ^boolean (.test #"\." sufstr))) + suffix-str (if (and #?(:clj (not= ".." sufstr) + :cljs (not (identical? ".." sufstr))) ;; leave cljs.core$macros/.. alone + #?(:clj (re-find #"\." sufstr) + :cljs ^boolean (.test #"\." sufstr))) (first (string/split sufstr #"\.")) suffix) suffix (symbol suffix-str)] From 5390b9335d58bb40269d0832ac30ee66475bffa3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 13 Jul 2015 06:48:18 -0400 Subject: [PATCH 1265/4033] sketch out cljs.js namespace --- src/main/cljs/cljs/js.cljs | 62 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/cljs/cljs/js.cljs diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs new file mode 100644 index 000000000..85aa9427d --- /dev/null +++ b/src/main/cljs/cljs/js.cljs @@ -0,0 +1,62 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns cljs.js + (:require-macros [cljs.env :as env]) + (:require [cljs.env :as env] + [cljs.analyzer :as ana] + [cljs.compiler :as comp] + [cljs.tools.reader :as r] + [cljs.tools.reader-types :as rt] + [cljs.tagged-literals :as tags])) + +(defn empty-env [] + (env/default-compiler-env)) + +;; ----------------------------------------------------------------------------- +;; Analyze + +(defn analyze* [env string cb] + (let [rdr (rt/string-push-back-reader f) + eof (js-obj) + env (ana/empty-env)] + (env/with-compiler-env env + (loop [] + (let [form (r/read {:eof eof} rdr)] + (if-not (identical? eof form) + (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*))] + (ana/analyze env form)) + (recur)) + (cb)))))) + +(defn analyze [env string cb] + (binding [ana/*cljs-ns* (or (:ns env) 'cljs.user) + *ns* (create-ns ana/*cljs-ns*) + r/*data-readers* tags/*cljs-data-readers*] + (analyze* env string cb))) + +;; ----------------------------------------------------------------------------- +;; Emit + +(defn emit* [env ast cb] + (cb (with-out-str (comp/emit ast)))) + +(defn emit [env ast cb] + (env/with-compiler-env env + (emit* env ast cb))) + +;; ----------------------------------------------------------------------------- +;; Eval + +(defn eval* [env form cb] + (let [ana-env (ana/empty-env)] + (cb (ana/analyze ana-env form)))) + +(defn eval [env form cb] + (env/with-compiler-env env + (eval* env form cb))) \ No newline at end of file From 6920b62be188809fcab97a593415cf0a72a39baa Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 12 Jul 2015 19:56:22 -0400 Subject: [PATCH 1266/4033] CLJS-1331: Regex literal emits invalid JS Regex emission in bootstrapped ClojureScript is simple and straightforward. --- src/main/clojure/cljs/compiler.cljc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 71df0da77..bf2595d6c 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -230,10 +230,10 @@ (if (= "" (str x)) (emits "(new RegExp(\"\"))") (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" (str x))] - (emits \/ - #?(:clj (.replaceAll (re-matcher #"/" pattern) "\\\\/") - :cljs (string/replace pattern #"/" "\\\\/")) - \/ flags)))) + #?(:clj (emits \/ + (.replaceAll (re-matcher #"/" pattern) "\\\\/") + \/ flags) + :cljs (emits pattern))))) (defn emits-keyword [kw] (let [ns (namespace kw) From 3c7b39f65497ef6d6ca680fd082dbd7dc8a394be Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 13 Jul 2015 07:09:18 -0400 Subject: [PATCH 1267/4033] string -> source --- src/main/cljs/cljs/js.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 85aa9427d..de80ac951 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -21,8 +21,8 @@ ;; ----------------------------------------------------------------------------- ;; Analyze -(defn analyze* [env string cb] - (let [rdr (rt/string-push-back-reader f) +(defn analyze* [env source cb] + (let [rdr (rt/string-push-back-reader source) eof (js-obj) env (ana/empty-env)] (env/with-compiler-env env @@ -34,11 +34,11 @@ (recur)) (cb)))))) -(defn analyze [env string cb] +(defn analyze [env source cb] (binding [ana/*cljs-ns* (or (:ns env) 'cljs.user) *ns* (create-ns ana/*cljs-ns*) r/*data-readers* tags/*cljs-data-readers*] - (analyze* env string cb))) + (analyze* env source cb))) ;; ----------------------------------------------------------------------------- ;; Emit From a660d8f882e6d9ec6578034336eaa87d1b8c478b Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Sun, 17 May 2015 23:42:32 +0200 Subject: [PATCH 1268/4033] CLJS-428: Added step to escape docstrings with */ and associated test --- src/main/clojure/cljs/compiler.cljc | 3 ++- src/test/clojure/cljs/compiler_tests.clj | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index bf2595d6c..1788da376 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -477,7 +477,8 @@ docs (remove nil? docs)] (letfn [(print-comment-lines [e] (doseq [next-line (string/split-lines e)] - (emitln " * " (string/trim next-line))))] + (emitln " * " (-> (string/trim next-line) + (string/replace "*/" "* /")))))] (when (seq docs) (emitln "/**") (doseq [e docs] diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 70aeeb684..21db4ec21 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -69,6 +69,16 @@ (env/with-compiler-env cenv (ana/resolve-var {:ns {:name 'cljs.core}} '..)))))) +(deftest test-cljs-428 + (letfn [(check-docs [docs] + (is (= 1 (count (re-seq #"\*/" docs)))))] + (check-docs (with-out-str + (comp/emit-comment "/* multiline comments */" nil))) + (check-docs (with-out-str + (comp/emit + (ana/analyze aenv + '(defn foo "foo is */ like this /*/" [] (+ 1 1)))))))) + (comment (env/with-compiler-env cenv (comp/emit From 2fbcefa23eead389d24ce198271ceac7d4a13436 Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Mon, 22 Jun 2015 22:55:43 +0200 Subject: [PATCH 1269/4033] CLJS-1282: Add a :pprint option to the default reporter in cljs.test --- src/main/cljs/cljs/test.cljs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index b63b11d12..4181b3ab9 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -243,18 +243,28 @@ cljs.test (:require-macros [clojure.template :as temp] [cljs.test :as test]) - (:require [clojure.string :as string])) + (:require [clojure.string :as string] + [cljs.pprint :as pprint])) ;; ============================================================================= ;; Default Reporting (defn empty-env + "Generates a testing environment with a reporter. + (empty-env) - uses the :cljs.test/default reporter. + (empty-env :cljs.test/pprint) - pretty prints all data structures. + (empty-env reporter) - uses a reporter of your choosing. + + To create your own reporter see cljs.test/report" ([] (empty-env ::default)) ([reporter] - {:report-counters {:test 0 :pass 0 :fail 0 :error 0} - :testing-vars () - :testing-contexts () - :reporter reporter})) + (cond-> {:report-counters {:test 0 :pass 0 :fail 0 :error 0} + :testing-vars () + :testing-contexts () + :formatter pr-str + :reporter reporter} + (= ::pprint reporter) (assoc :reporter ::default + :formatter pprint/pprint)))) (def ^:dynamic *current-env* nil) @@ -312,14 +322,18 @@ (defmethod report [::default :pass] [m] (inc-report-counter! :pass)) +(defn- print-comparison [m] + (let [formatter-fn (or (:formatter (get-current-env)) pr-str)] + (println "expected:" (formatter-fn (:expected m))) + (println " actual:" (formatter-fn (:actual m))))) + (defmethod report [::default :fail] [m] (inc-report-counter! :fail) (println "\nFAIL in" (testing-vars-str m)) (when (seq (:testing-contexts (get-current-env))) (println (testing-contexts-str))) (when-let [message (:message m)] (println message)) - (println "expected:" (pr-str (:expected m))) - (println " actual:" (pr-str (:actual m)))) + (print-comparison m)) (defmethod report [::default :error] [m] (inc-report-counter! :error) @@ -327,8 +341,7 @@ (when (seq (:testing-contexts (get-current-env))) (println (testing-contexts-str))) (when-let [message (:message m)] (println message)) - (println "expected:" (pr-str (:expected m))) - (print " actual: ") (prn (:actual m))) + (print-comparison m)) (defmethod report [::default :summary] [m] (println "\nRan" (:test m) "tests containing" @@ -345,6 +358,9 @@ (defmethod report [::default :end-test-var] [m]) (defmethod report [::default :end-run-tests] [m]) +;; ============================================================================= +;; File, Line, and Column Helpers + (defn js-line-and-column [stack-element] (let [parts (.split stack-element ":") cnt (count parts)] From dd6ee009d7e24e4176f18d659bbb19c12914dd64 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Jul 2015 06:44:02 -0400 Subject: [PATCH 1270/4033] CLJS-1309: remove redundant declare --- src/main/clojure/cljs/analyzer.cljc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index afaf9835e..83bc2723d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1024,8 +1024,6 @@ :catch catch :children [try catch finally]})) -(declare get-expander) - (defmethod parse 'def [op env form name _] (let [pfn (fn From 8d53b008cc60c622f6b4280b38dd96fbd1517ace Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Jul 2015 06:46:26 -0400 Subject: [PATCH 1271/4033] CLJS-1310: ns libspec error message misses :import --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 83bc2723d..bd027289b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1862,7 +1862,7 @@ (reduce (fn [m [k & libs]] (when-not (#{:use :use-macros :require :require-macros :import} k) - (throw (error env "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported"))) + (throw (error env "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported"))) (when-not (@valid-forms k) (throw (error env (str "Only one " k " form is allowed per namespace definition")))) (swap! valid-forms disj k) From 3e7b3b3f6c55e6f79182beb83f16e79d6e1d6eea Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Jul 2015 06:52:58 -0400 Subject: [PATCH 1272/4033] add cljs test and source paths to project.clj --- project.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project.clj b/project.clj index 3385c1e8d..11d476c9c 100644 --- a/project.clj +++ b/project.clj @@ -5,9 +5,9 @@ :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :jvm-opts ^:replace ["-Xmx512m" "-server"] - :source-paths ["src/main/clojure"] + :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] - :test-paths ["src/test/clojure"] + :test-paths ["src/test/clojure" "src/test/cljs"] :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.9.2"] From bf89165ddc8078aa416d507d20aeae8820d5ca71 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Jul 2015 06:57:06 -0400 Subject: [PATCH 1273/4033] CLJS-1276: var equality differs from Clojure --- src/main/cljs/cljs/core.cljs | 5 +++++ src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fdb93ab5e..545792044 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -952,6 +952,11 @@ (-deref [_] (val)) IMeta (-meta [_] _meta) + IEquiv + (-equiv [this other] + (if (instance? Var other) + (= (.-sym this) (.-sym other)) + false)) Fn IFn (-invoke [_] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b88988219..94031babe 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2857,6 +2857,10 @@ (is (= (namespace x) "js")) (is (= (name x) "Array")))) +(deftest test-1276 + (is (= #'first #'first)) + (is (not= #'first #'last))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From ed0a3a6539fec818e64369ec3c5af037031b349c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Jul 2015 07:20:39 -0400 Subject: [PATCH 1274/4033] CLJS-1248: alter-meta! does not work on vars --- src/main/cljs/cljs/core.cljs | 3 +++ src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 545792044..2aabcd200 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -952,6 +952,9 @@ (-deref [_] (val)) IMeta (-meta [_] _meta) + IWithMeta + (-with-meta [_ new-meta] + (Var. val sym new-meta)) IEquiv (-equiv [this other] (if (instance? Var other) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 94031babe..2ea275230 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2861,6 +2861,10 @@ (is (= #'first #'first)) (is (not= #'first #'last))) +(deftest test-1248 + (let [v (vary-meta #'first assoc :foo 'bar)] + (is (= (-> v meta :foo) 'bar)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 31d4dc528cfa8b523925ac01e815dda65d63842c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Jul 2015 07:38:14 -0400 Subject: [PATCH 1275/4033] CLJS-1210: Javascript built-in arguments replaces nil arguments locally defined by let Add arguments to the list of JS reserved words to avoid accidental capture. This is a real problem as we often create closures to preserve expression semantics. --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index bd027289b..d8dc54a1c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -89,7 +89,7 @@ :unsupported-js-module-type true}) (def js-reserved - #{"abstract" "boolean" "break" "byte" "case" + #{"arguments" "abstract" "boolean" "break" "byte" "case" "catch" "char" "class" "const" "continue" "debugger" "default" "delete" "do" "double" "else" "enum" "export" "extends" "final" diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 2ea275230..ec53ea010 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2865,6 +2865,13 @@ (let [v (vary-meta #'first assoc :foo 'bar)] (is (= (-> v meta :foo) 'bar)))) +(deftest test-1210 + (is (= ((fn [] + (let [{:keys [arguments]} {} + arguments (or arguments [])] + arguments))) + []))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From be0a8a801234e779640fdeaf255b329159df9040 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 12 Jul 2015 15:06:39 -0400 Subject: [PATCH 1276/4033] CLJS-1333: Analyze meta on quoted symbols Analyzes reader-attached meta on quoted symbols. --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d8dc54a1c..086153366 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2227,7 +2227,7 @@ "Finds the var associated with sym" [env sym] (if ^boolean (:quoted? env) - {:op :constant :env env :form sym :tag 'cljs.core/Symbol} + (analyze-wrap-meta {:op :constant :env env :form sym :tag 'cljs.core/Symbol}) (let [{:keys [line column]} (meta sym) env (if-not (nil? line) (assoc env :line line) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index ec53ea010..d8149093a 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1997,7 +1997,10 @@ (is (nil? (get-in {:foo {:bar 2}} [:foo :bar :baz])))) (deftest test-symbol-meta - (is (= (meta (with-meta 'foo {:tag 'int})) {:tag 'int}))) + (is (= (meta (with-meta 'foo {:tag 'int})) {:tag 'int})) + (is (= (meta (quote ^{:bar true} foo)) {:bar true})) + (is (= (meta (quote ^:bar foo)) {:bar true})) + (is (= (meta (first '[^:bar x])) {:bar true}))) (deftest test-467 (is (= (reduce-kv + 0 (apply hash-map (range 1000))) From 542dabec1184ea6353aa965b9644e0b12436f865 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Jul 2015 17:23:19 -0400 Subject: [PATCH 1277/4033] optimize clojure.string --- src/main/cljs/clojure/string.cljs | 68 +++++++++++++++++-------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index ff6c11a0e..200cd4228 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -31,11 +31,14 @@ string / string pattern / (string or function of match)." [s match replacement] - (cond (string? match) - (.replace s (js/RegExp. (gstring/regExpEscape match) "g") replacement) - (instance? js/RegExp match) - (.replace s (js/RegExp. (.-source match) "g") replacement) - :else (throw (str "Invalid match arg: " match)))) + (cond + (string? match) + (.replace s (js/RegExp. (gstring/regExpEscape match) "g") replacement) + + (instance? js/RegExp match) + (.replace s (js/RegExp. (.-source match) "g") replacement) + + :else (throw (str "Invalid match arg: " match)))) (defn replace-first "Replaces the first instance of match with replacement in s. @@ -51,12 +54,12 @@ separated by an optional separator." ([coll] (loop [sb (StringBuffer.) coll (seq coll)] - (if coll + (if-not (nil? coll) (recur (. sb (append (str (first coll)))) (next coll)) (.toString sb)))) ([separator coll] (loop [sb (StringBuffer.) coll (seq coll)] - (if coll + (if-not (nil? coll) (do (. sb (append (str (first coll)))) (let [coll (next coll)] @@ -96,13 +99,13 @@ (defn- pop-last-while-empty [v] (loop [v v] - (if (= "" (peek v)) + (if (identical? "" (peek v)) (recur (pop v)) v))) (defn- discard-trailing-if-needed [limit v] - (if (= 0 limit) + (if (== 0 limit) (pop-last-while-empty v) v)) @@ -110,7 +113,7 @@ [s limit] (if (or (<= limit 0) (>= limit (+ 2 (count s)))) (conj (vec (cons "" (map str (seq s)))) "") - (condp = limit + (condp == limit 1 (vector s) 2 (vector "" s) (let [c (- limit 2)] @@ -123,21 +126,22 @@ (split s re 0)) ([s re limit] (discard-trailing-if-needed limit - (if (= (str re) "/(?:)/") + (if (identical? "/(?:)/" (str re)) (split-with-empty-regex s limit) (if (< limit 1) (vec (.split (str s) re)) (loop [s s limit limit parts []] - (if (= limit 1) + (if (== 1 limit) (conj parts s) - (if-let [m (re-find re s)] - (let [index (.indexOf s m)] - (recur (.substring s (+ index (count m))) - (dec limit) - (conj parts (.substring s 0 index)))) - (conj parts s))))))))) + (let [m (re-find re s)] + (if-not (nil? m) + (let [index (.indexOf s m)] + (recur (.substring s (+ index (count m))) + (dec limit) + (conj parts (.substring s 0 index)))) + (conj parts s)))))))))) (defn split-lines "Splits s on \n or \r\n." @@ -145,19 +149,19 @@ (split s #"\n|\r\n")) (defn trim - "Removes whitespace from both ends of string." - [s] - (gstring/trim s)) + "Removes whitespace from both ends of string." + [s] + (gstring/trim s)) (defn triml - "Removes whitespace from the left side of string." - [s] - (gstring/trimLeft s)) + "Removes whitespace from the left side of string." + [s] + (gstring/trimLeft s)) (defn trimr - "Removes whitespace from the right side of string." - [s] - (gstring/trimRight s)) + "Removes whitespace from the right side of string." + [s] + (gstring/trimRight s)) (defn trim-newline "Removes all trailing newline \\n or return \\r characters from @@ -167,7 +171,8 @@ (if (zero? index) "" (let [ch (get s (dec index))] - (if (or (= ch \newline) (= ch \return)) + (if (or (identical? \newline ch) + (identical? \return ch)) (recur (dec index)) (.substring s 0 index)))))) @@ -186,10 +191,11 @@ (let [buffer (StringBuffer.) length (.-length s)] (loop [index 0] - (if (= length index) + (if (== length index) (. buffer (toString)) - (let [ch (.charAt s index)] - (if-let [replacement (get cmap ch)] + (let [ch (.charAt s index) + replacement (get cmap ch)] + (if-not (nil? replacement) (.append buffer (str replacement)) (.append buffer ch)) (recur (inc index))))))) From 654561ae192c9eb92bf7d6f499f1b9e050c8d1d1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Jul 2015 18:01:39 -0400 Subject: [PATCH 1278/4033] optimize lexical renames, now around ~1.5X of the JVM under JavaScriptCore, under 3X for V8 --- src/main/clojure/cljs/compiler.cljc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1788da376..ef630ace5 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -87,7 +87,7 @@ ;; Unshadowing (let [depth (shadow-depth s) code #?(:clj (System/identityHashCode s) - :cljs (hash s)) + :cljs (-hash ^not-native name)) renamed (get *lexical-renames* code) name (cond (true? field) (str "self__." name) @@ -766,9 +766,12 @@ (binding [*lexical-renames* (into *lexical-renames* (when (= :statement context) - (map #(vector #?(:clj (System/identityHashCode %) - :cljs (hash %)) - (gensym (str (:name %) "-"))) + (map + (fn [binding] + (let [name (:name binding)] + (vector #?(:clj (System/identityHashCode binding) + :cljs (-hash ^not-native name)) + (gensym (str name "-"))))) bindings)))] (doseq [{:keys [init] :as binding} bindings] (emits "var ") From a16da9acbc8595490f13eef4b606459049587fe8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Jul 2015 18:13:17 -0400 Subject: [PATCH 1279/4033] optimize emits --- src/main/clojure/cljs/compiler.cljc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index ef630ace5..cc5ee0fe9 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -169,11 +169,11 @@ (doseq [x xs] (cond (nil? x) nil - (map? x) (emit x) - (seq? x) (apply emits x) - (fn? x) (x) + #?(:clj (map? x) :cljs (ana/cljs-map? x)) (emit x) + #?(:clj (seq? x) :cljs (ana/cljs-seq? x)) (apply emits x) + #?(:clj (fn? x) :cljs (goog/isFunction x)) (x) :else (let [s (print-str x)] - (when *source-map-data* + (when-not (nil? *source-map-data*) (swap! *source-map-data* update-in [:gen-col] #(+ % (count s)))) (print s)))) From 84e2b7b3fe5a6dbbe04f6f374f46df61ec3670e4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Jul 2015 18:13:56 -0400 Subject: [PATCH 1280/4033] missing type hint --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index cc5ee0fe9..0025d37f4 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -171,7 +171,7 @@ (nil? x) nil #?(:clj (map? x) :cljs (ana/cljs-map? x)) (emit x) #?(:clj (seq? x) :cljs (ana/cljs-seq? x)) (apply emits x) - #?(:clj (fn? x) :cljs (goog/isFunction x)) (x) + #?(:clj (fn? x) :cljs ^boolean (goog/isFunction x)) (x) :else (let [s (print-str x)] (when-not (nil? *source-map-data*) (swap! *source-map-data* From da2e53800fb3aa4a7bf937843035e8ff1dfbbaf5 Mon Sep 17 00:00:00 2001 From: asheldo Date: Wed, 15 Jul 2015 07:44:49 -0400 Subject: [PATCH 1281/4033] fix when-first ns modifier in defmacro for --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 08ac25814..2943bf32e 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2136,7 +2136,7 @@ `(fn ~giter [~gxs] (lazy-seq (loop [~gxs ~gxs] - (core/when-first [~bind ~gxs] + (when-first [~bind ~gxs] ~(do-mod mod-pairs))))) #_"inner-most loop" (core/let [gi (gensym "i__") From 074445f5c8eccd5a4ff6d075b9d4b3d96bc367ee Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 17:19:20 -0400 Subject: [PATCH 1282/4033] CLJS-1247: Split out error printing from regular printing remove *out*, remove *err*. introduce *print-err-fn*, set-print-err-fn!. Update enable-console-print! & enable-util-print!. Change analyzer warnings to use *print-err-fn* when bootstrapped. --- src/main/cljs/cljs/core.cljs | 26 +++++++++++++++++--------- src/main/cljs/cljs/nodejs.cljs | 6 +++++- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 2aabcd200..378274f69 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -31,14 +31,6 @@ :doc "Var bound to the current namespace. Only used for bootstrapping."} *ns* nil) -(def - ^{:dynamic true} - *out* nil) - -(def - ^{:dynamic true} - *err* nil) - (def ^{:dynamic true} *assert* true) @@ -51,10 +43,22 @@ (fn [_] (throw (js/Error. "No *print-fn* fn set for evaluation environment")))) +(defonce + ^{:doc "Each runtime environment provides a different way to print error output. + Whatever function *print-fn* is bound to will be passed any + Strings which should be printed." :dynamic true} + *print-err-fn* + (fn [_] + (throw (js/Error. "No *print-err-fn* fn set for evaluation environment")))) + (defn set-print-fn! "Set *print-fn* to f." [f] (set! *print-fn* f)) +(defn set-print-err-fn! + "Set *print-err-fn* to f." + [f] (set! *print-err-fn* f)) + (def ^{:dynamic true :doc "When set to true, output will be flushed whenever a newline is printed. @@ -130,7 +134,11 @@ (set! *print-newline* false) (set! *print-fn* (fn [& args] - (.apply (.-log js/console) js/console (into-array args))))) + (.apply (.-log js/console) js/console (into-array args)))) + (set! *print-err-fn* + (fn [& args] + (.apply (.-error js/console) js/console (into-array args)))) + nil) (def ^{:doc "bound in a repl thread to the most recent value printed"} diff --git a/src/main/cljs/cljs/nodejs.cljs b/src/main/cljs/cljs/nodejs.cljs index d2be0bbe6..e467f2997 100644 --- a/src/main/cljs/cljs/nodejs.cljs +++ b/src/main/cljs/cljs/nodejs.cljs @@ -19,4 +19,8 @@ (set! *print-newline* false) (set! *print-fn* (fn [& args] - (.apply (.-log js/console) js/console (into-array args))))) + (.apply (.-log js/console) js/console (into-array args)))) + (set! *print-err-fn* + (fn [& args] + (.apply (.-error js/console) js/console (into-array args)))) + nil) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 086153366..2c36a8b36 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -341,8 +341,10 @@ (defn ^:private default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] - (binding [*out* *err*] - (println (message env (str "WARNING: " s))))))) + #?(:clj (binding [*out* *err*] + (println (message env (str "WARNING: " s)))) + :cljs (binding [*print-fn* *print-err-fn*] + (println (message env (str "WARNING: " s)))))))) (def ^:dynamic *cljs-warning-handlers* [default-warning-handler]) From 20324497884d0c794897260ffba52937b4b70ca9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 17:42:50 -0400 Subject: [PATCH 1283/4033] whitespace --- src/main/clojure/cljs/analyzer.cljc | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2c36a8b36..6e65ffa1e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1840,26 +1840,26 @@ (AssertionError. (str "Namespace " name " has a segment starting with an invaild " "JavaScript identifier")))))) - (let [docstring (if (string? (first args)) (first args)) - mdocstr (-> name meta :doc) - args (if docstring (next args) args) - metadata (if (map? (first args)) (first args)) - form-meta (meta form) - args (desugar-ns-specs (if metadata (next args) args)) - name (vary-meta name merge metadata) - excludes (parse-ns-excludes env args) - deps (atom #{}) - aliases (atom {:fns {} :macros {}}) - spec-parsers {:require (partial parse-require-spec env false deps aliases) + (let [docstring (if (string? (first args)) (first args)) + mdocstr (-> name meta :doc) + args (if docstring (next args) args) + metadata (if (map? (first args)) (first args)) + form-meta (meta form) + args (desugar-ns-specs (if metadata (next args) args)) + name (vary-meta name merge metadata) + excludes (parse-ns-excludes env args) + deps (atom #{}) + aliases (atom {:fns {} :macros {}}) + spec-parsers {:require (partial parse-require-spec env false deps aliases) :require-macros (partial parse-require-spec env true deps aliases) - :use (comp (partial parse-require-spec env false deps aliases) - (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}) - reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) - reloads (atom {}) + :use (comp (partial parse-require-spec env false deps aliases) + (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}) + reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) + reloads (atom {}) {uses :use requires :require use-macros :use-macros require-macros :require-macros imports :import :as params} (reduce (fn [m [k & libs]] @@ -1909,14 +1909,14 @@ (when (seq use-macros) (check-use-macros use-macros env)))) (let [ns-info - {:name name - :doc (or docstring mdocstr) - :excludes excludes - :use-macros use-macros + {:name name + :doc (or docstring mdocstr) + :excludes excludes + :use-macros use-macros :require-macros require-macros - :uses uses - :requires requires - :imports imports} + :uses uses + :requires requires + :imports imports} ns-info (if (:merge form-meta) ;; for merging information in via require usage in REPLs @@ -1932,7 +1932,7 @@ ns-info)) ns-info)] (swap! env/*compiler* update-in [::namespaces name] merge ns-info) - (merge {:env env :op :ns :form form + (merge {:op :ns :env env :form form :reloads @reloads} (cond-> ns-info (@reload :use) From 43bc4a837d6d5c0c37716b9ec12ad2117df04f20 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 17:47:38 -0400 Subject: [PATCH 1284/4033] move all information used for side-effects into the AST --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6e65ffa1e..e153884fb 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1916,7 +1916,10 @@ :require-macros require-macros :uses uses :requires requires - :imports imports} + :imports imports + :deps @deps + :reload @reload + :reloads @reloads} ns-info (if (:merge form-meta) ;; for merging information in via require usage in REPLs From dd6226198bc4ae8a22b070e798681b4060b23911 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 17:51:08 -0400 Subject: [PATCH 1285/4033] add cljs.js/*load-fn* --- src/main/cljs/cljs/js.cljs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index de80ac951..5f194c3fb 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -15,6 +15,14 @@ [cljs.tools.reader-types :as rt] [cljs.tagged-literals :as tags])) +(defonce + ^{:doc "Each runtime environment provides a different way to print error output. + Whatever function *print-fn* is bound to will be passed any + Strings which should be printed." :dynamic true} + *load-fn* + (fn [_] + (throw (js/Error. "No *load-fn* set")))) + (defn empty-env [] (env/default-compiler-env)) From 8ed5719d03e1d2c48483eec9ecc8c7c9de6c7103 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 18:02:54 -0400 Subject: [PATCH 1286/4033] fix cljs.js/*load-fn* docstring --- src/main/cljs/cljs/js.cljs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 5f194c3fb..c32659a27 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -16,11 +16,12 @@ [cljs.tagged-literals :as tags])) (defonce - ^{:doc "Each runtime environment provides a different way to print error output. - Whatever function *print-fn* is bound to will be passed any - Strings which should be printed." :dynamic true} + ^{:doc "Each runtime environment provides a different way to load libraries. + Whatever function *load-fn* is bound to will be passed a library name and a + callback. The callback should be invoked with source of the library (a string)." + :dynamic true} *load-fn* - (fn [_] + (fn [name cb] (throw (js/Error. "No *load-fn* set")))) (defn empty-env [] From bfc80ff71bafe3681474ff804e9e256f5d2d9611 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 18:04:34 -0400 Subject: [PATCH 1287/4033] docstring typos --- src/main/cljs/cljs/js.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index c32659a27..d01bfd89f 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -17,8 +17,9 @@ (defonce ^{:doc "Each runtime environment provides a different way to load libraries. - Whatever function *load-fn* is bound to will be passed a library name and a - callback. The callback should be invoked with source of the library (a string)." + Whatever function *load-fn* is bound to will be passed a library name + (a string) and a callback. The callback should be invoked with the source of + the library (a string)." :dynamic true} *load-fn* (fn [name cb] From 29109900d40b59ed195dafd0fbe97b59be5ca983 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 18:20:14 -0400 Subject: [PATCH 1288/4033] bring back *out* used by pprint --- src/main/cljs/cljs/core.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 378274f69..e83b71288 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -31,6 +31,10 @@ :doc "Var bound to the current namespace. Only used for bootstrapping."} *ns* nil) +(def + ^{:dynamic true} + *out* nil) + (def ^{:dynamic true} *assert* true) From 2c8bd1b6b00bc2e241c4b82e0fe14c5cadf219ff Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 18:26:17 -0400 Subject: [PATCH 1289/4033] AST passes need to take opts, add ns-side-effects pass fn --- src/main/clojure/cljs/analyzer.cljc | 33 +++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e153884fb..9be3296d7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2444,7 +2444,7 @@ :meta meta-expr :expr expr :children [meta-expr expr]}) expr))) -(defn infer-type [env ast] +(defn infer-type [env ast _] (let [tag (:tag ast)] (if (nil? tag) (let [tag (infer-tag env ast)] @@ -2453,6 +2453,35 @@ ast)) ast))) +(defn ns-side-effects + [env {:keys [op] :as ast} opts] + (if (= :ns op) + (let [{:keys [deps require-macros use-macros reload reloads]} ast] + (when (and *analyze-deps* (seq deps)) + (analyze-deps name deps env (dissoc opts :macros-ns))) + (when *load-macros* + (load-core) + (doseq [nsym (vals use-macros)] + (let [k (or (:use-macros reload) + (get-in reloads [:use-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) + (doseq [nsym (vals require-macros)] + (let [k (or (:require-macros reload) + (get-in reloads [:require-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) + (when (seq use-macros) + (check-use-macros use-macros env))) + ast) + ast)) + (def ^:dynamic *passes* nil) #?(:clj @@ -2505,7 +2534,7 @@ (if (seq form) form ()) form) ast (analyze-form env form name opts)] - (reduce (fn [ast pass] (pass env ast)) ast passes))) + (reduce (fn [ast pass] (pass env ast opts)) ast passes))) (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context From ae39482364227076a86e786e2c2bca5996f6893d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jul 2015 18:45:16 -0400 Subject: [PATCH 1290/4033] CLJS-1337: Move parse ns side-effects into a separate compiler pass --- src/main/clojure/cljs/analyzer.cljc | 47 +++++++++-------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9be3296d7..49b277abf 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1882,32 +1882,7 @@ (map (spec-parsers k) (remove #{:reload :reload-all} libs)))) {} (remove (fn [[r]] (= r :refer-clojure)) args))] - (when (and *analyze-deps* (seq @deps)) - #?(:clj (analyze-deps name @deps env (dissoc opts :macros-ns)))) - (when (and *analyze-deps* (seq uses)) - (check-uses uses env)) (set! *cljs-ns* name) - #?(:clj - (when *load-macros* - (load-core) - (doseq [nsym (vals use-macros)] - (let [k (or (:use-macros @reload) - (get-in @reloads [:use-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) - (intern-macros nsym k))) - (doseq [nsym (vals require-macros)] - (let [k (or (:require-macros @reload) - (get-in @reloads [:require-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) - (intern-macros nsym k))) - (when (seq use-macros) - (check-use-macros use-macros env)))) (let [ns-info {:name name :doc (or docstring mdocstr) @@ -1916,17 +1891,15 @@ :require-macros require-macros :uses uses :requires requires - :imports imports - :deps @deps - :reload @reload - :reloads @reloads} + :imports imports} ns-info (if (:merge form-meta) ;; for merging information in via require usage in REPLs (let [ns-info' (get-in @env/*compiler* [::namespaces name])] (if (pos? (count ns-info')) (let [merge-keys - [:use-macros :require-macros :uses :requires :imports]] + [:name :doc :excludes :use-macros :require-macros + :uses :requires :imports]] (merge ns-info' (merge-with merge @@ -1935,7 +1908,11 @@ ns-info)) ns-info)] (swap! env/*compiler* update-in [::namespaces name] merge ns-info) - (merge {:op :ns :env env :form form + (merge {:op :ns + :env env + :form form + :deps @deps + :reload @reload :reloads @reloads} (cond-> ns-info (@reload :use) @@ -2456,9 +2433,11 @@ (defn ns-side-effects [env {:keys [op] :as ast} opts] (if (= :ns op) - (let [{:keys [deps require-macros use-macros reload reloads]} ast] + (let [{:keys [deps uses require-macros use-macros reload reloads]} ast] (when (and *analyze-deps* (seq deps)) (analyze-deps name deps env (dissoc opts :macros-ns))) + (when (and *analyze-deps* (seq uses)) + (check-uses uses env)) (when *load-macros* (load-core) (doseq [nsym (vals use-macros)] @@ -2529,7 +2508,9 @@ (defn analyze* [env form name opts] (let [passes *passes* - passes (when (nil? passes) [infer-type]) + passes (when (nil? passes) + #?(:clj [infer-type ns-side-effects] + :cljs [infer-type])) form (if (instance? LazySeq form) (if (seq form) form ()) form) From 70381b438d1680cc8ca49556d4ae23f3727feadb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 15 Jul 2015 22:57:43 -0400 Subject: [PATCH 1291/4033] wip, if cljs.js ns required, add cljs/core.cljc as implicit dep --- src/main/clojure/cljs/closure.clj | 33 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 226abf08f..4f3d85f84 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -535,8 +535,11 @@ "Return an IJavaScript for this file. Compiled output will be written to the working directory." [opts {:keys [relative-path uri]}] - (let [js-file (comp/rename-to-js relative-path)] - (-compile uri (merge opts {:output-file js-file})))) + (let [js-file (comp/rename-to-js relative-path) + compiled (-compile uri (merge opts {:output-file js-file}))] + (cond-> compiled + (= ["cljs.js"] (deps/-provides compiled)) + (update-in compiled [:requires] conj ["cljs.core$macros"])))) (defn cljs-source-for-namespace "Given a namespace return the corresponding source with either a .cljs or @@ -579,6 +582,10 @@ (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))))))) +(defn compile-core-macros [opts] + (-compile (io/resource "cljs/core.cljc") + (merge opts {:output-file "core$macros.js"}))) + (defn cljs-dependencies "Given a list of all required namespaces, return a list of IJavaScripts which are the cljs dependencies. The returned list will @@ -588,19 +595,21 @@ Only load dependencies from the classpath." [opts requires] - (let [cljs-deps (fn [lib-names] - (->> (remove #(or ((@env/*compiler* :js-dependency-index) %) - (deps/find-classpath-lib %)) - lib-names) - (map cljs-source-for-namespace) - (remove (comp nil? :uri))))] + (letfn [(cljs-deps [lib-names] + (->> lib-names + (remove #(or ((@env/*compiler* :js-dependency-index) %) + (deps/find-classpath-lib %))) + (map cljs-source-for-namespace) + (remove (comp nil? :uri))))] (loop [required-files (cljs-deps requires) - visited (set required-files) - js-deps #{}] + visited (set required-files) + js-deps #{}] (if (seq required-files) (let [next-file (first required-files) - js (get-compiled-cljs opts next-file) - new-req (remove #(contains? visited %) (cljs-deps (deps/-requires js)))] + js (if (= "cljs.core$macros" next-file) + (compile-core-macros opts) + (get-compiled-cljs opts next-file)) + new-req (remove #(contains? visited %) (cljs-deps (deps/-requires js)))] (recur (into (rest required-files) new-req) (into visited new-req) (conj js-deps js))) From bdea41854f4a36e3ea0af42151bc210c4dfdd24d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Jul 2015 06:57:32 -0400 Subject: [PATCH 1292/4033] fix macros compilation --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4f3d85f84..df9857dc2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -584,7 +584,7 @@ (defn compile-core-macros [opts] (-compile (io/resource "cljs/core.cljc") - (merge opts {:output-file "core$macros.js"}))) + (merge opts {:output-file "cljs/core$macros.js"}))) (defn cljs-dependencies "Given a list of all required namespaces, return a list of From 49e4697e1b41f9cb973bfb9d7be85230c909aa30 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Jul 2015 07:13:08 -0400 Subject: [PATCH 1293/4033] fix cljs.tools.reader.reader-types import --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d01bfd89f..9be4420d6 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -12,7 +12,7 @@ [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.tools.reader :as r] - [cljs.tools.reader-types :as rt] + [cljs.tools.reader.reader-types :as rt] [cljs.tagged-literals :as tags])) (defonce From 2feb0e466f0558635a370f481b1cd68221562587 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Jul 2015 07:22:39 -0400 Subject: [PATCH 1294/4033] fix cljs.js/analyze* --- src/main/cljs/cljs/js.cljs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 9be4420d6..11ce7e1e7 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -32,17 +32,17 @@ ;; Analyze (defn analyze* [env source cb] - (let [rdr (rt/string-push-back-reader source) - eof (js-obj) - env (ana/empty-env)] + (let [rdr (rt/string-push-back-reader source) + eof (js-obj) + aenv (ana/empty-env)] (env/with-compiler-env env (loop [] (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) - (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*))] - (ana/analyze env form)) - (recur)) - (cb)))))) + (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*))] + (ana/analyze aenv form) + (recur)) + (cb))))))) (defn analyze [env source cb] (binding [ana/*cljs-ns* (or (:ns env) 'cljs.user) From d496117a6346d66375a11c01ef364ce93d53b56b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Jul 2015 07:28:11 -0400 Subject: [PATCH 1295/4033] cond-> typo --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index df9857dc2..9cb020b58 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -539,7 +539,7 @@ compiled (-compile uri (merge opts {:output-file js-file}))] (cond-> compiled (= ["cljs.js"] (deps/-provides compiled)) - (update-in compiled [:requires] conj ["cljs.core$macros"])))) + (update-in [:requires] conj ["cljs.core$macros"])))) (defn cljs-source-for-namespace "Given a namespace return the corresponding source with either a .cljs or From d4b76e9751704bcabab51c2dd499dc4fe06c296a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Jul 2015 07:48:23 -0400 Subject: [PATCH 1296/4033] conj lib name, not vector --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9cb020b58..3f6ecb44b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -539,7 +539,7 @@ compiled (-compile uri (merge opts {:output-file js-file}))] (cond-> compiled (= ["cljs.js"] (deps/-provides compiled)) - (update-in [:requires] conj ["cljs.core$macros"])))) + (update-in [:requires] conj "cljs.core$macros")))) (defn cljs-source-for-namespace "Given a namespace return the corresponding source with either a .cljs or From 42204f82d6ea494a7091b68112d5c5f00e3b3fb7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Jul 2015 08:02:33 -0400 Subject: [PATCH 1297/4033] explicit goog.require of macros ns --- src/main/cljs/cljs/js.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 11ce7e1e7..3014bfc93 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -15,6 +15,8 @@ [cljs.tools.reader.reader-types :as rt] [cljs.tagged-literals :as tags])) +(js/goog.require "cljs.core$macros") + (defonce ^{:doc "Each runtime environment provides a different way to load libraries. Whatever function *load-fn* is bound to will be passed a library name From 91e1d52df3e6811c02268d935ebc3271a78ab7a6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Jul 2015 11:11:07 -0400 Subject: [PATCH 1298/4033] fix macro compilation logic, now works --- src/main/clojure/cljs/closure.clj | 23 ++++++++++++----------- src/main/clojure/cljs/compiler.cljc | 4 +++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3f6ecb44b..549e94829 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -539,19 +539,22 @@ compiled (-compile uri (merge opts {:output-file js-file}))] (cond-> compiled (= ["cljs.js"] (deps/-provides compiled)) - (update-in [:requires] conj "cljs.core$macros")))) + (update-in [:requires] concat ["cljs.core$macros"])))) (defn cljs-source-for-namespace "Given a namespace return the corresponding source with either a .cljs or .cljc extension." [ns] - (let [path (-> (munge ns) (string/replace \. \/)) - relpath (str path ".cljs")] - (if-let [res (io/resource relpath)] - {:relative-path relpath :uri res} - (let [relpath (str path ".cljc")] - (if-let [res (io/resource relpath)] - {:relative-path relpath :uri res}))))) + (if (= "cljs.core$macros" ns) + (let [relpath "cljs/core.cljc"] + {:relative-path relpath :uri (io/resource relpath)}) + (let [path (-> (munge ns) (string/replace \. \/)) + relpath (str path ".cljs")] + (if-let [res (io/resource relpath)] + {:relative-path relpath :uri res} + (let [relpath (str path ".cljc")] + (if-let [res (io/resource relpath)] + {:relative-path relpath :uri res})))))) (defn source-for-namespace "Given a namespace and compilation environment return the relative path and @@ -606,9 +609,7 @@ js-deps #{}] (if (seq required-files) (let [next-file (first required-files) - js (if (= "cljs.core$macros" next-file) - (compile-core-macros opts) - (get-compiled-cljs opts next-file)) + js (get-compiled-cljs opts next-file) new-req (remove #(contains? visited %) (cljs-deps (deps/-requires js)))] (recur (into (rest required-files) new-req) (into visited new-req) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 0025d37f4..33f46c91e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1027,7 +1027,9 @@ (clojure.string/replace file-str #"\.cljs$" ".js") (.endsWith file-str ".cljc") - (clojure.string/replace file-str #"\.cljc$" ".js") + (if (= "cljs/core.cljc" file-str) + "cljs/core$macros.js" + (clojure.string/replace file-str #"\.cljc$" ".js")) :else (throw (IllegalArgumentException. From 6799d9df0c02dff043dedba72463c417f4334306 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jul 2015 17:46:38 -0400 Subject: [PATCH 1299/4033] conditionalize check-use-macros --- src/main/clojure/cljs/analyzer.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 49b277abf..e0948af8c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1632,7 +1632,8 @@ (defn check-use-macros [use-macros env] (doseq [[sym lib] use-macros] - (when (nil? (.findInternedVar ^clojure.lang.Namespace (find-ns lib) sym)) + (when (nil? #?(:clj (.findInternedVar ^clojure.lang.Namespace (find-ns lib) sym) + :cljs (.findInternedVar (find-macros-ns lib) sym))) (throw (error env (error-message :undeclared-ns-form {:type "macro" :lib lib :sym sym})))))) From 37cb116d7ffa04b3b3a39819d04f8761216fc816 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jul 2015 17:56:58 -0400 Subject: [PATCH 1300/4033] async cljs.js/analyze-deps wip --- src/main/cljs/cljs/js.cljs | 42 +++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 3014bfc93..cc354ffd4 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -33,7 +33,10 @@ ;; ----------------------------------------------------------------------------- ;; Analyze -(defn analyze* [env source cb] +(defn require [name cb] + (*load-fn* name cb)) + +(defn analyze* [env bound-vars source cb] (let [rdr (rt/string-push-back-reader source) eof (js-obj) aenv (ana/empty-env)] @@ -46,11 +49,40 @@ (recur)) (cb))))))) +(defn analyze-deps + "Given a lib, a namespace, deps, its dependencies, env, an analysis environment + and opts, compiler options - analyze all of the dependencies. Required to + correctly analyze usage of other namespaces." + ([lib deps env bound-vars cb] (analyze-deps lib deps env bound-vars nil cb)) + ([lib deps env bound-vars opts cb] + (let [compiler @(:*compiler* bound-vars)] + (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) + update-in [:dep-path] conj lib)] + (assert (every? #(not (contains? (:*cljs-dep-set* bound-vars) %)) deps) + (str "Circular dependency detected " + (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) + (if (seq deps) + (let [dep (first deps)] + (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) + (contains? (:js-dependency-index compiler) (name dep))) + (require name + (fn [source] + (if-not (nil? source) + (analyze* env bound-vars source + (fn [] + (analyze-deps lib (next deps) env bound-vars opts cb))) + (throw + (ana/error env + (ana/error-message :undeclared-ns + {:ns-sym dep :js-provide (name dep)})))))))) + (cb)))))) + (defn analyze [env source cb] - (binding [ana/*cljs-ns* (or (:ns env) 'cljs.user) - *ns* (create-ns ana/*cljs-ns*) - r/*data-readers* tags/*cljs-data-readers*] - (analyze* env source cb))) + (analyze* env + {:*cljs-ns* (or (:ns env) 'cljs.user) + :*ns* (create-ns ana/*cljs-ns*) + :*data-readers* tags/*cljs-data-readers*} + source cb)) ;; ----------------------------------------------------------------------------- ;; Emit From 53ff5b5b349629fe47d8e34ab35ad0332f70c7b9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jul 2015 18:03:20 -0400 Subject: [PATCH 1301/4033] need to thread bound vars through --- src/main/cljs/cljs/js.cljs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index cc354ffd4..38692801b 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -87,20 +87,19 @@ ;; ----------------------------------------------------------------------------- ;; Emit -(defn emit* [env ast cb] +(defn emit* [env bound-vars ast cb] (cb (with-out-str (comp/emit ast)))) (defn emit [env ast cb] - (env/with-compiler-env env - (emit* env ast cb))) + (emit* env {} ast cb)) ;; ----------------------------------------------------------------------------- ;; Eval -(defn eval* [env form cb] +(defn eval* [env bound-vars form cb] (let [ana-env (ana/empty-env)] (cb (ana/analyze ana-env form)))) (defn eval [env form cb] (env/with-compiler-env env - (eval* env form cb))) \ No newline at end of file + (eval* env {} form cb))) \ No newline at end of file From e0f4fe12a406b086dbe1f02d4ea7cd205502cbc2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jul 2015 18:18:02 -0400 Subject: [PATCH 1302/4033] sketch of macro loading --- src/main/cljs/cljs/js.cljs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 38692801b..8232c7c3e 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -33,8 +33,11 @@ ;; ----------------------------------------------------------------------------- ;; Analyze -(defn require [name cb] - (*load-fn* name cb)) +(defn require + ([name cb] + (*load-fn* name cb)) + ([name reload cb] + (*load-fn* name cb))) (defn analyze* [env bound-vars source cb] (let [rdr (rt/string-push-back-reader source) @@ -65,7 +68,7 @@ (let [dep (first deps)] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep))) - (require name + (*load-fn* name (fn [source] (if-not (nil? source) (analyze* env bound-vars source @@ -77,6 +80,23 @@ {:ns-sym dep :js-provide (name dep)})))))))) (cb)))))) +(defn load-macros [k macros reload reloads bound-vars cb] + (if (seq macros) + (let [nsym (first (vals macros))] + (let [k (or (k reload) + (get-in reloads [k nsym]) + (and (= nsym name) (:*reload-macros* bound-vars) :reload))] + (if k + (require nsym k + (fn [] + (load-macros k (next macros) reload reloads bound-vars cb))) + (require nsym + (fn [] + (load-macros k (next macros) reload reloads bound-vars cb)))) + ;(intern-macros nsym k) + )) + (cb))) + (defn analyze [env source cb] (analyze* env {:*cljs-ns* (or (:ns env) 'cljs.user) From d14c8fb9c967b71250a6ca061824353af3a1708d Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jul 2015 18:21:28 -0400 Subject: [PATCH 1303/4033] add cljs.js/*eval-fn* --- src/main/cljs/cljs/js.cljs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 8232c7c3e..3ce6c8ae9 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -27,6 +27,15 @@ (fn [name cb] (throw (js/Error. "No *load-fn* set")))) +(defonce + ^{:doc "Each runtime environment provides various ways to eval JavaScript + source. Whatever function *eval-fn* is bound to will be passed source to eval + (a string). The result of eval will be passed back immediately to the caller." + :dynamic true} + *eval-fn* + (fn [source] + (throw (js/Error. "No *eval-fn* set")))) + (defn empty-env [] (env/default-compiler-env)) From eef613f492ccdd4e2e4bd825fd0a7570ea6b85c1 Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Thu, 16 Jul 2015 15:15:12 +0200 Subject: [PATCH 1304/4033] CLJS-1341: Fix CommonJS conversion bug Fix bug, where we are trying to convert AMD to CommonJS, even though we already have a CommonJS module. --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 549e94829..05c650cd2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1257,7 +1257,8 @@ es6-loader (ES6ModuleLoader. closure-compiler module-root) cjs (ProcessCommonJSModules. closure-compiler es6-loader)] (compile-if can-convert-amd? - (.process (TransformAMDToCJSModule. closure-compiler) nil root)) + (when (= (:module-type js) :amd) + (.process (TransformAMDToCJSModule. closure-compiler) nil root))) (.process cjs nil root) (.toSource closure-compiler root)))) From 4af6a00e1e207fde5184cb3fb0085b838c2581bf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 17 Jul 2015 07:18:29 -0400 Subject: [PATCH 1305/4033] cljs.js wip --- src/main/cljs/cljs/js.cljs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 3ce6c8ae9..8779aa883 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -52,7 +52,10 @@ (let [rdr (rt/string-push-back-reader source) eof (js-obj) aenv (ana/empty-env)] - (env/with-compiler-env env + (binding [ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars) + env/*compiler* env] (loop [] (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) @@ -76,7 +79,7 @@ (if (seq deps) (let [dep (first deps)] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) - (contains? (:js-dependency-index compiler) (name dep))) + (contains? (:js-dependency-index compiler) (name dep))) (*load-fn* name (fn [source] (if-not (nil? source) From aa164b9da4ccaeee4c105dc50f724b8ed5967132 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 17 Jul 2015 07:35:41 -0400 Subject: [PATCH 1306/4033] async ns-side-effects ast pass --- src/main/cljs/cljs/js.cljs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 8779aa883..7f80a1149 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -109,6 +109,27 @@ )) (cb))) +(defn ns-side-effects + [env {:keys [op] :as ast} opts bound-vars cb] + (if (= :ns op) + (let [{:keys [deps uses require-macros use-macros reload reloads]} ast] + (letfn [(check-uses-and-load-macros [] + (when (and (:*analyze-deps* bound-vars) (seq uses)) + (ana/check-uses uses env)) + (when (:*load-macros* bound-vars) + (load-macros :use-macros use-macros reload reloads bound-vars + (fn [] + (load-macros :require-macros require-macros reloads reloads bound-vars + (fn [] + (when (seq use-macros) + (ana/check-use-macros use-macros env)) + (cb ast)))))))] + (if (and (:*analyze-deps* bound-vars) (seq deps)) + (analyze-deps name deps env (dissoc opts :macros-ns) + check-uses-and-load-macros) + (check-uses-and-load-macros)))) + (cb ast))) + (defn analyze [env source cb] (analyze* env {:*cljs-ns* (or (:ns env) 'cljs.user) From 047af0965c6760483aca94b55666563e0857629d Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 16 Jul 2015 16:07:28 +0200 Subject: [PATCH 1307/4033] CLJS-1342: cljs.reader/read-string should throw Error when not called with string --- src/main/cljs/cljs/reader.cljs | 3 ++- src/test/cljs/cljs/reader_test.cljs | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index da4ab2194..4f5d74ad5 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -443,10 +443,11 @@ nil if the end of stream has been reached") (defn read-string "Reads one object from the string s" [s] + (when-not (string? s) + (throw (js/Error. "Cannot read from non-string object."))) (let [r (push-back-reader s)] (read r false nil false))) - ;; read instances (defn ^:private zero-fill-right-and-truncate [s width] diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 99d8d0253..42d7c0516 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -140,6 +140,13 @@ :failed-to-throw (catch js/Error e :ok))] (is (= r :ok))))) + + (testing "Testing non-string input, CLJS-1342" + (let [r (try + (reader/read-string :foo) + :failed-to-throw + (catch js/Error e :ok))] + (is (= r :ok)))) ) (deftest test-717 From be864bcb598b5e60f77f6629045bec5d80caf605 Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Sun, 17 May 2015 18:57:58 +0200 Subject: [PATCH 1308/4033] CLJS-1267: Added the :end-test-all-vars and :end-test-vars events to have end events for all cljs.test api functions --- src/main/cljs/cljs/test.clj | 5 ++++- src/main/cljs/cljs/test.cljs | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/test.clj b/src/main/cljs/cljs/test.clj index a8750e8e0..5a1137d1c 100644 --- a/src/main/cljs/cljs/test.clj +++ b/src/main/cljs/cljs/test.clj @@ -334,7 +334,10 @@ "Calls test-vars on every var with :test metadata interned in the namespace, with fixtures." [[quote ns :as form]] - `(cljs.test/run-block (test-all-vars-block ~form))) + `(cljs.test/run-block + (concat (test-all-vars-block ~form) + [(fn [] + (report {:type :end-test-all-vars :ns ~form}))]))) (defmacro test-ns-block "Like test-ns, but returns a block for further composition and diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index 4181b3ab9..f41ee67a7 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -357,6 +357,8 @@ #_(println ":begin-test-var" (testing-vars-str m))) (defmethod report [::default :end-test-var] [m]) (defmethod report [::default :end-run-tests] [m]) +(defmethod report [::default :end-test-all-vars] [m]) +(defmethod report [::default :end-test-vars] [m]) ;; ============================================================================= ;; File, Line, and Column Helpers @@ -576,7 +578,9 @@ appropriate fixtures assuming they are present in the current testing environment." [vars] - (run-block (test-vars-block vars))) + (run-block (concat (test-vars-block vars) + [(fn [] + (report {:type :end-test-vars :vars vars}))]))) ;; ============================================================================= ;; Running Tests, high level functions From b2039e34871174f2f8a8a4052d7a9f9266876a88 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 15:12:19 -0400 Subject: [PATCH 1309/4033] analyze needs to take opts --- src/main/cljs/cljs/js.cljs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 7f80a1149..a9fde91d4 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -48,7 +48,9 @@ ([name reload cb] (*load-fn* name cb))) -(defn analyze* [env bound-vars source cb] +(declare ns-side-effects) + +(defn analyze* [env bound-vars source opts cb] (let [rdr (rt/string-push-back-reader source) eof (js-obj) aenv (ana/empty-env)] @@ -59,9 +61,12 @@ (loop [] (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) - (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*))] - (ana/analyze aenv form) - (recur)) + (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + ast (ana/analyze aenv form)] + (if (= (:op ast) :ns) + (ns-side-effects env ast opts bound-vars + (fn [_] (cb))) + (recur))) (cb))))))) (defn analyze-deps @@ -130,12 +135,15 @@ (check-uses-and-load-macros)))) (cb ast))) -(defn analyze [env source cb] - (analyze* env - {:*cljs-ns* (or (:ns env) 'cljs.user) - :*ns* (create-ns ana/*cljs-ns*) - :*data-readers* tags/*cljs-data-readers*} - source cb)) +(defn analyze + ([env source cb] + (analyze env source nil cb)) + ([env source opts cb] + (analyze* env + {:*cljs-ns* (or (:ns env) 'cljs.user) + :*ns* (create-ns ana/*cljs-ns*) + :*data-readers* tags/*cljs-data-readers*} + source opts cb))) ;; ----------------------------------------------------------------------------- ;; Emit From 0d4815c5138cd1ea85e0eeac778ede0fc44cdd34 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 15:55:22 -0400 Subject: [PATCH 1310/4033] missing passing opts --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index a9fde91d4..4b22d7769 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -88,7 +88,7 @@ (*load-fn* name (fn [source] (if-not (nil? source) - (analyze* env bound-vars source + (analyze* env bound-vars source opts (fn [] (analyze-deps lib (next deps) env bound-vars opts cb))) (throw From 692baae12dc5762965b0c56dafc6892edacfdaee Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 16:36:49 -0400 Subject: [PATCH 1311/4033] add cljs.js/*loaded*, cljs.js/require --- src/main/cljs/cljs/js.cljs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 4b22d7769..327c32eaf 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -42,11 +42,25 @@ ;; ----------------------------------------------------------------------------- ;; Analyze +(def *loaded* (atom #{})) + (defn require ([name cb] - (*load-fn* name cb)) + (when-not (contains? @*loaded* name) + (*load-fn* name + (fn [source] + (*eval-fn* source) + (cb true))))) ([name reload cb] - (*load-fn* name cb))) + (when (= :reload reload) + (swap! *loaded* disj name)) + (when (= :reload-all reload) + (reset! *loaded* #{})) + (when-not (contains? @*loaded* name) + (*load-fn* name + (fn [source] + (*eval-fn* source) + (cb true)))))) (declare ns-side-effects) From 653255b39a5ee0582457f2b45dd99b270c3be4ba Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 17:26:13 -0400 Subject: [PATCH 1312/4033] optimize pr-writer-impl --- src/main/cljs/cljs/core.cljs | 128 ++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e83b71288..78cb2727a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8623,73 +8623,77 @@ reduces them without incurring seq initialization" (declare print-map) +(defn ^boolean print-meta? [opts obj] + (and (not (nil? (get opts :meta))) + (implements? IMeta obj) + (not (nil? (meta obj))))) + (defn- pr-writer-impl [obj writer opts] (cond (nil? obj) (-write writer "nil") (undefined? obj) (-write writer "#") - :else (do - (when (and (get opts :meta) - (satisfies? IMeta obj) - (meta obj)) - (-write writer "^") - (pr-writer (meta obj) writer opts) - (-write writer " ")) - (cond - (nil? obj) (-write writer "nil") - - ;; handle CLJS ctors - ^boolean (.-cljs$lang$type obj) - (.cljs$lang$ctorPrWriter obj obj writer opts) - - ; Use the new, more efficient, IPrintWithWriter interface when possible. - (implements? IPrintWithWriter obj) - (-pr-writer ^not-native obj writer opts) - - (or (identical? (type obj) js/Boolean) (number? obj)) - (-write writer (str obj)) - - (object? obj) - (do - (-write writer "#js ") - (print-map - (map (fn [k] [(keyword k) (aget obj k)]) (js-keys obj)) - pr-writer writer opts)) - - (array? obj) - (pr-sequential-writer writer pr-writer "#js [" " " "]" opts obj) - - ^boolean (goog/isString obj) - (if (:readably opts) - (-write writer (quote-string obj)) - (-write writer obj)) - - (fn? obj) - (write-all writer "#<" (str obj) ">") - - (instance? js/Date obj) - (let [normalize (fn [n len] - (loop [ns (str n)] - (if (< (count ns) len) - (recur (str "0" ns)) - ns)))] - (write-all writer - "#inst \"" - (str (.getUTCFullYear obj)) "-" - (normalize (inc (.getUTCMonth obj)) 2) "-" - (normalize (.getUTCDate obj) 2) "T" - (normalize (.getUTCHours obj) 2) ":" - (normalize (.getUTCMinutes obj) 2) ":" - (normalize (.getUTCSeconds obj) 2) "." - (normalize (.getUTCMilliseconds obj) 3) "-" - "00:00\"")) - - (regexp? obj) (write-all writer "#\"" (.-source obj) "\"") - - (satisfies? IPrintWithWriter obj) - (-pr-writer obj writer opts) - - :else (write-all writer "#<" (str obj) ">"))))) + :else + (do + (when (print-meta? opts obj) + (-write writer "^") + (pr-writer (meta obj) writer opts) + (-write writer " ")) + (cond + (nil? obj) (-write writer "nil") + + ;; handle CLJS ctors + ^boolean (.-cljs$lang$type obj) + (.cljs$lang$ctorPrWriter obj obj writer opts) + + ; Use the new, more efficient, IPrintWithWriter interface when possible. + (implements? IPrintWithWriter obj) + (-pr-writer ^not-native obj writer opts) + + (or (true? obj) (false? obj) (number? obj)) + (-write writer (str obj)) + + (object? obj) + (do + (-write writer "#js ") + (print-map + (map (fn [k] [(keyword k) (aget obj k)]) (js-keys obj)) + pr-writer writer opts)) + + (array? obj) + (pr-sequential-writer writer pr-writer "#js [" " " "]" opts obj) + + ^boolean (goog/isString obj) + (if (:readably opts) + (-write writer (quote-string obj)) + (-write writer obj)) + + ^boolean (goog/isFunction obj) + (write-all writer "#<" (str obj) ">") + + (instance? js/Date obj) + (let [normalize (fn [n len] + (loop [ns (str n)] + (if (< (count ns) len) + (recur (str "0" ns)) + ns)))] + (write-all writer + "#inst \"" + (str (.getUTCFullYear obj)) "-" + (normalize (inc (.getUTCMonth obj)) 2) "-" + (normalize (.getUTCDate obj) 2) "T" + (normalize (.getUTCHours obj) 2) ":" + (normalize (.getUTCMinutes obj) 2) ":" + (normalize (.getUTCSeconds obj) 2) "." + (normalize (.getUTCMilliseconds obj) 3) "-" + "00:00\"")) + + (regexp? obj) (write-all writer "#\"" (.-source obj) "\"") + + (implements? IPrintWithWriter obj) + (-pr-writer obj writer opts) + + :else (write-all writer "#<" (str obj) ">"))))) (defn- pr-writer "Prefer this to pr-seq, because it makes the printing function From ab65d5132608ebb656721572e7833db33d5315dd Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 17:33:48 -0400 Subject: [PATCH 1313/4033] missing boolean hint on regexp? --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 78cb2727a..3fe5e7bdd 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8519,7 +8519,7 @@ reduces them without incurring seq initialization" ;;;;;;;;;;;;;;;;;;;;;;;;; Regular Expressions ;;;;;;;;;; -(defn regexp? +(defn ^boolean regexp? "Returns true if x is a JavaScript RegExp instance." [x] (instance? js/RegExp x)) From 8a8849219d55f02c94866e9ac1317680fa23be1d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 18:37:29 -0400 Subject: [PATCH 1314/4033] cljs.js wip --- src/main/cljs/cljs/js.cljs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 327c32eaf..dae1e4159 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -33,7 +33,7 @@ (a string). The result of eval will be passed back immediately to the caller." :dynamic true} *eval-fn* - (fn [source] + (fn [js-source] (throw (js/Error. "No *eval-fn* set")))) (defn empty-env [] @@ -171,10 +171,23 @@ ;; ----------------------------------------------------------------------------- ;; Eval -(defn eval* [env bound-vars form cb] +(defn eval* [env bound-vars form opts cb] (let [ana-env (ana/empty-env)] - (cb (ana/analyze ana-env form)))) - -(defn eval [env form cb] - (env/with-compiler-env env - (eval* env {} form cb))) \ No newline at end of file + (binding [ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars) + env/*compiler* env] + (*eval-fn* + (with-out-str + (comp/emit (ana/analyze ana-env form nil opts))) + cb)))) + +(defn eval + ([env form cb] (eval env form nil cb)) + ([env form opts cb] + (env/with-compiler-env env + (eval* env + {:*cljs-ns* (or (:ns env) 'cljs.user) + :*ns* (create-ns ana/*cljs-ns*) + :*data-readers* tags/*cljs-data-readers*} + form opts cb)))) \ No newline at end of file From 83b10d6fecada45f5a090543cea483c0de4e9cff Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 18:40:40 -0400 Subject: [PATCH 1315/4033] *eval-fn* takes 1 arg --- src/main/cljs/cljs/js.cljs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index dae1e4159..14e72fd8d 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -177,10 +177,9 @@ *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars) env/*compiler* env] - (*eval-fn* - (with-out-str - (comp/emit (ana/analyze ana-env form nil opts))) - cb)))) + (cb (*eval-fn* + (with-out-str + (comp/emit (ana/analyze ana-env form nil opts)))))))) (defn eval ([env form cb] (eval env form nil cb)) From b74c1bea8df98fb504b2fe992d156673ee08d646 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 19:09:12 -0400 Subject: [PATCH 1316/4033] simplifying basic eval api --- src/main/cljs/cljs/js.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 14e72fd8d..ed7102cb9 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -173,7 +173,8 @@ (defn eval* [env bound-vars form opts cb] (let [ana-env (ana/empty-env)] - (binding [ana/*cljs-ns* (:*cljs-ns* bound-vars) + (binding [*eval-fn* (:*eval-fn* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars) env/*compiler* env] @@ -188,5 +189,6 @@ (eval* env {:*cljs-ns* (or (:ns env) 'cljs.user) :*ns* (create-ns ana/*cljs-ns*) - :*data-readers* tags/*cljs-data-readers*} + :*data-readers* tags/*cljs-data-readers* + :*eval-fn* (or (:js-eval opts) js/eval)} form opts cb)))) \ No newline at end of file From bb7f13b2ffcdf6b77cda453b22af0b21c10cc6a3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jul 2015 19:38:32 -0400 Subject: [PATCH 1317/4033] cljs.env.macros not cljs.env --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ed7102cb9..f7121c67d 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.js - (:require-macros [cljs.env :as env]) + (:require-macros [cljs.env.macros :as env]) (:require [cljs.env :as env] [cljs.analyzer :as ana] [cljs.compiler :as comp] From a3abd60f5be155d4cf3b450d6d3a02c65a82e62d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 17 Jul 2015 22:14:39 -0400 Subject: [PATCH 1318/4033] cljs.js wip --- src/main/cljs/cljs/js.cljs | 26 +++++++++++++------------- src/main/clojure/cljs/analyzer.cljc | 6 +++++- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index f7121c67d..dbb46c544 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -172,12 +172,13 @@ ;; Eval (defn eval* [env bound-vars form opts cb] - (let [ana-env (ana/empty-env)] - (binding [*eval-fn* (:*eval-fn* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) - r/*data-readers* (:*data-readers* bound-vars) - env/*compiler* env] + (binding [env/*compiler* env + *eval-fn* (:*eval-fn* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars)] + (let [ana-env (assoc (ana/empty-env) + :ns (ana/get-namespace ana/*cljs-ns*))] (cb (*eval-fn* (with-out-str (comp/emit (ana/analyze ana-env form nil opts)))))))) @@ -185,10 +186,9 @@ (defn eval ([env form cb] (eval env form nil cb)) ([env form opts cb] - (env/with-compiler-env env - (eval* env - {:*cljs-ns* (or (:ns env) 'cljs.user) - :*ns* (create-ns ana/*cljs-ns*) - :*data-readers* tags/*cljs-data-readers* - :*eval-fn* (or (:js-eval opts) js/eval)} - form opts cb)))) \ No newline at end of file + (eval* env + {:*cljs-ns* 'cljs.user + :*ns* (create-ns 'cljs.user) + :*data-readers* tags/*cljs-data-readers* + :*eval-fn* (or (:js-eval opts) js/eval)} + form opts cb))) \ No newline at end of file diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e0948af8c..559401c31 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -407,7 +407,11 @@ default-namespaces))))) (defn get-namespace [key] - (get-in @env/*compiler* [::namespaces key])) + (let [ns (get-in @env/*compiler* [::namespaces key])] + (if-not (nil? ns) + ns + (when (= 'cljs.user key) + {:name 'cljs.user})))) #?(:clj (defmacro no-warn [& body] From bf643dbbb06eb7ae5ec27cb9feaa9aa41960862f Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Sat, 18 Jul 2015 01:01:18 +0200 Subject: [PATCH 1319/4033] CLJS-1345 Analyzer tests are broken due to new error messages --- src/test/clojure/cljs/analyzer_tests.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index fce0f767d..631d18afb 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -95,7 +95,7 @@ (a/analyze ns-env '(ns foo.bar (:unless []))) (catch Exception e (.getMessage e))) - "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported")) + "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported")) (is (.startsWith (try (a/analyze ns-env '(ns foo.bar (:require baz.woz) (:require noz.goz))) @@ -260,7 +260,7 @@ (a/analyze test-env '(defn foo 123)) (catch Exception e (.getMessage e))) - "Parameter declaration \"123\" should be a vector at line"))) + "Parameter declaration \"123\" should be a vector"))) ;; ============================================================================= ;; ns desugaring From a1bd16a04e65e6195f2a8d6fc2d6efef9a1c978c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 12:54:37 -0400 Subject: [PATCH 1320/4033] add compile, pass all dynamic vars together --- src/main/cljs/cljs/js.cljs | 95 ++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index dbb46c544..17483d9c3 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -42,16 +42,17 @@ ;; ----------------------------------------------------------------------------- ;; Analyze +(declare compile*) + (def *loaded* (atom #{})) (defn require - ([name cb] + ([bound-vars name opts cb] (when-not (contains? @*loaded* name) (*load-fn* name (fn [source] - (*eval-fn* source) - (cb true))))) - ([name reload cb] + (compile* bound-vars source opts (fn [ret] (cb true))))))) + ([bound-vars name reload opts cb] (when (= :reload reload) (swap! *loaded* disj name)) (when (= :reload-all reload) @@ -59,27 +60,25 @@ (when-not (contains? @*loaded* name) (*load-fn* name (fn [source] - (*eval-fn* source) - (cb true)))))) + (compile* bound-vars source opts (fn [ret] (cb true)))))))) (declare ns-side-effects) -(defn analyze* [env bound-vars source opts cb] +(defn analyze* [bound-vars source opts cb] (let [rdr (rt/string-push-back-reader source) eof (js-obj) aenv (ana/empty-env)] - (binding [ana/*cljs-ns* (:*cljs-ns* bound-vars) + (binding [env/*compiler* (:*compiler* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (:*ns* bound-vars) - r/*data-readers* (:*data-readers* bound-vars) - env/*compiler* env] + r/*data-readers* (:*data-readers* bound-vars)] (loop [] (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) ast (ana/analyze aenv form)] (if (= (:op ast) :ns) - (ns-side-effects env ast opts bound-vars - (fn [_] (cb))) + (ns-side-effects bound-vars aenv ast opts (fn [_] (cb))) (recur))) (cb))))))) @@ -87,8 +86,8 @@ "Given a lib, a namespace, deps, its dependencies, env, an analysis environment and opts, compiler options - analyze all of the dependencies. Required to correctly analyze usage of other namespaces." - ([lib deps env bound-vars cb] (analyze-deps lib deps env bound-vars nil cb)) - ([lib deps env bound-vars opts cb] + ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars lib deps nil cb)) + ([bound-vars ana-env lib deps opts cb] (let [compiler @(:*compiler* bound-vars)] (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) update-in [:dep-path] conj lib)] @@ -102,49 +101,51 @@ (*load-fn* name (fn [source] (if-not (nil? source) - (analyze* env bound-vars source opts + (analyze* bound-vars source opts (fn [] - (analyze-deps lib (next deps) env bound-vars opts cb))) + (analyze-deps bound-vars lib (next deps) opts cb))) (throw - (ana/error env + (ana/error ana-env (ana/error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))) (cb)))))) -(defn load-macros [k macros reload reloads bound-vars cb] +(defn load-macros [bound-vars k macros reload reloads opts cb] (if (seq macros) - (let [nsym (first (vals macros))] + (let [env (:*compiler* bound-vars) + nsym (first (vals macros))] (let [k (or (k reload) (get-in reloads [k nsym]) (and (= nsym name) (:*reload-macros* bound-vars) :reload))] (if k - (require nsym k + (require bound-vars nsym k opts (fn [] - (load-macros k (next macros) reload reloads bound-vars cb))) - (require nsym + (load-macros bound-vars k (next macros) reload reloads opts cb))) + (require bound-vars nsym opts (fn [] - (load-macros k (next macros) reload reloads bound-vars cb)))) + (load-macros bound-vars k (next macros) reload reloads opts cb)))) ;(intern-macros nsym k) )) (cb))) (defn ns-side-effects - [env {:keys [op] :as ast} opts bound-vars cb] + [bound-vars ana-env {:keys [op] :as ast} opts cb] (if (= :ns op) - (let [{:keys [deps uses require-macros use-macros reload reloads]} ast] + (let [{:keys [deps uses require-macros use-macros reload reloads]} ast + env (:*compiler* bound-vars)] (letfn [(check-uses-and-load-macros [] (when (and (:*analyze-deps* bound-vars) (seq uses)) (ana/check-uses uses env)) (when (:*load-macros* bound-vars) - (load-macros :use-macros use-macros reload reloads bound-vars + (load-macros bound-vars :use-macros use-macros reload reloads opts (fn [] - (load-macros :require-macros require-macros reloads reloads bound-vars + (load-macros bound-vars :require-macros require-macros reloads reloads opts (fn [] (when (seq use-macros) (ana/check-use-macros use-macros env)) (cb ast)))))))] (if (and (:*analyze-deps* bound-vars) (seq deps)) - (analyze-deps name deps env (dissoc opts :macros-ns) + (analyze-deps bound-vars name deps (dissoc opts :macros-ns) check-uses-and-load-macros) (check-uses-and-load-macros)))) (cb ast))) @@ -153,8 +154,9 @@ ([env source cb] (analyze env source nil cb)) ([env source opts cb] - (analyze* env - {:*cljs-ns* (or (:ns env) 'cljs.user) + (analyze* + {:*compiler* env + :*cljs-ns* (or (:ns env) 'cljs.user) :*ns* (create-ns ana/*cljs-ns*) :*data-readers* tags/*cljs-data-readers*} source opts cb))) @@ -171,8 +173,8 @@ ;; ----------------------------------------------------------------------------- ;; Eval -(defn eval* [env bound-vars form opts cb] - (binding [env/*compiler* env +(defn eval* [bound-vars form opts cb] + (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (:*ns* bound-vars) @@ -186,9 +188,32 @@ (defn eval ([env form cb] (eval env form nil cb)) ([env form opts cb] - (eval* env - {:*cljs-ns* 'cljs.user + (eval* + {:*compiler* env + :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* :*eval-fn* (or (:js-eval opts) js/eval)} - form opts cb))) \ No newline at end of file + form opts cb))) + +;; ----------------------------------------------------------------------------- +;; Compile + +(defn compile* [bound-vars source opts cb] + (binding [env/*compiler (:*compiler* bound-vars) + *eval-fn* (:*eval-fn* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars)] + )) + +(defn compile + ([env source cb] (compile env source cb)) + ([env source opts cb] + (compile* + {:*compiler* env + :*cljs-ns* 'cljs.user + :*ns* (create-ns 'cljs.user) + :*data-readers* tags/*cljs-data-readers* + :*eval-fn* (or (:js-eval opts) js/eval)}` + source opts cb))) \ No newline at end of file From 77de88deac753382e0ebcb6d9e9a93889c71feff Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 14:18:44 -0400 Subject: [PATCH 1321/4033] cljs.js wip --- src/main/cljs/cljs/js.cljs | 92 +++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 17483d9c3..6918806cd 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -13,7 +13,8 @@ [cljs.compiler :as comp] [cljs.tools.reader :as r] [cljs.tools.reader.reader-types :as rt] - [cljs.tagged-literals :as tags])) + [cljs.tagged-literals :as tags]) + (:import [goog.string StringBuffer])) (js/goog.require "cljs.core$macros") @@ -42,7 +43,7 @@ ;; ----------------------------------------------------------------------------- ;; Analyze -(declare compile*) +(declare eval-str*) (def *loaded* (atom #{})) @@ -51,7 +52,8 @@ (when-not (contains? @*loaded* name) (*load-fn* name (fn [source] - (compile* bound-vars source opts (fn [ret] (cb true))))))) + (eval-str* bound-vars source opts + (fn [ret] (cb true))))))) ([bound-vars name reload opts cb] (when (= :reload reload) (swap! *loaded* disj name)) @@ -60,7 +62,8 @@ (when-not (contains? @*loaded* name) (*load-fn* name (fn [source] - (compile* bound-vars source opts (fn [ret] (cb true)))))))) + (eval-str* bound-vars source opts + (fn [ret] (cb true)))))))) (declare ns-side-effects) @@ -72,15 +75,16 @@ ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars)] - (loop [] - (let [form (r/read {:eof eof} rdr)] - (if-not (identical? eof form) - (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - ast (ana/analyze aenv form)] - (if (= (:op ast) :ns) - (ns-side-effects bound-vars aenv ast opts (fn [_] (cb))) - (recur))) - (cb))))))) + ((fn analyze-loop [] + (let [form (r/read {:eof eof} rdr)] + (if-not (identical? eof form) + (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + ast (ana/analyze aenv form)] + (if (= :ns (:op ast)) + (ns-side-effects bound-vars aenv ast opts + (fn [_] (analyze-loop))) + (recur))) + (cb)))))))) (defn analyze-deps "Given a lib, a namespace, deps, its dependencies, env, an analysis environment @@ -200,12 +204,25 @@ ;; Compile (defn compile* [bound-vars source opts cb] - (binding [env/*compiler (:*compiler* bound-vars) - *eval-fn* (:*eval-fn* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) - r/*data-readers* (:*data-readers* bound-vars)] - )) + (let [rdr (rt/string-push-back-reader source) + eof (js-obj) + aenv (ana/empty-env) + sb (StringBuffer.)] + ((fn compile-loop [] + (binding [env/*compiler* (:*compiler* bound-vars) + *eval-fn* (:*eval-fn* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars)] + (let [form (r/read {:eof eof} rdr)] + (if-not (identical? eof form) + (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + ast (.append sb (comp/emit (ana/analyze aenv form)))] + (if (= :ns (:op ast)) + (ns-side-effects bound-vars aenv ast opts + (fn [_] (compile-loop))) + (recur))) + (cb (.toString cb))))))))) (defn compile ([env source cb] (compile env source cb)) @@ -216,4 +233,39 @@ :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* :*eval-fn* (or (:js-eval opts) js/eval)}` - source opts cb))) \ No newline at end of file + source opts cb))) + +;; ----------------------------------------------------------------------------- +;; Evaluate String + +(defn eval-str* [bound-vars source opts cb] + (let [rdr (rt/string-push-back-reader source) + eof (js-obj) + aenv (ana/empty-env) + sb (StringBuffer.)] + ((fn compile-loop [] + (binding [env/*compiler* (:*compiler* bound-vars) + *eval-fn* (:*eval-fn* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars)] + (let [form (r/read {:eof eof} rdr)] + (if-not (identical? eof form) + (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + ast (.append sb (comp/emit (ana/analyze aenv form)))] + (if (= :ns (:op ast)) + (ns-side-effects bound-vars aenv ast opts + (fn [_] (compile-loop))) + (recur))) + (cb (.toString cb))))))))) + +(defn eval-str + ([env source cb] (eval-str env source cb)) + ([env source opts cb] + (eval-str* + {:*compiler* env + :*cljs-ns* 'cljs.user + :*ns* (create-ns 'cljs.user) + :*data-readers* tags/*cljs-data-readers* + :*eval-fn* (or (:js-eval opts) js/eval)}` + source opts cb))) \ No newline at end of file From 47aad0e5b0e44ef11aecfd6e06737ed19557104e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 14:24:11 -0400 Subject: [PATCH 1322/4033] remove typos --- src/main/cljs/cljs/js.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 6918806cd..fe681325c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -232,7 +232,7 @@ :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* - :*eval-fn* (or (:js-eval opts) js/eval)}` + :*eval-fn* (or (:js-eval opts) js/eval)} source opts cb))) ;; ----------------------------------------------------------------------------- @@ -267,5 +267,5 @@ :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* - :*eval-fn* (or (:js-eval opts) js/eval)}` - source opts cb))) \ No newline at end of file + :*eval-fn* (or (:js-eval opts) js/eval)} + source opts cb))) \ No newline at end of file From b009a4bf4c8f3061b54f9aa602e2dd6ecc85b23a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 15:10:03 -0400 Subject: [PATCH 1323/4033] fix ns info merging regression --- src/main/clojure/cljs/analyzer.cljc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 559401c31..1dc4d96d7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1903,8 +1903,7 @@ (let [ns-info' (get-in @env/*compiler* [::namespaces name])] (if (pos? (count ns-info')) (let [merge-keys - [:name :doc :excludes :use-macros :require-macros - :uses :requires :imports]] + [:use-macros :require-macros :uses :requires :imports]] (merge ns-info' (merge-with merge From 4395ac88989cbe2edd5e54d3cebea3118ae3f2c4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 15:50:47 -0400 Subject: [PATCH 1324/4033] show all namespaces being loaded under :repl-verbose --- src/main/clojure/cljs/repl.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index d1da6858a..767480a3c 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -191,6 +191,8 @@ (doseq [source (->> sources (remove (comp #{:seed} :type)) (map #(cljsc/source-on-disk opts %)))] + (when (:repl-verbose opts) + (println "Loading:" (:provides source))) (-evaluate repl-env "" 1 (cljsc/add-dep-string opts source))) ;; REPLs that stream must manually load each dep - David From 5c1665016001f566be03eca0a90c1602dac10e3c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 16:00:17 -0400 Subject: [PATCH 1325/4033] remove dead code --- src/main/clojure/cljs/closure.clj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 05c650cd2..81c6e7f59 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -585,10 +585,6 @@ (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))))))) -(defn compile-core-macros [opts] - (-compile (io/resource "cljs/core.cljc") - (merge opts {:output-file "cljs/core$macros.js"}))) - (defn cljs-dependencies "Given a list of all required namespaces, return a list of IJavaScripts which are the cljs dependencies. The returned list will From 57bc51d146b17c46b62671cde2e1ad60610621f4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 19:22:09 -0400 Subject: [PATCH 1326/4033] support cljs.js under incremental compilation When the macros file is already compiled only parse-ns will be invoked. Enhance parse-ns to be core macros aware. fix compile-file* to account for parse-ns changes. make cljs.js handling immune to the fact that parse-ns returns symbols where compile-file returns strings for :provides :requires. --- src/main/clojure/cljs/analyzer.cljc | 33 +++++--- src/main/clojure/cljs/closure.clj | 6 +- src/main/clojure/cljs/compiler.cljc | 123 +++++++++++++++------------- 3 files changed, 90 insertions(+), 72 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1dc4d96d7..c0d138303 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1826,7 +1826,10 @@ :var (symbol (str clash-ns) (str name))}))))) (defn macro-ns-name [name] - (symbol (str name "$macros"))) + (let [name-str (str name)] + (if-not (.endsWith name-str "$macros") + (symbol (str name-str "$macros")) + name))) (defmethod parse 'ns [_ env [_ name & args :as form] _ opts] @@ -2648,21 +2651,27 @@ (if (seq forms) (let [env (empty-env) ast (no-warn (analyze env (first forms) nil opts))] - (if (= (:op ast) :ns) + (if (= :ns (:op ast)) (let [ns-name (:name ast) - deps (merge (:uses ast) (:requires ast))] + ns-name (if (and (= 'cljs.core ns-name) + (= "cljc" (util/ext src))) + 'cljs.core$macros + ns-name) + deps (merge (:uses ast) (:requires ast))] (.close ^Reader rdr) [(merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in compiler-env [:options :emit-constants]) - (conj 'constants-table))) - :file dest + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= 'cljs.core ns-name) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in compiler-env [:options :emit-constants]) + (conj 'constants-table))) + :file dest :source-file src - :ast ast} + :ast ast + :macros-ns (or (:macros-ns opts) + (= 'cljs.core$macros ns-name))} (when (and dest (.exists ^File dest)) {:lines (with-open [reader (io/reader dest)] (-> reader line-seq count))})) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 81c6e7f59..d21f454b7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -538,14 +538,16 @@ (let [js-file (comp/rename-to-js relative-path) compiled (-compile uri (merge opts {:output-file js-file}))] (cond-> compiled - (= ["cljs.js"] (deps/-provides compiled)) + ;; TODO: IJavaScript :provides :requires should really + ;; always be Vector - David + (= ["cljs.js"] (into [] (map str) (deps/-provides compiled))) (update-in [:requires] concat ["cljs.core$macros"])))) (defn cljs-source-for-namespace "Given a namespace return the corresponding source with either a .cljs or .cljc extension." [ns] - (if (= "cljs.core$macros" ns) + (if (= "cljs.core$macros" (str ns)) (let [relpath "cljs/core.cljc"] {:relative-path relpath :uri (io/resource relpath)}) (let [path (-> (munge ns) (string/replace \. \/)) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 33f46c91e..fa6addbab 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1074,9 +1074,9 @@ (util/debug-prn "Compiling" (str src))) (let [ext (util/ext src) {:keys [ns] :as ns-info} (ana/parse-ns src)] - (if-let [cached (and (= (:optimizations opts) :none) - (not= ext "cljc") - (= ns 'cljs.core) + (if-let [cached (and (= :none (:optimizations opts)) + (not= "cljc" ext) + (= 'cljs.core ns) (io/resource "cljs/core.aot.js"))] ;; no need to bother with analysis cache reading, handled by ;; with-core-cljs @@ -1092,68 +1092,75 @@ "file" (str (io/file (util/output-directory opts) "cljs" "core.js")))))) (ana/parse-ns src dest nil)) - (let [opts (if (or (= ext "clj") - (and (= ns 'cljs.core) (= ext "cljc")) + (let [opts (if (or (= "clj" ext) + (= 'cljs.core$macros ns) + (and (= ns 'cljs.core) (= "cljc" ext)) (:macros-ns opts)) (assoc opts :macros-ns true) opts)] (with-open [out ^java.io.Writer (io/make-writer dest {})] - (binding [*out* out - ana/*cljs-ns* 'cljs.user - ana/*cljs-file* (.getPath ^File src) - reader/*alias-map* (or reader/*alias-map* {}) + (binding [*out* out + ana/*cljs-ns* 'cljs.user + ana/*cljs-file* (.getPath ^File src) + reader/*alias-map* (or reader/*alias-map* {}) ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) - *source-map-data* (when (:source-map opts) - (atom - {:source-map (sorted-map) - :gen-col 0 - :gen-line 0}))] + *source-map-data* (when (:source-map opts) + (atom + {:source-map (sorted-map) + :gen-col 0 + :gen-line 0}))] (emitln (compiled-by-string opts)) (with-open [rdr (io/reader src)] - (loop [forms (ana/forms-seq* rdr (util/path src)) - ns-name nil - deps nil] - (if (seq forms) - (let [env (ana/empty-env) - ast (ana/analyze env (first forms) nil opts)] - (emit ast) - (if (= (:op ast) :ns) - (recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast))) - (recur (rest forms) ns-name deps))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :macros-ns (:macros-ns opts) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= (:optimizations opts) :none)) - (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" - (or (:source-map-url opts) (.getName sm-file)) - (if (true? (:source-map-timestamp opts)) - (str "?rel=" (System/currentTimeMillis)) - "")) - (spit sm-file - (sm/encode {(url-path src) (:source-map sm-data)} - {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest) - :source-map-timestamp (:source-map-timestamp opts) - :source-map-pretty-print (:source-map-pretty-print opts)})))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src (ana/parse-ns src) output-dir :write))) - ret)))))))))))))))) + (let [env (ana/empty-env)] + (loop [forms (ana/forms-seq* rdr (util/path src)) + ns-name nil + deps nil] + (if (seq forms) + (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) + ast (ana/analyze env (first forms) nil opts)] + (emit ast) + (if (= :ns (:op ast)) + (let [ns-name (:name ast) + ns-name (if (and (= 'cljs.core ns-name) + (= "cljc" ext)) + 'cljs.core$macros + ns-name)] + (recur (rest forms) ns-name (merge (:uses ast) (:requires ast)))) + (recur (rest forms) ns-name deps))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :macros-ns (:macros-ns opts) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= :none (:optimizations opts))) + (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] + (emits "\n//# sourceMappingURL=" + (or (:source-map-url opts) (.getName sm-file)) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + "")) + (spit sm-file + (sm/encode {(url-path src) (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 2) + :file (url-path dest) + :source-map-timestamp (:source-map-timestamp opts) + :source-map-pretty-print (:source-map-pretty-print opts)})))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src (ana/parse-ns src) output-dir :write))) + ret))))))))))))))))) #?(:clj (defn requires-compilation? From 7bd9e13bac227b8d3a00558c9677a306694f8516 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 19:44:22 -0400 Subject: [PATCH 1327/4033] need to exclude copy-arguments macro when bootstrapping --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 2943bf32e..55236d91f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -40,7 +40,7 @@ unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? - specify])]) + specify copy-arguments])]) #?(:cljs (:require-macros [cljs.core :as core])) (:require clojure.walk clojure.set From 08635bf6aaf54cc46ed960f198b0a6bb4faf15da Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jul 2015 19:52:57 -0400 Subject: [PATCH 1328/4033] if-not -> core/if-not in macros file --- src/main/clojure/cljs/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 55236d91f..953038f98 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1845,7 +1845,7 @@ [part bit] (fast-path-protocols p) msym (symbol (core/str "-cljs$lang$protocol_mask$partition" part "$"))] - (if-not (core/symbol? x) + (core/if-not (core/symbol? x) `(let [~xsym ~x] (if ~xsym (if (or ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit) false) @@ -1871,7 +1871,7 @@ [part bit] (fast-path-protocols p) msym (symbol (core/str "-cljs$lang$protocol_mask$partition" part "$"))] - (if-not (core/symbol? x) + (core/if-not (core/symbol? x) `(let [~xsym ~x] (if-not (nil? ~xsym) (if (or ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit) false) From ce11f6912b44ff9b0301cd46b47ba56aa91d72c1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 19 Jul 2015 11:37:31 -0400 Subject: [PATCH 1329/4033] cljs.js wip --- src/main/cljs/cljs/js.cljs | 92 ++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index fe681325c..0163e4f55 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -18,6 +18,11 @@ (js/goog.require "cljs.core$macros") +(defn debug-prn + [& args] + (binding [*print-fn* *print-err-fn*] + (apply println args))) + (defonce ^{:doc "Each runtime environment provides a different way to load libraries. Whatever function *load-fn* is bound to will be passed a library name @@ -49,21 +54,23 @@ (defn require ([bound-vars name opts cb] - (when-not (contains? @*loaded* name) - (*load-fn* name - (fn [source] - (eval-str* bound-vars source opts - (fn [ret] (cb true))))))) + (require bound-vars name nil opts cb)) ([bound-vars name reload opts cb] (when (= :reload reload) (swap! *loaded* disj name)) (when (= :reload-all reload) (reset! *loaded* #{})) (when-not (contains? @*loaded* name) - (*load-fn* name - (fn [source] - (eval-str* bound-vars source opts - (fn [ret] (cb true)))))))) + (let [env (:*env* bound-vars)] + (*load-fn* name + (fn [source] + (if source + (eval-str* bound-vars source opts + (fn [ret] (cb true))) + (throw + (ana/error env + (ana/error-message :undeclared-ns + {:ns-sym dep :js-provide (name dep)})))))))))) (declare ns-side-effects) @@ -86,6 +93,24 @@ (recur))) (cb)))))))) +(defn load-deps + ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars lib deps nil cb)) + ([bound-vars ana-env lib deps opts cb] + (let [compiler @(:*compiler* bound-vars)] + (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) + update-in [:dep-path] conj lib)] + (assert (every? #(not (contains? (:*cljs-dep-set* bound-vars) %)) deps) + (str "Circular dependency detected " + (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) + (if (seq deps) + (let [dep (first deps)] + (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) + (contains? (:js-dependency-index compiler) (name dep))) + (require bound-vars name opts + (fn [_] + (load-deps bound-vars lib (next deps) opts cb))))) + (cb)))))) + (defn analyze-deps "Given a lib, a namespace, deps, its dependencies, env, an analysis environment and opts, compiler options - analyze all of the dependencies. Required to @@ -133,26 +158,35 @@ (cb))) (defn ns-side-effects - [bound-vars ana-env {:keys [op] :as ast} opts cb] - (if (= :ns op) - (let [{:keys [deps uses require-macros use-macros reload reloads]} ast - env (:*compiler* bound-vars)] - (letfn [(check-uses-and-load-macros [] - (when (and (:*analyze-deps* bound-vars) (seq uses)) - (ana/check-uses uses env)) - (when (:*load-macros* bound-vars) - (load-macros bound-vars :use-macros use-macros reload reloads opts - (fn [] - (load-macros bound-vars :require-macros require-macros reloads reloads opts - (fn [] - (when (seq use-macros) - (ana/check-use-macros use-macros env)) - (cb ast)))))))] - (if (and (:*analyze-deps* bound-vars) (seq deps)) - (analyze-deps bound-vars name deps (dissoc opts :macros-ns) - check-uses-and-load-macros) - (check-uses-and-load-macros)))) - (cb ast))) + ([bound-vars ana-env ast opts cb] + (ns-side-effects false bound-vars ana-env ast opts cb)) + ([load bound-vars ana-env {:keys [op] :as ast} opts cb] + (if (= :ns op) + (let [{:keys [deps uses requires require-macros use-macros reload reloads]} ast + env (:*compiler* bound-vars)] + (letfn [(check-uses-and-load-macros [] + (when (and (:*analyze-deps* bound-vars) (seq uses)) + (ana/check-uses uses env)) + (when (:*load-macros* bound-vars) + (load-macros bound-vars :use-macros use-macros reload reloads opts + (fn [] + (load-macros bound-vars :require-macros require-macros reloads reloads opts + (fn [] + (when (seq use-macros) + (ana/check-use-macros use-macros env)) + (cb ast)))))))] + (cond + (and load (seq deps)) + (load-deps bound-vars name deps (dissoc opts :macros-ns) + check-uses-and-load-macros) + + (and (not load) (:*analyze-deps* bound-vars) (seq deps)) + (analyze-deps bound-vars name deps (dissoc opts :macros-ns) + check-uses-and-load-macros) + + :else + (check-uses-and-load-macros)))) + (cb ast)))) (defn analyze ([env source cb] From 7d7ce830ca4eb5ad9589b954ce4b6aebf540c1cf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 19 Jul 2015 13:26:45 -0400 Subject: [PATCH 1330/4033] typo, move analyze loop bindings into the loop --- src/main/cljs/cljs/js.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 0163e4f55..2cb537f6b 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -78,11 +78,11 @@ (let [rdr (rt/string-push-back-reader source) eof (js-obj) aenv (ana/empty-env)] - (binding [env/*compiler* (:*compiler* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) - r/*data-readers* (:*data-readers* bound-vars)] - ((fn analyze-loop [] + ((fn analyze-loop [] + (binding [env/*compiler* (:*compiler* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars)] (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) @@ -256,7 +256,7 @@ (ns-side-effects bound-vars aenv ast opts (fn [_] (compile-loop))) (recur))) - (cb (.toString cb))))))))) + (cb (.toString sb))))))))) (defn compile ([env source cb] (compile env source cb)) From cd8d7b063b2134b6bab6e697daf9882e6db51562 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 19 Jul 2015 14:26:13 -0400 Subject: [PATCH 1331/4033] map destructuring perf enhancement --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 953038f98..8f24b0e8d 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -646,7 +646,7 @@ (core/let [gmap (gensym "map__") defaults (:or b)] (core/loop [ret (core/-> bvec (conj gmap) (conj v) - (conj gmap) (conj `(if (seq? ~gmap) (apply hash-map ~gmap) ~gmap)) + (conj gmap) (conj `(if (implements? ISeq ~gmap) (apply hash-map ~gmap) ~gmap)) ((core/fn [ret] (if (:as b) (conj ret (:as b) gmap) From 0c2ad96a62899afe9629040673537fbdb344ab61 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 19 Jul 2015 14:02:01 -0400 Subject: [PATCH 1332/4033] CLJS-1348: meta is printing for def at REPL Coerce to boolean to additionally cover the case that it is not false, while simultaneously honoring ^boolean return annotation. --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3fe5e7bdd..187c61866 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8624,7 +8624,7 @@ reduces them without incurring seq initialization" (declare print-map) (defn ^boolean print-meta? [opts obj] - (and (not (nil? (get opts :meta))) + (and (boolean (get opts :meta)) (implements? IMeta obj) (not (nil? (meta obj))))) From 100b1787c2cff3a51ef27f1acf746ca1e557a98a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 19 Jul 2015 23:03:19 -0400 Subject: [PATCH 1333/4033] eval-str returns result not source --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2cb537f6b..571f029d7 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -291,7 +291,7 @@ (ns-side-effects bound-vars aenv ast opts (fn [_] (compile-loop))) (recur))) - (cb (.toString cb))))))))) + (cb (*eval-fn* (.toString cb)))))))))) (defn eval-str ([env source cb] (eval-str env source cb)) From 50f7e5b9be756534d32f48c3073505e57449c1b5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 19 Jul 2015 23:12:38 -0400 Subject: [PATCH 1334/4033] fix recursive calls for eval & eval-str --- src/main/cljs/cljs/js.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 571f029d7..c4d374124 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -259,7 +259,8 @@ (cb (.toString sb))))))))) (defn compile - ([env source cb] (compile env source cb)) + ([env source cb] + (compile env source nil cb)) ([env source opts cb] (compile* {:*compiler* env @@ -294,7 +295,8 @@ (cb (*eval-fn* (.toString cb)))))))))) (defn eval-str - ([env source cb] (eval-str env source cb)) + ([env source cb] + (eval-str env source nil cb)) ([env source opts cb] (eval-str* {:*compiler* env From 2babe74cade98e44eb9611ad9531e00dd7474334 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 19 Jul 2015 23:29:00 -0400 Subject: [PATCH 1335/4033] need to capture compiler output as string --- src/main/cljs/cljs/js.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index c4d374124..3f6dd28cf 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -251,7 +251,7 @@ (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - ast (.append sb (comp/emit (ana/analyze aenv form)))] + ast (.append sb (with-out-str (comp/emit (ana/analyze aenv form))))] (if (= :ns (:op ast)) (ns-side-effects bound-vars aenv ast opts (fn [_] (compile-loop))) @@ -287,7 +287,7 @@ (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - ast (.append sb (comp/emit (ana/analyze aenv form)))] + ast (.append sb (with-out-str (comp/emit (ana/analyze aenv form))))] (if (= :ns (:op ast)) (ns-side-effects bound-vars aenv ast opts (fn [_] (compile-loop))) From 2f824c5361b1a5ee0f64053f140d3fa8f597087e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 08:36:04 -0400 Subject: [PATCH 1336/4033] enhance cljs.analyzer/get-namespace to take environment as parameter --- src/main/clojure/cljs/analyzer.cljc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c0d138303..381692dfe 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -406,12 +406,15 @@ (::namespaces @env/*compiler*) default-namespaces))))) -(defn get-namespace [key] - (let [ns (get-in @env/*compiler* [::namespaces key])] - (if-not (nil? ns) - ns - (when (= 'cljs.user key) - {:name 'cljs.user})))) +(defn get-namespace + ([key] + (get-namespace env/*compiler* key)) + ([cenv key] + (let [ns (get-in @cenv [::namespaces key])] + (if-not (nil? ns) + ns + (when (= 'cljs.user key) + {:name 'cljs.user}))))) #?(:clj (defmacro no-warn [& body] From f46db8f693b7337cdc095819e412ee3e154d9e83 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 09:02:50 -0400 Subject: [PATCH 1337/4033] the default compiler environment should always include empty cljs.user namespace. This fixes long outstanding issues where there would be a missing namespace if the current-ns was cljs.user. Also necessary prep work for CLJS-1277. --- src/main/clojure/cljs/env.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index cbb621626..8310f7332 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -41,7 +41,8 @@ state that is accessed/maintained by many different components."} (defn default-compiler-env ([] (default-compiler-env {})) ([options] - (atom (merge {:options options} + (atom (merge {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} + :options options} #?(:clj {:js-dependency-index (js-dependency-index options)}))))) #?(:clj From 2ab9309f3a1e66b2a3bb8f0cb16df7f71f02187f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 09:28:42 -0400 Subject: [PATCH 1338/4033] fix eval-str & compile ast processing logic. fix typo in eval-str*, cb -> sb. add :verbose logging to eval-str* --- src/main/cljs/cljs/js.cljs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 3f6dd28cf..1b4f6dfc9 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -251,7 +251,8 @@ (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - ast (.append sb (with-out-str (comp/emit (ana/analyze aenv form))))] + ast (ana/analyze aenv form)] + (.append sb (with-out-str (comp/emit ast))) (if (= :ns (:op ast)) (ns-side-effects bound-vars aenv ast opts (fn [_] (compile-loop))) @@ -287,12 +288,16 @@ (let [form (r/read {:eof eof} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - ast (.append sb (with-out-str (comp/emit (ana/analyze aenv form))))] + ast (ana/analyze aenv form)] + (.append sb (with-out-str (comp/emit ast))) (if (= :ns (:op ast)) (ns-side-effects bound-vars aenv ast opts (fn [_] (compile-loop))) (recur))) - (cb (*eval-fn* (.toString cb)))))))))) + (let [js-source (.toString sb)] + (when (:verbose opts) + (debug-prn js-source)) + (cb (*eval-fn* js-source)))))))))) (defn eval-str ([env source cb] From 57e39e3bee2276f66e661bb26871723673c1e3c3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 09:48:46 -0400 Subject: [PATCH 1339/4033] change require so that can supply only just name and callback --- src/main/cljs/cljs/js.cljs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 1b4f6dfc9..120a7be1c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -53,6 +53,16 @@ (def *loaded* (atom #{})) (defn require + ([name cb] + (require name nil cb)) + ([name opts cb] + (require + {:*compiler* (env/default-compiler-env) + :*cljs-ns* 'cljs.user + :*ns* (create-ns 'cljs.user) + :*data-readers* tags/*cljs-data-readers* + :*eval-fn* (or (:js-eval opts) js/eval)} + name opts cb)) ([bound-vars name opts cb] (require bound-vars name nil opts cb)) ([bound-vars name reload opts cb] From c4338c1996eb7513fe81c60bd55f8f9a861b6c86 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 10:37:48 -0400 Subject: [PATCH 1340/4033] cljs.js/*load-fn* should be passed the munged library name as string without extension --- src/main/cljs/cljs/js.cljs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 120a7be1c..fb8f6c230 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -23,9 +23,14 @@ (binding [*print-fn* *print-err-fn*] (apply println args))) +(defn ns->relpath + "Given a namespace as a symbol return the relative path sans extension" + [ns-sym] + (string/replace (ana/munge-path ns-sym) \. \/)) + (defonce ^{:doc "Each runtime environment provides a different way to load libraries. - Whatever function *load-fn* is bound to will be passed a library name + Whatever function *load-fn* is bound to will be passed a munged library name (a string) and a callback. The callback should be invoked with the source of the library (a string)." :dynamic true} @@ -72,7 +77,7 @@ (reset! *loaded* #{})) (when-not (contains? @*loaded* name) (let [env (:*env* bound-vars)] - (*load-fn* name + (*load-fn* (ns->relpath name) (fn [source] (if source (eval-str* bound-vars source opts @@ -137,7 +142,7 @@ (let [dep (first deps)] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep))) - (*load-fn* name + (*load-fn* (ns->relpath name) (fn [source] (if-not (nil? source) (analyze* bound-vars source opts From 59d04da2c7b0762cf7c6f95df36c52a3d758f79c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 12:24:03 -0400 Subject: [PATCH 1341/4033] add cljs.analyzer.api/empty-state & cljs.analyzer.api/with-state, better captures how cljs.env is used semantically. add cljs.analyzer.api/get-options accessor --- src/main/clojure/cljs/analyzer/api.clj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index 3999694e0..bd99d0d15 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -15,6 +15,17 @@ ;; ============================================================================= ;; Useful Utilities +(defn empty-state + "Creates an empty compilation state Atom." + [] + (env/default-compiler-env)) + +(defmacro with-state + "Run the body with the given compilation state Atom." + [state body] + `(env/with-compiler-env ~state + ~@body)) + (defn empty-env "Creates an empty analysis environment." [] @@ -27,6 +38,11 @@ `(binding [ana/*cljs-warnings* ~no-warnings] ~@body))) +(defn get-options + "Return the compiler options from compiler state." + [] + (get @env/*compiler* :options)) + (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context (one of :statement, :expr, :return), :ns (a symbol naming the From 9a0b0e31a33d291b56503d70050bfd91d0e01435 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 14:10:27 -0400 Subject: [PATCH 1342/4033] missing import --- src/main/cljs/cljs/js.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index fb8f6c230..1a72baf64 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -8,7 +8,8 @@ (ns cljs.js (:require-macros [cljs.env.macros :as env]) - (:require [cljs.env :as env] + (:require [clojure.string :as string] + [cljs.env :as env] [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.tools.reader :as r] From e088f40624cf1bcbaefba1313c68076451d76600 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 14:57:30 -0400 Subject: [PATCH 1343/4033] clarify cljs.js/*load-fn* responsibility --- src/main/cljs/cljs/js.cljs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 1a72baf64..a2574d965 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -30,10 +30,11 @@ (string/replace (ana/munge-path ns-sym) \. \/)) (defonce - ^{:doc "Each runtime environment provides a different way to load libraries. - Whatever function *load-fn* is bound to will be passed a munged library name - (a string) and a callback. The callback should be invoked with the source of - the library (a string)." + ^{:doc "Each runtime environment provides a different way to load source. + Whatever function *load-fn* is bound to will be passed a munged relative + library path (a string) and a callback. It is up to the implementor to + correctly resolvea .cljs, .cljc, or .js file (the order must be respected). + The callback should be invoked with the source of the library (a string)." :dynamic true} *load-fn* (fn [name cb] From ff98cf60222a4a43fc9fe6366739b0ca7e2f3ae5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 15:29:11 -0400 Subject: [PATCH 1344/4033] typo in cljs.js/*load-fn* docstring --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index a2574d965..f20a43121 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -33,7 +33,7 @@ ^{:doc "Each runtime environment provides a different way to load source. Whatever function *load-fn* is bound to will be passed a munged relative library path (a string) and a callback. It is up to the implementor to - correctly resolvea .cljs, .cljc, or .js file (the order must be respected). + orrectly resolve a .cljs, .cljc, or .js file (the order must be respected). The callback should be invoked with the source of the library (a string)." :dynamic true} *load-fn* From ad2c509672d3d67e7b0001ae9e4157971a717f1c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 20 Jul 2015 09:56:44 -0400 Subject: [PATCH 1345/4033] CLJS-1352: cljs.js: Allow conditional readers --- src/main/cljs/cljs/js.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index f20a43121..33c75ae9a 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -100,7 +100,7 @@ ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars)] - (let [form (r/read {:eof eof} rdr)] + (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) ast (ana/analyze aenv form)] @@ -265,7 +265,7 @@ ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars)] - (let [form (r/read {:eof eof} rdr)] + (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) ast (ana/analyze aenv form)] @@ -302,7 +302,7 @@ ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars)] - (let [form (r/read {:eof eof} rdr)] + (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) ast (ana/analyze aenv form)] From 4a6f05b0ed495f8900ee77258f687902b6bdbfcc Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 16:10:51 -0400 Subject: [PATCH 1346/4033] clean up api namespace docstrings a bit. --- src/main/clojure/cljs/analyzer/api.clj | 2 ++ src/main/clojure/cljs/build/api.clj | 4 ++-- src/main/clojure/cljs/compiler/api.clj | 7 ++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index bd99d0d15..18cb8adcb 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -7,6 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.analyzer.api + "This is intended to be a stable api for those who need programmatic access + to the analyzer." (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve find-ns ns-publics remove-ns]) (:require [cljs.env :as env] diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 0474ec3fc..204067eba 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -6,8 +6,8 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software (ns cljs.build.api - "This is intended to be a stable api for those who intend to create - tools that use compiler data. + "This is intended to be a stable api for those who need programmatic access + to ClojureScript's project building facilities. For example: a build script may need to how to invalidate compiled files so that they will be recompiled." diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index 5aa79127e..c708c5947 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -7,11 +7,8 @@ ; You must not remove this notice, or any other, from this software (ns cljs.compiler.api - "This is intended to be a stable api for those who intend to create - tools that use compiler data. - - For example: a build script may need to how to invalidate compiled - files so that they will be recompiled." + "This is intended to be a stable api for those who need programmatic access + to the compiler." (:require [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] From e0688435bfd6de2b52f38018300c599df95ef908 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 18:54:26 -0400 Subject: [PATCH 1347/4033] add verbose logging to ns-side-effects, missing callback if all side-effects were suppressed --- src/main/cljs/cljs/js.cljs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 33c75ae9a..1d6fd4720 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -178,20 +178,32 @@ ([bound-vars ana-env ast opts cb] (ns-side-effects false bound-vars ana-env ast opts cb)) ([load bound-vars ana-env {:keys [op] :as ast} opts cb] + (when (:verbose opts) + (debug-prn "Namespace side effects for" (:name ast))) (if (= :ns op) (let [{:keys [deps uses requires require-macros use-macros reload reloads]} ast env (:*compiler* bound-vars)] (letfn [(check-uses-and-load-macros [] (when (and (:*analyze-deps* bound-vars) (seq uses)) + (when (:verbose opts) + (debug-prn "Checking uses")) (ana/check-uses uses env)) - (when (:*load-macros* bound-vars) - (load-macros bound-vars :use-macros use-macros reload reloads opts - (fn [] - (load-macros bound-vars :require-macros require-macros reloads reloads opts - (fn [] - (when (seq use-macros) - (ana/check-use-macros use-macros env)) - (cb ast)))))))] + (if (:*load-macros* bound-vars) + (do + (when (:verobse opts) + (debug-prn "Loading :use-macros")) + (load-macros bound-vars :use-macros use-macros reload reloads opts + (fn [] + (when (:verobse opts) + (debug-prn "Loading :require-macros")) + (load-macros bound-vars :require-macros require-macros reloads reloads opts + (fn [] + (when (seq use-macros) + (when (:verobse opts) + (debug-prn "Checking :use-macros")) + (ana/check-use-macros use-macros env)) + (cb ast)))))) + (cb ast)))] (cond (and load (seq deps)) (load-deps bound-vars name deps (dissoc opts :macros-ns) From fc6de12fd96e1fff403dfbc0a56adbe319797d88 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 19:06:37 -0400 Subject: [PATCH 1348/4033] analyze-deps & load-macros config for cljs.js/compile & cljs.js/eval-str --- src/main/cljs/cljs/js.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 1d6fd4720..42a9d7185 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -297,6 +297,8 @@ :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* + :*analyze-deps* (or (:analyze-deps opts) true) + :*load-macros* (or (:load-macros opts) true) :*eval-fn* (or (:js-eval opts) js/eval)} source opts cb))) @@ -337,5 +339,7 @@ :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* + :*analyze-deps* (or (:analyze-deps opts) true) + :*load-macros* (or (:load-macros opts) true) :*eval-fn* (or (:js-eval opts) js/eval)} source opts cb))) \ No newline at end of file From 7c5a53c6370b576e82e165733e5f08f8eafb4170 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 19:19:29 -0400 Subject: [PATCH 1349/4033] fix cljs.js/analyze-deps recursive call, pass analysis environment to analyze-deps from ns-side-effects --- src/main/cljs/cljs/js.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 42a9d7185..d7b046021 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -132,7 +132,8 @@ "Given a lib, a namespace, deps, its dependencies, env, an analysis environment and opts, compiler options - analyze all of the dependencies. Required to correctly analyze usage of other namespaces." - ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars lib deps nil cb)) + ([bound-vars ana-env lib deps cb] + (analyze-deps bound-vars ana-env lib deps nil cb)) ([bound-vars ana-env lib deps opts cb] (let [compiler @(:*compiler* bound-vars)] (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) @@ -210,7 +211,7 @@ check-uses-and-load-macros) (and (not load) (:*analyze-deps* bound-vars) (seq deps)) - (analyze-deps bound-vars name deps (dissoc opts :macros-ns) + (analyze-deps bound-vars ana-env deps (dissoc opts :macros-ns) check-uses-and-load-macros) :else From 443620e0b69d577c2ffff17695d68f64cea90470 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 19:29:34 -0400 Subject: [PATCH 1350/4033] fix analyze-deps calls --- src/main/cljs/cljs/js.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d7b046021..65b9d0837 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -150,7 +150,7 @@ (if-not (nil? source) (analyze* bound-vars source opts (fn [] - (analyze-deps bound-vars lib (next deps) opts cb))) + (analyze-deps bound-vars ana-env lib (next deps) opts cb))) (throw (ana/error ana-env (ana/error-message :undeclared-ns @@ -211,7 +211,7 @@ check-uses-and-load-macros) (and (not load) (:*analyze-deps* bound-vars) (seq deps)) - (analyze-deps bound-vars ana-env deps (dissoc opts :macros-ns) + (analyze-deps bound-vars ana-env lib deps (dissoc opts :macros-ns) check-uses-and-load-macros) :else From e52abec8548c52addf4e070e9d7af0f0f22c3819 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 19:33:52 -0400 Subject: [PATCH 1351/4033] typo in load-deps --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 65b9d0837..49a9b1c06 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -123,7 +123,7 @@ (let [dep (first deps)] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep))) - (require bound-vars name opts + (require bound-vars lib opts (fn [_] (load-deps bound-vars lib (next deps) opts cb))))) (cb)))))) From e1d80e22941c3403e9c5ebde31d48bc32cb859a0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2015 19:40:38 -0400 Subject: [PATCH 1352/4033] old analyze-deps logic not needed, user responsible via *load-fn*, typo, name -> dep --- src/main/cljs/cljs/js.cljs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 49a9b1c06..025e06d18 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -143,18 +143,16 @@ (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) (let [dep (first deps)] - (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) - (contains? (:js-dependency-index compiler) (name dep))) - (*load-fn* (ns->relpath name) - (fn [source] - (if-not (nil? source) - (analyze* bound-vars source opts - (fn [] - (analyze-deps bound-vars ana-env lib (next deps) opts cb))) - (throw - (ana/error ana-env - (ana/error-message :undeclared-ns - {:ns-sym dep :js-provide (name dep)})))))))) + (*load-fn* (ns->relpath dep) + (fn [source] + (if-not (nil? source) + (analyze* bound-vars source opts + (fn [] + (analyze-deps bound-vars ana-env lib (next deps) opts cb))) + (throw + (ana/error ana-env + (ana/error-message :undeclared-ns + {:ns-sym dep :js-provide (name dep)}))))))) (cb)))))) (defn load-macros [bound-vars k macros reload reloads opts cb] From 05954dccf56adb88e3309ab318fd867226e94c5a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 20:54:56 -0400 Subject: [PATCH 1353/4033] env -> state to mirror naming convention from api namespaces, various fixes --- src/main/cljs/cljs/js.cljs | 53 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 025e06d18..d09aa976e 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -49,7 +49,7 @@ (fn [js-source] (throw (js/Error. "No *eval-fn* set")))) -(defn empty-env [] +(defn empty-state [] (env/default-compiler-env)) ;; ----------------------------------------------------------------------------- @@ -87,9 +87,9 @@ (throw (ana/error env (ana/error-message :undeclared-ns - {:ns-sym dep :js-provide (name dep)})))))))))) + {:ns-sym name :js-provide (cljs.core/name name)})))))))))) -(declare ns-side-effects) +(declare ns-side-effects analyze-deps) (defn analyze* [bound-vars source opts cb] (let [rdr (rt/string-push-back-reader source) @@ -111,7 +111,8 @@ (cb)))))))) (defn load-deps - ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars lib deps nil cb)) + ([bound-vars ana-env lib deps cb] + (analyze-deps bound-vars lib deps nil cb)) ([bound-vars ana-env lib deps opts cb] (let [compiler @(:*compiler* bound-vars)] (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) @@ -209,7 +210,7 @@ check-uses-and-load-macros) (and (not load) (:*analyze-deps* bound-vars) (seq deps)) - (analyze-deps bound-vars ana-env lib deps (dissoc opts :macros-ns) + (analyze-deps bound-vars ana-env (:name ast) deps (dissoc opts :macros-ns) check-uses-and-load-macros) :else @@ -217,12 +218,12 @@ (cb ast)))) (defn analyze - ([env source cb] - (analyze env source nil cb)) - ([env source opts cb] + ([state source cb] + (analyze state source nil cb)) + ([state source opts cb] (analyze* - {:*compiler* env - :*cljs-ns* (or (:ns env) 'cljs.user) + {:*compiler* state + :*cljs-ns* 'cljs.user :*ns* (create-ns ana/*cljs-ns*) :*data-readers* tags/*cljs-data-readers*} source opts cb))) @@ -230,11 +231,12 @@ ;; ----------------------------------------------------------------------------- ;; Emit -(defn emit* [env bound-vars ast cb] - (cb (with-out-str (comp/emit ast)))) +(defn emit* [bound-vars ast cb] + (binding [env/*compiler* (:*compiler* bound-vars)] + (cb (with-out-str (comp/emit ast))))) -(defn emit [env ast cb] - (emit* env {} ast cb)) +(defn emit [state ast cb] + (emit* {:*compiler* state} ast cb)) ;; ----------------------------------------------------------------------------- ;; Eval @@ -252,10 +254,11 @@ (comp/emit (ana/analyze ana-env form nil opts)))))))) (defn eval - ([env form cb] (eval env form nil cb)) - ([env form opts cb] + ([state form cb] + (eval state form nil cb)) + ([state form opts cb] (eval* - {:*compiler* env + {:*compiler* state :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* @@ -288,11 +291,11 @@ (cb (.toString sb))))))))) (defn compile - ([env source cb] - (compile env source nil cb)) - ([env source opts cb] + ([state source cb] + (compile state source nil cb)) + ([state source opts cb] (compile* - {:*compiler* env + {:*compiler* state :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* @@ -330,11 +333,11 @@ (cb (*eval-fn* js-source)))))))))) (defn eval-str - ([env source cb] - (eval-str env source nil cb)) - ([env source opts cb] + ([state source cb] + (eval-str state source nil cb)) + ([state source opts cb] (eval-str* - {:*compiler* env + {:*compiler* state :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* From b740237c5fae63322e73bfe74cbaa10686e430c4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 21:03:17 -0400 Subject: [PATCH 1354/4033] *load-fn* must either be nil or a map of two keys :source & :lang, :lang may only be :clj or :js --- src/main/cljs/cljs/js.cljs | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d09aa976e..ab694d2b0 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -34,7 +34,13 @@ Whatever function *load-fn* is bound to will be passed a munged relative library path (a string) and a callback. It is up to the implementor to orrectly resolve a .cljs, .cljc, or .js file (the order must be respected). - The callback should be invoked with the source of the library (a string)." + The callback should be invoked with a map of two keys: + + :lang - the language, :clj or :js + :source - the source of the library (a string) + + If the library could not be resolved, the callback should be invoked with + nil." :dynamic true} *load-fn* (fn [name cb] @@ -80,10 +86,18 @@ (when-not (contains? @*loaded* name) (let [env (:*env* bound-vars)] (*load-fn* (ns->relpath name) - (fn [source] - (if source - (eval-str* bound-vars source opts - (fn [ret] (cb true))) + (fn [resolved] + (if resolved + (let [{:keys [lang source]} resolved] + (condp = lang + :clj (eval-str* bound-vars source opts + (fn [ret] (cb true))) + :js (do + (*eval-fn* source) + (cb true)) + (throw + (js/Error. + (str "Invalid :lang specified " lang ", only :clj or :js allowed"))))) (throw (ana/error env (ana/error-message :undeclared-ns @@ -145,11 +159,17 @@ (if (seq deps) (let [dep (first deps)] (*load-fn* (ns->relpath dep) - (fn [source] - (if-not (nil? source) - (analyze* bound-vars source opts - (fn [] - (analyze-deps bound-vars ana-env lib (next deps) opts cb))) + (fn [resolved] + (if resolved + (let [{:keys [lang source]} resolved] + (condp = lang + :clj (analyze* bound-vars source opts + (fn [] + (analyze-deps bound-vars ana-env lib (next deps) opts cb))) + :js (analyze-deps bound-vars ana-env lib (next deps) opts cb) + (throw + (js/Error. + (str "Invalid :lang specified " lang ", only :clj or :js allowed"))))) (throw (ana/error ana-env (ana/error-message :undeclared-ns From 2924265ac172ac95e9df362a88dc569cdfdbdb4e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 21:11:51 -0400 Subject: [PATCH 1355/4033] *load-fn* can only return a map or nil --- src/main/cljs/cljs/js.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ab694d2b0..69f79dfb0 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -87,6 +87,8 @@ (let [env (:*env* bound-vars)] (*load-fn* (ns->relpath name) (fn [resolved] + (assert (or (map? resolved) (nil? resolved)) + "*load-fn* may only return a map or nil") (if resolved (let [{:keys [lang source]} resolved] (condp = lang @@ -160,6 +162,8 @@ (let [dep (first deps)] (*load-fn* (ns->relpath dep) (fn [resolved] + (assert (or (map? resolved) (nil? resolved)) + "*load-fn* may only return a map or nil") (if resolved (let [{:keys [lang source]} resolved] (condp = lang From 72a16ed9c45276d7a8d272c5895e9ccd5b8b9713 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 21:34:34 -0400 Subject: [PATCH 1356/4033] add cljs.js/with-state macro --- src/main/cljs/cljs/js.clj | 15 +++++++++++++++ src/main/cljs/cljs/js.cljs | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/main/cljs/cljs/js.clj diff --git a/src/main/cljs/cljs/js.clj b/src/main/cljs/cljs/js.clj new file mode 100644 index 000000000..1dea99a90 --- /dev/null +++ b/src/main/cljs/cljs/js.clj @@ -0,0 +1,15 @@ +; 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.js + (:require [cljs.env.macros :as env])) + +(defmacro with-state + [state & body] + `(env/with-compiler-env ~state + ~@body)) \ No newline at end of file diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 69f79dfb0..ee74f7342 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -7,7 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.js - (:require-macros [cljs.env.macros :as env]) + (:require-macros [cljs.js] + [cljs.env.macros :as env]) (:require [clojure.string :as string] [cljs.env :as env] [cljs.analyzer :as ana] From 15b271c0f7c8c244940f00b21120f123dcdf26e8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 21:36:31 -0400 Subject: [PATCH 1357/4033] eval-str needs to load deps --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ee74f7342..3995a992d 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -349,7 +349,7 @@ ast (ana/analyze aenv form)] (.append sb (with-out-str (comp/emit ast))) (if (= :ns (:op ast)) - (ns-side-effects bound-vars aenv ast opts + (ns-side-effects true bound-vars aenv ast opts (fn [_] (compile-loop))) (recur))) (let [js-source (.toString sb)] From e3d0f938f5feda7ac31a5d484550f64ae0703bd4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 22:01:04 -0400 Subject: [PATCH 1358/4033] drop Closure require code gen when using eval-str --- src/main/cljs/cljs/js.cljs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 3995a992d..02d9b70b9 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -347,11 +347,15 @@ (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) ast (ana/analyze aenv form)] - (.append sb (with-out-str (comp/emit ast))) (if (= :ns (:op ast)) - (ns-side-effects true bound-vars aenv ast opts - (fn [_] (compile-loop))) - (recur))) + (do + (.append sb + (str "goog.provide(\"" (munge (:name ast)) "\");\n")) + (ns-side-effects true bound-vars aenv ast opts + (fn [_] (compile-loop)))) + (do + (.append sb (with-out-str (comp/emit ast))) + (recur)))) (let [js-source (.toString sb)] (when (:verbose opts) (debug-prn js-source)) From daaa7ef872b0ef08e55c38d632b8055edd17c12c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 22:24:35 -0400 Subject: [PATCH 1359/4033] make *eval-fn* *load-fn* more consistent, use out of bound-vars which survives async loads --- src/main/cljs/cljs/js.cljs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 02d9b70b9..128faf6f4 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -75,7 +75,8 @@ :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* - :*eval-fn* (or (:js-eval opts) js/eval)} + :*load-fn* (or (:load-fn opts) *load-fn*) + :*eval-fn* (or (:eval-fn opts) *eval-fn*)} name opts cb)) ([bound-vars name opts cb] (require bound-vars name nil opts cb)) @@ -86,7 +87,7 @@ (reset! *loaded* #{})) (when-not (contains? @*loaded* name) (let [env (:*env* bound-vars)] - (*load-fn* (ns->relpath name) + ((:*load-fn* bound-vars) (ns->relpath name) (fn [resolved] (assert (or (map? resolved) (nil? resolved)) "*load-fn* may only return a map or nil") @@ -96,7 +97,7 @@ :clj (eval-str* bound-vars source opts (fn [ret] (cb true))) :js (do - (*eval-fn* source) + ((:*eval-fn* bound-vars) source) (cb true)) (throw (js/Error. @@ -161,7 +162,7 @@ (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) (let [dep (first deps)] - (*load-fn* (ns->relpath dep) + ((:*load-fn* bound-vars) (ns->relpath dep) (fn [resolved] (assert (or (map? resolved) (nil? resolved)) "*load-fn* may only return a map or nil") @@ -287,7 +288,8 @@ :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* - :*eval-fn* (or (:js-eval opts) js/eval)} + :*load-fn* (or (:load-fn opts) *load-fn*) + :*eval-fn* (or (:eval-fn opts) *eval-fn*)} form opts cb))) ;; ----------------------------------------------------------------------------- @@ -326,7 +328,8 @@ :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) - :*eval-fn* (or (:js-eval opts) js/eval)} + :*load-fn* (or (:load-fn opts) *load-fn*) + :*eval-fn* (or (:eval-fn opts) *eval-fn*)} source opts cb))) ;; ----------------------------------------------------------------------------- @@ -372,5 +375,6 @@ :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) - :*eval-fn* (or (:js-eval opts) js/eval)} + :*load-fn* (or (:load-fn opts) *load-fn*) + :*eval-fn* (or (:eval-fn opts) *eval-fn*)} source opts cb))) \ No newline at end of file From 6bb85d580f88ccb4c30c0126c6ebc829e9390873 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 22:31:36 -0400 Subject: [PATCH 1360/4033] fix load-deps --- src/main/cljs/cljs/js.cljs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 128faf6f4..ceeeff174 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -140,11 +140,11 @@ (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) (let [dep (first deps)] - (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) - (contains? (:js-dependency-index compiler) (name dep))) - (require bound-vars lib opts - (fn [_] - (load-deps bound-vars lib (next deps) opts cb))))) + (when (:verbose opts) + (debug-prn "Loading" dep)) + (require bound-vars dep opts + (fn [_] + (load-deps bound-vars lib (next deps) opts cb)))) (cb)))))) (defn analyze-deps From fd1ebec03dcc23f43e18343a3769cba62e9d5199 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 22:44:16 -0400 Subject: [PATCH 1361/4033] fix load-deps bugs --- src/main/cljs/cljs/js.cljs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ceeeff174..0fd26a519 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -130,8 +130,10 @@ (defn load-deps ([bound-vars ana-env lib deps cb] - (analyze-deps bound-vars lib deps nil cb)) + (analyze-deps bound-vars ana-env lib deps nil cb)) ([bound-vars ana-env lib deps opts cb] + (when (:verbose opts) + (debug-prn "Loading dependencies")) (let [compiler @(:*compiler* bound-vars)] (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) update-in [:dep-path] conj lib)] @@ -144,7 +146,7 @@ (debug-prn "Loading" dep)) (require bound-vars dep opts (fn [_] - (load-deps bound-vars lib (next deps) opts cb)))) + (load-deps bound-vars ana-env lib (next deps) opts cb)))) (cb)))))) (defn analyze-deps @@ -232,7 +234,7 @@ (cb ast)))] (cond (and load (seq deps)) - (load-deps bound-vars name deps (dissoc opts :macros-ns) + (load-deps bound-vars ana-env (:name ast) deps (dissoc opts :macros-ns) check-uses-and-load-macros) (and (not load) (:*analyze-deps* bound-vars) (seq deps)) From 993ffa5beff24fdd9afa7a706ca5f22634a118ad Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Jul 2015 22:56:37 -0400 Subject: [PATCH 1362/4033] *load-fn* can also return :name key to identify the script --- src/main/cljs/cljs/js.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 0fd26a519..764709a0b 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -39,6 +39,7 @@ :lang - the language, :clj or :js :source - the source of the library (a string) + :name - optional, use to uniquely identify the script If the library could not be resolved, the callback should be invoked with nil." From 8e484959fbb5e6f8184b9183e096aaddc271005d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 06:25:36 -0400 Subject: [PATCH 1363/4033] CLJS-1355: cljs.js/*eval-fn* should take cljs.js/*load-fn*'s return value, a map CLJS-1356: cljs.js/*load-fn* should take the original library name --- src/main/cljs/cljs/js.cljs | 74 ++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 764709a0b..ccf32dcb7 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -31,17 +31,23 @@ (string/replace (ana/munge-path ns-sym) \. \/)) (defonce - ^{:doc "Each runtime environment provides a different way to load source. - Whatever function *load-fn* is bound to will be passed a munged relative - library path (a string) and a callback. It is up to the implementor to - orrectly resolve a .cljs, .cljc, or .js file (the order must be respected). - The callback should be invoked with a map of two keys: + ^{:doc "Each runtime environment provides a different way to load a library. + Whatever function *load-fn* is bound to will be passed two arguments - a + map and a callback function: The map should have the following keys: + + :name - the name of the library (a symbol) + :path - munged relative library path (a string) + + It is up to the implementor to correctly resolve the corresponding .cljs, + .cljc, or .js resource (the order must be respected). Upon resolution the + callback should be invoked with a map containing the following keys: :lang - the language, :clj or :js :source - the source of the library (a string) - :name - optional, use to uniquely identify the script + :name - optional, used to uniquely identify the script + :path - optional, relative URL style path representing location - If the library could not be resolved, the callback should be invoked with + If the resource could not be resolved, the callback should be invoked with nil." :dynamic true} *load-fn* @@ -50,13 +56,23 @@ (defonce ^{:doc "Each runtime environment provides various ways to eval JavaScript - source. Whatever function *eval-fn* is bound to will be passed source to eval - (a string). The result of eval will be passed back immediately to the caller." + source. Whatever function *eval-fn* is bound to will be passed a map + containing the following keys: + + :lang - the language, :clj or :js + :source - the source of the library (a string) + :name - optional, used to unique identify the script + :path - optional, relative URL style path representing location + + The result of evaluation should be the return value." :dynamic true} *eval-fn* (fn [js-source] (throw (js/Error. "No *eval-fn* set")))) +(defn js-eval [{:keys [source] :as resource}] + (js/eval source)) + (defn empty-state [] (env/default-compiler-env)) @@ -88,17 +104,17 @@ (reset! *loaded* #{})) (when-not (contains? @*loaded* name) (let [env (:*env* bound-vars)] - ((:*load-fn* bound-vars) (ns->relpath name) - (fn [resolved] - (assert (or (map? resolved) (nil? resolved)) + ((:*load-fn* bound-vars) {:name name :path (ns->relpath name)} + (fn [resource] + (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") - (if resolved - (let [{:keys [lang source]} resolved] + (if resource + (let [{:keys [lang source]} resource] (condp = lang :clj (eval-str* bound-vars source opts (fn [ret] (cb true))) :js (do - ((:*eval-fn* bound-vars) source) + ((:*eval-fn* bound-vars) resource) (cb true)) (throw (js/Error. @@ -165,12 +181,12 @@ (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) (let [dep (first deps)] - ((:*load-fn* bound-vars) (ns->relpath dep) - (fn [resolved] - (assert (or (map? resolved) (nil? resolved)) + ((:*load-fn* bound-vars) {:name dep :path (ns->relpath dep)} + (fn [resource] + (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") - (if resolved - (let [{:keys [lang source]} resolved] + (if resource + (let [{:keys [lang source]} resource] (condp = lang :clj (analyze* bound-vars source opts (fn [] @@ -279,8 +295,9 @@ (let [ana-env (assoc (ana/empty-env) :ns (ana/get-namespace ana/*cljs-ns*))] (cb (*eval-fn* - (with-out-str - (comp/emit (ana/analyze ana-env form nil opts)))))))) + {:lang :clj + :source (with-out-str + (comp/emit (ana/analyze ana-env form nil opts)))}))))) (defn eval ([state form cb] @@ -338,7 +355,7 @@ ;; ----------------------------------------------------------------------------- ;; Evaluate String -(defn eval-str* [bound-vars source opts cb] +(defn eval-str* [bound-vars source name opts cb] (let [rdr (rt/string-push-back-reader source) eof (js-obj) aenv (ana/empty-env) @@ -365,12 +382,17 @@ (let [js-source (.toString sb)] (when (:verbose opts) (debug-prn js-source)) - (cb (*eval-fn* js-source)))))))))) + (cb (*eval-fn* {:lang :clj + :name name + :path (ns->relpath name) + :source js-source})))))))))) (defn eval-str ([state source cb] (eval-str state source nil cb)) - ([state source opts cb] + ([state source name cb] + (eval-str state source name nil cb)) + ([state source opts name cb] (eval-str* {:*compiler* state :*cljs-ns* 'cljs.user @@ -380,4 +402,4 @@ :*load-macros* (or (:load-macros opts) true) :*load-fn* (or (:load-fn opts) *load-fn*) :*eval-fn* (or (:eval-fn opts) *eval-fn*)} - source opts cb))) \ No newline at end of file + source name opts cb))) \ No newline at end of file From feb780a667c915809d3d8a8c49d25fa447e976fe Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 06:33:58 -0400 Subject: [PATCH 1364/4033] should pass lib name to eval-str* from require --- src/main/cljs/cljs/js.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ccf32dcb7..2c4d4a717 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -109,9 +109,9 @@ (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") (if resource - (let [{:keys [lang source]} resource] + (let [{:keys [name lang source]} resource] (condp = lang - :clj (eval-str* bound-vars source opts + :clj (eval-str* bound-vars source name opts (fn [ret] (cb true))) :js (do ((:*eval-fn* bound-vars) resource) From 8f1918268e21fbe9cd742a0332f6ea93aba8156d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 06:52:35 -0400 Subject: [PATCH 1365/4033] add support to initialize empty-state, normally will want to populate with cljs.core analysis cache & macros analysis cache --- src/main/cljs/cljs/js.cljs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2c4d4a717..d2d05b8a5 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -73,8 +73,11 @@ (defn js-eval [{:keys [source] :as resource}] (js/eval source)) -(defn empty-state [] - (env/default-compiler-env)) +(defn empty-state + ([] + (env/default-compiler-env)) + ([init] + (doto (empty-state) (swap! init)))) ;; ----------------------------------------------------------------------------- ;; Analyze From 74e5cde55481229d25f4b37806f679fd4c4169c5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 07:19:33 -0400 Subject: [PATCH 1366/4033] source map support wip --- src/main/cljs/cljs/js.cljs | 60 +++++++++++++++------ src/main/cljs/cljs/source_map.cljs | 83 ++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d2d05b8a5..6c19b7952 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -15,7 +15,9 @@ [cljs.compiler :as comp] [cljs.tools.reader :as r] [cljs.tools.reader.reader-types :as rt] - [cljs.tagged-literals :as tags]) + [cljs.tagged-literals :as tags] + [goog.crypt.base64 :as base64] + [cljs.source-map :as sm]) (:import [goog.string StringBuffer])) (js/goog.require "cljs.core$macros") @@ -79,6 +81,12 @@ ([init] (doto (empty-state) (swap! init)))) +(defn sm-data [] + (atom + {:source-map (sorted-map) + :gen-col 0 + :gen-line 0})) + ;; ----------------------------------------------------------------------------- ;; Analyze @@ -212,10 +220,10 @@ (get-in reloads [k nsym]) (and (= nsym name) (:*reload-macros* bound-vars) :reload))] (if k - (require bound-vars nsym k opts + (require bound-vars nsym k (assoc opts :macros-ns true) (fn [] (load-macros bound-vars k (next macros) reload reloads opts cb))) - (require bound-vars nsym opts + (require bound-vars nsym (assoc opts :macros-ns true) (fn [] (load-macros bound-vars k (next macros) reload reloads opts cb)))) ;(intern-macros nsym k) @@ -324,11 +332,12 @@ aenv (ana/empty-env) sb (StringBuffer.)] ((fn compile-loop [] - (binding [env/*compiler* (:*compiler* bound-vars) - *eval-fn* (:*eval-fn* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) - r/*data-readers* (:*data-readers* bound-vars)] + (binding [env/*compiler* (:*compiler* bound-vars) + *eval-fn* (:*eval-fn* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars) + comp/*source-map-data* (:*sm-data* bound-vars)] (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) @@ -338,7 +347,23 @@ (ns-side-effects bound-vars aenv ast opts (fn [_] (compile-loop))) (recur))) - (cb (.toString sb))))))))) + (do + (when (:source-map opts) + (let [t (.valueOf (js/Date.)) + src (str "repl-" t ".cljs")] + (.append sb + "\n//# sourceURL=repl-" t ".js" + "\n//# sourceMappingURL=data:application/json;base64," + (base64/encodeString + (sm/encode + {src (:source-map @comp/*source-map-data*)} + {:lines (+ (:gen-line @comp/*source-map-data*) 3) + :file (str "repl-" t ".js") + :sources-content [(or (:source (meta form)) + ;; handle strings / primitives without metadata + (with-out-str (pr form)))]}) + true)))) + (cb (.toString sb)))))))))) (defn compile ([state source cb] @@ -352,7 +377,8 @@ :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*)} + :*eval-fn* (or (:eval-fn opts) *eval-fn*) + :*sm-data* (when (:source-map opts) (sm-data))} source opts cb))) ;; ----------------------------------------------------------------------------- @@ -364,11 +390,12 @@ aenv (ana/empty-env) sb (StringBuffer.)] ((fn compile-loop [] - (binding [env/*compiler* (:*compiler* bound-vars) - *eval-fn* (:*eval-fn* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) - r/*data-readers* (:*data-readers* bound-vars)] + (binding [env/*compiler* (:*compiler* bound-vars) + *eval-fn* (:*eval-fn* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars) + comp/*source-map-data* (:*sm-data* bound-vars)] (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) @@ -404,5 +431,6 @@ :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*)} + :*eval-fn* (or (:eval-fn opts) *eval-fn*) + :*sm-data* (when (:source-map opts) (sm-data))} source name opts cb))) \ No newline at end of file diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index 9adb0dadd..7a7d21f22 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -167,6 +167,89 @@ (recur (inc gline) (next lines) (assoc relseg 0 0) result)) result))))) +;; ----------------------------------------------------------------------------- +;; Encoding + +(defn lines->segs + "Take a nested sorted map encoding line and column information + for a file and return a vector of vectors of encoded segments. + Each vector represents a line, and the internal vectors are segments + representing the contents of the line." + [lines] + (let [relseg (atom [0 0 0 0 0])] + (reduce + (fn [segs cols] + (swap! relseg + (fn [[_ source line col name]] + [0 source line col name])) + (conj segs + (reduce + (fn [cols [gcol sidx line col name :as seg]] + (let [offset (map - seg @relseg)] + (swap! relseg + (fn [[_ _ _ _ lname]] + [gcol sidx line col (or name lname)])) + (conj cols (base64-vlq/encode offset)))) + [] cols))) + [] lines))) + +(defn encode + "Take an internal source map representation represented as nested + sorted maps of file, line, column and return a source map v3 JSON + string." + [m opts] + (let [lines (atom [[]]) + names->idx (atom {}) + name-idx (atom 0) + preamble-lines (take (or (:preamble-line-count opts) 0) (repeat [])) + info->segv (fn [info source-idx line col] + (let [segv [(:gcol info) source-idx line col]] + (if-let [name (:name info)] + (let [idx (if-let [idx (get @names->idx name)] + idx + (let [cidx @name-idx] + (swap! names->idx assoc name cidx) + (swap! name-idx inc) + cidx))] + (conj segv idx)) + segv))) + encode-cols (fn [infos source-idx line col] + (doseq [info infos] + (let [segv (info->segv info source-idx line col) + gline (:gline info) + lc (count @lines)] + (if (> gline (dec lc)) + (swap! lines + (fn [lines] + (conj (into lines (repeat (dec (- gline (dec lc))) [])) [segv]))) + (swap! lines + (fn [lines] + (update-in lines [gline] conj segv)))))))] + (doseq [[source-idx [_ lines]] (map-indexed (fn [i v] [i v]) m)] + (doseq [[line cols] lines] + (doseq [[col infos] cols] + (encode-cols infos source-idx line col)))) + (let [source-map-file-contents + (cond-> {"version" 3 + "file" (:file opts) + "sources" (let [paths (keys m) + f (comp + (if (true? (:source-map-timestamp opts)) + #(str % "?rel=" (.valueOf (js/Date.))) + identity) + #(last (string/split % #"/")))] + (->> paths (map f) (into []))) + "lineCount" (:lines opts) + "mappings" (->> (lines->segs (concat preamble-lines @lines)) + (map #(string/join "," %)) + (string/join ";")) + "names" (into [] + (map (set/map-invert @names->idx) + (range (count @names->idx))))} + (:sources-content opts) + (assoc "sourcesContent" (:sources-content opts)))] + (.stringify js/JSON source-map-file-contents)))) + ;; ----------------------------------------------------------------------------- ;; Merging From f0538f2c158afe1bc6d68aa56d89c080c593b87b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 07:23:51 -0400 Subject: [PATCH 1367/4033] cleanup cljs source map file --- src/main/cljs/cljs/source_map.cljs | 64 +++++++++++++++--------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index 7a7d21f22..aa79e4516 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -7,7 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.source-map - (:require [clojure.string :as string] + (:require [goog.object :as gobj] + [clojure.string :as string] [clojure.set :as set] [cljs.source-map.base64-vlq :as base64-vlq])) @@ -55,11 +56,11 @@ [seg source-map] (let [[gcol source line col name] seg] {:gcol gcol - :source (aget (.split (aget source-map "sources" source) "?") 0) + :source (aget (.split (gobj/get source-map "sources" source) "?") 0) :line line :col col :name (when-let [name (-> seg meta :name)] - (aget source-map "names" name))})) + (gobj/get source-map "names" name))})) (defn seg-combine "Combine a source map segment vector and a relative @@ -100,38 +101,39 @@ mapping original ClojureScript source locations to the generated JavaScript." ([source-map] - (decode-reverse (aget source-map "mappings") source-map)) + (decode-reverse + (gobj/get source-map "mappings") source-map)) ([mappings source-map] - (let [sources (aget source-map "sources") - relseg-init [0 0 0 0 0] - lines (seq (string/split mappings #";"))] - (loop [gline 0 - lines lines - relseg relseg-init - result (sorted-map-by (source-compare sources))] - (if lines - (let [line (first lines) + (let [sources (gobj/get source-map "sources") + relseg-init [0 0 0 0 0] + lines (seq (string/split mappings #";"))] + (loop [gline 0 + lines lines + relseg relseg-init + result (sorted-map-by (source-compare sources))] + (if lines + (let [line (first lines) + [result relseg] + (if (string/blank? line) [result relseg] - (if (string/blank? line) - [result relseg] - (let [segs (seq (string/split line #","))] - (loop [segs segs relseg relseg result result] - (if segs - (let [seg (first segs) - nrelseg (seg-combine (base64-vlq/decode seg) relseg)] - (recur (next segs) nrelseg - (update-reverse-result result (seg->map nrelseg source-map) gline))) - [result relseg]))))] - (recur (inc gline) (next lines) (assoc relseg 0 0) result)) - result))))) + (let [segs (seq (string/split line #","))] + (loop [segs segs relseg relseg result result] + (if segs + (let [seg (first segs) + nrelseg (seg-combine (base64-vlq/decode seg) relseg)] + (recur (next segs) nrelseg + (update-reverse-result result (seg->map nrelseg source-map) gline))) + [result relseg]))))] + (recur (inc gline) (next lines) (assoc relseg 0 0) result)) + result))))) (defn update-result "Helper for decode. Take a source map and update it based on a segment map." [result segmap gline] (let [{:keys [gcol source line col name]} segmap - d {:line line - :col col + d {:line line + :col col :source source} d (if name (assoc d :name name) d)] (update-in result [gline] @@ -145,11 +147,11 @@ generated JavaScript source locations to the original ClojureScript." ([source-map] - (decode (aget source-map "mappings") source-map)) + (decode (gobj/get source-map "mappings") source-map)) ([mappings source-map] - (let [sources (aget source-map "sources") + (let [sources (gobj/get source-map "sources") relseg-init [0 0 0 0 0] - lines (seq (string/split mappings #";"))] + lines (seq (string/split mappings #";"))] (loop [gline 0 lines lines relseg relseg-init result {}] (if lines (let [line (first lines) @@ -159,7 +161,7 @@ (let [segs (seq (string/split line #","))] (loop [segs segs relseg relseg result result] (if segs - (let [seg (first segs) + (let [seg (first segs) nrelseg (seg-combine (base64-vlq/decode seg) relseg)] (recur (next segs) nrelseg (update-result result (seg->map nrelseg source-map) gline))) From 4242f6336f71eddf0ed6e7c2a2d3af56b797a181 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 07:32:07 -0400 Subject: [PATCH 1368/4033] fix inline source mapping --- src/main/cljs/cljs/js.cljs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 6c19b7952..be72dd458 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -352,17 +352,17 @@ (let [t (.valueOf (js/Date.)) src (str "repl-" t ".cljs")] (.append sb - "\n//# sourceURL=repl-" t ".js" - "\n//# sourceMappingURL=data:application/json;base64," - (base64/encodeString - (sm/encode - {src (:source-map @comp/*source-map-data*)} - {:lines (+ (:gen-line @comp/*source-map-data*) 3) - :file (str "repl-" t ".js") - :sources-content [(or (:source (meta form)) - ;; handle strings / primitives without metadata - (with-out-str (pr form)))]}) - true)))) + (str "\n//# sourceURL=repl-" t ".js" + "\n//# sourceMappingURL=data:application/json;base64," + (base64/encodeString + (sm/encode + {src (:source-map @comp/*source-map-data*)} + {:lines (+ (:gen-line @comp/*source-map-data*) 3) + :file (str "repl-" t ".js") + :sources-content [(or (:source (meta form)) + ;; handle strings / primitives without metadata + (with-out-str (pr form)))]}) + true))))) (cb (.toString sb)))))))))) (defn compile From ba1af75f186ce265f97ea3a255f2139144f7f8be Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 07:33:54 -0400 Subject: [PATCH 1369/4033] more tweaks --- src/main/cljs/cljs/js.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index be72dd458..d312e9ce4 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -350,14 +350,15 @@ (do (when (:source-map opts) (let [t (.valueOf (js/Date.)) - src (str "repl-" t ".cljs")] + src (str "repl-" t ".cljs") + smd @comp/*source-map-data*] (.append sb (str "\n//# sourceURL=repl-" t ".js" "\n//# sourceMappingURL=data:application/json;base64," (base64/encodeString (sm/encode - {src (:source-map @comp/*source-map-data*)} - {:lines (+ (:gen-line @comp/*source-map-data*) 3) + {src (:source-map smd)} + {:lines (+ (:gen-line smd) 3) :file (str "repl-" t ".js") :sources-content [(or (:source (meta form)) ;; handle strings / primitives without metadata From 1111c36e0ac919a4a98adf222759048e782c0f92 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 07:49:21 -0400 Subject: [PATCH 1370/4033] compile has the source and not using source-logging-push-back-reader --- src/main/cljs/cljs/js.cljs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d312e9ce4..d6650319e 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -360,9 +360,7 @@ {src (:source-map smd)} {:lines (+ (:gen-line smd) 3) :file (str "repl-" t ".js") - :sources-content [(or (:source (meta form)) - ;; handle strings / primitives without metadata - (with-out-str (pr form)))]}) + :sources-content [source]}) true))))) (cb (.toString sb)))))))))) From 9d0fb707b262e2a6514746872ca32cc8c2a4ea52 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 08:29:40 -0400 Subject: [PATCH 1371/4033] more refactoring of source maps in compile --- src/main/cljs/cljs/js.cljs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d6650319e..28cee3419 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -349,19 +349,17 @@ (recur))) (do (when (:source-map opts) - (let [t (.valueOf (js/Date.)) - src (str "repl-" t ".cljs") - smd @comp/*source-map-data*] + (let [t (.valueOf (js/Date.)) + src (str "cljs-" t ".cljs") + file (str "cljs-" t ".js") + smd @comp/*source-map-data* + json (sm/encode {src (:source-map smd)} + {:lines (+ (:gen-line smd) 3) + :file file :sources-content [source]})] (.append sb (str "\n//# sourceURL=repl-" t ".js" "\n//# sourceMappingURL=data:application/json;base64," - (base64/encodeString - (sm/encode - {src (:source-map smd)} - {:lines (+ (:gen-line smd) 3) - :file (str "repl-" t ".js") - :sources-content [source]}) - true))))) + (base64/encodeString json true))))) (cb (.toString sb)))))))))) (defn compile From 45b1ce942ca664b904653f9c82d0bd21f2823fcc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 08:35:20 -0400 Subject: [PATCH 1372/4033] in :verbose, print the source map JSON --- src/main/cljs/cljs/js.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 28cee3419..e6d705387 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -356,6 +356,7 @@ json (sm/encode {src (:source-map smd)} {:lines (+ (:gen-line smd) 3) :file file :sources-content [source]})] + (when (:verbose opts) (debug-prn json)) (.append sb (str "\n//# sourceURL=repl-" t ".js" "\n//# sourceMappingURL=data:application/json;base64," From 712df3686ac5fa18c69ebfc4483b78dd5e7ed0dd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 08:55:08 -0400 Subject: [PATCH 1373/4033] cljs.source-map encode needs to emit JS values --- src/main/cljs/cljs/source_map.cljs | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index aa79e4516..971753626 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -232,24 +232,24 @@ (doseq [[col infos] cols] (encode-cols infos source-idx line col)))) (let [source-map-file-contents - (cond-> {"version" 3 - "file" (:file opts) - "sources" (let [paths (keys m) - f (comp - (if (true? (:source-map-timestamp opts)) - #(str % "?rel=" (.valueOf (js/Date.))) - identity) - #(last (string/split % #"/")))] - (->> paths (map f) (into []))) - "lineCount" (:lines opts) - "mappings" (->> (lines->segs (concat preamble-lines @lines)) - (map #(string/join "," %)) - (string/join ";")) - "names" (into [] - (map (set/map-invert @names->idx) - (range (count @names->idx))))} + (cond-> #js {"version" 3 + "file" (:file opts) + "sources" (let [paths (keys m) + f (comp + (if (true? (:source-map-timestamp opts)) + #(str % "?rel=" (.valueOf (js/Date.))) + identity) + #(last (string/split % #"/")))] + (->> paths (map f) (into-array))) + "lineCount" (:lines opts) + "mappings" (->> (lines->segs (concat preamble-lines @lines)) + (map #(string/join "," %)) + (string/join ";")) + "names" (into-array + (map (set/map-invert @names->idx) + (range (count @names->idx))))} (:sources-content opts) - (assoc "sourcesContent" (:sources-content opts)))] + (gobj/set "sourcesContent" (:sources-content opts)))] (.stringify js/JSON source-map-file-contents)))) ;; ----------------------------------------------------------------------------- From b01c55c5e8e6e1bf52cb61e0fd0ed69e95707c1a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 09:07:15 -0400 Subject: [PATCH 1374/4033] missing doto --- src/main/cljs/cljs/source_map.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index 971753626..e1794de66 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -249,7 +249,7 @@ (map (set/map-invert @names->idx) (range (count @names->idx))))} (:sources-content opts) - (gobj/set "sourcesContent" (:sources-content opts)))] + (doto (gobj/set "sourcesContent" (:sources-content opts))))] (.stringify js/JSON source-map-file-contents)))) ;; ----------------------------------------------------------------------------- From c71df2bc6c676cfe10b61f6dbc9fd7f65a7fc539 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 09:29:36 -0400 Subject: [PATCH 1375/4033] need to convert :source-content into JS array --- src/main/cljs/cljs/source_map.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index e1794de66..a0e8d7cb9 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -249,7 +249,7 @@ (map (set/map-invert @names->idx) (range (count @names->idx))))} (:sources-content opts) - (doto (gobj/set "sourcesContent" (:sources-content opts))))] + (doto (gobj/set "sourcesContent" (into-array (:sources-content opts)))))] (.stringify js/JSON source-map-file-contents)))) ;; ----------------------------------------------------------------------------- From 2dc1f83be18216d156b62f014f6dd32c5f5b064a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 09:52:32 -0400 Subject: [PATCH 1376/4033] need to switch to indexing-push-back-reader for source mapping make the interface to analyze, compile, eval-str the same, all should take optional source name --- src/main/cljs/cljs/js.cljs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index e6d705387..7d53963fa 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -137,8 +137,8 @@ (declare ns-side-effects analyze-deps) -(defn analyze* [bound-vars source opts cb] - (let [rdr (rt/string-push-back-reader source) +(defn analyze* [bound-vars source name opts cb] + (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) aenv (ana/empty-env)] ((fn analyze-loop [] @@ -197,9 +197,9 @@ (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") (if resource - (let [{:keys [lang source]} resource] + (let [{:keys [name lang source]} resource] (condp = lang - :clj (analyze* bound-vars source opts + :clj (analyze* bound-vars source name opts (fn [] (analyze-deps bound-vars ana-env lib (next deps) opts cb))) :js (analyze-deps bound-vars ana-env lib (next deps) opts cb) @@ -276,13 +276,15 @@ (defn analyze ([state source cb] (analyze state source nil cb)) - ([state source opts cb] + ([state source name cb] + (analyze state source name nil cb)) + ([state source name opts cb] (analyze* {:*compiler* state :*cljs-ns* 'cljs.user :*ns* (create-ns ana/*cljs-ns*) :*data-readers* tags/*cljs-data-readers*} - source opts cb))) + source name opts cb))) ;; ----------------------------------------------------------------------------- ;; Emit @@ -326,8 +328,8 @@ ;; ----------------------------------------------------------------------------- ;; Compile -(defn compile* [bound-vars source opts cb] - (let [rdr (rt/string-push-back-reader source) +(defn compile* [bound-vars source name opts cb] + (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) aenv (ana/empty-env) sb (StringBuffer.)] @@ -366,7 +368,9 @@ (defn compile ([state source cb] (compile state source nil cb)) - ([state source opts cb] + ([state source name cb] + (compile state source name nil cb)) + ([state source name opts cb] (compile* {:*compiler* state :*cljs-ns* 'cljs.user @@ -377,13 +381,13 @@ :*load-fn* (or (:load-fn opts) *load-fn*) :*eval-fn* (or (:eval-fn opts) *eval-fn*) :*sm-data* (when (:source-map opts) (sm-data))} - source opts cb))) + source name opts cb))) ;; ----------------------------------------------------------------------------- ;; Evaluate String (defn eval-str* [bound-vars source name opts cb] - (let [rdr (rt/string-push-back-reader source) + (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) aenv (ana/empty-env) sb (StringBuffer.)] From fa38b61b10eb6b8f2cef6588d81d71ad2f707137 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 10:12:03 -0400 Subject: [PATCH 1377/4033] typo --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 7d53963fa..ea42a8b0c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -360,7 +360,7 @@ :file file :sources-content [source]})] (when (:verbose opts) (debug-prn json)) (.append sb - (str "\n//# sourceURL=repl-" t ".js" + (str "\n//# sourceURL=" file "\n//# sourceMappingURL=data:application/json;base64," (base64/encodeString json true))))) (cb (.toString sb)))))))))) From d194082eb4837fa3fe2897433dbbe6da19509e82 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jul 2015 10:24:17 -0400 Subject: [PATCH 1378/4033] docstrings wip --- src/main/cljs/cljs/js.cljs | 89 +++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ea42a8b0c..e66ef1b32 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -72,10 +72,14 @@ (fn [js-source] (throw (js/Error. "No *eval-fn* set")))) -(defn js-eval [{:keys [source] :as resource}] +(defn js-eval + "A default JavaScript evaluation function." + [{:keys [source] :as resource}] (js/eval source)) (defn empty-state + "Construct an empty compiler state. Required to invoke analyze, compile, + eval and eval-str." ([] (env/default-compiler-env)) ([init] @@ -178,9 +182,6 @@ (cb)))))) (defn analyze-deps - "Given a lib, a namespace, deps, its dependencies, env, an analysis environment - and opts, compiler options - analyze all of the dependencies. Required to - correctly analyze usage of other namespaces." ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars ana-env lib deps nil cb)) ([bound-vars ana-env lib deps opts cb] @@ -274,6 +275,27 @@ (cb ast)))) (defn analyze + "Analyze ClojureScript source. The compiler state will be populated with + the results of analyzes. The parameters: + + state (atom) + the compiler state + + source (string) + the ClojureScript source + + name (symbol) + optional, the name of the source + + opts (map) + compilation options. + + :eval-fn - the eval function to invoke, see *eval-fn* + :load-fn - library resolution function, see *load-fn* + + cb (function) + callback, will be invoked with the evalution result." + ([state source cb] (analyze state source nil cb)) ([state source name cb] @@ -283,7 +305,9 @@ {:*compiler* state :*cljs-ns* 'cljs.user :*ns* (create-ns ana/*cljs-ns*) - :*data-readers* tags/*cljs-data-readers*} + :*data-readers* tags/*cljs-data-readers* + :*load-fn* (or (:load-fn opts) *load-fn*) + :*eval-fn* (or (:eval-fn opts) *eval-fn*)} source name opts cb))) ;; ----------------------------------------------------------------------------- @@ -313,6 +337,22 @@ (comp/emit (ana/analyze ana-env form nil opts)))}))))) (defn eval + "Evaluate a single ClojureScript form. The parameters: + + state (atom) + the compiler state + + form (s-expr) + the ClojureScript source + + opts (map) + compilation options. + + :eval-fn - the eval function to invoke, see *eval-fn* + :load-fn - library resolution function, see *load-fn* + + cb (function) + callback, will be invoked with the evalution result." ([state form cb] (eval state form nil cb)) ([state form opts cb] @@ -366,6 +406,25 @@ (cb (.toString sb)))))))))) (defn compile + "Compile ClojureScript source into JavaScript. The parameters: + + state (atom) + the compiler state + + source (string) + the ClojureScript source + + name (symbol) + optional, the name of the source + + opts (map) + compilation options. + + :load-fn - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + + cb (function) + callback, will be invoked with the compilation result (string)." ([state source cb] (compile state source nil cb)) ([state source name cb] @@ -420,6 +479,26 @@ :source js-source})))))))))) (defn eval-str + "Evalute ClojureScript source given as a string. The parameters: + + state (atom) + the compiler state + + source (string) + the ClojureScript source + + name (symbol) + optional, the name of the source + + opts (map) + compilation options. + + :eval-fn - eval function to invoke, see *eval-fn* + :load-fn - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + + cb (function) + callback, will be invoked with result of evalution" ([state source cb] (eval-str state source nil cb)) ([state source name cb] From 6235833cb398510bd62bfc33da1345dc6316259e Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jul 2015 12:17:39 -0400 Subject: [PATCH 1379/4033] also store the source map in compiler state --- src/main/cljs/cljs/js.cljs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index e66ef1b32..de265235e 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -392,13 +392,18 @@ (do (when (:source-map opts) (let [t (.valueOf (js/Date.)) - src (str "cljs-" t ".cljs") - file (str "cljs-" t ".js") + smn (if name + (munge name) + (str "cljs-" t)) smd @comp/*source-map-data* + src (str smn ".cljs") + file (str smn ".js") json (sm/encode {src (:source-map smd)} {:lines (+ (:gen-line smd) 3) :file file :sources-content [source]})] (when (:verbose opts) (debug-prn json)) + (swap! env/*compiler* assoc-in + [:source-maps name] (:source-map smd)) (.append sb (str "\n//# sourceURL=" file "\n//# sourceMappingURL=data:application/json;base64," From 5284c2d7b7cfe7b83f225c1e7e16db24a8788aa0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jul 2015 18:27:58 -0400 Subject: [PATCH 1380/4033] CLJS-1357: In cljs.js/eval-str accept a :context option in support of REPLs CLJS-1358: In cljs.js/eval-str accept an option to turn on :def-emits-var typos --- src/main/cljs/cljs/js.cljs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index de265235e..9399e3e24 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -152,7 +152,9 @@ r/*data-readers* (:*data-readers* bound-vars)] (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) - (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true)) ast (ana/analyze aenv form)] (if (= :ns (:op ast)) (ns-side-effects bound-vars aenv ast opts @@ -247,16 +249,16 @@ (ana/check-uses uses env)) (if (:*load-macros* bound-vars) (do - (when (:verobse opts) + (when (:verbose opts) (debug-prn "Loading :use-macros")) (load-macros bound-vars :use-macros use-macros reload reloads opts (fn [] - (when (:verobse opts) + (when (:verbose opts) (debug-prn "Loading :require-macros")) (load-macros bound-vars :require-macros require-macros reloads reloads opts (fn [] (when (seq use-macros) - (when (:verobse opts) + (when (:verbose opts) (debug-prn "Checking :use-macros")) (ana/check-use-macros use-macros env)) (cb ast)))))) @@ -323,18 +325,22 @@ ;; ----------------------------------------------------------------------------- ;; Eval +;; TODO: ns form handling, source mapping + (defn eval* [bound-vars form opts cb] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars)] - (let [ana-env (assoc (ana/empty-env) - :ns (ana/get-namespace ana/*cljs-ns*))] + (let [aenv (ana/empty-env) + aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true))] (cb (*eval-fn* {:lang :clj :source (with-out-str - (comp/emit (ana/analyze ana-env form nil opts)))}))))) + (comp/emit (ana/analyze aenv form nil opts)))}))))) (defn eval "Evaluate a single ClojureScript form. The parameters: @@ -382,7 +388,9 @@ comp/*source-map-data* (:*sm-data* bound-vars)] (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) - (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true)) ast (ana/analyze aenv form)] (.append sb (with-out-str (comp/emit ast))) (if (= :ns (:op ast)) @@ -464,7 +472,9 @@ comp/*source-map-data* (:*sm-data* bound-vars)] (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) - (let [aenv (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true)) ast (ana/analyze aenv form)] (if (= :ns (:op ast)) (do From d330004f9e6f1c4a2b8e22e66e7342cda7754913 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jul 2015 20:40:47 -0400 Subject: [PATCH 1381/4033] fix ns tracking logic of eval-str --- src/main/cljs/cljs/js.cljs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 9399e3e24..d117c6f00 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -463,35 +463,37 @@ eof (js-obj) aenv (ana/empty-env) sb (StringBuffer.)] - ((fn compile-loop [] + ((fn compile-loop [ns] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) + ana/*cljs-ns* ns + *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] (if-not (identical? eof form) - (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ns)) (:context opts) (assoc :context (:context opts)) (:def-emits-var opts) (assoc :def-emits-var true)) - ast (ana/analyze aenv form)] + ast (ana/analyze aenv form) + ns' ana/*cljs-ns*] (if (= :ns (:op ast)) (do (.append sb (str "goog.provide(\"" (munge (:name ast)) "\");\n")) (ns-side-effects true bound-vars aenv ast opts - (fn [_] (compile-loop)))) + (fn [_] (compile-loop ns')))) (do (.append sb (with-out-str (comp/emit ast))) - (recur)))) + (recur ns')))) (let [js-source (.toString sb)] (when (:verbose opts) (debug-prn js-source)) (cb (*eval-fn* {:lang :clj :name name :path (ns->relpath name) - :source js-source})))))))))) + :source js-source}))))))) + (:*cljs-ns* bound-vars)))) (defn eval-str "Evalute ClojureScript source given as a string. The parameters: From b0f4115aaea268a8ebe5ad17a047066d35621874 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jul 2015 20:49:00 -0400 Subject: [PATCH 1382/4033] eval-str should return current ns & return value --- src/main/cljs/cljs/js.cljs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d117c6f00..82af4d8b6 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -486,13 +486,14 @@ (do (.append sb (with-out-str (comp/emit ast))) (recur ns')))) - (let [js-source (.toString sb)] + (let [js-source (.toString sb) + evalm {:lang :clj + :name name + :path (ns->relpath name) + :source js-source}] (when (:verbose opts) (debug-prn js-source)) - (cb (*eval-fn* {:lang :clj - :name name - :path (ns->relpath name) - :source js-source}))))))) + (cb {:ns ns :value (*eval-fn* evalm)})))))) (:*cljs-ns* bound-vars)))) (defn eval-str From c15b42abae1a0ac91ef715c1b1c45369f479a30e Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jul 2015 21:37:48 -0400 Subject: [PATCH 1383/4033] add source maps to eval-str --- src/main/cljs/cljs/js.cljs | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 82af4d8b6..5f95a7a4a 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -91,6 +91,24 @@ :gen-col 0 :gen-line 0})) +(defn append-source-map [state name source sb sm-data opts] + (let [t (.valueOf (js/Date.)) + smn (if name + (munge name) + (str "cljs-" t)) + src (str smn ".cljs") + file (str smn ".js") + json (sm/encode {src (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 3) + :file file :sources-content [source]})] + (when (:verbose opts) (debug-prn json)) + (swap! state assoc-in + [:source-maps name] (:source-map sm-data)) + (.append sb + (str "\n//# sourceURL=" file + "\n//# sourceMappingURL=data:application/json;base64," + (base64/encodeString json true))))) + ;; ----------------------------------------------------------------------------- ;; Analyze @@ -398,24 +416,8 @@ (fn [_] (compile-loop))) (recur))) (do - (when (:source-map opts) - (let [t (.valueOf (js/Date.)) - smn (if name - (munge name) - (str "cljs-" t)) - smd @comp/*source-map-data* - src (str smn ".cljs") - file (str smn ".js") - json (sm/encode {src (:source-map smd)} - {:lines (+ (:gen-line smd) 3) - :file file :sources-content [source]})] - (when (:verbose opts) (debug-prn json)) - (swap! env/*compiler* assoc-in - [:source-maps name] (:source-map smd)) - (.append sb - (str "\n//# sourceURL=" file - "\n//# sourceMappingURL=data:application/json;base64," - (base64/encodeString json true))))) + (append-source-map env/*compiler* + name source sb @comp/*source-map-data* opts) (cb (.toString sb)))))))))) (defn compile @@ -493,6 +495,8 @@ :source js-source}] (when (:verbose opts) (debug-prn js-source)) + (append-source-map env/*compiler* + name source sb @comp/*source-map-data* opts) (cb {:ns ns :value (*eval-fn* evalm)})))))) (:*cljs-ns* bound-vars)))) From 99f78f48eebb9e3c6fb5666286c9b915427afd3f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 22 Jul 2015 08:40:14 -0400 Subject: [PATCH 1384/4033] don't append source maps if :source-map not set, cleanup load-macros --- src/main/cljs/cljs/js.cljs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 5f95a7a4a..2643b9b3d 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -240,13 +240,9 @@ (let [k (or (k reload) (get-in reloads [k nsym]) (and (= nsym name) (:*reload-macros* bound-vars) :reload))] - (if k - (require bound-vars nsym k (assoc opts :macros-ns true) - (fn [] - (load-macros bound-vars k (next macros) reload reloads opts cb))) - (require bound-vars nsym (assoc opts :macros-ns true) - (fn [] - (load-macros bound-vars k (next macros) reload reloads opts cb)))) + (require bound-vars nsym k (assoc opts :macros-ns true) + (fn [] + (load-macros bound-vars k (next macros) reload reloads opts cb))) ;(intern-macros nsym k) )) (cb))) @@ -416,8 +412,9 @@ (fn [_] (compile-loop))) (recur))) (do - (append-source-map env/*compiler* - name source sb @comp/*source-map-data* opts) + (when (:source-map opts) + (append-source-map env/*compiler* + name source sb @comp/*source-map-data* opts)) (cb (.toString sb)))))))))) (defn compile @@ -495,8 +492,9 @@ :source js-source}] (when (:verbose opts) (debug-prn js-source)) - (append-source-map env/*compiler* - name source sb @comp/*source-map-data* opts) + (when (:source-map opts) + (append-source-map env/*compiler* + name source sb @comp/*source-map-data* opts)) (cb {:ns ns :value (*eval-fn* evalm)})))))) (:*cljs-ns* bound-vars)))) From 54b3b72833a65304e54e09e07e8e66aba76aa582 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 22 Jul 2015 09:11:14 -0400 Subject: [PATCH 1385/4033] all serious fns must re-establish cljs.user ns and reset source-map atom (only ever worked at compile file level) --- src/main/cljs/cljs/js.cljs | 82 ++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2643b9b3d..ead65be08 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -122,8 +122,6 @@ ([name opts cb] (require {:*compiler* (env/default-compiler-env) - :*cljs-ns* 'cljs.user - :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* :*load-fn* (or (:load-fn opts) *load-fn*) :*eval-fn* (or (:eval-fn opts) *eval-fn*)} @@ -159,27 +157,6 @@ (declare ns-side-effects analyze-deps) -(defn analyze* [bound-vars source name opts cb] - (let [rdr (rt/indexing-push-back-reader source 1 name) - eof (js-obj) - aenv (ana/empty-env)] - ((fn analyze-loop [] - (binding [env/*compiler* (:*compiler* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) - r/*data-readers* (:*data-readers* bound-vars)] - (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] - (if-not (identical? eof form) - (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - (:context opts) (assoc :context (:context opts)) - (:def-emits-var opts) (assoc :def-emits-var true)) - ast (ana/analyze aenv form)] - (if (= :ns (:op ast)) - (ns-side-effects bound-vars aenv ast opts - (fn [_] (analyze-loop))) - (recur))) - (cb)))))))) - (defn load-deps ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars ana-env lib deps nil cb)) @@ -290,6 +267,32 @@ (check-uses-and-load-macros)))) (cb ast)))) +(defn analyze* [bound-vars source name opts cb] + (let [rdr (rt/indexing-push-back-reader source 1 name) + eof (js-obj) + aenv (ana/empty-env) + bound-vars (cond-> (merge bound-vars + {:*cljs-ns* 'cljs.user + :*ns* (create-ns ana/*cljs-ns*)}) + (:source-map opts) (assoc :*sm-data* (sm-data)))] + ((fn analyze-loop [] + (binding [env/*compiler* (:*compiler* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars) + comp/*source-map-data* (:*sm-data* bound-vars)] + (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] + (if-not (identical? eof form) + (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true)) + ast (ana/analyze aenv form)] + (if (= :ns (:op ast)) + (ns-side-effects bound-vars aenv ast opts + (fn [_] (analyze-loop))) + (recur))) + (cb)))))))) + (defn analyze "Analyze ClojureScript source. The compiler state will be populated with the results of analyzes. The parameters: @@ -319,8 +322,6 @@ ([state source name opts cb] (analyze* {:*compiler* state - :*cljs-ns* 'cljs.user - :*ns* (create-ns ana/*cljs-ns*) :*data-readers* tags/*cljs-data-readers* :*load-fn* (or (:load-fn opts) *load-fn*) :*eval-fn* (or (:eval-fn opts) *eval-fn*)} @@ -389,10 +390,14 @@ ;; Compile (defn compile* [bound-vars source name opts cb] - (let [rdr (rt/indexing-push-back-reader source 1 name) - eof (js-obj) - aenv (ana/empty-env) - sb (StringBuffer.)] + (let [rdr (rt/indexing-push-back-reader source 1 name) + eof (js-obj) + aenv (ana/empty-env) + sb (StringBuffer.) + bound-vars (cond-> (merge bound-vars + {:*cljs-ns* 'cljs.user + :*ns* (create-ns ana/*cljs-ns*)}) + (:source-map opts) (assoc :*sm-data* (sm-data)))] ((fn compile-loop [] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) @@ -444,8 +449,6 @@ ([state source name opts cb] (compile* {:*compiler* state - :*cljs-ns* 'cljs.user - :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) @@ -458,10 +461,14 @@ ;; Evaluate String (defn eval-str* [bound-vars source name opts cb] - (let [rdr (rt/indexing-push-back-reader source 1 name) - eof (js-obj) - aenv (ana/empty-env) - sb (StringBuffer.)] + (let [rdr (rt/indexing-push-back-reader source 1 name) + eof (js-obj) + aenv (ana/empty-env) + sb (StringBuffer.) + bound-vars (cond-> (merge bound-vars + {:*cljs-ns* 'cljs.user + :*ns* (create-ns ana/*cljs-ns*)}) + (:source-map opts) (assoc :*sm-data* (sm-data)))] ((fn compile-loop [ns] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) @@ -526,12 +533,9 @@ ([state source opts name cb] (eval-str* {:*compiler* state - :*cljs-ns* 'cljs.user - :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*) - :*sm-data* (when (:source-map opts) (sm-data))} + :*eval-fn* (or (:eval-fn opts) *eval-fn*)} source name opts cb))) \ No newline at end of file From 0d4619fdb21749536ec8de230da51ad47dd49d0c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 22 Jul 2015 09:27:08 -0400 Subject: [PATCH 1386/4033] pre-conditions for the top-level api --- src/main/cljs/cljs/js.cljs | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ead65be08..0f29c185d 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -32,6 +32,15 @@ [ns-sym] (string/replace (ana/munge-path ns-sym) \. \/)) +(defn atom? [x] + (instance? Atom x)) + +(defn valid-name? [x] + (or (nil? x) (symbol? x) (string? x))) + +(defn valid-opts? [x] + (or (nil? x) (map? x))) + (defonce ^{:doc "Each runtime environment provides a different way to load a library. Whatever function *load-fn* is bound to will be passed two arguments - a @@ -320,6 +329,8 @@ ([state source name cb] (analyze state source name nil cb)) ([state source name opts cb] + {:pre [(atom? state) (string? source) + (valid-name? name) (valid-opts? opts) (fn? cb)]} (analyze* {:*compiler* state :*data-readers* tags/*cljs-data-readers* @@ -447,15 +458,17 @@ ([state source name cb] (compile state source name nil cb)) ([state source name opts cb] - (compile* - {:*compiler* state - :*data-readers* tags/*cljs-data-readers* - :*analyze-deps* (or (:analyze-deps opts) true) - :*load-macros* (or (:load-macros opts) true) - :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*) - :*sm-data* (when (:source-map opts) (sm-data))} - source name opts cb))) + {:pre [(atom? state) (string? source) + (valid-name? name) (valid-opts? opts) (fn? cb)]} + (compile* + {:*compiler* state + :*data-readers* tags/*cljs-data-readers* + :*analyze-deps* (or (:analyze-deps opts) true) + :*load-macros* (or (:load-macros opts) true) + :*load-fn* (or (:load-fn opts) *load-fn*) + :*eval-fn* (or (:eval-fn opts) *eval-fn*) + :*sm-data* (when (:source-map opts) (sm-data))} + source name opts cb))) ;; ----------------------------------------------------------------------------- ;; Evaluate String @@ -530,7 +543,9 @@ (eval-str state source nil cb)) ([state source name cb] (eval-str state source name nil cb)) - ([state source opts name cb] + ([state source name opts cb] + {:pre [(atom? state) (string? source) + (valid-name? name) (valid-opts? opts) (fn? cb)]} (eval-str* {:*compiler* state :*data-readers* tags/*cljs-data-readers* From 210b5784557fe4e28304b4b3e13e5a4bb5a21866 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 22 Jul 2015 09:52:31 -0400 Subject: [PATCH 1387/4033] clean up docstrings. we use the actual name of the lib, the user cannot supply this --- src/main/cljs/cljs/js.cljs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 0f29c185d..0fb452512 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -44,7 +44,7 @@ (defonce ^{:doc "Each runtime environment provides a different way to load a library. Whatever function *load-fn* is bound to will be passed two arguments - a - map and a callback function: The map should have the following keys: + map and a callback function: The map will have the following keys: :name - the name of the library (a symbol) :path - munged relative library path (a string) @@ -55,8 +55,6 @@ :lang - the language, :clj or :js :source - the source of the library (a string) - :name - optional, used to uniquely identify the script - :path - optional, relative URL style path representing location If the resource could not be resolved, the callback should be invoked with nil." @@ -71,9 +69,8 @@ containing the following keys: :lang - the language, :clj or :js - :source - the source of the library (a string) - :name - optional, used to unique identify the script - :path - optional, relative URL style path representing location + :source - the source of the library (string) + :name - used to unique identify the script (symbol) The result of evaluation should be the return value." :dynamic true} @@ -149,7 +146,7 @@ (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") (if resource - (let [{:keys [name lang source]} resource] + (let [{:keys [lang source]} resource] (condp = lang :clj (eval-str* bound-vars source name opts (fn [ret] (cb true))) From aaa05c80ce73a1b70ea4de928bc75aa970233995 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 07:37:21 -0400 Subject: [PATCH 1388/4033] cljs.js - never throw in require always return error value, don't return boolean, return map with :value entry. --- src/main/cljs/cljs/js.cljs | 57 +++++++++++++++++++---------- src/main/clojure/cljs/analyzer.cljc | 11 +++--- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 0fb452512..9cb4dbede 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -83,6 +83,9 @@ [{:keys [source] :as resource}] (js/eval source)) +(defn wrap-error [ex] + {:error ex}) + (defn empty-state "Construct an empty compiler state. Required to invoke analyze, compile, eval and eval-str." @@ -141,25 +144,41 @@ (reset! *loaded* #{})) (when-not (contains? @*loaded* name) (let [env (:*env* bound-vars)] - ((:*load-fn* bound-vars) {:name name :path (ns->relpath name)} - (fn [resource] - (assert (or (map? resource) (nil? resource)) - "*load-fn* may only return a map or nil") - (if resource - (let [{:keys [lang source]} resource] - (condp = lang - :clj (eval-str* bound-vars source name opts - (fn [ret] (cb true))) - :js (do - ((:*eval-fn* bound-vars) resource) - (cb true)) - (throw - (js/Error. - (str "Invalid :lang specified " lang ", only :clj or :js allowed"))))) - (throw - (ana/error env - (ana/error-message :undeclared-ns - {:ns-sym name :js-provide (cljs.core/name name)})))))))))) + (try + ((:*load-fn* bound-vars) {:name name :path (ns->relpath name)} + (fn [resource] + (assert (or (map? resource) (nil? resource)) + "*load-fn* may only return a map or nil") + (if resource + (let [{:keys [lang source]} resource] + (condp = lang + :clj (try + (eval-str* bound-vars source name opts + (fn [ret] (cb {:value true}))) + (catch :default cause + (cb (wrap-error + (ana/error env + (str "Could not require " name) cause))))) + :js (let [res (try + ((:*eval-fn* bound-vars) resource) + (catch :default cause + (wrap-error + (ana/error env + (str "Could not require " name) cause))))] + (if (:error res) + (cb res) + (cb {:value true}))) + (cb (wrap-error + (ana/error env + (str "Invalid :lang specified " lang ", only :clj or :js allowed")))))) + (cb (wrap-error + (ana/error env + (ana/error-message :undeclared-ns + {:ns-sym name :js-provide (cljs.core/name name)}))))))) + (catch :default cause + (cb (wrap-error + (ana/error env + (str "Could not require " name) cause))))))))) (declare ns-side-effects analyze-deps) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 381692dfe..1fd440465 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -518,11 +518,12 @@ (handler warning-type env extra))) (defn error - ([env s] (error env s nil)) - ([env s cause] - (ex-info (message env s) - (assoc (source-info env) :tag :cljs/analysis-error) - cause))) + ([env msg] + (error env msg nil)) + ([env msg cause] + (ex-info (message env msg) + (assoc (source-info env) :tag :cljs/analysis-error) + cause))) (defn analysis-error? #?(:cljs {:tag boolean}) From 4a19f6ff6fb6daf5125c4ecdffbe556866034bcc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 07:54:59 -0400 Subject: [PATCH 1389/4033] error handling for load-deps, load-macros, and analyze-deps --- src/main/cljs/cljs/js.cljs | 63 +++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 9cb4dbede..14f6ad556 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -199,9 +199,13 @@ (when (:verbose opts) (debug-prn "Loading" dep)) (require bound-vars dep opts - (fn [_] - (load-deps bound-vars ana-env lib (next deps) opts cb)))) - (cb)))))) + (fn [res] + (if-not (:error res) + (load-deps bound-vars ana-env lib (next deps) opts cb) + (cb res))))) + (cb {:value nil})))))) + +(declare analyze*) (defn analyze-deps ([bound-vars ana-env lib deps cb] @@ -215,25 +219,32 @@ (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) (let [dep (first deps)] - ((:*load-fn* bound-vars) {:name dep :path (ns->relpath dep)} - (fn [resource] - (assert (or (map? resource) (nil? resource)) - "*load-fn* may only return a map or nil") - (if resource - (let [{:keys [name lang source]} resource] - (condp = lang - :clj (analyze* bound-vars source name opts - (fn [] - (analyze-deps bound-vars ana-env lib (next deps) opts cb))) - :js (analyze-deps bound-vars ana-env lib (next deps) opts cb) - (throw - (js/Error. - (str "Invalid :lang specified " lang ", only :clj or :js allowed"))))) - (throw - (ana/error ana-env - (ana/error-message :undeclared-ns - {:ns-sym dep :js-provide (name dep)}))))))) - (cb)))))) + (try + ((:*load-fn* bound-vars) {:name dep :path (ns->relpath dep)} + (fn [resource] + (assert (or (map? resource) (nil? resource)) + "*load-fn* may only return a map or nil") + (if resource + (let [{:keys [name lang source]} resource] + (condp = lang + :clj (analyze* bound-vars source name opts + (fn [res] + (if-not (:error res) + (analyze-deps bound-vars ana-env lib (next deps) opts cb) + (cb res)))) + :js (analyze-deps bound-vars ana-env lib (next deps) opts cb) + (wrap-error + (ana/error ana-env + (str "Invalid :lang specified " lang ", only :clj or :js allowed"))))) + (cb (wrap-error + (ana/error ana-env + (ana/error-message :undeclared-ns + {:ns-sym dep :js-provide (name dep)}))))))) + (catch :default e + (cb (wrap-error + (ana/error ana-env + (str "Could not analyze dep " dep))))))) + (cb {:value nil})))))) (defn load-macros [bound-vars k macros reload reloads opts cb] (if (seq macros) @@ -243,11 +254,13 @@ (get-in reloads [k nsym]) (and (= nsym name) (:*reload-macros* bound-vars) :reload))] (require bound-vars nsym k (assoc opts :macros-ns true) - (fn [] - (load-macros bound-vars k (next macros) reload reloads opts cb))) + (fn [res] + (if-not (:error res) + (load-macros bound-vars k (next macros) reload reloads opts cb) + (cb res)))) ;(intern-macros nsym k) )) - (cb))) + (cb {:value nil}))) (defn ns-side-effects ([bound-vars ana-env ast opts cb] From 9531d9e3f2c1ce08d456e62f39bab46ec85c186d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 08:50:09 -0400 Subject: [PATCH 1390/4033] ns-side-effects error handling --- src/main/cljs/cljs/js.cljs | 68 ++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 14f6ad556..4816bcef3 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -271,27 +271,46 @@ (if (= :ns op) (let [{:keys [deps uses requires require-macros use-macros reload reloads]} ast env (:*compiler* bound-vars)] - (letfn [(check-uses-and-load-macros [] - (when (and (:*analyze-deps* bound-vars) (seq uses)) - (when (:verbose opts) - (debug-prn "Checking uses")) - (ana/check-uses uses env)) - (if (:*load-macros* bound-vars) - (do - (when (:verbose opts) - (debug-prn "Loading :use-macros")) - (load-macros bound-vars :use-macros use-macros reload reloads opts - (fn [] - (when (:verbose opts) - (debug-prn "Loading :require-macros")) - (load-macros bound-vars :require-macros require-macros reloads reloads opts - (fn [] - (when (seq use-macros) - (when (:verbose opts) - (debug-prn "Checking :use-macros")) - (ana/check-use-macros use-macros env)) - (cb ast)))))) - (cb ast)))] + (letfn [(check-uses-and-load-macros [res] + (if (:error res) + (cb res) + (let [res (try + (when (and (:*analyze-deps* bound-vars) (seq uses)) + (when (:verbose opts) (debug-prn "Checking uses")) + (ana/check-uses uses env) + {:value nil}) + (catch :default cause + (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause))))] + (if (:error res) + (cb res) + (if (:*load-macros* bound-vars) + (do + (when (:verbose opts) (debug-prn "Loading :use-macros")) + (load-macros bound-vars :use-macros use-macros reload reloads opts + (fn [res] + (if (:error res) + (cb res) + (do + (when (:verbose opts) (debug-prn "Loading :require-macros")) + (load-macros bound-vars :require-macros require-macros reloads reloads opts + (fn [res] + (if (:error res) + (cb res) + (let [res (try + (when (seq use-macros) + (when (:verbose opts) (debug-prn "Checking :use-macros")) + (ana/check-use-macros use-macros env)) + {:value nil} + (catch :default cause + (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause))))] + (if (:error res) + (cb res) + (cb {:value ast}))))))))))) + (cb {:value ast}))))))] (cond (and load (seq deps)) (load-deps bound-vars ana-env (:name ast) deps (dissoc opts :macros-ns) @@ -302,8 +321,8 @@ check-uses-and-load-macros) :else - (check-uses-and-load-macros)))) - (cb ast)))) + (check-uses-and-load-macros {:value nil})))) + (cb {:value ast})))) (defn analyze* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) @@ -327,7 +346,8 @@ ast (ana/analyze aenv form)] (if (= :ns (:op ast)) (ns-side-effects bound-vars aenv ast opts - (fn [_] (analyze-loop))) + (fn [res] + (analyze-loop))) (recur))) (cb)))))))) From d335bec9677c2a03feff16f8db77a2cc35101732 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 08:57:21 -0400 Subject: [PATCH 1391/4033] error handling for analyze --- src/main/cljs/cljs/js.cljs | 42 +++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 4816bcef3..86cf7803f 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -338,18 +338,36 @@ *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] - (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] - (if-not (identical? eof form) - (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - (:context opts) (assoc :context (:context opts)) - (:def-emits-var opts) (assoc :def-emits-var true)) - ast (ana/analyze aenv form)] - (if (= :ns (:op ast)) - (ns-side-effects bound-vars aenv ast opts - (fn [res] - (analyze-loop))) - (recur))) - (cb)))))))) + (let [res (try + {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} + (catch :default cause + (wrap-error + (ana/error aenv + (str "Could not analyze " name) cause))))] + (if (:error res) + (cb res) + (let [form (:value res)] + (if-not (identical? eof form) + (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true)) + res (try + {:value (ana/analyze aenv form)} + (catch :default cause + (wrap-error + (ana/error aenv + (str "Could not analyze " name) cause))))] + (if (:error res) + (cb res) + (let [ast (:value res)] + (if (= :ns (:op ast)) + (ns-side-effects bound-vars aenv ast opts + (fn [res] + (if (:error res) + (cb res) + (analyze-loop)))) + (recur))))) + (cb {:value nil})))))))))) (defn analyze "Analyze ClojureScript source. The compiler state will be populated with From dafe3797ba15d333f0cb0993b2b512d5072e7c9e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 09:12:37 -0400 Subject: [PATCH 1392/4033] error handling for compile & eval-str --- src/main/cljs/cljs/js.cljs | 119 ++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 42 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 86cf7803f..e9ad38310 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -483,22 +483,38 @@ *ns* (:*ns* bound-vars) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] - (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] - (if-not (identical? eof form) - (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - (:context opts) (assoc :context (:context opts)) - (:def-emits-var opts) (assoc :def-emits-var true)) - ast (ana/analyze aenv form)] - (.append sb (with-out-str (comp/emit ast))) - (if (= :ns (:op ast)) - (ns-side-effects bound-vars aenv ast opts - (fn [_] (compile-loop))) - (recur))) - (do - (when (:source-map opts) - (append-source-map env/*compiler* - name source sb @comp/*source-map-data* opts)) - (cb (.toString sb)))))))))) + (let [res (try + {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} + (catch :default cause + (wrap-error + (ana/error aenv + (str "Could not compile " name) cause))))] + (if (:error res) + (cb res) + (let [form (:value res)] + (if-not (identical? eof form) + (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true)) + ast (try + (ana/analyze aenv form) + (catch :default cause + (wrap-error + (ana/error aenv + (str "Could not compile " name) cause))))] + (.append sb (with-out-str (comp/emit ast))) + (if (= :ns (:op ast)) + (ns-side-effects bound-vars aenv ast opts + (fn [res] + (if (:error res) + (cb res) + (compile-loop)))) + (recur))) + (do + (when (:source-map opts) + (append-source-map env/*compiler* + name source sb @comp/*source-map-data* opts)) + (cb {:value (.toString sb)}))))))))))) (defn compile "Compile ClojureScript source into JavaScript. The parameters: @@ -556,33 +572,52 @@ *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] - (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)] - (if-not (identical? eof form) - (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ns)) - (:context opts) (assoc :context (:context opts)) - (:def-emits-var opts) (assoc :def-emits-var true)) - ast (ana/analyze aenv form) - ns' ana/*cljs-ns*] - (if (= :ns (:op ast)) - (do - (.append sb - (str "goog.provide(\"" (munge (:name ast)) "\");\n")) - (ns-side-effects true bound-vars aenv ast opts - (fn [_] (compile-loop ns')))) - (do - (.append sb (with-out-str (comp/emit ast))) - (recur ns')))) - (let [js-source (.toString sb) - evalm {:lang :clj - :name name - :path (ns->relpath name) + (let [res (try + {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} + (catch :default cause + (wrap-error + (ana/error aenv + (str "Could not eval " name) cause))))] + (if (:error res) + (cb res) + (let [form (:value res)] + (if-not (identical? eof form) + (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ns)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true)) + res (try + {:value (ana/analyze aenv form)} + (catch :deafult cause + (wrap-error + (ana/error aenv + (str "Could not eval " name) cause))))] + (if (:error res) + (cb res) + (let [ast (:value res) + ns' ana/*cljs-ns*] + (if (= :ns (:op ast)) + (do + (.append sb + (str "goog.provide(\"" (munge (:name ast)) "\");\n")) + (ns-side-effects true bound-vars aenv ast opts + (fn [res] + (if (:error res) + (cb res) + (compile-loop ns'))))) + (do + (.append sb (with-out-str (comp/emit ast))) + (recur ns')))))) + (let [js-source (.toString sb) + evalm {:lang :clj + :name name + :path (ns->relpath name) :source js-source}] - (when (:verbose opts) - (debug-prn js-source)) - (when (:source-map opts) - (append-source-map env/*compiler* - name source sb @comp/*source-map-data* opts)) - (cb {:ns ns :value (*eval-fn* evalm)})))))) + (when (:verbose opts) + (debug-prn js-source)) + (when (:source-map opts) + (append-source-map env/*compiler* + name source sb @comp/*source-map-data* opts)) + (cb {:ns ns :value (*eval-fn* evalm)})))))))) (:*cljs-ns* bound-vars)))) (defn eval-str From 97541e9dcd76c44a8889961f7cc4b0568de392e3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 09:22:02 -0400 Subject: [PATCH 1393/4033] clarify docstrings to describe the error handling behavior. --- src/main/cljs/cljs/js.cljs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index e9ad38310..8e7cce77b 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -389,8 +389,10 @@ :load-fn - library resolution function, see *load-fn* cb (function) - callback, will be invoked with the evalution result." - + callback, will be invoked with a map. If successful the map will contain + a key :value, the actual value is not meaningful. If unsuccessful the + map will contain a key :error with an ex-info instance describing the cause + of failure." ([state source cb] (analyze state source nil cb)) ([state source name cb] @@ -535,7 +537,10 @@ :source-map - set to true to generate inline source map information cb (function) - callback, will be invoked with the compilation result (string)." + callback, will be invoked with a map. If successful the map will contain + a key :value with the compilation result (string). If unsuccessful the map + will contain a key :error with an ex-info instance describing the cause + of failure." ([state source cb] (compile state source nil cb)) ([state source name cb] @@ -640,7 +645,9 @@ :source-map - set to true to generate inline source map information cb (function) - callback, will be invoked with result of evalution" + callback, will be invoked with a map. If succesful the map will contain + a :value key with the result of evaluation. If unsuccessful will contain + a :error key with an ex-info instance describing the cause of failure." ([state source cb] (eval-str state source nil cb)) ([state source name cb] From c7387cf2ebc49b90c12eb315ee3d64ed8a1c5f4a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 09:35:02 -0400 Subject: [PATCH 1394/4033] :eval-fn -> :eval :load-fn -> :load --- src/main/cljs/cljs/js.cljs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 8e7cce77b..e4dcfd587 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -132,8 +132,8 @@ (require {:*compiler* (env/default-compiler-env) :*data-readers* tags/*cljs-data-readers* - :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*)} + :*load-fn* (or (:load opts) *load-fn*) + :*eval-fn* (or (:eval opts) *eval-fn*)} name opts cb)) ([bound-vars name opts cb] (require bound-vars name nil opts cb)) @@ -385,8 +385,8 @@ opts (map) compilation options. - :eval-fn - the eval function to invoke, see *eval-fn* - :load-fn - library resolution function, see *load-fn* + :eval - the eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* cb (function) callback, will be invoked with a map. If successful the map will contain @@ -403,8 +403,8 @@ (analyze* {:*compiler* state :*data-readers* tags/*cljs-data-readers* - :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*)} + :*load-fn* (or (:load opts) *load-fn*) + :*eval-fn* (or (:eval opts) *eval-fn*)} source name opts cb))) ;; ----------------------------------------------------------------------------- @@ -449,8 +449,8 @@ opts (map) compilation options. - :eval-fn - the eval function to invoke, see *eval-fn* - :load-fn - library resolution function, see *load-fn* + :eval - the eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* cb (function) callback, will be invoked with the evalution result." @@ -462,8 +462,8 @@ :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* - :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*)} + :*load-fn* (or (:load opts) *load-fn*) + :*eval-fn* (or (:eval opts) *eval-fn*)} form opts cb))) ;; ----------------------------------------------------------------------------- @@ -533,7 +533,7 @@ opts (map) compilation options. - :load-fn - library resolution function, see *load-fn* + :load - library resolution function, see *load-fn* :source-map - set to true to generate inline source map information cb (function) @@ -553,8 +553,8 @@ :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) - :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*) + :*load-fn* (or (:load opts) *load-fn*) + :*eval-fn* (or (:eval opts) *eval-fn*) :*sm-data* (when (:source-map opts) (sm-data))} source name opts cb))) @@ -640,8 +640,8 @@ opts (map) compilation options. - :eval-fn - eval function to invoke, see *eval-fn* - :load-fn - library resolution function, see *load-fn* + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* :source-map - set to true to generate inline source map information cb (function) @@ -660,6 +660,6 @@ :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) - :*load-fn* (or (:load-fn opts) *load-fn*) - :*eval-fn* (or (:eval-fn opts) *eval-fn*)} + :*load-fn* (or (:load opts) *load-fn*) + :*eval-fn* (or (:eval opts) *eval-fn*)} source name opts cb))) \ No newline at end of file From d23255b56fc1fb6f3501cb444eacaf875873ebc5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 09:38:33 -0400 Subject: [PATCH 1395/4033] drop emit from cljs.js api --- src/main/cljs/cljs/js.cljs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index e4dcfd587..3bde1bdcc 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -407,16 +407,6 @@ :*eval-fn* (or (:eval opts) *eval-fn*)} source name opts cb))) -;; ----------------------------------------------------------------------------- -;; Emit - -(defn emit* [bound-vars ast cb] - (binding [env/*compiler* (:*compiler* bound-vars)] - (cb (with-out-str (comp/emit ast))))) - -(defn emit [state ast cb] - (emit* {:*compiler* state} ast cb)) - ;; ----------------------------------------------------------------------------- ;; Eval From 0008e525787bf64cfe28c818d84f3e4ea0b87c4d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 08:57:16 -0500 Subject: [PATCH 1396/4033] ns form and error handlign for eval --- src/main/cljs/cljs/js.cljs | 54 +++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 3bde1bdcc..df31053fb 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -410,22 +410,39 @@ ;; ----------------------------------------------------------------------------- ;; Eval -;; TODO: ns form handling, source mapping - (defn eval* [bound-vars form opts cb] - (binding [env/*compiler* (:*compiler* bound-vars) - *eval-fn* (:*eval-fn* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) - r/*data-readers* (:*data-readers* bound-vars)] - (let [aenv (ana/empty-env) - aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) - (:context opts) (assoc :context (:context opts)) - (:def-emits-var opts) (assoc :def-emits-var true))] - (cb (*eval-fn* - {:lang :clj - :source (with-out-str - (comp/emit (ana/analyze aenv form nil opts)))}))))) + (let [bound-vars (cond-> (merge bound-vars + {:*cljs-ns* 'cljs.user + :*ns* (create-ns ana/*cljs-ns*)}) + (:source-map opts) (assoc :*sm-data* (sm-data)))] + (binding [env/*compiler* (:*compiler* bound-vars) + *eval-fn* (:*eval-fn* bound-vars) + ana/*cljs-ns* (:*cljs-ns* bound-vars) + *ns* (:*ns* bound-vars) + r/*data-readers* (:*data-readers* bound-vars) + comp/*source-map-data* (:*sm-data* bound-vars)] + (let [aenv (ana/empty-env) + aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) + (:context opts) (assoc :context (:context opts)) + (:def-emits-var opts) (assoc :def-emits-var true)) + res (try + {:value (ana/analyze aenv form nil opts)} + (catch :default cause + (wrap-error + (ana/error aenv + (str "Could not eval " form) cause))))] + (if (:error res) + (cb res) + (let [ast (:value res)] + (if (= :ns (:op ast)) + (ns-side-effects true bound-vars aenv ast opts + (fn [res] + (if (:error res) + (cb res) + (let [src (str "goog.provide(\"" (munge (:name ast)) "\")")] + (cb (*eval-fn* {:lang :clj :source src})))))) + (let [src (with-out-str (comp/emit ast))] + (cb (*eval-fn* {:lang :clj :source src})))))))))) (defn eval "Evaluate a single ClojureScript form. The parameters: @@ -443,7 +460,10 @@ :load - library resolution function, see *load-fn* cb (function) - callback, will be invoked with the evalution result." + callback, will be invoked with a map. If successful the map will contain + a :value key with the result of evalution. If unsuccessful the map wil + contain a :error key with an ex-info instance describing the cause of + failure." ([state form cb] (eval state form nil cb)) ([state form opts cb] @@ -452,6 +472,8 @@ :*cljs-ns* 'cljs.user :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* + :*analyze-deps* (or (:analyze-deps opts) true) + :*load-macros* (or (:load-macros opts) true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} form opts cb))) From 73a14b510457539406e3f148c86fec5a8a48f6b8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 09:12:45 -0500 Subject: [PATCH 1397/4033] *load-fn* must be passed a :macros modifier so that implementors can change resolution strategy correspondingly --- src/main/cljs/cljs/js.cljs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index df31053fb..9f9bed9dc 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -47,11 +47,14 @@ map and a callback function: The map will have the following keys: :name - the name of the library (a symbol) + :macros - modifier signaling a macros namespace load :path - munged relative library path (a string) It is up to the implementor to correctly resolve the corresponding .cljs, - .cljc, or .js resource (the order must be respected). Upon resolution the - callback should be invoked with a map containing the following keys: + .cljc, or .js resource (the order must be respected). If :macros is true + resolution should only consider .clj or .cljc resources (the order must be + respected). Upon resolution the callback should be invoked with a map + containing the following keys: :lang - the language, :clj or :js :source - the source of the library (a string) @@ -145,7 +148,10 @@ (when-not (contains? @*loaded* name) (let [env (:*env* bound-vars)] (try - ((:*load-fn* bound-vars) {:name name :path (ns->relpath name)} + ((:*load-fn* bound-vars) + {:name name + :macros (:macros-ns opts) + :path (ns->relpath name)} (fn [resource] (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") From 0d8754b13fa90d84a231d34ded773f29677be2dd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 09:50:26 -0500 Subject: [PATCH 1398/4033] defmacro is a macro --- src/main/clojure/cljs/core.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8f24b0e8d..36b4c7877 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2827,3 +2827,5 @@ (core/list 'do (cons `defn decl) (core/list 'set! `(. ~name ~'-cljs$lang$macro) true)))) + +#?(:cljs (set! (. defmacro -cljs$lang$macro) true)) From f7130caf763b36d2274bcf6122214ef8a36ef922 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 09:58:09 -0500 Subject: [PATCH 1399/4033] add check-uses-macros error handling TODO --- src/main/clojure/cljs/analyzer.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1fd440465..cb981b3e2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1638,6 +1638,8 @@ (error env (error-message :undeclared-ns-form {:type "var" :lib lib :sym sym}))))))) +;; TODO: better error handling here, we should be first checking if the ns +;; was found (defn check-use-macros [use-macros env] (doseq [[sym lib] use-macros] (when (nil? #?(:clj (.findInternedVar ^clojure.lang.Namespace (find-ns lib) sym) From 8445e810d014116aaa7e2ea927604e50d09f49b4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 10:17:10 -0500 Subject: [PATCH 1400/4033] need to pass opts to analyze, e -> cause for consistency --- src/main/cljs/cljs/js.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 9f9bed9dc..7d83fd1a2 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -246,10 +246,10 @@ (ana/error ana-env (ana/error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)}))))))) - (catch :default e + (catch :default cause (cb (wrap-error (ana/error ana-env - (str "Could not analyze dep " dep))))))) + (str "Could not analyze dep " dep) cause)))))) (cb {:value nil})))))) (defn load-macros [bound-vars k macros reload reloads opts cb] @@ -358,7 +358,7 @@ (:context opts) (assoc :context (:context opts)) (:def-emits-var opts) (assoc :def-emits-var true)) res (try - {:value (ana/analyze aenv form)} + {:value (ana/analyze aenv form nil opts)} (catch :default cause (wrap-error (ana/error aenv @@ -517,7 +517,7 @@ (:context opts) (assoc :context (:context opts)) (:def-emits-var opts) (assoc :def-emits-var true)) ast (try - (ana/analyze aenv form) + (ana/analyze aenv form nil opts) (catch :default cause (wrap-error (ana/error aenv @@ -609,8 +609,8 @@ (:context opts) (assoc :context (:context opts)) (:def-emits-var opts) (assoc :def-emits-var true)) res (try - {:value (ana/analyze aenv form)} - (catch :deafult cause + {:value (ana/analyze aenv form nil opts)} + (catch :default cause (wrap-error (ana/error aenv (str "Could not eval " name) cause))))] From 05a2f92a1051c0caf7e661789cf7e0834ccec4bf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 10:33:00 -0500 Subject: [PATCH 1401/4033] improve check-use-macros checking --- src/main/clojure/cljs/analyzer.cljc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cb981b3e2..69f1384a8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1638,15 +1638,14 @@ (error env (error-message :undeclared-ns-form {:type "var" :lib lib :sym sym}))))))) -;; TODO: better error handling here, we should be first checking if the ns -;; was found (defn check-use-macros [use-macros env] (doseq [[sym lib] use-macros] - (when (nil? #?(:clj (.findInternedVar ^clojure.lang.Namespace (find-ns lib) sym) - :cljs (.findInternedVar (find-macros-ns lib) sym))) - (throw - (error env - (error-message :undeclared-ns-form {:type "macro" :lib lib :sym sym})))))) + (let [the-ns #?(:clj (find-ns lib) + :cljs (find-macros-ns lib))] + (when (or (nil? the-ns) (nil? (.findInternedVar ^clojure.lang.Namespace the-ns sym))) + (throw + (error env + (error-message :undeclared-ns-form {:type "macro" :lib lib :sym sym}))))))) (defn parse-ns-error-msg [spec msg] (str msg "; offending spec: " (pr-str spec))) From a02ffa87afd828d025c158deb90168705d5b5b46 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 14:39:49 -0500 Subject: [PATCH 1402/4033] make verbose logging more useful --- src/main/cljs/cljs/js.cljs | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 7d83fd1a2..54d998c50 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -145,6 +145,8 @@ (swap! *loaded* disj name)) (when (= :reload-all reload) (reset! *loaded* #{})) + (when (:verbose opts) + (debug-prn (str "Loading " name (when (:macros-ns opts) " macros") " namespace"))) (when-not (contains? @*loaded* name) (let [env (:*env* bound-vars)] (try @@ -193,7 +195,7 @@ (analyze-deps bound-vars ana-env lib deps nil cb)) ([bound-vars ana-env lib deps opts cb] (when (:verbose opts) - (debug-prn "Loading dependencies")) + (debug-prn "Loading dependencies for" lib)) (let [compiler @(:*compiler* bound-vars)] (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) update-in [:dep-path] conj lib)] @@ -202,8 +204,6 @@ (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) (let [dep (first deps)] - (when (:verbose opts) - (debug-prn "Loading" dep)) (require bound-vars dep opts (fn [res] (if-not (:error res) @@ -254,18 +254,15 @@ (defn load-macros [bound-vars k macros reload reloads opts cb] (if (seq macros) - (let [env (:*compiler* bound-vars) - nsym (first (vals macros))] - (let [k (or (k reload) - (get-in reloads [k nsym]) - (and (= nsym name) (:*reload-macros* bound-vars) :reload))] - (require bound-vars nsym k (assoc opts :macros-ns true) - (fn [res] - (if-not (:error res) - (load-macros bound-vars k (next macros) reload reloads opts cb) - (cb res)))) - ;(intern-macros nsym k) - )) + (let [nsym (first (vals macros)) + k (or (k reload) + (get-in reloads [k nsym]) + (and (= nsym name) (:*reload-macros* bound-vars) :reload))] + (require bound-vars nsym k (assoc opts :macros-ns true) + (fn [res] + (if-not (:error res) + (load-macros bound-vars k (next macros) reload reloads opts cb) + (cb res))))) (cb {:value nil}))) (defn ns-side-effects @@ -293,20 +290,20 @@ (cb res) (if (:*load-macros* bound-vars) (do - (when (:verbose opts) (debug-prn "Loading :use-macros")) + (when (:verbose opts) (debug-prn "Processing :use-macros for" (:name ast))) (load-macros bound-vars :use-macros use-macros reload reloads opts (fn [res] (if (:error res) (cb res) (do - (when (:verbose opts) (debug-prn "Loading :require-macros")) + (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) (load-macros bound-vars :require-macros require-macros reloads reloads opts (fn [res] (if (:error res) (cb res) (let [res (try (when (seq use-macros) - (when (:verbose opts) (debug-prn "Checking :use-macros")) + (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) (ana/check-use-macros use-macros env)) {:value nil} (catch :default cause @@ -588,6 +585,7 @@ {:*cljs-ns* 'cljs.user :*ns* (create-ns ana/*cljs-ns*)}) (:source-map opts) (assoc :*sm-data* (sm-data)))] + (when (:verbose opts) (debug-prn "Evaluating" name)) ((fn compile-loop [ns] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) From d1499deaa7461fe30b6b0d35e36c64ef93047722 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 14:54:41 -0500 Subject: [PATCH 1403/4033] fix require not checking for errors returned from eval-str* --- src/main/cljs/cljs/js.cljs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 54d998c50..9ab3143a8 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -160,13 +160,11 @@ (if resource (let [{:keys [lang source]} resource] (condp = lang - :clj (try - (eval-str* bound-vars source name opts - (fn [ret] (cb {:value true}))) - (catch :default cause - (cb (wrap-error - (ana/error env - (str "Could not require " name) cause))))) + :clj (eval-str* bound-vars source name opts + (fn [res] + (if (:error res) + (cb res) + (cb {:value true})))) :js (let [res (try ((:*eval-fn* bound-vars) resource) (catch :default cause From d7553fd052f04cdec5ee6d0a06b0c48616f7eee0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 15:23:06 -0500 Subject: [PATCH 1404/4033] add missing conditionalization of macro-ns-name, bootstrapped ClojureScript can now load macro namespaces and compile them and use them. --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 69f1384a8..d19e54312 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -24,7 +24,8 @@ [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers] [clojure.edn :as edn]) - :cljs (:require [clojure.string :as string] + :cljs (:require [goog.string :as gstring] + [clojure.string :as string] [clojure.set :as set] [cljs.env :as env] [cljs.tagged-literals :as tags] @@ -1832,7 +1833,8 @@ (defn macro-ns-name [name] (let [name-str (str name)] - (if-not (.endsWith name-str "$macros") + (if-not #?(:clj (.endsWith name-str "$macros") + :cljs (gstring/endsWith name-str "$macros")) (symbol (str name-str "$macros")) name))) From eeae6cfaabf821b33fd6d0d55366947012937122 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jul 2015 16:19:01 -0500 Subject: [PATCH 1405/4033] fix *eval-fn* docstring, :lang isn't a thing. Fix eval* --- src/main/cljs/cljs/js.cljs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 9ab3143a8..116e7afce 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -71,7 +71,6 @@ source. Whatever function *eval-fn* is bound to will be passed a map containing the following keys: - :lang - the language, :clj or :js :source - the source of the library (string) :name - used to unique identify the script (symbol) @@ -441,9 +440,9 @@ (if (:error res) (cb res) (let [src (str "goog.provide(\"" (munge (:name ast)) "\")")] - (cb (*eval-fn* {:lang :clj :source src})))))) + (cb (*eval-fn* {:source src})))))) (let [src (with-out-str (comp/emit ast))] - (cb (*eval-fn* {:lang :clj :source src})))))))))) + (cb (*eval-fn* {:source src})))))))))) (defn eval "Evaluate a single ClojureScript form. The parameters: From 9c96d72ffaa5348baf0c18aaea1e5ade32646434 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 24 Jul 2015 13:11:18 -0500 Subject: [PATCH 1406/4033] fix source map debugging in eval-str --- src/main/cljs/cljs/js.cljs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 116e7afce..0de8c5d6c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -625,17 +625,18 @@ (do (.append sb (with-out-str (comp/emit ast))) (recur ns')))))) - (let [js-source (.toString sb) - evalm {:lang :clj - :name name - :path (ns->relpath name) - :source js-source}] - (when (:verbose opts) - (debug-prn js-source)) + (do (when (:source-map opts) (append-source-map env/*compiler* name source sb @comp/*source-map-data* opts)) - (cb {:ns ns :value (*eval-fn* evalm)})))))))) + (let [js-source (.toString sb) + evalm {:lang :clj + :name name + :path (ns->relpath name) + :source js-source}] + (when (:verbose opts) + (debug-prn js-source)) + (cb {:ns ns :value (*eval-fn* evalm)}))))))))) (:*cljs-ns* bound-vars)))) (defn eval-str From 0a15c1b829712939f29913cbbaf8f16fe144d8d9 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Fri, 24 Jul 2015 15:28:03 -0500 Subject: [PATCH 1407/4033] CLJS-1366: Warnings from bootstrap use of defrecord Fully namespace core/list, so that defrecord with default value does not give warnings when bootstrapped: cljs.user> (defprotocol IFoo (foo [this])) nil cljs.user> (defrecord Baz [b] IFoo (foo [this] (prn "some baz:" b))) WARNING: No such namespace: core, could not locate core.cljs, core.cljc, or Closure namespace "" WARNING: Use of undeclared Var core/list cljs.user/Baz --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 36b4c7877..cead46570 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1723,7 +1723,7 @@ ~(emit-defrecord &env rsym r fields impls) (set! (.-getBasis ~r) (fn [] '[~@fields])) (set! (.-cljs$lang$type ~r) true) - (set! (.-cljs$lang$ctorPrSeq ~r) (fn [this#] (core/list ~(core/str r)))) + (set! (.-cljs$lang$ctorPrSeq ~r) (fn [this#] (cljs.core/list ~(core/str r)))) (set! (.-cljs$lang$ctorPrWriter ~r) (fn [this# writer#] (-write writer# ~(core/str r)))) ~(build-positional-factory rsym r fields) ~(build-map-factory rsym r fields) From c1090db5ea90692d5cdbe7c58b40a9af9dc39316 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jul 2015 13:51:19 -0500 Subject: [PATCH 1408/4033] add load-ns and load-ns! --- src/main/cljs/cljs/js.cljs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 0de8c5d6c..e1b38a72f 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -96,6 +96,12 @@ ([init] (doto (empty-state) (swap! init)))) +(defn load-ns [pure-state ns cache] + (assoc-in pure-state [:ana/namespaces ns] cache)) + +(defn load-ns! [state ns cache] + (assoc-in state [:ana/namespaces ns] cache)) + (defn sm-data [] (atom {:source-map (sorted-map) From 69dcc9ba06c2b1359b7534b1d6ae9375cf4c0242 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jul 2015 13:59:20 -0500 Subject: [PATCH 1409/4033] typos --- src/main/cljs/cljs/js.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index e1b38a72f..c052d5f76 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -97,10 +97,10 @@ (doto (empty-state) (swap! init)))) (defn load-ns [pure-state ns cache] - (assoc-in pure-state [:ana/namespaces ns] cache)) + (assoc-in pure-state [::ana/namespaces ns] cache)) (defn load-ns! [state ns cache] - (assoc-in state [:ana/namespaces ns] cache)) + (assoc-in state [::ana/namespaces ns] cache)) (defn sm-data [] (atom From 72e01962bfeae9c24b37d6c27a86fd12412e7f07 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jul 2015 15:23:38 -0500 Subject: [PATCH 1410/4033] just dump core into the cljs.js/empty-state fixes CLJS-1362 if *load-fn* returns :js + :cache load :cache as an analysis cache. pass :cache to *eval-fn* so user can cache analysis --- src/main/cljs/cljs/js.clj | 11 ++++++++--- src/main/cljs/cljs/js.cljs | 25 ++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/js.clj b/src/main/cljs/cljs/js.clj index 1dea99a90..89b1a79c9 100644 --- a/src/main/cljs/cljs/js.clj +++ b/src/main/cljs/cljs/js.clj @@ -7,9 +7,14 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.js - (:require [cljs.env.macros :as env])) + (:require [cljs.env :as env] + [cljs.env.macros :as menv] + [cljs.analyzer :as ana])) (defmacro with-state [state & body] - `(env/with-compiler-env ~state - ~@body)) \ No newline at end of file + `(menv/with-compiler-env ~state + ~@body)) + +(defmacro dump-core [] + `(quote ~(get-in @env/*compiler* [::ana/namespaces 'cljs.core]))) \ No newline at end of file diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index c052d5f76..2ae0e2b8b 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.js - (:require-macros [cljs.js] + (:require-macros [cljs.js :refer [dump-core]] [cljs.env.macros :as env]) (:require [clojure.string :as string] [cljs.env :as env] @@ -58,6 +58,8 @@ :lang - the language, :clj or :js :source - the source of the library (a string) + :cache - optional, if a :clj namespace has been precompiled to :js, can give + an analysis cache for faster loads. If the resource could not be resolved, the callback should be invoked with nil." @@ -73,6 +75,8 @@ :source - the source of the library (string) :name - used to unique identify the script (symbol) + :cache - if the source was originally ClojureScript, will be given the + analysis cache. The result of evaluation should be the return value." :dynamic true} @@ -92,15 +96,13 @@ "Construct an empty compiler state. Required to invoke analyze, compile, eval and eval-str." ([] - (env/default-compiler-env)) + (doto (env/default-compiler-env) + (swap! assoc-in [::ana/namespaces 'cljs.core] (dump-core)))) ([init] (doto (empty-state) (swap! init)))) -(defn load-ns [pure-state ns cache] - (assoc-in pure-state [::ana/namespaces ns] cache)) - -(defn load-ns! [state ns cache] - (assoc-in state [::ana/namespaces ns] cache)) +(defn load-analysis-cache! [state ns cache] + (swap! state assoc-in [::ana/namespaces ns] cache)) (defn sm-data [] (atom @@ -163,7 +165,7 @@ (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") (if resource - (let [{:keys [lang source]} resource] + (let [{:keys [lang source cache]} resource] (condp = lang :clj (eval-str* bound-vars source name opts (fn [res] @@ -172,6 +174,9 @@ (cb {:value true})))) :js (let [res (try ((:*eval-fn* bound-vars) resource) + (when cache + (load-analysis-cache! + (:*compiler* bound-vars) name cache)) (catch :default cause (wrap-error (ana/error env @@ -639,7 +644,9 @@ evalm {:lang :clj :name name :path (ns->relpath name) - :source js-source}] + :source js-source + :cache (get-in env/*compiler* + [::ana/namespaces name])}] (when (:verbose opts) (debug-prn js-source)) (cb {:ns ns :value (*eval-fn* evalm)}))))))))) From 7357e166b81dcd603a8c6e9a81872e839711c1d1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jul 2015 17:58:42 -0500 Subject: [PATCH 1411/4033] copy JS engine stacktrace parsing into a .cljc ns --- src/main/cljs/cljs/stacktrace.cljc | 328 +++++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 src/main/cljs/cljs/stacktrace.cljc diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc new file mode 100644 index 000000000..54133b584 --- /dev/null +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -0,0 +1,328 @@ +;; 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.stacktrace + (:require #?(:clj [cljs.util :as util] + :cljs [goog.string :as gstring]) + [clojure.string :as string]) + #?(:clj (:import [java.util.regex Pattern]))) + +(defmulti parse-stacktrace (fn [repl-env st err opts] (:ua-product err))) + +(defn parse-int [s] + #?(:clj (Long/parseLong s) + :cljs (js/parseInt s 10))) + +(defn starts-with? + #?(:cljs {:tag boolean}) + [^String s0 s1] + #?(:clj (.startsWith s0 s1) + :cljs (gstring/startsWith s0 s1))) + +(defn ends-with? + #?(:cljs {:tag boolean}) + [^String s0 s1] + #?(:clj (.endsWith s0 s1) + :cljs (gstring/endsWith s0 s1))) + +(defmethod parse-stacktrace :default + [repl-env st err opts] st) + +(defn parse-file-line-column [flc] + (let [xs (string/split flc #":") + [pre [line column]] + (reduce + (fn [[pre post] [x i]] + (if (<= i 2) + [pre (conj post x)] + [(conj pre x) post])) + [[] []] (map vector xs (range (count xs) 0 -1))) + file (string/join ":" pre)] + [(cond-> file + (starts-with? file "(") (string/replace "(" "")) + (parse-int + (cond-> line + (ends-with? line ")") (string/replace ")" ""))) + (parse-int + (cond-> column + (ends-with? column ")") (string/replace ")" "")))])) + +(defn parse-file + "Given a browser file url convert it into a relative path that can be used + to locate the original source." + [{:keys [host host-port port] :as repl-env} file {:keys [asset-path] :as opts}] + (let [base-url-pattern (Pattern/compile (str "http://" host ":" (or host-port port) "/"))] + (if (re-find base-url-pattern file) + (-> file + (string/replace base-url-pattern "") + (string/replace + (Pattern/compile + ;; if :asset-path specified drop leading slash + (str "^" (or (and asset-path (string/replace asset-path #"^/" "")) + (util/output-directory opts)) "/")) + "")) + (if-let [asset-root (:asset-root opts)] + (string/replace file asset-root "") + (throw + (ex-info (str "Could not relativize URL " file) + {:type :parse-stacktrace + :reason :relativize-url})))))) + +;; ----------------------------------------------------------------------------- +;; Chrome Stacktrace + +(defn chrome-st-el->frame + [repl-env st-el opts] + (let [xs (-> st-el + (string/replace #"\s+at\s+" "") + (string/split #"\s+")) + [function flc] (if (== 1 (count xs)) + [nil (first xs)] + [(first xs) (last xs)]) + [file line column] (parse-file-line-column flc)] + (if (and file function line column) + {:file (parse-file repl-env file opts) + :function (string/replace function #"Object\." "") + :line line + :column column} + (when-not (string/blank? function) + {:file nil + :function (string/replace function #"Object\." "") + :line nil + :column nil})))) + +(comment + (chrome-st-el->frame {:host "localhost" :port 9000} + "\tat cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34)" {}) + ) + +(defmethod parse-stacktrace :chrome + [repl-env st err opts] + (->> st + string/split-lines + (drop-while #(starts-with? % "Error")) + (take-while #(not (starts-with? % " at eval"))) + (map #(chrome-st-el->frame repl-env % opts)) + (remove nil?) + vec)) + +(comment + (parse-stacktrace {:host "localhost" :port 9000} + "Error: 1 is not ISeqable + at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4258:8) + at Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4288:19) + at cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34) + at http://localhost:9000/out/cljs/core.js:16971:89 + at cljs.core.map.cljs$core$map__2 (http://localhost:9000/out/cljs/core.js:16972:3) + at http://localhost:9000/out/cljs/core.js:10981:129 + at cljs.core.LazySeq.sval (http://localhost:9000/out/cljs/core.js:10982:3) + at cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (http://localhost:9000/out/cljs/core.js:11073:10) + at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4239:13) + at Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/out/cljs/core.js:28706:14)" + {:ua-product :chrome} + nil) + + (parse-stacktrace {:host "localhost" :port 9000} + "Error: 1 is not ISeqable + at Object.cljs$core$seq [as seq] (http://localhost:9000/js/cljs/core.js:4258:8) + at Object.cljs$core$first [as first] (http://localhost:9000/js/cljs/core.js:4288:19) + at cljs$core$ffirst (http://localhost:9000/js/cljs/core.js:5356:34) + at http://localhost:9000/js/cljs/core.js:16971:89 + at cljs.core.map.cljs$core$map__2 (http://localhost:9000/js/cljs/core.js:16972:3) + at http://localhost:9000/js/cljs/core.js:10981:129 + at cljs.core.LazySeq.sval (http://localhost:9000/js/cljs/core.js:10982:3) + at cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (http://localhost:9000/js/cljs/core.js:11073:10) + at Object.cljs$core$seq [as seq] (http://localhost:9000/js/cljs/core.js:4239:13) + at Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/js/cljs/core.js:28706:14)" + {:ua-product :chrome} + {:asset-path "/js"}) + + (parse-stacktrace {:host "localhost" :port 9000} + "Error: 1 is not ISeqable + at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4259:8) + at Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4289:19) + at cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5357:18) + at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :1:106) + at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :9:3) + at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :14:4) + at http://localhost:9000/out/clojure/browser/repl.js:23:267 + at clojure$browser$repl$evaluate_javascript (http://localhost:9000/out/clojure/browser/repl.js:26:4) + at Object.callback (http://localhost:9000/out/clojure/browser/repl.js:121:169) + at goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)" + {:ua-product :chrome} + nil) + ) + +;; ----------------------------------------------------------------------------- +;; Safari Stacktrace + +(defn safari-st-el->frame + [repl-env st-el opts] + (let [[function flc] (if (re-find #"@" st-el) + (string/split st-el #"@") + [nil st-el]) + [file line column] (parse-file-line-column flc)] + (if (and file function line column) + {:file (parse-file repl-env file opts) + :function function + :line line + :column column} + (when-not (string/blank? function) + {:file nil + :function (string/trim function) + :line nil + :column nil})))) + +(comment + (safari-st-el->frame {:host "localhost" :port 9000} + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17" {}) + + (safari-st-el->frame {:host "localhost" :port 9000} + "cljs$core$seq@http://localhost:9000/js/cljs/core.js:4259:17" {:asset-path "js"}) + ) + +(defmethod parse-stacktrace :safari + [repl-env st err opts] + (->> st + string/split-lines + (drop-while #(starts-with? % "Error")) + (take-while #(not (starts-with? % "eval code"))) + (remove string/blank?) + (map #(safari-st-el->frame repl-env % opts)) + (remove nil?) + vec)) + +(comment + (parse-stacktrace nil + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17 +cljs$core$first@http://localhost:9000/out/cljs/core.js:4289:22 +cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5357:39 +http://localhost:9000/out/cljs/core.js:16972:92 +http://localhost:9000/out/cljs/core.js:16973:3 +http://localhost:9000/out/cljs/core.js:10982:133 +sval@http://localhost:9000/out/cljs/core.js:10983:3 +cljs$core$ISeqable$_seq$arity$1@http://localhost:9000/out/cljs/core.js:11074:14 +cljs$core$seq@http://localhost:9000/out/cljs/core.js:4240:44 +cljs$core$pr_sequential_writer@http://localhost:9000/out/cljs/core.js:28707:17 +cljs$core$IPrintWithWriter$_pr_writer$arity$3@http://localhost:9000/out/cljs/core.js:29386:38 +cljs$core$pr_writer_impl@http://localhost:9000/out/cljs/core.js:28912:57 +cljs$core$pr_writer@http://localhost:9000/out/cljs/core.js:29011:32 +cljs$core$pr_seq_writer@http://localhost:9000/out/cljs/core.js:29015:20 +cljs$core$pr_sb_with_opts@http://localhost:9000/out/cljs/core.js:29078:24 +cljs$core$pr_str_with_opts@http://localhost:9000/out/cljs/core.js:29092:48 +cljs$core$pr_str__delegate@http://localhost:9000/out/cljs/core.js:29130:34 +cljs$core$pr_str@http://localhost:9000/out/cljs/core.js:29139:39 +eval code +eval@[native code] +http://localhost:9000/out/clojure/browser/repl.js:23:271 +clojure$browser$repl$evaluate_javascript@http://localhost:9000/out/clojure/browser/repl.js:26:4 +http://localhost:9000/out/clojure/browser/repl.js:121:173 +deliver@http://localhost:9000/out/goog/messaging/abstractchannel.js:142:21 +xpcDeliver@http://localhost:9000/out/goog/net/xpc/crosspagechannel.js:733:19 +messageReceived_@http://localhost:9000/out/goog/net/xpc/nativemessagingtransport.js:321:23 +fireListener@http://localhost:9000/out/goog/events/events.js:741:25 +handleBrowserEvent_@http://localhost:9000/out/goog/events/events.js:862:34 +http://localhost:9000/out/goog/events/events.js:276:42" + {:ua-product :safari} + nil) + ) + +;; ----------------------------------------------------------------------------- +;; Firefox Stacktrace + +(defn firefox-clean-function [f] + (as-> f f + (cond + (string/blank? f) nil + (not= (.indexOf f " f + (string/replace #"<" "") + (string/replace #"\/" "")))) + +(defn firefox-st-el->frame + [repl-env st-el opts] + (let [[function flc] (if (re-find #"@" st-el) + (string/split st-el #"@") + [nil st-el]) + [file line column] (parse-file-line-column flc)] + (if (and file function line column) + {:file (parse-file repl-env file opts) + :function (firefox-clean-function function) + :line line + :column column} + (when-not (string/blank? function) + {:file nil + :function (firefox-clean-function function) + :line nil + :column nil})))) + +(comment + (firefox-st-el->frame {:host "localhost" :port 9000} + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4258:8" {}) + + (firefox-st-el->frame {:host "localhost" :port 9000} + "cljs.core.mapframe {:host "localhost" :port 9000} + "cljs.core.mapframe {:host "localhost" :port 9000} + "cljs.core.pr_strframe {:host "localhost" :port 9000} + "cljs.core.pr_str> st + string/split-lines + (drop-while #(starts-with? % "Error")) + (take-while #(= (.indexOf % "> eval") -1)) + (remove string/blank?) + (map #(firefox-st-el->frame repl-env % opts)) + (remove nil?) + vec)) + +(comment + (parse-stacktrace {:host "localhost" :port 9000} + "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4258:8 +cljs$core$first@http://localhost:9000/out/cljs/core.js:4288:9 +cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5356:24 +cljs.core.map eval:1:25 +@http://localhost:9000/out/clojure/browser/repl.js line 23 > eval:1:2 +clojure$browser$repl$evaluate_javascript/result<@http://localhost:9000/out/clojure/browser/repl.js:23:267 +clojure$browser$repl$evaluate_javascript@http://localhost:9000/out/clojure/browser/repl.js:23:15 +clojure$browser$repl$connect/ Date: Sat, 25 Jul 2015 18:03:32 -0500 Subject: [PATCH 1412/4033] add cljs.stacktrace/string->regex to eliminate platform differences --- src/main/cljs/cljs/stacktrace.cljc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 54133b584..5e11b87d6 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -30,6 +30,10 @@ #?(:clj (.endsWith s0 s1) :cljs (gstring/endsWith s0 s1))) +(defn string->regex [s] + #?(:clj (Pattern/compile s) + :cljs (RegExp. s))) + (defmethod parse-stacktrace :default [repl-env st err opts] st) @@ -56,12 +60,12 @@ "Given a browser file url convert it into a relative path that can be used to locate the original source." [{:keys [host host-port port] :as repl-env} file {:keys [asset-path] :as opts}] - (let [base-url-pattern (Pattern/compile (str "http://" host ":" (or host-port port) "/"))] + (let [base-url-pattern (string->regex (str "http://" host ":" (or host-port port) "/"))] (if (re-find base-url-pattern file) (-> file (string/replace base-url-pattern "") (string/replace - (Pattern/compile + (string->regex ;; if :asset-path specified drop leading slash (str "^" (or (and asset-path (string/replace asset-path #"^/" "")) (util/output-directory opts)) "/")) From 0fa91b5fcd41800d41efe3d4d10e3deec0889e50 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jul 2015 18:06:58 -0500 Subject: [PATCH 1413/4033] cljs.stacktrace, remove reliance on util ns if used from CLJS --- src/main/cljs/cljs/stacktrace.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 5e11b87d6..b4ac1576d 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -34,6 +34,10 @@ #?(:clj (Pattern/compile s) :cljs (RegExp. s))) +(defn output-directory [opts] + #?(:clj (util/output-directory opts) + :cljs (or (:output-dir opts) "out"))) + (defmethod parse-stacktrace :default [repl-env st err opts] st) @@ -68,7 +72,7 @@ (string->regex ;; if :asset-path specified drop leading slash (str "^" (or (and asset-path (string/replace asset-path #"^/" "")) - (util/output-directory opts)) "/")) + (output-directory opts)) "/")) "")) (if-let [asset-root (:asset-root opts)] (string/replace file asset-root "") From 30e01314e3f51d387370dbb566545ce99a352be9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jul 2015 18:20:35 -0500 Subject: [PATCH 1414/4033] stacktrace parsing works in ClojureScript --- src/main/cljs/cljs/stacktrace.cljc | 6 +++--- src/main/clojure/cljs/repl/browser.clj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index b4ac1576d..cd82c799f 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -32,7 +32,7 @@ (defn string->regex [s] #?(:clj (Pattern/compile s) - :cljs (RegExp. s))) + :cljs (js/RegExp. s))) (defn output-directory [opts] #?(:clj (util/output-directory opts) @@ -206,7 +206,7 @@ vec)) (comment - (parse-stacktrace nil + (parse-stacktrace {:host "localhost" :port 9000} "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17 cljs$core$first@http://localhost:9000/out/cljs/core.js:4289:22 cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5357:39 @@ -253,7 +253,7 @@ http://localhost:9000/out/goog/events/events.js:276:42" :else f) (-> f (string/replace #"<" "") - (string/replace #"\/" "")))) + (string/replace #?(:clj #"\/" :cljs (js/RegExp. "\\/")) "")))) (defn firefox-st-el->frame [repl-env st-el opts] diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index d0a1f5fa7..57dfd67f9 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -376,7 +376,7 @@ vec)) (comment - (parse-stacktrace nil + (parse-stacktrace {:host "localhost" :port 9000} "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17 cljs$core$first@http://localhost:9000/out/cljs/core.js:4289:22 cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5357:39 From 99ffd20ff88630986325c1a0b59db261ddf29b40 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jul 2015 18:40:26 -0500 Subject: [PATCH 1415/4033] cljs.repl.browser now relies on cljs.stacktrace for stacktrace parsing --- src/main/clojure/cljs/repl/browser.clj | 306 +------------------------ 1 file changed, 3 insertions(+), 303 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 57dfd67f9..beb124ef2 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -16,7 +16,8 @@ [cljs.env :as env] [cljs.closure :as cljsc] [cljs.repl :as repl] - [cljs.repl.server :as server]) + [cljs.repl.server :as server] + [cljs.stacktrace :as st]) (:import [java.util.regex Pattern] [java.util.concurrent Executors])) @@ -203,307 +204,6 @@ [repl-env provides url] (browser-eval (slurp url))) -;; ============================================================================= -;; Stracktrace parsing - -(defmulti parse-stacktrace (fn [repl-env st err opts] (:ua-product err))) - -(defmethod parse-stacktrace :default - [repl-env st err opts] st) - -(defn parse-file-line-column [flc] - (let [xs (string/split flc #":") - [pre [line column]] - (reduce - (fn [[pre post] [x i]] - (if (<= i 2) - [pre (conj post x)] - [(conj pre x) post])) - [[] []] (map vector xs (range (count xs) 0 -1))) - file (string/join ":" pre)] - [(cond-> file - (.startsWith file "(") (string/replace "(" "")) - (Long/parseLong - (cond-> line - (.endsWith line ")") (string/replace ")" ""))) - (Long/parseLong - (cond-> column - (.endsWith column ")") (string/replace ")" "")))])) - -(defn parse-file - "Given a browser file url convert it into a relative path that can be used - to locate the original source." - [{:keys [host host-port port] :as repl-env} file {:keys [asset-path] :as opts}] - (let [base-url-pattern (Pattern/compile (str "http://" host ":" (or host-port port) "/"))] - (if (re-find base-url-pattern file) - (-> file - (string/replace base-url-pattern "") - (string/replace - (Pattern/compile - ;; if :asset-path specified drop leading slash - (str "^" (or (and asset-path (string/replace asset-path #"^/" "")) - (util/output-directory opts)) "/")) - "")) - (if-let [asset-root (:asset-root opts)] - (string/replace file asset-root "") - (throw - (ex-info (str "Could not relativize URL " file) - {:type :parse-stacktrace - :reason :relativize-url})))))) - -;; ----------------------------------------------------------------------------- -;; Chrome Stacktrace - -(defn chrome-st-el->frame - [repl-env st-el opts] - (let [xs (-> st-el - (string/replace #"\s+at\s+" "") - (string/split #"\s+")) - [function flc] (if (== (count xs) 1) - [nil (first xs)] - [(first xs) (last xs)]) - [file line column] (parse-file-line-column flc)] - (if (and file function line column) - {:file (parse-file repl-env file opts) - :function (string/replace function #"Object\." "") - :line line - :column column} - (when-not (string/blank? function) - {:file nil - :function (string/replace function #"Object\." "") - :line nil - :column nil})))) - -(comment - (chrome-st-el->frame {:host "localhost" :port 9000} - "\tat cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34)" {}) - ) - -(defmethod parse-stacktrace :chrome - [repl-env st err opts] - (->> st - string/split-lines - (drop-while #(.startsWith % "Error")) - (take-while #(not (.startsWith % " at eval"))) - (map #(chrome-st-el->frame repl-env % opts)) - (remove nil?) - vec)) - -(comment - (parse-stacktrace {:host "localhost" :port 9000} - "Error: 1 is not ISeqable - at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4258:8) - at Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4288:19) - at cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5356:34) - at http://localhost:9000/out/cljs/core.js:16971:89 - at cljs.core.map.cljs$core$map__2 (http://localhost:9000/out/cljs/core.js:16972:3) - at http://localhost:9000/out/cljs/core.js:10981:129 - at cljs.core.LazySeq.sval (http://localhost:9000/out/cljs/core.js:10982:3) - at cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (http://localhost:9000/out/cljs/core.js:11073:10) - at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4239:13) - at Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/out/cljs/core.js:28706:14)" - {:ua-product :chrome} - nil) - - (parse-stacktrace {:host "localhost" :port 9000} - "Error: 1 is not ISeqable - at Object.cljs$core$seq [as seq] (http://localhost:9000/js/cljs/core.js:4258:8) - at Object.cljs$core$first [as first] (http://localhost:9000/js/cljs/core.js:4288:19) - at cljs$core$ffirst (http://localhost:9000/js/cljs/core.js:5356:34) - at http://localhost:9000/js/cljs/core.js:16971:89 - at cljs.core.map.cljs$core$map__2 (http://localhost:9000/js/cljs/core.js:16972:3) - at http://localhost:9000/js/cljs/core.js:10981:129 - at cljs.core.LazySeq.sval (http://localhost:9000/js/cljs/core.js:10982:3) - at cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (http://localhost:9000/js/cljs/core.js:11073:10) - at Object.cljs$core$seq [as seq] (http://localhost:9000/js/cljs/core.js:4239:13) - at Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (http://localhost:9000/js/cljs/core.js:28706:14)" - {:ua-product :chrome} - {:asset-path "/js"}) - - (parse-stacktrace {:host "localhost" :port 9000} - "Error: 1 is not ISeqable - at Object.cljs$core$seq [as seq] (http://localhost:9000/out/cljs/core.js:4259:8) - at Object.cljs$core$first [as first] (http://localhost:9000/out/cljs/core.js:4289:19) - at cljs$core$ffirst (http://localhost:9000/out/cljs/core.js:5357:18) - at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :1:106) - at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :9:3) - at eval (eval at (http://localhost:9000/out/clojure/browser/repl.js:23:272), :14:4) - at http://localhost:9000/out/clojure/browser/repl.js:23:267 - at clojure$browser$repl$evaluate_javascript (http://localhost:9000/out/clojure/browser/repl.js:26:4) - at Object.callback (http://localhost:9000/out/clojure/browser/repl.js:121:169) - at goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)" - {:ua-product :chrome} - nil) - ) - -;; ----------------------------------------------------------------------------- -;; Safari Stacktrace - -(defn safari-st-el->frame - [repl-env st-el opts] - (let [[function flc] (if (re-find #"@" st-el) - (string/split st-el #"@") - [nil st-el]) - [file line column] (parse-file-line-column flc)] - (if (and file function line column) - {:file (parse-file repl-env file opts) - :function function - :line line - :column column} - (when-not (string/blank? function) - {:file nil - :function (string/trim function) - :line nil - :column nil})))) - -(comment - (safari-st-el->frame {:host "localhost" :port 9000} - "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17" {}) - - (safari-st-el->frame {:host "localhost" :port 9000} - "cljs$core$seq@http://localhost:9000/js/cljs/core.js:4259:17" {:asset-path "js"}) - ) - -(defmethod parse-stacktrace :safari - [repl-env st err opts] - (->> st - string/split-lines - (drop-while #(.startsWith % "Error")) - (take-while #(not (.startsWith % "eval code"))) - (remove string/blank?) - (map #(safari-st-el->frame repl-env % opts)) - (remove nil?) - vec)) - -(comment - (parse-stacktrace {:host "localhost" :port 9000} - "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17 -cljs$core$first@http://localhost:9000/out/cljs/core.js:4289:22 -cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5357:39 -http://localhost:9000/out/cljs/core.js:16972:92 -http://localhost:9000/out/cljs/core.js:16973:3 -http://localhost:9000/out/cljs/core.js:10982:133 -sval@http://localhost:9000/out/cljs/core.js:10983:3 -cljs$core$ISeqable$_seq$arity$1@http://localhost:9000/out/cljs/core.js:11074:14 -cljs$core$seq@http://localhost:9000/out/cljs/core.js:4240:44 -cljs$core$pr_sequential_writer@http://localhost:9000/out/cljs/core.js:28707:17 -cljs$core$IPrintWithWriter$_pr_writer$arity$3@http://localhost:9000/out/cljs/core.js:29386:38 -cljs$core$pr_writer_impl@http://localhost:9000/out/cljs/core.js:28912:57 -cljs$core$pr_writer@http://localhost:9000/out/cljs/core.js:29011:32 -cljs$core$pr_seq_writer@http://localhost:9000/out/cljs/core.js:29015:20 -cljs$core$pr_sb_with_opts@http://localhost:9000/out/cljs/core.js:29078:24 -cljs$core$pr_str_with_opts@http://localhost:9000/out/cljs/core.js:29092:48 -cljs$core$pr_str__delegate@http://localhost:9000/out/cljs/core.js:29130:34 -cljs$core$pr_str@http://localhost:9000/out/cljs/core.js:29139:39 -eval code -eval@[native code] -http://localhost:9000/out/clojure/browser/repl.js:23:271 -clojure$browser$repl$evaluate_javascript@http://localhost:9000/out/clojure/browser/repl.js:26:4 -http://localhost:9000/out/clojure/browser/repl.js:121:173 -deliver@http://localhost:9000/out/goog/messaging/abstractchannel.js:142:21 -xpcDeliver@http://localhost:9000/out/goog/net/xpc/crosspagechannel.js:733:19 -messageReceived_@http://localhost:9000/out/goog/net/xpc/nativemessagingtransport.js:321:23 -fireListener@http://localhost:9000/out/goog/events/events.js:741:25 -handleBrowserEvent_@http://localhost:9000/out/goog/events/events.js:862:34 -http://localhost:9000/out/goog/events/events.js:276:42" - {:ua-product :safari} - nil) - ) - -;; ----------------------------------------------------------------------------- -;; Firefox Stacktrace - -(defn firefox-clean-function [f] - (as-> f f - (cond - (string/blank? f) nil - (not= (.indexOf f " f - (string/replace #"<" "") - (string/replace #"\/" "")))) - -(defn firefox-st-el->frame - [repl-env st-el opts] - (let [[function flc] (if (re-find #"@" st-el) - (string/split st-el #"@") - [nil st-el]) - [file line column] (parse-file-line-column flc)] - (if (and file function line column) - {:file (parse-file repl-env file opts) - :function (firefox-clean-function function) - :line line - :column column} - (when-not (string/blank? function) - {:file nil - :function (firefox-clean-function function) - :line nil - :column nil})))) - -(comment - (firefox-st-el->frame {:host "localhost" :port 9000} - "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4258:8" {}) - - (firefox-st-el->frame {:host "localhost" :port 9000} - "cljs.core.mapframe {:host "localhost" :port 9000} - "cljs.core.mapframe {:host "localhost" :port 9000} - "cljs.core.pr_strframe {:host "localhost" :port 9000} - "cljs.core.pr_str> st - string/split-lines - (drop-while #(.startsWith % "Error")) - (take-while #(= (.indexOf % "> eval") -1)) - (remove string/blank?) - (map #(firefox-st-el->frame repl-env % opts)) - (remove nil?) - vec)) - -(comment - (parse-stacktrace {:host "localhost" :port 9000} - "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4258:8 -cljs$core$first@http://localhost:9000/out/cljs/core.js:4288:9 -cljs$core$ffirst@http://localhost:9000/out/cljs/core.js:5356:24 -cljs.core.map eval:1:25 -@http://localhost:9000/out/clojure/browser/repl.js line 23 > eval:1:2 -clojure$browser$repl$evaluate_javascript/result<@http://localhost:9000/out/clojure/browser/repl.js:23:267 -clojure$browser$repl$evaluate_javascript@http://localhost:9000/out/clojure/browser/repl.js:23:15 -clojure$browser$repl$connect/ Date: Sat, 25 Jul 2015 19:11:10 -0500 Subject: [PATCH 1416/4033] helper to invert reverse source maps --- src/main/cljs/cljs/source_map.cljs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index a0e8d7cb9..75362dd8f 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -278,3 +278,28 @@ (recur (next line-map-seq) (assoc new-lines line new-cols))) new-lines))) + +;; ----------------------------------------------------------------------------- +;; Reverse Source Map Inversion + +(defn invert-reverse-map + "Given a ClojureScript to JavaScript source map, invert it. Useful when + mapping JavaScript stack traces when environment support is unavailable." + [reverse-map] + (let [inverted (atom (sorted-map))] + (doseq [[line columns] reverse-map] + (doseq [[column column-info] columns] + (doseq [{:keys [gline gcol name]} column-info] + (swap! inverted update-in [gline] + (fnil (fn [columns] + (update-in columns [column] (fnil conj []) + {:line line :col column :name name})) + (sorted-map)))))) + @inverted)) + +(comment + (invert-reverse-map + {1 + {1 [{:gcol 0, :gline 0, :name "cljs.core/map"}], + 5 [{:gcol 24, :gline 0, :name "cljs.core/inc"}]}}) + ) \ No newline at end of file From ae73cda5947dec1dbd7575ac7bf8973ac65c5127 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jul 2015 19:29:50 -0500 Subject: [PATCH 1417/4033] store normalized source maps in the compiler state --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2ae0e2b8b..e60d5b33a 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -122,7 +122,7 @@ :file file :sources-content [source]})] (when (:verbose opts) (debug-prn json)) (swap! state assoc-in - [:source-maps name] (:source-map sm-data)) + [:source-maps name] (sm/invert-reverse-map (:source-map sm-data))) (.append sb (str "\n//# sourceURL=" file "\n//# sourceMappingURL=data:application/json;base64," From de7520306439a4227d70b84c99e51d4b44ba02f2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 11:45:43 -0500 Subject: [PATCH 1418/4033] move rhino stacktrace parsing into cljs.stacktrace --- src/main/cljs/cljs/stacktrace.cljc | 65 +++++++++++++++++++++++++++- src/main/clojure/cljs/repl/rhino.clj | 22 +++------- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index cd82c799f..99d1de66a 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -10,7 +10,8 @@ (:require #?(:clj [cljs.util :as util] :cljs [goog.string :as gstring]) [clojure.string :as string]) - #?(:clj (:import [java.util.regex Pattern]))) + #?(:clj (:import [java.util.regex Pattern] + [java.io File]))) (defmulti parse-stacktrace (fn [repl-env st err opts] (:ua-product err))) @@ -333,4 +334,66 @@ goog.events.handleBrowserEvent_@http://localhost:9000/out/goog/events/events.js: goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" {:ua-product :firefox} nil) + ) + +;; ----------------------------------------------------------------------------- +;; Rhino Stacktrace + +(defmethod parse-stacktrace :rhino + [repl-env st err {:keys [output-dir] :as opts}] + (letfn [(process-frame [frame-str] + (when-not (or (string/blank? frame-str) + (== -1 (.indexOf frame-str "\tat"))) + (let [[file-side line-fn-side] (string/split frame-str #":") + file (string/replace file-side #"\s+at\s+" "") + [line function] (string/split line-fn-side #"\s+")] + {:file (string/replace file + (str output-dir + #?(:clj File/separator :cljs "/")) + "") + :function (when function + (-> function + (string/replace "(" "") + (string/replace ")" ""))) + :line (when (and line (not (string/blank? line))) + (parse-int line)) + :column 0})))] + (->> (string/split st #"\n") + (map process-frame) + (remove nil?) + vec))) + +(comment + (parse-stacktrace {} + "\tat .cljs_rhino_repl/goog/../cljs/core.js:4215 (seq) + \tat .cljs_rhino_repl/goog/../cljs/core.js:4245 (first) + \tat .cljs_rhino_repl/goog/../cljs/core.js:5295 (ffirst) + \tat :1 + \tat :1" + {:ua-product :rhino} + {:output-dir ".cljs_rhino_repl"}) + + (parse-stacktrace {} + "org.mozilla.javascript.JavaScriptException: Error: 1 is not ISeqable (.cljs_rhino_repl/goog/../cljs/core.js#3998) + \tat .cljs_rhino_repl/goog/../cljs/core.js:3998 (cljs$core$seq) + \tat .cljs_rhino_repl/goog/../cljs/core.js:4017 (cljs$core$first) + \tat .cljs_rhino_repl/goog/../cljs/core.js:5160 (cljs$core$ffirst) + \tat .cljs_rhino_repl/goog/../cljs/core.js:16005 + \tat .cljs_rhino_repl/goog/../cljs/core.js:16004 + \tat .cljs_rhino_repl/goog/../cljs/core.js:10243 + \tat .cljs_rhino_repl/goog/../cljs/core.js:10334 + \tat .cljs_rhino_repl/goog/../cljs/core.js:3979 (cljs$core$seq) + \tat .cljs_rhino_repl/goog/../cljs/core.js:28083 (cljs$core$pr_sequential_writer) + \tat .cljs_rhino_repl/goog/../cljs/core.js:28811 + \tat .cljs_rhino_repl/goog/../cljs/core.js:28267 (cljs$core$pr_writer_impl) + \tat .cljs_rhino_repl/goog/../cljs/core.js:28349 (cljs$core$pr_writer) + \tat .cljs_rhino_repl/goog/../cljs/core.js:28353 (cljs$core$pr_seq_writer) + \tat .cljs_rhino_repl/goog/../cljs/core.js:28416 (cljs$core$pr_sb_with_opts) + \tat .cljs_rhino_repl/goog/../cljs/core.js:28430 (cljs$core$pr_str_with_opts) + \tat .cljs_rhino_repl/goog/../cljs/core.js:28524 + \tat .cljs_rhino_repl/goog/../cljs/core.js:28520 (cljs$core$pr_str) + at :1 + " + {:ua-product :rhino} + {:output-dir ".cljs_rhino_repl"}) ) \ No newline at end of file diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj index d144af37d..a7ad5cf21 100644 --- a/src/main/clojure/cljs/repl/rhino.clj +++ b/src/main/clojure/cljs/repl/rhino.clj @@ -14,7 +14,8 @@ [cljs.closure :as closure] [cljs.analyzer :as ana] [cljs.repl :as repl] - [cljs.util :as util]) + [cljs.util :as util] + [cljs.stacktrace :as st]) (:import [java.io File Reader] [org.mozilla.javascript Context ScriptableObject RhinoException Undefined])) @@ -172,22 +173,9 @@ {:output-dir ".cljs_rhino_repl" :wrap wrap-fn}) repl/IParseStacktrace - (-parse-stacktrace [this frames-str ret {output-dir :output-dir}] - (vec - (map - (fn [frame-str] - (let [[file-side line-fn-side] (string/split frame-str #":") - file (string/replace file-side #"\s+at\s+" "") - [line function] (string/split line-fn-side #"\s+")] - {:file (string/replace file (str output-dir File/separator) "") - :function (when function - (-> function - (string/replace "(" "") - (string/replace ")" ""))) - :line (when line - (Integer/parseInt line)) - :column 0})) - (string/split frames-str #"\n")))) + (-parse-stacktrace [this frames-str ret opts] + (st/parse-stacktrace this frames-str + (assoc ret :ua-product :rhino) opts)) repl/IGetError (-get-error [this e env opts] (let [{:keys [scope]} this From 88b096f8593151ef07205395aeca3c3d76989097 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 12:01:18 -0500 Subject: [PATCH 1419/4033] move Nashorn stacktrace parsing into cljs.stacktrace --- src/main/cljs/cljs/stacktrace.cljc | 50 ++++++++++++++++++++++++++ src/main/clojure/cljs/repl/nashorn.clj | 20 +++-------- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 99d1de66a..29eb0c810 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -396,4 +396,54 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" " {:ua-product :rhino} {:output-dir ".cljs_rhino_repl"}) + ) + +;; ----------------------------------------------------------------------------- +;; Nashorn Stacktrace + +(defmethod parse-stacktrace :nashorn + [repl-env st err {:keys [output-dir] :as opts}] + (letfn [(process-frame [frame-str] + (when-not (or (string/blank? frame-str) + (== -1 (.indexOf frame-str "\tat"))) + (let [frame-str (string/replace frame-str #"\s+at\s+" "") + [function file-and-line] (string/split frame-str #"\s+") + [file-part line-part] (string/split file-and-line #":")] + {:file (string/replace (.substring file-part 1) + (str output-dir File/separator) "") + :function function + :line (when (and line-part (not (string/blank? line-part))) + (parse-int + (.substring line-part 0 + (dec (count line-part))))) + :column 0})))] + (->> (string/split st #"\n") + (map process-frame) + (remove nil?) + vec))) + +(comment + (parse-stacktrace {} + "Error: 1 is not ISeqable + \tat cljs$core$seq (.cljs_nashorn_repl/goog/../cljs/core.js:3998) + \tat cljs$core$first (.cljs_nashorn_repl/goog/../cljs/core.js:4017) + \tat cljs$core$ffirst (.cljs_nashorn_repl/goog/../cljs/core.js:5160) + \tat (.cljs_nashorn_repl/goog/../cljs/core.js:16005) + \tat (.cljs_nashorn_repl/goog/../cljs/core.js:16004) + \tat sval (.cljs_nashorn_repl/goog/../cljs/core.js:10243) + \tat cljs$core$ISeqable$_seq$arity$1-6 (.cljs_nashorn_repl/goog/../cljs/core.js:10334) + \tat cljs$core$seq (.cljs_nashorn_repl/goog/../cljs/core.js:3979) + \tat cljs$core$pr_sequential_writer (.cljs_nashorn_repl/goog/../cljs/core.js:28083) + \tat cljs$core$IPrintWithWriter$_pr_writer$arity$3-5 (.cljs_nashorn_repl/goog/../cljs/core.js:28811) + \tat cljs$core$pr_writer_impl (.cljs_nashorn_repl/goog/../cljs/core.js:28267) + \tat cljs$core$pr_writer (.cljs_nashorn_repl/goog/../cljs/core.js:28349) + \tat cljs$core$pr_seq_writer (.cljs_nashorn_repl/goog/../cljs/core.js:28353) + \tat cljs$core$pr_sb_with_opts (.cljs_nashorn_repl/goog/../cljs/core.js:28416) + \tat cljs$core$pr_str_with_opts (.cljs_nashorn_repl/goog/../cljs/core.js:28430) + \tat cljs$core$IFn$_invoke$arity$variadic-71 (.cljs_nashorn_repl/goog/../cljs/core.js:28524) + \tat cljs$core$pr_str (.cljs_nashorn_repl/goog/../cljs/core.js:28520) + \tat (:1) + \tat (:1)\n" + {:ua-product :nashorn} + {:output-dir ".cljs_nashorn_repl"}) ) \ No newline at end of file diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index b7bd1ec83..41d3b4d5c 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -15,7 +15,8 @@ [cljs.util :as util] [cljs.repl :as repl] [cljs.compiler :as comp] - [cljs.closure :as closure]) + [cljs.closure :as closure] + [cljs.stacktrace :as st]) (:import [java.io File] [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory] [com.google.common.base Throwables])) @@ -157,20 +158,9 @@ (load-ns engine ns)) (-tear-down [this]) repl/IParseStacktrace - (-parse-stacktrace [this frames-str ret {output-dir :output-dir}] - (vec - (map - (fn [frame-str] - (let [frame-str (string/replace frame-str #"\s+at\s+" "") - [function file-and-line] (string/split frame-str #"\s+") - [file-part line-part] (string/split file-and-line #":")] - {:file (string/replace (.substring file-part 1) - (str output-dir File/separator) "") - :function function - :line (Integer/parseInt - (.substring line-part 0 (dec (.length line-part)))) - :column 0})) - (string/split frames-str #"\n")))) + (-parse-stacktrace [this frames-str ret opts] + (st/parse-stacktrace this frames-str + (assoc ret :ua-product :nashorn) opts)) repl/IParseError (-parse-error [_ err _] (update-in err [:stacktrace] From b5f085c00f3f1f760da7b6bf37a57f1a0aa8504a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 14:05:21 -0500 Subject: [PATCH 1420/4033] update cljs.repl stacktrace mapping code comments --- src/main/clojure/cljs/repl.cljc | 88 ++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 767480a3c..b5b2167c7 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -228,16 +228,16 @@ (some-> (js-src->cljs-src f) ana/parse-ns :ns))] (when ns (as-> @env/*compiler* compiler-env - (let [t (util/last-modified smf)] - (if (or (and (= ns 'cljs.core) - (nil? (get-in compiler-env [::source-maps ns]))) - (and (not= ns 'cljs.core) - (> t (get-in compiler-env [::source-maps ns :last-modified] 0)))) - (swap! env/*compiler* assoc-in [::source-maps ns] - {:last-modified t - :source-map (sm/decode (json/read-str (slurp smf) :key-fn keyword))}) - compiler-env)) - (get-in compiler-env [::source-maps ns :source-map])))))) + (let [t (util/last-modified smf)] + (if (or (and (= ns 'cljs.core) + (nil? (get-in compiler-env [::source-maps ns]))) + (and (not= ns 'cljs.core) + (> t (get-in compiler-env [::source-maps ns :last-modified] 0)))) + (swap! env/*compiler* assoc-in [::source-maps ns] + {:last-modified t + :source-map (sm/decode (json/read-str (slurp smf) :key-fn keyword))}) + compiler-env)) + (get-in compiler-env [::source-maps ns :source-map])))))) (defn ns-info "Given a path to a js source file return the ns info for the corresponding @@ -351,25 +351,30 @@ "(" file (when line (str ":" line)) (when column (str ":" column)) ")")))))) (comment + (def st (env/default-compiler-env)) + (cljsc/build "samples/hello/src" {:optimizations :none :output-dir "samples/hello/out" :output-to "samples/hello/out/hello.js" - :source-map true}) - - (mapped-stacktrace - [{:file "hello/core.js" - :function "first" - :line 2 - :column 1}] - {:output-dir "samples/hello/out"}) - - (print-mapped-stacktrace - [{:file "hello/core.js" - :function "first" - :line 2 - :column 1}] - {:output-dir "samples/hello/out"}) + :source-map true} + st) + + (env/with-compiler-env st + (mapped-stacktrace + [{:file "hello/core.js" + :function "first" + :line 6 + :column 0}] + {:output-dir "samples/hello/out"})) + + (env/with-compiler-env st + (print-mapped-stacktrace + [{:file "hello/core.js" + :function "first" + :line 6 + :column 0}] + {:output-dir "samples/hello/out"})) ;; URL example @@ -377,21 +382,24 @@ {:optimizations :none :output-dir "out" :output-to "out/hello.js" - :source-map true}) - - (mapped-stacktrace - [{:file "cljs/core.js" - :function "first" - :line 2 - :column 1}] - {:output-dir "out"}) - - (print-mapped-stacktrace - [{:file "cljs/core.js" - :function "first" - :line 2 - :column 1}] - {:output-dir "out"}) + :source-map true} + st) + + (env/with-compiler-env st + (mapped-stacktrace + [{:file "cljs/core.js" + :function "first" + :line 2 + :column 1}] + {:output-dir "out"})) + + (env/with-compiler-env st + (print-mapped-stacktrace + [{:file "cljs/core.js" + :function "first" + :line 2 + :column 1}] + {:output-dir "out"})) ) (defn- display-error From 252ec7a311f606e21faacebcad50f790e4c05bab Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 14:19:14 -0500 Subject: [PATCH 1421/4033] add cross platform stacktrace mapping to cljs.stacktrace --- src/main/cljs/cljs/stacktrace.cljc | 133 ++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 29eb0c810..db1ae99a1 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -7,9 +7,10 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.stacktrace - (:require #?(:clj [cljs.util :as util] - :cljs [goog.string :as gstring]) - [clojure.string :as string]) + (:require #?@(:clj [[cljs.util :as util] + [clojure.java.io :as io]] + :cljs [[goog.string :as gstring]]) + [clojure.string :as string]) #?(:clj (:import [java.util.regex Pattern] [java.io File]))) @@ -446,4 +447,130 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" \tat (:1)\n" {:ua-product :nashorn} {:output-dir ".cljs_nashorn_repl"}) + ) + +;; ----------------------------------------------------------------------------- +;; Stacktrace Mapping + +(defn mapped-line-column-call + "Given a cljs.source-map source map data structure map a generated line + and column back to the original line, column, and function called." + [source-map line column] + ;; source maps are 0 indexed for columns + ;; multiple segments may exist at column + ;; the last segment seems most accurate + (letfn [(get-best-column [columns column] + (last (or (get columns + (last (filter #(<= % (dec column)) + (sort (keys columns))))) + (second (first columns))))) + (adjust [mapped] + (vec (map #(%1 %2) [inc inc identity] mapped)))] + (let [default [line column nil]] + ;; source maps are 0 indexed for lines + (if-let [columns (get source-map (dec line))] + (adjust (map (get-best-column columns column) [:line :col :name])) + default)))) + +(defn mapped-frame + "Given opts and a canonicalized JavaScript stacktrace frame, return the + ClojureScript frame." + [{:keys [function file line column]} sm opts] + (let [no-source-file? (if-not file true (starts-with? file "<")) + [line' column' call] (mapped-line-column-call sm line column) + file' (if (ends-with? file ".js") + (str (subs file 0 (- (count file) 3)) ".cljs") + file)] + {:function function + :call call + :file (if no-source-file? + (str "NO_SOURCE_FILE" (when file (str " " file))) + file') + :line line' + :column column'})) + +(defn mapped-stacktrace + "Given a vector representing the canonicalized JavaScript stacktrace + return the ClojureScript stacktrace. The canonical stacktrace must be + in the form: + + [{:file + :function + :line + :column }*] + + :file must be a URL path (without protocol) relative to :output-dir or a + identifier delimited by angle brackets. The returned mapped stacktrace will + also contain :url entries to the original sources if it can be determined + from the classpath." + ([stacktrace sm] (mapped-stacktrace stacktrace sm nil)) + ([stacktrace sm opts] + (letfn [(call->function [x] + (if (:call x) + (hash-map :function (:call x)) + {})) + (call-merge [function call] + (merge-with + (fn [munged-fn-name unmunged-call-name] + (if (= munged-fn-name + (string/replace (munge unmunged-call-name) "." "$")) + unmunged-call-name + munged-fn-name)) + function call))] + (let [mapped-frames (map (memoize #(mapped-frame % sm opts)) stacktrace)] + ;; take each non-nil :call and optionally merge it into :function one-level + ;; up to avoid replacing with local symbols, we only replace munged name if + ;; we can munge call symbol back to it + (vec (map call-merge + (map #(dissoc % :call) mapped-frames) + (concat (rest (map call->function mapped-frames)) [{}]))))))) + +(defn mapped-stacktrace-str + "Given a vector representing the canonicalized JavaScript stacktrace + print the ClojureScript stacktrace. See mapped-stacktrace." + ([stacktrace sm] + (mapped-stacktrace-str stacktrace sm nil)) + ([stacktrace sm opts] + (with-out-str + (doseq [{:keys [function file line column]} + (mapped-stacktrace stacktrace sm opts)] + (println "\t" + (str (when function (str function " ")) + "(" file (when line (str ":" line)) + (when column (str ":" column)) ")")))))) + +(comment + (require '[cljs.closure :as cljsc] + '[clojure.data.json :as json] + '[cljs.source-map :as sm] + '[clojure.pprint :as pp]) + + (cljsc/build "samples/hello/src" + {:optimizations :none + :output-dir "samples/hello/out" + :output-to "samples/hello/out/hello.js" + :source-map true}) + + (def sm + (sm/decode + (json/read-str + (slurp "samples/hello/out/hello/core.js.map") + :key-fn keyword))) + + (pp/pprint sm) + + ;; maps to :line 5 :column 24 + (mapped-stacktrace + [{:file "hello/core.js" + :function "first" + :line 6 + :column 0}] + sm {:output-dir "samples/hello/out"}) + + (mapped-stacktrace-str + [{:file "hello/core.js" + :function "first" + :line 6 + :column 0}] + sm {:output-dir "samples/hello/out"}) ) \ No newline at end of file From d73c1b3cdc4f36f9d10c3a048e5f40dc18f80853 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 15:25:21 -0400 Subject: [PATCH 1422/4033] add invert-reverse-map to Clojure source map support --- src/main/clojure/cljs/source_map.clj | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/clojure/cljs/source_map.clj b/src/main/clojure/cljs/source_map.clj index a78a1c619..914fb3b87 100644 --- a/src/main/clojure/cljs/source_map.clj +++ b/src/main/clojure/cljs/source_map.clj @@ -309,6 +309,24 @@ (assoc new-lines line new-cols))) new-lines))) +;; ----------------------------------------------------------------------------- +;; Reverse Source Map Inversion + +(defn invert-reverse-map + "Given a ClojureScript to JavaScript source map, invert it. Useful when + mapping JavaScript stack traces when environment support is unavailable." + [reverse-map] + (let [inverted (atom (sorted-map))] + (doseq [[line columns] reverse-map] + (doseq [[column column-info] columns] + (doseq [{:keys [gline gcol name]} column-info] + (swap! inverted update-in [gline] + (fnil (fn [columns] + (update-in columns [column] (fnil conj []) + {:line line :col column :name name})) + (sorted-map)))))) + @inverted)) + (comment ;; INSTRUCTIONS: From 4cac5bf0dfc616d98ee69b79e0f5bf24b1ffacef Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 16:14:54 -0400 Subject: [PATCH 1423/4033] fix cljs.source-map/seg->map --- src/main/cljs/cljs/source_map.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index 75362dd8f..e4d4d1117 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -56,11 +56,11 @@ [seg source-map] (let [[gcol source line col name] seg] {:gcol gcol - :source (aget (.split (gobj/get source-map "sources" source) "?") 0) + :source (aget (gobj/get source-map "sources") source) :line line :col col :name (when-let [name (-> seg meta :name)] - (gobj/get source-map "names" name))})) + (aget (gobj/get source-map "names") name))})) (defn seg-combine "Combine a source map segment vector and a relative From 9cdab40708d62c407b5be5a10b71497eb870b2b2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 16:19:44 -0400 Subject: [PATCH 1424/4033] inline cljs.core source map JSON into empty compiler state --- src/main/cljs/cljs/js.clj | 8 ++++++-- src/main/cljs/cljs/js.cljs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.clj b/src/main/cljs/cljs/js.clj index 89b1a79c9..05a24eeed 100644 --- a/src/main/cljs/cljs/js.clj +++ b/src/main/cljs/cljs/js.clj @@ -9,7 +9,8 @@ (ns cljs.js (:require [cljs.env :as env] [cljs.env.macros :as menv] - [cljs.analyzer :as ana])) + [cljs.analyzer :as ana] + [clojure.java.io :as io])) (defmacro with-state [state & body] @@ -17,4 +18,7 @@ ~@body)) (defmacro dump-core [] - `(quote ~(get-in @env/*compiler* [::ana/namespaces 'cljs.core]))) \ No newline at end of file + `(quote ~(get-in @env/*compiler* [::ana/namespaces 'cljs.core]))) + +(defmacro dump-core-source-map-json [] + (slurp (io/resource "cljs/core.aot.js.map"))) \ No newline at end of file diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index e60d5b33a..38a36aca3 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.js - (:require-macros [cljs.js :refer [dump-core]] + (:require-macros [cljs.js :refer [dump-core dump-core-source-map-json]] [cljs.env.macros :as env]) (:require [clojure.string :as string] [cljs.env :as env] @@ -97,7 +97,11 @@ eval and eval-str." ([] (doto (env/default-compiler-env) - (swap! assoc-in [::ana/namespaces 'cljs.core] (dump-core)))) + (swap! + (fn [state] + (-> state + (assoc-in [::ana/namespaces 'cljs.core] (dump-core)) + (assoc :core-source-map-json (dump-core-source-map-json))))))) ([init] (doto (empty-state) (swap! init)))) From 3f2c52a6603c424ab46607da363f46f12a344d99 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 16:47:30 -0400 Subject: [PATCH 1425/4033] fix demunge, add test add cljs.js/file->ns --- src/main/cljs/cljs/core.cljs | 3 ++- src/main/cljs/cljs/js.cljs | 6 ++++++ src/test/cljs/cljs/core_test.cljs | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 187c61866..f9ad48a99 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9898,7 +9898,8 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn- demunge-str [munged-name] (let [r (js/RegExp. (demunge-pattern) "g") munged-name (if (gstring/endsWith munged-name "$") - (.substring munged-name 0 (dec (. munged-name -length))))] + (.substring munged-name 0 (dec (. munged-name -length))) + munged-name)] (loop [ret "" last-match-end 0] (if-let [match (.exec r munged-name)] (let [[x] match] diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 38a36aca3..2e87c2388 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -32,6 +32,12 @@ [ns-sym] (string/replace (ana/munge-path ns-sym) \. \/)) +(defn file->ns + [file] + (let [lib-name (subs (string/replace file "/" ".") + 0 (- (count file) 5))] + (symbol (demunge lib-name)))) + (defn atom? [x] (instance? Atom x)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d8149093a..1a5b690e6 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2875,6 +2875,10 @@ arguments))) []))) +(deftest test-munge-demunge + (is (= 'cljs.core/first? + (demunge (munge 'cljs.core/first?))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 64e95db8f2d5e0fd1eecc95a75e32efd83a70c22 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 17:10:38 -0400 Subject: [PATCH 1426/4033] browser REPL should provide cljs.user --- src/main/cljs/clojure/browser/repl.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index f9a3cbc3d..5de956a87 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -119,6 +119,8 @@ (set! (.-require__ js/goog) js/goog.require) ;; suppress useless Google Closure error about duplicate provides (set! (.-isProvided_ js/goog) (fn [name] false)) + ;; provide cljs.user + (goog/provide "cljs.user") (set! (.-writeScriptTag__ js/goog) (fn [src opt_sourceText] ;; the page is already loaded, we can no longer leverage document.write From fa0da846e6f30eec330fac9e2ac8aced2660d20f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 17:21:24 -0400 Subject: [PATCH 1427/4033] switch away from goog.provide as Closure checks for this, use goog/constructNamespace_ for now. While it would seem to make sense to just goog.provide cljs.user in cljs.core, this will likely cause trouble later when we support files without namespaces (which will likely all live under cljs.user) --- src/main/cljs/clojure/browser/repl.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 5de956a87..e9998189f 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -120,7 +120,7 @@ ;; suppress useless Google Closure error about duplicate provides (set! (.-isProvided_ js/goog) (fn [name] false)) ;; provide cljs.user - (goog/provide "cljs.user") + (goog/constructNamespace_ "cljs.user") (set! (.-writeScriptTag__ js/goog) (fn [src opt_sourceText] ;; the page is already loaded, we can no longer leverage document.write From 85846bffd9f272d4a77a4ae89f9b346fd358141d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 20:17:53 -0400 Subject: [PATCH 1428/4033] need to wrap cljs.js/eval-str *eval-fn* invoke in a try/catch --- src/main/cljs/cljs/js.cljs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2e87c2388..a736f5b93 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -659,7 +659,11 @@ [::ana/namespaces name])}] (when (:verbose opts) (debug-prn js-source)) - (cb {:ns ns :value (*eval-fn* evalm)}))))))))) + (let [res (try + {:ns ns :value (*eval-fn* evalm)} + (catch :default cause + (wrap-error (ana/error aenv "ERROR" cause))))] + (cb res)))))))))) (:*cljs-ns* bound-vars)))) (defn eval-str From b24b3543a2d20b03c0351b50f42ce16e2b9d173f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 26 Jul 2015 22:36:38 -0400 Subject: [PATCH 1429/4033] browser REPL needs to set *print-err-fn* --- src/main/clojure/cljs/repl/browser.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index beb124ef2..6dd032c06 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -142,6 +142,7 @@ (send-for-eval conn (cljsc/-compile '[(set! *print-fn* clojure.browser.repl/repl-print) + (set! *print-err-fn* clojure.browser.repl/repl-print) (set! *print-newline* true)] {}) identity)) From dbf10e5421058d0387a419b88c7da3ce10059268 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 05:53:08 -0400 Subject: [PATCH 1430/4033] set *print-err-fn* in Rhino & Nashron REPLs --- src/main/clojure/cljs/repl/nashorn.clj | 3 ++- src/main/clojure/cljs/repl/rhino.clj | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index 41d3b4d5c..d1fb12a7c 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -121,7 +121,8 @@ '(do (.require js/goog "cljs.core") (set! *print-newline* false) - (set! *print-fn* js/print))) + (set! *print-fn* js/print) + (set! *print-err-fn* js/print))) ;; monkey-patch goog.isProvided_ to suppress useless errors (repl/evaluate-form this env repl-filename '(set! js/goog.isProvided_ (fn [ns] false))) diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj index a7ad5cf21..ad9673772 100644 --- a/src/main/clojure/cljs/repl/rhino.clj +++ b/src/main/clojure/cljs/repl/rhino.clj @@ -124,6 +124,8 @@ (Context/javaToJS opts scope)) (ScriptableObject/putProperty scope "out" (Context/javaToJS *out* scope)) + (ScriptableObject/putProperty scope + "err" (Context/javaToJS *err* scope)) ;; define file loading, load goog.base, load repl deps (rhino-eval repl-env "bootjs" 1 bootjs) @@ -135,7 +137,8 @@ (repl/evaluate-form repl-env env "" '(do (.require js/goog "cljs.core") - (set! *print-fn* (fn [x] (.write js/out x))))) + (set! *print-fn* (fn [x] (.write js/out x))) + (set! *print-err-fn* (fn [x] (.write js/err x))))) ;; allow namespace reloading (repl/evaluate-form repl-env env "" From 0da3496f81afd979c2496d74b5c21f4df1bb1b2d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 07:16:05 -0400 Subject: [PATCH 1431/4033] support mutiple evaluations at REPL by attaching timestamp to inline source maps --- src/main/cljs/cljs/js.cljs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index a736f5b93..c9a7bef5a 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -120,23 +120,31 @@ :gen-col 0 :gen-line 0})) -(defn append-source-map [state name source sb sm-data opts] +(defn prefix [s pre] + (str pre s)) + +(defn append-source-map + [state name source sb sm-data {:keys [output-dir asset-path] :as opts}] (let [t (.valueOf (js/Date.)) smn (if name - (munge name) + (string/replace (munge (str name)) "." "/") (str "cljs-" t)) - src (str smn ".cljs") - file (str smn ".js") + ts (.valueOf (js/Date.)) + out (or output-dir asset-path) + src (cond-> (str smn ".cljs?rel=" ts) + out (prefix (str out "/"))) + file (cond-> (str smn ".js?rel=" ts) + out (prefix (str out "/"))) json (sm/encode {src (:source-map sm-data)} {:lines (+ (:gen-line sm-data) 3) - :file file :sources-content [source]})] + :file file :sources-content [source]})] (when (:verbose opts) (debug-prn json)) (swap! state assoc-in [:source-maps name] (sm/invert-reverse-map (:source-map sm-data))) (.append sb (str "\n//# sourceURL=" file "\n//# sourceMappingURL=data:application/json;base64," - (base64/encodeString json true))))) + (base64/encodeString json))))) ;; ----------------------------------------------------------------------------- ;; Analyze From 14ef9bb10ea12d112d1cef795c114b48fc05d595 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 07:29:20 -0400 Subject: [PATCH 1432/4033] remove dump-core-source-map-json. Not buying us anything in modern browsers. In JSC or similar context will want to either load individual source maps anyway (i.e. cljs.core isn't enough - tools.reader, compiler, analyzer, etc). --- src/main/cljs/cljs/js.clj | 3 --- src/main/cljs/cljs/js.cljs | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/js.clj b/src/main/cljs/cljs/js.clj index 05a24eeed..20b0480b0 100644 --- a/src/main/cljs/cljs/js.clj +++ b/src/main/cljs/cljs/js.clj @@ -19,6 +19,3 @@ (defmacro dump-core [] `(quote ~(get-in @env/*compiler* [::ana/namespaces 'cljs.core]))) - -(defmacro dump-core-source-map-json [] - (slurp (io/resource "cljs/core.aot.js.map"))) \ No newline at end of file diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index c9a7bef5a..8d297b8c2 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.js - (:require-macros [cljs.js :refer [dump-core dump-core-source-map-json]] + (:require-macros [cljs.js :refer [dump-core]] [cljs.env.macros :as env]) (:require [clojure.string :as string] [cljs.env :as env] @@ -106,8 +106,7 @@ (swap! (fn [state] (-> state - (assoc-in [::ana/namespaces 'cljs.core] (dump-core)) - (assoc :core-source-map-json (dump-core-source-map-json))))))) + (assoc-in [::ana/namespaces 'cljs.core] (dump-core))))))) ([init] (doto (empty-state) (swap! init)))) From f2fd1c0c7d1900dd44debfc8ccd6943ae952ed05 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 08:34:20 -0400 Subject: [PATCH 1433/4033] exclude core compile from build api tests --- src/test/clojure/cljs/build_api_tests.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index b9306bf42..17106cd9a 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -1,4 +1,5 @@ (ns cljs.build-api-tests + (:refer-clojure :exclude [compile]) (:use cljs.build.api) (:use clojure.test) (:require [cljs.env :as env] From 8b70d7541ffd61789c6e95b1d58e471e12d310e0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 08:39:51 -0400 Subject: [PATCH 1434/4033] CLJS-1368: cljs.analyzer tests broken, undeclared-var warning doesn't work loaded-ns? incorrectly considered ClojureScript namespaces. Only consider JavaScript libraries that cannot curretnly be analyzed. --- src/main/clojure/cljs/analyzer.cljc | 33 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d19e54312..b5e967fe8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -549,11 +549,12 @@ (fn [env prefix suffix] (warning :undeclared-var env {:prefix prefix :suffix suffix}))) -(defn loaded-ns? +(defn loaded-js-ns? + "Check if a JavaScript namespace has been loaded. JavaScript vars are + not currently checked." #?(:cljs {:tag boolean}) [env prefix] - (if-not (nil? (gets @env/*compiler* ::namespaces prefix)) - true + (when-not (gets @env/*compiler* ::namespaces prefix) (let [ns (:ns env)] (if-not (nil? (get (:requires ns) prefix)) true @@ -566,19 +567,19 @@ (let [warn (confirm-var-exist-warning env prefix suffix)] (confirm-var-exists env prefix suffix warn))) ([env prefix suffix missing-fn] - (let [sufstr (str suffix) - suffix-str (if (and #?(:clj (not= ".." sufstr) - :cljs (not (identical? ".." sufstr))) ;; leave cljs.core$macros/.. alone - #?(:clj (re-find #"\." sufstr) - :cljs ^boolean (.test #"\." sufstr))) - (first (string/split sufstr #"\.")) - suffix) - suffix (symbol suffix-str)] - (when (and (not (implicit-import? env prefix suffix)) - (not (loaded-ns? env prefix)) - (not (and (= 'cljs.core prefix) (= 'unquote suffix))) - (nil? (gets @env/*compiler* ::namespaces prefix :defs suffix))) - (missing-fn env prefix suffix))))) + (let [sufstr (str suffix) + suffix-str (if (and #?(:clj (not= ".." sufstr) + :cljs (not (identical? ".." sufstr))) ;; leave cljs.core$macros/.. alone + #?(:clj (re-find #"\." sufstr) + :cljs ^boolean (.test #"\." sufstr))) + (first (string/split sufstr #"\.")) + suffix) + suffix (symbol suffix-str)] + (when (and (not (implicit-import? env prefix suffix)) + (not (loaded-js-ns? env prefix)) + (not (and (= 'cljs.core prefix) (= 'unquote suffix))) + (nil? (gets @env/*compiler* ::namespaces prefix :defs suffix))) + (missing-fn env prefix suffix))))) (defn confirm-var-exists-throw [] (fn [env prefix suffix] From 478eb80c3f1bd9aa896bf925b535f9a52bf7b8d4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 08:53:55 -0400 Subject: [PATCH 1435/4033] CLJS-1365: cljs.js: :context :expr propagates down into ns loading --- src/main/cljs/cljs/js.cljs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 8d297b8c2..ad87acb49 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -229,7 +229,7 @@ (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) (let [dep (first deps)] - (require bound-vars dep opts + (require bound-vars dep (dissoc opts :context) (fn [res] (if-not (:error res) (load-deps bound-vars ana-env lib (next deps) opts cb) @@ -283,7 +283,10 @@ k (or (k reload) (get-in reloads [k nsym]) (and (= nsym name) (:*reload-macros* bound-vars) :reload))] - (require bound-vars nsym k (assoc opts :macros-ns true) + (require bound-vars nsym k + (-> opts + (assoc :macros-ns true) + (dissoc :context)) (fn [res] (if-not (:error res) (load-macros bound-vars k (next macros) reload reloads opts cb) From 8b6adaedb57fb2826321c552c17506ca04e998f9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 09:06:24 -0400 Subject: [PATCH 1436/4033] conditionalize cljs.analyzer/ns-side-effects, only for :clj --- src/main/clojure/cljs/analyzer.cljc | 61 +++++++++++++++-------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b5e967fe8..35d9bdcee 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2445,36 +2445,37 @@ ast)) ast))) -(defn ns-side-effects - [env {:keys [op] :as ast} opts] - (if (= :ns op) - (let [{:keys [deps uses require-macros use-macros reload reloads]} ast] - (when (and *analyze-deps* (seq deps)) - (analyze-deps name deps env (dissoc opts :macros-ns))) - (when (and *analyze-deps* (seq uses)) - (check-uses uses env)) - (when *load-macros* - (load-core) - (doseq [nsym (vals use-macros)] - (let [k (or (:use-macros reload) - (get-in reloads [:use-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) - (intern-macros nsym k))) - (doseq [nsym (vals require-macros)] - (let [k (or (:require-macros reload) - (get-in reloads [:require-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) - (intern-macros nsym k))) - (when (seq use-macros) - (check-use-macros use-macros env))) - ast) - ast)) +#?(:clj + (defn ns-side-effects + [env {:keys [op] :as ast} opts] + (if (= :ns op) + (let [{:keys [deps uses require-macros use-macros reload reloads]} ast] + (when (and *analyze-deps* (seq deps)) + (analyze-deps name deps env (dissoc opts :macros-ns))) + (when (and *analyze-deps* (seq uses)) + (check-uses uses env)) + (when *load-macros* + (load-core) + (doseq [nsym (vals use-macros)] + (let [k (or (:use-macros reload) + (get-in reloads [:use-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) + (doseq [nsym (vals require-macros)] + (let [k (or (:require-macros reload) + (get-in reloads [:require-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (clojure.core/require nsym k) + (clojure.core/require nsym)) + (intern-macros nsym k))) + (when (seq use-macros) + (check-use-macros use-macros env))) + ast) + ast))) (def ^:dynamic *passes* nil) From d516268c65251b79a0bd8480c74402b8b89895ba Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 09:20:29 -0400 Subject: [PATCH 1437/4033] CLJS-1363: cljs.js: eval-str: Allow client to eval in ns other than cljs.user top level entry points of cljs.js now all take :ns in opts argument --- src/main/cljs/cljs/js.cljs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ad87acb49..6260e80d7 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -229,7 +229,10 @@ (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) (let [dep (first deps)] - (require bound-vars dep (dissoc opts :context) + (require bound-vars dep + (-> opts + (dissoc :context) + (dissoc :ns)) (fn [res] (if-not (:error res) (load-deps bound-vars ana-env lib (next deps) opts cb) @@ -286,7 +289,8 @@ (require bound-vars nsym k (-> opts (assoc :macros-ns true) - (dissoc :context)) + (dissoc :context) + (dissoc :ns)) (fn [res] (if-not (:error res) (load-macros bound-vars k (next macros) reload reloads opts cb) @@ -359,14 +363,13 @@ (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) aenv (ana/empty-env) - bound-vars (cond-> (merge bound-vars - {:*cljs-ns* 'cljs.user - :*ns* (create-ns ana/*cljs-ns*)}) + the-ns (or (:ns opts) 'cljs.user) + bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] ((fn analyze-loop [] (binding [env/*compiler* (:*compiler* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) + *ns* (create-ns (:*cljs-ns* bound-vars)) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try @@ -442,14 +445,13 @@ ;; Eval (defn eval* [bound-vars form opts cb] - (let [bound-vars (cond-> (merge bound-vars - {:*cljs-ns* 'cljs.user - :*ns* (create-ns ana/*cljs-ns*)}) + (let [the-ns (or (:ns opts) 'cljs.user) + bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) + *ns* (create-ns (:*cljs-ns* bound-vars)) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] (let [aenv (ana/empty-env) @@ -500,8 +502,6 @@ ([state form opts cb] (eval* {:*compiler* state - :*cljs-ns* 'cljs.user - :*ns* (create-ns 'cljs.user) :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (or (:analyze-deps opts) true) :*load-macros* (or (:load-macros opts) true) @@ -517,15 +517,14 @@ eof (js-obj) aenv (ana/empty-env) sb (StringBuffer.) - bound-vars (cond-> (merge bound-vars - {:*cljs-ns* 'cljs.user - :*ns* (create-ns ana/*cljs-ns*)}) + the-ns (or (:ns opts) 'cljs.user) + bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] ((fn compile-loop [] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (:*ns* bound-vars) + *ns* (create-ns (:*cljs-ns* bound-vars)) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try @@ -609,9 +608,8 @@ eof (js-obj) aenv (ana/empty-env) sb (StringBuffer.) - bound-vars (cond-> (merge bound-vars - {:*cljs-ns* 'cljs.user - :*ns* (create-ns ana/*cljs-ns*)}) + the-ns (or (:ns opts) 'cljs.user) + bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] (when (:verbose opts) (debug-prn "Evaluating" name)) ((fn compile-loop [ns] From 3fd748fc014442763a5a243d30e5814b37b5a50c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 09:48:30 -0400 Subject: [PATCH 1438/4033] add cljs.js/load-source-map!, extend *load-fn* support so that source maps for pre-compiled ClojureScript works --- src/main/cljs/cljs/js.cljs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 6260e80d7..91c657edb 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -62,10 +62,12 @@ respected). Upon resolution the callback should be invoked with a map containing the following keys: - :lang - the language, :clj or :js - :source - the source of the library (a string) - :cache - optional, if a :clj namespace has been precompiled to :js, can give - an analysis cache for faster loads. + :lang - the language, :clj or :js + :source - the source of the library (a string) + :cache - optional, if a :clj namespace has been precompiled to :js, can + give an analysis cache for faster loads. + :source-map - optional, if a :clj namespace has been precompiled to :js, can + give a V3 source map JSON If the resource could not be resolved, the callback should be invoked with nil." @@ -113,6 +115,10 @@ (defn load-analysis-cache! [state ns cache] (swap! state assoc-in [::ana/namespaces ns] cache)) +(defn load-source-map! [state ns sm-json] + (let [sm (sm/decode (.parse js/JSON sm-json))] + (swap! state assoc-in [:source-maps ns] sm))) + (defn sm-data [] (atom {:source-map (sorted-map) @@ -182,7 +188,7 @@ (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") (if resource - (let [{:keys [lang source cache]} resource] + (let [{:keys [lang source cache source-map]} resource] (condp = lang :clj (eval-str* bound-vars source name opts (fn [res] @@ -194,6 +200,9 @@ (when cache (load-analysis-cache! (:*compiler* bound-vars) name cache)) + (when source-map + (load-source-map! + (:*compiler* bound-vars) name source-map)) (catch :default cause (wrap-error (ana/error env From de8bc1c4cce44e9c6de07bbf79411463138939ee Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 10:11:29 -0400 Subject: [PATCH 1439/4033] CLJS-1369: eval-str should support compiled output caching --- src/main/cljs/cljs/js.cljs | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 91c657edb..2fe9939d5 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -672,15 +672,21 @@ :name name :path (ns->relpath name) :source js-source - :cache (get-in env/*compiler* - [::ana/namespaces name])}] - (when (:verbose opts) - (debug-prn js-source)) - (let [res (try - {:ns ns :value (*eval-fn* evalm)} - (catch :default cause - (wrap-error (ana/error aenv "ERROR" cause))))] - (cb res)))))))))) + :cache (get-in env/*compiler* [::ana/namespaces name])} + complete (fn [res] + (if (:error res) + (cb res) + (do + (when (:verbose opts) + (debug-prn js-source)) + (let [res (try + {:ns ns :value (*eval-fn* evalm)} + (catch :default cause + (wrap-error (ana/error aenv "ERROR" cause))))] + (cb res)))))] + (if-let [f (:cache-source opts)] + (f evalm complete) + (complete {:value nil})))))))))) (:*cljs-ns* bound-vars)))) (defn eval-str @@ -698,9 +704,15 @@ opts (map) compilation options. - :eval - eval function to invoke, see *eval-fn* - :load - library resolution function, see *load-fn* - :source-map - set to true to generate inline source map information + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :cache-source - optional, a function to run side-effects with the + compilation result prior to actual evalution. This function + takes two arguments, the first is the eval map, the source + will be under :source. The second argument is a callback of + one argument. If an error occurs an :error key should be + supplied. cb (function) callback, will be invoked with a map. If succesful the map will contain From c499b2af68f02bd68b054b3aa01ef0573a54005d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jul 2015 10:40:57 -0400 Subject: [PATCH 1440/4033] CLJS-1349: cljs.build.api tests are broken due to cljs.util/to-target-file behavior default to "cljs" extension if :source-file not provided --- src/main/clojure/cljs/util.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index de93b4dfb..f26bd204b 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -80,7 +80,9 @@ ([target-dir ns-info] (to-target-file target-dir ns-info "js")) ([target-dir {:keys [ns source-file] :as ns-info} ext] - (let [src-ext (cljs.util/ext source-file) + (let [src-ext (if source-file + (cljs.util/ext source-file) + "cljs") ns (if (or (= src-ext "clj") (and (= ns 'cljs.core) (= src-ext "cljc"))) (symbol (str ns "$macros")) From d7db622e4eecf305bbf24c107e3b05d926fd916a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 27 Jul 2015 12:27:14 -0400 Subject: [PATCH 1441/4033] CLJS-1372: cljs.stacktrace: Unconditional File reference in :nashorn section Employ same handling as done for :rhino --- src/main/cljs/cljs/stacktrace.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index db1ae99a1..532130481 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -411,7 +411,9 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" [function file-and-line] (string/split frame-str #"\s+") [file-part line-part] (string/split file-and-line #":")] {:file (string/replace (.substring file-part 1) - (str output-dir File/separator) "") + (str output-dir + #?(:clj File/separator :cljs "/")) + "") :function function :line (when (and line-part (not (string/blank? line-part))) (parse-int From 161e6ac224ec544f17fba19bc53071de4c7dae09 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 27 Jul 2015 18:21:41 -0400 Subject: [PATCH 1442/4033] minor cleanups to cljs.js --- src/main/cljs/cljs/js.cljs | 81 +++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2fe9939d5..d9b6e8f01 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -230,23 +230,22 @@ ([bound-vars ana-env lib deps opts cb] (when (:verbose opts) (debug-prn "Loading dependencies for" lib)) - (let [compiler @(:*compiler* bound-vars)] - (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) - update-in [:dep-path] conj lib)] - (assert (every? #(not (contains? (:*cljs-dep-set* bound-vars) %)) deps) - (str "Circular dependency detected " - (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) - (if (seq deps) - (let [dep (first deps)] - (require bound-vars dep - (-> opts - (dissoc :context) - (dissoc :ns)) - (fn [res] - (if-not (:error res) - (load-deps bound-vars ana-env lib (next deps) opts cb) - (cb res))))) - (cb {:value nil})))))) + (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) + update-in [:dep-path] conj lib)] + (assert (every? #(not (contains? (:*cljs-dep-set* bound-vars) %)) deps) + (str "Circular dependency detected " + (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) + (if (seq deps) + (let [dep (first deps)] + (require bound-vars dep + (-> opts + (dissoc :context) + (dissoc :ns)) + (fn [res] + (if-not (:error res) + (load-deps bound-vars ana-env lib (next deps) opts cb) + (cb res))))) + (cb {:value nil}))))) (declare analyze*) @@ -330,30 +329,30 @@ (if (:error res) (cb res) (if (:*load-macros* bound-vars) - (do - (when (:verbose opts) (debug-prn "Processing :use-macros for" (:name ast))) - (load-macros bound-vars :use-macros use-macros reload reloads opts - (fn [res] - (if (:error res) - (cb res) - (do - (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) - (load-macros bound-vars :require-macros require-macros reloads reloads opts - (fn [res] - (if (:error res) - (cb res) - (let [res (try - (when (seq use-macros) - (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) - (ana/check-use-macros use-macros env)) - {:value nil} - (catch :default cause - (wrap-error - (ana/error ana-env - (str "Could not parse ns form " (:name ast)) cause))))] - (if (:error res) - (cb res) - (cb {:value ast}))))))))))) + (do + (when (:verbose opts) (debug-prn "Processing :use-macros for" (:name ast))) + (load-macros bound-vars :use-macros use-macros reload reloads opts + (fn [res] + (if (:error res) + (cb res) + (do + (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) + (load-macros bound-vars :require-macros require-macros reloads reloads opts + (fn [res] + (if (:error res) + (cb res) + (let [res (try + (when (seq use-macros) + (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) + (ana/check-use-macros use-macros env)) + {:value nil} + (catch :default cause + (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause))))] + (if (:error res) + (cb res) + (cb {:value ast}))))))))))) (cb {:value ast}))))))] (cond (and load (seq deps)) From 6cf8b44c3c3a25a18e687ac61b7a87fa991e5b9e Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 27 Jul 2015 18:24:31 -0400 Subject: [PATCH 1443/4033] make cljs.stacktrace/parse-file more flexible - if :host not present but :output-dir is, just rely on :output-dir --- src/main/cljs/cljs/stacktrace.cljc | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 532130481..67a989174 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -66,10 +66,16 @@ "Given a browser file url convert it into a relative path that can be used to locate the original source." [{:keys [host host-port port] :as repl-env} file {:keys [asset-path] :as opts}] - (let [base-url-pattern (string->regex (str "http://" host ":" (or host-port port) "/"))] - (if (re-find base-url-pattern file) + (let [urlpat (if host + (string->regex + (str "http://" host ":" (or host-port port) "/")) + "") + match (if host + (re-find urlpat file) + (contains? opts :output-dir))] + (if match (-> file - (string/replace base-url-pattern "") + (string/replace urlpat "") (string/replace (string->regex ;; if :asset-path specified drop leading slash @@ -166,6 +172,22 @@ at goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)" {:ua-product :chrome} nil) + + ;; Node.js example + (parse-stacktrace {} + "Error: 1 is not ISeqable + at Object.cljs$core$seq [as seq] (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:3999:8) + at Object.cljs$core$first [as first] (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:4018:19) + at cljs$core$ffirst (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:5161:34) + at /home/my/cool/project/.cljs_bootstrap/cljs/core.js:16006:88 + at cljs.core.map.cljs$core$IFn$_invoke$arity$2 (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:16007:3) + at cljs.core.LazySeq.sval (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:10244:109) + at cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:10335:10) + at Object.cljs$core$seq [as seq] (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:3980:13) + at Object.cljs$core$pr_sequential_writer [as pr_sequential_writer] (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:28084:14) + at cljs.core.LazySeq.cljs$core$IPrintWithWriter$_pr_writer$arity$3 (/home/my/cool/project/.cljs_bootstrap/cljs/core.js:28812:18)" + {:ua-product :chrome} + {:output-dir "/home/my/cool/project/.cljs_bootstrap"}) ) ;; ----------------------------------------------------------------------------- From 23e663685905552038c71f562ce53c9fe87e5873 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 27 Jul 2015 18:30:39 -0400 Subject: [PATCH 1444/4033] docstring for cljs.stacktrace/parse-stacktrace --- src/main/cljs/cljs/stacktrace.cljc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 67a989174..c08552e40 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -14,7 +14,21 @@ #?(:clj (:import [java.util.regex Pattern] [java.io File]))) -(defmulti parse-stacktrace (fn [repl-env st err opts] (:ua-product err))) +(defmulti parse-stacktrace + "Parse a JavaScript stacktrace string into a canonical data form. The + arguments: + + repl-env - the repl environment, an optional map with :host and :port keys + if the stacktrace includes url, not file references + st - the original stacktrace string to parse + err - an error map. :ua-product key defines the type of stacktrace parser + to use, for example :chrome + opts - additional options. :output-dir maybe given in this argument if + :host and :port do not apply, for example, a file path + + The canonical stacktrace representation can easily be mapped to a + ClojureScript one see mapped-stacktrace and mapped-stacktrace-str" + (fn [repl-env st err opts] (:ua-product err))) (defn parse-int [s] #?(:clj (Long/parseLong s) From cc7404aab845fa32403521ac513c0eefbc770245 Mon Sep 17 00:00:00 2001 From: Sean Grove Date: Fri, 17 Jul 2015 09:46:34 -0700 Subject: [PATCH 1445/4033] CLJS-1324: Compiler fails to raise warning/error when invoking a keyword without arguments Warn during analyses when invoking a keyword without any arguments --- src/main/clojure/cljs/analyzer.cljc | 13 ++++++++----- src/test/clojure/cljs/analyzer_tests.clj | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 35d9bdcee..ca91a315c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2188,12 +2188,15 @@ (let [enve (assoc env :context :expr) fexpr (analyze enve f) argc (count args) - ^boolean fn-var? (-> fexpr :info :fn-var)] - (when fn-var? + ^boolean fn-var? (-> fexpr :info :fn-var) + kw? (= 'cljs.core/Keyword (:tag fexpr))] + (when (or fn-var? kw?) (let [{:keys [^boolean variadic max-fixed-arity method-params name]} (:info fexpr)] - (when (and (not (valid-arity? argc method-params)) - (or (not variadic) - (and variadic (< argc max-fixed-arity)))) + (when (or (and kw? (zero? argc)) + (and (not kw?) + (not (valid-arity? argc method-params)) + (or (not variadic) + (and variadic (< argc max-fixed-arity))))) (warning :fn-arity env {:name name :argc argc})))) (let [deprecated? (-> fexpr :info :deprecated) no-warn? (-> form meta :deprecation-nowarn)] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 631d18afb..ba6a8df9c 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -13,7 +13,8 @@ (def warning-forms {:undeclared-var (let [v (gensym)] `(~v 1 2 3)) :fn-arity '(do (defn x [a b] (+ a b)) - (x 1 2 3 4))}) + (x 1 2 3 4)) + :keyword-arity '(do (:argumentless-keyword-invocation))}) (defn warn-count [form] (let [counter (atom 0) From 3bc902bc305e210a98a2a735c37a465caa5bca50 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 28 Jul 2015 07:04:10 -0400 Subject: [PATCH 1446/4033] refactor last commit to produce a more informative error as well as cover all invalid arity cases for keywords --- src/main/clojure/cljs/analyzer.cljc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ca91a315c..948a03916 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2185,19 +2185,19 @@ (defn parse-invoke* [env [f & args :as form]] - (let [enve (assoc env :context :expr) - fexpr (analyze enve f) - argc (count args) - ^boolean fn-var? (-> fexpr :info :fn-var) - kw? (= 'cljs.core/Keyword (:tag fexpr))] - (when (or fn-var? kw?) + (let [enve (assoc env :context :expr) + fexpr (analyze enve f) + argc (count args) + fn-var? (-> fexpr :info :fn-var) + kw? (= 'cljs.core/Keyword (:tag fexpr))] + (when ^boolean fn-var? (let [{:keys [^boolean variadic max-fixed-arity method-params name]} (:info fexpr)] - (when (or (and kw? (zero? argc)) - (and (not kw?) - (not (valid-arity? argc method-params)) - (or (not variadic) - (and variadic (< argc max-fixed-arity))))) + (when (and (not (valid-arity? argc method-params)) + (or (not variadic) + (and variadic (< argc max-fixed-arity)))) (warning :fn-arity env {: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) no-warn? (-> form meta :deprecation-nowarn)] (when (and (boolean deprecated?) From f8b97e0e2e8843f933c8fb7eadd710f6dc130a9e Mon Sep 17 00:00:00 2001 From: Sean Grove Date: Fri, 17 Jul 2015 10:02:40 -0700 Subject: [PATCH 1447/4033] CLJS-1033: take a drop accept nil as n argument Require n to pass number? predicate when passed to drop or take --- src/main/cljs/cljs/core.cljs | 6 ++++++ src/test/cljs/cljs/core_test.cljs | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f9ad48a99..a097b8388 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4216,6 +4216,7 @@ reduces them without incurring seq initialization" there are fewer than n. Returns a stateful transducer when no collection is provided." ([n] + {:pre [(number? n)]} (fn [rf] (let [na (volatile! n)] (fn @@ -4231,6 +4232,7 @@ reduces them without incurring seq initialization" (ensure-reduced result) result))))))) ([n coll] + {:pre [(number? n)]} (lazy-seq (when (pos? n) (when-let [s (seq coll)] @@ -4240,6 +4242,7 @@ reduces them without incurring seq initialization" "Returns a lazy sequence of all but the first n items in coll. Returns a stateful transducer when no collection is provided." ([n] + {:pre [(number? n)]} (fn [rf] (let [na (volatile! n)] (fn @@ -4252,6 +4255,7 @@ reduces them without incurring seq initialization" result (rf result input)))))))) ([n coll] + {:pre [(number? n)]} (let [step (fn [n coll] (let [s (seq coll)] (if (and (pos? n) s) @@ -8370,6 +8374,7 @@ reduces them without incurring seq initialization" "Returns a lazy seq of every nth item in coll. Returns a stateful transducer when no collection is provided." ([n] + {:pre [(number? n)]} (fn [rf] (let [ia (volatile! -1)] (fn @@ -8381,6 +8386,7 @@ reduces them without incurring seq initialization" (rf result input) result))))))) ([n coll] + {:pre [(number? n)]} (lazy-seq (when-let [s (seq coll)] (cons (first s) (take-nth n (drop n s))))))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 1a5b690e6..d529efa05 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -698,7 +698,13 @@ (is (= (hash 'foo) (hash (symbol "foo")))) (is (= (hash 'foo/bar) (hash (symbol "foo" "bar")))) (is (= (lazy-cat [1] [2] [3]) '(1 2 3))) - )) + ;; Make sure take/drop raise an error when given nil as an argument + (is (try (do (take nil [1 2 3]) false) + (catch js/Error e true))) + (is (try (do (drop nil [1 2 3]) false) + (catch js/Error e true))) + (is (try (do (take-nth nil [1 2 3]) false) + (catch js/Error e true))))) (deftest test-booleans (testing "Testing boolean predicates" From 666e487361e0711abeab44793857bca45e4bf711 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 28 Jul 2015 11:11:19 -0400 Subject: [PATCH 1448/4033] bump to tools.reader 0.10.0-apha3 add executable comments to cljs.js and add supporting files --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/cljs/cljs/js.cljs | 125 ++++++++++++++++++++++++++++- src/test/bootstrap_test/core.cljs | 4 + src/test/bootstrap_test/macros.clj | 4 + 6 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 src/test/bootstrap_test/core.cljs create mode 100644 src/test/bootstrap_test/macros.clj diff --git a/pom.template.xml b/pom.template.xml index c7459da41..c2f46391d 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.9.2 + 0.10.0-alpha3 diff --git a/project.clj b/project.clj index 11d476c9c..af2cd3d66 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["src/test/clojure" "src/test/cljs"] :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "0.9.2"] + [org.clojure/tools.reader "0.10.0-alpha3"] [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] [com.google.javascript/closure-compiler "v20150609"] [org.mozilla/rhino "1.7R5"]] diff --git a/script/bootstrap b/script/bootstrap index c65da49c9..6c2ce0763 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -7,7 +7,7 @@ CLOSURE_RELEASE="20150609" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150505-021ed5b3" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="0.9.2" +TREADER_RELEASE="0.10.0-alpha3" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d9b6e8f01..91a64edfa 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -731,4 +731,127 @@ :*load-macros* (or (:load-macros opts) true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} - source name opts cb))) \ No newline at end of file + source name opts cb))) + +(comment + (ns cljs.js.test + (:require [cljs.js :as cljs])) + + (def vm (js/require "vm")) + (def fs (js/require "fs")) + (def st (cljs/empty-state)) + + (defn node-eval [{:keys [name source]}] + (.runInThisContext vm source (str (munge name) ".js"))) + + (def libs + {'bootstrap-test.core :cljs + 'bootstrap-test.macros :clj}) + + (defn node-load [{:keys [name macros]} cb] + (if (contains? libs name) + (let [path (str "src/test/" (cljs/ns->relpath name) + "." (cljs.core/name (get libs name)))] + (.readFile fs path "utf-8" + (fn [err src] + (cb (if-not err + {:lang :clj :source src} + (.error js/console err)))))) + (cb nil))) + + (cljs/eval st '(defn foo [a b] (+ a b)) + {:eval cljs/js-eval} + (fn [res] + (println res))) + + (cljs/compile st "(defprotocol IFoo (foo [this]))" + (fn [js-source] + (println "Source:") + (println js-source))) + + (cljs/eval-str st + "(defn foo [a b] (+ a b)) + (defn bar [c d] (+ c d))" + nil + {:eval cljs/js-eval} + (fn [res] + (println res))) + + (cljs/eval-str st "1" + nil + {:eval cljs/js-eval + :context :expr} + (fn [res] + (println res))) + + (cljs/eval-str st "(def x 1)" + nil + {:eval cljs/js-eval + :context :expr + :def-emits-var true} + (fn [res] + (println res))) + + (cljs/eval st '(ns foo.bar) + {:eval cljs/js-eval} + (fn [res] + (println res))) + + (cljs/compile st "(defn foo\n[a b]\n(+ a b))" 'cljs.foo + {:verbose true :source-map true} + (fn [js-source] + (println "Source:") + (println js-source))) + + (cljs/eval-str st + "(ns foo.bar (:require [bootstrap-test.core]))\n(bootstrap-test.core/foo 3 4)" + 'foo.bar + {:verbose true + :source-map true + :eval node-eval + :load node-load} + (fn [ret] + (println ret))) + + (cljs/eval-str st + "(ns foo.bar (:require-macros [bootstrap-test.macros :refer [foo]]))\n(foo 4 4)" + 'foo.bar + {:verbose true + :source-map true + :eval node-eval + :load node-load} + (fn [{:keys [error] :as res}] + (if error + (do + (println error) + (println (.. error -cause -stack))) + (println res)))) + + (cljs/eval-str st + "(ns foo.bar)\n(first [1 2 3])" + 'foo.bar + {:verbose true + :source-map true + :eval node-eval + :load node-load} + (fn [{:keys [error] :as res}] + (if error + (do + (println error) + (println (.. error -cause -stack))) + (println res)))) + + (cljs/eval-str st + "(ns foo.bar)\n(map inc [1 2 3])" + 'foo.bar + {:verbose true + :source-map true + :eval node-eval + :load node-load} + (fn [{:keys [error] :as res}] + (if error + (do + (println error) + (println (.. error -cause -stack))) + (println res)))) + ) \ No newline at end of file diff --git a/src/test/bootstrap_test/core.cljs b/src/test/bootstrap_test/core.cljs new file mode 100644 index 000000000..cb7004607 --- /dev/null +++ b/src/test/bootstrap_test/core.cljs @@ -0,0 +1,4 @@ +(ns bootstrap-test.core) + +(defn foo [c d] + (* c d)) diff --git a/src/test/bootstrap_test/macros.clj b/src/test/bootstrap_test/macros.clj new file mode 100644 index 000000000..d93559a8e --- /dev/null +++ b/src/test/bootstrap_test/macros.clj @@ -0,0 +1,4 @@ +(ns bootstrap-test.macros) + +(defmacro foo [a b] + `(* ~a ~b)) \ No newline at end of file From 083f04313e3fe5eda0bfb98467fa92b6c83e6ec0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 15:29:16 -0400 Subject: [PATCH 1449/4033] inline source maps appear to work fine in Node.js actually --- src/main/cljs/cljs/js.cljs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 91a64edfa..92c234881 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -734,8 +734,7 @@ source name opts cb))) (comment - (ns cljs.js.test - (:require [cljs.js :as cljs])) + (require '[cljs.js :as cljs]) (def vm (js/require "vm")) (def fs (js/require "fs")) @@ -751,7 +750,7 @@ (defn node-load [{:keys [name macros]} cb] (if (contains? libs name) (let [path (str "src/test/" (cljs/ns->relpath name) - "." (cljs.core/name (get libs name)))] + "." (cljs.core/name (get libs name)))] (.readFile fs path "utf-8" (fn [err src] (cb (if-not err @@ -854,4 +853,19 @@ (println error) (println (.. error -cause -stack))) (println res)))) + + ;; inline source maps work under Node.js + (cljs/eval-str st + "(ns foo.bar)\n(ffirst [1 2 3])" + 'foo.bar + {:verbose true + :source-map false + :eval node-eval + :load node-load} + (fn [{:keys [error] :as res}] + (if error + (do + (println error) + (println (.. error -cause -stack))) + (println res)))) ) \ No newline at end of file From a1dca29d3ba248062de055a7c951512949515b9b Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 17:30:18 -0400 Subject: [PATCH 1450/4033] CLJS-1375: cljs.js/require does not conj to *loaded* Use Mike Fikes changes which highlighted some broken thigns. require did not invoke the callback if library was already loaded. Macro namespaces need to be renamed, otherwise unintuitively prevents loads. --- src/main/cljs/cljs/js.cljs | 113 +++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 92c234881..b4ea751a4 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -171,56 +171,62 @@ ([bound-vars name opts cb] (require bound-vars name nil opts cb)) ([bound-vars name reload opts cb] - (when (= :reload reload) - (swap! *loaded* disj name)) - (when (= :reload-all reload) - (reset! *loaded* #{})) - (when (:verbose opts) - (debug-prn (str "Loading " name (when (:macros-ns opts) " macros") " namespace"))) - (when-not (contains? @*loaded* name) - (let [env (:*env* bound-vars)] - (try - ((:*load-fn* bound-vars) - {:name name - :macros (:macros-ns opts) - :path (ns->relpath name)} - (fn [resource] - (assert (or (map? resource) (nil? resource)) - "*load-fn* may only return a map or nil") - (if resource - (let [{:keys [lang source cache source-map]} resource] - (condp = lang - :clj (eval-str* bound-vars source name opts - (fn [res] - (if (:error res) - (cb res) - (cb {:value true})))) - :js (let [res (try - ((:*eval-fn* bound-vars) resource) - (when cache - (load-analysis-cache! - (:*compiler* bound-vars) name cache)) - (when source-map - (load-source-map! - (:*compiler* bound-vars) name source-map)) - (catch :default cause - (wrap-error - (ana/error env - (str "Could not require " name) cause))))] - (if (:error res) - (cb res) - (cb {:value true}))) - (cb (wrap-error - (ana/error env - (str "Invalid :lang specified " lang ", only :clj or :js allowed")))))) - (cb (wrap-error - (ana/error env - (ana/error-message :undeclared-ns - {:ns-sym name :js-provide (cljs.core/name name)}))))))) - (catch :default cause - (cb (wrap-error - (ana/error env - (str "Could not require " name) cause))))))))) + (let [name (cond-> name (:macro-ns opts) ana/macro-ns-name)] + (when (= :reload reload) + (swap! *loaded* disj name)) + (when (= :reload-all reload) + (reset! *loaded* #{})) + (when (:verbose opts) + (debug-prn (str "Loading " name (when (:macros-ns opts) " macros") " namespace"))) + (if-not (contains? @*loaded* name) + (let [env (:*env* bound-vars)] + (try + ((:*load-fn* bound-vars) + {:name name + :macros (:macros-ns opts) + :path (ns->relpath name)} + (fn [resource] + (assert (or (map? resource) (nil? resource)) + "*load-fn* may only return a map or nil") + (if resource + (let [{:keys [lang source cache source-map]} resource] + (condp = lang + :clj (eval-str* bound-vars source name opts + (fn [res] + (if (:error res) + (cb res) + (do + (swap! *loaded* conj name) + (cb {:value true}))))) + :js (let [res (try + ((:*eval-fn* bound-vars) resource) + (when cache + (load-analysis-cache! + (:*compiler* bound-vars) name cache)) + (when source-map + (load-source-map! + (:*compiler* bound-vars) name source-map)) + (catch :default cause + (wrap-error + (ana/error env + (str "Could not require " name) cause))))] + (if (:error res) + (cb res) + (do + (swap! *loaded* conj name) + (cb {:value true})))) + (cb (wrap-error + (ana/error env + (str "Invalid :lang specified " lang ", only :clj or :js allowed")))))) + (cb (wrap-error + (ana/error env + (ana/error-message :undeclared-ns + {:ns-sym name :js-provide (cljs.core/name name)}))))))) + (catch :default cause + (cb (wrap-error + (ana/error env + (str "Could not require " name) cause)))))) + (cb {:value true}))))) (declare ns-side-effects analyze-deps) @@ -822,9 +828,9 @@ (fn [{:keys [error] :as res}] (if error (do - (println error) + (println "Error:" error) (println (.. error -cause -stack))) - (println res)))) + (println "Result:" res)))) (cljs/eval-str st "(ns foo.bar)\n(first [1 2 3])" @@ -854,12 +860,11 @@ (println (.. error -cause -stack))) (println res)))) - ;; inline source maps work under Node.js (cljs/eval-str st "(ns foo.bar)\n(ffirst [1 2 3])" 'foo.bar {:verbose true - :source-map false + :source-map true :eval node-eval :load node-load} (fn [{:keys [error] :as res}] From 70d940558b5f7f9d271a0b546e42c4348d768761 Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Tue, 21 Jul 2015 16:39:48 -0500 Subject: [PATCH 1451/4033] CLJS-1360: Refactor JS module processing to work with recent Google Closure compiler changes The functionality of the ES6ModuleLoader has been rewritten, including some functionality that we use, such as the toModuleName method and the constructor of the ES6ModuleLoader. We now need to pass all modules of the same type to the Compiler, but are still converting only one module at a time. Also, it's safer to just get the module name from the converted lib directly instead of the method toModuleName. This change supports both versions of the ES6ModuleLoader for now. Once a new version of the Google Closure compiler gets released, we can probably remove support for the older version. Changes to the ES6ModuleLoader can be seen here: https://github.com/google/closure-compiler/pull/1043. --- src/main/clojure/cljs/closure.clj | 126 +++++++++++++++++------------- 1 file changed, 73 insertions(+), 53 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d21f454b7..29c0a8f80 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -55,7 +55,7 @@ CommandLineRunner AnonymousFunctionNamingPolicy JSModule JSModuleGraph SourceMap ProcessCommonJSModules ES6ModuleLoader AbstractCompiler TransformAMDToCJSModule - ProcessEs6Modules] + ProcessEs6Modules CompilerInput] [com.google.javascript.rhino Node] [java.security MessageDigest] [javax.xml.bind DatatypeConverter] @@ -72,26 +72,30 @@ (defn random-string [length] (apply str (take length (repeatedly random-char)))) -(defmacro ^:private compile-if - "Evaluate `exp` and if it returns logical true and doesn't error, expand to - `then`. Else expand to `else`." - [exp then & else] - (if (try (eval exp) - (catch Throwable _ false)) - `(do ~then) - `(do ~@else))) - -(compile-if +(util/compile-if + (.getConstructor ES6ModuleLoader + (into-array java.lang.Class + [java.util.List java.lang.Iterable])) + (do (def is-new-es6-loader? true) + (def default-module-root ES6ModuleLoader/DEFAULT_FILENAME_PREFIX)) + (def is-new-es6-loader? false)) + +(util/compile-if + (.getConstructor ES6ModuleLoader + (into-array java.lang.Class + [AbstractCompiler java.lang.String])) + (def is-old-es6-loader? true) + (def is-old-es6-loader? false)) + +(util/compile-if (and (.getConstructor ProcessCommonJSModules (into-array java.lang.Class [com.google.javascript.jscomp.Compiler ES6ModuleLoader])) - (.getConstructor ES6ModuleLoader - (into-array java.lang.Class - [AbstractCompiler java.lang.String]))) + (or is-new-es6-loader? is-old-es6-loader?)) (def can-convert-commonjs? true) (def can-convert-commonjs? false)) -(compile-if +(util/compile-if (and can-convert-commonjs? (.getConstructor TransformAMDToCJSModule (into-array java.lang.Class @@ -99,17 +103,14 @@ (def can-convert-amd? true) (def can-convert-amd? false)) -(compile-if +(util/compile-if (and (.getConstructor ProcessEs6Modules (into-array java.lang.Class [com.google.javascript.jscomp.Compiler ES6ModuleLoader Boolean/TYPE])) - (.getConstructor ES6ModuleLoader - (into-array java.lang.Class - [AbstractCompiler java.lang.String]))) + (or is-new-es6-loader? is-old-es6-loader?)) (def can-convert-es6? true) (def can-convert-es6? false)) - ;; Closure API ;; =========== @@ -1227,53 +1228,73 @@ (not (.startsWith path (str "." File/separator))) (str "." File/separator) (not (.endsWith path File/separator)) (#(str % File/separator))))) -(defn init-js-module-processing [js-file options] - (let [^List externs '() - ^SourceFile source-file (js-source-file js-file (slurp js-file)) - closure-compiler (doto (make-closure-compiler) - (.init externs [source-file] options)) - ^Node root (.parse closure-compiler source-file)] - {:closure-compiler closure-compiler - :root root})) +(util/compile-if is-new-es6-loader? + (defn make-es6-loader [source-files] + (let [^List module-roots (list default-module-root) + ^List compiler-inputs (map #(CompilerInput. %) source-files)] + (ES6ModuleLoader. module-roots compiler-inputs))) + (defn make-es6-loader [closure-compiler file] + (let [module-root (get-js-module-root file)] + (ES6ModuleLoader. closure-compiler module-root)))) + +(defn ^Node get-root-node [file closure-compiler] + (let [^CompilerInput input (->> (slurp file) + (js-source-file file) + (CompilerInput.))] + (.getAstRoot input closure-compiler))) + +(defn get-source-files [module-type opts] + (->> (:foreign-libs opts) + (filter #(= (:module-type %) module-type)) + (map #(js-source-file (:file %) (slurp (:file %)))))) (defmulti convert-js-module "Takes a JavaScript module and rewrites it into a Google Closure-compatible form. Returns the source of the new module as a single string." - (fn [{module-type :module-type :as js}] + (fn [{module-type :module-type :as js} opts] (if (and (= module-type :amd) can-convert-amd?) ;; AMD modules are converted via CommonJS modules :commonjs module-type))) -(compile-if can-convert-commonjs? - (defmethod convert-js-module :commonjs [js] - (let [js-file (:file js) - path (.getParent (io/file js-file)) - module-root (get-js-module-root js-file) +(util/compile-if can-convert-commonjs? + (defmethod convert-js-module :commonjs [js opts] + (let [{:keys [file module-type]} js + ^List externs '() + ^List source-files (get-source-files module-type opts) ^CompilerOptions options (CompilerOptions.) - {:keys [closure-compiler root]} (init-js-module-processing js-file options) - es6-loader (ES6ModuleLoader. closure-compiler module-root) - cjs (ProcessCommonJSModules. closure-compiler es6-loader)] - (compile-if can-convert-amd? + closure-compiler (doto (make-closure-compiler) + (.init externs source-files options)) + es6-loader (if is-new-es6-loader? + (make-es6-loader source-files) + (make-es6-loader closure-compiler file)) + cjs (ProcessCommonJSModules. closure-compiler es6-loader) + ^Node root (get-root-node file closure-compiler)] + (util/compile-if can-convert-amd? (when (= (:module-type js) :amd) (.process (TransformAMDToCJSModule. closure-compiler) nil root))) (.process cjs nil root) (.toSource closure-compiler root)))) -(compile-if can-convert-es6? - (defmethod convert-js-module :es6 [js] - (let [js-file (:file js) - module-root (get-js-module-root js-file) +(util/compile-if can-convert-es6? + (defmethod convert-js-module :es6 [js opts] + (let [{:keys [file module-type]} js + ^List externs '() + ^List source-files (get-source-files module-type opts) ^CompilerOptions options (doto (CompilerOptions.) (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) (.setLanguageOut CompilerOptions$LanguageMode/ECMASCRIPT5)) - {:keys [closure-compiler root]} (init-js-module-processing js-file options) - es6-loader (ES6ModuleLoader. closure-compiler module-root) - cjs (ProcessEs6Modules. closure-compiler es6-loader true)] + closure-compiler (doto (make-closure-compiler) + (.init externs source-files options)) + es6-loader (if is-new-es6-loader? + (make-es6-loader source-files) + (make-es6-loader closure-compiler file)) + cjs (ProcessEs6Modules. closure-compiler es6-loader true) + ^Node root (get-root-node file closure-compiler)] (.processFile cjs root) (.toSource closure-compiler root)))) -(defmethod convert-js-module :default [js] +(defmethod convert-js-module :default [js opts] (ana/warning :unsupported-js-module-type @env/*compiler* js) (deps/-source js)) @@ -1293,7 +1314,7 @@ (when-not (.exists out-file) (util/mkdirs out-file) (if (:module-type js) - (spit out-file (convert-js-module js)) + (spit out-file (convert-js-module js opts)) (spit out-file (deps/-source js)))) (if (map? js) (merge js ijs) @@ -1547,21 +1568,20 @@ options where new modules are passed with :libs option." [opts] (let [js-modules (filter :module-type (:foreign-libs opts))] - (reduce (fn [opts {:keys [file module-type] :as lib}] + (reduce (fn [new-opts {:keys [file module-type] :as lib}] (if (or (and (= module-type :commonjs) can-convert-commonjs?) (and (= module-type :amd) can-convert-amd?) (and (= module-type :es6) can-convert-es6?)) - (let [module-name (-> (ProcessCommonJSModules/toModuleName file) - (string/replace "_" "-")) - ijs (write-javascript opts (deps/load-foreign-library lib))] + (let [ijs (write-javascript opts (deps/load-foreign-library lib)) + module-name (-> (deps/load-library (:out-file ijs)) first :provides first)] (doseq [provide (:provides ijs)] (swap! env/*compiler* #(update-in % [:js-module-index] assoc provide module-name))) - (-> opts + (-> new-opts (update-in [:libs] (comp vec conj) (:out-file ijs)) (update-in [:foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs)))))) - opts)) + new-opts)) opts js-modules))) (defn build From 9e662c3ea5b9f536f303e9084415e988bf5d0878 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Mon, 27 Jul 2015 18:41:59 +0300 Subject: [PATCH 1452/4033] CLJS-1367: Better api call arguments This change adds additional arity for api calls which need compiler-env. Old arities continue to use the dynamic var. Also, api calls which take options map as argument, now use :warning-handlers options to set value for dynamic var cljs.analyzer/*cljs-warning-handlers*. --- src/main/clojure/cljs/analyzer/api.clj | 87 ++++++++++++-------- src/main/clojure/cljs/build/api.clj | 63 ++++++++------ src/main/clojure/cljs/compiler/api.clj | 52 +++++++----- src/test/clojure/cljs/analyzer_api_tests.clj | 28 +++++++ src/test/clojure/cljs/build_api_tests.clj | 4 +- 5 files changed, 153 insertions(+), 81 deletions(-) create mode 100644 src/test/clojure/cljs/analyzer_api_tests.clj diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index 18cb8adcb..3917c99c3 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -42,8 +42,9 @@ (defn get-options "Return the compiler options from compiler state." - [] - (get @env/*compiler* :options)) + ([] (get-options env/*compiler*)) + ([state] + (get @state :options))) (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context @@ -52,9 +53,13 @@ containing at least :form, :op and :env keys). If expr has any (immediately) nested exprs, must have :children [exprs...] entry. This will facilitate code walking without knowing the details of the op set." - ([env form] (ana/analyze env form nil)) - ([env form name] (ana/analyze env form name nil)) - ([env form name opts] (ana/analyze env form name opts))) + ([env form] (analyze env form nil)) + ([env form name] (analyze env form name nil)) + ([env form name opts] (analyze env/*compiler* env form name opts)) + ([state env form name opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/analyze env form name opts))))) (defn forms-seq "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally @@ -72,9 +77,13 @@ be used for *analyze-deps* and *load-macros* bindings respectively. This function does _not_ side-effect the ambient compilation environment unless requested via opts where :restore is false." - ([src] (ana/parse-ns src nil nil)) - ([src opts] (ana/parse-ns src nil opts)) - ([src dest opts] (ana/parse-ns src dest opts))) + ([src] (parse-ns src nil nil)) + ([src opts] (parse-ns src nil opts)) + ([src dest opts] (parse-ns env/*compiler* src dest opts)) + ([state src dest opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/parse-ns src dest opts))))) (defn analyze-file "Given a java.io.File, java.net.URL or a string identifying a resource on the @@ -85,8 +94,12 @@ compiler options, if :cache-analysis true will cache analysis to \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a meaningful value." - ([f] (ana/analyze-file f nil)) - ([f opts] (ana/analyze-file f opts))) + ([f] (analyze-file f nil)) + ([f opts] (analyze-file env/*compiler* f opts)) + ([state f opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/analyze-file f opts))))) ;; ============================================================================= ;; Main API @@ -105,48 +118,54 @@ (defn all-ns "Return all namespaces. Analagous to clojure.core/all-ns but returns symbols identifying namespaces not Namespace instances." - [] - (keys (get @env/*compiler* ::ana/namespaces))) + ([] (all-ns env/*compiler*)) + ([state] + (keys (get @state ::ana/namespaces)))) (defn find-ns "Given a namespace return the corresponding namespace analysis map. Analagous to clojure.core/find-ns." - [sym] - {:pre [(symbol? sym)]} - (get-in @env/*compiler* [::ana/namespaces sym])) + ([sym] (find-ns env/*compiler* sym)) + ([state sym] + {:pre [(symbol? sym)]} + (get-in @state [::ana/namespaces sym]))) (defn ns-interns "Given a namespace return all the var analysis maps. Analagous to clojure.core/ns-interns but returns var analysis maps not vars." - [ns] - {:pre [(symbol? ns)]} - (merge - (get-in @env/*compiler* [::ana/namespaces ns :macros]) - (get-in @env/*compiler* [::ana/namespaces ns :defs]))) + ([ns] (ns-interns env/*compiler*)) + ([state ns] + {:pre [(symbol? ns)]} + (merge + (get-in @state [::ana/namespaces ns :macros]) + (get-in @state [::ana/namespaces ns :defs])))) (defn ns-publics "Given a namespace return all the public var analysis maps. Analagous to clojure.core/ns-publics but returns var analysis maps not vars." - [ns] - {:pre [(symbol? ns)]} - (->> (merge - (get-in @env/*compiler* [::ana/namespaces ns :macros]) - (get-in @env/*compiler* [::ana/namespaces ns :defs])) - (remove (fn [[k v]] (:private v))) - (into {}))) + ([ns] (ns-publics env/*compiler*)) + ([state ns] + {:pre [(symbol? ns)]} + (->> (merge + (get-in @state [::ana/namespaces ns :macros]) + (get-in @state [::ana/namespaces ns :defs])) + (remove (fn [[k v]] (:private v))) + (into {})))) (defn ns-resolve "Given a namespace and a symbol return the corresponding var analysis map. Analagous to clojure.core/ns-resolve but returns var analysis map not Var." - [ns sym] - {:pre [(symbol? ns) (symbol? sym)]} - (get-in @env/*compiler* [::ana/namespaces ns :defs sym])) + ([ns sym] (ns-resolve env/*compiler* ns sym)) + ([state ns sym] + {:pre [(symbol? ns) (symbol? sym)]} + (get-in @state [::ana/namespaces ns :defs sym]))) (defn remove-ns "Removes the namespace named by the symbol." - [ns] - {:pre [(symbol? ns)]} - (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) + ([ns] (remove-ns env/*compiler* ns)) + ([state ns] + {:pre [(symbol? ns)]} + (swap! state update-in [::ana/namespaces] dissoc ns))) (defmacro in-cljs-user "Binds cljs.analyzer/*cljs-ns* to 'cljs.user and uses the given compilation @@ -154,4 +173,4 @@ [env & body] `(binding [cljs.analyzer/*cljs-ns* 'cljs.user] (cljs.env/with-compiler-env ~env - ~@body))) \ No newline at end of file + ~@body))) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 204067eba..a31c37173 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -56,23 +56,23 @@ ClojureScript namespaces that require and use the macros from 'example.macros : (cljs-dependents-for-macro-namespaces 'example.macros) -> - ('example.core 'example.util) - - This must be called when cljs.env/*compiler* is bound to the - compile env that you are inspecting. See cljs.env/with-compile-env." - [namespaces] - (map :name - (let [namespaces-set (set namespaces)] - (filter (fn [x] (not-empty - (intersection namespaces-set (-> x :require-macros vals set)))) - (vals (:cljs.analyzer/namespaces @env/*compiler*)))))) + ('example.core 'example.util)" + ([namespaces] (cljs-dependents-for-macro-namespaces env/*compiler* namespaces)) + ([state namespaces] + (map :name + (let [namespaces-set (set namespaces)] + (filter (fn [x] (not-empty + (intersection namespaces-set (-> x :require-macros vals set)))) + (vals (:cljs.analyzer/namespaces @state))))))) (defn cljs-ns-dependents "Given a namespace symbol return a seq of all dependent namespaces sorted in dependency order. Will include transient dependents." - [ns] - (ana/ns-dependents ns)) + ([ns] (cljs-ns-dependents env/*compiler* ns)) + ([state ns] + (env/with-compiler-env state + (ana/ns-dependents ns)))) (defn parse-js-ns "Given a Google Closure style JavaScript file or resource return the namespace @@ -84,15 +84,22 @@ (defn ^File src-file->target-file "Given a ClojureScript source file return the target file. May optionally provide build options with :output-dir specified." - ([src] (closure/src-file->target-file src)) - ([src opts] (closure/src-file->target-file src opts))) + ([src] (src-file->target-file src nil)) + ([src opts] (src-file->target-file env/*compiler* src opts)) + ([state src opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (closure/src-file->target-file src opts))))) (defn ^String src-file->goog-require "Given a ClojureScript or Google Closure style JavaScript source file return the goog.require statement for it." - ([src] (closure/src-file->goog-require src)) - ([src options] - (closure/src-file->goog-require src options))) + ([src] (src-file->goog-require src nil)) + ([src options] (src-file->goog-require env/*compiler* src options)) + ([state src options] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers options ana/*cljs-warning-handlers*)] + (closure/src-file->goog-require src options))))) ;; ============================================================================= ;; Main API @@ -156,8 +163,10 @@ (defn compile "Given a Compilable, compile it and return an IJavaScript." - [opts compilable] - (closure/compile compilable opts)) + ([opts compilable] (compile env/*compiler* opts compilable)) + ([state opts compilable] + (env/with-compiler-env state + (closure/compile compilable opts)))) (defn output-unoptimized "Ensure that all JavaScript source files are on disk (not in jars), @@ -172,18 +181,22 @@ (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] - (closure/build source opts)) + (build source opts nil)) ([source opts compiler-env] - (closure/build source opts compiler-env))) + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (closure/build source opts compiler-env)))) (defn watch "Given a source which can be compiled, watch it for changes to produce." ([source opts] - (closure/watch source opts)) + (watch source opts (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)))) ([source opts compiler-env] - (closure/watch source opts compiler-env)) + (watch source opts compiler-env nil)) ([source opts compiler-env stop] - (closure/watch source opts compiler-env stop))) + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (closure/watch source opts compiler-env stop)))) (comment @@ -204,4 +217,4 @@ #(target-file-for-cljs-ns % "out-dev") (env/with-compiler-env test-cenv (ns-dependents 'clojure.string))) - ) \ No newline at end of file + ) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index c708c5947..019a59d16 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -19,22 +19,30 @@ (defn emit "Given an AST node generated by the analyzer emit JavaScript as a string." - [ast] - (with-out-str - (comp/emit ast))) + ([ast] (emit env/*compiler* ast)) + ([state ast] + (env/with-compiler-env state + (with-out-str + (comp/emit ast))))) (defn with-core-cljs "Ensure that core.cljs has been loaded." ([] (comp/with-core-cljs nil)) - ([opts] (comp/with-core-cljs opts (fn []))) - ([opts body] (comp/with-core-cljs opts body))) + ([opts] (with-core-cljs opts (fn []))) + ([opts body] (with-core-cljs env/*compiler* opts body)) + ([state opts body] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (comp/with-core-cljs opts body))))) (defn requires-compilation? "Return true if the src file requires compilation." - ([src dest] - (comp/requires-compilation? src dest nil)) - ([src dest opts] - (comp/requires-compilation? src dest opts))) + ([src dest] (requires-compilation? src dest nil)) + ([src dest opts] (requires-compilation? env/*compiler* src dest opts)) + ([state src dest opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (comp/requires-compilation? src dest opts))))) (defn compile-file "Compiles src to a file of the same name, but with a .js extension, @@ -49,12 +57,13 @@ Returns a map containing {:ns .. :provides .. :requires .. :file ..}. If the file was not compiled returns only {:file ...}" - ([src] - (comp/compile-file src)) - ([src dest] - (comp/compile-file src dest)) - ([src dest opts] - (comp/compile-file src dest opts))) + ([src] (compile-file src)) + ([src dest] (compile-file src dest)) + ([src dest opts] (compile-file env/*compiler* src dest opts)) + ([state src dest opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (comp/compile-file src dest opts))))) (defn cljs-files-in "Return a sequence of all .cljs and .cljc files in the given directory." @@ -67,9 +76,10 @@ directory mirroring the source directory structure. Returns a list of maps containing information about each file which was compiled in dependency order." - ([src-dir] - (comp/compile-root src-dir "out")) - ([src-dir target-dir] - (comp/compile-root src-dir target-dir nil)) - ([src-dir target-dir opts] - (comp/compile-root src-dir target-dir opts))) \ No newline at end of file + ([src-dir] (compile-root src-dir "out")) + ([src-dir target-dir] (compile-root src-dir target-dir nil)) + ([src-dir target-dir opts] (compile-root env/*compiler* src-dir target-dir opts)) + ([state src-dir target-dir opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (with-bindings (api-opts opts) (comp/compile-root src-dir target-dir opts)))))) diff --git a/src/test/clojure/cljs/analyzer_api_tests.clj b/src/test/clojure/cljs/analyzer_api_tests.clj new file mode 100644 index 000000000..a4e71eae8 --- /dev/null +++ b/src/test/clojure/cljs/analyzer_api_tests.clj @@ -0,0 +1,28 @@ +(ns cljs.analyzer-api-tests + (:require [cljs.analyzer.api :as ana-api]) + (:use clojure.test)) + +(def warning-form + '(do (defn x [a b] (+ a b)) + (x 1 2 3 4))) + +(defn warning-handler [counter] + (fn [warning-type env extra] + (when (ana-api/warning-enabled? warning-type) + (swap! counter inc)))) + +(def test-cenv (atom {})) +(def test-env (ana-api/empty-env)) + +(deftest with-warning-handlers-test + (let [counter (atom 0)] + (ana-api/analyze test-cenv test-env warning-form nil + {:warning-handlers [(warning-handler counter)]}) + (is (= 1 @counter)))) + +(deftest vary-warning-handlers-test + (let [counter (atom 0)] + (cljs.analyzer/all-warn + (ana-api/analyze test-cenv test-env warning-form nil + {:warning-handlers [(warning-handler counter)]})) + (is (= 1 @counter)))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 17106cd9a..51d326c1d 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -111,4 +111,6 @@ '(bar.core baz.core))) (is (= (env/with-compiler-env test-cenv (cljs-ns-dependents 'graph.foo.core)) - '(graph.bar.core graph.baz.core)))) \ No newline at end of file + '(graph.bar.core graph.baz.core))) + (is (= (cljs-ns-dependents test-cenv 'graph.foo.core) + '(graph.bar.core graph.baz.core)))) From 49b930418bea93031c289509e20a846977bc2c6f Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Tue, 28 Jul 2015 19:54:24 +0300 Subject: [PATCH 1453/4033] CLJS-1367: Expose default-warning-handler and warning-enabled? As :warning-handlers option overwrites the handlers collection there should be a way to use default-warning-handler with :warning-handlers option. Warning-enabled? function is useful for custom warning-handlers where it might be necessary to check if warning was enabled. --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/main/clojure/cljs/analyzer/api.clj | 12 ++++++++++++ src/test/clojure/cljs/analyzer_api_tests.clj | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 948a03916..754b40388 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -339,7 +339,7 @@ (str "Unsupported JavaScript module type " module-type " for foreign library " file ".")) -(defn ^:private default-warning-handler [warning-type env extra] +(defn default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] #?(:clj (binding [*out* *err*] diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index 3917c99c3..da32e5c09 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -40,6 +40,18 @@ `(binding [ana/*cljs-warnings* ~no-warnings] ~@body))) +(defn warning-enabled? + "Test if the given warning-type is enabled." + [warning-type] + (ana/*cljs-warnings* warning-type)) + +(defn default-warning-handler + "The default warning handler. + + Outputs the warning messages to *err*." + [warning-type env extra] + (ana/default-warning-handler warning-type env extra)) + (defn get-options "Return the compiler options from compiler state." ([] (get-options env/*compiler*)) diff --git a/src/test/clojure/cljs/analyzer_api_tests.clj b/src/test/clojure/cljs/analyzer_api_tests.clj index a4e71eae8..3b520b22a 100644 --- a/src/test/clojure/cljs/analyzer_api_tests.clj +++ b/src/test/clojure/cljs/analyzer_api_tests.clj @@ -2,6 +2,12 @@ (:require [cljs.analyzer.api :as ana-api]) (:use clojure.test)) +(deftest cljs-warning-test + (is (ana-api/warning-enabled? :undeclared-var) + "Undeclared-var warning is enabled by default") + (is (not (ana-api/no-warn (ana-api/warning-enabled? :undeclared-var))) + "Disabled when all warnings are disabled")) + (def warning-form '(do (defn x [a b] (+ a b)) (x 1 2 3 4))) From 90bcf2b031f7c8b05a0ff75421bb6312408eb5fc Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 17:57:21 -0400 Subject: [PATCH 1454/4033] preserve old cljs.analyze.api/analyze behavior --- src/main/clojure/cljs/analyzer/api.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index da32e5c09..09f75a491 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -69,7 +69,10 @@ ([env form name] (analyze env form name nil)) ([env form name opts] (analyze env/*compiler* env form name opts)) ([state env form name opts] - (env/with-compiler-env state + (if state + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/analyze env form name opts))) (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (ana/analyze env form name opts))))) From c1f925f85d0668859bf7d2ea1f199393df5138c7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 18:08:01 -0400 Subject: [PATCH 1455/4033] fix stray typo --- src/main/clojure/cljs/compiler/api.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index 019a59d16..5a744c7d1 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -82,4 +82,4 @@ ([state src-dir target-dir opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] - (with-bindings (api-opts opts) (comp/compile-root src-dir target-dir opts)))))) + (comp/compile-root src-dir target-dir opts))))) From a56bf592b023954ad30b0a6805c09e043f388fb7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 18:12:34 -0400 Subject: [PATCH 1456/4033] correction, inline source maps do not yet work under Node.js --- src/main/cljs/cljs/js.cljs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index b4ea751a4..12a96b4a6 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -860,6 +860,9 @@ (println (.. error -cause -stack))) (println res)))) + ;; *NOT* source mapped under Node.js + ;; source-map-support does not yet work, users will need to map + ;; themselves (cljs/eval-str st "(ns foo.bar)\n(ffirst [1 2 3])" 'foo.bar From 69f94a068019c1d5b193c898c3dddef586ba22a0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 18:30:04 -0400 Subject: [PATCH 1457/4033] stacktrace mapping functions need to take Map --- src/main/cljs/cljs/stacktrace.cljc | 90 +++++++++++++++++------------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index c08552e40..9b22a1379 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -490,35 +490,46 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" ;; ----------------------------------------------------------------------------- ;; Stacktrace Mapping +(defn remove-ext [file] + (-> file + (string/replace #"\.js$" "") + (string/replace #"\.cljs$" "") + (string/replace #"\.cljc$" "") + (string/replace #"\.clj$" ""))) + (defn mapped-line-column-call "Given a cljs.source-map source map data structure map a generated line and column back to the original line, column, and function called." - [source-map line column] - ;; source maps are 0 indexed for columns - ;; multiple segments may exist at column - ;; the last segment seems most accurate - (letfn [(get-best-column [columns column] - (last (or (get columns - (last (filter #(<= % (dec column)) - (sort (keys columns))))) - (second (first columns))))) - (adjust [mapped] - (vec (map #(%1 %2) [inc inc identity] mapped)))] - (let [default [line column nil]] - ;; source maps are 0 indexed for lines - (if-let [columns (get source-map (dec line))] - (adjust (map (get-best-column columns column) [:line :col :name])) - default)))) + [sms file line column] + (let [source-map (get sms (symbol (string/replace (remove-ext file) "/" ".")))] + ;; source maps are 0 indexed for columns + ;; multiple segments may exist at column + ;; the last segment seems most accurate + (letfn [(get-best-column [columns column] + (last (or (get columns + (last (filter #(<= % (dec column)) + (sort (keys columns))))) + (second (first columns))))) + (adjust [mapped] + (vec (map #(%1 %2) [inc inc identity] mapped)))] + (let [default [line column nil]] + ;; source maps are 0 indexed for lines + (if-let [columns (get source-map (dec line))] + (adjust (map (get-best-column columns column) [:line :col :name])) + default))))) (defn mapped-frame "Given opts and a canonicalized JavaScript stacktrace frame, return the ClojureScript frame." - [{:keys [function file line column]} sm opts] + [{:keys [function file line column]} sms opts] (let [no-source-file? (if-not file true (starts-with? file "<")) - [line' column' call] (mapped-line-column-call sm line column) - file' (if (ends-with? file ".js") - (str (subs file 0 (- (count file) 3)) ".cljs") - file)] + [line' column' call] (if no-source-file? + [line column nil] + (mapped-line-column-call sms file line column)) + file' (when-not no-source-file? + (if (ends-with? file ".js") + (str (subs file 0 (- (count file) 3)) ".cljs") + file))] {:function function :call call :file (if no-source-file? @@ -541,8 +552,9 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" identifier delimited by angle brackets. The returned mapped stacktrace will also contain :url entries to the original sources if it can be determined from the classpath." - ([stacktrace sm] (mapped-stacktrace stacktrace sm nil)) - ([stacktrace sm opts] + ([stacktrace sms] + (mapped-stacktrace stacktrace sms nil)) + ([stacktrace sms opts] (letfn [(call->function [x] (if (:call x) (hash-map :function (:call x)) @@ -555,7 +567,7 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" unmunged-call-name munged-fn-name)) function call))] - (let [mapped-frames (map (memoize #(mapped-frame % sm opts)) stacktrace)] + (let [mapped-frames (map (memoize #(mapped-frame % sms opts)) stacktrace)] ;; take each non-nil :call and optionally merge it into :function one-level ;; up to avoid replacing with local symbols, we only replace munged name if ;; we can munge call symbol back to it @@ -564,14 +576,15 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" (concat (rest (map call->function mapped-frames)) [{}]))))))) (defn mapped-stacktrace-str - "Given a vector representing the canonicalized JavaScript stacktrace - print the ClojureScript stacktrace. See mapped-stacktrace." - ([stacktrace sm] - (mapped-stacktrace-str stacktrace sm nil)) - ([stacktrace sm opts] + "Given a vector representing the canonicalized JavaScript stacktrace and a map + of library names to decoded source maps, print the ClojureScript stacktrace . + See mapped-stacktrace." + ([stacktrace sms] + (mapped-stacktrace-str stacktrace sms nil)) + ([stacktrace sms opts] (with-out-str (doseq [{:keys [function file line column]} - (mapped-stacktrace stacktrace sm opts)] + (mapped-stacktrace stacktrace sms opts)] (println "\t" (str (when function (str function " ")) "(" file (when line (str ":" line)) @@ -589,13 +602,14 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" :output-to "samples/hello/out/hello.js" :source-map true}) - (def sm - (sm/decode - (json/read-str - (slurp "samples/hello/out/hello/core.js.map") - :key-fn keyword))) + (def sms + {'hello.core + (sm/decode + (json/read-str + (slurp "samples/hello/out/hello/core.js.map") + :key-fn keyword))}) - (pp/pprint sm) + (pp/pprint sms) ;; maps to :line 5 :column 24 (mapped-stacktrace @@ -603,12 +617,12 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" :function "first" :line 6 :column 0}] - sm {:output-dir "samples/hello/out"}) + sms {:output-dir "samples/hello/out"}) (mapped-stacktrace-str [{:file "hello/core.js" :function "first" :line 6 :column 0}] - sm {:output-dir "samples/hello/out"}) + sms {:output-dir "samples/hello/out"}) ) \ No newline at end of file From 078f207884304dd39c61775345505d4da7ca9b64 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 18:39:41 -0400 Subject: [PATCH 1458/4033] CLJS-1374: cljs.js/require doesn't supply good defaults --- src/main/cljs/cljs/js.cljs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 12a96b4a6..5ee98a5ce 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -162,16 +162,19 @@ ([name cb] (require name nil cb)) ([name opts cb] - (require - {:*compiler* (env/default-compiler-env) - :*data-readers* tags/*cljs-data-readers* - :*load-fn* (or (:load opts) *load-fn*) - :*eval-fn* (or (:eval opts) *eval-fn*)} - name opts cb)) + (require nil name opts cb)) ([bound-vars name opts cb] (require bound-vars name nil opts cb)) ([bound-vars name reload opts cb] - (let [name (cond-> name (:macro-ns opts) ana/macro-ns-name)] + (let [bound-vars (merge + {:*compiler* (env/default-compiler-env) + :*data-readers* tags/*cljs-data-readers* + :*load-macros* (or (:load-macros opts) true) + :*analyze-deps* (or (:analyze-deps opts) true) + :*load-fn* (or (:load opts) *load-fn*) + :*eval-fn* (or (:eval opts) *eval-fn*)} + bound-vars) + name (cond-> name (:macro-ns opts) ana/macro-ns-name)] (when (= :reload reload) (swap! *loaded* disj name)) (when (= :reload-all reload) @@ -746,6 +749,8 @@ (def fs (js/require "fs")) (def st (cljs/empty-state)) + (set! *target* "nodejs") + (defn node-eval [{:keys [name source]}] (.runInThisContext vm source (str (munge name) ".js"))) From 952dc4bfeb99087dd29c7c9fdba1d4108f689ac5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 18:54:35 -0400 Subject: [PATCH 1459/4033] consistently provide the compiler state in the api nses if not provided by user --- src/main/clojure/cljs/analyzer/api.clj | 26 ++++++++--- src/main/clojure/cljs/build/api.clj | 61 ++++++++++++++++++++------ src/main/clojure/cljs/compiler/api.clj | 35 ++++++++++++--- 3 files changed, 96 insertions(+), 26 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index 09f75a491..78d432728 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -67,12 +67,14 @@ facilitate code walking without knowing the details of the op set." ([env form] (analyze env form nil)) ([env form name] (analyze env form name nil)) - ([env form name opts] (analyze env/*compiler* env form name opts)) + ([env form name opts] + (analyze + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + env form name opts)) ([state env form name opts] - (if state - (env/with-compiler-env state - (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] - (ana/analyze env form name opts))) + (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (ana/analyze env form name opts))))) @@ -94,7 +96,12 @@ requested via opts where :restore is false." ([src] (parse-ns src nil nil)) ([src opts] (parse-ns src nil opts)) - ([src dest opts] (parse-ns env/*compiler* src dest opts)) + ([src dest opts] + (parse-ns + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + src dest opts)) ([state src dest opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -110,7 +117,12 @@ \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a meaningful value." ([f] (analyze-file f nil)) - ([f opts] (analyze-file env/*compiler* f opts)) + ([f opts] + (analyze-file + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + f opts)) ([state f opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index a31c37173..0bbf9a5ef 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -57,19 +57,29 @@ 'example.macros : (cljs-dependents-for-macro-namespaces 'example.macros) -> ('example.core 'example.util)" - ([namespaces] (cljs-dependents-for-macro-namespaces env/*compiler* namespaces)) + ([namespaces] + (cljs-dependents-for-macro-namespaces + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env)) + namespaces)) ([state namespaces] (map :name - (let [namespaces-set (set namespaces)] - (filter (fn [x] (not-empty - (intersection namespaces-set (-> x :require-macros vals set)))) - (vals (:cljs.analyzer/namespaces @state))))))) + (let [namespaces-set (set namespaces)] + (filter (fn [x] (not-empty + (intersection namespaces-set (-> x :require-macros vals set)))) + (vals (:cljs.analyzer/namespaces @state))))))) (defn cljs-ns-dependents "Given a namespace symbol return a seq of all dependent namespaces sorted in dependency order. Will include transient dependents." - ([ns] (cljs-ns-dependents env/*compiler* ns)) + ([ns] + (cljs-ns-dependents + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env)) + ns)) ([state ns] (env/with-compiler-env state (ana/ns-dependents ns)))) @@ -85,7 +95,12 @@ "Given a ClojureScript source file return the target file. May optionally provide build options with :output-dir specified." ([src] (src-file->target-file src nil)) - ([src opts] (src-file->target-file env/*compiler* src opts)) + ([src opts] + (src-file->target-file + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + src opts)) ([state src opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -95,7 +110,12 @@ "Given a ClojureScript or Google Closure style JavaScript source file return the goog.require statement for it." ([src] (src-file->goog-require src nil)) - ([src options] (src-file->goog-require env/*compiler* src options)) + ([src options] + (src-file->goog-require + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env)) + src options)) ([state src options] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers options ana/*cljs-warning-handlers*)] @@ -126,7 +146,11 @@ uri of the corresponding source regardless of the source language extension: .cljs, .cljc, .js. Returns a map containing :relative-path a string, and :uri a URL." - ([ns] (ns->location ns env/*compiler*)) + ([ns] + (ns->location ns + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env)))) ([ns compiler-env] (closure/source-for-namespace ns compiler-env))) @@ -163,7 +187,12 @@ (defn compile "Given a Compilable, compile it and return an IJavaScript." - ([opts compilable] (compile env/*compiler* opts compilable)) + ([opts compilable] + (compile + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + opts compilable)) ([state opts compilable] (env/with-compiler-env state (closure/compile compilable opts)))) @@ -181,7 +210,10 @@ (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] - (build source opts nil)) + (build source opts + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)))) ([source opts compiler-env] (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/build source opts compiler-env)))) @@ -189,9 +221,10 @@ (defn watch "Given a source which can be compiled, watch it for changes to produce." ([source opts] - (watch source opts (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)))) + (watch source opts + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)))) ([source opts compiler-env] (watch source opts compiler-env nil)) ([source opts compiler-env stop] diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index 5a744c7d1..fb83b1e21 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -19,7 +19,12 @@ (defn emit "Given an AST node generated by the analyzer emit JavaScript as a string." - ([ast] (emit env/*compiler* ast)) + ([ast] + (emit + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env)) + ast)) ([state ast] (env/with-compiler-env state (with-out-str @@ -29,7 +34,12 @@ "Ensure that core.cljs has been loaded." ([] (comp/with-core-cljs nil)) ([opts] (with-core-cljs opts (fn []))) - ([opts body] (with-core-cljs env/*compiler* opts body)) + ([opts body] + (with-core-cljs + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + opts body)) ([state opts body] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -38,7 +48,12 @@ (defn requires-compilation? "Return true if the src file requires compilation." ([src dest] (requires-compilation? src dest nil)) - ([src dest opts] (requires-compilation? env/*compiler* src dest opts)) + ([src dest opts] + (requires-compilation? + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + src dest opts)) ([state src dest opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -59,7 +74,12 @@ If the file was not compiled returns only {:file ...}" ([src] (compile-file src)) ([src dest] (compile-file src dest)) - ([src dest opts] (compile-file env/*compiler* src dest opts)) + ([src dest opts] + (compile-file + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + src dest opts)) ([state src dest opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -78,7 +98,12 @@ in dependency order." ([src-dir] (compile-root src-dir "out")) ([src-dir target-dir] (compile-root src-dir target-dir nil)) - ([src-dir target-dir opts] (compile-root env/*compiler* src-dir target-dir opts)) + ([src-dir target-dir opts] + (compile-root + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + src-dir target-dir opts)) ([state src-dir target-dir opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] From aaa6f5d5dd5934bbfc32449f4280a013716d1e7e Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 18:58:44 -0400 Subject: [PATCH 1460/4033] add bootstrap_test folder to test paths --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index af2cd3d66..575959be9 100644 --- a/project.clj +++ b/project.clj @@ -7,7 +7,7 @@ :jvm-opts ^:replace ["-Xmx512m" "-server"] :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] - :test-paths ["src/test/clojure" "src/test/cljs"] + :test-paths ["src/test/clojure" "src/test/cljs" "src/test/bootstrap_test"] :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] From b5147410de6fd2e51a7e9121c65700def627fb35 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 19:09:51 -0400 Subject: [PATCH 1461/4033] 1.7.N build script --- script/build | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/script/build b/script/build index 684c695fc..0585558c3 100755 --- a/script/build +++ b/script/build @@ -18,7 +18,9 @@ POM_FILE="pom.xml" # where 856 is the number of commits since the v0.0 tag. It will always # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). -REVISION=`git --no-replace-objects describe --match v0.0` +MAJOR="1" +MINOR="7" +REVISION=`git --no-replace-objects describe --match v$MAJOR\.$MINOR` # Extract the version number from the string. Do this in two steps so # it is a little easier to understand. @@ -27,14 +29,14 @@ REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters TAG=r$REVISION -sed -e s/CLOJURESCRIPT_VERSION/0.0-$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" +sed -e s/CLOJURESCRIPT_VERSION/$MAJOR.$MINOR-$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` -sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE +sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major '"$MAJOR"', :minor '"$MINOR"', :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE mv $COMP_FILE src/main/clojure/cljs/util.cljc CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` -sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/main/cljs/cljs/core.cljs > $CLJS_FILE +sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""$MAJOR.$MINOR-$REVISION"\"')/' src/main/cljs/cljs/core.cljs > $CLJS_FILE mv $CLJS_FILE src/main/cljs/cljs/core.cljs rm -f src/main/cljs/cljs/core.aot.js @@ -44,11 +46,11 @@ rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn ./script/aot_core AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` -sed -e 's/0.0-0000/0.0-$REVISION/' src/main/cljs/cljs/core.aot.js > $AOT_FILE +sed -e 's/0.0-0000/$MAJOR.$MINOR-$REVISION/' src/main/cljs/cljs/core.aot.js > $AOT_FILE mv $AOT_FILE src/main/cljs/cljs/core.aot.js AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` -sed -e 's/0.0-0000/0.0-$REVISION/' src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE +sed -e 's/0.0-0000/$MAJOR.$MINOR-$REVISION/' src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server From 25019841f67c877d1d6ec7b6625f2bb997f352bb Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jul 2015 19:18:26 -0400 Subject: [PATCH 1462/4033] ClojureScript 1.7.X --- script/build | 10 +++++----- src/main/clojure/cljs/util.cljc | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/script/build b/script/build index 0585558c3..c9c381628 100755 --- a/script/build +++ b/script/build @@ -20,7 +20,7 @@ POM_FILE="pom.xml" # if the tag is v0.0.1). MAJOR="1" MINOR="7" -REVISION=`git --no-replace-objects describe --match v$MAJOR\.$MINOR` +REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so # it is a little easier to understand. @@ -29,14 +29,14 @@ REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters TAG=r$REVISION -sed -e s/CLOJURESCRIPT_VERSION/$MAJOR.$MINOR-$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" +sed -e s/CLOJURESCRIPT_VERSION/$MAJOR.$MINOR.$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major '"$MAJOR"', :minor '"$MINOR"', :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE mv $COMP_FILE src/main/clojure/cljs/util.cljc CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` -sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""$MAJOR.$MINOR-$REVISION"\"')/' src/main/cljs/cljs/core.cljs > $CLJS_FILE +sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""$MAJOR.$MINOR.$REVISION"\"')/' src/main/cljs/cljs/core.cljs > $CLJS_FILE mv $CLJS_FILE src/main/cljs/cljs/core.cljs rm -f src/main/cljs/cljs/core.aot.js @@ -46,11 +46,11 @@ rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn ./script/aot_core AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` -sed -e 's/0.0-0000/$MAJOR.$MINOR-$REVISION/' src/main/cljs/cljs/core.aot.js > $AOT_FILE +sed -e 's/0.0.0000/$MAJOR.$MINOR.$REVISION/' src/main/cljs/cljs/core.aot.js > $AOT_FILE mv $AOT_FILE src/main/cljs/cljs/core.aot.js AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` -sed -e 's/0.0-0000/$MAJOR.$MINOR-$REVISION/' src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE +sed -e 's/0.0.0000/$MAJOR.$MINOR.$REVISION/' src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index f26bd204b..7162dfd91 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -28,21 +28,21 @@ (when-let [i (:incremental *clojurescript-version*)] (str "." i)) (when-let [q (:qualifier *clojurescript-version*)] - (str "-" q)) + (str "." q)) (when (:interim *clojurescript-version*) "-SNAPSHOT")) - "0.0-0000")) + "0.0.0000")) (defn ^String compiled-by-version [f] (with-open [reader (io/reader f)] (let [match (->> reader line-seq first - (re-matches #".*ClojureScript (\d+\.\d+-\d+).*$"))] - (or (and match (second match)) "0.0-0000")))) + (re-matches #".*ClojureScript (\d+\.\d+\.\d+).*$"))] + (or (and match (second match)) "0.0.0000")))) (defn build-options [^File f] (with-open [reader (io/reader f)] (let [match (->> reader line-seq first - (re-matches #".*ClojureScript \d+\.\d+-\d+ (.*)$"))] + (re-matches #".*ClojureScript \d+\.\d+\.\d+ (.*)$"))] (and match (edn/read-string (second match)))))) (defn munge-path [ss] From 4ad28eeed638f6c4d502ddcfb229b65c95553f64 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jul 2015 07:00:07 -0400 Subject: [PATCH 1463/4033] fix TAG calculation in build script --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index c9c381628..87e18b242 100755 --- a/script/build +++ b/script/build @@ -27,7 +27,7 @@ REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` REVISION=${REVISION:5} # drop the first 5 characters REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters -TAG=r$REVISION +TAG=v$MAJOR.$MINOR.$REVISION sed -e s/CLOJURESCRIPT_VERSION/$MAJOR.$MINOR.$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" From 27abf0a29da979ae9b0bd4a6c7611b3c38070920 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jul 2015 07:14:40 -0400 Subject: [PATCH 1464/4033] v -> r --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index 87e18b242..caac8f7ba 100755 --- a/script/build +++ b/script/build @@ -27,7 +27,7 @@ REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` REVISION=${REVISION:5} # drop the first 5 characters REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters -TAG=v$MAJOR.$MINOR.$REVISION +TAG=r$MAJOR.$MINOR.$REVISION sed -e s/CLOJURESCRIPT_VERSION/$MAJOR.$MINOR.$REVISION/ < "$POM_TEMPLATE" > "$POM_FILE" From bb4a39072b66342e47c92b9799656d56239dbb24 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jul 2015 07:51:53 -0400 Subject: [PATCH 1465/4033] in verbose mode show how many sources are being optimized --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 29c0a8f80..e24a6a152 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1666,7 +1666,7 @@ (- (count (.split #"\r?\n" fdeps-str -1)) 1))] (->> (util/measure compiler-stats - "Optimize sources" + (str "Optimizing " (count js-sources) " sources") (apply optimize all-opts (remove foreign-source? js-sources))) (add-wrapper all-opts) From e37b4da4ca462871c807dc7471809526a1e7f724 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jul 2015 08:01:26 -0400 Subject: [PATCH 1466/4033] make optimize verbose string more informative, show number of sources being optimized --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e24a6a152..e31d33059 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -983,7 +983,7 @@ "Use the Closure Compiler to optimize one or more JavaScript files." [opts & sources] (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Applying optimizations" (:optimizations opts))) + (util/debug-prn "Applying optimizations" (:optimizations opts) "to" (count sources) "sources")) (let [closure-compiler (make-closure-compiler) ^List externs (load-externs opts) compiler-options (make-options opts) From 1d3e4d6dbde9111c1af0544c22e14edc6464cc65 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jul 2015 09:33:27 -0400 Subject: [PATCH 1467/4033] fix constant table stability issue Analysis cache loading path would not correctly merge in all data collected from analyzing dependencies (would drop accumulated constants). --- src/main/clojure/cljs/analyzer.cljc | 7 +++++-- src/main/clojure/cljs/env.cljc | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 754b40388..c9478741d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2691,8 +2691,11 @@ (recur (rest forms)))) (throw (AssertionError. (str "No ns form found in " src)))))))] (when (false? (:restore opts)) - (swap! env/*compiler* update-in [::namespaces] merge - (get compiler-env' ::namespaces))) + (swap! env/*compiler* + (fn [old-state] + (-> old-state + (update-in [::namespaces] merge (get compiler-env' ::namespaces)) + (update-in [::constant-table] merge (get compiler-env' ::constant-table)))))) ijs))))) #?(:clj diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 8310f7332..1dbef55f3 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -42,6 +42,7 @@ state that is accessed/maintained by many different components."} ([] (default-compiler-env {})) ([options] (atom (merge {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} + :cljs.analyzer/constant-table {} :options options} #?(:clj {:js-dependency-index (js-dependency-index options)}))))) From 9e012cc59e2ba298d5cf8b286783644e71810634 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jul 2015 09:49:42 -0400 Subject: [PATCH 1468/4033] move bootstrap test files so that `lein test` works --- project.clj | 2 +- src/main/cljs/cljs/js.cljs | 2 +- src/test/bootstrap_test/core.cljs | 4 ---- src/test/bootstrap_test/macros.clj | 4 ---- 4 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 src/test/bootstrap_test/core.cljs delete mode 100644 src/test/bootstrap_test/macros.clj diff --git a/project.clj b/project.clj index 575959be9..af2cd3d66 100644 --- a/project.clj +++ b/project.clj @@ -7,7 +7,7 @@ :jvm-opts ^:replace ["-Xmx512m" "-server"] :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] - :test-paths ["src/test/clojure" "src/test/cljs" "src/test/bootstrap_test"] + :test-paths ["src/test/clojure" "src/test/cljs"] :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 5ee98a5ce..6ec116b0a 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -760,7 +760,7 @@ (defn node-load [{:keys [name macros]} cb] (if (contains? libs name) - (let [path (str "src/test/" (cljs/ns->relpath name) + (let [path (str "src/test/cljs/" (cljs/ns->relpath name) "." (cljs.core/name (get libs name)))] (.readFile fs path "utf-8" (fn [err src] diff --git a/src/test/bootstrap_test/core.cljs b/src/test/bootstrap_test/core.cljs deleted file mode 100644 index cb7004607..000000000 --- a/src/test/bootstrap_test/core.cljs +++ /dev/null @@ -1,4 +0,0 @@ -(ns bootstrap-test.core) - -(defn foo [c d] - (* c d)) diff --git a/src/test/bootstrap_test/macros.clj b/src/test/bootstrap_test/macros.clj deleted file mode 100644 index d93559a8e..000000000 --- a/src/test/bootstrap_test/macros.clj +++ /dev/null @@ -1,4 +0,0 @@ -(ns bootstrap-test.macros) - -(defmacro foo [a b] - `(* ~a ~b)) \ No newline at end of file From 4caa4fdb0cffc78e88d68e644525c86ccc6a06a1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jul 2015 09:58:28 -0400 Subject: [PATCH 1469/4033] fix cljs.analyzer.api, can run tests again --- src/main/clojure/cljs/analyzer/api.clj | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index 78d432728..33410ec00 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -145,14 +145,16 @@ (defn all-ns "Return all namespaces. Analagous to clojure.core/all-ns but returns symbols identifying namespaces not Namespace instances." - ([] (all-ns env/*compiler*)) + ([] + (all-ns env/*compiler*)) ([state] (keys (get @state ::ana/namespaces)))) (defn find-ns "Given a namespace return the corresponding namespace analysis map. Analagous to clojure.core/find-ns." - ([sym] (find-ns env/*compiler* sym)) + ([sym] + (find-ns env/*compiler* sym)) ([state sym] {:pre [(symbol? sym)]} (get-in @state [::ana/namespaces sym]))) @@ -160,7 +162,8 @@ (defn ns-interns "Given a namespace return all the var analysis maps. Analagous to clojure.core/ns-interns but returns var analysis maps not vars." - ([ns] (ns-interns env/*compiler*)) + ([ns] + (ns-interns env/*compiler* ns)) ([state ns] {:pre [(symbol? ns)]} (merge @@ -170,7 +173,8 @@ (defn ns-publics "Given a namespace return all the public var analysis maps. Analagous to clojure.core/ns-publics but returns var analysis maps not vars." - ([ns] (ns-publics env/*compiler*)) + ([ns] + (ns-publics env/*compiler* ns)) ([state ns] {:pre [(symbol? ns)]} (->> (merge @@ -182,14 +186,16 @@ (defn ns-resolve "Given a namespace and a symbol return the corresponding var analysis map. Analagous to clojure.core/ns-resolve but returns var analysis map not Var." - ([ns sym] (ns-resolve env/*compiler* ns sym)) + ([ns sym] + (ns-resolve env/*compiler* ns sym)) ([state ns sym] {:pre [(symbol? ns) (symbol? sym)]} (get-in @state [::ana/namespaces ns :defs sym]))) (defn remove-ns "Removes the namespace named by the symbol." - ([ns] (remove-ns env/*compiler* ns)) + ([ns] + (remove-ns env/*compiler* ns)) ([state ns] {:pre [(symbol? ns)]} (swap! state update-in [::ana/namespaces] dissoc ns))) From 07c9d03dbb8149fb3416a65b2a386f5263b256b3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jul 2015 10:16:02 -0400 Subject: [PATCH 1470/4033] update uberjar script --- script/uberjar | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/script/uberjar b/script/uberjar index dd24d8aaa..afb8cdd39 100755 --- a/script/uberjar +++ b/script/uberjar @@ -12,7 +12,9 @@ set -ex # where 856 is the number of commits since the v0.0 tag. It will always # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). -REVISION=`git --no-replace-objects describe --match v0.0` +MAJOR="1" +MINOR="7" +REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so # it is a little easier to understand. @@ -20,11 +22,11 @@ REVISION=${REVISION:5} # drop the first 5 characters REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` -sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major 0, :minor 0, :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE +sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major '"$MAJOR"', :minor '"$MINOR"', :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE mv $COMP_FILE src/main/clojure/cljs/util.cljc CLJS_FILE=`mktemp /tmp/core.cljs.XXXXXXXXXXX` -sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""0.0-$REVISION"\"')/' src/main/cljs/cljs/core.cljs > $CLJS_FILE +sed -e 's/^.def \*clojurescript-version\*.*$/(def *clojurescript-version* '\""$MAJOR.$MINOR.$REVISION"\"')/' src/main/cljs/cljs/core.cljs > $CLJS_FILE mv $CLJS_FILE src/main/cljs/cljs/core.cljs rm -f src/main/cljs/cljs/core.aot.js @@ -34,11 +36,11 @@ rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn ./script/aot_core AOT_FILE=`mktemp /tmp/core.aot.js.XXXXXXXXXXX` -sed -e "s/0.0-0000/0.0-$REVISION/" src/main/cljs/cljs/core.aot.js > $AOT_FILE +sed -e "s/0.0.0000/$MAJOR.$MINOR.$REVISION/" src/main/cljs/cljs/core.aot.js > $AOT_FILE mv $AOT_FILE src/main/cljs/cljs/core.aot.js AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` -sed -e "s/0.0-0000/0.0-$REVISION/" src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE +sed -e "s/0.0.0000/$MAJOR.$MINOR-$REVISION/" src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn lein uberjar clojure.main From 85e1dce7b9e5ef2d1e0e1108a032fdf51159d0b4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 29 Jul 2015 13:09:11 -0400 Subject: [PATCH 1471/4033] remove identical? check on strings in get-expander-ns for ClojureScript JVM --- src/main/clojure/cljs/analyzer.cljc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c9478741d..7195fe5e8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2265,16 +2265,13 @@ (defn get-expander-ns [env ^String nstr] (cond - (identical? "clojure.core" nstr) - #?(:clj (find-ns 'cljs.core) - :cljs (find-macros-ns CLJS_CORE_MACROS_SYM)) - (identical? "clojure.repl" nstr) - #?(:clj (find-ns 'cljs.repl) - :cljs (find-macros-ns 'cljs.repl)) - #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] - :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) - :else - (some-> env :ns :require-macros (get (symbol nstr)) find-ns))) + #?@(:clj [(= "clojure.core" nstr) (find-ns 'cljs.core)] + :cljs [(identical? "clojure.core" nstr) (find-macros-ns CLJS_CORE_MACROS_SYM)]) + #?@(:clj [(= "clojure.repl" nstr) (find-ns 'cljs.repl)] + :cljs [(identical? "clojure.repl" nstr) (find-macros-ns 'cljs.repl)]) + #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] + :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) + :else (some-> env :ns :require-macros (get (symbol nstr)) find-ns))) (defn get-expander* [sym env] (when-not (or (not (nil? (gets env :locals sym))) ; locals hide macros From 1fe476053ff890246fb163936e6ff4870171b65c Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 29 Jul 2015 13:09:25 -0400 Subject: [PATCH 1472/4033] add back bootstrap testing nses --- src/test/cljs/bootstrap_test/core.cljs | 4 ++++ src/test/cljs/bootstrap_test/macros.clj | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 src/test/cljs/bootstrap_test/core.cljs create mode 100644 src/test/cljs/bootstrap_test/macros.clj diff --git a/src/test/cljs/bootstrap_test/core.cljs b/src/test/cljs/bootstrap_test/core.cljs new file mode 100644 index 000000000..756a1f003 --- /dev/null +++ b/src/test/cljs/bootstrap_test/core.cljs @@ -0,0 +1,4 @@ +(ns bootstrap-test.core) + +(defn foo [a b] + (+ a b)) \ No newline at end of file diff --git a/src/test/cljs/bootstrap_test/macros.clj b/src/test/cljs/bootstrap_test/macros.clj new file mode 100644 index 000000000..d93559a8e --- /dev/null +++ b/src/test/cljs/bootstrap_test/macros.clj @@ -0,0 +1,4 @@ +(ns bootstrap-test.macros) + +(defmacro foo [a b] + `(* ~a ~b)) \ No newline at end of file From 3f543643571b49b23cd70f715ce8c568a07b3d1e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 30 Jul 2015 10:32:02 -0400 Subject: [PATCH 1473/4033] fix custom passes regression --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7195fe5e8..3c3ed8335 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2524,9 +2524,10 @@ (defn analyze* [env form name opts] (let [passes *passes* - passes (when (nil? passes) + passes (if (nil? passes) #?(:clj [infer-type ns-side-effects] - :cljs [infer-type])) + :cljs [infer-type]) + passes) form (if (instance? LazySeq form) (if (seq form) form ()) form) From bc0cd45ecf84c43ba3bab82d895e6b64c2ec8938 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 30 Jul 2015 10:33:22 -0400 Subject: [PATCH 1474/4033] make cljs.js/analyze more useful, return the last AST, fix some bugs support providing custom passes --- src/main/cljs/cljs/js.cljs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 6ec116b0a..8fa219d20 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -383,10 +383,11 @@ the-ns (or (:ns opts) 'cljs.user) bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] - ((fn analyze-loop [] + ((fn analyze-loop [last-ast] (binding [env/*compiler* (:*compiler* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (create-ns (:*cljs-ns* bound-vars)) + ana/*passes* (:*passes* bound-vars) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try @@ -416,9 +417,9 @@ (fn [res] (if (:error res) (cb res) - (analyze-loop)))) - (recur))))) - (cb {:value nil})))))))))) + (analyze-loop ast)))) + (recur ast))))) + (cb {:value last-ast}))))))) nil))) (defn analyze "Analyze ClojureScript source. The compiler state will be populated with @@ -454,6 +455,9 @@ (analyze* {:*compiler* state :*data-readers* tags/*cljs-data-readers* + :*passes* (or (:passes opts) ana/*passes*) + :*analyze-deps* (or (:analyze-deps opts) true) + :*load-macros* (or (:load-macros opts) true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} source name opts cb))) @@ -743,7 +747,8 @@ source name opts cb))) (comment - (require '[cljs.js :as cljs]) + (require '[cljs.js :as cljs] + '[cljs.analyzer :as ana]) (def vm (js/require "vm")) (def fs (js/require "fs")) @@ -769,6 +774,15 @@ (.error js/console err)))))) (cb nil))) + (defn elide-env [env ast opts] + (dissoc ast :env)) + + (cljs/analyze st "(+ 1 1)" nil + {:passes [ana/infer-type elide-env] + :eval cljs/js-eval} + (fn [{:keys [value]}] + (println value))) + (cljs/eval st '(defn foo [a b] (+ a b)) {:eval cljs/js-eval} (fn [res] From 2c80e25a917234a208e33f4163a13e07b5b01c7d Mon Sep 17 00:00:00 2001 From: spinningtopsofdoom Date: Thu, 30 Jul 2015 09:41:17 -0500 Subject: [PATCH 1475/4033] CLJS-836: Replace seq-based iterators with direct iterator for all non-seq collections that use SeqIterator Iterators are now created for PesistentHashMap, PersistentHashSet, PersistentQueue, and any type created by defrecord. --- benchmark/cljs/benchmark_runner.cljs | 20 ++++ src/main/cljs/cljs/core.cljs | 141 ++++++++++++++++++++++++++- src/main/clojure/cljs/core.cljc | 3 + src/test/cljs/cljs/core_test.cljs | 37 +++++++ 4 files changed, 198 insertions(+), 3 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index e4c886428..9fdced899 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -322,6 +322,26 @@ (simple-benchmark [r r] (last r) 1) (println) +(println ";; iterators") +(def ipmap (apply hash-map (range 2000))) + +(println ";; Sequence iterator") +(simple-benchmark [s (seq ipmap)] + (let [iter (seq-iter s)] + (loop [v nil] + (if (.hasNext iter) + (recur (.next iter)) + v))) + 1000) +(println ";; Direct iterator") +(simple-benchmark [] + (let [iter (-iterator ipmap)] + (loop [v nil] + (if (.hasNext iter) + (recur (.next iter)) + v))) + 1000) + (println ";;; comprehensions") (simple-benchmark [xs (range 512)] (last (for [x xs y xs] (+ x y))) 1) (simple-benchmark [xs (vec (range 512))] (last (for [x xs y xs] (+ x y))) 4) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a097b8388..b707e1aca 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5330,6 +5330,21 @@ reduces them without incurring seq initialization" ;;; PersistentQueue ;;; +(deftype PersistentQueueIter [^:mutable fseq riter] + Object + (hasNext [_] + (or (and (some? fseq) (seq fseq)) (and (some? riter) (.hasNext riter)))) + (next [_] + (cond + (some? fseq) + (let [ret (first fseq)] + (set! fseq (next fseq)) + ret) + (and (some? riter) ^boolean (.hasNext riter)) + (.next riter) + :else (throw (js/Error. "No such element")))) + (remove [_] (js/Error. "Unsupported operation"))) + (deftype PersistentQueueSeq [meta front rear ^:mutable __hash] Object (toString [coll] @@ -5380,6 +5395,10 @@ reduces them without incurring seq initialization" ICloneable (-clone [coll] (PersistentQueue. meta count front rear __hash)) + IIterable + (-iterator [coll] + (PersistentQueueIter. front (-iterator rear))) + IWithMeta (-with-meta [coll meta] (PersistentQueue. meta count front rear __hash)) @@ -5607,6 +5626,19 @@ reduces them without incurring seq initialization" (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 + (hasNext [_] + (or (< i base-count) (.hasNext ext-map-iter))) + (next [_] + (if (< i base-count) + (let [k (nth fields i)] + (set! i (inc i)) + [k (-lookup record k)]) + (.next ext-map-iter))) + (remove [_] (js/Error. "Unsupported operation"))) + ;; EXPERIMENTAL: subject to change (deftype ES6EntriesIterator [^:mutable s] Object @@ -6109,6 +6141,44 @@ reduces them without incurring seq initialization" (declare ArrayNode) + (deftype NodeIterator [arr ^:mutable i ^:mutable next-entry ^:mutable next-iter] + Object + (advance [this] + (let [len (alength arr)] + (loop [] + (if (< i len) + (let [key (aget arr i) + node-or-val (aget arr (inc i)) + ^boolean found + (cond (some? key) + (set! next-entry [key node-or-val]) + (some? node-or-val) + (let [new-iter (-iterator node-or-val)] + (if ^boolean (.hasNext new-iter) + (set! next-iter new-iter) + false)) + :else false)] + (set! i (+ i 2)) + (if found true (recur))) + false)))) + (hasNext [this] + (or (some? next-entry) (some? next-iter) (.advance this))) + (next [this] + (cond + (some? next-entry) + (let [ret next-entry] + (set! next-entry nil) + ret) + (some? next-iter) + (let [ret (.next next-iter)] + (when-not ^boolean (.hasNext next-iter) + (set! next-iter nil)) + ret) + ^boolean (.advance this) + (.next this) + :else (throw (js/Error. "No such element")))) + (remove [_] (js/Error. "Unsupported operation"))) + (deftype BitmapIndexedNode [edit ^:mutable bitmap ^:mutable arr] Object (inode-assoc [inode shift hash key val added-leaf?] @@ -6303,7 +6373,11 @@ reduces them without incurring seq initialization" :else inode))))) (kv-reduce [inode f init] - (inode-kv-reduce arr f init))) + (inode-kv-reduce arr f init)) + + IIterable + (-iterator [coll] + (NodeIterator. arr 0 nil nil))) (set! (.-EMPTY BitmapIndexedNode) (BitmapIndexedNode. nil 0 (make-array 0))) @@ -6320,6 +6394,26 @@ reduces them without incurring seq initialization" (recur (inc i) j bitmap)) (BitmapIndexedNode. edit bitmap new-arr))))) +(deftype ArrayNodeIterator [arr ^:mutable i ^:mutable next-iter] + Object + (hasNext [this] + (let [len (alength arr)] + (loop [] + (if-not (and (some? next-iter) ^boolean (.hasNext next-iter)) + (if (< i len) + (let [node (aget arr i)] + (set! i (inc i)) + (when (some? node) + (set! next-iter (-iterator node))) + (recur)) + false) + true)))) + (next [this] + (if ^boolean (.hasNext this) + (.next next-iter) + (throw (js/Error. "No such element")))) + (remove [_] (js/Error. "Unsupported operation"))) + (deftype ArrayNode [edit ^:mutable cnt ^:mutable arr] Object (inode-assoc [inode shift hash key val added-leaf?] @@ -6415,7 +6509,11 @@ reduces them without incurring seq initialization" @init (recur (inc i) init))) (recur (inc i) init))) - init))))) + init)))) + + IIterable + (-iterator [coll] + (ArrayNodeIterator. arr 0 nil))) (defn- hash-collision-node-find-index [arr cnt key] (let [lim (* 2 cnt)] @@ -6522,7 +6620,11 @@ reduces them without incurring seq initialization" editable)))))) (kv-reduce [inode f init] - (inode-kv-reduce arr f init))) + (inode-kv-reduce arr f init)) + + IIterable + (-iterator [coll] + (NodeIterator. arr 0 nil nil))) (defn- create-node ([shift key1 val1 key2hash key2 val2] @@ -6660,6 +6762,18 @@ reduces them without incurring seq initialization" (declare TransientHashMap) +(deftype HashMapIter [nil-val root-iter ^:mutable seen] + Object + (hasNext [_] + (and ^boolean seen ^boolean (.hasNext root-iter))) + (next [_] + (if-not ^boolean seen + (do + (set! seen true) + nil-val) + (.next root-iter))) + (remove [_] (js/Error. "Unsupported operation"))) + (deftype PersistentHashMap [meta cnt root ^boolean has-nil? nil-val ^:mutable __hash] Object (toString [coll] @@ -6685,6 +6799,13 @@ reduces them without incurring seq initialization" ICloneable (-clone [_] (PersistentHashMap. meta cnt root has-nil? nil-val __hash)) + IIterable + (-iterator [coll] + (let [root-iter (if ^boolean root (-iterator root) nil-iter)] + (if has-nil? + (HashMapIter. nil-val root-iter false) + root-iter))) + IWithMeta (-with-meta [coll meta] (PersistentHashMap. meta cnt root has-nil? nil-val __hash)) @@ -7812,6 +7933,16 @@ reduces them without incurring seq initialization" (declare TransientHashSet) +(deftype HashSetIter [iter] + Object + (hasNext [_] + (.hasNext iter)) + (next [_] + (if ^boolean (.hasNext iter) + (aget (.-tail (.next iter)) 0) + (throw (js/Error. "No such element")))) + (remove [_] (js/Error. "Unsupported operation"))) + (deftype PersistentHashSet [meta hash-map ^:mutable __hash] Object (toString [coll] @@ -7835,6 +7966,10 @@ reduces them without incurring seq initialization" ICloneable (-clone [_] (PersistentHashSet. meta hash-map __hash)) + IIterable + (-iterator [coll] + (HashSetIter. (-iterator hash-map))) + IWithMeta (-with-meta [coll meta] (PersistentHashSet. meta hash-map __hash)) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index cead46570..fd1f2d197 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1634,6 +1634,9 @@ `(~'-seq [this#] (seq (concat [~@(map #(core/list `vector (keyword %) %) base-fields)] ~'__extmap))) + 'IIterable + `(~'-iterator [~gs] (RecordIter. 0 ~gs ~(count base-fields) [~@(map keyword base-fields)] (-iterator ~'__extmap) )) + 'IPrintWithWriter `(~'-pr-writer [this# writer# opts#] (let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# pr-writer "" " " "" opts# keyval#))] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d529efa05..62a89f9ef 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1418,6 +1418,43 @@ (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])] From cf0374cfe9e85ae77f26c062190241f09caa5544 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 30 Jul 2015 11:15:41 -0500 Subject: [PATCH 1476/4033] Fully qualify core/str in case and time macros. --- src/main/clojure/cljs/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index fd1f2d197..36c7ef189 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2036,7 +2036,7 @@ (last clauses) `(throw (js/Error. - (core/str "No matching clause: " ~e)))) + (cljs.core/str "No matching clause: " ~e)))) env &env pairs (reduce (core/fn [m [test expr]] @@ -2443,7 +2443,7 @@ [expr] `(let [start# (.getTime (js/Date.)) ret# ~expr] - (prn (core/str "Elapsed time: " (- (.getTime (js/Date.)) start#) " msecs")) + (prn (cljs.core/str "Elapsed time: " (- (.getTime (js/Date.)) start#) " msecs")) ret#)) (core/defmacro simple-benchmark From 1b3b04310cbd2046a1f67b9512b305b2580dd9b6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Jul 2015 16:04:41 -0400 Subject: [PATCH 1477/4033] verify that eval in a specific ns works --- src/main/cljs/cljs/js.cljs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 8fa219d20..349042170 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -779,12 +779,12 @@ (cljs/analyze st "(+ 1 1)" nil {:passes [ana/infer-type elide-env] - :eval cljs/js-eval} + :eval node-eval} (fn [{:keys [value]}] (println value))) (cljs/eval st '(defn foo [a b] (+ a b)) - {:eval cljs/js-eval} + {:eval node-eval} (fn [res] (println res))) @@ -797,27 +797,35 @@ "(defn foo [a b] (+ a b)) (defn bar [c d] (+ c d))" nil - {:eval cljs/js-eval} + {:eval node-eval} (fn [res] (println res))) (cljs/eval-str st "1" nil - {:eval cljs/js-eval + {:eval node-eval :context :expr} (fn [res] (println res))) (cljs/eval-str st "(def x 1)" nil - {:eval cljs/js-eval + {:eval node-eval :context :expr :def-emits-var true} (fn [res] (println res))) (cljs/eval st '(ns foo.bar) - {:eval cljs/js-eval} + {:eval node-eval} + (fn [res] + (println res))) + + (cljs/eval st '(def x 1) + {:eval node-eval + :context :expr + :def-emits-var true + :ns 'foo.bar} (fn [res] (println res))) From 32e7559afab7b8b6620481ef44932697d3dd81d4 Mon Sep 17 00:00:00 2001 From: Samuel Miller Date: Wed, 29 Jul 2015 19:19:54 -0600 Subject: [PATCH 1478/4033] CLJS-1316 let does not detect invalid binding vector when it contains destructuring In order for assert-args to work properly you need to pass fnname first. Fixes issue for let and loop --- src/main/clojure/cljs/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 36c7ef189..307b6b3cc 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -692,7 +692,7 @@ the binding-forms are bound to their respective init-exprs or parts therein." [bindings & body] - (assert-args + (assert-args let (vector? bindings) "a vector for its binding" (even? (count bindings)) "an even number of forms in binding vector") `(let* ~(destructure bindings) ~@body)) @@ -702,7 +702,7 @@ the binding-forms are bound to their respective init-exprs or parts therein. Acts as a recur target." [bindings & body] - (assert-args + (assert-args loop (vector? bindings) "a vector for its binding" (even? (count bindings)) "an even number of forms in binding vector") (core/let [db (destructure bindings)] From 34c3b8985ed8197d90f441c46d168c4024a20eb8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Jul 2015 17:01:26 -0400 Subject: [PATCH 1479/4033] CLJS-1376: Printing in a tagged literal data form function and unknown object types print as #object ExceptionInfo printed as #error --- src/main/cljs/cljs/core.cljs | 15 ++++++++++++--- src/test/cljs/cljs/core_test.cljs | 8 ++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b707e1aca..17699f942 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8810,7 +8810,11 @@ reduces them without incurring seq initialization" (-write writer obj)) ^boolean (goog/isFunction obj) - (write-all writer "#<" (str obj) ">") + (let [name (.-name obj) + name (if (or (nil? name) (gstring/isEmpty name)) + "Function" + name)] + (write-all writer "#object[" name " \"" (str obj) "\"]")) (instance? js/Date obj) (let [normalize (fn [n len] @@ -8834,7 +8838,12 @@ reduces them without incurring seq initialization" (implements? IPrintWithWriter obj) (-pr-writer obj writer opts) - :else (write-all writer "#<" (str obj) ">"))))) + :else + (let [name (.. obj -constructor -name) + name (if (or (nil? name) (gstring/isEmpty name)) + "Object" + name)] + (write-all writer "#object[" name " " (str obj) "]")))))) (defn- pr-writer "Prefer this to pr-seq, because it makes the printing function @@ -9838,7 +9847,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." ;;; ExceptionInfo (defn- pr-writer-ex-info [obj writer opts] - (-write writer "#ExceptionInfo{:message ") + (-write writer "#error {:message ") (pr-writer (.-message obj) writer opts) (when (.-data obj) (-write writer ", :data ") diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 62a89f9ef..909c11461 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2022,10 +2022,10 @@ (catch ExceptionInfo e (ex-data e))))) (is (instance? js/Error (ex-info "asdf" {:foo 1}))) - (is (= (pr-str (ex-info "abc" {:x 1})) "#ExceptionInfo{:message \"abc\", :data {:x 1}}")) - (is (= (pr-str (ex-info "abc" {:x 1} "def")) "#ExceptionInfo{:message \"abc\", :data {:x 1}, :cause \"def\"}")) - (is (= (.toString (ex-info "abc" {:x 1} "def")) "#ExceptionInfo{:message \"abc\", :data {:x 1}, :cause \"def\"}")) - (is (= (str (ex-info "abc" {:x 1} "def")) "#ExceptionInfo{:message \"abc\", :data {:x 1}, :cause \"def\"}")) + (is (= (pr-str (ex-info "abc" {:x 1})) "#error {:message \"abc\", :data {:x 1}}")) + (is (= (pr-str (ex-info "abc" {:x 1} "def")) "#error {:message \"abc\", :data {:x 1}, :cause \"def\"}")) + (is (= (.toString (ex-info "abc" {:x 1} "def")) "#error {:message \"abc\", :data {:x 1}, :cause \"def\"}")) + (is (= (str (ex-info "abc" {:x 1} "def")) "#error {:message \"abc\", :data {:x 1}, :cause \"def\"}")) (is (not (instance? cljs.core.ExceptionInfo (js/Error.))))) (deftest test-435 From e90d7e0fd3e1f24e4841d81da25218bd41cc2567 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Jul 2015 17:25:23 -0400 Subject: [PATCH 1480/4033] #object printing for deftype w/o printing implementation, and for Volatile and for Atom --- src/main/cljs/cljs/core.cljs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 17699f942..6e48636c9 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8839,11 +8839,15 @@ reduces them without incurring seq initialization" (-pr-writer obj writer opts) :else - (let [name (.. obj -constructor -name) - name (if (or (nil? name) (gstring/isEmpty name)) - "Object" - name)] - (write-all writer "#object[" name " " (str obj) "]")))))) + (if (.. obj -constructor -cljs$lang$ctorStr) + (write-all writer + "#object[" (.replace (.. obj -constructor -cljs$lang$ctorStr) + (js/RegExp. "/" "g") ".") "]") + (let [name (.. obj -constructor -name) + name (if (or (nil? name) (gstring/isEmpty name)) + "Object" + name)] + (write-all writer "#object[" name " " (str obj) "]"))))))) (defn- pr-writer "Prefer this to pr-seq, because it makes the printing function @@ -9050,15 +9054,15 @@ reduces them without incurring seq initialization" Atom (-pr-writer [a writer opts] - (-write writer "#")) + (-write writer "#object [cljs.core.Atom ") + (pr-writer {:val (.-state a)} writer opts) + (-write writer "]")) Volatile (-pr-writer [a writer opts] - (-write writer "#")) + (-write writer "#object [cljs.core.Volatile ") + (pr-writer {:val (.-state a)} writer opts) + (-write writer "]")) Var (-pr-writer [a writer opts] From 1c62c10e6db0701ece23a8d86a252c625b7a01ed Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Jul 2015 17:32:16 -0400 Subject: [PATCH 1481/4033] make macro example less trivial, transient ns helper --- src/main/cljs/cljs/js.cljs | 3 ++- src/test/cljs/bootstrap_test/helper.clj | 4 ++++ src/test/cljs/bootstrap_test/macros.clj | 5 +++-- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 src/test/cljs/bootstrap_test/helper.clj diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 349042170..b7460ee66 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -761,7 +761,8 @@ (def libs {'bootstrap-test.core :cljs - 'bootstrap-test.macros :clj}) + 'bootstrap-test.macros :clj + 'bootstrap-test.helper :clj}) (defn node-load [{:keys [name macros]} cb] (if (contains? libs name) diff --git a/src/test/cljs/bootstrap_test/helper.clj b/src/test/cljs/bootstrap_test/helper.clj new file mode 100644 index 000000000..3a3fd73c2 --- /dev/null +++ b/src/test/cljs/bootstrap_test/helper.clj @@ -0,0 +1,4 @@ +(ns bootstrap-test.helper) + +(defn bar [a b] + `(* ~a ~b)) diff --git a/src/test/cljs/bootstrap_test/macros.clj b/src/test/cljs/bootstrap_test/macros.clj index d93559a8e..8648ffff2 100644 --- a/src/test/cljs/bootstrap_test/macros.clj +++ b/src/test/cljs/bootstrap_test/macros.clj @@ -1,4 +1,5 @@ -(ns bootstrap-test.macros) +(ns bootstrap-test.macros + (:require [bootstrap-test.helper :refer [bar]])) (defmacro foo [a b] - `(* ~a ~b)) \ No newline at end of file + (bar a b)) \ No newline at end of file From 0bc44129c08aa344a2c515fa6af82909d5c50ac1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Jul 2015 20:02:38 -0400 Subject: [PATCH 1482/4033] fix bad example --- src/main/cljs/cljs/js.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index b7460ee66..9c678c6e7 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -790,9 +790,9 @@ (println res))) (cljs/compile st "(defprotocol IFoo (foo [this]))" - (fn [js-source] + (fn [{:keys [value]}] (println "Source:") - (println js-source))) + (println value))) (cljs/eval-str st "(defn foo [a b] (+ a b)) From b143919103bb290c51f41c621b099fc56a8e1444 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 31 Jul 2015 07:49:28 -0400 Subject: [PATCH 1483/4033] put finishing touches on cljs.js, make private anything that isn't meant to be doc'ed. analyze -> analyze-str and compile -> compile-str for consistency. In the future we'll like have data oriented versions of these that can take a forms seq. --- src/main/cljs/cljs/js.cljs | 50 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 9c678c6e7..958d646b5 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -97,7 +97,7 @@ [{:keys [source] :as resource}] (js/eval source)) -(defn wrap-error [ex] +(defn- wrap-error [ex] {:error ex}) (defn empty-state @@ -119,16 +119,16 @@ (let [sm (sm/decode (.parse js/JSON sm-json))] (swap! state assoc-in [:source-maps ns] sm))) -(defn sm-data [] +(defn- sm-data [] (atom {:source-map (sorted-map) :gen-col 0 :gen-line 0})) -(defn prefix [s pre] +(defn- prefix [s pre] (str pre s)) -(defn append-source-map +(defn- append-source-map [state name source sb sm-data {:keys [output-dir asset-path] :as opts}] (let [t (.valueOf (js/Date.)) smn (if name @@ -233,7 +233,7 @@ (declare ns-side-effects analyze-deps) -(defn load-deps +(defn- load-deps ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars ana-env lib deps nil cb)) ([bound-vars ana-env lib deps opts cb] @@ -256,9 +256,9 @@ (cb res))))) (cb {:value nil}))))) -(declare analyze*) +(declare analyze-str*) -(defn analyze-deps +(defn- analyze-deps ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars ana-env lib deps nil cb)) ([bound-vars ana-env lib deps opts cb] @@ -278,7 +278,7 @@ (if resource (let [{:keys [name lang source]} resource] (condp = lang - :clj (analyze* bound-vars source name opts + :clj (analyze-str* bound-vars source name opts (fn [res] (if-not (:error res) (analyze-deps bound-vars ana-env lib (next deps) opts cb) @@ -297,7 +297,7 @@ (str "Could not analyze dep " dep) cause)))))) (cb {:value nil})))))) -(defn load-macros [bound-vars k macros reload reloads opts cb] +(defn- load-macros [bound-vars k macros reload reloads opts cb] (if (seq macros) (let [nsym (first (vals macros)) k (or (k reload) @@ -314,7 +314,7 @@ (cb res))))) (cb {:value nil}))) -(defn ns-side-effects +(defn- ns-side-effects ([bound-vars ana-env ast opts cb] (ns-side-effects false bound-vars ana-env ast opts cb)) ([load bound-vars ana-env {:keys [op] :as ast} opts cb] @@ -376,7 +376,7 @@ (check-uses-and-load-macros {:value nil})))) (cb {:value ast})))) -(defn analyze* [bound-vars source name opts cb] +(defn- analyze-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) aenv (ana/empty-env) @@ -421,7 +421,7 @@ (recur ast))))) (cb {:value last-ast}))))))) nil))) -(defn analyze +(defn analyze-str "Analyze ClojureScript source. The compiler state will be populated with the results of analyzes. The parameters: @@ -446,13 +446,13 @@ map will contain a key :error with an ex-info instance describing the cause of failure." ([state source cb] - (analyze state source nil cb)) + (analyze-str state source nil cb)) ([state source name cb] - (analyze state source name nil cb)) + (analyze-str state source name nil cb)) ([state source name opts cb] {:pre [(atom? state) (string? source) (valid-name? name) (valid-opts? opts) (fn? cb)]} - (analyze* + (analyze-str* {:*compiler* state :*data-readers* tags/*cljs-data-readers* :*passes* (or (:passes opts) ana/*passes*) @@ -465,7 +465,7 @@ ;; ----------------------------------------------------------------------------- ;; Eval -(defn eval* [bound-vars form opts cb] +(defn- eval* [bound-vars form opts cb] (let [the-ns (or (:ns opts) 'cljs.user) bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] @@ -533,7 +533,7 @@ ;; ----------------------------------------------------------------------------- ;; Compile -(defn compile* [bound-vars source name opts cb] +(defn- compile-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) aenv (ana/empty-env) @@ -581,7 +581,7 @@ name source sb @comp/*source-map-data* opts)) (cb {:value (.toString sb)}))))))))))) -(defn compile +(defn compile-str "Compile ClojureScript source into JavaScript. The parameters: state (atom) @@ -605,13 +605,13 @@ will contain a key :error with an ex-info instance describing the cause of failure." ([state source cb] - (compile state source nil cb)) + (compile-str state source nil cb)) ([state source name cb] - (compile state source name nil cb)) + (compile-str state source name nil cb)) ([state source name opts cb] {:pre [(atom? state) (string? source) (valid-name? name) (valid-opts? opts) (fn? cb)]} - (compile* + (compile-str* {:*compiler* state :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (or (:analyze-deps opts) true) @@ -624,7 +624,7 @@ ;; ----------------------------------------------------------------------------- ;; Evaluate String -(defn eval-str* [bound-vars source name opts cb] +(defn- eval-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) aenv (ana/empty-env) @@ -778,7 +778,7 @@ (defn elide-env [env ast opts] (dissoc ast :env)) - (cljs/analyze st "(+ 1 1)" nil + (cljs/analyze-str st "(+ 1 1)" nil {:passes [ana/infer-type elide-env] :eval node-eval} (fn [{:keys [value]}] @@ -789,7 +789,7 @@ (fn [res] (println res))) - (cljs/compile st "(defprotocol IFoo (foo [this]))" + (cljs/compile-str st "(defprotocol IFoo (foo [this]))" (fn [{:keys [value]}] (println "Source:") (println value))) @@ -830,7 +830,7 @@ (fn [res] (println res))) - (cljs/compile st "(defn foo\n[a b]\n(+ a b))" 'cljs.foo + (cljs/compile-str st "(defn foo\n[a b]\n(+ a b))" 'cljs.foo {:verbose true :source-map true} (fn [js-source] (println "Source:") From 4fd1e20d5548a05245c0bdb6a02e5ca4e568439e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 31 Jul 2015 07:55:59 -0400 Subject: [PATCH 1484/4033] remove dead code --- src/main/cljs/cljs/core.cljs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6e48636c9..dfd364515 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8773,7 +8773,6 @@ reduces them without incurring seq initialization" [obj writer opts] (cond (nil? obj) (-write writer "nil") - (undefined? obj) (-write writer "#") :else (do (when (print-meta? opts obj) @@ -8781,8 +8780,6 @@ reduces them without incurring seq initialization" (pr-writer (meta obj) writer opts) (-write writer " ")) (cond - (nil? obj) (-write writer "nil") - ;; handle CLJS ctors ^boolean (.-cljs$lang$type obj) (.cljs$lang$ctorPrWriter obj obj writer opts) From 86f8255d4536e4e2022adb467db46293c7616f74 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 31 Jul 2015 09:37:43 -0400 Subject: [PATCH 1485/4033] CLJS-1384: cljs.js/eval-str doc that :ns returned upon success --- src/main/cljs/cljs/js.cljs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 958d646b5..2c14d65b1 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -728,8 +728,9 @@ cb (function) callback, will be invoked with a map. If succesful the map will contain - a :value key with the result of evaluation. If unsuccessful will contain - a :error key with an ex-info instance describing the cause of failure." + a :value key with the result of evaluation and :ns the current namespace. + If unsuccessful will contain a :error key with an ex-info instance describing + the cause of failure." ([state source cb] (eval-str state source nil cb)) ([state source name cb] @@ -860,6 +861,20 @@ (println (.. error -cause -stack))) (println "Result:" res)))) + (cljs/compile-str st + "(ns foo.bar (:require-macros [bootstrap-test.macros :refer [foo]]))\n(foo 4 4)" + 'foo.bar + {:verbose true + :source-map true + :eval node-eval + :load node-load} + (fn [{:keys [error] :as res}] + (if error + (do + (println "Error:" error) + (println (.. error -cause -stack))) + (println "Result:" res)))) + (cljs/eval-str st "(ns foo.bar)\n(first [1 2 3])" 'foo.bar From f3cf45d745af5e63500375d084dd0d7b19c40dac Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Jul 2015 11:22:11 -0400 Subject: [PATCH 1486/4033] CLJS-1385: cljs.js/compile-str does not correctly track the current ns. --- src/main/cljs/cljs/js.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2c14d65b1..adf7b9809 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -541,11 +541,11 @@ the-ns (or (:ns opts) 'cljs.user) bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] - ((fn compile-loop [] + ((fn compile-loop [ns] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (create-ns (:*cljs-ns* bound-vars)) + ana/*cljs-ns* ns + *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try @@ -573,13 +573,13 @@ (fn [res] (if (:error res) (cb res) - (compile-loop)))) - (recur))) + (compile-loop (:name ast))))) + (recur ns))) (do (when (:source-map opts) (append-source-map env/*compiler* name source sb @comp/*source-map-data* opts)) - (cb {:value (.toString sb)}))))))))))) + (cb {:value (.toString sb)})))))))) the-ns))) (defn compile-str "Compile ClojureScript source into JavaScript. The parameters: From 33db236eb25df3e92d252959e1b6ea4e3b388dd0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Jul 2015 11:25:17 -0400 Subject: [PATCH 1487/4033] cljs.js/analyze-str also needs to track the current ns --- src/main/cljs/cljs/js.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index adf7b9809..fe6dfa142 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -383,10 +383,10 @@ the-ns (or (:ns opts) 'cljs.user) bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] - ((fn analyze-loop [last-ast] + ((fn analyze-loop [last-ast ns] (binding [env/*compiler* (:*compiler* bound-vars) - ana/*cljs-ns* (:*cljs-ns* bound-vars) - *ns* (create-ns (:*cljs-ns* bound-vars)) + ana/*cljs-ns* ns + *ns* (create-ns ns) ana/*passes* (:*passes* bound-vars) r/*data-readers* (:*data-readers* bound-vars) comp/*source-map-data* (:*sm-data* bound-vars)] @@ -417,9 +417,9 @@ (fn [res] (if (:error res) (cb res) - (analyze-loop ast)))) - (recur ast))))) - (cb {:value last-ast}))))))) nil))) + (analyze-loop ast (:name ast))))) + (recur ast ns))))) + (cb {:value last-ast}))))))) nil the-ns))) (defn analyze-str "Analyze ClojureScript source. The compiler state will be populated with From 601e1fe6783707a1c33693a0d3d297aa878a8610 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Jul 2015 13:12:15 -0400 Subject: [PATCH 1488/4033] eval hack is only required in Node.js + :simple because of modules --- src/main/cljs/cljs/core.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index dfd364515..a86a7da28 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10104,12 +10104,12 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn find-ns-obj [ns] (let [munged-ns (munge (str ns)) segs (.split munged-ns ".")] - (if ^boolean js/COMPILED - (js/eval munged-ns) - (case *target* - "nodejs" (find-ns-obj* js/global segs) - "default" (find-ns-obj* js/window segs) - (throw (js/Error. (str "find-ns-obj not supported for target " *target*))))))) + (case *target* + "nodejs" (if ^boolean js/COMPILED + (js/eval munged-ns) + (find-ns-obj* js/global segs)) + "default" (find-ns-obj* js/window segs) + (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) (defn ns-interns* [sym] (let [ns-obj (find-ns-obj sym) From b6803dbdabe1bcf370bd1d0eff48f6590be0b5a4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Jul 2015 13:45:18 -0400 Subject: [PATCH 1489/4033] 1.7.28 --- README.md | 6 +++--- changes.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d752250dc..d3955c8ac 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 0.0-3308 +Latest stable release: 1.7.28 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "0.0-3308"] +[org.clojure/clojurescript "1.7.28"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 0.0-3308 org.clojure clojurescript - 0.0-3308 + 1.7.28 ``` diff --git a/changes.md b/changes.md index 0a4fafd76..143fab63e 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,51 @@ +## 1.7.28 + +## Enhancement +* New namespace cljs.js provides analysis, compilation, and eval +* CLJS-1360: Refactor JS module processing to work with recent Google Closure compiler changes +* CLJS-1282: Add a :pprint option to the default reporter in cljs.test +* CLJS-1308: :analyze-path should be extended to take a vector of paths +* CLJS-1230: ES 2015 Module Processing +* CLJS-1231: AMD Module Processing +* CLJS-1092: CommonJS Module processing + +## Changes +* CLJS-1376: Printing in a tagged literal data form +* CLJS-836: Replace seq-based iterators with direct iterator for all non-seq collections that use SeqIterator +* CLJS-1367: Expose default-warning-handler and warning-enabled? +* CLJS-1267: Added the :end-test-all-vars and :end-test-vars events to have end events for all cljs.test api functions +* CLJS-1337: Move parse ns side-effects into a separate compiler pass +* CLJS-1247: Split out error printing from regular printing +* CLJS-1329: Support for reading #js tagged literals in bootstrap +* CLJS-1191: rebased patch Update clojure.walk to the current version on clojure +* CLJS-1321: remove getNamespace & getName method calls from defrecord +* CLJS-1281: Preserve test order +* CLJS-934: In the REPL return vars after defs + +## Fixes +* CLJS-1316 let does not detect invalid binding vector when it contains destructuring +* CLJS-1033: take a drop accept nil as n argument +* CLJS-1324: Compiler fails to raise warning/error when invoking a keyword without arguments +* CLJS-1352: cljs.js: Allow conditional readers +* CLJS-1348: meta is printing for def at REPL +* CLJS-1342: cljs.reader/read-string should throw Error when not called with string +* CLJS-1341: Fix CommonJS conversion bug +* CLJS-1333: Analyze meta on quoted symbols +* CLJS-1210: Javascript built-in arguments replaces nil arguments locally defined by let +* CLJS-1248: alter-meta! does not work on vars +* CLJS-1276: var equality differs from Clojure +* CLJS-1310: ns libspec error message misses :import +* CLJS-428: Added step to escape docstrings with */ and associated test +* CLJS-1331: Regex literal emits invalid JS +* CLJS-1338: NPE in confirm-var-exists if suffix is ".." +* CLJS-1319: Cannot locate module namespace when filename contains dash +* CLJS-1317: Incremental compilation issues for :nodejs target +* CLJS-1227 Raise error when if form has more than 4 statements +* CLJS-1306: Browser REPL :asset-path with leading slash breaks source map support +* CLJS-1290: :refer does not work with Closure JS namespaces +* CLJS-1307: Doc for ns missing +* CLJS-1301: local :foreign-libs are not picked up the first time browser REPL is started + ## 0.0-3308 ## Changes From 9f30110b90197b6f2eec0f6adb7b6b53e059eeac Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Jul 2015 13:45:38 -0400 Subject: [PATCH 1490/4033] typo --- changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.md b/changes.md index 143fab63e..fc434d193 100644 --- a/changes.md +++ b/changes.md @@ -1,6 +1,6 @@ ## 1.7.28 -## Enhancement +## Enhancements * New namespace cljs.js provides analysis, compilation, and eval * CLJS-1360: Refactor JS module processing to work with recent Google Closure compiler changes * CLJS-1282: Add a :pprint option to the default reporter in cljs.test From 4e2203012287ae260192ffb1809598b9442f34c9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Jul 2015 15:39:30 -0400 Subject: [PATCH 1491/4033] hide a few more things in cljs.js --- src/main/cljs/cljs/js.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index fe6dfa142..32a5ef92a 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -22,7 +22,7 @@ (js/goog.require "cljs.core$macros") -(defn debug-prn +(defn- debug-prn [& args] (binding [*print-fn* *print-err-fn*] (apply println args))) @@ -38,13 +38,13 @@ 0 (- (count file) 5))] (symbol (demunge lib-name)))) -(defn atom? [x] +(defn- atom? [x] (instance? Atom x)) -(defn valid-name? [x] +(defn- valid-name? [x] (or (nil? x) (symbol? x) (string? x))) -(defn valid-opts? [x] +(defn- valid-opts? [x] (or (nil? x) (map? x))) (defonce From 24e63f43677f3883a59a1ad4c09b63e7a1340039 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 1 Aug 2015 06:13:01 -0400 Subject: [PATCH 1492/4033] should minify foreign libs under :simple as well --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e31d33059..1a66cb917 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1420,7 +1420,7 @@ (defn foreign-deps-str [opts sources] (letfn [(to-js-str [ijs] - (let [url (or (and (= (:optimizations opts) :advanced) + (let [url (or (and (#{:advanced :simple} (:optimizations opts)) (:url-min ijs)) (:url ijs))] (slurp url)))] From d92b3004046d1f83ab76a2b94f4129cb16e47848 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 1 Aug 2015 07:11:10 -0400 Subject: [PATCH 1493/4033] CLJS-1386: Symbols should be added to the constants table --- src/main/clojure/cljs/analyzer.cljc | 12 ++++++--- src/main/clojure/cljs/compiler.cljc | 41 ++++++++++++++++++----------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3c3ed8335..884aaa201 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -357,12 +357,16 @@ (defn gen-constant-id [value] (let [prefix (cond - (keyword? value) "constant$keyword$" + (keyword? value) "cst$kw$" + (symbol? value) "cst$sym$" :else (throw #?(:clj (Exception. (str "constant type " (type value) " not supported")) :cljs (js/Error. (str "constant type " (type value) " not supported"))))) - name (-> value (str) (subs 1) (string/replace "-" "_DASH_") (munge) (string/replace "." "$"))] + name (if (keyword? value) + (subs (str value) 1) + (str value)) + name (-> name (string/replace "-" "_DASH_") (munge) (string/replace "." "$"))] (symbol (str prefix name)))) (defn- register-constant! @@ -2227,7 +2231,9 @@ "Finds the var associated with sym" [env sym] (if ^boolean (:quoted? env) - (analyze-wrap-meta {:op :constant :env env :form sym :tag 'cljs.core/Symbol}) + (do + (register-constant! env sym) + (analyze-wrap-meta {:op :constant :env env :form sym :tag 'cljs.core/Symbol})) (let [{:keys [line column]} (meta sym) env (if-not (nil? line) (assoc env :line line) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index fa6addbab..ffe93408e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -250,16 +250,9 @@ (emit-constant (hash kw)) (emits ")"))) -(defmethod emit-constant #?(:clj clojure.lang.Keyword :cljs Keyword) [x] - (if (-> @env/*compiler* :options :emit-constants) - (let [value (-> @env/*compiler* ::ana/constant-table x)] - (emits "cljs.core." value)) - (emits-keyword x) - )) - -(defmethod emit-constant #?(:clj clojure.lang.Symbol :cljs Symbol) [x] - (let [ns (namespace x) - name (name x) +(defn emits-symbol [sym] + (let [ns (namespace sym) + name (name sym) symstr (if-not (nil? ns) (str ns "/" name) name)] @@ -270,11 +263,23 @@ (emits ",") (emit-constant symstr) (emits ",") - (emit-constant (hash x)) + (emit-constant (hash sym)) (emits ",") (emit-constant nil) (emits ")"))) +(defmethod emit-constant #?(:clj clojure.lang.Keyword :cljs Keyword) [x] + (if (-> @env/*compiler* :options :emit-constants) + (let [value (-> @env/*compiler* ::ana/constant-table x)] + (emits "cljs.core." value)) + (emits-keyword x))) + +(defmethod emit-constant #?(:clj clojure.lang.Symbol :cljs Symbol) [x] + (if (-> @env/*compiler* :options :emit-constants) + (let [value (-> @env/*compiler* ::ana/constant-table x)] + (emits "cljs.core." value)) + (emits-symbol x))) + ;; tagged literal support (defmethod emit-constant #?(:clj java.util.Date :cljs js/Date) [^java.util.Date date] @@ -1293,11 +1298,17 @@ ;; TODO: needs fixing, table will include other things than keywords - David (defn emit-constants-table [table] - (doseq [[keyword value] table] - (let [ns (namespace keyword) - name (name keyword)] + (doseq [[sym value] table] + (let [ns (namespace sym) + name (name sym)] (emits "cljs.core." value " = ") - (emits-keyword keyword) + (cond + (keyword? sym) (emits-keyword sym) + (symbol? sym) (emits-symbol sym) + :else (throw + (ex-info + (str "Cannot emit constant for type " (type sym)) + {:error :invalid-constant-type}))) (emits ";\n")))) #?(:clj From e2e5525f5b12cf1f9e5b2143ffd9477426ee489b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 1 Aug 2015 07:58:22 -0400 Subject: [PATCH 1494/4033] make cljs.js core dumping configurable, it doubles output size and slows down compile time. --- src/main/cljs/cljs/js.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.clj b/src/main/cljs/cljs/js.clj index 20b0480b0..1c4a51a6b 100644 --- a/src/main/cljs/cljs/js.clj +++ b/src/main/cljs/cljs/js.clj @@ -18,4 +18,7 @@ ~@body)) (defmacro dump-core [] - `(quote ~(get-in @env/*compiler* [::ana/namespaces 'cljs.core]))) + (let [state @env/*compiler*] + (if-not (false? (get-in state [:options :dump-core])) + `(quote ~(get-in state [::ana/namespaces 'cljs.core])) + `(hash-map)))) From f14b561640cd5da3aa52d5425943734635ee749e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 1 Aug 2015 11:49:57 -0400 Subject: [PATCH 1495/4033] CLJS-1387: support local Closure libs that conform to classpath cljs.js-deps/find-classpath-lib didn't add :closure-lib true cljs.closure/lib-rel-path didn't handle nil lib-path --- src/main/clojure/cljs/closure.clj | 18 ++++++++++-------- src/main/clojure/cljs/js_deps.cljc | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1a66cb917..40066da49 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1186,14 +1186,16 @@ (- (count (.split #"\r?\n" fdeps-str -1)) 1) 0)}))))))) -(defn lib-rel-path [{:keys [lib-path url] :as ijs}] - (if (.endsWith lib-path ".js") - (util/get-name url) - (let [path (util/path url)] - (string/replace - path - (str (io/file (System/getProperty "user.dir") lib-path) File/separator) - "")))) +(defn lib-rel-path [{:keys [lib-path url provides] :as ijs}] + (if (nil? lib-path) + (str (string/replace (first provides) #"\." File/separator) ".js") + (if (.endsWith lib-path ".js") + (util/get-name url) + (let [path (util/path url)] + (string/replace + path + (str (io/file (System/getProperty "user.dir") lib-path) File/separator) + ""))))) (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 5b0508f70..969fef52a 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -308,7 +308,7 @@ JavaScript library containing provide/require 'declarations'." io/resource)] (let [{:keys [provides] :as lib-info} (library-graph-node lib-resource)] (if (some #{(name lib)} provides) - lib-info + (assoc lib-info :closure-lib true) (binding [*out* *err*] (println (format From d946a58153ec5132063f1ac29acd8d4bcd1bdeb2 Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Thu, 30 Jul 2015 20:38:19 -0500 Subject: [PATCH 1496/4033] CLJS-1311: Improve error reporting when converting JavaScript modules Show warnings and errors from Google Closure compiler when processing JS modules. Pull :closure-warnings and :closure-extra-annotations into set-options function so we can set those options and :pretty-print for module processing. --- src/main/clojure/cljs/closure.clj | 89 ++++++++++++++++++------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 40066da49..67a7408ab 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -125,6 +125,38 @@ (defmethod js-source-file BufferedInputStream [^String name ^BufferedInputStream source] (SourceFile/fromInputStream name source)) +(def check-level + {:error CheckLevel/ERROR + :warning CheckLevel/WARNING + :off CheckLevel/OFF}) + +(def warning-types + {:access-controls DiagnosticGroups/ACCESS_CONTROLS + :ambiguous-function-decl DiagnosticGroups/AMBIGUOUS_FUNCTION_DECL + :debugger-statement-present DiagnosticGroups/DEBUGGER_STATEMENT_PRESENT + :check-regexp DiagnosticGroups/CHECK_REGEXP + :check-types DiagnosticGroups/CHECK_TYPES + :check-useless-code DiagnosticGroups/CHECK_USELESS_CODE + :check-variables DiagnosticGroups/CHECK_VARIABLES + :const DiagnosticGroups/CONST + :constant-property DiagnosticGroups/CONSTANT_PROPERTY + :deprecated DiagnosticGroups/DEPRECATED + :duplicate-message DiagnosticGroups/DUPLICATE_MESSAGE + :es5-strict DiagnosticGroups/ES5_STRICT + :externs-validation DiagnosticGroups/EXTERNS_VALIDATION + :fileoverview-jsdoc DiagnosticGroups/FILEOVERVIEW_JSDOC + :global-this DiagnosticGroups/GLOBAL_THIS + :internet-explorer-checks DiagnosticGroups/INTERNET_EXPLORER_CHECKS + :invalid-casts DiagnosticGroups/INVALID_CASTS + :missing-properties DiagnosticGroups/MISSING_PROPERTIES + :non-standard-jsdoc DiagnosticGroups/NON_STANDARD_JSDOC + :strict-module-dep-check DiagnosticGroups/STRICT_MODULE_DEP_CHECK + :tweaks DiagnosticGroups/TWEAKS + :undefined-names DiagnosticGroups/UNDEFINED_NAMES + :undefined-variables DiagnosticGroups/UNDEFINED_VARIABLES + :unknown-defines DiagnosticGroups/UNKNOWN_DEFINES + :visiblity DiagnosticGroups/VISIBILITY}) + (defn set-options "TODO: Add any other options that we would like to support." [opts ^CompilerOptions compiler-options] @@ -157,39 +189,18 @@ (when (contains? opts :print-input-delimiter) (set! (.printInputDelimiter compiler-options) - (:print-input-delimiter opts)))) + (:print-input-delimiter opts))) -(def check-level - {:error CheckLevel/ERROR - :warning CheckLevel/WARNING - :off CheckLevel/OFF}) + (when (contains? opts :closure-warnings) + (doseq [[type level] (:closure-warnings opts)] + (. compiler-options + (setWarningLevel (type warning-types) (level check-level))))) -(def warning-types - {:access-controls DiagnosticGroups/ACCESS_CONTROLS - :ambiguous-function-decl DiagnosticGroups/AMBIGUOUS_FUNCTION_DECL - :debugger-statement-present DiagnosticGroups/DEBUGGER_STATEMENT_PRESENT - :check-regexp DiagnosticGroups/CHECK_REGEXP - :check-types DiagnosticGroups/CHECK_TYPES - :check-useless-code DiagnosticGroups/CHECK_USELESS_CODE - :check-variables DiagnosticGroups/CHECK_VARIABLES - :const DiagnosticGroups/CONST - :constant-property DiagnosticGroups/CONSTANT_PROPERTY - :deprecated DiagnosticGroups/DEPRECATED - :duplicate-message DiagnosticGroups/DUPLICATE_MESSAGE - :es5-strict DiagnosticGroups/ES5_STRICT - :externs-validation DiagnosticGroups/EXTERNS_VALIDATION - :fileoverview-jsdoc DiagnosticGroups/FILEOVERVIEW_JSDOC - :global-this DiagnosticGroups/GLOBAL_THIS - :internet-explorer-checks DiagnosticGroups/INTERNET_EXPLORER_CHECKS - :invalid-casts DiagnosticGroups/INVALID_CASTS - :missing-properties DiagnosticGroups/MISSING_PROPERTIES - :non-standard-jsdoc DiagnosticGroups/NON_STANDARD_JSDOC - :strict-module-dep-check DiagnosticGroups/STRICT_MODULE_DEP_CHECK - :tweaks DiagnosticGroups/TWEAKS - :undefined-names DiagnosticGroups/UNDEFINED_NAMES - :undefined-variables DiagnosticGroups/UNDEFINED_VARIABLES - :unknown-defines DiagnosticGroups/UNKNOWN_DEFINES - :visiblity DiagnosticGroups/VISIBILITY}) + (when (contains? opts :closure-extra-annotations) + (. compiler-options + (setExtraAnnotationNames (map name (:closure-extra-annotations opts))))) + + compiler-options) (defn ^CompilerOptions make-options "Create a CompilerOptions object and set options from opts map." @@ -209,9 +220,6 @@ (or (true? val) (false? val)) (.setDefineToBooleanLiteral compiler-options key val) :else (println "value for" key "must be string, int, float, or bool")))) - (doseq [[type level] (:closure-warnings opts)] - (. compiler-options - (setWarningLevel (type warning-types) (level check-level)))) (if-let [extra-annotations (:closure-extra-annotations opts)] (. compiler-options (setExtraAnnotationNames (map name extra-annotations)))) (when (contains? opts :source-map) @@ -1259,12 +1267,19 @@ :commonjs module-type))) +(defn make-convert-js-module-options [opts] + (-> opts + (select-keys [:closure-warnings + :pretty-print + :closure-extra-annotations]) + (set-options (CompilerOptions.)))) + (util/compile-if can-convert-commonjs? (defmethod convert-js-module :commonjs [js opts] (let [{:keys [file module-type]} js ^List externs '() ^List source-files (get-source-files module-type opts) - ^CompilerOptions options (CompilerOptions.) + ^CompilerOptions options (make-convert-js-module-options opts) closure-compiler (doto (make-closure-compiler) (.init externs source-files options)) es6-loader (if is-new-es6-loader? @@ -1276,6 +1291,7 @@ (when (= (:module-type js) :amd) (.process (TransformAMDToCJSModule. closure-compiler) nil root))) (.process cjs nil root) + (report-failure (.getResult closure-compiler)) (.toSource closure-compiler root)))) (util/compile-if can-convert-es6? @@ -1283,7 +1299,7 @@ (let [{:keys [file module-type]} js ^List externs '() ^List source-files (get-source-files module-type opts) - ^CompilerOptions options (doto (CompilerOptions.) + ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) (.setLanguageOut CompilerOptions$LanguageMode/ECMASCRIPT5)) closure-compiler (doto (make-closure-compiler) @@ -1294,6 +1310,7 @@ cjs (ProcessEs6Modules. closure-compiler es6-loader true) ^Node root (get-root-node file closure-compiler)] (.processFile cjs root) + (report-failure (.getResult closure-compiler)) (.toSource closure-compiler root)))) (defmethod convert-js-module :default [js opts] From 49f3ea9c1e5064bacb3e13d29f5f3f18af1f4bc1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 3 Aug 2015 06:50:05 -0400 Subject: [PATCH 1497/4033] CLJS-1388: Stacktrace element handling for :output-dir w/o file/line/column --- src/main/cljs/cljs/stacktrace.cljc | 46 ++++++++++++++++++------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 9b22a1379..fc8510c0f 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -58,23 +58,25 @@ [repl-env st err opts] st) (defn parse-file-line-column [flc] - (let [xs (string/split flc #":") - [pre [line column]] - (reduce - (fn [[pre post] [x i]] - (if (<= i 2) - [pre (conj post x)] - [(conj pre x) post])) - [[] []] (map vector xs (range (count xs) 0 -1))) - file (string/join ":" pre)] - [(cond-> file - (starts-with? file "(") (string/replace "(" "")) - (parse-int - (cond-> line - (ends-with? line ")") (string/replace ")" ""))) - (parse-int - (cond-> column - (ends-with? column ")") (string/replace ")" "")))])) + (if-not (re-find #":" flc) + [flc nil nil] + (let [xs (string/split flc #":") + [pre [line column]] + (reduce + (fn [[pre post] [x i]] + (if (<= i 2) + [pre (conj post x)] + [(conj pre x) post])) + [[] []] (map vector xs (range (count xs) 0 -1))) + file (string/join ":" pre)] + [(cond-> file + (starts-with? file "(") (string/replace "(" "")) + (parse-int + (cond-> line + (ends-with? line ")") (string/replace ")" ""))) + (parse-int + (cond-> column + (ends-with? column ")") (string/replace ")" "")))]))) (defn parse-file "Given a browser file url convert it into a relative path that can be used @@ -215,7 +217,7 @@ [file line column] (parse-file-line-column flc)] (if (and file function line column) {:file (parse-file repl-env file opts) - :function function + :function (string/trim function) :line line :column column} (when-not (string/blank? function) @@ -244,6 +246,14 @@ vec)) (comment + (parse-stacktrace {} + "cljs$core$seq@out/cljs/core.js:3999:17 + cljs$core$first@out/cljs/core.js:4018:22 + cljs$core$ffirst@out/cljs/core.js:5161:39 + global code" + {:ua-product :safari} + {:output-dir "out"}) + (parse-stacktrace {:host "localhost" :port 9000} "cljs$core$seq@http://localhost:9000/out/cljs/core.js:4259:17 cljs$core$first@http://localhost:9000/out/cljs/core.js:4289:22 From fb12e78321e741226e7c65c8eb188dd8def24f9e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 3 Aug 2015 07:50:44 -0400 Subject: [PATCH 1498/4033] CLJS-1296: browser REPL should queue prints before connection then flush after connection clojure.browser.repl now immediately sets up printing introduce a printing queue upon connection if the queue is not empty flush it --- src/main/cljs/clojure/browser/repl.cljs | 18 +++++++++++++++--- src/main/clojure/cljs/repl/browser.clj | 5 ++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index e9998189f..b661531cf 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -17,8 +17,9 @@ clojure.browser.repl (:require [goog.dom :as gdom] [goog.object :as gobj] + [goog.array :as garray] [goog.userAgent.product :as product] - [clojure.browser.net :as net] + [clojure.browser.net :as net] [clojure.browser.event :as event] ;; repl-connection callback will receive goog.require('cljs.repl') ;; and monkey-patched require expects to be able to derive it @@ -27,10 +28,21 @@ [cljs.repl])) (def xpc-connection (atom nil)) +(def print-queue (array)) + +(defn flush-print-queue! [conn] + (doseq [str print-queue] + (net/transmit conn :print str)) + (garray/clear print-queue)) (defn repl-print [data] - (if-let [conn @xpc-connection] - (net/transmit conn :print (pr-str data)))) + (.push print-queue (pr-str data)) + (when-let [conn @xpc-connection] + (flush-print-queue! conn))) + +(set! *print-fn* repl-print) +(set! *print-err-fn* repl-print) +(set! *print-newline* true) (defn get-ua-product [] (cond diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 6dd032c06..9c708de81 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -143,7 +143,10 @@ (cljsc/-compile '[(set! *print-fn* clojure.browser.repl/repl-print) (set! *print-err-fn* clojure.browser.repl/repl-print) - (set! *print-newline* true)] {}) + (set! *print-newline* true) + (when (pos? (count clojure.browser.repl/print-queue)) + (clojure.browser.repl/flush-print-queue! + @clojure.browser.repl/xpc-connection))] {}) identity)) (defn add-in-order [{:keys [expecting fns]} order f] From b7309fdf009fe5c6f1302ab65ef407497d9e046b Mon Sep 17 00:00:00 2001 From: Maria Neise Date: Wed, 29 Jul 2015 10:44:06 -0500 Subject: [PATCH 1499/4033] CLJS-1177: A compiler support for non-Closure transforms (JSX, etc) Adding support for preprocessing foreign libs. Users can implement the js-transforms function and then add a :preprocess option to their foreign lib. Works in conjunction with JS module processing or without. Both functions, js-transforms and convert-js-modules, now expect and return an IJavaScript. --- src/main/clojure/cljs/analyzer.cljc | 8 +++- src/main/clojure/cljs/closure.clj | 59 ++++++++++++++++++----------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 884aaa201..132f03f7e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -87,7 +87,8 @@ :munged-namespace true :ns-var-clash true :extend-type-invalid-method-shape true - :unsupported-js-module-type true}) + :unsupported-js-module-type true + :unsupported-preprocess-value true}) (def js-reserved #{"arguments" "abstract" "boolean" "break" "byte" "case" @@ -339,6 +340,11 @@ (str "Unsupported JavaScript module type " module-type " for foreign library " file ".")) +(defmethod error-message :unsupported-preprocess-value + [warning-type {:keys [preprocess file]}] + (str "Unsupported preprocess value " preprocess " for foreign library " + file ".")) + (defn default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 67a7408ab..d97f9dde9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1247,21 +1247,24 @@ (let [module-root (get-js-module-root file)] (ES6ModuleLoader. closure-compiler module-root)))) -(defn ^Node get-root-node [file closure-compiler] - (let [^CompilerInput input (->> (slurp file) - (js-source-file file) +(defn ^Node get-root-node [ijs closure-compiler] + (let [^CompilerInput input (->> (deps/-source ijs) + (js-source-file (:file ijs)) (CompilerInput.))] (.getAstRoot input closure-compiler))) (defn get-source-files [module-type opts] (->> (:foreign-libs opts) (filter #(= (:module-type %) module-type)) - (map #(js-source-file (:file %) (slurp (:file %)))))) + (map (fn [lib] + (let [lib (deps/load-foreign-library lib)] + (js-source-file (:file lib) (deps/-source lib))))))) (defmulti convert-js-module - "Takes a JavaScript module and rewrites it into a Google Closure-compatible - form. Returns the source of the new module as a single string." - (fn [{module-type :module-type :as js} opts] + "Takes a JavaScript module as an IJavaScript and rewrites it into a Google + Closure-compatible form. Returns an IJavaScript with the converted module + code set as source." + (fn [{module-type :module-type :as ijs} opts] (if (and (= module-type :amd) can-convert-amd?) ;; AMD modules are converted via CommonJS modules :commonjs @@ -1275,8 +1278,8 @@ (set-options (CompilerOptions.)))) (util/compile-if can-convert-commonjs? - (defmethod convert-js-module :commonjs [js opts] - (let [{:keys [file module-type]} js + (defmethod convert-js-module :commonjs [ijs opts] + (let [{:keys [file module-type]} ijs ^List externs '() ^List source-files (get-source-files module-type opts) ^CompilerOptions options (make-convert-js-module-options opts) @@ -1286,17 +1289,17 @@ (make-es6-loader source-files) (make-es6-loader closure-compiler file)) cjs (ProcessCommonJSModules. closure-compiler es6-loader) - ^Node root (get-root-node file closure-compiler)] + ^Node root (get-root-node ijs closure-compiler)] (util/compile-if can-convert-amd? - (when (= (:module-type js) :amd) + (when (= module-type :amd) (.process (TransformAMDToCJSModule. closure-compiler) nil root))) (.process cjs nil root) (report-failure (.getResult closure-compiler)) - (.toSource closure-compiler root)))) + (assoc ijs :source (.toSource closure-compiler root))))) (util/compile-if can-convert-es6? - (defmethod convert-js-module :es6 [js opts] - (let [{:keys [file module-type]} js + (defmethod convert-js-module :es6 [ijs opts] + (let [{:keys [file module-type]} ijs ^List externs '() ^List source-files (get-source-files module-type opts) ^CompilerOptions options (doto (make-convert-js-module-options opts) @@ -1308,14 +1311,24 @@ (make-es6-loader source-files) (make-es6-loader closure-compiler file)) cjs (ProcessEs6Modules. closure-compiler es6-loader true) - ^Node root (get-root-node file closure-compiler)] + ^Node root (get-root-node ijs closure-compiler)] (.processFile cjs root) (report-failure (.getResult closure-compiler)) - (.toSource closure-compiler root)))) + (assoc ijs :source (.toSource closure-compiler root))))) -(defmethod convert-js-module :default [js opts] - (ana/warning :unsupported-js-module-type @env/*compiler* js) - (deps/-source js)) +(defmethod convert-js-module :default [ijs opts] + (ana/warning :unsupported-js-module-type @env/*compiler* ijs) + ijs) + +(defmulti js-transforms + "Takes an IJavaScript with the source code set as source, transforms the + source code and returns an IJavascript with the new code set as source." + (fn [ijs opts] + (:preprocess ijs))) + +(defmethod js-transforms :default [ijs opts] + (ana/warning :unsupported-preprocess-value @env/*compiler* ijs) + ijs) (defn write-javascript "Write or copy a JavaScript file to output directory. Only write if the file @@ -1332,9 +1345,11 @@ :group (:group js)}] (when-not (.exists out-file) (util/mkdirs out-file) - (if (:module-type js) - (spit out-file (convert-js-module js opts)) - (spit out-file (deps/-source js)))) + (spit out-file + (cond-> (assoc js :source (deps/-source js)) + (:preprocess js) (js-transforms opts) + (:module-type js) (convert-js-module opts) + true deps/-source))) (if (map? js) (merge js ijs) ijs))) From 5ef5f10ab87b8cafbb13ce5b55e81d5b4fd655d9 Mon Sep 17 00:00:00 2001 From: Martin Klepsch Date: Mon, 3 Aug 2015 17:53:34 +0200 Subject: [PATCH 1500/4033] provide define macro to support proper use of goog.define Using Closure defines [1] has always been possible by attaching the appropriate JSDoc metadata to a var: (def ^{:jsdoc ["@define {boolean}"]} APP_DEBUG true) When compiling with :optimizations :none however these defines cannot be overridden. This commit introduces a `cljs.core/define` macro that helps with emitting proper `goog.define` calls. Defines done with `goog.define` can be overriden at runtime by supplying `CLOSURE_UNCOMPILED_DEFINES` or `CLOSURE_DEFINES`. [2] (ns your-app.core) (define APP_DEBUG true) emits the following `goog.define` call: /** @define {boolean} */ goog.define('your_app.core.APP_DEBUG', true) When compiling with `:none` the map passed to `:closure-defines` is now added to the main file, allowing `goog.define` to pick the right value at runtime. [1] https://developers.google.com/closure/compiler/docs/js-for-compiler?hl=en#tag-define [2] http://google.github.io/closure-library/api/source/closure/goog/base.js.src.html#l51 --- src/main/clojure/cljs/closure.clj | 7 +++++-- src/main/clojure/cljs/core.cljc | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d97f9dde9..3db68b3d3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1123,7 +1123,8 @@ (defn output-main-file [opts] (let [asset-path (or (:asset-path opts) - (util/output-directory opts))] + (util/output-directory opts)) + closure-defines (json/write-str (:closure-defines opts))] (case (:target opts) :nodejs (output-one-file opts @@ -1134,10 +1135,12 @@ "}\n" "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" + "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "goog.require(\"" (comp/munge (:main opts)) "\");\n" "goog.require(\"cljs.nodejscli\");\n")) (output-one-file opts - (str "if(typeof goog == \"undefined\") document.write('');\n" + (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "if(typeof goog == \"undefined\") document.write('');\n" "document.write('');\n" "document.write('');\n"))))) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 307b6b3cc..fe4e9cc92 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -685,6 +685,36 @@ :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs))))) (reduce process-entry [] bents))))) +(core/defmacro define + "Defines a var using `goog.define`. Passed default value must be + string, number or boolean. + + Default value can be overridden at compile time using the + compiler option `:closure-defines`. When overriding the string + you need to pass to `:closure-defines` is the munged version + of the original var. + + Example: + (ns your-app.core) + (define DEBUG! false) + ;; can be overridden with + :closure-defines {\"your_app.core.DEBUG_BANG_\" true}" + [sym default] + (assert-args define + (core/or (core/string? default) + (core/number? default) + (core/true? default) + (core/false? default)) "a string, number or boolean as default value") + (core/let [defname (cljs.compiler/munge (core/str *ns* "/" sym)) + type (core/cond + (core/string? default) "string" + (core/number? default) "number" + (core/or (core/true? default) (core/false? default)) "boolean")] + `(do + (declare ~(symbol sym)) + (~'js* ~(core/str "/** @define {" type "} */")) + (goog/define ~defname ~default)))) + (core/defmacro let "binding => binding-form init-expr From 952096fe6e8a768c4dfdf47e19b4e6bfe176da09 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 3 Aug 2015 14:59:09 -0400 Subject: [PATCH 1501/4033] fix docstring --- src/main/clojure/cljs/core.cljc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index fe4e9cc92..d45b8d9da 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -690,15 +690,13 @@ string, number or boolean. Default value can be overridden at compile time using the - compiler option `:closure-defines`. When overriding the string - you need to pass to `:closure-defines` is the munged version - of the original var. + compiler option `:closure-defines`. Example: (ns your-app.core) (define DEBUG! false) ;; can be overridden with - :closure-defines {\"your_app.core.DEBUG_BANG_\" true}" + :closure-defines {\"your-app.core.DEBUG!\" true}" [sym default] (assert-args define (core/or (core/string? default) From 37ce674742f2969597cbb3b5e25dfa0cc77bd619 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 3 Aug 2015 17:03:53 -0400 Subject: [PATCH 1502/4033] define -> goog-define --- src/main/clojure/cljs/core.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index d45b8d9da..c63d3aac0 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -685,7 +685,7 @@ :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs))))) (reduce process-entry [] bents))))) -(core/defmacro define +(core/defmacro goog-define "Defines a var using `goog.define`. Passed default value must be string, number or boolean. @@ -694,11 +694,11 @@ Example: (ns your-app.core) - (define DEBUG! false) + (goog-define DEBUG! false) ;; can be overridden with :closure-defines {\"your-app.core.DEBUG!\" true}" [sym default] - (assert-args define + (assert-args goog-define (core/or (core/string? default) (core/number? default) (core/true? default) From 2789067fa95a72c9ed00f8261d3f02a444675288 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 3 Aug 2015 21:37:50 -0400 Subject: [PATCH 1503/4033] bump to latest Closure Compiler release --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index c2f46391d..47a218a86 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20150609 + v20150729 org.clojure diff --git a/project.clj b/project.clj index af2cd3d66..a231703b0 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] - [com.google.javascript/closure-compiler "v20150609"] + [com.google.javascript/closure-compiler "v20150729"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 6c2ce0763..4401e74b7 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.7.0" -CLOSURE_RELEASE="20150609" +CLOSURE_RELEASE="20150729" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150505-021ed5b3" RHINO_RELEASE="1_7R5" From 12de563413bf0ebb7e469784e25b73a685753567 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Aug 2015 08:12:18 -0400 Subject: [PATCH 1504/4033] 4 space indent for pom files --- ...e-closure-library-third-party.pom.template | 229 ++++++++++-------- .../google-closure-library.pom.template | 225 +++++++++-------- 2 files changed, 258 insertions(+), 196 deletions(-) diff --git a/script/closure-library-release/google-closure-library-third-party.pom.template b/script/closure-library-release/google-closure-library-third-party.pom.template index 522de8436..b04c2cfa3 100644 --- a/script/closure-library-release/google-closure-library-third-party.pom.template +++ b/script/closure-library-release/google-closure-library-third-party.pom.template @@ -1,111 +1,142 @@ - - 4.0.0 - org.clojure - google-closure-library-third-party - RELEASE_VERSION - jar - Google Closure Library Third-Party Extensions + + 4.0.0 + org.clojure + google-closure-library-third-party + RELEASE_VERSION + jar + Google Closure Library Third-Party Extensions - - org.sonatype.oss - oss-parent - 9 - + + org.sonatype.oss + oss-parent + 9 + - http://code.google.com/p/closure-library/ + http://code.google.com/p/closure-library/ - - The Google Closure Library is a collection of JavaScript code - designed for use with the Google Closure JavaScript Compiler. + + The Google Closure Library is a collection of JavaScript code + designed for use with the Google Closure JavaScript Compiler. - This non-official distribution was prepared by the ClojureScript - team at http://clojure.org/ + This non-official distribution was prepared by the ClojureScript + team at http://clojure.org/ - This package contains extensions to the Google Closure Library - using third-party components, which may be distributed under - licenses other than the Apache license. Licenses for individual - library components may be found in source-code comments. - + This package contains extensions to the Google Closure Library + using third-party components, which may be distributed under + licenses other than the Apache license. Licenses for individual + library components may be found in source-code comments. + - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.html - repo - - Note: the Google Closure library third-party extensions - contain code under a variety of licenses, which may be found - in source-code comments in each file. - - - + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.html + repo + + Note: the Google Closure library third-party extensions + contain code under a variety of licenses, which may be found + in source-code comments in each file. + + + - - Google - http://www.google.com - + + Google + http://www.google.com + - - Google, Inc. - Mohamed Mansourhello@mohamedmansour.com - Bjorn Tiplingbjorn.tipling@gmail.com - SameGoal LLChelp@samegoal.com - Guido Tapiaguido.tapia@gmail.com - Andrew Mattieamattie@gmail.com - Ilia Mirkinibmirkin@gmail.com - Ivan Kozikivan.kozik@gmail.com - Rich Doughertyrich@rd.gen.nz - + + + Google, Inc. + + + Mohamed Mansour + hello@mohamedmansour.com + + + Bjorn Tipling + bjorn.tipling@gmail.com + + + SameGoal LLC + help@samegoal.com + + + Guido Tapia + guido.tapia@gmail.com + + + Andrew Mattie + amattie@gmail.com + + + Ilia Mirkin + ibmirkin@gmail.com + + + Ivan Kozik + ivan.kozik@gmail.com + + + Rich Dougherty + rich@rd.gen.nz + + - - scm:https://github.com/google/closure-library.git - scm:git:https://github.com/google/closure-library.git - https://github.com/google/closure-library - + + scm:https://github.com/google/closure-library.git + + + scm:git:https://github.com/google/closure-library.git + + https://github.com/google/closure-library + - - code.google.com - http://code.google.com/p/closure-library/issues - + + code.google.com + http://code.google.com/p/closure-library/issues + - - - sonatype-oss-release - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.4.4 - - - default-deploy - deploy - - - deploy - - - - - - https://oss.sonatype.org/ - - sonatype-nexus-staging - - - - - - + + + sonatype-oss-release + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.4.4 + + + default-deploy + deploy + + + deploy + + + + + + https://oss.sonatype.org/ + + sonatype-nexus-staging + + + + + + diff --git a/script/closure-library-release/google-closure-library.pom.template b/script/closure-library-release/google-closure-library.pom.template index f9298508f..05f421d11 100644 --- a/script/closure-library-release/google-closure-library.pom.template +++ b/script/closure-library-release/google-closure-library.pom.template @@ -1,109 +1,140 @@ - - 4.0.0 - org.clojure - google-closure-library - RELEASE_VERSION - jar - Google Closure Library + + 4.0.0 + org.clojure + google-closure-library + RELEASE_VERSION + jar + Google Closure Library - - org.sonatype.oss - oss-parent - 9 - + + org.sonatype.oss + oss-parent + 9 + - http://code.google.com/p/closure-library/ + http://code.google.com/p/closure-library/ - - The Google Closure Library is a collection of JavaScript code - designed for use with the Google Closure JavaScript Compiler. + + The Google Closure Library is a collection of JavaScript code + designed for use with the Google Closure JavaScript Compiler. - This non-official distribution was prepared by the ClojureScript - team at http://clojure.org/ - + This non-official distribution was prepared by the ClojureScript + team at http://clojure.org/ + - - - org.clojure - google-closure-library-third-party - RELEASE_VERSION - - + + + org.clojure + google-closure-library-third-party + RELEASE_VERSION + + - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.html - repo - - + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.html + repo + + - - Google - http://www.google.com - + + Google + http://www.google.com + - - Google, Inc. - Mohamed Mansourhello@mohamedmansour.com - Bjorn Tiplingbjorn.tipling@gmail.com - SameGoal LLChelp@samegoal.com - Guido Tapiaguido.tapia@gmail.com - Andrew Mattieamattie@gmail.com - Ilia Mirkinibmirkin@gmail.com - Ivan Kozikivan.kozik@gmail.com - Rich Doughertyrich@rd.gen.nz - + + + Google, Inc. + + + Mohamed Mansour + hello@mohamedmansour.com + + + Bjorn Tipling + bjorn.tipling@gmail.com + + + SameGoal LLC + help@samegoal.com + + + Guido Tapia + guido.tapia@gmail.com + + + Andrew Mattie + amattie@gmail.com + + + Ilia Mirkin + ibmirkin@gmail.com + + + Ivan Kozik + ivan.kozik@gmail.com + + + Rich Dougherty + rich@rd.gen.nz + + - - scm:https://github.com/google/closure-library.git - scm:git:https://github.com/google/closure-library.git - https://github.com/google/closure-library - + + scm:https://github.com/google/closure-library.git + + + scm:git:https://github.com/google/closure-library.git + + https://github.com/google/closure-library + - - code.google.com - http://code.google.com/p/closure-library/issues - + + code.google.com + http://code.google.com/p/closure-library/issues + - - - sonatype-oss-release - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.4.4 - - - default-deploy - deploy - - - deploy - - - - - - https://oss.sonatype.org/ - - sonatype-nexus-staging - - - - - - + + + sonatype-oss-release + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.4.4 + + + default-deploy + deploy + + + deploy + + + + + + https://oss.sonatype.org/ + + sonatype-nexus-staging + + + + + + From 38c9379085ff264286ea20b23076e67aac18cf79 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Aug 2015 09:39:04 -0400 Subject: [PATCH 1505/4033] tweak closure-library-release.sh script so that it installs locally to Maven when not on Hudson --- .../closure-library-release.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index 582c0f124..59e1595a0 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -172,5 +172,17 @@ if [ "$HUDSON" = "true" ]; then echo "Now log in to https://oss.sonatype.org/ to release" echo "the staging repository." else - echo "Skipping deployment because we are not on Hudson." + echo "Not on Hudson, local Maven install" + ( + cd "$third_party_project_dir" + mvn clean + mvn package + mvn install:install-file -Dfile=./target/google-closure-library-third-party-$release_version.jar -DpomFile=pom.xml + ) + ( + cd "$project_dir" + mvn clean + mvn package + mvn install:install-file -Dfile=./target/google-closure-library-$release_version.jar -DpomFile=pom.xml + ) fi From fbd23a79a76390d08078e24bb41b4f9ce254a768 Mon Sep 17 00:00:00 2001 From: Maria Geller Date: Tue, 4 Aug 2015 13:53:14 -0500 Subject: [PATCH 1506/4033] CLJS-1391: Error when building for target :nodejs Fix bug when building for target nodejs. Check if source is map before trying to assign source code to :source key. --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3db68b3d3..a98b90c05 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1349,7 +1349,7 @@ (when-not (.exists out-file) (util/mkdirs out-file) (spit out-file - (cond-> (assoc js :source (deps/-source js)) + (cond-> (if (map? js) (assoc js :source (deps/-source js)) js) (:preprocess js) (js-transforms opts) (:module-type js) (convert-js-module opts) true deps/-source))) From 18d2cce8fabfc90732f7502ba3460fe168d3f2bc Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 17:46:17 -0400 Subject: [PATCH 1507/4033] CLJS-1392: cljs.repl/source regression --- src/main/clojure/cljs/analyzer.cljc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 132f03f7e..3a6ed83f8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2378,9 +2378,13 @@ (if ^boolean (:quoted? env) (analyze-list env form) (let [line (-> form meta :line) - line (when (nil? line) (:line env)) + line (if (nil? line) + (:line env) + line) col (-> form meta :column) - col (when (nil? col) (:column env)) + col (if (nil? col) + (:column env) + col) env (assoc env :line line :column col)] (let [op (first form)] (when (nil? op) From 6ef9772f7a5843c09cfb113a2f547be409f261f8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 18:01:33 -0400 Subject: [PATCH 1508/4033] bump Google Closure Library dependency --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 47a218a86..54e1400ac 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20150505-021ed5b3 + 0.0-20150805-acd8b553 org.clojure diff --git a/project.clj b/project.clj index a231703b0..17873bac1 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] - [org.clojure/google-closure-library "0.0-20150505-021ed5b3"] + [org.clojure/google-closure-library "0.0-20150805-acd8b553"] [com.google.javascript/closure-compiler "v20150729"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} diff --git a/script/bootstrap b/script/bootstrap index 4401e74b7..7173b3901 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,7 +5,7 @@ set -e CLOJURE_RELEASE="1.7.0" CLOSURE_RELEASE="20150729" DJSON_RELEASE="0.2.6" -GCLOSURE_LIB_RELEASE="0.0-20150505-021ed5b3" +GCLOSURE_LIB_RELEASE="0.0-20150805-acd8b553" RHINO_RELEASE="1_7R5" TREADER_RELEASE="0.10.0-alpha3" From ca0173c2674c553769ea6ed6d23b911f09c93fb1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 18:13:35 -0400 Subject: [PATCH 1509/4033] 1.7.48 --- README.md | 6 +++--- changes.md | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d3955c8ac..12fa6e79a 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 1.7.28 +Latest stable release: 1.7.48 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.7.28"] +[org.clojure/clojurescript "1.7.48"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.7.28 org.clojure clojurescript - 1.7.28 + 1.7.48 ``` diff --git a/changes.md b/changes.md index fc434d193..ee6f68177 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,19 @@ +## 1.7.48 + +## Enhancements +* provide goog-define macro to support proper use of goog.define +* CLJS-1177: A compiler support for non-Closure transforms (JSX, etc) +* CLJS-1296: browser REPL should queue prints before connection then flush after connection +* add :dump-core compiler option for cljs.js config +* CLJS-1386: Symbols should be added to the constants table + +## Fixes +* CLJS-1392: cljs.repl/source regression +* CLJS-1391: Error when building for target :nodejs +* CLJS-1388: Stacktrace element handling for :output-dir w/o file/line/column +* CLJS-1311: Improve error reporting when converting JavaScript modules +* CLJS-1387: support local Closure libs that conform to classpath + ## 1.7.28 ## Enhancements From 68e64cbd0cfa8d948e203509d09042167e5b4731 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 18:25:39 -0400 Subject: [PATCH 1510/4033] add revision script, fix build & uberjar scripts --- script/build | 1 + script/revision | 20 ++++++++++++++++++++ script/uberjar | 1 + 3 files changed, 22 insertions(+) create mode 100755 script/revision diff --git a/script/build b/script/build index caac8f7ba..850055ffd 100755 --- a/script/build +++ b/script/build @@ -26,6 +26,7 @@ REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # it is a little easier to understand. REVISION=${REVISION:5} # drop the first 5 characters REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters +REVISION=${REVISION/\-/} TAG=r$MAJOR.$MINOR.$REVISION diff --git a/script/revision b/script/revision new file mode 100755 index 000000000..3e3e2c2bf --- /dev/null +++ b/script/revision @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -ex + +# The command `git describe --match v0.0` will return a string like +# +# v0.0-856-g329708b +# +# where 856 is the number of commits since the v0.0 tag. It will always +# find the v0.0 tag and will always return the total number of commits (even +# if the tag is v0.0.1). +MAJOR="1" +MINOR="7" +REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` + +# Extract the version number from the string. Do this in two steps so +# it is a little easier to understand. +REVISION=${REVISION:5} # drop the first 5 characters +REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters +REVISION=${REVISION/\-/} diff --git a/script/uberjar b/script/uberjar index afb8cdd39..8b3677546 100755 --- a/script/uberjar +++ b/script/uberjar @@ -20,6 +20,7 @@ REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # it is a little easier to understand. REVISION=${REVISION:5} # drop the first 5 characters REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters +REVISION=${REVISION/\-/} COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major '"$MAJOR"', :minor '"$MINOR"', :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE From 345de6480292562964bb9656b1967b4382aba6f2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 18:30:49 -0400 Subject: [PATCH 1511/4033] missing change notes on Closure Compiler & Library deps --- changes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changes.md b/changes.md index ee6f68177..16886c385 100644 --- a/changes.md +++ b/changes.md @@ -7,6 +7,10 @@ * add :dump-core compiler option for cljs.js config * CLJS-1386: Symbols should be added to the constants table +## Changes +* Bump Closure Compiler dependency +* Bump Closure Library dependency + ## Fixes * CLJS-1392: cljs.repl/source regression * CLJS-1391: Error when building for target :nodejs From e49404647f75f2790e73268625cfd643d12d199b Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 20:26:35 -0400 Subject: [PATCH 1512/4033] CLJS-1394: reify gensyms can clash namespace reify names --- src/main/clojure/cljs/core.cljc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index c63d3aac0..a76d8c8db 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -40,10 +40,11 @@ unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? - specify copy-arguments])]) + specify copy-arguments goog-define])]) #?(:cljs (:require-macros [cljs.core :as core])) (:require clojure.walk clojure.set + [clojure.string :as string] cljs.compiler [cljs.env :as env] #?(:cljs [cljs.core :as core]) @@ -1220,7 +1221,11 @@ (meta ^{:k :v} (reify Object (toString [this] \"foo\"))) == {:k :v}" [& impls] - (core/let [t (with-meta (gensym "t") {:anonymous true}) + (core/let [t (with-meta + (gensym + (core/str "t_" + (string/replace (core/str (munge ana/*cljs-ns*)) "." "$"))) + {:anonymous true}) meta-sym (gensym "meta") this-sym (gensym "_") locals (keys (:locals &env)) From a43531a5734a129ea331327999bbd5cb1d045aa3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 20:32:29 -0400 Subject: [PATCH 1513/4033] start adding cljs.js tests --- src/test/cljs/self_host/test.cljs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test/cljs/self_host/test.cljs diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs new file mode 100644 index 000000000..2766d6a0b --- /dev/null +++ b/src/test/cljs/self_host/test.cljs @@ -0,0 +1,25 @@ +(ns self-host.test + (:require [cljs.test :as test + :refer-macros [run-tests deftest testing is async]] + [cljs.js :as cljs])) + +(defn latch [m f] + (let [r (atom 0)] + (add-watch r :latch + (fn [_ _ o n] + (when (== n m) (f)))) + r)) + +(defn inc! [r] + (swap! r inc)) + +(def st (cljs/empty-state)) + +(deftest test-compile-str + (async done + (let [l (latch 1 done)] + (cljs/compile-str st "(+ 1 1)" + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= value "(1 + 1);\n")) + (inc! l)))))) \ No newline at end of file From 836609cf5228b0ebb9d5342828f57cac599a92ee Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 20:39:07 -0400 Subject: [PATCH 1514/4033] more compile-str tests --- src/test/cljs/self_host/test.cljs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs index 2766d6a0b..9b6d36325 100644 --- a/src/test/cljs/self_host/test.cljs +++ b/src/test/cljs/self_host/test.cljs @@ -17,9 +17,21 @@ (deftest test-compile-str (async done - (let [l (latch 1 done)] + (let [l (latch 3 done)] (cljs/compile-str st "(+ 1 1)" (fn [{:keys [error value]}] (is (nil? error)) (is (= value "(1 + 1);\n")) + (inc! l))) + (cljs/compile-str st "(fn [])" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= value "(function (){\nreturn null;\n})")) + (inc! l))) + (cljs/compile-str st "(if cljs.core.first 1 2)" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= value "(cljs.core.truth_(cljs.core.first)?1:2)")) (inc! l)))))) \ No newline at end of file From 0666699b0e78bcc10f6efe050e42df9d8bbf817f Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 20:44:22 -0400 Subject: [PATCH 1515/4033] eval tests --- src/test/cljs/self_host/test.cljs | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs index 9b6d36325..954b9a98e 100644 --- a/src/test/cljs/self_host/test.cljs +++ b/src/test/cljs/self_host/test.cljs @@ -13,8 +13,34 @@ (defn inc! [r] (swap! r inc)) +(set! *target* "nodejs") + +(def vm (js/require "vm")) +(def fs (js/require "fs")) (def st (cljs/empty-state)) +(defn node-eval [{:keys [name source]}] + (.runInThisContext vm source (str (munge name) ".js"))) + +(def libs + {'bootstrap-test.core :cljs + 'bootstrap-test.macros :clj + 'bootstrap-test.helper :clj}) + +(defn node-load [{:keys [name macros]} cb] + (if (contains? libs name) + (let [path (str "src/test/cljs/" (cljs/ns->relpath name) + "." (cljs.core/name (get libs name)))] + (.readFile fs path "utf-8" + (fn [err src] + (cb (if-not err + {:lang :clj :source src} + (.error js/console err)))))) + (cb nil))) + +(defn elide-env [env ast opts] + (dissoc ast :env)) + (deftest test-compile-str (async done (let [l (latch 3 done)] @@ -34,4 +60,30 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (= value "(cljs.core.truth_(cljs.core.first)?1:2)")) + (inc! l)))))) + +(deftest test-eval-str + (async done + (let [l (latch 3 done)] + (cljs/eval-str st "(+ 1 1)" nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== value 2)) + (inc! l))) + (cljs/eval-str st "(def x 1)" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (var? value)) + (inc! l))) + (cljs/eval-str st "(fn [])" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (fn? value)) (inc! l)))))) \ No newline at end of file From c7aca4baf8e790541883952acbe2f342e0174cfc Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2015 21:03:10 -0400 Subject: [PATCH 1516/4033] make it possible to run self-host tests at command line --- script/test-self-host | 10 ++++++++++ src/test/cljs/self_host/test.cljs | 16 +++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100755 script/test-self-host diff --git a/script/test-self-host b/script/test-self-host new file mode 100755 index 000000000..dcea462e5 --- /dev/null +++ b/script/test-self-host @@ -0,0 +1,10 @@ +#!/bin/sh + +# stop blowing compiled stuff +rm -rf builds/out-self +mkdir -p builds/out-self + +bin/cljsc src/test/cljs/self_host "{:optimizations :simple :static-fns true :output-dir \"builds/out-self\" :optimize-constants true :verbose true :compiler-stats true :target :nodejs}" > builds/out-self/core-self-test.js + +echo "Testing with Node" +node builds/out-self/core-self-test.js diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs index 954b9a98e..e19c42846 100644 --- a/src/test/cljs/self_host/test.cljs +++ b/src/test/cljs/self_host/test.cljs @@ -1,7 +1,10 @@ (ns self-host.test (:require [cljs.test :as test :refer-macros [run-tests deftest testing is async]] - [cljs.js :as cljs])) + [cljs.js :as cljs] + [cljs.nodejs :as nodejs])) + +(nodejs/enable-util-print!) (defn latch [m f] (let [r (atom 0)] @@ -13,8 +16,6 @@ (defn inc! [r] (swap! r inc)) -(set! *target* "nodejs") - (def vm (js/require "vm")) (def fs (js/require "fs")) (def st (cljs/empty-state)) @@ -71,7 +72,7 @@ (is (nil? error)) (is (== value 2)) (inc! l))) - (cljs/eval-str st "(def x 1)" nil + #_(cljs/eval-str st "(def x 1)" nil {:eval node-eval :context :expr :def-emits-var true} @@ -86,4 +87,9 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (fn? value)) - (inc! l)))))) \ No newline at end of file + (inc! l)))))) + +(defn -main [& args] + (run-tests)) + +(set! *main-cli-fn* -main) From cb26ec977ddb7a81e8ce6ac74ce4d29557133592 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 5 Aug 2015 22:15:41 -0400 Subject: [PATCH 1517/4033] #"\." -> ".", fixes Windows regression --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a98b90c05..115980ba2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1199,7 +1199,7 @@ (defn lib-rel-path [{:keys [lib-path url provides] :as ijs}] (if (nil? lib-path) - (str (string/replace (first provides) #"\." File/separator) ".js") + (str (string/replace (first provides) "." File/separator) ".js") (if (.endsWith lib-path ".js") (util/get-name url) (let [path (util/path url)] From 06f11253ddae2435b6cc3ea1848bfa0dd446ccb6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 5 Aug 2015 23:31:57 -0400 Subject: [PATCH 1518/4033] when compiled need local eval --- src/test/cljs/self_host/test.cljs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs index e19c42846..07cad81a7 100644 --- a/src/test/cljs/self_host/test.cljs +++ b/src/test/cljs/self_host/test.cljs @@ -4,6 +4,8 @@ [cljs.js :as cljs] [cljs.nodejs :as nodejs])) +(set! (.-user js/cljs) #js {}) + (nodejs/enable-util-print!) (defn latch [m f] @@ -21,7 +23,9 @@ (def st (cljs/empty-state)) (defn node-eval [{:keys [name source]}] - (.runInThisContext vm source (str (munge name) ".js"))) + (if-not js/COMPILED + (.runInThisContext vm source (str (munge name) ".js")) + (js/eval source))) (def libs {'bootstrap-test.core :cljs @@ -31,7 +35,7 @@ (defn node-load [{:keys [name macros]} cb] (if (contains? libs name) (let [path (str "src/test/cljs/" (cljs/ns->relpath name) - "." (cljs.core/name (get libs name)))] + "." (cljs.core/name (get libs name)))] (.readFile fs path "utf-8" (fn [err src] (cb (if-not err @@ -72,7 +76,7 @@ (is (nil? error)) (is (== value 2)) (inc! l))) - #_(cljs/eval-str st "(def x 1)" nil + (cljs/eval-str st "(def x 1)" nil {:eval node-eval :context :expr :def-emits-var true} From 86ccaa7608f57cb5830dabed100c5602448c1ead Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 6 Aug 2015 08:10:08 -0400 Subject: [PATCH 1519/4033] test self-host higher order invoke --- src/test/cljs/self_host/test.cljs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs index 07cad81a7..fe06822a1 100644 --- a/src/test/cljs/self_host/test.cljs +++ b/src/test/cljs/self_host/test.cljs @@ -69,7 +69,7 @@ (deftest test-eval-str (async done - (let [l (latch 3 done)] + (let [l (latch 4 done)] (cljs/eval-str st "(+ 1 1)" nil {:eval node-eval} (fn [{:keys [error value]}] @@ -91,6 +91,14 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (fn? value)) + (inc! l))) + (cljs/eval-str st "((fn [a b] (+ a b)) 1 2)" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== 3)) (inc! l)))))) (defn -main [& args] From 7d9dbc86d0e78efe172048b720fb49bc77563380 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 6 Aug 2015 08:26:07 -0400 Subject: [PATCH 1520/4033] more eval-str tests --- src/test/cljs/self_host/test.cljs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs index fe06822a1..0ab58b957 100644 --- a/src/test/cljs/self_host/test.cljs +++ b/src/test/cljs/self_host/test.cljs @@ -69,7 +69,7 @@ (deftest test-eval-str (async done - (let [l (latch 4 done)] + (let [l (latch 6 done)] (cljs/eval-str st "(+ 1 1)" nil {:eval node-eval} (fn [{:keys [error value]}] @@ -99,9 +99,28 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (== 3)) + (inc! l))) + (cljs/eval-str st "(ns foo.bar)" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (not (nil? js/foo.bar))) + (inc! l))) + (cljs/eval-str st "(defn foo [a b] (+ a b))" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== (js/cljs.user.foo 1 2) 3)) (inc! l)))))) (defn -main [& args] (run-tests)) (set! *main-cli-fn* -main) + +(comment + ) \ No newline at end of file From e5b7234314afc561776f9399a880aae846a88ff1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 6 Aug 2015 09:39:44 -0400 Subject: [PATCH 1521/4033] add more self-host tests, note about binding issue --- src/test/cljs/self_host/test.cljs | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs index 0ab58b957..6a6d81860 100644 --- a/src/test/cljs/self_host/test.cljs +++ b/src/test/cljs/self_host/test.cljs @@ -46,6 +46,19 @@ (defn elide-env [env ast opts] (dissoc ast :env)) +;; NOTE: can't set passes because callbacks happen _inside_ binding +;; do so will effect other tests + +(deftest test-analyze-str + (async done + (let [l (latch 1 done)] + (cljs/analyze-str st "(+ 1 1)" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= :js (:op value))) + (inc! l)))))) + (deftest test-compile-str (async done (let [l (latch 3 done)] @@ -117,6 +130,37 @@ (is (== (js/cljs.user.foo 1 2) 3)) (inc! l)))))) +(deftest test-eval-str-with-require + (async done + (let [l (latch 2 done)] + (cljs/eval-str st + "(ns foo.bar (:require [bootstrap-test.core]))\n(bootstrap-test.core/foo 3 4)" + nil + {:eval node-eval + :load node-load} + (fn [{:keys [value error]}] + (is (nil? error)) + (is (== 7 value)) + (inc! l))) + #_(cljs/eval-str st + "(ns foo.bar (:require-macros [bootstrap-test.macros :refer [foo]]))\n(foo 4 4)" + nil + {:eval node-eval + :load node-load} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== 16 value)) + (inc! l))) + (cljs/eval-str st + "(ns foo.bar)\n(first [1 2 3])" + nil + {:eval node-eval + :load node-load} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== 1 value)) + (inc! l)))))) + (defn -main [& args] (run-tests)) From 139da195125110646f8d5550fc753fdbf63f565c Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 6 Aug 2015 15:38:06 -0400 Subject: [PATCH 1522/4033] CLJS-1396: Allow starting Node.js with debug port set --- script/repl.clj | 2 +- src/main/clojure/cljs/repl/node.clj | 43 +++++++++++++++-------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/script/repl.clj b/script/repl.clj index 72de2584e..13f93d348 100644 --- a/script/repl.clj +++ b/script/repl.clj @@ -1,3 +1,3 @@ (require '[cljs.repl :as repl]) (require '[cljs.repl.node :as node]) -(repl/repl (node/repl-env)) \ No newline at end of file +(repl/repl (node/repl-env :debug-port 5002)) \ No newline at end of file diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index f0e82f0c6..3d0eb860b 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -99,26 +99,28 @@ (defn setup ([repl-env] (setup repl-env nil)) ([repl-env opts] - (let [output-dir (io/file (util/output-directory opts)) - _ (.mkdirs output-dir) - of (io/file output-dir "node_repl.js") - _ (spit of - (string/replace (slurp (io/resource "cljs/repl/node_repl.js")) - "var PORT = 5001;" - (str "var PORT = " (:port repl-env) ";"))) - proc (-> (ProcessBuilder. (into-array [(get opts :node-command "node")])) - (.redirectInput of) - .start) - _ (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) *out*)))) - (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) *err*))))) - env (ana/empty-env) - core (io/resource "cljs/core.cljs") + (let [output-dir (io/file (util/output-directory opts)) + _ (.mkdirs output-dir) + of (io/file output-dir "node_repl.js") + _ (spit of + (string/replace (slurp (io/resource "cljs/repl/node_repl.js")) + "var PORT = 5001;" + (str "var PORT = " (:port repl-env) ";"))) + xs (cond-> [(get opts :node-command "node")] + (:debug-port repl-env) (conj (str "--debug=" (:debug-port repl-env)))) + proc (-> (ProcessBuilder. (into-array xs)) + (.redirectInput of) + .start) + _ (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) *out*)))) + (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) *err*))))) + env (ana/empty-env) + core (io/resource "cljs/core.cljs") ;; represent paths as vectors so we can emit JS arrays, this is to ;; paper over Windows issues with minimum hassle - David - path (.getPath (.getCanonicalFile output-dir)) - [fc & cs] (rest (util/path-seq path)) ;; remove leading empty string - root (.substring path 0 (+ (.indexOf path fc) (count fc))) - root-path (vec (cons root cs)) + path (.getPath (.getCanonicalFile output-dir)) + [fc & cs] (rest (util/path-seq path)) ;; remove leading empty string + root (.substring path 0 (+ (.indexOf path fc) (count fc))) + root-path (vec (cons root cs)) rewrite-path (conj root-path "goog")] (reset! (:proc repl-env) proc) (loop [r nil] @@ -206,12 +208,13 @@ (close-socket @socket))) (defn repl-env* [options] - (let [{:keys [host port]} + (let [{:keys [host port debug-port]} (merge {:host "localhost" :port (+ 49000 (rand-int 10000))} options)] - (NodeEnv. host port (atom nil) (atom nil)))) + (assoc (NodeEnv. host port (atom nil) (atom nil)) + :debug-port debug-port))) (defn repl-env "Construct a Node.js evalution environment. Can supply :host and :port." From 923e1d9aa139b299bfe91522009e5fdc67ec0e9f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 6 Aug 2015 19:03:04 -0400 Subject: [PATCH 1523/4033] move self host tests out from normal tests so normal tests run again --- script/test-self-host | 2 +- .../{cljs => self}/bootstrap_test/core.cljs | 0 .../{cljs => self}/bootstrap_test/helper.clj | 0 .../{cljs => self}/bootstrap_test/macros.clj | 0 src/test/self/self_host/test.cljs | 170 ++++++++++++++++++ 5 files changed, 171 insertions(+), 1 deletion(-) rename src/test/{cljs => self}/bootstrap_test/core.cljs (100%) rename src/test/{cljs => self}/bootstrap_test/helper.clj (100%) rename src/test/{cljs => self}/bootstrap_test/macros.clj (100%) create mode 100644 src/test/self/self_host/test.cljs diff --git a/script/test-self-host b/script/test-self-host index dcea462e5..45b32d044 100755 --- a/script/test-self-host +++ b/script/test-self-host @@ -4,7 +4,7 @@ rm -rf builds/out-self mkdir -p builds/out-self -bin/cljsc src/test/cljs/self_host "{:optimizations :simple :static-fns true :output-dir \"builds/out-self\" :optimize-constants true :verbose true :compiler-stats true :target :nodejs}" > builds/out-self/core-self-test.js +bin/cljsc src/test/self/self_host "{:optimizations :simple :static-fns true :output-dir \"builds/out-self\" :optimize-constants true :verbose true :compiler-stats true :target :nodejs}" > builds/out-self/core-self-test.js echo "Testing with Node" node builds/out-self/core-self-test.js diff --git a/src/test/cljs/bootstrap_test/core.cljs b/src/test/self/bootstrap_test/core.cljs similarity index 100% rename from src/test/cljs/bootstrap_test/core.cljs rename to src/test/self/bootstrap_test/core.cljs diff --git a/src/test/cljs/bootstrap_test/helper.clj b/src/test/self/bootstrap_test/helper.clj similarity index 100% rename from src/test/cljs/bootstrap_test/helper.clj rename to src/test/self/bootstrap_test/helper.clj diff --git a/src/test/cljs/bootstrap_test/macros.clj b/src/test/self/bootstrap_test/macros.clj similarity index 100% rename from src/test/cljs/bootstrap_test/macros.clj rename to src/test/self/bootstrap_test/macros.clj diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs new file mode 100644 index 000000000..54a1a8488 --- /dev/null +++ b/src/test/self/self_host/test.cljs @@ -0,0 +1,170 @@ +(ns self-host.test + (:require [cljs.test :as test + :refer-macros [run-tests deftest testing is async]] + [cljs.js :as cljs] + [cljs.nodejs :as nodejs])) + +(set! (.-user js/cljs) #js {}) + +(nodejs/enable-util-print!) + +(defn latch [m f] + (let [r (atom 0)] + (add-watch r :latch + (fn [_ _ o n] + (when (== n m) (f)))) + r)) + +(defn inc! [r] + (swap! r inc)) + +(def vm (js/require "vm")) +(def fs (js/require "fs")) +(def st (cljs/empty-state)) + +(defn node-eval [{:keys [name source]}] + (if-not js/COMPILED + (.runInThisContext vm source (str (munge name) ".js")) + (js/eval source))) + +(def libs + {'bootstrap-test.core :cljs + 'bootstrap-test.macros :clj + 'bootstrap-test.helper :clj}) + +(defn node-load [{:keys [name macros]} cb] + (if (contains? libs name) + (let [path (str "src/test/self/" (cljs/ns->relpath name) + "." (cljs.core/name (get libs name)))] + (.readFile fs path "utf-8" + (fn [err src] + (cb (if-not err + {:lang :clj :source src} + (.error js/console err)))))) + (cb nil))) + +(defn elide-env [env ast opts] + (dissoc ast :env)) + +;; NOTE: can't set passes because callbacks happen _inside_ binding +;; do so will effect other tests + +(deftest test-analyze-str + (async done + (let [l (latch 1 done)] + (cljs/analyze-str st "(+ 1 1)" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= :js (:op value))) + (inc! l)))))) + +(deftest test-compile-str + (async done + (let [l (latch 3 done)] + (cljs/compile-str st "(+ 1 1)" + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= value "(1 + 1);\n")) + (inc! l))) + (cljs/compile-str st "(fn [])" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= value "(function (){\nreturn null;\n})")) + (inc! l))) + (cljs/compile-str st "(if cljs.core.first 1 2)" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= value "(cljs.core.truth_(cljs.core.first)?1:2)")) + (inc! l)))))) + +(deftest test-eval-str + (async done + (let [l (latch 5 done)] + (cljs/eval-str st "(+ 1 1)" nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== value 2)) + (inc! l))) + (cljs/eval-str st "(def x 1)" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (var? value)) + (inc! l))) + (cljs/eval-str st "(fn [])" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (fn? value)) + (inc! l))) + (cljs/eval-str st "((fn [a b] (+ a b)) 1 2)" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== 3)) + (inc! l))) + #_(cljs/eval-str st "(ns foo.bar)" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (not (nil? js/foo.bar))) + (inc! l))) + (cljs/eval-str st "(defn foo [a b] (+ a b))" nil + {:eval node-eval + :context :expr + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== (js/cljs.user.foo 1 2) 3)) + (inc! l)))))) + +#_(deftest test-eval-str-with-require + (async done + (let [l (latch 3 done)] + (cljs/eval-str st + "(ns foo.bar (:require [bootstrap-test.core]))\n(bootstrap-test.core/foo 3 4)" + nil + {:eval node-eval + :load node-load} + (fn [{:keys [value error]}] + (is (nil? error)) + (is (== 7 value)) + (inc! l))) + (cljs/eval-str st + "(ns foo.bar (:require-macros [bootstrap-test.macros :refer [foo]]))\n(foo 4 4)" + nil + {:eval node-eval + :load node-load} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== 16 value)) + (inc! l))) + (cljs/eval-str st + "(ns foo.bar)\n(first [1 2 3])" + nil + {:eval node-eval + :load node-load} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== 1 value)) + (inc! l)))))) + +(defn -main [& args] + (run-tests)) + +(set! *main-cli-fn* -main) + +(comment + ) \ No newline at end of file From 501bff0627a9ba5f75f19c1e6473abc945dbc579 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 6 Aug 2015 19:06:24 -0400 Subject: [PATCH 1524/4033] exclude .idea & builds --- Clojurescript.iml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Clojurescript.iml b/Clojurescript.iml index 7d2352c03..72a4f9995 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -15,7 +15,9 @@ + + From 1fa4db4221b7418f4bc84a4cd09cb93750008b1b Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 6 Aug 2015 19:12:12 -0400 Subject: [PATCH 1525/4033] remove self host tests from main tests --- src/test/cljs/self_host/test.cljs | 170 ------------------------------ 1 file changed, 170 deletions(-) delete mode 100644 src/test/cljs/self_host/test.cljs diff --git a/src/test/cljs/self_host/test.cljs b/src/test/cljs/self_host/test.cljs deleted file mode 100644 index 6a6d81860..000000000 --- a/src/test/cljs/self_host/test.cljs +++ /dev/null @@ -1,170 +0,0 @@ -(ns self-host.test - (:require [cljs.test :as test - :refer-macros [run-tests deftest testing is async]] - [cljs.js :as cljs] - [cljs.nodejs :as nodejs])) - -(set! (.-user js/cljs) #js {}) - -(nodejs/enable-util-print!) - -(defn latch [m f] - (let [r (atom 0)] - (add-watch r :latch - (fn [_ _ o n] - (when (== n m) (f)))) - r)) - -(defn inc! [r] - (swap! r inc)) - -(def vm (js/require "vm")) -(def fs (js/require "fs")) -(def st (cljs/empty-state)) - -(defn node-eval [{:keys [name source]}] - (if-not js/COMPILED - (.runInThisContext vm source (str (munge name) ".js")) - (js/eval source))) - -(def libs - {'bootstrap-test.core :cljs - 'bootstrap-test.macros :clj - 'bootstrap-test.helper :clj}) - -(defn node-load [{:keys [name macros]} cb] - (if (contains? libs name) - (let [path (str "src/test/cljs/" (cljs/ns->relpath name) - "." (cljs.core/name (get libs name)))] - (.readFile fs path "utf-8" - (fn [err src] - (cb (if-not err - {:lang :clj :source src} - (.error js/console err)))))) - (cb nil))) - -(defn elide-env [env ast opts] - (dissoc ast :env)) - -;; NOTE: can't set passes because callbacks happen _inside_ binding -;; do so will effect other tests - -(deftest test-analyze-str - (async done - (let [l (latch 1 done)] - (cljs/analyze-str st "(+ 1 1)" nil - {:context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= :js (:op value))) - (inc! l)))))) - -(deftest test-compile-str - (async done - (let [l (latch 3 done)] - (cljs/compile-str st "(+ 1 1)" - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= value "(1 + 1);\n")) - (inc! l))) - (cljs/compile-str st "(fn [])" nil - {:context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= value "(function (){\nreturn null;\n})")) - (inc! l))) - (cljs/compile-str st "(if cljs.core.first 1 2)" nil - {:context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= value "(cljs.core.truth_(cljs.core.first)?1:2)")) - (inc! l)))))) - -(deftest test-eval-str - (async done - (let [l (latch 6 done)] - (cljs/eval-str st "(+ 1 1)" nil - {:eval node-eval} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (== value 2)) - (inc! l))) - (cljs/eval-str st "(def x 1)" nil - {:eval node-eval - :context :expr - :def-emits-var true} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (var? value)) - (inc! l))) - (cljs/eval-str st "(fn [])" nil - {:eval node-eval - :context :expr - :def-emits-var true} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (fn? value)) - (inc! l))) - (cljs/eval-str st "((fn [a b] (+ a b)) 1 2)" nil - {:eval node-eval - :context :expr - :def-emits-var true} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (== 3)) - (inc! l))) - (cljs/eval-str st "(ns foo.bar)" nil - {:eval node-eval - :context :expr - :def-emits-var true} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (not (nil? js/foo.bar))) - (inc! l))) - (cljs/eval-str st "(defn foo [a b] (+ a b))" nil - {:eval node-eval - :context :expr - :def-emits-var true} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (== (js/cljs.user.foo 1 2) 3)) - (inc! l)))))) - -(deftest test-eval-str-with-require - (async done - (let [l (latch 2 done)] - (cljs/eval-str st - "(ns foo.bar (:require [bootstrap-test.core]))\n(bootstrap-test.core/foo 3 4)" - nil - {:eval node-eval - :load node-load} - (fn [{:keys [value error]}] - (is (nil? error)) - (is (== 7 value)) - (inc! l))) - #_(cljs/eval-str st - "(ns foo.bar (:require-macros [bootstrap-test.macros :refer [foo]]))\n(foo 4 4)" - nil - {:eval node-eval - :load node-load} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (== 16 value)) - (inc! l))) - (cljs/eval-str st - "(ns foo.bar)\n(first [1 2 3])" - nil - {:eval node-eval - :load node-load} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (== 1 value)) - (inc! l)))))) - -(defn -main [& args] - (run-tests)) - -(set! *main-cli-fn* -main) - -(comment - ) \ No newline at end of file From ef5eafb584358cf794eb0ba6bd480f3dbc37ccd5 Mon Sep 17 00:00:00 2001 From: Martin Klepsch Date: Thu, 6 Aug 2015 21:32:07 +0200 Subject: [PATCH 1526/4033] CLJS-1395: no trailing semicolons after JS comments --- src/main/clojure/cljs/compiler.cljc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index ffe93408e..f49dbcaec 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1013,12 +1013,15 @@ ")")))) (defmethod emit* :js - [{:keys [env code segs args]}] - (emit-wrap env - (if code - (emits code) - (emits (interleave (concat segs (repeat nil)) - (concat args [nil])))))) + [{:keys [op env code segs args]}] + (if (and code (= op :js) #?(:clj (.startsWith ^String code "/*") + :cljs (gstring/startsWith code "/*"))) + (emits code) + (emit-wrap env + (if code + (emits code) + (emits (interleave (concat segs (repeat nil)) + (concat args [nil]))))))) ;; TODO: unify renaming helpers - this one was hard to find - David From c99f65f28aa46f28167815bcef2244ef473c4844 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 6 Aug 2015 19:16:10 -0400 Subject: [PATCH 1527/4033] remove redundant test --- src/main/clojure/cljs/compiler.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index f49dbcaec..8249f9b17 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1014,8 +1014,8 @@ (defmethod emit* :js [{:keys [op env code segs args]}] - (if (and code (= op :js) #?(:clj (.startsWith ^String code "/*") - :cljs (gstring/startsWith code "/*"))) + (if (and code #?(:clj (.startsWith ^String code "/*") + :cljs (gstring/startsWith code "/*"))) (emits code) (emit-wrap env (if code From 8cbd49523298dbc98e14f68e5fe91fc3fed70e6c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 7 Aug 2015 11:56:41 -0400 Subject: [PATCH 1528/4033] add js-comment for emitting top-level JSDoc style comments --- src/main/clojure/cljs/core.cljc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a76d8c8db..d8e325902 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -865,6 +865,20 @@ [] (core/list 'js* "debugger;")) +(core/defmacro js-comment + "Emit top-level JavaScript multi-line comment. New lines will create a + new comment line." + [comment] + (let [[x & ys] (string/split comment #"\n")] + (core/list 'js* + (core/str + "\n/**\n" + (core/str " * " x "\n") + (core/->> ys + (map #(core/str " * " (subs % 3) "\n")) + (reduce core/str "")) + " */\n")))) + (core/defmacro true? [x] (bool-expr (core/list 'js* "~{} === true" x))) From ed4ba59dcf0604403754902fbb998bc119e37e63 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 7 Aug 2015 14:04:53 -0400 Subject: [PATCH 1529/4033] change comment emission to preserve whitespace formatting module 3 spaces of leading whitespace (to account for traditional Lisp indent) tweak js-comment accordingly --- src/main/clojure/cljs/compiler.cljc | 10 +++++++--- src/main/clojure/cljs/core.cljc | 14 +++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 8249f9b17..eb2c4d325 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -481,9 +481,13 @@ docs (if jsdoc (concat docs jsdoc) docs) docs (remove nil? docs)] (letfn [(print-comment-lines [e] - (doseq [next-line (string/split-lines e)] - (emitln " * " (-> (string/trim next-line) - (string/replace "*/" "* /")))))] + (let [[x & ys] (string/split-lines e)] + (emitln " * " x) + (doseq [next-line ys] + (emitln " * " + (-> next-line + (string/replace #"^ " "") + (string/replace "*/" "* /"))))))] (when (seq docs) (emitln "/**") (doseq [e docs] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index d8e325902..6261351ac 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -871,13 +871,13 @@ [comment] (let [[x & ys] (string/split comment #"\n")] (core/list 'js* - (core/str - "\n/**\n" - (core/str " * " x "\n") - (core/->> ys - (map #(core/str " * " (subs % 3) "\n")) - (reduce core/str "")) - " */\n")))) + (core/str + "\n/**\n" + (core/str " * " x "\n") + (core/->> ys + (map #(core/str " * " (string/replace % #"^ " "") "\n")) + (reduce core/str "")) + " */\n")))) (core/defmacro true? [x] (bool-expr (core/list 'js* "~{} === true" x))) From bc1e1ab7b44636f8b034a2cfae808faf449488ca Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Aug 2015 14:52:52 -0400 Subject: [PATCH 1530/4033] fix JSDoc typo --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a86a7da28..8db74e735 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -271,7 +271,7 @@ (def ITER_SYMBOL (.-iterator js/Symbol)) (def ITER_SYMBOL "@@iterator")) -(def ^{:jsdoc ["@enum {string"]} +(def ^{:jsdoc ["@enum {string}"]} CHAR_MAP #js {"-" "_" ":" "_COLON_" From 692bd221673e3da0c329c14d985174ec0668f25b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Aug 2015 15:05:31 -0400 Subject: [PATCH 1531/4033] ES6 collection now in official Google externs --- src/main/cljs/cljs/externs.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/main/cljs/cljs/externs.js b/src/main/cljs/cljs/externs.js index f1ea8fa4d..feffe1062 100644 --- a/src/main/cljs/cljs/externs.js +++ b/src/main/cljs/cljs/externs.js @@ -21,27 +21,6 @@ IteratorStep.prototype.done; */ IteratorStep.prototype.value; -/** - * @constructor; - */ -function Map() {}; -Map.prototype.keys = function() {}; -Map.prototype.entries = function() {}; -Map.prototype.values = function() {}; -Map.prototype.has = function(k) {}; -Map.prototype.get = function(k) {}; -Map.prototype.forEach = function(f) {}; - -/** - * @constructor; - */ -function Set() {}; -Set.prototype.keys = function() {}; -Set.prototype.entries = function() {}; -Set.prototype.values = function() {}; -Set.prototype.has = function(k) {}; -Set.prototype.forEach = function(f) {}; - /** * @constructor; */ From 202d499b17e930c2322af1cfa031e8f1962777d7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Aug 2015 16:15:30 -0400 Subject: [PATCH 1532/4033] fix cljs-428 regression --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index eb2c4d325..b87441c9e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -482,7 +482,7 @@ docs (remove nil? docs)] (letfn [(print-comment-lines [e] (let [[x & ys] (string/split-lines e)] - (emitln " * " x) + (emitln " * " (string/replace x "*/" "* /")) (doseq [next-line ys] (emitln " * " (-> next-line From 42dc1f58bc71a0059490064ff85096954546e7e1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Aug 2015 16:15:51 -0400 Subject: [PATCH 1533/4033] remove other es6 externs now included --- src/main/cljs/cljs/externs.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/main/cljs/cljs/externs.js b/src/main/cljs/cljs/externs.js index feffe1062..6b34cec7f 100644 --- a/src/main/cljs/cljs/externs.js +++ b/src/main/cljs/cljs/externs.js @@ -1,26 +1,5 @@ Math.imul = function(a, b) {}; -var cljs = {}; -cljs.core = {}; -/** - * @constructor; - */ -cljs.core.Iterator = function() {}; -cljs.core.Iterator.prototype.next = function() {}; - -/** - * @constructor; - */ -function IteratorStep() {}; -/** - * @type {boolean} - */ -IteratorStep.prototype.done; -/** - * @type {Object} - */ -IteratorStep.prototype.value; - /** * @constructor; */ From d3f1e553f17b60e979c56ee37015ee001ae2cdd1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Aug 2015 16:23:39 -0400 Subject: [PATCH 1534/4033] give Google Closure enough information so that type checking can work sensibly without extra noise --- src/main/cljs/cljs/core.cljs | 39 ++++++++++++++++++++++++--------- src/main/cljs/cljs/externs.js | 4 ++++ src/main/clojure/cljs/core.cljc | 16 ++++++++------ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8db74e735..4aaf8f969 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -28,11 +28,13 @@ (def ^{:dynamic true - :doc "Var bound to the current namespace. Only used for bootstrapping."} + :doc "Var bound to the current namespace. Only used for bootstrapping." + :jsdoc ["@type {*}"]} *ns* nil) (def - ^{:dynamic true} + ^{:dynamic true + :jsdoc ["@type {*}"]} *out* nil) (def @@ -106,7 +108,8 @@ :doc "When set to logical true, objects will be printed in a way that preserves their type when read in later. - Defaults to false."} + Defaults to false." + :jsdoc ["@type {null|number}"]} *print-length* nil) (def @@ -118,10 +121,14 @@ collection, its items are at level 1; and so on. If an object is a collection and is at a level greater than or equal to the value bound to *print-level*, the printer prints '#' to represent it. The root binding - is nil indicating no limit."} + is nil indicating no limit." + :jsdoc ["@type {null|number}"]} *print-level* nil) -(defonce ^:dynamic *loaded-libs* nil) +(defonce + ^{:dynamic true + :jsdoc ["@type {*}"]} + *loaded-libs* nil) (defn- pr-opts [] {:flush-on-newline *flush-on-newline* @@ -9143,7 +9150,9 @@ reduces them without incurring seq initialization" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gensym ;;;;;;;;;;;;;;;; ;; Internal - do not use! -(def gensym_counter nil) +(def + ^{:jsdoc ["@type {*}"]} + gensym_counter nil) (defn gensym "Returns a new symbol with a unique name. If a prefix string is @@ -9403,7 +9412,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." "Creates a hierarchy object for use with derive, isa? etc." [] {:parents {} :descendants {} :ancestors {}}) -(def ^:private -global-hierarchy nil) +(def + ^{:private true + :jsdoc ["@type {*}"]} + -global-hierarchy nil) (defn- get-global-hierarchy [] (when (nil? -global-hierarchy) @@ -9984,7 +9996,10 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." {:pre [(symbol? tag)]} (TaggedLiteral. tag form)) -(def ^:private js-reserved-arr +(def + ^{:private true + :jsdoc ["@type {*}"]} + js-reserved-arr #js ["abstract" "boolean" "break" "byte" "case" "catch" "char" "class" "const" "continue" "debugger" "default" "delete" "do" "double" @@ -9999,7 +10014,9 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." "volatile" "while" "with" "yield" "methods" "null"]) -(def js-reserved nil) +(def + ^{:jsdoc ["@type {*}"]} + js-reserved nil) (defn- js-reserved? [x] (when (nil? js-reserved) @@ -10093,7 +10110,9 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-hash [_] (hash name))) -(def NS_CACHE nil) +(def + ^{:jsdoc ["@type {*}"]} + NS_CACHE nil) (defn- find-ns-obj* [ctxt xs] (cond diff --git a/src/main/cljs/cljs/externs.js b/src/main/cljs/cljs/externs.js index 6b34cec7f..b996e1b57 100644 --- a/src/main/cljs/cljs/externs.js +++ b/src/main/cljs/cljs/externs.js @@ -1,5 +1,9 @@ Math.imul = function(a, b) {}; +Object.prototype.done; +Object.prototype.value; +Object.prototype.next = function() {}; + /** * @constructor; */ diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 6261351ac..219497c02 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2683,7 +2683,7 @@ :arglists-meta (doall (map meta [arglist]))})] `(do (def ~(with-meta name meta) - (fn [] + (fn [~'var_args] (let [args# (array)] (copy-arguments args#) (let [argseq# (when (< ~c-1 (alength args#)) @@ -2738,7 +2738,7 @@ args-sym (gensym "args")] `(do (def ~(with-meta name meta) - (fn [] + (fn [~'var_args] (let [~args-sym (array)] (copy-arguments ~args-sym) (case (alength ~args-sym) @@ -2821,16 +2821,18 @@ m (conj (if (meta name) (meta name) {}) m)] (core/cond (multi-arity-fn? fdecl) - (multi-arity-fn name m fdecl) + (multi-arity-fn name + (update-in m [:jsdoc] conj "@param {...*} var_args") fdecl) (variadic-fn? fdecl) - (variadic-fn name m fdecl) + (variadic-fn name + (update-in m [:jsdoc] conj "@param {...*} var_args") fdecl) :else (core/list 'def (with-meta name m) - ;;todo - restore propagation of fn name - ;;must figure out how to convey primitive hints to self calls first - (cons `fn fdecl)))))) + ;;todo - restore propagation of fn name + ;;must figure out how to convey primitive hints to self calls first + (cons `fn fdecl)))))) #?(:clj (. (var defn) (setMacro)) :cljs (set! (. defn -cljs$lang$macro) true)) From e6ecde0c65ce99126d32626310038e663997a0aa Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Aug 2015 18:13:45 -0400 Subject: [PATCH 1535/4033] fix js-comment for self-hosted ClojureScript --- src/main/clojure/cljs/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 219497c02..156ef14f6 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -40,7 +40,7 @@ unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? - specify copy-arguments goog-define])]) + specify copy-arguments goog-define js-comment])]) #?(:cljs (:require-macros [cljs.core :as core])) (:require clojure.walk clojure.set @@ -869,7 +869,7 @@ "Emit top-level JavaScript multi-line comment. New lines will create a new comment line." [comment] - (let [[x & ys] (string/split comment #"\n")] + (core/let [[x & ys] (string/split comment #"\n")] (core/list 'js* (core/str "\n/**\n" From 771f7cd7c80e06f1102bab756972e12abf90fd3c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 09:40:04 -0400 Subject: [PATCH 1536/4033] remove bad param emission from defrecord --- src/main/clojure/cljs/compiler.cljc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b87441c9e..7daedae31 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -993,11 +993,6 @@ (emitln "") (emitln "/**") (emitln "* @constructor") - (doseq [fld fields] - (emitln "* @param {*} " fld)) - (emitln "* @param {*=} __meta ") - (emitln "* @param {*=} __extmap") - (emitln "* @param {number|null} __hash") (emitln "*/") (emitln (munge t) " = (function (" (comma-sep fields) "){") (doseq [fld fields] From e351c98538ccfd98871d071a4389e9e20dfba1b6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 11:06:09 -0400 Subject: [PATCH 1537/4033] CLJS-1401: Unify runtime & compile time UUID hash calculation --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/compiler.cljc | 3 ++- src/test/cljs/cljs/core_test.cljs | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4aaf8f969..d40faceaf 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9834,7 +9834,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." IHash (-hash [this] (when (nil? __hash) - (set! __hash (goog.string/hashCode uuid))) + (set! __hash (hash-string* uuid))) __hash) IComparable diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 7daedae31..9faafa789 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -286,7 +286,8 @@ (emits "new Date(" (.getTime date) ")")) (defmethod emit-constant #?(:clj java.util.UUID :cljs UUID) [^java.util.UUID uuid] - (emits "new cljs.core.UUID(\"" (.toString uuid) "\")")) + (let [uuid-str (.toString uuid)] + (emits "new cljs.core.UUID(\"" uuid-str "\", " (hash uuid-str) ")"))) #?(:clj (defmacro emit-wrap [env & body] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 909c11461..fed54edb4 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2922,6 +2922,10 @@ (is (= 'cljs.core/first? (demunge (munge 'cljs.core/first?))))) +(deftest test-uuid-compile-and-runtime-hash + (is (= (hash (.toString #uuid "0d1f9029-40fc-4728-8bdd-9862172d4370")) + (hash (.toString (UUID. "0d1f9029-40fc-4728-8bdd-9862172d4370" nil)))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 8ff447cd9272cbfa4aa3cf5b2b42fa579c1d18ae Mon Sep 17 00:00:00 2001 From: Martin Klepsch Date: Fri, 7 Aug 2015 12:46:25 +0200 Subject: [PATCH 1538/4033] CLJS-1393: turn *target* into goog-define --- src/main/cljs/cljs/core.cljs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d40faceaf..a70b87a39 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -17,13 +17,12 @@ (def *unchecked-if* false) -(def +(goog-define ^{:dynamic true :doc "Var bound to the name value of the compiler build :target option. For example, if the compiler build :target is :nodejs, *target* will be bound to \"nodejs\". *target* is a Google Closure define and can be set by compiler - :closure-defines option." - :jsdoc ["@define {string}"]} + :closure-defines option."} *target* "default") (def From d2fdb5c01a7ad9eb1eaea9d0ff7de827434aeb18 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 11:22:32 -0400 Subject: [PATCH 1539/4033] fix bad code gen picked up by Closure type checking --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 9faafa789..709e7d18e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -349,7 +349,7 @@ (emits "new cljs.core.PersistentArrayMap(null, " (count keys) ", [" (comma-sep (interleave keys vals)) "], null)") - (emits "new cljs.core.PersistentArrayMap.fromArray([" + (emits "cljs.core.PersistentArrayMap.fromArray([" (comma-sep (interleave keys vals)) "], true, false)")) From d06a4f4273a93bd73876867c93e3018069f5836f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 11:40:00 -0400 Subject: [PATCH 1540/4033] clean up js/parseInt invokes --- src/main/cljs/cljs/pprint.cljs | 4 ++-- src/main/cljs/cljs/test.cljs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 53ec4badb..6f0c2e2d6 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -1418,7 +1418,7 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm" e (if (and (pos? (count e)) (= (nth e 0) \+)) (subs e 1) e)] (if (empty? m2) ["0" 0] - [m2 (- (js/parseInt e) delta)]))) + [m2 (- (js/parseInt e 10) delta)]))) (defn- inc-s "Assumption: The input string consists of one or more decimal digits, @@ -2475,7 +2475,7 @@ not a pretty writer (which keeps track of columns), this function always outputs (and (= (.-length p) 1) (contains? #{\v \V} (nth p 0))) :parameter-from-args (and (= (.-length p) 1) (= \# (nth p 0))) :remaining-arg-count (and (= (.-length p) 2) (= \' (nth p 0))) (nth p 1) - true (js/parseInt p)) + true (js/parseInt p 10)) offset]) (def ^{:private true} diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index f41ee67a7..73926a65c 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -366,8 +366,8 @@ (defn js-line-and-column [stack-element] (let [parts (.split stack-element ":") cnt (count parts)] - [(js/parseInt (nth parts (- cnt 2))) - (js/parseInt (nth parts (dec cnt)))])) + [(js/parseInt (nth parts (- cnt 2)) 10) + (js/parseInt (nth parts (dec cnt)) 10)])) (defn js-filename [stack-element] (first (.split (last (.split stack-element "/out/")) ":"))) From a741cd4a3d0683f9dcfd08822343abcfa477d951 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 13:28:39 -0400 Subject: [PATCH 1541/4033] fix js-reserved type --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a70b87a39..d35a57d1c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10014,7 +10014,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." "null"]) (def - ^{:jsdoc ["@type {*}"]} + ^{:jsdoc ["@type {null|Object}"]} js-reserved nil) (defn- js-reserved? [x] From 0ca217d1f30adfb9504abc8a3e0ba2fe35c8e548 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 14:11:15 -0400 Subject: [PATCH 1542/4033] munge @param in emitted comment so Closure type checking works with ClojureScript idiomatic names --- src/main/clojure/cljs/compiler.cljc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 709e7d18e..fb9d0fe0a 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -475,6 +475,17 @@ (emits "(function(){throw " throw "})()") (emitln "throw " throw ";"))) +(defn munge-param [line] + (if (re-find #"@param" line) + (let [[p t n & xs] (map string/trim + (string/split (string/trim line) #" "))] + (if (and (= "@param" p) + t #?(:clj (.startsWith ^String t "{") + :cljs (gstring/startsWith t "{"))) + (string/join " " (concat [p t (munge n)] xs)) + line)) + line)) + (defn emit-comment "Emit a nicely formatted comment string." [doc jsdoc] @@ -482,7 +493,7 @@ docs (if jsdoc (concat docs jsdoc) docs) docs (remove nil? docs)] (letfn [(print-comment-lines [e] - (let [[x & ys] (string/split-lines e)] + (let [[x & ys] (map munge-param (string/split-lines e))] (emitln " * " (string/replace x "*/" "* /")) (doseq [next-line ys] (emitln " * " From 8226cd2035382d65d07d982bdae77269a22597e4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 14:33:46 -0400 Subject: [PATCH 1543/4033] make comment handling less brittle, add js-inline-comment --- src/main/clojure/cljs/compiler.cljc | 4 ++-- src/main/clojure/cljs/core.cljc | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index fb9d0fe0a..28cc87a42 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1025,8 +1025,8 @@ (defmethod emit* :js [{:keys [op env code segs args]}] - (if (and code #?(:clj (.startsWith ^String code "/*") - :cljs (gstring/startsWith code "/*"))) + (if (and code #?(:clj (.startsWith ^String (string/trim code) "/*") + :cljs (gstring/startsWith (string/trim code) "/*"))) (emits code) (emit-wrap env (if code diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 156ef14f6..a1bd8aa31 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -40,7 +40,7 @@ unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? - specify copy-arguments goog-define js-comment])]) + specify copy-arguments goog-define js-comment js-inline-comment])]) #?(:cljs (:require-macros [cljs.core :as core])) (:require clojure.walk clojure.set @@ -866,8 +866,8 @@ (core/list 'js* "debugger;")) (core/defmacro js-comment - "Emit top-level JavaScript multi-line comment. New lines will create a - new comment line." + "Emit a top-level JavaScript multi-line comment. New lines will create a + new comment line. Comment block will be preceded and followed by a newline." [comment] (core/let [[x & ys] (string/split comment #"\n")] (core/list 'js* @@ -879,6 +879,11 @@ (reduce core/str "")) " */\n")))) +(core/defmacro js-inline-comment + "Emit an inline JavaScript comment." + [comment] + (core/list 'js* (core/str "/**" comment "*/"))) + (core/defmacro true? [x] (bool-expr (core/list 'js* "~{} === true" x))) From 5800cfba697c8c261d8a3ff4c22a49fb749a827b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 15:16:11 -0400 Subject: [PATCH 1544/4033] emit defs w/o :init, tweak js-comment, no extra newline --- src/main/clojure/cljs/compiler.cljc | 56 ++++++++++++++--------------- src/main/clojure/cljs/core.cljc | 4 +-- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 28cc87a42..b3d3c30d9 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -527,36 +527,36 @@ (defmethod emit* :def [{:keys [name var init env doc jsdoc export test var-ast]}] (let [mname (munge name)] + (emit-comment doc (concat jsdoc (:jsdoc init))) + (when (:def-emits-var env) + (when (= :return (:context env)) + (emitln "return (")) + (emitln "(function (){")) + (emits var) (when init - (emit-comment doc (concat jsdoc (:jsdoc init))) - (when (:def-emits-var env) - (when (= :return (:context env)) - (emitln "return (")) - (emitln "(function (){")) - (emits var) (emits " = " - (if-let [define (get-define mname jsdoc)] - define - init)) - (when (:def-emits-var env) - (emitln "; return (") - (emits (merge - {:op :var-special - :env (assoc env :context :expr)} - var-ast)) - (emitln ");})()") - (when (= :return (:context env)) - (emitln ")"))) - ;; NOTE: JavaScriptCore does not like this under advanced compilation - ;; this change was primarily for REPL interactions - David - ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") - (when-not (= :expr (:context env)) (emitln ";")) - (when export - (emitln "goog.exportSymbol('" (munge export) "', " mname ");")) - (when (and ana/*load-tests* test) - (when (= :expr (:context env)) - (emitln ";")) - (emitln var ".cljs$lang$test = " test ";"))))) + (if-let [define (get-define mname jsdoc)] + define + init))) + (when (:def-emits-var env) + (emitln "; return (") + (emits (merge + {:op :var-special + :env (assoc env :context :expr)} + var-ast)) + (emitln ");})()") + (when (= :return (:context env)) + (emitln ")"))) + ;; NOTE: JavaScriptCore does not like this under advanced compilation + ;; this change was primarily for REPL interactions - David + ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") + (when-not (= :expr (:context env)) (emitln ";")) + (when export + (emitln "goog.exportSymbol('" (munge export) "', " mname ");")) + (when (and ana/*load-tests* test) + (when (= :expr (:context env)) + (emitln ";")) + (emitln var ".cljs$lang$test = " test ";")))) (defn emit-apply-to [{:keys [name params env]}] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a1bd8aa31..85b87cc91 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -867,7 +867,7 @@ (core/defmacro js-comment "Emit a top-level JavaScript multi-line comment. New lines will create a - new comment line. Comment block will be preceded and followed by a newline." + new comment line. Comment block will be preceded by a newline." [comment] (core/let [[x & ys] (string/split comment #"\n")] (core/list 'js* @@ -877,7 +877,7 @@ (core/->> ys (map #(core/str " * " (string/replace % #"^ " "") "\n")) (reduce core/str "")) - " */\n")))) + " */")))) (core/defmacro js-inline-comment "Emit an inline JavaScript comment." From 3f3cb6d1f54e35e15091861ad6879d4d00f6cc30 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 15:33:27 -0400 Subject: [PATCH 1545/4033] add experimental unsafe cast --- src/main/clojure/cljs/core.cljc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 85b87cc91..53fae0da0 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -40,7 +40,8 @@ unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? - specify copy-arguments goog-define js-comment js-inline-comment])]) + specify copy-arguments goog-define js-comment js-inline-comment + unsafe-cast])]) #?(:cljs (:require-macros [cljs.core :as core])) (:require clojure.walk clojure.set @@ -879,6 +880,12 @@ (reduce core/str "")) " */")))) +(core/defmacro unsafe-cast + "EXPERIMENTAL: Subject to change. Unsafely cast a value to a different type." + [t x] + (core/let [cast-expr (core/str "~{} = /** @type {" t "} */ (~{})")] + (core/list 'js* cast-expr x x))) + (core/defmacro js-inline-comment "Emit an inline JavaScript comment." [comment] From 9b54d4db44ede59c928141f2a1e18ceaf640aa76 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 15:55:55 -0400 Subject: [PATCH 1546/4033] CLJS-1404: var resolution for @param and @return type --- src/main/clojure/cljs/compiler.cljc | 80 +++++++++++++++++++---------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b3d3c30d9..d96ee1da5 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -475,37 +475,63 @@ (emits "(function(){throw " throw "})()") (emitln "throw " throw ";"))) -(defn munge-param [line] - (if (re-find #"@param" line) - (let [[p t n & xs] (map string/trim - (string/split (string/trim line) #" "))] +(defn resolve-type [env t] + (str (:name (ana/resolve-var env (symbol t))))) + +(defn resolve-types [env ts] + (let [ts (-> ts string/trim (subs 1 (dec (count ts)))) + xs (string/split ts #"\|")] + (str + "{" + (->> (map #(resolve-type env %) xs) + (map munge) + (string/join "|")) + "}"))) + +(defn munge-param-return [env line] + (cond + (re-find #"@param" line) + (let [[p ts n & xs] (map string/trim + (string/split (string/trim line) #" "))] (if (and (= "@param" p) - t #?(:clj (.startsWith ^String t "{") - :cljs (gstring/startsWith t "{"))) - (string/join " " (concat [p t (munge n)] xs)) + ts #?(:clj (.startsWith ^String ts "{") + :cljs (gstring/startsWith ts "{"))) + (string/join " " (concat [p (resolve-types env ts) (munge n)] xs)) line)) - line)) + + (re-find #"@return" line) + (let [[p ts & xs] (map string/trim + (string/split (string/trim line) #" "))] + (if (and (= "@return" p) + ts #?(:clj (.startsWith ^String ts "{") + :cljs (gstring/startsWith ts "{"))) + (string/join " " (concat [p (resolve-types env ts)] xs)) + line)) + + :else line)) (defn emit-comment "Emit a nicely formatted comment string." - [doc jsdoc] - (let [docs (when doc [doc]) - docs (if jsdoc (concat docs jsdoc) docs) - docs (remove nil? docs)] - (letfn [(print-comment-lines [e] - (let [[x & ys] (map munge-param (string/split-lines e))] - (emitln " * " (string/replace x "*/" "* /")) - (doseq [next-line ys] - (emitln " * " - (-> next-line - (string/replace #"^ " "") - (string/replace "*/" "* /"))))))] - (when (seq docs) - (emitln "/**") - (doseq [e docs] - (when e - (print-comment-lines e))) - (emitln " */"))))) + ([doc jsdoc] + (emit-comment nil doc jsdoc)) + ([env doc jsdoc] + (let [docs (when doc [doc]) + docs (if jsdoc (concat docs jsdoc) docs) + docs (remove nil? docs)] + (letfn [(print-comment-lines [e] + (let [[x & ys] (map #(munge-param-return env %) (string/split-lines e))] + (emitln " * " (string/replace x "*/" "* /")) + (doseq [next-line ys] + (emitln " * " + (-> next-line + (string/replace #"^ " "") + (string/replace "*/" "* /"))))))] + (when (seq docs) + (emitln "/**") + (doseq [e docs] + (when e + (print-comment-lines e))) + (emitln " */")))))) (defn valid-define-value? [x] (or (string? x) @@ -527,7 +553,7 @@ (defmethod emit* :def [{:keys [name var init env doc jsdoc export test var-ast]}] (let [mname (munge name)] - (emit-comment doc (concat jsdoc (:jsdoc init))) + (emit-comment env doc (concat jsdoc (:jsdoc init))) (when (:def-emits-var env) (when (= :return (:context env)) (emitln "return (")) From afc57a7df587faf28ba3116d8789d6c9e6b217de Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 16:12:35 -0400 Subject: [PATCH 1547/4033] CLJS-1405: type resolution needs to handle modifiers like leading !, trailing =, object syntax punting on function & record syntax for now --- src/main/clojure/cljs/compiler.cljc | 34 +++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index d96ee1da5..f3e7e51cb 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -475,8 +475,38 @@ (emits "(function(){throw " throw "})()") (emitln "throw " throw ";"))) -(defn resolve-type [env t] - (str (:name (ana/resolve-var env (symbol t))))) +(def base-types + #{"boolean" "Boolean" + "string" "String" + "number" "Number" + "array" "Array" + "object" "Object" + "RegExp" + "Date"}) + +(defn resolve-type [env ^String t] + (cond + (get base-types t) t + + #?(:clj (.startsWith t "!") + :cljs (gstring/startsWith t "!")) t + + #?(:clj (.startsWith t "{") + :cljs (gstring/startsWith t "{")) t + + #?(:clj (.startsWith t "function") + :cljs (gstring/startsWith t "function")) t + + :else + (let [optional? #?(:clj (.endsWith t "=") + :cljs (gstring/endsWith t "=")) + t (if optional? + (subs t 0 (dec (count t))) + t) + ret (str (:name (ana/resolve-var env (symbol t))))] + (if optional? + (str ret "=") + ret)))) (defn resolve-types [env ts] (let [ts (-> ts string/trim (subs 1 (dec (count ts)))) From df2b5fd62466c207800f368b1f1c834610ba7412 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 20:15:29 -0400 Subject: [PATCH 1548/4033] tweaks to type support --- src/main/clojure/cljs/compiler.cljc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index f3e7e51cb..2be65b5f9 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -476,7 +476,8 @@ (emitln "throw " throw ";"))) (def base-types - #{"boolean" "Boolean" + #{"null" "*" + "boolean" "Boolean" "string" "String" "number" "Number" "array" "Array" @@ -484,10 +485,15 @@ "RegExp" "Date"}) +(def mapped-types + {"nil" "null"}) + (defn resolve-type [env ^String t] (cond (get base-types t) t + (get mapped-types t) (get mapped-types t) + #?(:clj (.startsWith t "!") :cljs (gstring/startsWith t "!")) t @@ -508,13 +514,19 @@ (str ret "=") ret)))) +(defn type-munge [s] + (cond + (= "null" s) s + (= "*" s) s + :else (munge s))) + (defn resolve-types [env ts] (let [ts (-> ts string/trim (subs 1 (dec (count ts)))) xs (string/split ts #"\|")] (str "{" (->> (map #(resolve-type env %) xs) - (map munge) + (map type-munge) (string/join "|")) "}"))) From c13383ca1081f4512231d42d376c6fa503f0a510 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Aug 2015 20:36:37 -0400 Subject: [PATCH 1549/4033] more tweaks / fixes --- src/main/clojure/cljs/compiler.cljc | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 2be65b5f9..c313be26d 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -476,7 +476,7 @@ (emitln "throw " throw ";"))) (def base-types - #{"null" "*" + #{"null" "*" "...*" "boolean" "Boolean" "string" "String" "number" "Number" @@ -509,26 +509,15 @@ t (if optional? (subs t 0 (dec (count t))) t) - ret (str (:name (ana/resolve-var env (symbol t))))] + ret (munge (str (:name (ana/resolve-var env (symbol t)))))] (if optional? (str ret "=") ret)))) -(defn type-munge [s] - (cond - (= "null" s) s - (= "*" s) s - :else (munge s))) - (defn resolve-types [env ts] (let [ts (-> ts string/trim (subs 1 (dec (count ts)))) xs (string/split ts #"\|")] - (str - "{" - (->> (map #(resolve-type env %) xs) - (map type-munge) - (string/join "|")) - "}"))) + (str "{" (string/join "|" (map #(resolve-type env %) xs)) "}"))) (defn munge-param-return [env line] (cond From d435b4395c719644a4a16f0277ae912f61db42a6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Aug 2015 10:16:26 -0400 Subject: [PATCH 1550/4033] mark protocols with @interface --- src/main/clojure/cljs/core.cljc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 53fae0da0..14d36f476 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1866,7 +1866,10 @@ (throw (missing-protocol ~(core/str psym "." fname) ~(first sig)))))))))) - psym (vary-meta psym assoc-in [:protocol-info :methods] + psym (-> psym + (vary-meta update-in [:jsdoc] conj + "@interface") + (vary-meta assoc-in [:protocol-info :methods] (into {} (map (core/fn [[fname & sigs]] @@ -1875,7 +1878,7 @@ sigs (take-while vector? sigs)] [(vary-meta fname assoc :doc doc) (vec sigs)])) - methods))) + methods)))) method (core/fn [[fname & sigs]] (core/let [doc (core/as-> (last sigs) doc (core/when (core/string? doc) doc)) From 3ecf5aff4151b10d8abf2376f8fae735c61f28a6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Aug 2015 10:53:59 -0400 Subject: [PATCH 1551/4033] fix arg order for cljs.build.api/mark-cljs-ns-for-recompile! --- src/main/clojure/cljs/build/api.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 0bbf9a5ef..04f00c8ba 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -42,7 +42,7 @@ "Backdates a cljs target file so that it the cljs compiler will recompile it." ([ns-sym] (mark-cljs-ns-for-recompile! ns-sym nil)) ([ns-sym output-dir] - (let [s (target-file-for-cljs-ns output-dir ns-sym)] + (let [s (target-file-for-cljs-ns ns-sym output-dir)] (when (.exists s) (.setLastModified s 5000))))) From 27af25a3e43e4b61ba924f77ed5068717613e026 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Aug 2015 11:27:53 -0400 Subject: [PATCH 1552/4033] CLJS-1409: Allow basic type checking of protocols include :protocols in the deftype & defrecord ASTs. emit function(){} instead of {} for protocol to make Closure happy. emit @implements for each protocol implemented by a type. --- src/main/clojure/cljs/analyzer.cljc | 9 ++++++--- src/main/clojure/cljs/compiler.cljc | 8 ++++++-- src/main/clojure/cljs/core.cljc | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3a6ed83f8..2ddbe30f8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1963,7 +1963,8 @@ :shadow (m fld)})) {} (if (= :defrecord* op) (concat fields '[__meta __extmap ^:mutable __hash]) - fields))] + fields)) + protocols (-> tsym meta :protocols)] (swap! env/*compiler* update-in [::namespaces (-> env :ns :name) :defs tsym] (fn [m] (let [m (assoc (or m {}) @@ -1973,9 +1974,11 @@ :record (= :defrecord* op))] (merge m (dissoc (meta tsym) :protocols) - {:protocols (-> tsym meta :protocols)} + {:protocols protocols} (source-info tsym env))))) - {:op op :env env :form form :t t :fields fields :pmasks pmasks :body (analyze (assoc env :locals locals) body)})) + {:op op :env env :form form :t t :fields fields :pmasks pmasks + :protocols (disj protocols 'cljs.core/Object) + :body (analyze (assoc env :locals locals) body)})) (defmethod parse 'deftype* [_ env form _ _] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index c313be26d..3aad9f18f 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1042,11 +1042,13 @@ (load-libs uses requires (:use reloads))) (defmethod emit* :deftype* - [{:keys [t fields pmasks body]}] + [{:keys [t fields pmasks body protocols]}] (let [fields (map munge fields)] (emitln "") (emitln "/**") (emitln "* @constructor") + (doseq [protocol protocols] + (emitln " * @implements {" (munge (str protocol)) "}")) (emitln "*/") (emitln (munge t) " = (function (" (comma-sep fields) "){") (doseq [fld fields] @@ -1057,11 +1059,13 @@ (emit body))) (defmethod emit* :defrecord* - [{:keys [t fields pmasks body]}] + [{:keys [t fields pmasks body protocols]}] (let [fields (concat (map munge fields) '[__meta __extmap __hash])] (emitln "") (emitln "/**") (emitln "* @constructor") + (doseq [protocol protocols] + (emitln " * @implements {" (munge (str protocol)) "}")) (emitln "*/") (emitln (munge t) " = (function (" (comma-sep fields) "){") (doseq [fld fields] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 14d36f476..495f62278 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1895,7 +1895,7 @@ sigs))))] `(do (set! ~'*unchecked-if* true) - (def ~psym (js-obj)) + (def ~psym (~'js* "function(){}")) ~@(map method methods) (set! ~'*unchecked-if* false)))) From f0c7d4c34b1cebeec38f26a098651dc34fcfaecb Mon Sep 17 00:00:00 2001 From: Martin Klepsch Date: Sun, 9 Aug 2015 15:56:50 +0200 Subject: [PATCH 1553/4033] fix goog-define docstring add-implicit-options only munges symbol keys, string keys remain untouched --- src/main/clojure/cljs/core.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 495f62278..716ede576 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -698,7 +698,10 @@ (ns your-app.core) (goog-define DEBUG! false) ;; can be overridden with - :closure-defines {\"your-app.core.DEBUG!\" true}" + :closure-defines {\"your-app.core.DEBUG_BANG_\" true} + or + :closure-defines {'your-app.core/DEBUG! true}" + [sym default] (assert-args goog-define (core/or (core/string? default) From f2f79e6d11ea73f3b52d4fd0faeb2983743f3024 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Aug 2015 11:29:30 -0400 Subject: [PATCH 1554/4033] whitespace --- src/main/clojure/cljs/core.cljc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 716ede576..83cf21174 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -701,7 +701,6 @@ :closure-defines {\"your-app.core.DEBUG_BANG_\" true} or :closure-defines {'your-app.core/DEBUG! true}" - [sym default] (assert-args goog-define (core/or (core/string? default) From 12be5eac7a67c01ef8d4080048a73ac822909029 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Aug 2015 11:38:57 -0400 Subject: [PATCH 1555/4033] cleanup resolve-type, properly handle non-nullable types --- src/main/clojure/cljs/compiler.cljc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 3aad9f18f..9dc21cb34 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -495,7 +495,8 @@ (get mapped-types t) (get mapped-types t) #?(:clj (.startsWith t "!") - :cljs (gstring/startsWith t "!")) t + :cljs (gstring/startsWith t "!")) + (str "!" (resolve-type env (subs t 1))) #?(:clj (.startsWith t "{") :cljs (gstring/startsWith t "{")) t @@ -503,16 +504,12 @@ #?(:clj (.startsWith t "function") :cljs (gstring/startsWith t "function")) t + #?(:clj (.endsWith t "=") + :cljs (gstring/endsWith t "=")) + (str (resolve-type env (subs t 0 (dec (count t)))) "=") + :else - (let [optional? #?(:clj (.endsWith t "=") - :cljs (gstring/endsWith t "=")) - t (if optional? - (subs t 0 (dec (count t))) - t) - ret (munge (str (:name (ana/resolve-var env (symbol t)))))] - (if optional? - (str ret "=") - ret)))) + (munge (str (:name (ana/resolve-var env (symbol t))))))) (defn resolve-types [env ts] (let [ts (-> ts string/trim (subs 1 (dec (count ts)))) From 9510d0a884c0a472f693d24f59e48eb9233218b8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Aug 2015 12:17:59 -0400 Subject: [PATCH 1556/4033] support function types --- src/main/clojure/cljs/compiler.cljc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 9dc21cb34..5d73c222a 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -502,7 +502,18 @@ :cljs (gstring/startsWith t "{")) t #?(:clj (.startsWith t "function") - :cljs (gstring/startsWith t "function")) t + :cljs (gstring/startsWith t "function")) + (let [idx (.lastIndexOf t ":") + [fstr rstr] (if-not (== -1 idx) + [(subs t 0 idx) (subs t (inc idx) (count t))] + [t nil]) + ret-t (when rstr (resolve-type env rstr)) + axstr (subs fstr 9 (dec (count fstr))) + args-ts (when-not (string/blank? axstr) + (map (comp #(resolve-type env %) string/trim) + (string/split axstr #",")))] + (cond-> (str "function(" (string/join "," args-ts) ")") + ret-t (str ":" ret-t))) #?(:clj (.endsWith t "=") :cljs (gstring/endsWith t "=")) From f7f837591a46228edf2fff2be703e5b632996860 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 10 Aug 2015 09:50:32 -0400 Subject: [PATCH 1557/4033] re-add trailing newline to comment block --- src/main/clojure/cljs/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 83cf21174..72a641911 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -870,7 +870,7 @@ (core/defmacro js-comment "Emit a top-level JavaScript multi-line comment. New lines will create a - new comment line. Comment block will be preceded by a newline." + new comment line. Comment block will be preceded and followed by a newline" [comment] (core/let [[x & ys] (string/split comment #"\n")] (core/list 'js* @@ -880,7 +880,7 @@ (core/->> ys (map #(core/str " * " (string/replace % #"^ " "") "\n")) (reduce core/str "")) - " */")))) + " */\n")))) (core/defmacro unsafe-cast "EXPERIMENTAL: Subject to change. Unsafely cast a value to a different type." From 8ddfb80e27dee5ec68a534124288872a94624c22 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 10 Aug 2015 10:06:25 -0400 Subject: [PATCH 1558/4033] CLJS-1414: Only munge @param @return if type checking --- src/main/clojure/cljs/compiler.cljc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 5d73c222a..adba6135a 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -549,6 +549,11 @@ :else line)) +(defn checking-types? [] + (#{:error :warn} + (get-in @env/*compiler* + [:options :closure-warnings :check-types]))) + (defn emit-comment "Emit a nicely formatted comment string." ([doc jsdoc] @@ -558,7 +563,9 @@ docs (if jsdoc (concat docs jsdoc) docs) docs (remove nil? docs)] (letfn [(print-comment-lines [e] - (let [[x & ys] (map #(munge-param-return env %) (string/split-lines e))] + (let [[x & ys] + (map #(if (checking-types?) (munge-param-return env %) %) + (string/split-lines e))] (emitln " * " (string/replace x "*/" "* /")) (doseq [next-line ys] (emitln " * " From 886a8582ac1a5c2dd73d6340582aaf08e99b2b25 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 10 Aug 2015 15:08:04 -0400 Subject: [PATCH 1559/4033] fix comment test --- src/test/clojure/cljs/compiler_tests.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 21db4ec21..4e72e7b4a 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -72,8 +72,9 @@ (deftest test-cljs-428 (letfn [(check-docs [docs] (is (= 1 (count (re-seq #"\*/" docs)))))] - (check-docs (with-out-str - (comp/emit-comment "/* multiline comments */" nil))) + (check-docs (with-out-str + (env/ensure + (comp/emit-comment "/* multiline comments */" nil)))) (check-docs (with-out-str (comp/emit (ana/analyze aenv From c90f4c169812ad35624493a10b2e3b919fe0a022 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 10 Aug 2015 17:39:23 -0400 Subject: [PATCH 1560/4033] CLJS-1017: Support :main for :advanced builds --- src/main/clojure/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 115980ba2..b948e0615 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1666,7 +1666,10 @@ (repeat warnings)) warnings))) ana/*verbose* (:verbose opts)] - (let [compiled (util/measure compiler-stats + (let [source (if (and (:main all-opts) (#{:advanced :simple} (:optimizations all-opts))) + (:uri (cljs-source-for-namespace (:main all-opts))) + source) + compiled (util/measure compiler-stats "Compile basic sources" (doall (-compile source all-opts))) js-sources (util/measure compiler-stats From c79327276dc9fc4aca1fb4adaa3ee776a0cca26f Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 10 Aug 2015 18:40:34 -0400 Subject: [PATCH 1561/4033] cljs.compiler alias in cljs.core macros file --- src/main/clojure/cljs/core.cljc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 72a641911..00a1de752 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -46,7 +46,7 @@ (:require clojure.walk clojure.set [clojure.string :as string] - cljs.compiler + [cljs.compiler :as comp] [cljs.env :as env] #?(:cljs [cljs.core :as core]) #?(:cljs [cljs.analyzer :as ana]))) @@ -707,7 +707,7 @@ (core/number? default) (core/true? default) (core/false? default)) "a string, number or boolean as default value") - (core/let [defname (cljs.compiler/munge (core/str *ns* "/" sym)) + (core/let [defname (comp/munge (core/str *ns* "/" sym)) type (core/cond (core/string? default) "string" (core/number? default) "number" @@ -1258,7 +1258,7 @@ this-sym (gensym "_") locals (keys (:locals &env)) ns (core/-> &env :ns :name) - munge cljs.compiler/munge] + munge comp/munge] `(do (when-not (exists? ~(symbol (core/str ns) (core/str t))) (deftype ~t [~@locals ~meta-sym] @@ -2602,7 +2602,7 @@ (= quote1 'quote) (core/symbol? sym)) "Arguments to ns-unmap must be quoted symbols") (swap! env/*compiler* update-in [::ana/namespaces ns :defs] dissoc sym) - `(js-delete ~(cljs.compiler/munge ns) ~(cljs.compiler/munge (core/str sym)))) + `(js-delete ~(comp/munge ns) ~(comp/munge (core/str sym)))) (core/defmacro vswap! "Non-atomically swaps the value of the volatile as if: From 856946189531d472278dda445c7c6a0c681b0837 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 10 Aug 2015 18:51:05 -0400 Subject: [PATCH 1562/4033] don't emit var_arg @param if we aren't type-checking --- src/main/clojure/cljs/core.cljc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 00a1de752..1dde6b5f9 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2839,11 +2839,15 @@ (core/cond (multi-arity-fn? fdecl) (multi-arity-fn name - (update-in m [:jsdoc] conj "@param {...*} var_args") fdecl) + (if (comp/checking-types?) + (update-in m [:jsdoc] conj "@param {...*} var_args") + m) fdecl) (variadic-fn? fdecl) (variadic-fn name - (update-in m [:jsdoc] conj "@param {...*} var_args") fdecl) + (if (comp/checking-types?) + (update-in m [:jsdoc] conj "@param {...*} var_args") + m) fdecl) :else (core/list 'def (with-meta name m) From 04d0b92da513c8e7129e06bc34b19b1801207c32 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 10 Aug 2015 19:20:39 -0400 Subject: [PATCH 1563/4033] CLJS-1481: self-host: defprotocol regressed with arity error Need to expicilty invoke cljs.core/-> --- src/main/clojure/cljs/core.cljc | 2 +- src/test/self/self_host/test.cljs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 1dde6b5f9..9eba4c212 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1868,7 +1868,7 @@ (throw (missing-protocol ~(core/str psym "." fname) ~(first sig)))))))))) - psym (-> psym + psym (core/-> psym (vary-meta update-in [:jsdoc] conj "@interface") (vary-meta assoc-in [:protocol-info :methods] diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 54a1a8488..61979bcb1 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -51,12 +51,17 @@ (deftest test-analyze-str (async done - (let [l (latch 1 done)] + (let [l (latch 2 done)] (cljs/analyze-str st "(+ 1 1)" nil {:context :expr} (fn [{:keys [error value]}] (is (nil? error)) (is (= :js (:op value))) + (inc! l))) + (cljs/analyze-str st "(defprotocol IFoo)" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) (inc! l)))))) (deftest test-compile-str From 01830cffcd7d83e923372a22e0958a2cf57c6549 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 10 Aug 2015 20:46:02 -0400 Subject: [PATCH 1564/4033] clean up tests a bit --- src/test/self/self_host/test.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 61979bcb1..92d10710c 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -70,19 +70,19 @@ (cljs/compile-str st "(+ 1 1)" (fn [{:keys [error value]}] (is (nil? error)) - (is (= value "(1 + 1);\n")) + (is (= "(1 + 1);\n" value)) (inc! l))) (cljs/compile-str st "(fn [])" nil {:context :expr} (fn [{:keys [error value]}] (is (nil? error)) - (is (= value "(function (){\nreturn null;\n})")) + (is (= "(function (){\nreturn null;\n})" value)) (inc! l))) (cljs/compile-str st "(if cljs.core.first 1 2)" nil {:context :expr} (fn [{:keys [error value]}] (is (nil? error)) - (is (= value "(cljs.core.truth_(cljs.core.first)?1:2)")) + (is (= "(cljs.core.truth_(cljs.core.first)?1:2)" value)) (inc! l)))))) (deftest test-eval-str @@ -92,7 +92,7 @@ {:eval node-eval} (fn [{:keys [error value]}] (is (nil? error)) - (is (== value 2)) + (is (== 2 value)) (inc! l))) (cljs/eval-str st "(def x 1)" nil {:eval node-eval @@ -116,7 +116,7 @@ :def-emits-var true} (fn [{:keys [error value]}] (is (nil? error)) - (is (== 3)) + (is (== 3 value)) (inc! l))) #_(cljs/eval-str st "(ns foo.bar)" nil {:eval node-eval @@ -132,7 +132,7 @@ :def-emits-var true} (fn [{:keys [error value]}] (is (nil? error)) - (is (== (js/cljs.user.foo 1 2) 3)) + (is (== 3 (js/cljs.user.foo 1 2))) (inc! l)))))) #_(deftest test-eval-str-with-require From 7f29513de3ace7a14df220f4d0be1a8faeb92723 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 10 Aug 2015 23:07:08 -0400 Subject: [PATCH 1565/4033] CLJS-1416: cljs.util/last-modified leaks files --- src/main/clojure/cljs/util.cljc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 7162dfd91..05a9ff274 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -142,7 +142,14 @@ (defn last-modified [src] (cond (file? src) (.lastModified ^File src) - (url? src) (.getLastModified (.openConnection ^URL src)) + (url? src) + (let [conn (.openConnection ^URL src)] + (try + (.getLastModified conn) + (finally + (let [ins (.getInputStream conn)] + (when ins + (.close ins)))))) :else (throw (IllegalArgumentException. (str "Cannot get last modified for " src))))) From 637e247dc57695b153f3deb27b9999f77c2fa015 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 10 Aug 2015 14:48:17 -0400 Subject: [PATCH 1566/4033] CLJS-1417: cljs.js: require macros failure error message pertains to non-macros Adds and uses a new analyzer error message specifically for the failure to load a macros ns. --- src/main/cljs/cljs/js.cljs | 4 +++- src/main/clojure/cljs/analyzer.cljc | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 32a5ef92a..afe0fd8c0 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -223,7 +223,9 @@ (str "Invalid :lang specified " lang ", only :clj or :js allowed")))))) (cb (wrap-error (ana/error env - (ana/error-message :undeclared-ns + (ana/error-message (if (:macros-ns opts) + :undeclared-macros-ns + :undeclared-ns) {:ns-sym name :js-provide (cljs.core/name name)}))))))) (catch :default cause (cb (wrap-error diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2ddbe30f8..3e914d782 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -228,6 +228,12 @@ ", " (ns->relpath ns-sym :cljc) ", or Closure namespace \"" js-provide "\"")) +(defmethod error-message :undeclared-macros-ns + [warning-type {:keys [ns-sym js-provide] :as info}] + (str "No such macros namespace: " ns-sym + ", could not locate " (ns->relpath ns-sym :clj) + " or " (ns->relpath ns-sym :cljc))) + (defmethod error-message :dynamic [warning-type info] (str (:name info) " not declared ^:dynamic")) From 30b809bedd976e1d2e37f99cfa72a20abd4b9186 Mon Sep 17 00:00:00 2001 From: Raymond Huang Date: Sat, 4 Jul 2015 08:35:17 -0700 Subject: [PATCH 1567/4033] CLJS-1299: Add support for more literals in reader Adds support for rich character literals like `\space`, `\return` and unicode literals like `\u1234`. The implementation is a port of the JVM's `CharacterReader`. See http://dev.clojure.org/jira/browse/CLJS-1299 --- src/main/cljs/cljs/reader.cljs | 17 ++++++++++++++++- src/test/cljs/cljs/reader_test.cljs | 4 ++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index 4f5d74ad5..32b0d0e74 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -334,6 +334,21 @@ nil if the end of stream has been reached") (.-length token))) (special-symbols token (symbol token))))) +(defn read-literal + [rdr ch] + (let [token (read-token rdr ch) + chars (subs token 1)] + (cond (identical? (.-length chars) 1) chars + (identical? chars "tab") "\t" + (identical? chars "return") "\r" + (identical? chars "newline") "\n" + (identical? chars "space") " " + (identical? chars "backspace") "\b" + (identical? chars "formfeed") "\f" + (identical? (.charAt chars 0) "u") (make-unicode-char (subs chars 1)) + (identical? (.charAt chars 0) "o") (not-implemented rdr token) + :else (reader-error rdr "Unknown character literal: " token)))) + (defn read-keyword [reader initch] (let [token (read-token reader (read-char reader)) @@ -407,7 +422,7 @@ nil if the end of stream has been reached") (identical? c \]) read-unmatched-delimiter (identical? c \{) read-map (identical? c \}) read-unmatched-delimiter - (identical? c \\) read-char + (identical? c \\) read-literal (identical? c \#) read-dispatch :else nil)) diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 42d7c0516..b8f4b3529 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -94,6 +94,10 @@ (reader/register-default-tag-parser! (fn [tag val] val)) (is (= [1 2] (reader/read-string "#a.b/c [1 2]")))) + (testing "Character Literals" + (is (= [\tab \return \newline \space \backspace \formfeed \u1234] + (reader/read-string "[\\tab \\return \\newline \\space \\backspace \\formfeed \\u1234]")))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Unicode Tests From 2677ff7a595e872d9393237818894d84a0214083 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 11 Aug 2015 15:10:13 -0400 Subject: [PATCH 1568/4033] bind cljs.tool.reader/resolve-symbol --- src/main/cljs/cljs/js.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index afe0fd8c0..5db829756 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -391,6 +391,7 @@ *ns* (create-ns ns) ana/*passes* (:*passes* bound-vars) r/*data-readers* (:*data-readers* bound-vars) + r/resolve-symbol ana/resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} @@ -476,6 +477,7 @@ ana/*cljs-ns* (:*cljs-ns* bound-vars) *ns* (create-ns (:*cljs-ns* bound-vars)) r/*data-readers* (:*data-readers* bound-vars) + r/resolve-symbol ana/resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [aenv (ana/empty-env) aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) @@ -549,6 +551,7 @@ ana/*cljs-ns* ns *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) + r/resolve-symbol ana/resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} @@ -641,6 +644,7 @@ ana/*cljs-ns* ns *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) + r/resolve-symbol ana/resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} From 54ac93024c1dbef899856ee955f209eced1c4dc2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Aug 2015 20:46:48 +0900 Subject: [PATCH 1569/4033] tweak memory defaults for cljsc script --- bin/cljsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cljsc b/bin/cljsc index eb8428590..82e3ac48c 100755 --- a/bin/cljsc +++ b/bin/cljsc @@ -17,5 +17,5 @@ then echo 'Usage: cljsc ' echo ' cljsc "{:optimizations :advanced}"' else - java -server -cp "$CLJSC_CP" clojure.main "$CLOJURESCRIPT_HOME/bin/cljsc.clj" "$@" + java -server -Xms1g -Xmx2g -cp "$CLJSC_CP" clojure.main "$CLOJURESCRIPT_HOME/bin/cljsc.clj" "$@" fi From fcd1358dfa9eb4d83a2eacf478ceecce2971691b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Aug 2015 20:49:17 +0900 Subject: [PATCH 1570/4033] add goog.object to set of implicit namespaces break out implicit namespaces set and reuse --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3e914d782..2b9a1720d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -556,10 +556,12 @@ (throw err#) (throw (error ~env (.getMessage err#) err#))))))) +(def implicit-nses '#{cljs.core goog goog.object goog.string goog.array Math}) + (defn implicit-import? #?(:cljs {:tag boolean}) [env prefix suffix] - (contains? '#{goog goog.object goog.string goog.array Math} prefix)) + (contains? implicit-nses prefix)) (defn confirm-var-exist-warning [env prefix suffix] (fn [env prefix suffix] @@ -615,7 +617,7 @@ "Given env, an analysis environment, and ns-sym, a symbol identifying a namespace, confirm that the namespace exists. Warn if not found." [env ns-sym] - (when (and (nil? (get '#{cljs.core goog Math goog.string} ns-sym)) + (when (and (nil? (get implicit-nses ns-sym)) (nil? (get (-> env :ns :requires) ns-sym)) ;; something else may have loaded the namespace, i.e. load-file (nil? (gets @env/*compiler* ::namespaces ns-sym)) From e5491d55bfadc641e9da05ca1df04b468832d16c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Aug 2015 20:52:27 +0900 Subject: [PATCH 1571/4033] add cljs.analyzer.api/get-js-index --- src/main/clojure/cljs/analyzer/api.clj | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.clj index 33410ec00..8cf0ff296 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.clj @@ -58,6 +58,13 @@ ([state] (get @state :options))) +(defn get-js-index + "Return the currently computed Google Closure js dependency index from the + compiler state." + ([] (get-options env/*compiler*)) + ([state] + (get @state :js-dependency-index))) + (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context (one of :statement, :expr, :return), :ns (a symbol naming the From d7740694be49ba38c778deae59a3e047ca881631 Mon Sep 17 00:00:00 2001 From: Peter Stephens Date: Sun, 16 Aug 2015 20:10:07 -0500 Subject: [PATCH 1572/4033] CLJS-1403: Added & updated Windows shell scripts. --- bin/cljsc.bat | 7 +-- bin/cljsc.clj | 4 +- script/bootstrap.ps1 | 134 +++++++++++++++++++++++++++++++++++++++++++ script/test.ps1 | 42 ++++++++++++++ 4 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 script/bootstrap.ps1 create mode 100644 script/test.ps1 diff --git a/bin/cljsc.bat b/bin/cljsc.bat index 780b535b6..694f32587 100644 --- a/bin/cljsc.bat +++ b/bin/cljsc.bat @@ -1,14 +1,13 @@ - @echo off setLocal EnableDelayedExpansion if "%CLOJURESCRIPT_HOME%" == "" set CLOJURESCRIPT_HOME=%~dp0..\ -set CLASSPATH=%CLOJURESCRIPT_HOME%src\clj;%CLOJURESCRIPT_HOME%src\cljs" -for /R "%CLOJURESCRIPT_HOME%\lib" %%a in (*.jar) do ( +set CLASSPATH=%CLOJURESCRIPT_HOME%src\main\clojure;%CLOJURESCRIPT_HOME%src\main\cljs;%CLOJURESCRIPT_HOME%src\test\cljs +for /R "%CLOJURESCRIPT_HOME%lib" %%a in (*.jar) do ( set CLASSPATH=!CLASSPATH!;%%a ) -set CLASSPATH=!CLASSPATH!" +set CLASSPATH=!CLASSPATH! if (%1) == () ( echo Usage: "cljsc > out.js" diff --git a/bin/cljsc.clj b/bin/cljsc.clj index 1e376323b..3ae8a78b0 100644 --- a/bin/cljsc.clj +++ b/bin/cljsc.clj @@ -14,7 +14,9 @@ opts-string (apply str (interpose " " (rest args))) options (when (> (count opts-string) 1) (try (read-string opts-string) - (catch Exception e (println e))))] + (catch Exception e + (binding [*out* *err*] + (println "Failed to parse command line args:" e)))))] {:source source :options (merge {:output-to :print} options)})) (let [args (transform-cl-args *command-line-args*)] diff --git a/script/bootstrap.ps1 b/script/bootstrap.ps1 new file mode 100644 index 000000000..a33e0e23d --- /dev/null +++ b/script/bootstrap.ps1 @@ -0,0 +1,134 @@ +$ErrorActionPreference = "Stop" +$root = Resolve-Path "$PSScriptRoot\.." +$shell = New-Object -com shell.application + +# Read white listed dependency version info from the /bin/sh script and store in variables +Get-Content $root\script\bootstrap | + Where-Object { $_ -match '^\s*(\w+)\s*=\s*\"([^\"]*)\"\s*$' } | + Where-Object { $matches[1] -in "CLOJURE_RELEASE", "CLOSURE_RELEASE", + "DJSON_RELEASE", "GCLOSURE_LIB_RELEASE", "RHINO_RELEASE", "TREADER_RELEASE" } | + Foreach-Object { New-Variable $matches[1] $matches[2] -Scope private } + +function Get-WebResource($url, $dstPath) { + Write-Verbose "Downloading '$url' -> '$dstPath'" + Invoke-RestMethod $url -OutFile $dstPath +} + +function Expand-ZipFile($srcPath, $dstDir, $items) { + Write-Verbose "Unzipping '$srcPath'" + + function Get-ShellFolder($dir) { + $folder = $shell.NameSpace($dir) + if($folder -eq $null) { + throw "Failed to bind to folder '$dir'" + } + $folder + } + + function Copy-ShellItem([Parameter(ValueFromPipeline=$true)] $src) { + process { + Write-Verbose "Expanding '$($src.Path)' -> '$dstDir'" + $dstFolder.CopyHere($src, 4 + 16 + 1024) + } + } + + function Parse-ShellItem([Parameter(ValueFromPipeline=$true)] $name) { + process { + $x = $srcFolder.ParseName($name) + if($x -eq $null) { + throw "Failed fo find item '$name' in zip file '$srcPath'" + } + $x + } + } + + $srcFolder = Get-ShellFolder($srcPath) + $dstFolder = Get-ShellFolder($dstDir) + + if($items -ne $null) { + $items | Parse-ShellItem | Copy-ShellItem + } + else { + $srcFolder.Items() | Copy-ShellItem + } +} + +function Move-File($srcPath, $dstPath) { + Delete-File $dstPath + Write-Verbose "Moving '$srcPath' -> '$dstPath'" + Move-Item $srcPath $dstPath +} + +function Copy-File($srcPath, $dstPath) { + Delete-File $dstPath + Write-Verbose "Copying '$srcPath' -> '$dstPath'" + Copy-Item $srcPath $dstPath +} + +function Delete-File([Parameter(ValueFromPipeline=$true)] $path) +{ + process { + if(Test-Path $path) { + Write-Verbose "Deleting '$path'" + Remove-Item $path -Recurse + } + } +} + +function Make-Dir($dir) { + if(!(Test-Path $dir -Type Container)) { + Write-Verbose "Making directory '$dir'" + New-Item $dir -ItemType Directory | Out-Null + } +} + +Make-Dir $root\lib +Make-Dir $root\closure\library +Make-Dir $root\closure\compiler + +Write-Host "Fetching Clojure..." +Get-WebResource ` + https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.zip ` + $root\clojure-$CLOJURE_RELEASE.zip +Delete-File $root\lib\clojure-$CLOJURE_RELEASE.jar +Expand-ZipFile $root\clojure-$CLOJURE_RELEASE.zip $root\lib clojure-$CLOJURE_RELEASE\clojure-$CLOJURE_RELEASE.jar +Move-File $root\lib\clojure-$CLOJURE_RELEASE.jar $root\lib\clojure.jar +Delete-File $root\clojure-$CLOJURE_RELEASE.zip + +Write-Host "Fetching data.json..." +Get-WebResource ` + https://repo1.maven.org/maven2/org/clojure/data.json/$DJSON_RELEASE/data.json-$DJSON_RELEASE.jar ` + $root\lib\data.json-$DJSON_RELEASE.jar + +# TODO: Implement Closure SVN support +Write-Host "Fetching Google Closure library..." +Get-WebResource ` + https://repo1.maven.org/maven2/org/clojure/google-closure-library/$GCLOSURE_LIB_RELEASE/google-closure-library-$GCLOSURE_LIB_RELEASE.jar ` + $root\lib\google-closure-library-$GCLOSURE_LIB_RELEASE.jar +Get-WebResource ` + https://repo1.maven.org/maven2/org/clojure/google-closure-library-third-party/$GCLOSURE_LIB_RELEASE/google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar ` + $root\lib\google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar + +Write-Host "Fetching Google Closure compiler..." +Get-WebResource ` + https://dl.google.com/closure-compiler/compiler-$CLOSURE_RELEASE.zip ` + $root\compiler-$CLOSURE_RELEASE.zip +Get-ChildItem $root\closure\compiler\* | Delete-File +Expand-ZipFile $root\compiler-$CLOSURE_RELEASE.zip $root\closure\compiler +Copy-File $root\closure\compiler\compiler.jar $root\lib\compiler.jar +Delete-File $root\compiler-$CLOSURE_RELEASE.zip + +Write-Host "Fetching Rhino..." +Get-WebResource ` + https://github.com/mozilla/rhino/releases/download/Rhino${RHINO_RELEASE}_RELEASE/rhino$RHINO_RELEASE.zip ` + $root\rhino$RHINO_RELEASE.zip +Delete-File $root\lib\js.jar +Expand-ZipFile $root\rhino$RHINO_RELEASE.zip $root\lib rhino$RHINO_RELEASE\js.jar +Delete-File $root\rhino$RHINO_RELEASE.zip + +Write-Host "Fetching tools.reader $TREADER_RELEASE ..." +Get-WebResource ` + https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar ` + $root\lib\tools.reader-$TREADER_RELEASE.jar + +Write-Host "[Bootstrap Completed]" \ No newline at end of file diff --git a/script/test.ps1 b/script/test.ps1 new file mode 100644 index 000000000..d11b5e897 --- /dev/null +++ b/script/test.ps1 @@ -0,0 +1,42 @@ +$ErrorActionPreference = "Stop" +$root = Resolve-Path $PSScriptRoot\.. + +$testjs = "builds/out-adv/core-advanced-test.js" + +$targets = + @{ env="V8_HOME"; name="V8"; cmd={ & "$env:V8_HOME\d8" $testjs } }, + @{ env="SPIDERMONKEY_HOME"; name="SpiderMonkey"; cmd={ & "$env:SPIDERMONKEY_HOME\js" -f $testjs } }, + @{ env="JSC_HOME"; name="JavaScriptCore"; cmd={ & "$env:JSC_HOME\jsc" -f $testjs } }, + @{ env="NASHORN_HOME"; name="Nashorn"; cmd={ & "$env:NASHORN_HOME\jjs" $testjs } } +$ran = 0 + +$opts = '{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :output-dir \"builds\\out-adv\"}' + +function Test-It($env, $name, [scriptblock] $cmd) { + $env_val = if(Test-Path env:$env) { (Get-Item env:$env).Value } else { "" } + if("$env_val" -eq "") { + Write-Host "$env not set, skipping $name tests" + } else { + Write-Host "Testing with $name" + & $cmd + $ran++ + } +} + +Push-Location $root +try { + "builds\out-adv", "out", "target" | + Where-Object { Test-Path $_ -Type leaf } | + Foreach-Object { Remove-Item $_ -recurse -force } + + New-Item builds\out-adv -ItemType Directory -Force | Out-Null + + bin\cljsc src\test/cljs $opts | Set-Content $testjs + + $targets | Foreach-Object { Test-It @_ } +} +finally { + Pop-Location + + Write-Host "Tested with $ran out of $($targets.Length) possible js targets" +} \ No newline at end of file From 5f3c494ce4274177e8e9cb71adcb87b5f9394423 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 24 Aug 2015 13:23:20 -0400 Subject: [PATCH 1573/4033] CLJS-1433: cljs.js/*eval-fn* passed nil :cache Need to deref the env/*compiler* atom --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 5db829756..b9118e3ec 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -690,7 +690,7 @@ :name name :path (ns->relpath name) :source js-source - :cache (get-in env/*compiler* [::ana/namespaces name])} + :cache (get-in @env/*compiler* [::ana/namespaces name])} complete (fn [res] (if (:error res) (cb res) From 19bcf10ccedf63f0e3bf0abfb7481f9c347895fc Mon Sep 17 00:00:00 2001 From: Chris Pickard Date: Fri, 21 Aug 2015 21:33:37 -0400 Subject: [PATCH 1574/4033] CLJS-1431: load-file doc output missing arglists --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index b5b2167c7..1c452a1f5 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1110,7 +1110,7 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) For each name in constructor-name-symbols, adds a mapping from name to the constructor named by closure-namespace to the current namespace. Use :import in the ns macro in preference to calling this directly."} - load-file {:arglist ([name]) + load-file {:arglists ([name]) :doc "Sequentially read and evaluate the set of forms contained in the file."}}) (defn- repl-special-doc [name-symbol] From bbcadde11fa5941fda804faa8fc92a5f33a242cf Mon Sep 17 00:00:00 2001 From: Samuel Miller Date: Sat, 15 Aug 2015 22:11:54 -0600 Subject: [PATCH 1575/4033] CLJS-1353: range inconsistent with Clojure if step is 0 Changed the implementation to match Clojure's and also updated tests. Used == instead of = --- src/main/cljs/cljs/core.cljs | 9 ++++----- src/test/cljs/cljs/core_test.cljs | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d35a57d1c..a1397efe4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8430,11 +8430,10 @@ reduces them without incurring seq initialization" ISeqable (-seq [rng] - (if (pos? step) - (when (< start end) - rng) - (when (> start end) - rng))) + (cond + (pos? step) (when (< start end) rng) + (neg? step) (when (> start end) rng) + :else (when-not (== start end) rng))) ISeq (-first [rng] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index fed54edb4..b1b3d40d8 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1221,11 +1221,11 @@ (is (= (range 0 -3 -1) (list 0 -1 -2))) (is (= (range 3 0 -1) (list 3 2 1))) (is (= (range 0 10 -1) (list))) - (is (= (range 0 1 0) (list))) + (is (= (take 3 (range 0 1 0)) (list 0 0 0))) (is (= (range 10 0 1) (list))) (is (= (range 0 0 0) (list))) (is (= (count (range 0 10 -1)) 0)) - (is (= (count (range 0 1 0)) 0)) + (is (= (count (take 3 (range 0 2 0))) 3)) (is (= (count (range 10 0 1)) 0)) (is (= (count (range 0 0 0)) 0)) (is (= (take 3 (range 1 0 0)) (list 1 1 1))) From 8b7177a579d52a146d19902a19aa92708a567983 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 27 Aug 2015 08:48:34 -0400 Subject: [PATCH 1576/4033] CLJS-1430: self-host: .toString is emitted as .toString$ Revises js-reserved? to not return true for things like "toString". --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ src/test/self/self_host/test.cljs | 8 +++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a1397efe4..23c1859f8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10021,7 +10021,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (set! js-reserved (reduce #(do (gobject/set %1 %2 true) %1) #js {} js-reserved-arr))) - (gobject/containsKey js-reserved x)) + (.hasOwnProperty js-reserved x)) (defn- demunge-pattern [] (when-not DEMUNGE_PATTERN diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b1b3d40d8..157815516 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2922,6 +2922,14 @@ (is (= 'cljs.core/first? (demunge (munge 'cljs.core/first?))))) +(deftest test-munge + (is (= "a_b" (munge "a-b"))) + (is (= "a_SLASH_b" (munge "a/b"))) + (is (= "_DOT__DOT_" (munge ".."))) + (is (= "abstract$" (munge "abstract"))) + (is (= 'abc (munge 'abc))) + (is (= "toString" (munge "toString")))) + (deftest test-uuid-compile-and-runtime-hash (is (= (hash (.toString #uuid "0d1f9029-40fc-4728-8bdd-9862172d4370")) (hash (.toString (UUID. "0d1f9029-40fc-4728-8bdd-9862172d4370" nil)))))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 92d10710c..da40ad9bd 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -66,7 +66,7 @@ (deftest test-compile-str (async done - (let [l (latch 3 done)] + (let [l (latch 4 done)] (cljs/compile-str st "(+ 1 1)" (fn [{:keys [error value]}] (is (nil? error)) @@ -83,6 +83,12 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (= "(cljs.core.truth_(cljs.core.first)?1:2)" value)) + (inc! l))) + (cljs/compile-str st "(.toString \"a\")" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "\"a\".toString()" value)) (inc! l)))))) (deftest test-eval-str From 3e146905999f9b9ffb249e139e266f845c4af34c Mon Sep 17 00:00:00 2001 From: Andrew Rosa Date: Sat, 13 Jun 2015 14:20:19 -0300 Subject: [PATCH 1577/4033] CLJS-1304: Behavior of c.string/replace differs from Clojure JavaScript own's `String.prototype.replace` knows how to do string replacement using functions, therefore is used as internal implementation. But the signature differs as the js version feeds the matched groups as variadic arguements to the replacement function. The solution here is to create a "function adapter" between the js replace and the replacement function. For cases where we have a single matching string without any subgroup, we pass it directly, without the wrapping vector. --- src/main/cljs/clojure/string.cljs | 16 +++++++++++++++- src/test/cljs/clojure/string_test.cljs | 5 ++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 200cd4228..68c8dbf5f 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -24,6 +24,18 @@ (-> (.replace s re-surrogate-pair "$2$1") (.. (split "") (reverse) (join "")))) +(defn- replace-all + [s re replacement] + (.replace s (js/RegExp. (.-source re) "g") replacement)) + +(defn- replace-with + [f] + (fn [& args] + (let [matches (drop-last 2 args)] + (if (= (count matches) 1) + (f (first matches)) + (f (vec matches)))))) + (defn replace "Replaces all instance of match with replacement in s. match/replacement can be: @@ -36,7 +48,9 @@ (.replace s (js/RegExp. (gstring/regExpEscape match) "g") replacement) (instance? js/RegExp match) - (.replace s (js/RegExp. (.-source match) "g") replacement) + (if (string? replacement) + (replace-all s match replacement) + (replace-all s match (replace-with replacement))) :else (throw (str "Invalid match arg: " match)))) diff --git a/src/test/cljs/clojure/string_test.cljs b/src/test/cljs/clojure/string_test.cljs index aae1254e2..981ce30d6 100644 --- a/src/test/cljs/clojure/string_test.cljs +++ b/src/test/cljs/clojure/string_test.cljs @@ -13,8 +13,11 @@ (testing "Testing string replace" (is (= "faabar" (s/replace "foobar" \o \a))) (is (= "barbarbar" (s/replace "foobarfoo" "foo" "bar"))) + (is (= "foobarfoo" (s/replace "foobarfoo" #"ooo" s/upper-case))) (is (= "FOObarFOO" (s/replace "foobarfoo" #"foo" s/upper-case))) - (is (= "barbar)foo" (s/replace "foo(bar)foo" "foo(" "bar")))) + (is (= "barbar)foo" (s/replace "foo(bar)foo" "foo(" "bar"))) + (is (= "FOO-ObarFOO-O" + (s/replace "foobarfoo" #"f(o)o" (fn [[m g1]] (s/upper-case (str m "-" g1))))))) (testing "Testing string join" (is (= "" (s/join nil))) From e55493f2e5c61d1266a9673051adec1cdbae6dbf Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 27 Aug 2015 15:56:12 -0400 Subject: [PATCH 1578/4033] goog-define docstring typo --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 9eba4c212..a79716f7b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -698,7 +698,7 @@ (ns your-app.core) (goog-define DEBUG! false) ;; can be overridden with - :closure-defines {\"your-app.core.DEBUG_BANG_\" true} + :closure-defines {\"your_app.core.DEBUG_BANG_\" true} or :closure-defines {'your-app.core/DEBUG! true}" [sym default] From c5e088786acbdfa5074199f1c5baa9fa7c30f5a6 Mon Sep 17 00:00:00 2001 From: Nikita Prokopov Date: Mon, 24 Aug 2015 22:09:15 +0600 Subject: [PATCH 1579/4033] CLJS-1432 '$ and '. symbols collision under advanced --- src/main/clojure/cljs/analyzer.cljc | 4 +++- src/test/cljs/cljs/core_test.cljs | 6 +++++- src/test/clojure/cljs/analyzer_tests.clj | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2b9a1720d..c9aa77382 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -378,7 +378,9 @@ name (if (keyword? value) (subs (str value) 1) (str value)) - name (-> name (string/replace "-" "_DASH_") (munge) (string/replace "." "$"))] + name (if (= "." name) + "_DOT_" + (-> name (string/replace "-" "_DASH_") (munge) (string/replace "." "$")))] (symbol (str prefix name)))) (defn- register-constant! diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 157815516..415548299 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -306,7 +306,11 @@ (is (= :a (keyword "a"))) (is (= :a (keyword 'a))) (is (= :a/b (keyword 'a 'b))) - (is (= :a (keyword :a))))) + (is (= :a (keyword :a)))) + + (testing "Testing name munging CLJS-1432" + (is (not= :$ :.)) + (is (not= '$ '.)))) (deftest test-map-operations (testing "Test basic map collection operations" diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index ba6a8df9c..d82464e86 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -342,6 +342,12 @@ ;; munge turns - into _, must preserve the dash first (is (not= (a/gen-constant-id :test-kw) (a/gen-constant-id :test_kw)))) + +(deftest test-symbols-munge-cljs-1432 + (is (not= (a/gen-constant-id :$) + (a/gen-constant-id :.))) + (is (not= (a/gen-constant-id '$) + (a/gen-constant-id '.)))) ;; Constants (deftest test-constants From 202bb804b653acda90c0c6bf2f26aa7e2355b95a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Aug 2015 04:13:49 -0400 Subject: [PATCH 1580/4033] fix macro doc regression caused by fcd158 --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c9aa77382..52ea0c313 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -558,7 +558,8 @@ (throw err#) (throw (error ~env (.getMessage err#) err#))))))) -(def implicit-nses '#{cljs.core goog goog.object goog.string goog.array Math}) +;; namespaces implicit to the inclusion of cljs.core +(def implicit-nses '#{goog goog.object goog.string goog.array Math}) (defn implicit-import? #?(:cljs {:tag boolean}) @@ -619,7 +620,8 @@ "Given env, an analysis environment, and ns-sym, a symbol identifying a namespace, confirm that the namespace exists. Warn if not found." [env ns-sym] - (when (and (nil? (get implicit-nses ns-sym)) + (when (and (not= 'cljs.core ns-sym) + (nil? (get implicit-nses ns-sym)) (nil? (get (-> env :ns :requires) ns-sym)) ;; something else may have loaded the namespace, i.e. load-file (nil? (gets @env/*compiler* ::namespaces ns-sym)) From 961a25f76021164d0ba55a9c4a5cbc3809b0c61f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Aug 2015 04:43:21 -0400 Subject: [PATCH 1581/4033] regression test for macro docs --- src/test/clojure/cljs/repl_tests.clj | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/repl_tests.clj b/src/test/clojure/cljs/repl_tests.clj index a4f1dd567..e1147acd4 100644 --- a/src/test/clojure/cljs/repl_tests.clj +++ b/src/test/clojure/cljs/repl_tests.clj @@ -1,11 +1,23 @@ (ns cljs.repl-tests (:require [clojure.java.io :as io] [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] [cljs.env :as env] [cljs.repl :as repl] - [cljs.repl.rhino :as rhino]) + [cljs.repl.rhino :as rhino] + [cljs.compiler :as comp]) (:use clojure.test)) +(def st (env/default-compiler-env)) + +(env/with-compiler-env st + (ana/analyze-file "cljs/core.cljs") + (ana/load-core)) + +(deftest test-doc + (env/with-compiler-env st + (is (string? (:doc (ana-api/resolve {} '->)))))) + #_(deftest file-info (let [repl-env (rhino/repl-env) compiler-env (env/default-compiler-env) From 1e626ddeeebb748701e113e60032f244209de589 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 29 Aug 2015 08:18:34 -0400 Subject: [PATCH 1582/4033] CLJS-1434: clojure.walk no longer preserves meta on vectors Remove redundant IMapEntry case which was capturing vectors and not preserving meta. Instead rely on coll? --- src/main/cljs/clojure/walk.cljs | 12 +++++------- src/test/cljs/clojure/walk_test.cljs | 9 ++++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/clojure/walk.cljs b/src/main/cljs/clojure/walk.cljs index 817b07526..62a9d3a80 100644 --- a/src/main/cljs/clojure/walk.cljs +++ b/src/main/cljs/clojure/walk.cljs @@ -43,13 +43,11 @@ the sorting function."} {:added "1.1"} [inner outer form] (cond - (list? form) (outer (apply list (map inner form))) - (satisfies? IMapEntry form) (outer (vec (map inner form))) - (seq? form) (outer (doall (map inner form))) - (satisfies? IRecord form) - (outer (reduce (fn [r x] (conj r (inner x))) form form)) - (coll? form) (outer (into (empty form) (map inner form))) - :else (outer form))) + (list? form) (outer (apply list (map inner form))) + (seq? form) (outer (doall (map inner form))) + (record? form) (outer (reduce (fn [r x] (conj r (inner x))) form form)) + (coll? form) (outer (into (empty form) (map inner form))) + :else (outer form))) (defn postwalk "Performs a depth-first, post-order traversal of form. Calls f on diff --git a/src/test/cljs/clojure/walk_test.cljs b/src/test/cljs/clojure/walk_test.cljs index 4c4d35e80..3d708c4db 100644 --- a/src/test/cljs/clojure/walk_test.cljs +++ b/src/test/cljs/clojure/walk_test.cljs @@ -19,4 +19,11 @@ (w/prewalk #(if (keyword? %) (str %) %) (Rec1. 1))) "Mirror Clojure behavior"))) - +(deftest test-preserves-meta + (testing "Test preserves meta" + (is (= (-> (w/prewalk identity [1 (with-meta [1 2] {:foo 3})]) + (nth 1) meta) + {:foo 3})) + (is (= (-> (w/postwalk identity [1 (with-meta [1 2] {:foo 3})]) + (nth 1) meta) + {:foo 3})))) From ed01ddf9a87eb67aa50feacd72e8aa66083cdbfc Mon Sep 17 00:00:00 2001 From: Chris Truter Date: Wed, 12 Aug 2015 15:10:11 +0200 Subject: [PATCH 1583/4033] CLJS-1435: self-host: lexical scope is broken --- src/main/clojure/cljs/compiler.cljc | 11 +++++++---- src/test/self/self_host/test.cljs | 12 ++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index adba6135a..ac5d6d939 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -55,6 +55,11 @@ (some #{(str name)} (ns-first-segments)) (inc d) :else d)))) +(defn hash-scope [s] + #?(:clj (System/identityHashCode s) + :cljs (hash-combine (-hash ^not-native (:name s)) + (shadow-depth s)))) + (declare munge) (defn fn-self-name [{:keys [name info] :as name-var}] @@ -86,8 +91,7 @@ (fn-self-name s) ;; Unshadowing (let [depth (shadow-depth s) - code #?(:clj (System/identityHashCode s) - :cljs (-hash ^not-native name)) + code (hash-scope s) renamed (get *lexical-renames* code) name (cond (true? field) (str "self__." name) @@ -862,8 +866,7 @@ (map (fn [binding] (let [name (:name binding)] - (vector #?(:clj (System/identityHashCode binding) - :cljs (-hash ^not-native name)) + (vector (hash-scope binding) (gensym (str name "-"))))) bindings)))] (doseq [{:keys [init] :as binding} bindings] diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index da40ad9bd..a754ba1e0 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -93,7 +93,7 @@ (deftest test-eval-str (async done - (let [l (latch 5 done)] + (let [l (latch 6 done)] (cljs/eval-str st "(+ 1 1)" nil {:eval node-eval} (fn [{:keys [error value]}] @@ -139,6 +139,14 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (== 3 (js/cljs.user.foo 1 2))) + (inc! l))) + (cljs/eval-str st "(def foo (let [x 1] (let [x (inc x)] x)))" nil + {:eval node-eval + :context :statement + :def-emits-var true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== 2 js/cljs.user.foo)) (inc! l)))))) #_(deftest test-eval-str-with-require @@ -178,4 +186,4 @@ (set! *main-cli-fn* -main) (comment - ) \ No newline at end of file + ) From c96bf68b084b4f82f144ecfe092bdcd5bb2fcf71 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 29 Aug 2015 11:54:10 -0400 Subject: [PATCH 1584/4033] add self host test dir to project.clj --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 17873bac1..0b3ce4a6a 100644 --- a/project.clj +++ b/project.clj @@ -7,7 +7,7 @@ :jvm-opts ^:replace ["-Xmx512m" "-server"] :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] - :test-paths ["src/test/clojure" "src/test/cljs"] + :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] From 3636a80c9b683cdc1190bd60910bf805e6ab22c0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 29 Aug 2015 12:26:28 -0400 Subject: [PATCH 1585/4033] CLJS-1400: doseq does not behave as expected add test. fix `with-out-str` for bootstrapped --- src/main/clojure/cljs/core.cljc | 2 +- src/test/self/self_host/test.cljs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a79716f7b..acb857d6e 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2560,7 +2560,7 @@ on a fresh StringBuffer. Returns the string created by any nested printing calls." [& body] - `(let [sb# (goog.string.StringBuffer.)] + `(let [sb# (js/goog.string.StringBuffer.)] (binding [cljs.core/*print-newline* true cljs.core/*print-fn* (fn [x#] (.append sb# x#))] ~@body) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index a754ba1e0..0e1c51d6e 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -93,7 +93,7 @@ (deftest test-eval-str (async done - (let [l (latch 6 done)] + (let [l (latch 7 done)] (cljs/eval-str st "(+ 1 1)" nil {:eval node-eval} (fn [{:keys [error value]}] @@ -146,7 +146,12 @@ :def-emits-var true} (fn [{:keys [error value]}] (is (nil? error)) - (is (== 2 js/cljs.user.foo)) + (inc! l))) + (cljs/eval-str st "(with-out-str (doseq [x [1 2]] (println x)))" nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (= "1\n2\n" value)) (inc! l)))))) #_(deftest test-eval-str-with-require From 033c5acf3d42e485221b47f300f69bb6cebda750 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 29 Aug 2015 12:39:02 -0400 Subject: [PATCH 1586/4033] Node.js REPL should set *target* --- src/main/clojure/cljs/repl/node.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index 3d0eb860b..f74204f57 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -180,6 +180,7 @@ ;; redef goog.require to track loaded libs (repl/evaluate-form repl-env env "" '(do + (set! *target* "nodejs") (set! *loaded-libs* #{"cljs.core"}) (set! (.-require js/goog) (fn [name reload] From a1ef3a1116d4e0a37b60dc07b158adfdc6762122 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 1 Sep 2015 18:25:01 -0400 Subject: [PATCH 1587/4033] update corelib.org --- devnotes/corelib.org | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/devnotes/corelib.org b/devnotes/corelib.org index 071649d35..600fb91ab 100644 --- a/devnotes/corelib.org +++ b/devnotes/corelib.org @@ -5,7 +5,7 @@ * DONE *3 * *agent* * *allow-unresolved-vars* -* *assert* +* DONE *assert* * *clojure-version* * *command-line-args* * *compile-files* @@ -20,10 +20,10 @@ * *ns* * *out* * *print-dup* -* *print-length* -* *print-level* -* *print-meta* -* *print-readably* +* DONE *print-length* +* DONE *print-level* +* DONE *print-meta* +* DONE *print-readably* * *read-eval* * *source-path* * TODO *unchecked-math* @@ -171,7 +171,7 @@ does what? * DONE defmulti * DONE defn * DONE defn- -* defonce +* DONE defonce * DONE defprotocol * DONE defrecord * defstruct @@ -337,7 +337,7 @@ does what? * DONE min * DONE min-key * DONE mod -* munge +* DONE munge * DONE name * DONE namespace * namespace-munge @@ -555,7 +555,7 @@ as macro * DONE vals * var-get * var-set -* var? +* DONE var? * DONE vary-meta * DONE vec * DONE vector @@ -573,7 +573,7 @@ as macro * TODO with-local-vars * DONE with-meta * with-open -* with-out-str +* DONE with-out-str * with-precision * with-redefs * with-redefs-fn From 1b7390450243693d0b24e8d3ad085c6da4eef204 Mon Sep 17 00:00:00 2001 From: oakes Date: Tue, 1 Sep 2015 16:15:49 -0400 Subject: [PATCH 1588/4033] CLJS-1440: Allow eval to work inside a web worker Use goog/global instead of js/window so the correct global is always used. --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 23c1859f8..4ab9525b5 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10125,7 +10125,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." "nodejs" (if ^boolean js/COMPILED (js/eval munged-ns) (find-ns-obj* js/global segs)) - "default" (find-ns-obj* js/window segs) + "default" (find-ns-obj* goog/global segs) (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) (defn ns-interns* [sym] From 1b8b4929c47b4d875d2b1c143f25dbfb462138c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 3 Sep 2015 07:17:40 -0400 Subject: [PATCH 1589/4033] bump Google Closure --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 54e1400ac..b82560ec4 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20150729 + v20150901 org.clojure diff --git a/project.clj b/project.clj index 0b3ce4a6a..d11c9d88e 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] [org.clojure/google-closure-library "0.0-20150805-acd8b553"] - [com.google.javascript/closure-compiler "v20150729"] + [com.google.javascript/closure-compiler "v20150901"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 7173b3901..4013a073a 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.7.0" -CLOSURE_RELEASE="20150729" +CLOSURE_RELEASE="20150901" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150805-acd8b553" RHINO_RELEASE="1_7R5" From ce46b80f9fa1d5ae134f36dba254a8bb5d6004a1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 2 Sep 2015 16:47:44 -0400 Subject: [PATCH 1590/4033] CLJS-1436: self-host: Doesn't load dep ns when loading own cache This revison first loads any :require-macros deps followed by any :requires and :imports deps before executing the original code associated with a :lang :js callback. The revision honors the async callback structure, pushing the original code into the innermost callback position. --- src/main/cljs/cljs/js.cljs | 85 ++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index b9118e3ec..de0ce706c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -158,6 +158,49 @@ (def *loaded* (atom #{})) +(defn- run-async! + "Like cljs.core/run!, but for an async procedure, and with the + ability to break prior to processing the entire collection. + + Chains successive calls to the supplied procedure for items in + the collection. The procedure should accept an item from the + collection and a callback of one argument. If the break? predicate, + when applied to the procedure callback value, yields a truthy + result, terminates early calling the supplied cb with the callback + value. Otherwise, when complete, calls cb with nil." + [proc coll break? cb] + (if (seq coll) + (proc (first coll) + (fn [res] + (if (break? res) + (cb res) + (run-async! proc (rest coll) break? cb)))) + (cb nil))) + +(declare require) + +(defn- process-deps + [bound-vars names opts cb] + (run-async! (fn [name cb] + (require bound-vars name nil opts cb)) + names + :error + cb)) + +(defn- process-macros-deps + [bound-vars cache opts cb] + (process-deps bound-vars + (distinct (vals (:require-macros cache))) + (assoc opts :macros-ns true) + cb)) + +(defn- process-libs-deps + [bound-vars cache opts cb] + (process-deps bound-vars + (distinct (concat (vals (:requires cache)) (vals (:imports cache)))) + (dissoc opts :macros-ns) + cb)) + (defn require ([name cb] (require name nil cb)) @@ -201,23 +244,31 @@ (do (swap! *loaded* conj name) (cb {:value true}))))) - :js (let [res (try - ((:*eval-fn* bound-vars) resource) - (when cache - (load-analysis-cache! - (:*compiler* bound-vars) name cache)) - (when source-map - (load-source-map! - (:*compiler* bound-vars) name source-map)) - (catch :default cause - (wrap-error - (ana/error env - (str "Could not require " name) cause))))] - (if (:error res) - (cb res) - (do - (swap! *loaded* conj name) - (cb {:value true})))) + :js (process-macros-deps bound-vars cache opts + (fn [res] + (if (:error res) + (cb res) + (process-libs-deps bound-vars cache opts + (fn [res] + (if (:error res) + (cb res) + (let [res (try + ((:*eval-fn* bound-vars) resource) + (when cache + (load-analysis-cache! + (:*compiler* bound-vars) name cache)) + (when source-map + (load-source-map! + (:*compiler* bound-vars) name source-map)) + (catch :default cause + (wrap-error + (ana/error env + (str "Could not require " name) cause))))] + (if (:error res) + (cb res) + (do + (swap! *loaded* conj name) + (cb {:value true})))))))))) (cb (wrap-error (ana/error env (str "Invalid :lang specified " lang ", only :clj or :js allowed")))))) From 371d5907cc9c0fb5fd8679f09e55cc3e4027287f Mon Sep 17 00:00:00 2001 From: Tom Marble Date: Fri, 4 Sep 2015 08:16:53 -0500 Subject: [PATCH 1591/4033] CLJS-1441: Add clojure.string functions for portability to cljs as for clj This is a direct port of the recent changes to Clojure from the ticket http://dev.clojure.org/jira/browse/CLJ-1449 with the exception that metadata and (non boolean) type hinting has been elided. --- src/main/cljs/clojure/string.cljs | 43 ++++++++++++++++++++ src/test/cljs/clojure/string_test.cljs | 54 +++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 68c8dbf5f..5989c4fd8 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -213,3 +213,46 @@ (.append buffer (str replacement)) (.append buffer ch)) (recur (inc index))))))) + +(defn index-of + "Return index of value (string or char) in s, optionally searching + forward from from-index or nil if not found." + ([s value] + (let [result (.indexOf s value)] + (if (neg? result) + nil + result))) + ([s value from-index] + (let [result (.indexOf s value from-index)] + (if (neg? result) + nil + result)))) + +(defn last-index-of + "Return last index of value (string or char) in s, optionally + searching backward from from-index or nil if not found." + ([s value] + (let [result (.lastIndexOf s value)] + (if (neg? result) + nil + result))) + ([s value from-index] + (let [result (.lastIndexOf s value from-index)] + (if (neg? result) + nil + result)))) + +(defn ^boolean starts-with? + "True if s starts with substr." + [s substr] + (gstring/startsWith s substr)) + +(defn ^boolean ends-with? + "True if s ends with substr." + [s substr] + (gstring/endsWith s substr)) + +(defn ^boolean includes? + "True if s includes substr." + [s substr] + (gstring/contains s substr)) diff --git a/src/test/cljs/clojure/string_test.cljs b/src/test/cljs/clojure/string_test.cljs index 981ce30d6..e9ffaa4da 100644 --- a/src/test/cljs/clojure/string_test.cljs +++ b/src/test/cljs/clojure/string_test.cljs @@ -9,7 +9,7 @@ (is (= "tab" (s/reverse "bat"))) (is (= "c\uD834\uDD1Ea" (s/reverse "a\uD834\uDD1Ec"))) ;; U+1D11E MUSICAL SYMBOL G CLEF ) - + (testing "Testing string replace" (is (= "faabar" (s/replace "foobar" \o \a))) (is (= "barbarbar" (s/replace "foobarfoo" "foo" "bar"))) @@ -87,7 +87,57 @@ (is (= "foo" (s/trim-newline "foo\r\n"))) (is (= "foo" (s/trim-newline "foo"))) (is (= "foo\r " (s/trim-newline "foo\r "))) - (is (= "" (s/trim-newline ""))))) + (is (= "" (s/trim-newline "")))) + + (testing "Testing string trim-newline" + (is (= "foo" (s/trim-newline "foo\n"))) + (is (= "foo" (s/trim-newline "foo\r\n"))) + (is (= "foo" (s/trim-newline "foo"))) + (is (= "foo\r " (s/trim-newline "foo\r "))) + (is (= "" (s/trim-newline "")))) + + (testing "Testing string index-of" + (let [sb "tacos"] + (is (= 2 (s/index-of sb "c"))) + (is (= 2 (s/index-of sb \c))) + (is (= 1 (s/index-of sb "ac"))) + (is (= 3 (s/index-of sb "o" 2))) + (is (= 3 (s/index-of sb \o 2))) + (is (= 3 (s/index-of sb "o" -100))) + (is (= nil (s/index-of sb "z"))) + (is (= nil (s/index-of sb \z))) + (is (= nil (s/index-of sb "z" 2))) + (is (= nil (s/index-of sb \z 2))) + (is (= nil (s/index-of sb "z" 100)) + (is (= nil (s/index-of sb "z" -10)))))) + + (testing "Testing string last-index-of" + (let [sb "banana"] + (is (= 4 (s/last-index-of sb "n"))) + (is (= 4 (s/last-index-of sb \n))) + (is (= 3 (s/last-index-of sb "an"))) + (is (= 4 (s/last-index-of sb "n" ))) + (is (= 4 (s/last-index-of sb "n" 5))) + (is (= 4 (s/last-index-of sb \n 5))) + (is (= 4 (s/last-index-of sb "n" 500))) + (is (= nil (s/last-index-of sb "z"))) + (is (= nil (s/last-index-of sb "z" 1))) + (is (= nil (s/last-index-of sb \z 1))) + (is (= nil (s/last-index-of sb "z" 100))) + (is (= nil (s/last-index-of sb "z" -10))))) + + (testing "Testing string starts-with?" + (is (s/starts-with? "clojure west" "clojure")) + (is (not (s/starts-with? "conj" "clojure")))) + + (testing "Testing string ends-with?" + (is (s/ends-with? "Clojure West" "West")) + (is (not (s/ends-with? "Conj" "West")))) + + (testing "Testing string includes?" + (let [sb "Clojure Applied Book"] + (is (s/includes? sb "Applied")) + (is (not (s/includes? sb "Living")))))) (comment From 27046fc69cc720a14d3319d803766a9f651ba8e9 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 5 Sep 2015 15:27:28 -0400 Subject: [PATCH 1592/4033] CLJS-1442: self-host: Typos in cb docstring cljs.js/eval Change to "will" Change to use phrase "a key :foo" --- src/main/cljs/cljs/js.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index de0ce706c..3347129af 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -570,8 +570,8 @@ cb (function) callback, will be invoked with a map. If successful the map will contain - a :value key with the result of evalution. If unsuccessful the map wil - contain a :error key with an ex-info instance describing the cause of + a key :value with the result of evalution. If unsuccessful the map will + contain a key :error with an ex-info instance describing the cause of failure." ([state form cb] (eval state form nil cb)) From dc999f01d569f7b5a869e2c65408fdb3676c2936 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 9 Sep 2015 14:37:41 -0400 Subject: [PATCH 1593/4033] expose all lang-in/out Closure options --- src/main/clojure/cljs/closure.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b948e0615..32aa0d092 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -177,12 +177,20 @@ (when (contains? opts :language-in) (case (:language-in opts) + :no-transpile (.setLanguageIn compiler-options CompilerOptions$LanguageMode/NO_TRANSPILE) + :ecmascript6 (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6) + :ecmascript6-strict (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT) + :ecmascript6-typed (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED) :ecmascript5 (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5) :ecmascript5-strict (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT) :ecmascript3 (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT3))) (when (contains? opts :language-out) (case (:language-out opts) + :no-transpile (.setLanguageOut compiler-options CompilerOptions$LanguageMode/NO_TRANSPILE) + :ecmascript6 (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6) + :ecmascript6-strict (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT) + :ecmascript6-typed (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED) :ecmascript5 (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5) :ecmascript5-strict (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT) :ecmascript3 (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT3))) From cc953d4be7b4a256fd5eae783f9106a2929a4126 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 9 Sep 2015 18:14:58 -0400 Subject: [PATCH 1594/4033] need to pass along :language-in and :language-out to module processing --- src/main/clojure/cljs/closure.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 32aa0d092..34c08ab65 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1283,10 +1283,10 @@ (defn make-convert-js-module-options [opts] (-> opts - (select-keys [:closure-warnings - :pretty-print - :closure-extra-annotations]) - (set-options (CompilerOptions.)))) + (select-keys + [:closure-warnings :closure-extra-annotations :pretty-print + :language-in :language-out]) + (set-options (CompilerOptions.)))) (util/compile-if can-convert-commonjs? (defmethod convert-js-module :commonjs [ijs opts] From 2fa41e7969d22f72876122d2250baf4ef936e6e9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 19 Sep 2015 21:06:33 -0400 Subject: [PATCH 1595/4033] CLJS-1455: high resolution `time` Added `cljs.core/system-time`, `time` macro now uses this instead --- src/main/cljs/cljs/core.cljs | 9 +++++++++ src/main/clojure/cljs/core.cljc | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4ab9525b5..e6209ff02 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -333,6 +333,15 @@ (def DEMUNGE_PATTERN nil) +(defn system-time + "Returns highest resolution time offered by host in milliseconds." + [] + (cond + (exists? js/performance) (.now js/performance) + (exists? js/process) (let [t (.hrtime js/process)] + (/ (+ (* (aget t 0) 1e9) (aget t 1)) 1e6)) + :else (.getTime (js/Date.)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; arrays ;;;;;;;;;;;;;;;; (defn ^array make-array diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index acb857d6e..5f4c98781 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2505,9 +2505,11 @@ (core/defmacro time "Evaluates expr and prints the time it took. Returns the value of expr." [expr] - `(let [start# (.getTime (js/Date.)) + `(let [start# (system-time) ret# ~expr] - (prn (cljs.core/str "Elapsed time: " (- (.getTime (js/Date.)) start#) " msecs")) + (prn (cljs.core/str "Elapsed time: " + (.toFixed (- (system-time) start#) 6) + " msecs")) ret#)) (core/defmacro simple-benchmark From 21914ced1b22a40590d44227b6a10c670a306082 Mon Sep 17 00:00:00 2001 From: Bo Jeanes Date: Mon, 21 Sep 2015 23:11:48 +1000 Subject: [PATCH 1596/4033] CLJS-1457 Munge unicode kws/syms constant names --- src/main/clojure/cljs/analyzer.cljc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 52ea0c313..070ac0f51 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -380,7 +380,12 @@ (str value)) name (if (= "." name) "_DOT_" - (-> name (string/replace "-" "_DASH_") (munge) (string/replace "." "$")))] + (-> name + (string/replace "-" "_DASH_") + (munge) + (string/replace "." "$") + (string/replace #"(?i)[^a-z0-9$_]" + #(str "_u" (format "%04x" (int (first %))) "_"))))] (symbol (str prefix name)))) (defn- register-constant! From 384796130d0fa760187020470171ddb4bdcf00f8 Mon Sep 17 00:00:00 2001 From: Bo Jeanes Date: Mon, 21 Sep 2015 23:26:40 +1000 Subject: [PATCH 1597/4033] CLJS-1457 Add test for unicode symbol munge --- src/test/clojure/cljs/analyzer_tests.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index d82464e86..cef32340d 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -348,6 +348,11 @@ (a/gen-constant-id :.))) (is (not= (a/gen-constant-id '$) (a/gen-constant-id '.)))) + +(deftest test-unicode-munging-cljs-1457 + (is (= (a/gen-constant-id :C♯) 'cst$kw$C_u266f_) + (= (a/gen-constant-id 'C♯) 'cst$sym$C_u266f_))) + ;; Constants (deftest test-constants From c72e9c52156b3b348aa66857830c2ed1f0179e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Pablo=20Fern=C3=A1ndez?= Date: Thu, 17 Sep 2015 15:42:32 +0100 Subject: [PATCH 1598/4033] When targeting nodejs with no optimizations, add headers the same way as with optimizations. This is to fix bug http://dev.clojure.org/jira/browse/CLJS-1454. The counterpart for optimized builds can be found here: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/closure.clj#L1723 --- src/main/clojure/cljs/closure.clj | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 34c08ab65..d6841ad58 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1129,6 +1129,8 @@ (defn output-deps-file [opts sources] (output-one-file opts (deps-file opts sources))) +(declare foreign-deps-str add-header add-source-map-link) + (defn output-main-file [opts] (let [asset-path (or (:asset-path opts) (util/output-directory opts)) @@ -1136,16 +1138,17 @@ (case (:target opts) :nodejs (output-one-file opts - (str "var path = require(\"path\");\n" - "try {\n" - " require(\"source-map-support\").install();\n" - "} catch(err) {\n" - "}\n" - "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" - "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" - "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - "goog.require(\"" (comp/munge (:main opts)) "\");\n" - "goog.require(\"cljs.nodejscli\");\n")) + (add-header opts + (str "var path = require(\"path\");\n" + "try {\n" + " require(\"source-map-support\").install();\n" + "} catch(err) {\n" + "}\n" + "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" + "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" + "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "goog.require(\"" (comp/munge (:main opts)) "\");\n" + "goog.require(\"cljs.nodejscli\");\n"))) (output-one-file opts (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "if(typeof goog == \"undefined\") document.write('');\n" @@ -1153,8 +1156,6 @@ "document.write('');\n"))))) -(declare foreign-deps-str add-header add-source-map-link) - (defn output-modules "Given compiler options, original IJavaScript sources and a sequence of module name and module description tuples output module sources to disk. From 5c8a7870584e589b98f38b66f3ea4c0f38c5a0a2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 5 Oct 2015 12:39:48 -0400 Subject: [PATCH 1599/4033] bump Closure Compiler --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index b82560ec4..9d23380e4 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20150901 + v20150920 org.clojure diff --git a/project.clj b/project.clj index d11c9d88e..df4d22f99 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] [org.clojure/google-closure-library "0.0-20150805-acd8b553"] - [com.google.javascript/closure-compiler "v20150901"] + [com.google.javascript/closure-compiler "v20150920"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 4013a073a..94ac54a0c 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.7.0" -CLOSURE_RELEASE="20150901" +CLOSURE_RELEASE="20150920" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150805-acd8b553" RHINO_RELEASE="1_7R5" From 44709936ef6f53befeabbbfd0cf23932d96abe8b Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 5 Oct 2015 13:17:52 -0400 Subject: [PATCH 1600/4033] CLJS-1462: self-host unit tests regressed with CLJS-1457 Fix self hosting regression caused by CLJS-1457. `int` to get character code and `format` is meaningless under ClojureScript --- src/main/clojure/cljs/analyzer.cljc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 070ac0f51..fd9caff92 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -367,6 +367,21 @@ `(binding [*cljs-warning-handlers* ~handlers] ~@body))) +(defn- repeat-char [c n] + (loop [ret c n n] + (if (pos? n) + (recur (str ret c) (dec n)) + ret))) + +(defn- hex-format [s pad] + #?(:clj (str "_u" (format (str "%0" pad "x") (int (first s))) "_") + :cljs (let [hex (.toString (.charCodeAt s 0) 16) + len (. hex -length) + hex (if (< len pad) + (str (repeat-char "0" (- pad len)) hex) + hex)] + (str "_u" hex "_")))) + (defn gen-constant-id [value] (let [prefix (cond (keyword? value) "cst$kw$" @@ -384,8 +399,7 @@ (string/replace "-" "_DASH_") (munge) (string/replace "." "$") - (string/replace #"(?i)[^a-z0-9$_]" - #(str "_u" (format "%04x" (int (first %))) "_"))))] + (string/replace #"(?i)[^a-z0-9$_]" #(hex-format % 4))))] (symbol (str prefix name)))) (defn- register-constant! From c7e49a972638c620fe36bb0367297b2f0328171e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 2 Oct 2015 18:07:46 -0400 Subject: [PATCH 1601/4033] CLJS-1449: self-host: Error if :require-macros with :as The k argument to cljs.js/load-macros should be either a keyword or nil. The current implementation employs an or construct that can set k to be the value false, and when ultimately passed back into load-macros this false value can inadvertently be used as a function. This patch a) revises the or construct to terminate wil a nil, ensuring k will be be set to nil where previously it would have been set to false b) revises a spot where k is used to look up a value in a map, reversing things so that the map is instead used as the (non-nil) fn --- src/main/cljs/cljs/js.cljs | 5 +++-- src/test/self/self_host/test.cljs | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 3347129af..b3f3afeac 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -353,9 +353,10 @@ (defn- load-macros [bound-vars k macros reload reloads opts cb] (if (seq macros) (let [nsym (first (vals macros)) - k (or (k reload) + k (or (reload k) (get-in reloads [k nsym]) - (and (= nsym name) (:*reload-macros* bound-vars) :reload))] + (and (= nsym name) (:*reload-macros* bound-vars) :reload) + nil)] (require bound-vars nsym k (-> opts (assoc :macros-ns true) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 0e1c51d6e..2efc767b4 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -154,6 +154,26 @@ (is (= "1\n2\n" value)) (inc! l)))))) +(deftest test-eval-str-with-require-macros + (async done + (let [l (latch 2 done)] + (cljs/eval-str st + "(ns cljs.user (:require-macros [cljs.user.macros]))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns cljs.user.macros)"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (inc! l))) + (cljs/eval-str st + "(ns cljs.user (:require-macros [cljs.user.macros :as cljs-user-macros]))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns cljs.user.macros)"}))} + (fn [{:keys [error value]}] + (is (nil? error)) + (inc! l)))))) + #_(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] From d8a47d9e8ef655a71597a0512a4489e0002fca39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20De=20Serres?= Date: Tue, 22 Sep 2015 17:30:24 +0200 Subject: [PATCH 1602/4033] CLJS-1456: require forms at REPL can corrupt the current namespace Wrap repl special functions within save/restore compiler state. --- src/main/clojure/cljs/repl.cljc | 133 ++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 56 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 1c452a1f5..cfe1ad660 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -617,6 +617,26 @@ (= ns ana/*cljs-ns*)))) specs)) +(defn- wrap-self + "Takes a self-ish fn and returns it wrapped with exception handling. + Compiler state is restored if self-ish fn fails." + [f] + (fn g + ([a b c] + (g a b c nil)) + ([a b c d] + (let [backup-comp @env/*compiler*] + (try + (apply f [a b c d]) + (catch #?(:clj Exception :cljs js/Error) e ;;Exception + (reset! env/*compiler* backup-comp) + (throw e))))))) + +(defn- wrap-special-fns + [wfn fns] + "Wrap wfn around all (fn) values in fns hashmap." + (into {} (for [[k v] fns] [k (wfn v)]))) + (def default-special-fns (let [load-file-fn (fn self @@ -637,62 +657,63 @@ (-evaluate repl-env "" 1 (str "goog.provide('" (comp/munge ns-name) "');"))) (set! ana/*cljs-ns* ns-name)))] - {'in-ns in-ns-fn - 'clojure.core/in-ns in-ns-fn - 'require - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (let [is-self-require? (self-require? specs) - [target-ns restore-ns] - (if-not is-self-require? - [ana/*cljs-ns* nil] - ['cljs.user ana/*cljs-ns*])] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~target-ns - (:require ~@(-> specs canonicalize-specs decorate-specs))) - {:merge true :line 1 :column 1}) - identity opts) - (when is-self-require? - (set! ana/*cljs-ns* restore-ns))))) - 'require-macros - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require-macros ~@(-> specs canonicalize-specs decorate-specs))) - {:merge true :line 1 :column 1}) - identity opts))) - 'import - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:import - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) - {:merge true :line 1 :column 1}) - identity opts))) - 'load-file load-file-fn - 'clojure.core/load-file load-file-fn - 'load-namespace - (fn self - ([repl-env env form] - (self env repl-env form nil)) - ([repl-env env [_ ns :as form] opts] - (load-namespace repl-env ns opts)))})) + (wrap-special-fns wrap-self + {'in-ns in-ns-fn + 'clojure.core/in-ns in-ns-fn + 'require + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (let [is-self-require? (self-require? specs) + [target-ns restore-ns] + (if-not is-self-require? + [ana/*cljs-ns* nil] + ['cljs.user ana/*cljs-ns*])] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~target-ns + (:require ~@(-> specs canonicalize-specs decorate-specs))) + {:merge true :line 1 :column 1}) + identity opts) + (when is-self-require? + (set! ana/*cljs-ns* restore-ns))))) + 'require-macros + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:require-macros ~@(-> specs canonicalize-specs decorate-specs))) + {:merge true :line 1 :column 1}) + identity opts))) + 'import + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:import + ~@(map + (fn [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw))) + specs))) + {:merge true :line 1 :column 1}) + identity opts))) + 'load-file load-file-fn + 'clojure.core/load-file load-file-fn + 'load-namespace + (fn self + ([repl-env env form] + (self env repl-env form nil)) + ([repl-env env [_ ns :as form] opts] + (load-namespace repl-env ns opts)))}))) (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate From 4df0dbdffd5355829e5d1224dd1da4e9194dff97 Mon Sep 17 00:00:00 2001 From: Daniel Compton Date: Tue, 6 Oct 2015 12:48:04 +1300 Subject: [PATCH 1603/4033] CLJS-1464: Add docstrings for transducer arity Add docstrings for the new transducer arity of interpose, distinct, and map-indexed. These docstrings match those found in Clojure. --- src/main/cljs/cljs/core.cljs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e6209ff02..0d7d1556e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3877,7 +3877,8 @@ reduces them without incurring seq initialization" "Returns a lazy sequence consisting of the result of applying f to 0 and the first item of coll, followed by applying f to 1 and the second item in coll, etc, until coll is exhausted. Thus function f should - accept 2 arguments, index and item." + accept 2 arguments, index and item. Returns a stateful transducer when + no collection is provided." ([f] (fn [rf] (let [i (volatile! -1)] @@ -4364,7 +4365,8 @@ reduces them without incurring seq initialization" (concat (map first ss) (apply interleave (map rest ss)))))))) (defn interpose - "Returns a lazy seq of the elements of coll separated by sep" + "Returns a lazy seq of the elements of coll separated by sep. + Returns a stateful transducer when no collection is provided." ([sep] (fn [rf] (let [started (volatile! false)] @@ -8246,7 +8248,8 @@ reduces them without incurring seq initialization" (map #(if-let [e (find smap %)] (second e) %) coll)))) (defn distinct - "Returns a lazy sequence of the elements of coll with duplicates removed" + "Returns a lazy sequence of the elements of coll with duplicates removed. + Returns a stateful transducer when no collection is provided." ([] (fn [rf] (let [seen (volatile! #{})] From 842cc369122b48eccf387c45ff074da7954c67e9 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 7 Oct 2015 21:28:09 -0400 Subject: [PATCH 1604/4033] CLJS-1465: doc for *main-cli-fn* refers to *main-fn* Revise doc to refer to *main-cli-fn*, and also reflow a little. --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0d7d1556e..163368a36 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -242,8 +242,8 @@ (identical? (.-prototype (.-constructor x)) x)) (def - ^{:doc "When compiled for a command-line target, whatever - function *main-fn* is set to will be called with the command-line + ^{:doc "When compiled for a command-line target, whatever function + *main-cli-fn* is set to will be called with the command-line argv as arguments"} *main-cli-fn* nil) From 1440774dcd49af17c105818340a23ccb126d77ee Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Oct 2015 20:57:33 -0400 Subject: [PATCH 1605/4033] fix broken UUID hashing --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 163368a36..58e300e5f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9844,7 +9844,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." IHash (-hash [this] (when (nil? __hash) - (set! __hash (hash-string* uuid))) + (set! __hash (hash uuid))) __hash) IComparable diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 415548299..049227bea 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1690,11 +1690,9 @@ (testing "UUID hashing" (let [id "550e8400-e29b-41d4-a716-446655440000" uuid (cljs.core/uuid id) - expected (goog.string/hashCode id)] + expected (hash id)] (is (= expected (hash uuid))) - ;; checking hash cache - (is (= expected (.-__hash uuid))) - (is (= expected (hash uuid)))))) + (is (= expected (.-__hash uuid)))))) (deftest test-comparable (testing "Testing IComparable" From d071bb52f4279bf4f0433f1229639193a813b53d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 13 Oct 2015 10:53:41 -0400 Subject: [PATCH 1606/4033] 1.7.145 --- README.md | 6 ++--- changes.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 12fa6e79a..b2d21f99b 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 1.7.48 +Latest stable release: 1.7.145 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.7.48"] +[org.clojure/clojurescript "1.7.145"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.7.48 org.clojure clojurescript - 1.7.48 + 1.7.145 ``` diff --git a/changes.md b/changes.md index 16886c385..b7e70c619 100644 --- a/changes.md +++ b/changes.md @@ -1,17 +1,64 @@ +## 1.7.145 + +### Enhancements +* CLJS-1455: high resoluting timing where available +* CLJS-1403: Add updated Windows shell scripts +* CLJS-1017: support :main for :advanced and :simple builds +* CLJS-1409: allow basic type checking of protocols +* CLJS-1404: var resolution for @param and @return +* CLJS-1395: Node.js REPL debug port support + +### Changes +* CLJS-1464: docstrings for transducer arities +* Latest Google Closure Compiler dependency +* Node.js REPL sets *target* +* add cljs.analyzer.api/get-js-index +* add goog.object to list of implicit namespaces +* CLJS-1393: turn *target* into goog-define + +### Fixes +* CLJS-1465: fix *main-cli-fn* doc +* CLJS-1456: bad require forms at REPL can corrupt REPL session +* CLJS-1449: self host :require-macros bug +* CLJS-1462: self host regression +* Add header bits for Node.js under :none +* CLJS-1457: unicode symbol munging +* CLJS-1442: self host, docstring typos +* CLJS-1441: portable clojure.string +* CLJS-1436: self-host, dep ns not loaded +* CLJS-1440: self-host, eval support in Web Workers +* CLJS-1400: self-host, doseq broken +* CLJS-1435: self-host, bad lexical scope +* CLJS-1434: clojure.walk no longer preseves meta +* CLJS-1432: '$ and '. symbol collision under advanced +* CLJS-1304: c.string/replace differs from Clojure +* CLJS-1430: bad code gen for self host .toString method calls +* CLJS-1353: range inconsistent with Clojure +* CLJS-1431: load-file doc output missing arglists +* CLJS-1433: cljs.js/*eval-fn* passed nil :cache +* CLJS-1299: add more support for literals to cljs.reader +* CLJS-1417: cljs.js require macros failures +* CLJS-1416: cljs.util/last-modified leaks files +* CLJS-1481: self host defprotocol regression +* CLJS-1414: only munge @param & @return if type checking +* CLJS-1401: unify runtime & compile UUID hashing +* CLJS-1395: no trailing semicolons after JS comment +* CLJS-1394: reify gensyms can clash + ## 1.7.48 -## Enhancements +### Enhancements * provide goog-define macro to support proper use of goog.define * CLJS-1177: A compiler support for non-Closure transforms (JSX, etc) * CLJS-1296: browser REPL should queue prints before connection then flush after connection * add :dump-core compiler option for cljs.js config * CLJS-1386: Symbols should be added to the constants table -## Changes +### Changes * Bump Closure Compiler dependency * Bump Closure Library dependency -## Fixes +### Fixes * CLJS-1392: cljs.repl/source regression * CLJS-1391: Error when building for target :nodejs * CLJS-1388: Stacktrace element handling for :output-dir w/o file/line/column @@ -20,7 +67,7 @@ ## 1.7.28 -## Enhancements +### Enhancements * New namespace cljs.js provides analysis, compilation, and eval * CLJS-1360: Refactor JS module processing to work with recent Google Closure compiler changes * CLJS-1282: Add a :pprint option to the default reporter in cljs.test @@ -29,7 +76,7 @@ * CLJS-1231: AMD Module Processing * CLJS-1092: CommonJS Module processing -## Changes +### Changes * CLJS-1376: Printing in a tagged literal data form * CLJS-836: Replace seq-based iterators with direct iterator for all non-seq collections that use SeqIterator * CLJS-1367: Expose default-warning-handler and warning-enabled? @@ -42,7 +89,7 @@ * CLJS-1281: Preserve test order * CLJS-934: In the REPL return vars after defs -## Fixes +### Fixes * CLJS-1316 let does not detect invalid binding vector when it contains destructuring * CLJS-1033: take a drop accept nil as n argument * CLJS-1324: Compiler fails to raise warning/error when invoking a keyword without arguments @@ -68,7 +115,7 @@ ## 0.0-3308 -## Changes +### Changes * Clojure 1.7.0-RC1 dependency * CLJS-1292: Add IPrintWithWriter implementation for TaggedLiteral * add cljs.core/random-uuid @@ -76,7 +123,7 @@ * CLJS-1256 cache UUID hash value * CLJS-1226: Added the :end-run-tests event to cljs.test and a dummy event handler for it -## Fixes +### Fixes * CLJS-1200: compare behaves differently from Clojure * CLJS-1293: Warning settings not conveyed via REPL * CLJS-1291: pprint whitespace/letter checks are incomplete @@ -100,14 +147,14 @@ ## 0.0-3269 -## Fixes +### Fixes * REPL support for Closure libraries that follow classpath conventions * don't break closure libs that follow classpath conventions * build missing .map source map & .edn caches files ## 0.0-3264 -## Fixes +### Fixes * Add missing JS files back to the build * CLJS-1168: REPL fails to find .js files in :libs * CLJS-1196: Assert failed on 3190+ while :require-ing .js file in :libs directory From 44f2e4468fdb240909b5ff0fdacebe1069e05926 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 13 Oct 2015 11:00:07 -0400 Subject: [PATCH 1607/4033] missing change --- changes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changes.md b/changes.md index b7e70c619..3f6556921 100644 --- a/changes.md +++ b/changes.md @@ -17,6 +17,7 @@ * CLJS-1393: turn *target* into goog-define ### Fixes +* UUID hashing * CLJS-1465: fix *main-cli-fn* doc * CLJS-1456: bad require forms at REPL can corrupt REPL session * CLJS-1449: self host :require-macros bug From a4d6a241cd9d45bf0356809c18df14585befd68f Mon Sep 17 00:00:00 2001 From: Maria Geller Date: Tue, 13 Oct 2015 23:53:02 +1300 Subject: [PATCH 1608/4033] CLJS-1467: Foreign Libraries not included when using :main with :simple or :advanced Fixes bug by checking if either output-file or output-to option is set, which means that compile-file will return a IJavaScript map instead of a String when output-to option is set. Need to check if output-to is a String, since it also could be the :print keyword, in which case we don't want to write the output to disk. --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d6841ad58..24d3b8bf3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -428,7 +428,7 @@ returns a JavaScriptFile. In either case the return value satisfies IJavaScript." [^File file {:keys [output-file output-to] :as opts}] - (if output-file + (if (or output-file (string? output-to)) (let [out-file (io/file (util/output-directory opts) (or output-file output-to))] (compiled-file (comp/compile-file file out-file opts))) From 85d41cfbaf46f6b7081003576b91dae368c483a8 Mon Sep 17 00:00:00 2001 From: Rohit Aggarwal Date: Wed, 14 Oct 2015 12:40:16 +0100 Subject: [PATCH 1609/4033] Added boolean annotation to string/blank? function --- src/main/cljs/clojure/string.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 5989c4fd8..37d1300e7 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -190,7 +190,7 @@ (recur (dec index)) (.substring s 0 index)))))) -(defn blank? +(defn ^boolean blank? "True is s is nil, empty, or contains only whitespace." [s] (gstring/isEmptySafe s)) From 0d78c3f748a1290b6d3f7f1f9b9fc37069be7354 Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Wed, 14 Oct 2015 22:10:43 +0200 Subject: [PATCH 1610/4033] CLJS-1466: Improperly munged output path for GClosure JavaScript --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 24d3b8bf3..0fd3d087f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1208,7 +1208,7 @@ (defn lib-rel-path [{:keys [lib-path url provides] :as ijs}] (if (nil? lib-path) - (str (string/replace (first provides) "." File/separator) ".js") + (util/ns->relpath (first provides) "js") (if (.endsWith lib-path ".js") (util/get-name url) (let [path (util/path url)] From e8330a2bb07c7703797cdbcb839e5839cc6c59a9 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 16 Oct 2015 22:55:27 -0400 Subject: [PATCH 1611/4033] CLJS-1423: self-host: Requiring analyzer/compiler breaks unchecked Boolean When analyzing, trap set! calls targeting *unchecked-if* iff the value is a compile-time boolean constant. Otherwise, if the cljs.analyzer namespace is required by ClojureScript code being compiled by ClojureScript-JVM, when the form (set! *unchecked-if* val) in the :cljs conditional-compilation branch is analyzed, it is unintentionally trapped and the *unchecked-if* is reset to the symbol 'val (which is truthy, causing the compiler to then emit unchecked if code). --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index fd9caff92..404023cd9 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1589,8 +1589,9 @@ (disallowing-recur (let [enve (assoc env :context :expr) targetexpr (cond - ;; TODO: proper resolve - (= target '*unchecked-if*) + (and + (= target '*unchecked-if*) ;; TODO: proper resolve + (or (true? val) (false? val))) (do #?(:clj (reset! *unchecked-if* val) :cljs (set! *unchecked-if* val)) From 6e7541bee410560bfcda0c1fac41784026996aac Mon Sep 17 00:00:00 2001 From: Michael Ballantyne Date: Wed, 26 Aug 2015 23:25:54 -0600 Subject: [PATCH 1612/4033] CLJS-1422: cljs.js/eval-str fails for ns form on node.js with simple optimizations The special case of find-ns-obj for node.js under optimizations now guards the eval of the initial path segment, catching ReferenceError. This fixes eval-str of ns forms. --- src/main/cljs/cljs/core.cljs | 11 ++++++++++- src/test/self/self_host/test.cljs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 58e300e5f..1e03eeac6 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10135,7 +10135,16 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." segs (.split munged-ns ".")] (case *target* "nodejs" (if ^boolean js/COMPILED - (js/eval munged-ns) + ; 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 + ; may throw ReferenceError. + (find-ns-obj* + (try + (js/eval (first segs)) + (catch js/ReferenceError e + nil)) + (next segs)) (find-ns-obj* js/global segs)) "default" (find-ns-obj* goog/global segs) (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 2efc767b4..64b308059 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -124,7 +124,7 @@ (is (nil? error)) (is (== 3 value)) (inc! l))) - #_(cljs/eval-str st "(ns foo.bar)" nil + (cljs/eval-str st "(ns foo.bar)" nil {:eval node-eval :context :expr :def-emits-var true} From ac4401cb6589b2953f8063bc2e7692d0bf04406e Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Sun, 18 Oct 2015 17:10:50 +0200 Subject: [PATCH 1613/4033] CLJS-1451 Protocol impl do not support qualified method names To solve the issue, the method's namespace is completely ignored, which makes methods with the right name but the wrong namespace legal. --- src/main/clojure/cljs/core.cljc | 7 ++++--- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 5f4c98781..c21b4ec32 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1403,7 +1403,7 @@ (ifn-invoke-methods type type-sym form)))) (core/defn- add-proto-methods* [pprefix type type-sym [f & meths :as form]] - (core/let [pf (core/str pprefix f)] + (core/let [pf (core/str pprefix (name f))] (if (vector? (first meths)) ;; single method case (core/let [meth meths] @@ -1435,9 +1435,10 @@ (core/when-not (= p 'Object) (core/let [var (ana/resolve-var (dissoc env :locals) p) minfo (core/-> var :protocol-info :methods) + ->name (comp symbol name first) [fname sigs] (if (core/vector? (second method)) - [(first method) [(second method)]] - [(first method) (map first (rest method))]) + [(->name method) [(second method)]] + [(->name method) (map first (rest method))]) decmeths (core/get minfo fname ::not-found)] (core/when (= decmeths ::not-found) (ana/warning :protocol-invalid-method env {:protocol p :fname fname :no-such-method true})) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 049227bea..b1ba40f8e 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2936,6 +2936,14 @@ (is (= (hash (.toString #uuid "0d1f9029-40fc-4728-8bdd-9862172d4370")) (hash (.toString (UUID. "0d1f9029-40fc-4728-8bdd-9862172d4370" nil)))))) +(defprotocol IFooBar + (a-method [t])) + +(deftest test-cljs-1451 + (is (= "foobar" (a-method (reify + IFooBar + (cljs.core-test/a-method [_] "foobar")))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 89b1a3179b98351718ade01f2671fb8fa1906ca8 Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Sun, 18 Oct 2015 17:03:38 +0200 Subject: [PATCH 1614/4033] Warn if protocol impl methods do not match its protocol Check if the provided method names exist and belong to the associated protocol. --- src/main/clojure/cljs/core.cljc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index c21b4ec32..5a876bb34 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1435,13 +1435,20 @@ (core/when-not (= p 'Object) (core/let [var (ana/resolve-var (dissoc env :locals) p) minfo (core/-> var :protocol-info :methods) - ->name (comp symbol name first) + method-name (first method) + ->name (comp symbol name) [fname sigs] (if (core/vector? (second method)) - [(->name method) [(second method)]] - [(->name method) (map first (rest method))]) + [(->name method-name) [(second method)]] + [(->name method-name) (map first (rest method))]) decmeths (core/get minfo fname ::not-found)] (core/when (= decmeths ::not-found) (ana/warning :protocol-invalid-method env {:protocol p :fname fname :no-such-method true})) + (core/when (namespace method-name) + (core/let [method-var (ana/resolve-var (dissoc env :locals) method-name + ana/confirm-var-exist-warning)] + (core/when-not (= (:name var) (:protocol method-var)) + (ana/warning :protocol-invalid-method env + {:protocol p :fname method-name :no-such-method true})))) (core/loop [sigs sigs seen #{}] (core/when (seq sigs) (core/let [sig (first sigs) From a0d85cf8e6219ac3243e581e00e9c4eaaf6ea3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20De=20Serres?= Date: Wed, 14 Oct 2015 16:40:01 +0200 Subject: [PATCH 1615/4033] FIX CLJS-1445 Syntax error for var args in protocol methods - add warning key :protocol-with-variadic-method - add defmethod error-message - warn if '& found in protocol sigs --- src/main/clojure/cljs/analyzer.cljc | 24 +++++++++++++++--------- src/main/clojure/cljs/core.cljc | 8 ++++++-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 404023cd9..73a3968f0 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -14,7 +14,7 @@ :refer [no-warn wrapping-errors disallowing-recur allowing-redef]] [cljs.env.macros :refer [ensure]])) - #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] + #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] @@ -83,6 +83,7 @@ :protocol-invalid-method true :protocol-duped-method true :protocol-multiple-impls true + :protocol-with-variadic-method true :single-segment-namespace true :munged-namespace true :ns-var-clash true @@ -294,6 +295,11 @@ [warning-type info] (str "Protocol " (:protocol info) " implemented multiple times")) +(defmethod error-message :protocol-with-variadic-method + [warning-type info] + (str "Protocol " (:protocol info) " declares method " + (:name info) " with variadic signature (&)")) + (defmethod error-message :multiple-variadic-overloads [warning-type info] (str (:name info) ": Can't have more than 1 variadic overload")) @@ -1142,7 +1148,7 @@ (and (:fn-var v) (not fn-var?))) (warning :fn-var env {:ns-name ns-name :sym sym}))) (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] - (merge + (merge {:name var-name} ;; remove actual test metadata, as it includes non-valid EDN and ;; cannot be present in analysis cached to disk - David @@ -1503,7 +1509,7 @@ (defn analyze-let [encl-env [_ bindings & exprs :as form] is-loop] - (when-not (and (vector? bindings) (even? (count bindings))) + (when-not (and (vector? bindings) (even? (count bindings))) (throw (error encl-env "bindings must be vector of even number of elements"))) (let [context (:context encl-env) [bes env] (analyze-let-bindings encl-env bindings) @@ -1538,9 +1544,9 @@ (let [context (:context env) frame (first *recur-frames*) exprs (disallowing-recur (vec (map #(analyze (assoc env :context :expr) %) exprs)))] - (when-not frame + (when-not frame (throw (error env "Can't recur here"))) - (when-not (= (count exprs) (count (:params frame))) + (when-not (= (count exprs) (count (:params frame))) (throw (error env "recur argument count mismatch"))) (reset! (:flag frame) true) (assoc {:env env :op :recur :form form} @@ -1554,7 +1560,7 @@ (defmethod parse 'new [_ env [_ ctor & args :as form] _ _] - (when-not (symbol? ctor) + (when-not (symbol? ctor) (throw (error env "First arg to new must be a symbol"))) (disallowing-recur (let [enve (assoc env :context :expr) @@ -1616,7 +1622,7 @@ (when (:field targetexpr) targetexpr)))) valexpr (analyze enve val)] - (when-not targetexpr + (when-not targetexpr (throw (error env "set! target must be a field or a symbol naming a var"))) (cond (= targetexpr ::set-unchecked-if) {:env env :op :no-op} @@ -1729,7 +1735,7 @@ (fn [s [k exclude xs]] (if (= k :refer-clojure) (do - (when-not (= exclude :exclude) + (when-not (= exclude :exclude) (throw (error env "Only [:refer-clojure :exclude (names)] form supported"))) (when (seq s) (throw (error env "Only one :refer-clojure form is allowed per namespace definition"))) @@ -1883,7 +1889,7 @@ (defmethod parse 'ns [_ env [_ name & args :as form] _ opts] - (when-not (symbol? name) + (when-not (symbol? name) (throw (error env "Namespaces must be named by a symbol."))) (let [name (cond-> name (:macros-ns opts) macro-ns-name)] (let [segments (string/split (clojure.core/name name) #"\.")] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 5a876bb34..b95e4120d 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1893,6 +1893,10 @@ (core/let [doc (core/as-> (last sigs) doc (core/when (core/string? doc) doc)) sigs (take-while vector? sigs) + amp (when (some #{'&} (apply concat sigs)) + (cljs.analyzer/warning + :protocol-with-variadic-method + &env {:protocol psym :name fname})) slot (symbol (core/str prefix (name fname))) fname (vary-meta fname assoc :protocol p @@ -2143,7 +2147,7 @@ thens (vec (vals pairs))] `(let [~esym (if (keyword? ~e) (.-fqn ~e) nil)] (case* ~esym ~tests ~thens ~default))) - + ;; equality :else `(let [~esym ~e] @@ -2579,7 +2583,7 @@ (core/defmacro lazy-cat "Expands to code which yields a lazy sequence of the concatenation of the supplied colls. Each coll expr is not evaluated until it is - needed. + needed. (lazy-cat xs ys zs) === (concat (lazy-seq xs) (lazy-seq ys) (lazy-seq zs))" [& colls] From 68cccd404f12a4721910c0260ea705754a2d4355 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 19 Oct 2015 12:45:03 -0400 Subject: [PATCH 1616/4033] bump Google Closure dep --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 9d23380e4..e3ca17637 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20150920 + v20151015 org.clojure diff --git a/project.clj b/project.clj index df4d22f99..f23784620 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] [org.clojure/google-closure-library "0.0-20150805-acd8b553"] - [com.google.javascript/closure-compiler "v20150920"] + [com.google.javascript/closure-compiler "v20151015"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 94ac54a0c..7faf1045b 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.7.0" -CLOSURE_RELEASE="20150920" +CLOSURE_RELEASE="20151015" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20150805-acd8b553" RHINO_RELEASE="1_7R5" From d2fbb1364bb60aa32249f1b003a4899284c7ea4c Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 19 Oct 2015 13:48:08 -0400 Subject: [PATCH 1617/4033] CLJS-1470: Bump GCL Dependency --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index e3ca17637..dd7a4e7c8 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20150805-acd8b553 + 0.0-20151016-61277aea org.clojure diff --git a/project.clj b/project.clj index f23784620..f016a61dc 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "0.10.0-alpha3"] - [org.clojure/google-closure-library "0.0-20150805-acd8b553"] + [org.clojure/google-closure-library "0.0-20151016-61277aea"] [com.google.javascript/closure-compiler "v20151015"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} diff --git a/script/bootstrap b/script/bootstrap index 7faf1045b..b3b3b0e0c 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,7 +5,7 @@ set -e CLOJURE_RELEASE="1.7.0" CLOSURE_RELEASE="20151015" DJSON_RELEASE="0.2.6" -GCLOSURE_LIB_RELEASE="0.0-20150805-acd8b553" +GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" RHINO_RELEASE="1_7R5" TREADER_RELEASE="0.10.0-alpha3" From 6d4ada4bc134e952c9c4ce21bc9133db9fd58fae Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Mon, 19 Oct 2015 22:18:12 +0200 Subject: [PATCH 1618/4033] CLJS-1469 :modules regression The sortInputsByDeps method now depends on the Google CompilerOptions which need to be set before calling the method. A test to check for similar regressions was added. --- src/main/clojure/cljs/closure.clj | 1 + src/test/clojure/cljs/build_api_tests.clj | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0fd3d087f..9b9fe280e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -965,6 +965,7 @@ (let [closure-compiler (make-closure-compiler) ^List externs (load-externs opts) compiler-options (make-options opts) + _ (.initOptions closure-compiler compiler-options) sources (if (= :whitespace (:optimizations opts)) (cons "var CLOSURE_NO_DEPS = true;" sources) sources) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 51d326c1d..39e922230 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -2,7 +2,9 @@ (:refer-clojure :exclude [compile]) (:use cljs.build.api) (:use clojure.test) - (:require [cljs.env :as env] + (:import java.io.File) + (:require [clojure.java.io :as io] + [cljs.env :as env] [cljs.analyzer :as ana])) (deftest test-target-file-for-cljs-ns @@ -114,3 +116,20 @@ '(graph.bar.core graph.baz.core))) (is (= (cljs-ns-dependents test-cenv 'graph.foo.core) '(graph.bar.core graph.baz.core)))) + +(deftest cljs-1469 + (let [srcs "samples/hello/src" + [common-tmp app-tmp] (mapv #(File/createTempFile % ".js") + ["common" "app"]) + opts {:optimizations :simple + :modules {:common {:entries #{"hello.foo.bar"} + :output-to (.getAbsolutePath common-tmp)} + :app {:entries #{"hello.core"} + :output-to (.getAbsolutePath app-tmp)}}}] + (.deleteOnExit common-tmp) + (.deleteOnExit app-tmp) + (is (every? #(zero? (.length %)) [common-tmp app-tmp]) + "The initial files are empty") + (build srcs opts) + (is (not (every? #(zero? (.length %)) [common-tmp app-tmp])) + "The files are not empty after compilation"))) From 82a3c14c09b54124b8e788d5649cd129a43d80b9 Mon Sep 17 00:00:00 2001 From: Sebastian Bensusan Date: Wed, 21 Oct 2015 16:45:31 +0200 Subject: [PATCH 1619/4033] CLJS-1472 Patch for CLJS-1467 causes regression for nodejscli The :output-to fallback of cljs.closure/compile-file is removed since it was not reachable before CLJS-1467 and it only caused problems after. Instead, all compile-file callers need to explicitly use :output-file, including the (:main & (:simple | :optimizations)) case tackled by CLJS-1467. --- src/main/clojure/cljs/closure.clj | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9b9fe280e..bf69f5c4c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -427,10 +427,9 @@ compiled JavaScript will written to this location and the function returns a JavaScriptFile. In either case the return value satisfies IJavaScript." - [^File file {:keys [output-file output-to] :as opts}] - (if (or output-file (string? output-to)) - (let [out-file (io/file (util/output-directory opts) - (or output-file output-to))] + [^File file {:keys [output-file] :as opts}] + (if output-file + (let [out-file (io/file (util/output-directory opts) output-file)] (compiled-file (comp/compile-file file out-file opts))) (let [path (.getPath ^File file)] (binding [ana/*cljs-file* path] @@ -1676,12 +1675,17 @@ (repeat warnings)) warnings))) ana/*verbose* (:verbose opts)] - (let [source (if (and (:main all-opts) (#{:advanced :simple} (:optimizations all-opts))) + (let [one-file? (and (:main all-opts) + (#{:advanced :simple} (:optimizations all-opts))) + source (if one-file? (:uri (cljs-source-for-namespace (:main all-opts))) source) + compile-opts (if one-file? + (assoc all-opts :output-file (:output-to all-opts)) + all-opts) compiled (util/measure compiler-stats "Compile basic sources" - (doall (-compile source all-opts))) + (doall (-compile source compile-opts))) js-sources (util/measure compiler-stats "Add dependencies" (doall From 6ae18ebb583994177a3e38805647be2a16d2b86b Mon Sep 17 00:00:00 2001 From: Martin Klepsch Date: Wed, 21 Oct 2015 18:55:30 +0200 Subject: [PATCH 1620/4033] CLJS-1475: indicate that cljs.reader/read is safe --- src/main/cljs/cljs/reader.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index 32b0d0e74..960552e84 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -438,7 +438,9 @@ nil if the end of stream has been reached") (defn read "Reads the first object from a PushbackReader. Returns the object read. - If EOF, throws if eof-is-error is true. Otherwise returns sentinel." + If EOF, throws if eof-is-error is true. Otherwise returns sentinel. + + Only supports edn (similar to clojure.edn/read)" [reader eof-is-error sentinel is-recursive] (let [ch (read-char reader)] (cond From 790d3a9ae69ec2cade2a284e5b6d4b4643af2d8e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 30 Oct 2015 15:36:43 -0400 Subject: [PATCH 1621/4033] CLJS-1476: Self-host: Protocol prefixing broken for three- (or more) segment namespaces Fixes protocol-prefix fn (two copies, one in core and one in compiler) for use in self-host. At its core, this function need to take a string like alpha.beta.gamma/IFoo and turn it into alpha$beta$gamma$IFoo$ and it was failing to replace the multiple dot characters when in self-host mode owing to the fact that .replace in JavaScript is not a global replace. This fix adds the "g" flag. --- src/main/clojure/cljs/compiler.cljc | 5 ++++- src/main/clojure/cljs/core.cljc | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index ac5d6d939..c868a2890 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -906,7 +906,10 @@ (when (= :expr context) (emits "})()")))) (defn protocol-prefix [psym] - (symbol (str (-> (str psym) (.replace \. \$) (.replace \/ \$)) "$"))) + (symbol (str (-> (str psym) + (.replace #?(:clj \. :cljs (js/RegExp. "\\." "g")) \$) + (.replace \/ \$)) + "$"))) (defmethod emit* :invoke [{:keys [f args env] :as expr}] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b95e4120d..7f2cf1145 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1190,7 +1190,10 @@ ;;; end of reducers macros (core/defn- protocol-prefix [psym] - (core/str (core/-> (core/str psym) (.replace \. \$) (.replace \/ \$)) "$")) + (core/str (core/-> (core/str psym) + (.replace #?(:clj \. :cljs (js/RegExp. "\\." "g")) \$) + (.replace \/ \$)) + "$")) (def #^:private base-type {nil "null" From 5ebb3ad1f5c32da901435731b4fa0a19ae0c7db7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 31 Oct 2015 13:12:01 -0400 Subject: [PATCH 1622/4033] when -> core/when --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 7f2cf1145..6fd978c29 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1896,7 +1896,7 @@ (core/let [doc (core/as-> (last sigs) doc (core/when (core/string? doc) doc)) sigs (take-while vector? sigs) - amp (when (some #{'&} (apply concat sigs)) + amp (core/when (some #{'&} (apply concat sigs)) (cljs.analyzer/warning :protocol-with-variadic-method &env {:protocol psym :name fname})) From b12d836eff32232ddb20cca274cf34835e0fd7c7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 3 Nov 2015 09:10:52 -0500 Subject: [PATCH 1623/4033] CLJS-1478: Self-host: Allow static-fns opt Bind cljs.analyzer/*cljs-static-fns* with :static-fns opts value in all API entry points. Test compilation and evaluation with respect to :static-fns in self-host mode. --- src/main/cljs/cljs/js.cljs | 4 ++++ src/test/self/self_host/test.cljs | 27 +++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index b3f3afeac..6ef4fa39e 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -440,6 +440,7 @@ ((fn analyze-loop [last-ast ns] (binding [env/*compiler* (:*compiler* bound-vars) ana/*cljs-ns* ns + ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns ns) ana/*passes* (:*passes* bound-vars) r/*data-readers* (:*data-readers* bound-vars) @@ -527,6 +528,7 @@ (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) + ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns (:*cljs-ns* bound-vars)) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol ana/resolve-symbol @@ -601,6 +603,7 @@ (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* ns + ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol ana/resolve-symbol @@ -694,6 +697,7 @@ (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* ns + ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol ana/resolve-symbol diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 64b308059..bf6c0c5d0 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -2,6 +2,7 @@ (:require [cljs.test :as test :refer-macros [run-tests deftest testing is async]] [cljs.js :as cljs] + [clojure.string :as string] [cljs.nodejs :as nodejs])) (set! (.-user js/cljs) #js {}) @@ -66,7 +67,7 @@ (deftest test-compile-str (async done - (let [l (latch 4 done)] + (let [l (latch 6 done)] (cljs/compile-str st "(+ 1 1)" (fn [{:keys [error value]}] (is (nil? error)) @@ -89,11 +90,24 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (= "\"a\".toString()" value)) + (inc! l))) + (cljs/compile-str st "(do (defn foo [a b] (+ a b)) (foo 1 2))" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (string/index-of value "cljs.user.foo.call(null,1,2)")) + (inc! l))) + (cljs/compile-str st "(do (defn foo [a b] (+ a b)) (foo 1 2))" nil + {:context :expr + :static-fns true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (string/index-of value "cljs.user.foo(1,2)")) (inc! l)))))) (deftest test-eval-str (async done - (let [l (latch 7 done)] + (let [l (latch 8 done)] (cljs/eval-str st "(+ 1 1)" nil {:eval node-eval} (fn [{:keys [error value]}] @@ -140,6 +154,15 @@ (is (nil? error)) (is (== 3 (js/cljs.user.foo 1 2))) (inc! l))) + (cljs/eval-str st "(do (defn foo [a b] (+ a b)) (foo 1 2))" nil + {:eval node-eval + :context :expr + :def-emits-var true + :static-fns true} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (== 3 value)) + (inc! l))) (cljs/eval-str st "(def foo (let [x 1] (let [x (inc x)] x)))" nil {:eval node-eval :context :statement From 62ae63b440e9c2e573c3302318c9d6b80d33f43f Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 3 Nov 2015 16:49:36 -0500 Subject: [PATCH 1624/4033] check that performance.now method actually exists --- src/main/cljs/cljs/core.cljs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1e03eeac6..25ea3fdca 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -337,9 +337,14 @@ "Returns highest resolution time offered by host in milliseconds." [] (cond - (exists? js/performance) (.now js/performance) - (exists? js/process) (let [t (.hrtime js/process)] - (/ (+ (* (aget t 0) 1e9) (aget t 1)) 1e6)) + (and (exists? js/performance) + (not (nil? (. js/performance -now)))) + (.now js/performance) + + (exists? js/process) + (let [t (.hrtime js/process)] + (/ (+ (* (aget t 0) 1e9) (aget t 1)) 1e6)) + :else (.getTime (js/Date.)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; arrays ;;;;;;;;;;;;;;;; From 409d1eca4fcf776be1f7b28f759d6f36f7a83ec8 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Tue, 1 Sep 2015 00:52:31 +0300 Subject: [PATCH 1625/4033] Refactor build pipeline This separates dependency finding and ordering from compiling the files. The file dependencies are searched for using new -find-sources method in Compilable protocol. Build function now first searches for all sources in given inputs, then adds dependency sources to those (e.g. from jars), orders the sources, and then compiles them all. --- src/main/clojure/cljs/analyzer.cljc | 6 ++ src/main/clojure/cljs/build/api.clj | 4 +- src/main/clojure/cljs/closure.clj | 144 ++++++++++++++++++++++++---- src/main/clojure/cljs/compiler.cljc | 10 ++ 4 files changed, 146 insertions(+), 18 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 73a3968f0..ff686687c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1637,6 +1637,12 @@ resource on the classpath or file from the root of the build." [ns] (or (util/ns->source ns) + ;; Find sources available in inputs given to cljs.closure/build - Juho Teperi + (some (fn [source] + (if (= ns (:ns source)) + (:source-file source))) + (:sources @env/*compiler*)) + ;; Find sources in directory given to cljs.compiler/compile-root - Juho Teperi (let [rootp (when-let [root (:root @env/*compiler*)] (.getPath ^File root)) cljsf (io/file rootp (ns->relpath ns :cljs)) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 04f00c8ba..688fccee7 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -183,7 +183,9 @@ (if (sequential? compiled) compiled [compiled])))] - (mapcat compile-input xs))))) + (mapcat compile-input xs))) + (-find-sources [_ opts] + (mapcat #(closure/-find-sources % opts) xs)))) (defn compile "Given a Compilable, compile it and return an IJavaScript." diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index bf69f5c4c..4ed0272fe 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -14,7 +14,7 @@ Use the 'build' function for end-to-end compilation. - build = compile -> add-dependencies -> optimize -> output + build = find-sources -> add-dependencies -> compile -> optimize -> output Two protocols are defined: IJavaScript and Compilable. The Compilable protocol is satisfied by something which can return one @@ -389,7 +389,8 @@ (-paths [this] [this])) (defprotocol Compilable - (-compile [this opts] "Returns one or more IJavaScripts.")) + (-compile [this opts] "Returns one or more IJavaScripts.") + (-find-sources [this opts] "Returns one or more IJavascripts, without compiling them.")) (defn compile-form-seq "Compile a sequence of forms to a JavaScript source string." @@ -477,6 +478,10 @@ (let [file-on-disk (jar-file-to-disk this (util/output-directory opts))] (-compile file-on-disk opts)))) +(defn find-jar-sources + [this opts] + [(comp/find-source (jar-file-to-disk this (util/output-directory opts)))]) + (extend-protocol Compilable File @@ -484,33 +489,48 @@ (if (.isDirectory this) (compile-dir this opts) (compile-file this opts))) + (-find-sources [this _] + (if (.isDirectory this) + (comp/find-root-sources this) + [(comp/find-source this)])) URL (-compile [this opts] (case (.getProtocol this) "file" (-compile (io/file this) opts) "jar" (compile-from-jar this opts))) + (-find-sources [this opts] + (case (.getProtocol this) + "file" (-find-sources (io/file this) opts) + "jar" (find-jar-sources this opts))) clojure.lang.PersistentList (-compile [this opts] (compile-form-seq [this])) + ; FIXME: -find-sources String (-compile [this opts] (-compile (io/file this) opts)) + (-find-sources [this opts] (-find-sources (io/file this) opts)) clojure.lang.PersistentVector (-compile [this opts] (compile-form-seq this)) + ; FIXME: -find-sources ) (comment ;; compile a file in memory (-compile "samples/hello/src/hello/core.cljs" {}) + (-find-sources "samples/hello/src/hello/core.cljs" {}) ;; compile a file to disk - see file @ 'out/clojure/set.js' (-compile (io/resource "clojure/set.cljs") {:output-file "clojure/set.js"}) + (-find-sources (io/resource "clojure/set.cljs") {:output-file "clojure/set.js"}) ;; compile a project (-compile (io/file "samples/hello/src") {}) + (-find-sources (io/file "samples/hello/src") {}) ;; compile a project with a custom output directory (-compile (io/file "samples/hello/src") {:output-dir "my-output"}) + (-find-sources (io/file "samples/hello/src") {:output-dir "my-output"}) ;; compile a form (-compile '(defn plus-one [x] (inc x)) {}) ;; compile a vector of forms @@ -639,6 +659,28 @@ (cljs-dependencies {} ["cljs.core" "clojure.string"]) ) +(defn find-cljs-dependencies + "Given set of cljs namespace symbols, find IJavaScript objects for the namespaces." + [requires] + (letfn [(cljs-deps [namespaces] + (->> namespaces + (remove #(or ((@env/*compiler* :js-dependency-index) %) + (deps/find-classpath-lib %))) + (map cljs-source-for-namespace) + (remove (comp nil? :uri))))] + (loop [required-files (cljs-deps requires) + visited (set required-files) + cljs-namespaces #{}] + (if (seq required-files) + (let [next-file (first required-files) + ns-info (ana/parse-ns (:uri next-file)) + new-req (remove #(contains? visited %) (cljs-deps (cond-> (deps/-requires ns-info) + (= 'cljs.js (:ns ns-info)) (conj "cljs.core$macros"))))] + (recur (into (rest required-files) new-req) + (into visited new-req) + (conj cljs-namespaces ns-info))) + (disj cljs-namespaces nil))))) + (defn add-dependencies "Given one or more IJavaScript objects in dependency order, produce a new sequence of IJavaScript objects which includes the input list @@ -670,6 +712,77 @@ required-cljs inputs))))) +(comment + (alter-var-root #'env/*compiler* (constantly (env/default-compiler-env))) + ;; only get cljs deps + (find-cljs-dependencies ["goog.string" "cljs.core"]) + ;; get transitive deps + (find-cljs-dependencies ["clojure.string"]) + ;; don't get cljs.core twice + (find-cljs-dependencies ["cljs.core" "clojure.string"]) + ) + +(defn add-dependency-sources + "Given list of IJavaScript objects, produce a new sequence of IJavaScript objects + of all dependencies of inputs." + [inputs] + (let [inputs (set inputs) + requires (set (mapcat deps/-requires inputs))] + (into inputs (find-cljs-dependencies requires)))) + +(defn check-unprovided + [inputs] + (let [requires (set (mapcat deps/-requires inputs)) + provided (set (mapcat deps/-provides inputs)) + unprovided (clojure.set/difference requires provided #{"constants-table"})] + (when (seq unprovided) + (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) + inputs)) + +(defn compile-sources + "Takes dependency ordered list of IJavaScript compatible maps from parse-ns + and compiles them." + [inputs compiler-stats opts] + (util/measure compiler-stats + "Compile sources" + (binding [comp/*inputs* (zipmap (map :ns inputs) inputs)] + (doall + (for [ns-info inputs] + ; TODO: compile-file calls parse-ns unnecessarily to get ns-info + ; TODO: we could mark dependent namespaces for recompile here + (-compile (:source-file ns-info) + ; - ns-info -> ns -> cljs file relpath -> js relpath + (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))))))) + +(defn add-goog-base + [inputs] + (cons (javascript-file nil (io/resource "goog/base.js") ["goog"] nil) + inputs)) + +(defn add-js-sources + "Given list of IJavaScript objects, add foreign-deps and constants-table + IJavaScript objects to the list." + [inputs opts] + (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) + [(when (-> @env/*compiler* :options :emit-constants) + (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] + (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] + inputs))) + +(comment + (comp/find-sources-root "samples/hello/src") + (find-dependency-sources (find-sources-root "samples/hello/src")) + (find-sources "samples/hello/src")) + (defn preamble-from-paths [paths] (when-let [missing (seq (remove io/resource paths))] (ana/warning :preamble-missing @env/*compiler* {:missing (sort missing)})) @@ -1653,7 +1766,9 @@ #(-> % (update-in [:options] merge all-opts) (assoc :target (:target opts)) - (assoc :js-dependency-index (deps/js-dependency-index all-opts)))) + (assoc :js-dependency-index (deps/js-dependency-index all-opts)) + ;; Save list of sources for cljs.analyzer/locate-src - Juho Teperi + (assoc :sources (-find-sources source all-opts)))) (binding [comp/*dependents* (when-not (false? (:recompile-dependents opts)) (atom {:recompile #{} :visited #{}})) ana/*cljs-static-fns* @@ -1683,20 +1798,15 @@ compile-opts (if one-file? (assoc all-opts :output-file (:output-to all-opts)) all-opts) - compiled (util/measure compiler-stats - "Compile basic sources" - (doall (-compile source compile-opts))) - js-sources (util/measure compiler-stats - "Add dependencies" - (doall - (concat - (apply add-dependencies all-opts - (concat - (if (sequential? compiled) compiled [compiled]) - (when (= :nodejs (:target all-opts)) - [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))) - (when (= :nodejs (:target all-opts)) - [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))) + js-sources (-> (-find-sources source all-opts) + add-dependency-sources + deps/dependency-order + (compile-sources compiler-stats compile-opts) + (add-js-sources all-opts) + (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") all-opts)])) + deps/dependency-order + add-goog-base + (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)]))) _ (when (:emit-constants all-opts) (comp/emit-constants-table-to-file (::ana/constant-table @env/*compiler*) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index c868a2890..037ec6198 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1391,6 +1391,16 @@ (assoc ijs :file-name (.getPath output-file))))) compiled))))))) +#?(:clj + (defn find-source [file] + (ana/parse-ns file))) + +#?(:clj + (defn find-root-sources + [src-dir] + (let [src-dir-file (io/file src-dir)] + (map find-source (cljs-files-in src-dir-file))))) + ;; TODO: needs fixing, table will include other things than keywords - David (defn emit-constants-table [table] From 9d1b008e6157145ba62e3c849886a2052493a8ac Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 3 Nov 2015 20:44:15 -0500 Subject: [PATCH 1626/4033] CLJS-1228: cljs.util/topo-sort is polynomial on larger dependency graphs remove ns-dependencies, remove corresponding build api. remove tests. simplify cljs.compiler/requires-compilation? now that we know that the build order is fixed. --- src/main/clojure/cljs/analyzer.cljc | 25 ----------------- src/main/clojure/cljs/build/api.clj | 14 ---------- src/main/clojure/cljs/closure.clj | 4 +-- src/main/clojure/cljs/compiler.cljc | 19 +++++-------- src/test/clojure/cljs/analyzer_tests.clj | 33 ----------------------- src/test/clojure/cljs/build_api_tests.clj | 13 --------- 6 files changed, 8 insertions(+), 100 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ff686687c..2a91db5d7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -782,31 +782,6 @@ (when ns (get-in namespaces [ns :macros sym])))))) -(defn ns-dependents - "Given a namespace as a symbol and a map from namespace symbol to - namespace information return the topologically sorted list of all - dependent namespaces. The map values of the optional second argument must - be maps with a :requires set of symbols, a :requires map of symbol -> alias - (analyzer format) or a :requires vector of munged namespace strings - (closure format)." - ([ns] (ns-dependents ns (get @env/*compiler* ::namespaces))) - ([ns ns-map] - (letfn [(parent? [parent [child {:keys [requires] :as ns-info}]] - (when-not (= parent child) - (cond - (or (map? requires) - (set? requires)) (contains? requires parent) - (vector? requires) (some #{(munge (name parent))} requires))))] - (topo-sort ns - (fn [ns'] - (set (map first (filter #(parent? ns' %) ns-map)))))))) - -(comment - (ns-dependents 'bar - '{bar {:requires #{cljs.core}} - foo {:requires #{cljs.core bar}}}) - ) - (declare analyze analyze-symbol analyze-seq) (def specials '#{if def fn* do let* loop* letfn* throw try recur new set! diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 688fccee7..e9b59ae2c 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -70,20 +70,6 @@ (intersection namespaces-set (-> x :require-macros vals set)))) (vals (:cljs.analyzer/namespaces @state))))))) -(defn cljs-ns-dependents - "Given a namespace symbol return a seq of all dependent - namespaces sorted in dependency order. Will include - transient dependents." - ([ns] - (cljs-ns-dependents - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env)) - ns)) - ([state ns] - (env/with-compiler-env state - (ana/ns-dependents ns)))) - (defn parse-js-ns "Given a Google Closure style JavaScript file or resource return the namespace information for the given file. Only returns the value extracted from the diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4ed0272fe..cc26e873e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1769,8 +1769,8 @@ (assoc :js-dependency-index (deps/js-dependency-index all-opts)) ;; Save list of sources for cljs.analyzer/locate-src - Juho Teperi (assoc :sources (-find-sources source all-opts)))) - (binding [comp/*dependents* (when-not (false? (:recompile-dependents opts)) - (atom {:recompile #{} :visited #{}})) + (binding [comp/*recompiled* (when-not (false? (:recompile-dependents opts)) + (atom #{})) ana/*cljs-static-fns* (or (and (= (:optimizations opts) :advanced) (not (false? (:static-fns opts)))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 037ec6198..c0c9b9ec4 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -35,7 +35,7 @@ (def js-reserved ana/js-reserved) -(def ^:dynamic *dependents* nil) +(def ^:dynamic *recompiled* nil) (def ^:dynamic *inputs* nil) (def ^:dynamic *source-map-data* nil) (def ^:dynamic *lexical-renames* {}) @@ -1268,7 +1268,7 @@ "Return true if the src file requires compilation." ([src dest] (requires-compilation? src dest nil)) ([^File src ^File dest opts] - (let [{:keys [ns]} (ana/parse-ns src)] + (let [{:keys [ns requires]} (ana/parse-ns src)] (ensure (or (not (.exists dest)) (> (.lastModified src) (.lastModified dest)) @@ -1283,10 +1283,8 @@ (if (= (:optimizations opts) :none) (not (.exists (io/file (str (.getPath dest) ".map")))) (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))) - (let [{:keys [recompile visited]} - (and *dependents* @*dependents*)] - (and (contains? recompile ns) - (not (contains? visited ns)))))))))) + (when-let [recompiled' (and *recompiled* @*recompiled*)] + (some requires recompiled')))))))) #?(:clj (defn compile-file @@ -1331,13 +1329,8 @@ (not= :interactive (:mode opts))) (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns)) (let [ret (compile-file* src-file dest-file opts)] - (when *dependents* - (swap! *dependents* - (fn [{:keys [recompile visited]}] - {:recompile (into recompile - (ana/ns-dependents ns - (merge *inputs* nses))) - :visited (conj visited ns)}))) + (when *recompiled* + (swap! *recompiled* conj ns)) ret)) (do ;; populate compilation environment with analysis information diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index cef32340d..1486b9a9d 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -305,39 +305,6 @@ (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble {:foo baz})))) {:foo 'baz})))) -(def test-deps-env (atom - {:cljs.analyzer/namespaces - {'file-reloading {:requires {'utils 'utils}} - 'core {:requires {'file-reloading 'file-reloading}} - 'dev {:requires {'client 'client - 'core 'core}} - 'client {:requires {'utils 'utils}} - 'utils {:requires {}}}})) - -(deftest test-ns-dependents - (binding [env/*compiler* test-deps-env] - (is (= (set (a/ns-dependents 'client)) - #{'dev})) - (is (= (set (a/ns-dependents 'core)) - #{'dev})) - (is (= (set (a/ns-dependents 'dev)) - #{})) - - ;; if 'file-reloading returns 'core - (is (= (set (a/ns-dependents 'file-reloading)) - #{'dev 'core})) - - ;; how can 'utils include 'file-reloading but not 'core - ;; FAILS with - ;; actual: (not (= #{file-reloading dev client} #{file-reloading dev client core})) - (is (= (set (a/ns-dependents 'utils)) - #{'file-reloading 'dev 'client 'core})))) - -(deftest test-ns-dependents-custom-ns-map - (let [ns-map '{bar {:requires #{cljs.core}} - foo {:requires #{cljs.core bar}}}] - (is (= (set (a/ns-dependents 'bar ns-map)) #{'foo})))) - (deftest test-cljs-1105 ;; munge turns - into _, must preserve the dash first (is (not= (a/gen-constant-id :test-kw) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 39e922230..f52e35ccb 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -104,19 +104,6 @@ (:require [graph.foo.core :as foo] [graph.bar.core :as bar])))))) -(deftest test-cljs-ns-dependencies - (is (= (env/with-compiler-env test-cenv - (cljs-ns-dependents 'clojure.string)) - '(cljs.user))) - (is (= (env/with-compiler-env test-cenv - (cljs-ns-dependents 'foo.core)) - '(bar.core baz.core))) - (is (= (env/with-compiler-env test-cenv - (cljs-ns-dependents 'graph.foo.core)) - '(graph.bar.core graph.baz.core))) - (is (= (cljs-ns-dependents test-cenv 'graph.foo.core) - '(graph.bar.core graph.baz.core)))) - (deftest cljs-1469 (let [srcs "samples/hello/src" [common-tmp app-tmp] (mapv #(File/createTempFile % ".js") From 278d2a566900d6d99762968ce78d0d19cad81b4f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Nov 2015 23:03:38 -0500 Subject: [PATCH 1627/4033] extend List & Vector to new Compilable method -find-sources change cljs.analyzer/parse-ns so that it can take a sequential list of forms change cljs.closure/compile-sources to handle List & Vector Compilable cases fixes browser REPL --- src/main/clojure/cljs/analyzer.cljc | 70 ++++++++++++++++------------- src/main/clojure/cljs/closure.clj | 9 ++-- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2a91db5d7..630185ebf 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2694,38 +2694,44 @@ (or (when (contains? opts :load-macros) (:load-macros opts)) false)] - (with-open [rdr (io/reader src)] - (loop [forms (forms-seq* rdr (source-path src))] - (if (seq forms) - (let [env (empty-env) - ast (no-warn (analyze env (first forms) nil opts))] - (if (= :ns (:op ast)) - (let [ns-name (:name ast) - ns-name (if (and (= 'cljs.core ns-name) - (= "cljc" (util/ext src))) - 'cljs.core$macros - ns-name) - deps (merge (:uses ast) (:requires ast))] - (.close ^Reader rdr) - [(merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= 'cljs.core ns-name) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in compiler-env [:options :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src - :ast ast - :macros-ns (or (:macros-ns opts) - (= 'cljs.core$macros ns-name))} - (when (and dest (.exists ^File dest)) - {:lines (with-open [reader (io/reader dest)] - (-> reader line-seq count))})) - @env/*compiler*]) - (recur (rest forms)))) - (throw (AssertionError. (str "No ns form found in " src)))))))] + (let [rdr (when-not (sequential? src) (io/reader src))] + (try + (loop [forms (if rdr + (forms-seq* rdr (source-path src)) + src)] + (if (seq forms) + (let [env (empty-env) + ast (no-warn (analyze env (first forms) nil opts))] + (if (= :ns (:op ast)) + (let [ns-name (:name ast) + ns-name (if (and (= 'cljs.core ns-name) + (= "cljc" (util/ext src))) + 'cljs.core$macros + ns-name) + deps (merge (:uses ast) (:requires ast))] + [(merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= 'cljs.core ns-name) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in compiler-env [:options :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file (when rdr src) + :source-forms (when-not rdr src) + :ast ast + :macros-ns (or (:macros-ns opts) + (= 'cljs.core$macros ns-name))} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))})) + @env/*compiler*]) + (recur (rest forms)))) + (throw (AssertionError. (str "No ns form found in " src))))) + (finally + (when rdr + (.close ^Reader rdr))))))] (when (false? (:restore opts)) (swap! env/*compiler* (fn [old-state] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cc26e873e..9a03c3773 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -507,7 +507,8 @@ clojure.lang.PersistentList (-compile [this opts] (compile-form-seq [this])) - ; FIXME: -find-sources + (-find-sources [this opts] + [(ana/parse-ns [this] opts)]) String (-compile [this opts] (-compile (io/file this) opts)) @@ -515,7 +516,8 @@ clojure.lang.PersistentVector (-compile [this opts] (compile-form-seq this)) - ; FIXME: -find-sources + (-find-sources [this opts] + [(ana/parse-ns this opts)]) ) (comment @@ -750,7 +752,8 @@ (for [ns-info inputs] ; TODO: compile-file calls parse-ns unnecessarily to get ns-info ; TODO: we could mark dependent namespaces for recompile here - (-compile (:source-file ns-info) + (-compile (or (:source-file ns-info) + (:source-forms ns-info)) ; - ns-info -> ns -> cljs file relpath -> js relpath (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))))))) From 2c633d57a0a5a3d2d065ff41a4495f0f9be988ad Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Nov 2015 23:09:00 -0500 Subject: [PATCH 1628/4033] in system-time check that js/process.hrtime is actually a thing --- src/main/cljs/cljs/core.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 25ea3fdca..f6d624fcb 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -341,7 +341,8 @@ (not (nil? (. js/performance -now)))) (.now js/performance) - (exists? js/process) + (and (exists? js/process) + (not (nil? (. js/process -hrtime)))) (let [t (.hrtime js/process)] (/ (+ (* (aget t 0) 1e9) (aget t 1)) 1e6)) From 6dc027b961e7bfb642caf3114283ce9073dfd667 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 3 Nov 2015 23:27:28 -0500 Subject: [PATCH 1629/4033] make browser REPL file reloads less chatty --- src/main/clojure/cljs/repl.cljc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index cfe1ad660..b48ee2354 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -188,13 +188,14 @@ (if (:output-dir opts) ;; REPLs that read from :output-dir just need to add deps, ;; environment will handle actual loading - David - (doseq [source (->> sources - (remove (comp #{:seed} :type)) - (map #(cljsc/source-on-disk opts %)))] - (when (:repl-verbose opts) - (println "Loading:" (:provides source))) - (-evaluate repl-env "" 1 - (cljsc/add-dep-string opts source))) + (let [sb (StringBuffer.)] + (doseq [source (->> sources + (remove (comp #{:seed} :type)) + (map #(cljsc/source-on-disk opts %)))] + (when (:repl-verbose opts) + (println "Loading:" (:provides source))) + (.append sb (cljsc/add-dep-string opts source))) + (-evaluate repl-env "" 1 (.toString sb))) ;; REPLs that stream must manually load each dep - David (doseq [{:keys [url provides]} deps] (-load repl-env provides url)))))) From 2a8e17fb05c9bf538e3e606457a09734f8c99037 Mon Sep 17 00:00:00 2001 From: Gary Fredericks Date: Wed, 4 Nov 2015 08:55:12 -0600 Subject: [PATCH 1630/4033] Generate larger range of random UUIDs The random-uuid function was only generating hex characters from 0-e, but should be using 0-f. Adjusted the arg to the first rand-int call to make this work, and adjusted the second one as well, which should have no effect on the behavior but is less confusing to read. --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f6d624fcb..0dc6b0d4c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9861,8 +9861,8 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (UUID. s nil)) (defn random-uuid [] - (letfn [(hex [] (.toString (rand-int 15) 16))] - (let [rhex (.toString (bit-or 0x8 (bit-and 0x3 (rand-int 14))) 16)] + (letfn [(hex [] (.toString (rand-int 16) 16))] + (let [rhex (.toString (bit-or 0x8 (bit-and 0x3 (rand-int 16))) 16)] (uuid (str (hex) (hex) (hex) (hex) (hex) (hex) (hex) (hex) "-" From b1c24f831cace15becfec72b9c7ac391f3dfed3b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Nov 2015 06:45:20 -0500 Subject: [PATCH 1631/4033] update readme & changes for 1.7.170 --- README.md | 6 +++--- changes.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2d21f99b..c93786d92 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets J ## Releases and dependency information ## -Latest stable release: 1.7.145 +Latest stable release: 1.7.170 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.7.145"] +[org.clojure/clojurescript "1.7.170"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.7.145 org.clojure clojurescript - 1.7.145 + 1.7.170 ``` diff --git a/changes.md b/changes.md index 3f6556921..a49cb5fb0 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,31 @@ +## 1.7.170 + +### Enhancements +* Refactor build pipeline +* CLJS-1478: Self-host: Allow static-fns opt + +### Changes +* Generate larger range of random UUIDs +* make browser REPL file reloads less chatty +* CLJS-1475: indicate that cljs.reader/read is safe +* CLJS-1470: Bump GCL Dependency +* bump Google Closure dep + +### Fixes +* in system-time check that js/process.hrtime is actually a thing +* CLJS-1228: cljs.util/topo-sort is polynomial on larger dependency graphs +* check that performance.now method actually exists +* CLJS-1476: Self-host: Protocol prefixing broken for three- (or more) segment namespaces +* CLJS-1472 Patch for CLJS-1467 causes regression for nodejscli +* CLJS-1469 :modules regression +* CLJS-1445: Syntax error for var args in protocol methods +* Warn if protocol impl methods do not match its protocol +* CLJS-1451 Protocol impl do not support qualified method names +* CLJS-1422: cljs.js/eval-str fails for ns form on node.js with simple optimizations +* CLJS-1423: self-host: Requiring analyzer/compiler breaks unchecked Boolean +* CLJS-1466: Improperly munged output path for GClosure JavaScript +* CLJS-1467: Foreign Libraries not included when using :main with :simple or :advanced + ## 1.7.145 ### Enhancements From 30e302c5226b1d894edd82d1b413731a8f8a0b39 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 6 Nov 2015 17:09:53 -0500 Subject: [PATCH 1632/4033] note tooling change --- changes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changes.md b/changes.md index a49cb5fb0..dac323391 100644 --- a/changes.md +++ b/changes.md @@ -1,5 +1,9 @@ ## 1.7.170 +This is a breaking change for tooling libraries like lein-cljsbuild, +lein-figwheel, and boot-cljs. Refer to the corresponding documentation to +determine which version you should use. + ### Enhancements * Refactor build pipeline * CLJS-1478: Self-host: Allow static-fns opt From 1346c3f8409a1db2010841b5647c1af0692c564d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Nov 2015 06:41:20 -0500 Subject: [PATCH 1633/4033] CLJS-1483: Minor DCE regression with advanced compilation mode change :def case in compiler so that we once again only emit if init supplied. The REPL is the only exception. Without the above change foward declarations would prevent DCE. fix minor DCE issue around murmur3 hashing. --- src/main/cljs/cljs/core.cljs | 36 ++++++++++------ src/main/clojure/cljs/compiler.cljc | 65 +++++++++++++++-------------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0dc6b0d4c..dde0bf52e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1198,8 +1198,13 @@ (next coll)) (mix-collection-hash hash-code n)))) -(def ^:private empty-ordered-hash - (mix-collection-hash 1 0)) +(def ^{:private true :jsdoc ["@type {*}"]} + EMPTY_ORDERED_HASH nil) + +(defn- empty-ordered-hash [] + (when (nil? EMPTY_ORDERED_HASH) + (set! EMPTY_ORDERED_HASH (mix-collection-hash 1 0))) + EMPTY_ORDERED_HASH) (defn ^number hash-unordered-coll "Returns the hash code, consistent with =, for an external unordered @@ -1213,8 +1218,13 @@ (recur (inc n) (bit-or (+ hash-code (hash (first coll))) 0) (next coll)) (mix-collection-hash hash-code n)))) -(def ^:private empty-unordered-hash - (mix-collection-hash 0 0)) +(def ^{:private true :jsdoc ["@type {*}"]} + EMPTY_UNORDERED_HASH nil) + +(defn- empty-unordered-hash [] + (when (nil? EMPTY_UNORDERED_HASH) + (set! EMPTY_UNORDERED_HASH (mix-collection-hash 0 0))) + EMPTY_UNORDERED_HASH) ;;;;;;;;;;;;;;;;;;; protocols on primitives ;;;;;;;; (declare hash-map list equiv-sequential) @@ -2801,7 +2811,7 @@ reduces them without incurring seq initialization" false)) IHash - (-hash [coll] empty-ordered-hash) + (-hash [coll] (empty-ordered-hash)) ISeqable (-seq [coll] nil) @@ -4916,7 +4926,7 @@ reduces them without incurring seq initialization" (set! (.-EMPTY-NODE PersistentVector) (VectorNode. nil (make-array 32))) (set! (.-EMPTY PersistentVector) - (PersistentVector. nil 0 5 (.-EMPTY-NODE PersistentVector) (array) empty-ordered-hash)) + (PersistentVector. nil 0 5 (.-EMPTY-NODE PersistentVector) (array) (empty-ordered-hash))) (set! (.-fromArray PersistentVector) (fn [xs ^boolean no-clone] @@ -5466,7 +5476,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-ordered-hash))) (es6-iterable PersistentQueue) @@ -5643,7 +5653,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (transient (into (hash-map) coll)))) -(set! (.-EMPTY ObjMap) (ObjMap. nil (array) (js-obj) 0 empty-unordered-hash)) +(set! (.-EMPTY ObjMap) (ObjMap. nil (array) (js-obj) 0 (empty-unordered-hash))) (set! (.-HASHMAP_THRESHOLD ObjMap) 8) @@ -5995,7 +6005,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientArrayMap. (js-obj) (alength arr) (aclone arr)))) -(set! (.-EMPTY PersistentArrayMap) (PersistentArrayMap. nil 0 (array) empty-unordered-hash)) +(set! (.-EMPTY PersistentArrayMap) (PersistentArrayMap. nil 0 (array) (empty-unordered-hash))) (set! (.-HASHMAP-THRESHOLD PersistentArrayMap) 8) @@ -6931,7 +6941,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientHashMap. (js-obj) root cnt has-nil? nil-val))) -(set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil empty-unordered-hash)) +(set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil (empty-unordered-hash))) (set! (.-fromArray PersistentHashMap) (fn [arr ^boolean no-clone] @@ -7724,7 +7734,7 @@ reduces them without incurring seq initialization" (-comparator [coll] comp)) -(set! (.-EMPTY PersistentTreeMap) (PersistentTreeMap. compare nil 0 nil empty-unordered-hash)) +(set! (.-EMPTY PersistentTreeMap) (PersistentTreeMap. compare nil 0 nil (empty-unordered-hash))) (es6-iterable PersistentTreeMap) @@ -8045,7 +8055,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientHashSet. (-as-transient hash-map)))) (set! (.-EMPTY PersistentHashSet) - (PersistentHashSet. nil (.-EMPTY PersistentArrayMap) empty-unordered-hash)) + (PersistentHashSet. nil (.-EMPTY PersistentArrayMap) (empty-unordered-hash))) (set! (.-fromArray PersistentHashSet) (fn [items ^boolean no-clone] @@ -8191,7 +8201,7 @@ reduces them without incurring seq initialization" (-lookup coll k not-found))) (set! (.-EMPTY PersistentTreeSet) - (PersistentTreeSet. nil (.-EMPTY PersistentTreeMap) empty-unordered-hash)) + (PersistentTreeSet. nil (.-EMPTY PersistentTreeMap) (empty-unordered-hash))) (es6-iterable PersistentTreeSet) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index c0c9b9ec4..3a48b26be 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -602,37 +602,40 @@ (defmethod emit* :def [{:keys [name var init env doc jsdoc export test var-ast]}] - (let [mname (munge name)] - (emit-comment env doc (concat jsdoc (:jsdoc init))) - (when (:def-emits-var env) - (when (= :return (:context env)) - (emitln "return (")) - (emitln "(function (){")) - (emits var) - (when init - (emits " = " - (if-let [define (get-define mname jsdoc)] - define - init))) - (when (:def-emits-var env) - (emitln "; return (") - (emits (merge - {:op :var-special - :env (assoc env :context :expr)} - var-ast)) - (emitln ");})()") - (when (= :return (:context env)) - (emitln ")"))) - ;; NOTE: JavaScriptCore does not like this under advanced compilation - ;; this change was primarily for REPL interactions - David - ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") - (when-not (= :expr (:context env)) (emitln ";")) - (when export - (emitln "goog.exportSymbol('" (munge export) "', " mname ");")) - (when (and ana/*load-tests* test) - (when (= :expr (:context env)) - (emitln ";")) - (emitln var ".cljs$lang$test = " test ";")))) + ;; We only want to emit if an init is supplied, this is to avoid dead code + ;; elimination issues. The REPL is the exception to this rule. + (when (or init (:def-emits-var env)) + (let [mname (munge name)] + (emit-comment env doc (concat jsdoc (:jsdoc init))) + (when (:def-emits-var env) + (when (= :return (:context env)) + (emitln "return (")) + (emitln "(function (){")) + (emits var) + (when init + (emits " = " + (if-let [define (get-define mname jsdoc)] + define + init))) + (when (:def-emits-var env) + (emitln "; return (") + (emits (merge + {:op :var-special + :env (assoc env :context :expr)} + var-ast)) + (emitln ");})()") + (when (= :return (:context env)) + (emitln ")"))) + ;; NOTE: JavaScriptCore does not like this under advanced compilation + ;; this change was primarily for REPL interactions - David + ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") + (when-not (= :expr (:context env)) (emitln ";")) + (when export + (emitln "goog.exportSymbol('" (munge export) "', " mname ");")) + (when (and ana/*load-tests* test) + (when (= :expr (:context env)) + (emitln ";")) + (emitln var ".cljs$lang$test = " test ";"))))) (defn emit-apply-to [{:keys [name params env]}] From d2d031605b1ad552077218c8f445868653c01744 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Nov 2015 07:03:04 -0500 Subject: [PATCH 1634/4033] revert murmur3 hashing changes, created new DCE issues --- src/main/cljs/cljs/core.cljs | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index dde0bf52e..0dc6b0d4c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1198,13 +1198,8 @@ (next coll)) (mix-collection-hash hash-code n)))) -(def ^{:private true :jsdoc ["@type {*}"]} - EMPTY_ORDERED_HASH nil) - -(defn- empty-ordered-hash [] - (when (nil? EMPTY_ORDERED_HASH) - (set! EMPTY_ORDERED_HASH (mix-collection-hash 1 0))) - EMPTY_ORDERED_HASH) +(def ^:private empty-ordered-hash + (mix-collection-hash 1 0)) (defn ^number hash-unordered-coll "Returns the hash code, consistent with =, for an external unordered @@ -1218,13 +1213,8 @@ (recur (inc n) (bit-or (+ hash-code (hash (first coll))) 0) (next coll)) (mix-collection-hash hash-code n)))) -(def ^{:private true :jsdoc ["@type {*}"]} - EMPTY_UNORDERED_HASH nil) - -(defn- empty-unordered-hash [] - (when (nil? EMPTY_UNORDERED_HASH) - (set! EMPTY_UNORDERED_HASH (mix-collection-hash 0 0))) - EMPTY_UNORDERED_HASH) +(def ^:private empty-unordered-hash + (mix-collection-hash 0 0)) ;;;;;;;;;;;;;;;;;;; protocols on primitives ;;;;;;;; (declare hash-map list equiv-sequential) @@ -2811,7 +2801,7 @@ reduces them without incurring seq initialization" false)) IHash - (-hash [coll] (empty-ordered-hash)) + (-hash [coll] empty-ordered-hash) ISeqable (-seq [coll] nil) @@ -4926,7 +4916,7 @@ reduces them without incurring seq initialization" (set! (.-EMPTY-NODE PersistentVector) (VectorNode. nil (make-array 32))) (set! (.-EMPTY PersistentVector) - (PersistentVector. nil 0 5 (.-EMPTY-NODE PersistentVector) (array) (empty-ordered-hash))) + (PersistentVector. nil 0 5 (.-EMPTY-NODE PersistentVector) (array) empty-ordered-hash)) (set! (.-fromArray PersistentVector) (fn [xs ^boolean no-clone] @@ -5476,7 +5466,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-ordered-hash)) (es6-iterable PersistentQueue) @@ -5653,7 +5643,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (transient (into (hash-map) coll)))) -(set! (.-EMPTY ObjMap) (ObjMap. nil (array) (js-obj) 0 (empty-unordered-hash))) +(set! (.-EMPTY ObjMap) (ObjMap. nil (array) (js-obj) 0 empty-unordered-hash)) (set! (.-HASHMAP_THRESHOLD ObjMap) 8) @@ -6005,7 +5995,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientArrayMap. (js-obj) (alength arr) (aclone arr)))) -(set! (.-EMPTY PersistentArrayMap) (PersistentArrayMap. nil 0 (array) (empty-unordered-hash))) +(set! (.-EMPTY PersistentArrayMap) (PersistentArrayMap. nil 0 (array) empty-unordered-hash)) (set! (.-HASHMAP-THRESHOLD PersistentArrayMap) 8) @@ -6941,7 +6931,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientHashMap. (js-obj) root cnt has-nil? nil-val))) -(set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil (empty-unordered-hash))) +(set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil empty-unordered-hash)) (set! (.-fromArray PersistentHashMap) (fn [arr ^boolean no-clone] @@ -7734,7 +7724,7 @@ reduces them without incurring seq initialization" (-comparator [coll] comp)) -(set! (.-EMPTY PersistentTreeMap) (PersistentTreeMap. compare nil 0 nil (empty-unordered-hash))) +(set! (.-EMPTY PersistentTreeMap) (PersistentTreeMap. compare nil 0 nil empty-unordered-hash)) (es6-iterable PersistentTreeMap) @@ -8055,7 +8045,7 @@ reduces them without incurring seq initialization" (-as-transient [coll] (TransientHashSet. (-as-transient hash-map)))) (set! (.-EMPTY PersistentHashSet) - (PersistentHashSet. nil (.-EMPTY PersistentArrayMap) (empty-unordered-hash))) + (PersistentHashSet. nil (.-EMPTY PersistentArrayMap) empty-unordered-hash)) (set! (.-fromArray PersistentHashSet) (fn [items ^boolean no-clone] @@ -8201,7 +8191,7 @@ reduces them without incurring seq initialization" (-lookup coll k not-found))) (set! (.-EMPTY PersistentTreeSet) - (PersistentTreeSet. nil (.-EMPTY PersistentTreeMap) (empty-unordered-hash))) + (PersistentTreeSet. nil (.-EMPTY PersistentTreeMap) empty-unordered-hash)) (es6-iterable PersistentTreeSet) From 72c9dfdf72e62fa2a030e91b27859ff5cca222ca Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 9 Nov 2015 12:31:38 +0100 Subject: [PATCH 1635/4033] split sm/encode into 2 functions so JSON generation is optional --- src/main/clojure/cljs/source_map.clj | 71 +++++++++++++++------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/main/clojure/cljs/source_map.clj b/src/main/clojure/cljs/source_map.clj index 914fb3b87..e2b881d9a 100644 --- a/src/main/clojure/cljs/source_map.clj +++ b/src/main/clojure/cljs/source_map.clj @@ -214,10 +214,9 @@ .toURI)] (str (.relativize source-map-parent-juri unrelativized-juri)))))) -(defn encode +(defn encode* "Take an internal source map representation represented as nested - sorted maps of file, line, column and return a source map v3 JSON - string." + sorted maps of file, line, column and return a v3 representation." [m opts] (let [lines (atom [[]]) names->idx (atom {}) @@ -252,35 +251,43 @@ (doseq [[line cols] lines] (doseq [[col infos] cols] (encode-cols infos source-idx line col)))) - (let [source-map-file-contents - (cond-> {"version" 3 - "file" (:file opts) - "sources" (into [] - (let [paths (keys m) - f (comp - (if (true? (:source-map-timestamp opts)) - #(str % "?rel=" (System/currentTimeMillis)) - identity) - (if (or (:output-dir opts) - (:source-map-path opts)) - #(relativize-path % opts) - #(last (string/split % #"/"))))] - (map f paths))) - "lineCount" (:lines opts) - "mappings" (->> (lines->segs (concat preamble-lines @lines)) - (map #(string/join "," %)) - (string/join ";")) - "names" (into [] - (map (set/map-invert @names->idx) - (range (count @names->idx))))} - (:sources-content opts) - (assoc "sourcesContent" (:sources-content opts)))] - (if (true? (:source-map-pretty-print opts)) - (with-out-str - (json/pprint - source-map-file-contents - :escape-slash false)) - (json/write-str source-map-file-contents))))) + + (cond-> {"version" 3 + "file" (:file opts) + "sources" (into [] + (let [paths (keys m) + f (comp + (if (true? (:source-map-timestamp opts)) + #(str % "?rel=" (System/currentTimeMillis)) + identity) + (if (or (:output-dir opts) + (:source-map-path opts)) + #(relativize-path % opts) + #(last (string/split % #"/"))))] + (map f paths))) + "lineCount" (:lines opts) + "mappings" (->> (lines->segs (concat preamble-lines @lines)) + (map #(string/join "," %)) + (string/join ";")) + "names" (into [] + (map (set/map-invert @names->idx) + (range (count @names->idx))))} + + (:sources-content opts) + (assoc "sourcesContent" (:sources-content opts))))) + +(defn encode + "Take an internal source map representation represented as nested + sorted maps of file, line, column and return a source map v3 JSON + string." + [m opts] + (let [source-map-file-contents (encode* m opts)] + (if (true? (:source-map-pretty-print opts)) + (with-out-str + (json/pprint + source-map-file-contents + :escape-slash false)) + (json/write-str source-map-file-contents)))) ;; ----------------------------------------------------------------------------- ;; Merging From f28b1cdb0c501f30f76e3cd2f3e07311ef660e7a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Nov 2015 09:56:29 -0500 Subject: [PATCH 1636/4033] CLJS-1330: self-host: .toString on int needs parens --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/self/self_host/test.cljs | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 3a48b26be..30b32b698 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -213,7 +213,7 @@ #?(:clj (defmethod emit-constant Double [x] (emits x)) :cljs - (defmethod emit-constant js/Number [x] (emits x))) + (defmethod emit-constant js/Number [x] (emits "(" x ")"))) #?(:clj (defmethod emit-constant BigDecimal [x] (emits (.doubleValue ^BigDecimal x)))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index bf6c0c5d0..888db6fc0 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -71,7 +71,7 @@ (cljs/compile-str st "(+ 1 1)" (fn [{:keys [error value]}] (is (nil? error)) - (is (= "(1 + 1);\n" value)) + (is (= "((1) + (1));\n" value)) (inc! l))) (cljs/compile-str st "(fn [])" nil {:context :expr} @@ -83,7 +83,7 @@ {:context :expr} (fn [{:keys [error value]}] (is (nil? error)) - (is (= "(cljs.core.truth_(cljs.core.first)?1:2)" value)) + (is (= "(cljs.core.truth_(cljs.core.first)?(1):(2))" value)) (inc! l))) (cljs/compile-str st "(.toString \"a\")" nil {:context :expr} @@ -95,14 +95,14 @@ {:context :expr} (fn [{:keys [error value]}] (is (nil? error)) - (is (string/index-of value "cljs.user.foo.call(null,1,2)")) + (is (string/index-of value "cljs.user.foo.call(null,(1),(2))")) (inc! l))) (cljs/compile-str st "(do (defn foo [a b] (+ a b)) (foo 1 2))" nil {:context :expr :static-fns true} (fn [{:keys [error value]}] (is (nil? error)) - (is (string/index-of value "cljs.user.foo(1,2)")) + (is (string/index-of value "cljs.user.foo((1),(2))")) (inc! l)))))) (deftest test-eval-str @@ -197,6 +197,14 @@ (is (nil? error)) (inc! l)))))) +(deftest test-CLJS-1330 + (cljs/eval-str st + "(.toString 1)" + nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (= "1" value))))) + #_(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] From 3e1af212e114a9968c6f69719b0346df12bd6d0e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Nov 2015 10:54:40 -0500 Subject: [PATCH 1637/4033] CLJS-1236: `constructor` needs to munged if used as namespace segment --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 630185ebf..437c8a007 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -104,7 +104,7 @@ "synchronized" "this" "throw" "throws" "transient" "try" "typeof" "var" "void" "volatile" "while" "with" "yield" "methods" - "null"}) + "null" "constructor"}) #?(:clj (def SENTINEL (Object.)) :cljs (def SENTINEL (js-obj))) From 2e43d87d45e36fad4652185b9df6c248eb120e1e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 14 Nov 2015 17:29:39 -0500 Subject: [PATCH 1638/4033] first pass at experimental support for parallel builds --- src/main/clojure/cljs/closure.clj | 73 ++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9a03c3773..d6104c096 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -47,7 +47,7 @@ [java.net URL] [java.util.logging Level] [java.util List Random] - [java.util.concurrent TimeUnit] + [java.util.concurrent TimeUnit LinkedBlockingDeque] [com.google.javascript.jscomp CompilerOptions CompilationLevel CompilerOptions$LanguageMode SourceMap$Format SourceMap$DetailLevel ClosureCodingConvention SourceFile @@ -741,21 +741,70 @@ (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) inputs)) +(defn compile-task [^LinkedBlockingDeque deque input-set compiled opts] + (loop [ns-info (.pollFirst deque)] + (when ns-info + (let [{:keys [requires]} ns-info + input-set' @input-set] + (if (every? #(not (contains? input-set' %)) requires) + (do + (try + (swap! compiled conj + (-compile (or (:source-file ns-info) + (:source-forms ns-info)) + ; - ns-info -> ns -> cljs file relpath -> js relpath + (merge opts + {:output-file (comp/rename-to-js + (util/ns->relpath (:ns ns-info)))}))) + (catch Throwable e + (util/debug-prn e))) + (when-let [ns (:ns ns-info)] + (swap! input-set disj ns)) + (recur (.pollFirst deque))) + (do + (Thread/sleep 10) + (recur ns-info))))))) + +(defn parallel-compile-sources [inputs compiler-stats opts] + (let [deque (LinkedBlockingDeque. (count inputs)) + input-set (atom #{}) + cnt (+ 2 (.. Runtime getRuntime availableProcessors)) + agents (repeatedly cnt + #(agent nil + :error-handler + (fn [err] + (util/debug-prn err)))) + compiled (atom [])] + (doseq [ns-info (reverse inputs)] + (when-let [ns (:ns ns-info)] + (swap! input-set conj ns)) + (.push deque ns-info)) + (doseq [agent agents] + (send agent + (fn [agent] + (compile-task deque input-set compiled opts) + agent))) + (util/measure compiler-stats + "Compile sources" (apply await agents)) + @compiled)) + (defn compile-sources "Takes dependency ordered list of IJavaScript compatible maps from parse-ns and compiles them." [inputs compiler-stats opts] - (util/measure compiler-stats - "Compile sources" - (binding [comp/*inputs* (zipmap (map :ns inputs) inputs)] - (doall - (for [ns-info inputs] - ; TODO: compile-file calls parse-ns unnecessarily to get ns-info - ; TODO: we could mark dependent namespaces for recompile here - (-compile (or (:source-file ns-info) - (:source-forms ns-info)) - ; - ns-info -> ns -> cljs file relpath -> js relpath - (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))))))) + (if (:parallel-build opts) + (parallel-compile-sources inputs compiler-stats opts) + (util/measure compiler-stats + "Compile sources" + (binding [comp/*inputs* (zipmap (map :ns inputs) inputs)] + (doall + (for [ns-info inputs] + ; TODO: compile-file calls parse-ns unnecessarily to get ns-info + ; TODO: we could mark dependent namespaces for recompile here + (-compile (or (:source-file ns-info) + (:source-forms ns-info)) + ; - ns-info -> ns -> cljs file relpath -> js relpath + (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))})))))))) (defn add-goog-base [inputs] From e8efc64d1f47aa923010031e96f6c2d94e92edff Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Nov 2015 10:35:04 -0500 Subject: [PATCH 1639/4033] cleanup parallel build logic, ad real failure handling --- src/main/clojure/cljs/closure.clj | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d6104c096..bdd9cbca0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -741,9 +741,9 @@ (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) inputs)) -(defn compile-task [^LinkedBlockingDeque deque input-set compiled opts] +(defn compile-task [^LinkedBlockingDeque deque input-set compiled opts failed] (loop [ns-info (.pollFirst deque)] - (when ns-info + (when (and ns-info (not @failed)) (let [{:keys [requires]} ns-info input-set' @input-set] (if (every? #(not (contains? input-set' %)) requires) @@ -757,32 +757,31 @@ {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))) (catch Throwable e - (util/debug-prn e))) - (when-let [ns (:ns ns-info)] - (swap! input-set disj ns)) - (recur (.pollFirst deque))) + (util/debug-prn e) + (reset! failed true))) + (when-not @failed + (when-let [ns (:ns ns-info)] + (swap! input-set disj ns)) + (recur (.pollFirst deque)))) (do (Thread/sleep 10) (recur ns-info))))))) (defn parallel-compile-sources [inputs compiler-stats opts] - (let [deque (LinkedBlockingDeque. (count inputs)) - input-set (atom #{}) + (let [deque (LinkedBlockingDeque. inputs) + input-set (atom (into #{} (comp (remove nil?) (map :ns)) inputs)) cnt (+ 2 (.. Runtime getRuntime availableProcessors)) agents (repeatedly cnt #(agent nil :error-handler (fn [err] (util/debug-prn err)))) - compiled (atom [])] - (doseq [ns-info (reverse inputs)] - (when-let [ns (:ns ns-info)] - (swap! input-set conj ns)) - (.push deque ns-info)) + compiled (atom []) + failed (atom false)] (doseq [agent agents] (send agent (fn [agent] - (compile-task deque input-set compiled opts) + (compile-task deque input-set compiled opts failed) agent))) (util/measure compiler-stats "Compile sources" (apply await agents)) From c98c0ee1cbdfe0192ac08993b2a85f27fa999347 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Nov 2015 11:12:02 -0500 Subject: [PATCH 1640/4033] enable parallel builds for test and self host tests --- script/test | 2 +- script/test-self-host | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/test b/script/test index 4b64b1b5b..91eb08754 100755 --- a/script/test +++ b/script/test @@ -8,7 +8,7 @@ mkdir -p builds/out-adv possible=4 ran=0 -bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js +bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" diff --git a/script/test-self-host b/script/test-self-host index 45b32d044..06fe23fff 100755 --- a/script/test-self-host +++ b/script/test-self-host @@ -4,7 +4,7 @@ rm -rf builds/out-self mkdir -p builds/out-self -bin/cljsc src/test/self/self_host "{:optimizations :simple :static-fns true :output-dir \"builds/out-self\" :optimize-constants true :verbose true :compiler-stats true :target :nodejs}" > builds/out-self/core-self-test.js +bin/cljsc src/test/self/self_host "{:optimizations :simple :static-fns true :output-dir \"builds/out-self\" :optimize-constants true :verbose true :compiler-stats true :parallel-build true :target :nodejs}" > builds/out-self/core-self-test.js echo "Testing with Node" node builds/out-self/core-self-test.js From 3efab99b2112697d413b648e56cc18eefcaf835b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Nov 2015 13:27:27 -0500 Subject: [PATCH 1641/4033] only use Java primitives for parallel builds to avoid bad interactions with downstream tooling (Figwheel etc.) --- src/main/clojure/cljs/closure.clj | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index bdd9cbca0..329383038 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -47,7 +47,8 @@ [java.net URL] [java.util.logging Level] [java.util List Random] - [java.util.concurrent TimeUnit LinkedBlockingDeque] + [java.util.concurrent + TimeUnit LinkedBlockingDeque Executors CountDownLatch] [com.google.javascript.jscomp CompilerOptions CompilationLevel CompilerOptions$LanguageMode SourceMap$Format SourceMap$DetailLevel ClosureCodingConvention SourceFile @@ -771,20 +772,16 @@ (let [deque (LinkedBlockingDeque. inputs) input-set (atom (into #{} (comp (remove nil?) (map :ns)) inputs)) cnt (+ 2 (.. Runtime getRuntime availableProcessors)) - agents (repeatedly cnt - #(agent nil - :error-handler - (fn [err] - (util/debug-prn err)))) + latch (CountDownLatch. cnt) + es (Executors/newFixedThreadPool cnt) compiled (atom []) failed (atom false)] - (doseq [agent agents] - (send agent - (fn [agent] + (dotimes [_ cnt] + (.execute es + (bound-fn [] (compile-task deque input-set compiled opts failed) - agent))) - (util/measure compiler-stats - "Compile sources" (apply await agents)) + (.countDown latch)))) + (util/measure compiler-stats "Compile sources" (.await latch)) @compiled)) (defn compile-sources From 14b7b5930c97c90736cef8e6467507b4010b150f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 19 Nov 2015 15:11:32 -0500 Subject: [PATCH 1642/4033] shutdown the thread pool --- src/main/clojure/cljs/closure.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 329383038..5980cb3cc 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -782,6 +782,7 @@ (compile-task deque input-set compiled opts failed) (.countDown latch)))) (util/measure compiler-stats "Compile sources" (.await latch)) + (.shutdown es) @compiled)) (defn compile-sources From e9c9c085a3d67ad8f7ab4aa2a8646ac1525f15c4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Nov 2015 16:34:21 -0500 Subject: [PATCH 1643/4033] add 2 arg compile-sources --- src/main/clojure/cljs/closure.clj | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5980cb3cc..b85a03767 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -788,20 +788,22 @@ (defn compile-sources "Takes dependency ordered list of IJavaScript compatible maps from parse-ns and compiles them." - [inputs compiler-stats opts] - (if (:parallel-build opts) - (parallel-compile-sources inputs compiler-stats opts) - (util/measure compiler-stats - "Compile sources" - (binding [comp/*inputs* (zipmap (map :ns inputs) inputs)] - (doall - (for [ns-info inputs] - ; TODO: compile-file calls parse-ns unnecessarily to get ns-info - ; TODO: we could mark dependent namespaces for recompile here - (-compile (or (:source-file ns-info) - (:source-forms ns-info)) - ; - ns-info -> ns -> cljs file relpath -> js relpath - (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))})))))))) + ([inputs opts] + (compile-sources inputs (:compiler-stats opts) opts)) + ([inputs compiler-stats opts] + (if (:parallel-build opts) + (parallel-compile-sources inputs compiler-stats opts) + (util/measure compiler-stats + "Compile sources" + (binding [comp/*inputs* (zipmap (map :ns inputs) inputs)] + (doall + (for [ns-info inputs] + ; TODO: compile-file calls parse-ns unnecessarily to get ns-info + ; TODO: we could mark dependent namespaces for recompile here + (-compile (or (:source-file ns-info) + (:source-forms ns-info)) + ; - ns-info -> ns -> cljs file relpath -> js relpath + (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))))))))) (defn add-goog-base [inputs] From d63c22373ddaa97d62704038190d852ac2ed6ed7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Nov 2015 16:43:33 -0500 Subject: [PATCH 1644/4033] formatting --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b85a03767..060f75fbc 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -801,7 +801,7 @@ ; TODO: compile-file calls parse-ns unnecessarily to get ns-info ; TODO: we could mark dependent namespaces for recompile here (-compile (or (:source-file ns-info) - (:source-forms ns-info)) + (:source-forms ns-info)) ; - ns-info -> ns -> cljs file relpath -> js relpath (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))))))))) From 39938634352f2e5ffe6cfd581dbaa9890d057b7f Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Sun, 1 Nov 2015 18:36:04 -0500 Subject: [PATCH 1645/4033] CLJS-1477: Do not attempt to resolve "native" type symbols - The analyzer tags expressions with "native" JavaScript types with unqualified symbols (e.g., `'string`, `'array`). - When the compiler encounters a protocol method invocation, it attempts to resolve the first argument's type tag to locate the protocol implementation. This does not behave as expected for these native types (see also `cljs.core/base-type`): - `clj-nil` - `number` - `string` - `boolean` - `function` - `object` - `array` - This could similarly affect other type tags: - `any` - `clj` - `clj-or-nil` - `seq` - `not-native` - `long` - `double` Add tests demonstrating unexpected `undeclared Var` warning --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/clojure/cljs/compiler_tests.clj | 47 +++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 30b32b698..bdc16b594 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -930,7 +930,7 @@ (or (= protocol tag) ;; ignore new type hints for now - David (and (not (set? tag)) - (not ('#{any clj clj-or-nil} tag)) + (not ('#{any clj clj-or-nil clj-nil number string boolean function object array} tag)) (when-let [ps (:protocols (ana/resolve-existing-var (dissoc env :locals) tag))] (ps protocol))))))) opt-not? (and (= (:name info) 'cljs.core/not) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 4e72e7b4a..baff78e42 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -3,7 +3,8 @@ (:require [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.env :as env] - [cljs.util :as util]) + [cljs.util :as util] + [cljs.tagged-literals :as tags]) (:import [java.io File])) (def aenv (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) @@ -87,6 +88,50 @@ '(defn foo ([a]) ([a b]))))) ) +(defn capture-warnings* [f] + (let [capture (atom []) + tracker (fn [warning-type env & [extra]] + (when (warning-type ana/*cljs-warnings*) + (let [err (ana/error-message warning-type extra) + msg (ana/message env (str "WARNING: " err))] + (swap! capture conj [warning-type msg]))))] + (ana/with-warning-handlers [tracker] + (f)) + @capture)) + +(defmacro capture-warnings [& body] + `(capture-warnings* (fn [] ~@body))) + +(deftest no-warn-on-emit-invoke-protocol-method + (let [define-foo #(assoc-in % [::ana/namespaces 'cljs.user :defs 'foo] + {:ns 'cljs.user + :name 'cljs.user/foo + :fn-var true + :method-params '([x]) + :protocol 'cljs.user/Foo}) + aenv-with-foo (define-foo aenv) + cenv-with-foo (define-foo @cenv)] + (binding [ana/*cljs-static-fns* true] + (are [form] + (empty? + (capture-warnings + (env/with-compiler-env (atom cenv-with-foo) + (with-out-str + (comp/emit + (ana/analyze aenv-with-foo form)))))) + + '(cljs.user/foo nil) + '(cljs.user/foo 0) + '(cljs.user/foo (inc 0)) + '(cljs.user/foo "") + '(cljs.user/foo true) + '(cljs.user/foo false) + '(cljs.user/foo (nil? nil)) + '(cljs.user/foo (fn [x] x)) + `(cljs.user/foo ~(tags/->JSValue {})) + `(cljs.user/foo ~(tags/->JSValue [])) + '(cljs.user/foo (make-array 0)))))) + ;; CLJS-1225 (comment From a5c5f4b119290d05115fa91775c8cb05043e4c7e Mon Sep 17 00:00:00 2001 From: Daniel Compton Date: Wed, 25 Nov 2015 11:53:38 +1300 Subject: [PATCH 1646/4033] Fix test warnings about single segment weeble namespaces --- src/test/clojure/cljs/analyzer_tests.clj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 1486b9a9d..5bc360836 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -277,32 +277,32 @@ (deftest test-namespace-metadata (binding [a/*cljs-ns* a/*cljs-ns*] - (is (= (do (a/analyze ns-env '(ns weeble {:foo bar})) + (is (= (do (a/analyze ns-env '(ns weeble.ns {:foo bar})) (meta a/*cljs-ns*)) {:foo 'bar})) - (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble)) + (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble.ns)) (meta a/*cljs-ns*)) {:foo 'bar})) - (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble {:baz quux})) + (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble.ns {:baz quux})) (meta a/*cljs-ns*)) {:foo 'bar :baz 'quux})) - (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble {:foo baz})) + (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble.ns {:foo baz})) (meta a/*cljs-ns*)) {:foo 'baz})) - (is (= (meta (:name (a/analyze ns-env '(ns weeble {:foo bar})))) + (is (= (meta (:name (a/analyze ns-env '(ns weeble.ns {:foo bar})))) {:foo 'bar})) - (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble)))) + (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble.ns)))) {:foo 'bar})) - (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble {:baz quux})))) + (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble.ns {:baz quux})))) {:foo 'bar :baz 'quux})) - (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble {:foo baz})))) + (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble.ns {:foo baz})))) {:foo 'baz})))) (deftest test-cljs-1105 From f73c426711bc36451b2d339e622569d06aa169b2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 1 Dec 2015 08:09:22 -0500 Subject: [PATCH 1647/4033] CLJS-1498: Fix parallel build logging put lock around debug logging --- src/main/clojure/cljs/util.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 05a9ff274..23882da1d 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -174,10 +174,14 @@ (defn valid-js-id-start? [s] (re-find #"(?U)^[\p{Alpha}_$]" s)) +(def debug-prn-mutex (Object.)) + (defn debug-prn [& args] (binding [*out* *err*] - (apply println args))) + (locking debug-prn-mutex + (apply println args) + (flush)))) (defmacro measure "Like cljs.core/time but toggleable and takes a message string." From 8d50cd89ed8a773a540ebe8f71aa7c66cdb87c77 Mon Sep 17 00:00:00 2001 From: Roman Scherer Date: Tue, 1 Dec 2015 21:58:57 +0100 Subject: [PATCH 1648/4033] Fix module compilation when modules aren't required --- src/main/clojure/cljs/closure.clj | 23 ++++++++++++++++----- src/test/cljs/module_test/main.cljs | 4 ++++ src/test/cljs/module_test/modules/a.cljs | 4 ++++ src/test/cljs/module_test/modules/b.cljs | 4 ++++ src/test/clojure/cljs/build_api_tests.clj | 25 +++++++++++++++++++++++ 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 src/test/cljs/module_test/main.cljs create mode 100644 src/test/cljs/module_test/modules/a.cljs create mode 100644 src/test/cljs/module_test/modules/b.cljs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 060f75fbc..828b1f43f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -41,6 +41,7 @@ [cljs.env :as env] [cljs.js-deps :as deps] [clojure.java.io :as io] + [clojure.set :as set] [clojure.string :as string] [clojure.data.json :as json]) (:import [java.io File BufferedInputStream StringWriter] @@ -725,13 +726,25 @@ (find-cljs-dependencies ["cljs.core" "clojure.string"]) ) +(defn- module-entries + "Return the module entries of `compile-opts` as a set." + [compile-opts] + (->> compile-opts :modules vals + (map :entries) + (remove nil?) + (apply concat) + (set))) + (defn add-dependency-sources "Given list of IJavaScript objects, produce a new sequence of IJavaScript objects of all dependencies of inputs." - [inputs] - (let [inputs (set inputs) - requires (set (mapcat deps/-requires inputs))] - (into inputs (find-cljs-dependencies requires)))) + ([inputs] + (add-dependency-sources inputs nil)) + ([inputs compile-opts] + (let [inputs (set inputs) + requires (set (mapcat deps/-requires inputs)) + module-entries (module-entries compile-opts)] + (into inputs (find-cljs-dependencies (set/union requires module-entries)))))) (defn check-unprovided [inputs] @@ -1850,7 +1863,7 @@ (assoc all-opts :output-file (:output-to all-opts)) all-opts) js-sources (-> (-find-sources source all-opts) - add-dependency-sources + (add-dependency-sources compile-opts) deps/dependency-order (compile-sources compiler-stats compile-opts) (add-js-sources all-opts) diff --git a/src/test/cljs/module_test/main.cljs b/src/test/cljs/module_test/main.cljs new file mode 100644 index 000000000..f44315e4f --- /dev/null +++ b/src/test/cljs/module_test/main.cljs @@ -0,0 +1,4 @@ +(ns module-test.main) + +(defn ^:export main [] + (println "Loading modules A and B ...")) diff --git a/src/test/cljs/module_test/modules/a.cljs b/src/test/cljs/module_test/modules/a.cljs new file mode 100644 index 000000000..99e7fd02e --- /dev/null +++ b/src/test/cljs/module_test/modules/a.cljs @@ -0,0 +1,4 @@ +(ns module-test.modules.a) + +(defn ^:export main [] + (println "Module A loaded.")) diff --git a/src/test/cljs/module_test/modules/b.cljs b/src/test/cljs/module_test/modules/b.cljs new file mode 100644 index 000000000..153319fe6 --- /dev/null +++ b/src/test/cljs/module_test/modules/b.cljs @@ -0,0 +1,4 @@ +(ns module-test.modules.b) + +(defn ^:export main [] + (println "Module B loaded.")) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index f52e35ccb..870e59996 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -120,3 +120,28 @@ (build srcs opts) (is (not (every? #(zero? (.length %)) [common-tmp app-tmp])) "The files are not empty after compilation"))) + +(deftest cljs-1500-test-modules + (let [module-main (io/file "out/module-main.js") + module-a (io/file "out/module-a.js") + module-b (io/file "out/module-b.js")] + (.delete module-main) + (.delete module-a) + (.delete module-b) + (build + (inputs "src/test/cljs") + {:main "module-test.main" + :optimizations :advanced + :verbose true + :modules + {:cljs-base + {:output-to (str module-main)} + :module-a + {:output-to (str module-a) + :entries #{'module-test.modules.a}} + :module-b + {:output-to (str module-b) + :entries #{'module-test.modules.b}}}}) + (is (re-find #"Loading modules A and B" (slurp module-main))) + (is (re-find #"Module A loaded" (slurp module-a))) + (is (re-find #"Module B loaded" (slurp module-b))))) From c73f4ee6a538b52b2fb3cf829777590fc3046193 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 2 Dec 2015 16:37:50 -0500 Subject: [PATCH 1649/4033] drop `new` from README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c93786d92..5c37b2a05 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## What is ClojureScript? ## -ClojureScript is a new compiler for [Clojure](http://clojure.org) that targets JavaScript. It is designed to emit JavaScript code which is compatible with the advanced compilation mode of the [Google Closure](http://code.google.com/closure/) optimizing compiler. +ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaScript. It is designed to emit JavaScript code which is compatible with the advanced compilation mode of the [Google Closure](http://code.google.com/closure/) optimizing compiler. ## Releases and dependency information ## From 738edb18ba197b81a244a3aeaa46f3dea0028b2c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 4 Dec 2015 09:47:34 -0500 Subject: [PATCH 1650/4033] include cljs.core in cljs.closure so that the macro ns gets AOTed --- src/main/clojure/cljs/closure.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 828b1f43f..f6584613d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -35,6 +35,7 @@ " (:refer-clojure :exclude [compile]) (:require [cljs.util :as util] + [cljs.core :as cljsm] [cljs.compiler :as comp] [cljs.analyzer :as ana] [cljs.source-map :as sm] From 16d5f2abcb3a8fde284124348795ca28e62f32b4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 5 Dec 2015 15:27:13 -0500 Subject: [PATCH 1651/4033] enhance missing var :refer error always include file information with errors when available --- src/main/clojure/cljs/analyzer.cljc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 437c8a007..9dca5c156 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -265,7 +265,7 @@ (defmethod error-message :undeclared-ns-form [warning-type info] - (str "Referred " (:type info) " " (:lib info) "/" (:sym info) " does not exist")) + (str "Invalid :refer, var " (:type info) " " (:lib info) "/" (:sym info) " does not exist")) (defmethod error-message :protocol-deprecated [warning-type info] @@ -554,8 +554,11 @@ :column (get-col name env)})) (defn message [env s] - (str s (when (:line env) - (str " at line " (:line env) " " *cljs-file*)))) + (str s + (if (:line env) + (str " at line " (:line env) " " *cljs-file*) + (when *cljs-file* + (str " in file " *cljs-file*))))) (defn warning [warning-type env extra] (doseq [handler *cljs-warning-handlers*] From e33e554331c555952079d284d822cf16219ac56a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 7 Dec 2015 15:04:38 -0500 Subject: [PATCH 1652/4033] CLJS-1504: Self-host: Pseudo-namespace for macro namespace analysis metadata Fix typo wrt using `:macros-ns` key to determine if a macro namespace is being processed. Store calculated macro-ns-name separately and use it in places where it is used as a key into the analysis cache, while retaining and using original name otherwise (for forming ns->relpath calculations, for example.) --- src/main/cljs/cljs/js.cljs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 6ef4fa39e..47a081de2 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -217,14 +217,14 @@ :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} bound-vars) - name (cond-> name (:macro-ns opts) ana/macro-ns-name)] + aname (cond-> name (:macros-ns opts) ana/macro-ns-name)] (when (= :reload reload) - (swap! *loaded* disj name)) + (swap! *loaded* disj aname)) (when (= :reload-all reload) (reset! *loaded* #{})) (when (:verbose opts) (debug-prn (str "Loading " name (when (:macros-ns opts) " macros") " namespace"))) - (if-not (contains? @*loaded* name) + (if-not (contains? @*loaded* aname) (let [env (:*env* bound-vars)] (try ((:*load-fn* bound-vars) @@ -242,7 +242,7 @@ (if (:error res) (cb res) (do - (swap! *loaded* conj name) + (swap! *loaded* conj aname) (cb {:value true}))))) :js (process-macros-deps bound-vars cache opts (fn [res] @@ -256,10 +256,10 @@ ((:*eval-fn* bound-vars) resource) (when cache (load-analysis-cache! - (:*compiler* bound-vars) name cache)) + (:*compiler* bound-vars) aname cache)) (when source-map (load-source-map! - (:*compiler* bound-vars) name source-map)) + (:*compiler* bound-vars) aname source-map)) (catch :default cause (wrap-error (ana/error env @@ -691,7 +691,8 @@ sb (StringBuffer.) the-ns (or (:ns opts) 'cljs.user) bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) - (:source-map opts) (assoc :*sm-data* (sm-data)))] + (:source-map opts) (assoc :*sm-data* (sm-data))) + aname (cond-> name (:macros-ns opts) ana/macro-ns-name)] (when (:verbose opts) (debug-prn "Evaluating" name)) ((fn compile-loop [ns] (binding [env/*compiler* (:*compiler* bound-vars) @@ -740,13 +741,13 @@ (do (when (:source-map opts) (append-source-map env/*compiler* - name source sb @comp/*source-map-data* opts)) + aname source sb @comp/*source-map-data* opts)) (let [js-source (.toString sb) evalm {:lang :clj :name name :path (ns->relpath name) :source js-source - :cache (get-in @env/*compiler* [::ana/namespaces name])} + :cache (get-in @env/*compiler* [::ana/namespaces aname])} complete (fn [res] (if (:error res) (cb res) From 53fd5991a41f49e7dffdd891c445f48f0835e223 Mon Sep 17 00:00:00 2001 From: Daniel Compton Date: Wed, 25 Nov 2015 10:18:11 +1300 Subject: [PATCH 1653/4033] CLJS-1491: Check :source-map is boolean when :optimizations :none When :optimizations are :none, :source-map can only be true or false. This patch adds a check for that, and tests to cover the new functionality. --- src/main/clojure/cljs/closure.clj | 9 +++++++-- src/main/clojure/cljs/util.cljc | 3 +++ src/test/clojure/cljs/closure_tests.clj | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f6584613d..398732362 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1694,10 +1694,10 @@ (pr-str output-dir)))) true) -(defn check-source-map [{:keys [output-to source-map output-dir] :as opts}] +(defn check-source-map [{:keys [output-to source-map output-dir optimizations] :as opts}] "When :source-map is specified in opts, " (when (and (contains? opts :source-map) - (not (= (:optimizations opts) :none))) + (not (= optimizations :none))) (assert (and (or (contains? opts :output-to) (contains? opts :modules)) (contains? opts :output-dir)) @@ -1718,6 +1718,11 @@ "parent %s if optimization setting applied") (pr-str output-dir) (pr-str (absolute-parent output-to))))) + (when (and (contains? opts :source-map) + (= optimizations :none)) + (assert (util/boolean? source-map) + (format ":source-map must be true or false when compiling with :optimizations :none but it is: %s" + (pr-str source-map)))) true) (defn check-source-map-path [{:keys [source-map-path] :as opts}] diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 23882da1d..bbf6707f4 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -202,3 +202,6 @@ (catch Throwable _ false)) `(do ~then) `(do ~else)))) + +(defn boolean? [x] + (or (true? x) (false? x))) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index a5312e8f2..a0ebfed38 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -28,3 +28,9 @@ (is (= "var preamble1 = require(\"preamble1\");\nvar preamble2 = require(\"preamble2\");\n" (make-preamble {:preamble ["cljs/preamble1.js" "cljs/preamble2.js"]}))))) + +(deftest test-check-sourcemap + (testing "optimizations none" + (is (check-source-map {:source-map true :optimizations :none})) + (is (check-source-map {:source-map false :optimizations :none})) + (is (thrown? AssertionError (check-source-map {:source-map "target/build/app.js.map" :optimizations :none}))))) From 7f97847b3d838a7ee09bcb01adb6be5934bab9ba Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Tue, 8 Dec 2015 01:16:07 -0500 Subject: [PATCH 1654/4033] CLJS-1505: Add tests to characterize `type` and `instance?` behavior - `instance?` is currently implemented in terms of JavaScript's `instanceof` operator, which has some idiosyncrasies (particularly around numbers, strings, and booleans). - Add tests to document the behavior and ensure it is not changed without considering backwards compatibility. --- src/test/cljs/cljs/core_test.cljs | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b1ba40f8e..061cd074a 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1929,6 +1929,45 @@ (-method1 [this] "some doc") (-method2 [this] "")) +(deftest test-type + (is (= nil (type nil))) + (is (= js/Number (type 0))) + (is (= js/Number (type js/NaN))) + (is (= js/Number (type js/Infinity))) + (is (= js/String (type ""))) + (is (= js/Boolean (type true))) + (is (= js/Boolean (type false))) + (is (= js/Function (type identity))) + (is (= js/Function (type (fn [x] x)))) + (is (= js/Object (type (js-obj)))) + (is (= js/Array (type (array)))) + (is (= js/Date (type (js/Date.)))) + (is (= js/Function (type js/Object)))) + +(deftest test-instance? + (is (not (instance? js/Object nil))) + (is (not (instance? js/Number 0))) + (is (not (instance? js/Number js/NaN))) + (is (not (instance? js/Number js/Infinity))) + (is (not (instance? js/String ""))) + (is (not (instance? js/Boolean true))) + (is (not (instance? js/Boolean false))) + (is (instance? js/Number (js/Number. 0))) + (is (instance? js/Object (js/Number. 0))) + (is (instance? js/String (js/String. ""))) + (is (instance? js/Object (js/String. ""))) + (is (instance? js/Boolean (js/Boolean.))) + (is (instance? js/Object (js/Boolean.))) + (is (instance? js/Function identity)) + (is (instance? js/Object identity)) + (is (instance? js/Function (fn [x] x))) + (is (instance? js/Object (js-obj))) + (is (instance? js/Array (array))) + (is (instance? js/Object (array))) + (is (instance? js/Date (js/Date.))) + (is (instance? js/Object (js/Date.))) + (is (instance? js/Function js/Object))) + ;; ============================================================================= ;; Tickets From 865f0a97db263cb538ebc73d3177c6114fd00a85 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 9 Dec 2015 10:56:38 -0500 Subject: [PATCH 1655/4033] CLJS-1506: doc for referred fn displays alias ns If you get the var for a referred symbol, the resolved var has the target namespace rather than the source. For example, (require '[clojure.string :refer [trim]]) results in (var trim) evaluating to #'cljs.user/trim (which itself cannot be resolved) rather than #'clojure.string/trim (as is the behavior in Clojure). This fixes this issue in a direct way, by putting the resolved var namespace in the data returned by cljs.analyzer/resolve-var. This fixes the underlying var issue and, as a consequence, fixes the doc output as well. --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9dca5c156..e2b2d39e3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -727,7 +727,7 @@ (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) - :ns (-> env :ns :name)})) + :ns full-ns})) (not (nil? (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym))) (recur env (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) confirm) From 0d52cf27d73444508ad2e75d94e0345e426dcaa9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 9 Dec 2015 15:29:28 -0500 Subject: [PATCH 1656/4033] clojure.core/require is not thread safe, use locks --- src/main/clojure/cljs/analyzer.cljc | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e2b2d39e3..e9bf6951e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -496,13 +496,17 @@ :name (symbol (str ns) (str k)))))])) (into {})))))) +(def load-mutex (Object.)) + #?(:clj (defn load-core [] (when (not @-cljs-macros-loaded) (reset! -cljs-macros-loaded true) (if *cljs-macros-is-classpath* - (load *cljs-macros-path*) - (load-file *cljs-macros-path*))) + (locking load-mutex + (load *cljs-macros-path*)) + (locking load-mutex + (load-file *cljs-macros-path*)))) (intern-macros 'cljs.core))) #?(:clj @@ -2502,16 +2506,20 @@ (get-in reloads [:use-macros nsym]) (and (= nsym name) *reload-macros* :reload))] (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) + (locking load-mutex + (clojure.core/require nsym k)) + (locking load-mutex + (clojure.core/require nsym))) (intern-macros nsym k))) (doseq [nsym (vals require-macros)] (let [k (or (:require-macros reload) (get-in reloads [:require-macros nsym]) (and (= nsym name) *reload-macros* :reload))] (if k - (clojure.core/require nsym k) - (clojure.core/require nsym)) + (locking load-mutex + (clojure.core/require nsym k)) + (locking load-mutex + (clojure.core/require nsym))) (intern-macros nsym k))) (when (seq use-macros) (check-use-macros use-macros env))) From 8d248e3c3ec2684ff2ad745fe85530413e2bb759 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 13 Dec 2015 22:49:20 -0500 Subject: [PATCH 1657/4033] CLJS-1512: Self-host: arithmetic form meta missing :numeric In cljs.analyzer/macroexpand-1*, the mac-var is obtained and its meta is checked for :cljs.analyzer/numeric. The problem is that this is a cljs.core/Var obtained from the findInternedVar method on cljs.core/Namespace, which slaps on a degenerate var-meta with the map: {:ns this}. Instead, employ a :cljs conditional branch to to extract the meta out of the compiler metadata. --- src/main/clojure/cljs/analyzer.cljc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e9bf6951e..0bb4b4e37 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2358,7 +2358,12 @@ sym (symbol "cljs.core" (str sym))) js-op {:js-op sym} - js-op (if (true? (-> mac-var meta ::numeric)) + numeric #?(:clj (-> mac-var meta ::numeric) + :cljs (let [mac-var-ns (symbol (namespace (.-sym mac-var))) + mac-var-name (symbol (name (.-sym mac-var)))] + (get-in @env/*compiler* + [::namespaces mac-var-ns :defs mac-var-name :meta ::numeric]))) + js-op (if (true? numeric) (assoc js-op :numeric true) js-op)] (vary-meta form' merge js-op)) From 452edf43927566cc0ea0a3846706c0294cef235d Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 14 Dec 2015 15:15:34 -0500 Subject: [PATCH 1658/4033] need to read conditionalize cljs.analyzer/load-mutex --- src/main/clojure/cljs/analyzer.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 0bb4b4e37..691d893fe 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -496,7 +496,8 @@ :name (symbol (str ns) (str k)))))])) (into {})))))) -(def load-mutex (Object.)) +#?(:clj + (def load-mutex (Object.))) #?(:clj (defn load-core [] From 9391870fa12a680281b80f2335a7c3342a3c08cb Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 16 Dec 2015 10:37:39 -0500 Subject: [PATCH 1659/4033] CLJS-1514: Remove Alpha designators on *-watch and ex-* Removes "Alpha - subject to change." from the docstrings of following, concomitant with Clojure: - add-watch - remove-watch - ex-info - ex-data - ex-message - ex-cause --- src/main/cljs/cljs/core.cljs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0dc6b0d4c..1d3c0091a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9132,9 +9132,7 @@ reduces them without incurring seq initialization" (set! (.-meta iref) m)) (defn add-watch - "Alpha - subject to change. - - Adds a watch function to an atom reference. The watch fn must be a + "Adds a watch function to an atom reference. The watch fn must be a fn of 4 args: a key, the reference, its old-state, its new-state. Whenever the reference's state might have been changed, any registered watches will have their functions called. The watch @@ -9157,9 +9155,7 @@ reduces them without incurring seq initialization" iref) (defn remove-watch - "Alpha - subject to change. - - Removes a watch (set by add-watch) from a reference" + "Removes a watch (set by add-watch) from a reference" [iref key] (-remove-watch iref key) iref) @@ -9916,32 +9912,28 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (this-as this (pr-str* this)))) (defn ex-info - "Alpha - subject to change. - Create an instance of ExceptionInfo, an Error type that carries a + "Create an instance of ExceptionInfo, an Error type that carries a map of additional data." ([msg data] (ex-info msg data nil)) ([msg data cause] (ExceptionInfo. msg data cause))) (defn ex-data - "Alpha - subject to change. - Returns exception data (a map) if ex is an ExceptionInfo. + "Returns exception data (a map) if ex is an ExceptionInfo. Otherwise returns nil." [ex] (when (instance? ExceptionInfo ex) (.-data ex))) (defn ex-message - "Alpha - subject to change. - Returns the message attached to the given Error / ExceptionInfo object. + "Returns the message attached to the given Error / ExceptionInfo object. For non-Errors returns nil." [ex] (when (instance? js/Error ex) (.-message ex))) (defn ex-cause - "Alpha - subject to change. - Returns exception cause (an Error / ExceptionInfo) if ex is an + "Returns exception cause (an Error / ExceptionInfo) if ex is an ExceptionInfo. Otherwise returns nil." [ex] From e1892f69286b03eee2da5801f2128b6baf7e211f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 20 Dec 2015 00:01:58 -0500 Subject: [PATCH 1660/4033] CLJS-1516: better error message when calling macros with arity When expanding a macro with an incorrect number of arguments, emit invalid arity diagnostic reporting the number of arguments decremented by 2 in order to hide the count associated with special variables &env and &form. - The approach taken for regular JVM ClojureScript is to catch the ArityException and throw a new one with the count decremented by two. - The approach taken for bootstrapped ClojureScript ---since a generic js/Error is thrown---is to instead add a check for multi-arity-fns to consult the meta in the invalid arity path to see if the function is actually a bootstrap macroexpander and to decrement by two in that case. --- src/main/clojure/cljs/analyzer.cljc | 7 +++++-- src/main/clojure/cljs/core.cljc | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 691d893fe..5a07ee286 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -34,7 +34,7 @@ [cljs.reader :as edn])) #?(:clj (:import [java.io File Reader PushbackReader] [java.net URL] - [clojure.lang Namespace Var LazySeq] + [clojure.lang Namespace Var LazySeq ArityException] [cljs.tagged_literals JSValue]))) #?(:clj (set! *warn-on-reflection* true)) @@ -2349,7 +2349,10 @@ (if-not (nil? mac-var) (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) - (let [form' (apply @mac-var form env (rest form))] + (let [form' (try + (apply @mac-var form env (rest form)) + #?(:clj (catch ArityException e + (throw (ArityException. (- (.actual e) 2) (.name e))))))] (if #?(:clj (seq? form') :cljs (cljs-seq? form')) (let [sym' (first form') sym (first form)] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 6fd978c29..8bd2ed3ff 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2784,9 +2784,11 @@ (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args maxfa) argseq#))) - `(throw (js/Error. - (str "Invalid arity: " - (alength ~args-sym))))))))) + (if (:macro meta) + `(throw (js/Error. + (str "Invalid arity: " (- (alength ~args-sym) 2)))) + `(throw (js/Error. + (str "Invalid arity: " (alength ~args-sym)))))))))) ~@(map fn-method fdecl) ;; optimization properties (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa))))) From 18158f9f5b3b577dbe9791057b3d8f29692658b6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 22 Dec 2015 17:20:44 -0500 Subject: [PATCH 1661/4033] CLJS-1487: Fix handling of timestamp comparison for dependencies in JARs Always set the last modified timestamp of generated files to the source last modified timestamp. --- src/main/clojure/cljs/analyzer.cljc | 23 ++-- src/main/clojure/cljs/closure.clj | 41 +++--- src/main/clojure/cljs/compiler.cljc | 201 ++++++++++++++++------------ src/main/clojure/cljs/repl.cljc | 4 +- src/main/clojure/cljs/util.cljc | 3 + 5 files changed, 156 insertions(+), 116 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 691d893fe..ab84eb450 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2798,19 +2798,24 @@ (let [out-src (util/to-target-file output-dir (parse-ns src))] (if (not (.exists out-src)) true - (if (> (util/last-modified src) (util/last-modified cache)) + (if (util/changed? src cache) true (let [version' (util/compiled-by-version cache) - version (util/clojurescript-version)] + version (util/clojurescript-version)] (and version (not= version version')))))))))) #?(:clj - (defn write-analysis-cache [ns cache-file] - (util/mkdirs cache-file) - (spit cache-file - (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" - (pr-str - (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)))))) + (defn write-analysis-cache + ([ns cache-file] + (write-analysis-cache ns cache-file nil)) + ([ns cache-file src] + (util/mkdirs cache-file) + (spit cache-file + (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" + (pr-str + (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)))) + (when src + (.setLastModified ^File cache-file (util/last-modified src)))))) #?(:clj (defn analyze-file @@ -2858,7 +2863,7 @@ (recur ns (next forms)))) ns)))] (when (and cache (true? (:cache-analysis opts))) - (write-analysis-cache ns cache)))) + (write-analysis-cache ns cache res)))) ;; we want want to keep dependency analysis information ;; don't revert the environment - David (let [{:keys [ns]} diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 398732362..3366755d5 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1522,8 +1522,8 @@ (defn write-javascript "Write or copy a JavaScript file to output directory. Only write if the file - does not already exist. Return IJavaScript for the file on disk at the new - location." + does not already exist. Return IJavaScript for the file on disk at the new + location." [opts js] (let [out-dir (io/file (util/output-directory opts)) out-name (rel-output-path js opts) @@ -1532,14 +1532,21 @@ :out-file (.toString out-file) :requires (deps/-requires js) :provides (deps/-provides js) - :group (:group js)}] - (when-not (.exists out-file) + :group (:group js)} + url (:url js)] + (when (or (not (.exists out-file)) + (and url (util/changed? out-file url))) + (when (and url (or ana/*verbose* (:verbose opts))) + (util/debug-prn "Copying" (str url) "to" (str out-file))) (util/mkdirs out-file) (spit out-file - (cond-> (if (map? js) (assoc js :source (deps/-source js)) js) - (:preprocess js) (js-transforms opts) - (:module-type js) (convert-js-module opts) - true deps/-source))) + (cond-> js + (map? js) (assoc :source (deps/-source js)) + (:preprocess js) (js-transforms opts) + (:module-type js) (convert-js-module opts) + true deps/-source)) + (when url + (.setLastModified ^File out-file (util/last-modified url)))) (if (map? js) (merge js ijs) ijs))) @@ -1556,7 +1563,7 @@ (defn source-on-disk "Ensure that the given IJavaScript exists on disk in the output directory. - Return updated IJavaScript with the new location if necessary." + Return updated IJavaScript with the new location if necessary." [opts js] (if (write-js? js) (write-javascript opts js) @@ -1568,9 +1575,11 @@ source-url (:source-url js)] (when (and out-file source-url (or (not (.exists ^File out-file)) - (> (.lastModified (io/file source-url)) - (.lastModified out-file)))) - (spit out-file (slurp source-url))) + (util/changed? (io/file source-url) out-file))) + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Copying" (str source-url) "to" (str out-file))) + (spit out-file (slurp source-url)) + (.setLastModified ^File out-file (util/last-modified source-url))) js))) (comment @@ -2060,16 +2069,16 @@ (defn aot-cache-core [] (let [base-path (io/file "src" "main" "cljs" "cljs") - src (io/file base-path "core.cljs") - dest (io/file base-path "core.aot.js") - cache (io/file base-path "core.cljs.cache.aot.edn")] + src (io/file base-path "core.cljs") + dest (io/file base-path "core.aot.js") + cache (io/file base-path "core.cljs.cache.aot.edn")] (util/mkdirs dest) (env/with-compiler-env (env/default-compiler-env) (comp/compile-file src dest {:source-map true :source-map-url "core.js.map" :output-dir (str "src" File/separator "main" File/separator "cljs")}) - (ana/write-analysis-cache 'cljs.core cache)))) + (ana/write-analysis-cache 'cljs.core cache src)))) (comment (time diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index bdc16b594..db583f8ee 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1167,6 +1167,108 @@ (when opts (str " " (pr-str (build-affecting-options opts)))))))) +#?(:clj + (defn cached-core [ns ext opts] + (and (= :none (:optimizations opts)) + (not= "cljc" ext) + (= 'cljs.core ns) + (io/resource "cljs/core.aot.js")))) + +#?(:clj + (defn macro-ns? [ns ext opts] + (or (= "clj" ext) + (= 'cljs.core$macros ns) + (and (= ns 'cljs.core) (= "cljc" ext)) + (:macros-ns opts)))) + +#?(:clj + (defn emit-cached-core [src dest cached opts] + ;; no need to bother with analysis cache reading, handled by + ;; with-core-cljs + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Using cached cljs.core" (str src))) + (spit dest (slurp cached)) + (.setLastModified ^File dest (util/last-modified src)) + (when (true? (:source-map opts)) + (spit (io/file (str dest ".map")) + (json/write-str + (assoc + (json/read-str (slurp (io/resource "cljs/core.aot.js.map"))) + "file" + (str (io/file (util/output-directory opts) "cljs" "core.js")))))) + (ana/parse-ns src dest nil))) + +#?(:clj + (defn emit-source-map [src dest sm-data opts] + (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] + (emits "\n//# sourceMappingURL=" + (or (:source-map-url opts) (.getName sm-file)) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + "")) + (spit sm-file + (sm/encode {(url-path src) (:source-map sm-data)} + {:lines (+ (:gen-line sm-data) 2) + :file (url-path dest) + :source-map-timestamp (:source-map-timestamp opts) + :source-map-pretty-print (:source-map-pretty-print opts)}))))) + +#?(:clj + (defn emit-source [src dest ext opts] + (with-open [out ^java.io.Writer (io/make-writer dest {})] + (binding [*out* out + ana/*cljs-ns* 'cljs.user + ana/*cljs-file* (.getPath ^File src) + reader/*alias-map* (or reader/*alias-map* {}) + ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) + *source-map-data* (when (:source-map opts) + (atom + {:source-map (sorted-map) + :gen-col 0 + :gen-line 0}))] + (emitln (compiled-by-string opts)) + (with-open [rdr (io/reader src)] + (let [env (ana/empty-env)] + (loop [forms (ana/forms-seq* rdr (util/path src)) + ns-name nil + deps nil] + (if (seq forms) + (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) + ast (ana/analyze env (first forms) nil opts)] + (emit ast) + (if (= :ns (:op ast)) + (let [ns-name (:name ast) + ns-name (if (and (= 'cljs.core ns-name) + (= "cljc" ext)) + 'cljs.core$macros + ns-name)] + (recur (rest forms) ns-name (merge (:uses ast) (:requires ast)))) + (recur (rest forms) ns-name deps))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :macros-ns (:macros-ns opts) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= :none (:optimizations opts))) + (emit-source-map src dest sm-data opts)) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src (ana/parse-ns src) output-dir :write) + src)) + ret)))))))))) + #?(:clj (defn compile-file* ([src dest] (compile-file* src dest nil)) @@ -1178,93 +1280,14 @@ (util/debug-prn "Compiling" (str src))) (let [ext (util/ext src) {:keys [ns] :as ns-info} (ana/parse-ns src)] - (if-let [cached (and (= :none (:optimizations opts)) - (not= "cljc" ext) - (= 'cljs.core ns) - (io/resource "cljs/core.aot.js"))] - ;; no need to bother with analysis cache reading, handled by - ;; with-core-cljs - (do - (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Using cached cljs.core" (str src))) - (spit dest (slurp cached)) - (when (true? (:source-map opts)) - (spit (io/file (str dest ".map")) - (json/write-str - (assoc - (json/read-str (slurp (io/resource "cljs/core.aot.js.map"))) - "file" - (str (io/file (util/output-directory opts) "cljs" "core.js")))))) - (ana/parse-ns src dest nil)) - (let [opts (if (or (= "clj" ext) - (= 'cljs.core$macros ns) - (and (= ns 'cljs.core) (= "cljc" ext)) - (:macros-ns opts)) - (assoc opts :macros-ns true) - opts)] - (with-open [out ^java.io.Writer (io/make-writer dest {})] - (binding [*out* out - ana/*cljs-ns* 'cljs.user - ana/*cljs-file* (.getPath ^File src) - reader/*alias-map* (or reader/*alias-map* {}) - ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) - *source-map-data* (when (:source-map opts) - (atom - {:source-map (sorted-map) - :gen-col 0 - :gen-line 0}))] - (emitln (compiled-by-string opts)) - (with-open [rdr (io/reader src)] - (let [env (ana/empty-env)] - (loop [forms (ana/forms-seq* rdr (util/path src)) - ns-name nil - deps nil] - (if (seq forms) - (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) - ast (ana/analyze env (first forms) nil opts)] - (emit ast) - (if (= :ns (:op ast)) - (let [ns-name (:name ast) - ns-name (if (and (= 'cljs.core ns-name) - (= "cljc" ext)) - 'cljs.core$macros - ns-name)] - (recur (rest forms) ns-name (merge (:uses ast) (:requires ast)))) - (recur (rest forms) ns-name deps))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :macros-ns (:macros-ns opts) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= :none (:optimizations opts))) - (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" - (or (:source-map-url opts) (.getName sm-file)) - (if (true? (:source-map-timestamp opts)) - (str "?rel=" (System/currentTimeMillis)) - "")) - (spit sm-file - (sm/encode {(url-path src) (:source-map sm-data)} - {:lines (+ (:gen-line sm-data) 2) - :file (url-path dest) - :source-map-timestamp (:source-map-timestamp opts) - :source-map-pretty-print (:source-map-pretty-print opts)})))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src (ana/parse-ns src) output-dir :write))) - ret))))))))))))))))) + (if-let [cached (cached-core ns ext opts)] + (emit-cached-core src dest cached opts) + (let [opts (if (macro-ns? ns ext opts) + (assoc opts :macros-ns true) + opts) + ret (emit-source src dest ext opts)] + (.setLastModified ^File dest (util/last-modified src)) + ret))))))))) #?(:clj (defn requires-compilation? @@ -1274,7 +1297,7 @@ (let [{:keys [ns requires]} (ana/parse-ns src)] (ensure (or (not (.exists dest)) - (> (.lastModified src) (.lastModified dest)) + (util/changed? src dest) (let [version' (util/compiled-by-version dest) version (util/clojurescript-version)] (and version (not= version version'))) @@ -1324,7 +1347,7 @@ (assoc opts :static-fns true) opts)] (if (or (requires-compilation? src-file dest-file opts) - (:force opts)) + (:force opts)) (do (util/mkdirs dest-file) (when (and (get-in nses [ns :defs]) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index b48ee2354..bb15fe3d8 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -190,8 +190,8 @@ ;; environment will handle actual loading - David (let [sb (StringBuffer.)] (doseq [source (->> sources - (remove (comp #{:seed} :type)) - (map #(cljsc/source-on-disk opts %)))] + (remove (comp #{:seed} :type)) + (map #(cljsc/source-on-disk opts %)))] (when (:repl-verbose opts) (println "Loading:" (:provides source))) (.append sb (cljsc/add-dep-string opts source))) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index bbf6707f4..6bccb4a70 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -154,6 +154,9 @@ (throw (IllegalArgumentException. (str "Cannot get last modified for " src))))) +(defn changed? [a b] + (not (== (last-modified a) (last-modified b)))) + (defn file-or-resource [s] (or (and (.exists (io/file s)) (io/file s)) (io/resource s))) From d1ab3451b74f66ee13210d845459c9e6f11915c4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 22 Dec 2015 17:34:10 -0500 Subject: [PATCH 1662/4033] bump Closure Compiler & tools.reader deps --- pom.template.xml | 4 ++-- project.clj | 4 ++-- script/bootstrap | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index dd7a4e7c8..4070b8dc6 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20151015 + v20151216 org.clojure @@ -50,7 +50,7 @@ org.clojure tools.reader - 0.10.0-alpha3 + 1.0.0-alpha1 diff --git a/project.clj b/project.clj index f016a61dc..a59189f2c 100644 --- a/project.clj +++ b/project.clj @@ -10,9 +10,9 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "0.10.0-alpha3"] + [org.clojure/tools.reader "1.0.0-alpha1"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] - [com.google.javascript/closure-compiler "v20151015"] + [com.google.javascript/closure-compiler "v20151216"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index b3b3b0e0c..6a293dcd2 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,11 +3,11 @@ set -e CLOJURE_RELEASE="1.7.0" -CLOSURE_RELEASE="20151015" +CLOSURE_RELEASE="20151216" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="0.10.0-alpha3" +TREADER_RELEASE="1.0.0-alpha1" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From 2fa6fcad7bbf2375d3dc7dba669c65b06269b8b3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 17 Jun 2015 19:37:02 -0400 Subject: [PATCH 1663/4033] CLJS-1313: REPL support for libs compiler opts Does module processing and dependency index setup if :libs or :foreign-libs is passed to REPL. --- src/main/clojure/cljs/repl.cljc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index bb15fe3d8..723efecc7 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -21,6 +21,7 @@ [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.env :as env] + [cljs.js-deps :as deps] [cljs.tagged-literals :as tags] [cljs.closure :as cljsc] [cljs.source-map :as sm]) @@ -830,6 +831,11 @@ (catch Throwable e (caught e repl-env opts) opts)))) + opts (if (or (:libs opts) (:foreign-libs opts)) + (let [opts (cljsc/process-js-modules opts)] + (swap! env/*compiler* assoc :js-dependency-index (deps/js-dependency-index opts)) + opts) + opts) init (or init #(evaluate-form repl-env env "" (with-meta From eef56c18322191a9db4a2545c4456a2b5c8853a2 Mon Sep 17 00:00:00 2001 From: Gary Fredericks Date: Sat, 21 Nov 2015 15:59:32 -0600 Subject: [PATCH 1664/4033] Convert pair of mutations into a try/finally --- src/main/cljs/cljs/test.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/test.clj b/src/main/cljs/cljs/test.clj index 5a1137d1c..ab0b7aaa2 100644 --- a/src/main/cljs/cljs/test.clj +++ b/src/main/cljs/cljs/test.clj @@ -194,9 +194,10 @@ ([string & body] `(do (cljs.test/update-current-env! [:testing-contexts] conj ~string) - (let [ret# (do ~@body)] - (cljs.test/update-current-env! [:testing-contexts] rest) - ret#)))) + (try + ~@body + (finally + (cljs.test/update-current-env! [:testing-contexts] rest)))))) ;; ============================================================================= ;; Defining Tests From 77f089a240a86856c4c571e35eb2f1d24988a287 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Mon, 5 Oct 2015 20:04:10 +0200 Subject: [PATCH 1665/4033] CLJS-1463: (js-debugger) should generate nil-returning expression js-debugger macro can now be used in last position of an (implicit) do. Previously this was not possible, because "return debugger;" was generated, resulting in an js syntax error. --- src/main/clojure/cljs/core.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8bd2ed3ff..699af9761 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -864,9 +864,11 @@ (core/list 'js* "~{} in ~{}" key obj)) (core/defmacro js-debugger - "Emit JavaScript \"debugger;\" statement." + "Emit JavaScript \"debugger;\" statement" [] - (core/list 'js* "debugger;")) + (core/list 'do + (core/list 'js* "debugger") + nil)) (core/defmacro js-comment "Emit a top-level JavaScript multi-line comment. New lines will create a From e090f1821945a0391d2e4be41bdce734b31a9191 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 22 Dec 2015 23:10:21 -0500 Subject: [PATCH 1666/4033] missing setLastModified when copying files from JARs --- src/main/clojure/cljs/closure.clj | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3366755d5..dcf50ea0e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -462,6 +462,7 @@ (slurp reader))] (util/mkdirs out-file) (spit out-file content) + (.setLastModified ^File out-file (util/last-modified url)) out-file)) ;; TODO: it would be nice if we could consolidate requires-compilation? @@ -1533,11 +1534,11 @@ :requires (deps/-requires js) :provides (deps/-provides js) :group (:group js)} - url (:url js)] + res (or (:url js) (:source-file js))] (when (or (not (.exists out-file)) - (and url (util/changed? out-file url))) - (when (and url (or ana/*verbose* (:verbose opts))) - (util/debug-prn "Copying" (str url) "to" (str out-file))) + (and res (util/changed? out-file res))) + (when (and res (or ana/*verbose* (:verbose opts))) + (util/debug-prn "Copying" (str res) "to" (str out-file))) (util/mkdirs out-file) (spit out-file (cond-> js @@ -1545,8 +1546,8 @@ (:preprocess js) (js-transforms opts) (:module-type js) (convert-js-module opts) true deps/-source)) - (when url - (.setLastModified ^File out-file (util/last-modified url)))) + (when res + (.setLastModified ^File out-file (util/last-modified res)))) (if (map? js) (merge js ijs) ijs))) From 8d72633e693e8b887494288e4ff49539d43f39c5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 22 Dec 2015 23:56:29 -0500 Subject: [PATCH 1667/4033] fix issue where files in JARs would get copied to out without correct timestamping add more logging minor code cleanup and comments for readability --- src/main/clojure/cljs/closure.clj | 51 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index dcf50ea0e..8aacd2513 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -456,31 +456,40 @@ (defn jar-file-to-disk "Copy a file contained within a jar to disk. Return the created file." - [url out-dir] - (let [out-file (io/file out-dir (path-from-jarfile url)) - content (with-open [reader (io/reader url)] - (slurp reader))] - (util/mkdirs out-file) - (spit out-file content) - (.setLastModified ^File out-file (util/last-modified url)) - out-file)) + ([url out-dir] + (jar-file-to-disk url out-dir nil)) + ([url out-dir opts] + (let [out-file (io/file out-dir (path-from-jarfile url)) + content (with-open [reader (io/reader url)] + (slurp reader))] + (when (and url (or ana/*verbose* (:verbose opts))) + (util/debug-prn "Copying" (str url) "to" (str out-file))) + (util/mkdirs out-file) + (spit out-file content) + (.setLastModified ^File out-file (util/last-modified url)) + out-file))) ;; TODO: it would be nice if we could consolidate requires-compilation? ;; logic - David (defn compile-from-jar - "Compile a file from a jar." - [this {:keys [output-file] :as opts}] - (or (when output-file - (let [out-file (io/file (util/output-directory opts) output-file)] - (when (and (.exists out-file) - (= (util/compiled-by-version out-file) - (util/clojurescript-version))) - (compile-file - (io/file (util/output-directory opts) - (last (string/split (.getPath ^URL this) #"\.jar!/"))) - opts)))) - (let [file-on-disk (jar-file-to-disk this (util/output-directory opts))] - (-compile file-on-disk opts)))) + "Compile a file from a jar if necessary. Returns IJavaScript." + [jar-file {:keys [output-file] :as opts}] + (let [out-file (when output-file + (io/file (util/output-directory opts) output-file))] + (if (or (nil? out-file) + (not (.exists ^File out-file)) + (not= (util/compiled-by-version out-file) + (util/clojurescript-version)) + (util/changed? jar-file out-file)) + ;; actually compile from JAR + (let [file-on-disk (jar-file-to-disk jar-file (util/output-directory opts) opts)] + (-compile file-on-disk opts)) + ;; have to call compile-file as it includes more IJavaScript + ;; information than ana/parse-ns + (compile-file + (io/file (util/output-directory opts) + (last (string/split (.getPath ^URL jar-file) #"\.jar!/"))) + opts)))) (defn find-jar-sources [this opts] From 1a054b3129a5480f2cd904a52d847b21a75c55e9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 23 Dec 2015 10:03:05 -0500 Subject: [PATCH 1668/4033] update developers list --- pom.template.xml | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/pom.template.xml b/pom.template.xml index 4070b8dc6..195dd2db6 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -57,42 +57,131 @@ Aaron Bedra Alan Dipert + Alex Dowad Alan Malloy Alen Ribic Alex Redington + Ambrose Bonnaire-Sergeant + Andrew Rosa + Antonin Hildebrand + Ben Moss + Benjamin Meyer + Bo Jeanes Bobby Calderwood Brandon Bloom Brenton Ashworth + Brian Jenkins + Brian Kim + Brian Taylor + Bruce Hauman + Chad Taylor + Chas Emerick + Charles Duffy + Chris Granger + Chris Pickard Chris Houser + Chris Truter Christopher Redinger + Colin Jones Creighton Kirkendall David Nolen + Daniel Compton + Daniel Skarda + Dave Sann Devin Walters + Dylan Butman + Edward Tsech + Eric Normand Eric Thorsen + Erik Ouchterlony + Evan Mezeske + Francis Avila Frank Failla + Francoise De Serre + Gary Fredericks + Gary Trakhman + Herwig Hochleitner Hubert Iwaniuk Hugo Duncan + Immo Heikkinen + Ivan Willig + J. Pablo Fernandez + Jamie Brandon + Jeff Dik Jess Martin + Joel Holdbrooks + Joel Martin John Li + Jonas De Vuyst Jonas Enlund + Jonathan Boston + Jozef Wagner Juergen Hoetzel + Juho Teperi + Julian Eluard + Justin Tirrell + Kovas Boguta Kevin J. Lynagh Laszlo Toeroek + Leon Grapenthin Luke VanderHart + Maria Geller + Martin Klepsch + Matjaz Gregoric + Max Gonzih + Max Penet + Max Veytsman + Michael Ballantyne Michael Fogus + Michael Glaesemann + Michael Griffiths + Michael O. Church Michał Marczyk + Michiel Borkent + Mike Fikes Moritz Ulrich + Murphy McMahon + Nelson Morris Nicola Mometto + Nikita Prokopov + Osbert Feng Paul Michael Bauer + Paul deGrandis + Peter Schuck + Peter Stephens + Peter Taoussanis + Pieter van Prooijen + Raphaël Amiard + Raymond Huang Rich Hickey Roman Gonzalez + Roman Scherer + Rupa Shankar Russ Olsen + Sam Umbach + Samuel Miller + Sean Grove + Sebastien Bensusan + Sean LeBron + Steven Kallstrom Stuart Halloway + Stuart Mitchell Stuart Sierra Takahiro Hozumi + Thomas Heller Thomas Scheiblauer + Tim Griesser + Timothy Pratley + Toby Crawley Tom Hickey + Tom Jack + Tom Marble + Travis Thieman + Travis Vachon Wilkes Joiner + Zachary Allaun + Zach Oakes + Zubair Quraishi From 15f9bbe59b47312f451b90972864a37a1c2246d2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 30 Dec 2015 05:57:10 -0500 Subject: [PATCH 1669/4033] CLJS-1524: Bad hashing for Cons --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1d3c0091a..677cfc3bd 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2880,7 +2880,7 @@ reduces them without incurring seq initialization" (if (nil? rest) nil (seq rest))) ICollection - (-conj [coll o] (Cons. nil o coll __hash)) + (-conj [coll o] (Cons. nil o coll nil)) IEmptyableCollection (-empty [coll] (with-meta (.-EMPTY List) meta)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 061cd074a..f0660362b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2983,6 +2983,18 @@ IFooBar (cljs.core-test/a-method [_] "foobar")))))) +(deftest test-cljs-1524 + (let [x0 [] + x1 (conj x0 1) + x2 (conj x1 2) + x3 (remove #{1} x2) + x4 (remove #{2} x3) + x5 (conj x4 3) + x6 (conj x5 4) + x7 (conj x6 5)] + (is (not (== (hash x0) (hash x1) (hash x2) (hash x3) (hash x4) + (hash x5) (hash x6) (hash x7)))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 71b9fa4a0e44c532ce438f47706bf6d65305a2f5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 30 Dec 2015 16:56:35 -0500 Subject: [PATCH 1670/4033] CLJS-1525: Mismatch between param and doc for number? Change the parameter to x to match the docstring. (The name x is used in Clojure and in other functions near number? in cljs.core) --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 677cfc3bd..c22606cc3 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -194,8 +194,8 @@ (defn ^boolean number? "Returns true if x is a JavaScript number." - [n] - (cljs.core/number? n)) + [x] + (cljs.core/number? x)) (defn ^boolean not "Returns true if x is logical false, false otherwise." From 20ab0d102b7a701a84cc923b856d84a699b59c26 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 30 Dec 2015 21:58:37 -0500 Subject: [PATCH 1671/4033] CLJS-1527: Doc for *print-length* copy of *print-dup* Copy over docstring from Clojure. --- src/main/cljs/cljs/core.cljs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c22606cc3..c6ca522a1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -104,10 +104,13 @@ (def ^{:dynamic true - :doc "When set to logical true, objects will be printed in a way that preserves - their type when read in later. - - Defaults to false." + :doc "*print-length* controls how many items of each collection the + printer will print. If it is bound to logical false, there is no + limit. Otherwise, it must be bound to an integer indicating the maximum + number of items of each collection to print. If a collection contains + more items, the printer will print items up to the limit followed by + '...' to represent the remaining items. The root binding is nil + indicating no limit." :jsdoc ["@type {null|number}"]} *print-length* nil) From b766dae3c4630fdabc7555e4dcca4afee7f4cf0b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 1 Jan 2016 08:40:15 -0500 Subject: [PATCH 1672/4033] CLJS-1531: realized? for lazy-seq - Add support for IPending to LazySeq. - Change param from d to x in places (no longer just delay) - Unit test via lazy-seq built with cons cells --- src/main/cljs/cljs/core.cljs | 16 ++++++++++------ src/test/cljs/cljs/core_test.cljs | 13 +++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c6ca522a1..f644aaa32 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -650,9 +650,9 @@ (defprotocol IPending "Protocol for types which can have a deferred realization. Currently only - implemented by Delay." - (^boolean -realized? [d] - "Returns true if a value for d has been produced, false otherwise.")) + implemented by Delay and LazySeq." + (^boolean -realized? [x] + "Returns true if a value for x has been produced, false otherwise.")) (defprotocol IWatchable "Protocol for types that can be watched. Currently only implemented by Atom." @@ -3014,6 +3014,10 @@ reduces them without incurring seq initialization" (set! fn nil) s))) + IPending + (-realized? [x] + (not fn)) + IWithMeta (-with-meta [coll meta] (LazySeq. meta fn s __hash)) @@ -9195,7 +9199,7 @@ reduces them without incurring seq initialization" value) IPending - (-realized? [d] + (-realized? [x] (not f))) (defn ^boolean delay? @@ -9211,8 +9215,8 @@ reduces them without incurring seq initialization" (defn ^boolean realized? "Returns true if a value has been produced for a delay or lazy sequence." - [d] - (-realized? d)) + [x] + (-realized? x)) (defn- preserving-reduced [rf] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index f0660362b..a428f1d3b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1639,6 +1639,19 @@ (is (= '(2 3) (next (range 1 4)))) )) +(deftest test-lazy-seq-realized? + (testing "Testing LazySeq IPending" + (let [xs (lazy-seq + (cons 1 + (lazy-seq + (cons 2 + (lazy-seq (cons 3 nil))))))] + (is (not (realized? xs))) + (is (not (realized? (rest xs)))) + (is (realized? xs)) + (is (not (realized? (nthrest xs 2)))) + (is (realized? (rest xs)))))) + (deftest test-chunked (let [r (range 64) v (into [] r)] From f525d97d920df0abaa30f4e557351ce03d71dae4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 1 Jan 2016 15:41:19 -0500 Subject: [PATCH 1673/4033] CLJS-1532: Mismatch between instance? params and docstring - Rename params to match those used in docstring (matching Clojure). - Also, for consistency use same param namings in macro. --- src/main/cljs/cljs/core.cljs | 4 ++-- src/main/clojure/cljs/core.cljc | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f644aaa32..3f9485a68 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -904,8 +904,8 @@ (defn ^boolean instance? "Evaluates x and tests if it is an instance of the type c. Returns true or false" - [t o] - (cljs.core/instance? t o)) + [c x] + (cljs.core/instance? c x)) (defn ^boolean symbol? "Return true if x is a Symbol" diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 699af9761..ba5b1919b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -921,14 +921,14 @@ (core/defmacro identical? [a b] (bool-expr (core/list 'js* "(~{} === ~{})" a b))) -(core/defmacro instance? [t o] +(core/defmacro instance? [c x] ;; Google Closure warns about some references to RegExp, so ;; (instance? RegExp ...) needs to be inlined, but the expansion ;; should preserve the order of argument evaluation. - (bool-expr (if (clojure.core/symbol? t) - (core/list 'js* "(~{} instanceof ~{})" o t) - `(let [t# ~t o# ~o] - (~'js* "(~{} instanceof ~{})" o# t#))))) + (bool-expr (if (clojure.core/symbol? c) + (core/list 'js* "(~{} instanceof ~{})" x c) + `(let [c# ~c x# ~x] + (~'js* "(~{} instanceof ~{})" x# c#))))) (core/defmacro number? [x] (bool-expr (core/list 'js* "typeof ~{} === 'number'" x))) From 331d6404956c23aca8310c876665eb71f935f4da Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 1 Jan 2016 00:49:36 -0500 Subject: [PATCH 1674/4033] CLJS-1530: Mismatch between rseq param and docstring Rename param from coll to rev to match docstring. This is also consistent with Clojure, which uses rev for the reversible parameter name. --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3f9485a68..7f2b25e1f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2828,8 +2828,8 @@ reduces them without incurring seq initialization" (defn ^seq rseq "Returns, in constant time, a seq of the items in rev (which can be a vector or sorted-map), in reverse order. If rev is empty returns nil" - [coll] - (-rseq coll)) + [rev] + (-rseq rev)) (defn reverse "Returns a seq of the items in coll in reverse order. Not lazy." From f3f70d60629a420ce81d076149fe9c8469e2c8ad Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 31 Dec 2015 22:48:24 -0500 Subject: [PATCH 1675/4033] CLJS-1528: Doc for IEmptyableCollection refers to count Change typo reference from "count" to "empty." Signed-off-by: Mike Fikes --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7f2b25e1f..aafcae242 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -466,7 +466,7 @@ "Protocol for creating an empty collection." (-empty [coll] "Returns an empty collection of the same category as coll. Used - by cljs.core/count.")) + by cljs.core/empty.")) (defprotocol ICollection "Protocol for adding to a collection." From 734e503793791159863c761f3496c34e47a0a928 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 1 Jan 2016 00:09:50 -0500 Subject: [PATCH 1676/4033] CLJS-1529: Typo in IIndexed docstring "idexed" -> "indexed" Signed-off-by: Mike Fikes --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index aafcae242..80636a87c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -480,7 +480,7 @@ (-index [coll])) (defprotocol IIndexed - "Protocol for collections to provide idexed-based access to their items." + "Protocol for collections to provide indexed-based access to their items." (-nth [coll n] [coll n not-found] "Returns the value at the index n in the collection coll. Returns not-found if index n is out of bounds and not-found is supplied.")) From f7e4c3c0bd0dab6a8f86b279909565d69418eab4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 5 Jan 2016 17:07:22 -0500 Subject: [PATCH 1677/4033] CLJS-1425: self-host: cljs.js/eval cb argument inconsistent with docstring --- src/main/cljs/cljs/js.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 47a081de2..6aad5d167 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -552,9 +552,9 @@ (if (:error res) (cb res) (let [src (str "goog.provide(\"" (munge (:name ast)) "\")")] - (cb (*eval-fn* {:source src})))))) + (cb {:value (*eval-fn* {:source src})}))))) (let [src (with-out-str (comp/emit ast))] - (cb (*eval-fn* {:source src})))))))))) + (cb {:value (*eval-fn* {:source src})}))))))))) (defn eval "Evaluate a single ClojureScript form. The parameters: From 17e167effe2c62f988e659f812dfecb8b4b8f26f Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 5 Jan 2016 17:29:52 -0500 Subject: [PATCH 1678/4033] CLJS-1298: source-on-disk conditional should include :source-url --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8aacd2513..2d8b90131 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1579,7 +1579,9 @@ (write-javascript opts js) ;; always copy original ClojureScript sources to the output directory ;; when source maps enabled - (let [out-file (when-let [ns (and (:source-map opts) (first (:provides js)))] + (let [out-file (when-let [ns (and (:source-map opts) + (:source-url js) + (first (:provides js)))] (io/file (io/file (util/output-directory opts)) (util/ns->relpath ns (util/ext (:source-url js))))) source-url (:source-url js)] From 971a464d40ba998a3b3426bc269cbe8874226ab0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 Jan 2016 13:24:29 -0500 Subject: [PATCH 1679/4033] failing test case for CLJS-1537 --- src/test/cljs/circular_deps/a.cljs | 5 +++++ src/test/cljs/circular_deps/b.cljs | 5 +++++ src/test/clojure/cljs/build_api_tests.clj | 9 +++++++++ 3 files changed, 19 insertions(+) create mode 100644 src/test/cljs/circular_deps/a.cljs create mode 100644 src/test/cljs/circular_deps/b.cljs diff --git a/src/test/cljs/circular_deps/a.cljs b/src/test/cljs/circular_deps/a.cljs new file mode 100644 index 000000000..15c3d5782 --- /dev/null +++ b/src/test/cljs/circular_deps/a.cljs @@ -0,0 +1,5 @@ +(ns circular-deps.a + (:require [circular-deps.b])) + +(defn foo [a b] + (+ a b)) diff --git a/src/test/cljs/circular_deps/b.cljs b/src/test/cljs/circular_deps/b.cljs new file mode 100644 index 000000000..881d120ad --- /dev/null +++ b/src/test/cljs/circular_deps/b.cljs @@ -0,0 +1,5 @@ +(ns circular-deps.b + (:require [circular-deps.a])) + +(defn bar [c d] + (* c d)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 870e59996..8de5c353a 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -145,3 +145,12 @@ (is (re-find #"Loading modules A and B" (slurp module-main))) (is (re-find #"Module A loaded" (slurp module-a))) (is (re-find #"Module B loaded" (slurp module-b))))) + +(deftest cljs-1537-circular-deps + (let [out-file (io/file "out/main.js")] + (.delete out-file) + (build (inputs "src/test/cljs") + {:main 'circular-deps.a + :optimizations :none + :verbose true + :output-to "out"}))) From 27bbf822902506c1c0c63c1345cbf7fc6f8c819d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 Jan 2016 13:32:41 -0500 Subject: [PATCH 1680/4033] CLJS-1537: Circular dependency detection regression --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/build_api_tests.clj | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2a343fde4..004597f25 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2503,7 +2503,7 @@ (defn ns-side-effects [env {:keys [op] :as ast} opts] (if (= :ns op) - (let [{:keys [deps uses require-macros use-macros reload reloads]} ast] + (let [{:keys [name deps uses require-macros use-macros reload reloads]} ast] (when (and *analyze-deps* (seq deps)) (analyze-deps name deps env (dissoc opts :macros-ns))) (when (and *analyze-deps* (seq uses)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 8de5c353a..35ca14f99 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -149,8 +149,12 @@ (deftest cljs-1537-circular-deps (let [out-file (io/file "out/main.js")] (.delete out-file) - (build (inputs "src/test/cljs") - {:main 'circular-deps.a - :optimizations :none - :verbose true - :output-to "out"}))) + (try + (build (inputs "src/test/cljs") + {:main 'circular-deps.a + :optimizations :none + :verbose true + :output-to "out"}) + (is false) + (catch Throwable e + (is true))))) From 5724f72ad92d62b93cdf1afe19e4bbfc7b6ddecc Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 Jan 2016 17:14:39 -0500 Subject: [PATCH 1681/4033] CLJS-1539: Parallel compilation fails on circular dependencies cljs.js-deps/dependency-order-visit now checks for circular dependencies as well --- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/main/clojure/cljs/js_deps.cljc | 25 +++++++++++-------- .../{cljs => cljs_build}/circular_deps/a.cljs | 0 .../{cljs => cljs_build}/circular_deps/b.cljs | 0 src/test/clojure/cljs/build_api_tests.clj | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) rename src/test/{cljs => cljs_build}/circular_deps/a.cljs (100%) rename src/test/{cljs => cljs_build}/circular_deps/b.cljs (100%) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 004597f25..b94316dbd 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1653,7 +1653,8 @@ (let [compiler @env/*compiler*] (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] (assert (every? #(not (contains? *cljs-dep-set* %)) deps) - (str "Circular dependency detected " (-> *cljs-dep-set* meta :dep-path))) + (str "Circular dependency detected, " + (apply str (interpose " -> " (conj (-> *cljs-dep-set* meta :dep-path) lib))))) (doseq [dep deps] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 969fef52a..7146c4712 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -142,14 +142,19 @@ case." {} deps)) (defn dependency-order-visit - [state ns-name] - (let [file (get state ns-name)] - (if (or (:visited file) (nil? file)) - state - (let [state (assoc-in state [ns-name :visited] true) - deps (:requires file) - state (reduce dependency-order-visit state deps)] - (assoc state :order (conj (:order state) file)))))) + ([state ns-name] + (dependency-order-visit state ns-name [])) + ([state ns-name seen] + (assert (not (some #{ns-name} seen)) + (str "Circular dependency detected, " + (apply str (interpose " -> " (conj seen ns-name))))) + (let [file (get state ns-name)] + (if (or (:visited file) (nil? file)) + state + (let [state (assoc-in state [ns-name :visited] true) + deps (:requires file) + state (reduce #(dependency-order-visit %1 %2 (conj seen ns-name)) state deps)] + (assoc state :order (conj (:order state) file))))))) (defn- pack-string [s] (if (string? s) @@ -167,8 +172,8 @@ case." [coll] (let [state (build-index (map pack-string coll))] (map unpack-string - (distinct - (:order (reduce dependency-order-visit (assoc state :order []) (keys state))))))) + (distinct + (:order (reduce dependency-order-visit (assoc state :order []) (keys state))))))) ;; Dependencies diff --git a/src/test/cljs/circular_deps/a.cljs b/src/test/cljs_build/circular_deps/a.cljs similarity index 100% rename from src/test/cljs/circular_deps/a.cljs rename to src/test/cljs_build/circular_deps/a.cljs diff --git a/src/test/cljs/circular_deps/b.cljs b/src/test/cljs_build/circular_deps/b.cljs similarity index 100% rename from src/test/cljs/circular_deps/b.cljs rename to src/test/cljs_build/circular_deps/b.cljs diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 35ca14f99..9e50ebbd2 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -150,7 +150,7 @@ (let [out-file (io/file "out/main.js")] (.delete out-file) (try - (build (inputs "src/test/cljs") + (build (inputs "src/test/cljs_build") {:main 'circular-deps.a :optimizations :none :verbose true From 28a7440eeb4f140096d6d86ced4de804ef2e1bd2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 Jan 2016 17:46:53 -0500 Subject: [PATCH 1682/4033] remove duped circular lib logic --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b94316dbd..afafa6e8a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1654,7 +1654,7 @@ (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] (assert (every? #(not (contains? *cljs-dep-set* %)) deps) (str "Circular dependency detected, " - (apply str (interpose " -> " (conj (-> *cljs-dep-set* meta :dep-path) lib))))) + (apply str (interpose " -> " (-> *cljs-dep-set* meta :dep-path))))) (doseq [dep deps] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) From e807ebf58cf0d21be1da282e86033dd94c8f28fb Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 Jan 2016 18:09:03 -0500 Subject: [PATCH 1683/4033] really fix circular dependency error --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index afafa6e8a..f4b6f6ad1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1654,7 +1654,10 @@ (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] (assert (every? #(not (contains? *cljs-dep-set* %)) deps) (str "Circular dependency detected, " - (apply str (interpose " -> " (-> *cljs-dep-set* meta :dep-path))))) + (apply str + (interpose " -> " + (conj (-> *cljs-dep-set* meta :dep-path) + (some *cljs-dep-set* deps)))))) (doseq [dep deps] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) From bc6f93e937f3f80cd8c47097863399752189d561 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 6 Jan 2016 17:24:22 -0500 Subject: [PATCH 1684/4033] CLJS-1538: Type hint some cljs.core predicates Add type hints to predicates that directly delegate to instance? or satisfies? --- src/main/cljs/cljs/core.cljs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 80636a87c..b78a707da 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1043,7 +1043,7 @@ (-invoke [_ a b c d e f g h i j k l m n o p q r s t rest] (apply (val) a b c d e f g h i j k l m n o p q r s t rest))) -(defn var? +(defn ^boolean var? "Returns true if v is of type cljs.core.Var" [v] (instance? cljs.core.Var v)) @@ -1052,7 +1052,7 @@ (declare array-seq prim-seq IndexedSeq) -(defn iterable? +(defn ^boolean iterable? "Return true if x implements IIterable protocol." [x] (satisfies? IIterable x)) @@ -1062,7 +1062,7 @@ [value] (-clone value)) -(defn cloneable? +(defn ^boolean cloneable? "Return true if x implements ICloneable protocol." [value] (satisfies? ICloneable value)) @@ -4070,7 +4070,7 @@ reduces them without incurring seq initialization" [val] (Volatile. val)) -(defn volatile? +(defn ^boolean volatile? "Returns true if x is a volatile." [x] (instance? Volatile x)) @@ -9999,7 +9999,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (-write writer (str "#" tag " ")) (pr-writer form writer opts))) -(defn tagged-literal? +(defn ^boolean tagged-literal? "Return true if the value is the data representation of a tagged literal" [value] (instance? TaggedLiteral value)) From 7dfebbbaf233f31cbeb51680cb1723e7e4da0d62 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 Jan 2016 13:31:10 -0500 Subject: [PATCH 1685/4033] 1.7.228 --- README.md | 4 ++-- changes.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c37b2a05..3265302e6 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.7.170 +Latest stable release: 1.7.228 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.7.170"] +[org.clojure/clojurescript "1.7.228"] ``` [Maven](http://maven.apache.org) dependency information: diff --git a/changes.md b/changes.md index dac323391..f560b14d1 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,35 @@ +## 1.7.228 + +### Enhancements +* New experimental :parallel-build compiler option + +### Changes +* CLJS-1538: Type hint some cljs.core predicates +* Docstring typos +* CLJS-1463: (js-debugger) should generate nil-returning expression +* CLJS-1516: better error message when calling macros with arity +* CLJS-1514: Remove Alpha designators on *-watch and ex-* +* clojure.core/require is not thread safe, use locks +* CLJS-1505: Add tests to characterize `type` and `instance?` behavior +* CLJS-1491: Check :source-map is boolean when :optimizations :none +* split sm/encode into 2 functions so JSON generation is optional + +### Fixes +* CLJS-1539: Parallel compilation fails on circular dependencies +* CLJS-1425: self-host: cljs.js/eval cb argument inconsistent with docstring +* CLJS-1425: self-host: cljs.js/eval cb argument inconsistent with docstring +* CLJS-1524: Bad hashing for Cons +* CLJS-1487: Fix handling of timestamp comparison for dependencies in JARs +* CLJS-1498: Fix parallel build logging +* CLJS-1477: Do not attempt to resolve "native" type symbols +* CLJS-1236: `constructor` needs to munged if used as namespace segment +* CLJS-1330: self-host: .toString on int needs parens +* CLJS-1512: Self-host: arithmetic form meta missing :numeric +* CLJS-1506: doc for referred fn displays alias ns +* CLJS-1504: Self-host: Pseudo-namespace for macro namespace analysis +metadata +* CLJS-1483: Minor DCE regression with advanced compilation mode + ## 1.7.170 This is a breaking change for tooling libraries like lein-cljsbuild, From 628d957f3ecabf8d26d57665abdef3dea765151e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 16 Jan 2016 13:30:13 -0500 Subject: [PATCH 1686/4033] CLJS-1546: cljs.core/run! does not always return nil The cljs.core/run! function only returns nil if the proc function passed to it returns nil, but the docstring for run! indicates that it returns nil. Fixes this issue by explicitly returning nil. This also makes the ClojureScript implementation of run! match that in Clojure 1.8. Adds unit tests for run!, copying tests from Clojure that exhibit the bug, while also adding a test for the primary functionality. --- src/main/cljs/cljs/core.cljs | 4 ++-- src/test/cljs/cljs/core_test.cljs | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b78a707da..99868a068 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9293,8 +9293,8 @@ reduces them without incurring seq initialization" "Runs the supplied procedure (via reduce), for purposes of side effects, on successive items in the collection. Returns nil" [proc coll] - (reduce #(proc %2) nil coll)) - + (reduce #(proc %2) nil coll) + nil) (defprotocol IEncodeJS (-clj->js [x] "Recursively transforms clj values to JavaScript") diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index a428f1d3b..2d67765f5 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -907,6 +907,16 @@ (is (contains? "f" 0)) (is (not (contains? "f" 55))))) +(deftest test-run! + (testing "Testing run!" + (let [a (atom 0)] + (run! (fn [n] + (swap! a + n)) + (range 5)) + (is (= 10 @a))) + (is (nil? (run! identity [1]))) + (is (nil? (run! reduced (range)))))) + (deftest test-distinct (testing "Testing distinct? & distinct" (is (distinct? 1 2 3)) From 404d6444cb6419658a7dacb343a5fed5b9451e0c Mon Sep 17 00:00:00 2001 From: "S. Zharinov" Date: Sun, 17 Jan 2016 22:06:22 +0300 Subject: [PATCH 1687/4033] CLJS-1547: Wrong output encoding when compile with goog.LOCALE Closure Compiler uses ASCII by default because of legacy reasons. Non-ASCII characters are escaped. It may lead to larger builds when using i18n facilities. Also, when using goog.i18n with custom goog.LOCALE option, REPL users can see formatted dates in legacy encodings of their languages (for example, CP1252 for goog.LOCALE="ru"). This patch provides UTF-8 encoding by default. Possible options are: * US-ASCII (current default) * ISO-8859-1 * UTF-8 (proposed default) * UTF-16BE * UTF-16LE * UTF-16 (https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html) --- src/main/clojure/cljs/closure.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2d8b90131..6c8b38680 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -211,6 +211,9 @@ (. compiler-options (setExtraAnnotationNames (map name (:closure-extra-annotations opts))))) + (. compiler-options + (setOutputCharset (:closure-output-charset opts "UTF-8"))) + compiler-options) (defn ^CompilerOptions make-options From db695a93c93c23dbdbcd846bd342fa76d4117a58 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 24 Jan 2016 09:27:22 -0500 Subject: [PATCH 1688/4033] Update Maven dependency information --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3265302e6..9e57f6c4d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Latest stable release: 1.7.228 org.clojure clojurescript - 1.7.170 + 1.7.228 ``` From 58cd6be66877ba43d1642138360b916de0983917 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 1 Feb 2016 19:27:54 -0500 Subject: [PATCH 1689/4033] CLJS-620: Warnings are generated when using a macro in argument position If the analyzer is about to emit a "use of undeclared Var" warning, first check to see if there is a macro present with the same name, and, in that case, change the emitted diagnostic to instead display a "can't take value of macro" warning. --- src/main/clojure/cljs/analyzer.cljc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f4b6f6ad1..c6f56d583 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -220,7 +220,10 @@ (defmethod error-message :undeclared-var [warning-type info] - (str "Use of undeclared Var " (:prefix info) "/" (:suffix info))) + (str (if (:macro-present? info) + "Can't take value of macro " + "Use of undeclared Var ") + (:prefix info) "/" (:suffix info))) (defmethod error-message :undeclared-ns [warning-type {:keys [ns-sym js-provide] :as info}] @@ -599,9 +602,14 @@ [env prefix suffix] (contains? implicit-nses prefix)) +(declare get-expander) + (defn confirm-var-exist-warning [env prefix suffix] (fn [env prefix suffix] - (warning :undeclared-var env {:prefix prefix :suffix suffix}))) + (warning :undeclared-var env + {:prefix prefix + :suffix suffix + :macro-present? (not (nil? (get-expander (symbol (str prefix) (str suffix)) env)))}))) (defn loaded-js-ns? "Check if a JavaScript namespace has been loaded. JavaScript vars are @@ -663,8 +671,6 @@ #?(:clj (nil? (util/ns->source ns-sym)))) (warning :undeclared-ns env {:ns-sym ns-sym}))) -(declare get-expander) - (defn core-name? "Is sym visible from core in the current compilation namespace?" #?(:cljs {:tag boolean}) From 170cece9953d1aba70bb489dba03f1a65b15ec43 Mon Sep 17 00:00:00 2001 From: Yehonathan Sharvit Date: Mon, 1 Feb 2016 08:36:55 +0200 Subject: [PATCH 1690/4033] CLJS-1318: Fix typo in documentation of `specify` In the docstring of specify, replace `specify` by `specify!` --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index ba5b1919b..7ac838ae7 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1284,7 +1284,7 @@ ~x))) (core/defmacro specify - "Identical to specify but does not mutate its first argument. The first + "Identical to specify! but does not mutate its first argument. The first argument must be an ICloneable instance." [expr & impls] `(cljs.core/specify! (cljs.core/clone ~expr) From 4b44c3e4d48c14d47eaa46c203fcc28df16d3941 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 31 Jan 2016 22:30:51 -0500 Subject: [PATCH 1691/4033] CLJS-1542: Self-host: cljs/compile-str not handling errors properly If you use cljs.js/compile-str on a form that cannot be analyzed, then the analysis error is wrapped, but inadvertently passed on to compilation as an AST structure. This results in the compiler derailing because it tries to process a nil :op. The fix is to employ the same pattern used in cljs.js/eval-str, namely: Wrap successful analysis in a {:value ast} map, and then additionally check for an :error key in the result and cb early, otherwise extract the :value and continue on to compilation. --- src/main/cljs/cljs/js.cljs | 23 +++++++++++++---------- src/test/self/self_host/test.cljs | 16 ++++++++++++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 6aad5d167..8a1e42cae 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -621,20 +621,23 @@ (let [aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) (:context opts) (assoc :context (:context opts)) (:def-emits-var opts) (assoc :def-emits-var true)) - ast (try - (ana/analyze aenv form nil opts) + res (try + {:value (ana/analyze aenv form nil opts)} (catch :default cause (wrap-error (ana/error aenv (str "Could not compile " name) cause))))] - (.append sb (with-out-str (comp/emit ast))) - (if (= :ns (:op ast)) - (ns-side-effects bound-vars aenv ast opts - (fn [res] - (if (:error res) - (cb res) - (compile-loop (:name ast))))) - (recur ns))) + (if (:error res) + (cb res) + (let [ast (:value res)] + (.append sb (with-out-str (comp/emit ast))) + (if (= :ns (:op ast)) + (ns-side-effects bound-vars aenv ast opts + (fn [res] + (if (:error res) + (cb res) + (compile-loop (:name ast))))) + (recur ns))))) (do (when (:source-map opts) (append-source-map env/*compiler* diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 888db6fc0..b3da160fe 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -52,7 +52,7 @@ (deftest test-analyze-str (async done - (let [l (latch 2 done)] + (let [l (latch 3 done)] (cljs/analyze-str st "(+ 1 1)" nil {:context :expr} (fn [{:keys [error value]}] @@ -63,11 +63,17 @@ {:context :expr} (fn [{:keys [error value]}] (is (nil? error)) + (inc! l))) + (cljs/analyze-str st "(fn [] (let [x 7 y] (prn y)))" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "Could not analyze " (ex-message error))) (inc! l)))))) (deftest test-compile-str (async done - (let [l (latch 6 done)] + (let [l (latch 7 done)] (cljs/compile-str st "(+ 1 1)" (fn [{:keys [error value]}] (is (nil? error)) @@ -103,6 +109,12 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (string/index-of value "cljs.user.foo((1),(2))")) + (inc! l))) + (cljs/compile-str st "(fn [] (let [x 7 y] (prn y)))" nil + {:context :expr} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "Could not compile " (ex-message error))) (inc! l)))))) (deftest test-eval-str From 609400910e983849e5b7932a4b7d0b0dda465df6 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 31 Jan 2016 18:27:10 -0500 Subject: [PATCH 1692/4033] CLJS-1557: Make special-symbol? return true for catch and finally --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 99868a068..c8a7b9ea0 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9955,7 +9955,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn ^boolean special-symbol? [x] (contains? - '#{if def fn* do let* loop* letfn* throw try + '#{if def fn* do let* loop* letfn* throw try catch finally recur new set! ns deftype* defrecord* . js* & quote} x)) From d63ad27bbf431274428b9ff391779bac04fef2d0 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 29 Jan 2016 21:44:51 -0500 Subject: [PATCH 1693/4033] CLJS-1488: cljs.repl/source Cannot read source of cljs functions that use #js reader --- src/main/clojure/cljs/repl.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 723efecc7..c49ba7ec8 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -13,6 +13,7 @@ [clojure.data.json :as json] [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers] + [cljs.tagged-literals :as tags] [clojure.stacktrace :as trace] [clojure.repl :as cljrepl] [clojure.edn :as edn] @@ -1225,8 +1226,9 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (with-open [pbr (PushbackReader. (io/reader f))] (let [rdr (readers/source-logging-push-back-reader pbr)] (dotimes [_ (dec (:line v))] (readers/read-line rdr)) - (-> (reader/read {:read-cond :allow :features #{:cljs}} rdr) - meta :source)))))))) + (binding [reader/*data-readers* tags/*cljs-data-readers*] + (-> (reader/read {:read-cond :allow :features #{:cljs}} rdr) + meta :source))))))))) (comment (def cenv (env/default-compiler-env)) From 89ec69f06919afaa102a078606dc0ab8b1015fb5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 27 Jan 2016 09:57:31 -0500 Subject: [PATCH 1694/4033] CLJS-1552: doc for & should match fn Currently `(doc &)` produces `(empty)` doc as if fn is a special form. This revision simply delegates to doc so that `(doc &)` is converted to `(doc fn)`. This results in the intended doc for the fn macro to be displayed. --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index c49ba7ec8..7e0d9aae1 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1154,7 +1154,7 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (binding [cljs.core/*print-newline* true] (with-out-str ~(if-let [special-name ('{& fn catch try finally try} name)] - `(cljs.repl/print-doc (quote ~(special-doc special-name))) + `(doc ~special-name) (cond (special-doc-map name) `(cljs.repl/print-doc (quote ~(special-doc name))) From 8f7b1d02d113e49965ebcfe418454b93ba45a6e5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 22 Jan 2016 23:06:32 -0500 Subject: [PATCH 1695/4033] CLJS-1551: Self-host: assert-args dormant in macros Since self-host must call macros defined in a different compilation stage, the assert-args macro used in if-let and other macros must be defined in a different namespace entirely (they can't be in cljs.core because this namespace is already being compiled as a macros namespace at this point). Introduce a cljs.support namespace for this purpose, and to keep things simple, also arrange to have non-bootstrap use this namespace for the purpose of assert-args. --- src/main/clojure/cljs/core.cljc | 38 ++++++++++-------------------- src/main/clojure/cljs/support.cljc | 18 ++++++++++++++ src/test/self/self_host/test.cljs | 23 ++++++++++++++++++ 3 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 src/main/clojure/cljs/support.cljc diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 7ac838ae7..5a0cf5474 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -7,8 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.core - (:refer-clojure :exclude [-> ->> .. amap and areduce alength aclone assert binding bound-fn case comment cond condp - declare definline definterface defmethod defmulti defn defn- defonce + (:refer-clojure :exclude [-> ->> .. amap and areduce alength aclone assert assert-args 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 @@ -35,19 +35,21 @@ cond-> cond->> as-> some-> some->> if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand - #?@(:cljs [alias assert-args coercive-not coercive-not= coercive-= coercive-boolean + #?@(:cljs [alias coercive-not coercive-not= coercive-= coercive-boolean truth_ js-arguments js-delete js-in js-debugger exists? divide js-mod unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? specify copy-arguments goog-define js-comment js-inline-comment unsafe-cast])]) - #?(:cljs (:require-macros [cljs.core :as core])) + #?(:cljs (:require-macros [cljs.core :as core] + [cljs.support :refer [assert-args]])) (:require clojure.walk clojure.set [clojure.string :as string] [cljs.compiler :as comp] [cljs.env :as env] + #?(:clj [cljs.support :refer [assert-args]]) #?(:cljs [cljs.core :as core]) #?(:cljs [cljs.analyzer :as ana]))) @@ -338,9 +340,9 @@ ([bindings then] `(if-let ~bindings ~then nil)) ([bindings then else & oldform] - (core/assert-args + (assert-args if-let (vector? bindings) "a vector for its binding" - (nil? oldform) "1 or 2 forms after binding vector" + (empty? oldform) "1 or 2 forms after binding vector" (= 2 (count bindings)) "exactly 2 forms in binding vector") (core/let [form (bindings 0) tst (bindings 1)] `(let [temp# ~tst] @@ -396,7 +398,7 @@ Roughly the same as (when (seq xs) (let [x (first xs)] body)) but xs is evaluated only once" [bindings & body] - (core/assert-args + (assert-args when-first (vector? bindings) "a vector for its binding" (= 2 (count bindings)) "exactly 2 forms in binding vector") (core/let [[x xs] bindings] @@ -410,7 +412,7 @@ When test is true, evaluates body with binding-form bound to the value of test" [bindings & body] - (core/assert-args + (assert-args when-let (vector? bindings) "a vector for its binding" (= 2 (count bindings)) "exactly 2 forms in binding vector") (core/let [form (bindings 0) tst (bindings 1)] @@ -504,9 +506,9 @@ ([bindings then] `(if-some ~bindings ~then nil)) ([bindings then else & oldform] - (core/assert-args + (assert-args if-some (vector? bindings) "a vector for its binding" - (nil? oldform) "1 or 2 forms after binding vector" + (empty? oldform) "1 or 2 forms after binding vector" (= 2 (count bindings)) "exactly 2 forms in binding vector") (core/let [form (bindings 0) tst (bindings 1)] `(let [temp# ~tst] @@ -522,7 +524,7 @@ When test is not nil, evaluates body with binding-form bound to the value of test" [bindings & body] - (core/assert-args + (assert-args when-some (vector? bindings) "a vector for its binding" (= 2 (count bindings)) "exactly 2 forms in binding vector") (core/let [form (bindings 0) tst (bindings 1)] @@ -602,20 +604,6 @@ `(when-not (exists? ~x) (def ~x ~init))) -(core/defmacro ^{:private true} assert-args [fnname & pairs] - #?(:clj `(do (when-not ~(first pairs) - (throw (IllegalArgumentException. - ~(core/str fnname " requires " (second pairs))))) - ~(core/let [more (nnext pairs)] - (core/when more - (list* `assert-args fnname more)))) - :cljs `(do (when-not ~(first pairs) - (throw (js/Error. - ~(core/str fnname " requires " (second pairs))))) - ~(core/let [more (nnext pairs)] - (core/when more - (list* `assert-args fnname more)))))) - (core/defn destructure [bindings] (core/let [bents (partition 2 bindings) pb (core/fn pb [bvec b v] diff --git a/src/main/clojure/cljs/support.cljc b/src/main/clojure/cljs/support.cljc new file mode 100644 index 000000000..d63c8341f --- /dev/null +++ b/src/main/clojure/cljs/support.cljc @@ -0,0 +1,18 @@ +; 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.support) + +(defmacro assert-args + "Internal - do not use!" + [fnname & pairs] + `(do (when-not ~(first pairs) + (throw (ex-info ~(str fnname " requires " (second pairs)) {}))) + ~(let [more (nnext pairs)] + (when more + (list* `assert-args fnname more))))) \ No newline at end of file diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index b3da160fe..2f1448fcf 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -217,6 +217,29 @@ (fn [{:keys [error value]}] (is (= "1" value))))) +(deftest test-CLJS-1551 + (cljs/eval-str st + "(if-let [x true y true] 3)" + nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "if-let requires exactly 2 forms in binding vector at line 1 " (ex-message (ex-cause error)))))) + (cljs/eval-str st + "(if-let [x true] 1 2 3)" + nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "if-let requires 1 or 2 forms after binding vector at line 1 " (ex-message (ex-cause error)))))) + (cljs/eval-str st + "(if-let '(x true) 1)" + nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "if-let requires a vector for its binding at line 1 " (ex-message (ex-cause error))))))) + #_(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] From c0a4e07426b67659ae575cc731e83f7007c36c43 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 22 Jan 2016 18:33:26 -0500 Subject: [PATCH 1696/4033] CLJS-1550: Enhance docstring for extend-type wrt type-sym --- src/main/clojure/cljs/core.cljc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 5a0cf5474..d8b3fc8c8 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1472,8 +1472,22 @@ (core/defmacro extend-type "Extend a type to a series of protocols. Useful when you are - supplying the definitions explicitly inline. Propagates the - type as a type hint on the first argument of all fns. + supplying the definitions explicitly inline. Propagates the + type as a type hint on the first argument of all fns. + + type-sym may be + + * default, meaning the definitions will apply for any value, + unless an extend-type exists for one of the more specific + cases below. + * nil, meaning the definitions will apply for the nil value. + * any of object, boolean, number, string, array, or function, + indicating the definitions will apply for values of the + associated base JavaScript types. Note that, for example, + string should be used instead of js/String. + * a JavaScript type not covered by the previous list, such + as js/RegExp. + * a type defined by deftype or defrecord. (extend-type MyType ICounted From cca12a51257b71a66156a090aba06dcd13949c0a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 6 Feb 2016 09:06:33 -0500 Subject: [PATCH 1697/4033] CLJS-1541: Self-host: Cannot require 'cljs.js using cljs.jar This change updates things to mimic the handling of the defn macro defined previously in the file. A result is that defmacro is available in the standalone shipping cljs.jar and this then makes it possible to (require 'cljs.js). --- src/main/clojure/cljs/core.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index d8b3fc8c8..a98816415 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2924,4 +2924,5 @@ (cons `defn decl) (core/list 'set! `(. ~name ~'-cljs$lang$macro) true)))) -#?(:cljs (set! (. defmacro -cljs$lang$macro) true)) +#?(:clj (. (var defmacro) (setMacro)) + :cljs (set! (. defmacro -cljs$lang$macro) true)) From 22c10ba8304a8e3f6da9a903042ce1a09f03e370 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 9 Feb 2016 22:48:51 -0500 Subject: [PATCH 1698/4033] CLJS-1565: Self-host: whitespace optimization is broken If generating single-file (in particular :whitespace) output for a project that makes use of cljs.js, it is possible for the concatenated file to have cljs.js appear prior to cljs.core$macros, and thus cause a dependency issue when loading the file. This is a consequence of the fact that the cljs.closure.JavaScriptFile compiled output for cljs.js doesn't specify cljs.core$macros macros as a dependency. There are already a few places where the cljs.core$macros dependency is patched in. This revision factors out a bit of that logic and adds it to another needed place. --- src/main/clojure/cljs/closure.clj | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 6c8b38680..891c4b804 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -585,17 +585,23 @@ (js-dependencies {:libs ["closure/library/third_party/closure"]} ["goog.dom.query"]) ) +(defn- add-core-macros-if-cljs-js + "If a compiled entity is the cljs.js namespace, explicitly + add the cljs.core macros namespace dependency to it." + [compiled] + (cond-> compiled + ;; TODO: IJavaScript :provides :requires should really + ;; always be Vector - David + (= ["cljs.js"] (into [] (map str) (deps/-provides compiled))) + (update-in [:requires] concat ["cljs.core$macros"]))) + (defn get-compiled-cljs "Return an IJavaScript for this file. Compiled output will be written to the working directory." [opts {:keys [relative-path uri]}] (let [js-file (comp/rename-to-js relative-path) compiled (-compile uri (merge opts {:output-file js-file}))] - (cond-> compiled - ;; TODO: IJavaScript :provides :requires should really - ;; always be Vector - David - (= ["cljs.js"] (into [] (map str) (deps/-provides compiled))) - (update-in [:requires] concat ["cljs.core$macros"])))) + (add-core-macros-if-cljs-js compiled))) (defn cljs-source-for-namespace "Given a namespace return the corresponding source with either a .cljs or @@ -1896,6 +1902,7 @@ (add-dependency-sources compile-opts) deps/dependency-order (compile-sources compiler-stats compile-opts) + (#(map add-core-macros-if-cljs-js %)) (add-js-sources all-opts) (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") all-opts)])) deps/dependency-order From fcdca3d3d2196e18b851ccf8c5328dbd50acfb97 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sat, 6 Feb 2016 13:34:49 +0100 Subject: [PATCH 1699/4033] CLJS-970: generate assert message when compiling --- src/main/clojure/cljs/core.cljc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a98816415..6d2caec1a 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2168,13 +2168,12 @@ ([x] (core/when *assert* `(when-not ~x - (throw (js/Error. - (cljs.core/str "Assert failed: " (cljs.core/pr-str '~x))))))) + (throw (js/Error. ~(core/str "Assert failed: " (core/pr-str x))))))) ([x message] (core/when *assert* `(when-not ~x (throw (js/Error. - (cljs.core/str "Assert failed: " ~message "\n" (cljs.core/pr-str '~x)))))))) + (cljs.core/str "Assert failed: " ~message "\n" ~(core/pr-str x)))))))) (core/defmacro for "List comprehension. Takes a vector of one or more From baaa73ace52f5a23de026971d1910b8905ad8ca8 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 31 Jan 2016 10:44:46 -0500 Subject: [PATCH 1700/4033] CLJS-1555: make-array macro missing 2 arg arity --- src/main/clojure/cljs/core.cljc | 14 ++++++++------ src/test/cljs/cljs/core_test.cljs | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 6d2caec1a..71c037f6a 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2328,12 +2328,14 @@ assoc :tag 'array))) (core/defmacro make-array - [size] - (vary-meta - (if (core/number? size) - `(array ~@(take size (repeat nil))) - `(js/Array. ~size)) - assoc :tag 'array)) + ([size] + (vary-meta + (if (core/number? size) + `(array ~@(take size (repeat nil))) + `(js/Array. ~size)) + assoc :tag 'array)) + ([type size] + `(make-array ~size))) (core/defmacro list ([] '(.-EMPTY cljs.core/List)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 2d67765f5..c3d7f7332 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1044,6 +1044,25 @@ (apply aset a [0 0 "bar"]) (is (= (aget a 0 0) "bar")))))) +(defn- primitive-arrays-equal + [a b] + (= (js->clj a) (js->clj b))) + +(deftest test-make-array + (testing "Testing make-array" + (is (primitive-arrays-equal #js [] (make-array 0))) + (is (primitive-arrays-equal #js [] (apply make-array [0]))) + (is (primitive-arrays-equal #js [nil] (make-array 1))) + (is (primitive-arrays-equal #js [nil] (apply make-array [1]))) + (is (primitive-arrays-equal #js [nil nil] (make-array 2))) + (is (primitive-arrays-equal #js [nil nil] (apply make-array [2]))) + (is (primitive-arrays-equal #js [] (make-array nil 0))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0]))) + (is (primitive-arrays-equal #js [nil] (make-array nil 1))) + (is (primitive-arrays-equal #js [nil] (apply make-array [nil 1]))) + (is (primitive-arrays-equal #js [nil nil] (make-array nil 2))) + (is (primitive-arrays-equal #js [nil nil] (apply make-array [nil 2]))))) + (deftest test-rearrange-sequential (testing "Test rearranging sequential collections" (is (= [1 2 3 4 5] (sort [5 3 1 4 2]))) From 74164b47e0254fbae319596ab4b517e0ce44ea68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 19 Oct 2015 19:27:59 +0200 Subject: [PATCH 1701/4033] CLJS-1411: make-array signature differs from clojure - added new function signature to match that of Clojure's - added warning about the computational cost of creating multi-dimensional arrays in JavaScript to the docstring --- src/main/cljs/cljs/core.cljs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c8a7b9ea0..8bdb3ef2a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -354,12 +354,20 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; arrays ;;;;;;;;;;;;;;;; (defn ^array make-array - "Construct a JavaScript array of specified size. Accepts ignored type - argument for compatibility with Clojure." + "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 + will run in polynomial time when called with 3 or more arguments." ([size] (js/Array. size)) ([type size] - (make-array size))) + (make-array size)) + ([type size & more-sizes] + (let [dims more-sizes + dimarray (make-array size)] + (dotimes [i (alength dimarray)] + (aset dimarray i (apply make-array nil dims))) + dimarray))) (defn aclone "Returns a javascript array, cloned from the passed in array" From 1538599b2e28b605c7837b45e23e9d3129c681a9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Feb 2016 17:05:01 -0500 Subject: [PATCH 1702/4033] bump to Clojure 1.8 --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index 6a293dcd2..5c343ded1 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.7.0" +CLOJURE_RELEASE="1.8.0" CLOSURE_RELEASE="20151216" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" From d4055ec82af3184fb1321016e7c85e492cf65362 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Feb 2016 17:11:08 -0500 Subject: [PATCH 1703/4033] 1.8 revision --- script/build | 2 +- script/revision | 2 +- script/uberjar | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/script/build b/script/build index 850055ffd..9c2c87bfc 100755 --- a/script/build +++ b/script/build @@ -19,7 +19,7 @@ POM_FILE="pom.xml" # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="7" +MINOR="8" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so diff --git a/script/revision b/script/revision index 3e3e2c2bf..b098ecca2 100755 --- a/script/revision +++ b/script/revision @@ -10,7 +10,7 @@ set -ex # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="7" +MINOR="8" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so diff --git a/script/uberjar b/script/uberjar index 8b3677546..d1a4267fe 100755 --- a/script/uberjar +++ b/script/uberjar @@ -13,7 +13,7 @@ set -ex # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="7" +MINOR="8" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so From 70a43138844e19f771ef20bc6e847829ce0a5711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 15 Feb 2016 01:05:23 +0100 Subject: [PATCH 1704/4033] CLJS-744: ISequential types should implement JS indexOf, lastIndexOf - implements `indexOf` and `lastIndexOf` for ISequential types - the implementation for both functions conforms to the TC39 specification so that the behavior is the same across native host types and ClojureScript persistent data structures --- src/main/cljs/cljs/core.cljs | 226 +++++++++++++++++++++++++++++- src/test/cljs/cljs/core_test.cljs | 39 ++++++ 2 files changed, 264 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8bdb3ef2a..5b49aa9fb 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1371,7 +1371,7 @@ reduces them without incurring seq initialization" (recur nval (inc n)))) val))))) -(declare hash-coll cons RSeq List) +(declare hash-coll cons drop count nth RSeq List) (defn ^boolean counted? "Returns true if coll implements count in constant time" @@ -1381,6 +1381,40 @@ reduces them without incurring seq initialization" "Returns true if coll implements nth in constant time" [x] (satisfies? IIndexed x)) +(defn- -indexOf + ([coll x] + (-indexOf coll x 0)) + ([coll x start] + (let [len (count coll)] + (if (>= start len) + -1 + (loop [idx (cond + (pos? start) start + (neg? start) (max 0 (+ start len)) + :else start)] + (if (< idx len) + (if (= (nth coll idx) x) + idx + (recur (inc idx))) + -1)))))) + +(defn- -lastIndexOf + ([coll x] + (-lastIndexOf coll x (count coll))) + ([coll x start] + (let [len (count coll)] + (if (zero? len) + -1 + (loop [idx (cond + (pos? start) (min (dec len) start) + (neg? start) (+ len start) + :else start)] + (if (>= idx 0) + (if (= (nth coll idx) x) + idx + (recur (dec idx))) + -1)))))) + (deftype IndexedSeqIterator [arr ^:mutable i] Object (hasNext [_] @@ -1396,6 +1430,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) ICloneable (-clone [_] (IndexedSeq. arr i)) @@ -1486,6 +1528,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) ICloneable (-clone [_] (RSeq. ci i meta)) @@ -2708,6 +2758,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x count)) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IList @@ -2774,6 +2832,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IList @@ -2869,6 +2935,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IList @@ -3021,6 +3095,14 @@ reduces them without incurring seq initialization" (set! s (fn)) (set! fn nil) s))) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IPending (-realized? [x] @@ -3135,6 +3217,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IWithMeta (-with-meta [coll m] @@ -3670,6 +3760,16 @@ reduces them without incurring seq initialization" (MultiStepper. (xform stepfn) iters nexts)))) (deftype LazyTransformer [^:mutable stepper ^:mutable first ^:mutable rest meta] + Object + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) + IWithMeta (-with-meta [this new-meta] (LazyTransformer. stepper first rest new-meta)) @@ -4752,6 +4852,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) ICloneable (-clone [_] (PersistentVector. meta cnt shift root tail __hash)) @@ -4974,6 +5082,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IWithMeta (-with-meta [coll m] @@ -5058,6 +5174,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) ICloneable (-clone [_] (Subvec. meta v start end __hash)) @@ -5389,6 +5513,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IWithMeta (-with-meta [coll meta] (PersistentQueueSeq. meta front rear __hash)) @@ -5429,6 +5561,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) ICloneable (-clone [coll] (PersistentQueue. meta count front rear __hash)) @@ -5788,6 +5928,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IMeta (-meta [coll] _meta) @@ -6688,6 +6836,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IMeta (-meta [coll] meta) @@ -6751,6 +6907,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IMeta (-meta [coll] meta) @@ -7086,6 +7250,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) ISeqable (-seq [this] this) @@ -7269,6 +7441,15 @@ reduces them without incurring seq initialization" (kv-reduce [node f init] (tree-map-kv-reduce node f init)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) + IMapEntry (-key [node] key) (-val [node] val) @@ -7412,6 +7593,15 @@ reduces them without incurring seq initialization" (kv-reduce [node f init] (tree-map-kv-reduce node f init)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) + IMapEntry (-key [node] key) (-val [node] val) @@ -7799,6 +7989,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IMeta (-meta [coll] _meta) @@ -7867,6 +8065,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) IMeta (-meta [coll] _meta) @@ -8451,6 +8657,14 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) ICloneable (-clone [_] (Range. meta start end step __hash)) @@ -9273,6 +9487,16 @@ reduces them without incurring seq initialization" (filter (fn [_] (< (rand) prob)) coll))) (deftype Eduction [xform coll] + Object + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) + ISequential ISeqable diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index c3d7f7332..04498aef8 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1077,6 +1077,45 @@ (is (not (empty? shuffles)))) )) +(deftest test-ISequential-indexOf + (testing "Testing JS .indexOf in ISequential types" + ;; PersistentVector + (is (= (.indexOf [] 2) -1)) + (is (= (.indexOf [] 2 3) -1)) + (is (= (.indexOf [1 2 3 4 5] 2) 1)) + (is (= (.indexOf [1 2 3 4 5] 6) -1)) + (is (= (.indexOf [1 2 3 4 5] -1) -1)) + (is (= (.indexOf [1 2 "x" 4 5 "a"] "a") 5)) + (is (= (.indexOf [1 2 3 4 5] 1 2) -1)) + (is (= (.indexOf [1 2 3 4 5] 2 2) -1)) + (is (= (.indexOf [1 2 3 1 5] 1 2) 3)) + (is (= (.indexOf [1 2 3 4 5] 2) 1)) + (is (= (.indexOf '(1 2 3 4 5) 2) 1)) + (is (= (.indexOf (list 1 2 3) 3) 2)) + (is (= (.indexOf (lazy-seq [1 2 3 4 5]) 3)) 2) + (is (= (.indexOf (sequence (map inc) '(0 1 2 3 4)) 5) 4)))) + +(deftest test-ISequential-lastIndexOf + (testing "Testing JS .lastIndexOf in ISequential types" + ;; PersistentVector + (is (= (.lastIndexOf [] 2) -1)) + (is (= (.lastIndexOf [] 2 3) -1)) + (is (= (.lastIndexOf [1 2 3 4 5] 2) 1)) + (is (= (.lastIndexOf [1 2 3 1 5] 1) 3)) + (is (= (.lastIndexOf [1 2 3 1 5] 1 3) 3)) + (is (= (.lastIndexOf [1 2 3 1 5] 1 2) 0)) + (is (= (.lastIndexOf [1 2 3 1] 1 0) 0)) + (is (= (.lastIndexOf [1 2 3 4 5] 3 100) 2)) + (is (= (.lastIndexOf [1 1 1 1 1] 1) 4)) + (is (= (.lastIndexOf [1 1 1 1 1] 1 6) 4)) + (is (= (.lastIndexOf [1 2 1 1 1] 2) 1)) + (is (= (.lastIndexOf [1 2 3 4 5] 3 -100) -1)) + (is (= (.lastIndexOf [1 2 3 4 5] 3 -2) 2)) + (is (= (.lastIndexOf '(1 2 1 4 5) 1) 2)) + (is (= (.lastIndexOf (list 1 2 3 1 5) 1) 3)) + (is (= (.lastIndexOf (lazy-seq [1 2 1 4 5]) 1)) 2) + (is (= (.lastIndexOf (sequence (map inc) '(0 1 0 3 4)) 1) 2)))) + (deftest test-js-clj-conversions (testing "Testing JS / CLJS data conversions" (testing "js->clj" From db83b5262a39a419d7cb6af6429edefedde18899 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Feb 2016 16:53:23 -0500 Subject: [PATCH 1705/4033] less cryptic error if :main doesn't correspond to any file --- src/main/clojure/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 891c4b804..06ed2f066 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1893,7 +1893,10 @@ (let [one-file? (and (:main all-opts) (#{:advanced :simple} (:optimizations all-opts))) source (if one-file? - (:uri (cljs-source-for-namespace (:main all-opts))) + (let [main (:main all-opts) + uri (:uri (cljs-source-for-namespace main))] + (assert uri (str "No file for namespace " main " exists")) + uri) source) compile-opts (if one-file? (assoc all-opts :output-file (:output-to all-opts)) From d6e39e9da9db04fd220e36a0341ebb6d172a6dba Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Feb 2016 16:53:34 -0500 Subject: [PATCH 1706/4033] move apply declare --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5b49aa9fb..4590a3ca2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -353,6 +353,8 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; arrays ;;;;;;;;;;;;;;;; +(declare apply) + (defn ^array make-array "Construct a JavaScript array of the specified dimensions. Accepts ignored type argument for compatibility with Clojure. Note that there is no efficient @@ -390,8 +392,6 @@ (recur (inc i))) a)))) -(declare apply) - (defn aget "Returns the value at the index." ([array i] From 431b6e86d5d0515baed3b8dc4a82c8fecc41f529 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 15 Feb 2016 00:08:26 -0500 Subject: [PATCH 1707/4033] CLJS-1571: Make special-symbol? true for 'var --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4590a3ca2..62518ef34 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10188,7 +10188,7 @@ Maps become Objects. Arbitrary keys are encoded to by key->js." (defn ^boolean special-symbol? [x] (contains? '#{if def fn* do let* loop* letfn* throw try catch finally - recur new set! ns deftype* defrecord* . js* & quote} + recur new set! ns deftype* defrecord* . js* & quote var} x)) (defn test From 6f14ccbe47b92c461f0571f563a46b2a2354aa59 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 13 Feb 2016 01:44:57 -0500 Subject: [PATCH 1708/4033] CLJS-1567: make-array macro missing > 2 arg arity Adds the additional arity to the macro, along with tests for the macro and fn. --- src/main/clojure/cljs/core.cljc | 10 +++++++++- src/test/cljs/cljs/core_test.cljs | 32 ++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 71c037f6a..03b384a1d 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2335,7 +2335,15 @@ `(js/Array. ~size)) assoc :tag 'array)) ([type size] - `(make-array ~size))) + `(make-array ~size)) + ([type size & more-sizes] + (vary-meta + `(let [dims# (list ~@more-sizes) + dimarray# (make-array ~size)] + (dotimes [i# (alength dimarray#)] + (aset dimarray# i# (apply make-array nil dims#))) + dimarray#) + assoc :tag 'array))) (core/defmacro list ([] '(.-EMPTY cljs.core/List)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 04498aef8..63eb329c2 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1061,7 +1061,37 @@ (is (primitive-arrays-equal #js [nil] (make-array nil 1))) (is (primitive-arrays-equal #js [nil] (apply make-array [nil 1]))) (is (primitive-arrays-equal #js [nil nil] (make-array nil 2))) - (is (primitive-arrays-equal #js [nil nil] (apply make-array [nil 2]))))) + (is (primitive-arrays-equal #js [nil nil] (apply make-array [nil 2]))) + (is (primitive-arrays-equal #js [] (make-array nil 0 0))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0 0]))) + (is (primitive-arrays-equal #js [] (make-array nil 0 1))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0 1]))) + (is (primitive-arrays-equal #js [#js []] (make-array nil 1 0))) + (is (primitive-arrays-equal #js [#js []] (apply make-array [nil 1 0]))) + (is (primitive-arrays-equal #js [#js [] #js []] (make-array nil 2 0))) + (is (primitive-arrays-equal #js [#js [] #js []] (apply make-array [nil 2 0]))) + (is (primitive-arrays-equal #js [#js [nil]] (make-array nil 1 1))) + (is (primitive-arrays-equal #js [#js [nil]] (apply make-array [nil 1 1]))) + (is (primitive-arrays-equal #js [#js [nil] #js [nil]] (make-array nil 2 1))) + (is (primitive-arrays-equal #js [#js [nil] #js [nil]] (apply make-array [nil 2 1]))) + (is (primitive-arrays-equal #js [#js [nil nil] #js [nil nil]] (make-array nil 2 2))) + (is (primitive-arrays-equal #js [#js [nil nil] #js [nil nil]] (apply make-array [nil 2 2]))) + (is (primitive-arrays-equal #js [] (make-array nil 0 0 0))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0 0 0]))) + (is (primitive-arrays-equal #js [] (make-array nil 0 1 1))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0 1 1]))) + (is (primitive-arrays-equal #js [#js []] (make-array nil 1 0 0))) + (is (primitive-arrays-equal #js [#js []] (apply make-array [nil 1 0 0]))) + (is (primitive-arrays-equal #js [#js [] #js []] (make-array nil 2 0 0))) + (is (primitive-arrays-equal #js [#js [] #js []] (apply make-array [nil 2 0 0]))) + (is (primitive-arrays-equal #js [#js [#js []]] (make-array nil 1 1 0))) + (is (primitive-arrays-equal #js [#js [#js []]] (apply make-array [nil 1 1 0]))) + (is (primitive-arrays-equal #js [#js [#js [nil]]] (make-array nil 1 1 1))) + (is (primitive-arrays-equal #js [#js [#js [nil]]] (apply make-array [nil 1 1 1]))) + (is (primitive-arrays-equal #js [#js [#js [nil nil] #js [nil nil]] #js [#js [nil nil] #js [nil nil]]] + (make-array nil 2 2 2))) + (is (primitive-arrays-equal #js [#js [#js [nil nil] #js [nil nil]] #js [#js [nil nil] #js [nil nil]]] + (apply make-array [nil 2 2 2]))))) (deftest test-rearrange-sequential (testing "Test rearranging sequential collections" From ce25dce6c332f395e550d6b572f76e3482b1acec Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 13 Feb 2016 20:46:35 -0500 Subject: [PATCH 1709/4033] CLJS-1492: Warn when using :optimisations instead of :optimizations Detect when an unknown compiler or REPL option is passed by comparing against explicitly-maintained set of all known options. When an unknown option is passed, find a known one with minimum Levenshtein distance from the unknown option (within a threshold), and warn and suggest correct option name. --- src/main/clojure/cljs/build/api.clj | 3 +++ src/main/clojure/cljs/closure.clj | 9 +++++++ src/main/clojure/cljs/repl.cljc | 9 +++++++ src/main/clojure/cljs/util.cljc | 39 ++++++++++++++++++++++++++++ src/test/clojure/cljs/util_tests.clj | 20 ++++++++++++++ 5 files changed, 80 insertions(+) create mode 100644 src/test/clojure/cljs/util_tests.clj diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index e9b59ae2c..d8cbd9966 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -203,6 +203,9 @@ env/*compiler* (env/default-compiler-env opts)))) ([source opts compiler-env] + (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)] + (println (str "WARNING: Unknown compiler option '" unknown-opt "'." + (when suggested-opt (str " Did you mean '" suggested-opt "'?"))))) (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/build source opts compiler-env)))) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 06ed2f066..b5a2a8da6 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -160,6 +160,15 @@ :unknown-defines DiagnosticGroups/UNKNOWN_DEFINES :visiblity DiagnosticGroups/VISIBILITY}) +(def known-opts + "Set of all known compiler options." + #{:anon-fn-naming-policy :asset-path :cache-analysis :closure-defines :closure-extra-annotations + :closure-warnings :compiler-stats :dump-core :elide-asserts :externs :foreign-libs + :hashbang :language-in :language-out :libs :main :modules :source-map-path :optimizations + :optimize-constants :output-dir :output-to :output-wrapper :parallel-build :preamble + :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map + :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings}) + (defn set-options "TODO: Add any other options that we would like to support." [opts ^CompilerOptions compiler-options] diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 7e0d9aae1..ee8dca22f 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -10,6 +10,7 @@ (:refer-clojure :exclude [load-file]) (:require [clojure.java.io :as io] [clojure.string :as string] + [clojure.set :as set] [clojure.data.json :as json] [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers] @@ -36,6 +37,11 @@ (def ^:dynamic *cljs-verbose* false) (def ^:dynamic *repl-opts* nil) +(def known-repl-opts + "Set of all known REPL options." + #{:analyze-path :caught :def-emits-var :flush :need-prompt :print :print-no-newline :prompt :read + :reader :repl-verbose :watch :watch-fn}) + (defmacro err-out [& body] `(binding [*out* *err*] ~@body)) @@ -773,6 +779,9 @@ [cljs.pprint :refer [pprint] :refer-macros [pp]]] bind-err true} :as opts}] + (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) (set/union known-repl-opts cljsc/known-opts))] + (println (str "WARNING: Unknown option '" unknown-opt "'." + (when suggested-opt (str " Did you mean '" suggested-opt "'?"))))) (let [repl-opts (-repl-options repl-env) repl-requires (into repl-requires (:repl-requires repl-opts)) {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 6bccb4a70..2b4bb095c 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -208,3 +208,42 @@ (defn boolean? [x] (or (true? x) (false? x))) + +(defn levenshtein-distance + "The the minimum number of single-element edits needed to + transform s in to t." + [s t] + (let [f (fn [f s t] + (cond + (empty? s) (count t) + (empty? t) (count s) + :else (let [cost (if (= (first s) (first t)) + 0 + 1)] + (min (inc (f f (rest s) t)) + (inc (f f s (rest t))) + (+ cost (f f (rest s) (rest t))))))) + g (memoize f)] + (g g s t))) + +(defn suggestion + "Provides a best suggestion for an unknown, taken from knowns, + minimizing the Levenshtein distance, returning nil if threshold + cannot be satisfied." + [threshold unknown knowns] + (let [distance (partial levenshtein-distance unknown) + closest (apply min-key distance knowns) + closest-dist (distance closest)] + (when (<= closest-dist threshold) + closest))) + +(defn unknown-opts + "Takes a set of passed opt keys and known opt keys and for each + unknown opt key returns a vector of the key and its (potentially + nil) suggestion." + [passed knowns] + {:pre [(set? passed) (set? knowns)]} + (for [unknown (set/difference passed knowns)] + [unknown (some-> (suggestion 3 (str unknown) (map str knowns)) + (subs 1) + keyword)])) diff --git a/src/test/clojure/cljs/util_tests.clj b/src/test/clojure/cljs/util_tests.clj new file mode 100644 index 000000000..01b1dd40f --- /dev/null +++ b/src/test/clojure/cljs/util_tests.clj @@ -0,0 +1,20 @@ +(ns cljs.util-tests + (:require [cljs.util :as util]) + (:use clojure.test)) + +(deftest test-levenshtein-distance + (testing "levenshtein-distance" + (is (= 0 (util/levenshtein-distance "abc" "abc"))) + (is (= 1 (util/levenshtein-distance "abc" "abcd"))) + (is (= 1 (util/levenshtein-distance "abcd" "abc"))) + (is (= 3 (util/levenshtein-distance "kitten" "sitting"))))) + +(deftest test-suggestion + (testing "suggestion" + (is (= ":optimization" (util/suggestion 3 ":optimization" [":optimization" ":static-fns"]))))) + +(deftest test-unknown-opts + (testing "unknown-opts" + (is (= [[:bogus nil] + [:optimisations :optimizations]] + (sort (util/unknown-opts #{:optimisations :bogus} #{:optimizations :static-fns})))))) From b98351ee286ede5261bd8e08762b25294dc27408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 15 Feb 2016 23:34:55 +0100 Subject: [PATCH 1710/4033] CLJS-1569: IndexedSeq doesn't implement IWithMeta / IMeta --- src/main/cljs/cljs/core.cljs | 22 ++++++++++++++-------- src/main/clojure/cljs/core.cljc | 4 ++-- src/test/cljs/cljs/core_test.cljs | 8 ++++++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 62518ef34..d89f71b20 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1087,11 +1087,11 @@ (array? coll) (when-not (zero? (alength coll)) - (IndexedSeq. coll 0)) + (IndexedSeq. coll 0 nil)) (string? coll) (when-not (zero? (alength coll)) - (IndexedSeq. coll 0)) + (IndexedSeq. coll 0 nil)) (native-satisfies? ISeqable coll) (-seq coll) @@ -1424,7 +1424,7 @@ reduces them without incurring seq initialization" (set! i (inc i)) ret))) -(deftype IndexedSeq [arr i] +(deftype IndexedSeq [arr i meta] Object (toString [coll] (pr-str* coll)) @@ -1440,23 +1440,29 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x start)) ICloneable - (-clone [_] (IndexedSeq. arr i)) + (-clone [_] (IndexedSeq. arr i meta)) ISeqable (-seq [this] (when (< i (alength arr)) this)) + IMeta + (-meta [coll] meta) + IWithMeta + (-with-meta [coll new-meta] + (IndexedSeq. arr i new-meta)) + ASeq ISeq (-first [_] (aget arr i)) (-rest [_] (if (< (inc i) (alength arr)) - (IndexedSeq. arr (inc i)) + (IndexedSeq. arr (inc i) nil) (list))) INext (-next [_] (if (< (inc i) (alength arr)) - (IndexedSeq. arr (inc i)) + (IndexedSeq. arr (inc i) nil) nil)) ICounted @@ -1511,7 +1517,7 @@ reduces them without incurring seq initialization" (prim-seq prim 0)) ([prim i] (when (< i (alength prim)) - (IndexedSeq. prim i)))) + (IndexedSeq. prim i nil)))) (defn array-seq "Create a seq from a JavaScript array." @@ -4935,7 +4941,7 @@ reduces them without incurring seq initialization" (-seq [coll] (cond (zero? cnt) nil - (<= cnt 32) (IndexedSeq. tail 0) + (<= cnt 32) (IndexedSeq. tail 0 nil) :else (chunked-seq coll (first-array-for-longvec coll) 0 0))) ICounted diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 03b384a1d..9dc452e7e 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2735,7 +2735,7 @@ (copy-arguments args#) (let [argseq# (when (< ~c-1 (alength args#)) (new ^::ana/no-resolve cljs.core/IndexedSeq - (.slice args# ~c-1) 0))] + (.slice args# ~c-1) 0 nil))] (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#)))))) ~(variadic-fn* rname method))))) @@ -2792,7 +2792,7 @@ ~@(mapcat #(fixed-arity rname %) sigs) ~(if variadic `(let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq - (.slice ~args-sym ~maxfa) 0)] + (.slice ~args-sym ~maxfa) 0 nil)] (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args maxfa) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 63eb329c2..93f295976 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3037,8 +3037,8 @@ ([x0 & xs] [x0 xs])) (deftest test-cljs-1284 - (let [xs (IndexedSeq. #js [] 0) - ys (IndexedSeq. #js [1] 3)] + (let [xs (IndexedSeq. #js [] 0 nil) + ys (IndexedSeq. #js [1] 3 nil)] (is (nil? (first xs))) (is (nil? (seq xs))) (is (= (rest xs) ())) @@ -3106,6 +3106,10 @@ (is (not (== (hash x0) (hash x1) (hash x2) (hash x3) (hash x4) (hash x5) (hash x6) (hash x7)))))) +(deftest test-cljs-1569 + (is (= (meta (with-meta (seq [1 2 3]) {:a 1})) {:a 1}))) + + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 2b948b0f36f897dab9575e6a5f35c70e61ea8a7f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Feb 2016 08:54:07 -0500 Subject: [PATCH 1711/4033] bump tools reader and Google Closure Compiler dependencies charset API changed in GCC, fix cljs.closure/set-options --- pom.template.xml | 4 ++-- project.clj | 4 ++-- script/bootstrap | 4 ++-- src/main/clojure/cljs/closure.clj | 19 ++++++++++++++++++- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 195dd2db6..d0fc4ba6a 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20151216 + v20160125 org.clojure @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.0.0-alpha1 + 1.0.0-alpha3 diff --git a/project.clj b/project.clj index a59189f2c..e47e2db0c 100644 --- a/project.clj +++ b/project.clj @@ -10,9 +10,9 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] :dependencies [[org.clojure/clojure "1.7.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.0.0-alpha1"] + [org.clojure/tools.reader "1.0.0-alpha3"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] - [com.google.javascript/closure-compiler "v20151216"] + [com.google.javascript/closure-compiler "v20160125"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 5c343ded1..c8883fb61 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,11 +3,11 @@ set -e CLOJURE_RELEASE="1.8.0" -CLOSURE_RELEASE="20151216" +CLOSURE_RELEASE="20160125" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.0.0-alpha1" +TREADER_RELEASE="1.0.0-alpha3" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b5a2a8da6..5ea08af04 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -64,6 +64,7 @@ [javax.xml.bind DatatypeConverter] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult] + [java.nio.charset Charset StandardCharsets] [com.sun.nio.file SensitivityWatchEventModifier] [com.google.common.base Throwables])) @@ -169,6 +170,22 @@ :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings}) +(def string->charset + {"iso-8859-1" StandardCharsets/ISO_8859_1 + "us-ascii" StandardCharsets/US_ASCII + "utf-16" StandardCharsets/UTF_16 + "utf-16be" StandardCharsets/UTF_16BE + "utf-16le" StandardCharsets/UTF_16LE + "utf-8" StandardCharsets/UTF_8}) + +(defn to-charset [charset] + (cond + (instance? Charset charset) charset + (string? charset) (get string->charset (string/lower-case charset)) + :else + (throw + (ex-info (str "Invalid :closure-output-charset " charset) {})))) + (defn set-options "TODO: Add any other options that we would like to support." [opts ^CompilerOptions compiler-options] @@ -221,7 +238,7 @@ (setExtraAnnotationNames (map name (:closure-extra-annotations opts))))) (. compiler-options - (setOutputCharset (:closure-output-charset opts "UTF-8"))) + (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8")))) compiler-options) From 48cf99cc6520204dff085692803f90732ac80ba9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 17 Feb 2016 09:44:14 -0500 Subject: [PATCH 1712/4033] cleanup charset handling, improve error message --- src/main/clojure/cljs/closure.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5ea08af04..0d73dedb1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -181,10 +181,15 @@ (defn to-charset [charset] (cond (instance? Charset charset) charset - (string? charset) (get string->charset (string/lower-case charset)) + (and (string? charset) + (contains? string->charset (string/lower-case charset))) + (get string->charset (string/lower-case charset)) :else (throw - (ex-info (str "Invalid :closure-output-charset " charset) {})))) + (ex-info + (str "Invalid :closure-output-charset " charset " given, only " + (string/join ", " (keys string->charset)) " supported ") + {})))) (defn set-options "TODO: Add any other options that we would like to support." From 7d3198d0eeb455cf8ee949df94b7095eecbc3b54 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 17 Feb 2016 17:55:37 -0500 Subject: [PATCH 1713/4033] revert to previous Closure Compiler for now --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index d0fc4ba6a..6fe328b90 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20160125 + v20151216 org.clojure diff --git a/project.clj b/project.clj index e47e2db0c..abf239bf0 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-alpha3"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] - [com.google.javascript/closure-compiler "v20160125"] + [com.google.javascript/closure-compiler "20151216"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index c8883fb61..8d97702db 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.8.0" -CLOSURE_RELEASE="20160125" +CLOSURE_RELEASE="20151216" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" RHINO_RELEASE="1_7R5" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0d73dedb1..a069adeea 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -243,7 +243,9 @@ (setExtraAnnotationNames (map name (:closure-extra-annotations opts))))) (. compiler-options - (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8")))) + (setOutputCharset (:closure-output-charset opts "UTF-8")) + #_(setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler + ) compiler-options) From cfa725745947a54c766af96f8b9790275aa6e6ef Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 18 Feb 2016 10:28:55 -0500 Subject: [PATCH 1714/4033] fix typo in project.clj --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index abf239bf0..dfec256dc 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-alpha3"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] - [com.google.javascript/closure-compiler "20151216"] + [com.google.javascript/closure-compiler "v20151216"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} From dbb7dc9f8a3bbc227c892d55d617aa336f08589e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Feb 2016 10:22:21 -0500 Subject: [PATCH 1715/4033] enable direct linking when building --- pom.template.xml | 3 ++- project.clj | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 6fe328b90..885807536 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.7.0 + 1.8.0 com.google.javascript @@ -208,6 +208,7 @@ UTF-8 src/main/clojure src/main/cljs + true diff --git a/project.clj b/project.clj index dfec256dc..367869710 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] - :dependencies [[org.clojure/clojure "1.7.0"] + :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-alpha3"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] From 0ec3e833fdb280cf3aa78f7c129cc5e36c7d20b2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 19 Feb 2016 10:25:07 -0500 Subject: [PATCH 1716/4033] add direct linking to project.clj --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 367869710..61895ea9d 100644 --- a/project.clj +++ b/project.clj @@ -4,7 +4,7 @@ :url "https://github.com/clojure/clojurescript" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} - :jvm-opts ^:replace ["-Xmx512m" "-server"] + :jvm-opts ^:replace ["-Dclojure.compiler.direct-linking=true" "-Xmx512m" "-server"] :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] From ace9da353d21b4a7430347e6d3f1d99bca6ec857 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 18 Feb 2016 20:36:27 -0500 Subject: [PATCH 1717/4033] CLJS-1580: Self-host: goog.provide offsets source-maps When goog.provide is emitted as a result of processing :ns :op, this modification to the JavaScript offsets the lines in that file by one, thus messing up source mapping lines, if enabled. Instead of simply using str to generate the line, use comp/emitln, which internally does the proper accounting with respect to source maps. --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 8a1e42cae..de76fbe78 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -732,7 +732,7 @@ (if (= :ns (:op ast)) (do (.append sb - (str "goog.provide(\"" (munge (:name ast)) "\");\n")) + (with-out-str (comp/emitln (str "goog.provide(\"" (munge (:name ast)) "\");")))) (ns-side-effects true bound-vars aenv ast opts (fn [res] (if (:error res) From 1e486d2599d60d6d0bdcecbe65a425531701a6c4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 18 Feb 2016 16:10:21 -0500 Subject: [PATCH 1718/4033] CLJS-1579: cljs.source-map/invert-reverse-map discards gcol The innermost update-in should refer to [gcol] in order to invert that bit of the mapping. --- src/main/cljs/cljs/source_map.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index e4d4d1117..7009bfd75 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -292,7 +292,7 @@ (doseq [{:keys [gline gcol name]} column-info] (swap! inverted update-in [gline] (fnil (fn [columns] - (update-in columns [column] (fnil conj []) + (update-in columns [gcol] (fnil conj []) {:line line :col column :name name})) (sorted-map)))))) @inverted)) From 68524a89eea8a8ae6cb69d1329acb3e3025c0fb6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Feb 2016 14:10:26 -0500 Subject: [PATCH 1719/4033] CLJS-1578: Corrupted Analysis Files Break Compilation Don't assume reading the analysis cache is going to work. For example it may have only been partially written out. If a error is thrown during reading the cache, retry and skip the analysis cache stuff. --- src/main/clojure/cljs/analyzer.cljc | 85 ++++++++++++++++------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c6f56d583..de5757d56 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -34,6 +34,7 @@ [cljs.reader :as edn])) #?(:clj (:import [java.io File Reader PushbackReader] [java.net URL] + [java.lang Throwable] [clojure.lang Namespace Var LazySeq ArityException] [cljs.tagged_literals JSValue]))) @@ -2840,57 +2841,63 @@ compiler options, if :cache-analysis true will cache analysis to \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a meaningful value." - ([f] (analyze-file f nil)) + ([f] + (analyze-file f nil)) ([f opts] + (analyze-file f false opts)) + ([f skip-cache opts] (binding [*file-defs* (atom #{})] (let [output-dir (util/output-directory opts) - res (cond - (instance? File f) f - (instance? URL f) f - (re-find #"^file://" f) (URL. f) - :else (io/resource f))] + res (cond + (instance? File f) f + (instance? URL f) f + (re-find #"^file://" f) (URL. f) + :else (io/resource f))] (assert res (str "Can't find " f " in classpath")) (ensure (let [ns-info (parse-ns res) - path (if (instance? File res) - (.getPath ^File res) - (.getPath ^URL res)) - cache (when (:cache-analysis opts) - (cache-file res ns-info output-dir))] + path (if (instance? File res) + (.getPath ^File res) + (.getPath ^URL res)) + cache (when (:cache-analysis opts) + (cache-file res ns-info output-dir))] (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info) :defs]) - (if (or (not cache) (requires-analysis? res output-dir)) + (if (or skip-cache (not cache) (requires-analysis? res output-dir)) (binding [*cljs-ns* 'cljs.user *cljs-file* path reader/*alias-map* (or reader/*alias-map* {})] (when (or *verbose* (:verbose opts)) (util/debug-prn "Analyzing" (str res))) (let [env (assoc (empty-env) :build-options opts) - ns (with-open [rdr (io/reader res)] - (loop [ns nil forms (seq (forms-seq* rdr (util/path res)))] - (if forms - (let [form (first forms) - env (assoc env :ns (get-namespace *cljs-ns*)) - ast (analyze env form nil opts)] - (if (= (:op ast) :ns) - (recur (:name ast) (next forms)) - (recur ns (next forms)))) - ns)))] + ns (with-open [rdr (io/reader res)] + (loop [ns nil forms (seq (forms-seq* rdr (util/path res)))] + (if forms + (let [form (first forms) + env (assoc env :ns (get-namespace *cljs-ns*)) + ast (analyze env form nil opts)] + (if (= (:op ast) :ns) + (recur (:name ast) (next forms)) + (recur ns (next forms)))) + ns)))] (when (and cache (true? (:cache-analysis opts))) (write-analysis-cache ns cache res)))) - ;; we want want to keep dependency analysis information - ;; don't revert the environment - David - (let [{:keys [ns]} - (parse-ns res - (merge opts - {:restore false - :analyze-deps true - :load-macros true}))] - (when (or *verbose* (:verbose opts)) - (util/debug-prn "Reading analysis cache for" (str res))) - (swap! env/*compiler* - (fn [cenv] - (let [cached-ns (edn/read-string (slurp cache))] - (doseq [x (get-in cached-ns [::constants :order])] - (register-constant! x)) - (-> cenv - (assoc-in [::namespaces ns] cached-ns))))))))))))))) + (try + ;; we want want to keep dependency analysis information + ;; don't revert the environment - David + (let [{:keys [ns]} (parse-ns res + (merge opts + {:restore false + :analyze-deps true + :load-macros true})) + cached-ns (edn/read-string (slurp cache))] + (when (or *verbose* (:verbose opts)) + (util/debug-prn "Reading analysis cache for" (str res))) + (swap! env/*compiler* + (fn [cenv] + (let [] + (doseq [x (get-in cached-ns [::constants :order])] + (register-constant! x)) + (-> cenv + (assoc-in [::namespaces ns] cached-ns)))))) + (catch Throwable e + (analyze-file f true opts)))))))))))) From d3a9444ba4ff6b8509f31e50c182b140a3aeaf1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 Feb 2016 13:32:22 +0100 Subject: [PATCH 1720/4033] CLJS-1568: LazyTransformer doesn't implement IMeta --- src/main/cljs/cljs/core.cljs | 3 +++ src/test/cljs/cljs/core_test.cljs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d89f71b20..935cf32cb 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3780,6 +3780,9 @@ reduces them without incurring seq initialization" (-with-meta [this new-meta] (LazyTransformer. stepper first rest new-meta)) + IMeta + (-meta [this] meta) + ICollection (-conj [this o] (cons o (-seq this))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 93f295976..76a5073cc 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1507,7 +1507,9 @@ (is (= (sequence xform data) '(36 200 10)))) (let [xf (map #(+ %1 %2))] (is (= (sequence xf [0 0] [1 2]) [1 2]))) - )) + (is (= (-> (sequence (map inc) [1 2 3]) + (with-meta {:a 1}) + meta) {:a 1})))) (deftest test-obj-equiv (testing "Object equiv method" From c59e957f6230c07e7a228070dd8eb393d5b8ce40 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Feb 2016 15:02:40 -0500 Subject: [PATCH 1721/4033] CLJS-1570: :parallel-build causes invalid truth check in cljs.reader/read-number make cljs.analyzer/*unchecked-if* a dynamic var with a default value of `false`. bind it around file scopes so that we can set! it thread locally, sufficient for parallel builds which works a file at a time. ClojureScript & bootstrapped can now share logic. --- src/main/clojure/cljs/analyzer.cljc | 11 +++++------ src/main/clojure/cljs/compiler.cljc | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index de5757d56..240911596 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -42,7 +42,7 @@ (def ^:dynamic *cljs-ns* 'cljs.user) (def ^:dynamic *cljs-file* nil) -#?(:clj (def ^:dynamic *unchecked-if* (atom false))) +#?(:clj (def ^:dynamic *unchecked-if* false)) (def ^:dynamic *cljs-static-fns* false) (def ^:dynamic *cljs-macros-path* "/cljs/core") (def ^:dynamic *cljs-macros-is-classpath* true) @@ -981,8 +981,7 @@ else-expr (allowing-redef (analyze env else))] {:env env :op :if :form form :test test-expr :then then-expr :else else-expr - :unchecked #?(:clj @*unchecked-if* - :cljs *unchecked-if*) + :unchecked *unchecked-if* :children [test-expr then-expr else-expr]})) (defmethod parse 'case* @@ -1589,8 +1588,7 @@ (= target '*unchecked-if*) ;; TODO: proper resolve (or (true? val) (false? val))) (do - #?(:clj (reset! *unchecked-if* val) - :cljs (set! *unchecked-if* val)) + (set! *unchecked-if* val) ::set-unchecked-if) (symbol? target) @@ -2846,7 +2844,8 @@ ([f opts] (analyze-file f false opts)) ([f skip-cache opts] - (binding [*file-defs* (atom #{})] + (binding [*file-defs* (atom #{}) + *unchecked-if* false] (let [output-dir (util/output-directory opts) res (cond (instance? File f) f diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index db583f8ee..50843ae45 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1333,7 +1333,8 @@ (compile-file src dest nil)) ([src dest opts] {:post [map?]} - (binding [ana/*file-defs* (atom #{})] + (binding [ana/*file-defs* (atom #{}) + ana/*unchecked-if* false] (let [nses (get @env/*compiler* ::ana/namespaces) src-file (io/file src) dest-file (io/file dest) From 16666f37cc13ead5a66330046db82a2976b6f1f0 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 20 Feb 2016 16:29:11 -0500 Subject: [PATCH 1722/4033] CLJS-1573: Self-host: Invalid UTF escaping in cljs-in-cljs Zero-pad \u escapes --- src/main/clojure/cljs/compiler.cljc | 6 +++-- src/test/self/self_host/test.cljs | 34 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 50843ae45..a2aa9cfb5 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -132,8 +132,10 @@ 9 "\\t" (if (< 31 cp 127) c ; Print simple ASCII characters - #?(:clj (format "\\u%04X" cp) - :cljs (str "\\u" (.toString cp 16))))))) ; Any other character is Unicode + #?(:clj (format "\\u%04X" cp) ; Any other character is Unicode + :cljs (let [unpadded (.toString cp 16) + pad (subs "0000" (.-length unpadded))] + (str "\\u" pad unpadded))))))) (defn- escape-string [^CharSequence s] (let [sb #?(:clj (StringBuilder. (count s)) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 2f1448fcf..c90bff8f8 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -240,6 +240,40 @@ (is (nil? value)) (is (= "if-let requires a vector for its binding at line 1 " (ex-message (ex-cause error))))))) +(deftest test-CLJS-1573 + (cljs/compile-str st + "\"90°\"" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "\"90\\u00b0\"" value)))) + (cljs/compile-str st + "\"Ϊ\"" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "\"\\u03aa\"" value)))) + (cljs/compile-str st + "\"ሴ\"" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "\"\\u1234\"" value)))) + (cljs/eval-str st + "\"90°\"" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "90°" value))))) + #_(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] From ca5dda18cad9dc1f04fc07a330e8932337d53f15 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 24 Dec 2015 09:18:18 -0500 Subject: [PATCH 1723/4033] CLJS-1521: Self-host: Macro namespaces cannot be aliased Use find-macro-ns when looking for macro expander so that aliased macro namespaces can properly resolve in bootstrap. Adds unit tests excercising the various ways you can require and make use of macros. --- src/main/clojure/cljs/analyzer.cljc | 3 +- src/test/self/self_host/test.cljs | 181 ++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 240911596..de7a2b653 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2322,7 +2322,8 @@ :cljs [(identical? "clojure.repl" nstr) (find-macros-ns 'cljs.repl)]) #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) - :else (some-> env :ns :require-macros (get (symbol nstr)) find-ns))) + :else (some-> env :ns :require-macros (get (symbol nstr)) #?(:clj find-ns + :cljs find-macros-ns)))) (defn get-expander* [sym env] (when-not (or (not (nil? (gets env :locals sym))) ; locals hide macros diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index c90bff8f8..ec4652759 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -189,6 +189,187 @@ (is (= "1\n2\n" value)) (inc! l)))))) +(deftest test-load-and-invoke-macros + (async done + (let [l (latch 9 done)] + ;; Normal require macros + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(ns cljs.user (:require-macros foo.core))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo.core/add 1 2)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 3 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Refer macro symbol + (cljs/eval-str st + "(ns cljs.user (:require-macros [foo.core :refer [add]]))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(add 1 3)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 4 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Alias the macro namespace + (cljs/eval-str st + "(ns cljs.user (:require-macros [foo.core :as foo]))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo/add 1 5)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 6 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Use instead of require + (cljs/eval-str st + "(ns cljs.user (:use-macros [foo.core :only [add]]))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(add 1 8)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 9 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Employ inline macro specification sugar (include) + (cljs/eval-str st + "(ns cljs.user (:require [foo.core :include-macros true]))" + nil + {:eval node-eval + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns foo.core)"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo.core/add 1 13)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 14 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Employ inline macro specification sugar (include with alias) + (cljs/eval-str st + "(ns cljs.user (:require [foo.core :as foo :include-macros true]))" + nil + {:eval node-eval + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns foo.core)"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo/add 1 21)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 22 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Employ inline macro specification sugar (refer) + (cljs/eval-str st + "(ns cljs.user (:require [foo.core :refer-macros [add]]))" + nil + {:eval node-eval + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns foo.core)"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(add 1 34)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 35 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Employ inline macro specification sugar (refer with alias) + (cljs/eval-str st + "(ns cljs.user (:require [foo.core :as foo :refer-macros [add]]))" + nil + {:eval node-eval + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns foo.core)"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(+ (add 2 3) (foo/add 5 8))" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 18 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Rely on implicit macro loading (ns loads its own macros) + (cljs/eval-str st + "(ns cljs.user (:require foo.core))" + nil + {:eval node-eval + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns foo.core (:require-macros foo.core))"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo.core/add 100 200)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 300 value)) + (inc! l))))))))) + (deftest test-eval-str-with-require-macros (async done (let [l (latch 2 done)] From 2023353d635578a0666a50c3305bf92347185646 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 23 Feb 2016 09:04:27 -0500 Subject: [PATCH 1724/4033] CLJS-1584: Self-host: core/str error with condp The condp macro uses core/str in a syntax-quote context, while every other place it appears that cljs.core/str is used in that context. --- src/main/clojure/cljs/core.cljc | 2 +- src/test/self/self_host/test.cljs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 9dc452e7e..ced0dacd6 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2056,7 +2056,7 @@ (split-at (if (= :>> (second args)) 3 2) args) n (count clause)] (core/cond - (= 0 n) `(throw (js/Error. (core/str "No matching clause: " ~expr))) + (= 0 n) `(throw (js/Error. (cljs.core/str "No matching clause: " ~expr))) (= 1 n) a (= 2 n) `(if (~pred ~a ~expr) ~b diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index ec4652759..50c55bb6a 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -455,6 +455,16 @@ (is (nil? error)) (is (= "90°" value))))) +(deftest test-CLJS-1584 + (cljs/eval-str st + "(condp = 1 1 2)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 2 value))))) + #_(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] From ea40068d34746fc27e61b6b612a66e65cd78b810 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 23 Feb 2016 09:36:46 -0500 Subject: [PATCH 1725/4033] CLJS-1564: Self-host: cached macro *loaded* update When requiring namespaces, the namespace name symbol is conjed into the *loaded* set, with the ana/macro-ns-name being conjed in the case of a macros namespace. (So for example, 'foo.core for a regular namespace and 'foo.core$macros for a macros namespace.) But, if the namespace being loaded is a macros namespace for which the client returns cached JavaScript, then the code instead conjes the base namespace name. This is actually the result of a simple oversight with the changes made for CLJS-1504: One place where *loaded* is manipulated, name is used instead of aname. --- src/main/cljs/cljs/js.cljs | 2 +- src/test/self/self_host/test.cljs | 54 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index de76fbe78..490ca62a0 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -267,7 +267,7 @@ (if (:error res) (cb res) (do - (swap! *loaded* conj name) + (swap! *loaded* conj aname) (cb {:value true})))))))))) (cb (wrap-error (ana/error env diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 50c55bb6a..d8e6119f8 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -50,6 +50,60 @@ ;; NOTE: can't set passes because callbacks happen _inside_ binding ;; do so will effect other tests +(deftest test-require-updates-*loading* + (async done + (let [l (latch 4 done)] + (cljs/require + {} + 'load1.core + :reload-all + {:load (fn [_ cb] (cb {:lang :clj + :source "(ns load1.core)"})) + :eval (constantly nil)} + (fn [{:keys [error value]}] + (is (nil? error)) + (is value) + (is (= #{'load1.core} @cljs/*loaded*)) + (inc! l))) + (cljs/require + {} + 'load2.core + :reload-all + {:macros-ns true + :load (fn [_ cb] (cb {:lang :clj + :source "(ns load2.core)"})) + :eval (constantly nil)} + (fn [{:keys [error value]}] + (is (nil? error)) + (is value) + (is (= #{'load2.core$macros} @cljs/*loaded*)) + (inc! l))) + (cljs/require + {} + 'load3.core + :reload-all + {:load (fn [_ cb] (cb {:lang :js + :source ""})) + :eval (constantly nil)} + (fn [{:keys [error value]}] + (is (nil? error)) + (is value) + (is (= #{'load3.core} @cljs/*loaded*)) + (inc! l))) + (cljs/require + {} + 'load4.core + :reload-all + {:macros-ns true + :load (fn [_ cb] (cb {:lang :js + :source ""})) + :eval (constantly nil)} + (fn [{:keys [error value]}] + (is (nil? error)) + (is value) + (is (= #{'load4.core$macros} @cljs/*loaded*)) + (inc! l)))))) + (deftest test-analyze-str (async done (let [l (latch 3 done)] From 22a2692c9fcbae7a002ddedc088e50c7c2cbccfe Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 23 Feb 2016 09:52:13 -0500 Subject: [PATCH 1726/4033] CLJS-1577: Self-host: syntax-quote resolves on dot forms If you try to evaluate a syntax-quoted dot form, as in `.x, by default symbol resolution will be applied to the dot form .x, yielding /x. Instead, we need this to yield just .x. To do this, revise cljs.js to check for this case before delegating to cljs.analyzer/resolve-symbol. --- src/main/cljs/cljs/js.cljs | 14 ++++++++++---- src/test/self/self_host/test.cljs | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 490ca62a0..58946f0da 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -38,6 +38,12 @@ 0 (- (count file) 5))] (symbol (demunge lib-name)))) +(defn- resolve-symbol + [sym] + (if (string/starts-with? (str sym) ".") + sym + (ana/resolve-symbol sym))) + (defn- atom? [x] (instance? Atom x)) @@ -444,7 +450,7 @@ *ns* (create-ns ns) ana/*passes* (:*passes* bound-vars) r/*data-readers* (:*data-readers* bound-vars) - r/resolve-symbol ana/resolve-symbol + r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} @@ -531,7 +537,7 @@ ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns (:*cljs-ns* bound-vars)) r/*data-readers* (:*data-readers* bound-vars) - r/resolve-symbol ana/resolve-symbol + r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [aenv (ana/empty-env) aenv (cond-> (assoc aenv :ns (ana/get-namespace ana/*cljs-ns*)) @@ -606,7 +612,7 @@ ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) - r/resolve-symbol ana/resolve-symbol + r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} @@ -704,7 +710,7 @@ ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns ns) r/*data-readers* (:*data-readers* bound-vars) - r/resolve-symbol ana/resolve-symbol + r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index d8e6119f8..054e989e2 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -509,6 +509,32 @@ (is (nil? error)) (is (= "90°" value))))) +(deftest test-CLJS-1577 + (cljs/analyze-str st + "`.x" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= '.x (:form value))))) + (cljs/compile-str st + "`.x" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (string/starts-with? value "new cljs.core.Symbol(null,\".x\",\".x\",")))) + (cljs/eval-str st + "`.x" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= '.x value))))) + (deftest test-CLJS-1584 (cljs/eval-str st "(condp = 1 1 2)" From 7f7b647be0bc32de56772a9f5eb9d86998197287 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 23 Feb 2016 16:04:18 -0500 Subject: [PATCH 1727/4033] CLJS-1585: Self-host: Alias-scoped keywords Since cljs.tools.reader makes use of *alias-map* to resolve aliases, bind it to the :requires map for the current namespace in the compiler state. This allows alias-scoped keywords (such as ::alias-name/foo to be properly expanded. --- src/main/cljs/cljs/js.cljs | 8 ++++++++ src/test/self/self_host/test.cljs | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 58946f0da..fecee1c2c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -157,6 +157,10 @@ "\n//# sourceMappingURL=data:application/json;base64," (base64/encodeString json))))) +(defn- current-alias-map + [] + (get-in @env/*compiler* [:cljs.analyzer/namespaces ana/*cljs-ns* :requires])) + ;; ----------------------------------------------------------------------------- ;; Analyze @@ -449,6 +453,7 @@ ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns ns) ana/*passes* (:*passes* bound-vars) + r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] @@ -536,6 +541,7 @@ ana/*cljs-ns* (:*cljs-ns* bound-vars) ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns (:*cljs-ns* bound-vars)) + r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] @@ -611,6 +617,7 @@ ana/*cljs-ns* ns ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns ns) + r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] @@ -709,6 +716,7 @@ ana/*cljs-ns* ns ana/*cljs-static-fns* (:static-fns opts) *ns* (create-ns ns) + r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 054e989e2..db0ebb3fb 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -545,6 +545,27 @@ (is (nil? error)) (is (= 2 value))))) +(deftest test-CLJS-1585 + (cljs/eval-str st + "(ns alias-load.core (:require [aliased.core :as alias]))" + nil + {:ns 'cljs.user + :context :expr + :eval cljs.js/js-eval + :load (fn [_ cb] + (cb {:lang :clj :source "(ns aliased.core)"}))} + (fn [{:keys [error value]}] + (is (nil? error)) + (cljs.js/eval-str st + "::alias/bar" + nil + {:ns 'alias-load.core + :context :expr + :eval cljs.js/js-eval} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= :aliased.core/bar value))))))) + #_(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] From e531c34e04adc815a6c25c8d2499465296ca290d Mon Sep 17 00:00:00 2001 From: rabidpraxis Date: Wed, 12 Aug 2015 13:15:09 -0700 Subject: [PATCH 1728/4033] CLJS-1420 - get-in behavior differs from Clojure by always deferring to the 3 arity fn Reverts back to original reduce for 2 arity get-in --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 935cf32cb..bfbb88669 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4668,7 +4668,7 @@ reduces them without incurring seq initialization" {:added "1.2" :static true} ([m ks] - (get-in m ks nil)) + (reduce get m ks)) ([m ks not-found] (loop [sentinel lookup-sentinel m m diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 76a5073cc..e4648f71f 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3111,6 +3111,15 @@ (deftest test-cljs-1569 (is (= (meta (with-meta (seq [1 2 3]) {:a 1})) {:a 1}))) +(deftest test-cljs-1420 + (is (= :2-arity + (get-in + (reify + ILookup + (-lookup [o k] :2-arity) + (-lookup [o k not-found] :3-arity)) + [:foo])))) + (comment ;; ObjMap From f58dcdf4dc37ef52d4eb1e2b7c994282bf6351f5 Mon Sep 17 00:00:00 2001 From: Nick Vrvilo Date: Mon, 22 Feb 2016 16:52:02 -0600 Subject: [PATCH 1729/4033] CLJS-1583: (hash (symbol "/")) does not match (hash '/) Modify symbol function to correctly handle "/" corner-case, and add tests comparing properties of '/ vs (symbol "/"). --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index bfbb88669..9ced40177 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -978,7 +978,7 @@ (if (symbol? name) name (let [idx (.indexOf name "/")] - (if (== idx -1) + (if (< idx 1) (symbol nil name) (symbol (.substring name 0 idx) (.substring name (inc idx) (. name -length))))))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index e4648f71f..79130c10f 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -302,6 +302,11 @@ (testing "Testing symbol ctor is idempotent" (is (= 'a (symbol 'a)))) + (testing "Testing constructed division symbol" + (is (= '/ (symbol "/"))) + (is (= (namespace '/) (namespace (symbol "/")))) + (is (= (hash '/) (hash (symbol "/"))))) + (testing "Testing keyword ctor" (is (= :a (keyword "a"))) (is (= :a (keyword 'a))) From 032077b52fd7c4359f18004f14faadab0d1a0782 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 3 Mar 2016 11:06:22 -0500 Subject: [PATCH 1730/4033] CLJS-1597: Redundant IPrintWithWriter test in pr-writer-impl Remove effectively dead code. --- src/main/cljs/cljs/core.cljs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9ced40177..e27c9d963 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9093,9 +9093,6 @@ reduces them without incurring seq initialization" (regexp? obj) (write-all writer "#\"" (.-source obj) "\"") - (implements? IPrintWithWriter obj) - (-pr-writer obj writer opts) - :else (if (.. obj -constructor -cljs$lang$ctorStr) (write-all writer From d56309ec0b9603a34fe096df4fd2ae8247687213 Mon Sep 17 00:00:00 2001 From: Peter Schuck Date: Wed, 2 Mar 2016 11:52:37 -0600 Subject: [PATCH 1731/4033] CLJS-1594: NaN and both infinities cannot be elements of a set Check if a number is Infinity, -Infinity, or NaN with isFinite then use matching hash values from Clojure for the values --- src/main/cljs/cljs/core.cljs | 9 ++++++++- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e27c9d963..792d815df 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -885,7 +885,14 @@ (-hash ^not-native o) (number? o) - (js-mod (Math/floor o) 2147483647) + (if (js/isFinite o) + (js-mod (Math/floor o) 2147483647) + (case o + Infinity + 2146435072 + -Infinity + -1048576 + 2146959360)) (true? o) 1 diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 79130c10f..d31e394cd 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3125,6 +3125,12 @@ (-lookup [o k not-found] :3-arity)) [:foo])))) +(deftest test-cljs-1594 + (is (not (js/isNaN (hash Infinity)))) + (is (not (js/isNaN (hash -Infinity)))) + (is (not (js/isNaN (hash NaN)))) + (is (= (hash-set Infinity -Infinity 0 1 2 3 4 5 6 7 8) + (set (keys (zipmap [Infinity -Infinity 0 1 2 3 4 5 6 7 8] (repeat nil))))))) (comment ;; ObjMap From aa0345197b2fa33ea1b3a58915400c9e7f37cdd2 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 4 Mar 2016 15:56:35 -0500 Subject: [PATCH 1732/4033] CLJS-1596: Self-host: :load-macros and :analyze-deps don't work in cljs.js Only default to true if not set. --- src/main/cljs/cljs/js.cljs | 20 +++---- src/test/self/self_host/test.cljs | 87 +++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index fecee1c2c..14f641bbe 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -222,8 +222,8 @@ (let [bound-vars (merge {:*compiler* (env/default-compiler-env) :*data-readers* tags/*cljs-data-readers* - :*load-macros* (or (:load-macros opts) true) - :*analyze-deps* (or (:analyze-deps opts) true) + :*load-macros* (:load-macros opts true) + :*analyze-deps* (:analyze-deps opts true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} bound-vars) @@ -523,8 +523,8 @@ {:*compiler* state :*data-readers* tags/*cljs-data-readers* :*passes* (or (:passes opts) ana/*passes*) - :*analyze-deps* (or (:analyze-deps opts) true) - :*load-macros* (or (:load-macros opts) true) + :*analyze-deps* (:analyze-deps opts true) + :*load-macros* (:load-macros opts true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} source name opts cb))) @@ -594,8 +594,8 @@ (eval* {:*compiler* state :*data-readers* tags/*cljs-data-readers* - :*analyze-deps* (or (:analyze-deps opts) true) - :*load-macros* (or (:load-macros opts) true) + :*analyze-deps* (:analyze-deps opts true) + :*load-macros* (:load-macros opts true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} form opts cb))) @@ -690,8 +690,8 @@ (compile-str* {:*compiler* state :*data-readers* tags/*cljs-data-readers* - :*analyze-deps* (or (:analyze-deps opts) true) - :*load-macros* (or (:load-macros opts) true) + :*analyze-deps* (:analyze-deps opts true) + :*load-macros* (:load-macros opts true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*) :*sm-data* (when (:source-map opts) (sm-data))} @@ -821,8 +821,8 @@ (eval-str* {:*compiler* state :*data-readers* tags/*cljs-data-readers* - :*analyze-deps* (or (:analyze-deps opts) true) - :*load-macros* (or (:load-macros opts) true) + :*analyze-deps* (:analyze-deps opts true) + :*load-macros* (:load-macros opts true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} source name opts cb))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index db0ebb3fb..b1a9676fe 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -243,6 +243,93 @@ (is (= "1\n2\n" value)) (inc! l)))))) +(deftest test-disable-analyze-deps + (cljs/analyze-str st + "(ns analyze-deps-as.core (:require [analyze-me.core :refer [abc]]))" + nil + {:context :expr + :eval cljs.js/js-eval + :analyze-deps false + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns analyze-me.core)"}))} + (fn [{:keys [error]}] + (is (nil? error)))) + + (cljs/eval st + '(ns analyze-deps-e.core (:require [analyze-me.core :refer [abc]])) + {:context :expr + :eval cljs.js/js-eval + :analyze-deps false + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns analyze-me.core)"}))} + (fn [{:keys [error]}] + (is (nil? error)))) + (cljs/compile-str st + "(ns analyze-deps-c.core (:require [analyze-me.core :refer [abc]]))" + nil + {:context :expr + :eval cljs.js/js-eval + :analyze-deps false + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns analyze-me.core)"}))} + (fn [{:keys [error]}] + (is (nil? error)))) + (cljs/eval-str st + "(ns analyze-deps-es.core (:require [analyze-me.core :refer [abc]]))" + nil + {:context :expr + :eval cljs.js/js-eval + :analyze-deps false + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns analyze-me.core)"}))} + (fn [{:keys [error]}] + (is (nil? error))))) + +(deftest test-disable-load-macros + (cljs/analyze-str st + "(ns load-macros-as.core (:require-macros [load-me.core]))" + nil + {:context :expr + :eval cljs.js/js-eval + :load-macros false + :load (fn [_ _] + (throw (ex-info "unexpected" {})))} + (fn [{:keys [error]}] + (is (nil? error)))) + (cljs/eval st + '(ns load-macros-e.core (:require-macros [load-me.core])) + {:context :expr + :eval cljs.js/js-eval + :load-macros false + :load (fn [_ _] + (throw (ex-info "unexpected" {})))} + (fn [{:keys [error]}] + (is (nil? error)))) + (cljs/compile-str st + "(ns load-macros-c.core (:require-macros [load-me.core]))" + nil + {:context :expr + :eval cljs.js/js-eval + :load-macros false + :load (fn [_ _] + (throw (ex-info "unexpected" {})))} + (fn [{:keys [error]}] + (is (nil? error)))) + (cljs/eval-str st + "(ns load-macros-es.core (:require-macros [load-me.core]))" + nil + {:context :expr + :eval cljs.js/js-eval + :load-macros false + :load (fn [_ _] + (throw (ex-info "unexpected" {})))} + (fn [{:keys [error]}] + (is (nil? error))))) + (deftest test-load-and-invoke-macros (async done (let [l (latch 9 done)] From c31e91f89dab42d519e9051e630ac8011cafa8d7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 27 Feb 2016 17:18:56 -0500 Subject: [PATCH 1733/4033] CLJS-1589: Self-host: case fail with nil In bootstrapped ClojureScript, case will fail with a nil test because char? will throw. This revision nil-patches the char? test conditionallyfor bootstrapped mode. --- src/main/clojure/cljs/core.cljc | 2 +- src/test/self/self_host/test.cljs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index ced0dacd6..7d40fe079 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2140,7 +2140,7 @@ esym (gensym) tests (keys pairs)] (core/cond - (every? (some-fn core/number? core/string? core/char? #(const? env %)) tests) + (every? (some-fn core/number? core/string? #?(:clj core/char? :cljs (core/fnil core/char? :nonchar)) #(const? env %)) tests) (core/let [no-default (if (odd? (count clauses)) (butlast clauses) clauses) tests (mapv #(if (seq? %) (vec %) [%]) (take-nth 2 no-default)) thens (vec (take-nth 2 (drop 1 no-default)))] diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index b1a9676fe..ee8c731eb 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -653,6 +653,16 @@ (is (nil? error)) (is (= :aliased.core/bar value))))))) +(deftest test-CLJS-1589 + (cljs/eval-str st + "(case 1 nil nil :x)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= :x value))))) + #_(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] From c3899acf797eb6779c53b313f5606c5018360b83 Mon Sep 17 00:00:00 2001 From: spinningtopsofdoom Date: Thu, 3 Mar 2016 17:13:41 -0600 Subject: [PATCH 1734/4033] LJS-1590: split, split-lines differs from Clojure on empty string Only discard training if split generates more than one element --- src/main/cljs/clojure/string.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 37d1300e7..4cf5ede11 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -119,7 +119,7 @@ (defn- discard-trailing-if-needed [limit v] - (if (== 0 limit) + (if (and (== 0 limit) (< 1 (count v))) (pop-last-while-empty v) v)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d31e394cd..5ac667b77 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3132,6 +3132,12 @@ (is (= (hash-set Infinity -Infinity 0 1 2 3 4 5 6 7 8) (set (keys (zipmap [Infinity -Infinity 0 1 2 3 4 5 6 7 8] (repeat nil))))))) +(deftest test-cljs-1590 + (is (= [""] (s/split "" #"\n"))) + (is (= [] (s/split "\n\n\n" #"\n"))) + (is (= [""] (s/split-lines ""))) + (is (= [] (s/split-lines "\n\n\n")))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From f057e3cdd7490faeba824920d8b48b80aac69a87 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 20 Feb 2016 12:18:37 -0500 Subject: [PATCH 1735/4033] CLJS-1582: Type-hint extend-type first arg for primitives If extending boolean or number to a protocol, propagate type hint to first arg of fns. This is done by walking the code in the impl-map, and associng the passed type-sym as the :tag meta for the first argument of all fns. --- src/main/clojure/cljs/core.cljc | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 7d40fe079..494ee849f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1470,6 +1470,34 @@ (recur (conj seen fname) (next methods))))) (recur (conj protos proto) impls))))) +(core/defn- type-hint-first-arg + [type-sym argv] + (assoc argv 0 (vary-meta (argv 0) assoc :tag type-sym))) + +(core/defn- type-hint-single-arity-sig + [type-sym sig] + (list* (first sig) (type-hint-first-arg type-sym (second sig)) (nnext sig))) + +(core/defn- type-hint-multi-arity-sig + [type-sym sig] + (list* (type-hint-first-arg type-sym (first sig)) (next sig))) + +(core/defn- type-hint-multi-arity-sigs + [type-sym sigs] + (list* (first sigs) (map (partial type-hint-multi-arity-sig type-sym) (rest sigs)))) + +(core/defn- type-hint-sigs + [type-sym sig] + (if (vector? (second sig)) + (type-hint-single-arity-sig type-sym sig) + (type-hint-multi-arity-sigs type-sym sig))) + +(core/defn- type-hint-impl-map + [type-sym impl-map] + (reduce-kv (core/fn [m proto sigs] + (assoc m proto (map (partial type-hint-sigs type-sym) sigs))) + {} impl-map)) + (core/defmacro extend-type "Extend a type to a series of protocols. Useful when you are supplying the definitions explicitly inline. Propagates the @@ -1500,6 +1528,9 @@ _ (validate-impls env impls) resolve (partial resolve-var env) impl-map (->impl-map impls) + impl-map (if ('#{boolean number} type-sym) + (type-hint-impl-map type-sym impl-map) + impl-map) [type assign-impls] (core/if-let [type (base-type type-sym)] [type base-assign-impls] [(resolve type-sym) proto-assign-impls])] From 43565685860e53c007938228ee0ebf43a14b6396 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Mar 2016 14:00:24 -0400 Subject: [PATCH 1736/4033] Updates for 1.8.34 --- README.md | 6 +++--- changes.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9e57f6c4d..8a7dea643 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.7.228 +Latest stable release: 1.8.34 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.7.228"] +[org.clojure/clojurescript "1.8.34"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.7.228 org.clojure clojurescript - 1.7.228 + 1.8.34 ``` diff --git a/changes.md b/changes.md index f560b14d1..52beb4eee 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,50 @@ +## 1.8.34 + +### Changes +* CLJS-1582: Type-hint extend-type first arg for primitives +* CLJS-1590: split, split-lines differs from Clojure on empty string +* CLJS-1594: NaN and both infinities cannot be elements of a set +* CLJS-1597: Redundant IPrintWithWriter test in pr-writer-impl +* CLJS-1583: (hash (symbol "/")) does not match (hash '/) +* bump tools reader +* CLJS-1492: Warn when using :optimisations instead of :optimizations +* less cryptic error if :main doesn't correspond to any file +* CLJS-744: ISequential types should implement JS indexOf, lastIndexOf +* CLJS-1411: make-array signature differs from clojure + +### Fixes +* CLJS-1589: Self-host: case fail with nil +* CLJS-1596: Self-host: :load-macros and :analyze-deps don't work in cljs.js +* CLJS-1420 - get-in behavior differs from Clojure by always deferring to the 3 arity fn +* CLJS-1585: Self-host: Alias-scoped keywords +* CLJS-1577: Self-host: syntax-quote resolves on dot forms +* CLJS-1564: Self-host: cached macro *loaded* update +* CLJS-1584: Self-host: core/str error with condp +* CLJS-1521: Self-host: Macro namespaces cannot be aliased +* CLJS-1573: Self-host: Invalid UTF escaping in cljs-in-cljs +* CLJS-1570: :parallel-build causes invalid truth check in cljs.reader/read-number +* CLJS-1568: LazyTransformer doesn't implement IMeta +* CLJS-1578: Corrupted Analysis Files Break Compilation +* CLJS-1579: cljs.source-map/invert-reverse-map discards gcol +* CLJS-1580: Self-host: goog.provide offsets source-maps +* CLJS-1569: IndexedSeq doesn't implement IWithMeta / IMeta +* CLJS-1567: make-array macro missing > 2 arg arity +* CLJS-1571: Make special-symbol? true for 'var +* CLJS-1555: make-array macro missing 2 arg arity +* CLJS-970: generate assert message when compiling +* CLJS-1565: Self-host: whitespace optimization is broken +* CLJS-1541: Self-host: Cannot require 'cljs.js using cljs.jar +* CLJS-1550: Enhance docstring for extend-type wrt type-sym +* CLJS-1551: Self-host: assert-args dormant in macros +* CLJS-1552: doc for & should match fn +* CLJS-1488: cljs.repl/source Cannot read source of cljs functions that use #js reader +* CLJS-1557: Make special-symbol? return true for catch and finally +* CLJS-1542: Self-host: cljs/compile-str not handling errors properly +* CLJS-1318: Fix typo in documentation of `specify` +* CLJS-620: Warnings are generated when using a macro in argument position +* CLJS-1547: Wrong output encoding when compile with goog.LOCALE +* CLJS-1546: cljs.core/run! does not always return nil + ## 1.7.228 ### Enhancements From 757b7591cf246ce90fb04e5d84ebcbc9f78d9923 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Mar 2016 14:32:06 -0400 Subject: [PATCH 1737/4033] add missing known compiler options --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a069adeea..b42ec3cb7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -168,7 +168,8 @@ :hashbang :language-in :language-out :libs :main :modules :source-map-path :optimizations :optimize-constants :output-dir :output-to :output-wrapper :parallel-build :preamble :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map - :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings}) + :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings + :emit-constants :ups-externs :ups-foreign-libs :ups-libs}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 From ba18d9f72e946114199471f6a3e50498ad9792ee Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 25 Feb 2016 12:15:20 -0500 Subject: [PATCH 1738/4033] CLJS-1592: Self-host: Robustness for core tests A couple minor things that can be improved in the core tests which facilitates running them in bootstrapped environments: 1. Restore *print-newline* 2. Add a little more order robustness for hash-based collections --- src/test/cljs/cljs/core_test.cljs | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5ac667b77..a6211735a 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1582,8 +1582,9 @@ (is (.-done (.next iter))))) (let [eiter (.entries {:foo "bar" :baz "woz"})] (testing "map entry iteration" - (is (= (seq (.-value (.next eiter))) (seq #js [:foo "bar"]))) - (is (= (seq (.-value (.next eiter))) (seq #js [:baz "woz"]))) + (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" @@ -1643,10 +1644,10 @@ "{:foo \"bar\"}")) (is (= (binding [*print-length* 0] (str {:foo "bar" :baz "woz"})) "{...}")) - (is (= (binding [*print-length* 1] (str {:foo "bar" :baz "woz"})) - "{:foo \"bar\", ...}")) - (is (= (binding [*print-length* 10] (str {:foo "bar" :baz "woz"})) - "{:foo \"bar\", :baz \"woz\"}"))) + (is (#{"{:foo \"bar\", ...}" "{:baz \"woz\", ...}"} + (binding [*print-length* 1] (str {:foo "bar" :baz "woz"})))) + (is (#{"{:foo \"bar\", :baz \"woz\"}" "{:baz \"woz\", :foo \"bar\"}"} + (binding [*print-length* 10] (str {:foo "bar" :baz "woz"}))))) ) (deftest test-print-with-opts @@ -1656,12 +1657,16 @@ "[]")) (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "\u2026" :print-length 1}) "[1 \u2026]")) - (is (= (pr-str-with-opts [#{1 2 3}] {:more-marker "\u2026" :print-length 2}) - "#{1 3 \u2026}")) + (is (#{"#{1 2 \u2026}" "#{1 3 \u2026}" + "#{2 1 \u2026}" "#{2 3 \u2026}" + "#{3 1 \u2026}" "#{3 2 \u2026}"} + (pr-str-with-opts [#{1 2 3}] {:more-marker "\u2026" :print-length 2}))) (is (= (pr-str-with-opts ['(1 2 3)] {:more-marker "\u2026" :print-length 2}) "(1 2 \u2026)")) - (is (= (pr-str-with-opts [{:1 1 :2 2 :3 3}] {:more-marker "\u2026" :print-length 2}) - "{:1 1, :2 2, \u2026}"))) + (is (#{"{:1 1, :2 2, \u2026}" "{:1 1, :3 3, \u2026}" + "{:2 2, :1 1, \u2026}" "{:2 2, :3 3, \u2026}" + "{:3 3, :1 1, \u2026}" "{:3 3, :2 2, \u2026}"} + (pr-str-with-opts [{:1 1 :2 2 :3 3}] {:more-marker "\u2026" :print-length 2})))) (testing "Testing printing with opts - :alt-impl" ; CLJS-1010 @@ -2429,10 +2434,9 @@ (deftest test-739 (testing "Testing CLJS-739, with-out-str" - (set! *print-newline* true) - (is (= (with-out-str (doseq [fn (cljs-739 [] [:a :b :c :d])] (fn))) - ":a\n:b\n:c\n:d\n")) - (set! *print-newline* false))) + (binding [*print-newline* true] + (is (= (with-out-str (doseq [fn (cljs-739 [] [:a :b :c :d])] (fn))) + ":a\n:b\n:c\n:d\n"))))) (deftest test-728 (testing "Testing CLJS-728, lookup with default" From 4681e47418ead29156ca01d58eee700e11c1e096 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Mar 2016 15:30:44 -0400 Subject: [PATCH 1739/4033] :warning-handlers missing from known compiler options --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b42ec3cb7..9855d62af 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -169,7 +169,7 @@ :optimize-constants :output-dir :output-to :output-wrapper :parallel-build :preamble :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings - :emit-constants :ups-externs :ups-foreign-libs :ups-libs}) + :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 From 177b05fc5935c659e4829b7b1798e216b6797bec Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 18 Mar 2016 21:37:12 -0700 Subject: [PATCH 1740/4033] CLJS-1603: Only warn for misspelled comp/REPL opts REPLs may have REPL-specific options which are added to the compiler/ REPL option map and this triggers "unknown compiler option" warnings. Instead, only issue warnings when there are known suggestions within the Levenshtein distance threshold. This effectively limits the feature to its original use case of detecting minor misspellings. --- src/main/clojure/cljs/build/api.clj | 4 ++-- src/main/clojure/cljs/repl.cljc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index d8cbd9966..efb0de14b 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -204,8 +204,8 @@ (env/default-compiler-env opts)))) ([source opts compiler-env] (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)] - (println (str "WARNING: Unknown compiler option '" unknown-opt "'." - (when suggested-opt (str " Did you mean '" suggested-opt "'?"))))) + (when suggested-opt + (println (str "WARNING: Unknown compiler option '" unknown-opt "'. Did you mean '" suggested-opt "'?")))) (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/build source opts compiler-env)))) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index ee8dca22f..ecffd9abf 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -780,8 +780,8 @@ bind-err true} :as opts}] (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) (set/union known-repl-opts cljsc/known-opts))] - (println (str "WARNING: Unknown option '" unknown-opt "'." - (when suggested-opt (str " Did you mean '" suggested-opt "'?"))))) + (when suggested-opt + (println (str "WARNING: Unknown option '" unknown-opt "'. Did you mean '" suggested-opt "'?")))) (let [repl-opts (-repl-options repl-env) repl-requires (into repl-requires (:repl-requires repl-opts)) {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts From f0ac4c92006ac618516c11e9ca3527904d35d4af Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Mon, 21 Mar 2016 22:42:19 +0200 Subject: [PATCH 1741/4033] CLJS-1605: Add all repl* options to known-repl-options --- src/main/clojure/cljs/repl.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index ecffd9abf..02abaa03e 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -39,8 +39,10 @@ (def known-repl-opts "Set of all known REPL options." - #{:analyze-path :caught :def-emits-var :flush :need-prompt :print :print-no-newline :prompt :read - :reader :repl-verbose :watch :watch-fn}) + #{:analyze-path :bind-err :caught :compiler-env :def-emits-var :eval :flush + :init :need-prompt :print :print-no-newline :prompt :quit-prompt :read + :reader :repl-requires :repl-verbose :source-map-inline :watch :watch-fn + :wrap}) (defmacro err-out [& body] `(binding [*out* *err*] From 4d44548ae6155208e4ed8b1ef280a6645c830f68 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 28 Mar 2016 08:05:57 -0400 Subject: [PATCH 1742/4033] 1.8.40 --- README.md | 6 +++--- changes.md | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8a7dea643..4d8b2ef7b 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.8.34 +Latest stable release: 1.8.40 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.8.34"] +[org.clojure/clojurescript "1.8.40"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.8.34 org.clojure clojurescript - 1.8.34 + 1.8.40 ``` diff --git a/changes.md b/changes.md index 52beb4eee..28f83bbe3 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,10 @@ +## 1.8.40 + +### Fixes +* CLJS-1603: Only warn for misspelled comp/REPL opts +* :warning-handlers missing for known compiler options +* CLJS-1592: Self-host: Robustness for core tests + ## 1.8.34 ### Changes From 4e64079741e5d586c16c5ef2746aad3886d6d7d6 Mon Sep 17 00:00:00 2001 From: Nicolas Berger Date: Sun, 3 Apr 2016 20:04:01 -0300 Subject: [PATCH 1743/4033] Avoid corrupted compiler env in ana/parse-ns parse-ns allows for running the parse in two ways: updating the global compiler env or not updating it. This is controlled via the :restore option. To implement this, it isolates the parsing process by binding the compiler env to a snapshot of the compiler env when it starts. When the parsing is done, it checks the :restore option and if it's false it merges back parts of the resulting compiler env (the ::namespaces and ::constants keys) into the global compiler env. The problem is that during the parsing, other compiler processes could have legitimately updated those same keys in the global compiler env, so this merge can occasionally result in compiler data loss. The race condition can avoided by using a different approach: only when :restore is true the compiler runs isolated from the global compiler env, by taking a snapshot of the env at the beginning. When :restore is false, no snapshot is taken: env/*compiler* binding is not modified, so the global compiler env is mutated in-place --- src/main/clojure/cljs/analyzer.cljc | 48 +++++++++++++---------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index de7a2b653..640701bad 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2707,9 +2707,10 @@ (let [src (if (symbol? src) (util/ns->source src) src) - compiler-env @env/*compiler* - [ijs compiler-env'] - (binding [env/*compiler* (atom compiler-env) + ijs + (binding [env/*compiler* (if (false? (:restore opts)) + env/*compiler* + (atom @env/*compiler*)) *cljs-ns* 'cljs.user *cljs-file* src *macro-infer* @@ -2739,35 +2740,28 @@ 'cljs.core$macros ns-name) deps (merge (:uses ast) (:requires ast))] - [(merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= 'cljs.core ns-name) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in compiler-env [:options :emit-constants]) - (conj 'constants-table))) - :file dest - :source-file (when rdr src) - :source-forms (when-not rdr src) - :ast ast - :macros-ns (or (:macros-ns opts) - (= 'cljs.core$macros ns-name))} - (when (and dest (.exists ^File dest)) - {:lines (with-open [reader (io/reader dest)] - (-> reader line-seq count))})) - @env/*compiler*]) + (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= 'cljs.core ns-name) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj 'constants-table))) + :file dest + :source-file (when rdr src) + :source-forms (when-not rdr src) + :ast ast + :macros-ns (or (:macros-ns opts) + (= 'cljs.core$macros ns-name))} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))}))) (recur (rest forms)))) (throw (AssertionError. (str "No ns form found in " src))))) (finally (when rdr (.close ^Reader rdr))))))] - (when (false? (:restore opts)) - (swap! env/*compiler* - (fn [old-state] - (-> old-state - (update-in [::namespaces] merge (get compiler-env' ::namespaces)) - (update-in [::constant-table] merge (get compiler-env' ::constant-table)))))) ijs))))) #?(:clj From 9a2be8bc665385be1ef866e2fd76b476c417d2bf Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 Apr 2016 15:07:20 -0400 Subject: [PATCH 1744/4033] bump tools.reader to 1.0.0-beta1 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 885807536..245976eb2 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.0.0-alpha3 + 1.0.0-beta1 diff --git a/project.clj b/project.clj index 61895ea9d..04126c07c 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.0.0-alpha3"] + [org.clojure/tools.reader "1.0.0-beta1"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] [com.google.javascript/closure-compiler "v20151216"] [org.mozilla/rhino "1.7R5"]] diff --git a/script/bootstrap b/script/bootstrap index 8d97702db..6423cb83c 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -7,7 +7,7 @@ CLOSURE_RELEASE="20151216" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.0.0-alpha3" +TREADER_RELEASE="1.0.0-beta1" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From 29eb8e014f60d57c974fef46f8609ca0be0fd247 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 8 Apr 2016 15:40:26 -0400 Subject: [PATCH 1745/4033] CLJS-1617: Evaluation order of arguments to 'list' is right-to-left don't optimize list construction unless we have constants --- src/main/clojure/cljs/core.cljc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 494ee849f..fe677143f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2377,9 +2377,13 @@ assoc :tag 'array))) (core/defmacro list - ([] '(.-EMPTY cljs.core/List)) + ([] + '(.-EMPTY cljs.core/List)) ([x & xs] - `(-conj (list ~@xs) ~x))) + (if (= :constant (:op (cljs.analyzer/analyze &env x))) + `(-conj (list ~@xs) ~x) + `(let [x# ~x] + (-conj (list ~@xs) x#))))) (core/defmacro vector ([] '(.-EMPTY cljs.core/PersistentVector)) From c1f45ff6e580d5a87d3c2fcae648478099275611 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 15 Apr 2016 08:43:35 -0400 Subject: [PATCH 1746/4033] CLJS-1624: Avoid the use of JSC_HOME for tests Don't make use of JSC_HOME because it triggers a warning in command-line apps that make use of JavaScriptCore owing to http://trac.webkit.org/changeset/194606 Instead, we can simply check to see if jsc is on the path. --- script/benchmark | 6 +++--- script/test | 6 +++--- script/test-simple | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/script/benchmark b/script/benchmark index 29f0a61de..8f68e7511 100755 --- a/script/benchmark +++ b/script/benchmark @@ -19,11 +19,11 @@ else "${SPIDERMONKEY_HOME}/js" -f builds/out-adv-bench/core-advanced-benchmark.js fi -if [ "$JSC_HOME" = "" ]; then - echo "JSC_HOME not set, skipping JavaScriptCore benchmarks" +if ! hash jsc 2>/dev/null; then + echo "jsc not on path, skipping JavaScriptCore benchmarks" else echo "Benchmarking with JavaScriptCore" - "${JSC_HOME}/jsc" -f builds/out-adv-bench/core-advanced-benchmark.js + jsc -f builds/out-adv-bench/core-advanced-benchmark.js fi if [ "$NASHORN_HOME" = "" ]; then diff --git a/script/test b/script/test index 91eb08754..aaf8cb17f 100755 --- a/script/test +++ b/script/test @@ -26,11 +26,11 @@ else ran=$((ran+1)) fi -if [ "$JSC_HOME" = "" ]; then - echo "JSC_HOME not set, skipping JavaScriptCore tests" +if ! hash jsc 2>/dev/null; then + echo "jsc not on path, skipping JavaScriptCore tests" else echo "Testing with JavaScriptCore" - "${JSC_HOME}/jsc" -f builds/out-adv/core-advanced-test.js + jsc -f builds/out-adv/core-advanced-test.js ran=$((ran+1)) fi diff --git a/script/test-simple b/script/test-simple index 3c2b37a02..46e3bc0b1 100755 --- a/script/test-simple +++ b/script/test-simple @@ -27,11 +27,11 @@ else fi # commented out because of memory issue in JSC w/ nested fn calls - David -#if [ "$JSC_HOME" = "" ]; then -# echo "JSC_HOME not set, skipping JavaScriptCore tests" +#if ! hash jsc 2>/dev/null; then +# echo "jsc not on path, skipping JavaScriptCore tests" #else # echo "Testing with JavaScriptCore" -# "${JSC_HOME}/jsc" -f builds/out-simp/core-simple-test.js +# jsc -f builds/out-simp/core-simple-test.js # ran=$[ran+1] #fi From 56611b5f653e5b8cbeaff9d975cf9f73b755f9c3 Mon Sep 17 00:00:00 2001 From: Roman Liutikov Date: Sun, 10 Apr 2016 20:38:47 +0300 Subject: [PATCH 1747/4033] CLJS-1621: Foreign libs modules of different type doesn't compile together These changes allows to include JavaScript modules with dependencies of different module type (AMD, CommonJS and ES6) into Closure Compiler compilation step. --- src/main/clojure/cljs/closure.clj | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9855d62af..91d5116e2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1496,9 +1496,12 @@ (CompilerInput.))] (.getAstRoot input closure-compiler))) -(defn get-source-files [module-type opts] +(defn get-source-files [opts] (->> (:foreign-libs opts) - (filter #(= (:module-type %) module-type)) + (filter #(let [module-type (:module-type %)] + (or (= module-type :amd) + (= module-type :commonjs) + (= module-type :es6)))) (map (fn [lib] (let [lib (deps/load-foreign-library lib)] (js-source-file (:file lib) (deps/-source lib))))))) @@ -1524,7 +1527,7 @@ (defmethod convert-js-module :commonjs [ijs opts] (let [{:keys [file module-type]} ijs ^List externs '() - ^List source-files (get-source-files module-type opts) + ^List source-files (get-source-files opts) ^CompilerOptions options (make-convert-js-module-options opts) closure-compiler (doto (make-closure-compiler) (.init externs source-files options)) @@ -1542,9 +1545,9 @@ (util/compile-if can-convert-es6? (defmethod convert-js-module :es6 [ijs opts] - (let [{:keys [file module-type]} ijs + (let [{:keys [file]} ijs ^List externs '() - ^List source-files (get-source-files module-type opts) + ^List source-files (get-source-files opts) ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) (.setLanguageOut CompilerOptions$LanguageMode/ECMASCRIPT5)) From 28e040d41f3a83901b6391898f51c4bfc2622452 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 20 Apr 2016 10:02:38 -0400 Subject: [PATCH 1748/4033] CLJS-1626: cljs.test for bootstrap --- script/test-self-parity | 19 ++ src/main/cljs/cljs/{test.clj => test.cljc} | 15 +- .../cljs/analyzer/{api.clj => api.cljc} | 143 ++++----- src/test/self/self_parity/aux.cljs | 150 +++++++++ src/test/self/self_parity/test.cljs | 284 ++++++++++++++++++ 5 files changed, 534 insertions(+), 77 deletions(-) create mode 100755 script/test-self-parity rename src/main/cljs/cljs/{test.clj => test.cljc} (96%) rename src/main/clojure/cljs/analyzer/{api.clj => api.cljc} (58%) create mode 100644 src/test/self/self_parity/aux.cljs create mode 100644 src/test/self/self_parity/test.cljs diff --git a/script/test-self-parity b/script/test-self-parity new file mode 100755 index 000000000..624f770b1 --- /dev/null +++ b/script/test-self-parity @@ -0,0 +1,19 @@ +#!/bin/sh + +# stop blowing compiled stuff +rm -rf builds/out-self-parity +mkdir -p builds/out-self-parity + +# extract needed files from clojure.jar +if [ ! -f lib/clojure.jar ]; then + echo "Run script/bootstrap first" + exit 1 +fi +jar xvf lib/clojure.jar clojure/template.clj > /dev/null +mkdir -p builds/out-self-parity/clojure +mv clojure/template.clj builds/out-self-parity/clojure + +bin/cljsc src/test/self/self_parity "{:optimizations :none :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}" + +echo "Testing with Node" +node builds/out-self-parity/main.js diff --git a/src/main/cljs/cljs/test.clj b/src/main/cljs/cljs/test.cljc similarity index 96% rename from src/main/cljs/cljs/test.clj rename to src/main/cljs/cljs/test.cljc index ab0b7aaa2..377a83eb9 100644 --- a/src/main/cljs/cljs/test.clj +++ b/src/main/cljs/cljs/test.cljc @@ -7,10 +7,11 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.test + #?(:cljs (:require-macros [clojure.template :as temp])) (:require [cljs.env :as env] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] - [clojure.template :as temp])) + #?(:clj [clojure.template :as temp]))) ;; ============================================================================= ;; Utilities for assertions @@ -137,7 +138,7 @@ You don't call this." [msg form] `(try - ~(cljs.test/assert-expr &env msg form) + ~(assert-expr &env msg form) (catch :default t# (cljs.test/do-report {:type :error, :message ~msg, @@ -162,7 +163,7 @@ re-find) the regular expression re." ([form] `(cljs.test/is ~form nil)) ([form msg] - `(cljs.test/try-expr ~msg ~form))) + `(try-expr ~msg ~form))) (defmacro are "Checks multiple assertions with a template expression. @@ -186,7 +187,7 @@ (pos? (count args)) (zero? (mod (count args) (count argv))))) `(clojure.template/do-template ~argv (is ~expr) ~@args) - (throw (IllegalArgumentException. "The number of args doesn't match are's argv.")))) + (throw (#?(:clj Exception. :cljs js/Error.) "The number of args doesn't match are's argv.")))) (defmacro testing "Adds a new string to the list of testing contexts. May be nested, @@ -276,8 +277,8 @@ namespaces)) [(fn [] (cljs.test/set-env! ~env) - (do-report (deref ~summary)) - (report (assoc (deref ~summary) :type :end-run-tests)) + (cljs.test/do-report (deref ~summary)) + (cljs.test/report (assoc (deref ~summary) :type :end-run-tests)) (cljs.test/clear-env!))])))) (defmacro run-tests @@ -384,4 +385,4 @@ [~@fns]) :else (throw - (Exception. "First argument to cljs.test/use-fixtures must be :once or :each")))) + (#?(:clj Exception. :cljs js/Error.) "First argument to cljs.test/use-fixtures must be :once or :each")))) diff --git a/src/main/clojure/cljs/analyzer/api.clj b/src/main/clojure/cljs/analyzer/api.cljc similarity index 58% rename from src/main/clojure/cljs/analyzer/api.clj rename to src/main/clojure/cljs/analyzer/api.cljc index 8cf0ff296..5108a5198 100644 --- a/src/main/clojure/cljs/analyzer/api.clj +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -65,75 +65,78 @@ ([state] (get @state :js-dependency-index))) -(defn analyze - "Given an environment, a map containing {:locals (mapping of names to bindings), :context - (one of :statement, :expr, :return), :ns (a symbol naming the - compilation ns)}, and form, returns an expression object (a map - containing at least :form, :op and :env keys). If expr has any (immediately) - nested exprs, must have :children [exprs...] entry. This will - facilitate code walking without knowing the details of the op set." - ([env form] (analyze env form nil)) - ([env form name] (analyze env form name nil)) - ([env form name opts] - (analyze - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - env form name opts)) - ([state env form name opts] - (env/with-compiler-env state - (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] - (ana/analyze env form name opts))))) - -(defn forms-seq - "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally - accepts a filename argument which will be used in any emitted errors." - ([rdr] (ana/forms-seq* rdr nil)) - ([rdr filename] (ana/forms-seq* rdr filename))) - -(defn parse-ns - "Helper for parsing only the essential namespace information from a - ClojureScript source file and returning a cljs.closure/IJavaScript compatible - map _not_ a namespace AST node. - - By default does not load macros or perform any analysis of dependencies. If - opts parameter provided :analyze-deps and :load-macros keys their values will - be used for *analyze-deps* and *load-macros* bindings respectively. This - function does _not_ side-effect the ambient compilation environment unless - requested via opts where :restore is false." - ([src] (parse-ns src nil nil)) - ([src opts] (parse-ns src nil opts)) - ([src dest opts] - (parse-ns - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - src dest opts)) - ([state src dest opts] - (env/with-compiler-env state - (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] - (ana/parse-ns src dest opts))))) - -(defn analyze-file - "Given a java.io.File, java.net.URL or a string identifying a resource on the - classpath attempt to analyze it. - - This function side-effects the ambient compilation environment - `cljs.env/*compiler*` to aggregate analysis information. opts argument is - compiler options, if :cache-analysis true will cache analysis to - \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a - meaningful value." - ([f] (analyze-file f nil)) - ([f opts] - (analyze-file - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - f opts)) - ([state f opts] - (env/with-compiler-env state - (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] - (ana/analyze-file f opts))))) +#?(:clj + (defn analyze + "Given an environment, a map containing {:locals (mapping of names to bindings), :context + (one of :statement, :expr, :return), :ns (a symbol naming the + compilation ns)}, and form, returns an expression object (a map + containing at least :form, :op and :env keys). If expr has any (immediately) + nested exprs, must have :children [exprs...] entry. This will + facilitate code walking without knowing the details of the op set." + ([env form] (analyze env form nil)) + ([env form name] (analyze env form name nil)) + ([env form name opts] + (analyze + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + env form name opts)) + ([state env form name opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/analyze env form name opts)))))) + +#?(:clj + (defn forms-seq + "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally + accepts a filename argument which will be used in any emitted errors." + ([rdr] (ana/forms-seq* rdr nil)) + ([rdr filename] (ana/forms-seq* rdr filename)))) + +#?(:clj + (defn parse-ns + "Helper for parsing only the essential namespace information from a + ClojureScript source file and returning a cljs.closure/IJavaScript compatible + map _not_ a namespace AST node. + + By default does not load macros or perform any analysis of dependencies. If + opts parameter provided :analyze-deps and :load-macros keys their values will + be used for *analyze-deps* and *load-macros* bindings respectively. This + function does _not_ side-effect the ambient compilation environment unless + requested via opts where :restore is false." + ([src] (parse-ns src nil nil)) + ([src opts] (parse-ns src nil opts)) + ([src dest opts] + (parse-ns + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + src dest opts)) + ([state src dest opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/parse-ns src dest opts)))))) +#?(:clj + (defn analyze-file + "Given a java.io.File, java.net.URL or a string identifying a resource on the + classpath attempt to analyze it. + + This function side-effects the ambient compilation environment + `cljs.env/*compiler*` to aggregate analysis information. opts argument is + compiler options, if :cache-analysis true will cache analysis to + \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a + meaningful value." + ([f] (analyze-file f nil)) + ([f opts] + (analyze-file + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)) + f opts)) + ([state f opts] + (env/with-compiler-env state + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (ana/analyze-file f opts)))))) ;; ============================================================================= ;; Main API @@ -146,7 +149,7 @@ (try (ana/resolve-var env sym (ana/confirm-var-exists-throw)) - (catch Exception e + (catch #?(:clj Exception :cljs :default) e (ana/resolve-macro-var env sym)))) (defn all-ns diff --git a/src/test/self/self_parity/aux.cljs b/src/test/self/self_parity/aux.cljs new file mode 100644 index 000000000..b55672b97 --- /dev/null +++ b/src/test/self/self_parity/aux.cljs @@ -0,0 +1,150 @@ +(ns ^{:doc "This auxiliary namespace is not actually loaded. + Its mere presence cause it to be compiled and thus causes + the libs listed here to be dumped into the compiler output + directory where they can be loaded on demand when running + the compiler tests in bootstrap mode."} + self-parity.aux + (:require + goog.Delay + goog.Disposable + goog.Promise + goog.Throttle + goog.Timer + goog.Uri + goog.array.ArrayLike + goog.color + goog.color.Hsl + goog.color.Hsv + goog.color.Rgb + goog.color.alpha + goog.color.names + goog.crypt + goog.crypt.Aes + goog.crypt.Arc4 + goog.crypt.BlobHasher + goog.crypt.Cbc + goog.crypt.Hash + goog.crypt.Hmac + goog.crypt.JpegEncoder + goog.crypt.Md5 + goog.crypt.Sha1 + goog.crypt.Sha2 + goog.crypt.Sha224 + goog.crypt.Sha256 + goog.crypt.Sha2_64bit + goog.crypt.Sha512 + goog.crypt.Sha512_256 + goog.crypt.base64 + goog.crypt.baseN + goog.crypt.hash32 + goog.crypt.hashTester + goog.crypt.pbkdf2 + goog.date.Date + goog.date.DateLike + goog.date.DateRange + goog.date.DateTime + goog.date.Interval + goog.date.UtcDateTime + goog.date.duration + goog.date.month + goog.date.relative.TimeDeltaFormatter + goog.date.relative.Unit + goog.date.relativeWithPlurals + goog.date.weekDay + goog.format + goog.format.EmailAddress + goog.format.HtmlPrettyPrinter + goog.format.InternationalizedEmailAddress + goog.format.JsonPrettyPrinter + goog.i18n.BidiFormatter + goog.i18n.CharListDecompressor + goog.i18n.CharPickerData + goog.i18n.DateTimeFormat + goog.i18n.DateTimeParse + goog.i18n.GraphemeBreak + goog.i18n.MessageFormat + goog.i18n.NumberFormat + goog.i18n.TimeZone + goog.i18n.bidi + goog.i18n.bidi.Dir + goog.i18n.bidi.Format + goog.i18n.collation + goog.i18n.currency + goog.i18n.mime + goog.i18n.ordinalRules + goog.i18n.pluralRules + goog.i18n.uChar + goog.i18n.uChar.LocalNameFetcher + goog.i18n.uChar.RemoteNameFetcher + goog.i18n.uCharNames + goog.iter + goog.iter.Iterable + goog.iter.Iterator + goog.json + goog.json.EvalJsonProcessor + goog.json.HybridJsonProcessor + goog.json.NativeJsonProcessor + goog.json.Replacer + goog.json.Reviver + goog.json.Serializer + goog.json.hybrid + goog.locale + goog.locale.TimeZoneFingerprint + goog.locale.defaultLocaleNameConstants + goog.locale.genericFontNames + goog.locale.timeZoneDetection + goog.math + goog.math.AffineTransform + goog.math.Bezier + goog.math.Box + goog.math.Coordinate + goog.math.Coordinate3 + goog.math.ExponentialBackoff + goog.math.Integer + goog.math.Line + goog.math.Long + goog.math.Matrix + goog.math.Path + goog.math.Path.Segment + goog.math.Range + goog.math.RangeSet + goog.math.Rect + goog.math.Size + goog.math.Vec2 + goog.math.Vec3 + goog.math.interpolator.Linear1 + goog.math.interpolator.Pchip1 + goog.math.interpolator.Spline1 + goog.math.paths + goog.math.tdma + goog.spell.SpellCheck + goog.string + goog.string.Const + goog.string.StringBuffer + goog.string.Unicode + goog.string.format + goog.string.newlines + goog.string.newlines.Line + goog.structs + goog.structs.AvlTree + goog.structs.AvlTree.Node + goog.structs.CircularBuffer + goog.structs.Heap + goog.structs.InversionMap + goog.structs.LinkedMap + goog.structs.Map + goog.structs.Node + goog.structs.Pool + goog.structs.PriorityPool + goog.structs.PriorityQueue + goog.structs.QuadTree + goog.structs.QuadTree.Node + goog.structs.QuadTree.Point + goog.structs.Queue + goog.structs.Set + goog.structs.SimplePool + goog.structs.StringSet + goog.structs.TreeNode + goog.structs.Trie + goog.structs.weak + goog.text.LoremIpsum)) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs new file mode 100644 index 000000000..538062325 --- /dev/null +++ b/src/test/self/self_parity/test.cljs @@ -0,0 +1,284 @@ +(ns + ^{:doc "Builds and runs the ClojureScript compiler test suite + in self-host mode, ensuring parity of bootstrapped ClojureScript + with JVM based ClojureScript. + + This involves dynamically loading the test suite files at runtime, + excercising that they can be compiled by the bootstrapped + ClojureScript compiler, and also running the resulting tests."} + self-parity.test + (:require [clojure.string :as string] + [cljs.nodejs :as nodejs] + [cljs.js :as cljs] + [cljs.reader :as reader])) + +(def out-dir "builds/out-self-parity") + +(def src-paths [out-dir + "src/main/cljs" + "src/main/clojure" + "src/test/cljs"]) + +(defn init-runtime + "Initializes the runtime so that we can use the cljs.user + namespace and so that Google Closure is set up to work + properly with :optimizations :none." + [] + (set! (.-user js/cljs) #js {}) + ;; monkey-patch isProvided_ to avoid useless warnings + (js* "goog.isProvided_ = function(x) { return false; };") + ;; monkey-patch goog.require, skip all the loaded checks + (set! (.-require js/goog) + (fn [name] + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name)))) + ;; setup printing + (nodejs/enable-util-print!) + ;; redef goog.require to track loaded libs + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (aget (.. js/goog -dependencies_ -nameToPath) name)))))) + +;; Node file reading fns + +(def fs (nodejs/require "fs")) + +(defn node-read-file + "Accepts a filename to read and a callback. Upon success, invokes + callback with the source. Otherwise invokes the callback with nil." + [filename cb] + (.readFile fs filename "utf-8" + (fn [err source] + (cb (when-not err + source))))) + +(defn node-read-file-sync + "Accepts a filename to read. Upon success, returns the source. + Otherwise returns nil." + [filename] + (.readFileSync fs filename "utf-8")) + +;; Facilities for loading Closure deps + +(defn closure-index + "Builds an index of Closure files. Similar to + cljs.js-deps/goog-dependencies*" + [] + (let [paths-to-provides + (map (fn [[_ path provides]] + [path (map second + (re-seq #"'(.*?)'" provides))]) + (re-seq #"\ngoog\.addDependency\('(.*)', \[(.*?)\].*" + (node-read-file-sync (str out-dir "/goog/deps.js"))))] + (into {} + (for [[path provides] paths-to-provides + provide provides] + [(symbol provide) (str out-dir "/goog/" (second (re-find #"(.*)\.js$" path)))])))) + +(def closure-index-mem (memoize closure-index)) + +(defn load-goog + "Loads a Google Closure implementation source file." + [name cb] + (if-let [goog-path (get (closure-index-mem) name)] + (if-let [source (node-read-file-sync (str goog-path ".js"))] + (cb {:source source + :lang :js}) + (cb nil)) + (cb nil))) + +;; Facilities for loading files + +(defn- filename->lang + "Converts a filename to a lang keyword by inspecting the file + extension." + [filename] + (if (string/ends-with? filename ".js") + :js + :clj)) + +(defn replace-extension + "Replaces the extension on a file." + [filename new-extension] + (string/replace filename #".clj[sc]?$" new-extension)) + +(defn parse-edn + "Parses edn source to Clojure data." + [edn-source] + (reader/read-string edn-source)) + +(defn- read-some + "Reads the first filename in a sequence of supplied filenames, + using a supplied read-file-fn, calling back upon first successful + read, otherwise calling back with nil. Before calling back, first + attempts to read AOT artifacts (JavaScript and cache edn)." + [[filename & more-filenames] read-file-fn cb] + (if filename + (read-file-fn + filename + (fn [source] + (if source + (let [source-cb-value {:lang (filename->lang filename) + :source source}] + (if (or (string/ends-with? filename ".cljs") + (string/ends-with? filename ".cljc")) + (read-file-fn + (replace-extension filename ".js") + (fn [javascript-source] + (if javascript-source + (read-file-fn + (str filename ".cache.edn") + (fn [cache-edn] + (if cache-edn + (cb {:lang :js + :source javascript-source + :cache (parse-edn cache-edn)}) + (cb source-cb-value)))) + (cb source-cb-value)))) + (cb source-cb-value))) + (read-some more-filenames read-file-fn cb)))) + (cb nil))) + +(defn filenames-to-try + "Produces a sequence of filenames to try reading, in the + order they should be tried." + [src-paths macros path] + (let [extensions (if macros + [".clj" ".cljc"] + [".cljs" ".cljc" ".js"])] + (for [extension extensions + src-path src-paths] + (str src-path "/" path extension)))) + +(defn skip-load? + "Indicates namespaces that we either don't need to load, + shouldn't load, or cannot load (owing to unresolved + technical issues)." + [name macros] + ((if macros + #{'cljs.pprint + 'cljs.env.macros + 'cljs.analyzer.macros + 'cljs.compiler.macros} + #{'goog.object + 'goog.string + 'goog.string.StringBuffer + 'goog.array + 'cljs.core + 'cljs.env + 'cljs.pprint + 'cljs.tools.reader}) name)) + +;; An atom to keep track of things we've already loaded +(def loaded (atom #{})) + +(defn load? + "Determines whether the given namespace should be loaded." + [name macros] + (let [do-not-load (or (@loaded [name macros]) + (skip-load? name macros))] + (swap! loaded conj [name macros]) + (not do-not-load))) + +(defn make-load-fn + "Makes a load function that will read from a sequence of src-paths + using a supplied read-file-fn. It returns a cljs.js-compatible + *load-fn*. + Read-file-fn is a 2-arity function (fn [filename source-cb] ...) where + source-cb is itself a function (fn [source] ...) that needs to be called + with the source of the library (as string)." + [src-paths read-file-fn] + (fn [{:keys [name macros path]} cb] + (if (load? name macros) + (if (re-matches #"^goog/.*" path) + (load-goog name cb) + (read-some (filenames-to-try src-paths macros path) read-file-fn cb)) + (cb {:source "" + :lang :js})))) + +;; Facilities for evaluating JavaScript + +(def vm (nodejs/require "vm")) + +(defn node-eval + "Evaluates JavaScript in node." + [{:keys [name source]}] + (if-not js/COMPILED + (.runInThisContext vm source (str (munge name) ".js")) + (js/eval source))) + +;; Facilities for driving cljs.js + +(def load-fn (make-load-fn src-paths node-read-file)) + +(defn eval-form + "Evaluates a supplied form in a given namespace, + calling back with the evaluation result." + [st ns form cb] + (cljs/eval st + form + {:ns ns + :context :expr + :load load-fn + :eval node-eval + :verbose false} + cb)) + +(defn run-tests + "Runs the tests." + [] + ;; Ideally we'd just load test_runner.cljs, but a few namespace tests + ;; don't yet run in bootstrapped ClojureScript. These are commented + ;; out below and can be uncommented as fixed. + (let [st (cljs/empty-state)] + (eval-form st 'cljs.user + '(ns parity.core + (:require [cljs.test :refer-macros [run-tests]] + [cljs.core-test :as core-test] + [cljs.reader-test] + [cljs.binding-test] + #_[cljs.ns-test] + [clojure.string-test] + [clojure.data-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.pprint])) + (fn [{:keys [value error]}] + (if error + (prn error) + (eval-form st 'parity.core + '(run-tests + 'cljs.core-test + 'cljs.reader-test + 'clojure.string-test + 'clojure.data-test + 'cljs.letfn-test + 'cljs.reducers-test + 'cljs.binding-test + 'cljs.macro-test + 'cljs.top-level + #_'cljs.keyword-test + #_'cljs.ns-test + 'cljs.ns-test.foo + 'foo.ns-shadow-test + 'cljs.import-test + #_'cljs.pprint) + (fn [{:keys [value error]}] + (when error + (prn error))))))))) + +(defn -main [& args] + (init-runtime) + (run-tests)) + +(set! *main-cli-fn* -main) From 1e794f0a9d6c03b12644f00cd8aa4f8a3e86ab83 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 17 Apr 2016 11:16:13 -0400 Subject: [PATCH 1749/4033] CLJS-1612: Resolve ns aliases in syntax quote --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/main/clojure/cljs/core.cljc | 20 +++++----- src/test/cljs/cljs/syntax_quote_test.cljs | 8 ++++ src/test/cljs/test_runner.cljs | 2 + src/test/self/self_host/test.cljs | 45 +++++++++++++++++++++++ 5 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 src/test/cljs/cljs/syntax_quote_test.cljs diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 640701bad..cff4b821a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2626,7 +2626,7 @@ :default (str x)))) (defn resolve-symbol [s] - (:name (resolve-var {:ns {:name *cljs-ns*}} s))) + (:name (resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*)) s))) #?(:clj (defn forms-seq* diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index fe677143f..8ef2a3bb4 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -636,7 +636,7 @@ (core/let [gmap (gensym "map__") defaults (:or b)] (core/loop [ret (core/-> bvec (conj gmap) (conj v) - (conj gmap) (conj `(if (implements? ISeq ~gmap) (apply hash-map ~gmap) ~gmap)) + (conj gmap) (conj `(if (implements? ISeq ~gmap) (apply cljs.core/hash-map ~gmap) ~gmap)) ((core/fn [ret] (if (:as b) (conj ret (:as b) gmap) @@ -654,8 +654,8 @@ bk (val (first bes)) has-default (contains? defaults bb)] (recur (pb ret bb (if has-default - (core/list `get gmap bk (defaults bb)) - (core/list `get gmap bk))) + (core/list 'cljs.core/get gmap bk (defaults bb)) + (core/list 'cljs.core/get gmap bk))) (next bes))) ret))))] (core/cond @@ -1391,7 +1391,7 @@ `(fn ~[this-sym argsym] (this-as ~this-sym (.apply (.-call ~this-sym) ~this-sym - (.concat (array ~this-sym) (aclone ~argsym))))) + (.concat (array ~this-sym) (cljs.core/aclone ~argsym))))) (meta form)))] (ifn-invoke-methods type type-sym form)))) @@ -1714,7 +1714,7 @@ `(~'-lookup [this# ~ksym else#] (case ~ksym ~@(mapcat (core/fn [f] [(keyword f) f]) base-fields) - (get ~'__extmap ~ksym else#))) + (cljs.core/get ~'__extmap ~ksym else#))) 'ICounted `(~'-count [this#] (+ ~(count base-fields) (count ~'__extmap))) 'ICollection @@ -2366,13 +2366,13 @@ `(js/Array. ~size)) assoc :tag 'array)) ([type size] - `(make-array ~size)) + `(cljs.core/make-array ~size)) ([type size & more-sizes] (vary-meta `(let [dims# (list ~@more-sizes) - dimarray# (make-array ~size)] + dimarray# (cljs.core/make-array ~size)] (dotimes [i# (alength dimarray#)] - (aset dimarray# i# (apply make-array nil dims#))) + (aset dimarray# i# (apply cljs.core/make-array nil dims#))) dimarray#) assoc :tag 'array))) @@ -2471,7 +2471,7 @@ array ret." [a idx ret expr] `(let [a# ~a - ~ret (aclone a#)] + ~ret (cljs.core/aclone a#)] (loop [~idx 0] (if (< ~idx (alength a#)) (do @@ -2556,7 +2556,7 @@ prefer-table# (atom {}) method-cache# (atom {}) cached-hierarchy# (atom {}) - hierarchy# (get ~options :hierarchy (cljs.core/get-global-hierarchy))] + hierarchy# (cljs.core/get ~options :hierarchy (cljs.core/get-global-hierarchy))] (cljs.core/MultiFn. (cljs.core/symbol ~mm-ns ~(name mm-name)) ~dispatch-fn ~default hierarchy# method-table# prefer-table# method-cache# cached-hierarchy#)))))) diff --git a/src/test/cljs/cljs/syntax_quote_test.cljs b/src/test/cljs/cljs/syntax_quote_test.cljs new file mode 100644 index 000000000..ae5f7074a --- /dev/null +++ b/src/test/cljs/cljs/syntax_quote_test.cljs @@ -0,0 +1,8 @@ +(ns cljs.syntax-quote-test + (:require [cljs.test :as test :refer-macros [deftest is]])) + +(deftest test-syntax-quote + (is (= 'cljs.syntax-quote-test/foo `foo)) + (is (= 'cljs.test/test-vars `test/test-vars)) + (is (= 'cljs.test/deftest `test/deftest)) + (is (= 'cljs.test/foo `test/foo))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 2573dd025..b512ede15 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -13,6 +13,7 @@ [cljs.top-level] [cljs.reducers-test] [cljs.keyword-test] + [cljs.syntax-quote-test] [cljs.import-test] [cljs.ns-test.foo] [cljs.pprint])) @@ -32,6 +33,7 @@ 'cljs.macro-test 'cljs.top-level 'cljs.keyword-test + 'cljs.syntax-quote-test 'cljs.ns-test 'cljs.ns-test.foo 'foo.ns-shadow-test diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index ee8c731eb..2640a8e84 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -47,6 +47,23 @@ (defn elide-env [env ast opts] (dissoc ast :env)) +(defn str-evals-to + "Checks that a string evaluates to an expected value." + ([st l expected s] + (str-evals-to st l expected nil)) + ([st l expected s opts] + (cljs/eval-str st + s + nil + (merge + {:context :expr + :eval node-eval} + opts) + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= expected value)) + (inc! l))))) + ;; NOTE: can't set passes because callbacks happen _inside_ binding ;; do so will effect other tests @@ -663,6 +680,34 @@ (is (nil? error)) (is (= :x value))))) +(deftest test-CLJS-1612 + (async done + (let [st (cljs/empty-state) + l (latch 10 done)] + (cljs/eval st '(ns foo.core + (:require [bar.core :as bar])) + {:load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns bar.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns bar.core (:refer-macros bar.core)) (defn sub [a b] (- a b))"})))} + (fn [_] (inc! l))) + (testing "various syntax quote patterns" + (str-evals-to st l 'foo.core/foo "`foo" {:ns 'foo.core}) + (str-evals-to st l 'bar.core/sub "`bar/sub" {:ns 'foo.core}) + (str-evals-to st l 'bar.core/add "`bar/add" {:ns 'foo.core}) + (str-evals-to st l 'bar.core/undeclared "`bar/undeclared" {:ns 'foo.core})) + (testing "core macros under syntax quote" + (str-evals-to st l 13 + "(do (defmulti bar (fn [x y] [x y])) 13)" {:ns 'foo.core}) + (str-evals-to st l 17 + "(do (deftype FnLikeB [a] IFn (-invoke [_] a)) 17)" {:ns 'foo.core}) + (str-evals-to st l [10 4] + "(let [{:keys [a b] :or {b 4}} {:a 10}] [a b])" {:ns 'foo.core}) + (str-evals-to st l [[nil]] + "(js->clj (make-array nil 1 1))" {:ns 'foo.core}) + (str-evals-to st l [1 1 1 1 1] + "(let [an-array (int-array 5 0)] (js->clj (amap an-array idx ret (+ 1 (aget an-array idx)))))" {:ns 'foo.core}))))) + #_(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] From 6ba817065113313a15b0f027c6491e0bc732f3e9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 23 Apr 2016 16:51:20 -0400 Subject: [PATCH 1750/4033] CLJS-1595: Update Closure Compiler --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 245976eb2..4988f6e6d 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20151216 + v20160315 org.clojure diff --git a/project.clj b/project.clj index 04126c07c..1140e3f92 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-beta1"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] - [com.google.javascript/closure-compiler "v20151216"] + [com.google.javascript/closure-compiler "v20160315"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 6423cb83c..fa6d53af6 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.8.0" -CLOSURE_RELEASE="20151216" +CLOSURE_RELEASE="20160315" DJSON_RELEASE="0.2.6" GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" RHINO_RELEASE="1_7R5" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 91d5116e2..cc25f6ea4 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -244,8 +244,7 @@ (setExtraAnnotationNames (map name (:closure-extra-annotations opts))))) (. compiler-options - (setOutputCharset (:closure-output-charset opts "UTF-8")) - #_(setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler + (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler ) compiler-options) From 8d33dd4e55cc87c205bb5c37d0234af6e2132388 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 24 Apr 2016 14:18:39 -0400 Subject: [PATCH 1751/4033] CLJS-1632: Typos, c/e, and consistency of docs/params --- src/main/cljs/cljs/core.cljs | 41 +++++++++++++++-------------- src/main/cljs/cljs/js.cljs | 4 +-- src/main/clojure/cljs/analyzer.cljc | 2 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 792d815df..845eb5d09 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -50,7 +50,7 @@ (defonce ^{:doc "Each runtime environment provides a different way to print error output. - Whatever function *print-fn* is bound to will be passed any + Whatever function *print-err-fn* is bound to will be passed any Strings which should be printed." :dynamic true} *print-err-fn* (fn [_] @@ -2595,7 +2595,7 @@ reduces them without incurring seq initialization" (reduce bit-or (cljs.core/bit-or x y) more))) (defn bit-and-not - "Bitwise and" + "Bitwise and with complement" ([x y] (cljs.core/bit-and-not x y)) ([x y & more] (reduce bit-and-not (cljs.core/bit-and-not x y) more))) @@ -2662,12 +2662,12 @@ reduces them without incurring seq initialization" (defn ^boolean pos? "Returns true if num is greater than zero, else false" - [n] (cljs.core/pos? n)) + [x] (cljs.core/pos? x)) (defn ^boolean zero? "Returns true if num is zero, else false" - [n] - (cljs.core/zero? n)) + [x] + (cljs.core/zero? x)) (defn ^boolean neg? "Returns true if num is less than zero, else false" @@ -3000,7 +3000,7 @@ reduces them without incurring seq initialization" (es6-iterable Cons) (defn cons - "Returns a new seq where x is the first element and seq is the rest." + "Returns a new seq where x is the first element and coll is the rest." [x coll] (if (or (nil? coll) (implements? ISeq coll)) @@ -3065,7 +3065,7 @@ reduces them without incurring seq initialization" false))) (defn ^boolean symbol-identical? - "Efficient test to determine that two symbol are identical." + "Efficient test to determine that two symbols are identical." [x y] (if (identical? x y) true @@ -3118,7 +3118,7 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x start)) IPending - (-realized? [x] + (-realized? [coll] (not fn)) IWithMeta @@ -3496,10 +3496,10 @@ reduces them without incurring seq initialization" (-persistent! tcoll)) (defn conj! - "Adds x to the transient collection, and return coll. The 'addition' + "Adds val to the transient collection, and return tcoll. The 'addition' may happen at different 'places' depending on the concrete type." ([] (transient [])) - ([coll] coll) + ([tcoll] tcoll) ([tcoll val] (-conj! tcoll val)) ([tcoll val & vals] @@ -3532,7 +3532,7 @@ reduces them without incurring seq initialization" (defn pop! "Removes the last item from a transient vector. If - the collection is empty, throws an exception. Returns coll" + the collection is empty, throws an exception. Returns tcoll" [tcoll] (-pop! tcoll)) @@ -4122,7 +4122,7 @@ reduces them without incurring seq initialization" (defn reset! "Sets the value of atom to newval without regard for the - current value. Returns newval." + current value. Returns new-value." [a new-value] (if (instance? Atom a) (let [validate (.-validator a)] @@ -4472,7 +4472,8 @@ reduces them without incurring seq initialization" ([n x] (take n (repeat x)))) (defn replicate - "Returns a lazy seq of n xs." + "DEPRECATED: Use 'repeat' instead. + Returns a lazy seq of n xs." [n x] (take n (repeat x))) (defn repeatedly @@ -4586,10 +4587,10 @@ reduces them without incurring seq initialization" (defn tree-seq "Returns a lazy sequence of the nodes in a tree, via a depth-first walk. - branch? must be a fn of one arg that returns true if passed a node - that can have children (but may not). children must be a fn of one - arg that returns a sequence of the children. Will only be called on - nodes for which branch? returns true. Root is the root node of the + branch? must be a fn of one arg that returns true if passed a node + that can have children (but may not). children must be a fn of one + arg that returns a sequence of the children. Will only be called on + nodes for which branch? returns true. Root is the root node of the tree." [branch? children root] (let [walk (fn walk [node] @@ -9559,9 +9560,9 @@ reduces them without incurring seq initialization" (pr-str k)))) (defn clj->js - "Recursively transforms ClojureScript values to JavaScript. -sets/vectors/lists become Arrays, Keywords and Symbol become Strings, -Maps become Objects. Arbitrary keys are encoded to by key->js." + "Recursively transforms ClojureScript values to JavaScript. + sets/vectors/lists become Arrays, Keywords and Symbol become Strings, + Maps become Objects. Arbitrary keys are encoded to by key->js." [x] (when-not (nil? x) (if (satisfies? IEncodeJS x) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 14f641bbe..95a7a3b3c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -79,7 +79,7 @@ nil." :dynamic true} *load-fn* - (fn [name cb] + (fn [m cb] (throw (js/Error. "No *load-fn* set")))) (defonce @@ -95,7 +95,7 @@ The result of evaluation should be the return value." :dynamic true} *eval-fn* - (fn [js-source] + (fn [m] (throw (js/Error. "No *eval-fn* set")))) (defn js-eval diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cff4b821a..6ede96296 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -269,7 +269,7 @@ (defmethod error-message :undeclared-ns-form [warning-type info] - (str "Invalid :refer, var " (:type info) " " (:lib info) "/" (:sym info) " does not exist")) + (str "Invalid :refer, " (:type info) " " (:lib info) "/" (:sym info) " does not exist")) (defmethod error-message :protocol-deprecated [warning-type info] From f110ed3b0adcaad90912a3c89228cc882ffe2214 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 24 Apr 2016 11:30:25 -0400 Subject: [PATCH 1752/4033] CLJS-1588: Self-host: satisfies? on defrecord instance A bit-shift operation for protocol masks overflows to signed negagive in JavaScript, causing bootstrap failure. Fix is to simply multiply by 2 instead of bit-shift left. --- src/main/clojure/cljs/core.cljc | 3 ++- src/test/cljs/cljs/core_test.cljs | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8ef2a3bb4..d9da4618f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -755,7 +755,8 @@ (iterate (core/fn [[p b]] (if (core/== 2147483648 b) [(core/inc p) 1] - [p (core/bit-shift-left b 1)])) + [p #?(:clj (core/bit-shift-left b 1) + :cljs (core/* 2 b))])) [0 1]))) (def fast-path-protocol-partitions-count diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index a6211735a..aff60a69c 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1899,6 +1899,9 @@ (defrecord C [a b c]) (defrecord A' [x]) (defrecord B' [x]) +(defrecord FooComparable [x] + IComparable + (-compare [_ o] (compare x (.-x o)))) (deftest test-records (let [fred (Person. "Fred" "Mertz") @@ -1939,7 +1942,8 @@ (is (= (set (keys (dissoc more-letters :d))) #{:a :b :c :e :f})) (is (= (set (keys (dissoc more-letters :d :e))) #{:a :b :c :f})) (is (= (set (keys (dissoc more-letters :d :e :f))) #{:a :b :c})) - (is (not= (A'. nil) (B'. nil)))))) + (is (not= (A'. nil) (B'. nil))) + (is (satisfies? IComparable (->FooComparable 1)))))) (deftype FnLike [] IFn From 396a62dd360dee917ff6063608eb25ef62ff094a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 25 Apr 2016 07:29:42 -0400 Subject: [PATCH 1753/4033] 1.8.51 --- README.md | 6 +++--- changes.md | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4d8b2ef7b..5c30b9b00 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.8.40 +Latest stable release: 1.8.51 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.8.40"] +[org.clojure/clojurescript "1.8.51"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.8.40 org.clojure clojurescript - 1.8.40 + 1.8.51 ``` diff --git a/changes.md b/changes.md index 28f83bbe3..ef8a6532a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,21 @@ +## 1.8.51 + +### Changes +* bump Closure Compiler to v20160315 +* bump tools.reader to 1.0.0-beta1 +* CLJS-1624: Avoid useage of JSC_HOME in test bash scripts + +### Enhancements +* CLJS-1626: cljs.test for bootstrap + +### Fixes +* CLJS-1588: defrecord satisfies? behavior under bootstrap +* CLJS-1632: docs / arglist consistency +* CLJS-1612: Resolve ns aliases in syntax-quote +* CLJS-1621: Foreign libs modules of different types don't compile together +* CLJS-1617: inlined `list` evaluation order +* :parallel-build race condition + ## 1.8.40 ### Fixes From edce7e0ea322cfcfb46f7b1947ab02d878fa545f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 30 Apr 2016 22:47:38 -0400 Subject: [PATCH 1754/4033] CLJS-1637: Missing docstrings for a few vars --- src/main/cljs/cljs/core.cljs | 6 +++++- src/main/clojure/cljs/core.cljc | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 845eb5d09..096ec0fa3 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -981,6 +981,7 @@ (-pr-writer [o writer _] (-write writer str))) (defn symbol + "Returns a Symbol with the given namespace and name." ([name] (if (symbol? name) name @@ -9158,6 +9159,7 @@ reduces them without incurring seq initialization" (string-print (pr-str-with-opts objs opts))) (defn newline + "Prints a newline using *print-fn*" ([] (newline nil)) ([opts] (string-print "\n") @@ -10199,7 +10201,9 @@ reduces them without incurring seq initialization" (fn [x y] (cond (pred x y) -1 (pred y x) 1 :else 0))) -(defn ^boolean special-symbol? [x] +(defn ^boolean special-symbol? + "Returns true if x names a special form" + [x] (contains? '#{if def fn* do let* loop* letfn* throw try catch finally recur new set! ns deftype* defrecord* . js* & quote var} diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index d9da4618f..0c2f631ff 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -600,7 +600,10 @@ (seq ret))) (core/list (asig fdecl)))))) -(core/defmacro defonce [x init] +(core/defmacro defonce + "defs name to have the root value of init iff the named var has no root value, + else init is unevaluated" + [x init] `(when-not (exists? ~x) (def ~x ~init))) From 7a2936266d2ce7fea73262587ec6b2153058926b Mon Sep 17 00:00:00 2001 From: r0man Date: Wed, 20 Apr 2016 14:57:35 +0200 Subject: [PATCH 1755/4033] CLJS-1629: Fix warning about duplicate test-pr-str --- src/test/cljs/cljs/core_test.cljs | 38 +++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index aff60a69c..5fcec7888 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -349,21 +349,6 @@ (is (= {"x" "y"} (meta ^{"x" "y"} []))) )) -(deftest test-pr-str - (testing "Testing printing" - (is (= "\"asdf\" \"asdf\"" (pr-str "asdf" "asdf"))) - (is (= "[1 true {:a 2, :b #\"x\\\"y\"} #js [3 4]]" - (pr-str [1 true {:a 2 :b #"x\"y"} (array 3 4)]))) - (is (= "\"asdf\"\n" (prn-str "asdf"))) - (is (= "[1 true {:a 2, :b 42} #js [3 4]]\n" - (prn-str [1 true {:a 2 :b 42} (array 3 4)]))) - (is (= "asdf" (print-str "asdf"))) - (is (= "asdf\n" (println-str "asdf"))) - (is (= "" (pr-str))) - (is (= "\n" (prn-str))) - (is (= "12" (with-out-str (print 1) (print 2)))) - (is (= "12" (with-out-str (*print-fn* 1) (*print-fn* 2)))))) - (deftest test-bit-operations (testing "Testing bit operations" (is (= [1 0 0 40 43 49 49]) @@ -1685,8 +1670,9 @@ (defrecord PrintMe [a b]) -(deftest test-pr-str +(deftest test-printing (testing "Testing pr-str" + (is (= (pr-str) "")) (is (= (pr-str 1) "1")) (is (= (pr-str -1) "-1")) (is (= (pr-str -1.5) "-1.5")) @@ -1724,7 +1710,25 @@ (is (= (pr-str uuid) (str "#uuid \"" uuid-str "\"")))) ;; pr-str PersistentQueueSeq - CLJS-800 (is (= (pr-str (rest (conj cljs.core.PersistentQueue.EMPTY 1 2 3))) "(2 3)")) - )) + (is (= "\"asdf\" \"asdf\"" (pr-str "asdf" "asdf"))) + ;; Different hash map order on self-host + (is (#{"[1 true {:a 2, :b #\"x\\\"y\"} #js [3 4]]" + "[1 true {:b #\"x\\\"y\", :a 2} #js [3 4]]"} + (pr-str [1 true {:a 2 :b #"x\"y"} (array 3 4)])))) + (testing "Testing print-str" + (is (= (print-str "asdf") "asdf"))) + (testing "Testing println-str" + (is (= (println-str "asdf") "asdf\n"))) + (testing "Testing prn-str" + (is (= (prn-str) "\n")) + (is (= (prn-str "asdf") "\"asdf\"\n")) + ;; Different hash map order on self-host + (is (#{"[1 true {:a 2, :b 42} #js [3 4]]\n" + "[1 true {:b 42, :a 2} #js [3 4]]\n"} + (prn-str [1 true {:a 2 :b 42} (array 3 4)])))) + (testing "Testing with-out-str" + (is (= "12" (with-out-str (print 1) (print 2)))) + (is (= "12" (with-out-str (*print-fn* 1) (*print-fn* 2)))))) (deftest test-inext (testing "Testing INext" From dd589037f242b4eaace113ffa28ab7b3791caf47 Mon Sep 17 00:00:00 2001 From: Christopher Vermilion Date: Tue, 26 Apr 2016 23:36:49 -0400 Subject: [PATCH 1756/4033] CLJS-1635: Var type implements IEquiv but not IHash Extends the Var deftype to implement the IHash protocol, with similar semantics to its IEquiv implementation: two var's should compare and hash equal iff their symbol is the same. --- src/main/cljs/cljs/core.cljs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 096ec0fa3..c39475b92 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1012,6 +1012,9 @@ (if (instance? Var other) (= (.-sym this) (.-sym other)) false)) + IHash + (-hash [_] + (hash-symbol sym)) Fn IFn (-invoke [_] From 39083a67ca3f08730d1d4f8bb29c1654c99a7dd4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 May 2016 09:52:08 -0400 Subject: [PATCH 1757/4033] fix Closure link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c30b9b00..ed7b46d1b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## What is ClojureScript? ## -ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaScript. It is designed to emit JavaScript code which is compatible with the advanced compilation mode of the [Google Closure](http://code.google.com/closure/) optimizing compiler. +ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaScript. It is designed to emit JavaScript code which is compatible with the advanced compilation mode of the [Google Closure](https://developers.google.com/closure/compiler/) optimizing compiler. ## Releases and dependency information ## From deec44aa16a2ce6c8b039dfc286888917b5eaceb Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 May 2016 18:02:01 -0400 Subject: [PATCH 1758/4033] CLJS-1642: cljs.core/reductions does not respect 'reduced' Apply CLJ-1185 patch to ClojureScript --- src/main/cljs/cljs/core.cljs | 16 +++++++++------- src/test/cljs/cljs/core_test.cljs | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c39475b92..115391f33 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8858,15 +8858,17 @@ reduces them without incurring seq initialization" "Returns a lazy seq of the intermediate values of the reduction (as per reduce) of coll by f, starting with init." ([f coll] - (lazy-seq - (if-let [s (seq coll)] - (reductions f (first s) (rest s)) - (list (f))))) + (lazy-seq + (if-let [s (seq coll)] + (reductions f (first s) (rest s)) + (list (f))))) ([f init coll] + (if (reduced? init) + (list @init) (cons init - (lazy-seq - (when-let [s (seq coll)] - (reductions f (f init (first s)) (rest s))))))) + (lazy-seq + (when-let [s (seq coll)] + (reductions f (f init (first s)) (rest s)))))))) (defn juxt "Takes a set of functions and returns a fn that is the juxtaposition diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5fcec7888..eb8aea536 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3150,6 +3150,21 @@ (is (= [""] (s/split-lines ""))) (is (= [] (s/split-lines "\n\n\n")))) +(deftest test-reductions-obeys-reduced + (is (= [0 :x] + (reductions (constantly (reduced :x)) + (range)))) + (is (= [:x] + (reductions (fn [acc x] x) + (reduced :x) + (range)))) + (is (= [2 6 12 12] + (reductions (fn [acc x] + (if (= x :stop) + (reduced acc) + (+ acc x))) + [2 4 6 :stop 8 10])))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From fc827fa5b0f1b290523f304fd84cf0a6c2a7bfa2 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Tue, 24 May 2016 23:32:50 +0300 Subject: [PATCH 1759/4033] CLJS-1647: Rethrow exception from parallel-build Build tooling should be able to catch the exception so that they can choose how to show the error the user. --- src/main/clojure/cljs/closure.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cc25f6ea4..4e86e8b86 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -824,8 +824,7 @@ {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))) (catch Throwable e - (util/debug-prn e) - (reset! failed true))) + (reset! failed e))) (when-not @failed (when-let [ns (:ns ns-info)] (swap! input-set disj ns)) @@ -849,6 +848,8 @@ (.countDown latch)))) (util/measure compiler-stats "Compile sources" (.await latch)) (.shutdown es) + (when @failed + (throw @failed)) @compiled)) (defn compile-sources From 9887580fc731e3194e1619ba603d30e2548cc768 Mon Sep 17 00:00:00 2001 From: Rohit Aggarwal Date: Thu, 26 May 2016 21:31:06 +0100 Subject: [PATCH 1760/4033] CLJS-1649: Possible issue with in cljs.reader or cljs.core/PersistentHashMap. We are storing last 256 computed hash-string values in a JS object. JS objects only accept strings as keys. So its not able to distinguish between a `nil` and a `"null"` if either is present in the cache already. We are already assuming that `nil` hashes to 0. So don't check for it in the cache or try to compute its hash. --- src/main/cljs/cljs/core.cljs | 10 ++++++---- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 115391f33..8420800fa 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -871,10 +871,12 @@ (when (> string-hash-cache-count 255) (set! string-hash-cache (js-obj)) (set! string-hash-cache-count 0)) - (let [h (aget string-hash-cache k)] - (if (number? h) - h - (add-to-string-hash-cache k)))) + (if (nil? k) + 0 + (let [h (aget string-hash-cache k)] + (if (number? h) + h + (add-to-string-hash-cache k))))) (defn hash "Returns the hash code of its argument. Note this is the hash code diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index eb8aea536..92f27b550 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3165,6 +3165,10 @@ (+ acc x))) [2 4 6 :stop 8 10])))) +(deftest test-nil-hashing-cljs-1649 + (is (zero? (hash-string nil))) + (is (not (zero? (hash-string "null"))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From a5cf24c39ba1c98971ff7a0eefc77eac2a7d8b87 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 May 2016 15:13:28 -0400 Subject: [PATCH 1761/4033] add test.check as test dep --- pom.template.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pom.template.xml b/pom.template.xml index 4988f6e6d..f11425425 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -52,6 +52,18 @@ tools.reader 1.0.0-beta1 + + org.clojure + test.check + 0.9.0 + test + + + org.clojure + clojure + + + From ffd4d0575d378a07d9e2fb3333831e6754a847ed Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 May 2016 16:23:47 -0400 Subject: [PATCH 1762/4033] port of clojure.spec.gen namespaces --- src/main/cljs/cljs/spec/gen.clj | 62 ++++++++++++++ src/main/cljs/cljs/spec/gen.cljs | 143 +++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 src/main/cljs/cljs/spec/gen.clj create mode 100644 src/main/cljs/cljs/spec/gen.cljs diff --git a/src/main/cljs/cljs/spec/gen.clj b/src/main/cljs/cljs/spec/gen.clj new file mode 100644 index 000000000..8847bd249 --- /dev/null +++ b/src/main/cljs/cljs/spec/gen.clj @@ -0,0 +1,62 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns cljs.spec.gen + (:refer-clojure :exclude [delay]) + (:require [cljs.core :as c])) + +(defmacro dynaload [[quote s]] + `(if (c/exists? ~s) + ~(vary-meta s assoc :cljs.analyzer/no-resolve true) + (fn [& ~'args] + (throw (js/Error. (str "Var " '~s " is not on the classpath")))))) + +(defmacro delay + "given body that returns a generator, returns a + generator that delegates to that, but delays + creation until used." + [& body] + `(cljs.spec.gen/delay-impl (c/delay ~@body))) + +(defmacro ^:skip-wiki lazy-combinator + "Implementation macro, do not call directly." + [s] + (let [fqn (symbol "clojure.test.check.generators" (name s)) + doc (str "Lazy loaded version of " fqn)] + `(let [g# (c/delay (dynaload '~fqn))] + (defn ~s + ~doc + [& ~'args] + (apply @g# ~'args))))) + +(defmacro ^:skip-wiki lazy-combinators + "Implementation macro, do not call directly." + [& syms] + `(do + ~@(map + (fn [s] (list `lazy-combinator s)) + syms))) + +(defmacro ^:skip-wiki lazy-prim + "Implementation macro, do not call directly." + [s] + (let [fqn (symbol "clojure.test.check.generators" (name s)) + doc (str "Fn returning " fqn)] + `(let [g# (c/delay (dynaload '~fqn))] + (defn ~s + ~doc + [& ~'args] + @g#)))) + +(defmacro ^:skip-wiki lazy-prims + "Implementation macro, do not call directly." + [& syms] + `(do + ~@(map + (fn [s] (list `lazy-prim s)) + syms))) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/gen.cljs b/src/main/cljs/cljs/spec/gen.cljs new file mode 100644 index 000000000..46c5beeb1 --- /dev/null +++ b/src/main/cljs/cljs/spec/gen.cljs @@ -0,0 +1,143 @@ +; 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.spec.gen + (:refer-clojure :exclude [boolean cat hash-map list map not-empty set vector + char double int keyword symbol string uuid delay]) + (:require-macros [cljs.core :as c] + [cljs.spec.gen :as gen :refer [dynaload lazy-combinators lazy-prims]]) + (:require [cljs.core :as c])) + +(def ^:private quick-check-ref + (c/delay (dynaload 'clojure.test.check/quick-check))) + +(defn quick-check + [& args] + (apply @quick-check-ref args)) + +(def ^:private for-all*-ref + (c/delay (dynaload 'clojure.test.check.properties/for-all*))) + +(defn for-all* + "Dynamically loaded clojure.test.check.properties/for-all*." + [& args] + (apply @for-all*-ref args)) + +(let [g? (c/delay (dynaload 'clojure.test.check.generators/generator?)) + g (c/delay (dynaload 'clojure.test.check.generators/generate)) + mkg (c/delay (dynaload 'clojure.test.check.generators/->Generator))] + (defn- generator? + [x] + (@g? x)) + (defn- generator + [gfn] + (@mkg gfn)) + (defn generate + "Generate a single value using generator." + [generator] + (@g generator))) + +(defn ^:skip-wiki delay-impl + [gfnd] + ;;N.B. depends on test.check impl details + (generator (fn [rnd size] + ((:gen @gfnd) rnd size)))) + +;(defn gen-for-name +; "Dynamically loads test.check generator named s." +; [s] +; (let [g (dynaload s)] +; (if (generator? g) +; g +; (throw (js/Error. (str "Var " s " is not a generator")))))) + +(lazy-combinators hash-map list map not-empty set vector fmap elements + bind choose one-of such-that tuple sample return) + +(lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double + int keyword keyword-ns large-integer ratio simple-type simple-type-printable + string string-ascii string-alphanumeric symbol symbol-ns uuid) + +(defn cat + "Returns a generator of a sequence catenated from results of +gens, each of which should generate something sequential." + [& gens] + (fmap #(apply concat %) + (apply tuple gens))) + +(def ^:private +gen-builtins + (c/delay + (let [simple (simple-type-printable)] + {number? (one-of [(large-integer) (double)]) + integer? (large-integer) + ;float? (double) + string? (string-alphanumeric) + keyword? (keyword-ns) + symbol? (symbol-ns) + map? (map simple simple) + vector? (vector simple) + list? (list simple) + seq? (list simple) + char? (char) + set? (set simple) + nil? (return nil) + false? (return false) + true? (return true) + zero? (return 0) + ;rational? (one-of [(large-integer) (ratio)]) + coll? (one-of [(map simple simple) + (list simple) + (vector simple) + (set simple)]) + empty? (elements [nil '() [] {} #{}]) + associative? (one-of [(map simple simple) (vector simple)]) + sequential? (one-of [(list simple) (vector simple)]) + ;ratio? (such-that ratio? (ratio)) + }))) + +(defn gen-for-pred + "Given a predicate, returns a built-in generator if one exists." + [pred] + (if (set? pred) + (elements pred) + (get @gen-builtins pred))) + +(comment + (require 'clojure.test.check) + (require 'clojure.test.check.properties) + (require 'cljs.spec.gen) + (in-ns 'cljs.spec.gen) + + ;; combinators, see call to lazy-combinators above for complete list + (generate (one-of [(gen-for-pred integer?) (gen-for-pred string?)])) + (generate (such-that #(< 10000 %) (gen-for-pred integer?))) + (let [reqs {:a (gen-for-pred number?) + :b (gen-for-pred ratio?)} + opts {:c (gen-for-pred string?)}] + (generate (bind (choose 0 (count opts)) + #(let [args (concat (seq reqs) (shuffle (seq opts)))] + (->> args + (take (+ % (count reqs))) + (mapcat identity) + (apply hash-map)))))) + (generate (cat (list (gen-for-pred string?)) + (list (gen-for-pred ratio?)))) + + ;; load your own generator + (gen-for-name 'clojure.test.check.generators/int) + + ;; failure modes + (gen-for-name 'unqualified) + (gen-for-name 'clojure.core/+) + (gen-for-name 'clojure.core/name-does-not-exist) + (gen-for-name 'ns.does.not.exist/f) + + ) + + From cee4f5373c12bf098fd34ae94d4eba2ed148684c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 May 2016 16:28:51 -0400 Subject: [PATCH 1763/4033] fix up the gen comment block so that it is executable in ClojureScript --- src/main/cljs/cljs/spec/gen.cljs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/spec/gen.cljs b/src/main/cljs/cljs/spec/gen.cljs index 46c5beeb1..8b01f0de0 100644 --- a/src/main/cljs/cljs/spec/gen.cljs +++ b/src/main/cljs/cljs/spec/gen.cljs @@ -118,7 +118,7 @@ gen-builtins (generate (one-of [(gen-for-pred integer?) (gen-for-pred string?)])) (generate (such-that #(< 10000 %) (gen-for-pred integer?))) (let [reqs {:a (gen-for-pred number?) - :b (gen-for-pred ratio?)} + :b (gen-for-pred keyword?)} opts {:c (gen-for-pred string?)}] (generate (bind (choose 0 (count opts)) #(let [args (concat (seq reqs) (shuffle (seq opts)))] @@ -127,16 +127,16 @@ gen-builtins (mapcat identity) (apply hash-map)))))) (generate (cat (list (gen-for-pred string?)) - (list (gen-for-pred ratio?)))) + (list (gen-for-pred integer?)))) ;; load your own generator - (gen-for-name 'clojure.test.check.generators/int) + ;(gen-for-name 'clojure.test.check.generators/int) ;; failure modes - (gen-for-name 'unqualified) - (gen-for-name 'clojure.core/+) - (gen-for-name 'clojure.core/name-does-not-exist) - (gen-for-name 'ns.does.not.exist/f) + ;(gen-for-name 'unqualified) + ;(gen-for-name 'clojure.core/+) + ;(gen-for-name 'clojure.core/name-does-not-exist) + ;(gen-for-name 'ns.does.not.exist/f) ) From f00df0770cb14bb0d9f7e6ff5dd0de6a0bdbf3ad Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 15:36:29 -0400 Subject: [PATCH 1764/4033] add locking macro --- src/main/clojure/cljs/core.cljc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 0c2f631ff..773e3fc57 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2680,6 +2680,10 @@ [vol f & args] `(-vreset! ~vol (~f (-deref ~vol) ~@args))) +(core/defmacro locking + [x & forms] + `(do ~@forms)) + ;; INTERNAL - do not use, only for Node.js (core/defmacro load-file* [f] `(. js/goog (~'nodeGlobalRequire ~f))) From ad4561c103327bd7c706577d1f9a337dbdf6e84e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 15:51:31 -0400 Subject: [PATCH 1765/4033] wip --- src/main/clojure/cljs/spec.clj | 353 ++++++++++ src/main/clojure/cljs/spec.cljs | 1104 +++++++++++++++++++++++++++++++ 2 files changed, 1457 insertions(+) create mode 100644 src/main/clojure/cljs/spec.clj create mode 100644 src/main/clojure/cljs/spec.cljs diff --git a/src/main/clojure/cljs/spec.clj b/src/main/clojure/cljs/spec.clj new file mode 100644 index 000000000..4dc014de9 --- /dev/null +++ b/src/main/clojure/cljs/spec.clj @@ -0,0 +1,353 @@ +; 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.spec + (:refer-clojure :exclude [+ * and or cat def keys]) + (:require [cljs.core :as c] + [clojure.walk :as walk] + [cljs.spec.gen :as gen] + [clojure.string :as str])) + +(defn- ->sym + "Returns a symbol from a symbol or var" + [x] + (if (var? x) + (let [^clojure.lang.Var v x] + (symbol (str (.name (.ns v))) + (str (.sym v)))) + x)) + +(defn- unfn [expr] + (if (c/and (seq? expr) + (symbol? (first expr)) + (= "fn*" (name (first expr)))) + (let [[[s] & form] (rest expr)] + (conj (walk/postwalk-replace {s '%} form) '[%] 'fn)) + expr)) + +(defn- res [form] + (cond + (keyword? form) form + (symbol? form) (c/or (-> form resolve ->sym) form) + (sequential? form) (walk/postwalk #(if (symbol? %) (res %) %) (unfn form)) + :else form)) + +(defmacro def + "Given a namespace-qualified keyword or symbol k, and a spec, spec-name, predicate or regex-op + makes an entry in the registry mapping k to the spec" + [k spec-form] + `(cljs.spec/def-impl ~k '~(res spec-form) ~spec-form)) + +(defmacro spec + "Takes a single predicate form, e.g. can be the name of a predicate, + like even?, or a fn literal like #(< % 42). Note that it is not + generally necessary to wrap predicates in spec when using the rest + of the spec macros, only to attach a unique generator + + Can also be passed the result of one of the regex ops - + cat, alt, *, +, ?, in which case it will return a regex-conforming + spec, useful when nesting an independent regex. + --- + + Optionally takes :gen generator-fn, which must be a fn of no args that + returns a test.check generator. + + Returns a spec." + [form & {:keys [gen]}] + `(cljs.spec/spec-impl '~(res form) ~form ~gen nil)) + +(defmacro multi-spec + "Takes the name of a spec/predicate-returning multimethod and a + tag-restoring keyword or fn (retag). Returns a spec that when + conforming or explaining data will pass it to the multimethod to get + an appropriate spec. You can e.g. use multi-spec to dynamically and + extensibly associate specs with 'tagged' data (i.e. data where one + of the fields indicates the shape of the rest of the structure). + + (defmulti mspec :tag) + + The methods should ignore their argument and return a predicate/spec: + (defmethod mspec :int [_] (s/keys :req-un [::tag ::i])) + + retag is used during generation to retag generated values with + matching tags. retag can either be a keyword, at which key the + dispatch-tag will be assoc'ed, or a fn of generated value and + dispatch-tag that should return an appropriately retagged value. + + Note that because the tags themselves comprise an open set, + the tag key spec cannot enumerate the values, but can e.g. + test for keyword?. + + Note also that the dispatch values of the multimethod will be + included in the path, i.e. in reporting and gen overrides, even + though those values are not evident in the spec. +" + [mm retag] + `(cljs.spec/multi-spec-impl '~(res mm) (var ~mm) ~retag)) + +(defmacro keys + "Creates and returns a map validating spec. :req and :opt are both + vectors of namespaced-qualified keywords. The validator will ensure + the :req keys are present. The :opt keys serve as documentation and + may be used by the generator. + + The :req key vector supports 'and' and 'or' for key groups: + + (s/keys :req [::x ::y (or ::secret (and ::user ::pwd))] :opt [::z]) + + There are also -un versions of :req and :opt. These allow + you to connect unqualified keys to specs. In each case, fully + qualfied keywords are passed, which name the specs, but unqualified + keys (with the same name component) are expected and checked at + conform-time, and generated during gen: + + (s/keys :req-un [:my.ns/x :my.ns/y]) + + The above says keys :x and :y are required, and will be validated + and generated by specs (if they exist) named :my.ns/x :my.ns/y + respectively. + + In addition, the values of *all* namespace-qualified keys will be validated + (and possibly destructured) by any registered specs. Note: there is + no support for inline value specification, by design. + + Optionally takes :gen generator-fn, which must be a fn of no args that + returns a test.check generator." + [& {:keys [req req-un opt opt-un gen]}] + (let [unk #(-> % name keyword) + req-keys (filterv keyword? (flatten req)) + req-un-specs (filterv keyword? (flatten req-un)) + _ (assert (every? #(c/and (keyword? %) (namespace %)) (concat req-keys req-un-specs opt opt-un)) + "all keys must be namespace-qualified keywords") + req-specs (into req-keys req-un-specs) + req-keys (into req-keys (map unk req-un-specs)) + opt-keys (into (vec opt) (map unk opt-un)) + opt-specs (into (vec opt) opt-un) + parse-req (fn [rk f] + (map (fn [x] + (if (keyword? x) + `#(contains? % ~(f x)) + (let [gx (gensym)] + `(fn* [~gx] + ~(walk/postwalk + (fn [y] (if (keyword? y) `(contains? ~gx ~(f y)) y)) + x))))) + rk)) + pred-exprs [`map?] + pred-exprs (into pred-exprs (parse-req req identity)) + pred-exprs (into pred-exprs (parse-req req-un unk)) + pred-forms (walk/postwalk res pred-exprs)] + ;; `(map-spec-impl ~req-keys '~req ~opt '~pred-forms ~pred-exprs ~gen) + `(cljs.spec/map-spec-impl {:req '~req :opt '~opt :req-un '~req-un :opt-un '~opt-un + :req-keys '~req-keys :req-specs '~req-specs + :opt-keys '~opt-keys :opt-specs '~opt-specs + :pred-forms '~pred-forms + :pred-exprs ~pred-exprs + :gfn ~gen}))) + +(defmacro or + "Takes key+pred pairs, e.g. + + (s/or :even even? :small #(< % 42)) + + Returns a destructuring spec that + returns a vector containing the key of the first matching pred and the + corresponding value." + [& key-pred-forms] + (let [pairs (partition 2 key-pred-forms) + keys (mapv first pairs) + pred-forms (mapv second pairs) + pf (mapv res pred-forms)] + (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") + `(cljs.spec/or-spec-impl ~keys '~pf ~pred-forms nil))) + +(defmacro and + "Takes predicate/spec-forms, e.g. + + (s/and even? #(< % 42)) + + Returns a spec that returns the conformed value. Successive + conformed values propagate through rest of predicates." + [& pred-forms] + `(cljs.spec/and-spec-impl '~(mapv res pred-forms) ~(vec pred-forms) nil)) + +(defmacro * + "Returns a regex op that matches zero or more values matching + pred. Produces a vector of matches iff there is at least one match" + [pred-form] + `(cljs.spec/rep-impl '~(res pred-form) ~pred-form)) + +(defmacro + + "Returns a regex op that matches one or more values matching + pred. Produces a vector of matches" + [pred-form] + `(cljs.spec/rep+impl '~(res pred-form) ~pred-form)) + +(defmacro ? + "Returns a regex op that matches zero or one value matching + pred. Produces a single value (not a collection) if matched." + [pred-form] + `(cljs.spec/maybe-impl ~pred-form '~pred-form)) + +(defmacro alt + "Takes key+pred pairs, e.g. + + (s/alt :even even? :small #(< % 42)) + + Returns a regex op that returns a vector containing the key of the + first matching pred and the corresponding value." + [& key-pred-forms] + (let [pairs (partition 2 key-pred-forms) + keys (mapv first pairs) + pred-forms (mapv second pairs) + pf (mapv res pred-forms)] + (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") + `(cljs.spec/alt-impl ~keys ~pred-forms '~pf))) + +(defmacro cat + "Takes key+pred pairs, e.g. + + (s/cat :e even? :o odd?) + + Returns a regex op that matches (all) values in sequence, returning a map + containing the keys of each pred and the corresponding value." + [& key-pred-forms] + (let [pairs (partition 2 key-pred-forms) + keys (mapv first pairs) + pred-forms (mapv second pairs) + pf (mapv res pred-forms)] + ;;(prn key-pred-forms) + (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") + `(cljs.spec/cat-impl ~keys ~pred-forms '~pf))) + +(defmacro & + "takes a regex op re, and predicates. Returns a regex-op that consumes + input as per re but subjects the resulting value to the + conjunction of the predicates, and any conforming they might perform." + [re & preds] + (let [pv (vec preds)] + `(cljs.spec/amp-impl ~re ~pv '~pv))) + +(defmacro conformer + "takes a predicate function with the semantics of conform i.e. it should return either a + (possibly converted) value or :clojure.spec/invalid, and returns a + spec that uses it as a predicate/conformer" + [f] + `(cljs.spec/spec-impl '~f ~f nil true)) + +(defmacro fspec + "takes :args :ret and (optional) :fn kwargs whose values are preds + and returns a spec whose conform/explain take a fn and validates it + using generative testing. The conformed value is always the fn itself. + + Optionally takes :gen generator-fn, which must be a fn of no args + that returns a test.check generator." + [& {:keys [args ret fn gen]}] + `(cljs.spec/fspec-impl ~args '~(res args) ~ret '~(res ret) ~fn '~(res fn) ~gen)) + +(defmacro tuple + "takes one or more preds and returns a spec for a tuple, a vector + where each element conforms to the corresponding pred. Each element + will be referred to in paths using its ordinal." + [& preds] + (assert (not (empty? preds))) + `(cljs.spec/tuple-impl '~(mapv res preds) ~(vec preds))) + +(defn- ns-qualify + "Qualify symbol s by resolving it or using the current *ns*." + [s] + (if-let [resolved (resolve s)] + (->sym resolved) + (if (namespace s) + s + (symbol (str (.name *ns*)) (str s))))) + +(defn- fn-spec-sym + [sym role] + (symbol (str (ns-qualify sym) "$" (name role)))) + +(defmacro fdef + "Takes a symbol naming a function, and one or more of the following: + + :args A regex spec for the function arguments as they were a list to be + passed to apply - in this way, a single spec can handle functions with + multiple arities + :ret A spec for the function's return value + :fn A spec of the relationship between args and ret - the + value passed is {:args conformed-args :ret conformed-ret} and is + expected to contain predicates that relate those values + + Qualifies fn-sym with resolve, or using *ns* if no resolution found. + Registers specs in the global registry, where they can be retrieved + by calling fn-specs. + + Once registered, function specs are included in doc, checked by + instrument, tested by the runner clojure.spec.test/run-tests, and (if + a macro) used to explain errors during macroexpansion. + + Note that :fn specs require the presence of :args and :ret specs to + conform values, and so :fn specs will be ignored if :args or :ret + are missing. + + Returns the qualified fn-sym. + + For example, to register function specs for the symbol function: + + (s/fdef clojure.core/symbol + :args (s/alt :separate (s/cat :ns string? :n string?) + :str string? + :sym symbol?) + :ret symbol?)" + [fn-sym & {:keys [args ret fn] :as m}] + (let [qn (ns-qualify fn-sym)] + `(do ~@(reduce + (c/fn [defns role] + (if (contains? m role) + (let [s (fn-spec-sym qn (name role))] + (conj defns `(cljs.spec/def '~s ~(get m role)))) + defns)) + [] [:args :ret :fn]) + '~qn))) + +(defmacro with-instrument-disabled + "Disables instrument's checking of calls, within a scope." + [& body] + `(binding [*instrument-enabled* nil] + ~@body)) + +(defmacro keys* + "takes the same arguments as spec/keys and returns a regex op that matches sequences of key/values, + converts them into a map, and conforms that map with a corresponding + spec/keys call: + + user=> (s/conform (s/keys :req-un [::a ::c]) {:a 1 :c 2}) + {:a 1, :c 2} + user=> (s/conform (s/keys* :req-un [::a ::c]) [:a 1 :c 2]) + {:a 1, :c 2} + + the resulting regex op can be composed into a larger regex: + + user=> (s/conform (s/cat :i1 integer? :m (s/keys* :req-un [::a ::c]) :i2 integer?) [42 :a 1 :c 2 :d 4 99]) + {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" + [& kspecs] + `(& (* (cat ::k keyword? ::v ::any)) ::kvs->map (keys ~@kspecs))) + +(defmacro nilable + "returns a spec that accepts nil and values satisfiying pred" + [pred] + `(and (or ::nil nil? ::pred ~pred) (conformer second))) + +(defmacro coll-of + "Returns a spec for a collection of items satisfying pred. The generator will fill an empty init-coll." + [pred init-coll] + `(spec (cljs.spec/coll-checker ~pred) :gen (cljs.spec/coll-gen ~pred ~init-coll))) + +(defmacro map-of + "Returns a spec for a map whose keys satisfy kpred and vals satisfy vpred." + [kpred vpred] + `(and (coll-of (tuple ~kpred ~vpred) {}) map?)) diff --git a/src/main/clojure/cljs/spec.cljs b/src/main/clojure/cljs/spec.cljs new file mode 100644 index 000000000..a3e92f0cb --- /dev/null +++ b/src/main/clojure/cljs/spec.cljs @@ -0,0 +1,1104 @@ +; 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.spec + (:refer-clojure :exclude [+ * and or cat def keys]) + (:require-macros [cljs.core :as c] + [cljs.spec :as s]) + (:require [cljs.core :as c] + [clojure.walk :as walk] + [cljs.spec.gen :as gen] + [clojure.string :as str])) + +(def ^:dynamic *recursion-limit* + "A soft limit on how many times a branching spec (or/alt/*/opt-keys/multi-spec) + can be recursed through during generation. After this a + non-recursive branch will be chosen." + 4) + +(def ^:dynamic *fspec-iterations* + "The number of times an anonymous fn specified by fspec will be (generatively) tested during conform" + 21) + +(def ^:dynamic *coll-check-limit* + "The number of items validated in a collection spec'ed with 'coll'" + 100) + +(def ^:private ^:dynamic *instrument-enabled* + "if false, instrumented fns call straight through" + true) + +(defprotocol Spec + (conform* [spec x]) + (explain* [spec path via in x]) + (gen* [spec overrides path rmap]) + (with-gen* [spec gfn]) + (describe* [spec])) + +(defonce ^:private registry-ref (atom {})) + +(defn- named? [x] (implements? INamed x)) + +(defn- with-name [spec name] + (with-meta spec (assoc (meta spec) ::name name))) + +(defn- spec-name [spec] + (when (implements? IMeta spec) + (-> (meta spec) ::name))) + +(defn- reg-resolve + "returns the spec/regex at end of alias chain starting with k, nil if not found, k if k not Named" + [k] + (if (named? k) + (let [reg @registry-ref] + (loop [spec k] + (if (named? spec) + (recur (get reg spec)) + (when spec + (with-name spec k))))) + k)) + +(defn spec? + "returns x if x is a spec object, else logical false" + [x] + (c/and (implements? Spec x) x)) + +(defn regex? + "returns x if x is a (clojure.spec) regex op, else logical false" + [x] + (c/and (::op x) x)) + +(declare spec-impl) +(declare regex-spec-impl) + +(defn- maybe-spec + "spec-or-k must be a spec, regex or resolvable kw/sym, else returns nil." + [spec-or-k] + (let [s (c/or (spec? spec-or-k) + (regex? spec-or-k) + (c/and (named? spec-or-k) (reg-resolve spec-or-k)) + nil)] + (if (regex? s) + (with-name (regex-spec-impl s nil) (spec-name s)) + s))) + +(defn- the-spec + "spec-or-k must be a spec, regex or kw/sym, else returns nil. Throws if unresolvable kw/sym" + [spec-or-k] + (c/or (maybe-spec spec-or-k) + (when (named? spec-or-k) + (throw (js/Error. (str "Unable to resolve spec: " spec-or-k)))))) + +(defn- specize [s] + (c/or (the-spec s) (spec-impl ::unknown s nil nil))) + +(defn conform + "Given a spec and a value, returns :clojure.spec/invalid if value does not match spec, + else the (possibly destructured) value." + [spec x] + (conform* (specize spec) x)) + +(defn form + "returns the spec as data" + [spec] + ;;TODO - incorporate gens + (describe* (specize spec))) + +(defn abbrev [form] + (cond + (seq? form) + (walk/postwalk (fn [form] + (cond + (c/and (symbol? form) (namespace form)) + (-> form name symbol) + + (c/and (seq? form) (= 'fn (first form)) (= '[%] (second form))) + (last form) + + :else form)) + form) + + (c/and (symbol? form) (namespace form)) + (-> form name symbol) + + :else form)) + +(defn describe + "returns an abbreviated description of the spec as data" + [spec] + (abbrev (form spec))) + +(defn with-gen + "Takes a spec and a no-arg, generator-returning fn and returns a version of that spec that uses that generator" + [spec gen-fn] + (with-gen* (specize spec) gen-fn)) + +(defn explain-data* [spec path via in x] + (when-let [probs (explain* (specize spec) path via in x)] + {::problems probs})) + +(defn explain-data + "Given a spec and a value x which ought to conform, returns nil if x + conforms, else a map with at least the key ::problems whose value is + a path->problem-map, where problem-map has at least :pred and :val + keys describing the predicate and the value that failed at that + path." + [spec x] + (explain-data* spec [] (if-let [name (spec-name spec)] [name] []) [] x)) + +(defn- explain-out + "prints an explanation to *out*." + [ed] + (if ed + (do + ;;(prn {:ed ed}) + (doseq [[path {:keys [pred val reason via in] :as prob}] (::problems ed)] + (when-not (empty? in) + (print "In:" in "")) + (print "val: ") + (pr val) + (print " fails") + (when-not (empty? via) + (print " spec:" (last via))) + (when-not (empty? path) + (print " at:" path)) + (print " predicate: ") + (pr pred) + (when reason (print ", " reason)) + (doseq [[k v] prob] + (when-not (#{:pred :val :reason :via :in} k) + (print "\n\t" k " ") + (pr v))) + (newline)) + (doseq [[k v] ed] + (when-not (#{::problems} k) + (print k " ") + (pr v) + (newline)))) + (println "Success!"))) + +(defn explain + "Given a spec and a value that fails to conform, prints an explanation to *out*." + [spec x] + (explain-out (explain-data spec x))) + +(defn explain-str + "Given a spec and a value that fails to conform, returns an explanation as a string." + [spec x] + (with-out-str (explain spec x))) + +(declare valid?) + +(defn- gensub + [spec overrides path rmap form] + ;;(prn {:spec spec :over overrides :path path :form form}) + (if-let [spec (specize spec)] + (if-let [g (c/or (get overrides path) (gen* spec overrides path rmap))] + (gen/such-that #(valid? spec %) g 100) + (throw (js/Error. (str "Unable to construct gen at: " path " for: " (abbrev form))))) + (throw (js/Error. (str "Unable to construct gen at: " path ", " (abbrev form) " can not be made a spec"))))) + +(defn gen + "Given a spec, returns the generator for it, or throws if none can + be constructed. Optionally an overrides map can be provided which + should map paths (vectors of keywords) to generators. These will be + used instead of the generators at those paths. Note that parent + generator (in the spec or overrides map) will supersede those of any + subtrees. A generator for a regex op must always return a + sequential collection (i.e. a generator for s/? should return either + an empty sequence/vector or a sequence/vector with one item in it)" + ([spec] (gen spec nil)) + ([spec overrides] (gensub spec overrides [] {::recursion-limit *recursion-limit*} spec))) + +(defn ^:skip-wiki def-impl + "Do not call this directly, use 'def'" + [k form spec] + (assert (c/and (named? k) (namespace k)) "k must be namespaced keyword/symbol") + (let [spec (if (c/or (spec? spec) (regex? spec) (get @registry-ref spec)) + spec + (spec-impl form spec nil nil))] + (swap! registry-ref assoc k spec) + k)) + +(defn registry + "returns the registry map" + [] + @registry-ref) + +(declare map-spec) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private fn-spec-roles [:args :ret :fn]) + +(defn- expect + "Returns nil if v conforms to spec, else throws ex-info with explain-data." + [spec v] + ) + +(defn- fn-specs? + "Fn-specs must include at least :args or :ret specs." + [m] + (c/or (:args m) (:ret m))) + +(defn fn-specs + "Returns :args/:ret/:fn map of specs for var or symbol v." + [v] + (let [s (->sym v) + reg (registry)] + (reduce + (fn [m role] + (assoc m role (get reg (fn-spec-sym s role)))) + {} + fn-spec-roles))) + +(defn- spec-checking-fn + [v f] + (let [conform! (fn [v role spec data args] + (let [conformed (conform spec data)] + (if (= ::invalid conformed) + (let [ed (assoc (explain-data* spec [role] [] [] data) + ::args args)] + (throw (ex-info + (str "Call to " v " did not conform to spec:\n" (with-out-str (explain-out ed))) + ed))) + conformed)))] + (c/fn + [& args] + (if *instrument-enabled* + (with-instrument-disabled + (let [specs (fn-specs v)] + (let [cargs (when (:args specs) (conform! v :args (:args specs) args args)) + ret (binding [*instrument-enabled* true] + (apply f args)) + cret (when (:ret specs) (conform! v :ret (:ret specs) ret args))] + (when (c/and (:args specs) (:ret specs) (:fn specs)) + (conform! v :fn (:fn specs) {:args cargs :ret cret} args)) + ret))) + (apply f args))))) + +;(defn- macroexpand-check +; [v args] +; (let [specs (fn-specs v)] +; (when-let [arg-spec (:args specs)] +; (when (= ::invalid (conform arg-spec args)) +; (let [ed (assoc (explain-data* arg-spec [:args] +; (if-let [name (spec-name arg-spec)] [name] []) [] args) +; ::args args)] +; (throw (js/Error. +; (str +; "Call to " (->sym v) " did not conform to spec:\n" +; (with-out-str (explain-out ed)))))))))) + + + +(defn- no-fn-specs + [v specs] + (ex-info (str "Fn at " v " is not spec'ed.") + {:var v :specs specs})) + +;(def ^:private instrumented-vars +; "Map for instrumented vars to :raw/:wrapped fns" +; (atom {})) +; +;(defn- ->var +; [s-or-v] +; (if (var? s-or-v) +; s-or-v +; (let [v (c/and (symbol? s-or-v) (resolve s-or-v))] +; (if (var? v) +; v +; (throw (js/Error. (str (pr-str s-or-v) " does not name a var"))))))) + +;(defn instrument +; "Instruments the var at v, a var or symbol, to check specs +;registered with fdef. Wraps the fn at v to check :args/:ret/:fn +;specs, if they exist, throwing an ex-info with explain-data if a +;check fails. Idempotent." +; [v] +; (let [v (->var v) +; specs (fn-specs v)] +; (if (fn-specs? specs) +; (locking instrumented-vars +; (let [{:keys [raw wrapped]} (get @instrumented-vars v) +; current @v] +; (when-not (= wrapped current) +; (let [checked (spec-checking-fn v current)] +; (alter-var-root v (constantly checked)) +; (swap! instrumented-vars assoc v {:raw current :wrapped checked})))) +; v) +; (throw (no-fn-specs v specs))))) +; +;(defn unstrument +; "Undoes instrument on the var at v, a var or symbol. Idempotent." +; [v] +; (let [v (->var v)] +; (locking instrumented-vars +; (when-let [{:keys [raw wrapped]} (get @instrumented-vars v)] +; (let [current @v] +; (when (= wrapped current) +; (alter-var-root v (constantly raw)))) +; (swap! instrumented-vars dissoc v)) +; v))) +; +;(defn speced-vars +; "Returns the set of vars whose namespace is in ns-syms AND +;whose vars have been speced with fdef. If no ns-syms are +;specified, return speced vars from all namespaces." +; [& ns-syms] +; (let [ns-match? (if (seq ns-syms) +; (set (map str ns-syms)) +; (constantly true))] +; (reduce-kv +; (fn [s k _] +; (if (c/and (symbol? k) +; (re-find #"\$(args|ret)$" (name k)) +; (ns-match? (namespace k))) +; (if-let [v (resolve (symbol (str/replace (str k) #"\$(args|ret)$" "")))] +; (conj s v) +; s) +; s)) +; #{} +; (registry)))) +; +;(defn instrument-ns +; "Call instrument for all speced-vars in namespaces named +;by ns-syms. Idempotent." +; [& ns-syms] +; (when (seq ns-syms) +; (locking instrumented-vars +; (doseq [v (apply speced-vars ns-syms)] +; (instrument v))))) +; +;(defn unstrument-ns +; "Call unstrument for all speced-vars in namespaces named +;by ns-syms. Idempotent." +; [& ns-syms] +; (when (seq ns-syms) +; (locking instrumented-vars +; (doseq [v (apply speced-vars ns-syms)] +; (unstrument v))))) +; +;(defn instrument-all +; "Call instrument for all speced-vars. Idempotent." +; [] +; (locking instrumented-vars +; (doseq [v (speced-vars)] +; (instrument v)))) +; +;(defn unstrument-all +; "Call unstrument for all speced-vars. Idempotent" +; [] +; (locking instrumented-vars +; (doseq [v (speced-vars)] +; (unstrument v)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; impl ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defn- recur-limit? [rmap id path k] + (c/and (> (get rmap id) (::recursion-limit rmap)) + (contains? (set path) k))) + +(defn- inck [m k] + (assoc m k (inc (c/or (get m k) 0)))) + +(defn- dt + ([pred x form] (dt pred x form nil)) + ([pred x form cpred?] + (if pred + (if-let [spec (the-spec pred)] + (conform spec x) + (if (ifn? pred) + (if cpred? + (pred x) + (if (pred x) x ::invalid)) + (throw (js/Error. (str (pr-str form) " is not a fn, expected predicate fn"))))) + x))) + +(defn valid? + "Helper function that returns true when x is valid for spec." + ([spec x] + (not= ::invalid (dt spec x ::unknown))) + ([spec x form] + (not= ::invalid (dt spec x form)))) + +(defn- explain-1 [form pred path via in v] + ;;(prn {:form form :pred pred :path path :in in :v v}) + (let [pred (maybe-spec pred)] + (if (spec? pred) + (explain* pred path (if-let [name (spec-name pred)] (conj via name) via) in v) + {path {:pred (abbrev form) :val v :via via :in in}}))) + +(defn ^:skip-wiki map-spec-impl + "Do not call this directly, use 'spec' with a map argument" + [{:keys [req-un opt-un pred-exprs opt-keys req-specs req req-keys opt-specs pred-forms opt gfn] + :as argm}] + (let [keys-pred (apply every-pred pred-exprs) + k->s (zipmap (concat req-keys opt-keys) (concat req-specs opt-specs)) + keys->specs #(c/or (k->s %) %) + id (random-uuid)] + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ m] + (if (keys-pred m) + (let [reg (registry)] + (loop [ret m, [k & ks :as keys] (c/keys m)] + (if keys + (if (contains? reg (keys->specs k)) + (let [v (get m k) + cv (conform (keys->specs k) v)] + (if (= cv ::invalid) + ::invalid + (recur (if (identical? cv v) ret (assoc ret k cv)) + ks))) + (recur ret ks)) + ret))) + ::invalid)) + (explain* [_ path via in x] + (if-not (map? x) + {path {:pred 'map? :val x :via via :in in}} + (let [reg (registry)] + (apply merge + (when-let [probs (->> (map (fn [pred form] (when-not (pred x) (abbrev form))) + pred-exprs pred-forms) + (keep identity) + seq)] + {path {:pred (vec probs) :val x :via via :in in}}) + (map (fn [[k v]] + (when-not (c/or (not (contains? reg (keys->specs k))) + (valid? (keys->specs k) v k)) + (explain-1 (keys->specs k) (keys->specs k) (conj path k) via (conj in k) v))) + (seq x)))))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [rmap (inck rmap id) + gen (fn [k s] (gensub s overrides (conj path k) rmap k)) + ogen (fn [k s] + (when-not (recur-limit? rmap id path k) + [k (gen/delay (gensub s overrides (conj path k) rmap k))])) + req-gens (map gen req-keys req-specs) + opt-gens (remove nil? (map ogen opt-keys opt-specs))] + (when (every? identity (concat req-gens opt-gens)) + (let [reqs (zipmap req-keys req-gens) + opts (into {} opt-gens)] + (gen/bind (gen/choose 0 (count opts)) + #(let [args (concat (seq reqs) (when (seq opts) (shuffle (seq opts))))] + (->> args + (take (c/+ % (count reqs))) + (apply concat) + (apply gen/hash-map))))))))) + (with-gen* [_ gfn] (map-spec-impl (assoc argm :gfn gfn))) + (describe* [_] (cons `keys + (cond-> [] + req (conj :req req) + opt (conj :opt opt) + req-un (conj :req-un req-un) + opt-un (conj :opt-un opt-un))))))) + + + + +(defn ^:skip-wiki spec-impl + "Do not call this directly, use 'spec'" + [form pred gfn cpred?] + (cond + (spec? pred) (cond-> pred gfn (with-gen gfn)) + (regex? pred) (regex-spec-impl pred gfn) + (named? pred) (cond-> (the-spec pred) gfn (with-gen gfn)) + :else + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ x] (dt pred x form cpred?)) + (explain* [_ path via in x] + (when (= ::invalid (dt pred x form cpred?)) + {path {:pred (abbrev form) :val x :via via :in in}})) + (gen* [_ _ _ _] (if gfn + (gfn) + (gen/gen-for-pred pred))) + (with-gen* [_ gfn] (spec-impl form pred gfn cpred?)) + (describe* [_] form)))) + +(defn ^:skip-wiki multi-spec-impl + "Do not call this directly, use 'multi-spec'" + ([form mmvar retag] (multi-spec-impl form mmvar retag nil)) + ([form mmvar retag gfn] + (let [id (random-uuid) + predx #(let [mm @mmvar] + (c/and (contains? (methods mm) + ((-dispatch-fn mm) %)) + (mm %))) + dval #((-dispatch-fn @mmvar) %) + tag (if (keyword? retag) + #(assoc %1 retag %2) + retag)] + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ x] (if-let [pred (predx x)] + (dt pred x form) + ::invalid)) + (explain* [_ path via in x] + (let [dv (dval x) + path (conj path dv)] + (if-let [pred (predx x)] + (explain-1 form pred path via in x) + {path {:pred form :val x :reason "no method" :via via :in in}}))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [gen (fn [[k f]] + (let [p (f nil)] + (let [rmap (inck rmap id)] + (when-not (recur-limit? rmap id path k) + (gen/delay + (gen/fmap + #(tag % k) + (gensub p overrides (conj path k) rmap (list 'method form k)))))))) + gs (->> (methods @mmvar) + (remove (fn [[k]] (= k ::invalid))) + (map gen) + (remove nil?))] + (when (every? identity gs) + (gen/one-of gs))))) + (with-gen* [_ gfn] (multi-spec-impl form mmvar retag gfn)) + (describe* [_] `(multi-spec ~form)))))) + +(defn ^:skip-wiki tuple-impl + "Do not call this directly, use 'tuple'" + ([forms preds] (tuple-impl forms preds nil)) + ([forms preds gfn] + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ x] + (if-not (c/and (vector? x) + (= (count x) (count preds))) + ::invalid + (loop [ret x, i 0] + (if (= i (count x)) + ret + (let [v (x i) + cv (dt (preds i) v (forms i))] + (if (= ::invalid cv) + ::invalid + (recur (if (identical? cv v) ret (assoc ret i cv)) + (inc i)))))))) + (explain* [_ path via in x] + (cond + (not (vector? x)) + {path {:pred 'vector? :val x :via via :in in}} + + (not= (count x) (count preds)) + {path {:pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}} + + :else + (apply merge + (map (fn [i form pred] + (let [v (x i)] + (when-not (valid? pred v) + (explain-1 form pred (conj path i) via (conj in i) v)))) + (range (count preds)) forms preds)))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [gen (fn [i p f] + (gensub p overrides (conj path i) rmap f)) + gs (map gen (range (count preds)) preds forms)] + (when (every? identity gs) + (apply gen/tuple gs))))) + (with-gen* [_ gfn] (tuple-impl forms preds gfn)) + (describe* [_] `(tuple ~@forms))))) + + +(defn ^:skip-wiki or-spec-impl + "Do not call this directly, use 'or'" + [keys forms preds gfn] + (let [id (random-uuid) + cform (fn [x] + (loop [i 0] + (if (< i (count preds)) + (let [pred (preds i)] + (let [ret (dt pred x (nth forms i))] + (if (= ::invalid ret) + (recur (inc i)) + [(keys i) ret]))) + ::invalid)))] + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ x] (cform x)) + (explain* [this path via in x] + (when-not (valid? this x) + (apply merge + (map (fn [k form pred] + (when-not (valid? pred x) + (explain-1 form pred (conj path k) via in x))) + keys forms preds)))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [gen (fn [k p f] + (let [rmap (inck rmap id)] + (when-not (recur-limit? rmap id path k) + (gen/delay + (gensub p overrides (conj path k) rmap f))))) + gs (remove nil? (map gen keys preds forms))] + (when-not (empty? gs) + (gen/one-of gs))))) + (with-gen* [_ gfn] (or-spec-impl keys forms preds gfn)) + (describe* [_] `(or ~@(mapcat vector keys forms)))))) + +(defn- and-preds [x preds forms] + (loop [ret x + [pred & preds] preds + [form & forms] forms] + (if pred + (let [nret (dt pred ret form)] + (if (= ::invalid nret) + ::invalid + ;;propagate conformed values + (recur nret preds forms))) + ret))) + +(defn- explain-pred-list + [forms preds path via in x] + (loop [ret x + [form & forms] forms + [pred & preds] preds] + (when pred + (let [nret (dt pred ret form)] + (if (not= ::invalid nret) + (recur nret forms preds) + (explain-1 form pred path via in ret)))))) + +(defn ^:skip-wiki and-spec-impl + "Do not call this directly, use 'and'" + [forms preds gfn] + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ x] (and-preds x preds forms)) + (explain* [_ path via in x] (explain-pred-list forms preds path via in x)) + (gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms)))) + (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) + (describe* [_] `(s/and ~@forms)))) + +;;;;;;;;;;;;;;;;;;;;;;; regex ;;;;;;;;;;;;;;;;;;; +;;See: +;; http://matt.might.net/articles/implementation-of-regular-expression-matching-in-scheme-with-derivatives/ +;; http://www.ccs.neu.edu/home/turon/re-deriv.pdf + +;;ctors +(defn- accept [x] {::op ::accept :ret x}) + +(defn- accept? [{:keys [::op]}] + (= ::accept op)) + +(defn- pcat* [{[p1 & pr :as ps] :ps, [k1 & kr :as ks] :ks, [f1 & fr :as forms] :forms, ret :ret, rep+ :rep+}] + (when (every? identity ps) + (if (accept? p1) + (let [rp (:ret p1) + ret (conj ret (if ks {k1 rp} rp))] + (if pr + (pcat* {:ps pr :ks kr :forms fr :ret ret}) + (accept ret))) + {::op ::pcat, :ps ps, :ret ret, :ks ks, :forms forms :rep+ rep+}))) + +(defn- pcat [& ps] (pcat* {:ps ps :ret []})) + +(defn ^:skip-wiki cat-impl + "Do not call this directly, use 'cat'" + [ks ps forms] + (pcat* {:ks ks, :ps ps, :forms forms, :ret {}})) + +(defn- rep* [p1 p2 ret splice form] + (when p1 + (let [r {::op ::rep, :p2 p2, :splice splice, :forms form :id (random-uuid)}] + (if (accept? p1) + (assoc r :p1 p2 :ret (conj ret (:ret p1))) + (assoc r :p1 p1, :ret ret))))) + +(defn ^:skip-wiki rep-impl + "Do not call this directly, use '*'" + [form p] (rep* p p [] false form)) + +(defn ^:skip-wiki rep+impl + "Do not call this directly, use '+'" + [form p] + (pcat* {:ps [p (rep* p p [] true form)] :forms `[~form (* ~form)] :ret [] :rep+ form})) + +(defn ^:skip-wiki amp-impl + "Do not call this directly, use '&'" + [re preds pred-forms] + {::op ::amp :p1 re :ps preds :forms pred-forms}) + +(defn- filter-alt [ps ks forms f] + (if (c/or ks forms) + (let [pks (->> (map vector ps + (c/or (seq ks) (repeat nil)) + (c/or (seq forms) (repeat nil))) + (filter #(-> % first f)))] + [(seq (map first pks)) (when ks (seq (map second pks))) (when forms (seq (map #(nth % 2) pks)))]) + [(seq (filter f ps)) ks forms])) + +(defn- alt* [ps ks forms] + (let [[[p1 & pr :as ps] [k1 :as ks] forms] (filter-alt ps ks forms identity)] + (when ps + (let [ret {::op ::alt, :ps ps, :ks ks :forms forms}] + (if (nil? pr) + (if k1 + (if (accept? p1) + (accept [k1 (:ret p1)]) + ret) + p1) + ret))))) + +(defn- alts [& ps] (alt* ps nil nil)) +(defn- alt2 [p1 p2] (if (c/and p1 p2) (alts p1 p2) (c/or p1 p2))) + +(defn ^:skip-wiki alt-impl + "Do not call this directly, use 'alt'" + [ks ps forms] (assoc (alt* ps ks forms) :id (random-uuid))) + +(defn ^:skip-wiki maybe-impl + "Do not call this directly, use '?'" + [p form] (alt* [p (accept ::nil)] nil [form ::nil])) + +(defn- noret? [p1 pret] + (c/or (= pret ::nil) + (c/and (#{::rep ::pcat} (::op (reg-resolve p1))) ;;hrm, shouldn't know these + (empty? pret)) + nil)) + +(declare preturn) + +(defn- accept-nil? [p] + (let [{:keys [::op ps p1 p2 forms] :as p} (reg-resolve p)] + (case op + ::accept true + nil nil + ::amp (c/and (accept-nil? p1) + (c/or (noret? p1 (preturn p1)) + (let [ret (-> (preturn p1) (and-preds ps (next forms)))] + (if (= ret ::invalid) + nil + ret)))) + ::rep (c/or (identical? p1 p2) (accept-nil? p1)) + ::pcat (every? accept-nil? ps) + ::alt (c/some accept-nil? ps)))) + +(declare add-ret) + +(defn- preturn [p] + (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms] :as p} (reg-resolve p)] + (case op + ::accept ret + nil nil + ::amp (let [pret (preturn p1)] + (if (noret? p1 pret) + ::nil + (and-preds pret ps forms))) + ::rep (add-ret p1 ret k) + ::pcat (add-ret p0 ret k) + ::alt (let [[[p0] [k0]] (filter-alt ps ks forms accept-nil?) + r (if (nil? p0) ::nil (preturn p0))] + (if k0 [k0 r] r))))) + +(defn- add-ret [p r k] + (let [{:keys [::op ps splice] :as p} (reg-resolve p) + prop #(let [ret (preturn p)] + (if (empty? ret) r ((if splice into conj) r (if k {k ret} ret))))] + (case op + nil r + (::alt ::accept ::amp) + (let [ret (preturn p)] + ;;(prn {:ret ret}) + (if (= ret ::nil) r (conj r (if k {k ret} ret)))) + + (::rep ::pcat) (prop)))) + +(defn- deriv + [p x] + (let [{[p0 & pr :as ps] :ps, [k0 & kr :as ks] :ks, :keys [::op p1 p2 ret splice forms] :as p} (reg-resolve p)] + (when p + (case op + ::accept nil + nil (let [ret (dt p x p)] + (when-not (= ::invalid ret) (accept ret))) + ::amp (when-let [p1 (deriv p1 x)] + (amp-impl p1 ps forms)) + ::pcat (alt2 (pcat* {:ps (cons (deriv p0 x) pr), :ks ks, :forms forms, :ret ret}) + (when (accept-nil? p0) (deriv (pcat* {:ps pr, :ks kr, :forms (next forms), :ret (add-ret p0 ret k0)}) x))) + ::alt (alt* (map #(deriv % x) ps) ks forms) + ::rep (alt2 (rep* (deriv p1 x) p2 ret splice forms) + (when (accept-nil? p1) (deriv (rep* p2 p2 (add-ret p1 ret nil) splice forms) x))))))) + +(defn- op-describe [p] + (let [{:keys [::op ps ks forms splice p1 rep+] :as p} (reg-resolve p)] + ;;(prn {:op op :ks ks :forms forms :p p}) + (when p + (case op + ::accept nil + nil p + ::amp (list* 'clojure.spec/& (op-describe p1) forms) + ::pcat (if rep+ + (list `+ rep+) + (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) (c/or (seq forms) (repeat nil))))) + ::alt (cons `alt (mapcat vector ks forms)) + ::rep (list (if splice `+ `*) forms))))) + +(defn- op-explain [form p path via in input] + ;;(prn {:form form :p p :path path :input input}) + (let [[x :as input] input + via (if-let [name (spec-name p)] (conj via name) via) + {:keys [::op ps ks forms splice p1 p2] :as p} (reg-resolve p) + insufficient (fn [path form] + {path {:reason "Insufficient input" + :pred (abbrev form) + :val () + :via via + :in in}})] + (when p + (case op + ::accept nil + nil (if (empty? input) + (insufficient path form) + (explain-1 form p path via in x)) + ::amp (if (empty? input) + (if (accept-nil? p1) + (explain-pred-list forms ps path via in (preturn p1)) + (insufficient path (op-describe p1))) + (if-let [p1 (deriv p1 x)] + (explain-pred-list forms ps path via in (preturn p1)) + (op-explain (op-describe p1) p1 path via in input))) + ::pcat (let [[pred k form] (->> (map vector + ps + (c/or (seq ks) (repeat nil)) + (c/or (seq forms) (repeat nil))) + (remove (fn [[p]] + (accept-nil? p))) + first) + path (if k (conj path k) path) + form (c/or form (op-describe pred))] + (if (c/and (empty? input) (not pred)) + (insufficient path form) + (op-explain form pred path via in input))) + ::alt (if (empty? input) + (insufficient path (op-describe p)) + (apply merge + (map (fn [k form pred] + (op-explain (c/or form (op-describe pred)) + pred + (if k (conj path k) path) + via + in + input)) + (c/or (seq ks) (repeat nil)) + (c/or (seq forms) (repeat nil)) + ps))) + ::rep (op-explain (if (identical? p1 p2) + forms + (op-describe p1)) + p1 path via in input))))) + +(defn- re-gen [p overrides path rmap f] + ;;(prn {:op op :ks ks :forms forms}) + (let [{:keys [::op ps ks p1 p2 forms splice ret id] :as p} (reg-resolve p) + rmap (if id (inck rmap id) rmap) + ggens (fn [ps ks forms] + (let [gen (fn [p k f] + ;;(prn {:k k :path path :rmap rmap :op op :id id}) + (when-not (c/and rmap id k (recur-limit? rmap id path k)) + (if id + (gen/delay (re-gen p overrides (if k (conj path k) path) rmap (c/or f p))) + (re-gen p overrides (if k (conj path k) path) rmap (c/or f p)))))] + (map gen ps (c/or (seq ks) (repeat nil)) (c/or (seq forms) (repeat nil)))))] + (c/or (when-let [g (get overrides path)] + (case op + (:accept nil) (gen/fmap vector g) + g)) + (when p + (case op + ::accept (if (= ret ::nil) + (gen/return []) + (gen/return [ret])) + nil (when-let [g (gensub p overrides path rmap f)] + (gen/fmap vector g)) + ::amp (re-gen p1 overrides path rmap (op-describe p1)) + ::pcat (let [gens (ggens ps ks forms)] + (when (every? identity gens) + (apply gen/cat gens))) + ::alt (let [gens (remove nil? (ggens ps ks forms))] + (when-not (empty? gens) + (gen/one-of gens))) + ::rep (if (recur-limit? rmap id [id] id) + (gen/return []) + (when-let [g (re-gen p2 overrides path rmap forms)] + (gen/fmap #(apply concat %) + (gen/vector g))))))))) + +(defn- re-conform [p [x & xs :as data]] + ;;(prn {:p p :x x :xs xs}) + (if (empty? data) + (if (accept-nil? p) + (let [ret (preturn p)] + (if (= ret ::nil) + nil + ret)) + ::invalid) + (if-let [dp (deriv p x)] + (recur dp xs) + ::invalid))) + +(defn- re-explain [path via in re input] + (loop [p re [x & xs :as data] input i 0] + ;;(prn {:p p :x x :xs xs :re re}) (prn) + (if (empty? data) + (if (accept-nil? p) + nil ;;success + (op-explain (op-describe p) p path via in nil)) + (if-let [dp (deriv p x)] + (recur dp xs (inc i)) + (if (accept? p) + {path {:reason "Extra input" + :pred (abbrev (op-describe re)) + :val data + :via via + :in (conj in i)}} + (c/or (op-explain (op-describe p) p path via (conj in i) (seq data)) + {path {:reason "Extra input" + :pred (abbrev (op-describe p)) + :val data + :via via + :in (conj in i)}})))))) + +(defn ^:skip-wiki regex-spec-impl + "Do not call this directly, use 'spec' with a regex op argument" + [re gfn] + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ x] + (if (c/or (nil? x) (coll? x)) + (re-conform re (seq x)) + ::invalid)) + (explain* [_ path via in x] + (if (c/or (nil? x) (coll? x)) + (re-explain path via in re (seq x)) + {path {:pred (abbrev (op-describe re)) :val x :via via :in in}})) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (re-gen re overrides path rmap (op-describe re)))) + (with-gen* [_ gfn] (regex-spec-impl re gfn)) + (describe* [_] (op-describe re)))) + +;;;;;;;;;;;;;;;;; HOFs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- call-valid? + [f specs args] + (let [cargs (conform (:args specs) args)] + (when-not (= cargs ::invalid) + (let [ret (apply f args) + cret (conform (:ret specs) ret)] + (c/and (not= cret ::invalid) + (if (:fn specs) + (valid? (:fn specs) {:args cargs :ret cret}) + true)))))) + +(defn- validate-fn + "returns f if valid, else smallest" + [f specs iters] + (let [g (gen (:args specs)) + prop (gen/for-all* [g] #(call-valid? f specs %))] + (let [ret (gen/quick-check iters prop)] + (if-let [[smallest] (-> ret :shrunk :smallest)] + smallest + f)))) + +(defn ^:skip-wiki fspec-impl + "Do not call this directly, use 'fspec'" + [argspec aform retspec rform fnspec fform gfn] + (assert (c/and argspec retspec)) + (let [specs {:args argspec :ret retspec :fn fnspec}] + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ f] (if (fn? f) + (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) + ::invalid)) + (explain* [_ path via in f] + (if (fn? f) + (let [args (validate-fn f specs 100)] + (if (identical? f args) ;;hrm, we might not be able to reproduce + nil + (let [ret (try (apply f args) (catch js/Error t t))] + (if (instance? js/Error ret) + ;;TODO add exception data + {path {:pred '(apply fn) :val args :reason (.-message ret) :via via :in in}} + + (let [cret (dt retspec ret rform)] + (if (= ::invalid cret) + (explain-1 rform retspec (conj path :ret) via in ret) + (when fnspec + (let [cargs (conform argspec args)] + (explain-1 fform fnspec (conj path :fn) via in {:args cargs :ret cret}))))))))) + {path {:pred 'fn? :val f :via via :in in}})) + (gen* [_ _ _ _] (if gfn + (gfn) + (when-not fnspec + (gen/return + (fn [& args] + (assert (valid? argspec args) (with-out-str (explain argspec args))) + (gen/generate (gen retspec))))))) + (with-gen* [_ gfn] (fspec-impl argspec aform retspec rform fnspec fform gfn)) + (describe* [_] `(fspec ~aform ~rform ~fform))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(s/def ::any (s/spec (constantly true) :gen gen/any)) +(s/def ::kvs->map (s/conformer #(zipmap (map ::k %) (map ::v %)))) + +(defn exercise + "generates a number (default 10) of values compatible with spec and maps conform over them, + returning a sequence of [val conformed-val] tuples. Optionally takes + a generator overrides map as per gen" + ([spec] (exercise spec 10)) + ([spec n] (exercise spec n nil)) + ([spec n overrides] + (map #(vector % (conform spec %)) (gen/sample (gen spec overrides) n)))) + +(defn coll-checker + "returns a predicate function that checks *coll-check-limit* items in a collection with pred" + [pred] + (let [check? #(valid? pred %)] + (fn [coll] + (c/or (nil? coll) + (c/and + (coll? coll) + (every? check? (take *coll-check-limit* coll))))))) + +(defn coll-gen + "returns a function of no args that returns a generator of + collections of items conforming to pred, with the same shape as + init-coll" + [pred init-coll] + (let [init (empty init-coll)] + (fn [] + (gen/fmap + #(if (vector? init) % (into init %)) + (gen/vector (gen pred)))))) From 3b51a7f72c25d75b74bd257822369e865a8c65d1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 16:01:28 -0400 Subject: [PATCH 1766/4033] need to thread env through for resolve to work --- src/main/clojure/cljs/spec.clj | 42 +++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/spec.clj b/src/main/clojure/cljs/spec.clj index 4dc014de9..01606eb9d 100644 --- a/src/main/clojure/cljs/spec.clj +++ b/src/main/clojure/cljs/spec.clj @@ -7,12 +7,14 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.spec - (:refer-clojure :exclude [+ * and or cat def keys]) - (:require [cljs.core :as c] + (:refer-clojure :exclude [+ * and or cat def keys resolve]) + (:require [cljs.analyzer.api :refer [resolve]] [clojure.walk :as walk] [cljs.spec.gen :as gen] [clojure.string :as str])) +(alias 'c 'clojure.core) + (defn- ->sym "Returns a symbol from a symbol or var" [x] @@ -30,18 +32,18 @@ (conj (walk/postwalk-replace {s '%} form) '[%] 'fn)) expr)) -(defn- res [form] +(defn- res [env form] (cond (keyword? form) form - (symbol? form) (c/or (-> form resolve ->sym) form) - (sequential? form) (walk/postwalk #(if (symbol? %) (res %) %) (unfn form)) + (symbol? form) (c/or (->> form (resolve env) ->sym) form) + (sequential? form) (walk/postwalk #(if (symbol? %) (res env %) %) (unfn form)) :else form)) (defmacro def "Given a namespace-qualified keyword or symbol k, and a spec, spec-name, predicate or regex-op makes an entry in the registry mapping k to the spec" [k spec-form] - `(cljs.spec/def-impl ~k '~(res spec-form) ~spec-form)) + `(cljs.spec/def-impl ~k '~(res &env spec-form) ~spec-form)) (defmacro spec "Takes a single predicate form, e.g. can be the name of a predicate, @@ -59,7 +61,7 @@ Returns a spec." [form & {:keys [gen]}] - `(cljs.spec/spec-impl '~(res form) ~form ~gen nil)) + `(cljs.spec/spec-impl '~(res &env form) ~form ~gen nil)) (defmacro multi-spec "Takes the name of a spec/predicate-returning multimethod and a @@ -88,7 +90,7 @@ though those values are not evident in the spec. " [mm retag] - `(cljs.spec/multi-spec-impl '~(res mm) (var ~mm) ~retag)) + `(cljs.spec/multi-spec-impl '~(res &env mm) (var ~mm) ~retag)) (defmacro keys "Creates and returns a map validating spec. :req and :opt are both @@ -174,19 +176,19 @@ Returns a spec that returns the conformed value. Successive conformed values propagate through rest of predicates." [& pred-forms] - `(cljs.spec/and-spec-impl '~(mapv res pred-forms) ~(vec pred-forms) nil)) + `(cljs.spec/and-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) (defmacro * "Returns a regex op that matches zero or more values matching pred. Produces a vector of matches iff there is at least one match" [pred-form] - `(cljs.spec/rep-impl '~(res pred-form) ~pred-form)) + `(cljs.spec/rep-impl '~(res &env pred-form) ~pred-form)) (defmacro + "Returns a regex op that matches one or more values matching pred. Produces a vector of matches" [pred-form] - `(cljs.spec/rep+impl '~(res pred-form) ~pred-form)) + `(cljs.spec/rep+impl '~(res &env pred-form) ~pred-form)) (defmacro ? "Returns a regex op that matches zero or one value matching @@ -248,7 +250,8 @@ Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator." [& {:keys [args ret fn gen]}] - `(cljs.spec/fspec-impl ~args '~(res args) ~ret '~(res ret) ~fn '~(res fn) ~gen)) + (let [env &env] + `(cljs.spec/fspec-impl ~args '~(res env args) ~ret '~(res env ret) ~fn '~(res env fn) ~gen))) (defmacro tuple "takes one or more preds and returns a spec for a tuple, a vector @@ -256,20 +259,20 @@ will be referred to in paths using its ordinal." [& preds] (assert (not (empty? preds))) - `(cljs.spec/tuple-impl '~(mapv res preds) ~(vec preds))) + `(cljs.spec/tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) (defn- ns-qualify "Qualify symbol s by resolving it or using the current *ns*." - [s] - (if-let [resolved (resolve s)] + [env s] + (if-let [resolved (resolve env s)] (->sym resolved) (if (namespace s) s (symbol (str (.name *ns*)) (str s))))) (defn- fn-spec-sym - [sym role] - (symbol (str (ns-qualify sym) "$" (name role)))) + [env sym role] + (symbol (str (ns-qualify env sym) "$" (name role)))) (defmacro fdef "Takes a symbol naming a function, and one or more of the following: @@ -304,11 +307,12 @@ :sym symbol?) :ret symbol?)" [fn-sym & {:keys [args ret fn] :as m}] - (let [qn (ns-qualify fn-sym)] + (let [env &env + qn (ns-qualify env fn-sym)] `(do ~@(reduce (c/fn [defns role] (if (contains? m role) - (let [s (fn-spec-sym qn (name role))] + (let [s (fn-spec-sym env qn (name role))] (conj defns `(cljs.spec/def '~s ~(get m role)))) defns)) [] [:args :ret :fn]) From e6ef0886dfe6789379233d2e24861d0c44c6689a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 16:21:14 -0400 Subject: [PATCH 1767/4033] add test.check dep to project.clj --- project.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/project.clj b/project.clj index 1140e3f92..0b4923e98 100644 --- a/project.clj +++ b/project.clj @@ -11,6 +11,7 @@ :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-beta1"] + [org.clojure/test.check "0.9.0" :scope "test"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] [com.google.javascript/closure-compiler "v20160315"] [org.mozilla/rhino "1.7R5"]] From 196c5cc513d84ab8743e29983419728a55e89d94 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 16:23:07 -0400 Subject: [PATCH 1768/4033] change cljs.spec.gen nses to cljs.spec.impl.gen to avoid clash with cljs.spec/gen --- src/main/cljs/cljs/spec/{ => impl}/gen.clj | 4 ++-- src/main/cljs/cljs/spec/{ => impl}/gen.cljs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/main/cljs/cljs/spec/{ => impl}/gen.clj (96%) rename src/main/cljs/cljs/spec/{ => impl}/gen.cljs (96%) diff --git a/src/main/cljs/cljs/spec/gen.clj b/src/main/cljs/cljs/spec/impl/gen.clj similarity index 96% rename from src/main/cljs/cljs/spec/gen.clj rename to src/main/cljs/cljs/spec/impl/gen.clj index 8847bd249..382b9e5d9 100644 --- a/src/main/cljs/cljs/spec/gen.clj +++ b/src/main/cljs/cljs/spec/impl/gen.clj @@ -6,7 +6,7 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software. -(ns cljs.spec.gen +(ns cljs.spec.impl.gen (:refer-clojure :exclude [delay]) (:require [cljs.core :as c])) @@ -21,7 +21,7 @@ generator that delegates to that, but delays creation until used." [& body] - `(cljs.spec.gen/delay-impl (c/delay ~@body))) + `(cljs.spec.impl.gen/delay-impl (c/delay ~@body))) (defmacro ^:skip-wiki lazy-combinator "Implementation macro, do not call directly." diff --git a/src/main/cljs/cljs/spec/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs similarity index 96% rename from src/main/cljs/cljs/spec/gen.cljs rename to src/main/cljs/cljs/spec/impl/gen.cljs index 8b01f0de0..b0991a009 100644 --- a/src/main/cljs/cljs/spec/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -6,11 +6,11 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software. -(ns cljs.spec.gen +(ns cljs.spec.impl.gen (:refer-clojure :exclude [boolean cat hash-map list map not-empty set vector char double int keyword symbol string uuid delay]) (:require-macros [cljs.core :as c] - [cljs.spec.gen :as gen :refer [dynaload lazy-combinators lazy-prims]]) + [cljs.spec.impl.gen :as gen :refer [dynaload lazy-combinators lazy-prims]]) (:require [cljs.core :as c])) (def ^:private quick-check-ref @@ -111,8 +111,8 @@ gen-builtins (comment (require 'clojure.test.check) (require 'clojure.test.check.properties) - (require 'cljs.spec.gen) - (in-ns 'cljs.spec.gen) + (require 'cljs.spec.impl.gen) + (in-ns 'cljs.spec.impl.gen) ;; combinators, see call to lazy-combinators above for complete list (generate (one-of [(gen-for-pred integer?) (gen-for-pred string?)])) From 74af7c9f2826e486dfd466975a01b81a171de438 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 16:23:41 -0400 Subject: [PATCH 1769/4033] put spec nses under cljs directory --- src/main/{clojure => cljs}/cljs/spec.clj | 0 src/main/{clojure => cljs}/cljs/spec.cljs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/main/{clojure => cljs}/cljs/spec.clj (100%) rename src/main/{clojure => cljs}/cljs/spec.cljs (99%) diff --git a/src/main/clojure/cljs/spec.clj b/src/main/cljs/cljs/spec.clj similarity index 100% rename from src/main/clojure/cljs/spec.clj rename to src/main/cljs/cljs/spec.clj diff --git a/src/main/clojure/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs similarity index 99% rename from src/main/clojure/cljs/spec.cljs rename to src/main/cljs/cljs/spec.cljs index a3e92f0cb..fa097413e 100644 --- a/src/main/clojure/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -12,7 +12,7 @@ [cljs.spec :as s]) (:require [cljs.core :as c] [clojure.walk :as walk] - [cljs.spec.gen :as gen] + [cljs.spec.impl.gen :as gen] [clojure.string :as str])) (def ^:dynamic *recursion-limit* @@ -271,7 +271,7 @@ (c/fn [& args] (if *instrument-enabled* - (with-instrument-disabled + (s/with-instrument-disabled (let [specs (fn-specs v)] (let [cargs (when (:args specs) (conform! v :args (:args specs) args args)) ret (binding [*instrument-enabled* true] From efd7f141876032956a4e67d89dfbd7388c05c7ed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 16:29:28 -0400 Subject: [PATCH 1770/4033] missing cljs.spec.gen -> cljs.spec.gen.impl, avoid weird resolution bug for now --- src/main/cljs/cljs/spec.clj | 2 +- src/main/cljs/cljs/spec.cljs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.clj index 01606eb9d..e15af665a 100644 --- a/src/main/cljs/cljs/spec.clj +++ b/src/main/cljs/cljs/spec.clj @@ -10,7 +10,7 @@ (:refer-clojure :exclude [+ * and or cat def keys resolve]) (:require [cljs.analyzer.api :refer [resolve]] [clojure.walk :as walk] - [cljs.spec.gen :as gen] + [cljs.spec.impl.gen :as gen] [clojure.string :as str])) (alias 'c 'clojure.core) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index fa097413e..567aae4f2 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1070,8 +1070,8 @@ (describe* [_] `(fspec ~aform ~rform ~fform))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(s/def ::any (s/spec (constantly true) :gen gen/any)) -(s/def ::kvs->map (s/conformer #(zipmap (map ::k %) (map ::v %)))) +(cljs.spec/def ::any (cljs.spec/spec (constantly true) :gen gen/any)) +(cljs.spec/def ::kvs->map (cljs.spec/conformer #(zipmap (map ::k %) (map ::v %)))) (defn exercise "generates a number (default 10) of values compatible with spec and maps conform over them, From eda670e00379703a03977772c77af1dd730e8103 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 16:38:06 -0400 Subject: [PATCH 1771/4033] fix some bad res missing &env parameter --- src/main/cljs/cljs/spec.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.clj index e15af665a..8d4a88dbf 100644 --- a/src/main/cljs/cljs/spec.clj +++ b/src/main/cljs/cljs/spec.clj @@ -164,7 +164,7 @@ (let [pairs (partition 2 key-pred-forms) keys (mapv first pairs) pred-forms (mapv second pairs) - pf (mapv res pred-forms)] + pf (mapv #(res &env %) pred-forms)] (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/or-spec-impl ~keys '~pf ~pred-forms nil))) @@ -207,7 +207,7 @@ (let [pairs (partition 2 key-pred-forms) keys (mapv first pairs) pred-forms (mapv second pairs) - pf (mapv res pred-forms)] + pf (mapv #(res &env %) pred-forms)] (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/alt-impl ~keys ~pred-forms '~pf))) @@ -222,7 +222,7 @@ (let [pairs (partition 2 key-pred-forms) keys (mapv first pairs) pred-forms (mapv second pairs) - pf (mapv res pred-forms)] + pf (mapv #(res &env %) pred-forms)] ;;(prn key-pred-forms) (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/cat-impl ~keys ~pred-forms '~pf))) From 303cab2a59e9e0d92eea6b4425511a043605cf96 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 16:47:14 -0400 Subject: [PATCH 1772/4033] ClojureScript resolved vars are maps, just return the :name in cljs.spec/->sym --- src/main/cljs/cljs/spec.clj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.clj index 8d4a88dbf..45f04d6aa 100644 --- a/src/main/cljs/cljs/spec.clj +++ b/src/main/cljs/cljs/spec.clj @@ -18,10 +18,8 @@ (defn- ->sym "Returns a symbol from a symbol or var" [x] - (if (var? x) - (let [^clojure.lang.Var v x] - (symbol (str (.name (.ns v))) - (str (.sym v)))) + (if (map? x) + (:name x) x)) (defn- unfn [expr] From 2da805d608950bc87a572685ffa5d482e1d9ef8d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 16:50:47 -0400 Subject: [PATCH 1773/4033] fix cljs.spec/explain-out so we don't get extra newlines --- src/main/cljs/cljs/spec.cljs | 51 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 567aae4f2..241f78384 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -155,31 +155,32 @@ "prints an explanation to *out*." [ed] (if ed - (do - ;;(prn {:ed ed}) - (doseq [[path {:keys [pred val reason via in] :as prob}] (::problems ed)] - (when-not (empty? in) - (print "In:" in "")) - (print "val: ") - (pr val) - (print " fails") - (when-not (empty? via) - (print " spec:" (last via))) - (when-not (empty? path) - (print " at:" path)) - (print " predicate: ") - (pr pred) - (when reason (print ", " reason)) - (doseq [[k v] prob] - (when-not (#{:pred :val :reason :via :in} k) - (print "\n\t" k " ") - (pr v))) - (newline)) - (doseq [[k v] ed] - (when-not (#{::problems} k) - (print k " ") - (pr v) - (newline)))) + (print + (with-out-str + ;;(prn {:ed ed}) + (doseq [[path {:keys [pred val reason via in] :as prob}] (::problems ed)] + (when-not (empty? in) + (print "In:" in "")) + (print "val: ") + (pr val) + (print " fails") + (when-not (empty? via) + (print " spec:" (last via))) + (when-not (empty? path) + (print " at:" path)) + (print " predicate: ") + (pr pred) + (when reason (print ", " reason)) + (doseq [[k v] prob] + (when-not (#{:pred :val :reason :via :in} k) + (print "\n\t" k " ") + (pr v))) + (newline)) + (doseq [[k v] ed] + (when-not (#{::problems} k) + (print k " ") + (pr v) + (newline))))) (println "Success!"))) (defn explain From 7f0b3f8cf08f57329d6399261949a01e12227c9b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 17:01:53 -0400 Subject: [PATCH 1774/4033] fix bad res call in cljs.spec/keys --- src/main/cljs/cljs/spec.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.clj index 45f04d6aa..bb1027c08 100644 --- a/src/main/cljs/cljs/spec.clj +++ b/src/main/cljs/cljs/spec.clj @@ -141,7 +141,7 @@ pred-exprs [`map?] pred-exprs (into pred-exprs (parse-req req identity)) pred-exprs (into pred-exprs (parse-req req-un unk)) - pred-forms (walk/postwalk res pred-exprs)] + pred-forms (walk/postwalk #(res &env %) pred-exprs)] ;; `(map-spec-impl ~req-keys '~req ~opt '~pred-forms ~pred-exprs ~gen) `(cljs.spec/map-spec-impl {:req '~req :opt '~opt :req-un '~req-un :opt-un '~opt-un :req-keys '~req-keys :req-specs '~req-specs From 7bae582a6a2eca2b358395b50fbc4a35869a7c71 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 17:43:24 -0400 Subject: [PATCH 1775/4033] working instrument --- src/main/cljs/cljs/spec.clj | 12 ++++++++ src/main/cljs/cljs/spec.cljs | 60 +++++++++++++++++------------------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.clj index bb1027c08..4ec1bc9d7 100644 --- a/src/main/cljs/cljs/spec.clj +++ b/src/main/cljs/cljs/spec.clj @@ -353,3 +353,15 @@ "Returns a spec for a map whose keys satisfy kpred and vals satisfy vpred." [kpred vpred] `(and (coll-of (tuple ~kpred ~vpred) {}) map?)) + +(defmacro instrument + "Instruments the var at v, a var or symbol, to check specs + registered with fdef. Wraps the fn at v to check :args/:ret/:fn + specs, if they exist, throwing an ex-info with explain-data if a + check fails. Idempotent." + [v] + (let [v (if-not (seq? v) (list 'var v) v) + sym (second v)] + `(when-let [checked# (cljs.spec/instrument* ~v)] + (set! ~sym checked#) + ~v))) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 241f78384..b17eb7868 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -242,11 +242,22 @@ [spec v] ) +(defn- ->sym + "Returns a symbol from a symbol or var" + [x] + (if (var? x) + (.-sym x) + x)) + (defn- fn-specs? "Fn-specs must include at least :args or :ret specs." [m] (c/or (:args m) (:ret m))) +(defn- fn-spec-sym + [sym role] + (symbol (str sym "$" (name role)))) + (defn fn-specs "Returns :args/:ret/:fn map of specs for var or symbol v." [v] @@ -303,38 +314,23 @@ (ex-info (str "Fn at " v " is not spec'ed.") {:var v :specs specs})) -;(def ^:private instrumented-vars -; "Map for instrumented vars to :raw/:wrapped fns" -; (atom {})) -; -;(defn- ->var -; [s-or-v] -; (if (var? s-or-v) -; s-or-v -; (let [v (c/and (symbol? s-or-v) (resolve s-or-v))] -; (if (var? v) -; v -; (throw (js/Error. (str (pr-str s-or-v) " does not name a var"))))))) - -;(defn instrument -; "Instruments the var at v, a var or symbol, to check specs -;registered with fdef. Wraps the fn at v to check :args/:ret/:fn -;specs, if they exist, throwing an ex-info with explain-data if a -;check fails. Idempotent." -; [v] -; (let [v (->var v) -; specs (fn-specs v)] -; (if (fn-specs? specs) -; (locking instrumented-vars -; (let [{:keys [raw wrapped]} (get @instrumented-vars v) -; current @v] -; (when-not (= wrapped current) -; (let [checked (spec-checking-fn v current)] -; (alter-var-root v (constantly checked)) -; (swap! instrumented-vars assoc v {:raw current :wrapped checked})))) -; v) -; (throw (no-fn-specs v specs))))) -; +(def ^:private instrumented-vars + "Map for instrumented vars to :raw/:wrapped fns" + (atom {})) + +(defn instrument* + [v] + (let [specs (fn-specs v)] + (if (fn-specs? specs) + (locking instrumented-vars + (let [{:keys [raw wrapped]} (get @instrumented-vars v) + current @v] + (when-not (= wrapped current) + (let [checked (spec-checking-fn v current)] + (swap! instrumented-vars assoc v {:raw current :wrapped checked}) + checked)))) + (throw (no-fn-specs v specs))))) + ;(defn unstrument ; "Undoes instrument on the var at v, a var or symbol. Idempotent." ; [v] From afa452b46ce82ba17448cd4a3d32a2806baffd29 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 17:49:36 -0400 Subject: [PATCH 1776/4033] working unstrument --- src/main/cljs/cljs/spec.clj | 8 ++++++++ src/main/cljs/cljs/spec.cljs | 22 ++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.clj index 4ec1bc9d7..8b4b71fdd 100644 --- a/src/main/cljs/cljs/spec.clj +++ b/src/main/cljs/cljs/spec.clj @@ -365,3 +365,11 @@ `(when-let [checked# (cljs.spec/instrument* ~v)] (set! ~sym checked#) ~v))) + +(defmacro unstrument + "Undoes instrument on the var at v, a var or symbol. Idempotent."[v] + (let [v (if-not (seq? v) (list 'var v) v) + sym (second v)] + `(when-let [raw# (cljs.spec/unstrument* ~v)] + (set! ~sym raw#) + ~v))) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index b17eb7868..3dd3f728b 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -331,18 +331,16 @@ checked)))) (throw (no-fn-specs v specs))))) -;(defn unstrument -; "Undoes instrument on the var at v, a var or symbol. Idempotent." -; [v] -; (let [v (->var v)] -; (locking instrumented-vars -; (when-let [{:keys [raw wrapped]} (get @instrumented-vars v)] -; (let [current @v] -; (when (= wrapped current) -; (alter-var-root v (constantly raw)))) -; (swap! instrumented-vars dissoc v)) -; v))) -; +(defn unstrument* + [v] + (locking instrumented-vars + (when-let [{:keys [raw wrapped]} (get @instrumented-vars v)] + (let [current @v] + (when (= wrapped current) + raw)) + (swap! instrumented-vars dissoc v)) + v)) + ;(defn speced-vars ; "Returns the set of vars whose namespace is in ns-syms AND ;whose vars have been speced with fdef. If no ns-syms are From abc47fc2e3b58e140eac0d306deb018fa66691b2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 18:25:43 -0400 Subject: [PATCH 1777/4033] implement cljs.spec/speced-vars --- src/main/cljs/cljs/spec.clj | 18 ++++++++++++++++++ src/main/cljs/cljs/spec.cljs | 20 -------------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.clj index 8b4b71fdd..9bff5e733 100644 --- a/src/main/cljs/cljs/spec.clj +++ b/src/main/cljs/cljs/spec.clj @@ -272,6 +272,23 @@ [env sym role] (symbol (str (ns-qualify env sym) "$" (name role)))) +(def ^:private _speced_vars (atom #{})) + +(defmacro speced-vars + "Returns the set of vars whose namespace is in ns-syms AND +whose vars have been speced with fdef. If no ns-syms are +specified, return speced vars from all namespaces." + [& ns-syms] + (let [ns-match? (if (seq ns-syms) + (set ns-syms) + (constantly true))] + (reduce + (fn [ret sym] + (if (ns-match? (namespace sym)) + (conj ret (list 'var sym)) + ret)) + #{} @_speced_vars))) + (defmacro fdef "Takes a symbol naming a function, and one or more of the following: @@ -305,6 +322,7 @@ :sym symbol?) :ret symbol?)" [fn-sym & {:keys [args ret fn] :as m}] + (swap! _speced_vars conj (:name (resolve &env fn-sym))) (let [env &env qn (ns-qualify env fn-sym)] `(do ~@(reduce diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 3dd3f728b..63155c820 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -341,26 +341,6 @@ (swap! instrumented-vars dissoc v)) v)) -;(defn speced-vars -; "Returns the set of vars whose namespace is in ns-syms AND -;whose vars have been speced with fdef. If no ns-syms are -;specified, return speced vars from all namespaces." -; [& ns-syms] -; (let [ns-match? (if (seq ns-syms) -; (set (map str ns-syms)) -; (constantly true))] -; (reduce-kv -; (fn [s k _] -; (if (c/and (symbol? k) -; (re-find #"\$(args|ret)$" (name k)) -; (ns-match? (namespace k))) -; (if-let [v (resolve (symbol (str/replace (str k) #"\$(args|ret)$" "")))] -; (conj s v) -; s) -; s)) -; #{} -; (registry)))) -; ;(defn instrument-ns ; "Call instrument for all speced-vars in namespaces named ;by ns-syms. Idempotent." From a28b4ef7391f3cd8261fea938d5276641ae0f427 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 18:33:18 -0400 Subject: [PATCH 1778/4033] add remaining instrument/unstrument helpers --- src/main/cljs/cljs/spec.clj | 50 +++++++++++++++++++++++++++++------- src/main/cljs/cljs/spec.cljs | 32 ----------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.clj index 9bff5e733..8c9827b5a 100644 --- a/src/main/cljs/cljs/spec.clj +++ b/src/main/cljs/cljs/spec.clj @@ -274,20 +274,26 @@ (def ^:private _speced_vars (atom #{})) +(defn speced-vars* + ([] + (speced-vars* nil)) + ([ns-syms] + (let [ns-match? (if (seq ns-syms) + (set ns-syms) + (constantly true))] + (reduce + (fn [ret sym] + (if (ns-match? (namespace sym)) + (conj ret (list 'var sym)) + ret)) + #{} @_speced_vars)))) + (defmacro speced-vars "Returns the set of vars whose namespace is in ns-syms AND whose vars have been speced with fdef. If no ns-syms are specified, return speced vars from all namespaces." [& ns-syms] - (let [ns-match? (if (seq ns-syms) - (set ns-syms) - (constantly true))] - (reduce - (fn [ret sym] - (if (ns-match? (namespace sym)) - (conj ret (list 'var sym)) - ret)) - #{} @_speced_vars))) + (speced-vars* ns-syms)) (defmacro fdef "Takes a symbol naming a function, and one or more of the following: @@ -391,3 +397,29 @@ specified, return speced vars from all namespaces." `(when-let [raw# (cljs.spec/unstrument* ~v)] (set! ~sym raw#) ~v))) + +(defmacro instrument-ns + "Call instrument for all speced-vars in namespaces named +by ns-syms. Idempotent." + [& ns-syms] + `(do + ~@(map #(list 'cljs.spec/instrument %) (speced-vars* ns-syms)))) + +(defmacro unstrument-ns + "Call unstrument for all speced-vars in namespaces named +by ns-syms. Idempotent." + [& ns-syms] + `(do + ~@(map #(list 'cljs.spec/unstrument %) (speced-vars* ns-syms)))) + +(defmacro instrument-all + "Call instrument for all speced-vars. Idempotent." + [] + `(do + ~@(map #(list 'cljs.spec/instrument %) (speced-vars*)))) + +(defmacro unstrument-all + "Call unstrument for all speced-vars. Idempotent" + [] + `(do + ~@(map #(list 'cljs.spec/unstrument %) (speced-vars*)))) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 63155c820..a406537de 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -341,38 +341,6 @@ (swap! instrumented-vars dissoc v)) v)) -;(defn instrument-ns -; "Call instrument for all speced-vars in namespaces named -;by ns-syms. Idempotent." -; [& ns-syms] -; (when (seq ns-syms) -; (locking instrumented-vars -; (doseq [v (apply speced-vars ns-syms)] -; (instrument v))))) -; -;(defn unstrument-ns -; "Call unstrument for all speced-vars in namespaces named -;by ns-syms. Idempotent." -; [& ns-syms] -; (when (seq ns-syms) -; (locking instrumented-vars -; (doseq [v (apply speced-vars ns-syms)] -; (unstrument v))))) -; -;(defn instrument-all -; "Call instrument for all speced-vars. Idempotent." -; [] -; (locking instrumented-vars -; (doseq [v (speced-vars)] -; (instrument v)))) -; -;(defn unstrument-all -; "Call unstrument for all speced-vars. Idempotent" -; [] -; (locking instrumented-vars -; (doseq [v (speced-vars)] -; (unstrument v)))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; impl ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- recur-limit? [rmap id path k] (c/and (> (get rmap id) (::recursion-limit rmap)) From d35f871a5e648baa58e85853a69c651580924ad1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 18:49:58 -0400 Subject: [PATCH 1779/4033] REPL support for spec --- src/main/cljs/cljs/repl.cljs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index bddef4430..6b6fdb704 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -7,9 +7,10 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.repl - (:require-macros cljs.repl)) + (:require-macros cljs.repl) + (:require [cljs.spec :as spec])) -(defn print-doc [m] +(defn print-doc [{n :ns nm :name :as m}] (println "-------------------------") (println (str (when-let [ns (:ns m)] (str ns "/")) (:name m))) (when (:protocol m) @@ -46,4 +47,11 @@ (println " " name) (println " " arglists) (when doc - (println " " doc))))))) + (println " " doc)))) + (when n + (when-let [specs (spec/fn-specs (symbol (str (ns-name n)) (name nm)))] + (println "Spec") + (run! (fn [[role spec]] + (when (and spec (not (= spec ::spec/unknown))) + (println " " (str (name role) ":") (spec/describe spec)))) + specs)))))) From 93134bc4be71827856aba5bff483bb2e0ddd54f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 18:57:31 -0400 Subject: [PATCH 1780/4033] .clj -> cljc --- src/main/cljs/cljs/{spec.clj => spec.cljc} | 0 src/main/cljs/cljs/spec/impl/{gen.clj => gen.cljc} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/main/cljs/cljs/{spec.clj => spec.cljc} (100%) rename src/main/cljs/cljs/spec/impl/{gen.clj => gen.cljc} (100%) diff --git a/src/main/cljs/cljs/spec.clj b/src/main/cljs/cljs/spec.cljc similarity index 100% rename from src/main/cljs/cljs/spec.clj rename to src/main/cljs/cljs/spec.cljc diff --git a/src/main/cljs/cljs/spec/impl/gen.clj b/src/main/cljs/cljs/spec/impl/gen.cljc similarity index 100% rename from src/main/cljs/cljs/spec/impl/gen.clj rename to src/main/cljs/cljs/spec/impl/gen.cljc From 6544a51766d51de78b907046c8cd7bdcb90aae76 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 28 May 2016 19:41:42 -0400 Subject: [PATCH 1781/4033] 1.9 --- script/build | 2 +- script/revision | 2 +- script/uberjar | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/script/build b/script/build index 9c2c87bfc..118091507 100755 --- a/script/build +++ b/script/build @@ -19,7 +19,7 @@ POM_FILE="pom.xml" # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="8" +MINOR="9" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so diff --git a/script/revision b/script/revision index b098ecca2..4b5e21159 100755 --- a/script/revision +++ b/script/revision @@ -10,7 +10,7 @@ set -ex # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="8" +MINOR="9" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so diff --git a/script/uberjar b/script/uberjar index d1a4267fe..57ab4e632 100755 --- a/script/uberjar +++ b/script/uberjar @@ -13,7 +13,7 @@ set -ex # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="8" +MINOR="9" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so From 8477f19dcf67a8f305b46f2fd2e793586e027263 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 28 May 2016 20:45:04 -0400 Subject: [PATCH 1782/4033] CLJS-1652: Self-host: Avoid alias so cljs.spec loadable --- src/main/cljs/cljs/spec.cljc | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 8c9827b5a..e69514767 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -13,8 +13,6 @@ [cljs.spec.impl.gen :as gen] [clojure.string :as str])) -(alias 'c 'clojure.core) - (defn- ->sym "Returns a symbol from a symbol or var" [x] @@ -23,7 +21,7 @@ x)) (defn- unfn [expr] - (if (c/and (seq? expr) + (if (clojure.core/and (seq? expr) (symbol? (first expr)) (= "fn*" (name (first expr)))) (let [[[s] & form] (rest expr)] @@ -33,7 +31,7 @@ (defn- res [env form] (cond (keyword? form) form - (symbol? form) (c/or (->> form (resolve env) ->sym) form) + (symbol? form) (clojure.core/or (->> form (resolve env) ->sym) form) (sequential? form) (walk/postwalk #(if (symbol? %) (res env %) %) (unfn form)) :else form)) @@ -122,7 +120,7 @@ (let [unk #(-> % name keyword) req-keys (filterv keyword? (flatten req)) req-un-specs (filterv keyword? (flatten req-un)) - _ (assert (every? #(c/and (keyword? %) (namespace %)) (concat req-keys req-un-specs opt opt-un)) + _ (assert (every? #(clojure.core/and (keyword? %) (namespace %)) (concat req-keys req-un-specs opt opt-un)) "all keys must be namespace-qualified keywords") req-specs (into req-keys req-un-specs) req-keys (into req-keys (map unk req-un-specs)) @@ -163,7 +161,7 @@ keys (mapv first pairs) pred-forms (mapv second pairs) pf (mapv #(res &env %) pred-forms)] - (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") + (assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/or-spec-impl ~keys '~pf ~pred-forms nil))) (defmacro and @@ -206,7 +204,7 @@ keys (mapv first pairs) pred-forms (mapv second pairs) pf (mapv #(res &env %) pred-forms)] - (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") + (assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/alt-impl ~keys ~pred-forms '~pf))) (defmacro cat @@ -222,7 +220,7 @@ pred-forms (mapv second pairs) pf (mapv #(res &env %) pred-forms)] ;;(prn key-pred-forms) - (assert (c/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") + (assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/cat-impl ~keys ~pred-forms '~pf))) (defmacro & @@ -332,7 +330,7 @@ specified, return speced vars from all namespaces." (let [env &env qn (ns-qualify env fn-sym)] `(do ~@(reduce - (c/fn [defns role] + (clojure.core/fn [defns role] (if (contains? m role) (let [s (fn-spec-sym env qn (name role))] (conj defns `(cljs.spec/def '~s ~(get m role)))) From b2e3a46c57156011932bd84d7aef1b941e01a2f6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 29 May 2016 13:16:29 -0400 Subject: [PATCH 1783/4033] need to bind cljs.analyzer/*unchecked-if* in the REPL --- src/main/clojure/cljs/repl.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 02abaa03e..2f5a25bd0 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -807,7 +807,8 @@ (env/with-compiler-env (or compiler-env (env/default-compiler-env opts)) (when (:source-map opts) (.start (Thread. (bound-fn [] (read-source-map "cljs/core.aot.js"))))) - (binding [*err* (if bind-err + (binding [ana/*unchecked-if* false + *err* (if bind-err (cond-> *out* (not (instance? PrintWriter *out*)) (PrintWriter.)) *err*) From f45a0b854c60be20da43bd38134327ece0419b16 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 May 2016 19:08:48 -0400 Subject: [PATCH 1784/4033] copy over spec testing support --- src/main/cljs/cljs/spec/test.cljc | 28 +++++++ src/main/cljs/cljs/spec/test.cljs | 131 ++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 src/main/cljs/cljs/spec/test.cljc create mode 100644 src/main/cljs/cljs/spec/test.cljs diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc new file mode 100644 index 000000000..57b35f328 --- /dev/null +++ b/src/main/cljs/cljs/spec/test.cljc @@ -0,0 +1,28 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns cljs.spec.test + (:require + [cljs.spec :as spec] + [cljs.spec.impl.gen :as gen])) + +(defn run-tests + "Like run-all-tests, but scoped to specific namespaces, or to +*ns* if no ns-sym are specified." + [& ns-syms] + (if (seq ns-syms) + (run-var-tests (->> (apply spec/speced-vars ns-syms) + (filter (fn [v] (:args (spec/fn-specs v)))))) + (run-tests (.name ^clojure.lang.Namespace *ns*)))) + +(defn run-all-tests + "Like clojure.test/run-all-tests, but runs test.check tests +for all speced vars. Prints per-test results to *out*, and +returns a map with :test,:pass,:fail, and :error counts." + [] + (run-var-tests (spec/speced-vars))) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs new file mode 100644 index 000000000..70652c9d6 --- /dev/null +++ b/src/main/cljs/cljs/spec/test.cljs @@ -0,0 +1,131 @@ +; 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.spec.test + (:require-macros [cljs.spec.test :as st]) + (:require + [cljs.spec :as spec] + [cljs.spec.impl.gen :as gen])) + +;; wrap spec/explain-data until specs always return nil for ok data +(defn- explain-data* + [spec v] + (when-not (spec/valid? spec v nil) + (spec/explain-data spec v))) + +;; wrap and unwrap spec failure data in an exception so that +;; quick-check will treat it as a failure. +(defn- wrap-failing + [explain-data step] + (ex-info "Wrapper" {::check-call (assoc explain-data :failed-on step)})) + +(defn- unwrap-failing + [ret] + (let [ret (if-let [explain (-> ret :result ex-data ::check-call)] + (assoc ret :result explain) + ret)] + (if-let [shrunk-explain (-> ret :shrunk :result ex-data ::check-call)] + (assoc-in ret [:shrunk :result] shrunk-explain) + ret))) + +(defn- check-call + "Returns true if call passes specs, otherwise *returns* an exception +with explain-data plus a :failed-on key under ::check-call." + [f specs args] + (let [cargs (when (:args specs) (spec/conform (:args specs) args))] + (if (= cargs ::spec/invalid) + (wrap-failing (explain-data* (:args specs) args) :args) + (let [ret (apply f args) + cret (when (:ret specs) (spec/conform (:ret specs) ret))] + (if (= cret ::spec/invalid) + (wrap-failing (explain-data* (:ret specs) ret) :ret) + (if (and (:args specs) (:ret specs) (:fn specs)) + (if (spec/valid? (:fn specs) {:args cargs :ret cret}) + true + (wrap-failing (explain-data* (:fn specs) {:args cargs :ret cret}) :fn)) + true)))))) + +(defn check-fn + "Check a function using provided specs and test.check. +Same options and return as check-var" + [f specs + & {:keys [num-tests seed max-size reporter-fn] + :or {num-tests 100 max-size 200 reporter-fn (constantly nil)}}] + (let [g (spec/gen (:args specs)) + prop (gen/for-all* [g] #(check-call f specs %))] + (let [ret (gen/quick-check num-tests prop :seed seed :max-size max-size :reporter-fn reporter-fn)] + (if-let [[smallest] (-> ret :shrunk :smallest)] + (unwrap-failing ret) + ret)))) + +(defn check-var + "Checks a var's specs using test.check. Optional args are +passed through to test.check/quick-check: + + num-tests number of tests to run, default 100 + seed random seed + max-size how large an input to generate, max 200 + reporter-fn reporting fn + +Returns a map as quick-check, with :explain-data added if +:result is false." + [v & opts] + (let [specs (spec/fn-specs v)] + (if (:args specs) + (apply check-fn @v specs opts) + (throw (js/Error. (str "No :args spec for " v)))))) + +(defn- run-var-tests + "Helper for run-tests, run-all-tests." + [vs] + (let [reporter-fn println] + (reduce + (fn [totals v] + (let [_ (println "Checking" v) + ret (check-var v :reporter-fn reporter-fn)] + (prn ret) + (cond-> totals + true (update :test inc) + (true? (:result ret)) (update :pass inc) + (::spec/problems (:result ret)) (update :fail inc) + (instance? js/Error (:result ret)) (update :error inc)))) + {:test 0, :pass 0, :fail 0, :error 0} + vs))) + + +(comment + (require '[cljs.pprint :as pp] + '[cljs.spec :as s] + '[cljs.spec.impl.gen :as gen] + '[cljs.test :as ctest]) + + (require :reload '[cjls.spec.test :as test]) + + ;; discover speced vars for your own test runner + (s/speced-vars) + + ;; check a single var + (test/check-var #'-) + (test/check-var #'+) + (test/check-var #'clojure.spec.broken-specs/throwing-fn) + + ;; old style example tests + (ctest/run-all-tests) + + (s/speced-vars 'clojure.spec.correct-specs) + ;; new style spec tests return same kind of map + (test/check-var #'subs) + (cljs.spec.test/run-tests 'clojure.core) + (test/run-all-tests) + + ) + + + + + From b7c8e94c3ae980098b07d466ebb6dda08fb207ab Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 May 2016 19:17:58 -0400 Subject: [PATCH 1785/4033] fix up cljs.spec.test macros --- src/main/cljs/cljs/spec/test.cljc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 57b35f328..f322f37a2 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -8,6 +8,7 @@ (ns cljs.spec.test (:require + [cljs.analyzer :as ana] [cljs.spec :as spec] [cljs.spec.impl.gen :as gen])) @@ -16,13 +17,14 @@ *ns* if no ns-sym are specified." [& ns-syms] (if (seq ns-syms) - (run-var-tests (->> (apply spec/speced-vars ns-syms) - (filter (fn [v] (:args (spec/fn-specs v)))))) - (run-tests (.name ^clojure.lang.Namespace *ns*)))) + `(cljs.spec.test/run-var-tests + (->> ~(spec/speced-vars* ns-syms) + (filter (fn [v] (:args (spec/fn-specs v)))))) + `(cljs.spec.test/run-tests '~ana/*cljs-ns*))) (defn run-all-tests "Like clojure.test/run-all-tests, but runs test.check tests for all speced vars. Prints per-test results to *out*, and returns a map with :test,:pass,:fail, and :error counts." [] - (run-var-tests (spec/speced-vars))) + `(cljs.spec.test/run-var-tests ~(spec/speced-vars*))) From 38dbb89bcaea2acececc3ba9ddf212e7edb84a24 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 May 2016 19:27:42 -0400 Subject: [PATCH 1786/4033] cljs.spec.test needs clojure.test.check, just require it, fix cljs.spec.test macros --- src/main/cljs/cljs/spec/test.cljc | 4 ++-- src/main/cljs/cljs/spec/test.cljs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index f322f37a2..d6fc01766 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -12,7 +12,7 @@ [cljs.spec :as spec] [cljs.spec.impl.gen :as gen])) -(defn run-tests +(defmacro run-tests "Like run-all-tests, but scoped to specific namespaces, or to *ns* if no ns-sym are specified." [& ns-syms] @@ -22,7 +22,7 @@ (filter (fn [v] (:args (spec/fn-specs v)))))) `(cljs.spec.test/run-tests '~ana/*cljs-ns*))) -(defn run-all-tests +(defmacro run-all-tests "Like clojure.test/run-all-tests, but runs test.check tests for all speced vars. Prints per-test results to *out*, and returns a map with :test,:pass,:fail, and :error counts." diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 70652c9d6..c6035fd01 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -9,6 +9,8 @@ (ns cljs.spec.test (:require-macros [cljs.spec.test :as st]) (:require + [clojure.test.check] + [clojure.test.check.properties] [cljs.spec :as spec] [cljs.spec.impl.gen :as gen])) From 15023ddf1a85270064eca3d04fe42489658e3365 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 May 2016 19:35:21 -0400 Subject: [PATCH 1787/4033] missing symbol conversion in cljs.spec/speced-vars* ns-match? check --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index e69514767..ceee503b2 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -281,7 +281,7 @@ (constantly true))] (reduce (fn [ret sym] - (if (ns-match? (namespace sym)) + (if (ns-match? (symbol (namespace sym))) (conj ret (list 'var sym)) ret)) #{} @_speced_vars)))) From 12d3ab3cf23fe2f667ddad6ce499049f3fd0d892 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 May 2016 19:51:34 -0400 Subject: [PATCH 1788/4033] tweak cljs.spec.test/run-tests --- src/main/cljs/cljs/spec/test.cljc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index d6fc01766..19a90e042 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -15,12 +15,12 @@ (defmacro run-tests "Like run-all-tests, but scoped to specific namespaces, or to *ns* if no ns-sym are specified." - [& ns-syms] - (if (seq ns-syms) - `(cljs.spec.test/run-var-tests - (->> ~(spec/speced-vars* ns-syms) - (filter (fn [v] (:args (spec/fn-specs v)))))) - `(cljs.spec.test/run-tests '~ana/*cljs-ns*))) + ([] + `(cljs.spec.test/run-tests '~ana/*cljs-ns*)) + ([& ns-syms] + `(cljs.spec.test/run-var-tests + (->> ~(spec/speced-vars* ns-syms) + (filter (fn [v] (:args (cljs.spec/fn-specs v)))))))) (defmacro run-all-tests "Like clojure.test/run-all-tests, but runs test.check tests From b22f8c152062cff488151d8e2e5aa90c863265fc Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 May 2016 20:01:17 -0400 Subject: [PATCH 1789/4033] fix cljs.spec.test/run-tests --- src/main/cljs/cljs/spec.cljc | 2 +- src/main/cljs/cljs/spec/test.cljc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index ceee503b2..578209068 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -277,7 +277,7 @@ (speced-vars* nil)) ([ns-syms] (let [ns-match? (if (seq ns-syms) - (set ns-syms) + (set (map second ns-syms)) (constantly true))] (reduce (fn [ret sym] diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 19a90e042..17375698b 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -20,7 +20,7 @@ ([& ns-syms] `(cljs.spec.test/run-var-tests (->> ~(spec/speced-vars* ns-syms) - (filter (fn [v] (:args (cljs.spec/fn-specs v)))))))) + (filter (fn [v#] (:args (cljs.spec/fn-specs v#)))))))) (defmacro run-all-tests "Like clojure.test/run-all-tests, but runs test.check tests From 19510523ad9de3f16832d287bae86f701e8b4263 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 30 May 2016 22:36:20 -0400 Subject: [PATCH 1790/4033] throw an exception if given a qualified parameter name to avoid generating invalid JS --- src/main/clojure/cljs/analyzer.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6ede96296..42101ca7e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1207,6 +1207,8 @@ (defn analyze-fn-method-param [env] (fn [[locals params] name] + (when (namespace name) + (throw (error env (str "Can't use qualified name as parameter: " name)))) (let [line (get-line name env) column (get-col name env) nmeta (meta name) From c0b114278c9d148e9c45b24e1ab50d05aeb29635 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 1 Jun 2016 10:24:02 -0400 Subject: [PATCH 1791/4033] same as Clojure commit 68fe71f --- src/main/cljs/cljs/spec.cljs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index a406537de..2871ab291 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -806,8 +806,8 @@ (defn- op-explain [form p path via in input] ;;(prn {:form form :p p :path path :input input}) (let [[x :as input] input - via (if-let [name (spec-name p)] (conj via name) via) {:keys [::op ps ks forms splice p1 p2] :as p} (reg-resolve p) + via (if-let [name (spec-name p)] (conj via name) via) insufficient (fn [path form] {path {:reason "Insufficient input" :pred (abbrev form) @@ -827,13 +827,13 @@ (if-let [p1 (deriv p1 x)] (explain-pred-list forms ps path via in (preturn p1)) (op-explain (op-describe p1) p1 path via in input))) - ::pcat (let [[pred k form] (->> (map vector - ps - (c/or (seq ks) (repeat nil)) - (c/or (seq forms) (repeat nil))) - (remove (fn [[p]] - (accept-nil? p))) - first) + ::pcat (let [pkfs (map vector + ps + (c/or (seq ks) (repeat nil)) + (c/or (seq forms) (repeat nil))) + [pred k form] (if (= 1 (count pkfs)) + (first pkfs) + (first (remove (fn [[p]] (accept-nil? p)) pkfs))) path (if k (conj path k) path) form (c/or form (op-describe pred))] (if (c/and (empty? input) (not pred)) @@ -916,11 +916,13 @@ (if-let [dp (deriv p x)] (recur dp xs (inc i)) (if (accept? p) - {path {:reason "Extra input" - :pred (abbrev (op-describe re)) - :val data - :via via - :in (conj in i)}} + (if (= (::op p) ::pcat) + (op-explain (op-describe p) p path via (conj in i) (seq data)) + {path {:reason "Extra input" + :pred (abbrev (op-describe re)) + :val data + :via via + :in (conj in i)}}) (c/or (op-explain (op-describe p) p path via (conj in i) (seq data)) {path {:reason "Extra input" :pred (abbrev (op-describe p)) From f75687f065f3b33c4c4b61ede51f201e94f4d1d9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 1 Jun 2016 12:32:36 -0400 Subject: [PATCH 1792/4033] make dynaload more ClojureScript friendly --- src/main/cljs/cljs/spec/impl/gen.cljc | 27 ++++++++++++++++++++------- src/main/cljs/cljs/spec/impl/gen.cljs | 20 +++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/spec/impl/gen.cljc b/src/main/cljs/cljs/spec/impl/gen.cljc index 382b9e5d9..9200a0bf3 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljc +++ b/src/main/cljs/cljs/spec/impl/gen.cljc @@ -8,13 +8,26 @@ (ns cljs.spec.impl.gen (:refer-clojure :exclude [delay]) - (:require [cljs.core :as c])) + (:require [cljs.core :as c] + [clojure.string :as string])) (defmacro dynaload [[quote s]] - `(if (c/exists? ~s) - ~(vary-meta s assoc :cljs.analyzer/no-resolve true) - (fn [& ~'args] - (throw (js/Error. (str "Var " '~s " is not on the classpath")))))) + (let [xs (string/split (namespace s) #"\.") + cnt (count xs) + checks (map + (fn [n xs] + `(c/exists? ~(symbol (string/join "." (take n xs))))) + (range 2 cnt) + (repeat xs))] + `(cljs.spec.impl.gen/LazyVar. + (fn [] + (if (and ~@checks (c/exists? ~s)) + ~(vary-meta s assoc :cljs.analyzer/no-resolve true) + (throw + (js/Error. + (str "Var " '~s " does not exist, " + (namespace '~s) " never required"))))) + nil))) (defmacro delay "given body that returns a generator, returns a @@ -28,7 +41,7 @@ [s] (let [fqn (symbol "clojure.test.check.generators" (name s)) doc (str "Lazy loaded version of " fqn)] - `(let [g# (c/delay (dynaload '~fqn))] + `(let [g# (dynaload '~fqn)] (defn ~s ~doc [& ~'args] @@ -47,7 +60,7 @@ [s] (let [fqn (symbol "clojure.test.check.generators" (name s)) doc (str "Fn returning " fqn)] - `(let [g# (c/delay (dynaload '~fqn))] + `(let [g# (dynaload '~fqn)] (defn ~s ~doc [& ~'args] diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index b0991a009..28cbcefaf 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -13,24 +13,34 @@ [cljs.spec.impl.gen :as gen :refer [dynaload lazy-combinators lazy-prims]]) (:require [cljs.core :as c])) +(deftype LazyVar [f ^:mutable cached] + IDeref + (-deref [this] + (if-not (nil? cached) + cached + (let [x (f)] + (when-not (nil? x) + (set! cached x)) + x)))) + (def ^:private quick-check-ref - (c/delay (dynaload 'clojure.test.check/quick-check))) + (dynaload 'clojure.test.check/quick-check)) (defn quick-check [& args] (apply @quick-check-ref args)) (def ^:private for-all*-ref - (c/delay (dynaload 'clojure.test.check.properties/for-all*))) + (dynaload 'clojure.test.check.properties/for-all*)) (defn for-all* "Dynamically loaded clojure.test.check.properties/for-all*." [& args] (apply @for-all*-ref args)) -(let [g? (c/delay (dynaload 'clojure.test.check.generators/generator?)) - g (c/delay (dynaload 'clojure.test.check.generators/generate)) - mkg (c/delay (dynaload 'clojure.test.check.generators/->Generator))] +(let [g? (dynaload 'clojure.test.check.generators/generator?) + g (dynaload 'clojure.test.check.generators/generate) + mkg (dynaload 'clojure.test.check.generators/->Generator)] (defn- generator? [x] (@g? x)) From d6440795c22e46d2a2f8ab585fb6cfabf62cc147 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Jun 2016 13:39:37 -0400 Subject: [PATCH 1793/4033] macro checking support --- src/main/clojure/cljs/analyzer.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 42101ca7e..b80e84384 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2361,7 +2361,11 @@ (if-not (nil? mac-var) (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) - (let [form' (try + (let [mchk #?(:clj (some-> (find-ns 'clojure.spec) + (ns-resolve 'macroexpand-check))) + _ #?(:clj (when mchk + (mchk mac-var (next form)))) + form' (try (apply @mac-var form env (rest form)) #?(:clj (catch ArityException e (throw (ArityException. (- (.actual e) 2) (.name e))))))] From c62a709b6944aa4d2977e67118fc4ecdecbb1be8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Jun 2016 13:46:54 -0400 Subject: [PATCH 1794/4033] 1.9.14 --- README.md | 6 +++--- changes.md | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ed7b46d1b..21ec86d14 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.8.51 +Latest stable release: 1.9.14 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.8.51"] +[org.clojure/clojurescript "1.9.14"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.8.51 org.clojure clojurescript - 1.8.51 + 1.9.14 ``` diff --git a/changes.md b/changes.md index ef8a6532a..219e40e18 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,16 @@ +## 1.9.14 + +### Enhancements +* clojure.spec ported to cljs.spec + +### Fixes +* CLJS-1649: Possible issue with in cljs.reader or cljs.core/PersistentHashMap +* CLJS-1647: Rethrow exception from parallel-build +* CLJS-1642: cljs.core/reductions does not respect 'reduced' +* CLJS-1635: Var type implements IEquiv but not IHash +* CLJS-1629: Fix warning about duplicate test-pr-str +* CLJS-1637: Missing docstrings for a few vars + ## 1.8.51 ### Changes From f38cda652699bf44d673f577137e181d8e20ec5d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Jun 2016 14:31:02 -0400 Subject: [PATCH 1795/4033] fix bad reader conditional --- src/main/clojure/cljs/analyzer.cljc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b80e84384..3e6fa75fe 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2361,10 +2361,10 @@ (if-not (nil? mac-var) (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) - (let [mchk #?(:clj (some-> (find-ns 'clojure.spec) - (ns-resolve 'macroexpand-check))) - _ #?(:clj (when mchk - (mchk mac-var (next form)))) + (let [#?@(:clj [mchk (some-> (find-ns 'clojure.spec) + (ns-resolve 'macroexpand-check)) + _ (when mchk + (mchk mac-var (next form)))]) form' (try (apply @mac-var form env (rest form)) #?(:clj (catch ArityException e From 9718d048448885364d8ca88e60a602afb5be8146 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jun 2016 12:43:03 -0400 Subject: [PATCH 1796/4033] add transit-clj as a provided dep --- pom.template.xml | 12 ++++++++++++ project.clj | 1 + script/bootstrap | 8 ++++++++ 3 files changed, 21 insertions(+) diff --git a/pom.template.xml b/pom.template.xml index f11425425..b61f56c36 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -64,6 +64,18 @@
+ + com.cognitect + transit-clj + 0.8.285 + provided + + + org.clojure + clojure + + + diff --git a/project.clj b/project.clj index 0b4923e98..d71cb8d73 100644 --- a/project.clj +++ b/project.clj @@ -12,6 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-beta1"] [org.clojure/test.check "0.9.0" :scope "test"] + [org.clojure/transit-clj "0.8.285" :scope "provided"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] [com.google.javascript/closure-compiler "v20160315"] [org.mozilla/rhino "1.7R5"]] diff --git a/script/bootstrap b/script/bootstrap index fa6d53af6..27c5d6ef7 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,6 +5,7 @@ set -e CLOJURE_RELEASE="1.8.0" CLOSURE_RELEASE="20160315" DJSON_RELEASE="0.2.6" +TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.0.0-beta1" @@ -33,6 +34,13 @@ cp data.json-$DJSON_RELEASE.jar lib/data.json-$DJSON_RELEASE.jar echo "Cleaning up data.json..." rm data.json-$DJSON_RELEASE.jar +echo "Fetching transit-clj..." +curl --retry 3 -O -s https://repo1.maven.org/maven2/com/cognitect/transit-clj/$TRANSIT_RELEASE/transit-clj-$TRANSIT_RELEASE.jar || { echo "Download failed."; exit 1; } +echo "Copying transit-cjl-$TRANSIT_RELEASE.jar to lib/transit-clj-$TRANSIT_RELEASE.jar..." +cp transit-clj-$TRANSIT_RELEASE.jar lib/transit-clj-$TRANSIT_RELEASE.jar +echo "Cleaning up data.json..." +rm transit-clj-$TRANSIT_RELEASE.jar + echo "Fetching Google Closure library..." mkdir -p closure/library cd closure/library From 513ee3abe3dfa8ae8d0f50189297f38fc67f38fc Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jun 2016 13:14:52 -0400 Subject: [PATCH 1797/4033] fix typo in project.clj --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index d71cb8d73..9e0f8936a 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-beta1"] [org.clojure/test.check "0.9.0" :scope "test"] - [org.clojure/transit-clj "0.8.285" :scope "provided"] + [com.cognitect/transit-clj "0.8.285" :scope "provided"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] [com.google.javascript/closure-compiler "v20160315"] [org.mozilla/rhino "1.7R5"]] From 821d46c08758bc64f4d7028db33abbea2337cb19 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jun 2016 15:36:19 -0400 Subject: [PATCH 1798/4033] lazy loading of transit reader/writer factory fns --- src/main/clojure/cljs/analyzer.cljc | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3e6fa75fe..0600ad37a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2817,6 +2817,30 @@ version (util/clojurescript-version)] (and version (not= version version')))))))))) +#?(:clj + (def transit-writer + (delay + (try + (require '[cognitect.transit]) + (some-> + (find-ns 'cognitect.transit) + (ns-resolve 'writer) + deref) + (catch Throwable t + nil))))) + +#?(:clj + (def transit-reader + (delay + (try + (require '[cognitect.transit]) + (some-> + (find-ns 'cognitect.transit) + (ns-resolve 'reader) + deref) + (catch Throwable t + nil))))) + #?(:clj (defn write-analysis-cache ([ns cache-file] @@ -2827,8 +2851,8 @@ (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" (pr-str (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)))) - (when src - (.setLastModified ^File cache-file (util/last-modified src)))))) + (when src + (.setLastModified ^File cache-file (util/last-modified src)))))) #?(:clj (defn analyze-file From a4d9ccc820152d506aeb38886f606ac7156a402e Mon Sep 17 00:00:00 2001 From: Rohit Aggarwal Date: Fri, 27 May 2016 10:41:31 +0100 Subject: [PATCH 1799/4033] CLJS-1650: `cljs.reader/read-map` now returns array-map/hash-map based on the size of the sequence. --- src/main/cljs/cljs/reader.cljs | 9 ++++++--- src/test/cljs/cljs/reader_test.cljs | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index 960552e84..b9433e425 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -273,10 +273,13 @@ nil if the end of stream has been reached") (defn read-map [rdr _] - (let [l (read-delimited-list "}" rdr true)] - (when (odd? (count l)) + (let [l (read-delimited-list "}" rdr true) + c (count l)] + (when (odd? c) (reader-error rdr "Map literal must contain an even number of forms")) - (apply hash-map l))) + (if (<= c (* 2 (.-HASHMAP-THRESHOLD PersistentArrayMap))) + (apply array-map l) + (apply hash-map l)))) (defn read-number [reader initch] diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index b8f4b3529..734dfc00c 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -174,6 +174,14 @@ (testing "Testing reading, CLJS-819" (is (= m " \u00a1"))))) +(deftest testing-map-type + (let [a (reader/read-string "{:a 1 :b 2 :c 3}") + b (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9}")] + (is (= a {:a 1 :b 2 :c 3})) + (is (instance? PersistentArrayMap a)) + (is (= b {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9})) + (is (instance? PersistentHashMap b)))) + ;; NOTE: issue uncovered by test.check (deftest test-slash-reading From 2f012cec88d05f42dd145338d9c942498d3ceb13 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jun 2016 19:13:58 -0400 Subject: [PATCH 1800/4033] CLJS-1663: Calling instrumented multi-arity function causes exception when instrumenting we set! the var to the spec verifying fn but this meant that arity references for correct dispatching would be missing link the arities from the original function object to the spec verifying fn via goog.object/extend --- src/main/cljs/cljs/spec.cljs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 2871ab291..b4a284cd7 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -10,7 +10,8 @@ (:refer-clojure :exclude [+ * and or cat def keys]) (:require-macros [cljs.core :as c] [cljs.spec :as s]) - (:require [cljs.core :as c] + (:require [goog.object :as gobj] + [cljs.core :as c] [clojure.walk :as walk] [cljs.spec.impl.gen :as gen] [clojure.string :as str])) @@ -280,19 +281,21 @@ (str "Call to " v " did not conform to spec:\n" (with-out-str (explain-out ed))) ed))) conformed)))] - (c/fn - [& args] - (if *instrument-enabled* - (s/with-instrument-disabled - (let [specs (fn-specs v)] - (let [cargs (when (:args specs) (conform! v :args (:args specs) args args)) - ret (binding [*instrument-enabled* true] - (apply f args)) - cret (when (:ret specs) (conform! v :ret (:ret specs) ret args))] - (when (c/and (:args specs) (:ret specs) (:fn specs)) - (conform! v :fn (:fn specs) {:args cargs :ret cret} args)) - ret))) - (apply f args))))) + (doto + (c/fn + [& args] + (if *instrument-enabled* + (s/with-instrument-disabled + (let [specs (fn-specs v)] + (let [cargs (when (:args specs) (conform! v :args (:args specs) args args)) + ret (binding [*instrument-enabled* true] + (apply f args)) + cret (when (:ret specs) (conform! v :ret (:ret specs) ret args))] + (when (c/and (:args specs) (:ret specs) (:fn specs)) + (conform! v :fn (:fn specs) {:args cargs :ret cret} args)) + ret))) + (apply f args))) + (gobj/extend f)))) ;(defn- macroexpand-check ; [v args] From 3977b131229ffbb5965196b809c8247fc3b95768 Mon Sep 17 00:00:00 2001 From: Rohit Aggarwal Date: Thu, 2 Jun 2016 12:58:37 +0100 Subject: [PATCH 1801/4033] Optimize seq (&) destructuring as per commit (0aa3467) of Clojure --- benchmark/cljs/benchmark_runner.cljs | 9 ++++ src/main/clojure/cljs/core.cljc | 80 +++++++++++++++++----------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 9fdced899..723316bfb 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -379,3 +379,12 @@ (simple-benchmark [f array] (f 1 2 3 4 5 6 7 8 9 0) 100000) (simple-benchmark [f vector] (f 1 2 3 4 5 6 7 8 9 0) 100000) (simple-benchmark [] (= 1 1 1 1 1 1 1 1 1 0) 100000) + +(println "\n") +(println ";; Destructuring a sequence") +(simple-benchmark [v (into [] (range 1000000))] + (loop [[x & xs] v] + (if-not (nil? xs) + (recur xs) + x)) + 10) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 773e3fc57..713805945 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -612,54 +612,70 @@ pb (core/fn pb [bvec b v] (core/let [pvec (core/fn [bvec b val] - (core/let [gvec (gensym "vec__")] - (core/loop [ret (core/-> bvec (conj gvec) (conj val)) + (core/let [gvec (gensym "vec__") + gseq (gensym "seq__") + gfirst (gensym "first__") + has-rest (some #{'&} b)] + (core/loop [ret (core/let [ret (conj bvec gvec val)] + (if has-rest + (conj ret gseq (core/list `seq gvec)) + ret)) n 0 bs b seen-rest? false] (if (seq bs) (core/let [firstb (first bs)] (core/cond - (= firstb '&) (recur (pb ret (second bs) (core/list `nthnext gvec n)) - n - (nnext bs) - true) + (= firstb '&) (recur (pb ret (second bs) gseq) + n + (nnext bs) + true) (= firstb :as) (pb ret (second bs) gvec) :else (if seen-rest? - (throw - #?(:clj (new Exception "Unsupported binding form, only :as can follow & parameter") - :cljs (new js/Error "Unsupported binding form, only :as can follow & parameter"))) - (recur (pb ret firstb (core/list `nth gvec n nil)) - (core/inc n) - (next bs) - seen-rest?)))) + (throw #?(:clj (new Exception "Unsupported binding form, only :as can follow & parameter") + :cljs (new js/Error "Unsupported binding form, only :as can follow & parameter"))) + (recur (pb (if has-rest + (conj ret + gfirst `(first ~gseq) + gseq `(next ~gseq)) + ret) + firstb + (if has-rest + gfirst + (core/list `nth gvec n nil))) + (core/inc n) + (next bs) + seen-rest?)))) ret)))) pmap (core/fn [bvec b v] (core/let [gmap (gensym "map__") defaults (:or b)] (core/loop [ret (core/-> bvec (conj gmap) (conj v) - (conj gmap) (conj `(if (implements? ISeq ~gmap) (apply cljs.core/hash-map ~gmap) ~gmap)) - ((core/fn [ret] - (if (:as b) - (conj ret (:as b) gmap) - ret)))) + (conj gmap) (conj `(if (implements? ISeq ~gmap) (apply cljs.core/hash-map ~gmap) ~gmap)) + ((core/fn [ret] + (if (:as b) + (conj ret (:as b) gmap) + ret)))) bes (reduce - (core/fn [bes entry] - (reduce #(assoc %1 %2 ((val entry) %2)) - (dissoc bes (key entry)) - ((key entry) bes))) - (dissoc b :as :or) - {:keys #(if (core/keyword? %) % (keyword (core/str %))), - :strs core/str, :syms #(core/list `quote %)})] + (core/fn [bes entry] + (reduce #(assoc %1 %2 ((val entry) %2)) + (dissoc bes (key entry)) + ((key entry) bes))) + (dissoc b :as :or) + {:keys #(if (core/keyword? %) % (keyword (core/str %))), + :strs core/str, :syms #(core/list `quote %)})] (if (seq bes) (core/let [bb (key (first bes)) bk (val (first bes)) - has-default (contains? defaults bb)] - (recur (pb ret bb (if has-default - (core/list 'cljs.core/get gmap bk (defaults bb)) - (core/list 'cljs.core/get gmap bk))) - (next bes))) + bv (if (contains? defaults bb) + (core/list 'cljs.core/get gmap bk (defaults bb)) + (core/list 'cljs.core/get gmap bk))] + (recur (core/cond + (core/symbol? bb) (core/-> ret (conj (if (namespace bb) (symbol (name bb)) bb)) (conj bv)) + (core/keyword? bb) (core/-> ret (conj (symbol (name bb)) bv)) + :else (pb ret bb bv)) + (next bes))) ret))))] (core/cond (core/symbol? b) (core/-> bvec (conj (if (namespace b) (symbol (name b)) b)) (conj v)) @@ -667,8 +683,8 @@ (vector? b) (pvec bvec b v) (map? b) (pmap bvec b v) :else (throw - #?(:clj (new Exception (core/str "Unsupported binding form: " b)) - :cljs (new js/Error (core/str "Unsupported binding form: " b))))))) + #?(:clj (new Exception (core/str "Unsupported binding form: " b)) + :cljs (new js/Error (core/str "Unsupported binding form: " b))))))) process-entry (core/fn [bvec b] (pb bvec (first b) (second b)))] (if (every? core/symbol? (map first bents)) bindings From fc5a45b2963083d77bd2cc91af362c908e00c082 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 2 Jun 2016 19:32:24 -0400 Subject: [PATCH 1802/4033] only print specs in REPL if we actually have some --- src/main/cljs/cljs/repl.cljs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index 6b6fdb704..9fdbe897a 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -49,9 +49,10 @@ (when doc (println " " doc)))) (when n - (when-let [specs (spec/fn-specs (symbol (str (ns-name n)) (name nm)))] - (println "Spec") - (run! (fn [[role spec]] - (when (and spec (not (= spec ::spec/unknown))) - (println " " (str (name role) ":") (spec/describe spec)))) - specs)))))) + (let [specs (spec/fn-specs (symbol (str (ns-name n)) (name nm)))] + (when (some identity (vals specs)) + (print "Spec") + (run! (fn [[role spec]] + (when (and spec (not (= spec ::spec/unknown))) + (print (str "\n " (name role) ":") (spec/describe spec)))) + specs))))))) From 358e5632544f1214ebd7cfc4d43c651ea78b25cb Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 3 Jun 2016 08:22:28 -0400 Subject: [PATCH 1803/4033] CLJS-1611: Function arity dispatch returns arity Variadic and multi-arity defn forms expand to code that perform the needed def, followed by some side-effecting code. Instead of returning the value of the last side-effecting form, conditionally return a var for the name if :def-emits-vars is true, otherwise nil (which gets elided from compiled JavaScript.) --- src/main/clojure/cljs/core.cljc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 713805945..ab8dd3c4d 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2773,7 +2773,7 @@ (.push ~dest (aget (js-arguments) i#)) (recur (inc i#)))))) -(core/defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl]] +(core/defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl] emit-var?] (core/letfn [(dest-args [c] (map (core/fn [n] `(aget (js-arguments) ~n)) (range c)))] @@ -2797,7 +2797,8 @@ (.slice args# ~c-1) 0 nil))] (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#)))))) - ~(variadic-fn* rname method))))) + ~(variadic-fn* rname method) + ~(core/when emit-var? `(var ~name)))))) (core/comment (require '[clojure.pprint :as pp]) @@ -2807,7 +2808,7 @@ (pp/pprint (variadic-fn 'foo {} '(([a [b & cs] & xs] xs)))) ) -(core/defn- multi-arity-fn [name meta fdecl] +(core/defn- multi-arity-fn [name meta fdecl emit-var?] (core/letfn [(dest-args [c] (map (core/fn [n] `(aget (js-arguments) ~n)) (range c))) @@ -2863,7 +2864,8 @@ (str "Invalid arity: " (alength ~args-sym)))))))))) ~@(map fn-method fdecl) ;; optimization properties - (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa))))) + (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa) + ~(core/when emit-var? `(var ~name)))))) (core/comment (require '[clojure.pprint :as pp]) @@ -2932,13 +2934,13 @@ (multi-arity-fn name (if (comp/checking-types?) (update-in m [:jsdoc] conj "@param {...*} var_args") - m) fdecl) + m) fdecl (:def-emits-var &env)) (variadic-fn? fdecl) (variadic-fn name (if (comp/checking-types?) (update-in m [:jsdoc] conj "@param {...*} var_args") - m) fdecl) + m) fdecl (:def-emits-var &env)) :else (core/list 'def (with-meta name m) From 00f4726d6664023c57380f69287fd1c6e7fc3671 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Jun 2016 09:46:39 -0400 Subject: [PATCH 1804/4033] same as Clojure 47b8d6b --- src/main/cljs/cljs/spec.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index b4a284cd7..fa758e7f2 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -49,7 +49,10 @@ (with-meta spec (assoc (meta spec) ::name name))) (defn- spec-name [spec] - (when (implements? IMeta spec) + (cond + (keyword? spec) spec + + (implements? IMeta spec) (-> (meta spec) ::name))) (defn- reg-resolve From 8e88387582f0e65c6ccf9f24a462dfa015ce8a90 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Jun 2016 10:38:56 -0400 Subject: [PATCH 1805/4033] same as Clojure 1f25347 --- src/main/cljs/cljs/spec.cljs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index fa758e7f2..d7568e1df 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -202,11 +202,10 @@ (defn- gensub [spec overrides path rmap form] ;;(prn {:spec spec :over overrides :path path :form form}) - (if-let [spec (specize spec)] + (let [spec (specize spec)] (if-let [g (c/or (get overrides path) (gen* spec overrides path rmap))] (gen/such-that #(valid? spec %) g 100) - (throw (js/Error. (str "Unable to construct gen at: " path " for: " (abbrev form))))) - (throw (js/Error. (str "Unable to construct gen at: " path ", " (abbrev form) " can not be made a spec"))))) + (throw (js/Error. (str "Unable to construct gen at: " path " for: " (abbrev form))))))) (defn gen "Given a spec, returns the generator for it, or throws if none can From 296f2bc4311e6023e83d3f7cdfe7bd8fc336b982 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Jun 2016 11:09:48 -0400 Subject: [PATCH 1806/4033] simplify how we lazy load transit. cljs.analyzer/write-analysis-cache now supports writing out transit if available. cljs.closure/aot-cache-core attempts to write out transit analysis cache for core. tweak .gitignore now for new cache file. --- .gitignore | 1 + src/main/clojure/cljs/analyzer.cljc | 43 ++++++++++++++--------------- src/main/clojure/cljs/closure.clj | 6 ++-- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 802fe2bed..a2e53453e 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ nashorn_code_cache src/main/cljs/cljs/core.aot.js src/main/cljs/cljs/core.aot.js.map src/main/cljs/cljs/core.cljs.cache.aot.edn +src/main/cljs/cljs/core.cljs.cache.aot.json diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 0600ad37a..c516b49ff 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -32,7 +32,7 @@ [cljs.tools.reader :as reader] [cljs.tools.reader.reader-types :as readers] [cljs.reader :as edn])) - #?(:clj (:import [java.io File Reader PushbackReader] + #?(:clj (:import [java.io File Reader PushbackReader FileOutputStream] [java.net URL] [java.lang Throwable] [clojure.lang Namespace Var LazySeq ArityException] @@ -2818,26 +2818,16 @@ (and version (not= version version')))))))))) #?(:clj - (def transit-writer + (def transit (delay (try (require '[cognitect.transit]) - (some-> - (find-ns 'cognitect.transit) - (ns-resolve 'writer) - deref) - (catch Throwable t - nil))))) - -#?(:clj - (def transit-reader - (delay - (try - (require '[cognitect.transit]) - (some-> - (find-ns 'cognitect.transit) - (ns-resolve 'reader) - deref) + (let [ns (find-ns 'cognitect.transit)] + (when ns + {:writer @(ns-resolve ns 'writer) + :reader @(ns-resolve ns 'reader) + :write @(ns-resolve ns 'write) + :read @(ns-resolve ns 'read)})) (catch Throwable t nil))))) @@ -2845,12 +2835,19 @@ (defn write-analysis-cache ([ns cache-file] (write-analysis-cache ns cache-file nil)) - ([ns cache-file src] + ([ns ^File cache-file src] (util/mkdirs cache-file) - (spit cache-file - (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" - (pr-str - (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)))) + (let [ext (util/ext cache-file) + analysis (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)] + (case ext + "edn" (spit cache-file + (str (when + (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n")) + (pr-str analysis))) + "json" (when-let [{:keys [writer write]} @transit] + (write + (writer (FileOutputStream. cache-file) :json) + analysis)))) (when src (.setLastModified ^File cache-file (util/last-modified src)))))) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4e86e8b86..bebbda958 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2133,14 +2133,16 @@ (let [base-path (io/file "src" "main" "cljs" "cljs") src (io/file base-path "core.cljs") dest (io/file base-path "core.aot.js") - cache (io/file base-path "core.cljs.cache.aot.edn")] + cache (io/file base-path "core.cljs.cache.aot.edn") + tcache (io/file base-path "core.cljs.cache.aot.json")] (util/mkdirs dest) (env/with-compiler-env (env/default-compiler-env) (comp/compile-file src dest {:source-map true :source-map-url "core.js.map" :output-dir (str "src" File/separator "main" File/separator "cljs")}) - (ana/write-analysis-cache 'cljs.core cache src)))) + (ana/write-analysis-cache 'cljs.core cache src) + (ana/write-analysis-cache 'cljs.core tcache src)))) (comment (time From 6dd7932ed2ad5df30c375922744566961b58d970 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Jun 2016 11:11:17 -0400 Subject: [PATCH 1807/4033] tweak build scripts to remove the new cache file --- script/build | 2 ++ script/uberjar | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/script/build b/script/build index 118091507..596b8ca81 100755 --- a/script/build +++ b/script/build @@ -43,6 +43,7 @@ mv $CLJS_FILE src/main/cljs/cljs/core.cljs rm -f src/main/cljs/cljs/core.aot.js rm -f src/main/cljs/cljs/core.aot.js.map rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn +rm -f src/main/cljs/cljs/core.cljs.cache.aot.json ./script/aot_core @@ -70,3 +71,4 @@ fi rm -f src/main/cljs/cljs/core.aot.js rm -f src/main/cljs/cljs/core.aot.js.map rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn +rm -f src/main/cljs/cljs/core.cljs.cache.aot.json diff --git a/script/uberjar b/script/uberjar index 57ab4e632..fde84a745 100755 --- a/script/uberjar +++ b/script/uberjar @@ -33,6 +33,7 @@ mv $CLJS_FILE src/main/cljs/cljs/core.cljs rm -f src/main/cljs/cljs/core.aot.js rm -f src/main/cljs/cljs/core.aot.js.map rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn +rm -f src/main/cljs/cljs/core.cljs.cache.aot.json ./script/aot_core @@ -49,4 +50,5 @@ mv target/clojurescript-0.0-SNAPSHOT-standalone.jar target/cljs.jar rm -f src/main/cljs/cljs/core.aot.js rm -f src/main/cljs/cljs/core.aot.js.map -rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn \ No newline at end of file +rm -f src/main/cljs/cljs/core.cljs.cache.aot.edn +rm -f src/main/cljs/cljs/core.cljs.cache.aot.json From 6774ada0ce0159032f5893f3564e72b9ae773cb4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Jun 2016 11:17:26 -0400 Subject: [PATCH 1808/4033] make cljs.analyzer/requires-analysis? and cljs.analyzer/cache-file transit aware move transit var up --- src/main/clojure/cljs/analyzer.cljc | 34 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c516b49ff..f510f3228 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -55,6 +55,20 @@ (def ^:dynamic *file-defs* nil) +#?(:clj + (def transit + (delay + (try + (require '[cognitect.transit]) + (let [ns (find-ns 'cognitect.transit)] + (when ns + {:writer @(ns-resolve ns 'writer) + :reader @(ns-resolve ns 'reader) + :write @(ns-resolve ns 'write) + :read @(ns-resolve ns 'read)})) + (catch Throwable t + nil))))) + ;; log compiler activities (def ^:dynamic *verbose* false) @@ -2782,7 +2796,8 @@ (if-let [core-cache (and (= mode :read) (= (:ns ns-info) 'cljs.core) - (io/resource "cljs/core.cljs.cache.aot.edn"))] + (or (and @transit (io/resource "cljs/core.cljs.cache.aot.json")) + (io/resource "cljs/core.cljs.cache.aot.edn")))] core-cache (let [target-file (util/to-target-file output-dir ns-info (util/ext (:source-file ns-info)))] @@ -2800,7 +2815,8 @@ ([src cache output-dir] (cond (and (util/url? cache) - (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.edn")) + (or (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.edn") + (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.json"))) false (and (util/file? cache) @@ -2817,20 +2833,6 @@ version (util/clojurescript-version)] (and version (not= version version')))))))))) -#?(:clj - (def transit - (delay - (try - (require '[cognitect.transit]) - (let [ns (find-ns 'cognitect.transit)] - (when ns - {:writer @(ns-resolve ns 'writer) - :reader @(ns-resolve ns 'reader) - :write @(ns-resolve ns 'write) - :read @(ns-resolve ns 'read)})) - (catch Throwable t - nil))))) - #?(:clj (defn write-analysis-cache ([ns cache-file] From 18cd16f025f543df96454f113b9cd7e9cfe0ba85 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Jun 2016 12:12:54 -0400 Subject: [PATCH 1809/4033] finish transit support, potential AOT issue that needs looking into --- src/main/clojure/cljs/analyzer.cljc | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f510f3228..d0919251b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -32,7 +32,7 @@ [cljs.tools.reader :as reader] [cljs.tools.reader.reader-types :as readers] [cljs.reader :as edn])) - #?(:clj (:import [java.io File Reader PushbackReader FileOutputStream] + #?(:clj (:import [java.io File Reader PushbackReader FileOutputStream FileInputStream] [java.net URL] [java.lang Throwable] [clojure.lang Namespace Var LazySeq ArityException] @@ -2801,7 +2801,9 @@ core-cache (let [target-file (util/to-target-file output-dir ns-info (util/ext (:source-file ns-info)))] - (io/file (str target-file ".cache.edn"))))))) + (if @transit + (io/file (str target-file ".cache.json")) + (io/file (str target-file ".cache.edn")))))))) #?(:clj (defn requires-analysis? @@ -2814,9 +2816,15 @@ (requires-analysis? src cache output-dir))) ([src cache output-dir] (cond + (util/url? cache) + (let [path (.getPath ^URL cache)] + (if (or (.endsWith path "cljs/core.cljs.cache.aot.edn") + (.endsWith path "cljs/core.cljs.cache.aot.json")) + false + (throw (Exception. (str "Invalid anlaysis cache, must be file not URL " cache))))) + (and (util/url? cache) - (or (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.edn") - (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.json"))) + (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.json")) false (and (util/file? cache) @@ -2912,7 +2920,11 @@ {:restore false :analyze-deps true :load-macros true})) - cached-ns (edn/read-string (slurp cache))] + ext (util/ext cache) + cached-ns (case ext + "edn" (edn/read-string (slurp cache)) + "json" (let [{:keys [reader read]} @transit] + (read (reader (FileInputStream. ^File cache) :json))))] (when (or *verbose* (:verbose opts)) (util/debug-prn "Reading analysis cache for" (str res))) (swap! env/*compiler* From 5d6868355b9a154bac5c090f7853ebb140eea1b6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Jun 2016 14:11:18 -0400 Subject: [PATCH 1810/4033] include transit-clj in project.clj for uberjar --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 9e0f8936a..2935d4275 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-beta1"] [org.clojure/test.check "0.9.0" :scope "test"] - [com.cognitect/transit-clj "0.8.285" :scope "provided"] + [com.cognitect/transit-clj "0.8.285"] [org.clojure/google-closure-library "0.0-20151016-61277aea"] [com.google.javascript/closure-compiler "v20160315"] [org.mozilla/rhino "1.7R5"]] From 0ab8c8b8e361bb8b4b2c202bc72a036667407b4f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Jun 2016 14:14:00 -0400 Subject: [PATCH 1811/4033] use 1.9.0-alpha4 in bootstrap script for testing --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index 27c5d6ef7..b778b13e3 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.8.0" +CLOJURE_RELEASE="1.9.0-alpha4" CLOSURE_RELEASE="20160315" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" From 1046cf9f592355655b534a8c657015e14268a2ce Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Jun 2016 14:25:19 -0400 Subject: [PATCH 1812/4033] remove redundant code --- src/main/clojure/cljs/analyzer.cljc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d0919251b..fc5b24661 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2823,10 +2823,6 @@ false (throw (Exception. (str "Invalid anlaysis cache, must be file not URL " cache))))) - (and (util/url? cache) - (.endsWith (.getPath ^URL cache) "cljs/core.cljs.cache.aot.json")) - false - (and (util/file? cache) (not (.exists ^File cache))) true From 74ef21b75a3baa6901f7ead0f3b16da0296ba20e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Jun 2016 14:46:36 -0400 Subject: [PATCH 1813/4033] need to handle file & urls when reading transit cache --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index fc5b24661..a581e7d94 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2920,7 +2920,7 @@ cached-ns (case ext "edn" (edn/read-string (slurp cache)) "json" (let [{:keys [reader read]} @transit] - (read (reader (FileInputStream. ^File cache) :json))))] + (read (reader (io/input-stream cache) :json))))] (when (or *verbose* (:verbose opts)) (util/debug-prn "Reading analysis cache for" (str res))) (swap! env/*compiler* From 40a137a14e161cbaf9987bb018376cedeb4af3e1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Jun 2016 15:19:32 -0400 Subject: [PATCH 1814/4033] now that we have deterministic compilation order we don't need to track versioning information in analysis caches. If some source changes, the analysis will always be updated - so all we care about is whether the analysis cache and original source are out of sync. --- src/main/clojure/cljs/analyzer.cljc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a581e7d94..fb812c6b4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2831,11 +2831,7 @@ (let [out-src (util/to-target-file output-dir (parse-ns src))] (if (not (.exists out-src)) true - (if (util/changed? src cache) - true - (let [version' (util/compiled-by-version cache) - version (util/clojurescript-version)] - (and version (not= version version')))))))))) + (util/changed? src cache))))))) #?(:clj (defn write-analysis-cache From 0a14a16f6fa87f56f910953db6194d8a1ab935b9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Jun 2016 16:04:45 -0400 Subject: [PATCH 1815/4033] 1.9.36 --- README.md | 6 +++--- changes.md | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 21ec86d14..8d9399dd8 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.9.14 +Latest stable release: 1.9.36 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.14"] +[org.clojure/clojurescript "1.9.36"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.9.14 org.clojure clojurescript - 1.9.14 + 1.9.36 ``` diff --git a/changes.md b/changes.md index 219e40e18..cbbe1e92f 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,19 @@ +## 1.9.36 + +### Enhancements +* Write analysis caches as Transit if transit-clj available + +### Changes +* Clojure 1f25347 +* Clojure 47b8d6b +* Optimize seq (&) destructuring as per commit (0aa3467) of Clojure + +### Fixes +* CLJS-1611: Function arity dispatch returns arity +* only print specs in REPL if we actually have some +* CLJS-1663: Calling instrumented multi-arity function causes exception +* CLJS-1650: `cljs.reader/read-map` now returns array-map/hash-map based on the size of the sequence. + ## 1.9.14 ### Enhancements From 1dab2d684a4bfb1b2ad31979600b392df689f6ba Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 5 Jun 2016 00:16:45 -0400 Subject: [PATCH 1816/4033] CLJS-1699: Self-host: s/fdef ns-qualify *ns* name field access --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 578209068..4455abe75 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -264,7 +264,7 @@ (->sym resolved) (if (namespace s) s - (symbol (str (.name *ns*)) (str s))))) + (symbol (str (.-name *ns*)) (str s))))) (defn- fn-spec-sym [env sym role] From e5e9ecc59c89623ae7586f4f42e68b1e11c2937d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 5 Jun 2016 10:13:06 -0400 Subject: [PATCH 1817/4033] CLJS-1667: bad describe* for and-spec-impl --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index d7568e1df..fd04a656c 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -642,7 +642,7 @@ (explain* [_ path via in x] (explain-pred-list forms preds path via in x)) (gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms)))) (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) - (describe* [_] `(s/and ~@forms)))) + (describe* [_] `(and ~@forms)))) ;;;;;;;;;;;;;;;;;;;;;;; regex ;;;;;;;;;;;;;;;;;;; ;;See: From 63a4634c3b2aa72f33404901843540fe3302496d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 3 Jun 2016 19:28:20 -0400 Subject: [PATCH 1818/4033] CLJS-1664: The filename aux.cljs is a problem on windows. --- src/test/self/self_parity/{aux.cljs => auxiliary.cljs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/self/self_parity/{aux.cljs => auxiliary.cljs} (99%) diff --git a/src/test/self/self_parity/aux.cljs b/src/test/self/self_parity/auxiliary.cljs similarity index 99% rename from src/test/self/self_parity/aux.cljs rename to src/test/self/self_parity/auxiliary.cljs index b55672b97..db301615d 100644 --- a/src/test/self/self_parity/aux.cljs +++ b/src/test/self/self_parity/auxiliary.cljs @@ -3,7 +3,7 @@ the libs listed here to be dumped into the compiler output directory where they can be loaded on demand when running the compiler tests in bootstrap mode."} - self-parity.aux + self-parity.auxiliary (:require goog.Delay goog.Disposable From 6bd816f0fc6f0c3369b9b03f2561b219de6a69fc Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 4 Jun 2016 16:58:45 -0400 Subject: [PATCH 1819/4033] CLJS-1668: Self-host: Macro checking support --- src/main/cljs/cljs/spec.cljs | 26 ++++++++++++-------------- src/main/clojure/cljs/analyzer.cljc | 8 +++++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index fd04a656c..550b28a8a 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -299,20 +299,18 @@ (apply f args))) (gobj/extend f)))) -;(defn- macroexpand-check -; [v args] -; (let [specs (fn-specs v)] -; (when-let [arg-spec (:args specs)] -; (when (= ::invalid (conform arg-spec args)) -; (let [ed (assoc (explain-data* arg-spec [:args] -; (if-let [name (spec-name arg-spec)] [name] []) [] args) -; ::args args)] -; (throw (js/Error. -; (str -; "Call to " (->sym v) " did not conform to spec:\n" -; (with-out-str (explain-out ed)))))))))) - - +(defn- macroexpand-check + [v args] + (let [specs (fn-specs v)] + (when-let [arg-spec (:args specs)] + (when (= ::invalid (conform arg-spec args)) + (let [ed (assoc (explain-data* arg-spec [:args] + (if-let [name (spec-name arg-spec)] [name] []) [] args) + ::args args)] + (throw (js/Error. + (str + "Call to " (->sym v) " did not conform to spec:\n" + (with-out-str (explain-out ed)))))))))) (defn- no-fn-specs [v specs] diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index fb812c6b4..6abc45741 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2375,10 +2375,12 @@ (if-not (nil? mac-var) (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) - (let [#?@(:clj [mchk (some-> (find-ns 'clojure.spec) + (let [mchk #?(:clj (some-> (find-ns 'clojure.spec) (ns-resolve 'macroexpand-check)) - _ (when mchk - (mchk mac-var (next form)))]) + :cljs (and ^::no-resolve cljs.spec + ^::no-resolve cljs.spec/macroexpand-check)) + _ (when mchk + (mchk mac-var (next form))) form' (try (apply @mac-var form env (rest form)) #?(:clj (catch ArityException e From da007b6b3781ea1746989ff3c55e1fa2d16901b6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Jun 2016 15:04:00 -0400 Subject: [PATCH 1820/4033] AOT cljs.spec nses --- pom.template.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.template.xml b/pom.template.xml index b61f56c36..a28d7382b 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -300,6 +300,9 @@ cljs.analyzer.api cljs.build.api cljs.compiler.api + cljs.spec + cljs.spec.test + cljs.spec.impl.gen cljs.repl cljs.repl.browser cljs.repl.nashorn From 13e199779acd1cdfea49111c27ccc716639ad81e Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Jun 2016 17:05:13 -0400 Subject: [PATCH 1821/4033] store the qualified name in _speced_vars --- src/main/cljs/cljs/spec.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 4455abe75..dac2645a1 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -326,9 +326,9 @@ specified, return speced vars from all namespaces." :sym symbol?) :ret symbol?)" [fn-sym & {:keys [args ret fn] :as m}] - (swap! _speced_vars conj (:name (resolve &env fn-sym))) (let [env &env qn (ns-qualify env fn-sym)] + (swap! _speced_vars conj qn) `(do ~@(reduce (clojure.core/fn [defns role] (if (contains? m role) @@ -389,7 +389,8 @@ specified, return speced vars from all namespaces." ~v))) (defmacro unstrument - "Undoes instrument on the var at v, a var or symbol. Idempotent."[v] + "Undoes instrument on the var at v, a var or symbol. Idempotent." + [v] (let [v (if-not (seq? v) (list 'var v) v) sym (second v)] `(when-let [raw# (cljs.spec/unstrument* ~v)] From 103aa6e5770242d8bdf9d234a85fca9e6dc918cf Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Jun 2016 17:39:27 -0400 Subject: [PATCH 1822/4033] CLJS-1671: Bad cljs.spec interactive instrumentation session this commit and previous commit solve the issue --- src/main/cljs/cljs/spec.cljs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 550b28a8a..712572664 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -340,9 +340,8 @@ (when-let [{:keys [raw wrapped]} (get @instrumented-vars v)] (let [current @v] (when (= wrapped current) - raw)) - (swap! instrumented-vars dissoc v)) - v)) + (swap! instrumented-vars dissoc v) + raw))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; impl ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- recur-limit? [rmap id path k] From 2a21c229d05c7140b26544da25bd715c4899313d Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Jun 2016 18:06:52 -0400 Subject: [PATCH 1823/4033] Same as Clojure 080121d --- src/main/cljs/cljs/spec.cljc | 7 ++- src/main/cljs/cljs/spec.cljs | 112 ++++++++++++++++++++++++++++------- 2 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index dac2645a1..120b71ba7 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -234,9 +234,10 @@ (defmacro conformer "takes a predicate function with the semantics of conform i.e. it should return either a (possibly converted) value or :clojure.spec/invalid, and returns a - spec that uses it as a predicate/conformer" - [f] - `(cljs.spec/spec-impl '~f ~f nil true)) + spec that uses it as a predicate/conformer. Optionally takes a + second fn that does unform of result of first" + ([f] `(cljs.spec/spec-impl '~f ~f nil true)) + ([f unf] `(cljs.spec/spec-impl '~f ~f nil true ~unf))) (defmacro fspec "takes :args :ret and (optional) :fn kwargs whose values are preds diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 712572664..83316122b 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -36,6 +36,7 @@ (defprotocol Spec (conform* [spec x]) + (unform* [spec y]) (explain* [spec path via in x]) (gen* [spec overrides path rmap]) (with-gen* [spec gfn]) @@ -107,6 +108,13 @@ [spec x] (conform* (specize spec) x)) +(defn unform + "Given a spec and a value created by or compliant with a call to + 'conform' with the same spec, returns a value with all conform + destructuring undone." + [spec x] + (unform* (specize spec) x)) + (defn form "returns the spec as data" [spec] @@ -405,6 +413,17 @@ (recur ret ks)) ret))) ::invalid)) + (unform* [_ m] + (let [reg (registry)] + (loop [ret m, [k & ks :as keys] (c/keys m)] + (if keys + (if (contains? reg (keys->specs k)) + (let [cv (get m k) + v (unform (keys->specs k) cv)] + (recur (if (identical? cv v) ret (assoc ret k v)) + ks)) + (recur ret ks)) + ret)))) (explain* [_ path via in x] (if-not (map? x) {path {:pred 'map? :val x :via via :in in}} @@ -452,25 +471,31 @@ (defn ^:skip-wiki spec-impl "Do not call this directly, use 'spec'" - [form pred gfn cpred?] - (cond - (spec? pred) (cond-> pred gfn (with-gen gfn)) - (regex? pred) (regex-spec-impl pred gfn) - (named? pred) (cond-> (the-spec pred) gfn (with-gen gfn)) - :else - (reify - IFn - (-invoke [this x] (valid? this x)) - Spec - (conform* [_ x] (dt pred x form cpred?)) - (explain* [_ path via in x] - (when (= ::invalid (dt pred x form cpred?)) - {path {:pred (abbrev form) :val x :via via :in in}})) - (gen* [_ _ _ _] (if gfn - (gfn) - (gen/gen-for-pred pred))) - (with-gen* [_ gfn] (spec-impl form pred gfn cpred?)) - (describe* [_] form)))) + ([form pred gfn cpred?] (spec-impl form pred gfn cpred? nil)) + ([form pred gfn cpred? unc] + (cond + (spec? pred) (cond-> pred gfn (with-gen gfn)) + (regex? pred) (regex-spec-impl pred gfn) + (named? pred) (cond-> (the-spec pred) gfn (with-gen gfn)) + :else + (reify + IFn + (-invoke [this x] (valid? this x)) + Spec + (conform* [_ x] (dt pred x form cpred?)) + (unform* [_ x] (if cpred? + (if unc + (unc x) + (throw (js/Error. "no unform fn for conformer"))) + x)) + (explain* [_ path via in x] + (when (= ::invalid (dt pred x form cpred?)) + {path {:pred (abbrev form) :val x :via via :in in}})) + (gen* [_ _ _ _] (if gfn + (gfn) + (gen/gen-for-pred pred))) + (with-gen* [_ gfn] (spec-impl form pred gfn cpred?)) + (describe* [_] form))))) (defn ^:skip-wiki multi-spec-impl "Do not call this directly, use 'multi-spec'" @@ -492,6 +517,9 @@ (conform* [_ x] (if-let [pred (predx x)] (dt pred x form) ::invalid)) + (unform* [_ x] (if-let [pred (predx x)] + (unform pred x) + (throw (js/Error. (str "No method of: " form " for dispatch value: " (dval x)))))) (explain* [_ path via in x] (let [dv (dval x) path (conj path dv)] @@ -539,6 +567,16 @@ ::invalid (recur (if (identical? cv v) ret (assoc ret i cv)) (inc i)))))))) + (unform* [_ x] + (assert (c/and (vector? x) + (= (count x) (count preds)))) + (loop [ret x, i 0] + (if (= i (count x)) + ret + (let [cv (x i) + v (unform (preds i) cv)] + (recur (if (identical? cv v) ret (assoc ret i v)) + (inc i)))))) (explain* [_ path via in x] (cond (not (vector? x)) @@ -570,6 +608,7 @@ "Do not call this directly, use 'or'" [keys forms preds gfn] (let [id (random-uuid) + kps (zipmap keys preds) cform (fn [x] (loop [i 0] (if (< i (count preds)) @@ -584,6 +623,7 @@ (-invoke [this x] (valid? this x)) Spec (conform* [_ x] (cform x)) + (unform* [_ [k x]] (unform (kps k) x)) (explain* [this path via in x] (when-not (valid? this x) (apply merge @@ -636,6 +676,7 @@ (-invoke [this x] (valid? this x)) Spec (conform* [_ x] (and-preds x preds forms)) + (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) (explain* [_ path via in x] (explain-pred-list forms preds path via in x)) (gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms)))) (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) @@ -720,7 +761,7 @@ (defn ^:skip-wiki maybe-impl "Do not call this directly, use '?'" - [p form] (alt* [p (accept ::nil)] nil [form ::nil])) + [p form] (assoc (alt* [p (accept ::nil)] nil [form ::nil]) :maybe form)) (defn- noret? [p1 pret] (c/or (= pret ::nil) @@ -762,6 +803,27 @@ r (if (nil? p0) ::nil (preturn p0))] (if k0 [k0 r] r))))) +(defn- op-unform [p x] + ;;(prn {:p p :x x}) + (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms rep+ maybe] :as p} (reg-resolve p) + kps (zipmap ks ps)] + (case op + ::accept [ret] + nil [(unform p x)] + ::amp (let [px (reduce #(unform %2 %1) x (reverse ps))] + (op-unform p1 px)) + ::rep (mapcat #(op-unform p1 %) x) + ::pcat (if rep+ + (mapcat #(op-unform p0 %) x) + (mapcat (fn [k] + (when (contains? x k) + (op-unform (kps k) (get x k)))) + ks)) + ::alt (if maybe + [(unform p0 x)] + (let [[k v] x] + (op-unform (kps k) v)))))) + (defn- add-ret [p r k] (let [{:keys [::op ps splice] :as p} (reg-resolve p) prop #(let [ret (preturn p)] @@ -792,7 +854,7 @@ (when (accept-nil? p1) (deriv (rep* p2 p2 (add-ret p1 ret nil) splice forms) x))))))) (defn- op-describe [p] - (let [{:keys [::op ps ks forms splice p1 rep+] :as p} (reg-resolve p)] + (let [{:keys [::op ps ks forms splice p1 rep+ maybe] :as p} (reg-resolve p)] ;;(prn {:op op :ks ks :forms forms :p p}) (when p (case op @@ -802,7 +864,9 @@ ::pcat (if rep+ (list `+ rep+) (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) (c/or (seq forms) (repeat nil))))) - ::alt (cons `alt (mapcat vector ks forms)) + ::alt (if maybe + (list `? maybe) + (cons `alt (mapcat vector ks forms))) ::rep (list (if splice `+ `*) forms))))) (defn- op-explain [form p path via in input] @@ -943,6 +1007,7 @@ (if (c/or (nil? x) (coll? x)) (re-conform re (seq x)) ::invalid)) + (unform* [_ x] (op-unform re x)) (explain* [_ path via in x] (if (c/or (nil? x) (coll? x)) (re-explain path via in re (seq x)) @@ -989,6 +1054,7 @@ (conform* [_ f] (if (fn? f) (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) ::invalid)) + (unform* [_ f] f) (explain* [_ path via in f] (if (fn? f) (let [args (validate-fn f specs 100)] @@ -1018,7 +1084,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (cljs.spec/def ::any (cljs.spec/spec (constantly true) :gen gen/any)) -(cljs.spec/def ::kvs->map (cljs.spec/conformer #(zipmap (map ::k %) (map ::v %)))) +(cljs.spec/def ::kvs->map (cljs.spec/conformer #(zipmap (map ::k %) (map ::v %)) #(map (fn [[k v]] {::k k ::v v}) %))) (defn exercise "generates a number (default 10) of values compatible with spec and maps conform over them, From 8f1f977224a644bb6681d17876c73d4a12154792 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 6 Jun 2016 17:52:48 -0400 Subject: [PATCH 1824/4033] CLJS-1660: cljs.spec: Always return var from instrument / unstrument --- src/main/cljs/cljs/spec.cljc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 120b71ba7..22287a62f 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -385,8 +385,9 @@ specified, return speced vars from all namespaces." [v] (let [v (if-not (seq? v) (list 'var v) v) sym (second v)] - `(when-let [checked# (cljs.spec/instrument* ~v)] - (set! ~sym checked#) + `(do + (when-let [checked# (cljs.spec/instrument* ~v)] + (set! ~sym checked#)) ~v))) (defmacro unstrument @@ -394,8 +395,9 @@ specified, return speced vars from all namespaces." [v] (let [v (if-not (seq? v) (list 'var v) v) sym (second v)] - `(when-let [raw# (cljs.spec/unstrument* ~v)] - (set! ~sym raw#) + `(do + (when-let [raw# (cljs.spec/unstrument* ~v)] + (set! ~sym raw#)) ~v))) (defmacro instrument-ns From 61308c71af4149b277a29e3dc7a6837d27dc3b19 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jun 2016 18:04:44 -0400 Subject: [PATCH 1825/4033] compute read/write opts for transit if possible, handle JSValue --- src/main/clojure/cljs/analyzer.cljc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6abc45741..419c69000 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -55,6 +55,24 @@ (def ^:dynamic *file-defs* nil) +#?(:clj + (def transit-read-opts + (util/compile-if (import '[com.cognitect.transit ReadHandler]) + {:handlers + {"cljs/js" + (reify com.cognitect.transit.ReadHandler + (fromRep [_ v] (JSValue. v)))}}))) + +#?(:clj + (def transit-write-opts + (util/compile-if (import '[com.cognitect.transit WriteHandler]) + {:handlers + {JSValue + (reify com.cognitect.transit.WriteHandler + (tag [_ _] "cljs/js") + (rep [_ js] (.val ^JSValue js)) + (stringRep [_ _] nil))}}))) + #?(:clj (def transit (delay From 463de005b81d4a00951188e8b8d38a61d684c18e Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Jun 2016 18:46:58 -0400 Subject: [PATCH 1826/4033] use the transit read / write options --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 419c69000..8bad1e07b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2868,7 +2868,8 @@ (pr-str analysis))) "json" (when-let [{:keys [writer write]} @transit] (write - (writer (FileOutputStream. cache-file) :json) + (writer (FileOutputStream. cache-file) :json + transit-write-opts) analysis)))) (when src (.setLastModified ^File cache-file (util/last-modified src)))))) @@ -2936,7 +2937,8 @@ cached-ns (case ext "edn" (edn/read-string (slurp cache)) "json" (let [{:keys [reader read]} @transit] - (read (reader (io/input-stream cache) :json))))] + (read (reader (io/input-stream cache) :json + transit-read-opts))))] (when (or *verbose* (:verbose opts)) (util/debug-prn "Reading analysis cache for" (str res))) (swap! env/*compiler* From 73ab8ff8f4610a6f11cf64cc09e7173dcada5dc0 Mon Sep 17 00:00:00 2001 From: Patrick Killean Date: Tue, 7 Jun 2016 22:54:41 -0400 Subject: [PATCH 1827/4033] new preds, specs, and gens --- src/main/cljs/cljs/core.cljs | 75 +++++++++++++++++++++++++ src/main/cljs/cljs/spec.cljc | 19 +++++++ src/main/cljs/cljs/spec.cljs | 14 +++++ src/main/cljs/cljs/spec/impl/gen.cljs | 26 ++++++++- src/test/cljs/cljs/core_test.cljs | 8 ++- src/test/cljs/cljs/predicates_test.cljs | 57 +++++++++++++++++++ 6 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 src/test/cljs/cljs/predicates_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8420800fa..a44182b3f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1263,6 +1263,23 @@ (garray/defaultCompare (.valueOf this) (.valueOf other)) (throw (js/Error. (str "Cannot compare " this " to " other)))))) +(defprotocol Inst + (inst-ms* [inst])) + +(extend-protocol Inst + js/Date + (inst-ms* [inst] (.getTime inst))) + +(defn inst-ms + "Return the number of milliseconds since January 1, 1970, 00:00:00 GMT" + [inst] + (inst-ms* inst)) + +(defn ^boolean inst? + "Return true if x satisfies Inst" + [x] + (satisfies? Inst x)) + (extend-type number IEquiv (-equiv [x o] (identical? x o))) @@ -2064,6 +2081,10 @@ reduces them without incurring seq initialization" "Returns true if x is the value true, false otherwise." [x] (cljs.core/true? x)) +(defn ^boolean boolean? + "Return true if x is a Boolean" + [x] (or (cljs.core/true? x) (cljs.core/false? x))) + (defn ^boolean undefined? "Returns true if x identical to the JavaScript undefined value." [x] @@ -2102,6 +2123,26 @@ reduces them without incurring seq initialization" (not (identical? n js/Infinity)) (== (js/parseFloat n) (js/parseInt n 10)))) +(defn ^boolean long? + "Return true if x is an instance of goog.math.Long" + [x] (instance? goog.math.Long x)) + +(defn ^boolean pos-long? + "Return true if x is a positive Long" + [x] (and (instance? goog.math.Long x) + (not (.isNegative x)) + (not (.isZero x)))) + +(defn ^boolean neg-long? + "Return true if x is a negative Long" + [x] (and (instance? goog.math.Long x) + (.isNegative x))) + +(defn ^boolean nat-long? + "Return true if x is a non-negative Long" + [x] (and (instance? goog.math.Long x) + (or (not (.isNegative x)) (.isZero x)))) + (defn ^boolean contains? "Returns true if key is present in the given collection, otherwise returns false. Note that for numerically indexed collections like @@ -3086,6 +3127,34 @@ reduces them without incurring seq initialization" (-namespace ^not-native x) (throw (js/Error. (str "Doesn't support namespace: " x))))) +(defn ^boolean ident? + "Return true if x is a symbol or keyword" + [x] (or (keyword? x) (symbol? x))) + +(defn ^boolean simple-ident? + "Return true if x is a symbol or keyword without a namespace" + [x] (and (ident? x) (nil? (namespace x)))) + +(defn ^boolean qualified-ident? + "Return true if x is a symbol or keyword with a namespace" + [x] (and (ident? x) (namespace x) true)) + +(defn ^boolean simple-symbol? + "Return true if x is a symbol without a namespace" + [x] (and (symbol? x) (nil? (namespace x)))) + +(defn ^boolean qualified-symbol? + "Return true if x is a symbol with a namespace" + [x] (and (symbol? x) (namespace x) true)) + +(defn ^boolean simple-keyword? + "Return true if x is a keyword without a namespace" + [x] (and (keyword? x) (nil? (namespace x)))) + +(defn ^boolean qualified-keyword? + "Return true if x is a keyword with a namespace" + [x] (and (keyword? x) (namespace x) true)) + (defn keyword "Returns a Keyword with the given namespace and name. Do not use : in the keyword strings, it will be added automatically." @@ -10090,8 +10159,11 @@ reduces them without incurring seq initialization" [multifn] (-dispatch-fn multifn)) ;; UUID +(defprotocol IUUID "A marker protocol for UUIDs") (deftype UUID [uuid ^:mutable __hash] + IUUID + Object (toString [_] uuid) (equiv [this other] @@ -10131,6 +10203,9 @@ reduces them without incurring seq initialization" (hex) (hex) (hex) (hex) (hex) (hex) (hex) (hex)))))) +(defn ^boolean uuid? + [x] (implements? IUUID x)) + ;;; ExceptionInfo (defn- pr-writer-ex-info [obj writer opts] diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 22287a62f..8149aadab 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -377,6 +377,25 @@ specified, return speced vars from all namespaces." [kpred vpred] `(and (coll-of (tuple ~kpred ~vpred) {}) map?)) +(defmacro inst-in + "Returns a spec that validates insts in the range from start + (inclusive) to end (exclusive)." + [start end] + `(let [st# (inst-ms ~start) + et# (inst-ms ~end) + mkdate# (fn [d#] (js/Date. d#))] + (spec (and inst? #(inst-in-range? ~start ~end %)) + :gen (fn [] + (gen/fmap mkdate# + (gen/large-integer* {:min st# :max et#})))))) + +(defmacro long-in + "Returns a spec that validates longs in the range from start + (inclusive) to end (exclusive)." + [start end] + `(spec (and c/long? #(long-in-range? ~start ~end %)) + :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) + (defmacro instrument "Instruments the var at v, a var or symbol, to check specs registered with fdef. Wraps the fn at v to check :args/:ret/:fn diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 83316122b..feda707bb 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1115,3 +1115,17 @@ (gen/fmap #(if (vector? init) % (into init %)) (gen/vector (gen pred)))))) + +(defn inst-in-range? + "Return true if inst at or after start and before end" + [start end inst] + (c/and (inst? inst) + (let [t (inst-ms inst)] + (c/and (<= (inst-ms start) t) (< t (inst-ms end)))))) + +(defn long-in-range? + "Return true if start <= val and val < end" + [start end val] + (c/and (long? val) + (.lessThanOrEqual (goog.math.Long.fromNumber start) val) + (.lessThan val (goog.math.Long.fromNumber end)))) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index 28cbcefaf..8230b6924 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -67,7 +67,8 @@ ; (throw (js/Error. (str "Var " s " is not a generator")))))) (lazy-combinators hash-map list map not-empty set vector fmap elements - bind choose one-of such-that tuple sample return) + bind choose one-of such-that tuple sample return + large-integer*) (lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double int keyword keyword-ns large-integer ratio simple-type simple-type-printable @@ -80,16 +81,39 @@ gens, each of which should generate something sequential." (fmap #(apply concat %) (apply tuple gens))) +(defn- ^boolean qualified? [ident] (not (nil? (namespace ident)))) + (def ^:private gen-builtins (c/delay (let [simple (simple-type-printable)] {number? (one-of [(large-integer) (double)]) integer? (large-integer) + long? (large-integer) + pos-long? (large-integer* {:min 1}) + neg-long? (large-integer* {:max -1}) + nat-long? (large-integer* {:min 0}) ;float? (double) string? (string-alphanumeric) + ident? (one-of [(keyword-ns) (symbol-ns)]) + simple-ident? (one-of [(keyword) (symbol)]) + qualified-ident? (such-that qualified? (one-of [(keyword-ns) (symbol-ns)])) keyword? (keyword-ns) + simple-keyword? (keyword) + qualified-keyword? (such-that qualified? (keyword-ns)) symbol? (symbol-ns) + simple-symbol? (symbol) + qualified-symbol? (such-that qualified? (symbol-ns)) + uuid? (uuid) + inst? (fmap #(js/Date. %) + (large-integer)) + seqable? (one-of [(return nil) + (list simple) + (vector simple) + (map simple simple) + (set simple) + (string-alphanumeric)]) + indexed? (vector simple) map? (map simple simple) vector? (vector simple) list? (list simple) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 92f27b550..ab64102d9 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -702,13 +702,17 @@ (deftest test-booleans (testing "Testing boolean predicates" - (is (= [true false true false false false] + (is (= [true false true false false false true true false false] [(true? true) (true? false) (false? false) (false? true) (true? js/undefined) - (false? js/undefined)])))) + (false? js/undefined) + (boolean? true) + (boolean? false) + (boolean? nil) + (boolean? js/undefined)])))) (deftest test-fn-with-metadata (let [f (fn [x] (* x 2)) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs new file mode 100644 index 000000000..62602b1e7 --- /dev/null +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -0,0 +1,57 @@ +(ns cljs.predicates-test + (:require [cljs.test :refer-macros [deftest is]]) + (:import [goog.math Long])) + + +(def pred-val-table + (let [now (js/Date.) + uuid (uuid nil)] + [[identity boolean? indexed? seqable? ident? uuid? inst? simple-ident? qualified-ident? simple-symbol? qualified-symbol? simple-keyword? qualified-keyword?] + [0 false false false false false false false false false false false false] + [1 false false false false false false false false false false false false] + [-1 false false false false false false false false false false false false] + [1.0 false false false false false false false false false false false false] + [true true false false false false false false false false false false false] + [[] false true true false false false false false false false false false] + [nil false false false false false false false false false false false false] + [{} false false true false false false false false false false false false] + [:foo false false false true false false true nil false false true nil] + [::foo false false false true false false false true false false false true] + ['foo false false false true false false true nil true nil false false] + ['foo/bar false false false true false false false true false true false false] + [uuid false false false false true false false false false false false false] + [now false false false false false true false false false false false false]])) + +(deftest test-preds + (let [[preds & rows] pred-val-table] + (doseq [row rows] + (let [v (first row)] + (dotimes [i (count row)] + (is (= ((nth preds i) v) (nth row i)) + (pr-str (list (nth preds i) v)))))))) + +(def int-val-table + (let [posint 10e10 + negint -10e10 + natl (goog.math.Long.getZero) + posl (goog.math.Long.fromNumber posint) + negl (goog.math.Long.fromNumber negint)] + [[identity neg? pos? integer? long? neg-long? pos-long? nat-long?] + [0 false false true false false false false ] + [1 false true true false false false false ] + [-1 true false true false false false false ] + [1.0 false true true false false false false ] + [-1.0 true false true false false false false ] + [posint false true true false false false false ] + [negint true false true false false false false ] + [natl false false false true false false true ] + [posl false true false true false true true ] + [negl true false false true true false false ]])) + +(deftest test-int-preds + (let [[preds & rows] int-val-table] + (doseq [row rows] + (let [v (first row)] + (dotimes [i (count row)] + (is (= ((nth preds i) v) (nth row i)) + (pr-str (list (nth preds i) v)))))))) \ No newline at end of file From c805d265e9b87b93dd8817aed74807ec775dbc49 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jun 2016 14:22:30 -0400 Subject: [PATCH 1828/4033] require goog.math.Long --- src/main/cljs/cljs/core.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a44182b3f..6b9737c2b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7,7 +7,8 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.core - (:require [goog.string :as gstring] + (:require goog.math.Long + [goog.string :as gstring] [goog.object :as gobject] [goog.array :as garray]) (:import [goog.string StringBuffer])) From 6796564a2fcd188f37dce3bef8dd66e3206cb2c5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Jun 2016 14:53:40 -0400 Subject: [PATCH 1829/4033] mark bootstrap helpers - note advanced compilation issues in comment --- src/main/cljs/cljs/core.cljs | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6b9737c2b..fb4896926 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10435,9 +10435,15 @@ reduces them without incurring seq initialization" ".." (demunge-str (str name)))))) -(defn- ns-lookup [ns-obj k] +;; ----------------------------------------------------------------------------- +;; Bootstrap helpers - incompatible with advanced compilation + +(defn- ns-lookup + "Bootstrap only." + [ns-obj k] (fn [] (gobject/get ns-obj k))) +;; Bootstrap only (deftype Namespace [obj name] Object (findInternedVar [this sym] @@ -10459,16 +10465,20 @@ reduces them without incurring seq initialization" (hash name))) (def - ^{:jsdoc ["@type {*}"]} + ^{:doc "Bootstrap only." :jsdoc ["@type {*}"]} NS_CACHE nil) -(defn- find-ns-obj* [ctxt xs] +(defn- find-ns-obj* + "Bootstrap only." + [ctxt xs] (cond (nil? ctxt) nil (nil? xs) ctxt :else (recur (gobject/get ctxt (first xs)) (next xs)))) -(defn find-ns-obj [ns] +(defn find-ns-obj + "Bootstrap only." + [ns] (let [munged-ns (munge (str ns)) segs (.split munged-ns ".")] (case *target* @@ -10487,7 +10497,9 @@ reduces them without incurring seq initialization" "default" (find-ns-obj* goog/global segs) (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) -(defn ns-interns* [sym] +(defn ns-interns* + "Bootstrap only." + [sym] (let [ns-obj (find-ns-obj sym) ns (Namespace. ns-obj sym)] (letfn [(step [ret k] @@ -10498,12 +10510,15 @@ reduces them without incurring seq initialization" (reduce step {} (js-keys ns-obj))))) (defn create-ns + "Bootstrap only." ([sym] (create-ns sym (find-ns-obj sym))) ([sym ns-obj] (Namespace. ns-obj sym))) -(defn find-ns [ns] +(defn find-ns + "Bootstrap only." + [ns] (when (nil? NS_CACHE) (set! NS_CACHE (atom {}))) (let [the-ns (get @NS_CACHE ns)] @@ -10515,7 +10530,9 @@ reduces them without incurring seq initialization" (swap! NS_CACHE assoc ns new-ns) new-ns)))))) -(defn find-macros-ns [ns] +(defn find-macros-ns + "Bootstrap only." + [ns] (when (nil? NS_CACHE) (set! NS_CACHE (atom {}))) (let [the-ns (get @NS_CACHE ns)] @@ -10531,5 +10548,7 @@ reduces them without incurring seq initialization" (swap! NS_CACHE assoc ns new-ns) new-ns)))))) -(defn ns-name [ns-obj] +(defn ns-name + "Bootstrap only." + [ns-obj] (.-name ns-obj)) From 391d5cf86fbbf3d110ef6bbfc263d8132518a172 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Jun 2016 09:57:47 -0400 Subject: [PATCH 1830/4033] same as Clojure b7199fb + a simple test --- src/main/cljs/cljs/spec.cljc | 2 +- src/main/cljs/cljs/spec.cljs | 10 ++++++---- src/test/cljs/cljs/spec_test.cljs | 25 +++++++++++++++++++++++++ src/test/cljs/cljs/test_runner.cljs | 6 ++++-- 4 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/test/cljs/cljs/spec_test.cljs diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 8149aadab..563475c8b 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -229,7 +229,7 @@ conjunction of the predicates, and any conforming they might perform." [re & preds] (let [pv (vec preds)] - `(cljs.spec/amp-impl ~re ~pv '~pv))) + `(cljs.spec/amp-impl ~re ~pv '~(mapv #(res &env %) pv)))) (defmacro conformer "takes a predicate function with the semantics of conform i.e. it should return either a diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index feda707bb..d254b41c7 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -779,9 +779,7 @@ ::amp (c/and (accept-nil? p1) (c/or (noret? p1 (preturn p1)) (let [ret (-> (preturn p1) (and-preds ps (next forms)))] - (if (= ret ::invalid) - nil - ret)))) + (not= ret ::invalid)))) ::rep (c/or (identical? p1 p2) (accept-nil? p1)) ::pcat (every? accept-nil? ps) ::alt (c/some accept-nil? ps)))) @@ -846,7 +844,11 @@ nil (let [ret (dt p x p)] (when-not (= ::invalid ret) (accept ret))) ::amp (when-let [p1 (deriv p1 x)] - (amp-impl p1 ps forms)) + (if (= ::accept (::op p1)) + (let [ret (-> (preturn p1) (and-preds ps (next forms)))] + (when-not (= ret ::invalid) + (accept ret))) + (amp-impl p1 ps forms))) ::pcat (alt2 (pcat* {:ps (cons (deriv p0 x) pr), :ks ks, :forms forms, :ret ret}) (when (accept-nil? p0) (deriv (pcat* {:ps pr, :ks kr, :forms (next forms), :ret (add-ret p0 ret k0)}) x))) ::alt (alt* (map #(deriv % x) ps) ks forms) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs new file mode 100644 index 000000000..22746f512 --- /dev/null +++ b/src/test/cljs/cljs/spec_test.cljs @@ -0,0 +1,25 @@ +(ns cljs.spec-test + (:require [cljs.spec :as s] + [cljs.test :as test :refer-macros [deftest is]])) + +(s/def ::even? (s/and number? even?)) +(s/def ::odd? (s/and number? odd?)) + +(def s2 + (s/cat :forty-two #{42} + :odds (s/+ ::odd?) + :m (s/keys :req-un [::a ::b ::c]) + :oes (s/& (s/* (s/cat :o ::odd? :e ::even?)) #(< (count %) 3)) + :ex (s/* (s/alt :odd ::odd? :even ::even?)))) + +(deftest test-roundtrip + (let [xs [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]] + (is (= xs (s/unform s2 (s/conform s2 xs)))))) + +(comment + + (s/conform s2 [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]) + (s/unform s2 + (s/conform s2 [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11])) + + ) \ No newline at end of file diff --git a/src/test/cljs/cljs/test_runner.cljs b/src/test/cljs/cljs/test_runner.cljs index 7a88b5b0e..1e93a5cec 100644 --- a/src/test/cljs/cljs/test_runner.cljs +++ b/src/test/cljs/cljs/test_runner.cljs @@ -14,7 +14,8 @@ [cljs.keyword-test] [cljs.import-test] [cljs.ns-test.foo] - [cljs.pprint])) + [cljs.pprint] + [cljs.spec-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -34,4 +35,5 @@ 'cljs.ns-test.foo 'foo.ns-shadow-test 'cljs.import-test - 'cljs.pprint) + 'cljs.pprint + 'cljs.spec-test) From 1bece47344089934f46db509756b32b102f1309b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 2 Jun 2016 00:19:57 -0400 Subject: [PATCH 1831/4033] CLJS-1661: cljs.spec: non-spec'ed fn var printing --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index d254b41c7..c47c967af 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -322,7 +322,7 @@ (defn- no-fn-specs [v specs] - (ex-info (str "Fn at " v " is not spec'ed.") + (ex-info (str "Fn at " (pr-str v) " is not spec'ed.") {:var v :specs specs})) (def ^:private instrumented-vars From 868c6e426df0a021b3223b47f08ddfa01e492608 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 31 May 2016 16:24:30 -0400 Subject: [PATCH 1832/4033] CLJS-1656: Self-host: cljs.spec: speced-vars* fn not resolving Resolve this var in self-hosted ClojureScript by qualifying with the cljs.spec$macros pseudo-namespace. --- src/main/cljs/cljs/spec/test.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 17375698b..0ec5dba97 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -19,7 +19,8 @@ `(cljs.spec.test/run-tests '~ana/*cljs-ns*)) ([& ns-syms] `(cljs.spec.test/run-var-tests - (->> ~(spec/speced-vars* ns-syms) + (->> #?(:clj ~(spec/speced-vars* ns-syms) + :cljs ~(cljs.spec$macros/speced-vars* ns-syms)) (filter (fn [v#] (:args (cljs.spec/fn-specs v#)))))))) (defmacro run-all-tests @@ -27,4 +28,5 @@ for all speced vars. Prints per-test results to *out*, and returns a map with :test,:pass,:fail, and :error counts." [] - `(cljs.spec.test/run-var-tests ~(spec/speced-vars*))) + `(cljs.spec.test/run-var-tests #?(:clj ~(spec/speced-vars*) + :cljs ~(cljs.spec$macros/speced-vars*)))) From b54f27e8282134b2a775f335b9d380b12ef983b6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Jun 2016 10:26:24 -0400 Subject: [PATCH 1833/4033] CLJS-1655: cljs.spec: conformer docstring indicates :clojure.spec/invalid --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 563475c8b..e3988edf6 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -233,7 +233,7 @@ (defmacro conformer "takes a predicate function with the semantics of conform i.e. it should return either a - (possibly converted) value or :clojure.spec/invalid, and returns a + (possibly converted) value or :cljs.spec/invalid, and returns a spec that uses it as a predicate/conformer. Optionally takes a second fn that does unform of result of first" ([f] `(cljs.spec/spec-impl '~f ~f nil true)) From 81fb24e2adc96c53834486b706b975336a4a0d23 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Jun 2016 10:28:31 -0400 Subject: [PATCH 1834/4033] CLJS-1654: cljs.spec: var name in s/fdef non-conformance --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index c47c967af..de0c47435 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -288,7 +288,7 @@ (let [ed (assoc (explain-data* spec [role] [] [] data) ::args args)] (throw (ex-info - (str "Call to " v " did not conform to spec:\n" (with-out-str (explain-out ed))) + (str "Call to " (pr-str v) " did not conform to spec:\n" (with-out-str (explain-out ed))) ed))) conformed)))] (doto From ceb307a65aaafda260f0e76d092485932ab88edc Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Wed, 25 May 2016 09:47:24 -0400 Subject: [PATCH 1835/4033] CLJS-1648: Getting Source Info into ex-info data for Analysis Errors --- src/main/clojure/cljs/analyzer.cljc | 20 +++++++++----------- src/main/clojure/cljs/repl.cljc | 6 +++++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 8bad1e07b..631786a57 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -581,18 +581,16 @@ (defn source-info ([env] - (when-let [line (:line env)] - {:file (if (= (-> env :ns :name) 'cljs.core) - "cljs/core.cljs" - *cljs-file*) - :line (get-line name env) - :column (get-col name env)})) + (when (:line env) + (source-info nil env))) ([name env] - {:file (if (= (-> env :ns :name) 'cljs.core) - "cljs/core.cljs" - *cljs-file*) - :line (get-line name env) - :column (get-col name env)})) + (cond-> {:file (if (= (-> env :ns :name) 'cljs.core) + "cljs/core.cljs" + *cljs-file*) + :line (get-line name env) + :column (get-col name env)} + (:root-source-info env) + (merge (select-keys env [:root-source-info]))))) (defn message [env s] (str s diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 2f5a25bd0..f928de068 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -447,7 +447,11 @@ (evaluate-form repl-env env filename form wrap *repl-opts*)) ([repl-env env filename form wrap opts] (binding [ana/*cljs-file* filename] - (let [ast (ana/analyze env form nil opts) + (let [env (assoc env + :root-source-info + {:source-type :fragment + :source-form form }) + ast (ana/analyze env form nil opts) js (comp/emit-str ast) def-emits-var (:def-emits-var opts) wrap-js From 2682a4a53b0002db35c9fa17879dab66b0a614ca Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jun 2016 15:12:30 -0400 Subject: [PATCH 1836/4033] CLJS-1673: Can't spec instrument a multimethod fix multimethods case. add tests for multimethod case and multi-arity fn case. --- src/main/cljs/cljs/spec.cljs | 4 ++-- src/test/cljs/cljs/spec_test.cljs | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index de0c47435..c90da9aa3 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -291,7 +291,7 @@ (str "Call to " (pr-str v) " did not conform to spec:\n" (with-out-str (explain-out ed))) ed))) conformed)))] - (doto + (cond-> (c/fn [& args] (if *instrument-enabled* @@ -305,7 +305,7 @@ (conform! v :fn (:fn specs) {:args cargs :ret cret} args)) ret))) (apply f args))) - (gobj/extend f)))) + (not (instance? MultiFn f)) (doto (gobj/extend f))))) (defn- macroexpand-check [v args] diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 22746f512..bcb71c342 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -1,6 +1,6 @@ (ns cljs.spec-test (:require [cljs.spec :as s] - [cljs.test :as test :refer-macros [deftest is]])) + [cljs.test :as test :refer-macros [deftest is run-tests]])) (s/def ::even? (s/and number? even?)) (s/def ::odd? (s/and number? odd?)) @@ -16,6 +16,33 @@ (let [xs [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]] (is (= xs (s/unform s2 (s/conform s2 xs)))))) +(defn adder + ([a] a) + ([a b] (+ a b))) + +(s/fdef adder + :args (s/cat :a integer? :b (s/? integer?)) + :ret integer?) + +(s/instrument #'adder) + +(deftest test-multi-arity-instrument + (is (= 1 (adder 1))) + (is (= 3 (adder 1 2))) + (is (thrown? js/Error (adder "foo")))) + +(defmulti testmm :type) +(defmethod testmm :default [_]) +(defmethod testmm :good [_] "good") + +(s/fdef testmm :args (s/cat :m map?) :ret string?) + +(s/instrument #'testmm) + +(deftest test-multifn-instrument + (is (= "good" (testmm {:type :good}))) + (is (thrown? js/Error (testmm "foo")))) + (comment (s/conform s2 [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]) From 4f9fb9e1810a0948767a8598a8313372becf5747 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jun 2016 15:45:38 -0400 Subject: [PATCH 1837/4033] validate :protocol meta, it must be a symbol - discard otherwise --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 631786a57..96286ee74 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1102,6 +1102,9 @@ :catch catch :children [try catch finally]})) +(defn valid-proto [x] + (when (symbol? x) x)) + (defmethod parse 'def [op env form name _] (let [pfn (fn @@ -1112,7 +1115,7 @@ sym (:sym args) sym-meta (meta sym) tag (-> sym meta :tag) - protocol (-> sym meta :protocol) + protocol (-> sym meta :protocol valid-proto) dynamic (-> sym meta :dynamic) ns-name (-> env :ns :name) locals (:locals env) From aaa281d5cfef89385a484ad5f204ce6973f01222 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jun 2016 17:08:40 -0400 Subject: [PATCH 1838/4033] bump Google Closure Library dep --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index a28d7382b..524da16fa 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20151016-61277aea + 0.0-20160609-f42b4a24 org.clojure diff --git a/project.clj b/project.clj index 2935d4275..ebd35bed3 100644 --- a/project.clj +++ b/project.clj @@ -13,7 +13,7 @@ [org.clojure/tools.reader "1.0.0-beta1"] [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.285"] - [org.clojure/google-closure-library "0.0-20151016-61277aea"] + [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] [com.google.javascript/closure-compiler "v20160315"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} diff --git a/script/bootstrap b/script/bootstrap index b778b13e3..d3594c269 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,7 +6,7 @@ CLOJURE_RELEASE="1.9.0-alpha4" CLOSURE_RELEASE="20160315" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" -GCLOSURE_LIB_RELEASE="0.0-20151016-61277aea" +GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.0.0-beta1" From 34baf6b68acb1b1bbad1e2c2c0d27318a5cd04ef Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Jun 2016 17:13:48 -0400 Subject: [PATCH 1839/4033] same as Clojure 0bc837b9 --- src/main/cljs/cljs/spec.cljs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index c90da9aa3..9c09c6b15 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1052,6 +1052,15 @@ (reify IFn (-invoke [this x] (valid? this x)) + ILookup + (-lookup [this k] + (-lookup this k nil)) + (-lookup [_ k not-found] + (case k + :args argspec + :ret retspec + :fn fnspec + not-found)) Spec (conform* [_ f] (if (fn? f) (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) From 178c2c0daa5a52691d9e591425d55273e6176db3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 11 Jun 2016 11:44:01 -0400 Subject: [PATCH 1840/4033] CLJS-1680: Self-host: Don't require items no longer provided by Closure --- src/test/self/self_parity/auxiliary.cljs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs index db301615d..c1a5c4daf 100644 --- a/src/test/self/self_parity/auxiliary.cljs +++ b/src/test/self/self_parity/auxiliary.cljs @@ -11,7 +11,6 @@ goog.Throttle goog.Timer goog.Uri - goog.array.ArrayLike goog.color goog.color.Hsl goog.color.Hsv @@ -25,7 +24,6 @@ goog.crypt.Cbc goog.crypt.Hash goog.crypt.Hmac - goog.crypt.JpegEncoder goog.crypt.Md5 goog.crypt.Sha1 goog.crypt.Sha2 From def60a2ee3e51a3036d9cc583a2ce70c125bf8fa Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 11 Jun 2016 09:46:50 -0400 Subject: [PATCH 1841/4033] CLJS-1679: Self-host: Incorporate spec tests Adds cljs.spec-test to the self-host test suite To do so involves some adjustments to self-host loading in this suite (a workaround for CLJS-1657 for some of the spec namespaces) and the need to skip loading code for cljs.core macros namespace. This also catches and fixes one production code issue in the cljs.spec macros namespace: the symbol *instrument-enabled* needs to be qualified so that it refers to the runtime namespace. --- src/main/cljs/cljs/spec.cljc | 2 +- src/test/self/self_parity/test.cljs | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index e3988edf6..33d49dd73 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -342,7 +342,7 @@ specified, return speced vars from all namespaces." (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." [& body] - `(binding [*instrument-enabled* nil] + `(binding [cljs.spec/*instrument-enabled* nil] ~@body)) (defmacro keys* diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 538062325..bf8bbe0ad 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -160,7 +160,8 @@ technical issues)." [name macros] ((if macros - #{'cljs.pprint + #{'cljs.core + 'cljs.pprint 'cljs.env.macros 'cljs.analyzer.macros 'cljs.compiler.macros} @@ -228,6 +229,14 @@ :verbose false} cb)) +(defn prime-analysis-cache-for-implicit-macro-loading + "Supports priming analysis cache in order to work around + http://dev.clojure.org/jira/browse/CLJS-1657" + [st ns-sym] + (swap! st assoc-in [::cljs.analyzer/namespaces ns-sym :require-macros] {ns-sym ns-sym})) + +;; Test suite runner + (defn run-tests "Runs the tests." [] @@ -235,6 +244,8 @@ ;; don't yet run in bootstrapped ClojureScript. These are commented ;; out below and can be uncommented as fixed. (let [st (cljs/empty-state)] + (prime-analysis-cache-for-implicit-macro-loading st 'cljs.spec) + (prime-analysis-cache-for-implicit-macro-loading st 'cljs.spec.impl.gen) (eval-form st 'cljs.user '(ns parity.core (:require [cljs.test :refer-macros [run-tests]] @@ -252,7 +263,8 @@ #_[cljs.keyword-test] [cljs.import-test] [cljs.ns-test.foo] - #_[cljs.pprint])) + #_[cljs.pprint] + [cljs.spec-test])) (fn [{:keys [value error]}] (if error (prn error) @@ -272,7 +284,8 @@ 'cljs.ns-test.foo 'foo.ns-shadow-test 'cljs.import-test - #_'cljs.pprint) + #_'cljs.pprint + 'cljs.spec-test) (fn [{:keys [value error]}] (when error (prn error))))))))) From 0e608c97c36b26d932370d0ebf614cbc55fbf3d8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 15 Jun 2016 14:02:33 -0400 Subject: [PATCH 1842/4033] exclude boolean? --- src/main/clojure/cljs/util.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 2b4bb095c..d0d5359d2 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -7,6 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.util + (:refer-clojure :exclude [boolean?]) (:require [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] From 733ff827c916ae2341cc9b794fff4275400e035c Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jun 2016 17:38:37 -0400 Subject: [PATCH 1843/4033] same as Clojure f571c4bb --- src/main/cljs/cljs/spec.cljc | 13 ++++++++----- src/main/cljs/cljs/spec.cljs | 11 ++++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 33d49dd73..89b25ec10 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -153,9 +153,10 @@ (s/or :even even? :small #(< % 42)) - Returns a destructuring spec that - returns a vector containing the key of the first matching pred and the - corresponding value." + Returns a destructuring spec that returns a map entry containing the + key of the first matching pred and the corresponding value. Thus the + 'key' and 'val' functions can be used to refer generically to the + components of the tagged return." [& key-pred-forms] (let [pairs (partition 2 key-pred-forms) keys (mapv first pairs) @@ -197,8 +198,10 @@ (s/alt :even even? :small #(< % 42)) - Returns a regex op that returns a vector containing the key of the - first matching pred and the corresponding value." + Returns a regex op that returns a map entry containing the key of the + first matching pred and the corresponding value. Thus the + 'key' and 'val' functions can be used to refer generically to the + components of the tagged return." [& key-pred-forms] (let [pairs (partition 2 key-pred-forms) keys (mapv first pairs) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 9c09c6b15..4529139f1 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -603,6 +603,11 @@ (with-gen* [_ gfn] (tuple-impl forms preds gfn)) (describe* [_] `(tuple ~@forms))))) +(defn- tagged-ret [v] + (specify! v + IMapEntry + (-key [_] (-nth v 0)) + (-val [_] (-nth v 1)))) (defn ^:skip-wiki or-spec-impl "Do not call this directly, use 'or'" @@ -616,7 +621,7 @@ (let [ret (dt pred x (nth forms i))] (if (= ::invalid ret) (recur (inc i)) - [(keys i) ret]))) + (tagged-ret [(keys i) ret])))) ::invalid)))] (reify IFn @@ -747,7 +752,7 @@ (if (nil? pr) (if k1 (if (accept? p1) - (accept [k1 (:ret p1)]) + (accept (tagged-ret [k1 (:ret p1)])) ret) p1) ret))))) @@ -799,7 +804,7 @@ ::pcat (add-ret p0 ret k) ::alt (let [[[p0] [k0]] (filter-alt ps ks forms accept-nil?) r (if (nil? p0) ::nil (preturn p0))] - (if k0 [k0 r] r))))) + (if k0 (tagged-ret [k0 r]) r))))) (defn- op-unform [p x] ;;(prn {:p p :x x}) From 2d20b331072af6b9b7a87147f48bedc2c8e4a48b Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jun 2016 17:44:24 -0400 Subject: [PATCH 1844/4033] same as Clojure 43e1c7f3b --- src/main/cljs/cljs/spec.cljs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 4529139f1..9864a3332 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -68,6 +68,14 @@ (with-name spec k))))) k)) +(defn- reg-resolve! + "returns the spec/regex at end of alias chain starting with k, throws if not found, k if k not ident" + [k] + (if (ident? k) + (c/or (reg-resolve k) + (throw (js/Error. (str "Unable to resolve spec: " k)))) + k)) + (defn spec? "returns x if x is a spec object, else logical false" [x] @@ -770,14 +778,14 @@ (defn- noret? [p1 pret] (c/or (= pret ::nil) - (c/and (#{::rep ::pcat} (::op (reg-resolve p1))) ;;hrm, shouldn't know these + (c/and (#{::rep ::pcat} (::op (reg-resolve! p1))) ;;hrm, shouldn't know these (empty? pret)) nil)) (declare preturn) (defn- accept-nil? [p] - (let [{:keys [::op ps p1 p2 forms] :as p} (reg-resolve p)] + (let [{:keys [::op ps p1 p2 forms] :as p} (reg-resolve! p)] (case op ::accept true nil nil @@ -792,7 +800,7 @@ (declare add-ret) (defn- preturn [p] - (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms] :as p} (reg-resolve p)] + (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms] :as p} (reg-resolve! p)] (case op ::accept ret nil nil @@ -808,7 +816,7 @@ (defn- op-unform [p x] ;;(prn {:p p :x x}) - (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms rep+ maybe] :as p} (reg-resolve p) + (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms rep+ maybe] :as p} (reg-resolve! p) kps (zipmap ks ps)] (case op ::accept [ret] @@ -828,7 +836,7 @@ (op-unform (kps k) v)))))) (defn- add-ret [p r k] - (let [{:keys [::op ps splice] :as p} (reg-resolve p) + (let [{:keys [::op ps splice] :as p} (reg-resolve! p) prop #(let [ret (preturn p)] (if (empty? ret) r ((if splice into conj) r (if k {k ret} ret))))] (case op @@ -842,7 +850,7 @@ (defn- deriv [p x] - (let [{[p0 & pr :as ps] :ps, [k0 & kr :as ks] :ks, :keys [::op p1 p2 ret splice forms] :as p} (reg-resolve p)] + (let [{[p0 & pr :as ps] :ps, [k0 & kr :as ks] :ks, :keys [::op p1 p2 ret splice forms] :as p} (reg-resolve! p)] (when p (case op ::accept nil @@ -861,7 +869,7 @@ (when (accept-nil? p1) (deriv (rep* p2 p2 (add-ret p1 ret nil) splice forms) x))))))) (defn- op-describe [p] - (let [{:keys [::op ps ks forms splice p1 rep+ maybe] :as p} (reg-resolve p)] + (let [{:keys [::op ps ks forms splice p1 rep+ maybe] :as p} (reg-resolve! p)] ;;(prn {:op op :ks ks :forms forms :p p}) (when p (case op @@ -879,7 +887,7 @@ (defn- op-explain [form p path via in input] ;;(prn {:form form :p p :path path :input input}) (let [[x :as input] input - {:keys [::op ps ks forms splice p1 p2] :as p} (reg-resolve p) + {:keys [::op ps ks forms splice p1 p2] :as p} (reg-resolve! p) via (if-let [name (spec-name p)] (conj via name) via) insufficient (fn [path form] {path {:reason "Insufficient input" @@ -932,7 +940,7 @@ (defn- re-gen [p overrides path rmap f] ;;(prn {:op op :ks ks :forms forms}) - (let [{:keys [::op ps ks p1 p2 forms splice ret id] :as p} (reg-resolve p) + (let [{:keys [::op ps ks p1 p2 forms splice ret id] :as p} (reg-resolve! p) rmap (if id (inck rmap id) rmap) ggens (fn [ps ks forms] (let [gen (fn [p k f] From b4f9d752df2d285f33713316a067df3461949f7f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jun 2016 17:46:07 -0400 Subject: [PATCH 1845/4033] Same as Clojure 84c16fd9f --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 9864a3332..65a546b07 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1104,7 +1104,7 @@ (assert (valid? argspec args) (with-out-str (explain argspec args))) (gen/generate (gen retspec))))))) (with-gen* [_ gfn] (fspec-impl argspec aform retspec rform fnspec fform gfn)) - (describe* [_] `(fspec ~aform ~rform ~fform))))) + (describe* [_] `(fspec :args ~aform :ret ~rform :fn ~fform))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (cljs.spec/def ::any (cljs.spec/spec (constantly true) :gen gen/any)) From 1942e70e2e9ce2fc7b979bace59e33f57bca2f94 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jun 2016 17:51:43 -0400 Subject: [PATCH 1846/4033] same as Clojure 69dd29d2c8 --- src/main/cljs/cljs/spec.cljc | 7 +++++-- src/main/cljs/cljs/spec.cljs | 11 ++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 89b25ec10..e4d1ce568 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -57,7 +57,8 @@ Returns a spec." [form & {:keys [gen]}] - `(cljs.spec/spec-impl '~(res &env form) ~form ~gen nil)) + (when form + `(cljs.spec/spec-impl '~(res &env form) ~form ~gen nil))) (defmacro multi-spec "Takes the name of a spec/predicate-returning multimethod and a @@ -251,7 +252,9 @@ that returns a test.check generator." [& {:keys [args ret fn gen]}] (let [env &env] - `(cljs.spec/fspec-impl ~args '~(res env args) ~ret '~(res env ret) ~fn '~(res env fn) ~gen))) + `(cljs.spec/fspec-impl (spec ~args) '~(res env args) + (spec ~ret) '~(res env ret) + (spec ~fn) '~(res env fn) ~gen))) (defmacro tuple "takes one or more preds and returns a spec for a tuple, a vector diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 65a546b07..6867642a4 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1060,20 +1060,13 @@ (defn ^:skip-wiki fspec-impl "Do not call this directly, use 'fspec'" [argspec aform retspec rform fnspec fform gfn] - (assert (c/and argspec retspec)) (let [specs {:args argspec :ret retspec :fn fnspec}] (reify IFn (-invoke [this x] (valid? this x)) ILookup - (-lookup [this k] - (-lookup this k nil)) - (-lookup [_ k not-found] - (case k - :args argspec - :ret retspec - :fn fnspec - not-found)) + (-lookup [this k] (get specs k)) + (-lookup [_ k not-found] (get specs k not-found)) Spec (conform* [_ f] (if (fn? f) (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) From 623f563727472ef1ebbd8152a0ff1e8fb78621b6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jun 2016 18:19:08 -0400 Subject: [PATCH 1847/4033] Same as Clojure 92df7b2a --- src/main/cljs/cljs/repl.cljs | 12 +++++------- src/main/cljs/cljs/spec.cljc | 17 +++-------------- src/main/cljs/cljs/spec.cljs | 34 +++++++++++----------------------- 3 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index 9fdbe897a..ba600f8fd 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -49,10 +49,8 @@ (when doc (println " " doc)))) (when n - (let [specs (spec/fn-specs (symbol (str (ns-name n)) (name nm)))] - (when (some identity (vals specs)) - (print "Spec") - (run! (fn [[role spec]] - (when (and spec (not (= spec ::spec/unknown))) - (print (str "\n " (name role) ":") (spec/describe spec)))) - specs))))))) + (when-let [fnspec (spec/fn-spec (symbol (str (ns-name n)) (name nm)))] + (print "Spec") + (doseq [role [:args :ret :fn]] + (when-let [spec (get fnspec role)] + (print (str "\n " (name role) ":") (spec/describe spec))))))))) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index e4d1ce568..270facd75 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -273,10 +273,6 @@ s (symbol (str (.-name *ns*)) (str s))))) -(defn- fn-spec-sym - [env sym role] - (symbol (str (ns-qualify env sym) "$" (name role)))) - (def ^:private _speced_vars (atom #{})) (defn speced-vars* @@ -313,7 +309,7 @@ specified, return speced vars from all namespaces." Qualifies fn-sym with resolve, or using *ns* if no resolution found. Registers specs in the global registry, where they can be retrieved - by calling fn-specs. + by calling fn-spec. Once registered, function specs are included in doc, checked by instrument, tested by the runner clojure.spec.test/run-tests, and (if @@ -332,18 +328,11 @@ specified, return speced vars from all namespaces." :str string? :sym symbol?) :ret symbol?)" - [fn-sym & {:keys [args ret fn] :as m}] + [fn-sym & specs] (let [env &env qn (ns-qualify env fn-sym)] (swap! _speced_vars conj qn) - `(do ~@(reduce - (clojure.core/fn [defns role] - (if (contains? m role) - (let [s (fn-spec-sym env qn (name role))] - (conj defns `(cljs.spec/def '~s ~(get m role)))) - defns)) - [] [:args :ret :fn]) - '~qn))) + `(cljs.spec/def '~qn (cljs.spec/fspec ~@specs)))) (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 6867642a4..b02e06037 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -254,8 +254,6 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(def ^:private fn-spec-roles [:args :ret :fn]) - (defn- expect "Returns nil if v conforms to spec, else throws ex-info with explain-data." [spec v] @@ -268,25 +266,15 @@ (.-sym x) x)) -(defn- fn-specs? - "Fn-specs must include at least :args or :ret specs." +(defn- fn-spec? + "Fn-spec must include at least :args or :ret specs." [m] (c/or (:args m) (:ret m))) -(defn- fn-spec-sym - [sym role] - (symbol (str sym "$" (name role)))) - -(defn fn-specs - "Returns :args/:ret/:fn map of specs for var or symbol v." +(defn fn-spec + "Returns fspec of specs for var or symbol v, or nil." [v] - (let [s (->sym v) - reg (registry)] - (reduce - (fn [m role] - (assoc m role (get reg (fn-spec-sym s role)))) - {} - fn-spec-roles))) + (get (registry) (->sym v))) (defn- spec-checking-fn [v f] @@ -304,7 +292,7 @@ [& args] (if *instrument-enabled* (s/with-instrument-disabled - (let [specs (fn-specs v)] + (let [specs (fn-spec v)] (let [cargs (when (:args specs) (conform! v :args (:args specs) args args)) ret (binding [*instrument-enabled* true] (apply f args)) @@ -317,7 +305,7 @@ (defn- macroexpand-check [v args] - (let [specs (fn-specs v)] + (let [specs (fn-spec v)] (when-let [arg-spec (:args specs)] (when (= ::invalid (conform arg-spec args)) (let [ed (assoc (explain-data* arg-spec [:args] @@ -328,7 +316,7 @@ "Call to " (->sym v) " did not conform to spec:\n" (with-out-str (explain-out ed)))))))))) -(defn- no-fn-specs +(defn- no-fn-spec [v specs] (ex-info (str "Fn at " (pr-str v) " is not spec'ed.") {:var v :specs specs})) @@ -339,8 +327,8 @@ (defn instrument* [v] - (let [specs (fn-specs v)] - (if (fn-specs? specs) + (let [spec (fn-spec v)] + (if (fn-spec? spec) (locking instrumented-vars (let [{:keys [raw wrapped]} (get @instrumented-vars v) current @v] @@ -348,7 +336,7 @@ (let [checked (spec-checking-fn v current)] (swap! instrumented-vars assoc v {:raw current :wrapped checked}) checked)))) - (throw (no-fn-specs v specs))))) + (throw (no-fn-spec v spec))))) (defn unstrument* [v] From f744e792dcd0fd4a9a46bdcc60ac5fb7589fc7b9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jun 2016 18:23:29 -0400 Subject: [PATCH 1848/4033] same as Clojure 30dd3d8 --- src/main/cljs/cljs/spec.cljc | 4 ++-- src/main/cljs/cljs/spec.cljs | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 270facd75..2712b04e5 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -393,8 +393,8 @@ specified, return speced vars from all namespaces." (defmacro instrument "Instruments the var at v, a var or symbol, to check specs - registered with fdef. Wraps the fn at v to check :args/:ret/:fn - specs, if they exist, throwing an ex-info with explain-data if a + registered with fdef. Wraps the fn at v to check :args + spec, if it exist, throwing an ex-info with explain-data if a check fails. Idempotent." [v] (let [v (if-not (seq? v) (list 'var v) v) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index b02e06037..39a2af0e4 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -293,13 +293,9 @@ (if *instrument-enabled* (s/with-instrument-disabled (let [specs (fn-spec v)] - (let [cargs (when (:args specs) (conform! v :args (:args specs) args args)) - ret (binding [*instrument-enabled* true] - (apply f args)) - cret (when (:ret specs) (conform! v :ret (:ret specs) ret args))] - (when (c/and (:args specs) (:ret specs) (:fn specs)) - (conform! v :fn (:fn specs) {:args cargs :ret cret} args)) - ret))) + (when (:args specs) (conform! v :args (:args specs) args args)) + (binding [*instrument-enabled* true] + (apply f args)))) (apply f args))) (not (instance? MultiFn f)) (doto (gobj/extend f))))) From a274b0bf3dc70710b5af92991bd93033c522b43b Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 16 Jun 2016 18:25:42 -0400 Subject: [PATCH 1849/4033] same as Clojure 544d01d --- src/main/cljs/cljs/spec.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 39a2af0e4..0ec6933df 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1052,12 +1052,12 @@ (-lookup [this k] (get specs k)) (-lookup [_ k not-found] (get specs k not-found)) Spec - (conform* [_ f] (if (fn? f) + (conform* [_ f] (if (ifn? f) (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) ::invalid)) (unform* [_ f] f) (explain* [_ path via in f] - (if (fn? f) + (if (ifn? f) (let [args (validate-fn f specs 100)] (if (identical? f args) ;;hrm, we might not be able to reproduce nil @@ -1072,7 +1072,7 @@ (when fnspec (let [cargs (conform argspec args)] (explain-1 fform fnspec (conj path :fn) via in {:args cargs :ret cret}))))))))) - {path {:pred 'fn? :val f :via via :in in}})) + {path {:pred 'ifn? :val f :via via :in in}})) (gen* [_ _ _ _] (if gfn (gfn) (when-not fnspec From bcf60ce194e5292fbc5c4b2d89dfc5a7b886b94c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jun 2016 15:44:45 -0400 Subject: [PATCH 1850/4033] rename long preds to int and cover all fixed precision integer types same as Clojure 20f67081 --- src/main/cljs/cljs/core.cljs | 71 ++++++++++++++++++++------- src/main/cljs/cljs/spec.cljc | 4 +- src/main/cljs/cljs/spec.cljs | 17 +++++-- src/main/cljs/cljs/spec/impl/gen.cljs | 8 +-- 4 files changed, 71 insertions(+), 29 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fb4896926..803dd8dc1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8,6 +8,7 @@ (ns cljs.core (:require goog.math.Long + goog.math.Integer [goog.string :as gstring] [goog.object :as gobject] [goog.array :as garray]) @@ -2124,25 +2125,57 @@ reduces them without incurring seq initialization" (not (identical? n js/Infinity)) (== (js/parseFloat n) (js/parseInt n 10)))) -(defn ^boolean long? - "Return true if x is an instance of goog.math.Long" - [x] (instance? goog.math.Long x)) - -(defn ^boolean pos-long? - "Return true if x is a positive Long" - [x] (and (instance? goog.math.Long x) - (not (.isNegative x)) - (not (.isZero x)))) - -(defn ^boolean neg-long? - "Return true if x is a negative Long" - [x] (and (instance? goog.math.Long x) - (.isNegative x))) - -(defn ^boolean nat-long? - "Return true if x is a non-negative Long" - [x] (and (instance? goog.math.Long x) - (or (not (.isNegative x)) (.isZero x)))) +(defn ^boolean int? + "Return true if x is an integer" + [x] + (or (integer? x) + (instance? goog.math.Integer x) + (instance? goog.math.Long x))) + +(defn ^boolean pos-int? + "Return true if x is a positive integer" + [x] + (cond + (integer? x) (pos? x) + + (instance? goog.math.Integer x) + (and (not (.isNegative x)) + (not (.isZero x))) + + (instance? goog.math.Long x) + (and (not (.isNegative x)) + (not (.isZero x))) + + :else false)) + +(defn ^boolean neg-int? + "Return true if x is a negative integer" + [x] + (cond + (integer? x) (neg? x) + + (instance? goog.math.Integer x) + (.isNegative x) + + (instance? goog.math.Long x) + (.isNegative x) + + :else false)) + +(defn ^boolean nat-int? + "Return true if x is a non-negative integer" + [x] + (cond + (integer? x) + (or (not (neg? x)) (zero? x)) + + (instance? goog.math.Integer x) + (or (not (.isNegative x)) (.isZero x)) + + (instance? goog.math.Long x) + (or (not (.isNegative x)) (.isZero x)) + + :else false)) (defn ^boolean contains? "Returns true if key is present in the given collection, otherwise diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 2712b04e5..2de74de00 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -384,11 +384,11 @@ specified, return speced vars from all namespaces." (gen/fmap mkdate# (gen/large-integer* {:min st# :max et#})))))) -(defmacro long-in +(defmacro int-in "Returns a spec that validates longs in the range from start (inclusive) to end (exclusive)." [start end] - `(spec (and c/long? #(long-in-range? ~start ~end %)) + `(spec (and c/int? #(int-in-range? ~start ~end %)) :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) (defmacro instrument diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 0ec6933df..5dc34af94 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1124,9 +1124,18 @@ (let [t (inst-ms inst)] (c/and (<= (inst-ms start) t) (< t (inst-ms end)))))) -(defn long-in-range? +(defn int-in-range? "Return true if start <= val and val < end" [start end val] - (c/and (long? val) - (.lessThanOrEqual (goog.math.Long.fromNumber start) val) - (.lessThan val (goog.math.Long.fromNumber end)))) \ No newline at end of file + (cond + (integer? val) (and (<= start val) (< val end)) + + (instance? goog.math.Long val) + (c/and (.lessThanOrEqual start val) + (.lessThan val end)) + + (instance? goog.math.Integer val) + (c/and (.lessThanOrEqual start val) + (.lessThan val end)) + + :else false)) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index 8230b6924..b8ea0009a 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -89,10 +89,10 @@ gen-builtins (let [simple (simple-type-printable)] {number? (one-of [(large-integer) (double)]) integer? (large-integer) - long? (large-integer) - pos-long? (large-integer* {:min 1}) - neg-long? (large-integer* {:max -1}) - nat-long? (large-integer* {:min 0}) + int? (large-integer) + pos-int? (large-integer* {:min 1}) + neg-int? (large-integer* {:max -1}) + nat-int? (large-integer* {:min 0}) ;float? (double) string? (string-alphanumeric) ident? (one-of [(keyword-ns) (symbol-ns)]) From 87c542dfb08fc099b14d4873f83911e6fd178e75 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jun 2016 16:05:01 -0400 Subject: [PATCH 1851/4033] fix def/fdef symbol resolution, replace fn-spec with more general get-spec same as Clojure 4978bf5c --- src/main/cljs/cljs/repl.cljs | 2 +- src/main/cljs/cljs/spec.cljc | 42 ++++++++++++++++++++---------------- src/main/cljs/cljs/spec.cljs | 36 +++++++++++++++---------------- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index ba600f8fd..443c011e7 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -49,7 +49,7 @@ (when doc (println " " doc)))) (when n - (when-let [fnspec (spec/fn-spec (symbol (str (ns-name n)) (name nm)))] + (when-let [fnspec (spec/get-spec (symbol (str (ns-name n)) (name nm)))] (print "Spec") (doseq [role [:args :ret :fn]] (when-let [spec (get fnspec role)] diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 2de74de00..f02f5d1ec 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -8,7 +8,8 @@ (ns cljs.spec (:refer-clojure :exclude [+ * and or cat def keys resolve]) - (:require [cljs.analyzer.api :refer [resolve]] + (:require [cljs.analyzer :as ana] + [cljs.analyzer.api :refer [resolve]] [clojure.walk :as walk] [cljs.spec.impl.gen :as gen] [clojure.string :as str])) @@ -35,11 +36,22 @@ (sequential? form) (walk/postwalk #(if (symbol? %) (res env %) %) (unfn form)) :else form)) +(defn- ns-qualify + "Qualify symbol s by resolving it or using the current *ns*." + [env s] + (if (namespace s) + (let [v (resolve env s)] + (assert v (str "Unable to resolve: " s)) + (->sym v)) + (symbol (str ana/*cljs-ns*) (str s)))) + (defmacro def - "Given a namespace-qualified keyword or symbol k, and a spec, spec-name, predicate or regex-op - makes an entry in the registry mapping k to the spec" + "Given a namespace-qualified keyword or resolveable symbol k, and a spec, + spec-name, predicate or regex-op makes an entry in the registry mapping k to + the spec" [k spec-form] - `(cljs.spec/def-impl ~k '~(res &env spec-form) ~spec-form)) + (let [k (if (symbol? k) (ns-qualify &env k) k)] + `(cljs.spec/def-impl '~k '~(res &env spec-form) ~spec-form))) (defmacro spec "Takes a single predicate form, e.g. can be the name of a predicate, @@ -248,6 +260,9 @@ and returns a spec whose conform/explain take a fn and validates it using generative testing. The conformed value is always the fn itself. + See 'fdef' for a single operation that creates an fspec and + registers it, as well as a full description of :args, :ret and :fn + Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator." [& {:keys [args ret fn gen]}] @@ -264,15 +279,6 @@ (assert (not (empty? preds))) `(cljs.spec/tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) -(defn- ns-qualify - "Qualify symbol s by resolving it or using the current *ns*." - [env s] - (if-let [resolved (resolve env s)] - (->sym resolved) - (if (namespace s) - s - (symbol (str (.-name *ns*)) (str s))))) - (def ^:private _speced_vars (atom #{})) (defn speced-vars* @@ -308,8 +314,8 @@ specified, return speced vars from all namespaces." expected to contain predicates that relate those values Qualifies fn-sym with resolve, or using *ns* if no resolution found. - Registers specs in the global registry, where they can be retrieved - by calling fn-spec. + Registers an fspec in the global registry, where it can be retrieved + by calling get-spec with the var or full-qualified symbol. Once registered, function specs are included in doc, checked by instrument, tested by the runner clojure.spec.test/run-tests, and (if @@ -329,10 +335,8 @@ specified, return speced vars from all namespaces." :sym symbol?) :ret symbol?)" [fn-sym & specs] - (let [env &env - qn (ns-qualify env fn-sym)] - (swap! _speced_vars conj qn) - `(cljs.spec/def '~qn (cljs.spec/fspec ~@specs)))) + (swap! _speced_vars conj (ns-qualify &env fn-sym)) + `(cljs.spec/def ~fn-sym (cljs.spec/fspec ~@specs))) (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 5dc34af94..bfcb76685 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -238,7 +238,7 @@ (defn ^:skip-wiki def-impl "Do not call this directly, use 'def'" [k form spec] - (assert (c/and (named? k) (namespace k)) "k must be namespaced keyword/symbol") + (assert (c/and (named? k) (namespace k)) "k must be namespaced keyword or resolveable symbol") (let [spec (if (c/or (spec? spec) (regex? spec) (get @registry-ref spec)) spec (spec-impl form spec nil nil))] @@ -246,10 +246,22 @@ k)) (defn registry - "returns the registry map" + "returns the registry map, prefer 'get-spec' to lookup a spec by name" [] @registry-ref) +(defn- ->sym + "Returns a symbol from a symbol or var" + [x] + (if (var? x) + (.-sym x) + x)) + +(defn get-spec + "Returns spec registered for keyword/symbol/var k, or nil." + [k] + (get (registry) (if (keyword? k) k (->sym k)))) + (declare map-spec) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -259,23 +271,11 @@ [spec v] ) -(defn- ->sym - "Returns a symbol from a symbol or var" - [x] - (if (var? x) - (.-sym x) - x)) - (defn- fn-spec? "Fn-spec must include at least :args or :ret specs." [m] (c/or (:args m) (:ret m))) -(defn fn-spec - "Returns fspec of specs for var or symbol v, or nil." - [v] - (get (registry) (->sym v))) - (defn- spec-checking-fn [v f] (let [conform! (fn [v role spec data args] @@ -292,7 +292,7 @@ [& args] (if *instrument-enabled* (s/with-instrument-disabled - (let [specs (fn-spec v)] + (let [specs (get-spec v)] (when (:args specs) (conform! v :args (:args specs) args args)) (binding [*instrument-enabled* true] (apply f args)))) @@ -301,7 +301,7 @@ (defn- macroexpand-check [v args] - (let [specs (fn-spec v)] + (let [specs (get-spec v)] (when-let [arg-spec (:args specs)] (when (= ::invalid (conform arg-spec args)) (let [ed (assoc (explain-data* arg-spec [:args] @@ -323,7 +323,7 @@ (defn instrument* [v] - (let [spec (fn-spec v)] + (let [spec (get-spec v)] (if (fn-spec? spec) (locking instrumented-vars (let [{:keys [raw wrapped]} (get @instrumented-vars v) @@ -1128,7 +1128,7 @@ "Return true if start <= val and val < end" [start end val] (cond - (integer? val) (and (<= start val) (< val end)) + (integer? val) (c/and (<= start val) (< val end)) (instance? goog.math.Long val) (c/and (.lessThanOrEqual start val) From 9a7cdbe1bae859ab9a67b4a719cc939e46d2007f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jun 2016 16:07:30 -0400 Subject: [PATCH 1852/4033] fspec gen ignores :fn rather than not gen. same as Clojure aa9b5677 --- src/main/cljs/cljs/spec.cljc | 4 ++++ src/main/cljs/cljs/spec.cljs | 9 ++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index f02f5d1ec..c9718826d 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -263,6 +263,10 @@ See 'fdef' for a single operation that creates an fspec and registers it, as well as a full description of :args, :ret and :fn + fspecs can generate functions that validate the arguments and + fabricate a return value compliant with the :ret spec, ignoring + the :fn spec if present. + Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator." [& {:keys [args ret fn gen]}] diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index bfcb76685..b43792a8f 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1075,11 +1075,10 @@ {path {:pred 'ifn? :val f :via via :in in}})) (gen* [_ _ _ _] (if gfn (gfn) - (when-not fnspec - (gen/return - (fn [& args] - (assert (valid? argspec args) (with-out-str (explain argspec args))) - (gen/generate (gen retspec))))))) + (gen/return + (fn [& args] + (assert (valid? argspec args) (with-out-str (explain argspec args))) + (gen/generate (gen retspec)))))) (with-gen* [_ gfn] (fspec-impl argspec aform retspec rform fnspec fform gfn)) (describe* [_] `(fspec :args ~aform :ret ~rform :fn ~fform))))) From b81bfb8a4dff7df9afbcd9f9b1963172acf8d73c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jun 2016 16:08:06 -0400 Subject: [PATCH 1853/4033] make explain-out public same as Clojure 8f118b1f --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index b43792a8f..0825f0ef6 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -171,7 +171,7 @@ [spec x] (explain-data* spec [] (if-let [name (spec-name spec)] [name] []) [] x)) -(defn- explain-out +(defn explain-out "prints an explanation to *out*." [ed] (if ed From 7a4e70f8848274f5db82ded9d823db772b9e5cbc Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jun 2016 16:09:57 -0400 Subject: [PATCH 1854/4033] specs are not ifns same as Clojure 6244247fc --- src/main/cljs/cljs/spec.cljs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 0825f0ef6..2ba90fecb 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -387,8 +387,6 @@ keys->specs #(c/or (k->s %) %) id (random-uuid)] (reify - IFn - (-invoke [this x] (valid? this x)) Spec (conform* [_ m] (if (keys-pred m) @@ -471,8 +469,6 @@ (named? pred) (cond-> (the-spec pred) gfn (with-gen gfn)) :else (reify - IFn - (-invoke [this x] (valid? this x)) Spec (conform* [_ x] (dt pred x form cpred?)) (unform* [_ x] (if cpred? @@ -503,8 +499,6 @@ #(assoc %1 retag %2) retag)] (reify - IFn - (-invoke [this x] (valid? this x)) Spec (conform* [_ x] (if-let [pred (predx x)] (dt pred x form) @@ -543,8 +537,6 @@ ([forms preds] (tuple-impl forms preds nil)) ([forms preds gfn] (reify - IFn - (-invoke [this x] (valid? this x)) Spec (conform* [_ x] (if-not (c/and (vector? x) @@ -616,8 +608,6 @@ (tagged-ret [(keys i) ret])))) ::invalid)))] (reify - IFn - (-invoke [this x] (valid? this x)) Spec (conform* [_ x] (cform x)) (unform* [_ [k x]] (unform (kps k) x)) @@ -669,8 +659,6 @@ "Do not call this directly, use 'and'" [forms preds gfn] (reify - IFn - (-invoke [this x] (valid? this x)) Spec (conform* [_ x] (and-preds x preds forms)) (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) @@ -999,8 +987,6 @@ "Do not call this directly, use 'spec' with a regex op argument" [re gfn] (reify - IFn - (-invoke [this x] (valid? this x)) Spec (conform* [_ x] (if (c/or (nil? x) (coll? x)) @@ -1046,8 +1032,6 @@ [argspec aform retspec rform fnspec fform gfn] (let [specs {:args argspec :ret retspec :fn fnspec}] (reify - IFn - (-invoke [this x] (valid? this x)) ILookup (-lookup [this k] (get specs k)) (-lookup [_ k not-found] (get specs k not-found)) From 3cf2e51f7a376a9f8e287335c5cc494c041b7573 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 17 Jun 2016 16:24:27 -0400 Subject: [PATCH 1855/4033] ClojureScript 1.9.76 --- README.md | 6 +++--- changes.md | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d9399dd8..353af2e60 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.9.36 +Latest stable release: 1.9.76 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.36"] +[org.clojure/clojurescript "1.9.76"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.9.36 org.clojure clojurescript - 1.9.36 + 1.9.76 ``` diff --git a/changes.md b/changes.md index cbbe1e92f..ab3d36c5a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,27 @@ +## 1.9.76 + +### Enhancements +* CLJS-1648: Getting Source Info into ex-info data for Analysis Errors +* cljs.spec updated to Clojure 1.9.0-alpha7 changes + +### Changes +* bump Google Closure Library dep +* AOT cljs.spec nses + +### Fixes +* CLJS-1679: Self-host: Incorporate spec tests +* CLJS-1680: Self-host: Don't require items no longer provided by Closure +* CLJS-1654: cljs.spec: var name in s/fdef non-conformance +* CLJS-1655: cljs.spec: conformer docstring indicates :clojure.spec/invalid +* CLJS-1656: Self-host: cljs.spec: speced-vars* fn not resolving +* 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-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 + ## 1.9.36 ### Enhancements From a5cb207d30c9343a850d6364df674b838fb9c9ba Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jun 2016 09:45:54 -0400 Subject: [PATCH 1856/4033] added bounded-count same Clojure 85a90b2e --- src/main/cljs/cljs/core.cljs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 803dd8dc1..0a41de165 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3538,13 +3538,17 @@ reduces them without incurring seq initialization" (aset a i init-val-or-seq)) a))))) -(defn- bounded-count [s n] - (if (counted? s) - (count s) - (loop [s s i n sum 0] - (if (and (pos? i) (seq s)) - (recur (next s) (dec i) (inc sum)) - sum)))) +(defn bounded-count + "If coll is counted? returns its count, else will count at most the first n + elements of coll using its seq" + {:added "1.9"} + [n coll] + (if (counted? coll) + (count coll) + (loop [i 0 s (seq coll)] + (if (and (not (nil? s)) (< i n)) + (recur (inc i) (next s)) + i)))) (defn spread [arglist] @@ -3668,7 +3672,7 @@ reduces them without incurring seq initialization" ([f args] (let [fixed-arity (.-cljs$lang$maxFixedArity f)] (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count args (inc fixed-arity))] + (let [bc (bounded-count (inc fixed-arity) args)] (if (<= bc fixed-arity) (apply-to f bc args) (.cljs$lang$applyTo f args))) @@ -3677,7 +3681,7 @@ reduces them without incurring seq initialization" (let [arglist (list* x args) fixed-arity (.-cljs$lang$maxFixedArity f)] (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count arglist (inc fixed-arity))] + (let [bc (bounded-count (inc fixed-arity) arglist)] (if (<= bc fixed-arity) (apply-to f bc arglist) (.cljs$lang$applyTo f arglist))) @@ -3686,7 +3690,7 @@ reduces them without incurring seq initialization" (let [arglist (list* x y args) fixed-arity (.-cljs$lang$maxFixedArity f)] (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count arglist (inc fixed-arity))] + (let [bc (bounded-count (inc fixed-arity) arglist)] (if (<= bc fixed-arity) (apply-to f bc arglist) (.cljs$lang$applyTo f arglist))) @@ -3695,7 +3699,7 @@ reduces them without incurring seq initialization" (let [arglist (list* x y z args) fixed-arity (.-cljs$lang$maxFixedArity f)] (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count arglist (inc fixed-arity))] + (let [bc (bounded-count (inc fixed-arity) arglist)] (if (<= bc fixed-arity) (apply-to f bc arglist) (.cljs$lang$applyTo f arglist))) @@ -3704,7 +3708,7 @@ reduces them without incurring seq initialization" (let [arglist (cons a (cons b (cons c (cons d (spread args))))) fixed-arity (.-cljs$lang$maxFixedArity f)] (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count arglist (inc fixed-arity))] + (let [bc (bounded-count (inc fixed-arity) arglist)] (if (<= bc fixed-arity) (apply-to f bc arglist) (.cljs$lang$applyTo f arglist))) From 5da67c1d13db7b7a4b347548184869097c5efa74 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 18 Jun 2016 10:05:04 -0400 Subject: [PATCH 1857/4033] first cut at every and every-kv same as Clojure 03496c03 --- src/main/cljs/cljs/spec.cljc | 34 ++++++++ src/main/cljs/cljs/spec.cljs | 108 ++++++++++++++++++++++++-- src/main/cljs/cljs/spec/impl/gen.cljs | 2 +- 3 files changed, 136 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index c9718826d..082df5abd 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -188,6 +188,40 @@ [& pred-forms] `(cljs.spec/and-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) +(defmacro every + "takes a pred and validates collection elements against that pred. + + Note that 'every' does not do exhaustive checking, rather it samples + *coll-check-limit* elements. Nor (as a result) does it do any + conforming of elements. 'explain' will report at most *coll-error-limit* + problems. Thus 'every' should be suitable for potentially large + collections. + + Takes several kwargs options that further constrain the collection: + + :count - specifies coll has exactly this count (default nil) + :min-count, :max-count - coll has count (<= min count max) (default nil) + :distinct - all the elements are distinct (default nil) + + And additional args that control gen + + :gen-max - the maximum coll size to generate (default 20) + :gen-into - the default colection to generate into (will be emptied) (default []) + + Optionally takes :gen generator-fn, which must be a fn of no args that + returns a test.check generator +" + [pred & {:keys [count max-count min-count distinct gen-max gen-into gen] :as opts}] + `(cljs.spec/every-impl '~pred ~pred ~(dissoc opts :gen) ~gen)) + +(defmacro every-kv + "like 'every' but takes separate key and val preds and works on associative collections. + + Same options as 'every'" + + [kpred vpred & opts] + `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (key v#)) :gen-into {} ~@opts)) + (defmacro * "Returns a regex op that matches zero or more values matching pred. Produces a vector of matches iff there is at least one match" diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 2ba90fecb..e0641cb8f 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -16,6 +16,8 @@ [cljs.spec.impl.gen :as gen] [clojure.string :as str])) +(def ^:const MAX_INT 9007199254740991) + (def ^:dynamic *recursion-limit* "A soft limit on how many times a branching spec (or/alt/*/opt-keys/multi-spec) can be recursed through during generation. After this a @@ -27,8 +29,12 @@ 21) (def ^:dynamic *coll-check-limit* - "The number of items validated in a collection spec'ed with 'coll'" - 100) + "The number of items validated in a collection spec'ed with 'every'" + 101) + +(def ^:dynamic *coll-error-limit* + "The number of errors reported by explain in a collection spec'ed with 'every'" + 20) (def ^:private ^:dynamic *instrument-enabled* "if false, instrumented fns call straight through" @@ -180,25 +186,25 @@ ;;(prn {:ed ed}) (doseq [[path {:keys [pred val reason via in] :as prob}] (::problems ed)] (when-not (empty? in) - (print "In:" in "")) + (print "In:" (pr-str in) "")) (print "val: ") (pr val) (print " fails") (when-not (empty? via) - (print " spec:" (last via))) + (print " spec:" (pr-str (last via)))) (when-not (empty? path) - (print " at:" path)) + (print " at:" (pr-str path))) (print " predicate: ") (pr pred) (when reason (print ", " reason)) (doseq [[k v] prob] (when-not (#{:pred :val :reason :via :in} k) - (print "\n\t" k " ") + (print "\n\t" (pr-str k) " ") (pr v))) (newline)) (doseq [[k v] ed] (when-not (#{::problems} k) - (print k " ") + (print (pr-str k) " ") (pr v) (newline))))) (println "Success!"))) @@ -667,6 +673,94 @@ (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) (describe* [_] `(and ~@forms)))) +(defn ^:skip-wiki every-impl + "Do not call this directly, use 'every'" + ([form pred opts] (every-impl form pred opts nil)) + ([form pred {:keys [count max-count min-count distinct gen-max gen-into ::kfn] + :or {gen-max 20, gen-into []} + :as opts} + gfn] + (let [check? #(valid? pred %) + kfn (c/or kfn (fn [i v] i))] + (reify + Spec + (conform* [_ x] + (cond + (c/or (not (seqable? x)) + (c/and distinct (not (empty? x)) (not (apply distinct? x))) + (c/and count (not= count (bounded-count (inc count) x))) + (c/and (c/or min-count max-count) + (not (<= (c/or min-count 0) + (bounded-count (if max-count (inc max-count) min-count) x) + (c/or max-count MAX_INT))))) + ::invalid + + :else + (if (indexed? x) + (let [step (max 1 (long (/ (c/count x) *coll-check-limit*)))] + (loop [i 0] + (if (>= i (c/count x)) + x + (if (check? (nth x i)) + (recur (c/+ i step)) + ::invalid)))) + (c/or (c/and (every? check? (take *coll-check-limit* x)) x) + ::invalid)))) + (unform* [_ x] x) + (explain* [_ path via in x] + (cond + (not (seqable? x)) + {path {:pred 'seqable? :val x :via via :in in}} + + (c/and distinct (not (empty? x)) (not (apply distinct? x))) + {path {:pred 'distinct? :val x :via via :in in}} + + (c/and count (not= count (bounded-count count x))) + {path {:pred `(= ~count (c/count %)) :val x :via via :in in}} + + (c/and (c/or min-count max-count) + (not (<= (c/or min-count 0) + (bounded-count (if max-count (inc max-count) min-count) x) + (c/or max-count MAX_INT)))) + {path {:pred `(<= ~(c/or min-count 0) (c/count %) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}} + + :else + (apply merge + (take *coll-error-limit* + (keep identity + (map (fn [i v] + (let [k (kfn i v)] + (when-not (check? v) + (let [prob (explain-1 form pred (conj path k) via (conj in k) v)] + prob)))) + (range) x)))))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [init (empty gen-into) + pgen (gensub pred overrides path rmap form)] + (gen/fmap + #(if (vector? init) % (into init %)) + (cond + distinct + (if count + (gen/vector-distinct pgen {:num-elements count :max-tries 100}) + (gen/vector-distinct pgen {:min-elements (c/or min-count 0) + :max-elements (c/or max-count (max gen-max (c/* 2 (c/or min-count 0)))) + :max-tries 100})) + + count + (gen/vector pgen count) + + (c/or min-count max-count) + (gen/vector pgen (c/or min-count 0) (c/or max-count (max gen-max (c/* 2 (c/or min-count 0))))) + + :else + (gen/vector pgen 0 gen-max)))))) + + (with-gen* [_ gfn] (every-impl form pred opts gfn)) + (describe* [_] `(every ~form ~@(mapcat identity opts))))))) + ;;;;;;;;;;;;;;;;;;;;;;; regex ;;;;;;;;;;;;;;;;;;; ;;See: ;; http://matt.might.net/articles/implementation-of-regular-expression-matching-in-scheme-with-derivatives/ diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index b8ea0009a..500d7158c 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -66,7 +66,7 @@ ; g ; (throw (js/Error. (str "Var " s " is not a generator")))))) -(lazy-combinators hash-map list map not-empty set vector fmap elements +(lazy-combinators hash-map list map not-empty set vector vector-distinct fmap elements bind choose one-of such-that tuple sample return large-integer*) From 06264b3723f6c129b3949dbac04d9ff3be6a6c4e Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jun 2016 11:10:01 -0400 Subject: [PATCH 1858/4033] support gen overrides by name in addition to path same as Clojure b0c94544 --- src/main/cljs/cljs/spec.cljs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index e0641cb8f..4e3fd7a85 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -224,7 +224,8 @@ (defn- gensub [spec overrides path rmap form] ;;(prn {:spec spec :over overrides :path path :form form}) - (let [spec (specize spec)] + (let [spec (c/or (get overrides spec) spec) + spec (specize spec)] (if-let [g (c/or (get overrides path) (gen* spec overrides path rmap))] (gen/such-that #(valid? spec %) g 100) (throw (js/Error. (str "Unable to construct gen at: " path " for: " (abbrev form))))))) @@ -232,12 +233,13 @@ (defn gen "Given a spec, returns the generator for it, or throws if none can be constructed. Optionally an overrides map can be provided which - should map paths (vectors of keywords) to generators. These will be - used instead of the generators at those paths. Note that parent - generator (in the spec or overrides map) will supersede those of any - subtrees. A generator for a regex op must always return a - sequential collection (i.e. a generator for s/? should return either - an empty sequence/vector or a sequence/vector with one item in it)" + should map spec names or paths (vectors of keywords) to + generators. These will be used instead of the generators at those + names/paths. Note that parent generator (in the spec or overrides + map) will supersede those of any subtrees. A generator for a regex + op must always return a sequential collection (i.e. a generator for + s/? should return either an empty sequence/vector or a + sequence/vector with one item in it)" ([spec] (gen spec nil)) ([spec overrides] (gensub spec overrides [] {::recursion-limit *recursion-limit*} spec))) From 0336696f4e805e96d1130f75a0e16241f96b55e1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jun 2016 14:17:36 -0400 Subject: [PATCH 1859/4033] CLJS-1668: cljs.spec: c alias needs expansion in int-in --- src/main/cljs/cljs/spec.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 082df5abd..7fb5c2ce7 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -8,7 +8,8 @@ (ns cljs.spec (:refer-clojure :exclude [+ * and or cat def keys resolve]) - (:require [cljs.analyzer :as ana] + (:require [cljs.core :as c] + [cljs.analyzer :as ana] [cljs.analyzer.api :refer [resolve]] [clojure.walk :as walk] [cljs.spec.impl.gen :as gen] From 416f322c25624b042e63e64a0754d5aaf48e552e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 20 Jun 2016 15:09:04 -0400 Subject: [PATCH 1860/4033] CLJS-1687: Self-host: cljs.spec: inst-in-range? and int-in-range? need qualification --- src/main/cljs/cljs/spec.cljc | 4 ++-- src/test/cljs/cljs/spec_test.cljs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 7fb5c2ce7..e7a35a495 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -422,7 +422,7 @@ specified, return speced vars from all namespaces." `(let [st# (inst-ms ~start) et# (inst-ms ~end) mkdate# (fn [d#] (js/Date. d#))] - (spec (and inst? #(inst-in-range? ~start ~end %)) + (spec (and inst? #(cljs.spec/inst-in-range? ~start ~end %)) :gen (fn [] (gen/fmap mkdate# (gen/large-integer* {:min st# :max et#})))))) @@ -431,7 +431,7 @@ specified, return speced vars from all namespaces." "Returns a spec that validates longs in the range from start (inclusive) to end (exclusive)." [start end] - `(spec (and c/int? #(int-in-range? ~start ~end %)) + `(spec (and c/int? #(cljs.spec/int-in-range? ~start ~end %)) :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) (defmacro instrument diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index bcb71c342..9df5b88aa 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -43,6 +43,14 @@ (is (= "good" (testmm {:type :good}))) (is (thrown? js/Error (testmm "foo")))) +(deftest int-in-test + (is (s/valid? (s/int-in 1 3) 2)) + (is (not (s/valid? (s/int-in 1 3) 0)))) + +(deftest inst-in-test + (is (s/valid? (s/inst-in #inst "1999" #inst "2001") #inst "2000")) + (is (not (s/valid? (s/inst-in #inst "1999" #inst "2001") #inst "1492")))) + (comment (s/conform s2 [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]) From 4628e011c193fe25a60a527bfa6771f2ff5403a1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jun 2016 09:33:23 -0400 Subject: [PATCH 1861/4033] missing cljs.spec/fn-specs -> cljs.spec/get-spec in cljs.spec.test ns --- src/main/cljs/cljs/spec/test.cljc | 2 +- src/main/cljs/cljs/spec/test.cljs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 0ec5dba97..50fc84595 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -21,7 +21,7 @@ `(cljs.spec.test/run-var-tests (->> #?(:clj ~(spec/speced-vars* ns-syms) :cljs ~(cljs.spec$macros/speced-vars* ns-syms)) - (filter (fn [v#] (:args (cljs.spec/fn-specs v#)))))))) + (filter (fn [v#] (:args (cljs.spec/get-spec v#)))))))) (defmacro run-all-tests "Like clojure.test/run-all-tests, but runs test.check tests diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index c6035fd01..a164c3b67 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -77,9 +77,9 @@ passed through to test.check/quick-check: Returns a map as quick-check, with :explain-data added if :result is false." [v & opts] - (let [specs (spec/fn-specs v)] - (if (:args specs) - (apply check-fn @v specs opts) + (let [fnspec (spec/get-spec v)] + (if (:args fnspec) + (apply check-fn @v fnspec opts) (throw (js/Error. (str "No :args spec for " v)))))) (defn- run-var-tests From c27cb9984f280ea558f78fe6a810f39c37a25f66 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jun 2016 11:22:57 -0400 Subject: [PATCH 1862/4033] split apart warning about missing goog and main require --- src/main/clojure/cljs/closure.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index bebbda958..c6c94a561 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1384,8 +1384,8 @@ (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "if(typeof goog == \"undefined\") document.write('');\n" "document.write('');\n" - "document.write('');\n"))))) + "document.write('');\n" + "document.write('');\n"))))) (defn output-modules "Given compiler options, original IJavaScript sources and a sequence of From 57c8a75f38c0bef90e436ccce704c00ac67da4a0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jun 2016 11:30:44 -0400 Subject: [PATCH 1863/4033] add :preloads to the list of compiler options --- src/main/clojure/cljs/closure.clj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c6c94a561..ef757fff8 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -169,7 +169,7 @@ :optimize-constants :output-dir :output-to :output-wrapper :parallel-build :preamble :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings - :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers}) + :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -1362,6 +1362,11 @@ (declare foreign-deps-str add-header add-source-map-link) +(defn preloads [syms] + (letfn [(preload-str [sym] + (str "document.write('');\n"))] + (map preload-str syms))) + (defn output-main-file [opts] (let [asset-path (or (:asset-path opts) (util/output-directory opts)) @@ -1385,6 +1390,7 @@ "if(typeof goog == \"undefined\") document.write('');\n" "document.write('');\n" "document.write('');\n" + (apply str (preloads (:preloads opts))) "document.write('');\n"))))) (defn output-modules From d187ad73ef673c3b1b0c3fe6b9ad3eb944057c3c Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jun 2016 12:31:18 -0400 Subject: [PATCH 1864/4033] CLJS-1688: :preloads compiler option for loading other entry points prior to :main handle :preloads in add-js-sources, handle Node.js --- src/main/clojure/cljs/closure.clj | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ef757fff8..b673ba302 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -878,8 +878,8 @@ inputs)) (defn add-js-sources - "Given list of IJavaScript objects, add foreign-deps and constants-table - IJavaScript objects to the list." + "Given list of IJavaScript objects, add foreign-deps, constants-table, and + preloads IJavaScript objects to the list." [inputs opts] (let [requires (set (mapcat deps/-requires inputs)) required-js (js-dependencies opts requires)] @@ -894,6 +894,12 @@ [(when (-> @env/*compiler* :options :emit-constants) (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] + (remove nil? + (map (fn [preload] + (if-let [uri (:uri (cljs-source-for-namespace preload))] + (-compile uri opts) + (util/debug-prn "WARNING: preloads namespace" preload "does not exist"))) + (:preloads opts))) inputs))) (comment @@ -1362,10 +1368,15 @@ (declare foreign-deps-str add-header add-source-map-link) -(defn preloads [syms] - (letfn [(preload-str [sym] - (str "document.write('');\n"))] - (map preload-str syms))) +(defn preloads + ([syms] + (preloads syms nil)) + ([syms mode] + (letfn [(preload-str [sym] + (str (when (= :browser mode) "document.write('');\n" "\n")))] + (map preload-str syms)))) (defn output-main-file [opts] (let [asset-path (or (:asset-path opts) @@ -1383,6 +1394,7 @@ "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + (apply str (preloads (:preloads opts))) "goog.require(\"" (comp/munge (:main opts)) "\");\n" "goog.require(\"cljs.nodejscli\");\n"))) (output-one-file opts @@ -1390,7 +1402,7 @@ "if(typeof goog == \"undefined\") document.write('');\n" "document.write('');\n" "document.write('');\n" - (apply str (preloads (:preloads opts))) + (apply str (preloads (:preloads opts) :browser)) "document.write('');\n"))))) (defn output-modules From a46eea2223c5d81acb28b1f219116038b0515704 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jun 2016 14:24:44 -0400 Subject: [PATCH 1865/4033] don't limit :preloads to ClojureScript sources --- src/main/clojure/cljs/closure.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b673ba302..4c8604f28 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -881,8 +881,9 @@ "Given list of IJavaScript objects, add foreign-deps, constants-table, and preloads IJavaScript objects to the list." [inputs opts] - (let [requires (set (mapcat deps/-requires inputs)) - required-js (js-dependencies opts requires)] + (let [requires (set (mapcat deps/-requires inputs)) + required-js (js-dependencies opts requires) + cenv @env/*compiler*] (concat (map (fn [{:keys [foreign url file provides requires] :as js-map}] @@ -891,12 +892,12 @@ (javascript-file foreign url provides requires) js-map))) required-js) - [(when (-> @env/*compiler* :options :emit-constants) + [(when (-> cenv :options :emit-constants) (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] (remove nil? (map (fn [preload] - (if-let [uri (:uri (cljs-source-for-namespace preload))] + (if-let [uri (:uri (source-for-namespace preload cenv))] (-compile uri opts) (util/debug-prn "WARNING: preloads namespace" preload "does not exist"))) (:preloads opts))) From 37b5d7d68a1cf928846c4f5777ed32e845ad11fe Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jun 2016 15:09:46 -0400 Subject: [PATCH 1866/4033] include source extension info in map returned by cljs.closure/source-for-namespace and cljs.closure/cljs-source-for-namespace --- src/main/clojure/cljs/closure.clj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4c8604f28..13034824d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -642,14 +642,14 @@ [ns] (if (= "cljs.core$macros" (str ns)) (let [relpath "cljs/core.cljc"] - {:relative-path relpath :uri (io/resource relpath)}) + {:relative-path relpath :uri (io/resource relpath) :ext :cljc}) (let [path (-> (munge ns) (string/replace \. \/)) relpath (str path ".cljs")] (if-let [res (io/resource relpath)] - {:relative-path relpath :uri res} + {:relative-path relpath :uri res :ext :cljs} (let [relpath (str path ".cljc")] (if-let [res (io/resource relpath)] - {:relative-path relpath :uri res})))))) + {:relative-path relpath :uri res :ext :cljc})))))) (defn source-for-namespace "Given a namespace and compilation environment return the relative path and @@ -660,13 +660,13 @@ path (string/replace ns-str \. \/) relpath (str path ".cljs")] (if-let [cljs-res (io/resource relpath)] - {:relative-path relpath :uri cljs-res} + {:relative-path relpath :uri cljs-res :ext :cljs} (let [relpath (str path ".cljc")] (if-let [cljc-res (io/resource relpath)] - {:relative-path relpath :uri cljc-res} + {:relative-path relpath :uri cljc-res :ext :cljc} (let [relpath (str path ".js")] (if-let [js-res (io/resource relpath)] - {:relative-path relpath :uri js-res} + {:relative-path relpath :uri js-res :ext :js} (let [ijs (get-in @compiler-env [:js-dependency-index (str ns)]) relpath (or (:file ijs) (:url ijs))] (if-let [js-res (and relpath @@ -675,7 +675,7 @@ (or (and (util/url? relpath) relpath) (try (URL. relpath) (catch Throwable t)) (io/resource relpath)))] - {:relative-path relpath :uri js-res} + {:relative-path relpath :uri js-res :ext :js} (throw (IllegalArgumentException. (str "Namespace " ns " does not exist")))))))))))) From b09f183161e5dec20d2fc50626033ab35a733410 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Jun 2016 16:46:57 -0400 Subject: [PATCH 1867/4033] preloads should work under all optimization settings. preloads should be able to load arbitrary dependencies including foreign libs etc. --- src/main/clojure/cljs/closure.clj | 68 +++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 13034824d..53d7a0988 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -763,9 +763,9 @@ (javascript-file foreign url provides requires) js-map))) required-js) - [(when (-> @env/*compiler* :options :emit-constants) - (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] - (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] + (when (-> @env/*compiler* :options :emit-constants) + (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] + [(javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)])) required-cljs inputs))))) @@ -878,12 +878,11 @@ inputs)) (defn add-js-sources - "Given list of IJavaScript objects, add foreign-deps, constants-table, and - preloads IJavaScript objects to the list." + "Given list of IJavaScript objects, add foreign-deps, constants-table + IJavaScript objects to the list." [inputs opts] (let [requires (set (mapcat deps/-requires inputs)) - required-js (js-dependencies opts requires) - cenv @env/*compiler*] + required-js (js-dependencies opts requires)] (concat (map (fn [{:keys [foreign url file provides requires] :as js-map}] @@ -892,17 +891,53 @@ (javascript-file foreign url provides requires) js-map))) required-js) - [(when (-> cenv :options :emit-constants) - (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] - (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))] - (remove nil? - (map (fn [preload] - (if-let [uri (:uri (source-for-namespace preload cenv))] - (-compile uri opts) - (util/debug-prn "WARNING: preloads namespace" preload "does not exist"))) - (:preloads opts))) + (when (-> @env/*compiler* :options :emit-constants) + (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] + [(javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)])) inputs))) +(defn distinct-by + ([k coll] + (let [step (fn step [xs seen] + (lazy-seq + ((fn [[f :as xs] seen] + (when-let [s (seq xs)] + (let [v (get f k)] + (if (contains? seen v) + (recur (rest s) seen) + (cons f (step (rest s) (conj seen v))))))) + xs seen)))] + (step coll #{})))) + +(defn add-preloads + "Add :preloads to a given set of inputs (IJavaScript). Returns a new + list of inputs where the preloaded namespaces and their deps come immediately after + cljs.core or the constants table depending on the optimization setting. Any + files needing copying or compilation will be compiled and/or copied to the + appropiate location." + [inputs opts] + (let [pred (fn [x] + (if (:emit-constants opts) + (not= ["constants-table"] (:provides x)) + (not= ["cljs.core"] (:provides x)))) + pre (take-while pred inputs) + post (drop-while pred inputs) + preloads (remove nil? + (map + (fn [preload] + (try + (comp/find-source preload) + (catch Throwable t + (util/debug-prn "WARNING: preload namespace" preload "does not exist")))) + (:preloads opts)))] + (distinct-by :provides + (concat pre [(first post)] + (-> (add-dependency-sources preloads opts) + deps/dependency-order + (compile-sources opts) + (add-js-sources opts)) + (next post))))) + (comment (comp/find-sources-root "samples/hello/src") (find-dependency-sources (find-sources-root "samples/hello/src")) @@ -1965,6 +2000,7 @@ (add-js-sources all-opts) (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") all-opts)])) deps/dependency-order + (add-preloads all-opts) add-goog-base (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)]))) _ (when (:emit-constants all-opts) From ea7d9005fa0577f46b8cd89038e0cf08384c665a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 22 Jun 2016 09:11:16 -0400 Subject: [PATCH 1868/4033] 1.9.89 --- README.md | 6 +++--- changes.md | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 353af2e60..da7045c7d 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.9.76 +Latest stable release: 1.9.89 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.76"] +[org.clojure/clojurescript "1.9.89"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.9.76 org.clojure clojurescript - 1.9.76 + 1.9.89 ``` diff --git a/changes.md b/changes.md index ab3d36c5a..96730547b 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,18 @@ +## 1.9.89 + +### Enhancements +* CLJS-1688: :preloads compiler option for loading other entry points prior to :main +* cljs.spec - support gen overrides by name in addition to path +* cljs.spec - every and every-kv + +### Changes +* added bounded-count + +### Fixes +* missing cljs.spec/fn-specs -> cljs.spec/get-spec in cljs.spec.test ns +* CLJS-1687: Self-host: cljs.spec: inst-in-range? and int-in-range? need qualification +* CLJS-1668: cljs.spec: c alias needs expansion in int-in + ## 1.9.76 ### Enhancements From 17bcf2a091accb6f7caf1e8fa3954b490e9d34fa Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 22 Jun 2016 16:54:27 -0400 Subject: [PATCH 1869/4033] add :source-map-asset-path to give more fine grained control over source map url gen --- src/main/clojure/cljs/closure.clj | 4 ++-- src/main/clojure/cljs/compiler.cljc | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 53d7a0988..0adfa83fb 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -165,8 +165,8 @@ "Set of all known compiler options." #{:anon-fn-naming-policy :asset-path :cache-analysis :closure-defines :closure-extra-annotations :closure-warnings :compiler-stats :dump-core :elide-asserts :externs :foreign-libs - :hashbang :language-in :language-out :libs :main :modules :source-map-path :optimizations - :optimize-constants :output-dir :output-to :output-wrapper :parallel-build :preamble + :hashbang :language-in :language-out :libs :main :modules :source-map-path :source-map-asset-path + :optimizations :optimize-constants :output-dir :output-to :output-wrapper :parallel-build :preamble :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads}) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a2aa9cfb5..f30b66ed2 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1203,11 +1203,21 @@ #?(:clj (defn emit-source-map [src dest sm-data opts] (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] - (emits "\n//# sourceMappingURL=" - (or (:source-map-url opts) (.getName sm-file)) - (if (true? (:source-map-timestamp opts)) - (str "?rel=" (System/currentTimeMillis)) - "")) + (if-let [smap (:source-map-asset-path opts)] + (emits "\n//# sourceMappingURL=" smap + (string/replace (util/path sm-file) + (str (util/path (io/file (:output-dir opts)))) + "") + (if (true? (:source-map-timestamp opts)) + (str + (if (= -1 (string/index-of smap "?")) "?" "&") + "rel=" (System/currentTimeMillis)) + "")) + (emits "\n//# sourceMappingURL=" + (or (:source-map-url opts) (.getName sm-file)) + (if (true? (:source-map-timestamp opts)) + (str "?rel=" (System/currentTimeMillis)) + ""))) (spit sm-file (sm/encode {(url-path src) (:source-map sm-data)} {:lines (+ (:gen-line sm-data) 2) From f120fdd1f53f5fe2ed8e63b142887e0f50c02755 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 22 Jun 2016 18:03:44 -0400 Subject: [PATCH 1870/4033] clean up cljs.source-map/relative-path add docstring --- src/main/clojure/cljs/source_map.clj | 38 +++++++++++++--------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/main/clojure/cljs/source_map.clj b/src/main/clojure/cljs/source_map.clj index e2b881d9a..8a2dbdcc7 100644 --- a/src/main/clojure/cljs/source_map.clj +++ b/src/main/clojure/cljs/source_map.clj @@ -193,26 +193,24 @@ [] cols))) [] lines))) -(defn relativize-path [path {:keys [output-dir source-map-path source-map relpaths]}] - (let [bare-munged-path (cond - (re-find #"\.jar!/" path) - (str (or source-map-path output-dir) (second (string/split path #"\.jar!"))) - - :else - (str (or source-map-path output-dir) "/" (get relpaths path)))] - (cond source-map-path - bare-munged-path - - :default - (let [unrelativized-juri (-> bare-munged-path - io/file - .toURI) - source-map-parent-juri (-> source-map - io/file - .getAbsoluteFile - .getParentFile - .toURI)] - (str (.relativize source-map-parent-juri unrelativized-juri)))))) +(defn relativize-path + "Relativize a path using :source-map-path if provided or the parent directory + otherwise." + [path {:keys [output-dir source-map-path source-map relpaths] :as opts}] + (let [bare-munged-path + (cond + (re-find #"\.jar!/" path) + (str (or source-map-path output-dir) + (second (string/split path #"\.jar!"))) + :else + (str (or source-map-path output-dir) + "/" (get relpaths path)))] + (cond + source-map-path bare-munged-path + :else + (let [unrel-uri (-> bare-munged-path io/file .toURI) + sm-parent-uri (-> source-map io/file .getAbsoluteFile .getParentFile .toURI)] + (str (.relativize sm-parent-uri unrel-uri)))))) (defn encode* "Take an internal source map representation represented as nested From 62322742840c467b30159da6c96dd86199afd53f Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 22 Jun 2016 19:29:34 -0400 Subject: [PATCH 1871/4033] fix :source-map-path support under :none optimizations --- src/main/clojure/cljs/compiler.cljc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index f30b66ed2..cfe10a227 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1222,8 +1222,11 @@ (sm/encode {(url-path src) (:source-map sm-data)} {:lines (+ (:gen-line sm-data) 2) :file (url-path dest) + :source-map-path (:source-map-path opts) :source-map-timestamp (:source-map-timestamp opts) - :source-map-pretty-print (:source-map-pretty-print opts)}))))) + :source-map-pretty-print (:source-map-pretty-print opts) + :relpaths {(util/path src) + (util/ns->relpath (first (:provides opts)) (:ext opts))}}))))) #?(:clj (defn emit-source [src dest ext opts] @@ -1271,7 +1274,8 @@ (when sm-data {:source-map (:source-map sm-data)}))] (when (and sm-data (= :none (:optimizations opts))) - (emit-source-map src dest sm-data opts)) + (emit-source-map src dest sm-data + (merge opts {:ext ext :provides [ns-name]}))) (let [path (.getPath (.toURL ^File dest))] (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) (let [{:keys [output-dir cache-analysis]} opts] From fdad5a9c55ade3ce70dc1d7098cfc8eac02ba275 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 23 Jun 2016 14:40:39 -0400 Subject: [PATCH 1872/4033] tweak souce map timestamp append --- src/main/clojure/cljs/source_map.clj | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/source_map.clj b/src/main/clojure/cljs/source_map.clj index 8a2dbdcc7..e11f2d507 100644 --- a/src/main/clojure/cljs/source_map.clj +++ b/src/main/clojure/cljs/source_map.clj @@ -255,13 +255,16 @@ "sources" (into [] (let [paths (keys m) f (comp - (if (true? (:source-map-timestamp opts)) - #(str % "?rel=" (System/currentTimeMillis)) - identity) - (if (or (:output-dir opts) - (:source-map-path opts)) - #(relativize-path % opts) - #(last (string/split % #"/"))))] + (if (true? (:source-map-timestamp opts)) + (fn [uri] + (if (= -1 (string/index-of uri "?")) + (str uri "?rel=" (System/currentTimeMillis)) + (str uri "&rel=" (System/currentTimeMillis)))) + identity) + (if (or (:output-dir opts) + (:source-map-path opts)) + #(relativize-path % opts) + #(last (string/split % #"/"))))] (map f paths))) "lineCount" (:lines opts) "mappings" (->> (lines->segs (concat preamble-lines @lines)) From 4e509809893374bd0378ffbc1410118e9c2dbab4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Jun 2016 16:00:50 -0400 Subject: [PATCH 1873/4033] fix some simple spec naming bugs --- src/main/cljs/cljs/spec.cljc | 6 +++--- src/test/cljs/cljs/predicates_test.cljs | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index e7a35a495..3e31d4ddf 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -419,10 +419,10 @@ specified, return speced vars from all namespaces." "Returns a spec that validates insts in the range from start (inclusive) to end (exclusive)." [start end] - `(let [st# (inst-ms ~start) - et# (inst-ms ~end) + `(let [st# (cljs.core/inst-ms ~start) + et# (cljs.core/inst-ms ~end) mkdate# (fn [d#] (js/Date. d#))] - (spec (and inst? #(cljs.spec/inst-in-range? ~start ~end %)) + (spec (and cljs.core/inst? #(cljs.spec/inst-in-range? ~start ~end %)) :gen (fn [] (gen/fmap mkdate# (gen/large-integer* {:min st# :max et#})))))) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 62602b1e7..6de1e1edf 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -2,7 +2,6 @@ (:require [cljs.test :refer-macros [deftest is]]) (:import [goog.math Long])) - (def pred-val-table (let [now (js/Date.) uuid (uuid nil)] @@ -36,7 +35,7 @@ natl (goog.math.Long.getZero) posl (goog.math.Long.fromNumber posint) negl (goog.math.Long.fromNumber negint)] - [[identity neg? pos? integer? long? neg-long? pos-long? nat-long?] + [[identity neg? pos? integer? int? neg-int? pos-int? nat-int?] [0 false false true false false false false ] [1 false true true false false false false ] [-1 true false true false false false false ] From 23632baa35f86de8866dede624545bc0cdf4a2bb Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Jun 2016 16:04:04 -0400 Subject: [PATCH 1874/4033] CLJS-1692: Autoalias clojure.* to exisiting cljs.* namespaces if possible --- src/main/clojure/cljs/analyzer.cljc | 37 +++++++++++++++++++++++- src/test/clojure/cljs/analyzer_tests.clj | 14 +++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 96286ee74..0e1655664 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1854,6 +1854,39 @@ (or (some #{ns} (vals use-macros)) (some #{ns} (vals require-macros)))))) +(defn clj-ns->cljs-ns + "Given a symbol that starts with clojure as the first segment return the + same symbol with the first segment replaced with cljs" + [sym] + (let [segs (string/split (clojure.core/name sym) #"\.")] + (if (= "clojure" (first segs)) + (symbol (string/join "." (cons "cljs" (next segs)))) + sym))) + +(defn aliasable-clj-ns? + "Predicate for testing with a symbol represents an aliasable clojure namespace." + [sym] + (when-not (util/ns->source sym) + (let [[seg1 :as segs] (string/split (clojure.core/name sym) #"\.")] + (when (= "clojure" seg1) + (let [sym' (clj-ns->cljs-ns sym)] + (util/ns->source sym')))))) + +(defn rewrite-cljs-aliases + "Alias non-existing clojure.* namespaces to existing cljs.* namespaces if + possible." + [args] + (letfn [(process-spec [maybe-spec] + (if (sequential? maybe-spec) + (let [[lib & xs] maybe-spec] + (cons (cond-> lib (aliasable-clj-ns? lib) clj-ns->cljs-ns) xs)) + maybe-spec)) + (process-form [[k & specs :as form]] + (if (#{:use :require} k) + (cons k (map process-spec specs)) + form))] + (map process-form args))) + (defn desugar-ns-specs "Given an original set of ns specs desugar :include-macros and :refer-macros usage into only primitive spec forms - :use, :require, :use-macros, @@ -1942,7 +1975,9 @@ args (if docstring (next args) args) metadata (if (map? (first args)) (first args)) form-meta (meta form) - args (desugar-ns-specs (if metadata (next args) args)) + args (desugar-ns-specs + (rewrite-cljs-aliases + (if metadata (next args) args))) name (vary-meta name merge metadata) excludes (parse-ns-excludes env args) deps (atom #{}) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 5bc360836..87f6a2e36 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -272,6 +272,20 @@ (set '((:require-macros (bar :refer [quux]) :reload) (:require (bar :refer [baz]) :reload))))))) +(deftest test-rewrite-cljs-aliases + (is (= (cljs.analyzer/rewrite-cljs-aliases + '((:require-macros (bar :refer [quux]) :reload) + (:require (clojure.spec :as [s]) :reload))) + '((:require-macros (bar :refer [quux]) :reload) + (:require (cljs.spec :as [s]) :reload)))) + (is (= (cljs.analyzer/rewrite-cljs-aliases + '((:refer-clojure :exclude [first]) + (:require-macros (bar :refer [quux]) :reload) + (:require (clojure.spec :as [s]) :reload))) + '((:refer-clojure :exclude [first]) + (:require-macros (bar :refer [quux]) :reload) + (:require (cljs.spec :as [s]) :reload))))) + ;; ============================================================================= ;; Namespace metadata From 783001a6786f8dca4a13fdeadc995716903b07f9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Jun 2016 19:02:59 -0400 Subject: [PATCH 1875/4033] CLJS-1507: Implicit macro loading: macro var inference in :refer defer check-uses until after macro loads. feed missing uses to check-use-macros. error macros we can't infer. find inferred macros, update the compilation environment. code style, fix some bootstrap issues --- src/main/clojure/cljs/analyzer.cljc | 165 ++++++++++++++--------- src/test/clojure/cljs/analyzer_tests.clj | 4 +- 2 files changed, 102 insertions(+), 67 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 0e1655664..7b53f1a7f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1711,24 +1711,48 @@ (error env (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))) +(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))))) + +(defn missing-use-macro? [lib sym] + (let [the-ns #?(:clj (find-ns lib) :cljs (find-macros-ns lib))] + (or (nil? the-ns) (nil? (.findInternedVar ^clojure.lang.Namespace the-ns sym))))) + +(defn missing-uses [uses env] + (let [cenv @env/*compiler*] + (into {} (filter (fn [[sym lib]] (missing-use? lib sym cenv)) uses)))) + +(defn missing-use-macros [use-macros env] + (let [cenv @env/*compiler*] + (into {} (filter (fn [[sym lib]] (missing-use-macro? lib sym)) use-macros)))) + +(defn inferred-use-macros [use-macros env] + (let [cenv @env/*compiler*] + (into {} (filter (fn [[sym lib]] (not (missing-use-macro? lib sym))) use-macros)))) + (defn check-uses [uses env] - (doseq [[sym lib] uses] - (let [js-lib (get-in @env/*compiler* [:js-dependency-index (name lib)])] - (when (and (= (get-in @env/*compiler* [::namespaces lib :defs sym] ::not-found) ::not-found) - (not (= (get js-lib :group) :goog)) - (not (get js-lib :closure-lib))) + (let [cenv @env/*compiler*] + (doseq [[sym lib] uses] + (when (missing-use? lib sym cenv) (throw (error env (error-message :undeclared-ns-form {:type "var" :lib lib :sym sym}))))))) -(defn check-use-macros [use-macros env] - (doseq [[sym lib] use-macros] - (let [the-ns #?(:clj (find-ns lib) - :cljs (find-macros-ns lib))] - (when (or (nil? the-ns) (nil? (.findInternedVar ^clojure.lang.Namespace the-ns sym))) - (throw - (error env - (error-message :undeclared-ns-form {:type "macro" :lib lib :sym sym}))))))) +(defn check-use-macros + ([use-macros env] + (check-use-macros use-macros nil env)) + ([use-macros missing-uses env] + (let [cenv @env/*compiler*] + (doseq [[sym lib] use-macros] + (when (missing-use-macro? lib sym) + (throw + (error env + (error-message :undeclared-ns-form {:type "macro" :lib lib :sym sym}))))) + (check-uses (missing-use-macros missing-uses env) env) + (inferred-use-macros missing-uses env)))) (defn parse-ns-error-msg [spec msg] (str msg "; offending spec: " (pr-str spec))) @@ -1863,29 +1887,31 @@ (symbol (string/join "." (cons "cljs" (next segs)))) sym))) -(defn aliasable-clj-ns? - "Predicate for testing with a symbol represents an aliasable clojure namespace." - [sym] - (when-not (util/ns->source sym) - (let [[seg1 :as segs] (string/split (clojure.core/name sym) #"\.")] - (when (= "clojure" seg1) - (let [sym' (clj-ns->cljs-ns sym)] - (util/ns->source sym')))))) - -(defn rewrite-cljs-aliases - "Alias non-existing clojure.* namespaces to existing cljs.* namespaces if - possible." - [args] - (letfn [(process-spec [maybe-spec] - (if (sequential? maybe-spec) - (let [[lib & xs] maybe-spec] - (cons (cond-> lib (aliasable-clj-ns? lib) clj-ns->cljs-ns) xs)) - maybe-spec)) - (process-form [[k & specs :as form]] - (if (#{:use :require} k) - (cons k (map process-spec specs)) - form))] - (map process-form args))) +#?(:clj + (defn aliasable-clj-ns? + "Predicate for testing with a symbol represents an aliasable clojure namespace." + [sym] + (when-not (util/ns->source sym) + (let [[seg1 :as segs] (string/split (clojure.core/name sym) #"\.")] + (when (= "clojure" seg1) + (let [sym' (clj-ns->cljs-ns sym)] + (util/ns->source sym'))))))) + +#?(:clj + (defn rewrite-cljs-aliases + "Alias non-existing clojure.* namespaces to existing cljs.* namespaces if + possible." + [args] + (letfn [(process-spec [maybe-spec] + (if (sequential? maybe-spec) + (let [[lib & xs] maybe-spec] + (cons (cond-> lib (aliasable-clj-ns? lib) clj-ns->cljs-ns) xs)) + maybe-spec)) + (process-form [[k & specs :as form]] + (if (#{:use :require} k) + (cons k (map process-spec specs)) + form))] + (map process-form args)))) (defn desugar-ns-specs "Given an original set of ns specs desugar :include-macros and :refer-macros @@ -1898,12 +1924,14 @@ (map (fn [[k & specs]] [k (into [] specs)])) (into {})) sugar-keys #{:include-macros :refer-macros} + ;; drop spec k and value from spec for generated :require-macros remove-from-spec (fn [pred spec] (if-not (and (sequential? spec) (some pred spec)) spec (let [[l r] (split-with (complement pred) spec)] (recur pred (concat l (drop 2 r)))))) + ;; rewrite :refer-macros to :refer for generated :require-macros replace-refer-macros (fn [spec] (if-not (sequential? spec) @@ -1976,8 +2004,9 @@ metadata (if (map? (first args)) (first args)) form-meta (meta form) args (desugar-ns-specs - (rewrite-cljs-aliases - (if metadata (next args) args))) + #?(:clj (rewrite-cljs-aliases + (if metadata (next args) args)) + :cljs (if metadata (next args) args))) name (vary-meta name merge metadata) excludes (parse-ns-excludes env args) deps (atom #{}) @@ -2592,33 +2621,39 @@ (let [{:keys [name deps uses require-macros use-macros reload reloads]} ast] (when (and *analyze-deps* (seq deps)) (analyze-deps name deps env (dissoc opts :macros-ns))) - (when (and *analyze-deps* (seq uses)) - (check-uses uses env)) - (when *load-macros* - (load-core) - (doseq [nsym (vals use-macros)] - (let [k (or (:use-macros reload) - (get-in reloads [:use-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (locking load-mutex - (clojure.core/require nsym k)) - (locking load-mutex - (clojure.core/require nsym))) - (intern-macros nsym k))) - (doseq [nsym (vals require-macros)] - (let [k (or (:require-macros reload) - (get-in reloads [:require-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (locking load-mutex - (clojure.core/require nsym k)) - (locking load-mutex - (clojure.core/require nsym))) - (intern-macros nsym k))) - (when (seq use-macros) - (check-use-macros use-macros env))) - ast) + (let [missing (when (and *analyze-deps* (seq uses)) + (missing-uses uses env))] + (if *load-macros* + (do + (load-core) + (doseq [nsym (vals use-macros)] + (let [k (or (:use-macros reload) + (get-in reloads [:use-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (locking load-mutex + (clojure.core/require nsym k)) + (locking load-mutex + (clojure.core/require nsym))) + (intern-macros nsym k))) + (doseq [nsym (vals require-macros)] + (let [k (or (:require-macros reload) + (get-in reloads [:require-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (locking load-mutex + (clojure.core/require nsym k)) + (locking load-mutex + (clojure.core/require nsym))) + (intern-macros nsym k))) + (let [ast' (update-in ast [:use-macros] merge + (check-use-macros use-macros missing env))] + (swap! env/*compiler* update-in + [::namespaces name :use-macros] merge (:use-macros ast')) + ast')) + (do + (check-uses missing env) + ast)))) ast))) (def ^:dynamic *passes* nil) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 87f6a2e36..8ff875454 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -273,12 +273,12 @@ (:require (bar :refer [baz]) :reload))))))) (deftest test-rewrite-cljs-aliases - (is (= (cljs.analyzer/rewrite-cljs-aliases + (is (= (a/rewrite-cljs-aliases '((:require-macros (bar :refer [quux]) :reload) (:require (clojure.spec :as [s]) :reload))) '((:require-macros (bar :refer [quux]) :reload) (:require (cljs.spec :as [s]) :reload)))) - (is (= (cljs.analyzer/rewrite-cljs-aliases + (is (= (a/rewrite-cljs-aliases '((:refer-clojure :exclude [first]) (:require-macros (bar :refer [quux]) :reload) (:require (clojure.spec :as [s]) :reload))) From 41a62bfd916208d30bf621aefba56c5dce031ce5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jun 2016 14:25:28 -0400 Subject: [PATCH 1876/4033] fix subtle issue with clojure.* -> cljs.* aliasing we need to emit an alias ns spec, [cljs.* :as clojure.*] we need to tweak macro ns resolution to check for this possibility --- src/main/clojure/cljs/analyzer.cljc | 31 +++++++++++++++--------- src/test/clojure/cljs/analyzer_tests.clj | 9 ++++--- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7b53f1a7f..1ca77b51c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1905,11 +1905,15 @@ (letfn [(process-spec [maybe-spec] (if (sequential? maybe-spec) (let [[lib & xs] maybe-spec] - (cons (cond-> lib (aliasable-clj-ns? lib) clj-ns->cljs-ns) xs)) - maybe-spec)) + (if (aliasable-clj-ns? lib) + (let [lib' (clj-ns->cljs-ns lib) + spec (cons lib' xs)] + [spec (list lib' :as lib)]) + [maybe-spec])) + [maybe-spec])) (process-form [[k & specs :as form]] (if (#{:use :require} k) - (cons k (map process-spec specs)) + (cons k (mapcat process-spec specs)) form))] (map process-form args)))) @@ -2414,15 +2418,18 @@ (not (nil? (gets @env/*compiler* ::namespaces (gets env :ns :name) :use-macros sym))))) (defn get-expander-ns [env ^String nstr] - (cond - #?@(:clj [(= "clojure.core" nstr) (find-ns 'cljs.core)] - :cljs [(identical? "clojure.core" nstr) (find-macros-ns CLJS_CORE_MACROS_SYM)]) - #?@(:clj [(= "clojure.repl" nstr) (find-ns 'cljs.repl)] - :cljs [(identical? "clojure.repl" nstr) (find-macros-ns 'cljs.repl)]) - #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] - :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) - :else (some-> env :ns :require-macros (get (symbol nstr)) #?(:clj find-ns - :cljs find-macros-ns)))) + ;; first check for clojure.* -> cljs.* cases + (let [res (resolve-ns-alias env (symbol nstr)) + nstr (if res (str res) nstr)] + (cond + #?@(:clj [(= "clojure.core" nstr) (find-ns 'cljs.core)] + :cljs [(identical? "clojure.core" nstr) (find-macros-ns CLJS_CORE_MACROS_SYM)]) + #?@(:clj [(= "clojure.repl" nstr) (find-ns 'cljs.repl)] + :cljs [(identical? "clojure.repl" nstr) (find-macros-ns 'cljs.repl)]) + #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] + :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) + :else (some-> env :ns :require-macros (get (symbol nstr)) #?(:clj find-ns + :cljs find-macros-ns))))) (defn get-expander* [sym env] (when-not (or (not (nil? (gets env :locals sym))) ; locals hide macros diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 8ff875454..de0bc34dc 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -275,16 +275,17 @@ (deftest test-rewrite-cljs-aliases (is (= (a/rewrite-cljs-aliases '((:require-macros (bar :refer [quux]) :reload) - (:require (clojure.spec :as [s]) :reload))) + (:require (clojure.spec :as s :refer [fdef]) :reload))) '((:require-macros (bar :refer [quux]) :reload) - (:require (cljs.spec :as [s]) :reload)))) + (:require (cljs.spec :as s :refer [fdef]) + (cljs.spec :as clojure.spec) :reload)))) (is (= (a/rewrite-cljs-aliases '((:refer-clojure :exclude [first]) (:require-macros (bar :refer [quux]) :reload) - (:require (clojure.spec :as [s]) :reload))) + (:require (clojure.spec :as s) :reload))) '((:refer-clojure :exclude [first]) (:require-macros (bar :refer [quux]) :reload) - (:require (cljs.spec :as [s]) :reload))))) + (:require (cljs.spec :as s) (cljs.spec :as clojure.spec) :reload))))) ;; ============================================================================= ;; Namespace metadata From 621a499fa2762b5d9dea29092bb30ef8e1dd3a2a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jun 2016 14:26:09 -0400 Subject: [PATCH 1877/4033] bump to tools.reader 1.0.0-beta3 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 524da16fa..b88173e8b 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.0.0-beta1 + 1.0.0-beta3 org.clojure diff --git a/project.clj b/project.clj index ebd35bed3..48913b044 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.0.0-beta1"] + [org.clojure/tools.reader "1.0.0-beta3"] [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.285"] [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] diff --git a/script/bootstrap b/script/bootstrap index d3594c269..70d4f2f53 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -8,7 +8,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.0.0-beta1" +TREADER_RELEASE="1.0.0-beta3" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From 566f0d83761c29aaf0c7b23972f2b95282af5010 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Jun 2016 14:30:44 -0400 Subject: [PATCH 1878/4033] bump to Clojure 1.9.0-alpha7 in bootstrap script --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index 70d4f2f53..612f7f09e 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.9.0-alpha4" +CLOJURE_RELEASE="1.9.0-alpha7" CLOSURE_RELEASE="20160315" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" From ad3aa4a403a3024e818902dc7a2e45831bde6dad Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 27 Jun 2016 08:31:41 -0400 Subject: [PATCH 1879/4033] fix bad string/index-of usage --- src/main/clojure/cljs/compiler.cljc | 2 +- src/main/clojure/cljs/source_map.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index cfe10a227..e376bea41 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1210,7 +1210,7 @@ "") (if (true? (:source-map-timestamp opts)) (str - (if (= -1 (string/index-of smap "?")) "?" "&") + (if-not (string/index-of smap "?") "?" "&") "rel=" (System/currentTimeMillis)) "")) (emits "\n//# sourceMappingURL=" diff --git a/src/main/clojure/cljs/source_map.clj b/src/main/clojure/cljs/source_map.clj index e11f2d507..a0735631f 100644 --- a/src/main/clojure/cljs/source_map.clj +++ b/src/main/clojure/cljs/source_map.clj @@ -257,7 +257,7 @@ f (comp (if (true? (:source-map-timestamp opts)) (fn [uri] - (if (= -1 (string/index-of uri "?")) + (if-not (string/index-of uri "?") (str uri "?rel=" (System/currentTimeMillis)) (str uri "&rel=" (System/currentTimeMillis)))) identity) From 4ecb64b78e937736ceef4a17f936946218d1dee8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 14:51:37 -0400 Subject: [PATCH 1880/4033] fix gen override by name, use in fspec ret gen same as Clojure daf0811dc --- src/main/cljs/cljs/spec.cljs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 4e3fd7a85..7c7dde172 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -224,9 +224,10 @@ (defn- gensub [spec overrides path rmap form] ;;(prn {:spec spec :over overrides :path path :form form}) - (let [spec (c/or (get overrides spec) spec) - spec (specize spec)] - (if-let [g (c/or (get overrides path) (gen* spec overrides path rmap))] + (let [spec (specize spec)] + (if-let [g (c/or (get overrides (c/or (spec-name spec) spec)) + (get overrides path) + (gen* spec overrides path rmap))] (gen/such-that #(valid? spec %) g 100) (throw (js/Error. (str "Unable to construct gen at: " path " for: " (abbrev form))))))) @@ -1153,12 +1154,12 @@ (let [cargs (conform argspec args)] (explain-1 fform fnspec (conj path :fn) via in {:args cargs :ret cret}))))))))) {path {:pred 'ifn? :val f :via via :in in}})) - (gen* [_ _ _ _] (if gfn + (gen* [_ overrides _ _] (if gfn (gfn) (gen/return (fn [& args] (assert (valid? argspec args) (with-out-str (explain argspec args))) - (gen/generate (gen retspec)))))) + (gen/generate (gen retspec overrides)))))) (with-gen* [_ gfn] (fspec-impl argspec aform retspec rform fnspec fform gfn)) (describe* [_] `(fspec :args ~aform :ret ~rform :fn ~fform))))) From 5bf51557b1b91d0c25550bb266fa6ac493d8ee33 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 15:02:10 -0400 Subject: [PATCH 1881/4033] added exercise-fn per dchelimsky same as Clojure dc8903d29 --- src/main/cljs/cljs/spec.cljc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 3e31d4ddf..f962bc44e 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -482,3 +482,20 @@ by ns-syms. Idempotent." [] `(do ~@(map #(list 'cljs.spec/unstrument %) (speced-vars*)))) + +(defmacro exercise-fn + "exercises the fn named by sym (a symbol) by applying it to + n (default 10) generated samples of its args spec. When fspec is + supplied its arg spec is used, and sym-or-f can be a fn. Returns a + sequence of tuples of [args ret]. " + ([sym] + `(exercise-fn ~sym 10)) + ([sym n] + `(exercise-fn ~sym ~n nil)) + ([sym n fspec] + `(let [fspec# ~(if-not fspec + `(cljs.spec/get-spec '~(:name (resolve &env sym))) + fspec) + f# ~sym] + (for [args# (gen/sample (gen (:args fspec#)) ~n)] + [args# (apply f# args#)])))) \ No newline at end of file From e6ae0c0bbdcbdb067999ce245bdf1d9e0826c799 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 15:34:20 -0400 Subject: [PATCH 1882/4033] first cut of conforming coll-of and map-of with count constraints same as Clojure e8557891 --- src/main/cljs/cljs/spec.cljc | 40 +++++++++---- src/main/cljs/cljs/spec.cljs | 111 +++++++++++++++++++---------------- 2 files changed, 90 insertions(+), 61 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index f962bc44e..85c22ed22 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -211,6 +211,8 @@ Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator + + See also - coll-of, every-kv " [pred & {:keys [count max-count min-count distinct gen-max gen-into gen] :as opts}] `(cljs.spec/every-impl '~pred ~pred ~(dissoc opts :gen) ~gen)) @@ -218,11 +220,37 @@ (defmacro every-kv "like 'every' but takes separate key and val preds and works on associative collections. - Same options as 'every'" + Same options as 'every' + + See also - map-of" [kpred vpred & opts] `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (key v#)) :gen-into {} ~@opts)) +(defmacro coll-of + "Returns a spec for a collection of items satisfying pred. The + generator will fill an empty init-coll. Unlike 'every', coll-of + will exhaustively conform every value. + + Same options as 'every'. + + See also - every, map-of" + [pred init-coll & opts] + `(every ~pred ::conform-all true :gen-into ~init-coll ~@opts)) + +(defmacro map-of + "Returns a spec for a map whose keys satisfy kpred and vals satisfy + vpred. Unlike 'every-kv', map-of will exhaustively conform every + value. + + Same options as 'every', with the addition of: + + :conform-keys - conform keys as well as values (default false) + + See also - every-kv" + [kpred vpred & opts] + `(and (every-kv ~kpred ~vpred ::conform-all true ~@opts) map?)) + (defmacro * "Returns a regex op that matches zero or more values matching pred. Produces a vector of matches iff there is at least one match" @@ -405,16 +433,6 @@ specified, return speced vars from all namespaces." [pred] `(and (or ::nil nil? ::pred ~pred) (conformer second))) -(defmacro coll-of - "Returns a spec for a collection of items satisfying pred. The generator will fill an empty init-coll." - [pred init-coll] - `(spec (cljs.spec/coll-checker ~pred) :gen (cljs.spec/coll-gen ~pred ~init-coll))) - -(defmacro map-of - "Returns a spec for a map whose keys satisfy kpred and vals satisfy vpred." - [kpred vpred] - `(and (coll-of (tuple ~kpred ~vpred) {}) map?)) - (defmacro inst-in "Returns a spec that validates insts in the range from start (inclusive) to end (exclusive)." diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 7c7dde172..b532c445c 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -676,28 +676,75 @@ (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) (describe* [_] `(and ~@forms)))) +(defn- coll-prob [x distinct count min-count max-count + path via in] + (cond + (not (seqable? x)) + {path {:pred 'seqable? :val x :via via :in in}} + + (c/and distinct (not (empty? x)) (not (apply distinct? x))) + {path {:pred 'distinct? :val x :via via :in in}} + + (c/and count (not= count (bounded-count count x))) + {path {:pred `(= ~count (c/count %)) :val x :via via :in in}} + + (c/and (c/or min-count max-count) + (not (<= (c/or min-count 0) + (bounded-count (if max-count (inc max-count) min-count) x) + (c/or max-count MAX_INT)))) + {path {:pred `(<= ~(c/or min-count 0) (c/count %) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}})) + (defn ^:skip-wiki every-impl - "Do not call this directly, use 'every'" + "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" ([form pred opts] (every-impl form pred opts nil)) - ([form pred {:keys [count max-count min-count distinct gen-max gen-into ::kfn] + ([form pred {:keys [count max-count min-count distinct gen-max gen-into ::kfn + conform-keys ::conform-all] :or {gen-max 20, gen-into []} :as opts} gfn] (let [check? #(valid? pred %) - kfn (c/or kfn (fn [i v] i))] + kfn (c/or kfn (fn [i v] i)) + addcv (fn [ret i v cv] (conj ret cv)) + cfns (fn [x] + ;;returns a tuple of [init add complete] fns + (cond + (vector? x) + [identity + (fn [ret i v cv] + (if (identical? v cv) + ret + (assoc ret i cv))) + identity] + + (map? x) + [(if conform-keys empty identity) + (fn [ret i v cv] + (if (c/and (identical? v cv) (not conform-keys)) + ret + (assoc ret (nth (if conform-keys cv v) 0) (nth cv 1)))) + identity] + + (list? x) [empty addcv reverse] + + :else [empty addcv identity]))] (reify Spec (conform* [_ x] (cond - (c/or (not (seqable? x)) - (c/and distinct (not (empty? x)) (not (apply distinct? x))) - (c/and count (not= count (bounded-count (inc count) x))) - (c/and (c/or min-count max-count) - (not (<= (c/or min-count 0) - (bounded-count (if max-count (inc max-count) min-count) x) - (c/or max-count MAX_INT))))) + (coll-prob x distinct count min-count max-count + nil nil nil) ::invalid + conform-all + (let [[init add complete] (cfns x)] + (loop [ret (init x), i 0, [v & vs :as vseq] (seq x)] + (if vseq + (let [cv (dt pred v nil)] + (if (= ::invalid cv) + ::invalid + (recur (add ret i v cv) (inc i) vs))) + (complete ret)))) + :else (if (indexed? x) (let [step (max 1 (long (/ (c/count x) *coll-check-limit*)))] @@ -711,30 +758,15 @@ ::invalid)))) (unform* [_ x] x) (explain* [_ path via in x] - (cond - (not (seqable? x)) - {path {:pred 'seqable? :val x :via via :in in}} - - (c/and distinct (not (empty? x)) (not (apply distinct? x))) - {path {:pred 'distinct? :val x :via via :in in}} - - (c/and count (not= count (bounded-count count x))) - {path {:pred `(= ~count (c/count %)) :val x :via via :in in}} - - (c/and (c/or min-count max-count) - (not (<= (c/or min-count 0) - (bounded-count (if max-count (inc max-count) min-count) x) - (c/or max-count MAX_INT)))) - {path {:pred `(<= ~(c/or min-count 0) (c/count %) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}} - - :else + (c/or (coll-prob x distinct count min-count max-count + path via in) (apply merge - (take *coll-error-limit* + ((if conform-all identity (partial take *coll-error-limit*)) (keep identity (map (fn [i v] (let [k (kfn i v)] (when-not (check? v) - (let [prob (explain-1 form pred (conj path k) via (conj in k) v)] + (let [prob (explain-1 form pred path via (conj in k) v)] prob)))) (range) x)))))) (gen* [_ overrides path rmap] @@ -1176,27 +1208,6 @@ ([spec n overrides] (map #(vector % (conform spec %)) (gen/sample (gen spec overrides) n)))) -(defn coll-checker - "returns a predicate function that checks *coll-check-limit* items in a collection with pred" - [pred] - (let [check? #(valid? pred %)] - (fn [coll] - (c/or (nil? coll) - (c/and - (coll? coll) - (every? check? (take *coll-check-limit* coll))))))) - -(defn coll-gen - "returns a function of no args that returns a generator of - collections of items conforming to pred, with the same shape as - init-coll" - [pred init-coll] - (let [init (empty init-coll)] - (fn [] - (gen/fmap - #(if (vector? init) % (into init %)) - (gen/vector (gen pred)))))) - (defn inst-in-range? "Return true if inst at or after start and before end" [start end inst] From 038a6523c062d7dc6c319931a0776d8167a3c0db Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 15:36:09 -0400 Subject: [PATCH 1883/4033] use gen-into targets for vec/map opts same as Clojure c86375c58 --- src/main/cljs/cljs/spec.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index b532c445c..627f6a079 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -708,7 +708,7 @@ cfns (fn [x] ;;returns a tuple of [init add complete] fns (cond - (vector? x) + (c/and (vector? x) (vector? gen-into)) [identity (fn [ret i v cv] (if (identical? v cv) @@ -716,7 +716,7 @@ (assoc ret i cv))) identity] - (map? x) + (c/and (map? x) (map? gen-into)) [(if conform-keys empty identity) (fn [ret i v cv] (if (c/and (identical? v cv) (not conform-keys)) From 95da99a3a14ccc44df76bb579ba73bbcbd018d48 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 15:37:11 -0400 Subject: [PATCH 1884/4033] use gen-into targets only for map opts same as Clojure 3528b32ed4 --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 627f6a079..dcafe023c 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -708,7 +708,7 @@ cfns (fn [x] ;;returns a tuple of [init add complete] fns (cond - (c/and (vector? x) (vector? gen-into)) + (vector? x) [identity (fn [ret i v cv] (if (identical? v cv) From 53d6a9b6b82b182a77273b032f3863799d7ca777 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 15:39:14 -0400 Subject: [PATCH 1885/4033] typos same as Clojure 40d875a --- src/main/cljs/cljs/spec.cljc | 2 +- src/main/cljs/cljs/spec.cljs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 85c22ed22..7d1a45d56 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -201,7 +201,7 @@ Takes several kwargs options that further constrain the collection: :count - specifies coll has exactly this count (default nil) - :min-count, :max-count - coll has count (<= min count max) (default nil) + :min-count, :max-count - coll has count (<= min-count count max-count) (defaults nil) :distinct - all the elements are distinct (default nil) And additional args that control gen diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index dcafe023c..5bbc7a563 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -686,13 +686,13 @@ {path {:pred 'distinct? :val x :via via :in in}} (c/and count (not= count (bounded-count count x))) - {path {:pred `(= ~count (c/count %)) :val x :via via :in in}} + {path {:pred `(= ~count ~(c/count x)) :val x :via via :in in}} (c/and (c/or min-count max-count) (not (<= (c/or min-count 0) (bounded-count (if max-count (inc max-count) min-count) x) (c/or max-count MAX_INT)))) - {path {:pred `(<= ~(c/or min-count 0) (c/count %) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}})) + {path {:pred `(<= ~(c/or min-count 0) ~(c/count x) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}})) (defn ^:skip-wiki every-impl "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" From cd43ec9bf40605e230dc8858c2855f9ad85b39d8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 17:39:40 -0400 Subject: [PATCH 1886/4033] added merge, merges keys specs new explain-data format - probs collection of prob-maps, :path in maps :into and :kind for every and coll-of no more init-coll for coll-of, use :into or :kind (or not) same as Clojure 23e3ec3f8 --- src/main/cljs/cljs/spec.cljc | 33 ++++++--- src/main/cljs/cljs/spec.cljs | 125 ++++++++++++++++++++++------------- 2 files changed, 102 insertions(+), 56 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 7d1a45d56..e9c0cd5a1 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -200,6 +200,7 @@ Takes several kwargs options that further constrain the collection: + :kind - one of [], (), {}, #{} - must be this kind of collection - (default nil) :count - specifies coll has exactly this count (default nil) :min-count, :max-count - coll has count (<= min-count count max-count) (defaults nil) :distinct - all the elements are distinct (default nil) @@ -207,20 +208,20 @@ And additional args that control gen :gen-max - the maximum coll size to generate (default 20) - :gen-into - the default colection to generate into (will be emptied) (default []) + :into - one of [], (), {}, #{} - the default collection to generate into (default same as :kind if supplied, else [] Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator See also - coll-of, every-kv " - [pred & {:keys [count max-count min-count distinct gen-max gen-into gen] :as opts}] + [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] `(cljs.spec/every-impl '~pred ~pred ~(dissoc opts :gen) ~gen)) (defmacro every-kv "like 'every' but takes separate key and val preds and works on associative collections. - Same options as 'every' + Same options as 'every', :into defaults to {} See also - map-of" @@ -228,28 +229,31 @@ `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (key v#)) :gen-into {} ~@opts)) (defmacro coll-of - "Returns a spec for a collection of items satisfying pred. The - generator will fill an empty init-coll. Unlike 'every', coll-of - will exhaustively conform every value. + "Returns a spec for a collection of items satisfying pred. Unlike + generator will fill an empty init-coll. + + Same options as 'every'. conform will produce a collection + corresponding to :into if supplied, else will match the input collection, + avoiding rebuilding when possible. Same options as 'every'. See also - every, map-of" - [pred init-coll & opts] - `(every ~pred ::conform-all true :gen-into ~init-coll ~@opts)) + [pred & opts] + `(every ~pred ::conform-all true ~@opts)) (defmacro map-of "Returns a spec for a map whose keys satisfy kpred and vals satisfy vpred. Unlike 'every-kv', map-of will exhaustively conform every value. - Same options as 'every', with the addition of: + Same options as 'every', :kind set to {}, with the addition of: :conform-keys - conform keys as well as values (default false) See also - every-kv" [kpred vpred & opts] - `(and (every-kv ~kpred ~vpred ::conform-all true ~@opts) map?)) + `(every-kv ~kpred ~vpred ::conform-all true ~@opts :kind {})) (defmacro * "Returns a regex op that matches zero or more values matching @@ -501,6 +505,15 @@ by ns-syms. Idempotent." `(do ~@(map #(list 'cljs.spec/unstrument %) (speced-vars*)))) +(defmacro merge + "Takes map-validating specs (e.g. 'keys' specs) and + returns a spec that returns a conformed map satisfying all of the + specs. Successive conformed values propagate through rest of + predicates. Unlike 'and', merge can generate maps satisfying the + union of the predicates." + [& pred-forms] + `(cljs.spec/merge-spec-impl '~(mapv res pred-forms) ~(vec pred-forms) nil)) + (defmacro exercise-fn "exercises the fn named by sym (a symbol) by applying it to n (default 10) generated samples of its args spec. When fspec is diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 5bbc7a563..72893c443 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.spec - (:refer-clojure :exclude [+ * and or cat def keys]) + (:refer-clojure :exclude [+ * and or cat def keys merge]) (:require-macros [cljs.core :as c] [cljs.spec :as s]) (:require [goog.object :as gobj] @@ -171,7 +171,7 @@ (defn explain-data "Given a spec and a value x which ought to conform, returns nil if x conforms, else a map with at least the key ::problems whose value is - a path->problem-map, where problem-map has at least :pred and :val + a collection of problem-maps, where problem-map has at least :path :pred and :val keys describing the predicate and the value that failed at that path." [spec x] @@ -184,7 +184,7 @@ (print (with-out-str ;;(prn {:ed ed}) - (doseq [[path {:keys [pred val reason via in] :as prob}] (::problems ed)] + (doseq [{:keys [path pred val reason via in] :as prob} (::problems ed)] (when-not (empty? in) (print "In:" (pr-str in) "")) (print "val: ") @@ -198,7 +198,7 @@ (pr pred) (when reason (print ", " reason)) (doseq [[k v] prob] - (when-not (#{:pred :val :reason :via :in} k) + (when-not (#{:path :pred :val :reason :via :in} k) (print "\n\t" (pr-str k) " ") (pr v))) (newline)) @@ -385,7 +385,7 @@ (let [pred (maybe-spec pred)] (if (spec? pred) (explain* pred path (if-let [name (spec-name pred)] (conj via name) via) in v) - {path {:pred (abbrev form) :val v :via via :in in}}))) + [{:path path :pred (abbrev form) :val v :via via :in in}]))) (defn ^:skip-wiki map-spec-impl "Do not call this directly, use 'spec' with a map argument" @@ -425,14 +425,14 @@ ret)))) (explain* [_ path via in x] (if-not (map? x) - {path {:pred 'map? :val x :via via :in in}} + [{:path path :pred 'map? :val x :via via :in in}] (let [reg (registry)] - (apply merge + (apply concat (when-let [probs (->> (map (fn [pred form] (when-not (pred x) (abbrev form))) pred-exprs pred-forms) (keep identity) seq)] - {path {:pred (vec probs) :val x :via via :in in}}) + [{:path path :pred (vec probs) :val x :via via :in in}]) (map (fn [[k v]] (when-not (c/or (not (contains? reg (keys->specs k))) (valid? (keys->specs k) v k)) @@ -487,7 +487,7 @@ x)) (explain* [_ path via in x] (when (= ::invalid (dt pred x form cpred?)) - {path {:pred (abbrev form) :val x :via via :in in}})) + [{:path path :pred (abbrev form) :val x :via via :in in}])) (gen* [_ _ _ _] (if gfn (gfn) (gen/gen-for-pred pred))) @@ -520,7 +520,7 @@ path (conj path dv)] (if-let [pred (predx x)] (explain-1 form pred path via in x) - {path {:pred form :val x :reason "no method" :via via :in in}}))) + [{:path path :pred form :val x :reason "no method" :via via :in in}]))) (gen* [_ overrides path rmap] (if gfn (gfn) @@ -573,13 +573,13 @@ (explain* [_ path via in x] (cond (not (vector? x)) - {path {:pred 'vector? :val x :via via :in in}} + [{:path path :pred 'vector? :val x :via via :in in}] (not= (count x) (count preds)) - {path {:pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}} + [{:path path :pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}] :else - (apply merge + (apply concat (map (fn [i form pred] (let [v (x i)] (when-not (valid? pred v) @@ -622,7 +622,7 @@ (unform* [_ [k x]] (unform (kps k) x)) (explain* [this path via in x] (when-not (valid? this x) - (apply merge + (apply concat (map (fn [k form pred] (when-not (valid? pred x) (explain-1 form pred (conj path k) via in x))) @@ -676,39 +676,69 @@ (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) (describe* [_] `(and ~@forms)))) -(defn- coll-prob [x distinct count min-count max-count +(defn- coll-prob [x kfn kform distinct count min-count max-count path via in] (cond (not (seqable? x)) - {path {:pred 'seqable? :val x :via via :in in}} + [{:path path :pred 'seqable? :val x :via via :in in}] (c/and distinct (not (empty? x)) (not (apply distinct? x))) - {path {:pred 'distinct? :val x :via via :in in}} + [{:path path :pred 'distinct? :val x :via via :in in}] (c/and count (not= count (bounded-count count x))) - {path {:pred `(= ~count ~(c/count x)) :val x :via via :in in}} + [{:path path :pred `(= ~count (c/count ~'%)) :val x :via via :in in}] (c/and (c/or min-count max-count) (not (<= (c/or min-count 0) (bounded-count (if max-count (inc max-count) min-count) x) (c/or max-count MAX_INT)))) - {path {:pred `(<= ~(c/or min-count 0) ~(c/count x) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}})) + [{:path path :pred `(<= ~(c/or min-count 0) ~(c/count ~'%) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}])) + +(defn ^:skip-wiki merge-spec-impl + "Do not call this directly, use 'merge'" + [forms preds gfn] + (reify + Spec + (conform* [_ x] (and-preds x preds forms)) + (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) + (explain* [_ path via in x] + (apply concat + (map #(explain-1 %1 %2 path via in x) + forms preds))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (gen/fmap + #(apply c/merge %) + (apply gen/tuple (map #(gensub %1 overrides path rmap %2) + preds forms))))) + (with-gen* [_ gfn] (merge-spec-impl forms preds gfn)) + (describe* [_] `(merge ~@forms)))) (defn ^:skip-wiki every-impl "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" ([form pred opts] (every-impl form pred opts nil)) - ([form pred {:keys [count max-count min-count distinct gen-max gen-into ::kfn + ([form pred {gen-into :into + :keys [kind count max-count min-count distinct gen-max ::kfn conform-keys ::conform-all] :or {gen-max 20, gen-into []} :as opts} gfn] - (let [check? #(valid? pred %) + (let [conform-into (c/or gen-into kind) + gen-into (c/or gen-into kind []) + check? #(valid? pred %) kfn (c/or kfn (fn [i v] i)) addcv (fn [ret i v cv] (conj ret cv)) + [kindfn kindform] (cond + (map? kind) [map? `map?] + (vector? kind) [vector? `vector?] + (list? kind) [list? `list?] + (set? kind) [set? `set?] + :else [seqable? `seqable?]) cfns (fn [x] ;;returns a tuple of [init add complete] fns (cond - (vector? x) + (c/and (vector? x) (c/or (not conform-into) (vector? conform-into))) [identity (fn [ret i v cv] (if (identical? v cv) @@ -716,7 +746,7 @@ (assoc ret i cv))) identity] - (c/and (map? x) (map? gen-into)) + (c/and (map? x) (map? conform-into)) [(if conform-keys empty identity) (fn [ret i v cv] (if (c/and (identical? v cv) (not conform-keys)) @@ -726,12 +756,12 @@ (list? x) [empty addcv reverse] - :else [empty addcv identity]))] + :else [#(empty (c/or conform-into %)) addcv identity]))] (reify Spec (conform* [_ x] (cond - (coll-prob x distinct count min-count max-count + (coll-prob x kindfn kindform distinct count min-count max-count nil nil nil) ::invalid @@ -758,9 +788,9 @@ ::invalid)))) (unform* [_ x] x) (explain* [_ path via in x] - (c/or (coll-prob x distinct count min-count max-count + (c/or (coll-prob x kindfn kindform distinct count min-count max-count path via in) - (apply merge + (apply concat ((if conform-all identity (partial take *coll-error-limit*)) (keep identity (map (fn [i v] @@ -991,11 +1021,12 @@ {:keys [::op ps ks forms splice p1 p2] :as p} (reg-resolve! p) via (if-let [name (spec-name p)] (conj via name) via) insufficient (fn [path form] - {path {:reason "Insufficient input" - :pred (abbrev form) - :val () - :via via - :in in}})] + [{:path path + :reason "Insufficient input" + :pred (abbrev form) + :val () + :via via + :in in}])] (when p (case op ::accept nil @@ -1023,7 +1054,7 @@ (op-explain form pred path via in input))) ::alt (if (empty? input) (insufficient path (op-describe p)) - (apply merge + (apply concat (map (fn [k form pred] (op-explain (c/or form (op-describe pred)) pred @@ -1100,17 +1131,19 @@ (if (accept? p) (if (= (::op p) ::pcat) (op-explain (op-describe p) p path via (conj in i) (seq data)) - {path {:reason "Extra input" - :pred (abbrev (op-describe re)) - :val data - :via via - :in (conj in i)}}) + [{:path path + :reason "Extra input" + :pred (abbrev (op-describe re)) + :val data + :via via + :in (conj in i)}]) (c/or (op-explain (op-describe p) p path via (conj in i) (seq data)) - {path {:reason "Extra input" - :pred (abbrev (op-describe p)) - :val data - :via via - :in (conj in i)}})))))) + [{:path path + :reason "Extra input" + :pred (abbrev (op-describe p)) + :val data + :via via + :in (conj in i)}])))))) (defn ^:skip-wiki regex-spec-impl "Do not call this directly, use 'spec' with a regex op argument" @@ -1125,7 +1158,7 @@ (explain* [_ path via in x] (if (c/or (nil? x) (coll? x)) (re-explain path via in re (seq x)) - {path {:pred (abbrev (op-describe re)) :val x :via via :in in}})) + [{:path path :pred (abbrev (op-describe re)) :val x :via via :in in}])) (gen* [_ overrides path rmap] (if gfn (gfn) @@ -1177,7 +1210,7 @@ (let [ret (try (apply f args) (catch js/Error t t))] (if (instance? js/Error ret) ;;TODO add exception data - {path {:pred '(apply fn) :val args :reason (.-message ret) :via via :in in}} + [{:path path :pred '(apply fn) :val args :reason (.-message ret) :via via :in in}] (let [cret (dt retspec ret rform)] (if (= ::invalid cret) @@ -1185,7 +1218,7 @@ (when fnspec (let [cargs (conform argspec args)] (explain-1 fform fnspec (conj path :fn) via in {:args cargs :ret cret}))))))))) - {path {:pred 'ifn? :val f :via via :in in}})) + [{:path path :pred 'ifn? :val f :via via :in in}])) (gen* [_ overrides _ _] (if gfn (gfn) (gen/return From ff38d72baa57639e7405b3572048d923755783a8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 17:44:37 -0400 Subject: [PATCH 1887/4033] missing :exclude for merge in macro ns --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index e9c0cd5a1..eafedcf7b 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.spec - (:refer-clojure :exclude [+ * and or cat def keys resolve]) + (:refer-clojure :exclude [+ * and or cat def keys merge resolve]) (:require [cljs.core :as c] [cljs.analyzer :as ana] [cljs.analyzer.api :refer [resolve]] From 1a297c52d958520065335815ffb3dee07c314aa7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 28 Jun 2016 18:10:12 -0400 Subject: [PATCH 1888/4033] every/coll :kind is pred/spec same as Clojure 386e7e6 --- src/main/cljs/cljs/spec.cljc | 14 +++-- src/main/cljs/cljs/spec.cljs | 99 +++++++++++++++++++----------------- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index eafedcf7b..bbb110cb7 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -200,7 +200,9 @@ Takes several kwargs options that further constrain the collection: - :kind - one of [], (), {}, #{} - must be this kind of collection - (default nil) + :kind - a pred/spec that the collection type must satisfy, e.g. vector? + (default nil) Note that if :kind is specified and :into is + not, this pred must generate in order for every to generate. :count - specifies coll has exactly this count (default nil) :min-count, :max-count - coll has count (<= min-count count max-count) (defaults nil) :distinct - all the elements are distinct (default nil) @@ -208,7 +210,8 @@ And additional args that control gen :gen-max - the maximum coll size to generate (default 20) - :into - one of [], (), {}, #{} - the default collection to generate into (default same as :kind if supplied, else [] + :into - one of [], (), {}, #{} - the default collection to generate into + (default same as :kind if supplied, else [] Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator @@ -216,7 +219,8 @@ See also - coll-of, every-kv " [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] - `(cljs.spec/every-impl '~pred ~pred ~(dissoc opts :gen) ~gen)) + (let [nopts (-> opts (dissoc :gen) (assoc ::kind-form `'~(res (:kind opts))))] + `(cljs.spec/every-impl '~pred ~pred ~nopts ~gen))) (defmacro every-kv "like 'every' but takes separate key and val preds and works on associative collections. @@ -247,13 +251,13 @@ vpred. Unlike 'every-kv', map-of will exhaustively conform every value. - Same options as 'every', :kind set to {}, with the addition of: + Same options as 'every', :kind defaults to map?, with the addition of: :conform-keys - conform keys as well as values (default false) See also - every-kv" [kpred vpred & opts] - `(every-kv ~kpred ~vpred ::conform-all true ~@opts :kind {})) + `(every-kv ~kpred ~vpred ::conform-all true :kind map? ~@opts)) (defmacro * "Returns a regex op that matches zero or more values matching diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 72893c443..1b984054c 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -166,7 +166,8 @@ (defn explain-data* [spec path via in x] (when-let [probs (explain* (specize spec) path via in x)] - {::problems probs})) + (when-not (empty? probs) + {::problems probs}))) (defn explain-data "Given a spec and a value x which ought to conform, returns nil if x @@ -195,7 +196,7 @@ (when-not (empty? path) (print " at:" (pr-str path))) (print " predicate: ") - (pr pred) + (pr (abbrev pred)) (when reason (print ", " reason)) (doseq [[k v] prob] (when-not (#{:path :pred :val :reason :via :in} k) @@ -432,12 +433,14 @@ pred-exprs pred-forms) (keep identity) seq)] - [{:path path :pred (vec probs) :val x :via via :in in}]) + (map + #(identity {:path path :pred % :val x :via via :in in}) + probs)) (map (fn [[k v]] (when-not (c/or (not (contains? reg (keys->specs k))) - (valid? (keys->specs k) v k)) + (valid? (keys->specs k) v k)) (explain-1 (keys->specs k) (keys->specs k) (conj path k) via (conj in k) v))) - (seq x)))))) + (seq x)))))) (gen* [_ overrides path rmap] (if gfn (gfn) @@ -465,9 +468,6 @@ req-un (conj :req-un req-un) opt-un (conj :opt-un opt-un))))))) - - - (defn ^:skip-wiki spec-impl "Do not call this directly, use 'spec'" ([form pred gfn cpred?] (spec-impl form pred gfn cpred? nil)) @@ -520,7 +520,7 @@ path (conj path dv)] (if-let [pred (predx x)] (explain-1 form pred path via in x) - [{:path path :pred form :val x :reason "no method" :via via :in in}]))) + [{:path path :pred (abbrev form) :val x :reason "no method" :via via :in in}]))) (gen* [_ overrides path rmap] (if gfn (gfn) @@ -678,21 +678,23 @@ (defn- coll-prob [x kfn kform distinct count min-count max-count path via in] - (cond - (not (seqable? x)) - [{:path path :pred 'seqable? :val x :via via :in in}] + (let [pred (c/or kfn coll?) + kform (c/or kform `coll?)] + (cond + (not (valid? pred x)) + (explain-1 kform pred path via in x) - (c/and distinct (not (empty? x)) (not (apply distinct? x))) - [{:path path :pred 'distinct? :val x :via via :in in}] + (c/and distinct (not (empty? x)) (not (apply distinct? x))) + [{:path path :pred 'distinct? :val x :via via :in in}] - (c/and count (not= count (bounded-count count x))) - [{:path path :pred `(= ~count (c/count ~'%)) :val x :via via :in in}] + (c/and count (not= count (bounded-count count x))) + [{:path path :pred `(= ~count (c/count ~'%)) :val x :via via :in in}] - (c/and (c/or min-count max-count) - (not (<= (c/or min-count 0) - (bounded-count (if max-count (inc max-count) min-count) x) - (c/or max-count MAX_INT)))) - [{:path path :pred `(<= ~(c/or min-count 0) ~(c/count ~'%) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}])) + (c/and (c/or min-count max-count) + (not (<= (c/or min-count 0) + (bounded-count (if max-count (inc max-count) min-count) x) + (c/or max-count MAX_INT)))) + [{:path path :pred `(<= ~(c/or min-count 0) ~(c/count ~'%) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}]))) (defn ^:skip-wiki merge-spec-impl "Do not call this directly, use 'merge'" @@ -719,13 +721,12 @@ "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" ([form pred opts] (every-impl form pred opts nil)) ([form pred {gen-into :into - :keys [kind count max-count min-count distinct gen-max ::kfn + :keys [kind ::kind-form count max-count min-count distinct gen-max ::kfn conform-keys ::conform-all] :or {gen-max 20, gen-into []} :as opts} gfn] - (let [conform-into (c/or gen-into kind) - gen-into (c/or gen-into kind []) + (let [conform-into gen-into check? #(valid? pred %) kfn (c/or kfn (fn [i v] i)) addcv (fn [ret i v cv] (conj ret cv)) @@ -746,7 +747,7 @@ (assoc ret i cv))) identity] - (c/and (map? x) (map? conform-into)) + (c/and (map? x) (c/or (c/and kind (not conform-into)) (map? conform-into))) [(if conform-keys empty identity) (fn [ret i v cv] (if (c/and (identical? v cv) (not conform-keys)) @@ -761,7 +762,7 @@ Spec (conform* [_ x] (cond - (coll-prob x kindfn kindform distinct count min-count max-count + (coll-prob x kind kind-form distinct count min-count max-count nil nil nil) ::invalid @@ -788,7 +789,7 @@ ::invalid)))) (unform* [_ x] x) (explain* [_ path via in x] - (c/or (coll-prob x kindfn kindform distinct count min-count max-count + (c/or (coll-prob x kind kind-form distinct count min-count max-count path via in) (apply concat ((if conform-all identity (partial take *coll-error-limit*)) @@ -802,26 +803,32 @@ (gen* [_ overrides path rmap] (if gfn (gfn) - (let [init (empty gen-into) - pgen (gensub pred overrides path rmap form)] - (gen/fmap - #(if (vector? init) % (into init %)) + (let [pgen (gensub pred overrides path rmap form)] + (gen/bind (cond - distinct - (if count - (gen/vector-distinct pgen {:num-elements count :max-tries 100}) - (gen/vector-distinct pgen {:min-elements (c/or min-count 0) - :max-elements (c/or max-count (max gen-max (c/* 2 (c/or min-count 0)))) - :max-tries 100})) - - count - (gen/vector pgen count) - - (c/or min-count max-count) - (gen/vector pgen (c/or min-count 0) (c/or max-count (max gen-max (c/* 2 (c/or min-count 0))))) - - :else - (gen/vector pgen 0 gen-max)))))) + gen-into (gen/return (empty gen-into)) + kind (gen/fmap #(if (empty? %) % (empty %)) + (gensub kind overrides path rmap form)) + :else (gen/return [])) + (fn [init] + (gen/fmap + #(if (vector? init) % (into init %)) + (cond + distinct + (if count + (gen/vector-distinct pgen {:num-elements count :max-tries 100}) + (gen/vector-distinct pgen {:min-elements (c/or min-count 0) + :max-elements (c/or max-count (max gen-max (c/* 2 (c/or min-count 0)))) + :max-tries 100})) + + count + (gen/vector pgen count) + + (c/or min-count max-count) + (gen/vector pgen (c/or min-count 0) (c/or max-count (max gen-max (c/* 2 (c/or min-count 0))))) + + :else + (gen/vector pgen 0 gen-max)))))))) (with-gen* [_ gfn] (every-impl form pred opts gfn)) (describe* [_] `(every ~form ~@(mapcat identity opts))))))) From 053d7f1ead6698b38e7ff656e0910ebc8bb8f729 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 26 Jun 2016 15:57:52 -0400 Subject: [PATCH 1889/4033] CLJS-1694: Self-host: Port macro var inference in :refer Employ the same general strategy used in JVM ClojureScript: Defer checking uses until after macro processing of the namespace form is complete, feeding missing uses to check-uses-macros, employing a factored out common check-use-macros-inferring-missing that JVM and bootstrapped ClojureScript can use (with this factored funtion taking care of finding inferred macros and updating the compilation environment). In short, the bootstrap port of the feature is faithful to the JVM implementation of the feature. Add a new unit test for this feature and tweak the self-host-parity test to avoid loading clojure.template, which otherwise results in the compiler enviroment dynamic var being inadvertently cleared. --- src/main/cljs/cljs/js.cljs | 78 +++++++++++++++-------------- src/main/clojure/cljs/analyzer.cljc | 14 ++++-- src/test/self/self_host/test.cljs | 23 ++++++++- src/test/self/self_parity/test.cljs | 3 +- 4 files changed, 74 insertions(+), 44 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 95a7a3b3c..bdf0e0f9c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -390,43 +390,47 @@ (letfn [(check-uses-and-load-macros [res] (if (:error res) (cb res) - (let [res (try - (when (and (:*analyze-deps* bound-vars) (seq uses)) - (when (:verbose opts) (debug-prn "Checking uses")) - (ana/check-uses uses env) - {:value nil}) - (catch :default cause - (wrap-error - (ana/error ana-env - (str "Could not parse ns form " (:name ast)) cause))))] - (if (:error res) - (cb res) - (if (:*load-macros* bound-vars) - (do - (when (:verbose opts) (debug-prn "Processing :use-macros for" (:name ast))) - (load-macros bound-vars :use-macros use-macros reload reloads opts - (fn [res] - (if (:error res) - (cb res) - (do - (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) - (load-macros bound-vars :require-macros require-macros reloads reloads opts - (fn [res] - (if (:error res) - (cb res) - (let [res (try - (when (seq use-macros) - (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) - (ana/check-use-macros use-macros env)) - {:value nil} - (catch :default cause - (wrap-error - (ana/error ana-env - (str "Could not parse ns form " (:name ast)) cause))))] - (if (:error res) - (cb res) - (cb {:value ast}))))))))))) - (cb {:value ast}))))))] + (let [missing (when (and (:*analyze-deps* bound-vars) (seq uses)) + (ana/missing-uses uses env))] + (if (:*load-macros* bound-vars) + (do + (when (:verbose opts) (debug-prn "Processing :use-macros for" (:name ast))) + (load-macros bound-vars :use-macros use-macros reload reloads opts + (fn [res] + (if (:error res) + (cb res) + (do + (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) + (load-macros bound-vars :require-macros require-macros reloads reloads opts + (fn [res] + (if (:error res) + (cb res) + (let [res (try + (when (seq use-macros) + (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) + (ana/check-use-macros use-macros env)) + {:value nil} + (catch :default cause + (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause))))] + (if (:error res) + (cb res) + (try + (let [ast' (ana/check-use-macros-inferring-missing ast (:name ast) use-macros missing env)] + (cb {:value ast'})) + (catch :default cause + (cb (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause))))))))))))))) + (try + (when (:verbose opts) (debug-prn "Checking uses")) + (ana/check-uses missing env) + (cb {:value ast}) + (catch :default cause + (cb (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause)))))))))] (cond (and load (seq deps)) (load-deps bound-vars ana-env (:name ast) deps (dissoc opts :macros-ns) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1ca77b51c..0eec93d12 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1754,6 +1754,14 @@ (check-uses (missing-use-macros missing-uses env) env) (inferred-use-macros missing-uses env)))) +(defn check-use-macros-inferring-missing + [ast name use-macros missing-uses env] + (let [ast' (update-in ast [:use-macros] merge + (check-use-macros use-macros missing-uses env))] + (swap! env/*compiler* update-in + [::namespaces name :use-macros] merge (:use-macros ast')) + ast')) + (defn parse-ns-error-msg [spec msg] (str msg "; offending spec: " (pr-str spec))) @@ -2653,11 +2661,7 @@ (locking load-mutex (clojure.core/require nsym))) (intern-macros nsym k))) - (let [ast' (update-in ast [:use-macros] merge - (check-use-macros use-macros missing env))] - (swap! env/*compiler* update-in - [::namespaces name :use-macros] merge (:use-macros ast')) - ast')) + (check-use-macros-inferring-missing ast name use-macros missing env)) (do (check-uses missing env) ast)))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 2640a8e84..a305bfda0 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -349,7 +349,7 @@ (deftest test-load-and-invoke-macros (async done - (let [l (latch 9 done)] + (let [l (latch 10 done)] ;; Normal require macros (let [st (cljs/empty-state)] (cljs/eval-str st @@ -526,6 +526,27 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (= 300 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Rely on implicit macro inference (ns loads its own macros) + (cljs/eval-str st + "(ns cljs.user (:require [foo.core :refer [add]]))" + nil + {:eval node-eval + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns foo.core (:require-macros foo.core))"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(add 110 210)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 320 value)) (inc! l))))))))) (deftest test-eval-str-with-require-macros diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index bf8bbe0ad..bf36e5d2b 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -164,7 +164,8 @@ 'cljs.pprint 'cljs.env.macros 'cljs.analyzer.macros - 'cljs.compiler.macros} + 'cljs.compiler.macros + 'clojure.template} #{'goog.object 'goog.string 'goog.string.StringBuffer From ed6c100452676283ff2786d7bef4a11a196ae8b4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 2 Jul 2016 18:42:50 -0400 Subject: [PATCH 1890/4033] CLJS-1699: Update docstring for ns - Update "macros written in Clojure" copy - Add explanation of implicit macro var infer - Add an example under Implicit macro loading - Add explanation and example for auto-aliasing of clojure.* to cljs.* --- src/main/clojure/cljs/repl.cljc | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index f928de068..bd2eaf9f4 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1021,18 +1021,26 @@ * :import is available for importing Google Closure classes - ClojureScript types and records should be brought in with :use or :require :refer, not :import ed - * Macros are written in Clojure, and are referenced via the new - :require-macros / :use-macros options to ns + * Macros must be defined in a different compilation stage than the one + from where they are consumed. One way to achieve this is to define + them in one namespace and use them from another. They are referenced + via the :require-macros / :use-macros options to ns - :require-macros and :use-macros support the same forms that :require and :use do Implicit macro loading: If a namespace is required or used, and that namespace itself requires or uses macros from its own namespace, then the macros will be implicitly required or used using the same - specifications. This oftentimes leads to simplified library usage, - such that the consuming namespace need not be concerned about + specifications. Furthermore, in this case, macro vars may be included + in a :refer or :only spec. This oftentimes leads to simplified library + usage, such that the consuming namespace need not be concerned about explicitly distinguishing between whether certain vars are functions - or macros. + or macros. For example: + + (ns testme.core (:require [cljs.test :as test :refer [test-var deftest]])) + + will result in test/is resolving properly, along with the test-var + function and the deftest macro being available unqualified. Inline macro specification: As a convenience, :require can be given either :include-macros true or :refer-macros [syms...]. Both desugar @@ -1050,7 +1058,18 @@ (:require [foo.core :as foo :refer [foo-fn]] [woz.core :as woz :refer [woz-fn]]) (:require-macros [foo.core :as foo] - [woz.core :as woz :refer [app jx]]))"} + [woz.core :as woz :refer [app jx]])) + + Auto-aliasing clojure namespaces: If a non-existing clojure.* namespace + is required or used and a matching cljs.* namespace exists, the cljs.* + namespace will be loaded and an alias will be automatically established + from the clojure.* namespace to the cljs.* namespace. For example: + + (ns testme.core (:require [clojure.test])) + + will be automatically converted to + + (ns testme.core (:require [cljs.test :as clojure.test]))"} def {:forms [(def symbol doc-string? init?)] :doc "Creates and interns a global var with the name of symbol in the current namespace (*ns*) or locates such a var if From 7264ca4ed0502fd69acea04e5d01c9bd1a3cb687 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 3 Jul 2016 10:44:04 -0400 Subject: [PATCH 1891/4033] CLJS-1697: doc on inferred macros fails When inferring a macro, additionally remove its symbol entry in :uses. --- src/main/clojure/cljs/analyzer.cljc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 0eec93d12..a1a746308 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1756,10 +1756,14 @@ (defn check-use-macros-inferring-missing [ast name use-macros missing-uses env] - (let [ast' (update-in ast [:use-macros] merge - (check-use-macros use-macros missing-uses env))] - (swap! env/*compiler* update-in - [::namespaces name :use-macros] merge (:use-macros ast')) + (let [remove-missing-uses #(apply dissoc % (keys missing-uses)) + ast' (-> ast + (update-in [:use-macros] merge + (check-use-macros use-macros missing-uses env)) + (update-in [:uses] remove-missing-uses))] + (swap! env/*compiler* #(-> % + (update-in [::namespaces name :use-macros] merge (:use-macros ast')) + (update-in [::namespaces name :uses] remove-missing-uses))) ast')) (defn parse-ns-error-msg [spec msg] From 16af9f651f09e5c3f91098270ffacb806b907302 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 2 Jul 2016 13:10:39 -0400 Subject: [PATCH 1892/4033] CLJS-1695: Self-host: Port cljs / clojure namespace aliasing If a failed attempt is made to load a clojure.* namespace, retry with the cljs.* version. If that load succeeds, proceed to patch the alias map in the compiler analysis metadata state with the new namespace symbol and also rewrite the ns ast so that it will pass subsequent namespace analysis checks and related processing. --- src/main/cljs/cljs/js.cljs | 79 ++++++++++++++++------ src/test/cljs/cljs/clojure_alias_test.cljs | 10 +++ src/test/cljs/cljs/test_runner.cljs | 6 +- src/test/cljs/test_runner.cljs | 6 +- src/test/self/self_parity/test.cljs | 6 +- 5 files changed, 79 insertions(+), 28 deletions(-) create mode 100644 src/test/cljs/cljs/clojure_alias_test.cljs diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index bdf0e0f9c..f55275017 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -10,6 +10,7 @@ (:require-macros [cljs.js :refer [dump-core]] [cljs.env.macros :as env]) (:require [clojure.string :as string] + [clojure.walk :as walk] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.compiler :as comp] @@ -296,6 +297,21 @@ (declare ns-side-effects analyze-deps) +(defn- patch-alias-map + [compiler in from to] + (let [patch (fn [k add-if-present?] + (swap! compiler update-in [::ana/namespaces in k] + (fn [m] + (let [replaced (walk/postwalk-replace {from to} m)] + (if (and add-if-present? + (some #{to} (vals replaced))) + (assoc replaced from to) + replaced)))))] + (patch :requires true) + (patch :require-macros true) + (patch :uses false) + (patch :use-macros false))) + (defn- load-deps ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars ana-env lib deps nil cb)) @@ -308,15 +324,28 @@ (str "Circular dependency detected " (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) (if (seq deps) - (let [dep (first deps)] - (require bound-vars dep - (-> opts - (dissoc :context) - (dissoc :ns)) + (let [dep (first deps) + opts' (-> opts + (dissoc :context) + (dissoc :ns))] + (require bound-vars dep opts' (fn [res] (if-not (:error res) (load-deps bound-vars ana-env lib (next deps) opts cb) - (cb res))))) + (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] + (get {dep nil} cljs-ns cljs-ns))] + (require bound-vars cljs-dep opts' + (fn [res] + (if (:error res) + (cb res) + (do + (patch-alias-map (:*compiler* bound-vars) lib dep cljs-dep) + (load-deps bound-vars ana-env lib (next deps) opts + (fn [res] + (if (:error res) + (cb res) + (cb (update res :aliased-loads assoc dep cljs-dep))))))))) + (cb res)))))) (cb {:value nil}))))) (declare analyze-str*) @@ -378,6 +407,12 @@ (cb res))))) (cb {:value nil}))) +(defn- rewrite-ns-ast + [ast smap] + (-> ast + (update :uses #(walk/postwalk-replace smap %)) + (update :requires #(merge smap (walk/postwalk-replace smap %))))) + (defn- ns-side-effects ([bound-vars ana-env ast opts cb] (ns-side-effects false bound-vars ana-env ast opts cb)) @@ -385,9 +420,9 @@ (when (:verbose opts) (debug-prn "Namespace side effects for" (:name ast))) (if (= :ns op) - (let [{:keys [deps uses requires require-macros use-macros reload reloads]} ast - env (:*compiler* bound-vars)] - (letfn [(check-uses-and-load-macros [res] + (letfn [(check-uses-and-load-macros [res rewritten-ast] + (let [env (:*compiler* bound-vars) + {:keys [uses requires require-macros use-macros reload reloads]} rewritten-ast] (if (:error res) (cb res) (let [missing (when (and (:*analyze-deps* bound-vars) (seq uses)) @@ -417,7 +452,7 @@ (if (:error res) (cb res) (try - (let [ast' (ana/check-use-macros-inferring-missing ast (:name ast) use-macros missing env)] + (let [ast' (ana/check-use-macros-inferring-missing rewritten-ast (:name ast) use-macros missing env)] (cb {:value ast'})) (catch :default cause (cb (wrap-error @@ -430,18 +465,18 @@ (catch :default cause (cb (wrap-error (ana/error ana-env - (str "Could not parse ns form " (:name ast)) cause)))))))))] - (cond - (and load (seq deps)) - (load-deps bound-vars ana-env (:name ast) deps (dissoc opts :macros-ns) - check-uses-and-load-macros) - - (and (not load) (:*analyze-deps* bound-vars) (seq deps)) - (analyze-deps bound-vars ana-env (:name ast) deps (dissoc opts :macros-ns) - check-uses-and-load-macros) - - :else - (check-uses-and-load-macros {:value nil})))) + (str "Could not parse ns form " (:name ast)) cause))))))))))] + (cond + (and load (seq (:deps ast))) + (load-deps bound-vars ana-env (:name ast) (:deps ast) (dissoc opts :macros-ns) + #(check-uses-and-load-macros % (rewrite-ns-ast ast (:aliased-loads %)))) + + (and (not load) (:*analyze-deps* bound-vars) (seq (:deps ast))) + (analyze-deps bound-vars ana-env (:name ast) (:deps ast) (dissoc opts :macros-ns) + #(check-uses-and-load-macros % ast)) + + :else + (check-uses-and-load-macros {:value nil} ast))) (cb {:value ast})))) (defn- analyze-str* [bound-vars source name opts cb] diff --git a/src/test/cljs/cljs/clojure_alias_test.cljs b/src/test/cljs/cljs/clojure_alias_test.cljs new file mode 100644 index 000000000..7067be001 --- /dev/null +++ b/src/test/cljs/cljs/clojure_alias_test.cljs @@ -0,0 +1,10 @@ +(ns cljs.clojure-alias-test + "Tests requiring via `clojure.*` instead of `cljs.*`" + (:require [clojure.test :refer [deftest is]] + [clojure.spec :as s :refer [spec?]])) + +(deftest normal-test + (is (= 1 1))) + +(deftest aliases-test + (is (= spec? clojure.spec/spec? cljs.spec/spec?))) diff --git a/src/test/cljs/cljs/test_runner.cljs b/src/test/cljs/cljs/test_runner.cljs index 1e93a5cec..ecd517b0a 100644 --- a/src/test/cljs/cljs/test_runner.cljs +++ b/src/test/cljs/cljs/test_runner.cljs @@ -15,7 +15,8 @@ [cljs.import-test] [cljs.ns-test.foo] [cljs.pprint] - [cljs.spec-test])) + [cljs.spec-test] + [cljs.clojure-alias-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -36,4 +37,5 @@ 'foo.ns-shadow-test 'cljs.import-test 'cljs.pprint - 'cljs.spec-test) + 'cljs.spec-test + 'cljs.clojure-alias-test) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index b512ede15..a23fd8b1f 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -16,7 +16,8 @@ [cljs.syntax-quote-test] [cljs.import-test] [cljs.ns-test.foo] - [cljs.pprint])) + [cljs.pprint] + [cljs.clojure-alias-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -38,4 +39,5 @@ 'cljs.ns-test.foo 'foo.ns-shadow-test 'cljs.import-test - 'cljs.pprint) + 'cljs.pprint + 'cljs.clojure-alias-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index bf36e5d2b..1171a2c15 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -265,7 +265,8 @@ [cljs.import-test] [cljs.ns-test.foo] #_[cljs.pprint] - [cljs.spec-test])) + [cljs.spec-test] + [cljs.clojure-alias-test])) (fn [{:keys [value error]}] (if error (prn error) @@ -286,7 +287,8 @@ 'foo.ns-shadow-test 'cljs.import-test #_'cljs.pprint - 'cljs.spec-test) + 'cljs.spec-test + 'cljs.clojure-alias-test) (fn [{:keys [value error]}] (when error (prn error))))))))) From 16bc7ace746e1c0d67f4628339c51aad0668c03d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 1 Jul 2016 19:22:49 -0400 Subject: [PATCH 1893/4033] CLJS-1698: cljs.spec: every res call needs &env --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index bbb110cb7..175e8c247 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -219,7 +219,7 @@ See also - coll-of, every-kv " [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] - (let [nopts (-> opts (dissoc :gen) (assoc ::kind-form `'~(res (:kind opts))))] + (let [nopts (-> opts (dissoc :gen) (assoc ::kind-form `'~(res &env (:kind opts))))] `(cljs.spec/every-impl '~pred ~pred ~nopts ~gen))) (defmacro every-kv From 15790875cbac55ca57a0f58389715f31165a19bf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 Jul 2016 08:57:35 -0400 Subject: [PATCH 1894/4033] merge, not flow, in merge conform/unform same as Clojure 0f2e5e5 --- src/main/cljs/cljs/spec.cljs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 1b984054c..96cc6c8b8 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -701,8 +701,11 @@ [forms preds gfn] (reify Spec - (conform* [_ x] (and-preds x preds forms)) - (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) + (conform* [_ x] (let [ms (map #(dt %1 x %2) preds forms)] + (if (some #{::invalid} ms) + ::invalid + (apply c/merge ms)))) + (unform* [_ x] (apply c/merge (map #(unform % x) (reverse preds)))) (explain* [_ path via in x] (apply concat (map #(explain-1 %1 %2 path via in x) From a88a98cd91aa09932961ab71759b275226ccd5a0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 Jul 2016 17:24:04 -0400 Subject: [PATCH 1895/4033] add any? to core, remove ::spec/any, gens for any? and some? Same as Clojure 0929d1d --- src/main/cljs/cljs/core.cljs | 4 ++++ src/main/cljs/cljs/spec.cljc | 2 +- src/main/cljs/cljs/spec.cljs | 1 - src/main/cljs/cljs/spec/impl/gen.cljs | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0a41de165..eb0345256 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -231,6 +231,10 @@ [x] (gstring/isUnicodeChar x)) +(defn ^boolean any + "Returns true if given any argument." + [x] true) + (set! *unchecked-if* true) (defn ^boolean native-satisfies? "Internal - do not use!" diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 175e8c247..1fd671448 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -434,7 +434,7 @@ specified, return speced vars from all namespaces." user=> (s/conform (s/cat :i1 integer? :m (s/keys* :req-un [::a ::c]) :i2 integer?) [42 :a 1 :c 2 :d 4 99]) {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" [& kspecs] - `(& (* (cat ::k keyword? ::v ::any)) ::kvs->map (keys ~@kspecs))) + `(& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map (keys ~@kspecs))) (defmacro nilable "returns a spec that accepts nil and values satisfiying pred" diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 96cc6c8b8..147bddf1e 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1239,7 +1239,6 @@ (describe* [_] `(fspec :args ~aform :ret ~rform :fn ~fform))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(cljs.spec/def ::any (cljs.spec/spec (constantly true) :gen gen/any)) (cljs.spec/def ::kvs->map (cljs.spec/conformer #(zipmap (map ::k %) (map ::v %)) #(map (fn [[k v]] {::k k ::v v}) %))) (defn exercise diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index 500d7158c..897d9aca6 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -87,7 +87,8 @@ gens, each of which should generate something sequential." gen-builtins (c/delay (let [simple (simple-type-printable)] - {number? (one-of [(large-integer) (double)]) + {any? (one-of [(return nil) (any-printable)]) + number? (one-of [(large-integer) (double)]) integer? (large-integer) int? (large-integer) pos-int? (large-integer* {:min 1}) From 180584bdf29e0cf5eafb617697b038787532672e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 Jul 2016 17:27:43 -0400 Subject: [PATCH 1896/4033] gen overrides should be no-arg fns same as Clojure 357df34 --- src/main/cljs/cljs/spec.cljs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 147bddf1e..ad128a1b7 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -226,17 +226,18 @@ [spec overrides path rmap form] ;;(prn {:spec spec :over overrides :path path :form form}) (let [spec (specize spec)] - (if-let [g (c/or (get overrides (c/or (spec-name spec) spec)) - (get overrides path) - (gen* spec overrides path rmap))] + (if-let [g (c/or (when-let [gfn (c/or (get overrides (c/or (spec-name spec) spec)) + (get overrides path))] + (gfn)) + (gen* spec overrides path rmap))] (gen/such-that #(valid? spec %) g 100) (throw (js/Error. (str "Unable to construct gen at: " path " for: " (abbrev form))))))) (defn gen "Given a spec, returns the generator for it, or throws if none can be constructed. Optionally an overrides map can be provided which - should map spec names or paths (vectors of keywords) to - generators. These will be used instead of the generators at those + should map spec names or paths (vectors of keywords) to no-arg + generator-creating fns. These will be used instead of the generators at those names/paths. Note that parent generator (in the spec or overrides map) will supersede those of any subtrees. A generator for a regex op must always return a sequential collection (i.e. a generator for From 3ae58d57d5c05eeb92494f26364f5cb883968619 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 Jul 2016 17:38:17 -0400 Subject: [PATCH 1897/4033] typo cljs.core/any -> cljs.core/any? --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index eb0345256..347ee79ba 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -231,7 +231,7 @@ [x] (gstring/isUnicodeChar x)) -(defn ^boolean any +(defn ^boolean any? "Returns true if given any argument." [x] true) From ef2417a484fa8cf75b100863d79b0503beac0f95 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 Jul 2016 17:41:18 -0400 Subject: [PATCH 1898/4033] with-gen now works on regexes w/o lifting to specs, used by keys* so it can now gen same as Clojure 1e23644 --- src/main/cljs/cljs/spec.cljc | 4 +++- src/main/cljs/cljs/spec.cljs | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 1fd671448..78c4570c5 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -434,7 +434,9 @@ specified, return speced vars from all namespaces." user=> (s/conform (s/cat :i1 integer? :m (s/keys* :req-un [::a ::c]) :i2 integer?) [42 :a 1 :c 2 :d 4 99]) {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" [& kspecs] - `(& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map (keys ~@kspecs))) + `(let [mspec# (keys ~@kspecs)] + (with-gen (& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) + (fn [] (gen/fmap (fn [m#] (apply concat m#)) (gen mspec#)))))) (defmacro nilable "returns a spec that accepts nil and values satisfiying pred" diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index ad128a1b7..57e347cc3 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -162,7 +162,10 @@ (defn with-gen "Takes a spec and a no-arg, generator-returning fn and returns a version of that spec that uses that generator" [spec gen-fn] - (with-gen* (specize spec) gen-fn)) + (let [spec (reg-resolve spec)] + (if (regex? spec) + (assoc spec ::gfn gen-fn) + (with-gen* (specize spec) gen-fn)))) (defn explain-data* [spec path via in x] (when-let [probs (explain* (specize spec) path via in x)] @@ -1083,7 +1086,7 @@ (defn- re-gen [p overrides path rmap f] ;;(prn {:op op :ks ks :forms forms}) - (let [{:keys [::op ps ks p1 p2 forms splice ret id] :as p} (reg-resolve! p) + (let [{:keys [::op ps ks p1 p2 forms splice ret id ::gfn] :as p} (reg-resolve! p) rmap (if id (inck rmap id) rmap) ggens (fn [ps ks forms] (let [gen (fn [p k f] @@ -1097,6 +1100,8 @@ (case op (:accept nil) (gen/fmap vector g) g)) + (when gfn + (gfn)) (when p (case op ::accept (if (= ret ::nil) From 9bef49489f2c1dc84a1835f0c8fc4a26f96c0919 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 Jul 2016 17:48:47 -0400 Subject: [PATCH 1899/4033] drop all instrument related code from cljs.spec, will be reimplemented in cljs.spec.test --- src/main/cljs/cljs/spec.cljc | 55 ---------------------------- src/main/cljs/cljs/spec.cljs | 70 ------------------------------------ 2 files changed, 125 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 78c4570c5..b67b6a3b2 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -413,12 +413,6 @@ specified, return speced vars from all namespaces." (swap! _speced_vars conj (ns-qualify &env fn-sym)) `(cljs.spec/def ~fn-sym (cljs.spec/fspec ~@specs))) -(defmacro with-instrument-disabled - "Disables instrument's checking of calls, within a scope." - [& body] - `(binding [cljs.spec/*instrument-enabled* nil] - ~@body)) - (defmacro keys* "takes the same arguments as spec/keys and returns a regex op that matches sequences of key/values, converts them into a map, and conforms that map with a corresponding @@ -462,55 +456,6 @@ specified, return speced vars from all namespaces." `(spec (and c/int? #(cljs.spec/int-in-range? ~start ~end %)) :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) -(defmacro instrument - "Instruments the var at v, a var or symbol, to check specs - registered with fdef. Wraps the fn at v to check :args - spec, if it exist, throwing an ex-info with explain-data if a - check fails. Idempotent." - [v] - (let [v (if-not (seq? v) (list 'var v) v) - sym (second v)] - `(do - (when-let [checked# (cljs.spec/instrument* ~v)] - (set! ~sym checked#)) - ~v))) - -(defmacro unstrument - "Undoes instrument on the var at v, a var or symbol. Idempotent." - [v] - (let [v (if-not (seq? v) (list 'var v) v) - sym (second v)] - `(do - (when-let [raw# (cljs.spec/unstrument* ~v)] - (set! ~sym raw#)) - ~v))) - -(defmacro instrument-ns - "Call instrument for all speced-vars in namespaces named -by ns-syms. Idempotent." - [& ns-syms] - `(do - ~@(map #(list 'cljs.spec/instrument %) (speced-vars* ns-syms)))) - -(defmacro unstrument-ns - "Call unstrument for all speced-vars in namespaces named -by ns-syms. Idempotent." - [& ns-syms] - `(do - ~@(map #(list 'cljs.spec/unstrument %) (speced-vars* ns-syms)))) - -(defmacro instrument-all - "Call instrument for all speced-vars. Idempotent." - [] - `(do - ~@(map #(list 'cljs.spec/instrument %) (speced-vars*)))) - -(defmacro unstrument-all - "Call unstrument for all speced-vars. Idempotent" - [] - `(do - ~@(map #(list 'cljs.spec/unstrument %) (speced-vars*)))) - (defmacro merge "Takes map-validating specs (e.g. 'keys' specs) and returns a spec that returns a conformed map satisfying all of the diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 57e347cc3..180b868f0 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -36,10 +36,6 @@ "The number of errors reported by explain in a collection spec'ed with 'every'" 20) -(def ^:private ^:dynamic *instrument-enabled* - "if false, instrumented fns call straight through" - true) - (defprotocol Spec (conform* [spec x]) (unform* [spec y]) @@ -278,41 +274,6 @@ (declare map-spec) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn- expect - "Returns nil if v conforms to spec, else throws ex-info with explain-data." - [spec v] - ) - -(defn- fn-spec? - "Fn-spec must include at least :args or :ret specs." - [m] - (c/or (:args m) (:ret m))) - -(defn- spec-checking-fn - [v f] - (let [conform! (fn [v role spec data args] - (let [conformed (conform spec data)] - (if (= ::invalid conformed) - (let [ed (assoc (explain-data* spec [role] [] [] data) - ::args args)] - (throw (ex-info - (str "Call to " (pr-str v) " did not conform to spec:\n" (with-out-str (explain-out ed))) - ed))) - conformed)))] - (cond-> - (c/fn - [& args] - (if *instrument-enabled* - (s/with-instrument-disabled - (let [specs (get-spec v)] - (when (:args specs) (conform! v :args (:args specs) args args)) - (binding [*instrument-enabled* true] - (apply f args)))) - (apply f args))) - (not (instance? MultiFn f)) (doto (gobj/extend f))))) - (defn- macroexpand-check [v args] (let [specs (get-spec v)] @@ -326,37 +287,6 @@ "Call to " (->sym v) " did not conform to spec:\n" (with-out-str (explain-out ed)))))))))) -(defn- no-fn-spec - [v specs] - (ex-info (str "Fn at " (pr-str v) " is not spec'ed.") - {:var v :specs specs})) - -(def ^:private instrumented-vars - "Map for instrumented vars to :raw/:wrapped fns" - (atom {})) - -(defn instrument* - [v] - (let [spec (get-spec v)] - (if (fn-spec? spec) - (locking instrumented-vars - (let [{:keys [raw wrapped]} (get @instrumented-vars v) - current @v] - (when-not (= wrapped current) - (let [checked (spec-checking-fn v current)] - (swap! instrumented-vars assoc v {:raw current :wrapped checked}) - checked)))) - (throw (no-fn-spec v spec))))) - -(defn unstrument* - [v] - (locking instrumented-vars - (when-let [{:keys [raw wrapped]} (get @instrumented-vars v)] - (let [current @v] - (when (= wrapped current) - (swap! instrumented-vars dissoc v) - raw))))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; impl ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- recur-limit? [rmap id path k] (c/and (> (get rmap id) (::recursion-limit rmap)) From 4a7ed2c14dc48f9f4d954eb0e5b1ffc15607db1b Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 12 Jul 2016 13:50:01 -0400 Subject: [PATCH 1900/4033] comment out instrument test for now --- src/test/cljs/cljs/spec_test.cljs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 9df5b88aa..4f5d1cd0b 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -24,12 +24,13 @@ :args (s/cat :a integer? :b (s/? integer?)) :ret integer?) -(s/instrument #'adder) +;;(s/instrument #'adder) (deftest test-multi-arity-instrument (is (= 1 (adder 1))) (is (= 3 (adder 1 2))) - (is (thrown? js/Error (adder "foo")))) + ;;(is (thrown? js/Error (adder "foo"))) + ) (defmulti testmm :type) (defmethod testmm :default [_]) @@ -37,11 +38,12 @@ (s/fdef testmm :args (s/cat :m map?) :ret string?) -(s/instrument #'testmm) +;;(s/instrument #'testmm) (deftest test-multifn-instrument (is (= "good" (testmm {:type :good}))) - (is (thrown? js/Error (testmm "foo")))) + ;;(is (thrown? js/Error (testmm "foo"))) + ) (deftest int-in-test (is (s/valid? (s/int-in 1 3) 2)) From fffe11553717e71a2632a1e27a4161bdd6bb427b Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 12 Jul 2016 14:02:03 -0400 Subject: [PATCH 1901/4033] make conform/unform roundtrip comment a test --- src/test/cljs/cljs/spec_test.cljs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 4f5d1cd0b..4c755b888 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -53,10 +53,6 @@ (is (s/valid? (s/inst-in #inst "1999" #inst "2001") #inst "2000")) (is (not (s/valid? (s/inst-in #inst "1999" #inst "2001") #inst "1492")))) -(comment - - (s/conform s2 [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]) - (s/unform s2 - (s/conform s2 [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11])) - - ) \ No newline at end of file +(deftest test-conform-unform + (let [xs [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]] + (is (= xs (s/unform s2 (s/conform s2 xs)))))) From 2f854f7d369556b52c93b4f9900b18c6b16a2f1d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 13 Jul 2016 17:29:47 -0400 Subject: [PATCH 1902/4033] add support for parsing Node.js stacktraces --- src/main/cljs/cljs/stacktrace.cljc | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index fc8510c0f..80905422b 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -497,6 +497,50 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" {:output-dir ".cljs_nashorn_repl"}) ) +;; ----------------------------------------------------------------------------- +;; Node.js Stacktrace + +(defmethod parse-stacktrace :nodejs + [repl-env st err {:keys [output-dir] :as opts}] + (letfn [(process-frame [frame-str] + (when-not (or (string/blank? frame-str) + (== -1 (.indexOf frame-str " at"))) + (let [frame-str (string/replace frame-str #"\s+at\s+" "") + [function file-and-line] (string/split frame-str #"\s+") + [file-part line-part] (string/split file-and-line #":")] + {:file (string/replace (.substring file-part 1) + (str output-dir + #?(:clj File/separator :cljs "/")) + "") + :function function + :line (when (and line-part (not (string/blank? line-part))) + (parse-int + (.substring line-part 0 + (dec (count line-part))))) + :column 0})))] + (->> (string/split st #"\n") + (map process-frame) + (remove nil?) + vec))) + +(comment + (parse-stacktrace {} + "Error: 1 is not ISeqable + at cljs$core$seq (.cljs_node_repl/cljs/core.cljs:1118:20) + at repl:1:65 + at repl:9:4 + at repl:17:3 + at repl:22:4 + at Object.exports.runInThisContext (vm.js:54:17) + at Domain. ([stdin]:41:34) + at Domain.run (domain.js:228:14) + at Socket. ([stdin]:40:25) + at emitOne (events.js:77:13)" + + {:ua-product :nodejs} + {:output-dir ".cljs_node_repl"}) + ) + ;; ----------------------------------------------------------------------------- ;; Stacktrace Mapping From b4f032b40ac0e1b3c0e7ec6dbd4bf79e8cfb7dc7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 13 Jul 2016 17:32:39 -0400 Subject: [PATCH 1903/4033] new instrument wip --- src/main/cljs/cljs/spec/test.cljc | 12 ++- src/main/cljs/cljs/spec/test.cljs | 124 ++++++++++++++++++++++++++---- 2 files changed, 119 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 50fc84595..98e322990 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -9,9 +9,15 @@ (ns cljs.spec.test (:require [cljs.analyzer :as ana] - [cljs.spec :as spec] + [cljs.spec :as s] [cljs.spec.impl.gen :as gen])) +(defmacro with-instrument-disabled + "Disables instrument's checking of calls, within a scope." + [& body] + `(binding [*instrument-enabled* nil] + ~@body)) + (defmacro run-tests "Like run-all-tests, but scoped to specific namespaces, or to *ns* if no ns-sym are specified." @@ -19,7 +25,7 @@ `(cljs.spec.test/run-tests '~ana/*cljs-ns*)) ([& ns-syms] `(cljs.spec.test/run-var-tests - (->> #?(:clj ~(spec/speced-vars* ns-syms) + (->> #?(:clj ~(s/speced-vars* ns-syms) :cljs ~(cljs.spec$macros/speced-vars* ns-syms)) (filter (fn [v#] (:args (cljs.spec/get-spec v#)))))))) @@ -28,5 +34,5 @@ for all speced vars. Prints per-test results to *out*, and returns a map with :test,:pass,:fail, and :error counts." [] - `(cljs.spec.test/run-var-tests #?(:clj ~(spec/speced-vars*) + `(cljs.spec.test/run-var-tests #?(:clj ~(s/speced-vars*) :cljs ~(cljs.spec$macros/speced-vars*)))) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index a164c3b67..240d73520 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -7,18 +7,114 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.spec.test - (:require-macros [cljs.spec.test :as st]) + (:require-macros [cljs.spec.test :refer [with-instrument-disabled]]) (:require + [goog.userAgent.product :as product] + [clojure.string :as str] + [cljs.pprint :as pp] + [cljs.spec :as s] + [cljs.spec.impl.gen :as gen] [clojure.test.check] - [clojure.test.check.properties] - [cljs.spec :as spec] - [cljs.spec.impl.gen :as gen])) + [clojure.test.check.properties])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private ^:dynamic *instrument-enabled* + "if false, instrumented fns call straight through" + true) + +(defn get-ua-product [] + (if (not= "browser" *target*) + (keyword *target*) + (cond + product/SAFARI :safari + product/CHROME :chrome + product/FIREFOX :firefox + product/IE :ie))) + +(defn- fn-spec? + "Fn-spec must include at least :args or :ret specs." + [m] + (or (:args m) (:ret m))) ;; wrap spec/explain-data until specs always return nil for ok data (defn- explain-data* [spec v] - (when-not (spec/valid? spec v nil) - (spec/explain-data spec v))) + (when-not (s/valid? spec v nil) + (s/explain-data spec v))) + +(defn- interpret-stack-trace-element + "Given the vector-of-syms form of a stacktrace element produced +by e.g. Throwable->map, returns a map form that adds some keys +guessing the original Clojure names. Returns a map with + + :class class name symbol from stack trace + :method method symbol from stack trace + :file filename from stack trace + :line line number from stack trace + :var-scope optional Clojure var symbol scoping fn def + :local-fn optional local Clojure symbol scoping fn def + +For non-Clojure fns, :scope and :local-fn will be absent." + [[cls method file line]] + (let [clojure? (contains? '#{invoke invokeStatic} method) + demunge #(demunge %) + degensym #(str/replace % #"--.*" "") + [ns-sym name-sym local] (when clojure? + (->> (str/split (str cls) #"\$" 3) + (map demunge)))] + (merge {:file file + :line line + :method method + :class cls} + (when (and ns-sym name-sym) + {:var-scope (symbol ns-sym name-sym)}) + (when local + {:local-fn (symbol (degensym local))})))) + +(defn- StacktTraceElement->vec [o] + []) + +(defn- stacktrace-relevant-to-instrument + "Takes a coll of stack trace elements (as returned by +StackTraceElement->vec) and returns a coll of maps as per +interpret-stack-trace-element that are relevant to a +failure in instrument." + [elems] + (let [plumbing? (fn [{:keys [var-scope]}] + (contains? '#{clojure.spec.test/spec-checking-fn} var-scope))] + (sequence (comp (map StackTraceElement->vec) + (map interpret-stack-trace-element) + (filter :var-scope) + (drop-while plumbing?)) + elems))) + +(defn- spec-checking-fn + [v f fn-spec] + (let [fn-spec (@#'s/maybe-spec fn-spec) + conform! (fn [v role spec data args] + (let [conformed (s/conform spec data)] + (if (= ::s/invalid conformed) + (let [caller (->> (.-stack (js/Error.)) + stacktrace-relevant-to-instrument + first) + ed (merge (assoc (s/explain-data* spec [role] [] [] data) + ::s/args args + ::s/failure :instrument) + (when caller + {::caller (dissoc caller :class :method)}))] + (throw (ex-info + (str "Call to " v " did not conform to spec:\n" (with-out-str (s/explain-out ed))) + ed))) + conformed)))] + (fn + [& args] + (if *instrument-enabled* + (with-instrument-disabled + (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) + (binding [*instrument-enabled* true] + (apply f args))) + (apply f args))))) ;; wrap and unwrap spec failure data in an exception so that ;; quick-check will treat it as a failure. @@ -39,15 +135,15 @@ "Returns true if call passes specs, otherwise *returns* an exception with explain-data plus a :failed-on key under ::check-call." [f specs args] - (let [cargs (when (:args specs) (spec/conform (:args specs) args))] - (if (= cargs ::spec/invalid) + (let [cargs (when (:args specs) (s/conform (:args specs) args))] + (if (= cargs ::s/invalid) (wrap-failing (explain-data* (:args specs) args) :args) (let [ret (apply f args) - cret (when (:ret specs) (spec/conform (:ret specs) ret))] - (if (= cret ::spec/invalid) + cret (when (:ret specs) (s/conform (:ret specs) ret))] + (if (= cret ::s/invalid) (wrap-failing (explain-data* (:ret specs) ret) :ret) (if (and (:args specs) (:ret specs) (:fn specs)) - (if (spec/valid? (:fn specs) {:args cargs :ret cret}) + (if (s/valid? (:fn specs) {:args cargs :ret cret}) true (wrap-failing (explain-data* (:fn specs) {:args cargs :ret cret}) :fn)) true)))))) @@ -58,7 +154,7 @@ Same options and return as check-var" [f specs & {:keys [num-tests seed max-size reporter-fn] :or {num-tests 100 max-size 200 reporter-fn (constantly nil)}}] - (let [g (spec/gen (:args specs)) + (let [g (s/gen (:args specs)) prop (gen/for-all* [g] #(check-call f specs %))] (let [ret (gen/quick-check num-tests prop :seed seed :max-size max-size :reporter-fn reporter-fn)] (if-let [[smallest] (-> ret :shrunk :smallest)] @@ -77,7 +173,7 @@ passed through to test.check/quick-check: Returns a map as quick-check, with :explain-data added if :result is false." [v & opts] - (let [fnspec (spec/get-spec v)] + (let [fnspec (s/get-spec v)] (if (:args fnspec) (apply check-fn @v fnspec opts) (throw (js/Error. (str "No :args spec for " v)))))) @@ -94,7 +190,7 @@ Returns a map as quick-check, with :explain-data added if (cond-> totals true (update :test inc) (true? (:result ret)) (update :pass inc) - (::spec/problems (:result ret)) (update :fail inc) + (::s/problems (:result ret)) (update :fail inc) (instance? js/Error (:result ret)) (update :error inc)))) {:test 0, :pass 0, :fail 0, :error 0} vs))) From 59f6bc243b1ca033d7995751a2366a32852bf75c Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 13 Jul 2016 17:39:20 -0400 Subject: [PATCH 1904/4033] elide REPL frames from Node.js stacktraces --- src/main/cljs/cljs/stacktrace.cljc | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 80905422b..f61fc7a54 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -504,20 +504,22 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" [repl-env st err {:keys [output-dir] :as opts}] (letfn [(process-frame [frame-str] (when-not (or (string/blank? frame-str) - (== -1 (.indexOf frame-str " at"))) - (let [frame-str (string/replace frame-str #"\s+at\s+" "") - [function file-and-line] (string/split frame-str #"\s+") - [file-part line-part] (string/split file-and-line #":")] - {:file (string/replace (.substring file-part 1) - (str output-dir - #?(:clj File/separator :cljs "/")) - "") - :function function - :line (when (and line-part (not (string/blank? line-part))) - (parse-int - (.substring line-part 0 - (dec (count line-part))))) - :column 0})))] + (== -1 (.indexOf frame-str " at")) + ) + (let [frame-str (string/replace frame-str #"\s+at\s+" "")] + (when-not (string/starts-with? frame-str "repl:") + (let [[function file-and-line] (string/split frame-str #"\s+") + [file-part line-part] (string/split file-and-line #":")] + {:file (string/replace (.substring file-part 1) + (str output-dir + #?(:clj File/separator :cljs "/")) + "") + :function function + :line (when (and line-part (not (string/blank? line-part))) + (parse-int + (.substring line-part 0 + (dec (count line-part))))) + :column 0})))))] (->> (string/split st #"\n") (map process-frame) (remove nil?) From 5153ed9e0aeb245b6c07316e612141d393fb3813 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 Jul 2016 15:11:27 -0400 Subject: [PATCH 1905/4033] formatting --- src/main/cljs/cljs/stacktrace.cljc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index f61fc7a54..6179b5b3a 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -504,8 +504,7 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" [repl-env st err {:keys [output-dir] :as opts}] (letfn [(process-frame [frame-str] (when-not (or (string/blank? frame-str) - (== -1 (.indexOf frame-str " at")) - ) + (== -1 (.indexOf frame-str " at"))) (let [frame-str (string/replace frame-str #"\s+at\s+" "")] (when-not (string/starts-with? frame-str "repl:") (let [[function file-and-line] (string/split frame-str #"\s+") From ea5b47cb35b9afb24de50cb0768c0cb14066d810 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 Jul 2016 16:11:29 -0400 Subject: [PATCH 1906/4033] use existing stacktrace infrastructure over new stuff --- src/main/cljs/cljs/spec/test.cljs | 64 ++++++++----------------------- 1 file changed, 15 insertions(+), 49 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 240d73520..0f044364d 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -11,6 +11,7 @@ (:require [goog.userAgent.product :as product] [clojure.string :as str] + [cljs.stacktrace :as st] [cljs.pprint :as pp] [cljs.spec :as s] [cljs.spec.impl.gen :as gen] @@ -23,6 +24,12 @@ "if false, instrumented fns call straight through" true) +(defn get-host-port [] + (if (not= "browser" *target*) + {} + {:host (.. js/window -location -host) + :port (.. js/window -location -port)})) + (defn get-ua-product [] (if (not= "browser" *target*) (keyword *target*) @@ -32,6 +39,9 @@ product/FIREFOX :firefox product/IE :ie))) +(defn get-env [] + {:ua-product (get-ua-product)}) + (defn- fn-spec? "Fn-spec must include at least :args or :ret specs." [m] @@ -43,66 +53,22 @@ (when-not (s/valid? spec v nil) (s/explain-data spec v))) -(defn- interpret-stack-trace-element - "Given the vector-of-syms form of a stacktrace element produced -by e.g. Throwable->map, returns a map form that adds some keys -guessing the original Clojure names. Returns a map with - - :class class name symbol from stack trace - :method method symbol from stack trace - :file filename from stack trace - :line line number from stack trace - :var-scope optional Clojure var symbol scoping fn def - :local-fn optional local Clojure symbol scoping fn def - -For non-Clojure fns, :scope and :local-fn will be absent." - [[cls method file line]] - (let [clojure? (contains? '#{invoke invokeStatic} method) - demunge #(demunge %) - degensym #(str/replace % #"--.*" "") - [ns-sym name-sym local] (when clojure? - (->> (str/split (str cls) #"\$" 3) - (map demunge)))] - (merge {:file file - :line line - :method method - :class cls} - (when (and ns-sym name-sym) - {:var-scope (symbol ns-sym name-sym)}) - (when local - {:local-fn (symbol (degensym local))})))) - -(defn- StacktTraceElement->vec [o] - []) - -(defn- stacktrace-relevant-to-instrument - "Takes a coll of stack trace elements (as returned by -StackTraceElement->vec) and returns a coll of maps as per -interpret-stack-trace-element that are relevant to a -failure in instrument." - [elems] - (let [plumbing? (fn [{:keys [var-scope]}] - (contains? '#{clojure.spec.test/spec-checking-fn} var-scope))] - (sequence (comp (map StackTraceElement->vec) - (map interpret-stack-trace-element) - (filter :var-scope) - (drop-while plumbing?)) - elems))) - (defn- spec-checking-fn [v f fn-spec] (let [fn-spec (@#'s/maybe-spec fn-spec) conform! (fn [v role spec data args] (let [conformed (s/conform spec data)] (if (= ::s/invalid conformed) - (let [caller (->> (.-stack (js/Error.)) - stacktrace-relevant-to-instrument + (let [caller (-> (st/parse-stacktrace + (get-host-port) + (.-stack (js/Error.)) + (get-env) nil) first) ed (merge (assoc (s/explain-data* spec [role] [] [] data) ::s/args args ::s/failure :instrument) (when caller - {::caller (dissoc caller :class :method)}))] + {::caller caller}))] (throw (ex-info (str "Call to " v " did not conform to spec:\n" (with-out-str (s/explain-out ed))) ed))) From 32611d197b20655b82e54fb0b722447e585fba12 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 Jul 2016 16:17:22 -0400 Subject: [PATCH 1907/4033] instrument wip --- src/main/cljs/cljs/spec/test.cljs | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 0f044364d..d1d193280 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -18,6 +18,10 @@ [clojure.test.check] [clojure.test.check.properties])) +(defn ->sym + [x] + (@#'s/->sym x)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:private ^:dynamic *instrument-enabled* @@ -82,6 +86,50 @@ (apply f args))) (apply f args))))) +(defn- no-fspec + [v spec] + (ex-info (str "Fn at " v " is not spec'ed.") + {:var v :spec spec ::s/failure :no-fspec})) + +(defonce ^:private instrumented-vars (atom {})) + +(defn- instrument-choose-fn + "Helper for instrument." + [f spec sym {over :gen :keys [stub replace]}] + (if (some #{sym} stub) + (-> spec (s/gen over) gen/generate) + (get replace sym f))) + +(defn- instrument-choose-spec + "Helper for instrument" + [spec sym {overrides :spec}] + (get overrides sym spec)) + +(defn- instrument-1* + [s v opts] + (when v + (let [spec (s/get-spec v) + {:keys [raw wrapped]} (get @instrumented-vars v) + current @v + to-wrap (if (= wrapped current) raw current) + ospec (or (instrument-choose-spec spec s opts) + (throw (no-fspec v spec))) + ofn (instrument-choose-fn to-wrap ospec s opts) + checked (spec-checking-fn v ofn ospec)] + ;(alter-var-root v (constantly checked)) + (swap! instrumented-vars assoc v {:raw to-wrap :wrapped checked}) + (->sym v)))) + +(defn- unstrument-1* + [s v] + (when v + (when-let [{:keys [raw wrapped]} (get @instrumented-vars v)] + (swap! instrumented-vars dissoc v) + (let [current @v] + (when (= wrapped current) + ;(alter-var-root v (constantly raw)) + (->sym v)))))) + ;; wrap and unwrap spec failure data in an exception so that ;; quick-check will treat it as a failure. (defn- wrap-failing From d49c020135757e68c730268a57bdbc90a02d9b33 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 Jul 2016 17:25:56 -0400 Subject: [PATCH 1908/4033] add instrument-1 and unstrument-1 macros --- src/main/cljs/cljs/spec/test.cljc | 17 +++++++++++++++++ src/main/cljs/cljs/spec/test.cljs | 25 +++++++++++-------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 98e322990..e9788d52f 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -9,6 +9,7 @@ (ns cljs.spec.test (:require [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] [cljs.spec :as s] [cljs.spec.impl.gen :as gen])) @@ -18,6 +19,22 @@ `(binding [*instrument-enabled* nil] ~@body)) +(defmacro instrument-1 + [s opts] + (let [v (ana-api/resolve &env s)] + (when v + `(let [checked# (instrument-1* ~s (var ~s) ~opts)] + (when checked# (set! ~s checked#)) + '~(:name v))))) + +(defmacro unstrument-1 + [s opts] + (let [v (ana-api/resolve &env s)] + (when v + `(let [raw# (unstrument-1* ~s (var ~s))] + (when raw# (set! ~s raw#)) + '~(:name v))))) + (defmacro run-tests "Like run-all-tests, but scoped to specific namespaces, or to *ns* if no ns-sym are specified." diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index d1d193280..f112b4d98 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -107,18 +107,16 @@ (defn- instrument-1* [s v opts] - (when v - (let [spec (s/get-spec v) - {:keys [raw wrapped]} (get @instrumented-vars v) - current @v - to-wrap (if (= wrapped current) raw current) - ospec (or (instrument-choose-spec spec s opts) - (throw (no-fspec v spec))) - ofn (instrument-choose-fn to-wrap ospec s opts) - checked (spec-checking-fn v ofn ospec)] - ;(alter-var-root v (constantly checked)) - (swap! instrumented-vars assoc v {:raw to-wrap :wrapped checked}) - (->sym v)))) + (let [spec (s/get-spec v) + {:keys [raw wrapped]} (get @instrumented-vars v) + current @v + to-wrap (if (= wrapped current) raw current) + ospec (or (instrument-choose-spec spec s opts) + (throw (no-fspec v spec))) + ofn (instrument-choose-fn to-wrap ospec s opts) + checked (spec-checking-fn v ofn ospec)] + (swap! instrumented-vars assoc v {:raw to-wrap :wrapped checked}) + checked)) (defn- unstrument-1* [s v] @@ -127,8 +125,7 @@ (swap! instrumented-vars dissoc v) (let [current @v] (when (= wrapped current) - ;(alter-var-root v (constantly raw)) - (->sym v)))))) + raw))))) ;; wrap and unwrap spec failure data in an exception so that ;; quick-check will treat it as a failure. From e8011e7fe95c81432ad9a1043422a807edb9c052 Mon Sep 17 00:00:00 2001 From: Matthew Huebert Date: Thu, 21 Jul 2016 14:02:54 +0200 Subject: [PATCH 1909/4033] Escape non-Latin1 characters before base64 encoding the source-map string --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index f55275017..9bf22cfea 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -156,7 +156,7 @@ (.append sb (str "\n//# sourceURL=" file "\n//# sourceMappingURL=data:application/json;base64," - (base64/encodeString json))))) + (base64/encodeString (string/replace json #"%([0-9A-F]{2})" (.fromCharCode js/String "0x$1"))))))) (defn- current-alias-map [] From b07ba518ff8d17a65d092523364c0a9c1804af3a Mon Sep 17 00:00:00 2001 From: Rohit Aggarwal Date: Thu, 21 Jul 2016 08:49:05 +0100 Subject: [PATCH 1910/4033] Add support for regex in transit for compiler analysis cache --- src/main/clojure/cljs/analyzer.cljc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a1a746308..08750d322 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -33,6 +33,7 @@ [cljs.tools.reader.reader-types :as readers] [cljs.reader :as edn])) #?(:clj (:import [java.io File Reader PushbackReader FileOutputStream FileInputStream] + [java.util.regex Pattern] [java.net URL] [java.lang Throwable] [clojure.lang Namespace Var LazySeq ArityException] @@ -61,7 +62,10 @@ {:handlers {"cljs/js" (reify com.cognitect.transit.ReadHandler - (fromRep [_ v] (JSValue. v)))}}))) + (fromRep [_ v] (JSValue. v))) + "cljs/regex" + (reify com.cognitect.transit.ReadHandler + (fromRep [_ v] (Pattern/compile v)))}}))) #?(:clj (def transit-write-opts @@ -71,6 +75,11 @@ (reify com.cognitect.transit.WriteHandler (tag [_ _] "cljs/js") (rep [_ js] (.val ^JSValue js)) + (stringRep [_ _] nil)) + Pattern + (reify com.cognitect.transit.WriteHandler + (tag [_ _] "cljs/regex") + (rep [_ pat] (.pattern ^Pattern pat)) (stringRep [_ _] nil))}}))) #?(:clj From 0fcbef2bf6be543ce72de4797701fdd55b332922 Mon Sep 17 00:00:00 2001 From: Nicolas Berger Date: Thu, 7 Jan 2016 13:42:49 -0300 Subject: [PATCH 1911/4033] Use keyword options in js->clj 1-arg impl js->clj expects keyword options so the 1-arg implementation should use that to set the default value :keywordize-keys false. There's no change in behavior with this change, because keywordize-keys was already falsey, but this was is less confusing for users who copy from the 1-arg impl when passing {:keywordize-keys true} as a proper map --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 347ee79ba..1f47b55d4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9708,7 +9708,7 @@ reduces them without incurring seq initialization" vectors, and JavaScript objects into ClojureScript maps. With option ':keywordize-keys true' will convert object fields from strings to keywords." - ([x] (js->clj x {:keywordize-keys false})) + ([x] (js->clj x :keywordize-keys false)) ([x & opts] (let [{:keys [keywordize-keys]} opts keyfn (if keywordize-keys keyword str) From cfbefad0b9f2ae9af92ebc2ec211c8472a884ddf Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 Jul 2016 16:47:30 -0400 Subject: [PATCH 1912/4033] improve code for finding spec-fn caller --- src/main/cljs/cljs/spec/test.cljc | 2 +- src/main/cljs/cljs/spec/test.cljs | 49 +++++++++++++++++++++++++----- src/main/cljs/cljs/stacktrace.cljc | 39 +++++++++++++++--------- 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index e9788d52f..fbf8abc76 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -28,7 +28,7 @@ '~(:name v))))) (defmacro unstrument-1 - [s opts] + [s] (let [v (ana-api/resolve &env s)] (when v `(let [raw# (unstrument-1* ~s (var ~s))] diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index f112b4d98..d1902da25 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -7,10 +7,10 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.spec.test - (:require-macros [cljs.spec.test :refer [with-instrument-disabled]]) + (:require-macros [cljs.spec.test :as m :refer [with-instrument-disabled]]) (:require [goog.userAgent.product :as product] - [clojure.string :as str] + [clojure.string :as string] [cljs.stacktrace :as st] [cljs.pprint :as pp] [cljs.spec :as s] @@ -57,17 +57,28 @@ (when-not (s/valid? spec v nil) (s/explain-data spec v))) +(defn- find-caller [st] + (letfn [(search-spec-fn [frame] + (when frame + (let [s (:function frame)] + (and (string? s) (not (string/blank? s)) + (re-find #"cljs\.spec\.test\.spec_checking_fn" s)))))] + (->> st + (drop-while #(not (search-spec-fn %))) + (drop-while search-spec-fn) + first))) + (defn- spec-checking-fn [v f fn-spec] (let [fn-spec (@#'s/maybe-spec fn-spec) conform! (fn [v role spec data args] (let [conformed (s/conform spec data)] (if (= ::s/invalid conformed) - (let [caller (-> (st/parse-stacktrace - (get-host-port) - (.-stack (js/Error.)) - (get-env) nil) - first) + (let [caller (find-caller + (st/parse-stacktrace + (get-host-port) + (.-stack (js/Error.)) + (get-env) nil)) ed (merge (assoc (s/explain-data* spec [role] [] [] data) ::s/args args ::s/failure :instrument) @@ -208,7 +219,8 @@ Returns a map as quick-check, with :explain-data added if (comment - (require '[cljs.pprint :as pp] + (require + '[cljs.pprint :as pp] '[cljs.spec :as s] '[cljs.spec.impl.gen :as gen] '[cljs.test :as ctest]) @@ -232,6 +244,27 @@ Returns a map as quick-check, with :explain-data added if (cljs.spec.test/run-tests 'clojure.core) (test/run-all-tests) + ;; example evaluation + (defn ranged-rand + "Returns random int in range start <= rand < end" + [start end] + (+ start (long (rand (- end start))))) + + (s/fdef ranged-rand + :args (s/and (s/cat :start int? :end int?) + #(< (:start %) (:end %))) + :ret int? + :fn (s/and #(>= (:ret %) (-> % :args :start)) + #(< (:ret %) (-> % :args :end)))) + + (m/instrument-1 ranged-rand {}) + (ranged-rand 8 5) + (defn foo + ([a]) + ([a b] + (ranged-rand 8 5))) + (foo 1 2) + (m/unstrument-1 ranged-rand) ) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 6179b5b3a..43374cb69 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -452,7 +452,7 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" [repl-env st err {:keys [output-dir] :as opts}] (letfn [(process-frame [frame-str] (when-not (or (string/blank? frame-str) - (== -1 (.indexOf frame-str "\tat"))) + (== -1 (.indexOf frame-str "\tat"))) (let [frame-str (string/replace frame-str #"\s+at\s+" "") [function file-and-line] (string/split frame-str #"\s+") [file-part line-part] (string/split file-and-line #":")] @@ -502,23 +502,32 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" (defmethod parse-stacktrace :nodejs [repl-env st err {:keys [output-dir] :as opts}] - (letfn [(process-frame [frame-str] + (letfn [(parse-source-loc-info [x] + (when (and x (not (string/blank? x))) + (parse-int x))) + (process-frame [frame-str] (when-not (or (string/blank? frame-str) - (== -1 (.indexOf frame-str " at"))) + (nil? (re-find #"^\s+at" frame-str))) (let [frame-str (string/replace frame-str #"\s+at\s+" "")] (when-not (string/starts-with? frame-str "repl:") - (let [[function file-and-line] (string/split frame-str #"\s+") - [file-part line-part] (string/split file-and-line #":")] - {:file (string/replace (.substring file-part 1) - (str output-dir - #?(:clj File/separator :cljs "/")) - "") - :function function - :line (when (and line-part (not (string/blank? line-part))) - (parse-int - (.substring line-part 0 - (dec (count line-part))))) - :column 0})))))] + (let [parts (string/split frame-str #"\s+") + [function file&line] (if (== 2 (count parts)) + [(first parts) + (subs (second parts) 1 + (dec (count (second parts))))] + [nil (first parts)]) + [file-part line-part col-part] (string/split file&line #":")] + {:file (if function + (cond-> file-part + output-dir + (string/replace + (str output-dir + #?(:clj File/separator :cljs "/")) + "")) + file-part) + :function function + :line (parse-source-loc-info line-part) + :column (parse-source-loc-info col-part)})))))] (->> (string/split st #"\n") (map process-frame) (remove nil?) From 48b83d900dc46e912fcefc2365f806f5c8c15dfe Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 25 Jul 2016 14:26:15 -0400 Subject: [PATCH 1913/4033] require -> :require --- src/main/clojure/cljs/source_map/base64_vlq.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/source_map/base64_vlq.clj b/src/main/clojure/cljs/source_map/base64_vlq.clj index ea3625f36..eaeed9946 100644 --- a/src/main/clojure/cljs/source_map/base64_vlq.clj +++ b/src/main/clojure/cljs/source_map/base64_vlq.clj @@ -1,5 +1,5 @@ (ns cljs.source-map.base64-vlq - (require [clojure.string :as string] + (:require [clojure.string :as string] [cljs.source-map.base64 :as base64])) (def ^:const vlq-base-shift 5) From 466f3437c3998b92739abe0abbed65e7986b2ea7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 25 Jul 2016 18:19:49 -0400 Subject: [PATCH 1914/4033] copy over unstrument/instrument wip --- src/main/cljs/cljs/spec/test.cljc | 64 +++++++++++++++++++++++++++++++ src/main/cljs/cljs/spec/test.cljs | 21 ++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index fbf8abc76..dbf409bf7 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -35,6 +35,70 @@ (when raw# (set! ~s raw#)) '~(:name v))))) +(defmacro instrument + "Instruments the vars named by sym-or-syms, a symbol or collection +of symbols, or all instrumentable vars if sym-or-syms is not +specified. + +If a var has an :args fn-spec, sets the var's root binding to a +fn that checks arg conformance (throwing an exception on failure) +before delegating to the original fn. + +The opts map can be used to override registered specs, and/or to +replace fn implementations entirely. Opts for symbols not included +in sym-or-syms are ignored. This facilitates sharing a common +options map across many different calls to instrument. + +The opts map may have the following keys: + + :spec a map from var-name symbols to override specs + :stub a set of var-name symbols to be replaced by stubs + :gen a map from spec names to generator overrides + :replace a map from var-name symbols to replacement fns + +:spec overrides registered fn-specs with specs your provide. Use +:spec overrides to provide specs for libraries that do not have +them, or to constrain your own use of a fn to a subset of its +spec'ed contract. + +:stub replaces a fn with a stub that checks :args, then uses the +:ret spec to generate a return value. + +:gen overrides are used only for :stub generation. + +:replace replaces a fn with a fn that checks args conformance, then +invokes the fn you provide, enabling arbitrary stubbing and mocking. + +:spec can be used in combination with :stub or :replace. + +Returns a collection of syms naming the vars instrumented." + ([] + `(instrument (instrumentable-syms))) + ([sym-or-syms] + `(instrument ~sym-or-syms nil)) + ([sym-or-syms opts] + `(into + [] + (comp (filter (instrumentable-syms opts)) + (distinct) + (map #(instrument-1* % opts)) + (remove nil?)) + (collectionize sym-or-syms)))) + +(defmacro unstrument + "Undoes instrument on the vars named by sym-or-syms, specified +as in instrument. With no args, unstruments all instrumented vars. +Returns a collection of syms naming the vars unstrumented." + ([] + `(unstrument (map ->sym (keys @instrumented-vars)))) + ([sym-or-syms] + `(into + [] + (comp (filter symbol?) + (map unstrument-1*) + (remove nil?)) + (collectionize sym-or-syms)))) + (defmacro run-tests "Like run-all-tests, but scoped to specific namespaces, or to *ns* if no ns-sym are specified." diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index d1902da25..404de40ea 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -138,6 +138,27 @@ (when (= wrapped current) raw))))) +(defn- fn-spec-name? + [s] + (symbol? s)) + +(defn- collectionize + [x] + (if (symbol? x) + (list x) + x)) + +(defn instrumentable-syms + "Given an opts map as per instrument, returns the set of syms +that can be instrumented." + ([] (instrumentable-syms nil)) + ([opts] + (assert (every? ident? (keys (:gen opts))) "instrument :gen expects ident keys") + (reduce into #{} [(filter fn-spec-name? (keys (s/registry))) + (keys (:spec opts)) + (:stub opts) + (keys (:replace opts))]))) + ;; wrap and unwrap spec failure data in an exception so that ;; quick-check will treat it as a failure. (defn- wrap-failing From 631ab5bb92f8c25b4fa9d89ebd17fbbf2668ca38 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 26 Jul 2016 09:11:40 -0400 Subject: [PATCH 1915/4033] add :macro true to namespace :macros var entries in compiler environment --- src/main/clojure/cljs/analyzer.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 08750d322..9e770a262 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -538,7 +538,8 @@ (let [ns (.getName ^Namespace (:ns vm))] (assoc vm :ns ns - :name (symbol (str ns) (str k)))))])) + :name (symbol (str ns) (str k)) + :macro true)))])) (into {})))))) #?(:clj From 5b8112aa3c15fd86b804556402a304d823d26472 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 26 Jul 2016 09:15:35 -0400 Subject: [PATCH 1916/4033] add enumerate-namespace, cljs.spec wip --- src/main/cljs/cljs/spec/test.cljc | 46 +++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index dbf409bf7..624b4fa5b 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -13,6 +13,27 @@ [cljs.spec :as s] [cljs.spec.impl.gen :as gen])) +(defn- collectionize + [x] + (if (symbol? x) + (list x) + x)) + +(defmacro enumerate-namespace + "Given a symbol naming an ns, or a collection of such symbols, +returns the set of all symbols naming vars in those nses." + [[quote ns-sym-or-syms]] + (let [xs (into #{} + (mapcat (fn [ns-sym] + (->> (vals (ana-api/ns-interns ns-sym)) + (filter #(not (:macro %))) + (map :name) + (map + (fn [name-sym] + (symbol (name ns-sym) (name name-sym))))))) + (collectionize ns-sym-or-syms))] + `(quote ~xs))) + (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." [& body] @@ -77,13 +98,14 @@ Returns a collection of syms naming the vars instrumented." ([sym-or-syms] `(instrument ~sym-or-syms nil)) ([sym-or-syms opts] - `(into - [] - (comp (filter (instrumentable-syms opts)) - (distinct) - (map #(instrument-1* % opts)) - (remove nil?)) - (collectionize sym-or-syms)))) + `(let [opts# ~opts] + (into + [] + (comp (filter (instrumentable-syms opts#)) + (distinct) + (map #(instrument-1* % opts#)) + (remove nil?)) + (collectionize ~sym-or-syms))))) (defmacro unstrument "Undoes instrument on the vars named by sym-or-syms, specified @@ -93,11 +115,11 @@ Returns a collection of syms naming the vars unstrumented." `(unstrument (map ->sym (keys @instrumented-vars)))) ([sym-or-syms] `(into - [] - (comp (filter symbol?) - (map unstrument-1*) - (remove nil?)) - (collectionize sym-or-syms)))) + [] + (comp (filter symbol?) + (map unstrument-1*) + (remove nil?)) + (collectionize ~sym-or-syms)))) (defmacro run-tests "Like run-all-tests, but scoped to specific namespaces, or to From c2831f2829696e9e502f439e4e19366fc5964306 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 14:45:55 -0400 Subject: [PATCH 1917/4033] move distinct-by to cljs.util --- src/main/clojure/cljs/closure.clj | 15 +-------------- src/main/clojure/cljs/util.cljc | 13 +++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0adfa83fb..03604f9da 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -34,7 +34,7 @@ JavaScript or a deps file for use during development. " (:refer-clojure :exclude [compile]) - (:require [cljs.util :as util] + (:require [cljs.util :as util :refer [distinct-by]] [cljs.core :as cljsm] [cljs.compiler :as comp] [cljs.analyzer :as ana] @@ -896,19 +896,6 @@ [(javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)])) inputs))) -(defn distinct-by - ([k coll] - (let [step (fn step [xs seen] - (lazy-seq - ((fn [[f :as xs] seen] - (when-let [s (seq xs)] - (let [v (get f k)] - (if (contains? seen v) - (recur (rest s) seen) - (cons f (step (rest s) (conj seen v))))))) - xs seen)))] - (step coll #{})))) - (defn add-preloads "Add :preloads to a given set of inputs (IJavaScript). Returns a new list of inputs where the preloaded namespaces and their deps come immediately after diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index d0d5359d2..dffe609f2 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -248,3 +248,16 @@ [unknown (some-> (suggestion 3 (str unknown) (map str knowns)) (subs 1) keyword)])) + +(defn distinct-by + ([f coll] + (let [step (fn step [xs seen] + (lazy-seq + ((fn [[x :as xs] seen] + (when-let [s (seq xs)] + (let [v (f x)] + (if (contains? seen v) + (recur (rest s) seen) + (cons x (step (rest s) (conj seen v))))))) + xs seen)))] + (step coll #{})))) From 212fb9d8574b8cea26f6b94fce60f6bf7204a90c Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 14:55:42 -0400 Subject: [PATCH 1918/4033] instrument wip --- src/main/cljs/cljs/spec/test.cljc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 624b4fa5b..7d07ac36e 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -8,6 +8,7 @@ (ns cljs.spec.test (:require + [cljs.util :refer [distinct-by]] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.spec :as s] @@ -99,13 +100,19 @@ Returns a collection of syms naming the vars instrumented." `(instrument ~sym-or-syms nil)) ([sym-or-syms opts] `(let [opts# ~opts] - (into + (reduce + (fn [ret [_ f]] + (let [sym (f)] + (cond-> ret sym (conj sym)))) [] - (comp (filter (instrumentable-syms opts#)) - (distinct) - (map #(instrument-1* % opts#)) - (remove nil?)) - (collectionize ~sym-or-syms))))) + (->> (zipmap + (collectionize ~sym-or-syms) + ~@(map + (fn [sym] + `(fn [] (instrument-1 ~sym opts#))) + (collectionize ~sym-or-syms))) + (filter #((instrumentable-syms opts#) (first %))) + (distinct-by first)))))) (defmacro unstrument "Undoes instrument on the vars named by sym-or-syms, specified From 052d45ed50dbd2fc85fbd9d77b69529676312da9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 15:19:07 -0400 Subject: [PATCH 1919/4033] tweaks verify that instrumentable syms works --- src/main/cljs/cljs/spec/test.cljc | 8 ++++---- src/main/cljs/cljs/spec/test.cljs | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 7d07ac36e..363c0c2d3 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -96,9 +96,9 @@ invokes the fn you provide, enabling arbitrary stubbing and mocking. Returns a collection of syms naming the vars instrumented." ([] `(instrument (instrumentable-syms))) - ([sym-or-syms] - `(instrument ~sym-or-syms nil)) - ([sym-or-syms opts] + ([xs] + `(instrument ~xs nil)) + ([[quote sym-or-syms] opts] `(let [opts# ~opts] (reduce (fn [ret [_ f]] @@ -109,7 +109,7 @@ Returns a collection of syms naming the vars instrumented." (collectionize ~sym-or-syms) ~@(map (fn [sym] - `(fn [] (instrument-1 ~sym opts#))) + `(fn [] (instrument-1 '~sym opts#))) (collectionize ~sym-or-syms))) (filter #((instrumentable-syms opts#) (first %))) (distinct-by first)))))) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 404de40ea..91574028c 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -246,7 +246,7 @@ Returns a map as quick-check, with :explain-data added if '[cljs.spec.impl.gen :as gen] '[cljs.test :as ctest]) - (require :reload '[cjls.spec.test :as test]) + (require :reload '[cljs.spec.test :as test]) ;; discover speced vars for your own test runner (s/speced-vars) @@ -278,6 +278,8 @@ Returns a map as quick-check, with :explain-data added if :fn (s/and #(>= (:ret %) (-> % :args :start)) #(< (:ret %) (-> % :args :end)))) + (instrumentable-syms) + (m/instrument-1 ranged-rand {}) (ranged-rand 8 5) (defn foo From 7a539cca3237c676a7cf194f7565135fcecd20f0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 15:54:34 -0400 Subject: [PATCH 1920/4033] fix some obvious issues with instrument macro --- src/main/cljs/cljs/spec/test.cljc | 31 ++++++++++++++++--------------- src/main/cljs/cljs/spec/test.cljs | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 363c0c2d3..0439ce30a 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -8,7 +8,6 @@ (ns cljs.spec.test (:require - [cljs.util :refer [distinct-by]] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.spec :as s] @@ -99,20 +98,22 @@ Returns a collection of syms naming the vars instrumented." ([xs] `(instrument ~xs nil)) ([[quote sym-or-syms] opts] - `(let [opts# ~opts] - (reduce - (fn [ret [_ f]] - (let [sym (f)] - (cond-> ret sym (conj sym)))) - [] - (->> (zipmap - (collectionize ~sym-or-syms) - ~@(map - (fn [sym] - `(fn [] (instrument-1 '~sym opts#))) - (collectionize ~sym-or-syms))) - (filter #((instrumentable-syms opts#) (first %))) - (distinct-by first)))))) + (let [opts-sym (gensym "opts")] + `(let [~opts-sym ~opts] + (reduce + (fn [ret# [_# f#]] + (let [sym# (f#)] + (cond-> ret# sym# (conj sym#)))) + [] + (->> (zipmap + (collectionize ~sym-or-syms) + ~(into [] + (map + (fn [sym] + `(fn [] (instrument-1 '~sym ~opts-sym)))) + (collectionize sym-or-syms))) + (filter #((instrumentable-syms ~opts-sym) (first %))) + (distinct-by first))))))) (defmacro unstrument "Undoes instrument on the vars named by sym-or-syms, specified diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 91574028c..0ea1e4d12 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -18,6 +18,19 @@ [clojure.test.check] [clojure.test.check.properties])) +(defn distinct-by + ([f coll] + (let [step (fn step [xs seen] + (lazy-seq + ((fn [[x :as xs] seen] + (when-let [s (seq xs)] + (let [v (f x)] + (if (contains? seen v) + (recur (rest s) seen) + (cons x (step (rest s) (conj seen v))))))) + xs seen)))] + (step coll #{})))) + (defn ->sym [x] (@#'s/->sym x)) @@ -281,6 +294,10 @@ Returns a map as quick-check, with :explain-data added if (instrumentable-syms) (m/instrument-1 ranged-rand {}) + + (m/instrument `ranged-rand) + (m/instrument `[ranged-rand]) + (ranged-rand 8 5) (defn foo ([a]) From f9ffbeede7ab5919f5df66591ca6d4cfc765e696 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 16:20:57 -0400 Subject: [PATCH 1921/4033] instrument-1 & unstrument-1 take quoted symbols --- src/main/cljs/cljs/spec/test.cljc | 4 ++-- src/main/cljs/cljs/spec/test.cljs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 0439ce30a..8b1c049f3 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -41,7 +41,7 @@ returns the set of all symbols naming vars in those nses." ~@body)) (defmacro instrument-1 - [s opts] + [[quote s] opts] (let [v (ana-api/resolve &env s)] (when v `(let [checked# (instrument-1* ~s (var ~s) ~opts)] @@ -49,7 +49,7 @@ returns the set of all symbols naming vars in those nses." '~(:name v))))) (defmacro unstrument-1 - [s] + [[quote s]] (let [v (ana-api/resolve &env s)] (when v `(let [raw# (unstrument-1* ~s (var ~s))] diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 0ea1e4d12..78cd51d35 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -293,7 +293,7 @@ Returns a map as quick-check, with :explain-data added if (instrumentable-syms) - (m/instrument-1 ranged-rand {}) + (m/instrument-1 `ranged-rand {}) (m/instrument `ranged-rand) (m/instrument `[ranged-rand]) From aa9fd3d409c4621d40a8fb3af4a5d7b380ff5ee4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 16:33:14 -0400 Subject: [PATCH 1922/4033] basic working instrument --- src/main/cljs/cljs/spec/test.cljc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 8b1c049f3..0a2bcf28c 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -106,12 +106,11 @@ Returns a collection of syms naming the vars instrumented." (cond-> ret# sym# (conj sym#)))) [] (->> (zipmap - (collectionize ~sym-or-syms) - ~(into [] - (map + (collectionize '~sym-or-syms) + [~@(map (fn [sym] - `(fn [] (instrument-1 '~sym ~opts-sym)))) - (collectionize sym-or-syms))) + `(fn [] (instrument-1 '~sym ~opts-sym))) + (collectionize sym-or-syms))]) (filter #((instrumentable-syms ~opts-sym) (first %))) (distinct-by first))))))) From 14e878e4692141b3aca32d5c146b8340927320a4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 18:14:04 -0400 Subject: [PATCH 1923/4033] fix instrument behavior if no syms provided, unstrument wip --- src/main/cljs/cljs/spec.cljc | 22 +---------- src/main/cljs/cljs/spec/test.cljc | 63 ++++++++++++++++++------------- src/main/cljs/cljs/spec/test.cljs | 5 +++ 3 files changed, 43 insertions(+), 47 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index b67b6a3b2..169408f6c 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -356,26 +356,8 @@ (def ^:private _speced_vars (atom #{})) -(defn speced-vars* - ([] - (speced-vars* nil)) - ([ns-syms] - (let [ns-match? (if (seq ns-syms) - (set (map second ns-syms)) - (constantly true))] - (reduce - (fn [ret sym] - (if (ns-match? (symbol (namespace sym))) - (conj ret (list 'var sym)) - ret)) - #{} @_speced_vars)))) - -(defmacro speced-vars - "Returns the set of vars whose namespace is in ns-syms AND -whose vars have been speced with fdef. If no ns-syms are -specified, return speced vars from all namespaces." - [& ns-syms] - (speced-vars* ns-syms)) +(defn speced-vars [] + @_speced_vars) (defmacro fdef "Takes a symbol naming a function, and one or more of the following: diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 0a2bcf28c..82b93a3ca 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -13,6 +13,8 @@ [cljs.spec :as s] [cljs.spec.impl.gen :as gen])) +(defonce ^:private instrumented-vars (atom #{})) + (defn- collectionize [x] (if (symbol? x) @@ -44,6 +46,7 @@ returns the set of all symbols naming vars in those nses." [[quote s] opts] (let [v (ana-api/resolve &env s)] (when v + (swap! instrumented-vars conj v) `(let [checked# (instrument-1* ~s (var ~s) ~opts)] (when checked# (set! ~s checked#)) '~(:name v))))) @@ -52,6 +55,7 @@ returns the set of all symbols naming vars in those nses." [[quote s]] (let [v (ana-api/resolve &env s)] (when v + (swap! instrumented-vars disj v) `(let [raw# (unstrument-1* ~s (var ~s))] (when raw# (set! ~s raw#)) '~(:name v))))) @@ -94,7 +98,7 @@ invokes the fn you provide, enabling arbitrary stubbing and mocking. Returns a collection of syms naming the vars instrumented." ([] - `(instrument (instrumentable-syms))) + `(instrument '[~@(s/speced-vars)])) ([xs] `(instrument ~xs nil)) ([[quote sym-or-syms] opts] @@ -119,30 +123,35 @@ Returns a collection of syms naming the vars instrumented." as in instrument. With no args, unstruments all instrumented vars. Returns a collection of syms naming the vars unstrumented." ([] - `(unstrument (map ->sym (keys @instrumented-vars)))) - ([sym-or-syms] - `(into + `(unstrument '[~@(deref instrumented-vars)])) + ([[quote sym-or-syms]] + `(reduce + (fn [ret# f#] + (let [sym# (f#)] + (cond-> ret# sym# (conj sym#)))) [] - (comp (filter symbol?) - (map unstrument-1*) - (remove nil?)) - (collectionize ~sym-or-syms)))) - -(defmacro run-tests - "Like run-all-tests, but scoped to specific namespaces, or to -*ns* if no ns-sym are specified." - ([] - `(cljs.spec.test/run-tests '~ana/*cljs-ns*)) - ([& ns-syms] - `(cljs.spec.test/run-var-tests - (->> #?(:clj ~(s/speced-vars* ns-syms) - :cljs ~(cljs.spec$macros/speced-vars* ns-syms)) - (filter (fn [v#] (:args (cljs.spec/get-spec v#)))))))) - -(defmacro run-all-tests - "Like clojure.test/run-all-tests, but runs test.check tests -for all speced vars. Prints per-test results to *out*, and -returns a map with :test,:pass,:fail, and :error counts." - [] - `(cljs.spec.test/run-var-tests #?(:clj ~(s/speced-vars*) - :cljs ~(cljs.spec$macros/speced-vars*)))) + [~@(->> (collectionize sym-or-syms) + (map + (fn [sym] + (when (symbol? symbol) + `(fn [] (unstrument-1 ~'sym))))) + (remove nil?))]))) + +;(defmacro run-tests +; "Like run-all-tests, but scoped to specific namespaces, or to +;*ns* if no ns-sym are specified." +; ([] +; `(cljs.spec.test/run-tests '~ana/*cljs-ns*)) +; ([& ns-syms] +; `(cljs.spec.test/run-var-tests +; (->> #?(:clj ~(s/speced-vars* ns-syms) +; :cljs ~(cljs.spec$macros/speced-vars* ns-syms)) +; (filter (fn [v#] (:args (cljs.spec/get-spec v#)))))))) +; +;(defmacro run-all-tests +; "Like clojure.test/run-all-tests, but runs test.check tests +;for all speced vars. Prints per-test results to *out*, and +;returns a map with :test,:pass,:fail, and :error counts." +; [] +; `(cljs.spec.test/run-var-tests #?(:clj ~(s/speced-vars) +; :cljs ~(cljs.spec$macros/speced-vars*)))) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 78cd51d35..55d1237ba 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -295,9 +295,14 @@ Returns a map as quick-check, with :explain-data added if (m/instrument-1 `ranged-rand {}) + (m/instrument) (m/instrument `ranged-rand) (m/instrument `[ranged-rand]) + (m/unstrument) + (m/unstrument `ranged-rand) + (m/unstrument `[ranged-rand]) + (ranged-rand 8 5) (defn foo ([a]) From 63e0b164298380eb64e98bc5f5c817fddac53fdb Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 18:28:02 -0400 Subject: [PATCH 1924/4033] fix some obvious bugs wip --- src/main/cljs/cljs/spec/test.cljc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 82b93a3ca..517c16c97 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -46,7 +46,7 @@ returns the set of all symbols naming vars in those nses." [[quote s] opts] (let [v (ana-api/resolve &env s)] (when v - (swap! instrumented-vars conj v) + (swap! instrumented-vars conj (:name v)) `(let [checked# (instrument-1* ~s (var ~s) ~opts)] (when checked# (set! ~s checked#)) '~(:name v))))) @@ -55,7 +55,7 @@ returns the set of all symbols naming vars in those nses." [[quote s]] (let [v (ana-api/resolve &env s)] (when v - (swap! instrumented-vars disj v) + (swap! instrumented-vars disj (:name v)) `(let [raw# (unstrument-1* ~s (var ~s))] (when raw# (set! ~s raw#)) '~(:name v))))) @@ -133,8 +133,8 @@ Returns a collection of syms naming the vars unstrumented." [~@(->> (collectionize sym-or-syms) (map (fn [sym] - (when (symbol? symbol) - `(fn [] (unstrument-1 ~'sym))))) + (when (symbol? sym) + `(fn [] (unstrument-1 '~sym))))) (remove nil?))]))) ;(defmacro run-tests From 8643c554e7164df4d74d3f71d576df9848b2e363 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 Jul 2016 18:45:55 -0400 Subject: [PATCH 1925/4033] tweak eval comments --- src/main/cljs/cljs/spec/test.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 55d1237ba..7ea993a04 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -294,6 +294,7 @@ Returns a map as quick-check, with :explain-data added if (instrumentable-syms) (m/instrument-1 `ranged-rand {}) + (m/unstrument-1 `ranged-rand) (m/instrument) (m/instrument `ranged-rand) From 8524d7b1ac7c1c418ec394e89322e341070414ca Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 28 Jul 2016 13:25:56 -0400 Subject: [PATCH 1926/4033] remove double analysis of forms at the REPL, problematic for macros with side-effects --- src/main/clojure/cljs/repl.cljc | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index bd2eaf9f4..dd148b07a 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -448,11 +448,9 @@ ([repl-env env filename form wrap opts] (binding [ana/*cljs-file* filename] (let [env (assoc env - :root-source-info - {:source-type :fragment - :source-form form }) - ast (ana/analyze env form nil opts) - js (comp/emit-str ast) + :root-source-info + {:source-type :fragment + :source-form form}) def-emits-var (:def-emits-var opts) wrap-js ;; TODO: check opts as well - David @@ -485,13 +483,16 @@ (ana/no-warn (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) (wrap form) nil opts))))] - (when (= (:op ast) :ns) - (load-dependencies repl-env - (into (vals (:requires ast)) - (distinct (vals (:uses ast)))) - opts)) + ;; NOTE: means macros which expand to ns aren't supported for now + ;; when eval'ing individual forms at the REPL - David + (when (= 'ns (first form)) + (let [ast (ana/analyze env form nil opts)] + (load-dependencies repl-env + (into (vals (:requires ast)) + (distinct (vals (:uses ast)))) + opts))) (when *cljs-verbose* - (err-out (println js))) + (err-out (println wrap-js))) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] (case (:status ret) :error (throw @@ -506,7 +507,7 @@ :error ret :repl-env repl-env :form form - :js js})) + :js wrap-js})) :success (:value ret))))))) (defn load-stream [repl-env filename res] From f2424b6b04d63694dcc050989b592566fee03992 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 28 Jul 2016 13:28:24 -0400 Subject: [PATCH 1927/4033] fix instrument and unstrument behavior when handed syntax-quoted forms --- src/main/cljs/cljs/spec/test.cljc | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 517c16c97..392e1ed6b 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -101,8 +101,9 @@ Returns a collection of syms naming the vars instrumented." `(instrument '[~@(s/speced-vars)])) ([xs] `(instrument ~xs nil)) - ([[quote sym-or-syms] opts] - (let [opts-sym (gensym "opts")] + ([sym-or-syms opts] + (let [sym-or-syms (eval sym-or-syms) + opts-sym (gensym "opts")] `(let [~opts-sym ~opts] (reduce (fn [ret# [_# f#]] @@ -124,18 +125,20 @@ as in instrument. With no args, unstruments all instrumented vars. Returns a collection of syms naming the vars unstrumented." ([] `(unstrument '[~@(deref instrumented-vars)])) - ([[quote sym-or-syms]] - `(reduce - (fn [ret# f#] - (let [sym# (f#)] - (cond-> ret# sym# (conj sym#)))) - [] - [~@(->> (collectionize sym-or-syms) - (map - (fn [sym] - (when (symbol? sym) - `(fn [] (unstrument-1 '~sym))))) - (remove nil?))]))) + ([sym-or-syms] + (let [sym-or-syms (eval sym-or-syms)] + `(reduce + (fn [ret# f#] + (let [sym# (f#)] + (cond-> ret# sym# (conj sym#)))) + [] + [~@(->> (collectionize sym-or-syms) + (map + (fn [sym] + (when (symbol? sym) + `(fn [] + (unstrument-1 '~sym))))) + (remove nil?))])))) ;(defmacro run-tests ; "Like run-all-tests, but scoped to specific namespaces, or to From b47670b2dc28c4920d83cbb4edfd64e667fe5ec2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 28 Jul 2016 15:16:12 -0400 Subject: [PATCH 1928/4033] remove old stuff --- src/main/cljs/cljs/spec/test.cljc | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 392e1ed6b..1804f98ea 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -139,22 +139,3 @@ Returns a collection of syms naming the vars unstrumented." `(fn [] (unstrument-1 '~sym))))) (remove nil?))])))) - -;(defmacro run-tests -; "Like run-all-tests, but scoped to specific namespaces, or to -;*ns* if no ns-sym are specified." -; ([] -; `(cljs.spec.test/run-tests '~ana/*cljs-ns*)) -; ([& ns-syms] -; `(cljs.spec.test/run-var-tests -; (->> #?(:clj ~(s/speced-vars* ns-syms) -; :cljs ~(cljs.spec$macros/speced-vars* ns-syms)) -; (filter (fn [v#] (:args (cljs.spec/get-spec v#)))))))) -; -;(defmacro run-all-tests -; "Like clojure.test/run-all-tests, but runs test.check tests -;for all speced vars. Prints per-test results to *out*, and -;returns a map with :test,:pass,:fail, and :error counts." -; [] -; `(cljs.spec.test/run-var-tests #?(:clj ~(s/speced-vars) -; :cljs ~(cljs.spec$macros/speced-vars*)))) From 0d4439e637e53a75fa8724a0cc1721bd6295f45f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 28 Jul 2016 15:19:51 -0400 Subject: [PATCH 1929/4033] naive copy of latest clojure.spec.test, wip --- src/main/cljs/cljs/spec/test.cljs | 237 ++++++++++++++++++++++-------- 1 file changed, 174 insertions(+), 63 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 7ea993a04..c76c81135 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -15,7 +15,7 @@ [cljs.pprint :as pp] [cljs.spec :as s] [cljs.spec.impl.gen :as gen] - [clojure.test.check] + [clojure.test.check :as stc] [clojure.test.check.properties])) (defn distinct-by @@ -172,85 +172,196 @@ that can be instrumented." (:stub opts) (keys (:replace opts))]))) -;; wrap and unwrap spec failure data in an exception so that -;; quick-check will treat it as a failure. -(defn- wrap-failing - [explain-data step] - (ex-info "Wrapper" {::check-call (assoc explain-data :failed-on step)})) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- unwrap-failing - [ret] - (let [ret (if-let [explain (-> ret :result ex-data ::check-call)] - (assoc ret :result explain) - ret)] - (if-let [shrunk-explain (-> ret :shrunk :result ex-data ::check-call)] - (assoc-in ret [:shrunk :result] shrunk-explain) - ret))) +(defn- explain-check + [args spec v role] + (ex-info + "Specification-based check failed" + (when-not (s/valid? spec v nil) + (assoc (s/explain-data* spec [role] [] [] v) + ::args args + ::val v + ::s/failure :check-failed)))) (defn- check-call "Returns true if call passes specs, otherwise *returns* an exception -with explain-data plus a :failed-on key under ::check-call." +with explain-data + ::s/failure." [f specs args] (let [cargs (when (:args specs) (s/conform (:args specs) args))] (if (= cargs ::s/invalid) - (wrap-failing (explain-data* (:args specs) args) :args) + (explain-check args (:args specs) args :args) (let [ret (apply f args) cret (when (:ret specs) (s/conform (:ret specs) ret))] (if (= cret ::s/invalid) - (wrap-failing (explain-data* (:ret specs) ret) :ret) + (explain-check args (:ret specs) ret :ret) (if (and (:args specs) (:ret specs) (:fn specs)) (if (s/valid? (:fn specs) {:args cargs :ret cret}) true - (wrap-failing (explain-data* (:fn specs) {:args cargs :ret cret}) :fn)) + (explain-check args (:fn specs) {:args cargs :ret cret} :fn)) true)))))) +(defn- quick-check + [f specs {gen :gen opts ::stc/opts}] + (let [{:keys [num-tests] :or {num-tests 1000}} opts + g (try (s/gen (:args specs) gen) (catch js/Error t t))] + (if (instance? js/Error g) + {:result g} + (let [prop (gen/for-all* [g] #(check-call f specs %))] + (apply gen/quick-check num-tests prop (mapcat identity opts)))))) + +(defn- make-check-result + "Builds spec result map." + [check-sym spec test-check-ret] + (merge {:spec spec + ::stc/ret test-check-ret} + (when check-sym + {:sym check-sym}) + (when-let [result (-> test-check-ret :result)] + (when-not (true? result) {:failure result})) + (when-let [shrunk (-> test-check-ret :shrunk)] + {:failure (:result shrunk)}))) + +(defn- check-1 + [{:keys [s f v spec]} opts] + (let [re-inst? (and v (seq (unstrument s)) true) + f (or f (when v @v))] + (try + (cond + (nil? f) + {:failure (ex-info "No fn to spec" {::s/failure :no-fn}) + :sym s :spec spec} + + (:args spec) + (let [tcret (quick-check f spec opts)] + (make-check-result s spec tcret)) + + :default + {:failure (ex-info "No :args spec" {::s/failure :no-args-spec}) + :sym s :spec spec}) + (finally + (when re-inst? (instrument s)))))) + +(defn- sym->check-map + [s] + (let [v (resolve s)] + {:s s + :v v + :spec (when v (s/get-spec v))})) + +(defn- validate-check-opts + [opts] + (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) + (defn check-fn - "Check a function using provided specs and test.check. -Same options and return as check-var" - [f specs - & {:keys [num-tests seed max-size reporter-fn] - :or {num-tests 100 max-size 200 reporter-fn (constantly nil)}}] - (let [g (s/gen (:args specs)) - prop (gen/for-all* [g] #(check-call f specs %))] - (let [ret (gen/quick-check num-tests prop :seed seed :max-size max-size :reporter-fn reporter-fn)] - (if-let [[smallest] (-> ret :shrunk :smallest)] - (unwrap-failing ret) - ret)))) - -(defn check-var - "Checks a var's specs using test.check. Optional args are -passed through to test.check/quick-check: - - num-tests number of tests to run, default 100 - seed random seed - max-size how large an input to generate, max 200 - reporter-fn reporting fn - -Returns a map as quick-check, with :explain-data added if -:result is false." - [v & opts] - (let [fnspec (s/get-spec v)] - (if (:args fnspec) - (apply check-fn @v fnspec opts) - (throw (js/Error. (str "No :args spec for " v)))))) - -(defn- run-var-tests - "Helper for run-tests, run-all-tests." - [vs] - (let [reporter-fn println] - (reduce - (fn [totals v] - (let [_ (println "Checking" v) - ret (check-var v :reporter-fn reporter-fn)] - (prn ret) - (cond-> totals - true (update :test inc) - (true? (:result ret)) (update :pass inc) - (::s/problems (:result ret)) (update :fail inc) - (instance? js/Error (:result ret)) (update :error inc)))) - {:test 0, :pass 0, :fail 0, :error 0} - vs))) + "Runs generative tests for fn f using spec and opts. See +'check' for options and return." + ([f spec] (check-fn f spec nil)) + ([f spec opts] + (validate-check-opts opts) + (check-1 {:f f :spec spec} opts))) + +(defn checkable-syms + "Given an opts map as per check, returns the set of syms that +can be checked." + ([] (checkable-syms nil)) + ([opts] + (validate-check-opts opts) + (reduce into #{} [(filter fn-spec-name? (keys (s/registry))) + (keys (:spec opts))]))) + +(defn check + "Run generative tests for spec conformance on vars named by +sym-or-syms, a symbol or collection of symbols. If sym-or-syms +is not specified, check all checkable vars. + +The opts map includes the following optional keys, where stc +aliases clojure.spec.test.check: + +::stc/opts opts to flow through test.check/quick-check +:gen map from spec names to generator overrides + +The ::stc/opts include :num-tests in addition to the keys +documented by test.check. Generator overrides are passed to +spec/gen when generating function args. + +Returns a lazy sequence of check result maps with the following +keys + +:spec the spec tested +:sym optional symbol naming the var tested +:failure optional test failure +::stc/ret optional value returned by test.check/quick-check + +The value for :failure can be any exception. Exceptions thrown by +spec itself will have an ::s/failure value in ex-data: + +:check-failed at least one checked return did not conform +:no-args-spec no :args spec provided +:no-fn no fn provided +:no-fspec no fspec provided +:no-gen unable to generate :args +:instrument invalid args detected by instrument +" + ([] (check (checkable-syms))) + ([sym-or-syms] (check sym-or-syms nil)) + ([sym-or-syms opts] + (->> (collectionize sym-or-syms) + (filter (checkable-syms opts)) + (map + #(check-1 (sym->check-map %) opts))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; check reporting ;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- failure-type + [x] + (::s/failure (ex-data x))) +(defn- unwrap-failure + [x] + (if (failure-type x) + (ex-data x) + x)) + +(defn- result-type + "Returns the type of the check result. This can be any of the +::s/failure keywords documented in 'check', or: + + :check-passed all checked fn returns conformed + :check-threw checked fn threw an exception" + [ret] + (let [failure (:failure ret)] + (cond + (nil? failure) :check-passed + (failure-type failure) (failure-type failure) + :default :check-threw))) + +(defn abbrev-result + "Given a check result, returns an abbreviated version +suitable for summary use." + [x] + (if (:failure x) + (-> (dissoc x ::stc/ret) + (update :spec s/describe) + (update :failure unwrap-failure)) + (dissoc x :spec ::stc/ret))) + +(defn summarize-results + "Given a collection of check-results, e.g. from 'check', pretty +prints the summary-result (default abbrev-result) of each. + +Returns a map with :total, the total number of results, plus a +key with a count for each different :type of result." + ([check-results] (summarize-results check-results abbrev-result)) + ([check-results summary-result] + (reduce + (fn [summary result] + (pp/pprint (summary-result result)) + (-> summary + (update :total inc) + (update (result-type result) (fnil inc 0)))) + {:total 0} + check-results))) (comment (require From 82a2a293e7b0b30f78399f997ec26d808473b8e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 28 Jul 2016 00:26:36 +0100 Subject: [PATCH 1930/4033] CLJS-1716: No longer possible to use same alias for :require-macros and :require --- src/main/clojure/cljs/analyzer.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9e770a262..fe09ab56a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2441,7 +2441,8 @@ (defn get-expander-ns [env ^String nstr] ;; first check for clojure.* -> cljs.* cases - (let [res (resolve-ns-alias env (symbol nstr)) + (let [res (or (resolve-macro-ns-alias env (symbol nstr)) + (resolve-ns-alias env (symbol nstr))) nstr (if res (str res) nstr)] (cond #?@(:clj [(= "clojure.core" nstr) (find-ns 'cljs.core)] From 82dce0b1ed322af25e1ebe2cf91e3afec9e249ec Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Fri, 29 Jul 2016 07:56:34 +0100 Subject: [PATCH 1931/4033] CLJS-1717 remove map from equiv-map --- src/main/cljs/cljs/core.cljs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1f47b55d4..9bfc14718 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5776,10 +5776,9 @@ reduces them without incurring seq initialization" (when (map? y) ; assume all maps are counted (when (== (count x) (count y)) - (every? identity - (map (fn [xkv] (= (get y (first xkv) never-equiv) - (second xkv))) - x)))))) + (every? (fn [xkv] (= (get y (first xkv) never-equiv) + (second xkv))) + x))))) (defn- scan-array [incr k array] From 689f6d3197a710daf55aa234b81e4468afd9ff9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 27 Jul 2016 14:31:14 +0100 Subject: [PATCH 1932/4033] CLJS-1700: Support clojure.* aliasing when not in vector --- src/main/clojure/cljs/analyzer.cljc | 16 ++++++++-------- src/test/clojure/cljs/analyzer_tests.clj | 7 ++++++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index fe09ab56a..7c8ca3c14 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1925,14 +1925,14 @@ possible." [args] (letfn [(process-spec [maybe-spec] - (if (sequential? maybe-spec) - (let [[lib & xs] maybe-spec] - (if (aliasable-clj-ns? lib) - (let [lib' (clj-ns->cljs-ns lib) - spec (cons lib' xs)] - [spec (list lib' :as lib)]) - [maybe-spec])) - [maybe-spec])) + (let [[lib & xs] (if (sequential? maybe-spec) + maybe-spec + [maybe-spec])] + (if (and (symbol? lib) (aliasable-clj-ns? lib)) + (let [lib' (clj-ns->cljs-ns lib) + spec (cons lib' xs)] + (into (if xs [spec] []) [(list lib' :as lib)])) + [maybe-spec]))) (process-form [[k & specs :as form]] (if (#{:use :require} k) (cons k (mapcat process-spec specs)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index de0bc34dc..9cb29a731 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -285,7 +285,12 @@ (:require (clojure.spec :as s) :reload))) '((:refer-clojure :exclude [first]) (:require-macros (bar :refer [quux]) :reload) - (:require (cljs.spec :as s) (cljs.spec :as clojure.spec) :reload))))) + (:require (cljs.spec :as s) (cljs.spec :as clojure.spec) :reload)))) + (is (= (a/rewrite-cljs-aliases + '((:require-macros (bar :refer [quux]) :reload) + (:require clojure.spec :reload))) + '((:require-macros (bar :refer [quux]) :reload) + (:require (cljs.spec :as clojure.spec) :reload))))) ;; ============================================================================= ;; Namespace metadata From 5a78c9fb881f08b1f9578f9cd9dd265bbdd5c720 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 27 Jul 2016 11:01:12 -0400 Subject: [PATCH 1933/4033] CLJS-1653: cljs.spec: keys* causes exception --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 169408f6c..44166a43f 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -411,7 +411,7 @@ {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" [& kspecs] `(let [mspec# (keys ~@kspecs)] - (with-gen (& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) + (with-gen (cljs.spec/& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) (fn [] (gen/fmap (fn [m#] (apply concat m#)) (gen mspec#)))))) (defmacro nilable From 7084cfcf62d3fbc26d6949d9178a3d27545f65a6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 29 Jul 2016 17:05:51 -0400 Subject: [PATCH 1934/4033] maintain compile time registry in addition to runtime registry --- src/main/cljs/cljs/spec.cljc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 44166a43f..11ffeed23 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -15,6 +15,8 @@ [cljs.spec.impl.gen :as gen] [clojure.string :as str])) +(defonce registry-ref (atom {})) + (defn- ->sym "Returns a symbol from a symbol or var" [x] @@ -51,8 +53,10 @@ spec-name, predicate or regex-op makes an entry in the registry mapping k to the spec" [k spec-form] - (let [k (if (symbol? k) (ns-qualify &env k) k)] - `(cljs.spec/def-impl '~k '~(res &env spec-form) ~spec-form))) + (let [k (if (symbol? k) (ns-qualify &env k) k) + form (res &env spec-form)] + (swap! registry-ref assoc k form) + `(cljs.spec/def-impl '~k '~form ~spec-form))) (defmacro spec "Takes a single predicate form, e.g. can be the name of a predicate, From b44d0e07b5ff89c662d77a261d4d06bc7da92655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 30 Jul 2016 00:00:51 +0100 Subject: [PATCH 1935/4033] CLJS-1719: Port destructuring namespaced keys and symbols --- src/main/clojure/cljs/core.cljc | 44 ++++++++++++++++-------- src/test/cljs/cljs/core_test.cljs | 37 ++++++++++++++++++++ src/test/clojure/cljs/compiler_tests.clj | 15 ++++++++ 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index ab8dd3c4d..4fdb084d7 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -657,24 +657,40 @@ (if (:as b) (conj ret (:as b) gmap) ret)))) - bes (reduce - (core/fn [bes entry] - (reduce #(assoc %1 %2 ((val entry) %2)) - (dissoc bes (key entry)) - ((key entry) bes))) - (dissoc b :as :or) - {:keys #(if (core/keyword? %) % (keyword (core/str %))), - :strs core/str, :syms #(core/list `quote %)})] + bes (core/let [transforms + (reduce + (core/fn [transforms mk] + (if (core/keyword? mk) + (core/let [mkns (namespace mk) + mkn (name mk)] + (core/cond (= mkn "keys") (assoc transforms mk #(keyword (core/or mkns (namespace %)) (name %))) + (= mkn "syms") (assoc transforms mk #(core/list `quote (symbol (core/or mkns (namespace %)) (name %)))) + (= mkn "strs") (assoc transforms mk core/str) + :else transforms)) + transforms)) + {} + (keys b))] + (reduce + (core/fn [bes entry] + (reduce #(assoc %1 %2 ((val entry) %2)) + (dissoc bes (key entry)) + ((key entry) bes))) + (dissoc b :as :or) + transforms))] (if (seq bes) (core/let [bb (key (first bes)) bk (val (first bes)) - bv (if (contains? defaults bb) - (core/list 'cljs.core/get gmap bk (defaults bb)) + local (if #?(:clj (core/instance? clojure.lang.Named bb) + :cljs (cljs.core/implements? INamed bb)) + (symbol nil (name bb)) + bb) + bv (if (contains? defaults local) + (core/list 'cljs.core/get gmap bk (defaults local)) (core/list 'cljs.core/get gmap bk))] - (recur (core/cond - (core/symbol? bb) (core/-> ret (conj (if (namespace bb) (symbol (name bb)) bb)) (conj bv)) - (core/keyword? bb) (core/-> ret (conj (symbol (name bb)) bv)) - :else (pb ret bb bv)) + (recur + (if (core/or (core/keyword? bb) (core/symbol? bb)) ;(ident? bb) + (core/-> ret (conj local bv)) + (pb ret bb bv)) (next bes))) ret))))] (core/cond diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index ab64102d9..cb904ef6d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -989,6 +989,43 @@ (is (= y 2)))) ))) +(deftest keywords-in-destructuring + (let [m {:a 1 :b 2}] + (let [{:keys [:a :b]} m] + (is (= [1 2] [a b]))) + (let [{:keys [:a :b :c] :or {c 3}} m] + (is (= [1 2 3] [a b c]))))) + +(deftest namespaced-keywords-in-destructuring + (let [m {:a/b 1 :c/d 2}] + (let [{:keys [:a/b :c/d]} m] + (is (= [1 2] [b d]))) + (let [{:keys [:a/b :c/d :e/f] :or {f 3}} m] + (is (= [1 2 3] [b d f]))))) + +(deftest namespaced-keys-in-destructuring + (let [m {:a/b 1 :c/d 2}] + (let [{:keys [a/b c/d]} m] + (is (= [1 2] [b d]))) + (let [{:keys [a/b c/d e/f] :or {f 3}} m] + (is (= [1 2 3] [b d f]))))) + +(deftest namespaced-syms-in-destructuring + (let [{:syms [a/b c/d e/f] :or {f 3}} {'a/b 1 'c/d 2}] + (is (= [1 2 3] [b d f])))) + +(deftest namespaced-keys-syntax + (let [{:a/keys [b c d] :or {d 3}} {:a/b 1 :a/c 2}] + (is (= [1 2 3] [b c d])))) + +(deftest namespaced-syms-syntax + (let [{:a/syms [b c d] :or {d 3}} {'a/b 1 'a/c 2}] + (is (= [1 2 3] [b c d])))) + +(deftest resolve-keyword-ns-alias-in-destructuring + (let [{:keys [::s/x ::s/y ::s/z] :or {z 3}} {:clojure.string/x 1 :clojure.string/y 2}] + (is (= [1 2 3] [x y z])))) + (deftest test-in-operations (testing "Testing update-in" (is (= {:foo {:bar {:baz 1}}} diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index baff78e42..9390a775d 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -102,6 +102,21 @@ (defmacro capture-warnings [& body] `(capture-warnings* (fn [] ~@body))) +(deftest or-doesnt-create-bindings + (let [cenv (atom @cenv)] + (binding [ana/*cljs-static-fns* true + ana/*analyze-deps* false] + (env/with-compiler-env cenv + (ana/analyze-file (File. "src/main/cljs/cljs/core.cljs")) + (let [warnings (-> (capture-warnings + (with-out-str + (comp/emit + (ana/analyze aenv + '(let [{:keys [a] :or {b 2}} {:a 1}] [a b]))))))] + (is (= (ffirst warnings) :undeclared-var)) + (is (.startsWith (-> warnings first second) + "WARNING: Use of undeclared Var cljs.user/b"))))))) + (deftest no-warn-on-emit-invoke-protocol-method (let [define-foo #(assoc-in % [::ana/namespaces 'cljs.user :defs 'foo] {:ns 'cljs.user From dd8797d35a98d79511ccb9ad60981ab7b2b81db4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 30 Jul 2016 12:45:15 -0400 Subject: [PATCH 1936/4033] macro-ize sym->checkmap --- src/main/cljs/cljs/spec/test.cljc | 10 ++++++++++ src/main/cljs/cljs/spec/test.cljs | 9 ++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 1804f98ea..4d31dc1a0 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -139,3 +139,13 @@ Returns a collection of syms naming the vars unstrumented." `(fn [] (unstrument-1 '~sym))))) (remove nil?))])))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmacro sym->check-map + [[quote s]] + (if-let [{:keys [name] :as v} (ana-api/resolve &env s)] + `{:s '~s + :v (var ~name) + :spec (s/get-spec (var ~name))} + `{:s '~s})) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index c76c81135..319fa7044 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -242,13 +242,6 @@ with explain-data + ::s/failure." (finally (when re-inst? (instrument s)))))) -(defn- sym->check-map - [s] - (let [v (resolve s)] - {:s s - :v v - :spec (when v (s/get-spec v))})) - (defn- validate-check-opts [opts] (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) @@ -422,6 +415,8 @@ key with a count for each different :type of result." (ranged-rand 8 5))) (foo 1 2) (m/unstrument-1 ranged-rand) + + (m/sym->check-map 'ranged-rand) ) From af2e8b38b648e1f9ec4abd48934a8fa478cfa53b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 30 Jul 2016 13:31:35 -0400 Subject: [PATCH 1937/4033] drop sym->check-map helper, implement check-1 --- src/main/cljs/cljs/spec/test.cljc | 31 ++++++++++++++++++++++++------- src/main/cljs/cljs/spec/test.cljs | 24 ++---------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 4d31dc1a0..04ec1c1bc 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -142,10 +142,27 @@ Returns a collection of syms naming the vars unstrumented." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defmacro sym->check-map - [[quote s]] - (if-let [{:keys [name] :as v} (ana-api/resolve &env s)] - `{:s '~s - :v (var ~name) - :spec (s/get-spec (var ~name))} - `{:s '~s})) \ No newline at end of file +(defmacro check-1 + [[quote s] f opts] + (let [{:keys [name] :as v} (ana-api/resolve &env s)] + `(let [s# '~name + opts# ~opts + v# ~(when v `(var ~name)) + spec# ~(when v `(s/get-spec (var ~name))) + re-inst?# (and v# (seq (unstrument '~name)) true) + f# (or ~f (when v# @v#))] + (try + (cond + (nil? f#) + {:failure (ex-info "No fn to spec" {::s/failure :no-fn}) + :sym s# :spec spec#} + + (:args spec#) + (let [tcret# (quick-check f# spec# opts#)] + (make-check-result s# spec# tcret#)) + + :default + {:failure (ex-info "No :args spec" {::s/failure :no-args-spec}) + :sym s# :spec spec#}) + (finally + (when re-inst?# (instrument '~name))))))) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 319fa7044..e8a71a6fc 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -222,26 +222,6 @@ with explain-data + ::s/failure." (when-let [shrunk (-> test-check-ret :shrunk)] {:failure (:result shrunk)}))) -(defn- check-1 - [{:keys [s f v spec]} opts] - (let [re-inst? (and v (seq (unstrument s)) true) - f (or f (when v @v))] - (try - (cond - (nil? f) - {:failure (ex-info "No fn to spec" {::s/failure :no-fn}) - :sym s :spec spec} - - (:args spec) - (let [tcret (quick-check f spec opts)] - (make-check-result s spec tcret)) - - :default - {:failure (ex-info "No :args spec" {::s/failure :no-args-spec}) - :sym s :spec spec}) - (finally - (when re-inst? (instrument s)))))) - (defn- validate-check-opts [opts] (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) @@ -414,9 +394,9 @@ key with a count for each different :type of result." ([a b] (ranged-rand 8 5))) (foo 1 2) - (m/unstrument-1 ranged-rand) + (m/unstrument-1 `ranged-rand) - (m/sym->check-map 'ranged-rand) + (m/check-1 `ranged-rand nil {}) ) From 2267a8e5e9607aff8c940fa372dbdb35816350ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 30 Jul 2016 18:28:57 +0100 Subject: [PATCH 1938/4033] CLJS-1490: Watch macro files in cljs.build.api/watch --- src/main/clojure/cljs/build/api.clj | 23 +++++----------- src/main/clojure/cljs/closure.clj | 41 +++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index efb0de14b..3fc446a9a 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -17,7 +17,6 @@ [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.closure :as closure] - [clojure.set :refer [intersection]] [cljs.js-deps :as js-deps] [clojure.java.io :as io]) (:import java.io.File)) @@ -32,19 +31,13 @@ For example: (target-file-from-cljs-ns \"resources/out\" 'example.core) -> " - ([ns-sym] (target-file-for-cljs-ns ns-sym nil)) - ([ns-sym output-dir] - (util/to-target-file - (util/output-directory {:output-dir output-dir}) - {:ns ns-sym}))) + ([ns-sym] (closure/target-file-for-cljs-ns ns-sym nil)) + ([ns-sym output-dir] (closure/target-file-for-cljs-ns ns-sym output-dir))) (defn mark-cljs-ns-for-recompile! "Backdates a cljs target file so that it the cljs compiler will recompile it." - ([ns-sym] (mark-cljs-ns-for-recompile! ns-sym nil)) - ([ns-sym output-dir] - (let [s (target-file-for-cljs-ns ns-sym output-dir)] - (when (.exists s) - (.setLastModified s 5000))))) + ([ns-sym] (closure/mark-cljs-ns-for-recompile! ns-sym nil)) + ([ns-sym output-dir] (closure/mark-cljs-ns-for-recompile! ns-sym output-dir))) (defn cljs-dependents-for-macro-namespaces "Takes a list of Clojure (.clj) namespaces that define macros and @@ -58,17 +51,13 @@ (cljs-dependents-for-macro-namespaces 'example.macros) -> ('example.core 'example.util)" ([namespaces] - (cljs-dependents-for-macro-namespaces + (closure/cljs-dependents-for-macro-namespaces (if-not (nil? env/*compiler*) env/*compiler* (env/default-compiler-env)) namespaces)) ([state namespaces] - (map :name - (let [namespaces-set (set namespaces)] - (filter (fn [x] (not-empty - (intersection namespaces-set (-> x :require-macros vals set)))) - (vals (:cljs.analyzer/namespaces @state))))))) + (closure/cljs-dependents-for-macro-namespaces state namespaces))) (defn parse-js-ns "Given a Google Closure style JavaScript file or resource return the namespace diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 03604f9da..d89757761 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2043,6 +2043,26 @@ :entries '#{cljs.reader hello.core}}}}) ) +(defn ^File target-file-for-cljs-ns + [ns-sym output-dir] + (util/to-target-file + (util/output-directory {:output-dir output-dir}) + {:ns ns-sym})) + +(defn mark-cljs-ns-for-recompile! + [ns-sym output-dir] + (let [s (target-file-for-cljs-ns ns-sym output-dir)] + (when (.exists s) + (.setLastModified s 5000)))) + +(defn cljs-dependents-for-macro-namespaces + [state namespaces] + (map :name + (let [namespaces-set (set namespaces)] + (filter (fn [x] (not-empty + (set/intersection namespaces-set (-> x :require-macros vals set)))) + (vals (:cljs.analyzer/namespaces @state)))))) + (defn watch "Given a source directory, produce runnable JavaScript. Watch the source directory for changes rebuliding when necessary. Takes the same arguments as @@ -2103,16 +2123,33 @@ (loop [key nil] (when (and (or (nil? quit) (not @quit)) (or (nil? key) (. ^WatchKey key reset))) - (let [key (. srvc (poll 300 TimeUnit/MILLISECONDS))] + (let [key (. srvc (poll 300 TimeUnit/MILLISECONDS)) + poll-events-seq (when key (seq (.pollEvents key)))] (when (and key (some (fn [^WatchEvent e] (let [fstr (.. e context toString)] (and (or (. fstr (endsWith "cljc")) (. fstr (endsWith "cljs")) + (. fstr (endsWith "clj")) (. fstr (endsWith "js"))) (not (. fstr (startsWith ".#")))))) - (seq (.pollEvents key)))) + poll-events-seq)) + (when-let [clj-files (seq (keep (fn [^WatchEvent e] + (let [ctx (.context e) + fstr (.toString ctx)] + (when (and (or (. fstr (endsWith "cljc")) + (. fstr (endsWith "clj"))) + (not (. fstr (startsWith ".#")))) + ctx))) + poll-events-seq))] + (let [^Path dir (.watchable key) + file-seq (map #(.toFile (.resolve dir %)) clj-files) + nses (map (comp :ns ana/parse-ns) file-seq)] + (doseq [ns nses] + (require ns :reload)) + (doseq [ns (cljs-dependents-for-macro-namespaces compiler-env nses)] + (mark-cljs-ns-for-recompile! ns (:output-dir opts))))) (println "Change detected, recompiling ...") (flush) (buildf)) From 03a7c20e96c4a1c964a3fd133cc8d09c96006466 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 30 Jul 2016 13:59:42 -0400 Subject: [PATCH 1939/4033] port check-fn --- src/main/cljs/cljs/spec/test.cljc | 18 ++++++++++++++---- src/main/cljs/cljs/spec/test.cljs | 23 ++++++++++------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 04ec1c1bc..27ba63dcf 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -143,12 +143,12 @@ Returns a collection of syms naming the vars unstrumented." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro check-1 - [[quote s] f opts] - (let [{:keys [name] :as v} (ana-api/resolve &env s)] + [[quote s :as qs] f spec opts] + (let [{:keys [name] :as v} (when qs (ana-api/resolve &env s))] `(let [s# '~name opts# ~opts v# ~(when v `(var ~name)) - spec# ~(when v `(s/get-spec (var ~name))) + spec# (or ~spec ~(when v `(s/get-spec (var ~name)))) re-inst?# (and v# (seq (unstrument '~name)) true) f# (or ~f (when v# @v#))] (try @@ -165,4 +165,14 @@ Returns a collection of syms naming the vars unstrumented." {:failure (ex-info "No :args spec" {::s/failure :no-args-spec}) :sym s# :spec spec#}) (finally - (when re-inst?# (instrument '~name))))))) \ No newline at end of file + (when re-inst?# (instrument '~name))))))) + +(defmacro check-fn + "Runs generative tests for fn f using spec and opts. See +'check' for options and return." + ([f spec] + `(check-fn ~f ~spec nil)) + ([f spec opts] + `(let [opts# ~opts] + (validate-check-opts opts#) + (check-1 nil ~f ~spec opts#)))) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index e8a71a6fc..7f0d3062d 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -226,14 +226,6 @@ with explain-data + ::s/failure." [opts] (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) -(defn check-fn - "Runs generative tests for fn f using spec and opts. See -'check' for options and return." - ([f spec] (check-fn f spec nil)) - ([f spec opts] - (validate-check-opts opts) - (check-1 {:f f :spec spec} opts))) - (defn checkable-syms "Given an opts map as per check, returns the set of syms that can be checked." @@ -370,10 +362,10 @@ key with a count for each different :type of result." (s/fdef ranged-rand :args (s/and (s/cat :start int? :end int?) - #(< (:start %) (:end %))) - :ret int? - :fn (s/and #(>= (:ret %) (-> % :args :start)) - #(< (:ret %) (-> % :args :end)))) + #(< (:start %) (:end %))) + :ret int? + :fn (s/and #(>= (:ret %) (-> % :args :start)) + #(< (:ret %) (-> % :args :end)))) (instrumentable-syms) @@ -396,7 +388,12 @@ key with a count for each different :type of result." (foo 1 2) (m/unstrument-1 `ranged-rand) - (m/check-1 `ranged-rand nil {}) + (m/check-1 `ranged-rand nil nil {}) + + (m/check-fn inc + (s/fspec + :args (s/cat :x int?) + :ret int?)) ) From f48d6eca9b12cb1bc4289c2c2d4c93728db80fed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 30 Jul 2016 14:35:53 -0400 Subject: [PATCH 1940/4033] implement checkable-syms --- src/main/cljs/cljs/spec/test.cljc | 18 +++++++++++++++++- src/main/cljs/cljs/spec/test.cljs | 11 ++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 27ba63dcf..ca4091d53 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -21,6 +21,10 @@ (list x) x)) +(defn- fn-spec-name? + [s] + (symbol? s)) + (defmacro enumerate-namespace "Given a symbol naming an ns, or a collection of such symbols, returns the set of all symbols naming vars in those nses." @@ -175,4 +179,16 @@ Returns a collection of syms naming the vars unstrumented." ([f spec opts] `(let [opts# ~opts] (validate-check-opts opts#) - (check-1 nil ~f ~spec opts#)))) \ No newline at end of file + (check-1 nil ~f ~spec opts#)))) + +(defmacro checkable-syms + "Given an opts map as per check, returns the set of syms that +can be checked." + ([] + `(checkable-syms nil)) + ([opts] + `(let [opts# ~opts] + (validate-check-opts opts#) + (reduce conj #{} + '[~@(filter fn-spec-name? (keys @s/registry-ref)) + ~@(keys (:spec opts))])))) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 7f0d3062d..14454a4e7 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -226,15 +226,6 @@ with explain-data + ::s/failure." [opts] (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) -(defn checkable-syms - "Given an opts map as per check, returns the set of syms that -can be checked." - ([] (checkable-syms nil)) - ([opts] - (validate-check-opts opts) - (reduce into #{} [(filter fn-spec-name? (keys (s/registry))) - (keys (:spec opts))]))) - (defn check "Run generative tests for spec conformance on vars named by sym-or-syms, a symbol or collection of symbols. If sym-or-syms @@ -394,6 +385,8 @@ key with a count for each different :type of result." (s/fspec :args (s/cat :x int?) :ret int?)) + + (m/checkable-syms) ) From e2db5d9ff8cb6a099ebc2a8cd379385bf4649b38 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 30 Jul 2016 14:55:42 -0400 Subject: [PATCH 1941/4033] implement check --- src/main/cljs/cljs/spec/test.cljc | 54 ++++++++++++++++++++++++++++++- src/main/cljs/cljs/spec/test.cljs | 43 ++---------------------- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index ca4091d53..7921cc42a 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -181,6 +181,11 @@ Returns a collection of syms naming the vars unstrumented." (validate-check-opts opts#) (check-1 nil ~f ~spec opts#)))) +(defn checkable-syms* [opts] + (reduce into #{} + [(filter fn-spec-name? (keys @s/registry-ref)) + (keys (:spec opts))])) + (defmacro checkable-syms "Given an opts map as per check, returns the set of syms that can be checked." @@ -191,4 +196,51 @@ can be checked." (validate-check-opts opts#) (reduce conj #{} '[~@(filter fn-spec-name? (keys @s/registry-ref)) - ~@(keys (:spec opts))])))) \ No newline at end of file + ~@(keys (:spec opts))])))) + +(defmacro check + "Run generative tests for spec conformance on vars named by +sym-or-syms, a symbol or collection of symbols. If sym-or-syms +is not specified, check all checkable vars. + +The opts map includes the following optional keys, where stc +aliases clojure.spec.test.check: + +::stc/opts opts to flow through test.check/quick-check +:gen map from spec names to generator overrides + +The ::stc/opts include :num-tests in addition to the keys +documented by test.check. Generator overrides are passed to +spec/gen when generating function args. + +Returns a lazy sequence of check result maps with the following +keys + +:spec the spec tested +:sym optional symbol naming the var tested +:failure optional test failure +::stc/ret optional value returned by test.check/quick-check + +The value for :failure can be any exception. Exceptions thrown by +spec itself will have an ::s/failure value in ex-data: + +:check-failed at least one checked return did not conform +:no-args-spec no :args spec provided +:no-fn no fn provided +:no-fspec no fspec provided +:no-gen unable to generate :args +:instrument invalid args detected by instrument +" + ([] + `(check '~(checkable-syms*))) + ([sym-or-syms] + `(check ~sym-or-syms nil)) + ([sym-or-syms opts] + (let [sym-or-syms (eval sym-or-syms) + opts-sym (gensym "opts")] + `(let [~opts-sym ~opts] + [~@(->> (collectionize sym-or-syms) + (filter (checkable-syms* opts)) + (map + (fn [sym] + (do `(check-1 '~sym nil nil ~opts-sym)))))])))) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 14454a4e7..b074ee4f5 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -226,47 +226,6 @@ with explain-data + ::s/failure." [opts] (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) -(defn check - "Run generative tests for spec conformance on vars named by -sym-or-syms, a symbol or collection of symbols. If sym-or-syms -is not specified, check all checkable vars. - -The opts map includes the following optional keys, where stc -aliases clojure.spec.test.check: - -::stc/opts opts to flow through test.check/quick-check -:gen map from spec names to generator overrides - -The ::stc/opts include :num-tests in addition to the keys -documented by test.check. Generator overrides are passed to -spec/gen when generating function args. - -Returns a lazy sequence of check result maps with the following -keys - -:spec the spec tested -:sym optional symbol naming the var tested -:failure optional test failure -::stc/ret optional value returned by test.check/quick-check - -The value for :failure can be any exception. Exceptions thrown by -spec itself will have an ::s/failure value in ex-data: - -:check-failed at least one checked return did not conform -:no-args-spec no :args spec provided -:no-fn no fn provided -:no-fspec no fspec provided -:no-gen unable to generate :args -:instrument invalid args detected by instrument -" - ([] (check (checkable-syms))) - ([sym-or-syms] (check sym-or-syms nil)) - ([sym-or-syms opts] - (->> (collectionize sym-or-syms) - (filter (checkable-syms opts)) - (map - #(check-1 (sym->check-map %) opts))))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; check reporting ;;;;;;;;;;;;;;;;;;;;;;;; (defn- failure-type @@ -387,6 +346,8 @@ key with a count for each different :type of result." :ret int?)) (m/checkable-syms) + + (m/check `ranged-rand) ) From 2cc8f921cb1e56dbeb901e61a7149d283ad78bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 5 Aug 2016 13:38:06 +0100 Subject: [PATCH 1942/4033] CLJS-1727: Regression when evaluating non-sequential forms at the REPL --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index dd148b07a..2311e1914 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -485,7 +485,7 @@ (wrap form) nil opts))))] ;; NOTE: means macros which expand to ns aren't supported for now ;; when eval'ing individual forms at the REPL - David - (when (= 'ns (first form)) + (when (and (sequential? form) (= 'ns (first form))) (let [ast (ana/analyze env form nil opts)] (load-dependencies repl-env (into (vals (:requires ast)) From aceccf78ff0fc78055bfa4eba4d9efc53bfcc568 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 9 Aug 2016 08:27:03 -0400 Subject: [PATCH 1943/4033] add browser REPL preload --- src/main/cljs/clojure/browser/repl/preload.cljs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/cljs/clojure/browser/repl/preload.cljs diff --git a/src/main/cljs/clojure/browser/repl/preload.cljs b/src/main/cljs/clojure/browser/repl/preload.cljs new file mode 100644 index 000000000..cd7435647 --- /dev/null +++ b/src/main/cljs/clojure/browser/repl/preload.cljs @@ -0,0 +1,15 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns clojure.browser.repl.preload + (:require [clojure.browser.repl :as repl])) + +(defonce conn + (repl/connect "http://localhost:9000/repl")) + +(enable-console-print!) From 0a3834a5ef966d24d9aa9cd8b504046f716ef321 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 9 Aug 2016 08:40:58 -0400 Subject: [PATCH 1944/4033] add spec TODO --- src/main/cljs/cljs/spec/test.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index b074ee4f5..1a1199469 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -81,6 +81,8 @@ (drop-while search-spec-fn) first))) +;; TODO: check ::caller result in other browsers - David + (defn- spec-checking-fn [v f fn-spec] (let [fn-spec (@#'s/maybe-spec fn-spec) From e6f37a018c5d611994174db55883ee611ab62006 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 9 Aug 2016 17:15:12 -0400 Subject: [PATCH 1945/4033] CLJS-1350: Compiler support for browser REPL Now that :preloads exists, this is simple to support --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d89757761..8694dcbe5 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1868,7 +1868,9 @@ (into {} (map (fn [[k v]] [(if (symbol? k) (str (comp/munge k)) k) v]) - (:closure-defines opts))))) + (:closure-defines opts)))) + (:browser-repl opts) + (update-in [:preloads] (fnil conj []) 'clojure.browser.repl.preload)) {:keys [libs foreign-libs externs]} (get-upstream-deps) emit-constants (or (and (= optimizations :advanced) (not (false? (:optimize-constants opts)))) From d388905d67d107e2afbc4d5d4be9f7f935063177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 1 Aug 2016 13:23:32 +0100 Subject: [PATCH 1946/4033] CLJS-1508: Extend ns form to support :rename option --- src/main/cljs/cljs/js.cljs | 34 ++++- src/main/clojure/cljs/analyzer.cljc | 163 ++++++++++++++++++--- src/test/cljs/cljs/clojure_alias_test.cljs | 9 +- src/test/cljs/cljs/ns_test.cljs | 15 +- src/test/clojure/cljs/analyzer_tests.clj | 94 +++++++++++- src/test/self/self_host/test.cljs | 23 ++- 6 files changed, 298 insertions(+), 40 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 9bf22cfea..b02cd45a1 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -306,11 +306,22 @@ (if (and add-if-present? (some #{to} (vals replaced))) (assoc replaced from to) - replaced)))))] + replaced))))) + patch-renames (fn [k] + (swap! compiler update-in [::ana/namespaces in k] + (fn [m] + (when m + (reduce (fn [acc [renamed qualified-sym :as entry]] + (if (= (str from) (namespace qualified-sym)) + (assoc acc renamed (symbol (str to) (name qualified-sym))) + (merge acc entry))) + {} m)))))] (patch :requires true) (patch :require-macros true) (patch :uses false) - (patch :use-macros false))) + (patch :use-macros false) + (patch-renames :renames) + (patch-renames :rename-macros))) (defn- load-deps ([bound-vars ana-env lib deps cb] @@ -409,9 +420,20 @@ (defn- rewrite-ns-ast [ast smap] - (-> ast - (update :uses #(walk/postwalk-replace smap %)) - (update :requires #(merge smap (walk/postwalk-replace smap %))))) + (let [rewrite-renames (fn [m] + (when m + (reduce (fn [acc [renamed qualified-sym :as entry]] + (let [from (symbol (namespace qualified-sym)) + to (get smap from)] + (if (some? to) + (assoc acc renamed (symbol (str to) (name qualified-sym))) + (merge acc entry)))) + {} m)))] + (-> ast + (update :uses #(walk/postwalk-replace smap %)) + (update :requires #(merge smap (walk/postwalk-replace smap %))) + (update :renames rewrite-renames) + (update :rename-macros rewrite-renames)))) (defn- ns-side-effects ([bound-vars ana-env ast opts cb] @@ -1038,4 +1060,4 @@ (println error) (println (.. error -cause -stack))) (println res)))) - ) \ No newline at end of file + ) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7c8ca3c14..3e48d0642 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -781,6 +781,15 @@ {:name (symbol (str full-ns) (str sym)) :ns full-ns})) + (not (nil? (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym))) + (let [qualified-symbol (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym) + full-ns (symbol (namespace qualified-symbol)) + sym (symbol (name qualified-symbol))] + (merge + (gets @env/*compiler* ::namespaces full-ns :defs sym) + {:name qualified-symbol + :ns full-ns})) + (not (nil? (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym))) (recur env (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) confirm) @@ -830,6 +839,12 @@ (let [full-ns (get-in namespaces [ns :use-macros sym])] (get-in namespaces [full-ns :macros sym])) + (get-in namespaces [ns :rename-macros sym]) + (let [qualified-symbol (get-in namespaces [ns :rename-macros sym]) + full-ns (symbol (namespace qualified-symbol)) + sym (symbol (name qualified-symbol))] + (get-in namespaces [full-ns :macros sym])) + :else (let [ns (cond (get-in namespaces [ns :macros sym]) ns @@ -1727,14 +1742,29 @@ (not (= (get js-lib :group) :goog)) (not (get js-lib :closure-lib))))) +(defn missing-rename? [sym cenv] + (let [lib (symbol (namespace sym)) + sym (symbol (name sym))] + (missing-use? lib sym cenv))) + (defn missing-use-macro? [lib sym] (let [the-ns #?(:clj (find-ns lib) :cljs (find-macros-ns lib))] (or (nil? the-ns) (nil? (.findInternedVar ^clojure.lang.Namespace the-ns sym))))) +(defn missing-rename-macro? [sym] + (let [lib (symbol (namespace sym)) + sym (symbol (name sym)) + the-ns #?(:clj (find-ns lib) :cljs (find-macros-ns lib))] + (or (nil? the-ns) (nil? (.findInternedVar ^clojure.lang.Namespace the-ns sym))))) + (defn missing-uses [uses env] (let [cenv @env/*compiler*] (into {} (filter (fn [[sym lib]] (missing-use? lib sym cenv)) uses)))) +(defn missing-renames [renames] + (let [cenv @env/*compiler*] + (into {} (filter (fn [[_ qualified-sym]] (missing-rename? qualified-sym cenv)) renames)))) + (defn missing-use-macros [use-macros env] (let [cenv @env/*compiler*] (into {} (filter (fn [[sym lib]] (missing-use-macro? lib sym)) use-macros)))) @@ -1743,6 +1773,9 @@ (let [cenv @env/*compiler*] (into {} (filter (fn [[sym lib]] (not (missing-use-macro? lib sym))) use-macros)))) +(defn inferred-rename-macros [rename-macros env] + (into {} (filter (fn [[_ qualified-sym]] (not (missing-rename-macro? qualified-sym))) rename-macros))) + (defn check-uses [uses env] (let [cenv @env/*compiler*] (doseq [[sym lib] uses] @@ -1766,14 +1799,21 @@ (defn check-use-macros-inferring-missing [ast name use-macros missing-uses env] - (let [remove-missing-uses #(apply dissoc % (keys missing-uses)) + (let [remove-missing-uses #(apply dissoc % (keys missing-uses)) + missing-renames (missing-renames (:renames ast)) + missing-rename-macros (inferred-rename-macros missing-renames env) + remove-missing-renames #(apply dissoc % (keys missing-renames)) ast' (-> ast (update-in [:use-macros] merge (check-use-macros use-macros missing-uses env)) - (update-in [:uses] remove-missing-uses))] + (update-in [:uses] remove-missing-uses) + (update-in [:rename-macros] merge missing-rename-macros) + (update-in [:renames] remove-missing-renames))] (swap! env/*compiler* #(-> % (update-in [::namespaces name :use-macros] merge (:use-macros ast')) - (update-in [::namespaces name :uses] remove-missing-uses))) + (update-in [::namespaces name :uses] remove-missing-uses) + (update-in [::namespaces name :rename-macros] merge (:rename-macros ast')) + (update-in [::namespaces name :renames] remove-missing-renames))) ast')) (defn parse-ns-error-msg [spec msg] @@ -1795,12 +1835,12 @@ (throw (error env (parse-ns-error-msg spec - "Only :as alias and :refer (names) options supported in :require")))) - (when-not (every? #{:as :refer} (map first (partition 2 (next spec)))) + "Only :as alias, :refer (names) and :rename {from to} options supported in :require")))) + (when-not (every? #{:as :refer :rename} (map first (partition 2 (next spec)))) (throw (error env (parse-ns-error-msg spec - "Only :as and :refer options supported in :require / :require-macros")))) + "Only :as, :refer and :rename options supported in :require / :require-macros")))) (when-not (let [fs (frequencies (next spec))] (and (<= (fs :as 0) 1) (<= (fs :refer 0) 1))) @@ -1811,24 +1851,76 @@ (defn parse-ns-excludes [env args] (reduce - (fn [s [k exclude xs]] + (fn [s [k & filters]] (if (= k :refer-clojure) (do - (when-not (= exclude :exclude) - (throw (error env "Only [:refer-clojure :exclude (names)] form supported"))) (when (seq s) (throw (error env "Only one :refer-clojure form is allowed per namespace definition"))) - (into s xs)) + (let [valid-kws #{:exclude :rename} + xs + (loop [fs (seq filters) + ret {:excludes #{} + :renames {}} + err (not (even? (count filters)))] + (cond + (true? err) + (throw + (error env "Only [:refer-clojure :exclude (names)] and optionally `:rename {from to}` specs supported")) + + (not (nil? fs)) + (let [kw (first fs)] + (if (valid-kws kw) + (let [refs (second fs)] + (cond + (not (or (and (= kw :exclude) (sequential? refs) (every? symbol? refs)) + (and (= kw :rename) (map? refs) (every? #(every? symbol? %) refs)))) + (recur fs ret true) + + (= kw :exclude) + (recur (nnext fs) (update-in ret [:excludes] into refs) false) + + (= kw :rename) + (recur (nnext fs) (update-in ret [:renames] merge refs) false))) + (recur fs ret true ))) + + :else ret))] + (merge-with into s xs))) s)) - #{} args)) + {} args)) -(defn use->require [env [lib kw referred :as spec]] - (when-not (and (symbol? lib) (= :only kw) (sequential? referred) (every? symbol? referred)) +(defn use->require [env [lib & filters :as spec]] + (when-not (and (symbol? lib) (odd? (count spec))) (throw (error env (parse-ns-error-msg spec - "Only [lib.ns :only (names)] specs supported in :use / :use-macros")))) - [lib :refer referred]) + "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")))) + (loop [fs (seq filters) ret [lib] err false] + (cond + (true? err) + (throw + (error env + (parse-ns-error-msg spec + "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros"))) + + (not (nil? fs)) + (let [kw (first fs) + only? (= kw :only)] + (if (or only? (= kw :rename)) + (if (some #{(if only? :refer kw)} ret) + (throw + (error env + (parse-ns-error-msg spec + "Each of :only and :rename options may only be specified once in :use / :use-macros"))) + (let [refs (second fs)] + (if-not (or (and only? (sequential? refs) (every? symbol? refs)) + (and (= kw :rename) (map? refs) (every? #(every? symbol? %) refs))) + (recur fs ret true) + (recur (nnext fs) (into ret [(if only? :refer kw) refs]) false)))) + (recur fs ret true ))) + + :else (if (some #{:refer} ret) + ret + (recur fs ret true))))) (defn parse-require-spec [env macros? deps aliases spec] (if (symbol? spec) @@ -1839,8 +1931,9 @@ lib (if-let [js-module-name (get-in @env/*compiler* [:js-module-index (name lib)])] (symbol js-module-name) lib) - {alias :as referred :refer :or {alias lib}} (apply hash-map opts) - [rk uk] (if macros? [:require-macros :use-macros] [:require :use])] + {alias :as referred :refer renamed :rename :or {alias lib}} (apply hash-map opts) + referred-without-renamed (seq (remove (set (keys renamed)) referred)) + [rk uk renk] (if macros? [:require-macros :use-macros :rename-macros] [:require :use :rename])] (when-not (or (symbol? alias) (nil? alias)) (throw (error env @@ -1866,7 +1959,14 @@ (merge (when alias {rk (merge {alias lib} {lib lib})}) - (when referred {uk (apply hash-map (interleave referred (repeat lib)))})))))) + (when referred-without-renamed {uk (apply hash-map (interleave referred-without-renamed (repeat lib)))}) + (when 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)})))))) (defn parse-import-spec [env deps spec] (when-not (or (and (sequential? spec) @@ -1977,6 +2077,7 @@ (if-not (reload-spec? x) (->> x (remove-from-spec #{:include-macros}) (remove-from-spec #{:refer}) + (remove-from-spec #{:rename}) (replace-refer-macros)) x))))) remove-sugar (partial remove-from-spec sugar-keys)] @@ -2034,7 +2135,10 @@ (if metadata (next args) args)) :cljs (if metadata (next args) args))) name (vary-meta name merge metadata) - excludes (parse-ns-excludes env args) + {excludes :excludes core-renames :renames} (parse-ns-excludes env args) + core-renames (reduce (fn [m [original renamed]] + (assoc m renamed (symbol "cljs.core" (str original)))) + {} core-renames) deps (atom #{}) aliases (atom {:fns {} :macros {}}) spec-parsers {:require (partial parse-require-spec env false deps aliases) @@ -2047,7 +2151,9 @@ valid-forms (atom #{:use :use-macros :require :require-macros :import}) reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) reloads (atom {}) - {uses :use requires :require use-macros :use-macros require-macros :require-macros imports :import :as params} + {uses :use requires :require renames :rename + use-macros :use-macros require-macros :require-macros + rename-macros :rename-macros imports :import :as params} (reduce (fn [m [k & libs]] (when-not (#{:use :use-macros :require :require-macros :import} k) @@ -2076,8 +2182,10 @@ :excludes excludes :use-macros use-macros :require-macros require-macros + :rename-macros rename-macros :uses uses :requires requires + :renames (merge renames core-renames) :imports imports} ns-info (if (:merge form-meta) @@ -2085,7 +2193,8 @@ (let [ns-info' (get-in @env/*compiler* [::namespaces name])] (if (pos? (count ns-info')) (let [merge-keys - [:use-macros :require-macros :uses :requires :imports]] + [:use-macros :require-macros :rename-macros + :uses :requires :renames :imports]] (merge ns-info' (merge-with merge @@ -2458,10 +2567,20 @@ (when-not (or (not (nil? (gets env :locals sym))) ; locals hide macros (and (excluded? env sym) (not (used? env sym)))) (let [nstr (namespace sym)] - (if-not (nil? nstr) + (cond + (not (nil? nstr)) (let [ns (get-expander-ns env nstr)] (when-not (nil? ns) (.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym))))) + + (not (nil? (gets env :ns :rename-macros sym))) + (let [qualified-symbol (gets env :ns :rename-macros sym) + nsym (symbol (namespace qualified-symbol)) + sym (symbol (name qualified-symbol))] + (.findInternedVar ^clojure.lang.Namespace + #?(:clj (find-ns nsym) :cljs (find-macros-ns nsym)) sym)) + + :else (let [nsym (gets env :ns :use-macros sym)] (if-not (nil? nsym) (.findInternedVar ^clojure.lang.Namespace diff --git a/src/test/cljs/cljs/clojure_alias_test.cljs b/src/test/cljs/cljs/clojure_alias_test.cljs index 7067be001..d929da3ad 100644 --- a/src/test/cljs/cljs/clojure_alias_test.cljs +++ b/src/test/cljs/cljs/clojure_alias_test.cljs @@ -1,10 +1,11 @@ (ns cljs.clojure-alias-test "Tests requiring via `clojure.*` instead of `cljs.*`" - (:require [clojure.test :refer [deftest is]] - [clojure.spec :as s :refer [spec?]])) + (:require [clojure.test :refer [deftest is] :rename {is is?}] + [clojure.spec :as s :refer [spec? spec] :rename {spec foo}])) (deftest normal-test - (is (= 1 1))) + (is? (= 1 1))) (deftest aliases-test - (is (= spec? clojure.spec/spec? cljs.spec/spec?))) + (is? (= spec? clojure.spec/spec? cljs.spec/spec?)) + (is? (foo number?))) diff --git a/src/test/cljs/cljs/ns_test.cljs b/src/test/cljs/cljs/ns_test.cljs index 95fc7e688..eb780f8e7 100644 --- a/src/test/cljs/cljs/ns_test.cljs +++ b/src/test/cljs/cljs/ns_test.cljs @@ -1,10 +1,11 @@ (ns cljs.ns-test - (:refer-clojure :exclude [+ for]) - (:require-macros [clojure.core :as lang] + (:refer-clojure :exclude [+ for] :rename {mapv core-mapv}) + (:require-macros [clojure.core :as lang :refer [when when-let] :rename {when always + when-let always-let}] [cljs.test :refer [deftest is]]) (:require [cljs.test] [cljs.ns-test.foo :refer [baz]] - [clojure.set :as s]) + [clojure.set :as s :refer [intersection] :rename {intersection itsc}]) (:use [cljs.ns-test.bar :only [quux]])) (def + -) @@ -18,3 +19,11 @@ (is (= (range 5) (lang/for [x (range 5)] x))) (is (= #{1 2 3} (s/union #{1} #{2 3})))) + +(deftest test-cljs-1508 + (is (= (itsc #{1 2 3} #{2}) #{2})) + (is (= #'itsc #'clojure.set/intersection)) + (is (= itsc clojure.set/intersection)) + (is (= (always true 42) 42)) + (is (= (core-mapv inc [1 2]) [2 3])) + (is (= (always-let [foo 42] foo) 42))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 9cb29a731..53d89aa0c 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -54,13 +54,13 @@ (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :plop]))) (catch Exception e (.getMessage e))) - "Only :as alias and :refer (names) options supported in :require")) + "Only :as alias, :refer (names) and :rename {from to} options supported in :require")) (is (.startsWith (try (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :plop true]))) (catch Exception e (.getMessage e))) - "Only :as and :refer options supported in :require / :require-macros")) + "Only :as, :refer and :rename options supported in :require / :require-macros")) (is (.startsWith (try (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :as boz :refer []]))) @@ -72,13 +72,55 @@ (a/analyze ns-env '(ns foo.bar (:refer-clojure :refer []))) (catch Exception e (.getMessage e))) - "Only [:refer-clojure :exclude (names)] form supported")) + "Only [:refer-clojure :exclude (names)] and optionally `:rename {from to}` specs supported")) + (is (.startsWith + (try + (a/analyze ns-env '(ns foo.bar (:refer-clojure :rename [1 2]))) + (catch Exception e + (.getMessage e))) + "Only [:refer-clojure :exclude (names)] and optionally `:rename {from to}` specs supported")) (is (.startsWith (try (a/analyze ns-env '(ns foo.bar (:use [baz.woz :exclude []]))) (catch Exception e (.getMessage e))) - "Only [lib.ns :only (names)] specs supported in :use / :use-macros")) + "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) + (is (.startsWith + (try + (a/analyze ns-env '(ns foo.bar (:use [baz.woz]))) + (catch Exception e + (.getMessage e))) + "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) + (is (.startsWith + (try + (a/analyze ns-env '(ns foo.bar (:use [baz.woz :only]))) + (catch Exception e + (.getMessage e))) + "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) + (is (.startsWith + (try + (a/analyze ns-env '(ns foo.bar (:use [baz.woz :only [1 2 3]]))) + (catch Exception e + (.getMessage e))) + "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) + (is (.startsWith + (try + (a/analyze ns-env '(ns foo.bar (:use [baz.woz :rename [1 2]]))) + (catch Exception e + (.getMessage e))) + "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) + (is (.startsWith + (try + (a/analyze ns-env '(ns foo.bar (:use [foo.bar :rename {baz qux}]))) + (catch Exception e + (.getMessage e))) + "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) + (is (.startsWith + (try + (a/analyze ns-env '(ns foo.bar (:use [baz.woz :only [foo] :only [bar]]))) + (catch Exception e + (.getMessage e))) + "Each of :only and :rename options may only be specified once in :use / :use-macros")) (is (.startsWith (try (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as []]))) @@ -91,6 +133,12 @@ (catch Exception e (.getMessage e))) ":as alias must be unique")) + (is (.startsWith + (try + (a/analyze ns-env '(ns foo.bar (:require [foo.bar :rename {baz qux}]))) + (catch Exception e + (.getMessage e))) + "Renamed symbol baz not referred")) (is (.startsWith (try (a/analyze ns-env '(ns foo.bar (:unless []))) @@ -355,3 +403,41 @@ (catch Exception e (.getMessage e))) "Can't set! a constant"))) + +(deftest test-cljs-1508-rename + (binding [a/*cljs-ns* a/*cljs-ns*] + (let [parsed-ns (e/with-compiler-env test-cenv + (a/analyze test-env + '(ns foo.core + (:require [clojure.set :as set :refer [intersection] :rename {intersection foo}]))))] + (is (nil? (-> parsed-ns :uses (get 'foo)))) + (is (nil? (-> parsed-ns :uses (get 'intersection)))) + (is (some? (-> parsed-ns :renames (get 'foo)))) + (is (= (-> parsed-ns :renames (get 'foo)) + 'clojure.set/intersection))) + (is (e/with-compiler-env test-cenv + (a/analyze test-env + '(ns foo.core + (:use [clojure.set :only [intersection] :rename {intersection foo}]))))) + (is (= (e/with-compiler-env (atom {::a/namespaces + {'foo.core {:renames '{foo clojure.set/intersection}}}}) + (a/resolve-var {:ns {:name 'foo.core}} 'foo)) + '{:name clojure.set/intersection + :ns clojure.set})) + (let [rwhen (e/with-compiler-env (atom (update-in @test-cenv [::a/namespaces] + merge {'foo.core {:rename-macros '{always cljs.core/when}}})) + (a/resolve-macro-var {:ns {:name 'foo.core}} 'always))] + (is (= (-> rwhen :name) + 'cljs.core/when))) + (let [parsed-ns (e/with-compiler-env test-cenv + (a/analyze test-env + '(ns foo.core + (:refer-clojure :rename {when always + map core-map}))))] + (is (= (-> parsed-ns :excludes) #{})) + (is (= (-> parsed-ns :rename-macros) '{always cljs.core/when})) + (is (= (-> parsed-ns :renames) '{core-map cljs.core/map}))) + (is (thrown? Exception (e/with-compiler-env test-cenv + (a/analyze test-env + '(ns foo.core + (:require [clojure.set :rename {intersection foo}])))))))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index a305bfda0..bf1658fa3 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -349,7 +349,7 @@ (deftest test-load-and-invoke-macros (async done - (let [l (latch 10 done)] + (let [l (latch 11 done)] ;; Normal require macros (let [st (cljs/empty-state)] (cljs/eval-str st @@ -542,6 +542,27 @@ (cljs/eval-str st "(add 110 210)" nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 320 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Rely on implicit macro inference for renames (ns loads its own macros) + (cljs/eval-str st + "(ns cljs.user (:require [foo.core :refer [add] :rename {add plus}]))" + nil + {:eval node-eval + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns foo.core (:require-macros foo.core))"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(plus 110 210)" + nil {:eval node-eval :context :expr} (fn [{:keys [error value]}] From 26bf43d2f90898d202f3adf51711f89eaa953c9a Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 9 Aug 2016 17:41:07 -0400 Subject: [PATCH 1947/4033] add :browser-repl to list of known opts --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8694dcbe5..8e9e796f1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -169,7 +169,8 @@ :optimizations :optimize-constants :output-dir :output-to :output-wrapper :parallel-build :preamble :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings - :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads}) + :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads + :browser-repl}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 From c6297c19b8f53c5784f908317621207a8330bff4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Aug 2016 13:53:59 -0400 Subject: [PATCH 1948/4033] remove the .iml from git just causes problems --- Clojurescript.iml | 42 +----------------------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/Clojurescript.iml b/Clojurescript.iml index 72a4f9995..19dbd15d4 100644 --- a/Clojurescript.iml +++ b/Clojurescript.iml @@ -1,46 +1,6 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 561940f154e72266a0e3ae0f557f29a63781b757 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Aug 2016 14:20:14 -0400 Subject: [PATCH 1949/4033] fix cljs.spec/every-kv bug --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 11ffeed23..f71468d99 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -234,7 +234,7 @@ See also - map-of" [kpred vpred & opts] - `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (key v#)) :gen-into {} ~@opts)) + `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (nth v# 0)) :into {} ~@opts)) (defmacro coll-of "Returns a spec for a collection of items satisfying pred. Unlike From 6b3277bf6c8a9bface7023b3882edcf6e6f7d360 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Aug 2016 14:20:44 -0400 Subject: [PATCH 1950/4033] copy over spec tests from Clojure --- src/test/cljs/cljs/spec_test.cljs | 146 +++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 4c755b888..f10e909c2 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -1,6 +1,6 @@ (ns cljs.spec-test (:require [cljs.spec :as s] - [cljs.test :as test :refer-macros [deftest is run-tests]])) + [cljs.test :as test :refer-macros [deftest is are run-tests]])) (s/def ::even? (s/and number? even?)) (s/def ::odd? (s/and number? odd?)) @@ -56,3 +56,147 @@ (deftest test-conform-unform (let [xs [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]] (is (= xs (s/unform s2 (s/conform s2 xs)))))) + +;; Copied from Clojure spec tests + +(def even-count? #(even? (count %))) + +(defn submap? + "Is m1 a subset of m2?" + [m1 m2] + (if (and (map? m1) (map? m2)) + (every? (fn [[k v]] (and (contains? m2 k) + (submap? v (get m2 k)))) + m1) + (= m1 m2))) + +(deftest conform-explain + (let [a (s/and #(> % 5) #(< % 10)) + o (s/or :s string? :k keyword?) + c (s/cat :a string? :b keyword?) + either (s/alt :a string? :b keyword?) + star (s/* keyword?) + plus (s/+ keyword?) + opt (s/? keyword?) + andre (s/& (s/* keyword?) even-count?) + m (s/map-of keyword? string?) + mkeys (s/map-of (s/and keyword? (s/conformer name)) any?) + mkeys2 (s/map-of (s/and keyword? (s/conformer name)) any? :conform-keys true) + s (s/coll-of (s/spec (s/cat :tag keyword? :val any?)) :kind list?) + v (s/coll-of keyword? :kind vector?) + coll (s/coll-of keyword?) + lrange (s/int-in 7 42) + ;drange (s/double-in :infinite? false :NaN? false :min 3.1 :max 3.2) + irange (s/inst-in #inst "1939" #inst "1946") + ] + (are [spec x conformed ed] + (let [co (s/conform spec x) + e (::s/problems (s/explain-data spec x))] + (when (not= conformed co) (println "conform fail\n\texpect=" conformed "\n\tactual=" co)) + (when (not (every? true? (map submap? ed e))) + (println "explain failures\n\texpect=" ed "\n\tactual failures=" e "\n\tsubmap?=" (map submap? ed e))) + (and (= conformed co) (every? true? (map submap? ed e)))) + + lrange 7 7 nil + lrange 8 8 nil + lrange 42 ::s/invalid [{:pred '(int-in-range? 7 42 %), :val 42}] + + irange #inst "1938" ::s/invalid [{:pred '(inst-in-range? #inst "1939-01-01T00:00:00.000-00:00" #inst "1946-01-01T00:00:00.000-00:00" %), :val #inst "1938"}] + irange #inst "1942" #inst "1942" nil + irange #inst "1946" ::s/invalid [{:pred '(inst-in-range? #inst "1939-01-01T00:00:00.000-00:00" #inst "1946-01-01T00:00:00.000-00:00" %), :val #inst "1946"}] + + ;drange 3.0 ::s/invalid [{:pred '(<= 3.1 %), :val 3.0}] + ;drange 3.1 3.1 nil + ;drange 3.2 3.2 nil + ;drange Double/POSITIVE_INFINITY ::s/invalid [{:pred '(not (isInfinite %)), :val Double/POSITIVE_INFINITY}] + ;; can't use equality-based test for Double/NaN + ;; drange Double/NaN ::s/invalid {[] {:pred '(not (isNaN %)), :val Double/NaN}} + + keyword? :k :k nil + keyword? nil ::s/invalid [{:pred ::s/unknown :val nil}] + keyword? "abc" ::s/invalid [{:pred ::s/unknown :val "abc"}] + + a 6 6 nil + a 3 ::s/invalid '[{:pred (> % 5), :val 3}] + a 20 ::s/invalid '[{:pred (< % 10), :val 20}] + ;a nil "java.lang.NullPointerException" "java.lang.NullPointerException" + ;a :k "java.lang.ClassCastException" "java.lang.ClassCastException" + + o "a" [:s "a"] nil + o :a [:k :a] nil + o 'a ::s/invalid '[{:pred string?, :val a, :path [:s]} {:pred keyword?, :val a :path [:k]}] + + c nil ::s/invalid '[{:reason "Insufficient input", :pred string?, :val (), :path [:a]}] + c [] ::s/invalid '[{:reason "Insufficient input", :pred string?, :val (), :path [:a]}] + c [:a] ::s/invalid '[{:pred string?, :val :a, :path [:a], :in [0]}] + c ["a"] ::s/invalid '[{:reason "Insufficient input", :pred keyword?, :val (), :path [:b]}] + c ["s" :k] '{:a "s" :b :k} nil + c ["s" :k 5] ::s/invalid '[{:reason "Extra input", :pred (cat :a string? :b keyword?), :val (5)}] + + ;; TODO: prevents termination for some reason - David + ;(s/cat) nil {} nil + ;(s/cat) [5] ::s/invalid '[{:reason "Extra input", :pred (cat), :val (5), :in [0]}] + + either nil ::s/invalid '[{:reason "Insufficient input", :pred (alt :a string? :b keyword?), :val () :via []}] + either [] ::s/invalid '[{:reason "Insufficient input", :pred (alt :a string? :b keyword?), :val () :via []}] + either [:k] [:b :k] nil + either ["s"] [:a "s"] nil + either [:b "s"] ::s/invalid '[{:reason "Extra input", :pred (alt :a string? :b keyword?), :val ("s") :via []}] + + star nil [] nil + star [] [] nil + star [:k] [:k] nil + star [:k1 :k2] [:k1 :k2] nil + star [:k1 :k2 "x"] ::s/invalid '[{:pred keyword?, :val "x" :via []}] + star ["a"] ::s/invalid '[{:pred keyword?, :val "a" :via []}] + + plus nil ::s/invalid '[{:reason "Insufficient input", :pred keyword?, :val () :via []}] + plus [] ::s/invalid '[{:reason "Insufficient input", :pred keyword?, :val () :via []}] + plus [:k] [:k] nil + plus [:k1 :k2] [:k1 :k2] nil + plus [:k1 :k2 "x"] ::s/invalid '[{:pred keyword?, :val "x", :in [2]}] + plus ["a"] ::s/invalid '[{:pred keyword?, :val "a" :via []}] + + opt nil nil nil + opt [] nil nil + opt :k ::s/invalid '[{:pred (? keyword?), :val :k}] + opt [:k] :k nil + opt [:k1 :k2] ::s/invalid '[{:reason "Extra input", :pred (? keyword?), :val (:k2)}] + opt [:k1 :k2 "x"] ::s/invalid '[{:reason "Extra input", :pred (? keyword?), :val (:k2 "x")}] + opt ["a"] ::s/invalid '[{:pred keyword?, :val "a"}] + + andre nil nil nil + andre [] nil nil + andre :k ::s/invalid '[{:pred (& (* keyword?) even-count?), :val :k}] + andre [:k] ::s/invalid '[{:pred even-count?, :val [:k]}] + andre [:j :k] [:j :k] nil + + m nil ::s/invalid '[{:pred map?, :val nil}] + m {} {} nil + m {:a "b"} {:a "b"} nil + + mkeys nil ::s/invalid '[{:pred map?, :val nil}] + mkeys {} {} nil + mkeys {:a 1 :b 2} {:a 1 :b 2} nil + + mkeys2 nil ::s/invalid '[{:pred map?, :val nil}] + mkeys2 {} {} nil + mkeys2 {:a 1 :b 2} {"a" 1 "b" 2} nil + + s '([:a 1] [:b "2"]) '({:tag :a :val 1} {:tag :b :val "2"}) nil + + v [:a :b] [:a :b] nil + v '(:a :b) ::s/invalid '[{:pred vector? :val (:a :b)}] + + coll nil ::s/invalid '[{:path [], :pred coll?, :val nil, :via [], :in []}] + coll [] [] nil + coll [:a] [:a] nil + coll [:a :b] [:a :b] nil + ;;coll [:a "b"] ::s/invalid '[{:pred (coll-checker keyword?), :val [:a b]}] + ))) + +(comment + + (run-tests) + + ) \ No newline at end of file From 73ad9745670f1f9dc63c47be157732d9ecd72301 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Aug 2016 14:23:24 -0400 Subject: [PATCH 1951/4033] add working (s/cat) test case --- src/test/cljs/cljs/spec_test.cljs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index f10e909c2..a05c387cc 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -133,8 +133,7 @@ c ["s" :k] '{:a "s" :b :k} nil c ["s" :k 5] ::s/invalid '[{:reason "Extra input", :pred (cat :a string? :b keyword?), :val (5)}] - ;; TODO: prevents termination for some reason - David - ;(s/cat) nil {} nil + (s/cat) nil {} nil ;(s/cat) [5] ::s/invalid '[{:reason "Extra input", :pred (cat), :val (5), :in [0]}] either nil ::s/invalid '[{:reason "Insufficient input", :pred (alt :a string? :b keyword?), :val () :via []}] From 72952bff161bed11545a45a96524fa4081cfcd4f Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Aug 2016 14:39:11 -0400 Subject: [PATCH 1952/4033] fix bug in op-describe for ::pcat case, tests now pass --- src/main/cljs/cljs/spec.cljs | 2 +- src/test/cljs/cljs/spec_test.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 180b868f0..818615b91 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -953,7 +953,7 @@ ::amp (list* 'clojure.spec/& (op-describe p1) forms) ::pcat (if rep+ (list `+ rep+) - (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) (c/or (seq forms) (repeat nil))))) + (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) forms))) ::alt (if maybe (list `? maybe) (cons `alt (mapcat vector ks forms))) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index a05c387cc..40e8453f3 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -134,7 +134,7 @@ c ["s" :k 5] ::s/invalid '[{:reason "Extra input", :pred (cat :a string? :b keyword?), :val (5)}] (s/cat) nil {} nil - ;(s/cat) [5] ::s/invalid '[{:reason "Extra input", :pred (cat), :val (5), :in [0]}] + (s/cat) [5] ::s/invalid '[{:reason "Extra input", :pred (cat), :val (5), :in [0]}] either nil ::s/invalid '[{:reason "Insufficient input", :pred (alt :a string? :b keyword?), :val () :via []}] either [] ::s/invalid '[{:reason "Insufficient input", :pred (alt :a string? :b keyword?), :val () :via []}] From 8c6bbf8bc9f5b3e04fd8b172e725894256076e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 10 Aug 2016 16:58:30 +0100 Subject: [PATCH 1953/4033] CLJS-1729: Support `use` special function in REPLs --- src/main/clojure/cljs/repl.cljc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 2311e1914..5a52358d8 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -705,6 +705,35 @@ (:require-macros ~@(-> specs canonicalize-specs decorate-specs))) {:merge true :line 1 :column 1}) identity opts))) + 'use + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (let [is-self-require? (self-require? specs) + [target-ns restore-ns] + (if-not is-self-require? + [ana/*cljs-ns* nil] + ['cljs.user ana/*cljs-ns*])] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~target-ns + (:use ~@(-> specs canonicalize-specs decorate-specs))) + {:merge true :line 1 :column 1}) + identity opts) + (when is-self-require? + (set! ana/*cljs-ns* restore-ns))))) + 'use-macros + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:use-macros ~@(-> specs canonicalize-specs decorate-specs))) + {:merge true :line 1 :column 1}) + identity opts))) 'import (fn self ([repl-env env form] From db896ff44c521a5323feb0de07f7ea9464b78215 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 9 Aug 2016 19:39:22 -0400 Subject: [PATCH 1954/4033] CLJS-1728: Update doc for ns for new :rename capability --- src/main/clojure/cljs/repl.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 5a52358d8..6b6d1fa68 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1041,11 +1041,13 @@ :doc "You must currently use the ns form only with the following caveats * You must use the :only form of :use - * :require supports :as and :refer - - both options can be skipped + * :require supports :as, :refer, and :rename + - all options can be skipped - in this case a symbol can be used as a libspec directly - that is, (:require lib.foo) and (:require [lib.foo]) are both supported and mean the same thing + - :rename specifies a map from referred var names to different + symbols (and can be used to prevent clashes) - prefix lists are not supported * The only option for :refer-clojure is :exclude * :import is available for importing Google Closure classes From 817b44d34795696eda473776e355fb956d3aa5ae Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Aug 2016 14:50:22 -0400 Subject: [PATCH 1955/4033] CLJS-1721: 3-arity get-in fails on types which do not implement ILookup --- src/main/cljs/cljs/core.cljs | 12 +++++------- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9bfc14718..6234cded8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4798,13 +4798,11 @@ reduces them without incurring seq initialization" (loop [sentinel lookup-sentinel m m ks (seq ks)] - (if ks - (if (not (satisfies? ILookup m)) - not-found - (let [m (get m (first ks) sentinel)] - (if (identical? sentinel m) - not-found - (recur sentinel m (next ks))))) + (if-not (nil? ks) + (let [m (get m (first ks) sentinel)] + (if (identical? sentinel m) + not-found + (recur sentinel m (next ks)))) m)))) (defn assoc-in diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index cb904ef6d..1a31df67d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3210,6 +3210,12 @@ (is (zero? (hash-string nil))) (is (not (zero? (hash-string "null"))))) +(deftest test-cljs-1721 + (is (= 1 (get-in {:a (array 1 2 3 4)} [:a 0] :not-found))) + (is (= :not-found (get-in {:a (array 1 2 3 4)} [:a 4] :not-found))) + (is (= "d" (get-in {:a "data"} [:a 0] :not-found))) + (is (= :not-found (get-in {:a "data"} [:a 4] :not-found)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From e8d14e690894aa57c4efa2d1fd425ed16c8a1553 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Aug 2016 15:12:12 -0400 Subject: [PATCH 1956/4033] CLJS-1640: Use the unshaded version of the closure compiler --- pom.template.xml | 2 +- project.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index b88173e8b..4b30181ad 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -29,7 +29,7 @@ com.google.javascript - closure-compiler + closure-compiler-unshaded v20160315 diff --git a/project.clj b/project.clj index 48913b044..de023372e 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.285"] [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] - [com.google.javascript/closure-compiler "v20160315"] + [com.google.javascript/closure-compiler-unshaded "v20160315"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} From 73e99298cc19569855de40eeb6ab1c21d25987b3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 7 May 2016 22:20:32 -0400 Subject: [PATCH 1957/4033] CLJS-1638: :elide-asserts disables atom validators in :advanced --- src/main/cljs/cljs/core.cljs | 3 ++- src/test/cljs/cljs/core_test.cljs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6234cded8..77994d217 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4244,7 +4244,8 @@ reduces them without incurring seq initialization" (if (instance? Atom a) (let [validate (.-validator a)] (when-not (nil? validate) - (assert (validate new-value) "Validator rejected reference state")) + (when-not (validate new-value) + (throw (js/Error. "Validator rejected reference state")))) (let [old-value (.-state a)] (set! (.-state a) new-value) (when-not (nil? (.-watches a)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 1a31df67d..39c6b9443 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -743,6 +743,7 @@ (let [a (atom [1] :validator coll? :meta {:a 1})] (testing "Testing atom validators" (is (= coll? (get-validator a))) + (is (thrown? js/Error (reset! a 1))) (is (= {:a 1} (meta a))) (alter-meta! a assoc :b 2) (is (= {:a 1 :b 2} (meta a))))) From c98e9a768f003f178b36d001a26cbfcd95fae3c6 Mon Sep 17 00:00:00 2001 From: Rohit Aggarwal Date: Fri, 3 Jun 2016 19:37:12 +0100 Subject: [PATCH 1958/4033] Use a js array to create collections in cljs.reader --- src/main/cljs/cljs/reader.cljs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index b9433e425..43dbbcd92 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -224,18 +224,22 @@ nil if the end of stream has been reached") (defn read-delimited-list [delim rdr recursive?] - (loop [a (transient [])] + (loop [a (array)] (let [ch (read-past whitespace? rdr)] (when-not ch (reader-error rdr "EOF while reading")) (if (identical? delim ch) - (persistent! a) + a (if-let [macrofn (macros ch)] (let [mret (macrofn rdr ch)] - (recur (if (identical? mret rdr) a (conj! a mret)))) + (recur (if (identical? mret rdr) a (do + (.push a mret) + a)))) (do (unread rdr ch) (let [o (read rdr true nil recursive?)] - (recur (if (identical? o rdr) a (conj! a o)))))))))) + (recur (if (identical? o rdr) a (do + (.push a o) + a)))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; data structure readers @@ -263,23 +267,27 @@ nil if the end of stream has been reached") (defn read-list [rdr _] - (apply list (read-delimited-list ")" rdr true))) + (let [arr (read-delimited-list ")" rdr true)] + (loop [i (alength arr) ^not-native r ()] + (if (> i 0) + (recur (dec i) (-conj r (aget arr (dec i)))) + r)))) (def read-comment skip-line) (defn read-vector [rdr _] - (read-delimited-list "]" rdr true)) + (vec (read-delimited-list "]" rdr true))) (defn read-map [rdr _] (let [l (read-delimited-list "}" rdr true) - c (count l)] + c (alength l)] (when (odd? c) (reader-error rdr "Map literal must contain an even number of forms")) (if (<= c (* 2 (.-HASHMAP-THRESHOLD PersistentArrayMap))) - (apply array-map l) - (apply hash-map l)))) + (.fromArray PersistentArrayMap l true true) + (.fromArray PersistentHashMap l true)))) (defn read-number [reader initch] @@ -398,7 +406,7 @@ nil if the end of stream has been reached") (defn read-set [rdr _] - (set (read-delimited-list "}" rdr true))) + (.fromArray PersistentHashSet (read-delimited-list "}" rdr true) true)) (defn read-regex [rdr ch] From 1589e5848ebb56ab451cb73f955dbc0b01e7aba0 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 14 Mar 2016 13:55:41 +0100 Subject: [PATCH 1959/4033] CLJS-1591 avoid analyzing invoke arguments multiple times It is safe to use the source form to figure out if an argument to an invoke needs to be assigned before being used. See CLJS-855 for more details on why the assignment is required. --- src/main/clojure/cljs/analyzer.cljc | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3e48d0642..4fc6c1df3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2459,7 +2459,7 @@ (defn- all-values? #?(:cljs {:tag boolean}) [exprs] - (every? #{:var :constant} (map :op exprs))) + (every? #(or (nil? %) (symbol? %) (string? %) (number? %) (true? %) (false? %)) exprs)) (defn- valid-arity? #?(:cljs {:tag boolean}) @@ -2488,19 +2488,19 @@ (warning :fn-deprecated env {:fexpr fexpr}))) (when-not (nil? (-> fexpr :info :type)) (warning :invoke-ctor env {:fexpr fexpr})) - (let [ana-expr #(analyze enve %) - argexprs (map ana-expr args)] - (if (or (not (boolean *cljs-static-fns*)) - (not (symbol? f)) - fn-var? - (analyzed? f) - (all-values? argexprs)) + (if (or (not (boolean *cljs-static-fns*)) + (not (symbol? f)) + fn-var? + (analyzed? f) + (all-values? args)) + (let [ana-expr #(analyze enve %) + argexprs (map ana-expr args)] {:env env :op :invoke :form form :f fexpr :args (vec argexprs) - :children (into [fexpr] argexprs)} - (let [arg-syms (take argc (repeatedly gensym))] - (analyze env - `(let [~@(vec (interleave arg-syms args))] - (~(vary-meta f assoc ::analyzed true) ~@arg-syms)))))))) + :children (into [fexpr] argexprs)}) + (let [arg-syms (take argc (repeatedly gensym))] + (analyze env + `(let [~@(vec (interleave arg-syms args))] + (~(vary-meta f assoc ::analyzed true) ~@arg-syms))))))) (defn parse-invoke [env form] From 273bcd33522b085fe98948598528fcf2c90a778c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 29 Jul 2016 15:14:36 +0100 Subject: [PATCH 1960/4033] CLJS-1607: bug with `specify!` in JS prototypes with `static-fns` true --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/clojure/cljs/compiler_tests.clj | 33 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index e376bea41..053105248 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -933,7 +933,7 @@ ;; ignore new type hints for now - David (and (not (set? tag)) (not ('#{any clj clj-or-nil clj-nil number string boolean function object array} tag)) - (when-let [ps (:protocols (ana/resolve-existing-var (dissoc env :locals) tag))] + (when-let [ps (:protocols (ana/resolve-existing-var env (symbol (name tag))))] (ps protocol))))))) opt-not? (and (= (:name info) 'cljs.core/not) (= (ana/infer-tag env (first (:args expr))) 'boolean)) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 9390a775d..77ae39281 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -147,6 +147,39 @@ `(cljs.user/foo ~(tags/->JSValue [])) '(cljs.user/foo (make-array 0)))))) +;; CLJS-1607 + +(deftest test-cljs-1607 + (let [define-Foo #(assoc-in % [::ana/namespaces 'cljs.user :defs 'Foo] + {:ns 'cljs.user + :name 'cljs.user/Foo + :protocol-symbol true + :protocol-info {:methods '{foo [[this]]}} + :protocol 'cljs.user/Foo}) + define-foo #(assoc-in % [::ana/namespaces 'cljs.user :defs 'foo] + {:ns 'cljs.user + :name 'cljs.user/foo + :fn-var true + :method-params '([x]) + :protocol 'cljs.user/Foo}) + aenv-with-foo (-> aenv define-foo define-Foo) + cenv-with-foo (-> @cenv define-foo define-Foo)] + (binding [ana/*cljs-static-fns* true] + (are [form] + (empty? + (capture-warnings + (env/with-compiler-env (atom cenv-with-foo) + (with-out-str + (comp/emit + (ana/analyze aenv-with-foo form)))))) + '(specify! [] + cljs.user/Foo + (cljs.user/foo [this] + :none) + Object + (bar [this] + (cljs.user/foo this))))))) + ;; CLJS-1225 (comment From f0b8531ab53ff2af0a47b122faac17feb1c36413 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 31 Jan 2016 18:11:15 -0500 Subject: [PATCH 1961/4033] CLJS-1556: Invalid code emit for obj literal Certain legal ClojureScript expresssions involving #js object literals end up causing invalid JavaScript to be emitted. In particular, this can occur if such an object literal is in statement context. It can also occur if simply evaluating an object literal in expression context by itself in a REPL. At the root of the problem is that the opening brace associated with an object literal should not be at the beginning of a JavaScript statement. Otherwise, the brace may be interpreted as the start of a JavaScript block. This patch fixes the issue by simply wrapping all emitted object literals in parens. --- src/main/clojure/cljs/compiler.cljc | 4 ++-- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 053105248..37b67cf01 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -406,13 +406,13 @@ (emit-wrap env (if (= js-type :object) (do - (emits "{") + (emits "({") (when-let [items (seq items)] (let [[[k v] & r] items] (emits "\"" (name k) "\": " v) (doseq [[k v] r] (emits ", \"" (name k) "\": " v)))) - (emits "}")) + (emits "})")) (emits "[" (comma-sep items) "]")))) (defmethod emit* :constant diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 39c6b9443..6b5859c9c 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2417,6 +2417,14 @@ (is (array? (aget #js {"foo" #js [1 2 3]} "foo"))) (is (= (seq (aget #js {"foo" #js [1 2 3]} "foo")) '(1 2 3))))) +(deftest test-1556 + (testing "Testing CLJS-1556, JS object literal code emission, beginning of statement" + ;; Really testing that this evaluates properly + (is (= 1 (do #js {:a 1} + 1))) + (is (= 1 (aget #js {:a 1} "a"))) + (is (= 1 (.-a #js {:a 1}))))) + (deftest test-725 (testing "Testing CLJS-725, drop" (is (= (apply vector (drop-while (partial = 1) [1 2 3])) [2 3])) From 429ca6bec270067d957b04f3549e5327b63df871 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 10 Aug 2016 17:41:21 -0400 Subject: [PATCH 1962/4033] add toString implementation to Vars --- src/main/cljs/cljs/core.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 77994d217..3afeb5664 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1008,6 +1008,8 @@ Object (isMacro [_] (. (val) -cljs$lang$macro)) + (toString [_] + (str "#'" sym)) IDeref (-deref [_] (val)) IMeta From eaba75c30c79785f457e7356b7539ac95741fa4b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 10 Aug 2016 17:00:54 -0400 Subject: [PATCH 1963/4033] CLJS-1731: Self-host: do_template problem with script/test-self-parity --- src/test/self/self_parity/test.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 1171a2c15..03f9fdd3d 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -164,8 +164,7 @@ 'cljs.pprint 'cljs.env.macros 'cljs.analyzer.macros - 'cljs.compiler.macros - 'clojure.template} + 'cljs.compiler.macros} #{'goog.object 'goog.string 'goog.string.StringBuffer @@ -173,7 +172,8 @@ 'cljs.core 'cljs.env 'cljs.pprint - 'cljs.tools.reader}) name)) + 'cljs.tools.reader + 'clojure.walk}) name)) ;; An atom to keep track of things we've already loaded (def loaded (atom #{})) From 20b08fe442a24c46526c812f6c52ad8c7d3f6c9b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 10 Aug 2016 22:10:25 -0400 Subject: [PATCH 1964/4033] CLJS-1720: Qualify symbols and namespaced keywords in spec macros There are currently a few symbols and namespaced keywords in the cljs.spec macros namespace that either need to be qualified for proper operation, or should be. The symbols fall into the category of calls to runtime functions in the cljs.spec namespace, and need qualification in order to avoid the $macros suffix. These comprise with-gen and gen. In terms of keywords, an example that causes a failure is ::kvs->map in keys*: It resolves to :cljs.spec$macros/kvs->map which doesn't match the :cljs.spec/kvs->map spec registered near the bottom of the cljs.spec runtime namespace. An example that doesn't cause an outright failure, but arguably inhibits its proper use by client code is ::nil and ::pred in the nilable macro. Ideally these would resolve to :cljs.spec/nil and :cljs.spec/pred so that client code can rely on these namespaced symbols identifying the branches. Given the nilable example, you could argue that the intent is that all namespaced keywords in the cljs.spec macro namespace that employ :: resolve in :cljs.spec so that they can be used not simply as unique identifiers (the intent is not simply to avoid potential collisions), but so that they can be used as stable identifiers. The set of keywords that should be qualified comprises: ::kind-form, ::kfn, ::conform-all, ::kvs->map, ::nil, and ::pred --- src/main/cljs/cljs/spec.cljc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index f71468d99..1880a6b17 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -223,7 +223,7 @@ See also - coll-of, every-kv " [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] - (let [nopts (-> opts (dissoc :gen) (assoc ::kind-form `'~(res &env (:kind opts))))] + (let [nopts (-> opts (dissoc :gen) (assoc :cljs.spec/kind-form `'~(res &env (:kind opts))))] `(cljs.spec/every-impl '~pred ~pred ~nopts ~gen))) (defmacro every-kv @@ -234,7 +234,7 @@ See also - map-of" [kpred vpred & opts] - `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (nth v# 0)) :into {} ~@opts)) + `(every (tuple ~kpred ~vpred) :cljs.spec/kfn (fn [i# v#] (nth v# 0)) :into {} ~@opts)) (defmacro coll-of "Returns a spec for a collection of items satisfying pred. Unlike @@ -248,7 +248,7 @@ See also - every, map-of" [pred & opts] - `(every ~pred ::conform-all true ~@opts)) + `(every ~pred :cljs.spec/conform-all true ~@opts)) (defmacro map-of "Returns a spec for a map whose keys satisfy kpred and vals satisfy @@ -261,7 +261,7 @@ See also - every-kv" [kpred vpred & opts] - `(every-kv ~kpred ~vpred ::conform-all true :kind map? ~@opts)) + `(every-kv ~kpred ~vpred :cljs.spec/conform-all true :kind map? ~@opts)) (defmacro * "Returns a regex op that matches zero or more values matching @@ -415,13 +415,13 @@ {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" [& kspecs] `(let [mspec# (keys ~@kspecs)] - (with-gen (cljs.spec/& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) - (fn [] (gen/fmap (fn [m#] (apply concat m#)) (gen mspec#)))))) + (cljs.spec/with-gen (cljs.spec/& (* (cat :cljs.spec/k keyword? :cljs.spec/v cljs.core/any?)) :cljs.spec/kvs->map mspec#) + (fn [] (gen/fmap (fn [m#] (apply concat m#)) (cljs.spec/gen mspec#)))))) (defmacro nilable "returns a spec that accepts nil and values satisfiying pred" [pred] - `(and (or ::nil nil? ::pred ~pred) (conformer second))) + `(and (or :cljs.spec/nil nil? :cljs.spec/pred ~pred) (conformer second))) (defmacro inst-in "Returns a spec that validates insts in the range from start @@ -466,4 +466,4 @@ fspec) f# ~sym] (for [args# (gen/sample (gen (:args fspec#)) ~n)] - [args# (apply f# args#)])))) \ No newline at end of file + [args# (apply f# args#)])))) From 60d534977ace15667c38cf2c2609e27ad72f539a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 10 Aug 2016 23:31:55 -0400 Subject: [PATCH 1965/4033] CLJS-1732: Add docstrings for new use and use-macros REPL specials --- src/main/clojure/cljs/repl.cljc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 6b6d1fa68..0d48af1d3 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1200,6 +1200,19 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) require-macros {:arglists ([& args]) :doc "Similar to the require REPL special function but only for macros."} + use {:arglists ([& args]) + :doc "Like require, but referring vars specified by the mandatory + :only option. + + Example: + + The following would load the library clojure.set while referring + the intersection var. + + (use '[clojure.set :only [intersection]])"} + use-macros {:arglists ([& args]) + :doc "Similar to the use REPL special function but + only for macros."} import {:arglists ([& import-symbols-or-lists]) :doc "import-list => (closure-namespace constructor-name-symbols*) From cc65ad3a87d9d3f6e51938bd3df8b9052e68d76a Mon Sep 17 00:00:00 2001 From: Andrea Richiardi Date: Wed, 10 Aug 2016 16:56:38 -0700 Subject: [PATCH 1966/4033] CLJS-1515: Self-host: Allow :file key in cljs.js/*load-fn* Bootstrapped ClojureScript is abstracted away from direct I/O by use of a *load-fn* callback. A result is that when a namespace is loaded, the :file attribute associated with def s in [:cljs.analyzer/namespaces 'foo.ns :defs] in the AST is nil, because cljs.analyzer/*cljs-file* cannot be set to a meaningful value. This ticket asks for an extension to *load-fn*, allowing a :file key to be optionally included by cljs.js clients, and for cljs.analyzer/*cljs-file* to be bound to that value in appropriate places in cljs.js so that the :file info appears in the AST. One rationale for this :file attribute is that it makes it easier for clients of cljs.js to look up the file for a def, say, for use when implementing a source REPL special, for example. --- src/main/cljs/cljs/js.cljs | 16 ++++++++++------ src/test/self/self_host/test.cljs | 24 +++++++++++++++++++++++- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index b02cd45a1..8df6eae50 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -71,6 +71,8 @@ :lang - the language, :clj or :js :source - the source of the library (a string) + :file - optional, the file path, it will be added to AST's :file keyword + (but not in :meta) :cache - optional, if a :clj namespace has been precompiled to :js, can give an analysis cache for faster loads. :source-map - optional, if a :clj namespace has been precompiled to :js, can @@ -246,9 +248,9 @@ (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") (if resource - (let [{:keys [lang source cache source-map]} resource] + (let [{:keys [lang source cache source-map file]} resource] (condp = lang - :clj (eval-str* bound-vars source name opts + :clj (eval-str* bound-vars source name (assoc opts :cljs-file file) (fn [res] (if (:error res) (cb res) @@ -379,9 +381,9 @@ (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") (if resource - (let [{:keys [name lang source]} resource] + (let [{:keys [name lang source file]} resource] (condp = lang - :clj (analyze-str* bound-vars source name opts + :clj (analyze-str* bound-vars source name (assoc opts :cljs-file file) (fn [res] (if-not (:error res) (analyze-deps bound-vars ana-env lib (next deps) opts cb) @@ -517,7 +519,8 @@ r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol - comp/*source-map-data* (:*sm-data* bound-vars)] + comp/*source-map-data* (:*sm-data* bound-vars) + ana/*cljs-file* (:cljs-file opts)] (let [res (try {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} (catch :default cause @@ -780,7 +783,8 @@ r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol - comp/*source-map-data* (:*sm-data* bound-vars)] + comp/*source-map-data* (:*sm-data* bound-vars) + ana/*cljs-file* (:cljs-file opts)] (let [res (try {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} (catch :default cause diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index bf1658fa3..fcec50f41 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -2,6 +2,7 @@ (:require [cljs.test :as test :refer-macros [run-tests deftest testing is async]] [cljs.js :as cljs] + [cljs.analyzer :as ana] [clojure.string :as string] [cljs.nodejs :as nodejs])) @@ -47,6 +48,27 @@ (defn elide-env [env ast opts] (dissoc ast :env)) +(defn var-ast + "Given an already derefed compiler state plus the symbols of a + namespace and a var (e.g. 'clojure.string and 'trim) , return the var + AST representation or nil if not found, probably because not required + yet. + + The 1-arity function does the splitting in case of a fully qualified + symbol (e.g. 'clojure.string/trim)." + ([st sym] + (var-ast st (symbol (namespace sym)) (symbol (name sym)))) + ([st ns-sym sym] + (get-in st [:cljs.analyzer/namespaces ns-sym :defs sym]))) + +(defn file->lang + "Converts a file path to a :lang keyword by inspecting the file + extension." + [file-path] + (if (string/ends-with? file-path ".js") + :js + :clj)) + (defn str-evals-to "Checks that a string evaluates to an expected value." ([st l expected s] @@ -750,7 +772,7 @@ (str-evals-to st l [1 1 1 1 1] "(let [an-array (int-array 5 0)] (js->clj (amap an-array idx ret (+ 1 (aget an-array idx)))))" {:ns 'foo.core}))))) -#_(deftest test-eval-str-with-require +(deftest test-eval-str-with-require (async done (let [l (latch 3 done)] (cljs/eval-str st From b04b7c6efcb861e948e0c6ffdf5529c13c09e141 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 15 Jul 2016 10:32:08 -0400 Subject: [PATCH 1967/4033] CLJS-1707: Self-host: with-instrument-disabled needs to qualify *instrument-enabled* --- src/main/cljs/cljs/spec/test.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 7921cc42a..fe5e6b47c 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -43,7 +43,7 @@ returns the set of all symbols naming vars in those nses." (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." [& body] - `(binding [*instrument-enabled* nil] + `(binding [cljs.spec.test/*instrument-enabled* nil] ~@body)) (defmacro instrument-1 From 39f6d7bae0919fe54e847db2fdcfd621cc6f930d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 10 Aug 2016 21:25:21 +0100 Subject: [PATCH 1968/4033] CLJS-1730: Support `refer-clojure` special function in REPLs --- src/main/clojure/cljs/repl.cljc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 0d48af1d3..fbba35a9b 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -751,6 +751,17 @@ specs))) {:merge true :line 1 :column 1}) identity opts))) + 'refer-clojure + (fn self + ([repl-env env form] + (self repl-env env form nil)) + ([repl-env env [_ & specs :as form] opts] + (evaluate-form repl-env env "" + (with-meta + `(~'ns ~ana/*cljs-ns* + (:refer-clojure ~@specs)) + {:merge true :line 1 :column 1}) + identity opts))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace From 85bab0736aae442d55d7f25ffc502b5195afe5c1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 27 Jul 2016 09:29:41 -0400 Subject: [PATCH 1969/4033] CLJS-1708: Self-host: [iu]nstrument-1 needs to qualify [iu]nstrument-1* --- src/main/cljs/cljs/spec/test.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index fe5e6b47c..4b65f1ce4 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -51,7 +51,7 @@ returns the set of all symbols naming vars in those nses." (let [v (ana-api/resolve &env s)] (when v (swap! instrumented-vars conj (:name v)) - `(let [checked# (instrument-1* ~s (var ~s) ~opts)] + `(let [checked# (cljs.spec.test/instrument-1* ~s (var ~s) ~opts)] (when checked# (set! ~s checked#)) '~(:name v))))) @@ -60,7 +60,7 @@ returns the set of all symbols naming vars in those nses." (let [v (ana-api/resolve &env s)] (when v (swap! instrumented-vars disj (:name v)) - `(let [raw# (unstrument-1* ~s (var ~s))] + `(let [raw# (cljs.spec.test/unstrument-1* ~s (var ~s))] (when raw# (set! ~s raw#)) '~(:name v))))) From 5b34a6d9fab1ff77fe3aee24413ad0783e015e9f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 11 Aug 2016 13:30:48 -0400 Subject: [PATCH 1970/4033] CLJS-1736: cljs.spec.test: checkable-syms* called with 0-arity --- src/main/cljs/cljs/spec/test.cljc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 4b65f1ce4..fefe43452 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -181,10 +181,13 @@ Returns a collection of syms naming the vars unstrumented." (validate-check-opts opts#) (check-1 nil ~f ~spec opts#)))) -(defn checkable-syms* [opts] - (reduce into #{} - [(filter fn-spec-name? (keys @s/registry-ref)) - (keys (:spec opts))])) +(defn checkable-syms* + ([] + (checkable-syms* nil)) + ([opts] + (reduce into #{} + [(filter fn-spec-name? (keys @s/registry-ref)) + (keys (:spec opts))]))) (defmacro checkable-syms "Given an opts map as per check, returns the set of syms that From afcbae8a768cf432a8786c8be462f68daceab32c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 11 Aug 2016 10:16:24 -0400 Subject: [PATCH 1971/4033] CLJS-1735: Self-host: cljs.spec speced-vars instance --- src/main/cljs/cljs/spec/test.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index fefe43452..ac6ae5ffe 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -102,7 +102,8 @@ invokes the fn you provide, enabling arbitrary stubbing and mocking. Returns a collection of syms naming the vars instrumented." ([] - `(instrument '[~@(s/speced-vars)])) + `(instrument '[~@(#?(:clj s/speced-vars + :cljs cljs.spec$macros/speced-vars))])) ([xs] `(instrument ~xs nil)) ([sym-or-syms opts] From 9880be64c6ce1e8230b2bf0d663224c4c747e1b3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Aug 2016 12:50:53 -0400 Subject: [PATCH 1972/4033] formatting --- src/main/clojure/cljs/analyzer.cljc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 4fc6c1df3..642bb006f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1799,7 +1799,7 @@ (defn check-use-macros-inferring-missing [ast name use-macros missing-uses env] - (let [remove-missing-uses #(apply dissoc % (keys missing-uses)) + (let [remove-missing-uses #(apply dissoc % (keys missing-uses)) missing-renames (missing-renames (:renames ast)) missing-rename-macros (inferred-rename-macros missing-renames env) remove-missing-renames #(apply dissoc % (keys missing-renames)) @@ -1809,11 +1809,12 @@ (update-in [:uses] remove-missing-uses) (update-in [:rename-macros] merge missing-rename-macros) (update-in [:renames] remove-missing-renames))] - (swap! env/*compiler* #(-> % - (update-in [::namespaces name :use-macros] merge (:use-macros ast')) - (update-in [::namespaces name :uses] remove-missing-uses) - (update-in [::namespaces name :rename-macros] merge (:rename-macros ast')) - (update-in [::namespaces name :renames] remove-missing-renames))) + (swap! env/*compiler* + #(-> % + (update-in [::namespaces name :use-macros] merge (:use-macros ast')) + (update-in [::namespaces name :uses] remove-missing-uses) + (update-in [::namespaces name :rename-macros] merge (:rename-macros ast')) + (update-in [::namespaces name :renames] remove-missing-renames))) ast')) (defn parse-ns-error-msg [spec msg] From a0648030adc43a0b393dd4441b2a6ae793f0c8a6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Aug 2016 12:54:13 -0400 Subject: [PATCH 1973/4033] code comment --- src/main/clojure/cljs/analyzer.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 642bb006f..60c65436d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1809,6 +1809,8 @@ (update-in [:uses] remove-missing-uses) (update-in [:rename-macros] merge missing-rename-macros) (update-in [:renames] remove-missing-renames))] + ;; we also need to side-effect the compilation environment + ;; the returned AST isn't actually used directly (swap! env/*compiler* #(-> % (update-in [::namespaces name :use-macros] merge (:use-macros ast')) From d197bcb2bef327e00bcb39346258ed314a69b8d9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Aug 2016 14:39:42 -0400 Subject: [PATCH 1974/4033] refactor macro inference passes --- src/main/clojure/cljs/analyzer.cljc | 99 +++++++++++++++++------------ 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 60c65436d..aae47f5ef 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1757,22 +1757,28 @@ the-ns #?(:clj (find-ns lib) :cljs (find-macros-ns lib))] (or (nil? the-ns) (nil? (.findInternedVar ^clojure.lang.Namespace the-ns sym))))) -(defn missing-uses [uses env] +;; returns (s/map-of symbol? symbol?) +(defn missing-uses + [uses env] (let [cenv @env/*compiler*] (into {} (filter (fn [[sym lib]] (missing-use? lib sym cenv)) uses)))) -(defn missing-renames [renames] +;; returns (s/map-of symbol? qualified-symbol?) +(defn missing-renames [renames env] (let [cenv @env/*compiler*] (into {} (filter (fn [[_ qualified-sym]] (missing-rename? qualified-sym cenv)) renames)))) +;; returns (s/map-of symbol? symbol?) (defn missing-use-macros [use-macros env] (let [cenv @env/*compiler*] (into {} (filter (fn [[sym lib]] (missing-use-macro? lib sym)) use-macros)))) +;; returns (s/map-of symbol? symbol?) (defn inferred-use-macros [use-macros env] (let [cenv @env/*compiler*] (into {} (filter (fn [[sym lib]] (not (missing-use-macro? lib sym))) use-macros)))) +;; returns (s/map-of symbol? symbol?) (defn inferred-rename-macros [rename-macros env] (into {} (filter (fn [[_ qualified-sym]] (not (missing-rename-macro? qualified-sym))) rename-macros))) @@ -1798,23 +1804,31 @@ (inferred-use-macros missing-uses env)))) (defn check-use-macros-inferring-missing - [ast name use-macros missing-uses env] - (let [remove-missing-uses #(apply dissoc % (keys missing-uses)) - missing-renames (missing-renames (:renames ast)) - missing-rename-macros (inferred-rename-macros missing-renames env) - remove-missing-renames #(apply dissoc % (keys missing-renames)) + [{:keys [name uses use-macros] :as ast} env] + (let [missing-uses (when (and *analyze-deps* (seq uses)) + (missing-uses uses env)) + remove-missing-uses #(apply dissoc % (keys missing-uses)) ast' (-> ast (update-in [:use-macros] merge (check-use-macros use-macros missing-uses env)) - (update-in [:uses] remove-missing-uses) + (update-in [:uses] remove-missing-uses))] + (swap! env/*compiler* + #(-> % + (update-in [::namespaces name :use-macros] merge (:use-macros ast')) + (update-in [::namespaces name :uses] remove-missing-uses))) + ast')) + +(defn check-rename-macros-inferring-missing + [{:keys [name renames] :as ast} env] + (let [missing-renames (when (and *analyze-deps* (seq renames)) + (missing-renames (:renames ast) env)) + missing-rename-macros (inferred-rename-macros missing-renames env) + remove-missing-renames #(apply dissoc % (keys missing-renames)) + ast' (-> ast (update-in [:rename-macros] merge missing-rename-macros) (update-in [:renames] remove-missing-renames))] - ;; we also need to side-effect the compilation environment - ;; the returned AST isn't actually used directly (swap! env/*compiler* #(-> % - (update-in [::namespaces name :use-macros] merge (:use-macros ast')) - (update-in [::namespaces name :uses] remove-missing-uses) (update-in [::namespaces name :rename-macros] merge (:rename-macros ast')) (update-in [::namespaces name :renames] remove-missing-renames))) ast')) @@ -2773,35 +2787,38 @@ (let [{:keys [name deps uses require-macros use-macros reload reloads]} ast] (when (and *analyze-deps* (seq deps)) (analyze-deps name deps env (dissoc opts :macros-ns))) - (let [missing (when (and *analyze-deps* (seq uses)) - (missing-uses uses env))] - (if *load-macros* - (do - (load-core) - (doseq [nsym (vals use-macros)] - (let [k (or (:use-macros reload) - (get-in reloads [:use-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (locking load-mutex - (clojure.core/require nsym k)) - (locking load-mutex - (clojure.core/require nsym))) - (intern-macros nsym k))) - (doseq [nsym (vals require-macros)] - (let [k (or (:require-macros reload) - (get-in reloads [:require-macros nsym]) - (and (= nsym name) *reload-macros* :reload))] - (if k - (locking load-mutex - (clojure.core/require nsym k)) - (locking load-mutex - (clojure.core/require nsym))) - (intern-macros nsym k))) - (check-use-macros-inferring-missing ast name use-macros missing env)) - (do - (check-uses missing env) - ast)))) + (if *load-macros* + (do + (load-core) + (doseq [nsym (vals use-macros)] + (let [k (or (:use-macros reload) + (get-in reloads [:use-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (locking load-mutex + (clojure.core/require nsym k)) + (locking load-mutex + (clojure.core/require nsym))) + (intern-macros nsym k))) + (doseq [nsym (vals require-macros)] + (let [k (or (:require-macros reload) + (get-in reloads [:require-macros nsym]) + (and (= nsym name) *reload-macros* :reload))] + (if k + (locking load-mutex + (clojure.core/require nsym k)) + (locking load-mutex + (clojure.core/require nsym))) + (intern-macros nsym k))) + (-> ast + (check-use-macros-inferring-missing env) + (check-rename-macros-inferring-missing env))) + (do + (check-uses + (when (and *analyze-deps* (seq uses)) + (missing-uses uses env)) + env) + ast))) ast))) (def ^:dynamic *passes* nil) From b49c19819f46a78e41a1e3c9147b1127a006664d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Aug 2016 15:04:31 -0400 Subject: [PATCH 1975/4033] macro inference also needs to check if uses that exist are *also* macros, partial solution to CLJS-1733 --- src/main/clojure/cljs/analyzer.cljc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index aae47f5ef..4ba357185 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1805,12 +1805,15 @@ (defn check-use-macros-inferring-missing [{:keys [name uses use-macros] :as ast} env] - (let [missing-uses (when (and *analyze-deps* (seq uses)) - (missing-uses uses env)) + (let [missing-uses (when (and *analyze-deps* (seq uses)) + (missing-uses uses env)) + maybe-macros (apply dissoc uses (keys missing-uses)) remove-missing-uses #(apply dissoc % (keys missing-uses)) ast' (-> ast - (update-in [:use-macros] merge - (check-use-macros use-macros missing-uses env)) + (update-in [:use-macros] + #(-> % + (merge (check-use-macros use-macros missing-uses env)) + (merge (inferred-use-macros maybe-macros env)))) (update-in [:uses] remove-missing-uses))] (swap! env/*compiler* #(-> % From d77bc7164bbcd565b794cbb321d567082d58d269 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Aug 2016 15:27:14 -0400 Subject: [PATCH 1976/4033] CLJS-1733: Macro inference issue for macros & runtime vars with the same name :rename pass must also check that found renames may also be macros --- src/main/clojure/cljs/analyzer.cljc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 4ba357185..592da80aa 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1824,11 +1824,15 @@ (defn check-rename-macros-inferring-missing [{:keys [name renames] :as ast} env] (let [missing-renames (when (and *analyze-deps* (seq renames)) - (missing-renames (:renames ast) env)) + (missing-renames renames env)) + maybe-macros (apply dissoc renames (keys missing-renames)) missing-rename-macros (inferred-rename-macros missing-renames env) remove-missing-renames #(apply dissoc % (keys missing-renames)) ast' (-> ast - (update-in [:rename-macros] merge missing-rename-macros) + (update-in [:rename-macros] + #(-> % + (merge missing-rename-macros) + (merge (inferred-rename-macros maybe-macros env)))) (update-in [:renames] remove-missing-renames))] (swap! env/*compiler* #(-> % From bb2dab152cdd234d802b46611a59bf5e49a8ff59 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Aug 2016 15:37:10 -0400 Subject: [PATCH 1977/4033] 1.9.198 --- README.md | 6 +++--- changes.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index da7045c7d..846729f89 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.9.89 +Latest stable release: 1.9.198 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.89"] +[org.clojure/clojurescript "1.9.198"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.9.89 org.clojure clojurescript - 1.9.89 + 1.9.198 ``` diff --git a/changes.md b/changes.md index 96730547b..33e14d062 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,56 @@ +## 1.9.198 + +### Enhancements +* CLJS-1508: Extend ns form to support :rename option +* CLJS-1507: Implicit macro loading: macro var inference in :refer +* CLJS-1692: Autoalias clojure.* to exisiting cljs.* namespaces if +possible +* CLJS-1350: Compiler support for browser REPL +* CLJS-1729: Support `use` special function in REPLs +* CLJS-1730: Support `refer-clojure` special function in REPLs + +### Changes +* CLJS-1515: Self-host: Allow :file key in cljs.js/*load-fn* +* add toString implementation to Vars +* Use a js array to create collections in cljs.reader +* CLJS-1640: Use the unshaded version of the closure compiler +* add :browser-repl to list of known opts +* add browser REPL preload +* parity with Clojure 1.9.0-alpha10 clojure.spec +* bump to tools.reader 1.0.0-beta3 + +### Fixes +* CLJS-1733: Macro inference issue for macros & runtime vars with the same name +* CLJS-1735: Self-host: cljs.spec speced-vars instance +* CLJS-1736: cljs.spec.test: checkable-syms* called with 0-arity +* CLJS-1708: Self-host: [iu]nstrument-1 needs to qualify [iu]nstrument-1* +* CLJS-1707: Self-host: with-instrument-disabled needs to qualify *instrument-enabled* +* CLJS-1732: Add docstrings for new use and use-macros REPL specials +* CLJS-1720: Qualify symbols and namespaced keywords in spec macros +* CLJS-1731: Self-host: do_template problem with script/test-self-parity +* CLJS-1556: Invalid code emit for obj literal +* CLJS-1607: bug with `specify!` in JS prototypes with `static-fns` true +* CLJS-1591 avoid analyzing invoke arguments multiple times +* CLJS-1638: :elide-asserts disables atom validators in :advanced +* CLJS-1721: 3-arity get-in fails on types which do not implement ILookup +* CLJS-1728: Update doc for ns for new :rename capability +* CLJS-1727: Regression when evaluating non-sequential forms at the REPL +* CLJS-1490: Watch macro files in cljs.build.api/watch +* CLJS-1719: Port destructuring namespaced keys and symbols +* CLJS-1653: cljs.spec: keys* causes exception +* CLJS-1700: Support clojure.* aliasing when not in vector +* CLJS-1717 remove map from equiv-map +* CLJS-1716: No longer possible to use same alias for :require-macros and :require +* Use keyword options in js->clj 1-arg impl +* Add support for regex in transit for compiler analysis cache +* Escape non-Latin1 characters before base64 encoding the source-map string +* CLJS-1698: cljs.spec: every res call needs &env +* CLJS-1695: Self-host: Port cljs / clojure namespace aliasing +* CLJS-1697: doc on inferred macros fails +* CLJS-1699: Update docstring for ns +* CLJS-1694: Self-host: Port macro var inference in :refer + + ## 1.9.89 ### Enhancements From 5d30d79b9e1fc3394f02de66504b496933458825 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 12 Aug 2016 15:52:55 -0400 Subject: [PATCH 1978/4033] CLJS-1738: Self-host: need to update call to check-use-macros-inferring-missing --- src/main/cljs/cljs/js.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 8df6eae50..4a8253466 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -476,7 +476,9 @@ (if (:error res) (cb res) (try - (let [ast' (ana/check-use-macros-inferring-missing rewritten-ast (:name ast) use-macros missing env)] + (let [ast' (-> rewritten-ast + (ana/check-use-macros-inferring-missing env) + (ana/check-rename-macros-inferring-missing env))] (cb {:value ast'})) (catch :default cause (cb (wrap-error From 7fe544696ca7dd45785980097f6033f9274e433b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 12 Aug 2016 19:25:50 -0400 Subject: [PATCH 1979/4033] CLJS-1739: seq on map literal with 9 elements leads to rest producing nil --- src/main/cljs/cljs/core.cljs | 3 ++- src/test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3afeb5664..1b927eaac 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7032,7 +7032,8 @@ reduces them without incurring seq initialization" (if-let [node-seq (.inode-seq node)] (NodeSeq. nil nodes (+ j 2) node-seq nil) (recur (+ j 2))) - (recur (+ j 2))))))) + (recur (+ j 2)))) + ()))) (NodeSeq. nil nodes i s nil)))) (deftype ArrayNodeSeq [meta nodes i s ^:mutable __hash] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 6b5859c9c..eaf3cfa8e 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3225,6 +3225,11 @@ (is (= "d" (get-in {:a "data"} [:a 0] :not-found))) (is (= :not-found (get-in {:a "data"} [:a 4] :not-found)))) +(deftest test-cljs-1739 + (is (= (-> {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9} + rest rest rest rest rest rest rest rest rest) + ()))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 4218670adc595fa0e654061a768189963eb09484 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 12 Aug 2016 21:46:43 -0400 Subject: [PATCH 1980/4033] fix bad res call in cljs.spec/merge macro --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 1880a6b17..a997d2e3b 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -449,7 +449,7 @@ predicates. Unlike 'and', merge can generate maps satisfying the union of the predicates." [& pred-forms] - `(cljs.spec/merge-spec-impl '~(mapv res pred-forms) ~(vec pred-forms) nil)) + `(cljs.spec/merge-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) (defmacro exercise-fn "exercises the fn named by sym (a symbol) by applying it to From 8a49ce62bb5f53167bc44d75ada31156bcec38b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 13 Aug 2016 14:31:37 +0100 Subject: [PATCH 1981/4033] CLJS-1737: Self-host: clojure alias implicit macro use regression When checking for clojure.* -> cljs.* cases in `get-expander-ns`, we want NS resolution to return nil if it can't resolve either the macros NS or the NS alias. `resolve-macro-ns-alias` and `resolve-ns-alias` need to be tweaked to return nil in this case. --- src/main/clojure/cljs/analyzer.cljc | 24 ++++++++++++++-------- src/test/cljs/cljs/clojure_alias_test.cljs | 4 ++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 592da80aa..5aab2816b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -690,13 +690,19 @@ (fn [env prefix suffix] (throw (error env (str "Unable to resolve var: " suffix " in this context"))))))) -(defn resolve-ns-alias [env name] - (let [sym (symbol name)] - (get (:requires (:ns env)) sym sym))) - -(defn resolve-macro-ns-alias [env name] - (let [sym (symbol name)] - (get (:require-macros (:ns env)) sym sym))) +(defn resolve-ns-alias + ([env name] + (resolve-ns-alias env name (symbol name))) + ([env name default] + (let [sym (symbol name)] + (get (:requires (:ns env)) sym default)))) + +(defn resolve-macro-ns-alias + ([env name] + (resolve-macro-ns-alias env name (symbol name))) + ([env name default] + (let [sym (symbol name)] + (get (:require-macros (:ns env)) sym default)))) (defn confirm-ns "Given env, an analysis environment, and ns-sym, a symbol identifying a @@ -2574,8 +2580,8 @@ (defn get-expander-ns [env ^String nstr] ;; first check for clojure.* -> cljs.* cases - (let [res (or (resolve-macro-ns-alias env (symbol nstr)) - (resolve-ns-alias env (symbol nstr))) + (let [res (or (resolve-macro-ns-alias env nstr nil) + (resolve-ns-alias env nstr nil)) nstr (if res (str res) nstr)] (cond #?@(:clj [(= "clojure.core" nstr) (find-ns 'cljs.core)] diff --git a/src/test/cljs/cljs/clojure_alias_test.cljs b/src/test/cljs/cljs/clojure_alias_test.cljs index d929da3ad..e2381f78d 100644 --- a/src/test/cljs/cljs/clojure_alias_test.cljs +++ b/src/test/cljs/cljs/clojure_alias_test.cljs @@ -9,3 +9,7 @@ (deftest aliases-test (is? (= spec? clojure.spec/spec? cljs.spec/spec?)) (is? (foo number?))) + +(deftest use-macros + (s/def ::even? (s/and number? even?)) + (is? (s/valid? ::even? 2))) From 0b8fd1271a13ba4f072f85c0f408dd4dfb174bf5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 12 Aug 2016 21:45:39 -0400 Subject: [PATCH 1982/4033] CLJS-1741: Add :rename to :refer-clojure in ns docstring --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index fbba35a9b..c75cd6a06 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1060,7 +1060,7 @@ - :rename specifies a map from referred var names to different symbols (and can be used to prevent clashes) - prefix lists are not supported - * The only option for :refer-clojure is :exclude + * The only options for :refer-clojure are :exclude and :rename * :import is available for importing Google Closure classes - ClojureScript types and records should be brought in with :use or :require :refer, not :import ed From 252f3893c043b25515897482c64e715f31c65765 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 13 Aug 2016 11:13:28 -0400 Subject: [PATCH 1983/4033] Remove .iml --- Clojurescript.iml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Clojurescript.iml diff --git a/Clojurescript.iml b/Clojurescript.iml deleted file mode 100644 index 19dbd15d4..000000000 --- a/Clojurescript.iml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 712a8d8f1e69b4f440088ce21d32f63a2e363988 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 12 Aug 2016 21:02:36 -0400 Subject: [PATCH 1984/4033] CLJS-1740: Self-host: Need to port more of CLJS-1733 This ports more of the changes for CLJS-1733, covering 2 items: 1. Move the missing that is calculated early in the let to be calculated later at its use site (agument to ana/check-uses). 2. Bind ana/*analyze-deps* when calling check-use-macros-inferring-missing and check-rename-macros-inferring-missing as this dynamic var is consulted inside those fns. --- src/main/cljs/cljs/js.cljs | 84 +++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 4a8253466..9768ba527 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -449,49 +449,51 @@ {:keys [uses requires require-macros use-macros reload reloads]} rewritten-ast] (if (:error res) (cb res) - (let [missing (when (and (:*analyze-deps* bound-vars) (seq uses)) - (ana/missing-uses uses env))] - (if (:*load-macros* bound-vars) - (do - (when (:verbose opts) (debug-prn "Processing :use-macros for" (:name ast))) - (load-macros bound-vars :use-macros use-macros reload reloads opts - (fn [res] - (if (:error res) - (cb res) - (do - (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) - (load-macros bound-vars :require-macros require-macros reloads reloads opts - (fn [res] - (if (:error res) - (cb res) - (let [res (try - (when (seq use-macros) - (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) - (ana/check-use-macros use-macros env)) - {:value nil} - (catch :default cause - (wrap-error - (ana/error ana-env - (str "Could not parse ns form " (:name ast)) cause))))] - (if (:error res) - (cb res) - (try - (let [ast' (-> rewritten-ast + (if (:*load-macros* bound-vars) + (do + (when (:verbose opts) (debug-prn "Processing :use-macros for" (:name ast))) + (load-macros bound-vars :use-macros use-macros reload reloads opts + (fn [res] + (if (:error res) + (cb res) + (do + (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) + (load-macros bound-vars :require-macros require-macros reloads reloads opts + (fn [res] + (if (:error res) + (cb res) + (let [res (try + (when (seq use-macros) + (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) + (ana/check-use-macros use-macros env)) + {:value nil} + (catch :default cause + (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause))))] + (if (:error res) + (cb res) + (try + (binding [ana/*analyze-deps* (:*analyze-deps* bound-vars)] + (let [ast' (-> rewritten-ast (ana/check-use-macros-inferring-missing env) (ana/check-rename-macros-inferring-missing env))] - (cb {:value ast'})) - (catch :default cause - (cb (wrap-error - (ana/error ana-env - (str "Could not parse ns form " (:name ast)) cause))))))))))))))) - (try - (when (:verbose opts) (debug-prn "Checking uses")) - (ana/check-uses missing env) - (cb {:value ast}) - (catch :default cause - (cb (wrap-error - (ana/error ana-env - (str "Could not parse ns form " (:name ast)) cause))))))))))] + (cb {:value ast'}))) + (catch :default cause + (cb (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause))))))))))))))) + (try + (when (:verbose opts) (debug-prn "Checking uses")) + (ana/check-uses + (when (and (:*analyze-deps* bound-vars) (seq uses)) + (ana/missing-uses uses env)) + env) + (cb {:value ast}) + (catch :default cause + (cb (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause)))))))))] (cond (and load (seq (:deps ast))) (load-deps bound-vars ana-env (:name ast) (:deps ast) (dissoc opts :macros-ns) From 709cc57d003590945329ea8745b799e75ac4ba88 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 14 Aug 2016 09:24:37 -0400 Subject: [PATCH 1985/4033] CLJS-1744: rest produces nil for larger maps remove rest logic from create-inod-seq, add rest logic where it belongs in both NodeSeq & ArrayNodeSeq --- src/main/cljs/cljs/core.cljs | 14 ++++++++------ src/test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1b927eaac..f5a5aa40b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6999,9 +6999,10 @@ reduces them without incurring seq initialization" (first s))) (-rest [coll] - (if (nil? s) - (create-inode-seq nodes (+ i 2) nil) - (create-inode-seq nodes i (next s)))) + (let [ret (if (nil? s) + (create-inode-seq nodes (+ i 2) nil) + (create-inode-seq nodes i (next s)))] + (if-not (nil? ret) ret ()))) ISeqable (-seq [this] this) @@ -7032,8 +7033,7 @@ reduces them without incurring seq initialization" (if-let [node-seq (.inode-seq node)] (NodeSeq. nil nodes (+ j 2) node-seq nil) (recur (+ j 2))) - (recur (+ j 2)))) - ()))) + (recur (+ j 2))))))) (NodeSeq. nil nodes i s nil)))) (deftype ArrayNodeSeq [meta nodes i s ^:mutable __hash] @@ -7066,7 +7066,9 @@ reduces them without incurring seq initialization" ISequential ISeq (-first [coll] (first s)) - (-rest [coll] (create-array-node-seq nil nodes i (next s))) + (-rest [coll] + (let [ret (create-array-node-seq nil nodes i (next s))] + (if-not (nil? ret) ret ()))) ISeqable (-seq [this] this) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index eaf3cfa8e..8645a0cc0 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3230,6 +3230,11 @@ rest rest rest rest rest rest rest rest rest) ()))) +(deftest test-cljs-1744 + (doseq [i (range 1 64)] + (let [m (zipmap (range i) (range i))] + (is (= () (last (take (inc i) (iterate rest m)))))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 756fa9bb196a97e0ae40fd644da5e492e0336c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 Aug 2016 16:19:22 +0100 Subject: [PATCH 1986/4033] CLJS-1274: Allow assignment to namespace-qualified names in current namespace --- src/main/clojure/cljs/analyzer.cljc | 16 ++++++++++++---- src/test/cljs/cljs/core_test.cljs | 6 ++++++ src/test/clojure/cljs/analyzer_tests.clj | 8 ++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5aab2816b..2d1d38997 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1137,7 +1137,7 @@ (when (symbol? x) x)) (defmethod parse 'def - [op env form name _] + [op env form _ _] (let [pfn (fn ([_ sym] {:sym sym}) ([_ sym init] {:sym sym :init init}) @@ -1150,13 +1150,21 @@ dynamic (-> sym meta :dynamic) ns-name (-> env :ns :name) locals (:locals env) - clash-ns (symbol (str ns-name "." sym))] + clash-ns (symbol (str ns-name "." sym)) + sym-ns (namespace sym) + sym (cond + (and sym-ns (not #?(:clj (= (symbol sym-ns) ns-name) + :cljs (symbol-identical? (symbol sym-ns) ns-name)))) + (throw (error env (str "Can't def ns-qualified name in namespace " sym-ns))) + + (some? sym-ns) + (symbol (name sym)) + + :else sym)] (when (get-in @env/*compiler* [::namespaces clash-ns]) (warning :ns-var-clash env {:ns (symbol (str ns-name "." sym)) :var (symbol (str ns-name) (str sym))})) - (when (namespace sym) - (throw (error env "Can't def ns-qualified name"))) (when (:const (resolve-var (dissoc env :locals) sym)) (throw (error env "Can't redefine a constant"))) (when-let [doc (:doc args)] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 8645a0cc0..90fce12f3 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3235,6 +3235,12 @@ (let [m (zipmap (range i) (range i))] (is (= () (last (take (inc i) (iterate rest m)))))))) +(def cljs.core-test/foo-1274 42) + +(deftest test-cljs-1274 + (is (= foo-1274 42)) + (is (= cljs.core-test/foo-1274 42))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 53d89aa0c..8ddf6d5a3 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -441,3 +441,11 @@ (a/analyze test-env '(ns foo.core (:require [clojure.set :rename {intersection foo}])))))))) + + +(deftest test-cljs-1274 + (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] + (binding [a/*cljs-ns* a/*cljs-ns*] + (is (thrown-with-msg? Exception #"Can't def ns-qualified name in namespace foo.core" + (a/analyze test-env '(def foo.core/foo 43)))) + (is (a/analyze test-env '(def cljs.user/foo 43)))))) From 63e0d7380ee5c45d74a93613aafe982ee600e990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 Aug 2016 19:56:22 +0100 Subject: [PATCH 1987/4033] CLJS-1742: Add docstring for new refer-clojure REPL special --- src/main/clojure/cljs/repl.cljc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index c75cd6a06..593cd7a0a 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1224,6 +1224,16 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) use-macros {:arglists ([& args]) :doc "Similar to the use REPL special function but only for macros."} + refer-clojure {:arglists ([& args]) + :doc "Refers to all the public vars of `cljs.core`, subject to + filters. + Filters can include at most one each of: + + :exclude list-of-symbols + :rename map-of-fromsymbol-tosymbol + + Filters can be used to select a subset, via exclusion, or to provide a mapping + to a symbol different from the var's name, in order to prevent clashes."} import {:arglists ([& import-symbols-or-lists]) :doc "import-list => (closure-namespace constructor-name-symbols*) From bc7f1d4c9602770e7fdcbcb2d6e39f36ab5f46a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 Aug 2016 23:16:28 +0100 Subject: [PATCH 1988/4033] CLJS-1657: Self-host: Implicit macro loading with alias the issue is no longer reproducible given recent enhancements. In any case, added a unit test that checks for possible future regressions. As per the feedback in JIRA, removed the workaround for this issue in self parity tests. --- src/test/self/self_host/test.cljs | 24 +++++++++++++++++++++++- src/test/self/self_parity/test.cljs | 10 +--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index fcec50f41..fbd75d5d2 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -371,7 +371,7 @@ (deftest test-load-and-invoke-macros (async done - (let [l (latch 11 done)] + (let [l (latch 12 done)] ;; Normal require macros (let [st (cljs/empty-state)] (cljs/eval-str st @@ -590,6 +590,28 @@ (fn [{:keys [error value]}] (is (nil? error)) (is (= 320 value)) + (inc! l)))))) + (let [st (cljs/empty-state)] + ;; Rely on implicit macro loading (ns loads its own macros), with an alias + ;; CLJS-1657 + (cljs/eval-str st + "(ns cljs.user (:require [foo.core :as foo]))" + nil + {:eval node-eval + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"}) + (cb {:lang :clj :source "(ns foo.core (:require-macros foo.core))"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo/add 300 500)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 800 value)) (inc! l))))))))) (deftest test-eval-str-with-require-macros diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 03f9fdd3d..d08321d13 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -230,23 +230,15 @@ :verbose false} cb)) -(defn prime-analysis-cache-for-implicit-macro-loading - "Supports priming analysis cache in order to work around - http://dev.clojure.org/jira/browse/CLJS-1657" - [st ns-sym] - (swap! st assoc-in [::cljs.analyzer/namespaces ns-sym :require-macros] {ns-sym ns-sym})) - ;; Test suite runner -(defn run-tests +(defn run-tests "Runs the tests." [] ;; Ideally we'd just load test_runner.cljs, but a few namespace tests ;; don't yet run in bootstrapped ClojureScript. These are commented ;; out below and can be uncommented as fixed. (let [st (cljs/empty-state)] - (prime-analysis-cache-for-implicit-macro-loading st 'cljs.spec) - (prime-analysis-cache-for-implicit-macro-loading st 'cljs.spec.impl.gen) (eval-form st 'cljs.user '(ns parity.core (:require [cljs.test :refer-macros [run-tests]] From aaf18ec17700ea81f48a2429fcaa74cc7e507cb2 Mon Sep 17 00:00:00 2001 From: Andrea Richiardi Date: Sun, 14 Aug 2016 18:18:22 -0700 Subject: [PATCH 1989/4033] CLJS-1746: Log the result of loading a dependency It traces the result of loading a dependency in load-deps when :verbose is true, that was just ignored before. --- src/main/cljs/cljs/js.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 9768ba527..4e29fd7e8 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -343,6 +343,8 @@ (dissoc :ns))] (require bound-vars dep opts' (fn [res] + (when (:verbose opts) + (debug-prn "Loading result: " res)) (if-not (:error res) (load-deps bound-vars ana-env lib (next deps) opts cb) (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] From 60f6f86d17c4cd72f9cc39d32d6324699480103a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Aug 2016 08:47:38 -0400 Subject: [PATCH 1990/4033] 1.9.211 --- README.md | 6 +++--- changes.md | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 846729f89..27a56e80c 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaS ## Releases and dependency information ## -Latest stable release: 1.9.198 +Latest stable release: 1.9.211 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.198"] +[org.clojure/clojurescript "1.9.211"] ``` [Maven](http://maven.apache.org) dependency information: @@ -20,7 +20,7 @@ Latest stable release: 1.9.198 org.clojure clojurescript - 1.9.198 + 1.9.211 ``` diff --git a/changes.md b/changes.md index 33e14d062..a4a479f18 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,18 @@ +## 1.9.211 + +### Fixes +* CLJS-1746: Log the result of loading a dependency +* CLJS-1657: Self-host: Implicit macro loading with alias +* CLJS-1742: Add docstring for new refer-clojure REPL special +* CLJS-1274: Allow assignment to namespace-qualified names in current namespace +* CLJS-1744: rest produces nil for larger maps +* CLJS-1740: Self-host: Need to port more of CLJS-1733 +* CLJS-1741: Add :rename to :refer-clojure in ns docstring +* CLJS-1737: Self-host: clojure alias implicit macro use regression +* invalid cljs.spec/merge res call +* CLJS-1739: seq on map literal with 9 elements leads to rest producing nil +* CLJS-1738: Self-host: need to update call to check-use-macros-inferring-missing + ## 1.9.198 ### Enhancements From b93beee2139689edbf20ab73e831bafe9722f152 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 15 Aug 2016 12:31:39 -0500 Subject: [PATCH 1991/4033] Change pointers to http://clojurescript.org site --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 27a56e80c..ec9e9c7f9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ ## What is ClojureScript? ## -ClojureScript is a compiler for [Clojure](http://clojure.org) that targets JavaScript. It is designed to emit JavaScript code which is compatible with the advanced compilation mode of the [Google Closure](https://developers.google.com/closure/compiler/) optimizing compiler. +[ClojureScript](http://clojurescript.org) is a compiler for [Clojure](http://clojure.org) that targets JavaScript. It is designed to emit JavaScript code which is compatible with the advanced compilation mode of the [Google Closure](https://developers.google.com/closure/compiler/) optimizing compiler. + +Official web site: http://clojurescript.org ## Releases and dependency information ## @@ -28,10 +30,10 @@ Latest stable release: 1.9.211 * [Compare with JavaScript](http://himera.herokuapp.com/synonym.html) * [Try it online](http://himera.herokuapp.com/index.html) -* Read the [Quick Start](https://github.com/clojure/clojurescript/wiki/Quick-Start) guide. -* Read the [Documentation](https://github.com/clojure/clojurescript/wiki). -* Try a [tutorial](https://github.com/clojure/clojurescript/wiki). -* [Companies using ClojureScript](https://github.com/clojure/clojurescript/wiki/Companies-Using-ClojureScript) +* Read the [Quick Start](http://clojurescript.org/guides/quick-start) guide. +* Read the [Documentation](http://clojurescript.org). +* Try a [tutorial](http://clojurescript.org/guides). +* [Companies using ClojureScript](http://clojurescript.org/community/companies) ## Questions, Feedback? ## From e7351a248fbea6279e610d133d5ecb509b8200be Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Aug 2016 13:51:37 -0400 Subject: [PATCH 1992/4033] fix CLJS-1663 regression --- src/main/cljs/cljs/spec/test.cljs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs index 1a1199469..5ca7a755b 100644 --- a/src/main/cljs/cljs/spec/test.cljs +++ b/src/main/cljs/cljs/spec/test.cljs @@ -9,6 +9,7 @@ (ns cljs.spec.test (:require-macros [cljs.spec.test :as m :refer [with-instrument-disabled]]) (:require + [goog.object :as gobj] [goog.userAgent.product :as product] [clojure.string :as string] [cljs.stacktrace :as st] @@ -103,14 +104,16 @@ (str "Call to " v " did not conform to spec:\n" (with-out-str (s/explain-out ed))) ed))) conformed)))] - (fn - [& args] - (if *instrument-enabled* - (with-instrument-disabled - (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) - (binding [*instrument-enabled* true] - (apply f args))) - (apply f args))))) + (doto + (fn + [& args] + (if *instrument-enabled* + (with-instrument-disabled + (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) + (binding [*instrument-enabled* true] + (apply f args))) + (apply f args))) + (gobj/extend f)))) (defn- no-fspec [v spec] From 862950da92581b60abc3bc615305461a7e1c1bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 15 Aug 2016 17:22:35 +0100 Subject: [PATCH 1993/4033] CLJS-1747: Port `clojure.spec/assert` over to ClojureScript --- src/main/cljs/cljs/spec.cljc | 37 ++++++++++++++++++++++------ src/main/cljs/cljs/spec.cljs | 41 ++++++++++++++++++++++++++++++- src/test/cljs/cljs/spec_test.cljs | 10 +++++++- 3 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index a997d2e3b..9351d0549 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -7,9 +7,10 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.spec - (:refer-clojure :exclude [+ * and or cat def keys merge resolve]) + (:refer-clojure :exclude [+ * and or cat def keys merge resolve assert]) (:require [cljs.core :as c] [cljs.analyzer :as ana] + [cljs.env :as env] [cljs.analyzer.api :refer [resolve]] [clojure.walk :as walk] [cljs.spec.impl.gen :as gen] @@ -44,7 +45,7 @@ [env s] (if (namespace s) (let [v (resolve env s)] - (assert v (str "Unable to resolve: " s)) + (clojure.core/assert v (str "Unable to resolve: " s)) (->sym v)) (symbol (str ana/*cljs-ns*) (str s)))) @@ -138,7 +139,7 @@ (let [unk #(-> % name keyword) req-keys (filterv keyword? (flatten req)) req-un-specs (filterv keyword? (flatten req-un)) - _ (assert (every? #(clojure.core/and (keyword? %) (namespace %)) (concat req-keys req-un-specs opt opt-un)) + _ (clojure.core/assert (every? #(clojure.core/and (keyword? %) (namespace %)) (concat req-keys req-un-specs opt opt-un)) "all keys must be namespace-qualified keywords") req-specs (into req-keys req-un-specs) req-keys (into req-keys (map unk req-un-specs)) @@ -180,7 +181,7 @@ keys (mapv first pairs) pred-forms (mapv second pairs) pf (mapv #(res &env %) pred-forms)] - (assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") + (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/or-spec-impl ~keys '~pf ~pred-forms nil))) (defmacro and @@ -295,7 +296,7 @@ keys (mapv first pairs) pred-forms (mapv second pairs) pf (mapv #(res &env %) pred-forms)] - (assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") + (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/alt-impl ~keys ~pred-forms '~pf))) (defmacro cat @@ -311,7 +312,7 @@ pred-forms (mapv second pairs) pf (mapv #(res &env %) pred-forms)] ;;(prn key-pred-forms) - (assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") + (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") `(cljs.spec/cat-impl ~keys ~pred-forms '~pf))) (defmacro & @@ -355,7 +356,7 @@ where each element conforms to the corresponding pred. Each element will be referred to in paths using its ordinal." [& preds] - (assert (not (empty? preds))) + (clojure.core/assert (not (empty? preds))) `(cljs.spec/tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) (def ^:private _speced_vars (atom #{})) @@ -467,3 +468,25 @@ f# ~sym] (for [args# (gen/sample (gen (:args fspec#)) ~n)] [args# (apply f# args#)])))) + +(defmacro ^:private init-compile-asserts [] + (let [compile-asserts (not (-> env/*compiler* deref :options :elide-asserts))] + compile-asserts)) + +(defmacro assert + "spec-checking assert expression. Returns x if x is valid? according +to spec, else throws an error with explain-data plus ::failure of +:assertion-failed. +Can be disabled at either compile time or runtime: +If *compile-asserts* is false at compile time, compiles to x. Defaults +to the negation value of the ':elide-asserts' compiler option, or true if +not set. +If (check-asserts?) is false at runtime, always returns x. Defaults to +value of 'cljs.spec/*runtime-asserts*', or false if not set. You can +toggle check-asserts? with (check-asserts bool)." + [spec x] + `(if cljs.spec/*compile-asserts* + (if cljs.spec/*runtime-asserts* + (cljs.spec/assert* ~spec ~x) + ~x) + ~x)) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 818615b91..ada8ec69f 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1207,4 +1207,43 @@ (c/and (.lessThanOrEqual start val) (.lessThan val end)) - :else false)) \ No newline at end of file + :else false)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; assert ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defonce + ^{:dynamic true + :doc "If true, compiler will enable spec asserts, which are then +subject to runtime control via check-asserts? If false, compiler +will eliminate all spec assert overhead. See 'assert'. +Initially set to the negation of the ':elide-asserts' compiler option. +Defaults to true."} + *compile-asserts* + (s/init-compile-asserts)) + +(defonce ^{:private true + :dynamic true} + *runtime-asserts* + false) + +(defn ^boolean check-asserts? + "Returns the value set by check-asserts." + [] + *runtime-asserts*) + +(defn check-asserts + "Enable or disable spec asserts that have been compiled +with '*compile-asserts*' true. See 'assert'. +Initially set to boolean value of cljs.spec/*runtime-asserts*. +Defaults to false." + [^boolean flag] + (set! *runtime-asserts* flag)) + +(defn assert* + "Do not call this directly, use 'assert'." + [spec x] + (if (valid? spec x) + x + (let [ed (c/merge (assoc (explain-data* spec [] [] [] x) + ::failure :assertion-failed))] + (throw (js/Error. + (str "Spec assertion failed\n" (with-out-str (explain-out ed)))))))) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 40e8453f3..ee5885f71 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -57,6 +57,14 @@ (let [xs [42 11 13 15 {:a 1 :b 2 :c 3} 1 2 3 42 43 44 11]] (is (= xs (s/unform s2 (s/conform s2 xs)))))) +(deftest test-assert + (s/def ::even-number (s/and number? even?)) + ;; assertions off by default + (is (= 42 (s/assert ::even-number 42))) + (s/check-asserts true) + (is (= 42 (s/assert ::even-number 42))) + (is (thrown? js/Error (s/assert ::even-number 5)))) + ;; Copied from Clojure spec tests (def even-count? #(even? (count %))) @@ -198,4 +206,4 @@ (run-tests) - ) \ No newline at end of file + ) From cd144c205ca08759869d2c32157bcc6a60173d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 15 Aug 2016 18:04:30 +0100 Subject: [PATCH 1994/4033] CLJS-1749: Missing `cljs.spec.impl.gen/double*` --- src/main/cljs/cljs/spec/impl/gen.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index 897d9aca6..7998747bd 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -68,7 +68,7 @@ (lazy-combinators hash-map list map not-empty set vector vector-distinct fmap elements bind choose one-of such-that tuple sample return - large-integer*) + large-integer* double*) (lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double int keyword keyword-ns large-integer ratio simple-type simple-type-printable From 80ad20d16ac4ffc36fd351d11b1f4c9f90651b8e Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 15 Aug 2016 13:55:24 -0400 Subject: [PATCH 1995/4033] 1.9.216 --- README.md | 6 +++--- changes.md | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ec9e9c7f9..c5a808d52 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.211 +Latest stable release: 1.9.216 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.211"] +[org.clojure/clojurescript "1.9.216"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.211 org.clojure clojurescript - 1.9.211 + 1.9.216 ``` diff --git a/changes.md b/changes.md index a4a479f18..a37c28336 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,10 @@ +## 1.9.216 + +### Fixes +* CLJS-1749: Missing `cljs.spec.impl.gen/double*` +* CLJS-1747: Port `clojure.spec/assert` over to ClojureScript +* fix CLJS-1663 multi-arity fn instrument regression + ## 1.9.211 ### Fixes From dd3b3bd5ec92f86c6d91c85a6302cee1d22ef399 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Aug 2016 11:03:27 -0400 Subject: [PATCH 1996/4033] fix REPL regression which removed warnings --- src/main/clojure/cljs/repl.cljc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 593cd7a0a..965dd46a7 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -460,9 +460,8 @@ :gen-col 0 :gen-line 0})] (let [js (comp/emit-str - (ana/no-warn - (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) - (wrap form) nil opts))) + (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) + (wrap form) nil opts)) t (System/currentTimeMillis)] (str js "\n//# sourceURL=repl-" t ".js" @@ -480,13 +479,12 @@ (with-out-str (pr form)))]}) "UTF-8"))))) (comp/emit-str - (ana/no-warn - (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) - (wrap form) nil opts))))] + (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) + (wrap form) nil opts)))] ;; NOTE: means macros which expand to ns aren't supported for now ;; when eval'ing individual forms at the REPL - David (when (and (sequential? form) (= 'ns (first form))) - (let [ast (ana/analyze env form nil opts)] + (let [ast (ana/no-warn (ana/analyze env form nil opts))] (load-dependencies repl-env (into (vals (:requires ast)) (distinct (vals (:uses ast)))) From 2a7454837244cf7de3dfed1e48f46f86c33a1809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 17 Aug 2016 14:22:23 +0100 Subject: [PATCH 1997/4033] CLJS-1754: Add boolean? generator --- script/test-self-parity | 4 +++- src/main/cljs/cljs/spec/impl/gen.cljs | 1 + src/test/cljs/cljs/spec_test.cljs | 7 ++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/script/test-self-parity b/script/test-self-parity index 624f770b1..c777af4f4 100755 --- a/script/test-self-parity +++ b/script/test-self-parity @@ -10,8 +10,10 @@ if [ ! -f lib/clojure.jar ]; then exit 1 fi jar xvf lib/clojure.jar clojure/template.clj > /dev/null -mkdir -p builds/out-self-parity/clojure +jar xvf lib/test.check.jar clojure/test/check/random clojure/test/check/generators.cljc clojure/test/check/rose_tree.cljc > /dev/null +mkdir -p builds/out-self-parity/clojure/test mv clojure/template.clj builds/out-self-parity/clojure +mv clojure/test/check builds/out-self-parity/clojure/test bin/cljsc src/test/self/self_parity "{:optimizations :none :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}" diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index 7998747bd..0400eedac 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -124,6 +124,7 @@ gen-builtins nil? (return nil) false? (return false) true? (return true) + boolean? (boolean) zero? (return 0) ;rational? (one-of [(large-integer) (ratio)]) coll? (one-of [(map simple simple) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index ee5885f71..c56486d9b 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -1,6 +1,8 @@ (ns cljs.spec-test (:require [cljs.spec :as s] - [cljs.test :as test :refer-macros [deftest is are run-tests]])) + [cljs.test :as test :refer-macros [deftest is are run-tests]] + [cljs.spec.impl.gen :as gen] + [clojure.test.check.generators])) (s/def ::even? (s/and number? even?)) (s/def ::odd? (s/and number? odd?)) @@ -65,6 +67,9 @@ (is (= 42 (s/assert ::even-number 42))) (is (thrown? js/Error (s/assert ::even-number 5)))) +(deftest test-cljs-1754 + (is (boolean? (gen/generate (s/gen boolean?))))) + ;; Copied from Clojure spec tests (def even-count? #(even? (count %))) From 74db88011dd6dd64f55521f074f0315231be0e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 17 Aug 2016 18:30:36 +0100 Subject: [PATCH 1998/4033] CLJS-1757: cljs.spec/exercise-fn doesn't work when passed a quoted symbol --- src/main/cljs/cljs/spec.cljc | 16 ++++++++++------ src/test/cljs/cljs/spec_test.cljs | 6 ++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 9351d0549..b05457a1a 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -462,12 +462,16 @@ ([sym n] `(exercise-fn ~sym ~n nil)) ([sym n fspec] - `(let [fspec# ~(if-not fspec - `(cljs.spec/get-spec '~(:name (resolve &env sym))) - fspec) - f# ~sym] - (for [args# (gen/sample (gen (:args fspec#)) ~n)] - [args# (apply f# args#)])))) + (let [sym (cond-> sym + (clojure.core/and (sequential? sym) + (= (first sym) 'quote)) + second)] + `(let [fspec# ~(if-not fspec + `(cljs.spec/get-spec '~(:name (resolve &env sym))) + fspec) + f# ~sym] + (for [args# (gen/sample (gen (:args fspec#)) ~n)] + [args# (apply f# args#)]))))) (defmacro ^:private init-compile-asserts [] (let [compile-asserts (not (-> env/*compiler* deref :options :elide-asserts))] diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index c56486d9b..e9415fd9d 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -70,6 +70,12 @@ (deftest test-cljs-1754 (is (boolean? (gen/generate (s/gen boolean?))))) +(s/fdef cljs-1757-x :args (s/cat ::first number?) :ret #(= % 2)) +(defn cljs-1757-x [b] 2) + +(deftest test-cljs-1757 + (is (s/exercise-fn `cljs-1757-x))) + ;; Copied from Clojure spec tests (def even-count? #(even? (count %))) From b8a14ee78394a866c592b7daa9909137ab43528c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 17 Aug 2016 16:02:40 +0100 Subject: [PATCH 1999/4033] CLJS-1756: Add test.check JAR to the bootstrap script --- script/bootstrap | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/script/bootstrap b/script/bootstrap index 612f7f09e..30ee279f2 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -9,6 +9,7 @@ TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.0.0-beta3" +TEST_CHECK_RELEASE="0.9.0" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } @@ -105,4 +106,10 @@ curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TR echo "Moving tools.reader.jar to lib/tools.reader.jar" mv tools.reader-$TREADER_RELEASE.jar lib/tools.reader-$TREADER_RELEASE.jar +echo "Fetching test.check $TEST_CHECK_RELEASE ..." +curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/test.check/$TEST_CHECK_RELEASE/test.check-$TEST_CHECK_RELEASE.jar || { echo "Download failed."; exit 1; } + +echo "Moving test.check.jar to lib/test.check.jar" +mv test.check-$TEST_CHECK_RELEASE.jar lib/test.check.jar + echo "[Bootstrap Completed]" From b6c48c700a788d2b19513170e3231e043afe9752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 15 Aug 2016 21:22:46 +0100 Subject: [PATCH 2000/4033] CLJS-1751: port fix lost type hints in map destructuring same as Clojure commit e8065ab --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 4fdb084d7..ed7e89ac2 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -682,7 +682,7 @@ bk (val (first bes)) local (if #?(:clj (core/instance? clojure.lang.Named bb) :cljs (cljs.core/implements? INamed bb)) - (symbol nil (name bb)) + (with-meta (symbol nil (name bb)) (meta bb)) bb) bv (if (contains? defaults local) (core/list 'cljs.core/get gmap bk (defaults local)) From 86a83d720beb44deb5d55d7d9c0bc2d5174816a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 19 Aug 2016 16:23:20 +0100 Subject: [PATCH 2001/4033] CLJS-1760: Self-host: test-cljs-1757 failing in test-self-parity --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index b05457a1a..bda8df39a 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -470,7 +470,7 @@ `(cljs.spec/get-spec '~(:name (resolve &env sym))) fspec) f# ~sym] - (for [args# (gen/sample (gen (:args fspec#)) ~n)] + (for [args# (gen/sample (cljs.spec/gen (:args fspec#)) ~n)] [args# (apply f# args#)]))))) (defmacro ^:private init-compile-asserts [] From 002708e530b6b3449151d3d077883beeadb92f94 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 19 Aug 2016 10:39:45 -0400 Subject: [PATCH 2002/4033] CLJS-1759: Errors writing transit analysis cache if parallel build Adds a coarse-grained mutex around the calls to write out transit analysis cache data. A result of this is that sporadic failures that only occur when concurrently writing out analysis cache info when compiling largeish multi-file projects with :parallel-build true is avoided. (Perhaps there is some thread-safety defect in the transit-clj stack yet to be discovered / fixed, and this patch acts as a suitable workaround until a proper solution is found.) --- src/main/clojure/cljs/analyzer.cljc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2d1d38997..7d2e4326b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3110,6 +3110,9 @@ true (util/changed? src cache))))))) +#?(:clj + (def transit-write-mutex (Object.))) + #?(:clj (defn write-analysis-cache ([ns cache-file] @@ -3124,10 +3127,11 @@ (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n")) (pr-str analysis))) "json" (when-let [{:keys [writer write]} @transit] - (write - (writer (FileOutputStream. cache-file) :json - transit-write-opts) - analysis)))) + (locking transit-write-mutex + (write + (writer (FileOutputStream. cache-file) :json + transit-write-opts) + analysis))))) (when src (.setLastModified ^File cache-file (util/last-modified src)))))) From a518320eec9fa6f7d1013585721bf1bc43faae6c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Aug 2016 13:34:14 -0400 Subject: [PATCH 2003/4033] 1.9.225 --- README.md | 6 +++--- changes.md | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c5a808d52..3520a2190 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.216 +Latest stable release: 1.9.225 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.216"] +[org.clojure/clojurescript "1.9.225"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.216 org.clojure clojurescript - 1.9.216 + 1.9.225 ``` diff --git a/changes.md b/changes.md index a37c28336..da313d180 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,14 @@ +## 1.9.225 + +### Fixes +* CLJS-1759: Errors writing transit analysis cache if parallel build +* CLJS-1760: Self-host: test-cljs-1757 failing in test-self-parity +* CLJS-1751: port fix lost type hints in map destructuring +* CLJS-1756: Add test.check JAR to the bootstrap script +* CLJS-1757: cljs.spec/exercise-fn doesn't work when passed a quoted symbol +* CLJS-1754: Add boolean? generator +* fix REPL regression which removed warnings + ## 1.9.216 ### Fixes From 7a06d008fadf56b11dba0f9e2ab97e61059f44fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 24 Aug 2016 16:42:41 +0100 Subject: [PATCH 2004/4033] CLJS-1763: Defining a var that clashes with `cljs.core` throws a compiler error instead of warning --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- src/test/clojure/cljs/analyzer_tests.clj | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7d2e4326b..8f15d7e90 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1892,7 +1892,7 @@ (fn [s [k & filters]] (if (= k :refer-clojure) (do - (when (seq s) + (when (seq (:excludes s)) (throw (error env "Only one :refer-clojure form is allowed per namespace definition"))) (let [valid-kws #{:exclude :rename} xs @@ -1924,7 +1924,8 @@ :else ret))] (merge-with into s xs))) s)) - {} args)) + {:excludes #{} + :renames {}} args)) (defn use->require [env [lib & filters :as spec]] (when-not (and (symbol? lib) (odd? (count spec))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 8ddf6d5a3..560a7a56c 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -449,3 +449,10 @@ (is (thrown-with-msg? Exception #"Can't def ns-qualified name in namespace foo.core" (a/analyze test-env '(def foo.core/foo 43)))) (is (a/analyze test-env '(def cljs.user/foo 43)))))) + +(deftest test-cljs-1763 + (let [parsed (a/parse-ns-excludes {} '())] + (is (= parsed + {:excludes #{} + :renames {}})) + (is (set? (:excludes parsed))))) From f937c613c366e2b7b20b00ef39399e7e3d171fb7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 26 Aug 2016 17:56:08 -0400 Subject: [PATCH 2005/4033] 1.9.227 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3520a2190..2bf9232d5 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.225 +Latest stable release: 1.9.227 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.225"] +[org.clojure/clojurescript "1.9.227"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.225 org.clojure clojurescript - 1.9.225 + 1.9.227 ``` diff --git a/changes.md b/changes.md index da313d180..2c95f7ce9 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 1.9.227 + +### Fixes +* CLJS-1763: Defining a var that clashes with `cljs.core` throws a compiler error instead of warning + ## 1.9.225 ### Fixes From ddf183681ee56b44913ce027b5795ada193dd344 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 31 Aug 2016 07:32:57 -0400 Subject: [PATCH 2006/4033] pass unconform along on conformer with-gen Same as Clojure de8992032 --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index ada8ec69f..331d79aed 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -425,7 +425,7 @@ (gen* [_ _ _ _] (if gfn (gfn) (gen/gen-for-pred pred))) - (with-gen* [_ gfn] (spec-impl form pred gfn cpred?)) + (with-gen* [_ gfn] (spec-impl form pred gfn cpred? unc)) (describe* [_] form))))) (defn ^:skip-wiki multi-spec-impl From ce6c657a751cce5fb1b8e94eb97e74944c0d7fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 3 Sep 2016 04:08:16 +0100 Subject: [PATCH 2007/4033] CLJS-1772: Dependency index can incorrectly overwrite `.cljs` files with `.cljc` files if both are present --- src/main/clojure/cljs/js_deps.cljc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 7146c4712..fc6ab3ae5 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -133,7 +133,18 @@ case." (fn [index' provide] (if (:foreign dep) (update-in index' [provide] merge dep) - (assoc index' provide dep))) + ;; when building the dependency index, we need to + ;; avoid overwriting a CLJS dep with a CLJC dep of + ;; the same namespace - António Monteiro + (let [file (when-let [f (or (:source-file dep) (:file dep))] + (.toString f)) + ext (when file + (.substring file (inc (.lastIndexOf file "."))))] + (update-in index' [provide] + (fn [d] + (if (and (= ext "cljc") (some? d)) + d + dep)))))) index provides) index)] (if (:foreign dep) From d0be39660f3a65422c3de6a774ceec0b6a863ee2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 6 Sep 2016 12:41:43 -0400 Subject: [PATCH 2008/4033] readme & change notes for release --- README.md | 6 +++--- changes.md | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2bf9232d5..774c8484d 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.227 +Latest stable release: 1.9.229 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.227"] +[org.clojure/clojurescript "1.9.229"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.227 org.clojure clojurescript - 1.9.227 + 1.9.229 ``` diff --git a/changes.md b/changes.md index 2c95f7ce9..155210fa6 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,9 @@ +## 1.9.229 + +### Fixes +* CLJS-1772: Dependency index can incorrectly overwrite `.cljs` files with `.cljc` files if both are present +* pass unconform along on conformer with-gen instead of warning + ## 1.9.227 ### Fixes From 5ac08cc0d5524e429200b1f9e68d27a5c07d5c3f Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 14 Sep 2016 16:05:02 -0400 Subject: [PATCH 2009/4033] CLJS-1779: keyword 2-arity constructor accepts anything for both parameters which leads to different hashing --- src/main/cljs/cljs/core.cljs | 11 ++++++++++- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f5a5aa40b..58c75c094 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3207,7 +3207,16 @@ reduces them without incurring seq initialization" (if (== (alength parts) 2) (Keyword. (aget parts 0) (aget parts 1) name nil) (Keyword. nil (aget parts 0) name nil))))) - ([ns name] (Keyword. ns name (str (when ns (str ns "/")) name) nil))) + ([ns name] + (let [ns (cond + (keyword? ns) (cljs.core/name ns) + (symbol? ns) (cljs.core/name ns) + :else ns) + name (cond + (keyword? name) (cljs.core/name name) + (symbol? name) (cljs.core/name name) + :else name)] + (Keyword. ns name (str (when ns (str ns "/")) name) nil)))) (deftype LazySeq [meta ^:mutable fn ^:mutable s ^:mutable __hash] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 90fce12f3..7484657c5 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3241,6 +3241,10 @@ (is (= foo-1274 42)) (is (= cljs.core-test/foo-1274 42))) +(deftest test-cljs-1779 + (is (= (hash (keyword 'app "foo")) + (hash (keyword "app" "foo"))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 5f4ee993bcc39c055bdc05bc16d70f8fc8b02263 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 7 Sep 2016 08:37:41 -0400 Subject: [PATCH 2010/4033] CLJS-1774: Self-host: Report filenames in warns in test-self-parity --- src/test/self/self_parity/test.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index d08321d13..19d95d4e8 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -123,6 +123,7 @@ (fn [source] (if source (let [source-cb-value {:lang (filename->lang filename) + :file filename :source source}] (if (or (string/ends-with? filename ".cljs") (string/ends-with? filename ".cljc")) From f1981a4232dbaf87173fea3aeac8f554b30b46f8 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Fri, 16 Sep 2016 08:26:24 +1000 Subject: [PATCH 2011/4033] CLJS-1780: Records without extmaps fail to iterate --- src/main/clojure/cljs/core.cljc | 5 ++++- src/test/cljs/cljs/core_test.cljs | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index ed7e89ac2..45547f638 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1778,7 +1778,10 @@ ~'__extmap))) 'IIterable - `(~'-iterator [~gs] (RecordIter. 0 ~gs ~(count base-fields) [~@(map keyword base-fields)] (-iterator ~'__extmap) )) + `(~'-iterator [~gs] + (RecordIter. 0 ~gs ~(count base-fields) [~@(map keyword base-fields)] (if ~'__extmap + (-iterator ~'__extmap) + (core/nil-iter)))) 'IPrintWithWriter `(~'-pr-writer [this# writer# opts#] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 7484657c5..81b4f9e70 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3245,6 +3245,17 @@ (is (= (hash (keyword 'app "foo")) (hash (keyword "app" "foo"))))) +(defrecord CLJS1780 [a b c]) + +(deftest test-cljs-1780 + (let [record (->CLJS1780 1 2 3)] + (is (= (into #{} (sequence (map identity) + record)) + #{[:a 1] [:b 2] [:c 3]})) + (is (= (into #{} (sequence (map identity) + (assoc record :d 4 :e 5)) ) + #{[:a 1] [:b 2] [:c 3] [:d 4] [:e 5]})))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 9cb8da1d82078cfe21c7f732d94115867f455a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 15 Sep 2016 00:20:24 +0100 Subject: [PATCH 2012/4033] CLJS-1775: `get` with `nil` returns as if `get` with `0` --- src/main/cljs/cljs/core.cljs | 8 ++++---- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 58c75c094..c75c9a975 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1819,7 +1819,7 @@ reduces them without incurring seq initialization" (aget o (int k))) (string? o) - (when (< k (.-length o)) + (when (and (some? k) (< k (.-length o))) (aget o (int k))) (native-satisfies? ILookup o) @@ -1834,12 +1834,12 @@ reduces them without incurring seq initialization" (array? o) (if (< k (.-length o)) - (aget o k) + (aget o (int k)) not-found) - + (string? o) (if (< k (.-length o)) - (aget o k) + (aget o (int k)) not-found) (native-satisfies? ILookup o) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 81b4f9e70..fca51978b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3256,6 +3256,12 @@ (assoc record :d 4 :e 5)) ) #{[:a 1] [:b 2] [:c 3] [:d 4] [:e 5]})))) +(deftest test-cljs-1775 + (is (nil? (get "foo" nil))) + (is (= 42 (get {nil 42} nil) 42)) + (is (= (get #js [\h \i] 1.7 :not-found) \i)) + (is (= (get "hi" 1.7 :not-found) \i))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From e9e161453bbea6f39a5b7c165acc5acaac796145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 30 Jul 2016 22:36:49 +0100 Subject: [PATCH 2013/4033] CLJS-1633: Improve error associated with invalid foreign-libs :file path --- src/main/clojure/cljs/closure.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8e9e796f1..1766a1110 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1753,10 +1753,12 @@ (defn foreign-deps-str [opts sources] (letfn [(to-js-str [ijs] - (let [url (or (and (#{:advanced :simple} (:optimizations opts)) - (:url-min ijs)) - (:url ijs))] - (slurp url)))] + (if-let [url (or (and (#{:advanced :simple} (:optimizations opts)) + (:url-min ijs)) + (:url ijs))] + (slurp url) + (throw (IllegalArgumentException. + (str "Foreign lib " ijs " does not exist")))))] (str (string/join "\n" (map to-js-str sources)) "\n"))) (defn add-wrapper [{:keys [output-wrapper] :as opts} js] From d994f883d34a946963c3410714c522e25a633944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 2 Sep 2016 15:25:57 +0100 Subject: [PATCH 2014/4033] CLJS-1335: resolve-macro-var: information missing for macros --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 8f15d7e90..e7170e2af 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -854,9 +854,11 @@ :else (let [ns (cond (get-in namespaces [ns :macros sym]) ns - (core-name? env sym) 'cljs.core)] + (core-name? env sym) #?(:clj 'cljs.core + :cljs CLJS_CORE_MACROS_SYM))] (when ns - (get-in namespaces [ns :macros sym])))))) + #?(:clj (get-in namespaces [ns :macros sym]) + :cljs (get-in namespaces [ns :defs sym]))))))) (declare analyze analyze-symbol analyze-seq) From 309de72e7204d53863a95cd6fa33424c9c9e269f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 30 Jul 2016 21:59:46 +0100 Subject: [PATCH 2015/4033] CLJS-1600: Destructuring defprotocol fn args causes defrecord impls to silently fail --- src/main/clojure/cljs/core.cljc | 35 ++++++++++++++++++------------- src/test/cljs/cljs/core_test.cljs | 31 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 45547f638..cd9b9bf55 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1939,20 +1939,27 @@ (core/str "Invalid protocol, " psym " defines method " mname " with arity 0")))))) expand-sig (core/fn [fname slot sig] - `(~sig - (if (and (not (nil? ~(first sig))) - (not (nil? (. ~(first sig) ~(symbol (core/str "-" slot)))))) ;; Property access needed here. - (. ~(first sig) ~slot ~@sig) - (let [x# (if (nil? ~(first sig)) nil ~(first sig)) - m# (aget ~(fqn fname) (goog/typeOf x#))] - (if-not (nil? m#) - (m# ~@sig) - (let [m# (aget ~(fqn fname) "_")] - (if-not (nil? m#) - (m# ~@sig) - (throw - (missing-protocol - ~(core/str psym "." fname) ~(first sig)))))))))) + (core/let [sig (core/if-not (every? core/symbol? sig) + (mapv (core/fn [arg] + (core/cond + (core/symbol? arg) arg + (core/and (map? arg) (some? (:as arg))) (:as arg) + :else (gensym))) sig) + sig)] + `(~sig + (if (and (not (nil? ~(first sig))) + (not (nil? (. ~(first sig) ~(symbol (core/str "-" slot)))))) ;; Property access needed here. + (. ~(first sig) ~slot ~@sig) + (let [x# (if (nil? ~(first sig)) nil ~(first sig)) + m# (aget ~(fqn fname) (goog/typeOf x#))] + (if-not (nil? m#) + (m# ~@sig) + (let [m# (aget ~(fqn fname) "_")] + (if-not (nil? m#) + (m# ~@sig) + (throw + (missing-protocol + ~(core/str psym "." fname) ~(first sig))))))))))) psym (core/-> psym (vary-meta update-in [:jsdoc] conj "@interface") diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index fca51978b..5f6b045a4 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3262,6 +3262,37 @@ (is (= (get #js [\h \i] 1.7 :not-found) \i)) (is (= (get "hi" 1.7 :not-found) \i))) +(defprotocol CLJS-1600-IFoo + (foo-fn [_ {:keys [a b] :as x}])) + +(defrecord CLJS-1600-Foo [] + CLJS-1600-IFoo + (foo-fn [_ {:keys [a b] :as args}] + args)) + +(deftest test-cljs-1600 + (let [foo (reify + CLJS-1600-IFoo + (foo-fn [_ {:keys [a b] :as args}] + args))] + (is (= (foo-fn (->CLJS-1600-Foo) {:a 1 :b 2}) + {:a 1 :b 2})) + (is (= (foo-fn foo {:a 1 :b 2}) + {:a 1 :b 2}))) + ;; test that the destructuring works + (let [foo (reify + CLJS-1600-IFoo + (foo-fn [_ {:keys [a b] :as args}] + {:a a :b b}))] + (is (= (foo-fn foo {:a 1 :b 2}) + {:a 1 :b 2}))) + (let [foo (reify + CLJS-1600-IFoo + (foo-fn [_ {:keys [a b c] :or {c 3}}] + {:c c}))] + (is (= (foo-fn foo {:a 1 :b 2}) + {:c 3})))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 92459050162fce53312d9df7d7b5c703d2d992c2 Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Mon, 22 Aug 2016 07:00:41 -0500 Subject: [PATCH 2016/4033] CLJS-1761: Allow parallel Transit analysis cache writes Pre-assemble the full read and write handler maps for transit types so we avoid any race conditions in transit's handler caching. --- src/main/clojure/cljs/analyzer.cljc | 71 +++++++++++++++-------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e7170e2af..da7831974 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -58,41 +58,48 @@ #?(:clj (def transit-read-opts - (util/compile-if (import '[com.cognitect.transit ReadHandler]) - {:handlers - {"cljs/js" - (reify com.cognitect.transit.ReadHandler - (fromRep [_ v] (JSValue. v))) - "cljs/regex" - (reify com.cognitect.transit.ReadHandler - (fromRep [_ v] (Pattern/compile v)))}}))) + (try + (require '[cognitect.transit]) + (when-some [ns (find-ns 'cognitect.transit)] + (let [read-handler @(ns-resolve ns 'read-handler) + read-handler-map @(ns-resolve ns 'read-handler-map)] + {:handlers + (read-handler-map + {"cljs/js" (read-handler (fn [_ v] (JSValue. v))) + "cljs/regex" (read-handler (fn [_ v] (Pattern/compile v)))})})) + (catch Throwable t + nil)))) #?(:clj (def transit-write-opts - (util/compile-if (import '[com.cognitect.transit WriteHandler]) - {:handlers - {JSValue - (reify com.cognitect.transit.WriteHandler - (tag [_ _] "cljs/js") - (rep [_ js] (.val ^JSValue js)) - (stringRep [_ _] nil)) - Pattern - (reify com.cognitect.transit.WriteHandler - (tag [_ _] "cljs/regex") - (rep [_ pat] (.pattern ^Pattern pat)) - (stringRep [_ _] nil))}}))) + (try + (require '[cognitect.transit]) + (when-some [ns (find-ns 'cognitect.transit)] + (let [write-handler @(ns-resolve ns 'write-handler) + write-handler-map @(ns-resolve ns 'write-handler-map)] + {:handlers + (write-handler-map + {JSValue + (write-handler + (fn [_ _] "cljs/js") + (fn [_ js] (.val ^JSValue js))) + Pattern + (write-handler + (fn [_ _] "cljs/regex") + (fn [_ pat] (.pattern ^Pattern pat)))})})) + (catch Throwable t + nil)))) #?(:clj (def transit (delay (try (require '[cognitect.transit]) - (let [ns (find-ns 'cognitect.transit)] - (when ns - {:writer @(ns-resolve ns 'writer) - :reader @(ns-resolve ns 'reader) - :write @(ns-resolve ns 'write) - :read @(ns-resolve ns 'read)})) + (when-some [ns (find-ns 'cognitect.transit)] + {:writer @(ns-resolve ns 'writer) + :reader @(ns-resolve ns 'reader) + :write @(ns-resolve ns 'write) + :read @(ns-resolve ns 'read)}) (catch Throwable t nil))))) @@ -3113,9 +3120,6 @@ true (util/changed? src cache))))))) -#?(:clj - (def transit-write-mutex (Object.))) - #?(:clj (defn write-analysis-cache ([ns cache-file] @@ -3130,11 +3134,10 @@ (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n")) (pr-str analysis))) "json" (when-let [{:keys [writer write]} @transit] - (locking transit-write-mutex - (write - (writer (FileOutputStream. cache-file) :json - transit-write-opts) - analysis))))) + (write + (writer (FileOutputStream. cache-file) :json + transit-write-opts) + analysis)))) (when src (.setLastModified ^File cache-file (util/last-modified src)))))) From 1d8964ea632fbb3c775cbacb1f11807fbdfe9e72 Mon Sep 17 00:00:00 2001 From: Tom Connors Date: Fri, 2 Sep 2016 09:49:56 -0400 Subject: [PATCH 2017/4033] CLJS-1770: goog-defines broken for integers Using goog-define with integers caused cljs.closure/make-options to attempt to call a nonexistent method setDefineToIntegerLiteral. This fix causes all numeric goog-define and :closure-defines compiler options to use setDefineToDoubleLiteral. --- src/main/clojure/cljs/closure.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1766a1110..d359b073b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -263,8 +263,7 @@ (let [key (name key)] (cond (string? val) (.setDefineToStringLiteral compiler-options key val) - (integer? val) (.setDefineToIntegerLiteral compiler-options key val) - (float? val) (.setDefineToDoubleLiteral compiler-options key val) + (number? val) (.setDefineToDoubleLiteral compiler-options key val) (or (true? val) (false? val)) (.setDefineToBooleanLiteral compiler-options key val) :else (println "value for" key "must be string, int, float, or bool")))) From 7e90ad51452ec5edd8ee7f2b7af9c7e7fb759c97 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 5 Sep 2016 18:20:27 -0400 Subject: [PATCH 2018/4033] CLJS-1773: Self-host: Don't resolve unqualified symbols / keywords with $macros Revise cljs.js so that unqualified symbol and :: keyword namespace resolution strip off $macros if present on the namespace. Also, go through all of the existing ClojureScript code that employs explicit qualification simply for the purpose of self-hosted compatibility, and remove it, reverting to the simpler code previously being used. --- src/main/cljs/cljs/js.cljs | 22 +++++-- src/main/cljs/cljs/spec.cljc | 76 +++++++++++------------ src/main/cljs/cljs/spec/impl/gen.cljc | 2 +- src/main/cljs/cljs/spec/test.cljc | 6 +- src/main/cljs/cljs/test.cljc | 86 +++++++++++++-------------- 5 files changed, 103 insertions(+), 89 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 4e29fd7e8..ed2711b2a 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -39,11 +39,25 @@ 0 (- (count file) 5))] (symbol (demunge lib-name)))) +(defn- drop-macros-suffix + [ns-name] + (if (string/ends-with? ns-name "$macros") + (subs ns-name 0 (- (count ns-name) 7)) + ns-name)) + +(defn- elide-macros-suffix + [sym] + (symbol (drop-macros-suffix (namespace sym)) (name sym))) + (defn- resolve-symbol [sym] (if (string/starts-with? (str sym) ".") sym - (ana/resolve-symbol sym))) + (elide-macros-suffix (ana/resolve-symbol sym)))) + +(defn- read [eof rdr] + (binding [*ns* (symbol (drop-macros-suffix (str *ns*)))] + (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr))) (defn- atom? [x] (instance? Atom x)) @@ -528,7 +542,7 @@ comp/*source-map-data* (:*sm-data* bound-vars) ana/*cljs-file* (:cljs-file opts)] (let [res (try - {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} + {:value (read eof rdr)} (catch :default cause (wrap-error (ana/error aenv @@ -692,7 +706,7 @@ r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] (let [res (try - {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} + {:value (read eof rdr)} (catch :default cause (wrap-error (ana/error aenv @@ -792,7 +806,7 @@ comp/*source-map-data* (:*sm-data* bound-vars) ana/*cljs-file* (:cljs-file opts)] (let [res (try - {:value (r/read {:eof eof :read-cond :allow :features #{:cljs}} rdr)} + {:value (read eof rdr)} (catch :default cause (wrap-error (ana/error aenv diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index bda8df39a..44520aaca 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -57,7 +57,7 @@ (let [k (if (symbol? k) (ns-qualify &env k) k) form (res &env spec-form)] (swap! registry-ref assoc k form) - `(cljs.spec/def-impl '~k '~form ~spec-form))) + `(def-impl '~k '~form ~spec-form))) (defmacro spec "Takes a single predicate form, e.g. can be the name of a predicate, @@ -76,7 +76,7 @@ Returns a spec." [form & {:keys [gen]}] (when form - `(cljs.spec/spec-impl '~(res &env form) ~form ~gen nil))) + `(spec-impl '~(res &env form) ~form ~gen nil))) (defmacro multi-spec "Takes the name of a spec/predicate-returning multimethod and a @@ -105,7 +105,7 @@ though those values are not evident in the spec. " [mm retag] - `(cljs.spec/multi-spec-impl '~(res &env mm) (var ~mm) ~retag)) + `(multi-spec-impl '~(res &env mm) (var ~mm) ~retag)) (defmacro keys "Creates and returns a map validating spec. :req and :opt are both @@ -160,12 +160,12 @@ pred-exprs (into pred-exprs (parse-req req-un unk)) pred-forms (walk/postwalk #(res &env %) pred-exprs)] ;; `(map-spec-impl ~req-keys '~req ~opt '~pred-forms ~pred-exprs ~gen) - `(cljs.spec/map-spec-impl {:req '~req :opt '~opt :req-un '~req-un :opt-un '~opt-un - :req-keys '~req-keys :req-specs '~req-specs - :opt-keys '~opt-keys :opt-specs '~opt-specs - :pred-forms '~pred-forms - :pred-exprs ~pred-exprs - :gfn ~gen}))) + `(map-spec-impl {:req '~req :opt '~opt :req-un '~req-un :opt-un '~opt-un + :req-keys '~req-keys :req-specs '~req-specs + :opt-keys '~opt-keys :opt-specs '~opt-specs + :pred-forms '~pred-forms + :pred-exprs ~pred-exprs + :gfn ~gen}))) (defmacro or "Takes key+pred pairs, e.g. @@ -182,7 +182,7 @@ pred-forms (mapv second pairs) pf (mapv #(res &env %) pred-forms)] (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") - `(cljs.spec/or-spec-impl ~keys '~pf ~pred-forms nil))) + `(or-spec-impl ~keys '~pf ~pred-forms nil))) (defmacro and "Takes predicate/spec-forms, e.g. @@ -192,7 +192,7 @@ Returns a spec that returns the conformed value. Successive conformed values propagate through rest of predicates." [& pred-forms] - `(cljs.spec/and-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) + `(and-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) (defmacro every "takes a pred and validates collection elements against that pred. @@ -224,8 +224,8 @@ See also - coll-of, every-kv " [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] - (let [nopts (-> opts (dissoc :gen) (assoc :cljs.spec/kind-form `'~(res &env (:kind opts))))] - `(cljs.spec/every-impl '~pred ~pred ~nopts ~gen))) + (let [nopts (-> opts (dissoc :gen) (assoc ::kind-form `'~(res &env (:kind opts))))] + `(every-impl '~pred ~pred ~nopts ~gen))) (defmacro every-kv "like 'every' but takes separate key and val preds and works on associative collections. @@ -235,7 +235,7 @@ See also - map-of" [kpred vpred & opts] - `(every (tuple ~kpred ~vpred) :cljs.spec/kfn (fn [i# v#] (nth v# 0)) :into {} ~@opts)) + `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (nth v# 0)) :into {} ~@opts)) (defmacro coll-of "Returns a spec for a collection of items satisfying pred. Unlike @@ -249,7 +249,7 @@ See also - every, map-of" [pred & opts] - `(every ~pred :cljs.spec/conform-all true ~@opts)) + `(every ~pred ::conform-all true ~@opts)) (defmacro map-of "Returns a spec for a map whose keys satisfy kpred and vals satisfy @@ -262,25 +262,25 @@ See also - every-kv" [kpred vpred & opts] - `(every-kv ~kpred ~vpred :cljs.spec/conform-all true :kind map? ~@opts)) + `(every-kv ~kpred ~vpred ::conform-all true :kind map? ~@opts)) (defmacro * "Returns a regex op that matches zero or more values matching pred. Produces a vector of matches iff there is at least one match" [pred-form] - `(cljs.spec/rep-impl '~(res &env pred-form) ~pred-form)) + `(rep-impl '~(res &env pred-form) ~pred-form)) (defmacro + "Returns a regex op that matches one or more values matching pred. Produces a vector of matches" [pred-form] - `(cljs.spec/rep+impl '~(res &env pred-form) ~pred-form)) + `(rep+impl '~(res &env pred-form) ~pred-form)) (defmacro ? "Returns a regex op that matches zero or one value matching pred. Produces a single value (not a collection) if matched." [pred-form] - `(cljs.spec/maybe-impl ~pred-form '~pred-form)) + `(maybe-impl ~pred-form '~pred-form)) (defmacro alt "Takes key+pred pairs, e.g. @@ -297,7 +297,7 @@ pred-forms (mapv second pairs) pf (mapv #(res &env %) pred-forms)] (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") - `(cljs.spec/alt-impl ~keys ~pred-forms '~pf))) + `(alt-impl ~keys ~pred-forms '~pf))) (defmacro cat "Takes key+pred pairs, e.g. @@ -313,7 +313,7 @@ pf (mapv #(res &env %) pred-forms)] ;;(prn key-pred-forms) (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") - `(cljs.spec/cat-impl ~keys ~pred-forms '~pf))) + `(cat-impl ~keys ~pred-forms '~pf))) (defmacro & "takes a regex op re, and predicates. Returns a regex-op that consumes @@ -321,15 +321,15 @@ conjunction of the predicates, and any conforming they might perform." [re & preds] (let [pv (vec preds)] - `(cljs.spec/amp-impl ~re ~pv '~(mapv #(res &env %) pv)))) + `(amp-impl ~re ~pv '~(mapv #(res &env %) pv)))) (defmacro conformer "takes a predicate function with the semantics of conform i.e. it should return either a (possibly converted) value or :cljs.spec/invalid, and returns a spec that uses it as a predicate/conformer. Optionally takes a second fn that does unform of result of first" - ([f] `(cljs.spec/spec-impl '~f ~f nil true)) - ([f unf] `(cljs.spec/spec-impl '~f ~f nil true ~unf))) + ([f] `(spec-impl '~f ~f nil true)) + ([f unf] `(spec-impl '~f ~f nil true ~unf))) (defmacro fspec "takes :args :ret and (optional) :fn kwargs whose values are preds @@ -347,7 +347,7 @@ that returns a test.check generator." [& {:keys [args ret fn gen]}] (let [env &env] - `(cljs.spec/fspec-impl (spec ~args) '~(res env args) + `(fspec-impl (spec ~args) '~(res env args) (spec ~ret) '~(res env ret) (spec ~fn) '~(res env fn) ~gen))) @@ -357,7 +357,7 @@ will be referred to in paths using its ordinal." [& preds] (clojure.core/assert (not (empty? preds))) - `(cljs.spec/tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) + `(tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) (def ^:private _speced_vars (atom #{})) @@ -398,7 +398,7 @@ :ret symbol?)" [fn-sym & specs] (swap! _speced_vars conj (ns-qualify &env fn-sym)) - `(cljs.spec/def ~fn-sym (cljs.spec/fspec ~@specs))) + `(cljs.spec/def ~fn-sym (fspec ~@specs))) (defmacro keys* "takes the same arguments as spec/keys and returns a regex op that matches sequences of key/values, @@ -416,13 +416,13 @@ {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" [& kspecs] `(let [mspec# (keys ~@kspecs)] - (cljs.spec/with-gen (cljs.spec/& (* (cat :cljs.spec/k keyword? :cljs.spec/v cljs.core/any?)) :cljs.spec/kvs->map mspec#) - (fn [] (gen/fmap (fn [m#] (apply concat m#)) (cljs.spec/gen mspec#)))))) + (with-gen (& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) + (fn [] (gen/fmap (fn [m#] (apply concat m#)) (gen mspec#)))))) (defmacro nilable "returns a spec that accepts nil and values satisfiying pred" [pred] - `(and (or :cljs.spec/nil nil? :cljs.spec/pred ~pred) (conformer second))) + `(and (or ::nil nil? ::pred ~pred) (conformer second))) (defmacro inst-in "Returns a spec that validates insts in the range from start @@ -431,7 +431,7 @@ `(let [st# (cljs.core/inst-ms ~start) et# (cljs.core/inst-ms ~end) mkdate# (fn [d#] (js/Date. d#))] - (spec (and cljs.core/inst? #(cljs.spec/inst-in-range? ~start ~end %)) + (spec (and cljs.core/inst? #(inst-in-range? ~start ~end %)) :gen (fn [] (gen/fmap mkdate# (gen/large-integer* {:min st# :max et#})))))) @@ -440,7 +440,7 @@ "Returns a spec that validates longs in the range from start (inclusive) to end (exclusive)." [start end] - `(spec (and c/int? #(cljs.spec/int-in-range? ~start ~end %)) + `(spec (and c/int? #(int-in-range? ~start ~end %)) :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) (defmacro merge @@ -450,7 +450,7 @@ predicates. Unlike 'and', merge can generate maps satisfying the union of the predicates." [& pred-forms] - `(cljs.spec/merge-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) + `(merge-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) (defmacro exercise-fn "exercises the fn named by sym (a symbol) by applying it to @@ -467,10 +467,10 @@ (= (first sym) 'quote)) second)] `(let [fspec# ~(if-not fspec - `(cljs.spec/get-spec '~(:name (resolve &env sym))) + `(get-spec '~(:name (resolve &env sym))) fspec) f# ~sym] - (for [args# (gen/sample (cljs.spec/gen (:args fspec#)) ~n)] + (for [args# (gen/sample (gen (:args fspec#)) ~n)] [args# (apply f# args#)]))))) (defmacro ^:private init-compile-asserts [] @@ -489,8 +489,8 @@ If (check-asserts?) is false at runtime, always returns x. Defaults to value of 'cljs.spec/*runtime-asserts*', or false if not set. You can toggle check-asserts? with (check-asserts bool)." [spec x] - `(if cljs.spec/*compile-asserts* - (if cljs.spec/*runtime-asserts* - (cljs.spec/assert* ~spec ~x) + `(if *compile-asserts* + (if *runtime-asserts* + (assert* ~spec ~x) ~x) ~x)) diff --git a/src/main/cljs/cljs/spec/impl/gen.cljc b/src/main/cljs/cljs/spec/impl/gen.cljc index 9200a0bf3..98bab2cb9 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljc +++ b/src/main/cljs/cljs/spec/impl/gen.cljc @@ -34,7 +34,7 @@ generator that delegates to that, but delays creation until used." [& body] - `(cljs.spec.impl.gen/delay-impl (c/delay ~@body))) + `(delay-impl (c/delay ~@body))) (defmacro ^:skip-wiki lazy-combinator "Implementation macro, do not call directly." diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index ac6ae5ffe..3957c1f9b 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -43,7 +43,7 @@ returns the set of all symbols naming vars in those nses." (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." [& body] - `(binding [cljs.spec.test/*instrument-enabled* nil] + `(binding [*instrument-enabled* nil] ~@body)) (defmacro instrument-1 @@ -51,7 +51,7 @@ returns the set of all symbols naming vars in those nses." (let [v (ana-api/resolve &env s)] (when v (swap! instrumented-vars conj (:name v)) - `(let [checked# (cljs.spec.test/instrument-1* ~s (var ~s) ~opts)] + `(let [checked# (instrument-1* ~s (var ~s) ~opts)] (when checked# (set! ~s checked#)) '~(:name v))))) @@ -60,7 +60,7 @@ returns the set of all symbols naming vars in those nses." (let [v (ana-api/resolve &env s)] (when v (swap! instrumented-vars disj (:name v)) - `(let [raw# (cljs.spec.test/unstrument-1* ~s (var ~s))] + `(let [raw# (unstrument-1* ~s (var ~s))] (when raw# (set! ~s raw#)) '~(:name v))))) diff --git a/src/main/cljs/cljs/test.cljc b/src/main/cljs/cljs/test.cljc index 377a83eb9..725c7570c 100644 --- a/src/main/cljs/cljs/test.cljc +++ b/src/main/cljs/cljs/test.cljc @@ -34,10 +34,10 @@ `(let [values# (list ~@args) result# (apply ~pred values#)] (if result# - (cljs.test/do-report + (do-report {:type :pass, :message ~msg, :expected '~form, :actual (cons ~pred values#)}) - (cljs.test/do-report + (do-report {:type :fail, :message ~msg, :expected '~form, :actual (list '~'not (cons '~pred values#))})) result#))) @@ -48,10 +48,10 @@ [msg form] `(let [value# ~form] (if value# - (cljs.test/do-report + (do-report {:type :pass, :message ~msg, :expected '~form, :actual value#}) - (cljs.test/do-report + (do-report {:type :fail, :message ~msg, :expected '~form, :actual value#})) value#)) @@ -72,7 +72,7 @@ (defmethod assert-expr :always-fail [menv msg form] ;; nil test: always fail - `(cljs.test/do-report {:type :fail, :message ~msg})) + `(do-report {:type :fail, :message ~msg})) (defmethod assert-expr :default [menv msg form] (if (and (sequential? form) @@ -86,10 +86,10 @@ object# ~(nth form 2)] (let [result# (instance? klass# object#)] (if result# - (cljs.test/do-report + (do-report {:type :pass, :message ~msg, :expected '~form, :actual (type object#)}) - (cljs.test/do-report + (do-report {:type :fail, :message ~msg, :expected '~form, :actual (type object#)})) result#))) @@ -102,11 +102,11 @@ body (nthnext form 2)] `(try ~@body - (cljs.test/do-report + (do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) (catch ~klass e# - (cljs.test/do-report + (do-report {:type :pass, :message ~msg, :expected '~form, :actual e#}) e#)))) @@ -121,14 +121,14 @@ body (nthnext form 3)] `(try ~@body - (cljs.test/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) + (do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) (catch ~klass e# (let [m# (.-message e#)] (if (re-find ~re m#) - (cljs.test/do-report + (do-report {:type :pass, :message ~msg, :expected '~form, :actual e#}) - (cljs.test/do-report + (do-report {:type :fail, :message ~msg, :expected '~form, :actual e#})) e#))))) @@ -140,7 +140,7 @@ `(try ~(assert-expr &env msg form) (catch :default t# - (cljs.test/do-report + (do-report {:type :error, :message ~msg, :expected '~form, :actual t#})))) @@ -194,11 +194,11 @@ but must occur inside a test function (deftest)." ([string & body] `(do - (cljs.test/update-current-env! [:testing-contexts] conj ~string) + (update-current-env! [:testing-contexts] conj ~string) (try ~@body (finally - (cljs.test/update-current-env! [:testing-contexts] rest)))))) + (update-current-env! [:testing-contexts] rest)))))) ;; ============================================================================= ;; Defining Tests @@ -258,28 +258,28 @@ env (gensym "env") summary (gensym "summary")] `(let [~env ~(if is-ns - `(cljs.test/empty-env) + `(empty-env) env-or-ns) ~summary (cljs.core/volatile! {:test 0 :pass 0 :fail 0 :error 0 :type :summary})] (concat ~@(map (fn [ns] - `(concat (cljs.test/test-ns-block ~env ~ns) + `(concat (test-ns-block ~env ~ns) [(fn [] (cljs.core/vswap! ~summary (partial merge-with +) (:report-counters - (cljs.test/get-and-clear-env!))))])) + (get-and-clear-env!))))])) (if is-ns (concat [env-or-ns] namespaces) namespaces)) [(fn [] - (cljs.test/set-env! ~env) - (cljs.test/do-report (deref ~summary)) - (cljs.test/report (assoc (deref ~summary) :type :end-run-tests)) - (cljs.test/clear-env!))])))) + (set-env! ~env) + (do-report (deref ~summary)) + (report (assoc (deref ~summary) :type :end-run-tests)) + (clear-env!))])))) (defmacro run-tests "Runs all tests in the given namespaces; prints results. @@ -287,23 +287,23 @@ value due to the possiblity of asynchronous execution. To detect test completion add a :end-run-tests method case to the cljs.test/report multimethod." - ([] `(run-tests (cljs.test/empty-env) '~ana/*cljs-ns*)) + ([] `(run-tests (empty-env) '~ana/*cljs-ns*)) ([env-or-ns] (if (ns? env-or-ns) - `(run-tests (cljs.test/empty-env) ~env-or-ns) + `(run-tests (empty-env) ~env-or-ns) `(run-tests ~env-or-ns '~ana/*cljs-ns*))) ([env-or-ns & namespaces] - `(cljs.test/run-block (run-tests-block ~env-or-ns ~@namespaces)))) + `(run-block (run-tests-block ~env-or-ns ~@namespaces)))) (defmacro run-all-tests "Runs all tests in all namespaces; prints results. Optional argument is a regular expression; only namespaces with names matching the regular expression (with re-matches) will be tested." - ([] `(cljs.test/run-all-tests nil (cljs.test/empty-env))) - ([re] `(cljs.test/run-all-tests ~re (cljs.test/empty-env))) + ([] `(run-all-tests nil (empty-env))) + ([re] `(run-all-tests ~re (empty-env))) ([re env] - `(cljs.test/run-tests ~env + `(run-tests ~env ~@(map (fn [ns] `(quote ~ns)) (cond->> (ana-api/all-ns) @@ -311,18 +311,18 @@ (defmacro test-all-vars-block ([[quote ns]] - `(let [env# (cljs.test/get-current-env)] + `(let [env# (get-current-env)] (concat [(fn [] (when (nil? env#) - (cljs.test/set-env! (cljs.test/empty-env))) + (set-env! (empty-env))) ~(when (ana-api/ns-resolve ns 'cljs-test-once-fixtures) - `(cljs.test/update-current-env! [:once-fixtures] assoc '~ns + `(update-current-env! [:once-fixtures] assoc '~ns ~(symbol (name ns) "cljs-test-once-fixtures"))) ~(when (ana-api/ns-resolve ns 'cljs-test-each-fixtures) - `(cljs.test/update-current-env! [:each-fixtures] assoc '~ns + `(update-current-env! [:each-fixtures] assoc '~ns ~(symbol (name ns) "cljs-test-each-fixtures"))))] - (cljs.test/test-vars-block + (test-vars-block [~@(->> (ana-api/ns-interns ns) (filter (fn [[_ v]] (:test v))) (sort-by (fn [[_ v]] (:line v))) @@ -330,13 +330,13 @@ `(var ~(symbol (name ns) (name k))))))]) [(fn [] (when (nil? env#) - (cljs.test/clear-env!)))])))) + (clear-env!)))])))) (defmacro test-all-vars "Calls test-vars on every var with :test metadata interned in the namespace, with fixtures." [[quote ns :as form]] - `(cljs.test/run-block + `(run-block (concat (test-all-vars-block ~form) [(fn [] (report {:type :end-test-all-vars :ns ~form}))]))) @@ -348,15 +348,15 @@ (assert (and (= quote 'quote) (symbol? ns)) "Argument to test-ns must be a quoted symbol") (assert (ana-api/find-ns ns) (str "Namespace " ns " does not exist")) `[(fn [] - (cljs.test/set-env! ~env) - (cljs.test/do-report {:type :begin-test-ns, :ns ~form}) + (set-env! ~env) + (do-report {:type :begin-test-ns, :ns ~form}) ;; If the namespace has a test-ns-hook function, call that: ~(if-let [v (ana-api/ns-resolve ns 'test-ns-hook)] `(~(symbol (name ns) "test-ns-hook")) ;; Otherwise, just test every var in the namespace. - `(cljs.test/block (cljs.test/test-all-vars-block ~form)))) + `(block (test-all-vars-block ~form)))) (fn [] - (cljs.test/do-report {:type :end-test-ns, :ns ~form}))])) + (do-report {:type :end-test-ns, :ns ~form}))])) (defmacro test-ns "If the namespace defines a function named test-ns-hook, calls that. @@ -365,12 +365,12 @@ Internally binds *report-counters* to a ref initialized to *initial-report-counters*. " - ([ns] `(cljs.test/test-ns (cljs.test/empty-env) ~ns)) + ([ns] `(test-ns (empty-env) ~ns)) ([env [quote ns :as form]] - `(cljs.test/run-block - (concat (cljs.test/test-ns-block ~env ~form) + `(run-block + (concat (test-ns-block ~env ~form) [(fn [] - (cljs.test/clear-env!))])))) + (clear-env!))])))) ;; ============================================================================= ;; Fixes From a8fb504b019d925db6394eb9e33657aad2abda1b Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Mon, 19 Sep 2016 11:41:37 +1000 Subject: [PATCH 2019/4033] CLJS-1784 - nth doesn't throw on strings or arrays nth now throws on arrays and strings when n is out of bounds. --- src/main/cljs/cljs/core.cljs | 18 ++++++++++-------- src/test/cljs/cljs/core_test.cljs | 11 +++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c75c9a975..cbf1292cd 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1742,7 +1742,7 @@ reduces them without incurring seq initialization" ([coll n] (cond (not (number? n)) - (throw (js/Error. "index argument to nth must be a number")) + (throw (js/Error. "Index argument to nth must be a number")) (nil? coll) coll @@ -1751,12 +1751,14 @@ reduces them without incurring seq initialization" (-nth ^not-native coll n) (array? coll) - (when (< n (.-length coll)) - (aget coll n)) + (if (and (>= n 0) (< n (.-length coll))) + (aget coll n) + (throw (js/Error. "Index out of bounds"))) (string? coll) - (when (< n (.-length coll)) - (.charAt coll n)) + (if (and (>= n 0) (< n (.-length coll))) + (.charAt coll n) + (throw (js/Error. "Index out of bounds"))) (implements? ISeq coll) (linear-traversal-nth coll n) @@ -1770,7 +1772,7 @@ reduces them without incurring seq initialization" ([coll n not-found] (cond (not (number? n)) - (throw (js/Error. "index argument to nth must be a number.")) + (throw (js/Error. "Index argument to nth must be a number.")) (nil? coll) not-found @@ -1779,12 +1781,12 @@ reduces them without incurring seq initialization" (-nth ^not-native coll n not-found) (array? coll) - (if (< n (.-length coll)) + (if (and (>= n 0) (< n (.-length coll))) (aget coll n) not-found) (string? coll) - (if (< n (.-length coll)) + (if (and (>= n 0) (< n (.-length coll))) (.charAt coll n) not-found) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5f6b045a4..6c5b02d6b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -3293,6 +3293,17 @@ (is (= (foo-fn foo {:a 1 :b 2}) {:c 3})))) +(deftest test-cljs-1748 + (is (thrown? js/Error (nth (array 0 1 2) 3))) + (is (thrown? js/Error (nth (array 0 1 2) -1))) + (is (= (nth (array 0 1 2) 3 :not-found) :not-found)) + (is (= (nth (array 0 1 2) -1 :not-found) :not-found)) + + (is (thrown? js/Error (nth "012" 3))) + (is (thrown? js/Error (nth "012" -1))) + (is (= (nth "012" 3 :not-found) :not-found)) + (is (= (nth "012" -1 :not-found) :not-found))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From c884022c21510707aabd907725dab81ebb14377a Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Sat, 27 Aug 2016 23:16:41 +0100 Subject: [PATCH 2020/4033] CLJS-1765: Empty iterator for hash maps with nil key --- src/main/cljs/cljs/core.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index cbf1292cd..a2125d289 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7115,12 +7115,12 @@ reduces them without incurring seq initialization" (deftype HashMapIter [nil-val root-iter ^:mutable seen] Object (hasNext [_] - (and ^boolean seen ^boolean (.hasNext root-iter))) + (or (not seen) ^boolean (.hasNext root-iter))) (next [_] (if-not ^boolean seen (do (set! seen true) - nil-val) + [nil nil-val]) (.next root-iter))) (remove [_] (js/Error. "Unsupported operation"))) @@ -7151,7 +7151,7 @@ reduces them without incurring seq initialization" IIterable (-iterator [coll] - (let [root-iter (if ^boolean root (-iterator root) nil-iter)] + (let [root-iter (if ^boolean root (-iterator root) (nil-iter))] (if has-nil? (HashMapIter. nil-val root-iter false) root-iter))) From 7923f80fd50b3a7d1f808dd746758a1375a8e25d Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Mon, 19 Sep 2016 11:03:35 +1000 Subject: [PATCH 2021/4033] added hash-map test file --- src/test/cljs/cljs/hash_map_test.cljs | 23 +++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 6 ++++-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/test/cljs/cljs/hash_map_test.cljs diff --git a/src/test/cljs/cljs/hash_map_test.cljs b/src/test/cljs/cljs/hash_map_test.cljs new file mode 100644 index 000000000..1506e0c46 --- /dev/null +++ b/src/test/cljs/cljs/hash_map_test.cljs @@ -0,0 +1,23 @@ +(ns cljs.hash-map-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]])) + +(defn iter->set + "Return a set of elements in iterator" + [iter] + (loop [entries #{} iter iter] + (if (.hasNext iter) + (recur (conj entries (.next iter)) iter) + entries))) + +(deftest test-cljs-1765 + (is (true? (.hasNext (-iterator (hash-map nil 1))))) + (is (true? (.hasNext (-iterator (hash-map :a 1 :b 2 :c 3 :d 4 nil 5))))) + (is (= #{[nil 1]} + (-> (hash-map nil 1) + (-iterator) + (iter->set)))) + (is (= #{[:a 1] [:b 2] [:c 3] [:d 4] [nil 5]} + (-> (hash-map :a 1 :b 2 :c 3 :d 4 nil 5) + (-iterator) + (iter->set))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index a23fd8b1f..549633e6d 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -17,7 +17,8 @@ [cljs.import-test] [cljs.ns-test.foo] [cljs.pprint] - [cljs.clojure-alias-test])) + [cljs.clojure-alias-test] + [cljs.hash-map-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -40,4 +41,5 @@ 'foo.ns-shadow-test 'cljs.import-test 'cljs.pprint - 'cljs.clojure-alias-test) + 'cljs.clojure-alias-test + 'cljs.hash-map-test) From d9394dbd570e0c37bd77fa90d37cdc073f324a98 Mon Sep 17 00:00:00 2001 From: Lauri Oherd Date: Fri, 23 Sep 2016 19:51:17 +0300 Subject: [PATCH 2022/4033] CLJS-1788: Port CLJ-2004: include retag in multi-spec form --- src/main/cljs/cljs/spec.cljs | 2 +- src/test/cljs/cljs/spec_test.cljs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 331d79aed..c1a70daa1 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -473,7 +473,7 @@ (when (every? identity gs) (gen/one-of gs))))) (with-gen* [_ gfn] (multi-spec-impl form mmvar retag gfn)) - (describe* [_] `(multi-spec ~form)))))) + (describe* [_] `(multi-spec ~form ~retag)))))) (defn ^:skip-wiki tuple-impl "Do not call this directly, use 'tuple'" diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index e9415fd9d..28fc5cab6 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -76,6 +76,12 @@ (deftest test-cljs-1757 (is (s/exercise-fn `cljs-1757-x))) +(deftest test-cljs-1788 + (defmulti mm :mm/type) + (s/def ::foo-1788 (s/multi-spec mm :mm/type)) + (is (= (s/form ::foo-1788) + '(cljs.spec/multi-spec cljs.spec-test/mm :mm/type)))) + ;; Copied from Clojure spec tests (def even-count? #(even? (count %))) From 4c515751bb4dddc223a8721317beecc3ba35d169 Mon Sep 17 00:00:00 2001 From: Joshua Miller Date: Wed, 21 Sep 2016 12:18:54 -0700 Subject: [PATCH 2023/4033] Add test for conforming sequences. --- src/test/cljs/cljs/spec_test.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 28fc5cab6..18cee9396 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -216,6 +216,7 @@ coll [] [] nil coll [:a] [:a] nil coll [:a :b] [:a :b] nil + coll (map identity [:a :b]) '(:a :b) nil ;;coll [:a "b"] ::s/invalid '[{:pred (coll-checker keyword?), :val [:a b]}] ))) From 7ef1ed8debc647ddef847ac85262b24bfe96c48a Mon Sep 17 00:00:00 2001 From: Joshua Miller Date: Wed, 21 Sep 2016 12:51:31 -0700 Subject: [PATCH 2024/4033] Port sequence check into conform. --- src/main/cljs/cljs/spec.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index c1a70daa1..bbf1497cd 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -692,7 +692,8 @@ (assoc ret (nth (if conform-keys cv v) 0) (nth cv 1)))) identity] - (list? x) [empty addcv reverse] + (c/or (list? conform-into) (seq? conform-into) (c/and (not conform-into) (c/or (list? x) (seq? x)))) + [empty addcv reverse] :else [#(empty (c/or conform-into %)) addcv identity]))] (reify From 00e46feb03e7c4e7784f6b0759762d537ea1daaa Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 14:31:20 -0400 Subject: [PATCH 2025/4033] CLJS-1785: Warn on reference to js/foo shadowed by local binding --- src/main/clojure/cljs/analyzer.cljc | 22 +++++++++++++++------- src/test/clojure/cljs/analyzer_tests.clj | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index da7831974..5b6209e85 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -138,7 +138,8 @@ :ns-var-clash true :extend-type-invalid-method-shape true :unsupported-js-module-type true - :unsupported-preprocess-value true}) + :unsupported-preprocess-value true + :js-shadowed-by-local true}) (def js-reserved #{"arguments" "abstract" "boolean" "break" "byte" "case" @@ -409,6 +410,10 @@ (str "Unsupported preprocess value " preprocess " for foreign library " file ".")) +(defmethod error-message :js-shadowed-by-local + [warning-type {:keys [name]}] + (str name " is shadowed by a local")) + (defn default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] @@ -740,12 +745,15 @@ warnings about unresolved vars." ([env sym] (resolve-var env sym nil)) ([env sym confirm] + (let [locals (:locals env)] (if #?(:clj (= "js" (namespace sym)) :cljs (identical? "js" (namespace sym))) - {:name sym :ns 'js} - (let [s (str sym) - lcls (:locals env) - lb (get lcls sym)] + (do + (when (contains? locals (-> sym name symbol)) + (warning :js-shadowed-by-local env {:name sym})) + {:name sym :ns 'js}) + (let [s (str sym) + lb (get locals sym)] (cond (not (nil? lb)) lb @@ -771,7 +779,7 @@ (let [idx (.indexOf s ".") prefix (symbol (subs s 0 idx)) suffix (subs s (inc idx)) - lb (get lcls prefix)] + lb (get locals prefix)] (if-not (nil? lb) {:name (symbol (str (:name lb)) suffix)} (let [cur-ns (-> env :ns :name) @@ -816,7 +824,7 @@ (confirm env full-ns sym)) (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) - :ns full-ns}))))))) + :ns full-ns})))))))) (defn resolve-existing-var "Given env, an analysis environment, and sym, a symbol, resolve an existing var. diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 560a7a56c..63a4bb421 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -6,6 +6,12 @@ [cljs.analyzer.api :as ana-api]) (:use clojure.test)) +(defn collecting-warning-handler [state] + (fn [warning-type env extra] + (when (warning-type a/*cljs-warnings*) + (when-let [s (a/error-message warning-type extra)] + (swap! state conj s))))) + ;;****************************************************************************** ;; cljs-warnings tests ;;****************************************************************************** @@ -456,3 +462,12 @@ {:excludes #{} :renames {}})) (is (set? (:excludes parsed))))) + +(deftest test-cljs-1785-js-shadowed-by-local + (let [ws (atom [])] + (a/with-warning-handlers [(collecting-warning-handler ws)] + (a/analyze ns-env + '(fn [foo] + (let [x js/foo] + (println x))))) + (is (.startsWith (first @ws) "js/foo is shadowed by a local")))) \ No newline at end of file From 5ad018ab442fd038c954e8996c7d6c0a456f2fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 19 Sep 2016 18:00:41 +0100 Subject: [PATCH 2026/4033] CLJS-1781: Add cljs.hash-map-test to self-parity tests --- src/test/self/self_parity/test.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 19d95d4e8..049c56144 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -259,7 +259,8 @@ [cljs.ns-test.foo] #_[cljs.pprint] [cljs.spec-test] - [cljs.clojure-alias-test])) + [cljs.clojure-alias-test] + [cljs.hash-map-test])) (fn [{:keys [value error]}] (if error (prn error) @@ -281,7 +282,8 @@ 'cljs.import-test #_'cljs.pprint 'cljs.spec-test - 'cljs.clojure-alias-test) + 'cljs.clojure-alias-test + 'cljs.hash-map-test) (fn [{:keys [value error]}] (when error (prn error))))))))) From 30ab498888bb228d29a80c6a268d9d8df96b36e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 19 Sep 2016 15:02:36 +0100 Subject: [PATCH 2027/4033] CLJS-1563: :source-map option to cljs.build.api/build should take nil --- src/main/clojure/cljs/closure.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d359b073b..f2267fb0d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1807,6 +1807,7 @@ (defn check-source-map [{:keys [output-to source-map output-dir optimizations] :as opts}] "When :source-map is specified in opts, " (when (and (contains? opts :source-map) + (some? (:source-map opts)) (not (= optimizations :none))) (assert (and (or (contains? opts :output-to) (contains? opts :modules)) From 8399062f0179770580f53ac331485c5e944a773c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 15:03:13 -0400 Subject: [PATCH 2028/4033] CLJS-1787: Make cljs.spec explain pluggable same as Clojure commit 99ab306 --- src/main/cljs/cljs/spec.cljs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index bbf1497cd..c69fc0048 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -177,8 +177,8 @@ [spec x] (explain-data* spec [] (if-let [name (spec-name spec)] [name] []) [] x)) -(defn explain-out - "prints an explanation to *out*." +(defn explain-printer + "Default printer for explain-data. nil indicates a successful validation." [ed] (if ed (print @@ -209,6 +209,14 @@ (newline))))) (println "Success!"))) +(def ^:dynamic *explain-out* explain-printer) + +(defn explain-out + "Prints explanation data (per 'explain-data') to *out* using the printer in *explain-out*, + by default explain-printer." + [ed] + (*explain-out* ed)) + (defn explain "Given a spec and a value that fails to conform, prints an explanation to *out*." [spec x] From 0ed939815738488efc2b627b8eeb3061a3c4dcd8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 15:21:57 -0400 Subject: [PATCH 2029/4033] CLJS-1710: spec/double-in not implemented add float?, double?, infinite? predicates add double-in spec :exclude float from cljs.pprint ns --- src/main/cljs/cljs/core.cljs | 16 ++++++++++++++++ src/main/cljs/cljs/pprint.cljs | 2 +- src/main/cljs/cljs/spec.cljc | 17 +++++++++++++++++ src/main/cljs/cljs/spec/impl/gen.cljs | 3 ++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a2125d289..6b5c45503 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2185,6 +2185,22 @@ reduces them without incurring seq initialization" :else false)) +(defn ^boolean float? + "Returns true for JavaScript numbers, false otherwise." + [x] + (number? x)) + +(defn ^boolean double? + "Returns true for JavaScript numbers, false otherwise." + [x] + (number? x)) + +(defn ^boolean infinite? + "Returns true for Infinity and -Infinity values." + [x] + (or (identical? x js/Number.POSITIVE_INFINITY) + (identical? x js/Number.NEGATIVE_INFINITY))) + (defn ^boolean contains? "Returns true if key is present in the given collection, otherwise returns false. Note that for numerically indexed collections like diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 6f0c2e2d6..208d3ced6 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.pprint - (:refer-clojure :exclude [deftype print println pr prn]) + (:refer-clojure :exclude [deftype print println pr prn float?]) (:require-macros [cljs.pprint :as m :refer [with-pretty-writer getf setf deftype pprint-logical-block print-length-loop diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 44520aaca..0b2f5e445 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -443,6 +443,23 @@ `(spec (and c/int? #(int-in-range? ~start ~end %)) :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) +(defmacro double-in + "Specs a 64-bit floating point number. Options: + + :infinite? - whether +/- infinity allowed (default true) + :NaN? - whether NaN allowed (default true) + :min - minimum value (inclusive, default none) + :max - maximum value (inclusive, default none)" + [& {:keys [infinite? NaN? min max] + :or {infinite? true NaN? true} + :as m}] + `(spec (and c/double? + ~@(when-not infinite? '[#(not (infinite? %))]) + ~@(when-not NaN? '[#(not (js/isNaN %))]) + ~@(when max `[#(<= % ~max)]) + ~@(when min `[#(<= ~min %)])) + :gen #(gen/double* ~m))) + (defmacro merge "Takes map-validating specs (e.g. 'keys' specs) and returns a spec that returns a conformed map satisfying all of the diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index 0400eedac..e55a482c1 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -94,7 +94,8 @@ gen-builtins pos-int? (large-integer* {:min 1}) neg-int? (large-integer* {:max -1}) nat-int? (large-integer* {:min 0}) - ;float? (double) + float? (double) + double? (double) string? (string-alphanumeric) ident? (one-of [(keyword-ns) (symbol-ns)]) simple-ident? (one-of [(keyword) (symbol)]) From eaebc1cdb39e3fed9e7e36ec1315337b77a0350d Mon Sep 17 00:00:00 2001 From: Rohit Aggarwal Date: Tue, 14 Jun 2016 16:55:37 +0100 Subject: [PATCH 2030/4033] CLJS-1682 - :foreign-libs with module conversion does not works properly if it is used form deps.cljs Look at modules found in `:ups-foreign-libs` for google closure conversion. --- src/main/clojure/cljs/closure.clj | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f2267fb0d..5059ce59c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1538,7 +1538,8 @@ (.getAstRoot input closure-compiler))) (defn get-source-files [opts] - (->> (:foreign-libs opts) + (->> (concat (:foreign-libs opts) + (:ups-foreign-libs opts)) (filter #(let [module-type (:module-type %)] (or (= module-type :amd) (= module-type :commonjs) @@ -1735,7 +1736,7 @@ ) -(defn get-upstream-deps* +(defn get-upstream-deps* "returns a merged map containing all upstream dependencies defined by libraries on the classpath." ([] @@ -1903,13 +1904,9 @@ (not (false? (:static-fns opts))) (assoc :static-fns true) (not (false? (:optimize-constants opts))) (assoc :optimize-constants true))))) -(defn process-js-modules - "Given the current compiler options, converts JavaScript modules to Google - Closure modules and writes them to disk. Adds mapping from original module - namespace to new module namespace to compiler env. Returns modified compiler - options where new modules are passed with :libs option." - [opts] - (let [js-modules (filter :module-type (:foreign-libs opts))] +(defn- process-js-modules* + [opts k] + (let [js-modules (filter :module-type (k opts))] (reduce (fn [new-opts {:keys [file module-type] :as lib}] (if (or (and (= module-type :commonjs) can-convert-commonjs?) (and (= module-type :amd) can-convert-amd?) @@ -1921,11 +1918,21 @@ #(update-in % [:js-module-index] assoc provide module-name))) (-> new-opts (update-in [:libs] (comp vec conj) (:out-file ijs)) - (update-in [:foreign-libs] + (update-in [k] (comp vec (fn [libs] (remove #(= (:file %) file) libs)))))) new-opts)) opts js-modules))) +(defn process-js-modules + "Given the current compiler options, converts JavaScript modules to Google + Closure modules and writes them to disk. Adds mapping from original module + namespace to new module namespace to compiler env. Returns modified compiler + options where new modules are passed with :libs option." + [opts] + (-> opts + (process-js-modules* :foreign-libs) + (process-js-modules* :ups-foreign-libs))) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] From 2283457c150d5749359dd7f267e2181565446773 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 15:51:21 -0400 Subject: [PATCH 2031/4033] break out some primitive tests --- src/test/cljs/cljs/core_test.cljs | 318 +---------------------- src/test/cljs/cljs/primitives_test.cljs | 323 ++++++++++++++++++++++++ src/test/cljs/cljs/test_runner.cljs | 2 + src/test/self/self_parity/test.cljs | 2 + 4 files changed, 335 insertions(+), 310 deletions(-) create mode 100644 src/test/cljs/cljs/primitives_test.cljs diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 6c5b02d6b..5523bdfa0 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1,322 +1,20 @@ +; 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.core-test (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] [clojure.string :as s] [clojure.set :as set])) -(deftest test-js-primitives - ;; js primitives - (let [keys #(vec (js-keys %))] - (testing "Testing js primitives" - (is (= [] (keys (js-obj)) (keys (apply js-obj [])))) - (is (= ["x"] (keys (js-obj "x" "y")) (keys (apply js-obj ["x" "y"]))))))) - -(deftest test-equiv - (testing "Testing -equiv" - (is (= 1)) - (is (= 1 1)) - (is (= 1 1 1)) - (is (= 1 1 1 1)) - (is (not (= 1 2))) - (is (not (= 1 2 1))) - (is (not (= 1 1 2))) - (is (not (= 1 1 2 1))) - (is (not (= 1 1 1 2))))) - -(deftest test-arithmetic - (testing "Testing addition" - (is (= (+) 0)) - (is (= (apply + []) 0)) - (is (= (+ 1) 1)) - (is (= (apply + [1]) 1)) - (is (= (+ 1 1) 2)) - (is (= (apply + [1 1]) 2)) - (is (= (+ 1 2 3) 6)) - (is (= (apply + [1 2 3]) 6))) - - (testing "Testing subtraction" - (is (= (- 1) -1)) - (is (= (apply - [1]) -1)) - (is (= (- 1 1) 0)) - (is (= (apply - [1 1]) 0)) - (is (= (- 3 2 1) 0)) - (is (= (apply - [3 2 1]) 0))) - - (testing "Testing multiplication" - (is (= (*) 1)) - (is (= (apply * []) 1)) - (is (= (* 2) 2)) - (is (= (apply * [2]) 2)) - (is (= (* 2 3) 6)) - (is (= (apply * [2 3]) 6))) - - (testing "Testing division" - (is (= (/ 2) 0.5)) - (is (= (apply / [2]) 0.5)) - (is (= (/ 6 2) 3)) - (is (= (apply / [6 2]) 3)) - (is (= (/ 6 3 2) 1)) - (is (= (apply / [6 3 2]) 1))) - - (testing "Testing less than" - (is (= (< 1) true)) - (is (= (apply < [1]) true)) - (is (= (< 1 2) true)) - (is (= (apply < [1 2]) true)) - (is (= (< 1 1) false)) - (is (= (apply < [1 1]) false)) - (is (= (< 2 1) false)) - (is (= (apply < [2 1]) false)) - (is (= (< 1 2 3) true)) - (is (= (apply < [1 2 3]) true)) - (is (= (< 1 1 3) false)) - (is (= (apply < [1 1 3]) false)) - (is (= (< 3 1 1) false)) - (is (= (apply < [3 1 1]) false))) - - (testing "Testing less than or equal to" - (is (= (<= 1) true)) - (is (= (apply <= [1]) true)) - (is (= (<= 1 1) true)) - (is (= (apply <= [1 1]) true)) - (is (= (<= 1 2) true)) - (is (= (apply <= [1 2]) true)) - (is (= (<= 2 1) false)) - (is (= (apply <= [2 1]) false)) - (is (= (<= 1 2 3) true)) - (is (= (apply <= [1 2 3]) true)) - (is (= (<= 1 1 3) true)) - (is (= (apply <= [1 1 3]) true)) - (is (= (<= 3 1 1) false)) - (is (= (apply <= [3 1 1]) false))) - - (testing "Testing greater than" - (is (= (> 1) true)) - (is (= (apply > [1]) true)) - (is (= (> 2 1) true)) - (is (= (apply > [2 1]) true)) - (is (= (> 1 1) false)) - (is (= (apply > [1 1]) false)) - (is (= (> 1 2) false)) - (is (= (apply > [1 2]) false)) - (is (= (> 3 2 1) true)) - (is (= (apply > [3 2 1]) true)) - (is (= (> 3 1 1) false)) - (is (= (apply > [3 1 1]) false)) - (is (= (> 1 1 3) false)) - (is (= (apply > [1 1 3]) false))) - - (testing "Testing greater than or equal to" - (is (= (>= 1) true)) - (is (= (apply >= [1]) true)) - (is (= (>= 2 1) true)) - (is (= (apply >= [2 1]) true)) - (is (= (>= 1 1) true)) - (is (= (apply >= [1 1]) true)) - (is (= (>= 1 2) false)) - (is (= (apply >= [1 2]) false)) - (is (= (>= 3 2 1) true)) - (is (= (apply >= [3 2 1]) true)) - (is (= (>= 3 1 1) true)) - (is (= (apply >= [3 1 1]) true)) - (is (= (>= 3 1 2) false)) - (is (= (apply >= [3 1 2]) false)) - (is (= (>= 1 1 3) false)) - (is (= (apply >= [1 1 3]) false))) - - (testing "Testing dec/inc" - (is (= (dec 1) 0)) - (is (= (apply dec [1]) 0)) - (is (= (inc 0) 1)) - (is (= (apply inc [0]) 1))) - - (testing "Testing zero? pos? neg? even? odd?" - (is (= (zero? 0) true)) - (is (= (apply zero? [0]) true)) - (is (= (zero? 1) false)) - (is (= (apply zero? [1]) false)) - (is (= (zero? -11) false)) - (is (= (apply zero? [-11]) false)) - (is (= (pos? 0) false)) - (is (= (apply pos? [0]) false)) - (is (= (pos? 1) true)) - (is (= (apply pos? [1]) true)) - (is (= (pos? -1) false)) - (is (= (apply pos? [-1]) false)) - (is (= (neg? -1) true)) - (is (= (apply neg? [-1]) true)) - (is (neg? -1)) - (is (not (neg? 1))) - (is (neg? -1.765)) - (is (not (neg? 0))) - (is (= [true false true false true false true false] - (map integer? - [1 1.00001 0x7e7 [] (- 88 1001991881) :foo 0 "0"]))) - (is (= [true false true false true false] - (map odd? [1 2 3 4 -1 0]))) - (is (= [true false true false true true] - (map even? [2 3 4 5 -2 0])))) - - (testing "Testing max / min" - (is (= (max 1) 1)) - (is (= (apply max [1]) 1)) - (is (= (max 1 2) 2)) - (is (= (apply max [1 2]) 2)) - (is (= (max 2 1) 2)) - (is (= (apply max [2 1]) 2)) - (is (= (max 1 2 3) 3)) - (is (= (apply max [1 2 3]) 3)) - (is (= (max 1 3 2) 3)) - (is (= (apply max [1 3 2]) 3)) - - (is (= (min 1) 1)) - (is (= (apply min [1]) 1)) - (is (= (min 1 2) 1)) - (is (= (apply min [1 2]) 1)) - (is (= (min 2 1) 1)) - (is (= (apply min [2 1]) 1)) - (is (= (min 1 2 3) 1)) - (is (= (apply min [1 2 3]) 1)) - (is (= (min 2 1 3) 1)) - (is (= (apply min [3 1 3]) 1))) - - (testing "Testing mod" - (is (= (mod 4 2) 0)) - (is (= (apply mod [4 2]) 0)) - (is (= (mod 3 2) 1)) - (is (= (apply mod [3 2]) 1)) - (is (= (mod -2 5) 3))) - - (testing "Testing numeric equality in collections" - (is (= [4 3 2 1 0] - (loop [i 0 j ()] - (if (< i 5) - (recur (inc i) (conj j (fn [] i))) - (map #(%) j))))) - (is (= [[1 1] [1 2] [1 3] [2 1] [2 2] [2 3]] - (map #(%) (for [i [1 2] j [1 2 3]] (fn [] [i j])))))) - - (testing "Testing integer? predicate" - (is (integer? 0)) - (is (integer? 42)) - (is (integer? -42)) - (is (not (integer? ""))) - (is (not (integer? 1e308))) - (is (not (integer? js/Infinity))) - (is (not (integer? (- js/Infinity)))) - (is (not (integer? js/NaN)))) - - (testing "Testing integer coercions" - (is (= 42 (int 42.5))) - (is (integer? (int 42.5))) - (is (= 42 (long 42.5))) - (is (integer? (long 42.5))) - (is (= -1 (int -1.5))) - (is (= -9 (long -9.8)))) - - (testing "Testing numeric equality from collection" - (is (= 2 (:b {:a 1 :b 2}))) - (is (= 2 ('b '{:a 1 b 2}))) - (is (= 2 ({:a 1 :b 2} :b))) - (is (= 2 ({1 1 2 2} 2))) - (is (= 2 (:a {:b 1} 2))) - (is (= 2 (:a {} 2))) - (is (= 2 ({:b 1} :a 2))) - (is (= 2 ({} :a 2))) - (is (= nil (:a {}))) - (is (= nil (:a ""))) - (is (= 2 (:a "" 2))) - (is (= 2 (#{1 2 3} 2))) - (is (= 1 (apply :a '[{:a 1 a 2}]))) - (is (= 1 (apply 'a '[{a 1 :b 2}]))) - (is (= 1 (apply {:a 1} [:a]))) - (is (= 2 (apply {:a 1} [:b 2])))) - - (testing "Testing quot" - (is (= (quot 4 2) 2)) - (is (= (quot 3 2) 1)) - (is (= (quot 6 4) 1)) - (is (= (quot 0 5) 0)) - (is (= (quot 42 5) 8)) - (is (= (quot 42 -5) -8)) - (is (= (quot -42 -5) 8)) - (is (= (quot 9 3) 3)) - (is (= (quot 9 -3) -3)) - (is (= (quot -9 3) -3)) - (is (= (quot 2 -5) 0)) - (is (= (quot -2 5) 0)) - (is (= (quot 0 3) 0)) - (is (= (quot 0 -3) 0))) - - (testing "Testing mod" - (is (= (mod 4 2) 0)) - (is (= (mod 3 2) 1)) - (is (= (mod 6 4) 2)) - (is (= (mod 0 5) 0)) - (is (= (mod 4.5 2.0) 0.5)) - (is (= (mod 42 5) 2)) - (is (= (mod 9 3) 0)) - (is (= (mod 9 -3) 0)) - (is (= (mod -9 3) 0)) - (is (= (mod -9 -3) 0)) - (is (= (mod 0 3) 0)) - (is (= (mod 3216478362187432 432143214) 120355456))) - - (testing "Testing rem" - (is (= (rem 4 2) 0)) - (is (= (rem 0 5) 0)) - (is (= (rem 4.5 2.0) 0.5)) - (is (= (rem 42 5) 2)) - (is (= (rem 2 5) 2)) - (is (= (rem 2 -5) 2)) - (is (= (rem 0 3) 0))) -) - (deftest test-hash-null (is (zero? (hash (aget (js-obj) "foo"))))) -;; See -;; https://github.com/clojure/tools.reader#differences-from-lispreaderjava -;; about why these tests won't pass. Not clear if we should change the reader -;; or the test -;; (assert (= "baz" (name 'foo/bar/baz))) -;; (assert (= "foo/bar" (namespace 'foo/bar/baz))) -;; (assert (= "baz" (name :foo/bar/baz))) -;; (assert (= "foo/bar" (namespace :foo/bar/baz))) -;; TODO: These next two tests need Clojure 1.5 -;; (assert (= "foo" (namespace 'foo//))) -;; (assert (= "/" (name 'foo//))) - -(deftest test-symbols-and-keywords - (testing "Testing name / namespace" - (is (nil? (namespace '/))) - (is (= "/" (name '/))) - (is (= "keyword" (name :keyword)))) - - (testing "Testing str on keywords / symbols" - (is (= ":hello" (str :hello))) - (is (= "hello" (str 'hello))) - (is (= "hello:world" (str "hello" :world))) - (is (= ":helloworld" (str :hello 'world)))) - - (testing "Testing symbol ctor is idempotent" - (is (= 'a (symbol 'a)))) - - (testing "Testing constructed division symbol" - (is (= '/ (symbol "/"))) - (is (= (namespace '/) (namespace (symbol "/")))) - (is (= (hash '/) (hash (symbol "/"))))) - - (testing "Testing keyword ctor" - (is (= :a (keyword "a"))) - (is (= :a (keyword 'a))) - (is (= :a/b (keyword 'a 'b))) - (is (= :a (keyword :a)))) - - (testing "Testing name munging CLJS-1432" - (is (not= :$ :.)) - (is (not= '$ '.)))) - (deftest test-map-operations (testing "Test basic map collection operations" (is (= {:a :b} (get {[1 2 3] {:a :b}, 4 5} [1 2 3]))) diff --git a/src/test/cljs/cljs/primitives_test.cljs b/src/test/cljs/cljs/primitives_test.cljs new file mode 100644 index 000000000..58e2cf393 --- /dev/null +++ b/src/test/cljs/cljs/primitives_test.cljs @@ -0,0 +1,323 @@ +; 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.primitives-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.string :as s] + [clojure.set :as set])) + +(deftest test-js-primitives + ;; js primitives + (let [keys #(vec (js-keys %))] + (testing "Testing js primitives" + (is (= [] (keys (js-obj)) (keys (apply js-obj [])))) + (is (= ["x"] (keys (js-obj "x" "y")) (keys (apply js-obj ["x" "y"]))))))) + +(deftest test-equiv + (testing "Testing -equiv" + (is (= 1)) + (is (= 1 1)) + (is (= 1 1 1)) + (is (= 1 1 1 1)) + (is (not (= 1 2))) + (is (not (= 1 2 1))) + (is (not (= 1 1 2))) + (is (not (= 1 1 2 1))) + (is (not (= 1 1 1 2))))) + +(deftest test-arithmetic + (testing "Testing addition" + (is (= (+) 0)) + (is (= (apply + []) 0)) + (is (= (+ 1) 1)) + (is (= (apply + [1]) 1)) + (is (= (+ 1 1) 2)) + (is (= (apply + [1 1]) 2)) + (is (= (+ 1 2 3) 6)) + (is (= (apply + [1 2 3]) 6))) + + (testing "Testing subtraction" + (is (= (- 1) -1)) + (is (= (apply - [1]) -1)) + (is (= (- 1 1) 0)) + (is (= (apply - [1 1]) 0)) + (is (= (- 3 2 1) 0)) + (is (= (apply - [3 2 1]) 0))) + + (testing "Testing multiplication" + (is (= (*) 1)) + (is (= (apply * []) 1)) + (is (= (* 2) 2)) + (is (= (apply * [2]) 2)) + (is (= (* 2 3) 6)) + (is (= (apply * [2 3]) 6))) + + (testing "Testing division" + (is (= (/ 2) 0.5)) + (is (= (apply / [2]) 0.5)) + (is (= (/ 6 2) 3)) + (is (= (apply / [6 2]) 3)) + (is (= (/ 6 3 2) 1)) + (is (= (apply / [6 3 2]) 1))) + + (testing "Testing less than" + (is (= (< 1) true)) + (is (= (apply < [1]) true)) + (is (= (< 1 2) true)) + (is (= (apply < [1 2]) true)) + (is (= (< 1 1) false)) + (is (= (apply < [1 1]) false)) + (is (= (< 2 1) false)) + (is (= (apply < [2 1]) false)) + (is (= (< 1 2 3) true)) + (is (= (apply < [1 2 3]) true)) + (is (= (< 1 1 3) false)) + (is (= (apply < [1 1 3]) false)) + (is (= (< 3 1 1) false)) + (is (= (apply < [3 1 1]) false))) + + (testing "Testing less than or equal to" + (is (= (<= 1) true)) + (is (= (apply <= [1]) true)) + (is (= (<= 1 1) true)) + (is (= (apply <= [1 1]) true)) + (is (= (<= 1 2) true)) + (is (= (apply <= [1 2]) true)) + (is (= (<= 2 1) false)) + (is (= (apply <= [2 1]) false)) + (is (= (<= 1 2 3) true)) + (is (= (apply <= [1 2 3]) true)) + (is (= (<= 1 1 3) true)) + (is (= (apply <= [1 1 3]) true)) + (is (= (<= 3 1 1) false)) + (is (= (apply <= [3 1 1]) false))) + + (testing "Testing greater than" + (is (= (> 1) true)) + (is (= (apply > [1]) true)) + (is (= (> 2 1) true)) + (is (= (apply > [2 1]) true)) + (is (= (> 1 1) false)) + (is (= (apply > [1 1]) false)) + (is (= (> 1 2) false)) + (is (= (apply > [1 2]) false)) + (is (= (> 3 2 1) true)) + (is (= (apply > [3 2 1]) true)) + (is (= (> 3 1 1) false)) + (is (= (apply > [3 1 1]) false)) + (is (= (> 1 1 3) false)) + (is (= (apply > [1 1 3]) false))) + + (testing "Testing greater than or equal to" + (is (= (>= 1) true)) + (is (= (apply >= [1]) true)) + (is (= (>= 2 1) true)) + (is (= (apply >= [2 1]) true)) + (is (= (>= 1 1) true)) + (is (= (apply >= [1 1]) true)) + (is (= (>= 1 2) false)) + (is (= (apply >= [1 2]) false)) + (is (= (>= 3 2 1) true)) + (is (= (apply >= [3 2 1]) true)) + (is (= (>= 3 1 1) true)) + (is (= (apply >= [3 1 1]) true)) + (is (= (>= 3 1 2) false)) + (is (= (apply >= [3 1 2]) false)) + (is (= (>= 1 1 3) false)) + (is (= (apply >= [1 1 3]) false))) + + (testing "Testing dec/inc" + (is (= (dec 1) 0)) + (is (= (apply dec [1]) 0)) + (is (= (inc 0) 1)) + (is (= (apply inc [0]) 1))) + + (testing "Testing zero? pos? neg? even? odd?" + (is (= (zero? 0) true)) + (is (= (apply zero? [0]) true)) + (is (= (zero? 1) false)) + (is (= (apply zero? [1]) false)) + (is (= (zero? -11) false)) + (is (= (apply zero? [-11]) false)) + (is (= (pos? 0) false)) + (is (= (apply pos? [0]) false)) + (is (= (pos? 1) true)) + (is (= (apply pos? [1]) true)) + (is (= (pos? -1) false)) + (is (= (apply pos? [-1]) false)) + (is (= (neg? -1) true)) + (is (= (apply neg? [-1]) true)) + (is (neg? -1)) + (is (not (neg? 1))) + (is (neg? -1.765)) + (is (not (neg? 0))) + (is (= [true false true false true false true false] + (map integer? + [1 1.00001 0x7e7 [] (- 88 1001991881) :foo 0 "0"]))) + (is (= [true false true false true false] + (map odd? [1 2 3 4 -1 0]))) + (is (= [true false true false true true] + (map even? [2 3 4 5 -2 0])))) + + (testing "Testing max / min" + (is (= (max 1) 1)) + (is (= (apply max [1]) 1)) + (is (= (max 1 2) 2)) + (is (= (apply max [1 2]) 2)) + (is (= (max 2 1) 2)) + (is (= (apply max [2 1]) 2)) + (is (= (max 1 2 3) 3)) + (is (= (apply max [1 2 3]) 3)) + (is (= (max 1 3 2) 3)) + (is (= (apply max [1 3 2]) 3)) + + (is (= (min 1) 1)) + (is (= (apply min [1]) 1)) + (is (= (min 1 2) 1)) + (is (= (apply min [1 2]) 1)) + (is (= (min 2 1) 1)) + (is (= (apply min [2 1]) 1)) + (is (= (min 1 2 3) 1)) + (is (= (apply min [1 2 3]) 1)) + (is (= (min 2 1 3) 1)) + (is (= (apply min [3 1 3]) 1))) + + (testing "Testing mod" + (is (= (mod 4 2) 0)) + (is (= (apply mod [4 2]) 0)) + (is (= (mod 3 2) 1)) + (is (= (apply mod [3 2]) 1)) + (is (= (mod -2 5) 3))) + + (testing "Testing numeric equality in collections" + (is (= [4 3 2 1 0] + (loop [i 0 j ()] + (if (< i 5) + (recur (inc i) (conj j (fn [] i))) + (map #(%) j))))) + (is (= [[1 1] [1 2] [1 3] [2 1] [2 2] [2 3]] + (map #(%) (for [i [1 2] j [1 2 3]] (fn [] [i j])))))) + + (testing "Testing integer? predicate" + (is (integer? 0)) + (is (integer? 42)) + (is (integer? -42)) + (is (not (integer? ""))) + (is (not (integer? 1e308))) + (is (not (integer? js/Infinity))) + (is (not (integer? (- js/Infinity)))) + (is (not (integer? js/NaN)))) + + (testing "Testing integer coercions" + (is (= 42 (int 42.5))) + (is (integer? (int 42.5))) + (is (= 42 (long 42.5))) + (is (integer? (long 42.5))) + (is (= -1 (int -1.5))) + (is (= -9 (long -9.8)))) + + (testing "Testing numeric equality from collection" + (is (= 2 (:b {:a 1 :b 2}))) + (is (= 2 ('b '{:a 1 b 2}))) + (is (= 2 ({:a 1 :b 2} :b))) + (is (= 2 ({1 1 2 2} 2))) + (is (= 2 (:a {:b 1} 2))) + (is (= 2 (:a {} 2))) + (is (= 2 ({:b 1} :a 2))) + (is (= 2 ({} :a 2))) + (is (= nil (:a {}))) + (is (= nil (:a ""))) + (is (= 2 (:a "" 2))) + (is (= 2 (#{1 2 3} 2))) + (is (= 1 (apply :a '[{:a 1 a 2}]))) + (is (= 1 (apply 'a '[{a 1 :b 2}]))) + (is (= 1 (apply {:a 1} [:a]))) + (is (= 2 (apply {:a 1} [:b 2])))) + + (testing "Testing quot" + (is (= (quot 4 2) 2)) + (is (= (quot 3 2) 1)) + (is (= (quot 6 4) 1)) + (is (= (quot 0 5) 0)) + (is (= (quot 42 5) 8)) + (is (= (quot 42 -5) -8)) + (is (= (quot -42 -5) 8)) + (is (= (quot 9 3) 3)) + (is (= (quot 9 -3) -3)) + (is (= (quot -9 3) -3)) + (is (= (quot 2 -5) 0)) + (is (= (quot -2 5) 0)) + (is (= (quot 0 3) 0)) + (is (= (quot 0 -3) 0))) + + (testing "Testing mod" + (is (= (mod 4 2) 0)) + (is (= (mod 3 2) 1)) + (is (= (mod 6 4) 2)) + (is (= (mod 0 5) 0)) + (is (= (mod 4.5 2.0) 0.5)) + (is (= (mod 42 5) 2)) + (is (= (mod 9 3) 0)) + (is (= (mod 9 -3) 0)) + (is (= (mod -9 3) 0)) + (is (= (mod -9 -3) 0)) + (is (= (mod 0 3) 0)) + (is (= (mod 3216478362187432 432143214) 120355456))) + + (testing "Testing rem" + (is (= (rem 4 2) 0)) + (is (= (rem 0 5) 0)) + (is (= (rem 4.5 2.0) 0.5)) + (is (= (rem 42 5) 2)) + (is (= (rem 2 5) 2)) + (is (= (rem 2 -5) 2)) + (is (= (rem 0 3) 0))) + ) + +;; See +;; https://github.com/clojure/tools.reader#differences-from-lispreaderjava +;; about why these tests won't pass. Not clear if we should change the reader +;; or the test +;; (assert (= "baz" (name 'foo/bar/baz))) +;; (assert (= "foo/bar" (namespace 'foo/bar/baz))) +;; (assert (= "baz" (name :foo/bar/baz))) +;; (assert (= "foo/bar" (namespace :foo/bar/baz))) +;; TODO: These next two tests need Clojure 1.5 +;; (assert (= "foo" (namespace 'foo//))) +;; (assert (= "/" (name 'foo//))) + +(deftest test-symbols-and-keywords + (testing "Testing name / namespace" + (is (nil? (namespace '/))) + (is (= "/" (name '/))) + (is (= "keyword" (name :keyword)))) + + (testing "Testing str on keywords / symbols" + (is (= ":hello" (str :hello))) + (is (= "hello" (str 'hello))) + (is (= "hello:world" (str "hello" :world))) + (is (= ":helloworld" (str :hello 'world)))) + + (testing "Testing symbol ctor is idempotent" + (is (= 'a (symbol 'a)))) + + (testing "Testing constructed division symbol" + (is (= '/ (symbol "/"))) + (is (= (namespace '/) (namespace (symbol "/")))) + (is (= (hash '/) (hash (symbol "/"))))) + + (testing "Testing keyword ctor" + (is (= :a (keyword "a"))) + (is (= :a (keyword 'a))) + (is (= :a/b (keyword 'a 'b))) + (is (= :a (keyword :a)))) + + (testing "Testing name munging CLJS-1432" + (is (not= :$ :.)) + (is (not= '$ '.)))) diff --git a/src/test/cljs/cljs/test_runner.cljs b/src/test/cljs/cljs/test_runner.cljs index ecd517b0a..f9b798739 100644 --- a/src/test/cljs/cljs/test_runner.cljs +++ b/src/test/cljs/cljs/test_runner.cljs @@ -1,5 +1,6 @@ (ns test-runner (:require [cljs.test :refer-macros [run-tests]] + [cljs.primitives-test] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -22,6 +23,7 @@ (set-print-fn! js/print) (run-tests + 'cljs.primitives-test 'cljs.core-test 'cljs.reader-test 'clojure.string-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 049c56144..784657616 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -243,6 +243,7 @@ (eval-form st 'cljs.user '(ns parity.core (:require [cljs.test :refer-macros [run-tests]] + [cljs.primitives-test] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -266,6 +267,7 @@ (prn error) (eval-form st 'parity.core '(run-tests + 'cljs.primitives-test 'cljs.core-test 'cljs.reader-test 'clojure.string-test From 1e739e7a5ac7591e913078f71c61258fb1e1cd6d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 15:57:14 -0400 Subject: [PATCH 2032/4033] move some more primitive tests --- src/test/cljs/cljs/core_test.cljs | 207 ------------------------ src/test/cljs/cljs/primitives_test.cljs | 207 ++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 207 deletions(-) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5523bdfa0..a64ecaabe 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -47,70 +47,6 @@ (is (= {"x" "y"} (meta ^{"x" "y"} []))) )) -(deftest test-bit-operations - (testing "Testing bit operations" - (is (= [1 0 0 40 43 49 49]) - [(bit-xor 0 1) - (bit-xor 1 1) - (bit-xor 1 0) - (bit-xor 41 1) - (bit-xor 42 1) - (bit-xor 42 1 26) - (apply bit-xor [42 1 26])]) - (is (= [0 0 1 0 1 1 1] - [(bit-and 1 0) - (bit-and 0 0) - (bit-and 1 1) - (bit-and 42 1) - (bit-and 41 1) - (bit-and 41 1 27) - (apply bit-and [41 1 27])])) - (is (= [1 0 1 43 41 59 59] - [(bit-or 1 0) - (bit-or 0 0) - (bit-or 1 1) - (bit-or 42 1) - (bit-or 41 1) - (bit-or 41 1 26) - (apply bit-or [41 1 26])])) - (is (= [1 0 0 42 32 32] - [(bit-and-not 1 0) - (bit-and-not 0 0) - (bit-and-not 1 1) - (bit-and-not 42 1) - (bit-and-not 41 1 27) - (apply bit-and-not [41 1 27])])) - (is (= [0 2 968 16649 0] - [(bit-clear 1 0) - (bit-clear 2 0) - (bit-clear 1000 5) - (bit-clear 16713 6) - (bit-clear 1024 10)])) - (is (= [0 0 992 18761 0] - [(bit-flip 1 0) - (bit-flip 2 1) - (bit-flip 1000 3) - (bit-flip 16713 11) - (bit-flip 1024 10)])) - (is (= [-2 -3 999 -16714 -1025] - [(bit-not 1) - (bit-not 2) - (bit-not -1000) - (bit-not 16713) - (bit-not 1024)])) - (is (= [1 2 1000 18761 1024] - [(bit-set 1 0) - (bit-set 2 1) - (bit-set 1000 3) - (bit-set 16713 11) - (bit-set 1024 10)])) - (is (= [true true true false true] - [(bit-test 1 0) - (bit-test 2 1) - (bit-test 1000 3) - (bit-test 16713 11) - (bit-test 1024 10)])))) - (deftest test-vectors (testing "Testing vectors" (is (= :a (nth [:a :b :c :d] 0))) @@ -185,73 +121,6 @@ (is (= () (rest [1]))) (is (= () (rest (array 1)))))) -(deftest test-apply - (testing "Testing apply" - (is (= 0 (apply + nil))) - (is (= 0 (apply + (list)))) - (is (= 1 (apply + (list 1)))) - (is (= 3 (apply + 1 (list 2)))) - (is (= 7 (apply + 1 2 (list 4)))) - (is (= 15 (apply + 1 2 4 (list 8)))) - (is (= 31 (apply + 1 2 4 8 (list 16)))) - (is (= 63 (apply + 1 2 4 8 16 (list 32)))) - (is (= 127 (apply + 1 2 4 8 16 (list 32 64)))) - (is (= 4950 (apply + (take 100 (iterate inc 0))))) - (is (= () (apply list []))) - (is (= [1 2 3] (apply list [1 2 3]))) - (is (= 6 (apply apply [+ [1 2 3]]))) - ;; apply with infinite sequence - (is (= 3 (apply (fn [& args] - (+ (nth args 0) - (nth args 1) - (nth args 2))) - (iterate inc 0)))) - (is (= [0 1 2 3 4] (take 5 (apply (fn [& m] m) (iterate inc 0))))) - (is (= [1 2 3 4 5] (take 5 (apply (fn [x & m] m) (iterate inc 0))))) - (is (= [2 3 4 5 6] (take 5 (apply (fn [x y & m] m) (iterate inc 0))))) - (is (= [3 4 5 6 7] (take 5 (apply (fn [x y z & m] m) (iterate inc 0))))) - (is (= [4 5 6 7 8] (take 5 (apply (fn [x y z a & m] m) (iterate inc 0))))) - (is (= [5 6 7 8 9] (take 5 (apply (fn [x y z a b & m] m) (iterate inc 0))))) - ;; apply arity tests - (let [single-arity-non-variadic (fn [x y z] [z y x]) - multiple-arity-non-variadic (fn ([x] x) ([x y] [y x]) ([x y z] [z y x])) - single-arity-variadic-fixedargs (fn [x y & more] [more y x]) - single-arity-variadic-nofixedargs (fn [& more] more) - multiple-arity-variadic (fn ([x] x) ([x y] [y x]) ([x y & more] [more y x]))] - (testing "arities" - (is (= [3 2 1] (apply single-arity-non-variadic [1 2 3]))) - (is (= [3 2 1] (apply single-arity-non-variadic 1 [2 3]))) - (is (= [3 2 1] (apply single-arity-non-variadic 1 2 [3]))) - (is (= 42 (apply multiple-arity-non-variadic [42]))) - (is (= [2 1] (apply multiple-arity-non-variadic [1 2]))) - (is (= [2 1] (apply multiple-arity-non-variadic 1 [2]))) - (is (= [3 2 1] (apply multiple-arity-non-variadic [1 2 3]))) - (is (= [3 2 1] (apply multiple-arity-non-variadic 1 [2 3]))) - (is (= [3 2 1] (apply multiple-arity-non-variadic 1 2 [3]))) - (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs [1 2 3 4 5]))) - (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 [2 3 4 5]))) - (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 [3 4 5]))) - (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 3 [4 5]))) - (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 3 4 [5]))) - (is (= [3 4 5] (take 3 (first (apply single-arity-variadic-fixedargs (iterate inc 1)))))) - (is (= [2 1] (rest (apply single-arity-variadic-fixedargs (iterate inc 1))))) - (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs [1 2 3 4 5]))) - (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 [2 3 4 5]))) - (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 [3 4 5]))) - (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 3 [4 5]))) - (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 3 4 [5]))) - (is (= [1 2 3 4 5] (take 5 (apply single-arity-variadic-nofixedargs (iterate inc 1))))) - (is (= 42 (apply multiple-arity-variadic [42]))) - (is (= [2 1] (apply multiple-arity-variadic [1 2]))) - (is (= [2 1] (apply multiple-arity-variadic 1 [2]))) - (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic [1 2 3 4 5]))) - (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 [2 3 4 5]))) - (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 [3 4 5]))) - (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 3 [4 5]))) - (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 3 4 [5]))) - (is (= [3 4 5] (take 3 (first (apply multiple-arity-variadic (iterate inc 1)))))) - (is (= [2 1] (rest (apply multiple-arity-variadic (iterate inc 1))))))))) - ;; this fails in v8 - why? ;; (assert (= "symbol\"'string" (pr-str (str 'symbol \" \' "string")))) (deftest test-misc @@ -398,20 +267,6 @@ (is (try (do (take-nth nil [1 2 3]) false) (catch js/Error e true))))) -(deftest test-booleans - (testing "Testing boolean predicates" - (is (= [true false true false false false true true false false] - [(true? true) - (true? false) - (false? false) - (false? true) - (true? js/undefined) - (false? js/undefined) - (boolean? true) - (boolean? false) - (boolean? nil) - (boolean? js/undefined)])))) - (deftest test-fn-with-metadata (let [f (fn [x] (* x 2)) m {:foo "bar"} @@ -513,51 +368,6 @@ (is (empty? e-queue)) (is (= {:b :c} (meta e-queue))))))) -(deftest test-try-catch - (let [a (atom nil)] - (testing "Testing try/catch" - (is (= 1 (try 1))) - (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 2)))) - (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 1 2)))) - (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 2) (catch :default e 3)))) - (is (= 3 (try 1 (throw true) (catch js/Error e 2) (catch :default e 3)))) - (is (= 2 (try 1 (throw 2) (catch js/Error e 3) (catch :default e e)))) - (is (= 1 (try 1 (finally (reset! a 42))))) - (is (= 42 (deref a)))))) - -(deftest test-list-comprehensions - (let [v [1 2 3]] - (testing "Testing list comprehensions" - (is (= v (for [e v] e))) - (is (= [[1 1] [2 4] [3 9]] (for [e v :let [m (* e e)]] [e m]))) - (is (= [1 2] (for [e v :while (< e 3)] e))) - (is (= [3] (for [e v :when (> e 2)] e))) - (is (= [[1 1] [2 4]] (for [e v :while (< e 3) :let [m (* e e)]] [e m])))))) - -(deftest test-partial-and-comp - (let [a10 (partial + 10) - a20 (partial + 10 10) - a21 (partial + 10 10 1) - a22 (partial + 10 5 4 3) - a23 (partial + 10 5 4 3 1)] - (testing "Testing partial" - (is (= 110 (a10 100))) - (is (= 120 (a20 100))) - (is (= 121 (a21 100))) - (is (= 122 (a22 100))) - (is (= 123 (a23 100))))) - (let [n2 (comp first rest) - n3 (comp first rest rest) - n4 (comp first rest rest rest) - n5 (comp first rest rest rest rest) - n6 (comp first rest rest rest rest rest)] - (testing "Testing comp" - (is (= 2 (n2 [1 2 3 4 5 6 7]))) - (is (= 3 (n3 [1 2 3 4 5 6 7]))) - (is (= 4 (n4 [1 2 3 4 5 6 7]))) - (is (= 5 (n5 [1 2 3 4 5 6 7]))) - (is (= 6 (n6 [1 2 3 4 5 6 7])))))) - (deftest test-sets (testing "Testing persistent sets" (is (set [])) @@ -636,23 +446,6 @@ (is (= (distinct [#{1 2} #{1 2}]) [#{1 2}])) (is (= (distinct [#{} #{}]) [#{}])))) -(deftest test-regexps - (testing "Testing regexps" - (let [r1 #"foo", r2 (re-pattern r1)] - (is (= r1 r2))) - (is (= (str (re-pattern "f(.)o")) (str (js* "/f(.)o/")))) - (is (= (re-find (re-pattern "foo") "foo bar foo baz foo zot") "foo")) - (is (= (re-find (re-pattern "f(.)o") "foo bar foo baz foo zot") ["foo" "o"])) - (is (= (re-matches (re-pattern "foo") "foo") "foo")) - (is (= (re-matches (re-pattern "foo") "foo bar foo baz foo zot") nil)) - (is (= (re-matches (re-pattern "foo.*") "foo bar foo baz foo zot") "foo bar foo baz foo zot")) - (is (= (re-seq (re-pattern "foo") "foo bar foo baz foo zot") (list "foo" "foo" "foo"))) - (is (= (re-seq (re-pattern "f(.)o") "foo bar foo baz foo zot") (list ["foo" "o"] ["foo" "o"] ["foo" "o"]))) - (is (= (re-matches (re-pattern "(?i)foo") "Foo") "Foo")) - ; new RegExp("").source => "(?:)" on webkit-family envs, "" elsewhere - (is (#{"#\"\"" "#\"(?:)\""} (pr-str #""))) - (is (= (re-find (re-pattern "[\u2028]") " \u2028 ") "\u2028")))) ; regression test for CLJS-889 - (deftest test-destructuring (testing "Testing destructuring" (is (= [2 1] (let [[a b] [1 2]] [b a]))) diff --git a/src/test/cljs/cljs/primitives_test.cljs b/src/test/cljs/cljs/primitives_test.cljs index 58e2cf393..036dc09e4 100644 --- a/src/test/cljs/cljs/primitives_test.cljs +++ b/src/test/cljs/cljs/primitives_test.cljs @@ -321,3 +321,210 @@ (testing "Testing name munging CLJS-1432" (is (not= :$ :.)) (is (not= '$ '.)))) + +(deftest test-bit-operations + (testing "Testing bit operations" + (is (= [1 0 0 40 43 49 49]) + [(bit-xor 0 1) + (bit-xor 1 1) + (bit-xor 1 0) + (bit-xor 41 1) + (bit-xor 42 1) + (bit-xor 42 1 26) + (apply bit-xor [42 1 26])]) + (is (= [0 0 1 0 1 1 1] + [(bit-and 1 0) + (bit-and 0 0) + (bit-and 1 1) + (bit-and 42 1) + (bit-and 41 1) + (bit-and 41 1 27) + (apply bit-and [41 1 27])])) + (is (= [1 0 1 43 41 59 59] + [(bit-or 1 0) + (bit-or 0 0) + (bit-or 1 1) + (bit-or 42 1) + (bit-or 41 1) + (bit-or 41 1 26) + (apply bit-or [41 1 26])])) + (is (= [1 0 0 42 32 32] + [(bit-and-not 1 0) + (bit-and-not 0 0) + (bit-and-not 1 1) + (bit-and-not 42 1) + (bit-and-not 41 1 27) + (apply bit-and-not [41 1 27])])) + (is (= [0 2 968 16649 0] + [(bit-clear 1 0) + (bit-clear 2 0) + (bit-clear 1000 5) + (bit-clear 16713 6) + (bit-clear 1024 10)])) + (is (= [0 0 992 18761 0] + [(bit-flip 1 0) + (bit-flip 2 1) + (bit-flip 1000 3) + (bit-flip 16713 11) + (bit-flip 1024 10)])) + (is (= [-2 -3 999 -16714 -1025] + [(bit-not 1) + (bit-not 2) + (bit-not -1000) + (bit-not 16713) + (bit-not 1024)])) + (is (= [1 2 1000 18761 1024] + [(bit-set 1 0) + (bit-set 2 1) + (bit-set 1000 3) + (bit-set 16713 11) + (bit-set 1024 10)])) + (is (= [true true true false true] + [(bit-test 1 0) + (bit-test 2 1) + (bit-test 1000 3) + (bit-test 16713 11) + (bit-test 1024 10)])))) + +(deftest test-apply + (testing "Testing apply" + (is (= 0 (apply + nil))) + (is (= 0 (apply + (list)))) + (is (= 1 (apply + (list 1)))) + (is (= 3 (apply + 1 (list 2)))) + (is (= 7 (apply + 1 2 (list 4)))) + (is (= 15 (apply + 1 2 4 (list 8)))) + (is (= 31 (apply + 1 2 4 8 (list 16)))) + (is (= 63 (apply + 1 2 4 8 16 (list 32)))) + (is (= 127 (apply + 1 2 4 8 16 (list 32 64)))) + (is (= 4950 (apply + (take 100 (iterate inc 0))))) + (is (= () (apply list []))) + (is (= [1 2 3] (apply list [1 2 3]))) + (is (= 6 (apply apply [+ [1 2 3]]))) + ;; apply with infinite sequence + (is (= 3 (apply (fn [& args] + (+ (nth args 0) + (nth args 1) + (nth args 2))) + (iterate inc 0)))) + (is (= [0 1 2 3 4] (take 5 (apply (fn [& m] m) (iterate inc 0))))) + (is (= [1 2 3 4 5] (take 5 (apply (fn [x & m] m) (iterate inc 0))))) + (is (= [2 3 4 5 6] (take 5 (apply (fn [x y & m] m) (iterate inc 0))))) + (is (= [3 4 5 6 7] (take 5 (apply (fn [x y z & m] m) (iterate inc 0))))) + (is (= [4 5 6 7 8] (take 5 (apply (fn [x y z a & m] m) (iterate inc 0))))) + (is (= [5 6 7 8 9] (take 5 (apply (fn [x y z a b & m] m) (iterate inc 0))))) + ;; apply arity tests + (let [single-arity-non-variadic (fn [x y z] [z y x]) + multiple-arity-non-variadic (fn ([x] x) ([x y] [y x]) ([x y z] [z y x])) + single-arity-variadic-fixedargs (fn [x y & more] [more y x]) + single-arity-variadic-nofixedargs (fn [& more] more) + multiple-arity-variadic (fn ([x] x) ([x y] [y x]) ([x y & more] [more y x]))] + (testing "arities" + (is (= [3 2 1] (apply single-arity-non-variadic [1 2 3]))) + (is (= [3 2 1] (apply single-arity-non-variadic 1 [2 3]))) + (is (= [3 2 1] (apply single-arity-non-variadic 1 2 [3]))) + (is (= 42 (apply multiple-arity-non-variadic [42]))) + (is (= [2 1] (apply multiple-arity-non-variadic [1 2]))) + (is (= [2 1] (apply multiple-arity-non-variadic 1 [2]))) + (is (= [3 2 1] (apply multiple-arity-non-variadic [1 2 3]))) + (is (= [3 2 1] (apply multiple-arity-non-variadic 1 [2 3]))) + (is (= [3 2 1] (apply multiple-arity-non-variadic 1 2 [3]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs [1 2 3 4 5]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 [2 3 4 5]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 [3 4 5]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 3 [4 5]))) + (is (= [[3 4 5] 2 1] (apply single-arity-variadic-fixedargs 1 2 3 4 [5]))) + (is (= [3 4 5] (take 3 (first (apply single-arity-variadic-fixedargs (iterate inc 1)))))) + (is (= [2 1] (rest (apply single-arity-variadic-fixedargs (iterate inc 1))))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs [1 2 3 4 5]))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 [2 3 4 5]))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 [3 4 5]))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 3 [4 5]))) + (is (= [1 2 3 4 5] (apply single-arity-variadic-nofixedargs 1 2 3 4 [5]))) + (is (= [1 2 3 4 5] (take 5 (apply single-arity-variadic-nofixedargs (iterate inc 1))))) + (is (= 42 (apply multiple-arity-variadic [42]))) + (is (= [2 1] (apply multiple-arity-variadic [1 2]))) + (is (= [2 1] (apply multiple-arity-variadic 1 [2]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic [1 2 3 4 5]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 [2 3 4 5]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 [3 4 5]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 3 [4 5]))) + (is (= [[3 4 5] 2 1] (apply multiple-arity-variadic 1 2 3 4 [5]))) + (is (= [3 4 5] (take 3 (first (apply multiple-arity-variadic (iterate inc 1)))))) + (is (= [2 1] (rest (apply multiple-arity-variadic (iterate inc 1))))))))) + +(deftest test-booleans + (testing "Testing boolean predicates" + (is (= [true false true false false false true true false false] + [(true? true) + (true? false) + (false? false) + (false? true) + (true? js/undefined) + (false? js/undefined) + (boolean? true) + (boolean? false) + (boolean? nil) + (boolean? js/undefined)])))) + +(deftest test-try-catch + (let [a (atom nil)] + (testing "Testing try/catch" + (is (= 1 (try 1))) + (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 2)))) + (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 1 2)))) + (is (= 2 (try 1 (throw (js/Error.)) (catch js/Error e 2) (catch :default e 3)))) + (is (= 3 (try 1 (throw true) (catch js/Error e 2) (catch :default e 3)))) + (is (= 2 (try 1 (throw 2) (catch js/Error e 3) (catch :default e e)))) + (is (= 1 (try 1 (finally (reset! a 42))))) + (is (= 42 (deref a)))))) + +(deftest test-list-comprehensions + (let [v [1 2 3]] + (testing "Testing list comprehensions" + (is (= v (for [e v] e))) + (is (= [[1 1] [2 4] [3 9]] (for [e v :let [m (* e e)]] [e m]))) + (is (= [1 2] (for [e v :while (< e 3)] e))) + (is (= [3] (for [e v :when (> e 2)] e))) + (is (= [[1 1] [2 4]] (for [e v :while (< e 3) :let [m (* e e)]] [e m])))))) + +(deftest test-partial-and-comp + (let [a10 (partial + 10) + a20 (partial + 10 10) + a21 (partial + 10 10 1) + a22 (partial + 10 5 4 3) + a23 (partial + 10 5 4 3 1)] + (testing "Testing partial" + (is (= 110 (a10 100))) + (is (= 120 (a20 100))) + (is (= 121 (a21 100))) + (is (= 122 (a22 100))) + (is (= 123 (a23 100))))) + (let [n2 (comp first rest) + n3 (comp first rest rest) + n4 (comp first rest rest rest) + n5 (comp first rest rest rest rest) + n6 (comp first rest rest rest rest rest)] + (testing "Testing comp" + (is (= 2 (n2 [1 2 3 4 5 6 7]))) + (is (= 3 (n3 [1 2 3 4 5 6 7]))) + (is (= 4 (n4 [1 2 3 4 5 6 7]))) + (is (= 5 (n5 [1 2 3 4 5 6 7]))) + (is (= 6 (n6 [1 2 3 4 5 6 7])))))) + +(deftest test-regexps + (testing "Testing regexps" + (let [r1 #"foo", r2 (re-pattern r1)] + (is (= r1 r2))) + (is (= (str (re-pattern "f(.)o")) (str (js* "/f(.)o/")))) + (is (= (re-find (re-pattern "foo") "foo bar foo baz foo zot") "foo")) + (is (= (re-find (re-pattern "f(.)o") "foo bar foo baz foo zot") ["foo" "o"])) + (is (= (re-matches (re-pattern "foo") "foo") "foo")) + (is (= (re-matches (re-pattern "foo") "foo bar foo baz foo zot") nil)) + (is (= (re-matches (re-pattern "foo.*") "foo bar foo baz foo zot") "foo bar foo baz foo zot")) + (is (= (re-seq (re-pattern "foo") "foo bar foo baz foo zot") (list "foo" "foo" "foo"))) + (is (= (re-seq (re-pattern "f(.)o") "foo bar foo baz foo zot") (list ["foo" "o"] ["foo" "o"] ["foo" "o"]))) + (is (= (re-matches (re-pattern "(?i)foo") "Foo") "Foo")) + ; new RegExp("").source => "(?:)" on webkit-family envs, "" elsewhere + (is (#{"#\"\"" "#\"(?:)\""} (pr-str #""))) + (is (= (re-find (re-pattern "[\u2028]") " \u2028 ") "\u2028")))) ; regression test for CLJS-889 From 9c0d0d20426c6ed6ecd860efa74929e27c532c74 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 16:07:02 -0400 Subject: [PATCH 2033/4033] move more primitives, destructuring test ns --- src/test/cljs/cljs/core_test.cljs | 219 --------------------- src/test/cljs/cljs/destructuring_test.cljs | 85 ++++++++ src/test/cljs/cljs/primitives_test.cljs | 147 ++++++++++++++ src/test/cljs/cljs/test_runner.cljs | 2 + src/test/self/self_parity/test.cljs | 2 + 5 files changed, 236 insertions(+), 219 deletions(-) create mode 100644 src/test/cljs/cljs/destructuring_test.cljs diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index a64ecaabe..4f817e184 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -446,78 +446,6 @@ (is (= (distinct [#{1 2} #{1 2}]) [#{1 2}])) (is (= (distinct [#{} #{}]) [#{}])))) -(deftest test-destructuring - (testing "Testing destructuring" - (is (= [2 1] (let [[a b] [1 2]] [b a]))) - (is (= #{1 2} (let [[a b] [1 2]] #{a b}))) - (is (= [1 2] (let [{a :a b :b} {:a 1 :b 2}] [a b]))) - (is (= [1 2] (let [{:keys [a b]} {:a 1 :b 2}] [a b]))) - (is (= [1 2 [1 2]] (let [[a b :as v] [1 2]] [a b v]))) - (is (= [1 42] (let [{:keys [a b] :or {b 42}} {:a 1}] [a b]))) - (is (= [1 nil] (let [{:keys [a b] :or {c 42}} {:a 1}] [a b]))) - (is (= [2 1] (let [[a b] '(1 2)] [b a]))) - (is (= {1 2} (let [[a b] [1 2]] {a b}))) - (is (= [2 1] (let [[a b] (seq [1 2])] [b a]))) - (testing "namespaced keys" - (let [{:keys [:a :b]} {:a 1 :b 2}] - (testing "basic" - (is (= 1 a)) - (is (= 2 b)))) - (let [{:keys [:a/b :c/d]} {:a/b 1 :c/d 2}] - (testing "keyword syntax" - (is (= 1 b)) - (is (= 2 d)))) - (let [{:keys [a/b c/d]} {:a/b 1 :c/d 2}] - (testing "symbol syntax" - (is (= 1 b)) - (is (= 2 d)))) - (let [{:syms [a/b c/d]} {'a/b 1 'c/d 2}] - (testing ":syms" - (is (= 1 b)) - (is (= 2 d)))) - (let [{:keys [::s/x ::s/y]} {:clojure.string/x 1 :clojure.string/y 2}] - (testing ":keys" - (is (= x 1)) - (is (= y 2)))) - ))) - -(deftest keywords-in-destructuring - (let [m {:a 1 :b 2}] - (let [{:keys [:a :b]} m] - (is (= [1 2] [a b]))) - (let [{:keys [:a :b :c] :or {c 3}} m] - (is (= [1 2 3] [a b c]))))) - -(deftest namespaced-keywords-in-destructuring - (let [m {:a/b 1 :c/d 2}] - (let [{:keys [:a/b :c/d]} m] - (is (= [1 2] [b d]))) - (let [{:keys [:a/b :c/d :e/f] :or {f 3}} m] - (is (= [1 2 3] [b d f]))))) - -(deftest namespaced-keys-in-destructuring - (let [m {:a/b 1 :c/d 2}] - (let [{:keys [a/b c/d]} m] - (is (= [1 2] [b d]))) - (let [{:keys [a/b c/d e/f] :or {f 3}} m] - (is (= [1 2 3] [b d f]))))) - -(deftest namespaced-syms-in-destructuring - (let [{:syms [a/b c/d e/f] :or {f 3}} {'a/b 1 'c/d 2}] - (is (= [1 2 3] [b d f])))) - -(deftest namespaced-keys-syntax - (let [{:a/keys [b c d] :or {d 3}} {:a/b 1 :a/c 2}] - (is (= [1 2 3] [b c d])))) - -(deftest namespaced-syms-syntax - (let [{:a/syms [b c d] :or {d 3}} {'a/b 1 'a/c 2}] - (is (= [1 2 3] [b c d])))) - -(deftest resolve-keyword-ns-alias-in-destructuring - (let [{:keys [::s/x ::s/y ::s/z] :or {z 3}} {:clojure.string/x 1 :clojure.string/y 2}] - (is (= [1 2 3] [x y z])))) - (deftest test-in-operations (testing "Testing update-in" (is (= {:foo {:bar {:baz 1}}} @@ -546,76 +474,6 @@ [1 :bar 1 :buzz])))) ) -(deftest test-arrays - (testing "Testing array operations" - (let [a (to-array [1 2 3])] - (testing "basic ops" - (is (= [10 20 30] (seq (amap a i ret (* 10 (aget a i)))))) - (is (= 6 (areduce a i ret 0 (+ ret (aget a i))))) - (is (= (seq a) (seq (to-array [1 2 3])))) - (is (= 42 (aset a 0 42))) - (is (not= (seq a) (seq (to-array [1 2 3])))) - (is (not= a (aclone a))))) - (let [a (array (array 1 2 3) (array 4 5 6))] - (testing "aget" - (is (= (aget a 0 1) 2)) - (is (= (apply aget a [0 1]) 2)) - (is (= (aget a 1 1) 5)) - (is (= (apply aget a [1 1]) 5)) - (aset a 0 0 "foo") - (is (= (aget a 0 0) "foo")) - (apply aset a [0 0 "bar"]) - (is (= (aget a 0 0) "bar")))))) - -(defn- primitive-arrays-equal - [a b] - (= (js->clj a) (js->clj b))) - -(deftest test-make-array - (testing "Testing make-array" - (is (primitive-arrays-equal #js [] (make-array 0))) - (is (primitive-arrays-equal #js [] (apply make-array [0]))) - (is (primitive-arrays-equal #js [nil] (make-array 1))) - (is (primitive-arrays-equal #js [nil] (apply make-array [1]))) - (is (primitive-arrays-equal #js [nil nil] (make-array 2))) - (is (primitive-arrays-equal #js [nil nil] (apply make-array [2]))) - (is (primitive-arrays-equal #js [] (make-array nil 0))) - (is (primitive-arrays-equal #js [] (apply make-array [nil 0]))) - (is (primitive-arrays-equal #js [nil] (make-array nil 1))) - (is (primitive-arrays-equal #js [nil] (apply make-array [nil 1]))) - (is (primitive-arrays-equal #js [nil nil] (make-array nil 2))) - (is (primitive-arrays-equal #js [nil nil] (apply make-array [nil 2]))) - (is (primitive-arrays-equal #js [] (make-array nil 0 0))) - (is (primitive-arrays-equal #js [] (apply make-array [nil 0 0]))) - (is (primitive-arrays-equal #js [] (make-array nil 0 1))) - (is (primitive-arrays-equal #js [] (apply make-array [nil 0 1]))) - (is (primitive-arrays-equal #js [#js []] (make-array nil 1 0))) - (is (primitive-arrays-equal #js [#js []] (apply make-array [nil 1 0]))) - (is (primitive-arrays-equal #js [#js [] #js []] (make-array nil 2 0))) - (is (primitive-arrays-equal #js [#js [] #js []] (apply make-array [nil 2 0]))) - (is (primitive-arrays-equal #js [#js [nil]] (make-array nil 1 1))) - (is (primitive-arrays-equal #js [#js [nil]] (apply make-array [nil 1 1]))) - (is (primitive-arrays-equal #js [#js [nil] #js [nil]] (make-array nil 2 1))) - (is (primitive-arrays-equal #js [#js [nil] #js [nil]] (apply make-array [nil 2 1]))) - (is (primitive-arrays-equal #js [#js [nil nil] #js [nil nil]] (make-array nil 2 2))) - (is (primitive-arrays-equal #js [#js [nil nil] #js [nil nil]] (apply make-array [nil 2 2]))) - (is (primitive-arrays-equal #js [] (make-array nil 0 0 0))) - (is (primitive-arrays-equal #js [] (apply make-array [nil 0 0 0]))) - (is (primitive-arrays-equal #js [] (make-array nil 0 1 1))) - (is (primitive-arrays-equal #js [] (apply make-array [nil 0 1 1]))) - (is (primitive-arrays-equal #js [#js []] (make-array nil 1 0 0))) - (is (primitive-arrays-equal #js [#js []] (apply make-array [nil 1 0 0]))) - (is (primitive-arrays-equal #js [#js [] #js []] (make-array nil 2 0 0))) - (is (primitive-arrays-equal #js [#js [] #js []] (apply make-array [nil 2 0 0]))) - (is (primitive-arrays-equal #js [#js [#js []]] (make-array nil 1 1 0))) - (is (primitive-arrays-equal #js [#js [#js []]] (apply make-array [nil 1 1 0]))) - (is (primitive-arrays-equal #js [#js [#js [nil]]] (make-array nil 1 1 1))) - (is (primitive-arrays-equal #js [#js [#js [nil]]] (apply make-array [nil 1 1 1]))) - (is (primitive-arrays-equal #js [#js [#js [nil nil] #js [nil nil]] #js [#js [nil nil] #js [nil nil]]] - (make-array nil 2 2 2))) - (is (primitive-arrays-equal #js [#js [#js [nil nil] #js [nil nil]] #js [#js [nil nil] #js [nil nil]]] - (apply make-array [nil 2 2 2]))))) - (deftest test-rearrange-sequential (testing "Test rearranging sequential collections" (is (= [1 2 3 4 5] (sort [5 3 1 4 2]))) @@ -1354,83 +1212,6 @@ (is (= expected (hash uuid))) (is (= expected (.-__hash uuid)))))) -(deftest test-comparable - (testing "Testing IComparable" - (is (= 0 (compare false false))) - (is (= -1 (compare false true))) - (is (= 1 (compare true false))) - - (is (= -1 (compare 0 1))) - (is (= -1 (compare -1 1))) - (is (= 0 (compare 1 1))) - (is (= 1 (compare 1 0))) - (is (= 1 (compare 1 -1))) - - (is (= 0 (compare "cljs" "cljs"))) - (is (= 0 (compare :cljs :cljs))) - (is (= 0 (compare 'cljs 'cljs))) - (is (= -1 (compare "a" "b"))) - (is (= -1 (compare :a :b))) - (is (= -1 (compare 'a 'b))) - ;; cases involving ns - (is (= -1 (compare :b/a :c/a))) - (is (= -1 (compare :c :a/b))) - (is (= 1 (compare :a/b :c))) - (is (= -1 (compare 'b/a 'c/a))) - (is (= -1 (compare 'c 'a/b))) - (is (= 1 (compare 'a/b 'c))) - - ;; This is different from clj. clj gives -2 next 3 tests - (is (= -1 (compare "a" "c"))) - (is (= -1 (compare :a :c))) - (is (= -1 (compare 'a 'c))) - - (is (= -1 (compare [1 2] [1 1 1]))) - (is (= -1 (compare [1 2] [1 2 1]))) - (is (= -1 (compare [1 1] [1 2]))) - (is (= 0 (compare [1 2] [1 2]))) - (is (= 1 (compare [1 2] [1 1]))) - (is (= 1 (compare [1 1 1] [1 2]))) - (is (= 1 (compare [1 1 2] [1 1 1]))) - (is (= 0 (compare [] []))) - (is (= 0 (compare (vec #js []) []))) - (is (= 0 (compare (with-meta [] {}) []))) - (is (= 0 (compare (pop [1]) []))) - - (is (= -1 (compare (subvec [1 2 3] 1) (subvec [1 2 4] 1)))) - (is (= 0 (compare (subvec [1 2 3] 1) (subvec [1 2 3] 1)))) - (is (= 1 (compare (subvec [1 2 4] 1) (subvec [1 2 3] 1)))) - (is (= 0 (compare (subvec [1] 0 0) (subvec [2] 0 0)))) - - (is (= 0 (compare (js/Date. 2015 2 8 19 13 00 999) - (js/Date. 2015 2 8 19 13 00 999)))) - (is (= -1 (compare (js/Date. 2015 2 8 19 12 00 999) - (js/Date. 2015 2 8 19 13 00 999)))) - (is (= 1 (compare (js/Date. 2015 2 8 19 14 00 999) - (js/Date. 2015 2 8 19 13 00 999)))) - )) - -(deftest test-dot - (let [s "abc"] - (testing "Testing dot operations" - (is (= 3 (.-length s))) - (is (= 3 (. s -length))) - (is (= 3 (. (str 138) -length))) - (is (= 3 (. "abc" -length))) - (is (= "bc" (.substring s 1))) - (is (= "bc" (.substring "abc" 1))) - (is (= "bc" ((memfn substring start) s 1))) - (is (= "bc" (. s substring 1))) - (is (= "bc" (. s (substring 1)))) - (is (= "bc" (. s (substring 1 3)))) - (is (= "bc" (.substring s 1 3))) - (is (= "ABC" (. s (toUpperCase)))) - (is (= "ABC" (. "abc" (toUpperCase)))) - (is (= "ABC" ((memfn toUpperCase) s))) - (is (= "BC" (. (. s (toUpperCase)) substring 1))) - (is (= 2 (.-length (. (. s (toUpperCase)) substring 1)))) - ))) - (defrecord Person [firstname lastname]) (defrecord A []) (defrecord C [a b c]) diff --git a/src/test/cljs/cljs/destructuring_test.cljs b/src/test/cljs/cljs/destructuring_test.cljs new file mode 100644 index 000000000..6a87fc0eb --- /dev/null +++ b/src/test/cljs/cljs/destructuring_test.cljs @@ -0,0 +1,85 @@ +; 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.destructuring-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.string :as s] + [clojure.set :as set])) + +(deftest test-destructuring + (testing "Testing destructuring" + (is (= [2 1] (let [[a b] [1 2]] [b a]))) + (is (= #{1 2} (let [[a b] [1 2]] #{a b}))) + (is (= [1 2] (let [{a :a b :b} {:a 1 :b 2}] [a b]))) + (is (= [1 2] (let [{:keys [a b]} {:a 1 :b 2}] [a b]))) + (is (= [1 2 [1 2]] (let [[a b :as v] [1 2]] [a b v]))) + (is (= [1 42] (let [{:keys [a b] :or {b 42}} {:a 1}] [a b]))) + (is (= [1 nil] (let [{:keys [a b] :or {c 42}} {:a 1}] [a b]))) + (is (= [2 1] (let [[a b] '(1 2)] [b a]))) + (is (= {1 2} (let [[a b] [1 2]] {a b}))) + (is (= [2 1] (let [[a b] (seq [1 2])] [b a]))) + (testing "namespaced keys" + (let [{:keys [:a :b]} {:a 1 :b 2}] + (testing "basic" + (is (= 1 a)) + (is (= 2 b)))) + (let [{:keys [:a/b :c/d]} {:a/b 1 :c/d 2}] + (testing "keyword syntax" + (is (= 1 b)) + (is (= 2 d)))) + (let [{:keys [a/b c/d]} {:a/b 1 :c/d 2}] + (testing "symbol syntax" + (is (= 1 b)) + (is (= 2 d)))) + (let [{:syms [a/b c/d]} {'a/b 1 'c/d 2}] + (testing ":syms" + (is (= 1 b)) + (is (= 2 d)))) + (let [{:keys [::s/x ::s/y]} {:clojure.string/x 1 :clojure.string/y 2}] + (testing ":keys" + (is (= x 1)) + (is (= y 2)))) + ))) + +(deftest keywords-in-destructuring + (let [m {:a 1 :b 2}] + (let [{:keys [:a :b]} m] + (is (= [1 2] [a b]))) + (let [{:keys [:a :b :c] :or {c 3}} m] + (is (= [1 2 3] [a b c]))))) + +(deftest namespaced-keywords-in-destructuring + (let [m {:a/b 1 :c/d 2}] + (let [{:keys [:a/b :c/d]} m] + (is (= [1 2] [b d]))) + (let [{:keys [:a/b :c/d :e/f] :or {f 3}} m] + (is (= [1 2 3] [b d f]))))) + +(deftest namespaced-keys-in-destructuring + (let [m {:a/b 1 :c/d 2}] + (let [{:keys [a/b c/d]} m] + (is (= [1 2] [b d]))) + (let [{:keys [a/b c/d e/f] :or {f 3}} m] + (is (= [1 2 3] [b d f]))))) + +(deftest namespaced-syms-in-destructuring + (let [{:syms [a/b c/d e/f] :or {f 3}} {'a/b 1 'c/d 2}] + (is (= [1 2 3] [b d f])))) + +(deftest namespaced-keys-syntax + (let [{:a/keys [b c d] :or {d 3}} {:a/b 1 :a/c 2}] + (is (= [1 2 3] [b c d])))) + +(deftest namespaced-syms-syntax + (let [{:a/syms [b c d] :or {d 3}} {'a/b 1 'a/c 2}] + (is (= [1 2 3] [b c d])))) + +(deftest resolve-keyword-ns-alias-in-destructuring + (let [{:keys [::s/x ::s/y ::s/z] :or {z 3}} {:clojure.string/x 1 :clojure.string/y 2}] + (is (= [1 2 3] [x y z])))) diff --git a/src/test/cljs/cljs/primitives_test.cljs b/src/test/cljs/cljs/primitives_test.cljs index 036dc09e4..6655d97b5 100644 --- a/src/test/cljs/cljs/primitives_test.cljs +++ b/src/test/cljs/cljs/primitives_test.cljs @@ -528,3 +528,150 @@ ; new RegExp("").source => "(?:)" on webkit-family envs, "" elsewhere (is (#{"#\"\"" "#\"(?:)\""} (pr-str #""))) (is (= (re-find (re-pattern "[\u2028]") " \u2028 ") "\u2028")))) ; regression test for CLJS-889 + +(deftest test-arrays + (testing "Testing array operations" + (let [a (to-array [1 2 3])] + (testing "basic ops" + (is (= [10 20 30] (seq (amap a i ret (* 10 (aget a i)))))) + (is (= 6 (areduce a i ret 0 (+ ret (aget a i))))) + (is (= (seq a) (seq (to-array [1 2 3])))) + (is (= 42 (aset a 0 42))) + (is (not= (seq a) (seq (to-array [1 2 3])))) + (is (not= a (aclone a))))) + (let [a (array (array 1 2 3) (array 4 5 6))] + (testing "aget" + (is (= (aget a 0 1) 2)) + (is (= (apply aget a [0 1]) 2)) + (is (= (aget a 1 1) 5)) + (is (= (apply aget a [1 1]) 5)) + (aset a 0 0 "foo") + (is (= (aget a 0 0) "foo")) + (apply aset a [0 0 "bar"]) + (is (= (aget a 0 0) "bar")))))) + +(defn- primitive-arrays-equal + [a b] + (= (js->clj a) (js->clj b))) + +(deftest test-make-array + (testing "Testing make-array" + (is (primitive-arrays-equal #js [] (make-array 0))) + (is (primitive-arrays-equal #js [] (apply make-array [0]))) + (is (primitive-arrays-equal #js [nil] (make-array 1))) + (is (primitive-arrays-equal #js [nil] (apply make-array [1]))) + (is (primitive-arrays-equal #js [nil nil] (make-array 2))) + (is (primitive-arrays-equal #js [nil nil] (apply make-array [2]))) + (is (primitive-arrays-equal #js [] (make-array nil 0))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0]))) + (is (primitive-arrays-equal #js [nil] (make-array nil 1))) + (is (primitive-arrays-equal #js [nil] (apply make-array [nil 1]))) + (is (primitive-arrays-equal #js [nil nil] (make-array nil 2))) + (is (primitive-arrays-equal #js [nil nil] (apply make-array [nil 2]))) + (is (primitive-arrays-equal #js [] (make-array nil 0 0))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0 0]))) + (is (primitive-arrays-equal #js [] (make-array nil 0 1))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0 1]))) + (is (primitive-arrays-equal #js [#js []] (make-array nil 1 0))) + (is (primitive-arrays-equal #js [#js []] (apply make-array [nil 1 0]))) + (is (primitive-arrays-equal #js [#js [] #js []] (make-array nil 2 0))) + (is (primitive-arrays-equal #js [#js [] #js []] (apply make-array [nil 2 0]))) + (is (primitive-arrays-equal #js [#js [nil]] (make-array nil 1 1))) + (is (primitive-arrays-equal #js [#js [nil]] (apply make-array [nil 1 1]))) + (is (primitive-arrays-equal #js [#js [nil] #js [nil]] (make-array nil 2 1))) + (is (primitive-arrays-equal #js [#js [nil] #js [nil]] (apply make-array [nil 2 1]))) + (is (primitive-arrays-equal #js [#js [nil nil] #js [nil nil]] (make-array nil 2 2))) + (is (primitive-arrays-equal #js [#js [nil nil] #js [nil nil]] (apply make-array [nil 2 2]))) + (is (primitive-arrays-equal #js [] (make-array nil 0 0 0))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0 0 0]))) + (is (primitive-arrays-equal #js [] (make-array nil 0 1 1))) + (is (primitive-arrays-equal #js [] (apply make-array [nil 0 1 1]))) + (is (primitive-arrays-equal #js [#js []] (make-array nil 1 0 0))) + (is (primitive-arrays-equal #js [#js []] (apply make-array [nil 1 0 0]))) + (is (primitive-arrays-equal #js [#js [] #js []] (make-array nil 2 0 0))) + (is (primitive-arrays-equal #js [#js [] #js []] (apply make-array [nil 2 0 0]))) + (is (primitive-arrays-equal #js [#js [#js []]] (make-array nil 1 1 0))) + (is (primitive-arrays-equal #js [#js [#js []]] (apply make-array [nil 1 1 0]))) + (is (primitive-arrays-equal #js [#js [#js [nil]]] (make-array nil 1 1 1))) + (is (primitive-arrays-equal #js [#js [#js [nil]]] (apply make-array [nil 1 1 1]))) + (is (primitive-arrays-equal #js [#js [#js [nil nil] #js [nil nil]] #js [#js [nil nil] #js [nil nil]]] + (make-array nil 2 2 2))) + (is (primitive-arrays-equal #js [#js [#js [nil nil] #js [nil nil]] #js [#js [nil nil] #js [nil nil]]] + (apply make-array [nil 2 2 2]))))) + +(deftest test-comparable + (testing "Testing IComparable" + (is (= 0 (compare false false))) + (is (= -1 (compare false true))) + (is (= 1 (compare true false))) + + (is (= -1 (compare 0 1))) + (is (= -1 (compare -1 1))) + (is (= 0 (compare 1 1))) + (is (= 1 (compare 1 0))) + (is (= 1 (compare 1 -1))) + + (is (= 0 (compare "cljs" "cljs"))) + (is (= 0 (compare :cljs :cljs))) + (is (= 0 (compare 'cljs 'cljs))) + (is (= -1 (compare "a" "b"))) + (is (= -1 (compare :a :b))) + (is (= -1 (compare 'a 'b))) + ;; cases involving ns + (is (= -1 (compare :b/a :c/a))) + (is (= -1 (compare :c :a/b))) + (is (= 1 (compare :a/b :c))) + (is (= -1 (compare 'b/a 'c/a))) + (is (= -1 (compare 'c 'a/b))) + (is (= 1 (compare 'a/b 'c))) + + ;; This is different from clj. clj gives -2 next 3 tests + (is (= -1 (compare "a" "c"))) + (is (= -1 (compare :a :c))) + (is (= -1 (compare 'a 'c))) + + (is (= -1 (compare [1 2] [1 1 1]))) + (is (= -1 (compare [1 2] [1 2 1]))) + (is (= -1 (compare [1 1] [1 2]))) + (is (= 0 (compare [1 2] [1 2]))) + (is (= 1 (compare [1 2] [1 1]))) + (is (= 1 (compare [1 1 1] [1 2]))) + (is (= 1 (compare [1 1 2] [1 1 1]))) + (is (= 0 (compare [] []))) + (is (= 0 (compare (vec #js []) []))) + (is (= 0 (compare (with-meta [] {}) []))) + (is (= 0 (compare (pop [1]) []))) + + (is (= -1 (compare (subvec [1 2 3] 1) (subvec [1 2 4] 1)))) + (is (= 0 (compare (subvec [1 2 3] 1) (subvec [1 2 3] 1)))) + (is (= 1 (compare (subvec [1 2 4] 1) (subvec [1 2 3] 1)))) + (is (= 0 (compare (subvec [1] 0 0) (subvec [2] 0 0)))) + + (is (= 0 (compare (js/Date. 2015 2 8 19 13 00 999) + (js/Date. 2015 2 8 19 13 00 999)))) + (is (= -1 (compare (js/Date. 2015 2 8 19 12 00 999) + (js/Date. 2015 2 8 19 13 00 999)))) + (is (= 1 (compare (js/Date. 2015 2 8 19 14 00 999) + (js/Date. 2015 2 8 19 13 00 999)))) + )) + +(deftest test-dot + (let [s "abc"] + (testing "Testing dot operations" + (is (= 3 (.-length s))) + (is (= 3 (. s -length))) + (is (= 3 (. (str 138) -length))) + (is (= 3 (. "abc" -length))) + (is (= "bc" (.substring s 1))) + (is (= "bc" (.substring "abc" 1))) + (is (= "bc" ((memfn substring start) s 1))) + (is (= "bc" (. s substring 1))) + (is (= "bc" (. s (substring 1)))) + (is (= "bc" (. s (substring 1 3)))) + (is (= "bc" (.substring s 1 3))) + (is (= "ABC" (. s (toUpperCase)))) + (is (= "ABC" (. "abc" (toUpperCase)))) + (is (= "ABC" ((memfn toUpperCase) s))) + (is (= "BC" (. (. s (toUpperCase)) substring 1))) + (is (= 2 (.-length (. (. s (toUpperCase)) substring 1)))) + ))) \ No newline at end of file diff --git a/src/test/cljs/cljs/test_runner.cljs b/src/test/cljs/cljs/test_runner.cljs index f9b798739..7c882dcf5 100644 --- a/src/test/cljs/cljs/test_runner.cljs +++ b/src/test/cljs/cljs/test_runner.cljs @@ -1,6 +1,7 @@ (ns test-runner (:require [cljs.test :refer-macros [run-tests]] [cljs.primitives-test] + [cljs.destructuring-test] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -24,6 +25,7 @@ (run-tests 'cljs.primitives-test + 'cljs.destructuring-test 'cljs.core-test 'cljs.reader-test 'clojure.string-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 784657616..3865f7cbc 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -244,6 +244,7 @@ '(ns parity.core (:require [cljs.test :refer-macros [run-tests]] [cljs.primitives-test] + [cljs.destructuring-test] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -268,6 +269,7 @@ (eval-form st 'parity.core '(run-tests 'cljs.primitives-test + 'cljs.destructuring-test 'cljs.core-test 'cljs.reader-test 'clojure.string-test From 11d70ccb3f8048e964b5ab192cc5ec597cfd490e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 16:25:18 -0400 Subject: [PATCH 2034/4033] move more tests, add new-new and printing tests --- src/test/cljs/cljs/core_test.cljs | 538 --------------------- src/test/cljs/cljs/destructuring_test.cljs | 63 +++ src/test/cljs/cljs/new_new_test.cljs | 118 +++++ src/test/cljs/cljs/primitives_test.cljs | 255 +++++++++- src/test/cljs/cljs/test_runner.cljs | 4 + src/test/self/self_parity/test.cljs | 4 + 6 files changed, 443 insertions(+), 539 deletions(-) create mode 100644 src/test/cljs/cljs/new_new_test.cljs diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 4f817e184..bbcc8460b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -121,152 +121,6 @@ (is (= () (rest [1]))) (is (= () (rest (array 1)))))) -;; this fails in v8 - why? -;; (assert (= "symbol\"'string" (pr-str (str 'symbol \" \' "string")))) -(deftest test-misc - (testing "Testing miscellaneous operations" - (is (= 9 (reduce + (next (seq (array 1 2 3 4)))))) - (is (not (= "one" "two"))) - (is (= 3 (count "abc"))) - (is (= 4 (count (array 1 2 3 4)))) - (is (= "c" (nth "abc" 2))) - (is (= "quux" (nth "abc" 3 "quux"))) - (is (= 1 (nth (array 1 2 3 4) 0))) - (is (= "val" (nth (array 1 2 3 4) 4 "val"))) - (is (= "b" (get "abc" 1))) - (is (= "harriet" (get "abcd" 4 "harriet"))) - (is (= 4 (get (array 1 2 3 4) 3))) - (is (= "zot" (get (array 1 2 3 4) 4 "zot"))) - (is (= 10 (reduce + (array 1 2 3 4)))) - (is (= 20 (reduce + 10 (array 1 2 3 4)))) - (is (= "cabd" (let [jumble (fn [a b] (str (apply str (reverse (str a))) b))] - (reduce jumble "abcd")))) - (is (= "cafrogbd" (let [jumble (fn [a b] (str (apply str (reverse (str a))) b))] - (reduce jumble "frog" "abcd")))) - (is (= [3] (nthnext [1 2 3] 2))) - (assert (not= 1 2)) - (is (not (not= 1 1))) - (is (not (not-empty []))) - (is (boolean (not-empty [1 2 3]))) - (is (= "joel" (min-key count "joel" "tom servo" "crooooooooow"))) - (is (= "crooooooooow" (max-key count "joel" "tom servo" "crooooooooow"))) - (is (= (partition-all 4 [1 2 3 4 5 6 7 8 9]) - [[1 2 3 4] [5 6 7 8] [9]])) - (is (= (partition-all 4 2 [1 2 3 4 5 6 7 8 9]) - [[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]])) - (is (= [true true] (take-while true? [true true 2 3 4]))) - (is (= [[true true] [false false false] [true true]] - (partition-by true? [true true false false false true true]))) - (is (= [0 2 4 6 8 10] (take-nth 2 [0 1 2 3 4 5 6 7 8 9 10]))) - (let [sf (some-fn number? keyword? symbol?)] - (testing "Testing some-fn" - (is (sf :foo 1)) - (is (sf :foo)) - (is (sf 'bar 1)) - (is (not (sf [] ()))))) - (let [ep (every-pred number? zero?)] - (testing "Testing every-pred" - (is (ep 0 0 0)) - (is (not (ep 1 2 3 0))))) - (is ((complement number?) :foo)) - (is (= [1 [2 3] [1 2 3]] ((juxt first rest seq) [1 2 3]))) - (is (= 5 (max 1 2 3 4 5))) - (is (= 5 (max 5 4 3 2 1))) - (is (= 5.5 (max 1 2 3 4 5 5.5))) - (is (= 1 (min 5 4 3 2 1))) - (is (= 1 (min 1 2 3 4 5))) - (is (= 0.5 (min 5 4 3 0.5 2 1))) - (let [x (array 1 2 3)] - (testing "Testing setting property on JS array" - (set! (.-foo x) :hello) - (is (= (.-foo x) :hello)))) - ;; last - (is (= nil (last nil))) - (is (= 3 (last [1 2 3]))) - ;; dotimes - (let [s (atom [])] - (dotimes [n 5] - (swap! s conj n)) - (is (= [0 1 2 3 4] @s))) - ;; doseq - (let [v [1 2 3 4 5] - s (atom ())] - (doseq [n v] (swap! s conj n)) - (is (= @s (reverse v)))) - ;; memoize - (let [f (memoize (fn [] (rand)))] - (f) - (is (= (f) (f)))) - ;; range - (is (= (range 10) (list 0 1 2 3 4 5 6 7 8 9))) - (is (= (range 10 20) (list 10 11 12 13 14 15 16 17 18 19))) - (is (= (range 10 20 2) (list 10 12 14 16 18))) - (is (= (take 20 (range)) (list 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19))) - ;; group-by - (let [d (group-by second {:a 1 :b 2 :c 1 :d 4 :e 1 :f 2})] - (testing "group-by" - (is (= 3 (count (get d 1)))) - (is (= 2 (count (get d 2)))) - (is (= 1 (count (get d 4)))))) - (is (= {1 2 3 4 5 6} (merge {1 2} {3 4} {5 6}))) - (is (= {1 2 3 4} (merge {1 2} {3 4} nil))) - ;; frequencies - (is (= {:a 3 :b 2} (frequencies [:a :b :a :b :a]))) - ;; reductions - (is (= [1 3 6 10 15] (reductions + [1 2 3 4 5]))) - ;; keep - (is (= [1 3 5 7 9] (keep #(if (odd? %) %) [1 2 3 4 5 6 7 8 9 10]))) - (is (= [2 4 6 8 10] (keep #(if (even? %) %) [1 2 3 4 5 6 7 8 9 10]))) - ;; keep-indexed - (is (= [1 3 5 7 9] (keep-indexed #(if (odd? %1) %2) [0 1 2 3 4 5 6 7 8 9 10]))) - (is (= [2 4 5] (keep-indexed #(if (pos? %2) %1) [-9 0 29 -7 45 3 -8]))) - ;; map-indexed - (is (= [[0 :a] [1 :b] [2 :c]] (map-indexed #(vector % %2) [:a :b :c]))) - ;; merge-with - (is (= '{"Foo" ("foo" "FOO" "fOo"), "Bar" ("bar" "BAR" "BAr"), "Baz" ["baz"], "Qux" ["qux" "quux"]} - (merge-with concat - {"Foo" ["foo" "FOO"] - "Bar" ["bar" "BAR"] - "Baz" ["baz"]} - {"Foo" ["fOo"] - "Bar" ["BAr"] - "Qux" ["qux" "quux"]}))) - (is (= {:a 111, :b 102, :c 13} - (merge-with + - {:a 1 :b 2 :c 3} - {:a 10 :c 10} - {:a 100 :b 100}))) - (is (= {:a 3, :b 102, :c 13} - (apply merge-with [+ - {:a 1 :b 100} - {:a 1 :b 2 :c 3} - {:a 1 :c 10}]))) - (is (= '[a c e] (replace '[a b c d e] [0 2 4]))) - (is (= [:one :zero :two :zero] - (replace {0 :zero 1 :one 2 :two} '(1 0 2 0)))) - ;; split-at - (is (= [[1 2] [3 4 5]] (split-at 2 [1 2 3 4 5]))) - ;; split-with - (is (= [[1 2 3] [4 5]] (split-with (partial >= 3) [1 2 3 4 5]))) - ;; trampoline - (is (= 10000 (trampoline (fn f [n] (if (>= n 10000) n #(f (inc n)))) 0))) - ;; vary-meta - (is (= {:a 1} (meta (vary-meta [] assoc :a 1)))) - (is (= {:a 1 :b 2} (meta (vary-meta (with-meta [] {:b 2}) assoc :a 1)))) - ;; comparator - (is (= [1 1 2 2 3 5] (seq (.sort (to-array [2 3 1 5 2 1]) (comparator <))))) - (is (= [5 3 2 2 1 1] (seq (.sort (to-array [2 3 1 5 2 1]) (comparator >))))) - (is (= (hash 'foo) (hash (symbol "foo")))) - (is (= (hash 'foo/bar) (hash (symbol "foo" "bar")))) - (is (= (lazy-cat [1] [2] [3]) '(1 2 3))) - ;; Make sure take/drop raise an error when given nil as an argument - (is (try (do (take nil [1 2 3]) false) - (catch js/Error e true))) - (is (try (do (drop nil [1 2 3]) false) - (catch js/Error e true))) - (is (try (do (take-nth nil [1 2 3]) false) - (catch js/Error e true))))) - (deftest test-fn-with-metadata (let [f (fn [x] (* x 2)) m {:foo "bar"} @@ -1005,145 +859,6 @@ (is (== (imul 0xfffffffe 5) -10)) )) -(deftest test-print-knobs - (testing "Testing printing knobs" - (is (= (binding [*print-length* 0] (str [1 2 3 4 5 6 7 8 9 0])) - "[...]")) - (is (= (binding [*print-length* 1] (str [1 2 3 4 5 6 7 8 9 0])) - "[1 ...]")) - (is (= (binding [*print-length* 2] (str [1 2 3 4 5 6 7 8 9 0])) - "[1 2 ...]")) - (is (= (binding [*print-length* 10] (str [1 2 3 4 5 6 7 8 9 0])) - "[1 2 3 4 5 6 7 8 9 0]")) - ;; CLJS-804 - (is (= (binding [*print-length* 10] (str {:foo "bar"})) - "{:foo \"bar\"}")) - (is (= (binding [*print-length* 0] (str {:foo "bar" :baz "woz"})) - "{...}")) - (is (#{"{:foo \"bar\", ...}" "{:baz \"woz\", ...}"} - (binding [*print-length* 1] (str {:foo "bar" :baz "woz"})))) - (is (#{"{:foo \"bar\", :baz \"woz\"}" "{:baz \"woz\", :foo \"bar\"}"} - (binding [*print-length* 10] (str {:foo "bar" :baz "woz"}))))) - ) - -(deftest test-print-with-opts - (testing "Testing printing with opts - :more-marker" - ; CLJS-1016 - (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "" :print-length 0}) - "[]")) - (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "\u2026" :print-length 1}) - "[1 \u2026]")) - (is (#{"#{1 2 \u2026}" "#{1 3 \u2026}" - "#{2 1 \u2026}" "#{2 3 \u2026}" - "#{3 1 \u2026}" "#{3 2 \u2026}"} - (pr-str-with-opts [#{1 2 3}] {:more-marker "\u2026" :print-length 2}))) - (is (= (pr-str-with-opts ['(1 2 3)] {:more-marker "\u2026" :print-length 2}) - "(1 2 \u2026)")) - (is (#{"{:1 1, :2 2, \u2026}" "{:1 1, :3 3, \u2026}" - "{:2 2, :1 1, \u2026}" "{:2 2, :3 3, \u2026}" - "{:3 3, :1 1, \u2026}" "{:3 3, :2 2, \u2026}"} - (pr-str-with-opts [{:1 1 :2 2 :3 3}] {:more-marker "\u2026" :print-length 2})))) - - (testing "Testing printing with opts - :alt-impl" - ; CLJS-1010 - (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts] ((:fallback-impl opts) obj writer opts))}) - "[1 2 3]")) - (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts] (-write writer (str "<" obj ">")))}) - "<[1 2 3]>")) - (is (= (pr-str-with-opts [[:start 1 2 [:middle] 3 4 :end] :standalone] {:alt-impl (fn [obj writer opts] - (if (keyword? obj) - (-write writer (str "|" (name obj) "|")) - ((:fallback-impl opts) obj writer opts)))}) - "[|start| 1 2 [|middle|] 3 4 |end|] |standalone|")) - (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts])}) - ""))) - ) - -(defrecord PrintMe [a b]) - -(deftest test-printing - (testing "Testing pr-str" - (is (= (pr-str) "")) - (is (= (pr-str 1) "1")) - (is (= (pr-str -1) "-1")) - (is (= (pr-str -1.5) "-1.5")) - (is (= (pr-str [3 4]) "[3 4]")) - (is (= (pr-str "foo") "\"foo\"")) - (is (= (pr-str :hello) ":hello")) - (is (= (pr-str 'goodbye) "goodbye")) - ;;(is (= (pr-str #{1 2 3}) "#{1 2 3}")) - (is (= (pr-str '(7 8 9)) "(7 8 9)")) - (is (= (pr-str '(deref foo)) "(deref foo)")) - (is (= (pr-str '(quote bar)) "(quote bar)")) - (is (= (pr-str 'foo/bar) "foo/bar")) - (is (= (pr-str \a) "\"a\"")) - (is (= (pr-str :foo/bar) ":foo/bar")) - (is (= (pr-str nil) "nil")) - (is (= (pr-str true) "true")) - (is (= (pr-str false) "false")) - (is (= (pr-str "string") "\"string\"")) - (is (= (pr-str ["üñîçó∂£" :ทดสอบ/你好 'こんにちは]) "[\"üñîçó∂£\" :ทดสอบ/你好 こんにちは]")) - (is (= (pr-str "escape chars \t \r \n \\ \" \b \f") "\"escape chars \\t \\r \\n \\\\ \\\" \\b \\f\"")) - (is (= (pr-str (PrintMe. 1 2)) "#cljs.core-test.PrintMe{:a 1, :b 2}")) - (is (= (pr-str (js/Date. "2010-11-12T13:14:15.666-05:00")) - "#inst \"2010-11-12T18:14:15.666-00:00\"")) - (doseq [month (range 1 13) - day (range 1 29) - hour (range 1 23)] - (let [pad (fn [n] - (if (< n 10) - (str "0" n) - n)) - inst (str "2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-00:00")] - (is (= (pr-str (js/Date. inst)) (str "#inst \"" inst "\""))))) - (let [uuid-str "550e8400-e29b-41d4-a716-446655440000" - uuid (cljs.core/uuid uuid-str)] - (is (= (pr-str uuid) (str "#uuid \"" uuid-str "\"")))) - ;; pr-str PersistentQueueSeq - CLJS-800 - (is (= (pr-str (rest (conj cljs.core.PersistentQueue.EMPTY 1 2 3))) "(2 3)")) - (is (= "\"asdf\" \"asdf\"" (pr-str "asdf" "asdf"))) - ;; Different hash map order on self-host - (is (#{"[1 true {:a 2, :b #\"x\\\"y\"} #js [3 4]]" - "[1 true {:b #\"x\\\"y\", :a 2} #js [3 4]]"} - (pr-str [1 true {:a 2 :b #"x\"y"} (array 3 4)])))) - (testing "Testing print-str" - (is (= (print-str "asdf") "asdf"))) - (testing "Testing println-str" - (is (= (println-str "asdf") "asdf\n"))) - (testing "Testing prn-str" - (is (= (prn-str) "\n")) - (is (= (prn-str "asdf") "\"asdf\"\n")) - ;; Different hash map order on self-host - (is (#{"[1 true {:a 2, :b 42} #js [3 4]]\n" - "[1 true {:b 42, :a 2} #js [3 4]]\n"} - (prn-str [1 true {:a 2 :b 42} (array 3 4)])))) - (testing "Testing with-out-str" - (is (= "12" (with-out-str (print 1) (print 2)))) - (is (= "12" (with-out-str (*print-fn* 1) (*print-fn* 2)))))) - -(deftest test-inext - (testing "Testing INext" - (is (= nil (next nil))) - (is (= nil (next (seq (array 1))))) - (is (= '(2 3) (next (seq (array 1 2 3))))) - (is (= nil (next (reverse (seq (array 1)))))) - (is (= '(2 1) (next (reverse (seq (array 1 2 3)))))) - (is (= nil (next (cons 1 nil)))) - (is (= '(2 3) (next (cons 1 (cons 2 (cons 3 nil)))))) - (is (= nil (next (lazy-seq (cons 1 nil))))) - (is (= '(2 3) (next (lazy-seq - (cons 1 - (lazy-seq - (cons 2 - (lazy-seq (cons 3 nil))))))))) - (is (= nil (next (list 1)))) - (is (= '(2 3) (next (list 1 2 3)))) - (is (= nil (next [1]))) - (is (= '(2 3) (next [1 2 3]))) - (is (= nil (next (range 1 2)))) - (is (= '(2 3) (next (range 1 4)))) - )) - (deftest test-lazy-seq-realized? (testing "Testing LazySeq IPending" (let [xs (lazy-seq @@ -1212,207 +927,6 @@ (is (= expected (hash uuid))) (is (= expected (.-__hash uuid)))))) -(defrecord Person [firstname lastname]) -(defrecord A []) -(defrecord C [a b c]) -(defrecord A' [x]) -(defrecord B' [x]) -(defrecord FooComparable [x] - IComparable - (-compare [_ o] (compare x (.-x o)))) - -(deftest test-records - (let [fred (Person. "Fred" "Mertz") - fred-too (Person. "Fred" "Mertz") - ethel (with-meta (assoc (Person. "Ethel" "Mertz") :husband :fred) - {:married true}) - ethel-too (with-meta (assoc (Person. "Ethel" "Mertz") :husband :fred) - {:married true}) - letters (C. "a" "b" "c") - more-letters (assoc letters :d "d" :e "e" :f "f")] - (testing "Testing records" - (is (record? fred)) - (is (not (record? {}))) - (is (= (:firstname fred) "Fred")) - (is (= fred fred-too)) - (is (false? (= fred nil))) - (is (false? (= nil fred))) - (is (= (meta ethel) {:married true})) - (is (= ethel ethel-too)) - (is (= (map->Person {:firstname "Fred" :lastname "Mertz"}) fred)) - (is (= (->Person "Fred" "Mertz") fred)) - (is (= (count fred) 2)) - (is (= (count ethel) 3)) - (is (= (conj fred {:wife :ethel :friend :ricky}) - (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel :friend :ricky}))) - (is (= (conj fred {:lastname "Flintstone"}) - (map->Person {:firstname "Fred" :lastname "Flintstone"}))) - (is (= (assoc fred :lastname "Flintstone") - (map->Person {:firstname "Fred" :lastname "Flintstone"}))) - (is (= (assoc fred :wife :ethel) - (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel}))) - (is (= (dissoc ethel :husband) - (map->Person {:firstname "Ethel" :lastname "Mertz"}))) - (is (= {:foo 'bar} (meta (with-meta (A.) {:foo 'bar})))) - (is (= 'bar (:foo (assoc (A.) :foo 'bar)))) - (is (= (set (keys letters)) #{:a :b :c})) - (is (= (set (keys more-letters)) #{:a :b :c :d :e :f})) - (is (= (set (keys (dissoc more-letters :d))) #{:a :b :c :e :f})) - (is (= (set (keys (dissoc more-letters :d :e))) #{:a :b :c :f})) - (is (= (set (keys (dissoc more-letters :d :e :f))) #{:a :b :c})) - (is (not= (A'. nil) (B'. nil))) - (is (satisfies? IComparable (->FooComparable 1)))))) - -(deftype FnLike [] - IFn - (-invoke [_] :a) - (-invoke [_ a] :b) - (-invoke [_ a b] :c)) - -(deftype FnLikeB [a] - IFn - (-invoke [_] a)) - -(deftest test-ifn - (testing "Testing IFn implementations" - (is (= :a ((FnLike.)))) - (is (= :b ((FnLike.) 1))) - (is (= :c ((FnLike.) 1 2))) - (is (= [:b :b :b] (map (FnLike.) [0 0 0]))) - (is (= 1 ((FnLikeB. 1)))) - )) - -(deftest test-case - (testing "Test case expr" - (let [x 1] - (is (= (case x 1 :one) :one))) - (let [x 1] - (is (= (case x 2 :two :default) :default))) - (let [x 1] - (is (= (try - (case x 3 :three) - (catch js/Error e - :fail)) - :fail))) - (let [x 1] - (is (= (case x - (1 2 3) :ok - :fail) - :ok))) - (let [x [:a :b]] - (is (= (case x - [:a :b] :ok) - :ok))) - (let [a 'a] - (is (= (case a - nil nil - & :amp - :none) - :none))) - (let [a '&] - (is (= (case a - nil nil - & :amp - :none) - :amp))) - (let [foo 'a] - (testing "multiple match" - (is (= (case foo - (a b c) :sym - :none) - :sym)) - (is (= (case foo - (b c d) :sym - :none) - :none)))) - )) - -(defprotocol IHasFirst - (-get-first [this])) - -(defprotocol IFindsFirst - (-find-first [this other])) - -(deftype First [xs] - ISeqable - (-seq [this] (seq xs)) - IIndexed - (-nth [this i] (nth xs i)) - (-nth [this i not-found] (nth xs i not-found)) - IFn - (-invoke [[x]] x) - (-invoke [this x] this) - Object - (toString [[x]] (str x)) - IHasFirst - (-get-first [[x]] x) - IFindsFirst - (-find-first [_ [x]] x)) - -(deftype DestructuringWithLocals [a] - IFindsFirst - (-find-first [_ [x y]] - [x y a])) - -(deftest test-protocol-method-destructuring - (testing "Testing protocol method destructuring" - (let [fv (First. [1 2 3]) - fs (First. "asdf")] - (testing "basic operations" - (is (= (fv) 1)) - (is (= (fs) \a)) - (is (= (str fs) \a)) - (is (= (-get-first fv) 1)) - (is (= (-get-first fs) \a)) - (is (= (-find-first fv [1]) 1)) - (is (identical? (fv 1) fv)))) - (let [t (DestructuringWithLocals. 1)] - (testing "with locals" - (is (= [2 3 1] (-find-first t [2 3]))))))) - -(defprotocol IProtocolWithDocStrings - (-method1 [this] "some doc") - (-method2 [this] "")) - -(deftest test-type - (is (= nil (type nil))) - (is (= js/Number (type 0))) - (is (= js/Number (type js/NaN))) - (is (= js/Number (type js/Infinity))) - (is (= js/String (type ""))) - (is (= js/Boolean (type true))) - (is (= js/Boolean (type false))) - (is (= js/Function (type identity))) - (is (= js/Function (type (fn [x] x)))) - (is (= js/Object (type (js-obj)))) - (is (= js/Array (type (array)))) - (is (= js/Date (type (js/Date.)))) - (is (= js/Function (type js/Object)))) - -(deftest test-instance? - (is (not (instance? js/Object nil))) - (is (not (instance? js/Number 0))) - (is (not (instance? js/Number js/NaN))) - (is (not (instance? js/Number js/Infinity))) - (is (not (instance? js/String ""))) - (is (not (instance? js/Boolean true))) - (is (not (instance? js/Boolean false))) - (is (instance? js/Number (js/Number. 0))) - (is (instance? js/Object (js/Number. 0))) - (is (instance? js/String (js/String. ""))) - (is (instance? js/Object (js/String. ""))) - (is (instance? js/Boolean (js/Boolean.))) - (is (instance? js/Object (js/Boolean.))) - (is (instance? js/Function identity)) - (is (instance? js/Object identity)) - (is (instance? js/Function (fn [x] x))) - (is (instance? js/Object (js-obj))) - (is (instance? js/Array (array))) - (is (instance? js/Object (array))) - (is (instance? js/Date (js/Date.))) - (is (instance? js/Object (js/Date.))) - (is (instance? js/Function js/Object))) - ;; ============================================================================= ;; Tickets @@ -1450,17 +964,6 @@ (is (= (hash :a) (hash (keyword "a"))))) -(defprotocol IBar (-bar [this x])) - -(defn baz [f] - (reify - IBar - (-bar [_ x] - (f x)))) - -(deftest test-405 - (is (= 2 (-bar (baz inc) 1)))) - (let [x "original"] (defn original-closure-stmt [] x)) @@ -1720,18 +1223,6 @@ [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] [2 1] [3 1] [1 0] [2 0] [3 0]))))) -(defprotocol IWoz - (-woz [this])) - -(def noz []) - -(deftest test-414 - (testing "Testing CLJS-414, specify" - (is (= (specify noz IWoz (-woz [_] :boz)) noz)) - (is (not (identical? (specify noz IWoz (-woz [_] :boz)) noz))) - (is (= (-woz (specify noz IWoz (-woz [this] this))) noz)) - (is (= (-woz (specify noz IWoz (-woz [_] :boz))) :boz)))) - (deftest test-734 (testing "Testing CLJS-734, transient operations" (is (= (-> (transient []) (conj! 1 2) persistent!) [1 2])) @@ -2303,35 +1794,6 @@ (deftest test-cljs-1199 (testing "array-map should skip dropped elements of IndexedSeq" (is (= {:a 1} (apply array-map (drop 1 [0 :a 1])))))) - -(defn foo-1216 - ([a] (foo-1216 a 10)) - ([a b & [c]] [a b c])) - -(defn destructure-1216 - ([kvs] kvs) - ([k v & args] [k v args])) - -(deftest test-cljs-1216 - (testing "varargs regression" - (is (= (foo-1216 1) [1 10 nil])) - (is (= (foo-1216 1 2) [1 2 nil])) - (is (= (foo-1216 1 2 3) [1 2 3])) - (is (= [1 2 [3 4]] - (destructure-1216 1 2 3 4))) - (is (= [1 2 [3 4]] - (apply destructure-1216 [1 2 3 4]))) - (is (= (destructure-1216 1 2 3 4)[1 2 [3 4]] - (apply destructure-1216 [1 2 3 4]))))) - -(deftype TypeBasis [a b]) - -(defrecord RecordBasis [c d e]) - -(deftest test-get-basis - (is (= (.getBasis TypeBasis) '[a b])) - (is (= (.getBasis RecordBasis) '[c d e]))) - (deftest test-cljs-1212 (is (= (set {:a 0 :b 0 :c 0 :d 0 :e 0 :f 0 :g 0 :h 0 :i 0}) #{[:a 0] [:b 0] [:c 0] [:d 0] [:e 0] [:f 0] [:g 0] [:h 0] [:i 0]}))) diff --git a/src/test/cljs/cljs/destructuring_test.cljs b/src/test/cljs/cljs/destructuring_test.cljs index 6a87fc0eb..9f1007d9a 100644 --- a/src/test/cljs/cljs/destructuring_test.cljs +++ b/src/test/cljs/cljs/destructuring_test.cljs @@ -83,3 +83,66 @@ (deftest resolve-keyword-ns-alias-in-destructuring (let [{:keys [::s/x ::s/y ::s/z] :or {z 3}} {:clojure.string/x 1 :clojure.string/y 2}] (is (= [1 2 3] [x y z])))) + +(defprotocol IHasFirst + (-get-first [this])) + +(defprotocol IFindsFirst + (-find-first [this other])) + +(deftype First [xs] + ISeqable + (-seq [this] (seq xs)) + IIndexed + (-nth [this i] (nth xs i)) + (-nth [this i not-found] (nth xs i not-found)) + IFn + (-invoke [[x]] x) + (-invoke [this x] this) + Object + (toString [[x]] (str x)) + IHasFirst + (-get-first [[x]] x) + IFindsFirst + (-find-first [_ [x]] x)) + +(deftype DestructuringWithLocals [a] + IFindsFirst + (-find-first [_ [x y]] + [x y a])) + +(deftest test-protocol-method-destructuring + (testing "Testing protocol method destructuring" + (let [fv (First. [1 2 3]) + fs (First. "asdf")] + (testing "basic operations" + (is (= (fv) 1)) + (is (= (fs) \a)) + (is (= (str fs) \a)) + (is (= (-get-first fv) 1)) + (is (= (-get-first fs) \a)) + (is (= (-find-first fv [1]) 1)) + (is (identical? (fv 1) fv)))) + (let [t (DestructuringWithLocals. 1)] + (testing "with locals" + (is (= [2 3 1] (-find-first t [2 3]))))))) + +(defn destructure-1216 + ([kvs] kvs) + ([k v & args] [k v args])) + +(defn foo-1216 + ([a] (foo-1216 a 10)) + ([a b & [c]] [a b c])) + +(deftest test-cljs-1216 + (testing "varargs regression" + (is (= (foo-1216 1) [1 10 nil])) + (is (= (foo-1216 1 2) [1 2 nil])) + (is (= (foo-1216 1 2 3) [1 2 3])) + (is (= [1 2 [3 4]] + (destructure-1216 1 2 3 4))) + (is (= [1 2 [3 4]] + (apply destructure-1216 [1 2 3 4]))) + (is (= (destructure-1216 1 2 3 4)[1 2 [3 4]] + (apply destructure-1216 [1 2 3 4]))))) \ No newline at end of file diff --git a/src/test/cljs/cljs/new_new_test.cljs b/src/test/cljs/cljs/new_new_test.cljs new file mode 100644 index 000000000..b1c3970fe --- /dev/null +++ b/src/test/cljs/cljs/new_new_test.cljs @@ -0,0 +1,118 @@ +; 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.new-new-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.string :as s] + [clojure.set :as set])) + +(defprotocol IProtocolWithDocStrings + (-method1 [this] "some doc") + (-method2 [this] "")) + +(defprotocol IBar (-bar [this x])) + +(defn baz [f] + (reify + IBar + (-bar [_ x] + (f x)))) + +(deftest test-405 + (is (= 2 (-bar (baz inc) 1)))) + +(defprotocol IWoz + (-woz [this])) + +(def noz []) + +(deftest test-414 + (testing "Testing CLJS-414, specify" + (is (= (specify noz IWoz (-woz [_] :boz)) noz)) + (is (not (identical? (specify noz IWoz (-woz [_] :boz)) noz))) + (is (= (-woz (specify noz IWoz (-woz [this] this))) noz)) + (is (= (-woz (specify noz IWoz (-woz [_] :boz))) :boz)))) + +(defrecord Person [firstname lastname]) +(defrecord A []) +(defrecord C [a b c]) +(defrecord A' [x]) +(defrecord B' [x]) +(defrecord FooComparable [x] + IComparable + (-compare [_ o] (compare x (.-x o)))) + +(deftest test-records + (let [fred (Person. "Fred" "Mertz") + fred-too (Person. "Fred" "Mertz") + ethel (with-meta (assoc (Person. "Ethel" "Mertz") :husband :fred) + {:married true}) + ethel-too (with-meta (assoc (Person. "Ethel" "Mertz") :husband :fred) + {:married true}) + letters (C. "a" "b" "c") + more-letters (assoc letters :d "d" :e "e" :f "f")] + (testing "Testing records" + (is (record? fred)) + (is (not (record? {}))) + (is (= (:firstname fred) "Fred")) + (is (= fred fred-too)) + (is (false? (= fred nil))) + (is (false? (= nil fred))) + (is (= (meta ethel) {:married true})) + (is (= ethel ethel-too)) + (is (= (map->Person {:firstname "Fred" :lastname "Mertz"}) fred)) + (is (= (->Person "Fred" "Mertz") fred)) + (is (= (count fred) 2)) + (is (= (count ethel) 3)) + (is (= (conj fred {:wife :ethel :friend :ricky}) + (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel :friend :ricky}))) + (is (= (conj fred {:lastname "Flintstone"}) + (map->Person {:firstname "Fred" :lastname "Flintstone"}))) + (is (= (assoc fred :lastname "Flintstone") + (map->Person {:firstname "Fred" :lastname "Flintstone"}))) + (is (= (assoc fred :wife :ethel) + (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel}))) + (is (= (dissoc ethel :husband) + (map->Person {:firstname "Ethel" :lastname "Mertz"}))) + (is (= {:foo 'bar} (meta (with-meta (A.) {:foo 'bar})))) + (is (= 'bar (:foo (assoc (A.) :foo 'bar)))) + (is (= (set (keys letters)) #{:a :b :c})) + (is (= (set (keys more-letters)) #{:a :b :c :d :e :f})) + (is (= (set (keys (dissoc more-letters :d))) #{:a :b :c :e :f})) + (is (= (set (keys (dissoc more-letters :d :e))) #{:a :b :c :f})) + (is (= (set (keys (dissoc more-letters :d :e :f))) #{:a :b :c})) + (is (not= (A'. nil) (B'. nil))) + (is (satisfies? IComparable (->FooComparable 1)))))) + +(deftype FnLike [] + IFn + (-invoke [_] :a) + (-invoke [_ a] :b) + (-invoke [_ a b] :c)) + +(deftype FnLikeB [a] + IFn + (-invoke [_] a)) + +(deftest test-ifn + (testing "Testing IFn implementations" + (is (= :a ((FnLike.)))) + (is (= :b ((FnLike.) 1))) + (is (= :c ((FnLike.) 1 2))) + (is (= [:b :b :b] (map (FnLike.) [0 0 0]))) + (is (= 1 ((FnLikeB. 1)))) + )) + +(deftype TypeBasis [a b]) + +(defrecord RecordBasis [c d e]) + +(deftest test-get-basis + (is (= (.getBasis TypeBasis) '[a b])) + (is (= (.getBasis RecordBasis) '[c d e]))) diff --git a/src/test/cljs/cljs/primitives_test.cljs b/src/test/cljs/cljs/primitives_test.cljs index 6655d97b5..caa56bd09 100644 --- a/src/test/cljs/cljs/primitives_test.cljs +++ b/src/test/cljs/cljs/primitives_test.cljs @@ -674,4 +674,257 @@ (is (= "ABC" ((memfn toUpperCase) s))) (is (= "BC" (. (. s (toUpperCase)) substring 1))) (is (= 2 (.-length (. (. s (toUpperCase)) substring 1)))) - ))) \ No newline at end of file + ))) + +(deftest test-type + (is (= nil (type nil))) + (is (= js/Number (type 0))) + (is (= js/Number (type js/NaN))) + (is (= js/Number (type js/Infinity))) + (is (= js/String (type ""))) + (is (= js/Boolean (type true))) + (is (= js/Boolean (type false))) + (is (= js/Function (type identity))) + (is (= js/Function (type (fn [x] x)))) + (is (= js/Object (type (js-obj)))) + (is (= js/Array (type (array)))) + (is (= js/Date (type (js/Date.)))) + (is (= js/Function (type js/Object)))) + +(deftest test-instance? + (is (not (instance? js/Object nil))) + (is (not (instance? js/Number 0))) + (is (not (instance? js/Number js/NaN))) + (is (not (instance? js/Number js/Infinity))) + (is (not (instance? js/String ""))) + (is (not (instance? js/Boolean true))) + (is (not (instance? js/Boolean false))) + (is (instance? js/Number (js/Number. 0))) + (is (instance? js/Object (js/Number. 0))) + (is (instance? js/String (js/String. ""))) + (is (instance? js/Object (js/String. ""))) + (is (instance? js/Boolean (js/Boolean.))) + (is (instance? js/Object (js/Boolean.))) + (is (instance? js/Function identity)) + (is (instance? js/Object identity)) + (is (instance? js/Function (fn [x] x))) + (is (instance? js/Object (js-obj))) + (is (instance? js/Array (array))) + (is (instance? js/Object (array))) + (is (instance? js/Date (js/Date.))) + (is (instance? js/Object (js/Date.))) + (is (instance? js/Function js/Object))) + +(deftest test-case + (testing "Test case expr" + (let [x 1] + (is (= (case x 1 :one) :one))) + (let [x 1] + (is (= (case x 2 :two :default) :default))) + (let [x 1] + (is (= (try + (case x 3 :three) + (catch js/Error e + :fail)) + :fail))) + (let [x 1] + (is (= (case x + (1 2 3) :ok + :fail) + :ok))) + (let [x [:a :b]] + (is (= (case x + [:a :b] :ok) + :ok))) + (let [a 'a] + (is (= (case a + nil nil + & :amp + :none) + :none))) + (let [a '&] + (is (= (case a + nil nil + & :amp + :none) + :amp))) + (let [foo 'a] + (testing "multiple match" + (is (= (case foo + (a b c) :sym + :none) + :sym)) + (is (= (case foo + (b c d) :sym + :none) + :none)))) + )) + +(deftest test-inext + (testing "Testing INext" + (is (= nil (next nil))) + (is (= nil (next (seq (array 1))))) + (is (= '(2 3) (next (seq (array 1 2 3))))) + (is (= nil (next (reverse (seq (array 1)))))) + (is (= '(2 1) (next (reverse (seq (array 1 2 3)))))) + (is (= nil (next (cons 1 nil)))) + (is (= '(2 3) (next (cons 1 (cons 2 (cons 3 nil)))))) + (is (= nil (next (lazy-seq (cons 1 nil))))) + (is (= '(2 3) (next (lazy-seq + (cons 1 + (lazy-seq + (cons 2 + (lazy-seq (cons 3 nil))))))))) + (is (= nil (next (list 1)))) + (is (= '(2 3) (next (list 1 2 3)))) + (is (= nil (next [1]))) + (is (= '(2 3) (next [1 2 3]))) + (is (= nil (next (range 1 2)))) + (is (= '(2 3) (next (range 1 4)))) + )) + +;; this fails in v8 - why? +;; (assert (= "symbol\"'string" (pr-str (str 'symbol \" \' "string")))) +(deftest test-misc + (testing "Testing miscellaneous operations" + (is (= 9 (reduce + (next (seq (array 1 2 3 4)))))) + (is (not (= "one" "two"))) + (is (= 3 (count "abc"))) + (is (= 4 (count (array 1 2 3 4)))) + (is (= "c" (nth "abc" 2))) + (is (= "quux" (nth "abc" 3 "quux"))) + (is (= 1 (nth (array 1 2 3 4) 0))) + (is (= "val" (nth (array 1 2 3 4) 4 "val"))) + (is (= "b" (get "abc" 1))) + (is (= "harriet" (get "abcd" 4 "harriet"))) + (is (= 4 (get (array 1 2 3 4) 3))) + (is (= "zot" (get (array 1 2 3 4) 4 "zot"))) + (is (= 10 (reduce + (array 1 2 3 4)))) + (is (= 20 (reduce + 10 (array 1 2 3 4)))) + (is (= "cabd" (let [jumble (fn [a b] (str (apply str (reverse (str a))) b))] + (reduce jumble "abcd")))) + (is (= "cafrogbd" (let [jumble (fn [a b] (str (apply str (reverse (str a))) b))] + (reduce jumble "frog" "abcd")))) + (is (= [3] (nthnext [1 2 3] 2))) + (assert (not= 1 2)) + (is (not (not= 1 1))) + (is (not (not-empty []))) + (is (boolean (not-empty [1 2 3]))) + (is (= "joel" (min-key count "joel" "tom servo" "crooooooooow"))) + (is (= "crooooooooow" (max-key count "joel" "tom servo" "crooooooooow"))) + (is (= (partition-all 4 [1 2 3 4 5 6 7 8 9]) + [[1 2 3 4] [5 6 7 8] [9]])) + (is (= (partition-all 4 2 [1 2 3 4 5 6 7 8 9]) + [[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]])) + (is (= [true true] (take-while true? [true true 2 3 4]))) + (is (= [[true true] [false false false] [true true]] + (partition-by true? [true true false false false true true]))) + (is (= [0 2 4 6 8 10] (take-nth 2 [0 1 2 3 4 5 6 7 8 9 10]))) + (let [sf (some-fn number? keyword? symbol?)] + (testing "Testing some-fn" + (is (sf :foo 1)) + (is (sf :foo)) + (is (sf 'bar 1)) + (is (not (sf [] ()))))) + (let [ep (every-pred number? zero?)] + (testing "Testing every-pred" + (is (ep 0 0 0)) + (is (not (ep 1 2 3 0))))) + (is ((complement number?) :foo)) + (is (= [1 [2 3] [1 2 3]] ((juxt first rest seq) [1 2 3]))) + (is (= 5 (max 1 2 3 4 5))) + (is (= 5 (max 5 4 3 2 1))) + (is (= 5.5 (max 1 2 3 4 5 5.5))) + (is (= 1 (min 5 4 3 2 1))) + (is (= 1 (min 1 2 3 4 5))) + (is (= 0.5 (min 5 4 3 0.5 2 1))) + (let [x (array 1 2 3)] + (testing "Testing setting property on JS array" + (set! (.-foo x) :hello) + (is (= (.-foo x) :hello)))) + ;; last + (is (= nil (last nil))) + (is (= 3 (last [1 2 3]))) + ;; dotimes + (let [s (atom [])] + (dotimes [n 5] + (swap! s conj n)) + (is (= [0 1 2 3 4] @s))) + ;; doseq + (let [v [1 2 3 4 5] + s (atom ())] + (doseq [n v] (swap! s conj n)) + (is (= @s (reverse v)))) + ;; memoize + (let [f (memoize (fn [] (rand)))] + (f) + (is (= (f) (f)))) + ;; range + (is (= (range 10) (list 0 1 2 3 4 5 6 7 8 9))) + (is (= (range 10 20) (list 10 11 12 13 14 15 16 17 18 19))) + (is (= (range 10 20 2) (list 10 12 14 16 18))) + (is (= (take 20 (range)) (list 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19))) + ;; group-by + (let [d (group-by second {:a 1 :b 2 :c 1 :d 4 :e 1 :f 2})] + (testing "group-by" + (is (= 3 (count (get d 1)))) + (is (= 2 (count (get d 2)))) + (is (= 1 (count (get d 4)))))) + (is (= {1 2 3 4 5 6} (merge {1 2} {3 4} {5 6}))) + (is (= {1 2 3 4} (merge {1 2} {3 4} nil))) + ;; frequencies + (is (= {:a 3 :b 2} (frequencies [:a :b :a :b :a]))) + ;; reductions + (is (= [1 3 6 10 15] (reductions + [1 2 3 4 5]))) + ;; keep + (is (= [1 3 5 7 9] (keep #(if (odd? %) %) [1 2 3 4 5 6 7 8 9 10]))) + (is (= [2 4 6 8 10] (keep #(if (even? %) %) [1 2 3 4 5 6 7 8 9 10]))) + ;; keep-indexed + (is (= [1 3 5 7 9] (keep-indexed #(if (odd? %1) %2) [0 1 2 3 4 5 6 7 8 9 10]))) + (is (= [2 4 5] (keep-indexed #(if (pos? %2) %1) [-9 0 29 -7 45 3 -8]))) + ;; map-indexed + (is (= [[0 :a] [1 :b] [2 :c]] (map-indexed #(vector % %2) [:a :b :c]))) + ;; merge-with + (is (= '{"Foo" ("foo" "FOO" "fOo"), "Bar" ("bar" "BAR" "BAr"), "Baz" ["baz"], "Qux" ["qux" "quux"]} + (merge-with concat + {"Foo" ["foo" "FOO"] + "Bar" ["bar" "BAR"] + "Baz" ["baz"]} + {"Foo" ["fOo"] + "Bar" ["BAr"] + "Qux" ["qux" "quux"]}))) + (is (= {:a 111, :b 102, :c 13} + (merge-with + + {:a 1 :b 2 :c 3} + {:a 10 :c 10} + {:a 100 :b 100}))) + (is (= {:a 3, :b 102, :c 13} + (apply merge-with [+ + {:a 1 :b 100} + {:a 1 :b 2 :c 3} + {:a 1 :c 10}]))) + (is (= '[a c e] (replace '[a b c d e] [0 2 4]))) + (is (= [:one :zero :two :zero] + (replace {0 :zero 1 :one 2 :two} '(1 0 2 0)))) + ;; split-at + (is (= [[1 2] [3 4 5]] (split-at 2 [1 2 3 4 5]))) + ;; split-with + (is (= [[1 2 3] [4 5]] (split-with (partial >= 3) [1 2 3 4 5]))) + ;; trampoline + (is (= 10000 (trampoline (fn f [n] (if (>= n 10000) n #(f (inc n)))) 0))) + ;; vary-meta + (is (= {:a 1} (meta (vary-meta [] assoc :a 1)))) + (is (= {:a 1 :b 2} (meta (vary-meta (with-meta [] {:b 2}) assoc :a 1)))) + ;; comparator + (is (= [1 1 2 2 3 5] (seq (.sort (to-array [2 3 1 5 2 1]) (comparator <))))) + (is (= [5 3 2 2 1 1] (seq (.sort (to-array [2 3 1 5 2 1]) (comparator >))))) + (is (= (hash 'foo) (hash (symbol "foo")))) + (is (= (hash 'foo/bar) (hash (symbol "foo" "bar")))) + (is (= (lazy-cat [1] [2] [3]) '(1 2 3))) + ;; Make sure take/drop raise an error when given nil as an argument + (is (try (do (take nil [1 2 3]) false) + (catch js/Error e true))) + (is (try (do (drop nil [1 2 3]) false) + (catch js/Error e true))) + (is (try (do (take-nth nil [1 2 3]) false) + (catch js/Error e true))))) diff --git a/src/test/cljs/cljs/test_runner.cljs b/src/test/cljs/cljs/test_runner.cljs index 7c882dcf5..d3880e20c 100644 --- a/src/test/cljs/cljs/test_runner.cljs +++ b/src/test/cljs/cljs/test_runner.cljs @@ -2,6 +2,8 @@ (:require [cljs.test :refer-macros [run-tests]] [cljs.primitives-test] [cljs.destructuring-test] + [cljs.new-new-test] + [cljs.printing-test] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -26,6 +28,8 @@ (run-tests 'cljs.primitives-test 'cljs.destructuring-test + 'cljs.new-new-test + 'cljs.printing-test 'cljs.core-test 'cljs.reader-test 'clojure.string-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 3865f7cbc..695a60d88 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -245,6 +245,8 @@ (:require [cljs.test :refer-macros [run-tests]] [cljs.primitives-test] [cljs.destructuring-test] + [cljs.new-new-test] + [cljs.printing-test] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -270,6 +272,8 @@ '(run-tests 'cljs.primitives-test 'cljs.destructuring-test + 'cljs.new-new-test + 'cljs.printing-test 'cljs.core-test 'cljs.reader-test 'clojure.string-test From 9f1c1be68a468d2bfb8001f7e123b5fcb99260e2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 16:32:10 -0400 Subject: [PATCH 2035/4033] missing printing test --- src/test/cljs/cljs/printing_test.cljs | 129 ++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/test/cljs/cljs/printing_test.cljs diff --git a/src/test/cljs/cljs/printing_test.cljs b/src/test/cljs/cljs/printing_test.cljs new file mode 100644 index 000000000..e53629e00 --- /dev/null +++ b/src/test/cljs/cljs/printing_test.cljs @@ -0,0 +1,129 @@ +; 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.printing-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.string :as s] + [clojure.set :as set])) + +(deftest test-print-knobs + (testing "Testing printing knobs" + (is (= (binding [*print-length* 0] (str [1 2 3 4 5 6 7 8 9 0])) + "[...]")) + (is (= (binding [*print-length* 1] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 ...]")) + (is (= (binding [*print-length* 2] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 2 ...]")) + (is (= (binding [*print-length* 10] (str [1 2 3 4 5 6 7 8 9 0])) + "[1 2 3 4 5 6 7 8 9 0]")) + ;; CLJS-804 + (is (= (binding [*print-length* 10] (str {:foo "bar"})) + "{:foo \"bar\"}")) + (is (= (binding [*print-length* 0] (str {:foo "bar" :baz "woz"})) + "{...}")) + (is (#{"{:foo \"bar\", ...}" "{:baz \"woz\", ...}"} + (binding [*print-length* 1] (str {:foo "bar" :baz "woz"})))) + (is (#{"{:foo \"bar\", :baz \"woz\"}" "{:baz \"woz\", :foo \"bar\"}"} + (binding [*print-length* 10] (str {:foo "bar" :baz "woz"}))))) + ) + +(deftest test-print-with-opts + (testing "Testing printing with opts - :more-marker" + ; CLJS-1016 + (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "" :print-length 0}) + "[]")) + (is (= (pr-str-with-opts [[1 2 3]] {:more-marker "\u2026" :print-length 1}) + "[1 \u2026]")) + (is (#{"#{1 2 \u2026}" "#{1 3 \u2026}" + "#{2 1 \u2026}" "#{2 3 \u2026}" + "#{3 1 \u2026}" "#{3 2 \u2026}"} + (pr-str-with-opts [#{1 2 3}] {:more-marker "\u2026" :print-length 2}))) + (is (= (pr-str-with-opts ['(1 2 3)] {:more-marker "\u2026" :print-length 2}) + "(1 2 \u2026)")) + (is (#{"{:1 1, :2 2, \u2026}" "{:1 1, :3 3, \u2026}" + "{:2 2, :1 1, \u2026}" "{:2 2, :3 3, \u2026}" + "{:3 3, :1 1, \u2026}" "{:3 3, :2 2, \u2026}"} + (pr-str-with-opts [{:1 1 :2 2 :3 3}] {:more-marker "\u2026" :print-length 2})))) + + (testing "Testing printing with opts - :alt-impl" + ; CLJS-1010 + (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts] ((:fallback-impl opts) obj writer opts))}) + "[1 2 3]")) + (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts] (-write writer (str "<" obj ">")))}) + "<[1 2 3]>")) + (is (= (pr-str-with-opts [[:start 1 2 [:middle] 3 4 :end] :standalone] {:alt-impl (fn [obj writer opts] + (if (keyword? obj) + (-write writer (str "|" (name obj) "|")) + ((:fallback-impl opts) obj writer opts)))}) + "[|start| 1 2 [|middle|] 3 4 |end|] |standalone|")) + (is (= (pr-str-with-opts [[1 2 3]] {:alt-impl (fn [obj writer opts])}) + ""))) + ) + +(defrecord PrintMe [a b]) + +(deftest test-printing + (testing "Testing pr-str" + (is (= (pr-str) "")) + (is (= (pr-str 1) "1")) + (is (= (pr-str -1) "-1")) + (is (= (pr-str -1.5) "-1.5")) + (is (= (pr-str [3 4]) "[3 4]")) + (is (= (pr-str "foo") "\"foo\"")) + (is (= (pr-str :hello) ":hello")) + (is (= (pr-str 'goodbye) "goodbye")) + ;;(is (= (pr-str #{1 2 3}) "#{1 2 3}")) + (is (= (pr-str '(7 8 9)) "(7 8 9)")) + (is (= (pr-str '(deref foo)) "(deref foo)")) + (is (= (pr-str '(quote bar)) "(quote bar)")) + (is (= (pr-str 'foo/bar) "foo/bar")) + (is (= (pr-str \a) "\"a\"")) + (is (= (pr-str :foo/bar) ":foo/bar")) + (is (= (pr-str nil) "nil")) + (is (= (pr-str true) "true")) + (is (= (pr-str false) "false")) + (is (= (pr-str "string") "\"string\"")) + (is (= (pr-str ["üñîçó∂£" :ทดสอบ/你好 'こんにちは]) "[\"üñîçó∂£\" :ทดสอบ/你好 こんにちは]")) + (is (= (pr-str "escape chars \t \r \n \\ \" \b \f") "\"escape chars \\t \\r \\n \\\\ \\\" \\b \\f\"")) + (is (= (pr-str (PrintMe. 1 2)) "#cljs.printing-test.PrintMe{:a 1, :b 2}")) + (is (= (pr-str (js/Date. "2010-11-12T13:14:15.666-05:00")) + "#inst \"2010-11-12T18:14:15.666-00:00\"")) + (doseq [month (range 1 13) + day (range 1 29) + hour (range 1 23)] + (let [pad (fn [n] + (if (< n 10) + (str "0" n) + n)) + inst (str "2010-" (pad month) "-" (pad day) "T" (pad hour) ":14:15.666-00:00")] + (is (= (pr-str (js/Date. inst)) (str "#inst \"" inst "\""))))) + (let [uuid-str "550e8400-e29b-41d4-a716-446655440000" + uuid (cljs.core/uuid uuid-str)] + (is (= (pr-str uuid) (str "#uuid \"" uuid-str "\"")))) + ;; pr-str PersistentQueueSeq - CLJS-800 + (is (= (pr-str (rest (conj cljs.core.PersistentQueue.EMPTY 1 2 3))) "(2 3)")) + (is (= "\"asdf\" \"asdf\"" (pr-str "asdf" "asdf"))) + ;; Different hash map order on self-host + (is (#{"[1 true {:a 2, :b #\"x\\\"y\"} #js [3 4]]" + "[1 true {:b #\"x\\\"y\", :a 2} #js [3 4]]"} + (pr-str [1 true {:a 2 :b #"x\"y"} (array 3 4)])))) + (testing "Testing print-str" + (is (= (print-str "asdf") "asdf"))) + (testing "Testing println-str" + (is (= (println-str "asdf") "asdf\n"))) + (testing "Testing prn-str" + (is (= (prn-str) "\n")) + (is (= (prn-str "asdf") "\"asdf\"\n")) + ;; Different hash map order on self-host + (is (#{"[1 true {:a 2, :b 42} #js [3 4]]\n" + "[1 true {:b 42, :a 2} #js [3 4]]\n"} + (prn-str [1 true {:a 2 :b 42} (array 3 4)])))) + (testing "Testing with-out-str" + (is (= "12" (with-out-str (print 1) (print 2)))) + (is (= "12" (with-out-str (*print-fn* 1) (*print-fn* 2)))))) From 71c93368aedd49ed3ab5156c78a379e14e275ce8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Sep 2016 16:59:53 -0400 Subject: [PATCH 2036/4033] more moving things around, test nses for hashing, seqs & collections --- src/test/cljs/cljs/collections_test.cljs | 613 ++++++++++++++ src/test/cljs/cljs/core_test.cljs | 912 --------------------- src/test/cljs/cljs/destructuring_test.cljs | 33 +- src/test/cljs/cljs/hashing_test.cljs | 90 ++ src/test/cljs/cljs/new_new_test.cljs | 5 + src/test/cljs/cljs/primitives_test.cljs | 23 + src/test/cljs/cljs/seqs_test.cljs | 190 +++++ src/test/cljs/cljs/test_runner.cljs | 6 + src/test/self/self_parity/test.cljs | 6 + 9 files changed, 965 insertions(+), 913 deletions(-) create mode 100644 src/test/cljs/cljs/collections_test.cljs create mode 100644 src/test/cljs/cljs/hashing_test.cljs create mode 100644 src/test/cljs/cljs/seqs_test.cljs diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs new file mode 100644 index 000000000..372412685 --- /dev/null +++ b/src/test/cljs/cljs/collections_test.cljs @@ -0,0 +1,613 @@ +; 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.collections-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.string :as s] + [clojure.set :as set])) + +(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)))) + (testing "Testing assoc dissoc" + (is (= {1 2 3 4} (assoc {} 1 2 3 4))) + (is (= {1 2} (assoc {} 1 2))) + (is (= [42 2] (assoc [1 2] 0 42))) + (is (= {} (dissoc {1 2 3 4} 1 3))) + (is (= {1 2} (dissoc {1 2 3 4} 3))) + (is (nil? (dissoc nil :foo)))) + (testing "Testing find" + (is (= (find {} :a) nil)) + (is (= (find {:a 1} :a) [:a 1])) + (is (= (find {:a 1} :b) nil)) + (is (= (find {:a 1 :b 2} :a) [:a 1])) + (is (= (find {:a 1 :b 2} :b) [:b 2])) + (is (= (find {:a 1 :b 2} :c) nil)) + (is (= (find {} nil) nil)) + (is (= (find {:a 1} nil) nil)) + (is (= (find {:a 1 :b 2} nil) nil)) + (is (= (find [1 2 3] 0) [0 1]))) + ) + +(deftest test-vectors + (testing "Testing vectors" + (is (= :a (nth [:a :b :c :d] 0))) + (is (= :a (nth [:a :b :c :d] 0.1))) + (let [pv (vec (range 97))] + (testing "basic ops" + (is (= (nth pv 96) 96)) + (is (= (nth pv 97 nil) nil)) + (is (= (pv 96) 96)) + (is (nil? (rseq []))) + (is (= (reverse pv) (rseq pv))))) + (let [pv (vec (range 33))] + (testing "pop" + (is (= pv (-> pv pop pop (conj 31) (conj 32)))))) + (let [stack1 (pop (vec (range 97))) + stack2 (pop stack1)] + (testing "stack operations" + (is (= 95 (peek stack1))) + (is (= 94 (peek stack2))))) + (let [v1 (vec (range 10)) + v2 (vec (range 5)) + s (subvec v1 2 8)] + (testing "subvec" + (is (= s (-> v1 (subvec 2) (subvec 0 6)) (->> v1 (drop 2) (take 6)))) + (is (= 6 (count s))) + (is (= [2 3 4 5 6] (pop s))) + (is (= 7 (peek s))) + (is (= [2 3 4 5 6 7 1] + (assoc s 6 1) + (conj s 1))) + (is (= 27 (reduce + s))) + (is (= s (vec s))) ; pour into plain vector + ;; go outside ranges + (is (= :fail (try (subvec v2 0 6) (catch js/Error e :fail)))) + (is (= :fail (try (subvec v2 6 10) (catch js/Error e :fail)))) + (is (= :fail (try (subvec v2 6 10) (catch js/Error e :fail)))) + (is (= :fail (try (subvec v2 3 6) (catch js/Error e :fail)))) + ;; no layered subvecs + (is (identical? v1 (.-v (subvec s 1 4)))) + (let [m {:x 1}] + (is (= m (meta (with-meta s m))))) + ;; CLJS-997 + (is (= (reduce-kv + 123 (vec (range 10 50))) + (reduce-kv + 123 (subvec (vec (range 100)) 10 50))))) + ;; CLJS-513 + (let [sentinel (js-obj) + s (subvec [0 1 2 3] 1 2)] + (testing "bounds checking" + (is (identical? sentinel (try (s -1) (catch js/Error _ sentinel)))) + (is (identical? sentinel (try (s 1) (catch js/Error _ sentinel)))))) + ;; CLJS-765 + (let [sv1 (subvec [0 1 2 3] 1 2) + sv2 (subvec [0 1 2 3] 1 1)] + (testing "rseq equality" + (is (= (rseq sv1) '(1))) + (is (nil? (rseq sv2)))))) + )) + +(deftest test-sets + (testing "Testing persistent sets" + (is (set [])) + (is (= #{} (set []))) + (is (= #{} (hash-set))) + (is (identical? cljs.core.PersistentHashSet (type (hash-set)))) + + (is (= #{"foo"} (set ["foo"]))) + (is (= #{"foo"} (hash-set "foo"))) + (is (= #{1 2 3} #{1 3 2})) + (is (= #{#{1 2 3} [4 5 6] {7 8} 9 10} + #{10 9 [4 5 6] {7 8} #{1 2 3}})) + (is (not (= #{nil [] {} 0 #{}} #{}))) + (is (= (count #{nil [] {} 0 #{}}) 5)) + (is (= (conj #{1} 1) #{1})) + (is (= (conj #{1} 2) #{2 1})) + (is (= #{} (-empty #{1 2 3 4}))) + (is (= (reduce + #{1 2 3 4 5}) 15)) + (is (= 4 (get #{1 2 3 4} 4))) + (is (contains? #{1 2 3 4} 4)) + (is (contains? #{[] nil 0 {} #{}} {})) + (is (contains? #{[1 2 3]} [1 2 3])) + (is (not (contains? (-disjoin #{1 2 3} 3) 3))) + (is (= #{1 2 3} (disj #{1 2 3}))) + (is (= #{1 2} (disj #{1 2 3} 3))) + (is (= #{1} (disj #{1 2 3} 2 3))) + (is (nil? (disj nil :foo))))) + +(deftest test-range + (testing "Testing Range" + ;; Range + (is (= (range 0 10 3) (list 0 3 6 9))) + (is (= (count (range 0 10 3)) 4)) + (is (= (range 0 -10 -3) (list 0 -3 -6 -9))) + (is (= (count (range 0 -10 -3)) 4)) + (is (= (range -10 10 3) (list -10 -7 -4 -1 2 5 8))) + (is (= (count (range -10 10 3)) 7)) + (is (= (range 0 1 1) (list 0))) + (is (= (range 0 -3 -1) (list 0 -1 -2))) + (is (= (range 3 0 -1) (list 3 2 1))) + (is (= (range 0 10 -1) (list))) + (is (= (take 3 (range 0 1 0)) (list 0 0 0))) + (is (= (range 10 0 1) (list))) + (is (= (range 0 0 0) (list))) + (is (= (count (range 0 10 -1)) 0)) + (is (= (count (take 3 (range 0 2 0))) 3)) + (is (= (count (range 10 0 1)) 0)) + (is (= (count (range 0 0 0)) 0)) + (is (= (take 3 (range 1 0 0)) (list 1 1 1))) + (is (= (take 3 (range 3 1 0)) (list 3 3 3))) + )) + +(deftest test-rseq + (testing "Testing RSeq" + (is (= '(3 2 1) (reverse (seq (array 1 2 3))))) + (is (= '(3 2 1) (reverse [1 2 3]))) + (is (= '(4 3 2 1) (cons 4 (reverse [1 2 3])))) + (is (= 6 (reduce + (reverse [1 2 3])))) + (is (= '(4 3 2) (map inc (reverse [1 2 3])))) + (is (= '(4 2) (filter even? (reverse [1 2 3 4])))) + )) + +(deftest test-sorted-map + (testing "Testing sorted maps" + ;; PersistentTreeMap + (let [m1 (sorted-map) + c2 (comp - compare) + m2 (sorted-map-by c2)] + (testing "basic ops 1" + (is (identical? cljs.core.PersistentTreeMap (type m1))) + (is (identical? cljs.core.PersistentTreeMap (type m2))) + (is (identical? compare (.-comp m1))) + (is (zero? (count m1))) + (is (zero? (count m2))) + (is (nil? (rseq m1))) + (let [m1 (assoc m1 :foo 1 :bar 2 :quux 3) + m2 (assoc m2 :foo 1 :bar 2 :quux 3)] + (testing "basic ops 2" + (is (= (count m1) 3)) + (is (= (count m2) 3)) + (is (= (seq m1) (list [:bar 2] [:foo 1] [:quux 3]))) + (is (= (seq m2) (list [:quux 3] [:foo 1] [:bar 2]))) + (is (= (seq m1) (rseq m2))) + (is (= (seq m2) (rseq m1))) + (is (= (conj m1 [:wibble 4]) {:foo 1 :bar 2 :quux 3 :wibble 4})) + (is (= (count (conj m1 [:wibble 4])) 4)) + (is (= (conj m2 [:wibble 4]) {:foo 1 :bar 2 :quux 3 :wibble 4})) + (is (= (count (conj m2 [:wibble 4])) 4)) + (is (= (map key (assoc m1 nil 4)) (list nil :bar :foo :quux))) + (is (= (map key (assoc m2 nil 4)) (list :quux :foo :bar nil))))))) + (let [m (->> [[0 10] [20 30] [10 20] [50 60] [30 40] [40 50]] + (mapcat (partial apply range)) + (mapcat #(list % %)) + (apply sorted-map)) + s1 (map #(vector % %) (range 60)) + s2 (map #(vector % %) (range 59 -1 -1))] + (testing "edge cases 1" + (is (= (count m) 60)) + (is (= (seq m) s1)) + (is (= (rseq m) s2)))) + (let [m (sorted-map :foo 1 :bar 2 :quux 3)] + (testing "edge cases 2" + (is (= (dissoc m :foo) (hash-map :bar 2 :quux 3))) + (is (= (count (dissoc m :foo)) 2)) + (is (= (hash m) (hash (hash-map :foo 1 :bar 2 :quux 3)))) + (is (= (subseq m < :foo) (list [:bar 2]))) + (is (= (subseq m <= :foo) (list [:bar 2] [:foo 1]))) + (is (= (subseq m > :foo) (list [:quux 3]))) + (is (= (subseq m >= :foo) (list [:foo 1] [:quux 3]))) + (is (= (map #(reduce (fn [_ x] x) %) m) (list 2 1 3))) + (is (= (map #(reduce (fn [x _] x) 7 %) m) (list 7 7 7))))) + )) + +(deftest test-sorted-sets + (let [s1 (sorted-set) + c2 (comp - compare) + s2 (sorted-set-by c2) + c3 #(compare (quot %1 2) (quot %2 2)) + s3 (sorted-set-by c3) + s4 (sorted-set-by <)] + (testing "Testing sorted set" + (is (identical? cljs.core.PersistentTreeSet (type s1))) + (is (identical? cljs.core.PersistentTreeSet (type s2))) + (is (identical? compare (-comparator s1))) + (is (zero? (count s1))) + (is (zero? (count s2))) + (is (nil? (rseq s1))) + (let [s1 (conj s1 1 2 3) + s2 (conj s2 1 2 3) + s3 (conj s3 1 2 3 7 8 9) + s4 (conj s4 1 2 3)] + (testing "basic ops" + (is (= (hash s1) (hash s2))) + (is (= (hash s1) (hash #{1 2 3}))) + (is (= (seq s1) (list 1 2 3))) + (is (= (rseq s1) (list 3 2 1))) + (is (= (seq s2) (list 3 2 1))) + (is (= (rseq s2) (list 1 2 3))) + (is (= (count s1) 3)) + (is (= (count s2) 3)) + (is (= (count s3) 4)) + (is (= (get s3 0) 1)) + (is (= (subseq s3 > 5) (list 7 8))) + (is (= (subseq s3 > 6) (list 8))) + (is (= (subseq s3 >= 6) (list 7 8))) + (is (= (subseq s3 >= 12) nil)) + (is (= (subseq s3 < 0) (list))) + (is (= (subseq s3 < 5) (list 1 2))) + (is (= (subseq s3 < 6) (list 1 2))) + (is (= (subseq s3 <= 6) (list 1 2 7))) + (is (= (subseq s3 >= 2 <= 6) (list 2 7))) + (is (= (subseq s4 >= 2 < 3) (list 2))) + (let [s1 (disj s1 2) + s2 (disj s2 2)] + (testing "edge cases" + (is (= (seq s1) (list 1 3))) + (is (= (rseq s1) (list 3 1))) + (is (= (seq s2) (list 3 1))) + (is (= (rseq s2) (list 1 3))) + (is (= (count s1) 2)) + (is (= (count s2) 2))))))))) + +(deftest test-lazy-seq-realized? + (testing "Testing LazySeq IPending" + (let [xs (lazy-seq + (cons 1 + (lazy-seq + (cons 2 + (lazy-seq (cons 3 nil))))))] + (is (not (realized? xs))) + (is (not (realized? (rest xs)))) + (is (realized? xs)) + (is (not (realized? (nthrest xs 2)))) + (is (realized? (rest xs)))))) + +(deftest test-784 + (testing "Testing CLJS-784, conj on maps" + (doseq [m [(array-map) (hash-map) (sorted-map)]] + (is (= :ok + (try + (conj m "foo") + (catch js/Error _ + :ok)))) + (is (= {:foo 1} (conj m [:foo 1]))) + (is (= {:foo 1} (conj m {:foo 1}))) + (is (= {:foo 1} (conj m (list [:foo 1]))))) + (doseq [mt [array-map hash-map sorted-map]] + (is (= {:foo 1 :bar 2 :baz 3} + (conj (mt :foo 1) + ((fn make-seq [from-seq] + ;; this tests specifically for user defined seq's, that implement the bare minimum, i.e. no INext + (when (seq from-seq) + (reify + ISeqable + (-seq [this] this) + ISeq + (-first [this] (first from-seq)) + (-rest [this] (make-seq (rest from-seq)))))) + [[:bar 2] [:baz 3]])))))) + ) + +(deftest test-849 + (let [xs [44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24]] + (testing "Testing CLJS-849, transient contains?" + (is (loop [m (transient (zipmap xs (repeat 1))) + xs xs] + (if-let [x (first xs)] + (if (contains? m x) + (recur (dissoc! m x) (next xs)) + false) + true)))))) + +(deftest test-large-array-map + (let [m (array-map 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15)] + (testing "Testing large array maps" + (is (instance? cljs.core/PersistentArrayMap m)) + (is (= (seq m) [[0 0] [1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9] [10 10] [11 11] [12 12] [13 13] [14 14] [15 15]]))))) + +(def test-map + {:a 1 + :b 2 + #inst "2013-12-19T05:00:00.000-00:00" 3 + :d 4 + :e 5 + #inst "2013-12-06T05:00:00.000-00:00" 6 + :g 7 + :h 8 + :i 9 + :j 10}) + +(deftest test-716 + (testing "Testing CLJS-716, date as keys in maps" + (is (= (test-map #inst "2013-12-19T05:00:00.000-00:00") 3)) + (is (= (test-map #inst "2013-12-06T05:00:00.000-00:00") 6)))) + +(deftest test-transient-edge-case-1 + (let [v1 (vec (range 15 48)) + v2 (vec (range 40 57)) + v1 (persistent! (assoc! (conj! (pop! (transient v1)) :foo) 0 :quux)) + v2 (persistent! (assoc! (conj! (transient v2) :bar) 0 :quux)) + v (into v1 v2)] + (is (= v (vec (concat [:quux] (range 16 47) [:foo] + [:quux] (range 41 57) [:bar])))))) + +(deftest test-transient-edge-case-2 + (is (loop [v (transient []) + xs (range 100)] + (if-let [x (first xs)] + (recur + (condp #(%1 (mod %2 3)) x + #{0 2} (conj! v x) + #{1} (assoc! v (count v) x)) + (next xs)) + (= (vec (range 100)) (persistent! v)))))) + +(deftest test-phm + ;; PersistentHashMap & TransientHashMap + (loop [m1 cljs.core.PersistentHashMap.EMPTY + m2 (transient cljs.core.PersistentHashMap.EMPTY) + i 0] + (if (< i 100) + (recur (assoc m1 i i) (assoc! m2 i i) (inc i)) + (let [m2 (persistent! m2)] + (is (= (count m1) 100)) + (is (= (count m2) 100)) + (is (= m1 m2)) + (loop [i 0] + (if (< i 100) + (do (is (= (m1 i) i)) + (is (= (m2 i) i)) + (is (= (get m1 i) i)) + (is (= (get m2 i) i)) + (is (contains? m1 i)) + (is (contains? m2 i)) + (recur (inc i))))) + (is (= (map vector (range 100) (range 100)) (sort-by first (seq m1)))) + (is (= (map vector (range 100) (range 100)) (sort-by first (seq m2)))) + (is (not (contains? (dissoc m1 3) 3)))))) + (let [tm (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY)) + (dissoc 3 5 7) + transient)] + (doseq [k [0 1 2 4 6 8 9]] + (is (= k (get tm k)))) + (let [m (persistent! tm)] + (is (= 2 (try (dissoc! tm 1) 1 (catch js/Error e 2)))) + (is (= 2 (try (assoc! tm 10 10) 1 (catch js/Error e 2)))) + (is (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) + (is (= 2 (try (count tm) 1 (catch js/Error e 2)))) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY)) + (dissoc 3 5 7))] + (testing "Testing PHM dissoc" + (is (= (count m) 7)) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY)) + (conj [:foo 1]))] + (testing "Testing PHM conj" + (is (= (count m) 11)) + (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1})))) + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY) + transient) + (conj! [:foo 1]) + persistent!)] + (testing "Testing PHM conj!" + (is (= (count m) 11)) + (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1})))) + (let [tm (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentHashMap.EMPTY) + transient)] + (testing "Testing transient PHM" + (is (loop [tm tm ks [3 5 7]] + (if-let [k (first ks)] + (recur (dissoc! tm k) (next ks)) + (let [m (persistent! tm)] + (and (= (count m) 7) + (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))))) + ) + +(deftype FixedHash [h v] + IHash + (-hash [this] h) + IEquiv + (-equiv [this other] + (and (instance? FixedHash other) (= v (.-v other))))) + +(def fixed-hash-foo (FixedHash. 0 :foo)) +(def fixed-hash-bar (FixedHash. 0 :bar)) + +(deftest test-phm-fixed-hash + (let [m (assoc cljs.core.PersistentHashMap.EMPTY + fixed-hash-foo 1 + fixed-hash-bar 2)] + (is (= (get m fixed-hash-foo) 1)) + (is (= (get m fixed-hash-bar) 2)) + (is (= (count m) 2)) + (let [m (dissoc m fixed-hash-foo)] + (is (= (get m fixed-hash-bar) 2)) + (is (not (contains? m fixed-hash-foo))) + (is (= (count m) 1)))) + + (let [m (into cljs.core.PersistentHashMap.EMPTY ; make sure we're testing + (zipmap (range 100) (range 100))) ; the correct map type + m (assoc m fixed-hash-foo 1 fixed-hash-bar 2)] + (is (= (count m) 102)) + (is (= (get m fixed-hash-foo) 1)) + (is (= (get m fixed-hash-bar) 2)) + (let [m (dissoc m 3 5 7 fixed-hash-foo)] + (is (= (get m fixed-hash-bar) 2)) + (is (not (contains? m fixed-hash-foo))) + (is (= (count m) 98)))) + + (let [m (into cljs.core.PersistentHashMap.EMPTY ; make sure we're testing + (zipmap (range 100) (range 100))) ; the correct map type + m (transient m) + m (assoc! m fixed-hash-foo 1) + m (assoc! m fixed-hash-bar 2) + m (persistent! m)] + (is (= (count m) 102)) + (is (= (get m fixed-hash-foo) 1)) + (is (= (get m fixed-hash-bar) 2)) + (let [m (dissoc m 3 5 7 fixed-hash-foo)] + (is (= (get m fixed-hash-bar) 2)) + (is (not (contains? m fixed-hash-foo))) + (is (= (count m) 98))))) + +(def array-map-conversion-threshold + cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD) + +(deftest test-pam + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentArrayMap.EMPTY)) + (dissoc 3 5 7))] + (is (= (count m) 7)) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9}))) + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentArrayMap.EMPTY)) + (conj [:foo 1]))] + (is (= (count m) 11)) + (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) + (let [m (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentArrayMap.EMPTY) + transient) + (conj! [:foo 1]) + persistent!)] + (is (= (count m) 11)) + (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) + (let [tm (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentArrayMap.EMPTY) + transient)] + (loop [tm tm ks [3 5 7]] + (if-let [k (first ks)] + (recur (dissoc! tm k) (next ks)) + (let [m (persistent! tm)] + (is (= (count m) 7)) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))) + (let [tm (-> (->> (interleave (range 10) (range 10)) + (apply assoc cljs.core.PersistentArrayMap.EMPTY)) + (dissoc 3 5 7) + transient)] + (doseq [k [0 1 2 4 6 8 9]] + (is (= k (get tm k)))) + (let [m (persistent! tm)] + (is (= 2 (try (dissoc! tm 1) 1 (catch js/Error e 2)))) + (is (= 2 (try (assoc! tm 10 10) 1 (catch js/Error e 2)))) + (is (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) + (is (= 2 (try (count tm) 1 (catch js/Error e 2)))) + (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) + (let [m (apply assoc cljs.core.PersistentArrayMap.EMPTY + (interleave (range (* 2 array-map-conversion-threshold)) + (range (* 2 array-map-conversion-threshold))))] + (is (= (count m) (* 2 array-map-conversion-threshold))) + (is (= (m array-map-conversion-threshold) array-map-conversion-threshold)) + (is (= m (into cljs.core.PersistentHashMap.EMPTY + (map #(vector % %) + (range (* 2 array-map-conversion-threshold))))))) + ) + +(deftest test-literal-maps + (loop [m1 {} m2 {} i 0] + (if (< i 100) + (recur (assoc m1 i i) (assoc m2 (str "foo" i) i) (inc i)) + (do (is (= m1 (into cljs.core.PersistentHashMap.EMPTY + (map vector (range 100) (range 100))))) + (is (= m2 (into cljs.core.PersistentHashMap.EMPTY + (map vector + (map (partial str "foo") (range 100)) + (range 100))))) + (is (= (count m1) 100)) + (is (= (count m2) 100))))) + ) + +(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))) + (let [n (inc (+ cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD 2)) + expected (repeat n {:foo :bar})] + (is (= result expected)))))) + +(deftest test-transient-hash-set + ;; TransientHashSet + (loop [s (transient #{}) + i 0] + (if (< i 100) + (recur (conj! s i) (inc i)) + (loop [s s i 0] + (if (< i 100) + (if (zero? (mod i 3)) + (recur (disj! s i) (inc i)) + (recur s (inc i))) + (let [s (persistent! s)] + (is (= s (loop [s #{} xs (remove #(zero? (mod % 3)) (range 100))] + (if-let [x (first xs)] + (recur (conj s x) (next xs)) + s)))) + (is (= s (set (remove #(zero? (mod % 3)) (range 100)))))))))) + ) + +(deftest test-cljs-1189 + (testing "array-map should always return array maps" + (let [am (apply array-map (range 100))] + (is (== (count am) 50)) + (is (instance? PersistentArrayMap am))))) + +(deftest test-cljs-1199 + (testing "array-map should skip dropped elements of IndexedSeq" + (is (= {:a 1} (apply array-map (drop 1 [0 :a 1])))))) + +(deftest test-cljs-1212 + (is (= (set {:a 0 :b 0 :c 0 :d 0 :e 0 :f 0 :g 0 :h 0 :i 0}) + #{[:a 0] [:b 0] [:c 0] [:d 0] [:e 0] [:f 0] [:g 0] [:h 0] [:i 0]}))) + +(deftest test-count-hash-set + (is + (== (count (hash-set [1 4] [2 4] [3 4] [0 3] [1 3] [2 3] [3 3] + [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] + [2 1] [3 1] [1 0] [2 0] [3 0])) + (count (list [1 4] [2 4] [3 4] [0 3] [1 3] [2 3] [3 3] + [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] + [2 1] [3 1] [1 0] [2 0] [3 0]))))) + +(deftest test-734 + (testing "Testing CLJS-734, transient operations" + (is (= (-> (transient []) (conj! 1 2) persistent!) [1 2])) + (is (= (-> (transient #{1 2 3}) (disj! 1 2) persistent!) #{3})) + (is (= (-> (transient {}) (assoc! :a 1 :b 2) persistent!) {:a 1 :b 2})) + (is (= (-> (transient {:a 1 :b 2 :c 3}) (dissoc! :a :b) persistent!) {:c 3})))) + +(deftest test-vec + (let [v (vec #js [1 2 3 4])] + (is (= (count v) 4)) + (is (= (first v) 1)) + (is (= (last v) 4)) + (is (= v [1 2 3 4])))) + +(deftest test-phm-from-array + (let [m (.fromArray PersistentHashMap #js [1 2 3 4] true)] + (is (= (count m) 2)) + (is (contains? m 1)) + (is (contains? m 3)) + (is (= (get m 1) 2)) + (is (= (get m 3) 4)) + (is (= m {1 2 3 4})))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index bbcc8460b..b8d4b3bcd 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -12,115 +12,11 @@ [clojure.string :as s] [clojure.set :as set])) -(deftest test-hash-null - (is (zero? (hash (aget (js-obj) "foo"))))) - -(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)))) - (testing "Testing assoc dissoc" - (is (= {1 2 3 4} (assoc {} 1 2 3 4))) - (is (= {1 2} (assoc {} 1 2))) - (is (= [42 2] (assoc [1 2] 0 42))) - (is (= {} (dissoc {1 2 3 4} 1 3))) - (is (= {1 2} (dissoc {1 2 3 4} 3))) - (is (nil? (dissoc nil :foo)))) - (testing "Testing find" - (is (= (find {} :a) nil)) - (is (= (find {:a 1} :a) [:a 1])) - (is (= (find {:a 1} :b) nil)) - (is (= (find {:a 1 :b 2} :a) [:a 1])) - (is (= (find {:a 1 :b 2} :b) [:b 2])) - (is (= (find {:a 1 :b 2} :c) nil)) - (is (= (find {} nil) nil)) - (is (= (find {:a 1} nil) nil)) - (is (= (find {:a 1 :b 2} nil) nil)) - (is (= (find [1 2 3] 0) [0 1]))) -) - (deftest test-metadata (testing "Testing metadata" (is (= {"x" "y"} (meta ^{"x" "y"} []))) )) -(deftest test-vectors - (testing "Testing vectors" - (is (= :a (nth [:a :b :c :d] 0))) - (is (= :a (nth [:a :b :c :d] 0.1))) - (let [pv (vec (range 97))] - (testing "basic ops" - (is (= (nth pv 96) 96)) - (is (= (nth pv 97 nil) nil)) - (is (= (pv 96) 96)) - (is (nil? (rseq []))) - (is (= (reverse pv) (rseq pv))))) - (let [pv (vec (range 33))] - (testing "pop" - (is (= pv (-> pv pop pop (conj 31) (conj 32)))))) - (let [stack1 (pop (vec (range 97))) - stack2 (pop stack1)] - (testing "stack operations" - (is (= 95 (peek stack1))) - (is (= 94 (peek stack2))))) - (let [v1 (vec (range 10)) - v2 (vec (range 5)) - s (subvec v1 2 8)] - (testing "subvec" - (is (= s (-> v1 (subvec 2) (subvec 0 6)) (->> v1 (drop 2) (take 6)))) - (is (= 6 (count s))) - (is (= [2 3 4 5 6] (pop s))) - (is (= 7 (peek s))) - (is (= [2 3 4 5 6 7 1] - (assoc s 6 1) - (conj s 1))) - (is (= 27 (reduce + s))) - (is (= s (vec s))) ; pour into plain vector - ;; go outside ranges - (is (= :fail (try (subvec v2 0 6) (catch js/Error e :fail)))) - (is (= :fail (try (subvec v2 6 10) (catch js/Error e :fail)))) - (is (= :fail (try (subvec v2 6 10) (catch js/Error e :fail)))) - (is (= :fail (try (subvec v2 3 6) (catch js/Error e :fail)))) - ;; no layered subvecs - (is (identical? v1 (.-v (subvec s 1 4)))) - (let [m {:x 1}] - (is (= m (meta (with-meta s m))))) - ;; CLJS-997 - (is (= (reduce-kv + 123 (vec (range 10 50))) - (reduce-kv + 123 (subvec (vec (range 100)) 10 50))))) - ;; CLJS-513 - (let [sentinel (js-obj) - s (subvec [0 1 2 3] 1 2)] - (testing "bounds checking" - (is (identical? sentinel (try (s -1) (catch js/Error _ sentinel)))) - (is (identical? sentinel (try (s 1) (catch js/Error _ sentinel)))))) - ;; CLJS-765 - (let [sv1 (subvec [0 1 2 3] 1 2) - sv2 (subvec [0 1 2 3] 1 1)] - (testing "rseq equality" - (is (= (rseq sv1) '(1))) - (is (nil? (rseq sv2)))))) - )) - -(deftest test-sequential-equality - (testing "Testing ISequential equality" - (is (= (list 3 2 1) [3 2 1])) - (is (= [3 2 1] (seq (array 3 2 1)))))) - -(deftest test-seq-operations - (testing "Testing basic seq operations" - (is (= () (rest nil))) - (is (= nil (seq (array)))) - (is (= nil (seq ""))) - (is (= nil (seq []))) - (is (= nil (seq {}))) - (is (= () (rest ()))) - (is (= () (rest [1]))) - (is (= () (rest (array 1)))))) - (deftest test-fn-with-metadata (let [f (fn [x] (* x 2)) m {:foo "bar"} @@ -162,94 +58,6 @@ (is (= 3 (vswap! v inc))) (is (= 3 @v))))) -(deftest test-empy-and-seq - (testing "Testing empty & seq" - (is (nil? (empty nil))) - (let [e-lazy-seq (empty (with-meta (lazy-seq (cons :a nil)) {:b :c}))] - (testing "lazy seq" - (is (seq? e-lazy-seq)) - (is (empty? e-lazy-seq)) - (is (= {:b :c} (meta e-lazy-seq))))) - (let [e-list (empty '^{:b :c} (1 2 3))] - (testing "list" - (is (seq? e-list)) - (is (empty? e-list)) - (is (= {:b :c} (meta e-list))))) - (let [e-elist (empty '^{:b :c} ())] - (testing "empty list with metadata" - (is (seq? e-elist)) - (is (empty? e-elist)) - (is (= :c (get (meta e-elist) :b))))) - (let [e-cons (empty (with-meta (cons :a nil) {:b :c}))] - (testing "cons" - (is (seq? e-cons)) - (is (empty? e-cons)) - (is (= {:b :c} (meta e-cons))))) - (let [e-vec (empty ^{:b :c} [:a :d :g])] - (testing "vector" - (is (vector? e-vec)) - (is (empty? e-vec)) - (is (= {:b :c} (meta e-vec))))) - (let [e-omap (empty ^{:b :c} {:a :d :g :h})] - (testing "map" - (is (map? e-omap)) - (is (empty? e-omap)) - (is (= {:b :c} (meta e-omap))))) - (let [e-hmap (empty ^{:b :c} {[1 2] :d :g :h})] - (testing "map with complex keys" - (is (map? e-hmap)) - (is (empty? e-hmap)) - (is (= {:b :c} (meta e-hmap))))) - (let [smap (with-meta (sorted-map-by (comp - compare) 2 :a 1 :b 5 :c) {:b :c}) - e-smap (empty smap)] - (testing "sorted-map-by" - (is (map? e-smap)) - (is (empty? e-smap)) - (is (= {:b :c} (meta e-smap))) - (is (identical? (-comparator smap) (-comparator e-smap))) - (is (= [[5 :c] [2 :a] [1 :b]] (seq (assoc e-smap 2 :a 1 :b 5 :c)))))) - (let [sset (with-meta (sorted-set-by (comp - compare) 5 1 2) {:b :c}) - e-sset (empty sset)] - (testing "sorted-set-by" - (is (set? e-sset)) - (is (empty? e-sset)) - (is (= {:b :c} (meta e-sset))) - (is (identical? (-comparator sset) (-comparator e-sset))) - (is (= [5 2 1] (seq (conj e-sset 5 1 2)))))) - (let [e-queue (empty (with-meta (.-EMPTY PersistentQueue) {:b :c}))] - (testing "queue" - (is (identical? (type e-queue) PersistentQueue)) - (is (empty? e-queue)) - (is (= {:b :c} (meta e-queue))))))) - -(deftest test-sets - (testing "Testing persistent sets" - (is (set [])) - (is (= #{} (set []))) - (is (= #{} (hash-set))) - (is (identical? cljs.core.PersistentHashSet (type (hash-set)))) - - (is (= #{"foo"} (set ["foo"]))) - (is (= #{"foo"} (hash-set "foo"))) - (is (= #{1 2 3} #{1 3 2})) - (is (= #{#{1 2 3} [4 5 6] {7 8} 9 10} - #{10 9 [4 5 6] {7 8} #{1 2 3}})) - (is (not (= #{nil [] {} 0 #{}} #{}))) - (is (= (count #{nil [] {} 0 #{}}) 5)) - (is (= (conj #{1} 1) #{1})) - (is (= (conj #{1} 2) #{2 1})) - (is (= #{} (-empty #{1 2 3 4}))) - (is (= (reduce + #{1 2 3 4 5}) 15)) - (is (= 4 (get #{1 2 3 4} 4))) - (is (contains? #{1 2 3 4} 4)) - (is (contains? #{[] nil 0 {} #{}} {})) - (is (contains? #{[1 2 3]} [1 2 3])) - (is (not (contains? (-disjoin #{1 2 3} 3) 3))) - (is (= #{1 2 3} (disj #{1 2 3}))) - (is (= #{1 2} (disj #{1 2 3} 3))) - (is (= #{1} (disj #{1 2 3} 2 3))) - (is (nil? (disj nil :foo))))) - (deftest test-contains? (testing "Testing contains?" (is (contains? {:a 1 :b 2} :a)) @@ -274,32 +82,6 @@ (is (nil? (run! identity [1]))) (is (nil? (run! reduced (range)))))) -(deftest test-distinct - (testing "Testing distinct? & distinct" - (is (distinct? 1 2 3)) - (is (not (distinct? 1 2 3 1))) - (is (= (distinct ()) ())) - (is (= (distinct '(1)) '(1))) - (is (= (distinct '(1 2 3 1 1 1)) '(1 2 3))) - (is (= (distinct [1 1 1 2]) '(1 2))) - (is (= (distinct [1 2 1 2]) '(1 2))) - (is (= (distinct "a") ["a"])) - (is (= (distinct "abcabab") ["a" "b" "c"])) - (is (= (distinct ["abc" "abc"]) ["abc"])) - (is (= (distinct [nil nil]) [nil])) - (is (= (distinct [0.0 0.0]) [0.0])) - (is (= (distinct ['sym 'sym]) '[sym])) - (is (= (distinct [:kw :kw]) [:kw])) - (is (= (distinct [42 42]) [42])) - (is (= (distinct [[] []]) [[]])) - (is (= (distinct ['(1 2) '(1 2)]) '[(1 2)])) - (is (= (distinct [() ()]) [()])) - (is (= (distinct [[1 2] [1 2]]) [[1 2]])) - (is (= (distinct [{:a 1 :b 2} {:a 1 :b 2}]) [{:a 1 :b 2}])) - (is (= (distinct [{} {}]) [{}])) - (is (= (distinct [#{1 2} #{1 2}]) [#{1 2}])) - (is (= (distinct [#{} #{}]) [#{}])))) - (deftest test-in-operations (testing "Testing update-in" (is (= {:foo {:bar {:baz 1}}} @@ -328,59 +110,6 @@ [1 :bar 1 :buzz])))) ) -(deftest test-rearrange-sequential - (testing "Test rearranging sequential collections" - (is (= [1 2 3 4 5] (sort [5 3 1 4 2]))) - (is (= [1 2 3 4 5] (sort < [5 3 1 4 2]))) - (is (= [5 4 3 2 1] (sort > [5 3 1 4 2]))) - (is (= ["a" [ 1 2] "foo"] (sort-by count ["foo" "a" [1 2]]))) - (is (= ["foo" [1 2] "a"] (sort-by count > ["foo" "a" [1 2]]))) - (let [coll [1 2 3 4 5 6 7 8 9 10] - ;; while it is technically possible for this test to fail with a false negative, - ;; it's _extraordinarily_ unlikely. - shuffles (filter #(not= coll %) (take 100 (iterate shuffle coll)))] - (is (not (empty? shuffles)))) - )) - -(deftest test-ISequential-indexOf - (testing "Testing JS .indexOf in ISequential types" - ;; PersistentVector - (is (= (.indexOf [] 2) -1)) - (is (= (.indexOf [] 2 3) -1)) - (is (= (.indexOf [1 2 3 4 5] 2) 1)) - (is (= (.indexOf [1 2 3 4 5] 6) -1)) - (is (= (.indexOf [1 2 3 4 5] -1) -1)) - (is (= (.indexOf [1 2 "x" 4 5 "a"] "a") 5)) - (is (= (.indexOf [1 2 3 4 5] 1 2) -1)) - (is (= (.indexOf [1 2 3 4 5] 2 2) -1)) - (is (= (.indexOf [1 2 3 1 5] 1 2) 3)) - (is (= (.indexOf [1 2 3 4 5] 2) 1)) - (is (= (.indexOf '(1 2 3 4 5) 2) 1)) - (is (= (.indexOf (list 1 2 3) 3) 2)) - (is (= (.indexOf (lazy-seq [1 2 3 4 5]) 3)) 2) - (is (= (.indexOf (sequence (map inc) '(0 1 2 3 4)) 5) 4)))) - -(deftest test-ISequential-lastIndexOf - (testing "Testing JS .lastIndexOf in ISequential types" - ;; PersistentVector - (is (= (.lastIndexOf [] 2) -1)) - (is (= (.lastIndexOf [] 2 3) -1)) - (is (= (.lastIndexOf [1 2 3 4 5] 2) 1)) - (is (= (.lastIndexOf [1 2 3 1 5] 1) 3)) - (is (= (.lastIndexOf [1 2 3 1 5] 1 3) 3)) - (is (= (.lastIndexOf [1 2 3 1 5] 1 2) 0)) - (is (= (.lastIndexOf [1 2 3 1] 1 0) 0)) - (is (= (.lastIndexOf [1 2 3 4 5] 3 100) 2)) - (is (= (.lastIndexOf [1 1 1 1 1] 1) 4)) - (is (= (.lastIndexOf [1 1 1 1 1] 1 6) 4)) - (is (= (.lastIndexOf [1 2 1 1 1] 2) 1)) - (is (= (.lastIndexOf [1 2 3 4 5] 3 -100) -1)) - (is (= (.lastIndexOf [1 2 3 4 5] 3 -2) 2)) - (is (= (.lastIndexOf '(1 2 1 4 5) 1) 2)) - (is (= (.lastIndexOf (list 1 2 3 1 5) 1) 3)) - (is (= (.lastIndexOf (lazy-seq [1 2 1 4 5]) 1)) 2) - (is (= (.lastIndexOf (sequence (map inc) '(0 1 0 3 4)) 1) 2)))) - (deftest test-js-clj-conversions (testing "Testing JS / CLJS data conversions" (testing "js->clj" @@ -545,140 +274,6 @@ (is (= foo2' (ffirst {foo2' 1}))) ))) -(deftest test-range - (testing "Testing Range" - ;; Range - (is (= (range 0 10 3) (list 0 3 6 9))) - (is (= (count (range 0 10 3)) 4)) - (is (= (range 0 -10 -3) (list 0 -3 -6 -9))) - (is (= (count (range 0 -10 -3)) 4)) - (is (= (range -10 10 3) (list -10 -7 -4 -1 2 5 8))) - (is (= (count (range -10 10 3)) 7)) - (is (= (range 0 1 1) (list 0))) - (is (= (range 0 -3 -1) (list 0 -1 -2))) - (is (= (range 3 0 -1) (list 3 2 1))) - (is (= (range 0 10 -1) (list))) - (is (= (take 3 (range 0 1 0)) (list 0 0 0))) - (is (= (range 10 0 1) (list))) - (is (= (range 0 0 0) (list))) - (is (= (count (range 0 10 -1)) 0)) - (is (= (count (take 3 (range 0 2 0))) 3)) - (is (= (count (range 10 0 1)) 0)) - (is (= (count (range 0 0 0)) 0)) - (is (= (take 3 (range 1 0 0)) (list 1 1 1))) - (is (= (take 3 (range 3 1 0)) (list 3 3 3))) - )) - -(deftest test-rseq - (testing "Testing RSeq" - (is (= '(3 2 1) (reverse (seq (array 1 2 3))))) - (is (= '(3 2 1) (reverse [1 2 3]))) - (is (= '(4 3 2 1) (cons 4 (reverse [1 2 3])))) - (is (= 6 (reduce + (reverse [1 2 3])))) - (is (= '(4 3 2) (map inc (reverse [1 2 3])))) - (is (= '(4 2) (filter even? (reverse [1 2 3 4])))) - )) - -(deftest test-sorted-map - (testing "Testing sorted maps" - ;; PersistentTreeMap - (let [m1 (sorted-map) - c2 (comp - compare) - m2 (sorted-map-by c2)] - (testing "basic ops 1" - (is (identical? cljs.core.PersistentTreeMap (type m1))) - (is (identical? cljs.core.PersistentTreeMap (type m2))) - (is (identical? compare (.-comp m1))) - (is (zero? (count m1))) - (is (zero? (count m2))) - (is (nil? (rseq m1))) - (let [m1 (assoc m1 :foo 1 :bar 2 :quux 3) - m2 (assoc m2 :foo 1 :bar 2 :quux 3)] - (testing "basic ops 2" - (is (= (count m1) 3)) - (is (= (count m2) 3)) - (is (= (seq m1) (list [:bar 2] [:foo 1] [:quux 3]))) - (is (= (seq m2) (list [:quux 3] [:foo 1] [:bar 2]))) - (is (= (seq m1) (rseq m2))) - (is (= (seq m2) (rseq m1))) - (is (= (conj m1 [:wibble 4]) {:foo 1 :bar 2 :quux 3 :wibble 4})) - (is (= (count (conj m1 [:wibble 4])) 4)) - (is (= (conj m2 [:wibble 4]) {:foo 1 :bar 2 :quux 3 :wibble 4})) - (is (= (count (conj m2 [:wibble 4])) 4)) - (is (= (map key (assoc m1 nil 4)) (list nil :bar :foo :quux))) - (is (= (map key (assoc m2 nil 4)) (list :quux :foo :bar nil))))))) - (let [m (->> [[0 10] [20 30] [10 20] [50 60] [30 40] [40 50]] - (mapcat (partial apply range)) - (mapcat #(list % %)) - (apply sorted-map)) - s1 (map #(vector % %) (range 60)) - s2 (map #(vector % %) (range 59 -1 -1))] - (testing "edge cases 1" - (is (= (count m) 60)) - (is (= (seq m) s1)) - (is (= (rseq m) s2)))) - (let [m (sorted-map :foo 1 :bar 2 :quux 3)] - (testing "edge cases 2" - (is (= (dissoc m :foo) (hash-map :bar 2 :quux 3))) - (is (= (count (dissoc m :foo)) 2)) - (is (= (hash m) (hash (hash-map :foo 1 :bar 2 :quux 3)))) - (is (= (subseq m < :foo) (list [:bar 2]))) - (is (= (subseq m <= :foo) (list [:bar 2] [:foo 1]))) - (is (= (subseq m > :foo) (list [:quux 3]))) - (is (= (subseq m >= :foo) (list [:foo 1] [:quux 3]))) - (is (= (map #(reduce (fn [_ x] x) %) m) (list 2 1 3))) - (is (= (map #(reduce (fn [x _] x) 7 %) m) (list 7 7 7))))) - )) - -(deftest test-sorted-sets - (let [s1 (sorted-set) - c2 (comp - compare) - s2 (sorted-set-by c2) - c3 #(compare (quot %1 2) (quot %2 2)) - s3 (sorted-set-by c3) - s4 (sorted-set-by <)] - (testing "Testing sorted set" - (is (identical? cljs.core.PersistentTreeSet (type s1))) - (is (identical? cljs.core.PersistentTreeSet (type s2))) - (is (identical? compare (-comparator s1))) - (is (zero? (count s1))) - (is (zero? (count s2))) - (is (nil? (rseq s1))) - (let [s1 (conj s1 1 2 3) - s2 (conj s2 1 2 3) - s3 (conj s3 1 2 3 7 8 9) - s4 (conj s4 1 2 3)] - (testing "basic ops" - (is (= (hash s1) (hash s2))) - (is (= (hash s1) (hash #{1 2 3}))) - (is (= (seq s1) (list 1 2 3))) - (is (= (rseq s1) (list 3 2 1))) - (is (= (seq s2) (list 3 2 1))) - (is (= (rseq s2) (list 1 2 3))) - (is (= (count s1) 3)) - (is (= (count s2) 3)) - (is (= (count s3) 4)) - (is (= (get s3 0) 1)) - (is (= (subseq s3 > 5) (list 7 8))) - (is (= (subseq s3 > 6) (list 8))) - (is (= (subseq s3 >= 6) (list 7 8))) - (is (= (subseq s3 >= 12) nil)) - (is (= (subseq s3 < 0) (list))) - (is (= (subseq s3 < 5) (list 1 2))) - (is (= (subseq s3 < 6) (list 1 2))) - (is (= (subseq s3 <= 6) (list 1 2 7))) - (is (= (subseq s3 >= 2 <= 6) (list 2 7))) - (is (= (subseq s4 >= 2 < 3) (list 2))) - (let [s1 (disj s1 2) - s2 (disj s2 2)] - (testing "edge cases" - (is (= (seq s1) (list 1 3))) - (is (= (rseq s1) (list 3 1))) - (is (= (seq s2) (list 3 1))) - (is (= (rseq s2) (list 1 3))) - (is (= (count s1) 2)) - (is (= (count s2) 2))))))))) - (deftest test-transducers (testing "Testing transducers" (is (= (sequence (map inc) (array 1 2 3)) '(2 3 4))) @@ -842,52 +437,6 @@ (is (.-done (.next iter))))) )) -(deftest test-mumur-support - (testing "Testing murmur support" - ;; int-rotate-left - (is (== (int-rotate-left (bit-or 0x87654321 0) 4) (bit-or 0x76543218 0))) - (is (== (int-rotate-left (bit-or 0x87654321 0) 8) (bit-or 0x65432187 0))) - (is (== (int-rotate-left (bit-or 0x80000000 0) 1) 0x1)) - (is (== (int-rotate-left (bit-or 0x78123456 0) 4) (bit-or 0x81234567 0))) - (is (== (int-rotate-left (bit-or 0xffffffff 0) 4) (bit-or 0xffffffff 0))) - - ;; imul - (is (== (imul 3 3) 9)) - (is (== (imul -1 8) -8)) - (is (== (imul -2 -2) 4)) - (is (== (imul 0xffffffff 5) -5)) - (is (== (imul 0xfffffffe 5) -10)) - )) - -(deftest test-lazy-seq-realized? - (testing "Testing LazySeq IPending" - (let [xs (lazy-seq - (cons 1 - (lazy-seq - (cons 2 - (lazy-seq (cons 3 nil))))))] - (is (not (realized? xs))) - (is (not (realized? (rest xs)))) - (is (realized? xs)) - (is (not (realized? (nthrest xs 2)))) - (is (realized? (rest xs)))))) - -(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))))))))) - (deftest test-reader-literals (testing "Testing reader literals" (is (= #queue [1] (into cljs.core.PersistentQueue.EMPTY [1]))) @@ -950,20 +499,6 @@ (is (= (meta (with-meta (reify IFoo (foo [this] :foo)) {:foo :bar})) {:foo :bar}))) -;; hashing bug in many JS runtimes CLJ-118 -(deftest test-clj-118 - (let [g #{(conj #{:2} :alt)} - h #{#{:2 :alt}}] - (is (= g h))) - (is (= (hash {:a 1 :b 2}) - (hash {:b 2 :a 1}))) - (is (= (hash (hash-map :a 1 :b 2)) - (hash (hash-map :b 2 :a 1)))) - (is (= (hash {:start 133 :end 134}) - (hash (apply hash-map [:start 133 :end 134])))) - (is (= (hash :a) - (hash (keyword "a"))))) - (let [x "original"] (defn original-closure-stmt [] x)) @@ -1063,15 +598,6 @@ (is (false? (exists? js/jQuery))) (is (exists? exists?-test-val)))) -(deftest test-496 - (is (= (char 65) \A)) - (is (= (char \A) \A))) - -(deftype PositionalFactoryTest [x]) - -(deftest test-515 - (is (== 1 (.-x (->PositionalFactoryTest 1))))) - (deftest test-518 (is (nil? (:test "test")))) @@ -1181,25 +707,6 @@ (deftest test-584 (is (= (count {some-x :foo some-y :bar}) 1))) -(deftest test-717 - (testing "Testing CLJS-717, JS literals" - (is (array? #js [1 2 3])) - (is (= (alength #js [1 2 3]) 3)) - (is (= (seq #js [1 2 3]) (seq [1 2 3]))) - (is (= (set (js-keys #js {:foo "bar" :baz "woz"})) #{"foo" "baz"})) - (is (= (aget #js {:foo "bar"} "foo") "bar")) - (is (= (aget #js {"foo" "bar"} "foo") "bar")) - (is (array? (aget #js {"foo" #js [1 2 3]} "foo"))) - (is (= (seq (aget #js {"foo" #js [1 2 3]} "foo")) '(1 2 3))))) - -(deftest test-1556 - (testing "Testing CLJS-1556, JS object literal code emission, beginning of statement" - ;; Really testing that this evaluates properly - (is (= 1 (do #js {:a 1} - 1))) - (is (= 1 (aget #js {:a 1} "a"))) - (is (= 1 (.-a #js {:a 1}))))) - (deftest test-725 (testing "Testing CLJS-725, drop" (is (= (apply vector (drop-while (partial = 1) [1 2 3])) [2 3])) @@ -1214,22 +721,6 @@ (is (true? (object? #js {}))) (is (false? (object? nil))))) -(deftest test-count-hash-set - (is - (== (count (hash-set [1 4] [2 4] [3 4] [0 3] [1 3] [2 3] [3 3] - [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] - [2 1] [3 1] [1 0] [2 0] [3 0])) - (count (list [1 4] [2 4] [3 4] [0 3] [1 3] [2 3] [3 3] - [0 2] [1 2] [2 2] [3 2] [4 2] [0 1] [1 1] - [2 1] [3 1] [1 0] [2 0] [3 0]))))) - -(deftest test-734 - (testing "Testing CLJS-734, transient operations" - (is (= (-> (transient []) (conj! 1 2) persistent!) [1 2])) - (is (= (-> (transient #{1 2 3}) (disj! 1 2) persistent!) #{3})) - (is (= (-> (transient {}) (assoc! :a 1 :b 2) persistent!) {:a 1 :b 2})) - (is (= (-> (transient {:a 1 :b 2 :c 3}) (dissoc! :a :b) persistent!) {:c 3})))) - (deftest test-767 (testing "Testing CLJS-767, invalid assoc" (doseq [n [nil "-1" "" "0" "1" false true (js-obj)]] @@ -1283,12 +774,6 @@ (is (= :fail (try (nth (range 1 3) n 4) (catch js/Error e :fail)))))) ) -(deftest test-778 - (testing "Testing CLJS-778, -rest, -next RSeq" - (is (= (-rest (rseq [0])) ())) - (is (nil? (-next (rseq [0])))) - (is (= (set (rseq [0])) #{0})))) - (def cljs-780 (atom {:foo (with-meta [] {:bar '(1 2 3)})})) (deftest test-780 @@ -1303,32 +788,6 @@ (is (= (.toString #uuid "550e8400-e29b-41d4-a716-446655440000") "550e8400-e29b-41d4-a716-446655440000")))) -(deftest test-784 - (testing "Testing CLJS-784, conj on maps" - (doseq [m [(array-map) (hash-map) (sorted-map)]] - (is (= :ok - (try - (conj m "foo") - (catch js/Error _ - :ok)))) - (is (= {:foo 1} (conj m [:foo 1]))) - (is (= {:foo 1} (conj m {:foo 1}))) - (is (= {:foo 1} (conj m (list [:foo 1]))))) - (doseq [mt [array-map hash-map sorted-map]] - (is (= {:foo 1 :bar 2 :baz 3} - (conj (mt :foo 1) - ((fn make-seq [from-seq] - ;; this tests specifically for user defined seq's, that implement the bare minimum, i.e. no INext - (when (seq from-seq) - (reify - ISeqable - (-seq [this] this) - ISeq - (-first [this] (first from-seq)) - (-rest [this] (make-seq (rest from-seq)))))) - [[:bar 2] [:baz 3]])))))) - ) - (deftest test-case-keyword (is (= (let [x "a"] (case x :a 1 "a")) "a"))) @@ -1369,40 +828,6 @@ (is (every? #(= :failed (try (re-matches #"nomatch" %) (catch js/TypeError _ :failed))) not-strings))))) -(deftest test-849 - (let [xs [44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24]] - (testing "Testing CLJS-849, transient contains?" - (is (loop [m (transient (zipmap xs (repeat 1))) - xs xs] - (if-let [x (first xs)] - (if (contains? m x) - (recur (dissoc! m x) (next xs)) - false) - true)))))) - -(deftest test-large-array-map - (let [m (array-map 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15)] - (testing "Testing large array maps" - (is (instance? cljs.core/PersistentArrayMap m)) - (is (= (seq m) [[0 0] [1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9] [10 10] [11 11] [12 12] [13 13] [14 14] [15 15]]))))) - -(def test-map - {:a 1 - :b 2 - #inst "2013-12-19T05:00:00.000-00:00" 3 - :d 4 - :e 5 - #inst "2013-12-06T05:00:00.000-00:00" 6 - :g 7 - :h 8 - :i 9 - :j 10}) - -(deftest test-716 - (testing "Testing CLJS-716, date as keys in maps" - (is (= (test-map #inst "2013-12-19T05:00:00.000-00:00") 3)) - (is (= (test-map #inst "2013-12-06T05:00:00.000-00:00") 6)))) - (deftest test-853 (testing "Testing CLJS-853, function metadata" (is (= {:foo true} (meta ^:foo (fn [])))))) @@ -1414,240 +839,6 @@ (is (= 1.5 1.5M)) (is (= 4.9E-324 5E-324M)))) -(deftest test-transient-edge-case-1 - (let [v1 (vec (range 15 48)) - v2 (vec (range 40 57)) - v1 (persistent! (assoc! (conj! (pop! (transient v1)) :foo) 0 :quux)) - v2 (persistent! (assoc! (conj! (transient v2) :bar) 0 :quux)) - v (into v1 v2)] - (is (= v (vec (concat [:quux] (range 16 47) [:foo] - [:quux] (range 41 57) [:bar])))))) - -(deftest test-transient-edge-case-2 - (is (loop [v (transient []) - xs (range 100)] - (if-let [x (first xs)] - (recur - (condp #(%1 (mod %2 3)) x - #{0 2} (conj! v x) - #{1} (assoc! v (count v) x)) - (next xs)) - (= (vec (range 100)) (persistent! v)))))) - -(deftest test-phm - ;; PersistentHashMap & TransientHashMap - (loop [m1 cljs.core.PersistentHashMap.EMPTY - m2 (transient cljs.core.PersistentHashMap.EMPTY) - i 0] - (if (< i 100) - (recur (assoc m1 i i) (assoc! m2 i i) (inc i)) - (let [m2 (persistent! m2)] - (is (= (count m1) 100)) - (is (= (count m2) 100)) - (is (= m1 m2)) - (loop [i 0] - (if (< i 100) - (do (is (= (m1 i) i)) - (is (= (m2 i) i)) - (is (= (get m1 i) i)) - (is (= (get m2 i) i)) - (is (contains? m1 i)) - (is (contains? m2 i)) - (recur (inc i))))) - (is (= (map vector (range 100) (range 100)) (sort-by first (seq m1)))) - (is (= (map vector (range 100) (range 100)) (sort-by first (seq m2)))) - (is (not (contains? (dissoc m1 3) 3)))))) - (let [tm (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY)) - (dissoc 3 5 7) - transient)] - (doseq [k [0 1 2 4 6 8 9]] - (is (= k (get tm k)))) - (let [m (persistent! tm)] - (is (= 2 (try (dissoc! tm 1) 1 (catch js/Error e 2)))) - (is (= 2 (try (assoc! tm 10 10) 1 (catch js/Error e 2)))) - (is (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) - (is (= 2 (try (count tm) 1 (catch js/Error e 2)))) - (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY)) - (dissoc 3 5 7))] - (testing "Testing PHM dissoc" - (is (= (count m) 7)) - (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY)) - (conj [:foo 1]))] - (testing "Testing PHM conj" - (is (= (count m) 11)) - (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1})))) - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY) - transient) - (conj! [:foo 1]) - persistent!)] - (testing "Testing PHM conj!" - (is (= (count m) 11)) - (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1})))) - (let [tm (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentHashMap.EMPTY) - transient)] - (testing "Testing transient PHM" - (is (loop [tm tm ks [3 5 7]] - (if-let [k (first ks)] - (recur (dissoc! tm k) (next ks)) - (let [m (persistent! tm)] - (and (= (count m) 7) - (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))))) -) - -(deftype FixedHash [h v] - IHash - (-hash [this] h) - IEquiv - (-equiv [this other] - (and (instance? FixedHash other) (= v (.-v other))))) - -(def fixed-hash-foo (FixedHash. 0 :foo)) -(def fixed-hash-bar (FixedHash. 0 :bar)) - -(deftest test-phm-fixed-hash - (let [m (assoc cljs.core.PersistentHashMap.EMPTY - fixed-hash-foo 1 - fixed-hash-bar 2)] - (is (= (get m fixed-hash-foo) 1)) - (is (= (get m fixed-hash-bar) 2)) - (is (= (count m) 2)) - (let [m (dissoc m fixed-hash-foo)] - (is (= (get m fixed-hash-bar) 2)) - (is (not (contains? m fixed-hash-foo))) - (is (= (count m) 1)))) - - (let [m (into cljs.core.PersistentHashMap.EMPTY ; make sure we're testing - (zipmap (range 100) (range 100))) ; the correct map type - m (assoc m fixed-hash-foo 1 fixed-hash-bar 2)] - (is (= (count m) 102)) - (is (= (get m fixed-hash-foo) 1)) - (is (= (get m fixed-hash-bar) 2)) - (let [m (dissoc m 3 5 7 fixed-hash-foo)] - (is (= (get m fixed-hash-bar) 2)) - (is (not (contains? m fixed-hash-foo))) - (is (= (count m) 98)))) - - (let [m (into cljs.core.PersistentHashMap.EMPTY ; make sure we're testing - (zipmap (range 100) (range 100))) ; the correct map type - m (transient m) - m (assoc! m fixed-hash-foo 1) - m (assoc! m fixed-hash-bar 2) - m (persistent! m)] - (is (= (count m) 102)) - (is (= (get m fixed-hash-foo) 1)) - (is (= (get m fixed-hash-bar) 2)) - (let [m (dissoc m 3 5 7 fixed-hash-foo)] - (is (= (get m fixed-hash-bar) 2)) - (is (not (contains? m fixed-hash-foo))) - (is (= (count m) 98))))) - -(def array-map-conversion-threshold - cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD) - -(deftest test-pam - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY)) - (dissoc 3 5 7))] - (is (= (count m) 7)) - (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9}))) - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY)) - (conj [:foo 1]))] - (is (= (count m) 11)) - (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) - (let [m (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY) - transient) - (conj! [:foo 1]) - persistent!)] - (is (= (count m) 11)) - (is (= m {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 :foo 1}))) - (let [tm (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY) - transient)] - (loop [tm tm ks [3 5 7]] - (if-let [k (first ks)] - (recur (dissoc! tm k) (next ks)) - (let [m (persistent! tm)] - (is (= (count m) 7)) - (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))))) - (let [tm (-> (->> (interleave (range 10) (range 10)) - (apply assoc cljs.core.PersistentArrayMap.EMPTY)) - (dissoc 3 5 7) - transient)] - (doseq [k [0 1 2 4 6 8 9]] - (is (= k (get tm k)))) - (let [m (persistent! tm)] - (is (= 2 (try (dissoc! tm 1) 1 (catch js/Error e 2)))) - (is (= 2 (try (assoc! tm 10 10) 1 (catch js/Error e 2)))) - (is (= 2 (try (persistent! tm) 1 (catch js/Error e 2)))) - (is (= 2 (try (count tm) 1 (catch js/Error e 2)))) - (is (= m {0 0 1 1 2 2 4 4 6 6 8 8 9 9})))) - (let [m (apply assoc cljs.core.PersistentArrayMap.EMPTY - (interleave (range (* 2 array-map-conversion-threshold)) - (range (* 2 array-map-conversion-threshold))))] - (is (= (count m) (* 2 array-map-conversion-threshold))) - (is (= (m array-map-conversion-threshold) array-map-conversion-threshold)) - (is (= m (into cljs.core.PersistentHashMap.EMPTY - (map #(vector % %) - (range (* 2 array-map-conversion-threshold))))))) - ) - -(deftest test-literal-maps - (loop [m1 {} m2 {} i 0] - (if (< i 100) - (recur (assoc m1 i i) (assoc m2 (str "foo" i) i) (inc i)) - (do (is (= m1 (into cljs.core.PersistentHashMap.EMPTY - (map vector (range 100) (range 100))))) - (is (= m2 (into cljs.core.PersistentHashMap.EMPTY - (map vector - (map (partial str "foo") (range 100)) - (range 100))))) - (is (= (count m1) 100)) - (is (= (count m2) 100))))) - ) - -(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))) - (let [n (inc (+ cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD 2)) - expected (repeat n {:foo :bar})] - (is (= result expected)))))) - -(deftest test-transient-hash-set - ;; TransientHashSet - (loop [s (transient #{}) - i 0] - (if (< i 100) - (recur (conj! s i) (inc i)) - (loop [s s i 0] - (if (< i 100) - (if (zero? (mod i 3)) - (recur (disj! s i) (inc i)) - (recur s (inc i))) - (let [s (persistent! s)] - (is (= s (loop [s #{} xs (remove #(zero? (mod % 3)) (range 100))] - (if-let [x (first xs)] - (recur (conj s x) (next xs)) - s)))) - (is (= s (set (remove #(zero? (mod % 3)) (range 100)))))))))) -) - (deftest test-921-var-meta-name (testing "testing CLJS-921, :name var metadata should be unqualified" (is (= (-> (var first) meta :name) 'first)))) @@ -1680,25 +871,6 @@ (is (== @a0 10)) (is (== @a1 20))))) -(deftest test-962-empty-literal-hashes - (testing "CLJS-962: empty literals should produce collections with correct hash codes" - (let [l () - v [] - s #{} - m {}] - (is (== (hash l) (hash v) (hash-ordered-coll ()))) - (is (== (hash s) (hash m) (hash-unordered-coll #{}))))) - (testing "CLJS-962: EMPTY collections should have correct hash codes" - (let [l (.-EMPTY List) - pv (.-EMPTY PersistentVector) - phs (.-EMPTY PersistentHashSet) - pts (.-EMPTY PersistentTreeSet) - pam (.-EMPTY PersistentArrayMap) - phm (.-EMPTY PersistentHashMap) - ptm (.-EMPTY PersistentTreeMap)] - (is (== (hash l) (hash pv) (hash-ordered-coll ()))) - (is (apply == (hash-unordered-coll #{}) (map hash [phs pts pam phm ptm])))))) - (deftest test-map-new-transducers (testing "Test distinct, interpose, map-indexed transducers" (is (= [1 2 3] @@ -1708,22 +880,6 @@ (is (= [[0 1] [1 2] [2 3]] (transduce (map-indexed (fn [i x] [i x])) conj [] [1 2 3]))))) -(deftest test-vec - (let [v (vec #js [1 2 3 4])] - (is (= (count v) 4)) - (is (= (first v) 1)) - (is (= (last v) 4)) - (is (= v [1 2 3 4])))) - -(deftest test-phm-from-array - (let [m (.fromArray PersistentHashMap #js [1 2 3 4] true)] - (is (= (count m) 2)) - (is (contains? m 1)) - (is (contains? m 3)) - (is (= (get m 1) 2)) - (is (= (get m 3) 4)) - (is (= m {1 2 3 4})))) - (defn foo-var [f] (fn [x] (f x))) @@ -1785,19 +941,6 @@ (is (= (with-out-str (print-foo-1187 :foo)) "foo!")))) -(deftest test-cljs-1189 - (testing "array-map should always return array maps" - (let [am (apply array-map (range 100))] - (is (== (count am) 50)) - (is (instance? PersistentArrayMap am))))) - -(deftest test-cljs-1199 - (testing "array-map should skip dropped elements of IndexedSeq" - (is (= {:a 1} (apply array-map (drop 1 [0 :a 1])))))) -(deftest test-cljs-1212 - (is (= (set {:a 0 :b 0 :c 0 :d 0 :e 0 :f 0 :g 0 :h 0 :i 0}) - #{[:a 0] [:b 0] [:c 0] [:d 0] [:e 0] [:f 0] [:g 0] [:h 0] [:i 0]}))) - (deftest test-var-arglists (is (= (-> #'first meta :arglists) '([coll]))) (is (= (-> #'hash-map meta :arglists) '([& keyvals]))) @@ -1885,10 +1028,6 @@ (is (= 'abc (munge 'abc))) (is (= "toString" (munge "toString")))) -(deftest test-uuid-compile-and-runtime-hash - (is (= (hash (.toString #uuid "0d1f9029-40fc-4728-8bdd-9862172d4370")) - (hash (.toString (UUID. "0d1f9029-40fc-4728-8bdd-9862172d4370" nil)))))) - (defprotocol IFooBar (a-method [t])) @@ -1897,18 +1036,6 @@ IFooBar (cljs.core-test/a-method [_] "foobar")))))) -(deftest test-cljs-1524 - (let [x0 [] - x1 (conj x0 1) - x2 (conj x1 2) - x3 (remove #{1} x2) - x4 (remove #{2} x3) - x5 (conj x4 3) - x6 (conj x5 4) - x7 (conj x6 5)] - (is (not (== (hash x0) (hash x1) (hash x2) (hash x3) (hash x4) - (hash x5) (hash x6) (hash x7)))))) - (deftest test-cljs-1569 (is (= (meta (with-meta (seq [1 2 3]) {:a 1})) {:a 1}))) @@ -1949,10 +1076,6 @@ (+ acc x))) [2 4 6 :stop 8 10])))) -(deftest test-nil-hashing-cljs-1649 - (is (zero? (hash-string nil))) - (is (not (zero? (hash-string "null"))))) - (deftest test-cljs-1721 (is (= 1 (get-in {:a (array 1 2 3 4)} [:a 0] :not-found))) (is (= :not-found (get-in {:a (array 1 2 3 4)} [:a 4] :not-found))) @@ -1975,10 +1098,6 @@ (is (= foo-1274 42)) (is (= cljs.core-test/foo-1274 42))) -(deftest test-cljs-1779 - (is (= (hash (keyword 'app "foo")) - (hash (keyword "app" "foo"))))) - (defrecord CLJS1780 [a b c]) (deftest test-cljs-1780 @@ -1996,37 +1115,6 @@ (is (= (get #js [\h \i] 1.7 :not-found) \i)) (is (= (get "hi" 1.7 :not-found) \i))) -(defprotocol CLJS-1600-IFoo - (foo-fn [_ {:keys [a b] :as x}])) - -(defrecord CLJS-1600-Foo [] - CLJS-1600-IFoo - (foo-fn [_ {:keys [a b] :as args}] - args)) - -(deftest test-cljs-1600 - (let [foo (reify - CLJS-1600-IFoo - (foo-fn [_ {:keys [a b] :as args}] - args))] - (is (= (foo-fn (->CLJS-1600-Foo) {:a 1 :b 2}) - {:a 1 :b 2})) - (is (= (foo-fn foo {:a 1 :b 2}) - {:a 1 :b 2}))) - ;; test that the destructuring works - (let [foo (reify - CLJS-1600-IFoo - (foo-fn [_ {:keys [a b] :as args}] - {:a a :b b}))] - (is (= (foo-fn foo {:a 1 :b 2}) - {:a 1 :b 2}))) - (let [foo (reify - CLJS-1600-IFoo - (foo-fn [_ {:keys [a b c] :or {c 3}}] - {:c c}))] - (is (= (foo-fn foo {:a 1 :b 2}) - {:c 3})))) - (deftest test-cljs-1748 (is (thrown? js/Error (nth (array 0 1 2) 3))) (is (thrown? js/Error (nth (array 0 1 2) -1))) diff --git a/src/test/cljs/cljs/destructuring_test.cljs b/src/test/cljs/cljs/destructuring_test.cljs index 9f1007d9a..7667bd659 100644 --- a/src/test/cljs/cljs/destructuring_test.cljs +++ b/src/test/cljs/cljs/destructuring_test.cljs @@ -145,4 +145,35 @@ (is (= [1 2 [3 4]] (apply destructure-1216 [1 2 3 4]))) (is (= (destructure-1216 1 2 3 4)[1 2 [3 4]] - (apply destructure-1216 [1 2 3 4]))))) \ No newline at end of file + (apply destructure-1216 [1 2 3 4]))))) + +(defprotocol CLJS-1600-IFoo + (foo-fn [_ {:keys [a b] :as x}])) + +(defrecord CLJS-1600-Foo [] + CLJS-1600-IFoo + (foo-fn [_ {:keys [a b] :as args}] + args)) + +(deftest test-cljs-1600 + (let [foo (reify + CLJS-1600-IFoo + (foo-fn [_ {:keys [a b] :as args}] + args))] + (is (= (foo-fn (->CLJS-1600-Foo) {:a 1 :b 2}) + {:a 1 :b 2})) + (is (= (foo-fn foo {:a 1 :b 2}) + {:a 1 :b 2}))) + ;; test that the destructuring works + (let [foo (reify + CLJS-1600-IFoo + (foo-fn [_ {:keys [a b] :as args}] + {:a a :b b}))] + (is (= (foo-fn foo {:a 1 :b 2}) + {:a 1 :b 2}))) + (let [foo (reify + CLJS-1600-IFoo + (foo-fn [_ {:keys [a b c] :or {c 3}}] + {:c c}))] + (is (= (foo-fn foo {:a 1 :b 2}) + {:c 3})))) \ No newline at end of file diff --git a/src/test/cljs/cljs/hashing_test.cljs b/src/test/cljs/cljs/hashing_test.cljs new file mode 100644 index 000000000..041305051 --- /dev/null +++ b/src/test/cljs/cljs/hashing_test.cljs @@ -0,0 +1,90 @@ +; 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.hashing-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.string :as s] + [clojure.set :as set])) + +(deftest test-hash-null + (is (zero? (hash (aget (js-obj) "foo"))))) + +;; hashing bug in many JS runtimes CLJ-118 +(deftest test-clj-118 + (let [g #{(conj #{:2} :alt)} + h #{#{:2 :alt}}] + (is (= g h))) + (is (= (hash {:a 1 :b 2}) + (hash {:b 2 :a 1}))) + (is (= (hash (hash-map :a 1 :b 2)) + (hash (hash-map :b 2 :a 1)))) + (is (= (hash {:start 133 :end 134}) + (hash (apply hash-map [:start 133 :end 134])))) + (is (= (hash :a) + (hash (keyword "a"))))) + +(deftest test-962-empty-literal-hashes + (testing "CLJS-962: empty literals should produce collections with correct hash codes" + (let [l () + v [] + s #{} + m {}] + (is (== (hash l) (hash v) (hash-ordered-coll ()))) + (is (== (hash s) (hash m) (hash-unordered-coll #{}))))) + (testing "CLJS-962: EMPTY collections should have correct hash codes" + (let [l (.-EMPTY List) + pv (.-EMPTY PersistentVector) + phs (.-EMPTY PersistentHashSet) + pts (.-EMPTY PersistentTreeSet) + pam (.-EMPTY PersistentArrayMap) + phm (.-EMPTY PersistentHashMap) + ptm (.-EMPTY PersistentTreeMap)] + (is (== (hash l) (hash pv) (hash-ordered-coll ()))) + (is (apply == (hash-unordered-coll #{}) (map hash [phs pts pam phm ptm])))))) + +(deftest test-uuid-compile-and-runtime-hash + (is (= (hash (.toString #uuid "0d1f9029-40fc-4728-8bdd-9862172d4370")) + (hash (.toString (UUID. "0d1f9029-40fc-4728-8bdd-9862172d4370" nil)))))) + +(deftest test-cljs-1524 + (let [x0 [] + x1 (conj x0 1) + x2 (conj x1 2) + x3 (remove #{1} x2) + x4 (remove #{2} x3) + x5 (conj x4 3) + x6 (conj x5 4) + x7 (conj x6 5)] + (is (not (== (hash x0) (hash x1) (hash x2) (hash x3) (hash x4) + (hash x5) (hash x6) (hash x7)))))) + +(deftest test-nil-hashing-cljs-1649 + (is (zero? (hash-string nil))) + (is (not (zero? (hash-string "null"))))) + +(deftest test-cljs-1779 + (is (= (hash (keyword 'app "foo")) + (hash (keyword "app" "foo"))))) + +(deftest test-mumur-support + (testing "Testing murmur support" + ;; int-rotate-left + (is (== (int-rotate-left (bit-or 0x87654321 0) 4) (bit-or 0x76543218 0))) + (is (== (int-rotate-left (bit-or 0x87654321 0) 8) (bit-or 0x65432187 0))) + (is (== (int-rotate-left (bit-or 0x80000000 0) 1) 0x1)) + (is (== (int-rotate-left (bit-or 0x78123456 0) 4) (bit-or 0x81234567 0))) + (is (== (int-rotate-left (bit-or 0xffffffff 0) 4) (bit-or 0xffffffff 0))) + + ;; imul + (is (== (imul 3 3) 9)) + (is (== (imul -1 8) -8)) + (is (== (imul -2 -2) 4)) + (is (== (imul 0xffffffff 5) -5)) + (is (== (imul 0xfffffffe 5) -10)) + )) \ No newline at end of file diff --git a/src/test/cljs/cljs/new_new_test.cljs b/src/test/cljs/cljs/new_new_test.cljs index b1c3970fe..fac856619 100644 --- a/src/test/cljs/cljs/new_new_test.cljs +++ b/src/test/cljs/cljs/new_new_test.cljs @@ -116,3 +116,8 @@ (deftest test-get-basis (is (= (.getBasis TypeBasis) '[a b])) (is (= (.getBasis RecordBasis) '[c d e]))) + +(deftype PositionalFactoryTest [x]) + +(deftest test-515 + (is (== 1 (.-x (->PositionalFactoryTest 1))))) diff --git a/src/test/cljs/cljs/primitives_test.cljs b/src/test/cljs/cljs/primitives_test.cljs index caa56bd09..b0d8083d0 100644 --- a/src/test/cljs/cljs/primitives_test.cljs +++ b/src/test/cljs/cljs/primitives_test.cljs @@ -928,3 +928,26 @@ (catch js/Error e true))) (is (try (do (take-nth nil [1 2 3]) false) (catch js/Error e true))))) + +(deftest test-496 + (is (= (char 65) \A)) + (is (= (char \A) \A))) + +(deftest test-717 + (testing "Testing CLJS-717, JS literals" + (is (array? #js [1 2 3])) + (is (= (alength #js [1 2 3]) 3)) + (is (= (seq #js [1 2 3]) (seq [1 2 3]))) + (is (= (set (js-keys #js {:foo "bar" :baz "woz"})) #{"foo" "baz"})) + (is (= (aget #js {:foo "bar"} "foo") "bar")) + (is (= (aget #js {"foo" "bar"} "foo") "bar")) + (is (array? (aget #js {"foo" #js [1 2 3]} "foo"))) + (is (= (seq (aget #js {"foo" #js [1 2 3]} "foo")) '(1 2 3))))) + +(deftest test-1556 + (testing "Testing CLJS-1556, JS object literal code emission, beginning of statement" + ;; Really testing that this evaluates properly + (is (= 1 (do #js {:a 1} + 1))) + (is (= 1 (aget #js {:a 1} "a"))) + (is (= 1 (.-a #js {:a 1}))))) \ No newline at end of file diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs new file mode 100644 index 000000000..7e75b0c13 --- /dev/null +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -0,0 +1,190 @@ +; 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.seqs-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.string :as s] + [clojure.set :as set])) + +(deftest test-sequential-equality + (testing "Testing ISequential equality" + (is (= (list 3 2 1) [3 2 1])) + (is (= [3 2 1] (seq (array 3 2 1)))))) + +(deftest test-seq-operations + (testing "Testing basic seq operations" + (is (= () (rest nil))) + (is (= nil (seq (array)))) + (is (= nil (seq ""))) + (is (= nil (seq []))) + (is (= nil (seq {}))) + (is (= () (rest ()))) + (is (= () (rest [1]))) + (is (= () (rest (array 1)))))) + +(deftest test-empy-and-seq + (testing "Testing empty & seq" + (is (nil? (empty nil))) + (let [e-lazy-seq (empty (with-meta (lazy-seq (cons :a nil)) {:b :c}))] + (testing "lazy seq" + (is (seq? e-lazy-seq)) + (is (empty? e-lazy-seq)) + (is (= {:b :c} (meta e-lazy-seq))))) + (let [e-list (empty '^{:b :c} (1 2 3))] + (testing "list" + (is (seq? e-list)) + (is (empty? e-list)) + (is (= {:b :c} (meta e-list))))) + (let [e-elist (empty '^{:b :c} ())] + (testing "empty list with metadata" + (is (seq? e-elist)) + (is (empty? e-elist)) + (is (= :c (get (meta e-elist) :b))))) + (let [e-cons (empty (with-meta (cons :a nil) {:b :c}))] + (testing "cons" + (is (seq? e-cons)) + (is (empty? e-cons)) + (is (= {:b :c} (meta e-cons))))) + (let [e-vec (empty ^{:b :c} [:a :d :g])] + (testing "vector" + (is (vector? e-vec)) + (is (empty? e-vec)) + (is (= {:b :c} (meta e-vec))))) + (let [e-omap (empty ^{:b :c} {:a :d :g :h})] + (testing "map" + (is (map? e-omap)) + (is (empty? e-omap)) + (is (= {:b :c} (meta e-omap))))) + (let [e-hmap (empty ^{:b :c} {[1 2] :d :g :h})] + (testing "map with complex keys" + (is (map? e-hmap)) + (is (empty? e-hmap)) + (is (= {:b :c} (meta e-hmap))))) + (let [smap (with-meta (sorted-map-by (comp - compare) 2 :a 1 :b 5 :c) {:b :c}) + e-smap (empty smap)] + (testing "sorted-map-by" + (is (map? e-smap)) + (is (empty? e-smap)) + (is (= {:b :c} (meta e-smap))) + (is (identical? (-comparator smap) (-comparator e-smap))) + (is (= [[5 :c] [2 :a] [1 :b]] (seq (assoc e-smap 2 :a 1 :b 5 :c)))))) + (let [sset (with-meta (sorted-set-by (comp - compare) 5 1 2) {:b :c}) + e-sset (empty sset)] + (testing "sorted-set-by" + (is (set? e-sset)) + (is (empty? e-sset)) + (is (= {:b :c} (meta e-sset))) + (is (identical? (-comparator sset) (-comparator e-sset))) + (is (= [5 2 1] (seq (conj e-sset 5 1 2)))))) + (let [e-queue (empty (with-meta (.-EMPTY PersistentQueue) {:b :c}))] + (testing "queue" + (is (identical? (type e-queue) PersistentQueue)) + (is (empty? e-queue)) + (is (= {:b :c} (meta e-queue))))))) + +(deftest test-distinct + (testing "Testing distinct? & distinct" + (is (distinct? 1 2 3)) + (is (not (distinct? 1 2 3 1))) + (is (= (distinct ()) ())) + (is (= (distinct '(1)) '(1))) + (is (= (distinct '(1 2 3 1 1 1)) '(1 2 3))) + (is (= (distinct [1 1 1 2]) '(1 2))) + (is (= (distinct [1 2 1 2]) '(1 2))) + (is (= (distinct "a") ["a"])) + (is (= (distinct "abcabab") ["a" "b" "c"])) + (is (= (distinct ["abc" "abc"]) ["abc"])) + (is (= (distinct [nil nil]) [nil])) + (is (= (distinct [0.0 0.0]) [0.0])) + (is (= (distinct ['sym 'sym]) '[sym])) + (is (= (distinct [:kw :kw]) [:kw])) + (is (= (distinct [42 42]) [42])) + (is (= (distinct [[] []]) [[]])) + (is (= (distinct ['(1 2) '(1 2)]) '[(1 2)])) + (is (= (distinct [() ()]) [()])) + (is (= (distinct [[1 2] [1 2]]) [[1 2]])) + (is (= (distinct [{:a 1 :b 2} {:a 1 :b 2}]) [{:a 1 :b 2}])) + (is (= (distinct [{} {}]) [{}])) + (is (= (distinct [#{1 2} #{1 2}]) [#{1 2}])) + (is (= (distinct [#{} #{}]) [#{}])))) + +(deftest test-rearrange-sequential + (testing "Test rearranging sequential collections" + (is (= [1 2 3 4 5] (sort [5 3 1 4 2]))) + (is (= [1 2 3 4 5] (sort < [5 3 1 4 2]))) + (is (= [5 4 3 2 1] (sort > [5 3 1 4 2]))) + (is (= ["a" [ 1 2] "foo"] (sort-by count ["foo" "a" [1 2]]))) + (is (= ["foo" [1 2] "a"] (sort-by count > ["foo" "a" [1 2]]))) + (let [coll [1 2 3 4 5 6 7 8 9 10] + ;; while it is technically possible for this test to fail with a false negative, + ;; it's _extraordinarily_ unlikely. + shuffles (filter #(not= coll %) (take 100 (iterate shuffle coll)))] + (is (not (empty? shuffles)))) + )) + +(deftest test-ISequential-indexOf + (testing "Testing JS .indexOf in ISequential types" + ;; PersistentVector + (is (= (.indexOf [] 2) -1)) + (is (= (.indexOf [] 2 3) -1)) + (is (= (.indexOf [1 2 3 4 5] 2) 1)) + (is (= (.indexOf [1 2 3 4 5] 6) -1)) + (is (= (.indexOf [1 2 3 4 5] -1) -1)) + (is (= (.indexOf [1 2 "x" 4 5 "a"] "a") 5)) + (is (= (.indexOf [1 2 3 4 5] 1 2) -1)) + (is (= (.indexOf [1 2 3 4 5] 2 2) -1)) + (is (= (.indexOf [1 2 3 1 5] 1 2) 3)) + (is (= (.indexOf [1 2 3 4 5] 2) 1)) + (is (= (.indexOf '(1 2 3 4 5) 2) 1)) + (is (= (.indexOf (list 1 2 3) 3) 2)) + (is (= (.indexOf (lazy-seq [1 2 3 4 5]) 3)) 2) + (is (= (.indexOf (sequence (map inc) '(0 1 2 3 4)) 5) 4)))) + +(deftest test-ISequential-lastIndexOf + (testing "Testing JS .lastIndexOf in ISequential types" + ;; PersistentVector + (is (= (.lastIndexOf [] 2) -1)) + (is (= (.lastIndexOf [] 2 3) -1)) + (is (= (.lastIndexOf [1 2 3 4 5] 2) 1)) + (is (= (.lastIndexOf [1 2 3 1 5] 1) 3)) + (is (= (.lastIndexOf [1 2 3 1 5] 1 3) 3)) + (is (= (.lastIndexOf [1 2 3 1 5] 1 2) 0)) + (is (= (.lastIndexOf [1 2 3 1] 1 0) 0)) + (is (= (.lastIndexOf [1 2 3 4 5] 3 100) 2)) + (is (= (.lastIndexOf [1 1 1 1 1] 1) 4)) + (is (= (.lastIndexOf [1 1 1 1 1] 1 6) 4)) + (is (= (.lastIndexOf [1 2 1 1 1] 2) 1)) + (is (= (.lastIndexOf [1 2 3 4 5] 3 -100) -1)) + (is (= (.lastIndexOf [1 2 3 4 5] 3 -2) 2)) + (is (= (.lastIndexOf '(1 2 1 4 5) 1) 2)) + (is (= (.lastIndexOf (list 1 2 3 1 5) 1) 3)) + (is (= (.lastIndexOf (lazy-seq [1 2 1 4 5]) 1)) 2) + (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))))))))) + +(deftest test-778 + (testing "Testing CLJS-778, -rest, -next RSeq" + (is (= (-rest (rseq [0])) ())) + (is (nil? (-next (rseq [0])))) + (is (= (set (rseq [0])) #{0})))) \ No newline at end of file diff --git a/src/test/cljs/cljs/test_runner.cljs b/src/test/cljs/cljs/test_runner.cljs index d3880e20c..987fc399b 100644 --- a/src/test/cljs/cljs/test_runner.cljs +++ b/src/test/cljs/cljs/test_runner.cljs @@ -4,6 +4,9 @@ [cljs.destructuring-test] [cljs.new-new-test] [cljs.printing-test] + [cljs.seqs-test] + [cljs.collections-test] + [cljs.hashing-test] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -30,6 +33,9 @@ 'cljs.destructuring-test 'cljs.new-new-test 'cljs.printing-test + 'cljs.seqs-test + 'cljs.collections-test + 'cljs.hashing-test 'cljs.core-test 'cljs.reader-test 'clojure.string-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 695a60d88..f45215eff 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -247,6 +247,9 @@ [cljs.destructuring-test] [cljs.new-new-test] [cljs.printing-test] + [cljs.seqs-test] + [cljs.collections-test] + [cljs.hashing-test] [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -274,6 +277,9 @@ 'cljs.destructuring-test 'cljs.new-new-test 'cljs.printing-test + 'cljs.seqs-test + 'cljs.collections-test + 'cljs.hashing-test 'cljs.core-test 'cljs.reader-test 'clojure.string-test From a4c627d7f4905db7366896f8db59c4ef72bb478e Mon Sep 17 00:00:00 2001 From: Lauri Oherd Date: Sun, 25 Sep 2016 12:51:03 +0300 Subject: [PATCH 2037/4033] CLJS-1790: Port CLJ-1935: Use multimethod dispatch value method lookup to take hierarchies into account in multi-spec --- src/main/cljs/cljs/spec.cljs | 3 +-- src/test/cljs/cljs/spec_test.cljs | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index c69fc0048..09d28df32 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -442,8 +442,7 @@ ([form mmvar retag gfn] (let [id (random-uuid) predx #(let [mm @mmvar] - (c/and (contains? (methods mm) - ((-dispatch-fn mm) %)) + (c/and (-get-method mm ((-dispatch-fn mm) %)) (mm %))) dval #((-dispatch-fn @mmvar) %) tag (if (keyword? retag) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 18cee9396..6e755c10f 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -82,6 +82,16 @@ (is (= (s/form ::foo-1788) '(cljs.spec/multi-spec cljs.spec-test/mm :mm/type)))) +(def h-cljs-1790 (derive (make-hierarchy) :a :b)) +(defmulti spec-type-1790 identity :hierarchy #'h-cljs-1790) +(defmethod spec-type-1790 :b [_] + (s/spec (constantly true))) + +(deftest test-cljs-1790 + (s/def ::multi (s/multi-spec spec-type-1790 identity)) + (is (= :b (s/conform ::multi :b))) + (is (= :a (s/conform ::multi :a)))) + ;; Copied from Clojure spec tests (def even-count? #(even? (count %))) From d467ff1503272fcab361d353b18cd7f1350d344d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 30 Sep 2016 14:10:34 +0100 Subject: [PATCH 2038/4033] CLJS-1798: Add new tests to all test-runner namespaces --- src/test/cljs/cljs/test_runner.cljs | 55 ----------------------------- src/test/cljs/test_runner.cljs | 31 +++++++++++++--- src/test/self/self_parity/test.cljs | 23 ++++++++---- 3 files changed, 42 insertions(+), 67 deletions(-) delete mode 100644 src/test/cljs/cljs/test_runner.cljs diff --git a/src/test/cljs/cljs/test_runner.cljs b/src/test/cljs/cljs/test_runner.cljs deleted file mode 100644 index 987fc399b..000000000 --- a/src/test/cljs/cljs/test_runner.cljs +++ /dev/null @@ -1,55 +0,0 @@ -(ns test-runner - (:require [cljs.test :refer-macros [run-tests]] - [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 :as core-test] - [cljs.reader-test] - [cljs.binding-test] - [cljs.ns-test] - [clojure.string-test] - [clojure.data-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.pprint] - [cljs.spec-test] - [cljs.clojure-alias-test])) - -(set! *print-newline* false) -(set-print-fn! js/print) - -(run-tests - '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.reader-test - 'clojure.string-test - 'clojure.data-test - 'cljs.letfn-test - 'cljs.reducers-test - 'cljs.binding-test - 'cljs.macro-test - 'cljs.top-level - 'cljs.keyword-test - 'cljs.ns-test - 'cljs.ns-test.foo - 'foo.ns-shadow-test - 'cljs.import-test - 'cljs.pprint - 'cljs.spec-test - 'cljs.clojure-alias-test) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 549633e6d..cc0862e8c 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -1,5 +1,12 @@ (ns test-runner (:require [cljs.test :refer-macros [run-tests]] + [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 :as core-test] [cljs.reader-test] [cljs.binding-test] @@ -13,33 +20,47 @@ [cljs.top-level] [cljs.reducers-test] [cljs.keyword-test] - [cljs.syntax-quote-test] [cljs.import-test] [cljs.ns-test.foo] + [cljs.syntax-quote-test] [cljs.pprint] + [cljs.pprint-test] + [cljs.spec-test] [cljs.clojure-alias-test] - [cljs.hash-map-test])) + [cljs.hash-map-test] + [cljs.predicates-test])) (set! *print-newline* false) (set-print-fn! js/print) (run-tests + '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.reader-test 'clojure.string-test 'clojure.data-test - 'clojure.walk-test + 'clojure.walk-test 'cljs.letfn-test 'cljs.reducers-test 'cljs.binding-test 'cljs.macro-test 'cljs.top-level 'cljs.keyword-test - 'cljs.syntax-quote-test 'cljs.ns-test 'cljs.ns-test.foo 'foo.ns-shadow-test 'cljs.import-test 'cljs.pprint + 'cljs.spec-test 'cljs.clojure-alias-test - 'cljs.hash-map-test) + 'cljs.hash-map-test + 'cljs.pprint-test + ;; TODO: CLJS-1799 + #_'cljs.predicates-test + 'cljs.syntax-quote-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index f45215eff..e7ad1212f 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -2,7 +2,7 @@ ^{:doc "Builds and runs the ClojureScript compiler test suite in self-host mode, ensuring parity of bootstrapped ClojureScript with JVM based ClojureScript. - + This involves dynamically loading the test suite files at runtime, excercising that they can be compiled by the bootstrapped ClojureScript compiler, and also running the resulting tests."} @@ -19,7 +19,7 @@ "src/main/clojure" "src/test/cljs"]) -(defn init-runtime +(defn init-runtime "Initializes the runtime so that we can use the cljs.user namespace and so that Google Closure is set up to work properly with :optimizations :none." @@ -65,7 +65,7 @@ ;; Facilities for loading Closure deps (defn closure-index - "Builds an index of Closure files. Similar to + "Builds an index of Closure files. Similar to cljs.js-deps/goog-dependencies*" [] (let [paths-to-provides @@ -207,7 +207,7 @@ (def vm (nodejs/require "vm")) -(defn node-eval +(defn node-eval "Evaluates JavaScript in node." [{:keys [name source]}] (if-not js/COMPILED @@ -218,7 +218,7 @@ (def load-fn (make-load-fn src-paths node-read-file)) -(defn eval-form +(defn eval-form "Evaluates a supplied form in a given namespace, calling back with the evaluation result." [st ns form cb] @@ -256,6 +256,7 @@ #_[cljs.ns-test] [clojure.string-test] [clojure.data-test] + [clojure.walk-test] [cljs.macro-test] [cljs.letfn-test] [foo.ns-shadow-test] @@ -265,9 +266,12 @@ [cljs.import-test] [cljs.ns-test.foo] #_[cljs.pprint] + #_[cljs.pprint-test] [cljs.spec-test] [cljs.clojure-alias-test] - [cljs.hash-map-test])) + [cljs.hash-map-test] + [cljs.syntax-quote-test] + [cljs.predicates-test])) (fn [{:keys [value error]}] (if error (prn error) @@ -284,6 +288,7 @@ 'cljs.reader-test 'clojure.string-test 'clojure.data-test + 'clojure.walk-test 'cljs.letfn-test 'cljs.reducers-test 'cljs.binding-test @@ -295,9 +300,13 @@ 'foo.ns-shadow-test 'cljs.import-test #_'cljs.pprint + #_'cljs.pprint-test 'cljs.spec-test 'cljs.clojure-alias-test - 'cljs.hash-map-test) + 'cljs.hash-map-test + 'cljs.syntax-quote-test + ;; TODO: CLJS-1799 + #_'cljs.predicates-test) (fn [{:keys [value error]}] (when error (prn error))))))))) From 647736a7dcb43521e25cc2cd2b96b2d497a3749f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Sep 2016 09:30:05 -0400 Subject: [PATCH 2039/4033] CLJS-1799: Fix cljs.predicates-test --- src/test/cljs/cljs/predicates_test.cljs | 32 +++++++++++++++---------- src/test/cljs/test_runner.cljs | 3 +-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 6de1e1edf..7f1f23c5a 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -1,6 +1,6 @@ (ns cljs.predicates-test - (:require [cljs.test :refer-macros [deftest is]]) - (:import [goog.math Long])) + (:require [cljs.test :as test :refer-macros [deftest is]]) + (:import [goog.math Long Integer])) (def pred-val-table (let [now (js/Date.) @@ -32,20 +32,26 @@ (def int-val-table (let [posint 10e10 negint -10e10 - natl (goog.math.Long.getZero) - posl (goog.math.Long.fromNumber posint) - negl (goog.math.Long.fromNumber negint)] + natl (Long.getZero) + posl (Long.fromNumber posint) + negl (Long.fromNumber negint) + nati (Integer.ZERO) + posi (Integer.fromNumber posint) + negi (Integer.fromNumber negint)] [[identity neg? pos? integer? int? neg-int? pos-int? nat-int?] - [0 false false true false false false false ] - [1 false true true false false false false ] - [-1 true false true false false false false ] - [1.0 false true true false false false false ] - [-1.0 true false true false false false false ] - [posint false true true false false false false ] - [negint true false true false false false false ] + [0 false false true true false false true ] + [1 false true true true false true true ] + [-1 true false true true true false false ] + [1.0 false true true true false true true ] + [-1.0 true false true true true false false ] + [posint false true true true false true true ] + [negint true false true true true false false ] [natl false false false true false false true ] [posl false true false true false true true ] - [negl true false false true true false false ]])) + [negl true false false true true false false ] + [nati false false false true false false true ] + [posi false true false true false true true ] + [negi true false false true true false false ]])) (deftest test-int-preds (let [[preds & rows] int-val-table] diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index cc0862e8c..e1e65faa0 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -61,6 +61,5 @@ 'cljs.clojure-alias-test 'cljs.hash-map-test 'cljs.pprint-test - ;; TODO: CLJS-1799 - #_'cljs.predicates-test + 'cljs.predicates-test 'cljs.syntax-quote-test) From 224e140117d330933fb9b7e993ff26f997f36cbd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Sep 2016 09:38:54 -0400 Subject: [PATCH 2040/4033] clarify integer predicate docstrings, formatting --- src/main/cljs/cljs/core.cljs | 11 ++++++----- src/test/cljs/cljs/predicates_test.cljs | 26 ++++++++++++------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6b5c45503..5c585d2bc 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2126,7 +2126,7 @@ reduces them without incurring seq initialization" (or (fn? f) (satisfies? IFn f))) (defn ^boolean integer? - "Returns true if n is an integer." + "Returns true if n is a JavaScript number with no decimal part." [n] (and (number? n) (not ^boolean (js/isNaN n)) @@ -2134,14 +2134,15 @@ reduces them without incurring seq initialization" (== (js/parseFloat n) (js/parseInt n 10)))) (defn ^boolean int? - "Return true if x is an integer" + "Return true if x satisfies integer? or is an instance of goog.math.Integer + or goog.math.Long." [x] (or (integer? x) (instance? goog.math.Integer x) (instance? goog.math.Long x))) (defn ^boolean pos-int? - "Return true if x is a positive integer" + "Return true if x satisfies int? and is positive." [x] (cond (integer? x) (pos? x) @@ -2157,7 +2158,7 @@ reduces them without incurring seq initialization" :else false)) (defn ^boolean neg-int? - "Return true if x is a negative integer" + "Return true if x satisfies int? and is positive." [x] (cond (integer? x) (neg? x) @@ -2171,7 +2172,7 @@ reduces them without incurring seq initialization" :else false)) (defn ^boolean nat-int? - "Return true if x is a non-negative integer" + "Return true if x satisfies int? and is a natural integer value." [x] (cond (integer? x) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 7f1f23c5a..5feac62da 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -39,19 +39,19 @@ posi (Integer.fromNumber posint) negi (Integer.fromNumber negint)] [[identity neg? pos? integer? int? neg-int? pos-int? nat-int?] - [0 false false true true false false true ] - [1 false true true true false true true ] - [-1 true false true true true false false ] - [1.0 false true true true false true true ] - [-1.0 true false true true true false false ] - [posint false true true true false true true ] - [negint true false true true true false false ] - [natl false false false true false false true ] - [posl false true false true false true true ] - [negl true false false true true false false ] - [nati false false false true false false true ] - [posi false true false true false true true ] - [negi true false false true true false false ]])) + [0 false false true true false false true ] + [1 false true true true false true true ] + [-1 true false true true true false false ] + [1.0 false true true true false true true ] + [-1.0 true false true true true false false ] + [posint false true true true false true true ] + [negint true false true true true false false ] + [natl false false false true false false true ] + [posl false true false true false true true ] + [negl true false false true true false false ] + [nati false false false true false false true ] + [posi false true false true false true true ] + [negi true false false true true false false ]])) (deftest test-int-preds (let [[preds & rows] int-val-table] From 80e41e234a8912731d23f5cb652cb4d63636fbe6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Sep 2016 10:18:16 -0400 Subject: [PATCH 2041/4033] Integer.ZERO is a property not method --- src/test/cljs/cljs/predicates_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 5feac62da..b820bf097 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -35,7 +35,7 @@ natl (Long.getZero) posl (Long.fromNumber posint) negl (Long.fromNumber negint) - nati (Integer.ZERO) + nati Integer.ZERO posi (Integer.fromNumber posint) negi (Integer.fromNumber negint)] [[identity neg? pos? integer? int? neg-int? pos-int? nat-int?] From e1f7d9e93c8687f807dad588c5a69c2aec68797d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Sep 2016 10:59:26 -0400 Subject: [PATCH 2042/4033] remove predicate tests on Date instance --- src/test/cljs/cljs/predicates_test.cljs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index b820bf097..a8bf149a2 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -3,8 +3,7 @@ (:import [goog.math Long Integer])) (def pred-val-table - (let [now (js/Date.) - uuid (uuid nil)] + (let [uuid (uuid nil)] [[identity boolean? indexed? seqable? ident? uuid? inst? simple-ident? qualified-ident? simple-symbol? qualified-symbol? simple-keyword? qualified-keyword?] [0 false false false false false false false false false false false false] [1 false false false false false false false false false false false false] @@ -18,8 +17,7 @@ [::foo false false false true false false false true false false false true] ['foo false false false true false false true nil true nil false false] ['foo/bar false false false true false false false true false true false false] - [uuid false false false false true false false false false false false false] - [now false false false false false true false false false false false false]])) + [uuid false false false false true false false false false false false false]])) (deftest test-preds (let [[preds & rows] pred-val-table] From fb8a0841b393062264ba6dd518218ba259643036 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Sep 2016 11:00:41 -0400 Subject: [PATCH 2043/4033] bring back predicates test ns to self-parity tests --- src/test/self/self_parity/test.cljs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index e7ad1212f..228e44fdb 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -305,8 +305,7 @@ 'cljs.clojure-alias-test 'cljs.hash-map-test 'cljs.syntax-quote-test - ;; TODO: CLJS-1799 - #_'cljs.predicates-test) + 'cljs.predicates-test) (fn [{:keys [value error]}] (when error (prn error))))))))) From 04751f337620279b0228856e4d224ae3d41abe72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 19 Sep 2016 23:14:27 +0100 Subject: [PATCH 2044/4033] CLJS-1782: Self-host: allow namespaces to require their own macros --- src/main/clojure/cljs/analyzer.cljc | 55 ++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5b6209e85..5950473de 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2230,7 +2230,19 @@ (apply merge-with merge m (map (spec-parsers k) (remove #{:reload :reload-all} libs)))) - {} (remove (fn [[r]] (= r :refer-clojure)) args))] + {} (remove (fn [[r]] (= r :refer-clojure)) args)) + ;; patch `require-macros` and `use-macros` in Bootstrap for namespaces + ;; that require their own macros + #?@(:cljs [[require-macros use-macros] + (map (fn [spec-map] + (if (:macros-ns opts) + (let [ns (symbol (subs (str name) 0 (- (count (str name)) 7)))] + (reduce (fn [m [k v]] + (cond-> m + (not (symbol-identical? v ns)) + (assoc k v))) + {} spec-map)) + spec-map)) [require-macros use-macros])])] (set! *cljs-ns* name) (let [ns-info {:name name @@ -2528,10 +2540,17 @@ fexpr (analyze enve f) argc (count args) fn-var? (-> fexpr :info :fn-var) - kw? (= 'cljs.core/Keyword (:tag fexpr))] + kw? (= 'cljs.core/Keyword (:tag fexpr)) + cur-ns (-> env :ns :name)] (when ^boolean fn-var? - (let [{:keys [^boolean variadic max-fixed-arity method-params name]} (:info fexpr)] - (when (and (not (valid-arity? argc method-params)) + (let [{:keys [^boolean variadic max-fixed-arity method-params name ns macro]} (:info fexpr)] + ;; don't warn about invalid arity when 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)))) + (not (valid-arity? argc method-params)) (or (not variadic) (and variadic (< argc max-fixed-arity)))) (warning :fn-arity env {:name name :argc argc})))) @@ -2581,14 +2600,26 @@ lb (get lcls sym)] (if-not (nil? lb) (assoc ret :op :var :info lb) - (if-not (true? (:def-var env)) - (let [sym-meta (meta sym) - info (if-not (contains? sym-meta ::analyzed) - (resolve-existing-var env sym) - (resolve-var env sym))] - (assoc ret :op :var :info info)) - (let [info (resolve-var env sym)] - (assoc ret :op :var :info info))))))) + (let [sym-meta (meta sym) + sym-ns (namespace 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` + ;; to avoid undeclared variable warnings - António Monteiro + #?@(:cljs [sym (if (and sym-ns + (not= sym-ns "cljs.core") + (gstring/endsWith cur-ns "$macros") + (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) + (resolve-existing-var env sym) + (resolve-var env sym))] + (if-not (true? (:def-var env)) + (assoc ret :op :var :info info) + (let [info (resolve-var env sym)] + (assoc ret :op :var :info info)))))))) (defn excluded? #?(:cljs {:tag boolean}) From 3ab6de48b4541900d83e4b41e26080408d742de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 28 Sep 2016 21:32:49 +0100 Subject: [PATCH 2045/4033] CLJS-1796: Measure Google Closure specific optimization time --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5059ce59c..17c4f891b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1267,7 +1267,9 @@ (cons "var CLOSURE_NO_DEPS = true;" sources) sources) ^List inputs (map #(js-source-file (javascript-name %) %) sources) - ^Result result (.compile closure-compiler externs inputs compiler-options)] + ^Result result (util/measure (:compiler-stats opts) + "Optimizing with Google Closure Compiler" + (.compile closure-compiler externs inputs compiler-options))] (if (.success result) ;; compiler.getSourceMap().reset() (let [source (.toSource closure-compiler)] From b5147bfeb1e8034e93014e35bb27c9fb4d9c10de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 29 Sep 2016 20:28:46 +0100 Subject: [PATCH 2046/4033] CLJS-1346: Support require outside of ns --- src/main/cljs/cljs/core.cljs | 2 +- src/main/cljs/cljs/js.cljs | 1 + src/main/cljs/cljs/nodejs.cljs | 3 +- src/main/clojure/cljs/analyzer.cljc | 189 ++++++++++++++++++--- src/main/clojure/cljs/analyzer/macros.clj | 5 +- src/main/clojure/cljs/compiler.cljc | 40 ++++- src/main/clojure/cljs/core.cljc | 28 ++- src/main/clojure/cljs/repl.cljc | 28 ++- src/test/cljs/cljs/clojure_alias_test.cljs | 1 + src/test/clojure/cljs/analyzer_tests.clj | 100 ++++++++++- 10 files changed, 344 insertions(+), 53 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5c585d2bc..0e18216af 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10361,7 +10361,7 @@ reduces them without incurring seq initialization" [x] (contains? '#{if def fn* do let* loop* letfn* throw try catch finally - recur new set! ns deftype* defrecord* . js* & quote var} + recur new set! ns deftype* defrecord* . js* & quote var ns*} x)) (defn test diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ed2711b2a..af4336fc4 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -7,6 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.js + (:refer-clojure :exclude [require]) (:require-macros [cljs.js :refer [dump-core]] [cljs.env.macros :as env]) (:require [clojure.string :as string] diff --git a/src/main/cljs/cljs/nodejs.cljs b/src/main/cljs/cljs/nodejs.cljs index e467f2997..356023609 100644 --- a/src/main/cljs/cljs/nodejs.cljs +++ b/src/main/cljs/cljs/nodejs.cljs @@ -9,7 +9,8 @@ ; Projects compiled with :target :nodejs can 'require' this namespace ; to get the nodejs globals loaded into cljs.nodejs and get ; ClojureScript's 'print' set up correctly. -(ns cljs.nodejs) +(ns cljs.nodejs + (:refer-clojure :exclude [require])) ; Define namespaced references to Node's externed globals: (def require (js* "require")) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5950473de..f1ca5cd9e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -12,7 +12,7 @@ #?(:cljs (:require-macros [cljs.analyzer.macros :refer [no-warn wrapping-errors - disallowing-recur allowing-redef]] + disallowing-recur allowing-redef disallowing-ns*]] [cljs.env.macros :refer [ensure]])) #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] [clojure.java.io :as io] @@ -36,6 +36,8 @@ [java.util.regex Pattern] [java.net URL] [java.lang Throwable] + [java.security MessageDigest] + [javax.xml.bind DatatypeConverter] [clojure.lang Namespace Var LazySeq ArityException] [cljs.tagged_literals JSValue]))) @@ -878,11 +880,12 @@ (declare analyze analyze-symbol analyze-seq) (def specials '#{if def fn* do let* loop* letfn* throw try recur new set! - ns deftype* defrecord* . js* & quote case* var}) + ns deftype* defrecord* . js* & quote case* var ns*}) (def ^:dynamic *recur-frames* nil) (def ^:dynamic *loop-lets* ()) (def ^:dynamic *allow-redef* false) +(def ^:dynamic *allow-ns* true) #?(:clj (defmacro disallowing-recur [& body] @@ -892,6 +895,10 @@ (defmacro allowing-redef [& body] `(binding [*allow-redef* true] ~@body))) +#?(:clj + (defmacro disallowing-ns* [& body] + `(binding [*allow-ns* false] ~@body))) + ;; TODO: move this logic out - David (defn analyze-keyword [env sym] @@ -1213,7 +1220,8 @@ (when dynamic {:dynamic true}) (source-info var-name env))) (disallowing-recur - (analyze (assoc env :context :expr) (:init args) sym))) + (disallowing-ns* + (analyze (assoc env :context :expr) (:init args) sym)))) fn-var? (and init-expr (= (:op init-expr) :fn)) tag (if fn-var? (or (:ret-tag init-expr) tag) @@ -1404,7 +1412,7 @@ menv (merge menv {:protocol-impl proto-impl :protocol-inline proto-inline}) - methods (map #(analyze-fn-method menv locals % type) meths) + methods (map #(disallowing-ns* (analyze-fn-method menv locals % type)) meths) mfa (apply max (map :max-fixed-arity methods)) variadic (boolean (some :variadic methods)) locals (if-not (nil? name) @@ -1418,7 +1426,7 @@ methods (if-not (nil? name) ;; a second pass with knowledge of our function-ness/arity ;; lets us optimize self calls - (analyze-fn-methods-pass2 menv locals type meths) + (disallowing-ns* (analyze-fn-methods-pass2 menv locals type meths)) methods) form (vary-meta form dissoc ::protocol-impl ::protocol-inline ::type) js-doc (when (true? variadic) @@ -2076,10 +2084,7 @@ (util/ns->source sym'))))))) #?(:clj - (defn rewrite-cljs-aliases - "Alias non-existing clojure.* namespaces to existing cljs.* namespaces if - possible." - [args] + (defn process-rewrite-form [[k & specs :as form]] (letfn [(process-spec [maybe-spec] (let [[lib & xs] (if (sequential? maybe-spec) maybe-spec @@ -2088,12 +2093,25 @@ (let [lib' (clj-ns->cljs-ns lib) spec (cons lib' xs)] (into (if xs [spec] []) [(list lib' :as lib)])) - [maybe-spec]))) - (process-form [[k & specs :as form]] - (if (#{:use :require} k) - (cons k (mapcat process-spec specs)) - form))] - (map process-form args)))) + [maybe-spec])))] + (if (#{:use :require} k) + (cons k (mapcat process-spec specs)) + form)))) + +#?(:clj + (defn rewrite-cljs-aliases + "Alias non-existing clojure.* namespaces to existing cljs.* namespaces if + possible." + [args] + (map process-rewrite-form args))) + +(defn canonicalize-specs [specs] + (letfn [(canonicalize [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (as-> (second quoted-spec-or-kw) spec + (if (vector? spec) spec [spec]))))] + (map canonicalize specs))) (defn desugar-ns-specs "Given an original set of ns specs desugar :include-macros and :refer-macros @@ -2166,6 +2184,8 @@ (defmethod parse 'ns [_ env [_ name & args :as form] _ opts] + (when-not *allow-ns* + (throw (error env "Namespace declarations must be at the top-level."))) (when-not (symbol? name) (throw (error env "Namespaces must be named by a symbol."))) (let [name (cond-> name (:macros-ns opts) macro-ns-name)] @@ -2285,6 +2305,93 @@ (update-in [:requires] (fn [m] (with-meta m {(@reload :require) true}))))))))) +(defmethod parse 'ns* + [_ env [_ quoted-specs :as form] _ opts] + (when-let [not-quoted (->> (remove keyword? quoted-specs) + (filter #(not= 'quote (first %)) ) + first)] + (throw (error env (str "Arguments to " (name (first quoted-specs)) + " must be quoted. Offending spec: " not-quoted)))) + (when-not *allow-ns* + (throw (error env "Namespace declarations must be at the top-level."))) + (let [specs (canonicalize-specs quoted-specs) + name 'cljs.user + args (desugar-ns-specs + #?(:clj (list (process-rewrite-form + specs)) + :cljs (list specs))) + {excludes :excludes core-renames :renames} (parse-ns-excludes env args) + core-renames (reduce (fn [m [original renamed]] + (assoc m renamed (symbol "cljs.core" (str original)))) + {} core-renames) + deps (atom #{}) + aliases (atom {:fns {} :macros {}}) + spec-parsers {:require (partial parse-require-spec env false deps aliases) + :require-macros (partial parse-require-spec env true deps aliases) + :use (comp (partial parse-require-spec env false deps aliases) + (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)} + reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) + reloads (atom {}) + {uses :use requires :require renames :rename + use-macros :use-macros require-macros :require-macros + rename-macros :rename-macros imports :import :as params} + (reduce + (fn [m [k & libs]] + ;; check for spec type reloads + (when-not (= :import k) + (when (some #{:reload} libs) + (swap! reload assoc k :reload)) + (when (some #{:reload-all} libs) + (swap! reload assoc k :reload-all))) + ;; check for individual ns reloads from REPL interactions + (when-let [xs (seq (filter #(-> % meta :reload) libs))] + (swap! reloads assoc k + (zipmap (map first xs) (map #(-> % meta :reload) xs)))) + (apply merge-with merge m + (map (spec-parsers k) + (remove #{:reload :reload-all} libs)))) + {} (remove (fn [[r]] (= r :refer-clojure)) args))] + (set! *cljs-ns* 'cljs.user) + (let [require-info + {:name name + :excludes excludes + :use-macros use-macros + :require-macros require-macros + :rename-macros rename-macros + :uses uses + :requires requires + :renames (merge renames core-renames) + :imports imports} + ns-info + (let [ns-info' (get-in @env/*compiler* [::namespaces name])] + (if (pos? (count ns-info')) + (let [merge-keys + [:use-macros :require-macros :rename-macros + :uses :requires :renames :imports]] + (merge + ns-info' + (merge-with merge + (select-keys ns-info' merge-keys) + (select-keys require-info merge-keys)))) + require-info))] + (swap! env/*compiler* update-in [::namespaces name] merge ns-info) + (merge {:op :ns* + :env env + :form form + :deps @deps + :reload @reload + :reloads @reloads} + (cond-> require-info + (@reload :use) + (update-in [:uses] + (fn [m] (with-meta m {(@reload :use) true}))) + (@reload :require) + (update-in [:requires] + (fn [m] (with-meta m {(@reload :require) true})))))))) + (defn parse-type [op env [_ tsym fields pmasks body :as form]] (let [t (:name (resolve-var (dissoc env :locals) tsym)) @@ -2853,7 +2960,7 @@ #?(:clj (defn ns-side-effects [env {:keys [op] :as ast} opts] - (if (= :ns op) + (if (#{:ns :ns*} op) (let [{:keys [name deps uses require-macros use-macros reload reloads]} ast] (when (and *analyze-deps* (seq deps)) (analyze-deps name deps env (dissoc opts :macros-ns))) @@ -3035,6 +3142,20 @@ [(forms-seq*) rdr] (forms-seq*)))))) +#?(:clj + (defn gen-user-ns [src] + (let [name (str src) + name (.substring name (inc (.lastIndexOf name "/")) (.lastIndexOf name ".")) + digest (MessageDigest/getInstance "SHA-1")] + (.reset digest) + (.update digest (.getBytes ^String name "utf8")) + (symbol + (str + "cljs.user$$gen_ns$$_" name + (->> (DatatypeConverter/printHexBinary (.digest digest)) + (take 7) + (apply str))))))) + #?(:clj (defn parse-ns "Helper for parsing only the essential namespace information from a @@ -3075,11 +3196,25 @@ (try (loop [forms (if rdr (forms-seq* rdr (source-path src)) - src)] + src) + ret (merge + {:ns (gen-user-ns src) + :provides [(gen-user-ns src)] + :file dest + :source-file (when rdr src) + :source-forms (when-not rdr src) + :macros-ns (:macros-ns opts) + :requires (cond-> #{'cljs.core} + (get-in @env/*compiler* [:options :emit-constants]) + (conj 'constants-table))} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))}))] (if (seq forms) (let [env (empty-env) ast (no-warn (analyze env (first forms) nil opts))] - (if (= :ns (:op ast)) + (cond + (= :ns (:op ast)) (let [ns-name (:name ast) ns-name (if (and (= 'cljs.core ns-name) (= "cljc" (util/ext src))) @@ -3103,8 +3238,14 @@ (when (and dest (.exists ^File dest)) {:lines (with-open [reader (io/reader dest)] (-> reader line-seq count))}))) - (recur (rest forms)))) - (throw (AssertionError. (str "No ns form found in " src))))) + + (= :ns* (:op ast)) + (let [deps (merge (:uses ast) (:requires ast))] + (recur (rest forms) + (update-in ret [:requires] into (set (vals deps))))) + + :else ret)) + ret)) (finally (when rdr (.close ^Reader rdr))))))] @@ -3225,8 +3366,14 @@ (let [form (first forms) env (assoc env :ns (get-namespace *cljs-ns*)) ast (analyze env form nil opts)] - (if (= (:op ast) :ns) + (cond + (= (:op ast) :ns) (recur (:name ast) (next forms)) + + (and (nil? ns) (= (:op ast) :ns*)) + (recur (gen-user-ns res) (next forms)) + + :else (recur ns (next forms)))) ns)))] (when (and cache (true? (:cache-analysis opts))) diff --git a/src/main/clojure/cljs/analyzer/macros.clj b/src/main/clojure/cljs/analyzer/macros.clj index 3bab964ef..bc3341438 100644 --- a/src/main/clojure/cljs/analyzer/macros.clj +++ b/src/main/clojure/cljs/analyzer/macros.clj @@ -51,4 +51,7 @@ (defmacro allowing-redef [& body] `(binding [cljs.analyzer/*allow-redef* true] - ~@body)) \ No newline at end of file + ~@body)) + +(defmacro disallowing-ns* [& body] + `(binding [cljs.analyzer/*allow-ns* false] ~@body)) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 37b67cf01..65ebf4224 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1059,6 +1059,11 @@ (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) +(defmethod emit* :ns* + [{:keys [name requires uses require-macros reloads env]}] + (load-libs requires nil (:require reloads)) + (load-libs uses requires (:use reloads))) + (defmethod emit* :ns [{:keys [name requires uses require-macros reloads env]}] (emitln "goog.provide('" (munge name) "');") @@ -1244,21 +1249,40 @@ (emitln (compiled-by-string opts)) (with-open [rdr (io/reader src)] (let [env (ana/empty-env)] - (loop [forms (ana/forms-seq* rdr (util/path src)) - ns-name nil - deps nil] + (loop [forms (ana/forms-seq* rdr (util/path src)) + ns-name nil + deps nil] (if (seq forms) (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) - ast (ana/analyze env (first forms) nil opts)] - (emit ast) - (if (= :ns (:op ast)) + {:keys [op] :as ast} (ana/analyze env (first forms) nil opts)] + (cond + (= op :ns) (let [ns-name (:name ast) ns-name (if (and (= 'cljs.core ns-name) - (= "cljc" ext)) + (= "cljc" ext)) 'cljs.core$macros ns-name)] + (emit ast) (recur (rest forms) ns-name (merge (:uses ast) (:requires ast)))) - (recur (rest forms) ns-name deps))) + + (= :ns* (:op ast)) + (let [ns-emitted? (some? ns-name) + ns-name (ana/gen-user-ns src)] + (if-not ns-emitted? + (emit (assoc ast :name ns-name :op :ns)) + (emit ast)) + (recur (rest forms) ns-name (merge deps (:uses ast) (:requires ast)))) + + :else + (let [ns-emitted? (some? ns-name) + ns-name (if-not ns-emitted? + (ana/gen-user-ns src) + ns-name)] + (when-not ns-emitted? + (emit {:op :ns + :name ns-name})) + (emit ast) + (recur (rest forms) ns-name deps)))) (let [sm-data (when *source-map-data* @*source-map-data*) ret (merge {:ns (or ns-name 'cljs.user) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index cd9b9bf55..f448daed5 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -34,6 +34,8 @@ cond-> cond->> as-> some-> some->> + require use refer-clojure + if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand #?@(:cljs [alias coercive-not coercive-not= coercive-= coercive-boolean truth_ js-arguments js-delete js-in js-debugger exists? divide js-mod @@ -41,7 +43,7 @@ defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? specify copy-arguments goog-define js-comment js-inline-comment - unsafe-cast])]) + unsafe-cast require-macros use-macros])]) #?(:cljs (:require-macros [cljs.core :as core] [cljs.support :refer [assert-args]])) (:require clojure.walk @@ -2726,6 +2728,30 @@ [x & forms] `(do ~@forms)) +(core/defmacro require + [& specs] + `(~'ns* ~(cons :require specs))) + +(core/defmacro require-macros + [& specs] + `(~'ns* ~(cons :require-macros specs))) + +(core/defmacro use + [& specs] + `(~'ns* ~(cons :use specs))) + +(core/defmacro use-macros + [& specs] + `(~'ns* ~(cons :use-macros specs))) + +(core/defmacro import + [& specs] + `(~'ns* ~(cons :import specs))) + +(core/defmacro refer-clojure + [& specs] + `(~'ns* ~(cons :refer-clojure specs))) + ;; INTERNAL - do not use, only for Node.js (core/defmacro load-file* [f] `(. js/goog (~'nodeGlobalRequire ~f))) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 965dd46a7..d6e211785 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -579,29 +579,21 @@ ((or (:wrap opts) wrap-fn) form) opts))) -(defn canonicalize-specs [specs] - (letfn [(canonicalize [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (as-> (second quoted-spec-or-kw) spec - (if (vector? spec) spec [spec]))))] - (map canonicalize specs))) - (defn decorate-specs [specs] (if-let [k (some #{:reload :reload-all} specs)] (->> specs (remove #{k}) (map #(vary-meta % assoc :reload k))) specs)) (comment - (canonicalize-specs + (ana/canonicalize-specs '['foo.bar '[bar.core :as bar]]) - (canonicalize-specs + (ana/canonicalize-specs '['foo.bar '[bar.core :as bar] :reload]) (map meta (decorate-specs - (canonicalize-specs + (ana/canonicalize-specs '['foo.bar '[bar.core :as bar] :reload]))) ) @@ -687,7 +679,7 @@ (evaluate-form repl-env env "" (with-meta `(~'ns ~target-ns - (:require ~@(-> specs canonicalize-specs decorate-specs))) + (:require ~@(-> specs ana/canonicalize-specs decorate-specs))) {:merge true :line 1 :column 1}) identity opts) (when is-self-require? @@ -700,7 +692,7 @@ (evaluate-form repl-env env "" (with-meta `(~'ns ~ana/*cljs-ns* - (:require-macros ~@(-> specs canonicalize-specs decorate-specs))) + (:require-macros ~@(-> specs ana/canonicalize-specs decorate-specs))) {:merge true :line 1 :column 1}) identity opts))) 'use @@ -716,7 +708,7 @@ (evaluate-form repl-env env "" (with-meta `(~'ns ~target-ns - (:use ~@(-> specs canonicalize-specs decorate-specs))) + (:use ~@(-> specs ana/canonicalize-specs decorate-specs))) {:merge true :line 1 :column 1}) identity opts) (when is-self-require? @@ -729,7 +721,7 @@ (evaluate-form repl-env env "" (with-meta `(~'ns ~ana/*cljs-ns* - (:use-macros ~@(-> specs canonicalize-specs decorate-specs))) + (:use-macros ~@(-> specs ana/canonicalize-specs decorate-specs))) {:merge true :line 1 :column 1}) identity opts))) 'import @@ -1212,12 +1204,12 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) use {:arglists ([& args]) :doc "Like require, but referring vars specified by the mandatory :only option. - + Example: - + The following would load the library clojure.set while referring the intersection var. - + (use '[clojure.set :only [intersection]])"} use-macros {:arglists ([& args]) :doc "Similar to the use REPL special function but diff --git a/src/test/cljs/cljs/clojure_alias_test.cljs b/src/test/cljs/cljs/clojure_alias_test.cljs index e2381f78d..957c50afe 100644 --- a/src/test/cljs/cljs/clojure_alias_test.cljs +++ b/src/test/cljs/cljs/clojure_alias_test.cljs @@ -1,5 +1,6 @@ (ns cljs.clojure-alias-test "Tests requiring via `clojure.*` instead of `cljs.*`" + (:refer-clojure :exclude [use-macros]) (:require [clojure.test :refer [deftest is] :rename {is is?}] [clojure.spec :as s :refer [spec? spec] :rename {spec foo}])) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 63a4bb421..9f3b8e0d8 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -448,7 +448,6 @@ '(ns foo.core (:require [clojure.set :rename {intersection foo}])))))))) - (deftest test-cljs-1274 (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] (binding [a/*cljs-ns* a/*cljs-ns*] @@ -470,4 +469,101 @@ '(fn [foo] (let [x js/foo] (println x))))) - (is (.startsWith (first @ws) "js/foo is shadowed by a local")))) \ No newline at end of file + (is (.startsWith (first @ws) "js/foo is shadowed by a local")))) + +(deftest test-canonicalize-specs + (is (= (a/canonicalize-specs '((quote [clojure.set :as set]))) + '([clojure.set :as set]))) + (is (= (a/canonicalize-specs '(:exclude (quote [map mapv]))) + '(:exclude [map mapv]))) + (is (= (a/canonicalize-specs '(:require (quote [clojure.set :as set]))) + '(:require [clojure.set :as set])))) + +(deftest test-cljs-1346 + (testing "`ns*` special form conformance" + (let [test-env (a/empty-env)] + (is (= (-> (a/parse-ns '((require '[clojure.set :as set]))) :requires) + '#{cljs.core clojure.set}))) + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* nil] + (let [test-env (a/empty-env)] + (is (= (-> (a/analyze test-env '(require '[clojure.set :as set])) :requires vals set) + '#{clojure.set}))) + (let [test-env (a/empty-env)] + (is (= (-> (a/analyze test-env '(require '[clojure.set :as set :refer [union intersection]])) :uses keys set) + '#{union intersection}))) + (let [test-env (a/empty-env)] + (is (= (-> (a/analyze test-env '(require '[clojure.set :as set] + '[clojure.string :as str])) + :requires vals set) + '#{clojure.set clojure.string}))) + (let [test-env (a/empty-env)] + (is (= (-> (a/analyze test-env '(require-macros '[cljs.test :as test])) :require-macros vals set) + '#{cljs.test}))) + (let [test-env (a/empty-env) + parsed (a/analyze test-env '(require-macros '[cljs.test :as test :refer [deftest is]]))] + (is (= (-> parsed :require-macros vals set) + '#{cljs.test})) + (is (= (-> parsed :use-macros keys set) + '#{is deftest}))) + (let [test-env (a/empty-env) + parsed (a/analyze test-env '(require '[cljs.test :as test :refer-macros [deftest is]]))] + (is (= (-> parsed :requires vals set) + '#{cljs.test})) + (is (= (-> parsed :require-macros vals set) + '#{cljs.test})) + (is (= (-> parsed :use-macros keys set) + '#{is deftest}))) + (let [test-env (a/empty-env) + parsed (a/analyze test-env '(use '[clojure.set :only [intersection]]))] + (is (= (-> parsed :uses keys set) + '#{intersection})) + (is (= (-> parsed :requires) + '{clojure.set clojure.set}))) + (let [test-env (a/empty-env) + parsed (a/analyze test-env '(use-macros '[cljs.test :only [deftest is]]))] + (is (= (-> parsed :use-macros keys set) + '#{deftest is})) + (is (= (-> parsed :require-macros) + '{cljs.test cljs.test})) + (is (nil? (-> parsed :requires)))) + (let [test-env (a/empty-env) + parsed (a/analyze test-env '(import '[goog.math Long Integer]))] + (is (= (-> parsed :imports) + (-> parsed :requires) + '{Long goog.math.Long + Integer goog.math.Integer}))) + (let [test-env (a/empty-env) + parsed (a/analyze test-env '(refer-clojure :exclude '[map mapv]))] + (is (= (-> parsed :excludes) + '#{map mapv}))))) + (testing "arguments to require should be quoted" + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* nil] + (is (thrown-with-msg? Exception #"Arguments to require must be quoted" + (a/analyze test-env + '(require [clojure.set :as set])))))) + (testing "`:ns` and `:ns*` should throw if not `:top-level`" + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* nil] + (are [analyzed] (thrown-with-msg? Exception + #"Namespace declarations must be at the top-level." + analyzed) + (a/analyze test-env + '(def foo + (ns foo.core + (:require [clojure.set :as set])))) + (a/analyze test-env + '(def foo + (require '[clojure.set :as set]))) + (a/analyze test-env + '(fn [] + (ns foo.core + (:require [clojure.set :as set])))) + (a/analyze test-env + '(fn [] (require '[clojure.set :as set]))) + (a/analyze test-env + '(map #(ns foo.core + (:require [clojure.set :as set])) [1 2])) + (a/analyze test-env + '(map #(require '[clojure.set :as set]) [1 2])))))) From c9c1229b5415b5176f8520e9ca2ba0879158d188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 1 Oct 2016 13:34:07 +0100 Subject: [PATCH 2047/4033] CLJS-1803: Use new require capability in REPLs --- src/main/clojure/cljs/analyzer.cljc | 4 +- src/main/clojure/cljs/repl.cljc | 115 +++------------------------ src/main/clojure/cljs/repl/rhino.clj | 11 ++- 3 files changed, 20 insertions(+), 110 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f1ca5cd9e..5e1a1d3c7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2315,7 +2315,7 @@ (when-not *allow-ns* (throw (error env "Namespace declarations must be at the top-level."))) (let [specs (canonicalize-specs quoted-specs) - name 'cljs.user + name (-> env :ns :name) args (desugar-ns-specs #?(:clj (list (process-rewrite-form specs)) @@ -2354,7 +2354,7 @@ (map (spec-parsers k) (remove #{:reload :reload-all} libs)))) {} (remove (fn [[r]] (= r :refer-clojure)) args))] - (set! *cljs-ns* 'cljs.user) + (set! *cljs-ns* name) (let [require-info {:name name :excludes excludes diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index d6e211785..675a00f08 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -452,6 +452,8 @@ {:source-type :fragment :source-form form}) def-emits-var (:def-emits-var opts) + ast (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) + (wrap form) nil opts) wrap-js ;; TODO: check opts as well - David (if (:source-map repl-env) @@ -459,9 +461,7 @@ (atom {:source-map (sorted-map) :gen-col 0 :gen-line 0})] - (let [js (comp/emit-str - (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) - (wrap form) nil opts)) + (let [js (comp/emit-str ast) t (System/currentTimeMillis)] (str js "\n//# sourceURL=repl-" t ".js" @@ -478,12 +478,10 @@ ;; handle strings / primitives without metadata (with-out-str (pr form)))]}) "UTF-8"))))) - (comp/emit-str - (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) - (wrap form) nil opts)))] + (comp/emit-str ast))] ;; NOTE: means macros which expand to ns aren't supported for now ;; when eval'ing individual forms at the REPL - David - (when (and (sequential? form) (= 'ns (first form))) + (when (#{:ns :ns*} (:op ast)) (let [ast (ana/no-warn (ana/analyze env form nil opts))] (load-dependencies repl-env (into (vals (:requires ast)) @@ -548,7 +546,11 @@ (defn- wrap-fn [form] (cond - (and (seq? form) (= 'ns (first form))) identity + (and (seq? form) + (#{'ns 'require 'require-macros + 'use 'use-macros 'import 'refer-clojure} (first form))) + identity + ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) :else (fn [x] @@ -612,17 +614,6 @@ ;; form - complete form entered at the repl ;; opts - REPL options, essentially augmented cljs.closure/build options -(defn self-require? [specs] - (some - (fn [quoted-spec-or-kw] - (and (not (keyword? quoted-spec-or-kw)) - (let [spec (second quoted-spec-or-kw) - ns (if (sequential? spec) - (first spec) - spec)] - (= ns ana/*cljs-ns*)))) - specs)) - (defn- wrap-self "Takes a self-ish fn and returns it wrapped with exception handling. Compiler state is restored if self-ish fn fails." @@ -666,92 +657,6 @@ (wrap-special-fns wrap-self {'in-ns in-ns-fn 'clojure.core/in-ns in-ns-fn - 'require - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (let [is-self-require? (self-require? specs) - [target-ns restore-ns] - (if-not is-self-require? - [ana/*cljs-ns* nil] - ['cljs.user ana/*cljs-ns*])] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~target-ns - (:require ~@(-> specs ana/canonicalize-specs decorate-specs))) - {:merge true :line 1 :column 1}) - identity opts) - (when is-self-require? - (set! ana/*cljs-ns* restore-ns))))) - 'require-macros - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:require-macros ~@(-> specs ana/canonicalize-specs decorate-specs))) - {:merge true :line 1 :column 1}) - identity opts))) - 'use - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (let [is-self-require? (self-require? specs) - [target-ns restore-ns] - (if-not is-self-require? - [ana/*cljs-ns* nil] - ['cljs.user ana/*cljs-ns*])] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~target-ns - (:use ~@(-> specs ana/canonicalize-specs decorate-specs))) - {:merge true :line 1 :column 1}) - identity opts) - (when is-self-require? - (set! ana/*cljs-ns* restore-ns))))) - 'use-macros - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:use-macros ~@(-> specs ana/canonicalize-specs decorate-specs))) - {:merge true :line 1 :column 1}) - identity opts))) - 'import - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:import - ~@(map - (fn [quoted-spec-or-kw] - (if (keyword? quoted-spec-or-kw) - quoted-spec-or-kw - (second quoted-spec-or-kw))) - specs))) - {:merge true :line 1 :column 1}) - identity opts))) - 'refer-clojure - (fn self - ([repl-env env form] - (self repl-env env form nil)) - ([repl-env env [_ & specs :as form] opts] - (evaluate-form repl-env env "" - (with-meta - `(~'ns ~ana/*cljs-ns* - (:refer-clojure ~@specs)) - {:merge true :line 1 :column 1}) - identity opts))) 'load-file load-file-fn 'clojure.core/load-file load-file-fn 'load-namespace diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj index ad9673772..a5da15001 100644 --- a/src/main/clojure/cljs/repl/rhino.clj +++ b/src/main/clojure/cljs/repl/rhino.clj @@ -39,7 +39,7 @@ String (-eval [this {:keys [cx scope]} filename line] (.evaluateString cx scope this filename line nil)) - + Reader (-eval [this {:keys [cx scope]} filename line] (.evaluateReader cx scope this filename line nil))) @@ -159,8 +159,13 @@ ;; https://groups.google.com/d/msg/mozilla.dev.tech.js-engine.rhino/inMyVKhPq6M/cY39hX20_z8J (defn wrap-fn [form] (cond - (and (seq? form) (= 'ns (first form))) identity + (and (seq? form) + (#{'ns 'require 'require-macros + 'use 'use-macros 'import 'refer-clojure} (first form))) + identity + ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) + :else (fn [x] `(cljs.core.pr-str @@ -254,5 +259,5 @@ (load-namespace 'goog.date.Date) (goog.date.Date.) - + ) From ca80f0ce60085b223e63879d8c93b765c6878f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 1 Oct 2016 11:53:32 +0100 Subject: [PATCH 2048/4033] CLJS-1802: Generated namespaces should be of the form `cljs.user.fileXXXX` --- src/main/clojure/cljs/analyzer.cljc | 30 +++++++++++------------- src/main/clojure/cljs/closure.clj | 15 ++++-------- src/main/clojure/cljs/util.cljc | 10 +++++++- src/test/clojure/cljs/analyzer_tests.clj | 18 +++++++++++++- 4 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5e1a1d3c7..cad4f89ef 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -36,8 +36,6 @@ [java.util.regex Pattern] [java.net URL] [java.lang Throwable] - [java.security MessageDigest] - [javax.xml.bind DatatypeConverter] [clojure.lang Namespace Var LazySeq ArityException] [cljs.tagged_literals JSValue]))) @@ -3144,17 +3142,14 @@ #?(:clj (defn gen-user-ns [src] - (let [name (str src) - name (.substring name (inc (.lastIndexOf name "/")) (.lastIndexOf name ".")) - digest (MessageDigest/getInstance "SHA-1")] - (.reset digest) - (.update digest (.getBytes ^String name "utf8")) + (let [full-name (str src) + name (.substring full-name + (inc (.lastIndexOf full-name "/")) + (.lastIndexOf full-name "."))] (symbol - (str - "cljs.user$$gen_ns$$_" name - (->> (DatatypeConverter/printHexBinary (.digest digest)) - (take 7) - (apply str))))))) + (apply str + "cljs.user." name + (take 7 (util/content-sha full-name))))))) #?(:clj (defn parse-ns @@ -3198,9 +3193,7 @@ (forms-seq* rdr (source-path src)) src) ret (merge - {:ns (gen-user-ns src) - :provides [(gen-user-ns src)] - :file dest + {:file dest :source-file (when rdr src) :source-forms (when-not rdr src) :macros-ns (:macros-ns opts) @@ -3242,7 +3235,12 @@ (= :ns* (:op ast)) (let [deps (merge (:uses ast) (:requires ast))] (recur (rest forms) - (update-in ret [:requires] into (set (vals deps))))) + (cond-> (update-in ret [:requires] into (set (vals deps))) + ;; we need to defer generating the user namespace + ;; until we actually need or it will break when + ;; `src` is a sequence of forms - António Monteiro + (not (:ns ret)) + (assoc :ns (gen-user-ns src) :provides [(gen-user-ns src)])))) :else ret)) ret)) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 17c4f891b..bd674e12a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -60,8 +60,6 @@ ES6ModuleLoader AbstractCompiler TransformAMDToCJSModule ProcessEs6Modules CompilerInput] [com.google.javascript.rhino Node] - [java.security MessageDigest] - [javax.xml.bind DatatypeConverter] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult] [java.nio.charset Charset StandardCharsets] @@ -1506,14 +1504,11 @@ :else (path-from-jarfile url)) (string? js) - (let [digest (MessageDigest/getInstance "SHA-1")] - (.reset digest) - (.update digest (.getBytes ^String js "utf8")) - (str - (->> (DatatypeConverter/printHexBinary (.digest digest)) - (take 7) - (apply str)) - ".js")) + (str + (->> (util/content-sha js) + (take 7) + (apply str)) + ".js") :else (str (random-string 5) ".js"))))) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index dffe609f2..d184494b1 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -13,7 +13,9 @@ [clojure.set :as set] [clojure.edn :as edn]) (:import [java.io File] - [java.net URL])) + [java.net URL] + [java.security MessageDigest] + [javax.xml.bind DatatypeConverter])) ;; next line is auto-generated by the build-script - Do not edit! (def ^:dynamic *clojurescript-version*) @@ -261,3 +263,9 @@ (cons x (step (rest s) (conj seen v))))))) xs seen)))] (step coll #{})))) + +(defn content-sha [^String s] + (let [digest (MessageDigest/getInstance "SHA-1")] + (.reset digest) + (.update digest (.getBytes s "utf8")) + (DatatypeConverter/printHexBinary (.digest digest)))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 9f3b8e0d8..4f83630bc 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -3,7 +3,8 @@ [cljs.analyzer :as a] [cljs.env :as e] [cljs.env :as env] - [cljs.analyzer.api :as ana-api]) + [cljs.analyzer.api :as ana-api] + [cljs.util :as util]) (:use clojure.test)) (defn collecting-warning-handler [state] @@ -567,3 +568,18 @@ (:require [clojure.set :as set])) [1 2])) (a/analyze test-env '(map #(require '[clojure.set :as set]) [1 2])))))) + +(deftest test-gen-user-ns + ;; note: can't use `with-redefs` because direct-linking is enabled + (let [s "src/cljs/foo.cljs" + sha (util/content-sha s)] + (is (= (a/gen-user-ns s) (symbol (str "cljs.user.foo" (apply str (take 7 sha))))))) + (let [a "src/cljs/foo.cljs" + b "src/cljs/foo.cljc"] + ;; namespaces should have different names because the filename hash will be different + (is (not= (a/gen-user-ns a) (a/gen-user-ns b))) + ;; specifically, only the hashes should differ + (let [nsa (str (a/gen-user-ns a)) + nsb (str (a/gen-user-ns b))] + (is (not= (.substring nsa (- (count nsa) 7)) (.substring nsb (- (count nsb) 7)))) + (is (= (.substring nsa 0 (- (count nsa) 7)) (.substring nsb 0 (- (count nsb) 7))))))) From 2f2b7f253cd2bc5156bf74caeb1145823570470b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 1 Oct 2016 16:35:17 +0100 Subject: [PATCH 2049/4033] CLJS-1804: Self-host: process namespace side-effects for new require without NS --- src/main/cljs/cljs/js.cljs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index af4336fc4..ca1da3699 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -460,7 +460,7 @@ ([load bound-vars ana-env {:keys [op] :as ast} opts cb] (when (:verbose opts) (debug-prn "Namespace side effects for" (:name ast))) - (if (= :ns op) + (if (#{:ns :ns*} op) (letfn [(check-uses-and-load-macros [res rewritten-ast] (let [env (:*compiler* bound-vars) {:keys [uses requires require-macros use-macros reload reloads]} rewritten-ast] @@ -564,7 +564,7 @@ (if (:error res) (cb res) (let [ast (:value res)] - (if (= :ns (:op ast)) + (if (#{:ns :ns*} (:op ast)) (ns-side-effects bound-vars aenv ast opts (fn [res] (if (:error res) @@ -643,7 +643,7 @@ (if (:error res) (cb res) (let [ast (:value res)] - (if (= :ns (:op ast)) + (if (#{:ns :ns*} (:op ast)) (ns-side-effects true bound-vars aenv ast opts (fn [res] (if (:error res) @@ -729,7 +729,7 @@ (cb res) (let [ast (:value res)] (.append sb (with-out-str (comp/emit ast))) - (if (= :ns (:op ast)) + (if (#{:ns :ns*} (:op ast)) (ns-side-effects bound-vars aenv ast opts (fn [res] (if (:error res) @@ -829,7 +829,7 @@ (cb res) (let [ast (:value res) ns' ana/*cljs-ns*] - (if (= :ns (:op ast)) + (if (#{:ns :ns*} (:op ast)) (do (.append sb (with-out-str (comp/emitln (str "goog.provide(\"" (munge (:name ast)) "\");")))) From ab7a4911f1fd3a81210b1a9f2d84857748f8268b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 1 Oct 2016 16:40:29 +0100 Subject: [PATCH 2050/4033] CLJS-1805: Source map should take false --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index bd674e12a..1172bc13c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1805,7 +1805,7 @@ (defn check-source-map [{:keys [output-to source-map output-dir optimizations] :as opts}] "When :source-map is specified in opts, " (when (and (contains? opts :source-map) - (some? (:source-map opts)) + (:source-map opts) (not (= optimizations :none))) (assert (and (or (contains? opts :output-to) (contains? opts :modules)) From 8cbedafb783647312a0a6a4fed6d9684e6dbec0a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 1 Oct 2016 15:45:46 -0400 Subject: [PATCH 2051/4033] add many missing copyright notices --- src/main/clojure/cljs/js_deps.cljc | 8 ++++++++ src/main/clojure/cljs/repl/node_repl.js | 11 ++++++++++- src/main/clojure/cljs/repl/reflect.clj | 8 ++++++++ src/main/clojure/cljs/repl/server.clj | 8 ++++++++ src/main/clojure/cljs/source_map.clj | 8 ++++++++ src/main/clojure/cljs/source_map/base64.clj | 8 ++++++++ src/main/clojure/cljs/source_map/base64_vlq.clj | 8 ++++++++ src/main/clojure/cljs/tagged_literals.cljc | 8 ++++++++ src/test/cljs/cljs/baz.cljs | 8 ++++++++ src/test/cljs/cljs/binding_test.cljs | 8 ++++++++ src/test/cljs/cljs/binding_test_other_ns.cljs | 8 ++++++++ src/test/cljs/cljs/clojure_alias_test.cljs | 8 ++++++++ src/test/cljs/cljs/foo/ns_shadow_test.cljs | 8 ++++++++ src/test/cljs/cljs/hash_map_test.cljs | 8 ++++++++ src/test/cljs/cljs/import_test.cljs | 8 ++++++++ src/test/cljs/cljs/keyword_other.cljs | 8 ++++++++ src/test/cljs/cljs/keyword_test.cljs | 8 ++++++++ src/test/cljs/cljs/letfn_test.cljs | 8 ++++++++ src/test/cljs/cljs/macro_test.cljs | 8 ++++++++ src/test/cljs/cljs/macro_test/macros.clj | 8 ++++++++ src/test/cljs/cljs/ns_test.cljs | 8 ++++++++ src/test/cljs/cljs/predicates_test.cljs | 8 ++++++++ src/test/cljs/cljs/reader_test.cljs | 8 ++++++++ src/test/cljs/cljs/reducers_test.cljs | 8 ++++++++ src/test/cljs/cljs/spec_test.cljs | 8 ++++++++ src/test/cljs/cljs/syntax_quote_test.cljs | 8 ++++++++ src/test/cljs/cljs/top_level.cljs | 8 ++++++++ src/test/cljs/clojure/data_test.cljs | 8 ++++++++ src/test/cljs/clojure/string_test.cljs | 8 ++++++++ src/test/cljs/clojure/walk_test.cljs | 8 ++++++++ src/test/cljs/foo/ns_shadow_test.cljs | 8 ++++++++ src/test/cljs/module_test/main.cljs | 8 ++++++++ src/test/cljs/module_test/modules/a.cljs | 8 ++++++++ src/test/cljs/module_test/modules/b.cljs | 8 ++++++++ src/test/cljs/test_runner.cljs | 8 ++++++++ src/test/cljs_build/circular_deps/a.cljs | 8 ++++++++ src/test/cljs_build/circular_deps/b.cljs | 8 ++++++++ src/test/clojure/cljs/analyzer_api_tests.clj | 8 ++++++++ src/test/clojure/cljs/analyzer_tests.clj | 8 ++++++++ src/test/clojure/cljs/build_api_tests.clj | 8 ++++++++ src/test/clojure/cljs/closure_tests.clj | 8 ++++++++ src/test/clojure/cljs/compiler_tests.clj | 8 ++++++++ src/test/clojure/cljs/repl_tests.clj | 8 ++++++++ src/test/clojure/cljs/util_tests.clj | 8 ++++++++ src/test/self/bootstrap_test/core.cljs | 8 ++++++++ src/test/self/bootstrap_test/helper.clj | 8 ++++++++ src/test/self/bootstrap_test/macros.clj | 8 ++++++++ src/test/self/self_host/test.cljs | 8 ++++++++ src/test/self/self_parity/auxiliary.cljs | 8 ++++++++ src/test/self/self_parity/test.cljs | 8 ++++++++ 50 files changed, 402 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index fc6ab3ae5..6a7c1dcde 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -1,3 +1,11 @@ +;; 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.js-deps (:require [clojure.java.io :as io] [clojure.string :as string]) diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js index 311bef27a..3651c0463 100644 --- a/src/main/clojure/cljs/repl/node_repl.js +++ b/src/main/clojure/cljs/repl/node_repl.js @@ -1,5 +1,14 @@ -process.env.NODE_DISABLE_COLORS = true; +/** + * 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. + */ +process.env.NODE_DISABLE_COLORS = true; var net = require("net"); var vm = require("vm"); var dom = require("domain").create(); diff --git a/src/main/clojure/cljs/repl/reflect.clj b/src/main/clojure/cljs/repl/reflect.clj index fb9670777..215ecd6fc 100644 --- a/src/main/clojure/cljs/repl/reflect.clj +++ b/src/main/clojure/cljs/repl/reflect.clj @@ -1,3 +1,11 @@ +;; 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.repl.reflect (:refer-clojure :exclude [macroexpand]) (:require [cljs.repl.server :as server] diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index 3df643bee..b52fd7053 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -1,3 +1,11 @@ +;; 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.repl.server (:refer-clojure :exclude [loaded-libs]) (:require [clojure.string :as str]) diff --git a/src/main/clojure/cljs/source_map.clj b/src/main/clojure/cljs/source_map.clj index a0735631f..1fdddf4ec 100644 --- a/src/main/clojure/cljs/source_map.clj +++ b/src/main/clojure/cljs/source_map.clj @@ -1,3 +1,11 @@ +;; 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.source-map (:require [clojure.java.io :as io] [clojure.string :as string] diff --git a/src/main/clojure/cljs/source_map/base64.clj b/src/main/clojure/cljs/source_map/base64.clj index 58505748d..e786c74f9 100644 --- a/src/main/clojure/cljs/source_map/base64.clj +++ b/src/main/clojure/cljs/source_map/base64.clj @@ -1,3 +1,11 @@ +;; 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.source-map.base64) (def chars64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") diff --git a/src/main/clojure/cljs/source_map/base64_vlq.clj b/src/main/clojure/cljs/source_map/base64_vlq.clj index eaeed9946..b6d33cca0 100644 --- a/src/main/clojure/cljs/source_map/base64_vlq.clj +++ b/src/main/clojure/cljs/source_map/base64_vlq.clj @@ -1,3 +1,11 @@ +;; 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.source-map.base64-vlq (:require [clojure.string :as string] [cljs.source-map.base64 :as base64])) diff --git a/src/main/clojure/cljs/tagged_literals.cljc b/src/main/clojure/cljs/tagged_literals.cljc index dc1190d27..d14dc0b47 100644 --- a/src/main/clojure/cljs/tagged_literals.cljc +++ b/src/main/clojure/cljs/tagged_literals.cljc @@ -1,3 +1,11 @@ +;; 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.tagged-literals #?(:clj (:require [clojure.instant :as inst]) :cljs (:require [cljs.reader :as reader]))) diff --git a/src/test/cljs/cljs/baz.cljs b/src/test/cljs/cljs/baz.cljs index a5c1dde59..09b1ad76f 100644 --- a/src/test/cljs/cljs/baz.cljs +++ b/src/test/cljs/cljs/baz.cljs @@ -1,3 +1,11 @@ +;; 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 baz) (defn f [x] x) diff --git a/src/test/cljs/cljs/binding_test.cljs b/src/test/cljs/cljs/binding_test.cljs index 845fc74dc..fa193f174 100644 --- a/src/test/cljs/cljs/binding_test.cljs +++ b/src/test/cljs/cljs/binding_test.cljs @@ -1,3 +1,11 @@ +;; 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.binding-test (:require [cljs.test :refer-macros [deftest is]] [cljs.binding-test-other-ns :as o])) diff --git a/src/test/cljs/cljs/binding_test_other_ns.cljs b/src/test/cljs/cljs/binding_test_other_ns.cljs index 806582a8e..962b00b5b 100644 --- a/src/test/cljs/cljs/binding_test_other_ns.cljs +++ b/src/test/cljs/cljs/binding_test_other_ns.cljs @@ -1,3 +1,11 @@ +;; 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.binding-test-other-ns) (def ^:dynamic *foo* 1) diff --git a/src/test/cljs/cljs/clojure_alias_test.cljs b/src/test/cljs/cljs/clojure_alias_test.cljs index 957c50afe..f35566cbc 100644 --- a/src/test/cljs/cljs/clojure_alias_test.cljs +++ b/src/test/cljs/cljs/clojure_alias_test.cljs @@ -1,3 +1,11 @@ +;; 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.clojure-alias-test "Tests requiring via `clojure.*` instead of `cljs.*`" (:refer-clojure :exclude [use-macros]) diff --git a/src/test/cljs/cljs/foo/ns_shadow_test.cljs b/src/test/cljs/cljs/foo/ns_shadow_test.cljs index d2f1b536f..1d3d2200b 100644 --- a/src/test/cljs/cljs/foo/ns_shadow_test.cljs +++ b/src/test/cljs/cljs/foo/ns_shadow_test.cljs @@ -1,3 +1,11 @@ +;; 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 foo.ns-shadow-test (:require [cljs.test :refer-macros [deftest is]] baz)) diff --git a/src/test/cljs/cljs/hash_map_test.cljs b/src/test/cljs/cljs/hash_map_test.cljs index 1506e0c46..528cefad5 100644 --- a/src/test/cljs/cljs/hash_map_test.cljs +++ b/src/test/cljs/cljs/hash_map_test.cljs @@ -1,3 +1,11 @@ +;; 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.hash-map-test (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]])) diff --git a/src/test/cljs/cljs/import_test.cljs b/src/test/cljs/cljs/import_test.cljs index f8cfc4a00..8c54e18c0 100644 --- a/src/test/cljs/cljs/import_test.cljs +++ b/src/test/cljs/cljs/import_test.cljs @@ -1,3 +1,11 @@ +;; 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.import-test (:require [cljs.test :refer-macros [deftest is]]) (:import goog.math.Long diff --git a/src/test/cljs/cljs/keyword_other.cljs b/src/test/cljs/cljs/keyword_other.cljs index a7e8021b7..4a4624b95 100644 --- a/src/test/cljs/cljs/keyword_other.cljs +++ b/src/test/cljs/cljs/keyword_other.cljs @@ -1,3 +1,11 @@ +;; 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.keyword-other) (defn foo [a b] diff --git a/src/test/cljs/cljs/keyword_test.cljs b/src/test/cljs/cljs/keyword_test.cljs index 4b2c6fe4e..12c065867 100644 --- a/src/test/cljs/cljs/keyword_test.cljs +++ b/src/test/cljs/cljs/keyword_test.cljs @@ -1,3 +1,11 @@ +;; 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.keyword-test (:require-macros [clojure.core :as cc] [cljs.test :refer [deftest is]]) diff --git a/src/test/cljs/cljs/letfn_test.cljs b/src/test/cljs/cljs/letfn_test.cljs index 4261389e1..b66ae6c01 100644 --- a/src/test/cljs/cljs/letfn_test.cljs +++ b/src/test/cljs/cljs/letfn_test.cljs @@ -1,3 +1,11 @@ +;; 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.letfn-test (:require [cljs.test :refer-macros [deftest is]])) diff --git a/src/test/cljs/cljs/macro_test.cljs b/src/test/cljs/cljs/macro_test.cljs index 88f195614..86ac04fb1 100644 --- a/src/test/cljs/cljs/macro_test.cljs +++ b/src/test/cljs/cljs/macro_test.cljs @@ -1,3 +1,11 @@ +;; 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.macro-test (:refer-clojure :exclude [==]) (:require [cljs.test :refer-macros [deftest is]]) diff --git a/src/test/cljs/cljs/macro_test/macros.clj b/src/test/cljs/cljs/macro_test/macros.clj index 2c495190f..610cdd1e3 100644 --- a/src/test/cljs/cljs/macro_test/macros.clj +++ b/src/test/cljs/cljs/macro_test/macros.clj @@ -1,3 +1,11 @@ +;; 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.macro-test.macros (:refer-clojure :exclude [==])) diff --git a/src/test/cljs/cljs/ns_test.cljs b/src/test/cljs/cljs/ns_test.cljs index eb780f8e7..b84782db8 100644 --- a/src/test/cljs/cljs/ns_test.cljs +++ b/src/test/cljs/cljs/ns_test.cljs @@ -1,3 +1,11 @@ +;; 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.ns-test (:refer-clojure :exclude [+ for] :rename {mapv core-mapv}) (:require-macros [clojure.core :as lang :refer [when when-let] :rename {when always diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index a8bf149a2..d816c5e33 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -1,3 +1,11 @@ +;; 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.predicates-test (:require [cljs.test :as test :refer-macros [deftest is]]) (:import [goog.math Long Integer])) diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 734dfc00c..2bbebdf20 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -1,3 +1,11 @@ +;; 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.reader-test (:require [cljs.test :refer-macros [deftest testing is]] [cljs.reader :as reader] diff --git a/src/test/cljs/cljs/reducers_test.cljs b/src/test/cljs/cljs/reducers_test.cljs index ffe7e5898..06cdf8f1a 100644 --- a/src/test/cljs/cljs/reducers_test.cljs +++ b/src/test/cljs/cljs/reducers_test.cljs @@ -1,3 +1,11 @@ +;; 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.reducers-test (:require [cljs.test :refer-macros [deftest is]] [clojure.core.reducers :as r])) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 6e755c10f..88009d548 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -1,3 +1,11 @@ +;; 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.spec-test (:require [cljs.spec :as s] [cljs.test :as test :refer-macros [deftest is are run-tests]] diff --git a/src/test/cljs/cljs/syntax_quote_test.cljs b/src/test/cljs/cljs/syntax_quote_test.cljs index ae5f7074a..ea7de9e34 100644 --- a/src/test/cljs/cljs/syntax_quote_test.cljs +++ b/src/test/cljs/cljs/syntax_quote_test.cljs @@ -1,3 +1,11 @@ +;; 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.syntax-quote-test (:require [cljs.test :as test :refer-macros [deftest is]])) diff --git a/src/test/cljs/cljs/top_level.cljs b/src/test/cljs/cljs/top_level.cljs index 887d1d288..fc938a429 100644 --- a/src/test/cljs/cljs/top_level.cljs +++ b/src/test/cljs/cljs/top_level.cljs @@ -1,3 +1,11 @@ +;; 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.top-level (:refer-clojure :exclude [test]) (:require [cljs.test :refer-macros [deftest is]])) diff --git a/src/test/cljs/clojure/data_test.cljs b/src/test/cljs/clojure/data_test.cljs index b2d9fc971..0675a6a5b 100644 --- a/src/test/cljs/clojure/data_test.cljs +++ b/src/test/cljs/clojure/data_test.cljs @@ -1,3 +1,11 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + (ns clojure.data-test (:require [cljs.test :refer-macros [deftest is]] [clojure.data :refer [diff]])) diff --git a/src/test/cljs/clojure/string_test.cljs b/src/test/cljs/clojure/string_test.cljs index e9ffaa4da..14f002c0a 100644 --- a/src/test/cljs/clojure/string_test.cljs +++ b/src/test/cljs/clojure/string_test.cljs @@ -1,3 +1,11 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + (ns clojure.string-test (:require [cljs.test :as test :refer-macros [deftest is testing]] diff --git a/src/test/cljs/clojure/walk_test.cljs b/src/test/cljs/clojure/walk_test.cljs index 3d708c4db..6743cf1c8 100644 --- a/src/test/cljs/clojure/walk_test.cljs +++ b/src/test/cljs/clojure/walk_test.cljs @@ -1,3 +1,11 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + (ns clojure.walk-test (:require [cljs.test :as test :refer-macros [deftest is testing]] diff --git a/src/test/cljs/foo/ns_shadow_test.cljs b/src/test/cljs/foo/ns_shadow_test.cljs index d2f1b536f..1d3d2200b 100644 --- a/src/test/cljs/foo/ns_shadow_test.cljs +++ b/src/test/cljs/foo/ns_shadow_test.cljs @@ -1,3 +1,11 @@ +;; 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 foo.ns-shadow-test (:require [cljs.test :refer-macros [deftest is]] baz)) diff --git a/src/test/cljs/module_test/main.cljs b/src/test/cljs/module_test/main.cljs index f44315e4f..e1c03f9d6 100644 --- a/src/test/cljs/module_test/main.cljs +++ b/src/test/cljs/module_test/main.cljs @@ -1,3 +1,11 @@ +;; 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 module-test.main) (defn ^:export main [] diff --git a/src/test/cljs/module_test/modules/a.cljs b/src/test/cljs/module_test/modules/a.cljs index 99e7fd02e..47cdd95fb 100644 --- a/src/test/cljs/module_test/modules/a.cljs +++ b/src/test/cljs/module_test/modules/a.cljs @@ -1,3 +1,11 @@ +;; 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 module-test.modules.a) (defn ^:export main [] diff --git a/src/test/cljs/module_test/modules/b.cljs b/src/test/cljs/module_test/modules/b.cljs index 153319fe6..1fe826668 100644 --- a/src/test/cljs/module_test/modules/b.cljs +++ b/src/test/cljs/module_test/modules/b.cljs @@ -1,3 +1,11 @@ +;; 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 module-test.modules.b) (defn ^:export main [] diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index e1e65faa0..77e10b594 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -1,3 +1,11 @@ +;; 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 test-runner (:require [cljs.test :refer-macros [run-tests]] [cljs.primitives-test] diff --git a/src/test/cljs_build/circular_deps/a.cljs b/src/test/cljs_build/circular_deps/a.cljs index 15c3d5782..e090bd8c3 100644 --- a/src/test/cljs_build/circular_deps/a.cljs +++ b/src/test/cljs_build/circular_deps/a.cljs @@ -1,3 +1,11 @@ +;; 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 circular-deps.a (:require [circular-deps.b])) diff --git a/src/test/cljs_build/circular_deps/b.cljs b/src/test/cljs_build/circular_deps/b.cljs index 881d120ad..17cc0f79c 100644 --- a/src/test/cljs_build/circular_deps/b.cljs +++ b/src/test/cljs_build/circular_deps/b.cljs @@ -1,3 +1,11 @@ +;; 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 circular-deps.b (:require [circular-deps.a])) diff --git a/src/test/clojure/cljs/analyzer_api_tests.clj b/src/test/clojure/cljs/analyzer_api_tests.clj index 3b520b22a..0f9fcd9a2 100644 --- a/src/test/clojure/cljs/analyzer_api_tests.clj +++ b/src/test/clojure/cljs/analyzer_api_tests.clj @@ -1,3 +1,11 @@ +;; 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-api-tests (:require [cljs.analyzer.api :as ana-api]) (:use clojure.test)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 4f83630bc..b551ce579 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1,3 +1,11 @@ +;; 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-tests (:require [clojure.java.io :as io] [cljs.analyzer :as a] diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 9e50ebbd2..9d57755ad 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -1,3 +1,11 @@ +;; 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.build-api-tests (:refer-clojure :exclude [compile]) (:use cljs.build.api) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index a0ebfed38..8799cac19 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -1,3 +1,11 @@ +;; 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.closure-tests (:refer-clojure :exclude [compile]) (:use cljs.closure) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 77ae39281..939fa4a55 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -1,3 +1,11 @@ +;; 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.compiler-tests (:use clojure.test) (:require [cljs.analyzer :as ana] diff --git a/src/test/clojure/cljs/repl_tests.clj b/src/test/clojure/cljs/repl_tests.clj index e1147acd4..d3577b4e6 100644 --- a/src/test/clojure/cljs/repl_tests.clj +++ b/src/test/clojure/cljs/repl_tests.clj @@ -1,3 +1,11 @@ +;; 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.repl-tests (:require [clojure.java.io :as io] [cljs.analyzer :as ana] diff --git a/src/test/clojure/cljs/util_tests.clj b/src/test/clojure/cljs/util_tests.clj index 01b1dd40f..23abd06ef 100644 --- a/src/test/clojure/cljs/util_tests.clj +++ b/src/test/clojure/cljs/util_tests.clj @@ -1,3 +1,11 @@ +;; 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.util-tests (:require [cljs.util :as util]) (:use clojure.test)) diff --git a/src/test/self/bootstrap_test/core.cljs b/src/test/self/bootstrap_test/core.cljs index 756a1f003..43b917c4d 100644 --- a/src/test/self/bootstrap_test/core.cljs +++ b/src/test/self/bootstrap_test/core.cljs @@ -1,3 +1,11 @@ +;; 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 bootstrap-test.core) (defn foo [a b] diff --git a/src/test/self/bootstrap_test/helper.clj b/src/test/self/bootstrap_test/helper.clj index 3a3fd73c2..7eff8b74c 100644 --- a/src/test/self/bootstrap_test/helper.clj +++ b/src/test/self/bootstrap_test/helper.clj @@ -1,3 +1,11 @@ +;; 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 bootstrap-test.helper) (defn bar [a b] diff --git a/src/test/self/bootstrap_test/macros.clj b/src/test/self/bootstrap_test/macros.clj index 8648ffff2..07c3611be 100644 --- a/src/test/self/bootstrap_test/macros.clj +++ b/src/test/self/bootstrap_test/macros.clj @@ -1,3 +1,11 @@ +;; 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 bootstrap-test.macros (:require [bootstrap-test.helper :refer [bar]])) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index fbd75d5d2..72e1ed399 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1,3 +1,11 @@ +;; 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 self-host.test (:require [cljs.test :as test :refer-macros [run-tests deftest testing is async]] diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs index c1a5c4daf..0ceb34f4c 100644 --- a/src/test/self/self_parity/auxiliary.cljs +++ b/src/test/self/self_parity/auxiliary.cljs @@ -1,3 +1,11 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + (ns ^{:doc "This auxiliary namespace is not actually loaded. Its mere presence cause it to be compiled and thus causes the libs listed here to be dumped into the compiler output diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 228e44fdb..3165b48e1 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -1,3 +1,11 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + (ns ^{:doc "Builds and runs the ClojureScript compiler test suite in self-host mode, ensuring parity of bootstrapped ClojureScript From 1c470b2b0719814cc568805c12d552ff171bb136 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 1 Oct 2016 15:58:54 -0400 Subject: [PATCH 2052/4033] add a couple of missing notices --- src/main/cljs/cljs/externs.js | 10 ++++++++++ src/main/cljs/cljs/imul.js | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/main/cljs/cljs/externs.js b/src/main/cljs/cljs/externs.js index b996e1b57..af77602c4 100644 --- a/src/main/cljs/cljs/externs.js +++ b/src/main/cljs/cljs/externs.js @@ -1,3 +1,13 @@ +/** + * 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. + */ + Math.imul = function(a, b) {}; Object.prototype.done; diff --git a/src/main/cljs/cljs/imul.js b/src/main/cljs/cljs/imul.js index d28a33c35..867181945 100644 --- a/src/main/cljs/cljs/imul.js +++ b/src/main/cljs/cljs/imul.js @@ -1,3 +1,15 @@ +/** + * 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. + */ + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul + if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) { Math.imul = function (a, b) { var ah = (a >>> 16) & 0xffff; From ec5ed67d3bd67e0b9cbc3e782e241876c691df7b Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 3 Oct 2016 12:18:02 -0400 Subject: [PATCH 2053/4033] remove comments from imul.js --- src/main/cljs/cljs/imul.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/main/cljs/cljs/imul.js b/src/main/cljs/cljs/imul.js index 867181945..d28a33c35 100644 --- a/src/main/cljs/cljs/imul.js +++ b/src/main/cljs/cljs/imul.js @@ -1,15 +1,3 @@ -/** - * 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. - */ - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul - if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) { Math.imul = function (a, b) { var ah = (a >>> 16) & 0xffff; From 9ec238fbe8e26f73dedb171475dd59b7b606fc9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 2 Oct 2016 16:04:41 +0100 Subject: [PATCH 2054/4033] CLJS-1807: Better error messages for `ns*` calls --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cad4f89ef..030e7b15a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2183,7 +2183,7 @@ (defmethod parse 'ns [_ env [_ name & args :as form] _ opts] (when-not *allow-ns* - (throw (error env "Namespace declarations must be at the top-level."))) + (throw (error env "Namespace declarations must appear at the top-level."))) (when-not (symbol? name) (throw (error env "Namespaces must be named by a symbol."))) (let [name (cond-> name (:macros-ns opts) macro-ns-name)] @@ -2311,7 +2311,8 @@ (throw (error env (str "Arguments to " (name (first quoted-specs)) " must be quoted. Offending spec: " not-quoted)))) (when-not *allow-ns* - (throw (error env "Namespace declarations must be at the top-level."))) + (throw (error env (str "Calls to `" (name (first quoted-specs)) + "` must appear at the top-level.")))) (let [specs (canonicalize-specs quoted-specs) name (-> env :ns :name) args (desugar-ns-specs From eb164409d973d03019e0da889b47d962c74b6785 Mon Sep 17 00:00:00 2001 From: andrey zaytsev Date: Tue, 20 Sep 2016 17:55:43 +0300 Subject: [PATCH 2055/4033] follow-up on CLJS-460 defmulti ignores optional :hierarchy argument --- src/main/cljs/cljs/core.cljs | 8 ++++---- src/test/cljs/cljs/core_test.cljs | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0e18216af..0eb157941 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9965,17 +9965,17 @@ reduces them without incurring seq initialization" false))) (defn- dominates - [x y prefer-table] - (or (prefers* x y prefer-table) (isa? x y))) + [x y prefer-table hierarchy] + (or (prefers* x y prefer-table) (isa? hierarchy x y))) (defn- find-and-cache-best-method [name dispatch-val hierarchy method-table prefer-table method-cache cached-hierarchy] (let [best-entry (reduce (fn [be [k _ :as e]] (if (isa? @hierarchy dispatch-val k) - (let [be2 (if (or (nil? be) (dominates k (first be) prefer-table)) + (let [be2 (if (or (nil? be) (dominates k (first be) prefer-table @hierarchy)) e be)] - (when-not (dominates (first be2) k prefer-table) + (when-not (dominates (first be2) k prefer-table @hierarchy) (throw (js/Error. (str "Multiple methods in multimethod '" name "' match dispatch value: " dispatch-val " -> " k diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b8d4b3bcd..ac76f264d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -243,6 +243,14 @@ (defmulti foo2' identity) (defmethod foo2' 0 [x] x) +(def three-levels-h (-> (make-hierarchy) + (derive :parent :gparent) + (derive :child :parent))) + +(defmulti multi-with-h (fn [v] v) :hierarchy #'three-levels-h) +(defmethod multi-with-h :gparent [_] :gparent) +(defmethod multi-with-h :parent [_] :parent) + (deftest test-multimethods-2 (let [r (rect 4 13) c (circle 12)] @@ -272,6 +280,7 @@ (is (not (my-map? not-m)))) ;; multimethod hashing (is (= foo2' (ffirst {foo2' 1}))) + (is (= :parent (multi-with-h :child))) ))) (deftest test-transducers From a600651527e877428d0ca0679a53cc60d0816bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 16 Aug 2016 22:28:20 +0100 Subject: [PATCH 2056/4033] CLJS-1536: REPL def symbol init collision --- src/main/clojure/cljs/analyzer.cljc | 16 ++++++++++------ src/main/clojure/cljs/compiler.cljc | 10 +++++----- src/test/cljs/cljs/core_test.cljs | 14 ++++++++++++++ src/test/clojure/cljs/analyzer_tests.clj | 10 ++++++++++ 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 030e7b15a..b65fc4793 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1022,7 +1022,10 @@ (defn- var-ast [env sym] - (let [var (resolve-var env sym (confirm-var-exists-throw)) + ;; we need to dissoc locals for the `(let [x 1] (def x x))` case, because we + ;; want the var's AST and `resolve-var` will check locals first. - António Monteiro + (let [env (dissoc env :locals) + var (resolve-var env sym (confirm-var-exists-throw)) expr-env (assoc env :context :expr)] (if-let [var-ns (:ns var)] {:var (analyze expr-env sym) @@ -1384,9 +1387,10 @@ (defmethod parse 'fn* [op env [_ & args :as form] name _] - (let [[name meths] (if (symbol? (first args)) - [(first args) (next args)] - [name (seq args)]) + (let [named-fn? (symbol? (first args)) + [name meths] (if named-fn? + [(first args) (next args)] + [name (seq args)]) ;; turn (fn [] ...) into (fn ([]...)) meths (if (vector? (first meths)) (list meths) @@ -1397,7 +1401,7 @@ (update-in env [:fn-scope] conj name-var) env) locals (if (and (not (nil? locals)) - (not (nil? name))) + named-fn?) (assoc locals name name-var) locals) form-meta (meta form) @@ -1413,7 +1417,7 @@ methods (map #(disallowing-ns* (analyze-fn-method menv locals % type)) meths) mfa (apply max (map :max-fixed-arity methods)) variadic (boolean (some :variadic methods)) - locals (if-not (nil? name) + locals (if named-fn? (update-in locals [name] assoc ;; TODO: can we simplify? - David :fn-var true diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 65ebf4224..e78fb6f99 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -609,9 +609,9 @@ (when (or init (:def-emits-var env)) (let [mname (munge name)] (emit-comment env doc (concat jsdoc (:jsdoc init))) - (when (:def-emits-var env) - (when (= :return (:context env)) + (when (= :return (:context env)) (emitln "return (")) + (when (:def-emits-var env) (emitln "(function (){")) (emits var) (when init @@ -625,9 +625,9 @@ {:op :var-special :env (assoc env :context :expr)} var-ast)) - (emitln ");})()") - (when (= :return (:context env)) - (emitln ")"))) + (emitln ");})()")) + (when (= :return (:context env)) + (emitln ")")) ;; NOTE: JavaScriptCore does not like this under advanced compilation ;; this change was primarily for REPL interactions - David ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index ac76f264d..5a02e4e12 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1135,6 +1135,20 @@ (is (= (nth "012" 3 :not-found) :not-found)) (is (= (nth "012" -1 :not-found) :not-found))) +(let [foo-1536 2] + (def foo-1536 foo-1536)) + +(let [foo-1536-2 1] + (defn foo-1536-2 [] + foo-1536-2)) + +(deftest test-cljs-1536 + (is (= foo-1536 2)) + (is (= (foo-1536-2) 1)) + ;; these two lines generate a `:redef-in-file` warning, which is caused by `cljs.test/is` + (is (= ((let [z 1] (defn z [] z))) 1)) + (is (= (let [w 1] ((defn w [] w))) 1))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index b551ce579..097c2256f 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -591,3 +591,13 @@ nsb (str (a/gen-user-ns b))] (is (not= (.substring nsa (- (count nsa) 7)) (.substring nsb (- (count nsb) 7)))) (is (= (.substring nsa 0 (- (count nsa) 7)) (.substring nsb 0 (- (count nsb) 7))))))) + +(deftest test-cljs-1536 + (let [parsed (e/with-compiler-env test-cenv + (a/analyze (assoc test-env :def-emits-var true) + '(def x 1)))] + (is (some? (:var-ast parsed)))) + (let [parsed (e/with-compiler-env test-cenv + (a/analyze (assoc test-env :def-emits-var true) + '(let [y 1] (def y 2))))] + (is (some? (-> parsed :expr :ret :var-ast))))) From 6da4d77c4605639fc07d4801802683921c9d8d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Oct 2016 17:43:09 +0200 Subject: [PATCH 2057/4033] CLJS-1815: Fix failing analyzer tests --- src/test/clojure/cljs/analyzer_tests.clj | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 097c2256f..5f7bb2e7a 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -556,24 +556,27 @@ (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* nil] (are [analyzed] (thrown-with-msg? Exception - #"Namespace declarations must be at the top-level." + #"Namespace declarations must appear at the top-level." analyzed) (a/analyze test-env '(def foo (ns foo.core (:require [clojure.set :as set])))) - (a/analyze test-env - '(def foo - (require '[clojure.set :as set]))) (a/analyze test-env '(fn [] (ns foo.core (:require [clojure.set :as set])))) - (a/analyze test-env - '(fn [] (require '[clojure.set :as set]))) (a/analyze test-env '(map #(ns foo.core - (:require [clojure.set :as set])) [1 2])) + (:require [clojure.set :as set])) [1 2]))) + (are [analyzed] (thrown-with-msg? Exception + #"Calls to `require` must appear at the top-level." + analyzed) + (a/analyze test-env + '(def foo + (require '[clojure.set :as set]))) + (a/analyze test-env + '(fn [] (require '[clojure.set :as set]))) (a/analyze test-env '(map #(require '[clojure.set :as set]) [1 2])))))) From f826c31792393c4355c939408b2d5a3494846dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 12 Oct 2016 14:36:48 +0200 Subject: [PATCH 2058/4033] CLJS-1817: Strange result when assoc'ing 0 to persistent hash map there was a bug in the port of Clojure's PersistentHashMap. When looking up a node in a BitmapIndexedNode, we need to increment the found index to compare the value. The relevant line in Clojure's codebase is: https://github.com/clojure/clojure/blob/e547d/src/jvm/clojure/lang/PersistentHashMap.java#L940 --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/hash_map_test.cljs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0eb157941..bd848332a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6888,7 +6888,7 @@ reduces them without incurring seq initialization" (aset new-arr (inc len) val) (set! (.-val added-leaf?) true) (HashCollisionNode. nil collision-hash (inc cnt) new-arr)) - (if (= (aget arr idx) val) + (if (= (aget arr (inc idx)) val) inode (HashCollisionNode. nil collision-hash cnt (clone-and-set arr (inc idx) val))))) (.inode-assoc (BitmapIndexedNode. nil (bitpos collision-hash shift) (array nil inode)) diff --git a/src/test/cljs/cljs/hash_map_test.cljs b/src/test/cljs/cljs/hash_map_test.cljs index 528cefad5..dcc07c5f6 100644 --- a/src/test/cljs/cljs/hash_map_test.cljs +++ b/src/test/cljs/cljs/hash_map_test.cljs @@ -29,3 +29,11 @@ (-> (hash-map :a 1 :b 2 :c 3 :d 4 nil 5) (-iterator) (iter->set))))) + +(deftest test-cljs-1817 + (let [cljscore-hash hash] + (with-redefs [hash (fn [x] + (if (or (#{:a :b} x) 0) + cljscore-hash))] + (let [x (hash-map :a :a :b -1)] + (is (= (assoc x :b :b) {:a :a :b :b})))))) From e0242833188a0dca6ab5ec909f54e7e73239cebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 12 Oct 2016 15:14:00 +0200 Subject: [PATCH 2059/4033] CLJS-1818: (hash false) returns different value from Clojure mirrors Clojure's and JVM's behavior of returning fixed prime numbers as the hashCode of true and false. http://docs.oracle.com/javase/7/docs/api/java/lang/Boolean.html#hashCode%28%29 --- src/main/cljs/cljs/core.cljs | 7 +++++-- src/test/cljs/cljs/hashing_test.cljs | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index bd848332a..d019807ec 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -902,9 +902,12 @@ -1048576 2146959360)) - (true? o) 1 + ;; note: mirrors Clojure's behavior on the JVM, where the hashCode is + ;; 1231 for true and 1237 for false + ;; http://docs.oracle.com/javase/7/docs/api/java/lang/Boolean.html#hashCode%28%29 + (true? o) 1231 - (false? o) 0 + (false? o) 1237 (string? o) (m3-hash-int (hash-string o)) diff --git a/src/test/cljs/cljs/hashing_test.cljs b/src/test/cljs/cljs/hashing_test.cljs index 041305051..f96a21d09 100644 --- a/src/test/cljs/cljs/hashing_test.cljs +++ b/src/test/cljs/cljs/hashing_test.cljs @@ -87,4 +87,8 @@ (is (== (imul -2 -2) 4)) (is (== (imul 0xffffffff 5) -5)) (is (== (imul 0xfffffffe 5) -10)) - )) \ No newline at end of file + )) + +(deftest test-cljs-1818 + (is (= (hash true) 1231)) + (is (= (hash false) 1237))) From 5cc8f04242247edfb85a115a0acc46e9cef224f6 Mon Sep 17 00:00:00 2001 From: Andre Anastacio Date: Sat, 8 Oct 2016 23:44:37 -0300 Subject: [PATCH 2060/4033] CLJS-1294: Let macroexpand(-1) accept any quoted argument. Fixed by Julien Eluard --- src/main/clojure/cljs/core.cljc | 14 +++++++++----- src/test/cljs/cljs/macro_test.cljs | 6 ++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index f448daed5..b945f5df7 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2763,7 +2763,9 @@ (core/assert (core/= (core/first quoted) 'quote) "Argument to macroexpand-1 must be quoted") (core/let [form (second quoted)] - `(quote ~(ana/macroexpand-1 &env form)))) + (if (seq? form) + `(quote ~(ana/macroexpand-1 &env form)) + form))) (core/defmacro macroexpand "Repeatedly calls macroexpand-1 on form until it no longer @@ -2774,10 +2776,12 @@ "Argument to macroexpand must be quoted") (core/let [form (second quoted) env &env] - (core/loop [form form form' (ana/macroexpand-1 env form)] - (core/if-not (core/identical? form form') - (recur form' (ana/macroexpand-1 env form')) - `(quote ~form'))))) + (if (seq? form) + (core/loop [form form form' (ana/macroexpand-1 env form)] + (core/if-not (core/identical? form form') + (recur form' (ana/macroexpand-1 env form')) + `(quote ~form'))) + form))) (core/defn- multi-arity-fn? [fdecl] (core/< 1 (count fdecl))) diff --git a/src/test/cljs/cljs/macro_test.cljs b/src/test/cljs/cljs/macro_test.cljs index 86ac04fb1..cadced9b2 100644 --- a/src/test/cljs/cljs/macro_test.cljs +++ b/src/test/cljs/cljs/macro_test.cljs @@ -13,3 +13,9 @@ (deftest test-macros (is (= (== 1 1) 2))) + +(deftest macroexpansion + (is (= 1 (macroexpand-1 '1))) + (is (= '(if true (do 1)) (macroexpand-1 '(when true 1)))) + (is (= 1 (macroexpand '1))) + (is (= '(if true (do 1)) (macroexpand '(when true 1))))) From 176c681b25b75e907bf376698263dacf6ce998f4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 18 Oct 2016 12:14:42 -0400 Subject: [PATCH 2061/4033] CLJS-1824: transit cache feature leaks files --- src/main/clojure/cljs/analyzer.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b65fc4793..bf9dd5f8b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3393,8 +3393,8 @@ cached-ns (case ext "edn" (edn/read-string (slurp cache)) "json" (let [{:keys [reader read]} @transit] - (read (reader (io/input-stream cache) :json - transit-read-opts))))] + (with-open [is (io/input-stream cache)] + (read (reader is :json transit-read-opts)))))] (when (or *verbose* (:verbose opts)) (util/debug-prn "Reading analysis cache for" (str res))) (swap! env/*compiler* From a86a0ba3d9b8feffc36cef3d9de6a3d6197bdb16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 5 Oct 2016 15:09:58 +0100 Subject: [PATCH 2062/4033] CLJS-1809: Add 0/1 arity to `into` --- src/main/cljs/cljs/core.cljs | 2 ++ src/test/cljs/cljs/collections_test.cljs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d019807ec..56ce190df 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4762,6 +4762,8 @@ reduces them without incurring seq initialization" (defn into "Returns a new coll consisting of to-coll with all of the items of from-coll conjoined. A transducer may be supplied." + ([] []) + ([to] to) ([to from] (if-not (nil? to) (if (implements? IEditableCollection to) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 372412685..cfc183c3e 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -611,3 +611,7 @@ (is (= (get m 1) 2)) (is (= (get m 3) 4)) (is (= m {1 2 3 4})))) + +(deftest test-cljs-1809 + (is (= (into) [])) + (is (= (into [1 2]) [1 2]))) From ab7db12db848c071396708d0262d1dbad0a8aa36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Oct 2016 17:27:54 +0200 Subject: [PATCH 2063/4033] CLJS-1814: Move docstrings for require, etc. from `cljs.repl` to their new definitions in `cljs.core` --- src/main/clojure/cljs/core.cljc | 97 +++++++++++++++++++++++++++++---- src/main/clojure/cljs/repl.cljc | 81 --------------------------- 2 files changed, 85 insertions(+), 93 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b945f5df7..c040f460b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2729,28 +2729,101 @@ `(do ~@forms)) (core/defmacro require - [& specs] - `(~'ns* ~(cons :require specs))) + "Loads libs, skipping any that are already loaded. Each argument is + either a libspec that identifies a lib or a flag that modifies how all the identified + libs are loaded. Use :require in the ns macro in preference to calling this + directly. + + Libs + + A 'lib' is a named set of resources in classpath whose contents define a + library of ClojureScript code. Lib names are symbols and each lib is associated + with a ClojureScript namespace. A lib's name also locates its root directory + within classpath using Java's package name to classpath-relative path mapping. + All resources in a lib should be contained in the directory structure under its + root directory. All definitions a lib makes should be in its associated namespace. + + 'require loads a lib by loading its root resource. The root resource path + is derived from the lib name in the following manner: + Consider a lib named by the symbol 'x.y.z; it has the root directory + /x/y/, and its root resource is /x/y/z.clj. The root + resource should contain code to create the lib's namespace (usually by using + the ns macro) and load any additional lib resources. + + Libspecs + + A libspec is a lib name or a vector containing a lib name followed by + options expressed as sequential keywords and arguments. + + Recognized options: + :as takes a symbol as its argument and makes that symbol an alias to the + lib's namespace in the current namespace. + :refer takes a list of symbols to refer from the namespace.. + :refer-macros takes a list of macro symbols to refer from the namespace. + :include-macros true causes macros from the namespace to be required. + + Flags + + A flag is a keyword. + Recognized flags: :reload, :reload-all, :verbose + :reload forces loading of all the identified libs even if they are + already loaded + :reload-all implies :reload and also forces loading of all libs that the + identified libs directly or indirectly load via require or use + :verbose triggers printing information about each load, alias, and refer + + Example: + + The following would load the library clojure.string :as string. + + (require '[clojure/string :as string])" + [& args] + `(~'ns* ~(cons :require args))) (core/defmacro require-macros - [& specs] - `(~'ns* ~(cons :require-macros specs))) + "Similar to require but only for macros." + [& args] + `(~'ns* ~(cons :require-macros args))) (core/defmacro use - [& specs] - `(~'ns* ~(cons :use specs))) + "Like require, but referring vars specified by the mandatory + :only option. + + Example: + + The following would load the library clojure.set while referring + the intersection var. + + (use '[clojure.set :only [intersection]])" + [& args] + `(~'ns* ~(cons :use args))) (core/defmacro use-macros - [& specs] - `(~'ns* ~(cons :use-macros specs))) + "Similar to use but only for macros." + [& args] + `(~'ns* ~(cons :use-macros args))) (core/defmacro import - [& specs] - `(~'ns* ~(cons :import specs))) + "import-list => (closure-namespace constructor-name-symbols*) + + For each name in constructor-name-symbols, adds a mapping from name to the + constructor named by closure-namespace to the current namespace. Use :import in the ns + macro in preference to calling this directly." + [& import-symbols-or-lists] + `(~'ns* ~(cons :import import-symbols-or-lists))) (core/defmacro refer-clojure - [& specs] - `(~'ns* ~(cons :refer-clojure specs))) + "Refers to all the public vars of `cljs.core`, subject to + filters. + Filters can include at most one each of: + + :exclude list-of-symbols + :rename map-of-fromsymbol-tosymbol + + Filters can be used to select a subset, via exclusion, or to provide a mapping + to a symbol different from the var's name, in order to prevent clashes." + [& args] + `(~'ns* ~(cons :refer-clojure args))) ;; INTERNAL - do not use, only for Node.js (core/defmacro load-file* [f] diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 675a00f08..34d25ba81 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1054,87 +1054,6 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (def repl-special-doc-map '{in-ns {:arglists ([name]) :doc "Sets *cljs-ns* to the namespace named by the symbol, creating it if needed."} - require {:arglists ([& args]) - :doc " Loads libs, skipping any that are already loaded. Each argument is - either a libspec that identifies a lib or a flag that modifies how all the identified - libs are loaded. Use :require in the ns macro in preference to calling this - directly. - - Libs - - A 'lib' is a named set of resources in classpath whose contents define a - library of ClojureScript code. Lib names are symbols and each lib is associated - with a ClojureScript namespace. A lib's name also locates its root directory - within classpath using Java's package name to classpath-relative path mapping. - All resources in a lib should be contained in the directory structure under its - root directory. All definitions a lib makes should be in its associated namespace. - - 'require loads a lib by loading its root resource. The root resource path - is derived from the lib name in the following manner: - Consider a lib named by the symbol 'x.y.z; it has the root directory - /x/y/, and its root resource is /x/y/z.clj. The root - resource should contain code to create the lib's namespace (usually by using - the ns macro) and load any additional lib resources. - - Libspecs - - A libspec is a lib name or a vector containing a lib name followed by - options expressed as sequential keywords and arguments. - - Recognized options: - :as takes a symbol as its argument and makes that symbol an alias to the - lib's namespace in the current namespace. - :refer takes a list of symbols to refer from the namespace.. - :refer-macros takes a list of macro symbols to refer from the namespace. - :include-macros true causes macros from the namespace to be required. - - Flags - - A flag is a keyword. - Recognized flags: :reload, :reload-all, :verbose - :reload forces loading of all the identified libs even if they are - already loaded - :reload-all implies :reload and also forces loading of all libs that the - identified libs directly or indirectly load via require or use - :verbose triggers printing information about each load, alias, and refer - - Example: - - The following would load the library clojure.string :as string. - - (require '[clojure/string :as string])"} - require-macros {:arglists ([& args]) - :doc "Similar to the require REPL special function but - only for macros."} - use {:arglists ([& args]) - :doc "Like require, but referring vars specified by the mandatory - :only option. - - Example: - - The following would load the library clojure.set while referring - the intersection var. - - (use '[clojure.set :only [intersection]])"} - use-macros {:arglists ([& args]) - :doc "Similar to the use REPL special function but - only for macros."} - refer-clojure {:arglists ([& args]) - :doc "Refers to all the public vars of `cljs.core`, subject to - filters. - Filters can include at most one each of: - - :exclude list-of-symbols - :rename map-of-fromsymbol-tosymbol - - Filters can be used to select a subset, via exclusion, or to provide a mapping - to a symbol different from the var's name, in order to prevent clashes."} - import {:arglists ([& import-symbols-or-lists]) - :doc "import-list => (closure-namespace constructor-name-symbols*) - - For each name in constructor-name-symbols, adds a mapping from name to the - constructor named by closure-namespace to the current namespace. Use :import in the ns - macro in preference to calling this directly."} load-file {:arglists ([name]) :doc "Sequentially read and evaluate the set of forms contained in the file."}}) From 62e4bc982c44c123d6af9981470f7d9f7bfc2946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 18 Oct 2016 19:40:43 +0200 Subject: [PATCH 2064/4033] CLJS-1821: `add-preloads` should only touch sources if `:preloads` option specified --- src/main/clojure/cljs/closure.clj | 50 ++++++++++++++++++------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1172bc13c..0b31c674d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -901,27 +901,29 @@ files needing copying or compilation will be compiled and/or copied to the appropiate location." [inputs opts] - (let [pred (fn [x] - (if (:emit-constants opts) - (not= ["constants-table"] (:provides x)) - (not= ["cljs.core"] (:provides x)))) - pre (take-while pred inputs) - post (drop-while pred inputs) - preloads (remove nil? - (map - (fn [preload] - (try - (comp/find-source preload) - (catch Throwable t - (util/debug-prn "WARNING: preload namespace" preload "does not exist")))) - (:preloads opts)))] - (distinct-by :provides - (concat pre [(first post)] - (-> (add-dependency-sources preloads opts) - deps/dependency-order - (compile-sources opts) - (add-js-sources opts)) - (next post))))) + (if-not (:preloads opts) + inputs + (let [pred (fn [x] + (if (:emit-constants opts) + (not= ["constants-table"] (:provides x)) + (not= ["cljs.core"] (:provides x)))) + pre (take-while pred inputs) + post (drop-while pred inputs) + preloads (remove nil? + (map + (fn [preload] + (try + (comp/find-source preload) + (catch Throwable t + (util/debug-prn "WARNING: preload namespace" preload "does not exist")))) + (:preloads opts)))] + (distinct-by :provides + (concat pre [(first post)] + (-> (add-dependency-sources preloads opts) + deps/dependency-order + (compile-sources opts) + (add-js-sources opts)) + (next post)))))) (comment (comp/find-sources-root "samples/hello/src") @@ -1854,6 +1856,11 @@ (assert (not (and (= target :nodejs) (= optimizations :whitespace))) (format ":nodejs target not compatible with :whitespace optimizations"))) +(defn check-preloads [{:keys [preloads optimizations] :as opts}] + (when (and (some? preloads) (not= optimizations :none)) + (binding [*out* *err*] + (println "WARNING: :preloads should only be specified with :none optimizations")))) + (defn foreign-source? [js] (and (satisfies? deps/IJavaScript js) (deps/-foreign? js))) @@ -1949,6 +1956,7 @@ (check-source-map-path opts) (check-output-wrapper opts) (check-node-target opts) + (check-preloads opts) (swap! compiler-env #(-> % (update-in [:options] merge all-opts) From 9de9face6c06c3998eec3aec4730a071e30a8c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 18 Oct 2016 20:13:03 +0200 Subject: [PATCH 2065/4033] CLJS-1825: :source-map error when passing `false` under simple optimizations --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0b31c674d..ced532773 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -267,7 +267,7 @@ :else (println "value for" key "must be string, int, float, or bool")))) (if-let [extra-annotations (:closure-extra-annotations opts)] (. compiler-options (setExtraAnnotationNames (map name extra-annotations)))) - (when (contains? opts :source-map) + (when (:source-map opts) (if (:modules opts) ;; name is not actually used by Closur in :modules case, ;; but we need to provide _something_ for Closure to not From d87ac45f8015485546bd5f1a2b71678e6a049584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 18 Oct 2016 20:24:27 +0200 Subject: [PATCH 2066/4033] CLJS-1826: Self-host: load-deps doesn't honor `:reload` and `reload-all` --- src/main/cljs/cljs/js.cljs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ca1da3699..fc033200d 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -342,8 +342,8 @@ (defn- load-deps ([bound-vars ana-env lib deps cb] - (analyze-deps bound-vars ana-env lib deps nil cb)) - ([bound-vars ana-env lib deps opts cb] + (analyze-deps bound-vars ana-env lib deps nil nil cb)) + ([bound-vars ana-env lib deps reload opts cb] (when (:verbose opts) (debug-prn "Loading dependencies for" lib)) (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) @@ -356,12 +356,12 @@ opts' (-> opts (dissoc :context) (dissoc :ns))] - (require bound-vars dep opts' + (require bound-vars dep reload opts' (fn [res] (when (:verbose opts) (debug-prn "Loading result: " res)) (if-not (:error res) - (load-deps bound-vars ana-env lib (next deps) opts cb) + (load-deps bound-vars ana-env lib (next deps) nil opts cb) (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] (get {dep nil} cljs-ns cljs-ns))] (require bound-vars cljs-dep opts' @@ -370,7 +370,7 @@ (cb res) (do (patch-alias-map (:*compiler* bound-vars) lib dep cljs-dep) - (load-deps bound-vars ana-env lib (next deps) opts + (load-deps bound-vars ana-env lib (next deps) nil opts (fn [res] (if (:error res) (cb res) @@ -513,8 +513,9 @@ (str "Could not parse ns form " (:name ast)) cause)))))))))] (cond (and load (seq (:deps ast))) - (load-deps bound-vars ana-env (:name ast) (:deps ast) (dissoc opts :macros-ns) - #(check-uses-and-load-macros % (rewrite-ns-ast ast (:aliased-loads %)))) + (let [{:keys [reload name deps]} ast] + (load-deps bound-vars ana-env name deps (or (:require reload) (:use reload)) (dissoc opts :macros-ns) + #(check-uses-and-load-macros % (rewrite-ns-ast ast (:aliased-loads %))))) (and (not load) (:*analyze-deps* bound-vars) (seq (:deps ast))) (analyze-deps bound-vars ana-env (:name ast) (:deps ast) (dissoc opts :macros-ns) From 7e15b1f2b894d93ef94ff86d75226f1fd3919580 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 18 Oct 2016 17:14:50 -0400 Subject: [PATCH 2067/4033] CLJS-1658: testing for protocol membership may return false positives use a defonc'ed sentinel value instead of checking for true Thomas Heller provided the original patch on which this is based --- src/main/cljs/cljs/core.cljs | 2 ++ src/main/clojure/cljs/core.cljc | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 56ce190df..df2fcf30b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -19,6 +19,8 @@ (def *unchecked-if* false) +(defonce PROTOCOL_SENTINEL #js {}) + (goog-define ^{:dynamic true :doc "Var bound to the name value of the compiler build :target option. diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index c040f460b..39e1d43bd 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1454,7 +1454,7 @@ (add-obj-methods type type-sym sigs) (concat (core/when-not (skip-flag psym) - [`(set! ~(extend-prefix type-sym pprefix) true)]) + [`(set! ~(extend-prefix type-sym pprefix) cljs.core/PROTOCOL_SENTINEL)]) (mapcat (core/fn [sig] (if (= psym 'cljs.core/IFn) @@ -2014,13 +2014,13 @@ `(let [~xsym ~x] (if ~xsym (if (or ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit) false) - ~(bool-expr `(. ~xsym ~(symbol (core/str "-" prefix))))) + (identical? cljs.core/PROTOCOL_SENTINEL (. ~xsym ~(symbol (core/str "-" prefix))))) true false) false)) `(if-not (nil? ~x) (if (or ~(if bit `(unsafe-bit-and (. ~x ~msym) ~bit) false) - ~(bool-expr `(. ~x ~(symbol (core/str "-" prefix))))) + (identical? cljs.core/PROTOCOL_SENTINEL (. ~x ~(symbol (core/str "-" prefix))))) true false) false)))) @@ -2040,7 +2040,7 @@ `(let [~xsym ~x] (if-not (nil? ~xsym) (if (or ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit) false) - ~(bool-expr `(. ~xsym ~(symbol (core/str "-" prefix))))) + (identical? cljs.core/PROTOCOL_SENTINEL (. ~xsym ~(symbol (core/str "-" prefix))))) true (if (coercive-not (. ~xsym ~msym)) (cljs.core/native-satisfies? ~psym ~xsym) @@ -2048,7 +2048,7 @@ (cljs.core/native-satisfies? ~psym ~xsym))) `(if-not (nil? ~x) (if (or ~(if bit `(unsafe-bit-and (. ~x ~msym) ~bit) false) - ~(bool-expr `(. ~x ~(symbol (core/str "-" prefix))))) + (identical? cljs.core/PROTOCOL_SENTINEL (. ~x ~(symbol (core/str "-" prefix))))) true (if (coercive-not (. ~x ~msym)) (cljs.core/native-satisfies? ~psym ~x) From 732034bc8cb7e356b211c5d2b88a7af94ba184e5 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Sat, 27 Aug 2016 12:32:11 +0300 Subject: [PATCH 2068/4033] CLJS-1762: Bump Closure Compiler - ES6ModuleLoader has been replaced with ModuleLoader, it no longer needs to be passed to ProcessCommonJSModules etc. constructors - This version requires the version 20160822 and any checks needed to work with older versions have been removed - Closure-compiler jar name has been changed so lib/ and closure/ directories should be removed before running bootstrap - Added simple tests to check that CommonJS module processing is working - convert-js-module has been changed convert-js-modules and it now converts all the modules per module-type at one go - Measure process-js-modules time --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 4 +- src/main/clojure/cljs/closure.clj | 239 +++++++----------- src/test/cljs/Circle.js | 14 + src/test/cljs/react.js | 18 ++ .../clojure/cljs/module_processing_tests.clj | 53 ++++ 7 files changed, 180 insertions(+), 152 deletions(-) create mode 100644 src/test/cljs/Circle.js create mode 100644 src/test/cljs/react.js create mode 100644 src/test/clojure/cljs/module_processing_tests.clj diff --git a/pom.template.xml b/pom.template.xml index 4b30181ad..5bae55d8e 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20160315 + v20160911 org.clojure diff --git a/project.clj b/project.clj index de023372e..aa832d96b 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.285"] [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] - [com.google.javascript/closure-compiler-unshaded "v20160315"] + [com.google.javascript/closure-compiler-unshaded "v20160911"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 30ee279f2..44e802f52 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha7" -CLOSURE_RELEASE="20160315" +CLOSURE_RELEASE="20160911" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" @@ -98,7 +98,7 @@ echo "Cleaning up Rhino archive..." rm rhino$RHINO_RELEASE.zip echo "Copying closure/compiler/compiler.jar to lib/compiler.jar" -cp closure/compiler/compiler.jar lib +cp closure/compiler/closure-compiler-v$CLOSURE_RELEASE.jar lib echo "Fetching tools.reader $TREADER_RELEASE ..." curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar || { echo "Download failed."; exit 1; } diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ced532773..9ec68e966 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -57,7 +57,7 @@ Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy JSModule JSModuleGraph SourceMap ProcessCommonJSModules - ES6ModuleLoader AbstractCompiler TransformAMDToCJSModule + AbstractCompiler TransformAMDToCJSModule ProcessEs6Modules CompilerInput] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey @@ -74,45 +74,6 @@ (defn random-string [length] (apply str (take length (repeatedly random-char)))) -(util/compile-if - (.getConstructor ES6ModuleLoader - (into-array java.lang.Class - [java.util.List java.lang.Iterable])) - (do (def is-new-es6-loader? true) - (def default-module-root ES6ModuleLoader/DEFAULT_FILENAME_PREFIX)) - (def is-new-es6-loader? false)) - -(util/compile-if - (.getConstructor ES6ModuleLoader - (into-array java.lang.Class - [AbstractCompiler java.lang.String])) - (def is-old-es6-loader? true) - (def is-old-es6-loader? false)) - -(util/compile-if - (and (.getConstructor ProcessCommonJSModules - (into-array java.lang.Class - [com.google.javascript.jscomp.Compiler ES6ModuleLoader])) - (or is-new-es6-loader? is-old-es6-loader?)) - (def can-convert-commonjs? true) - (def can-convert-commonjs? false)) - -(util/compile-if - (and can-convert-commonjs? - (.getConstructor TransformAMDToCJSModule - (into-array java.lang.Class - [AbstractCompiler]))) - (def can-convert-amd? true) - (def can-convert-amd? false)) - -(util/compile-if - (and (.getConstructor ProcessEs6Modules - (into-array java.lang.Class - [com.google.javascript.jscomp.Compiler ES6ModuleLoader Boolean/TYPE])) - (or is-new-es6-loader? is-old-es6-loader?)) - (def can-convert-es6? true) - (def can-convert-es6? false)) - ;; Closure API ;; =========== @@ -1514,45 +1475,17 @@ :else (str (random-string 5) ".js"))))) -(defn get-js-module-root [js-file] - (let [path (.getParent (io/file js-file))] - (cond->> path - (.startsWith path File/separator) (str ".") - (not (.startsWith path (str "." File/separator))) (str "." File/separator) - (not (.endsWith path File/separator)) (#(str % File/separator))))) - -(util/compile-if is-new-es6-loader? - (defn make-es6-loader [source-files] - (let [^List module-roots (list default-module-root) - ^List compiler-inputs (map #(CompilerInput. %) source-files)] - (ES6ModuleLoader. module-roots compiler-inputs))) - (defn make-es6-loader [closure-compiler file] - (let [module-root (get-js-module-root file)] - (ES6ModuleLoader. closure-compiler module-root)))) - -(defn ^Node get-root-node [ijs closure-compiler] - (let [^CompilerInput input (->> (deps/-source ijs) - (js-source-file (:file ijs)) - (CompilerInput.))] - (.getAstRoot input closure-compiler))) - -(defn get-source-files [opts] - (->> (concat (:foreign-libs opts) - (:ups-foreign-libs opts)) - (filter #(let [module-type (:module-type %)] - (or (= module-type :amd) - (= module-type :commonjs) - (= module-type :es6)))) - (map (fn [lib] - (let [lib (deps/load-foreign-library lib)] - (js-source-file (:file lib) (deps/-source lib))))))) - -(defmulti convert-js-module - "Takes a JavaScript module as an IJavaScript and rewrites it into a Google - Closure-compatible form. Returns an IJavaScript with the converted module +(defn get-source-files [js-modules] + (map (fn [lib] + (js-source-file (:file lib) (deps/-source lib))) + js-modules)) + +(defmulti convert-js-modules + "Takes a list JavaScript modules as an IJavaScript and rewrites them into a Google + Closure-compatible form. Returns list IJavaScript with the converted module code set as source." - (fn [{module-type :module-type :as ijs} opts] - (if (and (= module-type :amd) can-convert-amd?) + (fn [module-type js-modules opts] + (if (= module-type :amd) ;; AMD modules are converted via CommonJS modules :commonjs module-type))) @@ -1564,48 +1497,45 @@ :language-in :language-out]) (set-options (CompilerOptions.)))) -(util/compile-if can-convert-commonjs? - (defmethod convert-js-module :commonjs [ijs opts] - (let [{:keys [file module-type]} ijs - ^List externs '() - ^List source-files (get-source-files opts) - ^CompilerOptions options (make-convert-js-module-options opts) - closure-compiler (doto (make-closure-compiler) - (.init externs source-files options)) - es6-loader (if is-new-es6-loader? - (make-es6-loader source-files) - (make-es6-loader closure-compiler file)) - cjs (ProcessCommonJSModules. closure-compiler es6-loader) - ^Node root (get-root-node ijs closure-compiler)] - (util/compile-if can-convert-amd? - (when (= module-type :amd) - (.process (TransformAMDToCJSModule. closure-compiler) nil root))) - (.process cjs nil root) - (report-failure (.getResult closure-compiler)) - (assoc ijs :source (.toSource closure-compiler root))))) - -(util/compile-if can-convert-es6? - (defmethod convert-js-module :es6 [ijs opts] - (let [{:keys [file]} ijs - ^List externs '() - ^List source-files (get-source-files opts) - ^CompilerOptions options (doto (make-convert-js-module-options opts) - (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) - (.setLanguageOut CompilerOptions$LanguageMode/ECMASCRIPT5)) - closure-compiler (doto (make-closure-compiler) - (.init externs source-files options)) - es6-loader (if is-new-es6-loader? - (make-es6-loader source-files) - (make-es6-loader closure-compiler file)) - cjs (ProcessEs6Modules. closure-compiler es6-loader true) - ^Node root (get-root-node ijs closure-compiler)] - (.processFile cjs root) - (report-failure (.getResult closure-compiler)) - (assoc ijs :source (.toSource closure-compiler root))))) - -(defmethod convert-js-module :default [ijs opts] - (ana/warning :unsupported-js-module-type @env/*compiler* ijs) - ijs) +(defn get-js-root [closure-compiler] + (.getSecondChild (.getRoot closure-compiler))) + +(defn get-closure-sources + "Gets map of source file name -> Node, for files in Closure Compiler js root." + [closure-compiler] + (let [source-nodes (.children (get-js-root closure-compiler))] + (into {} (map (juxt #(.getSourceFileName ^Node %) identity) source-nodes)))) + +(defn add-converted-source [closure-compiler result-nodes {:keys [file] :as ijs}] + (assoc ijs :source (.toSource closure-compiler ^Node (get result-nodes file)))) + +(defmethod convert-js-modules :commonjs [module-type js-modules opts] + (let [^List externs '() + ^List source-files (get-source-files js-modules) + ^CompilerOptions options (doto (make-convert-js-module-options opts) + (.setProcessCommonJSModules true) + (.setTransformAMDToCJSModules (= module-type :amd))) + closure-compiler (doto (make-closure-compiler) + (.init externs source-files options))] + (.parse closure-compiler) + (report-failure (.getResult closure-compiler)) + (map (partial add-converted-source closure-compiler (get-closure-sources closure-compiler)) js-modules))) + +(defmethod convert-js-modules :es6 [module-type js-modules opts] + (let [^List externs '() + ^List source-files (get-source-files js-modules) + ^CompilerOptions options (doto (make-convert-js-module-options opts) + (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) + (.setLanguageOut CompilerOptions$LanguageMode/ECMASCRIPT5)) + closure-compiler (doto (make-closure-compiler) + (.init externs source-files options))] + (.parse closure-compiler) + (report-failure (.getResult closure-compiler)) + (map (partial add-converted-source closure-compiler (get-closure-sources closure-compiler)) js-modules))) + +(defmethod convert-js-modules :default [module-type js-modules opts] + (ana/warning :unsupported-js-module-type @env/*compiler* (first js-modules)) + js-modules) (defmulti js-transforms "Takes an IJavaScript with the source code set as source, transforms the @@ -1636,12 +1566,7 @@ (when (and res (or ana/*verbose* (:verbose opts))) (util/debug-prn "Copying" (str res) "to" (str out-file))) (util/mkdirs out-file) - (spit out-file - (cond-> js - (map? js) (assoc :source (deps/-source js)) - (:preprocess js) (js-transforms opts) - (:module-type js) (convert-js-module opts) - true deps/-source)) + (spit out-file (deps/-source js)) (when res (.setLastModified ^File out-file (util/last-modified res)))) (if (map? js) @@ -1908,34 +1833,52 @@ (not (false? (:static-fns opts))) (assoc :static-fns true) (not (false? (:optimize-constants opts))) (assoc :optimize-constants true))))) -(defn- process-js-modules* - [opts k] - (let [js-modules (filter :module-type (k opts))] - (reduce (fn [new-opts {:keys [file module-type] :as lib}] - (if (or (and (= module-type :commonjs) can-convert-commonjs?) - (and (= module-type :amd) can-convert-amd?) - (and (= module-type :es6) can-convert-es6?)) - (let [ijs (write-javascript opts (deps/load-foreign-library lib)) - module-name (-> (deps/load-library (:out-file ijs)) first :provides first)] - (doseq [provide (:provides ijs)] - (swap! env/*compiler* - #(update-in % [:js-module-index] assoc provide module-name))) - (-> new-opts - (update-in [:libs] (comp vec conj) (:out-file ijs)) - (update-in [k] - (comp vec (fn [libs] (remove #(= (:file %) file) libs)))))) - new-opts)) - opts js-modules))) - (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google Closure modules and writes them to disk. Adds mapping from original module namespace to new module namespace to compiler env. Returns modified compiler options where new modules are passed with :libs option." [opts] - (-> opts - (process-js-modules* :foreign-libs) - (process-js-modules* :ups-foreign-libs))) + (let [;; Modules from both :foreign-libs (compiler options) and :ups-foreign-libs (deps.cljs) + ;; are processed together, so that files from both sources can depend on each other. + ;; e.g. commonjs module in :foreign-libs can depend on commonjs module from :ups-foreign-libs. + js-modules (filter :module-type (concat (:foreign-libs opts) (:ups-foreign-libs opts)))] + (if (seq js-modules) + (util/measure + "Process JS modules" + (let [;; Load all modules - add :source so preprocessing and conversion can access it + js-modules (map (fn [lib] + (let [js (deps/load-foreign-library lib)] + (assoc js :source (deps/-source js)))) + js-modules) + js-modules (map (fn [js] + (if (:preprocess js) + (js-transforms js opts) + js)) + js-modules) + ;; Conversion is done per module-type, because Compiler needs to process e.g. all CommonJS + ;; modules on one go, so it can handle the dependencies between modules. + ;; Amdjs modules are converted separate from CommonJS modules so they can't + ;; depend on each other. + modules-per-type (group-by :module-type js-modules) + js-modules (mapcat (fn [[module-type js-modules]] + (convert-js-modules module-type js-modules opts)) + modules-per-type)] + + ;; Write modules to disk, update compiler state and build new options + (reduce (fn [new-opts {:keys [file] :as ijs}] + (let [ijs (write-javascript opts ijs) + module-name (-> (deps/load-library (:out-file ijs)) first :provides first)] + (doseq [provide (:provides ijs)] + (swap! env/*compiler* + #(update-in % [:js-module-index] assoc provide module-name))) + (-> new-opts + (update-in [:libs] (comp vec conj) (:out-file ijs)) + ;; js-module might be defined in either, so update both + (update-in [:foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs)))) + (update-in [:ups-foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs))))))) + opts js-modules))) + opts))) (defn build "Given a source which can be compiled, produce runnable JavaScript." diff --git a/src/test/cljs/Circle.js b/src/test/cljs/Circle.js new file mode 100644 index 000000000..afc4280ec --- /dev/null +++ b/src/test/cljs/Circle.js @@ -0,0 +1,14 @@ +var React = require('./react'); + +var Circle = React.createClass({ + render: function() { + return( + + + + + ); + } +}); + +module.exports = Circle; diff --git a/src/test/cljs/react.js b/src/test/cljs/react.js new file mode 100644 index 000000000..7c18e0008 --- /dev/null +++ b/src/test/cljs/react.js @@ -0,0 +1,18 @@ +(function(f){ +if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()} +else if(typeof define==="function"&&define.amd){define([],f)} +else{var g;if(typeof window!=="undefined"){g=window} + else if(typeof global!=="undefined"){g=global} + else if(typeof self!=="undefined"){g=self} + else{g=this}g.React = f()} +})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o\n" + "\\s*\n" + "\\s*\n" + "\\s*\n" + "\\s*\\)")) + (str " React.createElement(\"svg\", {width:\"200px\", height:\"200px\", className:\"center\"}, " + "React.createElement(\"circle\", {cx:\"100px\", cy:\"100px\", r:\"100px\", fill:this.props.color})" + ")")))) + +(def cenv (env/default-compiler-env)) + +(deftest commonjs-module-processing + ;; Processed files are only copied/written if input has changed + ;; In test case it makes sense to write files always, in case the processing logic has changed. + (doseq [f (file-seq (io/file "out")) + :when (.isFile f)] + (.delete f)) + + ;; Reset load-library cache so that changes to processed files are noticed + (alter-var-root #'cljs.js-deps/load-library (constantly (memoize cljs.js-deps/load-library*))) + + (is (= {:foreign-libs [] + :ups-foreign-libs [] + :libs ["out/react.js" + "out/Circle.js"] + :closure-warnings {:non-standard-jsdoc :off}} + (env/with-compiler-env cenv + (closure/process-js-modules + {:foreign-libs [{:file "src/test/cljs/react.js" + :provides ["React"] + :module-type :commonjs} + {:file "src/test/cljs/Circle.js" + :provides ["Circle"] + :module-type :commonjs + :preprocess :jsx}] + :closure-warnings {:non-standard-jsdoc :off}}))) + "processed modules are added to :libs") + + (is (= {"React" "module$src$test$cljs$react" + "Circle" "module$src$test$cljs$Circle"} + (:js-module-index @cenv)) + "Processed modules are added to :js-module-index")) From f2ebdd3ff09752d8c6afdb6b0c1f37459b536512 Mon Sep 17 00:00:00 2001 From: jrheard Date: Mon, 3 Oct 2016 10:03:46 -0700 Subject: [PATCH 2069/4033] fix cljs.spec.test/check docstring --- src/main/cljs/cljs/spec/test.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 3957c1f9b..aea980317 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -208,7 +208,7 @@ sym-or-syms, a symbol or collection of symbols. If sym-or-syms is not specified, check all checkable vars. The opts map includes the following optional keys, where stc -aliases clojure.spec.test.check: +aliases clojure.test.check: ::stc/opts opts to flow through test.check/quick-check :gen map from spec names to generator overrides From f6a03a3d6eee64be417206d419b3017b09c5ddfd Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 19 Oct 2016 13:51:39 -0400 Subject: [PATCH 2070/4033] make String an implicit ns like Math. revert char? and clarify docstring. add unit tests for char? --- src/main/cljs/cljs/core.cljs | 4 ++-- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/cljs/cljs/primitives_test.cljs | 12 +++++++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index df2fcf30b..2f9399689 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -229,9 +229,9 @@ (goog/isString x)) (defn ^boolean char? - "Returns true if x is a JavaScript char." + "Returns true if x is a JavaScript string of length one." [x] - (gstring/isUnicodeChar x)) + (and (string? x) (== 1 (.-length x)))) (defn ^boolean any? "Returns true if given any argument." diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index bf9dd5f8b..982a6c7f8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -648,7 +648,7 @@ (throw (error ~env (.getMessage err#) err#))))))) ;; namespaces implicit to the inclusion of cljs.core -(def implicit-nses '#{goog goog.object goog.string goog.array Math}) +(def implicit-nses '#{goog goog.object goog.string goog.array Math String}) (defn implicit-import? #?(:cljs {:tag boolean}) diff --git a/src/test/cljs/cljs/primitives_test.cljs b/src/test/cljs/cljs/primitives_test.cljs index b0d8083d0..7233288d8 100644 --- a/src/test/cljs/cljs/primitives_test.cljs +++ b/src/test/cljs/cljs/primitives_test.cljs @@ -950,4 +950,14 @@ (is (= 1 (do #js {:a 1} 1))) (is (= 1 (aget #js {:a 1} "a"))) - (is (= 1 (.-a #js {:a 1}))))) \ No newline at end of file + (is (= 1 (.-a #js {:a 1}))))) + +(deftest test-char? + (is (char? "0")) + (is (char? (String/fromCharCode 13))) + (is (char? (String/fromCharCode 10))) + (is (char? \newline)) + (is (char? \space)) + (is (char? "0")) + (is (char? "\u0080")) + (is (char? "\uFFFD"))) \ No newline at end of file From 01c1a86e8ebbf26ddfb5c476fd910011c6c31e4c Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 19 Oct 2016 14:08:02 -0400 Subject: [PATCH 2071/4033] 1.9.293 --- README.md | 6 +++--- changes.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 774c8484d..5a1252ad6 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.229 +Latest stable release: 1.9.293 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.229"] +[org.clojure/clojurescript "1.9.293"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.229 org.clojure clojurescript - 1.9.229 + 1.9.293 ``` diff --git a/changes.md b/changes.md index 155210fa6..2965bf2eb 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,54 @@ +## 1.9.293 + +### Enhancements +* CLJS-1346: Support require outside of ns + +### Changes +* CLJS-1762: Bump Closure Compiler, refactor module support +* CLJS-1658: testing for protocol membership may return false positives +* CLJS-1536: REPL def symbol init collision +* CLJS-1805: Source map should take false +* CLJS-1804: Self-host: process namespace side-effects for new require without NS +* CLJS-1803: Use new require capability in REPLs +* CLJS-1796: Measure Google Closure specific optimization time +* CLJS-1782: Self-host: allow namespaces to require their own macros +* CLJS-1563: :source-map option to cljs.build.api/build should take nil +* CLJS-1785: Warn on reference to js/foo shadowed by local binding + +### Fixes +* make String an implicit ns like Math. revert char? and clarify docstring. add unit tests for char? +* fix cljs.spec.test/check docstring +* CLJS-1826: Self-host: load-deps doesn't honor `:reload` and `reload-all` +* CLJS-1825: :source-map error when passing `false` under simple optimizations +* CLJS-1821: `add-preloads` should only touch sources if `:preloads` option specified +* CLJS-1814: Move docstrings for require, etc. from `cljs.repl` to their new definitions in `cljs.core` +* CLJS-1809: Add 0/1 arity to `into` +* CLJS-1824: transit cache feature leaks files +* CLJS-1294: Let macroexpand(-1) accept any quoted argument. +* CLJS-1818: (hash false) returns different value from Clojure +* CLJS-1817: Strange result when assoc'ing 0 to persistent hash map +* CLJS-1815: Fix failing analyzer tests +* follow-up on CLJS-460 defmulti ignores optional :hierarchy argument +* CLJS-1807: Better error messages for `ns*` calls +* CLJS-1802: Generated namespaces should be of the form `cljs.user.fileXXXX` +* CLJ-1935: Use multimethod dispatch value method lookup to take hierarchies into account in multi-spec +* CLJS-1682 :foreign-libs with module conversion does not works properly if it is used form deps.cljs +* CLJS-1710: spec/double-in not implemented +* CLJS-1787: Make cljs.spec explain pluggable +* CLJS-1781: Add cljs.hash-map-test to self-parity tests +* CLJS-1788: Port CLJ-2004: include retag in multi-spec form +* CLJS-1765: Empty iterator for hash maps with nil key +* CLJS-1784: nth doesn't throw on strings or arrays +* CLJS-1773: Self-host: Don't resolve unqualified symbols / keywords with $macros +* CLJS-1770: goog-defines broken for integers +* CLJS-1600: Destructuring defprotocol fn args causes defrecord impls to silently fail +* CLJS-1335: resolve-macro-var: information missing for macros +* CLJS-1633: Improve error associated with invalid foreign-libs :file path +* CLJS-1775: `get` with `nil` returns as if `get` with `0` +* CLJS-1780: Records without extmaps fail to iterate +* CLJS-1774: Self-host: Report filenames in warns in test-self-parity +* CLJS-1779: keyword 2-arity constructor accepts anything for both parameters which leads to different hashing + ## 1.9.229 ### Fixes From 4b24f8e4e0e6e7abc9c5aad77552e0f9fe42a2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 20 Oct 2016 15:00:11 +0200 Subject: [PATCH 2072/4033] CLJS-1828: Add `:rename` to `require`'s docstring --- src/main/clojure/cljs/core.cljc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 39e1d43bd..97092558b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2761,6 +2761,9 @@ :refer takes a list of symbols to refer from the namespace.. :refer-macros takes a list of macro symbols to refer from the namespace. :include-macros true causes macros from the namespace to be required. + :rename specifies a map from referred var names to different + symbols (and can be used to prevent clashes) + Flags From 9959ae779dd60ca0b2a7093d1129568f3a658446 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 21 Oct 2016 15:49:14 -0400 Subject: [PATCH 2073/4033] inline some? --- src/main/clojure/cljs/core.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 97092558b..590e294e3 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -37,6 +37,7 @@ require use refer-clojure if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand + some? #?@(:cljs [alias coercive-not coercive-not= coercive-= coercive-boolean truth_ js-arguments js-delete js-in js-debugger exists? divide js-mod unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash @@ -857,6 +858,9 @@ (core/defmacro nil? [x] `(coercive-= ~x nil)) +(core/defmacro some? [x] + `(not (nil? ~x))) + ;; internal - do not use. (core/defmacro coercive-not [x] (bool-expr (core/list 'js* "(!~{})" x))) @@ -1945,7 +1949,7 @@ (mapv (core/fn [arg] (core/cond (core/symbol? arg) arg - (core/and (map? arg) (some? (:as arg))) (:as arg) + (core/and (map? arg) (core/some? (:as arg))) (:as arg) :else (gensym))) sig) sig)] `(~sig From 1f2c0a922130769a3da398aa501b3621fd1f8cd5 Mon Sep 17 00:00:00 2001 From: Maria Geller Date: Fri, 28 Oct 2016 14:11:05 +1300 Subject: [PATCH 2074/4033] CLJS-1820: "No such namespace" warning when referring to JS module namespace without using alias JS modules need to be loaded using the name that has been created by the Google Closure compiler during module conversion, e.g. module$resources$libs$calculator. This means that we always need to add an alias from the original name, which has been specified with the `:provides` compiler option, to the new name created by the Google Closure compiler, e.g. calculator -> module$resources$libs$calculator. --- src/main/clojure/cljs/analyzer.cljc | 24 ++++-- src/test/cljs/calculator.js | 10 +++ .../clojure/cljs/module_processing_tests.clj | 85 ++++++++++++------- 3 files changed, 83 insertions(+), 36 deletions(-) create mode 100644 src/test/cljs/calculator.js diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 982a6c7f8..927e9b36a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -677,6 +677,10 @@ true false))))) +(defn js-module-exists? + [module] + (-> (get-in @env/*compiler* [:js-module-index]) vals set (contains? module))) + (defn confirm-var-exists ([env prefix suffix] (let [warn (confirm-var-exist-warning env prefix suffix)] @@ -693,7 +697,8 @@ (when (and (not (implicit-import? env prefix suffix)) (not (loaded-js-ns? env prefix)) (not (and (= 'cljs.core prefix) (= 'unquote suffix))) - (nil? (gets @env/*compiler* ::namespaces prefix :defs suffix))) + (nil? (gets @env/*compiler* ::namespaces prefix :defs suffix)) + (not (js-module-exists? (str prefix)))) (missing-fn env prefix suffix))))) (defn confirm-var-exists-throw [] @@ -727,7 +732,8 @@ (nil? (gets @env/*compiler* ::namespaces ns-sym)) ;; macros may refer to namespaces never explicitly required ;; confirm that the library at least exists - #?(:clj (nil? (util/ns->source ns-sym)))) + #?(:clj (nil? (util/ns->source ns-sym))) + (not (js-module-exists? (str ns-sym)))) (warning :undeclared-ns env {:ns-sym ns-sym}))) (defn core-name? @@ -1994,9 +2000,13 @@ (do (basic-validate-ns-spec env macros? spec) (let [[lib & opts] spec - lib (if-let [js-module-name (get-in @env/*compiler* [:js-module-index (name lib)])] - (symbol js-module-name) - lib) + ;; We need to load JS modules by the name that has been created by the + ;; Google Closure compiler, e.g. module$resources$libs$calculator. + ;; This means that we need to create an alias from the module name + ;; given with :provides to the new name. + [lib js-module-provides] (if-let [js-module-name (get-in @env/*compiler* [:js-module-index (str lib)])] + [(symbol js-module-name) lib] + [lib nil]) {alias :as referred :refer renamed :rename :or {alias lib}} (apply hash-map opts) referred-without-renamed (seq (remove (set (keys renamed)) referred)) [rk uk renk] (if macros? [:require-macros :use-macros :rename-macros] [:require :use :rename])] @@ -2012,7 +2022,7 @@ (throw (error env (parse-ns-error-msg spec ":as alias must be unique")))) (swap! aliases update-in [alias-type] - conj [alias lib]))) + conj [alias lib] (when js-module-provides [js-module-provides lib])))) (when-not (or (and (sequential? referred) (every? symbol? referred)) (nil? referred)) @@ -2024,7 +2034,7 @@ (swap! deps conj lib)) (merge (when alias - {rk (merge {alias lib} {lib lib})}) + {rk (merge {alias lib} (when js-module-provides {js-module-provides lib}))}) (when referred-without-renamed {uk (apply hash-map (interleave referred-without-renamed (repeat lib)))}) (when renamed {renk (reduce (fn [m [original renamed]] diff --git a/src/test/cljs/calculator.js b/src/test/cljs/calculator.js new file mode 100644 index 000000000..c69a6960c --- /dev/null +++ b/src/test/cljs/calculator.js @@ -0,0 +1,10 @@ +var calculator = { + add: function (a, b) { + return a + b; + }, + subtract: function (a, b) { + return a - b; + } +}; + +module.exports = calculator; diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index ceda629a0..161133b78 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -2,7 +2,10 @@ (:require [clojure.java.io :as io] [cljs.closure :as closure] [clojure.test :refer :all] - [cljs.env :as env])) + [cljs.env :as env] + [cljs.analyzer :as ana] + [cljs.compiler :as comp] + [cljs.js-deps :as deps])) ;; Hard coded JSX transform for the test case (defmethod closure/js-transforms :jsx [ijs _] @@ -18,36 +21,60 @@ "React.createElement(\"circle\", {cx:\"100px\", cy:\"100px\", r:\"100px\", fill:this.props.color})" ")")))) -(def cenv (env/default-compiler-env)) - -(deftest commonjs-module-processing - ;; Processed files are only copied/written if input has changed - ;; In test case it makes sense to write files always, in case the processing logic has changed. +(defn delete-out-files + "Processed files are only copied/written if input has changed. In test case it + makes sense to write files always, in case the processing logic has changed." + [] (doseq [f (file-seq (io/file "out")) :when (.isFile f)] - (.delete f)) + (.delete f))) + +(deftest commonjs-module-processing + (delete-out-files) + (let [cenv (env/default-compiler-env)] + + ;; Reset load-library cache so that changes to processed files are noticed + (alter-var-root #'cljs.js-deps/load-library (constantly (memoize cljs.js-deps/load-library*))) - ;; Reset load-library cache so that changes to processed files are noticed - (alter-var-root #'cljs.js-deps/load-library (constantly (memoize cljs.js-deps/load-library*))) + (is (= {:foreign-libs [] + :ups-foreign-libs [] + :libs ["out/react.js" + "out/Circle.js"] + :closure-warnings {:non-standard-jsdoc :off}} + (env/with-compiler-env cenv + (closure/process-js-modules + {:foreign-libs [{:file "src/test/cljs/react.js" + :provides ["React"] + :module-type :commonjs} + {:file "src/test/cljs/Circle.js" + :provides ["Circle"] + :module-type :commonjs + :preprocess :jsx}] + :closure-warnings {:non-standard-jsdoc :off}}))) + "processed modules are added to :libs") - (is (= {:foreign-libs [] - :ups-foreign-libs [] - :libs ["out/react.js" - "out/Circle.js"] - :closure-warnings {:non-standard-jsdoc :off}} - (env/with-compiler-env cenv - (closure/process-js-modules - {:foreign-libs [{:file "src/test/cljs/react.js" - :provides ["React"] - :module-type :commonjs} - {:file "src/test/cljs/Circle.js" - :provides ["Circle"] - :module-type :commonjs - :preprocess :jsx}] - :closure-warnings {:non-standard-jsdoc :off}}))) - "processed modules are added to :libs") + (is (= {"React" "module$src$test$cljs$react" + "Circle" "module$src$test$cljs$Circle"} + (:js-module-index @cenv)) + "Processed modules are added to :js-module-index"))) - (is (= {"React" "module$src$test$cljs$react" - "Circle" "module$src$test$cljs$Circle"} - (:js-module-index @cenv)) - "Processed modules are added to :js-module-index")) +(deftest test-module-name-substitution + (delete-out-files) + (let [cenv (env/default-compiler-env)] + (env/with-compiler-env cenv + (let [opts (closure/process-js-modules {:foreign-libs [{:file "src/test/cljs/calculator.js" + :provides ["calculator"] + :module-type :commonjs}]}) + compile (fn [form] (with-out-str + (comp/emit (ana/analyze (ana/empty-env) form)))) + output "module$src$test$cljs$calculator.add.call(null,(3),(4));\n"] + (swap! cenv + #(assoc % :js-dependency-index (deps/js-dependency-index opts))) + (binding [ana/*cljs-ns* 'cljs.user] + (is (= (compile '(ns my-calculator.core (:require [calculator :as calc :refer [subtract add] :rename {subtract sub}]))) + "goog.provide('my_calculator.core');\ngoog.require('cljs.core');\ngoog.require('module$src$test$cljs$calculator');\n")) + (is (= (compile '(calc/add 3 4)) output)) + (is (= (compile '(calculator/add 3 4)) output)) + (is (= (compile '(add 3 4)) output)) + (is (= (compile '(sub 5 4)) + "module$src$test$cljs$calculator.subtract.call(null,(5),(4));\n"))))))) From 00c4b3552b7216eb145b25dc753877447375c2af Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 30 Oct 2016 11:11:51 +0100 Subject: [PATCH 2075/4033] comment out brittle test --- src/test/clojure/cljs/compiler_tests.clj | 33 ++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 939fa4a55..fb030d5db 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -156,30 +156,31 @@ '(cljs.user/foo (make-array 0)))))) ;; CLJS-1607 +;; Commented out this test - too hard coded to unnecessary details - David -(deftest test-cljs-1607 +#_(deftest test-cljs-1607 (let [define-Foo #(assoc-in % [::ana/namespaces 'cljs.user :defs 'Foo] - {:ns 'cljs.user - :name 'cljs.user/Foo - :protocol-symbol true - :protocol-info {:methods '{foo [[this]]}} - :protocol 'cljs.user/Foo}) + {:ns 'cljs.user + :name 'cljs.user/Foo + :protocol-symbol true + :protocol-info {:methods '{foo [[this]]}} + :protocol 'cljs.user/Foo}) define-foo #(assoc-in % [::ana/namespaces 'cljs.user :defs 'foo] - {:ns 'cljs.user - :name 'cljs.user/foo - :fn-var true - :method-params '([x]) - :protocol 'cljs.user/Foo}) + {:ns 'cljs.user + :name 'cljs.user/foo + :fn-var true + :method-params '([x]) + :protocol 'cljs.user/Foo}) aenv-with-foo (-> aenv define-foo define-Foo) cenv-with-foo (-> @cenv define-foo define-Foo)] (binding [ana/*cljs-static-fns* true] (are [form] (empty? - (capture-warnings - (env/with-compiler-env (atom cenv-with-foo) - (with-out-str - (comp/emit - (ana/analyze aenv-with-foo form)))))) + (capture-warnings + (env/with-compiler-env (atom cenv-with-foo) + (with-out-str + (comp/emit + (ana/analyze aenv-with-foo form)))))) '(specify! [] cljs.user/Foo (cljs.user/foo [this] From 316ac08f9e117648afee29c282051bd5416e48b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 29 Oct 2016 11:53:29 +0200 Subject: [PATCH 2076/4033] CLJS-1837: Port halt-when over from Clojure --- src/main/cljs/cljs/core.cljs | 26 +++++++++++++++++++++++++- src/test/cljs/cljs/core_test.cljs | 13 +++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 2f9399689..8e44bc7c5 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9624,13 +9624,37 @@ reduces them without incurring seq initialization" collection, into the reduction." {:added "1.7"} [rf] - (let [rf1 (preserving-reduced rf)] + (let [rf1 (preserving-reduced rf)] (fn ([] (rf)) ([result] (rf result)) ([result input] (reduce rf1 result input))))) +(defn halt-when + "Returns a transducer that ends transduction when pred returns true + for an input. When retf is supplied it must be a fn of 2 arguments - + it will be passed the (completed) result so far and the input that + triggered the predicate, and its return value (if it does not throw + an exception) will be the return value of the transducer. If retf + is not supplied, the input that triggered the predicate will be + returned. If the predicate never returns true the transduction is + unaffected." + {:added "1.9"} + ([pred] (halt-when pred nil)) + ([pred retf] + (fn [rf] + (fn + ([] (rf)) + ([result] + (if (and (map? result) (contains? result ::halt)) + (::halt result) + (rf result))) + ([result input] + (if (pred input) + (reduced {::halt (if retf (retf (rf result) input) input)}) + (rf result input))))))) + (defn dedupe "Returns a lazy sequence removing consecutive duplicates in coll. Returns a transducer when no collection is provided." diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5a02e4e12..3a2fa5277 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1149,6 +1149,19 @@ (is (= ((let [z 1] (defn z [] z))) 1)) (is (= (let [w 1] ((defn w [] w))) 1))) +(deftest test-cljs-1837 + (testing "halt-when transducer" + (is (= (transduce (halt-when #{1}) conj [] [5 4 1 2 3]) + 1)) + (is (= (transduce (halt-when #{1} (fn [ret input] input)) conj [] [5 4 1 2 3]) + 1)) + (is (= (transduce (halt-when #{1} (fn [ret input] ret)) conj [] [5 4 1 2 3]) + [5 4])) + (is (= (transduce (halt-when #{1} (fn [ret input] (conj ret input))) conj [] [5 4 1 2 3]) + [5 4 1])) + (is (= (into [] (halt-when #{1} (fn [ret in] (conj! ret in))) [2 3 1])) + [2 3 1]))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From ab27e6a34a78daa311516b2704ae8e04f793a1f6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 30 Oct 2016 12:11:57 +0100 Subject: [PATCH 2077/4033] fix ns aliasing regression for JS namespaces --- src/main/clojure/cljs/analyzer.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 927e9b36a..624b746ae 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2034,7 +2034,8 @@ (swap! deps conj lib)) (merge (when alias - {rk (merge {alias lib} (when js-module-provides {js-module-provides lib}))}) + {rk (merge {alias lib} {lib lib} + (when js-module-provides {js-module-provides lib}))}) (when referred-without-renamed {uk (apply hash-map (interleave referred-without-renamed (repeat lib)))}) (when renamed {renk (reduce (fn [m [original renamed]] From 7c5dac5df69e1197b49ea59a98889c71325b27aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 24 Oct 2016 14:37:18 +0200 Subject: [PATCH 2078/4033] CLJS-1834: REPL regression, require of ns from the ns itself errors out in circular reference --- src/main/clojure/cljs/analyzer.cljc | 7 ++++++- src/main/clojure/cljs/repl.cljc | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 624b746ae..4470f3311 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2971,13 +2971,18 @@ ast)) ast))) +(defn- repl-self-require? [env deps] + (and (:repl-env env) (some #{*cljs-ns*} deps))) + #?(:clj (defn ns-side-effects [env {:keys [op] :as ast} opts] (if (#{:ns :ns*} op) (let [{:keys [name deps uses require-macros use-macros reload reloads]} ast] (when (and *analyze-deps* (seq deps)) - (analyze-deps name deps env (dissoc opts :macros-ns))) + (analyze-deps + (if (repl-self-require? env deps) 'cljs.user name) + deps env (dissoc opts :macros-ns))) (if *load-macros* (do (load-core) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 34d25ba81..005f0d5de 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -447,12 +447,12 @@ (evaluate-form repl-env env filename form wrap *repl-opts*)) ([repl-env env filename form wrap opts] (binding [ana/*cljs-file* filename] - (let [env (assoc env - :root-source-info - {:source-type :fragment - :source-form form}) + (let [env (merge env + {:root-source-info {:source-type :fragment + :source-form form} + :repl-env repl-env}) def-emits-var (:def-emits-var opts) - ast (ana/analyze (assoc env :repl-env repl-env :def-emits-var def-emits-var) + ast (ana/analyze (assoc env :def-emits-var def-emits-var) (wrap form) nil opts) wrap-js ;; TODO: check opts as well - David From d134a78acba9df6a52830ac26c84f9e5992d1986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 27 Oct 2016 23:28:01 +0200 Subject: [PATCH 2079/4033] CLJS-1835: REPL load special fn --- src/main/clojure/cljs/repl.cljc | 39 +++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 005f0d5de..c2c87ce3e 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -544,6 +544,30 @@ (assert res (str "Can't find " f " in classpath")) (load-stream repl-env f res)))))) +(defn- root-resource + "Returns the root directory path for a lib" + {:tag String} + [lib] + (str \/ + (.. (name lib) + (replace \- \_) + (replace \. \/)))) + +(defn- root-directory + "Returns the root resource path for a lib" + [lib] + (let [d (root-resource lib)] + (subs d 0 (.lastIndexOf d "/")))) + +(defn- load-path->cp-path + [path] + (let [src (if (= File/separatorChar (first path)) + path + (str (root-directory ana/*cljs-ns*) \/ path)) + src (.substring src 1)] + (or (io/resource (str src ".cljs")) + (io/resource (str src ".cljc"))))) + (defn- wrap-fn [form] (cond (and (seq? form) @@ -664,7 +688,14 @@ ([repl-env env form] (self env repl-env form nil)) ([repl-env env [_ ns :as form] opts] - (load-namespace repl-env ns opts)))}))) + (load-namespace repl-env ns opts))) + 'load + (fn self + ([repl-env env form] + (self env repl-env form nil)) + ([repl-env env [_ & paths :as form] opts] + (let [cp-paths (map load-path->cp-path paths)] + (run! #(load-file repl-env % opts) cp-paths))))}))) (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate @@ -1055,7 +1086,11 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) '{in-ns {:arglists ([name]) :doc "Sets *cljs-ns* to the namespace named by the symbol, creating it if needed."} load-file {:arglists ([name]) - :doc "Sequentially read and evaluate the set of forms contained in the file."}}) + :doc "Sequentially read and evaluate the set of forms contained in the file."} + load {:arglists ([& paths]) + :doc "Loads Clojure code from resources in classpath. A path is interpreted as + classpath-relative if it begins with a slash or relative to the root + directory for the current namespace otherwise."}}) (defn- repl-special-doc [name-symbol] (assoc (repl-special-doc-map name-symbol) From 94f7367ac37f8b18d24eaea8793dd359816b39c4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 30 Oct 2016 13:21:18 +0100 Subject: [PATCH 2080/4033] respect :language-out when processing ES6 modules --- src/main/clojure/cljs/closure.clj | 36 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9ec68e966..4507a85f0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -152,6 +152,16 @@ (string/join ", " (keys string->charset)) " supported ") {})))) +(defn ^CompilerOptions$LanguageMode lang-key->lang-mode [key] + (case key + :no-transpile CompilerOptions$LanguageMode/NO_TRANSPILE + :ecmascript6 CompilerOptions$LanguageMode/ECMASCRIPT6 + :ecmascript6-strict CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT + :ecmascript6-typed CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED + :ecmascript5 CompilerOptions$LanguageMode/ECMASCRIPT5 + :ecmascript5-strict CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT + :ecmascript3 CompilerOptions$LanguageMode/ECMASCRIPT3)) + (defn set-options "TODO: Add any other options that we would like to support." [opts ^CompilerOptions compiler-options] @@ -170,25 +180,11 @@ :mapped AnonymousFunctionNamingPolicy/MAPPED (throw (IllegalArgumentException. (str "Invalid :anon-fn-naming-policy value " policy " - only :off, :unmapped, :mapped permitted"))))))) - (when (contains? opts :language-in) - (case (:language-in opts) - :no-transpile (.setLanguageIn compiler-options CompilerOptions$LanguageMode/NO_TRANSPILE) - :ecmascript6 (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6) - :ecmascript6-strict (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT) - :ecmascript6-typed (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED) - :ecmascript5 (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5) - :ecmascript5-strict (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT) - :ecmascript3 (.setLanguageIn compiler-options CompilerOptions$LanguageMode/ECMASCRIPT3))) - - (when (contains? opts :language-out) - (case (:language-out opts) - :no-transpile (.setLanguageOut compiler-options CompilerOptions$LanguageMode/NO_TRANSPILE) - :ecmascript6 (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6) - :ecmascript6-strict (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT) - :ecmascript6-typed (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED) - :ecmascript5 (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5) - :ecmascript5-strict (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT) - :ecmascript3 (.setLanguageOut compiler-options CompilerOptions$LanguageMode/ECMASCRIPT3))) + (when-let [lang-key (:language-in opts)] + (.setLanguageIn compiler-options (lang-key->lang-mode lang-key))) + + (when-let [lang-key (:language-out opts)] + (.setLanguageOut compiler-options (lang-key->lang-mode lang-key))) (when (contains? opts :print-input-delimiter) (set! (.printInputDelimiter compiler-options) @@ -1526,7 +1522,7 @@ ^List source-files (get-source-files js-modules) ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) - (.setLanguageOut CompilerOptions$LanguageMode/ECMASCRIPT5)) + (.setLanguageOut (lang-key->lang-mode (:language-out opts)))) closure-compiler (doto (make-closure-compiler) (.init externs source-files options))] (.parse closure-compiler) From 50cafcee1b9c41c2d5a054ef84c0998f4c276d8b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 30 Oct 2016 16:10:52 +0100 Subject: [PATCH 2081/4033] default to :ecmascript3 if :language-out not specified for :es6 module --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4507a85f0..13a54f70d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1522,7 +1522,7 @@ ^List source-files (get-source-files js-modules) ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) - (.setLanguageOut (lang-key->lang-mode (:language-out opts)))) + (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) closure-compiler (doto (make-closure-compiler) (.init externs source-files options))] (.parse closure-compiler) From 8e03b787f052612d3be3f230ff0b4b50a3333dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 4 Nov 2016 17:41:20 +0100 Subject: [PATCH 2082/4033] CLJS-1839: Relax the constraint that `new` and dot forms must be passed a symbol --- src/main/clojure/cljs/analyzer.cljc | 5 ++--- src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 4470f3311..223f81daf 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1656,12 +1656,11 @@ (defmethod parse 'new [_ env [_ ctor & args :as form] _ _] - (when-not (symbol? ctor) - (throw (error env "First arg to new must be a symbol"))) (disallowing-recur (let [enve (assoc env :context :expr) ctorexpr (analyze enve ctor) - ctor-var (resolve-existing-var env ctor) + ctor-var (when (= (:op ctorexpr) :var) + (resolve-existing-var env ctor)) record-args (when (and (:record ctor-var) (not (-> ctor meta :internal-ctor))) (repeat 3 (analyze enve nil))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 3a2fa5277..9139c2b24 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1162,6 +1162,13 @@ (is (= (into [] (halt-when #{1} (fn [ret in] (conj! ret in))) [2 3 1])) [2 3 1]))) +(deftest test-cljs-1839 + (let [x #js {:foo (fn [])} + foo (.-foo x)] + (is (instance? foo (new foo))) + (is (instance? foo (foo.))) + (is (instance? foo (new (.-foo x)))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 5aa574f4fac4066685e6a6929dd832b04ffd16e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 7 Nov 2016 19:44:18 +0100 Subject: [PATCH 2083/4033] CLJS-1842: Remove analyzer `:merge` hack for REPLs --- src/main/clojure/cljs/analyzer.cljc | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 223f81daf..2e541fe03 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2286,22 +2286,7 @@ :uses uses :requires requires :renames (merge renames core-renames) - :imports imports} - ns-info - (if (:merge form-meta) - ;; for merging information in via require usage in REPLs - (let [ns-info' (get-in @env/*compiler* [::namespaces name])] - (if (pos? (count ns-info')) - (let [merge-keys - [:use-macros :require-macros :rename-macros - :uses :requires :renames :imports]] - (merge - ns-info' - (merge-with merge - (select-keys ns-info' merge-keys) - (select-keys ns-info merge-keys)))) - ns-info)) - ns-info)] + :imports imports}] (swap! env/*compiler* update-in [::namespaces name] merge ns-info) (merge {:op :ns :env env From de05b15568c848a1d4f80a44bdffd486abd05150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 7 Nov 2016 12:07:32 +0100 Subject: [PATCH 2084/4033] CLJS-1768: cljs.spec perf tweaks This patch implements the changes in the following commits: https://github.com/clojure/clojure/commit/de6a2b528a18bcb4768e82d0d707d2cab26268a6 https://github.com/clojure/clojure/commit/defa7b8ef268ea2b8772658ade2010ca5ad00dc4 https://github.com/clojure/clojure/commit/021a3adf131d3f4158acd9e5d08ca91eb36ab56d https://github.com/clojure/clojure/commit/9fa85c6a908c9a3e89b4c0c449c49887a4c35248 --- src/main/cljs/cljs/spec.cljc | 33 ++- src/main/cljs/cljs/spec.cljs | 494 +++++++++++++++++++++++------------ 2 files changed, 353 insertions(+), 174 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 0b2f5e445..4d6ea7879 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -145,19 +145,20 @@ req-keys (into req-keys (map unk req-un-specs)) opt-keys (into (vec opt) (map unk opt-un)) opt-specs (into (vec opt) opt-un) + gx (gensym) parse-req (fn [rk f] (map (fn [x] (if (keyword? x) - `#(contains? % ~(f x)) - (let [gx (gensym)] - `(fn* [~gx] - ~(walk/postwalk - (fn [y] (if (keyword? y) `(contains? ~gx ~(f y)) y)) - x))))) + `(contains? ~gx ~(f x)) + (walk/postwalk + (fn [y] (if (keyword? y) `(contains? ~gx ~(f y)) y)) + x))) rk)) - pred-exprs [`map?] + pred-exprs [`(map? ~gx)] pred-exprs (into pred-exprs (parse-req req identity)) pred-exprs (into pred-exprs (parse-req req-un unk)) + keys-pred `(fn* [~gx] (c/and ~@pred-exprs)) + pred-exprs (mapv (fn [e] `(fn* [~gx] ~e)) pred-exprs) pred-forms (walk/postwalk #(res &env %) pred-exprs)] ;; `(map-spec-impl ~req-keys '~req ~opt '~pred-forms ~pred-exprs ~gen) `(map-spec-impl {:req '~req :opt '~opt :req-un '~req-un :opt-un '~opt-un @@ -165,6 +166,7 @@ :opt-keys '~opt-keys :opt-specs '~opt-specs :pred-forms '~pred-forms :pred-exprs ~pred-exprs + :keys-pred ~keys-pred :gfn ~gen}))) (defmacro or @@ -224,8 +226,19 @@ See also - coll-of, every-kv " [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] - (let [nopts (-> opts (dissoc :gen) (assoc ::kind-form `'~(res &env (:kind opts))))] - `(every-impl '~pred ~pred ~nopts ~gen))) + (let [nopts (-> opts (dissoc :gen) (assoc ::kind-form `'~(res &env (:kind opts)))) + gx (gensym) + cpreds (cond-> [(list (clojure.core/or kind `coll?) gx)] + count (conj `(= ~count (c/bounded-count ~count ~gx))) + + (clojure.core/or min-count max-count) + (conj `(<= (c/or ~min-count 0) + (c/bounded-count (if ~max-count (inc ~max-count) ~min-count) ~gx) + (c/or ~max-count MAX_INT))) + + distinct + (conj `(c/or (empty? ~gx) (apply distinct? ~gx))))] + `(every-impl '~pred ~pred ~(assoc nopts ::cpred `(fn* [~gx] (c/and ~@cpreds))) ~gen))) (defmacro every-kv "like 'every' but takes separate key and val preds and works on associative collections. @@ -422,7 +435,7 @@ (defmacro nilable "returns a spec that accepts nil and values satisfiying pred" [pred] - `(and (or ::nil nil? ::pred ~pred) (conformer second))) + `(nonconforming (or ::nil nil? ::pred ~pred))) (defmacro inst-in "Returns a spec that validates insts in the range from start diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 09d28df32..72460cfb1 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -46,28 +46,21 @@ (defonce ^:private registry-ref (atom {})) -(defn- named? [x] (implements? INamed x)) - -(defn- with-name [spec name] - (with-meta spec (assoc (meta spec) ::name name))) - -(defn- spec-name [spec] - (cond - (keyword? spec) spec - - (implements? IMeta spec) - (-> (meta spec) ::name))) +(defn- deep-resolve [reg k] + (loop [spec k] + (if (ident? spec) + (recur (get reg spec)) + spec))) (defn- reg-resolve - "returns the spec/regex at end of alias chain starting with k, nil if not found, k if k not Named" + "returns the spec/regex at end of alias chain starting with k, nil if not found, k if k not ident" [k] - (if (named? k) - (let [reg @registry-ref] - (loop [spec k] - (if (named? spec) - (recur (get reg spec)) - (when spec - (with-name spec k))))) + (if (ident? k) + (let [reg @registry-ref + spec (get reg k)] + (if-not (ident? spec) + spec + (deep-resolve reg spec))) k)) (defn- reg-resolve! @@ -81,22 +74,40 @@ (defn spec? "returns x if x is a spec object, else logical false" [x] - (c/and (implements? Spec x) x)) + (when (implements? Spec x) + x)) (defn regex? "returns x if x is a (clojure.spec) regex op, else logical false" [x] (c/and (::op x) x)) +(defn- with-name [spec name] + (cond + (ident? spec) spec + (regex? spec) (assoc spec ::name name) + + (implements? IMeta spec) + (with-meta spec (assoc (meta spec) ::name name)))) + +(defn- spec-name [spec] + (cond + (ident? spec) spec + + (regex? spec) (::name spec) + + (implements? IMeta spec) + (-> (meta spec) ::name))) + (declare spec-impl) (declare regex-spec-impl) (defn- maybe-spec "spec-or-k must be a spec, regex or resolvable kw/sym, else returns nil." [spec-or-k] - (let [s (c/or (spec? spec-or-k) + (let [s (c/or (c/and (ident? spec-or-k) (reg-resolve spec-or-k)) + (spec? spec-or-k) (regex? spec-or-k) - (c/and (named? spec-or-k) (reg-resolve spec-or-k)) nil)] (if (regex? s) (with-name (regex-spec-impl s nil) (spec-name s)) @@ -106,11 +117,34 @@ "spec-or-k must be a spec, regex or kw/sym, else returns nil. Throws if unresolvable kw/sym" [spec-or-k] (c/or (maybe-spec spec-or-k) - (when (named? spec-or-k) + (when (ident? spec-or-k) (throw (js/Error. (str "Unable to resolve spec: " spec-or-k)))))) -(defn- specize [s] - (c/or (the-spec s) (spec-impl ::unknown s nil nil))) +(defprotocol Specize + (specize* [_] [_ form])) + +(extend-protocol Specize + Keyword + (specize* ([k] (specize* (reg-resolve! k))) + ([k _] (specize* (reg-resolve! k)))) + + Symbol + (specize* ([s] (specize* (reg-resolve! s))) + ([s _] (specize* (reg-resolve! s)))) + + default + (specize* + ([o] (spec-impl ::unknown o nil nil)) + ([o form] (spec-impl form o nil nil)))) + +(defn- specize + ([s] (c/or (spec? s) (specize* s))) + ([s form] (c/or (spec? s) (specize* s form)))) + +(defn invalid? + "tests the validity of a conform return value" + [ret] + (keyword-identical? ::invalid ret)) (defn conform "Given a spec and a value, returns :clojure.spec/invalid if value does not match spec, @@ -256,11 +290,11 @@ (defn ^:skip-wiki def-impl "Do not call this directly, use 'def'" [k form spec] - (assert (c/and (named? k) (namespace k)) "k must be namespaced keyword or resolveable symbol") + (assert (c/and (ident? k) (namespace k)) "k must be namespaced keyword or resolveable symbol") (let [spec (if (c/or (spec? spec) (regex? spec) (get @registry-ref spec)) spec (spec-impl form spec nil nil))] - (swap! registry-ref assoc k spec) + (swap! registry-ref assoc k (with-name spec k)) k)) (defn registry @@ -286,7 +320,7 @@ [v args] (let [specs (get-spec v)] (when-let [arg-spec (:args specs)] - (when (= ::invalid (conform arg-spec args)) + (when (invalid? (conform arg-spec args)) (let [ed (assoc (explain-data* arg-spec [:args] (if-let [name (spec-name arg-spec)] [name] []) [] args) ::args args)] @@ -319,9 +353,18 @@ (defn valid? "Helper function that returns true when x is valid for spec." ([spec x] - (not= ::invalid (dt spec x ::unknown))) + (let [spec (specize spec)] + (not (invalid? (conform* spec x))))) ([spec x form] - (not= ::invalid (dt spec x form)))) + (let [spec (specize spec form)] + (not (invalid? (conform* spec x)))))) + +(defn- pvalid? + "internal helper function that returns true when x is valid for spec." + ([pred x] + (not (invalid? (dt pred x ::unknown)))) + ([pred x form] + (not (invalid? (dt pred x form))))) (defn- explain-1 [form pred path via in v] ;;(prn {:form form :pred pred :path path :in in :v v}) @@ -332,36 +375,39 @@ (defn ^:skip-wiki map-spec-impl "Do not call this directly, use 'spec' with a map argument" - [{:keys [req-un opt-un pred-exprs opt-keys req-specs req req-keys opt-specs pred-forms opt gfn] + [{:keys [req-un opt-un keys-pred pred-exprs opt-keys req-specs req req-keys opt-specs pred-forms opt gfn] :as argm}] - (let [keys-pred (apply every-pred pred-exprs) - k->s (zipmap (concat req-keys opt-keys) (concat req-specs opt-specs)) - keys->specs #(c/or (k->s %) %) + (let [k->s (zipmap (concat req-keys opt-keys) (concat req-specs opt-specs)) + keys->specnames #(c/or (k->s %) %) id (random-uuid)] (reify + Specize + (specize* [s] s) + (specize* [s _] s) + Spec (conform* [_ m] (if (keys-pred m) (let [reg (registry)] - (loop [ret m, [k & ks :as keys] (c/keys m)] + (loop [ret m, [[k v] & ks :as keys] m] (if keys - (if (contains? reg (keys->specs k)) - (let [v (get m k) - cv (conform (keys->specs k) v)] - (if (= cv ::invalid) - ::invalid - (recur (if (identical? cv v) ret (assoc ret k cv)) - ks))) - (recur ret ks)) + (let [sname (keys->specnames k)] + (if-let [s (get reg sname)] + (let [cv (conform s v)] + (if (invalid? cv) + ::invalid + (recur (if (identical? cv v) ret (assoc ret k cv)) + ks))) + (recur ret ks))) ret))) ::invalid)) (unform* [_ m] (let [reg (registry)] (loop [ret m, [k & ks :as keys] (c/keys m)] (if keys - (if (contains? reg (keys->specs k)) + (if (contains? reg (keys->specnames k)) (let [cv (get m k) - v (unform (keys->specs k) cv)] + v (unform (keys->specnames k) cv)] (recur (if (identical? cv v) ret (assoc ret k v)) ks)) (recur ret ks)) @@ -379,9 +425,9 @@ #(identity {:path path :pred % :val x :via via :in in}) probs)) (map (fn [[k v]] - (when-not (c/or (not (contains? reg (keys->specs k))) - (valid? (keys->specs k) v k)) - (explain-1 (keys->specs k) (keys->specs k) (conj path k) via (conj in k) v))) + (when-not (c/or (not (contains? reg (keys->specnames k))) + (pvalid? (keys->specnames k) v k)) + (explain-1 (keys->specnames k) (keys->specnames k) (conj path k) via (conj in k) v))) (seq x)))))) (gen* [_ overrides path rmap] (if gfn @@ -417,18 +463,25 @@ (cond (spec? pred) (cond-> pred gfn (with-gen gfn)) (regex? pred) (regex-spec-impl pred gfn) - (named? pred) (cond-> (the-spec pred) gfn (with-gen gfn)) + (ident? pred) (cond-> (the-spec pred) gfn (with-gen gfn)) :else (reify + Specize + (specize* [s] s) + (specize* [s _] s) + Spec - (conform* [_ x] (dt pred x form cpred?)) + (conform* [_ x] (let [ret (pred x)] + (if cpred? + ret + (if ret x ::invalid)))) (unform* [_ x] (if cpred? (if unc (unc x) (throw (js/Error. "no unform fn for conformer"))) x)) (explain* [_ path via in x] - (when (= ::invalid (dt pred x form cpred?)) + (when (invalid? (dt pred x form cpred?)) [{:path path :pred (abbrev form) :val x :via via :in in}])) (gen* [_ _ _ _] (if gfn (gfn) @@ -449,6 +502,10 @@ #(assoc %1 retag %2) retag)] (reify + Specize + (specize* [s] s) + (specize* [s _] s) + Spec (conform* [_ x] (if-let [pred (predx x)] (dt pred x form) @@ -474,7 +531,7 @@ #(tag % k) (gensub p overrides (conj path k) rmap (list 'method form k)))))))) gs (->> (methods @mmvar) - (remove (fn [[k]] (= k ::invalid))) + (remove (fn [[k]] (invalid? k))) (map gen) (remove nil?))] (when (every? identity gs) @@ -486,56 +543,63 @@ "Do not call this directly, use 'tuple'" ([forms preds] (tuple-impl forms preds nil)) ([forms preds gfn] - (reify - Spec - (conform* [_ x] - (if-not (c/and (vector? x) - (= (count x) (count preds))) - ::invalid + (let [specs (delay (mapv specize preds forms)) + cnt (count preds)] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] + (let [specs @specs] + (if-not (c/and (vector? x) + (= (count x) cnt)) + ::invalid + (loop [ret x, i 0] + (if (= i cnt) + ret + (let [v (x i) + cv (conform* (specs i) v)] + (if (invalid? cv) + ::invalid + (recur (if (identical? cv v) ret (assoc ret i cv)) + (inc i))))))))) + (unform* [_ x] + (assert (c/and (vector? x) + (= (count x) (count preds)))) (loop [ret x, i 0] (if (= i (count x)) ret - (let [v (x i) - cv (dt (preds i) v (forms i))] - (if (= ::invalid cv) - ::invalid - (recur (if (identical? cv v) ret (assoc ret i cv)) - (inc i)))))))) - (unform* [_ x] - (assert (c/and (vector? x) - (= (count x) (count preds)))) - (loop [ret x, i 0] - (if (= i (count x)) - ret - (let [cv (x i) - v (unform (preds i) cv)] - (recur (if (identical? cv v) ret (assoc ret i v)) - (inc i)))))) - (explain* [_ path via in x] - (cond - (not (vector? x)) - [{:path path :pred 'vector? :val x :via via :in in}] - - (not= (count x) (count preds)) - [{:path path :pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}] - - :else - (apply concat - (map (fn [i form pred] - (let [v (x i)] - (when-not (valid? pred v) - (explain-1 form pred (conj path i) via (conj in i) v)))) - (range (count preds)) forms preds)))) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (let [gen (fn [i p f] - (gensub p overrides (conj path i) rmap f)) - gs (map gen (range (count preds)) preds forms)] - (when (every? identity gs) - (apply gen/tuple gs))))) - (with-gen* [_ gfn] (tuple-impl forms preds gfn)) - (describe* [_] `(tuple ~@forms))))) + (let [cv (x i) + v (unform (preds i) cv)] + (recur (if (identical? cv v) ret (assoc ret i v)) + (inc i)))))) + (explain* [_ path via in x] + (cond + (not (vector? x)) + [{:path path :pred 'vector? :val x :via via :in in}] + + (not= (count x) (count preds)) + [{:path path :pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}] + + :else + (apply concat + (map (fn [i form pred] + (let [v (x i)] + (when-not (pvalid? pred v) + (explain-1 form pred (conj path i) via (conj in i) v)))) + (range (count preds)) forms preds)))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [gen (fn [i p f] + (gensub p overrides (conj path i) rmap f)) + gs (map gen (range (count preds)) preds forms)] + (when (every? identity gs) + (apply gen/tuple gs))))) + (with-gen* [_ gfn] (tuple-impl forms preds gfn)) + (describe* [_] `(tuple ~@forms)))))) (defn- tagged-ret [v] (specify! v @@ -548,24 +612,52 @@ [keys forms preds gfn] (let [id (random-uuid) kps (zipmap keys preds) - cform (fn [x] - (loop [i 0] - (if (< i (count preds)) - (let [pred (preds i)] - (let [ret (dt pred x (nth forms i))] - (if (= ::invalid ret) - (recur (inc i)) - (tagged-ret [(keys i) ret])))) - ::invalid)))] + specs (delay (mapv specize preds forms)) + cform (case (count preds) + 2 (fn [x] + (let [specs @specs + ret (conform* (specs 0) x)] + (if (invalid? ret) + (let [ret (conform* (specs 1) x)] + (if (invalid? ret) + ::invalid + (tagged-ret [(keys 1) ret]))) + (tagged-ret [(keys 0) ret])))) + 3 (fn [x] + (let [specs @specs + ret (conform* (specs 0) x)] + (if (invalid? ret) + (let [ret (conform* (specs 1) x)] + (if (invalid? ret) + (let [ret (conform* (specs 2) x)] + (if (invalid? ret) + ::invalid + (tagged-ret [(keys 2) ret]))) + (tagged-ret [(keys 1) ret]))) + (tagged-ret [(keys 0) ret])))) + (fn [x] + (let [specs @specs] + (loop [i 0] + (if (< i (count specs)) + (let [spec (specs i)] + (let [ret (conform* spec x)] + (if (invalid? ret) + (recur (inc i)) + (tagged-ret [(keys i) ret])))) + ::invalid)))))] (reify + Specize + (specize* [s] s) + (specize* [s _] s) + Spec (conform* [_ x] (cform x)) (unform* [_ [k x]] (unform (kps k) x)) (explain* [this path via in x] - (when-not (valid? this x) + (when-not (pvalid? this x) (apply concat (map (fn [k form pred] - (when-not (valid? pred x) + (when-not (pvalid? pred x) (explain-1 form pred (conj path k) via in x))) keys forms preds)))) (gen* [_ overrides path rmap] @@ -588,7 +680,7 @@ [form & forms] forms] (if pred (let [nret (dt pred ret form)] - (if (= ::invalid nret) + (if (invalid? nret) ::invalid ;;propagate conformed values (recur nret preds forms))) @@ -601,33 +693,62 @@ [pred & preds] preds] (when pred (let [nret (dt pred ret form)] - (if (not= ::invalid nret) - (recur nret forms preds) - (explain-1 form pred path via in ret)))))) + (if (invalid? nret) + (explain-1 form pred path via in ret) + (recur nret forms preds)))))) (defn ^:skip-wiki and-spec-impl "Do not call this directly, use 'and'" [forms preds gfn] - (reify - Spec - (conform* [_ x] (and-preds x preds forms)) - (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) - (explain* [_ path via in x] (explain-pred-list forms preds path via in x)) - (gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms)))) - (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) - (describe* [_] `(and ~@forms)))) + (let [specs (delay (mapv specize preds forms)) + cform + (case (count preds) + 2 (fn [x] + (let [specs @specs + ret (conform* (specs 0) x)] + (if (invalid? ret) + ::invalid + (conform* (specs 1) ret)))) + 3 (fn [x] + (let [specs @specs + ret (conform* (specs 0) x)] + (if (invalid? ret) + ::invalid + (let [ret (conform* (specs 1) ret)] + (if (invalid? ret) + ::invalid + (conform* (specs 2) ret)))))) + (fn [x] + (let [specs @specs] + (loop [ret x i 0] + (if (< i (count specs)) + (let [nret (conform* (specs i) ret)] + (if (invalid? ret) + ::invalid + ;;propagate conformed values + (recur nret (inc i)))) + ret)))))] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (cform x)) + (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) + (explain* [_ path via in x] (explain-pred-list forms preds path via in x)) + (gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms)))) + (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) + (describe* [_] `(and ~@forms))))) (defn- coll-prob [x kfn kform distinct count min-count max-count path via in] (let [pred (c/or kfn coll?) kform (c/or kform `coll?)] (cond - (not (valid? pred x)) + (not (pvalid? pred x)) (explain-1 kform pred path via in x) - (c/and distinct (not (empty? x)) (not (apply distinct? x))) - [{:path path :pred 'distinct? :val x :via via :in in}] - (c/and count (not= count (bounded-count count x))) [{:path path :pred `(= ~count (c/count ~'%)) :val x :via via :in in}] @@ -635,15 +756,22 @@ (not (<= (c/or min-count 0) (bounded-count (if max-count (inc max-count) min-count) x) (c/or max-count MAX_INT)))) - [{:path path :pred `(<= ~(c/or min-count 0) ~(c/count ~'%) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}]))) + [{:path path :pred `(<= ~(c/or min-count 0) ~(c/count ~'%) ~(c/or max-count MAX_INT)) :val x :via via :in in}] + + (c/and distinct (not (empty? x)) (not (apply distinct? x))) + [{:path path :pred 'distinct? :val x :via via :in in}]))) (defn ^:skip-wiki merge-spec-impl "Do not call this directly, use 'merge'" [forms preds gfn] (reify + Specize + (specize* [s] s) + (specize* [s _] s) + Spec (conform* [_ x] (let [ms (map #(dt %1 x %2) preds forms)] - (if (some #{::invalid} ms) + (if (some invalid? ms) ::invalid (apply c/merge ms)))) (unform* [_ x] (apply c/merge (map #(unform % x) (reverse preds)))) @@ -665,13 +793,14 @@ "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" ([form pred opts] (every-impl form pred opts nil)) ([form pred {gen-into :into - :keys [kind ::kind-form count max-count min-count distinct gen-max ::kfn + :keys [kind ::kind-form count max-count min-count distinct gen-max ::kfn ::cpred conform-keys ::conform-all] :or {gen-max 20, gen-into []} :as opts} gfn] (let [conform-into gen-into - check? #(valid? pred %) + spec (delay (specize pred)) + check? #(valid? @spec %) kfn (c/or kfn (fn [i v] i)) addcv (fn [ret i v cv] (conj ret cv)) [kindfn kindform] (cond @@ -704,34 +833,41 @@ :else [#(empty (c/or conform-into %)) addcv identity]))] (reify + Specize + (specize* [s] s) + (specize* [s _] s) + Spec (conform* [_ x] - (cond - (coll-prob x kind kind-form distinct count min-count max-count - nil nil nil) - ::invalid - - conform-all - (let [[init add complete] (cfns x)] - (loop [ret (init x), i 0, [v & vs :as vseq] (seq x)] - (if vseq - (let [cv (dt pred v nil)] - (if (= ::invalid cv) - ::invalid - (recur (add ret i v cv) (inc i) vs))) - (complete ret)))) - - :else - (if (indexed? x) - (let [step (max 1 (long (/ (c/count x) *coll-check-limit*)))] - (loop [i 0] - (if (>= i (c/count x)) - x - (if (check? (nth x i)) - (recur (c/+ i step)) - ::invalid)))) - (c/or (c/and (every? check? (take *coll-check-limit* x)) x) - ::invalid)))) + (let [spec @spec] + (cond + (not (cpred x)) ::invalid + + conform-all + (let [[init add complete] (cfns x)] + (loop [ret (init x), i 0, [v & vs :as vseq] (seq x)] + (if vseq + (let [cv (conform* spec v)] + (if (invalid? cv) + ::invalid + (recur (add ret i v cv) (inc i) vs))) + (complete ret)))) + + :else + (if (indexed? x) + (let [step (max 1 (long (/ (c/count x) *coll-check-limit*)))] + (loop [i 0] + (if (>= i (c/count x)) + x + (if (valid? spec (nth x i)) + (recur (c/+ i step)) + ::invalid)))) + (let [limit *coll-check-limit*] + (loop [i 0 [v & vs :as vseq] (seq x)] + (cond + (c/or (nil? vseq) (= i limit)) x + (valid? spec v) (recur (inc i) vs) + :else ::invalid))))))) (unform* [_ x] x) (explain* [_ path via in x] (c/or (coll-prob x kind kind-form distinct count min-count max-count @@ -875,7 +1011,7 @@ ::amp (c/and (accept-nil? p1) (c/or (noret? p1 (preturn p1)) (let [ret (-> (preturn p1) (and-preds ps (next forms)))] - (not= ret ::invalid)))) + (not (invalid? ret))))) ::rep (c/or (identical? p1 p2) (accept-nil? p1)) ::pcat (every? accept-nil? ps) ::alt (c/some accept-nil? ps)))) @@ -938,11 +1074,11 @@ (case op ::accept nil nil (let [ret (dt p x p)] - (when-not (= ::invalid ret) (accept ret))) + (when-not (invalid? ret) (accept ret))) ::amp (when-let [p1 (deriv p1 x)] (if (= ::accept (::op p1)) (let [ret (-> (preturn p1) (and-preds ps (next forms)))] - (when-not (= ret ::invalid) + (when-not (invalid? ret) (accept ret))) (amp-impl p1 ps forms))) ::pcat (alt2 (pcat* {:ps (cons (deriv p0 x) pr), :ks ks, :forms forms, :ret ret}) @@ -1103,6 +1239,10 @@ "Do not call this directly, use 'spec' with a regex op argument" [re gfn] (reify + Specize + (specize* [s] s) + (specize* [s _] s) + Spec (conform* [_ x] (if (c/or (nil? x) (coll? x)) @@ -1125,12 +1265,12 @@ (defn- call-valid? [f specs args] (let [cargs (conform (:args specs) args)] - (when-not (= cargs ::invalid) + (when-not (invalid? cargs) (let [ret (apply f args) cret (conform (:ret specs) ret)] - (c/and (not= cret ::invalid) + (c/and (not (invalid? cret)) (if (:fn specs) - (valid? (:fn specs) {:args cargs :ret cret}) + (pvalid? (:fn specs) {:args cargs :ret cret}) true)))))) (defn- validate-fn @@ -1151,6 +1291,11 @@ ILookup (-lookup [this k] (get specs k)) (-lookup [_ k not-found] (get specs k not-found)) + + Specize + (specize* [s] s) + (specize* [s _] s) + Spec (conform* [_ f] (if (ifn? f) (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) @@ -1167,7 +1312,7 @@ [{:path path :pred '(apply fn) :val args :reason (.-message ret) :via via :in in}] (let [cret (dt retspec ret rform)] - (if (= ::invalid cret) + (if (invalid? cret) (explain-1 rform retspec (conj path :ret) via in ret) (when fnspec (let [cargs (conform argspec args)] @@ -1177,7 +1322,7 @@ (gfn) (gen/return (fn [& args] - (assert (valid? argspec args) (with-out-str (explain argspec args))) + (assert (pvalid? argspec args) (with-out-str (explain argspec args))) (gen/generate (gen retspec overrides)))))) (with-gen* [_ gfn] (fspec-impl argspec aform retspec rform fnspec fform gfn)) (describe* [_] `(fspec :args ~aform :ret ~rform :fn ~fform))))) @@ -1185,6 +1330,27 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (cljs.spec/def ::kvs->map (cljs.spec/conformer #(zipmap (map ::k %) (map ::v %)) #(map (fn [[k v]] {::k k ::v v}) %))) +(defn nonconforming + "takes a spec and returns a spec that has the same properties except + 'conform' returns the original (not the conformed) value. Note, will specize regex ops." + [spec] + (let [spec (specize spec)] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (let [ret (conform* spec x)] + (if (invalid? ret) + ::invalid + x))) + (unform* [_ x] (unform* spec x)) + (explain* [_ path via in x] (explain* spec path via in x)) + (gen* [_ overrides path rmap] (gen* spec overrides path rmap)) + (with-gen* [_ gfn] (nonconforming (with-gen* spec gfn))) + (describe* [_] `(nonconforming ~(describe* spec)))))) + (defn exercise "generates a number (default 10) of values compatible with spec and maps conform over them, returning a sequence of [val conformed-val] tuples. Optionally takes From 88ff3178509ffe93f2522eaeb6a8e2f63163605a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 7 Nov 2016 17:59:36 +0100 Subject: [PATCH 2085/4033] CLJS-1794: incomplete alias created for namespace cljs.spec warning under advanced compilation Before, this warning was emitted by simply including `cljs.analyzer` in the build. Added a require for `cljs.analyzer` in a test namespace to account for regressions. --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/cljs/cljs/ns_test.cljs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2e541fe03..71d90f58f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2801,7 +2801,7 @@ :cljs [do]) (let [mchk #?(:clj (some-> (find-ns 'clojure.spec) (ns-resolve 'macroexpand-check)) - :cljs (and ^::no-resolve cljs.spec + :cljs (and (find-ns 'cljs.spec) ^::no-resolve cljs.spec/macroexpand-check)) _ (when mchk (mchk mac-var (next form))) diff --git a/src/test/cljs/cljs/ns_test.cljs b/src/test/cljs/cljs/ns_test.cljs index b84782db8..acecbd0fe 100644 --- a/src/test/cljs/cljs/ns_test.cljs +++ b/src/test/cljs/cljs/ns_test.cljs @@ -13,7 +13,8 @@ [cljs.test :refer [deftest is]]) (:require [cljs.test] [cljs.ns-test.foo :refer [baz]] - [clojure.set :as s :refer [intersection] :rename {intersection itsc}]) + [clojure.set :as s :refer [intersection] :rename {intersection itsc}] + [cljs.analyzer :as ana]) (:use [cljs.ns-test.bar :only [quux]])) (def + -) From 78f6504e1c67c935c283a362ecf6cb5a3766022e Mon Sep 17 00:00:00 2001 From: Daniel Compton Date: Mon, 7 Nov 2016 11:26:36 +1300 Subject: [PATCH 2086/4033] Provide more descriptive error message when invalid libspec detected When an invalid libspec is detected while compiling, an error is thrown which doesn't specify what the invalid libspec is. Combined with the line/column number referring to the start of the file, this can make it difficult to track down what is causing the problem. This patch prints the invalid libspec in the error which should make it much easier to find and fix the problem. --- src/main/clojure/cljs/analyzer.cljc | 4 ++-- src/test/clojure/cljs/analyzer_tests.clj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 71d90f58f..341b6563a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2243,9 +2243,9 @@ use-macros :use-macros require-macros :require-macros rename-macros :rename-macros imports :import :as params} (reduce - (fn [m [k & libs]] + (fn [m [k & libs :as libspec]] (when-not (#{:use :use-macros :require :require-macros :import} k) - (throw (error env "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported"))) + (throw (error env (str "Only :refer-clojure, :require, :require-macros, :use, :use-macros, 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) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 5f7bb2e7a..503948e16 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -159,7 +159,7 @@ (a/analyze ns-env '(ns foo.bar (:unless []))) (catch Exception e (.getMessage e))) - "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported")) + "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported. Got (:unless []) instead.")) (is (.startsWith (try (a/analyze ns-env '(ns foo.bar (:require baz.woz) (:require noz.goz))) From 80bbded9527dce6216f36763f23b7e5ebf8930df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 7 Nov 2016 22:16:37 +0100 Subject: [PATCH 2087/4033] CLJS-1666: Flag to optionally disable transit analysis cache encoding --- src/main/clojure/cljs/analyzer.cljc | 29 +++++++++++++++++------------ src/main/clojure/cljs/closure.clj | 11 +++++++++-- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 341b6563a..2194218f3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3254,26 +3254,31 @@ (.close ^Reader rdr))))))] ijs))))) +#?(:clj + (defn- cache-analysis-ext + ([] (cache-analysis-ext (get-in @env/*compiler* [:options :cache-analysis-format] :transit))) + ([format] + (if (and (= format :transit) @transit) "json" "edn")))) + #?(:clj (defn cache-file "Given a ClojureScript source file returns the read/write path to the analysis cache file. Defaults to the read path which is usually also the write path." ([src] (cache-file src "out")) ([src output-dir] (cache-file src (parse-ns src) output-dir)) - ([src ns-info output-dir] (cache-file src (parse-ns src) output-dir :read)) + ([src ns-info output-dir] + (cache-file src (parse-ns src) output-dir :read)) ([src ns-info output-dir mode] {:pre [(map? ns-info)]} - (if-let [core-cache - (and (= mode :read) - (= (:ns ns-info) 'cljs.core) - (or (and @transit (io/resource "cljs/core.cljs.cache.aot.json")) - (io/resource "cljs/core.cljs.cache.aot.edn")))] - core-cache - (let [target-file (util/to-target-file output-dir ns-info - (util/ext (:source-file ns-info)))] - (if @transit - (io/file (str target-file ".cache.json")) - (io/file (str target-file ".cache.edn")))))))) + (let [ext (cache-analysis-ext)] + (if-let [core-cache + (and (= mode :read) + (= (:ns ns-info) 'cljs.core) + (io/resource (str "cljs/core.cljs.cache.aot" ext)))] + core-cache + (let [target-file (util/to-target-file output-dir ns-info + (util/ext (:source-file ns-info)))] + (io/file (str target-file ".cache." ext)))))))) #?(:clj (defn requires-analysis? diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 13a54f70d..200de4995 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -129,7 +129,7 @@ :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads - :browser-repl}) + :browser-repl :cache-analysis-format}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -1782,6 +1782,11 @@ (binding [*out* *err*] (println "WARNING: :preloads should only be specified with :none optimizations")))) +(defn check-cache-analysis-format [{:keys [cache-analysis cache-analysis-format] :as opts}] + (assert (not (and cache-analysis ((complement #{nil :edn :transit}) cache-analysis-format))) + (format ":cache-analysis format must be :edn or :transit but it is: %s" + (pr-str cache-analysis-format)))) + (defn foreign-source? [js] (and (satisfies? deps/IJavaScript js) (deps/-foreign? js))) @@ -1812,7 +1817,8 @@ :ups-libs libs :ups-foreign-libs foreign-libs :ups-externs externs - :emit-constants emit-constants) + :emit-constants emit-constants + :cache-analysis-format (:cache-analysis-format opts :transit)) (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))) (:target opts) @@ -1896,6 +1902,7 @@ (check-output-wrapper opts) (check-node-target opts) (check-preloads opts) + (check-cache-analysis-format opts) (swap! compiler-env #(-> % (update-in [:options] merge all-opts) From 2fb9d1914a932adc833bd82044551205d5793738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 7 Nov 2016 19:57:07 +0100 Subject: [PATCH 2088/4033] CLJS-1745: refer-clojure doesn't pull in previously excluded vars --- src/main/clojure/cljs/analyzer.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2194218f3..9331589f5 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2371,6 +2371,7 @@ :uses :requires :renames :imports]] (merge ns-info' + {:excludes excludes} (merge-with merge (select-keys ns-info' merge-keys) (select-keys require-info merge-keys)))) From 140eb7a7b6213f7dfb5cc01ea5e95c267d510a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 11 Nov 2016 17:38:37 +0100 Subject: [PATCH 2089/4033] CLJS-1847: REPL should recognize `clojure.core/load` --- src/main/clojure/cljs/repl.cljc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index c2c87ce3e..095d46945 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -677,7 +677,14 @@ (swap! env/*compiler* assoc-in [::ana/namespaces ns-name] {:name ns-name}) (-evaluate repl-env "" 1 (str "goog.provide('" (comp/munge ns-name) "');"))) - (set! ana/*cljs-ns* ns-name)))] + (set! ana/*cljs-ns* ns-name))) + load-fn + (fn self + ([repl-env env form] + (self env repl-env form nil)) + ([repl-env env [_ & paths :as form] opts] + (let [cp-paths (map load-path->cp-path paths)] + (run! #(load-file repl-env % opts) cp-paths))))] (wrap-special-fns wrap-self {'in-ns in-ns-fn 'clojure.core/in-ns in-ns-fn @@ -689,13 +696,8 @@ (self env repl-env form nil)) ([repl-env env [_ ns :as form] opts] (load-namespace repl-env ns opts))) - 'load - (fn self - ([repl-env env form] - (self env repl-env form nil)) - ([repl-env env [_ & paths :as form] opts] - (let [cp-paths (map load-path->cp-path paths)] - (run! #(load-file repl-env % opts) cp-paths))))}))) + 'load load-fn + 'clojure.core/load load-fn}))) (defn analyze-source "Given a source directory, analyzes all .cljs files. Used to populate From e28e3f2ce78d6781c549f6e757194600d8db4cfc Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Wed, 9 Nov 2016 10:40:01 +0000 Subject: [PATCH 2090/4033] CLJS-1845: Assoc on subvec should throw if out of bounds --- src/main/cljs/cljs/core.cljs | 4 +++- src/test/cljs/cljs/core_test.cljs | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8e44bc7c5..5079b4d29 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5426,7 +5426,9 @@ reduces them without incurring seq initialization" IVector (-assoc-n [coll n val] (let [v-pos (+ start n)] - (build-subvec meta (assoc v v-pos val) start (max end (inc v-pos)) nil))) + (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)))) IReduce (-reduce [coll f] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 9139c2b24..86b2d9951 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1169,6 +1169,15 @@ (is (instance? foo (foo.))) (is (instance? foo (new (.-foo x)))))) +(deftest test-cljs-1845 + (let [sv (subvec [0 1 2 3 4 5 7 8 9] 2 6)] + (is (= [2 3 4 5] sv)) + (is (= [2 3 0 5] (assoc sv 2 0))) + (is (= [2 3 4 0] (assoc sv 3 0))) + (is (= [2 3 4 5 0] (assoc sv 4 0))) + (is (thrown? js/Error (assoc sv 5 0))) + (is (thrown? js/Error (assoc sv -1 0))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 498c1da144eee7011cf57a392274e9166ff146b7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 11 Nov 2016 13:51:56 -0500 Subject: [PATCH 2091/4033] CLJS-1844: port over Maria Geller's externs file parsing code --- src/main/clojure/cljs/externs.clj | 83 +++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/main/clojure/cljs/externs.clj diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj new file mode 100644 index 000000000..9ddca3995 --- /dev/null +++ b/src/main/clojure/cljs/externs.clj @@ -0,0 +1,83 @@ +; 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.externs + (:require [clojure.string :as string]) + (:import [java.util.logging Level] + [java.io File BufferedInputStream] + [com.google.javascript.jscomp CompilerOptions SourceFile JsAst] + [com.google.javascript.rhino Node Token])) + +;; ------------------------------------------------------------------------------ +;; Externs Parsing + +(defmulti parse-extern-node (fn [^Node node] (.getType node))) + +(defmethod parse-extern-node Token/VAR [node] + (when (> (.getChildCount node) 0) + (parse-extern-node (.getFirstChild node)))) + +(defmethod parse-extern-node Token/EXPR_RESULT [node] + (when (> (.getChildCount node) 0) + (parse-extern-node (.getFirstChild node)))) + +(defmethod parse-extern-node Token/ASSIGN [node] + (when (> (.getChildCount node) 0) + (let [lhs (first (parse-extern-node (.getFirstChild node)))] + (if (> (.getChildCount node) 1) + (let [externs (parse-extern-node (.getChildAtIndex node 1))] + (conj (map (fn [ext] (concat lhs ext)) externs) + lhs)) + [lhs])))) + +(defmethod parse-extern-node Token/NAME [node] + (let [lhs (map symbol (string/split (.getQualifiedName node) #"\."))] + (if (> (.getChildCount node) 0) + (let [externs (parse-extern-node (.getFirstChild node))] + (conj (map (fn [ext] (concat lhs ext)) externs) + lhs)) + [lhs]))) + +(defmethod parse-extern-node Token/GETPROP [node] + [(map symbol (string/split (.getQualifiedName node) #"\."))]) + +(defmethod parse-extern-node Token/OBJECTLIT [node] + (when (> (.getChildCount node) 0) + (loop [nodes (.children node) + externs []] + (if (empty? nodes) + externs + (recur (rest nodes) + (concat externs (parse-extern-node (first nodes)))))))) + +(defmethod parse-extern-node Token/STRING_KEY [node] + (let [lhs (map symbol (string/split (.getString node) #"\."))] + (if (> (.getChildCount node) 0) + (let [externs (parse-extern-node (.getFirstChild node))] + (conj (map (fn [ext] (concat lhs ext)) externs) + lhs)) + [lhs]))) + +(defmethod parse-extern-node :default [node]) + +(defn parse-externs [^SourceFile source-file] + (let [^CompilerOptions compiler-options (CompilerOptions.) + closure-compiler (doto + (let [compiler (com.google.javascript.jscomp.Compiler.)] + (com.google.javascript.jscomp.Compiler/setLoggingLevel Level/WARNING) + compiler) + (.init (list source-file) '() compiler-options)) + js-ast (JsAst. source-file) + ^Node root (.getAstRoot js-ast closure-compiler)] + (loop [nodes (.children root) + externs []] + (if (empty? nodes) + externs + (let [node (first nodes) + new-extern (parse-extern-node node)] + (recur (rest nodes) (concat externs new-extern))))))) From 8b5487560a559f2f5c84de0454bd586554d3b654 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 11 Nov 2016 13:52:26 -0500 Subject: [PATCH 2092/4033] remove unused import --- src/main/clojure/cljs/externs.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 9ddca3995..4bf31dbcd 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -9,7 +9,6 @@ (ns cljs.externs (:require [clojure.string :as string]) (:import [java.util.logging Level] - [java.io File BufferedInputStream] [com.google.javascript.jscomp CompilerOptions SourceFile JsAst] [com.google.javascript.rhino Node Token])) From 0e92c75e646cb137f5ab635e85fccc6d0caf9fd3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 11 Nov 2016 14:06:13 -0500 Subject: [PATCH 2093/4033] add externs processing example --- src/main/clojure/cljs/closure.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 200de4995..b22eb79fc 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2004,6 +2004,11 @@ {:hello {:output-to "samples/hello/out/hello.js" :entries '#{cljs.reader hello.core}}}}) + + (require '[cljs.externs :as externs]) + + (externs/parse-externs + (js-source-file "cljs/externs.js" (io/file "src/main/cljs/cljs/externs.js"))) ) (defn ^File target-file-for-cljs-ns From 8dd629816d2fd473df42d7732d8afe148f91e442 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 11 Nov 2016 14:19:37 -0500 Subject: [PATCH 2094/4033] add map-merge util fn --- src/main/clojure/cljs/util.cljc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index d184494b1..e97c9b9ce 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -269,3 +269,17 @@ (.reset digest) (.update digest (.getBytes s "utf8")) (DatatypeConverter/printHexBinary (.digest digest)))) + +(defn map-merge [a b] + (if (and (map? a) (map? b)) + (loop [ks (seq (keys a)) ret a b' b] + (if ks + (let [k (first ks)] + (if (contains? b' k) + (recur + (next ks) + (assoc ret k (map-merge (get ret k) (get b' k))) + (dissoc b' k)) + (recur (next ks) ret b'))) + (merge ret b'))) + a)) From 4349d4294485a901d4257262d664df06d01ec4e5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 11 Nov 2016 14:24:05 -0500 Subject: [PATCH 2095/4033] add externs emission helpers --- src/main/clojure/cljs/compiler.cljc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index e78fb6f99..0acab39a5 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1483,3 +1483,29 @@ (with-open [out ^java.io.Writer (io/make-writer dest {})] (binding [*out* out] (emit-constants-table table))))) + +(defn emit-externs + ([externs] + (emit-externs [] externs (atom #{}))) + ([prefix externs top-level] + (loop [ks (seq (keys externs))] + (when ks + (let [k (first ks) + [top :as prefix'] (conj prefix k)] + (when-not (= 'prototype k) + (if-not (contains? @top-level top) + (do + (emitln "var " (string/join "." (map munge prefix')) ";") + (swap! top-level conj top)) + (emitln (string/join "." (map munge prefix')) ";"))) + (let [m (get externs k)] + (when-not (empty? m) + (emit-externs prefix' m top-level)))) + (recur (next ks)))))) + +#?(:clj + (defn emit-inferred-externs-to-file [externs dest] + (io/make-parents dest) + (with-open [out ^java.io.Writer (io/make-writer dest {})] + (binding [*out* out] + (emit-externs externs))))) From 602b4beeaca29ef3900c422167eabe62ab662d66 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 11 Nov 2016 14:35:51 -0500 Subject: [PATCH 2096/4033] add support for emitting inferred externs file --- src/main/clojure/cljs/closure.clj | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b22eb79fc..9d4111d71 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -129,7 +129,7 @@ :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads - :browser-repl :cache-analysis-format}) + :browser-repl :cache-analysis-format :infer-externs}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -251,7 +251,7 @@ Options may contain an :externs key with a list of file paths to load. The :use-only-custom-externs flag may be used to indicate that the default externs should be excluded." - [{:keys [externs use-only-custom-externs target ups-externs]}] + [{:keys [externs use-only-custom-externs target ups-externs infer-externs] :as opts}] (let [validate (fn validate [p us] (if (empty? us) (throw (IllegalArgumentException. @@ -273,12 +273,16 @@ ext))) load-js (fn [ext] (map #(js-source-file (.getFile %) (slurp %)) ext))] - (let [js-sources (-> externs filter-js add-target load-js) + (let [js-sources (-> externs filter-js add-target load-js) ups-sources (-> ups-externs filter-cp-js load-js) - all-sources (concat js-sources ups-sources)] - (if use-only-custom-externs - all-sources - (into all-sources (CommandLineRunner/getDefaultExterns)))))) + all-sources (vec (concat js-sources ups-sources))] + (cond-> + (if use-only-custom-externs + all-sources + (into all-sources (CommandLineRunner/getDefaultExterns))) + infer-externs + (conj (js-source-file nil + (io/file (util/output-directory opts) "inferred_externs.js"))))))) (defn ^com.google.javascript.jscomp.Compiler make-closure-compiler [] (let [compiler (com.google.javascript.jscomp.Compiler.)] @@ -1957,6 +1961,12 @@ (comp/emit-constants-table-to-file (::ana/constant-table @env/*compiler*) (str (util/output-directory all-opts) "/constants_table.js"))) + _ (when (:infer-externs all-opts) + (comp/emit-inferred-externs-to-file + (reduce util/map-merge {} + (map (comp :externs second) + (get @compiler-env ::ana/namespaces))) + (str (util/output-directory all-opts) "/inferred_externs.js"))) optim (:optimizations all-opts) ret (if (and optim (not= optim :none)) (do From a732f07456c997b113a7c020e627cfe614ec31fc Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 11 Nov 2016 14:37:30 -0500 Subject: [PATCH 2097/4033] add cljs.analyzer/analyze-form-seq - useful for testing --- src/main/clojure/cljs/analyzer.cljc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9331589f5..3d8499441 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3330,6 +3330,26 @@ (when src (.setLastModified ^File cache-file (util/last-modified src)))))) +(defn analyze-form-seq + ([forms] + (analyze-form-seq forms nil)) + ([forms opts] + (let [env (assoc (empty-env) :build-options opts)] + (binding [*file-defs* nil + *unchecked-if* false + *cljs-ns* 'cljs.user + *cljs-file* nil + reader/*alias-map* (or reader/*alias-map* {})] + (loop [ns nil forms forms] + (if forms + (let [form (first forms) + env (assoc env :ns (get-namespace *cljs-ns*)) + ast (analyze env form nil opts)] + (if (= (:op ast) :ns) + (recur (:name ast) (next forms)) + (recur ns (next forms)))) + ns)))))) + #?(:clj (defn analyze-file "Given a java.io.File, java.net.URL or a string identifying a resource on the From cebfb586355b526253fdd25965de976b49a6973e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 12 Nov 2016 12:14:20 +0100 Subject: [PATCH 2098/4033] CLJS-1849: Self-host: regression introduced by CLJS-1794 --- src/main/clojure/cljs/analyzer.cljc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3d8499441..42dc42468 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2802,8 +2802,7 @@ :cljs [do]) (let [mchk #?(:clj (some-> (find-ns 'clojure.spec) (ns-resolve 'macroexpand-check)) - :cljs (and (find-ns 'cljs.spec) - ^::no-resolve cljs.spec/macroexpand-check)) + :cljs (get (ns-interns* 'cljs.spec) 'macroexpand-check)) _ (when mchk (mchk mac-var (next form))) form' (try From 617ce7d4e33f65352be3d6d4865ace2996d65bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 12 Nov 2016 12:20:28 +0100 Subject: [PATCH 2099/4033] CLJS-1850: *unchecked-if* not declared ^:dynamic warning after commit a732f0 --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 42dc42468..7bc8fd102 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3335,7 +3335,7 @@ ([forms opts] (let [env (assoc (empty-env) :build-options opts)] (binding [*file-defs* nil - *unchecked-if* false + #?@(:clj [*unchecked-if* false]) *cljs-ns* 'cljs.user *cljs-file* nil reader/*alias-map* (or reader/*alias-map* {})] From f108ce85e9364e041b932af8071ba918b2380b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 12 Nov 2016 13:35:59 +0100 Subject: [PATCH 2100/4033] CLJS-1851: Only output JS module processing time when `:compiler-stats` is true --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9d4111d71..63fb84945 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1850,7 +1850,7 @@ ;; e.g. commonjs module in :foreign-libs can depend on commonjs module from :ups-foreign-libs. js-modules (filter :module-type (concat (:foreign-libs opts) (:ups-foreign-libs opts)))] (if (seq js-modules) - (util/measure + (util/measure (:compiler-stats opts) "Process JS modules" (let [;; Load all modules - add :source so preprocessing and conversion can access it js-modules (map (fn [lib] From 9484a134bdf039c10ec3c26c8aaa3acd0dcd9875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 29 Oct 2016 21:14:21 +0200 Subject: [PATCH 2101/4033] CLJS-1194: Support for `data_readers.cljc` --- src/main/cljs/cljs/reader.clj | 20 ++++++++++ src/main/cljs/cljs/reader.cljs | 16 ++++---- src/main/clojure/cljs/closure.clj | 42 +++++++++++++++++++- src/main/clojure/cljs/env.cljc | 5 +++ src/main/clojure/cljs/repl.cljc | 1 - src/main/clojure/cljs/tagged_literals.cljc | 10 +++-- src/test/cljs/cljs/tagged_literals_test.cljs | 20 ++++++++++ src/test/cljs/data_readers.cljc | 11 +++++ src/test/cljs/test_runner.cljs | 6 ++- 9 files changed, 116 insertions(+), 15 deletions(-) create mode 100644 src/main/cljs/cljs/reader.clj create mode 100644 src/test/cljs/cljs/tagged_literals_test.cljs create mode 100644 src/test/cljs/data_readers.cljc diff --git a/src/main/cljs/cljs/reader.clj b/src/main/cljs/cljs/reader.clj new file mode 100644 index 000000000..6f8094bc0 --- /dev/null +++ b/src/main/cljs/cljs/reader.clj @@ -0,0 +1,20 @@ +;; 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.reader + (:require [cljs.env :as env] + [cljs.analyzer :as ana])) + +(defmacro add-data-readers [default-readers] + (let [data-readers (get @env/*compiler* ::ana/data-readers) + data-readers (into {} + (map (fn [[k v]] + [(str k) (:name (ana/resolve-var (dissoc &env :locals) v))])) + data-readers)] + `(do + (merge ~default-readers ~data-readers)))) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index 43dbbcd92..82101dc45 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -7,6 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.reader + (:require-macros [cljs.reader :refer [add-data-readers]]) (:require [goog.string :as gstring]) (:import goog.string.StringBuffer)) @@ -515,11 +516,11 @@ nil if the end of stream has been reached") (defn ^:private check [low n high msg] (when-not (<= low n high) - (reader-error nil (str msg " Failed: " low "<=" n "<=" high))) + (reader-error nil (str msg " Failed: " low "<=" n "<=" high))) n) (defn parse-and-validate-timestamp [s] - (let [[_ years months days hours minutes seconds fraction offset-sign offset-hours offset-minutes :as v] + (let [[_ years months days hours minutes seconds fraction offset-sign offset-hours offset-minutes :as v] (re-matches timestamp-regex s)] (if-not v (reader-error nil (str "Unrecognized date/time syntax: " s)) @@ -580,7 +581,7 @@ nil if the end of stream has been reached") (doseq [[k v] form] (aset obj (name k) v)) obj) - + :else (reader-error nil (str "JS literal expects a vector or map containing " @@ -594,10 +595,11 @@ nil if the end of stream has been reached") (reader-error nil "UUID literal expects a string as its representation."))) (def ^:dynamic *tag-table* - (atom {"inst" read-date - "uuid" read-uuid - "queue" read-queue - "js" read-js})) + (atom (add-data-readers + {"inst" read-date + "uuid" read-uuid + "queue" read-queue + "js" read-js}))) (def ^:dynamic *default-data-reader-fn* (atom nil)) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 63fb84945..9df2b68ff 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -44,7 +44,9 @@ [clojure.java.io :as io] [clojure.set :as set] [clojure.string :as string] - [clojure.data.json :as json]) + [clojure.data.json :as json] + [clojure.tools.reader :as reader] + [clojure.tools.reader.reader-types :as readers]) (:import [java.io File BufferedInputStream StringWriter] [java.net URL] [java.util.logging Level] @@ -1886,6 +1888,43 @@ opts js-modules))) opts))) +(defn- load-data-reader-file [mappings ^java.net.URL url] + (with-open [rdr (readers/input-stream-push-back-reader (.openStream url))] + (binding [*file* (.getFile url)] + (let [new-mappings (reader/read {:eof nil :read-cond :allow} rdr)] + (when (not (map? new-mappings)) + (throw (ex-info (str "Not a valid data-reader map") + {:url url}))) + (reduce + (fn [m [k v]] + (when (not (symbol? k)) + (throw (ex-info (str "Invalid form in data-reader file") + {:url url + :form k}))) + (when (and (contains? mappings k) + (not= (mappings k) v)) + (throw (ex-info "Conflicting data-reader mapping" + {:url url + :conflict k + :mappings m}))) + (assoc m k v)) + mappings + new-mappings))))) + +(defn get-data-readers* + "returns a merged map containing all data readers defined by libraries + on the classpath." + ([] + (get-data-readers* (. (Thread/currentThread) (getContextClassLoader)))) + ([classloader] + (let [data-reader-urls (enumeration-seq (. classloader (getResources "data_readers.cljc")))] + (reduce load-data-reader-file {} data-reader-urls)))) + +(def get-data-readers (memoize get-data-readers*)) + +(defn load-data-readers! [compiler] + (swap! compiler update-in [:cljs.analyzer/data-readers] merge (get-data-readers))) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] @@ -1946,6 +1985,7 @@ compile-opts (if one-file? (assoc all-opts :output-file (:output-to all-opts)) all-opts) + _ (load-data-readers! compiler-env) js-sources (-> (-find-sources source all-opts) (add-dependency-sources compile-opts) deps/dependency-order diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 1dbef55f3..4ff99ba77 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -29,6 +29,10 @@ state that is accessed/maintained by many different components."} ;; * :cljs.analyzer/constant-table - map of (currently only keyword) constant ;; values to fixed ids ;; * :cljs.analyzer/namespaces - map of symbols to "namespace" maps +;; * :cljs.analyzer/data-readers - literal map of symbols, where the first +;; symbol in each pair is a tag that will be recognized by the reader. The +;; second symbol in the pair is the fully-qualified name of a Var which will +;; be invoked by the reader to parse the form following the tag. ;; * :cljs.compiler/compiled-cljs - cache of intermediate compilation results ;; that speeds incremental builds in conjunction with source map generation ;; * :cljs.closure/compiled-cljs - cache from js file path to map of @@ -43,6 +47,7 @@ state that is accessed/maintained by many different components."} ([options] (atom (merge {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} :cljs.analyzer/constant-table {} + :cljs.analyzer/data-readers {} :options options} #?(:clj {:js-dependency-index (js-dependency-index options)}))))) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 095d46945..15dbabe2d 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -24,7 +24,6 @@ [cljs.analyzer.api :as ana-api] [cljs.env :as env] [cljs.js-deps :as deps] - [cljs.tagged-literals :as tags] [cljs.closure :as cljsc] [cljs.source-map :as sm]) (:import [java.io File PushbackReader FileWriter PrintWriter] diff --git a/src/main/clojure/cljs/tagged_literals.cljc b/src/main/clojure/cljs/tagged_literals.cljc index d14dc0b47..2374337f4 100644 --- a/src/main/clojure/cljs/tagged_literals.cljc +++ b/src/main/clojure/cljs/tagged_literals.cljc @@ -85,7 +85,9 @@ (JSValue. form)) (def ^:dynamic *cljs-data-readers* - {'queue read-queue - 'uuid read-uuid - 'inst read-inst - 'js read-js}) + (merge ;; assumes we can read all data_readers + #?(:clj *data-readers*) + {'queue read-queue + 'uuid read-uuid + 'inst read-inst + 'js read-js})) diff --git a/src/test/cljs/cljs/tagged_literals_test.cljs b/src/test/cljs/cljs/tagged_literals_test.cljs new file mode 100644 index 000000000..1635381c6 --- /dev/null +++ b/src/test/cljs/cljs/tagged_literals_test.cljs @@ -0,0 +1,20 @@ +;; 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.tagged-literals-test + (:require [cljs.test :refer-macros [deftest is]] + [cljs.reader :as reader])) + +(deftest test-identity-custom-literal + (is (= #cljs/tag [1 2 3] [1 2 3]))) + +(deftest test-runtime-reader + (is (object? (reader/read-string "#js {}"))) + (is (= {} (reader/read-string "#cljs/tag {}"))) + (is (= (reader/read-string "#cljs/inc 0") 1)) + (is (= (reader/read-string "#cljs/union #{1}") #{1}))) diff --git a/src/test/cljs/data_readers.cljc b/src/test/cljs/data_readers.cljc new file mode 100644 index 000000000..3204c001b --- /dev/null +++ b/src/test/cljs/data_readers.cljc @@ -0,0 +1,11 @@ +;; 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. + +{cljs/tag clojure.core/identity + cljs/inc clojure.core/inc + cljs/union clojure.set/union} diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 77e10b594..e269aa706 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -36,7 +36,8 @@ [cljs.spec-test] [cljs.clojure-alias-test] [cljs.hash-map-test] - [cljs.predicates-test])) + [cljs.predicates-test] + [cljs.tagged-literals-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -70,4 +71,5 @@ 'cljs.hash-map-test 'cljs.pprint-test 'cljs.predicates-test - 'cljs.syntax-quote-test) + 'cljs.syntax-quote-test + 'cljs.tagged-literals-test) From 1288204b043e00ca39b0c3c5af7fc8ac7eece816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 13 Nov 2016 10:59:01 +0100 Subject: [PATCH 2102/4033] CLJS-1848: Analyzer can't find JS modules during macro-expansion --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7bc8fd102..2002a1428 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -679,7 +679,10 @@ (defn js-module-exists? [module] - (-> (get-in @env/*compiler* [:js-module-index]) vals set (contains? module))) + ;; we need to check both keys and values of the JS module index, because + ;; macroexpansion will be looking for the provided name - António Monteiro + (-> (into #{} (mapcat identity) (get-in @env/*compiler* [:js-module-index])) + (contains? module))) (defn confirm-var-exists ([env prefix suffix] From 4abcec8b7af601cb21342a559f5ee731fb19f7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 13 Nov 2016 18:26:27 +0100 Subject: [PATCH 2103/4033] CLJS-1651: Self-host: Cannot replace core macro-function --- src/main/clojure/cljs/analyzer.cljc | 8 +++++--- src/test/self/self_host/test.cljs | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2002a1428..94d750a08 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1216,10 +1216,12 @@ (let [env (if (or (and (not= ns-name 'cljs.core) (core-name? env sym)) (get-in @env/*compiler* [::namespaces ns-name :uses sym])) - (let [ev (resolve-existing-var (dissoc env :locals) sym)] + (let [ev (resolve-existing-var (dissoc env :locals) sym) + conj-to-set (fnil conj #{})] (warning :redef env {:sym sym :ns (:ns ev) :ns-name ns-name}) - (swap! env/*compiler* update-in [::namespaces ns-name :excludes] conj sym) - (update-in env [:ns :excludes] conj sym)) + (swap! env/*compiler* update-in [::namespaces ns-name :excludes] + conj-to-set sym) + (update-in env [:ns :excludes] conj-to-set sym)) env) var-name (:name (resolve-var (dissoc env :locals) sym)) init-expr (when (contains? args :init) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 72e1ed399..ef86d2055 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -833,6 +833,23 @@ (is (== 1 value)) (inc! l)))))) +(deftest test-cljs-1651 + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(defn double [x] (* 2 x))" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "[(double 3) (apply double [3])]" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (= value [6 6])))))))) + (defn -main [& args] (run-tests)) From e01b8a0366f5c0519a6fa49025592bb345718235 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 17 Nov 2016 21:48:10 -0500 Subject: [PATCH 2104/4033] CLJS-1856: Self-host: load-deps doesn't delegate to itself --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index fc033200d..3a4e7cbed 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -342,7 +342,7 @@ (defn- load-deps ([bound-vars ana-env lib deps cb] - (analyze-deps bound-vars ana-env lib deps nil nil cb)) + (load-deps bound-vars ana-env lib deps nil nil cb)) ([bound-vars ana-env lib deps reload opts cb] (when (:verbose opts) (debug-prn "Loading dependencies for" lib)) From d2cb3eeca6f37ef36e28be9975db3816d04355a1 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Wed, 16 Nov 2016 17:17:50 +0000 Subject: [PATCH 2105/4033] CLJS-1855: Subvec should implement IIterable --- src/main/cljs/cljs/core.cljs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5079b4d29..976918e51 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5450,7 +5450,11 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-nth coll k)) (-invoke [coll k not-found] - (-nth coll k not-found))) + (-nth coll k not-found)) + + IIterable + (-iterator [coll] + (ranged-iterator v start end))) (es6-iterable Subvec) From 6602f769ed4d52fd67577aacaf9cfe6db05b8ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 13 Nov 2016 13:40:57 +0100 Subject: [PATCH 2106/4033] CLJS-1816: Basic timing info in verbose output Adds basic compilation timing info per namespace when both `:verbose` and `compiler-stats` are true. --- src/main/clojure/cljs/closure.clj | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9df2b68ff..fc4af21ca 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -773,17 +773,20 @@ (loop [ns-info (.pollFirst deque)] (when (and ns-info (not @failed)) (let [{:keys [requires]} ns-info - input-set' @input-set] + input-set' @input-set + {:keys [compiler-stats verbose]} opts] (if (every? #(not (contains? input-set' %)) requires) (do (try - (swap! compiled conj - (-compile (or (:source-file ns-info) - (:source-forms ns-info)) - ; - ns-info -> ns -> cljs file relpath -> js relpath - (merge opts - {:output-file (comp/rename-to-js - (util/ns->relpath (:ns ns-info)))}))) + (util/measure (and compiler-stats verbose) + (str "Compile " (:ns ns-info)) + (swap! compiled conj + (-compile (or (:source-file ns-info) + (:source-forms ns-info)) + ; - ns-info -> ns -> cljs file relpath -> js relpath + (merge opts + {:output-file (comp/rename-to-js + (util/ns->relpath (:ns ns-info)))})))) (catch Throwable e (reset! failed e))) (when-not @failed @@ -828,10 +831,12 @@ (for [ns-info inputs] ; TODO: compile-file calls parse-ns unnecessarily to get ns-info ; TODO: we could mark dependent namespaces for recompile here - (-compile (or (:source-file ns-info) - (:source-forms ns-info)) - ; - ns-info -> ns -> cljs file relpath -> js relpath - (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))))))))) + (util/measure (and compiler-stats (:verbose opts)) + (str "Compile " (:ns ns-info)) + (-compile (or (:source-file ns-info) + (:source-forms ns-info)) + ; - ns-info -> ns -> cljs file relpath -> js relpath + (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))})))))))))) (defn add-goog-base [inputs] From 417350ddabea283ef8f576b8e361a249d9bfb9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 13 Nov 2016 15:20:45 +0100 Subject: [PATCH 2107/4033] CLJS-1643: Emit more informative error when emitting a type which has no emit multimethod case --- src/main/clojure/cljs/compiler.cljc | 8 ++++++++ src/test/clojure/cljs/compiler_tests.clj | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 0acab39a5..ca8582205 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -204,6 +204,14 @@ :cljs (defmulti emit-constant type)) +(defmethod emit-constant :default + [x] + (throw + (ex-info (str "failed compiling constant: " x "; " + (type x) " is not a valid ClojureScript constant.") + {:constant x + :type (type x)}))) + (defmethod emit-constant nil [x] (emits "null")) #?(:clj diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index fb030d5db..5b02f5fc3 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -189,6 +189,10 @@ (bar [this] (cljs.user/foo this))))))) +(deftest test-cljs-1643 + (is (thrown-with-msg? Exception #"is not a valid ClojureScript constant." + (comp/emit-constant clojure.core/inc)))) + ;; CLJS-1225 (comment @@ -212,4 +216,4 @@ (if ^boolean (goog.array/isEmpty x) true false)))))) - ) \ No newline at end of file + ) From 33a7e5bcac763d40ca684404cf772e9745d264ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 13 Nov 2016 15:51:47 +0100 Subject: [PATCH 2108/4033] CLJS-1616: Self-host: improve documentation for compile-str --- src/main/cljs/cljs/js.cljs | 81 +++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 15 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 3a4e7cbed..7abb17836 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -590,8 +590,21 @@ opts (map) compilation options. - :eval - the eval function to invoke, see *eval-fn* - :load - library resolution function, see *load-fn* + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :def-emits-var - sets whether def (and derived) forms return either a Var + (if set to true) or the def init value (if false). Default + is false. + :static-fns - employ static dispatch to specific function arities in + emitted JavaScript, as opposed to making use of the + `call` construct. Default is false. + :ns - optional, the namespace in which to evaluate the source. + :verbose - optional, emit details from compiler activity. Defaults to + false. + :context - optional, sets the context for the source. Possible values + are `:expr`, `:statement` and `:return`. Defaults to + `:expr`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -666,8 +679,21 @@ opts (map) compilation options. - :eval - the eval function to invoke, see *eval-fn* - :load - library resolution function, see *load-fn* + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :def-emits-var - sets whether def (and derived) forms return either a Var + (if set to true) or the def init value (if false). Default + is false. + :static-fns - employ static dispatch to specific function arities in + emitted JavaScript, as opposed to making use of the + `call` construct. Default is false. + :ns - optional, the namespace in which to evaluate the source. + :verbose - optional, emit details from compiler activity. Defaults to + false. + :context - optional, sets the context for the source. Possible values + are `:expr`, `:statement` and `:return`. Defaults to + `:expr`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -758,8 +784,21 @@ opts (map) compilation options. - :load - library resolution function, see *load-fn* - :source-map - set to true to generate inline source map information + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :def-emits-var - sets whether def (and derived) forms return either a Var + (if set to true) or the def init value (if false). Default + is false. + :static-fns - employ static dispatch to specific function arities in + emitted JavaScript, as opposed to making use of the + `call` construct. Default is false. + :ns - optional, the namespace in which to evaluate the source. + :verbose - optional, emit details from compiler activity. Defaults to + false. + :context - optional, sets the context for the source. Possible values + are `:expr`, `:statement` and `:return`. Defaults to + `:expr`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -883,15 +922,27 @@ opts (map) compilation options. - :eval - eval function to invoke, see *eval-fn* - :load - library resolution function, see *load-fn* - :source-map - set to true to generate inline source map information - :cache-source - optional, a function to run side-effects with the - compilation result prior to actual evalution. This function - takes two arguments, the first is the eval map, the source - will be under :source. The second argument is a callback of - one argument. If an error occurs an :error key should be - supplied. + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :cache-source - optional, a function to run side-effects with the + compilation result prior to actual evalution. This function + takes two arguments, the first is the eval map, the source + will be under :source. The second argument is a callback of + one argument. If an error occurs an :error key should be + supplied. + :def-emits-var - sets whether def (and derived) forms return either a Var + (if set to true) or the def init value (if false). Default + is false. + :static-fns - employ static dispatch to specific function arities in + emitted JavaScript, as opposed to making use of the + `call` construct. Default is false. + :ns - optional, the namespace in which to evaluate the source. + :verbose - optional, emit details from compiler activity. Defaults to + false. + :context - optional, sets the context for the source. Possible values + are `:expr`, `:statement` and `:return`. Defaults to + `:expr`. cb (function) callback, will be invoked with a map. If succesful the map will contain From 44d5e56b277e007e835a902abfd2afd5be6bb056 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Nov 2016 12:27:20 -0500 Subject: [PATCH 2109/4033] add helpers for indexing externs and returning the indexed default set --- src/main/clojure/cljs/externs.clj | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 4bf31dbcd..94d615d25 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -7,9 +7,11 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.externs - (:require [clojure.string :as string]) + (:require [clojure.string :as string] + [cljs.util :as util]) (:import [java.util.logging Level] - [com.google.javascript.jscomp CompilerOptions SourceFile JsAst] + [com.google.javascript.jscomp + CompilerOptions SourceFile JsAst CommandLineRunner] [com.google.javascript.rhino Node Token])) ;; ------------------------------------------------------------------------------ @@ -80,3 +82,22 @@ (let [node (first nodes) new-extern (parse-extern-node node)] (recur (rest nodes) (concat externs new-extern))))))) + +(defn index-externs [externs] + (reduce + (fn [m xs] + (cond-> m + (seq xs) (update-in xs merge {}))) + {} externs)) + +(defn default-externs [] + (let [xs (CommandLineRunner/getDefaultExterns)] + (reduce + (fn [externs externs-file] + (util/map-merge + externs (index-externs (parse-externs externs-file)))) + {} xs))) + +(comment + (default-externs) + ) \ No newline at end of file From 0560106fe8576fd7029d86e61be09cd1f246bfe0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Nov 2016 13:34:34 -0500 Subject: [PATCH 2110/4033] add default indexed externs into the compiler env, don't add externs entries for ones that already exist --- src/main/clojure/cljs/analyzer.cljc | 8 +++++++- src/main/clojure/cljs/env.cljc | 16 ++++++++++------ src/test/clojure/cljs/analyzer_tests.clj | 21 ++++++++++++++++++++- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 94d750a08..3410815fb 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -760,7 +760,13 @@ (do (when (contains? locals (-> sym name symbol)) (warning :js-shadowed-by-local env {:name sym})) - {:name sym :ns 'js}) + (let [pre (->> (string/split (name sym) #"\.") (map symbol) vec)] + (when-not (get-in @env/*compiler* (into [::externs] pre)) + (swap! env/*compiler* update-in + (into [::namespaces (-> env :ns :name) :externs] pre) merge {})) + {:name sym + :ns 'js + :tag (with-meta 'js {:prefix pre})})) (let [s (str sym) lb (get locals sym)] (cond diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 4ff99ba77..6de2d8bba 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -9,7 +9,8 @@ (ns ^{:doc "A namespace that exists solely to provide a place for \"compiler\" state that is accessed/maintained by many different components."} cljs.env - #?(:clj (:require [cljs.js-deps :refer (js-dependency-index)])) + #?(:clj (:require [cljs.js-deps :refer (js-dependency-index)] + [cljs.externs :as externs])) (:refer-clojure :exclude [ensure])) ;; bit of a misnomer, but: an atom containing a map that serves as the bag of @@ -45,11 +46,14 @@ state that is accessed/maintained by many different components."} (defn default-compiler-env ([] (default-compiler-env {})) ([options] - (atom (merge {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} - :cljs.analyzer/constant-table {} - :cljs.analyzer/data-readers {} - :options options} - #?(:clj {:js-dependency-index (js-dependency-index options)}))))) + (atom + (merge + {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} + :cljs.analyzer/constant-table {} + :cljs.analyzer/data-readers {} + :cljs.analyzer/externs (externs/default-externs) + :options options} + #?(:clj {:js-dependency-index (js-dependency-index options)}))))) #?(:clj (defmacro with-compiler-env diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 503948e16..faaa9b6b3 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -12,7 +12,8 @@ [cljs.env :as e] [cljs.env :as env] [cljs.analyzer.api :as ana-api] - [cljs.util :as util]) + [cljs.util :as util] + [cljs.externs :as externs]) (:use clojure.test)) (defn collecting-warning-handler [state] @@ -604,3 +605,21 @@ (a/analyze (assoc test-env :def-emits-var true) '(let [y 1] (def y 2))))] (is (some? (-> parsed :expr :ret :var-ast))))) + +(comment + (require '[cljs.compiler :as cc]) + + ;; empty? + (let [test-cenv (atom {::a/externs (externs/default-externs)})] + (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns foo.core) + (defn bar [a b] (+ a b)) + (def c js/React.Component) + (js/console.log "Hello world!")])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + ) \ No newline at end of file From d961e66edfd3d62f80bed18418c597ee1f569e85 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Nov 2016 14:48:06 -0500 Subject: [PATCH 2111/4033] we were not correctly propagating type hints for extend-type methods --- src/main/clojure/cljs/core.cljc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 590e294e3..b0c03760d 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1401,9 +1401,10 @@ ~@body))) (core/defn- adapt-proto-params [type [[this & args :as sig] & body]] - `(~(vec (cons (vary-meta this assoc :tag type) args)) - (this-as ~this - ~@body))) + (let [this' (vary-meta this assoc :tag type)] + `(~(vec (cons this' args)) + (this-as ~this' + ~@body)))) (core/defn- add-obj-methods [type type-sym sigs] (map (core/fn [[f & meths :as form]] From 3eb2663d1a707bf98c68ea1c86d195835c54fbba Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Nov 2016 16:09:13 -0500 Subject: [PATCH 2112/4033] first draft of externs inference --- src/main/cljs/cljs/core.cljs | 1 + src/main/clojure/cljs/analyzer.cljc | 72 ++++++++++++++++++++---- src/main/clojure/cljs/compiler.cljc | 5 +- src/test/clojure/cljs/analyzer_tests.clj | 23 ++++---- 4 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 976918e51..1b5aaad44 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -18,6 +18,7 @@ (def *clojurescript-version*) (def *unchecked-if* false) +(def *warn-on-infer* false) (defonce PROTOCOL_SENTINEL #js {}) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3410815fb..41ea6c7fc 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -139,7 +139,8 @@ :extend-type-invalid-method-shape true :unsupported-js-module-type true :unsupported-preprocess-value true - :js-shadowed-by-local true}) + :js-shadowed-by-local true + :infer-warning false}) (def js-reserved #{"arguments" "abstract" "boolean" "break" "byte" "case" @@ -414,6 +415,10 @@ [warning-type {:keys [name]}] (str name " is shadowed by a local")) +(defmethod error-message :infer-warning + [warning-type {:keys [form]}] + (str "Cannot infer target type for " form "")) + (defn default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) (when-let [s (error-message warning-type extra)] @@ -749,6 +754,21 @@ (= (.getName ns) #?(:clj 'cljs.core :cljs 'cljs.core$macros))))) (not (contains? (-> env :ns :excludes) sym)))) +(defn js-tag? [x] + (if (symbol? x) + (or (= 'js x) + (= "js" (namespace x))) + false)) + +(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)}) + x)) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -1701,13 +1721,18 @@ (disallowing-recur (let [enve (assoc env :context :expr) targetexpr (cond - (and - (= target '*unchecked-if*) ;; TODO: proper resolve - (or (true? val) (false? val))) + (and (= target '*unchecked-if*) ;; TODO: proper resolve + (or (true? val) (false? val))) (do (set! *unchecked-if* val) ::set-unchecked-if) + (and + (= target '*warn-on-infer*)) + (do + (set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning true)) + ::set-warn-on-infer) + (symbol? target) (do (when (:const (resolve-var (dissoc env :locals) target)) @@ -1730,9 +1755,12 @@ (when-not targetexpr (throw (error env "set! target must be a field or a symbol naming a var"))) (cond - (= targetexpr ::set-unchecked-if) {:env env :op :no-op} - :else {:env env :op :set! :form form :target targetexpr :val valexpr - :children [targetexpr valexpr]}))))) + (#{::set-unchecked-if ::set-warn-on-infer} targetexpr) + {:env env :op :no-op} + + :else + {:env env :op :set! :form form :target targetexpr :val valexpr + :children [targetexpr valexpr]}))))) (declare analyze-file) @@ -2515,7 +2543,24 @@ enve (assoc env :context :expr) targetexpr (analyze enve target) form-meta (meta form) - tag (:tag form-meta)] + target-tag (:tag targetexpr) + prop (or field method) + tag (or (:tag form-meta) + (and (js-tag? target-tag) + (vary-meta (normalize-js-tag target-tag) + update-in [:prefix] (fnil conj []) prop)) + nil)] + (when (and (not (string/starts-with? (str prop) "cljs$")) + (not= 'js target-tag) + (get-in env [:locals target])) + (when (or (nil? target-tag) + ('#{any} target-tag)) + (warning :infer-warning env {:form form}))) + (when (js-tag? tag) + (let [pre (-> tag meta :prefix)] + (when-not (get-in @env/*compiler* (into [::externs] pre)) + (swap! env/*compiler* update-in + (into [::namespaces (-> env :ns :name) :externs] pre) merge {})))) (case dot-action ::access (let [children [targetexpr]] {:op :dot @@ -2579,8 +2624,10 @@ ;; when functions like first won't return nil, so variadic ;; numeric functions like cljs.core/< would produce a spurious ;; warning without this - David - (if (nil? t) - true + (cond + (nil? t) true + (js-tag? t) true ;; TODO: revisit + :else (if (and (symbol? t) (not (nil? (get NUMERIC_SET t)))) true (when #?(:clj (set? t) @@ -3375,8 +3422,9 @@ ([f opts] (analyze-file f false opts)) ([f skip-cache opts] - (binding [*file-defs* (atom #{}) - *unchecked-if* false] + (binding [*file-defs* (atom #{}) + *unchecked-if* false + *cljs-warnings* *cljs-warnings*] (let [output-dir (util/output-directory opts) res (cond (instance? File f) f diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index ca8582205..6cd0c5039 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1381,8 +1381,9 @@ (compile-file src dest nil)) ([src dest opts] {:post [map?]} - (binding [ana/*file-defs* (atom #{}) - ana/*unchecked-if* false] + (binding [ana/*file-defs* (atom #{}) + ana/*unchecked-if* false + ana/*cljs-warnings* ana/*cljs-warnings*] (let [nses (get @env/*compiler* ::ana/namespaces) src-file (io/file src) dest-file (io/file dest) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index faaa9b6b3..7e38824d0 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -611,15 +611,16 @@ ;; empty? (let [test-cenv (atom {::a/externs (externs/default-externs)})] - (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env test-cenv - (a/analyze-form-seq - '[(ns foo.core) - (defn bar [a b] (+ a b)) - (def c js/React.Component) - (js/console.log "Hello world!")])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns foo.core) + (defn bar [a b] (+ a (.render b))) + (def c js/React.Component) + (js/console.log "Hello world!")])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) ) \ No newline at end of file From 01149751ae129ab01f5d427a48c5cedae9386f64 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Nov 2016 16:27:18 -0500 Subject: [PATCH 2113/4033] fix Number.MAX-VALUE typo in core.cljs --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1b5aaad44..ba2a9839e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8931,7 +8931,7 @@ reduces them without incurring seq initialization" "Returns a lazy seq of nums from start (inclusive) to end (exclusive), by step, where start defaults to 0, step to 1, and end to infinity." - ([] (range 0 (.-MAX-VALUE js/Number) 1)) + ([] (range 0 (.-MAX_VALUE js/Number) 1)) ([end] (range 0 end 1)) ([start end] (range start end 1)) ([start end step] (Range. nil start end step nil))) From 90b59fce092b53b5a85f3a403ed9c0767ad8b974 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Nov 2016 16:49:41 -0500 Subject: [PATCH 2114/4033] need to use the same default-extern? pred in resolve-var --- src/main/clojure/cljs/analyzer.cljc | 12 ++++++++++-- src/test/clojure/cljs/analyzer_tests.clj | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 41ea6c7fc..a882c83a3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -769,6 +769,14 @@ 'prototype)}) x)) +(defn default-extern? [pre] + (let [externs (get @env/*compiler* ::externs)] + (or (get-in externs pre) + (when (= 1 (count pre)) + (let [x (first pre)] + (or (get-in externs (conj '[Window prototype] x)) + (get-in externs (conj '[Number] x)))))))) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -781,7 +789,7 @@ (when (contains? locals (-> sym name symbol)) (warning :js-shadowed-by-local env {:name sym})) (let [pre (->> (string/split (name sym) #"\.") (map symbol) vec)] - (when-not (get-in @env/*compiler* (into [::externs] pre)) + (when-not (default-extern? pre) (swap! env/*compiler* update-in (into [::namespaces (-> env :ns :name) :externs] pre) merge {})) {:name sym @@ -2558,7 +2566,7 @@ (warning :infer-warning env {:form form}))) (when (js-tag? tag) (let [pre (-> tag meta :prefix)] - (when-not (get-in @env/*compiler* (into [::externs] pre)) + (when-not (default-extern? pre) (swap! env/*compiler* update-in (into [::namespaces (-> env :ns :name) :externs] pre) merge {})))) (case dot-action diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 7e38824d0..1323a2d14 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -616,7 +616,7 @@ (e/with-compiler-env test-cenv (a/analyze-form-seq '[(ns foo.core) - (defn bar [a b] (+ a (.render b))) + (defn bar [a] (js/parseInt a)) (def c js/React.Component) (js/console.log "Hello world!")])) (cc/emit-externs From 632b794cffdb00da2e3759cec20284bae84db1da Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 18 Nov 2016 16:54:18 -0500 Subject: [PATCH 2115/4033] if there is no prefix assume Object --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a882c83a3..23f0a1651 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2556,7 +2556,7 @@ tag (or (:tag form-meta) (and (js-tag? target-tag) (vary-meta (normalize-js-tag target-tag) - update-in [:prefix] (fnil conj []) prop)) + update-in [:prefix] (fnil conj '[Object]) prop)) nil)] (when (and (not (string/starts-with? (str prop) "cljs$")) (not= 'js target-tag) From c0c8ce446e9b5bf425d46d136e17b3a208ed32e0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 19 Nov 2016 08:22:34 -0500 Subject: [PATCH 2116/4033] disable :infer-warning at the REPL for now --- src/main/clojure/cljs/repl.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 15dbabe2d..d1f67dfbd 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -791,7 +791,7 @@ (merge ana/*cljs-warnings* (if (or (true? warnings) - (false? warnings)) + (false? warnings)) (zipmap (keys ana/*cljs-warnings*) (repeat warnings)) warnings) (zipmap @@ -799,7 +799,8 @@ :undeclared-ns :undeclared-ns-form] (repeat (if (false? warnings) false - warn-on-undeclared))))) + warn-on-undeclared))) + {:infer-warning false})) ana/*cljs-static-fns* static-fns *repl-opts* opts] (let [env {:context :expr :locals {}} From cfeb14721b9d9dced1ad3bb49cd1899a588529ed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 19 Nov 2016 13:30:40 -0500 Subject: [PATCH 2117/4033] ignore protocol extensions on js/Foo constructors, formatting, examining how to prevent extern generation around js/console --- src/main/clojure/cljs/analyzer.cljc | 9 +++++---- src/main/clojure/cljs/externs.clj | 26 +++++++++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 23f0a1651..d5cac330d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -772,10 +772,11 @@ (defn default-extern? [pre] (let [externs (get @env/*compiler* ::externs)] (or (get-in externs pre) - (when (= 1 (count pre)) - (let [x (first pre)] - (or (get-in externs (conj '[Window prototype] x)) - (get-in externs (conj '[Number] x)))))))) + (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 resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 94d615d25..f8c67d0ad 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -68,11 +68,12 @@ (defn parse-externs [^SourceFile source-file] (let [^CompilerOptions compiler-options (CompilerOptions.) - closure-compiler (doto - (let [compiler (com.google.javascript.jscomp.Compiler.)] - (com.google.javascript.jscomp.Compiler/setLoggingLevel Level/WARNING) - compiler) - (.init (list source-file) '() compiler-options)) + closure-compiler + (doto + (let [compiler (com.google.javascript.jscomp.Compiler.)] + (com.google.javascript.jscomp.Compiler/setLoggingLevel Level/WARNING) + compiler) + (.init (list source-file) '() compiler-options)) js-ast (JsAst. source-file) ^Node root (.getAstRoot js-ast closure-compiler)] (loop [nodes (.children root) @@ -100,4 +101,19 @@ (comment (default-externs) + + ;; webkit_dom.js defines Console and Window.prototype.console + (filter + (fn [s] + (let [m (-> s parse-externs index-externs)] + (get-in m '[Window prototype console]))) + (CommandLineRunner/getDefaultExterns)) + + (-> + (filter + (fn [s] + (= "externs.zip//webkit_dom.js" (.getName s))) + (CommandLineRunner/getDefaultExterns)) + first parse-externs index-externs) + ) \ No newline at end of file From e156e74fcd1dfa70fb0f35d187825dd9b131431c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 20 Nov 2016 20:12:10 +0100 Subject: [PATCH 2118/4033] CLJS-1857: Fix self-host tests --- src/main/clojure/cljs/core.cljc | 2 +- src/main/clojure/cljs/env.cljc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b0c03760d..3d3ba6fe3 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1401,7 +1401,7 @@ ~@body))) (core/defn- adapt-proto-params [type [[this & args :as sig] & body]] - (let [this' (vary-meta this assoc :tag type)] + (core/let [this' (vary-meta this assoc :tag type)] `(~(vec (cons this' args)) (this-as ~this' ~@body)))) diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 6de2d8bba..6517a1cee 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -51,7 +51,8 @@ state that is accessed/maintained by many different components."} {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} :cljs.analyzer/constant-table {} :cljs.analyzer/data-readers {} - :cljs.analyzer/externs (externs/default-externs) + :cljs.analyzer/externs #?(:clj (externs/default-externs) + :cljs nil) :options options} #?(:clj {:js-dependency-index (js-dependency-index options)}))))) From c0351cd981c342fd29983757269fecefe0b1ea11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 21 Nov 2016 23:31:33 +0100 Subject: [PATCH 2119/4033] CLJS-1858: Should allow `:cache-analysis true` and `cache-analysis-format nil` --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index fc4af21ca..365d1b534 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1794,7 +1794,9 @@ (println "WARNING: :preloads should only be specified with :none optimizations")))) (defn check-cache-analysis-format [{:keys [cache-analysis cache-analysis-format] :as opts}] - (assert (not (and cache-analysis ((complement #{nil :edn :transit}) cache-analysis-format))) + (assert (not (and cache-analysis + ((complement #{:edn :transit}) cache-analysis-format) + (not (nil? cache-analysis-format)))) (format ":cache-analysis format must be :edn or :transit but it is: %s" (pr-str cache-analysis-format)))) From 170fd767752a4839b25038c86b2d6a6aa3b25ab7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 25 Nov 2016 14:31:50 -0500 Subject: [PATCH 2120/4033] CLJS-1861: Use usr/bin/env in build scripts for portability --- script/aot_core | 2 +- script/self-compile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/aot_core b/script/aot_core index a5762c4ab..351ced5df 100755 --- a/script/aot_core +++ b/script/aot_core @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex diff --git a/script/self-compile b/script/self-compile index 04dec567e..22026f016 100755 --- a/script/self-compile +++ b/script/self-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash rm -rf classes mkdir classes From 79a20afe360249ab6cb652f4465b7ccd01a923f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 24 Nov 2016 12:38:31 +0100 Subject: [PATCH 2121/4033] CLJS-1860: Resolve JS modules referred by their fully-qualified namespace --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d5cac330d..292dafb6f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -807,7 +807,10 @@ :cljs (identical? "clojure.core" ns)) "cljs.core" ns) - full-ns (resolve-ns-alias env ns)] + full-ns (resolve-ns-alias env ns + (or (and (js-module-exists? ns) + (get-in @env/*compiler* [:js-module-index ns])) + (symbol ns)))] (when-not (nil? confirm) (when (not= (-> env :ns :name) full-ns) (confirm-ns env full-ns)) From 0ec11d2f18672949614b890b5f3002a939e35a88 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 28 Nov 2016 14:28:48 -0500 Subject: [PATCH 2122/4033] we cannot use the var trick in ClojureScript, instead wrap in single argument fns where we do not resolve the var --- src/main/cljs/cljs/reader.clj | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/reader.clj b/src/main/cljs/cljs/reader.clj index 6f8094bc0..93571df51 100644 --- a/src/main/cljs/cljs/reader.clj +++ b/src/main/cljs/cljs/reader.clj @@ -11,10 +11,9 @@ [cljs.analyzer :as ana])) (defmacro add-data-readers [default-readers] - (let [data-readers (get @env/*compiler* ::ana/data-readers) - data-readers (into {} - (map (fn [[k v]] - [(str k) (:name (ana/resolve-var (dissoc &env :locals) v))])) - data-readers)] - `(do - (merge ~default-readers ~data-readers)))) + (let [data-readers + (->> (get @env/*compiler* ::ana/data-readers) + (map (fn [[k v]] + [(str k) `(fn [x#] (~(vary-meta v assoc ::ana/no-resolve true) x#))])) + (into {}))] + `(do (merge ~default-readers ~data-readers)))) From 5781cb84bd746c18f86ffcafbca5c91dd05a6fa4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 2 Dec 2016 16:40:50 -0600 Subject: [PATCH 2123/4033] type hint equiv-map --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ba2a9839e..9a71d9d92 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5811,7 +5811,7 @@ reduces them without incurring seq initialization" (def ^:private never-equiv (NeverEquiv.)) -(defn- equiv-map +(defn- ^boolean equiv-map "Assumes y is a map. Returns true if x equals y, otherwise returns false." [x y] From 97d2d61e78ce747d02d0e5b2ced706f6fb68ec4e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 2 Dec 2016 17:10:01 -0600 Subject: [PATCH 2124/4033] CLJS-1718: Foreign lib files should be placed in a relative location --- src/main/clojure/cljs/closure.clj | 2 +- src/main/clojure/cljs/util.cljc | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 365d1b534..e19d43be1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1470,7 +1470,7 @@ url (cond (deps/-closure-lib? js) (lib-rel-path js) - (deps/-foreign? js) (util/get-name url) + (deps/-foreign? js) (util/relative-name url) :else (path-from-jarfile url)) (string? js) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index e97c9b9ce..6afbcfe16 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -142,6 +142,17 @@ (filename x) (last (string/split (path x) #"\/")))) +(defn ^String relative-name + "Given a file return a path relative to the working directory. Given a + URL return the JAR relative path of the resource." + [x] + {:pre [(or (file? x) (url? x))]} + (if (file? x) + (string/replace + (.getAbsolutePath x) + (str (System/getProperty "user.dir") File/separator)) + (last (string/split (.getFile x) #"\.jar!/")))) + (defn last-modified [src] (cond (file? src) (.lastModified ^File src) From 53cc7fbbccbd01667f3c843226cc9b6c394d7ee8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 7 Dec 2016 09:14:47 -0500 Subject: [PATCH 2125/4033] fix bad clojure.string/replace invoke in cljs.util/relative-name --- src/main/clojure/cljs/util.cljc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 6afbcfe16..49950ef88 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -148,9 +148,8 @@ [x] {:pre [(or (file? x) (url? x))]} (if (file? x) - (string/replace - (.getAbsolutePath x) - (str (System/getProperty "user.dir") File/separator)) + (string/replace (.getAbsolutePath x) + (str (System/getProperty "user.dir") File/separator) "") (last (string/split (.getFile x) #"\.jar!/")))) (defn last-modified [src] From 74405dfbfd7f6ce00c4ff0270e1797f95050e077 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Dec 2016 08:48:21 -0500 Subject: [PATCH 2126/4033] snippet demonstrating interacting with JSDocInfo --- src/main/clojure/cljs/externs.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index f8c67d0ad..d6d61f5f1 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -116,4 +116,12 @@ (CommandLineRunner/getDefaultExterns)) first parse-externs index-externs) + ;; interacting with JSDocInfo (put this in dispatch fn) + (let [info (.getJSDocInfo node)] + (when info + (when (or (.isConstructor info) (.isInterface info)) + (println node (.. node getFirstChild getQualifiedName))) + (when-let [ty (.getType info)] + (println ">>>>>" node ty))) + (.getType node)) ) \ No newline at end of file From bf934f4b43aa0dc61d3436ca135417a57130d159 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 12:33:56 -0500 Subject: [PATCH 2127/4033] first pass at returning annotated indexed externs --- src/main/clojure/cljs/externs.clj | 50 ++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index d6d61f5f1..8a5181c85 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -12,16 +12,40 @@ (:import [java.util.logging Level] [com.google.javascript.jscomp CompilerOptions SourceFile JsAst CommandLineRunner] - [com.google.javascript.rhino Node Token])) + [com.google.javascript.rhino + Node Token JSTypeExpression])) ;; ------------------------------------------------------------------------------ ;; Externs Parsing -(defmulti parse-extern-node (fn [^Node node] (.getType node))) +(defn annotate [props ty] + (conj + (into [] (butlast props)) + (vary-meta (last props) assoc :tag ty))) + +(defn get-type [^Node node] + (when node + (let [info (.getJSDocInfo node)] + (when info + (if-let [^JSTypeExpression ty (.getType info)] + (when-let [root (.getRoot ty)] + (if (.isString root) + (symbol (.getString root)) + (if-let [child (.. root getFirstChild)] + (if (.isString child) + (symbol (.. child getString)))))) + (when (or (.isConstructor info) (.isInterface info)) + (symbol (.. node getFirstChild getQualifiedName)))))))) + +(defmulti parse-extern-node + (fn [^Node node] + (.getType node))) (defmethod parse-extern-node Token/VAR [node] (when (> (.getChildCount node) 0) - (parse-extern-node (.getFirstChild node)))) + (let [ty (get-type node)] + (cond-> (parse-extern-node (.getFirstChild node)) + ty (-> first (annotate ty) vector))))) (defmethod parse-extern-node Token/EXPR_RESULT [node] (when (> (.getChildCount node) 0) @@ -45,7 +69,10 @@ [lhs]))) (defmethod parse-extern-node Token/GETPROP [node] - [(map symbol (string/split (.getQualifiedName node) #"\."))]) + (let [props (map symbol (string/split (.getQualifiedName node) #"\."))] + [(if-let [ty (get-type node)] + (annotate props ty) + props)])) (defmethod parse-extern-node Token/OBJECTLIT [node] (when (> (.getChildCount node) 0) @@ -102,6 +129,9 @@ (comment (default-externs) + (-> (default-externs) + (find 'console) first meta) + ;; webkit_dom.js defines Console and Window.prototype.console (filter (fn [s] @@ -114,14 +144,6 @@ (fn [s] (= "externs.zip//webkit_dom.js" (.getName s))) (CommandLineRunner/getDefaultExterns)) - first parse-externs index-externs) - - ;; interacting with JSDocInfo (put this in dispatch fn) - (let [info (.getJSDocInfo node)] - (when info - (when (or (.isConstructor info) (.isInterface info)) - (println node (.. node getFirstChild getQualifiedName))) - (when-let [ty (.getType info)] - (println ">>>>>" node ty))) - (.getType node)) + first parse-externs index-externs + (find 'console) first meta) ) \ No newline at end of file From 20f838a4b7c4d89c02f7e9e00098bf9fbb0f806c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 13:35:20 -0500 Subject: [PATCH 2128/4033] testing React externs --- src/main/clojure/cljs/externs.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 8a5181c85..4e8f47f22 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -146,4 +146,12 @@ (CommandLineRunner/getDefaultExterns)) first parse-externs index-externs (find 'console) first meta) + + (require '[clojure.java.io :as io] + '[cljs.closure :as cc]) + + (-> (cc/js-source-file nil (io/file "react.ext.js")) + parse-externs index-externs + (get 'React) + (find 'Component) first meta) ) \ No newline at end of file From 8bb85bdc646faff235cfec474c8e81a4d6e7f437 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 13:57:30 -0500 Subject: [PATCH 2129/4033] we need to calculate types for assignment nodes as well --- src/main/clojure/cljs/externs.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 4e8f47f22..1f916045e 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -53,7 +53,9 @@ (defmethod parse-extern-node Token/ASSIGN [node] (when (> (.getChildCount node) 0) - (let [lhs (first (parse-extern-node (.getFirstChild node)))] + (let [ty (get-type node) + lhs (cond-> (first (parse-extern-node (.getFirstChild node))) + ty (annotate ty))] (if (> (.getChildCount node) 1) (let [externs (parse-extern-node (.getChildAtIndex node 1))] (conj (map (fn [ext] (concat lhs ext)) externs) From 251478c623a10109bc7b4ae30375829b49d869eb Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 14:58:29 -0500 Subject: [PATCH 2130/4033] cljs.analyzer/default-extern? -> cljs.analyzer/has-extern? --- src/main/clojure/cljs/analyzer.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 292dafb6f..a7bb06c50 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -769,7 +769,7 @@ 'prototype)}) x)) -(defn default-extern? [pre] +(defn has-extern? [pre] (let [externs (get @env/*compiler* ::externs)] (or (get-in externs pre) (when (= 1 (count pre)) @@ -790,7 +790,7 @@ (when (contains? locals (-> sym name symbol)) (warning :js-shadowed-by-local env {:name sym})) (let [pre (->> (string/split (name sym) #"\.") (map symbol) vec)] - (when-not (default-extern? pre) + (when-not (has-extern? pre) (swap! env/*compiler* update-in (into [::namespaces (-> env :ns :name) :externs] pre) merge {})) {:name sym @@ -2570,7 +2570,7 @@ (warning :infer-warning env {:form form}))) (when (js-tag? tag) (let [pre (-> tag meta :prefix)] - (when-not (default-extern? pre) + (when-not (has-extern? pre) (swap! env/*compiler* update-in (into [::namespaces (-> env :ns :name) :externs] pre) merge {})))) (case dot-action From 3af5e64fa2af6071062749b03f7b1cb946e22661 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 15:29:19 -0500 Subject: [PATCH 2131/4033] tweak cljs.analyzer/has-extern? to take externs as optional 2nd arg --- src/main/clojure/cljs/analyzer.cljc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a7bb06c50..2fb670ae8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -769,14 +769,16 @@ 'prototype)}) x)) -(defn has-extern? [pre] - (let [externs (get @env/*compiler* ::externs)] - (or (get-in externs pre) - (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 has-extern? + ([pre] + (has-extern? pre (get @env/*compiler* ::externs))) + ([pre externs] + (or (get-in externs pre) + (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 resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing From 60cb72a763ab9863c8fd7a2bd0ac96e0f2e5254d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 15:51:40 -0500 Subject: [PATCH 2132/4033] add helper for determining if some property might have an extern --- src/main/clojure/cljs/analyzer.cljc | 18 +++++++++++++++++- src/test/clojure/cljs/analyzer_tests.clj | 1 - 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2fb670ae8..e279d40bb 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -769,6 +769,22 @@ 'prototype)}) x)) +(defn has-extern?* + ([pre externs] + (has-extern?* pre externs externs)) + ([pre externs top] + (if (empty? pre) + true + (let [x (first pre) + me (find externs x)] + (if-not me + false + (let [[x' externs'] me] + (if-let [tag (-> x' meta :tag)] + (let [pre' (into [] (map symbol) (string/split (str tag) #"\."))] + (has-extern?* (into (conj pre' 'prototype) (next pre)) top top)) + (recur (next pre) externs' top)))))))) + (defn has-extern? ([pre] (has-extern? pre (get @env/*compiler* ::externs))) @@ -777,7 +793,7 @@ (when (= 1 (count pre)) (let [x (first pre)] (or (get-in externs (conj '[Window prototype] x)) - (get-in externs (conj '[Number] x))))) + (get-in externs (conj '[Number] x))))) (-> (last pre) str (string/starts-with? "cljs$"))))) (defn resolve-var diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 1323a2d14..a3a81217f 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -609,7 +609,6 @@ (comment (require '[cljs.compiler :as cc]) - ;; empty? (let [test-cenv (atom {::a/externs (externs/default-externs)})] (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] From cade78409773042f4a5fcefa8542090274c5d4dd Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 15:52:40 -0500 Subject: [PATCH 2133/4033] can eliminate console from the externs --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e279d40bb..60f53a0c9 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -789,7 +789,7 @@ ([pre] (has-extern? pre (get @env/*compiler* ::externs))) ([pre externs] - (or (get-in externs pre) + (or (has-extern?* pre externs) (when (= 1 (count pre)) (let [x (first pre)] (or (get-in externs (conj '[Window prototype] x)) From 65b59775c22bdfea0bdd294dd7d33084bf259043 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 18:45:05 -0500 Subject: [PATCH 2134/4033] note about needing to handle method fn case --- src/test/clojure/cljs/analyzer_tests.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index a3a81217f..f515415fb 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -609,6 +609,7 @@ (comment (require '[cljs.compiler :as cc]) + ;; TODO: need to handle the method/fn case (let [test-cenv (atom {::a/externs (externs/default-externs)})] (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] @@ -617,9 +618,12 @@ '[(ns foo.core) (defn bar [a] (js/parseInt a)) (def c js/React.Component) - (js/console.log "Hello world!")])) + (js/console.log "Hello world!") + (fn [& args] + (.apply (.-log js/console) js/console (into-array args)))])) (cc/emit-externs (reduce util/map-merge {} (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + ) \ No newline at end of file From 0d7ab86675d696a3f50e4460447c75e1b50ebcb5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 9 Dec 2016 18:51:05 -0500 Subject: [PATCH 2135/4033] add function case --- src/main/clojure/cljs/externs.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 1f916045e..b323fb0e3 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -34,8 +34,11 @@ (if-let [child (.. root getFirstChild)] (if (.isString child) (symbol (.. child getString)))))) - (when (or (.isConstructor info) (.isInterface info)) - (symbol (.. node getFirstChild getQualifiedName)))))))) + (if (or (.isConstructor info) (.isInterface info)) + (symbol (.. node getFirstChild getQualifiedName)) + (if (.hasReturnType info) + nil + nil))))))) (defmulti parse-extern-node (fn [^Node node] From 9e0182522265975661da2e3d0a17e20c358d070b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Dec 2016 10:01:38 -0500 Subject: [PATCH 2136/4033] need to handle return type tags --- src/main/clojure/cljs/externs.clj | 36 ++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index b323fb0e3..238750b6f 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -19,26 +19,29 @@ ;; Externs Parsing (defn annotate [props ty] - (conj - (into [] (butlast props)) - (vary-meta (last props) assoc :tag ty))) + (when (seq props) + (conj + (into [] (butlast props)) + (with-meta (last props) ty)))) + +(defn get-type* [^JSTypeExpression texpr] + (when-let [root (.getRoot texpr)] + (if (.isString root) + (symbol (.getString root)) + (if-let [child (.. root getFirstChild)] + (if (.isString child) + (symbol (.. child getString))))))) (defn get-type [^Node node] (when node (let [info (.getJSDocInfo node)] (when info (if-let [^JSTypeExpression ty (.getType info)] - (when-let [root (.getRoot ty)] - (if (.isString root) - (symbol (.getString root)) - (if-let [child (.. root getFirstChild)] - (if (.isString child) - (symbol (.. child getString)))))) + {:tag (get-type* ty)} (if (or (.isConstructor info) (.isInterface info)) - (symbol (.. node getFirstChild getQualifiedName)) + {:tag (symbol (.. node getFirstChild getQualifiedName))} (if (.hasReturnType info) - nil - nil))))))) + {:ret-tag (get-type* (.getReturnType info))}))))))) (defmulti parse-extern-node (fn [^Node node] @@ -152,6 +155,15 @@ first parse-externs index-externs (find 'console) first meta) + (-> + (filter + (fn [s] + (= "externs.zip//webkit_dom.js" (.getName s))) + (CommandLineRunner/getDefaultExterns)) + first parse-externs index-externs + (get-in '[Console prototype]) + (find 'log) first meta) + (require '[clojure.java.io :as io] '[cljs.closure :as cc]) From 757896861594e53d46a421a3b0bc81b3d1343ad8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Dec 2016 10:05:11 -0500 Subject: [PATCH 2137/4033] tag all externs fn/methods as Function --- src/main/clojure/cljs/externs.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 238750b6f..2baa45c85 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -41,7 +41,8 @@ (if (or (.isConstructor info) (.isInterface info)) {:tag (symbol (.. node getFirstChild getQualifiedName))} (if (.hasReturnType info) - {:ret-tag (get-type* (.getReturnType info))}))))))) + {:tag 'Function + :ret-tag (get-type* (.getReturnType info))}))))))) (defmulti parse-extern-node (fn [^Node node] @@ -140,6 +141,8 @@ (-> (default-externs) (find 'console) first meta) + (get (default-externs) 'Function) + ;; webkit_dom.js defines Console and Window.prototype.console (filter (fn [s] From 12ed4c8723db8cb85479166cc56804487ff3b356 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Dec 2016 10:38:30 -0500 Subject: [PATCH 2138/4033] handle global values on the Window prototype, notes on current troublesome case where values are not on the prototype, Symbol.iterator etc. --- src/main/clojure/cljs/analyzer.cljc | 9 ++++++++- src/main/clojure/cljs/externs.clj | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 60f53a0c9..eb750d290 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -771,7 +771,14 @@ (defn has-extern?* ([pre externs] - (has-extern?* pre externs externs)) + (let [pre (if-let [me (find + (get-in externs '[Window prototype]) + (first pre))] + (if-let [tag (-> me first meta :tag)] + (into [tag 'prototype] (next pre)) + pre) + pre)] + (has-extern?* pre externs externs))) ([pre externs top] (if (empty? pre) true diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 2baa45c85..566f80d1f 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -143,6 +143,15 @@ (get (default-externs) 'Function) + (get (default-externs) 'Error) + + ;; values are not on the prototype + (get (default-externs) 'Symbol) + (get (default-externs) 'Number) + + (-> (get-in (default-externs) '[Window prototype]) + (find 'performance) first meta) + ;; webkit_dom.js defines Console and Window.prototype.console (filter (fn [s] From 6e18504d232248ac441e034c1a4db7684c4c0988 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Dec 2016 11:43:58 -0500 Subject: [PATCH 2139/4033] handle Symbol. In cljs.analyzer/has-extern?* we should check first that property exists before considering the prototype --- src/main/clojure/cljs/analyzer.cljc | 15 +++++++++++++-- src/test/clojure/cljs/analyzer_tests.clj | 4 +++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index eb750d290..e3656320b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -769,6 +769,15 @@ 'prototype)}) x)) +(def alias->type + '{object Object + string String + number Number + array Array + function Function + boolean Boolean + symbol Symbol}) + (defn has-extern?* ([pre externs] (let [pre (if-let [me (find @@ -788,8 +797,10 @@ false (let [[x' externs'] me] (if-let [tag (-> x' meta :tag)] - (let [pre' (into [] (map symbol) (string/split (str tag) #"\."))] - (has-extern?* (into (conj pre' 'prototype) (next pre)) top top)) + (let [pre' (into [] (map symbol) + (string/split (str (alias->type tag tag)) #"\."))] + (or (has-extern?* (into pre' (next pre)) top top) + (has-extern?* (into (conj pre' 'prototype) (next pre)) top top))) (recur (next pre) externs' top)))))))) (defn has-extern? diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index f515415fb..412e9529f 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -620,7 +620,9 @@ (def c js/React.Component) (js/console.log "Hello world!") (fn [& args] - (.apply (.-log js/console) js/console (into-array args)))])) + (.apply (.-log js/console) js/console (into-array args))) + (js/console.log js/Number.MAX_VALUE) + (js/console.log js/Symbol.iterator)])) (cc/emit-externs (reduce util/map-merge {} (map (comp :externs second) From 97aa4303f46401e9517cde49893666add482e3a4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Dec 2016 11:49:55 -0500 Subject: [PATCH 2140/4033] hardwire odds and ends that we use that won't appear in an externs file --- src/main/clojure/cljs/externs.clj | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 566f80d1f..301194b4a 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -127,13 +127,23 @@ (seq xs) (update-in xs merge {}))) {} externs)) -(defn default-externs [] - (let [xs (CommandLineRunner/getDefaultExterns)] - (reduce - (fn [externs externs-file] - (util/map-merge - externs (index-externs (parse-externs externs-file)))) - {} xs))) +(defn default-externs + ([] + (default-externs + '{eval {} + global {} + goog {nodeGlobalRequire {}} + COMPILED {} + TypeError {} + Error {prototype {number {} columnNumber {}}} + ReferenceError {}})) + ([defaults] + (let [xs (CommandLineRunner/getDefaultExterns)] + (reduce + (fn [externs externs-file] + (util/map-merge + externs (index-externs (parse-externs externs-file)))) + defaults xs)))) (comment (default-externs) From 363c7b6c9c675b797d7bc0299174873381058590 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Dec 2016 10:23:10 -0500 Subject: [PATCH 2141/4033] cljs.analyzer/has-extern?* should not consider types it has previously encountered --- src/main/clojure/cljs/analyzer.cljc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e3656320b..06bb43564 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -787,21 +787,24 @@ (into [tag 'prototype] (next pre)) pre) pre)] - (has-extern?* pre externs externs))) - ([pre externs top] + (has-extern?* pre externs externs #{}))) + ([pre externs top seen] (if (empty? pre) true (let [x (first pre) me (find externs x)] - (if-not me - false - (let [[x' externs'] me] + (cond + (seen x) true + (not me) false + :else + (let [seen' (conj seen x) + [x' externs'] me] (if-let [tag (-> x' meta :tag)] (let [pre' (into [] (map symbol) (string/split (str (alias->type tag tag)) #"\."))] - (or (has-extern?* (into pre' (next pre)) top top) - (has-extern?* (into (conj pre' 'prototype) (next pre)) top top))) - (recur (next pre) externs' top)))))))) + (or (has-extern?* (into pre' (next pre)) top top seen') + (has-extern?* (into (conj pre' 'prototype) (next pre)) top top seen'))) + (recur (next pre) externs' top seen')))))))) (defn has-extern? ([pre] From cb3e8c295ad5ed1961e7db1d0b19674eb7f71a5c Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Dec 2016 15:11:49 -0500 Subject: [PATCH 2142/4033] fix failing tests --- src/main/clojure/cljs/util.cljc | 13 +++++++++---- src/test/clojure/cljs/analyzer_tests.clj | 2 +- src/test/clojure/cljs/module_processing_tests.clj | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 49950ef88..3aac4800d 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -147,10 +147,15 @@ URL return the JAR relative path of the resource." [x] {:pre [(or (file? x) (url? x))]} - (if (file? x) - (string/replace (.getAbsolutePath x) - (str (System/getProperty "user.dir") File/separator) "") - (last (string/split (.getFile x) #"\.jar!/")))) + (letfn [(strip-user-dir [s] + (string/replace s + (str (System/getProperty "user.dir") File/separator) ""))] + (if (file? x) + (strip-user-dir (.getAbsolutePath x)) + (let [f (.getFile x)] + (if (string/includes? f ".jar!/") + (last (string/split f #"\.jar!/")) + (strip-user-dir f)))))) (defn last-modified [src] (cond diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 412e9529f..fa5292bd2 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -217,7 +217,7 @@ (deftest method-inference (is (= (e/with-compiler-env test-cenv (:tag (a/analyze test-env '(.foo js/bar)))) - 'any))) + 'js))) (deftest fn-inference ;(is (= (e/with-compiler-env test-cenv diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 161133b78..48095c70f 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -38,8 +38,8 @@ (is (= {:foreign-libs [] :ups-foreign-libs [] - :libs ["out/react.js" - "out/Circle.js"] + :libs ["out/src/test/cljs/react.js" + "out/src/test/cljs/Circle.js"] :closure-warnings {:non-standard-jsdoc :off}} (env/with-compiler-env cenv (closure/process-js-modules From 0ea1e4ce5e12c5a01e43eedc4f0760a41a85e69b Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 13 Dec 2016 13:58:14 -0500 Subject: [PATCH 2143/4033] Fix CLJS-1653 regression --- src/main/cljs/cljs/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 4d6ea7879..cecd3252a 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -429,7 +429,7 @@ {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" [& kspecs] `(let [mspec# (keys ~@kspecs)] - (with-gen (& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) + (with-gen (cljs.spec/& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) (fn [] (gen/fmap (fn [m#] (apply concat m#)) (gen mspec#)))))) (defmacro nilable From 3ca51dc947de98b9b83949356e30ecd307bc5088 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 11 Dec 2016 18:39:56 -0500 Subject: [PATCH 2144/4033] CLJS-1869: Regression importing goog.Uri Employ a new `canonicalize-import-specs` when using the new import macro (in lieu of `canonicalize-specs`) which is faithful to the original REPL code in a couple of respects: * It doesn't put bare libspec symbols into vectors * It simply allows sequential libspecs to pass through The first change is important because it allows `parse-import-spec`'s handling of non sequential lib specs to be applied when forming the import map. As a concrete example, `parse-import-spec` will convert both '[goog Uri] and 'goog.Uri to the import map '{Uri goog.Uri}. Without this change, for the form (import 'goog.Uri), `parse-import-spec` will instead be passed '[goog.Uri], which is converted to an empty import map. The second bullet point, which we get for free with the change to a faithful canonicalize fn, allows, for example: (import '(goog Uri)) as was previously possible. --- src/main/clojure/cljs/analyzer.cljc | 11 ++++++++++- src/test/clojure/cljs/analyzer_tests.clj | 12 +++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 06bb43564..f170f38c7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2215,6 +2215,13 @@ (if (vector? spec) spec [spec]))))] (map canonicalize specs))) +(defn canonicalize-import-specs [specs] + (letfn [(canonicalize [quoted-spec-or-kw] + (if (keyword? quoted-spec-or-kw) + quoted-spec-or-kw + (second quoted-spec-or-kw)))] + (map canonicalize specs))) + (defn desugar-ns-specs "Given an original set of ns specs desugar :include-macros and :refer-macros usage into only primitive spec forms - :use, :require, :use-macros, @@ -2402,7 +2409,9 @@ (when-not *allow-ns* (throw (error env (str "Calls to `" (name (first quoted-specs)) "` must appear at the top-level.")))) - (let [specs (canonicalize-specs quoted-specs) + (let [specs (if (= :import (first quoted-specs)) + (canonicalize-import-specs quoted-specs) + (canonicalize-specs quoted-specs)) name (-> env :ns :name) args (desugar-ns-specs #?(:clj (list (process-rewrite-form diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index fa5292bd2..5a2fa2cf5 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -487,7 +487,17 @@ (is (= (a/canonicalize-specs '(:exclude (quote [map mapv]))) '(:exclude [map mapv]))) (is (= (a/canonicalize-specs '(:require (quote [clojure.set :as set]))) - '(:require [clojure.set :as set])))) + '(:require [clojure.set :as set]))) + (is (= (a/canonicalize-specs '(:require (quote clojure.set))) + '(:require [clojure.set])))) + +(deftest test-canonicalize-import-specs + (is (= (a/canonicalize-import-specs '(:import (quote [goog Uri]))) + '(:import [goog Uri]))) + (is (= (a/canonicalize-import-specs '(:import (quote (goog Uri)))) + '(:import (goog Uri)))) + (is (= (a/canonicalize-import-specs '(:import (quote goog.Uri))) + '(:import goog.Uri)))) (deftest test-cljs-1346 (testing "`ns*` special form conformance" From cfbaba0f7f2f9f918896528ca8e814831218458b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 11 Dec 2016 16:36:13 -0500 Subject: [PATCH 2145/4033] CLJS-1870: Quoted specs check in require macro symbols --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/analyzer_tests.clj | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f170f38c7..80f66af8b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2402,7 +2402,7 @@ (defmethod parse 'ns* [_ env [_ quoted-specs :as form] _ opts] (when-let [not-quoted (->> (remove keyword? quoted-specs) - (filter #(not= 'quote (first %)) ) + (remove #(and (seq? %) (= 'quote (first %))) ) first)] (throw (error env (str "Arguments to " (name (first quoted-specs)) " must be quoted. Offending spec: " not-quoted)))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 5a2fa2cf5..d4299f1c8 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -562,7 +562,10 @@ a/*cljs-warnings* nil] (is (thrown-with-msg? Exception #"Arguments to require must be quoted" (a/analyze test-env - '(require [clojure.set :as set])))))) + '(require [clojure.set :as set])))) + (is (thrown-with-msg? Exception #"Arguments to require must be quoted" + (a/analyze test-env + '(require clojure.set)))))) (testing "`:ns` and `:ns*` should throw if not `:top-level`" (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* nil] From 45d58b455840560a39dc115b0ca40c43dc543f3d Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 13 Dec 2016 17:20:52 -0500 Subject: [PATCH 2146/4033] remove stray unquote --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 72460cfb1..dd4a15b12 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -756,7 +756,7 @@ (not (<= (c/or min-count 0) (bounded-count (if max-count (inc max-count) min-count) x) (c/or max-count MAX_INT)))) - [{:path path :pred `(<= ~(c/or min-count 0) ~(c/count ~'%) ~(c/or max-count MAX_INT)) :val x :via via :in in}] + [{:path path :pred `(<= ~(c/or min-count 0) (c/count ~'%) ~(c/or max-count MAX_INT)) :val x :via via :in in}] (c/and distinct (not (empty? x)) (not (apply distinct? x))) [{:path path :pred 'distinct? :val x :via via :in in}]))) From 3b3db232711266f9f41c94c777084664d6d0b71b Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Tue, 25 Oct 2016 20:57:55 +0100 Subject: [PATCH 2147/4033] CLJS-1836: nth doesn't throw for IndexedSeqs --- src/main/cljs/cljs/core.cljs | 7 ++++--- src/test/cljs/cljs/seqs_test.cljs | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9a71d9d92..e807fce0d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1516,11 +1516,12 @@ reduces them without incurring seq initialization" IIndexed (-nth [coll n] (let [i (+ n i)] - (when (< i (alength arr)) - (aget arr i)))) + (if (and (<= 0 i) (< i (alength arr))) + (aget arr i) + (throw (js/Error. "Index out of bounds"))))) (-nth [coll n not-found] (let [i (+ n i)] - (if (< i (alength arr)) + (if (and (<= 0 i) (< i (alength arr))) (aget arr i) not-found))) diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 7e75b0c13..2390db754 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -187,4 +187,19 @@ (testing "Testing CLJS-778, -rest, -next RSeq" (is (= (-rest (rseq [0])) ())) (is (nil? (-next (rseq [0])))) - (is (= (set (rseq [0])) #{0})))) \ No newline at end of file + (is (= (set (rseq [0])) #{0})))) + +(deftest test-indexed-seqs + (testing "Testing IndexedSeq" + (testing "Sequence equality" + (is (= (list 0 1 2 3 4 5) (seq (array 0 1 2 3 4 5))))) + (testing "nth lookup within bounds" + (is (= 0 (nth (seq (array 0 1 2 3 4 5)) 0))) + (is (= 0 (nth (seq (array 0 1 2 3 4 5)) 0 :not-found))) + (is (= 5 (nth (seq (array 0 1 2 3 4 5)) 5))) + (is (= 5 (nth (seq (array 0 1 2 3 4 5)) 5 :not-found)))) + (testing "nth lookup out of bounds" + (is (thrown? js/Error (nth (seq (array 0 1 2 3 4 5)) 6))) + (is (= :not-found (nth (seq (array 0 1 2 3 4 5)) 6 :not-found))) + (is (thrown? js/Error (nth (seq (array 0 1 2 3 4 5)) -1))) + (is (= :not-found (nth (seq (array 0 1 2 3 4 5)) -1 :not-found)))))) From 841254a96066ea14da9a4f647b41e13a7c5e0026 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Sat, 15 Oct 2016 16:17:13 +0100 Subject: [PATCH 2148/4033] read-string should throw error when duplicate keys --- src/main/cljs/cljs/core.cljs | 76 +++++++++++++++++++-------- src/main/cljs/cljs/reader.cljs | 6 +-- src/main/clojure/cljs/compiler.cljc | 6 +-- src/main/clojure/cljs/core.cljc | 4 +- src/test/cljs/cljs/hash_map_test.cljs | 13 +++++ src/test/cljs/cljs/hash_set_test.cljs | 24 +++++++++ src/test/cljs/cljs/reader_test.cljs | 26 +++++++++ 7 files changed, 125 insertions(+), 30 deletions(-) create mode 100644 src/test/cljs/cljs/hash_set_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e807fce0d..3a0ba681c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6339,24 +6339,34 @@ reduces them without incurring seq initialization" (set! (.-HASHMAP-THRESHOLD PersistentArrayMap) 8) -(set! (.-fromArray PersistentArrayMap) - (fn [arr ^boolean no-clone ^boolean no-check] - (as-> (if no-clone arr (aclone arr)) arr - (if no-check - arr +(set! (.-createWithCheck PersistentArrayMap) + (fn [arr] (let [ret (array)] (loop [i 0] (when (< i (alength arr)) (let [k (aget arr i) v (aget arr (inc i)) idx (array-index-of ret k)] - (when (== idx -1) - (.push ret k) - (.push ret v))) + (if (== idx -1) + (doto ret (.push k) (.push v)) + (throw (js/Error. (str "Duplicate key: " k))))) (recur (+ i 2)))) - ret)) - (let [cnt (/ (alength arr) 2)] - (PersistentArrayMap. nil cnt arr nil))))) + (let [cnt (/ (alength arr) 2)] + (PersistentArrayMap. nil cnt arr nil))))) + +(set! (.-createAsIfByAssoc PersistentArrayMap) + (fn [arr] + (let [ret (array)] + (loop [i 0] + (when (< i (alength arr)) + (let [k (aget arr i) + v (aget arr (inc i)) + idx (array-index-of ret k)] + (if (== idx -1) + (doto ret (.push k) (.push v)) + (aset ret (inc idx) v))) + (recur (+ i 2)))) + (PersistentArrayMap. nil (/ (alength ret) 2) ret nil)))) (es6-iterable PersistentArrayMap) @@ -7293,9 +7303,8 @@ reduces them without incurring seq initialization" (set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil empty-unordered-hash)) (set! (.-fromArray PersistentHashMap) - (fn [arr ^boolean no-clone] - (let [arr (if no-clone arr (aclone arr)) - len (alength arr)] + (fn [arr] + (let [len (alength arr)] (loop [i 0 ret (transient (.-EMPTY PersistentHashMap))] (if (< i len) (recur (+ i 2) @@ -7310,6 +7319,18 @@ reduces them without incurring seq initialization" (recur (inc i) (-assoc! out (aget ks i) (aget vs i))) (persistent! out)))))) +(set! (.-createWithCheck PersistentHashMap) + (fn [arr] + (let [len (alength arr) + ret (transient (.-EMPTY PersistentHashMap))] + (loop [i 0] + (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)))) + (recur (+ i 2))))) + (-persistent! ret)))) + (es6-iterable PersistentHashMap) (deftype TransientHashMap [^:mutable ^boolean edit @@ -8129,7 +8150,7 @@ reduces them without incurring seq initialization" (let [arr (if (and (instance? IndexedSeq keyvals) (zero? (.-i keyvals))) (.-arr keyvals) (into-array keyvals))] - (.fromArray cljs.core/PersistentArrayMap arr true false))) + (.createAsIfByAssoc PersistentArrayMap arr true false))) (defn obj-map "keyval => key val @@ -8464,6 +8485,23 @@ reduces them without incurring seq initialization" (recur (inc i) (-conj! out (aget items i))) (-persistent! out))))))) +(set! (.-createWithCheck PersistentHashSet) + (fn [items] + (let [len (alength items) + t (-as-transient (.-EMPTY PersistentHashSet))] + (dotimes [i len] + (-conj! t (aget items i)) + (when-not (= (count t) (inc i)) + (throw (js/Error. (str "Duplicate key: " (aget items i)))))) + (-persistent! t)))) + +(set! (.-createAsIfByAssoc PersistentHashSet) + (fn [items] + (let [len (alength items) + t (-as-transient (.-EMPTY PersistentHashSet))] + (dotimes [i len] (-conj! t (aget items i))) + (-persistent! t)))) + (es6-iterable PersistentHashSet) (deftype TransientHashSet [^:mutable transient-map] @@ -8596,12 +8634,6 @@ reduces them without incurring seq initialization" (es6-iterable PersistentTreeSet) -(defn set-from-indexed-seq [iseq] - (let [arr (.-arr iseq) - ret (areduce arr i ^not-native res (-as-transient #{}) - (-conj! res (aget arr i)))] - (-persistent! ^not-native ret))) - (defn set "Returns a set of the distinct elements of coll." [coll] @@ -8610,7 +8642,7 @@ reduces them without incurring seq initialization" (nil? in) #{} (and (instance? IndexedSeq in) (zero? (.-i in))) - (set-from-indexed-seq in) + (.createAsIfByAssoc PersistentHashSet (.-arr in)) :else (loop [^not-native in in diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index 82101dc45..fbecc4437 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -287,8 +287,8 @@ nil if the end of stream has been reached") (when (odd? c) (reader-error rdr "Map literal must contain an even number of forms")) (if (<= c (* 2 (.-HASHMAP-THRESHOLD PersistentArrayMap))) - (.fromArray PersistentArrayMap l true true) - (.fromArray PersistentHashMap l true)))) + (.createWithCheck PersistentArrayMap l) + (.createWithCheck PersistentHashMap l)))) (defn read-number [reader initch] @@ -407,7 +407,7 @@ nil if the end of stream has been reached") (defn read-set [rdr _] - (.fromArray PersistentHashSet (read-delimited-list "}" rdr true) true)) + (.createWithCheck PersistentHashSet (read-delimited-list "}" rdr true))) (defn read-regex [rdr ch] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 6cd0c5039..0e7665841 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -363,9 +363,9 @@ (emits "new cljs.core.PersistentArrayMap(null, " (count keys) ", [" (comma-sep (interleave keys vals)) "], null)") - (emits "cljs.core.PersistentArrayMap.fromArray([" + (emits "cljs.core.PersistentArrayMap.createAsIfByAssoc([" (comma-sep (interleave keys vals)) - "], true, false)")) + "])")) :else (emits "cljs.core.PersistentHashMap.fromArrays([" @@ -407,7 +407,7 @@ (emits "new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, " (count items) ", [" (comma-sep (interleave items (repeat "null"))) "], null), null)") - :else (emits "cljs.core.PersistentHashSet.fromArray([" (comma-sep items) "], true)")))) + :else (emits "cljs.core.PersistentHashSet.createAsIfByAssoc([" (comma-sep items) "], true)")))) (defmethod emit* :js-value [{:keys [items js-type env]}] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3d3ba6fe3..c3cf6fdc0 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2457,7 +2457,7 @@ (map #(cljs.analyzer/analyze &env %) keys)) (= (count (into #{} keys)) (count keys))) `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) - `(.fromArray cljs.core/PersistentArrayMap (array ~@kvs) true false))))) + `(.createAsIfByAssoc cljs.core/PersistentArrayMap (array ~@kvs)))))) (core/defmacro hash-map ([] `(.-EMPTY cljs.core/PersistentHashMap)) @@ -2480,7 +2480,7 @@ (cljs.core/PersistentArrayMap. nil ~(count xs) (array ~@(interleave xs (repeat nil))) nil) nil) (vary-meta - `(.fromArray cljs.core/PersistentHashSet (array ~@xs) true) + `(.createAsIfByAssoc cljs.core/PersistentHashSet (array ~@xs)) assoc :tag 'cljs.core/PersistentHashSet)))) (core/defn- js-obj* [kvs] diff --git a/src/test/cljs/cljs/hash_map_test.cljs b/src/test/cljs/cljs/hash_map_test.cljs index dcc07c5f6..83058f9fc 100644 --- a/src/test/cljs/cljs/hash_map_test.cljs +++ b/src/test/cljs/cljs/hash_map_test.cljs @@ -37,3 +37,16 @@ cljscore-hash))] (let [x (hash-map :a :a :b -1)] (is (= (assoc x :b :b) {:a :a :b :b})))))) + +(deftest test-array-map-with-duplicate-keys + (testing "Testing duplicate keys in array maps" + ;; runtime + (is (= [:foo] (keys (apply array-map [:foo 1 :foo 2])))) + (let [sym-a (with-meta 'foo :first) + sym-b (with-meta 'foo :second)] + (is (= {sym-a 2} (apply array-map [sym-a 1 sym-b 2])))) + ;; compile-time + (is (= {:foo 2} (array-map :foo 1 :foo 2))) + (let [sym-a (with-meta 'foo :first) + sym-b (with-meta 'foo :second)] + (is (= {sym-a 2} (array-map sym-a 1 sym-b 2)))))) diff --git a/src/test/cljs/cljs/hash_set_test.cljs b/src/test/cljs/cljs/hash_set_test.cljs new file mode 100644 index 000000000..264838631 --- /dev/null +++ b/src/test/cljs/cljs/hash_set_test.cljs @@ -0,0 +1,24 @@ +;; 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.hash-set-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is]])) + +(deftest test-hash-set-with-duplicate-keys + (testing "Testing duplicate keys in array maps" + ;; runtime + (is (= [:foo] (keys (apply array-map [:foo :foo])))) + (let [sym-a (with-meta 'foo :first) + sym-b (with-meta 'foo :second)] + (is (= #{sym-a} (apply hash-set [sym-a sym-b])))) + ;; compile-time + (is (= {:foo 2} (hash-set :foo :foo))) + (let [sym-a (with-meta 'foo :first) + sym-b (with-meta 'foo :second)] + (is (= #{sym-a} (hash-set sym-a sym-b)))))) diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 2bbebdf20..c5b3b72b5 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -197,3 +197,29 @@ (testing "Testing '/ reading" (is (= x (reader/read-string (pr-str x)))) (is (= (reader/read-string (pr-str x)) x))))) + +(deftest testing-cljs-1823 + (let [;; PersistentArrayMap + a (try + (reader/read-string "{:a 1 :b 2 :c 3 :a 1}") + :failed-to-throw + (catch js/Error e (ex-message e))) + ;; PersistentHashMap + b (try + (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :h 7 :i 8 :a 1}") + :failed-to-throw + (catch js/Error e (ex-message e))) + ;; PersistentArrayMap backed PHS + c (try + (reader/read-string "#{:a :b :c :d :a}") + :failed-to-throw + (catch js/Error e (ex-message e))) + ;; PersistentHashMap backed PHS + d (try + (reader/read-string "#{:a :b :c :d :e :f :g :h :i :a}") + :failed-to-throw + (catch js/Error e (ex-message e)))] + (is (= "Duplicate key: :a" a)) + (is (= "Duplicate key: :a" b)) + (is (= "Duplicate key: :a" c)) + (is (= "Duplicate key: :a" d)))) From 95fd110f55c57b890422763ed8f2644cfbf159de Mon Sep 17 00:00:00 2001 From: Lauri Oherd Date: Thu, 6 Oct 2016 22:59:51 +0300 Subject: [PATCH 2149/4033] CLJS-1786: Add knob for controlling printing of namespaced maps --- src/main/cljs/cljs/core.cljs | 40 +++++++++++++++++++++++++++-- src/main/cljs/cljs/pprint.cljs | 36 ++++++++++++++------------ src/main/clojure/cljs/repl.cljc | 18 ++++++++----- src/test/cljs/cljs/core_test.cljs | 5 ++++ src/test/cljs/cljs/pprint_test.cljs | 7 +++++ 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3a0ba681c..abfdb908c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -107,6 +107,14 @@ Defaults to false."} *print-dup* false) +(def + ^{:dynamic true + :doc "*print-namespace-maps* controls whether the printer will print + namespace map literal syntax. + + Defaults to false, but the REPL binds it to true."} + *print-namespace-maps* false) + (def ^{:dynamic true :doc "*print-length* controls how many items of each collection the @@ -9411,16 +9419,44 @@ reduces them without incurring seq initialization" (when *print-newline* (newline (pr-opts)))) -(defn print-map [m print-one writer opts] +(defn- strip-ns + [named] + (if (symbol? named) + (symbol nil (name named)) + (keyword nil (name named)))) + +(defn- lift-ns + "Returns [lifted-ns lifted-map] or nil if m can't be lifted." + [m] + (when *print-namespace-maps* + (loop [ns nil + [[k v :as entry] & entries] (seq m) + lm (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])))) + +(defn print-prefix-map [prefix m print-one writer opts] (pr-sequential-writer writer (fn [e w opts] (do (print-one (key e) w opts) (-write w \space) (print-one (val e) w opts))) - "{" ", " "}" + (str prefix "{") ", " "}" opts (seq m))) +(defn print-map [m print-one writer opts] + (let [[ns lift-map] (lift-ns m)] + (if ns + (print-prefix-map (str "#:" ns) lift-map print-one writer opts) + (print-prefix-map nil m print-one writer opts)))) + (extend-protocol IPrintWithWriter LazySeq (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 208d3ced6..13d63b916 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -611,7 +611,7 @@ beginning of aseq" ;; Variables that control the pretty printer ;;====================================================================== -;; *print-length*, *print-level* and *print-dup* are defined in cljs.core +;; *print-length*, *print-level*, *print-namespace-maps* and *print-dup* are defined in cljs.core (def ^:dynamic ^{:doc "Bind to true if you want write to use pretty printing"} *print-pretty* true) @@ -2837,21 +2837,25 @@ column number or pretty printing" ;;; (def pprint-map (formatter-out "~<{~;~@{~<~w~^ ~_~w~:>~^, ~_~}~;}~:>")) (defn- pprint-map [amap] - (pprint-logical-block :prefix "{" :suffix "}" - (print-length-loop [aseq (seq amap)] - (when aseq - ;;compiler gets confused with nested macro if it isn't namespaced - ;;it tries to use clojure.pprint/pprint-logical-block for some reason - (m/pprint-logical-block - (write-out (ffirst aseq)) - (-write *out* " ") - (pprint-newline :linear) - (set! *current-length* 0) ;always print both parts of the [k v] pair - (write-out (fnext (first aseq)))) - (when (next aseq) - (-write *out* ", ") - (pprint-newline :linear) - (recur (next aseq))))))) + (let [[ns lift-map] (when (not (record? amap)) + (#'cljs.core/lift-ns amap)) + amap (or lift-map amap) + prefix (if ns (str "#:" ns "{") "{")] + (pprint-logical-block :prefix prefix :suffix "}" + (print-length-loop [aseq (seq amap)] + (when aseq + ;;compiler gets confused with nested macro if it isn't namespaced + ;;it tries to use clojure.pprint/pprint-logical-block for some reason + (m/pprint-logical-block + (write-out (ffirst aseq)) + (-write *out* " ") + (pprint-newline :linear) + (set! *current-length* 0) ;always print both parts of the [k v] pair + (write-out (fnext (first aseq)))) + (when (next aseq) + (-write *out* ", ") + (pprint-newline :linear) + (recur (next aseq)))))))) (defn- pprint-simple-default [obj] ;;TODO: Update to handle arrays (?) and suppressing namespaces diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index d1f67dfbd..a0036bac1 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -822,13 +822,17 @@ (swap! env/*compiler* assoc :js-dependency-index (deps/js-dependency-index opts)) opts) opts) - init (or init - #(evaluate-form repl-env env "" - (with-meta - `(~'ns ~'cljs.user - (:require ~@repl-requires)) - {:line 1 :column 1}) - identity opts)) + init (do + (evaluate-form repl-env env "" + `(~'set! ~'cljs.core/*print-namespace-maps* true) + identity opts) + (or init + #(evaluate-form repl-env env "" + (with-meta + `(~'ns ~'cljs.user + (:require ~@repl-requires)) + {:line 1 :column 1}) + identity opts))) read-eval-print (fn [] (let [input (binding [*ns* (create-ns ana/*cljs-ns*) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 86b2d9951..30c336e18 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -759,6 +759,11 @@ (is (= (with-out-str (doseq [fn (cljs-739 [] [:a :b :c :d])] (fn))) ":a\n:b\n:c\n:d\n"))))) +(deftest print-ns-maps + (testing "Testing CLJS-1786, *print-namespace-maps*" + (is (= "#:user{:a 1}" (binding [*print-namespace-maps* true] (pr-str {:user/a 1})))) + (is (= "{:user/a 1}" (binding [*print-namespace-maps* false] (pr-str {:user/a 1})))))) + (deftest test-728 (testing "Testing CLJS-728, lookup with default" (doseq [n [nil "-1" "" "0" "1" false true (js-obj)]] diff --git a/src/test/cljs/cljs/pprint_test.cljs b/src/test/cljs/cljs/pprint_test.cljs index e0b2ae6fb..7a9b926f8 100644 --- a/src/test/cljs/cljs/pprint_test.cljs +++ b/src/test/cljs/cljs/pprint_test.cljs @@ -231,6 +231,13 @@ Usage: *hello* "#{123\n 456\n 789}\n" ) +(simple-tests print-namespace-maps-tests + (binding [*print-namespace-maps* true] (with-out-str (pprint {:user/a 1}))) + "#:user{:a 1}\n" + (binding [*print-namespace-maps* false] (with-out-str (pprint {:user/a 1}))) + "{:user/a 1}\n" + ) + ;;---------------------------------------------------------------------------- ;; clj-format tests ;;---------------------------------------------------------------------------- From d1b8b31f7247688098d9e61bb33302a6edc57c2c Mon Sep 17 00:00:00 2001 From: jrheard Date: Thu, 6 Oct 2016 12:48:12 -0700 Subject: [PATCH 2150/4033] cljs.spec.test/unstrument shouldn't return the names of vars that weren't instrumented in the first place. Fixes CLJS-1812 --- src/main/cljs/cljs/spec/test.cljc | 15 +++++++-------- src/test/cljs/cljs/spec_test.cljs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index aea980317..692c535e5 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -48,17 +48,16 @@ returns the set of all symbols naming vars in those nses." (defmacro instrument-1 [[quote s] opts] - (let [v (ana-api/resolve &env s)] - (when v - (swap! instrumented-vars conj (:name v)) - `(let [checked# (instrument-1* ~s (var ~s) ~opts)] - (when checked# (set! ~s checked#)) - '~(:name v))))) + (when-let [v (ana-api/resolve &env s)] + (swap! instrumented-vars conj (:name v)) + `(let [checked# (instrument-1* ~s (var ~s) ~opts)] + (when checked# (set! ~s checked#)) + '~(:name v)))) (defmacro unstrument-1 [[quote s]] - (let [v (ana-api/resolve &env s)] - (when v + (when-let [v (ana-api/resolve &env s)] + (when (@instrumented-vars (:name v)) (swap! instrumented-vars disj (:name v)) `(let [raw# (unstrument-1* ~s (var ~s))] (when raw# (set! ~s raw#)) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 88009d548..eb4df4bff 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -8,6 +8,7 @@ (ns cljs.spec-test (:require [cljs.spec :as s] + [cljs.spec.test :as stest] [cljs.test :as test :refer-macros [deftest is are run-tests]] [cljs.spec.impl.gen :as gen] [clojure.test.check.generators])) @@ -100,6 +101,19 @@ (is (= :b (s/conform ::multi :b))) (is (= :a (s/conform ::multi :a)))) +(defn h-cljs-1812 [x] true) +(s/fdef h-cljs-1812 :args (s/cat :x int?) :ret true?) + +(deftest test-cljs-1812 + (is (= (stest/unstrument `h-cljs-1812) + [])) + + (stest/check `h-cljs-1812 {:clojure.test.check/opts {:num-tests 1}}) + + ; Calling h-cljs-1812 with an argument of the wrong type shouldn't throw, + ; because the function should not have been instrumented by stest/check. + (h-cljs-1812 "foo")) + ;; Copied from Clojure spec tests (def even-count? #(even? (count %))) From 24f4189445d802fcd3155855cf5f51e4c1902785 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Dec 2016 07:19:06 -0500 Subject: [PATCH 2151/4033] revert all old API changes/deletions made in 841254a96066ea14da9a4f647b41e13a7c5e0026 formatting --- src/main/cljs/cljs/core.cljs | 88 +++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index abfdb908c..d22113055 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6347,34 +6347,53 @@ reduces them without incurring seq initialization" (set! (.-HASHMAP-THRESHOLD PersistentArrayMap) 8) -(set! (.-createWithCheck PersistentArrayMap) - (fn [arr] +(set! (.-fromArray PersistentArrayMap) + (fn [arr ^boolean no-clone ^boolean no-check] + (as-> (if no-clone arr (aclone arr)) arr + (if no-check + arr (let [ret (array)] (loop [i 0] (when (< i (alength arr)) (let [k (aget arr i) v (aget arr (inc i)) idx (array-index-of ret k)] - (if (== idx -1) - (doto ret (.push k) (.push v)) - (throw (js/Error. (str "Duplicate key: " k))))) + (when (== idx -1) + (.push ret k) + (.push ret v))) (recur (+ i 2)))) - (let [cnt (/ (alength arr) 2)] - (PersistentArrayMap. nil cnt arr nil))))) + ret)) + (let [cnt (/ (alength arr) 2)] + (PersistentArrayMap. nil cnt arr nil))))) + +(set! (.-createWithCheck PersistentArrayMap) + (fn [arr] + (let [ret (array)] + (loop [i 0] + (when (< i (alength arr)) + (let [k (aget arr i) + v (aget arr (inc i)) + idx (array-index-of ret k)] + (if (== idx -1) + (doto ret (.push k) (.push v)) + (throw (js/Error. (str "Duplicate key: " k))))) + (recur (+ i 2)))) + (let [cnt (/ (alength arr) 2)] + (PersistentArrayMap. nil cnt arr nil))))) (set! (.-createAsIfByAssoc PersistentArrayMap) - (fn [arr] - (let [ret (array)] - (loop [i 0] - (when (< i (alength arr)) - (let [k (aget arr i) - v (aget arr (inc i)) - idx (array-index-of ret k)] - (if (== idx -1) - (doto ret (.push k) (.push v)) - (aset ret (inc idx) v))) - (recur (+ i 2)))) - (PersistentArrayMap. nil (/ (alength ret) 2) ret nil)))) + (fn [arr] + (let [ret (array)] + (loop [i 0] + (when (< i (alength arr)) + (let [k (aget arr i) + v (aget arr (inc i)) + idx (array-index-of ret k)] + (if (== idx -1) + (doto ret (.push k) (.push v)) + (aset ret (inc idx) v))) + (recur (+ i 2)))) + (PersistentArrayMap. nil (/ (alength ret) 2) ret nil)))) (es6-iterable PersistentArrayMap) @@ -7311,8 +7330,9 @@ reduces them without incurring seq initialization" (set! (.-EMPTY PersistentHashMap) (PersistentHashMap. nil 0 nil false nil empty-unordered-hash)) (set! (.-fromArray PersistentHashMap) - (fn [arr] - (let [len (alength arr)] + (fn [arr ^boolean no-clone] + (let [arr (if no-clone arr (aclone arr)) + len (alength arr)] (loop [i 0 ret (transient (.-EMPTY PersistentHashMap))] (if (< i len) (recur (+ i 2) @@ -7328,16 +7348,16 @@ reduces them without incurring seq initialization" (persistent! out)))))) (set! (.-createWithCheck PersistentHashMap) - (fn [arr] - (let [len (alength arr) - ret (transient (.-EMPTY PersistentHashMap))] - (loop [i 0] - (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)))) - (recur (+ i 2))))) - (-persistent! ret)))) + (fn [arr] + (let [len (alength arr) + ret (transient (.-EMPTY PersistentHashMap))] + (loop [i 0] + (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)))) + (recur (+ i 2))))) + (-persistent! ret)))) (es6-iterable PersistentHashMap) @@ -8642,6 +8662,12 @@ reduces them without incurring seq initialization" (es6-iterable PersistentTreeSet) +(defn set-from-indexed-seq [iseq] + (let [arr (.-arr iseq) + ret (areduce arr i ^not-native res (-as-transient #{}) + (-conj! res (aget arr i)))] + (-persistent! ^not-native ret))) + (defn set "Returns a set of the distinct elements of coll." [coll] From 4f08dbdedb4d5645a977eb43d21a4ebf28597374 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Fri, 21 Oct 2016 11:46:29 +0100 Subject: [PATCH 2152/4033] CLJS-1829: get does not return not-found on negative indexes --- src/main/cljs/cljs/core.cljs | 14 +++++++------- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d22113055..3f60319dd 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1832,16 +1832,16 @@ reduces them without incurring seq initialization" (-lookup ^not-native o k) (array? o) - (when (< k (.-length o)) + (when (and (some? k) (< k (.-length o))) (aget o (int k))) - + (string? o) (when (and (some? k) (< k (.-length o))) - (aget o (int k))) + (.charAt o (int k))) (native-satisfies? ILookup o) (-lookup o k) - + :else nil))) ([o k not-found] (if-not (nil? o) @@ -1850,13 +1850,13 @@ reduces them without incurring seq initialization" (-lookup ^not-native o k not-found) (array? o) - (if (< k (.-length o)) + (if (and (some? k) (>= k 0) (< k (.-length o))) (aget o (int k)) not-found) (string? o) - (if (< k (.-length o)) - (aget o (int k)) + (if (and (some? k) (>= k 0) (< k (.-length o))) + (.charAt o (int k)) not-found) (native-satisfies? ILookup o) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 30c336e18..953c8c8de 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1183,6 +1183,12 @@ (is (thrown? js/Error (assoc sv 5 0))) (is (thrown? js/Error (assoc sv -1 0))))) +(deftest test-cljs-1829 + (is (= (get "0123" -1 :not-found) :not-found)) + (is (= (get #js [0 1 2 3] -1 :not-found) :not-found)) + (is (= (get "0123" nil :not-found) :not-found)) + (is (= (get #js [0 1 2 3] nil :not-found) :not-found))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 101d7d9e03e90518e6769781dd33fbe6387d2d44 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Sat, 17 Dec 2016 09:44:38 +0100 Subject: [PATCH 2153/4033] CLJS-1875 Difference in seqable? between CLJ & CLJS The current ClojureScript implementation is much more limited than the Clojure one, which accepts anything that can be passed to `seq`. This adds the same checks to seqable? that are currently used in seq, so it behaves the same as in Clojure. --- src/main/cljs/cljs/core.cljs | 7 +++++-- src/test/cljs/cljs/predicates_test.cljs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3f60319dd..ebc036416 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2123,9 +2123,12 @@ reduces them without incurring seq initialization" (satisfies? ISeq s))) (defn ^boolean seqable? - "Return true if s satisfies ISeqable" + "Return true if the seq function is supported for s" [s] - (satisfies? ISeqable s)) + (or + (satisfies? ISeqable s) + (array? s) + (string? s))) (defn ^boolean boolean "Coerce to boolean" diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index d816c5e33..76d54dbf0 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -25,7 +25,9 @@ [::foo false false false true false false false true false false false true] ['foo false false false true false false true nil true nil false false] ['foo/bar false false false true false false false true false true false false] - [uuid false false false false true false false false false false false false]])) + [uuid false false false false true false false false false false false false] + [(array) false false true false false false false false false false false false] + ["string" false false true false false false false false false false false false]])) (deftest test-preds (let [[preds & rows] pred-val-table] From 47fa30dd95c850aa0643aae9c274c09bc202065c Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Thu, 22 Dec 2016 16:24:19 +0000 Subject: [PATCH 2154/4033] CLJS-1880: missing ^boolean on some hasNext calls --- src/main/cljs/cljs/core.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ebc036416..b0c8cbe36 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3839,7 +3839,7 @@ reduces them without incurring seq initialization" (set! _next (next _seq)))) (not (nil? _next))) (next [this] - (if-not (.hasNext this) + (if-not ^boolean (.hasNext this) (throw (js/Error. "No such element")) (do (set! _seq _next) @@ -3898,7 +3898,7 @@ reduces them without incurring seq initialization" (loop [iters (seq iters)] (if-not (nil? iters) (let [iter (first iters)] - (if-not (.hasNext iter) + (if-not ^boolean (.hasNext iter) false (recur (next iters)))) true))) @@ -5101,7 +5101,7 @@ reduces them without incurring seq initialization" (let [me-iter (-iterator coll) you-iter (-iterator other)] (loop [] - (if (.hasNext me-iter) + (if ^boolean (.hasNext me-iter) (let [x (.next me-iter) y (.next you-iter)] (if (= x y) @@ -7187,7 +7187,7 @@ reduces them without incurring seq initialization" (deftype HashMapIter [nil-val root-iter ^:mutable seen] Object (hasNext [_] - (or (not seen) ^boolean (.hasNext root-iter))) + (or (not ^boolean seen) ^boolean (.hasNext root-iter))) (next [_] (if-not ^boolean seen (do From 96493c74d9d49cfd00e042a13f9d287ca238187f Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Wed, 21 Dec 2016 19:50:10 -0500 Subject: [PATCH 2155/4033] CLJS-1878: prefer `some?` over `(not (nil? %))` in analyzer Also add a few predicates to better propagate ^boolean tags. --- src/main/clojure/cljs/analyzer.cljc | 332 +++++++++++++--------------- 1 file changed, 155 insertions(+), 177 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 80f66af8b..8e8647f3d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -502,13 +502,13 @@ #?(:clj (reify clojure.lang.IDeref (deref [_] - (if-not (nil? env/*compiler*) + (if (some? env/*compiler*) (::namespaces @env/*compiler*) default-namespaces))) :cljs (reify IDeref (-deref [_] - (if-not (nil? env/*compiler*) + (if (some? env/*compiler*) (::namespaces @env/*compiler*) default-namespaces))))) @@ -516,11 +516,10 @@ ([key] (get-namespace env/*compiler* key)) ([cenv key] - (let [ns (get-in @cenv [::namespaces key])] - (if-not (nil? ns) + (if-some [ns (get-in @cenv [::namespaces key])] ns (when (= 'cljs.user key) - {:name 'cljs.user}))))) + {:name 'cljs.user})))) #?(:clj (defmacro no-warn [& body] @@ -676,11 +675,8 @@ [env prefix] (when-not (gets @env/*compiler* ::namespaces prefix) (let [ns (:ns env)] - (if-not (nil? (get (:requires ns) prefix)) - true - (if-not (nil? (get (:imports ns) prefix)) - true - false))))) + (or (some? (get (:requires ns) prefix)) + (some? (get (:imports ns) prefix)))))) (defn js-module-exists? [module] @@ -748,17 +744,17 @@ "Is sym visible from core in the current compilation namespace?" #?(:cljs {:tag boolean}) [env sym] - (and (or (gets @env/*compiler* ::namespaces 'cljs.core :defs sym) - (when-let [mac (get-expander sym env)] + (and (or (some? (gets @env/*compiler* ::namespaces 'cljs.core :defs sym)) + (if-some [mac (get-expander sym env)] (let [^Namespace ns (-> mac meta :ns)] - (= (.getName ns) #?(:clj 'cljs.core :cljs 'cljs.core$macros))))) + (= (.getName ns) #?(:clj 'cljs.core :cljs 'cljs.core$macros))) + false)) (not (contains? (-> env :ns :excludes) sym)))) (defn js-tag? [x] - (if (symbol? x) - (or (= 'js x) - (= "js" (namespace x))) - false)) + (and (symbol? x) + (or (= 'js x) + (= "js" (namespace x))))) (defn normalize-js-tag [x] ;; if not 'js, assume constructor @@ -780,10 +776,10 @@ (defn has-extern?* ([pre externs] - (let [pre (if-let [me (find - (get-in externs '[Window prototype]) - (first pre))] - (if-let [tag (-> me first meta :tag)] + (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)] @@ -799,7 +795,7 @@ :else (let [seen' (conj seen x) [x' externs'] me] - (if-let [tag (-> x' meta :tag)] + (if-some [tag (-> x' meta :tag)] (let [pre' (into [] (map symbol) (string/split (str (alias->type tag tag)) #"\."))] (or (has-extern?* (into pre' (next pre)) top top seen') @@ -838,9 +834,9 @@ (let [s (str sym) lb (get locals sym)] (cond - (not (nil? lb)) lb + (some? lb) lb - (not (nil? (namespace sym))) + (some? (namespace sym)) (let [ns (namespace sym) ns (if #?(:clj (= "clojure.core" ns) :cljs (identical? "clojure.core" ns)) @@ -850,7 +846,7 @@ (or (and (js-module-exists? ns) (get-in @env/*compiler* [:js-module-index ns])) (symbol ns)))] - (when-not (nil? confirm) + (when (some? confirm) (when (not= (-> env :ns :name) full-ns) (confirm-ns env full-ns)) (confirm env full-ns (symbol (name sym)))) @@ -864,31 +860,30 @@ (not ^boolean (goog.string/contains s "..")))) (let [idx (.indexOf s ".") prefix (symbol (subs s 0 idx)) - suffix (subs s (inc idx)) - lb (get locals prefix)] - (if-not (nil? lb) + suffix (subs s (inc idx))] + (if-some [lb (get locals prefix)] {:name (symbol (str (:name lb)) suffix)} - (let [cur-ns (-> env :ns :name) - full-ns (gets @env/*compiler* ::namespaces cur-ns :imports prefix)] - (if-not (nil? full-ns) + (let [cur-ns (-> env :ns :name)] + (if-some [full-ns (gets @env/*compiler* ::namespaces cur-ns :imports prefix)] {:name (symbol (str full-ns) suffix)} - (let [info (gets @env/*compiler* ::namespaces cur-ns :defs prefix)] - (if-not (nil? info) + + ;else + (if-some [info (gets @env/*compiler* ::namespaces cur-ns :defs prefix)] (merge info {:name (symbol (str cur-ns) (str sym)) :ns cur-ns}) (merge (gets @env/*compiler* ::namespaces prefix :defs (symbol suffix)) {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix)) - :ns prefix}))))))) + :ns prefix})))))) - (not (nil? (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym))) + (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)) (let [full-ns (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)] (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) :ns full-ns})) - (not (nil? (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym))) + (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym)) (let [qualified-symbol (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym) full-ns (symbol (namespace qualified-symbol)) sym (symbol (name qualified-symbol))] @@ -897,16 +892,16 @@ {:name qualified-symbol :ns full-ns})) - (not (nil? (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym))) + (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym)) (recur env (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) confirm) :else (let [cur-ns (-> env :ns :name) full-ns (cond - (not (nil? (gets @env/*compiler* ::namespaces cur-ns :defs sym))) cur-ns + (some? (gets @env/*compiler* ::namespaces cur-ns :defs sym)) cur-ns (core-name? env sym) 'cljs.core :else cur-ns)] - (when-not (nil? confirm) + (when (some? confirm) (confirm env full-ns sym)) (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) @@ -936,17 +931,17 @@ (let [ns (-> env :ns :name) namespaces (get @env/*compiler* ::namespaces)] (cond - (namespace sym) + (some? (namespace sym)) (let [ns (namespace sym) ns (if (= "clojure.core" ns) "cljs.core" ns) full-ns (resolve-macro-ns-alias env ns)] (get-in namespaces [full-ns :macros (symbol (name sym))])) - (get-in namespaces [ns :use-macros sym]) + (some? (get-in namespaces [ns :use-macros sym])) (let [full-ns (get-in namespaces [ns :use-macros sym])] (get-in namespaces [full-ns :macros sym])) - (get-in namespaces [ns :rename-macros sym]) + (some? (get-in namespaces [ns :rename-macros sym])) (let [qualified-symbol (get-in namespaces [ns :rename-macros sym]) full-ns (symbol (namespace qualified-symbol)) sym (symbol (name qualified-symbol))] @@ -954,10 +949,10 @@ :else (let [ns (cond - (get-in namespaces [ns :macros sym]) ns + (some? (get-in namespaces [ns :macros sym])) ns (core-name? env sym) #?(:clj 'cljs.core :cljs CLJS_CORE_MACROS_SYM))] - (when ns + (when (some? ns) #?(:clj (get-in namespaces [ns :macros sym]) :cljs (get-in namespaces [ns :defs sym]))))))) @@ -990,13 +985,11 @@ {:op :constant :env env :form sym :tag 'cljs.core/Keyword}) (defn get-tag [e] - (let [tag (-> e :tag)] - (if-not (nil? tag) + (if-some [tag (-> e :tag)] tag - (let [tag (-> e :info :tag)] - (if-not (nil? tag) + (if-some [tag (-> e :info :tag)] tag - (-> e :form meta :tag)))))) + (-> e :form meta :tag)))) (defn find-matching-method [f params] ;; if local fn, need to look in :info @@ -1013,18 +1006,15 @@ #?(:cljs {:tag boolean}) [env t] ;; don't use resolve-existing-var to avoid warnings - (when (and (not (nil? t)) (symbol? t)) - (let [var (resolve-var env t) - type (:type var)] - (if-not (nil? type) + (when (and (some? t) (symbol? t)) + (let [var (resolve-var env t)] + (if-some [type (:type var)] type - (let [type (-> var :info :type)] - (if-not (nil? type) + (if-some [type (-> var :info :type)] type - (let [proto (:protocol-symbol var)] - (if-not (nil? proto) + (if-some [proto (:protocol-symbol var)] proto - (get '#{cljs.core/PersistentHashMap cljs.core/List} t))))))))) + (get '#{cljs.core/PersistentHashMap cljs.core/List} t))))))) (declare infer-tag) @@ -1049,12 +1039,12 @@ #?(:clj (= then-tag IGNORE_SYM) :cljs (symbol-identical? then-tag IGNORE_SYM)) else-tag ;; TODO: temporary until we move not-native -> clj - David - (and (or (not (nil? (get NOT_NATIVE then-tag))) (type? env then-tag)) - (or (not (nil? (get NOT_NATIVE else-tag))) (type? env else-tag))) + (and (or (some? (get NOT_NATIVE then-tag)) (type? env then-tag)) + (or (some? (get NOT_NATIVE else-tag)) (type? env else-tag))) 'clj :else - (if (and (not (nil? (get BOOLEAN_OR_SEQ then-tag))) - (not (nil? (get BOOLEAN_OR_SEQ else-tag)))) + (if (and (some? (get BOOLEAN_OR_SEQ then-tag)) + (some? (get BOOLEAN_OR_SEQ else-tag))) 'seq (let [then-tag (if #?(:clj (set? then-tag) :cljs (cljs-set? then-tag)) @@ -1065,14 +1055,12 @@ (into then-tag else-tag)))))))) (defn infer-invoke [env e] - (let [{info :info :as f} (:f e) - ret-tag (when-not (nil? (:fn-var info)) (:ret-tag info))] - (if-not (nil? ret-tag) + (let [{info :info :as f} (:f e)] + (if-some [ret-tag (when (true? (:fn-var info)) (:ret-tag info))] ret-tag (let [args (:args e) - me (assoc (find-matching-method f args) :op :method) - ret-tag (infer-tag env me)] - (if-not (nil? ret-tag) + me (assoc (find-matching-method f args) :op :method)] + (if-some [ret-tag (infer-tag env me)] ret-tag ANY_SYM))))) @@ -1080,8 +1068,7 @@ "Given env, an analysis environment, and e, an AST node, return the inferred type of the node" [env e] - (let [tag (get-tag e)] - (if-not (nil? tag) + (if-some [tag (get-tag e)] tag (case (:op e) :recur IGNORE_SYM @@ -1097,12 +1084,12 @@ true BOOLEAN_SYM false BOOLEAN_SYM ANY_SYM) - :var (if-not (nil? (:init e)) - (infer-tag env (:init e)) + :var (if-some [init (:init e)] + (infer-tag env init) (infer-tag env (:info e))) :dot ANY_SYM :js ANY_SYM - nil)))) + nil))) (defmulti parse (fn [op & rest] op)) @@ -1113,7 +1100,7 @@ (let [env (dissoc env :locals) var (resolve-var env sym (confirm-var-exists-throw)) expr-env (assoc env :context :expr)] - (if-let [var-ns (:ns var)] + (when-some [var-ns (:ns var)] {:var (analyze expr-env sym) :sym (analyze expr-env `(quote ~(symbol (name var-ns) (name (:name var))))) :meta (let [ks [:ns :doc :file :line :column] @@ -1272,16 +1259,16 @@ (symbol (name sym)) :else sym)] - (when (get-in @env/*compiler* [::namespaces clash-ns]) + (when (some? (get-in @env/*compiler* [::namespaces clash-ns])) (warning :ns-var-clash env {:ns (symbol (str ns-name "." sym)) :var (symbol (str ns-name) (str sym))})) - (when (:const (resolve-var (dissoc env :locals) sym)) + (when (some? (:const (resolve-var (dissoc env :locals) sym))) (throw (error env "Can't redefine a constant"))) - (when-let [doc (:doc args)] + (when-some [doc (:doc args)] (when-not (string? doc) (throw (error env "Too many arguments to def")))) - (when-let [v (get-in @env/*compiler* [::namespaces ns-name :defs sym])] + (when-some [v (get-in @env/*compiler* [::namespaces ns-name :defs sym])] (when (and (not *allow-redef*) (not (:declared v)) (not (:declared sym-meta)) @@ -1292,7 +1279,7 @@ (swap! *file-defs* conj sym)) (let [env (if (or (and (not= ns-name 'cljs.core) (core-name? env sym)) - (get-in @env/*compiler* [::namespaces ns-name :uses sym])) + (some? (get-in @env/*compiler* [::namespaces ns-name :uses sym]))) (let [ev (resolve-existing-var (dissoc env :locals) sym) conj-to-set (fnil conj #{})] (warning :redef env {:sym sym :ns (:ns ev) :ns-name ns-name}) @@ -1306,21 +1293,21 @@ (merge {:name var-name} sym-meta - (when dynamic {:dynamic true}) + (when (true? dynamic) {:dynamic true}) (source-info var-name env))) (disallowing-recur (disallowing-ns* (analyze (assoc env :context :expr) (:init args) sym)))) - fn-var? (and init-expr (= (:op init-expr) :fn)) + fn-var? (and (some? init-expr) (= (:op init-expr) :fn)) tag (if fn-var? (or (:ret-tag init-expr) tag) tag) export-as (when-let [export-val (-> sym meta :export)] (if (= true export-val) var-name export-val)) doc (or (:doc args) (-> sym meta :doc))] - (when-let [v (get-in @env/*compiler* [::namespaces ns-name :defs sym])] + (when-some [v (get-in @env/*compiler* [::namespaces ns-name :defs sym])] (when (and (not (-> sym meta :declared)) - (and (:fn-var v) (not fn-var?))) + (and (true? (:fn-var v)) (not fn-var?))) (warning :fn-var env {:ns-name ns-name :sym sym}))) (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] (merge @@ -1337,7 +1324,7 @@ "cljs/core.cljs" f))))} (when doc {:doc doc}) - (when dynamic {:dynamic true}) + (when (true? dynamic) {:dynamic true}) (source-info var-name env) ;; the protocol a protocol fn belongs to (when protocol @@ -1355,14 +1342,14 @@ :protocol-impl (:protocol-impl init-expr) ;; inline protocol implementation context :protocol-inline (:protocol-inline init-expr)} - (if-let [top-fn-meta (:top-fn sym-meta)] + (if-some [top-fn-meta (:top-fn sym-meta)] top-fn-meta {:variadic (:variadic init-expr) :max-fixed-arity (:max-fixed-arity init-expr) :method-params params :arglists (:arglists sym-meta) :arglists-meta (doall (map meta (:arglists sym-meta)))}))) ) - (when (and fn-var? tag) + (when (and fn-var? (some? tag)) {:ret-tag tag}))) (merge {:env env @@ -1379,17 +1366,17 @@ :doc doc :jsdoc (:jsdoc sym-meta) :init init-expr} - (when (:def-emits-var env) + (when (true? (:def-emits-var env)) {:var-ast (var-ast env sym)}) - (when-let [test (:test sym-meta)] + (when-some [test (:test sym-meta)] {:test (analyze (assoc env :context :expr) test)}) - (when tag + (when (some? tag) (if fn-var? {:ret-tag tag} {:tag tag})) - (when dynamic {:dynamic true}) - (when export-as {:export export-as}) - (when init-expr {:children [init-expr]}))))) + (when (true? dynamic) {:dynamic true}) + (when (some? export-as) {:export export-as}) + (when (some? init-expr) {:children [init-expr]}))))) (defn analyze-fn-method-param [env] (fn [[locals params] name] @@ -1399,7 +1386,7 @@ column (get-col name env) nmeta (meta name) tag (:tag nmeta) - shadow (when-not (nil? locals) + shadow (when (some? locals) (locals name)) env (merge (select-keys env [:context]) {:line line :column column}) @@ -1451,7 +1438,7 @@ (declare analyze-wrap-meta) (defn fn-name-var [env locals name] - (when-not (nil? name) + (when (some? name) (let [ns (-> env :ns :name) shadow (get locals name) shadow (when (nil? shadow) @@ -1463,7 +1450,7 @@ :ns ns :shadow shadow}} tag (-> name meta :tag) - ret-tag (when-not (nil? tag) + ret-tag (when (some? tag) {:ret-tag tag})] (merge name-var ret-tag)))) @@ -1485,10 +1472,10 @@ meths) locals (:locals env) name-var (fn-name-var env locals name) - env (if-not (nil? name) + env (if (some? name) (update-in env [:fn-scope] conj name-var) env) - locals (if (and (not (nil? locals)) + locals (if (and (some? locals) named-fn?) (assoc locals name name-var) locals) @@ -1513,7 +1500,7 @@ :max-fixed-arity mfa :method-params (map :params methods)) locals) - methods (if-not (nil? name) + methods (if (some? name) ;; a second pass with knowledge of our function-ness/arity ;; lets us optimize self calls (disallowing-ns* (analyze-fn-methods-pass2 menv locals type meths)) @@ -1625,22 +1612,20 @@ (analyze env init))) (defn get-let-tag [name init-expr] - (let [tag (-> name meta :tag)] - (if-not (nil? tag) + (if-some [tag (-> name meta :tag)] tag - (let [tag (-> init-expr :tag)] - (if-not (nil? tag) + (if-some [tag (-> init-expr :tag)] tag - (-> init-expr :info :tag)))))) + (-> init-expr :info :tag)))) (defn analyze-let-bindings* [encl-env bindings] (loop [bes [] env (assoc encl-env :context :expr) bindings (seq (partition 2 bindings))] - (let [binding (first bindings)] - (if-not (nil? binding) - (let [[name init] binding] - (when (or (not (nil? (namespace name))) + + (if-some [[name init] (first bindings)] + (let [] + (when (or (some? (namespace name)) #?(:clj (.contains (str name) ".") :cljs ^boolean (goog.string/contains (str name) "."))) (throw (error encl-env (str "Invalid local name: " name)))) @@ -1672,7 +1657,7 @@ (recur (conj bes be) (assoc-in env [:locals name] be) (next bindings)))) - [bes env])))) + [bes env]))) (defn analyze-let-bindings [encl-env bindings] (disallowing-recur (analyze-let-bindings* encl-env bindings))) @@ -1698,7 +1683,7 @@ *recur-frames*) loop-lets (cond (true? is-loop) *loop-lets* - (not (nil? *loop-lets*)) (cons {:params bes} *loop-lets*)) + (some? *loop-lets*) (cons {:params bes} *loop-lets*)) expr (analyze-let-body env context exprs recur-frames loop-lets) op (if (true? is-loop) :loop :let) children (conj (vec (map :init bes)) expr)] @@ -1750,7 +1735,7 @@ known-num-fields (:num-fields ctor-var) argc (count args)] (when (and (not (-> ctor meta :internal-ctor)) - known-num-fields (not= known-num-fields argc)) + (some? known-num-fields) (not= known-num-fields argc)) (warning :fn-arity env {:argc argc :ctor ctor})) {:env env :op :new :form form :ctor ctorexpr :args argexprs :children (into [ctorexpr] argexprs) @@ -1778,15 +1763,14 @@ (set! *unchecked-if* val) ::set-unchecked-if) - (and - (= target '*warn-on-infer*)) + (= target '*warn-on-infer*) (do (set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning true)) ::set-warn-on-infer) (symbol? target) (do - (when (:const (resolve-var (dissoc env :locals) target)) + (when (some? (:const (resolve-var (dissoc env :locals) target))) (throw (error env "Can't set! a constant"))) (let [local (-> env :locals target)] (when-not (or (nil? local) @@ -1806,7 +1790,7 @@ (when-not targetexpr (throw (error env "set! target must be a field or a symbol naming a var"))) (cond - (#{::set-unchecked-if ::set-warn-on-infer} targetexpr) + (some? (#{::set-unchecked-if ::set-warn-on-infer} targetexpr)) {:env env :op :no-op} :else @@ -1841,7 +1825,7 @@ [dep] {:pre [(symbol? dep)]} (let [js-index (:js-dependency-index @env/*compiler*)] - (if-let [[_ {:keys [foreign]}] (find js-index (name dep))] + (if-some [[_ {:keys [foreign]}] (find js-index (name dep))] foreign false))) @@ -1863,7 +1847,7 @@ (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) #?(:clj (deps/find-classpath-lib dep))) - #?(:clj (if-let [src (locate-src dep)] + #?(:clj (if-some [src (locate-src dep)] (analyze-file src opts) (throw (error env @@ -2027,7 +2011,7 @@ (throw (error env "Only [:refer-clojure :exclude (names)] and optionally `:rename {from to}` specs supported")) - (not (nil? fs)) + (some? fs) (let [kw (first fs)] (if (valid-kws kw) (let [refs (second fs)] @@ -2063,11 +2047,11 @@ (parse-ns-error-msg spec "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros"))) - (not (nil? fs)) + (some? fs) (let [kw (first fs) only? (= kw :only)] (if (or only? (= kw :rename)) - (if (some #{(if only? :refer kw)} ret) + (if (some? (some #{(if only? :refer kw)} ret)) (throw (error env (parse-ns-error-msg spec @@ -2079,7 +2063,7 @@ (recur (nnext fs) (into ret [(if only? :refer kw) refs]) false)))) (recur fs ret true ))) - :else (if (some #{:refer} ret) + :else (if (some? (some #{:refer} ret)) ret (recur fs ret true))))) @@ -2093,7 +2077,7 @@ ;; Google Closure compiler, e.g. module$resources$libs$calculator. ;; This means that we need to create an alias from the module name ;; given with :provides to the new name. - [lib js-module-provides] (if-let [js-module-name (get-in @env/*compiler* [:js-module-index (str lib)])] + [lib js-module-provides] (if-some [js-module-name (get-in @env/*compiler* [:js-module-index (str lib)])] [(symbol js-module-name) lib] [lib nil]) {alias :as referred :refer renamed :rename :or {alias lib}} (apply hash-map opts) @@ -2104,10 +2088,10 @@ (error env (parse-ns-error-msg spec ":as must be followed by a symbol in :require / :require-macros")))) - (when alias + (when (some? alias) (let [alias-type (if macros? :macros :fns) lib' ((alias-type @aliases) alias)] - (when (and (not (nil? lib')) (not= lib lib')) + (when (and (some? lib') (not= lib lib')) (throw (error env (parse-ns-error-msg spec ":as alias must be unique")))) (swap! aliases update-in [alias-type] @@ -2122,11 +2106,12 @@ (when-not macros? (swap! deps conj lib)) (merge - (when alias + (when (some? alias) {rk (merge {alias lib} {lib lib} (when js-module-provides {js-module-provides lib}))}) - (when referred-without-renamed {uk (apply hash-map (interleave referred-without-renamed (repeat lib)))}) - (when renamed + (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 @@ -2264,7 +2249,7 @@ (replace-refer-macros)) x))))) remove-sugar (partial remove-from-spec sugar-keys)] - (if-let [require-specs (seq (to-macro-specs require))] + (if-some [require-specs (seq (to-macro-specs require))] (map (fn [x] (if-not (reload-spec? x) (let [[k v] x] @@ -2301,7 +2286,7 @@ (let [segments (string/split (clojure.core/name name) #"\.")] (when (= 1 (count segments)) (warning :single-segment-namespace env {:name name})) - (when (some js-reserved segments) + (when (some? (some js-reserved segments)) (warning :munged-namespace env {:name name})) (find-def-clash env name segments) #?(:clj @@ -2310,15 +2295,15 @@ (AssertionError. (str "Namespace " name " has a segment starting with an invaild " "JavaScript identifier")))))) - (let [docstring (if (string? (first args)) (first args)) + (let [docstring (when (string? (first args)) (first args)) mdocstr (-> name meta :doc) - args (if docstring (next args) args) - metadata (if (map? (first args)) (first args)) + args (if (some? docstring) (next args) args) + metadata (when (map? (first args)) (first args)) form-meta (meta form) args (desugar-ns-specs #?(:clj (rewrite-cljs-aliases (if metadata (next args) args)) - :cljs (if metadata (next args) args))) + :cljs (if (some? metadata) (next args) args))) name (vary-meta name merge metadata) {excludes :excludes core-renames :renames} (parse-ns-excludes env args) core-renames (reduce (fn [m [original renamed]] @@ -2348,9 +2333,9 @@ (swap! valid-forms disj k) ;; check for spec type reloads (when-not (= :import k) - (when (some #{:reload} libs) + (when (some? (some #{:reload} libs)) (swap! reload assoc k :reload)) - (when (some #{:reload-all} libs) + (when (some? (some #{:reload-all} libs)) (swap! reload assoc k :reload-all))) ;; check for individual ns reloads from REPL interactions (when-let [xs (seq (filter #(-> % meta :reload) libs))] @@ -2439,12 +2424,12 @@ (fn [m [k & libs]] ;; check for spec type reloads (when-not (= :import k) - (when (some #{:reload} libs) + (when (some? (some #{:reload} libs)) (swap! reload assoc k :reload)) - (when (some #{:reload-all} libs) + (when (some? (some #{:reload-all} libs)) (swap! reload assoc k :reload-all))) ;; check for individual ns reloads from REPL interactions - (when-let [xs (seq (filter #(-> % meta :reload) libs))] + (when-some [xs (seq (filter #(-> % meta :reload) libs))] (swap! reloads assoc k (zipmap (map first xs) (map #(-> % meta :reload) xs)))) (apply merge-with merge m @@ -2646,9 +2631,8 @@ (disallowing-recur (analyze-dot env target field member+ form))) (defn get-js-tag [form] - (let [form-meta (meta form) - tag (:tag form-meta)] - (if-not (nil? tag) + (let [form-meta (meta form)] + (if-some [tag (:tag form-meta)] tag (when (true? (:numeric form-meta)) 'number)))) @@ -2688,7 +2672,7 @@ (nil? t) true (js-tag? t) true ;; TODO: revisit :else - (if (and (symbol? t) (not (nil? (get NUMERIC_SET t)))) + (if (and (symbol? t) (some? (get NUMERIC_SET t))) true (when #?(:clj (set? t) :cljs (cljs-set? t)) @@ -2728,7 +2712,7 @@ [op env [_ jsform & args :as form] _ _] (when-not (string? jsform) (throw (error env "Invalid js* form"))) - (if-not (nil? args) + (if (some? args) (analyze-js-star env jsform args form) (let [code (apply str (js-star-interp env jsform)) tag (get-js-tag form) @@ -2785,7 +2769,7 @@ (when (and (boolean deprecated?) (not (boolean no-warn?))) (warning :fn-deprecated env {:fexpr fexpr}))) - (when-not (nil? (-> fexpr :info :type)) + (when (some? (-> fexpr :info :type)) (warning :invoke-ctor env {:fexpr fexpr})) (if (or (not (boolean *cljs-static-fns*)) (not (symbol? f)) @@ -2820,9 +2804,8 @@ (assoc env :column column) env) ret {:env env :form sym} - lcls (:locals env) - lb (get lcls sym)] - (if-not (nil? lb) + lcls (:locals env)] + (if-some [lb (get lcls sym)] (assoc ret :op :var :info lb) (let [sym-meta (meta sym) sym-ns (namespace sym) @@ -2848,22 +2831,20 @@ (defn excluded? #?(:cljs {:tag boolean}) [env sym] - (if-not (nil? (gets env :ns :excludes sym)) - true - (not (nil? (gets @env/*compiler* ::namespaces (gets env :ns :name) :excludes sym))))) + (or (some? (gets env :ns :excludes sym)) + (some? (gets @env/*compiler* ::namespaces (gets env :ns :name) :excludes sym)))) (defn used? #?(:cljs {:tag boolean}) [env sym] - (if-not (nil? (gets env :ns :use-macros sym)) - true - (not (nil? (gets @env/*compiler* ::namespaces (gets env :ns :name) :use-macros sym))))) + (or (some? (gets env :ns :use-macros sym)) + (some? (gets @env/*compiler* ::namespaces (gets env :ns :name) :use-macros sym)))) (defn get-expander-ns [env ^String nstr] ;; first check for clojure.* -> cljs.* cases (let [res (or (resolve-macro-ns-alias env nstr nil) (resolve-ns-alias env nstr nil)) - nstr (if res (str res) nstr)] + nstr (if (some? res) (str res) nstr)] (cond #?@(:clj [(= "clojure.core" nstr) (find-ns 'cljs.core)] :cljs [(identical? "clojure.core" nstr) (find-macros-ns CLJS_CORE_MACROS_SYM)]) @@ -2875,16 +2856,16 @@ :cljs find-macros-ns))))) (defn get-expander* [sym env] - (when-not (or (not (nil? (gets env :locals sym))) ; locals hide macros + (when-not (or (some? (gets env :locals sym)) ; locals hide macros (and (excluded? env sym) (not (used? env sym)))) (let [nstr (namespace sym)] (cond - (not (nil? nstr)) + (some? nstr) (let [ns (get-expander-ns env nstr)] - (when-not (nil? ns) + (when (some? ns) (.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym))))) - (not (nil? (gets env :ns :rename-macros sym))) + (some? (gets env :ns :rename-macros sym)) (let [qualified-symbol (gets env :ns :rename-macros sym) nsym (symbol (namespace qualified-symbol)) sym (symbol (name qualified-symbol))] @@ -2892,19 +2873,18 @@ #?(:clj (find-ns nsym) :cljs (find-macros-ns nsym)) sym)) :else - (let [nsym (gets env :ns :use-macros sym)] - (if-not (nil? nsym) + (if-some [nsym (gets env :ns :use-macros sym)] (.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 CLJS_CORE_MACROS_SYM)) sym))))))) + #?(:clj (find-ns 'cljs.core) :cljs (find-macros-ns CLJS_CORE_MACROS_SYM)) sym)))))) (defn get-expander "Given a sym, a symbol identifying a macro, and env, an analysis environment return the corresponding Clojure macroexpander." [sym env] (let [mvar (get-expander* sym env)] - (when (and (not (nil? mvar)) + (when (and (some? mvar) #?(:clj (.isMacro ^clojure.lang.Var mvar) :cljs ^boolean (.isMacro mvar))) mvar))) @@ -2912,16 +2892,16 @@ (defn macroexpand-1* [env form] (let [op (first form)] - (if-not (nil? (get specials op)) + (if (contains? specials op) form - (let [mac-var (when (symbol? op) (get-expander op env))] - (if-not (nil? mac-var) + ;else + (if-some [mac-var (when (symbol? op) (get-expander op env))] (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) (let [mchk #?(:clj (some-> (find-ns 'clojure.spec) (ns-resolve 'macroexpand-check)) :cljs (get (ns-interns* 'cljs.spec) 'macroexpand-check)) - _ (when mchk + _ (when (some? mchk) (mchk mac-var (next form))) form' (try (apply @mac-var form env (rest form)) @@ -2932,7 +2912,7 @@ sym (first form)] (if #?(:clj (= sym' 'js*) :cljs (symbol-identical? sym' JS_STAR_SYM)) - (let [sym (if (namespace sym) + (let [sym (if (some? (namespace sym)) sym (symbol "cljs.core" (str sym))) js-op {:js-op sym} @@ -2965,7 +2945,7 @@ (meta form)) :else form)) - form)))))) + form))))) (defn macroexpand-1 "Given a env, an analysis environment, and form, a ClojureScript form, @@ -2976,7 +2956,7 @@ (declare analyze-list) (defn analyze-seq* [op env form name opts] - (if-not (nil? (get specials op)) + (if (contains? specials op) (parse op env form name opts) (parse-invoke env form))) @@ -3056,7 +3036,7 @@ (defn analyze-wrap-meta [expr] (let [form (:form expr) m (elide-reader-meta (meta form))] - (if (seq m) + (if (some? (seq m)) (let [env (:env expr) ; take on expr's context ourselves expr (assoc-in expr [:env :context] :expr) ; change expr to :expr meta-expr (analyze-map (:env expr) m)] @@ -3065,13 +3045,11 @@ expr))) (defn infer-type [env ast _] - (let [tag (:tag ast)] - (if (nil? tag) - (let [tag (infer-tag env ast)] - (if-not (nil? tag) + (if (nil? (:tag ast)) + (if-some [tag (infer-tag env ast)] (assoc ast :tag tag) - ast)) - ast))) + ast) + ast)) (defn- repl-self-require? [env deps] (and (:repl-env env) (some #{*cljs-ns*} deps))) @@ -3147,7 +3125,7 @@ (defn analyze-form [env form name opts] (cond (symbol? form) (analyze-symbol env form) - (and (cljs-seq? form) (seq form)) (analyze-seq env form name opts) + (and (cljs-seq? form) (some? (seq form))) (analyze-seq env form name opts) (cljs-map? form) (analyze-map env form) (cljs-vector? form) (analyze-vector env form) (cljs-set? form) (analyze-set env form) @@ -3458,7 +3436,7 @@ *cljs-file* nil reader/*alias-map* (or reader/*alias-map* {})] (loop [ns nil forms forms] - (if forms + (if (some? forms) (let [form (first forms) env (assoc env :ns (get-namespace *cljs-ns*)) ast (analyze env form nil opts)] From cb66c277fc869ab9a5892cfb9ce6d1d0121d3dc7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 22 Dec 2016 07:02:26 +0100 Subject: [PATCH 2156/4033] str macro should call str/1 function directly, added str benchmark --- benchmark/cljs/benchmark_runner.cljs | 9 +++++++++ src/main/clojure/cljs/core.cljc | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 723316bfb..f3f68d573 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -388,3 +388,12 @@ (recur xs) x)) 10) + +(println "\n") +(println ";;; str") +(simple-benchmark [] (str 1) 1000000) +(simple-benchmark [] (str nil) 1000000) +(simple-benchmark [] (str "1") 1000000) +(simple-benchmark [] (str "1" "2") 1000000) +(simple-benchmark [] (str "1" "2" "3") 1000000) +(println) \ No newline at end of file diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index c3cf6fdc0..a73f8d085 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -806,7 +806,7 @@ (core/inc (core/quot c 32))))) (core/defmacro str [& xs] - (core/let [strs (core/->> (repeat (count xs) "cljs.core.str(~{})") + (core/let [strs (core/->> (repeat (count xs) "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})") (interpose ",") (apply core/str))] (list* 'js* (core/str "[" strs "].join('')") xs))) From abe62e15dd178bae3c0617d26c28f7083bc6c199 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 30 Dec 2016 13:55:20 -0500 Subject: [PATCH 2157/4033] add helper for expanding foreign-lib entries which specify directories --- src/main/clojure/cljs/closure.clj | 12 ++++++++++++ src/main/clojure/cljs/compiler.cljc | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e19d43be1..d8ecf01b6 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1804,6 +1804,18 @@ (and (satisfies? deps/IJavaScript js) (deps/-foreign? js))) +(defn expand-foreign [{:keys [foreign-libs]}] + (letfn [(expand-foreign* [{:keys [file] :as lib}] + (let [dir (io/file file)] + (if (.isDirectory dir) + (into [] + (comp + (filter #(.endsWith (.getName ^File %) ".js")) + (map #(assoc lib :file (.getPath ^File %)))) + (file-seq dir)) + [lib])))] + (into [] (mapcat expand-foreign* foreign-libs)))) + (defn add-implicit-options [{:keys [optimizations output-dir] :or {optimizations :none diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 0e7665841..993db5265 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1426,7 +1426,7 @@ (filter #(let [name (.getName ^File %)] (and (or (.endsWith name ".cljs") - (.endsWith name ".cljc")) + (.endsWith name ".cljc")) (not= \. (first name)) (not (contains? cljs-reserved-file-names name)))) (file-seq dir)))) From 08b5d185b616d309a5dd8cb25e5c08d59d2f5005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 2 Jan 2017 17:07:45 +0000 Subject: [PATCH 2158/4033] CLJS-1853: var metadata in compiled output the `cljs.pprint/write-option-table` map has Vars as its values, which causes their metadata to be included in the compiled output, even when compiling with advanced optimizations. This patch places comments around that map, which is unused throughout the whole codebase. --- src/main/cljs/cljs/pprint.cljs | 36 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 13d63b916..a3bfc60fb 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -689,23 +689,25 @@ radix specifier is in the form #XXr where XX is the decimal value of *print-base (declare format-simple-number) -(def ^{:private true} write-option-table - {;:array *print-array* - :base #'cljs.pprint/*print-base*, - ;;:case *print-case*, - :circle #'cljs.pprint/*print-circle*, - ;;:escape *print-escape*, - ;;:gensym *print-gensym*, - :length #'cljs.core/*print-length*, - :level #'cljs.core/*print-level*, - :lines #'cljs.pprint/*print-lines*, - :miser-width #'cljs.pprint/*print-miser-width*, - :dispatch #'cljs.pprint/*print-pprint-dispatch*, - :pretty #'cljs.pprint/*print-pretty*, - :radix #'cljs.pprint/*print-radix*, - :readably #'cljs.core/*print-readably*, - :right-margin #'cljs.pprint/*print-right-margin*, - :suppress-namespaces #'cljs.pprint/*print-suppress-namespaces*}) +;; This map causes var metadata to be included in the compiled output, even +;; in advanced compilation. See CLJS-1853 - António Monteiro +;; (def ^{:private true} write-option-table +;; {;:array *print-array* +;; :base #'cljs.pprint/*print-base*, +;; ;;:case *print-case*, +;; :circle #'cljs.pprint/*print-circle*, +;; ;;:escape *print-escape*, +;; ;;:gensym *print-gensym*, +;; :length #'cljs.core/*print-length*, +;; :level #'cljs.core/*print-level*, +;; :lines #'cljs.pprint/*print-lines*, +;; :miser-width #'cljs.pprint/*print-miser-width*, +;; :dispatch #'cljs.pprint/*print-pprint-dispatch*, +;; :pretty #'cljs.pprint/*print-pretty*, +;; :radix #'cljs.pprint/*print-radix*, +;; :readably #'cljs.core/*print-readably*, +;; :right-margin #'cljs.pprint/*print-right-margin*, +;; :suppress-namespaces #'cljs.pprint/*print-suppress-namespaces*}) (defn- table-ize [t m] (apply hash-map (mapcat From 12c805f7417f39135d2b36985cf2cda98a08fe40 Mon Sep 17 00:00:00 2001 From: r0man Date: Fri, 30 Dec 2016 23:42:52 +0100 Subject: [PATCH 2159/4033] CLJS-1882 Fix constant table sort order when using :modules --- src/main/clojure/cljs/analyzer.cljc | 8 +++-- src/main/clojure/cljs/closure.clj | 28 ++++++++++++------ src/main/clojure/cljs/compiler.cljc | 8 +++-- src/test/clojure/cljs/build_api_tests.clj | 34 +++++++-------------- src/test/clojure/cljs/closure_tests.clj | 28 +++++++++++++++++- src/test/clojure/cljs/test_util.clj | 36 +++++++++++++++++++++++ 6 files changed, 104 insertions(+), 38 deletions(-) create mode 100644 src/test/clojure/cljs/test_util.clj diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 8e8647f3d..e7ba5c560 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -56,6 +56,10 @@ (def ^:dynamic *file-defs* nil) +(def constants-ns-sym + "The namespace of the constants table as a symbol." + 'cljs.core.constants) + #?(:clj (def transit-read-opts (try @@ -3300,7 +3304,7 @@ :macros-ns (:macros-ns opts) :requires (cond-> #{'cljs.core} (get-in @env/*compiler* [:options :emit-constants]) - (conj 'constants-table))} + (conj constants-ns-sym))} (when (and dest (.exists ^File dest)) {:lines (with-open [reader (io/reader dest)] (-> reader line-seq count))}))] @@ -3322,7 +3326,7 @@ (set (vals deps)) (cond-> (conj (set (vals deps)) 'cljs.core) (get-in @env/*compiler* [:options :emit-constants]) - (conj 'constants-table))) + (conj constants-ns-sym))) :file dest :source-file (when rdr src) :source-forms (when-not rdr src) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d8ecf01b6..f1694c128 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -699,6 +699,18 @@ (conj cljs-namespaces ns-info))) (disj cljs-namespaces nil))))) +(defn- constants-filename + "Returns the filename of the constants table." + [opts] + (str (util/output-directory opts) File/separator + (string/replace (str ana/constants-ns-sym) "." File/separator) ".js")) + +(defn- constants-javascript-file + "Returns the constants table as a JavaScriptFile." + [opts] + (let [url (deps/to-url (constants-filename opts))] + (javascript-file nil url url [(str ana/constants-ns-sym)] ["cljs.core"] nil nil))) + (defn add-dependencies "Given one or more IJavaScript objects in dependency order, produce a new sequence of IJavaScript objects which includes the input list @@ -710,7 +722,7 @@ required-js (js-dependencies opts (into (set (mapcat deps/-requires required-cljs)) requires)) provided (set (mapcat deps/-provides (clojure.set/union inputs required-cljs required-js))) - unprovided (clojure.set/difference requires provided #{"constants-table"})] + unprovided (clojure.set/difference requires provided)] (when (seq unprovided) (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) (cons @@ -725,8 +737,7 @@ js-map))) required-js) (when (-> @env/*compiler* :options :emit-constants) - (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] - [(javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)])) + [(constants-javascript-file opts)]) required-cljs inputs))))) @@ -764,7 +775,7 @@ [inputs] (let [requires (set (mapcat deps/-requires inputs)) provided (set (mapcat deps/-provides inputs)) - unprovided (clojure.set/difference requires provided #{"constants-table"})] + unprovided (clojure.set/difference requires provided)] (when (seq unprovided) (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) inputs)) @@ -858,8 +869,7 @@ js-map))) required-js) (when (-> @env/*compiler* :options :emit-constants) - (let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))] - [(javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)])) + [(constants-javascript-file opts)]) inputs))) (defn add-preloads @@ -873,7 +883,7 @@ inputs (let [pred (fn [x] (if (:emit-constants opts) - (not= ["constants-table"] (:provides x)) + (not= [(str ana/constants-ns-sym)] (:provides x)) (not= ["cljs.core"] (:provides x)))) pre (take-while pred inputs) post (drop-while pred inputs) @@ -2018,8 +2028,8 @@ (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)]))) _ (when (:emit-constants all-opts) (comp/emit-constants-table-to-file - (::ana/constant-table @env/*compiler*) - (str (util/output-directory all-opts) "/constants_table.js"))) + (::ana/constant-table @env/*compiler*) + (constants-filename all-opts))) _ (when (:infer-externs all-opts) (comp/emit-inferred-externs-to-file (reduce util/map-merge {} diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 993db5265..99dcbcc13 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1076,7 +1076,9 @@ [{:keys [name requires uses require-macros reloads env]}] (emitln "goog.provide('" (munge name) "');") (when-not (= name 'cljs.core) - (emitln "goog.require('cljs.core');")) + (emitln "goog.require('cljs.core');") + (when (-> @env/*compiler* :options :emit-constants) + (emitln "goog.require('" (munge ana/constants-ns-sym) "');"))) (load-libs requires nil (:require reloads)) (load-libs uses requires (:use reloads))) @@ -1300,7 +1302,7 @@ (set (vals deps)) (cond-> (conj (set (vals deps)) 'cljs.core) (get-in @env/*compiler* [:options :emit-constants]) - (conj 'constants-table))) + (conj ana/constants-ns-sym))) :file dest :source-file src} (when sm-data @@ -1473,6 +1475,8 @@ ;; TODO: needs fixing, table will include other things than keywords - David (defn emit-constants-table [table] + (emitln "goog.provide('" (munge ana/constants-ns-sym) "');") + (emitln "goog.require('cljs.core');") (doseq [[sym value] table] (let [ns (namespace sym) name (name sym)] diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 9d57755ad..f84c7cde0 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -13,7 +13,8 @@ (:import java.io.File) (:require [clojure.java.io :as io] [cljs.env :as env] - [cljs.analyzer :as ana])) + [cljs.analyzer :as ana] + [cljs.test-util :as test])) (deftest test-target-file-for-cljs-ns (is (= (.getPath (target-file-for-cljs-ns 'example.core-lib nil)) @@ -130,29 +131,14 @@ "The files are not empty after compilation"))) (deftest cljs-1500-test-modules - (let [module-main (io/file "out/module-main.js") - module-a (io/file "out/module-a.js") - module-b (io/file "out/module-b.js")] - (.delete module-main) - (.delete module-a) - (.delete module-b) - (build - (inputs "src/test/cljs") - {:main "module-test.main" - :optimizations :advanced - :verbose true - :modules - {:cljs-base - {:output-to (str module-main)} - :module-a - {:output-to (str module-a) - :entries #{'module-test.modules.a}} - :module-b - {:output-to (str module-b) - :entries #{'module-test.modules.b}}}}) - (is (re-find #"Loading modules A and B" (slurp module-main))) - (is (re-find #"Module A loaded" (slurp module-a))) - (is (re-find #"Module B loaded" (slurp module-b))))) + (let [out (str (System/getProperty "java.io.tmpdir") "/cljs-1500-out") + project (test/project-with-modules out) + modules (-> project :opts :modules)] + (test/clean-outputs (:opts project)) + (build (inputs (:inputs project)) (:opts project)) + (is (re-find #"Loading modules A and B" (slurp (-> modules :cljs-base :output-to)))) + (is (re-find #"Module A loaded" (slurp (-> modules :module-a :output-to)))) + (is (re-find #"Module B loaded" (slurp (-> modules :module-b :output-to)))))) (deftest cljs-1537-circular-deps (let [out-file (io/file "out/main.js")] diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 8799cac19..015c5231d 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -9,7 +9,13 @@ (ns cljs.closure-tests (:refer-clojure :exclude [compile]) (:use cljs.closure) - (:use clojure.test)) + (:use clojure.test) + (:import [com.google.javascript.jscomp JSModule]) + (:require [cljs.build.api :as build] + [cljs.closure :as closure] + [cljs.test-util :as test] + [clojure.java.io :as io] + [clojure.string :as string])) (deftest test-make-preamble (testing "no options" @@ -42,3 +48,23 @@ (is (check-source-map {:source-map true :optimizations :none})) (is (check-source-map {:source-map false :optimizations :none})) (is (thrown? AssertionError (check-source-map {:source-map "target/build/app.js.map" :optimizations :none}))))) + +(deftest test-cljs-1882-constants-table-is-sorted + (let [out (str (System/getProperty "java.io.tmpdir") "/cljs-1882-out") + project (test/project-with-modules out) + modules (-> project :opts :modules)] + (test/clean-outputs (:opts project)) + (build/build (build/inputs (:inputs project)) (:opts project)) + (let [compiler (closure/make-closure-compiler) + module (JSModule. "module-c")] + (.initOptions compiler (closure/make-options (:opts project))) + (doseq [file ["cljs/core/constants.js" + "module_test/modules/a.js" + "cljs/core.js"]] + (.add module (closure/js-source-file nil (io/file out file)))) + (.sortInputsByDeps module compiler) + (is (= (->> (.getInputs module) + (map #(string/replace (.getName %) (str out "/") ""))) + ["cljs/core.js" + "cljs/core/constants.js" + "module_test/modules/a.js"]))))) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj new file mode 100644 index 000000000..cf8481ae6 --- /dev/null +++ b/src/test/clojure/cljs/test_util.clj @@ -0,0 +1,36 @@ +;; 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.test-util + (:require [clojure.java.io :as io])) + +(defn clean-outputs + "Remove the :output-to artifacts of a ClojureScript build." + [{:keys [output-to modules] :as opts}] + (some-> output-to io/file .delete) + (doseq [{:keys [output-to]} (vals modules)] + (.delete (io/file output-to)))) + +(defn project-with-modules + "Returns the build config for a project that uses Google Closure modules." + [output-dir] + {:inputs "src/test/cljs" + :opts + {:main "module-test.main" + :output-dir output-dir + :optimizations :advanced + :verbose true + :modules + {:cljs-base + {:output-to (str output-dir "/module-main.js")} + :module-a + {:output-to (str output-dir "/module-a.js") + :entries #{'module-test.modules.a}} + :module-b + {:output-to (str output-dir "/module-b.js") + :entries #{'module-test.modules.b}}}}}) From 15f242532b6dcf27e7336891fb2b6bea37ee6f14 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 4 Jan 2017 07:48:11 -0500 Subject: [PATCH 2160/4033] fix CLJS-1882 test --- src/test/clojure/cljs/closure_tests.clj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 015c5231d..a0e32256d 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -8,14 +8,14 @@ (ns cljs.closure-tests (:refer-clojure :exclude [compile]) - (:use cljs.closure) - (:use clojure.test) - (:import [com.google.javascript.jscomp JSModule]) + (:use cljs.closure clojure.test) (:require [cljs.build.api :as build] [cljs.closure :as closure] [cljs.test-util :as test] [clojure.java.io :as io] - [clojure.string :as string])) + [clojure.string :as string]) + (:import [java.io File] + [com.google.javascript.jscomp JSModule])) (deftest test-make-preamble (testing "no options" @@ -50,7 +50,7 @@ (is (thrown? AssertionError (check-source-map {:source-map "target/build/app.js.map" :optimizations :none}))))) (deftest test-cljs-1882-constants-table-is-sorted - (let [out (str (System/getProperty "java.io.tmpdir") "/cljs-1882-out") + (let [out (.getPath (io/file (System/getProperty "java.io.tmpdir") "cljs-1882-out")) project (test/project-with-modules out) modules (-> project :opts :modules)] (test/clean-outputs (:opts project)) @@ -64,7 +64,7 @@ (.add module (closure/js-source-file nil (io/file out file)))) (.sortInputsByDeps module compiler) (is (= (->> (.getInputs module) - (map #(string/replace (.getName %) (str out "/") ""))) + (map #(string/replace (.getName %) (str out File/separator) ""))) ["cljs/core.js" "cljs/core/constants.js" "module_test/modules/a.js"]))))) From 8e183ba8ad3eefea75c127a5fdb76c2925b809f0 Mon Sep 17 00:00:00 2001 From: r0man Date: Wed, 4 Jan 2017 16:49:10 +0100 Subject: [PATCH 2161/4033] CLJS-1883 Foreign libs can't be found on Node.js Since CLJS-1718 foreign libs are placed under their relative path, but still loaded from the root when using Node.js. This patch makes sure they are loaded from the correct place by using util/relative-name instead of util/get-name. --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/cljs_build/foreign_libs/core.cljs | 13 +++++++++ src/test/cljs_build/thirdparty/add.js | 11 ++++++++ src/test/clojure/cljs/build_api_tests.clj | 26 ++++++++++++++--- src/test/clojure/cljs/closure_tests.clj | 4 +-- .../clojure/cljs/module_processing_tests.clj | 15 +++------- src/test/clojure/cljs/test_util.clj | 28 ++++++++++++------- 7 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 src/test/cljs_build/foreign_libs/core.cljs create mode 100644 src/test/cljs_build/thirdparty/add.js diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 99dcbcc13..2a7072b42 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1050,7 +1050,7 @@ (let [{:keys [js-dependency-index options]} @env/*compiler* ijs-url (get-in js-dependency-index [(name lib) :url])] (emitln "cljs.core.load_file(\"" - (str (io/file (util/output-directory options) (util/get-name ijs-url))) + (str (io/file (util/output-directory options) (util/relative-name ijs-url))) "\");")) (emitln "goog.require('" (munge lib) "');"))))]) diff --git a/src/test/cljs_build/foreign_libs/core.cljs b/src/test/cljs_build/foreign_libs/core.cljs new file mode 100644 index 000000000..6ad1c0195 --- /dev/null +++ b/src/test/cljs_build/foreign_libs/core.cljs @@ -0,0 +1,13 @@ +;; 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 foreign-libs.core + (:require [thirdparty.add])) + +(defn main [] + (println (js/add 1 2))) diff --git a/src/test/cljs_build/thirdparty/add.js b/src/test/cljs_build/thirdparty/add.js new file mode 100644 index 000000000..32721eab3 --- /dev/null +++ b/src/test/cljs_build/thirdparty/add.js @@ -0,0 +1,11 @@ +// 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. + +function add(a, b) { + return a + b; +} diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index f84c7cde0..47f29b194 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -14,7 +14,8 @@ (:require [clojure.java.io :as io] [cljs.env :as env] [cljs.analyzer :as ana] - [cljs.test-util :as test])) + [cljs.test-util :as test] + [cljs.build.api :as build])) (deftest test-target-file-for-cljs-ns (is (= (.getPath (target-file-for-cljs-ns 'example.core-lib nil)) @@ -131,15 +132,32 @@ "The files are not empty after compilation"))) (deftest cljs-1500-test-modules - (let [out (str (System/getProperty "java.io.tmpdir") "/cljs-1500-out") - project (test/project-with-modules out) + (let [out (io/file (test/tmp-dir) "cljs-1500-out") + project (test/project-with-modules (str out)) modules (-> project :opts :modules)] - (test/clean-outputs (:opts project)) + (test/delete-out-files out) (build (inputs (:inputs project)) (:opts project)) (is (re-find #"Loading modules A and B" (slurp (-> modules :cljs-base :output-to)))) (is (re-find #"Module A loaded" (slurp (-> modules :module-a :output-to)))) (is (re-find #"Module B loaded" (slurp (-> modules :module-b :output-to)))))) +(deftest cljs-1883-test-foreign-libs-use-relative-path + (let [out (io/file (test/tmp-dir) "cljs-1883-out") + root (io/file "src" "test" "cljs_build") + opts {:foreign-libs + [{:file (str (io/file root "thirdparty" "add.js")) + :provides ["thirdparty.add"]}] + :output-dir (str out) + :target :nodejs}] + (test/delete-out-files out) + (build (inputs (io/file root "foreign_libs") (io/file root "thirdparty")) opts) + (let [foreign-lib-file (io/file out (-> opts :foreign-libs first :file))] + (is (.exists foreign-lib-file)) + (is (= (->> (slurp (io/file out "foreign_libs" "core.js")) + (re-matches #"(?s).*cljs\.core\.load_file\(\"([^\"]+)\"\);.*") + (second)) + (str foreign-lib-file)))))) + (deftest cljs-1537-circular-deps (let [out-file (io/file "out/main.js")] (.delete out-file) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index a0e32256d..a8c85c011 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -50,10 +50,10 @@ (is (thrown? AssertionError (check-source-map {:source-map "target/build/app.js.map" :optimizations :none}))))) (deftest test-cljs-1882-constants-table-is-sorted - (let [out (.getPath (io/file (System/getProperty "java.io.tmpdir") "cljs-1882-out")) + (let [out (.getPath (io/file (test/tmp-dir) "cljs-1882-out")) project (test/project-with-modules out) modules (-> project :opts :modules)] - (test/clean-outputs (:opts project)) + (test/delete-out-files out) (build/build (build/inputs (:inputs project)) (:opts project)) (let [compiler (closure/make-closure-compiler) module (JSModule. "module-c")] diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 48095c70f..86911aae1 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -5,7 +5,8 @@ [cljs.env :as env] [cljs.analyzer :as ana] [cljs.compiler :as comp] - [cljs.js-deps :as deps])) + [cljs.js-deps :as deps] + [cljs.test-util :as test])) ;; Hard coded JSX transform for the test case (defmethod closure/js-transforms :jsx [ijs _] @@ -21,16 +22,8 @@ "React.createElement(\"circle\", {cx:\"100px\", cy:\"100px\", r:\"100px\", fill:this.props.color})" ")")))) -(defn delete-out-files - "Processed files are only copied/written if input has changed. In test case it - makes sense to write files always, in case the processing logic has changed." - [] - (doseq [f (file-seq (io/file "out")) - :when (.isFile f)] - (.delete f))) - (deftest commonjs-module-processing - (delete-out-files) + (test/delete-out-files) (let [cenv (env/default-compiler-env)] ;; Reset load-library cache so that changes to processed files are noticed @@ -59,7 +52,7 @@ "Processed modules are added to :js-module-index"))) (deftest test-module-name-substitution - (delete-out-files) + (test/delete-out-files) (let [cenv (env/default-compiler-env)] (env/with-compiler-env cenv (let [opts (closure/process-js-modules {:foreign-libs [{:file "src/test/cljs/calculator.js" diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index cf8481ae6..3309021f5 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -9,17 +9,20 @@ (ns cljs.test-util (:require [clojure.java.io :as io])) -(defn clean-outputs - "Remove the :output-to artifacts of a ClojureScript build." - [{:keys [output-to modules] :as opts}] - (some-> output-to io/file .delete) - (doseq [{:keys [output-to]} (vals modules)] - (.delete (io/file output-to)))) +(defn delete-out-files + "Processed files are only copied/written if input has changed. In test case it + makes sense to write files always, in case the processing logic has changed." + ([] + (delete-out-files "out")) + ([directory] + (doseq [f (file-seq (io/file directory)) + :when (.isFile f)] + (.delete f)))) (defn project-with-modules "Returns the build config for a project that uses Google Closure modules." [output-dir] - {:inputs "src/test/cljs" + {:inputs (str (io/file "src" "test" "cljs")) :opts {:main "module-test.main" :output-dir output-dir @@ -27,10 +30,15 @@ :verbose true :modules {:cljs-base - {:output-to (str output-dir "/module-main.js")} + {:output-to (str (io/file output-dir "module-main.js"))} :module-a - {:output-to (str output-dir "/module-a.js") + {:output-to (str (io/file output-dir "module-a.js")) :entries #{'module-test.modules.a}} :module-b - {:output-to (str output-dir "/module-b.js") + {:output-to (str (io/file output-dir "module-b.js")) :entries #{'module-test.modules.b}}}}}) + +(defn tmp-dir + "Returns the temporary directory of the system." + [] + (System/getProperty "java.io.tmpdir")) From 5603b313de751f0d7344b7ae790ce0591fab5f77 Mon Sep 17 00:00:00 2001 From: Shaun LeBron Date: Thu, 12 Jan 2017 22:18:27 -0600 Subject: [PATCH 2162/4033] CLJS-1887: add :watch-error-fn option --- src/main/clojure/cljs/closure.clj | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f1694c128..53859bbbb 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2112,9 +2112,10 @@ (defn watch "Given a source directory, produce runnable JavaScript. Watch the source - directory for changes rebuliding when necessary. Takes the same arguments as - cljs.closure/build in addition to :watch-fn, a function of no arguments to - run after a successful build." + directory for changes rebuilding when necessary. Takes the same arguments as + cljs.closure/build in addition to some watch-specific options: + - :watch-fn, a function of no arguments to run after a successful build. + - :watch-error-fn, a function receiving the exception of a failed build." ([source opts] (watch source opts (if-not (nil? env/*compiler*) @@ -2140,8 +2141,10 @@ (when-let [f (:watch-fn opts)] (f)) (catch Throwable e - (binding [*out* *err*] - (println (Throwables/getStackTraceAsString e)))))) + (if-let [f (:watch-error-fn opts)] + (f e) + (binding [*out* *err*] + (println (Throwables/getStackTraceAsString e))))))) (watch-all [^Path root] (Files/walkFileTree root (reify From 34c7dabce6272061c3a1ccc029b972f1cd3473c6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Jan 2017 12:58:16 -0500 Subject: [PATCH 2163/4033] do not compute default-externs unless :infer-externs compiler option is set --- src/main/clojure/cljs/env.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 6517a1cee..5e1becffa 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -51,7 +51,8 @@ state that is accessed/maintained by many different components."} {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} :cljs.analyzer/constant-table {} :cljs.analyzer/data-readers {} - :cljs.analyzer/externs #?(:clj (externs/default-externs) + :cljs.analyzer/externs #?(:clj (when (:infer-externs options) + (externs/default-externs)) :cljs nil) :options options} #?(:clj {:js-dependency-index (js-dependency-index options)}))))) From ba0983bc1e7ffb45907cc3762fef11a3c3e0ee03 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Jan 2017 14:13:57 -0500 Subject: [PATCH 2164/4033] CLJS-1892: Dependencies in JARs are analyzed every time even if an analysis cache file exists This occurs in the case where we analyze an ns form in user code which references dependencies in JARs. Due to historical reasons we may not have passed the compiler options through recursive calls to analyze. Now in analyze we check if the cljs.env/*compiler* dynamic var has been bound. If it has we use it. --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e7ba5c560..c21711c19 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3166,7 +3166,10 @@ nested exprs, must have :children [exprs...] entry. This will facilitate code walking without knowing the details of the op set." ([env form] (analyze env form nil)) - ([env form name] (analyze env form name nil)) + ([env form name] + (analyze env form name + (when env/*compiler* + (:options @env/*compiler*)))) ([env form name opts] (ensure (wrapping-errors env From 462e2295d6e6c1a01b279c8aa091f832c9d09824 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Jan 2017 15:21:12 -0500 Subject: [PATCH 2165/4033] CLJS-1893: Unnecessary analysis of core.cljs In all places where opts is optionally provided check to see if cljs.env/*compiler* is bound, if it is deref and read :options --- src/main/clojure/cljs/analyzer.cljc | 25 ++++++++++++---- src/main/clojure/cljs/closure.clj | 41 ++++++++++++++++++-------- src/main/clojure/cljs/compiler.cljc | 31 ++++++++++++++----- src/main/clojure/cljs/compiler/api.clj | 5 +++- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c21711c19..5252213ec 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1837,7 +1837,10 @@ "Given a lib, a namespace, deps, its dependencies, env, an analysis environment and opts, compiler options - analyze all of the dependencies. Required to correctly analyze usage of other namespaces." - ([lib deps env] (analyze-deps lib deps env nil)) + ([lib deps env] + (analyze-deps lib deps env + (when env/*compiler* + (:options @env/*compiler*)))) ([lib deps env opts] (let [compiler @env/*compiler*] (binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)] @@ -2969,7 +2972,10 @@ (analyze-seq* op env form name opts))) (defn analyze-seq - ([env form name] (analyze-seq env form name nil)) + ([env form name] + (analyze-seq env form name + (when env/*compiler* + (:options @env/*compiler*)))) ([env form name opts] (if ^boolean (:quoted? env) (analyze-list env form) @@ -3270,7 +3276,10 @@ be used for *analyze-deps* and *load-macros* bindings respectively. This function does _not_ side-effect the ambient compilation environment unless requested via opts where :restore is false." - ([src] (parse-ns src nil nil)) + ([src] + (parse-ns src nil + (when env/*compiler* + (:options @env/*compiler*)))) ([src opts] (parse-ns src nil opts)) ([src dest opts] (ensure @@ -3434,7 +3443,9 @@ (defn analyze-form-seq ([forms] - (analyze-form-seq forms nil)) + (analyze-form-seq forms + (when env/*compiler* + (:options @env/*compiler*)))) ([forms opts] (let [env (assoc (empty-env) :build-options opts)] (binding [*file-defs* nil @@ -3463,7 +3474,9 @@ \":output-dir/some/ns/foo.cljs.cache.edn\". This function does not return a meaningful value." ([f] - (analyze-file f nil)) + (analyze-file f + (when env/*compiler* + (:options @env/*compiler*)))) ([f opts] (analyze-file f false opts)) ([f skip-cache opts] @@ -3490,7 +3503,7 @@ *cljs-file* path reader/*alias-map* (or reader/*alias-map* {})] (when (or *verbose* (:verbose opts)) - (util/debug-prn "Analyzing" (str res))) + (util/debug-prn "Analyzing" (str res) "cache" cache "opts" opts)) (let [env (assoc (empty-env) :build-options opts) ns (with-open [rdr (io/reader res)] (loop [ns nil forms (seq (forms-seq* rdr (util/path res)))] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 53859bbbb..121d38284 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -394,13 +394,17 @@ (defn compile-form-seq "Compile a sequence of forms to a JavaScript source string." - [forms] - (comp/with-core-cljs nil - (fn [] - (with-out-str - (binding [ana/*cljs-ns* 'cljs.user] - (doseq [form forms] - (comp/emit (ana/analyze (ana/empty-env) form)))))))) + ([forms] + (compile-form-seq forms + (when env/*compiler* + (:options @env/*compiler*)))) + ([forms opts] + (comp/with-core-cljs opts + (fn [] + (with-out-str + (binding [ana/*cljs-ns* 'cljs.user] + (doseq [form forms] + (comp/emit (ana/analyze (ana/empty-env) form))))))))) (defn compiled-file "Given a map with at least a :file key, return a map with @@ -454,7 +458,9 @@ (defn jar-file-to-disk "Copy a file contained within a jar to disk. Return the created file." ([url out-dir] - (jar-file-to-disk url out-dir nil)) + (jar-file-to-disk url out-dir + (when env/*compiler* + (:options @env/*compiler*)))) ([url out-dir opts] (let [out-file (io/file out-dir (path-from-jarfile url)) content (with-open [reader (io/reader url)] @@ -764,7 +770,9 @@ "Given list of IJavaScript objects, produce a new sequence of IJavaScript objects of all dependencies of inputs." ([inputs] - (add-dependency-sources inputs nil)) + (add-dependency-sources inputs + (when env/*compiler* + (:options @env/*compiler*)))) ([inputs compile-opts] (let [inputs (set inputs) requires (set (mapcat deps/-requires inputs)) @@ -966,7 +974,10 @@ (js-source-file (javascript-name url) (io/input-stream url)))) (defn add-cljs-base-module - ([modules] (add-cljs-base-module modules nil)) + ([modules] + (add-cljs-base-module modules + (when env/*compiler* + (:options @env/*compiler*)))) ([modules opts] (reduce (fn [modules module-name] @@ -1473,7 +1484,10 @@ "Given an IJavaScript which is either in memory, in a jar file, or is a foreign lib, return the path relative to the output directory." - ([js] (rel-output-path js nil)) + ([js] + (rel-output-path js + (when env/*compiler* + (:options @env/*compiler*)))) ([js opts] (let [url (deps/-url js)] (cond @@ -2229,7 +2243,10 @@ (deps/parse-js-ns (line-seq (io/reader f)))) (defn ^File src-file->target-file - ([src] (src-file->target-file src nil)) + ([src] + (src-file->target-file src + (when env/*compiler* + (:options @env/*compiler*)))) ([src opts] (util/to-target-file (when (:output-dir opts) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 2a7072b42..e8a134e13 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1159,7 +1159,9 @@ #?(:clj (defn with-core-cljs "Ensure that core.cljs has been loaded." - ([] (with-core-cljs nil)) + ([] (with-core-cljs + (when env/*compiler* + (:options @env/*compiler*)))) ([opts] (with-core-cljs opts (fn []))) ([opts body] {:pre [(or (nil? opts) (map? opts)) @@ -1177,7 +1179,10 @@ #?(:clj (defn compiled-by-string - ([] (compiled-by-string nil)) + ([] + (compiled-by-string + (when env/*compiler* + (:options @env/*compiler*)))) ([opts] (str "// Compiled by ClojureScript " (util/clojurescript-version) @@ -1321,7 +1326,10 @@ #?(:clj (defn compile-file* - ([src dest] (compile-file* src dest nil)) + ([src dest] + (compile-file* src dest + (when env/*compiler* + (:options @env/*compiler*)))) ([src dest opts] (ensure (with-core-cljs opts @@ -1342,7 +1350,10 @@ #?(:clj (defn requires-compilation? "Return true if the src file requires compilation." - ([src dest] (requires-compilation? src dest nil)) + ([src dest] + (requires-compilation? src dest + (when env/*compiler* + (:options @env/*compiler*)))) ([^File src ^File dest opts] (let [{:keys [ns requires]} (ana/parse-ns src)] (ensure @@ -1378,9 +1389,13 @@ If the file was not compiled returns only {:file ...}" ([src] (let [dest (rename-to-js src)] - (compile-file src dest nil))) + (compile-file src dest + (when env/*compiler* + (:options @env/*compiler*))))) ([src dest] - (compile-file src dest nil)) + (compile-file src dest + (when env/*compiler* + (:options @env/*compiler*)))) ([src dest opts] {:post [map?]} (binding [ana/*file-defs* (atom #{}) @@ -1443,7 +1458,9 @@ ([src-dir] (compile-root src-dir "out")) ([src-dir target-dir] - (compile-root src-dir target-dir nil)) + (compile-root src-dir target-dir + (when env/*compiler* + (:options @env/*compiler*)))) ([src-dir target-dir opts] (swap! env/*compiler* assoc :root src-dir) (let [src-dir-file (io/file src-dir) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index fb83b1e21..3a8474267 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -32,7 +32,10 @@ (defn with-core-cljs "Ensure that core.cljs has been loaded." - ([] (comp/with-core-cljs nil)) + ([] + (comp/with-core-cljs + (when env/*compiler* + (:options @env/*compiler*)))) ([opts] (with-core-cljs opts (fn []))) ([opts body] (with-core-cljs From 57502ef05e661e407e5d1e9d4b77025b2809bb94 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Jan 2017 15:24:02 -0500 Subject: [PATCH 2166/4033] remove stray debugging --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5252213ec..c0d3faa6c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3503,7 +3503,7 @@ *cljs-file* path reader/*alias-map* (or reader/*alias-map* {})] (when (or *verbose* (:verbose opts)) - (util/debug-prn "Analyzing" (str res) "cache" cache "opts" opts)) + (util/debug-prn "Analyzing" (str res))) (let [env (assoc (empty-env) :build-options opts) ns (with-open [rdr (io/reader res)] (loop [ns nil forms (seq (forms-seq* rdr (util/path res)))] From a3f552cb032d7de0a564d6d5667258b8004500a9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Jan 2017 15:34:58 -0500 Subject: [PATCH 2167/4033] CLJS-1894: Unnecessary analysis of core.cljs on first compile --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c0d3faa6c..aad63fd8f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3386,7 +3386,7 @@ (if-let [core-cache (and (= mode :read) (= (:ns ns-info) 'cljs.core) - (io/resource (str "cljs/core.cljs.cache.aot" ext)))] + (io/resource (str "cljs/core.cljs.cache.aot." ext)))] core-cache (let [target-file (util/to-target-file output-dir ns-info (util/ext (:source-file ns-info)))] From ec220f51efb8a456479c4d0b5c27e611d3abb551 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Jan 2017 16:12:51 -0500 Subject: [PATCH 2168/4033] CLJS-1811: Can't compose cljs.spec.test.instrument (or cljs.spec.test.check) with cljs.spec.test.enumerate-namespace Remove cljs.spec.test/enumerate-namespace. Instead if a symbol names an existing namespace, enumerate it --- src/main/cljs/cljs/spec/test.cljc | 56 ++++++++++++++++--------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc index 692c535e5..2477a4525 100644 --- a/src/main/cljs/cljs/spec/test.cljc +++ b/src/main/cljs/cljs/spec/test.cljc @@ -10,6 +10,7 @@ (:require [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] + [clojure.string :as string] [cljs.spec :as s] [cljs.spec.impl.gen :as gen])) @@ -25,21 +26,6 @@ [s] (symbol? s)) -(defmacro enumerate-namespace - "Given a symbol naming an ns, or a collection of such symbols, -returns the set of all symbols naming vars in those nses." - [[quote ns-sym-or-syms]] - (let [xs (into #{} - (mapcat (fn [ns-sym] - (->> (vals (ana-api/ns-interns ns-sym)) - (filter #(not (:macro %))) - (map :name) - (map - (fn [name-sym] - (symbol (name ns-sym) (name name-sym))))))) - (collectionize ns-sym-or-syms))] - `(quote ~xs))) - (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." [& body] @@ -63,10 +49,26 @@ returns the set of all symbols naming vars in those nses." (when raw# (set! ~s raw#)) '~(:name v))))) +(defn- sym-or-syms->syms [sym-or-syms] + (into [] + (mapcat + (fn [sym] + (if (and (string/includes? (str sym) ".") + (ana-api/find-ns sym)) + (->> (vals (ana-api/ns-interns sym)) + (filter #(not (:macro %))) + (map :name) + (map + (fn [name-sym] + (symbol (name sym) (name name-sym))))) + [sym]))) + (collectionize sym-or-syms))) + (defmacro instrument "Instruments the vars named by sym-or-syms, a symbol or collection of symbols, or all instrumentable vars if sym-or-syms is not -specified. +specified. If a symbol identifies a namespace then all symbols in that +namespace will be enumerated. If a var has an :args fn-spec, sets the var's root binding to a fn that checks arg conformance (throwing an exception on failure) @@ -106,20 +108,19 @@ Returns a collection of syms naming the vars instrumented." ([xs] `(instrument ~xs nil)) ([sym-or-syms opts] - (let [sym-or-syms (eval sym-or-syms) - opts-sym (gensym "opts")] + (let [syms (sym-or-syms->syms (eval sym-or-syms)) + opts-sym (gensym "opts")] `(let [~opts-sym ~opts] (reduce (fn [ret# [_# f#]] (let [sym# (f#)] (cond-> ret# sym# (conj sym#)))) [] - (->> (zipmap - (collectionize '~sym-or-syms) + (->> (zipmap '~syms [~@(map (fn [sym] `(fn [] (instrument-1 '~sym ~opts-sym))) - (collectionize sym-or-syms))]) + syms)]) (filter #((instrumentable-syms ~opts-sym) (first %))) (distinct-by first))))))) @@ -130,13 +131,13 @@ Returns a collection of syms naming the vars unstrumented." ([] `(unstrument '[~@(deref instrumented-vars)])) ([sym-or-syms] - (let [sym-or-syms (eval sym-or-syms)] + (let [syms (sym-or-syms->syms (eval sym-or-syms))] `(reduce (fn [ret# f#] (let [sym# (f#)] (cond-> ret# sym# (conj sym#)))) [] - [~@(->> (collectionize sym-or-syms) + [~@(->> syms (map (fn [sym] (when (symbol? sym) @@ -204,7 +205,8 @@ can be checked." (defmacro check "Run generative tests for spec conformance on vars named by sym-or-syms, a symbol or collection of symbols. If sym-or-syms -is not specified, check all checkable vars. +is not specified, check all checkable vars. If a symbol identifies a +namespace then all symbols in that namespace will be enumerated. The opts map includes the following optional keys, where stc aliases clojure.test.check: @@ -239,10 +241,10 @@ spec itself will have an ::s/failure value in ex-data: ([sym-or-syms] `(check ~sym-or-syms nil)) ([sym-or-syms opts] - (let [sym-or-syms (eval sym-or-syms) - opts-sym (gensym "opts")] + (let [syms (sym-or-syms->syms (eval sym-or-syms)) + opts-sym (gensym "opts")] `(let [~opts-sym ~opts] - [~@(->> (collectionize sym-or-syms) + [~@(->> syms (filter (checkable-syms* opts)) (map (fn [sym] From 2ef5b0d0b0f95f779cc99334300f5fe39144fd23 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Jan 2017 16:31:41 -0500 Subject: [PATCH 2169/4033] CLJS-1890: s/form for s/nilable in cljs.spec does not match clojure.spec Correctly port nilable? --- src/main/cljs/cljs/spec.cljc | 3 ++- src/main/cljs/cljs/spec.cljs | 26 ++++++++++++++++++++++++++ src/main/cljs/cljs/spec/impl/gen.cljs | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index cecd3252a..7138d0f4c 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -435,7 +435,8 @@ (defmacro nilable "returns a spec that accepts nil and values satisfiying pred" [pred] - `(nonconforming (or ::nil nil? ::pred ~pred))) + (let [pf (res &env pred)] + `(nilable-impl '~pf ~pred nil))) (defmacro inst-in "Returns a spec that validates insts in the range from start diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index dd4a15b12..cb280fe8c 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1351,6 +1351,32 @@ (with-gen* [_ gfn] (nonconforming (with-gen* spec gfn))) (describe* [_] `(nonconforming ~(describe* spec)))))) +(defn ^:skip-wiki nilable-impl + "Do not call this directly, use 'nilable'" + [form pred gfn] + (let [spec (specize pred form)] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (if (nil? x) nil (conform* spec x))) + (unform* [_ x] (if (nil? x) nil (unform* spec x))) + (explain* [_ path via in x] + (when-not (c/or (pvalid? spec x) (nil? x)) + (conj + (explain-1 form pred (conj path ::pred) via in x) + {:path (conj path ::nil) :pred 'nil? :val x :via via :in in}))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (gen/frequency + [[1 (gen/delay (gen/return nil))] + [9 (gen/delay (gensub pred overrides (conj path ::pred) rmap form))]]))) + (with-gen* [_ gfn] (nilable-impl form pred gfn)) + (describe* [_] `(nilable ~(describe* spec)))))) + (defn exercise "generates a number (default 10) of values compatible with spec and maps conform over them, returning a sequence of [val conformed-val] tuples. Optionally takes diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs index e55a482c1..9d68dc8e1 100644 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ b/src/main/cljs/cljs/spec/impl/gen.cljs @@ -68,7 +68,7 @@ (lazy-combinators hash-map list map not-empty set vector vector-distinct fmap elements bind choose one-of such-that tuple sample return - large-integer* double*) + large-integer* double* frequency) (lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double int keyword keyword-ns large-integer ratio simple-type simple-type-printable From 145201e1cbfafc67a6089d229f2a0e652fcd633a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 30 Dec 2016 16:49:10 -0500 Subject: [PATCH 2170/4033] add expand-libs, not working yet (wip) --- src/main/clojure/cljs/closure.clj | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 121d38284..9237d679d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1828,24 +1828,43 @@ (and (satisfies? deps/IJavaScript js) (deps/-foreign? js))) -(defn expand-foreign [{:keys [foreign-libs]}] - (letfn [(expand-foreign* [{:keys [file] :as lib}] - (let [dir (io/file file)] +(defn expand-libs + "EXPERIMENTAL. Given a set of libs expand any entries which only name + directories into a sequence of lib entries for all JS files recursively + found in that directory. All other options will be shared with the original + entry. The computed :provides assumes the specified directory is on the + classpath." + [libs] + (letfn [(prep-path [p root] + (subs (string/replace (subs p 0 (- (count p) 3)) root "") 1)) + (path->provides [p] + (let [p' (string/replace p File/separator ".")] + (cond-> [p'] + (string/includes? p' "_") + (conj (string/replace p' "_" "-"))))) + (expand-lib* [{:keys [file] :as lib}] + (let [root (.getAbsolutePath (io/file file)) + dir (io/file file)] (if (.isDirectory dir) (into [] (comp (filter #(.endsWith (.getName ^File %) ".js")) - (map #(assoc lib :file (.getPath ^File %)))) + (map + (fn [^File f] + (let [p (.getPath f) + ap (.getAbsolutePath f)] + (merge lib + {:file p :provides (path->provides (prep-path ap root))}))))) (file-seq dir)) [lib])))] - (into [] (mapcat expand-foreign* foreign-libs)))) + (into [] (mapcat expand-lib* libs)))) (defn add-implicit-options [{:keys [optimizations output-dir] :or {optimizations :none output-dir "out"} :as opts}] - (let [opts (cond-> opts + (let [opts (cond-> (update opts :foreign-libs expand-libs) (:closure-defines opts) (assoc :closure-defines (into {} From 30acd88d02857dc903b4da7c028c522d21b37b25 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Jan 2017 18:55:08 -0500 Subject: [PATCH 2171/4033] CLJS-1877: :foreign-libs entries should be allowed to specify directories along with individual files Basic experimental functionality in place. cljs.closure/expand-libs made more generic and made cljs.closure/write-javascript be processed JS module aware (simply by treating :libs already present in :output-dir as having been processed). Needs further testing. --- src/main/clojure/cljs/closure.clj | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9237d679d..74d44a731 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1583,17 +1583,22 @@ does not already exist. Return IJavaScript for the file on disk at the new location." [opts js] - (let [out-dir (io/file (util/output-directory opts)) - out-name (rel-output-path js opts) - out-file (io/file out-dir out-name) - ijs {:url (deps/to-url out-file) - :out-file (.toString out-file) - :requires (deps/-requires js) - :provides (deps/-provides js) - :group (:group js)} - res (or (:url js) (:source-file js))] - (when (or (not (.exists out-file)) - (and res (util/changed? out-file res))) + (let [out-dir (io/file (util/output-directory opts)) + out-name (rel-output-path js opts) + out-file (io/file out-dir out-name) + res (or (:url js) (:source-file js)) + js-module? (and res out-dir + (.startsWith (util/path res) (util/path out-dir))) ;; We already Closure processed it and wrote it out + ijs (merge + {:requires (deps/-requires js) + :provides (deps/-provides js) + :group (:group js)} + (when (not js-module?) + {:url (deps/to-url out-file) + :out-file (.toString out-file)}))] + (when (and (not js-module?) + (or (not (.exists out-file)) + (and res (util/changed? out-file res)))) (when (and res (or ana/*verbose* (:verbose opts))) (util/debug-prn "Copying" (str res) "to" (str out-file))) (util/mkdirs out-file) From 51ca92b7260bfa22f309766e6ae914d044e5cc5e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 16 Dec 2016 23:05:04 -0500 Subject: [PATCH 2172/4033] CLJS-1874: Self-host: :fn-var true for macros When a def is used to define a macro (as a result of defmacro in self- hosted ClojureScript) ensure that :fn-var is not set to true in this case. --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/self/self_host/test.cljs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index aad63fd8f..ce2e25901 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1341,7 +1341,7 @@ (when fn-var? (let [params (map #(vec (map :name (:params %))) (:methods init-expr))] (merge - {:fn-var true + {:fn-var (not (:macro sym-meta)) ;; protocol implementation context :protocol-impl (:protocol-impl init-expr) ;; inline protocol implementation context diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index ef86d2055..3f9867fbc 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -850,6 +850,19 @@ (fn [{:keys [value error]}] (is (= value [6 6])))))))) +(deftest test-cljs-1874 + (async done + (let [st (cljs/empty-state) + l (latch 1 done)] + (cljs/eval st '(ns foo.core + (:require-macros [bar.core])) + {:load (fn [_ cb] + (cb {:lang :clj + :source "(ns bar.core) (defmacro add [a b] `(+ ~a ~b))"}))} + (fn [_] + (is (false? (:fn-var (var-ast @st 'bar.core$macros/add)))) + (inc! l)))))) + (defn -main [& args] (run-tests)) From 1f9fad5ee4450e579f1659f62dcdbcafc20298f4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 16 Dec 2016 20:00:59 -0500 Subject: [PATCH 2173/4033] CLJS-1873: Self-host: Unit tests fail owing to test.check dep Fixes the self-host unit tests by moving the new unit tests for the cljs.spec.test namespace into a new cljs.spec.test-test namespace, and by skipping testing this namespace in the self-host test runner. --- src/test/cljs/cljs/spec/test_test.cljs | 17 +++++++++++++++++ src/test/cljs/cljs/spec_test.cljs | 14 -------------- src/test/cljs/test_runner.cljs | 2 ++ src/test/self/self_parity/test.cljs | 2 ++ 4 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 src/test/cljs/cljs/spec/test_test.cljs diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs new file mode 100644 index 000000000..e325d3085 --- /dev/null +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -0,0 +1,17 @@ +(ns cljs.spec.test-test + (:require [cljs.test :as test :refer-macros [deftest is are run-tests]] + [cljs.spec :as s] + [cljs.spec.test :as stest])) + +(defn h-cljs-1812 [x] true) +(s/fdef h-cljs-1812 :args (s/cat :x int?) :ret true?) + +(deftest test-cljs-1812 + (is (= (stest/unstrument `h-cljs-1812) + [])) + + (stest/check `h-cljs-1812 {:clojure.test.check/opts {:num-tests 1}}) + + ; Calling h-cljs-1812 with an argument of the wrong type shouldn't throw, + ; because the function should not have been instrumented by stest/check. + (h-cljs-1812 "foo")) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index eb4df4bff..88009d548 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -8,7 +8,6 @@ (ns cljs.spec-test (:require [cljs.spec :as s] - [cljs.spec.test :as stest] [cljs.test :as test :refer-macros [deftest is are run-tests]] [cljs.spec.impl.gen :as gen] [clojure.test.check.generators])) @@ -101,19 +100,6 @@ (is (= :b (s/conform ::multi :b))) (is (= :a (s/conform ::multi :a)))) -(defn h-cljs-1812 [x] true) -(s/fdef h-cljs-1812 :args (s/cat :x int?) :ret true?) - -(deftest test-cljs-1812 - (is (= (stest/unstrument `h-cljs-1812) - [])) - - (stest/check `h-cljs-1812 {:clojure.test.check/opts {:num-tests 1}}) - - ; Calling h-cljs-1812 with an argument of the wrong type shouldn't throw, - ; because the function should not have been instrumented by stest/check. - (h-cljs-1812 "foo")) - ;; Copied from Clojure spec tests (def even-count? #(even? (count %))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index e269aa706..9b1592165 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -34,6 +34,7 @@ [cljs.pprint] [cljs.pprint-test] [cljs.spec-test] + [cljs.spec.test-test] [cljs.clojure-alias-test] [cljs.hash-map-test] [cljs.predicates-test] @@ -67,6 +68,7 @@ 'cljs.import-test 'cljs.pprint 'cljs.spec-test + 'cljs.spec.test-test 'cljs.clojure-alias-test 'cljs.hash-map-test 'cljs.pprint-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 3165b48e1..d424d0954 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -276,6 +276,7 @@ #_[cljs.pprint] #_[cljs.pprint-test] [cljs.spec-test] + #_[cljs.spec.test-test] [cljs.clojure-alias-test] [cljs.hash-map-test] [cljs.syntax-quote-test] @@ -310,6 +311,7 @@ #_'cljs.pprint #_'cljs.pprint-test 'cljs.spec-test + #_'cljs.spec.test-test 'cljs.clojure-alias-test 'cljs.hash-map-test 'cljs.syntax-quote-test From ac0601cede2ba4201f229caebb69833625ad342e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 21 Jan 2017 14:37:18 -0500 Subject: [PATCH 2174/4033] first pass at propagating return type information from externs --- src/main/clojure/cljs/analyzer.cljc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ce2e25901..382359865 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2631,7 +2631,13 @@ :method method :args argexprs :children children - :tag tag})))) + :tag (if (js-tag? tag) + (let [pre (-> tag meta :prefix) + [sym _] (find (get-in @env/*compiler* + (into [::externs] (butlast pre))) + (last pre))] + (:ret-tag (meta sym) tag)) + tag)})))) (defmethod parse '. [_ env [_ target & [field & member+] :as form] _ _] From 0e258d8d4f4593384d2165b63d12fecb5019fe12 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Jan 2017 16:48:51 -0500 Subject: [PATCH 2175/4033] when writing out the analysis cache for core we need to set :infer-externs to true --- src/main/clojure/cljs/build/api.clj | 2 +- src/main/clojure/cljs/closure.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 3fc446a9a..6f7ca003a 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -89,7 +89,7 @@ (src-file->goog-require (if-not (nil? env/*compiler*) env/*compiler* - (env/default-compiler-env)) + (env/default-compiler-env options)) src options)) ([state src options] (env/with-compiler-env state diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 74d44a731..ff354caef 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2306,7 +2306,7 @@ cache (io/file base-path "core.cljs.cache.aot.edn") tcache (io/file base-path "core.cljs.cache.aot.json")] (util/mkdirs dest) - (env/with-compiler-env (env/default-compiler-env) + (env/with-compiler-env (env/default-compiler-env {:infer-externs true}) (comp/compile-file src dest {:source-map true :source-map-url "core.js.map" From 342923bd4428ec8cce3f487eaf37a8ee27bab14f Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Jan 2017 17:37:18 -0500 Subject: [PATCH 2176/4033] CLJS-1895: Externs inference needs to support user supplied externs --- src/main/clojure/cljs/build/api.clj | 6 ++-- src/main/clojure/cljs/closure.clj | 8 ++++- src/main/clojure/cljs/env.cljc | 2 +- src/main/clojure/cljs/externs.clj | 42 +++++++++++++----------- src/test/clojure/cljs/analyzer_tests.clj | 21 +++++++++++- src/test/externs/test.js | 11 +++++++ 6 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 src/test/externs/test.js diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 6f7ca003a..ae5fe8947 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -190,7 +190,8 @@ (build source opts (if-not (nil? env/*compiler*) env/*compiler* - (env/default-compiler-env opts)))) + (env/default-compiler-env + (closure/add-externs-sources opts))))) ([source opts compiler-env] (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)] (when suggested-opt @@ -204,7 +205,8 @@ (watch source opts (if-not (nil? env/*compiler*) env/*compiler* - (env/default-compiler-env opts)))) + (env/default-compiler-env + (closure/add-externs-sources opts))))) ([source opts compiler-env] (watch source opts compiler-env nil)) ([source opts compiler-env stop] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ff354caef..ef98a6201 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1992,13 +1992,19 @@ (defn load-data-readers! [compiler] (swap! compiler update-in [:cljs.analyzer/data-readers] merge (get-data-readers))) +(defn add-externs-sources [opts] + (cond-> opts + (:infer-externs opts) + (assoc :externs-sources (load-externs (dissoc opts :infer-externs))))) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] (build source opts (if-not (nil? env/*compiler*) env/*compiler* - (env/default-compiler-env opts)))) + (env/default-compiler-env + (add-externs-sources opts))))) ([source opts compiler-env] (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 5e1becffa..d3921322d 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -52,7 +52,7 @@ state that is accessed/maintained by many different components."} :cljs.analyzer/constant-table {} :cljs.analyzer/data-readers {} :cljs.analyzer/externs #?(:clj (when (:infer-externs options) - (externs/default-externs)) + (externs/externs-map (:externs-sources options))) :cljs nil) :options options} #?(:clj {:js-dependency-index (js-dependency-index options)}))))) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 301194b4a..4d2366cf4 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -127,39 +127,43 @@ (seq xs) (update-in xs merge {}))) {} externs)) -(defn default-externs +(defn externs-map ([] - (default-externs - '{eval {} - global {} - goog {nodeGlobalRequire {}} - COMPILED {} - TypeError {} - Error {prototype {number {} columnNumber {}}} - ReferenceError {}})) - ([defaults] - (let [xs (CommandLineRunner/getDefaultExterns)] + (externs-map (CommandLineRunner/getDefaultExterns))) + ([sources] + (externs-map sources + '{eval {} + global {} + goog {nodeGlobalRequire {}} + COMPILED {} + TypeError {} + Error {prototype {number {} columnNumber {}}} + ReferenceError {}})) + ([sources defaults] + (let [sources (if-not (empty? sources) + sources + (CommandLineRunner/getDefaultExterns))] (reduce (fn [externs externs-file] (util/map-merge externs (index-externs (parse-externs externs-file)))) - defaults xs)))) + defaults sources)))) (comment - (default-externs) + (externs-map) - (-> (default-externs) + (-> (externs-map) (find 'console) first meta) - (get (default-externs) 'Function) + (get (externs-map) 'Function) - (get (default-externs) 'Error) + (get (externs-map) 'Error) ;; values are not on the prototype - (get (default-externs) 'Symbol) - (get (default-externs) 'Number) + (get (externs-map) 'Symbol) + (get (externs-map) 'Number) - (-> (get-in (default-externs) '[Window prototype]) + (-> (get-in (externs-map) '[Window prototype]) (find 'performance) first meta) ;; webkit_dom.js defines Console and Window.prototype.console diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index d4299f1c8..7f99a5a4f 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -621,9 +621,10 @@ (comment (require '[cljs.compiler :as cc]) + (require '[cljs.closure :as closure]) ;; TODO: need to handle the method/fn case - (let [test-cenv (atom {::a/externs (externs/default-externs)})] + (let [test-cenv (atom {::a/externs (externs/externs-map)})] (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv @@ -641,4 +642,22 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + ;; User supplied externs + (let [test-cenv (atom {::a/externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true}))})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns foo.core) + (defn bar [^js/Foo a] + (.wozMethod a) + (.gozMethod a))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + ) \ No newline at end of file diff --git a/src/test/externs/test.js b/src/test/externs/test.js new file mode 100644 index 000000000..f591abda9 --- /dev/null +++ b/src/test/externs/test.js @@ -0,0 +1,11 @@ +/* + @constructor + */ +var Foo = function() {}; +Foo.prototype.wozMethod = function() { +}; + +/** + @return {Foo} + */ +baz = function() {}; From 8b32c5148cc39bae12f1585c9d9d80b15b3b0b67 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Jan 2017 17:53:26 -0500 Subject: [PATCH 2177/4033] return type extern inference appears to work --- src/test/clojure/cljs/analyzer_tests.clj | 18 ++++++++++++++---- src/test/externs/test.js | 8 ++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 7f99a5a4f..a2324cdb5 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -642,22 +642,32 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + (-> + (find (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true})) + 'baz) + first meta) + ;; User supplied externs (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs - {:externs ["src/test/externs/test.js"] - :use-only-custom-externs true}))})] + {:externs ["src/test/externs/test.js"]}))})] (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq '[(ns foo.core) - (defn bar [^js/Foo a] + (defn baz [^js/Foo a] (.wozMethod a) - (.gozMethod a))])) + (.gozMethod a)) + (js/console.log (.wozMethod (js/Foo.))) + (js/console.log (.wozMethod (js/baz)))])) (cc/emit-externs (reduce util/map-merge {} (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + ) \ No newline at end of file diff --git a/src/test/externs/test.js b/src/test/externs/test.js index f591abda9..cef7c15c3 100644 --- a/src/test/externs/test.js +++ b/src/test/externs/test.js @@ -1,11 +1,11 @@ -/* - @constructor +/** + * @constructor */ var Foo = function() {}; Foo.prototype.wozMethod = function() { }; /** - @return {Foo} + * @return {Foo} */ -baz = function() {}; +var baz = function() {}; From 38e19b38cd6a637591100f7fc4f71ed2c16b7a9d Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Jan 2017 17:56:12 -0500 Subject: [PATCH 2178/4033] document problematic cases --- src/test/clojure/cljs/analyzer_tests.clj | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index a2324cdb5..ffe32d53a 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -660,8 +660,7 @@ (a/analyze-form-seq '[(ns foo.core) (defn baz [^js/Foo a] - (.wozMethod a) - (.gozMethod a)) + (.wozMethod a)) (js/console.log (.wozMethod (js/Foo.))) (js/console.log (.wozMethod (js/baz)))])) (cc/emit-externs @@ -669,5 +668,22 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + ;; the following should produce externs, but does not + (let [test-cenv (atom {::a/externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns foo.core) + (defn baz [^js/Foo a] + (.gozMethod a)) + (js/console.log (.gozMethod (js/baz)))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + ) \ No newline at end of file From b80c8cc5650ee60963dd31b8cdc117f25b67984b Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 21 Jan 2017 18:14:42 -0500 Subject: [PATCH 2179/4033] starting to enumerate externs inference test cases --- src/main/clojure/cljs/externs.clj | 2 +- src/test/clojure/cljs/analyzer_tests.clj | 47 +++++++++++++++++++++--- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 4d2366cf4..95d771b9e 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -39,7 +39,7 @@ (if-let [^JSTypeExpression ty (.getType info)] {:tag (get-type* ty)} (if (or (.isConstructor info) (.isInterface info)) - {:tag (symbol (.. node getFirstChild getQualifiedName))} + {:tag 'Function} (if (.hasReturnType info) {:tag 'Function :ret-tag (get-type* (.getReturnType info))}))))))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index ffe32d53a..52156cfdb 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -650,7 +650,15 @@ 'baz) first meta) - ;; User supplied externs + (-> + (find (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true})) + 'Foo) + first meta) + + ;; works, does not generate extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))})] @@ -659,16 +667,29 @@ (e/with-compiler-env test-cenv (a/analyze-form-seq '[(ns foo.core) - (defn baz [^js/Foo a] - (.wozMethod a)) - (js/console.log (.wozMethod (js/Foo.))) (js/console.log (.wozMethod (js/baz)))])) (cc/emit-externs (reduce util/map-merge {} (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - ;; the following should produce externs, but does not + ;; works, does not generate extern + (let [test-cenv (atom {::a/externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns foo.core) + (defn baz [^js/Foo x] + (.wozMethod x))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + + ;; does NOT work, does not generate extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))})] @@ -678,7 +699,21 @@ (a/analyze-form-seq '[(ns foo.core) (defn baz [^js/Foo a] - (.gozMethod a)) + (.gozMethod a))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + + ;; does NOT work, does not generate extern + (let [test-cenv (atom {::a/externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns foo.core) (js/console.log (.gozMethod (js/baz)))])) (cc/emit-externs (reduce util/map-merge {} From 0239cd827ad0c2622b1b4f5332f1599d4ad72b62 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 11:40:00 -0500 Subject: [PATCH 2180/4033] externs inference test wip --- src/main/clojure/cljs/analyzer.cljc | 8 ++++---- src/main/clojure/cljs/externs.clj | 5 ++++- src/test/clojure/cljs/analyzer_tests.clj | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 382359865..473cae106 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -798,10 +798,10 @@ (not me) false :else (let [seen' (conj seen x) - [x' externs'] me] - (if-some [tag (-> x' meta :tag)] - (let [pre' (into [] (map symbol) - (string/split (str (alias->type tag tag)) #"\."))] + [x' externs'] me + xmeta (meta x')] + (if (and (= 'Function (:tag xmeta)) (:ctor xmeta)) + (let [pre' [(:ctor xmeta)]] (or (has-extern?* (into pre' (next pre)) top top seen') (has-extern?* (into (conj pre' 'prototype) (next pre)) top top seen'))) (recur (next pre) externs' top seen')))))))) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 95d771b9e..4b965333f 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -39,7 +39,10 @@ (if-let [^JSTypeExpression ty (.getType info)] {:tag (get-type* ty)} (if (or (.isConstructor info) (.isInterface info)) - {:tag 'Function} + (let [qname (symbol (.. node getFirstChild getQualifiedName))] + (cond-> {:tag 'Function} + (.isConstructor info) (merge {:ctor qname}) + (.isInterface info) (merge {:iface qname}))) (if (.hasReturnType info) {:tag 'Function :ret-tag (get-type* (.getReturnType info))}))))))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 52156cfdb..70a3e2aa0 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -619,6 +619,22 @@ '(let [y 1] (def y 2))))] (is (some? (-> parsed :expr :ret :var-ast))))) +(deftest test-infer-externs-basic + (let [externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true}))] + (is (true? (a/has-extern? '[Foo] externs))) + (is (false? (a/has-extern? '[foo] externs))) + (is (true? (a/has-extern? '[baz] externs))) + (is (false? (a/has-extern? '[Baz] externs))))) + +(deftest test-infer-externs-default + (let [externs (externs/externs-map)] + (is (true? (a/has-extern? '[console] externs))) + (is (true? (a/has-extern? '[console log] externs))) + (is (true? (a/has-extern? '[Number isNaN] externs))))) + (comment (require '[cljs.compiler :as cc]) (require '[cljs.closure :as closure]) @@ -673,6 +689,12 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + (a/has-extern?* '[baz] + (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true}))) + ;; works, does not generate extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs From 91c7217293917bd295fa6895e160240ff7fbdef6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 12:00:14 -0500 Subject: [PATCH 2181/4033] simplify & fix cljs.analyzer/has-extern?*, more tests --- src/main/clojure/cljs/analyzer.cljc | 20 ++++++++-------- src/test/clojure/cljs/analyzer_tests.clj | 29 ++++-------------------- 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 473cae106..436bbe291 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -787,24 +787,22 @@ (into [tag 'prototype] (next pre)) pre) pre)] - (has-extern?* pre externs externs #{}))) - ([pre externs top seen] - (if (empty? pre) - true + (has-extern?* pre externs externs))) + ([pre externs top] + (cond + (empty? pre) true + :else (let [x (first pre) me (find externs x)] (cond - (seen x) true (not me) false :else - (let [seen' (conj seen x) - [x' externs'] me + (let [[x' externs'] me xmeta (meta x')] (if (and (= 'Function (:tag xmeta)) (:ctor xmeta)) - (let [pre' [(:ctor xmeta)]] - (or (has-extern?* (into pre' (next pre)) top top seen') - (has-extern?* (into (conj pre' 'prototype) (next pre)) top top seen'))) - (recur (next pre) externs' top seen')))))))) + (or (has-extern?* (into '[prototype] (next pre)) externs' top) + (has-extern?* (next pre) externs' top)) + (recur (next pre) externs' top)))))))) (defn has-extern? ([pre] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 70a3e2aa0..a03e06f2c 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -8,11 +8,12 @@ (ns cljs.analyzer-tests (:require [clojure.java.io :as io] - [cljs.analyzer :as a] + [cljs.util :as util] [cljs.env :as e] [cljs.env :as env] + [cljs.analyzer :as a] [cljs.analyzer.api :as ana-api] - [cljs.util :as util] + [cljs.closure :as closure] [cljs.externs :as externs]) (:use clojure.test)) @@ -625,7 +626,9 @@ {:externs ["src/test/externs/test.js"] :use-only-custom-externs true}))] (is (true? (a/has-extern? '[Foo] externs))) + (is (true? (a/has-extern? '[Foo wozMethod] externs))) (is (false? (a/has-extern? '[foo] externs))) + (is (false? (a/has-extern? '[Foo gozMethod] externs))) (is (true? (a/has-extern? '[baz] externs))) (is (false? (a/has-extern? '[Baz] externs))))) @@ -658,22 +661,6 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - (-> - (find (externs/externs-map - (closure/load-externs - {:externs ["src/test/externs/test.js"] - :use-only-custom-externs true})) - 'baz) - first meta) - - (-> - (find (externs/externs-map - (closure/load-externs - {:externs ["src/test/externs/test.js"] - :use-only-custom-externs true})) - 'Foo) - first meta) - ;; works, does not generate extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs @@ -689,12 +676,6 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - (a/has-extern?* '[baz] - (externs/externs-map - (closure/load-externs - {:externs ["src/test/externs/test.js"] - :use-only-custom-externs true}))) - ;; works, does not generate extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs From cd72226ab34d1fcf7b45afc6142b808d43af8308 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 12:01:46 -0500 Subject: [PATCH 2182/4033] rename tests --- src/test/clojure/cljs/analyzer_tests.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index a03e06f2c..add81b0f3 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -620,7 +620,7 @@ '(let [y 1] (def y 2))))] (is (some? (-> parsed :expr :ret :var-ast))))) -(deftest test-infer-externs-basic +(deftest test-has-extern?-basic (let [externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"] @@ -632,7 +632,7 @@ (is (true? (a/has-extern? '[baz] externs))) (is (false? (a/has-extern? '[Baz] externs))))) -(deftest test-infer-externs-default +(deftest test-has-extern?-defaults (let [externs (externs/externs-map)] (is (true? (a/has-extern? '[console] externs))) (is (true? (a/has-extern? '[console log] externs))) From a88fd8739d81fc4e0848056a63c8594e6b501655 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 12:08:05 -0500 Subject: [PATCH 2183/4033] placeholder infer tests --- src/test/clojure/cljs/analyzer_tests.clj | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index add81b0f3..f66c0e337 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -638,6 +638,24 @@ (is (true? (a/has-extern? '[console log] externs))) (is (true? (a/has-extern? '[Number isNaN] externs))))) +(def externs-cenv + (atom + {::a/externs + (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true}))})) + +(deftest test-externs-infer + (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) + '(js/baz)))) + (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) + '(let [x (js/baz)] x))))) + (comment (require '[cljs.compiler :as cc]) (require '[cljs.closure :as closure]) From fe450eed9d05798af77176ba285018b0644fc13b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 12:50:18 -0500 Subject: [PATCH 2184/4033] js-tag helper for resolving 'js/foo types and tests --- src/main/clojure/cljs/analyzer.cljc | 24 +++++++++++++++++++++--- src/test/clojure/cljs/analyzer_tests.clj | 6 ++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 436bbe291..fdac2066c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -815,6 +815,21 @@ (get-in externs (conj '[Number] x))))) (-> (last pre) str (string/starts-with? "cljs$"))))) +(defn js-tag + ([pre] + (js-tag pre :tag)) + ([pre tag-type] + (js-tag pre :tag (get @env/*compiler* ::externs))) + ([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))))))) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -830,9 +845,12 @@ (when-not (has-extern? pre) (swap! env/*compiler* update-in (into [::namespaces (-> env :ns :name) :externs] pre) merge {})) - {:name sym - :ns 'js - :tag (with-meta 'js {:prefix pre})})) + (merge + {:name sym + :ns 'js + :tag (with-meta (or (js-tag pre) 'js) {:prefix pre})} + (when-let [ret-tag (js-tag pre :ret-tag)] + {:ret-tag ret-tag})))) (let [s (str sym) lb (get locals sym)] (cond diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index f66c0e337..1640ee9f6 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -646,6 +646,12 @@ {:externs ["src/test/externs/test.js"] :use-only-custom-externs true}))})) +(deftest test-js-tag + (let [externs (externs/externs-map)] + (is (= 'js/Console (a/js-tag '[console] :tag externs))) + (is (= 'js/Function (a/js-tag '[console log] :tag externs))) + (is (= 'js/Boolean (a/js-tag '[Number isNaN] :ret-tag externs))))) + (deftest test-externs-infer (binding [a/*cljs-ns* a/*cljs-ns*] (e/with-compiler-env externs-cenv From d1582f8c07e0ece8985f9a6e1b3a08f85a0a62f0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 13:00:51 -0500 Subject: [PATCH 2185/4033] fix js-tag, infer test --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/analyzer_tests.clj | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index fdac2066c..8f5a95b7d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -819,7 +819,7 @@ ([pre] (js-tag pre :tag)) ([pre tag-type] - (js-tag pre :tag (get @env/*compiler* ::externs))) + (js-tag pre tag-type (get @env/*compiler* ::externs))) ([pre tag-type externs] (js-tag pre tag-type externs externs)) ([pre tag-type externs top] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 1640ee9f6..00bbf40ef 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -643,24 +643,23 @@ {::a/externs (externs/externs-map (closure/load-externs - {:externs ["src/test/externs/test.js"] - :use-only-custom-externs true}))})) + {:externs ["src/test/externs/test.js"]}))})) (deftest test-js-tag - (let [externs (externs/externs-map)] + (let [externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))] (is (= 'js/Console (a/js-tag '[console] :tag externs))) (is (= 'js/Function (a/js-tag '[console log] :tag externs))) - (is (= 'js/Boolean (a/js-tag '[Number isNaN] :ret-tag externs))))) + (is (= 'js/Boolean (a/js-tag '[Number isNaN] :ret-tag externs))) + (is (= 'js/Foo (a/js-tag '[baz] :ret-tag externs))))) (deftest test-externs-infer - (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) - '(js/baz)))) - (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) - '(let [x (js/baz)] x))))) + (is (= 'js/Foo + (-> (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) 'js/baz))) + :info :ret-tag)))) (comment (require '[cljs.compiler :as cc]) From e15af2f671e993bfadf0ee5f72e63dc78b28c827 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 13:15:01 -0500 Subject: [PATCH 2186/4033] use js-tag to determine tag in cljs.analyzer/analyze-dot --- src/main/clojure/cljs/analyzer.cljc | 10 ++++------ src/test/clojure/cljs/analyzer_tests.clj | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 8f5a95b7d..e034082d8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2637,7 +2637,9 @@ :target targetexpr :field field :children children - :tag tag}) + :tag (if (js-tag? tag) + (or (js-tag (-> tag meta :prefix) :tag) tag) + tag)}) ::call (let [argexprs (map #(analyze enve %) args) children (into [targetexpr] argexprs)] {:op :dot @@ -2648,11 +2650,7 @@ :args argexprs :children children :tag (if (js-tag? tag) - (let [pre (-> tag meta :prefix) - [sym _] (find (get-in @env/*compiler* - (into [::externs] (butlast pre))) - (last pre))] - (:ret-tag (meta sym) tag)) + (or (js-tag (-> tag meta :prefix) :ret-tag) tag) tag)})))) (defmethod parse '. diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 00bbf40ef..40b3b2b91 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -665,7 +665,6 @@ (require '[cljs.compiler :as cc]) (require '[cljs.closure :as closure]) - ;; TODO: need to handle the method/fn case (let [test-cenv (atom {::a/externs (externs/externs-map)})] (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] @@ -715,7 +714,7 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - ;; does NOT work, does not generate extern + ;; works, generates extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))})] From f24177ab8a73f36b57aaa52043fd4bedd2f51f71 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 13:35:55 -0500 Subject: [PATCH 2187/4033] if we know some foreign js/foo is a fn from externs, annotate the AST with :js-fn-var in infer-invoke, handle :js-fn-var case, in the case of an invoke we do not know anything about set the :tag to 'js --- src/main/clojure/cljs/analyzer.cljc | 8 ++++++-- src/test/clojure/cljs/analyzer_tests.clj | 24 +++++++++++++++--------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e034082d8..e7f01e5c9 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -850,7 +850,8 @@ :ns 'js :tag (with-meta (or (js-tag pre) 'js) {:prefix pre})} (when-let [ret-tag (js-tag pre :ret-tag)] - {:ret-tag ret-tag})))) + {:js-fn-var true + :ret-tag ret-tag})))) (let [s (str sym) lb (get locals sym)] (cond @@ -1076,7 +1077,10 @@ (defn infer-invoke [env e] (let [{info :info :as f} (:f e)] - (if-some [ret-tag (when (true? (:fn-var info)) (:ret-tag info))] + (if-some [ret-tag (if (or (true? (:fn-var info)) + (true? (:js-fn-var info))) + (:ret-tag info) + (when (= 'js (:ns info)) 'js))] ret-tag (let [args (:args e) me (assoc (find-matching-method f args) :op :method)] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 40b3b2b91..bf49ee354 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -659,7 +659,17 @@ (-> (binding [a/*cljs-ns* a/*cljs-ns*] (e/with-compiler-env externs-cenv (a/analyze (a/empty-env) 'js/baz))) - :info :ret-tag)))) + :info :ret-tag))) + (is (= 'js/Foo + (-> (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) '(js/baz)))) + :tag))) + (is (= 'js + (-> (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) '(js/woz)))) + :tag)))) (comment (require '[cljs.compiler :as cc]) @@ -691,8 +701,7 @@ a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq - '[(ns foo.core) - (js/console.log (.wozMethod (js/baz)))])) + '[(js/console.log (.wozMethod (js/baz)))])) (cc/emit-externs (reduce util/map-merge {} (map (comp :externs second) @@ -706,8 +715,7 @@ a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq - '[(ns foo.core) - (defn baz [^js/Foo x] + '[(defn afun [^js/Foo x] (.wozMethod x))])) (cc/emit-externs (reduce util/map-merge {} @@ -722,8 +730,7 @@ a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq - '[(ns foo.core) - (defn baz [^js/Foo a] + '[(defn baz [^js/Foo a] (.gozMethod a))])) (cc/emit-externs (reduce util/map-merge {} @@ -738,8 +745,7 @@ a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq - '[(ns foo.core) - (js/console.log (.gozMethod (js/baz)))])) + '[(.gozMethod (js/baz))])) (cc/emit-externs (reduce util/map-merge {} (map (comp :externs second) From 3def8794789398a74cb2ba7cdb7929839aeb66a2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 13:41:40 -0500 Subject: [PATCH 2188/4033] more examples --- src/test/clojure/cljs/analyzer_tests.clj | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index bf49ee354..ebcc32dc7 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -737,7 +737,7 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - ;; does NOT work, does not generate extern + ;; works, generates extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))})] @@ -751,5 +751,19 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + ;; works, generates externs + (let [test-cenv (atom {::a/externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(.gozMethod (js/woz))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + ) \ No newline at end of file From b7cf258b9db1ff192cd72891b462050b6fb96d50 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 13:48:40 -0500 Subject: [PATCH 2189/4033] test top level expressions --- src/test/clojure/cljs/analyzer_tests.clj | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index ebcc32dc7..c4d1f2906 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -669,7 +669,17 @@ (-> (binding [a/*cljs-ns* a/*cljs-ns*] (e/with-compiler-env externs-cenv (a/analyze (a/empty-env) '(js/woz)))) - :tag)))) + :tag))) + (is (= 'js + (-> (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) '(def foo (js/woz))))) + :tag))) + (is (= 'js + (-> (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) '(def foo js/boz)))) + :tag)))) (comment (require '[cljs.compiler :as cc]) From 0ff0d2a2bb0e5ffe43b62ecd31d68be768f5a281 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 14:37:27 -0500 Subject: [PATCH 2190/4033] warning for unresolveable properties on known extern types --- src/main/clojure/cljs/analyzer.cljc | 25 +++++++++++++++++------- src/test/clojure/cljs/analyzer_tests.clj | 17 +++++++++++++--- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e7f01e5c9..d4fda7b5f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -420,8 +420,11 @@ (str name " is shadowed by a local")) (defmethod error-message :infer-warning - [warning-type {:keys [form]}] - (str "Cannot infer target type for " form "")) + [warning-type {:keys [warn-type form type property]}] + (case warn-type + :target (str "Cannot infer target type in expression " form "") + :property (str "Cannot resolve property " property + " for inferred type " type " in expression " form))) (defn default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) @@ -2623,11 +2626,19 @@ update-in [:prefix] (fnil conj '[Object]) prop)) nil)] (when (and (not (string/starts-with? (str prop) "cljs$")) - (not= 'js target-tag) - (get-in env [:locals target])) - (when (or (nil? target-tag) - ('#{any} target-tag)) - (warning :infer-warning env {:form form}))) + (not= 'js target-tag)) + (when (and (get-in env [:locals target]) + (or (nil? target-tag) + ('#{any} target-tag))) + (warning :infer-warning env + {:warn-type :target :form form})) + (let [[pre' pre] ((juxt (comp butlast butlast) identity) ;; drop prototype from pre' + (-> tag meta :prefix))] + (when (and (has-extern? pre') (not (has-extern? pre))) + (warning :infer-warning env + {:warn-type :property :form form + :type (symbol "js" (string/join "." pre')) + :property prop})))) (when (js-tag? tag) (let [pre (-> tag meta :prefix)] (when-not (has-extern? pre) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index c4d1f2906..e2ca50871 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -703,6 +703,18 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + (let [test-cenv (atom {::a/externs (externs/externs-map)})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(defn foo [^js/React.Component c] + (.render c))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + ;; works, does not generate extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs @@ -761,7 +773,7 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - ;; works, generates externs + ;; known extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))})] @@ -769,11 +781,10 @@ a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq - '[(.gozMethod (js/woz))])) + '[(.gozMethod (js/baz))])) (cc/emit-externs (reduce util/map-merge {} (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - ) \ No newline at end of file From ef3a4adba3889498f37936d4ae44e1f9ac972d6b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 15:39:39 -0500 Subject: [PATCH 2191/4033] support shorthand for language-in/out --- src/main/clojure/cljs/closure.clj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ef98a6201..3318a0786 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -156,13 +156,13 @@ (defn ^CompilerOptions$LanguageMode lang-key->lang-mode [key] (case key - :no-transpile CompilerOptions$LanguageMode/NO_TRANSPILE - :ecmascript6 CompilerOptions$LanguageMode/ECMASCRIPT6 - :ecmascript6-strict CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT - :ecmascript6-typed CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED - :ecmascript5 CompilerOptions$LanguageMode/ECMASCRIPT5 - :ecmascript5-strict CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT - :ecmascript3 CompilerOptions$LanguageMode/ECMASCRIPT3)) + :no-transpile CompilerOptions$LanguageMode/NO_TRANSPILE + (:ecmascript6 :es6) CompilerOptions$LanguageMode/ECMASCRIPT6 + (:ecmascript6-strict :es6-strict) CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT + (:ecmascript6-typed :es6-typed) CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED + (:ecmascript5 :es5) CompilerOptions$LanguageMode/ECMASCRIPT5 + (:ecmascript5-strict :es5-strict) CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT + (:ecmascript3 :es3) CompilerOptions$LanguageMode/ECMASCRIPT3)) (defn set-options "TODO: Add any other options that we would like to support." From 9ad6d5d61cb96a9f8552489c6811a479b93f864c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 17:59:35 -0500 Subject: [PATCH 2192/4033] do not throw on circular dependencies between Google Closure JS libs --- src/main/clojure/cljs/js_deps.cljc | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 6a7c1dcde..804fa08d9 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -164,16 +164,18 @@ case." ([state ns-name] (dependency-order-visit state ns-name [])) ([state ns-name seen] - (assert (not (some #{ns-name} seen)) - (str "Circular dependency detected, " - (apply str (interpose " -> " (conj seen ns-name))))) - (let [file (get state ns-name)] - (if (or (:visited file) (nil? file)) - state - (let [state (assoc-in state [ns-name :visited] true) - deps (:requires file) - state (reduce #(dependency-order-visit %1 %2 (conj seen ns-name)) state deps)] - (assoc state :order (conj (:order state) file))))))) + #_(assert (not (some #{ns-name} seen)) + (str "Circular dependency detected, " + (apply str (interpose " -> " (conj seen ns-name))))) + (if-not (some #{ns-name} seen) + (let [file (get state ns-name)] + (if (or (:visited file) (nil? file)) + state + (let [state (assoc-in state [ns-name :visited] true) + deps (:requires file) + state (reduce #(dependency-order-visit %1 %2 (conj seen ns-name)) state deps)] + (assoc state :order (conj (:order state) file))))) + state))) (defn- pack-string [s] (if (string? s) From ca57179327489d977ef7547a580febbdb7c1d9f8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 22 Jan 2017 19:28:53 -0500 Subject: [PATCH 2193/4033] broken example found by writing the guide --- src/test/clojure/cljs/analyzer_tests.clj | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index e2ca50871..8106d4d79 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -787,4 +787,22 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + ;; wrong + (let [test-cenv (atom {::a/externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(fn [^js/Foo.Bar x] + (let [z (.baz x)] + (.-wozz z)))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + + ;; TODO: test (def foo (js/require "bar.js")) pattern + ) \ No newline at end of file From 8838bb91286d4d4134749a8de6df11b869422530 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Jan 2017 08:04:31 -0500 Subject: [PATCH 2194/4033] we need to erase the prefix after a method call we don't the return type for, add test --- src/main/clojure/cljs/analyzer.cljc | 10 +++++++--- src/test/clojure/cljs/analyzer_tests.clj | 15 +++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d4fda7b5f..b75a633ce 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2627,17 +2627,21 @@ nil)] (when (and (not (string/starts-with? (str prop) "cljs$")) (not= 'js target-tag)) + ;; Cannot determine type of the target (when (and (get-in env [:locals target]) (or (nil? target-tag) ('#{any} target-tag))) (warning :infer-warning env {:warn-type :target :form form})) - (let [[pre' pre] ((juxt (comp butlast butlast) identity) ;; drop prototype from pre' + ;; Unresolveable property on existing extern + (let [[pre' pre] ((juxt butlast identity) ;; drop prototype from pre' (-> tag meta :prefix))] (when (and (has-extern? pre') (not (has-extern? pre))) (warning :infer-warning env {:warn-type :property :form form - :type (symbol "js" (string/join "." pre')) + :type (symbol "js" + (string/join "." + (cond-> pre' (= 'prototype (last pre')) butlast))) :property prop})))) (when (js-tag? tag) (let [pre (-> tag meta :prefix)] @@ -2665,7 +2669,7 @@ :args argexprs :children children :tag (if (js-tag? tag) - (or (js-tag (-> tag meta :prefix) :ret-tag) tag) + (or (js-tag (-> tag meta :prefix) :ret-tag) 'js) tag)})))) (defmethod parse '. diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 8106d4d79..262bbc3f8 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -676,10 +676,17 @@ (a/analyze (a/empty-env) '(def foo (js/woz))))) :tag))) (is (= 'js - (-> (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) '(def foo js/boz)))) - :tag)))) + (-> (binding [a/*cljs-ns* a/*cljs-ns*] + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) '(def foo js/boz)))) + :tag))) + (is (nil? (-> (binding [a/*cljs-ns* a/*cljs-ns*] + (a/no-warn + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) + '(let [z (.baz ^js/Foo.Bar x)] + z))))) + :tag meta :prefix)))) (comment (require '[cljs.compiler :as cc]) From e34628946bee10e626d0a7ed98e9e8095d68ecc9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Jan 2017 08:18:59 -0500 Subject: [PATCH 2195/4033] warn about externs to Object --- src/main/clojure/cljs/analyzer.cljc | 39 +++++++++++++----------- src/test/clojure/cljs/analyzer_tests.clj | 1 - 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b75a633ce..c0fdbaf18 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -424,7 +424,9 @@ (case warn-type :target (str "Cannot infer target type in expression " form "") :property (str "Cannot resolve property " property - " for inferred type " type " in expression " form))) + " for inferred type " type " in expression " form) + :object (str "Adding extern to Object for property " property " due to " + "ambiguous expression " form))) (defn default-warning-handler [warning-type env extra] (when (warning-type *cljs-warnings*) @@ -2625,24 +2627,27 @@ (vary-meta (normalize-js-tag target-tag) update-in [:prefix] (fnil conj '[Object]) prop)) nil)] - (when (and (not (string/starts-with? (str prop) "cljs$")) - (not= 'js target-tag)) - ;; Cannot determine type of the target - (when (and (get-in env [:locals target]) - (or (nil? target-tag) - ('#{any} target-tag))) + (when (not (string/starts-with? (str prop) "cljs$")) + ;; Adding to Object + (when (= 'Object (first (-> tag meta :prefix))) (warning :infer-warning env - {:warn-type :target :form form})) - ;; Unresolveable property on existing extern - (let [[pre' pre] ((juxt butlast identity) ;; drop prototype from pre' - (-> tag meta :prefix))] - (when (and (has-extern? pre') (not (has-extern? pre))) + {:warn-type :object :form form :property prop})) + (when (not= 'js target-tag) + ;; Cannot determine type of the target + (when (and (get-in env [:locals target]) + (or (nil? target-tag) + ('#{any} target-tag))) (warning :infer-warning env - {:warn-type :property :form form - :type (symbol "js" - (string/join "." - (cond-> pre' (= 'prototype (last pre')) butlast))) - :property prop})))) + {:warn-type :target :form form})) + ;; Unresolveable property on existing extern + (let [[pre' pre] ((juxt butlast identity) (-> tag meta :prefix))] + (when (and (has-extern? pre') (not (has-extern? pre))) + (warning :infer-warning env + {:warn-type :property :form form + :type (symbol "js" + (string/join "." + (cond-> pre' (= 'prototype (last pre')) butlast))) + :property prop}))))) (when (js-tag? tag) (let [pre (-> tag meta :prefix)] (when-not (has-extern? pre) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 262bbc3f8..de0ff1c07 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -794,7 +794,6 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - ;; wrong (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))})] From a5c164c9dc4404374a790a0f6796b72c07b4fe91 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Jan 2017 08:29:54 -0500 Subject: [PATCH 2196/4033] add more extern cases --- src/test/externs/test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/externs/test.js b/src/test/externs/test.js index cef7c15c3..fe206c1a0 100644 --- a/src/test/externs/test.js +++ b/src/test/externs/test.js @@ -4,8 +4,20 @@ var Foo = function() {}; Foo.prototype.wozMethod = function() { }; - /** * @return {Foo} */ var baz = function() {}; +/** + * @constructor + */ +Foo.Bar = function() {}; +/** + * @return {Foo.Boo} + */ +Foo.Bar.prototype.baz = function() {}; +/** + * @constructor + */ +Foo.Boo = function() {}; +Foo.Boo.prototype.woz = function() {}; \ No newline at end of file From 637763f01b2ed8000f72bbe956ddbc7d3fc97665 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Jan 2017 08:33:49 -0500 Subject: [PATCH 2197/4033] add comment case that produces unnecessary externs --- src/test/clojure/cljs/analyzer_tests.clj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index de0ff1c07..5e03b8712 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -751,6 +751,22 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) + ;; FIXME: generates externs we know about including the one we don't + (let [test-cenv (atom {::a/externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(defn afun [^js/Foo.Bar x] + (let [z (.baz x)] + (.wozz z)))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) + ;; works, generates extern (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs From d636dddd2e31465adbe79af5ef9fe9d3a918e22a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Jan 2017 08:58:30 -0500 Subject: [PATCH 2198/4033] add new case that doesn't work --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/analyzer_tests.clj | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c0fdbaf18..dc02edb0b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2636,7 +2636,7 @@ ;; Cannot determine type of the target (when (and (get-in env [:locals target]) (or (nil? target-tag) - ('#{any} target-tag))) + ('#{any} target-tag))) (warning :infer-warning env {:warn-type :target :form form})) ;; Unresolveable property on existing extern diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 5e03b8712..e6f25bc93 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -825,6 +825,20 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - ;; TODO: test (def foo (js/require "bar.js")) pattern + ;; FIXME: we don't get an extern for Component + (let [test-cenv (atom {::a/externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))})] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns foo.core) + (def React (js/require "react")) + (.log js/console (.-Component React))])) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces)))))) ) \ No newline at end of file From 2e59fceea7fb5cd426f774854018e439465a91d3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 23 Jan 2017 09:31:05 -0500 Subject: [PATCH 2199/4033] do not restrict warnings to locals --- src/main/clojure/cljs/analyzer.cljc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index dc02edb0b..dfc873554 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2634,9 +2634,7 @@ {:warn-type :object :form form :property prop})) (when (not= 'js target-tag) ;; Cannot determine type of the target - (when (and (get-in env [:locals target]) - (or (nil? target-tag) - ('#{any} target-tag))) + (when (or (nil? target-tag) ('#{any} target-tag)) (warning :infer-warning env {:warn-type :target :form form})) ;; Unresolveable property on existing extern From aa4f6365b40aa4238ce6764fae2309c6cca6e59e Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 23 Jan 2017 12:47:21 -0500 Subject: [PATCH 2200/4033] example showing that we don't preserve tag information --- src/test/clojure/cljs/analyzer_tests.clj | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index e6f25bc93..0abc07ab2 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -688,6 +688,27 @@ z))))) :tag meta :prefix)))) +(comment + (binding [a/*cljs-ns* a/*cljs-ns*] + (a/no-warn + (e/with-compiler-env externs-cenv + (a/analyze (a/empty-env) + '(let [React (js/require "react")] + React))))) + + ;; FIXME: we don't preserve tag information + (binding [a/*cljs-ns* a/*cljs-ns*] + (a/no-warn + (e/with-compiler-env externs-cenv + (let [aenv (a/empty-env) + _ (a/analyze aenv '(ns foo.core)) + aenv' (assoc-in aenv [:ns :name] 'foo.core) + _ (a/analyze aenv' '(def x 1))] + (dissoc (a/analyze-symbol (assoc-in aenv [:ns :name] 'foo.core) 'x) :env) + ;(get-in @externs-cenv [::a/namespaces 'foo.core]) + )))) + ) + (comment (require '[cljs.compiler :as cc]) (require '[cljs.closure :as closure]) From 0436a318a6e05db3d8d93d74384e374c4781afbf Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 23 Jan 2017 13:21:36 -0500 Subject: [PATCH 2201/4033] we did not associate :tag with vars in the case of assignment. changing this means we need to tweak numeric checks to accept 'clj-nil and 'js to new warnings --- src/main/clojure/cljs/analyzer.cljc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index dfc873554..9b247bd28 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1330,7 +1330,7 @@ fn-var? (and (some? init-expr) (= (:op init-expr) :fn)) tag (if fn-var? (or (:ret-tag init-expr) tag) - tag) + (or tag (:tag init-expr))) export-as (when-let [export-val (-> sym meta :export)] (if (= true export-val) var-name export-val)) doc (or (:doc args) (-> sym meta :doc))] @@ -1377,9 +1377,10 @@ :max-fixed-arity (:max-fixed-arity init-expr) :method-params params :arglists (:arglists sym-meta) - :arglists-meta (doall (map meta (:arglists sym-meta)))}))) ) - (when (and fn-var? (some? tag)) - {:ret-tag tag}))) + :arglists-meta (doall (map meta (:arglists sym-meta)))})))) + (if (and fn-var? (some? tag)) + {:ret-tag tag} + (when tag {:tag tag})))) (merge {:env env :op :def @@ -2719,6 +2720,7 @@ ;; warning without this - David (cond (nil? t) true + (= 'clj-nil t) true (js-tag? t) true ;; TODO: revisit :else (if (and (symbol? t) (some? (get NUMERIC_SET t))) @@ -2728,7 +2730,8 @@ (or (contains? t 'number) (contains? t 'long) (contains? t 'double) - (contains? t 'any)))))) + (contains? t 'any) + (contains? t 'js)))))) (defn analyze-js-star* [env jsform args form] (let [enve (assoc env :context :expr) From 19cc3945d302d9e3c2da103c9a5d42ff31ad8366 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 23 Jan 2017 16:20:08 -0500 Subject: [PATCH 2202/4033] update example, works fine --- src/test/clojure/cljs/analyzer_tests.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 0abc07ab2..03ebe9b8a 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -846,7 +846,6 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces)))))) - ;; FIXME: we don't get an extern for Component (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))})] From 960bb9b778190aa7359acb2f74cc61d452cef2ae Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 23 Jan 2017 16:57:11 -0500 Subject: [PATCH 2203/4033] CLJS-1897: Too many externs generated --- src/main/clojure/cljs/compiler.cljc | 14 ++-- src/test/clojure/cljs/analyzer_tests.clj | 100 +++++++++++------------ 2 files changed, 59 insertions(+), 55 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index e8a134e13..51469d72a 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1516,21 +1516,25 @@ (defn emit-externs ([externs] - (emit-externs [] externs (atom #{}))) - ([prefix externs top-level] + (emit-externs [] externs (atom #{}) + (when env/*compiler* + (::ana/externs @env/*compiler*)))) + ([prefix externs top-level known-externs] (loop [ks (seq (keys externs))] (when ks (let [k (first ks) [top :as prefix'] (conj prefix k)] - (when-not (= 'prototype k) - (if-not (contains? @top-level top) + (when (and (not= 'prototype k) + (nil? (get-in known-externs prefix'))) + (if-not (or (contains? @top-level top) + (contains? known-externs top)) (do (emitln "var " (string/join "." (map munge prefix')) ";") (swap! top-level conj top)) (emitln (string/join "." (map munge prefix')) ";"))) (let [m (get externs k)] (when-not (empty? m) - (emit-externs prefix' m top-level)))) + (emit-externs prefix' m top-level known-externs)))) (recur (next ks)))))) #?(:clj diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 03ebe9b8a..19eaebdc5 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -725,11 +725,11 @@ (fn [& args] (.apply (.-log js/console) js/console (into-array args))) (js/console.log js/Number.MAX_VALUE) - (js/console.log js/Symbol.iterator)])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + (js/console.log js/Symbol.iterator)]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) (let [test-cenv (atom {::a/externs (externs/externs-map)})] (binding [a/*cljs-ns* a/*cljs-ns* @@ -737,11 +737,11 @@ (e/with-compiler-env test-cenv (a/analyze-form-seq '[(defn foo [^js/React.Component c] - (.render c))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + (.render c))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) ;; works, does not generate extern (let [test-cenv (atom {::a/externs (externs/externs-map @@ -751,11 +751,11 @@ a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq - '[(js/console.log (.wozMethod (js/baz)))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + '[(js/console.log (.wozMethod (js/baz)))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) ;; works, does not generate extern (let [test-cenv (atom {::a/externs (externs/externs-map @@ -766,11 +766,11 @@ (e/with-compiler-env test-cenv (a/analyze-form-seq '[(defn afun [^js/Foo x] - (.wozMethod x))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + (.wozMethod x))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) ;; FIXME: generates externs we know about including the one we don't (let [test-cenv (atom {::a/externs (externs/externs-map @@ -782,11 +782,11 @@ (a/analyze-form-seq '[(defn afun [^js/Foo.Bar x] (let [z (.baz x)] - (.wozz z)))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + (.wozz z)))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) ;; works, generates extern (let [test-cenv (atom {::a/externs (externs/externs-map @@ -797,11 +797,11 @@ (e/with-compiler-env test-cenv (a/analyze-form-seq '[(defn baz [^js/Foo a] - (.gozMethod a))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + (.gozMethod a))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) ;; works, generates extern (let [test-cenv (atom {::a/externs (externs/externs-map @@ -811,11 +811,11 @@ a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq - '[(.gozMethod (js/baz))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + '[(.gozMethod (js/baz))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) ;; known extern (let [test-cenv (atom {::a/externs (externs/externs-map @@ -825,11 +825,11 @@ a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)] (e/with-compiler-env test-cenv (a/analyze-form-seq - '[(.gozMethod (js/baz))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + '[(.gozMethod (js/baz))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs @@ -840,11 +840,11 @@ (a/analyze-form-seq '[(fn [^js/Foo.Bar x] (let [z (.baz x)] - (.-wozz z)))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + (.-wozz z)))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) (let [test-cenv (atom {::a/externs (externs/externs-map (closure/load-externs @@ -855,10 +855,10 @@ (a/analyze-form-seq '[(ns foo.core) (def React (js/require "react")) - (.log js/console (.-Component React))])) - (cc/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))) + (.log js/console (.-Component React))]) + (cc/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::a/namespaces))))))) ) \ No newline at end of file From 94b4e9cdc845c1345d28f8e1a339189bd3de6971 Mon Sep 17 00:00:00 2001 From: Angus Fletcher Date: Mon, 9 Jan 2017 19:33:57 -0400 Subject: [PATCH 2204/4033] Change ns deps to a vector, use to inform load-libs --- src/main/clojure/cljs/analyzer.cljc | 8 ++++---- src/main/clojure/cljs/compiler.cljc | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9b247bd28..fe593149b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2342,7 +2342,7 @@ core-renames (reduce (fn [m [original renamed]] (assoc m renamed (symbol "cljs.core" (str original)))) {} core-renames) - deps (atom #{}) + deps (atom []) aliases (atom {:fns {} :macros {}}) spec-parsers {:require (partial parse-require-spec env false deps aliases) :require-macros (partial parse-require-spec env true deps aliases) @@ -2406,7 +2406,7 @@ (merge {:op :ns :env env :form form - :deps @deps + :deps (into [] (distinct @deps)) :reload @reload :reloads @reloads} (cond-> ns-info @@ -2439,7 +2439,7 @@ core-renames (reduce (fn [m [original renamed]] (assoc m renamed (symbol "cljs.core" (str original)))) {} core-renames) - deps (atom #{}) + deps (atom []) aliases (atom {:fns {} :macros {}}) spec-parsers {:require (partial parse-require-spec env false deps aliases) :require-macros (partial parse-require-spec env true deps aliases) @@ -2497,7 +2497,7 @@ (merge {:op :ns* :env env :form form - :deps @deps + :deps (into [] (distinct @deps)) :reload @reload :reloads @reloads} (cond-> require-info diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 51469d72a..6a06c6156 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1032,13 +1032,13 @@ (emit-wrap env (emits target " = " val))) (defn load-libs - [libs seen reloads] + [libs seen reloads deps] (let [loaded-libs (munge 'cljs.core.*loaded-libs*) loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))] (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) - (doseq [lib (remove (set (vals seen)) (distinct (vals libs)))] + (doseq [lib (remove (set (vals seen)) (filter #(get libs %) deps))] (cond #?@(:clj [(ana/foreign-dep? lib) @@ -1068,19 +1068,19 @@ (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) (defmethod emit* :ns* - [{:keys [name requires uses require-macros reloads env]}] - (load-libs requires nil (:require reloads)) - (load-libs uses requires (:use reloads))) + [{:keys [name requires uses require-macros reloads env deps]}] + (load-libs requires nil (:require reloads) deps) + (load-libs uses requires (:use reloads) deps)) (defmethod emit* :ns - [{:keys [name requires uses require-macros reloads env]}] + [{:keys [name requires uses require-macros reloads env deps]}] (emitln "goog.provide('" (munge name) "');") (when-not (= name 'cljs.core) (emitln "goog.require('cljs.core');") (when (-> @env/*compiler* :options :emit-constants) (emitln "goog.require('" (munge ana/constants-ns-sym) "');"))) - (load-libs requires nil (:require reloads)) - (load-libs uses requires (:use reloads))) + (load-libs requires nil (:require reloads) deps) + (load-libs uses requires (:use reloads) deps)) (defmethod emit* :deftype* [{:keys [t fields pmasks body protocols]}] From 2239b4b3998736b7f9fbece749d7b15f6a6df417 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 25 Jan 2017 16:52:03 -0500 Subject: [PATCH 2205/4033] old semantics not preserved in last commit --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 6a06c6156..cceacbd7e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1038,7 +1038,7 @@ (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) - (doseq [lib (remove (set (vals seen)) (filter #(get libs %) deps))] + (doseq [lib (remove (set (vals seen)) (filter (set (vals libs)) deps))] (cond #?@(:clj [(ana/foreign-dep? lib) From 7751a39e1f05cac1da46b71c4a88d962841c123a Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 25 Jan 2017 17:58:42 -0500 Subject: [PATCH 2206/4033] temporary fix to the process JS modules issue at the REPL --- src/main/clojure/cljs/repl.cljc | 34 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index a0036bac1..2736ff95c 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -817,6 +817,8 @@ (catch Throwable e (caught e repl-env opts) opts)))) + ;; TODO: consider alternative ways to deal with JS module processing at REPL + opts' opts ;; need to save opts prior to JS module processing for watch opts (if (or (:libs opts) (:foreign-libs opts)) (let [opts (cljsc/process-js-modules opts)] (swap! env/*compiler* assoc :js-dependency-index (deps/js-dependency-index opts)) @@ -863,21 +865,23 @@ (init) (catch Throwable e (caught e repl-env opts))) - (when-let [src (:watch opts)] - (.start - (Thread. - ((ns-resolve 'clojure.core 'binding-conveyor-fn) - (fn [] - (let [log-file (io/file (util/output-directory opts) "watch.log")] - (err-out (println "Watch compilation log available at:" (str log-file))) - (try - (let [log-out (FileWriter. log-file)] - (binding [*err* log-out - *out* log-out] - (cljsc/watch src (dissoc opts :watch) - env/*compiler* done?))) - (catch Throwable e - (caught e repl-env opts))))))))) + ;; TODO: consider alternative ways to deal with JS module processing at REPL + (let [opts opts'] ;; use opts prior to JS module processing + (when-let [src (:watch opts)] + (.start + (Thread. + ((ns-resolve 'clojure.core 'binding-conveyor-fn) + (fn [] + (let [log-file (io/file (util/output-directory opts) "watch.log")] + (err-out (println "Watch compilation log available at:" (str log-file))) + (try + (let [log-out (FileWriter. log-file)] + (binding [*err* log-out + *out* log-out] + (cljsc/watch src (dissoc opts :watch) + env/*compiler* done?))) + (catch Throwable e + (caught e repl-env opts)))))))))) ;; let any setup async messages flush (Thread/sleep 50) (binding [*in* (if (true? (:source-map-inline opts)) From a64ab2c975bcc000fd10b4b485b4ba4259e70276 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 24 Jan 2017 23:55:40 -0500 Subject: [PATCH 2207/4033] CLJS-1903: Remove anonymous vars from dir and apropos output --- src/main/clojure/cljs/repl.cljc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 2736ff95c..a2238128b 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1210,6 +1210,13 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) [n] `(println ~(or (source-fn &env n) (str "Source not found")))) +(defn- named-publics-vars + "Gets the public vars in a namespace that are not anonymous." + [ns] + (->> (ana-api/ns-publics ns) + (remove (comp :anonymous val)) + (map key))) + (defmacro apropos "Given a regular expression or stringable thing, return a seq of all public definitions in all currently-loaded namespaces that match the @@ -1224,13 +1231,13 @@ str-or-pattern." (fn [ns] (let [ns-name (str ns)] (map #(symbol ns-name (str %)) - (filter matches? (keys (ana-api/ns-publics ns)))))) + (filter matches? (named-publics-vars ns))))) (ana-api/all-ns)))))) (defmacro dir "Prints a sorted directory of public vars in a namespace" [ns] - `(doseq [sym# (quote ~(sort (keys (ana-api/ns-publics ns))))] + `(doseq [sym# (quote ~(sort (named-publics-vars ns)))] (println sym#))) (defmacro pst From cdaeff298e0f1d410aa5a7b6860232270d287084 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 25 Jan 2017 19:17:24 -0500 Subject: [PATCH 2208/4033] under Node.js we don't need require entries in the goog.addDependency calls in cljs_deps.js - runtime require is possible. It's surprising this hasn't come up as a problem before. --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3318a0786..816c0f7d1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1352,7 +1352,9 @@ "\", [" (ns-list (deps/-provides input)) "], [" - (ns-list (deps/-requires input)) + ;; under Node.js runtime require is possible + (when-not (= :nodejs (:target opts)) + (ns-list (deps/-requires input))) "]);\n"))) (defn deps-file From a98d9a64559764865af5dbdaf5234880fe1f3a02 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Jan 2017 13:21:24 -0500 Subject: [PATCH 2209/4033] bump Closure Compiler dep --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 5bae55d8e..85bbb4290 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20160911 + v20161201 org.clojure diff --git a/project.clj b/project.clj index aa832d96b..c4c1ddb66 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.285"] [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] - [com.google.javascript/closure-compiler-unshaded "v20160911"] + [com.google.javascript/closure-compiler-unshaded "v20161201"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 44e802f52..f0833fc20 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha7" -CLOSURE_RELEASE="20160911" +CLOSURE_RELEASE="20161201" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" From d4e0d9c52381a200175c12204eb5ecda050536e7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Jan 2017 13:21:58 -0500 Subject: [PATCH 2210/4033] fix cljs/externs.clj & modules tests for Closure dep change --- src/main/clojure/cljs/externs.clj | 2 +- src/test/clojure/cljs/test_util.clj | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 4b965333f..f7deca6a1 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -49,7 +49,7 @@ (defmulti parse-extern-node (fn [^Node node] - (.getType node))) + (.getToken node))) (defmethod parse-extern-node Token/VAR [node] (when (> (.getChildCount node) 0) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index 3309021f5..905adde2a 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -36,7 +36,8 @@ :entries #{'module-test.modules.a}} :module-b {:output-to (str (io/file output-dir "module-b.js")) - :entries #{'module-test.modules.b}}}}}) + :entries #{'module-test.modules.b}}} + :closure-warnings {:check-types :off}}}) (defn tmp-dir "Returns the temporary directory of the system." From 367e2fb600c54d3b04a1d85ccf9bc659b7f05151 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Jan 2017 17:00:21 -0500 Subject: [PATCH 2211/4033] disable type checking if not provided --- src/main/clojure/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 816c0f7d1..e383b582f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1908,7 +1908,10 @@ (= optimizations :advanced) (cond-> (not (false? (:static-fns opts))) (assoc :static-fns true) - (not (false? (:optimize-constants opts))) (assoc :optimize-constants true))))) + (not (false? (:optimize-constants opts))) (assoc :optimize-constants true)) + + (nil? (find (:closure-warnings opts) :check-types)) + (assoc-in [:closure-warnings :check-types] :off)))) (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google From a639bea8e21aa0629eccaea18afb0796a265f206 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Jan 2017 20:41:11 -0500 Subject: [PATCH 2212/4033] expose :closure-module-roots option --- src/main/clojure/cljs/closure.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e383b582f..59c9b1362 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -201,6 +201,10 @@ (. compiler-options (setExtraAnnotationNames (map name (:closure-extra-annotations opts))))) + (when (contains? opts :closure-module-roots) + (. compiler-options + (setModuleRoots (:closure-module-roots opts)))) + (. compiler-options (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler ) @@ -1527,7 +1531,7 @@ (-> opts (select-keys [:closure-warnings :closure-extra-annotations :pretty-print - :language-in :language-out]) + :language-in :language-out :closure-module-roots]) (set-options (CompilerOptions.)))) (defn get-js-root [closure-compiler] From 59a7f265fac02c931cc3d5615727da861db62e3b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Jan 2017 22:04:09 -0500 Subject: [PATCH 2213/4033] refactor Node.js support --- src/main/cljs/cljs/bootstrap_node.js | 2 +- src/main/cljs/cljs/nodejs.cljs | 4 ++-- src/main/cljs/cljs/nodejscli.cljs | 5 +---- src/main/clojure/cljs/repl/node.clj | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_node.js index e0fb15403..5c0d7924c 100644 --- a/src/main/cljs/cljs/bootstrap_node.js +++ b/src/main/cljs/cljs/bootstrap_node.js @@ -72,7 +72,7 @@ global.CLOSURE_IMPORT_SCRIPT = function(src) { // Sources are always expressed relative to closure's base.js, but // require() is always relative to the current source. - require(path.join(".", "..", src)); + nodeGlobalRequire(path.resolve(__dirname, '..', src)); return true; }; diff --git a/src/main/cljs/cljs/nodejs.cljs b/src/main/cljs/cljs/nodejs.cljs index 356023609..0c06471cd 100644 --- a/src/main/cljs/cljs/nodejs.cljs +++ b/src/main/cljs/cljs/nodejs.cljs @@ -13,8 +13,8 @@ (:refer-clojure :exclude [require])) ; Define namespaced references to Node's externed globals: -(def require (js* "require")) -(def process (js* "process")) +; (def require (js* "require")) ; +; (def process (js* "process")) (defn enable-util-print! [] (set! *print-newline* false) diff --git a/src/main/cljs/cljs/nodejscli.cljs b/src/main/cljs/cljs/nodejscli.cljs index 4c3f404d6..06fda2456 100644 --- a/src/main/cljs/cljs/nodejscli.cljs +++ b/src/main/cljs/cljs/nodejscli.cljs @@ -17,8 +17,5 @@ (set! js/goog.global js/global)) ;; Call the user's main function -(if (or (nil? cljs.core/*main-cli-fn*) - (not (fn? cljs.core/*main-cli-fn*))) - (throw (js/Error. "cljs.core/*main-cli-fn* not set")) +(when (fn? cljs.core/*main-cli-fn*) (apply cljs.core/*main-cli-fn* (drop 2 (.-argv nodejs/process)))) - diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index f74204f57..f3eb056c6 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -153,7 +153,7 @@ (string/replace "path.resolve(__dirname, '..', 'base.js')" (platform-path (conj rewrite-path "bootstrap" ".." "base.js"))) (string/replace - "path.join(\".\", \"..\", src)" + "path.resolve(__dirname, '..', src)" (str "path.join(" (platform-path rewrite-path) ", src)")) (string/replace "var CLJS_ROOT = \".\";" From fb90282f41799fe509b143e7870eaf4f9885de41 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 26 Jan 2017 22:09:12 -0500 Subject: [PATCH 2214/4033] fix warning about Node.js process --- src/main/cljs/cljs/nodejscli.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/nodejscli.cljs b/src/main/cljs/cljs/nodejscli.cljs index 06fda2456..de7d103b5 100644 --- a/src/main/cljs/cljs/nodejscli.cljs +++ b/src/main/cljs/cljs/nodejscli.cljs @@ -18,4 +18,4 @@ ;; Call the user's main function (when (fn? cljs.core/*main-cli-fn*) - (apply cljs.core/*main-cli-fn* (drop 2 (.-argv nodejs/process)))) + (apply cljs.core/*main-cli-fn* (drop 2 (.-argv js/process)))) From f4e057c7742861d48568b776024d142f68143fac Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 25 Jan 2017 20:37:31 -0500 Subject: [PATCH 2215/4033] CLJS-1906: Self-host: script/test-self-parity fails Need to bind env/*compiler* as it is used by fn called by check-use-macros-inferring-missing. --- src/main/cljs/cljs/js.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 7abb17836..2d305e690 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -491,7 +491,8 @@ (if (:error res) (cb res) (try - (binding [ana/*analyze-deps* (:*analyze-deps* bound-vars)] + (binding [ana/*analyze-deps* (:*analyze-deps* bound-vars) + env/*compiler* (:*compiler* bound-vars)] (let [ast' (-> rewritten-ast (ana/check-use-macros-inferring-missing env) (ana/check-rename-macros-inferring-missing env))] From 7de568f7c80ce5f2a41222063251f0ecf688c3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 26 Jan 2017 21:26:49 -0800 Subject: [PATCH 2216/4033] CLJS-1909: Self-host: circular dependency when requiring cljs.reader --- src/main/cljs/cljs/reader.clj | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/reader.clj b/src/main/cljs/cljs/reader.clj index 93571df51..16531bcb7 100644 --- a/src/main/cljs/cljs/reader.clj +++ b/src/main/cljs/cljs/reader.clj @@ -7,13 +7,12 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.reader - (:require [cljs.env :as env] - [cljs.analyzer :as ana])) + (:require [cljs.env :as env])) (defmacro add-data-readers [default-readers] (let [data-readers - (->> (get @env/*compiler* ::ana/data-readers) + (->> (get @env/*compiler* :cljs.analyzer/data-readers) (map (fn [[k v]] - [(str k) `(fn [x#] (~(vary-meta v assoc ::ana/no-resolve true) x#))])) + [(str k) `(fn [x#] (~(vary-meta v assoc :cljs.analyzer/no-resolve true) x#))])) (into {}))] `(do (merge ~default-readers ~data-readers)))) From 73cd8aa228721780f1957dea842427c3f1bef1b6 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 27 Jan 2017 02:03:37 -0500 Subject: [PATCH 2217/4033] CLJS-1911: Need to bind Node.js require --- src/main/cljs/cljs/bootstrap_node.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_node.js index 5c0d7924c..6623ceb1d 100644 --- a/src/main/cljs/cljs/bootstrap_node.js +++ b/src/main/cljs/cljs/bootstrap_node.js @@ -82,6 +82,7 @@ function nodeGlobalRequire(file) { var _module = global.module, _exports = global.exports; global.module = undefined; global.exports = undefined; + global.require = require; vm.runInThisContext(fs.readFileSync(file), file); global.exports = _exports; global.module = _module; From 35bcfce2565148cd96b31499a2bbc3bfc2257822 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Jan 2017 10:00:30 -0500 Subject: [PATCH 2218/4033] only assign global.require once when bootstrapping --- src/main/cljs/cljs/bootstrap_node.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_node.js index 6623ceb1d..c5ec1d5ce 100644 --- a/src/main/cljs/cljs/bootstrap_node.js +++ b/src/main/cljs/cljs/bootstrap_node.js @@ -49,7 +49,7 @@ var CLJS_ROOT = "."; * The goog namespace in the global scope. */ global.goog = {}; - +global.require = require; /** * Imports a script using Node's require() API. @@ -82,7 +82,6 @@ function nodeGlobalRequire(file) { var _module = global.module, _exports = global.exports; global.module = undefined; global.exports = undefined; - global.require = require; vm.runInThisContext(fs.readFileSync(file), file); global.exports = _exports; global.module = _module; From 18d1e5792f6c712b6c8e426ac9123ee4b898a374 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Jan 2017 10:20:59 -0500 Subject: [PATCH 2219/4033] revert removal of cljs.nodejs/process cljs.nodejs/require --- src/main/cljs/cljs/nodejs.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/nodejs.cljs b/src/main/cljs/cljs/nodejs.cljs index 0c06471cd..356023609 100644 --- a/src/main/cljs/cljs/nodejs.cljs +++ b/src/main/cljs/cljs/nodejs.cljs @@ -13,8 +13,8 @@ (:refer-clojure :exclude [require])) ; Define namespaced references to Node's externed globals: -; (def require (js* "require")) ; -; (def process (js* "process")) +(def require (js* "require")) +(def process (js* "process")) (defn enable-util-print! [] (set! *print-newline* false) From 7d856d7f04dde5e450462cbf24866011803f47a6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 27 Jan 2017 10:54:00 -0500 Subject: [PATCH 2220/4033] default :closure-module-roots to [] --- src/main/clojure/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 59c9b1362..3d87a3b8e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1915,7 +1915,10 @@ (not (false? (:optimize-constants opts))) (assoc :optimize-constants true)) (nil? (find (:closure-warnings opts) :check-types)) - (assoc-in [:closure-warnings :check-types] :off)))) + (assoc-in [:closure-warnings :check-types] :off) + + (nil? (:closure-module-roots opts)) + (assoc opts :closure-module-roots [])))) (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google From 3371158d41e908b1f938146125399710437fa2c6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 12:01:50 -0500 Subject: [PATCH 2221/4033] fix typo --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3d87a3b8e..ca97f8ddf 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1918,7 +1918,7 @@ (assoc-in [:closure-warnings :check-types] :off) (nil? (:closure-module-roots opts)) - (assoc opts :closure-module-roots [])))) + (assoc :closure-module-roots [])))) (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google From eb7396bb958ab94653b869bebdac155777da7c46 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 12:39:42 -0500 Subject: [PATCH 2222/4033] add file for testing Node.js module-deps calculation --- src/test/node/test.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/test/node/test.js diff --git a/src/test/node/test.js b/src/test/node/test.js new file mode 100644 index 000000000..a4ad1c40b --- /dev/null +++ b/src/test/node/test.js @@ -0,0 +1 @@ +var objectAssign = require("object-assign"); \ No newline at end of file From 9717d49e96a2ab249bd637f9d00542749881f6dd Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 13:12:36 -0500 Subject: [PATCH 2223/4033] wip cljs.build.api for computing node_modules deps --- src/main/cljs/cljs/module_deps.js | 1 + src/main/clojure/cljs/build/api.clj | 63 +++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/main/cljs/cljs/module_deps.js diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js new file mode 100644 index 000000000..888eac644 --- /dev/null +++ b/src/main/cljs/cljs/module_deps.js @@ -0,0 +1 @@ +var path = require("path");var mdeps = require('module-deps');var JSONStream = require('JSONStream');var md = mdeps();md.pipe(JSONStream.stringify()).pipe(process.stdout);md.end({file: path.resolve(path.join(__dirname, 'JS_FILE')) }); \ No newline at end of file diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index ae5fe8947..892925435 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -12,14 +12,17 @@ For example: a build script may need to how to invalidate compiled files so that they will be recompiled." (:refer-clojure :exclude [compile]) - (:require [cljs.util :as util] + (:require [clojure.java.io :as io] + [clojure.string :as string] + [clojure.data.json :as json] + [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.closure :as closure] - [cljs.js-deps :as js-deps] - [clojure.java.io :as io]) - (:import java.io.File)) + [cljs.js-deps :as js-deps]) + (:import [java.io File] + [java.lang ProcessBuilder Process])) ;; ============================================================================= ;; Useful Utilities @@ -213,8 +216,60 @@ (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/watch source opts compiler-env stop)))) +(defn add-package-jsons + "EXPERIMENTAL: see node-module-deps" + [deps] + (let [checked (atom #{})] + (reduce + (fn [ret {:keys [file] :as dep}] + (let [f (.getParentFile (io/file file)) + path (.getAbsolutePath f)] + (if-not (contains? @checked path) + (let [f' (io/file f "package.json")] + (swap! checked conj path) + (if (.exists f') + (conj ret dep + {:file (.getAbsolutePath f') + :module-type :commonjs}) + (conj ret dep))) + ret))) + [] deps))) + +(defn node-module-deps + "EXPERIMENTAL: return the foreign libs entries as computed by running + the module-deps package on the supplied JavaScript entry point. Assumes + that the module-deps & JSONStream NPM packages are either locally or + globally installed." + [entry] + (let [code (string/replace + (slurp (io/resource "cljs/module_deps.js")) + "JS_FILE" entry) + proc (-> (ProcessBuilder. + (into-array + ["node" "--eval" (str code)])) + .start) + err (.waitFor proc)] + (if (zero? err) + (let [is (.getInputStream proc)] + (into [] + (map (fn [{:strs [file]}] file + {:file file :module-type :commonjs})) + (butlast (json/read-str (slurp is))))) + []))) + (comment + (add-package-jsons (node-module-deps "src/test/node/test.js")) + ) +(defn node-inputs + "EXPERIMENTAL: return the foreign libs entries as computed by running + the module-deps package on the supplied JavaScript entry points. Assumes + that the module-deps & JSONStream NPM packages are either locally or + globally installed." + [entries] + ) + +(comment (def test-cenv (atom {})) (def test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) From 2e95d95974385623b777dd5c10871824b08634a0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 13:18:43 -0500 Subject: [PATCH 2224/4033] wip --- src/main/clojure/cljs/build/api.clj | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 892925435..7a5040721 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -240,10 +240,10 @@ the module-deps package on the supplied JavaScript entry point. Assumes that the module-deps & JSONStream NPM packages are either locally or globally installed." - [entry] + [{:keys [file]}] (let [code (string/replace (slurp (io/resource "cljs/module_deps.js")) - "JS_FILE" entry) + "JS_FILE" file) proc (-> (ProcessBuilder. (into-array ["node" "--eval" (str code)])) @@ -258,7 +258,9 @@ []))) (comment - (add-package-jsons (node-module-deps "src/test/node/test.js")) + (add-package-jsons + (node-module-deps + {:file "src/test/node/test.js"})) ) (defn node-inputs @@ -267,6 +269,11 @@ that the module-deps & JSONStream NPM packages are either locally or globally installed." [entries] + (add-package-jsons (vec (mapcat node-module-deps entries)))) + +(comment + (node-inputs + [{:file "src/test/node/test.js"}]) ) (comment From be15b573e24f37db5cc7f4bd20f836d5f5685703 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 13:28:53 -0500 Subject: [PATCH 2225/4033] add super basic error reporting --- src/main/clojure/cljs/build/api.clj | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 7a5040721..e36c510e9 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -252,10 +252,13 @@ (if (zero? err) (let [is (.getInputStream proc)] (into [] - (map (fn [{:strs [file]}] file - {:file file :module-type :commonjs})) - (butlast (json/read-str (slurp is))))) - []))) + (map (fn [{:strs [file]}] file + {:file file :module-type :commonjs})) + (butlast (json/read-str (slurp is))))) + (let [es (.getErrorStream proc)] + (binding [*out* *err*] + (println (slurp es))) + [])))) (comment (add-package-jsons From c435427c75751d7663319bc1d2ef72f5b21ae47c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 14:29:07 -0500 Subject: [PATCH 2226/4033] add some error handling --- src/main/clojure/cljs/build/api.clj | 37 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index e36c510e9..75d89151d 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -22,7 +22,8 @@ [cljs.closure :as closure] [cljs.js-deps :as js-deps]) (:import [java.io File] - [java.lang ProcessBuilder Process])) + [java.lang ProcessBuilder Process] + (java.util.concurrent TimeUnit))) ;; ============================================================================= ;; Useful Utilities @@ -241,23 +242,27 @@ that the module-deps & JSONStream NPM packages are either locally or globally installed." [{:keys [file]}] - (let [code (string/replace - (slurp (io/resource "cljs/module_deps.js")) - "JS_FILE" file) - proc (-> (ProcessBuilder. - (into-array - ["node" "--eval" (str code)])) - .start) - err (.waitFor proc)] - (if (zero? err) + (let [code (string/replace + (slurp (io/resource "cljs/module_deps.js")) + "JS_FILE" file) + proc (-> (ProcessBuilder. + (into-array + ["node" "--eval" (str code)])) + .start) + timeout? (.waitFor proc 10 TimeUnit/SECONDS)] + (when timeout? + (println "Node.js process timed out")) + (if (and (not (.isAlive proc)) + (zero? (.exitValue proc))) (let [is (.getInputStream proc)] (into [] - (map (fn [{:strs [file]}] file - {:file file :module-type :commonjs})) - (butlast (json/read-str (slurp is))))) - (let [es (.getErrorStream proc)] - (binding [*out* *err*] - (println (slurp es))) + (map (fn [{:strs [file]}] file + {:file file :module-type :commonjs})) + (butlast (json/read-str (slurp is))))) + (do + (when-not (.isAlive proc) + (let [es (.getErrorStream proc)] + (println (slurp es)))) [])))) (comment From 3a29fa4aaf81e6433365b7e89f6c2aa848a772f9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 15:07:35 -0500 Subject: [PATCH 2227/4033] avoid blocking on Node.js process doing I/O, tweak module_deps.js --- src/main/cljs/cljs/module_deps.js | 2 +- src/main/clojure/cljs/build/api.clj | 68 +++++++++++++++++++---------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 888eac644..bee775a67 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -1 +1 @@ -var path = require("path");var mdeps = require('module-deps');var JSONStream = require('JSONStream');var md = mdeps();md.pipe(JSONStream.stringify()).pipe(process.stdout);md.end({file: path.resolve(path.join(__dirname, 'JS_FILE')) }); \ No newline at end of file +var path = require('path');var mdeps = require('module-deps');var JSONStream = require('JSONStream');var md = mdeps();md.pipe(JSONStream.stringify()).pipe(process.stdout);md.end({file: path.resolve(path.join(__dirname, 'JS_FILE')) }); \ No newline at end of file diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 75d89151d..8a244e8ae 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -21,9 +21,11 @@ [cljs.compiler :as comp] [cljs.closure :as closure] [cljs.js-deps :as js-deps]) - (:import [java.io File] - [java.lang ProcessBuilder Process] - (java.util.concurrent TimeUnit))) + (:import [java.io + File StringWriter + BufferedReader + Writer InputStreamReader IOException] + [java.lang ProcessBuilder])) ;; ============================================================================= ;; Useful Utilities @@ -236,33 +238,55 @@ ret))) [] deps))) +(defn- alive? [proc] + (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) + +(defn- pipe [^Process proc in ^Writer out] + ;; we really do want system-default encoding here + (with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)] + (loop [buf (char-array 1024)] + (when (alive? proc) + (try + (let [len (.read in buf)] + (when-not (neg? len) + (.write out buf 0 len) + (.flush out))) + (catch IOException e + (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed"))) + (.printStackTrace e *err*)))) + (recur buf))))) + (defn node-module-deps "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry point. Assumes that the module-deps & JSONStream NPM packages are either locally or globally installed." [{:keys [file]}] - (let [code (string/replace - (slurp (io/resource "cljs/module_deps.js")) - "JS_FILE" file) - proc (-> (ProcessBuilder. - (into-array - ["node" "--eval" (str code)])) - .start) - timeout? (.waitFor proc 10 TimeUnit/SECONDS)] - (when timeout? - (println "Node.js process timed out")) - (if (and (not (.isAlive proc)) - (zero? (.exitValue proc))) - (let [is (.getInputStream proc)] - (into [] - (map (fn [{:strs [file]}] file - {:file file :module-type :commonjs})) - (butlast (json/read-str (slurp is))))) + (let [code (string/replace + (slurp (io/resource "cljs/module_deps.js")) + "JS_FILE" file) + proc (-> (ProcessBuilder. + ["node" "--eval" code]) + .start) + is (.getInputStream proc) + iw (StringWriter. (* 16 1024 1024)) + es (.getErrorStream proc) + ew (StringWriter. (* 1024 1024)) + _ (do (.start + (Thread. + (bound-fn [] (pipe proc is iw)))) + (.start + (Thread. + (bound-fn [] (pipe proc es ew))))) + err (.waitFor proc)] + (if (zero? err) + (into [] + (map (fn [{:strs [file]}] file + {:file file :module-type :commonjs})) + (butlast (json/read-str (str iw)))) (do (when-not (.isAlive proc) - (let [es (.getErrorStream proc)] - (println (slurp es)))) + (println (str ew))) [])))) (comment From 933678eac311ee87ef388b289c1f9ff5e101e1f5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 15:35:37 -0500 Subject: [PATCH 2228/4033] fix Node module stuff --- src/main/clojure/cljs/build/api.clj | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 8a244e8ae..fd917f0fb 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -235,7 +235,7 @@ {:file (.getAbsolutePath f') :module-type :commonjs}) (conj ret dep))) - ret))) + (conj ret dep)))) [] deps))) (defn- alive? [proc] @@ -264,7 +264,9 @@ [{:keys [file]}] (let [code (string/replace (slurp (io/resource "cljs/module_deps.js")) - "JS_FILE" file) + "JS_FILE" + (string/replace file + (System/getProperty "user.dir") "")) proc (-> (ProcessBuilder. ["node" "--eval" code]) .start) @@ -290,9 +292,12 @@ [])))) (comment + (node-module-deps + {:file (.getAbsolutePath (io/file "src/test/node/test.js"))}) + (add-package-jsons (node-module-deps - {:file "src/test/node/test.js"})) + {:file (.getAbsolutePath (io/file "src/test/node/test.js"))})) ) (defn node-inputs From f15b51a592ca60c00bfac61bfea9346ac7cb357c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 15:43:41 -0500 Subject: [PATCH 2229/4033] we need to call distinct --- src/main/clojure/cljs/build/api.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index fd917f0fb..928ab6ae0 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -306,7 +306,8 @@ that the module-deps & JSONStream NPM packages are either locally or globally installed." [entries] - (add-package-jsons (vec (mapcat node-module-deps entries)))) + (add-package-jsons + (vec (distinct (mapcat node-module-deps entries))))) (comment (node-inputs From 40a00edc30eb7d3b5a47be4f7c2cfdbf491eb804 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 16:33:27 -0500 Subject: [PATCH 2230/4033] 1.9.454 --- README.md | 6 ++-- changes.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5a1252ad6..6363fc6ad 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.293 +Latest stable release: 1.9.454 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.293"] +[org.clojure/clojurescript "1.9.454"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.293 org.clojure clojurescript - 1.9.293 + 1.9.454 ``` diff --git a/changes.md b/changes.md index 2965bf2eb..9d08b910b 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,86 @@ +## 1.9.454 + +### Enhancements +* Enhanced JavaScript module support +* Support Node resolution for CommonJS modules +* Externs inference +* Performance enhancements +* CLJS-1835: REPL load special fn +* CLJS-1194: Support for `data_readers.cljc` + +### Changes +* expose :closure-module-roots option +* bump Closure Compiler dep +* Under Node.js don't need require entries in the goog.addDependency calls in cljs_deps.js +* do not throw on circular dependencies between Google Closure JS libs +* str macro should call str/1 function directly, added str benchmark +* CLJS-1718: Foreign lib files should be placed in a relative location +* CLJS-1858: Should allow `:cache-analysis true` and `cache-analysis-format nil` +* CLJS-1616: Self-host: improve documentation for compile-str +* CLJS-1643: Emit more informative error when emitting a type which has no emit multimethod case +* CLJS-1816: Basic timing info in verbose output +* add support for emitting inferred externs file +* add cljs.analyzer/analyze-form-seq +* CLJS-1666: Flag to optionally disable transit analysis cache encoding +* Provide more descriptive error message when invalid libspec detected +* CLJS-1768: cljs.spec perf tweaks +* CLJS-1842: Remove analyzer `:merge` hack for REPLs +* CLJS-1839: Relax the constraint that `new` and dot forms must be passed a symbol +* default to :ecmascript3 if :language-out not specified for :es6 module +* respect :language-out when processing ES6 modules +* default to :ecmascript3 if :language-out not specified for :es6 module +* inline some? + +### Fixes +* CLJS-1911: Need to bind Node.js require +* CLJS-1909: Self-host: circular dependency when requiring cljs.reader +* CLJS-1906: Self-host: script/test-self-parity fails +* CLJS-1903: Remove anonymous vars from dir and apropos output +* CLJS-1897: Too many externs generated +* CLJS-1895: Externs inference needs to support user supplied externs +* CLJS-1873: Self-host: Unit tests fail owing to test.check dep +* CLJS-1874: Self-host: :fn-var true for macros +* CLJS-1877: :foreign-libs entries should be allowed to specify directories along with individual files +* CLJS-1890: s/form for s/nilable in cljs.spec does not match clojure.spec +* CLJS-1811: Can't compose cljs.spec.test.instrument (or cljs.spec.test.check) with cljs.spec.test.enumerate-namespace +* CLJS-1894: Unnecessary analysis of core.cljs on first compile +* CLJS-1893: Unnecessary analysis of core.cljs +* CLJS-1892: Dependencies in JARs are analyzed every time even if an analysis cache file exists +* CLJS-1887: add :watch-error-fn option +* CLJS-1883 Foreign libs can't be found on Node.js +* CLJS-1882 Fix constant table sort order when using :modules +* CLJS-1853: var metadata in compiled output +* CLJS-1878: prefer `some?` over `(not (nil? %))` in analyzer +* CLJS-1880: missing ^boolean on some hasNext calls +* CLJS-1875 Difference in seqable? between CLJ & CLJS +* CLJS-1829: get does not return not-found on negative indexes +* cljs.spec.test/unstrument shouldn't return the names of vars that weren't instrumented in the first place. Fixes CLJS-1812 +* CLJS-1786: Add knob for controlling printing of namespaced maps +* CLJS-1836: nth doesn't throw for IndexedSeqs +* CLJS-1870: Quoted specs check in require macro symbols +* CLJS-1869: Regression importing goog.Uri +* Fix CLJS-1653 regression +* CLJS-1860: Resolve JS modules referred by their fully-qualified namespace +* CLJS-1861: Use usr/bin/env in build scripts for portability +* CLJS-1857: Fix self-host tests +* CLJS-1855: Subvec should implement IIterable +* CLJS-1856: Self-host: load-deps doesn't delegate to itself +* CLJS-1651: Self-host: Cannot replace core macro-function +* CLJS-1848: Analyzer can't find JS modules during macro-expansion +* CLJS-1851: Only output JS module processing time when `:compiler-stats` is true +* CLJS-1850: *unchecked-if* not declared ^:dynamic warning after commit a732f0 +* CLJS-1849: Self-host: regression introduced by CLJS-1794 +* CLJS-1844: port over Maria Geller's externs file parsing code +* CLJS-1845: Assoc on subvec should throw if out of bounds +* CLJS-1847: REPL should recognize `clojure.core/load` +* CLJS-1745: refer-clojure doesn't pull in previously excluded vars +* CLJS-1794: incomplete alias created for namespace cljs.spec warning under advanced compilation +* CLJS-1834: REPL regression, require of ns from the ns itself errors out in circular reference +* fix ns aliasing regression for JS namespaces +* CLJS-1837: Port halt-when over from Clojure +* CLJS-1820: "No such namespace" warning when referring to JS module namespace without using alias +* CLJS-1828: Add `:rename` to `require`'s docstring + ## 1.9.293 ### Enhancements From e21db0cd33dd91be5ab46167a76a71df338fd261 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 17:06:54 -0500 Subject: [PATCH 2231/4033] try aliasing mvn --- script/build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/build b/script/build index 596b8ca81..d4c098e1f 100755 --- a/script/build +++ b/script/build @@ -8,6 +8,8 @@ set -ex cd `dirname $0`/.. rm -rf target +alias mvn=/var/lib/jenkins/tools/Maven_3.0.2/bin/mvn + POM_TEMPLATE="pom.template.xml" POM_FILE="pom.xml" From 2c787ebfe9073fe3bab9aba1b91ecf8a20d61e91 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 17:11:44 -0500 Subject: [PATCH 2232/4033] revert last commit --- script/build | 2 -- 1 file changed, 2 deletions(-) diff --git a/script/build b/script/build index d4c098e1f..596b8ca81 100755 --- a/script/build +++ b/script/build @@ -8,8 +8,6 @@ set -ex cd `dirname $0`/.. rm -rf target -alias mvn=/var/lib/jenkins/tools/Maven_3.0.2/bin/mvn - POM_TEMPLATE="pom.template.xml" POM_FILE="pom.xml" From 0a990622df563274f10922130b111f04cea01334 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Jan 2017 17:15:09 -0500 Subject: [PATCH 2233/4033] fix versions --- README.md | 6 +++--- changes.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6363fc6ad..6fc4cbfa4 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.454 +Latest stable release: 1.9.456 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.454"] +[org.clojure/clojurescript "1.9.456"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.454 org.clojure clojurescript - 1.9.454 + 1.9.456 ``` diff --git a/changes.md b/changes.md index 9d08b910b..0bdbdb7e0 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.9.454 +## 1.9.456 ### Enhancements * Enhanced JavaScript module support From 2f8a1529955acc943ac8082ab5848b2cba54bc4d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 28 Jan 2017 12:01:59 -0500 Subject: [PATCH 2234/4033] CLJS-1915: cljs.test: Index out of bounds for stack element w/o line/column --- src/main/cljs/cljs/test.cljs | 10 ++++++++-- src/test/cljs/cljs/test_test.cljs | 29 +++++++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 6 ++++-- src/test/self/self_parity/test.cljs | 6 ++++-- 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 src/test/cljs/cljs/test_test.cljs diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index 73926a65c..088df3f5d 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -364,10 +364,16 @@ ;; File, Line, and Column Helpers (defn js-line-and-column [stack-element] + "Returns a 2-element vector containing the line and + column encoded at the end of a stack element string. + A line or column will be represented as NaN if not + parsesable." (let [parts (.split stack-element ":") cnt (count parts)] - [(js/parseInt (nth parts (- cnt 2)) 10) - (js/parseInt (nth parts (dec cnt)) 10)])) + (if (> cnt 1) + [(js/parseInt (nth parts (- cnt 2)) 10) + (js/parseInt (nth parts (dec cnt)) 10)] + [NaN NaN]))) (defn js-filename [stack-element] (first (.split (last (.split stack-element "/out/")) ":"))) diff --git a/src/test/cljs/cljs/test_test.cljs b/src/test/cljs/cljs/test_test.cljs new file mode 100644 index 000000000..4885ca2d4 --- /dev/null +++ b/src/test/cljs/cljs/test_test.cljs @@ -0,0 +1,29 @@ +(ns cljs.test-test + (:require [cljs.test :refer-macros [deftest testing is] :as ct] + [clojure.string :as s] + [clojure.set :as set])) + +(defn- nan? + [x] + (and (number? x) + (js/isNaN x))) + +(deftest js-line-and-column-test + (is (= [2 3] (ct/js-line-and-column "foo:bar:2:3"))) + (is (= [2 3] (ct/js-line-and-column "foo:2:3"))) + (is (= [2 3] (ct/js-line-and-column "2:3"))) + (let [[line column] (ct/js-line-and-column "foo:bogus:3")] + (is (nan? line)) + (is (== 3 column))) + (let [[line column] (ct/js-line-and-column "foo:2:bogus")] + (is (== 2 line)) + (is (nan? column))) + (let [[line column] (ct/js-line-and-column "foo:bogus:bogus")] + (is (nan? line)) + (is (nan? column))) + (let [[line column] (ct/js-line-and-column "foo:3")] + (is (nan? line)) + (is (== 3 column))) + (let [[line column] (ct/js-line-and-column "foo")] + (is (nan? line)) + (is (nan? column)))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 9b1592165..cddc5bf94 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -38,7 +38,8 @@ [cljs.clojure-alias-test] [cljs.hash-map-test] [cljs.predicates-test] - [cljs.tagged-literals-test])) + [cljs.tagged-literals-test] + [cljs.test-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -74,4 +75,5 @@ 'cljs.pprint-test 'cljs.predicates-test 'cljs.syntax-quote-test - 'cljs.tagged-literals-test) + 'cljs.tagged-literals-test + 'cljs.test-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index d424d0954..6a62a4dda 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -280,7 +280,8 @@ [cljs.clojure-alias-test] [cljs.hash-map-test] [cljs.syntax-quote-test] - [cljs.predicates-test])) + [cljs.predicates-test] + [cljs.test-test])) (fn [{:keys [value error]}] (if error (prn error) @@ -315,7 +316,8 @@ 'cljs.clojure-alias-test 'cljs.hash-map-test 'cljs.syntax-quote-test - 'cljs.predicates-test) + 'cljs.predicates-test + 'cljs.test-test) (fn [{:keys [value error]}] (when error (prn error))))))))) From ba205f0d091cd541c241a5d0e9419cbf3e4e8808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 28 Jan 2017 11:31:39 -0800 Subject: [PATCH 2235/4033] CLJS-1916: __dirname and __filename are not defined when compiling for Node.js with optimizations :none --- src/main/cljs/cljs/bootstrap_node.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_node.js index c5ec1d5ce..e1b3b4f2c 100644 --- a/src/main/cljs/cljs/bootstrap_node.js +++ b/src/main/cljs/cljs/bootstrap_node.js @@ -82,6 +82,8 @@ function nodeGlobalRequire(file) { var _module = global.module, _exports = global.exports; global.module = undefined; global.exports = undefined; + global.__dirname = file.substring(0, file.lastIndexOf('/')); + global.__filename = file; vm.runInThisContext(fs.readFileSync(file), file); global.exports = _exports; global.module = _module; From 772e59a3611d5c48dadb17587a1dd74a003751d0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 29 Jan 2017 19:25:08 -0500 Subject: [PATCH 2236/4033] reset __dirname & __filename after script eval --- src/main/cljs/cljs/bootstrap_node.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_node.js index e1b3b4f2c..e955486d4 100644 --- a/src/main/cljs/cljs/bootstrap_node.js +++ b/src/main/cljs/cljs/bootstrap_node.js @@ -85,6 +85,8 @@ function nodeGlobalRequire(file) { global.__dirname = file.substring(0, file.lastIndexOf('/')); global.__filename = file; vm.runInThisContext(fs.readFileSync(file), file); + global.__dirname = undefined; + global.__filename = undefined; global.exports = _exports; global.module = _module; } From b373fe9c73c1d3eba282cf3e2f07b7e2754675b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 30 Jan 2017 13:39:25 -0800 Subject: [PATCH 2237/4033] CLJS-1920: cljs.build.api/node-inputs: package.json files are only added if module entries are top-level --- src/main/cljs/cljs/module_deps.js | 27 +++++++++++++++++++++- src/main/clojure/cljs/build/api.clj | 35 +++++------------------------ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index bee775a67..a3dbf96d9 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -1 +1,26 @@ -var path = require('path');var mdeps = require('module-deps');var JSONStream = require('JSONStream');var md = mdeps();md.pipe(JSONStream.stringify()).pipe(process.stdout);md.end({file: path.resolve(path.join(__dirname, 'JS_FILE')) }); \ No newline at end of file +var path = require('path'); +var mdeps = require('module-deps'); + +var md = mdeps({}); +var deps_files = []; + +md.on('package', function (pkg) { + // we don't want to include the package.json for users' projects + if (/node_modules/.test(pkg.__dirname)) { + deps_files.push({file: path.join(pkg.__dirname, 'package.json')}); + } +}); + +md.on('file', function(file) { + deps_files.push({file: file}); +}); + +md.on('end', function() { + process.stdout.write(JSON.stringify(deps_files)); +}); + +md.end({ + file: path.resolve(path.join(__dirname, 'JS_FILE')) +}); + +md.resume(); diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 928ab6ae0..5faf48bd5 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -219,25 +219,6 @@ (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/watch source opts compiler-env stop)))) -(defn add-package-jsons - "EXPERIMENTAL: see node-module-deps" - [deps] - (let [checked (atom #{})] - (reduce - (fn [ret {:keys [file] :as dep}] - (let [f (.getParentFile (io/file file)) - path (.getAbsolutePath f)] - (if-not (contains? @checked path) - (let [f' (io/file f "package.json")] - (swap! checked conj path) - (if (.exists f') - (conj ret dep - {:file (.getAbsolutePath f') - :module-type :commonjs}) - (conj ret dep))) - (conj ret dep)))) - [] deps))) - (defn- alive? [proc] (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) @@ -259,8 +240,7 @@ (defn node-module-deps "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry point. Assumes - that the module-deps & JSONStream NPM packages are either locally or - globally installed." + that the module-deps NPM package is either locally or globally installed." [{:keys [file]}] (let [code (string/replace (slurp (io/resource "cljs/module_deps.js")) @@ -285,7 +265,7 @@ (into [] (map (fn [{:strs [file]}] file {:file file :module-type :commonjs})) - (butlast (json/read-str (str iw)))) + (next (json/read-str (str iw)))) (do (when-not (.isAlive proc) (println (str ew))) @@ -295,19 +275,16 @@ (node-module-deps {:file (.getAbsolutePath (io/file "src/test/node/test.js"))}) - (add-package-jsons - (node-module-deps - {:file (.getAbsolutePath (io/file "src/test/node/test.js"))})) + (node-module-deps + {:file (.getAbsolutePath (io/file "src/test/node/test.js"))}) ) (defn node-inputs "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry points. Assumes - that the module-deps & JSONStream NPM packages are either locally or - globally installed." + that the module-deps NPM packages is either locally or globally installed." [entries] - (add-package-jsons - (vec (distinct (mapcat node-module-deps entries))))) + (into [] (distinct (mapcat node-module-deps entries)))) (comment (node-inputs From 8c9cde94cb05b3d5446c2e6182b55d6f29893031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 1 Feb 2017 13:52:37 -0800 Subject: [PATCH 2238/4033] CLJS-1925: Use of undeclared Var cljs.user/RegExp when extending protocol for RegExp This is a regression caused by an improper fix to CLJS-1607. This patch reverts the fix in CLJS-1607, adds a test for it that doesn't rely on internal details, and properly fixes the issue by not `dissoc`ing locals from env in `cljs.core/resolve-var`, because `specify!` creates a local binding via `let`. --- src/main/clojure/cljs/compiler.cljc | 2 +- src/main/clojure/cljs/core.cljc | 4 +- src/test/clojure/cljs/compiler_tests.clj | 90 +++++++++++++++--------- 3 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index cceacbd7e..12b3e0850 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -941,7 +941,7 @@ ;; ignore new type hints for now - David (and (not (set? tag)) (not ('#{any clj clj-or-nil clj-nil number string boolean function object array} tag)) - (when-let [ps (:protocols (ana/resolve-existing-var env (symbol (name tag))))] + (when-let [ps (:protocols (ana/resolve-existing-var env tag))] (ps protocol))))))) opt-not? (and (= (:name info) 'cljs.core/not) (= (ana/infer-tag env (first (:args expr))) 'boolean)) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a73f8d085..f8de03501 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1351,9 +1351,7 @@ (cljs.analyzer/warning :undeclared-protocol-symbol env {:protocol p}))))) (core/defn- resolve-var [env sym] - (core/let [ret (core/-> (dissoc env :locals) - (cljs.analyzer/resolve-var sym) - :name)] + (core/let [ret (:name (cljs.analyzer/resolve-var env sym))] (core/assert ret (core/str "Can't resolve: " sym)) ret)) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 5b02f5fc3..ba1eb90ee 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -155,44 +155,66 @@ `(cljs.user/foo ~(tags/->JSValue [])) '(cljs.user/foo (make-array 0)))))) -;; CLJS-1607 -;; Commented out this test - too hard coded to unnecessary details - David - -#_(deftest test-cljs-1607 - (let [define-Foo #(assoc-in % [::ana/namespaces 'cljs.user :defs 'Foo] - {:ns 'cljs.user - :name 'cljs.user/Foo - :protocol-symbol true - :protocol-info {:methods '{foo [[this]]}} - :protocol 'cljs.user/Foo}) - define-foo #(assoc-in % [::ana/namespaces 'cljs.user :defs 'foo] - {:ns 'cljs.user - :name 'cljs.user/foo - :fn-var true - :method-params '([x]) - :protocol 'cljs.user/Foo}) - aenv-with-foo (-> aenv define-foo define-Foo) - cenv-with-foo (-> @cenv define-foo define-Foo)] - (binding [ana/*cljs-static-fns* true] - (are [form] - (empty? - (capture-warnings - (env/with-compiler-env (atom cenv-with-foo) - (with-out-str - (comp/emit - (ana/analyze aenv-with-foo form)))))) - '(specify! [] - cljs.user/Foo - (cljs.user/foo [this] - :none) - Object - (bar [this] - (cljs.user/foo this))))))) - (deftest test-cljs-1643 (is (thrown-with-msg? Exception #"is not a valid ClojureScript constant." (comp/emit-constant clojure.core/inc)))) +(def test-cljs-1925-code + '(do + (defprotocol X + (x [x])) + + (defprotocol Y + (y [y])) + + (extend-protocol X + js/RegExp + (x [x] + (y x))) + + (extend-protocol Y + js/RegExp + (y [y] + :y)))) + +(def specify-test-code + '(do + (defprotocol IBug + (bug [this other] "A sample protocol")) + + (defn MyBug []) + (specify! (.-prototype MyBug) + IBug + (bug [this other] + "bug") + Object + (foo [this] + (bug this 3))))) + +(deftest test-cljs-1925 + (let [opts {:static-fns true} + cenv (env/default-compiler-env opts)] + (is (= [] (binding [ana/*unchecked-if* false + ana/*cljs-static-fns* true] + (capture-warnings + (env/with-compiler-env cenv + (with-out-str + (comp/emit + (comp/with-core-cljs + opts + (fn [] (ana/analyze aenv test-cljs-1925-code nil opts))))))))))) + (let [opts {:static-fns true} + cenv (env/default-compiler-env opts)] + (is (= [] (binding [ana/*unchecked-if* false + ana/*cljs-static-fns* true] + (capture-warnings + (env/with-compiler-env cenv + (with-out-str + (comp/emit + (comp/with-core-cljs + opts + (fn [] (ana/analyze aenv specify-test-code nil opts)))))))))))) + ;; CLJS-1225 (comment From c28e41ae6462fdf3931ec9458d8739bc493f54ba Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 2 Feb 2017 22:54:12 -0500 Subject: [PATCH 2239/4033] CLJS-1831: Self-host: Improperly munge ns names In self-hosted, munge namespaces using comp/munge, just as is done in regular ClojureScript. Add unit tests for both regular and self-hosted that exercise the munging feature. --- src/main/cljs/cljs/js.cljs | 4 ++-- src/test/cljs/static/core_test.cljs | 9 +++++++++ src/test/cljs/test_runner.cljs | 6 ++++-- src/test/self/self_parity/test.cljs | 6 ++++-- 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 src/test/cljs/static/core_test.cljs diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2d305e690..fb780745e 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -663,7 +663,7 @@ (fn [res] (if (:error res) (cb res) - (let [src (str "goog.provide(\"" (munge (:name ast)) "\")")] + (let [src (str "goog.provide(\"" (comp/munge (:name ast)) "\")")] (cb {:value (*eval-fn* {:source src})}))))) (let [src (with-out-str (comp/emit ast))] (cb {:value (*eval-fn* {:source src})}))))))))) @@ -873,7 +873,7 @@ (if (#{:ns :ns*} (:op ast)) (do (.append sb - (with-out-str (comp/emitln (str "goog.provide(\"" (munge (:name ast)) "\");")))) + (with-out-str (comp/emitln (str "goog.provide(\"" (comp/munge (:name ast)) "\");")))) (ns-side-effects true bound-vars aenv ast opts (fn [res] (if (:error res) diff --git a/src/test/cljs/static/core_test.cljs b/src/test/cljs/static/core_test.cljs new file mode 100644 index 000000000..8b3c45b3c --- /dev/null +++ b/src/test/cljs/static/core_test.cljs @@ -0,0 +1,9 @@ +(ns static.core-test + (:require [cljs.test :refer-macros [deftest is]])) + +; The purpose of this test namespace is to ensure +; that the use of a reserved JavaScript keyword +; (`static`) in the namespace is handled properly. + +(deftest foo-test + (is (= 1 1))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index cddc5bf94..e5b311825 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -39,7 +39,8 @@ [cljs.hash-map-test] [cljs.predicates-test] [cljs.tagged-literals-test] - [cljs.test-test])) + [cljs.test-test] + [static.core-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -76,4 +77,5 @@ 'cljs.predicates-test 'cljs.syntax-quote-test 'cljs.tagged-literals-test - 'cljs.test-test) + 'cljs.test-test + 'static.core-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 6a62a4dda..0402b641f 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -281,7 +281,8 @@ [cljs.hash-map-test] [cljs.syntax-quote-test] [cljs.predicates-test] - [cljs.test-test])) + [cljs.test-test] + [static.core-test])) (fn [{:keys [value error]}] (if error (prn error) @@ -317,7 +318,8 @@ 'cljs.hash-map-test 'cljs.syntax-quote-test 'cljs.predicates-test - 'cljs.test-test) + 'cljs.test-test + 'static.core-test) (fn [{:keys [value error]}] (when error (prn error))))))))) From f7f2142d44a2bddbb74bdabaf45ba032ad452c53 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Tue, 31 Jan 2017 03:02:50 +0200 Subject: [PATCH 2240/4033] CLJS-1922: Use :file as relative output path for foreign-libs --- src/main/clojure/cljs/closure.clj | 8 +++++++- src/main/clojure/cljs/js_deps.cljc | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ca97f8ddf..84386d296 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -319,6 +319,7 @@ (-foreign? [this] false) (-closure-lib? [this] false) (-url [this] nil) + (-relative-path [this] nil) (-provides [this] (:provides (deps/parse-js-ns (string/split-lines this)))) (-requires [this] (:requires (deps/parse-js-ns (string/split-lines this)))) (-source [this] this) @@ -328,6 +329,9 @@ (-closure-lib? [this] (:closure-lib this)) (-url [this] (or (:url this) (deps/to-url (:file this)))) + (-relative-path [this] (let [file (io/as-file (:file this))] + (if (and file (not (.isAbsolute file))) + (:file this)))) (-provides [this] (map name (:provides this))) (-requires [this] (map name (:requires this))) (-source [this] (if-let [s (:source this)] @@ -339,6 +343,7 @@ (-foreign? [this] foreign) (-closure-lib? [this] (:closure-lib this)) (-url [this] url) + (-relative-path [this] nil) (-provides [this] provides) (-requires [this] requires) (-source [this] @@ -1500,7 +1505,8 @@ url (cond (deps/-closure-lib? js) (lib-rel-path js) - (deps/-foreign? js) (util/relative-name url) + (deps/-foreign? js) (or (deps/-relative-path js) + (util/relative-name url)) :else (path-from-jarfile url)) (string? js) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 804fa08d9..49ccb5839 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -123,6 +123,7 @@ case." library") (-url [this] "The URL where this JavaScript is located. Returns nil when JavaScript exists in memory only.") + (-relative-path [this] "Relative path for this JavaScript.") (-provides [this] "A list of namespaces that this JavaScript provides.") (-requires [this] "A list of namespaces that this JavaScript requires.") (-source [this] "The JavaScript source string.")) From 74f342c3f01e448852b80f0d87675710341f5b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 5 Feb 2017 20:06:39 -0800 Subject: [PATCH 2241/4033] CLJS-1795: Support more options in the `:closure-warnings` compiler option --- src/main/clojure/cljs/closure.clj | 38 ++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 84386d296..248e27177 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -97,29 +97,65 @@ (def warning-types {:access-controls DiagnosticGroups/ACCESS_CONTROLS + :access-controls-const DiagnosticGroups/ACCESS_CONTROLS_CONST :ambiguous-function-decl DiagnosticGroups/AMBIGUOUS_FUNCTION_DECL - :debugger-statement-present DiagnosticGroups/DEBUGGER_STATEMENT_PRESENT + :analyzer-checks DiagnosticGroups/ANALYZER_CHECKS + :check-eventful-object-disposal DiagnosticGroups/CHECK_EVENTFUL_OBJECT_DISPOSAL :check-regexp DiagnosticGroups/CHECK_REGEXP :check-types DiagnosticGroups/CHECK_TYPES :check-useless-code DiagnosticGroups/CHECK_USELESS_CODE :check-variables DiagnosticGroups/CHECK_VARIABLES + :closure-dep-method-usage-checks DiagnosticGroups/CLOSURE_DEP_METHOD_USAGE_CHECKS + :common-js-module-load DiagnosticGroups/COMMON_JS_MODULE_LOAD + :conformance-violations DiagnosticGroups/CONFORMANCE_VIOLATIONS :const DiagnosticGroups/CONST :constant-property DiagnosticGroups/CONSTANT_PROPERTY + :debugger-statement-present DiagnosticGroups/DEBUGGER_STATEMENT_PRESENT :deprecated DiagnosticGroups/DEPRECATED + :deprecated-annotations DiagnosticGroups/DEPRECATED_ANNOTATIONS :duplicate-message DiagnosticGroups/DUPLICATE_MESSAGE + :duplicate-vars DiagnosticGroups/DUPLICATE_VARS + :es3 DiagnosticGroups/ES3 :es5-strict DiagnosticGroups/ES5_STRICT + :es5-strict-uncommon DiagnosticGroups/ES5_STRICT_UNCOMMON + :es5-strict-reflection DiagnosticGroups/ES5_STRICT_REFLECTION :externs-validation DiagnosticGroups/EXTERNS_VALIDATION + :extra-require DiagnosticGroups/EXTRA_REQUIRE :fileoverview-jsdoc DiagnosticGroups/FILEOVERVIEW_JSDOC + :function-params DiagnosticGroups/FUNCTION_PARAMS :global-this DiagnosticGroups/GLOBAL_THIS + :inferred-const-checks DiagnosticGroups/INFERRED_CONST_CHECKS :internet-explorer-checks DiagnosticGroups/INTERNET_EXPLORER_CHECKS :invalid-casts DiagnosticGroups/INVALID_CASTS + :j2cl-checks DiagnosticGroups/J2CL_CHECKS + :late-provide DiagnosticGroups/LATE_PROVIDE + :lint-checks DiagnosticGroups/LINT_CHECKS + :message-descriptions DiagnosticGroups/MESSAGE_DESCRIPTIONS + :misplaced-type-annotation DiagnosticGroups/MISPLACED_TYPE_ANNOTATION + :missing-getcssname DiagnosticGroups/MISSING_GETCSSNAME + :missing-override DiagnosticGroups/MISSING_OVERRIDE + :missing-polyfill DiagnosticGroups/MISSING_POLYFILL :missing-properties DiagnosticGroups/MISSING_PROPERTIES + :missing-provide DiagnosticGroups/MISSING_PROVIDE + :missing-require DiagnosticGroups/MISSING_REQUIRE + :missing-return DiagnosticGroups/MISSING_RETURN :non-standard-jsdoc DiagnosticGroups/NON_STANDARD_JSDOC + :report-unknown-types DiagnosticGroups/REPORT_UNKNOWN_TYPES + :strict-missing-require DiagnosticGroups/STRICT_MISSING_REQUIRE + :strict-module-checks DiagnosticGroups/STRICT_MODULE_CHECKS :strict-module-dep-check DiagnosticGroups/STRICT_MODULE_DEP_CHECK + :strict-requires DiagnosticGroups/STRICT_REQUIRES + :suspicious-code DiagnosticGroups/SUSPICIOUS_CODE :tweaks DiagnosticGroups/TWEAKS + :type-invalidation DiagnosticGroups/TYPE_INVALIDATION :undefined-names DiagnosticGroups/UNDEFINED_NAMES :undefined-variables DiagnosticGroups/UNDEFINED_VARIABLES + :underscore DiagnosticGroups/UNDERSCORE :unknown-defines DiagnosticGroups/UNKNOWN_DEFINES + :unused-local-variable DiagnosticGroups/UNUSED_LOCAL_VARIABLE + :unused-private-property DiagnosticGroups/UNUSED_PRIVATE_PROPERTY + :use-of-goog-base DiagnosticGroups/USE_OF_GOOG_BASE + :violated-module-dep DiagnosticGroups/VIOLATED_MODULE_DEP :visiblity DiagnosticGroups/VISIBILITY}) (def known-opts From c4348d4a8187062b3f0023f5256d2f19d9371474 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 25 Jan 2017 19:55:21 -0500 Subject: [PATCH 2242/4033] CLJS-1905: Self-host: Stacktraces for script/test-self-parity --- src/test/self/self_parity/test.cljs | 34 ++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 0402b641f..48e8a5dbc 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -18,7 +18,8 @@ (:require [clojure.string :as string] [cljs.nodejs :as nodejs] [cljs.js :as cljs] - [cljs.reader :as reader])) + [cljs.reader :as reader] + [cljs.stacktrace :as st])) (def out-dir "builds/out-self-parity") @@ -232,15 +233,32 @@ [st ns form cb] (cljs/eval st form - {:ns ns - :context :expr - :load load-fn - :eval node-eval - :verbose false} + {:ns ns + :context :expr + :load load-fn + :eval node-eval + :source-map true + :verbose false} cb)) ;; Test suite runner +(defn- handle-error + [error sms] + (loop [error error] + (let [message (if (instance? ExceptionInfo error) + (ex-message error) + (.-message error)) + parsed-stacktrace (st/parse-stacktrace {} + (.-stack error) + {:ua-product :nodejs} + {})] + (println message) + (print (st/mapped-stacktrace-str parsed-stacktrace sms)) + (when-some [cause (.-cause error)] + (print "caused by: ") + (recur cause))))) + (defn run-tests "Runs the tests." [] @@ -285,7 +303,7 @@ [static.core-test])) (fn [{:keys [value error]}] (if error - (prn error) + (handle-error error (:source-maps @st)) (eval-form st 'parity.core '(run-tests 'cljs.primitives-test @@ -322,7 +340,7 @@ 'static.core-test) (fn [{:keys [value error]}] (when error - (prn error))))))))) + (handle-error error (:source-maps @st)))))))))) (defn -main [& args] (init-runtime) From 182ad1621e2bc4293098dde2f214f5aac4091e75 Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Sat, 4 Feb 2017 15:00:26 -0500 Subject: [PATCH 2243/4033] CLJS-1929: When expanding libs don't include Hidden files --- src/main/clojure/cljs/closure.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 248e27177..df83bea69 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1902,6 +1902,7 @@ (into [] (comp (filter #(.endsWith (.getName ^File %) ".js")) + (filter #(not (.isHidden ^File %))) (map (fn [^File f] (let [p (.getPath f) From d26557075e06e561ef11fdb488b5726821ddcd6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 6 Feb 2017 15:59:12 -0800 Subject: [PATCH 2244/4033] CLJS-1930: Master broken wrt static field: ES5_STRICT_UNCOMMON removes closure warnings that are not public and were mistakenly added in a previous commit. --- src/main/clojure/cljs/closure.clj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index df83bea69..e0fde0225 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -97,7 +97,6 @@ (def warning-types {:access-controls DiagnosticGroups/ACCESS_CONTROLS - :access-controls-const DiagnosticGroups/ACCESS_CONTROLS_CONST :ambiguous-function-decl DiagnosticGroups/AMBIGUOUS_FUNCTION_DECL :analyzer-checks DiagnosticGroups/ANALYZER_CHECKS :check-eventful-object-disposal DiagnosticGroups/CHECK_EVENTFUL_OBJECT_DISPOSAL @@ -117,8 +116,6 @@ :duplicate-vars DiagnosticGroups/DUPLICATE_VARS :es3 DiagnosticGroups/ES3 :es5-strict DiagnosticGroups/ES5_STRICT - :es5-strict-uncommon DiagnosticGroups/ES5_STRICT_UNCOMMON - :es5-strict-reflection DiagnosticGroups/ES5_STRICT_REFLECTION :externs-validation DiagnosticGroups/EXTERNS_VALIDATION :extra-require DiagnosticGroups/EXTRA_REQUIRE :fileoverview-jsdoc DiagnosticGroups/FILEOVERVIEW_JSDOC @@ -142,7 +139,6 @@ :non-standard-jsdoc DiagnosticGroups/NON_STANDARD_JSDOC :report-unknown-types DiagnosticGroups/REPORT_UNKNOWN_TYPES :strict-missing-require DiagnosticGroups/STRICT_MISSING_REQUIRE - :strict-module-checks DiagnosticGroups/STRICT_MODULE_CHECKS :strict-module-dep-check DiagnosticGroups/STRICT_MODULE_DEP_CHECK :strict-requires DiagnosticGroups/STRICT_REQUIRES :suspicious-code DiagnosticGroups/SUSPICIOUS_CODE From aa00c033c838692562d0d96dc86b74dc2a1a9b06 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 9 Feb 2017 00:26:23 -0500 Subject: [PATCH 2245/4033] CLJS-1932: Self-host: Perf regression macroexpand-check Addresses a self-hosted perf regression obtaining cljs.spec/macroexpand-check. Previously for every macroexpansion, if cljs.spec was loaded, it would build a map of all of the vars in that namespace and then get the the var for macroexpand-check from that map. This revision avoids building the map upon every check, instead performing a cheaper find-ns-obj check to see if cljs.spec is loaded and if so, performing the same logic but within a delay so as to cache the result. --- src/main/clojure/cljs/analyzer.cljc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index fe593149b..bc2ce4dcd 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2941,6 +2941,12 @@ :cljs ^boolean (.isMacro mvar))) mvar))) +#?(:cljs + (let [cached-var (delay (get (ns-interns* 'cljs.spec) 'macroexpand-check))] + (defn get-macroexpand-check-var [] + (when (some? (find-ns-obj 'cljs.spec)) + @cached-var)))) + (defn macroexpand-1* [env form] (let [op (first form)] @@ -2952,7 +2958,7 @@ :cljs [do]) (let [mchk #?(:clj (some-> (find-ns 'clojure.spec) (ns-resolve 'macroexpand-check)) - :cljs (get (ns-interns* 'cljs.spec) 'macroexpand-check)) + :cljs (get-macroexpand-check-var)) _ (when (some? mchk) (mchk mac-var (next form))) form' (try From bf37131c02a393bbfde3da32d2ffd6f878393cb1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 9 Feb 2017 18:43:22 -0500 Subject: [PATCH 2246/4033] CLJS-1934: Self-host: require-macros :reload / :reload-all fails Fix typo where reload needs to be passed, not reloads. --- src/main/cljs/cljs/js.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index fb780745e..37380a284 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -475,7 +475,7 @@ (cb res) (do (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) - (load-macros bound-vars :require-macros require-macros reloads reloads opts + (load-macros bound-vars :require-macros require-macros reload reloads opts (fn [res] (if (:error res) (cb res) From f5ab470ef7260196ec4d8843e6cf8eb6a3332d45 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Feb 2017 12:48:08 -0500 Subject: [PATCH 2247/4033] change bootstrap script to use Clojure 1.9.0-alpha14 --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index f0833fc20..a8d6032ad 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.9.0-alpha7" +CLOJURE_RELEASE="1.9.0-alpha14" CLOSURE_RELEASE="20161201" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" From af2ca808fcc5efc088b48957b898fed0b59eddbe Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Feb 2017 13:05:56 -0500 Subject: [PATCH 2248/4033] CLJS-1931: Closure Compiler {{--generate_exports}} flag not supported --- src/main/clojure/cljs/closure.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e0fde0225..ea2e8c85e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -163,7 +163,7 @@ :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads - :browser-repl :cache-analysis-format :infer-externs}) + :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -237,6 +237,10 @@ (. compiler-options (setModuleRoots (:closure-module-roots opts)))) + (when (contains? opts :closure-generate-exports) + (. compiler-options + (setGenerateExports (:closure-generate-exports opts)))) + (. compiler-options (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler ) From c25c0a5f6e96d6e76b60a3495308cd951e07a1a9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Feb 2017 14:48:19 -0500 Subject: [PATCH 2249/4033] 1.9.473 --- README.md | 6 +++--- changes.md | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6fc4cbfa4..89961faaa 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.456 +Latest stable release: 1.9.473 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.456"] +[org.clojure/clojurescript "1.9.473"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.456 org.clojure clojurescript - 1.9.456 + 1.9.473 ``` diff --git a/changes.md b/changes.md index 0bdbdb7e0..8bb1747ad 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,20 @@ +## 1.9.473 + +## Fixes +* CLJS-1931: Closure Compiler {{--generate_exports}} flag not supported +* CLJS-1934: Self-host: require-macros :reload / :reload-all fails +* CLJS-1932: Self-host: Perf regression macroexpand-check +* CLJS-1930: Master broken wrt static field: ES5_STRICT_UNCOMMON +* CLJS-1929: When expanding libs don't include Hidden files +* CLJS-1905: Self-host: Stacktraces for script/test-self-parity +* CLJS-1795: Support more options in the `:closure-warnings` compiler option +* CLJS-1922: Use :file as relative output path for foreign-libs +* CLJS-1831: Self-host: Improperly munge ns names +* CLJS-1925: Use of undeclared Var cljs.user/RegExp when extending protocol for RegExp +* CLJS-1920: cljs.build.api/node-inputs: package.json files are only added if module entries are top-leve +* CLJS-1916: __dirname and __filename are not defined when compiling for Node.js with optimizations :none +* CLJS-1915: cljs.test: Index out of bounds for stack element w/o line/column + ## 1.9.456 ### Enhancements From 906889853b86b2f13b570e40d59f44ed6f2d8ebe Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Feb 2017 15:00:52 -0500 Subject: [PATCH 2250/4033] tweak changes.md --- changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.md b/changes.md index 8bb1747ad..c2321bc56 100644 --- a/changes.md +++ b/changes.md @@ -1,6 +1,6 @@ ## 1.9.473 -## Fixes +### Fixes * CLJS-1931: Closure Compiler {{--generate_exports}} flag not supported * CLJS-1934: Self-host: require-macros :reload / :reload-all fails * CLJS-1932: Self-host: Perf regression macroexpand-check From ff0b11c123bf46d9e0efff164a7327fb269d2262 Mon Sep 17 00:00:00 2001 From: Henrik Lundahl Date: Wed, 15 Feb 2017 18:28:00 +0100 Subject: [PATCH 2251/4033] CLJS-1935: When calling cljs.spec/valid?, subsequent predicates of cljs.spec/and are evaluated even when early predicate is unsatisfied Fix that value is used instead of conformed value when checking for invalid? in cljs.spec/and with more than 3 specs. --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index cb280fe8c..e4b474a75 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -723,7 +723,7 @@ (loop [ret x i 0] (if (< i (count specs)) (let [nret (conform* (specs i) ret)] - (if (invalid? ret) + (if (invalid? nret) ::invalid ;;propagate conformed values (recur nret (inc i)))) From 47cd1cef8f7ed5b16cffc7e2ae4d223328091ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 14 Feb 2017 13:50:18 -0800 Subject: [PATCH 2252/4033] CLJS-1942: Self-host: `cljs.env.macros` and `cljs.analyzer.macros` can't be loaded --- src/main/clojure/cljs/analyzer/macros.clj | 23 +++++++++++------------ src/main/clojure/cljs/compiler/macros.clj | 7 +++---- src/main/clojure/cljs/env/macros.clj | 7 +++---- src/test/self/self_parity/test.cljs | 5 +---- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/macros.clj b/src/main/clojure/cljs/analyzer/macros.clj index bc3341438..93a0bb828 100644 --- a/src/main/clojure/cljs/analyzer/macros.clj +++ b/src/main/clojure/cljs/analyzer/macros.clj @@ -7,16 +7,15 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.analyzer.macros - (:refer-clojure :exclude [binding]) - (:require [cljs.core :refer [binding]])) + (:refer-clojure :exclude [binding])) (defmacro with-warning-handlers [handlers & body] - `(binding [cljs.analyzer/*cljs-warning-handlers* ~handlers] + `(cljs.core/binding [cljs.analyzer/*cljs-warning-handlers* ~handlers] ~@body)) (defmacro no-warn [& body] - `(binding [cljs.analyzer/*cljs-warnings* - (zipmap (keys cljs.analyzer/*cljs-warnings*) (repeat false))] + `(cljs.core/binding [cljs.analyzer/*cljs-warnings* + (zipmap (keys cljs.analyzer/*cljs-warnings*) (repeat false))] ~@body)) (defmacro with-core-macros @@ -24,7 +23,7 @@ `(do (when (not= cljs.analyzer/*cljs-macros-path* ~path) (reset! cljs.analyzer/-cljs-macros-loaded false)) - (binding [cljs.analyzer/*cljs-macros-path* ~path] + (cljs.core/binding [cljs.analyzer/*cljs-macros-path* ~path] ~@body))) (defmacro with-core-macros-file @@ -32,8 +31,8 @@ `(do (when (not= cljs.analyzer/*cljs-macros-path* ~path) (reset! cljs.analyzer/-cljs-macros-loaded false)) - (binding [cljs.analyzer/*cljs-macros-path* ~path - cljs.analyzer/*cljs-macros-is-classpath* false] + (cljs.core/binding [cljs.analyzer/*cljs-macros-path* ~path + cljs.analyzer/*cljs-macros-is-classpath* false] ~@body))) (defmacro wrapping-errors [env & body] @@ -45,13 +44,13 @@ (throw (cljs.analyzer/error ~env (.-message err#) err#)))))) (defmacro disallowing-recur [& body] - `(binding [cljs.analyzer/*recur-frames* - (cons nil cljs.analyzer/*recur-frames*)] + `(cljs.core/binding [cljs.analyzer/*recur-frames* + (cons nil cljs.analyzer/*recur-frames*)] ~@body)) (defmacro allowing-redef [& body] - `(binding [cljs.analyzer/*allow-redef* true] + `(cljs.core/binding [cljs.analyzer/*allow-redef* true] ~@body)) (defmacro disallowing-ns* [& body] - `(binding [cljs.analyzer/*allow-ns* false] ~@body)) + `(cljs.core/binding [cljs.analyzer/*allow-ns* false] ~@body)) diff --git a/src/main/clojure/cljs/compiler/macros.clj b/src/main/clojure/cljs/compiler/macros.clj index 22b2f36ad..5ff36b50e 100644 --- a/src/main/clojure/cljs/compiler/macros.clj +++ b/src/main/clojure/cljs/compiler/macros.clj @@ -7,11 +7,10 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.compiler.macros - (:refer-clojure :exclude [let]) - (:require [cljs.core :refer [let]])) + (:refer-clojure :exclude [let])) (defmacro emit-wrap [env & body] - `(let [env# ~env] + `(cljs.core/let [env# ~env] (when (= :return (:context env#)) (cljs.compiler/emits "return ")) ~@body - (when-not (= :expr (:context env#)) (cljs.compiler/emitln ";")))) \ No newline at end of file + (when-not (= :expr (:context env#)) (cljs.compiler/emitln ";")))) diff --git a/src/main/clojure/cljs/env/macros.clj b/src/main/clojure/cljs/env/macros.clj index d7752fee0..863490848 100644 --- a/src/main/clojure/cljs/env/macros.clj +++ b/src/main/clojure/cljs/env/macros.clj @@ -7,8 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.env.macros - (:refer-clojure :exclude [binding ensure]) - (:require [cljs.core :refer [binding]])) + (:refer-clojure :exclude [binding ensure])) (defmacro with-compiler-env "Evaluates [body] with [env] bound as the value of the `*compiler*` var in @@ -23,7 +22,7 @@ (js/Error. (str "Compiler environment must be a map or atom containing a map, not " (type env#)))))] - (binding [cljs.env/*compiler* env#] + (cljs.core/binding [cljs.env/*compiler* env#] ~@body))) (defmacro ensure @@ -35,4 +34,4 @@ ~@body (finally (when (nil? val#) - (set! cljs.env/*compiler* nil)))))) \ No newline at end of file + (set! cljs.env/*compiler* nil)))))) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 48e8a5dbc..d77b18f3f 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -171,10 +171,7 @@ [name macros] ((if macros #{'cljs.core - 'cljs.pprint - 'cljs.env.macros - 'cljs.analyzer.macros - 'cljs.compiler.macros} + 'cljs.pprint} #{'goog.object 'goog.string 'goog.string.StringBuffer From 90fc275d939f6c701096378134a930adbd9493f3 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Mon, 13 Feb 2017 22:36:04 +0200 Subject: [PATCH 2253/4033] CLJS-1939: Fix Node load_file call for foreign-deps --- src/main/clojure/cljs/compiler.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 12b3e0850..80b9739b9 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1048,9 +1048,10 @@ (if (= :nodejs target) ;; under node.js we load foreign libs globally (let [{:keys [js-dependency-index options]} @env/*compiler* - ijs-url (get-in js-dependency-index [(name lib) :url])] + ijs (get js-dependency-index (name lib))] (emitln "cljs.core.load_file(\"" - (str (io/file (util/output-directory options) (util/relative-name ijs-url))) + (str (io/file (util/output-directory options) (or (deps/-relative-path ijs) + (util/relative-name (:url ijs))))) "\");")) (emitln "goog.require('" (munge lib) "');"))))]) From 627f7fd513d928531db48392d8f52142fea5eb38 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 12 Feb 2017 14:27:53 -0500 Subject: [PATCH 2254/4033] CLJS-1636: Mark some symbols in core macros ns as private --- src/main/clojure/cljs/core.cljc | 54 ++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index f8de03501..af0c0cb92 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -610,7 +610,7 @@ `(when-not (exists? ~x) (def ~x ~init))) -(core/defn destructure [bindings] +(core/defn- destructure [bindings] (core/let [bents (partition 2 bindings) pb (core/fn pb [bvec b v] (core/let [pvec @@ -779,7 +779,7 @@ (let ~(vec (interleave bs gs)) ~@body))))))) -(def fast-path-protocols +(def #^:private fast-path-protocols "protocol fqn -> [partition number, bit]" (zipmap (map #(symbol "cljs.core" (core/str %)) '[IFn ICounted IEmptyableCollection ICollection IIndexed ASeq ISeq INext @@ -797,7 +797,7 @@ :cljs (core/* 2 b))])) [0 1]))) -(def fast-path-protocol-partitions-count +(def #^:private fast-path-protocol-partitions-count "total number of partitions" (core/let [c (count fast-path-protocols) m (core/mod c 32)] @@ -862,45 +862,45 @@ `(not (nil? ~x))) ;; internal - do not use. -(core/defmacro coercive-not [x] +(core/defmacro ^:private coercive-not [x] (bool-expr (core/list 'js* "(!~{})" x))) ;; internal - do not use. -(core/defmacro coercive-not= [x y] +(core/defmacro ^:private coercive-not= [x y] (bool-expr (core/list 'js* "(~{} != ~{})" x y))) ;; internal - do not use. -(core/defmacro coercive-= [x y] +(core/defmacro ^:private coercive-= [x y] (bool-expr (core/list 'js* "(~{} == ~{})" x y))) ;; internal - do not use. -(core/defmacro coercive-boolean [x] +(core/defmacro ^:private coercive-boolean [x] (with-meta (core/list 'js* "~{}" x) {:tag 'boolean})) ;; internal - do not use. -(core/defmacro truth_ [x] +(core/defmacro ^:private truth_ [x] (core/assert (core/symbol? x) "x is substituted twice") (core/list 'js* "(~{} != null && ~{} !== false)" x x)) ;; internal - do not use -(core/defmacro js-arguments [] +(core/defmacro ^:private js-arguments [] (core/list 'js* "arguments")) -(core/defmacro js-delete [obj key] +(core/defmacro ^:private js-delete [obj key] (core/list 'js* "delete ~{}[~{}]" obj key)) -(core/defmacro js-in [key obj] +(core/defmacro ^:private js-in [key obj] (core/list 'js* "~{} in ~{}" key obj)) -(core/defmacro js-debugger +(core/defmacro ^:private js-debugger "Emit JavaScript \"debugger;\" statement" [] (core/list 'do (core/list 'js* "debugger") nil)) -(core/defmacro js-comment +(core/defmacro ^:private js-comment "Emit a top-level JavaScript multi-line comment. New lines will create a new comment line. Comment block will be preceded and followed by a newline" [comment] @@ -914,13 +914,13 @@ (reduce core/str "")) " */\n")))) -(core/defmacro unsafe-cast +(core/defmacro ^:private unsafe-cast "EXPERIMENTAL: Subject to change. Unsafely cast a value to a different type." [t x] (core/let [cast-expr (core/str "~{} = /** @type {" t "} */ (~{})")] (core/list 'js* cast-expr x x))) -(core/defmacro js-inline-comment +(core/defmacro ^:private js-inline-comment "Emit an inline JavaScript comment." [comment] (core/list 'js* (core/str "/**" comment "*/"))) @@ -1127,7 +1127,7 @@ ([x y & more] `(bit-and (bit-and ~x ~y) ~@more))) ;; internal do not use -(core/defmacro ^::ana/numeric unsafe-bit-and +(core/defmacro ^:private ^::ana/numeric unsafe-bit-and ([x y] (bool-expr (core/list 'js* "(~{} & ~{})" x y))) ([x y & more] `(unsafe-bit-and (unsafe-bit-and ~x ~y) ~@more))) @@ -1171,15 +1171,15 @@ (core/list 'js* "(~{} | (1 << ~{}))" x n)) ;; internal -(core/defmacro mask [hash shift] +(core/defmacro ^:private mask [hash shift] (core/list 'js* "((~{} >>> ~{}) & 0x01f)" hash shift)) ;; internal -(core/defmacro bitpos [hash shift] +(core/defmacro ^:private bitpos [hash shift] (core/list 'js* "(1 << ~{})" `(mask ~hash ~shift))) ;; internal -(core/defmacro caching-hash [coll hash-fn hash-key] +(core/defmacro ^:private caching-hash [coll hash-fn hash-key] (core/assert (clojure.core/symbol? hash-key) "hash-key is substituted twice") `(let [h# ~hash-key] (if-not (nil? h#) @@ -1373,7 +1373,7 @@ ~type ~(with-meta `(fn ~@meths) (meta form)))) sigs)))) -(core/defmulti extend-prefix (core/fn [tsym sym] (core/-> tsym meta :extend))) +(core/defmulti ^:private extend-prefix (core/fn [tsym sym] (core/-> tsym meta :extend))) (core/defmethod extend-prefix :instance [tsym sym] `(.. ~tsym ~(to-property sym))) @@ -1608,7 +1608,7 @@ (vary-meta (cons f (map #(cons (second %) (nnext %)) sigs)) merge annots))) -(core/defn dt->et +(core/defn- dt->et ([type specs fields] (dt->et type specs fields false)) ([type specs fields inline] @@ -2644,7 +2644,7 @@ (~print-fn (str ~bs-str ", " ~expr-str ", " ~iterations " runs, " elapsed# " msecs")))))) -(def cs (into [] (map (comp gensym core/str core/char) (range 97 118)))) +(def #^:private cs (into [] (map (comp gensym core/str core/char) (range 97 118)))) (core/defn- gen-apply-to-helper ([] (gen-apply-to-helper 1)) @@ -2661,7 +2661,7 @@ ~(gen-apply-to-helper (core/inc n)))) `(throw (js/Error. "Only up to 20 arguments supported on functions")))))) -(core/defmacro gen-apply-to [] +(core/defmacro ^:private gen-apply-to [] `(do (set! ~'*unchecked-if* true) (defn ~'apply-to [~'f ~'argc ~'args] @@ -2691,10 +2691,10 @@ [& colls] `(concat ~@(map #(core/list `lazy-seq %) colls))) -(core/defmacro js-str [s] +(core/defmacro ^:private js-str [s] (core/list 'js* "''+~{}" s)) -(core/defmacro es6-iterable [ty] +(core/defmacro ^:private es6-iterable [ty] `(aset (.-prototype ~ty) cljs.core/ITER_SYMBOL (fn [] (this-as this# @@ -2832,7 +2832,7 @@ `(~'ns* ~(cons :refer-clojure args))) ;; INTERNAL - do not use, only for Node.js -(core/defmacro load-file* [f] +(core/defmacro ^:private load-file* [f] `(. js/goog (~'nodeGlobalRequire ~f))) (core/defmacro macroexpand-1 @@ -2901,7 +2901,7 @@ (set! (. ~sym ~'-cljs$lang$applyTo) ~(apply-to))))))) -(core/defmacro copy-arguments [dest] +(core/defmacro ^:private copy-arguments [dest] `(let [len# (alength (js-arguments))] (loop [i# 0] (when (< i# len#) From 23beecb7e0b752c98cacebfe0aff3879eab0f0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 18 Feb 2017 20:17:12 -0800 Subject: [PATCH 2255/4033] CLJS-1946: Self-hosted: don't emit `goog.require` calls for foreign libs if optimizations different than `:none` --- src/main/clojure/cljs/compiler.cljc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 80b9739b9..2d0f6414f 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1053,7 +1053,12 @@ (str (io/file (util/output-directory options) (or (deps/-relative-path ijs) (util/relative-name (:url ijs))))) "\");")) - (emitln "goog.require('" (munge lib) "');"))))]) + (emitln "goog.require('" (munge lib) "');"))))] + :cljs + [(and (ana/foreign-dep? lib) + (when-let [{:keys [optimizations]} (get @env/*compiler* :options)] + (not (keyword-identical? optimizations :none)))) + nil]) (or (-> libs meta :reload) (= (get reloads lib) :reload)) From 77d9077c04f61a19d86e67d18bd7c93217e88aa3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 18 Feb 2017 12:41:38 -0500 Subject: [PATCH 2256/4033] CLJS-1944: Can't spec generate non-vector collections We need to not default gen-into as [], otherwise kind will not be used when in the gen* implementation. The fix involves simply copying over a missed change when Clojure 23e3ec3f8 was copied as ClojureScript cd43ec9bf. --- src/main/cljs/cljs/spec.cljs | 2 +- src/test/cljs/cljs/spec_test.cljs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index e4b474a75..7fb375c71 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -795,7 +795,7 @@ ([form pred {gen-into :into :keys [kind ::kind-form count max-count min-count distinct gen-max ::kfn ::cpred conform-keys ::conform-all] - :or {gen-max 20, gen-into []} + :or {gen-max 20} :as opts} gfn] (let [conform-into gen-into diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 88009d548..d2be3033b 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -100,6 +100,9 @@ (is (= :b (s/conform ::multi :b))) (is (= :a (s/conform ::multi :a)))) +(deftest test-cljs-1944 + (is (not-empty (s/exercise (s/coll-of string? :kind set?))))) + ;; Copied from Clojure spec tests (def even-count? #(even? (count %))) From 7326d79001f2fbbb2ee4086b1f645cab374c0e58 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 18 Feb 2017 13:18:29 -0500 Subject: [PATCH 2257/4033] CLJS-1945: cljs.spec/every-impl kind-fn kind-form dead code Copy over code deletion from Clojure 386e7e6 that was missed in ClojureScript 1a297c5. --- src/main/cljs/cljs/spec.cljs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 7fb375c71..58bbe2f2c 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -803,12 +803,6 @@ check? #(valid? @spec %) kfn (c/or kfn (fn [i v] i)) addcv (fn [ret i v cv] (conj ret cv)) - [kindfn kindform] (cond - (map? kind) [map? `map?] - (vector? kind) [vector? `vector?] - (list? kind) [list? `list?] - (set? kind) [set? `set?] - :else [seqable? `seqable?]) cfns (fn [x] ;;returns a tuple of [init add complete] fns (cond From 6c5fb679bf843bfbd065d0576e1a3d350f11d4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 15 Feb 2017 16:44:15 -0800 Subject: [PATCH 2258/4033] CLJS-1943: Self-host: `cljs.pprint`'s macros can't be compiled --- .../cljs/cljs/{pprint.clj => pprint.cljc} | 22 ++++++++++++++----- src/test/cljs/cljs/pprint_test.clj | 11 +++++----- src/test/self/self_parity/test.cljs | 12 +++++----- 3 files changed, 26 insertions(+), 19 deletions(-) rename src/main/cljs/cljs/{pprint.clj => pprint.cljc} (90%) diff --git a/src/main/cljs/cljs/pprint.clj b/src/main/cljs/cljs/pprint.cljc similarity index 90% rename from src/main/cljs/cljs/pprint.clj rename to src/main/cljs/cljs/pprint.cljc index 01410b8c4..6793c38c2 100644 --- a/src/main/cljs/cljs/pprint.clj +++ b/src/main/cljs/cljs/pprint.cljc @@ -7,8 +7,9 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.pprint - (:refer-clojure :exclude [deftype]) - (:require [clojure.walk :as walk])) + (:refer-clojure :exclude [deftype #?(:cljs macroexpand)]) + (:require [clojure.walk :as walk] + #?(:cljs [cljs.analyzer :as ana]))) ;; required the following changes: @@ -78,10 +79,19 @@ (cljs.pprint/end-block cljs.core/*out*)))) nil))) -(defn- pll-mod-body [var-sym body] +#?(:cljs + (defn macroexpand [env form] + (loop [form form + form' (ana/macroexpand-1 env form)] + (if-not (identical? form form') + (recur form' (ana/macroexpand-1 env form')) + form')))) + +(defn- pll-mod-body [env var-sym body] (letfn [(inner [form] (if (seq? form) - (let [form (macroexpand form)] + (let [form #?(:clj (macroexpand form) + :cljs (macroexpand env form))] (condp = (first form) 'loop* form 'recur (concat `(recur (inc ~var-sym)) (rest form)) @@ -94,7 +104,7 @@ for use in pretty-printer dispatch functions." [bindings & body] (let [count-var (gensym "length-count") - mod-body (pll-mod-body count-var body)] + mod-body (pll-mod-body &env count-var body)] `(loop ~(apply vector count-var 0 bindings) (if (or (not cljs.core/*print-length*) (< ~count-var cljs.core/*print-length*)) (do ~@mod-body) @@ -154,4 +164,4 @@ format-in can be either a control string or a previously compiled format." "A convenience macro that pretty prints the last thing output. This is exactly equivalent to (pprint *1)." {:added "1.2"} - [] `(cljs.pprint/pprint *1)) \ No newline at end of file + [] `(cljs.pprint/pprint *1)) diff --git a/src/test/cljs/cljs/pprint_test.clj b/src/test/cljs/cljs/pprint_test.clj index 8e6daa3fc..5112562c4 100644 --- a/src/test/cljs/cljs/pprint_test.clj +++ b/src/test/cljs/cljs/pprint_test.clj @@ -7,15 +7,15 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.pprint-test - (:require [cljs.test :refer [deftest is]])) + (:require cljs.test)) (defmacro simple-tests [name & test-pairs] - `(deftest ~name + `(cljs.test/deftest ~name ~@(for [[x y] (partition 2 test-pairs)] `(cond - (cljs.core/regexp? ~y) (is (.exec ~y ~x)) - (cljs.core/string? ~y) (is (= ~x ~y)) - :else (is (= ~x ~y)))))) + (cljs.core/regexp? ~y) (cljs.test/is (.exec ~y ~x)) + (cljs.core/string? ~y) (cljs.test/is (= ~x ~y)) + :else (cljs.test/is (= ~x ~y)))))) (defmacro code-block "Read a string then print it with code-dispatch and succeed if it comes out the same" @@ -28,4 +28,3 @@ (cljs.pprint/with-pprint-dispatch cljs.pprint/code-dispatch (cljs.pprint/pprint (cljs.reader/read-string ~block))))) (clojure.string/split-lines ~block)])))) - diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index d77b18f3f..12a36d7b0 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -170,15 +170,13 @@ technical issues)." [name macros] ((if macros - #{'cljs.core - 'cljs.pprint} + #{'cljs.core} #{'goog.object 'goog.string 'goog.string.StringBuffer 'goog.array 'cljs.core 'cljs.env - 'cljs.pprint 'cljs.tools.reader 'clojure.walk}) name)) @@ -288,8 +286,8 @@ #_[cljs.keyword-test] [cljs.import-test] [cljs.ns-test.foo] - #_[cljs.pprint] - #_[cljs.pprint-test] + [cljs.pprint] + [cljs.pprint-test] [cljs.spec-test] #_[cljs.spec.test-test] [cljs.clojure-alias-test] @@ -325,8 +323,8 @@ 'cljs.ns-test.foo 'foo.ns-shadow-test 'cljs.import-test - #_'cljs.pprint - #_'cljs.pprint-test + 'cljs.pprint + 'cljs.pprint-test 'cljs.spec-test #_'cljs.spec.test-test 'cljs.clojure-alias-test From 6325a29612b6266ee786ce49a83f12566708e696 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 21 Feb 2017 20:22:03 -0500 Subject: [PATCH 2259/4033] CLJS-1950: Eliminate instances of #^ --- src/main/clojure/cljs/core.cljc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index af0c0cb92..9ba5a409d 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -779,7 +779,7 @@ (let ~(vec (interleave bs gs)) ~@body))))))) -(def #^:private fast-path-protocols +(def ^:private fast-path-protocols "protocol fqn -> [partition number, bit]" (zipmap (map #(symbol "cljs.core" (core/str %)) '[IFn ICounted IEmptyableCollection ICollection IIndexed ASeq ISeq INext @@ -797,7 +797,7 @@ :cljs (core/* 2 b))])) [0 1]))) -(def #^:private fast-path-protocol-partitions-count +(def ^:private fast-path-protocol-partitions-count "total number of partitions" (core/let [c (count fast-path-protocols) m (core/mod c 32)] @@ -1227,7 +1227,7 @@ (.replace \/ \$)) "$")) -(def #^:private base-type +(def ^:private base-type {nil "null" 'object "object" 'string "string" @@ -1237,7 +1237,7 @@ 'boolean "boolean" 'default "_"}) -(def #^:private js-base-type +(def ^:private js-base-type {'js/Boolean "boolean" 'js/String "string" 'js/Array "array" @@ -2644,7 +2644,7 @@ (~print-fn (str ~bs-str ", " ~expr-str ", " ~iterations " runs, " elapsed# " msecs")))))) -(def #^:private cs (into [] (map (comp gensym core/str core/char) (range 97 118)))) +(def ^:private cs (into [] (map (comp gensym core/str core/char) (range 97 118)))) (core/defn- gen-apply-to-helper ([] (gen-apply-to-helper 1)) From 025dd9d3e57f1955fd442b9f69823c892a38cb58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 21 Feb 2017 15:16:51 -0800 Subject: [PATCH 2260/4033] CLJS-1949: Self-host: cljs.compiler/munge doesn't preserve JVM compiler semantics --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/self/self_host/test.cljs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 2d0f6414f..b49a5ec86 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -109,7 +109,7 @@ ss (map rf (string/split ss #"\.")) ss (string/join "." ss) ms #?(:clj (clojure.lang.Compiler/munge ss) - :cljs (cljs.core/munge ss))] + :cljs (cljs.core/munge-str ss))] (if (symbol? s) (symbol ms) ms))))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 3f9867fbc..7a1e72b5b 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -863,6 +863,20 @@ (is (false? (:fn-var (var-ast @st 'bar.core$macros/add)))) (inc! l)))))) +(deftest test-cljs-1949 + (async done + (let [st (cljs/empty-state) + l (latch 1 done)] + (cljs/eval-str + st + "(.catch (js/Promise. #(%2 \"x\")) #(println %))" + nil + {:context :expr + :eval node-eval} + (fn [{:keys [error] :as m}] + (is (nil? error)) + (inc! l)))))) + (defn -main [& args] (run-tests)) From a3a242236e7757a179fd16dd9b86767a73a8cb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 11 Feb 2017 17:00:41 -0800 Subject: [PATCH 2261/4033] CLJS-1936: cljs.analyzer declares vars which are only used in Clojure --- src/main/clojure/cljs/analyzer.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index bc2ce4dcd..b51525806 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1827,7 +1827,7 @@ {:env env :op :set! :form form :target targetexpr :val valexpr :children [targetexpr valexpr]}))))) -(declare analyze-file) +#?(:clj (declare analyze-file)) #?(:clj (defn locate-src @@ -2167,7 +2167,7 @@ {:import import-map :require import-map})) -(declare parse-ns) +#?(:clj (declare parse-ns)) (defn macro-autoload-ns? "Given a spec form check whether the spec namespace requires a macro file From 2aa8aae2d8499d1b09a25bfa0b447c46d230cfe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 12 Feb 2017 21:31:32 -0800 Subject: [PATCH 2262/4033] CLJS-1937: Self-host: undeclared cljs.core$macros/mod when compiling cljs/core.cljs --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 9ba5a409d..161b1de33 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1035,7 +1035,7 @@ ([x] `(- ~x))) (core/defmacro ^::ana/numeric unchecked-remainder-int - ([x n] `(mod ~x ~n))) + ([x n] `(core/mod ~x ~n))) (core/defmacro ^::ana/numeric unchecked-subtract ([& xs] `(- ~@xs))) From be768300d88bef57ac6db05e7d4d26878ea2c634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 23 Feb 2017 17:02:23 -0800 Subject: [PATCH 2263/4033] CLJS-1952: Bump Closure Compiler to Feb 2017 release --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- script/clean | 2 ++ src/main/clojure/cljs/closure.clj | 5 ++++- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 85bbb4290..77020dd53 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20161201 + v20170218 org.clojure diff --git a/project.clj b/project.clj index c4c1ddb66..1c58c4026 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.285"] [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] - [com.google.javascript/closure-compiler-unshaded "v20161201"] + [com.google.javascript/closure-compiler-unshaded "v20170218"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index a8d6032ad..08870fb02 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha14" -CLOSURE_RELEASE="20161201" +CLOSURE_RELEASE="20170218" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" diff --git a/script/clean b/script/clean index d041c9e9a..bcbd54172 100755 --- a/script/clean +++ b/script/clean @@ -5,3 +5,5 @@ rm -rf compilation rm -rf lib rm -rf target rm -rf builds +rm -rf clojure +rm -rf out diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ea2e8c85e..268a7a294 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -61,6 +61,7 @@ JSModule JSModuleGraph SourceMap ProcessCommonJSModules AbstractCompiler TransformAMDToCJSModule ProcessEs6Modules CompilerInput] + [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult] @@ -199,8 +200,10 @@ (defn set-options "TODO: Add any other options that we would like to support." [opts ^CompilerOptions compiler-options] + (.setModuleResolutionMode compiler-options ModuleLoader$ResolutionMode/NODE) + (when (contains? opts :pretty-print) - (set! (.prettyPrint compiler-options) (:pretty-print opts))) + (.setPrettyPrint compiler-options (:pretty-print opts))) (when (contains? opts :pseudo-names) (set! (.generatePseudoNames compiler-options) (:pseudo-names opts))) From fa7c692f4e01941ac83be04534b1603828cd134b Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Thu, 23 Feb 2017 17:39:26 +0000 Subject: [PATCH 2264/4033] CLJS-1951: Missing 0 and 1 arity versions of interleave --- src/main/cljs/cljs/core.cljs | 2 ++ src/test/cljs/cljs/collections_test.cljs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b0c8cbe36..461f5ff46 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4656,6 +4656,8 @@ reduces them without incurring seq initialization" (defn interleave "Returns a lazy seq of the first item in each coll, then the second etc." + ([] ()) + ([c1] (lazy-seq c1)) ([c1 c2] (lazy-seq (let [s1 (seq c1) s2 (seq c2)] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index cfc183c3e..04ab9f93b 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -615,3 +615,7 @@ (deftest test-cljs-1809 (is (= (into) [])) (is (= (into [1 2]) [1 2]))) + +(deftest test-cljs-1951 + (is (= () (interleave))) + (is (= '(1 2 3) (interleave [1 2 3])))) From f43fabc66041eab8399b6d867b481d23615c7c3d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Feb 2017 14:05:13 -0500 Subject: [PATCH 2265/4033] CLJS-1940: Undeclared var warning when invoking a protocol method on a `js` interop form Now that we propagate js type hint we to exclude when doing type resolution for protocol fn invokes --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b49a5ec86..0e6426f01 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -940,7 +940,7 @@ (or (= protocol tag) ;; ignore new type hints for now - David (and (not (set? tag)) - (not ('#{any clj clj-or-nil clj-nil number string boolean function object array} tag)) + (not ('#{any clj clj-or-nil clj-nil number string boolean function object array js} tag)) (when-let [ps (:protocols (ana/resolve-existing-var env tag))] (ps protocol))))))) opt-not? (and (= (:name info) 'cljs.core/not) From 3aa1951533ce2ff7a94f90dbcf74904fe413db48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 14 Feb 2017 10:25:15 -0800 Subject: [PATCH 2266/4033] CLJS-1941: `cljs.compiler/cljs-files-in` shouldn't return `.cljc` files if a `.cljs` file exists for the namespace --- src/main/clojure/cljs/compiler.cljc | 30 ++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 0e6426f01..b24f3aba8 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -14,6 +14,7 @@ #?(:clj (:require [cljs.util :as util] [clojure.java.io :as io] [clojure.string :as string] + [clojure.set :as set] [clojure.tools.reader :as reader] [cljs.env :as env :refer [ensure]] [cljs.tagged-literals :as tags] @@ -1446,13 +1447,28 @@ (defn cljs-files-in "Return a sequence of all .cljs and .cljc files in the given directory." [dir] - (filter - #(let [name (.getName ^File %)] - (and (or (.endsWith name ".cljs") - (.endsWith name ".cljc")) - (not= \. (first name)) - (not (contains? cljs-reserved-file-names name)))) - (file-seq dir)))) + (map io/file + (reduce + (fn [m x] + (if (.endsWith ^String x ".cljs") + (cond-> (conj m x) + (contains? m (str (subs x 0 (dec (count x))) "c")) + (set/difference #{(str (subs x 0 (dec (count x))) "c")})) + ;; ends with .cljc + (cond-> m + (not (contains? m (str (subs x 0 (dec (count x))) "s"))) + (conj x)))) + #{} + (into [] + (comp + (filter + #(let [name (.getName ^File %)] + (and (or (.endsWith name ".cljs") + (.endsWith name ".cljc")) + (not= \. (first name)) + (not (contains? cljs-reserved-file-names name))))) + (map #(.getPath ^File %))) + (file-seq dir)))))) #?(:clj (defn compile-root From 46dcd22713a924bfd90f0a19449bb09f128d11df Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 2 Feb 2017 22:21:35 -0500 Subject: [PATCH 2267/4033] CLJS-1928: Self-host: Macro namespace alias in keyword Also consult macros namespaces in analysis cache when setting up alias map. --- src/main/cljs/cljs/js.cljs | 5 ++++- src/test/cljs/cljs/keyword_macros.clj | 5 +++++ src/test/cljs/cljs/keyword_test.cljs | 4 ++-- src/test/self/self_parity/test.cljs | 4 ++-- 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 src/test/cljs/cljs/keyword_macros.clj diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 37380a284..ed38aab60 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -177,7 +177,10 @@ (defn- current-alias-map [] - (get-in @env/*compiler* [:cljs.analyzer/namespaces ana/*cljs-ns* :requires])) + (->> (merge (get-in @env/*compiler* [::ana/namespaces ana/*cljs-ns* :requires]) + (get-in @env/*compiler* [::ana/namespaces ana/*cljs-ns* :require-macros])) + (remove (fn [[k v]] (= k v))) + (into {}))) ;; ----------------------------------------------------------------------------- ;; Analyze diff --git a/src/test/cljs/cljs/keyword_macros.clj b/src/test/cljs/cljs/keyword_macros.clj new file mode 100644 index 000000000..0e3f0e58f --- /dev/null +++ b/src/test/cljs/cljs/keyword_macros.clj @@ -0,0 +1,5 @@ +(ns cljs.keyword-macros) + +(defmacro add + [a b] + `(+ ~a ~b)) diff --git a/src/test/cljs/cljs/keyword_test.cljs b/src/test/cljs/cljs/keyword_test.cljs index 12c065867..462281305 100644 --- a/src/test/cljs/cljs/keyword_test.cljs +++ b/src/test/cljs/cljs/keyword_test.cljs @@ -7,7 +7,7 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.keyword-test - (:require-macros [clojure.core :as cc] + (:require-macros [cljs.keyword-macros :as macros] [cljs.test :refer [deftest is]]) (:require [cljs.keyword-other :as other] [cljs.test])) @@ -15,4 +15,4 @@ (deftest test-keyword (is (= ::bar :cljs.keyword-test/bar)) (is (= ::other/foo :cljs.keyword-other/foo)) - (is (= ::cc/foo :clojure.core/foo))) + (is (= ::macros/foo :cljs.keyword-macros/foo))) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 12a36d7b0..3a0da83ef 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -283,7 +283,7 @@ [foo.ns-shadow-test] [cljs.top-level] [cljs.reducers-test] - #_[cljs.keyword-test] + [cljs.keyword-test] [cljs.import-test] [cljs.ns-test.foo] [cljs.pprint] @@ -318,7 +318,7 @@ 'cljs.binding-test 'cljs.macro-test 'cljs.top-level - #_'cljs.keyword-test + 'cljs.keyword-test #_'cljs.ns-test 'cljs.ns-test.foo 'foo.ns-shadow-test From 04541fbf8ec573d26bac4e4e61e69cb4c64a3dd7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Feb 2017 15:06:25 -0500 Subject: [PATCH 2268/4033] CLJS-1948: Possible race condition in compiler w/ parallel-build true the issue was that duplicate compiler inputs could appear. We were using distinct before but for various reasons this is not good enough. We now use :provides as the uniquely identifying bit about a compiler input. --- src/main/clojure/cljs/js_deps.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 49ccb5839..b1154e547 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -7,7 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.js-deps - (:require [clojure.java.io :as io] + (:require [cljs.util :refer [distinct-by]] + [clojure.java.io :as io] [clojure.string :as string]) (:import [java.io File] [java.net URL URLClassLoader] @@ -194,7 +195,7 @@ case." [coll] (let [state (build-index (map pack-string coll))] (map unpack-string - (distinct + (distinct-by :provides (:order (reduce dependency-order-visit (assoc state :order []) (keys state))))))) From e5b6218e1ef0d17d2c4543dfdd78c63eb5e3eaad Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Feb 2017 16:32:52 -0500 Subject: [PATCH 2269/4033] 1.9.493 --- README.md | 6 +++--- changes.md | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 89961faaa..a95fb5b5a 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.473 +Latest stable release: 1.9.493 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.473"] +[org.clojure/clojurescript "1.9.493"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.473 org.clojure clojurescript - 1.9.473 + 1.9.493 ``` diff --git a/changes.md b/changes.md index c2321bc56..13df951c8 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,25 @@ +## 1.9.493 + +### Fixes +* CLJS-1948: Possible race condition in compiler w/ parallel-build true +* CLJS-1941: `cljs.compiler/cljs-files-in` shouldn't return `.cljc` files if a `.cljs` file exists for the namespace +* CLJS-1941: `cljs.compiler/cljs-files-in` shouldn't return `.cljc` files if a `.cljs` file exists for the namespace +* CLJS-1940: Undeclared var warning when invoking a protocol method on a `js` interop form +* CLJS-1951: Missing 0 and 1 arity versions of interleave +* CLJS-1952: Bump Closure Compiler to Feb 2017 release +* CLJS-1937: Self-host: undeclared cljs.core$macros/mod when compiling cljs/core.cljs +* CLJS-1936: cljs.analyzer declares vars which are only used in Clojure +* CLJS-1949: Self-host: cljs.compiler/munge doesn't preserve JVM compiler semantics +* CLJS-1950: Eliminate instances of #^ +* CLJS-1943: Self-host: `cljs.pprint`'s macros can't be compiled +* CLJS-1945: cljs.spec/every-impl kind-fn kind-form dead code +* CLJS-1944: Can't spec generate non-vector collections +* CLJS-1946: Self-hosted: don't emit `goog.require` calls for foreign libs if optimizations different than `:none` +* CLJS-1636: Mark some symbols in core macros ns as private +* CLJS-1939: Fix Node load_file call for foreign-deps +* CLJS-1942: Self-host: `cljs.env.macros` and `cljs.analyzer.macros` can't be loaded +* CLJS-1935: When calling cljs.spec/valid?, subsequent predicates of cljs.spec/and are evaluated even when early predicate is unsatisfied + ## 1.9.473 ### Fixes From d438d07ef2a8f5180799de536c6448c1e0513cfb Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Feb 2017 19:11:15 -0500 Subject: [PATCH 2270/4033] 1.9.494 --- README.md | 6 ++-- changes.md | 5 +++ src/main/clojure/cljs/core.cljc | 55 +++++++++++++++------------------ 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index a95fb5b5a..7e7fd57c2 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.493 +Latest stable release: 1.9.494 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.493"] +[org.clojure/clojurescript "1.9.494"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.493 org.clojure clojurescript - 1.9.493 + 1.9.494 ``` diff --git a/changes.md b/changes.md index 13df951c8..04967f4b7 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 1.9.494 + +### Fixes +* revert CLJS-1636: Mark some symbols in core macros ns as private + ## 1.9.493 ### Fixes diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 161b1de33..dd6da0871 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -610,7 +610,7 @@ `(when-not (exists? ~x) (def ~x ~init))) -(core/defn- destructure [bindings] +(core/defn destructure [bindings] (core/let [bents (partition 2 bindings) pb (core/fn pb [bvec b v] (core/let [pvec @@ -779,7 +779,7 @@ (let ~(vec (interleave bs gs)) ~@body))))))) -(def ^:private fast-path-protocols +(def fast-path-protocols "protocol fqn -> [partition number, bit]" (zipmap (map #(symbol "cljs.core" (core/str %)) '[IFn ICounted IEmptyableCollection ICollection IIndexed ASeq ISeq INext @@ -797,7 +797,7 @@ :cljs (core/* 2 b))])) [0 1]))) -(def ^:private fast-path-protocol-partitions-count +(def fast-path-protocol-partitions-count "total number of partitions" (core/let [c (count fast-path-protocols) m (core/mod c 32)] @@ -861,46 +861,41 @@ (core/defmacro some? [x] `(not (nil? ~x))) -;; internal - do not use. -(core/defmacro ^:private coercive-not [x] +(core/defmacro coercive-not [x] (bool-expr (core/list 'js* "(!~{})" x))) -;; internal - do not use. -(core/defmacro ^:private coercive-not= [x y] +(core/defmacro coercive-not= [x y] (bool-expr (core/list 'js* "(~{} != ~{})" x y))) -;; internal - do not use. -(core/defmacro ^:private coercive-= [x y] +(core/defmacro coercive-= [x y] (bool-expr (core/list 'js* "(~{} == ~{})" x y))) -;; internal - do not use. -(core/defmacro ^:private coercive-boolean [x] +(core/defmacro coercive-boolean [x] (with-meta (core/list 'js* "~{}" x) {:tag 'boolean})) ;; internal - do not use. -(core/defmacro ^:private truth_ [x] +(core/defmacro truth_ [x] (core/assert (core/symbol? x) "x is substituted twice") (core/list 'js* "(~{} != null && ~{} !== false)" x x)) -;; internal - do not use -(core/defmacro ^:private js-arguments [] +(core/defmacro js-arguments [] (core/list 'js* "arguments")) -(core/defmacro ^:private js-delete [obj key] +(core/defmacro js-delete [obj key] (core/list 'js* "delete ~{}[~{}]" obj key)) -(core/defmacro ^:private js-in [key obj] +(core/defmacro js-in [key obj] (core/list 'js* "~{} in ~{}" key obj)) -(core/defmacro ^:private js-debugger +(core/defmacro js-debugger "Emit JavaScript \"debugger;\" statement" [] (core/list 'do (core/list 'js* "debugger") nil)) -(core/defmacro ^:private js-comment +(core/defmacro js-comment "Emit a top-level JavaScript multi-line comment. New lines will create a new comment line. Comment block will be preceded and followed by a newline" [comment] @@ -914,13 +909,13 @@ (reduce core/str "")) " */\n")))) -(core/defmacro ^:private unsafe-cast +(core/defmacro unsafe-cast "EXPERIMENTAL: Subject to change. Unsafely cast a value to a different type." [t x] (core/let [cast-expr (core/str "~{} = /** @type {" t "} */ (~{})")] (core/list 'js* cast-expr x x))) -(core/defmacro ^:private js-inline-comment +(core/defmacro js-inline-comment "Emit an inline JavaScript comment." [comment] (core/list 'js* (core/str "/**" comment "*/"))) @@ -1127,7 +1122,7 @@ ([x y & more] `(bit-and (bit-and ~x ~y) ~@more))) ;; internal do not use -(core/defmacro ^:private ^::ana/numeric unsafe-bit-and +(core/defmacro ^::ana/numeric unsafe-bit-and ([x y] (bool-expr (core/list 'js* "(~{} & ~{})" x y))) ([x y & more] `(unsafe-bit-and (unsafe-bit-and ~x ~y) ~@more))) @@ -1171,15 +1166,15 @@ (core/list 'js* "(~{} | (1 << ~{}))" x n)) ;; internal -(core/defmacro ^:private mask [hash shift] +(core/defmacro mask [hash shift] (core/list 'js* "((~{} >>> ~{}) & 0x01f)" hash shift)) ;; internal -(core/defmacro ^:private bitpos [hash shift] +(core/defmacro bitpos [hash shift] (core/list 'js* "(1 << ~{})" `(mask ~hash ~shift))) ;; internal -(core/defmacro ^:private caching-hash [coll hash-fn hash-key] +(core/defmacro caching-hash [coll hash-fn hash-key] (core/assert (clojure.core/symbol? hash-key) "hash-key is substituted twice") `(let [h# ~hash-key] (if-not (nil? h#) @@ -1608,7 +1603,7 @@ (vary-meta (cons f (map #(cons (second %) (nnext %)) sigs)) merge annots))) -(core/defn- dt->et +(core/defn dt->et ([type specs fields] (dt->et type specs fields false)) ([type specs fields inline] @@ -2661,7 +2656,7 @@ ~(gen-apply-to-helper (core/inc n)))) `(throw (js/Error. "Only up to 20 arguments supported on functions")))))) -(core/defmacro ^:private gen-apply-to [] +(core/defmacro gen-apply-to [] `(do (set! ~'*unchecked-if* true) (defn ~'apply-to [~'f ~'argc ~'args] @@ -2691,10 +2686,10 @@ [& colls] `(concat ~@(map #(core/list `lazy-seq %) colls))) -(core/defmacro ^:private js-str [s] +(core/defmacro js-str [s] (core/list 'js* "''+~{}" s)) -(core/defmacro ^:private es6-iterable [ty] +(core/defmacro es6-iterable [ty] `(aset (.-prototype ~ty) cljs.core/ITER_SYMBOL (fn [] (this-as this# @@ -2832,7 +2827,7 @@ `(~'ns* ~(cons :refer-clojure args))) ;; INTERNAL - do not use, only for Node.js -(core/defmacro ^:private load-file* [f] +(core/defmacro load-file* [f] `(. js/goog (~'nodeGlobalRequire ~f))) (core/defmacro macroexpand-1 @@ -2901,7 +2896,7 @@ (set! (. ~sym ~'-cljs$lang$applyTo) ~(apply-to))))))) -(core/defmacro ^:private copy-arguments [dest] +(core/defmacro copy-arguments [dest] `(let [len# (alength (js-arguments))] (loop [i# 0] (when (< i# len#) From 86f26bf267f1c729313320c38e53e64450c0e68d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Mar 2017 14:36:06 -0500 Subject: [PATCH 2271/4033] if we can't examine a zip-file just return nil --- src/main/clojure/cljs/js_deps.cljc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index b1154e547..cb6338092 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -29,16 +29,17 @@ If no ClassLoader is provided, RT/baseLoader is assumed." distinct))) (defn ^ZipFile zip-file [jar-path] - (cond - (instance? File jar-path) (ZipFile. ^File jar-path) - (string? jar-path) (ZipFile. ^String jar-path) - :else - (throw - (IllegalArgumentException. (str "Cannot construct zipfile from " jar-path))))) + (try + (cond + (instance? File jar-path) (ZipFile. ^File jar-path) + (string? jar-path) (ZipFile. ^String jar-path)) + (catch Exception _ + nil))) (defn jar-entry-names* [jar-path] - (with-open [z (zip-file jar-path)] - (doall (map #(.getName ^ZipEntry %) (enumeration-seq (.entries ^ZipFile z)))))) + (when-let [zf (zip-file jar-path)] + (with-open [z zf] + (doall (map #(.getName ^ZipEntry %) (enumeration-seq (.entries ^ZipFile z))))))) (def jar-entry-names (memoize jar-entry-names*)) From 1d38f73a86081ad54cb230c507fbae183d768d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 7 Mar 2017 13:15:06 -0800 Subject: [PATCH 2272/4033] CLJS-1968: Enable calling JS modules that export a single function --- src/main/clojure/cljs/analyzer.cljc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b51525806..2bac2efa1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -691,8 +691,9 @@ [module] ;; we need to check both keys and values of the JS module index, because ;; macroexpansion will be looking for the provided name - António Monteiro - (-> (into #{} (mapcat identity) (get-in @env/*compiler* [:js-module-index])) - (contains? module))) + (contains? + (into #{} (mapcat identity) (get-in @env/*compiler* [:js-module-index])) + (str module))) (defn confirm-var-exists ([env prefix suffix] @@ -711,7 +712,7 @@ (not (loaded-js-ns? env prefix)) (not (and (= 'cljs.core prefix) (= 'unquote suffix))) (nil? (gets @env/*compiler* ::namespaces prefix :defs suffix)) - (not (js-module-exists? (str prefix)))) + (not (js-module-exists? prefix))) (missing-fn env prefix suffix))))) (defn confirm-var-exists-throw [] @@ -746,7 +747,7 @@ ;; macros may refer to namespaces never explicitly required ;; confirm that the library at least exists #?(:clj (nil? (util/ns->source ns-sym))) - (not (js-module-exists? (str ns-sym)))) + (not (js-module-exists? ns-sym))) (warning :undeclared-ns env {:ns-sym ns-sym}))) (defn core-name? @@ -921,6 +922,13 @@ (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym)) (recur env (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) confirm) + (or (js-module-exists? s) + (js-module-exists? (resolve-ns-alias env s))) + (let [module (or (gets @env/*compiler* :js-module-index s) + (resolve-ns-alias env s))] + {:name (symbol module) + :ns 'js}) + :else (let [cur-ns (-> env :ns :name) full-ns (cond From 777d41b9b6fe83c3d29fc51ee3ddbdfeff4f803b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 24 Feb 2017 08:30:23 -0800 Subject: [PATCH 2273/4033] CLJS-1960: Require CommonJS modules directly from a ClojureScript namespace This patch addresses the first part of the solution outlined in the following design doc: https://github.com/clojure/clojurescript/wiki/Enhanced-Node.js-Modules-Support It makes possible to specify, install and require Node.js dependencies directly from ClojureScript namespaces. Future work can make it possible to support specifying these dependencies in `deps.cljs` files and handling conflict resolution between upstream foreign dependencies and foreign dependencies specified directly in the compiler options. --- src/main/cljs/cljs/module_deps.js | 57 +++++++++-- src/main/clojure/cljs/build/api.clj | 57 +---------- src/main/clojure/cljs/closure.clj | 143 +++++++++++++++++++++++++--- src/main/clojure/cljs/util.cljc | 14 +++ 4 files changed, 201 insertions(+), 70 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index a3dbf96d9..e8b08c608 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -1,26 +1,71 @@ var path = require('path'); var mdeps = require('module-deps'); +var nodeResolve = require('resolve'); +var browserResolve = require('browser-resolve'); -var md = mdeps({}); -var deps_files = []; +var target = 'CLJS_TARGET'; +var filename = path.resolve(__dirname, 'JS_FILE'); +var resolver = target === 'nodejs' ? nodeResolve : browserResolve; + +var md = mdeps({ + resolve: function(id, parent, cb) { + // set the basedir properly so we don't try to resolve requires in the Closure + // Compiler processed `node_modules` folder. + parent.basedir = parent.filename === filename ? __dirname: path.dirname(parent.filename); + + resolver(id, parent, cb); + }, + filter: function(id) { + return !nodeResolve.isCore(id); +}}); + +var pkgJsons = []; +var deps_files = {}; md.on('package', function (pkg) { // we don't want to include the package.json for users' projects if (/node_modules/.test(pkg.__dirname)) { - deps_files.push({file: path.join(pkg.__dirname, 'package.json')}); + var pkgJson = { + file: path.join(pkg.__dirname, 'package.json'), + }; + + if (pkg.name != null) { + pkgJson.provided = [ pkg.name ]; + } + + if (pkg.main != null) { + pkgJson.main = path.join(pkg.__dirname, pkg.main); + } + + pkgJsons.push(pkgJson); } }); md.on('file', function(file) { - deps_files.push({file: file}); + deps_files[file] = { file: file }; }); md.on('end', function() { - process.stdout.write(JSON.stringify(deps_files)); + for (var i = 0; i < pkgJsons.length; i++) { + var pkgJson = pkgJsons[i]; + + if (deps_files[pkgJson.main] != null && pkgJson.provided != null) { + deps_files[pkgJson.main].provides = pkgJson.provided; + } + + deps_files[pkgJson.file] = { file: pkgJson.file }; + } + + var values = []; + for (var key in deps_files) { + values.push(deps_files[key]); + } + + process.stdout.write(JSON.stringify(values)); }); md.end({ - file: path.resolve(path.join(__dirname, 'JS_FILE')) + file: filename, }); md.resume(); diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 5faf48bd5..b2b003b45 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -21,11 +21,7 @@ [cljs.compiler :as comp] [cljs.closure :as closure] [cljs.js-deps :as js-deps]) - (:import [java.io - File StringWriter - BufferedReader - Writer InputStreamReader IOException] - [java.lang ProcessBuilder])) + (:import [java.io File])) ;; ============================================================================= ;; Useful Utilities @@ -219,57 +215,12 @@ (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/watch source opts compiler-env stop)))) -(defn- alive? [proc] - (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) - -(defn- pipe [^Process proc in ^Writer out] - ;; we really do want system-default encoding here - (with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)] - (loop [buf (char-array 1024)] - (when (alive? proc) - (try - (let [len (.read in buf)] - (when-not (neg? len) - (.write out buf 0 len) - (.flush out))) - (catch IOException e - (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed"))) - (.printStackTrace e *err*)))) - (recur buf))))) - (defn node-module-deps "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry point. Assumes that the module-deps NPM package is either locally or globally installed." - [{:keys [file]}] - (let [code (string/replace - (slurp (io/resource "cljs/module_deps.js")) - "JS_FILE" - (string/replace file - (System/getProperty "user.dir") "")) - proc (-> (ProcessBuilder. - ["node" "--eval" code]) - .start) - is (.getInputStream proc) - iw (StringWriter. (* 16 1024 1024)) - es (.getErrorStream proc) - ew (StringWriter. (* 1024 1024)) - _ (do (.start - (Thread. - (bound-fn [] (pipe proc is iw)))) - (.start - (Thread. - (bound-fn [] (pipe proc es ew))))) - err (.waitFor proc)] - (if (zero? err) - (into [] - (map (fn [{:strs [file]}] file - {:file file :module-type :commonjs})) - (next (json/read-str (str iw)))) - (do - (when-not (.isAlive proc) - (println (str ew))) - [])))) + [entry] + (closure/node-module-deps entry)) (comment (node-module-deps @@ -284,7 +235,7 @@ the module-deps package on the supplied JavaScript entry points. Assumes that the module-deps NPM packages is either locally or globally installed." [entries] - (into [] (distinct (mapcat node-module-deps entries)))) + (closure/node-inputs entries)) (comment (node-inputs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 268a7a294..fa58f337a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -47,7 +47,9 @@ [clojure.data.json :as json] [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers]) - (:import [java.io File BufferedInputStream StringWriter] + (:import [java.lang ProcessBuilder] + [java.io File BufferedInputStream BufferedReader + Writer InputStreamReader IOException StringWriter] [java.net URL] [java.util.logging Level] [java.util List Random] @@ -342,7 +344,6 @@ (doseq [next (seq warnings)] (println "WARNING:" (.toString ^JSError next))))) - ;; Protocols for IJavaScript and Compilable ;; ======================================== @@ -353,7 +354,7 @@ (-source-map [this] "Return the CLJS compiler generated JS source mapping")) (extend-protocol deps/IJavaScript - + String (-foreign? [this] false) (-closure-lib? [this] false) @@ -362,7 +363,7 @@ (-provides [this] (:provides (deps/parse-js-ns (string/split-lines this)))) (-requires [this] (:requires (deps/parse-js-ns (string/split-lines this)))) (-source [this] this) - + clojure.lang.IPersistentMap (-foreign? [this] (:foreign this)) (-closure-lib? [this] (:closure-lib this)) @@ -481,7 +482,7 @@ returns a JavaScriptFile. In either case the return value satisfies IJavaScript." [^File file {:keys [output-file] :as opts}] - (if output-file + (if output-file (let [out-file (io/file (util/output-directory opts) output-file)] (compiled-file (comp/compile-file file out-file opts))) (let [path (.getPath ^File file)] @@ -567,17 +568,17 @@ (case (.getProtocol this) "file" (-find-sources (io/file this) opts) "jar" (find-jar-sources this opts))) - + clojure.lang.PersistentList (-compile [this opts] (compile-form-seq [this])) (-find-sources [this opts] [(ana/parse-ns [this] opts)]) - + String (-compile [this opts] (-compile (io/file this) opts)) (-find-sources [this opts] (-find-sources (io/file this) opts)) - + clojure.lang.PersistentVector (-compile [this opts] (compile-form-seq this)) (-find-sources [this opts] @@ -1339,7 +1340,7 @@ ;; optimize a ClojureScript form (optimize {:optimizations :simple} (-compile '(def x 3) {})) - + ;; optimize a project (println (->> (-compile "samples/hello/src" {}) (apply add-dependencies {}) @@ -1730,7 +1731,7 @@ (output-deps-file opts disk-sources)))) (comment - + ;; output unoptimized alone (output-unoptimized {} "goog.provide('test');\ngoog.require('cljs.core');\nalert('hello');\n") ;; output unoptimized with all dependencies @@ -1916,12 +1917,19 @@ [lib])))] (into [] (mapcat expand-lib* libs)))) +(declare index-node-modules) + (defn add-implicit-options [{:keys [optimizations output-dir] :or {optimizations :none output-dir "out"} :as opts}] - (let [opts (cond-> (update opts :foreign-libs expand-libs) + (let [opts (cond-> (update opts :foreign-libs + (fn [libs] + (into [] + (util/distinct-merge-by :file + (index-node-modules opts) + (expand-libs libs))))) (:closure-defines opts) (assoc :closure-defines (into {} @@ -1966,6 +1974,118 @@ (nil? (:closure-module-roots opts)) (assoc :closure-module-roots [])))) +(defn- alive? [proc] + (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) + +(defn- pipe [^Process proc in ^Writer out] + ;; we really do want system-default encoding here + (with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)] + (loop [buf (char-array 1024)] + (when (alive? proc) + (try + (let [len (.read in buf)] + (when-not (neg? len) + (.write out buf 0 len) + (.flush out))) + (catch IOException e + (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed"))) + (.printStackTrace e *err*)))) + (recur buf))))) + +(defn maybe-install-node-deps! + [{:keys [npm-deps verbose] :as opts}] + (if-not (empty? npm-deps) + (do + (when (or ana/*verbose* verbose) + (util/debug-prn "Installing Node.js dependencies")) + (let [proc (-> (ProcessBuilder. + (into ["npm" "install" "module-deps"] + (map (fn [[dep version]] (str (name dep) "@" version))) + npm-deps)) + .start) + is (.getInputStream proc) + iw (StringWriter. (* 16 1024 1024)) + es (.getErrorStream proc) + ew (StringWriter. (* 1024 1024)) + _ (do (.start + (Thread. + (bound-fn [] (pipe proc is iw)))) + (.start + (Thread. + (bound-fn [] (pipe proc es ew))))) + err (.waitFor proc)] + (when (and (not (zero? err)) (not (.isAlive proc))) + (println (str ew))) + opts)) + opts)) + +(defn node-module-deps + "EXPERIMENTAL: return the foreign libs entries as computed by running + the module-deps package on the supplied JavaScript entry point. Assumes + that the module-deps NPM package is either locally or globally installed." + ([entry] + (node-module-deps entry + (when env/*compiler* + (:options @env/*compiler*)))) + ([{:keys [file]} {:keys [target] :as opts}] + (let [code (-> (slurp (io/resource "cljs/module_deps.js")) + (string/replace "JS_FILE" file) + (string/replace "CLJS_TARGET" (str "" (when target (name target))))) + proc (-> (ProcessBuilder. + ["node" "--eval" code]) + .start) + is (.getInputStream proc) + iw (StringWriter. (* 16 1024 1024)) + es (.getErrorStream proc) + ew (StringWriter. (* 1024 1024)) + _ (do (.start + (Thread. + (bound-fn [] (pipe proc is iw)))) + (.start + (Thread. + (bound-fn [] (pipe proc es ew))))) + err (.waitFor proc)] + (if (zero? err) + (into [] + (map (fn [{:strs [file provides]}] file + (merge + {:file file + :module-type :commonjs} + (when provides + {:provides provides})))) + (next (json/read-str (str iw)))) + (do + (when-not (.isAlive proc) + (println (str ew))) + []))))) + +(defn node-inputs + "EXPERIMENTAL: return the foreign libs entries as computed by running + the module-deps package on the supplied JavaScript entry points. Assumes + that the module-deps NPM packages is either locally or globally installed." + ([entries] + (node-inputs entries + (when env/*compiler* + (:options @env/*compiler*)))) + ([entries opts] + (into [] (distinct (mapcat #(node-module-deps % opts) entries))))) + +(defn index-node-modules + ([] + (index-node-modules + (when env/*compiler* + (:options @env/*compiler*)))) + ([{:keys [npm-deps] :as opts}] + (let [node-modules (io/file "node_modules")] + (when (and (.exists node-modules) (.isDirectory node-modules)) + (let [modules (map name (keys npm-deps)) + deps-file (io/file (str (util/output-directory opts) File/separator + "cljs$node_modules.js"))] + (util/mkdirs deps-file) + (with-open [w (io/writer deps-file)] + (run! #(.write w (str "require('" % "');\n")) modules)) + (node-inputs [{:file (.getAbsolutePath deps-file)}])))))) + (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google Closure modules and writes them to disk. Adds mapping from original module @@ -2067,6 +2187,7 @@ (env/with-compiler-env compiler-env (let [compiler-stats (:compiler-stats opts) all-opts (-> opts + maybe-install-node-deps! add-implicit-options process-js-modules)] (check-output-to opts) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 3aac4800d..275c54bad 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -279,6 +279,20 @@ xs seen)))] (step coll #{})))) +(defn distinct-merge-by + [f & xss] + (let [xf (map (fn [x] + [(f x) x]))] + (vals (apply merge-with + (fn [a b] + (merge-with + (fn [a b] + (cond-> a + (sequential? a) + (into b))) + a b)) + (map #(into {} xf %) xss))))) + (defn content-sha [^String s] (let [digest (MessageDigest/getInstance "SHA-1")] (.reset digest) From 62337d018bf9902fe8943289b9d7f3bf5c698bff Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Tue, 7 Mar 2017 14:10:42 +0000 Subject: [PATCH 2274/4033] CLJS-1967: Missing ^boolean for removed-leaf? in THM impl Also uses the provided 'val' field of Box type rather than getting and setting a new '0' field. --- src/main/cljs/cljs/core.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 461f5ff46..4cead3e9f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6773,7 +6773,7 @@ reduces them without incurring seq initialization" (== bitmap bit) nil :else (.edit-and-remove-pair inode edit bit idx))) (key-test key key-or-nil) - (do (aset removed-leaf? 0 true) + (do (set! (.-val removed-leaf?) true) (.edit-and-remove-pair inode edit bit idx)) :else inode))))) @@ -7012,7 +7012,7 @@ reduces them without incurring seq initialization" (let [idx (hash-collision-node-find-index arr cnt key)] (if (== idx -1) inode - (do (aset removed-leaf? 0 true) + (do (set! (.-val removed-leaf?) true) (if (== cnt 1) nil (let [editable (.ensure-editable inode edit) @@ -7423,7 +7423,7 @@ reduces them without incurring seq initialization" (if (identical? node root) nil (set! root node)) - (if (aget removed-leaf? 0) + (if ^boolean (.-val removed-leaf?) (set! count (dec count))) tcoll))) (throw (js/Error. "dissoc! after persistent!")))) From 9e5b7ac64dd0388d3403f48381c80ba1e3998da8 Mon Sep 17 00:00:00 2001 From: "Rajoduo@yahoo.com" Date: Thu, 2 Feb 2017 20:47:05 +0100 Subject: [PATCH 2275/4033] CLJ-1868 - Output simpler dependency rel paths when compiling with Closure libs Until now compiler did not parse input paths for Closure dependencies correctly and as a result the output paths contained full absolute path as its part. That path contained charachters like : and ! that were ok on unix machines but caused problem on windows machines. With this fix the compiler will output correct rel paths. --- src/main/clojure/cljs/closure.clj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index fa58f337a..56276fec4 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1526,10 +1526,7 @@ (if (.endsWith lib-path ".js") (util/get-name url) (let [path (util/path url)] - (string/replace - path - (str (io/file (System/getProperty "user.dir") lib-path) File/separator) - ""))))) + (subs path (+ (.lastIndexOf path lib-path) (.length lib-path))))))) (defn ^String rel-output-path "Given an IJavaScript which is either in memory, in a jar file, From f7d08ba3f837f3e2d20ebdaf487221b18bb640c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 27 Feb 2017 09:48:44 -0800 Subject: [PATCH 2276/4033] CLJS-1957: Process JS modules errors and warnings don't get printed --- src/main/clojure/cljs/closure.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 56276fec4..a8147ad1e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -339,10 +339,11 @@ (defn report-failure [^Result result] (let [errors (.errors result) warnings (.warnings result)] - (doseq [next (seq errors)] - (println "ERROR:" (.toString ^JSError next))) - (doseq [next (seq warnings)] - (println "WARNING:" (.toString ^JSError next))))) + (binding [*out* *err*] + (doseq [next (seq errors)] + (println "ERROR:" (.toString ^JSError next))) + (doseq [next (seq warnings)] + (println "WARNING:" (.toString ^JSError next)))))) ;; Protocols for IJavaScript and Compilable ;; ======================================== From 3449c5328356fb4951d1a950158b88e339818d0b Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Mon, 13 Mar 2017 18:19:33 -0500 Subject: [PATCH 2277/4033] CLJS-1976: hash-map assoc stackoverflow create-node (and possibly other internal functions) assume that hash values collide when their values are equal. Because it is possible to return a hash value of more than 32 bits, it is possible to have a hash collision where hashes are unequal, causing undefined behavior. In this case, it caused a stackoverflow during hash-map and set assoc! and assoc. This patch forces the hash function to truncate all hash values from dates and -hash implementations to 32 bits. --- src/main/cljs/cljs/core.cljs | 6 ++--- src/test/cljs/cljs/hash_map_test.cljs | 35 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4cead3e9f..a6c621752 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -901,7 +901,7 @@ [o] (cond (implements? IHash o) - (-hash ^not-native o) + (bit-xor (-hash ^not-native o) 0) (number? o) (if (js/isFinite o) @@ -924,12 +924,12 @@ (m3-hash-int (hash-string o)) (instance? js/Date o) - (.valueOf o) + (bit-xor (.valueOf o) 0) (nil? o) 0 :else - (-hash o))) + (bit-xor (-hash o) 0))) (defn hash-combine [seed hash] ; a la boost diff --git a/src/test/cljs/cljs/hash_map_test.cljs b/src/test/cljs/cljs/hash_map_test.cljs index 83058f9fc..d9968e21e 100644 --- a/src/test/cljs/cljs/hash_map_test.cljs +++ b/src/test/cljs/cljs/hash_map_test.cljs @@ -50,3 +50,38 @@ (let [sym-a (with-meta 'foo :first) sym-b (with-meta 'foo :second)] (is (= {sym-a 2} (array-map sym-a 1 sym-b 2)))))) + +(defrecord T [index a b]) + +(deftest test-cljs-1976 + ;; We must detect hash collisions when two values have different hashes but + ;; still have the same 32-bit hash. Hash producers may be lazy and not + ;; truncate their hash to 32-bits. + (let [bad-record-1 (->T :eavt 17592186192276 nil) + ;; (hash bad-record-1) is 1454955434 + bad-record-2 (->T :avet 10 :fhir.ElementDefinition/minValueDateTime$cr) + ;; (hash bad-record-2) is -2840011862 + ;; But (bit-or (hash bad-record-2) 0) is 1454955434. Collision! + ;; These dates have the same property + bad-date-1 #inst "2017-03-13T22:21:08.666-00:00" + bad-date-2 #inst "2015-11-02T19:53:15.706-00:00"] + (testing "Transient assoc of hash-colliding keys with different hash values" + (is (= :ok (try + (hash-map bad-record-1 nil bad-record-2 nil) + :ok + (catch :default _ :error)))) + (is (= :ok (try + (hash-map bad-date-1 nil bad-date-2 nil) + :ok + (catch :default _ :error))))) + + (testing "Non-transient assoc of hash-colliding keys with different hash values" + (is (= :ok (try + (assoc (hash-map bad-record-1 nil) bad-record-2 nil) + :ok + (catch :default _ :error)))) + + (is (= :ok (try + (assoc (hash-map bad-date-1 nil) bad-date-2 nil) + :ok + (catch :default _ :error))))))) From 1c9802043de28b8c331bc7e3c27b651450147731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 14 Mar 2017 10:50:30 -0700 Subject: [PATCH 2278/4033] CLJS-1980: port CLJ-2100 (s/nilable form should retain original spec form) --- src/main/cljs/cljs/spec.cljc | 5 +++++ src/main/cljs/cljs/spec.cljs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 7138d0f4c..1cb71801b 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -40,6 +40,11 @@ (sequential? form) (walk/postwalk #(if (symbol? %) (res env %) %) (unfn form)) :else form)) +(defmacro ^:private mres + "a compile time res, for use in cljs/spec.cljs" + [form] + (res &env form)) + (defn- ns-qualify "Qualify symbol s by resolving it or using the current *ns*." [env s] diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index 58bbe2f2c..d7413fd4f 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -1369,7 +1369,7 @@ [[1 (gen/delay (gen/return nil))] [9 (gen/delay (gensub pred overrides (conj path ::pred) rmap form))]]))) (with-gen* [_ gfn] (nilable-impl form pred gfn)) - (describe* [_] `(nilable ~(describe* spec)))))) + (describe* [_] `(nilable ~(s/mres form)))))) (defn exercise "generates a number (default 10) of values compatible with spec and maps conform over them, From 6f4b313e4f9523e9e8345f13c4a55bb1b6f93ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 14 Mar 2017 10:45:06 -0700 Subject: [PATCH 2279/4033] CLJS-1979: port CLJ-2043 (fix s/form of s/conformer) --- src/main/cljs/cljs/spec.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 1cb71801b..22ded2b7b 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -346,8 +346,8 @@ (possibly converted) value or :cljs.spec/invalid, and returns a spec that uses it as a predicate/conformer. Optionally takes a second fn that does unform of result of first" - ([f] `(spec-impl '~f ~f nil true)) - ([f unf] `(spec-impl '~f ~f nil true ~unf))) + ([f] `(spec-impl '(conformer ~(res &env f)) ~f nil true)) + ([f unf] `(spec-impl '(conformer ~(res &env f) ~(res &env unf)) ~f nil true ~unf))) (defmacro fspec "takes :args :ret and (optional) :fn kwargs whose values are preds From e6abaa746e815f9f040484f0647b2173bfc45592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 14 Mar 2017 10:39:32 -0700 Subject: [PATCH 2280/4033] CLJS-1978: port CLJ-2035 --- src/main/cljs/cljs/spec.cljc | 24 ++++++++++++++++++++---- src/main/cljs/cljs/spec.cljs | 3 ++- src/test/cljs/cljs/spec_test.cljs | 24 ++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc index 22ded2b7b..f84ae6c63 100644 --- a/src/main/cljs/cljs/spec.cljc +++ b/src/main/cljs/cljs/spec.cljc @@ -201,6 +201,15 @@ [& pred-forms] `(and-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) +(defn- res-kind + [env opts] + (let [{kind :kind :as mopts} opts] + (->> + (if kind + (assoc mopts :kind `~(res env kind)) + mopts) + (mapcat identity)))) + (defmacro every "takes a pred and validates collection elements against that pred. @@ -231,7 +240,11 @@ See also - coll-of, every-kv " [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] - (let [nopts (-> opts (dissoc :gen) (assoc ::kind-form `'~(res &env (:kind opts)))) + (let [desc (::describe opts) + nopts (-> opts + (dissoc :gen ::describe) + (assoc ::kind-form `'~(res &env (:kind opts)) + ::describe (clojure.core/or desc `'(every ~(res &env pred) ~@(res-kind &env opts))))) gx (gensym) cpreds (cond-> [(list (clojure.core/or kind `coll?) gx)] count (conj `(= ~count (c/bounded-count ~count ~gx))) @@ -253,7 +266,8 @@ See also - map-of" [kpred vpred & opts] - `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (nth v# 0)) :into {} ~@opts)) + (let [desc `(every-kv ~(res &env kpred) ~(res &env vpred) ~@(res-kind &env opts))] + `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (nth v# 0)) :into {} ::describe '~desc ~@opts))) (defmacro coll-of "Returns a spec for a collection of items satisfying pred. Unlike @@ -267,7 +281,8 @@ See also - every, map-of" [pred & opts] - `(every ~pred ::conform-all true ~@opts)) + (let [desc `(coll-of ~(res &env pred) ~@(res-kind &env opts))] + `(every ~pred ::conform-all true ::describe '~desc ~@opts))) (defmacro map-of "Returns a spec for a map whose keys satisfy kpred and vals satisfy @@ -280,7 +295,8 @@ See also - every-kv" [kpred vpred & opts] - `(every-kv ~kpred ~vpred ::conform-all true :kind map? ~@opts)) + (let [desc `(map-of ~(res &env kpred) ~(res &env vpred) ~@(res-kind &env opts))] + `(every-kv ~kpred ~vpred ::conform-all true :kind map? ::describe '~desc ~@opts))) (defmacro * "Returns a regex op that matches zero or more values matching diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index d7413fd4f..fd039e142 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -793,6 +793,7 @@ "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" ([form pred opts] (every-impl form pred opts nil)) ([form pred {gen-into :into + describe-form ::describe :keys [kind ::kind-form count max-count min-count distinct gen-max ::kfn ::cpred conform-keys ::conform-all] :or {gen-max 20} @@ -906,7 +907,7 @@ (gen/vector pgen 0 gen-max)))))))) (with-gen* [_ gfn] (every-impl form pred opts gfn)) - (describe* [_] `(every ~form ~@(mapcat identity opts))))))) + (describe* [_] (c/or describe-form `(every ~(s/res form) ~@(mapcat identity opts)))))))) ;;;;;;;;;;;;;;;;;;;;;;; regex ;;;;;;;;;;;;;;;;;;; ;;See: diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index d2be3033b..51534793d 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -241,6 +241,30 @@ ;;coll [:a "b"] ::s/invalid '[{:pred (coll-checker keyword?), :val [:a b]}] ))) +(deftest coll-form + (are [spec form] + (= (s/form spec) form) + (s/map-of int? any?) + '(cljs.spec/map-of cljs.core/int? cljs.core/any?) + + (s/coll-of int?) + '(cljs.spec/coll-of cljs.core/int?) + + (s/every-kv int? int?) + '(cljs.spec/every-kv cljs.core/int? cljs.core/int?) + + (s/every int?) + '(cljs.spec/every cljs.core/int?) + + (s/coll-of (s/tuple (s/tuple int?))) + '(cljs.spec/coll-of (cljs.spec/tuple (cljs.spec/tuple cljs.core/int?))) + + (s/coll-of int? :kind vector?) + '(cljs.spec/coll-of cljs.core/int? :kind cljs.core/vector?) + + (s/coll-of int? :gen #(gen/return [1 2])) + '(cljs.spec/coll-of cljs.core/int? :gen (fn* [] (gen/return [1 2]))))) + (comment (run-tests) From 21da03e03e229a0b38fb872485d26a30fbf034b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 11 Mar 2017 13:16:39 -0800 Subject: [PATCH 2281/4033] CLJS-1973: Add support for `:npm-deps` in upstream `deps.cljs` This patch addresses the second part of the solution outlined in the following design doc: https://github.com/clojure/clojurescript/wiki/Enhanced-Node.js-Modules-Support It adds support for specifying a `:npm-deps` option in upstream `deps.cljs` files, as well as very basic conflict handling. --- src/main/clojure/cljs/closure.clj | 99 +++++++++++++++++++++---------- src/main/clojure/cljs/util.cljc | 7 +++ 2 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a8147ad1e..993140763 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1754,7 +1754,12 @@ ([classloader] (let [upstream-deps (map #(read-string (slurp %)) (enumeration-seq (. classloader (getResources "deps.cljs"))))] - (apply merge-with concat upstream-deps)))) + (apply merge-with + (fn [a b] + (if (map? a) + (merge-with #(into #{%1} #{%2}) a b) + (concat a b))) + upstream-deps)))) (def get-upstream-deps (memoize get-upstream-deps*)) @@ -1879,6 +1884,18 @@ (format ":cache-analysis format must be :edn or :transit but it is: %s" (pr-str cache-analysis-format)))) +(defn check-npm-deps [{:keys [npm-deps]}] + (let [{ups-npm-deps :npm-deps} (get-upstream-deps) + conflicts (filter (fn [[dep v]] + (and (coll? v) (not (contains? npm-deps dep)))) + ups-npm-deps)] + (binding [*out* *err*] + (doseq [[dep versions] conflicts] + (println (str "WARNING: NPM dependency " (name dep) + " conflicts between versions " + (util/conjunction-str versions) + ". Specify a version in :npm-deps or the latest will be installed.")))))) + (defn foreign-source? [js] (and (satisfies? deps/IJavaScript js) (deps/-foreign? js))) @@ -1917,8 +1934,24 @@ (declare index-node-modules) +(defn compute-upstream-npm-deps + ([] + (compute-upstream-npm-deps + (when env/*compiler* + (:options @env/*compiler*)))) + ([{:keys [npm-deps]}] + (let [{ups-npm-deps :npm-deps} (get-upstream-deps)] + (reduce + (fn [m [dep v]] + (cond-> m + (not (contains? npm-deps dep)) + (assoc dep (if (coll? v) + (last (sort v)) + v)))) + {} ups-npm-deps)))) + (defn add-implicit-options - [{:keys [optimizations output-dir] + [{:keys [optimizations output-dir npm-deps] :or {optimizations :none output-dir "out"} :as opts}] @@ -1926,7 +1959,7 @@ (fn [libs] (into [] (util/distinct-merge-by :file - (index-node-modules opts) + (index-node-modules npm-deps opts) (expand-libs libs))))) (:closure-defines opts) (assoc :closure-defines @@ -1946,7 +1979,10 @@ :optimizations optimizations :output-dir output-dir :ups-libs libs - :ups-foreign-libs foreign-libs + :ups-foreign-libs (into [] + (util/distinct-merge-by :file + (index-node-modules (compute-upstream-npm-deps opts) opts) + (expand-libs foreign-libs))) :ups-externs externs :emit-constants emit-constants :cache-analysis-format (:cache-analysis-format opts :transit)) @@ -1992,30 +2028,31 @@ (defn maybe-install-node-deps! [{:keys [npm-deps verbose] :as opts}] - (if-not (empty? npm-deps) - (do - (when (or ana/*verbose* verbose) - (util/debug-prn "Installing Node.js dependencies")) - (let [proc (-> (ProcessBuilder. - (into ["npm" "install" "module-deps"] - (map (fn [[dep version]] (str (name dep) "@" version))) - npm-deps)) - .start) - is (.getInputStream proc) - iw (StringWriter. (* 16 1024 1024)) - es (.getErrorStream proc) - ew (StringWriter. (* 1024 1024)) - _ (do (.start - (Thread. - (bound-fn [] (pipe proc is iw)))) - (.start - (Thread. - (bound-fn [] (pipe proc es ew))))) - err (.waitFor proc)] - (when (and (not (zero? err)) (not (.isAlive proc))) - (println (str ew))) - opts)) - opts)) + (let [npm-deps (merge npm-deps (compute-upstream-npm-deps opts))] + (if-not (empty? npm-deps) + (do + (when (or ana/*verbose* verbose) + (util/debug-prn "Installing Node.js dependencies")) + (let [proc (-> (ProcessBuilder. + (into ["npm" "install" "module-deps"] + (map (fn [[dep version]] (str (name dep) "@" version))) + npm-deps)) + .start) + is (.getInputStream proc) + iw (StringWriter. (* 16 1024 1024)) + es (.getErrorStream proc) + ew (StringWriter. (* 1024 1024)) + _ (do (.start + (Thread. + (bound-fn [] (pipe proc is iw)))) + (.start + (Thread. + (bound-fn [] (pipe proc es ew))))) + err (.waitFor proc)] + (when (and (not (zero? err)) (not (.isAlive proc))) + (println (str ew))) + opts)) + opts))) (defn node-module-deps "EXPERIMENTAL: return the foreign libs entries as computed by running @@ -2069,11 +2106,11 @@ (into [] (distinct (mapcat #(node-module-deps % opts) entries))))) (defn index-node-modules - ([] + ([npm-deps] (index-node-modules (when env/*compiler* (:options @env/*compiler*)))) - ([{:keys [npm-deps] :as opts}] + ([npm-deps opts] (let [node-modules (io/file "node_modules")] (when (and (.exists node-modules) (.isDirectory node-modules)) (let [modules (map name (keys npm-deps)) @@ -2183,6 +2220,8 @@ (add-externs-sources opts))))) ([source opts compiler-env] (env/with-compiler-env compiler-env + ;; we want to warn about NPM dep conflicts before installing the modules + (check-npm-deps opts) (let [compiler-stats (:compiler-stats opts) all-opts (-> opts maybe-install-node-deps! diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 275c54bad..435b2ab04 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -312,3 +312,10 @@ (recur (next ks) ret b'))) (merge ret b'))) a)) + +(defn conjunction-str [xs] + (let [xs (vec xs)] + (case (count xs) + 1 (first xs) + 2 (str (first xs) " and " (second xs)) + (str (string/join ", " (pop xs)) " and " (peek xs))))) From c2fbded498033515cbe32db122b2860a1245f4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 17 Mar 2017 12:13:44 -0700 Subject: [PATCH 2282/4033] CLJS-1983: res -> mres in spec.cljs --- src/main/cljs/cljs/spec.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs index fd039e142..b25595a95 100644 --- a/src/main/cljs/cljs/spec.cljs +++ b/src/main/cljs/cljs/spec.cljs @@ -907,7 +907,7 @@ (gen/vector pgen 0 gen-max)))))))) (with-gen* [_ gfn] (every-impl form pred opts gfn)) - (describe* [_] (c/or describe-form `(every ~(s/res form) ~@(mapcat identity opts)))))))) + (describe* [_] (c/or describe-form `(every ~(s/mres form) ~@(mapcat identity opts)))))))) ;;;;;;;;;;;;;;;;;;;;;;; regex ;;;;;;;;;;;;;;;;;;; ;;See: From 2171ae9859a2e982497764a04de10916aae68307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 27 Feb 2017 14:16:46 -0800 Subject: [PATCH 2283/4033] CLJS-1956: Add missing JS reserved keywords --- src/main/cljs/cljs/core.cljs | 4 ++-- src/main/clojure/cljs/analyzer.cljc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a6c621752..689583956 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10558,7 +10558,7 @@ reduces them without incurring seq initialization" ^{:private true :jsdoc ["@type {*}"]} js-reserved-arr - #js ["abstract" "boolean" "break" "byte" "case" + #js ["arguments" "abstract" "await" "boolean" "break" "byte" "case" "catch" "char" "class" "const" "continue" "debugger" "default" "delete" "do" "double" "else" "enum" "export" "extends" "final" @@ -10570,7 +10570,7 @@ reduces them without incurring seq initialization" "synchronized" "this" "throw" "throws" "transient" "try" "typeof" "var" "void" "volatile" "while" "with" "yield" "methods" - "null"]) + "null" "constructor"]) (def ^{:jsdoc ["@type {null|Object}"]} diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2bac2efa1..75f64f39e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -147,7 +147,7 @@ :infer-warning false}) (def js-reserved - #{"arguments" "abstract" "boolean" "break" "byte" "case" + #{"arguments" "abstract" "await" "boolean" "break" "byte" "case" "catch" "char" "class" "const" "continue" "debugger" "default" "delete" "do" "double" "else" "enum" "export" "extends" "final" From 197ff2e7c3f96b365e31cf4c1a4af39bdd60fc88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 1 Mar 2017 10:20:42 -0800 Subject: [PATCH 2284/4033] CLJS-1964: Validate that `:target :nodejs` and no optimizations requires a `:main` option to be present --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 993140763..9ead2592b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1870,7 +1870,9 @@ (defn check-node-target [{:keys [target optimizations] :as opts}] (assert (not (and (= target :nodejs) (= optimizations :whitespace))) - (format ":nodejs target not compatible with :whitespace optimizations"))) + (format ":nodejs target not compatible with :whitespace optimizations")) + (assert (not (and (= target :nodejs) (= optimizations :none) (not (contains? opts :main)))) + (format ":nodejs target with :none optimizations requires a :main entry"))) (defn check-preloads [{:keys [preloads optimizations] :as opts}] (when (and (some? preloads) (not= optimizations :none)) From aa67063fb04a24901001df8ae408ad52b31e8fc5 Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Fri, 24 Mar 2017 14:55:19 +0100 Subject: [PATCH 2285/4033] CLS-1519 Collection invoke errors report arity off by 1 --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b24f3aba8..06a875840 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -830,7 +830,7 @@ (emitln "return " n ".call(this" (if (zero? pcnt) nil (list "," (comma-sep (take pcnt maxparams)))) ");")))) (emitln "}") - (emitln "throw(new Error('Invalid arity: ' + arguments.length));") + (emitln "throw(new Error('Invalid arity: ' + (arguments.length - 1)));") (emitln "};") (when variadic (emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";") From 81c37b941fd7336ec620cfff1cfa0d2a5270d23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 21 Mar 2017 10:12:12 -0700 Subject: [PATCH 2286/4033] CLJS-1987: don't index node modules blindly --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9ead2592b..3c2ec172a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2114,7 +2114,7 @@ (:options @env/*compiler*)))) ([npm-deps opts] (let [node-modules (io/file "node_modules")] - (when (and (.exists node-modules) (.isDirectory node-modules)) + (when (and (not (empty? npm-deps)) (.exists node-modules) (.isDirectory node-modules)) (let [modules (map name (keys npm-deps)) deps-file (io/file (str (util/output-directory opts) File/separator "cljs$node_modules.js"))] From 8f5d9827553006b3a7b633e8a11c93e12411ef0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 18 Mar 2017 15:15:41 -0700 Subject: [PATCH 2287/4033] CLJS-1985: `index-node-modules` should pass opts to `node-inputs` --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3c2ec172a..cd2ab17b1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2121,7 +2121,7 @@ (util/mkdirs deps-file) (with-open [w (io/writer deps-file)] (run! #(.write w (str "require('" % "');\n")) modules)) - (node-inputs [{:file (.getAbsolutePath deps-file)}])))))) + (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)))))) (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google From 52ff7a2bdcaacf840f7aae72f4be4575297f7db1 Mon Sep 17 00:00:00 2001 From: Thomas Spellman Date: Sat, 18 Mar 2017 12:13:49 -0700 Subject: [PATCH 2288/4033] Fix tiny bug in index-node-modules when no second argument is given --- src/main/clojure/cljs/closure.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cd2ab17b1..b57c47a83 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2110,6 +2110,7 @@ (defn index-node-modules ([npm-deps] (index-node-modules + npm-deps (when env/*compiler* (:options @env/*compiler*)))) ([npm-deps opts] From 296d0a69340e832b92ed742b3cd0304a06bea27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 27 Mar 2017 11:44:28 -0700 Subject: [PATCH 2289/4033] CLJS-1988: add :npm-deps to recognized compiler options --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b57c47a83..2888aefbe 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -166,7 +166,7 @@ :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads - :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports}) + :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 From b35d693582db4fea39fa10bf0359b4055e8f4125 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 4 Apr 2017 09:09:23 +0200 Subject: [PATCH 2290/4033] CLJS-1994: assoc on nil returns PHM (expected PAM) --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 689583956..15815aada 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1865,7 +1865,7 @@ reduces them without incurring seq initialization" :else not-found) not-found))) -(declare PersistentHashMap) +(declare PersistentHashMap PersistentArrayMap) (defn assoc "assoc[iate]. When applied to a map, returns a new map of the @@ -1875,7 +1875,7 @@ reduces them without incurring seq initialization" ([coll k v] (if-not (nil? coll) (-assoc coll k v) - (hash-map k v))) + (array-map k v))) ([coll k v & kvs] (let [ret (assoc coll k v)] (if kvs From 79509651e16bf2b2fdc60a39de8671ef96206055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 3 Apr 2017 10:37:44 -0700 Subject: [PATCH 2291/4033] CLJS-1996: Support correct checking of :preloads when :optimizations not specified --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2888aefbe..469ca305e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2236,7 +2236,7 @@ (check-source-map-path opts) (check-output-wrapper opts) (check-node-target opts) - (check-preloads opts) + (check-preloads all-opts) (check-cache-analysis-format opts) (swap! compiler-env #(-> % From b799fb9dbddf8bf10815df01b56940c4c705296a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 13 Nov 2016 17:22:16 +0100 Subject: [PATCH 2292/4033] CLJS-1497: `find` on an associative collection does not return collection key --- src/main/cljs/cljs/core.cljs | 57 +++++++++++++++++++++--- src/test/cljs/cljs/collections_test.cljs | 33 ++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 15815aada..c552fd364 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -546,6 +546,10 @@ "Returns a new collection of coll with a mapping from key k to value v added to it.")) +(defprotocol IFind + "Protocol for implementing entry finding in collections." + (-find [coll k])) + (defprotocol IMap "Protocol for adding mapping functionality to collections." #_(-assoc-ex [coll k v]) @@ -2023,6 +2027,10 @@ reduces them without incurring seq initialization" "Returns true if coll implements Associative" [x] (satisfies? IAssociative x)) +(defn ^boolean ifind? + "Returns true if coll implements IFind" + [x] (satisfies? IFind x)) + (defn ^boolean sequential? "Returns true if coll satisfies ISequential" [x] (satisfies? ISequential x)) @@ -2237,7 +2245,9 @@ reduces them without incurring seq initialization" (when (and (not (nil? coll)) (associative? coll) (contains? coll k)) - [k (get coll k)])) + (if (ifind? coll) + (-find coll k) + [k (get coll k)]))) (defn ^boolean distinct? "Returns true if no two of the arguments are =" @@ -5152,6 +5162,10 @@ reduces them without incurring seq initialization" (-assoc-n coll k v) (throw (js/Error. "Vector's key for assoc must be a number.")))) + IFind + (-find [coll k] + [k (get coll k)]) + IVector (-assoc-n [coll n val] (cond @@ -5438,6 +5452,10 @@ reduces them without incurring seq initialization" (-assoc-n coll key val) (throw (js/Error. "Subvec's key for assoc must be a number.")))) + IFind + (-find [coll key] + [key (get coll key)]) + IVector (-assoc-n [coll n val] (let [v-pos (+ start n)] @@ -5954,6 +5972,10 @@ reduces them without incurring seq initialization" true false)) + IFind + (-find [coll k] + [k (get coll k)]) + IKVReduce (-kv-reduce [coll f init] (let [len (alength keys)] @@ -6126,7 +6148,7 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x (count coll))) (lastIndexOf [coll x start] (-lastIndexOf coll x start)) - + IMeta (-meta [coll] _meta) @@ -6154,7 +6176,7 @@ reduces them without incurring seq initialization" IHash (-hash [coll] (hash-ordered-coll coll)) - + ISeq (-first [coll] [(aget arr i) (aget arr (inc i))]) @@ -6261,7 +6283,7 @@ reduces them without incurring seq initialization" IIterable (-iterator [this] (PersistentArrayMapIterator. arr 0 (* cnt 2))) - + ISeqable (-seq [coll] (persistent-array-map-seq arr 0 nil)) @@ -6302,6 +6324,11 @@ reduces them without incurring seq initialization" (-contains-key? [coll k] (not (== (array-map-index-of coll k) -1))) + IFind + (-find [coll k] + (let [idx (array-map-index-of coll k)] + [(aget arr idx) (get coll k)])) + IMap (-dissoc [coll k] (let [idx (array-map-index-of coll k)] @@ -6472,7 +6499,7 @@ reduces them without incurring seq initialization" tcoll) (throw (js/Error. "dissoc! after persistent!"))))) -(declare TransientHashMap PersistentHashMap) +(declare TransientHashMap) (defn- array->transient-hash-map [len arr] (loop [out (transient (.-EMPTY PersistentHashMap)) @@ -7184,8 +7211,6 @@ reduces them without incurring seq initialization" (recur (inc j)))))) (ArrayNodeSeq. meta nodes i s nil)))) -(declare TransientHashMap) - (deftype HashMapIter [nil-val root-iter ^:mutable seen] Object (hasNext [_] @@ -7301,6 +7326,12 @@ reduces them without incurring seq initialization" :else (not (identical? (.inode-lookup root 0 (hash k) k lookup-sentinel) lookup-sentinel)))) + IFind + (-find [coll k] + (if has-nil? + [nil nil-val] + (.inode-find root 0 (hash k) k nil))) + IMap (-dissoc [coll k] (cond (nil? k) (if has-nil? @@ -7738,6 +7769,10 @@ reduces them without incurring seq initialization" (-assoc [node k v] (assoc [key val] k v)) + IFind + (-find [node k] + [key val]) + IVector (-assoc-n [node n v] (-assoc-n [key val] n v)) @@ -7890,6 +7925,10 @@ reduces them without incurring seq initialization" (-assoc [node k v] (assoc [key val] k v)) + IFind + (-find [node k] + [key val]) + IVector (-assoc-n [node n v] (-assoc-n [key val] n v)) @@ -8128,6 +8167,10 @@ reduces them without incurring seq initialization" (-contains-key? [coll k] (not (nil? (.entry-at coll k)))) + IFind + (-find [coll k] + (.entry-at coll k)) + IMap (-dissoc [coll k] (let [found (array nil) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 04ab9f93b..cd20b610b 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -619,3 +619,36 @@ (deftest test-cljs-1951 (is (= () (interleave))) (is (= '(1 2 3) (interleave [1 2 3])))) + +(deftest test-cljs-1497 + (testing "PersistentArrayMap" + (let [metadata {:a 1} + k [1 2 3] + v 1 + map (array-map (with-meta k metadata) v) + [k' v'] (find map k)] + (is (= k k')) + (is (= v v')) + (is (= metadata (meta k'))))) + (testing "PersistentHashMap" + (let [metadata {:a 1} + k [1 2 3] + v 1 + map (hash-map (with-meta k metadata) v) + [k' v'] (find map k)] + (is (= k k')) + (is (= v v')) + (is (= metadata (meta k')))) + (let [map (hash-map nil :foo)] + (is (= (find map nil) [nil :foo])))) + (testing "PersistentTreeMap" + (let [metadata {:a 1} + k [1 2 3] + v 1 + map (sorted-map (with-meta k metadata) v) + [k' v'] (find map k)] + (is (= k k')) + (is (= v v')) + (is (= metadata (meta k')))) + (let [map (sorted-map nil :foo)] + (is (= (find map nil) [nil :foo]))))) From 3a9e4b43a2c01be5ed8945b2a90d04dcd14af1c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 11 Apr 2017 16:10:25 -0700 Subject: [PATCH 2293/4033] CLJS-2006: Upgrade Closure Compiler to April 2017 release --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 77020dd53..a832b940b 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20170218 + v20170409 org.clojure diff --git a/project.clj b/project.clj index 1c58c4026..a4bdf5370 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.285"] [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] - [com.google.javascript/closure-compiler-unshaded "v20170218"] + [com.google.javascript/closure-compiler-unshaded "v20170409"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 08870fb02..21e752a5f 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha14" -CLOSURE_RELEASE="20170218" +CLOSURE_RELEASE="20170409" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" From 10fc3ce9580d3d4758b5c5b95992ab343deb24a7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 12 Apr 2017 16:41:15 -0400 Subject: [PATCH 2294/4033] 1.9.518 --- README.md | 6 +++--- changes.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7e7fd57c2..f922cbea3 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.494 +Latest stable release: 1.9.518 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.494"] +[org.clojure/clojurescript "1.9.518"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.494 org.clojure clojurescript - 1.9.494 + 1.9.518 ``` diff --git a/changes.md b/changes.md index 04967f4b7..c18fe9ee0 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,33 @@ +## 1.9.518 + +### Enhancements +* CLJS-1973: Add support for `:npm-deps` in upstream `deps.cljs` +* CLJS-1968: Enable calling JS modules that export a single function +* CLJS-1960: Require CommonJS modules directly from a ClojureScript namespace + +### Changes +* CLJS-2006: Upgrade Closure Compiler to April 2017 release + +### 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) +* CLJS-1988: add :npm-deps to recognized compiler options +* Fix tiny bug in index-node-modules when no second argument is given +* CLJS-1985: `index-node-modules` should pass opts to `node-inputs` +* CLJS-1987: don't index node modules blindly +* CLJS-1519 Collection invoke errors report arity off by 1 +* CLJS-1964: Validate that `:target :nodejs` and no optimizations requires a `:main` option to be present +* CLJS-1956: Add missing JS reserved keywords +* CLJS-1983: res -> mres in spec.cljs +* CLJS-1978: port CLJ-2035 +* CLJS-1979: port CLJ-2043 (fix s/form of s/conformer) +* CLJS-1980: port CLJ-2100 (s/nilable form should retain original spec form) +* CLJS-1976: hash-map assoc stackoverflow +* CLJS-1957: Process JS modules errors and warnings don't get printed +* CLJS-1868 - Output simpler dependency rel paths when compiling with Closure libs +* CLJS-1967: Missing ^boolean for removed-leaf? in THM impl + ## 1.9.494 ### Fixes From 3d2dcaf0593163e2605a425f45ffdbc102c3c2dd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Apr 2017 09:50:08 -0400 Subject: [PATCH 2295/4033] fix regression introduced by CLJS-1973 We only want to index by :file on :npm-deps, not on all foreign libs blindly --- src/main/clojure/cljs/closure.clj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 469ca305e..5e427e369 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1959,10 +1959,10 @@ :as opts}] (let [opts (cond-> (update opts :foreign-libs (fn [libs] - (into [] - (util/distinct-merge-by :file - (index-node-modules npm-deps opts) - (expand-libs libs))))) + (into (into [] + (util/distinct-merge-by :file + (index-node-modules npm-deps opts))) + (expand-libs libs)))) (:closure-defines opts) (assoc :closure-defines (into {} @@ -1981,10 +1981,10 @@ :optimizations optimizations :output-dir output-dir :ups-libs libs - :ups-foreign-libs (into [] - (util/distinct-merge-by :file - (index-node-modules (compute-upstream-npm-deps opts) opts) - (expand-libs foreign-libs))) + :ups-foreign-libs (into (into [] + (util/distinct-merge-by :file + (index-node-modules (compute-upstream-npm-deps opts) opts))) + (expand-libs foreign-libs)) :ups-externs externs :emit-constants emit-constants :cache-analysis-format (:cache-analysis-format opts :transit)) From b79e45a706201087f69df56647055c442dca7229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 14 Apr 2017 09:12:43 -0700 Subject: [PATCH 2296/4033] CLJS-2009: Remove unnecessary code introduced by CLJS-1973 --- src/main/clojure/cljs/closure.clj | 13 +++++-------- src/main/clojure/cljs/util.cljc | 14 -------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5e427e369..44e5116b5 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1959,9 +1959,7 @@ :as opts}] (let [opts (cond-> (update opts :foreign-libs (fn [libs] - (into (into [] - (util/distinct-merge-by :file - (index-node-modules npm-deps opts))) + (into (index-node-modules npm-deps opts) (expand-libs libs)))) (:closure-defines opts) (assoc :closure-defines @@ -1981,9 +1979,7 @@ :optimizations optimizations :output-dir output-dir :ups-libs libs - :ups-foreign-libs (into (into [] - (util/distinct-merge-by :file - (index-node-modules (compute-upstream-npm-deps opts) opts))) + :ups-foreign-libs (into (index-node-modules (compute-upstream-npm-deps opts) opts) (expand-libs foreign-libs)) :ups-externs externs :emit-constants emit-constants @@ -2115,14 +2111,15 @@ (:options @env/*compiler*)))) ([npm-deps opts] (let [node-modules (io/file "node_modules")] - (when (and (not (empty? npm-deps)) (.exists node-modules) (.isDirectory node-modules)) + (if (and (not (empty? npm-deps)) (.exists node-modules) (.isDirectory node-modules)) (let [modules (map name (keys npm-deps)) deps-file (io/file (str (util/output-directory opts) File/separator "cljs$node_modules.js"))] (util/mkdirs deps-file) (with-open [w (io/writer deps-file)] (run! #(.write w (str "require('" % "');\n")) modules)) - (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)))))) + (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)) + [])))) (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 435b2ab04..c8de4750e 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -279,20 +279,6 @@ xs seen)))] (step coll #{})))) -(defn distinct-merge-by - [f & xss] - (let [xf (map (fn [x] - [(f x) x]))] - (vals (apply merge-with - (fn [a b] - (merge-with - (fn [a b] - (cond-> a - (sequential? a) - (into b))) - a b)) - (map #(into {} xf %) xss))))) - (defn content-sha [^String s] (let [digest (MessageDigest/getInstance "SHA-1")] (.reset digest) From aa5f001300e9aebd976cb180f5b7ccb37fcb6898 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Apr 2017 12:24:04 -0400 Subject: [PATCH 2297/4033] 1.9.521 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f922cbea3..d5fbefb4a 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.518 +Latest stable release: 1.9.521 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.518"] +[org.clojure/clojurescript "1.9.521"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.518 org.clojure clojurescript - 1.9.518 + 1.9.521 ``` diff --git a/changes.md b/changes.md index c18fe9ee0..75b0355f3 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 1.9.521 + +### Fixes +* correct CLJS-1923 :foreign-libs regression + ## 1.9.518 ### Enhancements From c488bfed8d050d9825a7407dfc4bb813e2cdda85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 25 Apr 2017 15:58:32 -0700 Subject: [PATCH 2298/4033] CLJS-2017: Upgrade Closure Compiler to latest April 2017 release --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 3 +++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index a832b940b..d537d2100 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20170409 + v20170423 org.clojure diff --git a/project.clj b/project.clj index a4bdf5370..f4827f87c 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.285"] [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] - [com.google.javascript/closure-compiler-unshaded "v20170409"] + [com.google.javascript/closure-compiler-unshaded "v20170423"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 21e752a5f..335172edc 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,7 +3,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha14" -CLOSURE_RELEASE="20170409" +CLOSURE_RELEASE="20170423" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 44e5116b5..82e9634d7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2003,6 +2003,9 @@ (nil? (find (:closure-warnings opts) :check-types)) (assoc-in [:closure-warnings :check-types] :off) + (nil? (find (:closure-warnings opts) :check-variables)) + (assoc-in [:closure-warnings :check-variables] :off) + (nil? (:closure-module-roots opts)) (assoc :closure-module-roots [])))) From 3c0c775b8c731806765d425781d0bfd16c0d7f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 24 Apr 2017 15:39:21 -0700 Subject: [PATCH 2299/4033] CLJS-2015: Self-host: `defmacro` should return the Var --- src/main/clojure/cljs/core.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index dd6da0871..38c880df2 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3120,9 +3120,9 @@ (if p (recur (next p) (cons (first p) d)) d))] - (core/list 'do - (cons `defn decl) - (core/list 'set! `(. ~name ~'-cljs$lang$macro) true)))) + `(let [ret# ~(cons `defn decl)] + (set! (. ~name ~'-cljs$lang$macro) true) + ret#))) #?(:clj (. (var defmacro) (setMacro)) :cljs (set! (. defmacro -cljs$lang$macro) true)) From 837aeaf87b3b6eb7c6a48a12c699c98a21ecec37 Mon Sep 17 00:00:00 2001 From: "Kevin J. Lynagh" Date: Sun, 16 Apr 2017 15:34:05 -0700 Subject: [PATCH 2300/4033] CLJS-2007: Whitespace optimizations should respect :main option. --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 82e9634d7..8c27d7bcd 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2267,7 +2267,7 @@ warnings))) ana/*verbose* (:verbose opts)] (let [one-file? (and (:main all-opts) - (#{:advanced :simple} (:optimizations all-opts))) + (#{:advanced :simple :whitespace} (:optimizations all-opts))) source (if one-file? (let [main (:main all-opts) uri (:uri (cljs-source-for-namespace main))] From 9dafe7d87a4b4a387a82fce9e3a0d63bbe565e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 18 Apr 2017 13:38:01 -0700 Subject: [PATCH 2301/4033] CLJS-2010: refer-clojure :rename throws on valid invocations --- src/main/clojure/cljs/analyzer.cljc | 6 +++--- src/test/clojure/cljs/analyzer_tests.clj | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 75f64f39e..eedb4f39f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2058,7 +2058,7 @@ (let [refs (second fs)] (cond (not (or (and (= kw :exclude) (sequential? refs) (every? symbol? refs)) - (and (= kw :rename) (map? refs) (every? #(every? symbol? %) refs)))) + (and (= kw :rename) (map? refs) (every? #(every? symbol? %) refs)))) (recur fs ret true) (= kw :exclude) @@ -2066,7 +2066,7 @@ (= kw :rename) (recur (nnext fs) (update-in ret [:renames] merge refs) false))) - (recur fs ret true ))) + (recur fs ret true))) :else ret))] (merge-with into s xs))) @@ -2238,7 +2238,7 @@ (if (keyword? quoted-spec-or-kw) quoted-spec-or-kw (as-> (second quoted-spec-or-kw) spec - (if (vector? spec) spec [spec]))))] + (if (or (vector? spec) (map? spec)) spec [spec]))))] (map canonicalize specs))) (defn canonicalize-import-specs [specs] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 19eaebdc5..fc98091a7 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -490,7 +490,9 @@ (is (= (a/canonicalize-specs '(:require (quote [clojure.set :as set]))) '(:require [clojure.set :as set]))) (is (= (a/canonicalize-specs '(:require (quote clojure.set))) - '(:require [clojure.set])))) + '(:require [clojure.set]))) + (is (= (a/canonicalize-specs '(:refer-clojure :exclude '[map] :rename '{map core-map})) + '(:refer-clojure :exclude [map] :rename {map core-map})))) (deftest test-canonicalize-import-specs (is (= (a/canonicalize-import-specs '(:import (quote [goog Uri]))) @@ -556,6 +558,10 @@ Integer goog.math.Integer}))) (let [test-env (a/empty-env) parsed (a/analyze test-env '(refer-clojure :exclude '[map mapv]))] + (is (= (-> parsed :excludes) + '#{map mapv}))) + (let [test-env (a/empty-env) + parsed (a/analyze test-env '(refer-clojure :exclude '[map mapv] :rename '{mapv core-mapv}))] (is (= (-> parsed :excludes) '#{map mapv}))))) (testing "arguments to require should be quoted" @@ -861,4 +867,4 @@ (map (comp :externs second) (get @test-cenv ::a/namespaces))))))) - ) \ No newline at end of file + ) From 78891af8b68899e0e0456161116aeab23d1c031a Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Tue, 25 Apr 2017 21:04:12 +0100 Subject: [PATCH 2302/4033] CLJS-2013 - Add MapEntry type New additions: * A MapEntry type for use by PAM and PHM * -contains-key? from IAssociative protocol was missing from PersistentVector, RedNode and BlackNode Changes: * -nth on RedNode and BlackNode now throws on out of bounds * -invoke on RedNode and BlackNode now throws on out of bounds * -find on RedNode and BlackNode now returns correct value --- src/main/cljs/cljs/core.cljs | 122 +++++++++++++++++++-- src/test/cljs/cljs/map_entry_test.cljs | 146 +++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 + 3 files changed, 262 insertions(+), 8 deletions(-) create mode 100644 src/test/cljs/cljs/map_entry_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c552fd364..c588de28e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5161,6 +5161,10 @@ reduces them without incurring seq initialization" (if (number? k) (-assoc-n coll k v) (throw (js/Error. "Vector's key for assoc must be a number.")))) + (-contains-key? [coll k] + (if (integer? k) + (and (<= 0 k) (< k cnt)) + false)) IFind (-find [coll k] @@ -6134,6 +6138,98 @@ reduces them without incurring seq initialization" (declare TransientArrayMap) +(deftype MapEntry [key val ^:mutable __hash] + Object + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) + + IMapEntry + (-key [node] key) + (-val [node] val) + + IHash + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) + + IEquiv + (-equiv [coll other] (equiv-sequential coll other)) + + IMeta + (-meta [node] nil) + + IWithMeta + (-with-meta [node meta] + (with-meta [key val] meta)) + + IStack + (-peek [node] val) + + (-pop [node] [key]) + + ICollection + (-conj [node o] [key val o]) + + IEmptyableCollection + (-empty [node] []) + + ISequential + ISeqable + (-seq [node] (list key val)) + + ICounted + (-count [node] 2) + + IIndexed + (-nth [node n] + (cond (== n 0) key + (== n 1) val + :else (throw (js/Error. "Index out of bounds")))) + + (-nth [node n not-found] + (cond (== n 0) key + (== n 1) val + :else not-found)) + + ILookup + (-lookup [node k] (-nth node k nil)) + (-lookup [node k not-found] (-nth node k not-found)) + + IAssociative + (-assoc [node k v] + (assoc [key val] k v)) + (-contains-key? [node k] + (or (== k 0) (== k 1))) + + IFind + (-find [node k] + (cond + (== k 0) [0 key] + (== k 1) [1 val] + :else nil)) + + IVector + (-assoc-n [node n v] + (-assoc-n [key val] n v)) + + IReduce + (-reduce [node f] + (ci-reduce node f)) + + (-reduce [node f start] + (ci-reduce node f start)) + + IFn + (-invoke [node k] + (-nth node k)) + + (-invoke [node k not-found] + (-nth node k not-found))) + (deftype PersistentArrayMapSeq [arr i _meta] Object (toString [coll] @@ -7754,7 +7850,7 @@ reduces them without incurring seq initialization" (-nth [node n] (cond (== n 0) key (== n 1) val - :else nil)) + :else (throw (js/Error. "Index out of bounds")))) (-nth [node n not-found] (cond (== n 0) key @@ -7768,10 +7864,15 @@ reduces them without incurring seq initialization" IAssociative (-assoc [node k v] (assoc [key val] k v)) + (-contains-key? [node k] + (or (== k 0) (== k 1))) IFind (-find [node k] - [key val]) + (cond + (== k 0) [0 key] + (== k 1) [1 val] + :else nil)) IVector (-assoc-n [node n v] @@ -7786,10 +7887,10 @@ reduces them without incurring seq initialization" IFn (-invoke [node k] - (-lookup node k)) + (-nth node k)) (-invoke [node k not-found] - (-lookup node k not-found))) + (-nth node k not-found))) (es6-iterable BlackNode) @@ -7910,7 +8011,7 @@ reduces them without incurring seq initialization" (-nth [node n] (cond (== n 0) key (== n 1) val - :else nil)) + :else (throw (js/Error. "Index out of bounds")))) (-nth [node n not-found] (cond (== n 0) key @@ -7924,10 +8025,15 @@ reduces them without incurring seq initialization" IAssociative (-assoc [node k v] (assoc [key val] k v)) + (-contains-key? [node k] + (or (== k 0) (== k 1))) IFind (-find [node k] - [key val]) + (cond + (== k 0) [0 key] + (== k 1) [1 val] + :else nil)) IVector (-assoc-n [node n v] @@ -7942,10 +8048,10 @@ reduces them without incurring seq initialization" IFn (-invoke [node k] - (-lookup node k)) + (-nth node k)) (-invoke [node k not-found] - (-lookup node k not-found))) + (-nth node k not-found))) (es6-iterable RedNode) diff --git a/src/test/cljs/cljs/map_entry_test.cljs b/src/test/cljs/cljs/map_entry_test.cljs new file mode 100644 index 000000000..a07738426 --- /dev/null +++ b/src/test/cljs/cljs/map_entry_test.cljs @@ -0,0 +1,146 @@ +;; 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.map-entry-test + (:refer-clojure :exclude [iter]) + (:require [cljs.test :refer-macros [deftest testing is are]])) + +(defn map-entry-interface-tests + "Tests that a MapEntry implements all the expected interfaces correctly. + Expects a MapEntry type with key `:key` and a val `:val`." + [e] + (testing "map entry interfaces" + (testing "IMapEntry" + (testing "-key" + (is (= :key (-key e)))) + (testing "-val" + (is (= :val (-val e))))) + + (testing "IEquiv" + (testing "-equiv" + (are [x y] (-equiv x y) + e [:key :val] + e '(:key :val)))) + + (testing "ILookup" + (testing "-lookup 2-arity" + (are [x y] (= x y) + :key (-lookup e 0) + :val (-lookup e 1) + nil (-lookup e 2) + nil (-lookup e -1))) + (testing "-lookup 3-arity" + (are [x y] (= x y) + :key (-lookup e 0 :not-found) + :val (-lookup e 1 :not-found) + :not-found (-lookup e 2 :not-found) + :not-found (-lookup e -1 :not-found)))) + + (testing "IStack" + (testing "-peek" + (is (= :val (-peek e)))) + (testing "-pop" + (is (vector? (-pop e))) + (is (= [:key] (-pop e))))) + + (testing "ICollection" + (testing "-conj" + (is (vector? (-conj e :thing))) + (is (= [:key :val :thing] (-conj e :thing))))) + + (testing "IEmptyableCollection" + (testing "-empty" + (is (= [] (empty e))))) + + (testing "ISequential" + (is (satisfies? ISequential e))) + + (testing "ISeqable" + (testing "-seq" + (is (= (list :key :val) (-seq e))))) + + (testing "ICounted" + (testing "-count" + (is (= 2 (-count e))))) + + (testing "IIndexed" + (testing "-nth 2-arity" + (are [x y] (= x y) + :key (-nth e 0) + :val (-nth e 1)) + (is (thrown? js/Error (-nth e 2))) + (is (thrown? js/Error (-nth e -1)))) + (testing "-nth 3-arity" + (are [x y] (= x y) + :key (-nth e 0 :not-found) + :val (-nth e 1 :not-found) + :not-found (-nth e 2 :not-found) + :not-found (-nth e -1 :not-found)))) + + (testing "IAssociative" + (testing "-assoc" + (are [x y] (= x y) + [:new :val] (-assoc e 0 :new) + [:key :new] (-assoc e 1 :new) + [:key :val :new] (-assoc e 2 :new))) + (testing "-contains-key?" + (are [x y] (= x y) + true (-contains-key? e 0) + true (-contains-key? e 1) + false (-contains-key? e 2) + false (-contains-key? e -1)))) + + (testing "IVector" + (testing "-assoc-n" + (are [x y] (= x y) + [:new :val] (-assoc-n e 0 :new) + [:key :new] (-assoc-n e 1 :new) + [:key :val :new] (-assoc-n e 2 :new)))) + + (testing "IReduce" + (testing "-reduce 2-arity" + (is (= [:key :val] (-reduce e (fn [r e] [r e]))))) + (testing "-reduce 3-arity" + (is (= [:key :val] (-reduce e conj []))))) + + (testing "IFind" + (testing "-find" + (are [x y] (= x y) + [0 :key] (-find e 0) + [1 :val] (-find e 1) + ;; Commented out as unsure about contract of -find + ;; in the case where key is not present. + ;nil (-find e 2) + ;nil (-find e -1) + ;; So testing `find` in this case instead as contract is clear. + nil (find e 2) + nil (find e -1)))) + + (testing "IFn" + (testing "-invoke 2-arity" + (are [x y] (= x y) + :key (e 0) + :val (e 1)) + (is (thrown? js/Error (e 2))) + (is (thrown? js/Error (e -1)))) + (testing "-invoke 3-arity" + (are [x y] (= x y) + :key (e 0 :not-found) + :val (e 1 :not-found) + :not-found (e 2 :not-found) + :not-found (e -1 :not-found)))))) + +(deftest all-map-entry-tests + (testing "BlackNode" + (map-entry-interface-tests (BlackNode. :key :val nil nil nil))) + (testing "RedNode" + (map-entry-interface-tests (RedNode. :key :val nil nil nil))) + (testing "Vector" + (map-entry-interface-tests [:key :val])) + (testing "MapEntry" + (map-entry-interface-tests (MapEntry. :key :val nil)))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index e5b311825..ab979d089 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -37,6 +37,7 @@ [cljs.spec.test-test] [cljs.clojure-alias-test] [cljs.hash-map-test] + [cljs.map-entry-test] [cljs.predicates-test] [cljs.tagged-literals-test] [cljs.test-test] @@ -73,6 +74,7 @@ 'cljs.spec.test-test 'cljs.clojure-alias-test 'cljs.hash-map-test + 'cljs.map-entry-test 'cljs.pprint-test 'cljs.predicates-test 'cljs.syntax-quote-test From dda03f506866c6d7bf5fad22a7025802effbae27 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 May 2017 14:57:38 -0400 Subject: [PATCH 2303/4033] cljs.spec -> cljs.spec.alpha --- src/main/cljs/cljs/spec/alpha.cljc | 548 +++++++ src/main/cljs/cljs/spec/alpha.cljs | 1444 +++++++++++++++++++ src/main/cljs/cljs/spec/impl/gen/alpha.cljc | 75 + src/main/cljs/cljs/spec/impl/gen/alpha.cljs | 180 +++ src/main/cljs/cljs/spec/test/alpha.cljc | 251 ++++ src/main/cljs/cljs/spec/test/alpha.cljs | 361 +++++ src/test/cljs/cljs/clojure_alias_test.cljs | 4 +- src/test/cljs/cljs/spec/test_test.cljs | 4 +- src/test/cljs/cljs/spec_test.cljs | 20 +- 9 files changed, 2873 insertions(+), 14 deletions(-) create mode 100644 src/main/cljs/cljs/spec/alpha.cljc create mode 100644 src/main/cljs/cljs/spec/alpha.cljs create mode 100644 src/main/cljs/cljs/spec/impl/gen/alpha.cljc create mode 100644 src/main/cljs/cljs/spec/impl/gen/alpha.cljs create mode 100644 src/main/cljs/cljs/spec/test/alpha.cljc create mode 100644 src/main/cljs/cljs/spec/test/alpha.cljs diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc new file mode 100644 index 000000000..d401bb030 --- /dev/null +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -0,0 +1,548 @@ +; 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.spec.alpha + (:refer-clojure :exclude [+ * and or cat def keys merge resolve assert]) + (:require [cljs.core :as c] + [cljs.analyzer :as ana] + [cljs.env :as env] + [cljs.analyzer.api :refer [resolve]] + [clojure.walk :as walk] + [cljs.spec.impl.gen.alpha :as gen] + [clojure.string :as str])) + +(defonce registry-ref (atom {})) + +(defn- ->sym + "Returns a symbol from a symbol or var" + [x] + (if (map? x) + (:name x) + x)) + +(defn- unfn [expr] + (if (clojure.core/and (seq? expr) + (symbol? (first expr)) + (= "fn*" (name (first expr)))) + (let [[[s] & form] (rest expr)] + (conj (walk/postwalk-replace {s '%} form) '[%] 'fn)) + expr)) + +(defn- res [env form] + (cond + (keyword? form) form + (symbol? form) (clojure.core/or (->> form (resolve env) ->sym) form) + (sequential? form) (walk/postwalk #(if (symbol? %) (res env %) %) (unfn form)) + :else form)) + +(defmacro ^:private mres + "a compile time res, for use in cljs/spec.cljs" + [form] + (res &env form)) + +(defn- ns-qualify + "Qualify symbol s by resolving it or using the current *ns*." + [env s] + (if (namespace s) + (let [v (resolve env s)] + (clojure.core/assert v (str "Unable to resolve: " s)) + (->sym v)) + (symbol (str ana/*cljs-ns*) (str s)))) + +(defmacro def + "Given a namespace-qualified keyword or resolveable symbol k, and a spec, + spec-name, predicate or regex-op makes an entry in the registry mapping k to + the spec" + [k spec-form] + (let [k (if (symbol? k) (ns-qualify &env k) k) + form (res &env spec-form)] + (swap! registry-ref assoc k form) + `(def-impl '~k '~form ~spec-form))) + +(defmacro spec + "Takes a single predicate form, e.g. can be the name of a predicate, + like even?, or a fn literal like #(< % 42). Note that it is not + generally necessary to wrap predicates in spec when using the rest + of the spec macros, only to attach a unique generator + + Can also be passed the result of one of the regex ops - + cat, alt, *, +, ?, in which case it will return a regex-conforming + spec, useful when nesting an independent regex. + --- + + Optionally takes :gen generator-fn, which must be a fn of no args that + returns a test.check generator. + + Returns a spec." + [form & {:keys [gen]}] + (when form + `(spec-impl '~(res &env form) ~form ~gen nil))) + +(defmacro multi-spec + "Takes the name of a spec/predicate-returning multimethod and a + tag-restoring keyword or fn (retag). Returns a spec that when + conforming or explaining data will pass it to the multimethod to get + an appropriate spec. You can e.g. use multi-spec to dynamically and + extensibly associate specs with 'tagged' data (i.e. data where one + of the fields indicates the shape of the rest of the structure). + + (defmulti mspec :tag) + + The methods should ignore their argument and return a predicate/spec: + (defmethod mspec :int [_] (s/keys :req-un [::tag ::i])) + + retag is used during generation to retag generated values with + matching tags. retag can either be a keyword, at which key the + dispatch-tag will be assoc'ed, or a fn of generated value and + dispatch-tag that should return an appropriately retagged value. + + Note that because the tags themselves comprise an open set, + the tag key spec cannot enumerate the values, but can e.g. + test for keyword?. + + Note also that the dispatch values of the multimethod will be + included in the path, i.e. in reporting and gen overrides, even + though those values are not evident in the spec. +" + [mm retag] + `(multi-spec-impl '~(res &env mm) (var ~mm) ~retag)) + +(defmacro keys + "Creates and returns a map validating spec. :req and :opt are both + vectors of namespaced-qualified keywords. The validator will ensure + the :req keys are present. The :opt keys serve as documentation and + may be used by the generator. + + The :req key vector supports 'and' and 'or' for key groups: + + (s/keys :req [::x ::y (or ::secret (and ::user ::pwd))] :opt [::z]) + + There are also -un versions of :req and :opt. These allow + you to connect unqualified keys to specs. In each case, fully + qualfied keywords are passed, which name the specs, but unqualified + keys (with the same name component) are expected and checked at + conform-time, and generated during gen: + + (s/keys :req-un [:my.ns/x :my.ns/y]) + + The above says keys :x and :y are required, and will be validated + and generated by specs (if they exist) named :my.ns/x :my.ns/y + respectively. + + In addition, the values of *all* namespace-qualified keys will be validated + (and possibly destructured) by any registered specs. Note: there is + no support for inline value specification, by design. + + Optionally takes :gen generator-fn, which must be a fn of no args that + returns a test.check generator." + [& {:keys [req req-un opt opt-un gen]}] + (let [unk #(-> % name keyword) + req-keys (filterv keyword? (flatten req)) + req-un-specs (filterv keyword? (flatten req-un)) + _ (clojure.core/assert (every? #(clojure.core/and (keyword? %) (namespace %)) (concat req-keys req-un-specs opt opt-un)) + "all keys must be namespace-qualified keywords") + req-specs (into req-keys req-un-specs) + req-keys (into req-keys (map unk req-un-specs)) + opt-keys (into (vec opt) (map unk opt-un)) + opt-specs (into (vec opt) opt-un) + gx (gensym) + parse-req (fn [rk f] + (map (fn [x] + (if (keyword? x) + `(contains? ~gx ~(f x)) + (walk/postwalk + (fn [y] (if (keyword? y) `(contains? ~gx ~(f y)) y)) + x))) + rk)) + pred-exprs [`(map? ~gx)] + pred-exprs (into pred-exprs (parse-req req identity)) + pred-exprs (into pred-exprs (parse-req req-un unk)) + keys-pred `(fn* [~gx] (c/and ~@pred-exprs)) + pred-exprs (mapv (fn [e] `(fn* [~gx] ~e)) pred-exprs) + pred-forms (walk/postwalk #(res &env %) pred-exprs)] + ;; `(map-spec-impl ~req-keys '~req ~opt '~pred-forms ~pred-exprs ~gen) + `(map-spec-impl {:req '~req :opt '~opt :req-un '~req-un :opt-un '~opt-un + :req-keys '~req-keys :req-specs '~req-specs + :opt-keys '~opt-keys :opt-specs '~opt-specs + :pred-forms '~pred-forms + :pred-exprs ~pred-exprs + :keys-pred ~keys-pred + :gfn ~gen}))) + +(defmacro or + "Takes key+pred pairs, e.g. + + (s/or :even even? :small #(< % 42)) + + Returns a destructuring spec that returns a map entry containing the + key of the first matching pred and the corresponding value. Thus the + 'key' and 'val' functions can be used to refer generically to the + components of the tagged return." + [& key-pred-forms] + (let [pairs (partition 2 key-pred-forms) + keys (mapv first pairs) + pred-forms (mapv second pairs) + pf (mapv #(res &env %) pred-forms)] + (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") + `(or-spec-impl ~keys '~pf ~pred-forms nil))) + +(defmacro and + "Takes predicate/spec-forms, e.g. + + (s/and even? #(< % 42)) + + Returns a spec that returns the conformed value. Successive + conformed values propagate through rest of predicates." + [& pred-forms] + `(and-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) + +(defn- res-kind + [env opts] + (let [{kind :kind :as mopts} opts] + (->> + (if kind + (assoc mopts :kind `~(res env kind)) + mopts) + (mapcat identity)))) + +(defmacro every + "takes a pred and validates collection elements against that pred. + + Note that 'every' does not do exhaustive checking, rather it samples + *coll-check-limit* elements. Nor (as a result) does it do any + conforming of elements. 'explain' will report at most *coll-error-limit* + problems. Thus 'every' should be suitable for potentially large + collections. + + Takes several kwargs options that further constrain the collection: + + :kind - a pred/spec that the collection type must satisfy, e.g. vector? + (default nil) Note that if :kind is specified and :into is + not, this pred must generate in order for every to generate. + :count - specifies coll has exactly this count (default nil) + :min-count, :max-count - coll has count (<= min-count count max-count) (defaults nil) + :distinct - all the elements are distinct (default nil) + + And additional args that control gen + + :gen-max - the maximum coll size to generate (default 20) + :into - one of [], (), {}, #{} - the default collection to generate into + (default same as :kind if supplied, else [] + + Optionally takes :gen generator-fn, which must be a fn of no args that + returns a test.check generator + + See also - coll-of, every-kv +" + [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] + (let [desc (::describe opts) + nopts (-> opts + (dissoc :gen ::describe) + (assoc ::kind-form `'~(res &env (:kind opts)) + ::describe (clojure.core/or desc `'(every ~(res &env pred) ~@(res-kind &env opts))))) + gx (gensym) + cpreds (cond-> [(list (clojure.core/or kind `coll?) gx)] + count (conj `(= ~count (c/bounded-count ~count ~gx))) + + (clojure.core/or min-count max-count) + (conj `(<= (c/or ~min-count 0) + (c/bounded-count (if ~max-count (inc ~max-count) ~min-count) ~gx) + (c/or ~max-count MAX_INT))) + + distinct + (conj `(c/or (empty? ~gx) (apply distinct? ~gx))))] + `(every-impl '~pred ~pred ~(assoc nopts ::cpred `(fn* [~gx] (c/and ~@cpreds))) ~gen))) + +(defmacro every-kv + "like 'every' but takes separate key and val preds and works on associative collections. + + Same options as 'every', :into defaults to {} + + See also - map-of" + + [kpred vpred & opts] + (let [desc `(every-kv ~(res &env kpred) ~(res &env vpred) ~@(res-kind &env opts))] + `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (nth v# 0)) :into {} ::describe '~desc ~@opts))) + +(defmacro coll-of + "Returns a spec for a collection of items satisfying pred. Unlike + generator will fill an empty init-coll. + + Same options as 'every'. conform will produce a collection + corresponding to :into if supplied, else will match the input collection, + avoiding rebuilding when possible. + + Same options as 'every'. + + See also - every, map-of" + [pred & opts] + (let [desc `(coll-of ~(res &env pred) ~@(res-kind &env opts))] + `(every ~pred ::conform-all true ::describe '~desc ~@opts))) + +(defmacro map-of + "Returns a spec for a map whose keys satisfy kpred and vals satisfy + vpred. Unlike 'every-kv', map-of will exhaustively conform every + value. + + Same options as 'every', :kind defaults to map?, with the addition of: + + :conform-keys - conform keys as well as values (default false) + + See also - every-kv" + [kpred vpred & opts] + (let [desc `(map-of ~(res &env kpred) ~(res &env vpred) ~@(res-kind &env opts))] + `(every-kv ~kpred ~vpred ::conform-all true :kind map? ::describe '~desc ~@opts))) + +(defmacro * + "Returns a regex op that matches zero or more values matching + pred. Produces a vector of matches iff there is at least one match" + [pred-form] + `(rep-impl '~(res &env pred-form) ~pred-form)) + +(defmacro + + "Returns a regex op that matches one or more values matching + pred. Produces a vector of matches" + [pred-form] + `(rep+impl '~(res &env pred-form) ~pred-form)) + +(defmacro ? + "Returns a regex op that matches zero or one value matching + pred. Produces a single value (not a collection) if matched." + [pred-form] + `(maybe-impl ~pred-form '~pred-form)) + +(defmacro alt + "Takes key+pred pairs, e.g. + + (s/alt :even even? :small #(< % 42)) + + Returns a regex op that returns a map entry containing the key of the + first matching pred and the corresponding value. Thus the + 'key' and 'val' functions can be used to refer generically to the + components of the tagged return." + [& key-pred-forms] + (let [pairs (partition 2 key-pred-forms) + keys (mapv first pairs) + pred-forms (mapv second pairs) + pf (mapv #(res &env %) pred-forms)] + (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") + `(alt-impl ~keys ~pred-forms '~pf))) + +(defmacro cat + "Takes key+pred pairs, e.g. + + (s/cat :e even? :o odd?) + + Returns a regex op that matches (all) values in sequence, returning a map + containing the keys of each pred and the corresponding value." + [& key-pred-forms] + (let [pairs (partition 2 key-pred-forms) + keys (mapv first pairs) + pred-forms (mapv second pairs) + pf (mapv #(res &env %) pred-forms)] + ;;(prn key-pred-forms) + (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") + `(cat-impl ~keys ~pred-forms '~pf))) + +(defmacro & + "takes a regex op re, and predicates. Returns a regex-op that consumes + input as per re but subjects the resulting value to the + conjunction of the predicates, and any conforming they might perform." + [re & preds] + (let [pv (vec preds)] + `(amp-impl ~re ~pv '~(mapv #(res &env %) pv)))) + +(defmacro conformer + "takes a predicate function with the semantics of conform i.e. it should return either a + (possibly converted) value or :cljs.spec.alpha/invalid, and returns a + spec that uses it as a predicate/conformer. Optionally takes a + second fn that does unform of result of first" + ([f] `(spec-impl '(conformer ~(res &env f)) ~f nil true)) + ([f unf] `(spec-impl '(conformer ~(res &env f) ~(res &env unf)) ~f nil true ~unf))) + +(defmacro fspec + "takes :args :ret and (optional) :fn kwargs whose values are preds + and returns a spec whose conform/explain take a fn and validates it + using generative testing. The conformed value is always the fn itself. + + See 'fdef' for a single operation that creates an fspec and + registers it, as well as a full description of :args, :ret and :fn + + fspecs can generate functions that validate the arguments and + fabricate a return value compliant with the :ret spec, ignoring + the :fn spec if present. + + Optionally takes :gen generator-fn, which must be a fn of no args + that returns a test.check generator." + [& {:keys [args ret fn gen]}] + (let [env &env] + `(fspec-impl (spec ~args) '~(res env args) + (spec ~ret) '~(res env ret) + (spec ~fn) '~(res env fn) ~gen))) + +(defmacro tuple + "takes one or more preds and returns a spec for a tuple, a vector + where each element conforms to the corresponding pred. Each element + will be referred to in paths using its ordinal." + [& preds] + (clojure.core/assert (not (empty? preds))) + `(tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) + +(def ^:private _speced_vars (atom #{})) + +(defn speced-vars [] + @_speced_vars) + +(defmacro fdef + "Takes a symbol naming a function, and one or more of the following: + + :args A regex spec for the function arguments as they were a list to be + passed to apply - in this way, a single spec can handle functions with + multiple arities + :ret A spec for the function's return value + :fn A spec of the relationship between args and ret - the + value passed is {:args conformed-args :ret conformed-ret} and is + expected to contain predicates that relate those values + + Qualifies fn-sym with resolve, or using *ns* if no resolution found. + Registers an fspec in the global registry, where it can be retrieved + by calling get-spec with the var or full-qualified symbol. + + Once registered, function specs are included in doc, checked by + instrument, tested by the runner clojure.spec.test/run-tests, and (if + a macro) used to explain errors during macroexpansion. + + Note that :fn specs require the presence of :args and :ret specs to + conform values, and so :fn specs will be ignored if :args or :ret + are missing. + + Returns the qualified fn-sym. + + For example, to register function specs for the symbol function: + + (s/fdef clojure.core/symbol + :args (s/alt :separate (s/cat :ns string? :n string?) + :str string? + :sym symbol?) + :ret symbol?)" + [fn-sym & specs] + (swap! _speced_vars conj (ns-qualify &env fn-sym)) + `(cljs.spec.alpha/def ~fn-sym (fspec ~@specs))) + +(defmacro keys* + "takes the same arguments as spec/keys and returns a regex op that matches sequences of key/values, + converts them into a map, and conforms that map with a corresponding + spec/keys call: + + user=> (s/conform (s/keys :req-un [::a ::c]) {:a 1 :c 2}) + {:a 1, :c 2} + user=> (s/conform (s/keys* :req-un [::a ::c]) [:a 1 :c 2]) + {:a 1, :c 2} + + the resulting regex op can be composed into a larger regex: + + user=> (s/conform (s/cat :i1 integer? :m (s/keys* :req-un [::a ::c]) :i2 integer?) [42 :a 1 :c 2 :d 4 99]) + {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" + [& kspecs] + `(let [mspec# (keys ~@kspecs)] + (with-gen (cljs.spec.alpha/& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) + (fn [] (gen/fmap (fn [m#] (apply concat m#)) (gen mspec#)))))) + +(defmacro nilable + "returns a spec that accepts nil and values satisfiying pred" + [pred] + (let [pf (res &env pred)] + `(nilable-impl '~pf ~pred nil))) + +(defmacro inst-in + "Returns a spec that validates insts in the range from start + (inclusive) to end (exclusive)." + [start end] + `(let [st# (cljs.core/inst-ms ~start) + et# (cljs.core/inst-ms ~end) + mkdate# (fn [d#] (js/Date. d#))] + (spec (and cljs.core/inst? #(inst-in-range? ~start ~end %)) + :gen (fn [] + (gen/fmap mkdate# + (gen/large-integer* {:min st# :max et#})))))) + +(defmacro int-in + "Returns a spec that validates longs in the range from start + (inclusive) to end (exclusive)." + [start end] + `(spec (and c/int? #(int-in-range? ~start ~end %)) + :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) + +(defmacro double-in + "Specs a 64-bit floating point number. Options: + + :infinite? - whether +/- infinity allowed (default true) + :NaN? - whether NaN allowed (default true) + :min - minimum value (inclusive, default none) + :max - maximum value (inclusive, default none)" + [& {:keys [infinite? NaN? min max] + :or {infinite? true NaN? true} + :as m}] + `(spec (and c/double? + ~@(when-not infinite? '[#(not (infinite? %))]) + ~@(when-not NaN? '[#(not (js/isNaN %))]) + ~@(when max `[#(<= % ~max)]) + ~@(when min `[#(<= ~min %)])) + :gen #(gen/double* ~m))) + +(defmacro merge + "Takes map-validating specs (e.g. 'keys' specs) and + returns a spec that returns a conformed map satisfying all of the + specs. Successive conformed values propagate through rest of + predicates. Unlike 'and', merge can generate maps satisfying the + union of the predicates." + [& pred-forms] + `(merge-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) + +(defmacro exercise-fn + "exercises the fn named by sym (a symbol) by applying it to + n (default 10) generated samples of its args spec. When fspec is + supplied its arg spec is used, and sym-or-f can be a fn. Returns a + sequence of tuples of [args ret]. " + ([sym] + `(exercise-fn ~sym 10)) + ([sym n] + `(exercise-fn ~sym ~n nil)) + ([sym n fspec] + (let [sym (cond-> sym + (clojure.core/and (sequential? sym) + (= (first sym) 'quote)) + second)] + `(let [fspec# ~(if-not fspec + `(get-spec '~(:name (resolve &env sym))) + fspec) + f# ~sym] + (for [args# (gen/sample (gen (:args fspec#)) ~n)] + [args# (apply f# args#)]))))) + +(defmacro ^:private init-compile-asserts [] + (let [compile-asserts (not (-> env/*compiler* deref :options :elide-asserts))] + compile-asserts)) + +(defmacro assert + "spec-checking assert expression. Returns x if x is valid? according +to spec, else throws an error with explain-data plus ::failure of +:assertion-failed. +Can be disabled at either compile time or runtime: +If *compile-asserts* is false at compile time, compiles to x. Defaults +to the negation value of the ':elide-asserts' compiler option, or true if +not set. +If (check-asserts?) is false at runtime, always returns x. Defaults to +value of 'cljs.spec.alpha/*runtime-asserts*', or false if not set. You can +toggle check-asserts? with (check-asserts bool)." + [spec x] + `(if *compile-asserts* + (if *runtime-asserts* + (assert* ~spec ~x) + ~x) + ~x)) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs new file mode 100644 index 000000000..de2566ecf --- /dev/null +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -0,0 +1,1444 @@ +; 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.spec.alpha + (:refer-clojure :exclude [+ * and or cat def keys merge]) + (:require-macros [cljs.core :as c] + [cljs.spec.alpha :as s]) + (:require [goog.object :as gobj] + [cljs.core :as c] + [clojure.walk :as walk] + [cljs.spec.impl.gen.alpha :as gen] + [clojure.string :as str])) + +(def ^:const MAX_INT 9007199254740991) + +(def ^:dynamic *recursion-limit* + "A soft limit on how many times a branching spec (or/alt/*/opt-keys/multi-spec) + can be recursed through during generation. After this a + non-recursive branch will be chosen." + 4) + +(def ^:dynamic *fspec-iterations* + "The number of times an anonymous fn specified by fspec will be (generatively) tested during conform" + 21) + +(def ^:dynamic *coll-check-limit* + "The number of items validated in a collection spec'ed with 'every'" + 101) + +(def ^:dynamic *coll-error-limit* + "The number of errors reported by explain in a collection spec'ed with 'every'" + 20) + +(defprotocol Spec + (conform* [spec x]) + (unform* [spec y]) + (explain* [spec path via in x]) + (gen* [spec overrides path rmap]) + (with-gen* [spec gfn]) + (describe* [spec])) + +(defonce ^:private registry-ref (atom {})) + +(defn- deep-resolve [reg k] + (loop [spec k] + (if (ident? spec) + (recur (get reg spec)) + spec))) + +(defn- reg-resolve + "returns the spec/regex at end of alias chain starting with k, nil if not found, k if k not ident" + [k] + (if (ident? k) + (let [reg @registry-ref + spec (get reg k)] + (if-not (ident? spec) + spec + (deep-resolve reg spec))) + k)) + +(defn- reg-resolve! + "returns the spec/regex at end of alias chain starting with k, throws if not found, k if k not ident" + [k] + (if (ident? k) + (c/or (reg-resolve k) + (throw (js/Error. (str "Unable to resolve spec: " k)))) + k)) + +(defn spec? + "returns x if x is a spec object, else logical false" + [x] + (when (implements? Spec x) + x)) + +(defn regex? + "returns x if x is a (clojure.spec) regex op, else logical false" + [x] + (c/and (::op x) x)) + +(defn- with-name [spec name] + (cond + (ident? spec) spec + (regex? spec) (assoc spec ::name name) + + (implements? IMeta spec) + (with-meta spec (assoc (meta spec) ::name name)))) + +(defn- spec-name [spec] + (cond + (ident? spec) spec + + (regex? spec) (::name spec) + + (implements? IMeta spec) + (-> (meta spec) ::name))) + +(declare spec-impl) +(declare regex-spec-impl) + +(defn- maybe-spec + "spec-or-k must be a spec, regex or resolvable kw/sym, else returns nil." + [spec-or-k] + (let [s (c/or (c/and (ident? spec-or-k) (reg-resolve spec-or-k)) + (spec? spec-or-k) + (regex? spec-or-k) + nil)] + (if (regex? s) + (with-name (regex-spec-impl s nil) (spec-name s)) + s))) + +(defn- the-spec + "spec-or-k must be a spec, regex or kw/sym, else returns nil. Throws if unresolvable kw/sym" + [spec-or-k] + (c/or (maybe-spec spec-or-k) + (when (ident? spec-or-k) + (throw (js/Error. (str "Unable to resolve spec: " spec-or-k)))))) + +(defprotocol Specize + (specize* [_] [_ form])) + +(extend-protocol Specize + Keyword + (specize* ([k] (specize* (reg-resolve! k))) + ([k _] (specize* (reg-resolve! k)))) + + Symbol + (specize* ([s] (specize* (reg-resolve! s))) + ([s _] (specize* (reg-resolve! s)))) + + default + (specize* + ([o] (spec-impl ::unknown o nil nil)) + ([o form] (spec-impl form o nil nil)))) + +(defn- specize + ([s] (c/or (spec? s) (specize* s))) + ([s form] (c/or (spec? s) (specize* s form)))) + +(defn invalid? + "tests the validity of a conform return value" + [ret] + (keyword-identical? ::invalid ret)) + +(defn conform + "Given a spec and a value, returns :clojure.spec/invalid if value does not match spec, + else the (possibly destructured) value." + [spec x] + (conform* (specize spec) x)) + +(defn unform + "Given a spec and a value created by or compliant with a call to + 'conform' with the same spec, returns a value with all conform + destructuring undone." + [spec x] + (unform* (specize spec) x)) + +(defn form + "returns the spec as data" + [spec] + ;;TODO - incorporate gens + (describe* (specize spec))) + +(defn abbrev [form] + (cond + (seq? form) + (walk/postwalk (fn [form] + (cond + (c/and (symbol? form) (namespace form)) + (-> form name symbol) + + (c/and (seq? form) (= 'fn (first form)) (= '[%] (second form))) + (last form) + + :else form)) + form) + + (c/and (symbol? form) (namespace form)) + (-> form name symbol) + + :else form)) + +(defn describe + "returns an abbreviated description of the spec as data" + [spec] + (abbrev (form spec))) + +(defn with-gen + "Takes a spec and a no-arg, generator-returning fn and returns a version of that spec that uses that generator" + [spec gen-fn] + (let [spec (reg-resolve spec)] + (if (regex? spec) + (assoc spec ::gfn gen-fn) + (with-gen* (specize spec) gen-fn)))) + +(defn explain-data* [spec path via in x] + (when-let [probs (explain* (specize spec) path via in x)] + (when-not (empty? probs) + {::problems probs}))) + +(defn explain-data + "Given a spec and a value x which ought to conform, returns nil if x + conforms, else a map with at least the key ::problems whose value is + a collection of problem-maps, where problem-map has at least :path :pred and :val + keys describing the predicate and the value that failed at that + path." + [spec x] + (explain-data* spec [] (if-let [name (spec-name spec)] [name] []) [] x)) + +(defn explain-printer + "Default printer for explain-data. nil indicates a successful validation." + [ed] + (if ed + (print + (with-out-str + ;;(prn {:ed ed}) + (doseq [{:keys [path pred val reason via in] :as prob} (::problems ed)] + (when-not (empty? in) + (print "In:" (pr-str in) "")) + (print "val: ") + (pr val) + (print " fails") + (when-not (empty? via) + (print " spec:" (pr-str (last via)))) + (when-not (empty? path) + (print " at:" (pr-str path))) + (print " predicate: ") + (pr (abbrev pred)) + (when reason (print ", " reason)) + (doseq [[k v] prob] + (when-not (#{:path :pred :val :reason :via :in} k) + (print "\n\t" (pr-str k) " ") + (pr v))) + (newline)) + (doseq [[k v] ed] + (when-not (#{::problems} k) + (print (pr-str k) " ") + (pr v) + (newline))))) + (println "Success!"))) + +(def ^:dynamic *explain-out* explain-printer) + +(defn explain-out + "Prints explanation data (per 'explain-data') to *out* using the printer in *explain-out*, + by default explain-printer." + [ed] + (*explain-out* ed)) + +(defn explain + "Given a spec and a value that fails to conform, prints an explanation to *out*." + [spec x] + (explain-out (explain-data spec x))) + +(defn explain-str + "Given a spec and a value that fails to conform, returns an explanation as a string." + [spec x] + (with-out-str (explain spec x))) + +(declare valid?) + +(defn- gensub + [spec overrides path rmap form] + ;;(prn {:spec spec :over overrides :path path :form form}) + (let [spec (specize spec)] + (if-let [g (c/or (when-let [gfn (c/or (get overrides (c/or (spec-name spec) spec)) + (get overrides path))] + (gfn)) + (gen* spec overrides path rmap))] + (gen/such-that #(valid? spec %) g 100) + (throw (js/Error. (str "Unable to construct gen at: " path " for: " (abbrev form))))))) + +(defn gen + "Given a spec, returns the generator for it, or throws if none can + be constructed. Optionally an overrides map can be provided which + should map spec names or paths (vectors of keywords) to no-arg + generator-creating fns. These will be used instead of the generators at those + names/paths. Note that parent generator (in the spec or overrides + map) will supersede those of any subtrees. A generator for a regex + op must always return a sequential collection (i.e. a generator for + s/? should return either an empty sequence/vector or a + sequence/vector with one item in it)" + ([spec] (gen spec nil)) + ([spec overrides] (gensub spec overrides [] {::recursion-limit *recursion-limit*} spec))) + +(defn ^:skip-wiki def-impl + "Do not call this directly, use 'def'" + [k form spec] + (assert (c/and (ident? k) (namespace k)) "k must be namespaced keyword or resolveable symbol") + (let [spec (if (c/or (spec? spec) (regex? spec) (get @registry-ref spec)) + spec + (spec-impl form spec nil nil))] + (swap! registry-ref assoc k (with-name spec k)) + k)) + +(defn registry + "returns the registry map, prefer 'get-spec' to lookup a spec by name" + [] + @registry-ref) + +(defn- ->sym + "Returns a symbol from a symbol or var" + [x] + (if (var? x) + (.-sym x) + x)) + +(defn get-spec + "Returns spec registered for keyword/symbol/var k, or nil." + [k] + (get (registry) (if (keyword? k) k (->sym k)))) + +(declare map-spec) + +(defn- macroexpand-check + [v args] + (let [specs (get-spec v)] + (when-let [arg-spec (:args specs)] + (when (invalid? (conform arg-spec args)) + (let [ed (assoc (explain-data* arg-spec [:args] + (if-let [name (spec-name arg-spec)] [name] []) [] args) + ::args args)] + (throw (js/Error. + (str + "Call to " (->sym v) " did not conform to spec:\n" + (with-out-str (explain-out ed)))))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; impl ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defn- recur-limit? [rmap id path k] + (c/and (> (get rmap id) (::recursion-limit rmap)) + (contains? (set path) k))) + +(defn- inck [m k] + (assoc m k (inc (c/or (get m k) 0)))) + +(defn- dt + ([pred x form] (dt pred x form nil)) + ([pred x form cpred?] + (if pred + (if-let [spec (the-spec pred)] + (conform spec x) + (if (ifn? pred) + (if cpred? + (pred x) + (if (pred x) x ::invalid)) + (throw (js/Error. (str (pr-str form) " is not a fn, expected predicate fn"))))) + x))) + +(defn valid? + "Helper function that returns true when x is valid for spec." + ([spec x] + (let [spec (specize spec)] + (not (invalid? (conform* spec x))))) + ([spec x form] + (let [spec (specize spec form)] + (not (invalid? (conform* spec x)))))) + +(defn- pvalid? + "internal helper function that returns true when x is valid for spec." + ([pred x] + (not (invalid? (dt pred x ::unknown)))) + ([pred x form] + (not (invalid? (dt pred x form))))) + +(defn- explain-1 [form pred path via in v] + ;;(prn {:form form :pred pred :path path :in in :v v}) + (let [pred (maybe-spec pred)] + (if (spec? pred) + (explain* pred path (if-let [name (spec-name pred)] (conj via name) via) in v) + [{:path path :pred (abbrev form) :val v :via via :in in}]))) + +(defn ^:skip-wiki map-spec-impl + "Do not call this directly, use 'spec' with a map argument" + [{:keys [req-un opt-un keys-pred pred-exprs opt-keys req-specs req req-keys opt-specs pred-forms opt gfn] + :as argm}] + (let [k->s (zipmap (concat req-keys opt-keys) (concat req-specs opt-specs)) + keys->specnames #(c/or (k->s %) %) + id (random-uuid)] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ m] + (if (keys-pred m) + (let [reg (registry)] + (loop [ret m, [[k v] & ks :as keys] m] + (if keys + (let [sname (keys->specnames k)] + (if-let [s (get reg sname)] + (let [cv (conform s v)] + (if (invalid? cv) + ::invalid + (recur (if (identical? cv v) ret (assoc ret k cv)) + ks))) + (recur ret ks))) + ret))) + ::invalid)) + (unform* [_ m] + (let [reg (registry)] + (loop [ret m, [k & ks :as keys] (c/keys m)] + (if keys + (if (contains? reg (keys->specnames k)) + (let [cv (get m k) + v (unform (keys->specnames k) cv)] + (recur (if (identical? cv v) ret (assoc ret k v)) + ks)) + (recur ret ks)) + ret)))) + (explain* [_ path via in x] + (if-not (map? x) + [{:path path :pred 'map? :val x :via via :in in}] + (let [reg (registry)] + (apply concat + (when-let [probs (->> (map (fn [pred form] (when-not (pred x) (abbrev form))) + pred-exprs pred-forms) + (keep identity) + seq)] + (map + #(identity {:path path :pred % :val x :via via :in in}) + probs)) + (map (fn [[k v]] + (when-not (c/or (not (contains? reg (keys->specnames k))) + (pvalid? (keys->specnames k) v k)) + (explain-1 (keys->specnames k) (keys->specnames k) (conj path k) via (conj in k) v))) + (seq x)))))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [rmap (inck rmap id) + gen (fn [k s] (gensub s overrides (conj path k) rmap k)) + ogen (fn [k s] + (when-not (recur-limit? rmap id path k) + [k (gen/delay (gensub s overrides (conj path k) rmap k))])) + req-gens (map gen req-keys req-specs) + opt-gens (remove nil? (map ogen opt-keys opt-specs))] + (when (every? identity (concat req-gens opt-gens)) + (let [reqs (zipmap req-keys req-gens) + opts (into {} opt-gens)] + (gen/bind (gen/choose 0 (count opts)) + #(let [args (concat (seq reqs) (when (seq opts) (shuffle (seq opts))))] + (->> args + (take (c/+ % (count reqs))) + (apply concat) + (apply gen/hash-map))))))))) + (with-gen* [_ gfn] (map-spec-impl (assoc argm :gfn gfn))) + (describe* [_] (cons `keys + (cond-> [] + req (conj :req req) + opt (conj :opt opt) + req-un (conj :req-un req-un) + opt-un (conj :opt-un opt-un))))))) + +(defn ^:skip-wiki spec-impl + "Do not call this directly, use 'spec'" + ([form pred gfn cpred?] (spec-impl form pred gfn cpred? nil)) + ([form pred gfn cpred? unc] + (cond + (spec? pred) (cond-> pred gfn (with-gen gfn)) + (regex? pred) (regex-spec-impl pred gfn) + (ident? pred) (cond-> (the-spec pred) gfn (with-gen gfn)) + :else + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (let [ret (pred x)] + (if cpred? + ret + (if ret x ::invalid)))) + (unform* [_ x] (if cpred? + (if unc + (unc x) + (throw (js/Error. "no unform fn for conformer"))) + x)) + (explain* [_ path via in x] + (when (invalid? (dt pred x form cpred?)) + [{:path path :pred (abbrev form) :val x :via via :in in}])) + (gen* [_ _ _ _] (if gfn + (gfn) + (gen/gen-for-pred pred))) + (with-gen* [_ gfn] (spec-impl form pred gfn cpred? unc)) + (describe* [_] form))))) + +(defn ^:skip-wiki multi-spec-impl + "Do not call this directly, use 'multi-spec'" + ([form mmvar retag] (multi-spec-impl form mmvar retag nil)) + ([form mmvar retag gfn] + (let [id (random-uuid) + predx #(let [mm @mmvar] + (c/and (-get-method mm ((-dispatch-fn mm) %)) + (mm %))) + dval #((-dispatch-fn @mmvar) %) + tag (if (keyword? retag) + #(assoc %1 retag %2) + retag)] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (if-let [pred (predx x)] + (dt pred x form) + ::invalid)) + (unform* [_ x] (if-let [pred (predx x)] + (unform pred x) + (throw (js/Error. (str "No method of: " form " for dispatch value: " (dval x)))))) + (explain* [_ path via in x] + (let [dv (dval x) + path (conj path dv)] + (if-let [pred (predx x)] + (explain-1 form pred path via in x) + [{:path path :pred (abbrev form) :val x :reason "no method" :via via :in in}]))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [gen (fn [[k f]] + (let [p (f nil)] + (let [rmap (inck rmap id)] + (when-not (recur-limit? rmap id path k) + (gen/delay + (gen/fmap + #(tag % k) + (gensub p overrides (conj path k) rmap (list 'method form k)))))))) + gs (->> (methods @mmvar) + (remove (fn [[k]] (invalid? k))) + (map gen) + (remove nil?))] + (when (every? identity gs) + (gen/one-of gs))))) + (with-gen* [_ gfn] (multi-spec-impl form mmvar retag gfn)) + (describe* [_] `(multi-spec ~form ~retag)))))) + +(defn ^:skip-wiki tuple-impl + "Do not call this directly, use 'tuple'" + ([forms preds] (tuple-impl forms preds nil)) + ([forms preds gfn] + (let [specs (delay (mapv specize preds forms)) + cnt (count preds)] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] + (let [specs @specs] + (if-not (c/and (vector? x) + (= (count x) cnt)) + ::invalid + (loop [ret x, i 0] + (if (= i cnt) + ret + (let [v (x i) + cv (conform* (specs i) v)] + (if (invalid? cv) + ::invalid + (recur (if (identical? cv v) ret (assoc ret i cv)) + (inc i))))))))) + (unform* [_ x] + (assert (c/and (vector? x) + (= (count x) (count preds)))) + (loop [ret x, i 0] + (if (= i (count x)) + ret + (let [cv (x i) + v (unform (preds i) cv)] + (recur (if (identical? cv v) ret (assoc ret i v)) + (inc i)))))) + (explain* [_ path via in x] + (cond + (not (vector? x)) + [{:path path :pred 'vector? :val x :via via :in in}] + + (not= (count x) (count preds)) + [{:path path :pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}] + + :else + (apply concat + (map (fn [i form pred] + (let [v (x i)] + (when-not (pvalid? pred v) + (explain-1 form pred (conj path i) via (conj in i) v)))) + (range (count preds)) forms preds)))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [gen (fn [i p f] + (gensub p overrides (conj path i) rmap f)) + gs (map gen (range (count preds)) preds forms)] + (when (every? identity gs) + (apply gen/tuple gs))))) + (with-gen* [_ gfn] (tuple-impl forms preds gfn)) + (describe* [_] `(tuple ~@forms)))))) + +(defn- tagged-ret [v] + (specify! v + IMapEntry + (-key [_] (-nth v 0)) + (-val [_] (-nth v 1)))) + +(defn ^:skip-wiki or-spec-impl + "Do not call this directly, use 'or'" + [keys forms preds gfn] + (let [id (random-uuid) + kps (zipmap keys preds) + specs (delay (mapv specize preds forms)) + cform (case (count preds) + 2 (fn [x] + (let [specs @specs + ret (conform* (specs 0) x)] + (if (invalid? ret) + (let [ret (conform* (specs 1) x)] + (if (invalid? ret) + ::invalid + (tagged-ret [(keys 1) ret]))) + (tagged-ret [(keys 0) ret])))) + 3 (fn [x] + (let [specs @specs + ret (conform* (specs 0) x)] + (if (invalid? ret) + (let [ret (conform* (specs 1) x)] + (if (invalid? ret) + (let [ret (conform* (specs 2) x)] + (if (invalid? ret) + ::invalid + (tagged-ret [(keys 2) ret]))) + (tagged-ret [(keys 1) ret]))) + (tagged-ret [(keys 0) ret])))) + (fn [x] + (let [specs @specs] + (loop [i 0] + (if (< i (count specs)) + (let [spec (specs i)] + (let [ret (conform* spec x)] + (if (invalid? ret) + (recur (inc i)) + (tagged-ret [(keys i) ret])))) + ::invalid)))))] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (cform x)) + (unform* [_ [k x]] (unform (kps k) x)) + (explain* [this path via in x] + (when-not (pvalid? this x) + (apply concat + (map (fn [k form pred] + (when-not (pvalid? pred x) + (explain-1 form pred (conj path k) via in x))) + keys forms preds)))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [gen (fn [k p f] + (let [rmap (inck rmap id)] + (when-not (recur-limit? rmap id path k) + (gen/delay + (gensub p overrides (conj path k) rmap f))))) + gs (remove nil? (map gen keys preds forms))] + (when-not (empty? gs) + (gen/one-of gs))))) + (with-gen* [_ gfn] (or-spec-impl keys forms preds gfn)) + (describe* [_] `(or ~@(mapcat vector keys forms)))))) + +(defn- and-preds [x preds forms] + (loop [ret x + [pred & preds] preds + [form & forms] forms] + (if pred + (let [nret (dt pred ret form)] + (if (invalid? nret) + ::invalid + ;;propagate conformed values + (recur nret preds forms))) + ret))) + +(defn- explain-pred-list + [forms preds path via in x] + (loop [ret x + [form & forms] forms + [pred & preds] preds] + (when pred + (let [nret (dt pred ret form)] + (if (invalid? nret) + (explain-1 form pred path via in ret) + (recur nret forms preds)))))) + +(defn ^:skip-wiki and-spec-impl + "Do not call this directly, use 'and'" + [forms preds gfn] + (let [specs (delay (mapv specize preds forms)) + cform + (case (count preds) + 2 (fn [x] + (let [specs @specs + ret (conform* (specs 0) x)] + (if (invalid? ret) + ::invalid + (conform* (specs 1) ret)))) + 3 (fn [x] + (let [specs @specs + ret (conform* (specs 0) x)] + (if (invalid? ret) + ::invalid + (let [ret (conform* (specs 1) ret)] + (if (invalid? ret) + ::invalid + (conform* (specs 2) ret)))))) + (fn [x] + (let [specs @specs] + (loop [ret x i 0] + (if (< i (count specs)) + (let [nret (conform* (specs i) ret)] + (if (invalid? nret) + ::invalid + ;;propagate conformed values + (recur nret (inc i)))) + ret)))))] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (cform x)) + (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) + (explain* [_ path via in x] (explain-pred-list forms preds path via in x)) + (gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms)))) + (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) + (describe* [_] `(and ~@forms))))) + +(defn- coll-prob [x kfn kform distinct count min-count max-count + path via in] + (let [pred (c/or kfn coll?) + kform (c/or kform `coll?)] + (cond + (not (pvalid? pred x)) + (explain-1 kform pred path via in x) + + (c/and count (not= count (bounded-count count x))) + [{:path path :pred `(= ~count (c/count ~'%)) :val x :via via :in in}] + + (c/and (c/or min-count max-count) + (not (<= (c/or min-count 0) + (bounded-count (if max-count (inc max-count) min-count) x) + (c/or max-count MAX_INT)))) + [{:path path :pred `(<= ~(c/or min-count 0) (c/count ~'%) ~(c/or max-count MAX_INT)) :val x :via via :in in}] + + (c/and distinct (not (empty? x)) (not (apply distinct? x))) + [{:path path :pred 'distinct? :val x :via via :in in}]))) + +(defn ^:skip-wiki merge-spec-impl + "Do not call this directly, use 'merge'" + [forms preds gfn] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (let [ms (map #(dt %1 x %2) preds forms)] + (if (some invalid? ms) + ::invalid + (apply c/merge ms)))) + (unform* [_ x] (apply c/merge (map #(unform % x) (reverse preds)))) + (explain* [_ path via in x] + (apply concat + (map #(explain-1 %1 %2 path via in x) + forms preds))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (gen/fmap + #(apply c/merge %) + (apply gen/tuple (map #(gensub %1 overrides path rmap %2) + preds forms))))) + (with-gen* [_ gfn] (merge-spec-impl forms preds gfn)) + (describe* [_] `(merge ~@forms)))) + +(defn ^:skip-wiki every-impl + "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" + ([form pred opts] (every-impl form pred opts nil)) + ([form pred {gen-into :into + describe-form ::describe + :keys [kind ::kind-form count max-count min-count distinct gen-max ::kfn ::cpred + conform-keys ::conform-all] + :or {gen-max 20} + :as opts} + gfn] + (let [conform-into gen-into + spec (delay (specize pred)) + check? #(valid? @spec %) + kfn (c/or kfn (fn [i v] i)) + addcv (fn [ret i v cv] (conj ret cv)) + cfns (fn [x] + ;;returns a tuple of [init add complete] fns + (cond + (c/and (vector? x) (c/or (not conform-into) (vector? conform-into))) + [identity + (fn [ret i v cv] + (if (identical? v cv) + ret + (assoc ret i cv))) + identity] + + (c/and (map? x) (c/or (c/and kind (not conform-into)) (map? conform-into))) + [(if conform-keys empty identity) + (fn [ret i v cv] + (if (c/and (identical? v cv) (not conform-keys)) + ret + (assoc ret (nth (if conform-keys cv v) 0) (nth cv 1)))) + identity] + + (c/or (list? conform-into) (seq? conform-into) (c/and (not conform-into) (c/or (list? x) (seq? x)))) + [empty addcv reverse] + + :else [#(empty (c/or conform-into %)) addcv identity]))] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] + (let [spec @spec] + (cond + (not (cpred x)) ::invalid + + conform-all + (let [[init add complete] (cfns x)] + (loop [ret (init x), i 0, [v & vs :as vseq] (seq x)] + (if vseq + (let [cv (conform* spec v)] + (if (invalid? cv) + ::invalid + (recur (add ret i v cv) (inc i) vs))) + (complete ret)))) + + :else + (if (indexed? x) + (let [step (max 1 (long (/ (c/count x) *coll-check-limit*)))] + (loop [i 0] + (if (>= i (c/count x)) + x + (if (valid? spec (nth x i)) + (recur (c/+ i step)) + ::invalid)))) + (let [limit *coll-check-limit*] + (loop [i 0 [v & vs :as vseq] (seq x)] + (cond + (c/or (nil? vseq) (= i limit)) x + (valid? spec v) (recur (inc i) vs) + :else ::invalid))))))) + (unform* [_ x] x) + (explain* [_ path via in x] + (c/or (coll-prob x kind kind-form distinct count min-count max-count + path via in) + (apply concat + ((if conform-all identity (partial take *coll-error-limit*)) + (keep identity + (map (fn [i v] + (let [k (kfn i v)] + (when-not (check? v) + (let [prob (explain-1 form pred path via (conj in k) v)] + prob)))) + (range) x)))))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (let [pgen (gensub pred overrides path rmap form)] + (gen/bind + (cond + gen-into (gen/return (empty gen-into)) + kind (gen/fmap #(if (empty? %) % (empty %)) + (gensub kind overrides path rmap form)) + :else (gen/return [])) + (fn [init] + (gen/fmap + #(if (vector? init) % (into init %)) + (cond + distinct + (if count + (gen/vector-distinct pgen {:num-elements count :max-tries 100}) + (gen/vector-distinct pgen {:min-elements (c/or min-count 0) + :max-elements (c/or max-count (max gen-max (c/* 2 (c/or min-count 0)))) + :max-tries 100})) + + count + (gen/vector pgen count) + + (c/or min-count max-count) + (gen/vector pgen (c/or min-count 0) (c/or max-count (max gen-max (c/* 2 (c/or min-count 0))))) + + :else + (gen/vector pgen 0 gen-max)))))))) + + (with-gen* [_ gfn] (every-impl form pred opts gfn)) + (describe* [_] (c/or describe-form `(every ~(s/mres form) ~@(mapcat identity opts)))))))) + +;;;;;;;;;;;;;;;;;;;;;;; regex ;;;;;;;;;;;;;;;;;;; +;;See: +;; http://matt.might.net/articles/implementation-of-regular-expression-matching-in-scheme-with-derivatives/ +;; http://www.ccs.neu.edu/home/turon/re-deriv.pdf + +;;ctors +(defn- accept [x] {::op ::accept :ret x}) + +(defn- accept? [{:keys [::op]}] + (= ::accept op)) + +(defn- pcat* [{[p1 & pr :as ps] :ps, [k1 & kr :as ks] :ks, [f1 & fr :as forms] :forms, ret :ret, rep+ :rep+}] + (when (every? identity ps) + (if (accept? p1) + (let [rp (:ret p1) + ret (conj ret (if ks {k1 rp} rp))] + (if pr + (pcat* {:ps pr :ks kr :forms fr :ret ret}) + (accept ret))) + {::op ::pcat, :ps ps, :ret ret, :ks ks, :forms forms :rep+ rep+}))) + +(defn- pcat [& ps] (pcat* {:ps ps :ret []})) + +(defn ^:skip-wiki cat-impl + "Do not call this directly, use 'cat'" + [ks ps forms] + (pcat* {:ks ks, :ps ps, :forms forms, :ret {}})) + +(defn- rep* [p1 p2 ret splice form] + (when p1 + (let [r {::op ::rep, :p2 p2, :splice splice, :forms form :id (random-uuid)}] + (if (accept? p1) + (assoc r :p1 p2 :ret (conj ret (:ret p1))) + (assoc r :p1 p1, :ret ret))))) + +(defn ^:skip-wiki rep-impl + "Do not call this directly, use '*'" + [form p] (rep* p p [] false form)) + +(defn ^:skip-wiki rep+impl + "Do not call this directly, use '+'" + [form p] + (pcat* {:ps [p (rep* p p [] true form)] :forms `[~form (* ~form)] :ret [] :rep+ form})) + +(defn ^:skip-wiki amp-impl + "Do not call this directly, use '&'" + [re preds pred-forms] + {::op ::amp :p1 re :ps preds :forms pred-forms}) + +(defn- filter-alt [ps ks forms f] + (if (c/or ks forms) + (let [pks (->> (map vector ps + (c/or (seq ks) (repeat nil)) + (c/or (seq forms) (repeat nil))) + (filter #(-> % first f)))] + [(seq (map first pks)) (when ks (seq (map second pks))) (when forms (seq (map #(nth % 2) pks)))]) + [(seq (filter f ps)) ks forms])) + +(defn- alt* [ps ks forms] + (let [[[p1 & pr :as ps] [k1 :as ks] forms] (filter-alt ps ks forms identity)] + (when ps + (let [ret {::op ::alt, :ps ps, :ks ks :forms forms}] + (if (nil? pr) + (if k1 + (if (accept? p1) + (accept (tagged-ret [k1 (:ret p1)])) + ret) + p1) + ret))))) + +(defn- alts [& ps] (alt* ps nil nil)) +(defn- alt2 [p1 p2] (if (c/and p1 p2) (alts p1 p2) (c/or p1 p2))) + +(defn ^:skip-wiki alt-impl + "Do not call this directly, use 'alt'" + [ks ps forms] (assoc (alt* ps ks forms) :id (random-uuid))) + +(defn ^:skip-wiki maybe-impl + "Do not call this directly, use '?'" + [p form] (assoc (alt* [p (accept ::nil)] nil [form ::nil]) :maybe form)) + +(defn- noret? [p1 pret] + (c/or (= pret ::nil) + (c/and (#{::rep ::pcat} (::op (reg-resolve! p1))) ;;hrm, shouldn't know these + (empty? pret)) + nil)) + +(declare preturn) + +(defn- accept-nil? [p] + (let [{:keys [::op ps p1 p2 forms] :as p} (reg-resolve! p)] + (case op + ::accept true + nil nil + ::amp (c/and (accept-nil? p1) + (c/or (noret? p1 (preturn p1)) + (let [ret (-> (preturn p1) (and-preds ps (next forms)))] + (not (invalid? ret))))) + ::rep (c/or (identical? p1 p2) (accept-nil? p1)) + ::pcat (every? accept-nil? ps) + ::alt (c/some accept-nil? ps)))) + +(declare add-ret) + +(defn- preturn [p] + (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms] :as p} (reg-resolve! p)] + (case op + ::accept ret + nil nil + ::amp (let [pret (preturn p1)] + (if (noret? p1 pret) + ::nil + (and-preds pret ps forms))) + ::rep (add-ret p1 ret k) + ::pcat (add-ret p0 ret k) + ::alt (let [[[p0] [k0]] (filter-alt ps ks forms accept-nil?) + r (if (nil? p0) ::nil (preturn p0))] + (if k0 (tagged-ret [k0 r]) r))))) + +(defn- op-unform [p x] + ;;(prn {:p p :x x}) + (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms rep+ maybe] :as p} (reg-resolve! p) + kps (zipmap ks ps)] + (case op + ::accept [ret] + nil [(unform p x)] + ::amp (let [px (reduce #(unform %2 %1) x (reverse ps))] + (op-unform p1 px)) + ::rep (mapcat #(op-unform p1 %) x) + ::pcat (if rep+ + (mapcat #(op-unform p0 %) x) + (mapcat (fn [k] + (when (contains? x k) + (op-unform (kps k) (get x k)))) + ks)) + ::alt (if maybe + [(unform p0 x)] + (let [[k v] x] + (op-unform (kps k) v)))))) + +(defn- add-ret [p r k] + (let [{:keys [::op ps splice] :as p} (reg-resolve! p) + prop #(let [ret (preturn p)] + (if (empty? ret) r ((if splice into conj) r (if k {k ret} ret))))] + (case op + nil r + (::alt ::accept ::amp) + (let [ret (preturn p)] + ;;(prn {:ret ret}) + (if (= ret ::nil) r (conj r (if k {k ret} ret)))) + + (::rep ::pcat) (prop)))) + +(defn- deriv + [p x] + (let [{[p0 & pr :as ps] :ps, [k0 & kr :as ks] :ks, :keys [::op p1 p2 ret splice forms] :as p} (reg-resolve! p)] + (when p + (case op + ::accept nil + nil (let [ret (dt p x p)] + (when-not (invalid? ret) (accept ret))) + ::amp (when-let [p1 (deriv p1 x)] + (if (= ::accept (::op p1)) + (let [ret (-> (preturn p1) (and-preds ps (next forms)))] + (when-not (invalid? ret) + (accept ret))) + (amp-impl p1 ps forms))) + ::pcat (alt2 (pcat* {:ps (cons (deriv p0 x) pr), :ks ks, :forms forms, :ret ret}) + (when (accept-nil? p0) (deriv (pcat* {:ps pr, :ks kr, :forms (next forms), :ret (add-ret p0 ret k0)}) x))) + ::alt (alt* (map #(deriv % x) ps) ks forms) + ::rep (alt2 (rep* (deriv p1 x) p2 ret splice forms) + (when (accept-nil? p1) (deriv (rep* p2 p2 (add-ret p1 ret nil) splice forms) x))))))) + +(defn- op-describe [p] + (let [{:keys [::op ps ks forms splice p1 rep+ maybe] :as p} (reg-resolve! p)] + ;;(prn {:op op :ks ks :forms forms :p p}) + (when p + (case op + ::accept nil + nil p + ::amp (list* 'clojure.spec/& (op-describe p1) forms) + ::pcat (if rep+ + (list `+ rep+) + (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) forms))) + ::alt (if maybe + (list `? maybe) + (cons `alt (mapcat vector ks forms))) + ::rep (list (if splice `+ `*) forms))))) + +(defn- op-explain [form p path via in input] + ;;(prn {:form form :p p :path path :input input}) + (let [[x :as input] input + {:keys [::op ps ks forms splice p1 p2] :as p} (reg-resolve! p) + via (if-let [name (spec-name p)] (conj via name) via) + insufficient (fn [path form] + [{:path path + :reason "Insufficient input" + :pred (abbrev form) + :val () + :via via + :in in}])] + (when p + (case op + ::accept nil + nil (if (empty? input) + (insufficient path form) + (explain-1 form p path via in x)) + ::amp (if (empty? input) + (if (accept-nil? p1) + (explain-pred-list forms ps path via in (preturn p1)) + (insufficient path (op-describe p1))) + (if-let [p1 (deriv p1 x)] + (explain-pred-list forms ps path via in (preturn p1)) + (op-explain (op-describe p1) p1 path via in input))) + ::pcat (let [pkfs (map vector + ps + (c/or (seq ks) (repeat nil)) + (c/or (seq forms) (repeat nil))) + [pred k form] (if (= 1 (count pkfs)) + (first pkfs) + (first (remove (fn [[p]] (accept-nil? p)) pkfs))) + path (if k (conj path k) path) + form (c/or form (op-describe pred))] + (if (c/and (empty? input) (not pred)) + (insufficient path form) + (op-explain form pred path via in input))) + ::alt (if (empty? input) + (insufficient path (op-describe p)) + (apply concat + (map (fn [k form pred] + (op-explain (c/or form (op-describe pred)) + pred + (if k (conj path k) path) + via + in + input)) + (c/or (seq ks) (repeat nil)) + (c/or (seq forms) (repeat nil)) + ps))) + ::rep (op-explain (if (identical? p1 p2) + forms + (op-describe p1)) + p1 path via in input))))) + +(defn- re-gen [p overrides path rmap f] + ;;(prn {:op op :ks ks :forms forms}) + (let [{:keys [::op ps ks p1 p2 forms splice ret id ::gfn] :as p} (reg-resolve! p) + rmap (if id (inck rmap id) rmap) + ggens (fn [ps ks forms] + (let [gen (fn [p k f] + ;;(prn {:k k :path path :rmap rmap :op op :id id}) + (when-not (c/and rmap id k (recur-limit? rmap id path k)) + (if id + (gen/delay (re-gen p overrides (if k (conj path k) path) rmap (c/or f p))) + (re-gen p overrides (if k (conj path k) path) rmap (c/or f p)))))] + (map gen ps (c/or (seq ks) (repeat nil)) (c/or (seq forms) (repeat nil)))))] + (c/or (when-let [g (get overrides path)] + (case op + (:accept nil) (gen/fmap vector g) + g)) + (when gfn + (gfn)) + (when p + (case op + ::accept (if (= ret ::nil) + (gen/return []) + (gen/return [ret])) + nil (when-let [g (gensub p overrides path rmap f)] + (gen/fmap vector g)) + ::amp (re-gen p1 overrides path rmap (op-describe p1)) + ::pcat (let [gens (ggens ps ks forms)] + (when (every? identity gens) + (apply gen/cat gens))) + ::alt (let [gens (remove nil? (ggens ps ks forms))] + (when-not (empty? gens) + (gen/one-of gens))) + ::rep (if (recur-limit? rmap id [id] id) + (gen/return []) + (when-let [g (re-gen p2 overrides path rmap forms)] + (gen/fmap #(apply concat %) + (gen/vector g))))))))) + +(defn- re-conform [p [x & xs :as data]] + ;;(prn {:p p :x x :xs xs}) + (if (empty? data) + (if (accept-nil? p) + (let [ret (preturn p)] + (if (= ret ::nil) + nil + ret)) + ::invalid) + (if-let [dp (deriv p x)] + (recur dp xs) + ::invalid))) + +(defn- re-explain [path via in re input] + (loop [p re [x & xs :as data] input i 0] + ;;(prn {:p p :x x :xs xs :re re}) (prn) + (if (empty? data) + (if (accept-nil? p) + nil ;;success + (op-explain (op-describe p) p path via in nil)) + (if-let [dp (deriv p x)] + (recur dp xs (inc i)) + (if (accept? p) + (if (= (::op p) ::pcat) + (op-explain (op-describe p) p path via (conj in i) (seq data)) + [{:path path + :reason "Extra input" + :pred (abbrev (op-describe re)) + :val data + :via via + :in (conj in i)}]) + (c/or (op-explain (op-describe p) p path via (conj in i) (seq data)) + [{:path path + :reason "Extra input" + :pred (abbrev (op-describe p)) + :val data + :via via + :in (conj in i)}])))))) + +(defn ^:skip-wiki regex-spec-impl + "Do not call this directly, use 'spec' with a regex op argument" + [re gfn] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] + (if (c/or (nil? x) (coll? x)) + (re-conform re (seq x)) + ::invalid)) + (unform* [_ x] (op-unform re x)) + (explain* [_ path via in x] + (if (c/or (nil? x) (coll? x)) + (re-explain path via in re (seq x)) + [{:path path :pred (abbrev (op-describe re)) :val x :via via :in in}])) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (re-gen re overrides path rmap (op-describe re)))) + (with-gen* [_ gfn] (regex-spec-impl re gfn)) + (describe* [_] (op-describe re)))) + +;;;;;;;;;;;;;;;;; HOFs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- call-valid? + [f specs args] + (let [cargs (conform (:args specs) args)] + (when-not (invalid? cargs) + (let [ret (apply f args) + cret (conform (:ret specs) ret)] + (c/and (not (invalid? cret)) + (if (:fn specs) + (pvalid? (:fn specs) {:args cargs :ret cret}) + true)))))) + +(defn- validate-fn + "returns f if valid, else smallest" + [f specs iters] + (let [g (gen (:args specs)) + prop (gen/for-all* [g] #(call-valid? f specs %))] + (let [ret (gen/quick-check iters prop)] + (if-let [[smallest] (-> ret :shrunk :smallest)] + smallest + f)))) + +(defn ^:skip-wiki fspec-impl + "Do not call this directly, use 'fspec'" + [argspec aform retspec rform fnspec fform gfn] + (let [specs {:args argspec :ret retspec :fn fnspec}] + (reify + ILookup + (-lookup [this k] (get specs k)) + (-lookup [_ k not-found] (get specs k not-found)) + + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ f] (if (ifn? f) + (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) + ::invalid)) + (unform* [_ f] f) + (explain* [_ path via in f] + (if (ifn? f) + (let [args (validate-fn f specs 100)] + (if (identical? f args) ;;hrm, we might not be able to reproduce + nil + (let [ret (try (apply f args) (catch js/Error t t))] + (if (instance? js/Error ret) + ;;TODO add exception data + [{:path path :pred '(apply fn) :val args :reason (.-message ret) :via via :in in}] + + (let [cret (dt retspec ret rform)] + (if (invalid? cret) + (explain-1 rform retspec (conj path :ret) via in ret) + (when fnspec + (let [cargs (conform argspec args)] + (explain-1 fform fnspec (conj path :fn) via in {:args cargs :ret cret}))))))))) + [{:path path :pred 'ifn? :val f :via via :in in}])) + (gen* [_ overrides _ _] (if gfn + (gfn) + (gen/return + (fn [& args] + (assert (pvalid? argspec args) (with-out-str (explain argspec args))) + (gen/generate (gen retspec overrides)))))) + (with-gen* [_ gfn] (fspec-impl argspec aform retspec rform fnspec fform gfn)) + (describe* [_] `(fspec :args ~aform :ret ~rform :fn ~fform))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(cljs.spec.alpha/def ::kvs->map (cljs.spec.alpha/conformer #(zipmap (map ::k %) (map ::v %)) #(map (fn [[k v]] {::k k ::v v}) %))) + +(defn nonconforming + "takes a spec and returns a spec that has the same properties except + 'conform' returns the original (not the conformed) value. Note, will specize regex ops." + [spec] + (let [spec (specize spec)] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (let [ret (conform* spec x)] + (if (invalid? ret) + ::invalid + x))) + (unform* [_ x] (unform* spec x)) + (explain* [_ path via in x] (explain* spec path via in x)) + (gen* [_ overrides path rmap] (gen* spec overrides path rmap)) + (with-gen* [_ gfn] (nonconforming (with-gen* spec gfn))) + (describe* [_] `(nonconforming ~(describe* spec)))))) + +(defn ^:skip-wiki nilable-impl + "Do not call this directly, use 'nilable'" + [form pred gfn] + (let [spec (specize pred form)] + (reify + Specize + (specize* [s] s) + (specize* [s _] s) + + Spec + (conform* [_ x] (if (nil? x) nil (conform* spec x))) + (unform* [_ x] (if (nil? x) nil (unform* spec x))) + (explain* [_ path via in x] + (when-not (c/or (pvalid? spec x) (nil? x)) + (conj + (explain-1 form pred (conj path ::pred) via in x) + {:path (conj path ::nil) :pred 'nil? :val x :via via :in in}))) + (gen* [_ overrides path rmap] + (if gfn + (gfn) + (gen/frequency + [[1 (gen/delay (gen/return nil))] + [9 (gen/delay (gensub pred overrides (conj path ::pred) rmap form))]]))) + (with-gen* [_ gfn] (nilable-impl form pred gfn)) + (describe* [_] `(nilable ~(s/mres form)))))) + +(defn exercise + "generates a number (default 10) of values compatible with spec and maps conform over them, + returning a sequence of [val conformed-val] tuples. Optionally takes + a generator overrides map as per gen" + ([spec] (exercise spec 10)) + ([spec n] (exercise spec n nil)) + ([spec n overrides] + (map #(vector % (conform spec %)) (gen/sample (gen spec overrides) n)))) + +(defn inst-in-range? + "Return true if inst at or after start and before end" + [start end inst] + (c/and (inst? inst) + (let [t (inst-ms inst)] + (c/and (<= (inst-ms start) t) (< t (inst-ms end)))))) + +(defn int-in-range? + "Return true if start <= val and val < end" + [start end val] + (cond + (integer? val) (c/and (<= start val) (< val end)) + + (instance? goog.math.Long val) + (c/and (.lessThanOrEqual start val) + (.lessThan val end)) + + (instance? goog.math.Integer val) + (c/and (.lessThanOrEqual start val) + (.lessThan val end)) + + :else false)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; assert ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defonce + ^{:dynamic true + :doc "If true, compiler will enable spec asserts, which are then +subject to runtime control via check-asserts? If false, compiler +will eliminate all spec assert overhead. See 'assert'. +Initially set to the negation of the ':elide-asserts' compiler option. +Defaults to true."} + *compile-asserts* + (s/init-compile-asserts)) + +(defonce ^{:private true + :dynamic true} + *runtime-asserts* + false) + +(defn ^boolean check-asserts? + "Returns the value set by check-asserts." + [] + *runtime-asserts*) + +(defn check-asserts + "Enable or disable spec asserts that have been compiled +with '*compile-asserts*' true. See 'assert'. +Initially set to boolean value of cljs.spec.alpha/*runtime-asserts*. +Defaults to false." + [^boolean flag] + (set! *runtime-asserts* flag)) + +(defn assert* + "Do not call this directly, use 'assert'." + [spec x] + (if (valid? spec x) + x + (let [ed (c/merge (assoc (explain-data* spec [] [] [] x) + ::failure :assertion-failed))] + (throw (js/Error. + (str "Spec assertion failed\n" (with-out-str (explain-out ed)))))))) diff --git a/src/main/cljs/cljs/spec/impl/gen/alpha.cljc b/src/main/cljs/cljs/spec/impl/gen/alpha.cljc new file mode 100644 index 000000000..f7db052a0 --- /dev/null +++ b/src/main/cljs/cljs/spec/impl/gen/alpha.cljc @@ -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.spec.impl.gen.alpha + (:refer-clojure :exclude [delay]) + (:require [cljs.core :as c] + [clojure.string :as string])) + +(defmacro dynaload [[quote s]] + (let [xs (string/split (namespace s) #"\.") + cnt (count xs) + checks (map + (fn [n xs] + `(c/exists? ~(symbol (string/join "." (take n xs))))) + (range 2 cnt) + (repeat xs))] + `(cljs.spec.impl.gen.alpha/LazyVar. + (fn [] + (if (and ~@checks (c/exists? ~s)) + ~(vary-meta s assoc :cljs.analyzer/no-resolve true) + (throw + (js/Error. + (str "Var " '~s " does not exist, " + (namespace '~s) " never required"))))) + nil))) + +(defmacro delay + "given body that returns a generator, returns a + generator that delegates to that, but delays + creation until used." + [& body] + `(delay-impl (c/delay ~@body))) + +(defmacro ^:skip-wiki lazy-combinator + "Implementation macro, do not call directly." + [s] + (let [fqn (symbol "clojure.test.check.generators" (name s)) + doc (str "Lazy loaded version of " fqn)] + `(let [g# (dynaload '~fqn)] + (defn ~s + ~doc + [& ~'args] + (apply @g# ~'args))))) + +(defmacro ^:skip-wiki lazy-combinators + "Implementation macro, do not call directly." + [& syms] + `(do + ~@(map + (fn [s] (list `lazy-combinator s)) + syms))) + +(defmacro ^:skip-wiki lazy-prim + "Implementation macro, do not call directly." + [s] + (let [fqn (symbol "clojure.test.check.generators" (name s)) + doc (str "Fn returning " fqn)] + `(let [g# (dynaload '~fqn)] + (defn ~s + ~doc + [& ~'args] + @g#)))) + +(defmacro ^:skip-wiki lazy-prims + "Implementation macro, do not call directly." + [& syms] + `(do + ~@(map + (fn [s] (list `lazy-prim s)) + syms))) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/impl/gen/alpha.cljs b/src/main/cljs/cljs/spec/impl/gen/alpha.cljs new file mode 100644 index 000000000..fabba040b --- /dev/null +++ b/src/main/cljs/cljs/spec/impl/gen/alpha.cljs @@ -0,0 +1,180 @@ +; 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.spec.impl.gen.alpha + (:refer-clojure :exclude [boolean cat hash-map list map not-empty set vector + char double int keyword symbol string uuid delay]) + (:require-macros [cljs.core :as c] + [cljs.spec.impl.gen.alpha :as gen :refer [dynaload lazy-combinators lazy-prims]]) + (:require [cljs.core :as c])) + +(deftype LazyVar [f ^:mutable cached] + IDeref + (-deref [this] + (if-not (nil? cached) + cached + (let [x (f)] + (when-not (nil? x) + (set! cached x)) + x)))) + +(def ^:private quick-check-ref + (dynaload 'clojure.test.check/quick-check)) + +(defn quick-check + [& args] + (apply @quick-check-ref args)) + +(def ^:private for-all*-ref + (dynaload 'clojure.test.check.properties/for-all*)) + +(defn for-all* + "Dynamically loaded clojure.test.check.properties/for-all*." + [& args] + (apply @for-all*-ref args)) + +(let [g? (dynaload 'clojure.test.check.generators/generator?) + g (dynaload 'clojure.test.check.generators/generate) + mkg (dynaload 'clojure.test.check.generators/->Generator)] + (defn- generator? + [x] + (@g? x)) + (defn- generator + [gfn] + (@mkg gfn)) + (defn generate + "Generate a single value using generator." + [generator] + (@g generator))) + +(defn ^:skip-wiki delay-impl + [gfnd] + ;;N.B. depends on test.check impl details + (generator (fn [rnd size] + ((:gen @gfnd) rnd size)))) + +;(defn gen-for-name +; "Dynamically loads test.check generator named s." +; [s] +; (let [g (dynaload s)] +; (if (generator? g) +; g +; (throw (js/Error. (str "Var " s " is not a generator")))))) + +(lazy-combinators hash-map list map not-empty set vector vector-distinct fmap elements + bind choose one-of such-that tuple sample return + large-integer* double* frequency) + +(lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double + int keyword keyword-ns large-integer ratio simple-type simple-type-printable + string string-ascii string-alphanumeric symbol symbol-ns uuid) + +(defn cat + "Returns a generator of a sequence catenated from results of +gens, each of which should generate something sequential." + [& gens] + (fmap #(apply concat %) + (apply tuple gens))) + +(defn- ^boolean qualified? [ident] (not (nil? (namespace ident)))) + +(def ^:private +gen-builtins + (c/delay + (let [simple (simple-type-printable)] + {any? (one-of [(return nil) (any-printable)]) + number? (one-of [(large-integer) (double)]) + integer? (large-integer) + int? (large-integer) + pos-int? (large-integer* {:min 1}) + neg-int? (large-integer* {:max -1}) + nat-int? (large-integer* {:min 0}) + float? (double) + double? (double) + string? (string-alphanumeric) + ident? (one-of [(keyword-ns) (symbol-ns)]) + simple-ident? (one-of [(keyword) (symbol)]) + qualified-ident? (such-that qualified? (one-of [(keyword-ns) (symbol-ns)])) + keyword? (keyword-ns) + simple-keyword? (keyword) + qualified-keyword? (such-that qualified? (keyword-ns)) + symbol? (symbol-ns) + simple-symbol? (symbol) + qualified-symbol? (such-that qualified? (symbol-ns)) + uuid? (uuid) + inst? (fmap #(js/Date. %) + (large-integer)) + seqable? (one-of [(return nil) + (list simple) + (vector simple) + (map simple simple) + (set simple) + (string-alphanumeric)]) + indexed? (vector simple) + map? (map simple simple) + vector? (vector simple) + list? (list simple) + seq? (list simple) + char? (char) + set? (set simple) + nil? (return nil) + false? (return false) + true? (return true) + boolean? (boolean) + zero? (return 0) + ;rational? (one-of [(large-integer) (ratio)]) + coll? (one-of [(map simple simple) + (list simple) + (vector simple) + (set simple)]) + empty? (elements [nil '() [] {} #{}]) + associative? (one-of [(map simple simple) (vector simple)]) + sequential? (one-of [(list simple) (vector simple)]) + ;ratio? (such-that ratio? (ratio)) + }))) + +(defn gen-for-pred + "Given a predicate, returns a built-in generator if one exists." + [pred] + (if (set? pred) + (elements pred) + (get @gen-builtins pred))) + +(comment + (require 'clojure.test.check) + (require 'clojure.test.check.properties) + (require 'cljs.spec.impl.gen) + (in-ns 'cljs.spec.impl.gen) + + ;; combinators, see call to lazy-combinators above for complete list + (generate (one-of [(gen-for-pred integer?) (gen-for-pred string?)])) + (generate (such-that #(< 10000 %) (gen-for-pred integer?))) + (let [reqs {:a (gen-for-pred number?) + :b (gen-for-pred keyword?)} + opts {:c (gen-for-pred string?)}] + (generate (bind (choose 0 (count opts)) + #(let [args (concat (seq reqs) (shuffle (seq opts)))] + (->> args + (take (+ % (count reqs))) + (mapcat identity) + (apply hash-map)))))) + (generate (cat (list (gen-for-pred string?)) + (list (gen-for-pred integer?)))) + + ;; load your own generator + ;(gen-for-name 'clojure.test.check.generators/int) + + ;; failure modes + ;(gen-for-name 'unqualified) + ;(gen-for-name 'clojure.core/+) + ;(gen-for-name 'clojure.core/name-does-not-exist) + ;(gen-for-name 'ns.does.not.exist/f) + + ) + + diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc new file mode 100644 index 000000000..ab11b2fb0 --- /dev/null +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -0,0 +1,251 @@ +; 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.spec.test.alpha + (:require + [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] + [clojure.string :as string] + [cljs.spec.alpha :as s] + [cljs.spec.impl.gen.alpha :as gen])) + +(defonce ^:private instrumented-vars (atom #{})) + +(defn- collectionize + [x] + (if (symbol? x) + (list x) + x)) + +(defn- fn-spec-name? + [s] + (symbol? s)) + +(defmacro with-instrument-disabled + "Disables instrument's checking of calls, within a scope." + [& body] + `(binding [*instrument-enabled* nil] + ~@body)) + +(defmacro instrument-1 + [[quote s] opts] + (when-let [v (ana-api/resolve &env s)] + (swap! instrumented-vars conj (:name v)) + `(let [checked# (instrument-1* ~s (var ~s) ~opts)] + (when checked# (set! ~s checked#)) + '~(:name v)))) + +(defmacro unstrument-1 + [[quote s]] + (when-let [v (ana-api/resolve &env s)] + (when (@instrumented-vars (:name v)) + (swap! instrumented-vars disj (:name v)) + `(let [raw# (unstrument-1* ~s (var ~s))] + (when raw# (set! ~s raw#)) + '~(:name v))))) + +(defn- sym-or-syms->syms [sym-or-syms] + (into [] + (mapcat + (fn [sym] + (if (and (string/includes? (str sym) ".") + (ana-api/find-ns sym)) + (->> (vals (ana-api/ns-interns sym)) + (filter #(not (:macro %))) + (map :name) + (map + (fn [name-sym] + (symbol (name sym) (name name-sym))))) + [sym]))) + (collectionize sym-or-syms))) + +(defmacro instrument + "Instruments the vars named by sym-or-syms, a symbol or collection +of symbols, or all instrumentable vars if sym-or-syms is not +specified. If a symbol identifies a namespace then all symbols in that +namespace will be enumerated. + +If a var has an :args fn-spec, sets the var's root binding to a +fn that checks arg conformance (throwing an exception on failure) +before delegating to the original fn. + +The opts map can be used to override registered specs, and/or to +replace fn implementations entirely. Opts for symbols not included +in sym-or-syms are ignored. This facilitates sharing a common +options map across many different calls to instrument. + +The opts map may have the following keys: + + :spec a map from var-name symbols to override specs + :stub a set of var-name symbols to be replaced by stubs + :gen a map from spec names to generator overrides + :replace a map from var-name symbols to replacement fns + +:spec overrides registered fn-specs with specs your provide. Use +:spec overrides to provide specs for libraries that do not have +them, or to constrain your own use of a fn to a subset of its +spec'ed contract. + +:stub replaces a fn with a stub that checks :args, then uses the +:ret spec to generate a return value. + +:gen overrides are used only for :stub generation. + +:replace replaces a fn with a fn that checks args conformance, then +invokes the fn you provide, enabling arbitrary stubbing and mocking. + +:spec can be used in combination with :stub or :replace. + +Returns a collection of syms naming the vars instrumented." + ([] + `(instrument '[~@(#?(:clj s/speced-vars + :cljs cljs.spec$macros/speced-vars))])) + ([xs] + `(instrument ~xs nil)) + ([sym-or-syms opts] + (let [syms (sym-or-syms->syms (eval sym-or-syms)) + opts-sym (gensym "opts")] + `(let [~opts-sym ~opts] + (reduce + (fn [ret# [_# f#]] + (let [sym# (f#)] + (cond-> ret# sym# (conj sym#)))) + [] + (->> (zipmap '~syms + [~@(map + (fn [sym] + `(fn [] (instrument-1 '~sym ~opts-sym))) + syms)]) + (filter #((instrumentable-syms ~opts-sym) (first %))) + (distinct-by first))))))) + +(defmacro unstrument + "Undoes instrument on the vars named by sym-or-syms, specified +as in instrument. With no args, unstruments all instrumented vars. +Returns a collection of syms naming the vars unstrumented." + ([] + `(unstrument '[~@(deref instrumented-vars)])) + ([sym-or-syms] + (let [syms (sym-or-syms->syms (eval sym-or-syms))] + `(reduce + (fn [ret# f#] + (let [sym# (f#)] + (cond-> ret# sym# (conj sym#)))) + [] + [~@(->> syms + (map + (fn [sym] + (when (symbol? sym) + `(fn [] + (unstrument-1 '~sym))))) + (remove nil?))])))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmacro check-1 + [[quote s :as qs] f spec opts] + (let [{:keys [name] :as v} (when qs (ana-api/resolve &env s))] + `(let [s# '~name + opts# ~opts + v# ~(when v `(var ~name)) + spec# (or ~spec ~(when v `(s/get-spec (var ~name)))) + re-inst?# (and v# (seq (unstrument '~name)) true) + f# (or ~f (when v# @v#))] + (try + (cond + (nil? f#) + {:failure (ex-info "No fn to spec" {::s/failure :no-fn}) + :sym s# :spec spec#} + + (:args spec#) + (let [tcret# (quick-check f# spec# opts#)] + (make-check-result s# spec# tcret#)) + + :default + {:failure (ex-info "No :args spec" {::s/failure :no-args-spec}) + :sym s# :spec spec#}) + (finally + (when re-inst?# (instrument '~name))))))) + +(defmacro check-fn + "Runs generative tests for fn f using spec and opts. See +'check' for options and return." + ([f spec] + `(check-fn ~f ~spec nil)) + ([f spec opts] + `(let [opts# ~opts] + (validate-check-opts opts#) + (check-1 nil ~f ~spec opts#)))) + +(defn checkable-syms* + ([] + (checkable-syms* nil)) + ([opts] + (reduce into #{} + [(filter fn-spec-name? (keys @s/registry-ref)) + (keys (:spec opts))]))) + +(defmacro checkable-syms + "Given an opts map as per check, returns the set of syms that +can be checked." + ([] + `(checkable-syms nil)) + ([opts] + `(let [opts# ~opts] + (validate-check-opts opts#) + (reduce conj #{} + '[~@(filter fn-spec-name? (keys @s/registry-ref)) + ~@(keys (:spec opts))])))) + +(defmacro check + "Run generative tests for spec conformance on vars named by +sym-or-syms, a symbol or collection of symbols. If sym-or-syms +is not specified, check all checkable vars. If a symbol identifies a +namespace then all symbols in that namespace will be enumerated. + +The opts map includes the following optional keys, where stc +aliases clojure.test.check: + +::stc/opts opts to flow through test.check/quick-check +:gen map from spec names to generator overrides + +The ::stc/opts include :num-tests in addition to the keys +documented by test.check. Generator overrides are passed to +spec/gen when generating function args. + +Returns a lazy sequence of check result maps with the following +keys + +:spec the spec tested +:sym optional symbol naming the var tested +:failure optional test failure +::stc/ret optional value returned by test.check/quick-check + +The value for :failure can be any exception. Exceptions thrown by +spec itself will have an ::s/failure value in ex-data: + +:check-failed at least one checked return did not conform +:no-args-spec no :args spec provided +:no-fn no fn provided +:no-fspec no fspec provided +:no-gen unable to generate :args +:instrument invalid args detected by instrument +" + ([] + `(check '~(checkable-syms*))) + ([sym-or-syms] + `(check ~sym-or-syms nil)) + ([sym-or-syms opts] + (let [syms (sym-or-syms->syms (eval sym-or-syms)) + opts-sym (gensym "opts")] + `(let [~opts-sym ~opts] + [~@(->> syms + (filter (checkable-syms* opts)) + (map + (fn [sym] + (do `(check-1 '~sym nil nil ~opts-sym)))))])))) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs new file mode 100644 index 000000000..4195a84ca --- /dev/null +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -0,0 +1,361 @@ +; 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.spec.test.alpha + (:require-macros [cljs.spec.test.alpha :as m :refer [with-instrument-disabled]]) + (:require + [goog.object :as gobj] + [goog.userAgent.product :as product] + [clojure.string :as string] + [cljs.stacktrace :as st] + [cljs.pprint :as pp] + [cljs.spec.alpha :as s] + [cljs.spec.impl.gen.alpha :as gen] + [clojure.test.check :as stc] + [clojure.test.check.properties])) + +(defn distinct-by + ([f coll] + (let [step (fn step [xs seen] + (lazy-seq + ((fn [[x :as xs] seen] + (when-let [s (seq xs)] + (let [v (f x)] + (if (contains? seen v) + (recur (rest s) seen) + (cons x (step (rest s) (conj seen v))))))) + xs seen)))] + (step coll #{})))) + +(defn ->sym + [x] + (@#'s/->sym x)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private ^:dynamic *instrument-enabled* + "if false, instrumented fns call straight through" + true) + +(defn get-host-port [] + (if (not= "browser" *target*) + {} + {:host (.. js/window -location -host) + :port (.. js/window -location -port)})) + +(defn get-ua-product [] + (if (not= "browser" *target*) + (keyword *target*) + (cond + product/SAFARI :safari + product/CHROME :chrome + product/FIREFOX :firefox + product/IE :ie))) + +(defn get-env [] + {:ua-product (get-ua-product)}) + +(defn- fn-spec? + "Fn-spec must include at least :args or :ret specs." + [m] + (or (:args m) (:ret m))) + +;; wrap spec/explain-data until specs always return nil for ok data +(defn- explain-data* + [spec v] + (when-not (s/valid? spec v nil) + (s/explain-data spec v))) + +(defn- find-caller [st] + (letfn [(search-spec-fn [frame] + (when frame + (let [s (:function frame)] + (and (string? s) (not (string/blank? s)) + (re-find #"cljs\.spec\.test\.spec_checking_fn" s)))))] + (->> st + (drop-while #(not (search-spec-fn %))) + (drop-while search-spec-fn) + first))) + +;; TODO: check ::caller result in other browsers - David + +(defn- spec-checking-fn + [v f fn-spec] + (let [fn-spec (@#'s/maybe-spec fn-spec) + conform! (fn [v role spec data args] + (let [conformed (s/conform spec data)] + (if (= ::s/invalid conformed) + (let [caller (find-caller + (st/parse-stacktrace + (get-host-port) + (.-stack (js/Error.)) + (get-env) nil)) + ed (merge (assoc (s/explain-data* spec [role] [] [] data) + ::s/args args + ::s/failure :instrument) + (when caller + {::caller caller}))] + (throw (ex-info + (str "Call to " v " did not conform to spec:\n" (with-out-str (s/explain-out ed))) + ed))) + conformed)))] + (doto + (fn + [& args] + (if *instrument-enabled* + (with-instrument-disabled + (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) + (binding [*instrument-enabled* true] + (apply f args))) + (apply f args))) + (gobj/extend f)))) + +(defn- no-fspec + [v spec] + (ex-info (str "Fn at " v " is not spec'ed.") + {:var v :spec spec ::s/failure :no-fspec})) + +(defonce ^:private instrumented-vars (atom {})) + +(defn- instrument-choose-fn + "Helper for instrument." + [f spec sym {over :gen :keys [stub replace]}] + (if (some #{sym} stub) + (-> spec (s/gen over) gen/generate) + (get replace sym f))) + +(defn- instrument-choose-spec + "Helper for instrument" + [spec sym {overrides :spec}] + (get overrides sym spec)) + +(defn- instrument-1* + [s v opts] + (let [spec (s/get-spec v) + {:keys [raw wrapped]} (get @instrumented-vars v) + current @v + to-wrap (if (= wrapped current) raw current) + ospec (or (instrument-choose-spec spec s opts) + (throw (no-fspec v spec))) + ofn (instrument-choose-fn to-wrap ospec s opts) + checked (spec-checking-fn v ofn ospec)] + (swap! instrumented-vars assoc v {:raw to-wrap :wrapped checked}) + checked)) + +(defn- unstrument-1* + [s v] + (when v + (when-let [{:keys [raw wrapped]} (get @instrumented-vars v)] + (swap! instrumented-vars dissoc v) + (let [current @v] + (when (= wrapped current) + raw))))) + +(defn- fn-spec-name? + [s] + (symbol? s)) + +(defn- collectionize + [x] + (if (symbol? x) + (list x) + x)) + +(defn instrumentable-syms + "Given an opts map as per instrument, returns the set of syms +that can be instrumented." + ([] (instrumentable-syms nil)) + ([opts] + (assert (every? ident? (keys (:gen opts))) "instrument :gen expects ident keys") + (reduce into #{} [(filter fn-spec-name? (keys (s/registry))) + (keys (:spec opts)) + (:stub opts) + (keys (:replace opts))]))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- explain-check + [args spec v role] + (ex-info + "Specification-based check failed" + (when-not (s/valid? spec v nil) + (assoc (s/explain-data* spec [role] [] [] v) + ::args args + ::val v + ::s/failure :check-failed)))) + +(defn- check-call + "Returns true if call passes specs, otherwise *returns* an exception +with explain-data + ::s/failure." + [f specs args] + (let [cargs (when (:args specs) (s/conform (:args specs) args))] + (if (= cargs ::s/invalid) + (explain-check args (:args specs) args :args) + (let [ret (apply f args) + cret (when (:ret specs) (s/conform (:ret specs) ret))] + (if (= cret ::s/invalid) + (explain-check args (:ret specs) ret :ret) + (if (and (:args specs) (:ret specs) (:fn specs)) + (if (s/valid? (:fn specs) {:args cargs :ret cret}) + true + (explain-check args (:fn specs) {:args cargs :ret cret} :fn)) + true)))))) + +(defn- quick-check + [f specs {gen :gen opts ::stc/opts}] + (let [{:keys [num-tests] :or {num-tests 1000}} opts + g (try (s/gen (:args specs) gen) (catch js/Error t t))] + (if (instance? js/Error g) + {:result g} + (let [prop (gen/for-all* [g] #(check-call f specs %))] + (apply gen/quick-check num-tests prop (mapcat identity opts)))))) + +(defn- make-check-result + "Builds spec result map." + [check-sym spec test-check-ret] + (merge {:spec spec + ::stc/ret test-check-ret} + (when check-sym + {:sym check-sym}) + (when-let [result (-> test-check-ret :result)] + (when-not (true? result) {:failure result})) + (when-let [shrunk (-> test-check-ret :shrunk)] + {:failure (:result shrunk)}))) + +(defn- validate-check-opts + [opts] + (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; check reporting ;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- failure-type + [x] + (::s/failure (ex-data x))) + +(defn- unwrap-failure + [x] + (if (failure-type x) + (ex-data x) + x)) + +(defn- result-type + "Returns the type of the check result. This can be any of the +::s/failure keywords documented in 'check', or: + + :check-passed all checked fn returns conformed + :check-threw checked fn threw an exception" + [ret] + (let [failure (:failure ret)] + (cond + (nil? failure) :check-passed + (failure-type failure) (failure-type failure) + :default :check-threw))) + +(defn abbrev-result + "Given a check result, returns an abbreviated version +suitable for summary use." + [x] + (if (:failure x) + (-> (dissoc x ::stc/ret) + (update :spec s/describe) + (update :failure unwrap-failure)) + (dissoc x :spec ::stc/ret))) + +(defn summarize-results + "Given a collection of check-results, e.g. from 'check', pretty +prints the summary-result (default abbrev-result) of each. + +Returns a map with :total, the total number of results, plus a +key with a count for each different :type of result." + ([check-results] (summarize-results check-results abbrev-result)) + ([check-results summary-result] + (reduce + (fn [summary result] + (pp/pprint (summary-result result)) + (-> summary + (update :total inc) + (update (result-type result) (fnil inc 0)))) + {:total 0} + check-results))) + +(comment + (require + '[cljs.pprint :as pp] + '[cljs.spec :as s] + '[cljs.spec.impl.gen :as gen] + '[cljs.test :as ctest]) + + (require :reload '[cljs.spec.test :as test]) + + ;; discover speced vars for your own test runner + (s/speced-vars) + + ;; check a single var + (test/check-var #'-) + (test/check-var #'+) + (test/check-var #'clojure.spec.broken-specs/throwing-fn) + + ;; old style example tests + (ctest/run-all-tests) + + (s/speced-vars 'clojure.spec.correct-specs) + ;; new style spec tests return same kind of map + (test/check-var #'subs) + (cljs.spec.test/run-tests 'clojure.core) + (test/run-all-tests) + + ;; example evaluation + (defn ranged-rand + "Returns random int in range start <= rand < end" + [start end] + (+ start (long (rand (- end start))))) + + (s/fdef ranged-rand + :args (s/and (s/cat :start int? :end int?) + #(< (:start %) (:end %))) + :ret int? + :fn (s/and #(>= (:ret %) (-> % :args :start)) + #(< (:ret %) (-> % :args :end)))) + + (instrumentable-syms) + + (m/instrument-1 `ranged-rand {}) + (m/unstrument-1 `ranged-rand) + + (m/instrument) + (m/instrument `ranged-rand) + (m/instrument `[ranged-rand]) + + (m/unstrument) + (m/unstrument `ranged-rand) + (m/unstrument `[ranged-rand]) + + (ranged-rand 8 5) + (defn foo + ([a]) + ([a b] + (ranged-rand 8 5))) + (foo 1 2) + (m/unstrument-1 `ranged-rand) + + (m/check-1 `ranged-rand nil nil {}) + + (m/check-fn inc + (s/fspec + :args (s/cat :x int?) + :ret int?)) + + (m/checkable-syms) + + (m/check `ranged-rand) + ) + + + + + diff --git a/src/test/cljs/cljs/clojure_alias_test.cljs b/src/test/cljs/cljs/clojure_alias_test.cljs index f35566cbc..3f1ee5f40 100644 --- a/src/test/cljs/cljs/clojure_alias_test.cljs +++ b/src/test/cljs/cljs/clojure_alias_test.cljs @@ -10,13 +10,13 @@ "Tests requiring via `clojure.*` instead of `cljs.*`" (:refer-clojure :exclude [use-macros]) (:require [clojure.test :refer [deftest is] :rename {is is?}] - [clojure.spec :as s :refer [spec? spec] :rename {spec foo}])) + [clojure.spec.alpha :as s :refer [spec? spec] :rename {spec foo}])) (deftest normal-test (is? (= 1 1))) (deftest aliases-test - (is? (= spec? clojure.spec/spec? cljs.spec/spec?)) + (is? (= spec? clojure.spec.alpha/spec? cljs.spec.alpha/spec?)) (is? (foo number?))) (deftest use-macros diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index e325d3085..a1386da10 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -1,7 +1,7 @@ (ns cljs.spec.test-test (:require [cljs.test :as test :refer-macros [deftest is are run-tests]] - [cljs.spec :as s] - [cljs.spec.test :as stest])) + [cljs.spec.alpha :as s] + [cljs.spec.test.alpha :as stest])) (defn h-cljs-1812 [x] true) (s/fdef h-cljs-1812 :args (s/cat :x int?) :ret true?) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 51534793d..b123f6616 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -7,9 +7,9 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.spec-test - (:require [cljs.spec :as s] + (:require [cljs.spec.alpha :as s] [cljs.test :as test :refer-macros [deftest is are run-tests]] - [cljs.spec.impl.gen :as gen] + [cljs.spec.impl.gen.alpha :as gen] [clojure.test.check.generators])) (s/def ::even? (s/and number? even?)) @@ -88,7 +88,7 @@ (defmulti mm :mm/type) (s/def ::foo-1788 (s/multi-spec mm :mm/type)) (is (= (s/form ::foo-1788) - '(cljs.spec/multi-spec cljs.spec-test/mm :mm/type)))) + '(cljs.spec.alpha/multi-spec cljs.spec-test/mm :mm/type)))) (def h-cljs-1790 (derive (make-hierarchy) :a :b)) (defmulti spec-type-1790 identity :hierarchy #'h-cljs-1790) @@ -245,25 +245,25 @@ (are [spec form] (= (s/form spec) form) (s/map-of int? any?) - '(cljs.spec/map-of cljs.core/int? cljs.core/any?) + '(cljs.spec.alpha/map-of cljs.core/int? cljs.core/any?) (s/coll-of int?) - '(cljs.spec/coll-of cljs.core/int?) + '(cljs.spec.alpha/coll-of cljs.core/int?) (s/every-kv int? int?) - '(cljs.spec/every-kv cljs.core/int? cljs.core/int?) + '(cljs.spec.alpha/every-kv cljs.core/int? cljs.core/int?) (s/every int?) - '(cljs.spec/every cljs.core/int?) + '(cljs.spec.alpha/every cljs.core/int?) (s/coll-of (s/tuple (s/tuple int?))) - '(cljs.spec/coll-of (cljs.spec/tuple (cljs.spec/tuple cljs.core/int?))) + '(cljs.spec.alpha/coll-of (cljs.spec.alpha/tuple (cljs.spec.alpha/tuple cljs.core/int?))) (s/coll-of int? :kind vector?) - '(cljs.spec/coll-of cljs.core/int? :kind cljs.core/vector?) + '(cljs.spec.alpha/coll-of cljs.core/int? :kind cljs.core/vector?) (s/coll-of int? :gen #(gen/return [1 2])) - '(cljs.spec/coll-of cljs.core/int? :gen (fn* [] (gen/return [1 2]))))) + '(cljs.spec.alpha/coll-of cljs.core/int? :gen (fn* [] (gen/return [1 2]))))) (comment From a0eaa6a32def7ec0e867d735e982d74280f6c664 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 May 2017 15:07:45 -0400 Subject: [PATCH 2304/4033] delete old spec files --- src/main/cljs/cljs/spec.cljc | 548 ---------- src/main/cljs/cljs/spec.cljs | 1444 ------------------------- src/main/cljs/cljs/spec/impl/gen.cljc | 75 -- src/main/cljs/cljs/spec/impl/gen.cljs | 180 --- src/main/cljs/cljs/spec/test.cljc | 251 ----- src/main/cljs/cljs/spec/test.cljs | 361 ------- 6 files changed, 2859 deletions(-) delete mode 100644 src/main/cljs/cljs/spec.cljc delete mode 100644 src/main/cljs/cljs/spec.cljs delete mode 100644 src/main/cljs/cljs/spec/impl/gen.cljc delete mode 100644 src/main/cljs/cljs/spec/impl/gen.cljs delete mode 100644 src/main/cljs/cljs/spec/test.cljc delete mode 100644 src/main/cljs/cljs/spec/test.cljs diff --git a/src/main/cljs/cljs/spec.cljc b/src/main/cljs/cljs/spec.cljc deleted file mode 100644 index f84ae6c63..000000000 --- a/src/main/cljs/cljs/spec.cljc +++ /dev/null @@ -1,548 +0,0 @@ -; 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.spec - (:refer-clojure :exclude [+ * and or cat def keys merge resolve assert]) - (:require [cljs.core :as c] - [cljs.analyzer :as ana] - [cljs.env :as env] - [cljs.analyzer.api :refer [resolve]] - [clojure.walk :as walk] - [cljs.spec.impl.gen :as gen] - [clojure.string :as str])) - -(defonce registry-ref (atom {})) - -(defn- ->sym - "Returns a symbol from a symbol or var" - [x] - (if (map? x) - (:name x) - x)) - -(defn- unfn [expr] - (if (clojure.core/and (seq? expr) - (symbol? (first expr)) - (= "fn*" (name (first expr)))) - (let [[[s] & form] (rest expr)] - (conj (walk/postwalk-replace {s '%} form) '[%] 'fn)) - expr)) - -(defn- res [env form] - (cond - (keyword? form) form - (symbol? form) (clojure.core/or (->> form (resolve env) ->sym) form) - (sequential? form) (walk/postwalk #(if (symbol? %) (res env %) %) (unfn form)) - :else form)) - -(defmacro ^:private mres - "a compile time res, for use in cljs/spec.cljs" - [form] - (res &env form)) - -(defn- ns-qualify - "Qualify symbol s by resolving it or using the current *ns*." - [env s] - (if (namespace s) - (let [v (resolve env s)] - (clojure.core/assert v (str "Unable to resolve: " s)) - (->sym v)) - (symbol (str ana/*cljs-ns*) (str s)))) - -(defmacro def - "Given a namespace-qualified keyword or resolveable symbol k, and a spec, - spec-name, predicate or regex-op makes an entry in the registry mapping k to - the spec" - [k spec-form] - (let [k (if (symbol? k) (ns-qualify &env k) k) - form (res &env spec-form)] - (swap! registry-ref assoc k form) - `(def-impl '~k '~form ~spec-form))) - -(defmacro spec - "Takes a single predicate form, e.g. can be the name of a predicate, - like even?, or a fn literal like #(< % 42). Note that it is not - generally necessary to wrap predicates in spec when using the rest - of the spec macros, only to attach a unique generator - - Can also be passed the result of one of the regex ops - - cat, alt, *, +, ?, in which case it will return a regex-conforming - spec, useful when nesting an independent regex. - --- - - Optionally takes :gen generator-fn, which must be a fn of no args that - returns a test.check generator. - - Returns a spec." - [form & {:keys [gen]}] - (when form - `(spec-impl '~(res &env form) ~form ~gen nil))) - -(defmacro multi-spec - "Takes the name of a spec/predicate-returning multimethod and a - tag-restoring keyword or fn (retag). Returns a spec that when - conforming or explaining data will pass it to the multimethod to get - an appropriate spec. You can e.g. use multi-spec to dynamically and - extensibly associate specs with 'tagged' data (i.e. data where one - of the fields indicates the shape of the rest of the structure). - - (defmulti mspec :tag) - - The methods should ignore their argument and return a predicate/spec: - (defmethod mspec :int [_] (s/keys :req-un [::tag ::i])) - - retag is used during generation to retag generated values with - matching tags. retag can either be a keyword, at which key the - dispatch-tag will be assoc'ed, or a fn of generated value and - dispatch-tag that should return an appropriately retagged value. - - Note that because the tags themselves comprise an open set, - the tag key spec cannot enumerate the values, but can e.g. - test for keyword?. - - Note also that the dispatch values of the multimethod will be - included in the path, i.e. in reporting and gen overrides, even - though those values are not evident in the spec. -" - [mm retag] - `(multi-spec-impl '~(res &env mm) (var ~mm) ~retag)) - -(defmacro keys - "Creates and returns a map validating spec. :req and :opt are both - vectors of namespaced-qualified keywords. The validator will ensure - the :req keys are present. The :opt keys serve as documentation and - may be used by the generator. - - The :req key vector supports 'and' and 'or' for key groups: - - (s/keys :req [::x ::y (or ::secret (and ::user ::pwd))] :opt [::z]) - - There are also -un versions of :req and :opt. These allow - you to connect unqualified keys to specs. In each case, fully - qualfied keywords are passed, which name the specs, but unqualified - keys (with the same name component) are expected and checked at - conform-time, and generated during gen: - - (s/keys :req-un [:my.ns/x :my.ns/y]) - - The above says keys :x and :y are required, and will be validated - and generated by specs (if they exist) named :my.ns/x :my.ns/y - respectively. - - In addition, the values of *all* namespace-qualified keys will be validated - (and possibly destructured) by any registered specs. Note: there is - no support for inline value specification, by design. - - Optionally takes :gen generator-fn, which must be a fn of no args that - returns a test.check generator." - [& {:keys [req req-un opt opt-un gen]}] - (let [unk #(-> % name keyword) - req-keys (filterv keyword? (flatten req)) - req-un-specs (filterv keyword? (flatten req-un)) - _ (clojure.core/assert (every? #(clojure.core/and (keyword? %) (namespace %)) (concat req-keys req-un-specs opt opt-un)) - "all keys must be namespace-qualified keywords") - req-specs (into req-keys req-un-specs) - req-keys (into req-keys (map unk req-un-specs)) - opt-keys (into (vec opt) (map unk opt-un)) - opt-specs (into (vec opt) opt-un) - gx (gensym) - parse-req (fn [rk f] - (map (fn [x] - (if (keyword? x) - `(contains? ~gx ~(f x)) - (walk/postwalk - (fn [y] (if (keyword? y) `(contains? ~gx ~(f y)) y)) - x))) - rk)) - pred-exprs [`(map? ~gx)] - pred-exprs (into pred-exprs (parse-req req identity)) - pred-exprs (into pred-exprs (parse-req req-un unk)) - keys-pred `(fn* [~gx] (c/and ~@pred-exprs)) - pred-exprs (mapv (fn [e] `(fn* [~gx] ~e)) pred-exprs) - pred-forms (walk/postwalk #(res &env %) pred-exprs)] - ;; `(map-spec-impl ~req-keys '~req ~opt '~pred-forms ~pred-exprs ~gen) - `(map-spec-impl {:req '~req :opt '~opt :req-un '~req-un :opt-un '~opt-un - :req-keys '~req-keys :req-specs '~req-specs - :opt-keys '~opt-keys :opt-specs '~opt-specs - :pred-forms '~pred-forms - :pred-exprs ~pred-exprs - :keys-pred ~keys-pred - :gfn ~gen}))) - -(defmacro or - "Takes key+pred pairs, e.g. - - (s/or :even even? :small #(< % 42)) - - Returns a destructuring spec that returns a map entry containing the - key of the first matching pred and the corresponding value. Thus the - 'key' and 'val' functions can be used to refer generically to the - components of the tagged return." - [& key-pred-forms] - (let [pairs (partition 2 key-pred-forms) - keys (mapv first pairs) - pred-forms (mapv second pairs) - pf (mapv #(res &env %) pred-forms)] - (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "spec/or expects k1 p1 k2 p2..., where ks are keywords") - `(or-spec-impl ~keys '~pf ~pred-forms nil))) - -(defmacro and - "Takes predicate/spec-forms, e.g. - - (s/and even? #(< % 42)) - - Returns a spec that returns the conformed value. Successive - conformed values propagate through rest of predicates." - [& pred-forms] - `(and-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) - -(defn- res-kind - [env opts] - (let [{kind :kind :as mopts} opts] - (->> - (if kind - (assoc mopts :kind `~(res env kind)) - mopts) - (mapcat identity)))) - -(defmacro every - "takes a pred and validates collection elements against that pred. - - Note that 'every' does not do exhaustive checking, rather it samples - *coll-check-limit* elements. Nor (as a result) does it do any - conforming of elements. 'explain' will report at most *coll-error-limit* - problems. Thus 'every' should be suitable for potentially large - collections. - - Takes several kwargs options that further constrain the collection: - - :kind - a pred/spec that the collection type must satisfy, e.g. vector? - (default nil) Note that if :kind is specified and :into is - not, this pred must generate in order for every to generate. - :count - specifies coll has exactly this count (default nil) - :min-count, :max-count - coll has count (<= min-count count max-count) (defaults nil) - :distinct - all the elements are distinct (default nil) - - And additional args that control gen - - :gen-max - the maximum coll size to generate (default 20) - :into - one of [], (), {}, #{} - the default collection to generate into - (default same as :kind if supplied, else [] - - Optionally takes :gen generator-fn, which must be a fn of no args that - returns a test.check generator - - See also - coll-of, every-kv -" - [pred & {:keys [into kind count max-count min-count distinct gen-max gen-into gen] :as opts}] - (let [desc (::describe opts) - nopts (-> opts - (dissoc :gen ::describe) - (assoc ::kind-form `'~(res &env (:kind opts)) - ::describe (clojure.core/or desc `'(every ~(res &env pred) ~@(res-kind &env opts))))) - gx (gensym) - cpreds (cond-> [(list (clojure.core/or kind `coll?) gx)] - count (conj `(= ~count (c/bounded-count ~count ~gx))) - - (clojure.core/or min-count max-count) - (conj `(<= (c/or ~min-count 0) - (c/bounded-count (if ~max-count (inc ~max-count) ~min-count) ~gx) - (c/or ~max-count MAX_INT))) - - distinct - (conj `(c/or (empty? ~gx) (apply distinct? ~gx))))] - `(every-impl '~pred ~pred ~(assoc nopts ::cpred `(fn* [~gx] (c/and ~@cpreds))) ~gen))) - -(defmacro every-kv - "like 'every' but takes separate key and val preds and works on associative collections. - - Same options as 'every', :into defaults to {} - - See also - map-of" - - [kpred vpred & opts] - (let [desc `(every-kv ~(res &env kpred) ~(res &env vpred) ~@(res-kind &env opts))] - `(every (tuple ~kpred ~vpred) ::kfn (fn [i# v#] (nth v# 0)) :into {} ::describe '~desc ~@opts))) - -(defmacro coll-of - "Returns a spec for a collection of items satisfying pred. Unlike - generator will fill an empty init-coll. - - Same options as 'every'. conform will produce a collection - corresponding to :into if supplied, else will match the input collection, - avoiding rebuilding when possible. - - Same options as 'every'. - - See also - every, map-of" - [pred & opts] - (let [desc `(coll-of ~(res &env pred) ~@(res-kind &env opts))] - `(every ~pred ::conform-all true ::describe '~desc ~@opts))) - -(defmacro map-of - "Returns a spec for a map whose keys satisfy kpred and vals satisfy - vpred. Unlike 'every-kv', map-of will exhaustively conform every - value. - - Same options as 'every', :kind defaults to map?, with the addition of: - - :conform-keys - conform keys as well as values (default false) - - See also - every-kv" - [kpred vpred & opts] - (let [desc `(map-of ~(res &env kpred) ~(res &env vpred) ~@(res-kind &env opts))] - `(every-kv ~kpred ~vpred ::conform-all true :kind map? ::describe '~desc ~@opts))) - -(defmacro * - "Returns a regex op that matches zero or more values matching - pred. Produces a vector of matches iff there is at least one match" - [pred-form] - `(rep-impl '~(res &env pred-form) ~pred-form)) - -(defmacro + - "Returns a regex op that matches one or more values matching - pred. Produces a vector of matches" - [pred-form] - `(rep+impl '~(res &env pred-form) ~pred-form)) - -(defmacro ? - "Returns a regex op that matches zero or one value matching - pred. Produces a single value (not a collection) if matched." - [pred-form] - `(maybe-impl ~pred-form '~pred-form)) - -(defmacro alt - "Takes key+pred pairs, e.g. - - (s/alt :even even? :small #(< % 42)) - - Returns a regex op that returns a map entry containing the key of the - first matching pred and the corresponding value. Thus the - 'key' and 'val' functions can be used to refer generically to the - components of the tagged return." - [& key-pred-forms] - (let [pairs (partition 2 key-pred-forms) - keys (mapv first pairs) - pred-forms (mapv second pairs) - pf (mapv #(res &env %) pred-forms)] - (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "alt expects k1 p1 k2 p2..., where ks are keywords") - `(alt-impl ~keys ~pred-forms '~pf))) - -(defmacro cat - "Takes key+pred pairs, e.g. - - (s/cat :e even? :o odd?) - - Returns a regex op that matches (all) values in sequence, returning a map - containing the keys of each pred and the corresponding value." - [& key-pred-forms] - (let [pairs (partition 2 key-pred-forms) - keys (mapv first pairs) - pred-forms (mapv second pairs) - pf (mapv #(res &env %) pred-forms)] - ;;(prn key-pred-forms) - (clojure.core/assert (clojure.core/and (even? (count key-pred-forms)) (every? keyword? keys)) "cat expects k1 p1 k2 p2..., where ks are keywords") - `(cat-impl ~keys ~pred-forms '~pf))) - -(defmacro & - "takes a regex op re, and predicates. Returns a regex-op that consumes - input as per re but subjects the resulting value to the - conjunction of the predicates, and any conforming they might perform." - [re & preds] - (let [pv (vec preds)] - `(amp-impl ~re ~pv '~(mapv #(res &env %) pv)))) - -(defmacro conformer - "takes a predicate function with the semantics of conform i.e. it should return either a - (possibly converted) value or :cljs.spec/invalid, and returns a - spec that uses it as a predicate/conformer. Optionally takes a - second fn that does unform of result of first" - ([f] `(spec-impl '(conformer ~(res &env f)) ~f nil true)) - ([f unf] `(spec-impl '(conformer ~(res &env f) ~(res &env unf)) ~f nil true ~unf))) - -(defmacro fspec - "takes :args :ret and (optional) :fn kwargs whose values are preds - and returns a spec whose conform/explain take a fn and validates it - using generative testing. The conformed value is always the fn itself. - - See 'fdef' for a single operation that creates an fspec and - registers it, as well as a full description of :args, :ret and :fn - - fspecs can generate functions that validate the arguments and - fabricate a return value compliant with the :ret spec, ignoring - the :fn spec if present. - - Optionally takes :gen generator-fn, which must be a fn of no args - that returns a test.check generator." - [& {:keys [args ret fn gen]}] - (let [env &env] - `(fspec-impl (spec ~args) '~(res env args) - (spec ~ret) '~(res env ret) - (spec ~fn) '~(res env fn) ~gen))) - -(defmacro tuple - "takes one or more preds and returns a spec for a tuple, a vector - where each element conforms to the corresponding pred. Each element - will be referred to in paths using its ordinal." - [& preds] - (clojure.core/assert (not (empty? preds))) - `(tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) - -(def ^:private _speced_vars (atom #{})) - -(defn speced-vars [] - @_speced_vars) - -(defmacro fdef - "Takes a symbol naming a function, and one or more of the following: - - :args A regex spec for the function arguments as they were a list to be - passed to apply - in this way, a single spec can handle functions with - multiple arities - :ret A spec for the function's return value - :fn A spec of the relationship between args and ret - the - value passed is {:args conformed-args :ret conformed-ret} and is - expected to contain predicates that relate those values - - Qualifies fn-sym with resolve, or using *ns* if no resolution found. - Registers an fspec in the global registry, where it can be retrieved - by calling get-spec with the var or full-qualified symbol. - - Once registered, function specs are included in doc, checked by - instrument, tested by the runner clojure.spec.test/run-tests, and (if - a macro) used to explain errors during macroexpansion. - - Note that :fn specs require the presence of :args and :ret specs to - conform values, and so :fn specs will be ignored if :args or :ret - are missing. - - Returns the qualified fn-sym. - - For example, to register function specs for the symbol function: - - (s/fdef clojure.core/symbol - :args (s/alt :separate (s/cat :ns string? :n string?) - :str string? - :sym symbol?) - :ret symbol?)" - [fn-sym & specs] - (swap! _speced_vars conj (ns-qualify &env fn-sym)) - `(cljs.spec/def ~fn-sym (fspec ~@specs))) - -(defmacro keys* - "takes the same arguments as spec/keys and returns a regex op that matches sequences of key/values, - converts them into a map, and conforms that map with a corresponding - spec/keys call: - - user=> (s/conform (s/keys :req-un [::a ::c]) {:a 1 :c 2}) - {:a 1, :c 2} - user=> (s/conform (s/keys* :req-un [::a ::c]) [:a 1 :c 2]) - {:a 1, :c 2} - - the resulting regex op can be composed into a larger regex: - - user=> (s/conform (s/cat :i1 integer? :m (s/keys* :req-un [::a ::c]) :i2 integer?) [42 :a 1 :c 2 :d 4 99]) - {:i1 42, :m {:a 1, :c 2, :d 4}, :i2 99}" - [& kspecs] - `(let [mspec# (keys ~@kspecs)] - (with-gen (cljs.spec/& (* (cat ::k keyword? ::v cljs.core/any?)) ::kvs->map mspec#) - (fn [] (gen/fmap (fn [m#] (apply concat m#)) (gen mspec#)))))) - -(defmacro nilable - "returns a spec that accepts nil and values satisfiying pred" - [pred] - (let [pf (res &env pred)] - `(nilable-impl '~pf ~pred nil))) - -(defmacro inst-in - "Returns a spec that validates insts in the range from start - (inclusive) to end (exclusive)." - [start end] - `(let [st# (cljs.core/inst-ms ~start) - et# (cljs.core/inst-ms ~end) - mkdate# (fn [d#] (js/Date. d#))] - (spec (and cljs.core/inst? #(inst-in-range? ~start ~end %)) - :gen (fn [] - (gen/fmap mkdate# - (gen/large-integer* {:min st# :max et#})))))) - -(defmacro int-in - "Returns a spec that validates longs in the range from start - (inclusive) to end (exclusive)." - [start end] - `(spec (and c/int? #(int-in-range? ~start ~end %)) - :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) - -(defmacro double-in - "Specs a 64-bit floating point number. Options: - - :infinite? - whether +/- infinity allowed (default true) - :NaN? - whether NaN allowed (default true) - :min - minimum value (inclusive, default none) - :max - maximum value (inclusive, default none)" - [& {:keys [infinite? NaN? min max] - :or {infinite? true NaN? true} - :as m}] - `(spec (and c/double? - ~@(when-not infinite? '[#(not (infinite? %))]) - ~@(when-not NaN? '[#(not (js/isNaN %))]) - ~@(when max `[#(<= % ~max)]) - ~@(when min `[#(<= ~min %)])) - :gen #(gen/double* ~m))) - -(defmacro merge - "Takes map-validating specs (e.g. 'keys' specs) and - returns a spec that returns a conformed map satisfying all of the - specs. Successive conformed values propagate through rest of - predicates. Unlike 'and', merge can generate maps satisfying the - union of the predicates." - [& pred-forms] - `(merge-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) - -(defmacro exercise-fn - "exercises the fn named by sym (a symbol) by applying it to - n (default 10) generated samples of its args spec. When fspec is - supplied its arg spec is used, and sym-or-f can be a fn. Returns a - sequence of tuples of [args ret]. " - ([sym] - `(exercise-fn ~sym 10)) - ([sym n] - `(exercise-fn ~sym ~n nil)) - ([sym n fspec] - (let [sym (cond-> sym - (clojure.core/and (sequential? sym) - (= (first sym) 'quote)) - second)] - `(let [fspec# ~(if-not fspec - `(get-spec '~(:name (resolve &env sym))) - fspec) - f# ~sym] - (for [args# (gen/sample (gen (:args fspec#)) ~n)] - [args# (apply f# args#)]))))) - -(defmacro ^:private init-compile-asserts [] - (let [compile-asserts (not (-> env/*compiler* deref :options :elide-asserts))] - compile-asserts)) - -(defmacro assert - "spec-checking assert expression. Returns x if x is valid? according -to spec, else throws an error with explain-data plus ::failure of -:assertion-failed. -Can be disabled at either compile time or runtime: -If *compile-asserts* is false at compile time, compiles to x. Defaults -to the negation value of the ':elide-asserts' compiler option, or true if -not set. -If (check-asserts?) is false at runtime, always returns x. Defaults to -value of 'cljs.spec/*runtime-asserts*', or false if not set. You can -toggle check-asserts? with (check-asserts bool)." - [spec x] - `(if *compile-asserts* - (if *runtime-asserts* - (assert* ~spec ~x) - ~x) - ~x)) diff --git a/src/main/cljs/cljs/spec.cljs b/src/main/cljs/cljs/spec.cljs deleted file mode 100644 index b25595a95..000000000 --- a/src/main/cljs/cljs/spec.cljs +++ /dev/null @@ -1,1444 +0,0 @@ -; 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.spec - (:refer-clojure :exclude [+ * and or cat def keys merge]) - (:require-macros [cljs.core :as c] - [cljs.spec :as s]) - (:require [goog.object :as gobj] - [cljs.core :as c] - [clojure.walk :as walk] - [cljs.spec.impl.gen :as gen] - [clojure.string :as str])) - -(def ^:const MAX_INT 9007199254740991) - -(def ^:dynamic *recursion-limit* - "A soft limit on how many times a branching spec (or/alt/*/opt-keys/multi-spec) - can be recursed through during generation. After this a - non-recursive branch will be chosen." - 4) - -(def ^:dynamic *fspec-iterations* - "The number of times an anonymous fn specified by fspec will be (generatively) tested during conform" - 21) - -(def ^:dynamic *coll-check-limit* - "The number of items validated in a collection spec'ed with 'every'" - 101) - -(def ^:dynamic *coll-error-limit* - "The number of errors reported by explain in a collection spec'ed with 'every'" - 20) - -(defprotocol Spec - (conform* [spec x]) - (unform* [spec y]) - (explain* [spec path via in x]) - (gen* [spec overrides path rmap]) - (with-gen* [spec gfn]) - (describe* [spec])) - -(defonce ^:private registry-ref (atom {})) - -(defn- deep-resolve [reg k] - (loop [spec k] - (if (ident? spec) - (recur (get reg spec)) - spec))) - -(defn- reg-resolve - "returns the spec/regex at end of alias chain starting with k, nil if not found, k if k not ident" - [k] - (if (ident? k) - (let [reg @registry-ref - spec (get reg k)] - (if-not (ident? spec) - spec - (deep-resolve reg spec))) - k)) - -(defn- reg-resolve! - "returns the spec/regex at end of alias chain starting with k, throws if not found, k if k not ident" - [k] - (if (ident? k) - (c/or (reg-resolve k) - (throw (js/Error. (str "Unable to resolve spec: " k)))) - k)) - -(defn spec? - "returns x if x is a spec object, else logical false" - [x] - (when (implements? Spec x) - x)) - -(defn regex? - "returns x if x is a (clojure.spec) regex op, else logical false" - [x] - (c/and (::op x) x)) - -(defn- with-name [spec name] - (cond - (ident? spec) spec - (regex? spec) (assoc spec ::name name) - - (implements? IMeta spec) - (with-meta spec (assoc (meta spec) ::name name)))) - -(defn- spec-name [spec] - (cond - (ident? spec) spec - - (regex? spec) (::name spec) - - (implements? IMeta spec) - (-> (meta spec) ::name))) - -(declare spec-impl) -(declare regex-spec-impl) - -(defn- maybe-spec - "spec-or-k must be a spec, regex or resolvable kw/sym, else returns nil." - [spec-or-k] - (let [s (c/or (c/and (ident? spec-or-k) (reg-resolve spec-or-k)) - (spec? spec-or-k) - (regex? spec-or-k) - nil)] - (if (regex? s) - (with-name (regex-spec-impl s nil) (spec-name s)) - s))) - -(defn- the-spec - "spec-or-k must be a spec, regex or kw/sym, else returns nil. Throws if unresolvable kw/sym" - [spec-or-k] - (c/or (maybe-spec spec-or-k) - (when (ident? spec-or-k) - (throw (js/Error. (str "Unable to resolve spec: " spec-or-k)))))) - -(defprotocol Specize - (specize* [_] [_ form])) - -(extend-protocol Specize - Keyword - (specize* ([k] (specize* (reg-resolve! k))) - ([k _] (specize* (reg-resolve! k)))) - - Symbol - (specize* ([s] (specize* (reg-resolve! s))) - ([s _] (specize* (reg-resolve! s)))) - - default - (specize* - ([o] (spec-impl ::unknown o nil nil)) - ([o form] (spec-impl form o nil nil)))) - -(defn- specize - ([s] (c/or (spec? s) (specize* s))) - ([s form] (c/or (spec? s) (specize* s form)))) - -(defn invalid? - "tests the validity of a conform return value" - [ret] - (keyword-identical? ::invalid ret)) - -(defn conform - "Given a spec and a value, returns :clojure.spec/invalid if value does not match spec, - else the (possibly destructured) value." - [spec x] - (conform* (specize spec) x)) - -(defn unform - "Given a spec and a value created by or compliant with a call to - 'conform' with the same spec, returns a value with all conform - destructuring undone." - [spec x] - (unform* (specize spec) x)) - -(defn form - "returns the spec as data" - [spec] - ;;TODO - incorporate gens - (describe* (specize spec))) - -(defn abbrev [form] - (cond - (seq? form) - (walk/postwalk (fn [form] - (cond - (c/and (symbol? form) (namespace form)) - (-> form name symbol) - - (c/and (seq? form) (= 'fn (first form)) (= '[%] (second form))) - (last form) - - :else form)) - form) - - (c/and (symbol? form) (namespace form)) - (-> form name symbol) - - :else form)) - -(defn describe - "returns an abbreviated description of the spec as data" - [spec] - (abbrev (form spec))) - -(defn with-gen - "Takes a spec and a no-arg, generator-returning fn and returns a version of that spec that uses that generator" - [spec gen-fn] - (let [spec (reg-resolve spec)] - (if (regex? spec) - (assoc spec ::gfn gen-fn) - (with-gen* (specize spec) gen-fn)))) - -(defn explain-data* [spec path via in x] - (when-let [probs (explain* (specize spec) path via in x)] - (when-not (empty? probs) - {::problems probs}))) - -(defn explain-data - "Given a spec and a value x which ought to conform, returns nil if x - conforms, else a map with at least the key ::problems whose value is - a collection of problem-maps, where problem-map has at least :path :pred and :val - keys describing the predicate and the value that failed at that - path." - [spec x] - (explain-data* spec [] (if-let [name (spec-name spec)] [name] []) [] x)) - -(defn explain-printer - "Default printer for explain-data. nil indicates a successful validation." - [ed] - (if ed - (print - (with-out-str - ;;(prn {:ed ed}) - (doseq [{:keys [path pred val reason via in] :as prob} (::problems ed)] - (when-not (empty? in) - (print "In:" (pr-str in) "")) - (print "val: ") - (pr val) - (print " fails") - (when-not (empty? via) - (print " spec:" (pr-str (last via)))) - (when-not (empty? path) - (print " at:" (pr-str path))) - (print " predicate: ") - (pr (abbrev pred)) - (when reason (print ", " reason)) - (doseq [[k v] prob] - (when-not (#{:path :pred :val :reason :via :in} k) - (print "\n\t" (pr-str k) " ") - (pr v))) - (newline)) - (doseq [[k v] ed] - (when-not (#{::problems} k) - (print (pr-str k) " ") - (pr v) - (newline))))) - (println "Success!"))) - -(def ^:dynamic *explain-out* explain-printer) - -(defn explain-out - "Prints explanation data (per 'explain-data') to *out* using the printer in *explain-out*, - by default explain-printer." - [ed] - (*explain-out* ed)) - -(defn explain - "Given a spec and a value that fails to conform, prints an explanation to *out*." - [spec x] - (explain-out (explain-data spec x))) - -(defn explain-str - "Given a spec and a value that fails to conform, returns an explanation as a string." - [spec x] - (with-out-str (explain spec x))) - -(declare valid?) - -(defn- gensub - [spec overrides path rmap form] - ;;(prn {:spec spec :over overrides :path path :form form}) - (let [spec (specize spec)] - (if-let [g (c/or (when-let [gfn (c/or (get overrides (c/or (spec-name spec) spec)) - (get overrides path))] - (gfn)) - (gen* spec overrides path rmap))] - (gen/such-that #(valid? spec %) g 100) - (throw (js/Error. (str "Unable to construct gen at: " path " for: " (abbrev form))))))) - -(defn gen - "Given a spec, returns the generator for it, or throws if none can - be constructed. Optionally an overrides map can be provided which - should map spec names or paths (vectors of keywords) to no-arg - generator-creating fns. These will be used instead of the generators at those - names/paths. Note that parent generator (in the spec or overrides - map) will supersede those of any subtrees. A generator for a regex - op must always return a sequential collection (i.e. a generator for - s/? should return either an empty sequence/vector or a - sequence/vector with one item in it)" - ([spec] (gen spec nil)) - ([spec overrides] (gensub spec overrides [] {::recursion-limit *recursion-limit*} spec))) - -(defn ^:skip-wiki def-impl - "Do not call this directly, use 'def'" - [k form spec] - (assert (c/and (ident? k) (namespace k)) "k must be namespaced keyword or resolveable symbol") - (let [spec (if (c/or (spec? spec) (regex? spec) (get @registry-ref spec)) - spec - (spec-impl form spec nil nil))] - (swap! registry-ref assoc k (with-name spec k)) - k)) - -(defn registry - "returns the registry map, prefer 'get-spec' to lookup a spec by name" - [] - @registry-ref) - -(defn- ->sym - "Returns a symbol from a symbol or var" - [x] - (if (var? x) - (.-sym x) - x)) - -(defn get-spec - "Returns spec registered for keyword/symbol/var k, or nil." - [k] - (get (registry) (if (keyword? k) k (->sym k)))) - -(declare map-spec) - -(defn- macroexpand-check - [v args] - (let [specs (get-spec v)] - (when-let [arg-spec (:args specs)] - (when (invalid? (conform arg-spec args)) - (let [ed (assoc (explain-data* arg-spec [:args] - (if-let [name (spec-name arg-spec)] [name] []) [] args) - ::args args)] - (throw (js/Error. - (str - "Call to " (->sym v) " did not conform to spec:\n" - (with-out-str (explain-out ed)))))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; impl ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- recur-limit? [rmap id path k] - (c/and (> (get rmap id) (::recursion-limit rmap)) - (contains? (set path) k))) - -(defn- inck [m k] - (assoc m k (inc (c/or (get m k) 0)))) - -(defn- dt - ([pred x form] (dt pred x form nil)) - ([pred x form cpred?] - (if pred - (if-let [spec (the-spec pred)] - (conform spec x) - (if (ifn? pred) - (if cpred? - (pred x) - (if (pred x) x ::invalid)) - (throw (js/Error. (str (pr-str form) " is not a fn, expected predicate fn"))))) - x))) - -(defn valid? - "Helper function that returns true when x is valid for spec." - ([spec x] - (let [spec (specize spec)] - (not (invalid? (conform* spec x))))) - ([spec x form] - (let [spec (specize spec form)] - (not (invalid? (conform* spec x)))))) - -(defn- pvalid? - "internal helper function that returns true when x is valid for spec." - ([pred x] - (not (invalid? (dt pred x ::unknown)))) - ([pred x form] - (not (invalid? (dt pred x form))))) - -(defn- explain-1 [form pred path via in v] - ;;(prn {:form form :pred pred :path path :in in :v v}) - (let [pred (maybe-spec pred)] - (if (spec? pred) - (explain* pred path (if-let [name (spec-name pred)] (conj via name) via) in v) - [{:path path :pred (abbrev form) :val v :via via :in in}]))) - -(defn ^:skip-wiki map-spec-impl - "Do not call this directly, use 'spec' with a map argument" - [{:keys [req-un opt-un keys-pred pred-exprs opt-keys req-specs req req-keys opt-specs pred-forms opt gfn] - :as argm}] - (let [k->s (zipmap (concat req-keys opt-keys) (concat req-specs opt-specs)) - keys->specnames #(c/or (k->s %) %) - id (random-uuid)] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ m] - (if (keys-pred m) - (let [reg (registry)] - (loop [ret m, [[k v] & ks :as keys] m] - (if keys - (let [sname (keys->specnames k)] - (if-let [s (get reg sname)] - (let [cv (conform s v)] - (if (invalid? cv) - ::invalid - (recur (if (identical? cv v) ret (assoc ret k cv)) - ks))) - (recur ret ks))) - ret))) - ::invalid)) - (unform* [_ m] - (let [reg (registry)] - (loop [ret m, [k & ks :as keys] (c/keys m)] - (if keys - (if (contains? reg (keys->specnames k)) - (let [cv (get m k) - v (unform (keys->specnames k) cv)] - (recur (if (identical? cv v) ret (assoc ret k v)) - ks)) - (recur ret ks)) - ret)))) - (explain* [_ path via in x] - (if-not (map? x) - [{:path path :pred 'map? :val x :via via :in in}] - (let [reg (registry)] - (apply concat - (when-let [probs (->> (map (fn [pred form] (when-not (pred x) (abbrev form))) - pred-exprs pred-forms) - (keep identity) - seq)] - (map - #(identity {:path path :pred % :val x :via via :in in}) - probs)) - (map (fn [[k v]] - (when-not (c/or (not (contains? reg (keys->specnames k))) - (pvalid? (keys->specnames k) v k)) - (explain-1 (keys->specnames k) (keys->specnames k) (conj path k) via (conj in k) v))) - (seq x)))))) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (let [rmap (inck rmap id) - gen (fn [k s] (gensub s overrides (conj path k) rmap k)) - ogen (fn [k s] - (when-not (recur-limit? rmap id path k) - [k (gen/delay (gensub s overrides (conj path k) rmap k))])) - req-gens (map gen req-keys req-specs) - opt-gens (remove nil? (map ogen opt-keys opt-specs))] - (when (every? identity (concat req-gens opt-gens)) - (let [reqs (zipmap req-keys req-gens) - opts (into {} opt-gens)] - (gen/bind (gen/choose 0 (count opts)) - #(let [args (concat (seq reqs) (when (seq opts) (shuffle (seq opts))))] - (->> args - (take (c/+ % (count reqs))) - (apply concat) - (apply gen/hash-map))))))))) - (with-gen* [_ gfn] (map-spec-impl (assoc argm :gfn gfn))) - (describe* [_] (cons `keys - (cond-> [] - req (conj :req req) - opt (conj :opt opt) - req-un (conj :req-un req-un) - opt-un (conj :opt-un opt-un))))))) - -(defn ^:skip-wiki spec-impl - "Do not call this directly, use 'spec'" - ([form pred gfn cpred?] (spec-impl form pred gfn cpred? nil)) - ([form pred gfn cpred? unc] - (cond - (spec? pred) (cond-> pred gfn (with-gen gfn)) - (regex? pred) (regex-spec-impl pred gfn) - (ident? pred) (cond-> (the-spec pred) gfn (with-gen gfn)) - :else - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] (let [ret (pred x)] - (if cpred? - ret - (if ret x ::invalid)))) - (unform* [_ x] (if cpred? - (if unc - (unc x) - (throw (js/Error. "no unform fn for conformer"))) - x)) - (explain* [_ path via in x] - (when (invalid? (dt pred x form cpred?)) - [{:path path :pred (abbrev form) :val x :via via :in in}])) - (gen* [_ _ _ _] (if gfn - (gfn) - (gen/gen-for-pred pred))) - (with-gen* [_ gfn] (spec-impl form pred gfn cpred? unc)) - (describe* [_] form))))) - -(defn ^:skip-wiki multi-spec-impl - "Do not call this directly, use 'multi-spec'" - ([form mmvar retag] (multi-spec-impl form mmvar retag nil)) - ([form mmvar retag gfn] - (let [id (random-uuid) - predx #(let [mm @mmvar] - (c/and (-get-method mm ((-dispatch-fn mm) %)) - (mm %))) - dval #((-dispatch-fn @mmvar) %) - tag (if (keyword? retag) - #(assoc %1 retag %2) - retag)] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] (if-let [pred (predx x)] - (dt pred x form) - ::invalid)) - (unform* [_ x] (if-let [pred (predx x)] - (unform pred x) - (throw (js/Error. (str "No method of: " form " for dispatch value: " (dval x)))))) - (explain* [_ path via in x] - (let [dv (dval x) - path (conj path dv)] - (if-let [pred (predx x)] - (explain-1 form pred path via in x) - [{:path path :pred (abbrev form) :val x :reason "no method" :via via :in in}]))) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (let [gen (fn [[k f]] - (let [p (f nil)] - (let [rmap (inck rmap id)] - (when-not (recur-limit? rmap id path k) - (gen/delay - (gen/fmap - #(tag % k) - (gensub p overrides (conj path k) rmap (list 'method form k)))))))) - gs (->> (methods @mmvar) - (remove (fn [[k]] (invalid? k))) - (map gen) - (remove nil?))] - (when (every? identity gs) - (gen/one-of gs))))) - (with-gen* [_ gfn] (multi-spec-impl form mmvar retag gfn)) - (describe* [_] `(multi-spec ~form ~retag)))))) - -(defn ^:skip-wiki tuple-impl - "Do not call this directly, use 'tuple'" - ([forms preds] (tuple-impl forms preds nil)) - ([forms preds gfn] - (let [specs (delay (mapv specize preds forms)) - cnt (count preds)] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] - (let [specs @specs] - (if-not (c/and (vector? x) - (= (count x) cnt)) - ::invalid - (loop [ret x, i 0] - (if (= i cnt) - ret - (let [v (x i) - cv (conform* (specs i) v)] - (if (invalid? cv) - ::invalid - (recur (if (identical? cv v) ret (assoc ret i cv)) - (inc i))))))))) - (unform* [_ x] - (assert (c/and (vector? x) - (= (count x) (count preds)))) - (loop [ret x, i 0] - (if (= i (count x)) - ret - (let [cv (x i) - v (unform (preds i) cv)] - (recur (if (identical? cv v) ret (assoc ret i v)) - (inc i)))))) - (explain* [_ path via in x] - (cond - (not (vector? x)) - [{:path path :pred 'vector? :val x :via via :in in}] - - (not= (count x) (count preds)) - [{:path path :pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}] - - :else - (apply concat - (map (fn [i form pred] - (let [v (x i)] - (when-not (pvalid? pred v) - (explain-1 form pred (conj path i) via (conj in i) v)))) - (range (count preds)) forms preds)))) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (let [gen (fn [i p f] - (gensub p overrides (conj path i) rmap f)) - gs (map gen (range (count preds)) preds forms)] - (when (every? identity gs) - (apply gen/tuple gs))))) - (with-gen* [_ gfn] (tuple-impl forms preds gfn)) - (describe* [_] `(tuple ~@forms)))))) - -(defn- tagged-ret [v] - (specify! v - IMapEntry - (-key [_] (-nth v 0)) - (-val [_] (-nth v 1)))) - -(defn ^:skip-wiki or-spec-impl - "Do not call this directly, use 'or'" - [keys forms preds gfn] - (let [id (random-uuid) - kps (zipmap keys preds) - specs (delay (mapv specize preds forms)) - cform (case (count preds) - 2 (fn [x] - (let [specs @specs - ret (conform* (specs 0) x)] - (if (invalid? ret) - (let [ret (conform* (specs 1) x)] - (if (invalid? ret) - ::invalid - (tagged-ret [(keys 1) ret]))) - (tagged-ret [(keys 0) ret])))) - 3 (fn [x] - (let [specs @specs - ret (conform* (specs 0) x)] - (if (invalid? ret) - (let [ret (conform* (specs 1) x)] - (if (invalid? ret) - (let [ret (conform* (specs 2) x)] - (if (invalid? ret) - ::invalid - (tagged-ret [(keys 2) ret]))) - (tagged-ret [(keys 1) ret]))) - (tagged-ret [(keys 0) ret])))) - (fn [x] - (let [specs @specs] - (loop [i 0] - (if (< i (count specs)) - (let [spec (specs i)] - (let [ret (conform* spec x)] - (if (invalid? ret) - (recur (inc i)) - (tagged-ret [(keys i) ret])))) - ::invalid)))))] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] (cform x)) - (unform* [_ [k x]] (unform (kps k) x)) - (explain* [this path via in x] - (when-not (pvalid? this x) - (apply concat - (map (fn [k form pred] - (when-not (pvalid? pred x) - (explain-1 form pred (conj path k) via in x))) - keys forms preds)))) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (let [gen (fn [k p f] - (let [rmap (inck rmap id)] - (when-not (recur-limit? rmap id path k) - (gen/delay - (gensub p overrides (conj path k) rmap f))))) - gs (remove nil? (map gen keys preds forms))] - (when-not (empty? gs) - (gen/one-of gs))))) - (with-gen* [_ gfn] (or-spec-impl keys forms preds gfn)) - (describe* [_] `(or ~@(mapcat vector keys forms)))))) - -(defn- and-preds [x preds forms] - (loop [ret x - [pred & preds] preds - [form & forms] forms] - (if pred - (let [nret (dt pred ret form)] - (if (invalid? nret) - ::invalid - ;;propagate conformed values - (recur nret preds forms))) - ret))) - -(defn- explain-pred-list - [forms preds path via in x] - (loop [ret x - [form & forms] forms - [pred & preds] preds] - (when pred - (let [nret (dt pred ret form)] - (if (invalid? nret) - (explain-1 form pred path via in ret) - (recur nret forms preds)))))) - -(defn ^:skip-wiki and-spec-impl - "Do not call this directly, use 'and'" - [forms preds gfn] - (let [specs (delay (mapv specize preds forms)) - cform - (case (count preds) - 2 (fn [x] - (let [specs @specs - ret (conform* (specs 0) x)] - (if (invalid? ret) - ::invalid - (conform* (specs 1) ret)))) - 3 (fn [x] - (let [specs @specs - ret (conform* (specs 0) x)] - (if (invalid? ret) - ::invalid - (let [ret (conform* (specs 1) ret)] - (if (invalid? ret) - ::invalid - (conform* (specs 2) ret)))))) - (fn [x] - (let [specs @specs] - (loop [ret x i 0] - (if (< i (count specs)) - (let [nret (conform* (specs i) ret)] - (if (invalid? nret) - ::invalid - ;;propagate conformed values - (recur nret (inc i)))) - ret)))))] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] (cform x)) - (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) - (explain* [_ path via in x] (explain-pred-list forms preds path via in x)) - (gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms)))) - (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) - (describe* [_] `(and ~@forms))))) - -(defn- coll-prob [x kfn kform distinct count min-count max-count - path via in] - (let [pred (c/or kfn coll?) - kform (c/or kform `coll?)] - (cond - (not (pvalid? pred x)) - (explain-1 kform pred path via in x) - - (c/and count (not= count (bounded-count count x))) - [{:path path :pred `(= ~count (c/count ~'%)) :val x :via via :in in}] - - (c/and (c/or min-count max-count) - (not (<= (c/or min-count 0) - (bounded-count (if max-count (inc max-count) min-count) x) - (c/or max-count MAX_INT)))) - [{:path path :pred `(<= ~(c/or min-count 0) (c/count ~'%) ~(c/or max-count MAX_INT)) :val x :via via :in in}] - - (c/and distinct (not (empty? x)) (not (apply distinct? x))) - [{:path path :pred 'distinct? :val x :via via :in in}]))) - -(defn ^:skip-wiki merge-spec-impl - "Do not call this directly, use 'merge'" - [forms preds gfn] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] (let [ms (map #(dt %1 x %2) preds forms)] - (if (some invalid? ms) - ::invalid - (apply c/merge ms)))) - (unform* [_ x] (apply c/merge (map #(unform % x) (reverse preds)))) - (explain* [_ path via in x] - (apply concat - (map #(explain-1 %1 %2 path via in x) - forms preds))) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (gen/fmap - #(apply c/merge %) - (apply gen/tuple (map #(gensub %1 overrides path rmap %2) - preds forms))))) - (with-gen* [_ gfn] (merge-spec-impl forms preds gfn)) - (describe* [_] `(merge ~@forms)))) - -(defn ^:skip-wiki every-impl - "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" - ([form pred opts] (every-impl form pred opts nil)) - ([form pred {gen-into :into - describe-form ::describe - :keys [kind ::kind-form count max-count min-count distinct gen-max ::kfn ::cpred - conform-keys ::conform-all] - :or {gen-max 20} - :as opts} - gfn] - (let [conform-into gen-into - spec (delay (specize pred)) - check? #(valid? @spec %) - kfn (c/or kfn (fn [i v] i)) - addcv (fn [ret i v cv] (conj ret cv)) - cfns (fn [x] - ;;returns a tuple of [init add complete] fns - (cond - (c/and (vector? x) (c/or (not conform-into) (vector? conform-into))) - [identity - (fn [ret i v cv] - (if (identical? v cv) - ret - (assoc ret i cv))) - identity] - - (c/and (map? x) (c/or (c/and kind (not conform-into)) (map? conform-into))) - [(if conform-keys empty identity) - (fn [ret i v cv] - (if (c/and (identical? v cv) (not conform-keys)) - ret - (assoc ret (nth (if conform-keys cv v) 0) (nth cv 1)))) - identity] - - (c/or (list? conform-into) (seq? conform-into) (c/and (not conform-into) (c/or (list? x) (seq? x)))) - [empty addcv reverse] - - :else [#(empty (c/or conform-into %)) addcv identity]))] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] - (let [spec @spec] - (cond - (not (cpred x)) ::invalid - - conform-all - (let [[init add complete] (cfns x)] - (loop [ret (init x), i 0, [v & vs :as vseq] (seq x)] - (if vseq - (let [cv (conform* spec v)] - (if (invalid? cv) - ::invalid - (recur (add ret i v cv) (inc i) vs))) - (complete ret)))) - - :else - (if (indexed? x) - (let [step (max 1 (long (/ (c/count x) *coll-check-limit*)))] - (loop [i 0] - (if (>= i (c/count x)) - x - (if (valid? spec (nth x i)) - (recur (c/+ i step)) - ::invalid)))) - (let [limit *coll-check-limit*] - (loop [i 0 [v & vs :as vseq] (seq x)] - (cond - (c/or (nil? vseq) (= i limit)) x - (valid? spec v) (recur (inc i) vs) - :else ::invalid))))))) - (unform* [_ x] x) - (explain* [_ path via in x] - (c/or (coll-prob x kind kind-form distinct count min-count max-count - path via in) - (apply concat - ((if conform-all identity (partial take *coll-error-limit*)) - (keep identity - (map (fn [i v] - (let [k (kfn i v)] - (when-not (check? v) - (let [prob (explain-1 form pred path via (conj in k) v)] - prob)))) - (range) x)))))) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (let [pgen (gensub pred overrides path rmap form)] - (gen/bind - (cond - gen-into (gen/return (empty gen-into)) - kind (gen/fmap #(if (empty? %) % (empty %)) - (gensub kind overrides path rmap form)) - :else (gen/return [])) - (fn [init] - (gen/fmap - #(if (vector? init) % (into init %)) - (cond - distinct - (if count - (gen/vector-distinct pgen {:num-elements count :max-tries 100}) - (gen/vector-distinct pgen {:min-elements (c/or min-count 0) - :max-elements (c/or max-count (max gen-max (c/* 2 (c/or min-count 0)))) - :max-tries 100})) - - count - (gen/vector pgen count) - - (c/or min-count max-count) - (gen/vector pgen (c/or min-count 0) (c/or max-count (max gen-max (c/* 2 (c/or min-count 0))))) - - :else - (gen/vector pgen 0 gen-max)))))))) - - (with-gen* [_ gfn] (every-impl form pred opts gfn)) - (describe* [_] (c/or describe-form `(every ~(s/mres form) ~@(mapcat identity opts)))))))) - -;;;;;;;;;;;;;;;;;;;;;;; regex ;;;;;;;;;;;;;;;;;;; -;;See: -;; http://matt.might.net/articles/implementation-of-regular-expression-matching-in-scheme-with-derivatives/ -;; http://www.ccs.neu.edu/home/turon/re-deriv.pdf - -;;ctors -(defn- accept [x] {::op ::accept :ret x}) - -(defn- accept? [{:keys [::op]}] - (= ::accept op)) - -(defn- pcat* [{[p1 & pr :as ps] :ps, [k1 & kr :as ks] :ks, [f1 & fr :as forms] :forms, ret :ret, rep+ :rep+}] - (when (every? identity ps) - (if (accept? p1) - (let [rp (:ret p1) - ret (conj ret (if ks {k1 rp} rp))] - (if pr - (pcat* {:ps pr :ks kr :forms fr :ret ret}) - (accept ret))) - {::op ::pcat, :ps ps, :ret ret, :ks ks, :forms forms :rep+ rep+}))) - -(defn- pcat [& ps] (pcat* {:ps ps :ret []})) - -(defn ^:skip-wiki cat-impl - "Do not call this directly, use 'cat'" - [ks ps forms] - (pcat* {:ks ks, :ps ps, :forms forms, :ret {}})) - -(defn- rep* [p1 p2 ret splice form] - (when p1 - (let [r {::op ::rep, :p2 p2, :splice splice, :forms form :id (random-uuid)}] - (if (accept? p1) - (assoc r :p1 p2 :ret (conj ret (:ret p1))) - (assoc r :p1 p1, :ret ret))))) - -(defn ^:skip-wiki rep-impl - "Do not call this directly, use '*'" - [form p] (rep* p p [] false form)) - -(defn ^:skip-wiki rep+impl - "Do not call this directly, use '+'" - [form p] - (pcat* {:ps [p (rep* p p [] true form)] :forms `[~form (* ~form)] :ret [] :rep+ form})) - -(defn ^:skip-wiki amp-impl - "Do not call this directly, use '&'" - [re preds pred-forms] - {::op ::amp :p1 re :ps preds :forms pred-forms}) - -(defn- filter-alt [ps ks forms f] - (if (c/or ks forms) - (let [pks (->> (map vector ps - (c/or (seq ks) (repeat nil)) - (c/or (seq forms) (repeat nil))) - (filter #(-> % first f)))] - [(seq (map first pks)) (when ks (seq (map second pks))) (when forms (seq (map #(nth % 2) pks)))]) - [(seq (filter f ps)) ks forms])) - -(defn- alt* [ps ks forms] - (let [[[p1 & pr :as ps] [k1 :as ks] forms] (filter-alt ps ks forms identity)] - (when ps - (let [ret {::op ::alt, :ps ps, :ks ks :forms forms}] - (if (nil? pr) - (if k1 - (if (accept? p1) - (accept (tagged-ret [k1 (:ret p1)])) - ret) - p1) - ret))))) - -(defn- alts [& ps] (alt* ps nil nil)) -(defn- alt2 [p1 p2] (if (c/and p1 p2) (alts p1 p2) (c/or p1 p2))) - -(defn ^:skip-wiki alt-impl - "Do not call this directly, use 'alt'" - [ks ps forms] (assoc (alt* ps ks forms) :id (random-uuid))) - -(defn ^:skip-wiki maybe-impl - "Do not call this directly, use '?'" - [p form] (assoc (alt* [p (accept ::nil)] nil [form ::nil]) :maybe form)) - -(defn- noret? [p1 pret] - (c/or (= pret ::nil) - (c/and (#{::rep ::pcat} (::op (reg-resolve! p1))) ;;hrm, shouldn't know these - (empty? pret)) - nil)) - -(declare preturn) - -(defn- accept-nil? [p] - (let [{:keys [::op ps p1 p2 forms] :as p} (reg-resolve! p)] - (case op - ::accept true - nil nil - ::amp (c/and (accept-nil? p1) - (c/or (noret? p1 (preturn p1)) - (let [ret (-> (preturn p1) (and-preds ps (next forms)))] - (not (invalid? ret))))) - ::rep (c/or (identical? p1 p2) (accept-nil? p1)) - ::pcat (every? accept-nil? ps) - ::alt (c/some accept-nil? ps)))) - -(declare add-ret) - -(defn- preturn [p] - (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms] :as p} (reg-resolve! p)] - (case op - ::accept ret - nil nil - ::amp (let [pret (preturn p1)] - (if (noret? p1 pret) - ::nil - (and-preds pret ps forms))) - ::rep (add-ret p1 ret k) - ::pcat (add-ret p0 ret k) - ::alt (let [[[p0] [k0]] (filter-alt ps ks forms accept-nil?) - r (if (nil? p0) ::nil (preturn p0))] - (if k0 (tagged-ret [k0 r]) r))))) - -(defn- op-unform [p x] - ;;(prn {:p p :x x}) - (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms rep+ maybe] :as p} (reg-resolve! p) - kps (zipmap ks ps)] - (case op - ::accept [ret] - nil [(unform p x)] - ::amp (let [px (reduce #(unform %2 %1) x (reverse ps))] - (op-unform p1 px)) - ::rep (mapcat #(op-unform p1 %) x) - ::pcat (if rep+ - (mapcat #(op-unform p0 %) x) - (mapcat (fn [k] - (when (contains? x k) - (op-unform (kps k) (get x k)))) - ks)) - ::alt (if maybe - [(unform p0 x)] - (let [[k v] x] - (op-unform (kps k) v)))))) - -(defn- add-ret [p r k] - (let [{:keys [::op ps splice] :as p} (reg-resolve! p) - prop #(let [ret (preturn p)] - (if (empty? ret) r ((if splice into conj) r (if k {k ret} ret))))] - (case op - nil r - (::alt ::accept ::amp) - (let [ret (preturn p)] - ;;(prn {:ret ret}) - (if (= ret ::nil) r (conj r (if k {k ret} ret)))) - - (::rep ::pcat) (prop)))) - -(defn- deriv - [p x] - (let [{[p0 & pr :as ps] :ps, [k0 & kr :as ks] :ks, :keys [::op p1 p2 ret splice forms] :as p} (reg-resolve! p)] - (when p - (case op - ::accept nil - nil (let [ret (dt p x p)] - (when-not (invalid? ret) (accept ret))) - ::amp (when-let [p1 (deriv p1 x)] - (if (= ::accept (::op p1)) - (let [ret (-> (preturn p1) (and-preds ps (next forms)))] - (when-not (invalid? ret) - (accept ret))) - (amp-impl p1 ps forms))) - ::pcat (alt2 (pcat* {:ps (cons (deriv p0 x) pr), :ks ks, :forms forms, :ret ret}) - (when (accept-nil? p0) (deriv (pcat* {:ps pr, :ks kr, :forms (next forms), :ret (add-ret p0 ret k0)}) x))) - ::alt (alt* (map #(deriv % x) ps) ks forms) - ::rep (alt2 (rep* (deriv p1 x) p2 ret splice forms) - (when (accept-nil? p1) (deriv (rep* p2 p2 (add-ret p1 ret nil) splice forms) x))))))) - -(defn- op-describe [p] - (let [{:keys [::op ps ks forms splice p1 rep+ maybe] :as p} (reg-resolve! p)] - ;;(prn {:op op :ks ks :forms forms :p p}) - (when p - (case op - ::accept nil - nil p - ::amp (list* 'clojure.spec/& (op-describe p1) forms) - ::pcat (if rep+ - (list `+ rep+) - (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) forms))) - ::alt (if maybe - (list `? maybe) - (cons `alt (mapcat vector ks forms))) - ::rep (list (if splice `+ `*) forms))))) - -(defn- op-explain [form p path via in input] - ;;(prn {:form form :p p :path path :input input}) - (let [[x :as input] input - {:keys [::op ps ks forms splice p1 p2] :as p} (reg-resolve! p) - via (if-let [name (spec-name p)] (conj via name) via) - insufficient (fn [path form] - [{:path path - :reason "Insufficient input" - :pred (abbrev form) - :val () - :via via - :in in}])] - (when p - (case op - ::accept nil - nil (if (empty? input) - (insufficient path form) - (explain-1 form p path via in x)) - ::amp (if (empty? input) - (if (accept-nil? p1) - (explain-pred-list forms ps path via in (preturn p1)) - (insufficient path (op-describe p1))) - (if-let [p1 (deriv p1 x)] - (explain-pred-list forms ps path via in (preturn p1)) - (op-explain (op-describe p1) p1 path via in input))) - ::pcat (let [pkfs (map vector - ps - (c/or (seq ks) (repeat nil)) - (c/or (seq forms) (repeat nil))) - [pred k form] (if (= 1 (count pkfs)) - (first pkfs) - (first (remove (fn [[p]] (accept-nil? p)) pkfs))) - path (if k (conj path k) path) - form (c/or form (op-describe pred))] - (if (c/and (empty? input) (not pred)) - (insufficient path form) - (op-explain form pred path via in input))) - ::alt (if (empty? input) - (insufficient path (op-describe p)) - (apply concat - (map (fn [k form pred] - (op-explain (c/or form (op-describe pred)) - pred - (if k (conj path k) path) - via - in - input)) - (c/or (seq ks) (repeat nil)) - (c/or (seq forms) (repeat nil)) - ps))) - ::rep (op-explain (if (identical? p1 p2) - forms - (op-describe p1)) - p1 path via in input))))) - -(defn- re-gen [p overrides path rmap f] - ;;(prn {:op op :ks ks :forms forms}) - (let [{:keys [::op ps ks p1 p2 forms splice ret id ::gfn] :as p} (reg-resolve! p) - rmap (if id (inck rmap id) rmap) - ggens (fn [ps ks forms] - (let [gen (fn [p k f] - ;;(prn {:k k :path path :rmap rmap :op op :id id}) - (when-not (c/and rmap id k (recur-limit? rmap id path k)) - (if id - (gen/delay (re-gen p overrides (if k (conj path k) path) rmap (c/or f p))) - (re-gen p overrides (if k (conj path k) path) rmap (c/or f p)))))] - (map gen ps (c/or (seq ks) (repeat nil)) (c/or (seq forms) (repeat nil)))))] - (c/or (when-let [g (get overrides path)] - (case op - (:accept nil) (gen/fmap vector g) - g)) - (when gfn - (gfn)) - (when p - (case op - ::accept (if (= ret ::nil) - (gen/return []) - (gen/return [ret])) - nil (when-let [g (gensub p overrides path rmap f)] - (gen/fmap vector g)) - ::amp (re-gen p1 overrides path rmap (op-describe p1)) - ::pcat (let [gens (ggens ps ks forms)] - (when (every? identity gens) - (apply gen/cat gens))) - ::alt (let [gens (remove nil? (ggens ps ks forms))] - (when-not (empty? gens) - (gen/one-of gens))) - ::rep (if (recur-limit? rmap id [id] id) - (gen/return []) - (when-let [g (re-gen p2 overrides path rmap forms)] - (gen/fmap #(apply concat %) - (gen/vector g))))))))) - -(defn- re-conform [p [x & xs :as data]] - ;;(prn {:p p :x x :xs xs}) - (if (empty? data) - (if (accept-nil? p) - (let [ret (preturn p)] - (if (= ret ::nil) - nil - ret)) - ::invalid) - (if-let [dp (deriv p x)] - (recur dp xs) - ::invalid))) - -(defn- re-explain [path via in re input] - (loop [p re [x & xs :as data] input i 0] - ;;(prn {:p p :x x :xs xs :re re}) (prn) - (if (empty? data) - (if (accept-nil? p) - nil ;;success - (op-explain (op-describe p) p path via in nil)) - (if-let [dp (deriv p x)] - (recur dp xs (inc i)) - (if (accept? p) - (if (= (::op p) ::pcat) - (op-explain (op-describe p) p path via (conj in i) (seq data)) - [{:path path - :reason "Extra input" - :pred (abbrev (op-describe re)) - :val data - :via via - :in (conj in i)}]) - (c/or (op-explain (op-describe p) p path via (conj in i) (seq data)) - [{:path path - :reason "Extra input" - :pred (abbrev (op-describe p)) - :val data - :via via - :in (conj in i)}])))))) - -(defn ^:skip-wiki regex-spec-impl - "Do not call this directly, use 'spec' with a regex op argument" - [re gfn] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] - (if (c/or (nil? x) (coll? x)) - (re-conform re (seq x)) - ::invalid)) - (unform* [_ x] (op-unform re x)) - (explain* [_ path via in x] - (if (c/or (nil? x) (coll? x)) - (re-explain path via in re (seq x)) - [{:path path :pred (abbrev (op-describe re)) :val x :via via :in in}])) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (re-gen re overrides path rmap (op-describe re)))) - (with-gen* [_ gfn] (regex-spec-impl re gfn)) - (describe* [_] (op-describe re)))) - -;;;;;;;;;;;;;;;;; HOFs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn- call-valid? - [f specs args] - (let [cargs (conform (:args specs) args)] - (when-not (invalid? cargs) - (let [ret (apply f args) - cret (conform (:ret specs) ret)] - (c/and (not (invalid? cret)) - (if (:fn specs) - (pvalid? (:fn specs) {:args cargs :ret cret}) - true)))))) - -(defn- validate-fn - "returns f if valid, else smallest" - [f specs iters] - (let [g (gen (:args specs)) - prop (gen/for-all* [g] #(call-valid? f specs %))] - (let [ret (gen/quick-check iters prop)] - (if-let [[smallest] (-> ret :shrunk :smallest)] - smallest - f)))) - -(defn ^:skip-wiki fspec-impl - "Do not call this directly, use 'fspec'" - [argspec aform retspec rform fnspec fform gfn] - (let [specs {:args argspec :ret retspec :fn fnspec}] - (reify - ILookup - (-lookup [this k] (get specs k)) - (-lookup [_ k not-found] (get specs k not-found)) - - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ f] (if (ifn? f) - (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) - ::invalid)) - (unform* [_ f] f) - (explain* [_ path via in f] - (if (ifn? f) - (let [args (validate-fn f specs 100)] - (if (identical? f args) ;;hrm, we might not be able to reproduce - nil - (let [ret (try (apply f args) (catch js/Error t t))] - (if (instance? js/Error ret) - ;;TODO add exception data - [{:path path :pred '(apply fn) :val args :reason (.-message ret) :via via :in in}] - - (let [cret (dt retspec ret rform)] - (if (invalid? cret) - (explain-1 rform retspec (conj path :ret) via in ret) - (when fnspec - (let [cargs (conform argspec args)] - (explain-1 fform fnspec (conj path :fn) via in {:args cargs :ret cret}))))))))) - [{:path path :pred 'ifn? :val f :via via :in in}])) - (gen* [_ overrides _ _] (if gfn - (gfn) - (gen/return - (fn [& args] - (assert (pvalid? argspec args) (with-out-str (explain argspec args))) - (gen/generate (gen retspec overrides)))))) - (with-gen* [_ gfn] (fspec-impl argspec aform retspec rform fnspec fform gfn)) - (describe* [_] `(fspec :args ~aform :ret ~rform :fn ~fform))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(cljs.spec/def ::kvs->map (cljs.spec/conformer #(zipmap (map ::k %) (map ::v %)) #(map (fn [[k v]] {::k k ::v v}) %))) - -(defn nonconforming - "takes a spec and returns a spec that has the same properties except - 'conform' returns the original (not the conformed) value. Note, will specize regex ops." - [spec] - (let [spec (specize spec)] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] (let [ret (conform* spec x)] - (if (invalid? ret) - ::invalid - x))) - (unform* [_ x] (unform* spec x)) - (explain* [_ path via in x] (explain* spec path via in x)) - (gen* [_ overrides path rmap] (gen* spec overrides path rmap)) - (with-gen* [_ gfn] (nonconforming (with-gen* spec gfn))) - (describe* [_] `(nonconforming ~(describe* spec)))))) - -(defn ^:skip-wiki nilable-impl - "Do not call this directly, use 'nilable'" - [form pred gfn] - (let [spec (specize pred form)] - (reify - Specize - (specize* [s] s) - (specize* [s _] s) - - Spec - (conform* [_ x] (if (nil? x) nil (conform* spec x))) - (unform* [_ x] (if (nil? x) nil (unform* spec x))) - (explain* [_ path via in x] - (when-not (c/or (pvalid? spec x) (nil? x)) - (conj - (explain-1 form pred (conj path ::pred) via in x) - {:path (conj path ::nil) :pred 'nil? :val x :via via :in in}))) - (gen* [_ overrides path rmap] - (if gfn - (gfn) - (gen/frequency - [[1 (gen/delay (gen/return nil))] - [9 (gen/delay (gensub pred overrides (conj path ::pred) rmap form))]]))) - (with-gen* [_ gfn] (nilable-impl form pred gfn)) - (describe* [_] `(nilable ~(s/mres form)))))) - -(defn exercise - "generates a number (default 10) of values compatible with spec and maps conform over them, - returning a sequence of [val conformed-val] tuples. Optionally takes - a generator overrides map as per gen" - ([spec] (exercise spec 10)) - ([spec n] (exercise spec n nil)) - ([spec n overrides] - (map #(vector % (conform spec %)) (gen/sample (gen spec overrides) n)))) - -(defn inst-in-range? - "Return true if inst at or after start and before end" - [start end inst] - (c/and (inst? inst) - (let [t (inst-ms inst)] - (c/and (<= (inst-ms start) t) (< t (inst-ms end)))))) - -(defn int-in-range? - "Return true if start <= val and val < end" - [start end val] - (cond - (integer? val) (c/and (<= start val) (< val end)) - - (instance? goog.math.Long val) - (c/and (.lessThanOrEqual start val) - (.lessThan val end)) - - (instance? goog.math.Integer val) - (c/and (.lessThanOrEqual start val) - (.lessThan val end)) - - :else false)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; assert ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defonce - ^{:dynamic true - :doc "If true, compiler will enable spec asserts, which are then -subject to runtime control via check-asserts? If false, compiler -will eliminate all spec assert overhead. See 'assert'. -Initially set to the negation of the ':elide-asserts' compiler option. -Defaults to true."} - *compile-asserts* - (s/init-compile-asserts)) - -(defonce ^{:private true - :dynamic true} - *runtime-asserts* - false) - -(defn ^boolean check-asserts? - "Returns the value set by check-asserts." - [] - *runtime-asserts*) - -(defn check-asserts - "Enable or disable spec asserts that have been compiled -with '*compile-asserts*' true. See 'assert'. -Initially set to boolean value of cljs.spec/*runtime-asserts*. -Defaults to false." - [^boolean flag] - (set! *runtime-asserts* flag)) - -(defn assert* - "Do not call this directly, use 'assert'." - [spec x] - (if (valid? spec x) - x - (let [ed (c/merge (assoc (explain-data* spec [] [] [] x) - ::failure :assertion-failed))] - (throw (js/Error. - (str "Spec assertion failed\n" (with-out-str (explain-out ed)))))))) diff --git a/src/main/cljs/cljs/spec/impl/gen.cljc b/src/main/cljs/cljs/spec/impl/gen.cljc deleted file mode 100644 index 98bab2cb9..000000000 --- a/src/main/cljs/cljs/spec/impl/gen.cljc +++ /dev/null @@ -1,75 +0,0 @@ -; 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.spec.impl.gen - (:refer-clojure :exclude [delay]) - (:require [cljs.core :as c] - [clojure.string :as string])) - -(defmacro dynaload [[quote s]] - (let [xs (string/split (namespace s) #"\.") - cnt (count xs) - checks (map - (fn [n xs] - `(c/exists? ~(symbol (string/join "." (take n xs))))) - (range 2 cnt) - (repeat xs))] - `(cljs.spec.impl.gen/LazyVar. - (fn [] - (if (and ~@checks (c/exists? ~s)) - ~(vary-meta s assoc :cljs.analyzer/no-resolve true) - (throw - (js/Error. - (str "Var " '~s " does not exist, " - (namespace '~s) " never required"))))) - nil))) - -(defmacro delay - "given body that returns a generator, returns a - generator that delegates to that, but delays - creation until used." - [& body] - `(delay-impl (c/delay ~@body))) - -(defmacro ^:skip-wiki lazy-combinator - "Implementation macro, do not call directly." - [s] - (let [fqn (symbol "clojure.test.check.generators" (name s)) - doc (str "Lazy loaded version of " fqn)] - `(let [g# (dynaload '~fqn)] - (defn ~s - ~doc - [& ~'args] - (apply @g# ~'args))))) - -(defmacro ^:skip-wiki lazy-combinators - "Implementation macro, do not call directly." - [& syms] - `(do - ~@(map - (fn [s] (list `lazy-combinator s)) - syms))) - -(defmacro ^:skip-wiki lazy-prim - "Implementation macro, do not call directly." - [s] - (let [fqn (symbol "clojure.test.check.generators" (name s)) - doc (str "Fn returning " fqn)] - `(let [g# (dynaload '~fqn)] - (defn ~s - ~doc - [& ~'args] - @g#)))) - -(defmacro ^:skip-wiki lazy-prims - "Implementation macro, do not call directly." - [& syms] - `(do - ~@(map - (fn [s] (list `lazy-prim s)) - syms))) \ No newline at end of file diff --git a/src/main/cljs/cljs/spec/impl/gen.cljs b/src/main/cljs/cljs/spec/impl/gen.cljs deleted file mode 100644 index 9d68dc8e1..000000000 --- a/src/main/cljs/cljs/spec/impl/gen.cljs +++ /dev/null @@ -1,180 +0,0 @@ -; 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.spec.impl.gen - (:refer-clojure :exclude [boolean cat hash-map list map not-empty set vector - char double int keyword symbol string uuid delay]) - (:require-macros [cljs.core :as c] - [cljs.spec.impl.gen :as gen :refer [dynaload lazy-combinators lazy-prims]]) - (:require [cljs.core :as c])) - -(deftype LazyVar [f ^:mutable cached] - IDeref - (-deref [this] - (if-not (nil? cached) - cached - (let [x (f)] - (when-not (nil? x) - (set! cached x)) - x)))) - -(def ^:private quick-check-ref - (dynaload 'clojure.test.check/quick-check)) - -(defn quick-check - [& args] - (apply @quick-check-ref args)) - -(def ^:private for-all*-ref - (dynaload 'clojure.test.check.properties/for-all*)) - -(defn for-all* - "Dynamically loaded clojure.test.check.properties/for-all*." - [& args] - (apply @for-all*-ref args)) - -(let [g? (dynaload 'clojure.test.check.generators/generator?) - g (dynaload 'clojure.test.check.generators/generate) - mkg (dynaload 'clojure.test.check.generators/->Generator)] - (defn- generator? - [x] - (@g? x)) - (defn- generator - [gfn] - (@mkg gfn)) - (defn generate - "Generate a single value using generator." - [generator] - (@g generator))) - -(defn ^:skip-wiki delay-impl - [gfnd] - ;;N.B. depends on test.check impl details - (generator (fn [rnd size] - ((:gen @gfnd) rnd size)))) - -;(defn gen-for-name -; "Dynamically loads test.check generator named s." -; [s] -; (let [g (dynaload s)] -; (if (generator? g) -; g -; (throw (js/Error. (str "Var " s " is not a generator")))))) - -(lazy-combinators hash-map list map not-empty set vector vector-distinct fmap elements - bind choose one-of such-that tuple sample return - large-integer* double* frequency) - -(lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double - int keyword keyword-ns large-integer ratio simple-type simple-type-printable - string string-ascii string-alphanumeric symbol symbol-ns uuid) - -(defn cat - "Returns a generator of a sequence catenated from results of -gens, each of which should generate something sequential." - [& gens] - (fmap #(apply concat %) - (apply tuple gens))) - -(defn- ^boolean qualified? [ident] (not (nil? (namespace ident)))) - -(def ^:private -gen-builtins - (c/delay - (let [simple (simple-type-printable)] - {any? (one-of [(return nil) (any-printable)]) - number? (one-of [(large-integer) (double)]) - integer? (large-integer) - int? (large-integer) - pos-int? (large-integer* {:min 1}) - neg-int? (large-integer* {:max -1}) - nat-int? (large-integer* {:min 0}) - float? (double) - double? (double) - string? (string-alphanumeric) - ident? (one-of [(keyword-ns) (symbol-ns)]) - simple-ident? (one-of [(keyword) (symbol)]) - qualified-ident? (such-that qualified? (one-of [(keyword-ns) (symbol-ns)])) - keyword? (keyword-ns) - simple-keyword? (keyword) - qualified-keyword? (such-that qualified? (keyword-ns)) - symbol? (symbol-ns) - simple-symbol? (symbol) - qualified-symbol? (such-that qualified? (symbol-ns)) - uuid? (uuid) - inst? (fmap #(js/Date. %) - (large-integer)) - seqable? (one-of [(return nil) - (list simple) - (vector simple) - (map simple simple) - (set simple) - (string-alphanumeric)]) - indexed? (vector simple) - map? (map simple simple) - vector? (vector simple) - list? (list simple) - seq? (list simple) - char? (char) - set? (set simple) - nil? (return nil) - false? (return false) - true? (return true) - boolean? (boolean) - zero? (return 0) - ;rational? (one-of [(large-integer) (ratio)]) - coll? (one-of [(map simple simple) - (list simple) - (vector simple) - (set simple)]) - empty? (elements [nil '() [] {} #{}]) - associative? (one-of [(map simple simple) (vector simple)]) - sequential? (one-of [(list simple) (vector simple)]) - ;ratio? (such-that ratio? (ratio)) - }))) - -(defn gen-for-pred - "Given a predicate, returns a built-in generator if one exists." - [pred] - (if (set? pred) - (elements pred) - (get @gen-builtins pred))) - -(comment - (require 'clojure.test.check) - (require 'clojure.test.check.properties) - (require 'cljs.spec.impl.gen) - (in-ns 'cljs.spec.impl.gen) - - ;; combinators, see call to lazy-combinators above for complete list - (generate (one-of [(gen-for-pred integer?) (gen-for-pred string?)])) - (generate (such-that #(< 10000 %) (gen-for-pred integer?))) - (let [reqs {:a (gen-for-pred number?) - :b (gen-for-pred keyword?)} - opts {:c (gen-for-pred string?)}] - (generate (bind (choose 0 (count opts)) - #(let [args (concat (seq reqs) (shuffle (seq opts)))] - (->> args - (take (+ % (count reqs))) - (mapcat identity) - (apply hash-map)))))) - (generate (cat (list (gen-for-pred string?)) - (list (gen-for-pred integer?)))) - - ;; load your own generator - ;(gen-for-name 'clojure.test.check.generators/int) - - ;; failure modes - ;(gen-for-name 'unqualified) - ;(gen-for-name 'clojure.core/+) - ;(gen-for-name 'clojure.core/name-does-not-exist) - ;(gen-for-name 'ns.does.not.exist/f) - - ) - - diff --git a/src/main/cljs/cljs/spec/test.cljc b/src/main/cljs/cljs/spec/test.cljc deleted file mode 100644 index 2477a4525..000000000 --- a/src/main/cljs/cljs/spec/test.cljc +++ /dev/null @@ -1,251 +0,0 @@ -; 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.spec.test - (:require - [cljs.analyzer :as ana] - [cljs.analyzer.api :as ana-api] - [clojure.string :as string] - [cljs.spec :as s] - [cljs.spec.impl.gen :as gen])) - -(defonce ^:private instrumented-vars (atom #{})) - -(defn- collectionize - [x] - (if (symbol? x) - (list x) - x)) - -(defn- fn-spec-name? - [s] - (symbol? s)) - -(defmacro with-instrument-disabled - "Disables instrument's checking of calls, within a scope." - [& body] - `(binding [*instrument-enabled* nil] - ~@body)) - -(defmacro instrument-1 - [[quote s] opts] - (when-let [v (ana-api/resolve &env s)] - (swap! instrumented-vars conj (:name v)) - `(let [checked# (instrument-1* ~s (var ~s) ~opts)] - (when checked# (set! ~s checked#)) - '~(:name v)))) - -(defmacro unstrument-1 - [[quote s]] - (when-let [v (ana-api/resolve &env s)] - (when (@instrumented-vars (:name v)) - (swap! instrumented-vars disj (:name v)) - `(let [raw# (unstrument-1* ~s (var ~s))] - (when raw# (set! ~s raw#)) - '~(:name v))))) - -(defn- sym-or-syms->syms [sym-or-syms] - (into [] - (mapcat - (fn [sym] - (if (and (string/includes? (str sym) ".") - (ana-api/find-ns sym)) - (->> (vals (ana-api/ns-interns sym)) - (filter #(not (:macro %))) - (map :name) - (map - (fn [name-sym] - (symbol (name sym) (name name-sym))))) - [sym]))) - (collectionize sym-or-syms))) - -(defmacro instrument - "Instruments the vars named by sym-or-syms, a symbol or collection -of symbols, or all instrumentable vars if sym-or-syms is not -specified. If a symbol identifies a namespace then all symbols in that -namespace will be enumerated. - -If a var has an :args fn-spec, sets the var's root binding to a -fn that checks arg conformance (throwing an exception on failure) -before delegating to the original fn. - -The opts map can be used to override registered specs, and/or to -replace fn implementations entirely. Opts for symbols not included -in sym-or-syms are ignored. This facilitates sharing a common -options map across many different calls to instrument. - -The opts map may have the following keys: - - :spec a map from var-name symbols to override specs - :stub a set of var-name symbols to be replaced by stubs - :gen a map from spec names to generator overrides - :replace a map from var-name symbols to replacement fns - -:spec overrides registered fn-specs with specs your provide. Use -:spec overrides to provide specs for libraries that do not have -them, or to constrain your own use of a fn to a subset of its -spec'ed contract. - -:stub replaces a fn with a stub that checks :args, then uses the -:ret spec to generate a return value. - -:gen overrides are used only for :stub generation. - -:replace replaces a fn with a fn that checks args conformance, then -invokes the fn you provide, enabling arbitrary stubbing and mocking. - -:spec can be used in combination with :stub or :replace. - -Returns a collection of syms naming the vars instrumented." - ([] - `(instrument '[~@(#?(:clj s/speced-vars - :cljs cljs.spec$macros/speced-vars))])) - ([xs] - `(instrument ~xs nil)) - ([sym-or-syms opts] - (let [syms (sym-or-syms->syms (eval sym-or-syms)) - opts-sym (gensym "opts")] - `(let [~opts-sym ~opts] - (reduce - (fn [ret# [_# f#]] - (let [sym# (f#)] - (cond-> ret# sym# (conj sym#)))) - [] - (->> (zipmap '~syms - [~@(map - (fn [sym] - `(fn [] (instrument-1 '~sym ~opts-sym))) - syms)]) - (filter #((instrumentable-syms ~opts-sym) (first %))) - (distinct-by first))))))) - -(defmacro unstrument - "Undoes instrument on the vars named by sym-or-syms, specified -as in instrument. With no args, unstruments all instrumented vars. -Returns a collection of syms naming the vars unstrumented." - ([] - `(unstrument '[~@(deref instrumented-vars)])) - ([sym-or-syms] - (let [syms (sym-or-syms->syms (eval sym-or-syms))] - `(reduce - (fn [ret# f#] - (let [sym# (f#)] - (cond-> ret# sym# (conj sym#)))) - [] - [~@(->> syms - (map - (fn [sym] - (when (symbol? sym) - `(fn [] - (unstrument-1 '~sym))))) - (remove nil?))])))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defmacro check-1 - [[quote s :as qs] f spec opts] - (let [{:keys [name] :as v} (when qs (ana-api/resolve &env s))] - `(let [s# '~name - opts# ~opts - v# ~(when v `(var ~name)) - spec# (or ~spec ~(when v `(s/get-spec (var ~name)))) - re-inst?# (and v# (seq (unstrument '~name)) true) - f# (or ~f (when v# @v#))] - (try - (cond - (nil? f#) - {:failure (ex-info "No fn to spec" {::s/failure :no-fn}) - :sym s# :spec spec#} - - (:args spec#) - (let [tcret# (quick-check f# spec# opts#)] - (make-check-result s# spec# tcret#)) - - :default - {:failure (ex-info "No :args spec" {::s/failure :no-args-spec}) - :sym s# :spec spec#}) - (finally - (when re-inst?# (instrument '~name))))))) - -(defmacro check-fn - "Runs generative tests for fn f using spec and opts. See -'check' for options and return." - ([f spec] - `(check-fn ~f ~spec nil)) - ([f spec opts] - `(let [opts# ~opts] - (validate-check-opts opts#) - (check-1 nil ~f ~spec opts#)))) - -(defn checkable-syms* - ([] - (checkable-syms* nil)) - ([opts] - (reduce into #{} - [(filter fn-spec-name? (keys @s/registry-ref)) - (keys (:spec opts))]))) - -(defmacro checkable-syms - "Given an opts map as per check, returns the set of syms that -can be checked." - ([] - `(checkable-syms nil)) - ([opts] - `(let [opts# ~opts] - (validate-check-opts opts#) - (reduce conj #{} - '[~@(filter fn-spec-name? (keys @s/registry-ref)) - ~@(keys (:spec opts))])))) - -(defmacro check - "Run generative tests for spec conformance on vars named by -sym-or-syms, a symbol or collection of symbols. If sym-or-syms -is not specified, check all checkable vars. If a symbol identifies a -namespace then all symbols in that namespace will be enumerated. - -The opts map includes the following optional keys, where stc -aliases clojure.test.check: - -::stc/opts opts to flow through test.check/quick-check -:gen map from spec names to generator overrides - -The ::stc/opts include :num-tests in addition to the keys -documented by test.check. Generator overrides are passed to -spec/gen when generating function args. - -Returns a lazy sequence of check result maps with the following -keys - -:spec the spec tested -:sym optional symbol naming the var tested -:failure optional test failure -::stc/ret optional value returned by test.check/quick-check - -The value for :failure can be any exception. Exceptions thrown by -spec itself will have an ::s/failure value in ex-data: - -:check-failed at least one checked return did not conform -:no-args-spec no :args spec provided -:no-fn no fn provided -:no-fspec no fspec provided -:no-gen unable to generate :args -:instrument invalid args detected by instrument -" - ([] - `(check '~(checkable-syms*))) - ([sym-or-syms] - `(check ~sym-or-syms nil)) - ([sym-or-syms opts] - (let [syms (sym-or-syms->syms (eval sym-or-syms)) - opts-sym (gensym "opts")] - `(let [~opts-sym ~opts] - [~@(->> syms - (filter (checkable-syms* opts)) - (map - (fn [sym] - (do `(check-1 '~sym nil nil ~opts-sym)))))])))) diff --git a/src/main/cljs/cljs/spec/test.cljs b/src/main/cljs/cljs/spec/test.cljs deleted file mode 100644 index 5ca7a755b..000000000 --- a/src/main/cljs/cljs/spec/test.cljs +++ /dev/null @@ -1,361 +0,0 @@ -; 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.spec.test - (:require-macros [cljs.spec.test :as m :refer [with-instrument-disabled]]) - (:require - [goog.object :as gobj] - [goog.userAgent.product :as product] - [clojure.string :as string] - [cljs.stacktrace :as st] - [cljs.pprint :as pp] - [cljs.spec :as s] - [cljs.spec.impl.gen :as gen] - [clojure.test.check :as stc] - [clojure.test.check.properties])) - -(defn distinct-by - ([f coll] - (let [step (fn step [xs seen] - (lazy-seq - ((fn [[x :as xs] seen] - (when-let [s (seq xs)] - (let [v (f x)] - (if (contains? seen v) - (recur (rest s) seen) - (cons x (step (rest s) (conj seen v))))))) - xs seen)))] - (step coll #{})))) - -(defn ->sym - [x] - (@#'s/->sym x)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(def ^:private ^:dynamic *instrument-enabled* - "if false, instrumented fns call straight through" - true) - -(defn get-host-port [] - (if (not= "browser" *target*) - {} - {:host (.. js/window -location -host) - :port (.. js/window -location -port)})) - -(defn get-ua-product [] - (if (not= "browser" *target*) - (keyword *target*) - (cond - product/SAFARI :safari - product/CHROME :chrome - product/FIREFOX :firefox - product/IE :ie))) - -(defn get-env [] - {:ua-product (get-ua-product)}) - -(defn- fn-spec? - "Fn-spec must include at least :args or :ret specs." - [m] - (or (:args m) (:ret m))) - -;; wrap spec/explain-data until specs always return nil for ok data -(defn- explain-data* - [spec v] - (when-not (s/valid? spec v nil) - (s/explain-data spec v))) - -(defn- find-caller [st] - (letfn [(search-spec-fn [frame] - (when frame - (let [s (:function frame)] - (and (string? s) (not (string/blank? s)) - (re-find #"cljs\.spec\.test\.spec_checking_fn" s)))))] - (->> st - (drop-while #(not (search-spec-fn %))) - (drop-while search-spec-fn) - first))) - -;; TODO: check ::caller result in other browsers - David - -(defn- spec-checking-fn - [v f fn-spec] - (let [fn-spec (@#'s/maybe-spec fn-spec) - conform! (fn [v role spec data args] - (let [conformed (s/conform spec data)] - (if (= ::s/invalid conformed) - (let [caller (find-caller - (st/parse-stacktrace - (get-host-port) - (.-stack (js/Error.)) - (get-env) nil)) - ed (merge (assoc (s/explain-data* spec [role] [] [] data) - ::s/args args - ::s/failure :instrument) - (when caller - {::caller caller}))] - (throw (ex-info - (str "Call to " v " did not conform to spec:\n" (with-out-str (s/explain-out ed))) - ed))) - conformed)))] - (doto - (fn - [& args] - (if *instrument-enabled* - (with-instrument-disabled - (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) - (binding [*instrument-enabled* true] - (apply f args))) - (apply f args))) - (gobj/extend f)))) - -(defn- no-fspec - [v spec] - (ex-info (str "Fn at " v " is not spec'ed.") - {:var v :spec spec ::s/failure :no-fspec})) - -(defonce ^:private instrumented-vars (atom {})) - -(defn- instrument-choose-fn - "Helper for instrument." - [f spec sym {over :gen :keys [stub replace]}] - (if (some #{sym} stub) - (-> spec (s/gen over) gen/generate) - (get replace sym f))) - -(defn- instrument-choose-spec - "Helper for instrument" - [spec sym {overrides :spec}] - (get overrides sym spec)) - -(defn- instrument-1* - [s v opts] - (let [spec (s/get-spec v) - {:keys [raw wrapped]} (get @instrumented-vars v) - current @v - to-wrap (if (= wrapped current) raw current) - ospec (or (instrument-choose-spec spec s opts) - (throw (no-fspec v spec))) - ofn (instrument-choose-fn to-wrap ospec s opts) - checked (spec-checking-fn v ofn ospec)] - (swap! instrumented-vars assoc v {:raw to-wrap :wrapped checked}) - checked)) - -(defn- unstrument-1* - [s v] - (when v - (when-let [{:keys [raw wrapped]} (get @instrumented-vars v)] - (swap! instrumented-vars dissoc v) - (let [current @v] - (when (= wrapped current) - raw))))) - -(defn- fn-spec-name? - [s] - (symbol? s)) - -(defn- collectionize - [x] - (if (symbol? x) - (list x) - x)) - -(defn instrumentable-syms - "Given an opts map as per instrument, returns the set of syms -that can be instrumented." - ([] (instrumentable-syms nil)) - ([opts] - (assert (every? ident? (keys (:gen opts))) "instrument :gen expects ident keys") - (reduce into #{} [(filter fn-spec-name? (keys (s/registry))) - (keys (:spec opts)) - (:stub opts) - (keys (:replace opts))]))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; testing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn- explain-check - [args spec v role] - (ex-info - "Specification-based check failed" - (when-not (s/valid? spec v nil) - (assoc (s/explain-data* spec [role] [] [] v) - ::args args - ::val v - ::s/failure :check-failed)))) - -(defn- check-call - "Returns true if call passes specs, otherwise *returns* an exception -with explain-data + ::s/failure." - [f specs args] - (let [cargs (when (:args specs) (s/conform (:args specs) args))] - (if (= cargs ::s/invalid) - (explain-check args (:args specs) args :args) - (let [ret (apply f args) - cret (when (:ret specs) (s/conform (:ret specs) ret))] - (if (= cret ::s/invalid) - (explain-check args (:ret specs) ret :ret) - (if (and (:args specs) (:ret specs) (:fn specs)) - (if (s/valid? (:fn specs) {:args cargs :ret cret}) - true - (explain-check args (:fn specs) {:args cargs :ret cret} :fn)) - true)))))) - -(defn- quick-check - [f specs {gen :gen opts ::stc/opts}] - (let [{:keys [num-tests] :or {num-tests 1000}} opts - g (try (s/gen (:args specs) gen) (catch js/Error t t))] - (if (instance? js/Error g) - {:result g} - (let [prop (gen/for-all* [g] #(check-call f specs %))] - (apply gen/quick-check num-tests prop (mapcat identity opts)))))) - -(defn- make-check-result - "Builds spec result map." - [check-sym spec test-check-ret] - (merge {:spec spec - ::stc/ret test-check-ret} - (when check-sym - {:sym check-sym}) - (when-let [result (-> test-check-ret :result)] - (when-not (true? result) {:failure result})) - (when-let [shrunk (-> test-check-ret :shrunk)] - {:failure (:result shrunk)}))) - -(defn- validate-check-opts - [opts] - (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; check reporting ;;;;;;;;;;;;;;;;;;;;;;;; - -(defn- failure-type - [x] - (::s/failure (ex-data x))) - -(defn- unwrap-failure - [x] - (if (failure-type x) - (ex-data x) - x)) - -(defn- result-type - "Returns the type of the check result. This can be any of the -::s/failure keywords documented in 'check', or: - - :check-passed all checked fn returns conformed - :check-threw checked fn threw an exception" - [ret] - (let [failure (:failure ret)] - (cond - (nil? failure) :check-passed - (failure-type failure) (failure-type failure) - :default :check-threw))) - -(defn abbrev-result - "Given a check result, returns an abbreviated version -suitable for summary use." - [x] - (if (:failure x) - (-> (dissoc x ::stc/ret) - (update :spec s/describe) - (update :failure unwrap-failure)) - (dissoc x :spec ::stc/ret))) - -(defn summarize-results - "Given a collection of check-results, e.g. from 'check', pretty -prints the summary-result (default abbrev-result) of each. - -Returns a map with :total, the total number of results, plus a -key with a count for each different :type of result." - ([check-results] (summarize-results check-results abbrev-result)) - ([check-results summary-result] - (reduce - (fn [summary result] - (pp/pprint (summary-result result)) - (-> summary - (update :total inc) - (update (result-type result) (fnil inc 0)))) - {:total 0} - check-results))) - -(comment - (require - '[cljs.pprint :as pp] - '[cljs.spec :as s] - '[cljs.spec.impl.gen :as gen] - '[cljs.test :as ctest]) - - (require :reload '[cljs.spec.test :as test]) - - ;; discover speced vars for your own test runner - (s/speced-vars) - - ;; check a single var - (test/check-var #'-) - (test/check-var #'+) - (test/check-var #'clojure.spec.broken-specs/throwing-fn) - - ;; old style example tests - (ctest/run-all-tests) - - (s/speced-vars 'clojure.spec.correct-specs) - ;; new style spec tests return same kind of map - (test/check-var #'subs) - (cljs.spec.test/run-tests 'clojure.core) - (test/run-all-tests) - - ;; example evaluation - (defn ranged-rand - "Returns random int in range start <= rand < end" - [start end] - (+ start (long (rand (- end start))))) - - (s/fdef ranged-rand - :args (s/and (s/cat :start int? :end int?) - #(< (:start %) (:end %))) - :ret int? - :fn (s/and #(>= (:ret %) (-> % :args :start)) - #(< (:ret %) (-> % :args :end)))) - - (instrumentable-syms) - - (m/instrument-1 `ranged-rand {}) - (m/unstrument-1 `ranged-rand) - - (m/instrument) - (m/instrument `ranged-rand) - (m/instrument `[ranged-rand]) - - (m/unstrument) - (m/unstrument `ranged-rand) - (m/unstrument `[ranged-rand]) - - (ranged-rand 8 5) - (defn foo - ([a]) - ([a b] - (ranged-rand 8 5))) - (foo 1 2) - (m/unstrument-1 `ranged-rand) - - (m/check-1 `ranged-rand nil nil {}) - - (m/check-fn inc - (s/fspec - :args (s/cat :x int?) - :ret int?)) - - (m/checkable-syms) - - (m/check `ranged-rand) - ) - - - - - From 1c76684ae6e40473ab1c41d328e4094fa0a542f8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 May 2017 15:13:47 -0400 Subject: [PATCH 2305/4033] cljs.spec.impl.gen.alpha -> cljs.spec.gen.alpha update pom.template.xml --- pom.template.xml | 6 +++--- src/main/cljs/cljs/spec/alpha.cljc | 2 +- src/main/cljs/cljs/spec/alpha.cljs | 2 +- src/main/cljs/cljs/spec/{impl => }/gen/alpha.cljc | 4 ++-- src/main/cljs/cljs/spec/{impl => }/gen/alpha.cljs | 8 ++++---- src/main/cljs/cljs/spec/test/alpha.cljc | 2 +- src/main/cljs/cljs/spec/test/alpha.cljs | 4 ++-- src/test/cljs/cljs/spec_test.cljs | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) rename src/main/cljs/cljs/spec/{impl => }/gen/alpha.cljc (97%) rename src/main/cljs/cljs/spec/{impl => }/gen/alpha.cljs (96%) diff --git a/pom.template.xml b/pom.template.xml index d537d2100..9f73eb9f1 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -300,9 +300,9 @@ cljs.analyzer.api cljs.build.api cljs.compiler.api - cljs.spec - cljs.spec.test - cljs.spec.impl.gen + cljs.spec.alpha + cljs.spec.test.alpha + cljs.spec.gen.alpha cljs.repl cljs.repl.browser cljs.repl.nashorn diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index d401bb030..35278a6d0 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -13,7 +13,7 @@ [cljs.env :as env] [cljs.analyzer.api :refer [resolve]] [clojure.walk :as walk] - [cljs.spec.impl.gen.alpha :as gen] + [cljs.spec.gen.alpha :as gen] [clojure.string :as str])) (defonce registry-ref (atom {})) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index de2566ecf..58b0e9687 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -13,7 +13,7 @@ (:require [goog.object :as gobj] [cljs.core :as c] [clojure.walk :as walk] - [cljs.spec.impl.gen.alpha :as gen] + [cljs.spec.gen.alpha :as gen] [clojure.string :as str])) (def ^:const MAX_INT 9007199254740991) diff --git a/src/main/cljs/cljs/spec/impl/gen/alpha.cljc b/src/main/cljs/cljs/spec/gen/alpha.cljc similarity index 97% rename from src/main/cljs/cljs/spec/impl/gen/alpha.cljc rename to src/main/cljs/cljs/spec/gen/alpha.cljc index f7db052a0..6df81798d 100644 --- a/src/main/cljs/cljs/spec/impl/gen/alpha.cljc +++ b/src/main/cljs/cljs/spec/gen/alpha.cljc @@ -6,7 +6,7 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software. -(ns cljs.spec.impl.gen.alpha +(ns cljs.spec.gen.alpha (:refer-clojure :exclude [delay]) (:require [cljs.core :as c] [clojure.string :as string])) @@ -19,7 +19,7 @@ `(c/exists? ~(symbol (string/join "." (take n xs))))) (range 2 cnt) (repeat xs))] - `(cljs.spec.impl.gen.alpha/LazyVar. + `(cljs.spec.gen.alpha/LazyVar. (fn [] (if (and ~@checks (c/exists? ~s)) ~(vary-meta s assoc :cljs.analyzer/no-resolve true) diff --git a/src/main/cljs/cljs/spec/impl/gen/alpha.cljs b/src/main/cljs/cljs/spec/gen/alpha.cljs similarity index 96% rename from src/main/cljs/cljs/spec/impl/gen/alpha.cljs rename to src/main/cljs/cljs/spec/gen/alpha.cljs index fabba040b..27cedfce7 100644 --- a/src/main/cljs/cljs/spec/impl/gen/alpha.cljs +++ b/src/main/cljs/cljs/spec/gen/alpha.cljs @@ -6,11 +6,11 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software. -(ns cljs.spec.impl.gen.alpha +(ns cljs.spec.gen.alpha (:refer-clojure :exclude [boolean cat hash-map list map not-empty set vector char double int keyword symbol string uuid delay]) (:require-macros [cljs.core :as c] - [cljs.spec.impl.gen.alpha :as gen :refer [dynaload lazy-combinators lazy-prims]]) + [cljs.spec.gen.alpha :as gen :refer [dynaload lazy-combinators lazy-prims]]) (:require [cljs.core :as c])) (deftype LazyVar [f ^:mutable cached] @@ -148,8 +148,8 @@ gen-builtins (comment (require 'clojure.test.check) (require 'clojure.test.check.properties) - (require 'cljs.spec.impl.gen) - (in-ns 'cljs.spec.impl.gen) + (require 'cljs.spec.gen) + (in-ns 'cljs.spec.gen) ;; combinators, see call to lazy-combinators above for complete list (generate (one-of [(gen-for-pred integer?) (gen-for-pred string?)])) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index ab11b2fb0..ab4808dde 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -12,7 +12,7 @@ [cljs.analyzer.api :as ana-api] [clojure.string :as string] [cljs.spec.alpha :as s] - [cljs.spec.impl.gen.alpha :as gen])) + [cljs.spec.gen.alpha :as gen])) (defonce ^:private instrumented-vars (atom #{})) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 4195a84ca..f5b104e5f 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -15,7 +15,7 @@ [cljs.stacktrace :as st] [cljs.pprint :as pp] [cljs.spec.alpha :as s] - [cljs.spec.impl.gen.alpha :as gen] + [cljs.spec.gen.alpha :as gen] [clojure.test.check :as stc] [clojure.test.check.properties])) @@ -287,7 +287,7 @@ key with a count for each different :type of result." (require '[cljs.pprint :as pp] '[cljs.spec :as s] - '[cljs.spec.impl.gen :as gen] + '[cljs.spec.gen :as gen] '[cljs.test :as ctest]) (require :reload '[cljs.spec.test :as test]) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index b123f6616..b90075236 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -9,7 +9,7 @@ (ns cljs.spec-test (:require [cljs.spec.alpha :as s] [cljs.test :as test :refer-macros [deftest is are run-tests]] - [cljs.spec.impl.gen.alpha :as gen] + [cljs.spec.gen.alpha :as gen] [clojure.test.check.generators])) (s/def ::even? (s/and number? even?)) From cf617c8d4594efd061954c3df3d9e96dacefdb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 5 May 2017 13:43:59 -0700 Subject: [PATCH 2306/4033] CLJS-2022: cljs.spec -> cljs.spec.alpha fixes --- src/main/cljs/cljs/repl.cljs | 4 ++-- src/main/cljs/cljs/spec/test/alpha.cljc | 2 +- src/main/clojure/cljs/analyzer.cljc | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index 443c011e7..2035dd8f9 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -8,7 +8,7 @@ (ns cljs.repl (:require-macros cljs.repl) - (:require [cljs.spec :as spec])) + (:require [cljs.spec.alpha :as spec])) (defn print-doc [{n :ns nm :name :as m}] (println "-------------------------") @@ -29,7 +29,7 @@ (if (:special-form m) (do (println "Special Form") - (println " " (:doc m)) + (println " " (:doc m)) (if (contains? m :url) (when (:url m) (println (str "\n Please see http://clojure.org/" (:url m)))) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index ab4808dde..877a12355 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -104,7 +104,7 @@ invokes the fn you provide, enabling arbitrary stubbing and mocking. Returns a collection of syms naming the vars instrumented." ([] `(instrument '[~@(#?(:clj s/speced-vars - :cljs cljs.spec$macros/speced-vars))])) + :cljs cljs.spec.alpha$macros/speced-vars))])) ([xs] `(instrument ~xs nil)) ([sym-or-syms opts] diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index eedb4f39f..7425fd3ef 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2150,7 +2150,7 @@ (when (some? alias) {rk (merge {alias lib} {lib lib} (when js-module-provides {js-module-provides lib}))}) - (when (some? referred-without-renamed) + (when (some? referred-without-renamed) {uk (apply hash-map (interleave referred-without-renamed (repeat lib)))}) (when (some? renamed) {renk (reduce (fn [m [original renamed]] @@ -2950,9 +2950,9 @@ mvar))) #?(:cljs - (let [cached-var (delay (get (ns-interns* 'cljs.spec) 'macroexpand-check))] + (let [cached-var (delay (get (ns-interns* 'cljs.spec.alpha) 'macroexpand-check))] (defn get-macroexpand-check-var [] - (when (some? (find-ns-obj 'cljs.spec)) + (when (some? (find-ns-obj 'cljs.spec.alpha)) @cached-var)))) (defn macroexpand-1* @@ -2964,7 +2964,7 @@ (if-some [mac-var (when (symbol? op) (get-expander op env))] (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) - (let [mchk #?(:clj (some-> (find-ns 'clojure.spec) + (let [mchk #?(:clj (some-> (find-ns 'clojure.spec.alpha) (ns-resolve 'macroexpand-check)) :cljs (get-macroexpand-check-var)) _ (when (some? mchk) From 8b409e08ddf7775299ec19c12e9421cc331838d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 8 May 2017 16:01:08 -0700 Subject: [PATCH 2307/4033] CLJS-2028: `realized?` throws on LazyTransformer --- src/main/cljs/cljs/core.cljs | 6 +++++- src/test/cljs/cljs/core_test.cljs | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c588de28e..0cda95830 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4015,7 +4015,11 @@ reduces them without incurring seq initialization" (-seq this)) (if (nil? rest) nil - (-seq rest)))) + (-seq rest))) + + IPending + (-realized? [_] + (nil? stepper))) (es6-iterable LazyTransformer) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 953c8c8de..c3f6ecc9f 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -846,7 +846,7 @@ (testing "Testing CLJS-853, function metadata" (is (= {:foo true} (meta ^:foo (fn [])))))) -(deftest test-807 +(deftest test-807 (testing "Testing CLJS-807, big int, float, big dec literals" (is (= -1 -1N)) (is (= 9.007199254740996E15 9007199254740995N)) @@ -1189,6 +1189,12 @@ (is (= (get "0123" nil :not-found) :not-found)) (is (= (get #js [0 1 2 3] nil :not-found) :not-found))) +(deftest test-cljs-2028 + (let [x (sequence (filter pos?) [1 2 -1])] + (is (not (realized? x))) + (is (= x [1 2])) + (is (realized? x)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 947ccb4c45ab6ec283da6e29de94e83a56892454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 7 May 2017 20:51:33 -0700 Subject: [PATCH 2308/4033] CLJS-2025: Self-host: spec tests fail --- src/main/cljs/cljs/js.cljs | 18 ++++++++++++++++-- src/main/cljs/cljs/spec/alpha.cljc | 11 +++++++++-- src/main/clojure/cljs/analyzer.cljc | 10 +++++++--- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ed38aab60..ba6319782 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -179,7 +179,7 @@ [] (->> (merge (get-in @env/*compiler* [::ana/namespaces ana/*cljs-ns* :requires]) (get-in @env/*compiler* [::ana/namespaces ana/*cljs-ns* :require-macros])) - (remove (fn [[k v]] (= k v))) + (remove (fn [[k v]] (symbol-identical? k v))) (into {}))) ;; ----------------------------------------------------------------------------- @@ -457,6 +457,19 @@ (update :renames rewrite-renames) (update :rename-macros rewrite-renames)))) +(defn- check-macro-autoload-inferring-missing + [{:keys [requires name] :as ast} cenv] + (let [namespaces (-> @cenv ::ana/namespaces) + missing-require-macros (into {} + (filter (fn [[_ full-ns]] + (let [{:keys [use-macros require-macros]} (get namespaces full-ns)] + (or (some #{full-ns} (vals use-macros)) + (some #{full-ns} (vals require-macros)))))) + requires) + ast' (update-in ast [:require-macros] merge missing-require-macros)] + (swap! cenv update-in [::ana/namespaces name :require-macros] merge missing-require-macros) + ast')) + (defn- ns-side-effects ([bound-vars ana-env ast opts cb] (ns-side-effects false bound-vars ana-env ast opts cb)) @@ -498,7 +511,8 @@ env/*compiler* (:*compiler* bound-vars)] (let [ast' (-> rewritten-ast (ana/check-use-macros-inferring-missing env) - (ana/check-rename-macros-inferring-missing env))] + (ana/check-rename-macros-inferring-missing env) + (check-macro-autoload-inferring-missing env))] (cb {:value ast'}))) (catch :default cause (cb (wrap-error diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 35278a6d0..f3b15ba51 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -36,12 +36,19 @@ (defn- res [env form] (cond (keyword? form) form - (symbol? form) (clojure.core/or (->> form (resolve env) ->sym) form) + (symbol? form) #?(:clj (clojure.core/or (->> form (resolve env) ->sym) form) + :cljs (let [resolved (clojure.core/or (->> form (resolve env) ->sym) form) + ns-name (namespace resolved)] + (symbol + (if (clojure.core/and ns-name (str/ends-with? ns-name "$macros")) + (subs ns-name 0 (- (count ns-name) 7)) + ns-name) + (name resolved)))) (sequential? form) (walk/postwalk #(if (symbol? %) (res env %) %) (unfn form)) :else form)) (defmacro ^:private mres - "a compile time res, for use in cljs/spec.cljs" + "a compile time res, for use in cljs/spec/alpha.cljs" [form] (res &env form)) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7425fd3ef..c7e6d2750 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -968,8 +968,12 @@ (some? (namespace sym)) (let [ns (namespace sym) ns (if (= "clojure.core" ns) "cljs.core" ns) - full-ns (resolve-macro-ns-alias env ns)] - (get-in namespaces [full-ns :macros (symbol (name sym))])) + full-ns (resolve-macro-ns-alias env ns) + #?@(:cljs [full-ns (if-not (string/ends-with? (str full-ns) "$macros") + (symbol (str full-ns "$macros")) + full-ns)])] + #?(:clj (get-in namespaces [full-ns :macros (symbol (name sym))]) + :cljs (get-in namespaces [full-ns :defs (symbol (name sym))]))) (some? (get-in namespaces [ns :use-macros sym])) (let [full-ns (get-in namespaces [ns :use-macros sym])] @@ -2865,7 +2869,7 @@ env) ret {:env env :form sym} lcls (:locals env)] - (if-some [lb (get lcls sym)] + (if-some [lb (get lcls sym)] (assoc ret :op :var :info lb) (let [sym-meta (meta sym) sym-ns (namespace sym) From 6e232445e69e3e70107a8dbfe46eb9fc6a4059e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 7 May 2017 12:26:11 -0700 Subject: [PATCH 2309/4033] CLJS-2024: Self-host: `find-ns-obj` broken for namespaces with 'a' as the first segment --- src/main/cljs/cljs/core.cljs | 4 +++- src/test/self/self_host/test.cljs | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0cda95830..932e9cc48 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10852,7 +10852,9 @@ reduces them without incurring seq initialization" ; may throw ReferenceError. (find-ns-obj* (try - (js/eval (first segs)) + (let [ctxt (js/eval (first segs))] + (when (and ctxt (object? ctxt)) + ctxt)) (catch js/ReferenceError e nil)) (next segs)) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 7a1e72b5b..fa5925c10 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -877,6 +877,20 @@ (is (nil? error)) (inc! l)))))) +(deftest test-cljs-2024 + (async done + (let [st (cljs/empty-state) + l (latch 1 done)] + (cljs/eval-str + st + "(find-ns-obj 'a.x)" + nil + {:context :expr + :eval node-eval} + (fn [{:keys [error] :as m}] + (is (nil? error)) + (inc! l)))))) + (defn -main [& args] (run-tests)) From 7f3fd0f3341cfdb3be0a9460842e08e9c79f264a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 9 May 2017 09:23:49 -0400 Subject: [PATCH 2310/4033] CLJS-1518: Case macro expansion evaluates expression twice Store the value of the case expression in a local. --- src/main/clojure/cljs/core.cljc | 3 ++- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 38c880df2..873de82e4 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2229,7 +2229,8 @@ vec (mapv #(if (seq? %) (vec %) [%]))) thens (vec (vals pairs))] - `(let [~esym (if (keyword? ~e) (.-fqn ~e) nil)] + `(let [~esym ~e + ~esym (if (keyword? ~esym) (.-fqn ~esym) nil)] (case* ~esym ~tests ~thens ~default))) ;; equality diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index c3f6ecc9f..78b304786 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1195,6 +1195,14 @@ (is (= x [1 2])) (is (realized? x)))) +(deftest test-1518 + (testing "Test evaluate expression once - keyword tests" + (let [m {:a :b + :b :c} + x (atom :a)] + (case (swap! x m) :a 0 :default) + (is (= :b @x))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 7c7d6b70bf0f61bad5dfc1eee55729da722ab91b Mon Sep 17 00:00:00 2001 From: Jake McCrary Date: Thu, 4 Feb 2016 15:58:31 -0600 Subject: [PATCH 2311/4033] CLJS-485: RegExp flags are being dropped by string/replace Changes clojure.string/replace-all to respect previously set RegExp flags when creating new RegExp. test for multiline --- src/main/cljs/clojure/string.cljs | 7 ++++++- src/test/cljs/clojure/string_test.cljs | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 4cf5ede11..a2433e056 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -26,7 +26,12 @@ (defn- replace-all [s re replacement] - (.replace s (js/RegExp. (.-source re) "g") replacement)) + (let [r (js/RegExp. (.-source re) + (cond-> "g" + (.-ignoreCase re) (str "i") + (.-multiline re) (str "m") + (.-unicode re) (str "u")))] + (.replace s r replacement))) (defn- replace-with [f] diff --git a/src/test/cljs/clojure/string_test.cljs b/src/test/cljs/clojure/string_test.cljs index 14f002c0a..615c0af19 100644 --- a/src/test/cljs/clojure/string_test.cljs +++ b/src/test/cljs/clojure/string_test.cljs @@ -25,7 +25,9 @@ (is (= "FOObarFOO" (s/replace "foobarfoo" #"foo" s/upper-case))) (is (= "barbar)foo" (s/replace "foo(bar)foo" "foo(" "bar"))) (is (= "FOO-ObarFOO-O" - (s/replace "foobarfoo" #"f(o)o" (fn [[m g1]] (s/upper-case (str m "-" g1))))))) + (s/replace "foobarfoo" #"f(o)o" (fn [[m g1]] (s/upper-case (str m "-" g1)))))) + (is (= "faabarfaa" (s/replace "FOObarfoo" #"(?i)foo" "faa"))) + (is (= "aaa\nccc" (s/replace "aaa\nbbb" #"(?m)^bbb" "ccc")))) (testing "Testing string join" (is (= "" (s/join nil))) From dfadee51fa3fad58b7c4cf7de532e9a10e0f802f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 21 Feb 2016 23:57:17 -0500 Subject: [PATCH 2312/4033] CLJS-1572: REPL doesn't give error for expressions with too many right parentheses. Take extra care to preserve the state of *in* so that anything beyond the first form remains for reading. This fundamentally makes the ClojureScript REPL behave like the Clojure REPL. In particular, it allows entering multiple forms on a single line (which will be evaluated serially). It also means that if malformed input lies beyond the initial form, it will be read and will cause an exception (just like in the Clojure REPL). The bulk of the complexity in this commit has to do with the case where a new line-numbering reader is established, so that errors in forms can be associated with line numbers, starting with line 1 being the first line of the form. This requires a little extra handling because the source-logging-push-back-reader introduces an extra 1-character buffer which must be transferred back to the original (pre-bound) *in*, otherwise things like an unmatched extra paren right after a well-formed form won't be detected (as the paren would be in the 1-char buffer and discarded.) Also, a Java PushbackReader needs to be eliminated, as it causes things to fail to behave like the Clojure REPL. --- src/main/clojure/cljs/repl.cljc | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index a2238128b..eb0565e93 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -97,14 +97,18 @@ ([request-prompt request-exit] (repl-read request-prompt request-exit *repl-opts*)) ([request-prompt request-exit opts] - (binding [*in* (if (true? (:source-map-inline opts)) - ((:reader opts)) - *in*)] - (or ({:line-start request-prompt :stream-end request-exit} - (skip-whitespace *in*)) - (let [input (reader/read {:read-cond :allow :features #{:cljs}} *in*)] - (skip-if-eol *in*) - input))))) + (let [current-in *in* + bind-in? (true? (:source-map-inline opts))] + (binding [*in* (if bind-in? + ((:reader opts)) + *in*)] + (or ({:line-start request-prompt :stream-end request-exit} + (skip-whitespace *in*)) + (let [input (reader/read {:read-cond :allow :features #{:cljs}} *in*)] + ;; Transfer 1-char buffer to original *in* + (readers/unread current-in (readers/read-char *in*)) + (skip-if-eol (if bind-in? current-in *in*)) + input)))))) ;; ============================================================================= ;; CLJS Specifics @@ -745,7 +749,7 @@ print println caught repl-caught reader #(readers/source-logging-push-back-reader - (PushbackReader. (io/reader *in*)) + *in* 1 "NO_SOURCE_FILE") print-no-newline print source-map-inline true From 355c20e98fc5f5eddf5d7e08225718429c1bbb63 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 9 May 2017 13:53:29 -0400 Subject: [PATCH 2313/4033] typo in bootstrap script --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index 335172edc..06d057ccb 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -37,7 +37,7 @@ rm data.json-$DJSON_RELEASE.jar echo "Fetching transit-clj..." curl --retry 3 -O -s https://repo1.maven.org/maven2/com/cognitect/transit-clj/$TRANSIT_RELEASE/transit-clj-$TRANSIT_RELEASE.jar || { echo "Download failed."; exit 1; } -echo "Copying transit-cjl-$TRANSIT_RELEASE.jar to lib/transit-clj-$TRANSIT_RELEASE.jar..." +echo "Copying transit-clj-$TRANSIT_RELEASE.jar to lib/transit-clj-$TRANSIT_RELEASE.jar..." cp transit-clj-$TRANSIT_RELEASE.jar lib/transit-clj-$TRANSIT_RELEASE.jar echo "Cleaning up data.json..." rm transit-clj-$TRANSIT_RELEASE.jar From f94831627e8795a93937b97b5b6fe739e0280167 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sun, 30 Apr 2017 16:42:33 +0200 Subject: [PATCH 2314/4033] fix some signature errors uncovered by closure :check-types IndexedSeq is off by one, missing the metadata .createAsIfByAssoc calls were providing undeclared extra arguments --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/compiler.cljc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 932e9cc48..aa5068df5 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8336,7 +8336,7 @@ reduces them without incurring seq initialization" (let [arr (if (and (instance? IndexedSeq keyvals) (zero? (.-i keyvals))) (.-arr keyvals) (into-array keyvals))] - (.createAsIfByAssoc PersistentArrayMap arr true false))) + (.createAsIfByAssoc PersistentArrayMap arr))) (defn obj-map "keyval => key val diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 06a875840..5e89d60d8 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -408,7 +408,7 @@ (emits "new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, " (count items) ", [" (comma-sep (interleave items (repeat "null"))) "], null), null)") - :else (emits "cljs.core.PersistentHashSet.createAsIfByAssoc([" (comma-sep items) "], true)")))) + :else (emits "cljs.core.PersistentHashSet.createAsIfByAssoc([" (comma-sep items) "])")))) (defmethod emit* :js-value [{:keys [items js-type env]}] @@ -565,7 +565,7 @@ :else line)) (defn checking-types? [] - (#{:error :warn} + (#{:error :warning} (get-in @env/*compiler* [:options :closure-warnings :check-types]))) @@ -750,7 +750,7 @@ (emitln " = null;") (emitln "if (arguments.length > " (dec (count params)) ") {") (let [a (emit-arguments-to-array (dec (count params)))] - (emitln " " (last params) " = new cljs.core.IndexedSeq(" a ",0);")) + (emitln " " (last params) " = new cljs.core.IndexedSeq(" a ",0,null);")) (emitln "} ")) (emits "return " delegate-name ".call(this,") (doseq [param params] @@ -819,7 +819,7 @@ (emitln "var " restarg " = null;") (emitln "if (arguments.length > " max-fixed-arity ") {") (let [a (emit-arguments-to-array max-fixed-arity)] - (emitln restarg " = new cljs.core.IndexedSeq(" a ",0);")) + (emitln restarg " = new cljs.core.IndexedSeq(" a ",0,null);")) (emitln "}") (emitln "return " n ".cljs$core$IFn$_invoke$arity$variadic(" (comma-sep (butlast maxparams)) From 72926e38cd75629e111d6859528167bd38961aa1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 May 2017 14:50:11 -0400 Subject: [PATCH 2315/4033] bump to Clojure 1.9.0-alpha16 in bootstrap script --- script/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bootstrap b/script/bootstrap index 06d057ccb..6ad614ba0 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.9.0-alpha14" +CLOJURE_RELEASE="1.9.0-alpha16" CLOSURE_RELEASE="20170423" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" From 27c076ee07a81b1e784403b4625f4ee1b8426c3c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 May 2017 14:56:28 -0400 Subject: [PATCH 2316/4033] fix broken clojure -> cljs alias rewriting tests --- src/test/clojure/cljs/analyzer_tests.clj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index fc98091a7..863899a54 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -340,22 +340,22 @@ (deftest test-rewrite-cljs-aliases (is (= (a/rewrite-cljs-aliases '((:require-macros (bar :refer [quux]) :reload) - (:require (clojure.spec :as s :refer [fdef]) :reload))) + (:require (clojure.spec.alpha :as s :refer [fdef]) :reload))) '((:require-macros (bar :refer [quux]) :reload) - (:require (cljs.spec :as s :refer [fdef]) - (cljs.spec :as clojure.spec) :reload)))) + (:require (cljs.spec.alpha :as s :refer [fdef]) + (cljs.spec.alpha :as clojure.spec.alpha) :reload)))) (is (= (a/rewrite-cljs-aliases '((:refer-clojure :exclude [first]) (:require-macros (bar :refer [quux]) :reload) - (:require (clojure.spec :as s) :reload))) + (:require (clojure.spec.alpha :as s) :reload))) '((:refer-clojure :exclude [first]) (:require-macros (bar :refer [quux]) :reload) - (:require (cljs.spec :as s) (cljs.spec :as clojure.spec) :reload)))) + (:require (cljs.spec.alpha :as s) (cljs.spec.alpha :as clojure.spec.alpha) :reload)))) (is (= (a/rewrite-cljs-aliases '((:require-macros (bar :refer [quux]) :reload) - (:require clojure.spec :reload))) + (:require clojure.spec.alpha :reload))) '((:require-macros (bar :refer [quux]) :reload) - (:require (cljs.spec :as clojure.spec) :reload))))) + (:require (cljs.spec.alpha :as clojure.spec.alpha) :reload))))) ;; ============================================================================= ;; Namespace metadata From 51012a57ff042eac855d1284fe70b83fe663c4d1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 May 2017 15:21:14 -0400 Subject: [PATCH 2317/4033] fix bootstrap script for Clojure 1.9.0-alpha16 bump --- script/bootstrap | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/script/bootstrap b/script/bootstrap index 6ad614ba0..792bd570e 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,6 +3,8 @@ set -e CLOJURE_RELEASE="1.9.0-alpha16" +SPEC_ALPHA_RELEASE="0.1.108" +CORE_SPECS_ALPHA_RELEASE="0.1.10" CLOSURE_RELEASE="20170423" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" @@ -28,6 +30,20 @@ rm -rf clojure-$CLOJURE_RELEASE/ echo "Cleaning up Clojure archive..." rm clojure-$CLOJURE_RELEASE.zip +echo "Fetching specs.alpha...." +curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/spec.alpha/$SPEC_ALPHA_RELEASE/spec.alpha-$SPEC_ALPHA_RELEASE.jar || { echo "Download failed."; exit 1; } +echo "Copying spec.alpha-$SPEC_ALPHA_RELEASE/spec.alpha-$SPEC_ALPHA_RELEASE.jar to lib/spec.alpha.jar..." +cp spec.alpha-$SPEC_ALPHA_RELEASE.jar lib/spec.alpha-$SPEC_ALPHA_RELEASE.jar +echo "Cleaning up spec.alpha..." +rm spec.alpha-$SPEC_ALPHA_RELEASE.jar + +echo "Fetching core.specs.alpha...." +curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/core.specs.alpha/$CORE_SPECS_ALPHA_RELEASE/core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar || { echo "Download failed."; exit 1; } +echo "Copying core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE/core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar to lib/core.specs.alpha.jar..." +cp core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar lib/core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar +echo "Cleaning up core.specs.alpha..." +rm core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar + echo "Fetching data.json..." curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/data.json/$DJSON_RELEASE/data.json-$DJSON_RELEASE.jar || { echo "Download failed."; exit 1; } echo "Copying data.json-$DJSON_RELEASE.jar to lib/data.json-$DJSON_RELEASE.jar..." From 1d8e6fb2995f5d8b484085a68e00345bddef5aad Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 May 2017 16:17:22 -0400 Subject: [PATCH 2318/4033] 1.9.542 --- README.md | 6 +++--- changes.md | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d5fbefb4a..f43f97596 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.521 +Latest stable release: 1.9.542 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.521"] +[org.clojure/clojurescript "1.9.542"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.521 org.clojure clojurescript - 1.9.521 + 1.9.542 ``` diff --git a/changes.md b/changes.md index 75b0355f3..986edfd7b 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,22 @@ +## 1.9.542 + +### Enhancements +* CLJS-1572: REPL doesn't give error for expressions with too many right parentheses + +### Changes +* cljs.spec -> cljs.spec.alpha +* CLJS-2013 - Add MapEntry type +* CLJS-2015: Self-host: `defmacro` should return the Var +* CLJS-2017: Upgrade Closure Compiler to latest April 2017 release + +### Fixes +* CLJS-485: RegExp flags are being dropped by string/replace +* CLJS-1518: Case macro expansion evaluates expression twice +* CLJS-2024: Self-host: `find-ns-obj` broken for namespaces with 'a' as the first segment +* CLJS-2028: `realized?` throws on LazyTransformer +* CLJS-2010: refer-clojure :rename throws on valid invocations +* CLJS-2007: Whitespace optimizations should respect :main option. + ## 1.9.521 ### Fixes From d93c4356e7ab78743ae66d8cffe8df54869f0be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 12 May 2017 13:39:37 -0700 Subject: [PATCH 2319/4033] CLJS-2034: Sequence and Eduction produce infinite loop in transducer that appends to the reduction The implementation of transducers in ClojureScript tracked an old counterpart in the Clojure codebase. This patch addresses the change introduced in the following commit to Clojure, which replaced `LazyTransformer` with `TransformerIterator`, effectively making the transducer behavior akin to the one in Clojure. https://github.com/clojure/clojure/commit/c47e1bbcfa227723df28d1c9e0a6df2bcb0fecc1 --- src/main/cljs/cljs/core.cljs | 279 ++++++++++++++---------------- src/test/cljs/cljs/core_test.cljs | 8 +- 2 files changed, 134 insertions(+), 153 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index aa5068df5..62ff3e17b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3409,7 +3409,7 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x (count coll))) (lastIndexOf [coll x start] (-lastIndexOf coll x start)) - + IWithMeta (-with-meta [coll m] (ChunkedCons. chunk more m __hash)) @@ -3868,41 +3868,53 @@ reduces them without incurring seq initialization" (seqable? coll) (seq-iter coll) :else (throw (js/Error. (str "Cannot create iterator from " coll))))) -(declare LazyTransformer) +(deftype Many [vals] + Object + (add [this o] + (.push vals o) + this) + (remove [this] + (.shift vals)) + (isEmpty [this] + (zero? (.-length vals))) + (toString [this] + (str "Many: " vals))) -(defn lazy-transformer [stepper] - (LazyTransformer. stepper nil nil nil)) +(def ^:private NONE #js {}) -(deftype Stepper [xform iter] +(deftype Single [^:mutable val] Object - (step [this lt] - (loop [] - (if (and (not (nil? (.-stepper lt))) - (.hasNext iter)) - (if (reduced? (xform lt (.next iter))) - (when-not (nil? (.-rest lt)) - (set! (.. lt -rest -stepper) nil)) - (recur)))) - (when-not (nil? (.-stepper lt)) - (xform lt)))) - -(defn stepper [xform iter] - (letfn [(stepfn - ([result] - (let [lt (if (reduced? result) - @result - result)] - (set! (.-stepper lt) nil) - result)) - ([result input] - (let [lt result] - (set! (.-first lt) input) - (set! (.-rest lt) (lazy-transformer (.-stepper lt))) - (set! (.-stepper lt) nil) - (.-rest lt))))] - (Stepper. (xform stepfn) iter))) - -(deftype MultiStepper [xform iters nexts] + (add [this o] + (if (identical? val NONE) + (do + (set! val o) + this) + (Many. #js [val o]))) + (remove [this] + (if (identical? val NONE) + (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))) + +(deftype Empty [] + Object + (add [this o] + (Single. o)) + (remove [this] + (throw (js/Error. (str "Removing object from empty buffer")))) + (isEmpty [this] + true) + (toString [this] + "Empty")) + +(def ^:private EMPTY (Empty.)) + +(deftype MultiIterator [iters] Object (hasNext [_] (loop [iters (seq iters)] @@ -3913,128 +3925,83 @@ reduces them without incurring seq initialization" (recur (next iters)))) true))) (next [_] - (dotimes [i (alength iters)] - (aset nexts i (.next (aget iters i)))) - (prim-seq nexts 0)) - (step [this lt] - (loop [] - (if (and (not (nil? (.-stepper lt))) - (.hasNext this)) - (if (reduced? (apply xform (cons lt (.next this)))) - (when-not (nil? (.-rest lt)) - (set! (.. lt -rest -stepper) nil)) - (recur)))) - (when-not (nil? (.-stepper lt)) - (xform lt)))) - -(defn multi-stepper - ([xform iters] - (multi-stepper xform iters - (make-array (alength iters)))) - ([xform iters nexts] - (letfn [(stepfn - ([result] - (let [lt (if (reduced? result) - @result - result)] - (set! (.-stepper lt) nil) - lt)) - ([result input] - (let [lt result] - (set! (.-first lt) input) - (set! (.-rest lt) (lazy-transformer (.-stepper lt))) - (set! (.-stepper lt) nil) - (.-rest lt))))] - (MultiStepper. (xform stepfn) iters nexts)))) - -(deftype LazyTransformer [^:mutable stepper ^:mutable first ^:mutable rest meta] + (let [nexts (array)] + (dotimes [i (alength iters)] + (aset nexts i (.next (aget iters i)))) + (prim-seq nexts 0)))) + +(defn- chunkIteratorSeq [iter] + (lazy-seq + (when ^boolean (.hasNext iter) + (let [arr (array)] + (loop [n 0] + (if (and (.hasNext iter) (< n 32)) + (do + (aset arr n (.next iter)) + (recur (inc n))) + (chunk-cons (array-chunk arr 0 n) (chunkIteratorSeq iter)))))))) + +(deftype TransformerIterator [^:mutable buffer ^:mutable _next ^:mutable completed ^:mutable xf sourceIter multi] Object - (indexOf [coll x] - (-indexOf coll x 0)) - (indexOf [coll x start] - (-indexOf coll x start)) - (lastIndexOf [coll x] - (-lastIndexOf coll x (count coll))) - (lastIndexOf [coll x start] - (-lastIndexOf coll x start)) - - IWithMeta - (-with-meta [this new-meta] - (LazyTransformer. stepper first rest new-meta)) - - IMeta - (-meta [this] meta) - - ICollection - (-conj [this o] - (cons o (-seq this))) - - IEmptyableCollection - (-empty [this] - ()) - - ISequential - IEquiv - (-equiv [this other] - (let [s (-seq this)] - (if-not (nil? s) - (equiv-sequential this other) - (and (sequential? other) - (nil? (seq other)))))) - - IHash - (-hash [this] - (hash-ordered-coll this)) - - ISeqable - (-seq [this] - (when-not (nil? stepper) - (.step stepper this)) - (if (nil? rest) - nil - this)) - - ISeq - (-first [this] - (when-not (nil? stepper) - (-seq this)) - (if (nil? rest) - nil - first)) - - (-rest [this] - (when-not (nil? stepper) - (-seq this)) - (if (nil? rest) - () - rest)) - - INext - (-next [this] - (when-not (nil? stepper) - (-seq this)) - (if (nil? rest) - nil - (-seq rest))) - - IPending - (-realized? [_] - (nil? stepper))) - -(es6-iterable LazyTransformer) - -(set! (.-create LazyTransformer) + (step [this] + (if-not (identical? _next NONE) + true + (loop [] + (if (identical? _next NONE) + (if ^boolean (.isEmpty buffer) + (if ^boolean completed + false + (if ^boolean (.hasNext sourceIter) + (let [iter (if ^boolean multi + (apply xf (cons nil (.next sourceIter))) + (xf nil (.next sourceIter)))] + (when (reduced? iter) + (xf nil) + (set! completed true)) + (recur)) + (do + (xf nil) + (set! completed true) + (recur)))) + (do + (set! _next (.remove buffer)) + (recur))) + true)))) + (hasNext [this] + (.step this)) + (next [this] + (if ^boolean (.hasNext this) + (let [ret _next] + (set! _next NONE) + ret) + (throw (js/Error. "No such element")))) + (remove [_] + (js/Error. "Unsupported operation"))) + +(es6-iterable TransformerIterator) + +(defn transformer-iterator + [xform sourceIter multi] + (let [iterator (TransformerIterator. EMPTY NONE false nil sourceIter multi)] + (set! (.-xf iterator) + (xform (fn + ([] nil) + ([acc] acc) + ([acc o] + (set! (.-buffer iterator) (.add (.-buffer iterator) o)) + acc)))) + iterator)) + +(set! (.-create TransformerIterator) (fn [xform coll] - (LazyTransformer. (stepper xform (iter coll)) nil nil nil))) + (transformer-iterator xform (iter coll) false))) -(set! (.-createMulti LazyTransformer) +(set! (.-createMulti TransformerIterator) (fn [xform colls] (let [iters (array)] (doseq [coll colls] (.push iters (iter coll))) - (LazyTransformer. - (multi-stepper xform iters (make-array (alength iters))) - nil nil nil)))) + (transformer-iterator xform (MultiIterator. iters) true)))) (defn sequence "Coerces coll to a (possibly empty) sequence, if it is not already @@ -4050,9 +4017,13 @@ reduces them without incurring seq initialization" coll (or (seq coll) ()))) ([xform coll] - (.create LazyTransformer xform coll)) + (or (chunkIteratorSeq + (.create TransformerIterator xform coll)) + ())) ([xform coll & colls] - (.createMulti LazyTransformer xform (to-array (cons coll colls))))) + (or (chunkIteratorSeq + (.createMulti TransformerIterator xform (to-array (cons coll colls)))) + ()))) (defn ^boolean every? "Returns true if (pred x) is logical true for every x in coll, else @@ -4257,7 +4228,7 @@ reduces them without incurring seq initialization" (-equiv this other)) IAtom - + IEquiv (-equiv [o other] (identical? o other)) @@ -9645,7 +9616,7 @@ reduces them without incurring seq initialization" LazySeq (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) - LazyTransformer + TransformerIterator (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) IndexedSeq @@ -9955,6 +9926,10 @@ reduces them without incurring seq initialization" ISequential + IIterable + (-iterator [coll] + (.create TransformerIterator xform coll)) + ISeqable (-seq [_] (seq (sequence xform coll))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 78b304786..c103ed55e 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -348,7 +348,13 @@ (is (= (sequence xf [0 0] [1 2]) [1 2]))) (is (= (-> (sequence (map inc) [1 2 3]) (with-meta {:a 1}) - meta) {:a 1})))) + meta) {:a 1})) + (let [xf (fn [rf] + (fn + ([] (rf)) + ([result] (rf result :foo)) + ([result input] (rf result input))))] + (is (= (sequence xf [1 2 3]) [1 2 3 :foo]))))) (deftest test-obj-equiv (testing "Object equiv method" From 957cb8756f5a4b71bcc96d0d08943c83242dccfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 May 2017 19:11:42 -0700 Subject: [PATCH 2320/4033] CLJS-2020: defmulti "miss" performance poor This patch addresses the change applied to Clojure in commit https://github.com/clojure/clojure/commit/6d305a0959183acb75fc44f9192eed2abf33bf25, effectively caching the default multimethod dispatch value case. --- src/main/cljs/cljs/core.cljs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 62ff3e17b..52a27fd24 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10022,7 +10022,7 @@ reduces them without incurring seq initialization" (array? x) (vec (map thisfn x)) - + (identical? (type x) js/Object) (into {} (for [k (js-keys x)] [(keyfn k) (thisfn (aget x k))])) @@ -10235,7 +10235,7 @@ reduces them without incurring seq initialization" (or (prefers* x y prefer-table) (isa? hierarchy x y))) (defn- find-and-cache-best-method - [name dispatch-val hierarchy method-table prefer-table method-cache cached-hierarchy] + [name dispatch-val hierarchy method-table prefer-table method-cache cached-hierarchy default-dispatch-val] (let [best-entry (reduce (fn [be [k _ :as e]] (if (isa? @hierarchy dispatch-val k) (let [be2 (if (or (nil? be) (dominates k (first be) prefer-table @hierarchy)) @@ -10243,12 +10243,15 @@ 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 - "' match dispatch value: " dispatch-val " -> " k - " and " (first be2) ", and neither is preferred")))) + (str "Multiple methods in multimethod '" name + "' match dispatch value: " dispatch-val " -> " k + " and " (first be2) ", and neither is preferred")))) be2) be)) - nil @method-table)] + nil @method-table) + best-entry (if-let [entry (and (nil? best-entry) (@method-table default-dispatch-val))] + [default-dispatch-val entry] + best-entry)] (when best-entry (if (= @cached-hierarchy @hierarchy) (do @@ -10257,7 +10260,7 @@ reduces them without incurring seq initialization" (do (reset-cache method-cache method-table cached-hierarchy hierarchy) (find-and-cache-best-method name dispatch-val hierarchy method-table prefer-table - method-cache cached-hierarchy)))))) + method-cache cached-hierarchy default-dispatch-val)))))) (defprotocol IMultiFn (-reset [mf]) @@ -10432,10 +10435,8 @@ reduces them without incurring seq initialization" (reset-cache method-cache method-table cached-hierarchy hierarchy)) (if-let [target-fn (@method-cache dispatch-val)] target-fn - (if-let [target-fn (find-and-cache-best-method name dispatch-val hierarchy method-table - prefer-table method-cache cached-hierarchy)] - target-fn - (@method-table default-dispatch-val)))) + (find-and-cache-best-method name dispatch-val hierarchy method-table + prefer-table method-cache cached-hierarchy default-dispatch-val))) (-prefer-method [mf dispatch-val-x dispatch-val-y] (when (prefers* dispatch-val-x dispatch-val-y prefer-table) @@ -10452,7 +10453,7 @@ reduces them without incurring seq initialization" (-prefers [mf] @prefer-table) (-default-dispatch-val [mf] default-dispatch-val) (-dispatch-fn [mf] dispatch-fn) - + INamed (-name [this] (-name name)) (-namespace [this] (-namespace name)) From 123643b1821a415c104a8432b5e4dc033644ef98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 May 2017 18:11:24 -0700 Subject: [PATCH 2321/4033] CLJS-2023: User supplied type hints stopped working on js/goog.DEBUG --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/analyzer_tests.clj | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c7e6d2750..db32f76a2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -854,7 +854,7 @@ (merge {:name sym :ns 'js - :tag (with-meta (or (js-tag pre) 'js) {:prefix pre})} + :tag (with-meta (or (js-tag pre) (:tag (meta sym)) 'js) {:prefix pre})} (when-let [ret-tag (js-tag pre :ret-tag)] {:js-fn-var true :ret-tag ret-tag})))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 863899a54..7a7c014e3 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -694,6 +694,10 @@ z))))) :tag meta :prefix)))) +(deftest test-cljs-2023 + (let [form (with-meta 'js/goog.DEBUG {:tag 'boolean})] + (is (= (-> (ana-api/analyze (a/empty-env) form) :tag) 'boolean)))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From 2d59793ce1a5a5a0d2161fd0f887f58b9fd92f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 May 2017 17:58:05 -0700 Subject: [PATCH 2322/4033] CLJS-2026: Add Compiler option for rewrite polyfills --- src/main/clojure/cljs/closure.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8c27d7bcd..1bac4d0e4 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -246,6 +246,10 @@ (. compiler-options (setGenerateExports (:closure-generate-exports opts)))) + (when (contains? opts :rewrite-polyfills) + (. compiler-options + (setRewritePolyfills (:rewrite-polyfills opts)))) + (. compiler-options (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler ) @@ -1575,7 +1579,7 @@ (-> opts (select-keys [:closure-warnings :closure-extra-annotations :pretty-print - :language-in :language-out :closure-module-roots]) + :language-in :language-out :closure-module-roots :rewrite-polyfills]) (set-options (CompilerOptions.)))) (defn get-js-root [closure-compiler] From e35dd45a257b1b7bfd10e35f32a8ff0d122ae6c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 May 2017 17:42:10 -0700 Subject: [PATCH 2323/4033] CLJS-2032: Case macro expansion evaluates expression twice when no matching clause --- src/main/clojure/cljs/core.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 873de82e4..5b5043247 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2192,11 +2192,12 @@ expression, a vector can be used to match a list if needed. The test-constants need not be all of the same type." [e & clauses] - (core/let [default (if (odd? (count clauses)) + (core/let [esym (gensym) + default (if (odd? (count clauses)) (last clauses) `(throw (js/Error. - (cljs.core/str "No matching clause: " ~e)))) + (cljs.core/str "No matching clause: " ~esym)))) env &env pairs (reduce (core/fn [m [test expr]] @@ -2214,7 +2215,6 @@ :else (assoc-test m test expr env))) {} (partition 2 clauses)) - esym (gensym) tests (keys pairs)] (core/cond (every? (some-fn core/number? core/string? #?(:clj core/char? :cljs (core/fnil core/char? :nonchar)) #(const? env %)) tests) From be518275ec0590693ebdcb976cbe4cd8a6cef732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 14 May 2017 19:46:09 -0700 Subject: [PATCH 2324/4033] CLJS-2005: Bad error message with duplicate arity function definitions --- src/main/clojure/cljs/core.cljc | 5 ++++- src/test/clojure/cljs/analyzer_tests.clj | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 5b5043247..8827217fa 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2973,7 +2973,10 @@ :method-params sigs :arglists arglists :arglists-meta (doall (map meta arglists))}) - args-sym (gensym "args")] + args-sym (gensym "args") + param-counts (map count arglists)] + (core/when (not= (distinct param-counts) param-counts) + (ana/warning :overload-arity {} {:name name})) `(do (def ~(with-meta name meta) (fn [~'var_args] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 7a7c014e3..bf093bb59 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -482,6 +482,17 @@ (println x))))) (is (.startsWith (first @ws) "js/foo is shadowed by a local")))) +(deftest test-cljs-2005 + (let [ws (atom [])] + (try + (a/with-warning-handlers [(collecting-warning-handler ws)] + (a/analyze (a/empty-env) + '(defn myfun + ([x] x) + ([x] x)))) + (catch Exception _)) + (is (.startsWith (first @ws) "myfun: Can't have 2 overloads with same arity")))) + (deftest test-canonicalize-specs (is (= (a/canonicalize-specs '((quote [clojure.set :as set]))) '([clojure.set :as set]))) From 1ed7091056324d5bed26bbf1e317e1cbaeaee008 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 14 May 2017 20:05:31 -0400 Subject: [PATCH 2325/4033] CLJS-2008: Self-host: backport fixes to threading macros --- src/main/clojure/cljs/core.cljc | 42 +++++++++++++++++++---------- src/test/cljs/cljs/core_test.cljs | 44 +++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8827217fa..de0c46220 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -449,10 +449,13 @@ [expr & clauses] (core/assert (even? (count clauses))) (core/let [g (gensym) - pstep (core/fn [[test step]] `(if ~test (-> ~g ~step) ~g))] + steps (map (core/fn [[test step]] `(if ~test (-> ~g ~step) ~g)) + (partition 2 clauses))] `(let [~g ~expr - ~@(interleave (repeat g) (map pstep (partition 2 clauses)))] - ~g)))) + ~@(interleave (repeat g) (butlast steps))] + ~(if (empty? steps) + g + (last steps)))))) #?(:cljs (core/defmacro cond->> @@ -463,10 +466,13 @@ [expr & clauses] (core/assert (even? (count clauses))) (core/let [g (gensym) - pstep (core/fn [[test step]] `(if ~test (->> ~g ~step) ~g))] + steps (map (core/fn [[test step]] `(if ~test (->> ~g ~step) ~g)) + (partition 2 clauses))] `(let [~g ~expr - ~@(interleave (repeat g) (map pstep (partition 2 clauses)))] - ~g)))) + ~@(interleave (repeat g) (butlast steps))] + ~(if (empty? steps) + g + (last steps)))))) #?(:cljs (core/defmacro as-> @@ -475,8 +481,10 @@ successive form, returning the result of the last form." [expr name & forms] `(let [~name ~expr - ~@(interleave (repeat name) forms)] - ~name))) + ~@(interleave (repeat name) (butlast forms))] + ~(if (empty? forms) + name + (last forms))))) #?(:cljs (core/defmacro some-> @@ -484,10 +492,13 @@ and when that result is not nil, through the next etc" [expr & forms] (core/let [g (gensym) - pstep (core/fn [step] `(if (nil? ~g) nil (-> ~g ~step)))] + steps (map (core/fn [step] `(if (nil? ~g) nil (-> ~g ~step))) + forms)] `(let [~g ~expr - ~@(interleave (repeat g) (map pstep forms))] - ~g)))) + ~@(interleave (repeat g) (butlast steps))] + ~(if (empty? steps) + g + (last steps)))))) #?(:cljs (core/defmacro some->> @@ -495,10 +506,13 @@ and when that result is not nil, through the next etc" [expr & forms] (core/let [g (gensym) - pstep (core/fn [step] `(if (nil? ~g) nil (->> ~g ~step)))] + steps (map (core/fn [step] `(if (nil? ~g) nil (->> ~g ~step))) + forms)] `(let [~g ~expr - ~@(interleave (repeat g) (map pstep forms))] - ~g)))) + ~@(interleave (repeat g) (butlast steps))] + ~(if (empty? steps) + g + (last steps)))))) #?(:cljs (core/defmacro if-some diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index c103ed55e..e54b3c4ab 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -491,6 +491,50 @@ (is (= expected (hash uuid))) (is (= expected (.-__hash uuid)))))) +(def constantly-nil (constantly nil)) + +(deftest some->test + (is (nil? (some-> nil))) + (is (= 0 (some-> 0))) + (is (= -1 (some-> 1 (- 2)))) + (is (nil? (some-> 1 constantly-nil (- 2))))) + +(deftest some->>test + (is (nil? (some->> nil))) + (is (= 0 (some->> 0))) + (is (= 1 (some->> 1 (- 2)))) + (is (nil? (some->> 1 constantly-nil (- 2))))) + +(deftest cond->test + (is (= 0 (cond-> 0))) + (is (= -1 (cond-> 0 true inc true (- 2)))) + (is (= 0 (cond-> 0 false inc))) + (is (= -1 (cond-> 1 true (- 2) false inc)))) + +(deftest cond->>test + (is (= 0 (cond->> 0))) + (is (= 1 (cond->> 0 true inc true (- 2)))) + (is (= 0 (cond->> 0 false inc))) + (is (= 1 (cond->> 1 true (- 2) false inc)))) + +(deftest as->test + (is (= 0 (as-> 0 x))) + (is (= 1 (as-> 0 x (inc x)))) + (is (= 2 (as-> [0 1] x + (map inc x) + (reverse x) + (first x))))) + +(deftest threading-loop-recur + (is (nil? (loop [] + (as-> 0 x + (when-not (zero? x) + (recur)))))) + (is (nil? (loop [x nil] (some-> x recur)))) + (is (nil? (loop [x nil] (some->> x recur)))) + (is (= 0 (loop [x 0] (cond-> x false recur)))) + (is (= 0 (loop [x 0] (cond->> x false recur))))) + ;; ============================================================================= ;; Tickets From df6761abf0b2b5008956c57b8591fe1b0fa7d91c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 14 May 2017 17:53:31 -0400 Subject: [PATCH 2326/4033] CLJS-2033: set-validator! should check current state --- src/main/cljs/cljs/core.cljs | 3 +++ src/test/cljs/cljs/core_test.cljs | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 52a27fd24..df2c07643 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4325,6 +4325,9 @@ reduces them without incurring seq initialization" is not acceptable to the new validator, an Error will be thrown and the validator will not be changed." [iref val] + (when (and (some? val) + (not (val (-deref iref)))) + (throw (js/Error. "Validator rejected reference state"))) (set! (.-validator iref) val)) (defn get-validator diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index e54b3c4ab..0d3945755 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -47,6 +47,15 @@ (testing "Testing atom validators" (is (= coll? (get-validator a))) (is (thrown? js/Error (reset! a 1))) + (is (thrown? js/Error (set-validator! a number?))) + (is (some? (get-validator a))) + (set-validator! a nil) + (is (nil? (get-validator a))) + (let [e1 (ex-info "" {})] + (try + (set-validator! a (fn [_] (throw e1))) + (catch :default e2 + (is (identical? e1 e2))))) (is (= {:a 1} (meta a))) (alter-meta! a assoc :b 2) (is (= {:a 1 :b 2} (meta a))))) From 5cc7865a1e2e9aecc24f975454721f621ba8ee60 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 12 May 2017 22:04:58 -0400 Subject: [PATCH 2327/4033] CLJS-2035: Self-host: Add map-entry-test to self-parity --- src/test/self/self_parity/test.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 3a0da83ef..16e6c8ffa 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -292,6 +292,7 @@ #_[cljs.spec.test-test] [cljs.clojure-alias-test] [cljs.hash-map-test] + [cljs.map-entry-test] [cljs.syntax-quote-test] [cljs.predicates-test] [cljs.test-test] @@ -329,6 +330,7 @@ #_'cljs.spec.test-test 'cljs.clojure-alias-test 'cljs.hash-map-test + 'cljs.map-entry-test 'cljs.syntax-quote-test 'cljs.predicates-test 'cljs.test-test From 77e01a01af9f45c76cfa34aa67bfae154b075544 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 17 May 2017 11:42:26 +0200 Subject: [PATCH 2328/4033] Fix assumption that all closure-compliant JS is goog.* When using other Closure-compatible JS libs the compiler would otherwise try to emit CLJS-styles invokes. Expected: somefn(1) Actual: somefn.cljs$core$IFn$_invoke$arity$1 ? ... : somefn.call(null, 1) This is a CLJS idiom and should only be emitted if we have analyzer data for the given namespace. --- src/main/clojure/cljs/compiler.cljc | 4 +++- src/test/cljs/cljs/invoke_test.cljs | 16 ++++++++++++++ src/test/clojure/cljs/compiler_tests.clj | 27 +++++++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/test/cljs/cljs/invoke_test.cljs diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 5e89d60d8..a1d62edd3 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -951,7 +951,9 @@ goog? (when ns (or (= ns 'goog) (when-let [ns-str (str ns)] - (= (get (string/split ns-str #"\.") 0 nil) "goog")))) + (= (get (string/split ns-str #"\.") 0 nil) "goog")) + (not (contains? (::ana/namespaces @env/*compiler*) ns)))) + keyword? (and (= (-> f :op) :constant) (keyword? (-> f :form))) [f variadic-invoke] diff --git a/src/test/cljs/cljs/invoke_test.cljs b/src/test/cljs/cljs/invoke_test.cljs new file mode 100644 index 000000000..e25c00b5b --- /dev/null +++ b/src/test/cljs/cljs/invoke_test.cljs @@ -0,0 +1,16 @@ +(ns cljs.invoke-test + (:require [goog.string :as gstr])) + +(defn variadic-fn [& args]) + +(variadic-fn 1 2 3) + +(defn multi-fn + ([a] a) + ([a b] a)) + +(multi-fn 2) + +(gstr/urlEncode "foo") + +(js/goog.string.urlDecode "bar") diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index ba1eb90ee..a465b1233 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -10,9 +10,12 @@ (:use clojure.test) (:require [cljs.analyzer :as ana] [cljs.compiler :as comp] + [cljs.compiler.api :as comp-api] [cljs.env :as env] [cljs.util :as util] - [cljs.tagged-literals :as tags]) + [cljs.tagged-literals :as tags] + [clojure.java.io :as io] + [clojure.string :as str]) (:import [java.io File])) (def aenv (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) @@ -215,6 +218,28 @@ opts (fn [] (ana/analyze aenv specify-test-code nil opts)))))))))))) + +(deftest test-optimized-invoke-emit + (let [out-file + (io/file "target/invoke_test.js")] + (comp-api/with-core-cljs + (comp-api/compile-file + (io/file "src/test/cljs/cljs/invoke_test.cljs") + out-file + {:static-fns true})) + + (let [content (slurp out-file)] + ;; test for fn( not fn.call(, omitting arguments in test because they are not relevant + ;; should emit variadic invokes + (is (str/includes? content "cljs.invoke_test.variadic_fn.cljs$core$IFn$_invoke$arity$variadic(")) + ;; should emit optimized invokes + (is (str/includes? content "cljs.invoke_test.multi_fn.cljs$core$IFn$_invoke$arity$1(")) + ;; closure js code must never use .call( + (is (str/includes? content "goog.string.urlEncode(")) + ;; js/goog.string.urlDecode should not use .call + (is (str/includes? content "goog.string.urlDecode("))) + )) + ;; CLJS-1225 (comment From 3501c40b32db49292e9c9550bc143d1b74a473d6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 May 2017 16:20:08 -0400 Subject: [PATCH 2329/4033] fix tests broken by last commit where we dropped .call invokes for anything not coming from a ClojureScript namespace --- src/test/clojure/cljs/module_processing_tests.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 86911aae1..d3b4d4a08 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -60,7 +60,7 @@ :module-type :commonjs}]}) compile (fn [form] (with-out-str (comp/emit (ana/analyze (ana/empty-env) form)))) - output "module$src$test$cljs$calculator.add.call(null,(3),(4));\n"] + output "module$src$test$cljs$calculator.add((3),(4));\n"] (swap! cenv #(assoc % :js-dependency-index (deps/js-dependency-index opts))) (binding [ana/*cljs-ns* 'cljs.user] @@ -70,4 +70,4 @@ (is (= (compile '(calculator/add 3 4)) output)) (is (= (compile '(add 3 4)) output)) (is (= (compile '(sub 5 4)) - "module$src$test$cljs$calculator.subtract.call(null,(5),(4));\n"))))))) + "module$src$test$cljs$calculator.subtract((5),(4));\n"))))))) From c567440338925cb6885d04385c37f88b9611a35b Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Tue, 16 May 2017 11:11:02 +0100 Subject: [PATCH 2330/4033] CLJS-2039: remove extraneous argument from ChunkBuffer.chunk --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index df2c07643..703386c03 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3352,7 +3352,7 @@ reduces them without incurring seq initialization" (aset buf end o) (set! end (inc end))) - (chunk [_ o] + (chunk [_] (let [ret (ArrayChunk. buf 0 end)] (set! buf nil) ret)) From 314dd98467dbdb13515e4dc1e28e3845edf0f430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 26 May 2017 10:49:25 -0700 Subject: [PATCH 2331/4033] CLJS-2052: Port new spec.alpha enhancements --- src/main/cljs/cljs/spec/alpha.cljc | 16 +++-- src/main/cljs/cljs/spec/alpha.cljs | 88 +++++++++++++----------- src/test/cljs/cljs/spec_test.cljs | 103 ++++++++++++++++++----------- 3 files changed, 124 insertions(+), 83 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index f3b15ba51..40eb276c3 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -321,7 +321,7 @@ "Returns a regex op that matches zero or one value matching pred. Produces a single value (not a collection) if matched." [pred-form] - `(maybe-impl ~pred-form '~pred-form)) + `(maybe-impl ~pred-form '~(res &env pred-form))) (defmacro alt "Takes key+pred pairs, e.g. @@ -386,7 +386,7 @@ Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator." - [& {:keys [args ret fn gen]}] + [& {:keys [args ret fn gen] :or {ret `any}}] (let [env &env] `(fspec-impl (spec ~args) '~(res env args) (spec ~ret) '~(res env ret) @@ -421,7 +421,7 @@ by calling get-spec with the var or full-qualified symbol. Once registered, function specs are included in doc, checked by - instrument, tested by the runner clojure.spec.test/run-tests, and (if + instrument, tested by the runner clojure.spec.test.alpha/run-tests, and (if a macro) used to explain errors during macroexpansion. Note that :fn specs require the presence of :args and :ret specs to @@ -479,8 +479,8 @@ (gen/large-integer* {:min st# :max et#})))))) (defmacro int-in - "Returns a spec that validates longs in the range from start - (inclusive) to end (exclusive)." + "Returns a spec that validates fixed precision integers in the + range from start (inclusive) to end (exclusive)." [start end] `(spec (and c/int? #(int-in-range? ~start ~end %)) :gen #(gen/large-integer* {:min ~start :max (dec ~end)}))) @@ -529,8 +529,10 @@ `(get-spec '~(:name (resolve &env sym))) fspec) f# ~sym] - (for [args# (gen/sample (gen (:args fspec#)) ~n)] - [args# (apply f# args#)]))))) + (if-let [arg-spec# (c/and fspec# (:args fspec#))] + (for [args# (gen/sample (gen arg-spec#) ~n)] + [args# (apply f# args#)]) + (throw (js/Error. "No :args spec found, can't generate"))))))) (defmacro ^:private init-compile-asserts [] (let [compile-asserts (not (-> env/*compiler* deref :options :elide-asserts))] diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 58b0e9687..c990c2d40 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -200,7 +200,9 @@ (defn explain-data* [spec path via in x] (when-let [probs (explain* (specize spec) path via in x)] (when-not (empty? probs) - {::problems probs}))) + {::problems probs + ::spec spec + ::value x}))) (defn explain-data "Given a spec and a value x which ought to conform, returns nil if x @@ -215,32 +217,33 @@ "Default printer for explain-data. nil indicates a successful validation." [ed] (if ed - (print - (with-out-str - ;;(prn {:ed ed}) - (doseq [{:keys [path pred val reason via in] :as prob} (::problems ed)] - (when-not (empty? in) - (print "In:" (pr-str in) "")) - (print "val: ") - (pr val) - (print " fails") - (when-not (empty? via) - (print " spec:" (pr-str (last via)))) - (when-not (empty? path) - (print " at:" (pr-str path))) - (print " predicate: ") - (pr (abbrev pred)) - (when reason (print ", " reason)) - (doseq [[k v] prob] - (when-not (#{:path :pred :val :reason :via :in} k) - (print "\n\t" (pr-str k) " ") - (pr v))) - (newline)) - (doseq [[k v] ed] - (when-not (#{::problems} k) - (print (pr-str k) " ") - (pr v) - (newline))))) + (let [problems (sort-by #(- (count (:path %))) (::problems ed))] + (print + (with-out-str + ;;(prn {:ed ed}) + (doseq [{:keys [path pred val reason via in] :as prob} problems] + (when-not (empty? in) + (print "In:" (pr-str in) "")) + (print "val: ") + (pr val) + (print " fails") + (when-not (empty? via) + (print " spec:" (pr-str (last via)))) + (when-not (empty? path) + (print " at:" (pr-str path))) + (print " predicate: ") + (pr (abbrev pred)) + (when reason (print ", " reason)) + (doseq [[k v] prob] + (when-not (#{:path :pred :val :reason :via :in} k) + (print "\n\t" (pr-str k) " ") + (pr v))) + (newline)) + (doseq [[k v] ed] + (when-not (#{::problems} k) + (print (pr-str k) " ") + (pr v) + (newline)))))) (println "Success!"))) (def ^:dynamic *explain-out* explain-printer) @@ -371,7 +374,7 @@ (let [pred (maybe-spec pred)] (if (spec? pred) (explain* pred path (if-let [name (spec-name pred)] (conj via name) via) in v) - [{:path path :pred (abbrev form) :val v :via via :in in}]))) + [{:path path :pred form :val v :via via :in in}]))) (defn ^:skip-wiki map-spec-impl "Do not call this directly, use 'spec' with a map argument" @@ -417,7 +420,7 @@ [{:path path :pred 'map? :val x :via via :in in}] (let [reg (registry)] (apply concat - (when-let [probs (->> (map (fn [pred form] (when-not (pred x) (abbrev form))) + (when-let [probs (->> (map (fn [pred form] (when-not (pred x) form)) pred-exprs pred-forms) (keep identity) seq)] @@ -482,7 +485,7 @@ x)) (explain* [_ path via in x] (when (invalid? (dt pred x form cpred?)) - [{:path path :pred (abbrev form) :val x :via via :in in}])) + [{:path path :pred form :val x :via via :in in}])) (gen* [_ _ _ _] (if gfn (gfn) (gen/gen-for-pred pred))) @@ -518,7 +521,7 @@ path (conj path dv)] (if-let [pred (predx x)] (explain-1 form pred path via in x) - [{:path path :pred (abbrev form) :val x :reason "no method" :via via :in in}]))) + [{:path path :pred form :val x :reason "no method" :via via :in in}]))) (gen* [_ overrides path rmap] (if gfn (gfn) @@ -863,7 +866,15 @@ (c/or (nil? vseq) (= i limit)) x (valid? spec v) (recur (inc i) vs) :else ::invalid))))))) - (unform* [_ x] x) + (unform* [_ x] + (if conform-all + (let [spec @spec + [init add complete] (cfns x)] + (loop [ret (init x), i 0, [v & vs :as vseq] (seq x)] + (if (>= i (c/count x)) + (complete ret) + (recur (add ret i v (unform* spec v)) (inc i) vs)))) + x)) (explain* [_ path via in x] (c/or (coll-prob x kind kind-form distinct count min-count max-count path via in) @@ -1089,7 +1100,7 @@ (case op ::accept nil nil p - ::amp (list* 'clojure.spec/& (op-describe p1) forms) + ::amp (list* 'clojure.spec.alpha/& (op-describe p1) forms) ::pcat (if rep+ (list `+ rep+) (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) forms))) @@ -1106,7 +1117,7 @@ insufficient (fn [path form] [{:path path :reason "Insufficient input" - :pred (abbrev form) + :pred form :val () :via via :in in}])] @@ -1218,14 +1229,14 @@ (op-explain (op-describe p) p path via (conj in i) (seq data)) [{:path path :reason "Extra input" - :pred (abbrev (op-describe re)) + :pred (op-describe re) :val data :via via :in (conj in i)}]) (c/or (op-explain (op-describe p) p path via (conj in i) (seq data)) [{:path path :reason "Extra input" - :pred (abbrev (op-describe p)) + :pred (op-describe p) :val data :via via :in (conj in i)}])))))) @@ -1247,7 +1258,7 @@ (explain* [_ path via in x] (if (c/or (nil? x) (coll? x)) (re-explain path via in re (seq x)) - [{:path path :pred (abbrev (op-describe re)) :val x :via via :in in}])) + [{:path path :pred (op-describe re) :val x :via via :in in}])) (gen* [_ overrides path rmap] (if gfn (gfn) @@ -1389,7 +1400,8 @@ (c/and (<= (inst-ms start) t) (< t (inst-ms end)))))) (defn int-in-range? - "Return true if start <= val and val < end" + "Return true if start <= val, val < end and val is a fixed + precision integer." [start end val] (cond (integer? val) (c/and (<= start val) (< val end)) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index b90075236..4fdccd92a 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -132,9 +132,8 @@ v (s/coll-of keyword? :kind vector?) coll (s/coll-of keyword?) lrange (s/int-in 7 42) - ;drange (s/double-in :infinite? false :NaN? false :min 3.1 :max 3.2) - irange (s/inst-in #inst "1939" #inst "1946") - ] + drange (s/double-in :infinite? false :NaN? false :min 3.1 :max 3.2) + irange (s/inst-in #inst "1939" #inst "1946")] (are [spec x conformed ed] (let [co (s/conform spec x) e (::s/problems (s/explain-data spec x))] @@ -145,15 +144,15 @@ lrange 7 7 nil lrange 8 8 nil - lrange 42 ::s/invalid [{:pred '(int-in-range? 7 42 %), :val 42}] + lrange 42 ::s/invalid [{:pred '(cljs.core/fn [%] (cljs.spec.alpha/int-in-range? 7 42 %)), :val 42}] - irange #inst "1938" ::s/invalid [{:pred '(inst-in-range? #inst "1939-01-01T00:00:00.000-00:00" #inst "1946-01-01T00:00:00.000-00:00" %), :val #inst "1938"}] + irange #inst "1938" ::s/invalid [{:pred '(cljs.core/fn [%] (cljs.spec.alpha/inst-in-range? #inst "1939-01-01T00:00:00.000-00:00" #inst "1946-01-01T00:00:00.000-00:00" %)), :val #inst "1938"}] irange #inst "1942" #inst "1942" nil - irange #inst "1946" ::s/invalid [{:pred '(inst-in-range? #inst "1939-01-01T00:00:00.000-00:00" #inst "1946-01-01T00:00:00.000-00:00" %), :val #inst "1946"}] + irange #inst "1946" ::s/invalid [{:pred '(cljs.core/fn [%] (cljs.spec.alpha/inst-in-range? #inst "1939-01-01T00:00:00.000-00:00" #inst "1946-01-01T00:00:00.000-00:00" %)), :val #inst "1946"}] - ;drange 3.0 ::s/invalid [{:pred '(<= 3.1 %), :val 3.0}] - ;drange 3.1 3.1 nil - ;drange 3.2 3.2 nil + drange 3.0 ::s/invalid [{:pred '(cljs.core/fn [%] (cljs.core/<= 3.1 %)), :val 3.0}] + drange 3.1 3.1 nil + drange 3.2 3.2 nil ;drange Double/POSITIVE_INFINITY ::s/invalid [{:pred '(not (isInfinite %)), :val Double/POSITIVE_INFINITY}] ;; can't use equality-based test for Double/NaN ;; drange Double/NaN ::s/invalid {[] {:pred '(not (isNaN %)), :val Double/NaN}} @@ -163,77 +162,77 @@ keyword? "abc" ::s/invalid [{:pred ::s/unknown :val "abc"}] a 6 6 nil - a 3 ::s/invalid '[{:pred (> % 5), :val 3}] - a 20 ::s/invalid '[{:pred (< % 10), :val 20}] + a 3 ::s/invalid '[{:pred (cljs.core/fn [%] (cljs.core/> % 5)), :val 3}] + a 20 ::s/invalid '[{:pred (cljs.core/fn [%] (cljs.core/< % 10)), :val 20}] ;a nil "java.lang.NullPointerException" "java.lang.NullPointerException" ;a :k "java.lang.ClassCastException" "java.lang.ClassCastException" o "a" [:s "a"] nil o :a [:k :a] nil - o 'a ::s/invalid '[{:pred string?, :val a, :path [:s]} {:pred keyword?, :val a :path [:k]}] + o 'a ::s/invalid '[{:pred cljs.core/string?, :val a, :path [:s]} {:pred cljs.core/keyword?, :val a :path [:k]}] - c nil ::s/invalid '[{:reason "Insufficient input", :pred string?, :val (), :path [:a]}] - c [] ::s/invalid '[{:reason "Insufficient input", :pred string?, :val (), :path [:a]}] - c [:a] ::s/invalid '[{:pred string?, :val :a, :path [:a], :in [0]}] - c ["a"] ::s/invalid '[{:reason "Insufficient input", :pred keyword?, :val (), :path [:b]}] + c nil ::s/invalid '[{:reason "Insufficient input", :pred cljs.core/string?, :val (), :path [:a]}] + c [] ::s/invalid '[{:reason "Insufficient input", :pred cljs.core/string?, :val (), :path [:a]}] + c [:a] ::s/invalid '[{:pred cljs.core/string?, :val :a, :path [:a], :in [0]}] + c ["a"] ::s/invalid '[{:reason "Insufficient input", :pred cljs.core/keyword?, :val (), :path [:b]}] c ["s" :k] '{:a "s" :b :k} nil - c ["s" :k 5] ::s/invalid '[{:reason "Extra input", :pred (cat :a string? :b keyword?), :val (5)}] + c ["s" :k 5] ::s/invalid '[{:reason "Extra input", :pred (cljs.spec.alpha/cat :a cljs.core/string? :b cljs.core/keyword?), :val (5)}] (s/cat) nil {} nil - (s/cat) [5] ::s/invalid '[{:reason "Extra input", :pred (cat), :val (5), :in [0]}] + (s/cat) [5] ::s/invalid '[{:reason "Extra input", :pred (cljs.spec.alpha/cat), :val (5), :in [0]}] - either nil ::s/invalid '[{:reason "Insufficient input", :pred (alt :a string? :b keyword?), :val () :via []}] - either [] ::s/invalid '[{:reason "Insufficient input", :pred (alt :a string? :b keyword?), :val () :via []}] + either nil ::s/invalid '[{:reason "Insufficient input", :pred (cljs.spec.alpha/alt :a cljs.core/string? :b cljs.core/keyword?), :val () :via []}] + either [] ::s/invalid '[{:reason "Insufficient input", :pred (cljs.spec.alpha/alt :a cljs.core/string? :b cljs.core/keyword?), :val () :via []}] either [:k] [:b :k] nil either ["s"] [:a "s"] nil - either [:b "s"] ::s/invalid '[{:reason "Extra input", :pred (alt :a string? :b keyword?), :val ("s") :via []}] + either [:b "s"] ::s/invalid '[{:reason "Extra input", :pred (cljs.spec.alpha/alt :a cljs.core/string? :b cljs.core/keyword?), :val ("s") :via []}] star nil [] nil star [] [] nil star [:k] [:k] nil star [:k1 :k2] [:k1 :k2] nil - star [:k1 :k2 "x"] ::s/invalid '[{:pred keyword?, :val "x" :via []}] - star ["a"] ::s/invalid '[{:pred keyword?, :val "a" :via []}] + star [:k1 :k2 "x"] ::s/invalid '[{:pred cljs.core/keyword?, :val "x" :via []}] + star ["a"] ::s/invalid '[{:pred cljs.core/keyword?, :val "a" :via []}] - plus nil ::s/invalid '[{:reason "Insufficient input", :pred keyword?, :val () :via []}] - plus [] ::s/invalid '[{:reason "Insufficient input", :pred keyword?, :val () :via []}] + plus nil ::s/invalid '[{:reason "Insufficient input", :pred cljs.core/keyword?, :val () :via []}] + plus [] ::s/invalid '[{:reason "Insufficient input", :pred cljs.core/keyword?, :val () :via []}] plus [:k] [:k] nil plus [:k1 :k2] [:k1 :k2] nil - plus [:k1 :k2 "x"] ::s/invalid '[{:pred keyword?, :val "x", :in [2]}] - plus ["a"] ::s/invalid '[{:pred keyword?, :val "a" :via []}] + plus [:k1 :k2 "x"] ::s/invalid '[{:pred cljs.core/keyword?, :val "x", :in [2]}] + plus ["a"] ::s/invalid '[{:pred cljs.core/keyword?, :val "a" :via []}] opt nil nil nil opt [] nil nil - opt :k ::s/invalid '[{:pred (? keyword?), :val :k}] + opt :k ::s/invalid '[{:pred (cljs.spec.alpha/? cljs.core/keyword?), :val :k}] opt [:k] :k nil - opt [:k1 :k2] ::s/invalid '[{:reason "Extra input", :pred (? keyword?), :val (:k2)}] - opt [:k1 :k2 "x"] ::s/invalid '[{:reason "Extra input", :pred (? keyword?), :val (:k2 "x")}] - opt ["a"] ::s/invalid '[{:pred keyword?, :val "a"}] + opt [:k1 :k2] ::s/invalid '[{:reason "Extra input", :pred (cljs.spec.alpha/? cljs.core/keyword?), :val (:k2)}] + opt [:k1 :k2 "x"] ::s/invalid '[{:reason "Extra input", :pred (cljs.spec.alpha/? cljs.core/keyword?), :val (:k2 "x")}] + opt ["a"] ::s/invalid '[{:pred cljs.core/keyword?, :val "a"}] andre nil nil nil andre [] nil nil - andre :k ::s/invalid '[{:pred (& (* keyword?) even-count?), :val :k}] - andre [:k] ::s/invalid '[{:pred even-count?, :val [:k]}] + andre :k ::s/invalid '[{:pred (clojure.spec.alpha/& (cljs.spec.alpha/* cljs.core/keyword?) cljs.spec-test/even-count?), :val :k}] + andre [:k] ::s/invalid '[{:pred cljs.spec-test/even-count?, :val [:k]}] andre [:j :k] [:j :k] nil - m nil ::s/invalid '[{:pred map?, :val nil}] + m nil ::s/invalid '[{:pred cljs.core/map?, :val nil}] m {} {} nil m {:a "b"} {:a "b"} nil - mkeys nil ::s/invalid '[{:pred map?, :val nil}] + mkeys nil ::s/invalid '[{:pred cljs.core/map?, :val nil}] mkeys {} {} nil mkeys {:a 1 :b 2} {:a 1 :b 2} nil - mkeys2 nil ::s/invalid '[{:pred map?, :val nil}] + mkeys2 nil ::s/invalid '[{:pred cljs.core/map?, :val nil}] mkeys2 {} {} nil mkeys2 {:a 1 :b 2} {"a" 1 "b" 2} nil s '([:a 1] [:b "2"]) '({:tag :a :val 1} {:tag :b :val "2"}) nil v [:a :b] [:a :b] nil - v '(:a :b) ::s/invalid '[{:pred vector? :val (:a :b)}] + v '(:a :b) ::s/invalid '[{:pred cljs.core/vector? :val (:a :b)}] - coll nil ::s/invalid '[{:path [], :pred coll?, :val nil, :via [], :in []}] + coll nil ::s/invalid '[{:path [], :pred cljs.core/coll?, :val nil, :via [], :in []}] coll [] [] nil coll [:a] [:a] nil coll [:a :b] [:a :b] nil @@ -265,6 +264,34 @@ (s/coll-of int? :gen #(gen/return [1 2])) '(cljs.spec.alpha/coll-of cljs.core/int? :gen (fn* [] (gen/return [1 2]))))) +(defn check-conform-unform [spec vals expected-conforms] + (let [actual-conforms (map #(s/conform spec %) vals) + unforms (map #(s/unform spec %) actual-conforms)] + (is (= actual-conforms expected-conforms)) + (is (= vals unforms)))) + +(deftest coll-conform-unform + (check-conform-unform + (s/coll-of (s/or :i int? :s string?)) + [[1 "x"]] + [[[:i 1] [:s "x"]]]) + (check-conform-unform + (s/every (s/or :i int? :s string?)) + [[1 "x"]] + [[1 "x"]]) + (check-conform-unform + (s/map-of int? (s/or :i int? :s string?)) + [{10 10 20 "x"}] + [{10 [:i 10] 20 [:s "x"]}]) + (check-conform-unform + (s/map-of (s/or :i int? :s string?) int? :conform-keys true) + [{10 10 "x" 20}] + [{[:i 10] 10 [:s "x"] 20}]) + (check-conform-unform + (s/every-kv int? (s/or :i int? :s string?)) + [{10 10 20 "x"}] + [{10 10 20 "x"}])) + (comment (run-tests) From 0bb257c45793354c55ee2b411816afd1de4bd0ee Mon Sep 17 00:00:00 2001 From: ewen Date: Mon, 9 Jan 2017 23:54:58 +0100 Subject: [PATCH 2332/4033] 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 --- src/main/cljs/cljs/core.cljs | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 703386c03..90400aa66 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1637,7 +1637,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -3115,7 +3115,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (Cons. nil o coll nil)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) ISequential IEquiv @@ -3319,7 +3319,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) ISequential IEquiv @@ -3460,7 +3460,7 @@ reduces them without incurring seq initialization" (cons o this)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash))) @@ -4772,12 +4772,12 @@ reduces them without incurring seq initialization" ([to from] (if-not (nil? to) (if (implements? IEditableCollection to) - (with-meta (persistent! (reduce -conj! (transient to) from)) (meta to)) + (-with-meta (persistent! (reduce -conj! (transient to) from)) (meta to)) (reduce -conj to from)) (reduce conj () from))) ([to xform from] (if (implements? IEditableCollection to) - (with-meta (persistent! (transduce xform conj! (transient to) from)) (meta to)) + (-with-meta (persistent! (transduce xform conj! (transient to) from)) (meta to)) (transduce xform conj to from)))) (defn mapv @@ -5081,7 +5081,7 @@ reduces them without incurring seq initialization" (PersistentVector. meta (inc cnt) new-shift new-root (array o) nil)))) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY PersistentVector) meta)) + (-empty [coll] (-with-meta (.-EMPTY PersistentVector) meta)) ISequential IEquiv @@ -5312,7 +5312,7 @@ reduces them without incurring seq initialization" IEmptyableCollection (-empty [coll] - (with-meta (.-EMPTY PersistentVector) meta)) + (-with-meta (.-EMPTY PersistentVector) meta)) IChunkedSeq (-chunked-first [coll] @@ -5386,7 +5386,7 @@ reduces them without incurring seq initialization" (build-subvec meta (-assoc-n v end o) start (inc end) nil)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY PersistentVector) meta)) + (-empty [coll] (-with-meta (.-EMPTY PersistentVector) meta)) ISequential IEquiv @@ -5732,7 +5732,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) ISequential IEquiv @@ -5794,7 +5794,7 @@ reduces them without incurring seq initialization" (PersistentQueue. meta (inc count) (conj front o) [] nil))) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY PersistentQueue) meta)) + (-empty [coll] (-with-meta (.-EMPTY PersistentQueue) meta)) ISequential IEquiv @@ -5870,7 +5870,7 @@ reduces them without incurring seq initialization" (if (< i len) (let [k (aget ks i)] (recur (inc i) (assoc! out k (aget so k)))) - (with-meta (persistent! (assoc! out k v)) mm))))) + (-with-meta (persistent! (assoc! out k v)) mm))))) ;;; ObjMap - DEPRECATED @@ -5906,7 +5906,7 @@ reduces them without incurring seq initialization" entry))) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY ObjMap) meta)) + (-empty [coll] (-with-meta (.-EMPTY ObjMap) meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -6246,7 +6246,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) _meta)) + (-empty [coll] (-with-meta (.-EMPTY List) _meta)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -7175,7 +7175,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) ISequential ISeq @@ -7247,7 +7247,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) ISequential ISeq @@ -7626,7 +7626,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) meta)) + (-empty [coll] (-with-meta (.-EMPTY List) meta)) IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) @@ -7804,7 +7804,7 @@ reduces them without incurring seq initialization" IWithMeta (-with-meta [node meta] - (with-meta [key val] meta)) + (-with-meta [key val] meta)) IStack (-peek [node] val) @@ -7965,7 +7965,7 @@ reduces them without incurring seq initialization" IWithMeta (-with-meta [node meta] - (with-meta [key val] meta)) + (-with-meta [key val] meta)) IStack (-peek [node] val) @@ -8377,7 +8377,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) _meta)) + (-empty [coll] (-with-meta (.-EMPTY List) _meta)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -8453,7 +8453,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY List) _meta)) + (-empty [coll] (-with-meta (.-EMPTY List) _meta)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -8532,7 +8532,7 @@ reduces them without incurring seq initialization" (assoc ret key entry) ret) (next keys))) - (with-meta ret (meta map))))) + (-with-meta ret (meta map))))) ;;; PersistentHashSet @@ -8586,7 +8586,7 @@ reduces them without incurring seq initialization" (PersistentHashSet. meta (assoc hash-map o nil) nil)) IEmptyableCollection - (-empty [coll] (with-meta (.-EMPTY PersistentHashSet) meta)) + (-empty [coll] (-with-meta (.-EMPTY PersistentHashSet) meta)) IEquiv (-equiv [coll other] @@ -9084,7 +9084,7 @@ reduces them without incurring seq initialization" (-conj [rng o] (cons o rng)) IEmptyableCollection - (-empty [rng] (with-meta (.-EMPTY List) meta)) + (-empty [rng] (-with-meta (.-EMPTY List) meta)) ISequential IEquiv From 7cbbff5b0150b13f1134074a2e51fd1467225903 Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Tue, 2 May 2017 11:03:20 -0700 Subject: [PATCH 2333/4033] CLJS-2021: subvec throws when passed non-vector --- src/main/cljs/cljs/core.cljs | 15 +++++++++------ src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 90400aa66..53aca9d95 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5476,12 +5476,15 @@ reduces them without incurring seq initialization" (defn- build-subvec [meta v start end __hash] (if (instance? Subvec v) (recur meta (.-v v) (+ (.-start v) start) (+ (.-start v) end) __hash) - (let [c (count v)] - (when (or (neg? start) - (neg? end) - (> start c) - (> end c)) - (throw (js/Error. "Index out of bounds"))) + (do + (when-not (vector? v) + (throw (js/Error. "v must satisfy IVector"))) + (let [c (count v)] + (when (or (neg? start) + (neg? end) + (> start c) + (> end c)) + (throw (js/Error. "Index out of bounds")))) (Subvec. meta v start end __hash)))) (defn subvec diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 0d3945755..994e17fd2 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1262,6 +1262,13 @@ (case (swap! x m) :a 0 :default) (is (= :b @x))))) +(deftest test-cljs-2021 + (let [check-if-throws #(try (%) (catch js/Error e :fail))] + (is (= :fail (check-if-throws #(subvec nil 0 0)))) + (is (= :fail (check-if-throws #(subvec {:foo :bar} 0 1)))) + (is (= :fail (check-if-throws #(subvec '(:foo) 0 1)))) + (is (= :fail (check-if-throws #(subvec #{:foo} 0 1)))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 7e3dce6e48ce05c3020b9284cbb6cb57cb479316 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 26 May 2017 17:01:21 -0400 Subject: [PATCH 2334/4033] bump Google Closure Library dependency --- pom.template.xml | 4 ++-- project.clj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 9f73eb9f1..7d2444c77 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20160609-f42b4a24 + 0.0-20170519-fa0499ef org.clojure @@ -67,7 +67,7 @@ com.cognitect transit-clj - 0.8.285 + 0.8.300 provided diff --git a/project.clj b/project.clj index f4827f87c..2cc375476 100644 --- a/project.clj +++ b/project.clj @@ -12,8 +12,8 @@ [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-beta3"] [org.clojure/test.check "0.9.0" :scope "test"] - [com.cognitect/transit-clj "0.8.285"] - [org.clojure/google-closure-library "0.0-20160609-f42b4a24"] + [com.cognitect/transit-clj "0.8.300"] + [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] [com.google.javascript/closure-compiler-unshaded "v20170423"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} From 89faeec52acc2de8905206f079af6ea403994fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 26 May 2017 14:13:26 -0700 Subject: [PATCH 2335/4033] CLJS-2053: Regression: cljs.spec.alpha/any for fdef --- src/main/cljs/cljs/spec/alpha.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 40eb276c3..54903f1b7 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -386,7 +386,7 @@ Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator." - [& {:keys [args ret fn gen] :or {ret `any}}] + [& {:keys [args ret fn gen] :or {ret `any?}}] (let [env &env] `(fspec-impl (spec ~args) '~(res env args) (spec ~ret) '~(res env ret) From ccebc81419a9611e9521c2741c69851eebf327c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 22 May 2017 19:48:38 -0700 Subject: [PATCH 2336/4033] CLJS-2027: Add language-in for ECMA 2017 and ECMA Next Also upgrades Google Closure Compiler to the May 2017 release which contains the `CompilerOptions$LanguageMode/ECMASCRIPT_2017` enum. --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 7d2444c77..9bdeb37a7 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20170423 + v20170521 org.clojure diff --git a/project.clj b/project.clj index 2cc375476..4ee4e40ed 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] - [com.google.javascript/closure-compiler-unshaded "v20170423"] + [com.google.javascript/closure-compiler-unshaded "v20170521"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 792bd570e..7987535f6 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,7 +5,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha16" SPEC_ALPHA_RELEASE="0.1.108" CORE_SPECS_ALPHA_RELEASE="0.1.10" -CLOSURE_RELEASE="20170423" +CLOSURE_RELEASE="20170521" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1bac4d0e4..903d3c27d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -193,6 +193,8 @@ (case key :no-transpile CompilerOptions$LanguageMode/NO_TRANSPILE (:ecmascript6 :es6) CompilerOptions$LanguageMode/ECMASCRIPT6 + (:ecmascript-2017 :es-2017) CompilerOptions$LanguageMode/ECMASCRIPT_2017 + (:ecmascript-next :es-next) CompilerOptions$LanguageMode/ECMASCRIPT_NEXT (:ecmascript6-strict :es6-strict) CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT (:ecmascript6-typed :es6-typed) CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED (:ecmascript5 :es5) CompilerOptions$LanguageMode/ECMASCRIPT5 From 61805334a521df6b865650741046e29bfda35b72 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 26 May 2017 17:29:23 -0400 Subject: [PATCH 2337/4033] qualify any? for self-host --- src/main/cljs/cljs/spec/alpha.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 54903f1b7..4550acdc3 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -386,7 +386,7 @@ Optionally takes :gen generator-fn, which must be a fn of no args that returns a test.check generator." - [& {:keys [args ret fn gen] :or {ret `any?}}] + [& {:keys [args ret fn gen] :or {ret `cljs.core/any?}}] (let [env &env] `(fspec-impl (spec ~args) '~(res env args) (spec ~ret) '~(res env ret) From 06c1b9b5bbb514d34fdc0193a730467e89f73bb8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 26 May 2017 17:48:21 -0400 Subject: [PATCH 2338/4033] 1.9.562 --- README.md | 6 +++--- changes.md | 27 +++++++++++++++++++++++++++ src/main/clojure/cljs/core.cljc | 2 +- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f43f97596..c2a23f7ea 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.542 +Latest stable release: 1.9.562 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.542"] +[org.clojure/clojurescript "1.9.562"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.542 org.clojure clojurescript - 1.9.542 + 1.9.562 ``` diff --git a/changes.md b/changes.md index 986edfd7b..77e126f61 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,30 @@ +## 1.9.562 + +### Enhancements +* CLJS-2027: Add language-in for ECMA 2017 and ECMA Next +* CLJS-2026: Add Compiler option for rewrite polyfills + +### Changes +* CLJS-2021: subvec throws when passed non-vector +* 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 +* Fix assumption that all closure-compliant JS is goog.* +* CLJS-2035: Self-host: Add map-entry-test to self-parity +* CLJS-2033: set-validator! should check current state +* CLJS-2008: Self-host: backport fixes to threading macros +* CLJS-2005: Bad error message with duplicate arity function definitions +* CLJS-2032: Case macro expansion evaluates expression twice when no matching clause +* CLJS-2023: User supplied type hints stopped working on js/goog.DEBUG +* CLJS-2020: defmulti "miss" performance poor +* CLJS-2034: Sequence and Eduction produce infinite loop in transducer that appends to the reduction + ## 1.9.542 ### Enhancements diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index de0c46220..35cf48dea 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1783,7 +1783,7 @@ (new ~tagname ~@(remove #{'__extmap '__hash} fields) (assoc ~'__extmap k# ~gs) nil))) 'IMap `(~'-dissoc [this# k#] (if (contains? #{~@(map keyword base-fields)} k#) - (dissoc (with-meta (into {} this#) ~'__meta) k#) + (dissoc (-with-meta (into {} this#) ~'__meta) k#) (new ~tagname ~@(remove #{'__extmap '__hash} fields) (not-empty (dissoc ~'__extmap k#)) nil))) From dd7403f8f513c1e774b9a65cade2037451fe7565 Mon Sep 17 00:00:00 2001 From: rauhs Date: Wed, 31 May 2017 21:12:09 +0200 Subject: [PATCH 2339/4033] CLJS-2065: Improve analyzer munge performance Optimize the munge by improving shadow-depth: - Avoid slow string/split - Do a fast kv-reduce over the namespaces and stop as soon as the namespace is found. Add parameter to find-ns-starts-with - Extract get-first-ns-segmet for possible use in other code. --- src/main/clojure/cljs/compiler.cljc | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a1d62edd3..1e6533793 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -43,18 +43,33 @@ (def cljs-reserved-file-names #{"deps.cljs"}) -(defn ns-first-segments [] - (letfn [(get-first-ns-segment [ns] (first (string/split (str ns) #"\.")))] - (map get-first-ns-segment (keys (::ana/namespaces @env/*compiler*))))) +(defn get-first-ns-segment + "Gets the part up to the first `.` of a namespace. + Returns the empty string for nil. + Returns the entire string if no `.` in namespace" + [ns] + (let [ns (str ns) + idx (.indexOf ns ".")] + (if (== -1 idx) + ns + (subs ns 0 idx)))) + +(defn find-ns-starts-with [needle] + (reduce-kv + (fn [xs ns _] + (when (= needle (get-first-ns-segment ns)) + (reduced needle))) + nil + (::ana/namespaces @env/*compiler*))) ; Helper fn (defn shadow-depth [s] (let [{:keys [name info]} s] (loop [d 0, {:keys [shadow]} info] (cond - shadow (recur (inc d) shadow) - (some #{(str name)} (ns-first-segments)) (inc d) - :else d)))) + shadow (recur (inc d) shadow) + (find-ns-starts-with (str name)) (inc d) + :else d)))) (defn hash-scope [s] #?(:clj (System/identityHashCode s) From 3cf1db168c716fb404a466e90cb2affe6fc6865b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 1 Jun 2017 14:23:56 -0400 Subject: [PATCH 2340/4033] CLJS-2066: Avoid analyzing named fn literal bodies twice If a function literal is named, don't analyze method bodies on the first pass. The results of the first pass in this case are only used for arity analysis. --- src/main/clojure/cljs/analyzer.cljc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index db32f76a2..74c370cf9 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1450,7 +1450,7 @@ (binding [*recur-frames* recur-frames] (analyze env form))) -(defn- analyze-fn-method [env locals form type] +(defn- analyze-fn-method [env locals form type analyze-body?] (let [param-names (first form) variadic (boolean (some '#{&} param-names)) param-names (vec (remove '#{&} param-names)) @@ -1466,7 +1466,8 @@ recur-frames (cons recur-frame *recur-frames*) body-env (assoc env :context :return :locals locals) body-form `(do ~@body) - expr (analyze-fn-method-body body-env body-form recur-frames) + expr (when analyze-body? + (analyze-fn-method-body body-env body-form recur-frames)) recurs @(:flag recur-frame)] {:env env :variadic variadic @@ -1497,7 +1498,7 @@ (merge name-var ret-tag)))) (defn analyze-fn-methods-pass2* [menv locals type meths] - (doall (map #(analyze-fn-method menv locals % type) meths))) + (doall (map #(analyze-fn-method menv locals % type true) meths))) (defn analyze-fn-methods-pass2 [menv locals type meths] (no-warn (analyze-fn-methods-pass2* menv locals type meths))) @@ -1531,7 +1532,7 @@ menv (merge menv {:protocol-impl proto-impl :protocol-inline proto-inline}) - methods (map #(disallowing-ns* (analyze-fn-method menv locals % type)) meths) + methods (map #(disallowing-ns* (analyze-fn-method menv locals % type (nil? name))) meths) mfa (apply max (map :max-fixed-arity methods)) variadic (boolean (some :variadic methods)) locals (if named-fn? From 7a3ab0fb246a436f6b4c0f39457f05722e53a54f Mon Sep 17 00:00:00 2001 From: rauhs Date: Fri, 2 Jun 2017 17:32:07 +0200 Subject: [PATCH 2341/4033] CLJS-2067: reduce-kv / inode-kv-reduce fails to honor reduced? Do not return the value from Reduced object from `.kv-reduce` functions but instead return the `Reduced` object. Thus, the kv-reduce correctly honors the reduced state. --- src/main/cljs/cljs/core.cljs | 6 +++--- src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 53aca9d95..fe3605643 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6644,7 +6644,7 @@ reduces them without incurring seq initialization" (.kv-reduce node f init) init))))] (if (reduced? init) - @init + init (recur (+ i 2) init))) init)))) @@ -7015,7 +7015,7 @@ reduces them without incurring seq initialization" (if-not (nil? node) (let [init (.kv-reduce node f init)] (if (reduced? init) - @init + init (recur (inc i) init))) (recur (inc i) init))) init)))) @@ -7426,7 +7426,7 @@ reduces them without incurring seq initialization" (let [init (if has-nil? (f init nil nil-val) init)] (cond (reduced? init) @init - (not (nil? root)) (.kv-reduce root f init) + (not (nil? root)) (unreduced (.kv-reduce root f init)) :else init))) IFn diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 994e17fd2..84373b5c3 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -620,6 +620,13 @@ (is (= (str (ex-info "abc" {:x 1} "def")) "#error {:message \"abc\", :data {:x 1}, :cause \"def\"}")) (is (not (instance? cljs.core.ExceptionInfo (js/Error.))))) +(deftest test-2067 + (is (= 0 (reduce-kv + (fn [x k _] + (when (zero? k) + (reduced k))) + nil (zipmap (range 17) (repeat 0)))))) + (deftest test-435 (is (= (assoc {} 154618822656 1 261993005056 1) {154618822656 1 261993005056 1}))) From e78de4720c56da14bb63cf9e490165ffab0dcbaf Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 28 May 2017 17:14:13 -0400 Subject: [PATCH 2342/4033] CLJS-2056: Self-host: test-self-parity failing wrt cljs.core/fn symbol --- src/main/cljs/cljs/spec/alpha.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 4550acdc3..ee4d16a4a 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -30,7 +30,7 @@ (symbol? (first expr)) (= "fn*" (name (first expr)))) (let [[[s] & form] (rest expr)] - (conj (walk/postwalk-replace {s '%} form) '[%] 'fn)) + (conj (walk/postwalk-replace {s '%} form) '[%] 'cljs.core/fn)) expr)) (defn- res [env form] From 35090a749ef645b85633e1de5c389eb520dac443 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Jun 2017 13:47:39 -0400 Subject: [PATCH 2343/4033] tweak JVM memory settings in cljsc --- bin/cljsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cljsc b/bin/cljsc index 82e3ac48c..6f2bbb469 100755 --- a/bin/cljsc +++ b/bin/cljsc @@ -17,5 +17,5 @@ then echo 'Usage: cljsc ' echo ' cljsc "{:optimizations :advanced}"' else - java -server -Xms1g -Xmx2g -cp "$CLJSC_CP" clojure.main "$CLOJURESCRIPT_HOME/bin/cljsc.clj" "$@" + java -server -Xms2g -Xmx4g -cp "$CLJSC_CP" clojure.main "$CLOJURESCRIPT_HOME/bin/cljsc.clj" "$@" fi From d4db18970c8eec587b2c9e022034983e29eb8e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 3 Jun 2017 15:10:40 -0700 Subject: [PATCH 2344/4033] CLJS-2069: Self-host: automatic `clojure` -> `cljs` aliasing doesn't work when loading macro namespaces --- src/main/cljs/cljs/js.cljs | 66 ++++++++++++++-------- src/test/cljs/cljs/clojure_alias_test.cljs | 2 + 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index ba6319782..5b808a31b 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -362,7 +362,7 @@ (require bound-vars dep reload opts' (fn [res] (when (:verbose opts) - (debug-prn "Loading result: " res)) + (debug-prn "Loading result:" res)) (if-not (:error res) (load-deps bound-vars ana-env lib (next deps) nil opts cb) (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] @@ -428,16 +428,29 @@ k (or (reload k) (get-in reloads [k nsym]) (and (= nsym name) (:*reload-macros* bound-vars) :reload) - nil)] - (require bound-vars nsym k - (-> opts - (assoc :macros-ns true) - (dissoc :context) - (dissoc :ns)) + nil) + opts' (-> opts + (assoc :macros-ns true) + (dissoc :context) + (dissoc :ns))] + (require bound-vars nsym k opts' (fn [res] (if-not (:error res) (load-macros bound-vars k (next macros) reload reloads opts cb) - (cb res))))) + (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns nsym)] + (get {nsym nil} cljs-ns cljs-ns))] + (require bound-vars cljs-dep k opts' + (fn [res] + (if (:error res) + (cb res) + (do + (patch-alias-map (:*compiler* bound-vars) lib nsym cljs-dep) + (load-macros bound-vars k (next macros) reload reloads opts + (fn [res] + (if (:error res) + (cb res) + (cb (update res :aliased-loads assoc nsym cljs-dep))))))))) + (cb res)))))) (cb {:value nil}))) (defn- rewrite-ns-ast @@ -453,7 +466,9 @@ {} m)))] (-> ast (update :uses #(walk/postwalk-replace smap %)) + (update :use-macros #(walk/postwalk-replace smap %)) (update :requires #(merge smap (walk/postwalk-replace smap %))) + (update :require-macros #(merge smap (walk/postwalk-replace smap %))) (update :renames rewrite-renames) (update :rename-macros rewrite-renames)))) @@ -479,7 +494,7 @@ (if (#{:ns :ns*} op) (letfn [(check-uses-and-load-macros [res rewritten-ast] (let [env (:*compiler* bound-vars) - {:keys [uses requires require-macros use-macros reload reloads]} rewritten-ast] + {:keys [uses require-macros use-macros reload reloads]} rewritten-ast] (if (:error res) (cb res) (if (:*load-macros* bound-vars) @@ -489,23 +504,26 @@ (fn [res] (if (:error res) (cb res) - (do + (let [{:keys [require-macros] :as rewritten-ast} (rewrite-ns-ast rewritten-ast (:aliased-loads res))] (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) (load-macros bound-vars :require-macros require-macros reload reloads opts - (fn [res] - (if (:error res) - (cb res) - (let [res (try - (when (seq use-macros) - (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) - (ana/check-use-macros use-macros env)) - {:value nil} - (catch :default cause - (wrap-error - (ana/error ana-env - (str "Could not parse ns form " (:name ast)) cause))))] - (if (:error res) - (cb res) + (fn [res'] + (if (:error res') + (cb res') + (let [{:keys [require-macros use-macros] :as rewritten-ast} (rewrite-ns-ast rewritten-ast (:aliased-loads res)) + res' (try + (when (seq use-macros) + (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) + (binding [ana/*analyze-deps* (:*analyze-deps* bound-vars) + env/*compiler* (:*compiler* bound-vars)] + (ana/check-use-macros use-macros env))) + {:value nil} + (catch :default cause + (wrap-error + (ana/error ana-env + (str "Could not parse ns form " (:name ast)) cause))))] + (if (:error res') + (cb res') (try (binding [ana/*analyze-deps* (:*analyze-deps* bound-vars) env/*compiler* (:*compiler* bound-vars)] diff --git a/src/test/cljs/cljs/clojure_alias_test.cljs b/src/test/cljs/cljs/clojure_alias_test.cljs index 3f1ee5f40..7da390141 100644 --- a/src/test/cljs/cljs/clojure_alias_test.cljs +++ b/src/test/cljs/cljs/clojure_alias_test.cljs @@ -9,6 +9,8 @@ (ns cljs.clojure-alias-test "Tests requiring via `clojure.*` instead of `cljs.*`" (:refer-clojure :exclude [use-macros]) + (:require-macros clojure.spec.gen.alpha) + (:use-macros [clojure.analyzer.macros :only [no-warn]]) (:require [clojure.test :refer [deftest is] :rename {is is?}] [clojure.spec.alpha :as s :refer [spec? spec] :rename {spec foo}])) From 3c6d41f87145b767b4b80db75c12078c97404b3f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 7 Jun 2017 03:16:09 -0400 Subject: [PATCH 2345/4033] fix browser REPL regression - suppress undeclared warning for loaded fully namespaced vars --- src/main/clojure/cljs/repl/browser.clj | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 9c708de81..cbf2ccbb3 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -17,7 +17,8 @@ [cljs.closure :as cljsc] [cljs.repl :as repl] [cljs.repl.server :as server] - [cljs.stacktrace :as st]) + [cljs.stacktrace :as st] + [cljs.analyzer :as ana]) (:import [java.util.regex Pattern] [java.util.concurrent Executors])) @@ -140,13 +141,16 @@ (defmethod handle-post :ready [_ conn _] (send-via es ordering (fn [_] {:expecting nil :fns {}})) (send-for-eval conn - (cljsc/-compile - '[(set! *print-fn* clojure.browser.repl/repl-print) - (set! *print-err-fn* clojure.browser.repl/repl-print) - (set! *print-newline* true) - (when (pos? (count clojure.browser.repl/print-queue)) - (clojure.browser.repl/flush-print-queue! - @clojure.browser.repl/xpc-connection))] {}) + (binding [ana/*cljs-warnings* + (assoc ana/*cljs-warnings* + :undeclared-var false)] + (cljsc/-compile + '[(set! *print-fn* clojure.browser.repl/repl-print) + (set! *print-err-fn* clojure.browser.repl/repl-print) + (set! *print-newline* true) + (when (pos? (count clojure.browser.repl/print-queue)) + (clojure.browser.repl/flush-print-queue! + @clojure.browser.repl/xpc-connection))] {})) identity)) (defn add-in-order [{:keys [expecting fns]} order f] From f3f39ab3d94d5fc429fb6b6219166c5955ae2d54 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Mon, 5 Jun 2017 17:41:52 +0100 Subject: [PATCH 2346/4033] CLJS-2075: PersistentTreeMap.reduce-kv does not honor reduced? --- src/main/cljs/cljs/core.cljs | 15 ++++++--------- src/test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fe3605643..365bad710 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7742,16 +7742,13 @@ reduces them without incurring seq initialization" (tree-map-kv-reduce (.-left node) f init) init)] (if (reduced? init) - @init + init (let [init (f init (.-key node) (.-val node))] (if (reduced? init) - @init - (let [init (if-not (nil? (.-right node)) - (tree-map-kv-reduce (.-right node) f init) - init)] - (if (reduced? init) - @init - init))))))) + init + (if-not (nil? (.-right node)) + (tree-map-kv-reduce (.-right node) f init) + init)))))) (deftype BlackNode [key val left right ^:mutable __hash] Object @@ -8210,7 +8207,7 @@ reduces them without incurring seq initialization" IKVReduce (-kv-reduce [coll f init] (if-not (nil? tree) - (tree-map-kv-reduce tree f init) + (unreduced (tree-map-kv-reduce tree f init)) init)) IFn diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 84373b5c3..d3b31ed20 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1276,6 +1276,11 @@ (is (= :fail (check-if-throws #(subvec '(:foo) 0 1)))) (is (= :fail (check-if-throws #(subvec #{:foo} 0 1)))))) +(deftest test-cljs-2075 + (testing "PersistentTreeMap kv-reduce should honor reduced" + (let [sm (sorted-map 1 1, 2 2, 3 3, 4 4, 5 5, 6 6, 7 7)] + (is (= [1 2 3 4] (reduce-kv (fn [m k v] (if (= 5 k) (reduced m) (conj m k))) [] sm)))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 4cf7ac21167adcbbbe46d81d6c82e3e145a0b164 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Jun 2017 19:40:34 +0200 Subject: [PATCH 2347/4033] find-entry -> find-entries, prep for wildcard :modules entry --- src/main/clojure/cljs/closure.clj | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 903d3c27d..b01fc23cc 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1086,6 +1086,16 @@ :depends-on #{:core}}})) ) +(defn find-entries [sources entry] + #{(some + (fn [source] + (let [matcher + (into #{} + [(name entry) (name (comp/munge entry))])] + (when (some matcher (:provides source)) + source))) + sources)}) + (defn build-modules "Given a list of IJavaScript sources in dependency order and compiler options return a dependency sorted list of module name / description tuples. The @@ -1094,16 +1104,7 @@ a :foreign-deps vector containing foreign IJavaScript sources in dependency order." [sources opts] - (let [find-entry (fn [sources entry] - (some - (fn [source] - (let [matcher - (into #{} - [(name entry) (name (comp/munge entry))])] - (when (some matcher (:provides source)) - source))) - sources)) - used (atom {}) + (let [used (atom {}) [sources' modules] (reduce (fn [[sources ret] [name {:keys [entries output-to depends-on] :as module-desc}]] @@ -1117,10 +1118,10 @@ ;; as well as sources difference (reduce (fn [[sources ret] entry-sym] - (if-let [entry (find-entry sources entry-sym)] + (if-let [entries (find-entries sources entry-sym)] (do (swap! used assoc entry-sym name) - [(remove #{entry} sources) (conj ret entry)]) + [(remove entries sources) (into ret entries)]) (if (contains? @used entry-sym) (throw (IllegalArgumentException. From 4b68cf21f395f8ac2ba1a6425d7ed9fc8d9e4d1e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Jun 2017 19:47:31 +0200 Subject: [PATCH 2348/4033] comment out bad :use-macros for now from cljs.closure-alias-test --- src/test/cljs/cljs/clojure_alias_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/cljs/cljs/clojure_alias_test.cljs b/src/test/cljs/cljs/clojure_alias_test.cljs index 7da390141..aa5ca7150 100644 --- a/src/test/cljs/cljs/clojure_alias_test.cljs +++ b/src/test/cljs/cljs/clojure_alias_test.cljs @@ -10,7 +10,7 @@ "Tests requiring via `clojure.*` instead of `cljs.*`" (:refer-clojure :exclude [use-macros]) (:require-macros clojure.spec.gen.alpha) - (:use-macros [clojure.analyzer.macros :only [no-warn]]) + ;(:use-macros [clojure.analyzer.macros :only [no-warn]]) (:require [clojure.test :refer [deftest is] :rename {is is?}] [clojure.spec.alpha :as s :refer [spec? spec] :rename {spec foo}])) From 89cd5ecc64d2b29fc5b87b62ca4b0250d5aacc05 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Jun 2017 11:15:56 +0200 Subject: [PATCH 2349/4033] CLJS-2076: modules should support wildcard namespaces --- src/main/clojure/cljs/closure.clj | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b01fc23cc..8c02a1872 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1087,14 +1087,24 @@ ) (defn find-entries [sources entry] - #{(some - (fn [source] - (let [matcher - (into #{} - [(name entry) (name (comp/munge entry))])] - (when (some matcher (:provides source)) - source))) - sources)}) + (let [m (name (comp/munge entry)) + xs (string/split m #"\.")] + (if (= "_STAR_" (last xs)) + (let [matcher (str (string/join "." (butlast xs)) ".")] + (into #{} + (filter + (fn [source] + (when (some #(.startsWith ^String % matcher) (:provides source)) + source))) + sources)) + #{(some + (fn [source] + (let [matcher + (into #{} + [(name entry) (name (comp/munge entry))])] + (when (some matcher (:provides source)) + source))) + sources)}))) (defn build-modules "Given a list of IJavaScript sources in dependency order and compiler options From 3438f205b6b692f64af5d3f15b34a0e223363fa4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Jun 2017 14:50:59 +0200 Subject: [PATCH 2350/4033] CLJS-2078: add resolve macro add resolve macro. Make var-ast public and add a var-meta helper we can use to get the var meta from the resolve macro. --- src/main/clojure/cljs/analyzer.cljc | 45 +++++++++++++++++------------ src/main/clojure/cljs/core.cljc | 15 +++++++++- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 74c370cf9..e54722d3f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1134,7 +1134,32 @@ (defmulti parse (fn [op & rest] op)) -(defn- var-ast +(defn var-meta + ([var] + (var-meta var nil)) + ([var expr-env] + (let [sym (:name var) + ks [:ns :doc :file :line :column] + m (merge + (let [user-meta (:meta var) + uks (keys user-meta)] + (zipmap uks + (map #(list 'quote (get user-meta %)) uks))) + (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) + :name `(quote ~(symbol (name (:name var)))) + :test `(when ~sym (.-cljs$lang$test ~sym)) + :arglists (let [arglists (:arglists var) + arglists' (if (= 'quote (first arglists)) + (second arglists) + arglists)] + (list 'quote + (doall (map with-meta arglists' + (:arglists-meta var)))))))] + (if expr-env + (analyze expr-env m) + m)))) + +(defn var-ast [env sym] ;; we need to dissoc locals for the `(let [x 1] (def x x))` case, because we ;; want the var's AST and `resolve-var` will check locals first. - António Monteiro @@ -1144,23 +1169,7 @@ (when-some [var-ns (:ns var)] {:var (analyze expr-env sym) :sym (analyze expr-env `(quote ~(symbol (name var-ns) (name (:name var))))) - :meta (let [ks [:ns :doc :file :line :column] - m (merge - (let [user-meta (:meta var) - uks (keys user-meta)] - (zipmap uks - (map #(list 'quote (get user-meta %)) uks))) - (assoc (zipmap ks (map #(list 'quote (get var %)) ks)) - :name `(quote ~(symbol (name (:name var)))) - :test `(when ~sym (.-cljs$lang$test ~sym)) - :arglists (let [arglists (:arglists var) - arglists' (if (= 'quote (first arglists)) - (second arglists) - arglists)] - (list 'quote - (doall (map with-meta arglists' - (:arglists-meta var)))))))] - (analyze expr-env m))}))) + :meta (var-meta var expr-env)}))) (defmethod parse 'var [op env [_ sym :as form] _ _] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 35cf48dea..30c55a2c1 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -37,7 +37,7 @@ require use refer-clojure if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand - some? + some? resolve #?@(:cljs [alias coercive-not coercive-not= coercive-= coercive-boolean truth_ js-arguments js-delete js-in js-debugger exists? divide js-mod unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash @@ -3144,3 +3144,16 @@ #?(:clj (. (var defmacro) (setMacro)) :cljs (set! (. defmacro -cljs$lang$macro) true)) + +(core/defmacro resolve + "Returns the var to which a symbol will be resolved in the namespace else nil." + [[_ sym]] + (core/let [env &env + [var meta] (try + (core/let [var (ana/resolve-var env sym (ana/confirm-var-exists-throw)) ] + [var (ana/var-meta var)]) + (catch #?@(:clj [Throwable t] :cljs [:default e]) + [(ana/resolve-var env sym) nil])) + resolved (vary-meta (:name var) assoc ::ana/no-resolve true)] + `(when (exists? ~resolved) + (cljs.core/Var. ~resolved '~resolved ~meta)))) \ No newline at end of file From f957c69d8902250ef58281e9a055cd643c71adf9 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Fri, 9 Jun 2017 22:34:41 +0100 Subject: [PATCH 2351/4033] CLJS-2079: Records and maps are not equal --- src/main/cljs/cljs/core.cljs | 4 ++-- src/main/clojure/cljs/core.cljc | 20 ++++++++++++-------- src/test/cljs/cljs/core_test.cljs | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 365bad710..954b5d5f9 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5833,7 +5833,7 @@ reduces them without incurring seq initialization" false." [x y] (boolean - (when (map? y) + (when (and (map? y) (not (record? y))) ; assume all maps are counted (when (== (count x) (count y)) (every? (fn [xkv] (= (get y (first xkv) never-equiv) @@ -6338,7 +6338,7 @@ reduces them without incurring seq initialization" IEquiv (-equiv [coll other] - (if (implements? IMap other) + (if (and (implements? IMap other) (not (record? other))) (let [alen (alength arr) ^not-native other other] (if (== cnt (-count other)) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 30c55a2c1..a37d65d8c 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1748,13 +1748,17 @@ 'IHash `(~'-hash [this#] (caching-hash this# ~'hash-imap ~'__hash)) 'IEquiv - `(~'-equiv [this# other#] - (if (and other# - (identical? (.-constructor this#) - (.-constructor other#)) - (equiv-map this# other#)) - true - false)) + (let [this (gensym 'this) other (gensym 'other)] + `(~'-equiv [~this ~other] + (and (some? ~other) + (identical? (.-constructor ~this) + (.-constructor ~other)) + ~@(map (fn [field] + `(= (.. ~this ~(to-property field)) + (.. ~other ~(to-property field)))) + base-fields) + (= (.-__extmap ~this) + (.-__extmap ~other))))) 'IMeta `(~'-meta [this#] ~'__meta) 'IWithMeta @@ -1821,7 +1825,7 @@ ks (map keyword fields) getters (map (core/fn [k] `(~k ~ms)) ks)] `(defn ~fn-name [~ms] - (new ~rname ~@getters nil (dissoc ~ms ~@ks) nil)))) + (new ~rname ~@getters nil (not-empty (dissoc ~ms ~@ks)) nil)))) (core/defmacro defrecord "(defrecord name [fields*] options* specs*) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d3b31ed20..65ab18e1a 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1281,6 +1281,25 @@ (let [sm (sorted-map 1 1, 2 2, 3 3, 4 4, 5 5, 6 6, 7 7)] (is (= [1 2 3 4] (reduce-kv (fn [m k v] (if (= 5 k) (reduced m) (conj m k))) [] sm)))))) +(defrecord CLJS2079 [a b]) + +(deftest test-cljs-2079 + (testing "Records and maps should not be equal" + (let [am (array-map :a 1 :b 2) + hm (hash-map :a 1 :b 2) + sm (sorted-map :a 1 :b 2) + r (->CLJS2079 1 2)] + (is (= am hm sm)) + + (is (not= r am)) + (is (not= am r)) + + (is (not= r hm)) + (is (not= hm r)) + + (is (not= r sm)) + (is (not= sm r))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From f701e7946a6c1ad41edfc0b0bbb5b8a6103ae911 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 12 Jun 2017 11:31:34 -0400 Subject: [PATCH 2352/4033] let -> core/let --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a37d65d8c..f6e6d663d 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1748,7 +1748,7 @@ 'IHash `(~'-hash [this#] (caching-hash this# ~'hash-imap ~'__hash)) 'IEquiv - (let [this (gensym 'this) other (gensym 'other)] + (core/let [this (gensym 'this) other (gensym 'other)] `(~'-equiv [~this ~other] (and (some? ~other) (identical? (.-constructor ~this) From 48db4fa69c52af66153d7c1e62429d72ced8cf8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 12 Jun 2017 09:28:25 -0700 Subject: [PATCH 2353/4033] CLJS-2081: Self-host: Regression with CLJS-2079 --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index f6e6d663d..591679623 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1753,7 +1753,7 @@ (and (some? ~other) (identical? (.-constructor ~this) (.-constructor ~other)) - ~@(map (fn [field] + ~@(map (core/fn [field] `(= (.. ~this ~(to-property field)) (.. ~other ~(to-property field)))) base-fields) From 76c1a63c6062556e29c87e2b7e2d33d539b47b7b Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Mon, 12 Jun 2017 11:31:37 +0100 Subject: [PATCH 2354/4033] CLJS-2080: Faster equiv-map --- src/main/cljs/cljs/core.cljs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 954b5d5f9..67eb8c6db 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5836,9 +5836,16 @@ reduces them without incurring seq initialization" (when (and (map? y) (not (record? y))) ; assume all maps are counted (when (== (count x) (count y)) - (every? (fn [xkv] (= (get y (first xkv) never-equiv) - (second xkv))) - x))))) + (if (satisfies? IKVReduce x) + (reduce-kv (fn [_ k v] + (if (= (get y k never-equiv) v) + true + (reduced false))) + true + x) + (every? (fn [xkv] (= (get y (first xkv) never-equiv) + (second v))) + x)))))) (defn- scan-array [incr k array] From 138bc63daacbf33de1192736a3084626cf4c17c8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 12 Jun 2017 16:13:00 -0400 Subject: [PATCH 2355/4033] fix equiv-map typo, make public, clarify docstring --- src/main/cljs/cljs/core.cljs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 67eb8c6db..e0ec60a63 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5828,24 +5828,24 @@ reduces them without incurring seq initialization" (def ^:private never-equiv (NeverEquiv.)) -(defn- ^boolean equiv-map - "Assumes y is a map. Returns true if x equals y, otherwise returns - false." +(defn ^boolean equiv-map + "Test map equivalence. Returns true if x equals y, otherwise returns false." [x y] (boolean (when (and (map? y) (not (record? y))) ; assume all maps are counted (when (== (count x) (count y)) (if (satisfies? IKVReduce x) - (reduce-kv (fn [_ k v] - (if (= (get y k never-equiv) v) - true - (reduced false))) - true - x) - (every? (fn [xkv] (= (get y (first xkv) never-equiv) - (second v))) - x)))))) + (reduce-kv + (fn [_ k v] + (if (= (get y k never-equiv) v) + true + (reduced false))) + true x) + (every? + (fn [xkv] + (= (get y (first xkv) never-equiv) (second xkv))) + x)))))) (defn- scan-array [incr k array] From 15779cf5d2e1e55be6d015af92e4d0008eb46bb2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 12 Jun 2017 16:47:04 -0400 Subject: [PATCH 2356/4033] CLJS-1977: defrecord should use murmur hashing like Clojure --- src/main/clojure/cljs/core.cljc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 591679623..27265a8a3 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1746,7 +1746,13 @@ 'ICloneable `(~'-clone [this#] (new ~tagname ~@fields)) 'IHash - `(~'-hash [this#] (caching-hash this# ~'hash-imap ~'__hash)) + `(~'-hash [this#] + (caching-hash this# + (fn [coll#] + (bit-xor + ~(hash (core/-> rname comp/munge core/str)) + (hash-unordered-coll coll#))) + ~'__hash)) 'IEquiv (core/let [this (gensym 'this) other (gensym 'other)] `(~'-equiv [~this ~other] From 73447c54c9a108a3def58f8dc39aff0b31bcb537 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 12 Jun 2017 19:24:14 -0400 Subject: [PATCH 2357/4033] CLJS-2082: Support generative testing --- project.clj | 2 +- script/bootstrap | 2 +- script/test-self-parity | 4 ++-- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/project.clj b/project.clj index 4ee4e40ed..7729e5c45 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0-beta3"] - [org.clojure/test.check "0.9.0" :scope "test"] + [org.clojure/test.check "0.10.0-alpha1" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] [com.google.javascript/closure-compiler-unshaded "v20170521"] diff --git a/script/bootstrap b/script/bootstrap index 7987535f6..235f46cea 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -11,7 +11,7 @@ TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.0.0-beta3" -TEST_CHECK_RELEASE="0.9.0" +TEST_CHECK_RELEASE="0.10.0-alpha1" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } diff --git a/script/test-self-parity b/script/test-self-parity index c777af4f4..c3085100d 100755 --- a/script/test-self-parity +++ b/script/test-self-parity @@ -10,10 +10,10 @@ if [ ! -f lib/clojure.jar ]; then exit 1 fi jar xvf lib/clojure.jar clojure/template.clj > /dev/null -jar xvf lib/test.check.jar clojure/test/check/random clojure/test/check/generators.cljc clojure/test/check/rose_tree.cljc > /dev/null +jar xvf lib/test.check.jar clojure/test/check.cljc clojure/test/check/results.cljc clojure/test/check/clojure_test.cljc clojure/test/check/impl.cljc clojure/test/check/properties.cljc clojure/test/check/generators.cljc clojure/test/check/random.clj clojure/test/check/random/doubles.cljs clojure/test/check/random/longs/bit_count_impl.cljs clojure/test/check/random/longs.cljs clojure/test/check/random.cljs clojure/test/check/rose_tree.cljc > /dev/null mkdir -p builds/out-self-parity/clojure/test mv clojure/template.clj builds/out-self-parity/clojure -mv clojure/test/check builds/out-self-parity/clojure/test +mv clojure/test builds/out-self-parity/clojure bin/cljsc src/test/self/self_parity "{:optimizations :none :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}" diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 65ab18e1a..186c57419 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -9,6 +9,10 @@ (ns cljs.core-test (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] + [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])) @@ -544,6 +548,10 @@ (is (= 0 (loop [x 0] (cond-> x false recur)))) (is (= 0 (loop [x 0] (cond->> x false recur))))) +(defspec boolean-test 10 + (prop/for-all [b gen/boolean] + (boolean? b))) + ;; ============================================================================= ;; Tickets From 8a27f891bdd0cc9dbd6a5e2a1cdf8453020eabc9 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Tue, 13 Jun 2017 11:32:40 +0100 Subject: [PATCH 2358/4033] CLJS-2083: Test equiv-map for maps which do not impl IKVReduce --- src/test/cljs/cljs/core_test.cljs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 186c57419..b1d22b2af 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1308,6 +1308,18 @@ (is (not= r sm)) (is (not= sm r))))) +(deftype MapWithNoIKVReduce [backing-map] + IMap + (-dissoc [_ _] nil) + + ISeqable + (-seq [_] (seq backing-map))) + +(deftest test-cljs-2083 + (testing "maps which do not implement IKVReduce can be compared" + (is (true? (equiv-map (MapWithNoIKVReduce. {:a 1 :b 2 :c 3}) {:a 1 :b 2 :c 3}))) + (is (false? (equiv-map (MapWithNoIKVReduce. {:a 1 :b 2 :c 3}) {:a 1 :b 2 :c 4}))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 12c142c2a3c6c59f6b4b646887a72c2a265f3832 Mon Sep 17 00:00:00 2001 From: olivergeorge Date: Wed, 14 Jun 2017 08:32:38 +1000 Subject: [PATCH 2359/4033] CLJS-2036: Relative path exception thrown when :preloads requires a :foreign-lib Update relative-name to support Windows by normalising the format of the user.dir system property value. --- src/main/clojure/cljs/util.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index c8de4750e..928bad86a 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -148,8 +148,9 @@ [x] {:pre [(or (file? x) (url? x))]} (letfn [(strip-user-dir [s] - (string/replace s - (str (System/getProperty "user.dir") File/separator) ""))] + (let [user-dir (str (System/getProperty "user.dir") File/separator) + user-path (.getFile (.toURL (io/file user-dir)))] + (string/replace s user-path "")))] (if (file? x) (strip-user-dir (.getAbsolutePath x)) (let [f (.getFile x)] From a6faad4418e89161c6ec2fd036c657f29aec7e7b Mon Sep 17 00:00:00 2001 From: Christophe Grand Date: Thu, 15 Jun 2017 11:58:55 +0200 Subject: [PATCH 2360/4033] fix caching collision between macros ns and regular ns in boostrap. CLJS-2088 --- src/main/cljs/cljs/core.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e0ec60a63..792784bd1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10886,14 +10886,14 @@ reduces them without incurring seq initialization" [ns] (when (nil? NS_CACHE) (set! NS_CACHE (atom {}))) - (let [the-ns (get @NS_CACHE ns)] + (let [ns-str (str ns) + ns (if (not ^boolean (gstring/contains ns-str "$macros")) + (symbol (str ns-str "$macros")) + ns) + the-ns (get @NS_CACHE ns)] (if-not (nil? the-ns) the-ns - (let [ns-str (str ns) - ns (if (not ^boolean (gstring/contains ns-str "$macros")) - (symbol (str ns-str "$macros")) - ns) - ns-obj (find-ns-obj ns)] + (let [ns-obj (find-ns-obj ns)] (when-not (nil? ns-obj) (let [new-ns (create-ns ns ns-obj)] (swap! NS_CACHE assoc ns new-ns) From 9f6e53d9d59d3af5e3ea62119951ae7a094f7ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 14 Jun 2017 13:54:46 -0700 Subject: [PATCH 2361/4033] CLJS-2086: Bump tools.reader to 1.0.0 --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 9bdeb37a7..7720a955e 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.0.0-beta3 + 1.0.0 org.clojure diff --git a/project.clj b/project.clj index 7729e5c45..61c0196da 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.0.0-beta3"] + [org.clojure/tools.reader "1.0.0"] [org.clojure/test.check "0.10.0-alpha1" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] diff --git a/script/bootstrap b/script/bootstrap index 235f46cea..9b12a61a0 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.0.0-beta3" +TREADER_RELEASE="1.0.0" TEST_CHECK_RELEASE="0.10.0-alpha1" # check dependencies From ba5e70aac686d56a8000e3a32d17fdad90e2151f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 15 Jun 2017 14:36:44 -0400 Subject: [PATCH 2362/4033] CLJS-2085: defrecord recur method head target object --- src/main/clojure/cljs/analyzer.cljc | 16 +++++++- src/test/cljs/cljs/recur_test.cljs | 64 +++++++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 6 ++- src/test/self/self_parity/test.cljs | 6 ++- 4 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 src/test/cljs/cljs/recur_test.cljs diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e54722d3f..255bbd4fa 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -137,6 +137,7 @@ :protocol-duped-method true :protocol-multiple-impls true :protocol-with-variadic-method true + :protocol-impl-recur-with-target true :single-segment-namespace true :munged-namespace true :ns-var-clash true @@ -358,6 +359,10 @@ (str "Protocol " (:protocol info) " declares method " (:name info) " with variadic signature (&)")) +(defmethod error-message :protocol-impl-recur-with-target + [warning-type info] + (str "Ignoring target object \"" (:form info) "\" passed in recur to protocol method head")) + (defmethod error-message :multiple-variadic-overloads [warning-type info] (str (:name info) ": Can't have more than 1 variadic overload")) @@ -1471,7 +1476,9 @@ (butlast params) params) fixed-arity (count params') - recur-frame {:params params :flag (atom nil)} + recur-frame {:protocol-impl (:protocol-impl env) + :params params + :flag (atom nil)} recur-frames (cons recur-frame *recur-frames*) body-env (assoc env :context :return :locals locals) body-form `(do ~@body) @@ -1758,11 +1765,18 @@ [op env [_ & exprs :as form] _ _] (let [context (:context env) frame (first *recur-frames*) + ;; Add dummy implicit target object if recuring to proto impl method head + add-implicit-target-object? (and (:protocol-impl frame) + (= (count exprs) (dec (count (:params frame))))) + exprs (cond->> exprs add-implicit-target-object? (cons nil)) exprs (disallowing-recur (vec (map #(analyze (assoc env :context :expr) %) exprs)))] (when-not frame (throw (error env "Can't recur here"))) (when-not (= (count exprs) (count (:params frame))) (throw (error env "recur argument count mismatch"))) + (when (and (:protocol-impl frame) + (not add-implicit-target-object?)) + (warning :protocol-impl-recur-with-target env {:form (:form (first exprs))})) (reset! (:flag frame) true) (assoc {:env env :op :recur :form form} :frame frame diff --git a/src/test/cljs/cljs/recur_test.cljs b/src/test/cljs/cljs/recur_test.cljs new file mode 100644 index 000000000..9dbd3badb --- /dev/null +++ b/src/test/cljs/cljs/recur_test.cljs @@ -0,0 +1,64 @@ +;; 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.recur-test + (:require [cljs.test :refer-macros [deftest is]])) + +;; Setup for CLJS-2085 + +(defprotocol ISearch + (search [this coll])) + +;; Passing this in the recur call here will cause a warning to be emitted +(defrecord Search1 [needle] + ISearch + (search [this coll] + (when (seq coll) + (if (= needle (first coll)) + needle + (recur this (rest coll)))))) + +;; This code will be accepted as is +(defrecord Search2 [needle] + ISearch + (search [_ coll] + (when (seq coll) + (if (= needle (first coll)) + needle + (recur (rest coll)))))) + +;; This code will also be accepted as is; the recur is to a loop +(defrecord Search3 [needle] + ISearch + (search [this coll] + (loop [coll coll] + (when (seq coll) + (if (= needle (first coll)) + needle + (recur (rest coll))))))) + +;; This code should not cause a warning to be emitted +(defrecord Search4 [needle] + ISearch + (search [this coll] + (let [search-fn (fn [coll] + (when (seq coll) + (if (= needle (first coll)) + needle + (recur (rest coll)))))] + (search-fn coll)))) + +(deftest cljs-2085-test + (is (= 1 (-> (->Search1 1) (search [:a 1 "b"])))) + (is (nil? (-> (->Search1 :z) (search [:a 1 "b"])))) + (is (= 1 (-> (->Search2 1) (search [:a 1 "b"])))) + (is (nil? (-> (->Search2 :z) (search [:a 1 "b"])))) + (is (= 1 (-> (->Search3 1) (search [:a 1 "b"])))) + (is (nil? (-> (->Search3 :z) (search [:a 1 "b"])))) + (is (= 1 (-> (->Search4 1) (search [:a 1 "b"])))) + (is (nil? (-> (->Search4 :z) (search [:a 1 "b"]))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index ab979d089..d458d8163 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -41,7 +41,8 @@ [cljs.predicates-test] [cljs.tagged-literals-test] [cljs.test-test] - [static.core-test])) + [static.core-test] + [cljs.recur-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -80,4 +81,5 @@ 'cljs.syntax-quote-test 'cljs.tagged-literals-test 'cljs.test-test - 'static.core-test) + 'static.core-test + 'cljs.recur-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 16e6c8ffa..4c6535cea 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -296,7 +296,8 @@ [cljs.syntax-quote-test] [cljs.predicates-test] [cljs.test-test] - [static.core-test])) + [static.core-test] + [cljs.recur-test])) (fn [{:keys [value error]}] (if error (handle-error error (:source-maps @st)) @@ -334,7 +335,8 @@ 'cljs.syntax-quote-test 'cljs.predicates-test 'cljs.test-test - 'static.core-test) + 'static.core-test + 'cljs.recur-test) (fn [{:keys [value error]}] (when error (handle-error error (:source-maps @st)))))))))) From f4d1df3c21becb3bbf7cfb64718ad453d140c7e7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 15 Jun 2017 20:11:46 -0400 Subject: [PATCH 2363/4033] CLJS-2089: Warn message wrong for recur to protocol with nil --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 255bbd4fa..307f3c001 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -361,7 +361,7 @@ (defmethod error-message :protocol-impl-recur-with-target [warning-type info] - (str "Ignoring target object \"" (:form info) "\" passed in recur to protocol method head")) + (str "Ignoring target object \"" (pr-str (:form info)) "\" passed in recur to protocol method head")) (defmethod error-message :multiple-variadic-overloads [warning-type info] From cec745da96364a603718c5cf83cd774009b20548 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 15 Jun 2017 21:42:37 -0400 Subject: [PATCH 2364/4033] CLJS-2091: reify docstring ISeqable example needs correction --- src/main/clojure/cljs/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 27265a8a3..82da4e1d3 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1284,8 +1284,8 @@ (seq (let [f \"foo\"] (reify ISeqable - (-seq [this] (-seq f))))) - == (\\f \\o \\o)) + (-seq [this] (seq f))))) + == (\"f\" \"o\" \"o\")) reify always implements IMeta and IWithMeta and transfers meta data of the form to the created object. From db0cc1b8f5fc39e3c240f3a2147fd9c02ce0db36 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 29 May 2017 11:45:05 -0400 Subject: [PATCH 2365/4033] CLJS-2060: Backport CLJ-2141 Return only true/false from qualified-* predicates --- src/main/cljs/cljs/core.cljs | 6 +++--- src/test/cljs/cljs/predicates_test.cljs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 792784bd1..68d252f9d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3224,7 +3224,7 @@ reduces them without incurring seq initialization" (defn ^boolean qualified-ident? "Return true if x is a symbol or keyword with a namespace" - [x] (and (ident? x) (namespace x) true)) + [x] (boolean (and (ident? x) (namespace x) true))) (defn ^boolean simple-symbol? "Return true if x is a symbol without a namespace" @@ -3232,7 +3232,7 @@ reduces them without incurring seq initialization" (defn ^boolean qualified-symbol? "Return true if x is a symbol with a namespace" - [x] (and (symbol? x) (namespace x) true)) + [x] (boolean (and (symbol? x) (namespace x) true))) (defn ^boolean simple-keyword? "Return true if x is a keyword without a namespace" @@ -3240,7 +3240,7 @@ reduces them without incurring seq initialization" (defn ^boolean qualified-keyword? "Return true if x is a keyword with a namespace" - [x] (and (keyword? x) (namespace x) true)) + [x] (boolean (and (keyword? x) (namespace x) true))) (defn keyword "Returns a Keyword with the given namespace and name. Do not use : diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 76d54dbf0..33c33f26a 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -21,9 +21,9 @@ [[] false true true false false false false false false false false false] [nil false false false false false false false false false false false false] [{} false false true false false false false false false false false false] - [:foo false false false true false false true nil false false true nil] + [:foo false false false true false false true false false false true false] [::foo false false false true false false false true false false false true] - ['foo false false false true false false true nil true nil false false] + ['foo false false false true false false true false true false false false] ['foo/bar false false false true false false false true false true false false] [uuid false false false false true false false false false false false false] [(array) false false true false false false false false false false false false] From bb56fb4a6101e16a7ba072dc0b3d6bd6a2ed2373 Mon Sep 17 00:00:00 2001 From: Shaun LeBron Date: Sun, 28 May 2017 19:07:11 -0500 Subject: [PATCH 2366/4033] CLJS-2057: fix language-in options (es6 deprecated and add missing es2016) --- src/main/clojure/cljs/closure.clj | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8c02a1872..2e39c62a3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -190,16 +190,18 @@ {})))) (defn ^CompilerOptions$LanguageMode lang-key->lang-mode [key] - (case key - :no-transpile CompilerOptions$LanguageMode/NO_TRANSPILE - (:ecmascript6 :es6) CompilerOptions$LanguageMode/ECMASCRIPT6 - (:ecmascript-2017 :es-2017) CompilerOptions$LanguageMode/ECMASCRIPT_2017 - (:ecmascript-next :es-next) CompilerOptions$LanguageMode/ECMASCRIPT_NEXT - (:ecmascript6-strict :es6-strict) CompilerOptions$LanguageMode/ECMASCRIPT6_STRICT - (:ecmascript6-typed :es6-typed) CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED - (:ecmascript5 :es5) CompilerOptions$LanguageMode/ECMASCRIPT5 - (:ecmascript5-strict :es5-strict) CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT - (:ecmascript3 :es3) CompilerOptions$LanguageMode/ECMASCRIPT3)) + (case (keyword (string/replace (name key) #"^es" "ecmascript")) + :no-transpile CompilerOptions$LanguageMode/NO_TRANSPILE ;; same mode as input (for language-out only) + :ecmascript3 CompilerOptions$LanguageMode/ECMASCRIPT3 + :ecmascript5 CompilerOptions$LanguageMode/ECMASCRIPT5 + :ecmascript5-strict CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT + :ecmascript6 CompilerOptions$LanguageMode/ECMASCRIPT_2015 ;; (deprecated and remapped) + :ecmascript6-strict CompilerOptions$LanguageMode/ECMASCRIPT_2015 ;; (deprecated and remapped) + :ecmascript-2015 CompilerOptions$LanguageMode/ECMASCRIPT_2015 + :ecmascript6-typed CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED + :ecmascript-2016 CompilerOptions$LanguageMode/ECMASCRIPT_2016 + :ecmascript-2017 CompilerOptions$LanguageMode/ECMASCRIPT_2017 + :ecmascript-next CompilerOptions$LanguageMode/ECMASCRIPT_NEXT)) (defn set-options "TODO: Add any other options that we would like to support." @@ -1623,7 +1625,7 @@ (let [^List externs '() ^List source-files (get-source-files js-modules) ^CompilerOptions options (doto (make-convert-js-module-options opts) - (.setLanguageIn CompilerOptions$LanguageMode/ECMASCRIPT6) + (.setLanguageIn (lang-key->lang-mode :ecmascript6)) (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) closure-compiler (doto (make-closure-compiler) (.init externs source-files options))] From 9b4fcfcf26c37a88623c903e2814fac1eaf302ff Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Wed, 19 Apr 2017 13:12:28 +0100 Subject: [PATCH 2367/4033] CLJS-2012: Find on PHM with nil entry always returns nil entry --- src/main/cljs/cljs/core.cljs | 5 +++-- src/test/cljs/cljs/collections_test.cljs | 10 +++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 68d252f9d..34fdf06c6 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7412,8 +7412,9 @@ reduces them without incurring seq initialization" IFind (-find [coll k] - (if has-nil? - [nil nil-val] + (if (nil? k) + (when has-nil? + [nil nil-val]) (.inode-find root 0 (hash k) k nil))) IMap diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index cd20b610b..4f33db350 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -640,7 +640,15 @@ (is (= v v')) (is (= metadata (meta k')))) (let [map (hash-map nil :foo)] - (is (= (find map nil) [nil :foo])))) + (is (= (find map nil) [nil :foo]))) + (let [metadata {:a 1} + k [1 2 3] + v 1 + map (hash-map (with-meta k metadata) v nil 2) + [k' v'] (find map k)] + (is (= k k')) + (is (= v v')) + (is (= metadata (meta k'))))) (testing "PersistentTreeMap" (let [metadata {:a 1} k [1 2 3] From 55f9bcc5445e8bb6a6534ee6c528c871d0e5e280 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 4 Jun 2017 18:15:36 -0400 Subject: [PATCH 2368/4033] CLJS-2073: Don't flush for every emitted line --- src/main/clojure/cljs/compiler.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1e6533793..bf85754de 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -203,7 +203,8 @@ (defn emitln [& xs] (apply emits xs) - (println) + (binding [*flush-on-newline* false] + (println)) (when *source-map-data* (swap! *source-map-data* (fn [{:keys [gen-line] :as m}] From f506e88e990bf4297f049a5d6aacdf548615e2c1 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Thu, 11 May 2017 12:35:28 +0100 Subject: [PATCH 2369/4033] CLJS-2068: MapEntry, RedNode and BlackNode are IComparable --- src/main/cljs/cljs/core.cljs | 18 ++++++++++++++++++ src/test/cljs/cljs/map_entry_test.cljs | 6 +++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 34fdf06c6..996c63774 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9753,6 +9753,24 @@ reduces them without incurring seq initialization" (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))))) + + MapEntry + (-compare [x y] + (if (vector? y) + (compare-indexed x 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))))) + + RedNode (-compare [x y] (if (vector? y) (compare-indexed x y) diff --git a/src/test/cljs/cljs/map_entry_test.cljs b/src/test/cljs/cljs/map_entry_test.cljs index a07738426..65f0baaa3 100644 --- a/src/test/cljs/cljs/map_entry_test.cljs +++ b/src/test/cljs/cljs/map_entry_test.cljs @@ -133,7 +133,11 @@ :key (e 0 :not-found) :val (e 1 :not-found) :not-found (e 2 :not-found) - :not-found (e -1 :not-found)))))) + :not-found (e -1 :not-found)))) + + (testing "IComparable" + (testing "-compare" + (is (zero? (-compare e [:key :val]))))))) (deftest all-map-entry-tests (testing "BlackNode" From 028fd479799d543f68ab4c02ae86ac32b2c45858 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 4 Jun 2017 17:26:28 -0400 Subject: [PATCH 2370/4033] CLJS-2072: Eliminate reflection in cljs.js-deps/build-index --- src/main/clojure/cljs/js_deps.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index cb6338092..6bc823b5f 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -148,9 +148,9 @@ case." ;; avoid overwriting a CLJS dep with a CLJC dep of ;; the same namespace - António Monteiro (let [file (when-let [f (or (:source-file dep) (:file dep))] - (.toString f)) + (str f)) ext (when file - (.substring file (inc (.lastIndexOf file "."))))] + (subs file (inc (string/last-index-of file "."))))] (update-in index' [provide] (fn [d] (if (and (= ext "cljc") (some? d)) From 64f126f136270492555b5afe5a6947646a36bdc4 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Mon, 12 Dec 2016 11:23:23 +0000 Subject: [PATCH 2371/4033] CLJS-1876: Faster reduce for PV, Subvec and ChunkedSeq --- src/main/cljs/cljs/core.cljs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 996c63774..e546ae268 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5016,6 +5016,22 @@ reduces them without incurring seq initialization" (unchecked-array-for v i)) v start end))) +(defn- pv-reduce + ([pv f start end] + (if (< start end) + (pv-reduce pv f (nth pv start) (inc start) end) + (f))) + ([pv f init start end] + (loop [acc init i start arr (unchecked-array-for pv start)] + (if (< i end) + (let [j (bit-and i 0x01f) + arr (if (zero? j) (unchecked-array-for pv i) arr) + nacc (f acc (aget arr j))] + (if (reduced? nacc) + @nacc + (recur nacc (inc i) arr))) + acc)))) + (declare tv-editable-root tv-editable-tail TransientVector deref pr-sequential-writer pr-writer chunked-seq) @@ -5162,7 +5178,7 @@ reduces them without incurring seq initialization" IReduce (-reduce [v f] - (ci-reduce v f)) + (pv-reduce v f 0 cnt)) (-reduce [v f init] (loop [i 0 init init] (if (< i cnt) @@ -5334,10 +5350,10 @@ reduces them without incurring seq initialization" IReduce (-reduce [coll f] - (ci-reduce (subvec vec (+ i off) (count vec)) f)) + (pv-reduce vec f (+ i off) (count vec))) (-reduce [coll f start] - (ci-reduce (subvec vec (+ i off) (count vec)) f start))) + (pv-reduce vec f start (+ i off) (count vec)))) (es6-iterable ChunkedSeq) @@ -5447,9 +5463,9 @@ reduces them without incurring seq initialization" IReduce (-reduce [coll f] - (ci-reduce coll f)) - (-reduce [coll f start] - (ci-reduce coll f start)) + (pv-reduce v f start end)) + (-reduce [coll f init] + (pv-reduce v f init start end)) IKVReduce (-kv-reduce [coll f init] From a5a9632e06945067e941c5f915bbfb4d4dbacf0a Mon Sep 17 00:00:00 2001 From: ewen Date: Sun, 22 Jan 2017 21:25:06 +0100 Subject: [PATCH 2372/4033] CLJS-1886: RangedIterator should only be created from cljs.core.PersistentVector instances Create ranged iterators from APersistentVector instances only --- src/main/cljs/cljs/core.cljs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e546ae268..f146545c5 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5035,6 +5035,9 @@ reduces them without incurring seq initialization" (declare tv-editable-root tv-editable-tail TransientVector deref pr-sequential-writer pr-writer chunked-seq) +(defprotocol APersistentVector + "Marker protocol") + (deftype PersistentVector [meta cnt shift root tail ^:mutable __hash] Object (toString [coll] @@ -5164,6 +5167,7 @@ reduces them without incurring seq initialization" (-find [coll k] [k (get coll k)]) + APersistentVector IVector (-assoc-n [coll n val] (cond @@ -5485,7 +5489,9 @@ reduces them without incurring seq initialization" IIterable (-iterator [coll] - (ranged-iterator v start end))) + (if (implements? APersistentVector coll) + (ranged-iterator v start end) + (seq-iter coll)))) (es6-iterable Subvec) From 37666f72ab98c86fc956a3a15c2b2b8b41b9ff5e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jun 2017 11:27:15 -0400 Subject: [PATCH 2373/4033] CLJS-1891: UUID.toString can return non-string assert that argument to cljs.core/uuid is a string --- src/main/cljs/cljs/core.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f146545c5..c15c1b66a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10579,6 +10579,7 @@ reduces them without incurring seq initialization" (garray/defaultCompare uuid (.-uuid other)))) (defn uuid [s] + (assert (string? s)) (UUID. s nil)) (defn random-uuid [] From 4a418c992342c8990cdeea3159337d8f9936c3c7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 16 Jun 2017 12:55:48 -0400 Subject: [PATCH 2374/4033] CLJS-2094: Predicates unit tests constructs a uuid with nil MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead use the “nil” UUID as described in RFC 4122 section 4.1.7. --- src/test/cljs/cljs/predicates_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 33c33f26a..7de17bacc 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -11,7 +11,7 @@ (:import [goog.math Long Integer])) (def pred-val-table - (let [uuid (uuid nil)] + (let [uuid (uuid "00000000-0000-0000-0000-000000000000")] [[identity boolean? indexed? seqable? ident? uuid? inst? simple-ident? qualified-ident? simple-symbol? qualified-symbol? simple-keyword? qualified-keyword?] [0 false false false false false false false false false false false false] [1 false false false false false false false false false false false false] From 00309e895d4f5c5d9321f02d2ba23e5f4af884a1 Mon Sep 17 00:00:00 2001 From: rauhs Date: Tue, 9 May 2017 15:58:01 +0200 Subject: [PATCH 2375/4033] CLJS-2030: Case with grouped keyword test emit result-expr multiple times Improves case macro when grouping keywords. Now the result expression is only emitted once. --- src/main/clojure/cljs/core.cljc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 82da4e1d3..5970736c6 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2248,11 +2248,10 @@ `(let [~esym ~e] (case* ~esym ~tests ~thens ~default))) (every? core/keyword? tests) - (core/let [tests (core/->> tests - (map #(.substring (core/str %) 1)) - vec - (mapv #(if (seq? %) (vec %) [%]))) - thens (vec (vals pairs))] + (core/let [no-default (if (odd? (count clauses)) (butlast clauses) clauses) + kw-str #(.substring (core/str %) 1) + tests (mapv #(if (seq? %) (mapv kw-str %) [(kw-str %)]) (take-nth 2 no-default)) + thens (vec (take-nth 2 (drop 1 no-default)))] `(let [~esym ~e ~esym (if (keyword? ~esym) (.-fqn ~esym) nil)] (case* ~esym ~tests ~thens ~default))) From 93657bca464d32e375531f8f8d38e47c88c46fb0 Mon Sep 17 00:00:00 2001 From: Marc Daya Date: Wed, 23 Nov 2016 16:27:32 -0700 Subject: [PATCH 2376/4033] NodeJS REPL accepts a :path opt to set NODE_PATH. This makes it possible to specify a NodeJS module search path so that modules don't need to be installed globally. --- src/main/clojure/cljs/repl/node.clj | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index f3eb056c6..64c7190d9 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -96,6 +96,18 @@ (.printStackTrace e *err*)))) (recur buf))))) +(defn- build-process + [opts repl-env input-src] + (let [xs (cond-> [(get opts :node-command "node")] + (:debug-port repl-env) (conj (str "--debug=" (:debug-port repl-env)))) + proc (-> (ProcessBuilder. (into-array xs)) (.redirectInput input-src))] + (when-let [path-fs (:path repl-env)] + (.put (.environment proc) + "NODE_PATH" + (string/join File/pathSeparator + (map #(.getAbsolutePath (io/as-file %)) path-fs)))) + proc)) + (defn setup ([repl-env] (setup repl-env nil)) ([repl-env opts] @@ -106,11 +118,7 @@ (string/replace (slurp (io/resource "cljs/repl/node_repl.js")) "var PORT = 5001;" (str "var PORT = " (:port repl-env) ";"))) - xs (cond-> [(get opts :node-command "node")] - (:debug-port repl-env) (conj (str "--debug=" (:debug-port repl-env)))) - proc (-> (ProcessBuilder. (into-array xs)) - (.redirectInput of) - .start) + proc (.start (build-process opts repl-env of)) _ (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) *out*)))) (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) *err*))))) env (ana/empty-env) @@ -189,7 +197,7 @@ (js/CLOSURE_IMPORT_SCRIPT (aget (.. js/goog -dependencies_ -nameToPath) name)))))))))) -(defrecord NodeEnv [host port socket proc] +(defrecord NodeEnv [host port path socket proc] repl/IReplEnvOptions (-repl-options [this] {:output-dir ".cljs_node_repl" @@ -209,18 +217,19 @@ (close-socket @socket))) (defn repl-env* [options] - (let [{:keys [host port debug-port]} + (let [{:keys [host port path debug-port]} (merge {:host "localhost" :port (+ 49000 (rand-int 10000))} options)] - (assoc (NodeEnv. host port (atom nil) (atom nil)) + (assoc (NodeEnv. host port path (atom nil) (atom nil)) :debug-port debug-port))) (defn repl-env - "Construct a Node.js evalution environment. Can supply :host and :port." + "Construct a Node.js evalution environment. Can supply :host, :port + and :path (a vector used as the NODE_PATH)." [& {:as options}] (repl-env* options)) (defn -main [] - (repl/repl (repl-env))) \ No newline at end of file + (repl/repl (repl-env))) From e475e1df8454578135d70ce1ffadb513c270d3e4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 16 Jun 2017 13:25:19 -0400 Subject: [PATCH 2377/4033] CLJS-1599: UUIDs are not equal for upper/lower case strings --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c15c1b66a..4bb98d791 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10580,7 +10580,7 @@ reduces them without incurring seq initialization" (defn uuid [s] (assert (string? s)) - (UUID. s nil)) + (UUID. (.toLowerCase s) nil)) (defn random-uuid [] (letfn [(hex [] (.toString (rand-int 16) 16))] From 89628498bb20212186f77d87d6a9618031392783 Mon Sep 17 00:00:00 2001 From: Daniel Compton Date: Thu, 26 Jan 2017 12:55:02 +1300 Subject: [PATCH 2378/4033] Check for compilation success, and lib folder If script/bootstrap hasn't been run, then running script/test returns the slightly cryptic error message: Error: Could not find or load main class clojure.main This patch checks if the lib folder exists and has files in it. If not it prompts the user to run script/bootstrap and exits. This patch also adds a check for CLJS compilation success before running tests against the various VM engines. --- bin/cljsc | 5 +++++ script/test | 5 ++++- script/test-self-host | 5 ++++- script/test-self-parity | 5 ++++- script/test-simple | 5 ++++- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/bin/cljsc b/bin/cljsc index 6f2bbb469..4a866c998 100755 --- a/bin/cljsc +++ b/bin/cljsc @@ -7,6 +7,11 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then CLOJURESCRIPT_HOME="`dirname $0`/.." fi +if ! test "$(ls -A "$CLOJURESCRIPT_HOME/lib" 2>/dev/null)"; then + >&2 echo lib/ folder is empty, have you run \`script/bootstrap\`? + exit 1 +fi + CLJSC_CP='' for next in lib/*: src/main/clojure: src/main/cljs: src/test/cljs; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" diff --git a/script/test b/script/test index aaf8cb17f..9b04fd9f7 100755 --- a/script/test +++ b/script/test @@ -8,7 +8,10 @@ mkdir -p builds/out-adv possible=4 ran=0 -bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js +if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js; then + >&2 echo ClojureScript compilation failed + exit 1 +fi; if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" diff --git a/script/test-self-host b/script/test-self-host index 06fe23fff..c3df253a8 100755 --- a/script/test-self-host +++ b/script/test-self-host @@ -4,7 +4,10 @@ rm -rf builds/out-self mkdir -p builds/out-self -bin/cljsc src/test/self/self_host "{:optimizations :simple :static-fns true :output-dir \"builds/out-self\" :optimize-constants true :verbose true :compiler-stats true :parallel-build true :target :nodejs}" > builds/out-self/core-self-test.js +if ! bin/cljsc src/test/self/self_host "{:optimizations :simple :static-fns true :output-dir \"builds/out-self\" :optimize-constants true :verbose true :compiler-stats true :parallel-build true :target :nodejs}" > builds/out-self/core-self-test.js; then + >&2 echo ClojureScript compilation failed + exit 1 +fi; echo "Testing with Node" node builds/out-self/core-self-test.js diff --git a/script/test-self-parity b/script/test-self-parity index c3085100d..39a99049c 100755 --- a/script/test-self-parity +++ b/script/test-self-parity @@ -15,7 +15,10 @@ mkdir -p builds/out-self-parity/clojure/test mv clojure/template.clj builds/out-self-parity/clojure mv clojure/test builds/out-self-parity/clojure -bin/cljsc src/test/self/self_parity "{:optimizations :none :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}" +if ! bin/cljsc src/test/self/self_parity "{:optimizations :none :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}"; then + >&2 echo ClojureScript compilation failed + exit 1 +fi; echo "Testing with Node" node builds/out-self-parity/main.js diff --git a/script/test-simple b/script/test-simple index 46e3bc0b1..832372eb5 100755 --- a/script/test-simple +++ b/script/test-simple @@ -8,7 +8,10 @@ possible=4 ran=0 #bin/cljsc test >out/core-test.js -bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :compiler-stats true}" > builds/out-simp/core-simple-test.js +if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :compiler-stats true}" > builds/out-simp/core-simple-test.js; then + >&2 echo ClojureScript compilation failed + exit 1 +fi; if [ "$V8_HOME" = "" ]; then echo "V8_HOME not set, skipping V8 tests" From aa6ecdfa2e53236e1cd727bc2e4f5942f6efddc1 Mon Sep 17 00:00:00 2001 From: Stephen Brady Date: Mon, 16 May 2016 13:14:30 -0700 Subject: [PATCH 2379/4033] CLJS-1641: Multi-arity defn copies arguments unnecessarily for all cases Copying arguments to an array is only necessary in the variadic case. Move arguments array copy to only the variadic case in a multi-arity defn. --- src/main/clojure/cljs/core.cljc | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 5970736c6..66131cf54 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3003,22 +3003,22 @@ `(do (def ~(with-meta name meta) (fn [~'var_args] - (let [~args-sym (array)] - (copy-arguments ~args-sym) - (case (alength ~args-sym) - ~@(mapcat #(fixed-arity rname %) sigs) - ~(if variadic - `(let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq - (.slice ~args-sym ~maxfa) 0 nil)] - (. ~rname - (~'cljs$core$IFn$_invoke$arity$variadic - ~@(dest-args maxfa) - argseq#))) - (if (:macro meta) - `(throw (js/Error. - (str "Invalid arity: " (- (alength ~args-sym) 2)))) - `(throw (js/Error. - (str "Invalid arity: " (alength ~args-sym)))))))))) + (case (alength (js-arguments)) + ~@(mapcat #(fixed-arity rname %) sigs) + ~(if variadic + `(let [args-arr# (array)] + (copy-arguments args-arr#) + (let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq + (.slice args-arr# ~maxfa) 0 nil)] + (. ~rname + (~'cljs$core$IFn$_invoke$arity$variadic + ~@(dest-args maxfa) + argseq#)))) + (if (:macro meta) + `(throw (js/Error. + (str "Invalid arity: " (- (alength (js-arguments)) 2)))) + `(throw (js/Error. + (str "Invalid arity: " (alength (js-arguments)))))))))) ~@(map fn-method fdecl) ;; optimization properties (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa) From 977284eb744dd3d914a071351d184ff09ba3f0aa Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 16 Jun 2017 14:20:50 -0400 Subject: [PATCH 2380/4033] CLJS-2092: Redundant call to equiv-map in PAM.-equiv --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4bb98d791..1dde5187e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6367,7 +6367,7 @@ reduces them without incurring seq initialization" IEquiv (-equiv [coll other] - (if (and (implements? IMap other) (not (record? other))) + (if (and (map? other) (not (record? other))) (let [alen (alength arr) ^not-native other other] (if (== cnt (-count other)) @@ -6381,7 +6381,7 @@ reduces them without incurring seq initialization" false)) true)) false)) - (equiv-map coll other))) + false)) IHash (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) From 039d4f28d524cb745f505d5edf526c6da89338c6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 16 Jun 2017 14:28:57 -0400 Subject: [PATCH 2381/4033] CLJS-1685: Incorrectly lazy subvec when start param is nil --- src/main/cljs/cljs/core.cljs | 5 +++-- src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1dde5187e..23e1f6d7e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5516,9 +5516,10 @@ reduces them without incurring seq initialization" the resulting vector shares structure with the original and no trimming is done." ([v start] - (subvec v start (count v))) + (subvec v start (count v))) ([v start end] - (build-subvec nil v start end nil))) + (assert (and (not (nil? start)) (not (nil? end)))) + (build-subvec nil v start end nil))) (defn- tv-ensure-editable [edit node] (if (identical? edit (.-edit node)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b1d22b2af..6d06ce3db 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1320,6 +1320,13 @@ (is (true? (equiv-map (MapWithNoIKVReduce. {:a 1 :b 2 :c 3}) {:a 1 :b 2 :c 3}))) (is (false? (equiv-map (MapWithNoIKVReduce. {:a 1 :b 2 :c 3}) {:a 1 :b 2 :c 4}))))) +(deftest test-cljs-1685 + (testing "nil start or end param throws error" + (is (= :fail (try (subvec nil nil) + (catch js/Error e :fail)))) + (is (= :fail (try (subvec nil 1 nil) + (catch js/Error e :fail)))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 7341697376824522810ecc3e0b19afa2538761e1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 16 Jun 2017 15:41:34 -0400 Subject: [PATCH 2382/4033] CLJS-924: Better error message for mistaken use of 'def' --- src/main/clojure/cljs/analyzer.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 307f3c001..682339f26 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1291,6 +1291,8 @@ (defmethod parse 'def [op env form _ _] + (when (> (count form) 4) + (throw (error env "Too many arguments to def"))) (let [pfn (fn ([_ sym] {:sym sym}) ([_ sym init] {:sym sym :init init}) From b4206c43524f2f788e51a680788dc25a2c81b88c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 16 Jun 2017 15:52:45 -0400 Subject: [PATCH 2383/4033] CLJS-1251: Missing semicolons when emitting deftype and defrecord mistaken use of 'def' --- src/main/clojure/cljs/compiler.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index bf85754de..73498e5d7 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1121,7 +1121,7 @@ (emitln "this." fld " = " fld ";")) (doseq [[pno pmask] pmasks] (emitln "this.cljs$lang$protocol_mask$partition" pno "$ = " pmask ";")) - (emitln "})") + (emitln "});") (emit body))) (defmethod emit* :defrecord* @@ -1138,7 +1138,7 @@ (emitln "this." fld " = " fld ";")) (doseq [[pno pmask] pmasks] (emitln "this.cljs$lang$protocol_mask$partition" pno "$ = " pmask ";")) - (emitln "})") + (emitln "});") (emit body))) (defmethod emit* :dot From 92de138f24e2ee3c344b12dd14b034d01d1e9a47 Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Thu, 4 Aug 2016 20:41:57 +0200 Subject: [PATCH 2384/4033] CLJS-1724: Include IIterable in fast-path-protocols --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 66131cf54..304b048e6 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -803,7 +803,7 @@ IPrintWithWriter IPending IWatchable IEditableCollection ITransientCollection ITransientAssociative ITransientMap ITransientVector ITransientSet IMultiFn IChunkedSeq IChunkedNext IComparable INamed ICloneable IAtom - IReset ISwap]) + IReset ISwap IIterable]) (iterate (core/fn [[p b]] (if (core/== 2147483648 b) [(core/inc p) 1] From 57819844b5a8da0464832f82426ac57ebdf2eaea Mon Sep 17 00:00:00 2001 From: Daniel Compton Date: Fri, 27 Jan 2017 13:02:29 +1300 Subject: [PATCH 2385/4033] CLJS-1907: Improve error message from cljs.reader/read-string When reading keywords with numbers first. Before this patch, re-matches* would return nil, and then the aget for token would throw an error that null is not an object. --- src/main/cljs/cljs/reader.cljs | 1 + src/test/cljs/cljs/reader_test.cljs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index fbecc4437..bcb3ba7e1 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -365,6 +365,7 @@ nil if the end of stream has been reached") [reader initch] (let [token (read-token reader (read-char reader)) a (re-matches* symbol-pattern token) + _ (when (nil? a) (reader-error reader "Invalid keyword " (str ":" token))) token (aget a 0) ns (aget a 1) name (aget a 2)] diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index c5b3b72b5..3d78d60b8 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -223,3 +223,7 @@ (is (= "Duplicate key: :a" b)) (is (= "Duplicate key: :a" c)) (is (= "Duplicate key: :a" d)))) + +(deftest test-error-messages + (testing "Leading numbers in keywords" + (is (thrown-with-msg? js/Error #"Invalid keyword :0s" (reader/read-string ":0s"))))) From c6b4b764c7c5e146259e9435607c4e0c38d2314a Mon Sep 17 00:00:00 2001 From: OHTA Shogo Date: Thu, 9 Mar 2017 12:57:04 +0900 Subject: [PATCH 2386/4033] Fix typos in docstring of require macro --- src/main/clojure/cljs/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 304b048e6..fcf018e6b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2780,7 +2780,7 @@ Recognized options: :as takes a symbol as its argument and makes that symbol an alias to the lib's namespace in the current namespace. - :refer takes a list of symbols to refer from the namespace.. + :refer takes a list of symbols to refer from the namespace. :refer-macros takes a list of macro symbols to refer from the namespace. :include-macros true causes macros from the namespace to be required. :rename specifies a map from referred var names to different @@ -2801,7 +2801,7 @@ The following would load the library clojure.string :as string. - (require '[clojure/string :as string])" + (require '[clojure.string :as string])" [& args] `(~'ns* ~(cons :require args))) From e58cbb4dcbf8724f1e210247b26b84f67850d9fd Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Mon, 10 Apr 2017 14:09:32 +0200 Subject: [PATCH 2387/4033] CLJS-2003 remove redundant calls to `str` in munge/demunge --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 23e1f6d7e..fe82ce9ed 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10801,7 +10801,7 @@ reduces them without incurring seq initialization" :else name')] (if (symbol? name) (symbol name') - (str name')))) + name'))) (defn- demunge-str [munged-name] (let [r (js/RegExp. (demunge-pattern) "g") @@ -10825,7 +10825,7 @@ reduces them without incurring seq initialization" (let [name' (str name)] (if (identical? name' "_DOT__DOT_") ".." - (demunge-str (str name)))))) + (demunge-str name'))))) ;; ----------------------------------------------------------------------------- ;; Bootstrap helpers - incompatible with advanced compilation From b9d957fc9b0bde93a44d70cf54eef952b841ceac Mon Sep 17 00:00:00 2001 From: rauhs Date: Sat, 20 May 2017 14:18:53 +0200 Subject: [PATCH 2388/4033] CLJS-2042: Variadic invoke calls array_seq inefficiently Directly invoke prim_seq instead of going through the dispatcher. --- src/main/clojure/cljs/compiler.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 73498e5d7..38361d9d1 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1027,8 +1027,9 @@ (let [mfa (:max-fixed-arity variadic-invoke)] (emits f "(" (comma-sep (take mfa args)) (when-not (zero? mfa) ",") - "cljs.core.array_seq([" (comma-sep (drop mfa args)) "], 0))")) - + "cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([" + (comma-sep (drop mfa args)) "], 0))")) + (or fn? js? goog?) (emits f "(" (comma-sep args) ")") From a399958cf758a184c0e07cd84bd077c5dceb1a3f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 16 Jun 2017 19:10:18 -0400 Subject: [PATCH 2389/4033] CLJS-2093: inline ^:const var values first cut. Store init expr in the compiler environment. When returning var AST if we are not def'ing a var then analyze :const-init if it exists and add as :const-expr to var AST. During emission of var if :const-expr key exists emit that instead. --- src/main/clojure/cljs/analyzer.cljc | 8 +++++- src/main/clojure/cljs/compiler.cljc | 38 +++++++++++++++-------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 682339f26..93051df71 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1299,6 +1299,7 @@ ([_ sym doc init] {:sym sym :doc doc :init init})) args (apply pfn form) sym (:sym args) + const? (-> sym meta :const) sym-meta (meta sym) tag (-> sym meta :tag) protocol (-> sym meta :protocol valid-proto) @@ -1381,6 +1382,8 @@ "cljs/core.cljs" f))))} (when doc {:doc doc}) + (when const? + {:const-init (:init args)}) (when (true? dynamic) {:dynamic true}) (source-info var-name env) ;; the protocol a protocol fn belongs to @@ -2914,7 +2917,10 @@ (resolve-existing-var env sym) (resolve-var env sym))] (if-not (true? (:def-var env)) - (assoc ret :op :var :info info) + (merge + (assoc ret :op :var :info info) + (when-let [const-init (:const-init info)] + {:const-expr (analyze env const-init)})) (let [info (resolve-var env sym)] (assoc ret :op :var :info info)))))))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 38361d9d1..c7cb1f2c6 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -330,24 +330,26 @@ (defmethod emit* :no-op [m]) (defmethod emit* :var - [{:keys [info env form] :as arg}] - (let [var-name (:name info) - info (if (= (namespace var-name) "js") - (let [js-module-name (get-in @env/*compiler* [:js-module-index (name var-name)])] - (or js-module-name (name var-name))) - info)] - ; We need a way to write bindings out to source maps and javascript - ; without getting wrapped in an emit-wrap calls, otherwise we get - ; e.g. (function greet(return x, return y) {}). - (if (:binding-form? arg) - ; Emit the arg map so shadowing is properly handled when munging - ; (prevents duplicate fn-param-names) - (emits (munge arg)) - (when-not (= :statement (:context env)) - (emit-wrap env - (emits - (cond-> info - (not= form 'js/-Infinity) munge))))))) + [{:keys [info env form] :as ast}] + (if-let [const-expr (:const-expr ast)] + (emit const-expr) + (let [var-name (:name info) + info (if (= (namespace var-name) "js") + (let [js-module-name (get-in @env/*compiler* [:js-module-index (name var-name)])] + (or js-module-name (name var-name))) + info)] + ; We need a way to write bindings out to source maps and javascript + ; without getting wrapped in an emit-wrap calls, otherwise we get + ; e.g. (function greet(return x, return y) {}). + (if (:binding-form? ast) + ; Emit the arg map so shadowing is properly handled when munging + ; (prevents duplicate fn-param-names) + (emits (munge ast)) + (when-not (= :statement (:context env)) + (emit-wrap env + (emits + (cond-> info + (not= form 'js/-Infinity) munge)))))))) (defmethod emit* :var-special [{:keys [env var sym meta] :as arg}] From 7c140bef770ee8a7a9d43d764a178a158a673c59 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Jun 2017 08:51:20 -0400 Subject: [PATCH 2390/4033] change ^:const inlining to be less dramatic of a change, only inline values which are obviously constant --- src/main/clojure/cljs/analyzer.cljc | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 93051df71..2d8c06c56 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -53,7 +53,7 @@ (def ^:dynamic *load-macros* true) (def ^:dynamic *reload-macros* false) (def ^:dynamic *macro-infer* true) - +(def ^:dynamic *passes* nil) (def ^:dynamic *file-defs* nil) (def constants-ns-sym @@ -1289,6 +1289,15 @@ (defn valid-proto [x] (when (symbol? x) x)) +(defn elide-env [env ast opts] + (dissoc ast :env)) + +(defn constant-value? + [{:keys [op] :as ast}] + (or (= :constant op) + (and (#{:map :set :vector :list} op) + (every? constant-value? (:children ast))))) + (defmethod parse 'def [op env form _ _] (when (> (count form) 4) @@ -1383,7 +1392,11 @@ f))))} (when doc {:doc doc}) (when const? - {:const-init (:init args)}) + (let [const-expr + (binding [*passes* (conj *passes* elide-env)] + (analyze env (:init args)))] + (when (constant-value? const-expr) + {:const-expr const-expr}))) (when (true? dynamic) {:dynamic true}) (source-info var-name env) ;; the protocol a protocol fn belongs to @@ -2919,8 +2932,8 @@ (if-not (true? (:def-var env)) (merge (assoc ret :op :var :info info) - (when-let [const-init (:const-init info)] - {:const-expr (analyze env const-init)})) + (when-let [const-expr (:const-expr info)] + {:const-expr const-expr})) (let [info (resolve-var env sym)] (assoc ret :op :var :info info)))))))) @@ -3202,8 +3215,6 @@ ast))) ast))) -(def ^:dynamic *passes* nil) - #?(:clj (defn analyze-form [env form name opts] (load-core) From a6d0b54a057f5cce76dcbb3c5040763db94bb8f6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Jun 2017 09:10:04 -0400 Subject: [PATCH 2391/4033] need to put :const-expr in :context :expr --- src/main/clojure/cljs/analyzer.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2d8c06c56..5c5e3aa79 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1292,6 +1292,10 @@ (defn elide-env [env ast opts] (dissoc ast :env)) +(defn replace-env-pass [new-env] + (fn [env ast opts] + (assoc ast :env new-env))) + (defn constant-value? [{:keys [op] :as ast}] (or (= :constant op) @@ -1393,7 +1397,7 @@ (when doc {:doc doc}) (when const? (let [const-expr - (binding [*passes* (conj *passes* elide-env)] + (binding [*passes* (conj *passes* (replace-env-pass {:context :expr}))] (analyze env (:init args)))] (when (constant-value? const-expr) {:const-expr const-expr}))) From 114a528e08a627904e9c4d2083be58cfc5ff2075 Mon Sep 17 00:00:00 2001 From: rauhs Date: Fri, 16 Jun 2017 17:23:09 +0200 Subject: [PATCH 2392/4033] CLJS-2046: Optimize expression in call position If a function expression like `(@x :m)` is seen, the compiler first binds the `@x` so efficient calls that check for the IFn protocol are emitted. Also add a test for CLJS-855 --- src/main/clojure/cljs/analyzer.cljc | 34 ++++++++++++++++-------- src/test/cljs/cljs/invoke_test.cljs | 16 +++++++++++ src/test/clojure/cljs/compiler_tests.clj | 23 ++++++++++++++-- 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5c5e3aa79..bb59f3298 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2859,7 +2859,18 @@ argc (count args) fn-var? (-> fexpr :info :fn-var) kw? (= 'cljs.core/Keyword (:tag fexpr)) - cur-ns (-> env :ns :name)] + cur-ns (-> env :ns :name) + HO-invoke? (and (boolean *cljs-static-fns*) + (not fn-var?) + (not kw?) + (not (analyzed? f))) + ;; function expressions, eg: ((deref m) x) or ((:x m) :a) + bind-f-expr? (and HO-invoke? + (not (symbol? f))) + ;; Higher order invokes with (some) argument expressions. Bind the arguments + ;; to avoid exponential complexity that is created by the IFn arity check branch. + bind-args? (and HO-invoke? + (not (all-values? args)))] (when ^boolean fn-var? (let [{:keys [^boolean variadic max-fixed-arity method-params name ns macro]} (:info fexpr)] ;; don't warn about invalid arity when when compiling a macros namespace @@ -2881,19 +2892,20 @@ (warning :fn-deprecated env {:fexpr fexpr}))) (when (some? (-> fexpr :info :type)) (warning :invoke-ctor env {:fexpr fexpr})) - (if (or (not (boolean *cljs-static-fns*)) - (not (symbol? f)) - fn-var? - (analyzed? f) - (all-values? args)) + (if (or bind-args? bind-f-expr?) + (let [arg-syms (when bind-args? (take argc (repeatedly gensym))) + f-sym (when bind-f-expr? (gensym "fexpr__")) + bindings (cond-> [] + bind-args? (into (interleave arg-syms args)) + bind-f-expr? (conj f-sym f))] + (analyze env + `(let [~@bindings] + (~(vary-meta (if bind-f-expr? f-sym f) assoc ::analyzed true) + ~@(if bind-args? arg-syms args))))) (let [ana-expr #(analyze enve %) argexprs (map ana-expr args)] {:env env :op :invoke :form form :f fexpr :args (vec argexprs) - :children (into [fexpr] argexprs)}) - (let [arg-syms (take argc (repeatedly gensym))] - (analyze env - `(let [~@(vec (interleave arg-syms args))] - (~(vary-meta f assoc ::analyzed true) ~@arg-syms))))))) + :children (into [fexpr] argexprs)})))) (defn parse-invoke [env form] diff --git a/src/test/cljs/cljs/invoke_test.cljs b/src/test/cljs/cljs/invoke_test.cljs index e25c00b5b..47db42ced 100644 --- a/src/test/cljs/cljs/invoke_test.cljs +++ b/src/test/cljs/cljs/invoke_test.cljs @@ -9,6 +9,22 @@ ([a] a) ([a b] a)) +(defn hof-fn-expr-should-be-bound + [funexpr0 normal-arg] + ((complement funexpr0) normal-arg)) + +(defn hof-arg-should-be-bound + [hofinvoke inv-arg0] + (hofinvoke (inv-arg0))) + +(defn hof-fn-expr+args-should-be-bound + [funexpr1 inv-arg1] + ((complement funexpr1) (inv-arg1))) + +;; A keyword should not be bound in a let: +(def foo (delay + (:dont-bind-this js/x))) + (multi-fn 2) (gstr/urlEncode "foo") diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index a465b1233..96f7cabf5 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -237,8 +237,27 @@ ;; closure js code must never use .call( (is (str/includes? content "goog.string.urlEncode(")) ;; js/goog.string.urlDecode should not use .call - (is (str/includes? content "goog.string.urlDecode("))) - )) + (is (str/includes? content "goog.string.urlDecode(")) + ;; We should NOT emit a let binding for simple (:dont-bind-this js/x) + (is (str/includes? content + (str "new cljs.core.Keyword(null,\"dont-bind-this\",\"dont-bind-this\"," + "-140451389).cljs$core$IFn$_invoke$arity$1(x);"))) + ;; CLJS-2046: Emit bindings for expressions like: (@m a0) or ((:x m) a0) + ;; The test: ((complement funexpr0) normal-arg) + (is (re-find #"(?m)^.*var fexpr.*=.*cljs.core.complement\(funexpr0\);$" + content)) + ;; CLJS-855: Emit binding for expressions like: + ;; (hofinvoke (inv-arg0)) + (is (re-find #"(?m)^.*var .*=.*inv_arg0.cljs.core.IFn._invoke.arity.0 \?.*$" + content)) + + ;; Now test both (855,2046) together: + ;; ((complement funexpr1) (inv-arg1)) + (is (re-find #"(?m)^.*var fexpr.*=.*cljs.core.complement\(funexpr1\);$" + content)) + (is (re-find #"(?m)^.*var .*=.*inv_arg1.cljs.core.IFn._invoke.arity.0 \?.*$" + content))))) +#_(test-vars [#'test-optimized-invoke-emit]) ;; CLJS-1225 From fa041d9c8765858f0196f3e7fff2347caa290eb3 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sat, 17 Jun 2017 16:10:22 +0200 Subject: [PATCH 2393/4033] CLJS-1992: declare after def should have no effect --- src/main/clojure/cljs/analyzer.cljc | 107 ++++++++++++----------- src/test/clojure/cljs/analyzer_tests.clj | 12 +++ 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index bb59f3298..df7f1a366 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1346,8 +1346,6 @@ *file-defs* (get @*file-defs* sym)) (warning :redef-in-file env {:sym sym :line (:line v)}))) - (when *file-defs* - (swap! *file-defs* conj sym)) (let [env (if (or (and (not= ns-name 'cljs.core) (core-name? env sym)) (some? (get-in @env/*compiler* [::namespaces ns-name :uses sym]))) @@ -1380,55 +1378,62 @@ (when (and (not (-> sym meta :declared)) (and (true? (:fn-var v)) (not fn-var?))) (warning :fn-var env {:ns-name ns-name :sym sym}))) - (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] - (merge - {:name var-name} - ;; remove actual test metadata, as it includes non-valid EDN and - ;; cannot be present in analysis cached to disk - David - (cond-> sym-meta - (:test sym-meta) (assoc :test true)) - {:meta (-> sym-meta - (dissoc :test) - (update-in [:file] - (fn [f] - (if (= (-> env :ns :name) 'cljs.core) - "cljs/core.cljs" - f))))} - (when doc {:doc doc}) - (when const? - (let [const-expr - (binding [*passes* (conj *passes* (replace-env-pass {:context :expr}))] - (analyze env (:init args)))] - (when (constant-value? const-expr) - {:const-expr const-expr}))) - (when (true? dynamic) {:dynamic true}) - (source-info var-name env) - ;; the protocol a protocol fn belongs to - (when protocol - {:protocol protocol}) - ;; symbol for reified protocol - (when-let [protocol-symbol (-> sym meta :protocol-symbol)] - {:protocol-symbol protocol-symbol - :info (-> protocol-symbol meta :protocol-info) - :impls #{}}) - (when fn-var? - (let [params (map #(vec (map :name (:params %))) (:methods init-expr))] - (merge - {:fn-var (not (:macro sym-meta)) - ;; protocol implementation context - :protocol-impl (:protocol-impl init-expr) - ;; inline protocol implementation context - :protocol-inline (:protocol-inline init-expr)} - (if-some [top-fn-meta (:top-fn sym-meta)] - top-fn-meta - {:variadic (:variadic init-expr) - :max-fixed-arity (:max-fixed-arity init-expr) - :method-params params - :arglists (:arglists sym-meta) - :arglists-meta (doall (map meta (:arglists sym-meta)))})))) - (if (and fn-var? (some? tag)) - {:ret-tag tag} - (when tag {:tag tag})))) + + ;; declare must not replace any analyzer data of an already def'd sym + (when (or (nil? (get-in @env/*compiler* [::namespaces ns-name :defs sym])) + (not (:declared sym-meta))) + (when *file-defs* + (swap! *file-defs* conj sym)) + + (swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym] + (merge + {:name var-name} + ;; remove actual test metadata, as it includes non-valid EDN and + ;; cannot be present in analysis cached to disk - David + (cond-> sym-meta + (:test sym-meta) (assoc :test true)) + {:meta (-> sym-meta + (dissoc :test) + (update-in [:file] + (fn [f] + (if (= (-> env :ns :name) 'cljs.core) + "cljs/core.cljs" + f))))} + (when doc {:doc doc}) + (when const? + (let [const-expr + (binding [*passes* (conj *passes* (replace-env-pass {:context :expr}))] + (analyze env (:init args)))] + (when (constant-value? const-expr) + {:const-expr const-expr}))) + (when (true? dynamic) {:dynamic true}) + (source-info var-name env) + ;; the protocol a protocol fn belongs to + (when protocol + {:protocol protocol}) + ;; symbol for reified protocol + (when-let [protocol-symbol (-> sym meta :protocol-symbol)] + {:protocol-symbol protocol-symbol + :info (-> protocol-symbol meta :protocol-info) + :impls #{}}) + (when fn-var? + (let [params (map #(vec (map :name (:params %))) (:methods init-expr))] + (merge + {:fn-var (not (:macro sym-meta)) + ;; protocol implementation context + :protocol-impl (:protocol-impl init-expr) + ;; inline protocol implementation context + :protocol-inline (:protocol-inline init-expr)} + (if-some [top-fn-meta (:top-fn sym-meta)] + top-fn-meta + {:variadic (:variadic init-expr) + :max-fixed-arity (:max-fixed-arity init-expr) + :method-params params + :arglists (:arglists sym-meta) + :arglists-meta (doall (map meta (:arglists sym-meta)))})))) + (if (and fn-var? (some? tag)) + {:ret-tag tag} + (when tag {:tag tag}))))) (merge {:env env :op :def diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index bf093bb59..f778f362b 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -709,6 +709,18 @@ (let [form (with-meta 'js/goog.DEBUG {:tag 'boolean})] (is (= (-> (ana-api/analyze (a/empty-env) form) :tag) 'boolean)))) +(deftest test-cljs-1992 ;; declare after def should have no effect + (let [test-cenv (e/default-compiler-env)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-1992) + (defn test-fn [a b c] :foo) + (declare test-fn)] + )) + + (let [def (get-in @test-cenv [::a/namespaces 'test.cljs-1992 :defs 'test-fn])] + (is (:fn-var def))))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From 00c9aec4103c6b13302e40a4cf5cdebea36dfd50 Mon Sep 17 00:00:00 2001 From: rauhs Date: Sat, 17 Jun 2017 16:36:16 +0200 Subject: [PATCH 2394/4033] CLJS-2041: Compiler flag to drop Function.prototype.call invokes Introduce a new compiler option :fn-invoke-direct that, if true, does not generate .call(null...) calls for unknown functions, but instead directly invoke via f(a0,a1...) --- src/main/clojure/cljs/analyzer.cljc | 1 + src/main/clojure/cljs/closure.clj | 16 ++++++++++------ src/main/clojure/cljs/compiler.cljc | 10 ++++++++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index df7f1a366..76a494fb7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -45,6 +45,7 @@ (def ^:dynamic *cljs-file* nil) #?(:clj (def ^:dynamic *unchecked-if* false)) (def ^:dynamic *cljs-static-fns* false) +(def ^:dynamic *fn-invoke-direct* false) (def ^:dynamic *cljs-macros-path* "/cljs/core") (def ^:dynamic *cljs-macros-is-classpath* true) (def ^:dynamic *cljs-dep-set* (with-meta #{} {:dep-path []})) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2e39c62a3..489a6cefa 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -166,7 +166,8 @@ :pretty-print :print-input-delimiter :pseudo-names :recompile-dependents :source-map :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads - :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps}) + :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps + :fn-invoke-direct}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -2245,6 +2246,10 @@ ;; we want to warn about NPM dep conflicts before installing the modules (check-npm-deps opts) (let [compiler-stats (:compiler-stats opts) + static-fns? (or (and (= (:optimizations opts) :advanced) + (not (false? (:static-fns opts)))) + (:static-fns opts) + ana/*cljs-static-fns*) all-opts (-> opts maybe-install-node-deps! add-implicit-options @@ -2266,11 +2271,10 @@ (assoc :sources (-find-sources source all-opts)))) (binding [comp/*recompiled* (when-not (false? (:recompile-dependents opts)) (atom #{})) - ana/*cljs-static-fns* - (or (and (= (:optimizations opts) :advanced) - (not (false? (:static-fns opts)))) - (:static-fns opts) - ana/*cljs-static-fns*) + ana/*cljs-static-fns* static-fns? + ana/*fn-invoke-direct* (or (and static-fns? + (:fn-invoke-direct opts)) + ana/*fn-invoke-direct*) *assert* (not= (:elide-asserts opts) true) ana/*load-tests* (not= (:load-tests opts) false) ana/*cljs-warnings* diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index c7cb1f2c6..1f4605b10 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1039,8 +1039,14 @@ (if (and ana/*cljs-static-fns* (= (:op f) :var)) ;; higher order case, static information missing (let [fprop (str ".cljs$core$IFn$_invoke$arity$" (count args))] - (emits "(" f fprop " ? " f fprop "(" (comma-sep args) ") : " f ".call(" (comma-sep (cons "null" args)) "))")) - (emits f ".call(" (comma-sep (cons "null" args)) ")")))))) + (if ana/*fn-invoke-direct* + (emits "(" f fprop " ? " f fprop "(" (comma-sep args) ") : " + f "(" (comma-sep args) "))") + (emits "(" f fprop " ? " f fprop "(" (comma-sep args) ") : " + f ".call(" (comma-sep (cons "null" args)) "))"))) + (if ana/*fn-invoke-direct* + (emits f "(" (comma-sep args) ")") + (emits f ".call(" (comma-sep (cons "null" args)) ")"))))))) (defmethod emit* :new [{:keys [ctor args env]}] From a08587f00f48a1c548de5d662f9af8325c925349 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 17 Jun 2017 14:49:45 -0400 Subject: [PATCH 2395/4033] CLJS-2098: Set up CI --- .travis.yml | 22 ++++++++++++++++++++++ script/check.py | 11 +++++++++++ 2 files changed, 33 insertions(+) create mode 100644 .travis.yml create mode 100644 script/check.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..5c285fb9d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +dist: trusty + +language: node_js + +node_js: + - "8" + +before_install: + - wget https://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/jsshell-linux-x86_64.zip + - unzip jsshell-linux-x86_64.zip -d spidermoney + - sudo apt-get install -y libjavascriptcoregtk-3.0-bin + +before_script: + - script/bootstrap + - mkdir -p builds/out-adv + - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js + +script: + - jsc builds/out-adv/core-advanced-test.js | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py + - ./spidermoney/js -f builds/out-adv/core-advanced-test.js | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py + - script/test-self-host | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py + - script/test-self-parity | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py diff --git a/script/check.py b/script/check.py new file mode 100644 index 000000000..153e4bafe --- /dev/null +++ b/script/check.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import sys + +data = sys.stdin.read() +for line in data.split('\n'): + try: + if int(line) > 0: + sys.exit(-1) + except ValueError: + pass From 723e1fcdad8f9c5126498d649364dadb97cdc183 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Jun 2017 11:23:35 -0400 Subject: [PATCH 2396/4033] CLJS-2100: to-array calls seq too often optimize to-array-2d as well --- src/main/cljs/cljs/core.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fe82ce9ed..cd5f6afc1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3495,8 +3495,8 @@ reduces them without incurring seq initialization" "Naive impl of to-array as a start." [s] (let [ary (array)] - (loop [s s] - (if (seq s) + (loop [s (seq s)] + (if-not (nil? s) (do (. ary push (first s)) (recur (next s))) ary)))) @@ -3507,7 +3507,7 @@ reduces them without incurring seq initialization" [coll] (let [ret (make-array (count coll))] (loop [i 0 xs (seq coll)] - (when xs + (when-not (nil? xs) (aset ret i (to-array (first xs))) (recur (inc i) (next xs)))) ret)) From 3f588c8b98b20ab7908b4fd9a826689483e477fb Mon Sep 17 00:00:00 2001 From: rauhs Date: Sun, 18 Jun 2017 17:35:22 +0200 Subject: [PATCH 2397/4033] CLJS-2099: Optimize apply by avoiding .apply Avoid javascripts .apply in apply function. Similar to apply-to now walks the passed sequence one by one with first and next and calls the function efficiently. --- src/main/cljs/cljs/core.cljs | 120 +++++++++++++++++++++----------- src/main/clojure/cljs/core.cljc | 48 +++++++++---- 2 files changed, 115 insertions(+), 53 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index cd5f6afc1..367b2a53a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3614,11 +3614,12 @@ reduces them without incurring seq initialization" (defn spread [arglist] - (cond - (nil? arglist) nil - (nil? (next arglist)) (seq (first arglist)) - :else (cons (first arglist) - (spread (next arglist))))) + (when-not (nil? arglist) + (let [n (next arglist)] + (if (nil? n) + (seq (first arglist)) + (cons (first arglist) + (spread n)))))) (defn concat "Returns a lazy seq representing the concatenation of the elements in the supplied colls." @@ -3729,52 +3730,89 @@ reduces them without incurring seq initialization" (gen-apply-to) (set! *unchecked-if* true) + +(defn- apply-to-simple + "Internal. DO NOT USE! + Assumes args was already called with seq beforehand!" + ([f ^seq args] + (if (nil? args) + (if (.-cljs$core$IFn$_invoke$arity$0 f) + (.cljs$core$IFn$_invoke$arity$0 f) + (.call f f)) + (apply-to-simple f (-first args) (next args)))) + ([f a0 ^seq args] + (if (nil? args) + (if (.-cljs$core$IFn$_invoke$arity$1 f) + (.cljs$core$IFn$_invoke$arity$1 f a0) + (.call f f a0)) + (apply-to-simple f a0 (-first args) (next args)))) + ([f a0 a1 ^seq args] + (if (nil? args) + (if (.-cljs$core$IFn$_invoke$arity$2 f) + (.cljs$core$IFn$_invoke$arity$2 f a0 a1) + (.call f f a0 a1)) + (apply-to-simple f a0 a1 (-first args) (next args)))) + ([f a0 a1 a2 ^seq args] + (if (nil? args) + (if (.-cljs$core$IFn$_invoke$arity$3 f) + (.cljs$core$IFn$_invoke$arity$3 f a0 a1 a2) + (.call f f a0 a1 a2)) + (apply-to-simple f a0 a1 a2 (-first args) (next args)))) + ([f a0 a1 a2 a3 ^seq args] + (if (nil? args) + (if (.-cljs$core$IFn$_invoke$arity$4 f) + (.cljs$core$IFn$_invoke$arity$4 f a0 a1 a2 a3) + (.call f f a0 a1 a2 a3)) + (gen-apply-to-simple f 4 args)))) + (defn apply "Applies fn f to the argument list formed by prepending intervening arguments to args." ([f args] - (let [fixed-arity (.-cljs$lang$maxFixedArity f)] - (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count (inc fixed-arity) args)] - (if (<= bc fixed-arity) - (apply-to f bc args) - (.cljs$lang$applyTo f args))) - (.apply f f (to-array args))))) + (if (.-cljs$lang$applyTo f) + (let [fixed-arity (.-cljs$lang$maxFixedArity f) + bc (bounded-count (inc fixed-arity) args)] + (if (<= bc fixed-arity) + (apply-to f bc args) + (.cljs$lang$applyTo f args))) + (apply-to-simple f (seq args)))) ([f x args] + (if (.-cljs$lang$applyTo f) (let [arglist (list* x args) - fixed-arity (.-cljs$lang$maxFixedArity f)] - (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count (inc fixed-arity) arglist)] - (if (<= bc fixed-arity) - (apply-to f bc arglist) - (.cljs$lang$applyTo f arglist))) - (.apply f f (to-array arglist))))) + fixed-arity (.-cljs$lang$maxFixedArity f) + bc (inc (bounded-count fixed-arity args))] + (if (<= bc fixed-arity) + (apply-to f bc arglist) + (.cljs$lang$applyTo f arglist))) + (apply-to-simple f x (seq args)))) ([f x y args] + (if (.-cljs$lang$applyTo f) (let [arglist (list* x y args) - fixed-arity (.-cljs$lang$maxFixedArity f)] - (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count (inc fixed-arity) arglist)] - (if (<= bc fixed-arity) - (apply-to f bc arglist) - (.cljs$lang$applyTo f arglist))) - (.apply f f (to-array arglist))))) + fixed-arity (.-cljs$lang$maxFixedArity f) + bc (+ 2 (bounded-count (dec fixed-arity) args))] + (if (<= bc fixed-arity) + (apply-to f bc arglist) + (.cljs$lang$applyTo f arglist))) + (apply-to-simple f x y (seq args)))) ([f x y z args] + (if (.-cljs$lang$applyTo f) (let [arglist (list* x y z args) - fixed-arity (.-cljs$lang$maxFixedArity f)] - (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count (inc fixed-arity) arglist)] - (if (<= bc fixed-arity) - (apply-to f bc arglist) - (.cljs$lang$applyTo f arglist))) - (.apply f f (to-array arglist))))) + fixed-arity (.-cljs$lang$maxFixedArity f) + bc (+ 3 (bounded-count (- fixed-arity 2) args))] + (if (<= bc fixed-arity) + (apply-to f bc arglist) + (.cljs$lang$applyTo f arglist))) + (apply-to-simple f x y z (seq args)))) ([f a b c d & args] - (let [arglist (cons a (cons b (cons c (cons d (spread args))))) - fixed-arity (.-cljs$lang$maxFixedArity f)] - (if (.-cljs$lang$applyTo f) - (let [bc (bounded-count (inc fixed-arity) arglist)] - (if (<= bc fixed-arity) - (apply-to f bc arglist) - (.cljs$lang$applyTo f arglist))) - (.apply f f (to-array arglist)))))) + (if (.-cljs$lang$applyTo f) + (let [spread-args (spread args) + arglist (cons a (cons b (cons c (cons d spread-args)))) + fixed-arity (.-cljs$lang$maxFixedArity f) + bc (+ 4 (bounded-count (- fixed-arity 3) spread-args))] + (if (<= bc fixed-arity) + (apply-to f bc arglist) + (.cljs$lang$applyTo f arglist))) + (apply-to-simple f a b c d (spread args))))) + (set! *unchecked-if* false) (defn vary-meta diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index fcf018e6b..93f613c19 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -44,7 +44,7 @@ defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? specify copy-arguments goog-define js-comment js-inline-comment - unsafe-cast require-macros use-macros])]) + unsafe-cast require-macros use-macros gen-apply-to-simple])]) #?(:cljs (:require-macros [cljs.core :as core] [cljs.support :refer [assert-args]])) (:require clojure.walk @@ -2668,17 +2668,13 @@ (core/defn- gen-apply-to-helper ([] (gen-apply-to-helper 1)) ([n] - (core/let [prop (symbol (core/str "-cljs$core$IFn$_invoke$arity$" n)) - f (symbol (core/str "cljs$core$IFn$_invoke$arity$" n))] - (if (core/<= n 20) - `(let [~(cs (core/dec n)) (-first ~'args) - ~'args (-rest ~'args)] - (if (== ~'argc ~n) - (if (. ~'f ~prop) - (. ~'f (~f ~@(take n cs))) - (~'f ~@(take n cs))) - ~(gen-apply-to-helper (core/inc n)))) - `(throw (js/Error. "Only up to 20 arguments supported on functions")))))) + (if (core/<= n 20) + `(let [~(cs (core/dec n)) (-first ~'args) + ~'args (-rest ~'args)] + (if (== ~'argc ~n) + (~'f ~@(take n cs)) + ~(gen-apply-to-helper (core/inc n)))) + `(throw (js/Error. "Only up to 20 arguments supported on functions"))))) (core/defmacro gen-apply-to [] `(do @@ -2690,6 +2686,34 @@ ~(gen-apply-to-helper)))) (set! ~'*unchecked-if* false))) +(core/defn- gen-apply-to-simple-helper + [f num-args args] + (core/let [new-arg-sym (symbol (core/str "a" num-args)) + proto-name (core/str "cljs$core$IFn$_invoke$arity$" (core/inc num-args)) + proto-prop (symbol (core/str ".-" proto-name)) + proto-inv (symbol (core/str "." proto-name)) + next-sym (symbol (core/str "next_" num-args)) + all-args (mapv #(symbol (core/str "a" %)) (range (core/inc num-args)))] + `(let [~new-arg-sym (cljs.core/-first ~args) + ~next-sym (cljs.core/next ~args)] + (if (nil? ~next-sym) + (if (~proto-prop ~f) + (~proto-inv ~f ~@all-args) + (.call ~f ~f ~@all-args)) + ~(if (core/<= 19 num-args) + ;; We've exhausted all protocols, fallback to .apply: + `(let [arr# (cljs.core/array ~@all-args)] + (loop [s# ~next-sym] + (when s# + (do (.push arr# (cljs.core/-first s#)) + (recur (cljs.core/next s#))))) + (.apply ~f ~f arr#)) + (gen-apply-to-simple-helper f (core/inc num-args) next-sym)))))) + +(core/defmacro gen-apply-to-simple + [f num-args args] + (gen-apply-to-simple-helper f num-args args)) + (core/defmacro with-out-str "Evaluates exprs in a context in which *print-fn* is bound to .append on a fresh StringBuffer. Returns the string created by any nested From f45a6b193b988c9059b244c64ac19a501a7bfb2b Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Mon, 19 Jun 2017 13:09:38 -0500 Subject: [PATCH 2398/4033] CLJS-2104: Const-replaced exprs do not emit js "return" When const-replacement is done during emission, we must ensure we use the :context of the *replaced* form, not the const form. --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1f4605b10..9a9cf8329 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -332,7 +332,7 @@ (defmethod emit* :var [{:keys [info env form] :as ast}] (if-let [const-expr (:const-expr ast)] - (emit const-expr) + (emit (assoc const-expr :env env)) (let [var-name (:name info) info (if (= (namespace var-name) "js") (let [js-module-name (get-in @env/*compiler* [:js-module-index (name var-name)])] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 6d06ce3db..648b1f0a4 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1327,6 +1327,16 @@ (is (= :fail (try (subvec nil 1 nil) (catch js/Error e :fail)))))) +(def ^:const cljs-2104 "cljs-2104") + +(deftest test-const-emission + (testing "const exprs emission context, not definition context (CLJS-2104)" + (is (= cljs-2104 "cljs-2104")) + (is (= (if-some [x true] + cljs-2104 + "unreachable") + "cljs-2104")))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 7089c9d51ab8b1011560dc4b8ca7ad92c77e8fdc Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 19 Jun 2017 14:16:41 -0400 Subject: [PATCH 2399/4033] CLJS-2105: Add Nashorn CI test coverage --- .travis.yml | 1 + src/test/cljs/cljs/pprint_test.cljs | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5c285fb9d..19b8cac36 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,5 +18,6 @@ before_script: script: - jsc builds/out-adv/core-advanced-test.js | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py - ./spidermoney/js -f builds/out-adv/core-advanced-test.js | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py + - jjs builds/out-adv/core-advanced-test.js | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py - script/test-self-host | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py - script/test-self-parity | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py diff --git a/src/test/cljs/cljs/pprint_test.cljs b/src/test/cljs/cljs/pprint_test.cljs index 7a9b926f8..0bfb50339 100644 --- a/src/test/cljs/cljs/pprint_test.cljs +++ b/src/test/cljs/cljs/pprint_test.cljs @@ -1070,16 +1070,18 @@ Usage: *hello* | {:a is-a} | 1 | | 5 | 7 | " - ;;This test is changed a bit due to the way JS prints large numbers. The number - ;; was changed (54.7e17 to 54.7e20) to make sure JS prints it in E notation (5.47E21) + ;; This test is changed a bit due to the way JS prints large numbers, as well as the + ;; way Nashorn formats floating point output using Java. The number was changed + ;; (54.7e17 to 54.7e21) to make sure JS prints it in E notation (5.47E22) and Nashorn + ;; truncates at the desired precision. (with-out-str (print-table [:a :e :d :c] - [{:a 54.7e20 :b {:a 'is-a} :c ["hi" "there"]} + [{:a 54.7e21 :b {:a 'is-a} :c ["hi" "there"]} {:b 5 :a -23 :c "dog" :d 'panda}])) " | :a | :e | :d | :c | |----------+----+-------+----------------| -| 5.47e+21 | | | [\"hi\" \"there\"] | +| 5.47e+22 | | | [\"hi\" \"there\"] | | -23 | | panda | dog | " ) From 97c166c67a25e23d088c4eb40ec4dd1f0dd14e35 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 19 Jun 2017 18:36:43 -0400 Subject: [PATCH 2400/4033] CLJS-2106: Revise CI so you can see test output --- .travis.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 19b8cac36..2e01e7b0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,13 @@ before_script: - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js script: - - jsc builds/out-adv/core-advanced-test.js | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py - - ./spidermoney/js -f builds/out-adv/core-advanced-test.js | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py - - jjs builds/out-adv/core-advanced-test.js | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py - - script/test-self-host | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py - - script/test-self-parity | grep -o '[[:digit:]]*' | tail -n 2 | python script/check.py + - jsc builds/out-adv/core-advanced-test.js | tee test-out.txt + - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - ./spidermoney/js -f builds/out-adv/core-advanced-test.js | tee test-out.txt + - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - jjs builds/out-adv/core-advanced-test.js | tee test-out.txt + - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - script/test-self-host | tee test-out.txt + - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - script/test-self-parity | tee test-out.txt + - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py From 293b7ddb7c26d525e8fe33c0cf1707cee52c6f8b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 19 Jun 2017 21:10:02 -0400 Subject: [PATCH 2401/4033] CLJS-2101: Undeclared var in do chain of defs We need to force the compiler state analysis side effects which occur when analyzing the chain of forms inside do form, prior to analyzing the last form in the do. --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/analyzer_tests.clj | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 76a494fb7..55afdf955 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1666,7 +1666,7 @@ :children (conj (vec (map :init bes)) expr)})) (defn analyze-do-statements* [env exprs] - (seq (map #(analyze (assoc env :context :statement) %) (butlast exprs)))) + (seq (doall (map #(analyze (assoc env :context :statement) %) (butlast exprs))))) (defn analyze-do-statements [env exprs] (disallowing-recur (analyze-do-statements* env exprs))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index f778f362b..cad258e3d 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -721,6 +721,18 @@ (let [def (get-in @test-cenv [::a/namespaces 'test.cljs-1992 :defs 'test-fn])] (is (:fn-var def))))) +(deftest test-cljs-2101 + (let [test-cenv (e/default-compiler-env)] + (e/with-compiler-env test-cenv + (a/analyze-form-seq + ['(ns test.cljs-2101) + `(do + ;; Splice in 32 forms in order to consume first chunk in chunked sequence + ~@(range 32) + (def ~'x32 1) + ;; The previous def must be analyzed for subsequent var special to succeed + (def ~'x33 (var ~'x32)))])))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From 711ec34af603e2e8cdbf043fea9f56d9733c24c5 Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Tue, 20 Jun 2017 13:54:16 -0500 Subject: [PATCH 2402/4033] CLJS-2111: Transit analysis caching broken for JSValue or regex Transit read-handler and write-handler functions have the wrong arity: they do not receive the WriteHandler or ReadHandler as an argument, only the object or form itself. --- src/main/clojure/cljs/analyzer.cljc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 55afdf955..58fd84194 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -70,8 +70,8 @@ read-handler-map @(ns-resolve ns 'read-handler-map)] {:handlers (read-handler-map - {"cljs/js" (read-handler (fn [_ v] (JSValue. v))) - "cljs/regex" (read-handler (fn [_ v] (Pattern/compile v)))})})) + {"cljs/js" (read-handler (fn [v] (JSValue. v))) + "cljs/regex" (read-handler (fn [v] (Pattern/compile v)))})})) (catch Throwable t nil)))) @@ -86,12 +86,12 @@ (write-handler-map {JSValue (write-handler - (fn [_ _] "cljs/js") - (fn [_ js] (.val ^JSValue js))) + (fn [_] "cljs/js") + (fn [js] (.val ^JSValue js))) Pattern (write-handler - (fn [_ _] "cljs/regex") - (fn [_ pat] (.pattern ^Pattern pat)))})})) + (fn [_] "cljs/regex") + (fn [pat] (.pattern ^Pattern pat)))})})) (catch Throwable t nil)))) From 13faa48b396772a9b4bb8e9986d7bd0759ec05d1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 19 Jun 2017 22:05:36 -0400 Subject: [PATCH 2403/4033] CLJS-2107: Add lein test to CI --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2e01e7b0a..34cfee480 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ before_script: - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js script: + - lein test - jsc builds/out-adv/core-advanced-test.js | tee test-out.txt - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py - ./spidermoney/js -f builds/out-adv/core-advanced-test.js | tee test-out.txt From fb14b183e6dc2485f67ba7f7f668981dd7cdfd3c Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Wed, 14 Jun 2017 15:38:20 +0100 Subject: [PATCH 2404/4033] CLJS-2108: faster set equivalence Use backing maps IKVReduce to avoid making seqs and entry objects. --- src/main/cljs/cljs/core.cljs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 367b2a53a..1db64f04c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8662,8 +8662,10 @@ reduces them without incurring seq initialization" (and (set? other) (== (count coll) (count other)) - (every? #(contains? coll %) - other))) + ^boolean + (reduce-kv + #(or (contains? other %2) (reduced false)) + true hash-map))) IHash (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) @@ -8811,8 +8813,10 @@ reduces them without incurring seq initialization" (and (set? other) (== (count coll) (count other)) - (every? #(contains? coll %) - other))) + ^boolean + (reduce-kv + #(or (contains? other %2) (reduced false)) + true tree-map))) IHash (-hash [coll] (caching-hash coll hash-unordered-coll __hash)) From d6f0ec1abdbe77f56e19bebf6e3483b58d0a3ee4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 23 Jun 2017 10:30:16 -0400 Subject: [PATCH 2405/4033] CLJS-2115: Pass not-found in the native-satisfies? branch of nth --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1db64f04c..8b1203715 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1813,7 +1813,7 @@ reduces them without incurring seq initialization" (linear-traversal-nth coll n not-found) (native-satisfies? IIndexed coll) - (-nth coll n) + (-nth coll n not-found) :else (throw (js/Error. (str "nth not supported on this type " From d96bb1bb90c46bf361fe8001eff1d0222109e509 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 23 Jun 2017 09:16:02 -0400 Subject: [PATCH 2406/4033] CLJS-2113: nth function produces different results from clojure when using a negative index on a sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix range’s implementation of IIndexed for negative values of n --- src/main/cljs/cljs/core.cljs | 8 ++++---- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8b1203715..65ff4e72f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9174,15 +9174,15 @@ reduces them without incurring seq initialization" IIndexed (-nth [rng n] - (if (< n (-count rng)) + (if (and (<= 0 n) (< n (-count rng))) (+ start (* n step)) - (if (and (> start end) (zero? step)) + (if (and (<= 0 n) (> start end) (zero? step)) start (throw (js/Error. "Index out of bounds"))))) (-nth [rng n not-found] - (if (< n (-count rng)) + (if (and (<= 0 n) (< n (-count rng))) (+ start (* n step)) - (if (and (> start end) (zero? step)) + (if (and (<= 0 n) (> start end) (zero? step)) start not-found))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 648b1f0a4..46936eab8 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1337,6 +1337,12 @@ "unreachable") "cljs-2104")))) +(deftest test-cljs-2113 + (is (thrown? js/Error (nth (range 2) -2))) + (is (thrown? js/Error (nth (range 2 1 0) -2))) + (is (= ::not-found (nth (range 2) -2 ::not-found))) + (is (= ::not-found (nth (range 2 1 0) -2 ::not-found)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 43a344317cbd80d8b581de1219ce1552f8a3045f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 21 Jun 2017 16:27:16 -0400 Subject: [PATCH 2407/4033] Revert "CLJS-2082: Support generative testing" This reverts commit 73447c54c9a108a3def58f8dc39aff0b31bcb537. # Conflicts: # project.clj # script/bootstrap --- project.clj | 2 +- script/bootstrap | 2 +- script/test-self-parity | 4 ++-- src/test/cljs/cljs/core_test.cljs | 8 -------- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/project.clj b/project.clj index 61c0196da..25bc66350 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0"] - [org.clojure/test.check "0.10.0-alpha1" :scope "test"] + [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] [com.google.javascript/closure-compiler-unshaded "v20170521"] diff --git a/script/bootstrap b/script/bootstrap index 9b12a61a0..6c9930cc2 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -11,7 +11,7 @@ TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.0.0" -TEST_CHECK_RELEASE="0.10.0-alpha1" +TEST_CHECK_RELEASE="0.9.0" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } diff --git a/script/test-self-parity b/script/test-self-parity index 39a99049c..bf3c4b641 100755 --- a/script/test-self-parity +++ b/script/test-self-parity @@ -10,10 +10,10 @@ if [ ! -f lib/clojure.jar ]; then exit 1 fi jar xvf lib/clojure.jar clojure/template.clj > /dev/null -jar xvf lib/test.check.jar clojure/test/check.cljc clojure/test/check/results.cljc clojure/test/check/clojure_test.cljc clojure/test/check/impl.cljc clojure/test/check/properties.cljc clojure/test/check/generators.cljc clojure/test/check/random.clj clojure/test/check/random/doubles.cljs clojure/test/check/random/longs/bit_count_impl.cljs clojure/test/check/random/longs.cljs clojure/test/check/random.cljs clojure/test/check/rose_tree.cljc > /dev/null +jar xvf lib/test.check.jar clojure/test/check/random clojure/test/check/generators.cljc clojure/test/check/rose_tree.cljc > /dev/null mkdir -p builds/out-self-parity/clojure/test mv clojure/template.clj builds/out-self-parity/clojure -mv clojure/test builds/out-self-parity/clojure +mv clojure/test/check builds/out-self-parity/clojure/test if ! bin/cljsc src/test/self/self_parity "{:optimizations :none :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}"; then >&2 echo ClojureScript compilation failed diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 46936eab8..5009cae0d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -9,10 +9,6 @@ (ns cljs.core-test (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] - [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])) @@ -548,10 +544,6 @@ (is (= 0 (loop [x 0] (cond-> x false recur)))) (is (= 0 (loop [x 0] (cond->> x false recur))))) -(defspec boolean-test 10 - (prop/for-all [b gen/boolean] - (boolean? b))) - ;; ============================================================================= ;; Tickets From 4e48a3448efa24987cfef6fb676439a7b131f017 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Wed, 3 Aug 2016 17:08:55 +0100 Subject: [PATCH 2408/4033] CLJS-2112: Iterator based reduce path Introduces iter-reduce to support reduction of iterable collections efficiently. --- src/main/cljs/cljs/core.cljs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 65ff4e72f..bd3843cd0 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2373,6 +2373,29 @@ reduces them without incurring seq initialization" (garray/shuffle a) (vec a))) +(defn- iter-reduce + ([coll f] + (let [iter (-iterator coll)] + (if (.hasNext iter) + (let [init (.next iter)] + (loop [acc init] + (if (.hasNext iter) + (let [nacc (f acc (.next iter))] + (if (reduced? nacc) + @nacc + (recur nacc))) + acc))) + (f)))) + ([coll f init] + (let [iter (-iterator coll)] + (loop [acc init] + (if (.hasNext iter) + (let [nacc (f acc (.next iter))] + (if (reduced? nacc) + @nacc + (recur nacc))) + acc))))) + (defn reduce "f should be a function of 2 arguments. If val is not supplied, returns the result of applying f to the first 2 items in coll, then @@ -2397,6 +2420,9 @@ reduces them without incurring seq initialization" (native-satisfies? IReduce coll) (-reduce coll f) + (iterable? coll) + (iter-reduce coll f) + :else (seq-reduce f coll))) ([f val coll] @@ -2413,6 +2439,9 @@ reduces them without incurring seq initialization" (native-satisfies? IReduce coll) (-reduce coll f val) + (iterable? coll) + (iter-reduce coll f val) + :else (seq-reduce f val coll)))) @@ -6505,9 +6534,9 @@ reduces them without incurring seq initialization" IReduce (-reduce [coll f] - (seq-reduce f coll)) + (iter-reduce coll f)) (-reduce [coll f start] - (seq-reduce f start coll)) + (iter-reduce coll f start)) IFn (-invoke [coll k] From 86c79aea9c658ed3d532dea22a53cdf89ff5f056 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Jun 2017 11:38:37 -0400 Subject: [PATCH 2409/4033] add missing ^boolean type hints to iter-reduce --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index bd3843cd0..0686cf01f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2379,7 +2379,7 @@ reduces them without incurring seq initialization" (if (.hasNext iter) (let [init (.next iter)] (loop [acc init] - (if (.hasNext iter) + (if ^boolean (.hasNext iter) (let [nacc (f acc (.next iter))] (if (reduced? nacc) @nacc @@ -2389,7 +2389,7 @@ reduces them without incurring seq initialization" ([coll f init] (let [iter (-iterator coll)] (loop [acc init] - (if (.hasNext iter) + (if ^boolean (.hasNext iter) (let [nacc (f acc (.next iter))] (if (reduced? nacc) @nacc From f3886c9f38248d9525ce6cf85d6aadeaf4a09bbf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Jun 2017 12:09:30 -0400 Subject: [PATCH 2410/4033] CLJS-2109: incorrect syntax-quote symbol resolution (resolve-symbol 'clojure.core) -> 'clojure/core --- src/main/clojure/cljs/analyzer.cljc | 19 +++++++++++++------ src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 58fd84194..3aa1e39ea 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -842,6 +842,13 @@ (or (js-tag (next pre) tag-type externs' top) (js-tag (into '[prototype] (next pre)) tag-type (get top tag) top))))))) +(defn dotted-symbol? [sym] + (let [s (str sym)] + #?(:clj (and (.contains s ".") + (not (.contains s ".."))) + :cljs (and ^boolean (goog.string/contains s ".") + (not ^boolean (goog.string/contains s "..")))))) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -887,10 +894,7 @@ {:name (symbol (str full-ns) (str (name sym))) :ns full-ns})) - #?(:clj (and (.contains s ".") - (not (.contains s ".."))) - :cljs (and ^boolean (goog.string/contains s ".") - (not ^boolean (goog.string/contains s "..")))) + (dotted-symbol? sym) (let [idx (.indexOf s ".") prefix (symbol (subs s 0 idx)) suffix (subs s (inc idx))] @@ -3318,8 +3322,11 @@ (instance? File x) (.getAbsolutePath ^File x) :default (str x)))) -(defn resolve-symbol [s] - (:name (resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*)) s))) +(defn resolve-symbol [sym] + (if (and (not (namespace sym)) + (dotted-symbol? sym)) + sym + (:name (resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*)) sym)))) #?(:clj (defn forms-seq* diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5009cae0d..19887e08a 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1335,6 +1335,10 @@ (is (= ::not-found (nth (range 2) -2 ::not-found))) (is (= ::not-found (nth (range 2 1 0) -2 ::not-found)))) +(deftest test-cljs-2109 + (testing "Syntax quoted dotted symbol without namespace should resolve to itself" + (is (= 'clojure.core `clojure.core)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 18f580cf76015cbec0cfe213c49feff85a222706 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 23 Jun 2017 12:40:01 -0400 Subject: [PATCH 2411/4033] CLJS-2116: Need to handle un-namespaced symbol when evaluating `foo.core --- src/main/cljs/cljs/js.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 5b808a31b..6fdbf115c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -42,9 +42,10 @@ (defn- drop-macros-suffix [ns-name] - (if (string/ends-with? ns-name "$macros") - (subs ns-name 0 (- (count ns-name) 7)) - ns-name)) + (when ns-name + (if (string/ends-with? ns-name "$macros") + (subs ns-name 0 (- (count ns-name) 7)) + ns-name))) (defn- elide-macros-suffix [sym] From f119f6596cb136ddcb4d35e76156124a331a4e8b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Jun 2017 16:43:25 -0400 Subject: [PATCH 2412/4033] CLJS-1989: s/fdef expansion side effect fails when load cached source lift analysis cache reading into a helper. Handle writing out spec vars in write-analysis-cache and read-analysis-cache. --- src/main/clojure/cljs/analyzer.cljc | 63 +++++++++++++++++++---------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3aa1e39ea..313bbbf05 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3561,6 +3561,11 @@ (write-analysis-cache ns cache-file nil)) ([ns ^File cache-file src] (util/mkdirs cache-file) + (when-let [spec-ns (find-ns 'cljs.spec.alpha)] + (when-let [speced-vars (ns-resolve spec-ns '_speced_vars)] + (let [ns-str (str ns)] + (swap! env/*compiler* update-in [::namespaces ns :cljs.spec/speced-vars] + (fnil into #{}) (filter #(= ns-str (namespace %))) @@speced-vars)))) (let [ext (util/ext cache-file) analysis (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)] (case ext @@ -3576,6 +3581,41 @@ (when src (.setLastModified ^File cache-file (util/last-modified src)))))) +#?(:clj + (defn read-analysis-cache + ([cache-file src] + (read-analysis-cache cache-file src nil)) + ([^File cache-file src opts] + ;; we want want to keep dependency analysis information + ;; don't revert the environment - David + (let [{:keys [ns]} (parse-ns src + (merge opts + {:restore false + :analyze-deps true + :load-macros true})) + ext (util/ext cache-file) + cached-ns (case ext + "edn" (edn/read-string (slurp cache-file)) + "json" (let [{:keys [reader read]} @transit] + (with-open [is (io/input-stream cache-file)] + (read (reader is :json transit-read-opts)))))] + (when (or *verbose* (:verbose opts)) + (util/debug-prn "Reading analysis cache for" (str src))) + (swap! env/*compiler* + (fn [cenv] + (do + (when-let [vars (seq (:cljs.spec/speced-vars cached-ns))] + (try + (require 'cljs.spec.alpha) + (catch Throwable t)) + (when-let [spec-ns (find-ns 'cljs.spec.alpha)] + (when-let [speced-vars (ns-resolve spec-ns '_speced_vars)] + (swap! @speced-vars into vars)))) + (doseq [x (get-in cached-ns [::constants :order])] + (register-constant! x)) + (-> cenv + (assoc-in [::namespaces ns] cached-ns))))))))) + (defn analyze-form-seq ([forms] (analyze-form-seq forms @@ -3659,27 +3699,6 @@ (when (and cache (true? (:cache-analysis opts))) (write-analysis-cache ns cache res)))) (try - ;; we want want to keep dependency analysis information - ;; don't revert the environment - David - (let [{:keys [ns]} (parse-ns res - (merge opts - {:restore false - :analyze-deps true - :load-macros true})) - ext (util/ext cache) - cached-ns (case ext - "edn" (edn/read-string (slurp cache)) - "json" (let [{:keys [reader read]} @transit] - (with-open [is (io/input-stream cache)] - (read (reader is :json transit-read-opts)))))] - (when (or *verbose* (:verbose opts)) - (util/debug-prn "Reading analysis cache for" (str res))) - (swap! env/*compiler* - (fn [cenv] - (let [] - (doseq [x (get-in cached-ns [::constants :order])] - (register-constant! x)) - (-> cenv - (assoc-in [::namespaces ns] cached-ns)))))) + (read-analysis-cache cache res opts) (catch Throwable e (analyze-file f true opts)))))))))))) From 38c12da934284780779889125faa6667fc4fe13a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 23 Jun 2017 17:59:22 -0400 Subject: [PATCH 2413/4033] CLJS-2117: Self-host: Port CLJS-1989 to self-hosted --- src/main/cljs/cljs/js.cljs | 5 +++- src/main/clojure/cljs/analyzer.cljc | 46 +++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 6fdbf115c..51291e5bd 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -13,6 +13,7 @@ (:require [clojure.string :as string] [clojure.walk :as walk] [cljs.env :as env] + [cljs.spec.alpha] [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.tools.reader :as r] @@ -288,7 +289,8 @@ ((:*eval-fn* bound-vars) resource) (when cache (load-analysis-cache! - (:*compiler* bound-vars) aname cache)) + (:*compiler* bound-vars) aname cache) + (ana/register-cached-speced-vars cache)) (when source-map (load-source-map! (:*compiler* bound-vars) aname source-map)) @@ -922,6 +924,7 @@ (when (:source-map opts) (append-source-map env/*compiler* aname source sb @comp/*source-map-data* opts)) + (ana/dump-speced-vars-to-env aname) (let [js-source (.toString sb) evalm {:lang :clj :name name diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 313bbbf05..7dccbd0d7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3555,17 +3555,45 @@ true (util/changed? src cache))))))) +#?(:clj + (defn- get-speced-vars + [] + (when-let [spec-ns (find-ns 'cljs.spec.alpha)] + (ns-resolve spec-ns '_speced_vars))) + :cljs + ;; Here, we look up the symbol '-speced-vars because ns-interns* + ;; is implemented by invoking demunge on the result of js-keys. + (let [cached-var (delay (get (ns-interns* 'cljs.spec.alpha$macros) '-speced-vars))] + (defn- get-speced-vars [] + (when (some? (find-ns-obj 'cljs.spec.alpha$macros)) + @cached-var)))) + +(defn dump-speced-vars-to-env + "Dumps registered speced vars for a given namespace into the compiler + environment." + [ns] + (when-let [speced-vars (get-speced-vars)] + (let [ns-str (str ns)] + (swap! env/*compiler* update-in [::namespaces ns :cljs.spec/speced-vars] + (fnil into #{}) (filter #(= ns-str (namespace %))) @@speced-vars)))) + +(defn register-cached-speced-vars + "Registers speced vars found in a namespace analysis cache." + [cached-ns] + (when-let [vars (seq (:cljs.spec/speced-vars cached-ns))] + #?(:clj (try + (require 'cljs.spec.alpha) + (catch Throwable t))) + (when-let [speced-vars (get-speced-vars)] + (swap! @speced-vars into vars)))) + #?(:clj (defn write-analysis-cache ([ns cache-file] (write-analysis-cache ns cache-file nil)) ([ns ^File cache-file src] (util/mkdirs cache-file) - (when-let [spec-ns (find-ns 'cljs.spec.alpha)] - (when-let [speced-vars (ns-resolve spec-ns '_speced_vars)] - (let [ns-str (str ns)] - (swap! env/*compiler* update-in [::namespaces ns :cljs.spec/speced-vars] - (fnil into #{}) (filter #(= ns-str (namespace %))) @@speced-vars)))) + (dump-speced-vars-to-env ns) (let [ext (util/ext cache-file) analysis (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)] (case ext @@ -3604,13 +3632,7 @@ (swap! env/*compiler* (fn [cenv] (do - (when-let [vars (seq (:cljs.spec/speced-vars cached-ns))] - (try - (require 'cljs.spec.alpha) - (catch Throwable t)) - (when-let [spec-ns (find-ns 'cljs.spec.alpha)] - (when-let [speced-vars (ns-resolve spec-ns '_speced_vars)] - (swap! @speced-vars into vars)))) + (register-cached-speced-vars cached-ns) (doseq [x (get-in cached-ns [::constants :order])] (register-constant! x)) (-> cenv From 7de5a02f0f6c202482eac313ea528d5753dadbc9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Jun 2017 20:16:39 -0400 Subject: [PATCH 2414/4033] refactor spec var caching fns name --- src/main/cljs/cljs/js.cljs | 4 ++-- src/main/clojure/cljs/analyzer.cljc | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 51291e5bd..2cf0d9e04 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -290,7 +290,7 @@ (when cache (load-analysis-cache! (:*compiler* bound-vars) aname cache) - (ana/register-cached-speced-vars cache)) + (ana/register-specs cache)) (when source-map (load-source-map! (:*compiler* bound-vars) aname source-map)) @@ -924,7 +924,7 @@ (when (:source-map opts) (append-source-map env/*compiler* aname source sb @comp/*source-map-data* opts)) - (ana/dump-speced-vars-to-env aname) + (ana/dump-specs aname) (let [js-source (.toString sb) evalm {:lang :clj :name name diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7dccbd0d7..c6639d9e7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3568,7 +3568,7 @@ (when (some? (find-ns-obj 'cljs.spec.alpha$macros)) @cached-var)))) -(defn dump-speced-vars-to-env +(defn dump-specs "Dumps registered speced vars for a given namespace into the compiler environment." [ns] @@ -3577,7 +3577,7 @@ (swap! env/*compiler* update-in [::namespaces ns :cljs.spec/speced-vars] (fnil into #{}) (filter #(= ns-str (namespace %))) @@speced-vars)))) -(defn register-cached-speced-vars +(defn register-specs "Registers speced vars found in a namespace analysis cache." [cached-ns] (when-let [vars (seq (:cljs.spec/speced-vars cached-ns))] @@ -3593,7 +3593,7 @@ (write-analysis-cache ns cache-file nil)) ([ns ^File cache-file src] (util/mkdirs cache-file) - (dump-speced-vars-to-env ns) + (dump-specs ns) (let [ext (util/ext cache-file) analysis (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)] (case ext @@ -3632,7 +3632,7 @@ (swap! env/*compiler* (fn [cenv] (do - (register-cached-speced-vars cached-ns) + (register-specs cached-ns) (doseq [x (get-in cached-ns [::constants :order])] (register-constant! x)) (-> cenv From a6ff1a729fb27ceb5c5beef684a8b03d1961b9b3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Jun 2017 20:38:59 -0400 Subject: [PATCH 2415/4033] dump / restore cljs.spec.alpha/registry-ref to / from analysis caches --- src/main/clojure/cljs/analyzer.cljc | 45 ++++++++++++++++++----------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c6639d9e7..e87e16472 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3556,36 +3556,47 @@ (util/changed? src cache))))))) #?(:clj - (defn- get-speced-vars + (defn- get-spec-vars [] (when-let [spec-ns (find-ns 'cljs.spec.alpha)] - (ns-resolve spec-ns '_speced_vars))) + {:registry-ref (ns-resolve spec-ns 'registry-ref) + :speced-vars (ns-resolve spec-ns '_speced_vars)})) :cljs - ;; Here, we look up the symbol '-speced-vars because ns-interns* - ;; is implemented by invoking demunge on the result of js-keys. - (let [cached-var (delay (get (ns-interns* 'cljs.spec.alpha$macros) '-speced-vars))] - (defn- get-speced-vars [] + (let [registry-ref (delay (get (ns-interns* 'cljs.spec.alpha$macros) 'registry-ref)) + ;; Here, we look up the symbol '-speced-vars because ns-interns* + ;; is implemented by invoking demunge on the result of js-keys. + speced-vars (delay (get (ns-interns* 'cljs.spec.alpha$macros) '-speced-vars))] + (defn- get-spec-vars [] (when (some? (find-ns-obj 'cljs.spec.alpha$macros)) - @cached-var)))) + {:registry-ref @registry-ref + :speced-vars @speced-vars})))) (defn dump-specs "Dumps registered speced vars for a given namespace into the compiler environment." [ns] - (when-let [speced-vars (get-speced-vars)] - (let [ns-str (str ns)] - (swap! env/*compiler* update-in [::namespaces ns :cljs.spec/speced-vars] - (fnil into #{}) (filter #(= ns-str (namespace %))) @@speced-vars)))) + (let [spec-vars (get-spec-vars) + ns-str (str ns)] + (swap! env/*compiler* update-in [::namespaces ns] + merge + (when-let [registry-ref (:registry-ref spec-vars)] + {:cljs.spec/registry-ref (into [] (filter (fn [[k _]] (= ns-str (namespace k)))) @@registry-ref)}) + (when-let [speced-vars (:speced-vars spec-vars)] + {:cljs.spec/speced-vars (into [] (filter #(= ns-str (namespace %))) @@speced-vars)})))) (defn register-specs "Registers speced vars found in a namespace analysis cache." [cached-ns] - (when-let [vars (seq (:cljs.spec/speced-vars cached-ns))] - #?(:clj (try - (require 'cljs.spec.alpha) - (catch Throwable t))) - (when-let [speced-vars (get-speced-vars)] - (swap! @speced-vars into vars)))) + #?(:clj (try + (require 'cljs.spec.alpha) + (catch Throwable t))) + (let [{:keys [registry-ref speced-vars]} (get-spec-vars)] + (when-let [registry (seq (:cljs.spec/registry-ref cached-ns))] + (when registry-ref + (swap! @registry-ref merge registry))) + (when-let [vars (seq (:cljs.spec/speced-vars cached-ns))] + (when speced-vars + (swap! @speced-vars into vars))))) #?(:clj (defn write-analysis-cache From 055f39bed26bc85af962e85f3bfb6e3a33016714 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Jun 2017 07:55:09 -0400 Subject: [PATCH 2416/4033] CLJS-2122: Self-host: Non-symbol ns names dumped into env --- src/main/cljs/cljs/js.cljs | 3 ++- src/test/self/self_host/test.cljs | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2cf0d9e04..36e9a294d 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -924,7 +924,8 @@ (when (:source-map opts) (append-source-map env/*compiler* aname source sb @comp/*source-map-data* opts)) - (ana/dump-specs aname) + (when (symbol? aname) + (ana/dump-specs aname)) (let [js-source (.toString sb) evalm {:lang :clj :name name diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index fa5925c10..6f4e4ae48 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -891,6 +891,31 @@ (is (nil? error)) (inc! l)))))) +(deftest test-cljs-2122 + (async done + (let [st (cljs/empty-state) + l (latch 2 done)] + (cljs/eval-str + st + "1" + nil + {:context :expr + :eval node-eval} + (fn [{:keys [error] :as m}] + (is (nil? error)) + (is (every? symbol? (keys (get-in @st [:cljs.analyzer/namespaces])))) + (inc! l))) + (cljs/eval-str + st + "1" + "A string name" + {:context :expr + :eval node-eval} + (fn [{:keys [error] :as m}] + (is (nil? error)) + (is (every? symbol? (keys (get-in @st [:cljs.analyzer/namespaces])))) + (inc! l)))))) + (defn -main [& args] (run-tests)) From ebe1b266a8fd9dda88f74abe4023976fcba45c88 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Jun 2017 09:18:01 -0400 Subject: [PATCH 2417/4033] CLJS-2124: Self-host: Tests failing wth Could not find tag parser for :cljs.spec.alpha --- src/test/self/self_parity/test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 4c6535cea..3dc287aba 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -18,7 +18,7 @@ (:require [clojure.string :as string] [cljs.nodejs :as nodejs] [cljs.js :as cljs] - [cljs.reader :as reader] + [cljs.tools.reader :as reader] [cljs.stacktrace :as st])) (def out-dir "builds/out-self-parity") From c77eeeb1c317267dc4a1ac13c7fc8ac836eba34e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Jun 2017 08:01:23 -0400 Subject: [PATCH 2418/4033] CLJS-2123: CI doesn't catch case if compile fails early --- .travis.yml | 10 +++++----- script/check.py | 11 ----------- 2 files changed, 5 insertions(+), 16 deletions(-) delete mode 100644 script/check.py diff --git a/.travis.yml b/.travis.yml index 34cfee480..63f88dd19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,12 +18,12 @@ before_script: script: - lein test - jsc builds/out-adv/core-advanced-test.js | tee test-out.txt - - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - grep '0 failures, 0 errors.' test-out.txt - ./spidermoney/js -f builds/out-adv/core-advanced-test.js | tee test-out.txt - - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - grep '0 failures, 0 errors.' test-out.txt - jjs builds/out-adv/core-advanced-test.js | tee test-out.txt - - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - grep '0 failures, 0 errors.' test-out.txt - script/test-self-host | tee test-out.txt - - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - grep '0 failures, 0 errors.' test-out.txt - script/test-self-parity | tee test-out.txt - - grep -o '[[:digit:]]*' test-out.txt | tail -n 2 | python script/check.py + - grep '0 failures, 0 errors.' test-out.txt diff --git a/script/check.py b/script/check.py deleted file mode 100644 index 153e4bafe..000000000 --- a/script/check.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import sys - -data = sys.stdin.read() -for line in data.split('\n'): - try: - if int(line) > 0: - sys.exit(-1) - except ValueError: - pass From d3473f8700ed356e8503541d60542bb7c12813ed Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Jun 2017 07:14:02 -0400 Subject: [PATCH 2419/4033] CLJS-2121: Self-host: Document string as valid name arg --- src/main/cljs/cljs/js.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 36e9a294d..97aa7c175 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -623,7 +623,7 @@ source (string) the ClojureScript source - name (symbol) + name (symbol or string) optional, the name of the source opts (map) @@ -817,7 +817,7 @@ source (string) the ClojureScript source - name (symbol) + name (symbol or string) optional, the name of the source opts (map) @@ -957,7 +957,7 @@ source (string) the ClojureScript source - name (symbol) + name (symbol or string) optional, the name of the source opts (map) From c7cf897f4fd84c6b1879708c4aeb3dc96f9207bd Mon Sep 17 00:00:00 2001 From: OHTA Shogo Date: Sat, 24 Jun 2017 16:40:15 +0900 Subject: [PATCH 2420/4033] CLJS-2119: s/form for s/& is qualified with `clojure.spec.alpha` --- src/main/cljs/cljs/spec/alpha.cljs | 2 +- src/test/cljs/cljs/spec_test.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index c990c2d40..cc19ab662 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -1100,7 +1100,7 @@ (case op ::accept nil nil p - ::amp (list* 'clojure.spec.alpha/& (op-describe p1) forms) + ::amp (list* 'cljs.spec.alpha/& (op-describe p1) forms) ::pcat (if rep+ (list `+ rep+) (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) forms))) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 4fdccd92a..e2331a836 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -211,7 +211,7 @@ andre nil nil nil andre [] nil nil - andre :k ::s/invalid '[{:pred (clojure.spec.alpha/& (cljs.spec.alpha/* cljs.core/keyword?) cljs.spec-test/even-count?), :val :k}] + andre :k ::s/invalid '[{:pred (cljs.spec.alpha/& (cljs.spec.alpha/* cljs.core/keyword?) cljs.spec-test/even-count?), :val :k}] andre [:k] ::s/invalid '[{:pred cljs.spec-test/even-count?, :val [:k]}] andre [:j :k] [:j :k] nil From 940b6c8ffd98754f3dfc0e7fbe0878235f62debd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jun 2017 12:45:49 -0400 Subject: [PATCH 2421/4033] CLJS-2125: Duplicate HOF invoke warnings if :static-fns true --- src/main/clojure/cljs/analyzer.cljc | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e87e16472..f3f4656fa 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2847,10 +2847,14 @@ :js-op js-op :numeric numeric}))) +(defn analyzed + [x] + (vary-meta x assoc ::analyzed true)) + (defn- analyzed? #?(:cljs {:tag boolean}) - [f] - (contains? (meta f) ::analyzed)) + [x] + (boolean (::analyzed (meta x)))) (defn- all-values? #?(:cljs {:tag boolean}) @@ -2907,11 +2911,11 @@ f-sym (when bind-f-expr? (gensym "fexpr__")) bindings (cond-> [] bind-args? (into (interleave arg-syms args)) - bind-f-expr? (conj f-sym f))] + bind-f-expr? (conj f-sym (analyzed f)))] (analyze env - `(let [~@bindings] - (~(vary-meta (if bind-f-expr? f-sym f) assoc ::analyzed true) - ~@(if bind-args? arg-syms args))))) + `(let [~@bindings] + (~(analyzed (if bind-f-expr? f-sym f)) + ~@(if bind-args? arg-syms args))))) (let [ana-expr #(analyze enve %) argexprs (map ana-expr args)] {:env env :op :invoke :form form :f fexpr :args (vec argexprs) @@ -3296,6 +3300,11 @@ ast (analyze-form env form name opts)] (reduce (fn [ast pass] (pass env ast opts)) ast passes))) +(defn- warnings-for [form] + (if (analyzed? form) + (zipmap (keys *cljs-warnings*) (repeat false)) + *cljs-warnings*)) + (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context (one of :statement, :expr, :return), :ns (a symbol naming the @@ -3311,7 +3320,8 @@ ([env form name opts] (ensure (wrapping-errors env - (binding [reader/*alias-map* (or reader/*alias-map* {})] + (binding [*cljs-warnings* (warnings-for form) + reader/*alias-map* (or reader/*alias-map* {})] (analyze* env form name opts)))))) #?(:clj From 01a1427e45c9726244940baeeb37b7511acbd8b1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Jun 2017 17:34:49 -0400 Subject: [PATCH 2422/4033] CLJS-2054: Private core names still result in "already declared" warnings --- src/main/clojure/cljs/analyzer.cljc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f3f4656fa..05e30d061 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -767,6 +767,17 @@ false)) (not (contains? (-> env :ns :excludes) sym)))) +(defn public-name? + "Is sym public?" + #?(:cljs {:tag boolean}) + [ns sym] + (let [var-ast (or (gets @env/*compiler* ::namespaces ns :defs sym) + #?(:clj (gets @env/*compiler* ::namespaces ns :macros sym) + :cljs (gets @env/*compiler* ::namespaces (symbol (str (name ns) "$macros")) :defs sym)))] + (and (some? var-ast) + (not (or (:private var-ast) + (:anonymous var-ast)))))) + (defn js-tag? [x] (and (symbol? x) (or (= 'js x) @@ -1354,9 +1365,10 @@ (let [env (if (or (and (not= ns-name 'cljs.core) (core-name? env sym)) (some? (get-in @env/*compiler* [::namespaces ns-name :uses sym]))) - (let [ev (resolve-existing-var (dissoc env :locals) sym) + (let [ev (resolve-existing-var (dissoc env :locals) (with-meta sym {::no-resolve true})) conj-to-set (fnil conj #{})] - (warning :redef env {:sym sym :ns (:ns ev) :ns-name ns-name}) + (when (public-name? (:ns ev) sym) + (warning :redef env {:sym sym :ns (:ns ev) :ns-name ns-name})) (swap! env/*compiler* update-in [::namespaces ns-name :excludes] conj-to-set sym) (update-in env [:ns :excludes] conj-to-set sym)) From fb162392269601ee26429041924ed370dfd42e5e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jun 2017 13:06:15 -0400 Subject: [PATCH 2423/4033] comment on CLJS-2054 patch subtlety --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 05e30d061..ed1a20665 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1365,7 +1365,10 @@ (let [env (if (or (and (not= ns-name 'cljs.core) (core-name? env sym)) (some? (get-in @env/*compiler* [::namespaces ns-name :uses sym]))) - (let [ev (resolve-existing-var (dissoc env :locals) (with-meta sym {::no-resolve true})) + (let [ev (resolve-existing-var (dissoc env :locals) + ;; ::no-resolve true is to suppress "can't take value + ;; of macro warning" when sym resolves to a macro + (with-meta sym {::no-resolve true})) conj-to-set (fnil conj #{})] (when (public-name? (:ns ev) sym) (warning :redef env {:sym sym :ns (:ns ev) :ns-name ns-name})) From 69342169ba868574aa7f5a88669e6333ccc3df01 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jun 2017 13:16:42 -0400 Subject: [PATCH 2424/4033] docstrings for analyzed helpers --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ed1a20665..90d325aea 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2863,10 +2863,13 @@ :numeric numeric}))) (defn analyzed + "Mark a form as being analyzed. Assumes x satisfies IMeta. Useful to suppress + warnings that will have been caught by a first compiler pass." [x] (vary-meta x assoc ::analyzed true)) -(defn- analyzed? +(defn analyzed? + "Returns boolean if the form has already been marked as analyzed." #?(:cljs {:tag boolean}) [x] (boolean (::analyzed (meta x)))) From 3fe1afac7583131f4b56709105173df279d10ea6 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 25 Jun 2017 14:55:06 -0400 Subject: [PATCH 2425/4033] CLJS-2126: Add new compiler option :fn-invoke-direct to build-affecting options --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 9a9cf8329..3cfd8957f 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1209,7 +1209,7 @@ (.getPath (.toURL (.toURI f))))) (defn- build-affecting-options [opts] - (select-keys opts [:static-fns :optimize-constants :elide-asserts :target])) + (select-keys opts [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target])) #?(:clj (defn compiled-by-string From 776b02b4d292edb4e06a87f51eeee07a437aa64c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jun 2017 16:02:02 -0400 Subject: [PATCH 2426/4033] support new compiler option :fn-invoke-direct at the REPL --- src/main/clojure/cljs/repl.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index eb0565e93..03d557e67 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -762,7 +762,9 @@ (println (str "WARNING: Unknown option '" unknown-opt "'. Did you mean '" suggested-opt "'?")))) (let [repl-opts (-repl-options repl-env) repl-requires (into repl-requires (:repl-requires repl-opts)) - {:keys [analyze-path repl-verbose warn-on-undeclared special-fns static-fns] :as opts + {:keys [analyze-path repl-verbose warn-on-undeclared special-fns + static-fns fn-invoke-direct] + :as opts :or {warn-on-undeclared true}} (merge {:cache-analysis true :source-map true :def-emits-var true} @@ -806,6 +808,7 @@ warn-on-undeclared))) {:infer-warning false})) ana/*cljs-static-fns* static-fns + ana/*fn-invoke-direct* (and static-fns fn-invoke-direct) *repl-opts* opts] (let [env {:context :expr :locals {}} special-fns (merge default-special-fns special-fns) From a041251c6c9988a5e32ea0e6a9bbf4ceac3252c8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Jun 2017 16:05:26 -0400 Subject: [PATCH 2427/4033] support new compiler option :fn-invoke-direct in self-host --- src/main/cljs/cljs/js.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 97aa7c175..b4a145fea 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -575,6 +575,7 @@ (binding [env/*compiler* (:*compiler* bound-vars) ana/*cljs-ns* ns ana/*cljs-static-fns* (:static-fns opts) + ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) ana/*passes* (:*passes* bound-vars) r/*alias-map* (current-alias-map) @@ -678,6 +679,7 @@ *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) ana/*cljs-static-fns* (:static-fns opts) + ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns (:*cljs-ns* bound-vars)) r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) @@ -767,6 +769,7 @@ *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* ns ana/*cljs-static-fns* (:static-fns opts) + ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) @@ -879,6 +882,7 @@ *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* ns ana/*cljs-static-fns* (:static-fns opts) + ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) r/*alias-map* (current-alias-map) r/*data-readers* (:*data-readers* bound-vars) From f406b8aa68379de140592c3e923fd6725f4373f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 26 Jun 2017 08:19:41 -0400 Subject: [PATCH 2428/4033] fix bug from CLJS-1886 patch, must check backing vector not the subvec --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0686cf01f..59c84e2c7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5556,7 +5556,7 @@ reduces them without incurring seq initialization" IIterable (-iterator [coll] - (if (implements? APersistentVector coll) + (if (implements? APersistentVector v) (ranged-iterator v start end) (seq-iter coll)))) From 9dc57c6c42e13eea405bc850cba53f583fd00916 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Fri, 23 Jun 2017 17:36:26 +0100 Subject: [PATCH 2429/4033] CLJS-2128: Fix regression in CLJS-1886 Also adds fallback for Subvec.reduce when backing vector is not a APersistentVector --- src/main/cljs/cljs/core.cljs | 8 ++++++-- src/test/cljs/cljs/collections_test.cljs | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 59c84e2c7..7e0335aa5 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5534,9 +5534,13 @@ reduces them without incurring seq initialization" IReduce (-reduce [coll f] - (pv-reduce v f start end)) + (if (implements? APersistentVector v) + (pv-reduce v f start end) + (ci-reduce coll f))) (-reduce [coll f init] - (pv-reduce v f init start end)) + (if (implements? APersistentVector v) + (pv-reduce v f init start end) + (ci-reduce coll f init))) IKVReduce (-kv-reduce [coll f init] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 4f33db350..f9d2276c0 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -660,3 +660,26 @@ (is (= metadata (meta k')))) (let [map (sorted-map nil :foo)] (is (= (find map nil) [nil :foo]))))) + +(deftype CustomVectorThing [v] + IVector + (-assoc-n [coll i val] (assoc-n v i val)) + + IIndexed + (-nth [coll i] (nth v i)) + (-nth [coll i not-found] (nth v i not-found)) + + ICounted + (-count [coll] (count v))) + +(deftest test-cljs-2128 + (testing "Subvec iteration" + (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" + (testing "Subvec over PersistentVector reduces as expected" + (is (= [1 2] (reduce conj [] (subvec [0 1 2 3] 1 3))))) + (testing "Subvec over other vectors reduces as expected" + (is (= [1 2] (reduce conj [] (subvec (->CustomVectorThing [0 1 2 3]) 1 3))))))) From 3ec81ca771393c96ea20ef30bcbe78153f2bcc63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 26 Jun 2017 16:52:21 -0700 Subject: [PATCH 2430/4033] CLJS-2130: Self-host: Add `:fn-invoke-direct` to public API docstrings --- src/main/cljs/cljs/js.cljs | 140 ++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 64 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index b4a145fea..2cb651508 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -630,21 +630,24 @@ opts (map) compilation options. - :eval - eval function to invoke, see *eval-fn* - :load - library resolution function, see *load-fn* - :source-map - set to true to generate inline source map information - :def-emits-var - sets whether def (and derived) forms return either a Var - (if set to true) or the def init value (if false). Default - is false. - :static-fns - employ static dispatch to specific function arities in - emitted JavaScript, as opposed to making use of the - `call` construct. Default is false. - :ns - optional, the namespace in which to evaluate the source. - :verbose - optional, emit details from compiler activity. Defaults to - false. - :context - optional, sets the context for the source. Possible values - are `:expr`, `:statement` and `:return`. Defaults to - `:expr`. + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :def-emits-var - sets whether def (and derived) forms return either a Var + (if set to true) or the def init value (if false). + Defaults to false. + :static-fns - employ static dispatch to specific function arities in + emitted JavaScript, as opposed to making use of the + `call` construct. Defaults to false. + :fn-invoke-direct - if `true`, does not generate `.call(null...)` calls for + unknown functions, but instead direct invokes via + `f(a0,a1...)`. Defaults to `false`. + :ns - optional, the namespace in which to evaluate the source. + :verbose - optional, emit details from compiler activity. Defaults to + false. + :context - optional, sets the context for the source. Possible values + are `:expr`, `:statement` and `:return`. Defaults to + `:expr`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -720,21 +723,24 @@ opts (map) compilation options. - :eval - eval function to invoke, see *eval-fn* - :load - library resolution function, see *load-fn* - :source-map - set to true to generate inline source map information - :def-emits-var - sets whether def (and derived) forms return either a Var - (if set to true) or the def init value (if false). Default - is false. - :static-fns - employ static dispatch to specific function arities in - emitted JavaScript, as opposed to making use of the - `call` construct. Default is false. - :ns - optional, the namespace in which to evaluate the source. - :verbose - optional, emit details from compiler activity. Defaults to - false. - :context - optional, sets the context for the source. Possible values - are `:expr`, `:statement` and `:return`. Defaults to - `:expr`. + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :def-emits-var - sets whether def (and derived) forms return either a Var + (if set to true) or the def init value (if false). Default + is false. + :static-fns - employ static dispatch to specific function arities in + emitted JavaScript, as opposed to making use of the + `call` construct. Defaults to false. + :fn-invoke-direct - if `true`, does not generate `.call(null...)` calls for + unknown functions, but instead direct invokes via + `f(a0,a1...)`. Defaults to `false`. + :ns - optional, the namespace in which to evaluate the source. + :verbose - optional, emit details from compiler activity. Defaults to + false. + :context - optional, sets the context for the source. Possible values + are `:expr`, `:statement` and `:return`. Defaults to + `:expr`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -826,21 +832,24 @@ opts (map) compilation options. - :eval - eval function to invoke, see *eval-fn* - :load - library resolution function, see *load-fn* - :source-map - set to true to generate inline source map information - :def-emits-var - sets whether def (and derived) forms return either a Var - (if set to true) or the def init value (if false). Default - is false. - :static-fns - employ static dispatch to specific function arities in - emitted JavaScript, as opposed to making use of the - `call` construct. Default is false. - :ns - optional, the namespace in which to evaluate the source. - :verbose - optional, emit details from compiler activity. Defaults to - false. - :context - optional, sets the context for the source. Possible values - are `:expr`, `:statement` and `:return`. Defaults to - `:expr`. + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :def-emits-var - sets whether def (and derived) forms return either a Var + (if set to true) or the def init value (if false). Default + is false. + :static-fns - employ static dispatch to specific function arities in + emitted JavaScript, as opposed to making use of the + `call` construct. Defaults to false. + :fn-invoke-direct - if `true`, does not generate `.call(null...)` calls for + unknown functions, but instead direct invokes via + `f(a0,a1...)`. Defaults to `false`. + :ns - optional, the namespace in which to evaluate the source. + :verbose - optional, emit details from compiler activity. Defaults to + false. + :context - optional, sets the context for the source. Possible values + are `:expr`, `:statement` and `:return`. Defaults to + `:expr`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -967,25 +976,28 @@ opts (map) compilation options. - :eval - eval function to invoke, see *eval-fn* - :load - library resolution function, see *load-fn* - :source-map - set to true to generate inline source map information - :cache-source - optional, a function to run side-effects with the - compilation result prior to actual evalution. This function - takes two arguments, the first is the eval map, the source - will be under :source. The second argument is a callback of - one argument. If an error occurs an :error key should be - supplied. - :def-emits-var - sets whether def (and derived) forms return either a Var - (if set to true) or the def init value (if false). Default - is false. - :static-fns - employ static dispatch to specific function arities in - emitted JavaScript, as opposed to making use of the - `call` construct. Default is false. - :ns - optional, the namespace in which to evaluate the source. - :verbose - optional, emit details from compiler activity. Defaults to - false. - :context - optional, sets the context for the source. Possible values + :eval - eval function to invoke, see *eval-fn* + :load - library resolution function, see *load-fn* + :source-map - set to true to generate inline source map information + :cache-source - optional, a function to run side-effects with the + compilation result prior to actual evalution. This function + takes two arguments, the first is the eval map, the source + will be under :source. The second argument is a callback of + one argument. If an error occurs an :error key should be + supplied. + :def-emits-var - sets whether def (and derived) forms return either a Var + (if set to true) or the def init value (if false). Default + is false. + :static-fns - employ static dispatch to specific function arities in + emitted JavaScript, as opposed to making use of the + `call` construct. Defaults to false. + :fn-invoke-direct - if `true`, does not generate `.call(null...)` calls for + unknown functions, but instead direct invokes via + `f(a0,a1...)`. Defaults to `false`. + :ns - optional, the namespace in which to evaluate the source. + :verbose - optional, emit details from compiler activity. Defaults to + false. + :context - optional, sets the context for the source. Possible values are `:expr`, `:statement` and `:return`. Defaults to `:expr`. From 519b74d9655410b62f191685b1c93a45bb01edcf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 26 Jun 2017 22:07:29 -0400 Subject: [PATCH 2431/4033] 1.9.655 --- README.md | 6 ++--- changes.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c2a23f7ea..5f5cdf4e0 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.562 +Latest stable release: 1.9.655 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.562"] +[org.clojure/clojurescript "1.9.655"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.562 org.clojure clojurescript - 1.9.562 + 1.9.655 ``` diff --git a/changes.md b/changes.md index 77e126f61..00bfd9b1f 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,79 @@ +## 1.9.655 + +### Enhancements +* CLJS-2108: faster set equivalence +* CLJS-2099: Optimize apply by avoiding .apply +* CLJS-2046: Optimize expression in call position +* CLJS-1876: Faster reduce for PV, Subvec and ChunkedSeq +* CLJS-2080: Faster equiv-map +* CLJS-2066: Avoid analyzing named fn literal bodies twice +* CLJS-2065: Improve analyzer munge performance + +### Changes +* CLJS-2130: Self-host: Add `:fn-invoke-direct` to public API docstrings +* CLJS-2112: Iterator based reduce path +* CLJS-2100: to-array calls seq too often +* CLJS-2041: Compiler flag to drop Function.prototype.call invokes +* CLJS-2093: inline ^:const var values +* CLJS-2042: Variadic invoke calls array_seq inefficiently +* CLJS-2003 remove redundant calls to `str` in munge/demunge +* CLJS-1907: Improve error message from cljs.reader/read-string +* CLJS-1724: Include IIterable in fast-path-protocols +* CLJS-924: Better error message for mistaken use of 'def' +* CLJS-1599: UUIDs are not equal for upper/lower case strings +* NodeJS REPL accepts a :path opt to set NODE_PATH +* CLJS-1886: RangedIterator should only be created from cljs.core.PersistentVector instances +* CLJS-2068: MapEntry, RedNode and BlackNode are IComparable +* CLJS-2073: Don't flush for every emitted line +* CLJS-2089: Warn message wrong for recur to protocol with nil +* CLJS-2085: defrecord recur method head target object +* CLJS-1977: defrecord should use murmur hashing like Clojure +* CLJS-2076: modules should support wildcard namespaces +* CLJS-2078: add resolve macro + +### Fixes +* CLJS-2128: Fix regression in CLJS-1886 +* CLJS-2126: Add new compiler option :fn-invoke-direct to build-affecting options +* CLJS-2054: Private core names still result in "already declared" warnings +* CLJS-2125: Duplicate HOF invoke warnings if :static-fns true +* CLJS-2119: s/form for s/& is qualified with `clojure.spec.alpha` +* CLJS-2121: Self-host: Document string as valid name arg +* CLJS-2124: Self-host: Tests failing wth Could not find tag parser for :cljs.spec.alpha +* CLJS-2122: Self-host: Non-symbol ns names dumped into env +* CLJS-2117: Self-host: Port CLJS-1989 to self-hosted +* CLJS-1989: s/fdef expansion side effect fails when load cached source +* CLJS-2116: Need to handle un-namespaced symbol when evaluating `foo.core +* CLJS-2109: incorrect syntax-quote symbol resolution (resolve-symbol 'clojure.core) -> 'clojure/core +* CLJS-2113: nth function produces different results from clojure when using a negative index on a sequence +* CLJS-2115: Pass not-found in the native-satisfies? branch of nth +* CLJS-2111: Transit analysis caching broken for JSValue or regex +* CLJS-2101: Undeclared var in do chain of defs +* CLJS-2104: Const-replaced exprs do not emit js "return" +* CLJS-1992: declare after def should have no effect +* CLJS-1251: Missing semicolons when emitting deftype and defrecord mistaken use of 'def' +* CLJS-1685: Incorrectly lazy subvec when start param is nil +* CLJS-1641: Multi-arity defn copies arguments unnecessarily for all cases +* CLJS-2092: Redundant call to equiv-map in PAM.-equiv +* Check for compilation success, and lib folder +* CLJS-2030: Case with grouped keyword test emit result-expr multiple times +* CLJS-2094: Predicates unit tests constructs a uuid with nil +* CLJS-1891: UUID.toString can return non-string +* CLJS-2072: Eliminate reflection in cljs.js-deps/build-index +* CLJS-2012: Find on PHM with nil entry always returns nil entry +* CLJS-2057: fix language-in options (es6 deprecated and add missing es2016) +* CLJS-2060: Backport CLJ-2141 Return only true/false from qualified-* predicates +* CLJS-2091: reify docstring ISeqable example needs correction +* CLJS-2088: fix caching collision between macros ns and regular ns in boostrap +* CLJS-2036: Relative path exception thrown when :preloads requires a :foreign-lib +* CLJS-2083: Test equiv-map for maps which do not impl IKVReduce +* CLJS-2081: Self-host: Regression with CLJS-2079 +* CLJS-2079: Records and maps are not equal +* CLJS-2075: PersistentTreeMap.reduce-kv does not honor reduced? +* Browser REPL regression +* CLJS-2069: Self-host: automatic `clojure` -> `cljs` aliasing doesn't work when loading macro namespaces +* CLJS-2067: reduce-kv / inode-kv-reduce fails to honor reduced? +* CLJS-2056: Self-host: test-self-parity failing wrt cljs.core/fn symbol + ## 1.9.562 ### Enhancements From f0f67832bc236ce5f2c40900bf72021aef9ffe6b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 27 Jun 2017 09:34:58 -0400 Subject: [PATCH 2432/4033] CLJS-2133: Invalid variadic IFn implementations now fail hoist `self__` --- src/main/clojure/cljs/compiler.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 3cfd8957f..6c4cee54b 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -744,6 +744,8 @@ mname (munge name) delegate-name (str mname "__delegate")] (emitln "(function() { ") + (when type + (emitln "var self__ = this;")) (emits "var " delegate-name " = function (") (doseq [param params] (emit param) @@ -760,8 +762,6 @@ (if variadic (concat (butlast params) ['var_args]) params)) "){") - (when type - (emitln "var self__ = this;")) (when variadic (emits "var ") (emit (last params)) From b340a6eef39d671bb6f4e69b8fe62d9dfe7d5e12 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 27 Jun 2017 09:52:55 -0400 Subject: [PATCH 2433/4033] clarify confusing extend-type docstring --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 93f613c19..6f2688a7c 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1572,7 +1572,7 @@ (-count [c] ...) Foo (bar [x y] ...) - (baz ([x] ...) ([x y & zs] ...))" + (baz ([x] ...) ([x y] ...) ...)" [type-sym & impls] (core/let [env &env _ (validate-impls env impls) From 3417d468e9f3b4c8c527806eaf3e70922b5c2242 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 27 Jun 2017 10:03:38 -0400 Subject: [PATCH 2434/4033] CLJS-2134: Warn on variadic signatures in protocol method implementation as well --- src/main/clojure/cljs/analyzer.cljc | 6 ++++++ src/main/clojure/cljs/core.cljc | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 90d325aea..08a32ff24 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -138,6 +138,7 @@ :protocol-duped-method true :protocol-multiple-impls true :protocol-with-variadic-method true + :protocol-impl-with-variadic-method true :protocol-impl-recur-with-target true :single-segment-namespace true :munged-namespace true @@ -360,6 +361,11 @@ (str "Protocol " (:protocol info) " declares method " (:name info) " with variadic signature (&)")) +(defmethod error-message :protocol-impl-with-variadic-method + [warning-type info] + (str "Protocol " (:protocol info) " implements method " + (:name info) " with variadic signature (&)")) + (defmethod error-message :protocol-impl-recur-with-target [warning-type info] (str "Ignoring target object \"" (pr-str (:form info)) "\" passed in recur to protocol method head")) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 6f2688a7c..006a56e08 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1498,6 +1498,8 @@ c (count sig)] (core/when (contains? seen c) (ana/warning :protocol-duped-method env {:protocol p :fname fname})) + (core/when (some '#{&} sig) + (ana/warning :protocol-impl-with-variadic-method env {:protocol p :name fname})) (core/when (core/and (not= decmeths ::not-found) (not (some #{c} (map count decmeths)))) (ana/warning :protocol-invalid-method env {:protocol p :fname fname :invalid-arity c})) (recur (next sigs) (conj seen c)))))))) From 809f23ae11f1a02c45a69e0cc9532b1709385066 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 27 Jun 2017 10:25:36 -0400 Subject: [PATCH 2435/4033] CLJS-2133: Invalid variadic IFn implementations now fail revert self__ hoisting, need to emit self__ ref in variadic delegate --- src/main/clojure/cljs/compiler.cljc | 6 ++++-- src/test/cljs/cljs/core_test.cljs | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 6c4cee54b..688f7f022 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -744,13 +744,13 @@ mname (munge name) delegate-name (str mname "__delegate")] (emitln "(function() { ") - (when type - (emitln "var self__ = this;")) (emits "var " delegate-name " = function (") (doseq [param params] (emit param) (when-not (= param (last params)) (emits ","))) (emitln "){") + (when type + (emitln "var self__ = this;")) (when recurs (emitln "while(true){")) (emits expr) (when recurs @@ -762,6 +762,8 @@ (if variadic (concat (butlast params) ['var_args]) params)) "){") + (when type + (emitln "var self__ = this;")) (when variadic (emits "var ") (emit (last params)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 19887e08a..7819e18e7 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1339,6 +1339,16 @@ (testing "Syntax quoted dotted symbol without namespace should resolve to itself" (is (= 'clojure.core `clojure.core)))) +(deftype Partial [f args] + IFn + (-invoke [_ & a] + (apply (apply partial f args) a))) + +(deftest test-cljs-2133 + (testing "Invalid variadic IFn implementation should work" + (let [p (Partial. + [1])] + (p 2)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 5c227e85bdc2fa92bf3f8125082dc8a9e5558fea Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 27 Jun 2017 11:02:58 -0400 Subject: [PATCH 2436/4033] 1.9.660 --- README.md | 6 +++--- changes.md | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5f5cdf4e0..3c2da2ec9 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.655 +Latest stable release: 1.9.660 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.655"] +[org.clojure/clojurescript "1.9.660"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.655 org.clojure clojurescript - 1.9.655 + 1.9.660 ``` diff --git a/changes.md b/changes.md index 00bfd9b1f..d76d0c92a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,11 @@ +## 1.9.660 + +### Changes +* CLJS-2134: Warn on variadic signatures in protocol method implementation + +### Fixes +* CLJS-2133: Invalid variadic IFn implementations now fail + ## 1.9.655 ### Enhancements From 004107aceb3f477fcda8d332e6ea881f4eda67ff Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Jun 2017 16:14:38 -0400 Subject: [PATCH 2437/4033] fix :fn-invoke-direct edgecase around keywords --- src/main/clojure/cljs/compiler.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 688f7f022..932a1b809 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -974,8 +974,9 @@ (= (get (string/split ns-str #"\.") 0 nil) "goog")) (not (contains? (::ana/namespaces @env/*compiler*) ns)))) - keyword? (and (= (-> f :op) :constant) - (keyword? (-> f :form))) + keyword? (or (= 'cljs.core/Keyword (ana/infer-tag env f)) + (and (= (-> f :op) :constant) + (keyword? (-> f :form)))) [f variadic-invoke] (if fn? (let [arity (count args) From df4959ff3ad573d6c178e071efd594b002fee4eb Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Jun 2017 16:21:56 -0400 Subject: [PATCH 2438/4033] just emit call if for some reason we haven't already optimized --- src/main/clojure/cljs/compiler.cljc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 932a1b809..5b55a15a4 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1047,9 +1047,7 @@ f "(" (comma-sep args) "))") (emits "(" f fprop " ? " f fprop "(" (comma-sep args) ") : " f ".call(" (comma-sep (cons "null" args)) "))"))) - (if ana/*fn-invoke-direct* - (emits f "(" (comma-sep args) ")") - (emits f ".call(" (comma-sep (cons "null" args)) ")"))))))) + (emits f ".call(" (comma-sep (cons "null" args)) ")")))))) (defmethod emit* :new [{:keys [ctor args env]}] From aa69dea1f5fa89982cf85a56510d8af04b6d3444 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 28 Jun 2017 08:33:29 -0400 Subject: [PATCH 2439/4033] resolve returns improperly constructed Var --- src/main/clojure/cljs/core.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 006a56e08..757175bb7 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3191,4 +3191,4 @@ [(ana/resolve-var env sym) nil])) resolved (vary-meta (:name var) assoc ::ana/no-resolve true)] `(when (exists? ~resolved) - (cljs.core/Var. ~resolved '~resolved ~meta)))) \ No newline at end of file + (cljs.core/Var. (fn [] ~resolved) '~resolved ~meta)))) \ No newline at end of file diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 7819e18e7..c6b0416a9 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1349,6 +1349,10 @@ (let [p (Partial. + [1])] (p 2)))) +(deftest test-resolve + (testing "Resolve should return valid var" + (is (= 1 ((resolve 'first) [1 2 3]))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 1f95b5e804fb584624fbfb3c4cf77afbc34b7122 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 28 Jun 2017 09:12:16 -0400 Subject: [PATCH 2440/4033] need to elide :c.a/analyzed in c.a/analyze-wrap-meta --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 08a32ff24..c684d7a86 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3205,9 +3205,12 @@ (defn elide-reader-meta [m] (dissoc m :file :line :column :end-column :end-line :source)) +(defn elide-analyzer-meta [m] + (dissoc m ::analyzed)) + (defn analyze-wrap-meta [expr] (let [form (:form expr) - m (elide-reader-meta (meta form))] + m (-> (meta form) elide-reader-meta elide-analyzer-meta)] (if (some? (seq m)) (let [env (:env expr) ; take on expr's context ourselves expr (assoc-in expr [:env :context] :expr) ; change expr to :expr From 42c2e85601a81438c7b08e22b12aa0b9828feeca Mon Sep 17 00:00:00 2001 From: Francis Avila Date: Wed, 28 Jun 2017 09:38:00 -0500 Subject: [PATCH 2441/4033] CLJS-2136: Clarify IFind contract to avoid double-lookups * Adds a docstring to the -find method clarifying that implementors may need to return nil. * Changes find to test for and use IFind first rather than testing for key containment first. * Changes all IFind implementations in core to perform key checks and value retrieval in a single lookup. * Adds some more tests for find on a vector using degenerate keys. * Alters a test using find because of contact ambiguity to use -find instead. --- src/main/cljs/cljs/core.cljs | 53 +++++++++++++----------- src/test/cljs/cljs/collections_test.cljs | 7 +++- src/test/cljs/cljs/map_entry_test.cljs | 9 +--- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7e0335aa5..d3366b466 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -548,7 +548,7 @@ (defprotocol IFind "Protocol for implementing entry finding in collections." - (-find [coll k])) + (-find [coll k] "Returns the map entry for key, or nil if key not present.")) (defprotocol IMap "Protocol for adding mapping functionality to collections." @@ -2242,11 +2242,11 @@ reduces them without incurring seq initialization" (defn find "Returns the map entry for key, or nil if key not present." [coll k] - (when (and (not (nil? coll)) - (associative? coll) - (contains? coll k)) - (if (ifind? coll) - (-find coll k) + (if (ifind? coll) + (-find coll k) + (when (and (not (nil? coll)) + (associative? coll) + (contains? coll k)) [k (get coll k)]))) (defn ^boolean distinct? @@ -5231,8 +5231,9 @@ reduces them without incurring seq initialization" false)) IFind - (-find [coll k] - [k (get coll k)]) + (-find [coll n] + (when (and (== n (bit-or n 0)) (<= 0 n) (< n cnt)) + [n (aget (unchecked-array-for coll n) (bit-and n 0x01f))])) APersistentVector IVector @@ -5522,8 +5523,9 @@ reduces them without incurring seq initialization" (throw (js/Error. "Subvec's key for assoc must be a number.")))) IFind - (-find [coll key] - [key (get coll key)]) + (-find [coll n] + (when (<= end (+ start n)) + (-find v (+ start n)))) IVector (-assoc-n [coll n val] @@ -6060,7 +6062,9 @@ reduces them without incurring seq initialization" IFind (-find [coll k] - [k (get coll k)]) + (when (and ^boolean (goog/isString k) + (not (nil? (scan-array 1 k keys)))) + [k (aget strobj k)])) IKVReduce (-kv-reduce [coll f init] @@ -6289,10 +6293,10 @@ reduces them without incurring seq initialization" IFind (-find [node k] - (cond - (== k 0) [0 key] - (== k 1) [1 val] - :else nil)) + (case k + 0 [0 key] + 1 [1 val] + nil)) IVector (-assoc-n [node n v] @@ -6505,7 +6509,8 @@ reduces them without incurring seq initialization" IFind (-find [coll k] (let [idx (array-map-index-of coll k)] - [(aget arr idx) (get coll k)])) + (when-not (== idx -1) + [(aget arr idx) (aget arr (inc idx))]))) IMap (-dissoc [coll k] @@ -7949,10 +7954,10 @@ reduces them without incurring seq initialization" IFind (-find [node k] - (cond - (== k 0) [0 key] - (== k 1) [1 val] - :else nil)) + (case k + 0 [0 key] + 1 [1 val] + nil)) IVector (-assoc-n [node n v] @@ -8110,10 +8115,10 @@ reduces them without incurring seq initialization" IFind (-find [node k] - (cond - (== k 0) [0 key] - (== k 1) [1 val] - :else nil)) + (case k + 0 [0 key] + 1 [1 val] + nil)) IVector (-assoc-n [node n v] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index f9d2276c0..f04f1c889 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -36,7 +36,12 @@ (is (= (find {} nil) nil)) (is (= (find {:a 1} nil) nil)) (is (= (find {:a 1 :b 2} nil) nil)) - (is (= (find [1 2 3] 0) [0 1]))) + (is (= (find [1 2 3] 0) [0 1])) + (is (= (find [1 2 3] -1) nil)) + (is (= (find [1 2 3] js/NaN) nil)) + (is (= (find [1 2 3] 1.2) nil)) + (is (= (find [1 2 3] :a) nil)) + (is (= (find [1 2 3] 10) nil))) ) (deftest test-vectors diff --git a/src/test/cljs/cljs/map_entry_test.cljs b/src/test/cljs/cljs/map_entry_test.cljs index 65f0baaa3..f30dae04d 100644 --- a/src/test/cljs/cljs/map_entry_test.cljs +++ b/src/test/cljs/cljs/map_entry_test.cljs @@ -113,13 +113,8 @@ (are [x y] (= x y) [0 :key] (-find e 0) [1 :val] (-find e 1) - ;; Commented out as unsure about contract of -find - ;; in the case where key is not present. - ;nil (-find e 2) - ;nil (-find e -1) - ;; So testing `find` in this case instead as contract is clear. - nil (find e 2) - nil (find e -1)))) + nil (-find e 2) + nil (-find e -1)))) (testing "IFn" (testing "-invoke 2-arity" From 4814435535e3bed6e48b3b725d902aa3a92e7083 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 28 Jun 2017 12:58:25 -0400 Subject: [PATCH 2442/4033] remove integer validation in PV -find --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d3366b466..0aef0134b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5232,7 +5232,7 @@ reduces them without incurring seq initialization" IFind (-find [coll n] - (when (and (== n (bit-or n 0)) (<= 0 n) (< n cnt)) + (when (and (<= 0 n) (< n cnt)) [n (aget (unchecked-array-for coll n) (bit-and n 0x01f))])) APersistentVector From ef95e1e35713d3db9122657f3d1ff595932b682d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 28 Jun 2017 13:33:31 -0400 Subject: [PATCH 2443/4033] fix Subvec IFind, add tests remove PV find floating point test --- src/main/cljs/cljs/core.cljs | 3 ++- src/test/cljs/cljs/collections_test.cljs | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0aef0134b..4fa5ad733 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5524,7 +5524,8 @@ reduces them without incurring seq initialization" IFind (-find [coll n] - (when (<= end (+ start n)) + (when (and (not (neg? n)) + (< (+ start n) end)) (-find v (+ start n)))) IVector diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index f04f1c889..14dfa56cc 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -39,7 +39,6 @@ (is (= (find [1 2 3] 0) [0 1])) (is (= (find [1 2 3] -1) nil)) (is (= (find [1 2 3] js/NaN) nil)) - (is (= (find [1 2 3] 1.2) nil)) (is (= (find [1 2 3] :a) nil)) (is (= (find [1 2 3] 10) nil))) ) @@ -99,7 +98,14 @@ sv2 (subvec [0 1 2 3] 1 1)] (testing "rseq equality" (is (= (rseq sv1) '(1))) - (is (nil? (rseq sv2)))))) + (is (nil? (rseq sv2))))) + (let [s (subvec [0 1 2 3] 0 2)] + (testing "IFind" + (is (= (find s 0) [0 0])) + (is (= (find s 1) [1 1])) + (is (= (find s 2) nil)) + (is (= (find s -1) nil)))) + ) )) (deftest test-sets From d598fd9ca9b7a104741d6a68a6222093059d7389 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 28 Jun 2017 14:30:53 -0400 Subject: [PATCH 2444/4033] Subvec should not delegate -find, additional tests --- src/main/cljs/cljs/core.cljs | 7 ++++--- src/test/cljs/cljs/collections_test.cljs | 15 ++++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4fa5ad733..b4232f2b7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5524,9 +5524,10 @@ reduces them without incurring seq initialization" IFind (-find [coll n] - (when (and (not (neg? n)) - (< (+ start n) end)) - (-find v (+ start n)))) + (when-not (neg? n) + (let [idx (+ start n)] + (when (< idx end) + [n (-lookup v idx)])))) IVector (-assoc-n [coll n val] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 14dfa56cc..69a2335b6 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -99,12 +99,17 @@ (testing "rseq equality" (is (= (rseq sv1) '(1))) (is (nil? (rseq sv2))))) - (let [s (subvec [0 1 2 3] 0 2)] + (let [sv1 (subvec [0 1 2 3] 0 2) + sv2 (subvec [0 1 2 3] 1 3)] (testing "IFind" - (is (= (find s 0) [0 0])) - (is (= (find s 1) [1 1])) - (is (= (find s 2) nil)) - (is (= (find s -1) nil)))) + (is (= (find sv1 0) [0 0])) + (is (= (find sv1 1) [1 1])) + (is (= (find sv1 2) nil)) + (is (= (find sv1 -1) nil)) + (is (= (find sv2 0) [0 1])) + (is (= (find sv2 1) [1 2])) + (is (= (find sv2 2) nil)) + (is (= (find sv2 -1) nil)))) ) )) From b1b09bb4eefb1516b59dcb3dc760b0242917397c Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Thu, 29 Jun 2017 13:20:00 +0100 Subject: [PATCH 2445/4033] CLJS-2137: Missing INext on some sequences Added INext to ArrayNodeSeq, NodeSeq, PersistentQueueSeq and PersistentTreeMapSeq --- src/main/cljs/cljs/core.cljs | 25 ++++++++++++++++++++++++ src/test/cljs/cljs/collections_test.cljs | 4 +++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b4232f2b7..b0412a7fa 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5829,6 +5829,13 @@ reduces them without incurring seq initialization" (-empty coll) (PersistentQueueSeq. meta rear nil nil)))) + INext + (-next [coll] + (if-let [f1 (next front)] + (PersistentQueueSeq. meta f1 rear nil) + (when (some? rear) + (PersistentQueueSeq. meta rear nil nil)))) + ICollection (-conj [coll o] (cons o coll)) @@ -7301,6 +7308,12 @@ reduces them without incurring seq initialization" (create-inode-seq nodes i (next s)))] (if-not (nil? ret) ret ()))) + INext + (-next [coll] + (if (nil? s) + (create-inode-seq nodes (+ i 2) nil) + (create-inode-seq nodes i (next s)))) + ISeqable (-seq [this] this) @@ -7367,6 +7380,10 @@ reduces them without incurring seq initialization" (let [ret (create-array-node-seq nil nodes i (next s))] (if-not (nil? ret) ret ()))) + INext + (-next [coll] + (create-array-node-seq nil nodes i (next s))) + ISeqable (-seq [this] this) @@ -7724,6 +7741,14 @@ reduces them without incurring seq initialization" (if-not (nil? next-stack) (PersistentTreeMapSeq. nil next-stack ascending? (dec cnt) nil) ()))) + INext + (-next [this] + (let [t (first stack) + next-stack (tree-map-seq-push (if ascending? (.-right t) (.-left t)) + (next stack) + ascending?)] + (when-not (nil? next-stack) + (PersistentTreeMapSeq. nil next-stack ascending? (dec cnt) nil)))) ICounted (-count [coll] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 69a2335b6..607c6e334 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -678,8 +678,10 @@ (is (= (find map nil) [nil :foo]))))) (deftype CustomVectorThing [v] + ;; Subvec expects its argument to implement IVector. + ;; Note, that this method is never actually called. IVector - (-assoc-n [coll i val] (assoc-n v i val)) + (-assoc-n [coll i val] nil) IIndexed (-nth [coll i] (nth v i)) From 3fa42835a4b300ad2cfba17bc4cab9cde536066a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 29 Jun 2017 22:49:59 -0400 Subject: [PATCH 2446/4033] CLJS-2139: Undeclared var regression in fn bodies With CLJS-2066, a change was made to skip analyzing named fn method bodies on the first analysis pass, deferring this analyis to the second, richer pass dedicated to optmizing self calls. Since the second pass has all warnings suppressed, this introduces a subtle regression in that no warnings would be emitted for issues found in named function bodies. This fixes the issue by turning off the blanket no-warn for the second pass. Since warnings can only be emitted when analyzing method bodies (the analysis of parameters doesn't lead to warnings), this is sufficient to solve the problem. --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/analyzer_tests.clj | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c684d7a86..972052bad 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1570,7 +1570,7 @@ (doall (map #(analyze-fn-method menv locals % type true) meths))) (defn analyze-fn-methods-pass2 [menv locals type meths] - (no-warn (analyze-fn-methods-pass2* menv locals type meths))) + (analyze-fn-methods-pass2* menv locals type meths)) (defmethod parse 'fn* [op env [_ & args :as form] name _] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index cad258e3d..efdff10fa 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -733,6 +733,15 @@ ;; The previous def must be analyzed for subsequent var special to succeed (def ~'x33 (var ~'x32)))])))) +(deftest test-cljs-2139 + (let [ws (atom [])] + (try + (a/with-warning-handlers [(collecting-warning-handler ws)] + (a/analyze (a/empty-env) + '(defn foo [] x))) + (catch Exception _)) + (is (= ["Use of undeclared Var cljs.user/x"] @ws)))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From e86c36bb5408a0778bde6b56a292819d6d2af4c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 30 Jun 2017 09:34:37 -0400 Subject: [PATCH 2447/4033] 1.9.671 --- README.md | 6 +++--- changes.md | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c2da2ec9..bc5c3bede 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.660 +Latest stable release: 1.9.671 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.660"] +[org.clojure/clojurescript "1.9.671"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.660 org.clojure clojurescript - 1.9.660 + 1.9.670 ``` diff --git a/changes.md b/changes.md index d76d0c92a..a7c3e357a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,14 @@ +## 1.9.671 + +### Fixes +* CLJS-2139: Undeclared var regression in fn bodies +* 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 +* resolve returns improperly constructed Var +* fix :fn-invoke-direct edgecase around keywords + ## 1.9.660 ### Changes From d2c6966893d47772550bbc6842993310acb9e470 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 30 Jun 2017 13:53:18 -0400 Subject: [PATCH 2448/4033] CLJS-2142: Can't instrument a namespace containing constants Filter ^:const Vars from the set of instrumentable Vars --- src/main/cljs/cljs/spec/test/alpha.cljc | 9 +++++---- src/test/cljs/cljs/spec/test_test.cljs | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 877a12355..bcbae037c 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -35,10 +35,11 @@ (defmacro instrument-1 [[quote s] opts] (when-let [v (ana-api/resolve &env s)] - (swap! instrumented-vars conj (:name v)) - `(let [checked# (instrument-1* ~s (var ~s) ~opts)] - (when checked# (set! ~s checked#)) - '~(:name v)))) + (when (nil? (:const v)) + (swap! instrumented-vars conj (:name v)) + `(let [checked# (instrument-1* ~s (var ~s) ~opts)] + (when checked# (set! ~s checked#)) + '~(:name v))))) (defmacro unstrument-1 [[quote s]] diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index a1386da10..923f34286 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -15,3 +15,11 @@ ; Calling h-cljs-1812 with an argument of the wrong type shouldn't throw, ; because the function should not have been instrumented by stest/check. (h-cljs-1812 "foo")) + +;; Setup for CLJS-2142 +(def ^:const pi 3.14159) +(defn area [r] (* pi r r)) +(s/fdef area :args (s/cat :r number?)) + +(deftest test-cljs-2142 + (is (= `[area] (stest/instrument `[pi area])))) From b900d8a4819af0e3060e1b4c9f657d0b6dc2cbb2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 1 Jul 2017 11:35:10 -0400 Subject: [PATCH 2449/4033] CLJS-2145: inode_find issue with hash-map Need to check for nil? root. Add tests for PHM -find. --- src/main/cljs/cljs/core.cljs | 8 ++++---- src/test/cljs/cljs/collections_test.cljs | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b0412a7fa..640f38076 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7530,10 +7530,10 @@ reduces them without incurring seq initialization" IFind (-find [coll k] - (if (nil? k) - (when has-nil? - [nil nil-val]) - (.inode-find root 0 (hash k) k nil))) + (cond + (nil? k) (when has-nil? [nil nil-val]) + (nil? root) nil + :else (.inode-find root 0 (hash k) k nil))) IMap (-dissoc [coll k] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 607c6e334..3e9546f00 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -701,3 +701,11 @@ (is (= [1 2] (reduce conj [] (subvec [0 1 2 3] 1 3))))) (testing "Subvec over other vectors reduces as expected" (is (= [1 2] (reduce conj [] (subvec (->CustomVectorThing [0 1 2 3]) 1 3))))))) + +(deftest test-cljs-2145 + (testing "PersistentHashMap -find implementation" + (is (= (find (hash-map) :a) nil)) + (is (= (find (hash-map :a 1) :a) [:a 1])) + (is (= (find (hash-map :a false) :a) [:a false])) + (is (= (find (zipmap (range 1000) (repeat :foo)) 999) [999 :foo])) + (is (= (find (zipmap (range 1000) (repeat :foo)) 1000) nil)))) \ No newline at end of file From 179758f6f0f6945903df4e08b29ab61527345a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 30 Jun 2017 09:37:56 -0700 Subject: [PATCH 2450/4033] CLJS-2141: Self-host: cljs.js is using undeclared symbol lib --- src/main/cljs/cljs/js.cljs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 2cb651508..d6a5360d6 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -425,7 +425,7 @@ (str "Could not analyze dep " dep) cause)))))) (cb {:value nil})))))) -(defn- load-macros [bound-vars k macros reload reloads opts cb] +(defn- load-macros [bound-vars k macros lib reload reloads opts cb] (if (seq macros) (let [nsym (first (vals macros)) k (or (reload k) @@ -439,7 +439,7 @@ (require bound-vars nsym k opts' (fn [res] (if-not (:error res) - (load-macros bound-vars k (next macros) reload reloads opts cb) + (load-macros bound-vars k (next macros) lib reload reloads opts cb) (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns nsym)] (get {nsym nil} cljs-ns cljs-ns))] (require bound-vars cljs-dep k opts' @@ -448,7 +448,7 @@ (cb res) (do (patch-alias-map (:*compiler* bound-vars) lib nsym cljs-dep) - (load-macros bound-vars k (next macros) reload reloads opts + (load-macros bound-vars k (next macros) lib reload reloads opts (fn [res] (if (:error res) (cb res) @@ -497,19 +497,19 @@ (if (#{:ns :ns*} op) (letfn [(check-uses-and-load-macros [res rewritten-ast] (let [env (:*compiler* bound-vars) - {:keys [uses require-macros use-macros reload reloads]} rewritten-ast] + {:keys [uses require-macros use-macros reload reloads name]} rewritten-ast] (if (:error res) (cb res) (if (:*load-macros* bound-vars) (do - (when (:verbose opts) (debug-prn "Processing :use-macros for" (:name ast))) - (load-macros bound-vars :use-macros use-macros reload reloads opts + (when (:verbose opts) (debug-prn "Processing :use-macros for" name)) + (load-macros bound-vars :use-macros use-macros name reload reloads opts (fn [res] (if (:error res) (cb res) (let [{:keys [require-macros] :as rewritten-ast} (rewrite-ns-ast rewritten-ast (:aliased-loads res))] (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) - (load-macros bound-vars :require-macros require-macros reload reloads opts + (load-macros bound-vars :require-macros require-macros name reload reloads opts (fn [res'] (if (:error res') (cb res') From aa070a4f907cc680164bd0763ef0f43fb45a0fda Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 2 Jul 2017 01:17:23 -0400 Subject: [PATCH 2451/4033] Comprehensive enhancement to :modules This patch includes an enhancement suggested by Thomas Heller and features present in shadow-build and further clarified by Google Closure JSModuleGraph. We introduce a new namespace cljs.module-graph which computes the complete :modules information from user supplied :modules and dependency sorted compiler inputs. The logic of this namespace validates the user supplied :modules and automatically moves dependencies into the deepest common parent modules. This prevents moving unspecified dependencies directly into :cljs-base. Users could accomplish a similar effect with wildcard entries but this process is less error prone. This change has multiple benefits in that splits are automatically more optimal with little further guidance from the user. :modules is also now enabled for :none and :advanced compilation. The same optimal dependency module assignment algorithm is used to optimize dynamic loads during development in particular via a new namespace cljs.loader. This special namespace holds a properly configured Google Closure ModuleManager which can load splits on demand. Users can use functions in this namespace coupled with `resolve` to safely call across module boundaries even under advanced compilation. cljs.loader is incremental compilation aware and will only be recompiled if the dependency graph changes. A compiler change to support this work is that :out-file is the correct way to determine where a compiler input was compiled to, regardless of whether ClojureScript or JavaScript. --- src/main/cljs/cljs/core.cljs | 3 + src/main/cljs/cljs/loader.cljs | 56 ++++ src/main/clojure/cljs/analyzer.cljc | 15 + src/main/clojure/cljs/closure.clj | 267 ++++++++-------- src/main/clojure/cljs/compiler.cljc | 63 ++-- src/main/clojure/cljs/module_graph.cljc | 315 +++++++++++++++++++ src/main/clojure/cljs/util.cljc | 8 +- src/test/cljs_build/loader_test/bar.cljs | 6 + src/test/cljs_build/loader_test/foo.cljs | 6 + src/test/clojure/cljs/build_api_tests.clj | 24 ++ src/test/clojure/cljs/module_graph_tests.clj | 127 ++++++++ 11 files changed, 726 insertions(+), 164 deletions(-) create mode 100644 src/main/cljs/cljs/loader.cljs create mode 100644 src/main/clojure/cljs/module_graph.cljc create mode 100644 src/test/cljs_build/loader_test/bar.cljs create mode 100644 src/test/cljs_build/loader_test/foo.cljs create mode 100644 src/test/clojure/cljs/module_graph_tests.clj diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 640f38076..3a3a2b09a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -22,6 +22,9 @@ (defonce PROTOCOL_SENTINEL #js {}) +(def MODULE_URIS nil) ;; set by compiler +(def MODULE_INFOS nil) ;; set by compiler + (goog-define ^{:dynamic true :doc "Var bound to the name value of the compiler build :target option. diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs new file mode 100644 index 000000000..8f40a35dd --- /dev/null +++ b/src/main/cljs/cljs/loader.cljs @@ -0,0 +1,56 @@ +(ns cljs.loader + (:require [goog.object :as gobj]) + (:import [goog.module ModuleLoader] + [goog.module ModuleManager])) + +(def module-infos MODULE_INFOS) ;; set by compiler +(def module-uris MODULE_URIS) ;; set by compiler + +(defn deps-for [x graph] + (let [depends-on (get graph x)] + (-> (mapcat #(deps-for % graph) depends-on) + (concat depends-on) distinct vec))) + +(defn munge-kw [x] + (cond-> x + (keyword? x) (-> name munge))) + +(defn to-js [m] + (reduce-kv + (fn [ret k xs] + (let [arr (into-array (map munge-kw xs))] + (doto ret (gobj/set (-> k name munge) arr)))) + #js {} m)) + +(defn create-module-manager [] + (let [mm (ModuleManager.) + ml (ModuleLoader.)] + ;(.setSourceUrlInjection ml true) + ;(.setBatchModeEnabled mm true) + (.setLoader mm ml) + (.setAllModuleInfo mm (to-js module-infos)) + (.setModuleUris mm (to-js module-uris)) + mm)) + +(def ^:dynamic *module-manager* (create-module-manager)) + +(defn load + "Load a module. module-name should be a keyword matching a :modules module + definition." + ([module-name] + (load module-name nil)) + ([module-name cb] + (let [mname (-> module-name name munge)] + (if-not (nil? cb) + (.execOnLoad *module-manager* mname cb) + (.load *module-manager* mname))))) + +(defn set-loaded! + "Set a module as being loaded. module-name should be a keyword matching a + :modules module definition. Will mark all parent modules as also being + loaded." + [module-name] + (let [xs (deps-for module-name module-infos)] + (doseq [x xs] + (.setLoaded *module-manager* (munge-kw x))) + (.setLoaded *module-manager* (munge-kw module-name)))) \ No newline at end of file diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 972052bad..999260d13 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3351,6 +3351,21 @@ reader/*alias-map* (or reader/*alias-map* {})] (analyze* env form name opts)))))) +(defn add-consts + "Given a compiler state and a map from fully qualified symbols to constant + EDN values, update the compiler state marking these vars as const to support + direct substitution of these vars in source." + [compiler-state constants-map] + (reduce-kv + (fn [compiler-state sym value] + (let [ns (symbol (namespace sym))] + (update-in compiler-state + [::namespaces ns :defs (symbol (name sym))] merge + {:const-expr + (binding [*passes* (conj *passes* (replace-env-pass {:context :expr}))] + (analyze (empty-env) value))}))) + compiler-state constants-map)) + #?(:clj (defn- source-path "Returns a path suitable for providing to tools.reader as a 'filename'." diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 489a6cefa..db863c0f2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -46,7 +46,8 @@ [clojure.string :as string] [clojure.data.json :as json] [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers]) + [clojure.tools.reader.reader-types :as readers] + [cljs.module-graph :as module-graph]) (:import [java.lang ProcessBuilder] [java.io File BufferedInputStream BufferedReader Writer InputStreamReader IOException StringWriter] @@ -60,7 +61,7 @@ SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy - JSModule JSModuleGraph SourceMap ProcessCommonJSModules + JSModule SourceMap ProcessCommonJSModules AbstractCompiler TransformAMDToCJSModule ProcessEs6Modules CompilerInput] [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode] @@ -422,8 +423,14 @@ (:requires m) (:lines m) (:source-map m)) + (when-let [source-file (:source-file m)] + {:source-file source-file}) + (when-let [out-file (:out-file m)] + {:out-file out-file}) (when (:closure-lib m) {:closure-lib true}) + (when-let [ns (:ns m)] + {:ns ns}) (when (:macros-ns m) {:macros-ns true}))) @@ -1032,27 +1039,23 @@ (when-let [url (deps/-url js)] (js-source-file (javascript-name url) (io/input-stream url)))) -(defn add-cljs-base-module +(defn ensure-cljs-base-module + "Ensure that compiler :modules map has :cljs-base module with defined + :output-to. If :output-to not provided will default to :output-dir location + and the name of the file will be \"cljs_base.js.\"" ([modules] - (add-cljs-base-module modules + (ensure-cljs-base-module modules (when env/*compiler* (:options @env/*compiler*)))) ([modules opts] - (reduce - (fn [modules module-name] - (if-not (= module-name :cljs-base) - (update-in modules [module-name :depends-on] - (fnil identity #{:cljs-base})) - modules)) - (update-in modules [:cljs-base :output-to] - (fnil io/file - (io/file - (util/output-directory opts) - "cljs_base.js"))) - (keys modules)))) + (update-in modules [:cljs-base :output-to] + (fnil io/file + (io/file + (util/output-directory opts) + "cljs_base.js"))))) (comment - (add-cljs-base-module + (ensure-cljs-base-module {:cljs-base {:output-to "out/modules/base.js"} :core @@ -1064,50 +1067,28 @@ :depends-on #{:core}}}) ) -(defn sort-modules [modules-with-base] - (letfn [(get-deps [module] - (reduce - (fn [ret [name {:keys [depends-on] :as module-desc}]] - (cond-> ret - (contains? depends-on module) (conj name))) - [] modules-with-base))] - (vec (map (fn [module-name] - [module-name (module-name modules-with-base)]) - (into [:cljs-base] (util/topo-sort :cljs-base get-deps)))))) - -(comment - (sort-modules - (add-cljs-base-module - {:cljs-base - {:output-to "out/module/base.js"} - :core - {:output-to "out/modules/core.js" - :entries '#{cljs.core}} - :landing - {:output-to "out/modules/reader.js" - :entries '#{cljs.reader} - :depends-on #{:core}}})) - ) - -(defn find-entries [sources entry] - (let [m (name (comp/munge entry)) - xs (string/split m #"\.")] - (if (= "_STAR_" (last xs)) - (let [matcher (str (string/join "." (butlast xs)) ".")] - (into #{} - (filter - (fn [source] - (when (some #(.startsWith ^String % matcher) (:provides source)) - source))) - sources)) - #{(some - (fn [source] - (let [matcher - (into #{} - [(name entry) (name (comp/munge entry))])] - (when (some matcher (:provides source)) - source))) - sources)}))) +(defn compile-loader + "Special compilation pass for cljs.loader namespace. cljs.loader must be + compiled last after all inputs. This is because all inputs must be known and + they must already be sorted in dependency order." + [inputs {:keys [modules] :as opts}] + (when-let [loader (->> inputs + (filter + (fn [input] + (some '#{"cljs.loader" cljs.loader} + (:provides input)))) + first)] + (let [module-uris (module-graph/modules->module-uris modules inputs opts) + module-infos (module-graph/modules->module-infos modules)] + (env/with-compiler-env + (ana/add-consts @env/*compiler* + {'cljs.core/MODULE_URIS module-uris + 'cljs.core/MODULE_INFOS module-infos}) + (-compile (:source-file loader) + (merge opts + {:cache-key (util/content-sha (pr-str module-uris)) + :output-file (comp/rename-to-js (util/ns->relpath (:ns loader)))}))))) + inputs) (defn build-modules "Given a list of IJavaScript sources in dependency order and compiler options @@ -1117,33 +1098,26 @@ a :foreign-deps vector containing foreign IJavaScript sources in dependency order." [sources opts] - (let [used (atom {}) - [sources' modules] + (let [used (atom #{}) ;; track used inputs to avoid dupes + modules (reduce - (fn [[sources ret] [name {:keys [entries output-to depends-on] :as module-desc}]] + (fn [ret [name {:keys [entries depends-on] :as module-desc}]] (assert (or (= name :cljs-base) (not (empty? entries))) (str "Module " name " does not define any :entries")) (when (:verbose opts) (util/debug-prn "Building module" name)) (let [js-module (JSModule. (clojure.core/name name)) - [sources' module-sources] - ;; compute inputs for a closure module - ;; as well as sources difference + module-sources (reduce - (fn [[sources ret] entry-sym] - (if-let [entries (find-entries sources entry-sym)] - (do - (swap! used assoc entry-sym name) - [(remove entries sources) (into ret entries)]) - (if (contains? @used entry-sym) - (throw - (IllegalArgumentException. - (str "Already used namespace " entry-sym " " - "in module " (get @used entry-sym)))) - (throw - (IllegalArgumentException. - (str "Could not find namespace " entry-sym)))))) - [sources []] entries) + (fn [ret entry-sym] + (if-let [entries (module-graph/find-sources-for-module-entry entry-sym sources)] + (let [unused (set/difference entries @used)] + (swap! used into entries) + (into ret unused)) + (throw + (IllegalArgumentException. + (str "Could not find matching namespace for " entry-sym))))) + [] entries) foreign-deps (atom [])] ;; add inputs to module (doseq [ijs module-sources] @@ -1163,24 +1137,14 @@ (.addDependency js-module ^JSModule parent-module)) (throw (IllegalArgumentException. (str "Parent module " dep " does not exist"))))) - [sources' - (conj ret - [name (assoc module-desc - :closure-module js-module - :foreign-deps @foreign-deps)])])) - [sources []] (sort-modules (add-cljs-base-module (:modules opts) opts))) - cljs-base-closure-module (get-in (into {} modules) [:cljs-base :closure-module]) + (conj ret + [name (assoc module-desc + :closure-module js-module + :foreign-deps @foreign-deps)]))) + [] (module-graph/sort-modules + (ensure-cljs-base-module + (module-graph/expand-modules (:modules opts) sources) opts))) foreign-deps (atom [])] - (when (:verbose opts) - (util/debug-prn "Adding remaining namespaces to" :cljs-base)) - ;; add anything left to :cljs-base module - (doseq [source sources'] - (when (:verbose opts) - (util/debug-prn " adding entry" (:provides source))) - (if-not (deps/-foreign? source) - (.add ^JSModule cljs-base-closure-module - (js-source-file (javascript-name source) source)) - (swap! foreign-deps conj source))) (assoc-in modules [0 1 :foreign-deps] @foreign-deps))) (comment @@ -1442,7 +1406,8 @@ (cond (nil? output-to) js - (string? output-to) + (or (string? output-to) + (util/file? output-to)) (let [f (io/file output-to)] (util/mkdirs f) (spit f js)) @@ -1481,15 +1446,25 @@ "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" (apply str (preloads (:preloads opts))) - "goog.require(\"" (comp/munge (:main opts)) "\");\n" - "goog.require(\"cljs.nodejscli\");\n"))) + (apply str + (map (fn [entry] + (str "goog.require(\"" (comp/munge entry) "\");\n")) + (if-let [entries (:entries opts)] + entries + [(:main opts)]))) + "goog.require(\"cljs.nodejscli\");\n"))) (output-one-file opts (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "if(typeof goog == \"undefined\") document.write('');\n" "document.write('');\n" "document.write('');\n" (apply str (preloads (:preloads opts) :browser)) - "document.write('');\n"))))) + (apply str + (map (fn [entry] + (str "document.write('');\n")) + (if-let [entries (:entries opts)] + entries + [(:main opts)])))))))) (defn output-modules "Given compiler options, original IJavaScript sources and a sequence of @@ -1550,9 +1525,8 @@ (subs path (+ (.lastIndexOf path lib-path) (.length lib-path))))))) (defn ^String rel-output-path - "Given an IJavaScript which is either in memory, in a jar file, - or is a foreign lib, return the path relative to the output - directory." + "Given a IJavaScript which points to a .js file either in memory, in a jar file, + or is a foreign lib, return the path relative to the output directory." ([js] (rel-output-path js (when env/*compiler* @@ -1683,11 +1657,14 @@ "Returns true if IJavaScript instance needs to be written/copied to output directory. True when in memory, in a JAR, or if foreign library." [js] - (let [url ^URL (deps/-url js)] - (or (not url) - (= (.getProtocol url) "jar") - (deps/-closure-lib? js) - (deps/-foreign? js)))) + (try + (let [url ^URL (deps/-url js)] + (or (not url) + (= (.getProtocol url) "jar") + (deps/-closure-lib? js) + (deps/-foreign? js))) + (catch Throwable t + (throw (Exception. (str "Could not write JavaScript " (pr-str js))))))) (defn source-on-disk "Ensure that the given IJavaScript exists on disk in the output directory. @@ -1704,12 +1681,13 @@ (util/ns->relpath ns (util/ext (:source-url js))))) source-url (:source-url js)] (when (and out-file source-url - (or (not (.exists ^File out-file)) - (util/changed? (io/file source-url) out-file))) - (when (or ana/*verbose* (:verbose opts)) - (util/debug-prn "Copying" (str source-url) "to" (str out-file))) - (spit out-file (slurp source-url)) - (.setLastModified ^File out-file (util/last-modified source-url))) + (or (not (.exists ^File out-file)) + (util/changed? (io/file source-url) out-file))) + (do + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Copying" (str source-url) "to" (str out-file))) + (spit out-file (slurp source-url)) + (.setLastModified ^File out-file (util/last-modified source-url)))) js))) (comment @@ -1730,23 +1708,38 @@ The deps file for the current project will include third-party libraries." - [opts & sources] - (let [disk-sources (remove #(= (:group %) :goog) - (map #(source-on-disk opts %) sources)) - goog-deps (io/file (util/output-directory opts) - "goog" "deps.js") - main (:main opts)] + [{:keys [modules] :as opts} & sources] + ;; this source-on-disk call is currently necessary for REPLs - David + (let [disk-sources (doall + (remove #(= (:group %) :goog) + (map #(source-on-disk opts %) sources))) + goog-deps (io/file (util/output-directory opts) "goog" "deps.js") + main (:main opts) + output-deps #(output-deps-file + (assoc opts :output-to + (str (util/output-directory opts) + File/separator "cljs_deps.js")) + disk-sources)] (util/mkdirs goog-deps) (spit goog-deps (slurp (io/resource "goog/deps.js"))) - (if main + (cond + modules (do - (output-deps-file - (assoc opts :output-to - (str (util/output-directory opts) - File/separator "cljs_deps.js")) - disk-sources) + (output-deps) + (doall + (map + (fn [[module-name {:keys [output-to entries]}]] + (output-main-file + (merge opts + {:output-to output-to :entries entries}))) + modules))) + + main + (do + (output-deps) (output-main-file opts)) - (output-deps-file opts disk-sources)))) + + :else (output-deps-file opts disk-sources)))) (comment @@ -1972,6 +1965,10 @@ v)))) {} ups-npm-deps)))) +(defn ensure-module-opts [opts] + (update opts :modules + #(ensure-cljs-base-module % opts))) + (defn add-implicit-options [{:keys [optimizations output-dir npm-deps] :or {optimizations :none @@ -2027,7 +2024,10 @@ (assoc-in [:closure-warnings :check-variables] :off) (nil? (:closure-module-roots opts)) - (assoc :closure-module-roots [])))) + (assoc :closure-module-roots []) + + (contains? opts :modules) + (ensure-module-opts)))) (defn- alive? [proc] (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) @@ -2311,7 +2311,9 @@ deps/dependency-order (add-preloads all-opts) add-goog-base - (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)]))) + (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])) + (->> (map #(source-on-disk all-opts %)) doall) + (compile-loader all-opts)) _ (when (:emit-constants all-opts) (comp/emit-constants-table-to-file (::ana/constant-table @env/*compiler*) @@ -2328,8 +2330,7 @@ (when-let [fname (:source-map all-opts)] (assert (or (nil? (:output-to all-opts)) (:modules opts) (string? fname)) (str ":source-map must name a file when using :whitespace, " - ":simple, or :advanced optimizations with :output-to")) - (doall (map #(source-on-disk all-opts %) js-sources))) + ":simple, or :advanced optimizations with :output-to"))) (if (:modules all-opts) (->> (apply optimize-modules all-opts js-sources) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 5b55a15a4..ce8a77bd3 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -300,15 +300,15 @@ (emits ")"))) (defmethod emit-constant #?(:clj clojure.lang.Keyword :cljs Keyword) [x] - (if (-> @env/*compiler* :options :emit-constants) - (let [value (-> @env/*compiler* ::ana/constant-table x)] - (emits "cljs.core." value)) + (if-let [value (and (-> @env/*compiler* :options :emit-constants) + (-> @env/*compiler* ::ana/constant-table x))] + (emits "cljs.core." value) (emits-keyword x))) (defmethod emit-constant #?(:clj clojure.lang.Symbol :cljs Symbol) [x] - (if (-> @env/*compiler* :options :emit-constants) - (let [value (-> @env/*compiler* ::ana/constant-table x)] - (emits "cljs.core." value)) + (if-let [value (and (-> @env/*compiler* :options :emit-constants) + (-> @env/*compiler* ::ana/constant-table x))] + (emits "cljs.core." value) (emits-symbol x))) ;; tagged literal support @@ -1210,7 +1210,9 @@ (.getPath (.toURL (.toURI f))))) (defn- build-affecting-options [opts] - (select-keys opts [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target])) + (select-keys opts + [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target + :cache-key])) #?(:clj (defn compiled-by-string @@ -1253,7 +1255,9 @@ (json/read-str (slurp (io/resource "cljs/core.aot.js.map"))) "file" (str (io/file (util/output-directory opts) "cljs" "core.js")))))) - (ana/parse-ns src dest nil))) + (merge + (ana/parse-ns src dest nil) + {:out-file dest}))) #?(:clj (defn emit-source-map [src dest sm-data opts] @@ -1344,6 +1348,7 @@ (get-in @env/*compiler* [:options :emit-constants]) (conj ana/constants-ns-sym))) :file dest + :out-file dest :source-file src} (when sm-data {:source-map (:source-map sm-data)}))] @@ -1391,22 +1396,24 @@ (:options @env/*compiler*)))) ([^File src ^File dest opts] (let [{:keys [ns requires]} (ana/parse-ns src)] - (ensure - (or (not (.exists dest)) - (util/changed? src dest) - (let [version' (util/compiled-by-version dest) - version (util/clojurescript-version)] - (and version (not= version version'))) - (and opts - (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns))) - (not= (build-affecting-options opts) - (build-affecting-options (util/build-options dest)))) - (and opts (:source-map opts) - (if (= (:optimizations opts) :none) - (not (.exists (io/file (str (.getPath dest) ".map")))) - (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))) - (when-let [recompiled' (and *recompiled* @*recompiled*)] - (some requires recompiled')))))))) + (if (and (= 'cljs.loader ns) (not (contains? opts :cache-key))) + false + (ensure + (or (not (.exists dest)) + (util/changed? src dest) + (let [version' (util/compiled-by-version dest) + version (util/clojurescript-version)] + (and version (not= version version'))) + (and opts + (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns))) + (not= (build-affecting-options opts) + (build-affecting-options (util/build-options dest)))) + (and opts (:source-map opts) + (if (= (:optimizations opts) :none) + (not (.exists (io/file (str (.getPath dest) ".map")))) + (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)])))) + (when-let [recompiled' (and *recompiled* @*recompiled*)] + (some requires recompiled'))))))))) #?(:clj (defn compile-file @@ -1463,10 +1470,12 @@ (do ;; populate compilation environment with analysis information ;; when constants are optimized - (when (and (true? (:optimize-constants opts)) - (nil? (get-in nses [ns :defs]))) + (when (or (and (= ns 'cljs.loader) + (not (contains? opts :cache-key))) + (and (true? (:optimize-constants opts)) + (nil? (get-in nses [ns :defs])))) (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) - ns-info))) + (assoc ns-info :out-file dest-file)))) (catch Exception e (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) (throw (java.io.FileNotFoundException. (str "The file " src " does not exist."))))))))) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc new file mode 100644 index 000000000..8e9f32a65 --- /dev/null +++ b/src/main/clojure/cljs/module_graph.cljc @@ -0,0 +1,315 @@ +; 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.module-graph + (:require [clojure.string :as string] + [clojure.set :as set] + [clojure.java.io :as io] + [cljs.compiler :as comp])) + +(defn find-sources-for-module-entry + "Given an entry as a symbol, find all matching inputs in sources. If the + symbol ends in a *, then the symbol will be treated as a wildcard. This + function returns a set and is not order preserving. If there are no matches + returns nil." + [entry sources] + (let [m (name (comp/munge entry)) + xs (string/split m #"\.")] + (if (= "_STAR_" (last xs)) + (let [matcher (str (string/join "." (butlast xs)) ".") + matches (into #{} + (filter + (fn [source] + (when (some #(.startsWith ^String % matcher) + (map (comp str munge) (:provides source))) + source))) + sources)] + (when-not (empty? matches) + matches)) + (when-let [input (some + (fn [source] + (let [matcher + (into #{} + [(name entry) (name (comp/munge entry))])] + (when (some matcher (map (comp str munge) (:provides source))) + source))) + sources)] + #{input})))) + +;; Passes for constructing complete module information + +(defn normalize + "Normalize compiler :modules. All symbols in a module :entries will be + converted into munged strings." + [modules] + (reduce-kv + (fn [ret module-name module] + (assoc ret module-name + (update module :entries + (fn [es] (into #{} (map (comp str munge)) es))))) + {} modules)) + +(defn add-cljs-base + "Adds :cljs-base module to compiler :modules if not already present." + [modules] + (cond-> modules + (not (contains? modules :cljs-base)) + (assoc :cljs-base {}))) + +(defn add-cljs-base-dep + "Adds :cljs-base to any module in compiler :modules with an empty :depends-on." + [modules] + (reduce-kv + (fn [ret k {:keys [depends-on] :as module-info}] + (assoc ret k + (cond-> module-info + (and (not= :cljs-base k) (empty? depends-on)) + (assoc :depends-on [:cljs-base])))) + {} modules)) + +(defn depth-of + "Compute the depth of module-name based on dependency information in + compiler :modules." + [module-name modules] + (if (= module-name :cljs-base) + 0 + (let [mns (get-in modules [module-name :depends-on])] + (if (empty? mns) + 1 + (apply max + (map (fn [mn] (+ 1 (depth-of mn modules))) mns)))))) + +(defn annotate-depths + "Annotate all modules in compiler :modules with depth information." + [modules] + (reduce-kv + (fn [ret module-name module] + (let [module' (assoc module :depth (depth-of module-name modules))] + (assoc ret module-name module'))) + {} modules)) + +(defn index-inputs + "Index compiler inputs by :provides. If an input has multiple entries + in :provides will result in multiple entries in the map. The keys will be munged + strings not symbols." + [inputs] + (reduce + (fn [ret {:keys [provides] :as input}] + (into ret (map #(vector (-> % munge str) input)) provides)) + {} inputs)) + +(defn deps-for + "Return all dependencies for x in a graph using deps-key." + [x graph deps-key] + (let [requires (get-in graph [x deps-key])] + (-> (mapcat #(deps-for % graph deps-key) requires) + (concat requires) distinct vec))) + +(defn deps-for-entry + "Return all dependencies for an entry using a compiler inputs index." + [entry indexed-inputs] + (map #(-> % munge str) (deps-for entry indexed-inputs :requires))) + +(defn deps-for-module + "Return all dependencies of a module using compiler :modules." + [module modules] + (deps-for module modules :depends-on)) + +(defn deepest-common-parent + "Given a set of modules and a compiler :modules graph, compute the deepest + common parent module." + [modules all-modules] + (let [common-parents (reduce set/intersection + (map #(set (deps-for-module % all-modules)) modules))] + (apply max-key + (fn [p] (get-in all-modules [p :depth])) + common-parents))) + +(defn canonical-name + "Given an entry use indexed-inputs to return the canonical name. Throws if + entry cannot be found." + [entry indexed-inputs] + (if-let [entry (get indexed-inputs (-> entry comp/munge str))] + (-> (:provides entry) first comp/munge str) + (throw (Exception. (str "No input matching \"" entry "\""))))) + +(defn validate-modules + "Check that a compiler :modules map does not contain user supplied duplicates. + Throws if modules fails validation." + [modules indexed-inputs] + (let [seen (atom {})] + (doseq [[module-name {:keys [entries] :as module}] modules] + (let [entries (into #{} (map #(canonical-name % indexed-inputs)) entries)] + (doseq [entry entries] + (let [seen' @seen] + (if-some [module-name' (get seen' entry)] + (throw + (Exception. + (str "duplicate entry \"" entry "\", occurs in " module-name + " and " module-name' ". entry :provides is " + (get-in indexed-inputs [entry :provides])))) + (swap! seen assoc entry module-name)))))))) + +(defn inputs->assigned-modules + "Given compiler inputs assign each to a single module. This is done by first + starting with :entries. Dependencies for every entry in a module are also added + to that module. Inputs may of course be assigned to several modules initially + but we must eventually choose one. User supplied module :entries are respected + but all other input assignments are computed automatically via + deepest-common-parent. This function returns a map assigning all inputs (indexed + by munged name) to a single module. Any orphan inputs will be assigned to + :cljs-base." + [inputs modules] + (let [index (index-inputs inputs) + _ (validate-modules modules index) + deps #(deps-for-entry % index) + assign1 (fn [[entry maybe-assigned]] + [entry + (if (= 1 (count maybe-assigned)) + (first maybe-assigned) + (deepest-common-parent maybe-assigned modules))]) + canon (fn [xs] (into #{} (map #(canonical-name % index)) xs)) + assigns (fn [f] + (reduce-kv + (fn [ret module-name {:keys [entries] :as module}] + (let [entries' (canon entries)] + (reduce + (fn [ret entry] + (update ret entry (fnil conj #{}) module-name)) + ret (canon (f entries'))))) + {} modules)) + e->ms (assigns identity) + d->ms (assigns #(distinct (mapcat deps %))) + assigned (merge + (into {} (map assign1) d->ms) + (into {} (map assign1) e->ms)) + orphans (zipmap + (map (comp str munge first :provides) + (-> (reduce-kv (fn [m k _] (dissoc m k)) index assigned) + vals set)) + (repeat :cljs-base))] + (merge assigned orphans))) + +(defn expand-modules + "Given compiler :modules and a dependency sorted list of compiler inputs return + a complete :modules map where all depended upon inputs are assigned." + [modules inputs] + (let [order (first + (reduce + (fn [[ret n] {:keys [provides]}] + [(merge ret + (zipmap (map (comp str munge) provides) (repeat n))) + (inc n)]) + [{} 0] inputs)) + modules' (-> modules normalize add-cljs-base add-cljs-base-dep) + assigns (inputs->assigned-modules inputs + (annotate-depths modules')) + um (reduce-kv + (fn [ret entry module-name] + (update-in ret [module-name :entries] + (fnil conj #{}) entry)) + modules' assigns)] + (reduce-kv + (fn [ret module-name {:keys [entries]}] + (update-in ret [module-name :entries] #(vec (sort-by order %)))) + um um))) + +(comment + (inputs->assigned-modules inputs + (-> modules add-cljs-base add-cljs-base-dep annotate-depths)) + + (pprint + (expand-modules modules inputs)) + ) + +(defn topo-sort + "Topologically sort a graph using the given edges-key." + [graph edges-key] + (letfn [(no-incoming-edges [graph edges-key] + (->> graph + (filter + (fn [[k v]] + (every? #(not (contains? graph %)) (edges-key v)))) + (map first)))] + (when-not (empty? graph) + (let [nodes (no-incoming-edges graph edges-key) + graph' (reduce #(dissoc %1 %2) graph nodes)] + (concat nodes (topo-sort graph' edges-key)))))) + +(defn sort-modules [modules-with-base] + (into [] (map (fn [module] [module (module modules-with-base)])) + (topo-sort modules-with-base :depends-on))) + +(comment + (def ms + (sort-modules + (-> + {:cljs-base + {:output-to "out/module/base.js"} + :core + {:output-to "out/modules/core.js" + :entries '#{cljs.core}} + :landing + {:output-to "out/modules/reader.js" + :entries '#{cljs.reader} + :depends-on #{:core}}} + add-cljs-base add-cljs-base-dep))) + ) + +(defn modules->module-uris + "Given a :modules map, a dependency sorted list of compiler inputs, and + compiler options return a Closure module uris map. This map will include + all inputs by leveraging expand-modules." + [modules inputs {:keys [optimizations asset-path output-dir] :as opts}] + (assert optimizations "Must supply :optimizations in opts map") + (assert (#{:advanced :simple :none} optimizations) "Must supply valid :optimizations in opts map") + (assert output-dir "Must supply :output-dir in opts map") + (letfn [(get-uri [rel-path] + (cond->> rel-path + asset-path (str asset-path))) + (get-rel-path* [output-dir file] + (string/replace (.. (io/file file) getAbsoluteFile getPath) output-dir ""))] + (let [get-rel-path (partial get-rel-path* + (.. (io/file output-dir) + getAbsoluteFile getPath))] + (case optimizations + :none + (into {} + (map + (fn [[module-name {:keys [entries] :as module}]] + [module-name + (into [] + (comp + (mapcat #(find-sources-for-module-entry % inputs)) + (map + (comp get-uri get-rel-path + (fn [{:keys [out-file] :as ijs}] + (if-not out-file + (throw (Exception. (str "No :out-file for IJavaScript " (pr-str ijs)))) + out-file)))) + (distinct)) + entries)])) + (expand-modules modules inputs)) + (:advanced :simple) + (reduce-kv + (fn [ret k {:keys [output-to]}] + (assoc ret k [(-> output-to get-rel-path get-uri)])) + {:cljs-base [(-> (or (get-in modules [:cljs-base :output-to]) + (io/file output-dir "cljs_base.js")) + get-rel-path get-uri)]} + modules))))) + +(defn modules->module-infos + "Given a :modules map return a Closure module info map which maps modules + to depended upon modules." + [modules] + (let [modules (-> modules add-cljs-base add-cljs-base-dep)] + (reduce-kv + (fn [ret module-name {:keys [depends-on] :or {depends-on []} :as module}] + (assoc ret module-name depends-on)) + {} modules))) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 928bad86a..eb78ef5a8 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -118,20 +118,20 @@ (.getName f)) (defn ^String path [x] - {:pre [(or (file? x) (url? x) (string? x))]} (cond (file? x) (.getAbsolutePath ^File x) (url? x) (.getPath ^URL x) - (string? x) x)) + (string? x) x + :else (throw (Exception. (str "Expected file, url, or string. Got " (pr-str x)))))) (defn ^String ext "Given a file, url or string return the file extension." [x] - {:pre [(or (file? x) (url? x) (string? x))]} (let [s (cond (file? x) (filename x) (url? x) (path x) - (string? x) x)] + (string? x) x + :else (throw (Exception. (str "Expected file, url, or string. Got " (pr-str x)))))] (last (string/split s #"\.")))) (defn ^String get-name diff --git a/src/test/cljs_build/loader_test/bar.cljs b/src/test/cljs_build/loader_test/bar.cljs new file mode 100644 index 000000000..8b1925ae6 --- /dev/null +++ b/src/test/cljs_build/loader_test/bar.cljs @@ -0,0 +1,6 @@ +(ns loader-test.bar + (:require [cljs.loader :as loader])) + +(enable-console-print!) + +(println "Hello from bar!") \ No newline at end of file diff --git a/src/test/cljs_build/loader_test/foo.cljs b/src/test/cljs_build/loader_test/foo.cljs new file mode 100644 index 000000000..614b3db50 --- /dev/null +++ b/src/test/cljs_build/loader_test/foo.cljs @@ -0,0 +1,6 @@ +(ns loader-test.foo + (:require [cljs.loader :as loader])) + +(enable-console-print!) + +(println "Hello from foo!") \ No newline at end of file diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 47f29b194..015de6b4e 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -170,3 +170,27 @@ (is false) (catch Throwable e (is true))))) + +(defn loader-test-project [output-dir] + {:inputs (str (io/file "src" "test" "cljs_build" "loader_test")) + :opts + {:output-dir output-dir + :optimizations :none + :verbose true + :modules + {:foo + {:output-to (str (io/file output-dir "foo.js")) + :entries #{'loader-test.foo}} + :bar + {:output-to (str (io/file output-dir "bar.js")) + :entries #{'loader-test.bar}}}}}) + +(comment + + (deftest cljs-2077-test-loader + (let [out "out" + project (loader-test-project (str out)) + modules (-> project :opts :modules)] + (build (inputs (:inputs project)) (:opts project)))) + + ) \ No newline at end of file diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj new file mode 100644 index 000000000..ee1087346 --- /dev/null +++ b/src/test/clojure/cljs/module_graph_tests.clj @@ -0,0 +1,127 @@ +;; 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.module-graph-tests + (:require [clojure.test :refer [deftest is testing]] + [cljs.module-graph :as module-graph])) + +(def opts {:output-dir "out"}) + +(defn modules [{:keys [output-dir] :as opts}] + {:shared {:entries '[shared.a shared.b] + :output-to (str output-dir "/shared.js")} + :page1 {:entries '[page1.a page1.b] + :depends-on [:shared] + :output-to (str output-dir "/page1.js")} + :page2 {:entries '[page2.a page2.b] + :depends-on [:shared] + :output-to (str output-dir "/page2.js")}}) + +(defn inputs [{:keys [output-dir] :as opts}] + [{:provides '[goog] + :out-file (str output-dir "/goog/base.js")} + {:provides '[cljs.core] + :out-file (str output-dir "/cljs/core.js")} + {:provides ["cljs.reader"] + :requires ["cljs.core"] + :out-file (str output-dir "/cljs/reader.js")} + {:provides '[events "event.types"] + :requires ["cljs.core"] + :out-file (str output-dir "/events.js")} + {:provides ["shared.a"] + :requires ["cljs.core"] + :out-file (str output-dir "/shared/a.js")} + {:provides ["shared.b"] + :requires ["cljs.core"] + :out-file (str output-dir "/shared/b.js")} + {:provides ["page1.a"] + :requires ["cljs.core" "cljs.reader" "events" "shared.a"] + :out-file (str output-dir "/page1/a.js")} + {:provides ["page1.b"] + :requires '[cljs.core shared.b] + :out-file (str output-dir "/page1/b.js")} + {:provides ["page2.a"] + :requires ["cljs.core" "events" "shared.a"] + :out-file (str output-dir "/page2/a.js")} + {:provides ["page2.b"] + :requires ["cljs.core" "shared.b"] + :out-file (str output-dir "/page2/b.js")}]) + +(deftest test-add-cljs-base + (is (true? (contains? (module-graph/add-cljs-base (modules opts)) :cljs-base)))) + +(deftest test-add-cljs-base-dep + (let [modules' (-> (modules opts) + module-graph/add-cljs-base + module-graph/add-cljs-base-dep)] + (is (not (some #{:cljs-base} (get-in modules' [:cljs-base :depends-on])))) + (is (some #{:cljs-base} (get-in modules' [:shared :depends-on]))) + (is (not (some #{:cljs-base} (get-in modules' [:page1 :depends-on])))) + (is (not (some #{:cljs-base} (get-in modules' [:page2 :depends-on])))))) + +(deftest test-module-deps + (let [modules (-> (modules opts) + module-graph/add-cljs-base + module-graph/add-cljs-base-dep)] + (is (= (module-graph/deps-for-module :page1 modules) + [:cljs-base :shared])))) + +(deftest test-entry-deps + (let [inputs (module-graph/index-inputs (inputs opts))] + (is (= (module-graph/deps-for-entry "page2.a" inputs) + ["cljs.core" "events" "shared.a"])))) + +(deftest test-canonical-name + (let [ins (module-graph/index-inputs (inputs opts))] + (is (= "events" (module-graph/canonical-name 'events ins))) + (is (= "events" (module-graph/canonical-name "events" ins))) + (is (= "events" (module-graph/canonical-name 'event.types ins))) + (is (= "events" (module-graph/canonical-name "event.types" ins))))) + +(deftest test-inputs->assigned-modules + (let [modules' (-> (modules opts) + module-graph/add-cljs-base + module-graph/add-cljs-base-dep + module-graph/annotate-depths) + inputs' (inputs opts)] + (module-graph/inputs->assigned-modules inputs' modules'))) + +(def bad-modules + {:page1 {:entries '[page1.a page1.b events] + :output-to "out/page1.js"} + :page2 {:entries '[page2.a page2.b event.types] + :output-to "out/page2.js"}}) + +(deftest test-duplicate-entries + (let [modules' (-> bad-modules + module-graph/add-cljs-base + module-graph/add-cljs-base-dep) + index (module-graph/index-inputs (inputs opts))] + (is (= (try + (module-graph/validate-modules modules' index) + (catch Throwable t + :caught)) + :caught)))) + +(deftest test-module->module-uris + (is (= (module-graph/modules->module-uris (modules opts) (inputs opts) + {:output-dir (:output-dir opts) + :asset-path "/asset/js" + :optimizations :none}) + {:shared ["/asset/js/shared/a.js" "/asset/js/shared/b.js"] + :page1 ["/asset/js/cljs/reader.js" "/asset/js/page1/a.js" "/asset/js/page1/b.js"] + :page2 ["/asset/js/page2/a.js" "/asset/js/page2/b.js"] + :cljs-base ["/asset/js/goog/base.js" "/asset/js/cljs/core.js" "/asset/js/events.js"]})) + (is (= (module-graph/modules->module-uris (modules opts) (inputs opts) + {:output-dir (:output-dir opts) + :asset-path "/asset/js" + :optimizations :advanced}) + {:cljs-base ["/asset/js/cljs_base.js"] + :shared ["/asset/js/shared.js"] + :page1 ["/asset/js/page1.js"] + :page2 ["/asset/js/page2.js"]}))) \ No newline at end of file From 4a70832510e821cbdf811aa4ee552a312c22d2ee Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 2 Jul 2017 02:22:25 -0400 Subject: [PATCH 2452/4033] fix map->javascript-file to copy :url if available ijs maps can make into optimize, handle them --- src/main/clojure/cljs/closure.clj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index db863c0f2..5f6b2c49f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -415,7 +415,7 @@ (merge (javascript-file (:foreign m) - (when-let [f (:file m)] + (when-let [f (or (:file m) (:url m))] (deps/to-url f)) (when-let [sf (:source-file m)] (deps/to-url sf)) @@ -1289,7 +1289,14 @@ sources (if (= :whitespace (:optimizations opts)) (cons "var CLOSURE_NO_DEPS = true;" sources) sources) - ^List inputs (map #(js-source-file (javascript-name %) %) sources) + ^List inputs (doall + (map + (fn [source] + (let [source (cond-> source + (and (not (record? source)) (map? source)) + map->javascript-file)] + (js-source-file (javascript-name source) source))) + sources)) ^Result result (util/measure (:compiler-stats opts) "Optimizing with Google Closure Compiler" (.compile closure-compiler externs inputs compiler-options))] From 5c48ff8c583fcfc1d1c723073e333f9ba65e847a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 2 Jul 2017 13:06:38 -0400 Subject: [PATCH 2453/4033] enhance cljs.loader for hot-code reloading --- src/main/cljs/cljs/loader.cljs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 8f40a35dd..7d9fb4117 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -25,14 +25,13 @@ (defn create-module-manager [] (let [mm (ModuleManager.) ml (ModuleLoader.)] - ;(.setSourceUrlInjection ml true) - ;(.setBatchModeEnabled mm true) (.setLoader mm ml) - (.setAllModuleInfo mm (to-js module-infos)) - (.setModuleUris mm (to-js module-uris)) mm)) -(def ^:dynamic *module-manager* (create-module-manager)) +(defonce ^:dynamic *module-manager* (create-module-manager)) + +(.setAllModuleInfo *module-manager* (to-js module-infos)) +(.setModuleUris *module-manager* (to-js module-uris)) (defn load "Load a module. module-name should be a keyword matching a :modules module From addaf39e861191dab6aeb0189c66f5f6ef132918 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 2 Jul 2017 22:44:34 -0400 Subject: [PATCH 2454/4033] CLJS-2151: Rollback removal of dependency information for node targeted compilation revert commit cdaeff298e0f1d410aa5a7b6860232270d287084 --- src/main/clojure/cljs/closure.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5f6b2c49f..9690b5958 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1393,9 +1393,9 @@ "\", [" (ns-list (deps/-provides input)) "], [" - ;; under Node.js runtime require is possible - (when-not (= :nodejs (:target opts)) - (ns-list (deps/-requires input))) + ;; even under Node.js where runtime require is possible + ;; this is necessary - see CLJS-2151 + (ns-list (deps/-requires input)) "]);\n"))) (defn deps-file From 5a1ac949b2855efbb13521ec45b16751c523f9f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 2 Jul 2017 23:36:50 -0400 Subject: [PATCH 2455/4033] tweak browser REPL logging for simple HTTP server case --- src/main/clojure/cljs/repl/browser.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index cbf2ccbb3..4ded7ccbd 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -250,8 +250,10 @@ (assoc old :client-js (create-client-js-file repl-env (io/file working-dir "client.js"))))) - (repl/err-out (println "Waiting for browser to connect ...")) opts + (repl/err-out + (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) + (println "Listening for browser REPL connect ...")) (server/start repl-env))) (defrecord BrowserEnv [] From 507c843c4ed3572b4e3d0172033f7a2c314c1583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 2 Jul 2017 22:14:27 -0700 Subject: [PATCH 2456/4033] CLJS-2154: Provide compiler info & timing when compiling modules --- src/main/clojure/cljs/closure.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9690b5958..0fb111f07 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1255,6 +1255,8 @@ ^List inputs (map (comp :closure-module second) modules) _ (doseq [^JSModule input inputs] (.sortInputsByDeps input closure-compiler)) + _ (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Applying optimizations" (:optimizations opts) "to" (count sources) "sources")) ^Result result (.compileModules closure-compiler externs inputs compiler-options) ^SourceMap source-map (when (:source-map opts) (.getSourceMap closure-compiler))] @@ -2340,7 +2342,9 @@ ":simple, or :advanced optimizations with :output-to"))) (if (:modules all-opts) (->> - (apply optimize-modules all-opts js-sources) + (util/measure compiler-stats + (str "Optimizing " (count js-sources) " sources") + (apply optimize-modules all-opts js-sources)) (output-modules all-opts js-sources)) (let [fdeps-str (foreign-deps-str all-opts (filter foreign-source? js-sources)) From 15c1eb2d511c2f494400183bab1d9eca611777d6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 3 Jul 2017 09:11:26 -0400 Subject: [PATCH 2457/4033] CLJS-2157: Automatically generate cljs.loader/set-loaded! call --- src/main/clojure/cljs/compiler.cljc | 66 +++++++++++++++++------------ 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index ce8a77bd3..86422031a 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1287,6 +1287,14 @@ :relpaths {(util/path src) (util/ns->relpath (first (:provides opts)) (:ext opts))}}))))) +(defn module-for-entry [entry modules] + (->> modules + (filter + (fn [[module-name {:keys [entries]}]] + (some #{(-> entry munge str)} + (map #(-> % munge str) entries)))) + ffirst)) + #?(:clj (defn emit-source [src dest ext opts] (with-open [out ^java.io.Writer (io/make-writer dest {})] @@ -1337,32 +1345,38 @@ :name ns-name})) (emit ast) (recur (rest forms) ns-name deps)))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :macros-ns (:macros-ns opts) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj ana/constants-ns-sym))) - :file dest - :out-file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= :none (:optimizations opts))) - (emit-source-map src dest sm-data - (merge opts {:ext ext :provides [ns-name]}))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src (ana/parse-ns src) output-dir :write) - src)) - ret)))))))))) + (do + (when-let [module (and (contains? (set (vals deps)) 'cljs.loader) + (module-for-entry ns-name (:modules opts)))] + (emit + (ana/analyze env `(cljs.loader/set-loaded! ~module) + nil opts))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :macros-ns (:macros-ns opts) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj ana/constants-ns-sym))) + :file dest + :out-file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= :none (:optimizations opts))) + (emit-source-map src dest sm-data + (merge opts {:ext ext :provides [ns-name]}))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src (ana/parse-ns src) output-dir :write) + src)) + ret))))))))))) #?(:clj (defn compile-file* From 2687119f9c82ba4fd991cd5031396fbf46168def Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 3 Jul 2017 09:26:10 -0400 Subject: [PATCH 2458/4033] assert the module exists in c.l/load and c.l/set-loaded! update c.l/set-loaded! docstring to clarify use cases --- src/main/cljs/cljs/loader.cljs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 7d9fb4117..a284e0fda 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -39,6 +39,8 @@ ([module-name] (load module-name nil)) ([module-name cb] + (assert (contains? module-infos module-name) + (str "Module " module-name " does not exist")) (let [mname (-> module-name name munge)] (if-not (nil? cb) (.execOnLoad *module-manager* mname cb) @@ -47,8 +49,12 @@ (defn set-loaded! "Set a module as being loaded. module-name should be a keyword matching a :modules module definition. Will mark all parent modules as also being - loaded." + loaded. Note that calls to this function will be automatically generated + as the final expression for known :modules entry points that require the + cljs.loader namespace." [module-name] + (assert (contains? module-infos module-name) + (str "Module " module-name " does not exist")) (let [xs (deps-for module-name module-infos)] (doseq [x xs] (.setLoaded *module-manager* (munge-kw x))) From 4d44089fe2047d05d0a59f4298079b331bde57c3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 3 Jul 2017 11:46:51 -0400 Subject: [PATCH 2459/4033] CLJS-2158: cljs_base module generates empty goog.require --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0fb111f07..cc0b0edd6 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1473,7 +1473,8 @@ (str "document.write('');\n")) (if-let [entries (:entries opts)] entries - [(:main opts)])))))))) + (when-let [main (:main opts)] + [main]))))))))) (defn output-modules "Given compiler options, original IJavaScript sources and a sequence of From 1d2ec690ca15276fa6ee3493d13c8ec133de9dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 3 Jul 2017 12:33:49 -0700 Subject: [PATCH 2460/4033] CLJS-2161: Bump Closure Compiler to June 2017 release --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 7720a955e..e73bb2c53 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20170521 + v20170626 org.clojure diff --git a/project.clj b/project.clj index 25bc66350..e5827ff9f 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] - [com.google.javascript/closure-compiler-unshaded "v20170521"] + [com.google.javascript/closure-compiler-unshaded "v20170626"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 6c9930cc2..c31a7bd5e 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,7 +5,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha16" SPEC_ALPHA_RELEASE="0.1.108" CORE_SPECS_ALPHA_RELEASE="0.1.10" -CLOSURE_RELEASE="20170521" +CLOSURE_RELEASE="20170626" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cc0b0edd6..26850e27c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -63,7 +63,7 @@ CommandLineRunner AnonymousFunctionNamingPolicy JSModule SourceMap ProcessCommonJSModules AbstractCompiler TransformAMDToCJSModule - ProcessEs6Modules CompilerInput] + Es6RewriteModules CompilerInput] [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey From 79d2f96f2bb940e8cc35d5afeb91b2da64b75445 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 3 Jul 2017 15:41:55 -0400 Subject: [PATCH 2461/4033] remove unused imports --- src/main/clojure/cljs/closure.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 26850e27c..30db70eb0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -61,9 +61,7 @@ SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy - JSModule SourceMap ProcessCommonJSModules - AbstractCompiler TransformAMDToCJSModule - Es6RewriteModules CompilerInput] + JSModule SourceMap] [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey From fb6c04f4ae16877ba7fa797e9378d8a1ba74fd89 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 3 Jul 2017 15:16:00 -0400 Subject: [PATCH 2462/4033] CLJS-2148: Add unsafe-get and use goog.object Eliminates invalid use of aget / aset --- src/main/cljs/cljs/core.cljs | 18 +++++++++--------- src/main/cljs/cljs/reader.cljs | 5 +++-- src/main/clojure/cljs/core.cljc | 16 +++++++++++----- src/test/cljs/cljs/core_test.cljs | 16 +++++++++++----- src/test/cljs/cljs/hashing_test.cljs | 5 +++-- src/test/cljs/cljs/primitives_test.cljs | 13 +++++++------ src/test/cljs/cljs/reader_test.cljs | 8 ++++---- 7 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3a3a2b09a..ddb0a919f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -255,8 +255,8 @@ [p x] (let [x (if (nil? x) nil x)] (cond - (aget p (goog/typeOf x)) true - (aget p "_") true + (unsafe-get p (goog/typeOf x)) true + (unsafe-get p "_") true :else false))) (set! *unchecked-if* false) @@ -887,7 +887,7 @@ (defn add-to-string-hash-cache [k] (let [h (hash-string* k)] - (aset string-hash-cache k h) + (gobject/set string-hash-cache k h) (set! string-hash-cache-count (inc string-hash-cache-count)) h)) @@ -897,7 +897,7 @@ (set! string-hash-cache-count 0)) (if (nil? k) 0 - (let [h (aget string-hash-cache k)] + (let [h (unsafe-get string-hash-cache k)] (if (number? h) h (add-to-string-hash-cache k))))) @@ -2927,7 +2927,7 @@ reduces them without incurring seq initialization" [obj fn-map] (doseq [[key-name f] fn-map] (let [str-name (name key-name)] - (aset obj str-name f))) + (gobject/set obj str-name f))) obj) ;;;;;;;;;;;;;;;; cons ;;;;;;;;;;;;;;;; @@ -5998,7 +5998,7 @@ reduces them without incurring seq initialization" (loop [i 0] (when (< i l) (let [k (aget ks i)] - (aset new-obj k (aget obj k)) + (gobject/set new-obj k (gobject/get obj k)) (recur (inc i))))) new-obj)) @@ -8458,7 +8458,7 @@ reduces them without incurring seq initialization" (loop [kvs (seq keyvals)] (if kvs (do (.push ks (first kvs)) - (aset obj (first kvs) (second kvs)) + (gobject/set obj (first kvs) (second kvs)) (recur (nnext kvs))) (.fromObject ObjMap ks obj))))) @@ -9532,7 +9532,7 @@ reduces them without incurring seq initialization" [s] (str \" (.replace s (js/RegExp "[\\\\\"\b\f\n\r\t]" "g") - (fn [match] (aget char-escapes match))) + (fn [match] (unsafe-get char-escapes match))) \")) (declare print-map) @@ -10151,7 +10151,7 @@ reduces them without incurring seq initialization" (symbol? x) (str x) (map? x) (let [m (js-obj)] (doseq [[k v] x] - (aset m (key->js k) (clj->js v))) + (gobject/set m (key->js k) (clj->js v))) m) (coll? x) (let [arr (array)] (doseq [x (map clj->js x)] diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index bcb3ba7e1..e87e16a72 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -8,7 +8,8 @@ (ns cljs.reader (:require-macros [cljs.reader :refer [add-data-readers]]) - (:require [goog.string :as gstring]) + (:require [goog.object :as gobject] + [goog.string :as gstring]) (:import goog.string.StringBuffer)) (defprotocol PushbackReader @@ -580,7 +581,7 @@ nil if the end of stream has been reached") (map? form) (let [obj (js-obj)] (doseq [[k v] form] - (aset obj (name k) v)) + (gobject/set obj (name k) v)) obj) :else diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 757175bb7..02c904bb5 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -993,6 +993,12 @@ astr (apply core/str (repeat n "[~{}]"))] `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~a ~idx ~idx2 ~@idxv)))) +(core/defmacro unsafe-get + "Efficient alternative to goog.object/get which lacks opt_val and emits + unchecked property access." + [obj key] + (core/list 'js* "(~{}[~{}])" obj key)) + (core/defmacro ^::ana/numeric + ([] 0) ([x] x) @@ -1376,9 +1382,9 @@ (core/let [psym (resolve p) pfn-prefix (subs (core/str psym) 0 (clojure.core/inc (.indexOf (core/str psym) "/")))] - (cons `(aset ~psym ~type true) + (cons `(goog.object/set ~psym ~type true) (map (core/fn [[f & meths :as form]] - `(aset ~(symbol (core/str pfn-prefix f)) + `(goog.object/set ~(symbol (core/str pfn-prefix f)) ~type ~(with-meta `(fn ~@meths) (meta form)))) sigs)))) @@ -1977,10 +1983,10 @@ (not (nil? (. ~(first sig) ~(symbol (core/str "-" slot)))))) ;; Property access needed here. (. ~(first sig) ~slot ~@sig) (let [x# (if (nil? ~(first sig)) nil ~(first sig)) - m# (aget ~(fqn fname) (goog/typeOf x#))] + m# (unsafe-get ~(fqn fname) (goog/typeOf x#))] (if-not (nil? m#) (m# ~@sig) - (let [m# (aget ~(fqn fname) "_")] + (let [m# (unsafe-get ~(fqn fname) "_")] (if-not (nil? m#) (m# ~@sig) (throw @@ -2740,7 +2746,7 @@ (core/list 'js* "''+~{}" s)) (core/defmacro es6-iterable [ty] - `(aset (.-prototype ~ty) cljs.core/ITER_SYMBOL + `(goog.object/set (.-prototype ~ty) cljs.core/ITER_SYMBOL (fn [] (this-as this# (cljs.core/es6-iterator this#))))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index c6b0416a9..a6ee5a0a2 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -10,7 +10,8 @@ (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] [clojure.string :as s] - [clojure.set :as set])) + [clojure.set :as set] + [goog.object :as gobject])) (deftest test-metadata (testing "Testing metadata" @@ -143,11 +144,11 @@ (is (goog/isArray (clj->js #{}))) (is (goog/isArray (clj->js '()))) (is (goog/isObject (clj->js {}))) - (is (= (aget (clj->js {:a 1}) "a") 1)) + (is (= (gobject/get (clj->js {:a 1}) "a") 1)) (is (= (-> (clj->js {:a {:b {{:k :ey} :d}}}) - (aget "a") - (aget "b") - (aget "{:k :ey}")) + (gobject/get "a") + (gobject/get "b") + (gobject/get "{:k :ey}")) "d"))))) (deftest test-delay @@ -544,6 +545,11 @@ (is (= 0 (loop [x 0] (cond-> x false recur)))) (is (= 0 (loop [x 0] (cond->> x false recur))))) +(deftest unsafe-get-test + (is (= 1 (unsafe-get #js {:a 1} "a"))) + (is (nil? (unsafe-get #js {:a 1} "b"))) + (is (nil? (unsafe-get #js {:a 1} nil)))) + ;; ============================================================================= ;; Tickets diff --git a/src/test/cljs/cljs/hashing_test.cljs b/src/test/cljs/cljs/hashing_test.cljs index f96a21d09..e40178341 100644 --- a/src/test/cljs/cljs/hashing_test.cljs +++ b/src/test/cljs/cljs/hashing_test.cljs @@ -10,10 +10,11 @@ (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] [clojure.string :as s] - [clojure.set :as set])) + [clojure.set :as set] + [goog.object :as gobject])) (deftest test-hash-null - (is (zero? (hash (aget (js-obj) "foo"))))) + (is (zero? (hash (gobject/get (js-obj) "foo"))))) ;; hashing bug in many JS runtimes CLJ-118 (deftest test-clj-118 diff --git a/src/test/cljs/cljs/primitives_test.cljs b/src/test/cljs/cljs/primitives_test.cljs index 7233288d8..7656e5c75 100644 --- a/src/test/cljs/cljs/primitives_test.cljs +++ b/src/test/cljs/cljs/primitives_test.cljs @@ -10,7 +10,8 @@ (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] [clojure.string :as s] - [clojure.set :as set])) + [clojure.set :as set] + [goog.object :as gobject])) (deftest test-js-primitives ;; js primitives @@ -939,17 +940,17 @@ (is (= (alength #js [1 2 3]) 3)) (is (= (seq #js [1 2 3]) (seq [1 2 3]))) (is (= (set (js-keys #js {:foo "bar" :baz "woz"})) #{"foo" "baz"})) - (is (= (aget #js {:foo "bar"} "foo") "bar")) - (is (= (aget #js {"foo" "bar"} "foo") "bar")) - (is (array? (aget #js {"foo" #js [1 2 3]} "foo"))) - (is (= (seq (aget #js {"foo" #js [1 2 3]} "foo")) '(1 2 3))))) + (is (= (gobject/get #js {:foo "bar"} "foo") "bar")) + (is (= (gobject/get #js {"foo" "bar"} "foo") "bar")) + (is (array? (gobject/get #js {"foo" #js [1 2 3]} "foo"))) + (is (= (seq (gobject/get #js {"foo" #js [1 2 3]} "foo")) '(1 2 3))))) (deftest test-1556 (testing "Testing CLJS-1556, JS object literal code emission, beginning of statement" ;; Really testing that this evaluates properly (is (= 1 (do #js {:a 1} 1))) - (is (= 1 (aget #js {:a 1} "a"))) + (is (= 1 (gobject/get #js {:a 1} "a"))) (is (= 1 (.-a #js {:a 1}))))) (deftest test-char? diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 3d78d60b8..120716bf9 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -167,10 +167,10 @@ (is (= (alength (reader/read-string "#js [1 2 3]")) 3)) (is (= (seq (reader/read-string "#js [1 2 3]")) (seq [1 2 3]))) (is (= (set (js-keys (reader/read-string "#js {:foo \"bar\" :baz \"woz\"}"))) #{"foo" "baz"})) - (is (= (aget (reader/read-string "#js {:foo \"bar\"}") "foo") "bar")) - (is (= (aget (reader/read-string "#js {\"foo\" \"bar\"}") "foo") "bar")) - (is (array? (aget (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo"))) - (is (= (seq (aget (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo")) '(1 2 3))))) + (is (= (o/get (reader/read-string "#js {:foo \"bar\"}") "foo") "bar")) + (is (= (o/get (reader/read-string "#js {\"foo\" \"bar\"}") "foo") "bar")) + (is (array? (o/get (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo"))) + (is (= (seq (o/get (reader/read-string "#js {\"foo\" #js [1 2 3]}") "foo")) '(1 2 3))))) (deftest test-787 (testing "Testing reading, CLS-787" From 3b5d88a2546c07fc27da5014445f03ac15a9f22f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 1 Jul 2017 20:24:19 -0400 Subject: [PATCH 2463/4033] CLJS-2148: Add warnings for invalid use of aget and aset Defaults the warnings to false --- src/main/clojure/cljs/analyzer.cljc | 60 +++++++++++++++++++++--- src/test/clojure/cljs/analyzer_tests.clj | 24 ++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 999260d13..5fe63d9fc 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -134,6 +134,8 @@ :extending-base-js-type true :invoke-ctor true :invalid-arithmetic true + :invalid-aget false + :invalid-aset false :protocol-invalid-method true :protocol-duped-method true :protocol-multiple-impls true @@ -391,6 +393,24 @@ [warning-type info] (str (:js-op info) ", all arguments must be numbers, got " (:types info) " instead.")) +(defmethod error-message :invalid-aget + [warning-type info] + (str (:js-op info) ", arguments must be an array followed by numeric indices, got " (:types info) " instead" + (when (or (= 'object (first (:types info))) + (every? #{'string} (rest (:types info)))) + (str " (consider " + (if (== 2 (count (:types info))) + "goog.object/get" + "goog.object/getValueByKeys") + " for object access)")))) + +(defmethod error-message :invalid-aset + [warning-type info] + (str (:js-op info) ", arguments must be an array, followed by numeric indices, followed by a value, got " (:types info) " instead" + (when (or (= 'object (first (:types info))) + (every? #{'string} (butlast (rest (:types info))))) + " (consider goog.object/set for object access)"))) + (defmethod error-message :invoke-ctor [warning-type info] (str "Cannot invoke type constructor " (-> info :fexpr :info :name) " as function ")) @@ -2822,6 +2842,23 @@ (contains? t 'any) (contains? t 'js)))))) +(defn array-type? + #?(:cljs {:tag boolean}) + [t] + ;; TODO same inference caveats as the numeric-type? fn above + (cond + (nil? t) true + (= 'clj-nil t) true + (js-tag? t) true ;; TODO: revisit + :else + (if (and (symbol? t) (some? (get '#{any array} t))) + true + (when #?(:clj (set? t) + :cljs (cljs-set? t)) + (or (contains? t 'array) + (contains? t 'any) + (contains? t 'js)))))) + (defn analyze-js-star* [env jsform args form] (let [enve (assoc env :context :expr) argexprs (vec (map #(analyze enve %) args)) @@ -2829,13 +2866,24 @@ segs (js-star-seg jsform) tag (get-js-tag form) js-op (:js-op form-meta) - numeric (:numeric form-meta)] + numeric (:numeric form-meta) + validate (fn [warning-type valid-types?] + (let [types (map #(infer-tag env %) argexprs)] + (when-not (valid-types? types) + (warning warning-type env + {:js-op js-op + :types (into [] types)})))) + op-match? (fn [sym] + #?(:clj (= sym (:js-op form-meta)) + :cljs (symbol-identical? sym (:js-op form-meta))))] (when (true? numeric) - (let [types (map #(infer-tag env %) argexprs)] - (when-not (every? numeric-type? types) - (warning :invalid-arithmetic env - {:js-op js-op - :types (into [] types)})))) + (validate :invalid-arithmetic #(every? numeric-type? %))) + (when (op-match? 'cljs.core/aget) + (validate :invalid-aget #(and (array-type? (first %)) + (every? numeric-type? (rest %))))) + (when (op-match? 'cljs.core/aset) + (validate :invalid-aset #(and (array-type? (first %)) + (every? numeric-type? (butlast (rest %)))))) {:op :js :env env :segs segs diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index efdff10fa..c4b4ca0be 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -742,6 +742,30 @@ (catch Exception _)) (is (= ["Use of undeclared Var cljs.user/x"] @ws)))) +(deftest test-cljs-2148 + (binding [a/*cljs-warnings* (assoc a/*cljs-warnings* :invalid-aget true :invalid-aset true)] + (let [ws (atom [])] + (try + (a/with-warning-handlers [(collecting-warning-handler ws)] + (a/analyze (a/empty-env) + '(aget (js-obj) "a"))) + (catch Exception _)) + (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string] instead (consider goog.object/get for object access)"] @ws))) + (let [ws (atom [])] + (try + (a/with-warning-handlers [(collecting-warning-handler ws)] + (a/analyze (a/empty-env) + '(aget (js-obj) "foo" "bar"))) + (catch Exception _)) + (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string string] instead (consider goog.object/getValueByKeys for object access)"] @ws))) + (let [ws (atom [])] + (try + (a/with-warning-handlers [(collecting-warning-handler ws)] + (a/analyze (a/empty-env) + '(aset (js-obj) "a" 2))) + (catch Exception _)) + (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From 53e59c5ef3fbce1cce836df03344ae636c6787a3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Jul 2017 09:30:10 -0400 Subject: [PATCH 2464/4033] memoize c.m-g/deps-for --- src/main/clojure/cljs/module_graph.cljc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index 8e9f32a65..200405f84 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -103,7 +103,7 @@ (into ret (map #(vector (-> % munge str) input)) provides)) {} inputs)) -(defn deps-for +(defn ^:dynamic deps-for "Return all dependencies for x in a graph using deps-key." [x graph deps-key] (let [requires (get-in graph [x deps-key])] @@ -183,8 +183,10 @@ (update ret entry (fnil conj #{}) module-name)) ret (canon (f entries'))))) {} modules)) - e->ms (assigns identity) - d->ms (assigns #(distinct (mapcat deps %))) + e->ms (binding [deps-for (memoize deps-for)] + (assigns identity)) + d->ms (binding [deps-for (memoize deps-for)] + (assigns #(distinct (mapcat deps %)))) assigned (merge (into {} (map assign1) d->ms) (into {} (map assign1) e->ms)) From 41326b901848631b121c1a8929d31c93c5d318fe Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Jul 2017 10:12:03 -0400 Subject: [PATCH 2465/4033] CLJS-1998: Printing an Object with a null prototype throws an error Allow Objects with null constructors to be printable. --- src/main/cljs/cljs/core.cljs | 14 ++++++++------ src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ddb0a919f..600ea7ebd 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9606,15 +9606,17 @@ reduces them without incurring seq initialization" (regexp? obj) (write-all writer "#\"" (.-source obj) "\"") :else - (if (.. obj -constructor -cljs$lang$ctorStr) + (if (some-> obj .-constructor .-cljs$lang$ctorStr) (write-all writer "#object[" (.replace (.. obj -constructor -cljs$lang$ctorStr) (js/RegExp. "/" "g") ".") "]") - (let [name (.. obj -constructor -name) - name (if (or (nil? name) (gstring/isEmpty name)) - "Object" - name)] - (write-all writer "#object[" name " " (str obj) "]"))))))) + (let [name (some-> obj .-constructor .-name) + name (if (or (nil? name) (gstring/isEmpty name)) + "Object" + name)] + (if (nil? (. obj -constructor)) + (write-all writer "#object[" name "]") + (write-all writer "#object[" name " " (str obj) "]")))))))) (defn- pr-writer "Prefer this to pr-seq, because it makes the printing function diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index a6ee5a0a2..a6a7a6a25 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1359,6 +1359,10 @@ (testing "Resolve should return valid var" (is (= 1 ((resolve 'first) [1 2 3]))))) +(deftest test-cljs-1998 + (testing "printing an Object with a null constructor" + (is (= "#object[Object]" (pr-str (.create js/Object nil)))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 5e1ca777bbf7b50e70fb14977cd604e0fe26be96 Mon Sep 17 00:00:00 2001 From: Timothy Pote Date: Mon, 3 Jul 2017 18:05:53 -0500 Subject: [PATCH 2466/4033] Properly parse url string into file-path, query-string, and ref. --- src/main/clojure/cljs/repl/server.clj | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index b52fd7053..343c52549 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -60,6 +60,19 @@ (fn [old] (update-in old [method] #(conj (vec %) m)))))) +(defn parse-file-parts [file] + ;; This is a port of java.net.URL.Parts, which is package private. + (let [ref-idx (str/index-of file "#") + [file ref] (if ref-idx + [(subs file 0 ref-idx) (subs file (inc ref-idx))] + [file nil]) + q-idx (str/last-index-of file \?)] + (merge {:ref ref} + (if q-idx + {:path (subs file 0 q-idx) + :query-str (subs file (inc q-idx))} + {:path file})))) + ;;; assumes first line already consumed (defn parse-headers "Parse the headers of an HTTP POST request." @@ -80,21 +93,27 @@ (conj header-lines next-line))))) (defn read-post [line rdr] - (let [[_ path _] (str/split line #" ") + (let [[_ file _] (str/split line #" ") + {:keys [path ref query-str]} (parse-file-parts file) headers (parse-headers (read-headers rdr)) content-length (Integer/parseInt (:content-length headers)) content (char-array content-length)] (io! (.read rdr content 0 content-length) {:method :post :path path + :ref ref + :query-str query-str :headers headers :content (String. content)}))) (defn read-get [line rdr] - (let [[_ path _] (str/split line #" ") + (let [[_ file _] (str/split line #" ") + {:keys [path ref query-str]} (parse-file-parts file) headers (parse-headers (read-headers rdr))] {:method :get :path path + :ref ref + :query-str query-str :headers headers})) (defn read-request [rdr] From f965aea7b3bc66befc065912a1df4c336bb1eb8b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 4 Jul 2017 08:54:50 -0400 Subject: [PATCH 2467/4033] CLJS-2164: Require cljs.js results in warning about new unsafe-get macro --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 02c904bb5..cbcf43e12 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -44,7 +44,7 @@ defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? specify copy-arguments goog-define js-comment js-inline-comment - unsafe-cast require-macros use-macros gen-apply-to-simple])]) + unsafe-cast require-macros use-macros gen-apply-to-simple unsafe-get])]) #?(:cljs (:require-macros [cljs.core :as core] [cljs.support :refer [assert-args]])) (:require clojure.walk From d75b2531e4bcebf540272d106e08f1a083f64a6c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Jul 2017 10:46:20 -0400 Subject: [PATCH 2468/4033] stop blowing away :foreign-deps from :cljs-base module --- src/main/clojure/cljs/closure.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 30db70eb0..f60cc91c9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1141,9 +1141,8 @@ :foreign-deps @foreign-deps)]))) [] (module-graph/sort-modules (ensure-cljs-base-module - (module-graph/expand-modules (:modules opts) sources) opts))) - foreign-deps (atom [])] - (assoc-in modules [0 1 :foreign-deps] @foreign-deps))) + (module-graph/expand-modules (:modules opts) sources) opts)))] + modules)) (comment (build "samples/hello/src" From 0d47a4727b81e892d2f4947cd80af0799b6ae06d Mon Sep 17 00:00:00 2001 From: Petter Eriksson Date: Mon, 3 Jul 2017 11:08:51 -0700 Subject: [PATCH 2469/4033] CLJS-2160 Add loaded? and prefetch functions to cljs.loader --- src/main/cljs/cljs/loader.cljs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index a284e0fda..7caaf9aad 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -58,4 +58,28 @@ (let [xs (deps-for module-name module-infos)] (doseq [x xs] (.setLoaded *module-manager* (munge-kw x))) - (.setLoaded *module-manager* (munge-kw module-name)))) \ No newline at end of file + (.setLoaded *module-manager* (munge-kw module-name)))) + +(defn loaded? + "Return true if modules is loaded. module-name should be a keyword matching + a :modules module definition." + [module-name] + (assert (contains? module-infos module-name) + (str "Module " module-name " does not exist")) + (let [mname (-> module-name name munge) + module (.getModuleInfo *module-manager* mname)] + (when (some? module) + (.isLoaded module)))) + +(defn prefetch + "Prefetch a module. module-name should be a keyword matching a :modules + module definition. Will download the module but not evaluate it. To + complete module load, one must also call cljs.loader/load after prefetching + the module. Does nothing if the module is loading or has been loaded." + [module-name] + (assert (contains? module-infos module-name) + (str "Module " module-name " does not exist")) + (when-not (loaded? module-name) + (let [mname (-> module-name name munge)] + (when-not (.isModuleLoading *module-manager* mname) + (.prefetchModule *module-manager* mname))))) From 76ffac339c839041485fd4dc9fba50c95c26a6c2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Jul 2017 13:02:18 -0400 Subject: [PATCH 2470/4033] clarify unsafe-get docstring --- src/main/clojure/cljs/core.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index cbcf43e12..1a193762f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -994,8 +994,9 @@ `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~a ~idx ~idx2 ~@idxv)))) (core/defmacro unsafe-get - "Efficient alternative to goog.object/get which lacks opt_val and emits - unchecked property access." + "INTERNAL. Compiles to JavScript property access using bracket notation. Does + not distinguish between object and array types and not subject to compiler + static analysis." [obj key] (core/list 'js* "(~{}[~{}])" obj key)) From dd5d86223f359e06434a152ad0ea5e0bee317459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 4 Jul 2017 13:54:05 -0700 Subject: [PATCH 2471/4033] CLJS-2061: Support ns :require for JS libs, allow strings along with symbols The strategy employed by this patch is to defer processing JS modules until after we have found the sources and computed the "missing modules" in NS declarations. `cljs.analyzer/parse-ns` returns the extra `:missing-js-modules` key, which it infers by looking at string declarations in dependencies. --- samples/string-requires-npm-deps/.gitignore | 1 + samples/string-requires-npm-deps/README.md | 13 +++++++ samples/string-requires-npm-deps/build.clj | 13 +++++++ samples/string-requires-npm-deps/package.json | 3 ++ .../src/foo/core.cljs | 10 +++++ src/main/cljs/cljs/module_deps.js | 18 +++++++-- src/main/clojure/cljs/analyzer.cljc | 22 ++++++++--- src/main/clojure/cljs/closure.clj | 39 +++++++++++-------- src/test/clojure/cljs/analyzer_tests.clj | 12 ++++++ 9 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 samples/string-requires-npm-deps/.gitignore create mode 100644 samples/string-requires-npm-deps/README.md create mode 100644 samples/string-requires-npm-deps/build.clj create mode 100644 samples/string-requires-npm-deps/package.json create mode 100644 samples/string-requires-npm-deps/src/foo/core.cljs diff --git a/samples/string-requires-npm-deps/.gitignore b/samples/string-requires-npm-deps/.gitignore new file mode 100644 index 000000000..89f9ac04a --- /dev/null +++ b/samples/string-requires-npm-deps/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/samples/string-requires-npm-deps/README.md b/samples/string-requires-npm-deps/README.md new file mode 100644 index 000000000..010d79ecd --- /dev/null +++ b/samples/string-requires-npm-deps/README.md @@ -0,0 +1,13 @@ +# ClojureScript string-based requires demo + +Running: + +1. At the root of the ClojureScript repo, run `./script/bootstrap` +2. Switch into this directory: `cd samples/string-requires-npm-deps` +3. Build the project: + +``` shell +$ java -cp `ls ../../lib/*.jar | paste -sd ":" -`:../../src/main/cljs:../../src/main/clojure:src clojure.main build.clj +``` + +4. run the generated JavaScript with `node out/main.js` diff --git a/samples/string-requires-npm-deps/build.clj b/samples/string-requires-npm-deps/build.clj new file mode 100644 index 000000000..4b6ddc401 --- /dev/null +++ b/samples/string-requires-npm-deps/build.clj @@ -0,0 +1,13 @@ +(require '[cljs.build.api :as b]) + +(b/build "src" + {:output-dir "out" + :output-to "out/main.js" + :optimizations :none + :verbose true + :target :nodejs + :compiler-stats true + :main 'foo.core + :npm-deps {:react "15.6.1" + :react-dom "15.6.1"} + :closure-warnings {:non-standard-jsdoc :off}}) diff --git a/samples/string-requires-npm-deps/package.json b/samples/string-requires-npm-deps/package.json new file mode 100644 index 000000000..97a37b05b --- /dev/null +++ b/samples/string-requires-npm-deps/package.json @@ -0,0 +1,3 @@ +{ + "name": "string-requires-npm-deps" +} diff --git a/samples/string-requires-npm-deps/src/foo/core.cljs b/samples/string-requires-npm-deps/src/foo/core.cljs new file mode 100644 index 000000000..43cd8328e --- /dev/null +++ b/samples/string-requires-npm-deps/src/foo/core.cljs @@ -0,0 +1,10 @@ +(ns foo.core + (:require [react :refer [createElement]] + ["react-dom/server" :as rds :refer [renderToString]] + "create-react-class")) + +(enable-console-print!) + +(println "resolves single exports" create-react-class) + +(println (renderToString (createElement "div" nil "Hello World!"))) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index e8b08c608..f8259d26f 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -30,7 +30,7 @@ md.on('package', function (pkg) { }; if (pkg.name != null) { - pkgJson.provided = [ pkg.name ]; + pkgJson.provides = [ pkg.name ]; } if (pkg.main != null) { @@ -49,8 +49,8 @@ md.on('end', function() { for (var i = 0; i < pkgJsons.length; i++) { var pkgJson = pkgJsons[i]; - if (deps_files[pkgJson.main] != null && pkgJson.provided != null) { - deps_files[pkgJson.main].provides = pkgJson.provided; + if (deps_files[pkgJson.main] != null && pkgJson.provides != null) { + deps_files[pkgJson.main].provides = pkgJson.provides; } deps_files[pkgJson.file] = { file: pkgJson.file }; @@ -58,7 +58,17 @@ md.on('end', function() { var values = []; for (var key in deps_files) { - values.push(deps_files[key]); + var dep = deps_files[key]; + + if (dep.provides == null && !/node_modules\/[^/]*?\/package.json$/.test(dep.file)) { + var match = dep.file.match(/node_modules\/(.*)\.js(on)*$/) + + if (match != null){ + dep.provides = [ match[1] ]; + } + } + + values.push(dep); } process.stdout.write(JSON.stringify(values)); diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5fe63d9fc..0dea50848 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2106,13 +2106,13 @@ (str msg "; offending spec: " (pr-str spec))) (defn basic-validate-ns-spec [env macros? spec] - (when-not (or (symbol? spec) (sequential? spec)) + (when-not (or (symbol? spec) (string? spec) (sequential? spec)) (throw (error env (parse-ns-error-msg spec "Only [lib.ns & options] and lib.ns specs supported in :require / :require-macros")))) (when (sequential? spec) - (when-not (symbol? (first spec)) + (when-not (or (symbol? (first spec)) (string? (first spec))) (throw (error env (parse-ns-error-msg spec @@ -2210,7 +2210,7 @@ (recur fs ret true))))) (defn parse-require-spec [env macros? deps aliases spec] - (if (symbol? spec) + (if (or (symbol? spec) (string? spec)) (recur env macros? deps aliases [spec]) (do (basic-validate-ns-spec env macros? spec) @@ -2222,7 +2222,11 @@ [lib js-module-provides] (if-some [js-module-name (get-in @env/*compiler* [:js-module-index (str lib)])] [(symbol js-module-name) lib] [lib nil]) - {alias :as referred :refer renamed :rename :or {alias lib}} (apply hash-map opts) + {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] (if macros? [:require-macros :use-macros :rename-macros] [:require :use :rename])] (when-not (or (symbol? alias) (nil? alias)) @@ -3565,7 +3569,14 @@ (= "cljc" (util/ext src))) 'cljs.core$macros ns-name) - deps (merge (:uses ast) (:requires ast))] + deps (merge (:uses ast) (:requires ast)) + missing-js-modules (into #{} + (comp + (filter (fn [[k v]] + (and (or (string? k) (string? v)) + (not (js-module-exists? k))))) + (map val)) + deps)] (merge {:ns (or ns-name 'cljs.user) :provides [ns-name] @@ -3574,6 +3585,7 @@ (cond-> (conj (set (vals deps)) 'cljs.core) (get-in @env/*compiler* [:options :emit-constants]) (conj constants-ns-sym))) + :missing-js-modules missing-js-modules :file dest :source-file (when rdr src) :source-forms (when-not rdr src) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f60cc91c9..d0d390915 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1977,13 +1977,14 @@ #(ensure-cljs-base-module % opts))) (defn add-implicit-options - [{:keys [optimizations output-dir npm-deps] + [{:keys [optimizations output-dir npm-deps missing-js-modules] :or {optimizations :none output-dir "out"} :as opts}] (let [opts (cond-> (update opts :foreign-libs (fn [libs] - (into (index-node-modules npm-deps opts) + (into (index-node-modules + (into missing-js-modules (keys npm-deps)) opts) (expand-libs libs)))) (:closure-defines opts) (assoc :closure-defines @@ -2003,7 +2004,7 @@ :optimizations optimizations :output-dir output-dir :ups-libs libs - :ups-foreign-libs (into (index-node-modules (compute-upstream-npm-deps opts) opts) + :ups-foreign-libs (into (index-node-modules (keys (compute-upstream-npm-deps opts)) opts) (expand-libs foreign-libs)) :ups-externs externs :emit-constants emit-constants @@ -2134,15 +2135,15 @@ (into [] (distinct (mapcat #(node-module-deps % opts) entries))))) (defn index-node-modules - ([npm-deps] + ([modules] (index-node-modules - npm-deps + modules (when env/*compiler* (:options @env/*compiler*)))) - ([npm-deps opts] + ([modules opts] (let [node-modules (io/file "node_modules")] - (if (and (not (empty? npm-deps)) (.exists node-modules) (.isDirectory node-modules)) - (let [modules (map name (keys npm-deps)) + (if (and (not (empty? modules)) (.exists node-modules) (.isDirectory node-modules)) + (let [modules (into #{} (map name) modules) deps-file (io/file (str (util/output-directory opts) File/separator "cljs$node_modules.js"))] (util/mkdirs deps-file) @@ -2252,15 +2253,21 @@ (env/with-compiler-env compiler-env ;; we want to warn about NPM dep conflicts before installing the modules (check-npm-deps opts) + (maybe-install-node-deps! opts) (let [compiler-stats (:compiler-stats opts) static-fns? (or (and (= (:optimizations opts) :advanced) - (not (false? (:static-fns opts)))) - (:static-fns opts) - ana/*cljs-static-fns*) - all-opts (-> opts - maybe-install-node-deps! - add-implicit-options - process-js-modules)] + (not (false? (:static-fns opts)))) + (:static-fns opts) + ana/*cljs-static-fns*) + sources (-find-sources source opts) + missing-js-modules (into #{} + (comp + (map :missing-js-modules) + cat) + sources) + all-opts (-> (assoc opts :missing-js-modules missing-js-modules) + add-implicit-options + process-js-modules)] (check-output-to opts) (check-output-dir opts) (check-source-map opts) @@ -2275,7 +2282,7 @@ (assoc :target (:target opts)) (assoc :js-dependency-index (deps/js-dependency-index all-opts)) ;; Save list of sources for cljs.analyzer/locate-src - Juho Teperi - (assoc :sources (-find-sources source all-opts)))) + (assoc :sources sources))) (binding [comp/*recompiled* (when-not (false? (:recompile-dependents opts)) (atom #{})) ana/*cljs-static-fns* static-fns? diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index c4b4ca0be..6193aa22e 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -766,6 +766,18 @@ (catch Exception _)) (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))))) +(deftest cljs-2061 + (let [test-cenv (atom + (merge @(e/default-compiler-env) + {:js-module-index {"react" "module$src$test$cljs$react"}})) + ast (e/with-compiler-env test-cenv + (a/parse-ns + '[(ns test.cljs-2061 + (:require ["react" :as react] + ["react-dom/server" :refer [renderToString]]))]))] + (is (= (:missing-js-modules ast) #{"react-dom/server"})) + (is (= (:requires ast) '#{cljs.core module$src$test$cljs$react "react-dom/server"})))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From ba720b8505a7944f956da7cdc25dd32972e3e051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 4 Jul 2017 19:02:37 -0700 Subject: [PATCH 2472/4033] CLJS-2169: Error when compiling with :source-map and advanced optimizations --- src/main/clojure/cljs/closure.clj | 18 +++++----- src/test/clojure/cljs/build_api_tests.clj | 41 ++++++++++++----------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d0d390915..33b6347f7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -281,7 +281,7 @@ (. compiler-options (setExtraAnnotationNames (map name extra-annotations)))) (when (:source-map opts) (if (:modules opts) - ;; name is not actually used by Closur in :modules case, + ;; name is not actually used by Closure in :modules case, ;; but we need to provide _something_ for Closure to not ;; complain (set! (.sourceMapOutputPath compiler-options) @@ -779,7 +779,7 @@ "Returns the constants table as a JavaScriptFile." [opts] (let [url (deps/to-url (constants-filename opts))] - (javascript-file nil url url [(str ana/constants-ns-sym)] ["cljs.core"] nil nil))) + (javascript-file nil url [(str ana/constants-ns-sym)] ["cljs.core"]))) (defn add-dependencies "Given one or more IJavaScript objects in dependency order, produce @@ -1681,15 +1681,15 @@ (write-javascript opts js) ;; always copy original ClojureScript sources to the output directory ;; when source maps enabled - (let [out-file (when-let [ns (and (:source-map opts) - (:source-url js) - (first (:provides js)))] + (let [source-url (:source-url js) + out-file (when-let [ns (and (:source-map opts) + source-url + (first (:provides js)))] (io/file (io/file (util/output-directory opts)) - (util/ns->relpath ns (util/ext (:source-url js))))) - source-url (:source-url js)] + (util/ns->relpath ns (util/ext source-url))))] (when (and out-file source-url - (or (not (.exists ^File out-file)) - (util/changed? (io/file source-url) out-file))) + (or (not (.exists ^File out-file)) + (util/changed? (io/file source-url) out-file))) (do (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "Copying" (str source-url) "to" (str out-file))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 015de6b4e..3ee377cbe 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -8,19 +8,18 @@ (ns cljs.build-api-tests (:refer-clojure :exclude [compile]) - (:use cljs.build.api) (:use clojure.test) (:import java.io.File) (:require [clojure.java.io :as io] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.test-util :as test] - [cljs.build.api :as build])) + [cljs.build.api :as build :refer [build]])) (deftest test-target-file-for-cljs-ns - (is (= (.getPath (target-file-for-cljs-ns 'example.core-lib nil)) + (is (= (.getPath (build/target-file-for-cljs-ns 'example.core-lib nil)) "out/example/core_lib.js")) - (is (= (.getPath (target-file-for-cljs-ns 'example.core-lib "output")) + (is (= (.getPath (build/target-file-for-cljs-ns 'example.core-lib "output")) "output/example/core_lib.js"))) (deftest test-cljs-dependents-for-macro-namespaces @@ -41,13 +40,13 @@ 'example.fun {:require-macros nil :name 'example.fun }}) - (is (= (set (cljs-dependents-for-macro-namespaces ['example.macros])) + (is (= (set (build/cljs-dependents-for-macro-namespaces ['example.macros])) #{'example.core 'example.util})) - (is (= (set (cljs-dependents-for-macro-namespaces ['example.macros-again])) + (is (= (set (build/cljs-dependents-for-macro-namespaces ['example.macros-again])) #{'example.helpers})) - (is (= (set (cljs-dependents-for-macro-namespaces ['example.macros 'example.macros-again])) + (is (= (set (build/cljs-dependents-for-macro-namespaces ['example.macros 'example.macros-again])) #{'example.core 'example.util 'example.helpers})) - (is (= (set (cljs-dependents-for-macro-namespaces ['example.not-macros])) + (is (= (set (build/cljs-dependents-for-macro-namespaces ['example.not-macros])) #{})))) (def test-cenv (atom {})) @@ -136,7 +135,7 @@ project (test/project-with-modules (str out)) modules (-> project :opts :modules)] (test/delete-out-files out) - (build (inputs (:inputs project)) (:opts project)) + (build (build/inputs (:inputs project)) (:opts project)) (is (re-find #"Loading modules A and B" (slurp (-> modules :cljs-base :output-to)))) (is (re-find #"Module A loaded" (slurp (-> modules :module-a :output-to)))) (is (re-find #"Module B loaded" (slurp (-> modules :module-b :output-to)))))) @@ -150,7 +149,7 @@ :output-dir (str out) :target :nodejs}] (test/delete-out-files out) - (build (inputs (io/file root "foreign_libs") (io/file root "thirdparty")) opts) + (build (build/inputs (io/file root "foreign_libs") (io/file root "thirdparty")) opts) (let [foreign-lib-file (io/file out (-> opts :foreign-libs first :file))] (is (.exists foreign-lib-file)) (is (= (->> (slurp (io/file out "foreign_libs" "core.js")) @@ -162,7 +161,7 @@ (let [out-file (io/file "out/main.js")] (.delete out-file) (try - (build (inputs "src/test/cljs_build") + (build (build/inputs "src/test/cljs_build") {:main 'circular-deps.a :optimizations :none :verbose true @@ -185,12 +184,14 @@ {:output-to (str (io/file output-dir "bar.js")) :entries #{'loader-test.bar}}}}}) -(comment - - (deftest cljs-2077-test-loader - (let [out "out" - project (loader-test-project (str out)) - modules (-> project :opts :modules)] - (build (inputs (:inputs project)) (:opts project)))) - - ) \ No newline at end of file +(deftest cljs-2077-test-loader + (let [out "out" + project (merge-with merge (loader-test-project (str out))) + modules (-> project :opts :modules)] + (build (build/inputs (:inputs project)) (:opts project))) + (let [out "out" + project (merge-with merge (loader-test-project (str out)) + {:opts {:optimizations :advanced + :source-map true}}) + modules (-> project :opts :modules)] + (build (build/inputs (:inputs project)) (:opts project)))) From 444edad09deedf19abac16450172abf327086ada Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 5 Jul 2017 08:41:14 -0400 Subject: [PATCH 2473/4033] lock require of cljs.spec.alpha when registering specs --- src/main/clojure/cljs/analyzer.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 0dea50848..a0d141826 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3700,7 +3700,8 @@ "Registers speced vars found in a namespace analysis cache." [cached-ns] #?(:clj (try - (require 'cljs.spec.alpha) + (locking load-mutex + (clojure.core/require 'cljs.spec.alpha)) (catch Throwable t))) (let [{:keys [registry-ref speced-vars]} (get-spec-vars)] (when-let [registry (seq (:cljs.spec/registry-ref cached-ns))] From 4f5f1ac78e3742a374254eb7c2a3525ae6d5b476 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 5 Jul 2017 10:26:09 -0400 Subject: [PATCH 2474/4033] typo --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 1a193762f..5cdd14bb3 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -994,7 +994,7 @@ `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~a ~idx ~idx2 ~@idxv)))) (core/defmacro unsafe-get - "INTERNAL. Compiles to JavScript property access using bracket notation. Does + "INTERNAL. Compiles to JavaScript property access using bracket notation. Does not distinguish between object and array types and not subject to compiler static analysis." [obj key] From 84e38b638172b954f090f9bd11da74d3d76afb6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 4 Jul 2017 22:18:00 -0700 Subject: [PATCH 2475/4033] CLJS-2037: Throw if overwriting alias in current namespace --- src/main/clojure/cljs/analyzer.cljc | 17 +++++++++++++++++ src/test/clojure/cljs/analyzer_tests.clj | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a0d141826..1bd750eae 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2530,6 +2530,22 @@ (update-in [:requires] (fn [m] (with-meta m {(@reload :require) true}))))))))) +(defn- check-duplicate-aliases + [env old new] + (let [ns-name (:name old)] + (doseq [k [:requires :require-macros]] + (let [old-aliases (get old k) + new-aliases (get new k)] + (when-some [alias (some (set (keys new-aliases)) + (->> old-aliases + (remove (fn [[k v :as entry]] + (or (= k v) + (= entry (find new-aliases k))))) + keys))] + (throw (error env + (str "Alias " alias " already exists in namespace " ns-name + ", aliasing " (get old-aliases alias))))))))) + (defmethod parse 'ns* [_ env [_ quoted-specs :as form] _ opts] (when-let [not-quoted (->> (remove keyword? quoted-specs) @@ -2599,6 +2615,7 @@ (let [merge-keys [:use-macros :require-macros :rename-macros :uses :requires :renames :imports]] + (check-duplicate-aliases env ns-info' require-info) (merge ns-info' {:excludes excludes} diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 6193aa22e..3f942da41 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -778,6 +778,23 @@ (is (= (:missing-js-modules ast) #{"react-dom/server"})) (is (= (:requires ast) '#{cljs.core module$src$test$cljs$react "react-dom/server"})))) +(deftest test-cljs-2037 + (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] + (binding [a/*cljs-ns* a/*cljs-ns* + a/*analyze-deps* false] + (is (thrown-with-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" + (a/analyze test-env '(do + (require '[clojure.string :as str]) + (require '[clojure.set :as str]))))) + (is (thrown-with-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" + (a/analyze test-env '(do + (require-macros '[clojure.string :as str]) + (require-macros '[clojure.set :as str]))))) + (is (a/analyze test-env '(do + (require '[clojure.string :as str]) + (require '[clojure.string :as str]) + (require 'clojure.string))))))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From 440ac29f2d08ed22c945c4b145a57b819abdf38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 5 Jul 2017 09:51:23 -0700 Subject: [PATCH 2476/4033] CLJS-2173: Fix `npm install` when `:npm-deps` in Windows --- src/main/clojure/cljs/closure.clj | 3 ++- src/main/clojure/cljs/util.cljc | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 33b6347f7..3487eb380 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2063,7 +2063,8 @@ (when (or ana/*verbose* verbose) (util/debug-prn "Installing Node.js dependencies")) (let [proc (-> (ProcessBuilder. - (into ["npm" "install" "module-deps"] + (into (cond->> ["npm" "install" "module-deps"] + util/windows? (into ["cmd" "/c"])) (map (fn [[dep version]] (str (name dep) "@" version))) npm-deps)) .start) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index eb78ef5a8..8c3626dc0 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -306,3 +306,6 @@ 1 (first xs) 2 (str (first xs) " and " (second xs)) (str (string/join ", " (pop xs)) " and " (peek xs))))) + +(def windows? + (.startsWith (.toLowerCase (System/getProperty "os.name")) "windows")) From 6db8f0cce7c3768020ccd9d82e0f2c50caaceabb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 5 Jul 2017 10:38:33 -0700 Subject: [PATCH 2477/4033] CLJS-2174: Follow-up fixes to :npm-deps on Windows --- src/main/clojure/cljs/closure.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3487eb380..a00ff1c8e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2097,7 +2097,8 @@ (string/replace "JS_FILE" file) (string/replace "CLJS_TARGET" (str "" (when target (name target))))) proc (-> (ProcessBuilder. - ["node" "--eval" code]) + (cond->> ["node" "--eval" code] + util/windows? (into ["cmd" "/c"]))) .start) is (.getInputStream proc) iw (StringWriter. (* 16 1024 1024)) @@ -2145,8 +2146,7 @@ (let [node-modules (io/file "node_modules")] (if (and (not (empty? modules)) (.exists node-modules) (.isDirectory node-modules)) (let [modules (into #{} (map name) modules) - deps-file (io/file (str (util/output-directory opts) File/separator - "cljs$node_modules.js"))] + deps-file (io/file (util/output-directory opts) "cljs$node_modules.js")] (util/mkdirs deps-file) (with-open [w (io/writer deps-file)] (run! #(.write w (str "require('" % "');\n")) modules)) From 489dd729bb09c324e4028d4a73db5aa32b9483ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 5 Nov 2016 18:03:52 +0100 Subject: [PATCH 2478/4033] CLJS-1822: Use `:file-min` when processing JS modules with advanced optimizations --- src/main/clojure/cljs/closure.clj | 88 ++++++++++++++----- src/main/clojure/cljs/js_deps.cljc | 8 +- src/test/cljs/Circle-min.js | 14 +++ src/test/cljs/react-min.js | 20 +++++ .../clojure/cljs/module_processing_tests.clj | 66 ++++++++++---- 5 files changed, 149 insertions(+), 47 deletions(-) create mode 100644 src/test/cljs/Circle-min.js create mode 100644 src/test/cljs/react-min.js diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a00ff1c8e..55c98dac1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -367,35 +367,61 @@ String (-foreign? [this] false) (-closure-lib? [this] false) - (-url [this] nil) - (-relative-path [this] nil) + (-url + ([this] nil) + ([this _] nil)) + (-relative-path + ([this] nil) + ([this _] nil)) (-provides [this] (:provides (deps/parse-js-ns (string/split-lines this)))) (-requires [this] (:requires (deps/parse-js-ns (string/split-lines this)))) - (-source [this] this) + (-source + ([this] this) + ([this _] this)) clojure.lang.IPersistentMap (-foreign? [this] (:foreign this)) (-closure-lib? [this] (:closure-lib this)) - (-url [this] (or (:url this) - (deps/to-url (:file this)))) - (-relative-path [this] (let [file (io/as-file (:file this))] - (if (and file (not (.isAbsolute file))) - (:file this)))) + (-url + ([this] (deps/-url this nil)) + ([this opts] + (let [[url file] (if-let [url-min (and (#{:advanced :simple} (:optimizations opts)) + (:url-min this))] + [url-min (:file-min this)] + [(:url this) (:file this)])] + (or url (deps/to-url file))))) + (-relative-path + ([this] (deps/-relative-path this nil)) + ([this opts] + (let [file (if-let [file-min (and (#{:advanced :simple} (:optimizations opts)) + (:file-min this))] + file-min + (:file this)) + as-file (io/as-file file)] + (when (and as-file (not (.isAbsolute as-file))) + file)))) (-provides [this] (map name (:provides this))) (-requires [this] (map name (:requires this))) - (-source [this] (if-let [s (:source this)] - s (with-open [reader (io/reader (deps/-url this))] - (slurp reader))))) + (-source + ([this] (deps/-source this nil)) + ([this opts] + (if-let [s (:source this)] + s + (with-open [reader (io/reader (deps/-url this opts))] + (slurp reader)))))) (defrecord JavaScriptFile [foreign ^URL url ^URL source-url provides requires lines source-map] deps/IJavaScript (-foreign? [this] foreign) (-closure-lib? [this] (:closure-lib this)) (-url [this] url) + (-url [this opts] url) (-relative-path [this] nil) + (-relative-path [this opts] nil) (-provides [this] provides) (-requires [this] requires) - (-source [this] + (-source [this] (deps/-source this nil)) + (-source [this opts] (with-open [reader (io/reader url)] (slurp reader))) ISourceMap @@ -1539,12 +1565,12 @@ (when env/*compiler* (:options @env/*compiler*)))) ([js opts] - (let [url (deps/-url js)] + (let [url (deps/-url js opts)] (cond url (cond (deps/-closure-lib? js) (lib-rel-path js) - (deps/-foreign? js) (or (deps/-relative-path js) + (deps/-foreign? js) (or (deps/-relative-path js opts) (util/relative-name url)) :else (path-from-jarfile url)) @@ -1557,10 +1583,14 @@ :else (str (random-string 5) ".js"))))) -(defn get-source-files [js-modules] +(defn get-source-files [js-modules opts] (map (fn [lib] - (js-source-file (:file lib) (deps/-source lib))) - js-modules)) + (let [file (if-let [file-min (and (#{:advanced :simple} (:optimizations opts)) + (:file-min lib))] + file-min + (:file lib))] + (js-source-file file (deps/-source lib)))) + js-modules)) (defmulti convert-js-modules "Takes a list JavaScript modules as an IJavaScript and rewrites them into a Google @@ -1588,12 +1618,18 @@ (let [source-nodes (.children (get-js-root closure-compiler))] (into {} (map (juxt #(.getSourceFileName ^Node %) identity) source-nodes)))) -(defn add-converted-source [closure-compiler result-nodes {:keys [file] :as ijs}] - (assoc ijs :source (.toSource closure-compiler ^Node (get result-nodes file)))) +(defn add-converted-source + [closure-compiler result-nodes opts {:keys [file-min file] :as ijs}] + (let [processed-file (if-let [min (and (#{:advanced :simple} (:optimizations opts)) + file-min)] + min + file)] + (assoc ijs :source + (.toSource closure-compiler ^Node (get result-nodes processed-file))))) (defmethod convert-js-modules :commonjs [module-type js-modules opts] (let [^List externs '() - ^List source-files (get-source-files js-modules) + ^List source-files (get-source-files js-modules opts) ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setProcessCommonJSModules true) (.setTransformAMDToCJSModules (= module-type :amd))) @@ -1601,11 +1637,13 @@ (.init externs source-files options))] (.parse closure-compiler) (report-failure (.getResult closure-compiler)) - (map (partial add-converted-source closure-compiler (get-closure-sources closure-compiler)) js-modules))) + (map (partial add-converted-source + closure-compiler (get-closure-sources closure-compiler) opts) + js-modules))) (defmethod convert-js-modules :es6 [module-type js-modules opts] (let [^List externs '() - ^List source-files (get-source-files js-modules) + ^List source-files (get-source-files js-modules opts) ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setLanguageIn (lang-key->lang-mode :ecmascript6)) (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) @@ -1613,7 +1651,9 @@ (.init externs source-files options))] (.parse closure-compiler) (report-failure (.getResult closure-compiler)) - (map (partial add-converted-source closure-compiler (get-closure-sources closure-compiler)) js-modules))) + (map (partial add-converted-source + closure-compiler (get-closure-sources closure-compiler) opts) + js-modules))) (defmethod convert-js-modules :default [module-type js-modules opts] (ana/warning :unsupported-js-module-type @env/*compiler* (first js-modules)) @@ -2169,7 +2209,7 @@ (let [;; Load all modules - add :source so preprocessing and conversion can access it js-modules (map (fn [lib] (let [js (deps/load-foreign-library lib)] - (assoc js :source (deps/-source js)))) + (assoc js :source (deps/-source js opts)))) js-modules) js-modules (map (fn [js] (if (:preprocess js) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 6bc823b5f..02765fe82 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -67,7 +67,7 @@ If no ClassLoader is provided, RT/baseLoader is assumed." (when (.exists file) (map to-url (filter #(.endsWith ^String (.getName ^File %) ".js") (file-seq (io/file path))))))) -(defn find-js-classpath +(defn find-js-classpath "Returns a seq of URLs of all JavaScript files on the classpath." [path] (->> (all-classpath-urls) @@ -123,12 +123,12 @@ case." library (a js file that not have any goog.provide statement") (-closure-lib? [this] "Whether the Javascript represents a Closure style library") - (-url [this] "The URL where this JavaScript is located. Returns nil + (-url [this] [this opts] "The URL where this JavaScript is located. Returns nil when JavaScript exists in memory only.") - (-relative-path [this] "Relative path for this JavaScript.") + (-relative-path [this] [this opts] "Relative path for this JavaScript.") (-provides [this] "A list of namespaces that this JavaScript provides.") (-requires [this] "A list of namespaces that this JavaScript requires.") - (-source [this] "The JavaScript source string.")) + (-source [this] [this opts] "The JavaScript source string.")) (defn build-index "Index a list of dependencies by namespace and file name. There can diff --git a/src/test/cljs/Circle-min.js b/src/test/cljs/Circle-min.js new file mode 100644 index 000000000..dfe8e799c --- /dev/null +++ b/src/test/cljs/Circle-min.js @@ -0,0 +1,14 @@ +var React = require('./react-min'); + +var Circle = React.createClass({ + render: function() { + return( + + + + + ); + } +}); + +module.exports = Circle; diff --git a/src/test/cljs/react-min.js b/src/test/cljs/react-min.js new file mode 100644 index 000000000..9d2d11fe3 --- /dev/null +++ b/src/test/cljs/react-min.js @@ -0,0 +1,20 @@ +// React minified. +(function(f){ +if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()} +else if(typeof define==="function"&&define.amd){define([],f)} +else{var g;if(typeof window!=="undefined"){g=window} + else if(typeof global!=="undefined"){g=global} + else if(typeof self!=="undefined"){g=self} + else{g=this}g.React = f()} +})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o Date: Wed, 5 Jul 2017 14:21:21 -0700 Subject: [PATCH 2479/4033] CLJS-2176: module_deps.js: fix regexes for Windows paths --- src/main/cljs/cljs/module_deps.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index f8259d26f..14af1deb2 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -60,8 +60,8 @@ md.on('end', function() { for (var key in deps_files) { var dep = deps_files[key]; - if (dep.provides == null && !/node_modules\/[^/]*?\/package.json$/.test(dep.file)) { - var match = dep.file.match(/node_modules\/(.*)\.js(on)*$/) + if (dep.provides == null && !/node_modules[/\\][^/\\]*?[/\\]package.json$/.test(dep.file)) { + var match = dep.file.match(/node_modules[/\\](.*)\.js(on)*$/) if (match != null){ dep.provides = [ match[1] ]; From 37b7a69935c0624295e93dabffb3a4e3a85ddff6 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Thu, 6 Jul 2017 01:04:45 +0300 Subject: [PATCH 2480/4033] CLJS-2175: Add test to check ES6 module processing works --- src/test/cljs/es6_hello.js | 3 +++ .../clojure/cljs/module_processing_tests.clj | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/test/cljs/es6_hello.js diff --git a/src/test/cljs/es6_hello.js b/src/test/cljs/es6_hello.js new file mode 100644 index 000000000..cabd03a70 --- /dev/null +++ b/src/test/cljs/es6_hello.js @@ -0,0 +1,3 @@ +export var sayHello = function() { + console.log("Hello, world!"); +}; diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 8cf0e53e6..7ea6ee735 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -49,6 +49,32 @@ (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) +(deftest es6-module-processing + (test/delete-out-files) + (let [cenv (env/default-compiler-env)] + + ;; Reset load-library cache so that changes to processed files are noticed in REPL + (with-redefs [cljs.js-deps/load-library (memoize cljs.js-deps/load-library*)] + + (is (= {:foreign-libs [] + :ups-foreign-libs [] + :libs ["out/src/test/cljs/es6_hello.js"] + :closure-warnings {:non-standard-jsdoc :off}} + (env/with-compiler-env cenv + (closure/process-js-modules + {:foreign-libs [{:file "src/test/cljs/es6_hello.js" + :provides ["es6-hello"] + :module-type :es6}] + :closure-warnings {:non-standard-jsdoc :off}}))) + "processed modules are added to :libs") + + (is (= {"es6-hello" "module$src$test$cljs$es6-hello"} + (:js-module-index @cenv)) + "Processed modules are added to :js-module-index") + + (is (= "goog.provide(\"module$src$test$cljs$es6_hello\");var sayHello$$module$src$test$cljs$es6_hello=function(){console.log(\"Hello, world!\")};module$src$test$cljs$es6_hello.sayHello=sayHello$$module$src$test$cljs$es6_hello" + (slurp "out/src/test/cljs/es6_hello.js")))))) + (deftest test-module-name-substitution (test/delete-out-files) (let [cenv (env/default-compiler-env)] From e2fcea32ee234c3aaaf3ed0715401a8ce274eaf9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Jul 2017 18:25:06 -0400 Subject: [PATCH 2481/4033] CLJS-2175: ES6 Module processing broken with Closure v20170626 --- src/main/clojure/cljs/closure.clj | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 55c98dac1..c6da67374 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -61,7 +61,7 @@ SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy - JSModule SourceMap] + JSModule SourceMap Es6RewriteModules] [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey @@ -1648,8 +1648,11 @@ (.setLanguageIn (lang-key->lang-mode :ecmascript6)) (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) closure-compiler (doto (make-closure-compiler) - (.init externs source-files options))] - (.parse closure-compiler) + (.init externs source-files options)) + _ (.parse closure-compiler) + root (.getRoot closure-compiler)] + (.process (Es6RewriteModules. closure-compiler) + (.getFirstChild root) (.getSecondChild root)) (report-failure (.getResult closure-compiler)) (map (partial add-converted-source closure-compiler (get-closure-sources closure-compiler) opts) From 48b5287cb3d28f9de2853b3382ec2fe0c69bf131 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Jul 2017 18:30:33 -0400 Subject: [PATCH 2482/4033] report failure after parse when rewriting es6 modules --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c6da67374..38016c20b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1650,10 +1650,10 @@ closure-compiler (doto (make-closure-compiler) (.init externs source-files options)) _ (.parse closure-compiler) + _ (report-failure (.getResult closure-compiler)) root (.getRoot closure-compiler)] (.process (Es6RewriteModules. closure-compiler) (.getFirstChild root) (.getSecondChild root)) - (report-failure (.getResult closure-compiler)) (map (partial add-converted-source closure-compiler (get-closure-sources closure-compiler) opts) js-modules))) From e475004cefa7b1a65aeaefd7679abc9d70df305d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Jul 2017 18:58:36 -0400 Subject: [PATCH 2483/4033] make loader test actually test something --- src/test/clojure/cljs/build_api_tests.clj | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 3ee377cbe..43e3417f8 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -185,13 +185,14 @@ :entries #{'loader-test.bar}}}}}) (deftest cljs-2077-test-loader - (let [out "out" - project (merge-with merge (loader-test-project (str out))) - modules (-> project :opts :modules)] - (build (build/inputs (:inputs project)) (:opts project))) - (let [out "out" - project (merge-with merge (loader-test-project (str out)) + (test/delete-out-files) + (let [project (merge-with merge (loader-test-project "out")) + loader (io/file "out" "cljs" "loader.js")] + (build (build/inputs (:inputs project)) (:opts project)) + (is (.exists loader)) + (is (not (nil? (re-find #"/loader_test/foo\.js" (slurp loader)))))) + (test/delete-out-files) + (let [project (merge-with merge (loader-test-project "out") {:opts {:optimizations :advanced - :source-map true}}) - modules (-> project :opts :modules)] - (build (build/inputs (:inputs project)) (:opts project)))) + :source-map true}})] + (build (build/inputs (:inputs project)) (:opts project)))) From e00a00cb3447f686ca56caab99b9ad72a237849b Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Jul 2017 18:58:55 -0400 Subject: [PATCH 2484/4033] update loader test source files --- src/test/cljs_build/loader_test/bar.cljs | 5 ++++- src/test/cljs_build/loader_test/foo.cljs | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/test/cljs_build/loader_test/bar.cljs b/src/test/cljs_build/loader_test/bar.cljs index 8b1925ae6..fbd164b43 100644 --- a/src/test/cljs_build/loader_test/bar.cljs +++ b/src/test/cljs_build/loader_test/bar.cljs @@ -3,4 +3,7 @@ (enable-console-print!) -(println "Hello from bar!") \ No newline at end of file +(println "Hello from bar!") + +(defn woz [] + (println "Woz!")) diff --git a/src/test/cljs_build/loader_test/foo.cljs b/src/test/cljs_build/loader_test/foo.cljs index 614b3db50..4c46047cf 100644 --- a/src/test/cljs_build/loader_test/foo.cljs +++ b/src/test/cljs_build/loader_test/foo.cljs @@ -1,6 +1,15 @@ (ns loader-test.foo - (:require [cljs.loader :as loader])) + (:require [goog.dom :as gdom] + [goog.events :as events] + [cljs.loader :as loader]) + (:import [goog.events EventType])) (enable-console-print!) -(println "Hello from foo!") \ No newline at end of file +(println "Hello from foo!") + +(events/listen (gdom/getElement "button") EventType.CLICK + (fn [e] + (loader/load :bar + (fn [] + ((resolve 'loader-test.bar/woz)))))) From faed651bc6334dc008953215e242ceea96652e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 5 Jul 2017 17:48:57 -0700 Subject: [PATCH 2485/4033] CLJS-2177: NPM deps & JS modules fixes for Windows --- src/main/cljs/cljs/module_deps.js | 2 +- src/main/clojure/cljs/closure.clj | 9 ++++----- src/main/clojure/cljs/util.cljc | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 14af1deb2..5e239112f 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -64,7 +64,7 @@ md.on('end', function() { var match = dep.file.match(/node_modules[/\\](.*)\.js(on)*$/) if (match != null){ - dep.provides = [ match[1] ]; + dep.provides = [ match[1].replace('\\', '/') ]; } } diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 38016c20b..f9d03c8dc 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1623,7 +1623,8 @@ (let [processed-file (if-let [min (and (#{:advanced :simple} (:optimizations opts)) file-min)] min - file)] + file) + processed-file (string/replace processed-file "\\" "/")] (assoc ijs :source (.toSource closure-compiler ^Node (get result-nodes processed-file))))) @@ -2137,11 +2138,9 @@ (:options @env/*compiler*)))) ([{:keys [file]} {:keys [target] :as opts}] (let [code (-> (slurp (io/resource "cljs/module_deps.js")) - (string/replace "JS_FILE" file) + (string/replace "JS_FILE" (string/replace file "\\" "\\\\")) (string/replace "CLJS_TARGET" (str "" (when target (name target))))) - proc (-> (ProcessBuilder. - (cond->> ["node" "--eval" code] - util/windows? (into ["cmd" "/c"]))) + proc (-> (ProcessBuilder. ["node" "--eval" code]) .start) is (.getInputStream proc) iw (StringWriter. (* 16 1024 1024)) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 8c3626dc0..52611f469 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -13,7 +13,7 @@ [clojure.set :as set] [clojure.edn :as edn]) (:import [java.io File] - [java.net URL] + [java.net URL URLDecoder] [java.security MessageDigest] [javax.xml.bind DatatypeConverter])) @@ -153,7 +153,7 @@ (string/replace s user-path "")))] (if (file? x) (strip-user-dir (.getAbsolutePath x)) - (let [f (.getFile x)] + (let [f (URLDecoder/decode (.getFile x))] (if (string/includes? f ".jar!/") (last (string/split f #"\.jar!/")) (strip-user-dir f)))))) From c7f70877e7b920700e23ddf781f899d5eb9d7ad3 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 5 Jul 2017 22:53:03 +0300 Subject: [PATCH 2486/4033] CLJS-2143: Add support for symbol preprocess values If foreign-lib :preproces value is a symbol, the function referred by the symbol is used for preprocessing. Cljs will load the namespace the symbol points to if it is not loaded. --- src/main/clojure/cljs/closure.clj | 35 ++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f9d03c8dc..c2ad01c35 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2195,6 +2195,39 @@ (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)) [])))) +(defn preprocess-js + "Given js-module map, apply preprocessing defined by :preprocess value in the map." + [{:keys [preprocess] :as js-module} opts] + (cond + (keyword? preprocess) + (js-transforms js-module opts) + + (symbol? preprocess) + (let [preprocess-ns (symbol (namespace preprocess))] + + (when (not (find-ns preprocess-ns)) + (try + (locking ana/load-mutex + (require preprocess-ns)) + (catch Throwable t + (throw (ex-info (str "Cannot require namespace referred by :preprocess value " preprocess) + {:file (:file js-module) + :preprocess preprocess} + t))))) + + (try + ((find-var preprocess) js-module opts) + (catch Throwable t + (throw (ex-info (str "Error running preprocessing function " preprocess) + {:file (:file js-module) + :preprocess preprocess} + t))))) + + :else + (do + (ana/warning :unsupported-preprocess-value @env/*compiler* js-module) + js-module))) + (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google Closure modules and writes them to disk. Adds mapping from original module @@ -2215,7 +2248,7 @@ js-modules) js-modules (map (fn [js] (if (:preprocess js) - (js-transforms js opts) + (preprocess-js js opts) js)) js-modules) ;; Conversion is done per module-type, because Compiler needs to process e.g. all CommonJS From d08e2370e4c91e730461e2d67be3df0d267ed523 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 5 Jul 2017 22:57:03 -0400 Subject: [PATCH 2487/4033] check that preprocess symbol is fully qualified to avoid NPE --- src/main/clojure/cljs/closure.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c2ad01c35..a47cc6c08 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2203,8 +2203,13 @@ (js-transforms js-module opts) (symbol? preprocess) - (let [preprocess-ns (symbol (namespace preprocess))] - + (let [ns (namespace preprocess) + _ (when (nil? ns) + (throw + (ex-info (str "Preprocess symbol " preprocess " is not fully qualified") + {:file (:file js-module) + :preprocess preprocess}))) + preprocess-ns (symbol ns)] (when (not (find-ns preprocess-ns)) (try (locking ana/load-mutex From ae2a37088ca8f9f6eb32ebf785c21b21d54b5974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 5 Jul 2017 21:43:55 -0700 Subject: [PATCH 2488/4033] CLJS-2180: Allow compiling `:modules` with whitespace optimizations --- src/main/clojure/cljs/module_graph.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index 200405f84..1834e2a5c 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -269,7 +269,7 @@ all inputs by leveraging expand-modules." [modules inputs {:keys [optimizations asset-path output-dir] :as opts}] (assert optimizations "Must supply :optimizations in opts map") - (assert (#{:advanced :simple :none} optimizations) "Must supply valid :optimizations in opts map") + (assert (#{:advanced :simple :none :whitespace} optimizations) "Must supply valid :optimizations in opts map") (assert output-dir "Must supply :output-dir in opts map") (letfn [(get-uri [rel-path] (cond->> rel-path @@ -297,7 +297,7 @@ (distinct)) entries)])) (expand-modules modules inputs)) - (:advanced :simple) + (:advanced :simple :whitespace) (reduce-kv (fn [ret k {:keys [output-to]}] (assoc ret k [(-> output-to get-rel-path get-uri)])) From 773d60e5e8f65a0b4f95cd83db0365f2383a995a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 7 Jul 2017 11:39:21 -0400 Subject: [PATCH 2489/4033] CLJS-2186: Update docstrings for aget/aset to be consistent with Clojure --- src/main/cljs/cljs/core.cljs | 17 +++++++++-------- src/main/clojure/cljs/core.cljc | 16 ++++++++-------- src/test/cljs/cljs/core_test.cljs | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 600ea7ebd..9a1c188fa 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -413,16 +413,17 @@ a)))) (defn aget - "Returns the value at the index." - ([array i] - (cljs.core/aget array i)) - ([array i & idxs] - (apply aget (aget array i) idxs))) + "Returns the value at the index/indices. Works on JavaScript arrays." + ([array idx] + (cljs.core/aget array idx)) + ([array idx & idxs] + (apply aget (aget array idx) idxs))) (defn aset - "Sets the value at the index." - ([array i val] - (cljs.core/aset array i val)) + "Sets the value at the index/indices. Works on JavaScript arrays. + Returns val." + ([array idx val] + (cljs.core/aset array idx val)) ([array idx idx2 & idxv] (apply aset (aget array idx) idx2 idxv))) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 5cdd14bb3..885bbe800 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -979,19 +979,19 @@ (bool-expr `(instance? Keyword ~x))) (core/defmacro aget - ([a i] - (core/list 'js* "(~{}[~{}])" a i)) - ([a i & idxs] + ([array idx] + (core/list 'js* "(~{}[~{}])" array idx)) + ([array idx & idxs] (core/let [astr (apply core/str (repeat (count idxs) "[~{}]"))] - `(~'js* ~(core/str "(~{}[~{}]" astr ")") ~a ~i ~@idxs)))) + `(~'js* ~(core/str "(~{}[~{}]" astr ")") ~array ~idx ~@idxs)))) (core/defmacro aset - ([a i v] - (core/list 'js* "(~{}[~{}] = ~{})" a i v)) - ([a idx idx2 & idxv] + ([array idx val] + (core/list 'js* "(~{}[~{}] = ~{})" array idx val)) + ([array idx idx2 & idxv] (core/let [n (core/dec (count idxv)) astr (apply core/str (repeat n "[~{}]"))] - `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~a ~idx ~idx2 ~@idxv)))) + `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~array ~idx ~idx2 ~@idxv)))) (core/defmacro unsafe-get "INTERNAL. Compiles to JavaScript property access using bracket notation. Does diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index a6a7a6a25..4f4f1ff14 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -545,6 +545,26 @@ (is (= 0 (loop [x 0] (cond-> x false recur)))) (is (= 0 (loop [x 0] (cond->> x false recur))))) +(deftest aget-test + (is (= 11 (aget #js [10 11 12] 1))) + (is (= 11 (apply aget [#js [10 11 12] 1]))) + (is (= 3 (aget #js [1 2 #js [3 4]] 2 0))) + (is (= 3 (apply aget [#js [1 2 #js [3 4]] 2 0])))) + +(deftest aset-test + (let [array #js [10 11 12]] + (is (= 13 (aset array 1 13))) + (is (= 13 (aget array 1)))) + (let [array #js [10 11 12]] + (is (= 13 (apply aset [array 1 13]))) + (is (= 13 (aget array 1)))) + (let [array #js [1 2 #js [3 4]]] + (is (= 13 (aset array 2 0 13))) + (is (= 13 (aget array 2 0)))) + (let [array #js [1 2 #js [3 4]]] + (is (= 13 (apply aset [array 2 0 13]))) + (is (= 13 (aget array 2 0))))) + (deftest unsafe-get-test (is (= 1 (unsafe-get #js {:a 1} "a"))) (is (nil? (unsafe-get #js {:a 1} "b"))) From 0a9c2258d952aaeb6a5b9ace77fb26a83ecc3621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 6 Jul 2017 21:09:04 -0700 Subject: [PATCH 2490/4033] CLJS-2178: Add tests for `:npm-deps` --- src/test/cljs/Circle.js | 2 +- src/test/cljs/{react.js => reactJS.js} | 0 src/test/cljs_build/npm_deps_test/core.cljs | 6 +++ .../npm_deps_test/string_requires.cljs | 8 +++ src/test/clojure/cljs/build_api_tests.clj | 50 +++++++++++++++---- .../clojure/cljs/module_processing_tests.clj | 8 +-- 6 files changed, 60 insertions(+), 14 deletions(-) rename src/test/cljs/{react.js => reactJS.js} (100%) create mode 100644 src/test/cljs_build/npm_deps_test/core.cljs create mode 100644 src/test/cljs_build/npm_deps_test/string_requires.cljs diff --git a/src/test/cljs/Circle.js b/src/test/cljs/Circle.js index afc4280ec..898a6f632 100644 --- a/src/test/cljs/Circle.js +++ b/src/test/cljs/Circle.js @@ -1,4 +1,4 @@ -var React = require('./react'); +var React = require('./reactJS'); var Circle = React.createClass({ render: function() { diff --git a/src/test/cljs/react.js b/src/test/cljs/reactJS.js similarity index 100% rename from src/test/cljs/react.js rename to src/test/cljs/reactJS.js diff --git a/src/test/cljs_build/npm_deps_test/core.cljs b/src/test/cljs_build/npm_deps_test/core.cljs new file mode 100644 index 000000000..1391dadb8 --- /dev/null +++ b/src/test/cljs_build/npm_deps_test/core.cljs @@ -0,0 +1,6 @@ +(ns npm-deps-test.core + (:require left-pad)) + +(enable-console-print!) + +(println "Padded:" (left-pad 42 5 0)) diff --git a/src/test/cljs_build/npm_deps_test/string_requires.cljs b/src/test/cljs_build/npm_deps_test/string_requires.cljs new file mode 100644 index 000000000..3aa1ece4c --- /dev/null +++ b/src/test/cljs_build/npm_deps_test/string_requires.cljs @@ -0,0 +1,8 @@ +(ns npm-deps-test.string-requires + (:require [react :refer [createElement]] + ["react-dom/server" :as ReactDOMServer])) + +(enable-console-print!) + +(println "ReactDOMServer exists:" ReactDOMServer + (.-renderToString ReactDOMServer)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 43e3417f8..cac824de5 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -8,13 +8,13 @@ (ns cljs.build-api-tests (:refer-clojure :exclude [compile]) - (:use clojure.test) (:import java.io.File) - (:require [clojure.java.io :as io] + (:require [clojure.test :refer [deftest is testing]] + [clojure.java.io :as io] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.test-util :as test] - [cljs.build.api :as build :refer [build]])) + [cljs.build.api :as build])) (deftest test-target-file-for-cljs-ns (is (= (.getPath (build/target-file-for-cljs-ns 'example.core-lib nil)) @@ -126,7 +126,7 @@ (.deleteOnExit app-tmp) (is (every? #(zero? (.length %)) [common-tmp app-tmp]) "The initial files are empty") - (build srcs opts) + (build/build srcs opts) (is (not (every? #(zero? (.length %)) [common-tmp app-tmp])) "The files are not empty after compilation"))) @@ -135,7 +135,7 @@ project (test/project-with-modules (str out)) modules (-> project :opts :modules)] (test/delete-out-files out) - (build (build/inputs (:inputs project)) (:opts project)) + (build/build (build/inputs (:inputs project)) (:opts project)) (is (re-find #"Loading modules A and B" (slurp (-> modules :cljs-base :output-to)))) (is (re-find #"Module A loaded" (slurp (-> modules :module-a :output-to)))) (is (re-find #"Module B loaded" (slurp (-> modules :module-b :output-to)))))) @@ -149,7 +149,7 @@ :output-dir (str out) :target :nodejs}] (test/delete-out-files out) - (build (build/inputs (io/file root "foreign_libs") (io/file root "thirdparty")) opts) + (build/build (build/inputs (io/file root "foreign_libs") (io/file root "thirdparty")) opts) (let [foreign-lib-file (io/file out (-> opts :foreign-libs first :file))] (is (.exists foreign-lib-file)) (is (= (->> (slurp (io/file out "foreign_libs" "core.js")) @@ -161,7 +161,7 @@ (let [out-file (io/file "out/main.js")] (.delete out-file) (try - (build (build/inputs "src/test/cljs_build") + (build/build (build/inputs "src/test/cljs_build") {:main 'circular-deps.a :optimizations :none :verbose true @@ -188,11 +188,43 @@ (test/delete-out-files) (let [project (merge-with merge (loader-test-project "out")) loader (io/file "out" "cljs" "loader.js")] - (build (build/inputs (:inputs project)) (:opts project)) + (build/build (build/inputs (:inputs project)) (:opts project)) (is (.exists loader)) (is (not (nil? (re-find #"/loader_test/foo\.js" (slurp loader)))))) (test/delete-out-files) (let [project (merge-with merge (loader-test-project "out") {:opts {:optimizations :advanced :source-map true}})] - (build (build/inputs (:inputs project)) (:opts project)))) + (build/build (build/inputs (:inputs project)) (:opts project)))) + +(deftest test-npm-deps + (testing "simplest case, require" + (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'npm-deps-test.core + :output-dir out + :optimizations :none + :npm-deps {:left-pad "1.1.3"} + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (build/build (build/inputs (io/file inputs "npm_deps_test/core.cljs")) opts cenv) + (is (.exists (io/file out "node_modules/left-pad/index.js"))) + (is (contains? (:js-module-index @cenv) "left-pad")))) + (testing "mix of symbol & string-based requires" + (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'npm-deps-test.string-requires + :output-dir out + :optimizations :none + :npm-deps {:react "15.6.1" + :react-dom "15.6.1"} + :closure-warnings {:check-types :off + :non-standard-jsdoc :off}}} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (test/delete-out-files "node_modules") + (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) + (is (.exists (io/file out "node_modules/react/react.js"))) + (is (contains? (:js-module-index @cenv) "react")) + (is (contains? (:js-module-index @cenv) "react-dom/server"))))) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 7ea6ee735..79c1f017b 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -29,12 +29,12 @@ (with-redefs [cljs.js-deps/load-library (memoize cljs.js-deps/load-library*)] (is (= {:foreign-libs [] :ups-foreign-libs [] - :libs ["out/src/test/cljs/react.js" + :libs ["out/src/test/cljs/reactJS.js" "out/src/test/cljs/Circle.js"] :closure-warnings {:non-standard-jsdoc :off}} (env/with-compiler-env cenv (closure/process-js-modules - {:foreign-libs [{:file "src/test/cljs/react.js" + {:foreign-libs [{:file "src/test/cljs/reactJS.js" :provides ["React"] :module-type :commonjs} {:file "src/test/cljs/Circle.js" @@ -44,7 +44,7 @@ :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs")) - (is (= {"React" "module$src$test$cljs$react" + (is (= {"React" "module$src$test$cljs$reactJS" "Circle" "module$src$test$cljs$Circle"} (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) @@ -110,7 +110,7 @@ (env/with-compiler-env cenv (closure/process-js-modules {:optimizations :simple - :foreign-libs [{:file "src/test/cljs/react.js" + :foreign-libs [{:file "src/test/cljs/reactJS.js" :file-min "src/test/cljs/react-min.js" :provides ["React"] :module-type :commonjs} From 11700bb7c4fa4175875baa8f81c9304430b04f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 6 Jul 2017 19:44:02 -0700 Subject: [PATCH 2491/4033] CLJS-2185: Self-host: Docstrings for bootstrap helpers --- src/main/cljs/cljs/core.cljs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9a1c188fa..b3ffbb72a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10576,7 +10576,7 @@ reduces them without incurring seq initialization" (when-not target-fn (throw-no-method-error name dispatch-val)) (apply target-fn a b c d e f g h i j k l m n o p q r s t rest))) - + IMultiFn (-reset [mf] (swap! method-table (fn [mf] {})) @@ -11005,7 +11005,8 @@ reduces them without incurring seq initialization" (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) (defn ns-interns* - "Bootstrap only." + "Returns a map of the intern mappings for the namespace. + Bootstrap only." [sym] (let [ns-obj (find-ns-obj sym) ns (Namespace. ns-obj sym)] @@ -11017,14 +11018,15 @@ reduces them without incurring seq initialization" (reduce step {} (js-keys ns-obj))))) (defn create-ns - "Bootstrap only." + "Create a new namespace named by the symbol. Bootstrap only." ([sym] (create-ns sym (find-ns-obj sym))) ([sym ns-obj] (Namespace. ns-obj sym))) (defn find-ns - "Bootstrap only." + "Returns the namespace named by the symbol or nil if it doesn't exist. + Bootstrap only." [ns] (when (nil? NS_CACHE) (set! NS_CACHE (atom {}))) @@ -11038,7 +11040,8 @@ reduces them without incurring seq initialization" new-ns)))))) (defn find-macros-ns - "Bootstrap only." + "Returns the macros namespace named by the symbol or nil if it doesn't exist. + Bootstrap only." [ns] (when (nil? NS_CACHE) (set! NS_CACHE (atom {}))) @@ -11056,6 +11059,7 @@ reduces them without incurring seq initialization" new-ns)))))) (defn ns-name - "Bootstrap only." + "Returns the name of the namespace, a Namespace object. + Bootstrap only." [ns-obj] (.-name ns-obj)) From f303a08f6717627f5fc7471e5d0bd67153767a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 6 Jul 2017 13:46:24 -0700 Subject: [PATCH 2492/4033] CLJS-2182: Assert argument to resolve is a quoted symbol --- src/main/clojure/cljs/core.cljc | 11 +++++++---- src/test/clojure/cljs/analyzer_tests.clj | 7 +++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 885bbe800..0504b50a7 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3189,13 +3189,16 @@ (core/defmacro resolve "Returns the var to which a symbol will be resolved in the namespace else nil." - [[_ sym]] - (core/let [env &env + [quoted-sym] + (core/assert (core/and (seq? quoted-sym) (= (first quoted-sym 'quote))) + "Argument to resolve must be a quoted symbol") + (core/let [sym (second quoted-sym) + env &env [var meta] (try (core/let [var (ana/resolve-var env sym (ana/confirm-var-exists-throw)) ] [var (ana/var-meta var)]) (catch #?@(:clj [Throwable t] :cljs [:default e]) - [(ana/resolve-var env sym) nil])) + [(ana/resolve-var env sym) nil])) resolved (vary-meta (:name var) assoc ::ana/no-resolve true)] `(when (exists? ~resolved) - (cljs.core/Var. (fn [] ~resolved) '~resolved ~meta)))) \ No newline at end of file + (cljs.core/Var. (fn [] ~resolved) '~resolved ~meta)))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 3f942da41..e3090c251 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -795,6 +795,13 @@ (require '[clojure.string :as str]) (require 'clojure.string))))))) +(deftest test-cljs-2182 + (let [cenv (atom @test-cenv)] + (is (thrown-with-msg? Exception + #"Argument to resolve must be a quoted symbol" + (e/with-compiler-env test-cenv + (a/analyze test-env '(resolve foo.core))))))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From 6a6cdf1292414624271c189d4477e5f0eba77016 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Jul 2017 14:17:10 -0400 Subject: [PATCH 2493/4033] fix bad call to first --- src/main/clojure/cljs/core.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 0504b50a7..3a46fc8f3 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3190,7 +3190,9 @@ (core/defmacro resolve "Returns the var to which a symbol will be resolved in the namespace else nil." [quoted-sym] - (core/assert (core/and (seq? quoted-sym) (= (first quoted-sym 'quote))) + (core/assert + (core/and (seq? quoted-sym) + (= 'quote (first quoted-sym))) "Argument to resolve must be a quoted symbol") (core/let [sym (second quoted-sym) env &env From fbb3c3e4e8fa9cc9a2514d8091becb88d7b6c51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 6 Jul 2017 14:14:21 -0700 Subject: [PATCH 2494/4033] CLJS-2183: Assert arguments are quoted symbols in some core macros --- src/main/clojure/cljs/core.cljc | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3a46fc8f3..4e7336dda 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2754,23 +2754,29 @@ (core/defmacro ns-interns "Returns a map of the intern mappings for the namespace." - [[quote ns]] - (core/assert (core/and (= quote 'quote) (core/symbol? ns)) + [quoted-ns] + (core/assert (core/and (seq? quoted-ns) + (= (first quoted-ns) 'quote) + (core/symbol? (second quoted-ns))) "Argument to ns-interns must be a quoted symbol") - `(into {} - [~@(map - (core/fn [[sym _]] - `[(symbol ~(name sym)) (var ~(symbol (name ns) (name sym)))]) - (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))])) + (core/let [ns (second quoted-ns)] + `(into {} + [~@(map + (core/fn [[sym _]] + `[(symbol ~(name sym)) (var ~(symbol (name ns) (name sym)))]) + (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs]))]))) (core/defmacro ns-unmap "Removes the mappings for the symbol from the namespace." - [[quote0 ns] [quote1 sym]] - (core/assert (core/and (= quote0 'quote) (core/symbol? ns) - (= quote1 'quote) (core/symbol? sym)) + [quoted-ns quoted-sym] + (core/assert + (core/and (seq? quoted-ns) (= (first quoted-ns) 'quote) (core/symbol? (second quoted-ns)) + (seq? quoted-sym) (= (first quoted-sym) 'quote) (core/symbol? (second quoted-sym))) "Arguments to ns-unmap must be quoted symbols") - (swap! env/*compiler* update-in [::ana/namespaces ns :defs] dissoc sym) - `(js-delete ~(comp/munge ns) ~(comp/munge (core/str sym)))) + (core/let [ns (second quoted-ns) + sym (second quoted-sym)] + (swap! env/*compiler* update-in [::ana/namespaces ns :defs] dissoc sym) + `(js-delete ~(comp/munge ns) ~(comp/munge (core/str sym))))) (core/defmacro vswap! "Non-atomically swaps the value of the volatile as if: From 1c8dc308ef8097cd6d15a7befbed10fc29eb4148 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Jul 2017 14:37:34 -0400 Subject: [PATCH 2495/4033] npm test needs to create package.json --- src/test/clojure/cljs/build_api_tests.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index cac824de5..d780846ee 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -198,6 +198,7 @@ (build/build (build/inputs (:inputs project)) (:opts project)))) (deftest test-npm-deps + (spit (io/file "package.json") "{}") (testing "simplest case, require" (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) @@ -227,4 +228,5 @@ (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) (is (.exists (io/file out "node_modules/react/react.js"))) (is (contains? (:js-module-index @cenv) "react")) - (is (contains? (:js-module-index @cenv) "react-dom/server"))))) + (is (contains? (:js-module-index @cenv) "react-dom/server")))) + (.delete (io/file "package.json"))) From 50700973ed17512411286ff26a26379b7dffc294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 6 Jul 2017 15:13:04 -0700 Subject: [PATCH 2496/4033] CLJS-2184: Add `ns-publics` and `ns-imports` --- src/main/clojure/cljs/core.cljc | 33 ++++++++++++++++++++++++++++++- src/test/cljs/cljs/core_test.cljs | 11 +++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 4e7336dda..2509125a6 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -36,7 +36,8 @@ require use refer-clojure - if-some when-some test ns-interns ns-unmap var vswap! macroexpand-1 macroexpand + if-some when-some test ns-publics ns-imports ns-interns + ns-unmap var vswap! macroexpand-1 macroexpand some? resolve #?@(:cljs [alias coercive-not coercive-not= coercive-= coercive-boolean truth_ js-arguments js-delete js-in js-debugger exists? divide js-mod @@ -2752,6 +2753,36 @@ (this-as this# (cljs.core/es6-iterator this#))))) +(core/defmacro ns-publics + "Returns a map of the public intern mappings for the namespace." + [quoted-ns] + (core/assert (core/and (seq? quoted-ns) + (= (first quoted-ns) 'quote) + (core/symbol? (second quoted-ns))) + "Argument to ns-publics must be a quoted symbol") + (core/let [ns (second quoted-ns)] + `(into {} + [~@(map + (core/fn [[sym _]] + `[(symbol ~(name sym)) (var ~(symbol (name ns) (name sym)))]) + (filter (core/fn [[_ info]] + (not (core/-> info :meta :private))) + (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs])))]))) + +(core/defmacro ns-imports + "Returns a map of the import mappings for the namespace." + [quoted-ns] + (core/assert (core/and (seq? quoted-ns) + (= (first quoted-ns) 'quote) + (core/symbol? (second quoted-ns))) + "Argument to ns-imports must be a quoted symbol") + (core/let [ns (second quoted-ns)] + `(into {} + [~@(map + (core/fn [[ctor qualified-ctor]] + `[(symbol ~(name ctor)) ~(symbol qualified-ctor)]) + (get-in @env/*compiler* [:cljs.analyzer/namespaces ns :imports]))]))) + (core/defmacro ns-interns "Returns a map of the intern mappings for the namespace." [quoted-ns] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 4f4f1ff14..f18ae46a8 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1383,6 +1383,17 @@ (testing "printing an Object with a null constructor" (is (= "#object[Object]" (pr-str (.create js/Object nil)))))) +(deftest test-cljs-2184 + (testing "ns-publics" + (is (contains? (ns-publics 'clojure.string) 'join)) + (is (not (contains? (ns-publics 'clojure.string) 'replace-all))) + (is (= (find (ns-publics 'clojure.string) 'join) + ['join #'clojure.string/join]))) + (testing "ns-imports" + (is (contains? (ns-imports 'clojure.string) 'StringBuffer)) + (is (= (find (ns-imports 'clojure.string) 'StringBuffer) + ['StringBuffer goog.string.StringBuffer])))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 970b76c8b38e11420eb90d4ba4f4f4a9a2fc2a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 6 Jul 2017 11:36:21 -0700 Subject: [PATCH 2497/4033] CLJS-2181: Can't compile string sources with modules --- src/main/clojure/cljs/analyzer.cljc | 4 +-- src/main/clojure/cljs/closure.clj | 32 ++++++++++++++------ src/main/clojure/cljs/util.cljc | 16 +++++++--- src/test/clojure/cljs/build_api_tests.clj | 7 ++++- src/test/clojure/cljs/closure_tests.clj | 5 +++ src/test/clojure/cljs/module_graph_tests.clj | 2 ++ 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1bd750eae..f4faa1a1e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3517,9 +3517,7 @@ (inc (.lastIndexOf full-name "/")) (.lastIndexOf full-name "."))] (symbol - (apply str - "cljs.user." name - (take 7 (util/content-sha full-name))))))) + (str "cljs.user." name (util/content-sha full-name 7)))))) #?(:clj (defn parse-ns diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a47cc6c08..510546ac9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -373,7 +373,11 @@ (-relative-path ([this] nil) ([this _] nil)) - (-provides [this] (:provides (deps/parse-js-ns (string/split-lines this)))) + (-provides [this] + (let [{:keys [provides]} (deps/parse-js-ns (string/split-lines this))] + (cond-> provides + (empty? provides) + (conj (util/content-sha this 7))))) (-requires [this] (:requires (deps/parse-js-ns (string/split-lines this)))) (-source ([this] this) @@ -1052,7 +1056,9 @@ (defmethod javascript-name String [s] (if-let [name (first (deps/-provides s))] name "cljs/user.js")) -(defmethod javascript-name JavaScriptFile [js] (javascript-name (deps/-url js))) +(defmethod javascript-name JavaScriptFile [js] + (when-let [url (deps/-url js)] + (javascript-name url))) (defn build-provides "Given a vector of provides, builds required goog.provide statements" @@ -1060,8 +1066,10 @@ (apply str (map #(str "goog.provide('" % "');\n") provides))) (defmethod js-source-file JavaScriptFile [_ js] - (when-let [url (deps/-url js)] - (js-source-file (javascript-name url) (io/input-stream url)))) + (if-let [url (deps/-url js)] + (js-source-file (javascript-name url) (io/input-stream url)) + (when-let [source (:source js)] + (js-source-file (javascript-name source) source)))) (defn ensure-cljs-base-module "Ensure that compiler :modules map has :cljs-base module with defined @@ -1122,7 +1130,15 @@ a :foreign-deps vector containing foreign IJavaScript sources in dependency order." [sources opts] - (let [used (atom #{}) ;; track used inputs to avoid dupes + (let [sources (map + (fn [js] + (if (string? js) + (merge + (map->javascript-file {:provides (deps/-provides js)}) + {:source js}) + js)) + sources) + used (atom #{}) ;; track used inputs to avoid dupes modules (reduce (fn [ret [name {:keys [entries depends-on] :as module-desc}]] @@ -1575,11 +1591,7 @@ :else (path-from-jarfile url)) (string? js) - (str - (->> (util/content-sha js) - (take 7) - (apply str)) - ".js") + (str (util/content-sha js 7) ".js") :else (str (random-string 5) ".js"))))) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 52611f469..c5ded0b43 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -280,11 +280,17 @@ xs seen)))] (step coll #{})))) -(defn content-sha [^String s] - (let [digest (MessageDigest/getInstance "SHA-1")] - (.reset digest) - (.update digest (.getBytes s "utf8")) - (DatatypeConverter/printHexBinary (.digest digest)))) +(defn content-sha + ([^String s] + (content-sha s nil)) + ([^String s ^Long n] + (let [digest (MessageDigest/getInstance "SHA-1") + _ (.reset digest) + _ (.update digest (.getBytes s "utf8")) + sha (DatatypeConverter/printHexBinary (.digest digest))] + (if-not (nil? n) + (apply str (take n sha)) + sha)))) (defn map-merge [a b] (if (and (map? a) (map? b)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index d780846ee..1c787b7b7 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -195,7 +195,12 @@ (let [project (merge-with merge (loader-test-project "out") {:opts {:optimizations :advanced :source-map true}})] - (build/build (build/inputs (:inputs project)) (:opts project)))) + (build (build/inputs (:inputs project)) (:opts project))) + (testing "string inputs in modules" + (test/delete-out-files) + (let [project (merge-with merge (loader-test-project "out") + {:opts {:optimizations :whitespace}})] + (build (build/inputs (:inputs project)) (:opts project))))) (deftest test-npm-deps (spit (io/file "package.json") "{}") diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index a8c85c011..0f66fb36e 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -11,6 +11,8 @@ (:use cljs.closure clojure.test) (:require [cljs.build.api :as build] [cljs.closure :as closure] + [cljs.js-deps :as deps] + [cljs.util :as util] [cljs.test-util :as test] [clojure.java.io :as io] [clojure.string :as string]) @@ -68,3 +70,6 @@ ["cljs/core.js" "cljs/core/constants.js" "module_test/modules/a.js"]))))) + +(deftest test-string-provides + (is (= ["CB0BFFB"] (deps/-provides "var x = 42;")))) diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj index ee1087346..3af362611 100644 --- a/src/test/clojure/cljs/module_graph_tests.clj +++ b/src/test/clojure/cljs/module_graph_tests.clj @@ -8,6 +8,8 @@ (ns cljs.module-graph-tests (:require [clojure.test :refer [deftest is testing]] + [cljs.closure :as closure] + [cljs.util :as util] [cljs.module-graph :as module-graph])) (def opts {:output-dir "out"}) From 3e775a1c50464aac298de771dbfab33d0f138340 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Jul 2017 15:19:48 -0400 Subject: [PATCH 2498/4033] fix test --- src/test/clojure/cljs/build_api_tests.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 1c787b7b7..d409b2ea1 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -195,12 +195,12 @@ (let [project (merge-with merge (loader-test-project "out") {:opts {:optimizations :advanced :source-map true}})] - (build (build/inputs (:inputs project)) (:opts project))) + (build/build (build/inputs (:inputs project)) (:opts project))) (testing "string inputs in modules" (test/delete-out-files) (let [project (merge-with merge (loader-test-project "out") {:opts {:optimizations :whitespace}})] - (build (build/inputs (:inputs project)) (:opts project))))) + (build/build (build/inputs (:inputs project)) (:opts project))))) (deftest test-npm-deps (spit (io/file "package.json") "{}") From 50ee29659c78abec1d42629969613dc9aef136c9 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 7 Jul 2017 16:55:29 -0400 Subject: [PATCH 2499/4033] CLJS-2188: Use :invalid-array-access instead of :invalid-aget / :invalid-aset --- src/main/clojure/cljs/analyzer.cljc | 46 ++++++++++++------------ src/test/clojure/cljs/analyzer_tests.clj | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f4faa1a1e..5d91a5813 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -134,8 +134,7 @@ :extending-base-js-type true :invoke-ctor true :invalid-arithmetic true - :invalid-aget false - :invalid-aset false + :invalid-array-access false :protocol-invalid-method true :protocol-duped-method true :protocol-multiple-impls true @@ -393,23 +392,24 @@ [warning-type info] (str (:js-op info) ", all arguments must be numbers, got " (:types info) " instead.")) -(defmethod error-message :invalid-aget - [warning-type info] - (str (:js-op info) ", arguments must be an array followed by numeric indices, got " (:types info) " instead" - (when (or (= 'object (first (:types info))) - (every? #{'string} (rest (:types info)))) - (str " (consider " - (if (== 2 (count (:types info))) - "goog.object/get" - "goog.object/getValueByKeys") - " for object access)")))) - -(defmethod error-message :invalid-aset - [warning-type info] - (str (:js-op info) ", arguments must be an array, followed by numeric indices, followed by a value, got " (:types info) " instead" - (when (or (= 'object (first (:types info))) - (every? #{'string} (butlast (rest (:types info))))) - " (consider goog.object/set for object access)"))) +(defmethod error-message :invalid-array-access + [warning-type {:keys [js-op types]}] + (case js-op + cljs.core/aget + (str js-op ", arguments must be an array followed by numeric indices, got " types " instead" + (when (or (= 'object (first types)) + (every? #{'string} (rest types))) + (str " (consider " + (if (== 2 (count types)) + "goog.object/get" + "goog.object/getValueByKeys") + " for object access)"))) + + cljs.core/aset + (str js-op ", arguments must be an array, followed by numeric indices, followed by a value, got " types " instead" + (when (or (= 'object (first types)) + (every? #{'string} (butlast (rest types)))) + " (consider goog.object/set for object access)")))) (defmethod error-message :invoke-ctor [warning-type info] @@ -2900,11 +2900,11 @@ (when (true? numeric) (validate :invalid-arithmetic #(every? numeric-type? %))) (when (op-match? 'cljs.core/aget) - (validate :invalid-aget #(and (array-type? (first %)) - (every? numeric-type? (rest %))))) + (validate :invalid-array-access #(and (array-type? (first %)) + (every? numeric-type? (rest %))))) (when (op-match? 'cljs.core/aset) - (validate :invalid-aset #(and (array-type? (first %)) - (every? numeric-type? (butlast (rest %)))))) + (validate :invalid-array-access #(and (array-type? (first %)) + (every? numeric-type? (butlast (rest %)))))) {:op :js :env env :segs segs diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index e3090c251..65c517087 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -743,7 +743,7 @@ (is (= ["Use of undeclared Var cljs.user/x"] @ws)))) (deftest test-cljs-2148 - (binding [a/*cljs-warnings* (assoc a/*cljs-warnings* :invalid-aget true :invalid-aset true)] + (binding [a/*cljs-warnings* (assoc a/*cljs-warnings* :invalid-array-access true)] (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] From 8fe844e8bd3fe1cce2d775c9bd3d49edbbc51ed4 Mon Sep 17 00:00:00 2001 From: Enzzo Cavallo Date: Fri, 7 Jul 2017 23:30:58 -0300 Subject: [PATCH 2500/4033] Interaction with *print-namespace-maps* and js objects with slash in property name --- src/main/cljs/cljs/core.cljs | 3 ++- src/test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b3ffbb72a..933fd0912 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9754,7 +9754,8 @@ reduces them without incurring seq initialization" opts (seq m))) (defn print-map [m print-one writer opts] - (let [[ns lift-map] (lift-ns m)] + (let [[ns lift-map] (when (map? m) + (lift-ns m))] (if ns (print-prefix-map (str "#:" ns) lift-map print-one writer opts) (print-prefix-map nil m print-one writer opts)))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index f18ae46a8..05e34e20b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1393,6 +1393,11 @@ (is (contains? (ns-imports 'clojure.string) 'StringBuffer)) (is (= (find (ns-imports 'clojure.string) 'StringBuffer) ['StringBuffer goog.string.StringBuffer])))) +(deftest test-cljs-2190 + (binding [*print-namespace-maps* true] + (testing "printing a javascript map with a slash on keyword" + (is (= "#js {:foo/bar 33}" (pr-str (doto (js-obj) (gobject/set "foo/bar" 33))))) + (is (= "#js {:foo/bar #:var{:quux 66}}" (pr-str (doto (js-obj) (gobject/set "foo/bar" {:var/quux 66})))))))) (comment ;; ObjMap From 7c53ebe105be106bb02ebb477c7421b06380a0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 7 Jul 2017 16:25:34 -0700 Subject: [PATCH 2501/4033] CLJS-2189: Add test for :preloads --- src/test/cljs/preloads_test/core.cljs | 3 +++ src/test/cljs/preloads_test/preload.cljs | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 17 +++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 src/test/cljs/preloads_test/core.cljs create mode 100644 src/test/cljs/preloads_test/preload.cljs diff --git a/src/test/cljs/preloads_test/core.cljs b/src/test/cljs/preloads_test/core.cljs new file mode 100644 index 000000000..a775cdc65 --- /dev/null +++ b/src/test/cljs/preloads_test/core.cljs @@ -0,0 +1,3 @@ +(ns preloads-test.core) + +(def foo :foo) diff --git a/src/test/cljs/preloads_test/preload.cljs b/src/test/cljs/preloads_test/preload.cljs new file mode 100644 index 000000000..9f54615b7 --- /dev/null +++ b/src/test/cljs/preloads_test/preload.cljs @@ -0,0 +1,3 @@ +(ns preloads-test.preload) + +(def preload-var :foo) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index d409b2ea1..03f004473 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -235,3 +235,20 @@ (is (contains? (:js-module-index @cenv) "react")) (is (contains? (:js-module-index @cenv) "react-dom/server")))) (.delete (io/file "package.json"))) + +(deftest test-preloads + (let [out (.getPath (io/file (test/tmp-dir) "preloads-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs")) + :opts {:main 'preloads-test.core + :preloads '[preloads-test.preload] + :output-dir out + :optimizations :none + :npm-deps {:left-pad "1.1.3"} + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (build/build (build/inputs + (io/file inputs "preloads_test/core.cljs")) + opts cenv) + (is (.exists (io/file out "preloads_test/preload.cljs"))) + (is (contains? (get-in @cenv [::ana/namespaces 'preloads-test.preload :defs]) 'preload-var)))) From 9b426167644d166bf93773ebc7b40ebd6d1e5ed0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Jul 2017 10:58:44 -0400 Subject: [PATCH 2502/4033] need to suppress non-standard doc warnings even under none --- src/test/clojure/cljs/build_api_tests.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 03f004473..a33c15be8 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -211,7 +211,8 @@ :output-dir out :optimizations :none :npm-deps {:left-pad "1.1.3"} - :closure-warnings {:check-types :off}}} + :closure-warnings {:check-types :off + :non-standard-jsdoc :off}}} cenv (env/default-compiler-env)] (test/delete-out-files out) (build/build (build/inputs (io/file inputs "npm_deps_test/core.cljs")) opts cenv) From 8a496da0227e8dafc68d7e0fd0474f7a1183347b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 4 Jul 2017 08:28:57 -0400 Subject: [PATCH 2503/4033] CLJS-2163: Clean up uses of aget / aset on objects --- src/main/cljs/cljs/core.cljs | 20 ++++++++++---------- src/main/cljs/cljs/source_map.cljs | 4 ++-- src/main/cljs/clojure/browser/dom.cljs | 2 +- src/main/cljs/clojure/browser/net.cljs | 5 +++-- src/main/cljs/clojure/browser/repl.cljs | 2 +- src/main/clojure/cljs/core.cljc | 4 ++-- src/main/clojure/cljs/repl/nashorn.clj | 2 +- src/main/clojure/cljs/repl/node.clj | 4 ++-- src/main/clojure/cljs/repl/rhino.clj | 2 +- src/test/cljs/cljs/core_test.cljs | 4 ++++ src/test/self/self_parity/test.cljs | 7 ++++--- 11 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 933fd0912..a81e018e4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -446,7 +446,7 @@ "Invoke JavaScript object method via string. Needed when the string is not a valid unquoted property name." [obj s & args] - (.apply (aget obj s) obj (into-array args))) + (.apply (unsafe-get obj s) obj (into-array args))) ;;;;;;;;;;;;;;;;;;;;;;;;;;; core protocols ;;;;;;;;;;;;; @@ -5988,7 +5988,7 @@ reduces them without incurring seq initialization" out (transient (.-EMPTY PersistentHashMap))] (if (< i len) (let [k (aget ks i)] - (recur (inc i) (assoc! out k (aget so k)))) + (recur (inc i) (assoc! out k (gobject/get so k)))) (-with-meta (persistent! (assoc! out k v)) mm))))) ;;; ObjMap - DEPRECATED @@ -6036,7 +6036,7 @@ reduces them without incurring seq initialization" ISeqable (-seq [coll] (when (pos? (alength keys)) - (map #(vector % (aget strobj %)) + (map #(vector % (unsafe-get strobj %)) (.sort keys obj-map-compare-keys)))) ICounted @@ -6047,7 +6047,7 @@ reduces them without incurring seq initialization" (-lookup [coll k not-found] (if (and ^boolean (goog/isString k) (not (nil? (scan-array 1 k keys)))) - (aget strobj k) + (unsafe-get strobj k) not-found)) IAssociative @@ -6058,11 +6058,11 @@ reduces them without incurring seq initialization" (obj-map->hash-map coll k v) (if-not (nil? (scan-array 1 k keys)) (let [new-strobj (obj-clone strobj keys)] - (aset new-strobj k v) + (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)] - (aset new-strobj k v) + (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. @@ -6077,7 +6077,7 @@ reduces them without incurring seq initialization" (-find [coll k] (when (and ^boolean (goog/isString k) (not (nil? (scan-array 1 k keys)))) - [k (aget strobj k)])) + [k (unsafe-get strobj k)])) IKVReduce (-kv-reduce [coll f init] @@ -6086,7 +6086,7 @@ reduces them without incurring seq initialization" init init] (if (seq keys) (let [k (first keys) - init (f init k (aget strobj k))] + init (f init k (unsafe-get strobj k))] (if (reduced? init) @init (recur (rest keys) init))) @@ -9569,7 +9569,7 @@ reduces them without incurring seq initialization" (do (-write writer "#js ") (print-map - (map (fn [k] [(keyword k) (aget obj k)]) (js-keys obj)) + (map (fn [k] [(keyword k) (unsafe-get obj k)]) (js-keys obj)) pr-writer writer opts)) (array? obj) @@ -10191,7 +10191,7 @@ reduces them without incurring seq initialization" (identical? (type x) js/Object) (into {} (for [k (js-keys x)] - [(keyfn k) (thisfn (aget x k))])) + [(keyfn k) (thisfn (unsafe-get x k))])) :else x))] (f x)))) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index 7009bfd75..001b848c0 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -56,11 +56,11 @@ [seg source-map] (let [[gcol source line col name] seg] {:gcol gcol - :source (aget (gobj/get source-map "sources") source) + :source (unsafe-get (gobj/get source-map "sources") source) :line line :col col :name (when-let [name (-> seg meta :name)] - (aget (gobj/get source-map "names") name))})) + (unsafe-get (gobj/get source-map "names") name))})) (defn seg-combine "Combine a source map segment vector and a relative diff --git a/src/main/cljs/clojure/browser/dom.cljs b/src/main/cljs/clojure/browser/dom.cljs index 2b053a3f8..8c6a0a043 100644 --- a/src/main/cljs/clojure/browser/dom.cljs +++ b/src/main/cljs/clojure/browser/dom.cljs @@ -49,7 +49,7 @@ (log "v = " v) (when (or (keyword? k) (string? k)) - (doto o (aset (name k) v))))) + (doto o (gobject/set (name k) v))))) (js-obj) attrs) nil)] diff --git a/src/main/cljs/clojure/browser/net.cljs b/src/main/cljs/clojure/browser/net.cljs index bfd66dc06..016e10462 100644 --- a/src/main/cljs/clojure/browser/net.cljs +++ b/src/main/cljs/clojure/browser/net.cljs @@ -11,7 +11,8 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." :author "Bobby Calderwood and Alex Redington"} clojure.browser.net (:require [clojure.browser.event :as event] - [goog.json :as gjson]) + [goog.json :as gjson] + [goog.object :as gobj]) (:import [goog.net XhrIo EventType WebSocket] [goog.net.xpc CfgFields CrossPageChannel] [goog Uri])) @@ -132,7 +133,7 @@ Includes a common API over XhrIo, CrossPageChannel, and Websockets." (CrossPageChannel. (reduce (fn [sum [k v]] (if-let [field (get xpc-config-fields k)] - (doto sum (aset field v)) + (doto sum (gobj/set field v)) sum)) (js-obj) config)))) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index b661531cf..6fd45120b 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -171,7 +171,7 @@ (set! (.-cljsReloadAll_ js/goog) true)) (let [reload? (or reload (.-cljsReloadAll__ js/goog))] (when reload? - (let [path (aget js/goog.dependencies_.nameToPath src)] + (let [path (gobj/get js/goog.dependencies_.nameToPath src)] (gobj/remove js/goog.dependencies_.visited path) (gobj/remove js/goog.dependencies_.written path) (gobj/remove js/goog.dependencies_.written diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 2509125a6..7aa8ceaed 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2535,8 +2535,8 @@ (js-obj* '()) `(let [~@(apply concat (clojure.set/map-invert expr->local)) ~obj ~(js-obj* (filter-on-keys core/string? kvs))] - ~@(map (core/fn [[k v]] `(aset ~obj ~k ~v)) sym-pairs) - ~@(map (core/fn [[k v]] `(aset ~obj ~v ~(core/get kvs k))) expr->local) + ~@(map (core/fn [[k v]] `(gobject/set ~obj ~k ~v)) sym-pairs) + ~@(map (core/fn [[k v]] `(gobject/set ~obj ~v ~(core/get kvs k))) expr->local) ~obj)))) (core/defmacro alength [a] diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index d1fb12a7c..a96817a64 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -135,7 +135,7 @@ (when (or (not (contains? *loaded-libs* name)) reload) (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) + (goog.object/get (.. js/goog -dependencies_ -nameToPath) name))))))))) (-evaluate [{engine :engine :as this} filename line js] (when debug (println "Evaluating: " js)) (try diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index 64c7190d9..81c3ff7ce 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -179,7 +179,7 @@ '(set! (.-require js/goog) (fn [name] (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name))))) + (unsafe-get (.. js/goog -dependencies_ -nameToPath) name))))) ;; load cljs.core, setup printing (repl/evaluate-form repl-env env "" '(do @@ -195,7 +195,7 @@ (when (or (not (contains? *loaded-libs* name)) reload) (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name)))))))))) + (unsafe-get (.. js/goog -dependencies_ -nameToPath) name)))))))))) (defrecord NodeEnv [host port path socket proc] repl/IReplEnvOptions diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj index a5da15001..fd86c2098 100644 --- a/src/main/clojure/cljs/repl/rhino.clj +++ b/src/main/clojure/cljs/repl/rhino.clj @@ -153,7 +153,7 @@ (when (or (not (contains? *loaded-libs* name)) reload) (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name))))))))) + (goog.object/get (.. js/goog -dependencies_ -nameToPath) name))))))))) ;; Catching errors and rethrowing in Rhino swallows the original trace ;; https://groups.google.com/d/msg/mozilla.dev.tech.js-engine.rhino/inMyVKhPq6M/cY39hX20_z8J diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 05e34e20b..f410d02f0 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -570,6 +570,10 @@ (is (nil? (unsafe-get #js {:a 1} "b"))) (is (nil? (unsafe-get #js {:a 1} nil)))) +(deftest js-invoke-test + (let [o (doto (js-obj) (gobject/set "my sum" (fn [a b] (+ a b))))] + (is (= 5 (js-invoke o "my sum" 2 3))))) + ;; ============================================================================= ;; Tickets diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 3dc287aba..3bbf37a99 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -19,7 +19,8 @@ [cljs.nodejs :as nodejs] [cljs.js :as cljs] [cljs.tools.reader :as reader] - [cljs.stacktrace :as st])) + [cljs.stacktrace :as st] + [goog.object :as gobj])) (def out-dir "builds/out-self-parity") @@ -40,7 +41,7 @@ (set! (.-require js/goog) (fn [name] (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name)))) + (gobj/get (.. js/goog -dependencies_ -nameToPath) name)))) ;; setup printing (nodejs/enable-util-print!) ;; redef goog.require to track loaded libs @@ -50,7 +51,7 @@ (when (or (not (contains? *loaded-libs* name)) reload) (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) (js/CLOSURE_IMPORT_SCRIPT - (aget (.. js/goog -dependencies_ -nameToPath) name)))))) + (gobj/get (.. js/goog -dependencies_ -nameToPath) name)))))) ;; Node file reading fns From c2746c0270bf74ad667750d0f1e781866549221c Mon Sep 17 00:00:00 2001 From: Tom Kidd Date: Fri, 30 Sep 2016 09:40:15 -0400 Subject: [PATCH 2504/4033] CLJS-1797: Update aot_core to support build with MINGW on Windows In order to get Git Bash to work with ./script/build, aot_core needed to be updated to provide Maven's dependency:build-classpath plugin with new fileSeparator and pathSeparator values. FILE_SEP and PATH_SEP were added to aot_core, defaulting to unix conventions. The uname and tr commands are used to detect MINGW, and will update FILE_SEP and PATH_SEP if needed. Once these values are set, it was necessary to use them to ensure that the classpath is set correctly. PATH_SEP is now used to append src/main/clojure and src/main/cljs. --- script/aot_core | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/script/aot_core b/script/aot_core index 351ced5df..329d9b9bd 100755 --- a/script/aot_core +++ b/script/aot_core @@ -2,19 +2,27 @@ set -ex +FILE_SEP='/' +PATH_SEP=':' +OS_ID=`uname | tr [:upper:] [:lower:]` + +if [[ $OS_ID == *mingw* ]] +then + echo "MINGW detected" + # Refer to http://www.mingw.org/wiki/Posix_path_conversion + FILE_SEP='//' + PATH_SEP=';' +fi + CP_FILE=`mktemp /tmp/cljs_cp.txt.XXXXXXXXXXX` -mvn -f pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE +mvn -f pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP CLJS_CP=`cat $CP_FILE` # For Hudson server if [ "$HUDSON" = "true" ]; then - $JAVA_HOME/bin/java -server -cp "$CLJS_CP:src/main/clojure:src/main/cljs" clojure.main script/aot.clj + $JAVA_HOME/bin/java -server -cp "$CLJS_CP""$PATH_SEP""src/main/clojure""$PATH_SEP""src/main/cljs" clojure.main script/aot.clj else - java -server -cp "$CLJS_CP:src/main/clojure:src/main/cljs" clojure.main script/aot.clj + java -server -cp "$CLJS_CP""$PATH_SEP""src/main/clojure""$PATH_SEP""src/main/cljs" clojure.main script/aot.clj fi - - - - From 44fff8a4c6d075450e736c35d72dee5b1516d555 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Jul 2017 11:51:21 -0400 Subject: [PATCH 2505/4033] revert unnecessary unsage-get from cljs/source-map --- src/main/cljs/cljs/source_map.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/source_map.cljs b/src/main/cljs/cljs/source_map.cljs index 001b848c0..7009bfd75 100644 --- a/src/main/cljs/cljs/source_map.cljs +++ b/src/main/cljs/cljs/source_map.cljs @@ -56,11 +56,11 @@ [seg source-map] (let [[gcol source line col name] seg] {:gcol gcol - :source (unsafe-get (gobj/get source-map "sources") source) + :source (aget (gobj/get source-map "sources") source) :line line :col col :name (when-let [name (-> seg meta :name)] - (unsafe-get (gobj/get source-map "names") name))})) + (aget (gobj/get source-map "names") name))})) (defn seg-combine "Combine a source map segment vector and a relative From 07ee2250af02b25f232111890c0f40f23150768d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Jul 2017 15:01:26 -0400 Subject: [PATCH 2506/4033] CLJS-1800: Defer to tools.reader for cljs.reader functionality --- src/main/cljs/cljs/reader.cljs | 596 ++++------------------------ src/test/cljs/cljs/reader_test.cljs | 87 ++-- 2 files changed, 129 insertions(+), 554 deletions(-) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index e87e16a72..47f73afb3 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -9,485 +9,19 @@ (ns cljs.reader (:require-macros [cljs.reader :refer [add-data-readers]]) (:require [goog.object :as gobject] - [goog.string :as gstring]) - (:import goog.string.StringBuffer)) - -(defprotocol PushbackReader - (read-char [reader] "Returns the next char from the Reader, -nil if the end of stream has been reached") - (unread [reader ch] "Push back a single character on to the stream")) - -(deftype StringPushbackReader [s buffer ^:mutable idx] - PushbackReader - (read-char [reader] - (if (zero? (alength buffer)) - (do - (set! idx (inc idx)) - (aget s idx)) - (.pop buffer))) - (unread [reader ch] - (.push buffer ch))) - -(defn push-back-reader [s] - "Creates a StringPushbackReader from a given string" - (StringPushbackReader. s (array) -1)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; predicates -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn- ^boolean whitespace? - "Checks whether a given character is whitespace" - [ch] - (or (gstring/isBreakingWhitespace ch) (identical? \, ch))) - -(defn- ^boolean numeric? - "Checks whether a given character is numeric" - [ch] - (gstring/isNumeric ch)) - -(defn- ^boolean comment-prefix? - "Checks whether the character begins a comment." - [ch] - (identical? \; ch)) - -(defn- ^boolean number-literal? - "Checks whether the reader is at the start of a number literal" - [reader initch] - (or (numeric? initch) - (and (or (identical? \+ initch) (identical? \- initch)) - (numeric? (let [next-ch (read-char reader)] - (unread reader next-ch) - next-ch))))) - -(declare read macros dispatch-macros) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; read helpers -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -; later will do e.g. line numbers... -(defn reader-error - [rdr & msg] - (throw (js/Error. (apply str msg)))) - -(defn ^boolean macro-terminating? [ch] - (and (not (identical? ch "#")) - (not (identical? ch \')) - (not (identical? ch ":")) - (macros ch))) - -(defn read-token - [rdr initch] - (loop [sb (StringBuffer. initch) - ch (read-char rdr)] - (if (or (nil? ch) - (whitespace? ch) - (macro-terminating? ch)) - (do (unread rdr ch) (.toString sb)) - (recur (do (.append sb ch) sb) (read-char rdr))))) - -(defn skip-line - "Advances the reader to the end of a line. Returns the reader" - [reader _] - (loop [] - (let [ch (read-char reader)] - (if (or (identical? ch \newline) (identical? ch \return) (nil? ch)) - reader - (recur))))) - -(def int-pattern (re-pattern "^([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+))(N)?$")) -(def ratio-pattern (re-pattern "^([-+]?[0-9]+)/([0-9]+)$")) -(def float-pattern (re-pattern "^([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?$")) -(def symbol-pattern (re-pattern "^[:]?([^0-9/].*/)?([^0-9/][^/]*)$")) - -(defn- re-matches* - [re s] - (let [matches (.exec re s)] - (when (and (not (nil? matches)) - (identical? (aget matches 0) s)) - (if (== (alength matches) 1) - (aget matches 0) - matches)))) - -(defn- match-int - [s] - (let [groups (re-matches* int-pattern s) - ie8-fix (aget groups 2) - zero (if (= ie8-fix "") nil ie8-fix)] - (if-not (nil? zero) - 0 - (let [a (cond - (aget groups 3) (array (aget groups 3) 10) - (aget groups 4) (array (aget groups 4) 16) - (aget groups 5) (array (aget groups 5) 8) - (aget groups 6) (array (aget groups 7) - (js/parseInt (aget groups 6) 10)) - :else (array nil nil)) - n (aget a 0) - radix (aget a 1)] - (when-not (nil? n) - (let [parsed (js/parseInt n radix)] - (if (identical? "-" (aget groups 1)) - (- parsed) - parsed))))))) - -(defn- match-ratio - [s] - (let [groups (re-matches* ratio-pattern s) - numinator (aget groups 1) - denominator (aget groups 2)] - (/ (js/parseInt numinator 10) (js/parseInt denominator 10)))) - -(defn- match-float - [s] - (js/parseFloat s)) - -(defn- match-number - [s] - (cond - (re-matches* int-pattern s) (match-int s) - (re-matches* ratio-pattern s) (match-ratio s) - (re-matches* float-pattern s) (match-float s))) - -(defn escape-char-map [c] - (cond - (identical? c \t) "\t" - (identical? c \r) "\r" - (identical? c \n) "\n" - (identical? c \\) \\ - (identical? c \") \" - (identical? c \b) "\b" - (identical? c \f) "\f" - :else nil)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; unicode -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn read-2-chars [reader] - (.toString - (StringBuffer. - (read-char reader) - (read-char reader)))) - -(defn read-4-chars [reader] - (.toString - (StringBuffer. - (read-char reader) - (read-char reader) - (read-char reader) - (read-char reader)))) - -(def unicode-2-pattern (re-pattern "^[0-9A-Fa-f]{2}$")) -(def unicode-4-pattern (re-pattern "^[0-9A-Fa-f]{4}$")) - -(defn validate-unicode-escape [unicode-pattern reader escape-char unicode-str] - (if (re-matches unicode-pattern unicode-str) - unicode-str - (reader-error reader "Unexpected unicode escape \\" escape-char unicode-str))) - -(defn make-unicode-char [code-str] - (let [code (js/parseInt code-str 16)] - (.fromCharCode js/String code))) - -(defn escape-char - [buffer reader] - (let [ch (read-char reader) - mapresult (escape-char-map ch)] - (if mapresult - mapresult - (cond - (identical? ch \x) - (->> (read-2-chars reader) - (validate-unicode-escape unicode-2-pattern reader ch) - (make-unicode-char)) - - (identical? ch \u) - (->> (read-4-chars reader) - (validate-unicode-escape unicode-4-pattern reader ch) - (make-unicode-char)) - - (numeric? ch) - (.fromCharCode js/String ch) - - :else - (reader-error reader "Unexpected unicode escape \\" ch ))))) - -(defn read-past - "Read until first character that doesn't match pred, returning - char." - [pred rdr] - (loop [ch (read-char rdr)] - (if (pred ch) - (recur (read-char rdr)) - ch))) - -(defn read-delimited-list - [delim rdr recursive?] - (loop [a (array)] - (let [ch (read-past whitespace? rdr)] - (when-not ch (reader-error rdr "EOF while reading")) - (if (identical? delim ch) - a - (if-let [macrofn (macros ch)] - (let [mret (macrofn rdr ch)] - (recur (if (identical? mret rdr) a (do - (.push a mret) - a)))) - (do - (unread rdr ch) - (let [o (read rdr true nil recursive?)] - (recur (if (identical? o rdr) a (do - (.push a o) - a)))))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; data structure readers -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn not-implemented - [rdr ch] - (reader-error rdr "Reader for " ch " not implemented yet")) - -(declare maybe-read-tagged-type) - -(defn read-dispatch - [rdr _] - (let [ch (read-char rdr) - dm (dispatch-macros ch)] - (if dm - (dm rdr _) - (if-let [obj (maybe-read-tagged-type rdr ch)] - obj - (reader-error rdr "No dispatch macro for " ch))))) - -(defn read-unmatched-delimiter - [rdr ch] - (reader-error rdr "Unmatched delimiter " ch)) - -(defn read-list - [rdr _] - (let [arr (read-delimited-list ")" rdr true)] - (loop [i (alength arr) ^not-native r ()] - (if (> i 0) - (recur (dec i) (-conj r (aget arr (dec i)))) - r)))) - -(def read-comment skip-line) - -(defn read-vector - [rdr _] - (vec (read-delimited-list "]" rdr true))) - -(defn read-map - [rdr _] - (let [l (read-delimited-list "}" rdr true) - c (alength l)] - (when (odd? c) - (reader-error rdr "Map literal must contain an even number of forms")) - (if (<= c (* 2 (.-HASHMAP-THRESHOLD PersistentArrayMap))) - (.createWithCheck PersistentArrayMap l) - (.createWithCheck PersistentHashMap l)))) - -(defn read-number - [reader initch] - (loop [buffer (gstring/StringBuffer. initch) - ch (read-char reader)] - (if (or (nil? ch) (whitespace? ch) (macros ch)) - (do - (unread reader ch) - (let [s (.toString buffer)] - (or (match-number s) - (reader-error reader "Invalid number format [" s "]")))) - (recur (do (.append buffer ch) buffer) (read-char reader))))) - -(defn read-string* - [reader _] - (loop [buffer (gstring/StringBuffer.) - ch (read-char reader)] - (cond - (nil? ch) (reader-error reader "EOF while reading") - (identical? "\\" ch) (recur (do (.append buffer (escape-char buffer reader)) buffer) - (read-char reader)) - (identical? \" ch) (. buffer (toString)) - :default (recur (do (.append buffer ch) buffer) (read-char reader))))) - -(defn read-raw-string* - [reader _] - (loop [buffer (gstring/StringBuffer.) - ch (read-char reader)] - (cond - (nil? ch) (reader-error reader "EOF while reading") - (identical? "\\" ch) (do (.append buffer ch) - (let [nch (read-char reader)] - (if (nil? nch) - (reader-error reader "EOF while reading") - (recur (doto buffer (.append nch)) - (read-char reader))))) - (identical? "\"" ch) (.toString buffer) - :else (recur (doto buffer (.append ch)) (read-char reader))))) - -(defn special-symbols [t not-found] - (cond - (identical? t "nil") nil - (identical? t "true") true - (identical? t "false") false - (identical? t "/") '/ - :else not-found)) - -(defn read-symbol - [reader initch] - (let [token (read-token reader initch)] - (if (and (gstring/contains token "/") - (not (== (.-length token) 1))) - (symbol (subs token 0 (.indexOf token "/")) - (subs token (inc (.indexOf token "/")) - (.-length token))) - (special-symbols token (symbol token))))) - -(defn read-literal - [rdr ch] - (let [token (read-token rdr ch) - chars (subs token 1)] - (cond (identical? (.-length chars) 1) chars - (identical? chars "tab") "\t" - (identical? chars "return") "\r" - (identical? chars "newline") "\n" - (identical? chars "space") " " - (identical? chars "backspace") "\b" - (identical? chars "formfeed") "\f" - (identical? (.charAt chars 0) "u") (make-unicode-char (subs chars 1)) - (identical? (.charAt chars 0) "o") (not-implemented rdr token) - :else (reader-error rdr "Unknown character literal: " token)))) - -(defn read-keyword - [reader initch] - (let [token (read-token reader (read-char reader)) - a (re-matches* symbol-pattern token) - _ (when (nil? a) (reader-error reader "Invalid keyword " (str ":" token))) - token (aget a 0) - ns (aget a 1) - name (aget a 2)] - (if (or (and (not (undefined? ns)) - (identical? (. ns (substring (- (.-length ns) 2) (.-length ns))) ":/")) - (identical? (aget name (dec (.-length name))) ":") - (not (== (.indexOf token "::" 1) -1))) - (reader-error reader "Invalid token: " token) - (if (and (not (nil? ns)) (> (.-length ns) 0)) - (keyword (.substring ns 0 (.indexOf ns "/")) name) - (keyword token))))) - -(defn desugar-meta - [f] - (cond - (symbol? f) {:tag f} - (string? f) {:tag f} - (keyword? f) {f true} - :else f)) - -(defn wrapping-reader - [sym] - (fn [rdr _] - (list sym (read rdr true nil true)))) - -(defn throwing-reader - [msg] - (fn [rdr _] - (reader-error rdr msg))) - -(defn read-meta - [rdr _] - (let [m (desugar-meta (read rdr true nil true))] - (when-not (map? m) - (reader-error rdr "Metadata must be Symbol,Keyword,String or Map")) - (let [o (read rdr true nil true)] - (if (satisfies? IWithMeta o) - (with-meta o (merge (meta o) m)) - (reader-error rdr "Metadata can only be applied to IWithMetas"))))) - -(defn read-set - [rdr _] - (.createWithCheck PersistentHashSet (read-delimited-list "}" rdr true))) - -(defn read-regex - [rdr ch] - (-> (read-raw-string* rdr ch) re-pattern)) - -(defn read-discard - [rdr _] - (read rdr true nil true) - rdr) - -(defn macros [c] - (cond - (identical? c \") read-string* - (identical? c \:) read-keyword - (identical? c \;) read-comment - (identical? c \') (wrapping-reader 'quote) - (identical? c \@) (wrapping-reader 'deref) - (identical? c \^) read-meta - (identical? c \`) not-implemented - (identical? c \~) not-implemented - (identical? c \() read-list - (identical? c \)) read-unmatched-delimiter - (identical? c \[) read-vector - (identical? c \]) read-unmatched-delimiter - (identical? c \{) read-map - (identical? c \}) read-unmatched-delimiter - (identical? c \\) read-literal - (identical? c \#) read-dispatch - :else nil)) - -;; omitted by design: var reader, eval reader -(defn dispatch-macros [s] - (cond - (identical? s "{") read-set - (identical? s "<") (throwing-reader "Unreadable form") - (identical? s "\"") read-regex - (identical? s"!") read-comment - (identical? s "_") read-discard - :else nil)) - -(defn read - "Reads the first object from a PushbackReader. Returns the object read. - If EOF, throws if eof-is-error is true. Otherwise returns sentinel. - - Only supports edn (similar to clojure.edn/read)" - [reader eof-is-error sentinel is-recursive] - (let [ch (read-char reader)] - (cond - (nil? ch) (if eof-is-error (reader-error reader "EOF while reading") sentinel) - (whitespace? ch) (recur reader eof-is-error sentinel is-recursive) - (comment-prefix? ch) (recur (read-comment reader ch) eof-is-error sentinel is-recursive) - :else (let [f (macros ch) - res - (cond - f (f reader ch) - (number-literal? reader ch) (read-number reader ch) - :else (read-symbol reader ch))] - (if (identical? res reader) - (recur reader eof-is-error sentinel is-recursive) - res))))) - -(defn read-string - "Reads one object from the string s" - [s] - (when-not (string? s) - (throw (js/Error. "Cannot read from non-string object."))) - (let [r (push-back-reader s)] - (read r false nil false))) - -;; read instances + [cljs.tools.reader :as treader] + [cljs.tools.reader.edn :as edn]) + (:import [goog.string StringBuffer])) (defn ^:private zero-fill-right-and-truncate [s width] - (cond (= width (count s)) s - (< width (count s)) (subs s 0 width) - :else (loop [b (StringBuffer. s)] - (if (< (.getLength b) width) - (recur (.append b "0")) - (.toString b))))) + (cond + (= width (count s)) s + (< width (count s)) (subs s 0 width) + :else + (loop [b (StringBuffer. s)] + (if (< (.getLength b) width) + (recur (.append b "0")) + (.toString b))))) (defn ^:private divisible? [num div] @@ -495,7 +29,7 @@ nil if the end of stream has been reached") (defn ^:private indivisible? [num div] - (not (divisible? num div))) + (not (divisible? num div))) (defn ^:private leap-year? [year] @@ -518,14 +52,14 @@ nil if the end of stream has been reached") (defn ^:private check [low n high msg] (when-not (<= low n high) - (reader-error nil (str msg " Failed: " low "<=" n "<=" high))) + (throw (js/Error. (str msg " Failed: " low "<=" n "<=" high)))) n) (defn parse-and-validate-timestamp [s] (let [[_ years months days hours minutes seconds fraction offset-sign offset-hours offset-minutes :as v] (re-matches timestamp-regex s)] (if-not v - (reader-error nil (str "Unrecognized date/time syntax: " s)) + (throw (js/Error. (str "Unrecognized date/time syntax: " s))) (let [years (parse-int years) months (or (parse-int months) 1) days (or (parse-int days) 1) @@ -551,23 +85,21 @@ nil if the end of stream has been reached") (if-let [[years months days hours minutes seconds ms offset] (parse-and-validate-timestamp ts)] (js/Date. - (- (.UTC js/Date years (dec months) days hours minutes seconds ms) + (- (.UTC js/Date years (dec months) days hours minutes seconds ms) (* offset 60 1000))) - (reader-error nil (str "Unrecognized date/time syntax: " ts)))) + (throw (js/Error. (str "Unrecognized date/time syntax: " ts))))) (defn ^:private read-date [s] (if (string? s) (parse-timestamp s) - (reader-error nil "Instance literal expects a string for its timestamp."))) - + (throw (js/Error. "Instance literal expects a string for its timestamp.")))) (defn ^:private read-queue [elems] (if (vector? elems) - (into cljs.core.PersistentQueue.EMPTY elems) - (reader-error nil "Queue literal expects a vector for its elements."))) - + (into cljs.core/PersistentQueue.EMPTY elems) + (throw (js/Error. "Queue literal expects a vector for its elements.")))) (defn ^:private read-js [form] @@ -585,50 +117,86 @@ nil if the end of stream has been reached") obj) :else - (reader-error nil - (str "JS literal expects a vector or map containing " - "only string or unqualified keyword keys")))) - + (throw + (js/Error. + (str "JS literal expects a vector or map containing " + "only string or unqualified keyword keys"))))) (defn ^:private read-uuid [uuid] (if (string? uuid) (cljs.core/uuid uuid) - (reader-error nil "UUID literal expects a string as its representation."))) - -(def ^:dynamic *tag-table* - (atom (add-data-readers - {"inst" read-date - "uuid" read-uuid - "queue" read-queue - "js" read-js}))) + (throw (js/Error. "UUID literal expects a string as its representation.")))) (def ^:dynamic *default-data-reader-fn* (atom nil)) -(defn maybe-read-tagged-type - [rdr initch] - (let [tag (read-symbol rdr initch) - pfn (get @*tag-table* (str tag)) - dfn @*default-data-reader-fn*] - (cond - pfn (pfn (read rdr true nil false)) - dfn (dfn tag (read rdr true nil false)) - :else (reader-error rdr - "Could not find tag parser for " (str tag) - " in " (pr-str (keys @*tag-table*)))))) +(def ^:dynamic *tag-table* + (atom + {'inst read-date + 'uuid read-uuid + 'queue read-queue + 'js read-js})) + +(defn read + "Reads the first object from an cljs.tools.reader.reader-types/IPushbackReader. + Returns the object read. If EOF, throws if eof-error? is true otherwise returns eof. + If no reader is provided, *in* will be used. + + Reads data in the edn format (subset of Clojure data): + http://edn-format.org + + clojure.tools.reader.edn/read doesn't depend on dynamic Vars, all configuration + is done by passing an opt map. + + opts is a map that can include the following keys: + :eof - value to return on end-of-file. When not supplied, eof throws an exception. + :readers - a map of tag symbols to data-reader functions to be considered before default-data-readers. + When not supplied, only the default-data-readers will be used. + :default - A function of two args, that will, if present and no reader is found for a tag, + be called with the tag and the value." + ([reader] + (edn/read + {:readers @*tag-table* + :default @*default-data-reader-fn* + :eof nil} + reader)) + ([{:keys [eof] :as opts} reader] + (edn/read + (update (merge opts {:default @*default-data-reader-fn*}) + :readers (fn [m] (merge @*tag-table* m))) reader)) + ([reader eof-error? eof opts] + (edn/read reader eof-error? eof + (update (merge opts {:default @*default-data-reader-fn*}) + :readers (fn [m] (merge @*tag-table* m)))))) + +(defn read-string + "Reads one object from the string s. + Returns nil when s is nil or empty. + + Reads data in the edn format (subset of Clojure data): + http://edn-format.org + + opts is a map as per clojure.tools.reader.edn/read" + ([s] + (edn/read-string + {:readers @*tag-table* + :default @*default-data-reader-fn* + :eof nil} s)) + ([opts s] + (edn/read-string + (update (merge {:default @*default-data-reader-fn*} opts) + :readers (fn [m] (merge @*tag-table* m))) s))) (defn register-tag-parser! [tag f] - (let [tag (str tag) - old-parser (get @*tag-table* tag)] + (let [old-parser (get @*tag-table* tag)] (swap! *tag-table* assoc tag f) old-parser)) (defn deregister-tag-parser! [tag] - (let [tag (str tag) - old-parser (get @*tag-table* tag)] + (let [old-parser (get @*tag-table* tag)] (swap! *tag-table* dissoc tag) old-parser)) diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 120716bf9..b08a6b9c6 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -7,7 +7,7 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.reader-test - (:require [cljs.test :refer-macros [deftest testing is]] + (:require [cljs.test :refer-macros [deftest testing is] :as test] [cljs.reader :as reader] [goog.object :as o])) @@ -27,8 +27,10 @@ (is (= '% (reader/read-string "%"))) (is (= #{1 2 3} (reader/read-string "#{1 2 3}"))) (is (= '(7 8 9) (reader/read-string "(7 8 9)"))) - (is (= '(deref foo) (reader/read-string "@foo"))) - (is (= '(quote bar) (reader/read-string "'bar"))) + ;; Another bad test found by switching to tools.reader - David + ;(is (= '(deref foo) (reader/read-string "@foo"))) + ;; Another bad test found by switching to tools.reader - David + ;;(is (= 'bar (reader/read-string "'bar"))) (is (= 'foo/bar (reader/read-string "foo/bar"))) (is (= \a (reader/read-string "\\a"))) (is (= {:tag 'String} (meta (reader/read-string "^String {:a 1}")))) @@ -42,7 +44,7 @@ (is (= "escape chars \t \r \n \\ \" \b \f" (reader/read-string "\"escape chars \\t \\r \\n \\\\ \\\" \\b \\f\"")))) (testing "Test reading number literals" - (is (apply = 0 (map reader/read-string "0" "+0" "-0" " 0 "))) + (is (apply = 0 (map reader/read-string ["0" "+0" "-0" " 0 "]))) (is (apply = 42 (map reader/read-string ["052" "0x2a" "2r101010" "8R52" "16r2a" "36r16"]))) (is (apply = 42 (map reader/read-string ["+052" "+0x2a" "+2r101010" "+8r52" "+16R2a" "+36r16"]))) (is (apply = -42 (map reader/read-string ["-052" "-0X2a" "-2r101010" "-8r52" "-16r2a" "-36R16"])))) @@ -56,7 +58,8 @@ (reader/read-string "#queue [1 2]")))) (testing "Test reading comments" - (is (nil? (reader/read-string ";foo"))) + ;; Another bad test found by switching to tools.reader - David + ;;(is (nil? (reader/read-string ";foo"))) (is (= 3 (try (reader/read-string ";foo\n3") (catch js/Error e :threw)))) @@ -176,17 +179,19 @@ (testing "Testing reading, CLS-787" (is (nil? (reader/read-string ""))))) -(deftest test-819 - (let [re (reader/read-string "#\"\\s\\u00a1\"") - m (re-find re " \u00a1 ")] - (testing "Testing reading, CLJS-819" - (is (= m " \u00a1"))))) +;; Test doesn't seem relevant now that we rely on tools.reader - David +;(deftest test-819 +; (let [re (reader/read-string "#\"\s\u00a1\"") +; m (re-find re " \u00a1 ")] +; (testing "Testing reading, CLJS-819" +; (is (= m " \u00a1"))))) (deftest testing-map-type (let [a (reader/read-string "{:a 1 :b 2 :c 3}") b (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9}")] (is (= a {:a 1 :b 2 :c 3})) - (is (instance? PersistentArrayMap a)) + ;; Needs fix to cljs.tools.reader.edn - David + ;;(is (instance? PersistentArrayMap a)) (is (= b {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9})) (is (instance? PersistentHashMap b)))) @@ -198,32 +203,34 @@ (is (= x (reader/read-string (pr-str x)))) (is (= (reader/read-string (pr-str x)) x))))) -(deftest testing-cljs-1823 - (let [;; PersistentArrayMap - a (try - (reader/read-string "{:a 1 :b 2 :c 3 :a 1}") - :failed-to-throw - (catch js/Error e (ex-message e))) - ;; PersistentHashMap - b (try - (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :h 7 :i 8 :a 1}") - :failed-to-throw - (catch js/Error e (ex-message e))) - ;; PersistentArrayMap backed PHS - c (try - (reader/read-string "#{:a :b :c :d :a}") - :failed-to-throw - (catch js/Error e (ex-message e))) - ;; PersistentHashMap backed PHS - d (try - (reader/read-string "#{:a :b :c :d :e :f :g :h :i :a}") - :failed-to-throw - (catch js/Error e (ex-message e)))] - (is (= "Duplicate key: :a" a)) - (is (= "Duplicate key: :a" b)) - (is (= "Duplicate key: :a" c)) - (is (= "Duplicate key: :a" d)))) - -(deftest test-error-messages - (testing "Leading numbers in keywords" - (is (thrown-with-msg? js/Error #"Invalid keyword :0s" (reader/read-string ":0s"))))) +;; This need to be enforced elsewhere not during reading - David +;(deftest testing-cljs-1823 +; (let [;; PersistentArrayMap +; a (try +; (reader/read-string "{:a 1 :b 2 :c 3 :a 1}") +; :failed-to-throw +; (catch js/Error e (ex-message e))) +; ;; PersistentHashMap +; b (try +; (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :h 7 :i 8 :a 1}") +; :failed-to-throw +; (catch js/Error e (ex-message e))) +; ;; PersistentArrayMap backed PHS +; c (try +; (reader/read-string "#{:a :b :c :d :a}") +; :failed-to-throw +; (catch js/Error e (ex-message e))) +; ;; PersistentHashMap backed PHS +; d (try +; (reader/read-string "#{:a :b :c :d :e :f :g :h :i :a}") +; :failed-to-throw +; (catch js/Error e (ex-message e)))] +; (is (= "Duplicate key: :a" a)) +; (is (= "Duplicate key: :a" b)) +; (is (= "Duplicate key: :a" c)) +; (is (= "Duplicate key: :a" d)))) + +;; Not relevant now that we rely on tools.reader and it duplicates Clojure's behavior - David +;(deftest test-error-messages +; (testing "Leading numbers in keywords" +; (is (thrown-with-msg? js/Error #"Invalid keyword :0s" (reader/read-string ":0s"))))) From bd6ff4ff7c772e629c6cb66bf81e7a96577a3099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Jul 2017 10:54:31 -0700 Subject: [PATCH 2507/4033] CLJS-2193: :npm-deps dependencies are implicit --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 510546ac9..ad1ff9ae9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2119,7 +2119,7 @@ (when (or ana/*verbose* verbose) (util/debug-prn "Installing Node.js dependencies")) (let [proc (-> (ProcessBuilder. - (into (cond->> ["npm" "install" "module-deps"] + (into (cond->> ["npm" "install" "module-deps" "resolve" "browser-resolve"] util/windows? (into ["cmd" "/c"])) (map (fn [[dep version]] (str (name dep) "@" version))) npm-deps)) From 67fc20f4d1f401eb45960f570169763e0c4889e6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Jul 2017 17:20:18 -0400 Subject: [PATCH 2508/4033] need call add-data-readers when computing cljs.reader/*tag-table* remove bad test from pprint --- src/main/cljs/cljs/reader.clj | 2 +- src/main/cljs/cljs/reader.cljs | 9 +++--- src/test/cljs/cljs/pprint_test.cljs | 43 +++++++++++++++-------------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/main/cljs/cljs/reader.clj b/src/main/cljs/cljs/reader.clj index 16531bcb7..5aa4b86a3 100644 --- a/src/main/cljs/cljs/reader.clj +++ b/src/main/cljs/cljs/reader.clj @@ -13,6 +13,6 @@ (let [data-readers (->> (get @env/*compiler* :cljs.analyzer/data-readers) (map (fn [[k v]] - [(str k) `(fn [x#] (~(vary-meta v assoc :cljs.analyzer/no-resolve true) x#))])) + `['~k (fn [x#] (~(vary-meta v assoc :cljs.analyzer/no-resolve true) x#))])) (into {}))] `(do (merge ~default-readers ~data-readers)))) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index 47f73afb3..17a40669e 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -133,10 +133,11 @@ (def ^:dynamic *tag-table* (atom - {'inst read-date - 'uuid read-uuid - 'queue read-queue - 'js read-js})) + (add-data-readers + {'inst read-date + 'uuid read-uuid + 'queue read-queue + 'js read-js}))) (defn read "Reads the first object from an cljs.tools.reader.reader-types/IPushbackReader. diff --git a/src/test/cljs/cljs/pprint_test.cljs b/src/test/cljs/cljs/pprint_test.cljs index 0bfb50339..2846d796d 100644 --- a/src/test/cljs/cljs/pprint_test.cljs +++ b/src/test/cljs/cljs/pprint_test.cljs @@ -102,27 +102,28 @@ Usage: *hello* #_"(add-to-buffer\n this\n (make-buffer-blob (str (char c)) nil))" ) -(simple-tests pprint-reader-macro-test - ;;I'm not sure this will work without significant work on cljs. Short story, cljs - ;;reader only takes valid EDN, so #(* % %) won't work. - ;;see http://stackoverflow.com/a/25712675/546321 for more details - #_(with-pprint-dispatch code-dispatch - (write (reader/read-string "(map #(first %) [[1 2 3] [4 5 6] [7]])") - :stream nil)) - #_"(map #(first %) [[1 2 3] [4 5 6] [7]])" - - ;;TODO Not sure what to do about this test due to the reader handling of `@` - (with-pprint-dispatch code-dispatch - (write (reader/read-string "@@(ref (ref 1))") - :stream nil)) - "(deref (deref (ref (ref 1))))" - #_"@@(ref (ref 1))" - - (with-pprint-dispatch code-dispatch - (write (reader/read-string "'foo") - :stream nil)) - "'foo" - ) +;; Not a valid test now that we use tools.reader - David +;(simple-tests pprint-reader-macro-test +; ;;I'm not sure this will work without significant work on cljs. Short story, cljs +; ;;reader only takes valid EDN, so #(* % %) won't work. +; ;;see http://stackoverflow.com/a/25712675/546321 for more details +; #_(with-pprint-dispatch code-dispatch +; (write (reader/read-string "(map #(first %) [[1 2 3] [4 5 6] [7]])") +; :stream nil)) +; #_"(map #(first %) [[1 2 3] [4 5 6] [7]])" +; +; ;;TODO Not sure what to do about this test due to the reader handling of `@` +; (with-pprint-dispatch code-dispatch +; (write (reader/read-string "@@(ref (ref 1))") +; :stream nil)) +; "(deref (deref (ref (ref 1))))" +; #_"@@(ref (ref 1))" +; +; (with-pprint-dispatch code-dispatch +; (write (reader/read-string "'foo") +; :stream nil)) +; "'foo" +; ) (simple-tests xp-miser-test (binding [*print-pprint-dispatch* simple-dispatch From a9cb508c8b3163a8f5106a7d9d350dcfaad034df Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Jul 2017 17:38:26 -0400 Subject: [PATCH 2509/4033] bring back reader duplicate key map literal tests --- src/test/cljs/cljs/reader_test.cljs | 54 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index b08a6b9c6..8017a16e0 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -204,31 +204,35 @@ (is (= (reader/read-string (pr-str x)) x))))) ;; This need to be enforced elsewhere not during reading - David -;(deftest testing-cljs-1823 -; (let [;; PersistentArrayMap -; a (try -; (reader/read-string "{:a 1 :b 2 :c 3 :a 1}") -; :failed-to-throw -; (catch js/Error e (ex-message e))) -; ;; PersistentHashMap -; b (try -; (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :h 7 :i 8 :a 1}") -; :failed-to-throw -; (catch js/Error e (ex-message e))) -; ;; PersistentArrayMap backed PHS -; c (try -; (reader/read-string "#{:a :b :c :d :a}") -; :failed-to-throw -; (catch js/Error e (ex-message e))) -; ;; PersistentHashMap backed PHS -; d (try -; (reader/read-string "#{:a :b :c :d :e :f :g :h :i :a}") -; :failed-to-throw -; (catch js/Error e (ex-message e)))] -; (is (= "Duplicate key: :a" a)) -; (is (= "Duplicate key: :a" b)) -; (is (= "Duplicate key: :a" c)) -; (is (= "Duplicate key: :a" d)))) +(deftest testing-cljs-1823 + (let [;; PersistentArrayMap + a (try + (reader/read-string "{:a 1 :b 2 :c 3 :a 1}") + :failed-to-throw + (catch js/Error e (ex-message e))) + ;; PersistentHashMap + b (try + (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :h 7 :i 8 :a 1}") + :failed-to-throw + (catch js/Error e (ex-message e))) + ;; Waiting on tools.reader fixes + ;;; PersistentArrayMap backed PHS + ;c (try + ; (reader/read-string "#{:a :b :c :d :a}") + ; :failed-to-throw + ; (catch js/Error e (ex-message e))) + ;;; PersistentHashMap backed PHS + ;d (try + ; (reader/read-string "#{:a :b :c :d :e :f :g :h :i :a}") + ; :failed-to-throw + ; (catch js/Error e (ex-message e))) + ] + (is (= "Map literal contains duplicate key: :a" a)) + (is (= "Map literal contains duplicate key: :a" b)) + ;; Waiting on tools.reader fixes - David + ;(is (= "Duplicate key: :a" c)) + ;(is (= "Duplicate key: :a" d)) + )) ;; Not relevant now that we rely on tools.reader and it duplicates Clojure's behavior - David ;(deftest test-error-messages From df1351362e8456d5242793d20c56321392f07917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Jul 2017 12:13:21 -0700 Subject: [PATCH 2510/4033] CLJS-2152: "is not a relative path" exception thrown when `:libs` directory is provided. --- src/main/clojure/cljs/closure.clj | 2 +- src/test/cljs/js_libs/tabby.js | 5 +++++ src/test/cljs_build/libs_test/core.cljs | 6 ++++++ src/test/clojure/cljs/build_api_tests.clj | 17 +++++++++++++++++ src/test/clojure/cljs/closure_tests.clj | 10 ++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs/js_libs/tabby.js create mode 100644 src/test/cljs_build/libs_test/core.cljs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ad1ff9ae9..9db1d4298 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1571,7 +1571,7 @@ (if (.endsWith lib-path ".js") (util/get-name url) (let [path (util/path url)] - (subs path (+ (.lastIndexOf path lib-path) (.length lib-path))))))) + (subs path (+ (inc (.lastIndexOf path lib-path)) (.length lib-path))))))) (defn ^String rel-output-path "Given a IJavaScript which points to a .js file either in memory, in a jar file, diff --git a/src/test/cljs/js_libs/tabby.js b/src/test/cljs/js_libs/tabby.js new file mode 100644 index 000000000..b43ff9b57 --- /dev/null +++ b/src/test/cljs/js_libs/tabby.js @@ -0,0 +1,5 @@ +goog.provide("tabby"); + +tabby.hello = function() { + return "hello there from tabby"; +}; diff --git a/src/test/cljs_build/libs_test/core.cljs b/src/test/cljs_build/libs_test/core.cljs new file mode 100644 index 000000000..8c7a426e7 --- /dev/null +++ b/src/test/cljs_build/libs_test/core.cljs @@ -0,0 +1,6 @@ +(ns libs-test.core + (:require [tabby])) + +(enable-console-print!) + +(println (tabby/hello)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index a33c15be8..b77c5bb4b 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -253,3 +253,20 @@ opts cenv) (is (.exists (io/file out "preloads_test/preload.cljs"))) (is (contains? (get-in @cenv [::ana/namespaces 'preloads-test.preload :defs]) 'preload-var)))) + +(deftest test-libs-cljs-2152 + (let [out (.getPath (io/file (test/tmp-dir) "libs-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'libs-test.core + :output-dir out + :libs ["src/test/cljs/js_libs"] + :optimizations :none + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (build/build (build/inputs + (io/file "src/test/cljs_build/libs_test/core.cljs") (io/file "src/test/cljs/js_libs") + (io/file inputs "libs_test/core.cljs") + (io/file "src/test/cljs/js_libs")) + opts cenv) + (is (.exists (io/file out "tabby.js"))))) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 0f66fb36e..10f0bfa28 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -73,3 +73,13 @@ (deftest test-string-provides (is (= ["CB0BFFB"] (deps/-provides "var x = 42;")))) + +(deftest test-lib-rel-path-cljs-2152 + (let [ijs {:provides ["tabby"] + :url (io/as-url (io/file "src/test/cljs/js_libs/tabby.js")) + :lib-path "src/test/cljs/js_libs"}] + (is (= (closure/lib-rel-path ijs) "tabby.js"))) + (let [ijs {:provides ["tabby"] + :url (io/as-url (io/file "src/test/cljs/js_libs/tabby.js")) + :lib-path (.getAbsolutePath (io/file "src/test/cljs/js_libs/tabby.js"))}] + (is (= (closure/lib-rel-path ijs) "tabby.js")))) From 7caa255ae955dfb7e3b3075d0b353cdc9a5adfed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Jul 2017 12:20:39 -0700 Subject: [PATCH 2511/4033] CLJS-2179: Add test for preprocess JS module as symbol --- .../clojure/cljs/module_processing_tests.clj | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 79c1f017b..28e9ae176 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -9,7 +9,7 @@ [cljs.test-util :as test])) ;; Hard coded JSX transform for the test case -(defmethod closure/js-transforms :jsx [ijs _] +(defn preprocess-jsx [ijs _] (assoc ijs :source (clojure.string/replace (:source ijs) (re-pattern (str "\\(\n" @@ -22,6 +22,9 @@ "React.createElement(\"circle\", {cx:\"100px\", cy:\"100px\", r:\"100px\", fill:this.props.color})" ")")))) +(defmethod closure/js-transforms :jsx [ijs opts] + (preprocess-jsx ijs opts)) + (deftest commonjs-module-processing (test/delete-out-files) (let [cenv (env/default-compiler-env)] @@ -125,3 +128,30 @@ "Circle" "module$src$test$cljs$Circle-min"} (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) + +(deftest commonjs-module-processing-preprocess-symbol + (test/delete-out-files) + (let [cenv (env/default-compiler-env)] + ;; Reset load-library cache so that changes to processed files are noticed + (with-redefs [cljs.js-deps/load-library (memoize cljs.js-deps/load-library*)] + (is (= {:foreign-libs [] + :ups-foreign-libs [] + :libs ["out/src/test/cljs/reactJS.js" + "out/src/test/cljs/Circle.js"] + :closure-warnings {:non-standard-jsdoc :off}} + (env/with-compiler-env cenv + (closure/process-js-modules + {:foreign-libs [{:file "src/test/cljs/reactJS.js" + :provides ["React"] + :module-type :commonjs} + {:file "src/test/cljs/Circle.js" + :provides ["Circle"] + :module-type :commonjs + :preprocess 'cljs.module-processing-tests/preprocess-jsx}] + :closure-warnings {:non-standard-jsdoc :off}}))) + "processed modules are added to :libs")) + + (is (= {"React" "module$src$test$cljs$reactJS" + "Circle" "module$src$test$cljs$Circle"} + (:js-module-index @cenv)) + "Processed modules are added to :js-module-index"))) From 341b00419cab487234909fd15476264353ba6bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Jul 2017 15:41:53 -0700 Subject: [PATCH 2512/4033] CLJS-2195: npm-deps tests are not idempotent --- src/test/clojure/cljs/build_api_tests.clj | 25 +++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index b77c5bb4b..4b40b53cb 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -158,13 +158,15 @@ (str foreign-lib-file)))))) (deftest cljs-1537-circular-deps - (let [out-file (io/file "out/main.js")] + (let [out-file (io/file "out/main.js") + root "src/test/cljs_build"] (.delete out-file) (try - (build/build (build/inputs "src/test/cljs_build") + (build/build (build/inputs + (io/file (str root "a.cljs")) + (io/file (str root "b.cljs"))) {:main 'circular-deps.a :optimizations :none - :verbose true :output-to "out"}) (is false) (catch Throwable e @@ -186,9 +188,9 @@ (deftest cljs-2077-test-loader (test/delete-out-files) - (let [project (merge-with merge (loader-test-project "out")) + (let [{:keys [inputs opts]} (merge-with merge (loader-test-project "out")) loader (io/file "out" "cljs" "loader.js")] - (build/build (build/inputs (:inputs project)) (:opts project)) + (build/build (build/inputs (io/file inputs "bar.cljs") (io/file inputs "foo.cljs")) opts) (is (.exists loader)) (is (not (nil? (re-find #"/loader_test/foo\.js" (slurp loader)))))) (test/delete-out-files) @@ -202,7 +204,12 @@ {:opts {:optimizations :whitespace}})] (build/build (build/inputs (:inputs project)) (:opts project))))) +(defn delete-node-modules [] + (doseq [f (file-seq (io/file "node_modules"))] + (.delete f))) + (deftest test-npm-deps + (delete-node-modules) (spit (io/file "package.json") "{}") (testing "simplest case, require" (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) @@ -211,8 +218,7 @@ :output-dir out :optimizations :none :npm-deps {:left-pad "1.1.3"} - :closure-warnings {:check-types :off - :non-standard-jsdoc :off}}} + :closure-warnings {:check-types :off}}} cenv (env/default-compiler-env)] (test/delete-out-files out) (build/build (build/inputs (io/file inputs "npm_deps_test/core.cljs")) opts cenv) @@ -230,12 +236,13 @@ :non-standard-jsdoc :off}}} cenv (env/default-compiler-env)] (test/delete-out-files out) - (test/delete-out-files "node_modules") + (delete-node-modules) (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) (is (.exists (io/file out "node_modules/react/react.js"))) (is (contains? (:js-module-index @cenv) "react")) (is (contains? (:js-module-index @cenv) "react-dom/server")))) - (.delete (io/file "package.json"))) + (.delete (io/file "package.json")) + (delete-node-modules)) (deftest test-preloads (let [out (.getPath (io/file (test/tmp-dir) "preloads-test-out")) From b88d290f95f6ac9bc7040ceaf8cfb3a57e6b9935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Jul 2017 11:49:48 -0700 Subject: [PATCH 2513/4033] CLJS-2194: cljs.util/relative-name bug --- src/main/clojure/cljs/util.cljc | 7 +++++-- src/test/clojure/cljs/util_tests.clj | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index c5ded0b43..7e2234503 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -148,8 +148,11 @@ [x] {:pre [(or (file? x) (url? x))]} (letfn [(strip-user-dir [s] - (let [user-dir (str (System/getProperty "user.dir") File/separator) - user-path (.getFile (.toURL (io/file user-dir)))] + (let [user-dir (System/getProperty "user.dir") + user-path (.getFile (.toURL (io/file user-dir))) + user-path (cond-> user-path + (not (.endsWith user-path File/separator)) + (str File/separator))] (string/replace s user-path "")))] (if (file? x) (strip-user-dir (.getAbsolutePath x)) diff --git a/src/test/clojure/cljs/util_tests.clj b/src/test/clojure/cljs/util_tests.clj index 23abd06ef..24502f84d 100644 --- a/src/test/clojure/cljs/util_tests.clj +++ b/src/test/clojure/cljs/util_tests.clj @@ -7,7 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.util-tests - (:require [cljs.util :as util]) + (:require [cljs.util :as util] + [clojure.java.io :as io]) (:use clojure.test)) (deftest test-levenshtein-distance @@ -26,3 +27,10 @@ (is (= [[:bogus nil] [:optimisations :optimizations]] (sort (util/unknown-opts #{:optimisations :bogus} #{:optimizations :static-fns})))))) + +(deftest test-relative-name + (let [initial (System/getProperty "user.dir")] + (System/setProperty "user.dir" "/Users/user/clojurescript") + (is (= (util/relative-name (io/file "/Users/user/clojurescript/out/index.js")) "out/index.js")) + (is (= (util/relative-name (io/as-url (io/file "/Users/user/clojurescript/out/index.js"))) "out/index.js")) + (System/setProperty "user.dir" initial))) From e1123eb4bedfae21fbf732dfdbcb0c9ed6465883 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Jul 2017 09:11:22 -0400 Subject: [PATCH 2514/4033] clojure -> cljs in cljs.reader docstrings --- src/main/cljs/cljs/reader.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/reader.cljs b/src/main/cljs/cljs/reader.cljs index 17a40669e..964f6be31 100644 --- a/src/main/cljs/cljs/reader.cljs +++ b/src/main/cljs/cljs/reader.cljs @@ -147,7 +147,7 @@ Reads data in the edn format (subset of Clojure data): http://edn-format.org - clojure.tools.reader.edn/read doesn't depend on dynamic Vars, all configuration + cljs.tools.reader.edn/read doesn't depend on dynamic Vars, all configuration is done by passing an opt map. opts is a map that can include the following keys: @@ -178,7 +178,7 @@ Reads data in the edn format (subset of Clojure data): http://edn-format.org - opts is a map as per clojure.tools.reader.edn/read" + opts is a map as per cljs.tools.reader.edn/read" ([s] (edn/read-string {:readers @*tag-table* From 0f2a03faeaf5ccdbddb3ca3f40532fb69eef942c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 8 Jul 2017 07:58:44 -0400 Subject: [PATCH 2515/4033] CLJS-2191: Clean up doc references to clojure.spec.* in favor of cljs.spec.* --- src/main/cljs/cljs/spec/alpha.cljc | 4 ++-- src/main/cljs/cljs/spec/alpha.cljs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index ee4d16a4a..ed92fb4ef 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -421,7 +421,7 @@ by calling get-spec with the var or full-qualified symbol. Once registered, function specs are included in doc, checked by - instrument, tested by the runner clojure.spec.test.alpha/run-tests, and (if + instrument, tested by the runner cljs.spec.test.alpha/run-tests, and (if a macro) used to explain errors during macroexpansion. Note that :fn specs require the presence of :args and :ret specs to @@ -432,7 +432,7 @@ For example, to register function specs for the symbol function: - (s/fdef clojure.core/symbol + (s/fdef cljs.core/symbol :args (s/alt :separate (s/cat :ns string? :n string?) :str string? :sym symbol?) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index cc19ab662..f784ba040 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -78,7 +78,7 @@ x)) (defn regex? - "returns x if x is a (clojure.spec) regex op, else logical false" + "returns x if x is a (cljs.spec.alpha) regex op, else logical false" [x] (c/and (::op x) x)) @@ -147,8 +147,8 @@ (keyword-identical? ::invalid ret)) (defn conform - "Given a spec and a value, returns :clojure.spec/invalid if value does not match spec, - else the (possibly destructured) value." + "Given a spec and a value, returns :cljs.spec.alpha/invalid if value does + not match spec, else the (possibly destructured) value." [spec x] (conform* (specize spec) x)) From 0c0b3a5f32581b1084b19231844486631fd0c4c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Jul 2017 14:03:22 -0700 Subject: [PATCH 2516/4033] CLJS-1966: cljs.test assumes the output directory is '/out/' when determining the filename for a failed or errored test result. --- src/main/cljs/cljs/test.cljc | 4 ++++ src/main/cljs/cljs/test.cljs | 9 ++++++++- src/test/cljs/cljs/test_test.cljs | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/test.cljc b/src/main/cljs/cljs/test.cljc index 725c7570c..d648326ed 100644 --- a/src/main/cljs/cljs/test.cljc +++ b/src/main/cljs/cljs/test.cljc @@ -56,6 +56,10 @@ :expected '~form, :actual value#})) value#)) +(defmacro ^:private cljs-output-dir [] + (let [{:keys [output-dir]} (ana-api/get-options)] + (or output-dir "out"))) + ;; ============================================================================= ;; Assertion Methods diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index 088df3f5d..50f6fbe13 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -376,7 +376,14 @@ [NaN NaN]))) (defn js-filename [stack-element] - (first (.split (last (.split stack-element "/out/")) ":"))) + (let [output-dir (cljs.test/cljs-output-dir) + output-dir (cond-> output-dir + (not (.endsWith output-dir "/")) + (str "/"))] + (-> (.split stack-element output-dir) + last + (.split ":") + first))) (defn mapped-line-and-column [filename line column] (let [default [filename line column]] diff --git a/src/test/cljs/cljs/test_test.cljs b/src/test/cljs/cljs/test_test.cljs index 4885ca2d4..96b637c68 100644 --- a/src/test/cljs/cljs/test_test.cljs +++ b/src/test/cljs/cljs/test_test.cljs @@ -27,3 +27,6 @@ (let [[line column] (ct/js-line-and-column "foo")] (is (nan? line)) (is (nan? column)))) + +(deftest test-js-filename + (is (= "core-advanced-test.js" (ct/js-filename "nW@builds/out-adv/core-advanced-test.js:1191:77")))) From fc0989f1b44b97547410a2d2c807f16430b47486 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Jul 2017 09:51:48 -0400 Subject: [PATCH 2517/4033] CLJS-1959: under :nodejs target we should provide __dirname and __filename constants Instead of trying to address this directly, simply revert problematic changes made to Node.js bootstrap script & REPL in commmit 59a7f265fac02c931cc3d5615727da861db62e3b. The idea of the bad commit was to simplify testing of CommonJS/AMD/ES6 modules via the Node.js REPL. However this can be accomplished just as easily via browser REPLs. --- src/main/cljs/cljs/bootstrap_node.js | 46 +++++++++++++--------------- src/main/clojure/cljs/repl/node.clj | 2 +- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_node.js index e955486d4..974f6aa40 100644 --- a/src/main/cljs/cljs/bootstrap_node.js +++ b/src/main/cljs/cljs/bootstrap_node.js @@ -49,7 +49,7 @@ var CLJS_ROOT = "."; * The goog namespace in the global scope. */ global.goog = {}; -global.require = require; + /** * Imports a script using Node's require() API. @@ -58,37 +58,33 @@ global.require = require; * @return {boolean} True if the script was imported, false otherwise. */ global.CLOSURE_IMPORT_SCRIPT = function(src) { - // if CLJS_ROOT has been rewritten (by REPLs) need to compute require path - // so we can delete the old entry from the Node.js require cache - if(CLJS_ROOT !== ".") { - var cached = null; - if(src.substring(0, 2) == "..") { - cached = path.join(CLJS_ROOT, src.substring(3)); - } else { - cached = path.join(CLJS_ROOT, "goog", src); + // if CLJS_ROOT has been rewritten (by REPLs) need to compute require path + // so we can delete the old entry from the Node.js require cache + if(CLJS_ROOT !== ".") { + var cached = null; + if(src.substring(0, 2) == "..") { + cached = path.join(CLJS_ROOT, src.substring(3)); + } else { + cached = path.join(CLJS_ROOT, "goog", src); + } + if(require.cache[cached]) delete require.cache[cached]; } - if(require.cache[cached]) delete require.cache[cached]; - } - // Sources are always expressed relative to closure's base.js, but - // require() is always relative to the current source. - nodeGlobalRequire(path.resolve(__dirname, '..', src)); - return true; + // Sources are always expressed relative to closure's base.js, but + // require() is always relative to the current source. + require(path.join(".", "..", src)); + return true; }; // Declared here so it can be used to require base.js function nodeGlobalRequire(file) { - var _module = global.module, _exports = global.exports; - global.module = undefined; - global.exports = undefined; - global.__dirname = file.substring(0, file.lastIndexOf('/')); - global.__filename = file; - vm.runInThisContext(fs.readFileSync(file), file); - global.__dirname = undefined; - global.__filename = undefined; - global.exports = _exports; - global.module = _module; + var _module = global.module, _exports = global.exports; + global.module = undefined; + global.exports = undefined; + vm.runInThisContext(fs.readFileSync(file), file); + global.exports = _exports; + global.module = _module; } diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index 81c3ff7ce..ea5092c23 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -161,7 +161,7 @@ (string/replace "path.resolve(__dirname, '..', 'base.js')" (platform-path (conj rewrite-path "bootstrap" ".." "base.js"))) (string/replace - "path.resolve(__dirname, '..', src)" + "path.join(\".\", \"..\", src)" (str "path.join(" (platform-path rewrite-path) ", src)")) (string/replace "var CLJS_ROOT = \".\";" From 0aed47861b025eb1b3757a77c7ec425682d01354 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 9 Jul 2017 10:07:42 -0400 Subject: [PATCH 2518/4033] CLJS-2172: memfn docstring refers to Java and reflection --- src/main/clojure/cljs/core.cljc | 22 ++++++++++------------ src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 7aa8ceaed..afe99cf8d 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -377,18 +377,16 @@ (map #(cons `fn %) fnspecs))) ~@body))) -#?(:cljs - (core/defmacro memfn - "Expands into code that creates a fn that expects to be passed an - object and any args and calls the named instance method on the - object passing the args. Use when you want to treat a Java method as - a first-class fn. name may be type-hinted with the method receiver's - type in order to avoid reflective calls." - [name & args] - (core/let [t (with-meta (gensym "target") - (meta name))] - `(fn [~t ~@args] - (. ~t (~name ~@args)))))) +(core/defmacro memfn + "Expands into code that creates a fn that expects to be passed an + object and any args and calls the named instance method on the + object passing the args. Use when you want to treat a JavaScript + method as a first-class fn." + [name & args] + (core/let [t (with-meta (gensym "target") + (meta name))] + `(fn [~t ~@args] + (. ~t (~name ~@args))))) #?(:cljs (core/defmacro when diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index f410d02f0..5120f26a9 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -574,6 +574,12 @@ (let [o (doto (js-obj) (gobject/set "my sum" (fn [a b] (+ a b))))] (is (= 5 (js-invoke o "my sum" 2 3))))) +(deftest memfn-test + (let [substr (memfn substr start length)] + (is (= "cde" (substr "abcdefg" 2 3)))) + (let [trim (memfn trim)] + (is (= ["abc" "def"] (map trim [" abc " " def "]))))) + ;; ============================================================================= ;; Tickets From 6583de880ee6bf92e0baa50dee8da621e45494e1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 8 Jul 2017 15:56:10 -0400 Subject: [PATCH 2519/4033] CLJS-2192: Add ChakraCore testing facilities --- .travis.yml | 4 ++++ script/benchmark | 7 +++++++ script/test | 10 +++++++++- script/test-simple | 10 +++++++++- script/test.ps1 | 3 ++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 63f88dd19..d448fdfc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ before_install: - wget https://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/jsshell-linux-x86_64.zip - unzip jsshell-linux-x86_64.zip -d spidermoney - sudo apt-get install -y libjavascriptcoregtk-3.0-bin + - wget https://aka.ms/chakracore/cc_linux_x64_1_5_2 -O chakra-core.tar.gz + - tar xvzf chakra-core.tar.gz before_script: - script/bootstrap @@ -23,6 +25,8 @@ script: - grep '0 failures, 0 errors.' test-out.txt - jjs builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt + - ./ChakraCoreFiles/bin/ch builds/out-adv/core-advanced-test.js | tee test-out.txt + - grep '0 failures, 0 errors.' test-out.txt - script/test-self-host | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - script/test-self-parity | tee test-out.txt diff --git a/script/benchmark b/script/benchmark index 8f68e7511..189c79d92 100755 --- a/script/benchmark +++ b/script/benchmark @@ -32,3 +32,10 @@ else echo "Benchmarking with Nashorn" "${NASHORN_HOME}/jjs" builds/out-adv-bench/core-advanced-benchmark.js fi + +if [ "$CHAKRACORE_HOME" = "" ]; then + echo "CHAKRACORE_HOME not set, skipping ChakraCore benchmarks" +else + echo "Benchmarking with ChakraCore" + "${CHAKRACORE_HOME}/ch" builds/out-adv-bench/core-advanced-benchmark.js +fi diff --git a/script/test b/script/test index 9b04fd9f7..7727dabda 100755 --- a/script/test +++ b/script/test @@ -5,7 +5,7 @@ rm -rf out rm -rf target mkdir -p builds/out-adv -possible=4 +possible=5 ran=0 if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js; then @@ -45,4 +45,12 @@ else ran=$((ran+1)) fi +if [ "$CHAKRACORE_HOME" = "" ]; then + echo "CHAKRACORE_HOME not set, skipping ChakraCore tests" +else + echo "Testing with ChakraCore" + "${CHAKRACORE_HOME}/ch" builds/out-adv/core-advanced-test.js + ran=$((ran+1)) +fi + echo "Tested with $ran out of $possible possible js targets" diff --git a/script/test-simple b/script/test-simple index 832372eb5..64e79fa4b 100755 --- a/script/test-simple +++ b/script/test-simple @@ -4,7 +4,7 @@ rm -rf builds/out-simp mkdir -p builds/out-simp -possible=4 +possible=5 ran=0 #bin/cljsc test >out/core-test.js @@ -46,4 +46,12 @@ else ran=$[ran+1] fi +if [ "$CHAKRACORE_HOME" = "" ]; then + echo "CHAKRACORE_HOME not set, skipping ChakraCore tests" +else + echo "Testing with ChakraCore" + "${CHAKRACORE_HOME}/ch" builds/out-simp/core-simple-test.js + ran=$[ran+1] +fi + echo "Tested with $ran out of $possible possible js targets" diff --git a/script/test.ps1 b/script/test.ps1 index d11b5e897..c9efde243 100644 --- a/script/test.ps1 +++ b/script/test.ps1 @@ -7,7 +7,8 @@ $targets = @{ env="V8_HOME"; name="V8"; cmd={ & "$env:V8_HOME\d8" $testjs } }, @{ env="SPIDERMONKEY_HOME"; name="SpiderMonkey"; cmd={ & "$env:SPIDERMONKEY_HOME\js" -f $testjs } }, @{ env="JSC_HOME"; name="JavaScriptCore"; cmd={ & "$env:JSC_HOME\jsc" -f $testjs } }, - @{ env="NASHORN_HOME"; name="Nashorn"; cmd={ & "$env:NASHORN_HOME\jjs" $testjs } } + @{ env="NASHORN_HOME"; name="Nashorn"; cmd={ & "$env:NASHORN_HOME\jjs" $testjs } }, + @{ env="CHAKRACORE_HOME"; name="ChakraCore"; cmd={ & "$env:CHAKRACORE_HOME\ch" $testjs } } $ran = 0 $opts = '{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :output-dir \"builds\\out-adv\"}' From 2ade039f9b0af8ae18d46b57c75669114076d4ac Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Jul 2017 13:21:40 -0400 Subject: [PATCH 2520/4033] CLJS-2199: String requires broken after recompile We need to dissoc :js-module-index when computing missing-js-modules in cljs.closure/build. Add test to verify that builds with string requires are idempotent with respect cljs_dep.js content. --- src/main/clojure/cljs/closure.clj | 9 +++----- src/test/clojure/cljs/build_api_tests.clj | 28 +++++++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9db1d4298..df886795a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2352,12 +2352,9 @@ (not (false? (:static-fns opts)))) (:static-fns opts) ana/*cljs-static-fns*) - sources (-find-sources source opts) - missing-js-modules (into #{} - (comp - (map :missing-js-modules) - cat) - sources) + sources (env/with-compiler-env (dissoc @compiler-env :js-module-index) + (-find-sources source opts)) + missing-js-modules (into #{} (comp (map :missing-js-modules) cat) sources) all-opts (-> (assoc opts :missing-js-modules missing-js-modules) add-implicit-options process-js-modules)] diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 4b40b53cb..aa68f5ac7 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -224,23 +224,27 @@ (build/build (build/inputs (io/file inputs "npm_deps_test/core.cljs")) opts cenv) (is (.exists (io/file out "node_modules/left-pad/index.js"))) (is (contains? (:js-module-index @cenv) "left-pad")))) - (testing "mix of symbol & string-based requires" - (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) - {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) - :opts {:main 'npm-deps-test.string-requires - :output-dir out - :optimizations :none - :npm-deps {:react "15.6.1" - :react-dom "15.6.1"} - :closure-warnings {:check-types :off - :non-standard-jsdoc :off}}} - cenv (env/default-compiler-env)] + (let [cenv (env/default-compiler-env) + out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'npm-deps-test.string-requires + :output-dir out + :optimizations :none + :npm-deps {:react "15.6.1" + :react-dom "15.6.1"} + :closure-warnings {:check-types :off + :non-standard-jsdoc :off}}}] + (testing "mix of symbol & string-based requires" (test/delete-out-files out) (delete-node-modules) (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) (is (.exists (io/file out "node_modules/react/react.js"))) (is (contains? (:js-module-index @cenv) "react")) - (is (contains? (:js-module-index @cenv) "react-dom/server")))) + (is (contains? (:js-module-index @cenv) "react-dom/server"))) + (testing "builds with string requires are idempotent" + (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) + (is (not (nil? (re-find #"\.\./node_modules/react-dom/server\.js" (slurp (io/file out "cljs_deps.js")))))) + (test/delete-out-files out))) (.delete (io/file "package.json")) (delete-node-modules)) From b7a1b1a75de24734026a34dd0e93ba02e2b076c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Jul 2017 13:21:53 -0700 Subject: [PATCH 2521/4033] CLJS-1428: Add a cljs.core/*command-line-args* var --- src/main/cljs/cljs/core.cljs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a81e018e4..f3b2b9af1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -270,6 +270,11 @@ argv as arguments"} *main-cli-fn* nil) +(def + ^{:doc "A sequence of the supplied command line arguments, or nil if + none were supplied"} + *command-line-args* nil) + (defn type "Return x's constructor." [x] From a8c15f70fcdc6454f774fb83ec2a725de10ad0aa Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 28 Jun 2017 09:17:30 -0400 Subject: [PATCH 2522/4033] CLJS-2135: require macro prints last result of loaded-libs --- src/main/clojure/cljs/compiler.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 86422031a..a0a59913a 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1105,7 +1105,9 @@ (defmethod emit* :ns* [{:keys [name requires uses require-macros reloads env deps]}] (load-libs requires nil (:require reloads) deps) - (load-libs uses requires (:use reloads) deps)) + (load-libs uses requires (:use reloads) deps) + (when (:repl-env env) + (emitln "null;"))) (defmethod emit* :ns [{:keys [name requires uses require-macros reloads env deps]}] From 2fb7ba9a8d39442ca361b8d2722f3f40e188cf4b Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Sun, 9 Jul 2017 22:05:35 +0300 Subject: [PATCH 2523/4033] CLJS-2202: String requires should work from Cljs files in classpath Missing JS files need to be selected after `add-dependency-sources` has been ran so that all the Cljs files are considered. Existing tests where broken after addin the namespace which is loaded from classpath. To ensure that classpath file tries to use JS module that is not already loaded by local files, Lodash was added as example JS module. --- src/main/clojure/cljs/closure.clj | 49 +++++++++++++------ .../string_requires_in_classpath.cljs | 6 +++ .../npm_deps_test/string_requires.cljs | 3 +- src/test/clojure/cljs/build_api_tests.clj | 3 +- 4 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 src/test/cljs/npm_deps_test/string_requires_in_classpath.cljs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index df886795a..5c6052047 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2033,15 +2033,11 @@ #(ensure-cljs-base-module % opts))) (defn add-implicit-options - [{:keys [optimizations output-dir npm-deps missing-js-modules] + [{:keys [optimizations output-dir] :or {optimizations :none output-dir "out"} :as opts}] - (let [opts (cond-> (update opts :foreign-libs - (fn [libs] - (into (index-node-modules - (into missing-js-modules (keys npm-deps)) opts) - (expand-libs libs)))) + (let [opts (cond-> opts (:closure-defines opts) (assoc :closure-defines (into {} @@ -2334,6 +2330,29 @@ (:infer-externs opts) (assoc :externs-sources (load-externs (dissoc opts :infer-externs))))) +(defn handle-js-modules + "Given all Cljs sources (build inputs and dependencies in classpath) + + - find the missing js modules defined using string require + - index all the node node modules + - process the JS modules (preprocess + convert to Closure JS)o + - save js-dependency-index for compilation" + [{:keys [npm-deps] :as opts} js-sources compiler-env] + (let [missing-js-modules (into #{} + (comp + (map :missing-js-modules) + cat) + js-sources) + opts (-> opts + (update :foreign-libs + (fn [libs] + (into (index-node-modules + (into missing-js-modules (keys npm-deps)) opts) + (expand-libs libs)))) + process-js-modules)] + (swap! compiler-env assoc :js-dependency-index (deps/js-dependency-index opts)) + opts)) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] @@ -2352,12 +2371,8 @@ (not (false? (:static-fns opts)))) (:static-fns opts) ana/*cljs-static-fns*) - sources (env/with-compiler-env (dissoc @compiler-env :js-module-index) - (-find-sources source opts)) - missing-js-modules (into #{} (comp (map :missing-js-modules) cat) sources) - all-opts (-> (assoc opts :missing-js-modules missing-js-modules) - add-implicit-options - process-js-modules)] + sources (-find-sources source opts) + all-opts (add-implicit-options opts)] (check-output-to opts) (check-output-dir opts) (check-source-map opts) @@ -2370,7 +2385,6 @@ #(-> % (update-in [:options] merge all-opts) (assoc :target (:target opts)) - (assoc :js-dependency-index (deps/js-dependency-index all-opts)) ;; Save list of sources for cljs.analyzer/locate-src - Juho Teperi (assoc :sources sources))) (binding [comp/*recompiled* (when-not (false? (:recompile-dependents opts)) @@ -2405,8 +2419,13 @@ (assoc all-opts :output-file (:output-to all-opts)) all-opts) _ (load-data-readers! compiler-env) - js-sources (-> (-find-sources source all-opts) - (add-dependency-sources compile-opts) + ;; reset :js-module-index so that ana/parse-ns called by -find-sources + ;; can find the missing JS modules + js-sources (env/with-compiler-env (dissoc @compiler-env :js-module-index) + (-> (-find-sources source all-opts) + (add-dependency-sources compile-opts))) + all-opts (handle-js-modules all-opts js-sources compiler-env) + js-sources (-> js-sources deps/dependency-order (compile-sources compiler-stats compile-opts) (#(map add-core-macros-if-cljs-js %)) diff --git a/src/test/cljs/npm_deps_test/string_requires_in_classpath.cljs b/src/test/cljs/npm_deps_test/string_requires_in_classpath.cljs new file mode 100644 index 000000000..322367d84 --- /dev/null +++ b/src/test/cljs/npm_deps_test/string_requires_in_classpath.cljs @@ -0,0 +1,6 @@ +(ns npm-deps-test.string-requires-in-classpath + "This tests string require of a lib that is not loaded + by project local files from a classpath file." + (:require ["lodash/array" :as array])) + +(println "lodash/array is loaded:" (array/nth #js [true] 1)) diff --git a/src/test/cljs_build/npm_deps_test/string_requires.cljs b/src/test/cljs_build/npm_deps_test/string_requires.cljs index 3aa1ece4c..322d416b5 100644 --- a/src/test/cljs_build/npm_deps_test/string_requires.cljs +++ b/src/test/cljs_build/npm_deps_test/string_requires.cljs @@ -1,6 +1,7 @@ (ns npm-deps-test.string-requires (:require [react :refer [createElement]] - ["react-dom/server" :as ReactDOMServer])) + ["react-dom/server" :as ReactDOMServer] + [npm-deps-test.string-requires-in-classpath])) (enable-console-print!) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index aa68f5ac7..62af98b44 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -231,7 +231,8 @@ :output-dir out :optimizations :none :npm-deps {:react "15.6.1" - :react-dom "15.6.1"} + :react-dom "15.6.1" + :lodash "4.17.4"} :closure-warnings {:check-types :off :non-standard-jsdoc :off}}}] (testing "mix of symbol & string-based requires" From ec670068e7e3f06040b614e0949ff74ed5c669b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 9 Jul 2017 11:26:11 -0700 Subject: [PATCH 2524/4033] CLJS-2201: Self-host: test-js-filename failing --- src/test/cljs/cljs/test_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/cljs/cljs/test_test.cljs b/src/test/cljs/cljs/test_test.cljs index 96b637c68..f4b072bec 100644 --- a/src/test/cljs/cljs/test_test.cljs +++ b/src/test/cljs/cljs/test_test.cljs @@ -29,4 +29,4 @@ (is (nan? column)))) (deftest test-js-filename - (is (= "core-advanced-test.js" (ct/js-filename "nW@builds/out-adv/core-advanced-test.js:1191:77")))) + (is (= "core-advanced-test.js" (ct/js-filename (str "nW@" (ct/cljs-output-dir) "/core-advanced-test.js:1191:77"))))) From 149724bcb28c44bf331ff96c813c0c3aba287b0f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 9 Jul 2017 17:45:02 -0400 Subject: [PATCH 2525/4033] CLJS-2203: REPL is turning on all warnings by default (including :invalid-array-access) --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 03d557e67..e846eb10d 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -793,7 +793,7 @@ ana/*cljs-ns* ana/*cljs-ns* *cljs-verbose* repl-verbose ana/*cljs-warnings* - (let [warnings (opts :warnings true)] + (let [warnings (opts :warnings)] (merge ana/*cljs-warnings* (if (or (true? warnings) From 05bab7a6a3531b0474e3f7eed0e9f9699345a760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 9 Jul 2017 16:34:47 -0700 Subject: [PATCH 2526/4033] CLJS-2205: NPM deps: Correctly compute `:provides` if file ends in `index.js` --- src/main/cljs/cljs/module_deps.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 5e239112f..14cf1536a 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -61,10 +61,14 @@ md.on('end', function() { var dep = deps_files[key]; if (dep.provides == null && !/node_modules[/\\][^/\\]*?[/\\]package.json$/.test(dep.file)) { - var match = dep.file.match(/node_modules[/\\](.*)\.js(on)*$/) + var match = dep.file.match(/node_modules[/\\](.*)\.js(on)?$/) if (match != null){ - dep.provides = [ match[1].replace('\\', '/') ]; + var providedModule = match[1].replace('\\', '/'); + + dep.provides = providedModule.endsWith('/index') ? + [ providedModule, providedModule.replace(/\/index$/,'')] : + [ providedModule ]; } } From 075136890abc7c143b26a19c35716cfb1d49666f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 9 Jul 2017 15:00:05 -0700 Subject: [PATCH 2527/4033] CLJS-2204: Tests failing with respect to lodash/array namespace --- project.clj | 2 +- .../npm_deps_test/string_requires_in_classpath.cljs | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/test/{cljs => cljs_cp}/npm_deps_test/string_requires_in_classpath.cljs (100%) diff --git a/project.clj b/project.clj index e5827ff9f..fca0648c0 100644 --- a/project.clj +++ b/project.clj @@ -7,7 +7,7 @@ :jvm-opts ^:replace ["-Dclojure.compiler.direct-linking=true" "-Xmx512m" "-server"] :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] - :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self"] + :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.0.0"] diff --git a/src/test/cljs/npm_deps_test/string_requires_in_classpath.cljs b/src/test/cljs_cp/npm_deps_test/string_requires_in_classpath.cljs similarity index 100% rename from src/test/cljs/npm_deps_test/string_requires_in_classpath.cljs rename to src/test/cljs_cp/npm_deps_test/string_requires_in_classpath.cljs From d2711a2f222e56bdfa698610a59167de4124789d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 9 Jul 2017 12:51:02 -0700 Subject: [PATCH 2528/4033] CLJS-1764: Double warning for undeclared Var (REPL only) --- src/main/clojure/cljs/repl.cljc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index e846eb10d..eeb53bdad 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -188,12 +188,13 @@ ns) ;; TODO: add pre-condition to source-on-disk, the ;; source must supply at least :url - David - sources (cljsc/add-dependencies - (merge (env->opts repl-env) opts) - {:requires [(name ns)] - :type :seed - :url (:uri (cljsc/source-for-namespace - ns env/*compiler*))}) + sources (binding [ana/*analyze-deps* false] + (cljsc/add-dependencies + (merge (env->opts repl-env) opts) + {:requires [(name ns)] + :type :seed + :url (:uri (cljsc/source-for-namespace + ns env/*compiler*))})) deps (->> sources (remove (comp #{["goog"]} :provides)) (remove (comp #{:seed} :type)) @@ -455,8 +456,9 @@ :source-form form} :repl-env repl-env}) def-emits-var (:def-emits-var opts) - ast (ana/analyze (assoc env :def-emits-var def-emits-var) - (wrap form) nil opts) + ast (binding [ana/*analyze-deps* false] + (ana/analyze (assoc env :def-emits-var def-emits-var) + (wrap form) nil opts)) wrap-js ;; TODO: check opts as well - David (if (:source-map repl-env) From 8e8349f19be68e979a9a39eb5152ebf9493b93d8 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 10 Jul 2017 09:01:10 -0400 Subject: [PATCH 2529/4033] CLJS-2207: cljs.test/js-filename is using non-portable .endsWith --- src/main/cljs/cljs/test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index 50f6fbe13..b51098a0f 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -378,7 +378,7 @@ (defn js-filename [stack-element] (let [output-dir (cljs.test/cljs-output-dir) output-dir (cond-> output-dir - (not (.endsWith output-dir "/")) + (not (string/ends-with? output-dir "/")) (str "/"))] (-> (.split stack-element output-dir) last From 00df4ae8a49005dbbb3ad87bf1a24e71897d74f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 10 Jul 2017 10:23:41 -0700 Subject: [PATCH 2530/4033] CLJS-2208: module_deps.js is not compatible with older JS implementations --- src/main/cljs/cljs/module_deps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 14cf1536a..5856410b4 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -66,7 +66,7 @@ md.on('end', function() { if (match != null){ var providedModule = match[1].replace('\\', '/'); - dep.provides = providedModule.endsWith('/index') ? + dep.provides = /\/index$/.test(providedModule) ? [ providedModule, providedModule.replace(/\/index$/,'')] : [ providedModule ]; } From 8a4f6d13371d7038e2f99227cfbe04158e4e59c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 11 Jul 2017 07:23:00 -0400 Subject: [PATCH 2531/4033] CLJS-2200: bump to tools.reader 1.0.2 Bring back tests now addressed by this release --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/test/cljs/cljs/reader_test.cljs | 32 ++++++++++++++--------------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index e73bb2c53..3d631d5cf 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.0.0 + 1.0.2 org.clojure diff --git a/project.clj b/project.clj index fca0648c0..0ae18c1f6 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.0.0"] + [org.clojure/tools.reader "1.0.2"] [org.clojure/test.check "0.9.0" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] diff --git a/script/bootstrap b/script/bootstrap index c31a7bd5e..912e8bc74 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.285" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.0.0" +TREADER_RELEASE="1.0.2" TEST_CHECK_RELEASE="0.9.0" # check dependencies diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 8017a16e0..9a8a9b43d 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -187,11 +187,11 @@ ; (is (= m " \u00a1"))))) (deftest testing-map-type - (let [a (reader/read-string "{:a 1 :b 2 :c 3}") - b (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9}")] + (let [a (reader/read-string "{:a 1 :b 2 :c 3}") + b (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9}")] (is (= a {:a 1 :b 2 :c 3})) ;; Needs fix to cljs.tools.reader.edn - David - ;;(is (instance? PersistentArrayMap a)) + (is (instance? PersistentArrayMap a)) (is (= b {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9})) (is (instance? PersistentHashMap b)))) @@ -203,7 +203,6 @@ (is (= x (reader/read-string (pr-str x)))) (is (= (reader/read-string (pr-str x)) x))))) -;; This need to be enforced elsewhere not during reading - David (deftest testing-cljs-1823 (let [;; PersistentArrayMap a (try @@ -215,23 +214,22 @@ (reader/read-string "{:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :h 7 :i 8 :a 1}") :failed-to-throw (catch js/Error e (ex-message e))) - ;; Waiting on tools.reader fixes - ;;; PersistentArrayMap backed PHS - ;c (try - ; (reader/read-string "#{:a :b :c :d :a}") - ; :failed-to-throw - ; (catch js/Error e (ex-message e))) - ;;; PersistentHashMap backed PHS - ;d (try - ; (reader/read-string "#{:a :b :c :d :e :f :g :h :i :a}") - ; :failed-to-throw - ; (catch js/Error e (ex-message e))) + ;; PersistentArrayMap backed PHS + c (try + (reader/read-string "#{:a :b :c :d :a}") + :failed-to-throw + (catch js/Error e (ex-message e))) + ;; PersistentHashMap backed PHS + d (try + (reader/read-string "#{:a :b :c :d :e :f :g :h :i :a}") + :failed-to-throw + (catch js/Error e (ex-message e))) ] (is (= "Map literal contains duplicate key: :a" a)) (is (= "Map literal contains duplicate key: :a" b)) ;; Waiting on tools.reader fixes - David - ;(is (= "Duplicate key: :a" c)) - ;(is (= "Duplicate key: :a" d)) + (is (= "Set literal contains duplicate key: :a" c)) + (is (= "Set literal contains duplicate key: :a" d)) )) ;; Not relevant now that we rely on tools.reader and it duplicates Clojure's behavior - David From 921b2630804b387f342e54a711e220a1cf8ff0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 11 Jul 2017 14:29:38 -0700 Subject: [PATCH 2532/4033] CLJS-2211: Add function to index a top-level node_modules installation --- src/main/cljs/cljs/module_deps.js | 2 +- src/main/clojure/cljs/closure.clj | 42 +++++++++++++++++++++++ src/main/clojure/cljs/util.cljc | 18 ++++++++++ src/test/clojure/cljs/build_api_tests.clj | 10 ++---- src/test/clojure/cljs/closure_tests.clj | 41 ++++++++++++++++++++++ src/test/clojure/cljs/test_util.clj | 4 +++ 6 files changed, 109 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 5856410b4..655aadc1b 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -60,7 +60,7 @@ md.on('end', function() { for (var key in deps_files) { var dep = deps_files[key]; - if (dep.provides == null && !/node_modules[/\\][^/\\]*?[/\\]package.json$/.test(dep.file)) { + if (dep.provides == null && !/node_modules[/\\][^/\\]+?[/\\]package.json$/.test(dep.file)) { var match = dep.file.match(/node_modules[/\\](.*)\.js(on)?$/) if (match != null){ diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5c6052047..90f2c5dbb 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2203,6 +2203,48 @@ (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)) [])))) +(defn index-node-modules-dir + ([] + (index-node-modules-dir + (when env/*compiler* + (:options @env/*compiler*)))) + ([{:keys [verbose target]}] + (letfn [(package-json? [path] + (boolean (re-find #"node_modules[/\\][^/\\]+?[/\\]package.json$" path)))] + (let [module-fseq (util/module-file-seq) + pkg-jsons (into {} + (comp + (map #(.getAbsolutePath %)) + (filter package-json?) + (map (fn [path] + [path (json/read-str (slurp path))]))) + module-fseq)] + (into [] + (comp + (map #(.getAbsolutePath %)) + (map (fn [path] + (merge + {:file path + :module-type :commonjs} + (when-not (package-json? path) + (let [pkg-json-main (some + (fn [[pkg-json-path {:strs [main name]}]] + (when-not (nil? main) + (let [main-path (str (string/replace pkg-json-path #"package\.json$" "") + main)] + (when (= main-path path) + name)))) + pkg-jsons)] + {:provides (if (some? pkg-json-main) + [pkg-json-main] + (let [module-rel-name (string/replace + (subs path (.lastIndexOf path "node_modules")) + #"node_modules[\\\/]" "")] + (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")] + (boolean (re-find #"[\\\/]index\.js(on)?$" module-rel-name)) + (conj (string/replace module-rel-name #"[\\\/]index\.js(on)?$" "")))))})))))) + module-fseq))))) + (defn preprocess-js "Given js-module map, apply preprocessing defined by :preprocess value in the map." [{:keys [preprocess] :as js-module} opts] diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 7e2234503..77f712961 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -318,3 +318,21 @@ (def windows? (.startsWith (.toLowerCase (System/getProperty "os.name")) "windows")) + +(defn module-file-seq + ([] (module-file-seq (io/file "node_modules"))) + ([dir] + (let [fseq (tree-seq + (fn [^File f] + (and (. f (isDirectory)) + (not (boolean + (re-find #"node_modules[\\\/].*[\\\/]node_modules" + (.getPath f)))))) + (fn [^File d] + (seq (. d (listFiles)))) + dir)] + (filter (fn [f] + (let [path (.getPath f)] + (or (.endsWith path ".json") + (.endsWith path ".js")))) + fseq)))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 62af98b44..dde406e3f 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -204,12 +204,8 @@ {:opts {:optimizations :whitespace}})] (build/build (build/inputs (:inputs project)) (:opts project))))) -(defn delete-node-modules [] - (doseq [f (file-seq (io/file "node_modules"))] - (.delete f))) - (deftest test-npm-deps - (delete-node-modules) + (test/delete-node-modules) (spit (io/file "package.json") "{}") (testing "simplest case, require" (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) @@ -237,7 +233,7 @@ :non-standard-jsdoc :off}}}] (testing "mix of symbol & string-based requires" (test/delete-out-files out) - (delete-node-modules) + (test/delete-node-modules) (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) (is (.exists (io/file out "node_modules/react/react.js"))) (is (contains? (:js-module-index @cenv) "react")) @@ -247,7 +243,7 @@ (is (not (nil? (re-find #"\.\./node_modules/react-dom/server\.js" (slurp (io/file out "cljs_deps.js")))))) (test/delete-out-files out))) (.delete (io/file "package.json")) - (delete-node-modules)) + (test/delete-node-modules)) (deftest test-preloads (let [out (.getPath (io/file (test/tmp-dir) "preloads-test-out")) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 10f0bfa28..7a4410880 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -83,3 +83,44 @@ :url (io/as-url (io/file "src/test/cljs/js_libs/tabby.js")) :lib-path (.getAbsolutePath (io/file "src/test/cljs/js_libs/tabby.js"))}] (is (= (closure/lib-rel-path ijs) "tabby.js")))) + +(deftest test-index-node-modules + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (closure/maybe-install-node-deps! {:npm-deps {:left-pad "1.1.3"}}) + (let [modules (closure/index-node-modules-dir)] + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/left-pad/index.js")) + :provides ["left-pad"]})) modules)))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (closure/maybe-install-node-deps! {:npm-deps {:react "15.6.1" + :react-dom "15.6.1"}}) + (let [modules (closure/index-node-modules-dir)] + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react/react.js")) + :provides ["react"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react/lib/React.js")) + :provides ["react/lib/React.js" "react/lib/React"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) + :provides ["react-dom/server.js" "react-dom/server"]})) + modules)))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (closure/maybe-install-node-deps! {:npm-deps {:node-fetch "1.7.1"}}) + (let [modules (closure/index-node-modules-dir)] + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/node-fetch/lib/index.js")) + :provides ["node-fetch/lib/index.js" "node-fetch/lib/index" "node-fetch/lib"]})) + modules)))) + (.delete (io/file "package.json")) + (test/delete-node-modules)) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index 905adde2a..ad013c791 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -19,6 +19,10 @@ :when (.isFile f)] (.delete f)))) +(defn delete-node-modules [] + (doseq [f (file-seq (io/file "node_modules"))] + (.delete f))) + (defn project-with-modules "Returns the build config for a project that uses Google Closure modules." [output-dir] From 1eabfe9aced4b48271be93d367104d407e8d6bf3 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 12 Jul 2017 01:19:42 +0300 Subject: [PATCH 2533/4033] CLJS-2212: Replace missing-js-modules with new index-node-modules-dir --- src/main/clojure/cljs/analyzer.cljc | 10 +--------- src/main/clojure/cljs/closure.clj | 24 ++++++++++++++---------- src/test/clojure/cljs/analyzer_tests.clj | 12 ------------ 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5d91a5813..3a7027d04 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3584,14 +3584,7 @@ (= "cljc" (util/ext src))) 'cljs.core$macros ns-name) - deps (merge (:uses ast) (:requires ast)) - missing-js-modules (into #{} - (comp - (filter (fn [[k v]] - (and (or (string? k) (string? v)) - (not (js-module-exists? k))))) - (map val)) - deps)] + deps (merge (:uses ast) (:requires ast))] (merge {:ns (or ns-name 'cljs.user) :provides [ns-name] @@ -3600,7 +3593,6 @@ (cond-> (conj (set (vals deps)) 'cljs.core) (get-in @env/*compiler* [:options :emit-constants]) (conj constants-ns-sym))) - :missing-js-modules missing-js-modules :file dest :source-file (when rdr src) :source-forms (when-not rdr src) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 90f2c5dbb..7ba324e67 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2056,8 +2056,7 @@ :optimizations optimizations :output-dir output-dir :ups-libs libs - :ups-foreign-libs (into (index-node-modules (keys (compute-upstream-npm-deps opts)) opts) - (expand-libs foreign-libs)) + :ups-foreign-libs (expand-libs foreign-libs) :ups-externs externs :emit-constants emit-constants :cache-analysis-format (:cache-analysis-format opts :transit)) @@ -2375,21 +2374,26 @@ (defn handle-js-modules "Given all Cljs sources (build inputs and dependencies in classpath) - - find the missing js modules defined using string require - index all the node node modules - process the JS modules (preprocess + convert to Closure JS)o - save js-dependency-index for compilation" [{:keys [npm-deps] :as opts} js-sources compiler-env] - (let [missing-js-modules (into #{} - (comp - (map :missing-js-modules) - cat) - js-sources) + (let [;; Find all the top-level Node packages and their files + top-level-modules (reduce (fn [acc m] + (reduce (fn [acc p] + (assoc acc p m)) + acc + (:provides m))) + {} + (index-node-modules-dir)) + requires (set (mapcat deps/-requires js-sources)) + ;; Select Node files that are required by Cljs code, + ;; and create list of all their dependencies + required-node-modules (set/intersection (set (keys top-level-modules)) requires) opts (-> opts (update :foreign-libs (fn [libs] - (into (index-node-modules - (into missing-js-modules (keys npm-deps)) opts) + (into (index-node-modules required-node-modules) (expand-libs libs)))) process-js-modules)] (swap! compiler-env assoc :js-dependency-index (deps/js-dependency-index opts)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 65c517087..fa2adc430 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -766,18 +766,6 @@ (catch Exception _)) (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))))) -(deftest cljs-2061 - (let [test-cenv (atom - (merge @(e/default-compiler-env) - {:js-module-index {"react" "module$src$test$cljs$react"}})) - ast (e/with-compiler-env test-cenv - (a/parse-ns - '[(ns test.cljs-2061 - (:require ["react" :as react] - ["react-dom/server" :refer [renderToString]]))]))] - (is (= (:missing-js-modules ast) #{"react-dom/server"})) - (is (= (:requires ast) '#{cljs.core module$src$test$cljs$react "react-dom/server"})))) - (deftest test-cljs-2037 (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] (binding [a/*cljs-ns* a/*cljs-ns* From fb3ca9eeb59bf9888065c0b541f7aa8d5a9905fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 11 Jul 2017 23:40:54 -0700 Subject: [PATCH 2534/4033] CLJS-2213: Node.js target should use node_modules index to emit platform specific require --- src/main/clojure/cljs/analyzer.cljc | 53 ++++++++++++++----- src/main/clojure/cljs/closure.clj | 29 ++++++---- src/main/clojure/cljs/compiler.cljc | 49 ++++++++++------- .../emit_node_requires_test/core.cljs | 10 ++++ src/test/clojure/cljs/build_api_tests.clj | 34 ++++++++++++ 5 files changed, 131 insertions(+), 44 deletions(-) create mode 100644 src/test/cljs_build/emit_node_requires_test/core.cljs diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3a7027d04..7bab5bc83 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -727,6 +727,12 @@ (into #{} (mapcat identity) (get-in @env/*compiler* [:js-module-index])) (str module))) +(defn node-module-dep? + [module] + (contains? + (get-in @env/*compiler* [:node-module-index]) + (str module))) + (defn confirm-var-exists ([env prefix suffix] (let [warn (confirm-var-exist-warning env prefix suffix)] @@ -886,6 +892,9 @@ :cljs (and ^boolean (goog.string/contains s ".") (not ^boolean (goog.string/contains s "..")))))) +(defn munge-node-lib [name] + (munge (string/replace (str name) #"[.\/]" "\\$"))) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -927,9 +936,13 @@ (when (not= (-> env :ns :name) full-ns) (confirm-ns env full-ns)) (confirm env full-ns (symbol (name sym)))) - (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym))) - {:name (symbol (str full-ns) (str (name sym))) - :ns full-ns})) + (if (node-module-dep? full-ns) + {:name (symbol (str (-> env :ns :name)) + (str (munge-node-lib full-ns) "." (name sym))) + :ns (-> env :ns :name)} + (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym))) + {:name (symbol (str full-ns) (str (name sym))) + :ns full-ns}))) (dotted-symbol? sym) (let [idx (.indexOf s ".") @@ -952,10 +965,13 @@ (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)) (let [full-ns (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)] - (merge - (gets @env/*compiler* ::namespaces full-ns :defs sym) - {:name (symbol (str full-ns) (str sym)) - :ns full-ns})) + (if (node-module-dep? full-ns) + {:name (symbol (str (-> env :ns :name)) (str (munge-node-lib full-ns) "." sym)) + :ns (-> env :ns :name)} + (merge + (gets @env/*compiler* ::namespaces full-ns :defs sym) + {:name (symbol (str full-ns) (str sym)) + :ns full-ns}))) (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym)) (let [qualified-symbol (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym) @@ -976,6 +992,12 @@ {:name (symbol module) :ns 'js}) + (or (node-module-dep? s) + (node-module-dep? (resolve-ns-alias env s))) + (let [module (resolve-ns-alias env s)] + {:name (symbol (str (-> env :ns :name)) (munge-node-lib module)) + :ns (-> env :ns :name)}) + :else (let [cur-ns (-> env :ns :name) full-ns (cond @@ -1988,6 +2010,7 @@ (doseq [dep deps] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) + (contains? (:node-module-index compiler) (name dep)) #?(:clj (deps/find-classpath-lib dep))) #?(:clj (if-some [src (locate-src dep)] (analyze-file src opts) @@ -2002,7 +2025,8 @@ (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 (get js-lib :closure-lib)) + (not (node-module-dep? lib))))) (defn missing-rename? [sym cenv] (let [lib (symbol (namespace sym)) @@ -2010,8 +2034,10 @@ (missing-use? lib sym cenv))) (defn missing-use-macro? [lib sym] - (let [the-ns #?(:clj (find-ns lib) :cljs (find-macros-ns lib))] - (or (nil? the-ns) (nil? (.findInternedVar ^clojure.lang.Namespace the-ns sym))))) + ;; guard against string requires + (when (symbol? lib) + (let [the-ns #?(:clj (find-ns lib) :cljs (find-macros-ns lib))] + (or (nil? the-ns) (nil? (.findInternedVar ^clojure.lang.Namespace the-ns sym)))))) (defn missing-rename-macro? [sym] (let [lib (symbol (namespace sym)) @@ -3105,11 +3131,12 @@ #?(:clj (find-ns nsym) :cljs (find-macros-ns nsym)) sym)) :else - (if-some [nsym (gets env :ns :use-macros sym)] + (let [nsym (gets env :ns :use-macros sym)] + (if (and (some? nsym) (symbol? nsym)) (.findInternedVar ^clojure.lang.Namespace - #?(:clj (find-ns nsym) :cljs (find-macros-ns nsym)) sym) + #?(:clj (find-ns nsym) :cljs (find-macros-ns nsym)) sym) (.findInternedVar ^clojure.lang.Namespace - #?(:clj (find-ns 'cljs.core) :cljs (find-macros-ns CLJS_CORE_MACROS_SYM)) sym)))))) + #?(:clj (find-ns 'cljs.core) :cljs (find-macros-ns CLJS_CORE_MACROS_SYM)) sym))))))) (defn get-expander "Given a sym, a symbol identifying a macro, and env, an analysis environment diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 7ba324e67..05161869b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1436,7 +1436,10 @@ "], [" ;; even under Node.js where runtime require is possible ;; this is necessary - see CLJS-2151 - (ns-list (deps/-requires input)) + (ns-list (cond->> (deps/-requires input) + ;; under Node.js we emit native `require`s for these + (= :nodejs (:target opts)) + (filter (complement ana/node-module-dep?)))) "]);\n"))) (defn deps-file @@ -2377,7 +2380,7 @@ - index all the node node modules - process the JS modules (preprocess + convert to Closure JS)o - save js-dependency-index for compilation" - [{:keys [npm-deps] :as opts} js-sources compiler-env] + [{:keys [npm-deps target] :as opts} js-sources compiler-env] (let [;; Find all the top-level Node packages and their files top-level-modules (reduce (fn [acc m] (reduce (fn [acc p] @@ -2389,15 +2392,19 @@ requires (set (mapcat deps/-requires js-sources)) ;; Select Node files that are required by Cljs code, ;; and create list of all their dependencies - required-node-modules (set/intersection (set (keys top-level-modules)) requires) - opts (-> opts - (update :foreign-libs - (fn [libs] - (into (index-node-modules required-node-modules) - (expand-libs libs)))) - process-js-modules)] - (swap! compiler-env assoc :js-dependency-index (deps/js-dependency-index opts)) - opts)) + required-node-modules (set/intersection (set (keys top-level-modules)) requires)] + (if-not (= target :nodejs) + (let [opts (-> opts + (update :foreign-libs + (fn [libs] + (into (index-node-modules required-node-modules) + (expand-libs libs)))) + process-js-modules)] + (swap! compiler-env assoc :js-dependency-index (deps/js-dependency-index opts)) + opts) + (do + (swap! compiler-env update-in [:node-module-index] (fnil into #{}) (map str required-node-modules)) + opts)))) (defn build "Given a source which can be compiled, produce runnable JavaScript." diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a0a59913a..994de1199 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1061,28 +1061,33 @@ (emit-wrap env (emits target " = " val))) (defn load-libs - [libs seen reloads deps] - (let [loaded-libs (munge 'cljs.core.*loaded-libs*) - loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*))] + [libs seen reloads deps ns-name] + (let [{:keys [target optimizations]} (get @env/*compiler* :options) + loaded-libs (munge 'cljs.core.*loaded-libs*) + loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*)) + [node-libs libs-to-load] (let [libs (remove (set (vals seen)) (filter (set (vals libs)) deps))] + (if (= :nodejs target) + (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? libs)] + [node-libs libs-to-load]) + [nil libs]))] (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) - (doseq [lib (remove (set (vals seen)) (filter (set (vals libs)) deps))] + (doseq [lib libs-to-load] (cond #?@(:clj [(ana/foreign-dep? lib) - (let [{:keys [target optimizations]} (get @env/*compiler* :options)] - ;; we only load foreign libraries under optimizations :none - (when (= :none optimizations) - (if (= :nodejs target) - ;; under node.js we load foreign libs globally - (let [{:keys [js-dependency-index options]} @env/*compiler* - ijs (get js-dependency-index (name lib))] - (emitln "cljs.core.load_file(\"" - (str (io/file (util/output-directory options) (or (deps/-relative-path ijs) - (util/relative-name (:url ijs))))) - "\");")) - (emitln "goog.require('" (munge lib) "');"))))] + ;; we only load foreign libraries under optimizations :none + (when (= :none optimizations) + (if (= :nodejs target) + ;; under node.js we load foreign libs globally + (let [{:keys [js-dependency-index options]} @env/*compiler* + ijs (get js-dependency-index (name lib))] + (emitln "cljs.core.load_file(\"" + (str (io/file (util/output-directory options) (or (deps/-relative-path ijs) + (util/relative-name (:url ijs))))) + "\");")) + (emitln "goog.require('" (munge lib) "');")))] :cljs [(and (ana/foreign-dep? lib) (when-let [{:keys [optimizations]} (get @env/*compiler* :options)] @@ -1099,13 +1104,17 @@ :else (emitln "goog.require('" (munge lib) "');"))) + (doseq [lib node-libs] + (emitln (munge ns-name) "." + (ana/munge-node-lib lib) + " = require('" lib "');")) (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) (defmethod emit* :ns* [{:keys [name requires uses require-macros reloads env deps]}] - (load-libs requires nil (:require reloads) deps) - (load-libs uses requires (:use reloads) deps) + (load-libs requires nil (:require reloads) deps name) + (load-libs uses requires (:use reloads) deps name) (when (:repl-env env) (emitln "null;"))) @@ -1116,8 +1125,8 @@ (emitln "goog.require('cljs.core');") (when (-> @env/*compiler* :options :emit-constants) (emitln "goog.require('" (munge ana/constants-ns-sym) "');"))) - (load-libs requires nil (:require reloads) deps) - (load-libs uses requires (:use reloads) deps)) + (load-libs requires nil (:require reloads) deps name) + (load-libs uses requires (:use reloads) deps name)) (defmethod emit* :deftype* [{:keys [t fields pmasks body protocols]}] diff --git a/src/test/cljs_build/emit_node_requires_test/core.cljs b/src/test/cljs_build/emit_node_requires_test/core.cljs new file mode 100644 index 000000000..98ca70ae9 --- /dev/null +++ b/src/test/cljs_build/emit_node_requires_test/core.cljs @@ -0,0 +1,10 @@ +(ns emit-node-requires-test.core + (:require [react :refer [createElement]] + ["react-dom/server" :as ReactDOMServer])) + +(enable-console-print!) + +(println "ReactDOMServer exists:" ReactDOMServer + (.-renderToString ReactDOMServer)) + +(println "hi" (ReactDOMServer/renderToString (createElement "div" nil "Hello World!"))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index dde406e3f..ba2fd7c98 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -278,3 +278,37 @@ (io/file "src/test/cljs/js_libs")) opts cenv) (is (.exists (io/file out "tabby.js"))))) + +(defn collecting-warning-handler [state] + (fn [warning-type env extra] + (when (warning-type ana/*cljs-warnings*) + (when-let [s (ana/error-message warning-type extra)] + (swap! state conj s))))) + +(deftest test-emit-node-requires-cljs-2213 + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (testing "simplest case, require" + (let [ws (atom []) + out (.getPath (io/file (test/tmp-dir) "emit-node-requires-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'emit-node-requires-test.core + :output-dir out + :optimizations :none + :target :nodejs + :npm-deps {:react "15.6.1" + :react-dom "15.6.1"} + :closure-warnings {:check-types :off + :non-standard-jsdoc :off}}} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (build/build (build/inputs (io/file inputs "emit_node_requires_test/core.cljs")) opts cenv)) + ;; wasn't processed by Closure + (is (not (.exists (io/file out "node_modules/react/react.js")))) + (is (.exists (io/file out "emit_node_requires_test/core.js"))) + (is (true? (boolean (re-find #"emit_node_requires_test\.core\.react_dom\$server = require\('react-dom/server'\);" + (slurp (io/file out "emit_node_requires_test/core.js")))))) + (is (empty? @ws)))) + (.delete (io/file "package.json")) + (test/delete-node-modules)) From 79b9f973580cb1ba1764e45b3ca8ed3e8cb916d9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 12 Jul 2017 07:57:50 -0400 Subject: [PATCH 2535/4033] prevent likely hood of clash with ns vars by prepending "node$module$" to the munged lib name --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/build_api_tests.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7bab5bc83..1db13633a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -893,7 +893,7 @@ (not ^boolean (goog.string/contains s "..")))))) (defn munge-node-lib [name] - (munge (string/replace (str name) #"[.\/]" "\\$"))) + (str "node$module$" (munge (string/replace (str name) #"[.\/]" "\\$")))) (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index ba2fd7c98..66edf8d59 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -307,7 +307,7 @@ ;; wasn't processed by Closure (is (not (.exists (io/file out "node_modules/react/react.js")))) (is (.exists (io/file out "emit_node_requires_test/core.js"))) - (is (true? (boolean (re-find #"emit_node_requires_test\.core\.react_dom\$server = require\('react-dom/server'\);" + (is (true? (boolean (re-find #"emit_node_requires_test\.core\.node\$module\$react_dom\$server = require\('react-dom/server'\);" (slurp (io/file out "emit_node_requires_test/core.js")))))) (is (empty? @ws)))) (.delete (io/file "package.json")) From 6be311f5be76d86ec4b02e8d47e7c58fbd016860 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 12 Jul 2017 16:22:04 -0400 Subject: [PATCH 2536/4033] add *print-fn-bodies* knob, set to false --- src/main/cljs/cljs/core.cljs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f3b2b9af1..bd8d989aa 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -143,6 +143,12 @@ :jsdoc ["@type {null|number}"]} *print-level* nil) +(def + ^{:dynamic true + :doc "*print-fns-bodies* controls whether functions print their source or + only their names."} + *print-fn-bodies* false) + (defonce ^{:dynamic true :jsdoc ["@type {*}"]} @@ -9590,7 +9596,11 @@ reduces them without incurring seq initialization" name (if (or (nil? name) (gstring/isEmpty name)) "Function" name)] - (write-all writer "#object[" name " \"" (str obj) "\"]")) + (write-all writer "#object[" name + (if *print-fn-bodies* + (str " \"" (str obj) "\"") + "") + "]")) (instance? js/Date obj) (let [normalize (fn [n len] From cd3017e75ec1542b584a503874e33c98d8ee814b Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 12 Jul 2017 19:01:51 +0300 Subject: [PATCH 2537/4033] CLJS-2214: Support :global-exports for foreign libraries --- src/main/clojure/cljs/analyzer.cljc | 33 +++++++++++++++++-- src/main/clojure/cljs/compiler.cljc | 9 ++++- .../emit_global_requires_test/core.cljs | 10 ++++++ src/test/clojure/cljs/build_api_tests.clj | 33 +++++++++++++++++++ 4 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 src/test/cljs_build/emit_global_requires_test/core.cljs diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1db13633a..35e9fbdb3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -733,6 +733,12 @@ (get-in @env/*compiler* [:node-module-index]) (str module))) +(defn dep-has-global-exports? + [module] + (contains? + (get-in @env/*compiler* [:js-dependency-index (str module) :global-exports]) + (symbol module))) + (defn confirm-var-exists ([env prefix suffix] (let [warn (confirm-var-exist-warning env prefix suffix)] @@ -895,6 +901,9 @@ (defn munge-node-lib [name] (str "node$module$" (munge (string/replace (str name) #"[.\/]" "\\$")))) +(defn munge-global-export [name] + (str "global$module$" (munge (string/replace (str name) #"[.\/]" "\\$")))) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -936,10 +945,16 @@ (when (not= (-> env :ns :name) full-ns) (confirm-ns env full-ns)) (confirm env full-ns (symbol (name sym)))) - (if (node-module-dep? full-ns) + (cond + (node-module-dep? full-ns) {:name (symbol (str (-> env :ns :name)) (str (munge-node-lib full-ns) "." (name sym))) :ns (-> env :ns :name)} + (dep-has-global-exports? full-ns) + {:name (symbol (str (-> env :ns :name)) + (str (munge-global-export full-ns) "." (name sym))) + :ns (-> env :ns :name)} + :else (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym))) {:name (symbol (str full-ns) (str (name sym))) :ns full-ns}))) @@ -965,9 +980,14 @@ (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)) (let [full-ns (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)] - (if (node-module-dep? full-ns) + (cond + (node-module-dep? full-ns) {:name (symbol (str (-> env :ns :name)) (str (munge-node-lib full-ns) "." sym)) :ns (-> env :ns :name)} + (dep-has-global-exports? full-ns) + {:name (symbol (str (-> env :ns :name)) (str (munge-global-export full-ns) "." sym)) + :ns (-> env :ns :name)} + :else (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) @@ -998,6 +1018,12 @@ {:name (symbol (str (-> env :ns :name)) (munge-node-lib module)) :ns (-> env :ns :name)}) + (or (dep-has-global-exports? s) + (dep-has-global-exports? (resolve-ns-alias env s))) + (let [module (resolve-ns-alias env s)] + {:name (symbol (str (-> env :ns :name)) (munge-global-export module)) + :ns (-> env :ns :name)}) + :else (let [cur-ns (-> env :ns :name) full-ns (cond @@ -2026,7 +2052,8 @@ (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 (node-module-dep? lib)) + (not (dep-has-global-exports? lib))))) (defn missing-rename? [sym cenv] (let [lib (symbol (namespace sym)) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 994de1199..6f1d8bd96 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1069,7 +1069,8 @@ (if (= :nodejs target) (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? libs)] [node-libs libs-to-load]) - [nil libs]))] + [nil libs])) + {global-exports-libs true, libs-to-load false} (group-by ana/dep-has-global-exports? libs-to-load)] (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) @@ -1108,6 +1109,12 @@ (emitln (munge ns-name) "." (ana/munge-node-lib lib) " = require('" lib "');")) + (doseq [lib global-exports-libs] + (let [{:keys [js-dependency-index options]} @env/*compiler* + ijs (get js-dependency-index (name lib))] + (emitln (munge ns-name) "." + (ana/munge-global-export lib) + " = goog.global." (get (:global-exports ijs) (symbol lib)) ";"))) (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) diff --git a/src/test/cljs_build/emit_global_requires_test/core.cljs b/src/test/cljs_build/emit_global_requires_test/core.cljs new file mode 100644 index 000000000..7d2773a7e --- /dev/null +++ b/src/test/cljs_build/emit_global_requires_test/core.cljs @@ -0,0 +1,10 @@ +(ns emit-global-requires-test.core + (:require [react :refer [createElement]] + ["react-dom/server" :as ReactDOMServer])) + +(enable-console-print!) + +(println "ReactDOMServer exists:" ReactDOMServer + (.-renderToString ReactDOMServer)) + +(println "hi" (ReactDOMServer/renderToString (createElement "div" nil "Hello World!"))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 66edf8d59..663f08f1f 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -309,6 +309,39 @@ (is (.exists (io/file out "emit_node_requires_test/core.js"))) (is (true? (boolean (re-find #"emit_node_requires_test\.core\.node\$module\$react_dom\$server = require\('react-dom/server'\);" (slurp (io/file out "emit_node_requires_test/core.js")))))) + (is (true? (boolean (re-find #"emit_node_requires_test\.core\.node\$module\$react_dom\$server\.renderToString" + (slurp (io/file out "emit_node_requires_test/core.js")))))) (is (empty? @ws)))) (.delete (io/file "package.json")) (test/delete-node-modules)) + +(deftest test-emit-global-requires-cljs-2214 + (testing "simplest case, require" + (let [ws (atom []) + out (.getPath (io/file (test/tmp-dir) "emit-global-requires-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'emit-node-requires-test.core + :output-dir out + :optimizations :none + ;; Doesn't matter what :file is used here, as long at it exists + :foreign-libs [{:file "src/test/cljs_build/thirdparty/add.js" + :provides ["react"] + :global-exports '{react React}} + {:file "src/test/cljs_build/thirdparty/add.js" + :provides ["react-dom"] + :requires ["react"] + :global-exports '{react-dom ReactDOM}} + {:file "src/test/cljs_build/thirdparty/add.js" + :provides ["react-dom/server"] + :requires ["react-dom"] + :global-exports '{react-dom/server ReactDOMServer}}]}} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (build/build (build/inputs (io/file inputs "emit_global_requires_test/core.cljs")) opts cenv)) + (is (.exists (io/file out "emit_global_requires_test/core.js"))) + (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$react_dom\$server = goog\.global\.ReactDOMServer;" + (slurp (io/file out "emit_global_requires_test/core.js")))))) + (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$react_dom\$server\.renderToString" + (slurp (io/file out "emit_global_requires_test/core.js")))))) + (is (empty? @ws))))) From cdecac24f305086538f6c3fefeec50bc017e16d1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 12 Jul 2017 17:48:02 -0400 Subject: [PATCH 2538/4033] add a comment --- src/main/clojure/cljs/analyzer.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 35e9fbdb3..42a3853e8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1005,6 +1005,7 @@ (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym)) (recur env (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) confirm) + ;; The following three cases support for invoking JS modules that export themselves as function -David (or (js-module-exists? s) (js-module-exists? (resolve-ns-alias env s))) (let [module (or (gets @env/*compiler* :js-module-index s) From 9ca067a6290b78e771687a98ecbb003c7a505a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 12 Jul 2017 20:25:02 -0700 Subject: [PATCH 2539/4033] CLJS-2220: Add runtime :npm-deps tests --- script/clean | 3 +++ script/test | 6 +++++- script/test-simple | 6 +++++- src/test/cljs/cljs/npm_deps_test.cljs | 6 ++++++ src/test/cljs/deps.cljs | 1 + src/test/cljs/externs.js | 1 + src/test/cljs/test_runner.cljs | 2 ++ 7 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/test/cljs/cljs/npm_deps_test.cljs create mode 100644 src/test/cljs/deps.cljs create mode 100644 src/test/cljs/externs.js diff --git a/script/clean b/script/clean index bcbd54172..cd990434a 100755 --- a/script/clean +++ b/script/clean @@ -7,3 +7,6 @@ rm -rf target rm -rf builds rm -rf clojure rm -rf out +rm -rf node_modules +rm -rf package.json +rm -rf package-lock.json diff --git a/script/test b/script/test index 7727dabda..1758eda18 100755 --- a/script/test +++ b/script/test @@ -3,12 +3,16 @@ rm -rf builds/out-adv rm -rf out rm -rf target +rm -rf package.json +rm -rf package-lock.json + +echo {} > package.json mkdir -p builds/out-adv possible=5 ran=0 -if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off}}" > builds/out-adv/core-advanced-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/script/test-simple b/script/test-simple index 64e79fa4b..dd786f70d 100755 --- a/script/test-simple +++ b/script/test-simple @@ -2,13 +2,17 @@ # stop blowing compiled stuff rm -rf builds/out-simp +rm -rf package.json +rm -rf package-lock.json + +echo {} > package.json mkdir -p builds/out-simp possible=5 ran=0 #bin/cljsc test >out/core-test.js -if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :compiler-stats true}" > builds/out-simp/core-simple-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :verbose true :compiler-stats true :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off}}" > builds/out-simp/core-simple-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/src/test/cljs/cljs/npm_deps_test.cljs b/src/test/cljs/cljs/npm_deps_test.cljs new file mode 100644 index 000000000..b7a5be339 --- /dev/null +++ b/src/test/cljs/cljs/npm_deps_test.cljs @@ -0,0 +1,6 @@ +(ns cljs.npm-deps-test + (:require [cljs.test :refer [deftest is]] + ["lodash/array" :as array])) + +(deftest test-module-processing + (is (= (array/nth #js [1 2 3] 1) 2))) diff --git a/src/test/cljs/deps.cljs b/src/test/cljs/deps.cljs new file mode 100644 index 000000000..18a200ca4 --- /dev/null +++ b/src/test/cljs/deps.cljs @@ -0,0 +1 @@ +{:externs ["externs.js"]} diff --git a/src/test/cljs/externs.js b/src/test/cljs/externs.js new file mode 100644 index 000000000..0227355f2 --- /dev/null +++ b/src/test/cljs/externs.js @@ -0,0 +1 @@ +var nth = function(array, n){}; diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index d458d8163..709916bb2 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -38,6 +38,7 @@ [cljs.clojure-alias-test] [cljs.hash-map-test] [cljs.map-entry-test] + [cljs.npm-deps-test] [cljs.predicates-test] [cljs.tagged-literals-test] [cljs.test-test] @@ -76,6 +77,7 @@ 'cljs.clojure-alias-test 'cljs.hash-map-test 'cljs.map-entry-test + 'cljs.npm-deps-test 'cljs.pprint-test 'cljs.predicates-test 'cljs.syntax-quote-test From 6530e0d0da91d6a5f324ae4b86ca1d2d208c40d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 12 Jul 2017 16:57:39 -0700 Subject: [PATCH 2540/4033] CLJS-2218: Make ClojureScript aware of native node modules --- src/main/clojure/cljs/env.cljc | 6 ++++-- src/main/clojure/cljs/js_deps.cljc | 7 +++++++ .../native_modules.cljs | 6 ++++++ src/test/clojure/cljs/build_api_tests.clj | 20 ++++++++++++++++++- 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/test/cljs_build/emit_node_requires_test/native_modules.cljs diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index d3921322d..2f112e15d 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -9,7 +9,7 @@ (ns ^{:doc "A namespace that exists solely to provide a place for \"compiler\" state that is accessed/maintained by many different components."} cljs.env - #?(:clj (:require [cljs.js-deps :refer (js-dependency-index)] + #?(:clj (:require [cljs.js-deps :as deps] [cljs.externs :as externs])) (:refer-clojure :exclude [ensure])) @@ -55,7 +55,9 @@ state that is accessed/maintained by many different components."} (externs/externs-map (:externs-sources options))) :cljs nil) :options options} - #?(:clj {:js-dependency-index (js-dependency-index options)}))))) + (when (= (:target options) :nodejs) + {:node-module-index deps/native-node-modules}) + #?(:clj {:js-dependency-index (deps/js-dependency-index options)}))))) #?(:clj (defmacro with-compiler-env diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 02765fe82..dd6c1272b 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -344,3 +344,10 @@ JavaScript library containing provide/require 'declarations'." (str "WARNING: JavaScript file found on classpath for library `%s`, " "but does not contain a corresponding `goog.provide` declaration: %s") lib lib-resource))))))) + +(def native-node-modules + #{"assert" "buffer_ieee754" "buffer" "child_process" "cluster" "console" + "constants" "crypto" "_debugger" "dgram" "dns" "domain" "events" "freelist" + "fs" "http" "https" "_linklist" "module" "net" "os" "path" "punycode" + "querystring" "readline" "repl" "stream" "string_decoder" "sys" "timers" + "tls" "tty" "url" "util" "vm" "zlib" "_http_server" "process" "v8"}) diff --git a/src/test/cljs_build/emit_node_requires_test/native_modules.cljs b/src/test/cljs_build/emit_node_requires_test/native_modules.cljs new file mode 100644 index 000000000..1d4a9e573 --- /dev/null +++ b/src/test/cljs_build/emit_node_requires_test/native_modules.cljs @@ -0,0 +1,6 @@ +(ns emit-node-requires-test.native-modules + (:require [path :refer [isAbsolute]])) + +(enable-console-print!) + +(println (isAbsolute (path/resolve js/__filename))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 663f08f1f..07a78fa6b 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -300,7 +300,7 @@ :react-dom "15.6.1"} :closure-warnings {:check-types :off :non-standard-jsdoc :off}}} - cenv (env/default-compiler-env)] + cenv (env/default-compiler-env opts)] (test/delete-out-files out) (ana/with-warning-handlers [(collecting-warning-handler ws)] (build/build (build/inputs (io/file inputs "emit_node_requires_test/core.cljs")) opts cenv)) @@ -312,6 +312,24 @@ (is (true? (boolean (re-find #"emit_node_requires_test\.core\.node\$module\$react_dom\$server\.renderToString" (slurp (io/file out "emit_node_requires_test/core.js")))))) (is (empty? @ws)))) + (testing "Node native modules, CLJS-2218" + (let [ws (atom []) + out (.getPath (io/file (test/tmp-dir) "emit-node-requires-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'emit-node-requires-test.native-modules + :output-dir out + :optimizations :none + :target :nodejs + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env opts)] + (test/delete-out-files out) + (test/delete-node-modules) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (build/build (build/inputs (io/file inputs "emit_node_requires_test/native_modules.cljs")) opts cenv)) + (is (.exists (io/file out "emit_node_requires_test/native_modules.js"))) + (is (true? (boolean (re-find #"emit_node_requires_test\.native_modules\.node\$module\$path\.isAbsolute" + (slurp (io/file out "emit_node_requires_test/native_modules.js")))))) + (is (empty? @ws)))) (.delete (io/file "package.json")) (test/delete-node-modules)) From e1ef65856c5c95d02139452c110543f73e9a94c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 13 Jul 2017 09:34:15 -0700 Subject: [PATCH 2541/4033] CLJS-2217: Support `:rename` for JS modules --- script/test | 2 +- script/test-simple | 2 +- src/main/clojure/cljs/analyzer.cljc | 93 +++++++++++++++------------ src/test/cljs/calculator_global.js | 8 +++ src/test/cljs/cljs/npm_deps_test.cljs | 10 ++- src/test/cljs/externs.js | 3 + 6 files changed, 73 insertions(+), 45 deletions(-) create mode 100644 src/test/cljs/calculator_global.js diff --git a/script/test b/script/test index 1758eda18..a01c24bd7 100755 --- a/script/test +++ b/script/test @@ -12,7 +12,7 @@ mkdir -p builds/out-adv possible=5 ran=0 -if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off}}" > builds/out-adv/core-advanced-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-adv/core-advanced-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/script/test-simple b/script/test-simple index dd786f70d..ba71bf8c4 100755 --- a/script/test-simple +++ b/script/test-simple @@ -12,7 +12,7 @@ possible=5 ran=0 #bin/cljsc test >out/core-test.js -if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :verbose true :compiler-stats true :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off}}" > builds/out-simp/core-simple-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :verbose true :compiler-stats true :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-simp/core-simple-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 42a3853e8..89f3ff32e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -927,7 +927,8 @@ {:js-fn-var true :ret-tag ret-tag})))) (let [s (str sym) - lb (get locals sym)] + lb (get locals sym) + current-ns (-> env :ns :name)] (cond (some? lb) lb @@ -942,18 +943,18 @@ (get-in @env/*compiler* [:js-module-index ns])) (symbol ns)))] (when (some? confirm) - (when (not= (-> env :ns :name) full-ns) + (when (not= current-ns full-ns) (confirm-ns env full-ns)) (confirm env full-ns (symbol (name sym)))) (cond (node-module-dep? full-ns) - {:name (symbol (str (-> env :ns :name)) + {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) - :ns (-> env :ns :name)} + :ns current-ns} (dep-has-global-exports? full-ns) - {:name (symbol (str (-> env :ns :name)) + {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." (name sym))) - :ns (-> env :ns :name)} + :ns current-ns} :else (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym))) {:name (symbol (str full-ns) (str (name sym))) @@ -965,45 +966,56 @@ suffix (subs s (inc idx))] (if-some [lb (get locals prefix)] {:name (symbol (str (:name lb)) suffix)} - (let [cur-ns (-> env :ns :name)] - (if-some [full-ns (gets @env/*compiler* ::namespaces cur-ns :imports prefix)] - {:name (symbol (str full-ns) suffix)} - - ;else - (if-some [info (gets @env/*compiler* ::namespaces cur-ns :defs prefix)] - (merge info - {:name (symbol (str cur-ns) (str sym)) - :ns cur-ns}) - (merge (gets @env/*compiler* ::namespaces prefix :defs (symbol suffix)) - {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix)) - :ns prefix})))))) - - (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)) - (let [full-ns (gets @env/*compiler* ::namespaces (-> env :ns :name) :uses sym)] + (if-some [full-ns (gets @env/*compiler* ::namespaces current-ns :imports prefix)] + {:name (symbol (str full-ns) suffix)} + + ;else + (if-some [info (gets @env/*compiler* ::namespaces current-ns :defs prefix)] + (merge info + {:name (symbol (str current-ns) (str sym)) + :ns current-ns}) + (merge (gets @env/*compiler* ::namespaces prefix :defs (symbol suffix)) + {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix)) + :ns prefix}))))) + + (some? (gets @env/*compiler* ::namespaces current-ns :uses sym)) + (let [full-ns (gets @env/*compiler* ::namespaces current-ns :uses sym)] (cond (node-module-dep? full-ns) - {:name (symbol (str (-> env :ns :name)) (str (munge-node-lib full-ns) "." sym)) - :ns (-> env :ns :name)} + {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." sym)) + :ns current-ns} + (dep-has-global-exports? full-ns) - {:name (symbol (str (-> env :ns :name)) (str (munge-global-export full-ns) "." sym)) - :ns (-> env :ns :name)} + {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." sym)) + :ns current-ns} + :else (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) {:name (symbol (str full-ns) (str sym)) :ns full-ns}))) - (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym)) - (let [qualified-symbol (gets @env/*compiler* ::namespaces (-> env :ns :name) :renames sym) + (some? (gets @env/*compiler* ::namespaces current-ns :renames sym)) + (let [qualified-symbol (gets @env/*compiler* ::namespaces current-ns :renames sym) full-ns (symbol (namespace qualified-symbol)) sym (symbol (name qualified-symbol))] - (merge - (gets @env/*compiler* ::namespaces full-ns :defs sym) - {:name qualified-symbol - :ns full-ns})) + (cond + (node-module-dep? full-ns) + {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." sym)) + :ns current-ns} + + (dep-has-global-exports? full-ns) + {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." sym)) + :ns current-ns} + + :else + (merge + (gets @env/*compiler* ::namespaces full-ns :defs sym) + {:name qualified-symbol + :ns full-ns}))) - (some? (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym)) - (recur env (gets @env/*compiler* ::namespaces (-> env :ns :name) :imports sym) confirm) + (some? (gets @env/*compiler* ::namespaces current-ns :imports sym)) + (recur env (gets @env/*compiler* ::namespaces current-ns :imports sym) confirm) ;; The following three cases support for invoking JS modules that export themselves as function -David (or (js-module-exists? s) @@ -1016,21 +1028,20 @@ (or (node-module-dep? s) (node-module-dep? (resolve-ns-alias env s))) (let [module (resolve-ns-alias env s)] - {:name (symbol (str (-> env :ns :name)) (munge-node-lib module)) - :ns (-> env :ns :name)}) + {:name (symbol (str current-ns) (munge-node-lib module)) + :ns current-ns}) (or (dep-has-global-exports? s) (dep-has-global-exports? (resolve-ns-alias env s))) (let [module (resolve-ns-alias env s)] - {:name (symbol (str (-> env :ns :name)) (munge-global-export module)) - :ns (-> env :ns :name)}) + {:name (symbol (str current-ns) (munge-global-export module)) + :ns current-ns}) :else - (let [cur-ns (-> env :ns :name) - full-ns (cond - (some? (gets @env/*compiler* ::namespaces cur-ns :defs sym)) cur-ns + (let [full-ns (cond + (some? (gets @env/*compiler* ::namespaces current-ns :defs sym)) current-ns (core-name? env sym) 'cljs.core - :else cur-ns)] + :else current-ns)] (when (some? confirm) (confirm env full-ns sym)) (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) diff --git a/src/test/cljs/calculator_global.js b/src/test/cljs/calculator_global.js new file mode 100644 index 000000000..d1bcf38af --- /dev/null +++ b/src/test/cljs/calculator_global.js @@ -0,0 +1,8 @@ +Calculator = { + add: function (a, b) { + return a + b; + }, + subtract: function (a, b) { + return a - b; + } +}; diff --git a/src/test/cljs/cljs/npm_deps_test.cljs b/src/test/cljs/cljs/npm_deps_test.cljs index b7a5be339..e58007f95 100644 --- a/src/test/cljs/cljs/npm_deps_test.cljs +++ b/src/test/cljs/cljs/npm_deps_test.cljs @@ -1,6 +1,12 @@ (ns cljs.npm-deps-test (:require [cljs.test :refer [deftest is]] - ["lodash/array" :as array])) + ["lodash/array" :as array :refer [slice] :rename {slice slc}] + [calculator :refer [add] :rename {add plus}])) (deftest test-module-processing - (is (= (array/nth #js [1 2 3] 1) 2))) + (is (= (array/nth #js [1 2 3] 1) 2)) + ;; rename works + (is (= (array-seq (slc #js [1 2 3] 1)) [2 3]))) + +(deftest test-global-exports + (is (= (plus 1 2) 3))) diff --git a/src/test/cljs/externs.js b/src/test/cljs/externs.js index 0227355f2..f7224fd6d 100644 --- a/src/test/cljs/externs.js +++ b/src/test/cljs/externs.js @@ -1 +1,4 @@ var nth = function(array, n){}; +var Calculator = { + add: function(a, b) {} +}; From a50b4dcc80df665442b74c1174cfb791fe273ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 12 Jul 2017 19:57:09 -0700 Subject: [PATCH 2542/4033] CLJS-2219: Enable JSC under test-simple --- script/test-simple | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/script/test-simple b/script/test-simple index ba71bf8c4..fa0b75c27 100755 --- a/script/test-simple +++ b/script/test-simple @@ -33,14 +33,13 @@ else ran=$[ran+1] fi -# commented out because of memory issue in JSC w/ nested fn calls - David -#if ! hash jsc 2>/dev/null; then -# echo "jsc not on path, skipping JavaScriptCore tests" -#else -# echo "Testing with JavaScriptCore" -# jsc -f builds/out-simp/core-simple-test.js -# ran=$[ran+1] -#fi +if ! hash jsc 2>/dev/null; then + echo "jsc not on path, skipping JavaScriptCore tests" +else + echo "Testing with JavaScriptCore" + jsc -f builds/out-simp/core-simple-test.js + ran=$[ran+1] +fi if [ "$NASHORN_HOME" = "" ]; then echo "NASHORN_HOME not set, skipping Nashorn tests" From 8005f5710eda74afff56b5eaa3513e23e7337318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 13 Jul 2017 10:44:48 -0700 Subject: [PATCH 2543/4033] CLJS-2222: CI failing after CLJS-2217 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d448fdfc7..3bbf4ff50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ before_install: before_script: - script/bootstrap - mkdir -p builds/out-adv - - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\"}" > builds/out-adv/core-advanced-test.js + - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-adv/core-advanced-test.js script: - lein test From 84a2128dab9f52e67ee227a66be4f849d83de0a3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 9 Jul 2017 13:51:30 -0400 Subject: [PATCH 2544/4033] CLJS-2198: Safe array operations --- src/main/cljs/cljs/core.cljs | 83 +++++++++-- src/main/cljs/cljs/js.cljs | 27 ++++ src/main/clojure/cljs/analyzer.cljc | 100 ++++++++++--- src/main/clojure/cljs/closure.clj | 3 + src/main/clojure/cljs/compiler.cljc | 17 ++- src/main/clojure/cljs/core.cljc | 49 +++++-- src/main/clojure/cljs/repl.cljc | 4 +- src/main/clojure/cljs/repl/node.clj | 4 +- src/test/cljs/cljs/array_access/alpha.cljs | 6 + src/test/cljs/cljs/array_access/beta.cljs | 4 + src/test/cljs/cljs/array_access/helper.clj | 5 + src/test/cljs/cljs/array_access_test.cljs | 155 +++++++++++++++++++++ src/test/cljs/cljs/core_test.cljs | 8 +- src/test/cljs/test_runner.cljs | 6 +- src/test/clojure/cljs/analyzer_tests.clj | 26 ++-- src/test/self/self_parity/test.cljs | 6 +- 16 files changed, 427 insertions(+), 76 deletions(-) create mode 100644 src/test/cljs/cljs/array_access/alpha.cljs create mode 100644 src/test/cljs/cljs/array_access/beta.cljs create mode 100644 src/test/cljs/cljs/array_access/helper.clj create mode 100644 src/test/cljs/cljs/array_access_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index bd8d989aa..b97d45e64 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -17,9 +17,22 @@ ;; next line is auto-generated by the build-script - Do not edit! (def *clojurescript-version*) +;; Setting of these Vars is in ClojureScript code is associated with intrinsics +;; that affect compilation state, but otherwise turn into no-ops in the emitted +;; JavaScript. + +;; The scope of *unchecked-if* is controlled by balanced pairs of set! calls. (def *unchecked-if* false) +;; The scope of *unchecked-arrays* is file-scope: In JVM ClojureScript its side- +;; effect is to set same-named analyzer dynamic Var, which is unset via binding +;; scopes. In self-hosted it is cleared via cljs.js/post-file-side-effects. +(def *unchecked-arrays* false) +;; The scope of *warn-on-infer* is global. Its side effect is to affect +;; cljs.analyzer/*cljs-warnings*. (def *warn-on-infer* false) +(set! *unchecked-arrays* true) + (defonce PROTOCOL_SENTINEL #js {}) (def MODULE_URIS nil) ;; set by compiler @@ -261,8 +274,8 @@ [p x] (let [x (if (nil? x) nil x)] (cond - (unsafe-get p (goog/typeOf x)) true - (unsafe-get p "_") true + (unchecked-get p (goog/typeOf x)) true + (unchecked-get p "_") true :else false))) (set! *unchecked-if* false) @@ -423,6 +436,54 @@ (recur (inc i))) a)))) +(defn- maybe-warn + [e] + (when (and (exists? js/console) + (exists? js/console.warn)) + (js/console.warn e))) + +(defn- checked-aget + ([array idx] + (try + (assert (or (array? array) (js/goog.isArrayLike array))) + (assert (number? idx)) + (assert (not (neg? idx))) + (assert (< idx (alength array))) + (catch :default e + (maybe-warn e))) + (unchecked-get array idx)) + ([array idx & idxs] + (apply checked-aget (checked-aget array idx) idxs))) + +(defn- checked-aset + ([array idx val] + (try + (assert (or (array? array) (js/goog.isArrayLike array))) + (assert (number? idx)) + (assert (not (neg? idx))) + (assert (< idx (alength array))) + (catch :default e + (maybe-warn e))) + (unchecked-set array idx val)) + ([array idx idx2 & idxv] + (apply checked-aset (checked-aget array idx) idx2 idxv))) + +(defn- checked-aget' + ([array idx] + {:pre [(or (array? array) (js/goog.isArrayLike array)) + (number? idx) (not (neg? idx)) (< idx (alength array))]} + (unchecked-get array idx)) + ([array idx & idxs] + (apply checked-aget' (checked-aget' array idx) idxs))) + +(defn- checked-aset' + ([array idx val] + {:pre [(or (array? array) (js/goog.isArrayLike array)) + (number? idx) (not (neg? idx)) (< idx (alength array))]} + (unchecked-set array idx val)) + ([array idx idx2 & idxv] + (apply checked-aset' (checked-aget' array idx) idx2 idxv))) + (defn aget "Returns the value at the index/indices. Works on JavaScript arrays." ([array idx] @@ -457,7 +518,7 @@ "Invoke JavaScript object method via string. Needed when the string is not a valid unquoted property name." [obj s & args] - (.apply (unsafe-get obj s) obj (into-array args))) + (.apply (unchecked-get obj s) obj (into-array args))) ;;;;;;;;;;;;;;;;;;;;;;;;;;; core protocols ;;;;;;;;;;;;; @@ -909,7 +970,7 @@ (set! string-hash-cache-count 0)) (if (nil? k) 0 - (let [h (unsafe-get string-hash-cache k)] + (let [h (unchecked-get string-hash-cache k)] (if (number? h) h (add-to-string-hash-cache k))))) @@ -6047,7 +6108,7 @@ reduces them without incurring seq initialization" ISeqable (-seq [coll] (when (pos? (alength keys)) - (map #(vector % (unsafe-get strobj %)) + (map #(vector % (unchecked-get strobj %)) (.sort keys obj-map-compare-keys)))) ICounted @@ -6058,7 +6119,7 @@ reduces them without incurring seq initialization" (-lookup [coll k not-found] (if (and ^boolean (goog/isString k) (not (nil? (scan-array 1 k keys)))) - (unsafe-get strobj k) + (unchecked-get strobj k) not-found)) IAssociative @@ -6088,7 +6149,7 @@ reduces them without incurring seq initialization" (-find [coll k] (when (and ^boolean (goog/isString k) (not (nil? (scan-array 1 k keys)))) - [k (unsafe-get strobj k)])) + [k (unchecked-get strobj k)])) IKVReduce (-kv-reduce [coll f init] @@ -6097,7 +6158,7 @@ reduces them without incurring seq initialization" init init] (if (seq keys) (let [k (first keys) - init (f init k (unsafe-get strobj k))] + init (f init k (unchecked-get strobj k))] (if (reduced? init) @init (recur (rest keys) init))) @@ -9544,7 +9605,7 @@ reduces them without incurring seq initialization" [s] (str \" (.replace s (js/RegExp "[\\\\\"\b\f\n\r\t]" "g") - (fn [match] (unsafe-get char-escapes match))) + (fn [match] (unchecked-get char-escapes match))) \")) (declare print-map) @@ -9580,7 +9641,7 @@ reduces them without incurring seq initialization" (do (-write writer "#js ") (print-map - (map (fn [k] [(keyword k) (unsafe-get obj k)]) (js-keys obj)) + (map (fn [k] [(keyword k) (unchecked-get obj k)]) (js-keys obj)) pr-writer writer opts)) (array? obj) @@ -10206,7 +10267,7 @@ reduces them without incurring seq initialization" (identical? (type x) js/Object) (into {} (for [k (js-keys x)] - [(keyfn k) (thisfn (unsafe-get x k))])) + [(keyfn k) (thisfn (unchecked-get x k))])) :else x))] (f x)))) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index d6a5360d6..f334bbe31 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -234,6 +234,15 @@ (dissoc opts :macros-ns) cb)) +(defn post-file-side-effects + [file opts] + (when (:verbose opts) + (debug-prn "Post-file side-effects" file)) + ;; Note, we don't (set! *unchecked-arrays* false) here, as that would interpreted + ;; an intrinsic affecting the compilation of this file, emitting a no-op. We bypass this + ;; and emit our own runtime assignment code. + (js* "cljs.core._STAR_unchecked_arrays_STAR_ = false;")) + (defn require ([name cb] (require name nil cb)) @@ -272,6 +281,7 @@ (condp = lang :clj (eval-str* bound-vars source name (assoc opts :cljs-file file) (fn [res] + (post-file-side-effects file opts) (if (:error res) (cb res) (do @@ -408,6 +418,7 @@ (condp = lang :clj (analyze-str* bound-vars source name (assoc opts :cljs-file file) (fn [res] + (post-file-side-effects file opts) (if-not (:error res) (analyze-deps bound-vars ana-env lib (next deps) opts cb) (cb res)))) @@ -574,6 +585,7 @@ ((fn analyze-loop [last-ast ns] (binding [env/*compiler* (:*compiler* bound-vars) ana/*cljs-ns* ns + ana/*checked-arrays* (:checked-arrays opts) ana/*cljs-static-fns* (:static-fns opts) ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) @@ -636,6 +648,9 @@ :def-emits-var - sets whether def (and derived) forms return either a Var (if set to true) or the def init value (if false). Defaults to false. + :checked-arrays - if :warn or :error, checks inferred types and values passed + to aget/aset. Logs for incorrect values if :warn, throws if + :error. Defaults to false. :static-fns - employ static dispatch to specific function arities in emitted JavaScript, as opposed to making use of the `call` construct. Defaults to false. @@ -681,6 +696,7 @@ (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* (:*cljs-ns* bound-vars) + ana/*checked-arrays* (:checked-arrays opts) ana/*cljs-static-fns* (:static-fns opts) ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns (:*cljs-ns* bound-vars)) @@ -729,6 +745,9 @@ :def-emits-var - sets whether def (and derived) forms return either a Var (if set to true) or the def init value (if false). Default is false. + :checked-arrays - if :warn or :error, checks inferred types and values passed + to aget/aset. Logs for incorrect values if :warn, throws if + :error. Defaults to false. :static-fns - employ static dispatch to specific function arities in emitted JavaScript, as opposed to making use of the `call` construct. Defaults to false. @@ -774,6 +793,7 @@ (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* ns + ana/*checked-arrays* (:checked-arrays opts) ana/*cljs-static-fns* (:static-fns opts) ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) @@ -838,6 +858,9 @@ :def-emits-var - sets whether def (and derived) forms return either a Var (if set to true) or the def init value (if false). Default is false. + :checked-arrays - if :warn or :error, checks inferred types and values passed + to aget/aset. Logs for incorrect values if :warn, throws if + :error. Defaults to false. :static-fns - employ static dispatch to specific function arities in emitted JavaScript, as opposed to making use of the `call` construct. Defaults to false. @@ -890,6 +913,7 @@ (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* ns + ana/*checked-arrays* (:checked-arrays opts) ana/*cljs-static-fns* (:static-fns opts) ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) @@ -988,6 +1012,9 @@ :def-emits-var - sets whether def (and derived) forms return either a Var (if set to true) or the def init value (if false). Default is false. + :checked-arrays - if :warn or :error, checks inferred types and values passed + to aget/aset. Logs for incorrect values if :warn, throws if + :error. Defaults to false. :static-fns - employ static dispatch to specific function arities in emitted JavaScript, as opposed to making use of the `call` construct. Defaults to false. diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 89f3ff32e..5c5c33edf 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -44,6 +44,8 @@ (def ^:dynamic *cljs-ns* 'cljs.user) (def ^:dynamic *cljs-file* nil) #?(:clj (def ^:dynamic *unchecked-if* false)) +#?(:clj (def ^:dynamic *unchecked-arrays* false)) +(def ^:dynamic *checked-arrays* false) (def ^:dynamic *cljs-static-fns* false) (def ^:dynamic *fn-invoke-direct* false) (def ^:dynamic *cljs-macros-path* "/cljs/core") @@ -134,7 +136,7 @@ :extending-base-js-type true :invoke-ctor true :invalid-arithmetic true - :invalid-array-access false + :invalid-array-access true :protocol-invalid-method true :protocol-duped-method true :protocol-multiple-impls true @@ -150,6 +152,17 @@ :js-shadowed-by-local true :infer-warning false}) +(defn unchecked-arrays? [] + *unchecked-arrays*) + +(defn checked-arrays + "Returns false-y, :warn, or :error based on configuration and the + current value of *unchecked-arrays*." + [] + (when (and (not (-> @env/*compiler* :options :advanced)) + (not *unchecked-arrays*)) + *checked-arrays*)) + (def js-reserved #{"arguments" "abstract" "await" "boolean" "break" "byte" "case" "catch" "char" "class" "const" "continue" @@ -393,10 +406,10 @@ (str (:js-op info) ", all arguments must be numbers, got " (:types info) " instead.")) (defmethod error-message :invalid-array-access - [warning-type {:keys [js-op types]}] - (case js-op - cljs.core/aget - (str js-op ", arguments must be an array followed by numeric indices, got " types " instead" + [warning-type {:keys [name types]}] + (case name + (cljs.core/checked-aget cljs.core/checked-aget') + (str "cljs.core/aget, arguments must be an array followed by numeric indices, got " types " instead" (when (or (= 'object (first types)) (every? #{'string} (rest types))) (str " (consider " @@ -405,8 +418,8 @@ "goog.object/getValueByKeys") " for object access)"))) - cljs.core/aset - (str js-op ", arguments must be an array, followed by numeric indices, followed by a value, got " types " instead" + (cljs.core/checked-aset cljs.core/checked-aset') + (str "cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got " types " instead" (when (or (= 'object (first types)) (every? #{'string} (butlast (rest types)))) " (consider goog.object/set for object access)")))) @@ -904,6 +917,21 @@ (defn munge-global-export [name] (str "global$module$" (munge (string/replace (str name) #"[.\/]" "\\$")))) +(defn resolve-alias + "Takes a namespace and an unqualified symbol and potentially returns a new + symbol to be used in lieu of the original." + [ns sym] + ;; Conditionally alias aget/aset fns to checked variants + (if (and (= 'cljs.core ns) + ('#{aget aset} sym) + (checked-arrays)) + (get-in '{:warn {aget checked-aget + aset checked-aset} + :error {aget checked-aget' + aset checked-aset'}} + [(checked-arrays) sym]) + sym)) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -1041,7 +1069,8 @@ (let [full-ns (cond (some? (gets @env/*compiler* ::namespaces current-ns :defs sym)) current-ns (core-name? env sym) 'cljs.core - :else current-ns)] + :else current-ns) + sym (resolve-alias full-ns sym)] (when (some? confirm) (confirm env full-ns sym)) (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) @@ -1962,6 +1991,12 @@ (set! *unchecked-if* val) ::set-unchecked-if) + (and (= target '*unchecked-arrays*) ;; TODO: proper resolve + (or (true? val) (false? val))) + (do + (set! *unchecked-arrays* val) + ::set-unchecked-arrays) + (= target '*warn-on-infer*) (do (set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning true)) @@ -1989,7 +2024,7 @@ (when-not targetexpr (throw (error env "set! target must be a field or a symbol naming a var"))) (cond - (some? (#{::set-unchecked-if ::set-warn-on-infer} targetexpr)) + (some? (#{::set-unchecked-if ::set-unchecked-arrays ::set-warn-on-infer} targetexpr)) {:env env :op :no-op} :else @@ -2964,12 +2999,6 @@ :cljs (symbol-identical? sym (:js-op form-meta))))] (when (true? numeric) (validate :invalid-arithmetic #(every? numeric-type? %))) - (when (op-match? 'cljs.core/aget) - (validate :invalid-array-access #(and (array-type? (first %)) - (every? numeric-type? (rest %))))) - (when (op-match? 'cljs.core/aset) - (validate :invalid-array-access #(and (array-type? (first %)) - (every? numeric-type? (butlast (rest %)))))) {:op :js :env env :segs segs @@ -3407,6 +3436,33 @@ ast))) ast))) +;; A set of validators that can be used to do static type +;; checking of runtime fns based on inferred argument types. +(def invoke-arg-type-validators + (let [aget-validator {:valid? #(and (array-type? (first %)) + (every? numeric-type? (rest %))) + :warning-type :invalid-array-access} + aset-validator {:valid? #(and (array-type? (first %)) + (every? numeric-type? (butlast (rest %)))) + :warning-type :invalid-array-access}] + {'cljs.core/checked-aget aget-validator + 'cljs.core/checked-aset aset-validator + 'cljs.core/checked-aget' aget-validator + 'cljs.core/checked-aset' aset-validator})) + +(defn check-invoke-arg-types + [env {:keys [op] :as ast} opts] + (when (and (not (analyzed? ast)) + #?(:clj (= :invoke op) + :cljs (keyword-identical? :invoke op))) + (when-some [[name {:keys [valid? warning-type]}] (find invoke-arg-type-validators (-> ast :f :info :name))] + (let [types (mapv :tag (:args ast))] + (when-not (valid? types) + (warning warning-type env + {:name name + :types types}))))) + (analyzed ast)) + #?(:clj (defn analyze-form [env form name opts] (load-core) @@ -3453,8 +3509,8 @@ (defn analyze* [env form name opts] (let [passes *passes* passes (if (nil? passes) - #?(:clj [infer-type ns-side-effects] - :cljs [infer-type]) + #?(:clj [infer-type check-invoke-arg-types ns-side-effects] + :cljs [infer-type check-invoke-arg-types]) passes) form (if (instance? LazySeq form) (if (seq form) form ()) @@ -3843,7 +3899,8 @@ ([forms opts] (let [env (assoc (empty-env) :build-options opts)] (binding [*file-defs* nil - #?@(:clj [*unchecked-if* false]) + #?@(:clj [*unchecked-if* false + *unchecked-arrays* false]) *cljs-ns* 'cljs.user *cljs-file* nil reader/*alias-map* (or reader/*alias-map* {})] @@ -3874,9 +3931,10 @@ ([f opts] (analyze-file f false opts)) ([f skip-cache opts] - (binding [*file-defs* (atom #{}) - *unchecked-if* false - *cljs-warnings* *cljs-warnings*] + (binding [*file-defs* (atom #{}) + *unchecked-if* false + *unchecked-arrays* false + *cljs-warnings* *cljs-warnings*] (let [output-dir (util/output-directory opts) res (cond (instance? File f) f diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 05161869b..00a209186 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2420,6 +2420,8 @@ (check-npm-deps opts) (maybe-install-node-deps! opts) (let [compiler-stats (:compiler-stats opts) + checked-arrays (or (:checked-arrays opts) + ana/*checked-arrays*) static-fns? (or (and (= (:optimizations opts) :advanced) (not (false? (:static-fns opts)))) (:static-fns opts) @@ -2442,6 +2444,7 @@ (assoc :sources sources))) (binding [comp/*recompiled* (when-not (false? (:recompile-dependents opts)) (atom #{})) + ana/*checked-arrays* checked-arrays ana/*cljs-static-fns* static-fns? ana/*fn-invoke-direct* (or (and static-fns? (:fn-invoke-direct opts)) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 6f1d8bd96..725d5d091 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1230,7 +1230,7 @@ (defn- build-affecting-options [opts] (select-keys opts [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target - :cache-key])) + :cache-key :checked-arrays])) #?(:clj (defn compiled-by-string @@ -1320,6 +1320,7 @@ ana/*cljs-ns* 'cljs.user ana/*cljs-file* (.getPath ^File src) reader/*alias-map* (or reader/*alias-map* {}) + ana/*checked-arrays* (or ana/*checked-arrays* (:checked-arrays opts)) ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) *source-map-data* (when (:source-map opts) (atom @@ -1472,9 +1473,10 @@ (:options @env/*compiler*)))) ([src dest opts] {:post [map?]} - (binding [ana/*file-defs* (atom #{}) - ana/*unchecked-if* false - ana/*cljs-warnings* ana/*cljs-warnings*] + (binding [ana/*file-defs* (atom #{}) + ana/*unchecked-if* false + ana/*unchecked-arrays* false + ana/*cljs-warnings* ana/*cljs-warnings*] (let [nses (get @env/*compiler* ::ana/namespaces) src-file (io/file src) dest-file (io/file dest) @@ -1483,9 +1485,10 @@ (try (let [{ns :ns :as ns-info} (ana/parse-ns src-file dest-file opts) opts (if (and (not= (util/ext src) "clj") ;; skip cljs.core macro-ns - (= ns 'cljs.core) - (not (false? (:static-fns opts)))) - (assoc opts :static-fns true) + (= ns 'cljs.core)) + (cond-> opts + (not (false? (:static-fns opts))) (assoc :static-fns true) + true (dissoc :checked-arrays)) opts)] (if (or (requires-compilation? src-file dest-file opts) (:force opts)) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index afe99cf8d..8e5ea54e1 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -45,7 +45,7 @@ defcurried rfn specify! js-this this-as implements? array js-obj simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? specify copy-arguments goog-define js-comment js-inline-comment - unsafe-cast require-macros use-macros gen-apply-to-simple unsafe-get])]) + unsafe-cast require-macros use-macros gen-apply-to-simple unchecked-get unchecked-set])]) #?(:cljs (:require-macros [cljs.core :as core] [cljs.support :refer [assert-args]])) (:require clojure.walk @@ -979,26 +979,45 @@ (core/defmacro aget ([array idx] - (core/list 'js* "(~{}[~{}])" array idx)) + (core/case (ana/checked-arrays) + :warn `(checked-aget ~array ~idx) + :error `(checked-aget' ~array ~idx) + (core/list 'js* "(~{}[~{}])" array idx))) ([array idx & idxs] - (core/let [astr (apply core/str (repeat (count idxs) "[~{}]"))] - `(~'js* ~(core/str "(~{}[~{}]" astr ")") ~array ~idx ~@idxs)))) + (core/case (ana/checked-arrays) + :warn `(checked-aget ~array ~idx ~@idxs) + :error `(checked-aget' ~array ~idx ~@idxs) + (core/let [astr (apply core/str (repeat (count idxs) "[~{}]"))] + `(~'js* ~(core/str "(~{}[~{}]" astr ")") ~array ~idx ~@idxs))))) (core/defmacro aset ([array idx val] - (core/list 'js* "(~{}[~{}] = ~{})" array idx val)) + (core/case (ana/checked-arrays) + :warn `(checked-aset ~array ~idx ~val) + :error `(checked-aset' ~array ~idx ~val) + (core/list 'js* "(~{}[~{}] = ~{})" array idx val))) ([array idx idx2 & idxv] - (core/let [n (core/dec (count idxv)) - astr (apply core/str (repeat n "[~{}]"))] - `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~array ~idx ~idx2 ~@idxv)))) - -(core/defmacro unsafe-get + (core/case (ana/checked-arrays) + :warn `(checked-aset ~array ~idx ~idx2 ~@idxv) + :error `(checked-aset' ~array ~idx ~idx2 ~@idxv) + (core/let [n (core/dec (count idxv)) + astr (apply core/str (repeat n "[~{}]"))] + `(~'js* ~(core/str "(~{}[~{}][~{}]" astr " = ~{})") ~array ~idx ~idx2 ~@idxv))))) + +(core/defmacro unchecked-get "INTERNAL. Compiles to JavaScript property access using bracket notation. Does not distinguish between object and array types and not subject to compiler static analysis." [obj key] (core/list 'js* "(~{}[~{}])" obj key)) +(core/defmacro unchecked-set + "INTERNAL. Compiles to JavaScript property access using bracket notation. Does + not distinguish between object and array types and not subject to compiler + static analysis." + [obj key val] + (core/list 'js* "(~{}[~{}] = ~{})" obj key val)) + (core/defmacro ^::ana/numeric + ([] 0) ([x] x) @@ -1983,10 +2002,10 @@ (not (nil? (. ~(first sig) ~(symbol (core/str "-" slot)))))) ;; Property access needed here. (. ~(first sig) ~slot ~@sig) (let [x# (if (nil? ~(first sig)) nil ~(first sig)) - m# (unsafe-get ~(fqn fname) (goog/typeOf x#))] + m# (unchecked-get ~(fqn fname) (goog/typeOf x#))] (if-not (nil? m#) (m# ~@sig) - (let [m# (unsafe-get ~(fqn fname) "_")] + (let [m# (unchecked-get ~(fqn fname) "_")] (if-not (nil? m#) (m# ~@sig) (throw @@ -2992,12 +3011,12 @@ `(let [len# (alength (js-arguments))] (loop [i# 0] (when (< i# len#) - (.push ~dest (aget (js-arguments) i#)) + (.push ~dest (unchecked-get (js-arguments) i#)) (recur (inc i#)))))) (core/defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl] emit-var?] (core/letfn [(dest-args [c] - (map (core/fn [n] `(aget (js-arguments) ~n)) + (map (core/fn [n] `(unchecked-get (js-arguments) ~n)) (range c)))] (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) sig (remove '#{&} arglist) @@ -3032,7 +3051,7 @@ (core/defn- multi-arity-fn [name meta fdecl emit-var?] (core/letfn [(dest-args [c] - (map (core/fn [n] `(aget (js-arguments) ~n)) + (map (core/fn [n] `(unchecked-get (js-arguments) ~n)) (range c))) (fixed-arity [rname sig] (core/let [c (count sig)] diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index eeb53bdad..4182aa519 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -765,7 +765,7 @@ (let [repl-opts (-repl-options repl-env) repl-requires (into repl-requires (:repl-requires repl-opts)) {:keys [analyze-path repl-verbose warn-on-undeclared special-fns - static-fns fn-invoke-direct] + checked-arrays static-fns fn-invoke-direct] :as opts :or {warn-on-undeclared true}} (merge @@ -788,6 +788,7 @@ (when (:source-map opts) (.start (Thread. (bound-fn [] (read-source-map "cljs/core.aot.js"))))) (binding [ana/*unchecked-if* false + ana/*unchecked-arrays* false *err* (if bind-err (cond-> *out* (not (instance? PrintWriter *out*)) (PrintWriter.)) @@ -809,6 +810,7 @@ false warn-on-undeclared))) {:infer-warning false})) + ana/*checked-arrays* checked-arrays ana/*cljs-static-fns* static-fns ana/*fn-invoke-direct* (and static-fns fn-invoke-direct) *repl-opts* opts] diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index ea5092c23..b1a653890 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -179,7 +179,7 @@ '(set! (.-require js/goog) (fn [name] (js/CLOSURE_IMPORT_SCRIPT - (unsafe-get (.. js/goog -dependencies_ -nameToPath) name))))) + (unchecked-get (.. js/goog -dependencies_ -nameToPath) name))))) ;; load cljs.core, setup printing (repl/evaluate-form repl-env env "" '(do @@ -195,7 +195,7 @@ (when (or (not (contains? *loaded-libs* name)) reload) (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) (js/CLOSURE_IMPORT_SCRIPT - (unsafe-get (.. js/goog -dependencies_ -nameToPath) name)))))))))) + (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))))))) (defrecord NodeEnv [host port path socket proc] repl/IReplEnvOptions diff --git a/src/test/cljs/cljs/array_access/alpha.cljs b/src/test/cljs/cljs/array_access/alpha.cljs new file mode 100644 index 000000000..b3e934350 --- /dev/null +++ b/src/test/cljs/cljs/array_access/alpha.cljs @@ -0,0 +1,6 @@ +(ns cljs.array-access.alpha + (:require-macros [cljs.array-access.helper :as helper]) + (:require [cljs.array-access.beta])) + +(defn unchecked-arrays? [] + (helper/unchecked-arrays?)) diff --git a/src/test/cljs/cljs/array_access/beta.cljs b/src/test/cljs/cljs/array_access/beta.cljs new file mode 100644 index 000000000..dab735ff4 --- /dev/null +++ b/src/test/cljs/cljs/array_access/beta.cljs @@ -0,0 +1,4 @@ +(ns cljs.array-access.beta + (:require-macros [cljs.array-access.helper :as helper])) + +(set! *unchecked-arrays* true) diff --git a/src/test/cljs/cljs/array_access/helper.clj b/src/test/cljs/cljs/array_access/helper.clj new file mode 100644 index 000000000..f60bf8834 --- /dev/null +++ b/src/test/cljs/cljs/array_access/helper.clj @@ -0,0 +1,5 @@ +(ns cljs.array-access.helper + (:require [cljs.analyzer :as ana])) + +(defmacro unchecked-arrays? [] + (ana/unchecked-arrays?)) diff --git a/src/test/cljs/cljs/array_access_test.cljs b/src/test/cljs/cljs/array_access_test.cljs new file mode 100644 index 000000000..fd76d5cf0 --- /dev/null +++ b/src/test/cljs/cljs/array_access_test.cljs @@ -0,0 +1,155 @@ +(ns cljs.array-access-test + (:require [cljs.test :refer [deftest is]] + [cljs.array-access.alpha :as alpha])) + +(deftest unchecked-arrays-file-scope-test + (is (not (alpha/unchecked-arrays?)))) + +(deftest aget-test + (is (thrown? js/Error (aget nil 1))) + (is (nil? (aget #js {} 1))) + (is (nil? (aget #js [] 0))) + (is (nil? (aget #js [1] -1))) + (is (nil? (aget #js [1] 1))) + (is (== 1 (aget #js [1] "0"))) + (is (nil? (aget [1] 0))) + (is (== 1 (aget #js [1] 0))) + (is (== 1 (aget #js {:foo 1} "foo"))) + (is (nil? (aget #js [#js {}] 0 0))) + (is (nil? (aget #js [#js []] 0 0))) + (is (nil? (aget #js [#js [1]] 0 -1))) + (is (nil? (aget #js [#js [1]] 0 1))) + (is (== 1 (aget #js [#js [1]] 0 "0"))) + (is (== 1 (aget #js [#js [1]] 0 0)))) + +(deftest aset-test + (is (thrown? js/Error (aset nil 1 "x"))) + (is (= "x" (aset #js {} 1 "x"))) + (is (= "x" (aset #js [] 0 "x"))) + (is (= "x" (aset #js [1] -1 "x"))) + (is (= "x" (aset #js [1] 1 "x"))) + (is (= "x" (aset #js [1] "0" "x"))) + (is (= "x" (aset [1] 0 "x"))) + (is (= "x" (aset #js [1] 0 "x"))) + (let [v #js [1]] + (aset v 0 "x") + (is (= "x" (aget v 0)))) + (let [v #js {:foo 1}] + (aset v "foo" "x") + (is (= "x" (aget v "foo")))) + (is (= "x" (aset #js [#js {}] 0 0 "x"))) + (is (= "x" (aset #js [#js []] 0 0 "x"))) + (is (= "x" (aset #js [#js [1]] 0 -1 "x"))) + (is (= "x" (aset #js [#js [1]] 0 1 "x"))) + (is (= "x" (aset #js [#js [1]] 0 "0" "x"))) + (is (= "x" (aset #js [#js [1]] 0 0 "x"))) + (let [v #js [#js [1]]] + (aset v 0 0 "x") + (is (= "x" (aget v 0 0))))) + +(deftest unchecked-aget-test + (is (thrown? js/Error (unchecked-get nil 1))) + (is (nil? (unchecked-get #js {} 1))) + (is (nil? (unchecked-get #js [] 0))) + (is (nil? (unchecked-get #js [1] -1))) + (is (nil? (unchecked-get #js [1] 1))) + (is (== 1 (unchecked-get #js [1] "0"))) + (is (nil? (unchecked-get [1] 0))) + (is (== 1 (unchecked-get #js [1] 0))) + (is (== 1 (unchecked-get #js {:foo 1} "foo")))) + +(deftest unchecked-set-test + (is (thrown? js/Error (unchecked-set nil 1 "x"))) + (is (= "x" (unchecked-set #js {} 1 "x"))) + (is (= "x" (unchecked-set #js [] 0 "x"))) + (is (= "x" (unchecked-set #js [1] -1 "x"))) + (is (= "x" (unchecked-set #js [1] 1 "x"))) + (is (= "x" (unchecked-set #js [1] "0" "x"))) + (is (= "x" (unchecked-set [1] 0 "x"))) + (is (= "x" (unchecked-set #js [1] 0 "x"))) + (let [v #js [1]] + (unchecked-set v 0 "x") + (is (= "x" (aget v 0)))) + (let [v #js {:foo 1}] + (unchecked-set v "foo" "x") + (is (= "x" (aget v "foo"))))) + +(deftest checked-aget-test + (is (thrown? js/Error (checked-aget nil 1))) + (is (nil? (checked-aget #js {} 1))) + (is (nil? (checked-aget #js [] 0))) + (is (nil? (checked-aget #js [1] -1))) + (is (nil? (checked-aget #js [1] 1))) + (is (== 1 (checked-aget #js [1] "0"))) + (is (nil? (checked-aget [1] 0))) + (is (== 1 (checked-aget #js [1] 0))) + (is (== 1 (checked-aget #js {:foo 1} "foo"))) + (is (nil? (checked-aget #js [#js {}] 0 0))) + (is (nil? (checked-aget #js [#js []] 0 0))) + (is (nil? (checked-aget #js [#js [1]] 0 -1))) + (is (nil? (checked-aget #js [#js [1]] 0 1))) + (is (== 1 (checked-aget #js [#js [1]] 0 "0"))) + (is (== 1 (checked-aget #js [#js [1]] 0 0)))) + +(deftest checked-aset-test + (is (thrown? js/Error (checked-aset nil 1 "x"))) + (is (= "x" (checked-aset #js {} 1 "x"))) + (is (= "x" (checked-aset #js [] 0 "x"))) + (is (= "x" (checked-aset #js [1] -1 "x"))) + (is (= "x" (checked-aset #js [1] 1 "x"))) + (is (= "x" (checked-aset #js [1] "0" "x"))) + (is (= "x" (checked-aset [1] 0 "x"))) + (is (= "x" (checked-aset #js [1] 0 "x"))) + (let [v #js [1]] + (checked-aset v 0 "x") + (is (= "x" (aget v 0)))) + (let [v #js {:foo 1}] + (checked-aset v "foo" "x") + (is (= "x" (aget v "foo")))) + (is (= "x" (checked-aset #js [#js {}] 0 0 "x"))) + (is (= "x" (checked-aset #js [#js []] 0 0 "x"))) + (is (= "x" (checked-aset #js [#js [1]] 0 -1 "x"))) + (is (= "x" (checked-aset #js [#js [1]] 0 1 "x"))) + (is (= "x" (checked-aset #js [#js [1]] 0 "0" "x"))) + (is (= "x" (checked-aset #js [#js [1]] 0 0 "x"))) + (let [v #js [#js [1]]] + (checked-aset v 0 0 "x") + (is (= "x" (aget v 0 0))))) + +(deftest checked-aget'-test + (is (thrown? js/Error (checked-aget' nil 1))) + (is (thrown? js/Error (checked-aget' #js {} 1))) + (is (thrown? js/Error (checked-aget' #js [] 0))) + (is (thrown? js/Error (checked-aget' #js [1] -1))) + (is (thrown? js/Error (checked-aget' #js [1] 1))) + (is (thrown? js/Error (checked-aget' #js [1] "0"))) + (is (thrown? js/Error (checked-aget' [1] 0))) + (is (== 1 (checked-aget' #js [1] 0))) + (is (thrown? js/Error (checked-aget' #js [#js {}] 0 0))) + (is (thrown? js/Error (checked-aget' #js [#js []] 0 0))) + (is (thrown? js/Error (checked-aget' #js [#js [1]] 0 -1))) + (is (thrown? js/Error (checked-aget' #js [#js [1]] 0 1))) + (is (thrown? js/Error (checked-aget' #js [#js [1]] 0 "0"))) + (is (== 1 (checked-aget' #js [#js [1]] 0 0)))) + +(deftest checked-aset'-test + (is (thrown? js/Error (checked-aset' nil 1 "x"))) + (is (thrown? js/Error (checked-aset' #js {} 1 "x"))) + (is (thrown? js/Error (checked-aset' #js [] 0 "x"))) + (is (thrown? js/Error (checked-aset' #js [1] -1 "x"))) + (is (thrown? js/Error (checked-aset' #js [1] 1 "x"))) + (is (thrown? js/Error (checked-aset' #js [1] "0" "x"))) + (is (thrown? js/Error (checked-aset' [1] 0 "x"))) + (is (= "x" (checked-aset' #js [1] 0 "x"))) + (let [v #js [1]] + (checked-aset' v 0 "x") + (is (= "x" (aget v 0)))) + (is (thrown? js/Error (checked-aset' #js [#js {}] 0 0 "x"))) + (is (thrown? js/Error (checked-aset' #js [#js []] 0 0 "x"))) + (is (thrown? js/Error (checked-aset' #js [#js [1]] 0 -1 "x"))) + (is (thrown? js/Error (checked-aset' #js [#js [1]] 0 1 "x"))) + (is (thrown? js/Error (checked-aset' #js [#js [1]] 0 "0" "x"))) + (is (= "x" (checked-aset' #js [#js [1]] 0 0 "x"))) + (let [v #js [#js [1]]] + (checked-aset' v 0 0 "x") + (is (= "x" (aget v 0 0))))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5120f26a9..06eccc0bf 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -565,10 +565,10 @@ (is (= 13 (apply aset [array 2 0 13]))) (is (= 13 (aget array 2 0))))) -(deftest unsafe-get-test - (is (= 1 (unsafe-get #js {:a 1} "a"))) - (is (nil? (unsafe-get #js {:a 1} "b"))) - (is (nil? (unsafe-get #js {:a 1} nil)))) +(deftest unchecked-get-test + (is (= 1 (unchecked-get #js {:a 1} "a"))) + (is (nil? (unchecked-get #js {:a 1} "b"))) + (is (nil? (unchecked-get #js {:a 1} nil)))) (deftest js-invoke-test (let [o (doto (js-obj) (gobject/set "my sum" (fn [a b] (+ a b))))] diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 709916bb2..f004b286b 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -43,7 +43,8 @@ [cljs.tagged-literals-test] [cljs.test-test] [static.core-test] - [cljs.recur-test])) + [cljs.recur-test] + [cljs.array-access-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -84,4 +85,5 @@ 'cljs.tagged-literals-test 'cljs.test-test 'static.core-test - 'cljs.recur-test) + 'cljs.recur-test + 'cljs.array-access-test) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index fa2adc430..d94844d7d 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -14,7 +14,8 @@ [cljs.analyzer :as a] [cljs.analyzer.api :as ana-api] [cljs.closure :as closure] - [cljs.externs :as externs]) + [cljs.externs :as externs] + [cljs.analyzer :as ana]) (:use clojure.test)) (defn collecting-warning-handler [state] @@ -743,26 +744,29 @@ (is (= ["Use of undeclared Var cljs.user/x"] @ws)))) (deftest test-cljs-2148 - (binding [a/*cljs-warnings* (assoc a/*cljs-warnings* :invalid-array-access true)] - (let [ws (atom [])] + (binding [ana/*checked-arrays* :warn] + (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] - (a/analyze (a/empty-env) - '(aget (js-obj) "a"))) + (e/with-compiler-env test-cenv + (a/analyze (a/empty-env) + '(aget (js-obj) "a")))) (catch Exception _)) (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string] instead (consider goog.object/get for object access)"] @ws))) - (let [ws (atom [])] + (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] - (a/analyze (a/empty-env) - '(aget (js-obj) "foo" "bar"))) + (e/with-compiler-env test-cenv + (a/analyze (a/empty-env) + '(aget (js-obj) "foo" "bar")))) (catch Exception _)) (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string string] instead (consider goog.object/getValueByKeys for object access)"] @ws))) - (let [ws (atom [])] + (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] - (a/analyze (a/empty-env) - '(aset (js-obj) "a" 2))) + (e/with-compiler-env test-cenv + (a/analyze (a/empty-env) + '(aset (js-obj) "a" 2)))) (catch Exception _)) (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))))) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 3bbf37a99..9677ece68 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -298,7 +298,8 @@ [cljs.predicates-test] [cljs.test-test] [static.core-test] - [cljs.recur-test])) + [cljs.recur-test] + [cljs.array-access-test])) (fn [{:keys [value error]}] (if error (handle-error error (:source-maps @st)) @@ -337,7 +338,8 @@ 'cljs.predicates-test 'cljs.test-test 'static.core-test - 'cljs.recur-test) + 'cljs.recur-test + 'cljs.array-access-test) (fn [{:keys [value error]}] (when error (handle-error error (:source-maps @st)))))))))) From c7553f346ccff4bff98f82e4e3486dce53891030 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 13 Jul 2017 16:08:05 -0400 Subject: [PATCH 2545/4033] document user file-local compiler flags add TODO for c.a/analyzed and c.a/analyzed? --- src/main/clojure/cljs/analyzer.cljc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5c5c33edf..eb5600835 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -41,10 +41,13 @@ #?(:clj (set! *warn-on-reflection* true)) -(def ^:dynamic *cljs-ns* 'cljs.user) -(def ^:dynamic *cljs-file* nil) +;; User file-local compiler flags #?(:clj (def ^:dynamic *unchecked-if* false)) #?(:clj (def ^:dynamic *unchecked-arrays* false)) + +;; Compiler dynamic vars +(def ^:dynamic *cljs-ns* 'cljs.user) +(def ^:dynamic *cljs-file* nil) (def ^:dynamic *checked-arrays* false) (def ^:dynamic *cljs-static-fns* false) (def ^:dynamic *fn-invoke-direct* false) @@ -3031,6 +3034,9 @@ :js-op js-op :numeric numeric}))) +;; TODO: analyzed analyzed? should take pass name as qualified keyword arg +;; then compiler passes can mark/check individually - David + (defn analyzed "Mark a form as being analyzed. Assumes x satisfies IMeta. Useful to suppress warnings that will have been caught by a first compiler pass." From f3955e95ab17da6e316dfca9ec8146a2d1cacec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 13 Jul 2017 11:17:25 -0700 Subject: [PATCH 2546/4033] CLJS-2223: Self-host: Undeclared Var deps/native-node-modules --- src/main/clojure/cljs/env.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 2f112e15d..dbffc3041 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -55,9 +55,9 @@ state that is accessed/maintained by many different components."} (externs/externs-map (:externs-sources options))) :cljs nil) :options options} - (when (= (:target options) :nodejs) - {:node-module-index deps/native-node-modules}) - #?(:clj {:js-dependency-index (deps/js-dependency-index options)}))))) + #?@(:clj [(when (= (:target options) :nodejs) + {:node-module-index deps/native-node-modules}) + {:js-dependency-index (deps/js-dependency-index options)}]))))) #?(:clj (defmacro with-compiler-env From bc28ddd68f0d4c769cdc9986d559dd60eb41be0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 13 Jul 2017 13:29:15 -0700 Subject: [PATCH 2547/4033] CLJS-2224: Resolve-var is wrong wrt. module resolution --- src/main/clojure/cljs/analyzer.cljc | 53 +++++++++++++++++++-------- src/test/cljs/cljs/npm_deps_test.cljs | 13 ++++++- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index eb5600835..d70ecdf9a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1048,37 +1048,58 @@ (some? (gets @env/*compiler* ::namespaces current-ns :imports sym)) (recur env (gets @env/*compiler* ::namespaces current-ns :imports sym) confirm) + (some? (gets @env/*compiler* ::namespaces current-ns :defs sym)) + (do + (when (some? confirm) + (confirm env current-ns sym)) + (merge (gets @env/*compiler* ::namespaces current-ns :defs sym) + {:name (symbol (str current-ns) (str sym)) + :ns current-ns})) + + (core-name? env sym) + (do + (when (some? confirm) + (confirm env 'cljs.core sym)) + (merge (gets @env/*compiler* ::namespaces 'cljs.core :defs sym) + {:name (symbol "cljs.core" (str sym)) + :ns 'cljs.core})) + ;; The following three cases support for invoking JS modules that export themselves as function -David - (or (js-module-exists? s) - (js-module-exists? (resolve-ns-alias env s))) + (and (or (js-module-exists? s) + (js-module-exists? (resolve-ns-alias env s))) + (let [full-ns (resolve-ns-alias env s)] + (or (contains? (set (vals (gets env :ns :requires))) full-ns) + (contains? (set (vals (gets env :ns :uses))) full-ns)))) (let [module (or (gets @env/*compiler* :js-module-index s) - (resolve-ns-alias env s))] + (resolve-ns-alias env s))] {:name (symbol module) :ns 'js}) - (or (node-module-dep? s) - (node-module-dep? (resolve-ns-alias env s))) + (and (or (node-module-dep? s) + (node-module-dep? (resolve-ns-alias env s))) + (let [full-ns (resolve-ns-alias env s)] + (or (contains? (set (vals (gets env :ns :requires))) full-ns) + (contains? (set (vals (gets env :ns :uses))) full-ns)))) (let [module (resolve-ns-alias env s)] {:name (symbol (str current-ns) (munge-node-lib module)) :ns current-ns}) - (or (dep-has-global-exports? s) - (dep-has-global-exports? (resolve-ns-alias env s))) + (and (or (dep-has-global-exports? s) + (dep-has-global-exports? (resolve-ns-alias env s))) + (let [full-ns (resolve-ns-alias env s)] + (or (contains? (set (vals (gets env :ns :requires))) full-ns) + (contains? (set (vals (gets env :ns :uses))) full-ns)))) (let [module (resolve-ns-alias env s)] {:name (symbol (str current-ns) (munge-global-export module)) :ns current-ns}) :else - (let [full-ns (cond - (some? (gets @env/*compiler* ::namespaces current-ns :defs sym)) current-ns - (core-name? env sym) 'cljs.core - :else current-ns) - sym (resolve-alias full-ns sym)] + (do (when (some? confirm) - (confirm env full-ns sym)) - (merge (gets @env/*compiler* ::namespaces full-ns :defs sym) - {:name (symbol (str full-ns) (str sym)) - :ns full-ns})))))))) + (confirm env current-ns sym)) + (merge (gets @env/*compiler* ::namespaces current-ns :defs sym) + {:name (symbol (str current-ns) (str sym)) + :ns current-ns})))))))) (defn resolve-existing-var "Given env, an analysis environment, and sym, a symbol, resolve an existing var. diff --git a/src/test/cljs/cljs/npm_deps_test.cljs b/src/test/cljs/cljs/npm_deps_test.cljs index e58007f95..776416e55 100644 --- a/src/test/cljs/cljs/npm_deps_test.cljs +++ b/src/test/cljs/cljs/npm_deps_test.cljs @@ -1,7 +1,12 @@ (ns cljs.npm-deps-test + (:refer-clojure :exclude [array vector]) (:require [cljs.test :refer [deftest is]] ["lodash/array" :as array :refer [slice] :rename {slice slc}] - [calculator :refer [add] :rename {add plus}])) + [calculator :as vector :refer [add] :rename {add plus}])) + +(def array #js [1 2 3]) + +(def vector [1]) (deftest test-module-processing (is (= (array/nth #js [1 2 3] 1) 2)) @@ -10,3 +15,9 @@ (deftest test-global-exports (is (= (plus 1 2) 3))) + +(deftest test-cljs-2224 + ;; array should be correctly resolved in the current NS (shadows module) + (is (= (array-seq array) [1 2 3])) + ;; same should happen with global-exports + (is (= vector [1]))) From e9a922306a9dbac742f5b14867076d427833ba21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 13 Jul 2017 14:07:55 -0700 Subject: [PATCH 2548/4033] CLJS-2226: :npm-deps can't index scoped packages --- src/main/clojure/cljs/closure.clj | 2 +- src/test/clojure/cljs/closure_tests.clj | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 00a209186..3fe5a0e96 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2212,7 +2212,7 @@ (:options @env/*compiler*)))) ([{:keys [verbose target]}] (letfn [(package-json? [path] - (boolean (re-find #"node_modules[/\\][^/\\]+?[/\\]package.json$" path)))] + (boolean (re-find #"node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$" path)))] (let [module-fseq (util/module-file-seq) pkg-jsons (into {} (comp diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 7a4410880..b812b0770 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -122,5 +122,15 @@ :file (.getAbsolutePath (io/file "node_modules/node-fetch/lib/index.js")) :provides ["node-fetch/lib/index.js" "node-fetch/lib/index" "node-fetch/lib"]})) modules)))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (closure/maybe-install-node-deps! {:npm-deps {"@comandeer/css-filter" "1.0.1"}}) + (let [modules (closure/index-node-modules-dir)] + (is (true? (some (fn [module] + (= module + {:file (.getAbsolutePath (io/file "node_modules/@comandeer/css-filter/dist/css-filter.umd.js")) + :module-type :commonjs + :provides ["@comandeer/css-filter"]})) + modules)))) (.delete (io/file "package.json")) (test/delete-node-modules)) From 2247b0c2be4f2900bebfb544fea18ea3ed58f540 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 13 Jul 2017 16:38:45 -0400 Subject: [PATCH 2549/4033] CLJS-2225: Need to add :checked-arrays to known compiler opts --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3fe5a0e96..9721c1624 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -166,7 +166,7 @@ :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps - :fn-invoke-direct}) + :fn-invoke-direct :checked-arrays}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 From e33509bf91f31e6c5c1fdd80812a4903b1773d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 8 Jul 2017 15:11:44 -0700 Subject: [PATCH 2550/4033] CLJS-1955: data_readers.cljc can't reference handlers in user code --- src/main/clojure/cljs/closure.clj | 10 +++++++++- src/test/cljs/data_readers.cljc | 3 ++- src/test/cljs/data_readers_test/core.cljc | 5 +++++ src/test/clojure/cljs/build_api_tests.clj | 13 ++++++++++++- 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 src/test/cljs/data_readers_test/core.cljc diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9721c1624..2085a218a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1752,6 +1752,7 @@ (do (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "Copying" (str source-url) "to" (str out-file))) + (util/mkdirs out-file) (spit out-file (slurp source-url)) (.setLastModified ^File out-file (util/last-modified source-url)))) js))) @@ -2367,7 +2368,14 @@ (def get-data-readers (memoize get-data-readers*)) (defn load-data-readers! [compiler] - (swap! compiler update-in [:cljs.analyzer/data-readers] merge (get-data-readers))) + (let [data-readers (get-data-readers) + nses (map (comp symbol namespace) (vals data-readers))] + (swap! compiler update-in [:cljs.analyzer/data-readers] merge (get-data-readers)) + (doseq [ns nses] + (try + (locking ana/load-mutex + (require ns)) + (catch Throwable _))))) (defn add-externs-sources [opts] (cond-> opts diff --git a/src/test/cljs/data_readers.cljc b/src/test/cljs/data_readers.cljc index 3204c001b..59daf97f8 100644 --- a/src/test/cljs/data_readers.cljc +++ b/src/test/cljs/data_readers.cljc @@ -8,4 +8,5 @@ {cljs/tag clojure.core/identity cljs/inc clojure.core/inc - cljs/union clojure.set/union} + cljs/union clojure.set/union + test/custom-identity data-readers-test.core/custom-identity} diff --git a/src/test/cljs/data_readers_test/core.cljc b/src/test/cljs/data_readers_test/core.cljc new file mode 100644 index 000000000..163850394 --- /dev/null +++ b/src/test/cljs/data_readers_test/core.cljc @@ -0,0 +1,5 @@ +(ns data-readers-test.core) + +(def custom-identity identity) + +(assert (= 1 #test/custom-identity 1)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 07a78fa6b..7858db3fb 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -252,7 +252,6 @@ :preloads '[preloads-test.preload] :output-dir out :optimizations :none - :npm-deps {:left-pad "1.1.3"} :closure-warnings {:check-types :off}}} cenv (env/default-compiler-env)] (test/delete-out-files out) @@ -363,3 +362,15 @@ (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$react_dom\$server\.renderToString" (slurp (io/file out "emit_global_requires_test/core.js")))))) (is (empty? @ws))))) + +(deftest test-data-readers + (let [out (.getPath (io/file (test/tmp-dir) "data-readers-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs")) + :opts {:main 'data-readers-test.core + :output-dir out + :optimizations :none + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) + (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)))) From 26fb6b1f19d0b698c7720c3140f107fa360fd7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 13 Jul 2017 15:20:35 -0700 Subject: [PATCH 2551/4033] CLJS-2228: Port CLJS-2226 to module_deps.js --- src/main/cljs/cljs/module_deps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 655aadc1b..443511af3 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -60,7 +60,7 @@ md.on('end', function() { for (var key in deps_files) { var dep = deps_files[key]; - if (dep.provides == null && !/node_modules[/\\][^/\\]+?[/\\]package.json$/.test(dep.file)) { + if (dep.provides == null && !/node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$/.test(dep.file)) { var match = dep.file.match(/node_modules[/\\](.*)\.js(on)?$/) if (match != null){ From 73173111b04c4b23ed979d3269fc2b12b196fcd3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 13 Jul 2017 19:18:17 -0400 Subject: [PATCH 2552/4033] CLJS-2227: Squelch some of the array access tests Use *print-err-fn* in maybe-warn Suppress compile/runtime warnings from array check tests --- src/main/cljs/cljs/core.cljs | 5 +- src/test/cljs/cljs/array_access_test.cljc | 13 ++ src/test/cljs/cljs/array_access_test.cljs | 163 +++++++++++++--------- 3 files changed, 109 insertions(+), 72 deletions(-) create mode 100644 src/test/cljs/cljs/array_access_test.cljc diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b97d45e64..1894198fa 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -438,9 +438,8 @@ (defn- maybe-warn [e] - (when (and (exists? js/console) - (exists? js/console.warn)) - (js/console.warn e))) + (when *print-err-fn* + (*print-err-fn* e))) (defn- checked-aget ([array idx] diff --git a/src/test/cljs/cljs/array_access_test.cljc b/src/test/cljs/cljs/array_access_test.cljc new file mode 100644 index 000000000..2cabd60a1 --- /dev/null +++ b/src/test/cljs/cljs/array_access_test.cljc @@ -0,0 +1,13 @@ +;; 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.array-access-test) + +(defmacro suppress-errs [& forms] + `(cljs.core/with-redefs [cljs.core/*print-err-fn* nil] + ~@forms)) \ No newline at end of file diff --git a/src/test/cljs/cljs/array_access_test.cljs b/src/test/cljs/cljs/array_access_test.cljs index fd76d5cf0..44a01f334 100644 --- a/src/test/cljs/cljs/array_access_test.cljs +++ b/src/test/cljs/cljs/array_access_test.cljs @@ -1,5 +1,14 @@ +;; 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.array-access-test - (:require [cljs.test :refer [deftest is]] + (:require-macros [cljs.array-access-test :refer [suppress-errs]]) + (:require [cljs.test :as test :refer [deftest is]] [cljs.array-access.alpha :as alpha])) (deftest unchecked-arrays-file-scope-test @@ -74,82 +83,98 @@ (unchecked-set v "foo" "x") (is (= "x" (aget v "foo"))))) +;; to suppress compile time warnings +(defn checked-aget-alias [& args] + (apply checked-aget args)) + +(defn checked-aset-alias [& args] + (apply checked-aset args)) + (deftest checked-aget-test - (is (thrown? js/Error (checked-aget nil 1))) - (is (nil? (checked-aget #js {} 1))) - (is (nil? (checked-aget #js [] 0))) - (is (nil? (checked-aget #js [1] -1))) - (is (nil? (checked-aget #js [1] 1))) - (is (== 1 (checked-aget #js [1] "0"))) - (is (nil? (checked-aget [1] 0))) - (is (== 1 (checked-aget #js [1] 0))) - (is (== 1 (checked-aget #js {:foo 1} "foo"))) - (is (nil? (checked-aget #js [#js {}] 0 0))) - (is (nil? (checked-aget #js [#js []] 0 0))) - (is (nil? (checked-aget #js [#js [1]] 0 -1))) - (is (nil? (checked-aget #js [#js [1]] 0 1))) - (is (== 1 (checked-aget #js [#js [1]] 0 "0"))) - (is (== 1 (checked-aget #js [#js [1]] 0 0)))) + (suppress-errs + (is (thrown? js/Error (checked-aget-alias nil 1))) + (is (nil? (checked-aget-alias #js {} 1))) + (is (nil? (checked-aget-alias #js [] 0))) + (is (nil? (checked-aget-alias #js [1] -1))) + (is (nil? (checked-aget-alias #js [1] 1))) + (is (== 1 (checked-aget-alias #js [1] "0"))) + (is (nil? (checked-aget-alias [1] 0))) + (is (== 1 (checked-aget-alias #js [1] 0))) + (is (== 1 (checked-aget-alias #js {:foo 1} "foo"))) + (is (nil? (checked-aget-alias #js [#js {}] 0 0))) + (is (nil? (checked-aget-alias #js [#js []] 0 0))) + (is (nil? (checked-aget-alias #js [#js [1]] 0 -1))) + (is (nil? (checked-aget-alias #js [#js [1]] 0 1))) + (is (== 1 (checked-aget-alias #js [#js [1]] 0 "0"))) + (is (== 1 (checked-aget-alias #js [#js [1]] 0 0))))) (deftest checked-aset-test - (is (thrown? js/Error (checked-aset nil 1 "x"))) - (is (= "x" (checked-aset #js {} 1 "x"))) - (is (= "x" (checked-aset #js [] 0 "x"))) - (is (= "x" (checked-aset #js [1] -1 "x"))) - (is (= "x" (checked-aset #js [1] 1 "x"))) - (is (= "x" (checked-aset #js [1] "0" "x"))) - (is (= "x" (checked-aset [1] 0 "x"))) - (is (= "x" (checked-aset #js [1] 0 "x"))) - (let [v #js [1]] - (checked-aset v 0 "x") - (is (= "x" (aget v 0)))) - (let [v #js {:foo 1}] - (checked-aset v "foo" "x") - (is (= "x" (aget v "foo")))) - (is (= "x" (checked-aset #js [#js {}] 0 0 "x"))) - (is (= "x" (checked-aset #js [#js []] 0 0 "x"))) - (is (= "x" (checked-aset #js [#js [1]] 0 -1 "x"))) - (is (= "x" (checked-aset #js [#js [1]] 0 1 "x"))) - (is (= "x" (checked-aset #js [#js [1]] 0 "0" "x"))) - (is (= "x" (checked-aset #js [#js [1]] 0 0 "x"))) - (let [v #js [#js [1]]] - (checked-aset v 0 0 "x") - (is (= "x" (aget v 0 0))))) + (suppress-errs + (is (thrown? js/Error (checked-aset-alias nil 1 "x"))) + (is (= "x" (checked-aset-alias #js {} 1 "x"))) + (is (= "x" (checked-aset-alias #js [] 0 "x"))) + (is (= "x" (checked-aset-alias #js [1] -1 "x"))) + (is (= "x" (checked-aset-alias #js [1] 1 "x"))) + (is (= "x" (checked-aset-alias #js [1] "0" "x"))) + (is (= "x" (checked-aset-alias [1] 0 "x"))) + (is (= "x" (checked-aset-alias #js [1] 0 "x"))) + (let [v #js [1]] + (checked-aset-alias v 0 "x") + (is (= "x" (aget v 0)))) + (let [v #js {:foo 1}] + (checked-aset-alias v "foo" "x") + (is (= "x" (aget v "foo")))) + (is (= "x" (checked-aset-alias #js [#js {}] 0 0 "x"))) + (is (= "x" (checked-aset-alias #js [#js []] 0 0 "x"))) + (is (= "x" (checked-aset-alias #js [#js [1]] 0 -1 "x"))) + (is (= "x" (checked-aset-alias #js [#js [1]] 0 1 "x"))) + (is (= "x" (checked-aset-alias #js [#js [1]] 0 "0" "x"))) + (is (= "x" (checked-aset-alias #js [#js [1]] 0 0 "x"))) + (let [v #js [#js [1]]] + (checked-aset-alias v 0 0 "x") + (is (= "x" (aget v 0 0)))))) + +;; to suppress compile time warnings +(defn checked-aget'-alias [& args] + (apply checked-aget' args)) + +(defn checked-aset'-alias [& args] + (apply checked-aset' args)) (deftest checked-aget'-test - (is (thrown? js/Error (checked-aget' nil 1))) - (is (thrown? js/Error (checked-aget' #js {} 1))) - (is (thrown? js/Error (checked-aget' #js [] 0))) - (is (thrown? js/Error (checked-aget' #js [1] -1))) - (is (thrown? js/Error (checked-aget' #js [1] 1))) - (is (thrown? js/Error (checked-aget' #js [1] "0"))) - (is (thrown? js/Error (checked-aget' [1] 0))) - (is (== 1 (checked-aget' #js [1] 0))) - (is (thrown? js/Error (checked-aget' #js [#js {}] 0 0))) - (is (thrown? js/Error (checked-aget' #js [#js []] 0 0))) - (is (thrown? js/Error (checked-aget' #js [#js [1]] 0 -1))) - (is (thrown? js/Error (checked-aget' #js [#js [1]] 0 1))) - (is (thrown? js/Error (checked-aget' #js [#js [1]] 0 "0"))) - (is (== 1 (checked-aget' #js [#js [1]] 0 0)))) + (is (thrown? js/Error (checked-aget'-alias nil 1))) + (is (thrown? js/Error (checked-aget'-alias #js {} 1))) + (is (thrown? js/Error (checked-aget'-alias #js [] 0))) + (is (thrown? js/Error (checked-aget'-alias #js [1] -1))) + (is (thrown? js/Error (checked-aget'-alias #js [1] 1))) + (is (thrown? js/Error (checked-aget'-alias #js [1] "0"))) + (is (thrown? js/Error (checked-aget'-alias [1] 0))) + (is (== 1 (checked-aget'-alias #js [1] 0))) + (is (thrown? js/Error (checked-aget'-alias #js [#js {}] 0 0))) + (is (thrown? js/Error (checked-aget'-alias #js [#js []] 0 0))) + (is (thrown? js/Error (checked-aget'-alias #js [#js [1]] 0 -1))) + (is (thrown? js/Error (checked-aget'-alias #js [#js [1]] 0 1))) + (is (thrown? js/Error (checked-aget'-alias #js [#js [1]] 0 "0"))) + (is (== 1 (checked-aget'-alias #js [#js [1]] 0 0)))) (deftest checked-aset'-test - (is (thrown? js/Error (checked-aset' nil 1 "x"))) - (is (thrown? js/Error (checked-aset' #js {} 1 "x"))) - (is (thrown? js/Error (checked-aset' #js [] 0 "x"))) - (is (thrown? js/Error (checked-aset' #js [1] -1 "x"))) - (is (thrown? js/Error (checked-aset' #js [1] 1 "x"))) - (is (thrown? js/Error (checked-aset' #js [1] "0" "x"))) - (is (thrown? js/Error (checked-aset' [1] 0 "x"))) - (is (= "x" (checked-aset' #js [1] 0 "x"))) + (is (thrown? js/Error (checked-aset'-alias nil 1 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js {} 1 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [] 0 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [1] -1 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [1] 1 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [1] "0" "x"))) + (is (thrown? js/Error (checked-aset'-alias [1] 0 "x"))) + (is (= "x" (checked-aset'-alias #js [1] 0 "x"))) (let [v #js [1]] - (checked-aset' v 0 "x") + (checked-aset'-alias v 0 "x") (is (= "x" (aget v 0)))) - (is (thrown? js/Error (checked-aset' #js [#js {}] 0 0 "x"))) - (is (thrown? js/Error (checked-aset' #js [#js []] 0 0 "x"))) - (is (thrown? js/Error (checked-aset' #js [#js [1]] 0 -1 "x"))) - (is (thrown? js/Error (checked-aset' #js [#js [1]] 0 1 "x"))) - (is (thrown? js/Error (checked-aset' #js [#js [1]] 0 "0" "x"))) - (is (= "x" (checked-aset' #js [#js [1]] 0 0 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [#js {}] 0 0 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [#js []] 0 0 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [#js [1]] 0 -1 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [#js [1]] 0 1 "x"))) + (is (thrown? js/Error (checked-aset'-alias #js [#js [1]] 0 "0" "x"))) + (is (= "x" (checked-aset'-alias #js [#js [1]] 0 0 "x"))) (let [v #js [#js [1]]] - (checked-aset' v 0 0 "x") + (checked-aset'-alias v 0 0 "x") (is (= "x" (aget v 0 0))))) From 5e3fc147e2f5fc7e634a5fcd419ace60a59c933c Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 13 Jul 2017 19:30:40 -0400 Subject: [PATCH 2553/4033] support Clojure primitive array type hints, core.async no longer gives warnings --- src/main/clojure/cljs/analyzer.cljc | 11 +++++++---- src/test/clojure/cljs/analyzer_tests.clj | 11 ++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d70ecdf9a..681045d0d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2987,6 +2987,9 @@ (contains? t 'any) (contains? t 'js)))))) +(def array-types + '#{array objects ints longs floats doubles chars shorts bytes boolean}) + (defn array-type? #?(:cljs {:tag boolean}) [t] @@ -2996,13 +2999,13 @@ (= 'clj-nil t) true (js-tag? t) true ;; TODO: revisit :else - (if (and (symbol? t) (some? (get '#{any array} t))) + (if (and (symbol? t) (contains? array-types t)) true (when #?(:clj (set? t) :cljs (cljs-set? t)) - (or (contains? t 'array) - (contains? t 'any) - (contains? t 'js)))))) + (or (contains? t 'any) + (contains? t 'js) + (boolean (some array-types t))))))) (defn analyze-js-star* [env jsform args form] (let [enve (assoc env :context :expr) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index d94844d7d..365803f5b 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -768,7 +768,16 @@ (a/analyze (a/empty-env) '(aset (js-obj) "a" 2)))) (catch Exception _)) - (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))))) + (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))) + (let [ws (atom [])] + (try + (a/with-warning-handlers [(collecting-warning-handler ws)] + (e/with-compiler-env test-cenv + (a/analyze (a/empty-env) + '(let [^objects arr (into-array [1 2 3])] + (aget arr 0))))) + (catch Exception _)) + (is (empty? @ws))))) (deftest test-cljs-2037 (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] From ee391d736ddcb3ae615f3dca46371022191e8c3d Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 13 Jul 2017 19:42:32 -0400 Subject: [PATCH 2554/4033] fix issue with 'any --- src/main/clojure/cljs/analyzer.cljc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 681045d0d..42dafcc1f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2998,14 +2998,15 @@ (nil? t) true (= 'clj-nil t) true (js-tag? t) true ;; TODO: revisit + (= 'any t) true + (contains? array-types t) true :else - (if (and (symbol? t) (contains? array-types t)) - true + (boolean (when #?(:clj (set? t) :cljs (cljs-set? t)) (or (contains? t 'any) (contains? t 'js) - (boolean (some array-types t))))))) + (some array-types t)))))) (defn analyze-js-star* [env jsform args form] (let [enve (assoc env :context :expr) From 797e247fbef676544060a57da995f058db061f37 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 13 Jul 2017 21:40:55 -0400 Subject: [PATCH 2555/4033] CLJS-2002: Don't throw when no *print-fn* is set --- src/main/cljs/cljs/core.cljs | 10 ++++------ src/main/cljs/cljs/pprint.cljs | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1894198fa..7ba7a083b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -65,17 +65,13 @@ ^{:doc "Each runtime environment provides a different way to print output. Whatever function *print-fn* is bound to will be passed any Strings which should be printed." :dynamic true} - *print-fn* - (fn [_] - (throw (js/Error. "No *print-fn* fn set for evaluation environment")))) + *print-fn* nil) (defonce ^{:doc "Each runtime environment provides a different way to print error output. Whatever function *print-err-fn* is bound to will be passed any Strings which should be printed." :dynamic true} - *print-err-fn* - (fn [_] - (throw (js/Error. "No *print-err-fn* fn set for evaluation environment")))) + *print-err-fn* nil) (defn set-print-fn! "Set *print-fn* to f." @@ -9584,6 +9580,8 @@ reduces them without incurring seq initialization" (-write writer s))) (defn string-print [x] + (when (nil? *print-fn*) + (throw (js/Error. "No *print-fn* fn set for evaluation environment"))) (*print-fn* x) nil) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index a3bfc60fb..1dfde81e2 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -806,7 +806,7 @@ The following keyword arguments can be passed with values: (binding [*out* base-writer] (pr object))) (if (true? optval) - (*print-fn* (str sb))) + (string-print (str sb))) (if (nil? optval) (str sb))))))) @@ -815,7 +815,7 @@ The following keyword arguments can be passed with values: (let [sb (StringBuffer.)] (binding [*out* (StringBufferWriter. sb)] (pprint object *out*) - (*print-fn* (str sb))))) + (string-print (str sb))))) ([object writer] (with-pretty-writer writer (binding [*print-pretty* true] @@ -2736,7 +2736,7 @@ column number or pretty printing" (-flush wrapped-stream)))) (cond (not stream) (str sb) - (true? stream) (*print-fn* (str sb)) + (true? stream) (string-print (str sb)) :else nil)))) ([format args] (map-passing-context From 424e26415476ed302098d37fd2d3cc0e6d5a3051 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Jul 2017 06:43:36 -0400 Subject: [PATCH 2556/4033] CLJS-2230: Double checked arrays Root cause were macros that called out into the analyzer to determine if further optimization was possible. These analysis calls are now done with c.a/no-warn. Added test case. --- src/main/clojure/cljs/core.cljc | 10 +++++----- src/test/clojure/cljs/analyzer_tests.clj | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8e5ea54e1..78b298ec1 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -842,7 +842,7 @@ ([x & next] (core/let [forms (concat [x] next)] (if (every? #(simple-test-expr? &env %) - (map #(cljs.analyzer/analyze &env %) forms)) + (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) forms)) (core/let [and-str (core/->> (repeat (count forms) "(~{})") (interpose " && ") (apply core/str))] @@ -860,7 +860,7 @@ ([x & next] (core/let [forms (concat [x] next)] (if (every? #(simple-test-expr? &env %) - (map #(cljs.analyzer/analyze &env %) forms)) + (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) forms)) (core/let [or-str (core/->> (repeat (count forms) "(~{})") (interpose " || ") (apply core/str))] @@ -2477,7 +2477,7 @@ ([] '(.-EMPTY cljs.core/List)) ([x & xs] - (if (= :constant (:op (cljs.analyzer/analyze &env x))) + (if (= :constant (:op (cljs.analyzer/no-warn (cljs.analyzer/analyze &env x)))) `(-conj (list ~@xs) ~x) `(let [x# ~x] (-conj (list ~@xs) x#))))) @@ -2498,7 +2498,7 @@ ([& kvs] (core/let [keys (map first (partition 2 kvs))] (if (core/and (every? #(= (:op %) :constant) - (map #(cljs.analyzer/analyze &env %) keys)) + (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) keys)) (= (count (into #{} keys)) (count keys))) `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil) `(.createAsIfByAssoc cljs.core/PersistentArrayMap (array ~@kvs)))))) @@ -2518,7 +2518,7 @@ ([& xs] (if (core/and (core/<= (count xs) 8) (every? #(= (:op %) :constant) - (map #(cljs.analyzer/analyze &env %) xs)) + (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) xs)) (= (count (into #{} xs)) (count xs))) `(cljs.core/PersistentHashSet. nil (cljs.core/PersistentArrayMap. nil ~(count xs) (array ~@(interleave xs (repeat nil))) nil) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 365803f5b..82846339a 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -745,7 +745,7 @@ (deftest test-cljs-2148 (binding [ana/*checked-arrays* :warn] - (let [ws (atom [])] + (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv @@ -753,7 +753,7 @@ '(aget (js-obj) "a")))) (catch Exception _)) (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string] instead (consider goog.object/get for object access)"] @ws))) - (let [ws (atom [])] + (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv @@ -761,7 +761,7 @@ '(aget (js-obj) "foo" "bar")))) (catch Exception _)) (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string string] instead (consider goog.object/getValueByKeys for object access)"] @ws))) - (let [ws (atom [])] + (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv @@ -769,7 +769,7 @@ '(aset (js-obj) "a" 2)))) (catch Exception _)) (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))) - (let [ws (atom [])] + (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv @@ -777,7 +777,15 @@ '(let [^objects arr (into-array [1 2 3])] (aget arr 0))))) (catch Exception _)) - (is (empty? @ws))))) + (is (empty? @ws))) + (let [ws (atom [])] + (try + (a/with-warning-handlers [(collecting-warning-handler ws)] + (e/with-compiler-env test-cenv + (a/analyze (a/empty-env) + '(and true (or (aget (js-obj "foo" 1) "foo") 2))))) + (catch Exception _)) + (is (= 1 (count @ws)))))) (deftest test-cljs-2037 (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] From 2d08f2e999fbf4b524d4c248ef39b46d9a1b6432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 13 Jul 2017 22:42:59 -0700 Subject: [PATCH 2557/4033] CLJS-2232: Self-host: Add support for string-based requires --- src/main/cljs/cljs/js.cljs | 45 ++++++++++++++++++---- src/main/clojure/cljs/analyzer.cljc | 12 ++++-- src/test/self/self_host/test.cljs | 60 +++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index f334bbe31..dacf6362b 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -575,6 +575,15 @@ (check-uses-and-load-macros {:value nil} ast))) (cb {:value ast})))) +(defn- node-side-effects + [bound-vars sb deps ns-name] + (doseq [dep deps] + (.append sb + (with-out-str + (comp/emitln (munge ns-name) "." + (ana/munge-node-lib dep) + " = require('" dep "');"))))) + (defn- analyze-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) @@ -716,14 +725,22 @@ (str "Could not eval " form) cause))))] (if (:error res) (cb res) - (let [ast (:value res)] + (let [ast (:value res) + [node-deps ast] (if (keyword-identical? (:target opts) :nodejs) + (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? (:deps ast))] + [node-libs (assoc ast :deps libs-to-load)]) + [nil ast])] (if (#{:ns :ns*} (:op ast)) (ns-side-effects true bound-vars aenv ast opts (fn [res] (if (:error res) (cb res) - (let [src (str "goog.provide(\"" (comp/munge (:name ast)) "\")")] - (cb {:value (*eval-fn* {:source src})}))))) + (let [sb (StringBuffer.)] + (.append sb + (with-out-str (comp/emitln (str "goog.provide(\"" (comp/munge (:name ast)) "\");")))) + (when-not (nil? node-deps) + (node-side-effects bound-vars sb node-deps (:name ast))) + (cb {:value (*eval-fn* {:source (.toString sb)})}))))) (let [src (with-out-str (comp/emit ast))] (cb {:value (*eval-fn* {:source src})}))))))))) @@ -822,14 +839,21 @@ (str "Could not compile " name) cause))))] (if (:error res) (cb res) - (let [ast (:value res)] + (let [ast (:value res) + [node-deps ast] (if (keyword-identical? (:target opts) :nodejs) + (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? (:deps ast))] + [node-libs (assoc ast :deps libs-to-load)]) + [nil ast])] (.append sb (with-out-str (comp/emit ast))) (if (#{:ns :ns*} (:op ast)) (ns-side-effects bound-vars aenv ast opts (fn [res] (if (:error res) (cb res) - (compile-loop (:name ast))))) + (do + (when-not (nil? node-deps) + (node-side-effects bound-vars sb node-deps (:name ast))) + (compile-loop (:name ast)))))) (recur ns))))) (do (when (:source-map opts) @@ -944,7 +968,11 @@ (if (:error res) (cb res) (let [ast (:value res) - ns' ana/*cljs-ns*] + ns' ana/*cljs-ns* + [node-deps ast] (if (keyword-identical? (:target opts) :nodejs) + (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? (:deps ast))] + [node-libs (assoc ast :deps libs-to-load)]) + [nil ast])] (if (#{:ns :ns*} (:op ast)) (do (.append sb @@ -953,7 +981,10 @@ (fn [res] (if (:error res) (cb res) - (compile-loop ns'))))) + (do + (when-not (nil? node-deps) + (node-side-effects bound-vars sb node-deps (:name ast))) + (compile-loop ns')))))) (do (.append sb (with-out-str (comp/emit ast))) (recur ns')))))) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 42dafcc1f..6c1e966f4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -744,10 +744,16 @@ (str module))) (defn node-module-dep? + #?(:cljs {:tag boolean}) [module] - (contains? - (get-in @env/*compiler* [:node-module-index]) - (str module))) + #?(:clj (contains? + (get-in @env/*compiler* [:node-module-index]) + (str module)) + :cljs (try + (and (= *target* "nodejs") + (boolean (js/require.resolve (str module)))) + (catch :default _ + false)))) (defn dep-has-global-exports? [module] diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 6f4e4ae48..fb4b0f1c7 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -916,6 +916,66 @@ (is (every? symbol? (keys (get-in @st [:cljs.analyzer/namespaces])))) (inc! l)))))) +(deftest test-string-requires-cljs-2232 + (async done + (let [st (cljs/empty-state) + l (latch 4 done)] + (cljs/compile-str + (atom @st) + "(ns foo.core (:require [path]))" + nil + {:context :expr + :target :nodejs + :eval node-eval} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (is (some? (re-find #"foo\.core\.node\$module\$path = require\('path'\);" value))) + (inc! l))) + (cljs/eval-str + (atom @st) + "(ns foo.core (:require [path])) (path/basename \"/foo/bar\")" + nil + {:context :expr + :target :nodejs + :eval node-eval} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (is (= value "bar")) + (inc! l))) + (cljs/analyze-str + (atom @st) + "(ns foo.core (:require [path]))" + nil + {:context :expr + :target :nodejs + :load (fn [_ cb] + (cb {:lang :js + :source ""}))} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (is (= (:deps value) '[path])) + (inc! l))) + (let [st (cljs/empty-state)] + (cljs/eval + st + '(ns foo.core (:require [path])) + {:context :expr + :target :nodejs + :eval node-eval} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (cljs/eval + st + '(path/basename "/foo/bar") + {:context :expr + :ns 'foo.core + :target :nodejs + :eval node-eval} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= value "bar")) + (inc! l))))))))) + (defn -main [& args] (run-tests)) From d02265d9459997cd23acdfd4a543726534a1ccc3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Jul 2017 07:05:31 -0400 Subject: [PATCH 2558/4033] tweak namespace not found warning to account for recent module support changes --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6c1e966f4..880164792 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -305,7 +305,7 @@ (str "No such namespace: " ns-sym ", could not locate " (ns->relpath ns-sym :cljs) ", " (ns->relpath ns-sym :cljc) - ", or Closure namespace \"" js-provide "\"")) + ", or JavaScript providing \"" js-provide "\"")) (defmethod error-message :undeclared-macros-ns [warning-type {:keys [ns-sym js-provide] :as info}] From 4e85b044902b5a32a0dcc6e1e5f4ff22bbaf0505 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Jul 2017 08:19:06 -0400 Subject: [PATCH 2559/4033] first pass at refactoring resolve-var post module enhancements remove code duplication & move decisions based on module types out --- src/main/clojure/cljs/analyzer.cljc | 129 +++++++++++++--------------- 1 file changed, 58 insertions(+), 71 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 880164792..a5b504ffa 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -941,6 +941,59 @@ [(checked-arrays) sym]) sym)) +(defn ns->module-type [ns] + (cond + (js-module-exists? ns) :js + (node-module-dep? ns) :node + (dep-has-global-exports? ns) :global)) + +(defmulti resolve* (fn [sym full-ns current-ns] (ns->module-type full-ns))) + +(defmethod resolve* :node + [sym full-ns current-ns] + {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) + :ns current-ns}) + +(defmethod resolve* :global + [sym full-ns current-ns] + {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." (name sym))) + :ns current-ns}) + +(defmethod resolve* :default + [sym full-ns current-ns] + (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym))) + {:name (symbol (str full-ns) (str (name sym))) + :ns full-ns})) + +(defn required? [ns env] + (or (contains? (set (vals (gets env :ns :requires))) ns) + (contains? (set (vals (gets env :ns :uses))) ns))) + +(defn invokeable-ns? + "Returns true if ns is a required namespace and a JavaScript module that + might be invokeable as a function." + [ns env] + (let [ns (resolve-ns-alias env ns)] + (and (or (js-module-exists? ns) + (node-module-dep? ns) + (dep-has-global-exports? ns)) + (required? ns env)))) + +(defn resolve-invokeable-ns [ns current-ns env] + (let [ns (resolve-ns-alias env ns) + module-type (ns->module-type ns)] + (case module-type + :js {:name (symbol + (or (gets @env/*compiler* :js-module-index ns) + (resolve-ns-alias env ns))) + :ns 'js} + :node {:name (symbol (str current-ns) + (munge-node-lib (resolve-ns-alias env ns))) + :ns current-ns} + :global {:name (symbol (str current-ns) + (munge-global-export (resolve-ns-alias env ns))) + :ns current-ns}))) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -983,19 +1036,7 @@ (when (not= current-ns full-ns) (confirm-ns env full-ns)) (confirm env full-ns (symbol (name sym)))) - (cond - (node-module-dep? full-ns) - {:name (symbol (str current-ns) - (str (munge-node-lib full-ns) "." (name sym))) - :ns current-ns} - (dep-has-global-exports? full-ns) - {:name (symbol (str current-ns) - (str (munge-global-export full-ns) "." (name sym))) - :ns current-ns} - :else - (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym))) - {:name (symbol (str full-ns) (str (name sym))) - :ns full-ns}))) + (resolve* sym full-ns current-ns)) (dotted-symbol? sym) (let [idx (.indexOf s ".") @@ -1005,8 +1046,6 @@ {:name (symbol (str (:name lb)) suffix)} (if-some [full-ns (gets @env/*compiler* ::namespaces current-ns :imports prefix)] {:name (symbol (str full-ns) suffix)} - - ;else (if-some [info (gets @env/*compiler* ::namespaces current-ns :defs prefix)] (merge info {:name (symbol (str current-ns) (str sym)) @@ -1017,39 +1056,13 @@ (some? (gets @env/*compiler* ::namespaces current-ns :uses sym)) (let [full-ns (gets @env/*compiler* ::namespaces current-ns :uses sym)] - (cond - (node-module-dep? full-ns) - {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." sym)) - :ns current-ns} - - (dep-has-global-exports? full-ns) - {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." sym)) - :ns current-ns} - - :else - (merge - (gets @env/*compiler* ::namespaces full-ns :defs sym) - {:name (symbol (str full-ns) (str sym)) - :ns full-ns}))) + (resolve* sym full-ns current-ns)) (some? (gets @env/*compiler* ::namespaces current-ns :renames sym)) (let [qualified-symbol (gets @env/*compiler* ::namespaces current-ns :renames sym) full-ns (symbol (namespace qualified-symbol)) sym (symbol (name qualified-symbol))] - (cond - (node-module-dep? full-ns) - {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." sym)) - :ns current-ns} - - (dep-has-global-exports? full-ns) - {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." sym)) - :ns current-ns} - - :else - (merge - (gets @env/*compiler* ::namespaces full-ns :defs sym) - {:name qualified-symbol - :ns full-ns}))) + (resolve* sym full-ns current-ns)) (some? (gets @env/*compiler* ::namespaces current-ns :imports sym)) (recur env (gets @env/*compiler* ::namespaces current-ns :imports sym) confirm) @@ -1070,34 +1083,8 @@ {:name (symbol "cljs.core" (str sym)) :ns 'cljs.core})) - ;; The following three cases support for invoking JS modules that export themselves as function -David - (and (or (js-module-exists? s) - (js-module-exists? (resolve-ns-alias env s))) - (let [full-ns (resolve-ns-alias env s)] - (or (contains? (set (vals (gets env :ns :requires))) full-ns) - (contains? (set (vals (gets env :ns :uses))) full-ns)))) - (let [module (or (gets @env/*compiler* :js-module-index s) - (resolve-ns-alias env s))] - {:name (symbol module) - :ns 'js}) - - (and (or (node-module-dep? s) - (node-module-dep? (resolve-ns-alias env s))) - (let [full-ns (resolve-ns-alias env s)] - (or (contains? (set (vals (gets env :ns :requires))) full-ns) - (contains? (set (vals (gets env :ns :uses))) full-ns)))) - (let [module (resolve-ns-alias env s)] - {:name (symbol (str current-ns) (munge-node-lib module)) - :ns current-ns}) - - (and (or (dep-has-global-exports? s) - (dep-has-global-exports? (resolve-ns-alias env s))) - (let [full-ns (resolve-ns-alias env s)] - (or (contains? (set (vals (gets env :ns :requires))) full-ns) - (contains? (set (vals (gets env :ns :uses))) full-ns)))) - (let [module (resolve-ns-alias env s)] - {:name (symbol (str current-ns) (munge-global-export module)) - :ns current-ns}) + (invokeable-ns? s env) + (resolve-invokeable-ns s current-ns env) :else (do From 41fa44e40c29055079b3547ff059aaa1e1dff831 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Jul 2017 09:50:51 -0400 Subject: [PATCH 2560/4033] change handle-js-modules to take only-required? flag, REPLs will want to start with node_modules indexed and will provide no js-sources --- src/main/clojure/cljs/closure.clj | 55 ++++++++++++++++--------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2085a218a..409c1753c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2386,33 +2386,36 @@ "Given all Cljs sources (build inputs and dependencies in classpath) - index all the node node modules - - process the JS modules (preprocess + convert to Closure JS)o + - process the JS modules (preprocess + convert to Closure JS) - save js-dependency-index for compilation" - [{:keys [npm-deps target] :as opts} js-sources compiler-env] - (let [;; Find all the top-level Node packages and their files - top-level-modules (reduce (fn [acc m] - (reduce (fn [acc p] - (assoc acc p m)) - acc - (:provides m))) - {} - (index-node-modules-dir)) - requires (set (mapcat deps/-requires js-sources)) - ;; Select Node files that are required by Cljs code, - ;; and create list of all their dependencies - required-node-modules (set/intersection (set (keys top-level-modules)) requires)] - (if-not (= target :nodejs) - (let [opts (-> opts - (update :foreign-libs - (fn [libs] - (into (index-node-modules required-node-modules) - (expand-libs libs)))) - process-js-modules)] - (swap! compiler-env assoc :js-dependency-index (deps/js-dependency-index opts)) - opts) - (do - (swap! compiler-env update-in [:node-module-index] (fnil into #{}) (map str required-node-modules)) - opts)))) + ([opts js-sources compiler-env] + (handle-js-modules opts js-sources compiler-env true)) + ([{:keys [npm-deps target] :as opts} js-sources compiler-env only-required?] + (let [;; Find all the top-level Node packages and their files + top-level (reduce + (fn [acc m] + (reduce (fn [acc p] (assoc acc p m)) acc (:provides m))) + {} (index-node-modules-dir)) + requires (set (mapcat deps/-requires js-sources)) + ;; Select Node files that are required by Cljs code, + ;; and create list of all their dependencies + node-required (if only-required? + (set/intersection (set (keys top-level)) requires) + (keys top-level))] + (if-not (= target :nodejs) + (let [opts (-> opts + (update :foreign-libs + (fn [libs] + (into (index-node-modules node-required) + (expand-libs libs)))) + process-js-modules)] + (swap! compiler-env assoc :js-dependency-index + (deps/js-dependency-index opts)) + opts) + (do + (swap! compiler-env update-in [:node-module-index] + (fnil into #{}) (map str node-required)) + opts))))) (defn build "Given a source which can be compiled, produce runnable JavaScript." From 2ba8ec76ec3977143afc5137e22c47838ac9d5a7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Jul 2017 11:11:24 -0400 Subject: [PATCH 2561/4033] remove old module processing stuff, install node deps & index all top-level modules --- src/main/clojure/cljs/repl.cljc | 45 ++++++++++++++------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 4182aa519..98eca7e95 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -193,8 +193,8 @@ (merge (env->opts repl-env) opts) {:requires [(name ns)] :type :seed - :url (:uri (cljsc/source-for-namespace - ns env/*compiler*))})) + :url (:uri (cljsc/source-for-namespace ns env/*compiler*))})) + opts' (cljsc/handle-js-modules opts sources env/*compiler* false) deps (->> sources (remove (comp #{["goog"]} :provides)) (remove (comp #{:seed} :type)) @@ -828,13 +828,6 @@ (catch Throwable e (caught e repl-env opts) opts)))) - ;; TODO: consider alternative ways to deal with JS module processing at REPL - opts' opts ;; need to save opts prior to JS module processing for watch - opts (if (or (:libs opts) (:foreign-libs opts)) - (let [opts (cljsc/process-js-modules opts)] - (swap! env/*compiler* assoc :js-dependency-index (deps/js-dependency-index opts)) - opts) - opts) init (do (evaluate-form repl-env env "" `(~'set! ~'cljs.core/*print-namespace-maps* true) @@ -865,6 +858,8 @@ (print nil)) (let [value (eval repl-env env input opts)] (print value))))))] + (cljsc/maybe-install-node-deps! opts) + (cljsc/handle-js-modules opts '() env/*compiler* false) (comp/with-core-cljs opts (fn [] (binding [*repl-opts* opts] @@ -876,23 +871,21 @@ (init) (catch Throwable e (caught e repl-env opts))) - ;; TODO: consider alternative ways to deal with JS module processing at REPL - (let [opts opts'] ;; use opts prior to JS module processing - (when-let [src (:watch opts)] - (.start - (Thread. - ((ns-resolve 'clojure.core 'binding-conveyor-fn) - (fn [] - (let [log-file (io/file (util/output-directory opts) "watch.log")] - (err-out (println "Watch compilation log available at:" (str log-file))) - (try - (let [log-out (FileWriter. log-file)] - (binding [*err* log-out - *out* log-out] - (cljsc/watch src (dissoc opts :watch) - env/*compiler* done?))) - (catch Throwable e - (caught e repl-env opts)))))))))) + (when-let [src (:watch opts)] + (.start + (Thread. + ((ns-resolve 'clojure.core 'binding-conveyor-fn) + (fn [] + (let [log-file (io/file (util/output-directory opts) "watch.log")] + (err-out (println "Watch compilation log available at:" (str log-file))) + (try + (let [log-out (FileWriter. log-file)] + (binding [*err* log-out + *out* log-out] + (cljsc/watch src (dissoc opts :watch) + env/*compiler* done?))) + (catch Throwable e + (caught e repl-env opts))))))))) ;; let any setup async messages flush (Thread/sleep 50) (binding [*in* (if (true? (:source-map-inline opts)) From efc7efa67d0bbbdd565a5dd63c973d256712c32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 14 Jul 2017 11:45:10 -0700 Subject: [PATCH 2562/4033] CLJS-2240: don't shell out to module_deps.js if `:npm-deps` not specified --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 409c1753c..4fde000ac 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2406,7 +2406,9 @@ (let [opts (-> opts (update :foreign-libs (fn [libs] - (into (index-node-modules node-required) + (into (if-not (empty? npm-deps) + (index-node-modules node-required) + []) (expand-libs libs)))) process-js-modules)] (swap! compiler-env assoc :js-dependency-index From b17b83121486d6e8ffe9887208cdf105993b73cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 14 Jul 2017 11:56:17 -0700 Subject: [PATCH 2563/4033] CLJS-2238: Perf regression with node module indexing --- src/main/clojure/cljs/closure.clj | 76 +++++++++++++++++-------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4fde000ac..9d207b5f3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2206,47 +2206,53 @@ (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)) [])))) +(defn- node-file-seq->libs-spec* + [module-fseq] + (letfn [(package-json? [path] + (boolean (re-find #"node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$" path)))] + (let [pkg-jsons (into {} + (comp + (map #(.getAbsolutePath %)) + (filter package-json?) + (map (fn [path] + [path (json/read-str (slurp path))]))) + module-fseq)] + (into [] + (comp + (map #(.getAbsolutePath %)) + (map (fn [path] + (merge + {:file path + :module-type :commonjs} + (when-not (package-json? path) + (let [pkg-json-main (some + (fn [[pkg-json-path {:strs [main name]}]] + (when-not (nil? main) + (let [main-path (str (string/replace pkg-json-path #"package\.json$" "") + main)] + (when (= main-path path) + name)))) + pkg-jsons)] + {:provides (if (some? pkg-json-main) + [pkg-json-main] + (let [module-rel-name (string/replace + (subs path (.lastIndexOf path "node_modules")) + #"node_modules[\\\/]" "")] + (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")] + (boolean (re-find #"[\\\/]index\.js(on)?$" module-rel-name)) + (conj (string/replace module-rel-name #"[\\\/]index\.js(on)?$" "")))))})))))) + module-fseq)))) + +(def node-file-seq->libs-spec (memoize node-file-seq->libs-spec*)) + (defn index-node-modules-dir ([] (index-node-modules-dir (when env/*compiler* (:options @env/*compiler*)))) ([{:keys [verbose target]}] - (letfn [(package-json? [path] - (boolean (re-find #"node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$" path)))] - (let [module-fseq (util/module-file-seq) - pkg-jsons (into {} - (comp - (map #(.getAbsolutePath %)) - (filter package-json?) - (map (fn [path] - [path (json/read-str (slurp path))]))) - module-fseq)] - (into [] - (comp - (map #(.getAbsolutePath %)) - (map (fn [path] - (merge - {:file path - :module-type :commonjs} - (when-not (package-json? path) - (let [pkg-json-main (some - (fn [[pkg-json-path {:strs [main name]}]] - (when-not (nil? main) - (let [main-path (str (string/replace pkg-json-path #"package\.json$" "") - main)] - (when (= main-path path) - name)))) - pkg-jsons)] - {:provides (if (some? pkg-json-main) - [pkg-json-main] - (let [module-rel-name (string/replace - (subs path (.lastIndexOf path "node_modules")) - #"node_modules[\\\/]" "")] - (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")] - (boolean (re-find #"[\\\/]index\.js(on)?$" module-rel-name)) - (conj (string/replace module-rel-name #"[\\\/]index\.js(on)?$" "")))))})))))) - module-fseq))))) + (let [module-fseq (util/module-file-seq)] + (node-file-seq->libs-spec module-fseq)))) (defn preprocess-js "Given js-module map, apply preprocessing defined by :preprocess value in the map." From 1e6706007ed34420207a85ded0aae41e2269a31d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Jul 2017 15:35:45 -0400 Subject: [PATCH 2564/4033] remove provided check from cljs.closure/add-dependencies, seems vestigial, was added in 2014 before deterministic build order etc. ignore :seed inputs. change REPL, now that we ignore :seed inputs no need to fabricate a :url. Process JS modules. --- src/main/clojure/cljs/closure.clj | 21 +++++++++++---------- src/main/clojure/cljs/repl.cljc | 6 ++---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9d207b5f3..4bd929316 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -811,6 +811,8 @@ (let [url (deps/to-url (constants-filename opts))] (javascript-file nil url [(str ana/constants-ns-sym)] ["cljs.core"]))) +;; Internally only REPLs use this. We do expose it in cljs.build.api - David + (defn add-dependencies "Given one or more IJavaScript objects in dependency order, produce a new sequence of IJavaScript objects which includes the input list @@ -820,21 +822,20 @@ requires (set (mapcat deps/-requires inputs)) required-cljs (clojure.set/difference (cljs-dependencies opts requires) inputs) required-js (js-dependencies opts - (into (set (mapcat deps/-requires required-cljs)) requires)) - provided (set (mapcat deps/-provides (clojure.set/union inputs required-cljs required-js))) - unprovided (clojure.set/difference requires provided)] - (when (seq unprovided) - (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)})) + (into (set (mapcat deps/-requires required-cljs)) requires))] (cons (javascript-file nil (io/resource "goog/base.js") ["goog"] nil) (deps/dependency-order (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))) + (fn [{:keys [type foreign url file provides requires] :as js-map}] + ;; ignore :seed inputs, only for REPL - David + (if (not= :seed type) + (let [url (or url (io/resource file))] + (merge + (javascript-file foreign url provides requires) + js-map)) + js-map)) required-js) (when (-> @env/*compiler* :options :emit-constants) [(constants-javascript-file opts)]) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 98eca7e95..6da8e07d0 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -191,14 +191,12 @@ sources (binding [ana/*analyze-deps* false] (cljsc/add-dependencies (merge (env->opts repl-env) opts) - {:requires [(name ns)] - :type :seed - :url (:uri (cljsc/source-for-namespace ns env/*compiler*))})) - opts' (cljsc/handle-js-modules opts sources env/*compiler* false) + {:requires [(name ns)] :type :seed})) deps (->> sources (remove (comp #{["goog"]} :provides)) (remove (comp #{:seed} :type)) (map #(select-keys % [:provides :url])))] + (cljsc/handle-js-modules opts sources env/*compiler* false) (if (:output-dir opts) ;; REPLs that read from :output-dir just need to add deps, ;; environment will handle actual loading - David From ac533eeef5e4b06413f127f138fbfaa97e9e5422 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Jul 2017 16:10:42 -0400 Subject: [PATCH 2565/4033] create package.json for user if it doesn't exist --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4bd929316..cafb9c58d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2115,9 +2115,11 @@ [{:keys [npm-deps verbose] :as opts}] (let [npm-deps (merge npm-deps (compute-upstream-npm-deps opts))] (if-not (empty? npm-deps) - (do + (let [pkg-json (io/file "package.json")] (when (or ana/*verbose* verbose) (util/debug-prn "Installing Node.js dependencies")) + (when-not (.exists pkg-json) + (spit pkg-json "{}")) (let [proc (-> (ProcessBuilder. (into (cond->> ["npm" "install" "module-deps" "resolve" "browser-resolve"] util/windows? (into ["cmd" "/c"])) From dea414197fb3a5d11275f4314b43d21660c31735 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Jul 2017 16:54:27 -0400 Subject: [PATCH 2566/4033] fix cljs.analyzer/gen-user-ns to produce sensible value when given forms instead of failing --- src/main/clojure/cljs/analyzer.cljc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a5b504ffa..288125a97 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3657,13 +3657,15 @@ (forms-seq*)))))) #?(:clj - (defn gen-user-ns [src] - (let [full-name (str src) - name (.substring full-name - (inc (.lastIndexOf full-name "/")) - (.lastIndexOf full-name "."))] - (symbol - (str "cljs.user." name (util/content-sha full-name 7)))))) + (defn gen-user-ns + [src] + (if (sequential? src) + (symbol (str "cljs.user.source$form$" (util/content-sha (pr-str src) 7))) + (let [full-name (str src) + name (.substring full-name + (inc (.lastIndexOf full-name "/")) + (.lastIndexOf full-name "."))] + (symbol (str "cljs.user." name (util/content-sha full-name 7))))))) #?(:clj (defn parse-ns From 31ae4dd2adf8ad5f84206d3cd2b0997d9e821a94 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Jul 2017 17:08:50 -0400 Subject: [PATCH 2567/4033] more informative error for cljs.analyzer/foreign-dep? --- src/main/clojure/cljs/analyzer.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 288125a97..42a9bd994 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2074,7 +2074,8 @@ (defn foreign-dep? #?(:cljs {:tag boolean}) [dep] - {:pre [(symbol? dep)]} + (assert (symbol? dep) + (str "cljs.analyzer/foreign-dep? expected symbol got " dep)) (let [js-index (:js-dependency-index @env/*compiler*)] (if-some [[_ {:keys [foreign]}] (find js-index (name dep))] foreign From 66bfe92807b55aac05ced6a550b4f0b1b5bc1de4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Jul 2017 17:09:21 -0400 Subject: [PATCH 2568/4033] fix error string for last commit --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 42a9bd994..92319272a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2075,7 +2075,7 @@ #?(:cljs {:tag boolean}) [dep] (assert (symbol? dep) - (str "cljs.analyzer/foreign-dep? expected symbol got " dep)) + (str "cljs.analyzer/foreign-dep? expected symbol got " (pr-str dep))) (let [js-index (:js-dependency-index @env/*compiler*)] (if-some [[_ {:keys [foreign]}] (find js-index (name dep))] foreign From 914b9b1a3be3921b666d3e40709bc2a4c8b9d849 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Jul 2017 17:58:06 -0400 Subject: [PATCH 2569/4033] add a way to suppress alias dupe checking --- src/main/clojure/cljs/analyzer.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 92319272a..0d5d47822 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -49,6 +49,7 @@ (def ^:dynamic *cljs-ns* 'cljs.user) (def ^:dynamic *cljs-file* nil) (def ^:dynamic *checked-arrays* false) +(def ^:dynamic *check-alias-dupes* true) (def ^:dynamic *cljs-static-fns* false) (def ^:dynamic *fn-invoke-direct* false) (def ^:dynamic *cljs-macros-path* "/cljs/core") @@ -2733,7 +2734,8 @@ (let [merge-keys [:use-macros :require-macros :rename-macros :uses :requires :renames :imports]] - (check-duplicate-aliases env ns-info' require-info) + (when *check-alias-dupes* + (check-duplicate-aliases env ns-info' require-info)) (merge ns-info' {:excludes excludes} From 1d16bc50a33a6e26bebf20b4cac14a2ede373421 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Jul 2017 18:04:19 -0400 Subject: [PATCH 2570/4033] CLJS-2229: Ensure that new modules work works correctly with REPLs 1. when processing ns forms we need to check if we need to process js modules. This potentially requires two passes in the second pass suppresss dupe alias check which would otherwise get fired due to rewriting the alias to the generated Closure module name. 2. We need to apply a simpler strategy to transitive dependencies. When we've determined all the sources, call handle-js-modules deps on these. --- src/main/clojure/cljs/repl.cljc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 6da8e07d0..728b338af 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -196,7 +196,7 @@ (remove (comp #{["goog"]} :provides)) (remove (comp #{:seed} :type)) (map #(select-keys % [:provides :url])))] - (cljsc/handle-js-modules opts sources env/*compiler* false) + (cljsc/handle-js-modules opts sources env/*compiler*) (if (:output-dir opts) ;; REPLs that read from :output-dir just need to add deps, ;; environment will handle actual loading - David @@ -454,9 +454,17 @@ :source-form form} :repl-env repl-env}) def-emits-var (:def-emits-var opts) - ast (binding [ana/*analyze-deps* false] - (ana/analyze (assoc env :def-emits-var def-emits-var) - (wrap form) nil opts)) + ->ast (fn [form] + (binding [ana/*analyze-deps* false] + (ana/analyze (assoc env :def-emits-var def-emits-var) + (wrap form) nil opts))) + ast (->ast form) + ast (if-not (#{:ns :ns*} (:op ast)) + ast + (let [ijs (ana/parse-ns [form])] ;; if ns form need to check for js modules - David + (cljsc/handle-js-modules opts [ijs] env/*compiler*) + (binding [ana/*check-alias-dupes* false] + (ana/no-warn (->ast form))))) wrap-js ;; TODO: check opts as well - David (if (:source-map repl-env) @@ -857,7 +865,6 @@ (let [value (eval repl-env env input opts)] (print value))))))] (cljsc/maybe-install-node-deps! opts) - (cljsc/handle-js-modules opts '() env/*compiler* false) (comp/with-core-cljs opts (fn [] (binding [*repl-opts* opts] From 3cf960bb161d8c5fd75b046a4a119d9d0f645409 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 14 Jul 2017 18:31:45 -0400 Subject: [PATCH 2571/4033] CLJS-2241: Multiple requires of Node.js modules in non :nodejs target are not idempotent at the REPL leave a comment about module subtlely, drop unneeded analyze --- src/main/clojure/cljs/repl.cljc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 728b338af..9a5b7ceda 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -464,7 +464,7 @@ (let [ijs (ana/parse-ns [form])] ;; if ns form need to check for js modules - David (cljsc/handle-js-modules opts [ijs] env/*compiler*) (binding [ana/*check-alias-dupes* false] - (ana/no-warn (->ast form))))) + (ana/no-warn (->ast form))))) ;; need new AST after we know what the modules are - David wrap-js ;; TODO: check opts as well - David (if (:source-map repl-env) @@ -493,11 +493,9 @@ ;; NOTE: means macros which expand to ns aren't supported for now ;; when eval'ing individual forms at the REPL - David (when (#{:ns :ns*} (:op ast)) - (let [ast (ana/no-warn (ana/analyze env form nil opts))] - (load-dependencies repl-env - (into (vals (:requires ast)) - (distinct (vals (:uses ast)))) - opts))) + (load-dependencies repl-env + (into (vals (:requires ast)) (distinct (vals (:uses ast)))) + opts)) (when *cljs-verbose* (err-out (println wrap-js))) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] From a29e19ddbdb35bb6bb6473ce93bedac3e487ef66 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 09:12:07 -0400 Subject: [PATCH 2572/4033] CLJS-2242: Lots of undeclared Var warns in cljs.spec.gen.alpha Revert last commit. We must analyze deps before loading requires. Instead update c.a/analyze-deps to check js-module-exists? --- src/main/clojure/cljs/analyzer.cljc | 1 + src/main/clojure/cljs/repl.cljc | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 0d5d47822..6f3b2271f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2103,6 +2103,7 @@ (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) (contains? (:node-module-index compiler) (name dep)) + (js-module-exists? (name dep)) #?(:clj (deps/find-classpath-lib dep))) #?(:clj (if-some [src (locate-src dep)] (analyze-file src opts) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 9a5b7ceda..78bc4f30b 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -493,9 +493,11 @@ ;; NOTE: means macros which expand to ns aren't supported for now ;; when eval'ing individual forms at the REPL - David (when (#{:ns :ns*} (:op ast)) - (load-dependencies repl-env - (into (vals (:requires ast)) (distinct (vals (:uses ast)))) - opts)) + (let [ast (ana/no-warn (ana/analyze env form nil opts))] + (load-dependencies repl-env + (into (vals (:requires ast)) + (distinct (vals (:uses ast)))) + opts))) (when *cljs-verbose* (err-out (println wrap-js))) (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)] From ede37b51b4ea8465d644b6c6f5e359c6a1f643a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 14 Jul 2017 22:22:46 -0700 Subject: [PATCH 2573/4033] CLJS-2243: Self-host: Add support for :global-exports also cleanup some variable accesses in `cljs.compiler/load-libs` --- src/main/cljs/cljs/js.cljs | 35 ++++++++++++--- src/main/clojure/cljs/compiler.cljc | 16 +++---- src/test/self/self_host/test.cljs | 67 +++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index dacf6362b..1b819b93c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -584,6 +584,17 @@ (ana/munge-node-lib dep) " = require('" dep "');"))))) +(defn- global-exports-side-effects + [bound-vars sb deps ns-name] + (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/emitln (munge ns-name) "." + (ana/munge-global-export dep) + " = goog.global." (get global-exports (symbol dep)) ";"))))))) + (defn- analyze-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) eof (js-obj) @@ -735,11 +746,15 @@ (fn [res] (if (:error res) (cb res) - (let [sb (StringBuffer.)] + (let [ns-name (:name ast) + sb (StringBuffer.)] (.append sb - (with-out-str (comp/emitln (str "goog.provide(\"" (comp/munge (:name ast)) "\");")))) + (with-out-str (comp/emitln (str "goog.provide(\"" (comp/munge ns-name) "\");")))) (when-not (nil? node-deps) - (node-side-effects bound-vars sb node-deps (:name ast))) + (node-side-effects bound-vars sb node-deps ns-name)) + (global-exports-side-effects bound-vars sb + (filter ana/dep-has-global-exports? (:deps ast)) + ns-name) (cb {:value (*eval-fn* {:source (.toString sb)})}))))) (let [src (with-out-str (comp/emit ast))] (cb {:value (*eval-fn* {:source src})}))))))))) @@ -850,9 +865,12 @@ (fn [res] (if (:error res) (cb res) - (do + (let [ns-name (:name ast)] (when-not (nil? node-deps) - (node-side-effects bound-vars sb node-deps (:name ast))) + (node-side-effects bound-vars sb node-deps ns-name)) + (global-exports-side-effects bound-vars sb + (filter ana/dep-has-global-exports? (:deps ast)) + ns-name) (compile-loop (:name ast)))))) (recur ns))))) (do @@ -981,9 +999,12 @@ (fn [res] (if (:error res) (cb res) - (do + (let [ns-name (:name ast)] (when-not (nil? node-deps) - (node-side-effects bound-vars sb node-deps (:name ast))) + (node-side-effects bound-vars sb node-deps ns-name)) + (global-exports-side-effects bound-vars sb + (filter ana/dep-has-global-exports? (:deps ast)) + ns-name) (compile-loop ns')))))) (do (.append sb (with-out-str (comp/emit ast))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 725d5d091..dd5be7942 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1062,7 +1062,8 @@ (defn load-libs [libs seen reloads deps ns-name] - (let [{:keys [target optimizations]} (get @env/*compiler* :options) + (let [{:keys [options js-dependency-index]} @env/*compiler* + {:keys [target optimizations]} options loaded-libs (munge 'cljs.core.*loaded-libs*) loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*)) [node-libs libs-to-load] (let [libs (remove (set (vals seen)) (filter (set (vals libs)) deps))] @@ -1070,7 +1071,7 @@ (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? libs)] [node-libs libs-to-load]) [nil libs])) - {global-exports-libs true, libs-to-load false} (group-by ana/dep-has-global-exports? libs-to-load)] + {global-exports-libs true libs-to-load false} (group-by ana/dep-has-global-exports? libs-to-load)] (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) @@ -1082,8 +1083,7 @@ (when (= :none optimizations) (if (= :nodejs target) ;; under node.js we load foreign libs globally - (let [{:keys [js-dependency-index options]} @env/*compiler* - ijs (get js-dependency-index (name lib))] + (let [ijs (get js-dependency-index (name lib))] (emitln "cljs.core.load_file(\"" (str (io/file (util/output-directory options) (or (deps/-relative-path ijs) (util/relative-name (:url ijs))))) @@ -1091,8 +1091,7 @@ (emitln "goog.require('" (munge lib) "');")))] :cljs [(and (ana/foreign-dep? lib) - (when-let [{:keys [optimizations]} (get @env/*compiler* :options)] - (not (keyword-identical? optimizations :none)))) + (not (keyword-identical? optimizations :none))) nil]) (or (-> libs meta :reload) @@ -1110,11 +1109,10 @@ (ana/munge-node-lib lib) " = require('" lib "');")) (doseq [lib global-exports-libs] - (let [{:keys [js-dependency-index options]} @env/*compiler* - ijs (get js-dependency-index (name lib))] + (let [{:keys [global-exports]} (get js-dependency-index (name lib))] (emitln (munge ns-name) "." (ana/munge-global-export lib) - " = goog.global." (get (:global-exports ijs) (symbol lib)) ";"))) + " = goog.global." (get global-exports (symbol lib)) ";"))) (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index fb4b0f1c7..ec25ab313 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -976,6 +976,73 @@ (is (= value "bar")) (inc! l))))))))) +(deftest test-global-exports-cljs-2243 + (async done + (let [calculator-load (fn [_ cb] + (cb {:lang :js + :source "global.Calculator = { + add: function (a, b) { + return a + b; + }, + subtract: function (a, b) { + return a - b; + } +};"})) + st (cljs/empty-state) + l (latch 4 done)] + (swap! st assoc :js-dependency-index {"calculator" {:global-exports '{calculator Calculator}}}) + (cljs/compile-str + (atom @st) + "(ns foo.core (:require [calculator]))" + nil + {:context :expr + :load calculator-load + :eval node-eval} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (is (some? (re-find #"foo\.core\.global\$module\$calculator = goog.global.Calculator;" value))) + (inc! l))) + (cljs/eval-str + (atom @st) + "(ns foo.core (:require [calculator])) (calculator/add 1 2)" + nil + {:context :expr + :load calculator-load + :eval node-eval} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (is (= value 3)) + (inc! l))) + (cljs/analyze-str + (atom @st) + "(ns foo.core (:require [calculator]))" + nil + {:context :expr + :load calculator-load} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (is (= (:deps value) '[calculator])) + (inc! l))) + (let [st (atom @st)] + (cljs/eval + st + '(ns foo.core (:require [calculator])) + {:context :expr + :load calculator-load + :eval node-eval} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (cljs/eval + st + '(calculator/add 1 2) + {:context :expr + :ns 'foo.core + :eval node-eval} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= value 3)) + (inc! l))))))))) + (defn -main [& args] (run-tests)) From aca54adb69d2ba7c50ac2cd1bea8f1f33555b1e1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 10:13:11 -0400 Subject: [PATCH 2574/4033] tweak missing namespace message --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6f3b2271f..53b7f1670 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -306,7 +306,7 @@ (str "No such namespace: " ns-sym ", could not locate " (ns->relpath ns-sym :cljs) ", " (ns->relpath ns-sym :cljc) - ", or JavaScript providing \"" js-provide "\"")) + ", or JavaScript source providing \"" js-provide "\"")) (defmethod error-message :undeclared-macros-ns [warning-type {:keys [ns-sym js-provide] :as info}] From 1f5e7958eda07799c70047bfc21ef1976f679aaa Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 10:14:50 -0400 Subject: [PATCH 2575/4033] CLJS-2244: Orphaned processed JS modules breaks :modules Handle processed JS libs whose :lib-path is in the output directory specially when computing module uris --- src/main/clojure/cljs/module_graph.cljc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index 1834e2a5c..e5f4771b3 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -263,6 +263,23 @@ add-cljs-base add-cljs-base-dep))) ) +(defn parent? [f0 f1] + (.startsWith + (.getAbsolutePath (io/file f0)) + (.getAbsolutePath (io/file f1)))) + +;; JS modules become Closure libs that exist in the output directory. However in +;; the current indexing pipeline, these will not have an :out-file. Correct these +;; entries for module->module-uris - David + +(defn maybe-add-out-file + [{:keys [lib-path] :as ijs} {:keys [output-dir] :as opts}] + (if-not lib-path + ijs + (if (parent? lib-path output-dir) + (assoc ijs :out-file lib-path) + ijs))) + (defn modules->module-uris "Given a :modules map, a dependency sorted list of compiler inputs, and compiler options return a Closure module uris map. This map will include @@ -293,7 +310,8 @@ (fn [{:keys [out-file] :as ijs}] (if-not out-file (throw (Exception. (str "No :out-file for IJavaScript " (pr-str ijs)))) - out-file)))) + out-file)) + #(maybe-add-out-file % opts))) (distinct)) entries)])) (expand-modules modules inputs)) From ecdbdcc1285de60cf840abd28e0b920477135724 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 12:24:34 -0400 Subject: [PATCH 2576/4033] refactor handle-js-modules remove only-required? param, not used in Node.js context we still need to build :node-module-index which is just the entire Node.js top level. This is necessary in REPL contexts where compilation is always incremental. We may for example need to analyze dependencies without compiling them - but in this case we won't know have information about deps from node_modules up front (in a build this would not occur since we process files in dep order). The REPL strategy could probably be refactored to remove these issues but that is out of scope for the coming release. --- src/main/clojure/cljs/closure.clj | 59 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cafb9c58d..919bdef82 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2397,36 +2397,35 @@ - index all the node node modules - process the JS modules (preprocess + convert to Closure JS) - save js-dependency-index for compilation" - ([opts js-sources compiler-env] - (handle-js-modules opts js-sources compiler-env true)) - ([{:keys [npm-deps target] :as opts} js-sources compiler-env only-required?] - (let [;; Find all the top-level Node packages and their files - top-level (reduce - (fn [acc m] - (reduce (fn [acc p] (assoc acc p m)) acc (:provides m))) - {} (index-node-modules-dir)) - requires (set (mapcat deps/-requires js-sources)) - ;; Select Node files that are required by Cljs code, - ;; and create list of all their dependencies - node-required (if only-required? - (set/intersection (set (keys top-level)) requires) - (keys top-level))] - (if-not (= target :nodejs) - (let [opts (-> opts - (update :foreign-libs - (fn [libs] - (into (if-not (empty? npm-deps) - (index-node-modules node-required) - []) - (expand-libs libs)))) - process-js-modules)] - (swap! compiler-env assoc :js-dependency-index - (deps/js-dependency-index opts)) - opts) - (do - (swap! compiler-env update-in [:node-module-index] - (fnil into #{}) (map str node-required)) - opts))))) + [{:keys [npm-deps target] :as opts} js-sources compiler-env] + (let [;; Find all the top-level Node packages and their files + top-level (reduce + (fn [acc m] + (reduce (fn [acc p] (assoc acc p m)) acc (:provides m))) + {} (index-node-modules-dir)) + requires (set (mapcat deps/-requires js-sources)) + ;; Select Node files that are required by Cljs code, + ;; and create list of all their dependencies + node-required (set/intersection (set (keys top-level)) requires)] + (if-not (= target :nodejs) + (let [opts (-> opts + (update :foreign-libs + (fn [libs] + (into (if-not (empty? npm-deps) + (index-node-modules node-required) + []) + (expand-libs libs)))) + process-js-modules)] + (swap! compiler-env merge + ;; we need to also track the whole top level - this is to support + ;; cljs.analyze/analyze-deps, particularly in REPL contexts - David + {:js-dependency-index (deps/js-dependency-index opts) + :node-module-index (into #{} (map str (keys top-level)))}) + opts) + (do + (swap! compiler-env update-in [:node-module-index] + (fnil into #{}) (map str node-required)) + opts)))) (defn build "Given a source which can be compiled, produce runnable JavaScript." From 13126041b8020d1b5f0ebfbb67c0baaa5246e38d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 12:29:25 -0400 Subject: [PATCH 2577/4033] Fix REPL issues when processed JS modules which are transitive deps of a require failed to load. it seems it's possible for a source to have stale :provides & :requires. This doesn't seem to have been observed in builds because Closure uses the information in the compiled sources, and under :none we compute dep.js in one pass. This fixes the immediate REPL issue with processed JS modules by simply using the compiled source to determine :provides / :requires --- src/main/clojure/cljs/repl.cljc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 78bc4f30b..f7d4a0bb6 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -206,7 +206,17 @@ (map #(cljsc/source-on-disk opts %)))] (when (:repl-verbose opts) (println "Loading:" (:provides source))) - (.append sb (cljsc/add-dep-string opts source))) + ;; Need to get :requires and :provides from compiled source + ;; not from our own compilation, this issue oddly doesn't seem to + ;; affect compiled ClojureScript, should be cleaned up so we + ;; don't need this step here - David + (with-open [rdr (io/reader (:url source))] + (.append sb + (cljsc/add-dep-string opts + (merge source + (deps/parse-js-ns (line-seq rdr))))))) + (when (:repl-verbose opts) + (println (.toString sb))) (-evaluate repl-env "" 1 (.toString sb))) ;; REPLs that stream must manually load each dep - David (doseq [{:keys [url provides]} deps] From 4a4567f256cc11df26d9d9dcaa3c7f1592e202b7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 12:37:49 -0400 Subject: [PATCH 2578/4033] add nashorn repl script for easy testing from IDE/editor --- script/nashorn_repl.clj | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 script/nashorn_repl.clj diff --git a/script/nashorn_repl.clj b/script/nashorn_repl.clj new file mode 100644 index 000000000..252042f4d --- /dev/null +++ b/script/nashorn_repl.clj @@ -0,0 +1,3 @@ +(require '[cljs.repl :as repl]) +(require '[cljs.repl.nashorn :as nashorn]) +(repl/repl (nashorn/repl-env)) \ No newline at end of file From afe65a0b4c45fdf3eb81fd2b263da047fcbf7225 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 12:46:19 -0400 Subject: [PATCH 2579/4033] add Rhino REPL script for easy testing from IDE/editor --- script/rhino_repl.clj | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 script/rhino_repl.clj diff --git a/script/rhino_repl.clj b/script/rhino_repl.clj new file mode 100644 index 000000000..e3b4e31f5 --- /dev/null +++ b/script/rhino_repl.clj @@ -0,0 +1,3 @@ +(require '[cljs.repl :as repl]) +(require '[cljs.repl.rhino :as rhino]) +(repl/repl (rhino/repl-env)) From a0ed8c4cebf720f8e2e2382836795bd31307d3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 15 Jul 2017 11:27:35 -0700 Subject: [PATCH 2580/4033] CLJS-2245: Add support for using a local `node_modules` installation through a new `:node-modules` compiler flag --- .travis.yml | 1 + src/main/clojure/cljs/closure.clj | 4 ++-- src/test/clojure/cljs/build_api_tests.clj | 28 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3bbf4ff50..9fd3ec8b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ node_js: - "8" before_install: + - npm install -g yarn - wget https://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/jsshell-linux-x86_64.zip - unzip jsshell-linux-x86_64.zip -d spidermoney - sudo apt-get install -y libjavascriptcoregtk-3.0-bin diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 919bdef82..df45a70c4 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2397,7 +2397,7 @@ - index all the node node modules - process the JS modules (preprocess + convert to Closure JS) - save js-dependency-index for compilation" - [{:keys [npm-deps target] :as opts} js-sources compiler-env] + [{:keys [npm-deps node-modules target] :as opts} js-sources compiler-env] (let [;; Find all the top-level Node packages and their files top-level (reduce (fn [acc m] @@ -2411,7 +2411,7 @@ (let [opts (-> opts (update :foreign-libs (fn [libs] - (into (if-not (empty? npm-deps) + (into (if (or (not (empty? npm-deps)) (true? node-modules)) (index-node-modules node-required) []) (expand-libs libs)))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 7858db3fb..6c6b384e7 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -10,7 +10,9 @@ (:refer-clojure :exclude [compile]) (:import java.io.File) (:require [clojure.test :refer [deftest is testing]] + [clojure.data.json :as json] [clojure.java.io :as io] + [clojure.java.shell :as sh] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.test-util :as test] @@ -374,3 +376,29 @@ (test/delete-out-files out) (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)))) + +(deftest test-node-modules-cljs-2245 + (test/delete-node-modules) + (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} + :devDependencies {:module-deps "*" + :resolve "*" + :browser-resolve "*"}})) + (sh/sh "yarn" "install") + (let [ws (atom []) + out (.getPath (io/file (test/tmp-dir) "node-modules-opt-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'node-modules-opt-test.core + :output-dir out + :optimizations :none + :node-modules true + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env opts)] + (test/delete-out-files out) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (build/build (build/inputs (io/file inputs "node_modules_opt_test/core.cljs")) opts cenv)) + (is (.exists (io/file out "node_modules/left-pad/index.js"))) + (is (contains? (:js-module-index @cenv) "left-pad")) + (is (empty? @ws))) + (.delete (io/file "package.json")) + (.delete (io/file "yarn.lock")) + (test/delete-node-modules)) From aa5b09b09989be5e529c3be172e35eb1aa8c98c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 15 Jul 2017 13:28:30 -0700 Subject: [PATCH 2581/4033] CLJS-2246: Revert CLJS-2245 and CLJS-2240 and fix `lein test` Revert "CLJS-2240: don't shell out to module_deps.js if `:npm-deps` not specified" This reverts commit efc7efa67d0bbbdd565a5dd63c973d256712c32c. Revert "CLJS-2245: Add support for using a local `node_modules` installation through a new `:node-modules` compiler flag" This reverts commit a0ed8c4cebf720f8e2e2382836795bd31307d3e0. --- src/main/clojure/cljs/closure.clj | 6 ++---- src/test/cljs_build/node_modules_opt_test/core.cljs | 6 ++++++ src/test/clojure/cljs/build_api_tests.clj | 3 +-- 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 src/test/cljs_build/node_modules_opt_test/core.cljs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index df45a70c4..0e48c3ed3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2397,7 +2397,7 @@ - index all the node node modules - process the JS modules (preprocess + convert to Closure JS) - save js-dependency-index for compilation" - [{:keys [npm-deps node-modules target] :as opts} js-sources compiler-env] + [{:keys [npm-deps target] :as opts} js-sources compiler-env] (let [;; Find all the top-level Node packages and their files top-level (reduce (fn [acc m] @@ -2411,9 +2411,7 @@ (let [opts (-> opts (update :foreign-libs (fn [libs] - (into (if (or (not (empty? npm-deps)) (true? node-modules)) - (index-node-modules node-required) - []) + (into (index-node-modules node-required) (expand-libs libs)))) process-js-modules)] (swap! compiler-env merge diff --git a/src/test/cljs_build/node_modules_opt_test/core.cljs b/src/test/cljs_build/node_modules_opt_test/core.cljs new file mode 100644 index 000000000..37848420a --- /dev/null +++ b/src/test/cljs_build/node_modules_opt_test/core.cljs @@ -0,0 +1,6 @@ +(ns node-modules-opt-test.core + (:require left-pad)) + +(enable-console-print!) + +(println "Padded:" (left-pad 42 5 0)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 6c6b384e7..d4b491875 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -377,7 +377,7 @@ (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)))) -(deftest test-node-modules-cljs-2245 +(deftest test-node-modules-cljs-2246 (test/delete-node-modules) (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} :devDependencies {:module-deps "*" @@ -390,7 +390,6 @@ :opts {:main 'node-modules-opt-test.core :output-dir out :optimizations :none - :node-modules true :closure-warnings {:check-types :off}}} cenv (env/default-compiler-env opts)] (test/delete-out-files out) From 59685f50330bde9be6658385fa2b4f106810e983 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 19:17:48 -0400 Subject: [PATCH 2582/4033] comment out failing test --- src/test/clojure/cljs/build_api_tests.clj | 48 +++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index d4b491875..bedcc4623 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -377,27 +377,27 @@ (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)))) -(deftest test-node-modules-cljs-2246 - (test/delete-node-modules) - (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} - :devDependencies {:module-deps "*" - :resolve "*" - :browser-resolve "*"}})) - (sh/sh "yarn" "install") - (let [ws (atom []) - out (.getPath (io/file (test/tmp-dir) "node-modules-opt-test-out")) - {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) - :opts {:main 'node-modules-opt-test.core - :output-dir out - :optimizations :none - :closure-warnings {:check-types :off}}} - cenv (env/default-compiler-env opts)] - (test/delete-out-files out) - (ana/with-warning-handlers [(collecting-warning-handler ws)] - (build/build (build/inputs (io/file inputs "node_modules_opt_test/core.cljs")) opts cenv)) - (is (.exists (io/file out "node_modules/left-pad/index.js"))) - (is (contains? (:js-module-index @cenv) "left-pad")) - (is (empty? @ws))) - (.delete (io/file "package.json")) - (.delete (io/file "yarn.lock")) - (test/delete-node-modules)) +;(deftest test-node-modules-cljs-2246 +; (test/delete-node-modules) +; (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} +; :devDependencies {:module-deps "*" +; :resolve "*" +; :browser-resolve "*"}})) +; (sh/sh "yarn" "install") +; (let [ws (atom []) +; out (.getPath (io/file (test/tmp-dir) "node-modules-opt-test-out")) +; {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) +; :opts {:main 'node-modules-opt-test.core +; :output-dir out +; :optimizations :none +; :closure-warnings {:check-types :off}}} +; cenv (env/default-compiler-env opts)] +; (test/delete-out-files out) +; (ana/with-warning-handlers [(collecting-warning-handler ws)] +; (build/build (build/inputs (io/file inputs "node_modules_opt_test/core.cljs")) opts cenv)) +; (is (.exists (io/file out "node_modules/left-pad/index.js"))) +; (is (contains? (:js-module-index @cenv) "left-pad")) +; (is (empty? @ws))) +; (.delete (io/file "package.json")) +; (.delete (io/file "yarn.lock")) +; (test/delete-node-modules)) From d4b871cce73e43e489496b6c2bf460492bb7742a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 15 Jul 2017 20:40:19 -0400 Subject: [PATCH 2583/4033] cljs.build.api/build was broken, we were computing an incomplete :js-dependency-index --- src/main/clojure/cljs/build/api.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index b2b003b45..7c1bbf5fe 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -193,7 +193,8 @@ (if-not (nil? env/*compiler*) env/*compiler* (env/default-compiler-env - (closure/add-externs-sources opts))))) + (closure/add-externs-sources + (closure/add-implicit-options opts)))))) ([source opts compiler-env] (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)] (when suggested-opt From 8d65bab354d5e3f9622ea87f0a9002de6fb411a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 15 Jul 2017 17:29:02 -0700 Subject: [PATCH 2584/4033] CLJS-2249: Provide a test for d4b871cce73 --- src/test/cljs/deps.cljs | 4 +++- .../cljs_build/foreign_libs_cljs_2249/core.cljs | 13 +++++++++++++ src/test/clojure/cljs/build_api_tests.clj | 9 +++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs_build/foreign_libs_cljs_2249/core.cljs diff --git a/src/test/cljs/deps.cljs b/src/test/cljs/deps.cljs index 18a200ca4..21cf75ab0 100644 --- a/src/test/cljs/deps.cljs +++ b/src/test/cljs/deps.cljs @@ -1 +1,3 @@ -{:externs ["externs.js"]} +{:externs ["externs.js"] + :foreign-libs [{:file "calculator_global.js" + :provides ["thirdparty.calculator"]}]} diff --git a/src/test/cljs_build/foreign_libs_cljs_2249/core.cljs b/src/test/cljs_build/foreign_libs_cljs_2249/core.cljs new file mode 100644 index 000000000..f7f99d2b1 --- /dev/null +++ b/src/test/cljs_build/foreign_libs_cljs_2249/core.cljs @@ -0,0 +1,13 @@ +;; 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 foreign-libs-cljs-2249.core + (:require [thirdparty.calculator])) + +(defn main [] + (println (js/Calculator.add 1 2))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index bedcc4623..984e1b5c0 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -401,3 +401,12 @@ ; (.delete (io/file "package.json")) ; (.delete (io/file "yarn.lock")) ; (test/delete-node-modules)) + +(deftest test-cljs-2249 + (let [out (io/file (test/tmp-dir) "cljs-2249-out") + root (io/file "src" "test" "cljs_build") + opts {:output-dir (str out) + :target :nodejs}] + (test/delete-out-files out) + (build/build (build/inputs (io/file root "foreign_libs_cljs_2249")) opts) + (is (.exists (io/file out "calculator_global.js"))))) From 309e209f4739f411e7fad959095ecbdc17def948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 15 Jul 2017 23:04:19 -0700 Subject: [PATCH 2585/4033] CLJS-2251: Follow-up fix to CLJS-2249 and related commit --- src/main/clojure/cljs/build/api.clj | 3 +-- src/main/clojure/cljs/closure.clj | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 7c1bbf5fe..b2b003b45 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -193,8 +193,7 @@ (if-not (nil? env/*compiler*) env/*compiler* (env/default-compiler-env - (closure/add-externs-sources - (closure/add-implicit-options opts)))))) + (closure/add-externs-sources opts))))) ([source opts compiler-env] (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)] (when suggested-opt diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0e48c3ed3..06e28213a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2459,6 +2459,9 @@ #(-> % (update-in [:options] merge all-opts) (assoc :target (:target opts)) + ;; Save the current js-dependency index once we have computed all-opts + ;; or the analyzer won't be able to find upstream dependencies - Antonio + (assoc :js-dependency-index (deps/js-dependency-index all-opts)) ;; Save list of sources for cljs.analyzer/locate-src - Juho Teperi (assoc :sources sources))) (binding [comp/*recompiled* (when-not (false? (:recompile-dependents opts)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 984e1b5c0..d0bbfc663 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -16,7 +16,8 @@ [cljs.env :as env] [cljs.analyzer :as ana] [cljs.test-util :as test] - [cljs.build.api :as build])) + [cljs.build.api :as build] + [cljs.closure :as closure])) (deftest test-target-file-for-cljs-ns (is (= (.getPath (build/target-file-for-cljs-ns 'example.core-lib nil)) @@ -409,4 +410,7 @@ :target :nodejs}] (test/delete-out-files out) (build/build (build/inputs (io/file root "foreign_libs_cljs_2249")) opts) + (is (.exists (io/file out "calculator_global.js"))) + (test/delete-out-files out) + (closure/build (build/inputs (io/file root "foreign_libs_cljs_2249")) opts) (is (.exists (io/file out "calculator_global.js"))))) From 8429372b13a06fd90c69eb296c6fd63980557326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 14 Jul 2017 11:30:04 -0700 Subject: [PATCH 2586/4033] CLJS-2239: Self-host: Add `:target :nodejs` to the docstrings in cljs.js --- src/main/cljs/cljs/js.cljs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 1b819b93c..4b5a2ca54 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -677,6 +677,8 @@ :fn-invoke-direct - if `true`, does not generate `.call(null...)` calls for unknown functions, but instead direct invokes via `f(a0,a1...)`. Defaults to `false`. + :target - use `:nodejs` if targeting Node.js. Takes no other options + at the moment. :ns - optional, the namespace in which to evaluate the source. :verbose - optional, emit details from compiler activity. Defaults to false. @@ -786,6 +788,8 @@ :fn-invoke-direct - if `true`, does not generate `.call(null...)` calls for unknown functions, but instead direct invokes via `f(a0,a1...)`. Defaults to `false`. + :target - use `:nodejs` if targeting Node.js. Takes no other options + at the moment. :ns - optional, the namespace in which to evaluate the source. :verbose - optional, emit details from compiler activity. Defaults to false. @@ -909,6 +913,8 @@ :fn-invoke-direct - if `true`, does not generate `.call(null...)` calls for unknown functions, but instead direct invokes via `f(a0,a1...)`. Defaults to `false`. + :target - use `:nodejs` if targeting Node.js. Takes no other options + at the moment. :ns - optional, the namespace in which to evaluate the source. :verbose - optional, emit details from compiler activity. Defaults to false. @@ -1073,6 +1079,8 @@ :fn-invoke-direct - if `true`, does not generate `.call(null...)` calls for unknown functions, but instead direct invokes via `f(a0,a1...)`. Defaults to `false`. + :target - use `:nodejs` if targeting Node.js. Takes no other options + at the moment. :ns - optional, the namespace in which to evaluate the source. :verbose - optional, emit details from compiler activity. Defaults to false. From cd9fdeb8acb516ebd5e485267567a9273cfa1e2c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 16 Jul 2017 18:29:50 +0200 Subject: [PATCH 2587/4033] add missing module graph tests --- src/test/clojure/cljs/module_graph_tests.clj | 25 +++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj index 3af362611..de95d6ee7 100644 --- a/src/test/clojure/cljs/module_graph_tests.clj +++ b/src/test/clojure/cljs/module_graph_tests.clj @@ -86,12 +86,31 @@ (is (= "events" (module-graph/canonical-name "event.types" ins))))) (deftest test-inputs->assigned-modules - (let [modules' (-> (modules opts) + (let [modules (modules opts) + modules' (-> modules module-graph/add-cljs-base module-graph/add-cljs-base-dep module-graph/annotate-depths) - inputs' (inputs opts)] - (module-graph/inputs->assigned-modules inputs' modules'))) + inputs' (inputs opts) + indexed (module-graph/index-inputs inputs') + assigns (module-graph/inputs->assigned-modules inputs' modules') + assigns' (reduce-kv + (fn [ret module-name {:keys [entries]}] + (merge ret + (zipmap + (map #(module-graph/canonical-name % indexed) + entries) + (repeat module-name)))) + {} modules)] + ;; every input assigned, including orphans + (is (every? #(contains? assigns %) + (map #(module-graph/canonical-name % indexed) + (mapcat :provides inputs')))) + ;; every user specified assignment should be respected + (is (every? + (fn [[e m]] + (= m (get assigns e))) + assigns')))) (def bad-modules {:page1 {:entries '[page1.a page1.b events] From 576f2865289d68743597022033c6752c8d0ebc18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 15 Jul 2017 15:39:12 -0700 Subject: [PATCH 2588/4033] CLJS-2248: Build API tests rely on Yarn --- .travis.yml | 3 +- src/test/clojure/cljs/build_api_tests.clj | 51 ++++++++++++----------- src/test/clojure/cljs/test_util.clj | 6 ++- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9fd3ec8b7..b53d868ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,9 @@ dist: trusty language: node_js node_js: - - "8" + - "6" before_install: - - npm install -g yarn - wget https://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/jsshell-linux-x86_64.zip - unzip jsshell-linux-x86_64.zip -d spidermoney - sudo apt-get install -y libjavascriptcoregtk-3.0-bin diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index d0bbfc663..a177dbf59 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -378,31 +378,6 @@ (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)))) -;(deftest test-node-modules-cljs-2246 -; (test/delete-node-modules) -; (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} -; :devDependencies {:module-deps "*" -; :resolve "*" -; :browser-resolve "*"}})) -; (sh/sh "yarn" "install") -; (let [ws (atom []) -; out (.getPath (io/file (test/tmp-dir) "node-modules-opt-test-out")) -; {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) -; :opts {:main 'node-modules-opt-test.core -; :output-dir out -; :optimizations :none -; :closure-warnings {:check-types :off}}} -; cenv (env/default-compiler-env opts)] -; (test/delete-out-files out) -; (ana/with-warning-handlers [(collecting-warning-handler ws)] -; (build/build (build/inputs (io/file inputs "node_modules_opt_test/core.cljs")) opts cenv)) -; (is (.exists (io/file out "node_modules/left-pad/index.js"))) -; (is (contains? (:js-module-index @cenv) "left-pad")) -; (is (empty? @ws))) -; (.delete (io/file "package.json")) -; (.delete (io/file "yarn.lock")) -; (test/delete-node-modules)) - (deftest test-cljs-2249 (let [out (io/file (test/tmp-dir) "cljs-2249-out") root (io/file "src" "test" "cljs_build") @@ -414,3 +389,29 @@ (test/delete-out-files out) (closure/build (build/inputs (io/file root "foreign_libs_cljs_2249")) opts) (is (.exists (io/file out "calculator_global.js"))))) + +(deftest test-node-modules-cljs-2246 + (test/delete-node-modules) + (.delete (io/file "package-lock.json")) + (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} + :devDependencies {:module-deps "*" + :resolve "*" + :browser-resolve "*"}})) + (sh/sh "npm" "install") + (let [ws (atom []) + out (.getPath (io/file (test/tmp-dir) "node-modules-opt-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'node-modules-opt-test.core + :output-dir out + :optimizations :none + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env opts)] + (test/delete-out-files out) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (build/build (build/inputs (io/file inputs "node_modules_opt_test/core.cljs")) opts cenv)) + (is (.exists (io/file out "node_modules/left-pad/index.js"))) + (is (contains? (:js-module-index @cenv) "left-pad")) + (is (empty? @ws))) + (.delete (io/file "package.json")) + (.delete (io/file "package-lock.json")) + (test/delete-node-modules)) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index ad013c791..ff9a38a8e 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -20,8 +20,10 @@ (.delete f)))) (defn delete-node-modules [] - (doseq [f (file-seq (io/file "node_modules"))] - (.delete f))) + (let [nm (io/file "node_modules")] + (while (.exists nm) + (doseq [f (file-seq nm)] + (.delete f))))) (defn project-with-modules "Returns the build config for a project that uses Google Closure modules." From 7139c4c17932b1e11e7a2b665914b32936f02644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 16 Jul 2017 18:12:19 -0700 Subject: [PATCH 2589/4033] CLJS-2254: Module Indexing: Provide relative paths for a package's main module Also test indexing via `module_deps.js`. --- src/main/cljs/cljs/module_deps.js | 26 +++++--- src/main/clojure/cljs/closure.clj | 19 +++--- src/test/clojure/cljs/closure_tests.clj | 84 +++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 21 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 443511af3..e46b015ac 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -60,15 +60,23 @@ md.on('end', function() { for (var key in deps_files) { var dep = deps_files[key]; - if (dep.provides == null && !/node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$/.test(dep.file)) { - var match = dep.file.match(/node_modules[/\\](.*)\.js(on)?$/) - - if (match != null){ - var providedModule = match[1].replace('\\', '/'); - - dep.provides = /\/index$/.test(providedModule) ? - [ providedModule, providedModule.replace(/\/index$/,'')] : - [ providedModule ]; + // add provides to files that are not `package.json`s + if (!/node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$/.test(dep.file)) { + if (dep.file.indexOf('node_modules') !== -1) { + var providedModule = dep.file + .substring(dep.file.lastIndexOf('node_modules')) + .replace('\\', '/') + .replace('node_modules/', ''); + + dep.provides = dep.provides || []; + dep.provides.push(providedModule, providedModule.replace(/\.js(on)?$/, '')); + + var indexReplaced = providedModule.replace(/\/index\.js(on)?$/,''); + + if (/\/index\.js(on)?$/.test(providedModule) && + dep.provides.indexOf(indexReplaced) === -1) { + dep.provides.push(indexReplaced); + } } } diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 06e28213a..d6bce648d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2236,14 +2236,17 @@ (when (= main-path path) name)))) pkg-jsons)] - {:provides (if (some? pkg-json-main) - [pkg-json-main] - (let [module-rel-name (string/replace - (subs path (.lastIndexOf path "node_modules")) - #"node_modules[\\\/]" "")] - (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")] - (boolean (re-find #"[\\\/]index\.js(on)?$" module-rel-name)) - (conj (string/replace module-rel-name #"[\\\/]index\.js(on)?$" "")))))})))))) + {:provides (let [module-rel-name (string/replace + (subs path (.lastIndexOf path "node_modules")) + #"node_modules[\\\/]" "") + provides (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")] + (some? pkg-json-main) + (conj pkg-json-main)) + index-replaced (string/replace module-rel-name #"[\\\/]index\.js(on)?$" "")] + (cond-> provides + (and (boolean (re-find #"[\\\/]index\.js(on)?$" module-rel-name)) + (not (some #{index-replaced} provides))) + (conj index-replaced)))})))))) module-fseq)))) (def node-file-seq->libs-spec (memoize node-file-seq->libs-spec*)) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index b812b0770..3dd55916e 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -92,7 +92,10 @@ (is (true? (some (fn [module] (= module {:module-type :commonjs :file (.getAbsolutePath (io/file "node_modules/left-pad/index.js")) - :provides ["left-pad"]})) modules)))) + :provides ["left-pad/index.js" + "left-pad/index" + "left-pad"]})) + modules)))) (test/delete-node-modules) (spit (io/file "package.json") "{}") (closure/maybe-install-node-deps! {:npm-deps {:react "15.6.1" @@ -101,7 +104,9 @@ (is (true? (some (fn [module] (= module {:module-type :commonjs :file (.getAbsolutePath (io/file "node_modules/react/react.js")) - :provides ["react"]})) + :provides ["react/react.js" + "react/react" + "react"]})) modules))) (is (true? (some (fn [module] (= module {:module-type :commonjs @@ -120,7 +125,9 @@ (is (true? (some (fn [module] (= module {:module-type :commonjs :file (.getAbsolutePath (io/file "node_modules/node-fetch/lib/index.js")) - :provides ["node-fetch/lib/index.js" "node-fetch/lib/index" "node-fetch/lib"]})) + :provides ["node-fetch/lib/index.js" + "node-fetch/lib/index" + "node-fetch/lib"]})) modules)))) (test/delete-node-modules) (spit (io/file "package.json") "{}") @@ -130,7 +137,76 @@ (= module {:file (.getAbsolutePath (io/file "node_modules/@comandeer/css-filter/dist/css-filter.umd.js")) :module-type :commonjs - :provides ["@comandeer/css-filter"]})) + :provides ["@comandeer/css-filter/dist/css-filter.umd.js" + "@comandeer/css-filter/dist/css-filter.umd" + "@comandeer/css-filter"]})) modules)))) (.delete (io/file "package.json")) (test/delete-node-modules)) + +(deftest test-index-node-modules-module-deps-js + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {:left-pad "1.1.3"}} + out (util/output-directory opts)] + (test/delete-node-modules) + (test/delete-out-files out) + (closure/maybe-install-node-deps! opts) + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/left-pad/index.js")) + :provides ["left-pad" + "left-pad/index.js" + "left-pad/index"]})) + (closure/index-node-modules ["left-pad"] opts)))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (test/delete-out-files out) + (let [opts {:npm-deps {:react "15.6.1" + :react-dom "15.6.1"}} + _ (closure/maybe-install-node-deps! opts) + modules (closure/index-node-modules ["react" "react-dom" "react-dom/server"] opts)] + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react/react.js")) + :provides ["react" + "react/react.js" + "react/react"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react/lib/React.js")) + :provides ["react/lib/React.js" "react/lib/React"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) + :provides ["react-dom/server.js" "react-dom/server"]})) + modules)))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (test/delete-out-files out) + (let [opts {:npm-deps {:node-fetch "1.7.1"}}] + (closure/maybe-install-node-deps! opts) + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/node-fetch/lib/index.js")) + :provides ["node-fetch/lib/index.js" + "node-fetch/lib/index" + "node-fetch/lib"]})) + (closure/index-node-modules ["node-fetch/lib"] opts))))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (test/delete-out-files out) + (let [opts {:npm-deps {"@comandeer/css-filter" "1.0.1"}}] + (closure/maybe-install-node-deps! opts) + (is (true? (some (fn [module] + (= module + {:file (.getAbsolutePath (io/file "node_modules/@comandeer/css-filter/dist/css-filter.umd.js")) + :module-type :commonjs + :provides ["@comandeer/css-filter" + "@comandeer/css-filter/dist/css-filter.umd.js" + "@comandeer/css-filter/dist/css-filter.umd"]})) + (closure/index-node-modules ["@comandeer/css-filter"] opts))))) + (.delete (io/file "package.json")) + (test/delete-node-modules) + (test/delete-out-files out))) From 62926332a78e89935f5aa263195fc29cc297c3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 17 Jul 2017 13:23:59 -0700 Subject: [PATCH 2590/4033] CLJS-2256: Generated code doesn't add newline after sourceMappingURL comment --- src/main/clojure/cljs/closure.clj | 6 +++--- src/main/clojure/cljs/compiler.cljc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d6bce648d..3f16e89e0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1864,9 +1864,9 @@ (defn add-source-map-link [{:keys [source-map output-to] :as opts} js] (if source-map - (if (= output-to :print) - (str js "\n//# sourceMappingURL=" source-map) - (str js "\n//# sourceMappingURL=" (path-relative-to (io/file output-to) {:url source-map}))) + (if (= output-to :print) + (str js "\n//# sourceMappingURL=" source-map "\n\n") + (str js "\n//# sourceMappingURL=" (path-relative-to (io/file output-to) {:url source-map}) "\n\n")) js)) (defn absolute-path? [path] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index dd5be7942..fd5ed939b 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1279,7 +1279,7 @@ (defn emit-source-map [src dest sm-data opts] (let [sm-file (io/file (str (.getPath ^File dest) ".map"))] (if-let [smap (:source-map-asset-path opts)] - (emits "\n//# sourceMappingURL=" smap + (emitln "\n//# sourceMappingURL=" smap (string/replace (util/path sm-file) (str (util/path (io/file (:output-dir opts)))) "") @@ -1288,7 +1288,7 @@ (if-not (string/index-of smap "?") "?" "&") "rel=" (System/currentTimeMillis)) "")) - (emits "\n//# sourceMappingURL=" + (emitln "\n//# sourceMappingURL=" (or (:source-map-url opts) (.getName sm-file)) (if (true? (:source-map-timestamp opts)) (str "?rel=" (System/currentTimeMillis)) From 8f7a15687e515dd0a5be6e0d8cc0c57eeb89d924 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Jul 2017 07:40:18 +0200 Subject: [PATCH 2591/4033] cleanup compilation reporting, do not reporting compilation unless actually compiling. --- src/main/clojure/cljs/closure.clj | 26 +++++++++++--------------- src/main/clojure/cljs/compiler.cljc | 26 +++++++++++++++----------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3f16e89e0..ac3b8d311 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -892,15 +892,13 @@ (if (every? #(not (contains? input-set' %)) requires) (do (try - (util/measure (and compiler-stats verbose) - (str "Compile " (:ns ns-info)) - (swap! compiled conj - (-compile (or (:source-file ns-info) - (:source-forms ns-info)) - ; - ns-info -> ns -> cljs file relpath -> js relpath - (merge opts - {:output-file (comp/rename-to-js - (util/ns->relpath (:ns ns-info)))})))) + (swap! compiled conj + (-compile (or (:source-file ns-info) + (:source-forms ns-info)) + ; - ns-info -> ns -> cljs file relpath -> js relpath + (merge opts + {:output-file (comp/rename-to-js + (util/ns->relpath (:ns ns-info)))}))) (catch Throwable e (reset! failed e))) (when-not @failed @@ -945,12 +943,10 @@ (for [ns-info inputs] ; TODO: compile-file calls parse-ns unnecessarily to get ns-info ; TODO: we could mark dependent namespaces for recompile here - (util/measure (and compiler-stats (:verbose opts)) - (str "Compile " (:ns ns-info)) - (-compile (or (:source-file ns-info) - (:source-forms ns-info)) - ; - ns-info -> ns -> cljs file relpath -> js relpath - (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))})))))))))) + (-compile (or (:source-file ns-info) + (:source-forms ns-info)) + ; - ns-info -> ns -> cljs file relpath -> js relpath + (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))}))))))))) (defn add-goog-base [inputs] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index fd5ed939b..4a58ca9b2 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1405,18 +1405,22 @@ (ensure (with-core-cljs opts (fn [] - (when (or ana/*verbose* (:verbose opts)) + (when (and (or ana/*verbose* (:verbose opts)) + (not (:compiler-stats opts))) (util/debug-prn "Compiling" (str src))) - (let [ext (util/ext src) - {:keys [ns] :as ns-info} (ana/parse-ns src)] - (if-let [cached (cached-core ns ext opts)] - (emit-cached-core src dest cached opts) - (let [opts (if (macro-ns? ns ext opts) - (assoc opts :macros-ns true) - opts) - ret (emit-source src dest ext opts)] - (.setLastModified ^File dest (util/last-modified src)) - ret))))))))) + (util/measure (and (or ana/*verbose* (:verbose opts)) + (:compiler-stats opts)) + (str "Compiling " src) + (let [ext (util/ext src) + {:keys [ns] :as ns-info} (ana/parse-ns src)] + (if-let [cached (cached-core ns ext opts)] + (emit-cached-core src dest cached opts) + (let [opts (if (macro-ns? ns ext opts) + (assoc opts :macros-ns true) + opts) + ret (emit-source src dest ext opts)] + (.setLastModified ^File dest (util/last-modified src)) + ret)))))))))) #?(:clj (defn requires-compilation? From c4079b8980ce82e52e3c0cfb8063fe6de248b8fa Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Jul 2017 07:55:33 +0200 Subject: [PATCH 2592/4033] required? test should come first in invokeable? --- src/main/clojure/cljs/analyzer.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 53b7f1670..f5f0498f8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -975,10 +975,10 @@ might be invokeable as a function." [ns env] (let [ns (resolve-ns-alias env ns)] - (and (or (js-module-exists? ns) + (and (required? ns env) + (or (js-module-exists? ns) (node-module-dep? ns) - (dep-has-global-exports? ns)) - (required? ns env)))) + (dep-has-global-exports? ns))))) (defn resolve-invokeable-ns [ns current-ns env] (let [ns (resolve-ns-alias env ns) From 013c99c5d5aae3f6f25abc0fd284093687a8be58 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Jul 2017 08:09:35 +0200 Subject: [PATCH 2593/4033] add missing typehint --- src/main/clojure/cljs/util.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 77f712961..071c5093d 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -331,8 +331,8 @@ (fn [^File d] (seq (. d (listFiles)))) dir)] - (filter (fn [f] + (filter (fn [f ^File] (let [path (.getPath f)] (or (.endsWith path ".json") - (.endsWith path ".js")))) + (.endsWith path ".js")))) fseq)))) From 5e178603f5eb9a29113c3540fde5dee2b44662b1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Jul 2017 08:16:53 +0200 Subject: [PATCH 2594/4033] typo --- src/main/clojure/cljs/util.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 071c5093d..880112d5f 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -331,7 +331,7 @@ (fn [^File d] (seq (. d (listFiles)))) dir)] - (filter (fn [f ^File] + (filter (fn [^File f] (let [path (.getPath f)] (or (.endsWith path ".json") (.endsWith path ".js")))) From f6f4c0431b21fcc87b4de0fcb41f799083f98b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 18 Jul 2017 08:53:07 -0700 Subject: [PATCH 2595/4033] CLJS-2258: Stack overflow regression for sequence xform applied to eduction --- src/main/cljs/cljs/core.cljs | 21 +++++++++------------ src/test/cljs/cljs/core_test.cljs | 4 +++- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7ba7a083b..8c83c070c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3999,10 +3999,10 @@ reduces them without incurring seq initialization" (defn iter [coll] (cond + (iterable? coll) (-iterator coll) (nil? coll) (nil-iter) (string? coll) (string-iter coll) (array? coll) (array-iter coll) - (iterable? coll) (-iterator coll) (seqable? coll) (seq-iter coll) :else (throw (js/Error. (str "Cannot create iterator from " coll))))) @@ -4131,15 +4131,12 @@ reduces them without incurring seq initialization" iterator)) (set! (.-create TransformerIterator) - (fn [xform coll] - (transformer-iterator xform (iter coll) false))) + (fn [xform source] + (transformer-iterator xform source false))) (set! (.-createMulti TransformerIterator) - (fn [xform colls] - (let [iters (array)] - (doseq [coll colls] - (.push iters (iter coll))) - (transformer-iterator xform (MultiIterator. iters) true)))) + (fn [xform sources] + (transformer-iterator xform (MultiIterator. (to-array sources)) true))) (defn sequence "Coerces coll to a (possibly empty) sequence, if it is not already @@ -4156,11 +4153,11 @@ reduces them without incurring seq initialization" (or (seq coll) ()))) ([xform coll] (or (chunkIteratorSeq - (.create TransformerIterator xform coll)) + (.create TransformerIterator xform (iter coll))) ())) ([xform coll & colls] (or (chunkIteratorSeq - (.createMulti TransformerIterator xform (to-array (cons coll colls)))) + (.createMulti TransformerIterator xform (map iter (cons coll colls)))) ()))) (defn ^boolean every? @@ -10166,8 +10163,8 @@ reduces them without incurring seq initialization" ISequential IIterable - (-iterator [coll] - (.create TransformerIterator xform coll)) + (-iterator [_] + (.create TransformerIterator xform (iter coll))) ISeqable (-seq [_] (seq (sequence xform coll))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 06eccc0bf..a67c57161 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -364,7 +364,9 @@ ([] (rf)) ([result] (rf result :foo)) ([result input] (rf result input))))] - (is (= (sequence xf [1 2 3]) [1 2 3 :foo]))))) + (is (= (sequence xf [1 2 3]) [1 2 3 :foo])))) + (testing "CLJS-2258" + (is (= ["1"] (sequence (map str) (eduction [1])))))) (deftest test-obj-equiv (testing "Object equiv method" From 3037f04cdc7d8cc977842e9f129ef9f3aee70796 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 19 Jul 2017 08:18:51 -0400 Subject: [PATCH 2596/4033] CLJS-2262: Correct comment that *warn-on-infer* is file-scope --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8c83c070c..3c10f2f07 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -27,8 +27,8 @@ ;; effect is to set same-named analyzer dynamic Var, which is unset via binding ;; scopes. In self-hosted it is cleared via cljs.js/post-file-side-effects. (def *unchecked-arrays* false) -;; The scope of *warn-on-infer* is global. Its side effect is to affect -;; cljs.analyzer/*cljs-warnings*. +;; The scope of *warn-on-infer* is file-scope: Its side effect is to set the +;; cljs.analyzer/*cljs-warnings* dynamic Var, which is unset via binding scopes. (def *warn-on-infer* false) (set! *unchecked-arrays* true) From 39b6c260837d2eec33b60a30f805b62146364383 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 19 Jul 2017 16:35:20 -0400 Subject: [PATCH 2597/4033] CLJS-2263: Docstring for neg-int? backwards --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3c10f2f07..8fb8752fb 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2255,7 +2255,7 @@ reduces them without incurring seq initialization" :else false)) (defn ^boolean neg-int? - "Return true if x satisfies int? and is positive." + "Return true if x satisfies int? and is negative." [x] (cond (integer? x) (neg? x) From e6ad026c5fca50fc572e2f7a858b546a02cc28aa Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 21 Jul 2017 23:53:58 -0400 Subject: [PATCH 2598/4033] CLJS-2267: Allow ^:const inlined vars to affect if emission --- src/main/clojure/cljs/compiler.cljc | 22 +++++++++++++--------- src/test/cljs/cljs/core_test.cljs | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 4a58ca9b2..eed11118e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -447,15 +447,19 @@ (when-not (= :statement (:context env)) (emit-wrap env (emit-constant form)))) -(defn truthy-constant? [{:keys [op form]}] - (and (= op :constant) - form - (not (or (and (string? form) (= form "")) - (and (number? form) (zero? form)))))) - -(defn falsey-constant? [{:keys [op form]}] - (and (= op :constant) - (or (false? form) (nil? form)))) +(defn truthy-constant? [{:keys [op form const-expr]}] + (or (and (= op :constant) + form + (not (or (and (string? form) (= form "")) + (and (number? form) (zero? form))))) + (and (some? const-expr) + (truthy-constant? const-expr)))) + +(defn falsey-constant? [{:keys [op form const-expr]}] + (or (and (= op :constant) + (or (false? form) (nil? form))) + (and (some? const-expr) + (falsey-constant? const-expr)))) (defn safe-test? [env e] (let [tag (ana/infer-tag env e)] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index a67c57161..5e0468669 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1411,6 +1411,23 @@ (is (= "#js {:foo/bar 33}" (pr-str (doto (js-obj) (gobject/set "foo/bar" 33))))) (is (= "#js {:foo/bar #:var{:quux 66}}" (pr-str (doto (js-obj) (gobject/set "foo/bar" {:var/quux 66})))))))) +(def ^:const true-2267 true) +(def ^:const false-2267 false) +(def ^:const nil-2267 nil) +(def ^:const empty-string-2267 "") +(def ^:const non-empty-string-2267 "x") +(def ^:const zero-2267 0) +(def ^:const non-zero-2267 1) + +(deftest test-cljs-2267 + (is (= :then (if true-2267 :then :else))) + (is (= :else (if false-2267 :then :else))) + (is (= :else (if nil-2267 :then :else))) + (is (= :then (if empty-string-2267 :then :else))) + (is (= :then (if non-empty-string-2267 :then :else))) + (is (= :then (if zero-2267 :then :else))) + (is (= :then (if non-zero-2267 :then :else)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 8972224b4b4617a98a9fdd497af1aeb91a29ed2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 17 Jul 2017 18:35:55 -0700 Subject: [PATCH 2599/4033] CLJS-2255: Clean up :npm-deps --- src/main/clojure/cljs/build/api.clj | 33 ++++++++++++++++++----- src/test/clojure/cljs/build_api_tests.clj | 30 +++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index b2b003b45..34f5e8d81 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -215,12 +215,33 @@ (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/watch source opts compiler-env stop)))) -(defn node-module-deps - "EXPERIMENTAL: return the foreign libs entries as computed by running - the module-deps package on the supplied JavaScript entry point. Assumes - that the module-deps NPM package is either locally or globally installed." - [entry] - (closure/node-module-deps entry)) +;; ============================================================================= +;; Node.js / NPM dependencies + +(defn install-node-deps! + "EXPERIMENTAL: Install the supplied dependencies via NPM. dependencies must be + a map of name to version." + ([dependencies] + (install-node-deps! dependencies + (when-not (nil? env/*compiler*) + (:options @env/*compiler*)))) + ([dependencies opts] + {:pre [(map? dependencies)]} + (closure/maybe-install-node-deps! (merge opts {:npm-deps dependencies})))) + +(defn get-node-deps + "EXPERIMENTAL: Get the Node.js dependency graph of the supplied dependencies. + Dependencies must be a sequence of strings or symbols naming packages or paths + within packages (e.g. [react \"react-dom/server\"]. Assumes dependencies have + been been previously installed, either by `cljs.build.api/install-node-deps!` + or by an NPM client, and reside in the `node_modules` directory." + ([dependencies] + (get-node-deps dependencies + (when-not (nil? env/*compiler*) + (:options @env/*compiler*)))) + ([dependencies opts] + {:pre [(sequential? dependencies)]} + (closure/index-node-modules dependencies opts))) (comment (node-module-deps diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index a177dbf59..212a1749f 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -415,3 +415,33 @@ (.delete (io/file "package.json")) (.delete (io/file "package-lock.json")) (test/delete-node-modules)) + +(deftest test-deps-api-cljs-2255 + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (build/install-node-deps! {:left-pad "1.1.3"}) + (is (.exists (io/file "node_modules/left-pad/package.json"))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (build/install-node-deps! {:react "15.6.1" + :react-dom "15.6.1"}) + (let [modules (build/get-node-deps '[react "react-dom/server"])] + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react/react.js")) + :provides ["react" + "react/react.js" + "react/react"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react/lib/React.js")) + :provides ["react/lib/React.js" "react/lib/React"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :commonjs + :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) + :provides ["react-dom/server.js" "react-dom/server"]})) + modules)))) + (test/delete-node-modules) + (.delete (io/file "package.json"))) From c6f879f0ec25ed465cee4c3d2df7850940dee777 Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Thu, 13 Jul 2017 19:49:51 +0200 Subject: [PATCH 2600/4033] CLJS-2235: Allow passing extra maven opts to build scripts --- script/aot_core | 3 ++- script/build | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/script/aot_core b/script/aot_core index 329d9b9bd..58496a073 100755 --- a/script/aot_core +++ b/script/aot_core @@ -5,6 +5,7 @@ set -ex FILE_SEP='/' PATH_SEP=':' OS_ID=`uname | tr [:upper:] [:lower:]` +CLJS_SCRIPT_MVN_OPTS=${CLJS_SCRIPT_MVN_OPTS:-""} if [[ $OS_ID == *mingw* ]] then @@ -16,7 +17,7 @@ fi CP_FILE=`mktemp /tmp/cljs_cp.txt.XXXXXXXXXXX` -mvn -f pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP +mvn -f pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP $CLJS_SCRIPT_MVN_OPTS CLJS_CP=`cat $CP_FILE` diff --git a/script/build b/script/build index 596b8ca81..75c45fe7a 100755 --- a/script/build +++ b/script/build @@ -10,6 +10,7 @@ rm -rf target POM_TEMPLATE="pom.template.xml" POM_FILE="pom.xml" +CLJS_SCRIPT_MVN_OPTS=${CLJS_SCRIPT_MVN_OPTS:-""} # The command `git describe --match v0.0` will return a string like # @@ -57,7 +58,7 @@ mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server if [ "$HUDSON" = "true" ]; then - mvn --fail-at-end -Psonatype-oss-release \ + mvn --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ clean deploy nexus-staging:release echo "Creating tag $TAG" @@ -65,7 +66,7 @@ if [ "$HUDSON" = "true" ]; then git push origin "$TAG" else echo "Skipping remote deployment and Git tag because we are not on Hudson." - mvn clean install + mvn $CLJS_SCRIPT_MVN_OPTS clean install fi rm -f src/main/cljs/cljs/core.aot.js From ac1e399c81a308a1693779596c6aee5334639fcb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 25 Jul 2017 08:27:35 +0200 Subject: [PATCH 2601/4033] c.b.a/install-node-deps! needs to check the deps as well --- src/main/clojure/cljs/build/api.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 34f5e8d81..88cce0f45 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -227,6 +227,7 @@ (:options @env/*compiler*)))) ([dependencies opts] {:pre [(map? dependencies)]} + (closure/check-npm-deps opts) (closure/maybe-install-node-deps! (merge opts {:npm-deps dependencies})))) (defn get-node-deps From fb8ce05143dac9e9feb602be2544b72c87b337a3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 25 Jul 2017 08:33:18 +0200 Subject: [PATCH 2602/4033] do not auto install node deps - add new flag for old behavior, update compiler options --- src/main/clojure/cljs/closure.clj | 8 +++++--- src/main/clojure/cljs/repl.cljc | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ac3b8d311..43c19e004 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -166,7 +166,8 @@ :source-map-inline :source-map-timestamp :static-fns :target :verbose :warnings :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps - :fn-invoke-direct :checked-arrays}) + :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs + :watch-error-fn :watch-fn :install-deps}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -2435,8 +2436,9 @@ ([source opts compiler-env] (env/with-compiler-env compiler-env ;; we want to warn about NPM dep conflicts before installing the modules - (check-npm-deps opts) - (maybe-install-node-deps! opts) + (when (:install-deps opts) + (check-npm-deps opts) + (maybe-install-node-deps! opts)) (let [compiler-stats (:compiler-stats opts) checked-arrays (or (:checked-arrays opts) ana/*checked-arrays*) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index f7d4a0bb6..71e1a41ca 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -874,7 +874,9 @@ (print nil)) (let [value (eval repl-env env input opts)] (print value))))))] - (cljsc/maybe-install-node-deps! opts) + (when (:install-deps opts) + (cljsc/check-npm-deps opts) + (cljsc/maybe-install-node-deps! opts)) (comp/with-core-cljs opts (fn [] (binding [*repl-opts* opts] From ea923717762ac4c7ba7d38e4ecbc5e4b36ce73c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 25 Jul 2017 10:42:57 -0700 Subject: [PATCH 2603/4033] CLJS-2272: Tests that depended on default install deps behavior failing --- script/test | 2 +- script/test-simple | 2 +- src/test/clojure/cljs/build_api_tests.clj | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/script/test b/script/test index a01c24bd7..ba6beb10f 100755 --- a/script/test +++ b/script/test @@ -12,7 +12,7 @@ mkdir -p builds/out-adv possible=5 ran=0 -if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-adv/core-advanced-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-adv/core-advanced-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/script/test-simple b/script/test-simple index fa0b75c27..61fce7a80 100755 --- a/script/test-simple +++ b/script/test-simple @@ -12,7 +12,7 @@ possible=5 ran=0 #bin/cljsc test >out/core-test.js -if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :verbose true :compiler-stats true :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-simp/core-simple-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :verbose true :compiler-stats true :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-simp/core-simple-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 212a1749f..510b612b9 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -216,6 +216,7 @@ :opts {:main 'npm-deps-test.core :output-dir out :optimizations :none + :install-deps true :npm-deps {:left-pad "1.1.3"} :closure-warnings {:check-types :off}}} cenv (env/default-compiler-env)] @@ -229,6 +230,7 @@ :opts {:main 'npm-deps-test.string-requires :output-dir out :optimizations :none + :install-deps true :npm-deps {:react "15.6.1" :react-dom "15.6.1" :lodash "4.17.4"} @@ -298,6 +300,7 @@ :output-dir out :optimizations :none :target :nodejs + :install-deps true :npm-deps {:react "15.6.1" :react-dom "15.6.1"} :closure-warnings {:check-types :off From a1c6c2e3346c78169b2dc688120eaeba399deecd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Jul 2017 07:23:44 +0200 Subject: [PATCH 2604/4033] add c.m-g/module-for --- src/main/clojure/cljs/module_graph.cljc | 12 ++++++++++++ src/test/clojure/cljs/module_graph_tests.clj | 8 ++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index e5f4771b3..91d84c46d 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -333,3 +333,15 @@ (fn [ret module-name {:keys [depends-on] :or {depends-on []} :as module}] (assoc ret module-name depends-on)) {} modules))) + +(defn module-for + "Given an entry find the module it belongs to." + [entry modules] + (let [modules' (normalize modules) + entry' (str (munge entry))] + (->> modules' + (some + (fn [[module-name {:keys [entries]} :as me]] + (when (some #{entry'} entries) + me))) + first))) diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj index de95d6ee7..c91f5377f 100644 --- a/src/test/clojure/cljs/module_graph_tests.clj +++ b/src/test/clojure/cljs/module_graph_tests.clj @@ -7,7 +7,7 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.module-graph-tests - (:require [clojure.test :refer [deftest is testing]] + (:require [clojure.test :as test :refer [deftest is testing]] [cljs.closure :as closure] [cljs.util :as util] [cljs.module-graph :as module-graph])) @@ -145,4 +145,8 @@ {:cljs-base ["/asset/js/cljs_base.js"] :shared ["/asset/js/shared.js"] :page1 ["/asset/js/page1.js"] - :page2 ["/asset/js/page2.js"]}))) \ No newline at end of file + :page2 ["/asset/js/page2.js"]}))) + +(deftest test-module-for + (is (= :page1 (module-graph/module-for 'page1.a (modules opts)))) + (is (= :page1 (module-graph/module-for "page1.a" (modules opts))))) From 364c2ff66bf7e7ff11a28d9366f3f98a82ffdbcb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Jul 2017 10:59:08 +0200 Subject: [PATCH 2605/4033] CLJS-2269: Warn on top level code split loads cljs.loader/load is now a macro that passes in the module that initiated the load. This is validated to check that it is already loaded to avoid common misunderstanding about how loader is intended to be used. --- src/main/cljs/cljs/loader.clj | 30 ++++++++++++++++++++++++ src/main/cljs/cljs/loader.cljs | 42 ++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 src/main/cljs/cljs/loader.clj diff --git a/src/main/cljs/cljs/loader.clj b/src/main/cljs/cljs/loader.clj new file mode 100644 index 000000000..394adc51f --- /dev/null +++ b/src/main/cljs/cljs/loader.clj @@ -0,0 +1,30 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software + +(ns cljs.loader + (:refer-clojure :exclude [load]) + (:require [cljs.module-graph :as mg] + [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api])) + +(defn load-expr + ([env module-name] + (load-expr env module-name nil)) + ([env module-name cb] + (let [sinfo (ana/srce-info env) + loader (mg/module-for (-> env :ns :name) + (:modules (ana-api/get-options)))] + `(cljs.loader/load* ~module-name ~loader ~cb)))) + +(defmacro load + "Load a module. module-name should be a keyword matching a :modules module + definition." + ([module-name] + (load-expr &env module-name)) + ([module-name cb] + (load-expr &env module-name cb))) \ No newline at end of file diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 7caaf9aad..b36ab3696 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -1,4 +1,13 @@ +; 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.loader + (:require-macros cljs.loader) (:require [goog.object :as gobj]) (:import [goog.module ModuleLoader] [goog.module ModuleManager])) @@ -33,14 +42,30 @@ (.setAllModuleInfo *module-manager* (to-js module-infos)) (.setModuleUris *module-manager* (to-js module-uris)) -(defn load +(defn loaded? + "Return true if modules is loaded. module-name should be a keyword matching + a :modules module definition." + [module-name] + (assert (contains? module-infos module-name) + (str "Module " module-name " does not exist")) + (let [mname (-> module-name name munge) + module (.getModuleInfo *module-manager* mname)] + (when (some? module) + (.isLoaded module)))) + +(defn load* "Load a module. module-name should be a keyword matching a :modules module definition." ([module-name] - (load module-name nil)) - ([module-name cb] + (throw (js/Error. "Invalid load call, must provide loader argument"))) + ([module-name loader] + (load* module-name loader nil)) + ([module-name loader cb] (assert (contains? module-infos module-name) (str "Module " module-name " does not exist")) + (assert (loaded? loader) + (str "Module " loader " not fully loaded, but attempted to " + "load module " module-name)) (let [mname (-> module-name name munge)] (if-not (nil? cb) (.execOnLoad *module-manager* mname cb) @@ -60,17 +85,6 @@ (.setLoaded *module-manager* (munge-kw x))) (.setLoaded *module-manager* (munge-kw module-name)))) -(defn loaded? - "Return true if modules is loaded. module-name should be a keyword matching - a :modules module definition." - [module-name] - (assert (contains? module-infos module-name) - (str "Module " module-name " does not exist")) - (let [mname (-> module-name name munge) - module (.getModuleInfo *module-manager* mname)] - (when (some? module) - (.isLoaded module)))) - (defn prefetch "Prefetch a module. module-name should be a keyword matching a :modules module definition. Will download the module but not evaluate it. To From 6c2d17e3e3bfc725c52f6cadb7a808693f07c8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 25 Jul 2017 21:31:29 -0700 Subject: [PATCH 2606/4033] CLJS-2273: Bump tools.reader to 1.0.3 and development dependencies --- pom.template.xml | 4 ++-- project.clj | 4 ++-- script/bootstrap | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 3d631d5cf..8b4a675cb 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,12 +50,12 @@ org.clojure tools.reader - 1.0.2 + 1.0.3 org.clojure test.check - 0.9.0 + 0.10.0-alpha2 test diff --git a/project.clj b/project.clj index 0ae18c1f6..391946ec3 100644 --- a/project.clj +++ b/project.clj @@ -10,8 +10,8 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.0.2"] - [org.clojure/test.check "0.9.0" :scope "test"] + [org.clojure/tools.reader "1.0.3"] + [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] [com.google.javascript/closure-compiler-unshaded "v20170626"] diff --git a/script/bootstrap b/script/bootstrap index 912e8bc74..c5c1ce744 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,16 +2,16 @@ set -e -CLOJURE_RELEASE="1.9.0-alpha16" -SPEC_ALPHA_RELEASE="0.1.108" +CLOJURE_RELEASE="1.9.0-alpha17" +SPEC_ALPHA_RELEASE="0.1.123" CORE_SPECS_ALPHA_RELEASE="0.1.10" CLOSURE_RELEASE="20170626" DJSON_RELEASE="0.2.6" -TRANSIT_RELEASE="0.8.285" +TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.0.2" -TEST_CHECK_RELEASE="0.9.0" +TREADER_RELEASE="1.0.3" +TEST_CHECK_RELEASE="0.10.0-alpha2" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From 446aa38e7c07053aa11f412a91ca848c13d1e885 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Jul 2017 14:20:08 +0200 Subject: [PATCH 2607/4033] remove bad let binding in last commit --- src/main/cljs/cljs/loader.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/loader.clj b/src/main/cljs/cljs/loader.clj index 394adc51f..338275ca0 100644 --- a/src/main/cljs/cljs/loader.clj +++ b/src/main/cljs/cljs/loader.clj @@ -16,8 +16,7 @@ ([env module-name] (load-expr env module-name nil)) ([env module-name cb] - (let [sinfo (ana/srce-info env) - loader (mg/module-for (-> env :ns :name) + (let [loader (mg/module-for (-> env :ns :name) (:modules (ana-api/get-options)))] `(cljs.loader/load* ~module-name ~loader ~cb)))) From 5ebf0806701a48cf9ca577af8ea76287b2975b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 26 Jul 2017 09:40:20 -0700 Subject: [PATCH 2608/4033] CLJS-2274: Update CI script to install deps --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b53d868ce..5ef26d0f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ before_install: before_script: - script/bootstrap - mkdir -p builds/out-adv - - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-adv/core-advanced-test.js + - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-adv/core-advanced-test.js script: - lein test From 45f63555aaad62384573001afb667e6bff58097e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 27 Jul 2017 08:02:57 -0400 Subject: [PATCH 2609/4033] CLJS-2259: Extra .cljs_node_repl directory containing cljs.core output Need to remove :output-dir when compiling core. --- src/main/clojure/cljs/repl/node.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index b1a653890..bb6a5ffc3 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -144,9 +144,8 @@ ;; for bootstrap to load, use new closure/compile as it can handle ;; resources in JARs (let [core-js (closure/compile core - (assoc opts - :output-file - (closure/src-file->target-file core))) + (assoc (dissoc opts :output-dir) + :output-file (closure/src-file->target-file core))) deps (closure/add-dependencies opts core-js)] ;; output unoptimized code and the deps file ;; for all compiled namespaces From 250a9fafa4acfad3383213e657ccfca6cfca5182 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 27 Jul 2017 08:32:25 -0400 Subject: [PATCH 2610/4033] do not immediately fail if foreign lib-spec does not provide :file, prep for CLJS-2250 --- src/main/clojure/cljs/js_deps.cljc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index dd6c1272b..f95c98f00 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -224,10 +224,10 @@ case." ([lib-spec] (load-foreign-library* lib-spec false)) ([lib-spec cp-only?] (let [find-func (if cp-only? io/resource find-url)] - (cond-> - (merge lib-spec - {:foreign true - :url (find-func (:file lib-spec))}) + (cond-> (assoc lib-spec :foreign true) + (:file lib-spec) + (assoc :url (find-func (:file lib-spec))) + (:file-min lib-spec) (assoc :url-min (find-func (:file-min lib-spec))))))) From 02b8d6e9e5c819b723062f144b6bfb81fc2b8eb4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 27 Jul 2017 10:30:07 -0400 Subject: [PATCH 2611/4033] wip --- src/main/clojure/cljs/build/api.clj | 5 ++++- src/main/clojure/cljs/closure.clj | 5 ++++- src/main/clojure/cljs/js_deps.cljc | 11 ++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 88cce0f45..46fa471d6 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -193,7 +193,10 @@ (if-not (nil? env/*compiler*) env/*compiler* (env/default-compiler-env - (closure/add-externs-sources opts))))) + ;; need to dissoc :foreign-libs since we won't know what overriding + ;; foreign libspecs are referring to until after add-implicit-options + ;; - David + (closure/add-externs-sources (dissoc opts :foreign-libs)))))) ([source opts compiler-env] (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)] (when suggested-opt diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 43c19e004..c60d9d5f7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2432,7 +2432,10 @@ (if-not (nil? env/*compiler*) env/*compiler* (env/default-compiler-env - (add-externs-sources opts))))) + ;; need to dissoc :foreign-libs since we won't know what overriding + ;; foreign libspecs are referring to until after add-implicit-options + ;; - David + (add-externs-sources (dissoc opts :foreign-libs)))))) ([source opts compiler-env] (env/with-compiler-env compiler-env ;; we want to warn about NPM dep conflicts before installing the modules diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index f95c98f00..cb96dba15 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -130,6 +130,11 @@ case." (-requires [this] "A list of namespaces that this JavaScript requires.") (-source [this] [this opts] "The JavaScript source string.")) +(defn get-file [libspec index] + (or (:file libspec) + (some (fn [provide] (get-in index [provide :file])) + (:provides libspec)))) + (defn build-index "Index a list of dependencies by namespace and file name. There can be zero or more namespaces provided per file. Upstream foreign libraies @@ -159,7 +164,11 @@ case." index provides) index)] (if (:foreign dep) - (update-in index' [(:file dep)] merge dep) + (if-let [file (get-file dep index)] + (update-in index' [file] merge dep) + (throw + (Exception. + (str "No :file provided for foreign libspec " (pr-str dep))))) (assoc index' (:file dep) dep)))) {} deps)) From 18761390b13d0dc4d80c0925c75d3eab49279ba3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 27 Jul 2017 14:58:10 -0400 Subject: [PATCH 2612/4033] CLJS-2275: cljs.spec.alpha/fdef resolves eagerly --- src/main/cljs/cljs/spec/alpha.cljc | 4 +--- src/test/cljs/cljs/spec_test.cljs | 9 +++++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index ed92fb4ef..cf8566a86 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -56,9 +56,7 @@ "Qualify symbol s by resolving it or using the current *ns*." [env s] (if (namespace s) - (let [v (resolve env s)] - (clojure.core/assert v (str "Unable to resolve: " s)) - (->sym v)) + (->sym (ana/resolve-var env s)) (symbol (str ana/*cljs-ns*) (str s)))) (defmacro def diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index e2331a836..02130dfda 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -8,6 +8,7 @@ (ns cljs.spec-test (:require [cljs.spec.alpha :as s] + [cljs.spec.test.alpha :as st] [cljs.test :as test :refer-macros [deftest is are run-tests]] [cljs.spec.gen.alpha :as gen] [clojure.test.check.generators])) @@ -34,7 +35,7 @@ :args (s/cat :a integer? :b (s/? integer?)) :ret integer?) -;;(s/instrument #'adder) +;;(st/instrument `adder) (deftest test-multi-arity-instrument (is (= 1 (adder 1))) @@ -48,7 +49,7 @@ (s/fdef testmm :args (s/cat :m map?) :ret string?) -;;(s/instrument #'testmm) +;;(st/instrument `testmm) (deftest test-multifn-instrument (is (= "good" (testmm {:type :good}))) @@ -292,6 +293,10 @@ [{10 10 20 "x"}] [{10 10 20 "x"}])) +(s/fdef foo.bar/cljs-2275 + :args (s/cat :k keyword?) + :ret string?) + (comment (run-tests) From 190fa6489c9578ec7dba8235e5c905ae32ff7fda Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 27 Jul 2017 15:38:57 -0400 Subject: [PATCH 2613/4033] CLJS-2276: Self-host: Need test.check dep for CLJS-2275 Revives addition of test.check --- script/test-self-parity | 4 ++-- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/script/test-self-parity b/script/test-self-parity index bf3c4b641..39a99049c 100755 --- a/script/test-self-parity +++ b/script/test-self-parity @@ -10,10 +10,10 @@ if [ ! -f lib/clojure.jar ]; then exit 1 fi jar xvf lib/clojure.jar clojure/template.clj > /dev/null -jar xvf lib/test.check.jar clojure/test/check/random clojure/test/check/generators.cljc clojure/test/check/rose_tree.cljc > /dev/null +jar xvf lib/test.check.jar clojure/test/check.cljc clojure/test/check/results.cljc clojure/test/check/clojure_test.cljc clojure/test/check/impl.cljc clojure/test/check/properties.cljc clojure/test/check/generators.cljc clojure/test/check/random.clj clojure/test/check/random/doubles.cljs clojure/test/check/random/longs/bit_count_impl.cljs clojure/test/check/random/longs.cljs clojure/test/check/random.cljs clojure/test/check/rose_tree.cljc > /dev/null mkdir -p builds/out-self-parity/clojure/test mv clojure/template.clj builds/out-self-parity/clojure -mv clojure/test/check builds/out-self-parity/clojure/test +mv clojure/test builds/out-self-parity/clojure if ! bin/cljsc src/test/self/self_parity "{:optimizations :none :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}"; then >&2 echo ClojureScript compilation failed diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5e0468669..c0674c3fe 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -9,6 +9,10 @@ (ns cljs.core-test (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] + [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] [goog.object :as gobject])) @@ -547,6 +551,10 @@ (is (= 0 (loop [x 0] (cond-> x false recur)))) (is (= 0 (loop [x 0] (cond->> x false recur))))) +(defspec boolean-test 10 + (prop/for-all [b gen/boolean] + (boolean? b))) + (deftest aget-test (is (= 11 (aget #js [10 11 12] 1))) (is (= 11 (apply aget [#js [10 11 12] 1]))) From 2046dc8dd7c4df9237a40cfbe48e8b97312d4d59 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 27 Jul 2017 21:35:48 -0400 Subject: [PATCH 2614/4033] CLJS-2278: JavaScript object literals are printed wth keys that cannot be read --- src/main/cljs/cljs/core.cljs | 4 +++- src/test/cljs/cljs/core_test.cljs | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8fb8752fb..01bf4e70a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9635,7 +9635,9 @@ reduces them without incurring seq initialization" (do (-write writer "#js ") (print-map - (map (fn [k] [(keyword k) (unchecked-get obj k)]) (js-keys obj)) + (map (fn [k] + [(cond-> k (some? (re-matches #"[A-Za-z][\w\*\+\?!\-']*" k)) keyword) (unchecked-get obj k)]) + (js-keys obj)) pr-writer writer opts)) (array? obj) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index c0674c3fe..743b38ea9 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1413,11 +1413,12 @@ (is (contains? (ns-imports 'clojure.string) 'StringBuffer)) (is (= (find (ns-imports 'clojure.string) 'StringBuffer) ['StringBuffer goog.string.StringBuffer])))) + (deftest test-cljs-2190 (binding [*print-namespace-maps* true] (testing "printing a javascript map with a slash on keyword" - (is (= "#js {:foo/bar 33}" (pr-str (doto (js-obj) (gobject/set "foo/bar" 33))))) - (is (= "#js {:foo/bar #:var{:quux 66}}" (pr-str (doto (js-obj) (gobject/set "foo/bar" {:var/quux 66})))))))) + (is (= "#js {\"foo/bar\" 33}" (pr-str (doto (js-obj) (gobject/set "foo/bar" 33))))) + (is (= "#js {\"foo/bar\" #:var{:quux 66}}" (pr-str (doto (js-obj) (gobject/set "foo/bar" {:var/quux 66})))))))) (def ^:const true-2267 true) (def ^:const false-2267 false) @@ -1436,6 +1437,13 @@ (is (= :then (if zero-2267 :then :else))) (is (= :then (if non-zero-2267 :then :else)))) +(deftest test-cljs-2278 + (is (= "#js {:alpha 1, \"beta gamma\" 2, \"delta/epsilon\" 3}" (pr-str #js {"alpha" 1 "beta gamma" 2 "delta/epsilon" 3}))) + (is (= "#js {\":abc\" 1}" (pr-str #js {":abc" 1}))) + (is (= "#js {\"0abc\" 1}" (pr-str #js {"0abc" 1}))) + (is (= "#js {:abc-def 1}" (pr-str #js {"abc-def" 1}))) + (is (= "#js {:x*+?!-' 1}" (pr-str #js {"x*+?!-'" 1})))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From a460b75c42caab6f912c053c3417c0a1a34d08e4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jul 2017 08:21:49 -0400 Subject: [PATCH 2615/4033] ignore :foreign-libs overrides in cljs.closure/expand-libs --- src/main/clojure/cljs/closure.clj | 32 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c60d9d5f7..b3ee467e1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1995,21 +1995,23 @@ (string/includes? p' "_") (conj (string/replace p' "_" "-"))))) (expand-lib* [{:keys [file] :as lib}] - (let [root (.getAbsolutePath (io/file file)) - dir (io/file file)] - (if (.isDirectory dir) - (into [] - (comp - (filter #(.endsWith (.getName ^File %) ".js")) - (filter #(not (.isHidden ^File %))) - (map - (fn [^File f] - (let [p (.getPath f) - ap (.getAbsolutePath f)] - (merge lib - {:file p :provides (path->provides (prep-path ap root))}))))) - (file-seq dir)) - [lib])))] + (if-not file + [] ;; foreign-lib override case - David + (let [root (.getAbsolutePath (io/file file)) + dir (io/file file)] + (if (.isDirectory dir) + (into [] + (comp + (filter #(.endsWith (.getName ^File %) ".js")) + (filter #(not (.isHidden ^File %))) + (map + (fn [^File f] + (let [p (.getPath f) + ap (.getAbsolutePath f)] + (merge lib + {:file p :provides (path->provides (prep-path ap root))}))))) + (file-seq dir)) + [lib]))))] (into [] (mapcat expand-lib* libs)))) (declare index-node-modules) From 0cd4ac59676dcb1a588069f95d444b7d6a52307f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jul 2017 08:33:23 -0400 Subject: [PATCH 2616/4033] more consistent naming convention --- src/main/clojure/cljs/js_deps.cljc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index cb96dba15..d4f03abe5 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -130,10 +130,10 @@ case." (-requires [this] "A list of namespaces that this JavaScript requires.") (-source [this] [this opts] "The JavaScript source string.")) -(defn get-file [libspec index] - (or (:file libspec) +(defn get-file [lib-spec index] + (or (:file lib-spec) (some (fn [provide] (get-in index [provide :file])) - (:provides libspec)))) + (:provides lib-spec)))) (defn build-index "Index a list of dependencies by namespace and file name. There can @@ -168,7 +168,7 @@ case." (update-in index' [file] merge dep) (throw (Exception. - (str "No :file provided for foreign libspec " (pr-str dep))))) + (str "No :file provided for :foreign-libs spec " (pr-str dep))))) (assoc index' (:file dep) dep)))) {} deps)) From b258e0f35bc36fca096848a3282b31465e273a8e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jul 2017 09:22:33 -0400 Subject: [PATCH 2617/4033] CLJS-2250: Support :foreign-libs overrides via :provides --- src/main/clojure/cljs/closure.clj | 2 +- src/main/clojure/cljs/js_deps.cljc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b3ee467e1..a92571675 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1996,7 +1996,7 @@ (conj (string/replace p' "_" "-"))))) (expand-lib* [{:keys [file] :as lib}] (if-not file - [] ;; foreign-lib override case - David + [lib] ;; foreign-lib override case - David (let [root (.getAbsolutePath (io/file file)) dir (io/file file)] (if (.isDirectory dir) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index d4f03abe5..44f89682f 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -164,7 +164,7 @@ case." index provides) index)] (if (:foreign dep) - (if-let [file (get-file dep index)] + (if-let [file (get-file dep index')] (update-in index' [file] merge dep) (throw (Exception. From 0c855a1dd83328a273cb3981576e90c33524e683 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jul 2017 09:39:05 -0400 Subject: [PATCH 2618/4033] need preserve original :provides --- src/main/clojure/cljs/js_deps.cljc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 44f89682f..d6eff14af 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -135,6 +135,11 @@ case." (some (fn [provide] (get-in index [provide :file])) (:provides lib-spec)))) +(defn lib-spec-merge [a b] + (merge a + (cond-> b + (contains? a :provides) (dissoc :provides)))) + (defn build-index "Index a list of dependencies by namespace and file name. There can be zero or more namespaces provided per file. Upstream foreign libraies @@ -148,7 +153,7 @@ case." (reduce (fn [index' provide] (if (:foreign dep) - (update-in index' [provide] merge dep) + (update-in index' [provide] lib-spec-merge dep) ;; when building the dependency index, we need to ;; avoid overwriting a CLJS dep with a CLJC dep of ;; the same namespace - António Monteiro @@ -165,7 +170,7 @@ case." index)] (if (:foreign dep) (if-let [file (get-file dep index')] - (update-in index' [file] merge dep) + (update-in index' [file] lib-spec-merge dep) (throw (Exception. (str "No :file provided for :foreign-libs spec " (pr-str dep))))) From 09d596934682a682502cc9469d4cddfbc7412f0d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 27 Jul 2017 11:15:39 -0400 Subject: [PATCH 2619/4033] CLJS-1854: Self-host: Reload ns with const --- src/main/cljs/cljs/js.cljs | 44 ++++++++++++++++++---------- src/test/self/self_host/test.cljs | 48 +++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 4b5a2ca54..fe2cc7a45 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -234,7 +234,17 @@ (dissoc opts :macros-ns) cb)) -(defn post-file-side-effects +(defn- pre-file-side-effects + [st name file opts] + (when (:verbose opts) + (debug-prn "Pre-file side-effects" file)) + ;; In case any constants are defined in the namespace, flush any analysis metadata + ;; so that the constants can be defined wihtout triggering re-defined errors. + (when (and (get-in @st [::ana/namespaces name :defs]) + (not ('#{cljs.core cljs.core$macros} name))) + (swap! st update ::ana/namespaces dissoc name))) + +(defn- post-file-side-effects [file opts] (when (:verbose opts) (debug-prn "Post-file side-effects" file)) @@ -279,14 +289,16 @@ (if resource (let [{:keys [lang source cache source-map file]} resource] (condp = lang - :clj (eval-str* bound-vars source name (assoc opts :cljs-file file) - (fn [res] - (post-file-side-effects file opts) - (if (:error res) - (cb res) - (do - (swap! *loaded* conj aname) - (cb {:value true}))))) + :clj (do + (pre-file-side-effects (:*compiler* bound-vars) aname file opts) + (eval-str* bound-vars source name (assoc opts :cljs-file file) + (fn [res] + (post-file-side-effects file opts) + (if (:error res) + (cb res) + (do + (swap! *loaded* conj aname) + (cb {:value true})))))) :js (process-macros-deps bound-vars cache opts (fn [res] (if (:error res) @@ -416,12 +428,14 @@ (if resource (let [{:keys [name lang source file]} resource] (condp = lang - :clj (analyze-str* bound-vars source name (assoc opts :cljs-file file) - (fn [res] - (post-file-side-effects file opts) - (if-not (:error res) - (analyze-deps bound-vars ana-env lib (next deps) opts cb) - (cb res)))) + :clj (do + (pre-file-side-effects (:*compiler* bound-vars) name file opts) + (analyze-str* bound-vars source name (assoc opts :cljs-file file) + (fn [res] + (post-file-side-effects file opts) + (if-not (:error res) + (analyze-deps bound-vars ana-env lib (next deps) opts cb) + (cb res))))) :js (analyze-deps bound-vars ana-env lib (next deps) opts cb) (wrap-error (ana/error ana-env diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index ec25ab313..84b4fb391 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -850,6 +850,54 @@ (fn [{:keys [value error]}] (is (= value [6 6])))))))) +(deftest test-cljs-1854 + (let [st (cljs/empty-state)] + (cljs/eval st + '(require 'foo.core1854) + {:eval node-eval + :context :expr + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 1)"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval st + 'foo.core1854/x + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (nil? error)) + (is (= value 1)))) + (cljs/eval st + '(require 'foo.core1854 :reload) + {:eval node-eval + :context :expr + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 2)"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval st + 'foo.core1854/x + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (nil? error)) + (is (= value 2)))) + (cljs/eval st + '(require 'bar.core1854 :reload-all) + {:eval node-eval + :context :expr + :load (fn [{:keys [name]} cb] + (case name + bar.core1854 (cb {:lang :clj :source "(ns bar.core1854 (:require [foo.core1854]))"}) + foo.core1854 (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 3)"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval st + 'foo.core1854/x + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (nil? error)) + (is (= value 3)))))))))))) + (deftest test-cljs-1874 (async done (let [st (cljs/empty-state) From 035929110f430f2ead4e3e73224303f55a366264 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jul 2017 11:13:10 -0400 Subject: [PATCH 2620/4033] always disable non-standard JS Doc warnings for JS modules add :watch to list of compiler options --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a92571675..ad37236fa 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -167,7 +167,7 @@ :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs - :watch-error-fn :watch-fn :install-deps}) + :watch :watch-error-fn :watch-fn :install-deps}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -1620,6 +1620,7 @@ (select-keys [:closure-warnings :closure-extra-annotations :pretty-print :language-in :language-out :closure-module-roots :rewrite-polyfills]) + (assoc-in [:closure-warnings :non-standard-jsdoc] :off) (set-options (CompilerOptions.)))) (defn get-js-root [closure-compiler] From 5fda6710e15ec3117c51404eb6c61db0ca610ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Fri, 28 Jul 2017 10:19:59 -0700 Subject: [PATCH 2621/4033] CLJS-2279: Infer `:module-type ` for provided `node_modules` --- src/main/clojure/cljs/closure.clj | 5 +++-- src/test/clojure/cljs/build_api_tests.clj | 6 +++--- src/test/clojure/cljs/closure_tests.clj | 24 +++++++++++------------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ad37236fa..b486a9c3e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1660,6 +1660,7 @@ (let [^List externs '() ^List source-files (get-source-files js-modules opts) ^CompilerOptions options (doto (make-convert-js-module-options opts) + (.setProcessCommonJSModules true) (.setLanguageIn (lang-key->lang-mode :ecmascript6)) (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) closure-compiler (doto (make-closure-compiler) @@ -2172,7 +2173,7 @@ (map (fn [{:strs [file provides]}] file (merge {:file file - :module-type :commonjs} + :module-type :es6} (when provides {:provides provides})))) (next (json/read-str (str iw)))) @@ -2226,7 +2227,7 @@ (map (fn [path] (merge {:file path - :module-type :commonjs} + :module-type :es6} (when-not (package-json? path) (let [pkg-json-main (some (fn [[pkg-json-path {:strs [main name]}]] diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 510b612b9..f02ec5623 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -430,19 +430,19 @@ :react-dom "15.6.1"}) (let [modules (build/get-node-deps '[react "react-dom/server"])] (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react/react.js")) :provides ["react" "react/react.js" "react/react"]})) modules))) (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react/lib/React.js")) :provides ["react/lib/React.js" "react/lib/React"]})) modules))) (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) :provides ["react-dom/server.js" "react-dom/server"]})) modules)))) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 3dd55916e..132c14e5b 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -90,7 +90,7 @@ (closure/maybe-install-node-deps! {:npm-deps {:left-pad "1.1.3"}}) (let [modules (closure/index-node-modules-dir)] (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/left-pad/index.js")) :provides ["left-pad/index.js" "left-pad/index" @@ -102,19 +102,19 @@ :react-dom "15.6.1"}}) (let [modules (closure/index-node-modules-dir)] (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react/react.js")) :provides ["react/react.js" "react/react" "react"]})) modules))) (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react/lib/React.js")) :provides ["react/lib/React.js" "react/lib/React"]})) modules))) (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) :provides ["react-dom/server.js" "react-dom/server"]})) modules)))) @@ -123,7 +123,7 @@ (closure/maybe-install-node-deps! {:npm-deps {:node-fetch "1.7.1"}}) (let [modules (closure/index-node-modules-dir)] (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/node-fetch/lib/index.js")) :provides ["node-fetch/lib/index.js" "node-fetch/lib/index" @@ -136,7 +136,7 @@ (is (true? (some (fn [module] (= module {:file (.getAbsolutePath (io/file "node_modules/@comandeer/css-filter/dist/css-filter.umd.js")) - :module-type :commonjs + :module-type :es6 :provides ["@comandeer/css-filter/dist/css-filter.umd.js" "@comandeer/css-filter/dist/css-filter.umd" "@comandeer/css-filter"]})) @@ -152,7 +152,7 @@ (test/delete-out-files out) (closure/maybe-install-node-deps! opts) (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/left-pad/index.js")) :provides ["left-pad" "left-pad/index.js" @@ -166,19 +166,19 @@ _ (closure/maybe-install-node-deps! opts) modules (closure/index-node-modules ["react" "react-dom" "react-dom/server"] opts)] (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react/react.js")) :provides ["react" "react/react.js" "react/react"]})) modules))) (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react/lib/React.js")) :provides ["react/lib/React.js" "react/lib/React"]})) modules))) (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) :provides ["react-dom/server.js" "react-dom/server"]})) modules)))) @@ -188,7 +188,7 @@ (let [opts {:npm-deps {:node-fetch "1.7.1"}}] (closure/maybe-install-node-deps! opts) (is (true? (some (fn [module] - (= module {:module-type :commonjs + (= module {:module-type :es6 :file (.getAbsolutePath (io/file "node_modules/node-fetch/lib/index.js")) :provides ["node-fetch/lib/index.js" "node-fetch/lib/index" @@ -202,7 +202,7 @@ (is (true? (some (fn [module] (= module {:file (.getAbsolutePath (io/file "node_modules/@comandeer/css-filter/dist/css-filter.umd.js")) - :module-type :commonjs + :module-type :es6 :provides ["@comandeer/css-filter" "@comandeer/css-filter/dist/css-filter.umd.js" "@comandeer/css-filter/dist/css-filter.umd"]})) From 307b860005f3527f3997fdb64749cb82f96e83d5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 28 Jul 2017 14:53:49 -0400 Subject: [PATCH 2622/4033] fix cljs.build.api/install-node-deps! and cljs.build.api/get-node-deps --- src/main/clojure/cljs/build/api.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 46fa471d6..a66849da9 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -231,7 +231,8 @@ ([dependencies opts] {:pre [(map? dependencies)]} (closure/check-npm-deps opts) - (closure/maybe-install-node-deps! (merge opts {:npm-deps dependencies})))) + (closure/maybe-install-node-deps! + (update-in opts [:npm-deps] merge dependencies)))) (defn get-node-deps "EXPERIMENTAL: Get the Node.js dependency graph of the supplied dependencies. @@ -245,7 +246,9 @@ (:options @env/*compiler*)))) ([dependencies opts] {:pre [(sequential? dependencies)]} - (closure/index-node-modules dependencies opts))) + (closure/index-node-modules + (distinct (concat (keys (:npm-deps opts)) (map str dependencies))) + opts))) (comment (node-module-deps From 01884de5cf39ef3e49b229bf042f51ee9d008f7e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 28 Jul 2017 14:54:01 -0400 Subject: [PATCH 2623/4033] add process.env shim ns --- src/main/cljs/process/env.cljs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/cljs/process/env.cljs diff --git a/src/main/cljs/process/env.cljs b/src/main/cljs/process/env.cljs new file mode 100644 index 000000000..c8a6909eb --- /dev/null +++ b/src/main/cljs/process/env.cljs @@ -0,0 +1,12 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns process.env + "A shim namespace for the Node.js process library") + +(goog-define NODE_ENV "development") From 3b497115019b46f8a0f7137efc86e4a1cd564868 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 28 Jul 2017 15:22:54 -0400 Subject: [PATCH 2624/4033] change cljs.build.api/install-node-deps! and cljs.build.api/get-deps so they can take compiler opts directly --- src/main/clojure/cljs/build/api.clj | 34 ++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index a66849da9..e133d2ac1 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -221,13 +221,24 @@ ;; ============================================================================= ;; Node.js / NPM dependencies +(defn compiler-opts? [m] + (and (map? m) + (or (contains? m :output-to) + (contains? m :modules) + (contains? m :npm-deps) + (contains? m :main) + (contains? m :optimizations) + (contains? m :foreign-libs)))) + (defn install-node-deps! "EXPERIMENTAL: Install the supplied dependencies via NPM. dependencies must be - a map of name to version." + a map of name to version or a valid compiler options map." ([dependencies] - (install-node-deps! dependencies - (when-not (nil? env/*compiler*) - (:options @env/*compiler*)))) + (if (compiler-opts? dependencies) + (install-node-deps! (:npm-deps dependencies) dependencies) + (install-node-deps! dependencies + (when-not (nil? env/*compiler*) + (:options @env/*compiler*))))) ([dependencies opts] {:pre [(map? dependencies)]} (closure/check-npm-deps opts) @@ -237,13 +248,16 @@ (defn get-node-deps "EXPERIMENTAL: Get the Node.js dependency graph of the supplied dependencies. Dependencies must be a sequence of strings or symbols naming packages or paths - within packages (e.g. [react \"react-dom/server\"]. Assumes dependencies have - been been previously installed, either by `cljs.build.api/install-node-deps!` - or by an NPM client, and reside in the `node_modules` directory." + within packages (e.g. [react \"react-dom/server\"] or a valid compiler options + map. Assumes dependencies have been been previously installed, either by + `cljs.build.api/install-node-deps!` or by an NPM client, and reside in the + `node_modules` directory." ([dependencies] - (get-node-deps dependencies - (when-not (nil? env/*compiler*) - (:options @env/*compiler*)))) + (if (compiler-opts? dependencies) + (get-node-deps (keys (:npm-deps dependencies)) dependencies) + (get-node-deps + (when-not (nil? env/*compiler*) + (:options @env/*compiler*))))) ([dependencies opts] {:pre [(sequential? dependencies)]} (closure/index-node-modules From 6ef533563c38c5f26cbbd00acfcd9dad6e77ef64 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 28 Jul 2017 15:34:22 -0400 Subject: [PATCH 2625/4033] fix typo in cljs.build.api/get-node-deps --- src/main/clojure/cljs/build/api.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index e133d2ac1..5766cd8be 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -255,7 +255,7 @@ ([dependencies] (if (compiler-opts? dependencies) (get-node-deps (keys (:npm-deps dependencies)) dependencies) - (get-node-deps + (get-node-deps dependencies (when-not (nil? env/*compiler*) (:options @env/*compiler*))))) ([dependencies opts] From 537f60c975a29983c62647b4ea67b0bc08979366 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 28 Jul 2017 16:08:27 -0400 Subject: [PATCH 2626/4033] CLJS-2280: Provide process.env :preload and auto-configure --- src/main/clojure/cljs/closure.clj | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b486a9c3e..16dcc2846 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -167,7 +167,7 @@ :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs - :watch :watch-error-fn :watch-fn :install-deps}) + :watch :watch-error-fn :watch-fn :install-deps :process-shim}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -1955,7 +1955,9 @@ (format ":nodejs target with :none optimizations requires a :main entry"))) (defn check-preloads [{:keys [preloads optimizations] :as opts}] - (when (and (some? preloads) (not= optimizations :none)) + (when (and (some? preloads) + (not= preloads '[process.env]) + (not= optimizations :none)) (binding [*out* *err*] (println "WARNING: :preloads should only be specified with :none optimizations")))) @@ -2038,18 +2040,28 @@ (update opts :modules #(ensure-cljs-base-module % opts))) +(defn shim-process? [opts] + (not (false? (:process-shim opts)))) + (defn add-implicit-options [{:keys [optimizations output-dir] :or {optimizations :none output-dir "out"} :as opts}] (let [opts (cond-> opts - (:closure-defines opts) - (assoc :closure-defines - (into {} - (map (fn [[k v]] - [(if (symbol? k) (str (comp/munge k)) k) v]) - (:closure-defines opts)))) + (shim-process? opts) + (-> (update-in [:preloads] (fnil conj []) 'process.env) + (cond-> + (not= :none optimizations) + (update-in [:closure-defines 'process.env/NODE_ENV] (fnil str "production")))) + + (or (:closure-defines opts) (shim-process? opts)) + (update :closure-defines + (fn [defines] + (into {} + (map (fn [[k v]] + [(if (symbol? k) (str (comp/munge k)) k) v]) + defines)))) (:browser-repl opts) (update-in [:preloads] (fnil conj []) 'clojure.browser.repl.preload)) {:keys [libs foreign-libs externs]} (get-upstream-deps) From 90dbbf889580e4914a16466440e83f982e27f3ed Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 28 Jul 2017 17:13:02 -0400 Subject: [PATCH 2627/4033] 1.9.854 --- README.md | 4 +- changes.md | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc5c3bede..9e9cd9cd0 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.671 +Latest stable release: 1.9.854 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.671"] +[org.clojure/clojurescript "1.9.854"] ``` [Maven](http://maven.apache.org) dependency information: diff --git a/changes.md b/changes.md index a7c3e357a..7ea76296d 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,125 @@ +## 1.9.854 + +### Enhancements +* CLJS-2280: Provide process.env :preload and auto-configure +* CLJS-2279: Infer `:module-type ` for provided `node_modules` +* CLJS-2250: Support :foreign-libs overrides via :provides +* CLJS-2243: Self-host: Add support for :global-exports +* CLJS-2232: Self-host: Add support for string-based requires +* add *print-fn-bodies* knob, set to false +* CLJS-2198: Safe array operations +* CLJS-2217: Support `:rename` for JS modules +* CLJS-2214: Support :global-exports for foreign libraries +* CLJS-1428: Add a cljs.core/*command-line-args* var +* CLJS-2061: Support ns :require for JS libs, allow strings along with symbol +* CLJS-2148: Add warnings for invalid use of aget and aset +* CLJS-2143: Add support for symbol preprocess values + +### Changes +* CLJS-2273: Bump tools.reader to 1.0.3 and development dependencies +* CLJS-2235: Allow passing extra maven opts to build scripts +* CLJS-2267: Allow ^:const inlined vars to affect if emission +* CLJS-2245: Add support for using a local `node_modules` installation through a new `:node-modules` compiler flag +* CLJS-2002: Don't throw when no *print-fn* is set +* support Clojure primitive array type hints, core.async no longer + gives warnings +* CLJS-2213: Node.js target should use node_modules index to emit platform specific require +* CLJS-2200: bump to tools.reader 1.0.2 +* CLJS-2135: require macro prints last result of loaded-libs +* CLJS-2192: Add ChakraCore testing facilities +* CLJS-1800: Defer to tools.reader for cljs.reader functionality +* CLJS-2163: Clean up uses of aget / aset on objects +* CLJS-2184: Add `ns-publics` and `ns-imports` +* CLJS-2183: Assert arguments are quoted symbols in some core macros +* CLJS-2182: Assert argument to resolve is a quoted symbol +* CLJS-2186: Update docstrings for aget/aset to be consistent with Clojure +* CLJS-2180: Allow compiling `:modules` with whitespace optimizations +* CLJS-1822: Use `:file-min` when processing JS modules with advanced optimizations +* CLJS-2169: Error when compiling with :source-map and advanced optimizations +* CLJS-2037: Throw if overwriting alias in current namespace +* CLJS-2160: Add loaded? and prefetch functions to cljs.loader +* CLJS-2148: Add unsafe-get and use goog.object +* CLJS-2161: Bump Closure Compiler to June 2017 release + +### Fixes +* CLJS-1854: Self-host: Reload ns with const +* CLJS-2278: JavaScript object literals are printed wth keys that cannot be read +* CLJS-2276: Self-host: Need test.check dep for CLJS-2275 +* CLJS-2275: cljs.spec.alpha/fdef resolves eagerly +* CLJS-2259: Extra .cljs_node_repl directory containing cljs.core output +* CLJS-2274: Update CI script to install deps +* CLJS-2269: Warn on top level code split loads +* CLJS-2272: Tests that depended on default install deps behavior failing +* CLJS-2255: Clean up :npm-deps +* CLJS-2263: Docstring for neg-int? backwards +* CLJS-2262: Correct comment that *warn-on-infer* is file-scope +* CLJS-2258: Stack overflow regression for sequence xform applied to eduction +* CLJS-2256: Generated code doesn't add newline after sourceMappingURL comment +* CLJS-2254: Module Indexing: Provide relative paths for a package's main module +* CLJS-2248: Build API tests rely on Yarn +* CLJS-2239: Self-host: Add `:target :nodejs` to the docstrings in cljs.js +* CLJS-2251: Follow-up fix to CLJS-2249 and related commit +* CLJS-2249: Provide a test for d4b871cce73 +* CLJS-2246: Revert CLJS-2245 and CLJS-2240 and fix `lein test` +* CLJS-2244: Orphaned processed JS modules breaks :modules +* CLJS-2242: Lots of undeclared Var warns in cljs.spec.gen.alpha +* CLJS-2241: Multiple requires of Node.js modules in non :nodejs target are not idempotent at the REPL +* CLJS-2229: Ensure that new modules work works correctly with REPLs +* CLJS-2238: Perf regression with node module indexing +* CLJS-2240: don't shell out to module_deps.js if `:npm-deps` not specified +* CLJS-2230: Double checked arrays +* CLJS-2227: Squelch some of the array access tests +* CLJS-2228: Port CLJS-2226 to module_deps.js +* CLJS-1955: data_readers.cljc can't reference handlers in user code +* CLJS-2225: Need to add :checked-arrays to known compiler opts +* CLJS-2226: :npm-deps can't index scoped packages +* CLJS-2224: Resolve-var is wrong wrt. module resolution +* CLJS-2223: Self-host: Undeclared Var deps/native-node-modules +* CLJS-2222: CI failing after CLJS-2217 +* CLJS-2219: Enable JSC under test-simple +* CLJS-2218: Make ClojureScript aware of native node modules +* CLJS-2220: Add runtime :npm-deps tests +* CLJS-2212: Replace missing-js-modules with new index-node-modules-dir +* CLJS-2211: Add function to index a top-level node_modules installation +* CLJS-2208: module_deps.js is not compatible with older JS implementations +* CLJS-2207: cljs.test/js-filename is using non-portable .endsWith +* CLJS-1764: Double warning for undeclared Var (REPL only) +* CLJS-2204: Tests failing with respect to lodash/array namespace +* CLJS-2205: NPM deps: Correctly compute `:provides` if file ends in `index.js` +* CLJS-2203: REPL is turning on all warnings by default (including :invalid-array-access) +* CLJS-2201: Self-host: test-js-filename failing +* CLJS-2202: String requires should work from Cljs files in classpath +* CLJS-2199: String requires broken after recompile +* CLJS-2172: memfn docstring refers to Java and reflection +* CLJS-1959: under :nodejs target we should provide __dirname and __filename constants +* CLJS-1966: cljs.test assumes the output directory is '/out/' when determining the filename for a failed or errored test result. +* CLJS-2191: Clean up doc references to clojure.spec.* in favor of cljs.spec.* +* CLJS-2194: cljs.util/relative-name bug +* CLJS-2195: npm-deps tests are not idempotent +* CLJS-2179: Add test for preprocess JS module as symbol +* CLJS-2152: "is not a relative path" exception thrown when `:libs` directory is provided. +* CLJS-2193: :npm-deps dependencies are implicit +* CLJS-1797: Update aot_core to support build with MINGW on Windows +* CLJS-2189: Add test for :preloads +* CLJS-2188: Use :invalid-array-access instead of :invalid-aget / :invalid-aset +* CLJS-2181: Can't compile string sources with modules +* CLJS-2185: Self-host: Docstrings for bootstrap helpers +* CLJS-2178: Add tests for `:npm-deps` +* CLJS-2177: NPM deps & JS modules fixes for Windows +* CLJS-2175: ES6 Module processing broken with Closure v20170626 +* CLJS-2175: Add test to check ES6 module processing works +* CLJS-2176: module_deps.js: fix regexes for Windows paths +* CLJS-2173: Fix `npm install` when `:npm-deps` in Windows +* CLJS-2164: Require cljs.js results in warning about new unsafe-get macro +* CLJS-1998: Printing an Object with a null prototype throws an error +* CLJS-2158: cljs_base module generates empty goog.require +* CLJS-2157: Automatically generate cljs.loader/set-loaded! call +* CLJS-2154: Provide compiler info & timing when compiling modules +* CLJS-2151: Rollback removal of dependency information for node targeted compilation +* CLJS-2141: Self-host: cljs.js is using undeclared symbol lib +* CLJS-2145: inode_find issue with hash-map +* CLJS-2142: Can't instrument a namespace containing constants + ## 1.9.671 ### Fixes From 33cefec1a786ca856ed903b89db43daac6b25bae Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 29 Jul 2017 08:50:01 -0400 Subject: [PATCH 2628/4033] CLJS-2283: Regression with js-obj and gobject alias --- src/main/clojure/cljs/core.cljc | 4 ++-- src/test/cljs/cljs/macro_test.cljs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 78b298ec1..c64ac1807 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2552,8 +2552,8 @@ (js-obj* '()) `(let [~@(apply concat (clojure.set/map-invert expr->local)) ~obj ~(js-obj* (filter-on-keys core/string? kvs))] - ~@(map (core/fn [[k v]] `(gobject/set ~obj ~k ~v)) sym-pairs) - ~@(map (core/fn [[k v]] `(gobject/set ~obj ~v ~(core/get kvs k))) expr->local) + ~@(map (core/fn [[k v]] `(goog.object/set ~obj ~k ~v)) sym-pairs) + ~@(map (core/fn [[k v]] `(goog.object/set ~obj ~v ~(core/get kvs k))) expr->local) ~obj)))) (core/defmacro alength [a] diff --git a/src/test/cljs/cljs/macro_test.cljs b/src/test/cljs/cljs/macro_test.cljs index cadced9b2..f8f66088f 100644 --- a/src/test/cljs/cljs/macro_test.cljs +++ b/src/test/cljs/cljs/macro_test.cljs @@ -19,3 +19,6 @@ (is (= '(if true (do 1)) (macroexpand-1 '(when true 1)))) (is (= 1 (macroexpand '1))) (is (= '(if true (do 1)) (macroexpand '(when true 1))))) + +(deftest test-cljs-2283 + (is (= ":a" (first (js-keys (js-obj :a 1)))))) From ddedc7a3535be9d9c887879076f1033829201c1e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 29 Jul 2017 08:27:46 -0400 Subject: [PATCH 2629/4033] CLJS-2282: Some valid keywords are strings in JS object literals --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 01bf4e70a..fe8772807 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9636,7 +9636,7 @@ reduces them without incurring seq initialization" (-write writer "#js ") (print-map (map (fn [k] - [(cond-> k (some? (re-matches #"[A-Za-z][\w\*\+\?!\-']*" k)) keyword) (unchecked-get obj k)]) + [(cond-> k (some? (re-matches #"[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*" k)) keyword) (unchecked-get obj k)]) (js-keys obj)) pr-writer writer opts)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 743b38ea9..253a7d5aa 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1444,6 +1444,10 @@ (is (= "#js {:abc-def 1}" (pr-str #js {"abc-def" 1}))) (is (= "#js {:x*+?!-' 1}" (pr-str #js {"x*+?!-'" 1})))) +(deftest test-cljs-2282 + (is (= "#js {:_abc 1}" (pr-str #js {"_abc" 1}))) + (is (= "#js {:*compiler* 1}" (pr-str #js {"*compiler*" 1})))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From e0ec29a4bb7c755b5a4459ddd3a2fc85055ddb4e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 29 Jul 2017 11:12:29 -0400 Subject: [PATCH 2630/4033] update Maven dependency information --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e9cd9cd0..0977c4fa2 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Latest stable release: 1.9.854 org.clojure clojurescript - 1.9.670 + 1.9.854 ``` From 97459100a9c71c74a585a4de523ec4a6e3f4dc7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 29 Jul 2017 16:06:26 -0700 Subject: [PATCH 2631/4033] CLJS-2284: Fix build API tests not to pollute `out` in the current directory use tmpdir instead --- src/test/clojure/cljs/build_api_tests.clj | 109 ++++++++++++---------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index f02ec5623..5ba362a3e 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -117,14 +117,17 @@ [graph.bar.core :as bar])))))) (deftest cljs-1469 - (let [srcs "samples/hello/src" + (let [out (.getPath (io/file (test/tmp-dir) "loader-test-out")) + srcs "samples/hello/src" [common-tmp app-tmp] (mapv #(File/createTempFile % ".js") - ["common" "app"]) + ["common" "app"]) opts {:optimizations :simple + :output-dir out :modules {:common {:entries #{"hello.foo.bar"} :output-to (.getAbsolutePath common-tmp)} :app {:entries #{"hello.core"} :output-to (.getAbsolutePath app-tmp)}}}] + (test/delete-out-files out) (.deleteOnExit common-tmp) (.deleteOnExit app-tmp) (is (every? #(zero? (.length %)) [common-tmp app-tmp]) @@ -161,16 +164,17 @@ (str foreign-lib-file)))))) (deftest cljs-1537-circular-deps - (let [out-file (io/file "out/main.js") + (let [out (.getPath (io/file (test/tmp-dir) "cljs-1537-test-out")) + out-file (io/file out "main.js") root "src/test/cljs_build"] - (.delete out-file) + (test/delete-out-files out) (try (build/build (build/inputs (io/file (str root "a.cljs")) (io/file (str root "b.cljs"))) {:main 'circular-deps.a :optimizations :none - :output-to "out"}) + :output-to out}) (is false) (catch Throwable e (is true))))) @@ -190,22 +194,23 @@ :entries #{'loader-test.bar}}}}}) (deftest cljs-2077-test-loader - (test/delete-out-files) - (let [{:keys [inputs opts]} (merge-with merge (loader-test-project "out")) - loader (io/file "out" "cljs" "loader.js")] - (build/build (build/inputs (io/file inputs "bar.cljs") (io/file inputs "foo.cljs")) opts) - (is (.exists loader)) - (is (not (nil? (re-find #"/loader_test/foo\.js" (slurp loader)))))) - (test/delete-out-files) - (let [project (merge-with merge (loader-test-project "out") - {:opts {:optimizations :advanced - :source-map true}})] - (build/build (build/inputs (:inputs project)) (:opts project))) - (testing "string inputs in modules" - (test/delete-out-files) - (let [project (merge-with merge (loader-test-project "out") - {:opts {:optimizations :whitespace}})] - (build/build (build/inputs (:inputs project)) (:opts project))))) + (let [out (.getPath (io/file (test/tmp-dir) "loader-test-out"))] + (test/delete-out-files out) + (let [{:keys [inputs opts]} (merge-with merge (loader-test-project out)) + loader (io/file out "cljs" "loader.js")] + (build/build (build/inputs (io/file inputs "bar.cljs") (io/file inputs "foo.cljs")) opts) + (is (.exists loader)) + (is (not (nil? (re-find #"/loader_test/foo\.js" (slurp loader)))))) + (test/delete-out-files out) + (let [project (merge-with merge (loader-test-project out) + {:opts {:optimizations :advanced + :source-map true}})] + (build/build (build/inputs (:inputs project)) (:opts project))) + (testing "string inputs in modules" + (test/delete-out-files out) + (let [project (merge-with merge (loader-test-project out) + {:opts {:optimizations :whitespace}})] + (build/build (build/inputs (:inputs project)) (:opts project)))))) (deftest test-npm-deps (test/delete-node-modules) @@ -420,31 +425,37 @@ (test/delete-node-modules)) (deftest test-deps-api-cljs-2255 - (test/delete-node-modules) - (spit (io/file "package.json") "{}") - (build/install-node-deps! {:left-pad "1.1.3"}) - (is (.exists (io/file "node_modules/left-pad/package.json"))) - (test/delete-node-modules) - (spit (io/file "package.json") "{}") - (build/install-node-deps! {:react "15.6.1" - :react-dom "15.6.1"}) - (let [modules (build/get-node-deps '[react "react-dom/server"])] - (is (true? (some (fn [module] - (= module {:module-type :es6 - :file (.getAbsolutePath (io/file "node_modules/react/react.js")) - :provides ["react" - "react/react.js" - "react/react"]})) - modules))) - (is (true? (some (fn [module] - (= module {:module-type :es6 - :file (.getAbsolutePath (io/file "node_modules/react/lib/React.js")) - :provides ["react/lib/React.js" "react/lib/React"]})) - modules))) - (is (true? (some (fn [module] - (= module {:module-type :es6 - :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) - :provides ["react-dom/server.js" "react-dom/server"]})) - modules)))) - (test/delete-node-modules) - (.delete (io/file "package.json"))) + (let [out (.getPath (io/file (test/tmp-dir) "cljs-2255-test-out"))] + (test/delete-out-files out) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (build/install-node-deps! {:left-pad "1.1.3"} {:output-dir out}) + (is (.exists (io/file "node_modules/left-pad/package.json"))) + (test/delete-out-files out) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (build/install-node-deps! + {:react "15.6.1" + :react-dom "15.6.1"} + {:output-dir out}) + (let [modules (build/get-node-deps '[react "react-dom/server"] {:output-dir out})] + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/react/react.js")) + :provides ["react" + "react/react.js" + "react/react"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/react/lib/React.js")) + :provides ["react/lib/React.js" "react/lib/React"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) + :provides ["react-dom/server.js" "react-dom/server"]})) + modules)))) + (test/delete-out-files out) + (test/delete-node-modules) + (.delete (io/file "package.json")))) From da9dd66458aed0bed95f2c0f4d9cd2d6ba36c2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sat, 29 Jul 2017 16:37:20 -0700 Subject: [PATCH 2632/4033] CLJS-2281: module_deps.js cannot compute inputs for ES6 sources --- src/main/cljs/cljs/module_deps.js | 43 ++++++++++++++++------- src/main/clojure/cljs/build/api.clj | 3 +- src/main/clojure/cljs/closure.clj | 9 +++-- src/test/clojure/cljs/build_api_tests.clj | 3 +- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index e46b015ac..2a1f223a9 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -1,7 +1,8 @@ var path = require('path'); -var mdeps = require('module-deps'); +var mdeps = require('@cljs-oss/module-deps'); var nodeResolve = require('resolve'); var browserResolve = require('browser-resolve'); +var konan = require('konan'); var target = 'CLJS_TARGET'; var filename = path.resolve(__dirname, 'JS_FILE'); @@ -11,18 +12,25 @@ var md = mdeps({ resolve: function(id, parent, cb) { // set the basedir properly so we don't try to resolve requires in the Closure // Compiler processed `node_modules` folder. - parent.basedir = parent.filename === filename ? __dirname: path.dirname(parent.filename); + parent.basedir = + parent.filename === filename ? __dirname : path.dirname(parent.filename); resolver(id, parent, cb); }, filter: function(id) { return !nodeResolve.isCore(id); -}}); + }, + detect: function(src) { + var deps = konan(src); + + return deps.strings; + }, +}); var pkgJsons = []; var deps_files = {}; -md.on('package', function (pkg) { +md.on('package', function(pkg) { // we don't want to include the package.json for users' projects if (/node_modules/.test(pkg.__dirname)) { var pkgJson = { @@ -30,7 +38,7 @@ md.on('package', function (pkg) { }; if (pkg.name != null) { - pkgJson.provides = [ pkg.name ]; + pkgJson.provides = [pkg.name]; } if (pkg.main != null) { @@ -61,20 +69,29 @@ md.on('end', function() { var dep = deps_files[key]; // add provides to files that are not `package.json`s - if (!/node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$/.test(dep.file)) { + if ( + !/node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$/.test( + dep.file + ) + ) { if (dep.file.indexOf('node_modules') !== -1) { var providedModule = dep.file - .substring(dep.file.lastIndexOf('node_modules')) - .replace('\\', '/') - .replace('node_modules/', ''); + .substring(dep.file.lastIndexOf('node_modules')) + .replace('\\', '/') + .replace('node_modules/', ''); dep.provides = dep.provides || []; - dep.provides.push(providedModule, providedModule.replace(/\.js(on)?$/, '')); + dep.provides.push( + providedModule, + providedModule.replace(/\.js(on)?$/, '') + ); - var indexReplaced = providedModule.replace(/\/index\.js(on)?$/,''); + var indexReplaced = providedModule.replace(/\/index\.js(on)?$/, ''); - if (/\/index\.js(on)?$/.test(providedModule) && - dep.provides.indexOf(indexReplaced) === -1) { + if ( + /\/index\.js(on)?$/.test(providedModule) && + dep.provides.indexOf(indexReplaced) === -1 + ) { dep.provides.push(indexReplaced); } } diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 5766cd8be..03dc1050b 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -275,7 +275,8 @@ (defn node-inputs "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry points. Assumes - that the module-deps NPM packages is either locally or globally installed." + that the `@cljs-oss/module-deps` and `konan` NPM packages are either + locally or globally installed." [entries] (closure/node-inputs entries)) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 16dcc2846..1ff9f3dc3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2134,7 +2134,8 @@ (when-not (.exists pkg-json) (spit pkg-json "{}")) (let [proc (-> (ProcessBuilder. - (into (cond->> ["npm" "install" "module-deps" "resolve" "browser-resolve"] + (into (cond->> ["npm" "install" "@cljs-oss/module-deps" + "resolve" "browser-resolve" "konan"] util/windows? (into ["cmd" "/c"])) (map (fn [[dep version]] (str (name dep) "@" version))) npm-deps)) @@ -2158,7 +2159,8 @@ (defn node-module-deps "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry point. Assumes - that the module-deps NPM package is either locally or globally installed." + that the `@cljs-oss/module-deps` and `konan` NPM packages are either + locally or globally installed." ([entry] (node-module-deps entry (when env/*compiler* @@ -2197,7 +2199,8 @@ (defn node-inputs "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry points. Assumes - that the module-deps NPM packages is either locally or globally installed." + that the `@cljs-oss/module-deps` and `konan` NPM packages are either + locally or globally installed." ([entries] (node-inputs entries (when env/*compiler* diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 5ba362a3e..a57de9200 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -402,7 +402,8 @@ (test/delete-node-modules) (.delete (io/file "package-lock.json")) (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} - :devDependencies {:module-deps "*" + :devDependencies {"@cljs-oss/module-deps" "*" + :konan "*" :resolve "*" :browser-resolve "*"}})) (sh/sh "npm" "install") From 10fcec603bd36680104121aa0e5bd1237a83a680 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 1 Aug 2017 07:56:48 -0400 Subject: [PATCH 2633/4033] exclude `cljs.core` from :reload-all --- src/main/clojure/cljs/compiler.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index eed11118e..8ff84b8ff 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1077,8 +1077,8 @@ [nil libs])) {global-exports-libs true libs-to-load false} (group-by ana/dep-has-global-exports? libs-to-load)] (when (-> libs meta :reload-all) - (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set();") - (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set();")) + (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set([\"cljs.core\"]);") + (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set([\"cljs.core\"]);")) (doseq [lib libs-to-load] (cond #?@(:clj From b17c18604daf97fa6ca54d226bdfedcfd7ad6a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 31 Jul 2017 20:36:19 -0700 Subject: [PATCH 2634/4033] CLJS-2290: Node packages using require('assert') fail compilation --- src/main/cljs/cljs/module_deps.js | 2 +- src/test/clojure/cljs/closure_tests.clj | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 2a1f223a9..45378538c 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -18,7 +18,7 @@ var md = mdeps({ resolver(id, parent, cb); }, filter: function(id) { - return !nodeResolve.isCore(id); + return !(target === 'nodejs' && nodeResolve.isCore(id)); }, detect: function(src) { var deps = konan(src); diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 132c14e5b..2dc0644d4 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -185,7 +185,8 @@ (test/delete-node-modules) (spit (io/file "package.json") "{}") (test/delete-out-files out) - (let [opts {:npm-deps {:node-fetch "1.7.1"}}] + (let [opts {:npm-deps {:node-fetch "1.7.1"} + :target :nodejs}] (closure/maybe-install-node-deps! opts) (is (true? (some (fn [module] (= module {:module-type :es6 From 9ac09b2d0bcf6155302c2054d92d150d2d29d990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 30 Jul 2017 21:41:05 -0700 Subject: [PATCH 2635/4033] CLJS-2287: Self-host: `require` prints result of loading node deps / global exports --- src/main/cljs/cljs/js.cljs | 27 ++++++++++++---------- src/test/self/self_host/test.cljs | 38 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index fe2cc7a45..36bc5a391 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -590,16 +590,18 @@ (cb {:value ast})))) (defn- node-side-effects - [bound-vars sb deps ns-name] + [bound-vars sb deps ns-name emit-nil-result?] (doseq [dep deps] (.append sb (with-out-str (comp/emitln (munge ns-name) "." (ana/munge-node-lib dep) - " = require('" dep "');"))))) + " = require('" dep "');")))) + (when (and (seq deps) emit-nil-result?) + (.append sb "null;"))) (defn- global-exports-side-effects - [bound-vars sb deps ns-name] + [bound-vars sb deps ns-name emit-nil-result?] (let [{:keys [js-dependency-index]} @(:*compiler* bound-vars)] (doseq [dep deps] (let [{:keys [global-exports]} (get js-dependency-index (name dep))] @@ -607,7 +609,9 @@ (with-out-str (comp/emitln (munge ns-name) "." (ana/munge-global-export dep) - " = goog.global." (get global-exports (symbol dep)) ";"))))))) + " = goog.global." (get global-exports (symbol dep)) ";"))))) + (when (and (seq deps) emit-nil-result?) + (.append sb "null;")))) (defn- analyze-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) @@ -767,10 +771,11 @@ (.append sb (with-out-str (comp/emitln (str "goog.provide(\"" (comp/munge ns-name) "\");")))) (when-not (nil? node-deps) - (node-side-effects bound-vars sb node-deps ns-name)) + (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) + ns-name + (:def-emits-var opts)) (cb {:value (*eval-fn* {:source (.toString sb)})}))))) (let [src (with-out-str (comp/emit ast))] (cb {:value (*eval-fn* {:source src})}))))))))) @@ -885,10 +890,7 @@ (cb res) (let [ns-name (:name ast)] (when-not (nil? node-deps) - (node-side-effects bound-vars sb node-deps ns-name)) - (global-exports-side-effects bound-vars sb - (filter ana/dep-has-global-exports? (:deps ast)) - ns-name) + (node-side-effects bound-vars sb node-deps ns-name (:def-emits-var opts))) (compile-loop (:name ast)))))) (recur ns))))) (do @@ -1021,10 +1023,11 @@ (cb res) (let [ns-name (:name ast)] (when-not (nil? node-deps) - (node-side-effects bound-vars sb node-deps ns-name)) + (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) + ns-name + (:def-emits-var opts)) (compile-loop ns')))))) (do (.append sb (with-out-str (comp/emit ast))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 84b4fb391..bab35a6a4 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1091,6 +1091,44 @@ (is (= value 3)) (inc! l))))))))) +(deftest test-cljs-2287 + (async done + (let [st (cljs/empty-state) + l (latch 1 done)] + (cljs/eval-str + (atom @st) + "(ns foo.core (:require [path]))" + nil + {:context :expr + :target :nodejs + :def-emits-var true + :eval identity} + (fn [{{:keys [source]} :value}] + (is (some? (re-find #"foo\.core\.node\$module\$path = require\('path'\);\snull;" source))) + (inc! l))) + (let [calculator-load (fn [_ cb] + (cb {:lang :js + :source "global.Calculator = { + add: function (a, b) { + return a + b; + }, + subtract: function (a, b) { + return a - b; + } +};"}))] + (swap! st assoc :js-dependency-index {"calculator" {:global-exports '{calculator Calculator}}}) + (cljs/eval-str + (atom @st) + "(ns foo.core (:require [calculator])) (calculator/add 1 2)" + nil + {:context :expr + :def-emits-var true + :load calculator-load + :eval identity} + (fn [{{:keys [source]} :value}] + (is (some? (re-find #"foo\.core\.global\$module\$calculator = goog.global.Calculator;\snull;" source))) + (inc! l))))))) + (defn -main [& args] (run-tests)) From 1a21fd27ee03ae87f6f3f1d27316bce54bd7aba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 30 Jul 2017 16:51:00 -0700 Subject: [PATCH 2636/4033] CLJS-2286: Simplify JS module processing --- .travis.yml | 2 +- script/test | 2 +- script/test-simple | 2 +- src/main/clojure/cljs/closure.clj | 52 +++++++-------------------- src/test/cljs/calculator_amd.js | 5 +++ src/test/cljs/cljs/npm_deps_test.cljs | 6 +++- src/test/cljs/es6_dep.js | 3 ++ 7 files changed, 29 insertions(+), 43 deletions(-) create mode 100644 src/test/cljs/calculator_amd.js create mode 100644 src/test/cljs/es6_dep.js diff --git a/.travis.yml b/.travis.yml index 5ef26d0f0..94bfadf85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ before_install: before_script: - script/bootstrap - mkdir -p builds/out-adv - - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-adv/core-advanced-test.js + - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :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_amd.js\" :module-type :amd :provides [\"calculator_amd\"] :requires [\"es6_calc\"]}]}" > builds/out-adv/core-advanced-test.js script: - lein test diff --git a/script/test b/script/test index ba6beb10f..3272d64b0 100755 --- a/script/test +++ b/script/test @@ -12,7 +12,7 @@ mkdir -p builds/out-adv possible=5 ran=0 -if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-adv/core-advanced-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :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_amd.js\" :module-type :amd :provides [\"calculator_amd\"] :requires [\"es6_calc\"]}]}" > builds/out-adv/core-advanced-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/script/test-simple b/script/test-simple index 61fce7a80..416cebf6a 100755 --- a/script/test-simple +++ b/script/test-simple @@ -12,7 +12,7 @@ possible=5 ran=0 #bin/cljsc test >out/core-test.js -if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :verbose true :compiler-stats true :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] :global-exports {calculator Calculator}}]}" > builds/out-simp/core-simple-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :verbose true :compiler-stats true :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :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_amd.js\" :module-type :amd :provides [\"calculator_amd\"] :requires [\"es6_calc\"]}]}" > builds/out-simp/core-simple-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1ff9f3dc3..ee465fbd9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1605,16 +1605,6 @@ (js-source-file file (deps/-source lib)))) js-modules)) -(defmulti convert-js-modules - "Takes a list JavaScript modules as an IJavaScript and rewrites them into a Google - Closure-compatible form. Returns list IJavaScript with the converted module - code set as source." - (fn [module-type js-modules opts] - (if (= module-type :amd) - ;; AMD modules are converted via CommonJS modules - :commonjs - module-type))) - (defn make-convert-js-module-options [opts] (-> opts (select-keys @@ -1642,25 +1632,18 @@ (assoc ijs :source (.toSource closure-compiler ^Node (get result-nodes processed-file))))) -(defmethod convert-js-modules :commonjs [module-type js-modules opts] - (let [^List externs '() - ^List source-files (get-source-files js-modules opts) - ^CompilerOptions options (doto (make-convert-js-module-options opts) - (.setProcessCommonJSModules true) - (.setTransformAMDToCJSModules (= module-type :amd))) - closure-compiler (doto (make-closure-compiler) - (.init externs source-files options))] - (.parse closure-compiler) - (report-failure (.getResult closure-compiler)) - (map (partial add-converted-source - closure-compiler (get-closure-sources closure-compiler) opts) - js-modules))) - -(defmethod convert-js-modules :es6 [module-type js-modules opts] +(defn convert-js-modules + "Takes a list JavaScript modules as an IJavaScript and rewrites them into a Google + Closure-compatible form. Returns list IJavaScript with the converted module + code set as source." + [js-modules opts] (let [^List externs '() ^List source-files (get-source-files js-modules opts) ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setProcessCommonJSModules true) + (.setTransformAMDToCJSModules + (boolean (some (fn [{:keys [module-type]}] + (= module-type :amd)) js-modules))) (.setLanguageIn (lang-key->lang-mode :ecmascript6)) (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) closure-compiler (doto (make-closure-compiler) @@ -1674,10 +1657,6 @@ closure-compiler (get-closure-sources closure-compiler) opts) js-modules))) -(defmethod convert-js-modules :default [module-type js-modules opts] - (ana/warning :unsupported-js-module-type @env/*compiler* (first js-modules)) - js-modules) - (defmulti js-transforms "Takes an IJavaScript with the source code set as source, transforms the source code and returns an IJavascript with the new code set as source." @@ -2327,7 +2306,10 @@ (if (seq js-modules) (util/measure (:compiler-stats opts) "Process JS modules" - (let [;; Load all modules - add :source so preprocessing and conversion can access it + (let [_ (when-let [unsupported (first (filter (complement #{:es6 :commonjs :amd}) + (map :module-type js-modules)))] + (ana/warning :unsupported-js-module-type @env/*compiler* unsupported)) + ;; Load all modules - add :source so preprocessing and conversion can access it js-modules (map (fn [lib] (let [js (deps/load-foreign-library lib)] (assoc js :source (deps/-source js opts)))) @@ -2337,15 +2319,7 @@ (preprocess-js js opts) js)) js-modules) - ;; Conversion is done per module-type, because Compiler needs to process e.g. all CommonJS - ;; modules on one go, so it can handle the dependencies between modules. - ;; Amdjs modules are converted separate from CommonJS modules so they can't - ;; depend on each other. - modules-per-type (group-by :module-type js-modules) - js-modules (mapcat (fn [[module-type js-modules]] - (convert-js-modules module-type js-modules opts)) - modules-per-type)] - + js-modules (convert-js-modules js-modules opts)] ;; Write modules to disk, update compiler state and build new options (reduce (fn [new-opts {:keys [file] :as ijs}] (let [ijs (write-javascript opts ijs) diff --git a/src/test/cljs/calculator_amd.js b/src/test/cljs/calculator_amd.js new file mode 100644 index 000000000..5b6fd995b --- /dev/null +++ b/src/test/cljs/calculator_amd.js @@ -0,0 +1,5 @@ +define({ + add: function(x, y){ + return x + y; + } +}); diff --git a/src/test/cljs/cljs/npm_deps_test.cljs b/src/test/cljs/cljs/npm_deps_test.cljs index 776416e55..4deeb3a0b 100644 --- a/src/test/cljs/cljs/npm_deps_test.cljs +++ b/src/test/cljs/cljs/npm_deps_test.cljs @@ -2,7 +2,8 @@ (:refer-clojure :exclude [array vector]) (:require [cljs.test :refer [deftest is]] ["lodash/array" :as array :refer [slice] :rename {slice slc}] - [calculator :as vector :refer [add] :rename {add plus}])) + [calculator :as vector :refer [add] :rename {add plus}] + [es6_calc])) (def array #js [1 2 3]) @@ -21,3 +22,6 @@ (is (= (array-seq array) [1 2 3])) ;; same should happen with global-exports (is (= vector [1]))) + +(deftest test-cljs-2286 + (is (= 3 (es6_calc/calculator.add 1 2)))) diff --git a/src/test/cljs/es6_dep.js b/src/test/cljs/es6_dep.js new file mode 100644 index 000000000..304cbe265 --- /dev/null +++ b/src/test/cljs/es6_dep.js @@ -0,0 +1,3 @@ +import * as calc from './calculator_amd'; + +export var calculator = calc; From aff39caac5f512fdeda7ad41cdd6224d2d4ca48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 30 Jul 2017 15:08:00 -0700 Subject: [PATCH 2637/4033] CLJS-1620: In JavaScript ES2015 modules default export name is munged to default$ --- .travis.yml | 25 ++++++++++- script/test | 25 ++++++++++- script/test-simple | 26 ++++++++++- src/main/cljs/cljs/pprint.cljs | 2 +- src/main/cljs/cljs/stacktrace.cljc | 4 +- src/main/cljs/cljs/test.cljs | 6 +-- src/main/clojure/cljs/analyzer.cljc | 45 ++++++++++++------- src/main/clojure/cljs/closure.clj | 13 +++--- src/main/clojure/cljs/compiler.cljc | 25 ++++++++--- src/test/cljs/cljs/npm_deps_test.cljs | 6 ++- src/test/cljs/es6_default_hello.js | 3 ++ .../clojure/cljs/module_processing_tests.clj | 21 ++++++--- 12 files changed, 155 insertions(+), 46 deletions(-) create mode 100644 src/test/cljs/es6_default_hello.js diff --git a/.travis.yml b/.travis.yml index 94bfadf85..b56cb292a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,30 @@ before_install: before_script: - script/bootstrap - mkdir -p builds/out-adv - - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :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_amd.js\" :module-type :amd :provides [\"calculator_amd\"] :requires [\"es6_calc\"]}]}" > builds/out-adv/core-advanced-test.js + - bin/cljsc src/test/cljs "{:optimizations :advanced + :output-wrapper true + :verbose true + :compiler-stats true + :parallel-build true + :output-dir \"builds/out-adv\" + :npm-deps {:lodash \"4.17.4\"} + :closure-warnings {:non-standard-jsdoc :off :global-this :off} + :language-in :es6 + :language-out :es5 + :install-deps true + :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_amd.js\" + :module-type :amd + :provides [\"calculator_amd\"] + :requires [\"es6_calc\"]} + {:file \"src/test/cljs/es6_default_hello.js\" + :provides [\"es6_default_hello\"] + :module-type :es6}]}" > builds/out-adv/core-advanced-test.js script: - lein test diff --git a/script/test b/script/test index 3272d64b0..965d1cee4 100755 --- a/script/test +++ b/script/test @@ -12,7 +12,30 @@ mkdir -p builds/out-adv possible=5 ran=0 -if ! bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :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_amd.js\" :module-type :amd :provides [\"calculator_amd\"] :requires [\"es6_calc\"]}]}" > builds/out-adv/core-advanced-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :advanced + :output-wrapper true + :verbose true + :compiler-stats true + :parallel-build true + :output-dir \"builds/out-adv\" + :npm-deps {:lodash \"4.17.4\"} + :closure-warnings {:non-standard-jsdoc :off :global-this :off} + :install-deps true + :language-in :es6 + :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_amd.js\" + :module-type :amd + :provides [\"calculator_amd\"] + :requires [\"es6_calc\"]} + {:file \"src/test/cljs/es6_default_hello.js\" + :provides [\"es6_default_hello\"] + :module-type :es6}]}" > builds/out-adv/core-advanced-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/script/test-simple b/script/test-simple index 416cebf6a..d84ab815e 100755 --- a/script/test-simple +++ b/script/test-simple @@ -12,7 +12,31 @@ possible=5 ran=0 #bin/cljsc test >out/core-test.js -if ! bin/cljsc src/test/cljs "{:optimizations :simple :static-fns true :output-dir \"builds/out-simp\" :cache-analysis true :output-wrapper true :verbose true :compiler-stats true :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :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_amd.js\" :module-type :amd :provides [\"calculator_amd\"] :requires [\"es6_calc\"]}]}" > builds/out-simp/core-simple-test.js; then +if ! bin/cljsc src/test/cljs "{:optimizations :simple + :static-fns true + :output-dir \"builds/out-simp\" + :cache-analysis true + :output-wrapper true + :verbose true + :compiler-stats true + :npm-deps {:lodash \"4.17.4\"} + :closure-warnings {:non-standard-jsdoc :off :global-this :off} + :install-deps true + :language-in :es6 + :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_amd.js\" + :module-type :amd + :provides [\"calculator_amd\"] + :requires [\"es6_calc\"]} + {:file \"src/test/cljs/es6_default_hello.js\" + :provides [\"es6_default_hello\"] + :module-type :es6}]}" > builds/out-simp/core-simple-test.js; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 1dfde81e2..2654e89be 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -2537,7 +2537,7 @@ of parameters as well." (merge ; create the result map (into (array-map) ; start with the default values, make sure the order is right - (reverse (for [[name [default]] (:params def)] [name [default offset]]))) + (reverse (for [[name [default-def]] (:params def)] [name [default-def offset]]))) (reduce #(apply assoc %1 %2) {} (filter #(first (nth % 1)) (zipmap (keys (:params def)) params))) ; add the specified parameters, filtering out nils flags)); and finally add the flags diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 43374cb69..df03de098 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -576,11 +576,11 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" (second (first columns))))) (adjust [mapped] (vec (map #(%1 %2) [inc inc identity] mapped)))] - (let [default [line column nil]] + (let [default-ret [line column nil]] ;; source maps are 0 indexed for lines (if-let [columns (get source-map (dec line))] (adjust (map (get-best-column columns column) [:line :col :name])) - default))))) + default-ret))))) (defn mapped-frame "Given opts and a canonicalized JavaScript stacktrace frame, return the diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index b51098a0f..5499ab6ec 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -386,7 +386,7 @@ first))) (defn mapped-line-and-column [filename line column] - (let [default [filename line column]] + (let [default-ret [filename line column]] (if-let [source-map (:source-map (get-current-env))] ;; source maps are 0 indexed for lines (if-let [columns (get-in source-map [filename (dec line)])] @@ -400,8 +400,8 @@ mapping (second (first columns)))) [:source :line :col])) - default) - default))) + default-ret) + default-ret))) (defn file-and-line [exception depth] ;; TODO: flesh out diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f5f0498f8..89b793ef2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -182,6 +182,9 @@ "volatile" "while" "with" "yield" "methods" "null" "constructor"}) +(def es5-allowed + #{"default"}) + #?(:clj (def SENTINEL (Object.)) :cljs (def SENTINEL (js-obj))) @@ -741,7 +744,10 @@ ;; we need to check both keys and values of the JS module index, because ;; macroexpansion will be looking for the provided name - António Monteiro (contains? - (into #{} (mapcat identity) (get-in @env/*compiler* [:js-module-index])) + (into #{} + (mapcat (fn [[k v]] + [k (:name v)])) + (get-in @env/*compiler* [:js-module-index])) (str module))) (defn node-module-dep? @@ -791,16 +797,16 @@ (defn resolve-ns-alias ([env name] (resolve-ns-alias env name (symbol name))) - ([env name default] + ([env name not-found] (let [sym (symbol name)] - (get (:requires (:ns env)) sym default)))) + (get (:requires (:ns env)) sym not-found)))) (defn resolve-macro-ns-alias ([env name] (resolve-macro-ns-alias env name (symbol name))) - ([env name default] + ([env name not-found] (let [sym (symbol name)] - (get (:require-macros (:ns env)) sym default)))) + (get (:require-macros (:ns env)) sym not-found)))) (defn confirm-ns "Given env, an analysis environment, and ns-sym, a symbol identifying a @@ -950,6 +956,11 @@ (defmulti resolve* (fn [sym full-ns current-ns] (ns->module-type full-ns))) +(defmethod resolve* :js + [sym full-ns current-ns] + {:name (symbol (str full-ns) (str (name sym))) + :ns full-ns}) + (defmethod resolve* :node [sym full-ns current-ns] {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) @@ -985,7 +996,7 @@ module-type (ns->module-type ns)] (case module-type :js {:name (symbol - (or (gets @env/*compiler* :js-module-index ns) + (or (gets @env/*compiler* :js-module-index ns :name) (resolve-ns-alias env ns))) :ns 'js} :node {:name (symbol (str current-ns) @@ -1031,7 +1042,7 @@ ns) full-ns (resolve-ns-alias env ns (or (and (js-module-exists? ns) - (get-in @env/*compiler* [:js-module-index ns])) + (gets @env/*compiler* :js-module-index ns :name)) (symbol ns)))] (when (some? confirm) (when (not= current-ns full-ns) @@ -1348,14 +1359,14 @@ :children [test-expr then-expr else-expr]})) (defmethod parse 'case* - [op env [_ sym tests thens default :as form] name _] + [op env [_ sym tests thens default-clause :as form] name _] (assert (symbol? sym) "case* must switch on symbol") (assert (every? vector? tests) "case* tests must be grouped in vectors") (let [expr-env (assoc env :context :expr) v (disallowing-recur (analyze expr-env sym)) tests (mapv #(mapv (fn [t] (analyze expr-env t)) %) tests) thens (mapv #(analyze env %) thens) - default (analyze env default)] + default-clause (analyze env default-clause)] (assert (every? (fn [t] (or (-> t :info :const) @@ -1364,8 +1375,8 @@ (apply concat tests)) "case* tests must be numbers, strings, or constants") {:env env :op :case* :form form - :v v :tests tests :thens thens :default default - :children (vec (concat [v] tests thens (if default [default])))})) + :v v :tests tests :thens thens :default default-clause + :children (vec (concat [v] tests thens (if default-clause [default-clause])))})) (defmethod parse 'throw [op env [_ throw :as form] name _] @@ -1404,9 +1415,9 @@ finally (when (seq fblock) (analyze (assoc env :context :statement) `(do ~@(rest fblock)))) e (when (or (seq cblocks) dblock) (gensym "e")) - default (if-let [[_ _ name & cb] dblock] - `(cljs.core/let [~name ~e] ~@cb) - `(throw ~e)) + default-block (if-let [[_ _ name & cb] dblock] + `(cljs.core/let [~name ~e] ~@cb) + `(throw ~e)) cblock (if (seq cblocks) `(cljs.core/cond ~@(mapcat @@ -1415,8 +1426,8 @@ `[(cljs.core/instance? ~type ~e) (cljs.core/let [~name ~e] ~@cb)]) cblocks) - :else ~default) - default) + :else ~default-block) + default-block) locals (:locals catchenv) locals (if e (assoc locals e @@ -2339,7 +2350,7 @@ ;; Google Closure compiler, e.g. module$resources$libs$calculator. ;; This means that we need to create an alias from the module name ;; given with :provides to the new name. - [lib js-module-provides] (if-some [js-module-name (get-in @env/*compiler* [:js-module-index (str lib)])] + [lib js-module-provides] (if-some [js-module-name (gets @env/*compiler* :js-module-index (str lib) :name)] [(symbol js-module-name) lib] [lib nil]) {alias :as referred :refer renamed :rename diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ee465fbd9..d5b0c23f0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2321,17 +2321,18 @@ js-modules) js-modules (convert-js-modules js-modules opts)] ;; Write modules to disk, update compiler state and build new options - (reduce (fn [new-opts {:keys [file] :as ijs}] + (reduce (fn [new-opts {:keys [file module-type] :as ijs}] (let [ijs (write-javascript opts ijs) module-name (-> (deps/load-library (:out-file ijs)) first :provides first)] (doseq [provide (:provides ijs)] (swap! env/*compiler* - #(update-in % [:js-module-index] assoc provide module-name))) + #(update-in % [:js-module-index] assoc provide {:name module-name + :module-type module-type}))) (-> new-opts - (update-in [:libs] (comp vec conj) (:out-file ijs)) - ;; js-module might be defined in either, so update both - (update-in [:foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs)))) - (update-in [:ups-foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs))))))) + (update-in [:libs] (comp vec conj) (:out-file ijs)) + ;; js-module might be defined in either, so update both + (update-in [:foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs)))) + (update-in [:ups-foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs))))))) opts js-modules))) opts))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 8ff84b8ff..1f8f1b8a8 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -36,6 +36,15 @@ (def js-reserved ana/js-reserved) +(def ^:private es5>= + (into #{} + (comp + (mapcat (fn [lang] + [lang (keyword (string/replace (name lang) #"^ecmascript" "es"))]))) + [:ecmascript5 :ecmascript5-strict :ecmascript6 :ecmascript6-strict + :ecmascript-2015 :ecmascript6-typed :ecmascript-2016 :ecmascript-2017 + :ecmascript-next])) + (def ^:dynamic *recompiled* nil) (def ^:dynamic *inputs* nil) (def ^:dynamic *source-map-data* nil) @@ -333,9 +342,10 @@ [{:keys [info env form] :as ast}] (if-let [const-expr (:const-expr ast)] (emit (assoc const-expr :env env)) - (let [var-name (:name info) + (let [{:keys [options] :as cenv} @env/*compiler* + var-name (:name info) info (if (= (namespace var-name) "js") - (let [js-module-name (get-in @env/*compiler* [:js-module-index (name var-name)])] + (let [js-module-name (get-in cenv [:js-module-index (name var-name) :name])] (or js-module-name (name var-name))) info)] ; We need a way to write bindings out to source maps and javascript @@ -346,10 +356,13 @@ ; (prevents duplicate fn-param-names) (emits (munge ast)) (when-not (= :statement (:context env)) - (emit-wrap env - (emits - (cond-> info - (not= form 'js/-Infinity) munge)))))))) + (let [reserved (cond-> js-reserved + (es5>= (:language-out options)) + (set/difference ana/es5-allowed))] + (emit-wrap env + (emits + (cond-> info + (not= form 'js/-Infinity) (munge reserved)))))))))) (defmethod emit* :var-special [{:keys [env var sym meta] :as arg}] diff --git a/src/test/cljs/cljs/npm_deps_test.cljs b/src/test/cljs/cljs/npm_deps_test.cljs index 4deeb3a0b..9c19ab985 100644 --- a/src/test/cljs/cljs/npm_deps_test.cljs +++ b/src/test/cljs/cljs/npm_deps_test.cljs @@ -3,7 +3,8 @@ (:require [cljs.test :refer [deftest is]] ["lodash/array" :as array :refer [slice] :rename {slice slc}] [calculator :as vector :refer [add] :rename {add plus}] - [es6_calc])) + [es6_calc] + [es6_default_hello :as es6hello])) (def array #js [1 2 3]) @@ -25,3 +26,6 @@ (deftest test-cljs-2286 (is (= 3 (es6_calc/calculator.add 1 2)))) + +(deftest test-cljs-1620 + (is (= "Hello, world!" (es6hello/default)))) diff --git a/src/test/cljs/es6_default_hello.js b/src/test/cljs/es6_default_hello.js new file mode 100644 index 000000000..9cc911553 --- /dev/null +++ b/src/test/cljs/es6_default_hello.js @@ -0,0 +1,3 @@ +export default function sayHello () { + return "Hello, world!"; +}; diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 28e9ae176..ec3b07958 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -47,8 +47,10 @@ :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs")) - (is (= {"React" "module$src$test$cljs$reactJS" - "Circle" "module$src$test$cljs$Circle"} + (is (= {"React" {:name "module$src$test$cljs$reactJS" + :module-type :commonjs} + "Circle" {:name "module$src$test$cljs$Circle" + :module-type :commonjs}} (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) @@ -71,7 +73,8 @@ :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs") - (is (= {"es6-hello" "module$src$test$cljs$es6-hello"} + (is (= {"es6-hello" {:name "module$src$test$cljs$es6-hello" + :module-type :es6}} (:js-module-index @cenv)) "Processed modules are added to :js-module-index") @@ -124,8 +127,10 @@ :preprocess :jsx}] :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs")) - (is (= {"React" "module$src$test$cljs$react-min" - "Circle" "module$src$test$cljs$Circle-min"} + (is (= {"React" {:name "module$src$test$cljs$react-min" + :module-type :commonjs} + "Circle" {:name "module$src$test$cljs$Circle-min" + :module-type :commonjs}} (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) @@ -151,7 +156,9 @@ :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs")) - (is (= {"React" "module$src$test$cljs$reactJS" - "Circle" "module$src$test$cljs$Circle"} + (is (= {"React" {:name "module$src$test$cljs$reactJS" + :module-type :commonjs} + "Circle" {:name "module$src$test$cljs$Circle" + :module-type :commonjs}} (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) From c1aa91ca6188e9344e71f46e426bc004c6f197cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 2 Aug 2017 20:49:20 -0700 Subject: [PATCH 2638/4033] CLJS-2295: `index-node-modules-dir` can't determine :main for package.json files that have `.` in the string --- src/main/clojure/cljs/closure.clj | 10 +++++++-- src/test/clojure/cljs/closure_tests.clj | 27 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d5b0c23f0..369cbb262 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2226,8 +2226,14 @@ (let [pkg-json-main (some (fn [[pkg-json-path {:strs [main name]}]] (when-not (nil? main) - (let [main-path (str (string/replace pkg-json-path #"package\.json$" "") - main)] + ;; should be the only edge case in + ;; the package.json main field - Antonio + (let [main (cond-> main + (.startsWith main "./") + (subs 2)) + main-path (-> pkg-json-path + (string/replace #"package\.json$" "") + (str main))] (when (= main-path path) name)))) pkg-jsons)] diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 2dc0644d4..a83bf46c2 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -141,6 +141,19 @@ "@comandeer/css-filter/dist/css-filter.umd" "@comandeer/css-filter"]})) modules)))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (closure/maybe-install-node-deps! {:npm-deps {"jss-extend" "5.0.0"}}) + (let [modules (closure/index-node-modules-dir)] + (is (true? (some (fn [module] + (= module + {:file (.getAbsolutePath (io/file "node_modules/jss-extend/lib/index.js")) + :module-type :es6 + :provides ["jss-extend/lib/index.js" + "jss-extend/lib/index" + "jss-extend" + "jss-extend/lib"]})) + modules)))) (.delete (io/file "package.json")) (test/delete-node-modules)) @@ -208,6 +221,20 @@ "@comandeer/css-filter/dist/css-filter.umd.js" "@comandeer/css-filter/dist/css-filter.umd"]})) (closure/index-node-modules ["@comandeer/css-filter"] opts))))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (test/delete-out-files out) + (let [opts {:npm-deps {"jss-extend" "5.0.0"}}] + (closure/maybe-install-node-deps! opts) + (is (true? (some (fn [module] + (= module + {:file (.getAbsolutePath (io/file "node_modules/jss-extend/lib/index.js")) + :module-type :es6 + :provides ["jss-extend" + "jss-extend/lib/index.js" + "jss-extend/lib/index" + "jss-extend/lib"]})) + (closure/index-node-modules ["jss-extend"] opts))))) (.delete (io/file "package.json")) (test/delete-node-modules) (test/delete-out-files out))) From 8cc8491b8c6d969d4f46bbf5de8ac90e87463e31 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 2 Aug 2017 10:27:32 -0400 Subject: [PATCH 2639/4033] CLJS-2293: Self-host: Can't load cljs.js owing to set alias --- src/main/clojure/cljs/compiler.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1f8f1b8a8..766c3d441 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -24,6 +24,7 @@ [cljs.js-deps :as deps]) :cljs (:require [goog.string :as gstring] [clojure.string :as string] + [clojure.set :as set] [cljs.tools.reader :as reader] [cljs.env :as env] [cljs.analyzer :as ana] From 0e3e485dd2f9d6a57e9402dc2392cf44c65f1134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 1 Aug 2017 16:28:56 -0700 Subject: [PATCH 2640/4033] CLJS-2291: Set up Windows CI --- appveyor.yml | 44 +++++++++++++++++++ script/bootstrap.ps1 | 30 +++++++++++-- script/test.ps1 | 4 +- src/main/cljs/cljs/module_deps.js | 2 +- src/main/clojure/cljs/closure.clj | 9 ++-- src/main/clojure/cljs/util.cljc | 17 ++++--- src/test/clojure/cljs/build_api_tests.clj | 12 ++--- .../clojure/cljs/module_processing_tests.clj | 29 +++++++----- src/test/clojure/cljs/test_util.clj | 7 ++- src/test/clojure/cljs/util_tests.clj | 17 ++++--- 10 files changed, 131 insertions(+), 40 deletions(-) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..fc8394f07 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,44 @@ +environment: + nodejs_version: "6" + +platform: + - x64 + +configuration: + - Release + +matrix: + allow_failures: + - platform: x64 + configuration: Release + +cache: + - '%UserProfile%\.m2' + +install: + # these need to have a line in between because of Windows line endings + - ps: >- + New-Item c:\scripts -type directory + + $env:Path += ";C:\scripts" + + Invoke-WebRequest -Uri https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein.bat -OutFile "C:\scripts\lein.bat" + + lein self-install + + lein version + - ps: Install-Product node $env:nodejs_version x64 + - ps: wget 'http://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/jsshell-win64.zip' -OutFile "$pwd\jsshell.zip" + - ps: 7z x "-o$pwd\jsshell" jsshell.zip -r + - ps: .\script\bootstrap.ps1 + - ps: "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8" + - ps: $env:SPIDERMONKEY_HOME = "$pwd/jsshell" + - ps: $SPIDERMONKEY_HOME = "$pwd/jsshell" + +test_script: + - cmd: lein test + # surprisingly this is the only way to have the output shown in Appveyor + - cmd: powershell -noninteractive -noprofile -command .\script\test.ps1 + +# Don't actually build (MSBuild). +build: off diff --git a/script/bootstrap.ps1 b/script/bootstrap.ps1 index a33e0e23d..e918d76b7 100644 --- a/script/bootstrap.ps1 +++ b/script/bootstrap.ps1 @@ -5,8 +5,10 @@ $shell = New-Object -com shell.application # Read white listed dependency version info from the /bin/sh script and store in variables Get-Content $root\script\bootstrap | Where-Object { $_ -match '^\s*(\w+)\s*=\s*\"([^\"]*)\"\s*$' } | - Where-Object { $matches[1] -in "CLOJURE_RELEASE", "CLOSURE_RELEASE", - "DJSON_RELEASE", "GCLOSURE_LIB_RELEASE", "RHINO_RELEASE", "TREADER_RELEASE" } | + Where-Object { $matches[1] -in "CLOJURE_RELEASE", "SPEC_ALPHA_RELEASE", + "CORE_SPECS_ALPHA_RELEASE", "CLOSURE_RELEASE", "DJSON_RELEASE", + "TRANSIT_RELEASE", "GCLOSURE_LIB_RELEASE", "RHINO_RELEASE", + "TREADER_RELEASE", "TEST_CHECK_RELEASE" } | Foreach-Object { New-Variable $matches[1] $matches[2] -Scope private } function Get-WebResource($url, $dstPath) { @@ -95,11 +97,26 @@ Expand-ZipFile $root\clojure-$CLOJURE_RELEASE.zip $root\lib clojure-$CLOJURE_REL Move-File $root\lib\clojure-$CLOJURE_RELEASE.jar $root\lib\clojure.jar Delete-File $root\clojure-$CLOJURE_RELEASE.zip +Write-Host "Fetching specs.alpha...." +Get-WebResource ` + https://repo1.maven.org/maven2/org/clojure/spec.alpha/$SPEC_ALPHA_RELEASE/spec.alpha-$SPEC_ALPHA_RELEASE.jar ` + $root\lib\spec.alpha-$SPEC_ALPHA_RELEASE.jar + +Write-Host "Fetching core.specs.alpha...." +Get-WebResource ` + https://repo1.maven.org/maven2/org/clojure/core.specs.alpha/$CORE_SPECS_ALPHA_RELEASE/core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar ` + $root\lib\core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar + Write-Host "Fetching data.json..." Get-WebResource ` https://repo1.maven.org/maven2/org/clojure/data.json/$DJSON_RELEASE/data.json-$DJSON_RELEASE.jar ` $root\lib\data.json-$DJSON_RELEASE.jar +Write-Host "Fetching transit-clj..." +Get-WebResource ` + https://repo1.maven.org/maven2/com/cognitect/transit-clj/$TRANSIT_RELEASE/transit-clj-$TRANSIT_RELEASE.jar ` + $root\lib\transit-clj-$TRANSIT_RELEASE.jar + # TODO: Implement Closure SVN support Write-Host "Fetching Google Closure library..." Get-WebResource ` @@ -115,7 +132,7 @@ Get-WebResource ` $root\compiler-$CLOSURE_RELEASE.zip Get-ChildItem $root\closure\compiler\* | Delete-File Expand-ZipFile $root\compiler-$CLOSURE_RELEASE.zip $root\closure\compiler -Copy-File $root\closure\compiler\compiler.jar $root\lib\compiler.jar +Copy-File $root\closure\compiler\closure-compiler-v$CLOSURE_RELEASE.jar $root\lib\compiler.jar Delete-File $root\compiler-$CLOSURE_RELEASE.zip Write-Host "Fetching Rhino..." @@ -131,4 +148,9 @@ Get-WebResource ` https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar ` $root\lib\tools.reader-$TREADER_RELEASE.jar -Write-Host "[Bootstrap Completed]" \ No newline at end of file +Write-Host "Fetching test.check $TEST_CHECK_RELEASE ..." +Get-WebResource ` + https://repo1.maven.org/maven2/org/clojure/test.check/$TEST_CHECK_RELEASE/test.check-$TEST_CHECK_RELEASE.jar ` + $root\lib\test.check-$TEST_CHECK_RELEASE.jar + +Write-Host "[Bootstrap Completed]" diff --git a/script/test.ps1 b/script/test.ps1 index c9efde243..a2d0ba09b 100644 --- a/script/test.ps1 +++ b/script/test.ps1 @@ -11,7 +11,7 @@ $targets = @{ env="CHAKRACORE_HOME"; name="ChakraCore"; cmd={ & "$env:CHAKRACORE_HOME\ch" $testjs } } $ran = 0 -$opts = '{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :output-dir \"builds\\out-adv\"}' +$opts = '{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :language-in :es6 :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_amd.js\" :module-type :amd :provides [\"calculator_amd\"] :requires [\"es6_calc\"]} {:file \"src/test/cljs/es6_default_hello.js\" :provides [\"es6_default_hello\"] :module-type :es6}]}"' function Test-It($env, $name, [scriptblock] $cmd) { $env_val = if(Test-Path env:$env) { (Get-Item env:$env).Value } else { "" } @@ -32,7 +32,7 @@ try { New-Item builds\out-adv -ItemType Directory -Force | Out-Null - bin\cljsc src\test/cljs $opts | Set-Content $testjs + bin\cljsc src\test\cljs $opts | Set-Content $testjs $targets | Foreach-Object { Test-It @_ } } diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 45378538c..e26b01151 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -77,7 +77,7 @@ md.on('end', function() { if (dep.file.indexOf('node_modules') !== -1) { var providedModule = dep.file .substring(dep.file.lastIndexOf('node_modules')) - .replace('\\', '/') + .replace(/\\/g, '/') .replace('node_modules/', ''); dep.provides = dep.provides || []; diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 369cbb262..c096202dc 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2232,14 +2232,15 @@ (.startsWith main "./") (subs 2)) main-path (-> pkg-json-path + (string/replace #"\\" "/") (string/replace #"package\.json$" "") (str main))] - (when (= main-path path) + (when (= main-path (string/replace path #"\\" "/")) name)))) pkg-jsons)] - {:provides (let [module-rel-name (string/replace - (subs path (.lastIndexOf path "node_modules")) - #"node_modules[\\\/]" "") + {:provides (let [module-rel-name (-> (subs path (.lastIndexOf path "node_modules")) + (string/replace #"\\" "/") + (string/replace #"node_modules[\\\/]" "")) provides (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")] (some? pkg-json-main) (conj pkg-json-main)) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 880112d5f..37d7da7e9 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -142,6 +142,9 @@ (filename x) (last (string/split (path x) #"\/")))) +(def windows? + (.startsWith (.toLowerCase (System/getProperty "os.name")) "windows")) + (defn ^String relative-name "Given a file return a path relative to the working directory. Given a URL return the JAR relative path of the resource." @@ -149,9 +152,14 @@ {:pre [(or (file? x) (url? x))]} (letfn [(strip-user-dir [s] (let [user-dir (System/getProperty "user.dir") - user-path (.getFile (.toURL (io/file user-dir))) - user-path (cond-> user-path - (not (.endsWith user-path File/separator)) + ;; on Windows, URLs end up having forward slashes like + ;; /C:/Users/... - Antonio + s (-> (cond-> s + windows? (string/replace #"^[\\/]" "")) + (string/replace "\\" File/separator) + (string/replace "/" File/separator)) + user-path (cond-> user-dir + (not (.endsWith user-dir File/separator)) (str File/separator))] (string/replace s user-path "")))] (if (file? x) @@ -316,9 +324,6 @@ 2 (str (first xs) " and " (second xs)) (str (string/join ", " (pop xs)) " and " (peek xs))))) -(def windows? - (.startsWith (.toLowerCase (System/getProperty "os.name")) "windows")) - (defn module-file-seq ([] (module-file-seq (io/file "node_modules"))) ([dir] diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index a57de9200..5bed18f60 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -15,15 +15,16 @@ [clojure.java.shell :as sh] [cljs.env :as env] [cljs.analyzer :as ana] + [cljs.util :as util] [cljs.test-util :as test] [cljs.build.api :as build] [cljs.closure :as closure])) (deftest test-target-file-for-cljs-ns (is (= (.getPath (build/target-file-for-cljs-ns 'example.core-lib nil)) - "out/example/core_lib.js")) + (test/platform-path "out/example/core_lib.js"))) (is (= (.getPath (build/target-file-for-cljs-ns 'example.core-lib "output")) - "output/example/core_lib.js"))) + (test/platform-path "output/example/core_lib.js")))) (deftest test-cljs-dependents-for-macro-namespaces (env/with-compiler-env (env/default-compiler-env) @@ -200,7 +201,7 @@ loader (io/file out "cljs" "loader.js")] (build/build (build/inputs (io/file inputs "bar.cljs") (io/file inputs "foo.cljs")) opts) (is (.exists loader)) - (is (not (nil? (re-find #"/loader_test/foo\.js" (slurp loader)))))) + (is (not (nil? (re-find #"[\\/]loader_test[\\/]foo\.js" (slurp loader)))))) (test/delete-out-files out) (let [project (merge-with merge (loader-test-project out) {:opts {:optimizations :advanced @@ -250,7 +251,7 @@ (is (contains? (:js-module-index @cenv) "react-dom/server"))) (testing "builds with string requires are idempotent" (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) - (is (not (nil? (re-find #"\.\./node_modules/react-dom/server\.js" (slurp (io/file out "cljs_deps.js")))))) + (is (not (nil? (re-find #"\.\.[\\/]node_modules[\\/]react-dom[\\/]server\.js" (slurp (io/file out "cljs_deps.js")))))) (test/delete-out-files out))) (.delete (io/file "package.json")) (test/delete-node-modules)) @@ -406,7 +407,8 @@ :konan "*" :resolve "*" :browser-resolve "*"}})) - (sh/sh "npm" "install") + (apply sh/sh (cond->> ["npm" "install"] + util/windows? (into ["cmd" "/c"]))) (let [ws (atom []) out (.getPath (io/file (test/tmp-dir) "node-modules-opt-test-out")) {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index ec3b07958..fc7895d33 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -6,6 +6,7 @@ [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.js-deps :as deps] + [cljs.util :as util] [cljs.test-util :as test])) ;; Hard coded JSX transform for the test case @@ -32,8 +33,8 @@ (with-redefs [cljs.js-deps/load-library (memoize cljs.js-deps/load-library*)] (is (= {:foreign-libs [] :ups-foreign-libs [] - :libs ["out/src/test/cljs/reactJS.js" - "out/src/test/cljs/Circle.js"] + :libs [(test/platform-path "out/src/test/cljs/reactJS.js") + (test/platform-path "out/src/test/cljs/Circle.js")] :closure-warnings {:non-standard-jsdoc :off}} (env/with-compiler-env cenv (closure/process-js-modules @@ -63,7 +64,7 @@ (is (= {:foreign-libs [] :ups-foreign-libs [] - :libs ["out/src/test/cljs/es6_hello.js"] + :libs [(test/platform-path "out/src/test/cljs/es6_hello.js")] :closure-warnings {:non-standard-jsdoc :off}} (env/with-compiler-env cenv (closure/process-js-modules @@ -88,19 +89,23 @@ (let [opts (closure/process-js-modules {:foreign-libs [{:file "src/test/cljs/calculator.js" :provides ["calculator"] :module-type :commonjs}]}) - compile (fn [form] (with-out-str - (comp/emit (ana/analyze (ana/empty-env) form)))) - output "module$src$test$cljs$calculator.add((3),(4));\n"] + compile (fn [form] + (with-out-str + (comp/emit (ana/analyze (ana/empty-env) form)))) + crlf (if util/windows? "\r\n" "\n") + output (str "module$src$test$cljs$calculator.add((3),(4));" crlf)] (swap! cenv #(assoc % :js-dependency-index (deps/js-dependency-index opts))) (binding [ana/*cljs-ns* 'cljs.user] (is (= (compile '(ns my-calculator.core (:require [calculator :as calc :refer [subtract add] :rename {subtract sub}]))) - "goog.provide('my_calculator.core');\ngoog.require('cljs.core');\ngoog.require('module$src$test$cljs$calculator');\n")) + (str "goog.provide('my_calculator.core');" crlf + "goog.require('cljs.core');" crlf + "goog.require('module$src$test$cljs$calculator');" crlf))) (is (= (compile '(calc/add 3 4)) output)) (is (= (compile '(calculator/add 3 4)) output)) (is (= (compile '(add 3 4)) output)) (is (= (compile '(sub 5 4)) - "module$src$test$cljs$calculator.subtract((5),(4));\n"))))))) + (str "module$src$test$cljs$calculator.subtract((5),(4));" crlf)))))))) (deftest test-cljs-1822 (test/delete-out-files) @@ -110,8 +115,8 @@ (is (= {:optimizations :simple :foreign-libs [] :ups-foreign-libs [] - :libs ["out/src/test/cljs/react-min.js" - "out/src/test/cljs/Circle-min.js"] + :libs [(test/platform-path "out/src/test/cljs/react-min.js") + (test/platform-path "out/src/test/cljs/Circle-min.js")] :closure-warnings {:non-standard-jsdoc :off}} (env/with-compiler-env cenv (closure/process-js-modules @@ -141,8 +146,8 @@ (with-redefs [cljs.js-deps/load-library (memoize cljs.js-deps/load-library*)] (is (= {:foreign-libs [] :ups-foreign-libs [] - :libs ["out/src/test/cljs/reactJS.js" - "out/src/test/cljs/Circle.js"] + :libs [(test/platform-path "out/src/test/cljs/reactJS.js") + (test/platform-path "out/src/test/cljs/Circle.js")] :closure-warnings {:non-standard-jsdoc :off}} (env/with-compiler-env cenv (closure/process-js-modules diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index ff9a38a8e..7e9d96288 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -7,7 +7,9 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.test-util - (:require [clojure.java.io :as io])) + (:require [clojure.java.io :as io] + [clojure.string :as string]) + (:import [java.io File])) (defn delete-out-files "Processed files are only copied/written if input has changed. In test case it @@ -49,3 +51,6 @@ "Returns the temporary directory of the system." [] (System/getProperty "java.io.tmpdir")) + +(defn platform-path [path] + (.replace path \/ (.charAt (str File/separator) 0))) diff --git a/src/test/clojure/cljs/util_tests.clj b/src/test/clojure/cljs/util_tests.clj index 24502f84d..c3e218b97 100644 --- a/src/test/clojure/cljs/util_tests.clj +++ b/src/test/clojure/cljs/util_tests.clj @@ -29,8 +29,15 @@ (sort (util/unknown-opts #{:optimisations :bogus} #{:optimizations :static-fns})))))) (deftest test-relative-name - (let [initial (System/getProperty "user.dir")] - (System/setProperty "user.dir" "/Users/user/clojurescript") - (is (= (util/relative-name (io/file "/Users/user/clojurescript/out/index.js")) "out/index.js")) - (is (= (util/relative-name (io/as-url (io/file "/Users/user/clojurescript/out/index.js"))) "out/index.js")) - (System/setProperty "user.dir" initial))) + (if util/windows? + (let [initial (System/getProperty "user.dir")] + (System/setProperty "user.dir" "C:\\Users\\anmonteiro\\Downloads\\clojurescript-master") + (is (= (util/relative-name (io/file "C:\\Users\\anmonteiro\\Downloads\\clojurescript-master\\out\\index.js")) "out\\index.js")) + (is (= (util/relative-name (io/as-url (io/file "C:\\Users\\anmonteiro\\Downloads\\clojurescript-master\\node_modules\\lodash\\array.js"))) "node_modules\\lodash\\array.js")) + (System/setProperty "user.dir" initial)) + ;; Non-windows + (let [initial (System/getProperty "user.dir")] + (System/setProperty "user.dir" "/Users/user/clojurescript") + (is (= (util/relative-name (io/file "/Users/user/clojurescript/out/index.js")) "out/index.js")) + (is (= (util/relative-name (io/as-url (io/file "/Users/user/clojurescript/out/index.js"))) "out/index.js")) + (System/setProperty "user.dir" initial)))) From 3057bbeeaea3adf04c25d4cbfa417bf0e4a3848a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Thu, 3 Aug 2017 12:22:54 -0700 Subject: [PATCH 2641/4033] CLJS-2296: Foreign libs that expose modules are not being processed under target node --- src/main/clojure/cljs/closure.clj | 35 ++++++++++--------- .../cljs_build/foreign-libs-dir/vendor/lib.js | 3 ++ .../foreign_libs_dir_test/core.cljs | 15 ++++++++ src/test/clojure/cljs/build_api_tests.clj | 16 +++++++++ 4 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 src/test/cljs_build/foreign-libs-dir/vendor/lib.js create mode 100644 src/test/cljs_build/foreign_libs_dir_test/core.cljs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c096202dc..1ccc4cb0c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2408,23 +2408,24 @@ ;; Select Node files that are required by Cljs code, ;; and create list of all their dependencies node-required (set/intersection (set (keys top-level)) requires)] - (if-not (= target :nodejs) - (let [opts (-> opts - (update :foreign-libs - (fn [libs] - (into (index-node-modules node-required) - (expand-libs libs)))) - process-js-modules)] - (swap! compiler-env merge - ;; we need to also track the whole top level - this is to support - ;; cljs.analyze/analyze-deps, particularly in REPL contexts - David - {:js-dependency-index (deps/js-dependency-index opts) - :node-module-index (into #{} (map str (keys top-level)))}) - opts) - (do - (swap! compiler-env update-in [:node-module-index] - (fnil into #{}) (map str node-required)) - opts)))) + (let [opts (-> opts + (update :foreign-libs + (fn [libs] + (into (if (= target :nodejs) + [] + (index-node-modules node-required)) + (expand-libs libs)))) + process-js-modules)] + (swap! compiler-env (fn [cenv] + (-> cenv + ;; we need to also track the whole top level - this is to support + ;; cljs.analyze/analyze-deps, particularly in REPL contexts - David + (merge {:js-dependency-index (deps/js-dependency-index opts)}) + (update-in [:node-module-index] (fnil into #{}) + (if (= target :nodejs) + (map str node-required) + (map str (keys top-level))))))) + opts))) (defn build "Given a source which can be compiled, produce runnable JavaScript." diff --git a/src/test/cljs_build/foreign-libs-dir/vendor/lib.js b/src/test/cljs_build/foreign-libs-dir/vendor/lib.js new file mode 100644 index 000000000..ca285d935 --- /dev/null +++ b/src/test/cljs_build/foreign-libs-dir/vendor/lib.js @@ -0,0 +1,3 @@ +module.exports = { + foo: 'bar', +} diff --git a/src/test/cljs_build/foreign_libs_dir_test/core.cljs b/src/test/cljs_build/foreign_libs_dir_test/core.cljs new file mode 100644 index 000000000..927054fd8 --- /dev/null +++ b/src/test/cljs_build/foreign_libs_dir_test/core.cljs @@ -0,0 +1,15 @@ +;; 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 foreign-libs-dir-test.core + (:require [vendor.lib :as lib])) + +(enable-console-print!) + +(defn main [] + (println lib)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 5bed18f60..0ddc3f32b 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -462,3 +462,19 @@ (test/delete-out-files out) (test/delete-node-modules) (.delete (io/file "package.json")))) + +(deftest test-cljs-2296 + (let [out (.getPath (io/file (test/tmp-dir) "cljs-2296-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'foreign_libs_dir_test.core + :output-dir out + :optimizations :none + :target :nodejs + ;; :file is a directory + :foreign-libs [{:file "src/test/cljs_build/foreign-libs-dir" + :module-type :commonjs}]}}] + (test/delete-out-files out) + (build/build (build/inputs (io/file inputs "foreign_libs_dir_test/core.cljs")) opts) + (is (.exists (io/file out "src/test/cljs_build/foreign-libs-dir/vendor/lib.js"))) + (is (true? (boolean (re-find #"goog\.provide\(\"module\$src\$test\$cljs_build\$foreign_libs_dir\$vendor\$lib\"\)" + (slurp (io/file out "src/test/cljs_build/foreign-libs-dir/vendor/lib.js")))))))) From bf604271273325fd8cd5b08bd6e37c6ab5a95ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 1 Aug 2017 15:41:16 -0700 Subject: [PATCH 2642/4033] CLJS-2261: Issue using interop record constructors in macros namespaces Also bumps tools.reader to 1.0.5 which has the fix --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- script/noderepljs | 2 +- src/test/cljs/cljs/macro_test/cljs2261.clj | 4 ++ src/test/cljs/cljs/macro_test/cljs2261.cljs | 3 ++ src/test/self/self_host/test.cljs | 47 +++++++++++++++++++++ 7 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 src/test/cljs/cljs/macro_test/cljs2261.clj create mode 100644 src/test/cljs/cljs/macro_test/cljs2261.cljs diff --git a/pom.template.xml b/pom.template.xml index 8b4a675cb..3f1b4a647 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.0.3 + 1.0.5 org.clojure diff --git a/project.clj b/project.clj index 391946ec3..958165587 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.0.3"] + [org.clojure/tools.reader "1.0.5"] [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] diff --git a/script/bootstrap b/script/bootstrap index c5c1ce744..af0901b76 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.0.3" +TREADER_RELEASE="1.0.5" TEST_CHECK_RELEASE="0.10.0-alpha2" # check dependencies diff --git a/script/noderepljs b/script/noderepljs index c0886a5a3..bfd9aca36 100755 --- a/script/noderepljs +++ b/script/noderepljs @@ -5,7 +5,7 @@ if [ "$CLOJURESCRIPT_HOME" = "" ]; then fi CLJSC_CP='' -for next in lib/*: src/main/clojure: src/main/cljs: test/cljs; do +for next in lib/*: src/main/clojure: src/main/cljs: src/test/cljs; do CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next done diff --git a/src/test/cljs/cljs/macro_test/cljs2261.clj b/src/test/cljs/cljs/macro_test/cljs2261.clj new file mode 100644 index 000000000..6a13e3f46 --- /dev/null +++ b/src/test/cljs/cljs/macro_test/cljs2261.clj @@ -0,0 +1,4 @@ +(ns cljs.macro-test.cljs2261) + +(defmacro cake [] + `(X.)) diff --git a/src/test/cljs/cljs/macro_test/cljs2261.cljs b/src/test/cljs/cljs/macro_test/cljs2261.cljs new file mode 100644 index 000000000..8d3632c78 --- /dev/null +++ b/src/test/cljs/cljs/macro_test/cljs2261.cljs @@ -0,0 +1,3 @@ +(ns cljs.macro-test.cljs2261) + +(defrecord X []) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index bab35a6a4..6e451f5bb 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1129,6 +1129,53 @@ (is (some? (re-find #"foo\.core\.global\$module\$calculator = goog.global.Calculator;\snull;" source))) (inc! l))))))) +(deftest test-cljs-2261 + (async done + (let [st (cljs/empty-state) + l (latch 2 done)] + (cljs/eval st '(ns bar.core2261a + (:require [foo.core2261a :refer-macros [cake]])) + {:ns 'cljs.user + :eval node-eval + :context :expr + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj + :source "(ns foo.core2261a) (defmacro cake [] `(->X))"}) + (cb {:lang :clj + :source "(ns foo.core2261a) (defrecord X [])"})))} + (fn [{:keys [error]}] + (is (nil? error)) + (cljs/eval-str st "(pr-str (cake))" nil + {:ns 'bar.core2261a + :eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "#foo.core2261a.X{}" value)) + (inc! l))))) + (cljs/eval st '(ns bar.core2261b + (:require [foo.core2261b :refer-macros [cake]])) + {:ns 'cljs.user + :eval node-eval + :context :expr + :load (fn [{:keys [macros]} cb] + (if macros + (cb {:lang :clj + :source "(ns foo.core2261b) (defmacro cake [] `(X.))"}) + (cb {:lang :clj + :source "(ns foo.core2261b) (defrecord X [])"})))} + (fn [{:keys [error]}] + (is (nil? error)) + (cljs/eval-str st "(pr-str (cake))" nil + {:ns 'bar.core2261b + :eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "#foo.core2261b.X{}" value)) + (inc! l)))))))) + (defn -main [& args] (run-tests)) From 0f10898a440fbc8b52aed0e63fb541671f307905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Sun, 6 Aug 2017 14:08:06 -0700 Subject: [PATCH 2643/4033] CLJS-2304: Fix compiler infrastructure tests on Windows --- src/main/clojure/cljs/closure.clj | 3 ++- src/main/clojure/cljs/module_graph.cljc | 4 +++- src/main/clojure/cljs/util.cljc | 28 ++++++++++++++--------- src/test/clojure/cljs/build_api_tests.clj | 18 +++++++-------- src/test/clojure/cljs/closure_tests.clj | 4 +++- src/test/clojure/cljs/util_tests.clj | 4 ++++ 6 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1ccc4cb0c..cefd7a3b9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1571,7 +1571,8 @@ (util/ns->relpath (first provides) "js") (if (.endsWith lib-path ".js") (util/get-name url) - (let [path (util/path url)] + (let [path (util/path url) + lib-path (util/normalize-path lib-path)] (subs path (+ (inc (.lastIndexOf path lib-path)) (.length lib-path))))))) (defn ^String rel-output-path diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index 91d84c46d..f1796d112 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -292,7 +292,9 @@ (cond->> rel-path asset-path (str asset-path))) (get-rel-path* [output-dir file] - (string/replace (.. (io/file file) getAbsoluteFile getPath) output-dir ""))] + (-> (.. (io/file file) getAbsoluteFile getPath) + (string/replace output-dir "") + (string/replace #"[\\/]" "/")))] (let [get-rel-path (partial get-rel-path* (.. (io/file output-dir) getAbsoluteFile getPath))] diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 37d7da7e9..393b11a2e 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -108,6 +108,9 @@ {:pre [(or (nil? opts) (map? opts))]} (or (:output-dir opts) default))) +(def windows? + (.startsWith (.toLowerCase (System/getProperty "os.name")) "windows")) + (defn file? [f] (instance? File f)) @@ -117,10 +120,21 @@ (defn ^String filename [^File f] (.getName f)) +;; on Windows, URLs end up having forward slashes like +;; /C:/Users/... - Antonio +(defn ^String normalize-path [^String x] + (-> (cond-> x + windows? (string/replace #"^[\\/]" "")) + (string/replace "\\" File/separator) + (string/replace "/" File/separator))) + (defn ^String path [x] (cond (file? x) (.getAbsolutePath ^File x) - (url? x) (.getPath ^URL x) + (url? x) (if windows? + (let [f (URLDecoder/decode (.getFile x))] + (normalize-path f)) + (.getPath ^URL x)) (string? x) x :else (throw (Exception. (str "Expected file, url, or string. Got " (pr-str x)))))) @@ -140,10 +154,7 @@ {:pre [(or (file? x) (url? x))]} (if (file? x) (filename x) - (last (string/split (path x) #"\/")))) - -(def windows? - (.startsWith (.toLowerCase (System/getProperty "os.name")) "windows")) + (last (string/split (path x) #"[\\\/]")))) (defn ^String relative-name "Given a file return a path relative to the working directory. Given a @@ -152,12 +163,7 @@ {:pre [(or (file? x) (url? x))]} (letfn [(strip-user-dir [s] (let [user-dir (System/getProperty "user.dir") - ;; on Windows, URLs end up having forward slashes like - ;; /C:/Users/... - Antonio - s (-> (cond-> s - windows? (string/replace #"^[\\/]" "")) - (string/replace "\\" File/separator) - (string/replace "/" File/separator)) + s (normalize-path s) user-path (cond-> user-dir (not (.endsWith user-dir File/separator)) (str File/separator))] diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 0ddc3f32b..1f6e4c923 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -197,21 +197,21 @@ (deftest cljs-2077-test-loader (let [out (.getPath (io/file (test/tmp-dir) "loader-test-out"))] (test/delete-out-files out) - (let [{:keys [inputs opts]} (merge-with merge (loader-test-project out)) + (let [{:keys [inputs opts]} (loader-test-project out) loader (io/file out "cljs" "loader.js")] - (build/build (build/inputs (io/file inputs "bar.cljs") (io/file inputs "foo.cljs")) opts) + (build/build (build/inputs inputs) opts) (is (.exists loader)) (is (not (nil? (re-find #"[\\/]loader_test[\\/]foo\.js" (slurp loader)))))) (test/delete-out-files out) - (let [project (merge-with merge (loader-test-project out) - {:opts {:optimizations :advanced - :source-map true}})] - (build/build (build/inputs (:inputs project)) (:opts project))) + (let [{:keys [inputs opts]} (merge-with merge (loader-test-project out) + {:opts {:optimizations :advanced + :source-map true}})] + (build/build (build/inputs inputs) opts)) (testing "string inputs in modules" (test/delete-out-files out) - (let [project (merge-with merge (loader-test-project out) - {:opts {:optimizations :whitespace}})] - (build/build (build/inputs (:inputs project)) (:opts project)))))) + (let [{:keys [inputs opts]} (merge-with merge (loader-test-project out) + {:opts {:optimizations :whitespace}})] + (build/build (build/inputs inputs) opts))))) (deftest test-npm-deps (test/delete-node-modules) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index a83bf46c2..1b4d6776c 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -66,7 +66,9 @@ (.add module (closure/js-source-file nil (io/file out file)))) (.sortInputsByDeps module compiler) (is (= (->> (.getInputs module) - (map #(string/replace (.getName %) (str out File/separator) ""))) + (map #(string/replace + (.getName %) + (str (string/replace out #"[\\\/]" "/") "/") ""))) ["cljs/core.js" "cljs/core/constants.js" "module_test/modules/a.js"]))))) diff --git a/src/test/clojure/cljs/util_tests.clj b/src/test/clojure/cljs/util_tests.clj index c3e218b97..ee5b33f64 100644 --- a/src/test/clojure/cljs/util_tests.clj +++ b/src/test/clojure/cljs/util_tests.clj @@ -41,3 +41,7 @@ (is (= (util/relative-name (io/file "/Users/user/clojurescript/out/index.js")) "out/index.js")) (is (= (util/relative-name (io/as-url (io/file "/Users/user/clojurescript/out/index.js"))) "out/index.js")) (System/setProperty "user.dir" initial)))) + +(deftest test-path + (is (= (.getAbsolutePath (io/file "src/main/clojure/cljs/closure.clj")) + (util/path (io/as-url (io/file "src/main/clojure/cljs/closure.clj")))))) From 9b7847ff78b96cb7e82c76f8a1073f7590ebfe61 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sat, 5 Aug 2017 19:13:12 -0700 Subject: [PATCH 2644/4033] CLJS-2266: Self-host: Cannot require clojure.x where clojure.x has no macros namespace --- src/main/cljs/cljs/js.cljs | 44 +++++++++++++++---------------- src/test/self/self_host/test.cljs | 15 +++++++++++ 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 36bc5a391..e8e18037b 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -340,8 +340,6 @@ (str "Could not require " name) cause)))))) (cb {:value true}))))) -(declare ns-side-effects analyze-deps) - (defn- patch-alias-map [compiler in from to] (let [patch (fn [k add-if-present?] @@ -482,23 +480,25 @@ (cb {:value nil}))) (defn- rewrite-ns-ast - [ast smap] - (let [rewrite-renames (fn [m] - (when m - (reduce (fn [acc [renamed qualified-sym :as entry]] - (let [from (symbol (namespace qualified-sym)) - to (get smap from)] - (if (some? to) - (assoc acc renamed (symbol (str to) (name qualified-sym))) - (merge acc entry)))) - {} m)))] - (-> ast - (update :uses #(walk/postwalk-replace smap %)) - (update :use-macros #(walk/postwalk-replace smap %)) - (update :requires #(merge smap (walk/postwalk-replace smap %))) - (update :require-macros #(merge smap (walk/postwalk-replace smap %))) - (update :renames rewrite-renames) - (update :rename-macros rewrite-renames)))) + ([ast smap] + (rewrite-ns-ast ast smap false)) + ([ast smap macros?] + (let [[uk rk renk] (if macros? + [:use-macros :require-macros :rename-macros] + [:uses :requires :renames]) + rewrite-renames (fn [m] + (when m + (reduce (fn [acc [renamed qualified-sym :as entry]] + (let [from (symbol (namespace qualified-sym)) + to (get smap from)] + (if (some? to) + (assoc acc renamed (symbol (str to) (name qualified-sym))) + (merge acc entry)))) + {} m)))] + (-> ast + (update uk #(walk/postwalk-replace smap %)) + (update rk #(merge smap (walk/postwalk-replace smap %))) + (update renk rewrite-renames))))) (defn- check-macro-autoload-inferring-missing [{:keys [requires name] :as ast} cenv] @@ -522,7 +522,7 @@ (if (#{:ns :ns*} op) (letfn [(check-uses-and-load-macros [res rewritten-ast] (let [env (:*compiler* bound-vars) - {:keys [uses require-macros use-macros reload reloads name]} rewritten-ast] + {:keys [uses use-macros reload reloads name]} rewritten-ast] (if (:error res) (cb res) (if (:*load-macros* bound-vars) @@ -532,13 +532,13 @@ (fn [res] (if (:error res) (cb res) - (let [{:keys [require-macros] :as rewritten-ast} (rewrite-ns-ast rewritten-ast (:aliased-loads res))] + (let [{:keys [require-macros] :as rewritten-ast} (rewrite-ns-ast rewritten-ast (:aliased-loads res) true)] (when (:verbose opts) (debug-prn "Processing :require-macros for" (:name ast))) (load-macros bound-vars :require-macros require-macros name reload reloads opts (fn [res'] (if (:error res') (cb res') - (let [{:keys [require-macros use-macros] :as rewritten-ast} (rewrite-ns-ast rewritten-ast (:aliased-loads res)) + (let [{:keys [use-macros] :as rewritten-ast} (rewrite-ns-ast rewritten-ast (:aliased-loads res) true) res' (try (when (seq use-macros) (when (:verbose opts) (debug-prn "Checking :use-macros for" (:name ast))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 6e451f5bb..526ccccf9 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1176,6 +1176,21 @@ (is (= "#foo.core2261b.X{}" value)) (inc! l)))))))) +(deftest test-cljs-2266 + (async done + (let [st (cljs/empty-state) + l (latch 1 done)] + (cljs.js/eval-str st "(require 'clojure.x)" nil + {:eval node-eval + :load (fn [{:keys [name macros]} cb] + (cb (when (and (= name 'cljs.x) + (not macros)) + {:lang :clj + :source "(ns cljs.x)"})))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l)))))) + (defn -main [& args] (run-tests)) From 4f8dde57e01b5f3b23bf454e6c414896cf806e78 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sat, 5 Aug 2017 15:16:30 -0700 Subject: [PATCH 2645/4033] CLJS-2302: Disable process-shim by default in Node.js targets --- src/main/clojure/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cefd7a3b9..5400b7b90 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2020,8 +2020,10 @@ (update opts :modules #(ensure-cljs-base-module % opts))) -(defn shim-process? [opts] - (not (false? (:process-shim opts)))) +(defn shim-process? [{:keys [target process-shim]}] + (if (= target :nodejs) + (true? (:process-shim opts)) + (not (false? (:process-shim opts))))) (defn add-implicit-options [{:keys [optimizations output-dir] From 870d8731014c3cfe56539580eefd5671437fde36 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sat, 5 Aug 2017 15:48:33 -0700 Subject: [PATCH 2646/4033] CLJS-2299: Failure with alias and bad require of clojure.spec --- src/main/clojure/cljs/repl.cljc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 71e1a41ca..f96be3706 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -464,6 +464,7 @@ :source-form form} :repl-env repl-env}) def-emits-var (:def-emits-var opts) + backup-comp @env/*compiler* ->ast (fn [form] (binding [ana/*analyze-deps* false] (ana/analyze (assoc env :def-emits-var def-emits-var) @@ -503,7 +504,11 @@ ;; NOTE: means macros which expand to ns aren't supported for now ;; when eval'ing individual forms at the REPL - David (when (#{:ns :ns*} (:op ast)) - (let [ast (ana/no-warn (ana/analyze env form nil opts))] + (let [ast (try + (ana/no-warn (ana/analyze env form nil opts)) + (catch #?(:clj Exception :cljs js/Error) e + (reset! env/*compiler* backup-comp) + (throw e)))] (load-dependencies repl-env (into (vals (:requires ast)) (distinct (vals (:uses ast)))) From a15846b3adc724f99f0db77c090a067585ebf39c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 7 Aug 2017 10:08:56 -0400 Subject: [PATCH 2647/4033] CLJS-2305 Tests: Unable to resolve symbol: opts in this context --- src/main/clojure/cljs/closure.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5400b7b90..eee43245d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2020,10 +2020,11 @@ (update opts :modules #(ensure-cljs-base-module % opts))) -(defn shim-process? [{:keys [target process-shim]}] - (if (= target :nodejs) - (true? (:process-shim opts)) - (not (false? (:process-shim opts))))) +(defn shim-process? + [{:keys [target process-shim] :as opts}] + (if (= :nodejs target) + (true? process-shim) + (not (false? process-shim)))) (defn add-implicit-options [{:keys [optimizations output-dir] From 02c6d2a706d6668b007c2a186f602b498cfaaa65 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 7 Aug 2017 14:42:08 -0400 Subject: [PATCH 2648/4033] CLJS-2307: Closure warns on unreachable checked array code --- src/main/cljs/cljs/core.cljs | 30 ++++++++++++++++-------------- src/main/clojure/cljs/core.cljc | 3 +++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fe8772807..f8f85a1c0 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -439,26 +439,28 @@ (defn- checked-aget ([array idx] - (try - (assert (or (array? array) (js/goog.isArrayLike array))) - (assert (number? idx)) - (assert (not (neg? idx))) - (assert (< idx (alength array))) - (catch :default e - (maybe-warn e))) + (when-assert + (try + (assert (or (array? array) (js/goog.isArrayLike array))) + (assert (number? idx)) + (assert (not (neg? idx))) + (assert (< idx (alength array))) + (catch :default e + (maybe-warn e)))) (unchecked-get array idx)) ([array idx & idxs] (apply checked-aget (checked-aget array idx) idxs))) (defn- checked-aset ([array idx val] - (try - (assert (or (array? array) (js/goog.isArrayLike array))) - (assert (number? idx)) - (assert (not (neg? idx))) - (assert (< idx (alength array))) - (catch :default e - (maybe-warn e))) + (when-assert + (try + (assert (or (array? array) (js/goog.isArrayLike array))) + (assert (number? idx)) + (assert (not (neg? idx))) + (assert (< idx (alength array))) + (catch :default e + (maybe-warn e)))) (unchecked-set array idx val)) ([array idx idx2 & idxv] (apply checked-aset (checked-aget array idx) idx2 idxv))) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index c64ac1807..d03115489 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2290,6 +2290,9 @@ ~@(mapcat (core/fn [[m c]] `((cljs.core/= ~m ~esym) ~c)) pairs) :else ~default))))) +(core/defmacro ^:private when-assert [x] + (core/when *assert* x)) + (core/defmacro assert "Evaluates expr and throws an exception if it does not evaluate to logical true." From 6e0715ca03d642700e9f538a95961a3dca4104f1 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sat, 5 Aug 2017 19:30:42 -0700 Subject: [PATCH 2649/4033] CLJS-2303: Disable duplicate alias checking for self-host --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- src/test/self/self_host/test.cljs | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 89b793ef2..3c55427a0 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2746,8 +2746,9 @@ (let [merge-keys [:use-macros :require-macros :rename-macros :uses :requires :renames :imports]] - (when *check-alias-dupes* - (check-duplicate-aliases env ns-info' require-info)) + #?(:clj + (when *check-alias-dupes* + (check-duplicate-aliases env ns-info' require-info))) (merge ns-info' {:excludes excludes} diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 526ccccf9..ad2f295bf 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1191,6 +1191,27 @@ (is (nil? error)) (inc! l)))))) +(deftest test-cljs-2303 + (async done + (let [st (cljs/empty-state) + load (fn [{:keys [name macros]} cb] + (cb (when (and (= name 'cljs.x) + (not macros)) + {:lang :clj + :source "(ns cljs.x)"}))) + l (latch 1 done)] + (cljs.js/eval-str st "(require 'clojure.x)" nil + {:eval node-eval + :load load} + (fn [{:keys [error]}] + (is (nil? error)) + (cljs.js/eval-str st "(require 'clojure.x)" nil + {:eval node-eval + :load load} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l)))))))) + (defn -main [& args] (run-tests)) From 06d81bcbe11f1d0b4166c106c950f46dbfd8c714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Mon, 7 Aug 2017 10:42:30 -0700 Subject: [PATCH 2650/4033] CLJS-2306: Provide better warning message when namespace can't be found --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3c55427a0..ff8177444 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -821,7 +821,7 @@ ;; confirm that the library at least exists #?(:clj (nil? (util/ns->source ns-sym))) (not (js-module-exists? ns-sym))) - (warning :undeclared-ns env {:ns-sym ns-sym}))) + (warning :undeclared-ns env {:ns-sym ns-sym :js-provide ns-sym}))) (defn core-name? "Is sym visible from core in the current compilation namespace?" From b63830313c1fbede0a2973481509ecba18912f21 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 10 Aug 2017 06:48:59 -0400 Subject: [PATCH 2651/4033] wip (do not use) --- src/main/clojure/cljs/compiler.cljc | 5 ++++- src/test/cljs_build/loader_test/foo.cljs | 3 ++- src/test/cljs_build/loader_test/foreign.js | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 2 ++ 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/test/cljs_build/loader_test/foreign.js diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 766c3d441..f63227456 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1098,7 +1098,10 @@ #?@(:clj [(ana/foreign-dep? lib) ;; we only load foreign libraries under optimizations :none - (when (= :none optimizations) + ;; under :modules we also elide loads, as the module loader will + ;; have handled it - David + (when (and (= :none optimizations) + (not (contains? options :modules))) (if (= :nodejs target) ;; under node.js we load foreign libs globally (let [ijs (get js-dependency-index (name lib))] diff --git a/src/test/cljs_build/loader_test/foo.cljs b/src/test/cljs_build/loader_test/foo.cljs index 4c46047cf..fdbf50410 100644 --- a/src/test/cljs_build/loader_test/foo.cljs +++ b/src/test/cljs_build/loader_test/foo.cljs @@ -1,7 +1,8 @@ (ns loader-test.foo (:require [goog.dom :as gdom] [goog.events :as events] - [cljs.loader :as loader]) + [cljs.loader :as loader] + [my.foreign]) (:import [goog.events EventType])) (enable-console-print!) diff --git a/src/test/cljs_build/loader_test/foreign.js b/src/test/cljs_build/loader_test/foreign.js new file mode 100644 index 000000000..dff6ae896 --- /dev/null +++ b/src/test/cljs_build/loader_test/foreign.js @@ -0,0 +1,3 @@ +var foreign = function() { + console.log("I'm foreign!") +}; \ No newline at end of file diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 1f6e4c923..6e24c5a9c 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -186,6 +186,8 @@ {:output-dir output-dir :optimizations :none :verbose true + :foreign-libs [{:file "loader_test/foreign.js" + :provides ["my.foreign"]}] :modules {:foo {:output-to (str (io/file output-dir "foo.js")) From 5c12d660561492ab98bddd39d5e5da1dfb6bb74f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 9 Aug 2017 16:47:28 -0400 Subject: [PATCH 2652/4033] CLJS-2314: Eliminate str call on literal strings in str macro --- src/main/clojure/cljs/core.cljc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index d03115489..685813353 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -819,7 +819,12 @@ (core/inc (core/quot c 32))))) (core/defmacro str [& xs] - (core/let [strs (core/->> (repeat (count xs) "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})") + (core/let [interpolate (core/fn [x] + (if (core/string? x) + "~{}" + "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})")) + strs (core/->> xs + (map interpolate) (interpose ",") (apply core/str))] (list* 'js* (core/str "[" strs "].join('')") xs))) From 5d7fd9c1fbc7afb0b52ff581d1ec2d93f114ff0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 9 Aug 2017 09:43:41 -0700 Subject: [PATCH 2653/4033] CLJS-2313: :language-out is a build affecting option --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 766c3d441..cd1db9b14 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1246,7 +1246,7 @@ (defn- build-affecting-options [opts] (select-keys opts [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target - :cache-key :checked-arrays])) + :cache-key :checked-arrays :language-out])) #?(:clj (defn compiled-by-string From fe77a1349aba903856cbaa0a0f10e03b6fb30d92 Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Thu, 13 Jul 2017 19:48:51 +0200 Subject: [PATCH 2654/4033] CLJS-2234: Make build scripts optionally less verbose --- script/aot_core | 6 +++++- script/build | 6 +++++- script/uberjar | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/script/aot_core b/script/aot_core index 58496a073..dfa9c36ee 100755 --- a/script/aot_core +++ b/script/aot_core @@ -1,6 +1,10 @@ #!/usr/bin/env bash -set -ex +set -e + +if [[ -z "$CLJS_SCRIPT_QUIET" ]]; then + set -x +fi FILE_SEP='/' PATH_SEP=':' diff --git a/script/build b/script/build index 75c45fe7a..d579c2b0d 100755 --- a/script/build +++ b/script/build @@ -3,7 +3,11 @@ # This script must be run within the ClojureScript top-level project # directory. -set -ex +set -e + +if [[ -z "$CLJS_SCRIPT_QUIET" ]]; then + set -x +fi cd `dirname $0`/.. rm -rf target diff --git a/script/uberjar b/script/uberjar index fde84a745..95f6b6a51 100755 --- a/script/uberjar +++ b/script/uberjar @@ -3,7 +3,11 @@ # This script must be run within the ClojureScript top-level project # directory. -set -ex +set -e + +if [[ -z "$CLJS_SCRIPT_QUIET" ]]; then + set -x +fi # The command `git describe --match v0.0` will return a string like # From 8e4f49d73a83972f27de478424f8ca8de7d6742a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 9 Aug 2017 17:52:33 -0700 Subject: [PATCH 2655/4033] CLJS-2315: module_deps.js can't resolve JSON modules --- src/main/cljs/cljs/module_deps.js | 9 +++++---- src/test/cljs_build/json_modules_test/a.js | 2 ++ src/test/cljs_build/json_modules_test/b.json | 1 + src/test/clojure/cljs/closure_tests.clj | 20 ++++++++++++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 src/test/cljs_build/json_modules_test/a.js create mode 100644 src/test/cljs_build/json_modules_test/b.json diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index e26b01151..88955fd7f 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -9,13 +9,14 @@ var filename = path.resolve(__dirname, 'JS_FILE'); var resolver = target === 'nodejs' ? nodeResolve : browserResolve; var md = mdeps({ - resolve: function(id, parent, cb) { + resolve: function(id, parentOpts, cb) { // set the basedir properly so we don't try to resolve requires in the Closure // Compiler processed `node_modules` folder. - parent.basedir = - parent.filename === filename ? __dirname : path.dirname(parent.filename); + parentOpts.basedir = + parentOpts.filename === filename ? __dirname : path.dirname(parentOpts.filename); + parentOpts.extensions = ['.js', '.json']; - resolver(id, parent, cb); + resolver(id, parentOpts, cb); }, filter: function(id) { return !(target === 'nodejs' && nodeResolve.isCore(id)); diff --git a/src/test/cljs_build/json_modules_test/a.js b/src/test/cljs_build/json_modules_test/a.js new file mode 100644 index 000000000..970187ecd --- /dev/null +++ b/src/test/cljs_build/json_modules_test/a.js @@ -0,0 +1,2 @@ +// b is a .json module +var theJSON = require('./b'); diff --git a/src/test/cljs_build/json_modules_test/b.json b/src/test/cljs_build/json_modules_test/b.json new file mode 100644 index 000000000..f17a4c295 --- /dev/null +++ b/src/test/cljs_build/json_modules_test/b.json @@ -0,0 +1 @@ +{"foo": 42} diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 1b4d6776c..fcb76c30d 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -10,6 +10,8 @@ (:refer-clojure :exclude [compile]) (:use cljs.closure clojure.test) (:require [cljs.build.api :as build] + [clojure.data.json :as json] + [clojure.java.shell :as sh] [cljs.closure :as closure] [cljs.js-deps :as deps] [cljs.util :as util] @@ -240,3 +242,21 @@ (.delete (io/file "package.json")) (test/delete-node-modules) (test/delete-out-files out))) + +(deftest test-cljs-2315 + (spit (io/file "package.json") (json/json-str {:devDependencies {"@cljs-oss/module-deps" "*" + :konan "*" + :resolve "*" + :browser-resolve "*"}})) + (apply sh/sh (cond->> ["npm" "install"] + util/windows? (into ["cmd" "/c"]))) + (let [file (io/file (test/tmp-dir) "cljs-2315-inputs.js") + _ (spit file "require('./src/test/cljs_build/json_modules_test/a.js');") + node-inputs (closure/node-inputs [{:file (str file)}])] + (is (= node-inputs + [{:file (.getAbsolutePath (io/file "src/test/cljs_build/json_modules_test/a.js")) + :module-type :es6} + {:file (.getAbsolutePath (io/file "src/test/cljs_build/json_modules_test/b.json")) + :module-type :es6}]))) + (.delete (io/file "package.json")) + (test/delete-node-modules)) From 20a6e1fcd8724ad65a3776112dcc7bfd5124a094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Wed, 9 Aug 2017 10:30:52 -0700 Subject: [PATCH 2656/4033] CLJS-2312: Miss-compile: Uncaught SyntaxError: Unexpected token default --- src/main/cljs/cljs/pprint.cljs | 2 +- src/main/cljs/cljs/stacktrace.cljc | 6 +++--- src/main/cljs/cljs/test.cljs | 8 ++++---- src/main/clojure/cljs/analyzer.cljc | 18 +++++++++--------- src/main/clojure/cljs/compiler.cljc | 18 +++++++++++------- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 2654e89be..1dfde81e2 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -2537,7 +2537,7 @@ of parameters as well." (merge ; create the result map (into (array-map) ; start with the default values, make sure the order is right - (reverse (for [[name [default-def]] (:params def)] [name [default-def offset]]))) + (reverse (for [[name [default]] (:params def)] [name [default offset]]))) (reduce #(apply assoc %1 %2) {} (filter #(first (nth % 1)) (zipmap (keys (:params def)) params))) ; add the specified parameters, filtering out nils flags)); and finally add the flags diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index df03de098..0f05a10e3 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -576,11 +576,11 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" (second (first columns))))) (adjust [mapped] (vec (map #(%1 %2) [inc inc identity] mapped)))] - (let [default-ret [line column nil]] + (let [default [line column nil]] ;; source maps are 0 indexed for lines (if-let [columns (get source-map (dec line))] (adjust (map (get-best-column columns column) [:line :col :name])) - default-ret))))) + default))))) (defn mapped-frame "Given opts and a canonicalized JavaScript stacktrace frame, return the @@ -689,4 +689,4 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" :line 6 :column 0}] sms {:output-dir "samples/hello/out"}) - ) \ No newline at end of file + ) diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index 5499ab6ec..4ec51c48a 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -386,7 +386,7 @@ first))) (defn mapped-line-and-column [filename line column] - (let [default-ret [filename line column]] + (let [default [filename line column]] (if-let [source-map (:source-map (get-current-env))] ;; source maps are 0 indexed for lines (if-let [columns (get-in source-map [filename (dec line)])] @@ -400,8 +400,8 @@ mapping (second (first columns)))) [:source :line :col])) - default-ret) - default-ret))) + default) + default))) (defn file-and-line [exception depth] ;; TODO: flesh out @@ -439,7 +439,7 @@ (defn run-block "Invoke all functions in fns with no arguments. A fn can optionally return - + an async test - is invoked with a continuation running left fns a seq of fns tagged per block - are invoked immediately after fn" diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ff8177444..973aea843 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1359,14 +1359,14 @@ :children [test-expr then-expr else-expr]})) (defmethod parse 'case* - [op env [_ sym tests thens default-clause :as form] name _] + [op env [_ sym tests thens default :as form] name _] (assert (symbol? sym) "case* must switch on symbol") (assert (every? vector? tests) "case* tests must be grouped in vectors") (let [expr-env (assoc env :context :expr) v (disallowing-recur (analyze expr-env sym)) tests (mapv #(mapv (fn [t] (analyze expr-env t)) %) tests) thens (mapv #(analyze env %) thens) - default-clause (analyze env default-clause)] + default (analyze env default)] (assert (every? (fn [t] (or (-> t :info :const) @@ -1375,8 +1375,8 @@ (apply concat tests)) "case* tests must be numbers, strings, or constants") {:env env :op :case* :form form - :v v :tests tests :thens thens :default default-clause - :children (vec (concat [v] tests thens (if default-clause [default-clause])))})) + :v v :tests tests :thens thens :default default + :children (vec (concat [v] tests thens (if default [default])))})) (defmethod parse 'throw [op env [_ throw :as form] name _] @@ -1415,9 +1415,9 @@ finally (when (seq fblock) (analyze (assoc env :context :statement) `(do ~@(rest fblock)))) e (when (or (seq cblocks) dblock) (gensym "e")) - default-block (if-let [[_ _ name & cb] dblock] - `(cljs.core/let [~name ~e] ~@cb) - `(throw ~e)) + default (if-let [[_ _ name & cb] dblock] + `(cljs.core/let [~name ~e] ~@cb) + `(throw ~e)) cblock (if (seq cblocks) `(cljs.core/cond ~@(mapcat @@ -1426,8 +1426,8 @@ `[(cljs.core/instance? ~type ~e) (cljs.core/let [~name ~e] ~@cb)]) cblocks) - :else ~default-block) - default-block) + :else ~default) + default) locals (:locals catchenv) locals (if e (assoc locals e diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index cd1db9b14..b7bbad583 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -349,16 +349,20 @@ (let [js-module-name (get-in cenv [:js-module-index (name var-name) :name])] (or js-module-name (name var-name))) info)] - ; We need a way to write bindings out to source maps and javascript - ; without getting wrapped in an emit-wrap calls, otherwise we get - ; e.g. (function greet(return x, return y) {}). + ;; We need a way to write bindings out to source maps and javascript + ;; without getting wrapped in an emit-wrap calls, otherwise we get + ;; e.g. (function greet(return x, return y) {}). (if (:binding-form? ast) - ; Emit the arg map so shadowing is properly handled when munging - ; (prevents duplicate fn-param-names) + ;; Emit the arg map so shadowing is properly handled when munging + ;; (prevents duplicate fn-param-names) (emits (munge ast)) (when-not (= :statement (:context env)) (let [reserved (cond-> js-reserved - (es5>= (:language-out options)) + (and (es5>= (:language-out options)) + ;; we can skip munging things like `my.ns.default` + ;; but not standalone `default` variable names + ;; as they're not valid ES5 - Antonio + (some? (namespace var-name))) (set/difference ana/es5-allowed))] (emit-wrap env (emits @@ -1045,7 +1049,7 @@ keyword? (emits f ".cljs$core$IFn$_invoke$arity$" (count args) "(" (comma-sep args) ")") - + variadic-invoke (let [mfa (:max-fixed-arity variadic-invoke)] (emits f "(" (comma-sep (take mfa args)) From 92433701acc6f86665b81349dc8c9eb4048ec464 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Fri, 11 Aug 2017 08:30:02 -0700 Subject: [PATCH 2657/4033] CLJS-2317: Upgrade Google Closure Library --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/cljs/cljs/bootstrap_node.js | 22 +++++++++++++++++++--- src/test/self/self_parity/auxiliary.cljs | 2 -- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 3f1b4a647..7ba606056 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20170519-fa0499ef + 0.0-20170809-b9c14c6b org.clojure diff --git a/project.clj b/project.clj index 958165587..0f21e0018 100644 --- a/project.clj +++ b/project.clj @@ -13,7 +13,7 @@ [org.clojure/tools.reader "1.0.5"] [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] - [org.clojure/google-closure-library "0.0-20170519-fa0499ef"] + [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] [com.google.javascript/closure-compiler-unshaded "v20170626"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} diff --git a/script/bootstrap b/script/bootstrap index af0901b76..d152a8674 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -8,7 +8,7 @@ CORE_SPECS_ALPHA_RELEASE="0.1.10" CLOSURE_RELEASE="20170626" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" -GCLOSURE_LIB_RELEASE="0.0-20160609-f42b4a24" +GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.0.5" TEST_CHECK_RELEASE="0.10.0-alpha2" diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_node.js index 974f6aa40..66a19f2dd 100644 --- a/src/main/cljs/cljs/bootstrap_node.js +++ b/src/main/cljs/cljs/bootstrap_node.js @@ -57,7 +57,7 @@ global.goog = {}; * @param {string} src The script source. * @return {boolean} True if the script was imported, false otherwise. */ -global.CLOSURE_IMPORT_SCRIPT = function(src) { +global.CLOSURE_IMPORT_SCRIPT = function(src, opt_sourceText) { // if CLJS_ROOT has been rewritten (by REPLs) need to compute require path // so we can delete the old entry from the Node.js require cache if(CLJS_ROOT !== ".") { @@ -72,17 +72,33 @@ global.CLOSURE_IMPORT_SCRIPT = function(src) { // Sources are always expressed relative to closure's base.js, but // require() is always relative to the current source. - require(path.join(".", "..", src)); + if (opt_sourceText === undefined) { + require(path.join(".", "..", src)); + } else { + eval(opt_sourceText); + } return true; }; +/** + * Loads a file when using Closure's goog.require() API with goog.modules. + * + * @param {string} src The file source. + * @return {string} The file contents. + */ +global.CLOSURE_LOAD_FILE_SYNC = function(src) { + return fs.readFileSync( + path.resolve(__dirname, '..', src), {encoding: 'utf-8'}); +}; + + // Declared here so it can be used to require base.js function nodeGlobalRequire(file) { var _module = global.module, _exports = global.exports; global.module = undefined; global.exports = undefined; - vm.runInThisContext(fs.readFileSync(file), file); + vm.runInThisContext.call(global, fs.readFileSync(file), file); global.exports = _exports; global.module = _module; } diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs index 0ceb34f4c..5cfb6b8c2 100644 --- a/src/test/self/self_parity/auxiliary.cljs +++ b/src/test/self/self_parity/auxiliary.cljs @@ -88,7 +88,6 @@ goog.iter.Iterator goog.json goog.json.EvalJsonProcessor - goog.json.HybridJsonProcessor goog.json.NativeJsonProcessor goog.json.Replacer goog.json.Reviver @@ -152,5 +151,4 @@ goog.structs.StringSet goog.structs.TreeNode goog.structs.Trie - goog.structs.weak goog.text.LoremIpsum)) From 5ee5f3f1e35b77eb0953c696b79889699c9c56ba Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Wed, 9 Aug 2017 20:07:38 -0700 Subject: [PATCH 2658/4033] CLJS-2316: Upgrade Closure Compiler to August release --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 20 +++++++------------- script/bootstrap.ps1 | 10 ++++------ src/main/clojure/cljs/closure.clj | 1 - 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 7ba606056..560d35de2 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20170626 + v20170806 org.clojure diff --git a/project.clj b/project.clj index 0f21e0018..dae2581e1 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] - [com.google.javascript/closure-compiler-unshaded "v20170626"] + [com.google.javascript/closure-compiler-unshaded "v20170806"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index d152a8674..282dd0b75 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,7 +5,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha17" SPEC_ALPHA_RELEASE="0.1.123" CORE_SPECS_ALPHA_RELEASE="0.1.10" -CLOSURE_RELEASE="20170626" +CLOSURE_RELEASE="20170806" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" @@ -55,7 +55,7 @@ echo "Fetching transit-clj..." curl --retry 3 -O -s https://repo1.maven.org/maven2/com/cognitect/transit-clj/$TRANSIT_RELEASE/transit-clj-$TRANSIT_RELEASE.jar || { echo "Download failed."; exit 1; } echo "Copying transit-clj-$TRANSIT_RELEASE.jar to lib/transit-clj-$TRANSIT_RELEASE.jar..." cp transit-clj-$TRANSIT_RELEASE.jar lib/transit-clj-$TRANSIT_RELEASE.jar -echo "Cleaning up data.json..." +echo "Cleaning up transit-clj..." rm transit-clj-$TRANSIT_RELEASE.jar echo "Fetching Google Closure library..." @@ -86,16 +86,13 @@ else cp google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar ../../lib/google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar rm google-closure-library-third-party-$GCLOSURE_LIB_RELEASE.jar fi -cd .. +cd ../.. echo "Fetching Google Closure compiler..." -mkdir -p compiler -cd compiler -curl --retry 3 -O -s https://dl.google.com/closure-compiler/compiler-$CLOSURE_RELEASE.zip || { echo "Download failed."; exit 1; } -unzip -qu compiler-$CLOSURE_RELEASE.zip -echo "Cleaning up Google Closure compiler archive..." -rm compiler-$CLOSURE_RELEASE.zip -cd ../.. +curl --retry 3 -O -s http://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v$CLOSURE_RELEASE/closure-compiler-v$CLOSURE_RELEASE.jar || { echo "Download failed."; exit 1; } +cp closure-compiler-v$CLOSURE_RELEASE.jar lib/closure-compiler-v$CLOSURE_RELEASE.jar +echo "Cleaning up closure-compiler.jar..." +rm closure-compiler-v$CLOSURE_RELEASE.jar if [ "$1" = "--closure-library-head" ] ; then echo "Building lib/goog.jar..." @@ -113,9 +110,6 @@ rm -rf rhino$RHINO_RELEASE/ echo "Cleaning up Rhino archive..." rm rhino$RHINO_RELEASE.zip -echo "Copying closure/compiler/compiler.jar to lib/compiler.jar" -cp closure/compiler/closure-compiler-v$CLOSURE_RELEASE.jar lib - echo "Fetching tools.reader $TREADER_RELEASE ..." curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar || { echo "Download failed."; exit 1; } diff --git a/script/bootstrap.ps1 b/script/bootstrap.ps1 index e918d76b7..7edd241f1 100644 --- a/script/bootstrap.ps1 +++ b/script/bootstrap.ps1 @@ -128,12 +128,10 @@ Get-WebResource ` Write-Host "Fetching Google Closure compiler..." Get-WebResource ` - https://dl.google.com/closure-compiler/compiler-$CLOSURE_RELEASE.zip ` - $root\compiler-$CLOSURE_RELEASE.zip -Get-ChildItem $root\closure\compiler\* | Delete-File -Expand-ZipFile $root\compiler-$CLOSURE_RELEASE.zip $root\closure\compiler -Copy-File $root\closure\compiler\closure-compiler-v$CLOSURE_RELEASE.jar $root\lib\compiler.jar -Delete-File $root\compiler-$CLOSURE_RELEASE.zip + http://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v$CLOSURE_RELEASE/closure-compiler-v$CLOSURE_RELEASE.jar ` + $root\closure-compiler-v$CLOSURE_RELEASE.jar +Copy-File $root\closure-compiler-v$CLOSURE_RELEASE.jar $root\lib\compiler.jar +Delete-File $root\closure-compiler-v$CLOSURE_RELEASE.jar Write-Host "Fetching Rhino..." Get-WebResource ` diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index eee43245d..909177229 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -123,7 +123,6 @@ :fileoverview-jsdoc DiagnosticGroups/FILEOVERVIEW_JSDOC :function-params DiagnosticGroups/FUNCTION_PARAMS :global-this DiagnosticGroups/GLOBAL_THIS - :inferred-const-checks DiagnosticGroups/INFERRED_CONST_CHECKS :internet-explorer-checks DiagnosticGroups/INTERNET_EXPLORER_CHECKS :invalid-casts DiagnosticGroups/INVALID_CASTS :j2cl-checks DiagnosticGroups/J2CL_CHECKS From 1163381724c0e8ab11e998e9ab02d7a752848acc Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sat, 12 Aug 2017 20:51:54 -0700 Subject: [PATCH 2659/4033] CLJS-2318: module-deps.js doesn't respect the package.json `module` field --- appveyor.yml | 2 + src/main/cljs/cljs/module_deps.js | 45 ++++++++++++++++++----- src/main/clojure/cljs/build/api.clj | 4 +- src/main/clojure/cljs/closure.clj | 10 ++--- src/test/clojure/cljs/build_api_tests.clj | 3 +- src/test/clojure/cljs/closure_tests.clj | 22 ++++++++++- 6 files changed, 66 insertions(+), 20 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index fc8394f07..9818d2181 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,5 @@ +image: Visual Studio 2017 + environment: nodejs_version: "6" diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 88955fd7f..ae31444ba 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -1,22 +1,35 @@ +var fs = require('fs'); var path = require('path'); var mdeps = require('@cljs-oss/module-deps'); var nodeResolve = require('resolve'); -var browserResolve = require('browser-resolve'); var konan = require('konan'); +var enhancedResolve = require('enhanced-resolve'); var target = 'CLJS_TARGET'; -var filename = path.resolve(__dirname, 'JS_FILE'); -var resolver = target === 'nodejs' ? nodeResolve : browserResolve; +var filename = fs.realpathSync(path.resolve(__dirname, 'JS_FILE')); +var mainFields = + target === 'nodejs' ? ['module', 'main'] : ['module', 'browser', 'main']; + +var resolver = enhancedResolve.create({ + fileSystem: new enhancedResolve.CachedInputFileSystem( + new enhancedResolve.NodeJsInputFileSystem(), + 4000 + ), + extensions: ['.js', '.json'], + mainFields: mainFields, + moduleExtensions: ['.js', '.json'], +}); var md = mdeps({ resolve: function(id, parentOpts, cb) { // set the basedir properly so we don't try to resolve requires in the Closure // Compiler processed `node_modules` folder. parentOpts.basedir = - parentOpts.filename === filename ? __dirname : path.dirname(parentOpts.filename); - parentOpts.extensions = ['.js', '.json']; + parentOpts.filename === filename + ? path.resolve(__dirname) + : path.dirname(parentOpts.filename); - resolver(id, parentOpts, cb); + resolver(parentOpts.basedir, id, cb); }, filter: function(id) { return !(target === 'nodejs' && nodeResolve.isCore(id)); @@ -28,6 +41,17 @@ var md = mdeps({ }, }); +function getPackageJsonMainEntry(pkgJson) { + for (var i = 0; i < mainFields.length; i++) { + var entry = mainFields[i]; + + if (pkgJson[entry] != null) { + return pkgJson[entry]; + } + } + return null; +} + var pkgJsons = []; var deps_files = {}; @@ -42,8 +66,9 @@ md.on('package', function(pkg) { pkgJson.provides = [pkg.name]; } - if (pkg.main != null) { - pkgJson.main = path.join(pkg.__dirname, pkg.main); + var pkgJsonMainEntry = getPackageJsonMainEntry(pkg); + if (pkgJsonMainEntry != null) { + pkgJson.mainEntry = path.join(pkg.__dirname, pkgJsonMainEntry); } pkgJsons.push(pkgJson); @@ -58,8 +83,8 @@ md.on('end', function() { for (var i = 0; i < pkgJsons.length; i++) { var pkgJson = pkgJsons[i]; - if (deps_files[pkgJson.main] != null && pkgJson.provides != null) { - deps_files[pkgJson.main].provides = pkgJson.provides; + if (deps_files[pkgJson.mainEntry] != null && pkgJson.provides != null) { + deps_files[pkgJson.mainEntry].provides = pkgJson.provides; } deps_files[pkgJson.file] = { file: pkgJson.file }; diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 03dc1050b..5577b5d0d 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -275,8 +275,8 @@ (defn node-inputs "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry points. Assumes - that the `@cljs-oss/module-deps` and `konan` NPM packages are either - locally or globally installed." + that the `@cljs-oss/module-deps`, `enhanced-resolve` and `konan` NPM packages + are either locally or globally installed." [entries] (closure/node-inputs entries)) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 909177229..7e57e31c0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2117,7 +2117,7 @@ (spit pkg-json "{}")) (let [proc (-> (ProcessBuilder. (into (cond->> ["npm" "install" "@cljs-oss/module-deps" - "resolve" "browser-resolve" "konan"] + "enhanced-resolve" "resolve" "konan"] util/windows? (into ["cmd" "/c"])) (map (fn [[dep version]] (str (name dep) "@" version))) npm-deps)) @@ -2141,8 +2141,8 @@ (defn node-module-deps "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry point. Assumes - that the `@cljs-oss/module-deps` and `konan` NPM packages are either - locally or globally installed." + that the `@cljs-oss/module-deps`, `enhanced-resolve`, `enhanced-resolve` and + `konan` NPM packages are either locally or globally installed." ([entry] (node-module-deps entry (when env/*compiler* @@ -2181,8 +2181,8 @@ (defn node-inputs "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry points. Assumes - that the `@cljs-oss/module-deps` and `konan` NPM packages are either - locally or globally installed." + that the `@cljs-oss/module-deps`, `enhanced-resolve` and `konan` NPM packages + are either locally or globally installed." ([entries] (node-inputs entries (when env/*compiler* diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 1f6e4c923..a289e4e24 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -405,8 +405,7 @@ (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} :devDependencies {"@cljs-oss/module-deps" "*" :konan "*" - :resolve "*" - :browser-resolve "*"}})) + :enhanced-resolve "*"}})) (apply sh/sh (cond->> ["npm" "install"] util/windows? (into ["cmd" "/c"]))) (let [ws (atom []) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index fcb76c30d..9258831d9 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -247,7 +247,7 @@ (spit (io/file "package.json") (json/json-str {:devDependencies {"@cljs-oss/module-deps" "*" :konan "*" :resolve "*" - :browser-resolve "*"}})) + :enhanced-resolve "*"}})) (apply sh/sh (cond->> ["npm" "install"] util/windows? (into ["cmd" "/c"]))) (let [file (io/file (test/tmp-dir) "cljs-2315-inputs.js") @@ -260,3 +260,23 @@ :module-type :es6}]))) (.delete (io/file "package.json")) (test/delete-node-modules)) + +(deftest test-cljs-2318 + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {:react "15.6.1" + :react-dom "15.6.1" + :react-addons-css-transition-group "15.5.1" + "@blueprintjs/core" "1.24.0"}} + out (util/output-directory opts)] + (test/delete-node-modules) + (test/delete-out-files out) + (closure/maybe-install-node-deps! opts) + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/tslib/tslib.es6.js")) + :provides ["tslib" + "tslib/tslib.es6.js" + "tslib/tslib.es6"]})) + (closure/index-node-modules ["tslib"] opts)))) + (test/delete-node-modules) + (test/delete-out-files out))) From a452351f22f885ef2012880c01a192616af87d38 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 13 Aug 2017 17:00:19 -0400 Subject: [PATCH 2660/4033] remove foreign bits from test for now --- src/test/cljs_build/loader_test/foo.cljs | 3 ++- src/test/clojure/cljs/build_api_tests.clj | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/cljs_build/loader_test/foo.cljs b/src/test/cljs_build/loader_test/foo.cljs index fdbf50410..4218cbdd6 100644 --- a/src/test/cljs_build/loader_test/foo.cljs +++ b/src/test/cljs_build/loader_test/foo.cljs @@ -2,7 +2,8 @@ (:require [goog.dom :as gdom] [goog.events :as events] [cljs.loader :as loader] - [my.foreign]) + ;[my.foreign] + ) (:import [goog.events EventType])) (enable-console-print!) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 6e24c5a9c..9720587d6 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -186,8 +186,8 @@ {:output-dir output-dir :optimizations :none :verbose true - :foreign-libs [{:file "loader_test/foreign.js" - :provides ["my.foreign"]}] + ;:foreign-libs [{:file "loader_test/foreign.js" + ; :provides ["my.foreign"]}] :modules {:foo {:output-to (str (io/file output-dir "foo.js")) From 345041b87f8004102aa300b50829e70fa0d646c4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 13 Aug 2017 17:28:01 -0400 Subject: [PATCH 2661/4033] only load base deps in the `:cljs-base` module, not all modules add docstring to output-main-file, simplify required arguments for module case pass all inputs and expanded modules to output-main-file, prep for loading all foreign libs separately --- src/main/clojure/cljs/closure.clj | 74 +++++++++++++++++++------------ 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index eee43245d..6d054320d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1478,40 +1478,56 @@ (if (= :browser mode) "');\n" "\n")))] (map preload-str syms)))) -(defn output-main-file [opts] - (let [asset-path (or (:asset-path opts) +(defn output-main-file + "Output an entry point. In the non-modules case, opts is simply compiler + options. When emitting a module entry point opts must contain :module-name, + fully expanded :modules and :inputs (all compiler IJavaScript input sources)." + [opts] + (assert (or (not (contains? opts :module-name)) + (get (:modules opts) (:module-name opts))) + (str "Module " (:module-name opts) " does not exist")) + (let [module (get (:modules opts) (:module-name opts)) + asset-path (or (:asset-path opts) (util/output-directory opts)) closure-defines (json/write-str (:closure-defines opts))] (case (:target opts) :nodejs - (output-one-file opts + (output-one-file + (merge opts + (when module + {:output-to (:output-to module)})) (add-header opts - (str "var path = require(\"path\");\n" - "try {\n" - " require(\"source-map-support\").install();\n" - "} catch(err) {\n" - "}\n" - "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" - "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" - "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - (apply str (preloads (:preloads opts))) - (apply str - (map (fn [entry] - (str "goog.require(\"" (comp/munge entry) "\");\n")) - (if-let [entries (:entries opts)] - entries - [(:main opts)]))) + (str (when (or (not module) (= :cljs-base (:module-name opts))) + (str "var path = require(\"path\");\n" + "try {\n" + " require(\"source-map-support\").install();\n" + "} catch(err) {\n" + "}\n" + "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" + "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" + "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + (apply str (preloads (:preloads opts))))) + (apply str + (map (fn [entry] + (str "goog.require(\"" (comp/munge entry) "\");\n")) + (if-let [entries (when module (:entries module))] + entries + [(:main opts)]))) "goog.require(\"cljs.nodejscli\");\n"))) - (output-one-file opts - (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - "if(typeof goog == \"undefined\") document.write('');\n" - "document.write('');\n" - "document.write('');\n" - (apply str (preloads (:preloads opts) :browser)) + (output-one-file + (merge opts + (when module + {:output-to (:output-to module)})) + (str (when (or (not module) (= :cljs-base (:module-name opts))) + (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "if(typeof goog == \"undefined\") document.write('');\n" + "document.write('');\n" + "document.write('');\n" + (apply str (preloads (:preloads opts) :browser)))) (apply str (map (fn [entry] (str "document.write('');\n")) - (if-let [entries (:entries opts)] + (if-let [entries (when module (:entries module))] entries (when-let [main (:main opts)] [main]))))))))) @@ -1771,14 +1787,16 @@ (spit goog-deps (slurp (io/resource "goog/deps.js"))) (cond modules - (do + (let [modules' (module-graph/expand-modules modules sources)] (output-deps) (doall (map - (fn [[module-name {:keys [output-to entries]}]] + (fn [[module-name _]] (output-main-file (merge opts - {:output-to output-to :entries entries}))) + {:module-name module-name + :modules modules' + :inputs sources}))) modules))) main From d04287dabb081e193a44e0ff1a16c81c92c1c6c3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 13 Aug 2017 17:34:01 -0400 Subject: [PATCH 2662/4033] do not rely on Closure to load deps, do it ourselves. do not load goog/base.js twice --- src/main/clojure/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 6d054320d..0d01a5b60 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1520,13 +1520,16 @@ {:output-to (:output-to module)})) (str (when (or (not module) (= :cljs-base (:module-name opts))) (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "var CLOSURE_NO_DEPS = true;\n" "if(typeof goog == \"undefined\") document.write('');\n" + "document.write('');\n" "document.write('');\n" "document.write('');\n" (apply str (preloads (:preloads opts) :browser)))) (apply str (map (fn [entry] - (str "document.write('');\n")) + (when-not (= "goog" entry) + (str "document.write('');\n"))) (if-let [entries (when module (:entries module))] entries (when-let [main (:main opts)] From 6b602b7fdd3967b2084e382c70678b921582fa18 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 13 Aug 2017 17:57:22 -0400 Subject: [PATCH 2663/4033] CLJS-2321: Do not automatically call `set-loaded!` on the user's behalf Revert CLJS-2157 --- src/main/clojure/cljs/compiler.cljc | 66 ++++++++++++----------------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 576fe2737..1bdee5038 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1328,14 +1328,6 @@ :relpaths {(util/path src) (util/ns->relpath (first (:provides opts)) (:ext opts))}}))))) -(defn module-for-entry [entry modules] - (->> modules - (filter - (fn [[module-name {:keys [entries]}]] - (some #{(-> entry munge str)} - (map #(-> % munge str) entries)))) - ffirst)) - #?(:clj (defn emit-source [src dest ext opts] (with-open [out ^java.io.Writer (io/make-writer dest {})] @@ -1387,38 +1379,32 @@ :name ns-name})) (emit ast) (recur (rest forms) ns-name deps)))) - (do - (when-let [module (and (contains? (set (vals deps)) 'cljs.loader) - (module-for-entry ns-name (:modules opts)))] - (emit - (ana/analyze env `(cljs.loader/set-loaded! ~module) - nil opts))) - (let [sm-data (when *source-map-data* @*source-map-data*) - ret (merge - {:ns (or ns-name 'cljs.user) - :macros-ns (:macros-ns opts) - :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj ana/constants-ns-sym))) - :file dest - :out-file dest - :source-file src} - (when sm-data - {:source-map (:source-map sm-data)}))] - (when (and sm-data (= :none (:optimizations opts))) - (emit-source-map src dest sm-data - (merge opts {:ext ext :provides [ns-name]}))) - (let [path (.getPath (.toURL ^File dest))] - (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) - (let [{:keys [output-dir cache-analysis]} opts] - (when (and (true? cache-analysis) output-dir) - (ana/write-analysis-cache ns-name - (ana/cache-file src (ana/parse-ns src) output-dir :write) - src)) - ret))))))))))) + (let [sm-data (when *source-map-data* @*source-map-data*) + ret (merge + {:ns (or ns-name 'cljs.user) + :macros-ns (:macros-ns opts) + :provides [ns-name] + :requires (if (= ns-name 'cljs.core) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj ana/constants-ns-sym))) + :file dest + :out-file dest + :source-file src} + (when sm-data + {:source-map (:source-map sm-data)}))] + (when (and sm-data (= :none (:optimizations opts))) + (emit-source-map src dest sm-data + (merge opts {:ext ext :provides [ns-name]}))) + (let [path (.getPath (.toURL ^File dest))] + (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (let [{:keys [output-dir cache-analysis]} opts] + (when (and (true? cache-analysis) output-dir) + (ana/write-analysis-cache ns-name + (ana/cache-file src (ana/parse-ns src) output-dir :write) + src)) + ret)))))))))) #?(:clj (defn compile-file* From a6ededcd7d3a7d32f68eee4f24b19c176c5668cd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 14 Aug 2017 08:32:14 -0400 Subject: [PATCH 2664/4033] Revert CLJS-2269 --- src/main/cljs/cljs/loader.clj | 29 ----------------------------- src/main/cljs/cljs/loader.cljs | 12 +++--------- 2 files changed, 3 insertions(+), 38 deletions(-) delete mode 100644 src/main/cljs/cljs/loader.clj diff --git a/src/main/cljs/cljs/loader.clj b/src/main/cljs/cljs/loader.clj deleted file mode 100644 index 338275ca0..000000000 --- a/src/main/cljs/cljs/loader.clj +++ /dev/null @@ -1,29 +0,0 @@ -; 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.loader - (:refer-clojure :exclude [load]) - (:require [cljs.module-graph :as mg] - [cljs.analyzer :as ana] - [cljs.analyzer.api :as ana-api])) - -(defn load-expr - ([env module-name] - (load-expr env module-name nil)) - ([env module-name cb] - (let [loader (mg/module-for (-> env :ns :name) - (:modules (ana-api/get-options)))] - `(cljs.loader/load* ~module-name ~loader ~cb)))) - -(defmacro load - "Load a module. module-name should be a keyword matching a :modules module - definition." - ([module-name] - (load-expr &env module-name)) - ([module-name cb] - (load-expr &env module-name cb))) \ No newline at end of file diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index b36ab3696..7153abfba 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -7,7 +7,6 @@ ; You must not remove this notice, or any other, from this software (ns cljs.loader - (:require-macros cljs.loader) (:require [goog.object :as gobj]) (:import [goog.module ModuleLoader] [goog.module ModuleManager])) @@ -53,19 +52,14 @@ (when (some? module) (.isLoaded module)))) -(defn load* +(defn load "Load a module. module-name should be a keyword matching a :modules module definition." ([module-name] - (throw (js/Error. "Invalid load call, must provide loader argument"))) - ([module-name loader] - (load* module-name loader nil)) - ([module-name loader cb] + (load module-name nil)) + ([module-name cb] (assert (contains? module-infos module-name) (str "Module " module-name " does not exist")) - (assert (loaded? loader) - (str "Module " loader " not fully loaded, but attempted to " - "load module " module-name)) (let [mname (-> module-name name munge)] (if-not (nil? cb) (.execOnLoad *module-manager* mname cb) From f5cd5f902e96aef3861bb72af483779305e59fd6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 14 Aug 2017 08:57:45 -0400 Subject: [PATCH 2665/4033] do no need to pass expanded :modules or :inputs to output-main-file for foreign libs loaded via a module entry point the goog.require mechanism is sufficient since the DOM is not yet fully loaded. --- src/main/clojure/cljs/closure.clj | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e2a0496b8..eb5d9e83f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1479,8 +1479,7 @@ (defn output-main-file "Output an entry point. In the non-modules case, opts is simply compiler - options. When emitting a module entry point opts must contain :module-name, - fully expanded :modules and :inputs (all compiler IJavaScript input sources)." + options. When emitting a module entry point, opts must contain :module-name." [opts] (assert (or (not (contains? opts :module-name)) (get (:modules opts) (:module-name opts))) @@ -1789,16 +1788,14 @@ (spit goog-deps (slurp (io/resource "goog/deps.js"))) (cond modules - (let [modules' (module-graph/expand-modules modules sources)] + (do (output-deps) (doall (map (fn [[module-name _]] (output-main-file (merge opts - {:module-name module-name - :modules modules' - :inputs sources}))) + {:module-name module-name}))) modules))) main From 0b8cff8027fa7fc5b2d325df0206ba90d780f7a6 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sun, 13 Aug 2017 15:48:45 -0700 Subject: [PATCH 2666/4033] CLJS-2309: :module foreign-libs order not preserved this patch adds a test that demonstrates the issue is no longer reproducible --- src/test/cljs_build/loader_test/foo.cljs | 4 ++-- src/test/cljs_build/loader_test/foreign.js | 3 --- src/test/cljs_build/loader_test/foreignA.js | 3 +++ src/test/cljs_build/loader_test/foreignB.js | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 15 ++++++++++++--- 5 files changed, 20 insertions(+), 8 deletions(-) delete mode 100644 src/test/cljs_build/loader_test/foreign.js create mode 100644 src/test/cljs_build/loader_test/foreignA.js create mode 100644 src/test/cljs_build/loader_test/foreignB.js diff --git a/src/test/cljs_build/loader_test/foo.cljs b/src/test/cljs_build/loader_test/foo.cljs index 4218cbdd6..232e82050 100644 --- a/src/test/cljs_build/loader_test/foo.cljs +++ b/src/test/cljs_build/loader_test/foo.cljs @@ -2,8 +2,8 @@ (:require [goog.dom :as gdom] [goog.events :as events] [cljs.loader :as loader] - ;[my.foreign] - ) + [foreign.a] + [foreign.b]) (:import [goog.events EventType])) (enable-console-print!) diff --git a/src/test/cljs_build/loader_test/foreign.js b/src/test/cljs_build/loader_test/foreign.js deleted file mode 100644 index dff6ae896..000000000 --- a/src/test/cljs_build/loader_test/foreign.js +++ /dev/null @@ -1,3 +0,0 @@ -var foreign = function() { - console.log("I'm foreign!") -}; \ No newline at end of file diff --git a/src/test/cljs_build/loader_test/foreignA.js b/src/test/cljs_build/loader_test/foreignA.js new file mode 100644 index 000000000..5b190303f --- /dev/null +++ b/src/test/cljs_build/loader_test/foreignA.js @@ -0,0 +1,3 @@ +global.foreignA = function() { + console.log("I'm foreign!") +}; diff --git a/src/test/cljs_build/loader_test/foreignB.js b/src/test/cljs_build/loader_test/foreignB.js new file mode 100644 index 000000000..2fc7e3d23 --- /dev/null +++ b/src/test/cljs_build/loader_test/foreignB.js @@ -0,0 +1,3 @@ +global.foreignB = function() { + console.log("I'm foreign too!"); +}; diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 2a6e0f4c9..618d7fc4c 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -186,8 +186,11 @@ {:output-dir output-dir :optimizations :none :verbose true - ;:foreign-libs [{:file "loader_test/foreign.js" - ; :provides ["my.foreign"]}] + :foreign-libs [{:file "src/test/cljs_build/loader_test/foreignA.js" + :provides ["foreign.a"]} + {:file "src/test/cljs_build/loader_test/foreignB.js" + :provides ["foreign.b"] + :requires ["foreign.a"]}] :modules {:foo {:output-to (str (io/file output-dir "foo.js")) @@ -213,7 +216,13 @@ (test/delete-out-files out) (let [{:keys [inputs opts]} (merge-with merge (loader-test-project out) {:opts {:optimizations :whitespace}})] - (build/build (build/inputs inputs) opts))))) + (build/build (build/inputs inputs) opts))) + (testing "CLJS-2309 foreign libs order preserved" + (test/delete-out-files out) + (let [{:keys [inputs opts]} (merge-with merge (loader-test-project out) + {:opts {:optimizations :advanced}})] + (build/build (build/inputs inputs) opts) + (is (not (nil? (re-find #"foreignA[\s\S]+foreignB" (slurp (io/file out "foo.js")))))))))) (deftest test-npm-deps (test/delete-node-modules) From 190f3fa6bcad8f78edba2d4f509f665902c88e0f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 14 Aug 2017 11:28:18 -0400 Subject: [PATCH 2667/4033] add `:debug-inputs` for debugging `:modules` issues --- src/main/clojure/cljs/closure.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index eb5d9e83f..7a1e52859 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1786,6 +1786,9 @@ disk-sources)] (util/mkdirs goog-deps) (spit goog-deps (slurp (io/resource "goog/deps.js"))) + (when (:debug-inputs opts) + (util/debug-prn "DEBUG: all compiler inputs") + (util/debug-prn (pr-str sources))) (cond modules (do From a62747da9000f7341517a80690652098527124f9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Aug 2017 10:20:54 -0400 Subject: [PATCH 2668/4033] add test case demonstrating that PersistentArrayMap generated for defrecord --- src/test/cljs/data_readers_test/records.cljc | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/test/cljs/data_readers_test/records.cljc diff --git a/src/test/cljs/data_readers_test/records.cljc b/src/test/cljs/data_readers_test/records.cljc new file mode 100644 index 000000000..c622c7bff --- /dev/null +++ b/src/test/cljs/data_readers_test/records.cljc @@ -0,0 +1,6 @@ +(ns data-readers-test.records + #?(:cljs (:require-macros [data-readers-test.records]))) + +(defrecord Foo [a b]) + +(assert (= (Foo. 1 2) #data_readers_test.records.Foo{:a 1 :b 2})) \ No newline at end of file From f5af28f814896d3a538cba717331b651a5ef9239 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Aug 2017 11:34:30 -0400 Subject: [PATCH 2669/4033] CLJS-2323: Wrong return value for data readers with records add analyzer-record case to analyzer. add emission for record values. still need to finish test case --- src/main/clojure/cljs/analyzer.cljc | 21 +++++++++++++++++++++ src/main/clojure/cljs/compiler.cljc | 5 +++++ src/test/clojure/cljs/build_api_tests.clj | 11 +++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 973aea843..db49a5d33 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3406,6 +3406,25 @@ :children items :tag (if (map? val) 'object 'array)})) +(defn analyze-record + [env x] + (let [items (disallowing-recur + (analyze (assoc env :context :expr) (into {} x))) + [ns name] (map symbol + #?(:clj + ((juxt (comp #(string/join "." %) butlast) last) + (string/split (.getName ^Class (type x)) #"\.")) + :cljs + (string/split (pr-str (type x)) #"/")))] + {:op :record-value + :ns ns + :name name + :env env + :form x + :items items + :children [items] + :tag name})) + (defn elide-reader-meta [m] (dissoc m :file :line :column :end-column :end-line :source)) @@ -3509,6 +3528,7 @@ (cond (symbol? form) (analyze-symbol env form) (and (seq? form) (seq form)) (analyze-seq env form name opts) + (record? form) (analyze-record env form) (map? form) (analyze-map env form) (vector? form) (analyze-vector env form) (set? form) (analyze-set env form) @@ -3530,6 +3550,7 @@ (cond (symbol? form) (analyze-symbol env form) (and (cljs-seq? form) (some? (seq form))) (analyze-seq env form name opts) + (record? form) (analyze-record env form) (cljs-map? form) (analyze-map env form) (cljs-vector? form) (analyze-vector env form) (cljs-set? form) (analyze-set env form) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1bdee5038..5419fa77f 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -460,6 +460,11 @@ (emits "})")) (emits "[" (comma-sep items) "]")))) +(defmethod emit* :record-value + [{:keys [items ns name items env]}] + (emit-wrap env + (emits ns ".map__GT_" name "(" items ")"))) + (defmethod emit* :constant [{:keys [form env]}] (when-not (= :statement (:context env)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 618d7fc4c..4c344c96b 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -398,6 +398,17 @@ (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)))) +(comment + (let [out "out" + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs")) + :opts {:main 'data-readers-test.records + :output-dir out + :optimizations :none + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env)] + (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv)) + ) + (deftest test-cljs-2249 (let [out (io/file (test/tmp-dir) "cljs-2249-out") root (io/file "src" "test" "cljs_build") From fca452123d9c85dddfb72842cd1096b2eaf9b77b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Aug 2017 13:16:51 -0400 Subject: [PATCH 2670/4033] fix AST :tag value for analyze-record --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index db49a5d33..11bad728b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3423,7 +3423,7 @@ :form x :items items :children [items] - :tag name})) + :tag (symbol (str ns) (str name))})) (defn elide-reader-meta [m] (dissoc m :file :line :column :end-column :end-line :source)) From 4bc7e87f74a760e570428565a6473796628adc04 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Aug 2017 13:39:46 -0400 Subject: [PATCH 2671/4033] add test for CLJS-2323 --- src/test/clojure/cljs/build_api_tests.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 4c344c96b..7a39c6d34 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -398,16 +398,18 @@ (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)))) -(comment - (let [out "out" +(deftest test-data-readers-records + (let [out (.getPath (io/file (test/tmp-dir) "data-readers-test-records-out")) {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs")) :opts {:main 'data-readers-test.records :output-dir out :optimizations :none :closure-warnings {:check-types :off}}} cenv (env/default-compiler-env)] - (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv)) - ) + (test/delete-out-files out) + (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) + (is (true? (boolean (re-find #"data_readers_test.records.map__GT_Foo\(" + (slurp (io/file out "data_readers_test" "records.js")))))))) (deftest test-cljs-2249 (let [out (io/file (test/tmp-dir) "cljs-2249-out") From 86b482a0390b2d90d8bee1c88ef248e339e35fcc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Aug 2017 16:33:19 -0400 Subject: [PATCH 2672/4033] CLJS-2324: module-graph doesn't munge :requires when indexing inputs munge :requires and :provides when indexing inputs, needs test --- src/main/clojure/cljs/module_graph.cljc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index f1796d112..ea5153aef 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -100,7 +100,15 @@ [inputs] (reduce (fn [ret {:keys [provides] :as input}] - (into ret (map #(vector (-> % munge str) input)) provides)) + (into ret + (map + (fn [provide] + (vector + (-> provide munge str) + (-> input + (update :provides #(into [] (map (comp str munge)) %)) + (update :requires #(into [] (map (comp str munge)) %)))))) + provides)) {} inputs)) (defn ^:dynamic deps-for From c66e1385f69d078360f84ef5c60b538d757bab9e Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Mon, 14 Aug 2017 18:34:18 -0700 Subject: [PATCH 2673/4033] CLJS-2322: Require only `@cljs-oss/module-deps` to be installed to figure out Node.js dep graph --- src/main/clojure/cljs/build/api.clj | 4 ++-- src/main/clojure/cljs/closure.clj | 11 +++++------ src/test/clojure/cljs/build_api_tests.clj | 4 +--- src/test/clojure/cljs/closure_tests.clj | 5 +---- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 5577b5d0d..892b25db4 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -275,8 +275,8 @@ (defn node-inputs "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry points. Assumes - that the `@cljs-oss/module-deps`, `enhanced-resolve` and `konan` NPM packages - are either locally or globally installed." + that the `@cljs-oss/module-deps` NPM package is either locally or globally + installed." [entries] (closure/node-inputs entries)) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 7a1e52859..30a41caa4 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2137,8 +2137,7 @@ (when-not (.exists pkg-json) (spit pkg-json "{}")) (let [proc (-> (ProcessBuilder. - (into (cond->> ["npm" "install" "@cljs-oss/module-deps" - "enhanced-resolve" "resolve" "konan"] + (into (cond->> ["npm" "install" "@cljs-oss/module-deps"] util/windows? (into ["cmd" "/c"])) (map (fn [[dep version]] (str (name dep) "@" version))) npm-deps)) @@ -2162,8 +2161,8 @@ (defn node-module-deps "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry point. Assumes - that the `@cljs-oss/module-deps`, `enhanced-resolve`, `enhanced-resolve` and - `konan` NPM packages are either locally or globally installed." + that the `@cljs-oss/module-deps` NPM package is either locally or globally + installed." ([entry] (node-module-deps entry (when env/*compiler* @@ -2202,8 +2201,8 @@ (defn node-inputs "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry points. Assumes - that the `@cljs-oss/module-deps`, `enhanced-resolve` and `konan` NPM packages - are either locally or globally installed." + that the `@cljs-oss/module-deps` NPM package is either locally or globally + installed." ([entries] (node-inputs entries (when env/*compiler* diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 7a39c6d34..d49b523b2 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -427,9 +427,7 @@ (test/delete-node-modules) (.delete (io/file "package-lock.json")) (spit (io/file "package.json") (json/json-str {:dependencies {:left-pad "1.1.3"} - :devDependencies {"@cljs-oss/module-deps" "*" - :konan "*" - :enhanced-resolve "*"}})) + :devDependencies {"@cljs-oss/module-deps" "*"}})) (apply sh/sh (cond->> ["npm" "install"] util/windows? (into ["cmd" "/c"]))) (let [ws (atom []) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 9258831d9..6dd737c85 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -244,10 +244,7 @@ (test/delete-out-files out))) (deftest test-cljs-2315 - (spit (io/file "package.json") (json/json-str {:devDependencies {"@cljs-oss/module-deps" "*" - :konan "*" - :resolve "*" - :enhanced-resolve "*"}})) + (spit (io/file "package.json") (json/json-str {:devDependencies {"@cljs-oss/module-deps" "*"}})) (apply sh/sh (cond->> ["npm" "install"] util/windows? (into ["cmd" "/c"]))) (let [file (io/file (test/tmp-dir) "cljs-2315-inputs.js") From a196ba528b225438296c207233df703211501b0f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Aug 2017 16:41:48 -0400 Subject: [PATCH 2674/4033] consistently use cljs.compiler/munge over clojure.core/munge --- src/main/clojure/cljs/module_graph.cljc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index ea5153aef..36690ba84 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -26,7 +26,7 @@ (filter (fn [source] (when (some #(.startsWith ^String % matcher) - (map (comp str munge) (:provides source))) + (map (comp str comp/munge) (:provides source))) source))) sources)] (when-not (empty? matches) @@ -36,7 +36,7 @@ (let [matcher (into #{} [(name entry) (name (comp/munge entry))])] - (when (some matcher (map (comp str munge) (:provides source))) + (when (some matcher (map (comp str comp/munge) (:provides source))) source))) sources)] #{input})))) @@ -51,7 +51,7 @@ (fn [ret module-name module] (assoc ret module-name (update module :entries - (fn [es] (into #{} (map (comp str munge)) es))))) + (fn [es] (into #{} (map (comp str comp/munge)) es))))) {} modules)) (defn add-cljs-base @@ -104,10 +104,10 @@ (map (fn [provide] (vector - (-> provide munge str) + (-> provide comp/munge str) (-> input - (update :provides #(into [] (map (comp str munge)) %)) - (update :requires #(into [] (map (comp str munge)) %)))))) + (update :provides #(into [] (map (comp str comp/munge)) %)) + (update :requires #(into [] (map (comp str comp/munge)) %)))))) provides)) {} inputs)) @@ -121,7 +121,7 @@ (defn deps-for-entry "Return all dependencies for an entry using a compiler inputs index." [entry indexed-inputs] - (map #(-> % munge str) (deps-for entry indexed-inputs :requires))) + (map #(-> % comp/munge str) (deps-for entry indexed-inputs :requires))) (defn deps-for-module "Return all dependencies of a module using compiler :modules." @@ -199,7 +199,7 @@ (into {} (map assign1) d->ms) (into {} (map assign1) e->ms)) orphans (zipmap - (map (comp str munge first :provides) + (map (comp str comp/munge first :provides) (-> (reduce-kv (fn [m k _] (dissoc m k)) index assigned) vals set)) (repeat :cljs-base))] @@ -213,7 +213,7 @@ (reduce (fn [[ret n] {:keys [provides]}] [(merge ret - (zipmap (map (comp str munge) provides) (repeat n))) + (zipmap (map (comp str comp/munge) provides) (repeat n))) (inc n)]) [{} 0] inputs)) modules' (-> modules normalize add-cljs-base add-cljs-base-dep) @@ -348,7 +348,7 @@ "Given an entry find the module it belongs to." [entry modules] (let [modules' (normalize modules) - entry' (str (munge entry))] + entry' (str (comp/munge entry))] (->> modules' (some (fn [[module-name {:keys [entries]} :as me]] From e31267417fc093de0e9808df1bb62a03ae0b5aea Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Aug 2017 16:47:52 -0400 Subject: [PATCH 2675/4033] mix symbols into :requires of inputs in module-graph tests --- src/test/clojure/cljs/module_graph_tests.clj | 43 +++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj index c91f5377f..811440994 100644 --- a/src/test/clojure/cljs/module_graph_tests.clj +++ b/src/test/clojure/cljs/module_graph_tests.clj @@ -35,23 +35,23 @@ {:provides '[events "event.types"] :requires ["cljs.core"] :out-file (str output-dir "/events.js")} - {:provides ["shared.a"] + {:provides '[shared.a] :requires ["cljs.core"] :out-file (str output-dir "/shared/a.js")} - {:provides ["shared.b"] - :requires ["cljs.core"] + {:provides '[shared.b] + :requires '[cljs.core] :out-file (str output-dir "/shared/b.js")} {:provides ["page1.a"] - :requires ["cljs.core" "cljs.reader" "events" "shared.a"] + :requires ["cljs.core" "cljs.reader" "events" 'shared.a] :out-file (str output-dir "/page1/a.js")} {:provides ["page1.b"] :requires '[cljs.core shared.b] :out-file (str output-dir "/page1/b.js")} {:provides ["page2.a"] - :requires ["cljs.core" "events" "shared.a"] + :requires ["cljs.core" "events" 'shared.a] :out-file (str output-dir "/page2/a.js")} {:provides ["page2.b"] - :requires ["cljs.core" "shared.b"] + :requires ['cljs.core 'shared.b] :out-file (str output-dir "/page2/b.js")}]) (deftest test-add-cljs-base @@ -150,3 +150,34 @@ (deftest test-module-for (is (= :page1 (module-graph/module-for 'page1.a (modules opts)))) (is (= :page1 (module-graph/module-for "page1.a" (modules opts))))) + +(comment + (require '[clojure.java.io :as io] + '[clojure.edn :as edn] + '[clojure.pprint :refer [pprint]] + '[clojure.set :as set]) + + (def modules + {:entry-point {:output-to "resources/public/js/demos/demos.js" + :entries '#{cards.card-ui}} + :main {:output-to "resources/public/js/demos/main-ui.js" + :entries '#{recipes.dynamic-ui-main}}}) + + (def inputs + (edn/read-string + {:readers {'object (fn [x] nil) + 'cljs.closure.JavaScriptFile (fn [x] x)}} + (slurp (io/file "inputs.edn")))) + + (module-graph/expand-modules modules inputs) + + (pprint + (binding [module-graph/deps-for (memoize module-graph/deps-for)] + (module-graph/deps-for-entry "cards.card_ui" + (module-graph/index-inputs inputs)))) + + (get (module-graph/index-inputs inputs) "cards.card_ui") + + (get (module-graph/index-inputs inputs) "cards.dynamic_routing_cards") + (get (module-graph/index-inputs inputs) "fulcro.client.routing") + ) \ No newline at end of file From 3567cc0155f436617978eb75be08f17ec2811472 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Aug 2017 16:49:21 -0400 Subject: [PATCH 2676/4033] test case for CLJS-2324 --- src/test/clojure/cljs/module_graph_tests.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj index 811440994..b6a15b153 100644 --- a/src/test/clojure/cljs/module_graph_tests.clj +++ b/src/test/clojure/cljs/module_graph_tests.clj @@ -76,7 +76,8 @@ (deftest test-entry-deps (let [inputs (module-graph/index-inputs (inputs opts))] (is (= (module-graph/deps-for-entry "page2.a" inputs) - ["cljs.core" "events" "shared.a"])))) + ["cljs.core" "events" "shared.a"])) + (is (some #{"shared.a"} (module-graph/deps-for-entry "page1.a" inputs))))) (deftest test-canonical-name (let [ins (module-graph/index-inputs (inputs opts))] From 6130143bbc07e49c3d6e9313377226b9551be434 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 15 Aug 2017 18:39:05 -0400 Subject: [PATCH 2677/4033] dep source compiler inputs after add-preloads --- src/main/clojure/cljs/closure.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 30a41caa4..f48fe71c6 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2540,6 +2540,7 @@ (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") all-opts)])) deps/dependency-order (add-preloads all-opts) + deps/dependency-order add-goog-base (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])) (->> (map #(source-on-disk all-opts %)) doall) From 56a880cb09d57e287a3eba4839c4f5688958850f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 16 Aug 2017 12:31:06 -0400 Subject: [PATCH 2678/4033] =?UTF-8?q?fold=20konan=20logic=20into=20module?= =?UTF-8?q?=5Fdeps.js=20for=20now,=20can=20now=20handle=20`export=20...=20?= =?UTF-8?q?from=20'AAA'`.=20As=20noted=20by=20Antoni=C3=B3,=20`export=20de?= =?UTF-8?q?fault=20from=20'AAA'`=20probably=20doesn't=20work.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/cljs/cljs/module_deps.js | 172 +++++++++++++++++++----------- 1 file changed, 108 insertions(+), 64 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index ae31444ba..f7aa5fa06 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -1,16 +1,60 @@ -var fs = require('fs'); -var path = require('path'); -var mdeps = require('@cljs-oss/module-deps'); -var nodeResolve = require('resolve'); -var konan = require('konan'); -var enhancedResolve = require('enhanced-resolve'); - -var target = 'CLJS_TARGET'; -var filename = fs.realpathSync(path.resolve(__dirname, 'JS_FILE')); -var mainFields = - target === 'nodejs' ? ['module', 'main'] : ['module', 'browser', 'main']; - -var resolver = enhancedResolve.create({ +let fs = require('fs'); +let path = require('path'); +let mdeps = require('@cljs-oss/module-deps'); +let nodeResolve = require('resolve'); +let babylon = require('babylon'); +let traverse = require('babel-traverse').default; +let enhancedResolve = require('enhanced-resolve'); + +let target = 'CLJS_TARGET'; +let filename = fs.realpathSync(path.resolve(__dirname, 'JS_FILE')); +let mainFields = + target === 'nodejs' ? ['module', 'main'] : ['module', 'browser', 'main']; + +// https://github.com/egoist/konan +let getDeps = function (src, {dynamicImport = true, parse = {sourceType: 'module', plugins: '*'}} = {}) { + const modules = {strings: [], expressions: []}; + + let ast; + + if (typeof src === 'string') { + const moduleRe = /\b(require|import)\b/; + + if (!moduleRe.test(src)) { + return modules; + } + + ast = babylon.parse(src, parse); + } else { + ast = src; + } + + traverse(ast, { + enter(path) { + if (path.node.type === 'CallExpression') { + const callee = path.get('callee'); + const isDynamicImport = dynamicImport && callee.isImport(); + if (callee.isIdentifier({name: 'require'}) || isDynamicImport) { + const arg = path.node.arguments[0]; + if (arg.type === 'StringLiteral') { + modules.strings.push(arg.value); + } else { + modules.expressions.push(src.slice(arg.start, arg.end)); + } + } + } else if (path.node.type === 'ImportDeclaration') { + modules.strings.push(path.node.source.value); + } else if (path.node.type === 'ExportNamedDeclaration' && path.node.source) { + // this branch handles `export ... from` - David + modules.strings.push(path.node.source.value); + } + } + }); + + return modules; +}; + +let resolver = enhancedResolve.create({ fileSystem: new enhancedResolve.CachedInputFileSystem( new enhancedResolve.NodeJsInputFileSystem(), 4000 @@ -20,7 +64,7 @@ var resolver = enhancedResolve.create({ moduleExtensions: ['.js', '.json'], }); -var md = mdeps({ +let md = mdeps({ resolve: function(id, parentOpts, cb) { // set the basedir properly so we don't try to resolve requires in the Closure // Compiler processed `node_modules` folder. @@ -35,15 +79,15 @@ var md = mdeps({ return !(target === 'nodejs' && nodeResolve.isCore(id)); }, detect: function(src) { - var deps = konan(src); + let deps = getDeps(src); return deps.strings; - }, + } }); function getPackageJsonMainEntry(pkgJson) { - for (var i = 0; i < mainFields.length; i++) { - var entry = mainFields[i]; + for (let i = 0; i < mainFields.length; i++) { + let entry = mainFields[i]; if (pkgJson[entry] != null) { return pkgJson[entry]; @@ -52,13 +96,13 @@ function getPackageJsonMainEntry(pkgJson) { return null; } -var pkgJsons = []; -var deps_files = {}; +let pkgJsons = []; +let deps_files = {}; md.on('package', function(pkg) { // we don't want to include the package.json for users' projects if (/node_modules/.test(pkg.__dirname)) { - var pkgJson = { + let pkgJson = { file: path.join(pkg.__dirname, 'package.json'), }; @@ -66,7 +110,7 @@ md.on('package', function(pkg) { pkgJson.provides = [pkg.name]; } - var pkgJsonMainEntry = getPackageJsonMainEntry(pkg); + let pkgJsonMainEntry = getPackageJsonMainEntry(pkg); if (pkgJsonMainEntry != null) { pkgJson.mainEntry = path.join(pkg.__dirname, pkgJsonMainEntry); } @@ -75,62 +119,62 @@ md.on('package', function(pkg) { } }); -md.on('file', function(file) { - deps_files[file] = { file: file }; +md.on('file', function (file) { + deps_files[file] = {file: file}; }); -md.on('end', function() { - for (var i = 0; i < pkgJsons.length; i++) { - var pkgJson = pkgJsons[i]; +md.on('end', function () { + for (let i = 0; i < pkgJsons.length; i++) { + let pkgJson = pkgJsons[i]; - if (deps_files[pkgJson.mainEntry] != null && pkgJson.provides != null) { - deps_files[pkgJson.mainEntry].provides = pkgJson.provides; - } + if (deps_files[pkgJson.mainEntry] != null && pkgJson.provides != null) { + deps_files[pkgJson.mainEntry].provides = pkgJson.provides; + } - deps_files[pkgJson.file] = { file: pkgJson.file }; - } + deps_files[pkgJson.file] = {file: pkgJson.file}; + } - var values = []; - for (var key in deps_files) { - var dep = deps_files[key]; - - // add provides to files that are not `package.json`s - if ( - !/node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$/.test( - dep.file - ) - ) { - if (dep.file.indexOf('node_modules') !== -1) { - var providedModule = dep.file - .substring(dep.file.lastIndexOf('node_modules')) - .replace(/\\/g, '/') - .replace('node_modules/', ''); - - dep.provides = dep.provides || []; - dep.provides.push( - providedModule, - providedModule.replace(/\.js(on)?$/, '') - ); - - var indexReplaced = providedModule.replace(/\/index\.js(on)?$/, ''); + let values = []; + for (let key in deps_files) { + let dep = deps_files[key]; + // add provides to files that are not `package.json`s if ( - /\/index\.js(on)?$/.test(providedModule) && - dep.provides.indexOf(indexReplaced) === -1 + !/node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$/.test( + dep.file + ) ) { - dep.provides.push(indexReplaced); + if (dep.file.indexOf('node_modules') !== -1) { + let providedModule = dep.file + .substring(dep.file.lastIndexOf('node_modules')) + .replace(/\\/g, '/') + .replace('node_modules/', ''); + + dep.provides = dep.provides || []; + dep.provides.push( + providedModule, + providedModule.replace(/\.js(on)?$/, '') + ); + + let indexReplaced = providedModule.replace(/\/index\.js(on)?$/, ''); + + if ( + /\/index\.js(on)?$/.test(providedModule) && + dep.provides.indexOf(indexReplaced) === -1 + ) { + dep.provides.push(indexReplaced); + } + } } - } - } - values.push(dep); - } + values.push(dep); + } - process.stdout.write(JSON.stringify(values)); + process.stdout.write(JSON.stringify(values)); }); md.end({ - file: filename, + file: filename }); md.resume(); From 58a783c79e802ae023f3c2ef50540bf254011e13 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 16 Aug 2017 17:32:44 -0400 Subject: [PATCH 2679/4033] remove comment block from module tests --- src/test/clojure/cljs/module_graph_tests.clj | 31 -------------------- 1 file changed, 31 deletions(-) diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj index b6a15b153..723ee45e1 100644 --- a/src/test/clojure/cljs/module_graph_tests.clj +++ b/src/test/clojure/cljs/module_graph_tests.clj @@ -151,34 +151,3 @@ (deftest test-module-for (is (= :page1 (module-graph/module-for 'page1.a (modules opts)))) (is (= :page1 (module-graph/module-for "page1.a" (modules opts))))) - -(comment - (require '[clojure.java.io :as io] - '[clojure.edn :as edn] - '[clojure.pprint :refer [pprint]] - '[clojure.set :as set]) - - (def modules - {:entry-point {:output-to "resources/public/js/demos/demos.js" - :entries '#{cards.card-ui}} - :main {:output-to "resources/public/js/demos/main-ui.js" - :entries '#{recipes.dynamic-ui-main}}}) - - (def inputs - (edn/read-string - {:readers {'object (fn [x] nil) - 'cljs.closure.JavaScriptFile (fn [x] x)}} - (slurp (io/file "inputs.edn")))) - - (module-graph/expand-modules modules inputs) - - (pprint - (binding [module-graph/deps-for (memoize module-graph/deps-for)] - (module-graph/deps-for-entry "cards.card_ui" - (module-graph/index-inputs inputs)))) - - (get (module-graph/index-inputs inputs) "cards.card_ui") - - (get (module-graph/index-inputs inputs) "cards.dynamic_routing_cards") - (get (module-graph/index-inputs inputs) "fulcro.client.routing") - ) \ No newline at end of file From 70fa3e9c64670e370fe46b54e00f241f9771042f Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 16 Aug 2017 17:38:40 -0400 Subject: [PATCH 2680/4033] 1.9.908 --- README.md | 6 +++--- changes.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0977c4fa2..6d7977ff3 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.854 +Latest stable release: 1.9.908 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.854"] +[org.clojure/clojurescript "1.9.908"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.854 org.clojure clojurescript - 1.9.854 + 1.9.908 ``` diff --git a/changes.md b/changes.md index 7ea76296d..f993ff660 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,45 @@ +## 1.9.908 + +### Enhancements +* CLJS-2323: data readers support for records + +### Changes +* CLJS-2322: Require only `@cljs-oss/module-deps` to be installed to figure out Node.js dep graph +* CLJS-2321: Do not automatically call `set-loaded!` on the user's behalf +* CLJS-2316: Upgrade Closure Compiler to August release +* CLJS-2317: Upgrade Google Closure Library +* CLJS-2234: Make build scripts optionally less verbose +* CLJS-2314: Eliminate str call on literal strings in str macro +* CLJS-2291: Set up Windows CI +* CLJS-2286: Simplify JS module processing + +### Fixes +* CLJS-2324: module-graph doesn't munge :requires when indexing inputs +* CLJS-2309: :module foreign-libs order not preserved +* CLJS-2318: module-deps.js doesn't respect the package.json `module` field +* CLJS-2312: Miss-compile: Uncaught SyntaxError: Unexpected token default +* CLJS-2315: module_deps.js can't resolve JSON modules +* CLJS-2313: :language-out is a build affecting option +* CLJS-2306: Provide better warning message when namespace can't be found +* CLJS-2303: Disable duplicate alias checking for self-host +* CLJS-2307: Closure warns on unreachable checked array code +* CLJS-2305 Tests: Unable to resolve symbol: opts in this context +* CLJS-2299: Failure with alias and bad require of clojure.spec +* CLJS-2302: Disable process-shim by default in Node.js targets +* CLJS-2266: Self-host: Cannot require clojure.x where clojure.x has no macros namespace +* CLJS-2304: Fix compiler infrastructure tests on Windows +* CLJS-2261: Issue using interop record constructors in macros namespaces +* CLJS-2296: Foreign libs that expose modules are not being processed under target nod +* CLJS-2293: Self-host: Can't load cljs.js owing to set alias +* CLJS-2295: `index-node-modules-dir` can't determine :main for package.json files that have `.` in the string +* CLJS-1620: In JavaScript ES2015 modules default export name is munged to default$ +* CLJS-2287: Self-host: `require` prints result of loading node deps / global exports +* CLJS-2290: Node packages using require('assert') fail compilation +* CLJS-2281: module_deps.js cannot compute inputs for ES6 sources +* CLJS-2284: Fix build API tests not to pollute `out` in the current directory +* CLJS-2282: Some valid keywords are strings in JS object literals +* CLJS-2283: Regression with js-obj and gobject alias + ## 1.9.854 ### Enhancements From 1d3dfbdc7d155d2ed001075964ddad8db3933c88 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 17 Aug 2017 14:01:21 -0400 Subject: [PATCH 2681/4033] consistently format module_deps.js --- src/main/cljs/cljs/module_deps.js | 92 +++++++++++++++---------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index f7aa5fa06..d08e6c48c 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -55,68 +55,68 @@ let getDeps = function (src, {dynamicImport = true, parse = {sourceType: 'module }; let resolver = enhancedResolve.create({ - fileSystem: new enhancedResolve.CachedInputFileSystem( - new enhancedResolve.NodeJsInputFileSystem(), - 4000 - ), - extensions: ['.js', '.json'], - mainFields: mainFields, - moduleExtensions: ['.js', '.json'], + fileSystem: new enhancedResolve.CachedInputFileSystem( + new enhancedResolve.NodeJsInputFileSystem(), + 4000 + ), + extensions: ['.js', '.json'], + mainFields: mainFields, + moduleExtensions: ['.js', '.json'], }); let md = mdeps({ - resolve: function(id, parentOpts, cb) { - // set the basedir properly so we don't try to resolve requires in the Closure - // Compiler processed `node_modules` folder. - parentOpts.basedir = - parentOpts.filename === filename - ? path.resolve(__dirname) - : path.dirname(parentOpts.filename); - - resolver(parentOpts.basedir, id, cb); - }, - filter: function(id) { - return !(target === 'nodejs' && nodeResolve.isCore(id)); - }, - detect: function(src) { - let deps = getDeps(src); - - return deps.strings; - } + resolve: function (id, parentOpts, cb) { + // set the basedir properly so we don't try to resolve requires in the Closure + // Compiler processed `node_modules` folder. + parentOpts.basedir = + parentOpts.filename === filename + ? path.resolve(__dirname) + : path.dirname(parentOpts.filename); + + resolver(parentOpts.basedir, id, cb); + }, + filter: function (id) { + return !(target === 'nodejs' && nodeResolve.isCore(id)); + }, + detect: function (src) { + let deps = getDeps(src); + + return deps.strings; + } }); function getPackageJsonMainEntry(pkgJson) { - for (let i = 0; i < mainFields.length; i++) { - let entry = mainFields[i]; + for (let i = 0; i < mainFields.length; i++) { + let entry = mainFields[i]; - if (pkgJson[entry] != null) { - return pkgJson[entry]; + if (pkgJson[entry] != null) { + return pkgJson[entry]; + } } - } - return null; + return null; } let pkgJsons = []; let deps_files = {}; -md.on('package', function(pkg) { - // we don't want to include the package.json for users' projects - if (/node_modules/.test(pkg.__dirname)) { - let pkgJson = { - file: path.join(pkg.__dirname, 'package.json'), - }; +md.on('package', function (pkg) { + // we don't want to include the package.json for users' projects + if (/node_modules/.test(pkg.__dirname)) { + let pkgJson = { + file: path.join(pkg.__dirname, 'package.json'), + }; - if (pkg.name != null) { - pkgJson.provides = [pkg.name]; - } + if (pkg.name != null) { + pkgJson.provides = [pkg.name]; + } - let pkgJsonMainEntry = getPackageJsonMainEntry(pkg); - if (pkgJsonMainEntry != null) { - pkgJson.mainEntry = path.join(pkg.__dirname, pkgJsonMainEntry); - } + let pkgJsonMainEntry = getPackageJsonMainEntry(pkg); + if (pkgJsonMainEntry != null) { + pkgJson.mainEntry = path.join(pkg.__dirname, pkgJsonMainEntry); + } - pkgJsons.push(pkgJson); - } + pkgJsons.push(pkgJson); + } }); md.on('file', function (file) { From 5a5c1e7a4462165d92e4658e6d067a1c4a1f1c95 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 17 Aug 2017 14:54:03 -0400 Subject: [PATCH 2682/4033] fix browser npm module resolution bug --- src/main/cljs/cljs/module_deps.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index d08e6c48c..a0fbfab90 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -8,8 +8,8 @@ let enhancedResolve = require('enhanced-resolve'); let target = 'CLJS_TARGET'; let filename = fs.realpathSync(path.resolve(__dirname, 'JS_FILE')); -let mainFields = - target === 'nodejs' ? ['module', 'main'] : ['module', 'browser', 'main']; +let mainFields = ['module', 'main']; +let aliasFields = target === 'nodejs' ? [] : ['browser']; // https://github.com/egoist/konan let getDeps = function (src, {dynamicImport = true, parse = {sourceType: 'module', plugins: '*'}} = {}) { @@ -61,7 +61,8 @@ let resolver = enhancedResolve.create({ ), extensions: ['.js', '.json'], mainFields: mainFields, - moduleExtensions: ['.js', '.json'], + aliasFields: aliasFields, + moduleExtensions: ['.js', '.json'] }); let md = mdeps({ From 5180e7e2891a92d56b7f1a3f1268e615a96dfd33 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Thu, 17 Aug 2017 21:53:05 -0700 Subject: [PATCH 2683/4033] CLJS-2327: module_deps.js doesn't know about browser field advanced usage --- src/main/cljs/cljs/module_deps.js | 94 ++++++++++++++++++------- src/main/clojure/cljs/closure.clj | 2 +- src/test/clojure/cljs/closure_tests.clj | 62 ++++++++++++++++ 3 files changed, 133 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index a0fbfab90..61d1edfe0 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -8,7 +8,9 @@ let enhancedResolve = require('enhanced-resolve'); let target = 'CLJS_TARGET'; let filename = fs.realpathSync(path.resolve(__dirname, 'JS_FILE')); -let mainFields = ['module', 'main']; +let mainFields = target === 'nodejs' + ? ['module', 'main'] + : ['browser', 'module', 'main']; let aliasFields = target === 'nodejs' ? [] : ['browser']; // https://github.com/egoist/konan @@ -61,7 +63,7 @@ let resolver = enhancedResolve.create({ ), extensions: ['.js', '.json'], mainFields: mainFields, - aliasFields: aliasFields, + aliasFields: target === 'nodejs' ? [] : ['browser'], moduleExtensions: ['.js', '.json'] }); @@ -89,14 +91,51 @@ let md = mdeps({ function getPackageJsonMainEntry(pkgJson) { for (let i = 0; i < mainFields.length; i++) { let entry = mainFields[i]; - - if (pkgJson[entry] != null) { - return pkgJson[entry]; + const entryVal = pkgJson[entry]; + + if (entryVal != null) { + if (typeof entryVal === 'string') { + return entryVal; + } else if (typeof entryVal === 'object') { + for (let j = i; j < mainFields.length; j++) { + let otherEntry = mainFields[j]; + const otherEntryVal = pkgJson[entry]; + + if (entryVal[otherEntryVal] != null) { + return entryVal[otherEntryVal] + } + } + } } } return null; } +function depProvides(provides, file) { + const result = provides != null ? provides.slice(0) : []; + + let providedModule = file + .substring(file.lastIndexOf('node_modules')) + .replace(/\\/g, '/') + .replace('node_modules/', ''); + + result.push( + providedModule, + providedModule.replace(/\.js(on)?$/, '') + ); + + let indexReplaced = providedModule.replace(/\/index\.js(on)?$/, ''); + + if ( + /\/index\.js(on)?$/.test(providedModule) && + result.indexOf(indexReplaced) === -1 + ) { + result.push(indexReplaced); + } + + return result; +} + let pkgJsons = []; let deps_files = {}; @@ -104,6 +143,7 @@ md.on('package', function (pkg) { // we don't want to include the package.json for users' projects if (/node_modules/.test(pkg.__dirname)) { let pkgJson = { + basedir: pkg.__dirname, file: path.join(pkg.__dirname, 'package.json'), }; @@ -116,6 +156,14 @@ md.on('package', function (pkg) { pkgJson.mainEntry = path.join(pkg.__dirname, pkgJsonMainEntry); } + // we'll need these later + for (let i = 0; i < aliasFields.length; i++) { + const field = aliasFields[i]; + if (pkg[field] != null) { + pkgJson[field] = pkg[field]; + } + } + pkgJsons.push(pkgJson); } }); @@ -132,6 +180,22 @@ md.on('end', function () { deps_files[pkgJson.mainEntry].provides = pkgJson.provides; } + for (let j = 0; j < aliasFields.length; j++) { + const field = aliasFields[j]; + const fieldValue = pkgJson[field]; + + if (fieldValue != null && typeof fieldValue === 'object') { + for (let key in fieldValue) { + const replacement = path.resolve(pkgJson.basedir, fieldValue[key]); + + if (deps_files[replacement] != null) { + deps_files[replacement].provides = depProvides(deps_files[replacement].provides, path.resolve(pkgJson.basedir, key)); + } + } + } + } + + deps_files[pkgJson.file] = {file: pkgJson.file}; } @@ -146,25 +210,7 @@ md.on('end', function () { ) ) { if (dep.file.indexOf('node_modules') !== -1) { - let providedModule = dep.file - .substring(dep.file.lastIndexOf('node_modules')) - .replace(/\\/g, '/') - .replace('node_modules/', ''); - - dep.provides = dep.provides || []; - dep.provides.push( - providedModule, - providedModule.replace(/\.js(on)?$/, '') - ); - - let indexReplaced = providedModule.replace(/\/index\.js(on)?$/, ''); - - if ( - /\/index\.js(on)?$/.test(providedModule) && - dep.provides.indexOf(indexReplaced) === -1 - ) { - dep.provides.push(indexReplaced); - } + dep.provides = depProvides(dep.provides, dep.file); } } diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f48fe71c6..43a713e96 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2281,7 +2281,7 @@ (index-node-modules-dir (when env/*compiler* (:options @env/*compiler*)))) - ([{:keys [verbose target]}] + ([opts] (let [module-fseq (util/module-file-seq)] (node-file-seq->libs-spec module-fseq)))) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 6dd737c85..e54177094 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -277,3 +277,65 @@ (closure/index-node-modules ["tslib"] opts)))) (test/delete-node-modules) (test/delete-out-files out))) + +(deftest test-cljs-2327 + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {:react "16.0.0-beta.5" + :react-dom "16.0.0-beta.5"}} + out (util/output-directory opts)] + (test/delete-node-modules) + (test/delete-out-files out) + (closure/maybe-install-node-deps! opts) + (let [modules (closure/index-node-modules ["react" "react-dom" "react-dom/server"] opts)] + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/react/index.js")) + :provides ["react" + "react/index.js" + "react/index"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/react-dom/index.js")) + :provides ["react-dom" + "react-dom/index.js" + "react-dom/index"]})) + modules))) + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/react-dom/server.browser.js")) + :provides ["react-dom/server.js" + "react-dom/server" + "react-dom/server.browser.js" + "react-dom/server.browser"]})) + modules)))) + (test/delete-node-modules) + (test/delete-out-files out) + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {:warning "3.0.0"}} + _ (closure/maybe-install-node-deps! opts) + modules (closure/index-node-modules ["warning"] opts)] + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/warning/browser.js")) + :provides ["warning" + "warning/browser.js" + "warning/browser"]})) + modules)))) + (test/delete-node-modules) + (test/delete-out-files out) + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {:react-dom "16.0.0-beta.5" + :react "16.0.0-beta.5"} + :target :nodejs} + _ (closure/maybe-install-node-deps! opts) + modules (closure/index-node-modules ["react-dom/server"] opts)] + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/react-dom/server.js")) + :provides ["react-dom/server.js" + "react-dom/server"]})) + modules)))) + (.delete (io/file "package.json")) + (test/delete-node-modules) + (test/delete-out-files out))) From 799325ac84a7cc3acdd68ce98df61328779c63d7 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Thu, 17 Aug 2017 14:21:15 -0700 Subject: [PATCH 2684/4033] CLJS-2328: Args are not provided to *main-cli-fn* with optimizations advanced --- src/main/cljs/cljs/nodejscli.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/nodejscli.cljs b/src/main/cljs/cljs/nodejscli.cljs index de7d103b5..e0a8257c0 100644 --- a/src/main/cljs/cljs/nodejscli.cljs +++ b/src/main/cljs/cljs/nodejscli.cljs @@ -10,7 +10,8 @@ ; job is to make sure cljs.nodejs is loaded and that the *main-cli-fn* ; is called with the script's command-line arguments. (ns cljs.nodejscli - (:require [cljs.nodejs :as nodejs])) + (:require [cljs.nodejs :as nodejs] + [goog.object :as gobj])) ;; need to set js/goog.global if COMPILED (when ^boolean js/COMPILED @@ -18,4 +19,4 @@ ;; Call the user's main function (when (fn? cljs.core/*main-cli-fn*) - (apply cljs.core/*main-cli-fn* (drop 2 (.-argv js/process)))) + (apply cljs.core/*main-cli-fn* (drop 2 (gobj/get js/process "argv")))) From befb3671911c9ff5de3ec10d41a9e0beeb006a03 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 18 Aug 2017 11:34:39 -0400 Subject: [PATCH 2685/4033] instead of sorting after add-preloads, sort after add-js-sources in add-preloads --- src/main/clojure/cljs/closure.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 43a713e96..467ca50a8 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -999,7 +999,8 @@ (-> (add-dependency-sources preloads opts) deps/dependency-order (compile-sources opts) - (add-js-sources opts)) + (add-js-sources opts) + deps/dependency-order) (next post)))))) (comment @@ -2540,7 +2541,6 @@ (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") all-opts)])) deps/dependency-order (add-preloads all-opts) - deps/dependency-order add-goog-base (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])) (->> (map #(source-on-disk all-opts %)) doall) From 694a623018ff2918eba88a9570dbfa685019c6f3 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Thu, 17 Aug 2017 22:31:25 -0700 Subject: [PATCH 2686/4033] CLJS-2326: Indexing node_modules can't find `main` when it doesn't have an extension --- src/main/cljs/cljs/module_deps.js | 11 +++++++-- src/main/clojure/cljs/closure.clj | 8 +++++-- src/test/clojure/cljs/closure_tests.clj | 30 +++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 61d1edfe0..809d7e953 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -175,9 +175,16 @@ md.on('file', function (file) { md.on('end', function () { for (let i = 0; i < pkgJsons.length; i++) { let pkgJson = pkgJsons[i]; + const candidates = /\.js(on)?$/.test(pkgJson.mainEntry) + ? [pkgJson.mainEntry] + : [pkgJson.mainEntry, pkgJson.mainEntry + '.js', pkgJson.mainEntry + '.json']; - if (deps_files[pkgJson.mainEntry] != null && pkgJson.provides != null) { - deps_files[pkgJson.mainEntry].provides = pkgJson.provides; + for (let j = 0; j < candidates.length; j++) { + const candidate = candidates[j]; + + if (deps_files[candidate] != null && pkgJson.provides != null) { + deps_files[candidate].provides = pkgJson.provides; + } } for (let j = 0; j < aliasFields.length; j++) { diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 467ca50a8..ac77267d8 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2259,8 +2259,12 @@ (string/replace #"\\" "/") (string/replace #"package\.json$" "") (str main))] - (when (= main-path (string/replace path #"\\" "/")) - name)))) + (some (fn [candidate] + (when (= candidate (string/replace path #"\\" "/")) + name)) + (cond-> [main-path] + (nil? (re-find #"\.js(on)?$" main-path)) + (into [(str main-path ".js") (str main-path ".json")])))))) pkg-jsons)] {:provides (let [module-rel-name (-> (subs path (.lastIndexOf path "node_modules")) (string/replace #"\\" "/") diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index e54177094..142113d90 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -275,6 +275,7 @@ "tslib/tslib.es6.js" "tslib/tslib.es6"]})) (closure/index-node-modules ["tslib"] opts)))) + (.delete (io/file "package.json")) (test/delete-node-modules) (test/delete-out-files out))) @@ -339,3 +340,32 @@ (.delete (io/file "package.json")) (test/delete-node-modules) (test/delete-out-files out))) + +(deftest test-cljs-2326 + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {:bootstrap "4.0.0-beta"}} + out (util/output-directory opts)] + (test/delete-node-modules) + (test/delete-out-files out) + (closure/maybe-install-node-deps! opts) + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/bootstrap/dist/js/bootstrap.js")) + :provides ["bootstrap" + "bootstrap/dist/js/bootstrap.js" + "bootstrap/dist/js/bootstrap"]})) + (closure/index-node-modules ["bootstrap"] opts)))) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (test/delete-out-files out)) + (closure/maybe-install-node-deps! {:npm-deps {:bootstrap "4.0.0-beta"}}) + (let [modules (closure/index-node-modules-dir)] + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/bootstrap/dist/js/bootstrap.js")) + :provides ["bootstrap/dist/js/bootstrap.js" + "bootstrap/dist/js/bootstrap" + "bootstrap"]})) + modules)))) + (.delete (io/file "package.json")) + (test/delete-node-modules)) From adc7deaafc54f6f7d27cc631db4e381d222a818c Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Fri, 18 Aug 2017 10:45:15 -0700 Subject: [PATCH 2687/4033] CLJS-2330: Don't set `"browser"` field for Closure if target is :nodejs --- src/main/clojure/cljs/build/api.clj | 8 ++++++-- src/main/clojure/cljs/closure.clj | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 892b25db4..15fe56eed 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -277,8 +277,12 @@ the module-deps package on the supplied JavaScript entry points. Assumes that the `@cljs-oss/module-deps` NPM package is either locally or globally installed." - [entries] - (closure/node-inputs entries)) + ([entries] + (node-inputs entries + (when-not (nil? env/*compiler*) + (:options @env/*compiler*)))) + ([entries opts] + (closure/node-inputs entries opts))) (comment (node-inputs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ac77267d8..b0375e2cf 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1665,6 +1665,8 @@ (= module-type :amd)) js-modules))) (.setLanguageIn (lang-key->lang-mode :ecmascript6)) (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) + _ (when (= (:target opts) :nodejs) + (.setPackageJsonEntryNames options ^List '("module", "main"))) closure-compiler (doto (make-closure-compiler) (.init externs source-files options)) _ (.parse closure-compiler) From 98656d305e6447c62e36849ee615532d53211fdd Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Fri, 18 Aug 2017 21:35:11 -0700 Subject: [PATCH 2688/4033] CLJS-2332: module_deps.js doesn't process `export from` correctly --- src/main/cljs/cljs/module_deps.js | 15 +++++++++------ src/test/clojure/cljs/closure_tests.clj | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 809d7e953..8d6c96a0c 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -20,7 +20,7 @@ let getDeps = function (src, {dynamicImport = true, parse = {sourceType: 'module let ast; if (typeof src === 'string') { - const moduleRe = /\b(require|import)\b/; + const moduleRe = /\b(require|import|export)\b/; if (!moduleRe.test(src)) { return modules; @@ -44,11 +44,14 @@ let getDeps = function (src, {dynamicImport = true, parse = {sourceType: 'module modules.expressions.push(src.slice(arg.start, arg.end)); } } - } else if (path.node.type === 'ImportDeclaration') { - modules.strings.push(path.node.source.value); - } else if (path.node.type === 'ExportNamedDeclaration' && path.node.source) { - // this branch handles `export ... from` - David - modules.strings.push(path.node.source.value); + } else if (path.node.type === 'ImportDeclaration' || + path.node.type === 'ExportNamedDeclaration' || + path.node.type === 'ExportAllDeclaration') { + const source = path.node.source; + + if (source != null) { + modules.strings.push(path.node.source.value); + } } } }); diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 142113d90..eb25f8fc9 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -369,3 +369,21 @@ modules)))) (.delete (io/file "package.json")) (test/delete-node-modules)) + +(deftest test-cljs-2332 + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {"@material/drawer" "0.5.4"}} + out (util/output-directory opts)] + (test/delete-node-modules) + (test/delete-out-files out) + (closure/maybe-install-node-deps! opts) + (let [modules (closure/index-node-modules ["@material/drawer"] opts)] + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/@material/drawer/slidable/constants.js")) + :provides ["@material/drawer/slidable/constants.js" + "@material/drawer/slidable/constants"]})) + modules)))) + (.delete (io/file "package.json")) + (test/delete-node-modules) + (test/delete-out-files out))) From 88e1f39d5653f154da6b6362bced3c3cb5c15e3b Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Mon, 21 Aug 2017 11:16:24 -0700 Subject: [PATCH 2689/4033] CLJS-2333: module-deps.js doesn't correctly compute `main` if aliased in browser field --- src/main/cljs/cljs/module_deps.js | 7 ++++++- src/test/clojure/cljs/closure_tests.clj | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 8d6c96a0c..19b6cf560 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -199,7 +199,12 @@ md.on('end', function () { const replacement = path.resolve(pkgJson.basedir, fieldValue[key]); if (deps_files[replacement] != null) { - deps_files[replacement].provides = depProvides(deps_files[replacement].provides, path.resolve(pkgJson.basedir, key)); + const file = path.resolve(pkgJson.basedir, key); + deps_files[replacement].provides = depProvides(deps_files[replacement].provides, file); + + if (file === pkgJson.mainEntry) { + Array.prototype.push.apply(deps_files[replacement].provides, pkgJson.provides); + } } } } diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index eb25f8fc9..71b120b94 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -387,3 +387,26 @@ (.delete (io/file "package.json")) (test/delete-node-modules) (test/delete-out-files out))) + +(deftest test-cljs-2333 + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {"asap" "2.0.6"}} + out (util/output-directory opts)] + (test/delete-node-modules) + (test/delete-out-files out) + (closure/maybe-install-node-deps! opts) + (let [modules (closure/index-node-modules ["asap"] opts)] + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/asap/browser-asap.js")) + :provides ["asap/asap", + "asap/asap", + "asap/asap.js", + "asap/asap", + "asap", + "asap/browser-asap.js", + "asap/browser-asap"]})) + modules)))) + (.delete (io/file "package.json")) + (test/delete-node-modules) + (test/delete-out-files out))) From 79041d10ce11e9e2f15c261a9a4174c6a7066834 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Tue, 22 Aug 2017 22:56:11 -0700 Subject: [PATCH 2690/4033] CLJS-2334: Also gather dependencies from foreign-libs that are modules --- src/main/clojure/cljs/closure.clj | 74 ++++++++++++------- .../foreign_libs_cljs_2334/core.cljs | 6 ++ .../cljs_build/foreign_libs_cljs_2334/lib.js | 5 ++ src/test/clojure/cljs/build_api_tests.clj | 26 ++++++- .../clojure/cljs/module_processing_tests.clj | 52 +++++++++---- 5 files changed, 121 insertions(+), 42 deletions(-) create mode 100644 src/test/cljs_build/foreign_libs_cljs_2334/core.cljs create mode 100644 src/test/cljs_build/foreign_libs_cljs_2334/lib.js diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b0375e2cf..733d3ad01 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2198,7 +2198,8 @@ (next (json/read-str (str iw)))) (do (when-not (.isAlive proc) - (println (str ew))) + (binding [*out* *err*] + (println (str ew)))) []))))) (defn node-inputs @@ -2330,6 +2331,9 @@ (ana/warning :unsupported-preprocess-value @env/*compiler* js-module) js-module))) +(defn- to-absolute-path [^String file-str] + (.getAbsolutePath (io/file file-str))) + (defn process-js-modules "Given the current compiler options, converts JavaScript modules to Google Closure modules and writes them to disk. Adds mapping from original module @@ -2347,15 +2351,20 @@ (map :module-type js-modules)))] (ana/warning :unsupported-js-module-type @env/*compiler* unsupported)) ;; Load all modules - add :source so preprocessing and conversion can access it - js-modules (map (fn [lib] - (let [js (deps/load-foreign-library lib)] - (assoc js :source (deps/-source js opts)))) - js-modules) - js-modules (map (fn [js] - (if (:preprocess js) - (preprocess-js js opts) - js)) - js-modules) + js-modules (into [] + (comp + (map (fn [lib] + (let [js (deps/load-foreign-library lib)] + (assoc js :source (deps/-source js opts))))) + (map (fn [js] + (if (:preprocess js) + (preprocess-js js opts) + js))) + (map (fn [js] + (cond-> (update-in js [:file] to-absolute-path) + (some? (:file-min js)) + (update-in [:file-min] to-absolute-path))))) + js-modules) js-modules (convert-js-modules js-modules opts)] ;; Write modules to disk, update compiler state and build new options (reduce (fn [new-opts {:keys [file module-type] :as ijs}] @@ -2368,9 +2377,17 @@ (-> new-opts (update-in [:libs] (comp vec conj) (:out-file ijs)) ;; js-module might be defined in either, so update both - (update-in [:foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs)))) - (update-in [:ups-foreign-libs] (comp vec (fn [libs] (remove #(= (:file %) file) libs))))))) - opts js-modules))) + (update-in [:foreign-libs] + (fn [libs] + (into [] + (remove #(= (to-absolute-path (:file %)) file)) + libs))) + (update-in [:ups-foreign-libs] + (fn [libs] + (into [] + (remove #(= (to-absolute-path (:file %)) (to-absolute-path file))) + libs)))))) + opts js-modules))) opts))) (defn- load-data-reader-file [mappings ^java.net.URL url] @@ -2437,25 +2454,30 @@ requires (set (mapcat deps/-requires js-sources)) ;; Select Node files that are required by Cljs code, ;; and create list of all their dependencies - node-required (set/intersection (set (keys top-level)) requires)] - (let [opts (-> opts + node-required (set/intersection (set (keys top-level)) requires) + expanded-libs (expand-libs (:foreign-libs opts)) + opts (-> opts (update :foreign-libs (fn [libs] (into (if (= target :nodejs) [] (index-node-modules node-required)) - (expand-libs libs)))) + (into expanded-libs + (node-inputs (filter (fn [{:keys [module-type]}] + (and (some? module-type) + (not= module-type :amd))) + expanded-libs)))))) process-js-modules)] - (swap! compiler-env (fn [cenv] - (-> cenv - ;; we need to also track the whole top level - this is to support - ;; cljs.analyze/analyze-deps, particularly in REPL contexts - David - (merge {:js-dependency-index (deps/js-dependency-index opts)}) - (update-in [:node-module-index] (fnil into #{}) - (if (= target :nodejs) - (map str node-required) - (map str (keys top-level))))))) - opts))) + (swap! compiler-env (fn [cenv] + (-> cenv + ;; we need to also track the whole top level - this is to support + ;; cljs.analyze/analyze-deps, particularly in REPL contexts - David + (merge {:js-dependency-index (deps/js-dependency-index opts)}) + (update-in [:node-module-index] (fnil into #{}) + (if (= target :nodejs) + (map str node-required) + (map str (keys top-level))))))) + opts)) (defn build "Given a source which can be compiled, produce runnable JavaScript." diff --git a/src/test/cljs_build/foreign_libs_cljs_2334/core.cljs b/src/test/cljs_build/foreign_libs_cljs_2334/core.cljs new file mode 100644 index 000000000..1f6cf737e --- /dev/null +++ b/src/test/cljs_build/foreign_libs_cljs_2334/core.cljs @@ -0,0 +1,6 @@ +(ns foreign-libs-cljs-2334.core + (:require [mylib])) + +(enable-console-print!) + +(println "mylib:" mylib) diff --git a/src/test/cljs_build/foreign_libs_cljs_2334/lib.js b/src/test/cljs_build/foreign_libs_cljs_2334/lib.js new file mode 100644 index 000000000..c78bb0458 --- /dev/null +++ b/src/test/cljs_build/foreign_libs_cljs_2334/lib.js @@ -0,0 +1,5 @@ +import leftPad from 'left-pad'; + +export var lp = function() { + return leftPad(42, 5, 0); +} diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index d49b523b2..d27062eb1 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -497,5 +497,29 @@ (test/delete-out-files out) (build/build (build/inputs (io/file inputs "foreign_libs_dir_test/core.cljs")) opts) (is (.exists (io/file out "src/test/cljs_build/foreign-libs-dir/vendor/lib.js"))) - (is (true? (boolean (re-find #"goog\.provide\(\"module\$src\$test\$cljs_build\$foreign_libs_dir\$vendor\$lib\"\)" + (is (true? (boolean (re-find #"goog\.provide\(\"module\$[A-Za-z0-9$_]+?src\$test\$cljs_build\$foreign_libs_dir\$vendor\$lib\"\)" (slurp (io/file out "src/test/cljs_build/foreign-libs-dir/vendor/lib.js")))))))) + +(deftest cljs-1883-test-foreign-libs-use-relative-path + (test/delete-node-modules) + (let [out "cljs-2334-out" + root (io/file "src" "test" "cljs_build") + opts {:foreign-libs + [{:file (str (io/file root "foreign_libs_cljs_2334" "lib.js")) + :module-type :es6 + :provides ["mylib"]}] + :npm-deps {:left-pad "1.1.3"} + :install-deps true + :output-dir (str out)}] + (test/delete-out-files out) + (build/build (build/inputs (io/file root "foreign_libs_cljs_2334")) opts) + (let [foreign-lib-file (io/file out (-> opts :foreign-libs first :file))] + (is (.exists foreign-lib-file)) + ;; assert Closure finds and processes the left-pad dep in node_modules + ;; if it can't be found the require will be issued to module$left_pad + ;; so we assert it's of the form module$path$to$node_modules$left_pad$index + (is (some? (re-find + #"(?s).*?goog\.require\(\"[A-Za-z0-9$_]+?node_modules\$left_pad\$index\"\);.*" + (slurp foreign-lib-file))))) + (test/delete-out-files out) + (test/delete-node-modules))) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index fc7895d33..ec21d667a 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -1,13 +1,15 @@ (ns cljs.module-processing-tests (:require [clojure.java.io :as io] [cljs.closure :as closure] - [clojure.test :refer :all] + [clojure.string :as string] + [clojure.test :refer [deftest is]] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.js-deps :as deps] [cljs.util :as util] - [cljs.test-util :as test])) + [cljs.test-util :as test]) + (:import [java.io File])) ;; Hard coded JSX transform for the test case (defn preprocess-jsx [ijs _] @@ -23,6 +25,24 @@ "React.createElement(\"circle\", {cx:\"100px\", cy:\"100px\", r:\"100px\", fill:this.props.color})" ")")))) +(defn absolute-module-path + ([relpath] + (absolute-module-path relpath false)) + ([relpath code?] + (let [filename (as-> (subs relpath (inc (.lastIndexOf relpath "/"))) $ + (string/replace $ "_" "-") + (subs $ 0 (.lastIndexOf $ "."))) + dirname (as-> (io/file relpath) $ + (.getAbsolutePath $) + (subs $ 0 (.lastIndexOf $ (str File/separator))) + (string/replace $ "/" "$") + ;; Windows + (string/replace $ "\\" "$") + (if code? + (string/replace $ ":" "_") + (string/replace $ ":" "-")))] + (str "module" (when-not (.startsWith dirname "$") "$") dirname "$" filename)))) + (defmethod closure/js-transforms :jsx [ijs opts] (preprocess-jsx ijs opts)) @@ -47,10 +67,9 @@ :preprocess :jsx}] :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs")) - - (is (= {"React" {:name "module$src$test$cljs$reactJS" + (is (= {"React" {:name (absolute-module-path "src/test/cljs/reactJS.js") :module-type :commonjs} - "Circle" {:name "module$src$test$cljs$Circle" + "Circle" {:name (absolute-module-path "src/test/cljs/Circle.js") :module-type :commonjs}} (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) @@ -74,13 +93,14 @@ :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs") - (is (= {"es6-hello" {:name "module$src$test$cljs$es6-hello" + (is (= {"es6-hello" {:name (absolute-module-path "src/test/cljs/es6_hello.js") :module-type :es6}} (:js-module-index @cenv)) "Processed modules are added to :js-module-index") - (is (= "goog.provide(\"module$src$test$cljs$es6_hello\");var sayHello$$module$src$test$cljs$es6_hello=function(){console.log(\"Hello, world!\")};module$src$test$cljs$es6_hello.sayHello=sayHello$$module$src$test$cljs$es6_hello" - (slurp "out/src/test/cljs/es6_hello.js")))))) + (is (re-find + #"goog.provide\(\"module\$[a-zA-Z0-9$_]+?src\$test\$cljs\$es6_hello\"\);" + (slurp "out/src/test/cljs/es6_hello.js")))))) (deftest test-module-name-substitution (test/delete-out-files) @@ -93,19 +113,21 @@ (with-out-str (comp/emit (ana/analyze (ana/empty-env) form)))) crlf (if util/windows? "\r\n" "\n") - output (str "module$src$test$cljs$calculator.add((3),(4));" crlf)] + output (str (absolute-module-path "src/test/cljs/calculator.js" true) ".add((3),(4));" crlf)] (swap! cenv #(assoc % :js-dependency-index (deps/js-dependency-index opts))) (binding [ana/*cljs-ns* 'cljs.user] (is (= (compile '(ns my-calculator.core (:require [calculator :as calc :refer [subtract add] :rename {subtract sub}]))) (str "goog.provide('my_calculator.core');" crlf "goog.require('cljs.core');" crlf - "goog.require('module$src$test$cljs$calculator');" crlf))) + "goog.require('" (absolute-module-path "src/test/cljs/calculator.js" true) "');" + crlf))) (is (= (compile '(calc/add 3 4)) output)) (is (= (compile '(calculator/add 3 4)) output)) (is (= (compile '(add 3 4)) output)) (is (= (compile '(sub 5 4)) - (str "module$src$test$cljs$calculator.subtract((5),(4));" crlf)))))))) + (str (absolute-module-path "src/test/cljs/calculator.js" true) + ".subtract((5),(4));" crlf)))))))) (deftest test-cljs-1822 (test/delete-out-files) @@ -132,9 +154,9 @@ :preprocess :jsx}] :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs")) - (is (= {"React" {:name "module$src$test$cljs$react-min" + (is (= {"React" {:name (absolute-module-path "src/test/cljs/react-min.js") :module-type :commonjs} - "Circle" {:name "module$src$test$cljs$Circle-min" + "Circle" {:name (absolute-module-path "src/test/cljs/Circle-min.js") :module-type :commonjs}} (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) @@ -161,9 +183,9 @@ :closure-warnings {:non-standard-jsdoc :off}}))) "processed modules are added to :libs")) - (is (= {"React" {:name "module$src$test$cljs$reactJS" + (is (= {"React" {:name (absolute-module-path "src/test/cljs/reactJS.js") :module-type :commonjs} - "Circle" {:name "module$src$test$cljs$Circle" + "Circle" {:name (absolute-module-path "src/test/cljs/Circle.js") :module-type :commonjs}} (:js-module-index @cenv)) "Processed modules are added to :js-module-index"))) From bee66e064aca8fd9edb021cabf686c42bc8b6c31 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 26 Aug 2017 17:31:15 -0400 Subject: [PATCH 2691/4033] CLJS-2335: Avoid alength on strings --- src/main/cljs/cljs/core.cljs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f8f85a1c0..f7a8ed35b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -923,17 +923,17 @@ (defn ^number m3-hash-unencoded-chars [in] (let [h1 (loop [i 1 h1 m3-seed] - (if (< i (alength in)) + (if (< i (.-length in)) (recur (+ i 2) (m3-mix-H1 h1 (m3-mix-K1 (bit-or (.charCodeAt in (dec i)) (bit-shift-left (.charCodeAt in i) 16))))) h1)) - h1 (if (== (bit-and (alength in) 1) 1) - (bit-xor h1 (m3-mix-K1 (.charCodeAt in (dec (alength in))))) + h1 (if (== (bit-and (.-length in) 1) 1) + (bit-xor h1 (m3-mix-K1 (.charCodeAt in (dec (.-length in))))) h1)] - (m3-fmix h1 (imul 2 (alength in))))) + (m3-fmix h1 (imul 2 (.-length in))))) ;;;;;;;;;;;;;;;;;;; symbols ;;;;;;;;;;;;;;; @@ -946,7 +946,7 @@ ;;http://hg.openjdk.java.net/jdk7u/jdk7u6/jdk/file/8c2c5d63a17e/src/share/classes/java/lang/String.java (defn hash-string* [s] (if-not (nil? s) - (let [len (alength s)] + (let [len (.-length s)] (if (pos? len) (loop [i 0 hash 0] (if (< i len) @@ -1798,7 +1798,7 @@ reduces them without incurring seq initialization" (alength coll) (string? coll) - (alength coll) + (.-length coll) (implements? ISeqable coll) (accumulating-seq-count coll) @@ -3953,7 +3953,7 @@ reduces them without incurring seq initialization" (deftype StringIter [s ^:mutable i] Object - (hasNext [_] (< i (alength s))) + (hasNext [_] (< i (.-length s))) (next [_] (let [ret (.charAt s i)] (set! i (inc i)) From 998933f5090254611b46a2b86626fb17cabc994a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 26 Aug 2017 18:23:36 -0400 Subject: [PATCH 2692/4033] CLJS-2336: Call alength once in areduce and amap --- benchmark/cljs/benchmark_runner.cljs | 7 +++++++ src/main/clojure/cljs/core.cljc | 8 +++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index f3f68d573..2daaa6b80 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -24,6 +24,13 @@ (simple-benchmark [coll arr] (array-reduce coll + 0) 1) (simple-benchmark [coll arr] (array-reduce coll sum 0) 1) +(println ";; areduce") +(def x (atom 0)) +(simple-benchmark [arr (to-array (range 1000000))] (reset! x (areduce arr i ret 0 (+ ret (aget arr i)))) 1) + +(println ";; amap") +(simple-benchmark [arr (to-array (range 1000000))] (amap arr i ret (* 10 (aget arr i))) 1) + (println ";;; instance?") ;; WARNING: will get compiled away under advanced (simple-benchmark [coll []] (instance? PersistentVector coll) 1000000) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 685813353..662467bbd 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2576,9 +2576,10 @@ array ret." [a idx ret expr] `(let [a# ~a + l# (alength a#) ~ret (cljs.core/aclone a#)] (loop [~idx 0] - (if (< ~idx (alength a#)) + (if (< ~idx l#) (do (aset ~ret ~idx ~expr) (recur (inc ~idx))) @@ -2589,9 +2590,10 @@ and return value named ret, initialized to init, setting ret to the evaluation of expr at each step, returning ret." [a idx ret init expr] - `(let [a# ~a] + `(let [a# ~a + l# (alength a#)] (loop [~idx 0 ~ret ~init] - (if (< ~idx (alength a#)) + (if (< ~idx l#) (recur (inc ~idx) ~expr) ~ret)))) From 35a360943ffade5fc319203512010458c054e241 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Thu, 7 Sep 2017 15:38:14 -0700 Subject: [PATCH 2693/4033] CLJS-2349: Port reset-vals! and swap-vals! over from Clojure --- src/main/cljs/cljs/core.cljs | 54 ++++++++++++++++++++++++------- src/test/cljs/cljs/core_test.cljs | 25 ++++++++++++++ 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f7a8ed35b..519e4ab06 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4423,27 +4423,57 @@ reduces them without incurring seq initialization" new-value)) (-reset! a new-value))) +(defn reset-vals! + "Sets the value of atom to newval. Returns [old new], the value of the + atom before and after the reset." + {:added "1.9"} + [a new-value] + (let [validate (.-validator a)] + (when-not (nil? validate) + (when-not (validate new-value) + (throw (js/Error. "Validator rejected reference state")))) + (let [old-value (.-state a)] + (set! (.-state a) new-value) + (when-not (nil? (.-watches a)) + (-notify-watches a old-value new-value)) + [old-value new-value]))) + (defn swap! "Atomically swaps the value of atom to be: (apply f current-value-of-atom args). Note that f may be called multiple times, and thus should be free of side effects. Returns the value that was swapped in." ([a f] - (if (instance? Atom a) - (reset! a (f (.-state a))) - (-swap! a f))) + (if (instance? Atom a) + (reset! a (f (.-state a))) + (-swap! a f))) + ([a f x] + (if (instance? Atom a) + (reset! a (f (.-state a) x)) + (-swap! a f x))) + ([a f x y] + (if (instance? Atom a) + (reset! a (f (.-state a) x y)) + (-swap! a f x y))) + ([a f x y & more] + (if (instance? Atom a) + (reset! a (apply f (.-state a) x y more)) + (-swap! a f x y more)))) + +(defn swap-vals! + "Atomically swaps the value of atom to be: + (apply f current-value-of-atom args). Note that f may be called + multiple times, and thus should be free of side effects. + Returns [old new], the value of the atom before and after the swap." + {:added "1.9"} + ([a f] + (reset-vals! a (f (.-state a)))) ([a f x] - (if (instance? Atom a) - (reset! a (f (.-state a) x)) - (-swap! a f x))) + (reset-vals! a (f (.-state a) x))) ([a f x y] - (if (instance? Atom a) - (reset! a (f (.-state a) x y)) - (-swap! a f x y))) + (reset-vals! a (f (.-state a) x y))) ([a f x y & more] - (if (instance? Atom a) - (reset! a (apply f (.-state a) x y more)) - (-swap! a f x y more)))) + (reset-vals! a (apply f (.-state a) x y more)))) (defn compare-and-set! "Atomically sets the value of atom to newval if and only if the diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 253a7d5aa..f2d7b065d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1448,6 +1448,31 @@ (is (= "#js {:_abc 1}" (pr-str #js {"_abc" 1}))) (is (= "#js {:*compiler* 1}" (pr-str #js {"*compiler*" 1})))) +(deftest swap-vals-returns-old-value + (let [a (atom 0)] + (is (= [0 1] (swap-vals! a inc))) + (is (= [1 2] (swap-vals! a inc))) + (is (= 2 @a)))) + +(deftest deref-swap-arities + (binding [*warn-on-reflection* true] + (let [a (atom 0)] + (is (= [0 1] (swap-vals! a + 1))) + (is (= [1 3] (swap-vals! a + 1 1))) + (is (= [3 6] (swap-vals! a + 1 1 1))) + (is (= [6 10] (swap-vals! a + 1 1 1 1))) + (is (= 10 @a))))) + +(deftest deref-reset-returns-old-value + (let [a (atom 0)] + (is (= [0 :b] (reset-vals! a :b))) + (is (= [:b 45M] (reset-vals! a 45M))) + (is (= 45M @a)))) + +(deftest reset-on-deref-reset-equality + (let [a (atom :usual-value)] + (is (= :usual-value (reset! a (first (reset-vals! a :almost-never-seen-value))))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From f88fa62ae7b0cf403a5f356ac20be0466aa11a73 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Thu, 7 Sep 2017 22:37:15 -0700 Subject: [PATCH 2694/4033] CLJS-2350: Fix circular dependencies test --- src/test/clojure/cljs/build_api_tests.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index d27062eb1..6d17feb2e 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -171,14 +171,15 @@ (test/delete-out-files out) (try (build/build (build/inputs - (io/file (str root "a.cljs")) - (io/file (str root "b.cljs"))) + (io/file root "circular_deps" "a.cljs") + (io/file root "circular_deps" "b.cljs")) {:main 'circular-deps.a :optimizations :none :output-to out}) (is false) (catch Throwable e - (is true))))) + (is (re-find #"Circular dependency detected, circular-deps.a -> circular-deps.b -> circular-deps.a" + (.getMessage (.getCause e)))))))) (defn loader-test-project [output-dir] {:inputs (str (io/file "src" "test" "cljs_build" "loader_test")) From b74538b4e9947973175cf197704c18b10a5ce6bf Mon Sep 17 00:00:00 2001 From: Chas Emerick Date: Thu, 31 Aug 2017 14:21:25 -0400 Subject: [PATCH 2695/4033] CLJS-2345: escape paths emitted as args to cljs.core.load_file --- src/main/clojure/cljs/compiler.cljc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 5419fa77f..f8982c1f1 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1114,10 +1114,14 @@ (if (= :nodejs target) ;; under node.js we load foreign libs globally (let [ijs (get js-dependency-index (name lib))] - (emitln "cljs.core.load_file(\"" - (str (io/file (util/output-directory options) (or (deps/-relative-path ijs) - (util/relative-name (:url ijs))))) - "\");")) + (emitln "cljs.core.load_file(" + (-> (io/file (util/output-directory options) + (or (deps/-relative-path ijs) + (util/relative-name (:url ijs)))) + str + escape-string + wrap-in-double-quotes) + ");")) (emitln "goog.require('" (munge lib) "');")))] :cljs [(and (ana/foreign-dep? lib) From fe28810661256907f1402ac1c7c8556db1cf616a Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sat, 9 Sep 2017 23:03:30 -0700 Subject: [PATCH 2696/4033] CLJS-2353: use portable `node-module-dep?` function in analyze-deps --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 11bad728b..11e4c2e1d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2113,7 +2113,7 @@ (doseq [dep deps] (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) - (contains? (:node-module-index compiler) (name dep)) + (node-module-dep? dep) (js-module-exists? (name dep)) #?(:clj (deps/find-classpath-lib dep))) #?(:clj (if-some [src (locate-src dep)] From 4b5fd54dc3081830adaddef206e6b80e0ed83df9 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sun, 10 Sep 2017 14:17:19 -0700 Subject: [PATCH 2697/4033] CLJS-2354: Self-host: `compile-str` doesn't handle `clojure` -> `cljs` aliasing --- src/main/cljs/cljs/js.cljs | 41 ++++++++++++++++++++++--------- src/test/self/self_host/test.cljs | 18 +++++++++++++- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index e8e18037b..7532dfa69 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -423,7 +423,20 @@ (fn [resource] (assert (or (map? resource) (nil? resource)) "*load-fn* may only return a map or nil") - (if resource + (if-not resource + (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] + (get {dep nil} cljs-ns cljs-ns))] + (do + (patch-alias-map (:*compiler* bound-vars) lib dep cljs-dep) + (analyze-deps bound-vars ana-env lib (cons cljs-dep (next deps)) opts + (fn [res] + (if (:error res) + (cb res) + (cb (update res :aliased-loads assoc dep cljs-dep)))))) + (cb (wrap-error + (ana/error ana-env + (ana/error-message :undeclared-ns + {:ns-sym dep :js-provide (name dep)}))))) (let [{:keys [name lang source file]} resource] (condp = lang :clj (do @@ -437,11 +450,7 @@ :js (analyze-deps bound-vars ana-env lib (next deps) opts cb) (wrap-error (ana/error ana-env - (str "Invalid :lang specified " lang ", only :clj or :js allowed"))))) - (cb (wrap-error - (ana/error ana-env - (ana/error-message :undeclared-ns - {:ns-sym dep :js-provide (name dep)}))))))) + (str "Invalid :lang specified " lang ", only :clj or :js allowed")))))))) (catch :default cause (cb (wrap-error (ana/error ana-env @@ -494,11 +503,19 @@ (if (some? to) (assoc acc renamed (symbol (str to) (name qualified-sym))) (merge acc entry)))) - {} m)))] + {} m))) + rewrite-deps (fn [deps] + (into [] + (map (fn [dep] + (if-let [new-dep (get smap dep)] + new-dep + dep))) + deps))] (-> ast (update uk #(walk/postwalk-replace smap %)) (update rk #(merge smap (walk/postwalk-replace smap %))) - (update renk rewrite-renames))))) + (update renk rewrite-renames) + (update :deps rewrite-deps))))) (defn- check-macro-autoload-inferring-missing [{:keys [requires name] :as ast} cenv] @@ -583,7 +600,7 @@ (and (not load) (:*analyze-deps* bound-vars) (seq (:deps ast))) (analyze-deps bound-vars ana-env (:name ast) (:deps ast) (dissoc opts :macros-ns) - #(check-uses-and-load-macros % ast)) + #(check-uses-and-load-macros % (rewrite-ns-ast ast (:aliased-loads %)))) :else (check-uses-and-load-macros {:value nil} ast))) @@ -882,17 +899,19 @@ (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? (:deps ast))] [node-libs (assoc ast :deps libs-to-load)]) [nil ast])] - (.append sb (with-out-str (comp/emit ast))) (if (#{:ns :ns*} (:op ast)) (ns-side-effects bound-vars aenv ast opts (fn [res] (if (:error res) (cb res) (let [ns-name (:name ast)] + (.append sb (with-out-str (comp/emit (:value res)))) (when-not (nil? node-deps) (node-side-effects bound-vars sb node-deps ns-name (:def-emits-var opts))) (compile-loop (:name ast)))))) - (recur ns))))) + (do + (.append sb (with-out-str (comp/emit ast))) + (recur ns)))))) (do (when (:source-map opts) (append-source-map env/*compiler* diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index ad2f295bf..5b3efffb7 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1094,7 +1094,7 @@ (deftest test-cljs-2287 (async done (let [st (cljs/empty-state) - l (latch 1 done)] + l (latch 2 done)] (cljs/eval-str (atom @st) "(ns foo.core (:require [path]))" @@ -1212,6 +1212,22 @@ (is (nil? error)) (inc! l)))))))) +(deftest test-cljs-2354 + (async done + (let [st (cljs/empty-state) + load (fn [{:keys [name macros]} cb] + (cb (when (and (= name 'cljs.x) + (not macros)) + {:lang :clj + :source "(ns cljs.x)"}))) + l (latch 1 done)] + (cljs.js/compile-str st "(require 'clojure.x)" nil + {:load load} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (is (re-find #"goog\.require\('cljs.x'\)" value)) + (inc! l)))))) + (defn -main [& args] (run-tests)) From 238028ccc51afe45de98fb853be4396a71afb602 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sun, 10 Sep 2017 21:24:22 -0700 Subject: [PATCH 2698/4033] CLJS-2356: Self-host: circular dependency detection is not correct --- src/main/cljs/cljs/js.cljs | 166 ++++++++++++++++-------------- src/test/self/self_host/test.cljs | 31 ++++++ 2 files changed, 122 insertions(+), 75 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 7532dfa69..04966d1d0 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -288,7 +288,7 @@ "*load-fn* may only return a map or nil") (if resource (let [{:keys [lang source cache source-map file]} resource] - (condp = lang + (condp keyword-identical? lang :clj (do (pre-file-side-effects (:*compiler* bound-vars) aname file opts) (eval-str* bound-vars source name (assoc opts :cljs-file file) @@ -374,35 +374,41 @@ (debug-prn "Loading dependencies for" lib)) (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) update-in [:dep-path] conj lib)] - (assert (every? #(not (contains? (:*cljs-dep-set* bound-vars) %)) deps) - (str "Circular dependency detected " - (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) - (if (seq deps) - (let [dep (first deps) - opts' (-> opts - (dissoc :context) - (dissoc :ns))] - (require bound-vars dep reload opts' - (fn [res] - (when (:verbose opts) - (debug-prn "Loading result:" res)) - (if-not (:error res) - (load-deps bound-vars ana-env lib (next deps) nil opts cb) - (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] - (get {dep nil} cljs-ns cljs-ns))] - (require bound-vars cljs-dep opts' - (fn [res] - (if (:error res) - (cb res) - (do - (patch-alias-map (:*compiler* bound-vars) lib dep cljs-dep) - (load-deps bound-vars ana-env lib (next deps) nil opts - (fn [res] - (if (:error res) - (cb res) - (cb (update res :aliased-loads assoc dep cljs-dep))))))))) - (cb res)))))) - (cb {:value nil}))))) + (let [bound-vars (assoc bound-vars :*cljs-dep-set* ana/*cljs-dep-set*)] + (if-not (every? #(not (contains? ana/*cljs-dep-set* %)) deps) + (cb (wrap-error + (ana/error ana-env + (str "Circular dependency detected " + (apply str + (interpose " -> " + (conj (-> ana/*cljs-dep-set* meta :dep-path) + (some ana/*cljs-dep-set* deps)))))))) + (if (seq deps) + (let [dep (first deps) + opts' (-> opts + (dissoc :context) + (dissoc :ns))] + (require bound-vars dep reload opts' + (fn [res] + (when (:verbose opts) + (debug-prn "Loading result:" res)) + (if-not (:error res) + (load-deps bound-vars ana-env lib (next deps) nil opts cb) + (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] + (get {dep nil} cljs-ns cljs-ns))] + (require bound-vars cljs-dep opts' + (fn [res] + (if (:error res) + (cb res) + (do + (patch-alias-map (:*compiler* bound-vars) lib dep cljs-dep) + (load-deps bound-vars ana-env lib (next deps) nil opts + (fn [res] + (if (:error res) + (cb res) + (cb (update res :aliased-loads assoc dep cljs-dep))))))))) + (cb res)))))) + (cb {:value nil}))))))) (declare analyze-str*) @@ -410,52 +416,58 @@ ([bound-vars ana-env lib deps cb] (analyze-deps bound-vars ana-env lib deps nil cb)) ([bound-vars ana-env lib deps opts cb] - (let [compiler @(:*compiler* bound-vars)] - (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) - update-in [:dep-path] conj lib)] - (assert (every? #(not (contains? (:*cljs-dep-set* bound-vars) %)) deps) - (str "Circular dependency detected " - (-> (:*cljs-dep-set* bound-vars) meta :dep-path))) - (if (seq deps) - (let [dep (first deps)] - (try - ((:*load-fn* bound-vars) {:name dep :path (ns->relpath dep)} - (fn [resource] - (assert (or (map? resource) (nil? resource)) - "*load-fn* may only return a map or nil") - (if-not resource - (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] - (get {dep nil} cljs-ns cljs-ns))] - (do - (patch-alias-map (:*compiler* bound-vars) lib dep cljs-dep) - (analyze-deps bound-vars ana-env lib (cons cljs-dep (next deps)) opts - (fn [res] - (if (:error res) - (cb res) - (cb (update res :aliased-loads assoc dep cljs-dep)))))) - (cb (wrap-error + (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) + update-in [:dep-path] conj lib)] + (let [compiler @(:*compiler* bound-vars) + bound-vars (assoc bound-vars :*cljs-dep-set* ana/*cljs-dep-set*)] + (if-not (every? #(not (contains? ana/*cljs-dep-set* %)) deps) + (cb (wrap-error + (ana/error ana-env + (str "Circular dependency detected " + (apply str + (interpose " -> " + (conj (-> ana/*cljs-dep-set* meta :dep-path) + (some ana/*cljs-dep-set* deps)))))))) + (if (seq deps) + (let [dep (first deps)] + (try + ((:*load-fn* bound-vars) {:name dep :path (ns->relpath dep)} + (fn [resource] + (assert (or (map? resource) (nil? resource)) + "*load-fn* may only return a map or nil") + (if-not resource + (if-let [cljs-dep (let [cljs-ns (ana/clj-ns->cljs-ns dep)] + (get {dep nil} cljs-ns cljs-ns))] + (do + (patch-alias-map (:*compiler* bound-vars) lib dep cljs-dep) + (analyze-deps bound-vars ana-env lib (cons cljs-dep (next deps)) opts + (fn [res] + (if (:error res) + (cb res) + (cb (update res :aliased-loads assoc dep cljs-dep)))))) + (cb (wrap-error + (ana/error ana-env + (ana/error-message :undeclared-ns + {:ns-sym dep :js-provide (name dep)}))))) + (let [{:keys [name lang source file]} resource] + (condp keyword-identical? lang + :clj (do + (pre-file-side-effects (:*compiler* bound-vars) name file opts) + (analyze-str* bound-vars source name (assoc opts :cljs-file file) + (fn [res] + (post-file-side-effects file opts) + (if-not (:error res) + (analyze-deps bound-vars ana-env lib (next deps) opts cb) + (cb res))))) + :js (analyze-deps bound-vars ana-env lib (next deps) opts cb) + (wrap-error (ana/error ana-env - (ana/error-message :undeclared-ns - {:ns-sym dep :js-provide (name dep)}))))) - (let [{:keys [name lang source file]} resource] - (condp = lang - :clj (do - (pre-file-side-effects (:*compiler* bound-vars) name file opts) - (analyze-str* bound-vars source name (assoc opts :cljs-file file) - (fn [res] - (post-file-side-effects file opts) - (if-not (:error res) - (analyze-deps bound-vars ana-env lib (next deps) opts cb) - (cb res))))) - :js (analyze-deps bound-vars ana-env lib (next deps) opts cb) - (wrap-error - (ana/error ana-env - (str "Invalid :lang specified " lang ", only :clj or :js allowed")))))))) - (catch :default cause - (cb (wrap-error - (ana/error ana-env - (str "Could not analyze dep " dep) cause)))))) - (cb {:value nil})))))) + (str "Invalid :lang specified " lang ", only :clj or :js allowed")))))))) + (catch :default cause + (cb (wrap-error + (ana/error ana-env + (str "Could not analyze dep " dep) cause)))))) + (cb {:value nil}))))))) (defn- load-macros [bound-vars k macros lib reload reloads opts cb] (if (seq macros) @@ -738,6 +750,7 @@ :*data-readers* tags/*cljs-data-readers* :*passes* (or (:passes opts) ana/*passes*) :*analyze-deps* (:analyze-deps opts true) + :*cljs-dep-set* ana/*cljs-dep-set* :*load-macros* (:load-macros opts true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} @@ -845,6 +858,7 @@ {:*compiler* state :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (:analyze-deps opts true) + :*cljs-dep-set* ana/*cljs-dep-set* :*load-macros* (:load-macros opts true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} @@ -972,6 +986,7 @@ (compile-str* {:*compiler* state :*data-readers* tags/*cljs-data-readers* + :*cljs-dep-set* ana/*cljs-dep-set* :*analyze-deps* (:analyze-deps opts true) :*load-macros* (:load-macros opts true) :*load-fn* (or (:load opts) *load-fn*) @@ -1140,6 +1155,7 @@ {:*compiler* state :*data-readers* tags/*cljs-data-readers* :*analyze-deps* (:analyze-deps opts true) + :*cljs-dep-set* ana/*cljs-dep-set* :*load-macros* (:load-macros opts true) :*load-fn* (or (:load opts) *load-fn*) :*eval-fn* (or (:eval opts) *eval-fn*)} diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 5b3efffb7..71ed00b70 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1228,6 +1228,37 @@ (is (re-find #"goog\.require\('cljs.x'\)" value)) (inc! l)))))) +(deftest test-cljs-2356 + (async done + (let [st (cljs/empty-state) + load (fn [{:keys [name macros]} cb] + (cb (cond + (= name 'circular.a) + {:lang :clj + :source "(ns circular.a (:require circular.b))"} + + (= name 'circular.b) + {:lang :clj + :source "(ns circular.b (:require circular.a))"}))) + l (latch 2 done)] + (binding [ana/*cljs-dep-set* (with-meta #{} {:dep-path []})] + (cljs.js/compile-str st "(ns circular.a (:require circular.b))" nil + {:load load} + (fn [{:keys [error value] :as m}] + (is (some? error)) + (is (= "Circular dependency detected circular.a -> circular.b -> circular.a" + (.-message error))) + (inc! l)))) + (binding [ana/*cljs-dep-set* (with-meta #{} {:dep-path []})] + (cljs.js/eval-str st "(ns circular.a (:require circular.b))" nil + {:load load + :eval node-eval} + (fn [{:keys [error value] :as m}] + (is (some? error)) + (is (= "Circular dependency detected circular.a -> circular.b -> circular.a" + (.-message error))) + (inc! l))))))) + (defn -main [& args] (run-tests)) From 33ce3d5c1c5d9fd4c38fbc6798a8d7085338eb4d Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sun, 10 Sep 2017 21:30:11 -0700 Subject: [PATCH 2699/4033] CLJS-2357: Self-host: run all async tests We may not be running all tests because they're async and we weren't using the `async` macro in some of them. --- src/test/self/self_host/test.cljs | 561 ++++++++++++++++-------------- 1 file changed, 300 insertions(+), 261 deletions(-) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 71ed00b70..075555da5 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -220,7 +220,7 @@ (deftest test-eval-str (async done - (let [l (latch 8 done)] + (let [l (latch 9 done)] (cljs/eval-str st "(+ 1 1)" nil {:eval node-eval} (fn [{:keys [error value]}] @@ -291,91 +291,102 @@ (inc! l)))))) (deftest test-disable-analyze-deps - (cljs/analyze-str st - "(ns analyze-deps-as.core (:require [analyze-me.core :refer [abc]]))" - nil - {:context :expr - :eval cljs.js/js-eval - :analyze-deps false - :load (fn [_ cb] - (cb {:lang :clj - :source "(ns analyze-me.core)"}))} - (fn [{:keys [error]}] - (is (nil? error)))) - - (cljs/eval st - '(ns analyze-deps-e.core (:require [analyze-me.core :refer [abc]])) - {:context :expr - :eval cljs.js/js-eval - :analyze-deps false - :load (fn [_ cb] - (cb {:lang :clj - :source "(ns analyze-me.core)"}))} - (fn [{:keys [error]}] - (is (nil? error)))) - (cljs/compile-str st - "(ns analyze-deps-c.core (:require [analyze-me.core :refer [abc]]))" - nil - {:context :expr - :eval cljs.js/js-eval - :analyze-deps false - :load (fn [_ cb] - (cb {:lang :clj - :source "(ns analyze-me.core)"}))} - (fn [{:keys [error]}] - (is (nil? error)))) - (cljs/eval-str st - "(ns analyze-deps-es.core (:require [analyze-me.core :refer [abc]]))" - nil - {:context :expr - :eval cljs.js/js-eval - :analyze-deps false - :load (fn [_ cb] - (cb {:lang :clj - :source "(ns analyze-me.core)"}))} - (fn [{:keys [error]}] - (is (nil? error))))) + (async done + (let [l (latch 4 done)] + (cljs/analyze-str st + "(ns analyze-deps-as.core (:require [analyze-me.core :refer [abc]]))" + nil + {:context :expr + :eval cljs.js/js-eval + :analyze-deps false + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns analyze-me.core)"}))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l))) + (cljs/eval st + '(ns analyze-deps-e.core (:require [analyze-me.core :refer [abc]])) + {:context :expr + :eval cljs.js/js-eval + :analyze-deps false + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns analyze-me.core)"}))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l))) + (cljs/compile-str st + "(ns analyze-deps-c.core (:require [analyze-me.core :refer [abc]]))" + nil + {:context :expr + :eval cljs.js/js-eval + :analyze-deps false + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns analyze-me.core)"}))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l))) + (cljs/eval-str st + "(ns analyze-deps-es.core (:require [analyze-me.core :refer [abc]]))" + nil + {:context :expr + :eval cljs.js/js-eval + :analyze-deps false + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns analyze-me.core)"}))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l)))))) (deftest test-disable-load-macros - (cljs/analyze-str st - "(ns load-macros-as.core (:require-macros [load-me.core]))" - nil - {:context :expr - :eval cljs.js/js-eval - :load-macros false - :load (fn [_ _] - (throw (ex-info "unexpected" {})))} - (fn [{:keys [error]}] - (is (nil? error)))) - (cljs/eval st - '(ns load-macros-e.core (:require-macros [load-me.core])) - {:context :expr - :eval cljs.js/js-eval - :load-macros false - :load (fn [_ _] - (throw (ex-info "unexpected" {})))} - (fn [{:keys [error]}] - (is (nil? error)))) - (cljs/compile-str st - "(ns load-macros-c.core (:require-macros [load-me.core]))" - nil - {:context :expr - :eval cljs.js/js-eval - :load-macros false - :load (fn [_ _] - (throw (ex-info "unexpected" {})))} - (fn [{:keys [error]}] - (is (nil? error)))) - (cljs/eval-str st - "(ns load-macros-es.core (:require-macros [load-me.core]))" - nil - {:context :expr - :eval cljs.js/js-eval - :load-macros false - :load (fn [_ _] - (throw (ex-info "unexpected" {})))} - (fn [{:keys [error]}] - (is (nil? error))))) + (async done + (let [l (latch 4 done)] + (cljs/analyze-str st + "(ns load-macros-as.core (:require-macros [load-me.core]))" + nil + {:context :expr + :eval cljs.js/js-eval + :load-macros false + :load (fn [_ _] + (throw (ex-info "unexpected" {})))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l))) + (cljs/eval st + '(ns load-macros-e.core (:require-macros [load-me.core])) + {:context :expr + :eval cljs.js/js-eval + :load-macros false + :load (fn [_ _] + (throw (ex-info "unexpected" {})))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l))) + (cljs/compile-str st + "(ns load-macros-c.core (:require-macros [load-me.core]))" + nil + {:context :expr + :eval cljs.js/js-eval + :load-macros false + :load (fn [_ _] + (throw (ex-info "unexpected" {})))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l))) + (cljs/eval-str st + "(ns load-macros-es.core (:require-macros [load-me.core]))" + nil + {:context :expr + :eval cljs.js/js-eval + :load-macros false + :load (fn [_ _] + (throw (ex-info "unexpected" {})))} + (fn [{:keys [error]}] + (is (nil? error)) + (inc! l)))))) (deftest test-load-and-invoke-macros (async done @@ -643,136 +654,160 @@ (inc! l)))))) (deftest test-CLJS-1330 - (cljs/eval-str st - "(.toString 1)" - nil - {:eval node-eval} - (fn [{:keys [error value]}] - (is (= "1" value))))) + (async done + (cljs/eval-str st + "(.toString 1)" + nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (= "1" value)) + (done))))) (deftest test-CLJS-1551 - (cljs/eval-str st - "(if-let [x true y true] 3)" - nil - {:eval node-eval} - (fn [{:keys [error value]}] - (is (nil? value)) - (is (= "if-let requires exactly 2 forms in binding vector at line 1 " (ex-message (ex-cause error)))))) - (cljs/eval-str st - "(if-let [x true] 1 2 3)" - nil - {:eval node-eval} - (fn [{:keys [error value]}] - (is (nil? value)) - (is (= "if-let requires 1 or 2 forms after binding vector at line 1 " (ex-message (ex-cause error)))))) - (cljs/eval-str st - "(if-let '(x true) 1)" - nil - {:eval node-eval} - (fn [{:keys [error value]}] - (is (nil? value)) - (is (= "if-let requires a vector for its binding at line 1 " (ex-message (ex-cause error))))))) + (async done + (let [l (latch 3 done)] + (cljs/eval-str st + "(if-let [x true y true] 3)" + nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "if-let requires exactly 2 forms in binding vector at line 1 " (ex-message (ex-cause error)))) + (inc! l))) + (cljs/eval-str st + "(if-let [x true] 1 2 3)" + nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "if-let requires 1 or 2 forms after binding vector at line 1 " (ex-message (ex-cause error)))) + (inc! l))) + (cljs/eval-str st + "(if-let '(x true) 1)" + nil + {:eval node-eval} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "if-let requires a vector for its binding at line 1 " (ex-message (ex-cause error)))) + (inc! l)))))) (deftest test-CLJS-1573 - (cljs/compile-str st - "\"90°\"" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= "\"90\\u00b0\"" value)))) - (cljs/compile-str st - "\"Ϊ\"" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= "\"\\u03aa\"" value)))) - (cljs/compile-str st - "\"ሴ\"" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= "\"\\u1234\"" value)))) - (cljs/eval-str st - "\"90°\"" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= "90°" value))))) + (async done + (let [l (latch 4 done)] + (cljs/compile-str st + "\"90°\"" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "\"90\\u00b0\"" value)) + (inc! l))) + (cljs/compile-str st + "\"Ϊ\"" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "\"\\u03aa\"" value)) + (inc! l))) + (cljs/compile-str st + "\"ሴ\"" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "\"\\u1234\"" value)) + (inc! l))) + (cljs/eval-str st + "\"90°\"" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= "90°" value)) + (inc! l)))))) (deftest test-CLJS-1577 - (cljs/analyze-str st - "`.x" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= '.x (:form value))))) - (cljs/compile-str st - "`.x" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (string/starts-with? value "new cljs.core.Symbol(null,\".x\",\".x\",")))) - (cljs/eval-str st - "`.x" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= '.x value))))) + (async done + (let [l (latch 3 done)] + (cljs/analyze-str st + "`.x" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= '.x (:form value))) + (inc! l))) + (cljs/compile-str st + "`.x" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (string/starts-with? value "new cljs.core.Symbol(null,\".x\",\".x\",")) + (inc! l))) + (cljs/eval-str st + "`.x" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= '.x value)) + (inc! l)))))) (deftest test-CLJS-1584 - (cljs/eval-str st - "(condp = 1 1 2)" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= 2 value))))) + (async done + (cljs/eval-str st + "(condp = 1 1 2)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 2 value)) + (done))))) (deftest test-CLJS-1585 - (cljs/eval-str st - "(ns alias-load.core (:require [aliased.core :as alias]))" - nil - {:ns 'cljs.user - :context :expr - :eval cljs.js/js-eval - :load (fn [_ cb] - (cb {:lang :clj :source "(ns aliased.core)"}))} - (fn [{:keys [error value]}] - (is (nil? error)) - (cljs.js/eval-str st - "::alias/bar" - nil - {:ns 'alias-load.core - :context :expr - :eval cljs.js/js-eval} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= :aliased.core/bar value))))))) + (async done + (cljs/eval-str st + "(ns alias-load.core (:require [aliased.core :as alias]))" + nil + {:ns 'cljs.user + :context :expr + :eval cljs.js/js-eval + :load (fn [_ cb] + (cb {:lang :clj :source "(ns aliased.core)"}))} + (fn [{:keys [error value]}] + (is (nil? error)) + (cljs.js/eval-str st + "::alias/bar" + nil + {:ns 'alias-load.core + :context :expr + :eval cljs.js/js-eval} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= :aliased.core/bar value)) + (done))))))) (deftest test-CLJS-1589 - (cljs/eval-str st - "(case 1 nil nil :x)" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [error value]}] - (is (nil? error)) - (is (= :x value))))) + (async done + (cljs/eval-str st + "(case 1 nil nil :x)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= :x value)) + (done))))) (deftest test-CLJS-1612 (async done @@ -835,68 +870,72 @@ (deftest test-cljs-1651 (let [st (cljs/empty-state)] - (cljs/eval-str st - "(defn double [x] (* 2 x))" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [value error]}] - (is (nil? error)) - (cljs/eval-str st - "[(double 3) (apply double [3])]" - nil - {:eval node-eval - :context :expr} - (fn [{:keys [value error]}] - (is (= value [6 6])))))))) + (async done + (cljs/eval-str st + "(defn double [x] (* 2 x))" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "[(double 3) (apply double [3])]" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (= value [6 6])) + (done)))))))) (deftest test-cljs-1854 (let [st (cljs/empty-state)] - (cljs/eval st - '(require 'foo.core1854) - {:eval node-eval - :context :expr - :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 1)"}))} - (fn [{:keys [value error]}] - (is (nil? error)) - (cljs/eval st - 'foo.core1854/x - {:eval node-eval - :context :expr} - (fn [{:keys [value error]}] - (is (nil? error)) - (is (= value 1)))) - (cljs/eval st - '(require 'foo.core1854 :reload) - {:eval node-eval - :context :expr - :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 2)"}))} - (fn [{:keys [value error]}] - (is (nil? error)) - (cljs/eval st - 'foo.core1854/x - {:eval node-eval - :context :expr} - (fn [{:keys [value error]}] - (is (nil? error)) - (is (= value 2)))) - (cljs/eval st - '(require 'bar.core1854 :reload-all) - {:eval node-eval - :context :expr - :load (fn [{:keys [name]} cb] - (case name - bar.core1854 (cb {:lang :clj :source "(ns bar.core1854 (:require [foo.core1854]))"}) - foo.core1854 (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 3)"})))} - (fn [{:keys [value error]}] - (is (nil? error)) - (cljs/eval st - 'foo.core1854/x - {:eval node-eval - :context :expr} - (fn [{:keys [value error]}] - (is (nil? error)) - (is (= value 3)))))))))))) + (async done + (cljs/eval st + '(require 'foo.core1854) + {:eval node-eval + :context :expr + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 1)"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval st + 'foo.core1854/x + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (nil? error)) + (is (= value 1)))) + (cljs/eval st + '(require 'foo.core1854 :reload) + {:eval node-eval + :context :expr + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 2)"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval st + 'foo.core1854/x + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (nil? error)) + (is (= value 2)))) + (cljs/eval st + '(require 'bar.core1854 :reload-all) + {:eval node-eval + :context :expr + :load (fn [{:keys [name]} cb] + (case name + bar.core1854 (cb {:lang :clj :source "(ns bar.core1854 (:require [foo.core1854]))"}) + foo.core1854 (cb {:lang :clj :source "(ns foo.core1854) (def ^:const x 3)"})))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval st + 'foo.core1854/x + {:eval node-eval + :context :expr} + (fn [{:keys [value error]}] + (is (nil? error)) + (is (= value 3)) + (done)))))))))))) (deftest test-cljs-1874 (async done From 3c0df9c6e712ea1cb2e8a469b5ceaf0dff17d458 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Wed, 13 Sep 2017 21:58:08 -0700 Subject: [PATCH 2700/4033] CLJS-2361: Self-host: circular dependency detection doesn't handle REPL self-require --- src/main/cljs/cljs/js.cljs | 10 ++++++++-- src/test/self/self_host/test.cljs | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 04966d1d0..368c65b6c 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -366,14 +366,20 @@ (patch-renames :renames) (patch-renames :rename-macros))) +(defn- self-require? [deps opts] + (and (true? (:def-emits-var opts)) (some #{ana/*cljs-ns*} deps))) + (defn- load-deps ([bound-vars ana-env lib deps cb] (load-deps bound-vars ana-env lib deps nil nil cb)) ([bound-vars ana-env lib deps reload opts cb] (when (:verbose opts) (debug-prn "Loading dependencies for" lib)) - (binding [ana/*cljs-dep-set* (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) - update-in [:dep-path] conj lib)] + (binding [ana/*cljs-dep-set* (let [lib (if (self-require? deps opts) + 'cljs.user + lib)] + (vary-meta (conj (:*cljs-dep-set* bound-vars) lib) + update-in [:dep-path] conj lib))] (let [bound-vars (assoc bound-vars :*cljs-dep-set* ana/*cljs-dep-set*)] (if-not (every? #(not (contains? ana/*cljs-dep-set* %)) deps) (cb (wrap-error diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 075555da5..6deb92bf2 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1298,6 +1298,27 @@ (.-message error))) (inc! l))))))) +(deftest test-self-host-self-require + (async done + (let [st (cljs/empty-state) + l (latch 1 done) + load (fn [{:keys [name macros]} cb] + (cb {:lang :clj + :source "(ns foo.core)"}))] + (binding [ana/*cljs-dep-set* (with-meta #{} {:dep-path []})] + (cljs.js/eval-str st "(ns foo.core)" nil + {:eval node-eval} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (cljs.js/eval-str st "(require 'foo.core :reload)" nil + {:load load + :eval node-eval + :def-emits-var true + :ns 'foo.core} + (fn [{:keys [error value] :as m}] + (is (nil? error)) + (inc! l))))))))) + (defn -main [& args] (run-tests)) From fc940017c93a14748e0d93612ddf626fcb0aaae6 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sun, 10 Sep 2017 20:01:58 -0700 Subject: [PATCH 2701/4033] CLJS-2355: Fix tests after CLJS-2349 *warn-on-reflection* doesn't exist in CLJS --- src/test/cljs/cljs/core_test.cljs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index f2d7b065d..8fd8be1f1 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1455,13 +1455,12 @@ (is (= 2 @a)))) (deftest deref-swap-arities - (binding [*warn-on-reflection* true] - (let [a (atom 0)] - (is (= [0 1] (swap-vals! a + 1))) - (is (= [1 3] (swap-vals! a + 1 1))) - (is (= [3 6] (swap-vals! a + 1 1 1))) - (is (= [6 10] (swap-vals! a + 1 1 1 1))) - (is (= 10 @a))))) + (let [a (atom 0)] + (is (= [0 1] (swap-vals! a + 1))) + (is (= [1 3] (swap-vals! a + 1 1))) + (is (= [3 6] (swap-vals! a + 1 1 1))) + (is (= [6 10] (swap-vals! a + 1 1 1 1))) + (is (= 10 @a)))) (deftest deref-reset-returns-old-value (let [a (atom 0)] From 7a8803ef70cb84c686341353e7ab29928487e388 Mon Sep 17 00:00:00 2001 From: Pieter du Toit Date: Wed, 30 Aug 2017 15:54:58 +0200 Subject: [PATCH 2702/4033] CLJS-2338: Support renamePrefix{Namespace} closure compiler option --- src/main/clojure/cljs/closure.clj | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 733d3ad01..9920ed3e4 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -166,7 +166,7 @@ :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs - :watch :watch-error-fn :watch-fn :install-deps :process-shim}) + :watch :watch-error-fn :watch-fn :install-deps :process-shim :rename-prefix :rename-prefix-namespace}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -254,6 +254,14 @@ (. compiler-options (setRewritePolyfills (:rewrite-polyfills opts)))) + (when (contains? opts :rename-prefix) + (. compiler-options + (setRenamePrefix (:rename-prefix opts)))) + + (when (contains? opts :rename-prefix-namespace) + (. compiler-options + (setRenamePrefixNamespace (:rename-prefix-namespace opts)))) + (. compiler-options (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler ) From 245bdee2c35e19a9685b7a0849f26fce8bdaf7ca Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sun, 17 Sep 2017 21:11:50 -0700 Subject: [PATCH 2703/4033] CLJS-2339: Significant code reload slowdown with :npm-deps --- src/main/clojure/cljs/analyzer.cljc | 12 ++++-- src/main/clojure/cljs/closure.clj | 67 +++++++++++++++++++---------- src/main/clojure/cljs/repl.cljc | 5 ++- 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 11e4c2e1d..ca29615a1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -739,17 +739,23 @@ (or (some? (get (:requires ns) prefix)) (some? (get (:imports ns) prefix)))))) -(defn js-module-exists? - [module] +(defn- internal-js-module-exists? + [js-module-index module] ;; we need to check both keys and values of the JS module index, because ;; macroexpansion will be looking for the provided name - António Monteiro (contains? (into #{} (mapcat (fn [[k v]] [k (:name v)])) - (get-in @env/*compiler* [:js-module-index])) + js-module-index) (str module))) +(def js-module-exists?* (memoize internal-js-module-exists?)) + +(defn js-module-exists? + [module] + (js-module-exists?* (get-in @env/*compiler* [:js-module-index]) module)) + (defn node-module-dep? #?(:cljs {:tag boolean}) [module] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9920ed3e4..246277b5b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2141,7 +2141,7 @@ (defn maybe-install-node-deps! [{:keys [npm-deps verbose] :as opts}] (let [npm-deps (merge npm-deps (compute-upstream-npm-deps opts))] - (if-not (empty? npm-deps) + (when-not (empty? npm-deps) (let [pkg-json (io/file "package.json")] (when (or ana/*verbose* verbose) (util/debug-prn "Installing Node.js dependencies")) @@ -2165,9 +2165,8 @@ (bound-fn [] (pipe proc es ew))))) err (.waitFor proc)] (when (and (not (zero? err)) (not (.isAlive proc))) - (println (str ew))) - opts)) - opts))) + (println (str ew))))) + true))) (defn node-module-deps "EXPERIMENTAL: return the foreign libs entries as computed by running @@ -2232,11 +2231,23 @@ (let [node-modules (io/file "node_modules")] (if (and (not (empty? modules)) (.exists node-modules) (.isDirectory node-modules)) (let [modules (into #{} (map name) modules) - deps-file (io/file (util/output-directory opts) "cljs$node_modules.js")] + deps-file (io/file (util/output-directory opts) "cljs$node_modules.js") + old-contents (when (.exists deps-file) + (slurp deps-file)) + new-contents (let [sb (StringBuffer.)] + (run! #(.append sb (str "require('" % "');\n")) modules) + (str sb))] (util/mkdirs deps-file) - (with-open [w (io/writer deps-file)] - (run! #(.write w (str "require('" % "');\n")) modules)) - (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)) + (if-not (= old-contents new-contents) + (do + (spit deps-file new-contents) + (let [transitive-js (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)] + (when-not (nil? env/*compiler*) + (swap! env/*compiler* update-in [::transitive-dep-set] + assoc modules transitive-js)) + transitive-js)) + (when-not (nil? env/*compiler*) + (get-in @env/*compiler* [::transitive-dep-set modules])))) [])))) (defn- node-file-seq->libs-spec* @@ -2454,8 +2465,8 @@ - process the JS modules (preprocess + convert to Closure JS) - save js-dependency-index for compilation" [{:keys [npm-deps target] :as opts} js-sources compiler-env] - (let [;; Find all the top-level Node packages and their files - top-level (reduce + ;; Find all the top-level Node packages and their files + (let [top-level (reduce (fn [acc m] (reduce (fn [acc p] (assoc acc p m)) acc (:provides m))) {} (index-node-modules-dir)) @@ -2464,18 +2475,24 @@ ;; and create list of all their dependencies node-required (set/intersection (set (keys top-level)) requires) expanded-libs (expand-libs (:foreign-libs opts)) - opts (-> opts - (update :foreign-libs - (fn [libs] - (into (if (= target :nodejs) - [] - (index-node-modules node-required)) - (into expanded-libs - (node-inputs (filter (fn [{:keys [module-type]}] - (and (some? module-type) - (not= module-type :amd))) - expanded-libs)))))) - process-js-modules)] + output-dir (util/output-directory opts) + opts (update opts :foreign-libs + (fn [libs] + (into (if (= target :nodejs) + [] + (index-node-modules node-required)) + (into expanded-libs + (node-inputs (filter (fn [{:keys [module-type]}] + (and (some? module-type) + (not= module-type :amd))) + expanded-libs)))))) + opts (if (some + (fn [ijs] + (let [dest (io/file output-dir (rel-output-path (assoc ijs :foreign true) opts))] + (util/changed? (deps/-url ijs opts) dest))) + (:foreign-libs opts)) + (process-js-modules opts) + (:options @compiler-env))] (swap! compiler-env (fn [cenv] (-> cenv ;; we need to also track the whole top level - this is to support @@ -2503,7 +2520,10 @@ ;; we want to warn about NPM dep conflicts before installing the modules (when (:install-deps opts) (check-npm-deps opts) - (maybe-install-node-deps! opts)) + (swap! compiler-env update-in [:npm-deps-installed?] + (fn [installed?] + (when-not installed? + (maybe-install-node-deps! opts))))) (let [compiler-stats (:compiler-stats opts) checked-arrays (or (:checked-arrays opts) ana/*checked-arrays*) @@ -2569,6 +2589,7 @@ (-> (-find-sources source all-opts) (add-dependency-sources compile-opts))) all-opts (handle-js-modules all-opts js-sources compiler-env) + _ (swap! env/*compiler* update-in [:options] merge all-opts) js-sources (-> js-sources deps/dependency-order (compile-sources compiler-stats compile-opts) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index f96be3706..bd9035eb6 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -881,7 +881,10 @@ (print value))))))] (when (:install-deps opts) (cljsc/check-npm-deps opts) - (cljsc/maybe-install-node-deps! opts)) + (swap! env/*compiler* update-in [:npm-deps-installed?] + (fn [installed?] + (when-not installed? + (cljsc/maybe-install-node-deps! opts))))) (comp/with-core-cljs opts (fn [] (binding [*repl-opts* opts] From 89914d2ead964122f99e638edda0cd96d330cb66 Mon Sep 17 00:00:00 2001 From: OHTA Shogo Date: Tue, 12 Sep 2017 14:25:45 +0900 Subject: [PATCH 2704/4033] CLJS-2352: Emit valid JS for NaN etc. even when used w/ CLJ >= 1.9.0-alpha20 --- src/main/clojure/cljs/compiler.cljc | 19 +++++++++++++++++-- src/test/clojure/cljs/compiler_tests.clj | 10 ++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index f8982c1f1..a51fe40d5 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -248,9 +248,24 @@ (defmethod emit-constant Integer [x] (emits x))) ; reader puts Integers in metadata #?(:clj - (defmethod emit-constant Double [x] (emits x)) + (defmethod emit-constant Double [x] + (let [x (double x)] + (cond (Double/isNaN x) + (emits "NaN") + + (Double/isInfinite x) + (emits (if (pos? x) "Infinity" "-Infinity")) + + :else (emits x)))) :cljs - (defmethod emit-constant js/Number [x] (emits "(" x ")"))) + (defmethod emit-constant js/Number [x] + (cond (js/isNaN x) + (emits "NaN") + + (not (js/isFinite x)) + (emits (if (pos? x) "Infinity" "-Infinity")) + + :else (emits "(" x ")")))) #?(:clj (defmethod emit-constant BigDecimal [x] (emits (.doubleValue ^BigDecimal x)))) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 96f7cabf5..77ddb773f 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -65,6 +65,16 @@ (ana/analyze (assoc aenv :context :expr) 'js/-Infinity))) "-Infinity"))) +(deftest test-cljs-2352 + (are [form result] + (= (with-out-str + (comp/emit + (ana/analyze (assoc aenv :context :expr) form))) + result) + Double/NaN "##NaN" + Double/POSITIVE_INFINITY "##Inf" + Double/NEGATIVE_INFINITY "##-Inf")) + (deftest test-munge-dotdot (is (= 'cljs.core._DOT__DOT_ (comp/munge 'cljs.core/..))) (is (= "cljs.core._DOT__DOT_" (comp/munge "cljs.core/.."))) From 4607affd1f399839b44eef1aa01e0645cee539d4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 20 Sep 2017 14:37:02 -0400 Subject: [PATCH 2705/4033] CLJS-2367: Self-host: :def-emits-var leaks into loaded namespace processing --- src/main/cljs/cljs/js.cljs | 2 ++ src/test/self/self_host/test.cljs | 41 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 368c65b6c..934d67730 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -393,6 +393,7 @@ (let [dep (first deps) opts' (-> opts (dissoc :context) + (dissoc :def-emits-var) (dissoc :ns))] (require bound-vars dep reload opts' (fn [res] @@ -485,6 +486,7 @@ opts' (-> opts (assoc :macros-ns true) (dissoc :context) + (dissoc :def-emits-var) (dissoc :ns))] (require bound-vars nsym k opts' (fn [res] diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 6deb92bf2..98f89eb82 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1319,6 +1319,47 @@ (is (nil? error)) (inc! l))))))))) +(deftest test-cljs-2367 + (async done + (let [st (cljs/empty-state) + l (latch 2 done)] + (cljs.js/eval st + '(require (quote foo-2367.core)) + {:context :expr + :def-emits-var true + :eval node-eval + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns foo-2367.core) (def b (def a 3))"}))} + (fn [{:keys [error value]}] + (is (nil? error)) + (cljs.js/eval st + 'foo-2367.core/b + {:context :expr + :eval node-eval} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 3 value)) + (inc! l))))) + (cljs.js/eval st + '(require-macros (quote bar-2367.core)) + {:context :expr + :def-emits-var true + :eval node-eval + :load (fn [_ cb] + (cb {:lang :clj + :source "(ns bar-2367.core) (def b (def a 4)) (defmacro c [] b)"}))} + (fn [{:keys [error value]}] + (is (nil? error)) + (cljs.js/eval st + '(bar-2367.core/c) + {:context :expr + :eval node-eval} + (fn [{:keys [error value]}] + (is (nil? error)) + (is (= 4 value)) + (inc! l)))))))) + (defn -main [& args] (run-tests)) From 5aa12a3cb965a3657a4d9e58a6c168b8ac8bfc81 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 29 Aug 2017 19:37:50 -0400 Subject: [PATCH 2706/4033] CLJS-2340: Have js-keys delegate directly to good.object/getKeys --- benchmark/cljs/benchmark_runner.cljs | 4 ++++ src/main/cljs/cljs/core.cljs | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 2daaa6b80..6e8b85e93 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -31,6 +31,10 @@ (println ";; amap") (simple-benchmark [arr (to-array (range 1000000))] (amap arr i ret (* 10 (aget arr i))) 1) +(println ";; js-keys") +(simple-benchmark [obj (js-obj "a" 1 "b" 2) f js-keys] (f obj) 400000) +(simple-benchmark [obj (js-obj "a" 1 "b" 2 "c" 3 "d" 4 "e" 5 "f" 6) f js-keys] (f obj) 400000) + (println ";;; instance?") ;; WARNING: will get compiled away under advanced (simple-benchmark [coll []] (instance? PersistentVector coll) 1000000) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 519e4ab06..5025f976c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2150,9 +2150,7 @@ reduces them without incurring seq initialization" (defn js-keys "Return the JavaScript keys for an object." [obj] - (let [keys (array)] - (gobject/forEach obj (fn [val key obj] (.push keys key))) - keys)) + (gobject/getKeys obj)) (defn js-delete "Delete a property from a JavaScript object." From 6e5cb9046458cdacb783d2b2e0c376525a21518e Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sat, 16 Sep 2017 16:58:27 -0700 Subject: [PATCH 2707/4033] CLJS-2364: Bump Closure Compiler to the Sep 2017 version --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 560d35de2..fbbe2736e 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20170806 + v20170910 org.clojure diff --git a/project.clj b/project.clj index dae2581e1..68b60976e 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] - [com.google.javascript/closure-compiler-unshaded "v20170806"] + [com.google.javascript/closure-compiler-unshaded "v20170910"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main clojure.main}} diff --git a/script/bootstrap b/script/bootstrap index 282dd0b75..723d765c3 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,7 +5,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha17" SPEC_ALPHA_RELEASE="0.1.123" CORE_SPECS_ALPHA_RELEASE="0.1.10" -CLOSURE_RELEASE="20170806" +CLOSURE_RELEASE="20170910" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" From 7916a6782b672c80a8c31621b0e66d3dc88db73a Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Fri, 22 Sep 2017 22:52:14 -0700 Subject: [PATCH 2708/4033] CLJS-2368: Self-host: Never compile macro namespaces with `:optimize-constants true` Macro namespaces may be evaluated in a compiler environment that is different from the one which has emitted the constants table, which leads to cryptic errors because the constants are not available in the evaluating environment. --- src/main/cljs/cljs/js.cljs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 934d67730..f5b5cfe04 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -224,7 +224,9 @@ [bound-vars cache opts cb] (process-deps bound-vars (distinct (vals (:require-macros cache))) - (assoc opts :macros-ns true) + (-> opts + (assoc :macros-ns true) + (dissoc :emit-constants :optimize-constants)) cb)) (defn- process-libs-deps @@ -487,7 +489,8 @@ (assoc :macros-ns true) (dissoc :context) (dissoc :def-emits-var) - (dissoc :ns))] + (dissoc :ns) + (dissoc :emit-constants :optimize-constants))] (require bound-vars nsym k opts' (fn [res] (if-not (:error res) @@ -1072,7 +1075,8 @@ (:def-emits-var opts)) (compile-loop ns')))))) (do - (.append sb (with-out-str (comp/emit ast))) + (env/with-compiler-env (assoc @(:*compiler* bound-vars) :options opts) + (.append sb (with-out-str (comp/emit ast)))) (recur ns')))))) (do (when (:source-map opts) From 62e78a830543de0806b0ebcaf01ad22a5f253cfb Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sun, 24 Sep 2017 15:48:50 -0700 Subject: [PATCH 2709/4033] CLJS-2370: Tests failing after CLJS-2352 --- src/test/clojure/cljs/compiler_tests.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 77ddb773f..230a68c24 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -71,9 +71,9 @@ (comp/emit (ana/analyze (assoc aenv :context :expr) form))) result) - Double/NaN "##NaN" - Double/POSITIVE_INFINITY "##Inf" - Double/NEGATIVE_INFINITY "##-Inf")) + Double/NaN "NaN" + Double/POSITIVE_INFINITY "Infinity" + Double/NEGATIVE_INFINITY "-Infinity")) (deftest test-munge-dotdot (is (= 'cljs.core._DOT__DOT_ (comp/munge 'cljs.core/..))) From 35ad08e6728d83d0e7e1cce1d724f080eb5abce4 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Mon, 25 Sep 2017 15:29:54 +0100 Subject: [PATCH 2710/4033] CLJS-2166: Add uri? predicate Clojure introduced a `uri?` predicate in 1.9. This commit adds support for `uri` to Clojurescript under `cljs.core/uri?`. It returns true if the given argument is an instance of `goog.Uri`, false otherwise. --- src/main/cljs/cljs/core.cljs | 9 ++++++++- src/test/cljs/cljs/core_test.cljs | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5025f976c..5686dc3ff 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11,7 +11,8 @@ goog.math.Integer [goog.string :as gstring] [goog.object :as gobject] - [goog.array :as garray]) + [goog.array :as garray] + [goog.Uri]) (:import [goog.string StringBuffer])) ;; next line is auto-generated by the build-script - Do not edit! @@ -11166,3 +11167,9 @@ reduces them without incurring seq initialization" Bootstrap only." [ns-obj] (.-name ns-obj)) + +(defn uri? + "Returns true X is a goog.Uri instance." + {:added "1.9"} + [x] + (instance? goog.Uri x)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 8fd8be1f1..daf324ffc 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1492,3 +1492,13 @@ ;; (assert (= (:arglists var-meta) '([a b])))) ) + +(deftest uri-predicate + (testing "Testing uri?" + (is (not (uri? "http://clojurescript.org"))) + (is (not (uri? 42))) + (is (not (uri? []))) + (is (not (uri? {}))) + (is (uri? (goog.Uri. ""))) + (is (uri? (goog.Uri. "http://clojurescript.org"))) + (is (uri? (goog.Uri. "some string"))))) From add0a41316839388da6331cdd45ce5d0a09a97fc Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Sat, 30 Sep 2017 01:15:37 +0200 Subject: [PATCH 2711/4033] CLJS-2372: update hash to use the new infinity literals --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5686dc3ff..bdbff6f45 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -985,9 +985,9 @@ (if (js/isFinite o) (js-mod (Math/floor o) 2147483647) (case o - Infinity + ##Inf 2146435072 - -Infinity + ##-Inf -1048576 2146959360)) From 26bf9a5baf30bfbe81ef98b9db300b8c9232939f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 30 Sep 2017 09:46:01 +0200 Subject: [PATCH 2712/4033] bump tools.reader --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index fbbe2736e..e5a29e5fb 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.0.5 + 1.1.0 org.clojure diff --git a/project.clj b/project.clj index 68b60976e..c0188c452 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,7 @@ :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.0.5"] + [org.clojure/tools.reader "1.1.0"] [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] diff --git a/script/bootstrap b/script/bootstrap index 723d765c3..a1e78589f 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.0.5" +TREADER_RELEASE="1.1.0" TEST_CHECK_RELEASE="0.10.0-alpha2" # check dependencies From 4d2d258c806f800b0260e67ecadaae5aad207ecf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 30 Sep 2017 10:55:00 +0200 Subject: [PATCH 2713/4033] NaN -> ##NaN --- src/main/cljs/cljs/test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index 4ec51c48a..a6aa186c3 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -373,7 +373,7 @@ (if (> cnt 1) [(js/parseInt (nth parts (- cnt 2)) 10) (js/parseInt (nth parts (dec cnt)) 10)] - [NaN NaN]))) + [##NaN ##NaN]))) (defn js-filename [stack-element] (let [output-dir (cljs.test/cljs-output-dir) From d45012273029bd5df3973ea716394f368e1c44cc Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 30 Sep 2017 09:21:44 -0400 Subject: [PATCH 2714/4033] CLJS-2374: Print js/Infinity, js/-Infinity, js/NaN using new reader literals --- benchmark/cljs/benchmark_runner.cljs | 8 ++++++++ src/main/cljs/cljs/core.cljs | 10 +++++++++- src/test/cljs/cljs/core_test.cljs | 5 +++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 6e8b85e93..ad81ca810 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -407,4 +407,12 @@ (simple-benchmark [] (str "1") 1000000) (simple-benchmark [] (str "1" "2") 1000000) (simple-benchmark [] (str "1" "2" "3") 1000000) + +(println ";; printing of numbers and handling of ##Nan, ##Inf, ##-Inf") +(simple-benchmark [x true] (pr-str x) 1000000) +(simple-benchmark [x 10] (pr-str x) 1000000) +(simple-benchmark [x js/NaN] (pr-str x) 1000000) +(simple-benchmark [x js/Infinity] (pr-str x) 1000000) +(simple-benchmark [x js/-Infinity] (pr-str x) 1000000) +(simple-benchmark [x (js-obj)] (pr-str x) 1000000) (println) \ No newline at end of file diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index bdbff6f45..5762cc0d2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9659,9 +9659,17 @@ reduces them without incurring seq initialization" (implements? IPrintWithWriter obj) (-pr-writer ^not-native obj writer opts) - (or (true? obj) (false? obj) (number? obj)) + (or (true? obj) (false? obj)) (-write writer (str obj)) + (number? obj) + (-write writer + (cond + ^boolean (js/isNaN obj) "##NaN" + (identical? obj js/Number.POSITIVE_INFINITY) "##Inf" + (identical? obj js/Number.NEGATIVE_INFINITY) "##-Inf" + :else (str obj))) + (object? obj) (do (-write writer "#js ") diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index daf324ffc..cddfcd10d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1472,6 +1472,11 @@ (let [a (atom :usual-value)] (is (= :usual-value (reset! a (first (reset-vals! a :almost-never-seen-value))))))) +(deftest test-cljs-2374 + (is (= "##NaN" (pr-str js/NaN))) + (is (= "##Inf" (pr-str js/Infinity))) + (is (= "##-Inf" (pr-str js/-Infinity)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 300e326b83ba4110657bb646a9d396c5cb666501 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 2 Aug 2017 18:01:09 +0300 Subject: [PATCH 2715/4033] CLJS-2294: Always use opts with implicit opts added This fixes e.g. cases where opts checkers presume that :optimizations is always set. add-implicit-opts adds for example :optimization :none. Two test cases used :target :nodejs without :main which is against assertion, but previously the assertion didn't catch this, because tests don't have :optimizations set. --- src/main/clojure/cljs/closure.clj | 93 ++++++++++++----------- src/test/clojure/cljs/build_api_tests.clj | 2 + 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 246277b5b..c3d8d31ed 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2517,37 +2517,38 @@ (add-externs-sources (dissoc opts :foreign-libs)))))) ([source opts compiler-env] (env/with-compiler-env compiler-env - ;; we want to warn about NPM dep conflicts before installing the modules - (when (:install-deps opts) - (check-npm-deps opts) - (swap! compiler-env update-in [:npm-deps-installed?] - (fn [installed?] - (when-not installed? - (maybe-install-node-deps! opts))))) - (let [compiler-stats (:compiler-stats opts) + (let [opts (add-implicit-options opts) + ;; we want to warn about NPM dep conflicts before installing the modules + _ (when (:install-deps opts) + (check-npm-deps opts) + (swap! compiler-env update-in [:npm-deps-installed?] + (fn [installed?] + (when-not installed? + (maybe-install-node-deps! opts))))) + + compiler-stats (:compiler-stats opts) checked-arrays (or (:checked-arrays opts) ana/*checked-arrays*) static-fns? (or (and (= (:optimizations opts) :advanced) (not (false? (:static-fns opts)))) (:static-fns opts) ana/*cljs-static-fns*) - sources (-find-sources source opts) - all-opts (add-implicit-options opts)] + sources (-find-sources source opts)] (check-output-to opts) (check-output-dir opts) (check-source-map opts) (check-source-map-path opts) (check-output-wrapper opts) (check-node-target opts) - (check-preloads all-opts) + (check-preloads opts) (check-cache-analysis-format opts) (swap! compiler-env #(-> % - (update-in [:options] merge all-opts) + (update-in [:options] merge opts) (assoc :target (:target opts)) - ;; Save the current js-dependency index once we have computed all-opts + ;; Save the current js-dependency index once we have computed opts ;; or the analyzer won't be able to find upstream dependencies - Antonio - (assoc :js-dependency-index (deps/js-dependency-index all-opts)) + (assoc :js-dependency-index (deps/js-dependency-index opts)) ;; Save list of sources for cljs.analyzer/locate-src - Juho Teperi (assoc :sources sources))) (binding [comp/*recompiled* (when-not (false? (:recompile-dependents opts)) @@ -2571,76 +2572,76 @@ (repeat warnings)) warnings))) ana/*verbose* (:verbose opts)] - (let [one-file? (and (:main all-opts) - (#{:advanced :simple :whitespace} (:optimizations all-opts))) + (let [one-file? (and (:main opts) + (#{:advanced :simple :whitespace} (:optimizations opts))) source (if one-file? - (let [main (:main all-opts) + (let [main (:main opts) uri (:uri (cljs-source-for-namespace main))] (assert uri (str "No file for namespace " main " exists")) uri) source) compile-opts (if one-file? - (assoc all-opts :output-file (:output-to all-opts)) - all-opts) + (assoc opts :output-file (:output-to opts)) + opts) _ (load-data-readers! compiler-env) ;; reset :js-module-index so that ana/parse-ns called by -find-sources ;; can find the missing JS modules js-sources (env/with-compiler-env (dissoc @compiler-env :js-module-index) - (-> (-find-sources source all-opts) + (-> (-find-sources source opts) (add-dependency-sources compile-opts))) - all-opts (handle-js-modules all-opts js-sources compiler-env) - _ (swap! env/*compiler* update-in [:options] merge all-opts) + opts (handle-js-modules opts js-sources compiler-env) + _ (swap! env/*compiler* update-in [:options] merge opts) js-sources (-> js-sources deps/dependency-order (compile-sources compiler-stats compile-opts) (#(map add-core-macros-if-cljs-js %)) - (add-js-sources all-opts) - (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") all-opts)])) + (add-js-sources opts) + (cond-> (= :nodejs (:target opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") opts)])) deps/dependency-order - (add-preloads all-opts) + (add-preloads opts) add-goog-base - (cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])) - (->> (map #(source-on-disk all-opts %)) doall) - (compile-loader all-opts)) - _ (when (:emit-constants all-opts) + (cond-> (= :nodejs (:target opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") opts)])) + (->> (map #(source-on-disk opts %)) doall) + (compile-loader opts)) + _ (when (:emit-constants opts) (comp/emit-constants-table-to-file (::ana/constant-table @env/*compiler*) - (constants-filename all-opts))) - _ (when (:infer-externs all-opts) + (constants-filename opts))) + _ (when (:infer-externs opts) (comp/emit-inferred-externs-to-file (reduce util/map-merge {} (map (comp :externs second) (get @compiler-env ::ana/namespaces))) - (str (util/output-directory all-opts) "/inferred_externs.js"))) - optim (:optimizations all-opts) + (str (util/output-directory opts) "/inferred_externs.js"))) + optim (:optimizations opts) ret (if (and optim (not= optim :none)) (do - (when-let [fname (:source-map all-opts)] - (assert (or (nil? (:output-to all-opts)) (:modules opts) (string? fname)) + (when-let [fname (:source-map opts)] + (assert (or (nil? (:output-to opts)) (:modules opts) (string? fname)) (str ":source-map must name a file when using :whitespace, " ":simple, or :advanced optimizations with :output-to"))) - (if (:modules all-opts) + (if (:modules opts) (->> (util/measure compiler-stats (str "Optimizing " (count js-sources) " sources") - (apply optimize-modules all-opts js-sources)) - (output-modules all-opts js-sources)) - (let [fdeps-str (foreign-deps-str all-opts + (apply optimize-modules opts js-sources)) + (output-modules opts js-sources)) + (let [fdeps-str (foreign-deps-str opts (filter foreign-source? js-sources)) - all-opts (assoc all-opts + opts (assoc opts :foreign-deps-line-count (- (count (.split #"\r?\n" fdeps-str -1)) 1))] (->> (util/measure compiler-stats (str "Optimizing " (count js-sources) " sources") - (apply optimize all-opts + (apply optimize opts (remove foreign-source? js-sources))) - (add-wrapper all-opts) - (add-source-map-link all-opts) + (add-wrapper opts) + (add-source-map-link opts) (str fdeps-str) - (add-header all-opts) - (output-one-file all-opts))))) - (apply output-unoptimized all-opts js-sources))] + (add-header opts) + (output-one-file opts))))) + (apply output-unoptimized opts js-sources))] ;; emit Node.js bootstrap script for :none & :whitespace optimizations (when (and (= (:target opts) :nodejs) (not= (:optimizations opts) :whitespace)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 6d17feb2e..e54276edd 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -154,6 +154,7 @@ [{:file (str (io/file root "thirdparty" "add.js")) :provides ["thirdparty.add"]}] :output-dir (str out) + :main 'foreign-libs.core :target :nodejs}] (test/delete-out-files out) (build/build (build/inputs (io/file root "foreign_libs") (io/file root "thirdparty")) opts) @@ -416,6 +417,7 @@ (let [out (io/file (test/tmp-dir) "cljs-2249-out") root (io/file "src" "test" "cljs_build") opts {:output-dir (str out) + :main 'foreign-libs-cljs-2249.core :target :nodejs}] (test/delete-out-files out) (build/build (build/inputs (io/file root "foreign_libs_cljs_2249")) opts) From 9778b34d9e988a28c64133c4751d235bbbd3e966 Mon Sep 17 00:00:00 2001 From: Matthew Huebert Date: Sun, 1 Oct 2017 13:58:45 +0200 Subject: [PATCH 2716/4033] CLJS-1576: fix source-map string encoding by applying encodeURIComponent and fixing string/replace call --- src/main/cljs/cljs/js.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index f5b5cfe04..6b662957a 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -175,7 +175,10 @@ (.append sb (str "\n//# sourceURL=" file "\n//# sourceMappingURL=data:application/json;base64," - (base64/encodeString (string/replace json #"%([0-9A-F]{2})" (.fromCharCode js/String "0x$1"))))))) + (-> (js/encodeURIComponent json) + (string/replace #"%([0-9A-F]{2})" (fn [[_ match]] + (.fromCharCode js/String (str "0x" match)))) + (base64/encodeString)))))) (defn- current-alias-map [] From 9c2ec8b56a8516540b54e2a4fc6d942f9c05699c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 4 Aug 2017 22:16:09 -0400 Subject: [PATCH 2717/4033] CLJS-2300: Delegate clojure.string/capitalize to goog.string/capitalize --- benchmark/cljs/benchmark_runner.cljs | 8 +++++++- src/main/cljs/clojure/string.cljs | 5 +---- src/test/cljs/clojure/string_test.cljs | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index ad81ca810..65723cd8d 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -1,7 +1,8 @@ (ns cljs.benchmark-runner (:refer-clojure :exclude [println]) (:require [cljs.reader :as reader] - [clojure.core.reducers :as r])) + [clojure.core.reducers :as r] + [clojure.string :as string])) (def println print) @@ -408,6 +409,11 @@ (simple-benchmark [] (str "1" "2") 1000000) (simple-benchmark [] (str "1" "2" "3") 1000000) +(println "\n") +(println ";;; clojure.string") +(simple-benchmark [s "a" f clojure.string/capitalize] (f s) 1000000) +(simple-benchmark [s "aBcDeF" f clojure.string/capitalize] (f s) 1000000) + (println ";; printing of numbers and handling of ##Nan, ##Inf, ##-Inf") (simple-benchmark [x true] (pr-str x) 1000000) (simple-benchmark [x 10] (pr-str x) 1000000) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index a2433e056..334750e4c 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -101,10 +101,7 @@ "Converts first character of the string to upper-case, all other characters to lower-case." [s] - (if (< (count s) 2) - (upper-case s) - (str (upper-case (subs s 0 1)) - (lower-case (subs s 1))))) + (gstring/capitalize s)) ;; The JavaScript split function takes a limit argument but the return ;; value is not the same as the Java split function. diff --git a/src/test/cljs/clojure/string_test.cljs b/src/test/cljs/clojure/string_test.cljs index 615c0af19..5837f806b 100644 --- a/src/test/cljs/clojure/string_test.cljs +++ b/src/test/cljs/clojure/string_test.cljs @@ -9,6 +9,10 @@ (ns clojure.string-test (:require [cljs.test :as test :refer-macros [deftest is testing]] + [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])) (deftest test-api @@ -149,6 +153,22 @@ (is (s/includes? sb "Applied")) (is (not (s/includes? sb "Living")))))) +(defspec test-cljs-2300 + ;; The reference implementation is the implementation prior to the change. + ;; Since some JavaScript implementations fail to properly change case for + ;; some characters (for example, the upper case of "ß" is "SS"), we limit + ;; this test to strings comprising only printable ASCII characters. + (let [ref-impl (fn [s] + (if (< (count s) 2) + (s/upper-case s) + (str (s/upper-case (subs s 0 1)) + (s/lower-case (subs s 1))))) + char-codes->string (fn [xs] + (apply (.-fromCharCode js/String) xs))] + (prop/for-all [s (gen/fmap char-codes->string + (gen/not-empty (gen/vector (gen/choose 0x20 0x7E))))] + (= (ref-impl s) (s/capitalize s))))) + (comment (deftest char-sequence-handling From 85b882b728984734793d635c923bfab0f71ba00f Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 2 Oct 2017 14:25:37 -0400 Subject: [PATCH 2718/4033] 1.9.946 --- README.md | 6 +++--- changes.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6d7977ff3..95f81b491 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.9.908 +Latest stable release: 1.9.946 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.9.908"] +[org.clojure/clojurescript "1.9.946"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.9.908 org.clojure clojurescript - 1.9.908 + 1.9.946 ``` diff --git a/changes.md b/changes.md index f993ff660..2d559ec01 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,38 @@ +## 1.9.946 + +### Changes +* CLJS-2300: Delegate clojure.string/capitalize to goog.string/capitalize +* CLJS-2374: Print js/Infinity, js/-Infinity, js/NaN using new reader literals +* bump tools.reader (1.1.0) +* CLJS-2372: update hash to use the new infinity literals +* CLJS-2364: Bump Closure Compiler to the Sep 2017 version +* CLJS-2340: Have js-keys delegate directly to good.object/getKeys +* CLJS-2338: Support renamePrefix{Namespace} closure compiler option + +### Fixes +* CLJS-1576: fix source-map string encoding by applying encodeURIComponent and fixing string/replace call +* CLJS-2294: Always use opts with implicit opts added +* CLJS-2166: Add uri? predicate +* CLJS-2368: Self-host: Never compile macro namespaces with `:optimize-constants true +* CLJS-2367: Self-host: :def-emits-var leaks into loaded namespace processing +* CLJS-2352: Emit valid JS for NaN etc. even when used w/ CLJ >= 1.9.0-alpha20 +* CLJS-2339: Significant code reload slowdown with :npm-deps +* CLJS-2361: Self-host: circular dependency detection doesn't handle REPL self-require +* CLJS-2356: Self-host: circular dependency detection is not correct +* CLJS-2354: Self-host: `compile-str` doesn't handle `clojure` -> `cljs` aliasing +* CLJS-2353: use portable `node-module-dep?` function in analyze-deps +* CLJS-2345: escape paths emitted as args to cljs.core.load_file +* CLJS-2349: Port reset-vals! and swap-vals! over from Clojure +* CLJS-2336: Call alength once in areduce and amap +* CLJS-2335: Avoid alength on strings +* CLJS-2334: Also gather dependencies from foreign-libs that are modules +* CLJS-2333: module-deps.js doesn't correctly compute `main` if aliased in browser field +* CLJS-2332: module_deps.js doesn't process `export from` correctly +* CLJS-2330: Don't set `"browser"` field for Closure if target is :nodejs +* CLJS-2326: Indexing node_modules can't find `main` when it doesn't have an extension +* CLJS-2328: Args are not provided to *main-cli-fn* with optimizations advanced +* CLJS-2327: module_deps.js doesn't know about browser field advanced usage + ## 1.9.908 ### Enhancements From d4e19bffcd19208997c1ffb14e3077e62eedbdfc Mon Sep 17 00:00:00 2001 From: Jinseop Kim Date: Wed, 4 Oct 2017 23:30:23 +0900 Subject: [PATCH 2719/4033] CLJS-2378: keep the :npm-deps-installed? to avoid to reinstall NPM deps --- src/main/clojure/cljs/closure.clj | 5 +++-- src/main/clojure/cljs/repl.cljc | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c3d8d31ed..735e9b2e3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2523,8 +2523,9 @@ (check-npm-deps opts) (swap! compiler-env update-in [:npm-deps-installed?] (fn [installed?] - (when-not installed? - (maybe-install-node-deps! opts))))) + (if-not installed? + (maybe-install-node-deps! opts) + installed?)))) compiler-stats (:compiler-stats opts) checked-arrays (or (:checked-arrays opts) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index bd9035eb6..542a5ef7c 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -883,8 +883,9 @@ (cljsc/check-npm-deps opts) (swap! env/*compiler* update-in [:npm-deps-installed?] (fn [installed?] - (when-not installed? - (cljsc/maybe-install-node-deps! opts))))) + (if-not installed? + (cljsc/maybe-install-node-deps! opts) + installed?)))) (comp/with-core-cljs opts (fn [] (binding [*repl-opts* opts] From 7c78a35c55428ba690f2a8ab656054188f805c71 Mon Sep 17 00:00:00 2001 From: "oliver@DESKTOP-86841BV.localdomain" Date: Mon, 30 Oct 2017 14:15:49 +1100 Subject: [PATCH 2720/4033] CLJS-2391: Unable to :stub a function using stest/instrument Patch instrument-1 macro to correctly pass symbol to instrument-1*. Includes a test to verify correct stubbing behaviour. --- src/main/cljs/cljs/spec/test/alpha.cljc | 2 +- src/test/cljs/cljs/spec/test_test.cljs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index bcbae037c..89aaf35c4 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -37,7 +37,7 @@ (when-let [v (ana-api/resolve &env s)] (when (nil? (:const v)) (swap! instrumented-vars conj (:name v)) - `(let [checked# (instrument-1* ~s (var ~s) ~opts)] + `(let [checked# (instrument-1* '~s (var ~s) ~opts)] (when checked# (set! ~s checked#)) '~(:name v))))) diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 923f34286..4197ee899 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -23,3 +23,13 @@ (deftest test-cljs-2142 (is (= `[area] (stest/instrument `[pi area])))) + +(defn f-2391 [] 1) +(s/fdef f-2391 :args (s/cat) :ret #{2}) + +(deftest test-cljs-2391 + (is (= 1 (f-2391))) + (stest/instrument `f-2391 {:stub #{`f-2391}}) + (is (= 2 (f-2391))) + (stest/unstrument `f-2391) + (is (= 1 (f-2391)))) From 2389e52049a9bd001d173a1cb4772ed8a25de196 Mon Sep 17 00:00:00 2001 From: Hendrik Poernama Date: Thu, 2 Nov 2017 18:47:07 +0700 Subject: [PATCH 2721/4033] Fix CLJS-2394 --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 735e9b2e3..a686f8784 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2238,7 +2238,9 @@ (run! #(.append sb (str "require('" % "');\n")) modules) (str sb))] (util/mkdirs deps-file) - (if-not (= old-contents new-contents) + (if (or (not= old-contents new-contents) + (nil? env/*compiler*) + (nil? (::transitive-dep-set @env/*compiler*))) (do (spit deps-file new-contents) (let [transitive-js (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)] From e3a9f0cd8511dcfe0c80f37834b5aa32c7b058d4 Mon Sep 17 00:00:00 2001 From: Enzzo Cavallo Date: Tue, 11 Jul 2017 21:17:03 -0300 Subject: [PATCH 2722/4033] Allow clj->js to preserve namespaces --- src/main/cljs/cljs/core.cljs | 54 +++++++++++++++++-------------- src/test/cljs/cljs/core_test.cljs | 8 ++++- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5762cc0d2..7a98db19a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10243,36 +10243,40 @@ reduces them without incurring seq initialization" (declare clj->js) -(defn key->js [k] - (if (satisfies? IEncodeJS k) - (-clj->js k) - (if (or (string? k) - (number? k) - (keyword? k) - (symbol? k)) - (clj->js k) - (pr-str k)))) +(defn key->js + ([k] (key->js k clj->js)) + ([k primitive-fn] + (cond + (satisfies? IEncodeJS k) (-clj->js k) + (or (string? k) + (number? k) + (keyword? k) + (symbol? k)) (primitive-fn k) + :default (pr-str k)))) (defn clj->js "Recursively transforms ClojureScript values to JavaScript. sets/vectors/lists become Arrays, Keywords and Symbol become Strings, Maps become Objects. Arbitrary keys are encoded to by key->js." - [x] - (when-not (nil? x) - (if (satisfies? IEncodeJS x) - (-clj->js x) - (cond - (keyword? x) (name x) - (symbol? x) (str x) - (map? x) (let [m (js-obj)] - (doseq [[k v] x] - (gobject/set m (key->js k) (clj->js v))) - m) - (coll? x) (let [arr (array)] - (doseq [x (map clj->js x)] - (.push arr x)) - arr) - :else x)))) + [x & {:keys [keyword-fn] + :or {keyword-fn name}}] + (letfn [(keyfn [k] (key->js k thisfn)) + (thisfn [x] (cond + (nil? x) nil + (satisfies? IEncodeJS x) (-clj->js x) + (keyword? x) (keyword-fn x) + (symbol? x) (str x) + (map? x) (let [m (js-obj)] + (doseq [[k v] x] + (gobject/set m (keyfn k) (thisfn v))) + m) + (coll? x) (let [arr (array)] + (doseq [x (map thisfn x)] + (.push arr x)) + arr) + :else x))] + (thisfn x))) + (defprotocol IEncodeClojure (-js->clj [x options] "Transforms JavaScript values to Clojure")) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index cddfcd10d..9a223a364 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -153,7 +153,13 @@ (gobject/get "a") (gobject/get "b") (gobject/get "{:k :ey}")) - "d"))))) + "d"))) + (is (= (-> (clj->js {:foo/bar "a"}) + (gobject/get "bar")) + "a")) + (is (= (-> (clj->js {:foo/bar "a"} :keyword-fn namespace) + (gobject/get "foo")) + "a")))) (deftest test-delay (let [a (atom 0) From 1e7e01f92da0d3b724c9bb7225620565c67703f0 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 19 Nov 2017 09:42:37 -0500 Subject: [PATCH 2723/4033] CLJS-2404: Travis JavaScriptCore unit tests failing Break the tests into smaller pieces to avoid CLJS-910. --- src/test/cljs/cljs/spec/test_test.cljs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 4197ee899..fd294d49a 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -27,9 +27,13 @@ (defn f-2391 [] 1) (s/fdef f-2391 :args (s/cat) :ret #{2}) -(deftest test-cljs-2391 - (is (= 1 (f-2391))) +(deftest test-cljs-2391-a + (is (= 1 (f-2391)))) + +(deftest test-cljs-2391-b (stest/instrument `f-2391 {:stub #{`f-2391}}) - (is (= 2 (f-2391))) + (is (= 2 (f-2391)))) + +(deftest test-cljs-2391-c (stest/unstrument `f-2391) (is (= 1 (f-2391)))) From 544c1b77d76d48f234cdb03746ea993158c46aff Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 17 Nov 2017 10:14:28 -0500 Subject: [PATCH 2724/4033] CLJS-2377: The CLJS compiled uses deprecated modules on Java 9 --- src/main/clojure/cljs/repl.cljc | 9 +++++++-- src/main/clojure/cljs/util.cljc | 19 ++++++++++++++++--- src/test/clojure/cljs/repl_tests.clj | 3 +++ src/test/clojure/cljs/util_tests.clj | 7 +++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 542a5ef7c..bcf58f35d 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -28,7 +28,7 @@ [cljs.source-map :as sm]) (:import [java.io File PushbackReader FileWriter PrintWriter] [java.net URL] - [javax.xml.bind DatatypeConverter] + [java.util Base64] [clojure.lang IExceptionInfo] [java.util.regex Pattern] [com.google.common.base Throwables])) @@ -449,6 +449,11 @@ (println st))) (println st)))))) +(defn- bytes-to-base64-str + "Convert a byte array into a base-64 encoded string." + [^bytes bytes] + (.encodeToString (Base64/getEncoder) bytes)) + (defn evaluate-form "Evaluate a ClojureScript form in the JavaScript environment. Returns a string which is the ClojureScript return value. This string may or may @@ -488,7 +493,7 @@ (str js "\n//# sourceURL=repl-" t ".js" "\n//# sourceMappingURL=data:application/json;base64," - (DatatypeConverter/printBase64Binary + (bytes-to-base64-str (.getBytes (sm/encode {(str "repl-" t ".cljs") diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 393b11a2e..8b71a4682 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -14,8 +14,7 @@ [clojure.edn :as edn]) (:import [java.io File] [java.net URL URLDecoder] - [java.security MessageDigest] - [javax.xml.bind DatatypeConverter])) + [java.security MessageDigest])) ;; next line is auto-generated by the build-script - Do not edit! (def ^:dynamic *clojurescript-version*) @@ -297,6 +296,20 @@ xs seen)))] (step coll #{})))) +(def ^:private hex-digits (char-array "0123456789ABCDEF")) + +(defn- bytes-to-hex-str + "Convert an array of bytes into a hex encoded string." + [^bytes bytes] + (loop [index (int 0) + buffer (StringBuilder. (int (* 2 (alength bytes))))] + (if (== (alength bytes) index) + (.toString buffer) + (let [byte (aget bytes index)] + (.append buffer (aget ^chars hex-digits (bit-and (bit-shift-right byte 4) 0xF))) + (.append buffer (aget ^chars hex-digits (bit-and byte 0xF))) + (recur (inc index) buffer))))) + (defn content-sha ([^String s] (content-sha s nil)) @@ -304,7 +317,7 @@ (let [digest (MessageDigest/getInstance "SHA-1") _ (.reset digest) _ (.update digest (.getBytes s "utf8")) - sha (DatatypeConverter/printHexBinary (.digest digest))] + sha (bytes-to-hex-str (.digest digest))] (if-not (nil? n) (apply str (take n sha)) sha)))) diff --git a/src/test/clojure/cljs/repl_tests.clj b/src/test/clojure/cljs/repl_tests.clj index d3577b4e6..dda28e38d 100644 --- a/src/test/clojure/cljs/repl_tests.clj +++ b/src/test/clojure/cljs/repl_tests.clj @@ -41,3 +41,6 @@ (is file) (and file (is (io/resource file)))))) + +(deftest test-bytes-to-base64-str + (is (= "YWJj" (#'repl/bytes-to-base64-str (.getBytes "abc"))))) diff --git a/src/test/clojure/cljs/util_tests.clj b/src/test/clojure/cljs/util_tests.clj index ee5b33f64..bbc0d768a 100644 --- a/src/test/clojure/cljs/util_tests.clj +++ b/src/test/clojure/cljs/util_tests.clj @@ -45,3 +45,10 @@ (deftest test-path (is (= (.getAbsolutePath (io/file "src/main/clojure/cljs/closure.clj")) (util/path (io/as-url (io/file "src/main/clojure/cljs/closure.clj")))))) + +(deftest test-bytes-to-hex-str + (is (= "09616263" (#'util/bytes-to-hex-str (.getBytes "\u0009abc"))))) + +(deftest test-content-sha + (is (= "40BD001563085FC35165329EA1FF5C5ECBDBBEEF" (util/content-sha "123"))) + (is (= "40BD0" (util/content-sha "123" 5)))) From f7f187754d9953fd10f2872cf9117fce36c83890 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 29 Nov 2017 09:40:46 -0500 Subject: [PATCH 2725/4033] CLJS-2425: Remove unnecessary zero? checks from nat-int? --- src/main/cljs/cljs/core.cljs | 6 +++--- src/test/cljs/cljs/predicates_test.cljs | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7a98db19a..e6300f13f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2274,13 +2274,13 @@ reduces them without incurring seq initialization" [x] (cond (integer? x) - (or (not (neg? x)) (zero? x)) + (not (neg? x)) (instance? goog.math.Integer x) - (or (not (.isNegative x)) (.isZero x)) + (not (.isNegative x)) (instance? goog.math.Long x) - (or (not (.isNegative x)) (.isZero x)) + (not (.isNegative x)) :else false)) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 7de17bacc..0759888c5 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -40,6 +40,7 @@ (def int-val-table (let [posint 10e10 negint -10e10 + neg0 (/ ##-Inf) natl (Long.getZero) posl (Long.fromNumber posint) negl (Long.fromNumber negint) @@ -48,6 +49,7 @@ negi (Integer.fromNumber negint)] [[identity neg? pos? integer? int? neg-int? pos-int? nat-int?] [0 false false true true false false true ] + [neg0 false false true true false false true ] [1 false true true true false true true ] [-1 true false true true true false false ] [1.0 false true true true false true true ] From 76efc272b0e3142e93fc27c423174cb34c3aa904 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 26 Nov 2017 12:13:28 -0500 Subject: [PATCH 2726/4033] CLJS-2420: Make dir work on aliases --- src/main/clojure/cljs/repl.cljc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index bcf58f35d..6c649fb97 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1269,10 +1269,18 @@ str-or-pattern." (filter matches? (named-publics-vars ns))))) (ana-api/all-ns)))))) +(defn- resolve-ns + "Resolves a namespace symbol to a namespace by first checking to see if it + is a namespace alias." + [ns-sym] + (or (get-in @env/*compiler* [::ana/namespaces ana/*cljs-ns* :requires ns-sym]) + (get-in @env/*compiler* [::ana/namespaces ana/*cljs-ns* :require-macros ns-sym]) + ns-sym)) + (defmacro dir "Prints a sorted directory of public vars in a namespace" [ns] - `(doseq [sym# (quote ~(sort (named-publics-vars ns)))] + `(doseq [sym# (quote ~(sort (named-publics-vars (resolve-ns ns))))] (println sym#))) (defmacro pst From 11265fdf19e7bd33001fd9698a0f86aa46ef9c95 Mon Sep 17 00:00:00 2001 From: puredanger Date: Mon, 27 Nov 2017 14:08:12 -0600 Subject: [PATCH 2727/4033] use clojure release jar directly --- script/bootstrap | 10 +++------- script/bootstrap.ps1 | 8 ++------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index a1e78589f..145d84b90 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -20,15 +20,11 @@ unzip -v >/dev/null || { echo "The 'unzip' utility is missing, or not on your sy mkdir -p lib echo "Fetching Clojure..." -curl -O -s https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.zip || { echo "Download failed."; exit 1; } -unzip -qu clojure-$CLOJURE_RELEASE.zip +curl -O -s https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar || { echo "Download failed."; exit 1; } echo "Copying clojure-$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar to lib/clojure.jar..." cp clojure-$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar lib/clojure.jar - -echo "Cleaning up Clojure directory..." -rm -rf clojure-$CLOJURE_RELEASE/ -echo "Cleaning up Clojure archive..." -rm clojure-$CLOJURE_RELEASE.zip +echo "Cleaning up Clojure jar..." +rm clojure-$CLOJURE_RELEASE.jar echo "Fetching specs.alpha...." curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/spec.alpha/$SPEC_ALPHA_RELEASE/spec.alpha-$SPEC_ALPHA_RELEASE.jar || { echo "Download failed."; exit 1; } diff --git a/script/bootstrap.ps1 b/script/bootstrap.ps1 index 7edd241f1..3c099bb8f 100644 --- a/script/bootstrap.ps1 +++ b/script/bootstrap.ps1 @@ -90,12 +90,8 @@ Make-Dir $root\closure\compiler Write-Host "Fetching Clojure..." Get-WebResource ` - https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.zip ` - $root\clojure-$CLOJURE_RELEASE.zip -Delete-File $root\lib\clojure-$CLOJURE_RELEASE.jar -Expand-ZipFile $root\clojure-$CLOJURE_RELEASE.zip $root\lib clojure-$CLOJURE_RELEASE\clojure-$CLOJURE_RELEASE.jar -Move-File $root\lib\clojure-$CLOJURE_RELEASE.jar $root\lib\clojure.jar -Delete-File $root\clojure-$CLOJURE_RELEASE.zip + https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar ` + $root\lib\clojure-$CLOJURE_RELEASE.jar Write-Host "Fetching specs.alpha...." Get-WebResource ` From 32dfa11ab2698d7a60ae294b51d1441546866c94 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 25 Nov 2017 08:26:56 -0500 Subject: [PATCH 2728/4033] CLJS-2418: cljs.analyzer/specials and cljs.core/special-symbol? out of sync --- src/main/cljs/cljs/core.cljs | 3 ++- src/main/clojure/cljs/analyzer.cljc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e6300f13f..95a99eda4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10908,9 +10908,10 @@ reduces them without incurring seq initialization" (defn ^boolean special-symbol? "Returns true if x names a special form" [x] + ;; If adding new special symbols, be sure to update cljs.analyzer/specials (contains? '#{if def fn* do let* loop* letfn* throw try catch finally - recur new set! ns deftype* defrecord* . js* & quote var ns*} + recur new set! ns deftype* defrecord* . js* & quote case* var ns*} x)) (defn test diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ca29615a1..977db88e5 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1167,7 +1167,8 @@ (declare analyze analyze-symbol analyze-seq) -(def specials '#{if def fn* do let* loop* letfn* throw try recur new set! +;; If adding new specials, be sure to update cljs.core/special-symbol? +(def specials '#{if def fn* do let* loop* letfn* throw try catch finally recur new set! ns deftype* defrecord* . js* & quote case* var ns*}) (def ^:dynamic *recur-frames* nil) From 0891643da6edf0be85213c9f719b9586fe2ea336 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 24 Nov 2017 18:27:57 -0500 Subject: [PATCH 2729/4033] CLJS-2416: Self-host: defn macro Var doesn't have :macro true meta --- src/main/clojure/cljs/core.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 662467bbd..06aea266a 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3136,7 +3136,8 @@ to the var metadata. prepost-map defines a map with optional keys :pre and :post that contain collections of pre or post conditions." :arglists '([name doc-string? attr-map? [params*] prepost-map? body] - [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])} + [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?]) + :macro true} defn (core/fn defn [&form &env name & fdecl] ;; Note: Cannot delegate this check to def because of the call to (with-meta name ..) (if (core/instance? #?(:clj clojure.lang.Symbol :cljs Symbol) name) From 13d39005e399e4d0e3d7644c52910cd1068bdb85 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 24 Nov 2017 15:21:38 -0500 Subject: [PATCH 2730/4033] CLJS-2415: Self-host: Enable tests with instrument and cljs.spec.test-test --- src/test/self/self_parity/test.cljs | 238 ++++++++++++++++++---------- 1 file changed, 155 insertions(+), 83 deletions(-) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 9677ece68..cf50b221f 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -16,6 +16,7 @@ ClojureScript compiler, and also running the resulting tests."} self-parity.test (:require [clojure.string :as string] + [cljs.compiler :as comp] [cljs.nodejs :as nodejs] [cljs.js :as cljs] [cljs.tools.reader :as reader] @@ -221,6 +222,8 @@ ;; Facilities for driving cljs.js +(def st (cljs/empty-state)) + (def load-fn (make-load-fn src-paths node-read-file)) (defn eval-form @@ -237,7 +240,7 @@ :verbose false} cb)) -;; Test suite runner +;; Error handler (defn- handle-error [error sms] @@ -255,97 +258,166 @@ (print "caused by: ") (recur cause))))) +;; The following volatiles and fns set up a scheme to +;; emit function values into JavaScript as numeric +;; references that are looked up. Needed to implement eval. + +(defonce ^:private fn-index (volatile! 0)) +(defonce ^:private fn-refs (volatile! {})) + +(defn- put-fn + "Saves a function, returning a numeric representation." + [f] + (let [n (vswap! fn-index inc)] + (vswap! fn-refs assoc n f) + n)) + +(defn- get-fn + "Gets a function, given its numeric representation." + [n] + (get @fn-refs n)) + +(defn- emit-fn [f] + (print "self_parity.test.get_fn(" (put-fn f) ")")) + +(defmethod comp/emit-constant js/Function + [f] + (emit-fn f)) + +(defmethod comp/emit-constant cljs.core/Var + [f] + (emit-fn f)) + +;; Inject an implementation of eval into needed macros namespaces + +(defn- eval + ([form] + (eval form (.-name *ns*))) + ([form ns] + (let [result (atom nil)] + (cljs/eval st form + {:ns ns + :context :expr + :def-emits-var true} + (fn [{:keys [value error]}] + (if error + (handle-error error (:source-maps @st)) + (reset! result value)))) + @result))) + +(defn- intern + ([ns name] + (when-let [the-ns (find-ns (cond-> ns (instance? Namespace ns) ns-name))] + (eval `(def ~name) (ns-name the-ns)))) + ([ns name val] + (when-let [the-ns (find-ns (cond-> ns (instance? Namespace ns) ns-name))] + (eval `(def ~name ~val) (ns-name the-ns))))) + +(defn- inject-eval + [target-ns] + (intern target-ns 'eval eval)) + +(defn- setup-eval [] + (eval-form st 'cljs.user + '(require-macros 'cljs.spec.test.alpha) + (fn [{:keys [value error]}] + (if error + (handle-error error (:source-maps @st)) + (inject-eval 'cljs.spec.test.alpha$macros))))) + +;; Test suite runner + (defn run-tests "Runs the tests." [] ;; Ideally we'd just load test_runner.cljs, but a few namespace tests ;; don't yet run in bootstrapped ClojureScript. These are commented ;; out below and can be uncommented as fixed. - (let [st (cljs/empty-state)] - (eval-form st 'cljs.user - '(ns parity.core - (:require [cljs.test :refer-macros [run-tests]] - [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 :as core-test] - [cljs.reader-test] - [cljs.binding-test] - #_[cljs.ns-test] - [clojure.string-test] - [clojure.data-test] - [clojure.walk-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.pprint] - [cljs.pprint-test] - [cljs.spec-test] - #_[cljs.spec.test-test] - [cljs.clojure-alias-test] - [cljs.hash-map-test] - [cljs.map-entry-test] - [cljs.syntax-quote-test] - [cljs.predicates-test] - [cljs.test-test] - [static.core-test] - [cljs.recur-test] - [cljs.array-access-test])) - (fn [{:keys [value error]}] - (if error - (handle-error error (:source-maps @st)) - (eval-form st 'parity.core - '(run-tests - '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.reader-test - 'clojure.string-test - 'clojure.data-test - 'clojure.walk-test - 'cljs.letfn-test - 'cljs.reducers-test - 'cljs.binding-test - 'cljs.macro-test - 'cljs.top-level - 'cljs.keyword-test - #_'cljs.ns-test - 'cljs.ns-test.foo - 'foo.ns-shadow-test - 'cljs.import-test - 'cljs.pprint - 'cljs.pprint-test - 'cljs.spec-test - #_'cljs.spec.test-test - 'cljs.clojure-alias-test - 'cljs.hash-map-test - 'cljs.map-entry-test - 'cljs.syntax-quote-test - 'cljs.predicates-test - 'cljs.test-test - 'static.core-test - 'cljs.recur-test - 'cljs.array-access-test) - (fn [{:keys [value error]}] - (when error - (handle-error error (:source-maps @st)))))))))) + (eval-form st 'cljs.user + '(ns parity.core + (:require [cljs.test :refer-macros [run-tests]] + [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 :as core-test] + [cljs.reader-test] + [cljs.binding-test] + #_[cljs.ns-test] + [clojure.string-test] + [clojure.data-test] + [clojure.walk-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.pprint] + [cljs.pprint-test] + [cljs.spec-test] + [cljs.spec.test-test] + [cljs.clojure-alias-test] + [cljs.hash-map-test] + [cljs.map-entry-test] + [cljs.syntax-quote-test] + [cljs.predicates-test] + [cljs.test-test] + [static.core-test] + [cljs.recur-test] + [cljs.array-access-test])) + (fn [{:keys [value error]}] + (if error + (handle-error error (:source-maps @st)) + (eval-form st 'parity.core + '(run-tests + '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.reader-test + 'clojure.string-test + 'clojure.data-test + 'clojure.walk-test + 'cljs.letfn-test + 'cljs.reducers-test + 'cljs.binding-test + 'cljs.macro-test + 'cljs.top-level + 'cljs.keyword-test + #_'cljs.ns-test + 'cljs.ns-test.foo + 'foo.ns-shadow-test + 'cljs.import-test + 'cljs.pprint + 'cljs.pprint-test + 'cljs.spec-test + 'cljs.spec.test-test + 'cljs.clojure-alias-test + 'cljs.hash-map-test + 'cljs.map-entry-test + 'cljs.syntax-quote-test + 'cljs.predicates-test + 'cljs.test-test + 'static.core-test + 'cljs.recur-test + 'cljs.array-access-test) + (fn [{:keys [value error]}] + (when error + (handle-error error (:source-maps @st))))))))) (defn -main [& args] (init-runtime) + (setup-eval) (run-tests)) (set! *main-cli-fn* -main) From 1f9bfe15f4e21bb91c16e89ee380c3f92efb14d8 Mon Sep 17 00:00:00 2001 From: Levi Tan Ong Date: Tue, 28 Nov 2017 06:01:16 +0800 Subject: [PATCH 2731/4033] CLJS-2423: Allow custom :output-wrapper function Expands support from 2 types to 4 types: - function - string (`format` compatible string interpolation) - truthy (that is of course, neither function nor string) - falsey Previously, any truthy value would cause the javascript to be wrapped by some default stringified function. For most simple applications, this would have sufficed. In cases where the `this` context in the wrapper function is used, something more expressive is needed to be able to assign this context. The string and function support aims to fill this gap. --- src/main/clojure/cljs/closure.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a686f8784..bd1f60b1d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1869,8 +1869,11 @@ (defn add-wrapper [{:keys [output-wrapper] :as opts} js] (if output-wrapper - (str ";(function(){\n" js "\n})();\n") - js)) + (cond + (fn? output-wrapper) (output-wrapper js) + (string? output-wrapper) (format output-wrapper js) + :else (str ";(function(){\n" js "\n})();\n")) + js)) (defn add-source-map-link [{:keys [source-map output-to] :as opts} js] (if source-map From b65563956e168a6a93acfe2e2482f6a42ca108fa Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 20 Nov 2017 12:23:26 -0500 Subject: [PATCH 2732/4033] CLJS-2405: Register dumped specs fails --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 977db88e5..3fbe83a63 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3905,7 +3905,7 @@ (let [{:keys [registry-ref speced-vars]} (get-spec-vars)] (when-let [registry (seq (:cljs.spec/registry-ref cached-ns))] (when registry-ref - (swap! @registry-ref merge registry))) + (swap! @registry-ref into registry))) (when-let [vars (seq (:cljs.spec/speced-vars cached-ns))] (when speced-vars (swap! @speced-vars into vars))))) From 9bcf0ff15247b37561d49e310cfb162bc0fc3156 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 20 Nov 2017 10:17:28 -0500 Subject: [PATCH 2733/4033] CLJS-2387: CLJS Analyzer does not correctly detect cache hits for analyzed spec files --- src/main/clojure/cljs/analyzer.cljc | 9 ++++++++- src/main/clojure/cljs/compiler.cljc | 1 + src/test/cljs_build/analyzer_test/no_defs.cljs | 1 + src/test/clojure/cljs/analyzer_tests.clj | 6 ++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs_build/analyzer_test/no_defs.cljs diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3fbe83a63..cfb91f7c1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2118,7 +2118,7 @@ (conj (-> *cljs-dep-set* meta :dep-path) (some *cljs-dep-set* deps)))))) (doseq [dep deps] - (when-not (or (not-empty (get-in compiler [::namespaces dep :defs])) + (when-not (or (some? (get-in compiler [::namespaces dep :defs])) (contains? (:js-dependency-index compiler) (name dep)) (node-module-dep? dep) (js-module-exists? (name dep)) @@ -3984,6 +3984,12 @@ (recur ns (next forms)))) ns)))))) +(defn ensure-defs + "Ensures that a non-nil defs map exists in the compiler state for a given + ns. (A non-nil defs map signifies that the namespace has been analyzed.)" + [ns] + (swap! env/*compiler* update-in [::namespaces ns :defs] #(or % {}))) + #?(:clj (defn analyze-file "Given a java.io.File, java.net.URL or a string identifying a resource on the @@ -4043,6 +4049,7 @@ :else (recur ns (next forms)))) ns)))] + (ensure-defs ns) (when (and cache (true? (:cache-analysis opts))) (write-analysis-cache ns cache res)))) (try diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a51fe40d5..b89e2ee5a 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1423,6 +1423,7 @@ (merge opts {:ext ext :provides [ns-name]}))) (let [path (.getPath (.toURL ^File dest))] (swap! env/*compiler* assoc-in [::compiled-cljs path] ret)) + (ana/ensure-defs ns-name) (let [{:keys [output-dir cache-analysis]} opts] (when (and (true? cache-analysis) output-dir) (ana/write-analysis-cache ns-name diff --git a/src/test/cljs_build/analyzer_test/no_defs.cljs b/src/test/cljs_build/analyzer_test/no_defs.cljs new file mode 100644 index 000000000..44a9220bd --- /dev/null +++ b/src/test/cljs_build/analyzer_test/no_defs.cljs @@ -0,0 +1 @@ +(ns analyzer-test.no-defs) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 82846339a..0e9167a09 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -811,6 +811,12 @@ (e/with-compiler-env test-cenv (a/analyze test-env '(resolve foo.core))))))) +(deftest test-cljs-2387 + (a/no-warn + (e/with-compiler-env test-cenv + (a/analyze-file (io/file "src/test/cljs_build/analyzer_test/no_defs.cljs")))) + (is (= {} (get-in @test-cenv [::a/namespaces 'analyzer-test.no-defs :defs])))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From 566be7ce88145bcd612ecede31d090aa0ca4fe0a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 1 Dec 2017 19:37:38 -0500 Subject: [PATCH 2734/4033] CLJS-2426: script/bootstrap fails --- script/bootstrap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index 145d84b90..347ddb9fb 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -21,8 +21,8 @@ mkdir -p lib echo "Fetching Clojure..." curl -O -s https://repo1.maven.org/maven2/org/clojure/clojure/$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar || { echo "Download failed."; exit 1; } -echo "Copying clojure-$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar to lib/clojure.jar..." -cp clojure-$CLOJURE_RELEASE/clojure-$CLOJURE_RELEASE.jar lib/clojure.jar +echo "Copying clojure-$CLOJURE_RELEASE.jar to lib/clojure.jar..." +cp clojure-$CLOJURE_RELEASE.jar lib/clojure.jar echo "Cleaning up Clojure jar..." rm clojure-$CLOJURE_RELEASE.jar From f55b19b89e98a210a89151f52e67567108c536cf Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 24 Nov 2017 16:03:58 -0500 Subject: [PATCH 2735/4033] CLJS-2414: Self-host: Macro specs are instrumented --- src/main/cljs/cljs/spec/test/alpha.cljc | 3 ++- src/test/cljs/cljs/spec/test/test_macros.cljc | 10 ++++++++++ src/test/cljs/cljs/spec/test_test.cljs | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs/cljs/spec/test/test_macros.cljc diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 89aaf35c4..a3028129f 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -35,7 +35,8 @@ (defmacro instrument-1 [[quote s] opts] (when-let [v (ana-api/resolve &env s)] - (when (nil? (:const v)) + (when (and (nil? (:const v)) + #?(:cljs (nil? (:macro v)))) (swap! instrumented-vars conj (:name v)) `(let [checked# (instrument-1* '~s (var ~s) ~opts)] (when checked# (set! ~s checked#)) diff --git a/src/test/cljs/cljs/spec/test/test_macros.cljc b/src/test/cljs/cljs/spec/test/test_macros.cljc new file mode 100644 index 000000000..4a26ace6a --- /dev/null +++ b/src/test/cljs/cljs/spec/test/test_macros.cljc @@ -0,0 +1,10 @@ +(ns cljs.spec.test.test-macros + #?(:cljs (:require [cljs.spec.alpha :as s]))) + +(defmacro add + [a b] + `(+ ~a ~b)) + +#?(:cljs + (s/fdef add + :args (s/cat :a number? :b number?))) diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index fd294d49a..efdb1538d 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -1,4 +1,5 @@ (ns cljs.spec.test-test + (:require-macros [cljs.spec.test.test-macros]) (:require [cljs.test :as test :refer-macros [deftest is are run-tests]] [cljs.spec.alpha :as s] [cljs.spec.test.alpha :as stest])) @@ -37,3 +38,6 @@ (deftest test-cljs-2391-c (stest/unstrument `f-2391) (is (= 1 (f-2391)))) + +(deftest test-cljs-2414 + (is (empty? (stest/instrument 'cljs.spec.test.test-macros$macros/add)))) From d98c00ff4607b80d78787f7044346bd24b8fd6b5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 5 Dec 2017 13:42:14 -0500 Subject: [PATCH 2736/4033] --debug -> --inspect for Node.js debugger REPL port, deprecation warning was breaking REPL start --- src/main/clojure/cljs/repl/node.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index bb6a5ffc3..e3eb795e2 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -99,7 +99,7 @@ (defn- build-process [opts repl-env input-src] (let [xs (cond-> [(get opts :node-command "node")] - (:debug-port repl-env) (conj (str "--debug=" (:debug-port repl-env)))) + (:debug-port repl-env) (conj (str "--inspect=" (:debug-port repl-env)))) proc (-> (ProcessBuilder. (into-array xs)) (.redirectInput input-src))] (when-let [path-fs (:path repl-env)] (.put (.environment proc) From 53070a2c5392e8bdc00bb81c45034268e1cd81f2 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Mon, 4 Dec 2017 14:15:26 +0200 Subject: [PATCH 2737/4033] CLJS-2430: Fix foreign-libs with Node target Foreign libraries need to be loaded using nodeGlobalRequire instead of Node require. This will emit opt_loadFlag for foreign lib deps to cljs_deps.js and check that in CLOSURE_IMPORT_SCRIPT to select Node require or nodeGlobalRequire. --- src/main/cljs/cljs/bootstrap_node.js | 7 ++++++- src/main/clojure/cljs/closure.clj | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_node.js index 66a19f2dd..5c262a314 100644 --- a/src/main/cljs/cljs/bootstrap_node.js +++ b/src/main/cljs/cljs/bootstrap_node.js @@ -73,7 +73,12 @@ global.CLOSURE_IMPORT_SCRIPT = function(src, opt_sourceText) { // Sources are always expressed relative to closure's base.js, but // require() is always relative to the current source. if (opt_sourceText === undefined) { - require(path.join(".", "..", src)); + var flags = goog.dependencies_.loadFlags[src]; + if (flags && flags["foreign-lib"]) { + nodeGlobalRequire(path.resolve(__dirname, "..", src)); + } else { + require(path.join(".", "..", src)); + } } else { eval(opt_sourceText); } diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index bd1f60b1d..d4caff0a8 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1446,7 +1446,9 @@ ;; under Node.js we emit native `require`s for these (= :nodejs (:target opts)) (filter (complement ana/node-module-dep?)))) - "]);\n"))) + "]" + (if (deps/-foreign? input) ", {'foreign-lib': true}") + ");\n"))) (defn deps-file "Return a deps file string for a sequence of inputs." From 6362dd09e124f2445ada266915e510311a3924c9 Mon Sep 17 00:00:00 2001 From: Paulus Esterhazy Date: Wed, 22 Nov 2017 15:44:16 +0100 Subject: [PATCH 2738/4033] Avoid js/global --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 95a99eda4..823bb47d9 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11117,7 +11117,7 @@ reduces them without incurring seq initialization" (catch js/ReferenceError e nil)) (next segs)) - (find-ns-obj* js/global segs)) + (find-ns-obj* goog/global segs)) "default" (find-ns-obj* goog/global segs) (throw (js/Error. (str "find-ns-obj not supported for target " *target*)))))) From 49f98a2cb03bc9f6d74b91226a0d14cb8dbd63ba Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 8 Dec 2017 09:30:05 -0500 Subject: [PATCH 2739/4033] CLJS-2437: Update docstrings for str/replace and str/replace-first from Clojure --- src/main/cljs/clojure/string.cljs | 33 +++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 334750e4c..21e46fa08 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -43,10 +43,24 @@ (defn replace "Replaces all instance of match with replacement in s. + match/replacement can be: string / string - pattern / (string or function of match)." + pattern / (string or function of match). + + See also replace-first. + + The replacement is literal (i.e. none of its characters are treated + specially) for all cases above except pattern / string. + + For pattern / string, $1, $2, etc. in the replacement string are + substituted with the string that matched the corresponding + parenthesized group in the pattern. + + Example: + (clojure.string/replace \"Almost Pig Latin\" #\"\\b(\\w)(\\w+)\\b\" \"$2$1ay\") + -> \"lmostAay igPay atinLay\"" [s match replacement] (cond (string? match) @@ -61,10 +75,25 @@ (defn replace-first "Replaces the first instance of match with replacement in s. + match/replacement can be: string / string - pattern / (string or function of match)." + pattern / (string or function of match). + + See also replace. + + The replacement is literal (i.e. none of its characters are treated + specially) for all cases above except pattern / string. + + For pattern / string, $1, $2, etc. in the replacement string are + substituted with the string that matched the corresponding + parenthesized group in the pattern. + + Example: + (clojure.string/replace-first \"swap first two words\" + #\"(\\w+)(\\s+)(\\w+)\" \"$3$2$1\") + -> \"first swap two words\"" [s match replacement] (.replace s match replacement)) From ebdaf6c06c1112a67ba5a12498801c6d858e5a0a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 21 Nov 2017 09:33:20 -0500 Subject: [PATCH 2740/4033] CLJS-2407: Add docstrings for map and positional factory functions --- src/main/clojure/cljs/core.cljc | 5 ++++- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 06aea266a..39ecfd86a 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1676,8 +1676,10 @@ [rsym rname fields] (core/let [fn-name (with-meta (symbol (core/str '-> rsym)) (assoc (meta rsym) :factory :positional)) + docstring (core/str "Positional factory function for " rname ".") field-values (if (core/-> rsym meta :internal-ctor) (conj fields nil nil nil) fields)] `(defn ~fn-name + ~docstring [~@fields] (new ~rname ~@field-values)))) @@ -1859,10 +1861,11 @@ (core/defn- build-map-factory [rsym rname fields] (core/let [fn-name (with-meta (symbol (core/str 'map-> rsym)) (assoc (meta rsym) :factory :map)) + docstring (core/str "Factory function for " rname ", taking a map of keywords to field values.") ms (gensym) ks (map keyword fields) getters (map (core/fn [k] `(~k ~ms)) ks)] - `(defn ~fn-name [~ms] + `(defn ~fn-name ~docstring [~ms] (new ~rname ~@getters nil (not-empty (dissoc ~ms ~@ks)) nil)))) (core/defmacro defrecord diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 9a223a364..0a9bbcc3b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1483,6 +1483,14 @@ (is (= "##Inf" (pr-str js/Infinity))) (is (= "##-Inf" (pr-str js/-Infinity)))) +(deftype Foo2407 [x y]) +(defrecord Bar2407 [x y]) + +(deftest test-cljs-2407 + (is (= "Positional factory function for cljs.core-test/Foo2407." (:doc (meta #'->Foo2407)))) + (is (= "Positional factory function for cljs.core-test/Bar2407." (:doc (meta #'->Bar2407)))) + (is (= "Factory function for cljs.core-test/Bar2407, taking a map of keywords to field values." (:doc (meta #'map->Bar2407))))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 9ec796d791b1b2bd613af2f62cdecfd25caa6482 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sun, 29 Oct 2017 00:29:17 +0200 Subject: [PATCH 2741/4033] CLJS-1439: goog-define should supply :tag for defined symbol --- src/main/clojure/cljs/core.cljc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 39ecfd86a..1d82f99a8 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -752,7 +752,12 @@ (core/number? default) "number" (core/or (core/true? default) (core/false? default)) "boolean")] `(do - (declare ~(symbol sym)) + (declare ~(core/vary-meta sym + (fn [m] + (core/cond-> m + (core/not (core/contains? m :tag)) + (core/assoc :tag (core/symbol type)) + )))) (~'js* ~(core/str "/** @define {" type "} */")) (goog/define ~defname ~default)))) From 2448716b4537977f159c02138a0adb8c04e93b9c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 21 Dec 2017 16:44:33 -0500 Subject: [PATCH 2742/4033] CLJS-2449: Can't bind catch --- src/main/cljs/cljs/core.cljs | 1 - src/main/clojure/cljs/analyzer.cljc | 5 +++-- src/test/cljs/cljs/core_test.cljs | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 823bb47d9..8659eb2d2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10908,7 +10908,6 @@ reduces them without incurring seq initialization" (defn ^boolean special-symbol? "Returns true if x names a special form" [x] - ;; If adding new special symbols, be sure to update cljs.analyzer/specials (contains? '#{if def fn* do let* loop* letfn* throw try catch finally recur new set! ns deftype* defrecord* . js* & quote case* var ns*} diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cfb91f7c1..61a37f9a5 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1167,8 +1167,9 @@ (declare analyze analyze-symbol analyze-seq) -;; If adding new specials, be sure to update cljs.core/special-symbol? -(def specials '#{if def fn* do let* loop* letfn* throw try catch finally recur new set! +;; Note: This is the set of parse multimethod dispatch values, +;; along with '&, and differs from cljs.core/special-symbol? +(def specials '#{if def fn* do let* loop* letfn* throw try recur new set! ns deftype* defrecord* . js* & quote case* var ns*}) (def ^:dynamic *recur-frames* nil) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 0a9bbcc3b..a929f6eaf 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1483,6 +1483,10 @@ (is (= "##Inf" (pr-str js/Infinity))) (is (= "##-Inf" (pr-str js/-Infinity)))) +(deftest test-cljs-2449 + (is (= 1 (let [catch identity] (catch 1)))) + (is (= 1 (let [finally identity] (finally 1))))) + (deftype Foo2407 [x y]) (defrecord Bar2407 [x y]) From 457faf89757b249345fa37bb33961dde6de9a7c6 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 22 Dec 2017 08:17:13 -0500 Subject: [PATCH 2743/4033] CLJS-2445: Reducible sequence generators --- benchmark/cljs/benchmark_runner.cljs | 29 ++- src/main/cljs/cljs/core.cljs | 239 ++++++++++++++++++++++- src/test/cljs/cljs/collections_test.cljs | 223 ++++++++++++++++++++- 3 files changed, 480 insertions(+), 11 deletions(-) diff --git a/benchmark/cljs/benchmark_runner.cljs b/benchmark/cljs/benchmark_runner.cljs index 65723cd8d..a4ea583c9 100644 --- a/benchmark/cljs/benchmark_runner.cljs +++ b/benchmark/cljs/benchmark_runner.cljs @@ -421,4 +421,31 @@ (simple-benchmark [x js/Infinity] (pr-str x) 1000000) (simple-benchmark [x js/-Infinity] (pr-str x) 1000000) (simple-benchmark [x (js-obj)] (pr-str x) 1000000) -(println) \ No newline at end of file + +(println "\n") +(println ";; cycle") +(simple-benchmark [] (doall (take 1000 (cycle [1 2 3]))) 1000) +(simple-benchmark [] (into [] (take 1000) (cycle [1 2 3])) 1000) +(simple-benchmark [] (reduce + (take 64 (cycle [1 2 3]))) 10000) +(simple-benchmark [] (transduce (take 64) + (cycle [1 2 3])) 10000) + +(println "\n") +(println ";; repeat") +(simple-benchmark [] (doall (take 1000 (repeat 1))) 1000) +(simple-benchmark [] (into [] (take 1000) (repeat 1)) 1000) +(simple-benchmark [] (doall (repeat 1000 1)) 1000) +(simple-benchmark [] (into [] (repeat 1000 1)) 1000) +(simple-benchmark [] (reduce + 0 (repeat 1000 1)) 1000) +(simple-benchmark [] (into [] (take 1000) (repeat 1)) 1000) +(simple-benchmark [] (reduce + (take 64 (repeat 1))) 10000) +(simple-benchmark [] (transduce (take 64) + (repeat 1)) 10000) +(simple-benchmark [] (reduce + (take 64 (repeat 48 1))) 10000) +(simple-benchmark [] (transduce (take 64) + (repeat 48 1)) 10000) + +(println "\n") +(println ";; iterate") +(simple-benchmark [] (doall (take 1000 (iterate inc 0))) 1000) +(simple-benchmark [] (into [] (take 1000) (iterate inc 0)) 1000) +(simple-benchmark [] (reduce + (take 64 (iterate inc 0))) 10000) +(simple-benchmark [] (transduce (take 64) + (iterate inc 0)) 10000) +(println) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8659eb2d2..c473a34d4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1897,7 +1897,7 @@ reduces them without incurring seq initialization" "Returns the nth rest of coll, coll when n is 0." [coll n] (loop [n n xs coll] - (if (and (pos? n) (seq xs)) + (if-let [xs (and (pos? n) (seq xs))] (recur (dec n) (rest xs)) xs))) @@ -4775,21 +4775,175 @@ reduces them without incurring seq initialization" s)))] (lazy-seq (step pred coll))))) +(deftype Cycle [meta all prev ^:mutable current ^:mutable _next] + Object + (toString [coll] + (pr-str* coll)) + (currentval [coll] + (when-not ^seq current + (if-let [c (next prev)] + (set! current c) + (set! current all))) + current) + + IPending + (-realized? [coll] + (some? current)) + + IWithMeta + (-with-meta [coll meta] (Cycle. meta all prev current _next)) + + IMeta + (-meta [coll] meta) + + ISeq + (-first [coll] + (first (.currentval coll))) + (-rest [coll] + (when (nil? _next) + (set! _next (Cycle. nil all (.currentval coll) nil nil))) + _next) + + INext + (-next [coll] + (-rest coll)) + + ICollection + (-conj [coll o] (cons o coll)) + + IEmptyableCollection + (-empty [coll] (-with-meta (.-EMPTY List) meta)) + + ISequential + ISeqable + (-seq [coll] coll) + + IReduce + (-reduce [coll f] + (loop [s (.currentval coll) ret (first s)] + (let [s (or (next s) all) + ret (f ret (first s))] + (if (reduced? ret) + @ret + (recur s ret))))) + (-reduce [coll f start] + (loop [s (.currentval coll) ret start] + (let [ret (f ret (first s))] + (if (reduced? ret) + @ret + (recur (or (next s) all) ret)))))) + (defn cycle "Returns a lazy (infinite!) sequence of repetitions of the items in coll." - [coll] (lazy-seq - (when-let [s (seq coll)] - (concat s (cycle s))))) + [coll] (if-let [vals (seq coll)] + (Cycle. nil vals nil vals nil) + (.-EMPTY List))) (defn split-at "Returns a vector of [(take n coll) (drop n coll)]" [n coll] [(take n coll) (drop n coll)]) +(deftype Repeat [meta count val ^:mutable next ^:mutable __hash] + Object + (toString [coll] + (pr-str* coll)) + (equiv [this other] + (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x count)) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) + + IPending + (-realized? [coll] false) + + IWithMeta + (-with-meta [coll meta] (Repeat. meta count val next nil)) + + IMeta + (-meta [coll] meta) + + ISeq + (-first [coll] + val) + (-rest [coll] + (if (nil? next) + (if (> count 1) + (do + (set! next (Repeat. nil (dec count) val nil nil)) + next) + (if (== -1 count) + coll + ())) + next)) + + INext + (-next [coll] + (if (nil? next) + (if (> count 1) + (do + (set! next (Repeat. nil (dec count) val nil nil)) + next) + (if (== -1 count) + coll + nil)) + next)) + + ICollection + (-conj [coll o] (cons o coll)) + + IEmptyableCollection + (-empty [coll] (-with-meta (.-EMPTY List) meta)) + + IHash + (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) + + ISequential + ISeqable + (-seq [coll] coll) + + IEquiv + (-equiv [coll other] (equiv-sequential coll other)) + + IReduce + (-reduce [coll f] + (if (== count -1) + (loop [ret (f val val)] + (if (reduced? ret) + @ret + (recur (f ret val)))) + (loop [i 1 ret val] + (if (< i count) + (let [ret (f ret val)] + (if (reduced? ret) + @ret + (recur (inc i) ret))) + ret)))) + (-reduce [coll f start] + (if (== count -1) + (loop [ret (f start val)] + (if (reduced? ret) + @ret + (recur (f ret val)))) + (loop [i 0 ret start] + (if (< i count) + (let [ret (f ret val)] + (if (reduced? ret) + @ret + (recur (inc i) ret))) + ret))))) + (defn repeat "Returns a lazy (infinite!, or length n if supplied) sequence of xs." - ([x] (lazy-seq (cons x (repeat x)))) - ([n x] (take n (repeat x)))) + ([x] (Repeat. nil -1 x nil nil)) + ([n x] (if (pos? n) + (Repeat. nil n x nil nil) + (.-EMPTY List)))) (defn replicate "DEPRECATED: Use 'repeat' instead. @@ -4803,10 +4957,68 @@ reduces them without incurring seq initialization" ([f] (lazy-seq (cons (f) (repeatedly f)))) ([n f] (take n (repeatedly f)))) +(def ^:private UNREALIZED-SEED #js {}) + +(deftype Iterate [meta f prev-seed ^:mutable seed ^:mutable next] + Object + (toString [coll] + (pr-str* coll)) + + IPending + (-realized? [coll] + (not (identical? seed UNREALIZED-SEED))) + + IWithMeta + (-with-meta [coll meta] (Iterate. meta f prev-seed seed next)) + + IMeta + (-meta [coll] meta) + + ISeq + (-first [coll] + (when (identical? UNREALIZED-SEED seed) + (set! seed (f prev-seed))) + seed) + (-rest [coll] + (when (nil? next) + (set! next (Iterate. nil f (-first coll) UNREALIZED-SEED nil))) + next) + + INext + (-next [coll] + (-rest coll)) + + ICollection + (-conj [coll o] (cons o coll)) + + IEmptyableCollection + (-empty [coll] (-with-meta (.-EMPTY List) meta)) + + ISequential + ISeqable + (-seq [coll] coll) + + IReduce + (-reduce [coll rf] + (let [first (-first coll) + v (f first)] + (loop [ret (rf first v) v v] + (if (reduced? ret) + @ret + (let [v (f v)] + (recur (rf ret v) v)))))) + (-reduce [coll rf start] + (let [v (-first coll)] + (loop [ret (rf start v) v v] + (if (reduced? ret) + @ret + (let [v (f v)] + (recur (rf ret v) v))))))) + (defn iterate "Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects" {:added "1.0"} - [f x] (cons x (lazy-seq (iterate f (f x))))) + [f x] (Iterate. nil f nil x nil)) (defn interleave "Returns a lazy seq of the first item in each coll, then the second etc." @@ -9507,8 +9719,8 @@ reduces them without incurring seq initialization" be used to force any effects. Walks through the successive nexts of the seq, does not retain the head and returns nil." ([coll] - (when (seq coll) - (recur (next coll)))) + (when-let [s (seq coll)] + (recur (next s)))) ([n coll] (when (and (seq coll) (pos? n)) (recur (dec n) (next coll))))) @@ -9960,6 +10172,15 @@ reduces them without incurring seq initialization" Range (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + Cycle + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + + Repeat + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + + Iterate + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + ES6IteratorSeq (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 3e9546f00..51514f2a1 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -8,7 +8,7 @@ (ns cljs.collections-test (:refer-clojure :exclude [iter]) - (:require [cljs.test :refer-macros [deftest testing is]] + (:require [cljs.test :refer-macros [deftest testing is are]] [clojure.string :as s] [clojure.set :as set])) @@ -165,6 +165,227 @@ (is (= (take 3 (range 3 1 0)) (list 3 3 3))) )) +(deftest test-cycle + (testing "Testing Cycle" + + (is (= "(1 2 3 1 2 ...)" (binding [*print-length* 5] (pr-str (cycle [1 2 3]))))) + + (are [x y] (= x y) + (cycle []) () + (cycle nil) () + + (take 3 (cycle [1])) '(1 1 1) + (take 5 (cycle [1 2 3])) '(1 2 3 1 2) + + (take 3 (cycle [nil])) '(nil nil nil) + + (transduce (take 5) + (cycle [1])) 5 + (transduce (take 5) + 2 (cycle [1])) 7 + (transduce (take 5) + (cycle [3 7])) 23 + (transduce (take 5) + 2 (cycle [3 7])) 25 + + (take 2 (cycle (map #(/ 42 %) '(2 1 0)))) '(21 42) + (first (cycle [1 2 3])) 1 + (first (rest (cycle [1 2 3]))) 2 + (first (next (cycle [1 2 3]))) 2 + (first (conj (cycle [1 2 3]) :hi)) :hi + (empty (cycle [1 2 3])) () + (first (next (cycle (map #(/ 42 %) '(2 1 0))))) 42 + (into [] (take 2) (cycle (map #(/ 42 %) '(2 1 0)))) '(21 42) + + (first (seq (cycle [1 2 3]))) 1) + + ; indexOf fns work on the finite cycle + (is (= -1 (.indexOf (cycle []) 19))) + (is (= -1 (.indexOf (cycle []) 19 2))) + (is (= -1 (.lastIndexOf (cycle []) 19))) + (is (= -1 (.lastIndexOf (cycle []) 19 2))) + + (is (= {:a 1} (meta (with-meta (cycle [1 2 3]) {:a 1})))) + (is (= {:a 1} (meta (empty (with-meta (cycle [1 2 3]) {:a 1}))))) + (is (= (take 7 (with-meta (cycle [1 2 3]) {:a 1})) (take 7 (cycle [1 2 3])))) + + (is (realized? (cycle [1 2 3]))) + + (are [x y] (= (transduce (take x) conj (cycle [1 2 3])) y) + 0 [] + 1 [1] + 2 [1 2] + 3 [1 2 3] + 4 [1 2 3 1] + 5 [1 2 3 1 2] + 6 [1 2 3 1 2 3] + 7 [1 2 3 1 2 3 1]) + + (are [x y] (= (transduce (take x) conj [:x] (cycle [1 2 3])) y) + 0 [:x] + 1 [:x 1] + 2 [:x 1 2] + 3 [:x 1 2 3] + 4 [:x 1 2 3 1] + 5 [:x 1 2 3 1 2] + 6 [:x 1 2 3 1 2 3] + 7 [:x 1 2 3 1 2 3 1]))) + +(deftest test-repeat + (testing "Testing Repeat" + (is (= (repeat 5 :x) (repeat 5 :x))) + (is (= (repeat 5 :x) '(:x :x :x :x :x))) + (is (= (hash (repeat 5 :x)) (hash '(:x :x :x :x :x)))) + (is (= (assoc (array-map (repeat 1 :x) :y) '(:x) :z) {'(:x) :z})) + (is (= (assoc (hash-map (repeat 1 :x) :y) '(:x) :z) {'(:x) :z})) + + (is (= "(7 7 7 ...)" (binding [*print-length* 3] (pr-str (repeat 7))))) + (is (= "(7 7 7)" (pr-str (repeat 3 7)))) + + (are [x y] (= x y) + (take 0 (repeat 7)) () + (take 1 (repeat 7)) '(7) + (take 2 (repeat 7)) '(7 7) + (take 5 (repeat 7)) '(7 7 7 7 7)) + + ; limited sequence + (are [x y] (= x y) + (repeat 0 7) () + (repeat 1 7) '(7) + (repeat 2 7) '(7 7) + (repeat 5 7) '(7 7 7 7 7) + + (repeat -1 7) () + (repeat -3 7) ()) + + ;; counts + (are [x y] (= (count x) y) + (repeat 0 7) 0 + (repeat 1 7) 1 + (repeat 2 7) 2 + (repeat 5 7) 5 + + (repeat -1 7) 0 + (repeat -3 7) 0) + + ; test different data types + (are [x] (= (repeat 3 x) (list x x x)) + nil + false true + 0 42 + 0.0 3.14 + 0M 1M + \c + "" "abc" + 'sym + :kw + () '(1 2) + [] [1 2] + {} {:a 1 :b 2} + #{} #{1 2}) + + ; indexOf / lastIndexOf work on finite repeats + (is (= -1 (.indexOf (repeat 7 5) 19))) + (is (= -1 (.indexOf (repeat 7 5) 19 2))) + (is (= -1 (.lastIndexOf (repeat 7 5) 19))) + (is (= -1 (.lastIndexOf (repeat 7 5) 19 2))) + (is (= 0 (.indexOf (repeat 7 5) 5))) + (is (= 6 (.lastIndexOf (repeat 7 5) 5))) + (is (= 3 (.indexOf (repeat 7 5) 5 3))) + (is (= 3 (.lastIndexOf (repeat 7 5) 5 3))) + + (is (= {:a 1} (meta (with-meta (repeat 5 7) {:a 1})))) + (is (= {:a 1} (meta (empty (with-meta (repeat 5 7) {:a 1}))))) + (is (= (with-meta (repeat 5 7) {:a 1}) (repeat 5 7))) + + (is (not (realized? (repeat 5 7)))) + + (is (= [1 1] (into [] (drop 98) (repeat 100 1)))) + + (is (= () (empty (repeat 100 1)))) + (is (= () (empty (repeat 7)))) + + (are [x y] (= (transduce (take x) conj (repeat 1)) y) + 0 [] + 1 [1] + 2 [1 1] + 3 [1 1 1]) + + (are [x y] (= (transduce (take x) conj [:x] (repeat 1)) y) + 0 [:x] + 1 [:x 1] + 2 [:x 1 1] + 3 [:x 1 1 1]) + + (are [x y] (= (transduce (take x) conj (repeat 2 1)) y) + 0 [] + 1 [1] + 2 [1 1] + 3 [1 1]) + + (are [x y] (= (transduce (take x) conj [:x] (repeat 2 1)) y) + 0 [:x] + 1 [:x 1] + 2 [:x 1 1] + 3 [:x 1 1]))) + +(deftest test-iterate + (testing "Testing Iterate" + (are [x y] (= x y) + (take 0 (iterate inc 0)) () + (take 1 (iterate inc 0)) '(0) + (take 2 (iterate inc 0)) '(0 1) + (take 5 (iterate inc 0)) '(0 1 2 3 4)) + + (is (= "(0 1 2 ...)" (binding [*print-length* 3] (pr-str (iterate inc 0))))) + + (is (not (realized? (rest (iterate inc 0))))) + + (is (= {:a 1} (meta (with-meta (iterate inc 0) {:a 1})))) + (is (= {:a 1} (meta (empty (with-meta (iterate inc 0) {:a 1}))))) + (is (= (take 20 (with-meta (iterate inc 0) {:a 1})) (take 20 (iterate inc 0)))) + + (is (= [:first 0 1] (take 3 (conj (iterate inc 0) :first)))) + + (is (= () (empty (iterate inc 0)))) + + (let [v (iterate inc 0)] + (is (identical? v (seq v)))) + + (is (= 0 (first (iterate inc 0)))) + (is (= 1 (first (rest (iterate inc 0))))) + (is (= 1 (first (next (iterate inc 0))))) + + ;; test other fns + (is (= '(:foo 42 :foo 42) (take 4 (iterate #(if (= % :foo) 42 :foo) :foo)))) + (is (= '(1 false true true) (take 4 (iterate boolean? 1)))) + (is (= '(256 128 64 32 16 8 4 2 1 0) (take 10 (iterate #(quot % 2) 256)))) + + ;; reduce via transduce + (is (= (transduce (take 5) + (iterate #(* 2 %) 2)) 62)) + (is (= (transduce (take 5) + 1 (iterate #(* 2 %) 2)) 63)) + + (are [x y] (= (transduce (take x) conj (iterate inc 0)) y) + 0 [] + 1 [0] + 2 [0 1] + 3 [0 1 2]) + + (are [x y] (= (transduce (take x) conj [:x] (iterate inc 0)) y) + 0 [:x] + 1 [:x 0] + 2 [:x 0 1] + 3 [:x 0 1 2]))) + +(deftest test-split-at + (is (vector? (split-at 2 []))) + (is (vector? (split-at 2 [1 2 3]))) + + (are [x y] (= x y) + (split-at 2 []) [() ()] + (split-at 2 [1 2 3 4 5]) [(list 1 2) (list 3 4 5)] + + (split-at 5 [1 2 3]) [(list 1 2 3) ()] + (split-at 0 [1 2 3]) [() (list 1 2 3)] + (split-at -1 [1 2 3]) [() (list 1 2 3)] + (split-at -5 [1 2 3]) [() (list 1 2 3)] )) + (deftest test-rseq (testing "Testing RSeq" (is (= '(3 2 1) (reverse (seq (array 1 2 3))))) From 47aab44204b9b0be2f139d55bd494a27a023a9e2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 Dec 2017 12:07:21 -0500 Subject: [PATCH 2744/4033] CLJS-2443: doseq should return nil with no collections --- src/main/clojure/cljs/core.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 1d82f99a8..b3d406278 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2417,7 +2417,7 @@ (core/let [err (core/fn [& msg] (throw (ex-info (apply core/str msg) {}))) step (core/fn step [recform exprs] (core/if-not exprs - [true `(do ~@body)] + [true `(do ~@body nil)] (core/let [k (first exprs) v (second exprs) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index a929f6eaf..199607eb0 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1495,6 +1495,9 @@ (is (= "Positional factory function for cljs.core-test/Bar2407." (:doc (meta #'->Bar2407)))) (is (= "Factory function for cljs.core-test/Bar2407, taking a map of keywords to field values." (:doc (meta #'map->Bar2407))))) +(deftest test-cljs-2283 + (is (nil? (doseq [])))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 9de75f367d31097c78973e31fe4908cc224926fc Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 24 Nov 2017 21:14:27 -0500 Subject: [PATCH 2745/4033] CLJS-2413: Port core.specs.alpha to ClojureScript --- src/main/cljs/cljs/core/specs/alpha.cljc | 234 +++++++++++++++++++++++ src/main/cljs/cljs/core/specs/alpha.cljs | 10 + 2 files changed, 244 insertions(+) create mode 100644 src/main/cljs/cljs/core/specs/alpha.cljc create mode 100644 src/main/cljs/cljs/core/specs/alpha.cljs diff --git a/src/main/cljs/cljs/core/specs/alpha.cljc b/src/main/cljs/cljs/core/specs/alpha.cljc new file mode 100644 index 000000000..b873f41b7 --- /dev/null +++ b/src/main/cljs/cljs/core/specs/alpha.cljc @@ -0,0 +1,234 @@ +; 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.core.specs.alpha + (:require [clojure.spec.alpha :as s] + #?(:clj [cljs.core :as core] + :cljs [cljs.core$macros :as core]))) + +;;;; destructure + +(s/def ::local-name (s/and simple-symbol? #(not= '& %))) + +(s/def ::binding-form + (s/or :sym ::local-name + :seq ::seq-binding-form + :map ::map-binding-form)) + +;; sequential destructuring + +(s/def ::seq-binding-form + (s/and vector? + (s/cat :elems (s/* ::binding-form) + :rest (s/? (s/cat :amp #{'&} :form ::binding-form)) + :as (s/? (s/cat :as #{:as} :sym ::local-name))))) + +;; map destructuring + +(s/def ::keys (s/coll-of ident? :kind vector?)) +(s/def ::syms (s/coll-of symbol? :kind vector?)) +(s/def ::strs (s/coll-of simple-symbol? :kind vector?)) +(s/def ::or (s/map-of simple-symbol? any?)) +(s/def ::as ::local-name) + +(s/def ::map-special-binding + (s/keys :opt-un [::as ::or ::keys ::syms ::strs])) + +(s/def ::map-binding (s/tuple ::binding-form any?)) + +(s/def ::ns-keys + (s/tuple + (s/and qualified-keyword? #(-> % name #{"keys" "syms"})) + (s/coll-of simple-symbol? :kind vector?))) + +(s/def ::map-bindings + (s/every (s/or :mb ::map-binding + :nsk ::ns-keys + :msb (s/tuple #{:as :or :keys :syms :strs} any?)) :into {})) + +(s/def ::map-binding-form (s/merge ::map-bindings ::map-special-binding)) + +;; bindings + +(s/def ::binding (s/cat :binding ::binding-form :init-expr any?)) +(s/def ::bindings (s/and vector? (s/* ::binding))) + +;; let, if-let, when-let + +(s/fdef core/let + :args (s/cat :bindings ::bindings + :body (s/* any?))) + +(s/fdef core/if-let + :args (s/cat :bindings (s/and vector? ::binding) + :then any? + :else (s/? any?))) + +(s/fdef core/when-let + :args (s/cat :bindings (s/and vector? ::binding) + :body (s/* any?))) + +;; defn, defn-, fn + +(s/def ::arg-list + (s/and + vector? + (s/cat :args (s/* ::binding-form) + :varargs (s/? (s/cat :amp #{'&} :form ::binding-form))))) + +(s/def ::args+body + (s/cat :args ::arg-list + :body (s/alt :prepost+body (s/cat :prepost map? + :body (s/+ any?)) + :body (s/* any?)))) + +(s/def ::defn-args + (s/cat :name simple-symbol? + :docstring (s/? string?) + :meta (s/? map?) + :bs (s/alt :arity-1 ::args+body + :arity-n (s/cat :bodies (s/+ (s/spec ::args+body)) + :attr (s/? map?))))) + +(s/fdef core/defn + :args ::defn-args + :ret any?) + +(s/fdef core/defn- + :args ::defn-args + :ret any?) + +(s/fdef core/fn + :args (s/cat :name (s/? simple-symbol?) + :bs (s/alt :arity-1 ::args+body + :arity-n (s/+ (s/spec ::args+body)))) + :ret any?) + +;;;; ns + +(s/def ::exclude (s/coll-of simple-symbol?)) +(s/def ::only (s/coll-of simple-symbol?)) +(s/def ::rename (s/map-of simple-symbol? simple-symbol?)) +(s/def ::filters (s/keys* :opt-un [::exclude ::only ::rename])) + +(s/def ::ns-refer-clojure + (s/spec (s/cat :clause #{:refer-clojure} + :filters ::filters))) + +(s/def ::refer (s/coll-of simple-symbol?)) +(s/def ::refer-macros (s/coll-of simple-symbol?)) +(s/def ::include-macros #{true}) + +(s/def ::lib (s/or :sym simple-symbol? + :str string?)) + +(s/def ::libspec + (s/alt :lib ::lib + :lib+opts (s/spec (s/cat :lib ::lib + :options (s/keys* :opt-un [::as ::refer ::refer-macros ::include-macros]))))) + +(s/def ::macros-libspec + (s/alt :lib simple-symbol? + :lib+opts (s/spec (s/cat :lib simple-symbol? + :options (s/keys* :opt-un [::as ::refer]))))) + +(s/def ::ns-require + (s/spec (s/cat :clause #{:require} + :body (s/+ (s/alt :libspec ::libspec + :flag #{:reload :reload-all :verbose}))))) + +(s/def ::ns-require-macros + (s/spec (s/cat :clause #{:require-macros} + :body (s/+ (s/alt :libspec ::macros-libspec + :flag #{:reload :reload-all :verbose}))))) + +(s/def ::package-list + (s/spec + (s/cat :package simple-symbol? + :classes (s/* simple-symbol?)))) + +(s/def ::import-list + (s/* (s/alt :class simple-symbol? + :package-list ::package-list))) + +(s/def ::ns-import + (s/spec + (s/cat :clause #{:import} + :classes ::import-list))) + +;; same as ::libspec, but also supports the ::filters options in the libspec +(s/def ::use-libspec + (s/alt :lib ::lib + :lib+opts (s/spec (s/cat :lib ::lib + :options (s/keys* :req-un [::only] :opt-un [::rename]))))) + +(s/def ::ns-use + (s/spec (s/cat :clause #{:use} + :libs (s/+ (s/alt :libspec ::use-libspec + :flag #{:reload :reload-all :verbose}))))) + +;; same as ::libspec-macros, but also supports the ::filters options in the libspec +(s/def ::use-macros-libspec + (s/alt :lib simple-symbol? + :lib+opts (s/spec (s/cat :lib simple-symbol? + :options (s/keys* :req-un [::only] :opt-un [::rename]))))) + +(s/def ::ns-use-macros + (s/spec (s/cat :clause #{:use-macros} + :libs (s/+ (s/alt :libspec ::use-macros-libspec + :flag #{:reload :reload-all :verbose}))))) + + +(s/def ::ns-clauses + (s/* (s/alt :refer-clojure ::ns-refer-clojure + :require ::ns-require + :require-macros ::ns-require-macros + :import ::ns-import + :use ::ns-use + :use-macros ::ns-use-macros))) + +(s/def ::ns-form + (s/cat :name simple-symbol? + :docstring (s/? string?) + :attr-map (s/? map?) + :clauses ::ns-clauses)) + +#_(s/fdef clojure.core/ns + :args ::ns-form) + +(defmacro ^:private quotable + "Returns a spec that accepts both the spec and a (quote ...) form of the spec" + [spec] + `(s/or :spec ~spec :quoted-spec (s/cat :quote #{'quote} :spec ~spec))) + +(s/def ::quotable-import-list + (s/* (s/alt :class (quotable simple-symbol?) + :package-list (quotable ::package-list)))) + +(s/fdef core/import + :args ::quotable-import-list) + +(s/fdef core/require + :args (s/+ (s/spec (s/cat :quote #{'quote} + :spec (s/alt :libspec ::libspec + :flag #{:reload :reload-all :verbose}))))) + +(s/fdef core/require-macros + :args (s/+ (s/spec (s/cat :quote #{'quote} + :spec (s/alt :libspec ::macros-libspec + :flag #{:reload :reload-all :verbose}))))) + +(s/fdef core/use + :args (s/+ (s/spec (s/cat :quote #{'quote} + :spec (s/alt :libspec ::use-libspec + :flag #{:reload :reload-all :verbose}))))) + +(s/fdef core/use-macros + :args (s/+ (s/spec (s/cat :quote #{'quote} + :spec (s/alt :libspec ::use-macros-libspec + :flag #{:reload :reload-all :verbose}))))) diff --git a/src/main/cljs/cljs/core/specs/alpha.cljs b/src/main/cljs/cljs/core/specs/alpha.cljs new file mode 100644 index 000000000..eba64b09d --- /dev/null +++ b/src/main/cljs/cljs/core/specs/alpha.cljs @@ -0,0 +1,10 @@ +; 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.core.specs.alpha + (:require-macros [cljs.core.specs.alpha])) From b11cbeefa5c148b256bcc0942d714c23ab4c6c81 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 25 Nov 2017 09:47:30 -0500 Subject: [PATCH 2746/4033] Experiment: Attach ns spec to internal-use Var --- src/main/cljs/cljs/core/specs/alpha.cljc | 2 +- src/main/clojure/cljs/analyzer.cljc | 21 ++++++++++++++------- src/main/clojure/cljs/core.cljc | 3 +++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core/specs/alpha.cljc b/src/main/cljs/cljs/core/specs/alpha.cljc index b873f41b7..6f3341700 100644 --- a/src/main/cljs/cljs/core/specs/alpha.cljc +++ b/src/main/cljs/cljs/core/specs/alpha.cljc @@ -198,7 +198,7 @@ :attr-map (s/? map?) :clauses ::ns-clauses)) -#_(s/fdef clojure.core/ns +(s/fdef core/ns-special-form :args ::ns-form) (defmacro ^:private quotable diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 61a37f9a5..fdbf9816a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3270,21 +3270,28 @@ (when (some? (find-ns-obj 'cljs.spec.alpha)) @cached-var)))) +(defn- do-macroexpand-check + [form mac-var] + (let [mchk #?(:clj (some-> (find-ns 'clojure.spec.alpha) + (ns-resolve 'macroexpand-check)) + :cljs (get-macroexpand-check-var))] + (when (some? mchk) + (mchk mac-var (next form))))) + (defn macroexpand-1* [env form] (let [op (first form)] (if (contains? specials op) - form + (do + (when (= 'ns op) + (do-macroexpand-check form (get-expander 'cljs.core/ns-special-form env))) + form) ;else (if-some [mac-var (when (symbol? op) (get-expander op env))] (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) - (let [mchk #?(:clj (some-> (find-ns 'clojure.spec.alpha) - (ns-resolve 'macroexpand-check)) - :cljs (get-macroexpand-check-var)) - _ (when (some? mchk) - (mchk mac-var (next form))) - form' (try + (do-macroexpand-check form mac-var) + (let [form' (try (apply @mac-var form env (rest form)) #?(:clj (catch ArityException e (throw (ArityException. (- (.actual e) 2) (.name e))))))] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b3d406278..0747d0b1c 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2855,6 +2855,9 @@ [x & forms] `(do ~@forms)) +;; An internal-use Var for defining specs on the ns special form +(core/defmacro ^:private ns-special-form []) + (core/defmacro require "Loads libs, skipping any that are already loaded. Each argument is either a libspec that identifies a lib or a flag that modifies how all the identified From 1aa07b0c9a88b9c0fcc0cb08390ccb5f919c73e0 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 19 Nov 2017 08:31:17 -0500 Subject: [PATCH 2747/4033] CLJS-2403: Document and test that min-key, max-key return last --- src/main/cljs/cljs/core.cljs | 8 ++++++-- src/test/cljs/cljs/core_test.cljs | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c473a34d4..ac0c180ca 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9357,14 +9357,18 @@ reduces them without incurring seq initialization" (persistent! map)))) (defn max-key - "Returns the x for which (k x), a number, is greatest." + "Returns the x for which (k x), a number, is greatest. + + If there are multiple such xs, the last one is returned." ([k x] x) ([k x y] (if (> (k x) (k y)) x y)) ([k x y & more] (reduce #(max-key k %1 %2) (max-key k x y) more))) (defn min-key - "Returns the x for which (k x), a number, is least." + "Returns the x for which (k x), a number, is least. + + If there are multiple such xs, the last one is returned." ([k x] x) ([k x y] (if (< (k x) (k y)) x y)) ([k x y & more] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 199607eb0..e25b59339 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -8,7 +8,7 @@ (ns cljs.core-test (:refer-clojure :exclude [iter]) - (:require [cljs.test :refer-macros [deftest testing is]] + (:require [cljs.test :refer-macros [deftest testing is are]] [clojure.test.check :as tc] [clojure.test.check.clojure-test :refer-macros [defspec]] [clojure.test.check.generators :as gen] @@ -1454,6 +1454,11 @@ (is (= "#js {:_abc 1}" (pr-str #js {"_abc" 1}))) (is (= "#js {:*compiler* 1}" (pr-str #js {"*compiler*" 1})))) +(deftest test-cljs-2403 + (are [f k coll expected] (= expected (apply f k coll)) + min-key :x [{:x 1000} {:x 1001} {:x 1002} {:x 1000 :second true}] {:x 1000 :second true} + max-key :x [{:x 1000} {:x 999} {:x 998} {:x 1000 :second true}] {:x 1000 :second true})) + (deftest swap-vals-returns-old-value (let [a (atom 0)] (is (= [0 1] (swap-vals! a inc))) From db4d77113aa4a04cf1ef384abe5c5d4217a88b48 Mon Sep 17 00:00:00 2001 From: Mark Hepburn Date: Thu, 9 Nov 2017 09:36:16 +1100 Subject: [PATCH 2748/4033] CLJS-2221: cljs.util/relative-name still has issues on case-insensitive platforms Use java.nio.file.Path for more robust path manipulation cljs.util/relative-name uses string substitution for path manipulation. This can cause issues in file systems (notably, on the Windows platform) that are case-insensitive, where two strings that are similar up to case-sensitivity are equal as paths but the string substitution will fail. The interface java.nio.file.Path handles paths in a platform-specific fashion. --- src/main/clojure/cljs/util.cljc | 12 ++++++------ src/test/clojure/cljs/util_tests.clj | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 8b71a4682..ea167a866 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -161,12 +161,12 @@ [x] {:pre [(or (file? x) (url? x))]} (letfn [(strip-user-dir [s] - (let [user-dir (System/getProperty "user.dir") - s (normalize-path s) - user-path (cond-> user-dir - (not (.endsWith user-dir File/separator)) - (str File/separator))] - (string/replace s user-path "")))] + (let [user-path (.toPath (io/file (System/getProperty "user.dir"))) + base-count (.getNameCount user-path) + file-path (.toPath (io/file s))] + (if (.startsWith file-path user-path) + (str (.subpath file-path base-count (.getNameCount file-path))) + s)))] (if (file? x) (strip-user-dir (.getAbsolutePath x)) (let [f (URLDecoder/decode (.getFile x))] diff --git a/src/test/clojure/cljs/util_tests.clj b/src/test/clojure/cljs/util_tests.clj index bbc0d768a..9ee44b3ea 100644 --- a/src/test/clojure/cljs/util_tests.clj +++ b/src/test/clojure/cljs/util_tests.clj @@ -34,12 +34,20 @@ (System/setProperty "user.dir" "C:\\Users\\anmonteiro\\Downloads\\clojurescript-master") (is (= (util/relative-name (io/file "C:\\Users\\anmonteiro\\Downloads\\clojurescript-master\\out\\index.js")) "out\\index.js")) (is (= (util/relative-name (io/as-url (io/file "C:\\Users\\anmonteiro\\Downloads\\clojurescript-master\\node_modules\\lodash\\array.js"))) "node_modules\\lodash\\array.js")) + ;; Check case-sensitivity: + (System/setProperty "user.dir" "c:\\users\\anmonteiro\\Downloads\\clojurescript-master") + (is (= (util/relative-name (io/file "C:\\Users\\anmonteiro\\Downloads\\clojurescript-master\\out\\index.js")) "out\\index.js")) + (is (= (util/relative-name (io/as-url (io/file "C:\\Users\\anmonteiro\\Downloads\\clojurescript-master\\node_modules\\lodash\\array.js"))) "node_modules\\lodash\\array.js")) + ;; Check pass-through: + (is (= (util/relative-name (io/file "C:\\Temp\\clojurescript\\out\\index.js")) "C:\\Temp\\clojurescript\\out\\index.js")) (System/setProperty "user.dir" initial)) ;; Non-windows (let [initial (System/getProperty "user.dir")] (System/setProperty "user.dir" "/Users/user/clojurescript") (is (= (util/relative-name (io/file "/Users/user/clojurescript/out/index.js")) "out/index.js")) (is (= (util/relative-name (io/as-url (io/file "/Users/user/clojurescript/out/index.js"))) "out/index.js")) + ;; Check pass-through: + (is (= (util/relative-name (io/file "/tmp/clojurescript/out/index.js")) "/tmp/clojurescript/out/index.js")) (System/setProperty "user.dir" initial)))) (deftest test-path From aa22a2eb322606b0c55055419e4c0467e80d5ea6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 Dec 2017 13:45:40 -0500 Subject: [PATCH 2749/4033] fn -> core/fn --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 0747d0b1c..4b0877132 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -753,7 +753,7 @@ (core/or (core/true? default) (core/false? default)) "boolean")] `(do (declare ~(core/vary-meta sym - (fn [m] + (core/fn [m] (core/cond-> m (core/not (core/contains? m :tag)) (core/assoc :tag (core/symbol type)) From 93a841b6e1a043e4bac0fcae3d82cc0410f7f3fc Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 Dec 2017 15:09:57 -0500 Subject: [PATCH 2750/4033] CLJS-2397: Multi-arity function instrumentation fails with :static-fns true CLJS-2197: Calling instrumented var fails to check conformance The instrument var wrapper would copy over arity methods from the original fn but these would not be wrapped. Instead copy them over from a MetaFn that takes a validating fn. --- src/main/cljs/cljs/spec/test/alpha.cljs | 22 ++++++++++------- src/test/cljs/cljs/spec/test_test.cljs | 32 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index f5b104e5f..4c94dddda 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -104,16 +104,20 @@ (str "Call to " v " did not conform to spec:\n" (with-out-str (s/explain-out ed))) ed))) conformed)))] - (doto - (fn - [& args] - (if *instrument-enabled* - (with-instrument-disabled - (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) - (binding [*instrument-enabled* true] + (doto (fn [& args] + (if *instrument-enabled* + (with-instrument-disabled + (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) + (binding [*instrument-enabled* true] + (apply f args))) (apply f args))) - (apply f args))) - (gobj/extend f)))) + (gobj/extend (MetaFn. (fn [& args] + (if *instrument-enabled* + (with-instrument-disabled + (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) + (binding [*instrument-enabled* true] + (apply f args))) + (apply f args))) nil))))) (defn- no-fspec [v spec] diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index efdb1538d..2953ecc3e 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -4,6 +4,12 @@ [cljs.spec.alpha :as s] [cljs.spec.test.alpha :as stest])) +(s/fdef clojure.core/symbol + :args (s/alt :separate (s/cat :ns string? :n string?) + :str string? + :sym symbol?) + :ret symbol?) + (defn h-cljs-1812 [x] true) (s/fdef h-cljs-1812 :args (s/cat :x int?) :ret true?) @@ -41,3 +47,29 @@ (deftest test-cljs-2414 (is (empty? (stest/instrument 'cljs.spec.test.test-macros$macros/add)))) + +(deftest test-cljs-2197 + (stest/instrument `symbol) + (is (thrown? js/Error (symbol 3))) + (is (thrown? js/Error (#'symbol 3))) + (is (thrown? js/Error (apply symbol [3]))) + (stest/unstrument `symbol)) + +(defn arities + ([a] + (inc a)) + ([a b] + (+ a b)) + ([a b c] 0)) + +(s/fdef arities + :args (s/or :arity-1 (s/cat :a number?) + :arity-2 (s/cat :a number? :b number?) + :arity-3 (s/cat :a string? :b boolean? :c map?)) + :ret number?) + +(deftest test-2397 + (stest/instrument `arities) + (is (arities 1)) + (is (thrown? js/Error (arities "bad"))) + (stest/unstrument `arities)) \ No newline at end of file From ff37f2c8a8a3f3cfb7158f48dacd9b4d91da3e0f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 17 Oct 2017 10:09:18 -0400 Subject: [PATCH 2751/4033] CLJS-2384: Old Infinity and NaN literals are still used in tests --- src/test/cljs/cljs/core_test.cljs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index e25b59339..e0a84c71d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1181,11 +1181,11 @@ [:foo])))) (deftest test-cljs-1594 - (is (not (js/isNaN (hash Infinity)))) - (is (not (js/isNaN (hash -Infinity)))) - (is (not (js/isNaN (hash NaN)))) - (is (= (hash-set Infinity -Infinity 0 1 2 3 4 5 6 7 8) - (set (keys (zipmap [Infinity -Infinity 0 1 2 3 4 5 6 7 8] (repeat nil))))))) + (is (not (js/isNaN (hash js/Infinity)))) + (is (not (js/isNaN (hash js/-Infinity)))) + (is (not (js/isNaN (hash js/NaN)))) + (is (= (hash-set js/Infinity js/-Infinity 0 1 2 3 4 5 6 7 8) + (set (keys (zipmap [js/Infinity js/-Infinity 0 1 2 3 4 5 6 7 8] (repeat nil))))))) (deftest test-cljs-1590 (is (= [""] (s/split "" #"\n"))) From a553fbf6d2ad8631af8d7179406a786b245cb605 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 2 Dec 2017 14:25:06 -0500 Subject: [PATCH 2752/4033] CLJS-2427: docstring for clojure.string/split-lines needs escaping --- src/main/cljs/clojure/string.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 21e46fa08..26f63f26f 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -189,7 +189,7 @@ (conj parts s)))))))))) (defn split-lines - "Splits s on \n or \r\n." + "Splits s on \\n or \\r\\n." [s] (split s #"\n|\r\n")) From e007f07cae30acdc0e9cd1589bf6f6d2e7f5bdff Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 2 Dec 2017 20:41:36 -0500 Subject: [PATCH 2753/4033] CLJS-2428: source fails on Vars whose source has ns-aliased keywords --- src/main/clojure/cljs/repl.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 6c649fb97..663689532 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1221,7 +1221,8 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (with-open [pbr (PushbackReader. (io/reader f))] (let [rdr (readers/source-logging-push-back-reader pbr)] (dotimes [_ (dec (:line v))] (readers/read-line rdr)) - (binding [reader/*data-readers* tags/*cljs-data-readers*] + (binding [reader/*alias-map* identity + reader/*data-readers* tags/*cljs-data-readers*] (-> (reader/read {:read-cond :allow :features #{:cljs}} rdr) meta :source))))))))) From 072677dc0678c3f06acc612d438275d845881f9b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 Dec 2017 16:56:18 -0500 Subject: [PATCH 2754/4033] CLJS-2441: Support variable and property maps --- src/main/clojure/cljs/closure.clj | 110 ++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 37 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d4caff0a8..f13111487 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -49,8 +49,9 @@ [clojure.tools.reader.reader-types :as readers] [cljs.module-graph :as module-graph]) (:import [java.lang ProcessBuilder] - [java.io File BufferedInputStream BufferedReader - Writer InputStreamReader IOException StringWriter] + [java.io + File BufferedInputStream BufferedReader + Writer InputStreamReader IOException StringWriter ByteArrayInputStream] [java.net URL] [java.util.logging Level] [java.util List Random] @@ -61,7 +62,7 @@ SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy - JSModule SourceMap Es6RewriteModules] + JSModule SourceMap Es6RewriteModules VariableMap] [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey @@ -166,7 +167,9 @@ :emit-constants :ups-externs :ups-foreign-libs :ups-libs :warning-handlers :preloads :browser-repl :cache-analysis-format :infer-externs :closure-generate-exports :npm-deps :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs - :watch :watch-error-fn :watch-fn :install-deps :process-shim :rename-prefix :rename-prefix-namespace}) + :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}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -262,6 +265,18 @@ (. compiler-options (setRenamePrefixNamespace (:rename-prefix-namespace opts)))) + (when (contains? opts :closure-variable-map-in) + (let [var-in (io/file (:closure-variable-map-in opts))] + (when (.exists var-in) + (.setInputVariableMap compiler-options + (VariableMap/load (.getAbsolutePath var-in)))))) + + (when (contains? opts :closure-property-map-in) + (let [prop-in (io/file (:closure-property-map-in opts))] + (when (.exists prop-in) + (.setInputPropertyMap compiler-options + (VariableMap/load (.getAbsolutePath prop-in)))))) + (. compiler-options (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler ) @@ -1276,6 +1291,16 @@ :source-map-pretty-print (:source-map-pretty-print opts) :relpaths relpaths})))))) +(defn write-variable-maps [^Result result opts] + (let [var-out (:closure-variable-map-out opts)] + (when-let [var-map (and var-out (.-variableMap result))] + (io/copy (ByteArrayInputStream. (.toBytes var-map)) + (io/file var-out)))) + (let [prop-out (:closure-variable-map-out opts)] + (when-let [prop-map (and prop-out (.-propertyMap result))] + (io/copy (ByteArrayInputStream. (.toBytes prop-map)) + (io/file prop-out))))) + (defn optimize-modules "Use the Closure Compiler to optimize one or more Closure JSModules. Returns a dependency sorted list of module name and description tuples." @@ -1308,21 +1333,23 @@ (assert (or (nil? (:source-map opts)) source-map) "Could not create source maps for modules") (if (.success result) - (vec - (for [[name {:keys [output-to closure-module] :as module}] modules] - [name - (merge - (assoc module - :source - (do - (when source-map (.reset source-map)) - (.toSource closure-compiler ^JSModule closure-module))) - (when source-map - (let [sw (StringWriter.) - source-map-name (str output-to ".map.closure")] - (.appendTo source-map sw source-map-name) - {:source-map-json (.toString sw) - :source-map-name source-map-name})))])) + (do + (write-variable-maps result opts) + (vec + (for [[name {:keys [output-to closure-module] :as module}] modules] + [name + (merge + (assoc module + :source + (do + (when source-map (.reset source-map)) + (.toSource closure-compiler ^JSModule closure-module))) + (when source-map + (let [sw (StringWriter.) + source-map-name (str output-to ".map.closure")] + (.appendTo source-map sw source-map-name) + {:source-map-json (.toString sw) + :source-map-name source-map-name})))]))) (report-failure result)))) (defn optimize @@ -1349,23 +1376,25 @@ (.compile closure-compiler externs inputs compiler-options))] (if (.success result) ;; compiler.getSourceMap().reset() - (let [source (.toSource closure-compiler)] - (when-let [name (:source-map opts)] - (let [name' (str name ".closure") - sw (StringWriter.) - sm-json-str (do - (.appendTo (.getSourceMap closure-compiler) sw name') - (.toString sw))] - (when (true? (:closure-source-map opts)) - (spit (io/file name') sm-json-str)) - (emit-optimized-source-map - (json/read-str sm-json-str :key-fn keyword) - sources name - (assoc opts - :preamble-line-count - (+ (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1) - (if (:output-wrapper opts) 1 0)))))) - source) + (do + (write-variable-maps result opts) + (let [source (.toSource closure-compiler)] + (when-let [name (:source-map opts)] + (let [name' (str name ".closure") + sw (StringWriter.) + sm-json-str (do + (.appendTo (.getSourceMap closure-compiler) sw name') + (.toString sw))] + (when (true? (:closure-source-map opts)) + (spit (io/file name') sm-json-str)) + (emit-optimized-source-map + (json/read-str sm-json-str :key-fn keyword) + sources name + (assoc opts + :preamble-line-count + (+ (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1) + (if (:output-wrapper opts) 1 0)))))) + source)) (report-failure result)))) (comment @@ -2123,7 +2152,14 @@ (assoc :closure-module-roots []) (contains? opts :modules) - (ensure-module-opts)))) + (ensure-module-opts) + + (contains? opts :stable-names) + (->> (merge + {:closure-variable-map-in (io/file output-dir "closure_var.map") + :closure-variable-map-out (io/file output-dir "closure_var.map") + :closure-property-map-in (io/file output-dir "closure_prop.map") + :closure-property-map-out (io/file output-dir "closure_prop.map")}))))) (defn- alive? [proc] (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) From e380903ba9af1ce9dd25691cccc1fb62c1a38d5b Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Fri, 22 Dec 2017 21:24:29 +0100 Subject: [PATCH 2755/4033] Support targeting webworker --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/closure.clj | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ac0c180ca..0188001ba 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11342,7 +11342,7 @@ reduces them without incurring seq initialization" nil)) (next segs)) (find-ns-obj* goog/global segs)) - "default" (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*)))))) (defn ns-interns* diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f13111487..86eea41c6 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1552,7 +1552,29 @@ entries [(:main opts)]))) "goog.require(\"cljs.nodejscli\");\n"))) + + :webworker (output-one-file + (merge opts + (when module + {:output-to (:output-to module)})) + (str (when (or (not module) (= :cljs-base (:module-name opts))) + (str "var CLOSURE_BASE_PATH = \"" asset-path "/goog/\";\n" + "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "var CLOSURE_IMPORT_SCRIPT = (function(global) { return function(src) {global['importScripts'](src); return true;};})(this);\n" + "if(typeof goog == 'undefined') importScripts(\"" asset-path "/goog/base.js\");\n" + "importScripts(\"" asset-path "/cljs_deps.js\");\n" + (apply str (preloads (:preloads opts))))) + (apply str + (map (fn [entry] + (when-not (= "goog" entry) + (str "goog.require(\"" (comp/munge entry) "\");\n"))) + (if-let [entries (when module (:entries module))] + entries + (when-let [main (:main opts)] + [main])))))) + + (output-one-file (merge opts (when module {:output-to (:output-to module)})) From 123e8f9aa59899c6886bbe392128b22ae9a0def3 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 20 Dec 2017 21:22:21 +0200 Subject: [PATCH 2756/4033] CLJS-2447: Ignore css JS modules Pass empty source to Closure for CSS files required by JS modules. --- src/main/clojure/cljs/closure.clj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 86eea41c6..0b480f7cd 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2438,8 +2438,11 @@ js-modules (into [] (comp (map (fn [lib] - (let [js (deps/load-foreign-library lib)] - (assoc js :source (deps/-source js opts))))) + (let [js (deps/load-foreign-library lib) + url (str (deps/-url js opts))] + (if (and url (not (or (.endsWith url ".js") (.endsWith url ".json")))) + (assoc js :source "") + (assoc js :source (deps/-source js opts)))))) (map (fn [js] (if (:preprocess js) (preprocess-js js opts) From 1e4a25ccac810eb9d733989089a47b67f67a464d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 4 Dec 2017 20:27:37 -0500 Subject: [PATCH 2757/4033] CLJS-2431: complement is tagged with boolean return type --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0188001ba..2cbb1dc82 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4199,7 +4199,7 @@ reduces them without incurring seq initialization" "Returns true if n is odd, throws an exception if n is not an integer" [n] (not (even? n))) -(defn ^boolean complement +(defn complement "Takes a fn f and returns a fn that takes the same arguments as f, has the same effects, if any, and returns the opposite truth value." [f] From 980639aced9771b846b759ae5d89e6ca4a06887a Mon Sep 17 00:00:00 2001 From: Martin Klepsch Date: Fri, 15 Dec 2017 15:52:25 +0100 Subject: [PATCH 2758/4033] CLJS-2436: Print compiler options when running with :verbose true --- src/main/clojure/cljs/closure.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0b480f7cd..b0bd76084 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2641,6 +2641,8 @@ (repeat warnings)) warnings))) ana/*verbose* (:verbose opts)] + (when ana/*verbose* + (util/debug-prn "Options passed to ClojureScript compiler:" (pr-str opts))) (let [one-file? (and (:main opts) (#{:advanced :simple :whitespace} (:optimizations opts))) source (if one-file? From 9840425fb6d52e0ee609be50633d9f04b740aba5 Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Wed, 4 Oct 2017 20:10:25 +0200 Subject: [PATCH 2759/4033] CLJS-2277 Update docstring to reflect exist status --- src/main/cljs/cljs/core.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 2cbb1dc82..5ce8287d4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2154,7 +2154,8 @@ reduces them without incurring seq initialization" (gobject/getKeys obj)) (defn js-delete - "Delete a property from a JavaScript object." + "Delete a property from a JavaScript object. + Returns true upon success, false otherwise." [obj key] (cljs.core/js-delete obj key)) From 94e244d47f4882d7864c536da981f6f0beb63dcc Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 1 Jul 2017 12:38:40 -0400 Subject: [PATCH 2760/4033] CLJS-2146: docstring for associative? should refer to IAssociative --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5ce8287d4..32d22fcc7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2098,7 +2098,7 @@ reduces them without incurring seq initialization" (satisfies? ISet x))) (defn ^boolean associative? - "Returns true if coll implements Associative" + "Returns true if coll implements IAssociative" [x] (satisfies? IAssociative x)) (defn ^boolean ifind? From a042a574931429858b10f8a3d21ef79309b0069b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 11 Mar 2017 21:31:57 -0500 Subject: [PATCH 2761/4033] CLJS-1974: Remove cljs.core/fixture1 and fixture2 --- src/main/cljs/cljs/core.cljs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 32d22fcc7..418a1a0c2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10311,11 +10311,6 @@ reduces them without incurring seq initialization" (set! gensym_counter (atom 0))) (symbol (str prefix-string (swap! gensym_counter inc))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Fixtures ;;;;;;;;;;;;;;;; - -(def fixture1 1) -(def fixture2 2) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Delay ;;;;;;;;;;;;;;;;;;;; (deftype Delay [^:mutable f ^:mutable value] From 09dae5fa309ec55777e40e8baf585e171445345d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 7 Mar 2017 18:52:54 -0500 Subject: [PATCH 2762/4033] CLJS-1969: Docstring for condp indicates IllegalArgumentException --- src/main/clojure/cljs/core.cljc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 4b0877132..0c31bf6b6 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2185,8 +2185,7 @@ predicate as its argument, the result of that call being the return value of condp. A single default expression can follow the clauses, and its value will be returned if no clause matches. If no default - expression is provided and no clause matches, an - IllegalArgumentException is thrown." + expression is provided and no clause matches, an Error is thrown." {:added "1.0"} [pred expr & clauses] From 3ebf5e72b8148e28f4d846b1fbe41cabe4426303 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 Dec 2017 20:06:19 -0500 Subject: [PATCH 2763/4033] need to bump Clojure to 1.9.0 and include org.clojure/core.specs.alpha for uberjar --- project.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project.clj b/project.clj index c0188c452..c25caa8c4 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,8 @@ :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] - :dependencies [[org.clojure/clojure "1.8.0"] + :dependencies [[org.clojure/clojure "1.9.0"] + [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.1.0"] [org.clojure/test.check "0.10.0-alpha2" :scope "test"] From 9a54081d3e0ffab699ee2e58b2adc595918f7c2b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 Dec 2017 20:10:16 -0500 Subject: [PATCH 2764/4033] CLJS-2417: Inter-ns s/fdef expansion side effect fails when load cached source --- src/main/cljs/cljs/spec/alpha.cljc | 4 +++- src/main/clojure/cljs/analyzer.cljc | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index cf8566a86..b8a18ebb6 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -436,7 +436,9 @@ :sym symbol?) :ret symbol?)" [fn-sym & specs] - (swap! _speced_vars conj (ns-qualify &env fn-sym)) + (swap! _speced_vars conj + (vary-meta (ns-qualify &env fn-sym) + assoc :fdef-ns (-> &env :ns :name))) `(cljs.spec.alpha/def ~fn-sym (fspec ~@specs))) (defmacro keys* diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index fdbf9816a..f13fc15c5 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3895,13 +3895,22 @@ environment." [ns] (let [spec-vars (get-spec-vars) - ns-str (str ns)] + ns-str (str ns)] (swap! env/*compiler* update-in [::namespaces ns] merge (when-let [registry-ref (:registry-ref spec-vars)] - {:cljs.spec/registry-ref (into [] (filter (fn [[k _]] (= ns-str (namespace k)))) @@registry-ref)}) + {:cljs.spec/registry-ref + (into [] + (filter (fn [[k _]] (= ns-str (namespace k)))) + @@registry-ref)}) (when-let [speced-vars (:speced-vars spec-vars)] - {:cljs.spec/speced-vars (into [] (filter #(= ns-str (namespace %))) @@speced-vars)})))) + {:cljs.spec/speced-vars + (into [] + (filter + (fn [v] + (or (= ns-str (namespace v)) + (= ns (-> v meta :fdef-ns))))) + @@speced-vars)})))) (defn register-specs "Registers speced vars found in a namespace analysis cache." From 0c2ffea5fe2f82939f364ff4e7d6c95de24f1091 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 Dec 2017 22:47:16 -0500 Subject: [PATCH 2765/4033] check :stable-names for truth-y value --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b0bd76084..524a436f1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2176,7 +2176,7 @@ (contains? opts :modules) (ensure-module-opts) - (contains? opts :stable-names) + (:stable-names opts) (->> (merge {:closure-variable-map-in (io/file output-dir "closure_var.map") :closure-variable-map-out (io/file output-dir "closure_var.map") From 8baeacd8729170aae484820fff03eb3ec149719a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 23 Dec 2017 08:43:15 -0500 Subject: [PATCH 2766/4033] make parent dirs for closure var/prop out files --- src/main/clojure/cljs/closure.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 524a436f1..fcafc0b6c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1294,10 +1294,12 @@ (defn write-variable-maps [^Result result opts] (let [var-out (:closure-variable-map-out opts)] (when-let [var-map (and var-out (.-variableMap result))] + (util/mkdirs var-out) (io/copy (ByteArrayInputStream. (.toBytes var-map)) (io/file var-out)))) (let [prop-out (:closure-variable-map-out opts)] (when-let [prop-map (and prop-out (.-propertyMap result))] + (util/mkdirs prop-out) (io/copy (ByteArrayInputStream. (.toBytes prop-map)) (io/file prop-out))))) From f7d611d87f6ea8a605eae7c0339f30b79a840b49 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Sat, 23 Dec 2017 14:43:32 +0200 Subject: [PATCH 2767/4033] CLJS-2450: Allow configuring ingnored JS module extensions --- src/main/clojure/cljs/closure.clj | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index fcafc0b6c..be528fe60 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -169,7 +169,7 @@ :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs :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}) + :stable-names :ignore-js-module-exts}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -2183,7 +2183,10 @@ {:closure-variable-map-in (io/file output-dir "closure_var.map") :closure-variable-map-out (io/file output-dir "closure_var.map") :closure-property-map-in (io/file output-dir "closure_prop.map") - :closure-property-map-out (io/file output-dir "closure_prop.map")}))))) + :closure-property-map-out (io/file output-dir "closure_prop.map")})) + + (nil? (:ignore-js-module-exts opts)) + (assoc :ignore-js-module-exts [".css"])))) (defn- alive? [proc] (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) @@ -2442,8 +2445,13 @@ (map (fn [lib] (let [js (deps/load-foreign-library lib) url (str (deps/-url js opts))] - (if (and url (not (or (.endsWith url ".js") (.endsWith url ".json")))) - (assoc js :source "") + (if (and url (some (fn [ext] + (.endsWith url ext)) + (:ignore-js-module-exts opts))) + (do + (when (or ana/*verbose* (:verbose opts)) + (util/debug-prn "Ignoring JS module" url "based on the file extension")) + (assoc js :source "")) (assoc js :source (deps/-source js opts)))))) (map (fn [js] (if (:preprocess js) From b48023595f90b8567d64e1d1e8162ad12e7d0d18 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 24 Dec 2017 12:54:18 -0500 Subject: [PATCH 2768/4033] fix typo - :stable-names can now be true or a directory to write the var and property files --- src/main/clojure/cljs/closure.clj | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index be528fe60..b017b1364 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1297,7 +1297,7 @@ (util/mkdirs var-out) (io/copy (ByteArrayInputStream. (.toBytes var-map)) (io/file var-out)))) - (let [prop-out (:closure-variable-map-out opts)] + (let [prop-out (:closure-property-map-out opts)] (when-let [prop-map (and prop-out (.-propertyMap result))] (util/mkdirs prop-out) (io/copy (ByteArrayInputStream. (.toBytes prop-map)) @@ -2179,11 +2179,16 @@ (ensure-module-opts) (:stable-names opts) - (->> (merge - {:closure-variable-map-in (io/file output-dir "closure_var.map") - :closure-variable-map-out (io/file output-dir "closure_var.map") - :closure-property-map-in (io/file output-dir "closure_prop.map") - :closure-property-map-out (io/file output-dir "closure_prop.map")})) + (as-> opts + (let [out-dir (if (true? (:stable-names opts)) + output-dir + (:stable-names opts))] + (merge + {:closure-variable-map-in (io/file out-dir "closure_var.map") + :closure-variable-map-out (io/file out-dir "closure_var.map") + :closure-property-map-in (io/file out-dir "closure_prop.map") + :closure-property-map-out (io/file out-dir "closure_prop.map")} + opts))) (nil? (:ignore-js-module-exts opts)) (assoc :ignore-js-module-exts [".css"])))) From cb47e15e0f35a22415ccf704bc0da9fab4332177 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 27 Dec 2017 10:18:05 -0500 Subject: [PATCH 2769/4033] CLJS-2452: reverse empty vector returns nil --- src/main/cljs/cljs/core.cljs | 3 ++- src/test/cljs/cljs/collections_test.cljs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 418a1a0c2..4e3acbba9 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5611,7 +5611,8 @@ reduces them without incurring seq initialization" IReversible (-rseq [coll] (if (pos? cnt) - (RSeq. coll (dec cnt) nil))) + (RSeq. coll (dec cnt) nil) + ())) IIterable (-iterator [this] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 51514f2a1..ff93781f1 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -52,7 +52,6 @@ (is (= (nth pv 96) 96)) (is (= (nth pv 97 nil) nil)) (is (= (pv 96) 96)) - (is (nil? (rseq []))) (is (= (reverse pv) (rseq pv))))) (let [pv (vec (range 33))] (testing "pop" @@ -929,4 +928,7 @@ (is (= (find (hash-map :a 1) :a) [:a 1])) (is (= (find (hash-map :a false) :a) [:a false])) (is (= (find (zipmap (range 1000) (repeat :foo)) 999) [999 :foo])) - (is (= (find (zipmap (range 1000) (repeat :foo)) 1000) nil)))) \ No newline at end of file + (is (= (find (zipmap (range 1000) (repeat :foo)) 1000) nil)))) + +(deftest test-cljs-2452 + (is (= (reverse []) ()))) \ No newline at end of file From 92cc9f34d88b3aa31eff86c1f570af1162f44e4d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 27 Dec 2017 10:43:25 -0500 Subject: [PATCH 2770/4033] revert last commit except reverse test as it broke rseq on an empty vector instead just change reverse to follow Clojure's implementation for now so that (reverse []) -> () --- src/main/cljs/cljs/core.cljs | 9 +++------ src/test/cljs/cljs/collections_test.cljs | 3 ++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4e3acbba9..e9feb58cc 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3157,9 +3157,7 @@ reduces them without incurring seq initialization" (defn reverse "Returns a seq of the items in coll in reverse order. Not lazy." [coll] - (if (reversible? coll) - (rseq coll) - (reduce conj () coll))) + (reduce conj () coll)) (defn list "Creates a new list containing the items." @@ -5610,9 +5608,8 @@ reduces them without incurring seq initialization" IReversible (-rseq [coll] - (if (pos? cnt) - (RSeq. coll (dec cnt) nil) - ())) + (when (pos? cnt) + (RSeq. coll (dec cnt) nil))) IIterable (-iterator [this] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index ff93781f1..8d9a4a51f 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -52,7 +52,8 @@ (is (= (nth pv 96) 96)) (is (= (nth pv 97 nil) nil)) (is (= (pv 96) 96)) - (is (= (reverse pv) (rseq pv))))) + (is (= (reverse pv) (rseq pv))) + (is (nil? (rseq []))))) (let [pv (vec (range 33))] (testing "pop" (is (= pv (-> pv pop pop (conj 31) (conj 32)))))) From 4a24d18ca86ba9f41856cc37314cfa4d4797a3b1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 27 Dec 2017 10:52:10 -0500 Subject: [PATCH 2771/4033] use Mike Fike's tweak on reverse --- src/main/cljs/cljs/core.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e9feb58cc..be8a344fe 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3157,7 +3157,9 @@ reduces them without incurring seq initialization" (defn reverse "Returns a seq of the items in coll in reverse order. Not lazy." [coll] - (reduce conj () coll)) + (if (reversible? coll) + (or (rseq coll) ()) + (reduce conj () coll))) (defn list "Creates a new list containing the items." From 994069152cd8e8f1338602dd5e17fa63b2694bd9 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Thu, 28 Dec 2017 16:16:12 +1100 Subject: [PATCH 2772/4033] CLJS-1743: Transient maps should support IFn --- src/main/cljs/cljs/core.cljs | 16 ++++++++++++++-- src/test/cljs/cljs/collections_test.cljs | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index be8a344fe..b5607cb1b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7001,7 +7001,13 @@ reduces them without incurring seq initialization" (doto arr .pop .pop) (set! len (- len 2))) tcoll) - (throw (js/Error. "dissoc! after persistent!"))))) + (throw (js/Error. "dissoc! after persistent!")))) + + IFn + (-invoke [tcoll key] + (-lookup tcoll key nil)) + (-invoke [tcoll key not-found] + (-lookup tcoll key not-found))) (declare TransientHashMap) @@ -8013,7 +8019,13 @@ reduces them without incurring seq initialization" (-assoc! [tcoll key val] (.assoc! tcoll key val)) ITransientMap - (-dissoc! [tcoll key] (.without! tcoll key))) + (-dissoc! [tcoll key] (.without! tcoll key)) + + IFn + (-invoke [tcoll key] + (-lookup tcoll key)) + (-invoke [tcoll key not-found] + (-lookup tcoll key not-found))) ;;; PersistentTreeMap diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 8d9a4a51f..817c56aaf 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -932,4 +932,18 @@ (is (= (find (zipmap (range 1000) (repeat :foo)) 1000) nil)))) (deftest test-cljs-2452 - (is (= (reverse []) ()))) \ No newline at end of file + (is (= (reverse []) ()))) + +(deftest test-cljs-1743 + (testing "TransientArrayMap as an invokable function" + (let [tam (transient (array-map :a 1 :b 2))] + (is (= (tam :a) 1)) + (is (= (tam :a :not-found) 1)) + (is (= (tam :x) nil)) + (is (= (tam :x :not-found) :not-found)))) + (testing "TransientHashMap as an invokable function" + (let [thm (transient (hash-map :a 1 :b 2))] + (is (= (thm :a) 1)) + (is (= (thm :a :not-found) 1)) + (is (= (thm :x) nil)) + (is (= (thm :x :not-found) :not-found))))) From 11e645aef91a46adcf03ad22ce8398df26c2eeee Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Thu, 28 Dec 2017 18:08:35 +1100 Subject: [PATCH 2773/4033] CLJS-1793: fix misplaced docstrings --- src/main/clojure/cljs/closure.clj | 9 ++++++--- src/main/clojure/cljs/repl/reflect.clj | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b017b1364..63a07974a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1943,13 +1943,15 @@ (defn absolute-parent [path] (.getParent (.getAbsoluteFile (io/file path)))) -(defn in-same-dir? [path-1 path-2] +(defn in-same-dir? "Checks that path-1 and path-2 are siblings in the same logical directory." + [path-1 path-2] (= (absolute-parent path-1) (absolute-parent path-2))) -(defn same-or-subdirectory-of? [dir path] +(defn same-or-subdirectory-of? "Checks that path names a file or directory that is the dir or a subdirectory there of." + [dir path] (let [dir-path (.getAbsolutePath (io/file dir)) path-path (.getAbsolutePath (io/file path))] (.startsWith path-path dir-path))) @@ -1969,8 +1971,9 @@ (pr-str output-dir)))) true) -(defn check-source-map [{:keys [output-to source-map output-dir optimizations] :as opts}] +(defn check-source-map "When :source-map is specified in opts, " + [{:keys [output-to source-map output-dir optimizations] :as opts}] (when (and (contains? opts :source-map) (:source-map opts) (not (= optimizations :none))) diff --git a/src/main/clojure/cljs/repl/reflect.clj b/src/main/clojure/cljs/repl/reflect.clj index 215ecd6fc..48302db56 100644 --- a/src/main/clojure/cljs/repl/reflect.clj +++ b/src/main/clojure/cljs/repl/reflect.clj @@ -37,8 +37,9 @@ (update-in [:name] str) (update-in [:method-params] #(str (vec %))))))) -(defn macroexpand [form] +(defn macroexpand "Fully expands a cljs macro form." + [form] (let [mform (analyzer/macroexpand-1 {} form)] (if (identical? form mform) mform From 4c9af39640bab43253b34026e41c96d8fcf82dc0 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 29 Dec 2017 11:01:26 -0500 Subject: [PATCH 2774/4033] CLJS-2453: Improved re-seq termination for empty matches --- src/main/cljs/cljs/core.cljs | 5 +++-- src/test/cljs/cljs/core_test.cljs | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b5607cb1b..9c9d4436d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9795,8 +9795,9 @@ reduces them without incurring seq initialization" (let [match-data (re-find re s) match-idx (.search s re) match-str (if (coll? match-data) (first match-data) match-data) - post-match (subs s (+ match-idx (count match-str)))] - (when match-data (lazy-seq (cons match-data (when (seq post-match) (re-seq re post-match))))))) + post-idx (+ match-idx (max 1 (count match-str))) + post-match (subs s post-idx)] + (when match-data (lazy-seq (cons match-data (when (<= post-idx (count s)) (re-seq re post-match))))))) (defn re-pattern "Returns an instance of RegExp which has compiled the provided string." diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index e0a84c71d..f1d7a9942 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1503,6 +1503,19 @@ (deftest test-cljs-2283 (is (nil? (doseq [])))) +(deftest test-cljs-2453 + (is (= (re-seq #"[Bc]?" "aBcD") '("" "B" "c" "" ""))) + (is (= (re-seq #"[BcD]?$" "aBcD") '("D" ""))) + (is (= (map first (re-seq #"(\d+)" "ClojureScript 1.9.222")) '("1" "9" "222"))) + (is (= (re-seq #"\d+" "a1b2c3d") '("1" "2" "3"))) + (is (= (re-seq #"\d?" "a1b2c3d") '("" "1" "" "2" "" "3" "" ""))) + (is (= (re-seq #"\d*" "a1b2c3d") '("" "1" "" "2" "" "3" "" ""))) + (is (= (re-seq #"\d+" "a1b22c333d") '("1" "22" "333"))) + (is (= (re-seq #"\d?" "a1b22c333d") '("" "1" "" "2" "2" "" "3" "3" "3" "" ""))) + (is (= (re-seq #"\d*" "a1b22c333d") '("" "1" "" "22" "" "333" "" ""))) + (is (= (re-seq #"\w+" "once upon a time") '("once" "upon" "a" "time"))) + (is (nil? (re-seq #"\w+" "")))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 8de3e8acaff526bd05a6d3f52a9f1582b53d0f80 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Thu, 28 Dec 2017 17:53:21 +1100 Subject: [PATCH 2775/4033] CLJS-2131: Calling empty on a ChunkedSeq should return empty list --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/seqs_test.cljs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9c9d4436d..3512eb4bb 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5712,7 +5712,7 @@ reduces them without incurring seq initialization" IEmptyableCollection (-empty [coll] - (-with-meta (.-EMPTY PersistentVector) meta)) + ()) IChunkedSeq (-chunked-first [coll] diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 2390db754..3eb6839a5 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -203,3 +203,10 @@ (is (= :not-found (nth (seq (array 0 1 2 3 4 5)) 6 :not-found))) (is (thrown? js/Error (nth (seq (array 0 1 2 3 4 5)) -1))) (is (= :not-found (nth (seq (array 0 1 2 3 4 5)) -1 :not-found)))))) + +(deftest test-cljs-2131 + (testing "calling empty on a ChunkedSeq returns an empty list" + (let [small-vec [1 2 3] + big-vec (into [] (range 1000))] + (is (identical? (empty (seq small-vec)) ())) + (is (identical? (empty (seq big-vec)) ()))))) From 0ddafa7792efa1ef885b10c96f171a704890e7d7 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Sat, 30 Dec 2017 18:38:08 +1100 Subject: [PATCH 2776/4033] CLJS-2001: Add map-entry? predicate --- src/main/cljs/cljs/core.cljs | 5 +++++ src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3512eb4bb..63a608fe5 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6637,6 +6637,11 @@ reduces them without incurring seq initialization" (-invoke [node k not-found] (-nth node k not-found))) +(defn ^boolean map-entry? + "Returns true if x is a map entry" + [x] + (instance? MapEntry x)) + (deftype PersistentArrayMapSeq [arr i _meta] Object (toString [coll] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index f1d7a9942..d14a60e03 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1516,6 +1516,10 @@ (is (= (re-seq #"\w+" "once upon a time") '("once" "upon" "a" "time"))) (is (nil? (re-seq #"\w+" "")))) +(deftest test-cljs-2001 + (is (map-entry? (MapEntry. :key :val))) + (is (not (map-entry? [:key :val])))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 9f04cb2abec3d6425b35c001d6cd311119570a17 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 3 Jan 2018 15:57:24 -0500 Subject: [PATCH 2777/4033] CLJS-2457: For non-seqable types, seq evals to object info when running tests Force the tests that manipulate object to do this after all other tests have been executed. --- src/test/cljs/cljs/core_test.cljs | 24 +++-------------- src/test/cljs/cljs/extend_to_object_test.cljs | 26 +++++++++++++++++++ src/test/cljs/test_runner.cljs | 6 +++-- src/test/self/self_parity/test.cljs | 6 +++-- 4 files changed, 37 insertions(+), 25 deletions(-) create mode 100644 src/test/cljs/cljs/extend_to_object_test.cljs diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d14a60e03..1a69cf2b6 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -728,27 +728,6 @@ (deftest test-518 (is (nil? (:test "test")))) -;; r1798 core fn protocol regression -(extend-type object - ISeqable - (-seq [coll] - (map #(vector % (aget coll %)) (js-keys coll))) - - ILookup - (-lookup - ([coll k] - (-lookup coll k nil)) - ([coll k not-found] - (if-let [v (aget coll k)] - v - not-found)))) - -(deftest test-extend-to-object - (is (= (seq (js-obj "foo" 1 "bar" 2)) '(["foo" 1] ["bar" 2]))) - (is (= (get (js-obj "foo" 1) "foo") 1)) - (is (= (get (js-obj "foo" 1) "bar" ::not-found) ::not-found)) - (is (= (reduce (fn [s [k v]] (+ s v)) 0 (js-obj "foo" 1 "bar" 2)) 3))) - (deftest test-541 (letfn [(f! [x] (print \f) x) (g! [x] (print \g) x)] @@ -1520,6 +1499,9 @@ (is (map-entry? (MapEntry. :key :val))) (is (not (map-entry? [:key :val])))) +(deftest test-cljs-2457 + (is (thrown-with-msg? js/Error #".* is not ISeqable" (seq #js {:a 1 :b 2})))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) diff --git a/src/test/cljs/cljs/extend_to_object_test.cljs b/src/test/cljs/cljs/extend_to_object_test.cljs new file mode 100644 index 000000000..c02d26af6 --- /dev/null +++ b/src/test/cljs/cljs/extend_to_object_test.cljs @@ -0,0 +1,26 @@ +(ns cljs.extend-to-object-test + (:require [cljs.test :refer-macros [deftest is]])) + +;;; Note: The tests in this namespace manipulate object (at test +;;; run time) and this namespace should be loaded last by test +;;; runners so as to not affect other tests. + +;; r1798 core fn protocol regression +(deftest test-extend-to-object + (extend-type object + ISeqable + (-seq [coll] + (map #(vector % (aget coll %)) (js-keys coll))) + + ILookup + (-lookup + ([coll k] + (-lookup coll k nil)) + ([coll k not-found] + (if-let [v (aget coll k)] + v + not-found)))) + (is (= (seq (js-obj "foo" 1 "bar" 2)) '(["foo" 1] ["bar" 2]))) + (is (= (get (js-obj "foo" 1) "foo") 1)) + (is (= (get (js-obj "foo" 1) "bar" ::not-found) ::not-found)) + (is (= (reduce (fn [s [k v]] (+ s v)) 0 (js-obj "foo" 1 "bar" 2)) 3))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index f004b286b..f0801780d 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -44,7 +44,8 @@ [cljs.test-test] [static.core-test] [cljs.recur-test] - [cljs.array-access-test])) + [cljs.array-access-test] + [cljs.extend-to-object-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -86,4 +87,5 @@ 'cljs.test-test 'static.core-test 'cljs.recur-test - 'cljs.array-access-test) + 'cljs.array-access-test + 'cljs.extend-to-object-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index cf50b221f..e83b1bcbb 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -370,7 +370,8 @@ [cljs.test-test] [static.core-test] [cljs.recur-test] - [cljs.array-access-test])) + [cljs.array-access-test] + [cljs.extend-to-object-test])) (fn [{:keys [value error]}] (if error (handle-error error (:source-maps @st)) @@ -410,7 +411,8 @@ 'cljs.test-test 'static.core-test 'cljs.recur-test - 'cljs.array-access-test) + 'cljs.array-access-test + 'cljs.extend-to-object-test) (fn [{:keys [value error]}] (when error (handle-error error (:source-maps @st))))))))) From 983b7fd6e17728b5947a398176bc60aa48ed04b8 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 3 Jan 2018 16:13:04 -0500 Subject: [PATCH 2778/4033] CLJS-2458: MapEntry test is passing wrong number of args --- src/test/cljs/cljs/core_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 1a69cf2b6..88077c6d0 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1496,7 +1496,7 @@ (is (nil? (re-seq #"\w+" "")))) (deftest test-cljs-2001 - (is (map-entry? (MapEntry. :key :val))) + (is (map-entry? (MapEntry. :key :val 0))) (is (not (map-entry? [:key :val])))) (deftest test-cljs-2457 From cc2ad0d9a7d34973a71c3dda73e66cbbea21212e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 3 Jan 2018 09:47:15 -0500 Subject: [PATCH 2779/4033] CLJS-2455: nth fails on eduction This change makes ClojureScript follow Clojure's nth's support for sequential types that in that coercion should be attempted by calling seq. --- src/main/cljs/cljs/core.cljs | 6 ++++-- src/test/cljs/cljs/core_test.cljs | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 63a608fe5..e0d4d0bcf 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1853,7 +1853,8 @@ reduces them without incurring seq initialization" (.charAt coll n) (throw (js/Error. "Index out of bounds"))) - (implements? ISeq coll) + (or (implements? ISeq coll) + (implements? ISequential coll)) (linear-traversal-nth coll n) (native-satisfies? IIndexed coll) @@ -1883,7 +1884,8 @@ reduces them without incurring seq initialization" (.charAt coll n) not-found) - (implements? ISeq coll) + (or (implements? ISeq coll) + (implements? ISequential coll)) (linear-traversal-nth coll n not-found) (native-satisfies? IIndexed coll) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 88077c6d0..92db48b0e 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1499,6 +1499,17 @@ (is (map-entry? (MapEntry. :key :val 0))) (is (not (map-entry? [:key :val])))) +(deftype Foo2455 [] + ISequential) + +(deftest test-cljs-2455 + (is (= :x (nth (eduction [:x]) 0))) + (is (thrown-with-msg? js/Error #"Index out of bounds" (nth (eduction [:x]) 1))) + (is (= :x (nth (eduction [:x]) 0 :not-found))) + (is (= :not-found (nth (eduction [:x]) 1 :not-found))) + ;; Calling nth on a type satisfying ISequential should attempt coercion + (is (thrown-with-msg? js/Error #".* is not ISeqable" (nth (->Foo2455) 0)))) + (deftest test-cljs-2457 (is (thrown-with-msg? js/Error #".* is not ISeqable" (seq #js {:a 1 :b 2})))) From 50410be605c479e98aa9e978e9e6ac89543ec36c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 12 Jan 2018 15:44:03 -0500 Subject: [PATCH 2780/4033] add deps.edn file --- deps.edn | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 deps.edn diff --git a/deps.edn b/deps.edn new file mode 100644 index 000000000..ccd7b911e --- /dev/null +++ b/deps.edn @@ -0,0 +1,14 @@ +{:paths ["src/main/clojure" "src/main/cljs"] + :deps + {org.clojure/clojure {:mvn/version "1.9.0"} + org.clojure/tools.reader {:mvn/version "1.1.1"} + org.clojure/test.check {:mvn/version "0.10.0-alpha2"} + org.clojure/spec.alpha {:mvn/version "0.1.143"} + org.clojure/core.specs.alpha {:mvn/version "0.1.24"} + org.clojure/data.json {:mvn/version "0.2.6"} + com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180101"} + org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"} + org.mozilla/rhino {:mvn/version "1.7R5"}} + :aliases + {:test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" + "src/test/clojure" "src/test/self"]}}} \ No newline at end of file From 56da266bcd5357e437f12d4b36e41b0fbaae230f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 16 Jan 2018 20:52:54 -0500 Subject: [PATCH 2781/4033] CLJS-2473: Infer character literals to have string type --- src/main/clojure/cljs/analyzer.cljc | 1 + src/test/clojure/cljs/analyzer_tests.clj | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f13fc15c5..cc17dfb66 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3555,6 +3555,7 @@ (nil? form) 'clj-nil (number? form) 'number (string? form) 'string + (instance? Character form) 'string (true? form) 'boolean (false? form) 'boolean)] (cond-> {:op :constant :env env :form form} diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 0e9167a09..1c53955bb 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -188,6 +188,9 @@ (is (= (e/with-compiler-env test-cenv (:tag (a/analyze test-env '"foo"))) 'string)) + (is (= (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '\a))) + 'string)) (is (= (e/with-compiler-env test-cenv (:tag (a/analyze test-env '(make-array 10)))) 'array)) From a68dd3b01fbb4ab83cc27ba7f13dfe7765a407bf Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 19 Jan 2018 16:46:07 -0500 Subject: [PATCH 2782/4033] bump tools.reader to 1.1.3.1 --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index ccd7b911e..49e3a9551 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,7 @@ {:paths ["src/main/clojure" "src/main/cljs"] :deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.clojure/tools.reader {:mvn/version "1.1.1"} + org.clojure/tools.reader {:mvn/version "1.1.3.1"} org.clojure/test.check {:mvn/version "0.10.0-alpha2"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} diff --git a/pom.template.xml b/pom.template.xml index e5a29e5fb..e6d0e5961 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.1.0 + 1.1.3.1 org.clojure diff --git a/project.clj b/project.clj index c25caa8c4..750fa7b5c 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :dependencies [[org.clojure/clojure "1.9.0"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.1.0"] + [org.clojure/tools.reader "1.1.3.1"] [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] diff --git a/script/bootstrap b/script/bootstrap index 347ddb9fb..4b72b3610 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,13 +10,15 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.1.0" +TREADER_RELEASE="1.1.3.1" TEST_CHECK_RELEASE="0.10.0-alpha2" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } unzip -v >/dev/null || { echo "The 'unzip' utility is missing, or not on your system path."; exit 1; } +rm -rf lib + mkdir -p lib echo "Fetching Clojure..." From 9ddd356d344aa1ebf9bd9443dd36a1911c92d32f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 19 Jan 2018 16:25:24 -0500 Subject: [PATCH 2783/4033] CLJS-2171: Non deterministic compilation failure --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cc17dfb66..a6b33f134 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3879,8 +3879,9 @@ (defn- get-spec-vars [] (when-let [spec-ns (find-ns 'cljs.spec.alpha)] - {:registry-ref (ns-resolve spec-ns 'registry-ref) - :speced-vars (ns-resolve spec-ns '_speced_vars)})) + (locking load-mutex + {:registry-ref (ns-resolve spec-ns 'registry-ref) + :speced-vars (ns-resolve spec-ns '_speced_vars)}))) :cljs (let [registry-ref (delay (get (ns-interns* 'cljs.spec.alpha$macros) 'registry-ref)) ;; Here, we look up the symbol '-speced-vars because ns-interns* From 6ba7aaba9f4bc3aa35172a5568e4c126cea42f5a Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Sun, 7 Jan 2018 10:38:17 +1100 Subject: [PATCH 2784/4033] CLJS-2456: Have map types return MapEntry instead of vector seq and iterate operations on PAM and PHM return MapEntrys. Collection which implement 'IFind' also return MapEntry. --- src/main/cljs/cljs/core.cljs | 40 ++++++++++++------------ src/test/cljs/cljs/collections_test.cljs | 20 ++++++++++++ 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e0d4d0bcf..cd3c122d3 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1945,7 +1945,7 @@ reduces them without incurring seq initialization" :else not-found) not-found))) -(declare PersistentHashMap PersistentArrayMap) +(declare PersistentHashMap PersistentArrayMap MapEntry) (defn assoc "assoc[iate]. When applied to a map, returns a new map of the @@ -2322,7 +2322,7 @@ reduces them without incurring seq initialization" (when (and (not (nil? coll)) (associative? coll) (contains? coll k)) - [k (get coll k)]))) + (MapEntry. k (get coll k) nil)))) (defn ^boolean distinct? "Returns true if no two of the arguments are =" @@ -5547,7 +5547,7 @@ reduces them without incurring seq initialization" IFind (-find [coll n] (when (and (<= 0 n) (< n cnt)) - [n (aget (unchecked-array-for coll n) (bit-and n 0x01f))])) + (MapEntry. n (aget (unchecked-array-for coll n) (bit-and n 0x01f)) nil))) APersistentVector IVector @@ -5841,7 +5841,7 @@ reduces them without incurring seq initialization" (when-not (neg? n) (let [idx (+ start n)] (when (< idx end) - [n (-lookup v idx)])))) + (MapEntry. n (-lookup v idx) nil))))) IVector (-assoc-n [coll n val] @@ -6387,7 +6387,7 @@ reduces them without incurring seq initialization" (-find [coll k] (when (and ^boolean (goog/isString k) (not (nil? (scan-array 1 k keys)))) - [k (unchecked-get strobj k)])) + (MapEntry. k (unchecked-get strobj k) nil))) IKVReduce (-kv-reduce [coll f init] @@ -6617,8 +6617,8 @@ reduces them without incurring seq initialization" IFind (-find [node k] (case k - 0 [0 key] - 1 [1 val] + 0 (MapEntry. 0 key nil) + 1 (MapEntry. 1 val nil) nil)) IVector @@ -6689,7 +6689,7 @@ reduces them without incurring seq initialization" ISeq (-first [coll] - [(aget arr i) (aget arr (inc i))]) + (MapEntry. (aget arr i) (aget arr (inc i)) nil)) (-rest [coll] (if (< i (- (alength arr) 2)) @@ -6718,7 +6718,7 @@ reduces them without incurring seq initialization" (hasNext [_] (< i cnt)) (next [_] - (let [ret [(aget arr i) (aget arr (inc i))]] + (let [ret (MapEntry. (aget arr i) (aget arr (inc i)) nil)] (set! i (+ i 2)) ret))) @@ -6838,7 +6838,7 @@ reduces them without incurring seq initialization" (-find [coll k] (let [idx (array-map-index-of coll k)] (when-not (== idx -1) - [(aget arr idx) (aget arr (inc idx))]))) + (MapEntry. (aget arr idx) (aget arr (inc idx)) nil)))) IMap (-dissoc [coll k] @@ -7100,7 +7100,7 @@ reduces them without incurring seq initialization" node-or-val (aget arr (inc i)) ^boolean found (cond (some? key) - (set! next-entry [key node-or-val]) + (set! next-entry (MapEntry. key node-or-val nil)) (some? node-or-val) (let [new-iter (-iterator node-or-val)] (if ^boolean (.hasNext new-iter) @@ -7212,7 +7212,7 @@ reduces them without incurring seq initialization" key-or-nil (aget arr (* 2 idx)) val-or-node (aget arr (inc (* 2 idx)))] (cond (nil? key-or-nil) (.inode-find val-or-node (+ shift 5) hash key not-found) - (key-test key key-or-nil) [key-or-nil val-or-node] + (key-test key key-or-nil) (MapEntry. key-or-nil val-or-node nil) :else not-found))))) (inode-seq [inode] @@ -7510,7 +7510,7 @@ reduces them without incurring seq initialization" (inode-find [inode shift hash key not-found] (let [idx (hash-collision-node-find-index arr cnt key)] (cond (< idx 0) not-found - (key-test key (aget arr idx)) [(aget arr idx) (aget arr (inc idx))] + (key-test key (aget arr idx)) (MapEntry. (aget arr idx) (aget arr (inc idx)) nil) :else not-found))) (inode-seq [inode] @@ -7624,7 +7624,7 @@ reduces them without incurring seq initialization" ISeq (-first [coll] (if (nil? s) - [(aget nodes i) (aget nodes (inc i))] + (MapEntry. (aget nodes i) (aget nodes (inc i)) nil) (first s))) (-rest [coll] @@ -7856,7 +7856,7 @@ reduces them without incurring seq initialization" IFind (-find [coll k] (cond - (nil? k) (when has-nil? [nil nil-val]) + (nil? k) (when has-nil? (MapEntry. nil nil-val nil)) (nil? root) nil :else (.inode-find root 0 (hash k) k nil))) @@ -8313,8 +8313,8 @@ reduces them without incurring seq initialization" IFind (-find [node k] (case k - 0 [0 key] - 1 [1 val] + 0 (MapEntry. 0 key nil) + 1 (MapEntry. 1 val nil) nil)) IVector @@ -8474,8 +8474,8 @@ reduces them without incurring seq initialization" IFind (-find [node k] (case k - 0 [0 key] - 1 [1 val] + 0 (MapEntry. 0 key nil) + 1 (MapEntry. 1 val nil) nil)) IVector @@ -9009,7 +9009,7 @@ reduces them without incurring seq initialization" (.hasNext iter)) (next [_] (if ^boolean (.hasNext iter) - (aget (.-tail (.next iter)) 0) + (.-key (.next iter)) (throw (js/Error. "No such element")))) (remove [_] (js/Error. "Unsupported operation"))) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 817c56aaf..169fc3d76 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -947,3 +947,23 @@ (is (= (thm :a :not-found) 1)) (is (= (thm :x) nil)) (is (= (thm :x :not-found) :not-found))))) + +(deftest test-cljs-2456 + (testing "Maps" + (testing "PersistentArrayMap" + (let [pam (array-map :a 1 :b 2 :c 3)] + (is (map-entry? (first pam))) + (is (every? map-entry? pam)) + (is (map-entry? (find pam :a))) + (is (map-entry? (.next (-iterator pam)))))) + (testing "PersistentHashMap" + (let [phm (hash-map :a 1 :b 2 :c 3)] + (is (map-entry? (first phm))) + (is (every? map-entry? phm)) + (is (map-entry? (find phm :a))) + (is (map-entry? (.next (-iterator phm))))))) + (testing "Vectors" + (testing "PersistentVector" + (is (map-entry? (find [0 1 2] 0)))) + (testing "MapEntry" + (is (map-entry? (find (MapEntry. :key :val nil) 0)))))) From d1dc3c53c778772b176fc2163bf090f56e2a75e6 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 24 Jan 2018 22:07:00 -0500 Subject: [PATCH 2785/4033] CLJS-2477: empty map entry should be nil - Updates all map entry implementations to return nil - Adds a case to clojure.walk/walk (otherwise nil return value will be mishandled) - Adds a case to js->clj (otherwise nil return value will be mishandled) --- src/main/cljs/cljs/core.cljs | 9 ++++++--- src/main/cljs/clojure/walk.cljs | 11 ++++++----- src/test/cljs/cljs/core_test.cljs | 4 +++- src/test/cljs/cljs/map_entry_test.cljs | 2 +- src/test/cljs/clojure/walk_test.cljs | 3 +++ 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index cd3c122d3..4ac6333fa 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6584,7 +6584,7 @@ reduces them without incurring seq initialization" (-conj [node o] [key val o]) IEmptyableCollection - (-empty [node] []) + (-empty [node] nil) ISequential ISeqable @@ -8280,7 +8280,7 @@ reduces them without incurring seq initialization" (-conj [node o] [key val o]) IEmptyableCollection - (-empty [node] []) + (-empty [node] nil) ISequential ISeqable @@ -8441,7 +8441,7 @@ reduces them without incurring seq initialization" (-conj [node o] [key val o]) IEmptyableCollection - (-empty [node] []) + (-empty [node] nil) ISequential ISeqable @@ -10539,6 +10539,9 @@ reduces them without incurring seq initialization" (seq? x) (doall (map thisfn x)) + (map-entry? x) + (MapEntry. (thisfn (key x)) (thisfn (val x)) nil) + (coll? x) (into (empty x) (map thisfn x)) diff --git a/src/main/cljs/clojure/walk.cljs b/src/main/cljs/clojure/walk.cljs index 62a9d3a80..ffbb6a7c2 100644 --- a/src/main/cljs/clojure/walk.cljs +++ b/src/main/cljs/clojure/walk.cljs @@ -43,11 +43,12 @@ the sorting function."} {:added "1.1"} [inner outer form] (cond - (list? form) (outer (apply list (map inner form))) - (seq? form) (outer (doall (map inner form))) - (record? form) (outer (reduce (fn [r x] (conj r (inner x))) form form)) - (coll? form) (outer (into (empty form) (map inner form))) - :else (outer form))) + (list? form) (outer (apply list (map inner form))) + (map-entry? form) (outer (vec (map inner form))) + (seq? form) (outer (doall (map inner form))) + (record? form) (outer (reduce (fn [r x] (conj r (inner x))) form form)) + (coll? form) (outer (into (empty form) (map inner form))) + :else (outer form))) (defn postwalk "Performs a depth-first, post-order traversal of form. Calls f on diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 92db48b0e..172fc1572 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -136,7 +136,9 @@ (js->clj (js* "[[{\"a\":1,\"b\":2}, {\"a\":1,\"b\":2}]]") :keywordize-keys true))) (is (= [[{:a 1, :b 2} {:a 1, :b 2}]] (js->clj [[{:a 1, :b 2} {:a 1, :b 2}]]))) - (is (= (js->clj nil) nil))) + (is (= (js->clj nil) nil)) + (let [map-entry (->MapEntry #js {:foo 1} #js [1 2] nil)] + (is (= (->MapEntry {"foo" 1} [1 2] nil) (js->clj map-entry))))) (testing "clj->js" (is (= (clj->js 'a) "a")) (is (= (clj->js :a) "a")) diff --git a/src/test/cljs/cljs/map_entry_test.cljs b/src/test/cljs/cljs/map_entry_test.cljs index f30dae04d..e835e53b0 100644 --- a/src/test/cljs/cljs/map_entry_test.cljs +++ b/src/test/cljs/cljs/map_entry_test.cljs @@ -55,7 +55,7 @@ (testing "IEmptyableCollection" (testing "-empty" - (is (= [] (empty e))))) + (is (= (if (instance? PersistentVector e) [] nil) (empty e))))) (testing "ISequential" (is (satisfies? ISequential e))) diff --git a/src/test/cljs/clojure/walk_test.cljs b/src/test/cljs/clojure/walk_test.cljs index 6743cf1c8..11d41312b 100644 --- a/src/test/cljs/clojure/walk_test.cljs +++ b/src/test/cljs/clojure/walk_test.cljs @@ -35,3 +35,6 @@ (is (= (-> (w/postwalk identity [1 (with-meta [1 2] {:foo 3})]) (nth 1) meta) {:foo 3})))) + +(deftest test-map-entry + (is (= [:a 2] (clojure.walk/postwalk #(cond-> % (number? %) inc) (->MapEntry :a 1 nil))))) From 5daa57293dc97f7d1a6abafb5aa977365e286d61 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 25 Jan 2018 08:47:48 -0500 Subject: [PATCH 2786/4033] CLJS-2482: Return MapEntry for seq on defrecord --- src/main/clojure/cljs/core.cljc | 2 +- src/test/cljs/cljs/seqs_test.cljs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 0c31bf6b6..a6d6ca2dd 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1837,7 +1837,7 @@ (not-empty (dissoc ~'__extmap k#)) nil))) 'ISeqable - `(~'-seq [this#] (seq (concat [~@(map #(core/list `vector (keyword %) %) base-fields)] + `(~'-seq [this#] (seq (concat [~@(map #(core/list `MapEntry. (keyword %) % nil) base-fields)] ~'__extmap))) 'IIterable diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 3eb6839a5..b4ad4d173 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -210,3 +210,9 @@ big-vec (into [] (range 1000))] (is (identical? (empty (seq small-vec)) ())) (is (identical? (empty (seq big-vec)) ()))))) + +(defrecord Foo [a b]) + +(deftest test-cljs-2482 + (testing "seq on defrecord returns map entries" + (is (every? map-entry? (seq (->Foo 1 2)))))) From 620ac16d93a91786e1c636fac1269ed66087aa03 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 26 Jan 2018 15:26:45 -0500 Subject: [PATCH 2787/4033] CLJS-2478: map-entry? should return true for sorted-map --- src/main/cljs/cljs/core.cljs | 28 ++++++++++++++---------- src/test/cljs/cljs/collections_test.cljs | 9 ++++++++ src/test/cljs/cljs/map_entry_test.cljs | 2 -- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4ac6333fa..f3e77c61a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5528,12 +5528,6 @@ reduces them without incurring seq initialization" (-nth coll k not-found) not-found)) - IMapEntry - (-key [coll] - (-nth coll 0)) - (-val [coll] - (-nth coll 1)) - IAssociative (-assoc [coll k v] (if (number? k) @@ -6640,9 +6634,9 @@ reduces them without incurring seq initialization" (-nth node k not-found))) (defn ^boolean map-entry? - "Returns true if x is a map entry" + "Returns true if x satisfies IMapEntry" [x] - (instance? MapEntry x)) + (implements? IMapEntry x)) (deftype PersistentArrayMapSeq [arr i _meta] Object @@ -6966,8 +6960,14 @@ reduces them without incurring seq initialization" ITransientCollection (-conj! [tcoll o] (if editable? - (if (satisfies? IMapEntry o) + (cond + (map-entry? o) (-assoc! tcoll (key o) (val o)) + + (vector? o) + (-assoc! tcoll (o 0) (o 1)) + + :else (loop [es (seq o) tcoll tcoll] (if-let [e (first es)] (recur (next es) @@ -7933,8 +7933,14 @@ reduces them without incurring seq initialization" Object (conj! [tcoll o] (if edit - (if (satisfies? IMapEntry o) + (cond + (map-entry? o) (.assoc! tcoll (key o) (val o)) + + (vector? o) + (.assoc! tcoll (o 0) (o 1)) + + :else (loop [es (seq o) tcoll tcoll] (if-let [e (first es)] (recur (next es) @@ -9912,7 +9918,7 @@ reduces them without incurring seq initialization" (-write writer "#js ") (print-map (map (fn [k] - [(cond-> k (some? (re-matches #"[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*" k)) keyword) (unchecked-get obj k)]) + (MapEntry. (cond-> k (some? (re-matches #"[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*" k)) keyword) (unchecked-get obj k) nil)) (js-keys obj)) pr-writer writer opts)) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 169fc3d76..bff0737f4 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -934,6 +934,15 @@ (deftest test-cljs-2452 (is (= (reverse []) ()))) +(deftest test-cljs-2478 + (is (not (map-entry? [:a 1]))) + (is (= {:a 1 :b 2 :c 3} (into (hash-map :a 1) [[:b 2] [:c 3]]))) + (is (= {:a 1 :b 2 :c 3} (into (hash-map :a 1) {:b 2 :c 3}))) + (is (= {:a 1 :b 2 :c 3} (into (hash-map :a 1) (seq {:b 2 :c 3})))) + (is (= {:a 1 :b 2 :c 3} (into (array-map :a 1) [[:b 2] [:c 3]]))) + (is (= {:a 1 :b 2 :c 3} (into (array-map :a 1) {:b 2 :c 3}))) + (is (= {:a 1 :b 2 :c 3} (into (array-map :a 1) (seq {:b 2 :c 3}))))) + (deftest test-cljs-1743 (testing "TransientArrayMap as an invokable function" (let [tam (transient (array-map :a 1 :b 2))] diff --git a/src/test/cljs/cljs/map_entry_test.cljs b/src/test/cljs/cljs/map_entry_test.cljs index e835e53b0..26af0169c 100644 --- a/src/test/cljs/cljs/map_entry_test.cljs +++ b/src/test/cljs/cljs/map_entry_test.cljs @@ -139,7 +139,5 @@ (map-entry-interface-tests (BlackNode. :key :val nil nil nil))) (testing "RedNode" (map-entry-interface-tests (RedNode. :key :val nil nil nil))) - (testing "Vector" - (map-entry-interface-tests [:key :val])) (testing "MapEntry" (map-entry-interface-tests (MapEntry. :key :val nil)))) From 15343585996c1266c15bc54d20c176f8a7b789c7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 8 Jan 2018 09:20:30 -0500 Subject: [PATCH 2788/4033] CLJS-2460: Print MapEntry as vector --- src/main/cljs/cljs/core.cljs | 3 +++ src/test/cljs/cljs/collections_test.cljs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f3e77c61a..e7ff9bb0d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10169,6 +10169,9 @@ reduces them without incurring seq initialization" RedNode (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "[" " " "]" opts coll)) + 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)) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index bff0737f4..1dfc9b77e 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -898,6 +898,11 @@ (let [map (sorted-map nil :foo)] (is (= (find map nil) [nil :foo]))))) +(deftest cljs-2460 + (is (= "[:a 1]" (pr-str (->MapEntry :a 1 nil)))) + (binding [*print-length* 1] + (is (= "[:a ...]" (pr-str (->MapEntry :a 1 nil)))))) + (deftype CustomVectorThing [v] ;; Subvec expects its argument to implement IVector. ;; Note, that this method is never actually called. From 91431bd556f7a11db59319fcc082737a448f651e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 26 Jan 2018 22:05:20 -0500 Subject: [PATCH 2789/4033] CLJS-2484: Need rseq for map entry --- src/main/cljs/cljs/core.cljs | 9 +++++++++ src/test/cljs/cljs/map_entry_test.cljs | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e7ff9bb0d..489ee5d4e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6584,6 +6584,9 @@ reduces them without incurring seq initialization" ISeqable (-seq [node] (list key val)) + IReversible + (-rseq [node] (list val key)) + ICounted (-count [node] 2) @@ -8292,6 +8295,9 @@ reduces them without incurring seq initialization" ISeqable (-seq [node] (list key val)) + IReversible + (-rseq [node] (list val key)) + ICounted (-count [node] 2) @@ -8453,6 +8459,9 @@ reduces them without incurring seq initialization" ISeqable (-seq [node] (list key val)) + IReversible + (-rseq [node] (list val key)) + ICounted (-count [node] 2) diff --git a/src/test/cljs/cljs/map_entry_test.cljs b/src/test/cljs/cljs/map_entry_test.cljs index 26af0169c..809b567e4 100644 --- a/src/test/cljs/cljs/map_entry_test.cljs +++ b/src/test/cljs/cljs/map_entry_test.cljs @@ -64,6 +64,10 @@ (testing "-seq" (is (= (list :key :val) (-seq e))))) + (testing "IReversible" + (testing "-rseq" + (is (= (list :val :key) (-rseq e))))) + (testing "ICounted" (testing "-count" (is (= 2 (-count e))))) From b2a003b2efbb0445eb836e99a8aaeff81db75ec0 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Fri, 26 Jan 2018 13:21:00 +0000 Subject: [PATCH 2790/4033] CLJS-2488: use MapEntry for tagged returns in spec.alpha/conform --- src/main/cljs/cljs/spec/alpha.cljs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index f784ba040..adad18c7c 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -604,11 +604,8 @@ (with-gen* [_ gfn] (tuple-impl forms preds gfn)) (describe* [_] `(tuple ~@forms)))))) -(defn- tagged-ret [v] - (specify! v - IMapEntry - (-key [_] (-nth v 0)) - (-val [_] (-nth v 1)))) +(defn- tagged-ret [tag ret] + (MapEntry. tag ret nil)) (defn ^:skip-wiki or-spec-impl "Do not call this directly, use 'or'" @@ -624,8 +621,8 @@ (let [ret (conform* (specs 1) x)] (if (invalid? ret) ::invalid - (tagged-ret [(keys 1) ret]))) - (tagged-ret [(keys 0) ret])))) + (tagged-ret (keys 1) ret))) + (tagged-ret (keys 0) ret)))) 3 (fn [x] (let [specs @specs ret (conform* (specs 0) x)] @@ -635,9 +632,9 @@ (let [ret (conform* (specs 2) x)] (if (invalid? ret) ::invalid - (tagged-ret [(keys 2) ret]))) - (tagged-ret [(keys 1) ret]))) - (tagged-ret [(keys 0) ret])))) + (tagged-ret (keys 2) ret))) + (tagged-ret (keys 1) ret))) + (tagged-ret (keys 0) ret)))) (fn [x] (let [specs @specs] (loop [i 0] @@ -646,7 +643,7 @@ (let [ret (conform* spec x)] (if (invalid? ret) (recur (inc i)) - (tagged-ret [(keys i) ret])))) + (tagged-ret (keys i) ret)))) ::invalid)))))] (reify Specize @@ -985,7 +982,7 @@ (if (nil? pr) (if k1 (if (accept? p1) - (accept (tagged-ret [k1 (:ret p1)])) + (accept (tagged-ret k1 (:ret p1))) ret) p1) ret))))) @@ -1037,7 +1034,7 @@ ::pcat (add-ret p0 ret k) ::alt (let [[[p0] [k0]] (filter-alt ps ks forms accept-nil?) r (if (nil? p0) ::nil (preturn p0))] - (if k0 (tagged-ret [k0 r]) r))))) + (if k0 (tagged-ret k0 r) r))))) (defn- op-unform [p x] ;;(prn {:p p :x x}) From d95705b92fbdb04165a990382f27d865c152da43 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 2 Feb 2018 11:29:40 +0000 Subject: [PATCH 2791/4033] add Nashorn bootstrap script so that Nashorn environment gets standard JS event loop helpers. Load Nashorn bootstrap script in Nashorn REPL --- src/main/cljs/cljs/bootstrap_nashorn.js | 59 +++++++++++++++++++++++++ src/main/clojure/cljs/repl/nashorn.clj | 1 + 2 files changed, 60 insertions(+) create mode 100644 src/main/cljs/cljs/bootstrap_nashorn.js diff --git a/src/main/cljs/cljs/bootstrap_nashorn.js b/src/main/cljs/cljs/bootstrap_nashorn.js new file mode 100644 index 000000000..8614ed8b7 --- /dev/null +++ b/src/main/cljs/cljs/bootstrap_nashorn.js @@ -0,0 +1,59 @@ +// https://blogs.oracle.com/nashorn/setinterval-and-settimeout-javascript-functions + +var Platform = Java.type("javafx.application.Platform"); +var JFXPanel = Java.type("javafx.embed.swing.JFXPanel"); +var Timer = Java.type("java.util.Timer"); +var init = new JFXPanel(); // need to invoke to init JFX so Platform.runLater works + +function setTimerRequest(handler, delay, interval, args) { + handler = handler || function() {}; + delay = delay || 0; + interval = interval || 0; + var applyHandler = function() { handler.apply(this, args); } + var runLater = function() { Platform.runLater(applyHandler); } + var timer = new Timer("setTimerRequest", true); + if (interval > 0) { + timer.schedule(runLater, delay, interval); + } else { + timer.schedule(runLater, delay); + } + return timer; +} + +function clearTimerRequest(timer) { + timer.cancel(); +} + +function setInterval() { + var args = Array.prototype.slice.call(arguments); + var handler = args.shift(); + var ms = args.shift(); + return setTimerRequest(handler, ms, ms, args); +} + +function clearInterval(timer) { + clearTimerRequest(timer); +} + +function setTimeout() { + var args = Array.prototype.slice.call(arguments); + var handler = args.shift(); + var ms = args.shift(); + + return setTimerRequest(handler, ms, 0, args); +} + +function clearTimeout(timer) { + clearTimerRequest(timer); +} + +function setImmediate() { + var args = Array.prototype.slice.call(arguments); + var handler = args.shift(); + + return setTimerRequest(handler, 0, 0, args); +} + +function clearImmediate(timer) { + clearTimerRequest(timer); +} \ No newline at end of file diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index a96817a64..e7aa1f8db 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -91,6 +91,7 @@ (assoc opts :output-to (.getPath (io/file output-dir deps-file))) deps) ;; load the deps file so we can goog.require cljs.core etc. + (eval-resource engine "cljs/bootstrap_nashorn.js" false) (load-js-file engine deps-file)))) (defn load-ns [engine ns] From f08bfe0dda0b8938923d96d1f474db7f62fd15a2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 2 Feb 2018 14:40:02 +0000 Subject: [PATCH 2792/4033] CLI support WIP. -e, --eval now works with the standard REPLs --- src/main/clojure/cljs/cli.clj | 82 +++++++++++++++++++++++++ src/main/clojure/cljs/repl/browser.clj | 5 +- src/main/clojure/cljs/repl/nashorn.clj | 13 ++-- src/main/clojure/cljs/repl/node.clj | 6 +- src/main/clojure/cljs/repl/node_repl.js | 2 - src/main/clojure/cljs/repl/rhino.clj | 5 +- 6 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 src/main/clojure/cljs/cli.clj diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj new file mode 100644 index 000000000..97b820f44 --- /dev/null +++ b/src/main/clojure/cljs/cli.clj @@ -0,0 +1,82 @@ +;; 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.cli + (:require [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] + [cljs.repl :as repl] + [cljs.env :as env]) + (:import [java.io StringReader])) + +(defn repl-opt + "Start a repl with args and inits. Print greeting if no eval options were +present" + [repl-env [_ & args] inits] + (repl/repl (repl-env))) + +(defn- eval-opt + "Evals expressions in str, prints each non-nil result using prn" + [repl-env str] + ;; TODO: use forms-seq instead of read-string + (env/ensure + (let [renv (repl-env) + forms (ana-api/forms-seq (StringReader. str))] + (repl/-setup renv (repl/-repl-options renv)) + (doseq [form forms] + (println + (repl/evaluate-form renv (ana/empty-env) "" form))) + (repl/-tear-down renv)))) + +(defn main-opt + "Call the -main function from a namespace with string arguments from + the command line." + [repl-env [_ main-ns & args] inits] + ;; NOT YET IMPLEMENTED + ) + +(defn init-dispatch + "Returns the handler associated with an init opt" + [repl-env opt] + ;; NOT YET IMPLEMENTED + ({"-e" (partial eval-opt repl-env) + "--eval" (partial eval-opt repl-env)} opt)) + +(defn- initialize + "Common initialize routine for repl, script, and null opts" + [repl-env args inits] + (doseq [[opt arg] inits] + ((init-dispatch repl-env opt) arg))) + +(defn- null-opt + "No repl or script opt present, just bind args and run inits" + [repl-env args inits] + (initialize repl-env args inits)) + +(defn main-dispatch + "Returns the handler associated with a main option" + [repl-env opt] + ({"-r" (partial repl-opt repl-env) + "--repl" (partial repl-opt repl-env) + ;"-m" main-opt + ;"--main" main-opt + nil (partial null-opt repl-env) + ;"-h" help-opt + ;"--help" help-opt + ;"-?" help-opt + } opt)) + +(defn main [repl-env & args] + (try + (if args + (loop [[opt arg & more :as args] args inits []] + (if (init-dispatch repl-env opt) + (recur more (conj inits [opt arg])) + ((main-dispatch repl-env opt) args inits))) + (repl-opt repl-env nil nil)) + (finally + (flush)))) \ No newline at end of file diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 4ded7ccbd..dd03d401a 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -16,6 +16,7 @@ [cljs.env :as env] [cljs.closure :as cljsc] [cljs.repl :as repl] + [cljs.cli :as cli] [cljs.repl.server :as server] [cljs.stacktrace :as st] [cljs.analyzer :as ana]) @@ -333,8 +334,8 @@ [& {:as opts}] (repl-env* opts)) -(defn -main [] - (repl/repl (repl-env))) +(defn -main [& args] + (apply cli/main repl-env args)) (comment diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index e7aa1f8db..c9e302d4b 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -14,12 +14,11 @@ [cljs.env :as env] [cljs.util :as util] [cljs.repl :as repl] + [cljs.cli :as cli] [cljs.compiler :as comp] [cljs.closure :as closure] [cljs.stacktrace :as st]) - (:import [java.io File] - [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory] - [com.google.common.base Throwables])) + (:import [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory])) (util/compile-if (Class/forName "jdk.nashorn.api.scripting.NashornException") (do @@ -180,8 +179,12 @@ [& {:as opts}] (repl-env* opts)) - (defn -main [] - (repl/repl (repl-env)))) + ;; ------------------------------------------------------------------------- + ;; Command Line Support + + (defn -main [& args] + (apply cli/main repl-env args))) + (do (defn repl-env* [{:keys [debug] :as opts}] (throw (ex-info "Nashorn not supported" {:type :repl-error}))) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index e3eb795e2..d4b691271 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -13,6 +13,7 @@ [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.repl :as repl] + [cljs.cli :as cli] [cljs.closure :as closure] [clojure.data.json :as json]) (:import java.net.Socket @@ -140,6 +141,7 @@ (if @(:socket repl-env) (recur (read-response (:in @(:socket repl-env)))) (recur nil)))) + (println "ClojureScript Node.js REPL server listening on" (:port repl-env)) ;; compile cljs.core & its dependencies, goog/base.js must be available ;; for bootstrap to load, use new closure/compile as it can handle ;; resources in JARs @@ -230,5 +232,5 @@ [& {:as options}] (repl-env* options)) -(defn -main [] - (repl/repl (repl-env))) +(defn -main [& args] + (apply cli/main repl-env args)) diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js index 3651c0463..c6d288a46 100644 --- a/src/main/clojure/cljs/repl/node_repl.js +++ b/src/main/clojure/cljs/repl/node_repl.js @@ -79,5 +79,3 @@ net.createServer(function (socket) { }); }).listen(PORT); - -console.log("ClojureScript Node.js REPL server listening on", PORT); diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj index fd86c2098..a8b171339 100644 --- a/src/main/clojure/cljs/repl/rhino.clj +++ b/src/main/clojure/cljs/repl/rhino.clj @@ -14,6 +14,7 @@ [cljs.closure :as closure] [cljs.analyzer :as ana] [cljs.repl :as repl] + [cljs.cli :as cli] [cljs.util :as util] [cljs.stacktrace :as st]) (:import [java.io File Reader] @@ -220,8 +221,8 @@ [& {:as opts}] (repl-env* opts)) -(defn -main [] - (repl/repl (repl-env))) +(defn -main [& args] + (apply cli/main repl-env args)) (comment From 252944ea0a99fa596f81f6ddb5f08bfe07b5c186 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 2 Feb 2018 15:49:06 +0000 Subject: [PATCH 2793/4033] make it possible to set the output-dir from the command line --- src/main/clojure/cljs/cli.clj | 48 +++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 97b820f44..9c34671ea 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -13,24 +13,31 @@ [cljs.env :as env]) (:import [java.io StringReader])) +(def ^:dynamic *cli-opts* nil) + (defn repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] inits] - (repl/repl (repl-env))) + (repl/repl* (repl-env) *cli-opts*)) (defn- eval-opt "Evals expressions in str, prints each non-nil result using prn" [repl-env str] ;; TODO: use forms-seq instead of read-string (env/ensure - (let [renv (repl-env) - forms (ana-api/forms-seq (StringReader. str))] - (repl/-setup renv (repl/-repl-options renv)) - (doseq [form forms] - (println - (repl/evaluate-form renv (ana/empty-env) "" form))) - (repl/-tear-down renv)))) + (binding [repl/*repl-opts* *cli-opts*] + (let [renv (repl-env) + forms (ana-api/forms-seq (StringReader. str))] + (repl/-setup renv (merge (repl/-repl-options renv) repl/*repl-opts*)) + (doseq [form forms] + (println + (repl/evaluate-form renv (ana/empty-env) "" form))) + (repl/-tear-down renv))))) + +(defn output-dir-opt + [repl-env output-dir] + (set! *cli-opts* (merge *cli-opts* {:output-dir output-dir}))) (defn main-opt "Call the -main function from a namespace with string arguments from @@ -43,8 +50,10 @@ present" "Returns the handler associated with an init opt" [repl-env opt] ;; NOT YET IMPLEMENTED - ({"-e" (partial eval-opt repl-env) - "--eval" (partial eval-opt repl-env)} opt)) + ({"-e" (partial eval-opt repl-env) + "--eval" (partial eval-opt repl-env) + "-o" (partial output-dir-opt repl-env) + "--output-dir" (partial output-dir-opt repl-env)} opt)) (defn- initialize "Common initialize routine for repl, script, and null opts" @@ -71,12 +80,13 @@ present" } opt)) (defn main [repl-env & args] - (try - (if args - (loop [[opt arg & more :as args] args inits []] - (if (init-dispatch repl-env opt) - (recur more (conj inits [opt arg])) - ((main-dispatch repl-env opt) args inits))) - (repl-opt repl-env nil nil)) - (finally - (flush)))) \ No newline at end of file + (binding [*cli-opts* {}] + (try + (if args + (loop [[opt arg & more :as args] args inits []] + (if (init-dispatch repl-env opt) + (recur more (conj inits [opt arg])) + ((main-dispatch repl-env opt) args inits))) + (repl-opt repl-env nil nil)) + (finally + (flush))))) From 63f63c1491c452980a9484849f7471f6c704b476 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 11:07:40 +0000 Subject: [PATCH 2794/4033] bump spec deps in bootstrap and project.clj --- project.clj | 1 + script/bootstrap | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/project.clj b/project.clj index 750fa7b5c..ac9530147 100644 --- a/project.clj +++ b/project.clj @@ -9,6 +9,7 @@ :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.9.0"] + [org.clojure/core.spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.1.3.1"] diff --git a/script/bootstrap b/script/bootstrap index 4b72b3610..6dc8faa22 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -3,8 +3,8 @@ set -e CLOJURE_RELEASE="1.9.0-alpha17" -SPEC_ALPHA_RELEASE="0.1.123" -CORE_SPECS_ALPHA_RELEASE="0.1.10" +SPEC_ALPHA_RELEASE="0.1.143" +CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20170910" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" From 2dc08c81c93c38effb294eaf5dad00f4a993b349 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 11:09:27 +0000 Subject: [PATCH 2795/4033] spec.alpha dep typo --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index ac9530147..0bba36ce7 100644 --- a/project.clj +++ b/project.clj @@ -9,7 +9,7 @@ :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.9.0"] - [org.clojure/core.spec.alpha "0.1.143"] + [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.1.3.1"] From 48ccd2ca88c399d77bbaace2182a7e58cb4c9b3a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 14:39:00 +0000 Subject: [PATCH 2796/4033] make eval be a init option that feeds main opt --- src/main/clojure/cljs/cli.clj | 100 ++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 40 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 9c34671ea..84dfc8116 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -7,86 +7,106 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.cli - (:require [cljs.analyzer :as ana] + (:require [cljs.util :as util] + [cljs.env :as env] + [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.repl :as repl] - [cljs.env :as env]) + [cljs.build.api :as build]) (:import [java.io StringReader])) (def ^:dynamic *cli-opts* nil) -(defn repl-opt - "Start a repl with args and inits. Print greeting if no eval options were -present" - [repl-env [_ & args] inits] - (repl/repl* (repl-env) *cli-opts*)) - -(defn- eval-opt - "Evals expressions in str, prints each non-nil result using prn" - [repl-env str] - ;; TODO: use forms-seq instead of read-string - (env/ensure - (binding [repl/*repl-opts* *cli-opts*] - (let [renv (repl-env) - forms (ana-api/forms-seq (StringReader. str))] - (repl/-setup renv (merge (repl/-repl-options renv) repl/*repl-opts*)) - (doseq [form forms] - (println - (repl/evaluate-form renv (ana/empty-env) "" form))) - (repl/-tear-down renv))))) - (defn output-dir-opt [repl-env output-dir] (set! *cli-opts* (merge *cli-opts* {:output-dir output-dir}))) -(defn main-opt - "Call the -main function from a namespace with string arguments from - the command line." - [repl-env [_ main-ns & args] inits] - ;; NOT YET IMPLEMENTED - ) +(defn verbose-opt + [repl-env value] + (set! *cli-opts* (merge *cli-opts* {:verbose (or (= value "true") false)}))) + +(defn- eval-opt + [repl-env form-str] + (set! *cli-opts* + (merge *cli-opts* {:eval-forms (ana-api/forms-seq (StringReader. form-str))}))) (defn init-dispatch "Returns the handler associated with an init opt" [repl-env opt] - ;; NOT YET IMPLEMENTED - ({"-e" (partial eval-opt repl-env) - "--eval" (partial eval-opt repl-env) - "-o" (partial output-dir-opt repl-env) + ({"-e" (partial eval-opt repl-env) + "--eval" (partial eval-opt repl-env) + "-v" (partial verbose-opt repl-env) + "--verbose" (partial verbose-opt repl-env) + "-o" (partial output-dir-opt repl-env) "--output-dir" (partial output-dir-opt repl-env)} opt)) (defn- initialize "Common initialize routine for repl, script, and null opts" - [repl-env args inits] + [repl-env inits] (doseq [[opt arg] inits] ((init-dispatch repl-env opt) arg))) +(defn repl-opt + "Start a repl with args and inits. Print greeting if no eval options were +present" + [repl-env [_ & args] inits] + ;; TODO: handle eval forms + (repl/repl* (repl-env) (build/add-implicit-options *cli-opts*))) + +(defn main-opt + "Call the -main function from a namespace with string arguments from + the command line." + [repl-env [_ main-ns & args] inits] + (env/ensure + (initialize repl-env inits) + (let [renv (repl-env)] + (binding [repl/*repl-opts* + (build/add-implicit-options + (merge (repl/-repl-options renv) *cli-opts*)) + ana/*verbose* (:verbose repl/*repl-opts*)] + (repl/-setup renv (merge (repl/-repl-options renv) repl/*repl-opts*)) + (doseq [form (:eval-forms repl/*repl-opts*)] + (println (repl/evaluate-form renv (ana/empty-env) "" form))) + (when main-ns + (repl/evaluate-form renv (ana/empty-env) "" + `(do + (set! *command-line-args* (list ~@args)) + (~(symbol (name main-ns) "-main") ~@args)))) + (repl/-tear-down renv))))) + (defn- null-opt "No repl or script opt present, just bind args and run inits" [repl-env args inits] - (initialize repl-env args inits)) + (initialize repl-env inits)) (defn main-dispatch "Returns the handler associated with a main option" [repl-env opt] ({"-r" (partial repl-opt repl-env) "--repl" (partial repl-opt repl-env) - ;"-m" main-opt - ;"--main" main-opt + "-m" (partial main-opt repl-env) + "--main" (partial main-opt repl-env) nil (partial null-opt repl-env) ;"-h" help-opt ;"--help" help-opt ;"-?" help-opt } opt)) +(defn adapt-args [args] + (cond-> args + (and (some #{"-e" "--eval"} args) + (not (some #{"-m" "--main"} args))) + (concat ["-m"]))) + (defn main [repl-env & args] (binding [*cli-opts* {}] (try (if args - (loop [[opt arg & more :as args] args inits []] - (if (init-dispatch repl-env opt) - (recur more (conj inits [opt arg])) - ((main-dispatch repl-env opt) args inits))) + (let [args' (adapt-args args)] + (loop [[opt arg & more :as args] args' inits []] + (if (init-dispatch repl-env opt) + (recur more (conj inits [opt arg])) + ((main-dispatch repl-env opt) args inits)))) (repl-opt repl-env nil nil)) (finally (flush))))) From 006338708db0bc5bea60d1f5aa421b15dce8b62d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 15:15:36 +0000 Subject: [PATCH 2797/4033] need to wrap evaluation in cljs.compiler/with-core-cljs, make -e imply -m --- src/main/clojure/cljs/cli.clj | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 84dfc8116..5caf1b742 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -11,6 +11,7 @@ [cljs.env :as env] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] + [cljs.compiler :as comp] [cljs.repl :as repl] [cljs.build.api :as build]) (:import [java.io StringReader])) @@ -61,18 +62,20 @@ present" (initialize repl-env inits) (let [renv (repl-env)] (binding [repl/*repl-opts* - (build/add-implicit-options - (merge (repl/-repl-options renv) *cli-opts*)) - ana/*verbose* (:verbose repl/*repl-opts*)] - (repl/-setup renv (merge (repl/-repl-options renv) repl/*repl-opts*)) - (doseq [form (:eval-forms repl/*repl-opts*)] - (println (repl/evaluate-form renv (ana/empty-env) "" form))) - (when main-ns - (repl/evaluate-form renv (ana/empty-env) "" - `(do - (set! *command-line-args* (list ~@args)) - (~(symbol (name main-ns) "-main") ~@args)))) - (repl/-tear-down renv))))) + (build/add-implicit-options + (merge (repl/-repl-options renv) *cli-opts*)) + ana/*verbose* (:verbose repl/*repl-opts*)] + (comp/with-core-cljs repl/*repl-opts* + (fn [] + (repl/-setup renv (merge (repl/-repl-options renv) repl/*repl-opts*)) + (doseq [form (:eval-forms repl/*repl-opts*)] + (println (repl/evaluate-form renv (ana/empty-env) "" form))) + (when main-ns + (repl/evaluate-form renv (ana/empty-env) "" + `(do + (set! *command-line-args* (list ~@args)) + (~(symbol (name main-ns) "-main") ~@args)))) + (repl/-tear-down renv))))))) (defn- null-opt "No repl or script opt present, just bind args and run inits" @@ -95,7 +98,7 @@ present" (defn adapt-args [args] (cond-> args (and (some #{"-e" "--eval"} args) - (not (some #{"-m" "--main"} args))) + (not (some #{"-m" "--main"} args))) (concat ["-m"]))) (defn main [repl-env & args] From 6c1c7ba99061503e73b13e1b812ba806d8943b69 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 16:30:38 +0000 Subject: [PATCH 2798/4033] write compiler options to :output-dir --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 63a07974a..517d810df 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2603,7 +2603,8 @@ (add-externs-sources (dissoc opts :foreign-libs)))))) ([source opts compiler-env] (env/with-compiler-env compiler-env - (let [opts (add-implicit-options opts) + (let [orig-opts opts + opts (add-implicit-options opts) ;; we want to warn about NPM dep conflicts before installing the modules _ (when (:install-deps opts) (check-npm-deps opts) @@ -2702,6 +2703,7 @@ (map (comp :externs second) (get @compiler-env ::ana/namespaces))) (str (util/output-directory opts) "/inferred_externs.js"))) + _ (spit (io/file (util/output-directory opts) ".cljsc_opts") (pr-str orig-opts)) optim (:optimizations opts) ret (if (and optim (not= optim :none)) (do From 42b540063a8515b8b2c44293faba49383066723e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 16:31:36 +0000 Subject: [PATCH 2799/4033] basic -m support working --- src/main/clojure/cljs/cli.clj | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 5caf1b742..5baa56823 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -7,13 +7,15 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.cli - (:require [cljs.util :as util] + (:require [clojure.java.io :as io] + [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.compiler :as comp] [cljs.repl :as repl] - [cljs.build.api :as build]) + [cljs.build.api :as build] + [clojure.edn :as edn]) (:import [java.io StringReader])) (def ^:dynamic *cli-opts* nil) @@ -60,20 +62,39 @@ present" [repl-env [_ main-ns & args] inits] (env/ensure (initialize repl-env inits) - (let [renv (repl-env)] + (let [renv (repl-env) + opts *cli-opts* + coptsf (when-let [od (:output-dir opts)] + (io/file od ".cljsc_opts"))] (binding [repl/*repl-opts* - (build/add-implicit-options - (merge (repl/-repl-options renv) *cli-opts*)) + (as-> + (build/add-implicit-options + (merge (repl/-repl-options renv) opts)) opts + (let [copts (when (and coptsf (.exists coptsf)) + (-> (edn/read-string (slurp coptsf)) + ;; need to remove the entry point bits, + ;; user is trying load some arbitrary ns + (dissoc :main) + (dissoc :output-to)))] + (merge copts opts))) ana/*verbose* (:verbose repl/*repl-opts*)] + (when ana/*verbose* + (util/debug-prn "Compiler options:" repl/*repl-opts*)) (comp/with-core-cljs repl/*repl-opts* (fn [] (repl/-setup renv (merge (repl/-repl-options renv) repl/*repl-opts*)) + ;; REPLs don't normally load cljs_deps.js + (when (and coptsf (.exists coptsf)) + (let [depsf (io/file (:output-dir opts) "cljs_deps.js")] + (when (.exists depsf) + (repl/-evaluate renv "cljs_deps.js" 1 (slurp depsf))))) (doseq [form (:eval-forms repl/*repl-opts*)] (println (repl/evaluate-form renv (ana/empty-env) "" form))) (when main-ns (repl/evaluate-form renv (ana/empty-env) "" `(do (set! *command-line-args* (list ~@args)) + (.require js/goog ~(-> main-ns munge str)) (~(symbol (name main-ns) "-main") ~@args)))) (repl/-tear-down renv))))))) @@ -101,6 +122,7 @@ present" (not (some #{"-m" "--main"} args))) (concat ["-m"]))) +;; TODO: validate arg order to produce better error message - David (defn main [repl-env & args] (binding [*cli-opts* {}] (try From 472076df7e422eb3e19b8951ce7bb49bd3e87c11 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 16:50:40 +0000 Subject: [PATCH 2800/4033] need to analyze the ns with -main --- src/main/clojure/cljs/cli.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 5baa56823..15016845e 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -91,6 +91,7 @@ present" (doseq [form (:eval-forms repl/*repl-opts*)] (println (repl/evaluate-form renv (ana/empty-env) "" form))) (when main-ns + (ana-api/analyze-file (build/ns->source main-ns) opts) (repl/evaluate-form renv (ana/empty-env) "" `(do (set! *command-line-args* (list ~@args)) From cd4d83f0b65fc32afafceb0cfd98329ae843ebd0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 17:11:54 +0000 Subject: [PATCH 2801/4033] add initial docstring and wire up help, even though not all features are implemented --- src/main/clojure/cljs/cli.clj | 52 ++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 15016845e..fe0714b3d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -18,6 +18,8 @@ [clojure.edn :as edn]) (:import [java.io StringReader])) +(declare main) + (def ^:dynamic *cli-opts* nil) (defn output-dir-opt @@ -104,6 +106,10 @@ present" [repl-env args inits] (initialize repl-env inits)) +(defn- help-opt + [_ _] + (println (:doc (meta (var main))))) + (defn main-dispatch "Returns the handler associated with a main option" [repl-env opt] @@ -112,9 +118,9 @@ present" "-m" (partial main-opt repl-env) "--main" (partial main-opt repl-env) nil (partial null-opt repl-env) - ;"-h" help-opt - ;"--help" help-opt - ;"-?" help-opt + "-h" help-opt + "--help" help-opt + "-?" help-opt } opt)) (defn adapt-args [args] @@ -124,7 +130,45 @@ present" (concat ["-m"]))) ;; TODO: validate arg order to produce better error message - David -(defn main [repl-env & args] +(defn main + "Usage: java -cp cljs.jar clojure.main -m REPL-NS [init-opt*] [main-opt] [arg*] + + REPL-NS is any Clojure namespace that supplies a -main that builds a + ClojureScript REPL. Note that cljs.repl.node, cljs.repl.browser, cljs.repl.rhino + and cljs.repl.nashorn ship with ClojureScript. + + With no options or args, runs an interactive Read-Eval-Print Loop + + init options: + -i, --init path Load a file or resource + -e, --eval string Evaluate expressions in string; print non-nil values + -v, --verbose bool if true, will enable ClojureScriptt verbose logging + -o, --output-dir path Set the output directory to use. If supplied, .cljsc_opts + in that direction will be used to set ClojureScript + compiler options. + + main options: + -m, --main ns-name Call the -main function from a namespace with args + -r, --repl Run a repl + path Run a script from a file or resource + - Run a script from standard input + -h, -?, --help Print this help message and exit + + operation: + + - Enters the user namespace + - Binds *command-line-args* to a seq of strings containing command line + args that appear after any main option + - Runs all init options in order + - Calls a -main function or runs a repl or script if requested + + The init options may be repeated and mixed freely, but must appear before + any main option. The appearance of any eval option before running a repl + suppresses the usual repl greeting message: \"Clojure ~(clojure-version)\". + + Paths may be absolute or relative in the filesystem or relative to + classpath. Classpath-relative paths have prefix of @ or @/" + [repl-env & args] (binding [*cli-opts* {}] (try (if args From 14aacf6e1afda5a6ad707f0e578b1d6555d7ba45 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 9 Feb 2018 17:32:21 +0000 Subject: [PATCH 2802/4033] first pass at init support, add some TODOs, script support next --- src/main/clojure/cljs/cli.clj | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index fe0714b3d..145d84b30 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -30,15 +30,24 @@ [repl-env value] (set! *cli-opts* (merge *cli-opts* {:verbose (or (= value "true") false)}))) +(defn- init-opt + [repl-env file] + (set! *cli-opts* (merge *cli-opts* {:init-script file}))) + (defn- eval-opt [repl-env form-str] (set! *cli-opts* (merge *cli-opts* {:eval-forms (ana-api/forms-seq (StringReader. form-str))}))) +;; TODO: need support for feature that init options like -e and -i can appear +;; multiple times interleaved - David + (defn init-dispatch "Returns the handler associated with an init opt" [repl-env opt] - ({"-e" (partial eval-opt repl-env) + ({"-i" (partial init-opt repl-env) + "--init" (partial init-opt repl-env) + "-e" (partial eval-opt repl-env) "--eval" (partial eval-opt repl-env) "-v" (partial verbose-opt repl-env) "--verbose" (partial verbose-opt repl-env) @@ -92,6 +101,8 @@ present" (repl/-evaluate renv "cljs_deps.js" 1 (slurp depsf))))) (doseq [form (:eval-forms repl/*repl-opts*)] (println (repl/evaluate-form renv (ana/empty-env) "" form))) + (when-let [init-script (:init-script opts)] + (repl/load-file renv init-script)) (when main-ns (ana-api/analyze-file (build/ns->source main-ns) opts) (repl/evaluate-form renv (ana/empty-env) "" @@ -107,7 +118,7 @@ present" (initialize repl-env inits)) (defn- help-opt - [_ _] + [_ _ _] (println (:doc (meta (var main))))) (defn main-dispatch @@ -118,10 +129,11 @@ present" "-m" (partial main-opt repl-env) "--main" (partial main-opt repl-env) nil (partial null-opt repl-env) - "-h" help-opt - "--help" help-opt - "-?" help-opt - } opt)) + "-h" (partial help-opt repl-env) + "--help" (partial help-opt repl-env) + "-?" (partial help-opt repl-env)} opt + ;script-opt + )) (defn adapt-args [args] (cond-> args From 333dddbfb890f11dc6a21987437552093c5b9511 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Thu, 8 Feb 2018 22:22:35 +0200 Subject: [PATCH 2803/4033] CLJS-2375: Remove AMD Module Support Closure has deprecated this and enabling the feature can caused problems. --- .travis.yml | 7 +++---- script/test | 7 +++---- script/test-simple | 7 +++---- script/test.ps1 | 2 +- src/main/clojure/cljs/closure.clj | 8 ++------ src/test/cljs/calculator_amd.js | 5 ----- src/test/cljs/es6_dep.js | 2 +- 7 files changed, 13 insertions(+), 25 deletions(-) delete mode 100644 src/test/cljs/calculator_amd.js diff --git a/.travis.yml b/.travis.yml index b56cb292a..11c644787 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,10 +32,9 @@ before_script: {:file \"src/test/cljs/es6_dep.js\" :module-type :es6 :provides [\"es6_calc\"]} - {:file \"src/test/cljs/calculator_amd.js\" - :module-type :amd - :provides [\"calculator_amd\"] - :requires [\"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}]}" > builds/out-adv/core-advanced-test.js diff --git a/script/test b/script/test index 965d1cee4..6569b5ea9 100755 --- a/script/test +++ b/script/test @@ -29,10 +29,9 @@ if ! bin/cljsc src/test/cljs "{:optimizations :advanced {:file \"src/test/cljs/es6_dep.js\" :module-type :es6 :provides [\"es6_calc\"]} - {:file \"src/test/cljs/calculator_amd.js\" - :module-type :amd - :provides [\"calculator_amd\"] - :requires [\"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}]}" > builds/out-adv/core-advanced-test.js; then diff --git a/script/test-simple b/script/test-simple index d84ab815e..9cd4fc9dc 100755 --- a/script/test-simple +++ b/script/test-simple @@ -30,10 +30,9 @@ if ! bin/cljsc src/test/cljs "{:optimizations :simple {:file \"src/test/cljs/es6_dep.js\" :module-type :es6 :provides [\"es6_calc\"]} - {:file \"src/test/cljs/calculator_amd.js\" - :module-type :amd - :provides [\"calculator_amd\"] - :requires [\"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}]}" > builds/out-simp/core-simple-test.js; then diff --git a/script/test.ps1 b/script/test.ps1 index a2d0ba09b..50499f7ff 100644 --- a/script/test.ps1 +++ b/script/test.ps1 @@ -11,7 +11,7 @@ $targets = @{ env="CHAKRACORE_HOME"; name="ChakraCore"; cmd={ & "$env:CHAKRACORE_HOME\ch" $testjs } } $ran = 0 -$opts = '{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :language-in :es6 :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_amd.js\" :module-type :amd :provides [\"calculator_amd\"] :requires [\"es6_calc\"]} {:file \"src/test/cljs/es6_default_hello.js\" :provides [\"es6_default_hello\"] :module-type :es6}]}"' +$opts = '{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :language-in :es6 :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}]}"' function Test-It($env, $name, [scriptblock] $cmd) { $env_val = if(Test-Path env:$env) { (Get-Item env:$env).Value } else { "" } diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 517d810df..566b9853d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1723,9 +1723,6 @@ ^List source-files (get-source-files js-modules opts) ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setProcessCommonJSModules true) - (.setTransformAMDToCJSModules - (boolean (some (fn [{:keys [module-type]}] - (= module-type :amd)) js-modules))) (.setLanguageIn (lang-key->lang-mode :ecmascript6)) (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) _ (when (= (:target opts) :nodejs) @@ -2444,7 +2441,7 @@ (if (seq js-modules) (util/measure (:compiler-stats opts) "Process JS modules" - (let [_ (when-let [unsupported (first (filter (complement #{:es6 :commonjs :amd}) + (let [_ (when-let [unsupported (first (filter (complement #{:es6 :commonjs}) (map :module-type js-modules)))] (ana/warning :unsupported-js-module-type @env/*compiler* unsupported)) ;; Load all modules - add :source so preprocessing and conversion can access it @@ -2569,8 +2566,7 @@ (index-node-modules node-required)) (into expanded-libs (node-inputs (filter (fn [{:keys [module-type]}] - (and (some? module-type) - (not= module-type :amd))) + (some? module-type)) expanded-libs)))))) opts (if (some (fn [ijs] diff --git a/src/test/cljs/calculator_amd.js b/src/test/cljs/calculator_amd.js deleted file mode 100644 index 5b6fd995b..000000000 --- a/src/test/cljs/calculator_amd.js +++ /dev/null @@ -1,5 +0,0 @@ -define({ - add: function(x, y){ - return x + y; - } -}); diff --git a/src/test/cljs/es6_dep.js b/src/test/cljs/es6_dep.js index 304cbe265..da2836fa1 100644 --- a/src/test/cljs/es6_dep.js +++ b/src/test/cljs/es6_dep.js @@ -1,3 +1,3 @@ -import * as calc from './calculator_amd'; +import {default as calc} from './calculator'; export var calculator = calc; From 2f9e50c230969c217e1465958a9480883a55961b Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Fri, 9 Feb 2018 12:21:18 +0000 Subject: [PATCH 2804/4033] CLJS-2496 PHM seq and iter should return MapEntry on nil key case This was not addressed in CLJS-2456 and is still returning a vector for the nil key. --- src/main/cljs/cljs/core.cljs | 4 ++-- src/test/cljs/cljs/hash_map_test.cljs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 489ee5d4e..98c820991 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7749,7 +7749,7 @@ reduces them without incurring seq initialization" (if-not ^boolean seen (do (set! seen true) - [nil nil-val]) + (MapEntry. nil nil-val nil)) (.next root-iter))) (remove [_] (js/Error. "Unsupported operation"))) @@ -7818,7 +7818,7 @@ reduces them without incurring seq initialization" (when (pos? cnt) (let [s (if-not (nil? root) (.inode-seq root))] (if has-nil? - (cons [nil nil-val] s) + (cons (MapEntry. nil nil-val nil) s) s)))) ICounted diff --git a/src/test/cljs/cljs/hash_map_test.cljs b/src/test/cljs/cljs/hash_map_test.cljs index d9968e21e..90280c01d 100644 --- a/src/test/cljs/cljs/hash_map_test.cljs +++ b/src/test/cljs/cljs/hash_map_test.cljs @@ -85,3 +85,15 @@ (assoc (hash-map bad-date-1 nil) bad-date-2 nil) :ok (catch :default _ :error))))))) + + +(deftest test-cljs-2496 + (testing "A seq or iterator over a PAM/PHM should be composed of instances of IMapEntry" + (testing "PersistentHashMap" + (let [m (hash-map nil nil 1 1 2 2)] + (is (every? map-entry? m)) + (is (every? map-entry? (iter->set (-iterator m)))))) + (testing "PersistentArrayMap" + (let [m (array-map nil nil 1 1 2 2)] + (is (every? map-entry? m)) + (is (every? map-entry? (iter->set (-iterator m)))))))) From 72e2ab6e63b3341aa26abcbdd72dc291cbd0c462 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Thu, 8 Feb 2018 22:43:22 +0200 Subject: [PATCH 2805/4033] CLJS-2389: Update Closure-compiler New Closure has three changes that affect ClojureScript: Closure doesn't add goog.provide/require calls to the processed files. Goog.provide call is used by ClojureScript compiler to check which names are available. Easiest fix to be seems to emit the call ourselves instead of trying to find places where it is needed. Another change is that all CJS exports are now under `default` property, so compiler needs to emit ["default"] when accessing vars from JS modules. Closure now uses ES2017 as default languageIn option, and some optimization passes don't work with that. We can default to ES5 as JS modules are transpiled separately. Advanced optimization now removes unncessary backslashes from regex literals, so pr-str test was changed. https://github.com/google/closure-compiler/commit/179b62cc4770fd6a9eb306d3cf529eb99e992026 This commit also adds new closure-snapshot lein profile which can be used to test ClojureScript with the latest Closure snapshot, e.g. lein with-profile +closure-snapshot repl --- deps.edn | 4 +- pom.template.xml | 2 +- project.clj | 8 ++- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 70 +++++++++++++------ src/main/clojure/cljs/compiler.cljc | 22 ++++-- src/test/cljs/cljs/printing_test.cljs | 6 +- .../npm_deps_test/string_requires.cljs | 19 ++++- src/test/clojure/cljs/build_api_tests.clj | 62 +++++++++------- .../clojure/cljs/module_processing_tests.clj | 50 +++++++------ 10 files changed, 161 insertions(+), 84 deletions(-) diff --git a/deps.edn b/deps.edn index 49e3a9551..852b467b0 100644 --- a/deps.edn +++ b/deps.edn @@ -6,9 +6,9 @@ org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} - com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180101"} + com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180204"} org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"} org.mozilla/rhino {:mvn/version "1.7R5"}} :aliases {:test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" - "src/test/clojure" "src/test/self"]}}} \ No newline at end of file + "src/test/clojure" "src/test/self"]}}} diff --git a/pom.template.xml b/pom.template.xml index e6d0e5961..870132fed 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20170910 + v20180204 org.clojure diff --git a/project.clj b/project.clj index 0bba36ce7..0adf3723f 100644 --- a/project.clj +++ b/project.clj @@ -16,10 +16,12 @@ [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] - [com.google.javascript/closure-compiler-unshaded "v20170910"] + [com.google.javascript/closure-compiler-unshaded "v20180204"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} - :uberjar {:aot :all :main clojure.main}} + :uberjar {:aot :all :main clojure.main} + :closure-snapshot {:dependencies [[com.google.javascript/closure-compiler-unshaded "1.0-SNAPSHOT"]]}} :aliases {"test-all" ["with-profile" "test,1.5:test,1.6" "test"] "check-all" ["with-profile" "1.5:1.6" "check"]} - :min-lein-version "2.0.0") + :min-lein-version "2.0.0" + :repositories {"sonatype-snapshot" {:url "https://oss.sonatype.org/content/repositories/snapshots"}}) diff --git a/script/bootstrap b/script/bootstrap index 6dc8faa22..4c58e0f14 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,7 +5,7 @@ set -e CLOJURE_RELEASE="1.9.0-alpha17" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" -CLOSURE_RELEASE="20170910" +CLOSURE_RELEASE="20180204" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 566b9853d..82a22612e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -58,12 +58,14 @@ [java.util.concurrent TimeUnit LinkedBlockingDeque Executors CountDownLatch] [com.google.javascript.jscomp CompilerOptions CompilationLevel + CompilerInput CompilerInput$ModuleType DependencyOptions CompilerOptions$LanguageMode SourceMap$Format SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy - JSModule SourceMap Es6RewriteModules VariableMap] - [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode] + JSModule SourceMap Es6RewriteModules VariableMap + ProcessCommonJSModules Es6RewriteScriptsToModules] + [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode ModuleNames] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult] @@ -108,7 +110,6 @@ :check-useless-code DiagnosticGroups/CHECK_USELESS_CODE :check-variables DiagnosticGroups/CHECK_VARIABLES :closure-dep-method-usage-checks DiagnosticGroups/CLOSURE_DEP_METHOD_USAGE_CHECKS - :common-js-module-load DiagnosticGroups/COMMON_JS_MODULE_LOAD :conformance-violations DiagnosticGroups/CONFORMANCE_VIOLATIONS :const DiagnosticGroups/CONST :constant-property DiagnosticGroups/CONSTANT_PROPERTY @@ -226,7 +227,7 @@ :mapped AnonymousFunctionNamingPolicy/MAPPED (throw (IllegalArgumentException. (str "Invalid :anon-fn-naming-policy value " policy " - only :off, :unmapped, :mapped permitted"))))))) - (when-let [lang-key (:language-in opts)] + (when-let [lang-key (:language-in opts :ecmascript5)] (.setLanguageIn compiler-options (lang-key->lang-mode lang-key))) (when-let [lang-key (:language-out opts)] @@ -1695,24 +1696,39 @@ (assoc-in [:closure-warnings :non-standard-jsdoc] :off) (set-options (CompilerOptions.)))) -(defn get-js-root [closure-compiler] - (.getSecondChild (.getRoot closure-compiler))) - -(defn get-closure-sources - "Gets map of source file name -> Node, for files in Closure Compiler js root." - [closure-compiler] - (let [source-nodes (.children (get-js-root closure-compiler))] - (into {} (map (juxt #(.getSourceFileName ^Node %) identity) source-nodes)))) +(defn module-type->keyword [^CompilerInput$ModuleType module-type] + (case (.name module-type) + "NONE" :none + "GOOG" :goog + "ES6" :es6 + "COMMONJS" :commonjs + "JSON" :json + "IMPORTED_SCRIPT" :imported-script)) (defn add-converted-source - [closure-compiler result-nodes opts {:keys [file-min file] :as ijs}] + [closure-compiler inputs-by-name opts {:keys [file-min file requires] :as ijs}] (let [processed-file (if-let [min (and (#{:advanced :simple} (:optimizations opts)) file-min)] min file) - processed-file (string/replace processed-file "\\" "/")] - (assoc ijs :source - (.toSource closure-compiler ^Node (get result-nodes processed-file))))) + processed-file (string/replace processed-file "\\" "/") + ^CompilerInput input (get inputs-by-name processed-file) + ^Node ast-root (.getAstRoot input closure-compiler) + module-name (ModuleNames/fileToModuleName processed-file) + ;; getJsModuleType returns NONE for ES6 files, but getLoadsFlags module returns es6 for those + module-type (or (some-> (.get (.getLoadFlags input) "module") keyword) + (module-type->keyword (.getJsModuleType input)))] + (assoc ijs + :module-type module-type + :source + ;; Add goog.provide/require calls ourselves, not emited by Closure since + ;; https://github.com/google/closure-compiler/pull/2641 + (str + "goog.provide(\"" module-name "\");\n" + (apply str (map (fn [n] + (str "goog.require(\"" n "\");\n")) + (.getRequires input))) + (.toSource closure-compiler ast-root))))) (defn convert-js-modules "Takes a list JavaScript modules as an IJavaScript and rewrites them into a Google @@ -1724,18 +1740,26 @@ ^CompilerOptions options (doto (make-convert-js-module-options opts) (.setProcessCommonJSModules true) (.setLanguageIn (lang-key->lang-mode :ecmascript6)) - (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))) + (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3))) + (.setDependencyOptions (doto (DependencyOptions.) + (.setDependencySorting true)))) _ (when (= (:target opts) :nodejs) (.setPackageJsonEntryNames options ^List '("module", "main"))) closure-compiler (doto (make-closure-compiler) (.init externs source-files options)) _ (.parse closure-compiler) _ (report-failure (.getResult closure-compiler)) - root (.getRoot closure-compiler)] - (.process (Es6RewriteModules. closure-compiler) - (.getFirstChild root) (.getSecondChild root)) + ^Node extern-and-js-root (.getRoot closure-compiler) + ^Node extern-root (.getFirstChild extern-and-js-root) + ^Node js-root (.getSecondChild extern-and-js-root) + inputs-by-name (into {} (map (juxt #(.getName %) identity) (vals (.getInputsById closure-compiler))))] + + (.process (Es6RewriteScriptsToModules. closure-compiler) extern-root js-root) + (.process (Es6RewriteModules. closure-compiler) extern-root js-root) + (.process (ProcessCommonJSModules. closure-compiler) extern-root js-root) + (map (partial add-converted-source - closure-compiler (get-closure-sources closure-compiler) opts) + closure-compiler inputs-by-name opts) js-modules))) (defmulti js-transforms @@ -2272,6 +2296,8 @@ (map (fn [{:strs [file provides]}] file (merge {:file file + ;; Just tag everything es6 here, add-converted-source will + ;; ask the real type, CJS/ES6, from Closure. :module-type :es6} (when provides {:provides provides})))) @@ -2472,6 +2498,8 @@ (reduce (fn [new-opts {:keys [file module-type] :as ijs}] (let [ijs (write-javascript opts ijs) module-name (-> (deps/load-library (:out-file ijs)) first :provides first)] + (swap! env/*compiler* + #(assoc-in % [:js-namespaces module-name] {:module-type module-type})) (doseq [provide (:provides ijs)] (swap! env/*compiler* #(update-in % [:js-module-index] assoc provide {:name module-name diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b89e2ee5a..acaa33b06 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -378,11 +378,25 @@ ;; but not standalone `default` variable names ;; as they're not valid ES5 - Antonio (some? (namespace var-name))) - (set/difference ana/es5-allowed))] + (set/difference ana/es5-allowed)) + js-module (get-in cenv [:js-namespaces (or (namespace var-name) (name var-name))]) + info (cond-> info + (not= form 'js/-Infinity) (munge reserved))] (emit-wrap env - (emits - (cond-> info - (not= form 'js/-Infinity) (munge reserved)))))))))) + (case (:module-type js-module) + ;; Closure exports CJS exports through default property + :commonjs + (if (namespace var-name) + (emits (munge (namespace var-name) reserved) "[\"default\"]." (munge (name var-name) reserved)) + (emits (munge (name var-name) reserved) "[\"default\"]")) + + ;; Emit bracket notation for default prop access instead of dot notation + :es6 + (if (and (namespace var-name) (= "default" (name var-name))) + (emits (munge (namespace var-name) reserved) "[\"default\"]") + (emits info)) + + (emits info))))))))) (defmethod emit* :var-special [{:keys [env var sym meta] :as arg}] diff --git a/src/test/cljs/cljs/printing_test.cljs b/src/test/cljs/cljs/printing_test.cljs index e53629e00..c114893df 100644 --- a/src/test/cljs/cljs/printing_test.cljs +++ b/src/test/cljs/cljs/printing_test.cljs @@ -110,9 +110,9 @@ (is (= (pr-str (rest (conj cljs.core.PersistentQueue.EMPTY 1 2 3))) "(2 3)")) (is (= "\"asdf\" \"asdf\"" (pr-str "asdf" "asdf"))) ;; Different hash map order on self-host - (is (#{"[1 true {:a 2, :b #\"x\\\"y\"} #js [3 4]]" - "[1 true {:b #\"x\\\"y\", :a 2} #js [3 4]]"} - (pr-str [1 true {:a 2 :b #"x\"y"} (array 3 4)])))) + (is (#{"[1 true {:a 2, :b \"x\\\"y\"} #js [3 4]]" + "[1 true {:b \"x\\\"y\", :a 2} #js [3 4]]"} + (pr-str [1 true {:a 2 :b "x\"y"} (array 3 4)])))) (testing "Testing print-str" (is (= (print-str "asdf") "asdf"))) (testing "Testing println-str" diff --git a/src/test/cljs_build/npm_deps_test/string_requires.cljs b/src/test/cljs_build/npm_deps_test/string_requires.cljs index 322d416b5..b5836dffe 100644 --- a/src/test/cljs_build/npm_deps_test/string_requires.cljs +++ b/src/test/cljs_build/npm_deps_test/string_requires.cljs @@ -1,9 +1,24 @@ (ns npm-deps-test.string-requires (:require [react :refer [createElement]] ["react-dom/server" :as ReactDOMServer] + ["lodash-es/toArray" :refer [default] :rename {default toArray}] + ["lodash-es/toFinite" :as toFinite] + ["lodash-es/array" :as array] [npm-deps-test.string-requires-in-classpath])) (enable-console-print!) -(println "ReactDOMServer exists:" ReactDOMServer - (.-renderToString ReactDOMServer)) +;; CJS namespace access +(println ReactDOMServer) + +;; CJS method call +(ReactDOMServer/renderToString nil) + +;; es6 default with refer rename +(toArray nil) + +;; es6 :as and default +(toFinite/default nil) + +;; es6 +(array/findIndex #js [1 2] 2) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index e54276edd..92b79bbee 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -226,23 +226,35 @@ (build/build (build/inputs inputs) opts) (is (not (nil? (re-find #"foreignA[\s\S]+foreignB" (slurp (io/file out "foo.js")))))))))) +(deftest test-npm-deps-simple + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'npm-deps-test.core + :output-dir out + :optimizations :none + :install-deps true + :npm-deps {:left-pad "1.1.3"} + :foreign-libs [{:module-type :es6 + :file "src/test/cljs/es6_dep.js" + :provides ["es6_calc"]} + {:module-type :es6 + :file "src/test/cljs/es6_default_hello.js" + :provides ["es6_default_hello"]}] + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (build/build (build/inputs (io/file inputs "npm_deps_test/core.cljs")) opts cenv) + (is (.exists (io/file out "node_modules/left-pad/index.js"))) + (is (contains? (:js-module-index @cenv) "left-pad"))) + + (.delete (io/file "package.json")) + (test/delete-node-modules)) + (deftest test-npm-deps (test/delete-node-modules) (spit (io/file "package.json") "{}") - (testing "simplest case, require" - (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) - {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) - :opts {:main 'npm-deps-test.core - :output-dir out - :optimizations :none - :install-deps true - :npm-deps {:left-pad "1.1.3"} - :closure-warnings {:check-types :off}}} - cenv (env/default-compiler-env)] - (test/delete-out-files out) - (build/build (build/inputs (io/file inputs "npm_deps_test/core.cljs")) opts cenv) - (is (.exists (io/file out "node_modules/left-pad/index.js"))) - (is (contains? (:js-module-index @cenv) "left-pad")))) (let [cenv (env/default-compiler-env) out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) @@ -252,20 +264,21 @@ :install-deps true :npm-deps {:react "15.6.1" :react-dom "15.6.1" + :lodash-es "4.17.4" :lodash "4.17.4"} :closure-warnings {:check-types :off :non-standard-jsdoc :off}}}] + (test/delete-out-files out) (testing "mix of symbol & string-based requires" - (test/delete-out-files out) - (test/delete-node-modules) (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) (is (.exists (io/file out "node_modules/react/react.js"))) (is (contains? (:js-module-index @cenv) "react")) (is (contains? (:js-module-index @cenv) "react-dom/server"))) + (testing "builds with string requires are idempotent" (build/build (build/inputs (io/file inputs "npm_deps_test/string_requires.cljs")) opts cenv) - (is (not (nil? (re-find #"\.\.[\\/]node_modules[\\/]react-dom[\\/]server\.js" (slurp (io/file out "cljs_deps.js")))))) - (test/delete-out-files out))) + (is (not (nil? (re-find #"\.\.[\\/]node_modules[\\/]react-dom[\\/]server\.js" (slurp (io/file out "cljs_deps.js")))))))) + (.delete (io/file "package.json")) (test/delete-node-modules)) @@ -500,8 +513,8 @@ (test/delete-out-files out) (build/build (build/inputs (io/file inputs "foreign_libs_dir_test/core.cljs")) opts) (is (.exists (io/file out "src/test/cljs_build/foreign-libs-dir/vendor/lib.js"))) - (is (true? (boolean (re-find #"goog\.provide\(\"module\$[A-Za-z0-9$_]+?src\$test\$cljs_build\$foreign_libs_dir\$vendor\$lib\"\)" - (slurp (io/file out "src/test/cljs_build/foreign-libs-dir/vendor/lib.js")))))))) + (is (re-find #"goog\.provide\(\"module\$[A-Za-z0-9$_]+?src\$test\$cljs_build\$foreign_libs_dir\$vendor\$lib\"\)" + (slurp (io/file out "src/test/cljs_build/foreign-libs-dir/vendor/lib.js")))))) (deftest cljs-1883-test-foreign-libs-use-relative-path (test/delete-node-modules) @@ -516,13 +529,14 @@ :output-dir (str out)}] (test/delete-out-files out) (build/build (build/inputs (io/file root "foreign_libs_cljs_2334")) opts) - (let [foreign-lib-file (io/file out (-> opts :foreign-libs first :file))] + (let [foreign-lib-file (io/file out (-> opts :foreign-libs first :file)) + index-js (slurp (io/file "cljs-2334-out" "node_modules" "left-pad" "index.js"))] (is (.exists foreign-lib-file)) + (is (re-find #"module\$.*\$node_modules\$left_pad\$index=" index-js)) + (is (not (re-find #"module\.exports" index-js))) ;; assert Closure finds and processes the left-pad dep in node_modules ;; if it can't be found the require will be issued to module$left_pad ;; so we assert it's of the form module$path$to$node_modules$left_pad$index - (is (some? (re-find - #"(?s).*?goog\.require\(\"[A-Za-z0-9$_]+?node_modules\$left_pad\$index\"\);.*" - (slurp foreign-lib-file))))) + (is (re-find #"module\$.*\$node_modules\$left_pad\$index\[\"default\"\]\(42,5,0\)" (slurp foreign-lib-file)))) (test/delete-out-files out) (test/delete-node-modules))) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index ec21d667a..1514ec563 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -105,33 +105,37 @@ (deftest test-module-name-substitution (test/delete-out-files) (let [cenv (env/default-compiler-env)] - (env/with-compiler-env cenv - (let [opts (closure/process-js-modules {:foreign-libs [{:file "src/test/cljs/calculator.js" - :provides ["calculator"] - :module-type :commonjs}]}) - compile (fn [form] - (with-out-str - (comp/emit (ana/analyze (ana/empty-env) form)))) - crlf (if util/windows? "\r\n" "\n") - output (str (absolute-module-path "src/test/cljs/calculator.js" true) ".add((3),(4));" crlf)] - (swap! cenv - #(assoc % :js-dependency-index (deps/js-dependency-index opts))) - (binding [ana/*cljs-ns* 'cljs.user] - (is (= (compile '(ns my-calculator.core (:require [calculator :as calc :refer [subtract add] :rename {subtract sub}]))) - (str "goog.provide('my_calculator.core');" crlf - "goog.require('cljs.core');" crlf - "goog.require('" (absolute-module-path "src/test/cljs/calculator.js" true) "');" - crlf))) - (is (= (compile '(calc/add 3 4)) output)) - (is (= (compile '(calculator/add 3 4)) output)) - (is (= (compile '(add 3 4)) output)) - (is (= (compile '(sub 5 4)) - (str (absolute-module-path "src/test/cljs/calculator.js" true) - ".subtract((5),(4));" crlf)))))))) + ;; Make sure load-library is not cached when developing on REPL + (with-redefs [cljs.js-deps/load-library (memoize cljs.js-deps/load-library*) + cljs.js-deps/load-foreign-library (memoize cljs.js-deps/load-foreign-library*)] + (env/with-compiler-env cenv + (let [opts (closure/process-js-modules {:foreign-libs [{:file "src/test/cljs/calculator.js" + :provides ["calculator"] + :module-type :commonjs}]}) + compile (fn [form] + (with-out-str + (comp/emit (ana/analyze (ana/empty-env) form)))) + crlf (if util/windows? "\r\n" "\n") + output (str (absolute-module-path "src/test/cljs/calculator.js" true) "[\"default\"].add((3),(4));" crlf)] + (swap! cenv + #(assoc % :js-dependency-index (deps/js-dependency-index opts))) + (binding [ana/*cljs-ns* 'cljs.user] + (is (= (str "goog.provide('my_calculator.core');" crlf + "goog.require('cljs.core');" crlf + "goog.require('" (absolute-module-path "src/test/cljs/calculator.js" true) "');" + crlf) + (compile '(ns my-calculator.core (:require [calculator :as calc :refer [subtract add] :rename {subtract sub}]))))) + (is (= output (compile '(calc/add 3 4)))) + (is (= output (compile '(calculator/add 3 4)))) + (is (= output (compile '(add 3 4)))) + (is (= (str (absolute-module-path "src/test/cljs/calculator.js" true) + "[\"default\"].subtract((5),(4));" crlf) + (compile '(sub 5 4)))))))))) (deftest test-cljs-1822 (test/delete-out-files) (let [cenv (env/default-compiler-env)] + ;; Make sure load-library is not cached when developing on REPL (with-redefs [cljs.js-deps/load-library (memoize cljs.js-deps/load-library*) cljs.js-deps/load-foreign-library (memoize cljs.js-deps/load-foreign-library*)] (is (= {:optimizations :simple From 486de1a8b0836dbe3a622662a69f57aa92d232de Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Thu, 8 Feb 2018 22:40:45 +0200 Subject: [PATCH 2806/4033] CLJS-2494: If :npm-deps is false, don't index node_modules/ dir --- src/main/clojure/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 82a22612e..7815881b7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2580,7 +2580,10 @@ (let [top-level (reduce (fn [acc m] (reduce (fn [acc p] (assoc acc p m)) acc (:provides m))) - {} (index-node-modules-dir)) + {} + ;; if :npm-deps option is false, node_modules/ dir shouldn't be indexed + (if (not (false? npm-deps)) + (index-node-modules-dir))) requires (set (mapcat deps/-requires js-sources)) ;; Select Node files that are required by Cljs code, ;; and create list of all their dependencies From 6c2fba9def9bc830ec7f950f14ff4d89db141439 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Thu, 8 Feb 2018 22:30:18 +0200 Subject: [PATCH 2807/4033] CLJS-2495: Closure compilation errors should stop Cljs compilation --- src/main/clojure/cljs/closure.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 7815881b7..69c6dc501 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -375,7 +375,9 @@ (doseq [next (seq errors)] (println "ERROR:" (.toString ^JSError next))) (doseq [next (seq warnings)] - (println "WARNING:" (.toString ^JSError next)))))) + (println "WARNING:" (.toString ^JSError next))) + (when (seq errors) + (throw (Exception. "Closure compilation failed")))))) ;; Protocols for IJavaScript and Compilable ;; ======================================== From f2d5d69fdfe6d760440a40bcde37cdd1122542fe Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Feb 2018 12:25:58 +0000 Subject: [PATCH 2808/4033] add cljs.main ns --- src/main/cljs/cljs/main.clj | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/cljs/cljs/main.clj diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj new file mode 100644 index 000000000..0310f2596 --- /dev/null +++ b/src/main/cljs/cljs/main.clj @@ -0,0 +1,10 @@ +;; 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.main + (:require [cljs.cli :as cli])) From e77a9444e43440d6403bb3b1235c715f4da7baf3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Feb 2018 17:46:35 +0000 Subject: [PATCH 2809/4033] cljs.main --- pom.template.xml | 7 +++++++ project.clj | 2 +- script/uberjar | 2 +- src/main/cljs/cljs/main.clj | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 870132fed..f39b04811 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -318,6 +318,13 @@ maven-jar-plugin 2.4 + + + + cljs.main + + + default-jar diff --git a/project.clj b/project.clj index 0adf3723f..87cf47cd9 100644 --- a/project.clj +++ b/project.clj @@ -19,7 +19,7 @@ [com.google.javascript/closure-compiler-unshaded "v20180204"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} - :uberjar {:aot :all :main clojure.main} + :uberjar {:aot :all :main cljs.main} :closure-snapshot {:dependencies [[com.google.javascript/closure-compiler-unshaded "1.0-SNAPSHOT"]]}} :aliases {"test-all" ["with-profile" "test,1.5:test,1.6" "test"] "check-all" ["with-profile" "1.5:1.6" "check"]} diff --git a/script/uberjar b/script/uberjar index 95f6b6a51..2e4401b20 100755 --- a/script/uberjar +++ b/script/uberjar @@ -49,7 +49,7 @@ AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` sed -e "s/0.0.0000/$MAJOR.$MINOR-$REVISION/" src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn -lein uberjar clojure.main +lein uberjar mv target/clojurescript-0.0-SNAPSHOT-standalone.jar target/cljs.jar rm -f src/main/cljs/cljs/core.aot.js diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index 0310f2596..d35ceb40f 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -7,4 +7,36 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.main - (:require [cljs.cli :as cli])) + (:require [cljs.repl.nashorn :as nashorn] + [cljs.cli :as cli]) + (:gen-class)) + +(defn get-js-opt [args] + (if (= 2 (count args)) + (let [repl-ns (symbol + (str "cljs.repl." + (if (= 1 (count args)) + "nashorn" + (nth args 1))))] + (try + (require repl-ns) + (if-let [repl-env (ns-resolve repl-ns 'repl-env)] + repl-env + (throw + (ex-info (str "REPL namespace " repl-ns " does not define repl-env var") + {:repl-ns repl-ns}))) + (catch Throwable _ + (throw + (ex-info (str "REPL namespace " repl-ns " does not exist") + {:repl-ns repl-ns}))))) + nashorn/repl-env)) + +(defn -main [& args] + (let [pred (complement #{"-js" "--js-eval"}) + [pre post] + ((juxt #(take-while pred %) + #(drop-while pred %)) + args) + [js-args args] ((juxt #(take 2 %) #(drop 2 %)) post) + repl-opt (get-js-opt js-args)] + (apply cli/main repl-opt (concat pre args)))) From 7e5e8a417361e2d8aa4807cfc6a3ab482e599d13 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Feb 2018 17:49:55 +0000 Subject: [PATCH 2810/4033] fix up cljs.cli/main docstring now that cljs.main is a thing --- src/main/clojure/cljs/cli.clj | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 145d84b30..fb8122ddb 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -143,21 +143,20 @@ present" ;; TODO: validate arg order to produce better error message - David (defn main - "Usage: java -cp cljs.jar clojure.main -m REPL-NS [init-opt*] [main-opt] [arg*] - - REPL-NS is any Clojure namespace that supplies a -main that builds a - ClojureScript REPL. Note that cljs.repl.node, cljs.repl.browser, cljs.repl.rhino - and cljs.repl.nashorn ship with ClojureScript. + "Usage: java -cp cljs.jar cljs.main [init-opt*] [main-opt] [arg*] With no options or args, runs an interactive Read-Eval-Print Loop init options: - -i, --init path Load a file or resource - -e, --eval string Evaluate expressions in string; print non-nil values - -v, --verbose bool if true, will enable ClojureScriptt verbose logging - -o, --output-dir path Set the output directory to use. If supplied, .cljsc_opts - in that direction will be used to set ClojureScript - compiler options. + -js, --js-eval engine The JavaScript engine to use. Built-in supported + engines: nashorn, node, browser, rhino. Defaults to + nashorn + -i, --init path Load a file or resource + -e, --eval string Evaluate expressions in string; print non-nil values + -v, --verbose bool if true, will enable ClojureScriptt verbose logging + -o, --output-dir path Set the output directory to use. If supplied, .cljsc_opts + in that directory will be used to set ClojureScript + compiler options. main options: -m, --main ns-name Call the -main function from a namespace with args From d9408cb07263252df5e752427bfdb78b90dd10cb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Feb 2018 18:00:57 +0000 Subject: [PATCH 2811/4033] refactor so we have a reusable thing for all the other main cases --- src/main/clojure/cljs/cli.clj | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index fb8122ddb..aa387ea71 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -64,13 +64,11 @@ "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] inits] - ;; TODO: handle eval forms + ;; TODO: handle -e and -i args (repl/repl* (repl-env) (build/add-implicit-options *cli-opts*))) -(defn main-opt - "Call the -main function from a namespace with string arguments from - the command line." - [repl-env [_ main-ns & args] inits] +(defn main-opt* + [repl-env {:keys [main script args inits]}] (env/ensure (initialize repl-env inits) (let [renv (repl-env) @@ -103,15 +101,24 @@ present" (println (repl/evaluate-form renv (ana/empty-env) "" form))) (when-let [init-script (:init-script opts)] (repl/load-file renv init-script)) - (when main-ns - (ana-api/analyze-file (build/ns->source main-ns) opts) + (when main + (ana-api/analyze-file (build/ns->source main) opts) (repl/evaluate-form renv (ana/empty-env) "" `(do (set! *command-line-args* (list ~@args)) - (.require js/goog ~(-> main-ns munge str)) - (~(symbol (name main-ns) "-main") ~@args)))) + (.require js/goog ~(-> main)) + (~(symbol (name main) "-main") ~@args)))) (repl/-tear-down renv))))))) +(defn main-opt + "Call the -main function from a namespace with string arguments from + the command line." + [repl-env [_ main-ns & args] inits] + (main-opt* repl-env + {:main main-ns + :args args + :inits inits})) + (defn- null-opt "No repl or script opt present, just bind args and run inits" [repl-env args inits] @@ -121,6 +128,13 @@ present" [_ _ _] (println (:doc (meta (var main))))) +(defn script-opt + [repl-env [_ script & args] inits] + (main-opt* repl-env + {:script script + :args args + :inits inits})) + (defn main-dispatch "Returns the handler associated with a main option" [repl-env opt] @@ -132,8 +146,7 @@ present" "-h" (partial help-opt repl-env) "--help" (partial help-opt repl-env) "-?" (partial help-opt repl-env)} opt - ;script-opt - )) + script-opt)) (defn adapt-args [args] (cond-> args From 343fcb113643d8eef9b449bf6f38910b5c2be4d2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Feb 2018 19:00:57 +0000 Subject: [PATCH 2812/4033] basics of script option --- src/main/clojure/cljs/cli.clj | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index aa387ea71..46282cc0b 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -101,6 +101,10 @@ present" (println (repl/evaluate-form renv (ana/empty-env) "" form))) (when-let [init-script (:init-script opts)] (repl/load-file renv init-script)) + (when script + (if (= "-" script) + (repl/load-stream renv "" *in*) + (repl/load-file renv script))) (when main (ana-api/analyze-file (build/ns->source main) opts) (repl/evaluate-form renv (ana/empty-env) "" @@ -129,9 +133,9 @@ present" (println (:doc (meta (var main))))) (defn script-opt - [repl-env [_ script & args] inits] + [repl-env [path & args] inits] (main-opt* repl-env - {:script script + {:script path :args args :inits inits})) @@ -146,7 +150,7 @@ present" "-h" (partial help-opt repl-env) "--help" (partial help-opt repl-env) "-?" (partial help-opt repl-env)} opt - script-opt)) + (partial script-opt repl-env))) (defn adapt-args [args] (cond-> args @@ -187,8 +191,7 @@ present" - Calls a -main function or runs a repl or script if requested The init options may be repeated and mixed freely, but must appear before - any main option. The appearance of any eval option before running a repl - suppresses the usual repl greeting message: \"Clojure ~(clojure-version)\". + any main option. Paths may be absolute or relative in the filesystem or relative to classpath. Classpath-relative paths have prefix of @ or @/" From 84fc1810df95c12caca45644bd71c6cbe0d37508 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 10 Feb 2018 20:15:40 +0000 Subject: [PATCH 2813/4033] --js-eval -> --js-engine --- src/main/cljs/cljs/main.clj | 2 +- src/main/clojure/cljs/cli.clj | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index d35ceb40f..9e6a0ea22 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -32,7 +32,7 @@ nashorn/repl-env)) (defn -main [& args] - (let [pred (complement #{"-js" "--js-eval"}) + (let [pred (complement #{"-js" "--js-engine"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 46282cc0b..a6abf323e 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -165,15 +165,15 @@ present" With no options or args, runs an interactive Read-Eval-Print Loop init options: - -js, --js-eval engine The JavaScript engine to use. Built-in supported - engines: nashorn, node, browser, rhino. Defaults to - nashorn - -i, --init path Load a file or resource - -e, --eval string Evaluate expressions in string; print non-nil values - -v, --verbose bool if true, will enable ClojureScriptt verbose logging - -o, --output-dir path Set the output directory to use. If supplied, .cljsc_opts - in that directory will be used to set ClojureScript - compiler options. + -js, --js-engine engine The JavaScript engine to use. Built-in supported + engines: nashorn, node, browser, rhino. Defaults to + nashorn + -i, --init path Load a file or resource + -e, --eval string Evaluate expressions in string; print non-nil values + -v, --verbose bool if true, will enable ClojureScriptt verbose logging + -o, --output-dir path Set the output directory to use. If supplied, .cljsc_opts + in that directory will be used to set ClojureScript + compiler options. main options: -m, --main ns-name Call the -main function from a namespace with args From 3a1101687aa435d709d20163739f2bd0f00029e1 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Sun, 11 Feb 2018 01:18:19 +0200 Subject: [PATCH 2814/4033] CLJS-2500: Call process-js-modules after compiler restart Process-js-modules needs to be called to populate compiler-env with :js-namespaces and :js-modules-index, even if JS files haven't changed since last compile. --- src/main/clojure/cljs/closure.clj | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 69c6dc501..a77626163 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2601,11 +2601,15 @@ (node-inputs (filter (fn [{:keys [module-type]}] (some? module-type)) expanded-libs)))))) - opts (if (some - (fn [ijs] - (let [dest (io/file output-dir (rel-output-path (assoc ijs :foreign true) opts))] - (util/changed? (deps/-url ijs opts) dest))) - (:foreign-libs opts)) + ;; If compiler-env doesn't contain JS module info we need to process + ;; modules even if files haven't changed since last compile. + opts (if (or (nil? (:js-namespaces @compiler-env)) + (nil? (:js-module-index @compiler-env)) + (some + (fn [ijs] + (let [dest (io/file output-dir (rel-output-path (assoc ijs :foreign true) opts))] + (util/changed? (deps/-url ijs opts) dest))) + (:foreign-libs opts))) (process-js-modules opts) (:options @compiler-env))] (swap! compiler-env (fn [cenv] From c7d800c4385c76385582f298ac802e4701cc10bf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 15:02:15 +0000 Subject: [PATCH 2815/4033] refactor inits to be more functional in design, make sure inits can work with -r option. support multiple inits interleaved in any order. --- src/main/clojure/cljs/cli.clj | 116 +++++++++++++++++--------------- src/main/clojure/cljs/repl.cljc | 14 +++- 2 files changed, 75 insertions(+), 55 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index a6abf323e..edfff1c46 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -20,65 +20,76 @@ (declare main) -(def ^:dynamic *cli-opts* nil) - (defn output-dir-opt - [repl-env output-dir] - (set! *cli-opts* (merge *cli-opts* {:output-dir output-dir}))) + [inits-map output-dir] + (assoc-in inits-map [:options :output-dir] output-dir)) (defn verbose-opt - [repl-env value] - (set! *cli-opts* (merge *cli-opts* {:verbose (or (= value "true") false)}))) - -(defn- init-opt - [repl-env file] - (set! *cli-opts* (merge *cli-opts* {:init-script file}))) - -(defn- eval-opt - [repl-env form-str] - (set! *cli-opts* - (merge *cli-opts* {:eval-forms (ana-api/forms-seq (StringReader. form-str))}))) + [inits-map value] + (assoc-in inits-map [:options :verbose] (or (= value "true") false))) + +(defn init-opt + [inits-map file] + (update-in inits-map [:inits] + (fnil conj []) + {:type :init-script + :script file})) + +(defn eval-opt + [inits-map form-str] + (update-in inits-map [:inits] + (fnil conj []) + {:type :eval-forms + :forms (ana-api/forms-seq (StringReader. form-str))})) ;; TODO: need support for feature that init options like -e and -i can appear ;; multiple times interleaved - David (defn init-dispatch "Returns the handler associated with an init opt" - [repl-env opt] - ({"-i" (partial init-opt repl-env) - "--init" (partial init-opt repl-env) - "-e" (partial eval-opt repl-env) - "--eval" (partial eval-opt repl-env) - "-v" (partial verbose-opt repl-env) - "--verbose" (partial verbose-opt repl-env) - "-o" (partial output-dir-opt repl-env) - "--output-dir" (partial output-dir-opt repl-env)} opt)) + [opt] + ({"-i" init-opt + "--init" init-opt + "-e" eval-opt + "--eval" eval-opt + "-v" verbose-opt + "--verbose" verbose-opt + "-o" output-dir-opt + "--output-dir" output-dir-opt} opt)) (defn- initialize "Common initialize routine for repl, script, and null opts" - [repl-env inits] - (doseq [[opt arg] inits] - ((init-dispatch repl-env opt) arg))) + [inits] + (reduce + (fn [ret [opt arg]] + ((init-dispatch opt) ret arg)) + {} inits)) (defn repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] inits] - ;; TODO: handle -e and -i args - (repl/repl* (repl-env) (build/add-implicit-options *cli-opts*))) + (let [{:keys [options inits]} (initialize inits)] + (repl/repl* (repl-env) + (assoc + (build/add-implicit-options options) + :inits + (into + [{:type :eval-forms + :forms [`(set! *command-line-args* (list ~@args))]}] + inits))))) (defn main-opt* [repl-env {:keys [main script args inits]}] (env/ensure - (initialize repl-env inits) - (let [renv (repl-env) - opts *cli-opts* - coptsf (when-let [od (:output-dir opts)] + (let [{:keys [options inits]} (initialize inits) + renv (repl-env) + coptsf (when-let [od (:output-dir options)] (io/file od ".cljsc_opts"))] (binding [repl/*repl-opts* (as-> (build/add-implicit-options - (merge (repl/-repl-options renv) opts)) opts + (merge (repl/-repl-options renv) options)) opts (let [copts (when (and coptsf (.exists coptsf)) (-> (edn/read-string (slurp coptsf)) ;; need to remove the entry point bits, @@ -94,22 +105,20 @@ present" (repl/-setup renv (merge (repl/-repl-options renv) repl/*repl-opts*)) ;; REPLs don't normally load cljs_deps.js (when (and coptsf (.exists coptsf)) - (let [depsf (io/file (:output-dir opts) "cljs_deps.js")] + (let [depsf (io/file (:output-dir options) "cljs_deps.js")] (when (.exists depsf) (repl/-evaluate renv "cljs_deps.js" 1 (slurp depsf))))) - (doseq [form (:eval-forms repl/*repl-opts*)] - (println (repl/evaluate-form renv (ana/empty-env) "" form))) - (when-let [init-script (:init-script opts)] - (repl/load-file renv init-script)) + (repl/evaluate-form renv (ana/empty-env) "" + `(set! *command-line-args* (list ~@args))) + (repl/run-inits renv inits) (when script (if (= "-" script) (repl/load-stream renv "" *in*) (repl/load-file renv script))) (when main - (ana-api/analyze-file (build/ns->source main) opts) + (ana-api/analyze-file (build/ns->source main) options) (repl/evaluate-form renv (ana/empty-env) "" `(do - (set! *command-line-args* (list ~@args)) (.require js/goog ~(-> main)) (~(symbol (name main) "-main") ~@args)))) (repl/-tear-down renv))))))) @@ -126,7 +135,9 @@ present" (defn- null-opt "No repl or script opt present, just bind args and run inits" [repl-env args inits] - (initialize repl-env inits)) + (main-opt* repl-env + {:args args + :inits inits})) (defn- help-opt [_ _ _] @@ -196,14 +207,13 @@ present" Paths may be absolute or relative in the filesystem or relative to classpath. Classpath-relative paths have prefix of @ or @/" [repl-env & args] - (binding [*cli-opts* {}] - (try - (if args - (let [args' (adapt-args args)] - (loop [[opt arg & more :as args] args' inits []] - (if (init-dispatch repl-env opt) - (recur more (conj inits [opt arg])) - ((main-dispatch repl-env opt) args inits)))) - (repl-opt repl-env nil nil)) - (finally - (flush))))) + (try + (if args + (let [args' (adapt-args args)] + (loop [[opt arg & more :as args] args' inits []] + (if (init-dispatch opt) + (recur more (conj inits [opt arg])) + ((main-dispatch repl-env opt) args inits)))) + (repl-opt repl-env nil nil)) + (finally + (flush)))) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 663689532..5e77f26cd 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -39,7 +39,7 @@ (def known-repl-opts "Set of all known REPL options." #{:analyze-path :bind-err :caught :compiler-env :def-emits-var :eval :flush - :init :need-prompt :print :print-no-newline :prompt :quit-prompt :read + :init :inits :need-prompt :print :print-no-newline :prompt :quit-prompt :read :reader :repl-requires :repl-verbose :source-map-inline :watch :watch-fn :wrap}) @@ -762,8 +762,17 @@ opts))) (.printStackTrace e *err*))) +(defn run-inits [renv inits] + (doseq [{:keys [type] :as init} inits] + (case type + :eval-forms + (doseq [form (:forms init)] + (println (evaluate-form renv (ana/empty-env) "" form))) + :init-script + (load-file renv (:script init))))) + (defn repl* - [repl-env {:keys [init need-prompt quit-prompt prompt flush read eval print caught reader + [repl-env {:keys [init inits need-prompt quit-prompt prompt flush read eval print caught reader print-no-newline source-map-inline wrap repl-requires compiler-env bind-err] :or {need-prompt #(if (readers/indexing-reader? *in*) @@ -900,6 +909,7 @@ (run! #(analyze-source % opts) analyze-path) (analyze-source analyze-path opts))) (init) + (run-inits repl-env inits) (catch Throwable e (caught e repl-env opts))) (when-let [src (:watch opts)] From b7e0ae893a0f9d8cd55f5cb040ed1eec1d642e79 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 16:04:29 +0000 Subject: [PATCH 2816/4033] cleanup, setting *command-line-args* has to be done separately from :eval-forms as those get printed --- src/main/clojure/cljs/cli.clj | 17 +++++------------ src/main/clojure/cljs/repl.cljc | 3 +++ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index edfff1c46..506140cd2 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -75,7 +75,7 @@ present" (build/add-implicit-options options) :inits (into - [{:type :eval-forms + [{:type :init-forms :forms [`(set! *command-line-args* (list ~@args))]}] inits))))) @@ -163,12 +163,6 @@ present" "-?" (partial help-opt repl-env)} opt (partial script-opt repl-env))) -(defn adapt-args [args] - (cond-> args - (and (some #{"-e" "--eval"} args) - (not (some #{"-m" "--main"} args))) - (concat ["-m"]))) - ;; TODO: validate arg order to produce better error message - David (defn main "Usage: java -cp cljs.jar cljs.main [init-opt*] [main-opt] [arg*] @@ -209,11 +203,10 @@ present" [repl-env & args] (try (if args - (let [args' (adapt-args args)] - (loop [[opt arg & more :as args] args' inits []] - (if (init-dispatch opt) - (recur more (conj inits [opt arg])) - ((main-dispatch repl-env opt) args inits)))) + (loop [[opt arg & more :as args] args inits []] + (if (init-dispatch opt) + (recur more (conj inits [opt arg])) + ((main-dispatch repl-env opt) args inits))) (repl-opt repl-env nil nil)) (finally (flush)))) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 5e77f26cd..71837cf0b 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -765,6 +765,9 @@ (defn run-inits [renv inits] (doseq [{:keys [type] :as init} inits] (case type + :init-forms + (doseq [form (:forms init)] + (evaluate-form renv (ana/empty-env) "" form)) :eval-forms (doseq [form (:forms init)] (println (evaluate-form renv (ana/empty-env) "" form))) From dbd861647f6435ac2845bb3db46ba647c46d7ca0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 16:09:05 +0000 Subject: [PATCH 2817/4033] code cleanup --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 506140cd2..5b36591f5 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -26,7 +26,7 @@ (defn verbose-opt [inits-map value] - (assoc-in inits-map [:options :verbose] (or (= value "true") false))) + (assoc-in inits-map [:options :verbose] (= value "true"))) (defn init-opt [inits-map file] From 0a7b1b076dd9d2fcb7f019a10784ef5dabcdd362 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 17:38:27 +0000 Subject: [PATCH 2818/4033] always generate a namespace, fix tests so that we supply a main to elide other sources from the build. --- script/test | 1 + src/main/clojure/cljs/analyzer.cljc | 6 +++++- src/main/clojure/cljs/compiler.cljc | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/script/test b/script/test index 6569b5ea9..eb1fa4f92 100755 --- a/script/test +++ b/script/test @@ -13,6 +13,7 @@ possible=5 ran=0 if ! bin/cljsc src/test/cljs "{:optimizations :advanced + :main test-runner :output-wrapper true :verbose true :compiler-stats true diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a6b33f134..02cd76370 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3819,7 +3819,11 @@ (finally (when rdr (.close ^Reader rdr))))))] - ijs))))) + (cond-> ijs + (not (contains? ijs :ns)) + (merge + {:ns (gen-user-ns src) + :provides [(gen-user-ns src)]}))))))) #?(:clj (defn- cache-analysis-ext diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index acaa33b06..2b68c1759 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1457,10 +1457,10 @@ (fn [] (when (and (or ana/*verbose* (:verbose opts)) (not (:compiler-stats opts))) - (util/debug-prn "Compiling" (str src))) + (util/debug-prn "Compiling" (str src) "to" (str dest))) (util/measure (and (or ana/*verbose* (:verbose opts)) (:compiler-stats opts)) - (str "Compiling " src) + (str "Compiling " (str src) " to " (str dest)) (let [ext (util/ext src) {:keys [ns] :as ns-info} (ana/parse-ns src)] (if-let [cached (cached-core ns ext opts)] From 06aa5cc8e0acdfa9b2a5498e9a402d4984185e30 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 17:57:48 +0000 Subject: [PATCH 2819/4033] fix bad c.compiler.api/compile-file arities --- src/main/clojure/cljs/compiler/api.clj | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index 3a8474267..811a60289 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -12,7 +12,8 @@ (:require [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] - [cljs.compiler :as comp])) + [cljs.compiler :as comp] + [cljs.closure :as closure])) ;; ============================================================================= ;; Main API @@ -75,8 +76,10 @@ Returns a map containing {:ns .. :provides .. :requires .. :file ..}. If the file was not compiled returns only {:file ...}" - ([src] (compile-file src)) - ([src dest] (compile-file src dest)) + ([src] + (compile-file src (closure/src-file->target-file src))) + ([src dest] + (compile-file src dest nil)) ([src dest opts] (compile-file (if-not (nil? env/*compiler*) From 4b60d2e0eadbd5dffb975776cfc8b419c4e099ca Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 18:09:43 +0000 Subject: [PATCH 2820/4033] we should compile the ns specified by -m --- src/main/clojure/cljs/cli.clj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 5b36591f5..49a4ebf38 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -116,11 +116,9 @@ present" (repl/load-stream renv "" *in*) (repl/load-file renv script))) (when main - (ana-api/analyze-file (build/ns->source main) options) + (repl/load-file renv (build/ns->source main)) (repl/evaluate-form renv (ana/empty-env) "" - `(do - (.require js/goog ~(-> main)) - (~(symbol (name main) "-main") ~@args)))) + `(~(symbol (name main) "-main") ~@args))) (repl/-tear-down renv))))))) (defn main-opt From d3b9fc0dacd64e26842feb882ad7dba43bcc8e7a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 20:49:01 +0000 Subject: [PATCH 2821/4033] CLJS-1076: :nashorn target refactor Nashorn REPL bootstrap so we have script that works for REPL & builds. Add :nashorn target case to cljs.closure --- src/main/cljs/cljs/bootstrap_nashorn.js | 28 ++++++++-- src/main/clojure/cljs/closure.clj | 74 ++++++++++++++++--------- src/main/clojure/cljs/repl/nashorn.clj | 19 +------ 3 files changed, 74 insertions(+), 47 deletions(-) diff --git a/src/main/cljs/cljs/bootstrap_nashorn.js b/src/main/cljs/cljs/bootstrap_nashorn.js index 8614ed8b7..ac310d50e 100644 --- a/src/main/cljs/cljs/bootstrap_nashorn.js +++ b/src/main/cljs/cljs/bootstrap_nashorn.js @@ -1,17 +1,33 @@ +var global = this; // required by React + +var nashorn_load = function(path) { + var outputPath = (typeof CLJS_OUTPUT_DIR != "undefined" ? CLJS_OUTPUT_DIR : ".") + java.io.File.separator + path; + if (typeof CLJS_DEBUG != "undefined" && CLJS_DEBUG) print("loading:" + outputPath); + load(outputPath); +}; + +goog.global.CLOSURE_IMPORT_SCRIPT = function(path) { + nashorn_load("goog/" + path); + return true; +}; + +goog.global.isProvided_ = function(name) { return false; }; + // https://blogs.oracle.com/nashorn/setinterval-and-settimeout-javascript-functions -var Platform = Java.type("javafx.application.Platform"); -var JFXPanel = Java.type("javafx.embed.swing.JFXPanel"); -var Timer = Java.type("java.util.Timer"); -var init = new JFXPanel(); // need to invoke to init JFX so Platform.runLater works +var __Platform = Java.type("javafx.application.Platform"); +var __JFXPanel = Java.type("javafx.embed.swing.JFXPanel"); +var __Timer = Java.type("java.util.Timer"); + +new __JFXPanel(); // need to invoke to init JFX so Platform.runLater works function setTimerRequest(handler, delay, interval, args) { handler = handler || function() {}; delay = delay || 0; interval = interval || 0; var applyHandler = function() { handler.apply(this, args); } - var runLater = function() { Platform.runLater(applyHandler); } - var timer = new Timer("setTimerRequest", true); + var runLater = function() { __Platform.runLater(applyHandler); } + var timer = new __Timer("setTimerRequest", true); if (interval > 0) { timer.schedule(runLater, delay, interval); } else { diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a77626163..30db6e009 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1534,6 +1534,27 @@ (util/output-directory opts)) closure-defines (json/write-str (:closure-defines opts))] (case (:target opts) + :nashorn + (output-one-file + (merge opts + (when module + {:output-to (:output-to module)})) + (add-header opts + (str (when (or (not module) (= :cljs-base (:module-name opts))) + (str "var CLJS_OUTPUT_DIR = \"" asset-path "\";\n" + "load((new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"), \"base.js\")).getPath());\n" + "load((new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"), \"deps.js\")).getPath());\n" + "load((new java.io.File(new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"),\"bootstrap\"),\"nashorn.js\")).getPath());\n" + "load((new java.io.File(\"" asset-path "\",\"cljs_deps.js\")).getPath());\n" + "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + (apply str (preloads (:preloads opts))))) + (apply str + (map (fn [entry] + (str "goog.require(\"" (comp/munge entry) "\");\n")) + (if-let [entries (when module (:entries module))] + entries + [(:main opts)])))))) + :nodejs (output-one-file (merge opts @@ -1560,24 +1581,24 @@ :webworker (output-one-file - (merge opts - (when module - {:output-to (:output-to module)})) - (str (when (or (not module) (= :cljs-base (:module-name opts))) - (str "var CLOSURE_BASE_PATH = \"" asset-path "/goog/\";\n" - "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - "var CLOSURE_IMPORT_SCRIPT = (function(global) { return function(src) {global['importScripts'](src); return true;};})(this);\n" - "if(typeof goog == 'undefined') importScripts(\"" asset-path "/goog/base.js\");\n" - "importScripts(\"" asset-path "/cljs_deps.js\");\n" - (apply str (preloads (:preloads opts))))) - (apply str - (map (fn [entry] - (when-not (= "goog" entry) - (str "goog.require(\"" (comp/munge entry) "\");\n"))) - (if-let [entries (when module (:entries module))] - entries - (when-let [main (:main opts)] - [main])))))) + (merge opts + (when module + {:output-to (:output-to module)})) + (str (when (or (not module) (= :cljs-base (:module-name opts))) + (str "var CLOSURE_BASE_PATH = \"" asset-path "/goog/\";\n" + "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "var CLOSURE_IMPORT_SCRIPT = (function(global) { return function(src) {global['importScripts'](src); return true;};})(this);\n" + "if(typeof goog == 'undefined') importScripts(\"" asset-path "/goog/base.js\");\n" + "importScripts(\"" asset-path "/cljs_deps.js\");\n" + (apply str (preloads (:preloads opts))))) + (apply str + (map (fn [entry] + (when-not (= "goog" entry) + (str "goog.require(\"" (comp/munge entry) "\");\n"))) + (if-let [entries (when module (:entries module))] + entries + (when-let [main (:main opts)] + [main])))))) (output-one-file (merge opts @@ -2623,6 +2644,15 @@ (map str (keys top-level))))))) opts)) +(defn output-bootstrap [{:keys [target] :as opts}] + (when (and (#{:nodejs :nashorn} target) + (not= (:optimizations opts) :whitespace)) + (let [target-str (name target) + outfile (io/file (util/output-directory opts) + "goog" "bootstrap" (str target-str ".js"))] + (util/mkdirs outfile) + (spit outfile (slurp (io/resource (str "cljs/bootstrap_" target-str ".js"))))))) + (defn build "Given a source which can be compiled, produce runnable JavaScript." ([source opts] @@ -2766,13 +2796,7 @@ (add-header opts) (output-one-file opts))))) (apply output-unoptimized opts js-sources))] - ;; emit Node.js bootstrap script for :none & :whitespace optimizations - (when (and (= (:target opts) :nodejs) - (not= (:optimizations opts) :whitespace)) - (let [outfile (io/file (util/output-directory opts) - "goog" "bootstrap" "nodejs.js")] - (util/mkdirs outfile) - (spit outfile (slurp (io/resource "cljs/bootstrap_node.js"))))) + (output-bootstrap opts) ret)))))) (comment diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index c9e302d4b..9573befc4 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -52,23 +52,11 @@ (when debug (println "loaded: " path)))) (defn init-engine [engine output-dir debug] + (eval-str engine (format "var CLJS_DEBUG = %s;" (boolean debug))) + (eval-str engine (format "var CLJS_OUTPUT_DIR = \"%s\";" output-dir)) (eval-resource engine "goog/base.js" debug) (eval-resource engine "goog/deps.js" debug) - (eval-str engine "var global = this") ; required by React - (eval-str engine - (format - (str "var nashorn_load = function(path) {" - " var outputPath = \"%s\" + \"/\" + path;" - (when debug " print(\"loading: \" + outputPath) ; ") - " load(outputPath);" - "};") - output-dir)) - (eval-str engine - (str "goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {" - " nashorn_load(\"goog/\" + path);" - " return true;" - "};")) - (eval-str engine "goog.global.isProvided_ = function(name) { return false; };") + (eval-resource engine "cljs/bootstrap_nashorn.js" debug) engine) (defn load-js-file [engine file] @@ -90,7 +78,6 @@ (assoc opts :output-to (.getPath (io/file output-dir deps-file))) deps) ;; load the deps file so we can goog.require cljs.core etc. - (eval-resource engine "cljs/bootstrap_nashorn.js" false) (load-js-file engine deps-file)))) (defn load-ns [engine ns] From af20aea87dc3c219d2f25be3725ab3973a428bdd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 21:34:15 +0000 Subject: [PATCH 2822/4033] automatically setup printing for JS environment with console availble as well as Nashorn --- src/main/cljs/cljs/core.cljs | 17 +++++++++++++++++ src/main/clojure/cljs/cli.clj | 2 +- src/main/clojure/cljs/repl/nashorn.clj | 17 +++++++++-------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 98c820991..adbcadfbc 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11447,3 +11447,20 @@ reduces them without incurring seq initialization" {:added "1.9"} [x] (instance? goog.Uri x)) + +(defn maybe-enable-print! [] + (cond + (exists? js/console) + (enable-console-print!) + + (identical? *target* "nashorn") + (let [system (.type js/Java "java.lang.System")] + (set! *print-newline* false) + (set! *print-fn* + (fn [& args] + (.println (.-out system) (.join (into-array args) "")))) + (set! *print-err-fn* + (fn [& args] + (.println (.-error system) (.join (into-array args) ""))))))) + +(maybe-enable-print!) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 49a4ebf38..ddf1b881f 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -102,7 +102,7 @@ present" (util/debug-prn "Compiler options:" repl/*repl-opts*)) (comp/with-core-cljs repl/*repl-opts* (fn [] - (repl/-setup renv (merge (repl/-repl-options renv) repl/*repl-opts*)) + (repl/-setup renv repl/*repl-opts*) ;; REPLs don't normally load cljs_deps.js (when (and coptsf (.exists coptsf)) (let [depsf (io/file (:output-dir options) "cljs_deps.js")] diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index 9573befc4..79867cb11 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -10,6 +10,7 @@ (:require [clojure.java.io :as io] [clojure.string :as string] [clojure.stacktrace] + [clojure.data.json :as json] [cljs.analyzer :as ana] [cljs.env :as env] [cljs.util :as util] @@ -51,12 +52,15 @@ (eval-str engine (slurp r)) (when debug (println "loaded: " path)))) - (defn init-engine [engine output-dir debug] + (defn init-engine [engine {:keys [output-dir] :as opts} debug] (eval-str engine (format "var CLJS_DEBUG = %s;" (boolean debug))) (eval-str engine (format "var CLJS_OUTPUT_DIR = \"%s\";" output-dir)) (eval-resource engine "goog/base.js" debug) (eval-resource engine "goog/deps.js" debug) (eval-resource engine "cljs/bootstrap_nashorn.js" debug) + (eval-str engine + (format "goog.global.CLOSURE_UNCOMPILED_DEFINES = %s;" + (json/write-str (:closure-defines opts)))) engine) (defn load-js-file [engine file] @@ -96,20 +100,17 @@ (defrecord NashornEnv [engine debug] repl/IReplEnvOptions (-repl-options [this] - {:output-dir ".cljs_nashorn_repl"}) + {:output-dir ".cljs_nashorn_repl" + :target :nashorn}) repl/IJavaScriptEnv (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] - (init-engine engine output-dir debug) + (init-engine engine opts debug) (let [env (ana/empty-env)] (if output-to (load-js-file engine output-to) (bootstrap-repl engine output-dir opts)) (repl/evaluate-form this env repl-filename - '(do - (.require js/goog "cljs.core") - (set! *print-newline* false) - (set! *print-fn* js/print) - (set! *print-err-fn* js/print))) + '(.require js/goog "cljs.core")) ;; monkey-patch goog.isProvided_ to suppress useless errors (repl/evaluate-form this env repl-filename '(set! js/goog.isProvided_ (fn [ns] false))) From a1d49b536b714c16d2d6a292593662eba100dccb Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 22 Jan 2018 09:15:35 -0500 Subject: [PATCH 2823/4033] CLJS-2476: recur across try should fail compilation Additionally fixes a few places where recur was incorrectly used in cljs.js. --- src/main/cljs/cljs/js.cljs | 39 ++++++++++++++++-------- src/main/clojure/cljs/analyzer.cljc | 6 ++-- src/test/clojure/cljs/analyzer_tests.clj | 9 ++++++ 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 6b662957a..79dc41cd8 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -656,14 +656,23 @@ (when (and (seq deps) emit-nil-result?) (.append sb "null;")))) +(defn- trampoline-safe + "Returns a new function that calls f but discards any return value, + returning nil instead, thus avoiding any inadvertent trampoline continuation + if a function happens to be returned." + [f] + (comp (constantly nil) f)) + (defn- analyze-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) + cb (trampoline-safe cb) eof (js-obj) aenv (ana/empty-env) the-ns (or (:ns opts) 'cljs.user) bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] - ((fn analyze-loop [last-ast ns] + (trampoline + (fn analyze-loop [last-ast ns] (binding [env/*compiler* (:*compiler* bound-vars) ana/*cljs-ns* ns ana/*checked-arrays* (:checked-arrays opts) @@ -699,12 +708,12 @@ (cb res) (let [ast (:value res)] (if (#{:ns :ns*} (:op ast)) - (ns-side-effects bound-vars aenv ast opts + ((trampoline-safe ns-side-effects) bound-vars aenv ast opts (fn [res] (if (:error res) (cb res) - (analyze-loop ast (:name ast))))) - (recur ast ns))))) + (trampoline analyze-loop ast (:name ast))))) + #(analyze-loop ast ns))))) (cb {:value last-ast}))))))) nil the-ns))) (defn analyze-str @@ -883,13 +892,15 @@ (defn- compile-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) + cb (trampoline-safe cb) eof (js-obj) aenv (ana/empty-env) sb (StringBuffer.) the-ns (or (:ns opts) 'cljs.user) bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns}) (:source-map opts) (assoc :*sm-data* (sm-data)))] - ((fn compile-loop [ns] + (trampoline + (fn compile-loop [ns] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* ns @@ -928,7 +939,7 @@ [node-libs (assoc ast :deps libs-to-load)]) [nil ast])] (if (#{:ns :ns*} (:op ast)) - (ns-side-effects bound-vars aenv ast opts + ((trampoline-safe ns-side-effects) bound-vars aenv ast opts (fn [res] (if (:error res) (cb res) @@ -936,10 +947,10 @@ (.append sb (with-out-str (comp/emit (:value res)))) (when-not (nil? node-deps) (node-side-effects bound-vars sb node-deps ns-name (:def-emits-var opts))) - (compile-loop (:name ast)))))) + (trampoline compile-loop (:name ast)))))) (do (.append sb (with-out-str (comp/emit ast))) - (recur ns)))))) + #(compile-loop ns)))))) (do (when (:source-map opts) (append-source-map env/*compiler* @@ -1013,6 +1024,7 @@ (defn- eval-str* [bound-vars source name opts cb] (let [rdr (rt/indexing-push-back-reader source 1 name) + cb (trampoline-safe cb) eof (js-obj) aenv (ana/empty-env) sb (StringBuffer.) @@ -1021,7 +1033,8 @@ (:source-map opts) (assoc :*sm-data* (sm-data))) aname (cond-> name (:macros-ns opts) ana/macro-ns-name)] (when (:verbose opts) (debug-prn "Evaluating" name)) - ((fn compile-loop [ns] + (trampoline + (fn compile-loop [ns] (binding [env/*compiler* (:*compiler* bound-vars) *eval-fn* (:*eval-fn* bound-vars) ana/*cljs-ns* ns @@ -1065,7 +1078,7 @@ (do (.append sb (with-out-str (comp/emitln (str "goog.provide(\"" (comp/munge (:name ast)) "\");")))) - (ns-side-effects true bound-vars aenv ast opts + ((trampoline-safe ns-side-effects) true bound-vars aenv ast opts (fn [res] (if (:error res) (cb res) @@ -1076,11 +1089,11 @@ (filter ana/dep-has-global-exports? (:deps ast)) ns-name (:def-emits-var opts)) - (compile-loop ns')))))) + (trampoline compile-loop ns')))))) (do (env/with-compiler-env (assoc @(:*compiler* bound-vars) :options opts) (.append sb (with-out-str (comp/emit ast)))) - (recur ns')))))) + #(compile-loop ns')))))) (do (when (:source-map opts) (append-source-map env/*compiler* @@ -1105,7 +1118,7 @@ (wrap-error (ana/error aenv "ERROR" cause))))] (cb res)))))] (if-let [f (:cache-source opts)] - (f evalm complete) + ((trampoline-safe f) evalm complete) (complete {:value nil})))))))))) (:*cljs-ns* bound-vars)))) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 02cd76370..c2be899a8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1421,7 +1421,7 @@ parser)) finally (when (seq fblock) - (analyze (assoc env :context :statement) `(do ~@(rest fblock)))) + (disallowing-recur (analyze (assoc env :context :statement) `(do ~@(rest fblock))))) e (when (or (seq cblocks) dblock) (gensym "e")) default (if-let [[_ _ name & cb] dblock] `(cljs.core/let [~name ~e] ~@cb) @@ -1444,8 +1444,8 @@ :column (get-col e env)}) locals) catch (when cblock - (analyze (assoc catchenv :locals locals) cblock)) - try (analyze (if (or e finally) catchenv env) `(do ~@body))] + (disallowing-recur (analyze (assoc catchenv :locals locals) cblock))) + try (disallowing-recur (analyze (if (or e finally) catchenv env) `(do ~@body)))] {:env env :op :try :form form :try try diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 1c53955bb..0b0000858 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -820,6 +820,15 @@ (a/analyze-file (io/file "src/test/cljs_build/analyzer_test/no_defs.cljs")))) (is (= {} (get-in @test-cenv [::a/namespaces 'analyzer-test.no-defs :defs])))) +(deftest test-cljs-2476 + (doseq [invalid-try-recur-form '[(loop [] (try (recur))) + (loop [] (try (catch js/Error t (recur)))) + (loop [] (try (catch :default t (recur)))) + (loop [] (try (finally (recur))))]] + (is (thrown-with-msg? Exception + #"Can't recur here" + (a/analyze test-env invalid-try-recur-form))))) + (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn From 15027e1d558a42ae91939d5f7332c8a95bfd0070 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 28 Jan 2018 22:31:31 -0500 Subject: [PATCH 2824/4033] CLJS-2486: Map entry much slower for first --- src/main/cljs/cljs/core.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index adbcadfbc..5e6061966 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6582,10 +6582,10 @@ reduces them without incurring seq initialization" ISequential ISeqable - (-seq [node] (list key val)) + (-seq [node] (IndexedSeq. #js [key val] 0 nil)) IReversible - (-rseq [node] (list val key)) + (-rseq [node] (IndexedSeq. #js [val key] 0 nil)) ICounted (-count [node] 2) @@ -8293,10 +8293,10 @@ reduces them without incurring seq initialization" ISequential ISeqable - (-seq [node] (list key val)) + (-seq [node] (IndexedSeq. #js [key val] 0 nil)) IReversible - (-rseq [node] (list val key)) + (-rseq [node] (IndexedSeq. #js [val key] 0 nil)) ICounted (-count [node] 2) @@ -8457,10 +8457,10 @@ reduces them without incurring seq initialization" ISequential ISeqable - (-seq [node] (list key val)) + (-seq [node] (IndexedSeq. #js [key val] 0 nil)) IReversible - (-rseq [node] (list val key)) + (-rseq [node] (IndexedSeq. #js [val key] 0 nil)) ICounted (-count [node] 2) From 755924247bad8a539eadc08d024beb1b4d841c90 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 30 Jan 2018 15:34:24 -0500 Subject: [PATCH 2825/4033] CLJS-2487: Unroll (list ...) constructs to List ctor calls --- src/main/clojure/cljs/core.cljc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a6d6ca2dd..ce75b4923 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2491,11 +2491,11 @@ (core/defmacro list ([] '(.-EMPTY cljs.core/List)) + ([x] + `(cljs.core/List. nil ~x nil 1 nil)) ([x & xs] - (if (= :constant (:op (cljs.analyzer/no-warn (cljs.analyzer/analyze &env x)))) - `(-conj (list ~@xs) ~x) - `(let [x# ~x] - (-conj (list ~@xs) x#))))) + (core/let [cnt (core/inc (count xs))] + `(cljs.core/List. nil ~x (list ~@xs) ~cnt nil)))) (core/defmacro vector ([] '(.-EMPTY cljs.core/PersistentVector)) From 8c08b2129496a800c7b356cb45b48c497c1e11ab Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 11 Feb 2018 23:06:15 -0500 Subject: [PATCH 2826/4033] remove old TODO --- src/main/clojure/cljs/cli.clj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index ddf1b881f..b5c94749d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -42,9 +42,6 @@ {:type :eval-forms :forms (ana-api/forms-seq (StringReader. form-str))})) -;; TODO: need support for feature that init options like -e and -i can appear -;; multiple times interleaved - David - (defn init-dispatch "Returns the handler associated with an init opt" [opt] From 1c16b4e8066237c1cafa7ce72ed62a00d763d66a Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Sun, 11 Feb 2018 11:01:06 +0100 Subject: [PATCH 2827/4033] CLJS-2501: Fix crash in cljs.util/compiled-by-version and build-options --- src/main/clojure/cljs/util.cljc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index ea167a866..ffb625d0e 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -37,15 +37,15 @@ (defn ^String compiled-by-version [f] (with-open [reader (io/reader f)] - (let [match (->> reader line-seq first - (re-matches #".*ClojureScript (\d+\.\d+\.\d+).*$"))] + (let [match (some->> reader line-seq first + (re-matches #".*ClojureScript (\d+\.\d+\.\d+).*$"))] (or (and match (second match)) "0.0.0000")))) (defn build-options [^File f] (with-open [reader (io/reader f)] - (let [match (->> reader line-seq first - (re-matches #".*ClojureScript \d+\.\d+\.\d+ (.*)$"))] - (and match (edn/read-string (second match)))))) + (let [match (some->> reader line-seq first + (re-matches #".*ClojureScript \d+\.\d+\.\d+ (.*)$"))] + (and match (edn/read-string (second match)))))) (defn munge-path [ss] (clojure.lang.Compiler/munge (str ss))) From 20fb118c88f253dca51ed8ab891b51e9c9d10e03 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 20 Jan 2018 17:33:08 -0500 Subject: [PATCH 2828/4033] CLJS-2474: with-meta on lazy-seq causes separate realization --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/collections_test.cljs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5e6061966..ffb5f24b4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3397,7 +3397,7 @@ reduces them without incurring seq initialization" (not fn)) IWithMeta - (-with-meta [coll meta] (LazySeq. meta fn s __hash)) + (-with-meta [coll meta] (LazySeq. meta #(-seq coll) nil __hash)) IMeta (-meta [coll] meta) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 1dfc9b77e..7ba6a2552 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -981,3 +981,16 @@ (is (map-entry? (find [0 1 2] 0)))) (testing "MapEntry" (is (map-entry? (find (MapEntry. :key :val nil) 0)))))) + +(deftest test-cljs-2474 + (let [rand-seq (fn rand-seq [] (lazy-seq (cons (rand) (rand-seq)))) + xs (rand-seq) + ys (with-meta xs {:foo 1})] + (is (not (realized? xs))) + (is (not (realized? ys))) + (is (= (take 3 xs) (take 3 ys)))) + (let [xs (lazy-seq) + ys (with-meta xs {:foo 1})] + (is (not (realized? xs))) + (is (not (realized? ys))) + (is (= () xs ys)))) From 74e2fd036011749b66d5c499a2f7fd47716aa819 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 9 Jan 2018 12:37:56 -0500 Subject: [PATCH 2829/4033] CLJS-2462: subvec on non-integral indexes fails --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/collections_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ffb5f24b4..1efdf5323 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5902,7 +5902,7 @@ reduces them without incurring seq initialization" (subvec v start (count v))) ([v start end] (assert (and (not (nil? start)) (not (nil? end)))) - (build-subvec nil v start end nil))) + (build-subvec nil v (int start) (int end) nil))) (defn- tv-ensure-editable [edit node] (if (identical? edit (.-edit node)) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 7ba6a2552..74da730a5 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -939,6 +939,10 @@ (deftest test-cljs-2452 (is (= (reverse []) ()))) +(deftest test-cljs-2462 + (is (= 1 (count (subvec [1 2] 0 1.5)))) + (is (= [1 2 3] (subvec [0 1 2 3 4 5] 1.2 4.7)))) + (deftest test-cljs-2478 (is (not (map-entry? [:a 1]))) (is (= {:a 1 :b 2 :c 3} (into (hash-map :a 1) [[:b 2] [:c 3]]))) From 578f732f19e36d39ed2b215e3fa67158424966d2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 12 Feb 2018 12:30:39 -0500 Subject: [PATCH 2830/4033] CLJS-2516 Build API fails targeting Node (QuickStart): change Node bootstrap file name so generic bootstrapping works --- src/main/cljs/cljs/{bootstrap_node.js => bootstrap_nodejs.js} | 0 src/main/clojure/cljs/repl/node.clj | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/main/cljs/cljs/{bootstrap_node.js => bootstrap_nodejs.js} (100%) diff --git a/src/main/cljs/cljs/bootstrap_node.js b/src/main/cljs/cljs/bootstrap_nodejs.js similarity index 100% rename from src/main/cljs/cljs/bootstrap_node.js rename to src/main/cljs/cljs/bootstrap_nodejs.js diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index d4b691271..fc719e3ce 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -158,7 +158,7 @@ ;; bootstrap, replace __dirname as __dirname won't be set ;; properly due to how we are running it - David (node-eval repl-env - (-> (slurp (io/resource "cljs/bootstrap_node.js")) + (-> (slurp (io/resource "cljs/bootstrap_nodejs.js")) (string/replace "path.resolve(__dirname, '..', 'base.js')" (platform-path (conj rewrite-path "bootstrap" ".." "base.js"))) (string/replace From fbf20b1a52cc2b26d39cb0c8f32c58bbc92a6e41 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 13:43:41 -0500 Subject: [PATCH 2831/4033] CLJS-2510: Misspelling of ClojureScript in help output --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index b5c94749d..3d8e18a7c 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -170,7 +170,7 @@ present" nashorn -i, --init path Load a file or resource -e, --eval string Evaluate expressions in string; print non-nil values - -v, --verbose bool if true, will enable ClojureScriptt verbose logging + -v, --verbose bool if true, will enable ClojureScript verbose logging -o, --output-dir path Set the output directory to use. If supplied, .cljsc_opts in that directory will be used to set ClojureScript compiler options. From 5e96f8b5cd007dff42ce3cd550836905d0d973c9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 13:44:59 -0500 Subject: [PATCH 2832/4033] CLJS-2505: REPL evals should use prn, not println --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 71837cf0b..5d5a6efac 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -770,7 +770,7 @@ (evaluate-form renv (ana/empty-env) "" form)) :eval-forms (doseq [form (:forms init)] - (println (evaluate-form renv (ana/empty-env) "" form))) + (prn (evaluate-form renv (ana/empty-env) "" form))) :init-script (load-file renv (:script init))))) From 40806f03a0b27c28bd49b728e39c27a0101c5a71 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 14:26:16 -0500 Subject: [PATCH 2833/4033] revert 5e96f8b5cd007dff42ce3cd550836905d0d973c9 --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 5d5a6efac..71837cf0b 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -770,7 +770,7 @@ (evaluate-form renv (ana/empty-env) "" form)) :eval-forms (doseq [form (:forms init)] - (prn (evaluate-form renv (ana/empty-env) "" form))) + (println (evaluate-form renv (ana/empty-env) "" form))) :init-script (load-file renv (:script init))))) From 5b012c8cbf4d4061dfb8854b48190e30b67c243e Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 15:28:21 -0500 Subject: [PATCH 2834/4033] CLJS-2505: cljs.main REPL -e evals don't print correctly Use eval-cljs not evaluate-form, we need to wrap forms so that the JS environment invokes pr-str on the value it will return --- src/main/clojure/cljs/repl.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 71837cf0b..0ba607025 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -767,10 +767,10 @@ (case type :init-forms (doseq [form (:forms init)] - (evaluate-form renv (ana/empty-env) "" form)) + (eval-cljs renv (ana/empty-env) form)) :eval-forms (doseq [form (:forms init)] - (println (evaluate-form renv (ana/empty-env) "" form))) + (println (eval-cljs renv (ana/empty-env) form))) :init-script (load-file renv (:script init))))) From 2b1baa4c16d8c9e926fd269dc4a649398c7a2541 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 15:44:21 -0500 Subject: [PATCH 2835/4033] CLJS-2504: Extra blank line for nil REPL evals --- src/main/clojure/cljs/repl.cljc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 0ba607025..8a39f0b79 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -762,6 +762,9 @@ opts))) (.printStackTrace e *err*))) +(defn repl-nil? [x] + (boolean (= "nil" x))) + (defn run-inits [renv inits] (doseq [{:keys [type] :as init} inits] (case type @@ -770,7 +773,9 @@ (eval-cljs renv (ana/empty-env) form)) :eval-forms (doseq [form (:forms init)] - (println (eval-cljs renv (ana/empty-env) form))) + (let [value (eval-cljs renv (ana/empty-env) form)] + (when-not (repl-nil? value) + (println value)))) :init-script (load-file renv (:script init))))) From ff693219b8a58d6b9f6c0e85578b73cbdd5c92d9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 16:08:45 -0500 Subject: [PATCH 2836/4033] CLJS-2507: Can't def in REPL eval Need to construct cljs.user, need to set :def-emits-var true when evaluating eval forms --- src/main/clojure/cljs/cli.clj | 5 ++++- src/main/clojure/cljs/repl.cljc | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 3d8e18a7c..a76656c09 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -83,7 +83,8 @@ present" renv (repl-env) coptsf (when-let [od (:output-dir options)] (io/file od ".cljsc_opts"))] - (binding [repl/*repl-opts* + (binding [ana/*cljs-ns* 'cljs.user + repl/*repl-opts* (as-> (build/add-implicit-options (merge (repl/-repl-options renv) options)) opts @@ -107,6 +108,8 @@ present" (repl/-evaluate renv "cljs_deps.js" 1 (slurp depsf))))) (repl/evaluate-form renv (ana/empty-env) "" `(set! *command-line-args* (list ~@args))) + (repl/evaluate-form renv (ana/empty-env) "" + `(~'ns ~'cljs.user)) (repl/run-inits renv inits) (when script (if (= "-" script) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 8a39f0b79..03fdd0ea3 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -772,10 +772,11 @@ (doseq [form (:forms init)] (eval-cljs renv (ana/empty-env) form)) :eval-forms - (doseq [form (:forms init)] - (let [value (eval-cljs renv (ana/empty-env) form)] - (when-not (repl-nil? value) - (println value)))) + (binding [*repl-opts* (merge *repl-opts* {:def-emits-var true})] + (doseq [form (:forms init)] + (let [value (eval-cljs renv (ana/empty-env) form)] + (when-not (repl-nil? value) + (println value))))) :init-script (load-file renv (:script init))))) From 3fac886e5a88081b8da4952e9078ec74c8124ce8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 17:06:25 -0500 Subject: [PATCH 2837/4033] CLJS-2513: Extra out directory in out need to use REPL options. Fix another issue where we weren't getting :verbose option early enough in main-opt --- src/main/clojure/cljs/cli.clj | 38 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index a76656c09..dbbc8787a 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -66,10 +66,12 @@ "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] inits] - (let [{:keys [options inits]} (initialize inits)] - (repl/repl* (repl-env) - (assoc - (build/add-implicit-options options) + (let [{:keys [options inits]} (initialize inits) + renv (repl-env) + opts (build/add-implicit-options + (merge (repl/-repl-options renv) options))] + (repl/repl* renv + (assoc opts :inits (into [{:type :init-forms @@ -82,20 +84,20 @@ present" (let [{:keys [options inits]} (initialize inits) renv (repl-env) coptsf (when-let [od (:output-dir options)] - (io/file od ".cljsc_opts"))] - (binding [ana/*cljs-ns* 'cljs.user - repl/*repl-opts* - (as-> - (build/add-implicit-options - (merge (repl/-repl-options renv) options)) opts - (let [copts (when (and coptsf (.exists coptsf)) - (-> (edn/read-string (slurp coptsf)) - ;; need to remove the entry point bits, - ;; user is trying load some arbitrary ns - (dissoc :main) - (dissoc :output-to)))] - (merge copts opts))) - ana/*verbose* (:verbose repl/*repl-opts*)] + (io/file od ".cljsc_opts")) + opts (as-> + (build/add-implicit-options + (merge (repl/-repl-options renv) options)) opts + (let [copts (when (and coptsf (.exists coptsf)) + (-> (edn/read-string (slurp coptsf)) + ;; need to remove the entry point bits, + ;; user is trying load some arbitrary ns + (dissoc :main) + (dissoc :output-to)))] + (merge copts opts)))] + (binding [ana/*cljs-ns* 'cljs.user + repl/*repl-opts* opts + ana/*verbose* (:verbose opts)] (when ana/*verbose* (util/debug-prn "Compiler options:" repl/*repl-opts*)) (comp/with-core-cljs repl/*repl-opts* From 82998109292c5982380e945e7565d7425879e0f3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 17:10:45 -0500 Subject: [PATCH 2838/4033] CLJS-2508: No command line args should be nil instead of empty list --- src/main/clojure/cljs/cli.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index dbbc8787a..09aedc7ce 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -75,7 +75,8 @@ present" :inits (into [{:type :init-forms - :forms [`(set! *command-line-args* (list ~@args))]}] + :forms (when-not (empty? args) + [`(set! *command-line-args* (list ~@args))])}] inits))))) (defn main-opt* @@ -109,7 +110,8 @@ present" (when (.exists depsf) (repl/-evaluate renv "cljs_deps.js" 1 (slurp depsf))))) (repl/evaluate-form renv (ana/empty-env) "" - `(set! *command-line-args* (list ~@args))) + (when-not (empty? args) + `(set! *command-line-args* (list ~@args)))) (repl/evaluate-form renv (ana/empty-env) "" `(~'ns ~'cljs.user)) (repl/run-inits renv inits) From ab8d3802c77791ba1f61dcd014bc3fb4c8a516c3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 12 Feb 2018 17:40:29 -0500 Subject: [PATCH 2839/4033] wip --- src/main/clojure/cljs/cli.clj | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 09aedc7ce..70758808e 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -165,6 +165,32 @@ present" "-?" (partial help-opt repl-env)} opt (partial script-opt repl-env))) +(def main-opts + #{"-r" "--repl" + "-m" "--main" + nil}) + +(def valid-opts + (into main-opts + #{"-i" "--init" + "-e" "--eval" + "-v" "--verbose" + "-o" "--output-dir" + "-h" "--help" "-?"})) + +(defn normalize [args] + (when (seq args) + (let [pred (complement #{"-v" "--verbose"}) + [pre post] ((juxt #(take-while pred %) + #(drop-while pred %)) + args)] + (concat pre + (if (contains? valid-opts (fnext post)) + (concat pre [(first post) "true"] + (normalize (next post))) + (concat pre [(first post) (fnext post)] + (normalize (nnext post)))))))) + ;; TODO: validate arg order to produce better error message - David (defn main "Usage: java -cp cljs.jar cljs.main [init-opt*] [main-opt] [arg*] From a4dc727891c2ad8547f97eb5ede48a3009698fc7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 12 Feb 2018 20:42:28 -0500 Subject: [PATCH 2840/4033] CLJS-2512: Allow -v / --verbose to take no args, and imply true --- src/main/clojure/cljs/cli.clj | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 70758808e..ff5f0a02a 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -179,17 +179,16 @@ present" "-h" "--help" "-?"})) (defn normalize [args] - (when (seq args) + (when (not (contains? main-opts (first args))) (let [pred (complement #{"-v" "--verbose"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) args)] - (concat pre - (if (contains? valid-opts (fnext post)) - (concat pre [(first post) "true"] - (normalize (next post))) - (concat pre [(first post) (fnext post)] - (normalize (nnext post)))))))) + (if (contains? valid-opts (fnext post)) + (concat pre [(first post) "true"] + (normalize (next post))) + (concat pre [(first post) (fnext post)] + (normalize (nnext post))))))) ;; TODO: validate arg order to produce better error message - David (defn main @@ -231,7 +230,7 @@ present" [repl-env & args] (try (if args - (loop [[opt arg & more :as args] args inits []] + (loop [[opt arg & more :as args] (normalize args) inits []] (if (init-dispatch opt) (recur more (conj inits [opt arg])) ((main-dispatch repl-env opt) args inits))) From 87646c7c73a2d69979f2dabfbe5c3069dc6881f0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 12 Feb 2018 21:38:47 -0500 Subject: [PATCH 2841/4033] CLJS-2511: Failure if -js specified twice also fixes defects in CLJS-2512 patch --- src/main/cljs/cljs/main.clj | 28 +++++++++++++++++++++++----- src/main/clojure/cljs/cli.clj | 12 +++++++++--- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index 9e6a0ea22..c6568cf27 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -31,12 +31,30 @@ {:repl-ns repl-ns}))))) nashorn/repl-env)) +(defn normalize* [args] + (if (not (contains? cli/main-opts (first args))) + (let [pred (complement #{"-js" "--js-engine"}) + [pre post] ((juxt #(take-while pred %) + #(drop-while pred %)) + args)] + (if (= pre args) + [nil pre] + (let [[js-opt post'] (normalize* (nnext post))] + (if js-opt + [js-opt (concat pre post')] + [[(first post) (fnext post)] (concat pre post')])))) + [nil args])) + +(defn normalize [args] + (let [[js-opt args] (normalize* args)] + (concat js-opt args))) + (defn -main [& args] - (let [pred (complement #{"-js" "--js-engine"}) - [pre post] - ((juxt #(take-while pred %) - #(drop-while pred %)) - args) + (let [args (normalize (cli/normalize args)) + pred (complement #{"-js" "--js-engine"}) + [pre post] ((juxt #(take-while pred %) + #(drop-while pred %)) + args) [js-args args] ((juxt #(take 2 %) #(drop 2 %)) post) repl-opt (get-js-opt js-args)] (apply cli/main repl-opt (concat pre args)))) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index ff5f0a02a..c379de576 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -179,16 +179,22 @@ present" "-h" "--help" "-?"})) (defn normalize [args] - (when (not (contains? main-opts (first args))) + (if (not (contains? main-opts (first args))) (let [pred (complement #{"-v" "--verbose"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) args)] - (if (contains? valid-opts (fnext post)) + (cond + (= pre args) pre + + (contains? valid-opts (fnext post)) (concat pre [(first post) "true"] (normalize (next post))) + + :else (concat pre [(first post) (fnext post)] - (normalize (nnext post))))))) + (normalize (nnext post))))) + args)) ;; TODO: validate arg order to produce better error message - David (defn main From e9e7b70c6302c8bee72291cdec9d6c47c67110d1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 12 Feb 2018 21:56:45 -0500 Subject: [PATCH 2842/4033] don't log the Node.js REPL port --- src/main/clojure/cljs/repl/node.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index fc719e3ce..14da304c3 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -141,7 +141,6 @@ (if @(:socket repl-env) (recur (read-response (:in @(:socket repl-env)))) (recur nil)))) - (println "ClojureScript Node.js REPL server listening on" (:port repl-env)) ;; compile cljs.core & its dependencies, goog/base.js must be available ;; for bootstrap to load, use new closure/compile as it can handle ;; resources in JARs From cc042e8a8f731a60ff36cb52178c0ba88eeb2eea Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 13 Feb 2018 08:10:25 -0500 Subject: [PATCH 2843/4033] suppress JavaFX icon when launching Nashorn REPL --- src/main/clojure/cljs/cli.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index c379de576..86f264dbb 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -234,6 +234,9 @@ present" Paths may be absolute or relative in the filesystem or relative to classpath. Classpath-relative paths have prefix of @ or @/" [repl-env & args] + ;; On OS X suppress the Dock icon + (System/setProperty "apple.awt.UIElement" "true") + (java.awt.Toolkit/getDefaultToolkit) (try (if args (loop [[opt arg & more :as args] (normalize args) inits []] From 4c803fcc3b88520209cdff5ce294a6a8a962a747 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 13 Feb 2018 11:25:15 -0500 Subject: [PATCH 2844/4033] fix set-loaded! docstring --- src/main/cljs/cljs/loader.cljs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 7153abfba..7b2cc406a 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -68,9 +68,7 @@ (defn set-loaded! "Set a module as being loaded. module-name should be a keyword matching a :modules module definition. Will mark all parent modules as also being - loaded. Note that calls to this function will be automatically generated - as the final expression for known :modules entry points that require the - cljs.loader namespace." + loaded." [module-name] (assert (contains? module-infos module-name) (str "Module " module-name " does not exist")) From 908f5f301f2e7ddfa6fa016c9aad42d465ab2e1c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 13 Feb 2018 11:07:33 -0500 Subject: [PATCH 2845/4033] CLJS-2518: Command line fails to terminate on some OSs --- src/main/cljs/cljs/bootstrap_nashorn.js | 4 ++++ src/main/clojure/cljs/repl/nashorn.clj | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/bootstrap_nashorn.js b/src/main/cljs/cljs/bootstrap_nashorn.js index ac310d50e..6c16aedb5 100644 --- a/src/main/cljs/cljs/bootstrap_nashorn.js +++ b/src/main/cljs/cljs/bootstrap_nashorn.js @@ -21,6 +21,10 @@ var __Timer = Java.type("java.util.Timer"); new __JFXPanel(); // need to invoke to init JFX so Platform.runLater works +var nashorn_tear_down = function() { + __Platform.exit(); +} + function setTimerRequest(handler, delay, interval, args) { handler = handler || function() {}; delay = delay || 0; diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index 79867cb11..8a9a309c8 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -63,6 +63,9 @@ (json/write-str (:closure-defines opts)))) engine) + (defn tear-down-engine [engine] + (eval-str engine "nashorn_tear_down();")) + (defn load-js-file [engine file] (eval-str engine (format "nashorn_load(\"%s\");" file))) @@ -145,7 +148,8 @@ (.getStackTrace root-cause))))})))) (-load [{engine :engine :as this} ns url] (load-ns engine ns)) - (-tear-down [this]) + (-tear-down [this] + (tear-down-engine engine)) repl/IParseStacktrace (-parse-stacktrace [this frames-str ret opts] (st/parse-stacktrace this frames-str From 80f46de7522af32e5bc05ebe75b1c2aaff068d43 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 13 Feb 2018 18:45:55 -0500 Subject: [PATCH 2846/4033] add compilation support to cljs.main rename -o to -d -o is now --output-to add -l for setting optimization level add -w for watch builds add new main dispatch -c / --compile tweak cljs.closure/build so it can take nil for source update docstring --- src/main/clojure/cljs/cli.clj | 83 ++++++++++++++++++++++++------- src/main/clojure/cljs/closure.clj | 3 +- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 86f264dbb..b9e0dd29b 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -28,6 +28,18 @@ [inits-map value] (assoc-in inits-map [:options :verbose] (= value "true"))) +(defn watch-opt + [inits-map path] + (assoc-in inits-map [:options :watch] path)) + +(defn optimize-opt + [inits-map level] + (assoc-in inits-map [:options :optimizations] (keyword level))) + +(defn output-to-opt + [inits-map path] + (assoc-in inits-map [:options :output-to] path)) + (defn init-opt [inits-map file] (update-in inits-map [:inits] @@ -51,8 +63,14 @@ "--eval" eval-opt "-v" verbose-opt "--verbose" verbose-opt - "-o" output-dir-opt - "--output-dir" output-dir-opt} opt)) + "-d" output-dir-opt + "--output-dir" output-dir-opt + "-o" output-to-opt + "--output-to" output-to-opt + "-l" optimize-opt + "-level" optimize-opt + "-w" watch-opt + "--watch" watch-opt} opt)) (defn- initialize "Common initialize routine for repl, script, and null opts" @@ -152,22 +170,40 @@ present" :args args :inits inits})) +(defn compile-opt + [repl-env [_ ns-name & args] inits] + (let [{:keys [options]} (initialize inits) + env-opts (repl/-repl-options (repl-env)) + main-ns (symbol ns-name) + opts (merge options + {:main main-ns} + (when-let [target (:target env-opts)] + {:target target})) + source (when (= :none (:optimizations opts)) + (:uri (build/ns->location main-ns)))] + (if-let [path (:watch opts)] + (build/watch path opts) + (build/build source opts)))) + (defn main-dispatch "Returns the handler associated with a main option" [repl-env opt] - ({"-r" (partial repl-opt repl-env) - "--repl" (partial repl-opt repl-env) - "-m" (partial main-opt repl-env) - "--main" (partial main-opt repl-env) - nil (partial null-opt repl-env) - "-h" (partial help-opt repl-env) - "--help" (partial help-opt repl-env) - "-?" (partial help-opt repl-env)} opt + ({"-r" (partial repl-opt repl-env) + "--repl" (partial repl-opt repl-env) + "-m" (partial main-opt repl-env) + "--main" (partial main-opt repl-env) + "-c" (partial compile-opt repl-env) + "--compile" (partial compile-opt repl-env) + nil (partial null-opt repl-env) + "-h" (partial help-opt repl-env) + "--help" (partial help-opt repl-env) + "-?" (partial help-opt repl-env)} opt (partial script-opt repl-env))) (def main-opts - #{"-r" "--repl" - "-m" "--main" + #{"-m" "--main" + "-r" "--repl" + "-c" "--compile" nil}) (def valid-opts @@ -175,8 +211,11 @@ present" #{"-i" "--init" "-e" "--eval" "-v" "--verbose" - "-o" "--output-dir" - "-h" "--help" "-?"})) + "-d" "--output-dir" + "-o" "--output-to" + "-h" "--help" "-?" + "-l" "--level" + "-w" "--watch"})) (defn normalize [args] (if (not (contains? main-opts (first args))) @@ -202,25 +241,33 @@ present" With no options or args, runs an interactive Read-Eval-Print Loop - init options: + init options for --main and --repl: -js, --js-engine engine The JavaScript engine to use. Built-in supported engines: nashorn, node, browser, rhino. Defaults to nashorn -i, --init path Load a file or resource -e, --eval string Evaluate expressions in string; print non-nil values -v, --verbose bool if true, will enable ClojureScript verbose logging - -o, --output-dir path Set the output directory to use. If supplied, .cljsc_opts + -d, --output-dir path Set the output directory to use. If supplied, .cljsc_opts in that directory will be used to set ClojureScript - compiler options. + compiler options + + init options for --compile: + -o, --output-to Set the output compiled file + -l, --level level Set optimization level, only effective with -c main + option. Valid values are: none, whitespace, simple, + advanced + -w, --watch path Continuously build, only effect with -c main option main options: -m, --main ns-name Call the -main function from a namespace with args -r, --repl Run a repl + -c, --compile ns-name Compile a namespace path Run a script from a file or resource - Run a script from standard input -h, -?, --help Print this help message and exit - operation: + For --main and --repl: - Enters the user namespace - Binds *command-line-args* to a seq of strings containing command line diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 30db6e009..1827b98ce 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2684,7 +2684,8 @@ (not (false? (:static-fns opts)))) (:static-fns opts) ana/*cljs-static-fns*) - sources (-find-sources source opts)] + sources (when source + (-find-sources source opts))] (check-output-to opts) (check-output-dir opts) (check-source-map opts) From f81183cbd9c041778daeadbfe466875bd01b49be Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 13 Feb 2018 18:46:49 -0500 Subject: [PATCH 2847/4033] browser REPL should return :browser-repl true compiler option --- src/main/clojure/cljs/repl/browser.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index dd03d401a..49288765b 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -275,7 +275,8 @@ (.shutdown (:es this))) repl/IReplEnvOptions (-repl-options [this] - {:repl-requires + {:browser-repl true + :repl-requires '[[clojure.browser.repl]]}) repl/IParseStacktrace (-parse-stacktrace [this st err opts] From e3190ef58085d8ffe2927f9da3b2dc53d53c51ef Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 13 Feb 2018 18:56:43 -0500 Subject: [PATCH 2848/4033] :none is the default --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index b9e0dd29b..55c41783d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -179,7 +179,7 @@ present" {:main main-ns} (when-let [target (:target env-opts)] {:target target})) - source (when (= :none (:optimizations opts)) + source (when (= :none (:optimizations opts :none)) (:uri (build/ns->location main-ns)))] (if-let [path (:watch opts)] (build/watch path opts) From d11dce513838be4d2ffdad6f40fb0db2cf580af1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 13 Feb 2018 19:06:39 -0500 Subject: [PATCH 2849/4033] get :target and :browser-repl from repl options --- src/main/clojure/cljs/cli.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 55c41783d..1bc3860c3 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -177,8 +177,7 @@ present" main-ns (symbol ns-name) opts (merge options {:main main-ns} - (when-let [target (:target env-opts)] - {:target target})) + (select-keys env-opts [:target :browser-repl])) source (when (= :none (:optimizations opts :none)) (:uri (build/ns->location main-ns)))] (if-let [path (:watch opts)] From c883e50fe9d8eda586debaff605453db26ffe056 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 13 Feb 2018 19:24:41 -0500 Subject: [PATCH 2850/4033] -l -> -O --- src/main/clojure/cljs/cli.clj | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 1bc3860c3..3353e1512 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -57,20 +57,20 @@ (defn init-dispatch "Returns the handler associated with an init opt" [opt] - ({"-i" init-opt - "--init" init-opt - "-e" eval-opt - "--eval" eval-opt - "-v" verbose-opt - "--verbose" verbose-opt - "-d" output-dir-opt - "--output-dir" output-dir-opt - "-o" output-to-opt - "--output-to" output-to-opt - "-l" optimize-opt - "-level" optimize-opt - "-w" watch-opt - "--watch" watch-opt} opt)) + ({"-i" init-opt + "--init" init-opt + "-e" eval-opt + "--eval" eval-opt + "-v" verbose-opt + "--verbose" verbose-opt + "-d" output-dir-opt + "--output-dir" output-dir-opt + "-o" output-to-opt + "--output-to" output-to-opt + "-O" optimize-opt + "--optimizations" optimize-opt + "-w" watch-opt + "--watch" watch-opt} opt)) (defn- initialize "Common initialize routine for repl, script, and null opts" @@ -213,7 +213,7 @@ present" "-d" "--output-dir" "-o" "--output-to" "-h" "--help" "-?" - "-l" "--level" + "-O" "--optimizations" "-w" "--watch"})) (defn normalize [args] @@ -253,7 +253,7 @@ present" init options for --compile: -o, --output-to Set the output compiled file - -l, --level level Set optimization level, only effective with -c main + -O, --optimizations Set optimization level, only effective with -c main option. Valid values are: none, whitespace, simple, advanced -w, --watch path Continuously build, only effect with -c main option From ebc16c7f2ebffbd632cbcd581c019769f714b6f2 Mon Sep 17 00:00:00 2001 From: Andrea Richiardi Date: Thu, 8 Feb 2018 11:51:08 -0800 Subject: [PATCH 2851/4033] CLJS-2493: Self host: respect :source-map-timestamp compiler option The patch adds support for :source-map-timestamp in cljs.js so that when false we avoid appending "?rel=" to the source map file name. --- src/main/cljs/cljs/js.cljs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 79dc41cd8..cf2644446 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -155,16 +155,18 @@ (str pre s)) (defn- append-source-map - [state name source sb sm-data {:keys [output-dir asset-path] :as opts}] + [state name source sb sm-data {:keys [output-dir asset-path source-map-timestamp] :as opts}] (let [t (.valueOf (js/Date.)) smn (if name (string/replace (munge (str name)) "." "/") (str "cljs-" t)) ts (.valueOf (js/Date.)) out (or output-dir asset-path) - src (cond-> (str smn ".cljs?rel=" ts) + src (cond-> (str smn ".cljs") + (true? source-map-timestamp) (str "?rel=" ts) out (prefix (str out "/"))) - file (cond-> (str smn ".js?rel=" ts) + file (cond-> (str smn ".js") + (true? source-map-timestamp) (str "?rel=" ts) out (prefix (str out "/"))) json (sm/encode {src (:source-map sm-data)} {:lines (+ (:gen-line sm-data) 3) From 78b2395960767ea44b68ddd632d1dfe9a4957853 Mon Sep 17 00:00:00 2001 From: r0man Date: Tue, 13 Feb 2018 16:07:01 +0100 Subject: [PATCH 2852/4033] CLJS-2519: Module loader doesn't load :cljs-base properly --- src/main/clojure/cljs/closure.clj | 8 +++-- src/test/cljs_build/code-split/index.html | 9 +++++ src/test/cljs_build/code-split/repl.clj | 26 +++++++++++++++ .../code-split/src/code/split/a.cljs | 31 +++++++++++++++++ .../code-split/src/code/split/b.cljs | 9 +++++ .../code-split/src/code/split/c.cljs | 11 +++++++ .../code-split/src/code/split/d.cljs | 4 +++ src/test/clojure/cljs/build_api_tests.clj | 33 +++++++++++++++++++ src/test/clojure/cljs/test_util.clj | 8 +++++ 9 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 src/test/cljs_build/code-split/index.html create mode 100644 src/test/cljs_build/code-split/repl.clj create mode 100644 src/test/cljs_build/code-split/src/code/split/a.cljs create mode 100644 src/test/cljs_build/code-split/src/code/split/b.cljs create mode 100644 src/test/cljs_build/code-split/src/code/split/c.cljs create mode 100644 src/test/cljs_build/code-split/src/code/split/d.cljs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1827b98ce..0b636376b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1883,8 +1883,12 @@ The deps file for the current project will include third-party libraries." [{:keys [modules] :as opts} & sources] - ;; this source-on-disk call is currently necessary for REPLs - David - (let [disk-sources (doall + (let [{:keys [modules] :as opts} + (assoc opts :modules + (ensure-cljs-base-module + (module-graph/expand-modules modules sources) opts)) + ;; this source-on-disk call is currently necessary for REPLs - David + disk-sources (doall (remove #(= (:group %) :goog) (map #(source-on-disk opts %) sources))) goog-deps (io/file (util/output-directory opts) "goog" "deps.js") diff --git a/src/test/cljs_build/code-split/index.html b/src/test/cljs_build/code-split/index.html new file mode 100644 index 000000000..e3bf72d46 --- /dev/null +++ b/src/test/cljs_build/code-split/index.html @@ -0,0 +1,9 @@ + + +

Module A

+ + + + + + diff --git a/src/test/cljs_build/code-split/repl.clj b/src/test/cljs_build/code-split/repl.clj new file mode 100644 index 000000000..bc751f69d --- /dev/null +++ b/src/test/cljs_build/code-split/repl.clj @@ -0,0 +1,26 @@ +;; Instructions: + +;; ./script/uberjar +;; cd src/test/cljs_build/code-split +;; java -cp ../../../../target/cljs.jar:src clojure.main repl.clj +;; chromium http://localhost:9000/index.html + +(require '[cljs.repl :as r]) +(require '[cljs.build.api :as b]) +(require '[cljs.repl.browser :as rb]) + +(def opts + {:output-dir "out" + :asset-path "/out" + :optimizations :none + :modules {:a {:entries '#{code.split.a} + :output-to "out/a.js"} + :b {:entries '#{code.split.b} + :output-to "out/b.js"} + :c {:entries '#{code.split.c} + :output-to "out/c.js"}} + :browser-repl true + :verbose true}) + +(b/build "src" opts) +(r/repl* (rb/repl-env) (dissoc opts :modules)) diff --git a/src/test/cljs_build/code-split/src/code/split/a.cljs b/src/test/cljs_build/code-split/src/code/split/a.cljs new file mode 100644 index 000000000..3c1597777 --- /dev/null +++ b/src/test/cljs_build/code-split/src/code/split/a.cljs @@ -0,0 +1,31 @@ +(ns code.split.a + (:require [cljs.loader :as loader] + [clojure.pprint :refer [pprint]] + [goog.dom :as gdom] + [goog.events :as events]) + (:import [goog.debug Console] + [goog.events EventType])) + +(def loader + "The module loader." + (.getLoader loader/*module-manager*)) + +;; Enable logging, to see debug messages. +(.setCapturing (Console.) true) + +(defn print-modules [] + (println "Module Infos:") + (pprint loader/module-infos) + (println "Module URIs:") + (pprint loader/module-uris)) + +(events/listen (gdom/getElement "button-b") EventType.CLICK + (fn [e] (loader/load :b #((resolve 'code.split.b/hello))))) + +(events/listen (gdom/getElement "button-c") EventType.CLICK + (fn [e] (loader/load :c #((resolve 'code.split.c/hello))))) + +(enable-console-print!) +(print-modules) + +(loader/set-loaded! :a) diff --git a/src/test/cljs_build/code-split/src/code/split/b.cljs b/src/test/cljs_build/code-split/src/code/split/b.cljs new file mode 100644 index 000000000..800ca25e0 --- /dev/null +++ b/src/test/cljs_build/code-split/src/code/split/b.cljs @@ -0,0 +1,9 @@ +(ns code.split.b + (:require [cljs.loader :as loader] + [code.split.d :as d])) + +(defn hello [] + (println "Hello from code.split.b.") + (d/hello)) + +(loader/set-loaded! :b) diff --git a/src/test/cljs_build/code-split/src/code/split/c.cljs b/src/test/cljs_build/code-split/src/code/split/c.cljs new file mode 100644 index 000000000..e70cd5be2 --- /dev/null +++ b/src/test/cljs_build/code-split/src/code/split/c.cljs @@ -0,0 +1,11 @@ +(ns code.split.c + (:require [cljs.loader :as loader] + [code.split.d :as d])) + +(enable-console-print!) + +(defn hello [] + (println "Hello from code.split.c.") + (d/hello)) + +(loader/set-loaded! :c) diff --git a/src/test/cljs_build/code-split/src/code/split/d.cljs b/src/test/cljs_build/code-split/src/code/split/d.cljs new file mode 100644 index 000000000..fe481fdb7 --- /dev/null +++ b/src/test/cljs_build/code-split/src/code/split/d.cljs @@ -0,0 +1,4 @@ +(ns code.split.d) + +(defn hello [] + (println "Hello from code.split.d.")) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 92b79bbee..c615c76e9 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -540,3 +540,36 @@ (is (re-find #"module\$.*\$node_modules\$left_pad\$index\[\"default\"\]\(42,5,0\)" (slurp foreign-lib-file)))) (test/delete-out-files out) (test/delete-node-modules))) + +(deftest cljs-2519-test-cljs-base-entries + (let [dir (io/file "src" "test" "cljs_build" "code-split") + out (io/file (test/tmp-dir) "cljs-base-entries") + opts {:output-dir (str out) + :asset-path "/out" + :optimizations :none + :modules {:a {:entries '#{code.split.a} + :output-to (io/file out "a.js")} + :b {:entries '#{code.split.b} + :output-to (io/file out "b.js")} + :c {:entries '#{code.split.c} + :output-to (io/file out "c.js")}}}] + (test/delete-out-files out) + (build/build (build/inputs dir) opts) + (testing "Module :cljs-base" + (let [content (slurp (io/file out "cljs_base.js"))] + (testing "requires code.split.d (used in :b and :c)" + (is (test/document-write? content 'code.split.d))))) + (testing "Module :a" + (let [content (slurp (-> opts :modules :a :output-to))] + (testing "requires code.split.a" + (is (test/document-write? content 'code.split.a))) + (testing "requires cljs.pprint (only used in :a)" + (is (test/document-write? content 'cljs.pprint))))) + (testing "Module :b" + (let [content (slurp (-> opts :modules :b :output-to))] + (testing "requires code.split.b" + (is (test/document-write? content 'code.split.b))))) + (testing "Module :c" + (let [content (slurp (-> opts :modules :c :output-to))] + (testing "requires code.split.c" + (is (test/document-write? content 'code.split.c))))))) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index 7e9d96288..c1ec705ea 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -27,6 +27,14 @@ (doseq [f (file-seq nm)] (.delete f))))) +(defn document-write? + "Returns true if the string `s` contains a document.write statement to + load the namespace `ns`, otherwise false." + [s ns] + (->> (format "document.write('');" ns) + (string/index-of s) + (some?))) + (defn project-with-modules "Returns the build config for a project that uses Google Closure modules." [output-dir] From e4ac870ac5b958caa79b4aed7705a313663497ae Mon Sep 17 00:00:00 2001 From: r0man Date: Wed, 14 Feb 2018 16:24:40 +0100 Subject: [PATCH 2853/4033] CLJS-2521: Only expand module graph when modules are actually used Fixes a regression in self-parity introduced with CLJS-2519 --- src/main/clojure/cljs/closure.clj | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0b636376b..cd66b9ad5 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1883,12 +1883,8 @@ The deps file for the current project will include third-party libraries." [{:keys [modules] :as opts} & sources] - (let [{:keys [modules] :as opts} - (assoc opts :modules - (ensure-cljs-base-module - (module-graph/expand-modules modules sources) opts)) - ;; this source-on-disk call is currently necessary for REPLs - David - disk-sources (doall + ;; this source-on-disk call is currently necessary for REPLs - David + (let [disk-sources (doall (remove #(= (:group %) :goog) (map #(source-on-disk opts %) sources))) goog-deps (io/file (util/output-directory opts) "goog" "deps.js") @@ -1905,14 +1901,15 @@ (util/debug-prn (pr-str sources))) (cond modules - (do + (let [modules' (module-graph/expand-modules modules sources)] (output-deps) (doall (map (fn [[module-name _]] (output-main-file (merge opts - {:module-name module-name}))) + {:module-name module-name + :modules modules'}))) modules))) main From 633aaef52be892336c0b18264d57d012c4116483 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 14 Feb 2018 10:47:18 -0500 Subject: [PATCH 2854/4033] .cljsc_opts -> cljsc_opts.edn --- src/main/clojure/cljs/cli.clj | 8 ++++---- src/main/clojure/cljs/closure.clj | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 3353e1512..fd93ce153 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -103,7 +103,7 @@ present" (let [{:keys [options inits]} (initialize inits) renv (repl-env) coptsf (when-let [od (:output-dir options)] - (io/file od ".cljsc_opts")) + (io/file od "cljsc_opts.edn")) opts (as-> (build/add-implicit-options (merge (repl/-repl-options renv) options)) opts @@ -247,9 +247,9 @@ present" -i, --init path Load a file or resource -e, --eval string Evaluate expressions in string; print non-nil values -v, --verbose bool if true, will enable ClojureScript verbose logging - -d, --output-dir path Set the output directory to use. If supplied, .cljsc_opts - in that directory will be used to set ClojureScript - compiler options + -d, --output-dir path Set the output directory to use. If supplied, + cljsc_opts.edn in that directory will be used to + set ClojureScript compiler options init options for --compile: -o, --output-to Set the output compiled file diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cd66b9ad5..5d8b7fc1d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2768,7 +2768,7 @@ (map (comp :externs second) (get @compiler-env ::ana/namespaces))) (str (util/output-directory opts) "/inferred_externs.js"))) - _ (spit (io/file (util/output-directory opts) ".cljsc_opts") (pr-str orig-opts)) + _ (spit (io/file (util/output-directory opts) "cljsc_opts.edn") (pr-str orig-opts)) optim (:optimizations opts) ret (if (and optim (not= optim :none)) (do From dad02eb1cf430cade858f28ae0befc51c304f048 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 14 Feb 2018 11:03:40 -0500 Subject: [PATCH 2855/4033] add -t / --target option, takes priority over implicit :target --- src/main/clojure/cljs/cli.clj | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index fd93ce153..7d40868ce 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -40,6 +40,10 @@ [inits-map path] (assoc-in inits-map [:options :output-to] path)) +(defn target-opt + [inits-map target] + (assoc-in inits-map [:options :target] (keyword target))) + (defn init-opt [inits-map file] (update-in inits-map [:inits] @@ -67,6 +71,8 @@ "--output-dir" output-dir-opt "-o" output-to-opt "--output-to" output-to-opt + "-t" target-opt + "--target" target-opt "-O" optimize-opt "--optimizations" optimize-opt "-w" watch-opt @@ -175,9 +181,10 @@ present" (let [{:keys [options]} (initialize inits) env-opts (repl/-repl-options (repl-env)) main-ns (symbol ns-name) - opts (merge options - {:main main-ns} - (select-keys env-opts [:target :browser-repl])) + opts (merge + (select-keys env-opts [:target :browser-repl]) + options + {:main main-ns}) source (when (= :none (:optimizations opts :none)) (:uri (build/ns->location main-ns)))] (if-let [path (:watch opts)] @@ -212,6 +219,7 @@ present" "-v" "--verbose" "-d" "--output-dir" "-o" "--output-to" + "-t" "--target" "-h" "--help" "-?" "-O" "--optimizations" "-w" "--watch"})) From 194ef60312f095debd9faf31a777fb402ad03875 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 14 Feb 2018 11:06:53 -0500 Subject: [PATCH 2856/4033] update docstring for target --- src/main/clojure/cljs/cli.clj | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 7d40868ce..5b7267e69 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -248,7 +248,11 @@ present" With no options or args, runs an interactive Read-Eval-Print Loop - init options for --main and --repl: + init options: + -t, --target name The JavaScript target. Supported values: nodejs, + nashorn, webworker + + init options only for --main and --repl: -js, --js-engine engine The JavaScript engine to use. Built-in supported engines: nashorn, node, browser, rhino. Defaults to nashorn @@ -259,7 +263,7 @@ present" cljsc_opts.edn in that directory will be used to set ClojureScript compiler options - init options for --compile: + init options only for --compile: -o, --output-to Set the output compiled file -O, --optimizations Set optimization level, only effective with -c main option. Valid values are: none, whitespace, simple, @@ -267,12 +271,12 @@ present" -w, --watch path Continuously build, only effect with -c main option main options: - -m, --main ns-name Call the -main function from a namespace with args - -r, --repl Run a repl - -c, --compile ns-name Compile a namespace - path Run a script from a file or resource - - Run a script from standard input - -h, -?, --help Print this help message and exit + -m, --main ns-name Call the -main function from a namespace with args + -r, --repl Run a repl + -c, --compile ns-name Compile a namespace + path Run a script from a file or resource + - Run a script from standard input + -h, -?, --help Print this help message and exit For --main and --repl: From 38488bb3d53ae65696f48e87cdd5ed80919383bd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 14 Feb 2018 11:09:24 -0500 Subject: [PATCH 2857/4033] -js / --js-engine -> -re / --repl-env --- src/main/cljs/cljs/main.clj | 4 ++-- src/main/clojure/cljs/cli.clj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index c6568cf27..91c7da335 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -33,7 +33,7 @@ (defn normalize* [args] (if (not (contains? cli/main-opts (first args))) - (let [pred (complement #{"-js" "--js-engine"}) + (let [pred (complement #{"-re" "--repl-env"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) args)] @@ -51,7 +51,7 @@ (defn -main [& args] (let [args (normalize (cli/normalize args)) - pred (complement #{"-js" "--js-engine"}) + pred (complement #{"-re" "--repl-env"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) args) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 5b7267e69..4585c4bdf 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -253,8 +253,8 @@ present" nashorn, webworker init options only for --main and --repl: - -js, --js-engine engine The JavaScript engine to use. Built-in supported - engines: nashorn, node, browser, rhino. Defaults to + -re, --repl-env The REPL environment to use. Built-in supported + values: nashorn, node, browser, rhino. Defaults to nashorn -i, --init path Load a file or resource -e, --eval string Evaluate expressions in string; print non-nil values From 0472aff50086f1eb25d0bb54f0e3d70038a72720 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 14 Feb 2018 14:30:50 -0500 Subject: [PATCH 2858/4033] allow REPL envs to control ::main & ::compile --- src/main/cljs/cljs/main.clj | 2 +- src/main/clojure/cljs/cli.clj | 175 +++++++++++++++------------------- 2 files changed, 78 insertions(+), 99 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index 91c7da335..3c5396119 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -32,7 +32,7 @@ nashorn/repl-env)) (defn normalize* [args] - (if (not (contains? cli/main-opts (first args))) + (if (not (contains? @cli/main-dispatch (first args))) (let [pred (complement #{"-re" "--repl-env"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 4585c4bdf..90b47bf87 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -21,77 +21,75 @@ (declare main) (defn output-dir-opt - [inits-map output-dir] - (assoc-in inits-map [:options :output-dir] output-dir)) + [cfg output-dir] + (assoc-in cfg [:options :output-dir] output-dir)) (defn verbose-opt - [inits-map value] - (assoc-in inits-map [:options :verbose] (= value "true"))) + [cfg value] + (assoc-in cfg [:options :verbose] (= value "true"))) (defn watch-opt - [inits-map path] - (assoc-in inits-map [:options :watch] path)) + [cfg path] + (assoc-in cfg [:options :watch] path)) (defn optimize-opt - [inits-map level] - (assoc-in inits-map [:options :optimizations] (keyword level))) + [cfg level] + (assoc-in cfg [:options :optimizations] (keyword level))) (defn output-to-opt - [inits-map path] - (assoc-in inits-map [:options :output-to] path)) + [cfg path] + (assoc-in cfg [:options :output-to] path)) (defn target-opt - [inits-map target] - (assoc-in inits-map [:options :target] (keyword target))) + [cfg target] + (assoc-in cfg [:options :target] (keyword target))) (defn init-opt - [inits-map file] - (update-in inits-map [:inits] + [cfg file] + (update-in cfg [:inits] (fnil conj []) {:type :init-script :script file})) (defn eval-opt - [inits-map form-str] - (update-in inits-map [:inits] + [cfg form-str] + (update-in cfg [:inits] (fnil conj []) {:type :eval-forms :forms (ana-api/forms-seq (StringReader. form-str))})) -(defn init-dispatch - "Returns the handler associated with an init opt" - [opt] - ({"-i" init-opt - "--init" init-opt - "-e" eval-opt - "--eval" eval-opt - "-v" verbose-opt - "--verbose" verbose-opt - "-d" output-dir-opt - "--output-dir" output-dir-opt - "-o" output-to-opt - "--output-to" output-to-opt - "-t" target-opt - "--target" target-opt - "-O" optimize-opt - "--optimizations" optimize-opt - "-w" watch-opt - "--watch" watch-opt} opt)) +(def init-dispatch + (atom + {"-i" {:fn init-opt} + "--init" {:fn init-opt} + "-e" {:fn eval-opt} + "--eval" {:fn eval-opt} + "-v" {:fn verbose-opt} + "--verbose" {:fn verbose-opt} + "-d" {:fn output-dir-opt} + "--output-dir" {:fn output-dir-opt} + "-o" {:fn output-to-opt} + "--output-to" {:fn output-to-opt} + "-t" {:fn target-opt} + "--target" {:fn target-opt} + "-O" {:fn optimize-opt} + "--optimizations" {:fn optimize-opt} + "-w" {:fn watch-opt} + "--watch" {:fn watch-opt}})) (defn- initialize "Common initialize routine for repl, script, and null opts" [inits] (reduce (fn [ret [opt arg]] - ((init-dispatch opt) ret arg)) + ((:fn (@init-dispatch opt)) ret arg)) {} inits)) (defn repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" - [repl-env [_ & args] inits] - (let [{:keys [options inits]} (initialize inits) - renv (repl-env) + [repl-env [_ & args] {:keys [options inits] :as cfg}] + (let [renv (repl-env) opts (build/add-implicit-options (merge (repl/-repl-options renv) options))] (repl/repl* renv @@ -104,10 +102,9 @@ present" inits))))) (defn main-opt* - [repl-env {:keys [main script args inits]}] + [repl-env {:keys [main script args options inits] :as cfg}] (env/ensure - (let [{:keys [options inits]} (initialize inits) - renv (repl-env) + (let [renv (repl-env) coptsf (when-let [od (:output-dir options)] (io/file od "cljsc_opts.edn")) opts (as-> @@ -152,35 +149,29 @@ present" (defn main-opt "Call the -main function from a namespace with string arguments from the command line." - [repl-env [_ main-ns & args] inits] - (main-opt* repl-env - {:main main-ns - :args args - :inits inits})) + [repl-env [_ ns & args] cfg] + ((::main (repl/-repl-options (repl-env)) main-opt*) + repl-env (merge cfg {:main ns :args args}))) (defn- null-opt "No repl or script opt present, just bind args and run inits" - [repl-env args inits] - (main-opt* repl-env - {:args args - :inits inits})) + [repl-env args cfg] + ((::main (repl/-repl-options (repl-env)) main-opt*) + repl-env (merge cfg {:args args}))) (defn- help-opt [_ _ _] (println (:doc (meta (var main))))) (defn script-opt - [repl-env [path & args] inits] - (main-opt* repl-env - {:script path - :args args - :inits inits})) - -(defn compile-opt - [repl-env [_ ns-name & args] inits] - (let [{:keys [options]} (initialize inits) - env-opts (repl/-repl-options (repl-env)) - main-ns (symbol ns-name) + [repl-env [path & args] cfg] + ((::main (repl/-repl-options (repl-env)) main-opt*) + repl-env (merge cfg {:script path :args args}))) + +(defn compile-opt* + [repl-env {:keys [ns args options] :as cfg}] + (let [env-opts (repl/-repl-options (repl-env)) + main-ns (symbol ns) opts (merge (select-keys env-opts [:target :browser-repl]) options @@ -191,41 +182,26 @@ present" (build/watch path opts) (build/build source opts)))) -(defn main-dispatch - "Returns the handler associated with a main option" - [repl-env opt] - ({"-r" (partial repl-opt repl-env) - "--repl" (partial repl-opt repl-env) - "-m" (partial main-opt repl-env) - "--main" (partial main-opt repl-env) - "-c" (partial compile-opt repl-env) - "--compile" (partial compile-opt repl-env) - nil (partial null-opt repl-env) - "-h" (partial help-opt repl-env) - "--help" (partial help-opt repl-env) - "-?" (partial help-opt repl-env)} opt - (partial script-opt repl-env))) - -(def main-opts - #{"-m" "--main" - "-r" "--repl" - "-c" "--compile" - nil}) - -(def valid-opts - (into main-opts - #{"-i" "--init" - "-e" "--eval" - "-v" "--verbose" - "-d" "--output-dir" - "-o" "--output-to" - "-t" "--target" - "-h" "--help" "-?" - "-O" "--optimizations" - "-w" "--watch"})) +(defn compile-opt + [repl-env [_ ns & args] cfg] + ((::compile (repl/-repl-options (repl-env)) compile-opt*) + repl-env (merge cfg {:args args :ns ns}))) + +(def main-dispatch + (atom + {"-r" {:fn repl-opt} + "--repl" {:fn repl-opt} + "-m" {:fn main-opt} + "--main" {:fn main-opt} + "-c" {:fn compile-opt} + "--compile" {:fn compile-opt} + nil {:fn null-opt} + "-h" {:fn help-opt} + "--help" {:fn help-opt} + "-?" {:fn help-opt}})) (defn normalize [args] - (if (not (contains? main-opts (first args))) + (if (not (contains? (set (keys @main-dispatch)) (first args))) (let [pred (complement #{"-v" "--verbose"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) @@ -233,7 +209,9 @@ present" (cond (= pre args) pre - (contains? valid-opts (fnext post)) + (contains? + (into (set (keys @main-dispatch)) + (keys @init-dispatch)) (fnext post)) (concat pre [(first post) "true"] (normalize (next post))) @@ -298,9 +276,10 @@ present" (try (if args (loop [[opt arg & more :as args] (normalize args) inits []] - (if (init-dispatch opt) + (if (contains? @init-dispatch opt) (recur more (conj inits [opt arg])) - ((main-dispatch repl-env opt) args inits))) + ((:fn (@main-dispatch opt script-opt)) repl-env args + (initialize inits)))) (repl-opt repl-env nil nil)) (finally (flush)))) From ba56bc078328e07a847904a47b1704d37681c825 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 14 Feb 2018 16:53:10 -0600 Subject: [PATCH 2859/4033] extensible dispatch, map node to nodejs for target, wip help formatting --- src/main/cljs/cljs/main.clj | 2 +- src/main/clojure/cljs/cli.clj | 193 ++++++++++++++++++++++++++-------- 2 files changed, 153 insertions(+), 42 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index 3c5396119..be54b6155 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -32,7 +32,7 @@ nashorn/repl-env)) (defn normalize* [args] - (if (not (contains? @cli/main-dispatch (first args))) + (if (not (cli/dispatch? :main (first args))) (let [pred (complement #{"-re" "--repl-env"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 90b47bf87..b3fd6c53d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -8,6 +8,7 @@ (ns cljs.cli (:require [clojure.java.io :as io] + [clojure.string :as string] [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] @@ -16,10 +17,16 @@ [cljs.repl :as repl] [cljs.build.api :as build] [clojure.edn :as edn]) - (:import [java.io StringReader])) + (:import [java.io StringReader] + [java.text BreakIterator] + [java.util Locale])) (declare main) +(defonce _cli_registry + (atom {:main-dispatch nil + :init-dispatch nil})) + (defn output-dir-opt [cfg output-dir] (assoc-in cfg [:options :output-dir] output-dir)) @@ -42,7 +49,8 @@ (defn target-opt [cfg target] - (assoc-in cfg [:options :target] (keyword target))) + (let [target (if (= "node" target) "nodejs" target)] + (assoc-in cfg [:options :target] (keyword target)))) (defn init-opt [cfg file] @@ -58,31 +66,19 @@ {:type :eval-forms :forms (ana-api/forms-seq (StringReader. form-str))})) -(def init-dispatch - (atom - {"-i" {:fn init-opt} - "--init" {:fn init-opt} - "-e" {:fn eval-opt} - "--eval" {:fn eval-opt} - "-v" {:fn verbose-opt} - "--verbose" {:fn verbose-opt} - "-d" {:fn output-dir-opt} - "--output-dir" {:fn output-dir-opt} - "-o" {:fn output-to-opt} - "--output-to" {:fn output-to-opt} - "-t" {:fn target-opt} - "--target" {:fn target-opt} - "-O" {:fn optimize-opt} - "--optimizations" {:fn optimize-opt} - "-w" {:fn watch-opt} - "--watch" {:fn watch-opt}})) +(defn get-dispatch + ([k opt] + (get-dispatch k opt nil)) + ([k opt default] + (let [k' (keyword (str (name k) "-dispatch"))] + (or (get-in @_cli_registry [k' opt]) default)))) (defn- initialize "Common initialize routine for repl, script, and null opts" [inits] (reduce (fn [ret [opt arg]] - ((:fn (@init-dispatch opt)) ret arg)) + ((get-dispatch :init opt) ret arg)) {} inits)) (defn repl-opt @@ -187,21 +183,138 @@ present" ((::compile (repl/-repl-options (repl-env)) compile-opt*) repl-env (merge cfg {:args args :ns ns}))) -(def main-dispatch - (atom - {"-r" {:fn repl-opt} - "--repl" {:fn repl-opt} - "-m" {:fn main-opt} - "--main" {:fn main-opt} - "-c" {:fn compile-opt} - "--compile" {:fn compile-opt} - nil {:fn null-opt} - "-h" {:fn help-opt} - "--help" {:fn help-opt} - "-?" {:fn help-opt}})) +(def help-template + "Usage: java -cp cljs.jar cljs.main [init-opt*] [main-opt] [arg*] + + With no options or args, runs an interactive Read-Eval-Print Loop + + %s + + For --main and --repl: + + - Enters the user namespace + - Binds *command-line-args* to a seq of strings containing command line + args that appear after any main option + - Runs all init options in order + - Calls a -main function or runs a repl or script if requested + + The init options may be repeated and mixed freely, but must appear before + any main option. + + Paths may be absolute or relative in the filesystem or relative to + classpath. Classpath-relative paths have prefix of @ or @/") + +(defn auto-fill + ([ws] + (auto-fill ws 40)) + ([^String ws max-len] + (let [b (BreakIterator/getLineInstance Locale/ENGLISH)] + (.setText b ws) + (loop [s (.first b) e (.next b) line-len 0 line "" ret []] + (if (not= e BreakIterator/DONE) + (let [w (.substring ws s e) + word-len (.length w) + line-len (+ line-len word-len)] + (if (> line-len max-len) + (recur e (.next b) word-len w (conj ret line)) + (recur e (.next b) line-len (str line w) ret))) + (conj ret (str line (.substring ws s (.length ws))))))))) + +(defn opt->str [[cs {:keys [arg doc]}]] + (let [[f & r] cs + fstr (cond-> (format "%1$5s" f) + (not (empty? r)) (str ", " (string/join ", " r)) + arg (str " " arg)) + filled (auto-fill doc)] + (if (< (.length fstr) 30) + (str + (str (format "%1$-30s" fstr) " " (first filled)) "\n" + (string/join "\n" + (map #(apply str (concat (repeat 6 " ") [%])) + (rest filled))))))) + +(def cli-options + {:groups [{::main&compile.init {:desc "init option"} + ::main.init {:desc "init options only for --main and --repl" + :pseudos {"path" "Run a script from a file or resource" + "-" "Run a script from standard input"}} + ::compile.init {:desc "init options only for --compile"} + ::main {:desc "main options"}}] + :init + {["-i" "--init"] {:group ::main.init :fn init-opt + :arg "path" + :doc "Load a file or resource"} + ["-e" "--eval"] {:group ::main.init :fn eval-opt + :arg "string" + :doc "Evaluate expressions in string; print non-nil values"} + ["-v" "--verbose"] {:group ::main.init :fn verbose-opt + :arg "bool" + :doc "If true, will enable ClojureScript verbose logging"} + ["-d" "--output-dir"] {:group ::main.init :fn output-dir-opt + :arg "path" + :doc (str "Set the output directory to use. If " + "supplied, cljsc_opts.edn in that directory " + "will be used to set ClojureScript compiler " + "options") } + ["-w" "--watch"] {:group ::main.init :fn watch-opt + :arg "path" + :doc "Continuously build, only effective with -c main option"} + ["-o" "--output-to"] {:group ::compile.init :fn output-to-opt + :arg "file" + :doc "Set the output compiled file"} + ["-O" "--optimizations"] {:group ::compile.init :fn optimize-opt + :arg "level" + :doc + (str "Set optimization level, only effective with " + "-c main option. Valid values are: none, " + "whitespace, simple, advanced")} + + ["-t" "--target"] {:group ::main&compile.init :fn target-opt + :arg "name" + :doc + (str "The JavaScript target. Supported values: " + "nodejs, nashorn, webworker") }} + :main + {["-r" "--repl"] {:group ::main :fn repl-opt + :doc "Run a repl"} + ["-m" "--main"] {:group ::main :fn main-opt + :arg "ns" + :doc "Call the -main function from a namespace with args"} + ["-c" "--compile"] {:group ::main :fn compile-opt + :arg "ns" + :doc "Compile a namespace"} + [nil] {:group ::main :fn null-opt} + ["-h" "--help" "-?"] {:group ::main :fn help-opt + :doc "Print this help message and exit"}}}) + +(defn get-options [k] + (if (= :all k) + (into (get-options :main) (get-options :init)) + (-> (get @_cli_registry (keyword (str (name k) "-dispatch"))) + keys set))) + +(defn dispatch? [k opt] + (contains? (get-options k) opt)) + +(defn register-options! [{:keys [groups main init]}] + (letfn [(merge-dispatch [st k options] + (update-in st [k] + (fn [m] + (reduce + (fn [ret [cs csm]] + (merge ret + (zipmap cs (repeat (:fn csm))))) + m options))))] + (swap! _cli_registry + (fn [st] + (-> st + (merge-dispatch :init-dispatch init) + (merge-dispatch :main-dispatch main)))))) + +(register-options! cli-options) (defn normalize [args] - (if (not (contains? (set (keys @main-dispatch)) (first args))) + (if (not (contains? (get-options :main) (first args))) (let [pred (complement #{"-v" "--verbose"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) @@ -209,9 +322,7 @@ present" (cond (= pre args) pre - (contains? - (into (set (keys @main-dispatch)) - (keys @init-dispatch)) (fnext post)) + (contains? (get-options :all) (fnext post)) (concat pre [(first post) "true"] (normalize (next post))) @@ -246,7 +357,7 @@ present" -O, --optimizations Set optimization level, only effective with -c main option. Valid values are: none, whitespace, simple, advanced - -w, --watch path Continuously build, only effect with -c main option + -w, --watch path Continuously build, only effective with -c main option main options: -m, --main ns-name Call the -main function from a namespace with args @@ -276,10 +387,10 @@ present" (try (if args (loop [[opt arg & more :as args] (normalize args) inits []] - (if (contains? @init-dispatch opt) + (if (dispatch? :init opt) (recur more (conj inits [opt arg])) - ((:fn (@main-dispatch opt script-opt)) repl-env args - (initialize inits)))) + ((get-dispatch :main opt script-opt) + repl-env args (initialize inits)))) (repl-opt repl-env nil nil)) (finally (flush)))) From b4d398a74d45ac83ec8d09f230ec02b6189d7036 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 14 Feb 2018 17:52:00 -0600 Subject: [PATCH 2860/4033] compute help string from the registry --- src/main/clojure/cljs/cli.clj | 258 ++++++++++++++++++---------------- 1 file changed, 140 insertions(+), 118 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index b3fd6c53d..510345e2f 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -23,10 +23,122 @@ (declare main) +;; ----------------------------------------------------------------------------- +;; Registry + (defonce _cli_registry (atom {:main-dispatch nil :init-dispatch nil})) +;; ----------------------------------------------------------------------------- +;; Help String formatting + +(def help-template + "Usage: java -cp cljs.jar cljs.main [init-opt*] [main-opt] [arg*] + +With no options or args, runs an interactive Read-Eval-Print Loop + +%s +For --main and --repl: + + - Enters the user namespace + - Binds *command-line-args* to a seq of strings containing command line + args that appear after any main option + - Runs all init options in order + - Calls a -main function or runs a repl or script if requested + +The init options may be repeated and mixed freely, but must appear before +any main option. + +Paths may be absolute or relative in the filesystem or relative to +classpath. Classpath-relative paths have prefix of @ or @/") + +(defn auto-fill + ([ws] + (auto-fill ws 50)) + ([^String ws max-len] + (let [b (BreakIterator/getLineInstance Locale/ENGLISH)] + (.setText b ws) + (loop [s (.first b) e (.next b) line-len 0 line "" ret []] + (if (not= e BreakIterator/DONE) + (let [w (.substring ws s e) + word-len (.length w) + line-len (+ line-len word-len)] + (if (> line-len max-len) + (recur e (.next b) word-len w (conj ret line)) + (recur e (.next b) line-len (str line w) ret))) + (conj ret (str line (.substring ws s (.length ws))))))))) + +(defn opt->str [cs {:keys [arg doc]}] + (letfn [(desc-string [filled] + (string/join "\n" + (map #(apply str (concat (repeat 6 " ") [%])) + filled)))] + (let [[f & r] cs + + fstr (cond-> (if (= 1 (count cs)) + (str " " f) + (format "%1$5s" f)) + (not (empty? r)) (str ", " (string/join ", " r)) + arg (str " " arg)) + filled (auto-fill doc)] + (if (< (.length fstr) 30) + (cond-> (str (format "%1$-30s" fstr) (first filled) "\n") + (seq (rest filled)) (str (desc-string (rest filled)) "\n")) + (str + fstr "\n" + (desc-string fstr) "\n"))))) + +(defn group->str [options group] + (let [{:keys [desc pseudos]} (get-in options [:groups group])] + (apply str + desc ":\n" + (->> (:init options) + (filter (fn [[k v]] (= (:group v) group))) + (concat pseudos) + (sort-by ffirst) + (map (fn [[k v]] (opt->str k v))))))) + +(defn primary-groups-str [options] + (str + (group->str options ::main&compile) "\n" + (group->str options ::main) "\n" + (group->str options ::compile) "\n")) + +(defn all-groups-str [{:keys [groups] :as options}] + (let [custom-groups + (disj (set (keys groups)) + ::main&compile ::main ::compile)] + (apply str + (primary-groups-str options) + (map + (fn [group] + (str (group->str options group) "\n")) + custom-groups)))) + +(defn main-str [options] + (let [pseudos {["path"] {:doc "Run a script from a file or resource"} + ["-"] {:doc "Run a script from standard input"}}] + (apply str + "main options:\n" + (->> (:main options) + (concat pseudos) + (sort-by ffirst) + (remove (fn [[k v]] (nil? (ffirst k)))) + (map (fn [[k v]] (opt->str k v))))))) + +(defn options-str [options] + (str + (all-groups-str options) + (main-str options))) + +(defn help-str [] + (format help-template + (options-str @_cli_registry))) + +;; ----------------------------------------------------------------------------- +;; Main + (defn output-dir-opt [cfg output-dir] (assoc-in cfg [:options :output-dir] output-dir)) @@ -157,7 +269,7 @@ present" (defn- help-opt [_ _ _] - (println (:doc (meta (var main))))) + (println (help-str))) (defn script-opt [repl-env [path & args] cfg] @@ -183,108 +295,60 @@ present" ((::compile (repl/-repl-options (repl-env)) compile-opt*) repl-env (merge cfg {:args args :ns ns}))) -(def help-template - "Usage: java -cp cljs.jar cljs.main [init-opt*] [main-opt] [arg*] - - With no options or args, runs an interactive Read-Eval-Print Loop - - %s - - For --main and --repl: - - - Enters the user namespace - - Binds *command-line-args* to a seq of strings containing command line - args that appear after any main option - - Runs all init options in order - - Calls a -main function or runs a repl or script if requested - - The init options may be repeated and mixed freely, but must appear before - any main option. - - Paths may be absolute or relative in the filesystem or relative to - classpath. Classpath-relative paths have prefix of @ or @/") - -(defn auto-fill - ([ws] - (auto-fill ws 40)) - ([^String ws max-len] - (let [b (BreakIterator/getLineInstance Locale/ENGLISH)] - (.setText b ws) - (loop [s (.first b) e (.next b) line-len 0 line "" ret []] - (if (not= e BreakIterator/DONE) - (let [w (.substring ws s e) - word-len (.length w) - line-len (+ line-len word-len)] - (if (> line-len max-len) - (recur e (.next b) word-len w (conj ret line)) - (recur e (.next b) line-len (str line w) ret))) - (conj ret (str line (.substring ws s (.length ws))))))))) - -(defn opt->str [[cs {:keys [arg doc]}]] - (let [[f & r] cs - fstr (cond-> (format "%1$5s" f) - (not (empty? r)) (str ", " (string/join ", " r)) - arg (str " " arg)) - filled (auto-fill doc)] - (if (< (.length fstr) 30) - (str - (str (format "%1$-30s" fstr) " " (first filled)) "\n" - (string/join "\n" - (map #(apply str (concat (repeat 6 " ") [%])) - (rest filled))))))) - (def cli-options - {:groups [{::main&compile.init {:desc "init option"} - ::main.init {:desc "init options only for --main and --repl" - :pseudos {"path" "Run a script from a file or resource" - "-" "Run a script from standard input"}} - ::compile.init {:desc "init options only for --compile"} - ::main {:desc "main options"}}] + {:groups {::main&compile {:desc "init option" + :pseudos + {["-re" "--repl-env"] + {:doc (str "The REPL environment to use. Built-in " + "supported values: nashorn, node, browser, " + "rhino. Defaults to nashorn")}}} + ::main {:desc "init options only for --main and --repl"} + ::compile {:desc "init options only for --compile"}} :init - {["-i" "--init"] {:group ::main.init :fn init-opt + {["-i" "--init"] {:group ::main :fn init-opt :arg "path" :doc "Load a file or resource"} - ["-e" "--eval"] {:group ::main.init :fn eval-opt + ["-e" "--eval"] {:group ::main :fn eval-opt :arg "string" :doc "Evaluate expressions in string; print non-nil values"} - ["-v" "--verbose"] {:group ::main.init :fn verbose-opt + ["-v" "--verbose"] {:group ::main :fn verbose-opt :arg "bool" :doc "If true, will enable ClojureScript verbose logging"} - ["-d" "--output-dir"] {:group ::main.init :fn output-dir-opt + ["-d" "--output-dir"] {:group ::main :fn output-dir-opt :arg "path" :doc (str "Set the output directory to use. If " "supplied, cljsc_opts.edn in that directory " "will be used to set ClojureScript compiler " "options") } - ["-w" "--watch"] {:group ::main.init :fn watch-opt + ["-w" "--watch"] {:group ::compile :fn watch-opt :arg "path" :doc "Continuously build, only effective with -c main option"} - ["-o" "--output-to"] {:group ::compile.init :fn output-to-opt + ["-o" "--output-to"] {:group ::compile :fn output-to-opt :arg "file" :doc "Set the output compiled file"} - ["-O" "--optimizations"] {:group ::compile.init :fn optimize-opt + ["-O" "--optimizations"] {:group ::compile :fn optimize-opt :arg "level" :doc (str "Set optimization level, only effective with " "-c main option. Valid values are: none, " "whitespace, simple, advanced")} - ["-t" "--target"] {:group ::main&compile.init :fn target-opt + ["-t" "--target"] {:group ::main&compile :fn target-opt :arg "name" :doc (str "The JavaScript target. Supported values: " "nodejs, nashorn, webworker") }} :main - {["-r" "--repl"] {:group ::main :fn repl-opt + {["-r" "--repl"] {:fn repl-opt :doc "Run a repl"} - ["-m" "--main"] {:group ::main :fn main-opt + ["-m" "--main"] {:fn main-opt :arg "ns" :doc "Call the -main function from a namespace with args"} - ["-c" "--compile"] {:group ::main :fn compile-opt + ["-c" "--compile"] {:fn compile-opt :arg "ns" :doc "Compile a namespace"} - [nil] {:group ::main :fn null-opt} - ["-h" "--help" "-?"] {:group ::main :fn help-opt + [nil] {:fn null-opt} + ["-h" "--help" "-?"] {:fn help-opt :doc "Print this help message and exit"}}}) (defn get-options [k] @@ -308,6 +372,9 @@ present" (swap! _cli_registry (fn [st] (-> st + (update-in [:groups] merge groups) + (update-in [:main] merge main) + (update-in [:init] merge init) (merge-dispatch :init-dispatch init) (merge-dispatch :main-dispatch main)))))) @@ -331,55 +398,10 @@ present" (normalize (nnext post))))) args)) -;; TODO: validate arg order to produce better error message - David (defn main - "Usage: java -cp cljs.jar cljs.main [init-opt*] [main-opt] [arg*] - - With no options or args, runs an interactive Read-Eval-Print Loop - - init options: - -t, --target name The JavaScript target. Supported values: nodejs, - nashorn, webworker - - init options only for --main and --repl: - -re, --repl-env The REPL environment to use. Built-in supported - values: nashorn, node, browser, rhino. Defaults to - nashorn - -i, --init path Load a file or resource - -e, --eval string Evaluate expressions in string; print non-nil values - -v, --verbose bool if true, will enable ClojureScript verbose logging - -d, --output-dir path Set the output directory to use. If supplied, - cljsc_opts.edn in that directory will be used to - set ClojureScript compiler options - - init options only for --compile: - -o, --output-to Set the output compiled file - -O, --optimizations Set optimization level, only effective with -c main - option. Valid values are: none, whitespace, simple, - advanced - -w, --watch path Continuously build, only effective with -c main option - - main options: - -m, --main ns-name Call the -main function from a namespace with args - -r, --repl Run a repl - -c, --compile ns-name Compile a namespace - path Run a script from a file or resource - - Run a script from standard input - -h, -?, --help Print this help message and exit - - For --main and --repl: - - - Enters the user namespace - - Binds *command-line-args* to a seq of strings containing command line - args that appear after any main option - - Runs all init options in order - - Calls a -main function or runs a repl or script if requested - - The init options may be repeated and mixed freely, but must appear before - any main option. - - Paths may be absolute or relative in the filesystem or relative to - classpath. Classpath-relative paths have prefix of @ or @/" + "A generic runner for ClojureScript. repl-env must satisfy + cljs.repl/IReplEnvOptions and cljs.repl/IJavaScriptEnv protocols. args is a + sequence of command line flags." [repl-env & args] ;; On OS X suppress the Dock icon (System/setProperty "apple.awt.UIElement" "true") From 155c2162f87d1402d5f83b750833bd1d4c010eaa Mon Sep 17 00:00:00 2001 From: r0man Date: Wed, 14 Feb 2018 18:11:45 +0100 Subject: [PATCH 2861/4033] CLJS-2522: Handle sources that are maps in build-modules --- src/main/clojure/cljs/closure.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5d8b7fc1d..1953c1f54 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1156,11 +1156,16 @@ [sources opts] (let [sources (map (fn [js] - (if (string? js) + (cond + (instance? JavaScriptFile js) + js + (map? js) + (map->JavaScriptFile js) + (string? js) (merge (map->javascript-file {:provides (deps/-provides js)}) {:source js}) - js)) + :else js)) sources) used (atom #{}) ;; track used inputs to avoid dupes modules From 0f08541275c6214b6a1326ccc24507228b1938a1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 14 Feb 2018 09:42:18 -0500 Subject: [PATCH 2862/4033] CLJS-2520: Synthesize ClojureScript version if using non-built ClojureScript dep --- src/main/clojure/cljs/util.cljc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index ffb625d0e..d73444425 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -19,6 +19,20 @@ ;; next line is auto-generated by the build-script - Do not edit! (def ^:dynamic *clojurescript-version*) +(defn- main-src-directory [] + (some (fn [file] + (when (= "main" (.getName file)) + file)) + (iterate (memfn getParentFile) (io/as-file (io/resource "cljs/util.cljc"))))) + +(defn- file-hash [file] + (if (.isDirectory file) + 0 + (Math/abs (hash (slurp file))))) + +(def ^:private synthetic-clojurescript-version + (delay (str "0.0." (reduce + (map file-hash (file-seq (main-src-directory))))))) + (defn ^String clojurescript-version "Returns clojurescript version as a printable string." [] @@ -33,7 +47,7 @@ (str "." q)) (when (:interim *clojurescript-version*) "-SNAPSHOT")) - "0.0.0000")) + @synthetic-clojurescript-version)) (defn ^String compiled-by-version [f] (with-open [reader (io/reader f)] From d193d6eee412c6ae8e1c78f7e41f29647b778cb0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 Feb 2018 09:36:42 -0600 Subject: [PATCH 2863/4033] initialize JavaFX w/o JFXPanel --- src/main/cljs/cljs/bootstrap_nashorn.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/bootstrap_nashorn.js b/src/main/cljs/cljs/bootstrap_nashorn.js index 6c16aedb5..e1fe3ab7d 100644 --- a/src/main/cljs/cljs/bootstrap_nashorn.js +++ b/src/main/cljs/cljs/bootstrap_nashorn.js @@ -16,10 +16,10 @@ goog.global.isProvided_ = function(name) { return false; }; // https://blogs.oracle.com/nashorn/setinterval-and-settimeout-javascript-functions var __Platform = Java.type("javafx.application.Platform"); -var __JFXPanel = Java.type("javafx.embed.swing.JFXPanel"); +var __PImpl = Java.type("com.sun.javafx.application.PlatformImpl"); var __Timer = Java.type("java.util.Timer"); -new __JFXPanel(); // need to invoke to init JFX so Platform.runLater works +__PImpl.startup(function(){}); // init JavaFX var nashorn_tear_down = function() { __Platform.exit(); From d7130ffc64e638c5392a20ff8cc507d7500dd107 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 15 Feb 2018 11:03:44 -0500 Subject: [PATCH 2864/4033] CLJS-2524: Nashorn REPL fails if ssh'd into macOS --- src/main/cljs/cljs/bootstrap_nashorn.js | 38 ++++++++++--------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/main/cljs/cljs/bootstrap_nashorn.js b/src/main/cljs/cljs/bootstrap_nashorn.js index e1fe3ab7d..b6f77ea2b 100644 --- a/src/main/cljs/cljs/bootstrap_nashorn.js +++ b/src/main/cljs/cljs/bootstrap_nashorn.js @@ -13,16 +13,11 @@ goog.global.CLOSURE_IMPORT_SCRIPT = function(path) { goog.global.isProvided_ = function(name) { return false; }; -// https://blogs.oracle.com/nashorn/setinterval-and-settimeout-javascript-functions - -var __Platform = Java.type("javafx.application.Platform"); -var __PImpl = Java.type("com.sun.javafx.application.PlatformImpl"); -var __Timer = Java.type("java.util.Timer"); - -__PImpl.startup(function(){}); // init JavaFX +var __executorService = Java.type("java.util.concurrent.Executors").newScheduledThreadPool(1); +var __millis = Java.type("java.util.concurrent.TimeUnit").valueOf("MILLISECONDS"); var nashorn_tear_down = function() { - __Platform.exit(); + __executorService.shutdownNow(); } function setTimerRequest(handler, delay, interval, args) { @@ -30,18 +25,15 @@ function setTimerRequest(handler, delay, interval, args) { delay = delay || 0; interval = interval || 0; var applyHandler = function() { handler.apply(this, args); } - var runLater = function() { __Platform.runLater(applyHandler); } - var timer = new __Timer("setTimerRequest", true); if (interval > 0) { - timer.schedule(runLater, delay, interval); + return __executorService.scheduleWithFixedDelay(applyHandler, delay, interval, __millis); } else { - timer.schedule(runLater, delay); - } - return timer; + return __executorService["schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit)"](applyHandler, delay, __millis); + }; } -function clearTimerRequest(timer) { - timer.cancel(); +function clearTimerRequest(future) { + future.cancel(false); } function setInterval() { @@ -51,8 +43,8 @@ function setInterval() { return setTimerRequest(handler, ms, ms, args); } -function clearInterval(timer) { - clearTimerRequest(timer); +function clearInterval(future) { + clearTimerRequest(future); } function setTimeout() { @@ -63,8 +55,8 @@ function setTimeout() { return setTimerRequest(handler, ms, 0, args); } -function clearTimeout(timer) { - clearTimerRequest(timer); +function clearTimeout(future) { + clearTimerRequest(future); } function setImmediate() { @@ -74,6 +66,6 @@ function setImmediate() { return setTimerRequest(handler, 0, 0, args); } -function clearImmediate(timer) { - clearTimerRequest(timer); -} \ No newline at end of file +function clearImmediate(future) { + clearTimerRequest(future); +} From a4713db2ca94b53cbad600c4e2675c2ccd63f676 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 Feb 2018 11:51:31 -0600 Subject: [PATCH 2865/4033] use shutdown --- src/main/cljs/cljs/bootstrap_nashorn.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/bootstrap_nashorn.js b/src/main/cljs/cljs/bootstrap_nashorn.js index b6f77ea2b..ccf78f362 100644 --- a/src/main/cljs/cljs/bootstrap_nashorn.js +++ b/src/main/cljs/cljs/bootstrap_nashorn.js @@ -17,7 +17,7 @@ var __executorService = Java.type("java.util.concurrent.Executors").newScheduled var __millis = Java.type("java.util.concurrent.TimeUnit").valueOf("MILLISECONDS"); var nashorn_tear_down = function() { - __executorService.shutdownNow(); + __executorService.shutdown(); } function setTimerRequest(handler, delay, interval, args) { From 1d37817e6cd5d7f7634ea08cd54c43d70dab7de5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 Feb 2018 12:03:59 -0600 Subject: [PATCH 2866/4033] fix -d group --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 510345e2f..69ab48c51 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -314,7 +314,7 @@ present" ["-v" "--verbose"] {:group ::main :fn verbose-opt :arg "bool" :doc "If true, will enable ClojureScript verbose logging"} - ["-d" "--output-dir"] {:group ::main :fn output-dir-opt + ["-d" "--output-dir"] {:group ::main&compile :fn output-dir-opt :arg "path" :doc (str "Set the output directory to use. If " "supplied, cljsc_opts.edn in that directory " From 3dce65e89e859d8fdc9a838935275c6e31647f17 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 Feb 2018 14:30:39 -0600 Subject: [PATCH 2867/4033] minor cleanup --- src/main/cljs/cljs/main.clj | 4 +-- src/main/clojure/cljs/cli.clj | 56 +++++++++++++++++------------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index be54b6155..dfeab46a6 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -11,7 +11,7 @@ [cljs.cli :as cli]) (:gen-class)) -(defn get-js-opt [args] +(defn- get-js-opt [args] (if (= 2 (count args)) (let [repl-ns (symbol (str "cljs.repl." @@ -31,7 +31,7 @@ {:repl-ns repl-ns}))))) nashorn/repl-env)) -(defn normalize* [args] +(defn- normalize* [args] (if (not (cli/dispatch? :main (first args))) (let [pred (complement #{"-re" "--repl-env"}) [pre post] ((juxt #(take-while pred %) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 69ab48c51..2ea4386ab 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -26,14 +26,14 @@ ;; ----------------------------------------------------------------------------- ;; Registry -(defonce _cli_registry +(defonce ^{:private true} _cli_registry (atom {:main-dispatch nil :init-dispatch nil})) ;; ----------------------------------------------------------------------------- ;; Help String formatting -(def help-template +(def ^{:private true} help-template "Usage: java -cp cljs.jar cljs.main [init-opt*] [main-opt] [arg*] With no options or args, runs an interactive Read-Eval-Print Loop @@ -53,7 +53,7 @@ any main option. Paths may be absolute or relative in the filesystem or relative to classpath. Classpath-relative paths have prefix of @ or @/") -(defn auto-fill +(defn- auto-fill ([ws] (auto-fill ws 50)) ([^String ws max-len] @@ -69,7 +69,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") (recur e (.next b) line-len (str line w) ret))) (conj ret (str line (.substring ws s (.length ws))))))))) -(defn opt->str [cs {:keys [arg doc]}] +(defn- opt->str [cs {:keys [arg doc]}] (letfn [(desc-string [filled] (string/join "\n" (map #(apply str (concat (repeat 6 " ") [%])) @@ -89,7 +89,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") fstr "\n" (desc-string fstr) "\n"))))) -(defn group->str [options group] +(defn- group->str [options group] (let [{:keys [desc pseudos]} (get-in options [:groups group])] (apply str desc ":\n" @@ -99,13 +99,13 @@ classpath. Classpath-relative paths have prefix of @ or @/") (sort-by ffirst) (map (fn [[k v]] (opt->str k v))))))) -(defn primary-groups-str [options] +(defn- primary-groups-str [options] (str (group->str options ::main&compile) "\n" (group->str options ::main) "\n" (group->str options ::compile) "\n")) -(defn all-groups-str [{:keys [groups] :as options}] +(defn- all-groups-str [{:keys [groups] :as options}] (let [custom-groups (disj (set (keys groups)) ::main&compile ::main ::compile)] @@ -116,7 +116,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") (str (group->str options group) "\n")) custom-groups)))) -(defn main-str [options] +(defn- main-str [options] (let [pseudos {["path"] {:doc "Run a script from a file or resource"} ["-"] {:doc "Run a script from standard input"}}] (apply str @@ -127,7 +127,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") (remove (fn [[k v]] (nil? (ffirst k)))) (map (fn [[k v]] (opt->str k v))))))) -(defn options-str [options] +(defn- options-str [options] (str (all-groups-str options) (main-str options))) @@ -139,39 +139,39 @@ classpath. Classpath-relative paths have prefix of @ or @/") ;; ----------------------------------------------------------------------------- ;; Main -(defn output-dir-opt +(defn- output-dir-opt [cfg output-dir] (assoc-in cfg [:options :output-dir] output-dir)) -(defn verbose-opt +(defn- verbose-opt [cfg value] (assoc-in cfg [:options :verbose] (= value "true"))) -(defn watch-opt +(defn- watch-opt [cfg path] (assoc-in cfg [:options :watch] path)) -(defn optimize-opt +(defn- optimize-opt [cfg level] (assoc-in cfg [:options :optimizations] (keyword level))) -(defn output-to-opt +(defn- output-to-opt [cfg path] (assoc-in cfg [:options :output-to] path)) -(defn target-opt +(defn- target-opt [cfg target] (let [target (if (= "node" target) "nodejs" target)] (assoc-in cfg [:options :target] (keyword target)))) -(defn init-opt +(defn- init-opt [cfg file] (update-in cfg [:inits] (fnil conj []) {:type :init-script :script file})) -(defn eval-opt +(defn- eval-opt [cfg form-str] (update-in cfg [:inits] (fnil conj []) @@ -185,7 +185,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") (let [k' (keyword (str (name k) "-dispatch"))] (or (get-in @_cli_registry [k' opt]) default)))) -(defn- initialize +(defn initialize "Common initialize routine for repl, script, and null opts" [inits] (reduce @@ -193,7 +193,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") ((get-dispatch :init opt) ret arg)) {} inits)) -(defn repl-opt +(defn- repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] {:keys [options inits] :as cfg}] @@ -209,7 +209,7 @@ present" [`(set! *command-line-args* (list ~@args))])}] inits))))) -(defn main-opt* +(defn default-main [repl-env {:keys [main script args options inits] :as cfg}] (env/ensure (let [renv (repl-env) @@ -254,29 +254,29 @@ present" `(~(symbol (name main) "-main") ~@args))) (repl/-tear-down renv))))))) -(defn main-opt +(defn- main-opt "Call the -main function from a namespace with string arguments from the command line." [repl-env [_ ns & args] cfg] - ((::main (repl/-repl-options (repl-env)) main-opt*) + ((::main (repl/-repl-options (repl-env)) default-main) repl-env (merge cfg {:main ns :args args}))) (defn- null-opt "No repl or script opt present, just bind args and run inits" [repl-env args cfg] - ((::main (repl/-repl-options (repl-env)) main-opt*) + ((::main (repl/-repl-options (repl-env)) default-main) repl-env (merge cfg {:args args}))) (defn- help-opt [_ _ _] (println (help-str))) -(defn script-opt +(defn- script-opt [repl-env [path & args] cfg] - ((::main (repl/-repl-options (repl-env)) main-opt*) + ((::main (repl/-repl-options (repl-env)) default-main) repl-env (merge cfg {:script path :args args}))) -(defn compile-opt* +(defn default-compile [repl-env {:keys [ns args options] :as cfg}] (let [env-opts (repl/-repl-options (repl-env)) main-ns (symbol ns) @@ -290,9 +290,9 @@ present" (build/watch path opts) (build/build source opts)))) -(defn compile-opt +(defn- compile-opt [repl-env [_ ns & args] cfg] - ((::compile (repl/-repl-options (repl-env)) compile-opt*) + ((::compile (repl/-repl-options (repl-env)) default-compile) repl-env (merge cfg {:args args :ns ns}))) (def cli-options From 38d81c6dc65bd1c2590a7fbba3b50babddd13f57 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 Feb 2018 17:41:49 -0600 Subject: [PATCH 2868/4033] cleanup --- src/main/clojure/cljs/cli.clj | 30 +++++++++++++++--------------- src/main/clojure/cljs/repl.cljc | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 2ea4386ab..89f253b6f 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -9,14 +9,14 @@ (ns cljs.cli (:require [clojure.java.io :as io] [clojure.string :as string] + [clojure.edn :as edn] [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] - [cljs.compiler :as comp] - [cljs.repl :as repl] + [cljs.compiler.api :as comp] [cljs.build.api :as build] - [clojure.edn :as edn]) + [cljs.repl :as repl]) (:import [java.io StringReader] [java.text BreakIterator] [java.util Locale])) @@ -199,7 +199,7 @@ present" [repl-env [_ & args] {:keys [options inits] :as cfg}] (let [renv (repl-env) opts (build/add-implicit-options - (merge (repl/-repl-options renv) options))] + (merge (repl/repl-options renv) options))] (repl/repl* renv (assoc opts :inits @@ -217,7 +217,7 @@ present" (io/file od "cljsc_opts.edn")) opts (as-> (build/add-implicit-options - (merge (repl/-repl-options renv) options)) opts + (merge (repl/repl-options renv) options)) opts (let [copts (when (and coptsf (.exists coptsf)) (-> (edn/read-string (slurp coptsf)) ;; need to remove the entry point bits, @@ -232,16 +232,16 @@ present" (util/debug-prn "Compiler options:" repl/*repl-opts*)) (comp/with-core-cljs repl/*repl-opts* (fn [] - (repl/-setup renv repl/*repl-opts*) + (repl/setup renv repl/*repl-opts*) ;; REPLs don't normally load cljs_deps.js (when (and coptsf (.exists coptsf)) (let [depsf (io/file (:output-dir options) "cljs_deps.js")] (when (.exists depsf) - (repl/-evaluate renv "cljs_deps.js" 1 (slurp depsf))))) - (repl/evaluate-form renv (ana/empty-env) "" + (repl/evaluate renv "cljs_deps.js" 1 (slurp depsf))))) + (repl/evaluate-form renv (ana-api/empty-env) "" (when-not (empty? args) `(set! *command-line-args* (list ~@args)))) - (repl/evaluate-form renv (ana/empty-env) "" + (repl/evaluate-form renv (ana-api/empty-env) "" `(~'ns ~'cljs.user)) (repl/run-inits renv inits) (when script @@ -250,21 +250,21 @@ present" (repl/load-file renv script))) (when main (repl/load-file renv (build/ns->source main)) - (repl/evaluate-form renv (ana/empty-env) "" + (repl/evaluate-form renv (ana-api/empty-env) "" `(~(symbol (name main) "-main") ~@args))) - (repl/-tear-down renv))))))) + (repl/tear-down renv))))))) (defn- main-opt "Call the -main function from a namespace with string arguments from the command line." [repl-env [_ ns & args] cfg] - ((::main (repl/-repl-options (repl-env)) default-main) + ((::main (repl/repl-options (repl-env)) default-main) repl-env (merge cfg {:main ns :args args}))) (defn- null-opt "No repl or script opt present, just bind args and run inits" [repl-env args cfg] - ((::main (repl/-repl-options (repl-env)) default-main) + ((::main (repl/repl-options (repl-env)) default-main) repl-env (merge cfg {:args args}))) (defn- help-opt @@ -273,12 +273,12 @@ present" (defn- script-opt [repl-env [path & args] cfg] - ((::main (repl/-repl-options (repl-env)) default-main) + ((::main (repl/repl-options (repl-env)) default-main) repl-env (merge cfg {:script path :args args}))) (defn default-compile [repl-env {:keys [ns args options] :as cfg}] - (let [env-opts (repl/-repl-options (repl-env)) + (let [env-opts (repl/repl-options (repl-env)) main-ns (symbol ns) opts (merge (select-keys env-opts [:target :browser-repl]) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 03fdd0ea3..e68d62507 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -7,7 +7,7 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.repl - (:refer-clojure :exclude [load-file]) + (:refer-clojure :exclude [load load-file]) (:require [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] @@ -116,12 +116,27 @@ (defprotocol IReplEnvOptions (-repl-options [repl-env] "Return default REPL options for a REPL Env")) +(defn repl-options [repl-env] + (-repl-options repl-env)) + (defprotocol IJavaScriptEnv (-setup [repl-env opts] "initialize the environment") (-evaluate [repl-env filename line js] "evaluate a javascript string") (-load [repl-env provides url] "load code at url into the environment") (-tear-down [repl-env] "dispose of the environment")) +(defn setup [repl-env opts] + (-setup repl-env opts)) + +(defn evaluate [repl-env filename line js] + (-evaluate repl-env filename line js)) + +(defn load [repl-env provides url] + (-load repl-env provides url)) + +(defn tear-down [repl-env] + (-tear-down repl-env)) + (extend-type Object IReplEnvOptions From 6f05654dccd3460e054790122f1d321cec172ca8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 Feb 2018 18:04:08 -0600 Subject: [PATCH 2869/4033] allow -r to come after -c --- src/main/clojure/cljs/cli.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 89f253b6f..743c8f28f 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -288,7 +288,10 @@ present" (:uri (build/ns->location main-ns)))] (if-let [path (:watch opts)] (build/watch path opts) - (build/build source opts)))) + (do + (build/build source opts) + (when (#{"-r" "--repl"} (first args)) + (repl-opt repl-env args cfg)))))) (defn- compile-opt [repl-env [_ ns & args] cfg] @@ -346,7 +349,8 @@ present" :doc "Call the -main function from a namespace with args"} ["-c" "--compile"] {:fn compile-opt :arg "ns" - :doc "Compile a namespace"} + :doc (str "Compile a namespace. If -r / --repl present after " + "namespace will launch a REPL after the compile completes")} [nil] {:fn null-opt} ["-h" "--help" "-?"] {:fn help-opt :doc "Print this help message and exit"}}}) From 4df0542339e533b2d1e1dd7f6ffa8824eab64893 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Feb 2018 14:21:25 -0600 Subject: [PATCH 2870/4033] default :output-to --- src/main/clojure/cljs/cli.clj | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 743c8f28f..fa8b4b741 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -280,10 +280,15 @@ present" [repl-env {:keys [ns args options] :as cfg}] (let [env-opts (repl/repl-options (repl-env)) main-ns (symbol ns) - opts (merge - (select-keys env-opts [:target :browser-repl]) - options - {:main main-ns}) + opts (as-> + (merge + (select-keys env-opts [:target :browser-repl]) + options + {:main main-ns}) opts + (cond-> opts + (not (:output-to opts)) + (assoc :output-to + (.getPath (io/file (:output-dir opts "out") "main.js"))))) source (when (= :none (:optimizations opts :none)) (:uri (build/ns->location main-ns)))] (if-let [path (:watch opts)] From dcf11e9f547c56495bd90a1d77f03894c2388ac9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Feb 2018 14:36:13 -0600 Subject: [PATCH 2871/4033] make command extension functional --- src/main/cljs/cljs/main.clj | 4 +- src/main/clojure/cljs/cli.clj | 224 +++++++++++++++++----------------- 2 files changed, 114 insertions(+), 114 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index dfeab46a6..a9139ce44 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -32,7 +32,7 @@ nashorn/repl-env)) (defn- normalize* [args] - (if (not (cli/dispatch? :main (first args))) + (if (not (cli/dispatch? cli/default-commands :main (first args))) (let [pred (complement #{"-re" "--repl-env"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) @@ -50,7 +50,7 @@ (concat js-opt args))) (defn -main [& args] - (let [args (normalize (cli/normalize args)) + (let [args (normalize (cli/normalize cli/default-commands args)) pred (complement #{"-re" "--repl-env"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index fa8b4b741..eb8eab652 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -23,13 +23,6 @@ (declare main) -;; ----------------------------------------------------------------------------- -;; Registry - -(defonce ^{:private true} _cli_registry - (atom {:main-dispatch nil - :init-dispatch nil})) - ;; ----------------------------------------------------------------------------- ;; Help String formatting @@ -132,9 +125,11 @@ classpath. Classpath-relative paths have prefix of @ or @/") (all-groups-str options) (main-str options))) -(defn help-str [] +(declare merged-commands) + +(defn help-str [repl-env] (format help-template - (options-str @_cli_registry))) + (options-str (merged-commands repl-env)))) ;; ----------------------------------------------------------------------------- ;; Main @@ -179,18 +174,18 @@ classpath. Classpath-relative paths have prefix of @ or @/") :forms (ana-api/forms-seq (StringReader. form-str))})) (defn get-dispatch - ([k opt] - (get-dispatch k opt nil)) - ([k opt default] + ([commands k opt] + (get-dispatch commands k opt nil)) + ([commands k opt default] (let [k' (keyword (str (name k) "-dispatch"))] - (or (get-in @_cli_registry [k' opt]) default)))) + (or (get-in commands [k' opt]) default)))) (defn initialize "Common initialize routine for repl, script, and null opts" - [inits] + [inits commands] (reduce (fn [ret [opt arg]] - ((get-dispatch :init opt) ret arg)) + ((get-dispatch commands :init opt) ret arg)) {} inits)) (defn- repl-opt @@ -268,8 +263,8 @@ present" repl-env (merge cfg {:args args}))) (defn- help-opt - [_ _ _] - (println (help-str))) + [repl-env _ _] + (println (help-str repl-env))) (defn- script-opt [repl-env [path & args] cfg] @@ -303,94 +298,94 @@ present" ((::compile (repl/-repl-options (repl-env)) default-compile) repl-env (merge cfg {:args args :ns ns}))) -(def cli-options - {:groups {::main&compile {:desc "init option" - :pseudos - {["-re" "--repl-env"] - {:doc (str "The REPL environment to use. Built-in " - "supported values: nashorn, node, browser, " - "rhino. Defaults to nashorn")}}} - ::main {:desc "init options only for --main and --repl"} - ::compile {:desc "init options only for --compile"}} - :init - {["-i" "--init"] {:group ::main :fn init-opt - :arg "path" - :doc "Load a file or resource"} - ["-e" "--eval"] {:group ::main :fn eval-opt - :arg "string" - :doc "Evaluate expressions in string; print non-nil values"} - ["-v" "--verbose"] {:group ::main :fn verbose-opt - :arg "bool" - :doc "If true, will enable ClojureScript verbose logging"} - ["-d" "--output-dir"] {:group ::main&compile :fn output-dir-opt - :arg "path" - :doc (str "Set the output directory to use. If " - "supplied, cljsc_opts.edn in that directory " - "will be used to set ClojureScript compiler " - "options") } - ["-w" "--watch"] {:group ::compile :fn watch-opt - :arg "path" - :doc "Continuously build, only effective with -c main option"} - ["-o" "--output-to"] {:group ::compile :fn output-to-opt - :arg "file" - :doc "Set the output compiled file"} - ["-O" "--optimizations"] {:group ::compile :fn optimize-opt - :arg "level" - :doc - (str "Set optimization level, only effective with " - "-c main option. Valid values are: none, " - "whitespace, simple, advanced")} - - ["-t" "--target"] {:group ::main&compile :fn target-opt - :arg "name" - :doc - (str "The JavaScript target. Supported values: " - "nodejs, nashorn, webworker") }} - :main - {["-r" "--repl"] {:fn repl-opt - :doc "Run a repl"} - ["-m" "--main"] {:fn main-opt - :arg "ns" - :doc "Call the -main function from a namespace with args"} - ["-c" "--compile"] {:fn compile-opt - :arg "ns" - :doc (str "Compile a namespace. If -r / --repl present after " - "namespace will launch a REPL after the compile completes")} - [nil] {:fn null-opt} - ["-h" "--help" "-?"] {:fn help-opt - :doc "Print this help message and exit"}}}) - -(defn get-options [k] +(defn get-options [commands k] (if (= :all k) - (into (get-options :main) (get-options :init)) - (-> (get @_cli_registry (keyword (str (name k) "-dispatch"))) + (into (get-options commands :main) (get-options commands :init)) + (-> (get commands (keyword (str (name k) "-dispatch"))) keys set))) -(defn dispatch? [k opt] - (contains? (get-options k) opt)) - -(defn register-options! [{:keys [groups main init]}] - (letfn [(merge-dispatch [st k options] - (update-in st [k] - (fn [m] - (reduce - (fn [ret [cs csm]] - (merge ret - (zipmap cs (repeat (:fn csm))))) - m options))))] - (swap! _cli_registry - (fn [st] - (-> st - (update-in [:groups] merge groups) - (update-in [:main] merge main) - (update-in [:init] merge init) - (merge-dispatch :init-dispatch init) - (merge-dispatch :main-dispatch main)))))) - -(register-options! cli-options) - -(defn normalize [args] - (if (not (contains? (get-options :main) (first args))) +(defn dispatch? [commands k opt] + (contains? (get-options commands k) opt)) + +(defn add-commands + ([commands] + (add-commands {:main-dispatch nil :init-dispatch nil} commands)) + ([commands {:keys [groups main init]}] + (letfn [(merge-dispatch [st k options] + (update-in st [k] + (fn [m] + (reduce + (fn [ret [cs csm]] + (merge ret + (zipmap cs (repeat (:fn csm))))) + m options))))] + (-> commands + (update-in [:groups] merge groups) + (update-in [:main] merge main) + (update-in [:init] merge init) + (merge-dispatch :init-dispatch init) + (merge-dispatch :main-dispatch main))))) + +(def default-commands + (add-commands + {:groups {::main&compile {:desc "init option" + :pseudos + {["-re" "--repl-env"] + {:doc (str "The REPL environment to use. Built-in " + "supported values: nashorn, node, browser, " + "rhino. Defaults to nashorn")}}} + ::main {:desc "init options only for --main and --repl"} + ::compile {:desc "init options only for --compile"}} + :init + {["-i" "--init"] {:group ::main :fn init-opt + :arg "path" + :doc "Load a file or resource"} + ["-e" "--eval"] {:group ::main :fn eval-opt + :arg "string" + :doc "Evaluate expressions in string; print non-nil values"} + ["-v" "--verbose"] {:group ::main :fn verbose-opt + :arg "bool" + :doc "If true, will enable ClojureScript verbose logging"} + ["-d" "--output-dir"] {:group ::main&compile :fn output-dir-opt + :arg "path" + :doc (str "Set the output directory to use. If " + "supplied, cljsc_opts.edn in that directory " + "will be used to set ClojureScript compiler " + "options") } + ["-w" "--watch"] {:group ::compile :fn watch-opt + :arg "path" + :doc "Continuously build, only effective with -c main option"} + ["-o" "--output-to"] {:group ::compile :fn output-to-opt + :arg "file" + :doc "Set the output compiled file"} + ["-O" "--optimizations"] {:group ::compile :fn optimize-opt + :arg "level" + :doc + (str "Set optimization level, only effective with " + "-c main option. Valid values are: none, " + "whitespace, simple, advanced")} + + ["-t" "--target"] {:group ::main&compile :fn target-opt + :arg "name" + :doc + (str "The JavaScript target. Supported values: " + "nodejs, nashorn, webworker") }} + :main + {["-r" "--repl"] {:fn repl-opt + :doc "Run a repl"} + ["-m" "--main"] {:fn main-opt + :arg "ns" + :doc "Call the -main function from a namespace with args"} + ["-c" "--compile"] {:fn compile-opt + :arg "ns" + :doc (str "Compile a namespace. If -r / --repl present after " + "namespace will launch a REPL after the compile completes")} + [nil] {:fn null-opt} + ["-h" "--help" "-?"] {:fn help-opt + :doc "Print this help message and exit"}}})) + +(defn normalize [commands args] + (if (not (contains? (get-options commands :main) (first args))) (let [pred (complement #{"-v" "--verbose"}) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) @@ -398,15 +393,19 @@ present" (cond (= pre args) pre - (contains? (get-options :all) (fnext post)) + (contains? (get-options commands :all) (fnext post)) (concat pre [(first post) "true"] - (normalize (next post))) + (normalize commands (next post))) :else (concat pre [(first post) (fnext post)] - (normalize (nnext post))))) + (normalize commands (nnext post))))) args)) +(defn merged-commands [repl-env] + (add-commands default-commands + (::commands (repl/repl-options (repl-env))))) + (defn main "A generic runner for ClojureScript. repl-env must satisfy cljs.repl/IReplEnvOptions and cljs.repl/IJavaScriptEnv protocols. args is a @@ -416,12 +415,13 @@ present" (System/setProperty "apple.awt.UIElement" "true") (java.awt.Toolkit/getDefaultToolkit) (try - (if args - (loop [[opt arg & more :as args] (normalize args) inits []] - (if (dispatch? :init opt) - (recur more (conj inits [opt arg])) - ((get-dispatch :main opt script-opt) - repl-env args (initialize inits)))) - (repl-opt repl-env nil nil)) + (let [commands (merged-commands repl-env)] + (if args + (loop [[opt arg & more :as args] (normalize commands args) inits []] + (if (dispatch? commands :init opt) + (recur more (conj inits [opt arg])) + ((get-dispatch commands :main opt script-opt) + repl-env args (initialize inits commands)))) + (repl-opt repl-env nil nil))) (finally (flush)))) From 313bdb6d717b766960c4a0ed35beadad6edf15f6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Feb 2018 15:24:54 -0600 Subject: [PATCH 2872/4033] add clj.main and clj.cli to list of AOTed nses --- pom.template.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.template.xml b/pom.template.xml index f39b04811..adc011f9a 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -310,6 +310,8 @@ cljs.repl.reflect cljs.repl.rhino cljs.repl.server + cljs.main + cljs.cli
From addc856ecff320958920ed1694d4d99da98de3b6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Feb 2018 20:46:56 -0500 Subject: [PATCH 2873/4033] make sure some transitive compiled nses aren't making it into the AOT jar --- src/assembly/aot.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/assembly/aot.xml b/src/assembly/aot.xml index 7ca20f937..b9e0c5cb2 100644 --- a/src/assembly/aot.xml +++ b/src/assembly/aot.xml @@ -5,14 +5,14 @@ false - - src/resources - / - true - target/classes / + + clojure/data/** + clojure/tools/** + cognitect/** + \ No newline at end of file From 3d5c545ebf9a719035bfa4032c428ec2a95c7adf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Feb 2018 21:09:22 -0500 Subject: [PATCH 2874/4033] use AOT deps --- deps.edn | 4 ++-- pom.template.xml | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/deps.edn b/deps.edn index 852b467b0..277f88416 100644 --- a/deps.edn +++ b/deps.edn @@ -1,11 +1,11 @@ {:paths ["src/main/clojure" "src/main/cljs"] :deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.clojure/tools.reader {:mvn/version "1.1.3.1"} + org.clojure/tools.reader {:mvn/version "1.2.1" :classifier "aot"} org.clojure/test.check {:mvn/version "0.10.0-alpha2"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} - org.clojure/data.json {:mvn/version "0.2.6"} + org.clojure/data.json {:mvn/version "0.2.6" :classifier "aot"} com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180204"} org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"} org.mozilla/rhino {:mvn/version "1.7R5"}} diff --git a/pom.template.xml b/pom.template.xml index adc011f9a..6fc97f7d9 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -41,6 +41,7 @@ org.clojure data.json 0.2.6 + aot
org.mozilla @@ -50,7 +51,8 @@ org.clojure tools.reader - 1.1.3.1 + 1.2.1 + aot org.clojure From 5740df90357c5232f3421283d21a8ecf17efef95 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Feb 2018 21:16:10 -0500 Subject: [PATCH 2875/4033] no browser REPL under advanced --- src/main/clojure/cljs/cli.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index eb8eab652..f77c2bb75 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -283,7 +283,9 @@ present" (cond-> opts (not (:output-to opts)) (assoc :output-to - (.getPath (io/file (:output-dir opts "out") "main.js"))))) + (.getPath (io/file (:output-dir opts "out") "main.js"))) + (= :advanced (:optimizations opts)) + (dissoc :browser-repl))) source (when (= :none (:optimizations opts :none)) (:uri (build/ns->location main-ns)))] (if-let [path (:watch opts)] @@ -369,7 +371,7 @@ present" :arg "name" :doc (str "The JavaScript target. Supported values: " - "nodejs, nashorn, webworker") }} + "nodejs, nashorn, webworker") }} :main {["-r" "--repl"] {:fn repl-opt :doc "Run a repl"} From a76db38b6c0c7e33547942b1a6586a1dc9b50523 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Feb 2018 22:10:16 -0500 Subject: [PATCH 2876/4033] don't emit main stuff if target is :none --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1953c1f54..2a3f8521c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1917,7 +1917,7 @@ :modules modules'}))) modules))) - main + (and main (not= :none (:target opts))) (do (output-deps) (output-main-file opts)) From 87e61bd45cb9f4897f0bb7caa75d550461ebd72b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Feb 2018 22:12:37 -0500 Subject: [PATCH 2877/4033] bump tools.reader, 1.2.2 --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index 277f88416..ab6377a35 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,7 @@ {:paths ["src/main/clojure" "src/main/cljs"] :deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.clojure/tools.reader {:mvn/version "1.2.1" :classifier "aot"} + org.clojure/tools.reader {:mvn/version "1.2.2" :classifier "aot"} org.clojure/test.check {:mvn/version "0.10.0-alpha2"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} diff --git a/pom.template.xml b/pom.template.xml index 6fc97f7d9..7aad0c45f 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -51,7 +51,7 @@ org.clojure tools.reader - 1.2.1 + 1.2.2 aot diff --git a/project.clj b/project.clj index 87cf47cd9..c2918e8bf 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.1.3.1"] + [org.clojure/tools.reader "1.2.2"] [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] diff --git a/script/bootstrap b/script/bootstrap index 4c58e0f14..67a5d9997 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.1.3.1" +TREADER_RELEASE="1.2.2" TEST_CHECK_RELEASE="0.10.0-alpha2" # check dependencies From d302cc33253356da00f713b1cba58fe271befc35 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 15 Feb 2018 14:06:18 -0500 Subject: [PATCH 2878/4033] CLJS-2525: Nashorn process fails to terminate --- src/main/cljs/cljs/bootstrap_nashorn.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/bootstrap_nashorn.js b/src/main/cljs/cljs/bootstrap_nashorn.js index ccf78f362..19d087d50 100644 --- a/src/main/cljs/cljs/bootstrap_nashorn.js +++ b/src/main/cljs/cljs/bootstrap_nashorn.js @@ -13,7 +13,8 @@ goog.global.CLOSURE_IMPORT_SCRIPT = function(path) { goog.global.isProvided_ = function(name) { return false; }; -var __executorService = Java.type("java.util.concurrent.Executors").newScheduledThreadPool(1); +var __executorService = Java.type("java.util.concurrent.Executors").newScheduledThreadPool(0); +__executorService.setMaximumPoolSize(1); var __millis = Java.type("java.util.concurrent.TimeUnit").valueOf("MILLISECONDS"); var nashorn_tear_down = function() { From 46167fe9c216e054386c825d2c8a7742f37f4331 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Feb 2018 09:03:44 -0500 Subject: [PATCH 2879/4033] CLJS-2523: RecordIter should return MapEntry --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 1efdf5323..3db2bee7d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6432,7 +6432,7 @@ reduces them without incurring seq initialization" (if (< i base-count) (let [k (nth fields i)] (set! i (inc i)) - [k (-lookup record k)]) + (MapEntry. k (-lookup record k) nil)) (.next ext-map-iter))) (remove [_] (js/Error. "Unsupported operation"))) From 1b8c4e7ff5127ff47f22376a5f9a7fb634da1ed7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Feb 2018 09:44:55 -0500 Subject: [PATCH 2880/4033] CLJS-2534: Eliminate icon suppression code --- src/main/clojure/cljs/cli.clj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index f77c2bb75..b72142153 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -413,9 +413,6 @@ present" cljs.repl/IReplEnvOptions and cljs.repl/IJavaScriptEnv protocols. args is a sequence of command line flags." [repl-env & args] - ;; On OS X suppress the Dock icon - (System/setProperty "apple.awt.UIElement" "true") - (java.awt.Toolkit/getDefaultToolkit) (try (let [commands (merged-commands repl-env)] (if args From 4f3c2c8596e929db482e3e744b125920ae4a131b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Feb 2018 11:51:42 -0500 Subject: [PATCH 2881/4033] drop AOT dep artifacts for now --- deps.edn | 4 ++-- pom.template.xml | 4 +--- script/bootstrap | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/deps.edn b/deps.edn index ab6377a35..acb970b21 100644 --- a/deps.edn +++ b/deps.edn @@ -1,11 +1,11 @@ {:paths ["src/main/clojure" "src/main/cljs"] :deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.clojure/tools.reader {:mvn/version "1.2.2" :classifier "aot"} + org.clojure/tools.reader {:mvn/version "1.2.2"} org.clojure/test.check {:mvn/version "0.10.0-alpha2"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} - org.clojure/data.json {:mvn/version "0.2.6" :classifier "aot"} + org.clojure/data.json {:mvn/version "0.2.6"} com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180204"} org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"} org.mozilla/rhino {:mvn/version "1.7R5"}} diff --git a/pom.template.xml b/pom.template.xml index 7aad0c45f..9846dde01 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.8.0 + 1.9.0 com.google.javascript @@ -41,7 +41,6 @@ org.clojure data.json 0.2.6 - aot org.mozilla @@ -52,7 +51,6 @@ org.clojure tools.reader 1.2.2 - aot org.clojure diff --git a/script/bootstrap b/script/bootstrap index 67a5d9997..42540c31a 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,7 +2,7 @@ set -e -CLOJURE_RELEASE="1.9.0-alpha17" +CLOJURE_RELEASE="1.9.0" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20180204" From 85d2fb6966411e1458f3ed9ab369fd73b775ad34 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Feb 2018 12:57:58 -0500 Subject: [PATCH 2882/4033] 1.10 --- script/build | 2 +- script/uberjar | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/build b/script/build index d579c2b0d..f08f9e4f3 100755 --- a/script/build +++ b/script/build @@ -24,7 +24,7 @@ CLJS_SCRIPT_MVN_OPTS=${CLJS_SCRIPT_MVN_OPTS:-""} # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="9" +MINOR="10" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so diff --git a/script/uberjar b/script/uberjar index 2e4401b20..3b3d1d1af 100755 --- a/script/uberjar +++ b/script/uberjar @@ -17,7 +17,7 @@ fi # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="9" +MINOR="10" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so From bba3a30bd9e6e79059d3bc81a74846ec47a522c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 18 Feb 2018 13:15:15 -0500 Subject: [PATCH 2883/4033] fix fragile version extraction --- script/build | 11 ++++++----- script/uberjar | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/script/build b/script/build index f08f9e4f3..28254938d 100755 --- a/script/build +++ b/script/build @@ -27,11 +27,12 @@ MAJOR="1" MINOR="10" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` -# Extract the version number from the string. Do this in two steps so -# it is a little easier to understand. -REVISION=${REVISION:5} # drop the first 5 characters -REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters -REVISION=${REVISION/\-/} +# Extract the version number from the string. +REVISION_REGEX="v[0-9]*\.[0-9]*-([0-9]*)-.*" +if [[ $REVISION =~ $REVISION_REGEX ]] +then + REVISION="${BASH_REMATCH[1]}" +fi TAG=r$MAJOR.$MINOR.$REVISION diff --git a/script/uberjar b/script/uberjar index 3b3d1d1af..0cf280911 100755 --- a/script/uberjar +++ b/script/uberjar @@ -20,11 +20,12 @@ MAJOR="1" MINOR="10" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` -# Extract the version number from the string. Do this in two steps so -# it is a little easier to understand. -REVISION=${REVISION:5} # drop the first 5 characters -REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters -REVISION=${REVISION/\-/} +# Extract the version number from the string. +REVISION_REGEX="v[0-9]*\.[0-9]*-([0-9]*)-.*" +if [[ $REVISION =~ $REVISION_REGEX ]] +then + REVISION="${BASH_REMATCH[1]}" +fi COMP_FILE=`mktemp /tmp/compiler.clj.XXXXXXXXXXX` sed -e 's/^.def ^:dynamic \*clojurescript-version\*.*$/(def ^:dynamic *clojurescript-version* {:major '"$MAJOR"', :minor '"$MINOR"', :qualifier '"$REVISION"'})/' src/main/clojure/cljs/util.cljc > $COMP_FILE From a32262577c697aa94d32054605fdbe909b890faa Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 18 Feb 2018 17:20:12 -0500 Subject: [PATCH 2884/4033] excluding transitive nses breaks the AOT jar --- src/assembly/aot.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/assembly/aot.xml b/src/assembly/aot.xml index b9e0c5cb2..ce9897faf 100644 --- a/src/assembly/aot.xml +++ b/src/assembly/aot.xml @@ -8,11 +8,14 @@ target/classes / + + \ No newline at end of file From 8670cc40cf09f90f1d36cb4bf0a2c68713fb1d68 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 18 Feb 2018 13:42:30 -0500 Subject: [PATCH 2885/4033] CLJS-2529: Externs are ignored under Java 9 --- src/main/clojure/cljs/js_deps.cljc | 37 ++++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index d6eff14af..73f2fe605 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -14,19 +14,27 @@ [java.net URL URLClassLoader] [java.util.zip ZipFile ZipEntry])) -; taken from pomegranate/dynapath -; https://github.com/tobias/dynapath/blob/master/src/dynapath/util.clj -(defn- all-classpath-urls - "Walks up the parentage chain for a ClassLoader, concatenating any URLs it retrieves. -If no ClassLoader is provided, RT/baseLoader is assumed." - ([] (all-classpath-urls (clojure.lang.RT/baseLoader))) - ([cl] - (->> (iterate #(.getParent ^ClassLoader %) cl) - (take-while identity) - reverse - (filter (partial instance? URLClassLoader)) - (mapcat #(.getURLs ^URLClassLoader %)) - distinct))) +(def ^:private java-8? (-> (System/getProperty "java.version") (string/starts-with? "1.8."))) + +(defn- classpath-files + "Returns a list of classpath files. Under Java 8, walks up the parentage + chain of RT/baseLoader, concatenating any URLs it retrieves. Under Java 9 and + later, builds file list from the java.class.path system property." + [] + (->> + (if java-8? + ; taken from pomegranate/dynapath + ; https://github.com/tobias/dynapath/blob/master/src/dynapath/util.clj + (->> (clojure.lang.RT/baseLoader) + (iterate #(.getParent ^ClassLoader %)) + (take-while identity) + reverse + (filter (partial instance? URLClassLoader)) + (mapcat #(.getURLs ^URLClassLoader %))) + (-> (System/getProperty "java.class.path") + (string/split (re-pattern File/pathSeparator)))) + distinct + (map io/file))) (defn ^ZipFile zip-file [jar-path] (try @@ -70,8 +78,7 @@ If no ClassLoader is provided, RT/baseLoader is assumed." (defn find-js-classpath "Returns a seq of URLs of all JavaScript files on the classpath." [path] - (->> (all-classpath-urls) - (map io/file) + (->> (classpath-files) (reduce (fn [files jar-or-dir] (let [name (.toLowerCase (.getName ^File jar-or-dir)) From 06e591d02dcce8abc253826f9e4de9dbb4b01e06 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 18 Feb 2018 09:41:18 -0500 Subject: [PATCH 2886/4033] CLJS-2535: cljs.main :analyze-path support --- src/main/clojure/cljs/cli.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index b72142153..ee88c4203 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -275,8 +275,12 @@ present" [repl-env {:keys [ns args options] :as cfg}] (let [env-opts (repl/repl-options (repl-env)) main-ns (symbol ns) + coptsf (when-let [od (:output-dir options)] + (io/file od "cljsc_opts.edn")) opts (as-> (merge + (when (and coptsf (.exists coptsf)) + (edn/read-string (slurp coptsf))) (select-keys env-opts [:target :browser-repl]) options {:main main-ns}) opts @@ -286,6 +290,7 @@ present" (.getPath (io/file (:output-dir opts "out") "main.js"))) (= :advanced (:optimizations opts)) (dissoc :browser-repl))) + cfg (update cfg :options merge (select-keys opts repl/known-repl-opts)) source (when (= :none (:optimizations opts :none)) (:uri (build/ns->location main-ns)))] (if-let [path (:watch opts)] From c6961d518beb69a8ecd84421b13778fab33bdaab Mon Sep 17 00:00:00 2001 From: Andrea Richiardi Date: Thu, 15 Feb 2018 13:25:30 -0800 Subject: [PATCH 2887/4033] Self-host: munge file name when appending source maps --- src/main/cljs/cljs/js.cljs | 12 +++-- src/test/self/self_host/test.cljs | 85 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index cf2644446..30f6b4c78 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -157,9 +157,11 @@ (defn- append-source-map [state name source sb sm-data {:keys [output-dir asset-path source-map-timestamp] :as opts}] (let [t (.valueOf (js/Date.)) - smn (if name - (string/replace (munge (str name)) "." "/") + mn (if name + (munge (str name)) (str "cljs-" t)) + smn (cond-> mn + name (string/replace "." "/")) ts (.valueOf (js/Date.)) out (or output-dir asset-path) src (cond-> (str smn ".cljs") @@ -173,7 +175,7 @@ :file file :sources-content [source]})] (when (:verbose opts) (debug-prn json)) (swap! state assoc-in - [:source-maps name] (sm/invert-reverse-map (:source-map sm-data))) + [:source-maps (symbol mn)] (sm/invert-reverse-map (:source-map sm-data))) (.append sb (str "\n//# sourceURL=" file "\n//# sourceMappingURL=data:application/json;base64," @@ -969,7 +971,7 @@ the ClojureScript source name (symbol or string) - optional, the name of the source + optional, the name of the source - used as key in :source-maps opts (map) compilation options. @@ -1134,7 +1136,7 @@ the ClojureScript source name (symbol or string) - optional, the name of the source + optional, the name of the source - used as key in :source-maps opts (map) compilation options. diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 98f89eb82..ed61f11bd 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -12,6 +12,7 @@ [cljs.js :as cljs] [cljs.analyzer :as ana] [clojure.string :as string] + [cljs.stacktrace :as st] [cljs.nodejs :as nodejs])) (set! (.-user js/cljs) #js {}) @@ -1360,6 +1361,90 @@ (is (= 4 value)) (inc! l)))))))) +(deftest test-mapping-stacktrace + (async done + (let [l (latch 1 done)] + (testing "it should correctly map from canonical representation (smoke test)." + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(ns cljs.user (:require foo.bar :reload))" + 'cljs.user + {:source-map true + :ns 'cljs.user + :target :nodejs + :eval node-eval + :load (fn [{:keys [name]} cb] + (cb (when (= name 'foo.bar) + {:lang :clj + :source "(ns foo.bar)\n(defn broken-first [] (ffirst 0))" + :file "foo/bar.cljs"})))} + (fn [{:keys [ns value error file]}] + (let [sms (:source-maps @st)] + (is (= [{:function "broken-first" + :file "foo/bar.cljs" + :line 2 + :column 7}] + (st/mapped-stacktrace + [{:file "foo/bar.js" + :function "broken-first" + :line 2 + :column 0}] + sms))))))))))) + +(deftest test-mapping-stacktrace-with-underscore + (async done + (let [l (latch 1 done)] + (testing "it should correctly map when file names contain underscore" + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(ns cljs.user (:require foo.with-underscore :reload))" + 'cljs.user + {:source-map true + :ns 'cljs.user + :target :nodejs + :eval node-eval + :load (fn [{:keys [name]} cb] + (cb (when (= name 'foo.with-underscore) + {:lang :clj + :source "(ns foo.with-underscore)\n(defn broken-first [] (ffirst 0))" + :file "foo/with_underscore.cljs"})))} + (fn [{:keys [ns value error file]}] + (let [sms (:source-maps @st)] + (is (= [{:function "broken-first" + :file "foo/with_underscore.cljs" + :line 2 + :column 7}] + (st/mapped-stacktrace + [{:file "foo/with_underscore.js" + :function "broken-first" + :line 2 + :column 0}] + sms))))))))))) + +(deftest test-append-source-map-with-nil-name + (async done + (let [ + l (latch 1 done)] + (testing "it should correctly use cljs-{js/Date as number} when name to cljs.js/eval-str is nil" + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(ns cljs.user (:require foo.bar :reload))" + nil + {:source-map true + :ns 'cljs.user + :target :nodejs + :eval node-eval + :load (fn [{:keys [name]} cb] + (cb (when (= name 'foo.bar) + {:lang :clj + :source "(ns foo.bar)" + :file "foo/bar.cljs"})))} + (fn [{:keys [ns value error file]}] + (let [cljs-timestamp? #(let [[c t] (string/split % "-")] + (and (= "cljs" c) (not (js/isNaN (js/parseInt t))))) + sms (:source-maps @st)] + (is (some cljs-timestamp? (keys sms))))))))))) + (defn -main [& args] (run-tests)) From 7c754fbb9ffb9da790f21776d53a3b83deef922b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 20 Feb 2018 08:54:16 -0500 Subject: [PATCH 2888/4033] CLJS-2539: script/test-self-host terminating early --- src/test/self/self_host/test.cljs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index ed61f11bd..a6bd46e26 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1389,7 +1389,8 @@ :function "broken-first" :line 2 :column 0}] - sms))))))))))) + sms))) + (inc! l))))))))) (deftest test-mapping-stacktrace-with-underscore (async done @@ -1419,7 +1420,8 @@ :function "broken-first" :line 2 :column 0}] - sms))))))))))) + sms))) + (inc! l))))))))) (deftest test-append-source-map-with-nil-name (async done @@ -1443,7 +1445,8 @@ (let [cljs-timestamp? #(let [[c t] (string/split % "-")] (and (= "cljs" c) (not (js/isNaN (js/parseInt t))))) sms (:source-maps @st)] - (is (some cljs-timestamp? (keys sms))))))))))) + (is (some cljs-timestamp? (keys sms))) + (inc! l))))))))) (defn -main [& args] (run-tests)) From a7dafb5f56256640c73c872a6201fc97fd2bff12 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 22 Feb 2018 19:21:42 -0500 Subject: [PATCH 2889/4033] CLJS-2536: cljs.main: double out directory Node, Rhino, and Nashorn REPLs incorrectly emitted cljs.core if :output-dir was set. This was not apparent before as :output-dir was not a typical option for REPLs. --- src/main/clojure/cljs/cli.clj | 3 +-- src/main/clojure/cljs/repl/nashorn.clj | 5 +++-- src/main/clojure/cljs/repl/node.clj | 5 +++-- src/main/clojure/cljs/repl/rhino.clj | 6 +++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index ee88c4203..3705920d7 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -193,8 +193,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") present" [repl-env [_ & args] {:keys [options inits] :as cfg}] (let [renv (repl-env) - opts (build/add-implicit-options - (merge (repl/repl-options renv) options))] + opts (merge (repl/repl-options renv) options)] (repl/repl* renv (assoc opts :inits diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index 8a9a309c8..af03d8773 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -76,8 +76,9 @@ (let [deps-file ".nashorn_repl_deps.js" core (io/resource "cljs/core.cljs") core-js (closure/compile core - (assoc opts - :output-file (closure/src-file->target-file core))) + (assoc opts :output-file + (closure/src-file->target-file + core (dissoc opts :output-dir)))) deps (closure/add-dependencies opts core-js)] ;; output unoptimized code and the deps file ;; for all compiled namespaces diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index 14da304c3..ec3d8be8b 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -145,8 +145,9 @@ ;; for bootstrap to load, use new closure/compile as it can handle ;; resources in JARs (let [core-js (closure/compile core - (assoc (dissoc opts :output-dir) - :output-file (closure/src-file->target-file core))) + (assoc opts :output-file + (closure/src-file->target-file + core (dissoc opts :output-dir)))) deps (closure/add-dependencies opts core-js)] ;; output unoptimized code and the deps file ;; for all compiled namespaces diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj index a8b171339..b89f80902 100644 --- a/src/main/clojure/cljs/repl/rhino.clj +++ b/src/main/clojure/cljs/repl/rhino.clj @@ -108,9 +108,9 @@ core (io/resource "cljs/core.cljs") base-js (io/resource "goog/base.js") core-js (closure/compile core - (assoc opts - :output-file - (closure/src-file->target-file core))) + (assoc opts :output-file + (closure/src-file->target-file + core (dissoc opts :output-dir)))) deps (closure/add-dependencies opts core-js) output-dir (util/output-directory opts) repl-deps (io/file output-dir "rhino_repl_deps.js")] From 532aed003cc8d8b77fb022f075f3da7b5cbea72e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 22 Feb 2018 19:28:49 -0500 Subject: [PATCH 2890/4033] CLJS-2533: Document that -t accepts none --- src/main/clojure/cljs/cli.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 3705920d7..65dad4382 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -374,8 +374,9 @@ present" ["-t" "--target"] {:group ::main&compile :fn target-opt :arg "name" :doc - (str "The JavaScript target. Supported values: " - "nodejs, nashorn, webworker") }} + (str "The JavaScript target. Configures environment bootstrap and " + "defaults to browser. Supported values: nodejs, nashorn, " + "webworker, none") }} :main {["-r" "--repl"] {:fn repl-opt :doc "Run a repl"} From 08beee84b05f911a6bb0eff0dd1235c201b26c70 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 22 Feb 2018 20:39:54 -0500 Subject: [PATCH 2891/4033] CLJS-2526: Graceful termination for Node.js REPL --- src/main/clojure/cljs/repl/node.clj | 7 +++++-- src/main/clojure/cljs/repl/node_repl.js | 21 ++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index ec3d8be8b..035aa0f36 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -214,8 +214,11 @@ (-load [this provides url] (load-javascript this provides url)) (-tear-down [this] - (.destroy ^Process @proc) - (close-socket @socket))) + (let [{:keys [out]} @socket] + (write out ":cljs/quit") + (while (alive? @proc) + (Thread/sleep 50)) + (close-socket @socket)))) (defn repl-env* [options] (let [{:keys [host port path debug-port]} diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js index c6d288a46..657d1631d 100644 --- a/src/main/clojure/cljs/repl/node_repl.js +++ b/src/main/clojure/cljs/repl/node_repl.js @@ -19,7 +19,7 @@ try { } catch(err) { } -net.createServer(function (socket) { +var server = net.createServer(function (socket) { var buffer = "", ret = null, err = null; @@ -45,12 +45,19 @@ net.createServer(function (socket) { if(data) { // not sure how \0's are getting through - David data = data.replace(/\0/g, ""); - try { - dom.run(function() { - ret = vm.runInThisContext(data, "repl"); - }); - } catch (e) { - err = e; + + if(":cljs/quit" == data) { + server.close(); + socket.destroy(); + return; + } else { + try { + dom.run(function () { + ret = vm.runInThisContext(data, "repl"); + }); + } catch (e) { + err = e; + } } } From c1f1fc86e03ed78aa7681e7c44560411d6b701f1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 11 Feb 2018 18:40:10 -0500 Subject: [PATCH 2892/4033] CLJS-2475: Actual and expected on recur argument count --- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/test/clojure/cljs/analyzer_tests.clj | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c2be899a8..03eeca726 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1972,7 +1972,8 @@ (when-not frame (throw (error env "Can't recur here"))) (when-not (= (count exprs) (count (:params frame))) - (throw (error env "recur argument count mismatch"))) + (throw (error env (str "recur argument count mismatch, expected: " + (count (:params frame)) " args, got: " (count exprs))))) (when (and (:protocol-impl frame) (not add-implicit-target-object?)) (warning :protocol-impl-recur-with-target env {:form (:form (first exprs))})) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 0b0000858..77ff33ef8 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -820,6 +820,10 @@ (a/analyze-file (io/file "src/test/cljs_build/analyzer_test/no_defs.cljs")))) (is (= {} (get-in @test-cenv [::a/namespaces 'analyzer-test.no-defs :defs])))) +(deftest test-cljs-2475 + (is (thrown-with-msg? Exception #"recur argument count mismatch, expected: 2 args, got: 1" + (a/analyze test-env '(loop [x 1 y 2] (recur 3)))))) + (deftest test-cljs-2476 (doseq [invalid-try-recur-form '[(loop [] (try (recur))) (loop [] (try (catch js/Error t (recur)))) From 763a9a37627a8d97b38417567e112ee9abeac090 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 09:23:55 -0500 Subject: [PATCH 2893/4033] CLJS-2463: js calls are mistakenly treated as higher order invokes --- src/main/clojure/cljs/analyzer.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 03eeca726..cf646a92c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3112,6 +3112,7 @@ cur-ns (-> env :ns :name) HO-invoke? (and (boolean *cljs-static-fns*) (not fn-var?) + (not (js-tag? f)) (not kw?) (not (analyzed? f))) ;; function expressions, eg: ((deref m) x) or ((:x m) :a) From f5a4a0998e5058b44ada51b4e4f57340e80e460f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 13:01:14 -0500 Subject: [PATCH 2894/4033] add compile-when helper --- src/main/clojure/cljs/util.cljc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index d73444425..34c1f089d 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -255,6 +255,10 @@ `(do ~then) `(do ~else)))) +(defmacro compile-when + [exp then] + `(compile-if ~exp ~then nil)) + (defn boolean? [x] (or (true? x) (false? x))) From 74411e9e841dd0d535fe72f1499e3a7de672d7ca Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 13:09:08 -0500 Subject: [PATCH 2895/4033] cljs.repl.nashorn cannot be AOT compiled --- pom.template.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 9846dde01..1f11511cb 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -305,7 +305,6 @@ cljs.spec.gen.alpha cljs.repl cljs.repl.browser - cljs.repl.nashorn cljs.repl.node cljs.repl.reflect cljs.repl.rhino From 89fe00e6527c27230f7c74cb93eee9715d4eb234 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 13:58:18 -0500 Subject: [PATCH 2896/4033] :cache-analysis and :source-map are already implicitly true due to cljsc/add-implicit-options --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index e68d62507..6b223378c 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -828,7 +828,7 @@ :as opts :or {warn-on-undeclared true}} (merge - {:cache-analysis true :source-map true :def-emits-var true} + {:def-emits-var true} (cljsc/add-implicit-options (merge-with (fn [a b] (if (nil? b) a b)) repl-opts From 373a77668a7fdf09a681b13a61ab267e714b1b74 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 14:17:48 -0500 Subject: [PATCH 2897/4033] expose eval-cljs so we can use it elsewhere --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 6b223378c..87bf99e49 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -635,7 +635,7 @@ (set! *e e#) (throw e#)))))) -(defn- eval-cljs +(defn eval-cljs "Given a REPL evaluation environment, an analysis environment, and a form, evaluate the form and return the result. The result is always the value represented as a string." From af2cc77453ed846c16436f3c358282a3489ba47b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 16:09:05 -0500 Subject: [PATCH 2898/4033] pREPL wip --- project.clj | 2 +- src/main/clojure/cljs/core/server.clj | 119 ++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 src/main/clojure/cljs/core/server.clj diff --git a/project.clj b/project.clj index c2918e8bf..7d9b5eb53 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs"] :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] - :dependencies [[org.clojure/clojure "1.9.0"] + :dependencies [[org.clojure/clojure "1.10.0-alpha4"] [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj new file mode 100644 index 000000000..4de5ffa05 --- /dev/null +++ b/src/main/clojure/cljs/core/server.clj @@ -0,0 +1,119 @@ +; 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.core.server + (:refer-clojure :exclude [with-bindings resolve-fn prepl io-prepl]) + (:require [cljs.util :as util] + [cljs.env :as env] + [cljs.closure :as closure] + [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] + [cljs.repl :as repl])) + +(defmacro with-bindings [& body] + `(binding [ana/*cljs-ns* ana/*cljs-ns* + ana/*unchecked-if* ana/*unchecked-if* + ana/*unchecked-arrays* ana/*unchecked-arrays*] + ~@body)) + +(defn- resolve-fn [valf] + (if (symbol? valf) + (or (resolve valf) + (when-let [nsname (namespace valf)] + (require (symbol nsname)) + (resolve valf)) + (throw (Exception. (str "can't resolve: " valf)))) + valf)) + +(defn prepl + "a REPL with structured output (for programs) + reads forms to eval from in-reader (a LineNumberingPushbackReader) + Closing the input or passing the form :cljs/quit or :repl/quit will cause it + to return + + Calls out-fn with data, one of: + {:tag :ret + :val val ;;eval result + :ns ns-name-string + :ms long ;;eval time in milliseconds + :form string ;;iff successfully read + } + {:tag :out + :val string} ;chars from during-eval *out* + {:tag :err + :val string} ;chars from during-eval *err* + {:tag :tap + :val val} ;values from tap> + + You might get more than one :out or :err per eval, but exactly one :ret + tap output can happen at any time (i.e. between evals) + If during eval an attempt is made to read *in* it will read from in-reader unless :stdin is supplied +" + [repl-env opts in-reader out-fn & {:keys [stdin]}] + (let [repl-opts (repl/repl-options repl-env) + opts (merge + {:def-emits-var true} + (closure/add-implicit-options + (merge-with (fn [a b] (if (nil? b) a b)) + repl-opts opts))) + EOF (Object.) + tapfn #(out-fn {:tag :tap :val %1}) + env (ana-api/empty-env)] + (with-bindings + (env/ensure + (let [opts (:merge-opts (repl/setup repl-env opts))] + (binding [*in* (or stdin in-reader) + *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) + *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] + (try + (add-tap tapfn) + (loop [] + (when (try + (let [[form s] (read+string in-reader false EOF)] + (try + (when-not (identical? form EOF) + (let [start (System/nanoTime) + ret (repl/eval-cljs repl-env env form opts) + ms (quot (- (System/nanoTime) start) 1000000)] + (when-not (= :repl/quit ret) + (out-fn {:tag :ret + :val (if (instance? Throwable ret) + (Throwable->map ret) + ret) + :ns (str (.name *ns*)) + :ms ms + :form s}) + true))) + (catch Throwable ex + (out-fn {:tag :ret :val (Throwable->map ex) + :ns (str (name ana/*cljs-ns*)) :form s}) + true))) + (catch Throwable ex + (out-fn {:tag :ret :val (Throwable->map ex) + :ns (str (name ana/*cljs-ns*))}) + true)) + (recur))) + (finally + (remove-tap tapfn) + (repl/tear-down repl-env))))))))) + +(defn io-prepl + "prepl bound to *in* and *out*, suitable for use with e.g. server/repl (socket-repl). + :ret and :tap vals will be processed by valf, a fn of one argument + or a symbol naming same (default pr-str)" + [& {:keys [valf repl-env opts] :or {valf identity}}] + (let [valf (resolve-fn valf) + out *out* + lock (Object.)] + (prepl repl-env opts *in* + #(binding [*out* out, ;*flush-on-newline* true, *print-readably* true + ] + (locking lock + (prn (cond-> %1 + (#{:ret :tap} (:tag %1)) + (assoc :val (valf (:val %1)))))))))) From e196bd6029e3117e0bc113c68b12e0e6ff6d2981 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 16:17:54 -0500 Subject: [PATCH 2899/4033] need to ensure, need with-core-cljs, add repl-quit? helper --- src/main/clojure/cljs/core/server.clj | 76 +++++++++++++++------------ 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 4de5ffa05..322258f4d 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -13,7 +13,8 @@ [cljs.closure :as closure] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] - [cljs.repl :as repl])) + [cljs.repl :as repl] + [cljs.compiler :as comp])) (defmacro with-bindings [& body] `(binding [ana/*cljs-ns* ana/*cljs-ns* @@ -30,6 +31,9 @@ (throw (Exception. (str "can't resolve: " valf)))) valf)) +(defn repl-quit? [v] + (#{":repl/quit" ":cljs/quit"} v)) + (defn prepl "a REPL with structured output (for programs) reads forms to eval from in-reader (a LineNumberingPushbackReader) @@ -64,43 +68,45 @@ EOF (Object.) tapfn #(out-fn {:tag :tap :val %1}) env (ana-api/empty-env)] - (with-bindings - (env/ensure - (let [opts (:merge-opts (repl/setup repl-env opts))] - (binding [*in* (or stdin in-reader) - *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) - *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] - (try - (add-tap tapfn) - (loop [] - (when (try - (let [[form s] (read+string in-reader false EOF)] - (try - (when-not (identical? form EOF) - (let [start (System/nanoTime) - ret (repl/eval-cljs repl-env env form opts) - ms (quot (- (System/nanoTime) start) 1000000)] - (when-not (= :repl/quit ret) - (out-fn {:tag :ret - :val (if (instance? Throwable ret) - (Throwable->map ret) - ret) - :ns (str (.name *ns*)) - :ms ms - :form s}) + (env/ensure + (comp/with-core-cljs opts + (fn [] + (with-bindings + (let [opts (:merge-opts (repl/setup repl-env opts))] + (binding [*in* (or stdin in-reader) + *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) + *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] + (try + (add-tap tapfn) + (loop [] + (when (try + (let [[form s] (read+string in-reader false EOF)] + (try + (when-not (identical? form EOF) + (let [start (System/nanoTime) + ret (repl/eval-cljs repl-env env form opts) + ms (quot (- (System/nanoTime) start) 1000000)] + (when-not (repl-quit? ret) + (out-fn {:tag :ret + :val (if (instance? Throwable ret) + (Throwable->map ret) + ret) + :ns (str (.name *ns*)) + :ms ms + :form s}) + true))) + (catch Throwable ex + (out-fn {:tag :ret :val (Throwable->map ex) + :ns (str (name ana/*cljs-ns*)) :form s}) true))) (catch Throwable ex (out-fn {:tag :ret :val (Throwable->map ex) - :ns (str (name ana/*cljs-ns*)) :form s}) - true))) - (catch Throwable ex - (out-fn {:tag :ret :val (Throwable->map ex) - :ns (str (name ana/*cljs-ns*))}) - true)) - (recur))) - (finally - (remove-tap tapfn) - (repl/tear-down repl-env))))))))) + :ns (str (name ana/*cljs-ns*))}) + true)) + (recur))) + (finally + (remove-tap tapfn) + (repl/tear-down repl-env))))))))))) (defn io-prepl "prepl bound to *in* and *out*, suitable for use with e.g. server/repl (socket-repl). From 549db5795128c82ba52458ddf47f6b7327eea4a5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 16:22:19 -0500 Subject: [PATCH 2900/4033] fix bad :ns value --- src/main/clojure/cljs/core/server.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 322258f4d..07cf8263d 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -91,17 +91,17 @@ :val (if (instance? Throwable ret) (Throwable->map ret) ret) - :ns (str (.name *ns*)) + :ns (name ana/*cljs-ns*) :ms ms :form s}) true))) (catch Throwable ex (out-fn {:tag :ret :val (Throwable->map ex) - :ns (str (name ana/*cljs-ns*)) :form s}) + :ns (name ana/*cljs-ns*) :form s}) true))) (catch Throwable ex (out-fn {:tag :ret :val (Throwable->map ex) - :ns (str (name ana/*cljs-ns*))}) + :ns (name ana/*cljs-ns*)}) true)) (recur))) (finally From 3d73dec71763585977f015cb45c9d5c74540f466 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 16:39:25 -0500 Subject: [PATCH 2901/4033] simple demo in comment --- src/main/clojure/cljs/core/server.clj | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 07cf8263d..5d1e62bca 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -123,3 +123,21 @@ (prn (cond-> %1 (#{:ret :tap} (:tag %1)) (assoc :val (valf (:val %1)))))))))) + +(comment + + ;; eval in order + + (defmacro clj-eval [form] + `(quote ~(eval form))) + + (require '[cljs.repl.node :as node]) + + (io-prepl :repl-env (node/repl-env)) + + ;; wait a moment for Node REPL to be ready, then eval the following + + (cljs.core.server/clj-eval + (cljs.analyzer.api/ns-resolve 'cljs.core 'first)) + + ) From cdb853a61cf51f1007de7d28a779b15faafd448d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 17:01:38 -0500 Subject: [PATCH 2902/4033] add a couple of TODOs --- src/main/clojure/cljs/core/server.clj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 5d1e62bca..e7848483e 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -34,6 +34,10 @@ (defn repl-quit? [v] (#{":repl/quit" ":cljs/quit"} v)) +;; TODO: +;; - create user ns +;; - special fns + (defn prepl "a REPL with structured output (for programs) reads forms to eval from in-reader (a LineNumberingPushbackReader) From 00b795fb130fdb1db8d9fd1f143a18d737a3f586 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 17:05:27 -0500 Subject: [PATCH 2903/4033] another TODO --- src/main/clojure/cljs/core/server.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index e7848483e..3da958277 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -37,6 +37,7 @@ ;; TODO: ;; - create user ns ;; - special fns +;; - npm deps (defn prepl "a REPL with structured output (for programs) From dea215843d9db11eed97684f8ce55bd71d485641 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 17:25:37 -0500 Subject: [PATCH 2904/4033] added cljs.user, special-fns wip --- src/main/clojure/cljs/core/server.clj | 30 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 3da958277..ccfac23e2 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -35,8 +35,6 @@ (#{":repl/quit" ":cljs/quit"} v)) ;; TODO: -;; - create user ns -;; - special fns ;; - npm deps (defn prepl @@ -63,21 +61,25 @@ tap output can happen at any time (i.e. between evals) If during eval an attempt is made to read *in* it will read from in-reader unless :stdin is supplied " - [repl-env opts in-reader out-fn & {:keys [stdin]}] + [repl-env {:keys [special-fns] :as opts} in-reader out-fn & {:keys [stdin]}] (let [repl-opts (repl/repl-options repl-env) - opts (merge - {:def-emits-var true} - (closure/add-implicit-options - (merge-with (fn [a b] (if (nil? b) a b)) - repl-opts opts))) - EOF (Object.) - tapfn #(out-fn {:tag :tap :val %1}) - env (ana-api/empty-env)] + opts (merge + {:def-emits-var true} + (closure/add-implicit-options + (merge-with (fn [a b] (if (nil? b) a b)) + repl-opts opts))) + EOF (Object.) + tapfn #(out-fn {:tag :tap :val %1}) + env (ana-api/empty-env) + special-fns (merge repl/default-special-fns special-fns) + is-special-fn? (set (keys special-fns))] (env/ensure (comp/with-core-cljs opts (fn [] (with-bindings (let [opts (:merge-opts (repl/setup repl-env opts))] + (repl/evaluate-form repl-env env "" + (with-meta `(~'ns ~'cljs.user) {:line 1 :column 1}) identity opts) (binding [*in* (or stdin in-reader) *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] @@ -89,7 +91,11 @@ (try (when-not (identical? form EOF) (let [start (System/nanoTime) - ret (repl/eval-cljs repl-env env form opts) + ret (if (and (seq? form) (is-special-fn? (first form))) + (do + ((get special-fns (first form)) repl-env env form opts) + "nil") + (repl/eval-cljs repl-env env form opts)) ms (quot (- (System/nanoTime) start) 1000000)] (when-not (repl-quit? ret) (out-fn {:tag :ret From e5bb928b4b93a47c338c0fb33c77f27cf74b7557 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 18:11:09 -0500 Subject: [PATCH 2905/4033] require works --- src/main/clojure/cljs/core/server.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index ccfac23e2..9288afde3 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -77,10 +77,11 @@ (comp/with-core-cljs opts (fn [] (with-bindings - (let [opts (:merge-opts (repl/setup repl-env opts))] + (let [opts (merge opts (:merge-opts (repl/setup repl-env opts)))] (repl/evaluate-form repl-env env "" (with-meta `(~'ns ~'cljs.user) {:line 1 :column 1}) identity opts) - (binding [*in* (or stdin in-reader) + (binding [repl/*repl-opts* opts + *in* (or stdin in-reader) *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] (try @@ -151,4 +152,7 @@ (cljs.core.server/clj-eval (cljs.analyzer.api/ns-resolve 'cljs.core 'first)) + (require '[clojure.string :as string]) + (string/includes? "foo" "o") + ) From 7c8e2d4614c478ab9c881df491add8efff56ccf6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 18:34:29 -0500 Subject: [PATCH 2906/4033] add-tap, remove-tap, tap>, add default *exec-tap-fn* --- src/main/cljs/cljs/core.cljs | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3db2bee7d..d25922e2b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -68,6 +68,11 @@ Strings which should be printed." :dynamic true} *print-fn* nil) +(defn ^{:dynamic true} + *exec-tap-fn* + [f] + (js/setTimeout f 0)) + (defonce ^{:doc "Each runtime environment provides a different way to print error output. Whatever function *print-err-fn* is bound to will be passed any @@ -11318,6 +11323,39 @@ reduces them without incurring seq initialization" ".." (demunge-str name'))))) +(defonce ^{:jsdoc ["@type {*}"] :private true} + tapset nil) + +(defn maybe-init-tapset [] + (when (nil? tapset) + (set! tapset (atom {})))) + +(defn add-tap + "Adds f, a fn of one argument, to the tap set. This function will be called with + anything sent via tap>. Remember f in order to remove-tap" + [f] + (maybe-init-tapset) + (swap! tapset conj f) + nil) + +(defn remove-tap + "Remove f from the tap set the tap set." + [f] + (maybe-init-tapset) + (swap! tapset disj f) + nil) + +(defn tap> + "Sends x to any taps." + [x] + (maybe-init-tapset) + (*exec-tap-fn* + (fn [] + (doseq [tap @tapset] + (try + (tap x) + (catch js/Error ex)))))) + ;; ----------------------------------------------------------------------------- ;; Bootstrap helpers - incompatible with advanced compilation From 7d469fe6c0bcf3f9032ece088841f5b8263e5fc3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 18:42:48 -0500 Subject: [PATCH 2907/4033] remove unused ns --- src/main/clojure/cljs/core/server.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 9288afde3..3da99d18c 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -8,8 +8,7 @@ (ns cljs.core.server (:refer-clojure :exclude [with-bindings resolve-fn prepl io-prepl]) - (:require [cljs.util :as util] - [cljs.env :as env] + (:require [cljs.env :as env] [cljs.closure :as closure] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] From 3615c88cdcc71050ee5712e196867ccc480ee0ec Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 18:50:14 -0500 Subject: [PATCH 2908/4033] fix docstrings --- src/main/clojure/cljs/core/server.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 3da99d18c..0d22e464a 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -37,14 +37,14 @@ ;; - npm deps (defn prepl - "a REPL with structured output (for programs) + "A REPL with structured output (for programs) reads forms to eval from in-reader (a LineNumberingPushbackReader) Closing the input or passing the form :cljs/quit or :repl/quit will cause it to return Calls out-fn with data, one of: {:tag :ret - :val val ;;eval result + :val string ;;eval result :ns ns-name-string :ms long ;;eval time in milliseconds :form string ;;iff successfully read @@ -54,7 +54,7 @@ {:tag :err :val string} ;chars from during-eval *err* {:tag :tap - :val val} ;values from tap> + :val string} ;values from tap> You might get more than one :out or :err per eval, but exactly one :ret tap output can happen at any time (i.e. between evals) @@ -122,7 +122,7 @@ (defn io-prepl "prepl bound to *in* and *out*, suitable for use with e.g. server/repl (socket-repl). :ret and :tap vals will be processed by valf, a fn of one argument - or a symbol naming same (default pr-str)" + or a symbol naming same (default identity)" [& {:keys [valf repl-env opts] :or {valf identity}}] (let [valf (resolve-fn valf) out *out* From 6e5fdad58d60be5d4bead66257db36ddc28c68b1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 19:08:36 -0500 Subject: [PATCH 2909/4033] CLJS-2547: :npm-deps support in pREPL --- src/main/clojure/cljs/core/server.clj | 1 + src/main/clojure/cljs/repl.cljc | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 0d22e464a..3fe1165e7 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -73,6 +73,7 @@ special-fns (merge repl/default-special-fns special-fns) is-special-fn? (set (keys special-fns))] (env/ensure + (repl/maybe-install-npm-deps opts) (comp/with-core-cljs opts (fn [] (with-bindings diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 87bf99e49..3ec1bf4cc 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -795,6 +795,15 @@ :init-script (load-file renv (:script init))))) +(defn maybe-install-npm-deps [opts] + (when (:install-deps opts) + (cljsc/check-npm-deps opts) + (swap! env/*compiler* update-in [:npm-deps-installed?] + (fn [installed?] + (if-not installed? + (cljsc/maybe-install-node-deps! opts) + installed?))))) + (defn repl* [repl-env {:keys [init inits need-prompt quit-prompt prompt flush read eval print caught reader print-no-newline source-map-inline wrap repl-requires @@ -917,13 +926,7 @@ (print nil)) (let [value (eval repl-env env input opts)] (print value))))))] - (when (:install-deps opts) - (cljsc/check-npm-deps opts) - (swap! env/*compiler* update-in [:npm-deps-installed?] - (fn [installed?] - (if-not installed? - (cljsc/maybe-install-node-deps! opts) - installed?)))) + (maybe-install-npm-deps opts) (comp/with-core-cljs opts (fn [] (binding [*repl-opts* opts] From 2f9ff09ea3046fc873ebeb1df774e1ceb7719174 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2018 19:24:33 -0500 Subject: [PATCH 2910/4033] remove todo --- src/main/clojure/cljs/core/server.clj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 3fe1165e7..5839356df 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -33,9 +33,6 @@ (defn repl-quit? [v] (#{":repl/quit" ":cljs/quit"} v)) -;; TODO: -;; - npm deps - (defn prepl "A REPL with structured output (for programs) reads forms to eval from in-reader (a LineNumberingPushbackReader) From 6b002d022d60e31c0453ca46a018fcc7f9cf9143 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Feb 2018 09:53:53 -0500 Subject: [PATCH 2911/4033] io needs to be bound before setting up REPLs --- src/main/clojure/cljs/core/server.clj | 84 +++++++++++++-------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 5839356df..4350226ae 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -74,48 +74,48 @@ (comp/with-core-cljs opts (fn [] (with-bindings - (let [opts (merge opts (:merge-opts (repl/setup repl-env opts)))] - (repl/evaluate-form repl-env env "" - (with-meta `(~'ns ~'cljs.user) {:line 1 :column 1}) identity opts) - (binding [repl/*repl-opts* opts - *in* (or stdin in-reader) - *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) - *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] - (try - (add-tap tapfn) - (loop [] - (when (try - (let [[form s] (read+string in-reader false EOF)] - (try - (when-not (identical? form EOF) - (let [start (System/nanoTime) - ret (if (and (seq? form) (is-special-fn? (first form))) - (do - ((get special-fns (first form)) repl-env env form opts) - "nil") - (repl/eval-cljs repl-env env form opts)) - ms (quot (- (System/nanoTime) start) 1000000)] - (when-not (repl-quit? ret) - (out-fn {:tag :ret - :val (if (instance? Throwable ret) - (Throwable->map ret) - ret) - :ns (name ana/*cljs-ns*) - :ms ms - :form s}) - true))) - (catch Throwable ex - (out-fn {:tag :ret :val (Throwable->map ex) - :ns (name ana/*cljs-ns*) :form s}) - true))) - (catch Throwable ex - (out-fn {:tag :ret :val (Throwable->map ex) - :ns (name ana/*cljs-ns*)}) - true)) - (recur))) - (finally - (remove-tap tapfn) - (repl/tear-down repl-env))))))))))) + (binding [*in* (or stdin in-reader) + *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) + *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] + (let [opts (merge opts (:merge-opts (repl/setup repl-env opts)))] + (binding [repl/*repl-opts* opts] + (repl/evaluate-form repl-env env "" + (with-meta `(~'ns ~'cljs.user) {:line 1 :column 1}) identity opts) + (try + (add-tap tapfn) + (loop [] + (when (try + (let [[form s] (read+string in-reader false EOF)] + (try + (when-not (identical? form EOF) + (let [start (System/nanoTime) + ret (if (and (seq? form) (is-special-fn? (first form))) + (do + ((get special-fns (first form)) repl-env env form opts) + "nil") + (repl/eval-cljs repl-env env form opts)) + ms (quot (- (System/nanoTime) start) 1000000)] + (when-not (repl-quit? ret) + (out-fn {:tag :ret + :val (if (instance? Throwable ret) + (Throwable->map ret) + ret) + :ns (name ana/*cljs-ns*) + :ms ms + :form s}) + true))) + (catch Throwable ex + (out-fn {:tag :ret :val (Throwable->map ex) + :ns (name ana/*cljs-ns*) :form s}) + true))) + (catch Throwable ex + (out-fn {:tag :ret :val (Throwable->map ex) + :ns (name ana/*cljs-ns*)}) + true)) + (recur))) + (finally + (remove-tap tapfn) + (repl/tear-down repl-env)))))))))))) (defn io-prepl "prepl bound to *in* and *out*, suitable for use with e.g. server/repl (socket-repl). From 4c1b86a0579874c647ea61853f76fd451bc3660f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Feb 2018 09:17:58 -0500 Subject: [PATCH 2912/4033] CLJS-2549: tapset initialized to a map --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d25922e2b..82495b775 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11328,7 +11328,7 @@ reduces them without incurring seq initialization" (defn maybe-init-tapset [] (when (nil? tapset) - (set! tapset (atom {})))) + (set! tapset (atom #{})))) (defn add-tap "Adds f, a fn of one argument, to the tap set. This function will be called with diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 172fc1572..00fc57cab 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1515,6 +1515,13 @@ (deftest test-cljs-2457 (is (thrown-with-msg? js/Error #".* is not ISeqable" (seq #js {:a 1 :b 2})))) +(deftest test-cljs-2549 + (let [tap (fn [_])] + (add-tap tap) + (is (set? @tapset)) + (is (contains? @tapset tap)) + (remove-tap tap))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 4c459cc3829f99872accc58ca7f514ceaa4af3ee Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 23 Feb 2018 20:21:56 -0500 Subject: [PATCH 2913/4033] CLJS-2548: Upgrade Chakra used in Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 11c644787..6dc09f3a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_install: - wget https://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/jsshell-linux-x86_64.zip - unzip jsshell-linux-x86_64.zip -d spidermoney - sudo apt-get install -y libjavascriptcoregtk-3.0-bin - - wget https://aka.ms/chakracore/cc_linux_x64_1_5_2 -O chakra-core.tar.gz + - wget https://aka.ms/chakracore/cc_linux_x64_1_8_1 -O chakra-core.tar.gz - tar xvzf chakra-core.tar.gz before_script: From 7ac247164bdc587b05fffd6a9bd526868eb7b4a3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Feb 2018 12:20:56 -0500 Subject: [PATCH 2914/4033] CLJS-2545: cljs.main background the watcher/compiler on a thread --- src/main/clojure/cljs/cli.clj | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 65dad4382..adc6883b2 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -17,7 +17,7 @@ [cljs.compiler.api :as comp] [cljs.build.api :as build] [cljs.repl :as repl]) - (:import [java.io StringReader] + (:import [java.io StringReader FileWriter] [java.text BreakIterator] [java.util Locale])) @@ -270,6 +270,15 @@ present" ((::main (repl/repl-options (repl-env)) default-main) repl-env (merge cfg {:script path :args args}))) +(defn watch-proc [cenv path opts] + (let [log-file (io/file (util/output-directory opts) "watch.log")] + (util/mkdirs log-file) + (repl/err-out (println "Watch compilation log available at:" (str log-file))) + (let [log-out (FileWriter. log-file)] + (binding [*err* log-out + *out* log-out] + (build/watch path (dissoc opts :watch) cenv))))) + (defn default-compile [repl-env {:keys [ns args options] :as cfg}] (let [env-opts (repl/repl-options (repl-env)) @@ -288,16 +297,22 @@ present" (assoc :output-to (.getPath (io/file (:output-dir opts "out") "main.js"))) (= :advanced (:optimizations opts)) - (dissoc :browser-repl))) + (dissoc :browser-repl) + (not (:output-dir opts)) + (assoc :output-dir "out"))) cfg (update cfg :options merge (select-keys opts repl/known-repl-opts)) source (when (= :none (:optimizations opts :none)) - (:uri (build/ns->location main-ns)))] + (:uri (build/ns->location main-ns))) + repl? (boolean (#{"-r" "--repl"} (first args))) + cenv (env/default-compiler-env)] (if-let [path (:watch opts)] - (build/watch path opts) - (do - (build/build source opts) - (when (#{"-r" "--repl"} (first args)) - (repl-opt repl-env args cfg)))))) + (if repl? + (.start (Thread. #(watch-proc cenv path opts))) + (build/watch path opts cenv)) + (build/build source opts cenv)) + (when repl? + (repl-opt repl-env args + (assoc-in cfg [:options :compiler-env] cenv))))) (defn- compile-opt [repl-env [_ ns & args] cfg] From 3f4084efcb5cd92cceb8ca30765144691d8cfd7e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 24 Feb 2018 16:48:37 -0500 Subject: [PATCH 2915/4033] CLJS-2550: make maybe-init-tapset private Also maybe-enable-print! --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 82495b775..f1d246e1d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11326,7 +11326,7 @@ reduces them without incurring seq initialization" (defonce ^{:jsdoc ["@type {*}"] :private true} tapset nil) -(defn maybe-init-tapset [] +(defn- maybe-init-tapset [] (when (nil? tapset) (set! tapset (atom #{})))) @@ -11486,7 +11486,7 @@ reduces them without incurring seq initialization" [x] (instance? goog.Uri x)) -(defn maybe-enable-print! [] +(defn- maybe-enable-print! [] (cond (exists? js/console) (enable-console-print!) From 55081639ac6953cf9cddddf4dc1cfdc12441352c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 25 Feb 2018 11:15:56 -0500 Subject: [PATCH 2916/4033] CLJS-2552: Make tap> return true / false --- src/main/cljs/cljs/core.cljs | 12 ++++++++---- src/test/cljs/cljs/core_test.cljs | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f1d246e1d..6adfc7d5c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -68,10 +68,14 @@ Strings which should be printed." :dynamic true} *print-fn* nil) -(defn ^{:dynamic true} +(defn ^{:doc "Arranges to have tap functions executed via the supplied f, a + function of no arguments. Returns true if successful, false otherwise." :dynamic true} *exec-tap-fn* [f] - (js/setTimeout f 0)) + (and + (exists? js/setTimeout) + (js/setTimeout f 0) + true)) (defonce ^{:doc "Each runtime environment provides a different way to print error output. @@ -11345,8 +11349,8 @@ reduces them without incurring seq initialization" (swap! tapset disj f) nil) -(defn tap> - "Sends x to any taps." +(defn ^boolean tap> + "Sends x to any taps. Returns the result of *exec-tap-fn*, a Boolean value." [x] (maybe-init-tapset) (*exec-tap-fn* diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 00fc57cab..ada361688 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1522,6 +1522,9 @@ (is (contains? @tapset tap)) (remove-tap tap))) +(deftest test-cljs-2552 + (is (boolean? (tap> nil)))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From b54db69a2133ec51b110ed06d12097c2c0e7ebbd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Feb 2018 11:54:18 -0500 Subject: [PATCH 2917/4033] bump tools.reader --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index acb970b21..2e25df92b 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,7 @@ {:paths ["src/main/clojure" "src/main/cljs"] :deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.clojure/tools.reader {:mvn/version "1.2.2"} + org.clojure/tools.reader {:mvn/version "1.3.0-alpha1"} org.clojure/test.check {:mvn/version "0.10.0-alpha2"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} diff --git a/pom.template.xml b/pom.template.xml index 1f11511cb..0d0b6f68c 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.2.2 + 1.3.0-alpha1 org.clojure diff --git a/project.clj b/project.clj index 7d9b5eb53..d105fd3c9 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.2.2"] + [org.clojure/tools.reader "1.3.0-alpha1"] [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] diff --git a/script/bootstrap b/script/bootstrap index 42540c31a..1550d317d 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.2.2" +TREADER_RELEASE="1.3.0-alpha1" TEST_CHECK_RELEASE="0.10.0-alpha2" # check dependencies From d31e17d695e354a574781b5d1a0de43cc5015525 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Feb 2018 13:49:48 -0500 Subject: [PATCH 2918/4033] wip --- src/main/clojure/cljs/core/server.clj | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 4350226ae..e47b2b3fc 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -8,12 +8,15 @@ (ns cljs.core.server (:refer-clojure :exclude [with-bindings resolve-fn prepl io-prepl]) - (:require [cljs.env :as env] + (:require [clojure.tools.reader.reader-types :as readers] + [clojure.tools.reader :as reader] + [cljs.env :as env] [cljs.closure :as closure] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.repl :as repl] - [cljs.compiler :as comp])) + [cljs.compiler :as comp] + [cljs.tagged-literals :as tags])) (defmacro with-bindings [& body] `(binding [ana/*cljs-ns* ana/*cljs-ns* @@ -85,7 +88,15 @@ (add-tap tapfn) (loop [] (when (try - (let [[form s] (read+string in-reader false EOF)] + (let [[form s] (binding [*ns* (create-ns ana/*cljs-ns*) + reader/resolve-symbol ana/resolve-symbol + reader/*data-readers* tags/*cljs-data-readers* + reader/*alias-map* + (apply merge + ((juxt :requires :require-macros) + (ana/get-namespace ana/*cljs-ns*)))] + (reader/read+string in-reader + {:eof EOF :read-cond :allow :features #{:cljs}}))] (try (when-not (identical? form EOF) (let [start (System/nanoTime) @@ -125,7 +136,8 @@ (let [valf (resolve-fn valf) out *out* lock (Object.)] - (prepl repl-env opts *in* + (prepl repl-env opts + (readers/source-logging-push-back-reader *in* 1 "NO_SOURCE_FILE") #(binding [*out* out, ;*flush-on-newline* true, *print-readably* true ] (locking lock From 05e5cff50e5e4cc7ac3f680a0438f8d7d2be19cf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Feb 2018 14:09:22 -0500 Subject: [PATCH 2919/4033] bump tools.reader --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index 2e25df92b..c7e731938 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,7 @@ {:paths ["src/main/clojure" "src/main/cljs"] :deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.clojure/tools.reader {:mvn/version "1.3.0-alpha1"} + org.clojure/tools.reader {:mvn/version "1.3.0-alpha3"} org.clojure/test.check {:mvn/version "0.10.0-alpha2"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} diff --git a/pom.template.xml b/pom.template.xml index 0d0b6f68c..f252d9012 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.3.0-alpha1 + 1.3.0-alpha3 org.clojure diff --git a/project.clj b/project.clj index d105fd3c9..fa560cada 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.3.0-alpha1"] + [org.clojure/tools.reader "1.3.0-alpha3"] [org.clojure/test.check "0.10.0-alpha2" :scope "test"] [com.cognitect/transit-clj "0.8.300"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] diff --git a/script/bootstrap b/script/bootstrap index 1550d317d..a3fe8f95b 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.300" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" RHINO_RELEASE="1_7R5" -TREADER_RELEASE="1.3.0-alpha1" +TREADER_RELEASE="1.3.0-alpha3" TEST_CHECK_RELEASE="0.10.0-alpha2" # check dependencies From 2c849fb47f5213705d6ff9ad00a061ef1a2296c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Feb 2018 15:18:54 -0500 Subject: [PATCH 2920/4033] add namespaces to make it easier to start socket REPLs and pREPLs --- src/main/clojure/cljs/server/browser.clj | 18 ++++++++++++++++++ src/main/clojure/cljs/server/nashorn.clj | 18 ++++++++++++++++++ src/main/clojure/cljs/server/node.clj | 18 ++++++++++++++++++ src/main/clojure/cljs/server/rhino.clj | 18 ++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 src/main/clojure/cljs/server/browser.clj create mode 100644 src/main/clojure/cljs/server/nashorn.clj create mode 100644 src/main/clojure/cljs/server/node.clj create mode 100644 src/main/clojure/cljs/server/rhino.clj diff --git a/src/main/clojure/cljs/server/browser.clj b/src/main/clojure/cljs/server/browser.clj new file mode 100644 index 000000000..4b9f9930b --- /dev/null +++ b/src/main/clojure/cljs/server/browser.clj @@ -0,0 +1,18 @@ +; 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.server.browser + (:require [cljs.repl :as repl] + [cljs.repl.browser :as browser] + [cljs.core.server :as server])) + +(defn repl [] + (repl/repl (browser/repl-env))) + +(defn prepl [] + (server/io-prepl :repl-env (browser/repl-env))) diff --git a/src/main/clojure/cljs/server/nashorn.clj b/src/main/clojure/cljs/server/nashorn.clj new file mode 100644 index 000000000..f561951f4 --- /dev/null +++ b/src/main/clojure/cljs/server/nashorn.clj @@ -0,0 +1,18 @@ +; 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.server.nashorn + (:require [cljs.repl :as repl] + [cljs.repl.nashorn :as nashorn] + [cljs.core.server :as server])) + +(defn repl [] + (repl/repl (nashorn/repl-env))) + +(defn prepl [] + (server/io-prepl :repl-env (nashorn/repl-env))) diff --git a/src/main/clojure/cljs/server/node.clj b/src/main/clojure/cljs/server/node.clj new file mode 100644 index 000000000..a7d562f45 --- /dev/null +++ b/src/main/clojure/cljs/server/node.clj @@ -0,0 +1,18 @@ +; 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.server.node + (:require [cljs.repl :as repl] + [cljs.repl.node :as node] + [cljs.core.server :as server])) + +(defn repl [] + (repl/repl (node/repl-env))) + +(defn prepl [] + (server/io-prepl :repl-env (node/repl-env))) diff --git a/src/main/clojure/cljs/server/rhino.clj b/src/main/clojure/cljs/server/rhino.clj new file mode 100644 index 000000000..dace6e6c6 --- /dev/null +++ b/src/main/clojure/cljs/server/rhino.clj @@ -0,0 +1,18 @@ +; 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.server.rhino + (:require [cljs.repl :as repl] + [cljs.repl.rhino :as rhino] + [cljs.core.server :as server])) + +(defn repl [] + (repl/repl (rhino/repl-env))) + +(defn prepl [] + (server/io-prepl :repl-env (rhino/repl-env))) From 68addc4178c0e8c5fee02ae5c998832adc80b6bf Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Sun, 25 Feb 2018 15:47:22 -0500 Subject: [PATCH 2921/4033] CLJS-2553: Supply a default index.html for browser repl when it doesn't already exist. --- src/main/clojure/cljs/repl/browser.clj | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 49288765b..cb8c26870 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -111,7 +111,8 @@ (io/file (string/replace path (str (System/getProperty "user.dir") "/") "")) :else nil) local-path)] - (if local-path + (cond + local-path (if-let [ext (some #(if (.endsWith path %) %) (keys ext->mime-type))] (let [mime-type (ext->mime-type ext "text/plain") encoding (mime-type->encoding mime-type "UTF-8")] @@ -122,7 +123,20 @@ mime-type encoding)) (server/send-and-close conn 200 (slurp local-path) "text/plain")) - (server/send-404 conn path))) + ;; "/index.html" doesn't exist, provide our own + (and (= path "/index.html") + env/*compiler* + (some->> @env/*compiler* :options ((juxt :main :output-to)) (every? some?))) + (let [{:keys [output-to]} (:options @env/*compiler*)] + (server/send-and-close + conn + 200 + (str "" + "" + "") + "text/html" + "UTF-8")) + :else (server/send-404 conn path))) (server/send-404 conn path))) (server/dispatch-on :get From 9f92eac32cf34cf7c9aa5d0c1abcab8e4b755597 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Feb 2018 19:10:17 -0500 Subject: [PATCH 2922/4033] cleanup --- src/main/clojure/cljs/repl/browser.clj | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index cb8c26870..946ce04c6 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -11,7 +11,6 @@ (:require [clojure.java.io :as io] [clojure.string :as string] [clojure.edn :as edn] - [cljs.compiler :as comp] [cljs.util :as util] [cljs.env :as env] [cljs.closure :as cljsc] @@ -110,7 +109,8 @@ (re-find (Pattern/compile (System/getProperty "user.dir")) path) (io/file (string/replace path (str (System/getProperty "user.dir") "/") "")) :else nil) - local-path)] + local-path) + copts (when env/*compiler* (get @env/*compiler* :options))] (cond local-path (if-let [ext (some #(if (.endsWith path %) %) (keys ext->mime-type))] @@ -124,18 +124,14 @@ encoding)) (server/send-and-close conn 200 (slurp local-path) "text/plain")) ;; "/index.html" doesn't exist, provide our own - (and (= path "/index.html") - env/*compiler* - (some->> @env/*compiler* :options ((juxt :main :output-to)) (every? some?))) - (let [{:keys [output-to]} (:options @env/*compiler*)] - (server/send-and-close - conn - 200 - (str "" - "" - "") - "text/html" - "UTF-8")) + (and (= path "/index.html") (:output-to copts)) + (let [{:keys [output-to]} copts] + (server/send-and-close conn 200 + (str "" + "" + "") + "text/html" + "UTF-8")) :else (server/send-404 conn path))) (server/send-404 conn path))) @@ -265,7 +261,6 @@ (assoc old :client-js (create-client-js-file repl-env (io/file working-dir "client.js"))))) - opts (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) From 23724c0d233b1442d48ddbba8ca31f5525cc2f05 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Feb 2018 19:16:10 -0500 Subject: [PATCH 2923/4033] add div with CSS id `app` to default index.html --- src/main/clojure/cljs/repl/browser.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 946ce04c6..05857624e 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -128,6 +128,7 @@ (let [{:keys [output-to]} copts] (server/send-and-close conn 200 (str "" + "
" "" "") "text/html" From d8e33cd9826cfc6da70e79f8f6984ad8c5caa9bb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Feb 2018 21:24:30 -0500 Subject: [PATCH 2924/4033] allow browser REPL to work w/o an explicit compilation step this path is taken only if the path is "/out/main.js" and it doesn't exist on disk --- src/main/clojure/cljs/repl/browser.clj | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 05857624e..a803373f2 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -18,7 +18,8 @@ [cljs.cli :as cli] [cljs.repl.server :as server] [cljs.stacktrace :as st] - [cljs.analyzer :as ana]) + [cljs.analyzer :as ana] + [cljs.build.api :as build]) (:import [java.util.regex Pattern] [java.util.concurrent Executors])) @@ -124,8 +125,8 @@ encoding)) (server/send-and-close conn 200 (slurp local-path) "text/plain")) ;; "/index.html" doesn't exist, provide our own - (and (= path "/index.html") (:output-to copts)) - (let [{:keys [output-to]} copts] + (= path "/index.html") + (let [{:keys [output-to] :or {output-to "out/main.js"}} copts] (server/send-and-close conn 200 (str "" "
" @@ -133,6 +134,21 @@ "") "text/html" "UTF-8")) + (= path "/out/main.js") + (do + ;; TODO: this could be cleaner if compiling forms resulted in a + ;; :output-to file with the result of compiling those forms - David + (env/with-compiler-env (env/default-compiler-env) + (build/build + '[(require '[clojure.browser.repl.preload])] + {:output-to "out/cljs_deps.js"})) + (server/send-and-close conn 200 + (str "document.write('');\n" + "document.write('');\n" + "document.write('');\n" + "document.write('');\n") + "text/javascript" + "UTF-8")) :else (server/send-404 conn path))) (server/send-404 conn path))) From f75a0d62e9f356fce4b1731e09addc900a348958 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 25 Feb 2018 21:40:17 -0500 Subject: [PATCH 2925/4033] attempt to open browser REPL url if possible --- src/main/clojure/cljs/repl/browser.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index a803373f2..f2e1dd9c9 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -19,7 +19,8 @@ [cljs.repl.server :as server] [cljs.stacktrace :as st] [cljs.analyzer :as ana] - [cljs.build.api :as build]) + [cljs.build.api :as build] + [clojure.java.shell :as shell]) (:import [java.util.regex Pattern] [java.util.concurrent Executors])) @@ -281,6 +282,9 @@ (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) + (try + (shell/sh "open" (str "http://" (:host repl-env) ":" (:port repl-env))) + (catch Throwable t)) (server/start repl-env))) (defrecord BrowserEnv [] From ec0e4c49fa5bf97d6c8b5a8e5d3bd957855bfe56 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 25 Feb 2018 23:25:58 -0500 Subject: [PATCH 2926/4033] CLJS-2555: Auto-open browser REPL on Linux and Windows --- src/main/clojure/cljs/repl/browser.clj | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index f2e1dd9c9..dce4903a6 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -9,6 +9,7 @@ (ns cljs.repl.browser (:refer-clojure :exclude [loaded-libs]) (:require [clojure.java.io :as io] + [clojure.java.browse :as browse] [clojure.string :as string] [clojure.edn :as edn] [cljs.util :as util] @@ -19,8 +20,7 @@ [cljs.repl.server :as server] [cljs.stacktrace :as st] [cljs.analyzer :as ana] - [cljs.build.api :as build] - [clojure.java.shell :as shell]) + [cljs.build.api :as build]) (:import [java.util.regex Pattern] [java.util.concurrent Executors])) @@ -282,10 +282,8 @@ (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) - (try - (shell/sh "open" (str "http://" (:host repl-env) ":" (:port repl-env))) - (catch Throwable t)) - (server/start repl-env))) + (server/start repl-env) + (browse/browse-url (str "http://" (:host repl-env) ":" (:port repl-env))))) (defrecord BrowserEnv [] repl/IJavaScriptEnv From adba8f7f45eeb6700cc43e71abef4f40557b23b3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 26 Feb 2018 12:00:06 -0500 Subject: [PATCH 2927/4033] CLJS-2558: Add logo to browser REPL default index.html --- deps.edn | 2 +- resources/cljs-logo-icon-32.png | Bin 0 -> 3071 bytes resources/cljs-logo.svg | 18 ++++++++++++++ src/main/clojure/cljs/repl/browser.clj | 33 ++++++++++++++++++++++--- 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 resources/cljs-logo-icon-32.png create mode 100644 resources/cljs-logo.svg diff --git a/deps.edn b/deps.edn index c7e731938..31207eb45 100644 --- a/deps.edn +++ b/deps.edn @@ -1,4 +1,4 @@ -{:paths ["src/main/clojure" "src/main/cljs"] +{:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps {org.clojure/clojure {:mvn/version "1.9.0"} org.clojure/tools.reader {:mvn/version "1.3.0-alpha3"} diff --git a/resources/cljs-logo-icon-32.png b/resources/cljs-logo-icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..16d54958d2d3f2ca44ba561d8faefe112f8aef6d GIT binary patch literal 3071 zcmZuzc{mhWA0BHMuE^RoW*Aw*%n%u43`tqCjIFF$XBgXzv5n<2NFsZdku4vREoHg) zq6nq5V2bRrrjW8T$VYXT?|%0@&)MGdzQ669KTbT>M30B-I2Ql_;4#qGH9wdkhn<7{ zVDt|ST|1bV{LS@r0C&5^J|1j1N%}Va000bn*qJD$T0sB+vogWbI>6f42<1xjfjPSo zU2rgp59xpn0H7(TgQX8Hz!^gE@%HscQ8XpLF;EBVLl`az`9=xw(v-9|#zM{#$vB8A zOa+FJ#Bf0%5H#5hk22Ri|3iMT)0FfG2q2;0@ZjKJSnw$rk?amvQdd`pBb4FF%8CaJ zMgI`r0B4G#uRrv=kw5I{;{0971X2Kj=nFZt>+C`d4A7L6JaqJD`w^!P=}$+#{y(xh z$PZ3&Cc%|p2>8E=0tooO!uuc9;mLQ|x2(Q9Lm!j|b(V~C4j_^(i9~PA8CN3N7ZQXc z`yWQA0#k*lqT&A}{S_?w&?@RI(T7CF`5#I!2lW5L{>is7$N3Yz0}qRUIfX#{Q2ZVJ zr^5PQ6@N#6DA4f3s{XCY->>i+dT@sru7hm;cn=sZHI?<#2QLb5pnJyBnloS6$J=6H zeRQq{nLIs+3=-47#l$R8UlLVCAjmmuSN>Y8=pne1PD8yVIB3w7PM6yqnJp;NJ zAg+~3m3>=kE;p)3;i=HXF31}?NXgq=k*B9@3&-#vFD!f5Pln|-g?Kq-@IJYw1E4P@ zbdh7bkIdN=FpjU6j2Tm}oUaY_S|v|^&Qc@l&BzltI@}+IO{`!oQ0O6+t~F#`j;3cr zaFPHhdm?|6#omf)u5Eg`cqC*NP0JZN^Gl>-C7# zJAz`{(l})mo*6`X4H-={`f*awqg+YV%b`G2plDNX6?0S2G2hxdEIkwY6Lqv)fyyX-*_X!*4ERIEGC|iG}@?n3OD? z-Dsb++N3ZnLz`q~%jGv?nycB6RJa+M$lIa?sQXho=M?dfcfgld&SQMTj~O1 zckv5cOT6NiBxCA-ZZ{^hHb+%dpp%Z{w8!2ImZR$$(};sX_Q@s*R{L+OUrBC|I@i2( z_6m_)TdQ-XGV^xglTp1A685uPm-?&3MO%VDXY8MB+ut@QTj{$E-ObD|B%CB&HihZJ zhEJq6+MiM24fLLlM4FWr%#W9H>Qx$)?+Xj8ooDaM=ZV1eUyth_i>paEx0hg85-<8P zcdC~ss;HypnREQY!PTMhmYeHJmFO*uDd@SHg zg0JKifmt)PjRnl7(WJ)&SL5Z)#xu%F0h$Rd#iXg5nX(4l`hz|JfOfOgrWM7-a$M>B zGwb%PGj9S%e_DWT>W!s?7Au*g9YAMg1PUMWo7BdCAdvWPn*iuHIe*|RBFpHC*#nJy+1z?<0Nq?N3bmb|Fbc|r4xk=Ur-4m>qTc4-R zEW=NTn72<&F)g8X@3pAI@XDTB+jV)nq!&CvrS1X=TzNnPCR5}%3(W)a&OXDhHEpze z({c1A^oAO3#_{6u8vBHQ=)jzqr8M&8Whm|nW zu4>RM$)c=E)x0=K5f*+Q&1Zp)l)bu>FJ7drz}IbTS7ea`j} z6%z_+hTK`=?Z*40CVJ#_b}{YH}n+2@r`DLo-pK0j}1*o!e}#&xfzR>$Po zpr)7AkqQ_W2z9jL!mTTPWEcmyVf2>NPo^0AcChVC=Gwv#9pua&Q_>Cddwp3;u6L%v za#wa>#x1|N`d-Oa^On!)gw?yU;YRK31l9O~?y50pVoWA3muHCd=x8s0axWX#B7S4I zbH_%xluGw_^dP;TM|xI8D_E8L$&x+oBj4i9j~vHeA$&@_ht;?r(&4w@p(c-k>>##+ ziG+65r77@gGm?XP_p8Xe?Sewb`GiNiVO*?P84|Z1Xt#F(3FquIj)_b;sJURnYO=X` zd7YLM#9b{PE=7_yf>W^P)9$sl!mX)brZL3=Hr=pe zy@As5>%85@`gtFaD;&6vy;lzW__moIB|FK!5ydQ^RBn|p=0%3vCx>Nx%bsP@gGwL8 z&naxmR(#TUvpkO3&x|rV<11x*60>2gg!f**sXV{LaI;;37uu$$p?;UuQBJ#-F^MEc zP{8jZYNfZ|=EzVWvW9h49R_@6U=z7FcUFq{y(^q%ikFD<<^?^GhgW5g3nYjuRPU-^$L` z+iH2gQ1z)DA%Ifox)f>emE)LxN+?ApdvwzQ5d-H>Do0_#=1yo}>Khdc`j~R5-WK Z^b+C4nNoDq;_z2)aLz>cu8vdme*wtAYZ?Fm literal 0 HcmV?d00001 diff --git a/resources/cljs-logo.svg b/resources/cljs-logo.svg new file mode 100644 index 000000000..714dad59b --- /dev/null +++ b/resources/cljs-logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index dce4903a6..8963ca94e 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -35,6 +35,7 @@ ".jpg" "image/jpeg" ".png" "image/png" ".gif" "image/gif" + ".svg" "image/svg+xml" ".js" "text/javascript" ".json" "application/json" @@ -50,6 +51,7 @@ "image/jpeg" "ISO-8859-1" "image/png" "ISO-8859-1" "image/gif" "ISO-8859-1" + "image/svg+xml" "UTF-8" "text/javascript" "UTF-8" "text/x-clojure" "UTF-8" "application/json" "UTF-8"}) @@ -127,10 +129,35 @@ (server/send-and-close conn 200 (slurp local-path) "text/plain")) ;; "/index.html" doesn't exist, provide our own (= path "/index.html") - (let [{:keys [output-to] :or {output-to "out/main.js"}} copts] + (let [{:keys [output-dir output-to] :or {output-dir "out" output-to "out/main.js"}} copts] + (let [maybe-copy-resource (fn [name] (let [f (io/file output-dir name)] + (when-not (.exists f) + (spit f (slurp (io/resource name))))))] + (maybe-copy-resource "cljs-logo-icon-32.png") + (maybe-copy-resource "cljs-logo.svg")) (server/send-and-close conn 200 - (str "" - "
" + (str "" + "" + "
" + "

Welcome to the default index.html provided by the ClojureScript Browser REPL.

" + "

This page provides the evaluation environment for your Browser REPL and application.

" + "

You can quickly validate the connection by typing (js/alert \"Hello CLJS!\") into the " + "ClojureScript REPL that launched this page.

You can easily use your own HTML file to host your application " + "and REPL by providing your own index.html in the directory that you launched this REPL from.

" + "

Start with this template:

" + "
"
+                 "<!DOCTYPE html>\n"
+                 "<html>\n"
+                 "  <head>\n"
+                 "    <meta charset=\"UTF-8\">\n"
+                 "  </head>\n"
+                 "  <body>\n"
+                 "    <script src=\"" output-to "\" type=\"text/javascript\"></script>\n"
+                 "  </body>\n"
+                 "</html>\n"
+                 "
" + "
" + "
" "" "") "text/html" From 5769f093218bd5455b71d1f1ae9b0eaf30fa5c0f Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 12:52:02 -0500 Subject: [PATCH 2928/4033] clean up svg & png handling a bit --- pom.template.xml | 7 +++ src/main/clojure/cljs/repl/browser.clj | 66 ++++++++++++-------------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index f252d9012..89d6f3154 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -232,6 +232,8 @@ UTF-8 src/main/clojure src/main/cljs + src/main/cljs + resources true @@ -262,6 +264,9 @@ ${cljs.source.dir} + + ${resources.dir} + @@ -341,6 +346,8 @@ **/*.js **/*.map **/*.edn + **/*.svg + **/*.png diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 8963ca94e..a64d8d25a 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -94,6 +94,32 @@ "") "text/html")) +(defn default-index [output-to] + (str "" + "" + "
" + "

Welcome to the default index.html provided by the ClojureScript Browser REPL.

" + "

This page provides the evaluation environment for your Browser REPL and application.

" + "

You can quickly validate the connection by typing (js/alert \"Hello CLJS!\") into the " + "ClojureScript REPL that launched this page.

You can easily use your own HTML file to host your application " + "and REPL by providing your own index.html in the directory that you launched this REPL from.

" + "

Start with this template:

" + "
"
+    "<!DOCTYPE html>\n"
+    "<html>\n"
+    "  <head>\n"
+    "    <meta charset=\"UTF-8\">\n"
+    "  </head>\n"
+    "  <body>\n"
+    "    <script src=\"" output-to "\" type=\"text/javascript\"></script>\n"
+    "  </body>\n"
+    "</html>\n"
+    "
" + "
" + "
" + "" + "")) + (defn send-static [{path :path :as request} conn opts] (if (and (:static-dir opts) (not= "/favicon.ico" path)) @@ -112,6 +138,8 @@ (io/resource (second (string/split path #".jar!/"))) (re-find (Pattern/compile (System/getProperty "user.dir")) path) (io/file (string/replace path (str (System/getProperty "user.dir") "/") "")) + (#{"/cljs-logo-icon-32.png" "/cljs-logo.svg"} path) + (io/resource (subs path 1)) :else nil) local-path) copts (when env/*compiler* (get @env/*compiler* :options))] @@ -129,39 +157,8 @@ (server/send-and-close conn 200 (slurp local-path) "text/plain")) ;; "/index.html" doesn't exist, provide our own (= path "/index.html") - (let [{:keys [output-dir output-to] :or {output-dir "out" output-to "out/main.js"}} copts] - (let [maybe-copy-resource (fn [name] (let [f (io/file output-dir name)] - (when-not (.exists f) - (spit f (slurp (io/resource name))))))] - (maybe-copy-resource "cljs-logo-icon-32.png") - (maybe-copy-resource "cljs-logo.svg")) - (server/send-and-close conn 200 - (str "" - "" - "
" - "

Welcome to the default index.html provided by the ClojureScript Browser REPL.

" - "

This page provides the evaluation environment for your Browser REPL and application.

" - "

You can quickly validate the connection by typing (js/alert \"Hello CLJS!\") into the " - "ClojureScript REPL that launched this page.

You can easily use your own HTML file to host your application " - "and REPL by providing your own index.html in the directory that you launched this REPL from.

" - "

Start with this template:

" - "
"
-                 "<!DOCTYPE html>\n"
-                 "<html>\n"
-                 "  <head>\n"
-                 "    <meta charset=\"UTF-8\">\n"
-                 "  </head>\n"
-                 "  <body>\n"
-                 "    <script src=\"" output-to "\" type=\"text/javascript\"></script>\n"
-                 "  </body>\n"
-                 "</html>\n"
-                 "
" - "
" - "
" - "" - "") - "text/html" - "UTF-8")) + (let [{:keys [output-to] :or {output-to "out/main.js"}} copts] + (server/send-and-close conn 200 (default-index output-to) "text/html" "UTF-8")) (= path "/out/main.js") (do ;; TODO: this could be cleaner if compiling forms resulted in a @@ -175,8 +172,7 @@ "document.write('');\n" "document.write('');\n" "document.write('');\n") - "text/javascript" - "UTF-8")) + "text/javascript" "UTF-8")) :else (server/send-404 conn path))) (server/send-404 conn path))) From 3ee2e2f7c4b3db45388cb4ff27b0daf8034e84dc Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 13:03:39 -0500 Subject: [PATCH 2929/4033] basic styling --- src/main/clojure/cljs/repl/browser.clj | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index a64d8d25a..0f4076f70 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -95,8 +95,17 @@ "text/html")) (defn default-index [output-to] - (str "" - "" + (str + "" + "" + "" + "" + "" + "" + "" "
" "

Welcome to the default index.html provided by the ClojureScript Browser REPL.

" "

This page provides the evaluation environment for your Browser REPL and application.

" From 58c5895c654a1d0895dd2d335899d782e14467ab Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 13:07:35 -0500 Subject: [PATCH 2930/4033] CLJS-2559: Make browser REPL the default --- src/main/cljs/cljs/main.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index a9139ce44..73e2e560e 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -7,7 +7,7 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.main - (:require [cljs.repl.nashorn :as nashorn] + (:require [cljs.repl.browser :as browser] [cljs.cli :as cli]) (:gen-class)) @@ -29,7 +29,7 @@ (throw (ex-info (str "REPL namespace " repl-ns " does not exist") {:repl-ns repl-ns}))))) - nashorn/repl-env)) + browser/repl-env)) (defn- normalize* [args] (if (not (cli/dispatch? cli/default-commands :main (first args))) From 6a45b2b218312bcc1cd8bda0400b67398acb446d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 26 Feb 2018 08:41:25 -0500 Subject: [PATCH 2931/4033] CLJS-2557: cljs.main: Browser REPL doesn't exit quickly --- src/main/clojure/cljs/repl/browser.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 0f4076f70..8de3446ac 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -332,7 +332,8 @@ (-tear-down [this] (binding [server/state (:server-state this)] (server/stop)) - (.shutdown (:es this))) + (.shutdown (:es this)) + (shutdown-agents)) repl/IReplEnvOptions (-repl-options [this] {:browser-repl true From 58bed1fde019bb9150cd3e70d60f03f68afec407 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 13:41:11 -0500 Subject: [PATCH 2932/4033] move styling stuff into app div --- src/main/clojure/cljs/repl/browser.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 8de3446ac..9e12fecf2 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -100,13 +100,14 @@ "" "" "" + "" + "" + "
" "" "" - "" - "
" "

Welcome to the default index.html provided by the ClojureScript Browser REPL.

" "

This page provides the evaluation environment for your Browser REPL and application.

" "

You can quickly validate the connection by typing (js/alert \"Hello CLJS!\") into the " From 5d3003ec13beb94c8fda0f9b0fa1e68cfa94c931 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 14:22:03 -0500 Subject: [PATCH 2933/4033] CLJS-2561: AOT compile browser REPL client js --- project.clj | 2 +- src/main/clojure/cljs/closure.clj | 38 +++++++++++++++++++++++++- src/main/clojure/cljs/repl/browser.clj | 28 ++----------------- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/project.clj b/project.clj index fa560cada..bcef10d9a 100644 --- a/project.clj +++ b/project.clj @@ -6,7 +6,7 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :jvm-opts ^:replace ["-Dclojure.compiler.direct-linking=true" "-Xmx512m" "-server"] :source-paths ["src/main/clojure" "src/main/cljs"] - :resource-paths ["src/main/cljs"] + :resource-paths ["src/main/cljs" "resources"] :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.10.0-alpha4"] [org.clojure/spec.alpha "0.1.143"] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2a3f8521c..53bb01c04 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2994,6 +2994,35 @@ goog-ns (str goog-ns)))))) +;; Browser REPL client stuff + +(defn compile-client-js [opts] + (let [copts {:optimizations (:optimizations opts) + :output-dir (:working-dir opts)}] + ;; we're inside the REPL process where cljs.env/*compiler* is already + ;; established, need to construct a new one to avoid mutating the one + ;; the REPL uses + (build + '[(ns clojure.browser.repl.client + (:require [goog.events :as event] + [clojure.browser.repl :as repl])) + (defn start [url] + (event/listen js/window + "load" + (fn [] + (repl/start-evaluator url))))] + copts (env/default-compiler-env copts)))) + +(defn create-client-js-file [opts file-path] + (if-let [cached (io/resource "brepl_client.js")] + cached + (let [file (io/file file-path)] + (when (not (.exists file)) + (spit file (compile-client-js opts))) + file))) + +;; AOTed resources + (defn aot-cache-core [] (let [base-path (io/file "src" "main" "cljs" "cljs") src (io/file base-path "core.cljs") @@ -3007,7 +3036,14 @@ :source-map-url "core.js.map" :output-dir (str "src" File/separator "main" File/separator "cljs")}) (ana/write-analysis-cache 'cljs.core cache src) - (ana/write-analysis-cache 'cljs.core tcache src)))) + (ana/write-analysis-cache 'cljs.core tcache src)) + (create-client-js-file + {:optimizations :whitespace + :working-dir "aot_out"} + (io/file "resources" "brepl_client.js")) + (doseq [f (file-seq (io/file "aot_out")) + :when (.isFile f)] + (.delete f)))) (comment (time diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 9e12fecf2..907d7dfeb 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -274,33 +274,9 @@ [repl-env provides url] (browser-eval (slurp url))) - ;; ============================================================================= ;; BrowserEnv -(defn compile-client-js [opts] - (let [copts {:optimizations (:optimizations opts) - :output-dir (:working-dir opts)}] - ;; we're inside the REPL process where cljs.env/*compiler* is already - ;; established, need to construct a new one to avoid mutating the one - ;; the REPL uses - (cljsc/build - '[(ns clojure.browser.repl.client - (:require [goog.events :as event] - [clojure.browser.repl :as repl])) - (defn start [url] - (event/listen js/window - "load" - (fn [] - (repl/start-evaluator url))))] - copts (env/default-compiler-env copts)))) - -(defn create-client-js-file [opts file-path] - (let [file (io/file file-path)] - (when (not (.exists file)) - (spit file (compile-client-js opts))) - file)) - (defn setup [{:keys [working-dir] :as repl-env} opts] (binding [browser-state (:browser-state repl-env) ordering (:ordering repl-env) @@ -310,8 +286,8 @@ (swap! browser-state (fn [old] (assoc old :client-js - (create-client-js-file - repl-env (io/file working-dir "client.js"))))) + (cljsc/create-client-js-file + repl-env (io/file working-dir "brepl_client.js"))))) (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) From 151edf6fb7984d0d26ce65c12d6f5644c40c1063 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 26 Feb 2018 14:20:13 -0500 Subject: [PATCH 2934/4033] CLJS-2562: shutdown-agents in browser REPL tear-down is too aggressive --- src/main/cljs/cljs/main.clj | 3 ++- src/main/clojure/cljs/repl/browser.clj | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index 73e2e560e..88845c3c2 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -57,4 +57,5 @@ args) [js-args args] ((juxt #(take 2 %) #(drop 2 %)) post) repl-opt (get-js-opt js-args)] - (apply cli/main repl-opt (concat pre args)))) + (apply cli/main repl-opt (concat pre args)) + (shutdown-agents))) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 907d7dfeb..e371128da 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -309,8 +309,7 @@ (-tear-down [this] (binding [server/state (:server-state this)] (server/stop)) - (.shutdown (:es this)) - (shutdown-agents)) + (.shutdown (:es this))) repl/IReplEnvOptions (-repl-options [this] {:browser-repl true From 1be96c0589dcb4870830f5f09c02210b0a0a31ee Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 15:19:51 -0500 Subject: [PATCH 2935/4033] CLJS-2556: browser REPL failing on Windows (unsupported escape sequence) --- src/main/clojure/cljs/repl/browser.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index e371128da..d5383acb4 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -146,7 +146,7 @@ (cond (re-find #".jar" path) (io/resource (second (string/split path #".jar!/"))) - (re-find (Pattern/compile (System/getProperty "user.dir")) path) + (string/includes? path (System/getProperty "user.dir")) (io/file (string/replace path (str (System/getProperty "user.dir") "/") "")) (#{"/cljs-logo-icon-32.png" "/cljs-logo.svg"} path) (io/resource (subs path 1)) From ccdd935a07064a15e09f0b83d9e6cbd4d9a3a282 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 16:54:58 -0500 Subject: [PATCH 2936/4033] allow setting browser host/post via cljs.main --- src/main/clojure/cljs/cli.clj | 6 +++--- src/main/clojure/cljs/repl/browser.clj | 22 +++++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index adc6883b2..1b8d3a4c9 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -191,8 +191,8 @@ classpath. Classpath-relative paths have prefix of @ or @/") (defn- repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" - [repl-env [_ & args] {:keys [options inits] :as cfg}] - (let [renv (repl-env) + [repl-env [_ & args] {:keys [repl-env-options options inits] :as cfg}] + (let [renv (apply repl-env (mapcat identity repl-env-options)) opts (merge (repl/repl-options renv) options)] (repl/repl* renv (assoc opts @@ -415,7 +415,7 @@ present" (cond (= pre args) pre - (contains? (get-options commands :all) (fnext post)) + (not (#{"true" "false"} (fnext post))) (concat pre [(first post) "true"] (normalize commands (next post))) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index d5383acb4..4087cad2b 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -314,7 +314,18 @@ (-repl-options [this] {:browser-repl true :repl-requires - '[[clojure.browser.repl]]}) + '[[clojure.browser.repl]] + :cljs.cli/commands + {:groups {::repl {:desc "browser REPL options"}} + :init + {["-H" "--host"] + {:group ::repl :fn #(assoc-in %1 [:repl-env-options ::host] %2) + :arg "address" + :doc "Address to bind"} + ["-p" "--port"] + {:group ::repl :fn #(assoc-in %1 [:repl-env-options ::port] (Integer/parseInt %2)) + :arg "number" + :doc "Port to bind"}}}}) repl/IParseStacktrace (-parse-stacktrace [this st err opts] (st/parse-stacktrace this st err opts)) @@ -329,16 +340,15 @@ :stacktrace (.-stack ~e)})))))) (defn repl-env* - [{:keys [output-dir] :as opts}] + [{:keys [output-dir ::host ::port] :or {host "localhost" port 9000} :as opts}] (merge (BrowserEnv.) - {:host "localhost" - :port 9000 + {:host host + :port port :working-dir (->> [".repl" (util/clojurescript-version)] (remove empty?) (string/join "-")) :serve-static true :static-dir (cond-> ["." "out/"] output-dir (conj output-dir)) :preloaded-libs [] - :optimizations :simple :src "src/" :browser-state (atom {:return-value-fn nil :client-js nil}) @@ -364,8 +374,6 @@ Defaults to true. static-dir: List of directories to search for static content. Defaults to [\".\" \"out/\"]. - optimizations: The level of optimization to use when compiling the client - end of the REPL. Defaults to :simple. src: The source directory containing user-defined cljs files. Used to support reflection. Defaults to \"src/\". " From ae34de7c316461b3dbce49bbc4e5ab2ae4d9bde9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 18:11:06 -0500 Subject: [PATCH 2937/4033] allow browser REPL host/port to be configurable --- src/main/cljs/clojure/browser/repl.cljs | 3 ++ .../cljs/clojure/browser/repl/preload.cljs | 2 +- src/main/clojure/cljs/closure.clj | 20 +++++----- src/main/clojure/cljs/repl/browser.clj | 38 ++++++++++--------- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 6fd45120b..9e87a09f7 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -27,6 +27,9 @@ ;; with clojure.browser.repl: [cljs.repl])) +(goog-define HOST "localhost") +(goog-define PORT 9000) + (def xpc-connection (atom nil)) (def print-queue (array)) diff --git a/src/main/cljs/clojure/browser/repl/preload.cljs b/src/main/cljs/clojure/browser/repl/preload.cljs index cd7435647..dcaa25ebc 100644 --- a/src/main/cljs/clojure/browser/repl/preload.cljs +++ b/src/main/cljs/clojure/browser/repl/preload.cljs @@ -10,6 +10,6 @@ (:require [clojure.browser.repl :as repl])) (defonce conn - (repl/connect "http://localhost:9000/repl")) + (repl/connect (str "http://" repl/HOST ":" repl/PORT "/repl"))) (enable-console-print!) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 53bb01c04..7f181192a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2168,6 +2168,12 @@ (true? process-shim) (not (false? process-shim)))) +(defn normalize-closure-defines [defines] + (into {} + (map (fn [[k v]] + [(if (symbol? k) (str (comp/munge k)) k) v]) + defines))) + (defn add-implicit-options [{:keys [optimizations output-dir] :or {optimizations :none @@ -2181,12 +2187,7 @@ (update-in [:closure-defines 'process.env/NODE_ENV] (fnil str "production")))) (or (:closure-defines opts) (shim-process? opts)) - (update :closure-defines - (fn [defines] - (into {} - (map (fn [[k v]] - [(if (symbol? k) (str (comp/munge k)) k) v]) - defines)))) + (update :closure-defines normalize-closure-defines) (:browser-repl opts) (update-in [:preloads] (fnil conj []) 'clojure.browser.repl.preload)) {:keys [libs foreign-libs externs]} (get-upstream-deps) @@ -2997,8 +2998,7 @@ ;; Browser REPL client stuff (defn compile-client-js [opts] - (let [copts {:optimizations (:optimizations opts) - :output-dir (:working-dir opts)}] + (let [copts (select-keys opts [:optimizations :output-dir])] ;; we're inside the REPL process where cljs.env/*compiler* is already ;; established, need to construct a new one to avoid mutating the one ;; the REPL uses @@ -3038,8 +3038,8 @@ (ana/write-analysis-cache 'cljs.core cache src) (ana/write-analysis-cache 'cljs.core tcache src)) (create-client-js-file - {:optimizations :whitespace - :working-dir "aot_out"} + {:optimizations :simple + :output-dir "aot_out"} (io/file "resources" "brepl_client.js")) (doseq [f (file-seq (io/file "aot_out")) :when (.isFile f)] diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 4087cad2b..ce1827702 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -12,6 +12,7 @@ [clojure.java.browse :as browse] [clojure.string :as string] [clojure.edn :as edn] + [clojure.data.json :as json] [cljs.util :as util] [cljs.env :as env] [cljs.closure :as cljsc] @@ -21,8 +22,7 @@ [cljs.stacktrace :as st] [cljs.analyzer :as ana] [cljs.build.api :as build]) - (:import [java.util.regex Pattern] - [java.util.concurrent Executors])) + (:import [java.util.concurrent Executors])) (def ^:dynamic browser-state nil) (def ^:dynamic ordering nil) @@ -130,14 +130,12 @@ "" "")) -(defn send-static [{path :path :as request} conn opts] - (if (and (:static-dir opts) - (not= "/favicon.ico" path)) - (let [path (if (= "/" path) "/index.html" path) - st-dir (:static-dir opts) +(defn send-static [{path :path :as request} conn {:keys [static-dir host port] :as opts}] + (if (and static-dir (not= "/favicon.ico" path)) + (let [path (if (= "/" path) "/index.html" path) local-path (cond-> - (seq (for [x (if (string? st-dir) [st-dir] st-dir) + (seq (for [x (if (string? static-dir) [static-dir] static-dir) :when (.exists (io/file (str x path)))] (str x path))) (complement nil?) first) @@ -170,15 +168,19 @@ (let [{:keys [output-to] :or {output-to "out/main.js"}} copts] (server/send-and-close conn 200 (default-index output-to) "text/html" "UTF-8")) (= path "/out/main.js") - (do + (let [closure-defines (-> `{clojure.browser.repl/HOST ~host + clojure.browser.repl/PORT ~port} + cljsc/normalize-closure-defines + json/write-str)] ;; TODO: this could be cleaner if compiling forms resulted in a ;; :output-to file with the result of compiling those forms - David - (env/with-compiler-env (env/default-compiler-env) + (spit (io/file "out/cljs_deps.js") (build/build - '[(require '[clojure.browser.repl.preload])] - {:output-to "out/cljs_deps.js"})) + '[(require '[clojure.browser.repl.preload])] {:optimizations :none})) (server/send-and-close conn 200 - (str "document.write('');\n" + (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "var CLOSURE_NO_DEPS = true;\n" + "document.write('');\n" "document.write('');\n" "document.write('');\n" "document.write('');\n") @@ -287,7 +289,9 @@ (fn [old] (assoc old :client-js (cljsc/create-client-js-file - repl-env (io/file working-dir "brepl_client.js"))))) + {:optimizations :simple + :output-dir working-dir} + (io/file working-dir "brepl_client.js"))))) (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) @@ -319,11 +323,11 @@ {:groups {::repl {:desc "browser REPL options"}} :init {["-H" "--host"] - {:group ::repl :fn #(assoc-in %1 [:repl-env-options ::host] %2) + {:group ::repl :fn #(assoc-in %1 [:repl-env-options :host] %2) :arg "address" :doc "Address to bind"} ["-p" "--port"] - {:group ::repl :fn #(assoc-in %1 [:repl-env-options ::port] (Integer/parseInt %2)) + {:group ::repl :fn #(assoc-in %1 [:repl-env-options :port] (Integer/parseInt %2)) :arg "number" :doc "Port to bind"}}}}) repl/IParseStacktrace @@ -340,7 +344,7 @@ :stacktrace (.-stack ~e)})))))) (defn repl-env* - [{:keys [output-dir ::host ::port] :or {host "localhost" port 9000} :as opts}] + [{:keys [output-dir host port] :or {host "localhost" port 9000} :as opts}] (merge (BrowserEnv.) {:host host :port port From fcef30496bfc06376447aefd1d12a79363cbd9da Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 18:14:31 -0500 Subject: [PATCH 2938/4033] allow direct setting of REPL env & build config via edn --- src/main/clojure/cljs/cli.clj | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 1b8d3a4c9..f652a3af3 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -159,6 +159,15 @@ classpath. Classpath-relative paths have prefix of @ or @/") (let [target (if (= "node" target) "nodejs" target)] (assoc-in cfg [:options :target] (keyword target)))) +(defn- repl-env-opts-opt + [cfg ropts] + (update cfg :repl-env-options merge (edn/read-string ropts))) + +(defn- compile-opts-opt + [cfg copts] + (update cfg :options merge (edn/read-string copts))) + + (defn- init-opt [cfg file] (update-in cfg [:inits] @@ -385,13 +394,18 @@ present" (str "Set optimization level, only effective with " "-c main option. Valid values are: none, " "whitespace, simple, advanced")} - ["-t" "--target"] {:group ::main&compile :fn target-opt :arg "name" :doc (str "The JavaScript target. Configures environment bootstrap and " "defaults to browser. Supported values: nodejs, nashorn, " - "webworker, none") }} + "webworker, none") } + ["-rf" "--repl-opts"] {:group ::main&compile :fn repl-env-opts-opt + :arg "edn" + :doc (str "Options to configure the repl-env")} + ["-cf" "--compile-opts"] {:group ::main&compile :fn compile-opts-opt + :arg "edn" + :doc (str "Options to configure the build")}} :main {["-r" "--repl"] {:fn repl-opt :doc "Run a repl"} From f582f26e47a22c0d7247c5e726384dde07d395eb Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 18:32:16 -0500 Subject: [PATCH 2939/4033] add some basic error handling --- src/main/clojure/cljs/cli.clj | 47 +++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index f652a3af3..47a127608 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -248,13 +248,50 @@ present" `(~'ns ~'cljs.user)) (repl/run-inits renv inits) (when script - (if (= "-" script) + (cond + (= "-" script) (repl/load-stream renv "" *in*) - (repl/load-file renv script))) + + (.exists (io/file script)) + (repl/load-file renv script) + + (string/starts-with? script "@") + (if-let [rsrc (io/resource (subs script 1))] + (repl/load-file renv rsrc) + (throw + (ex-info + (str "Resource script " (subs script 1) " does not exist") + {:cljs.main/error :invalid-arg}))) + + (string/starts-with? script "@/") + (if-let [rsrc (io/resource (subs script 2))] + (repl/load-file renv rsrc) + (throw + (ex-info + (str "Resource script " (subs script 2) " does not exist") + {:cljs.main/error :invalid-arg}))) + + (string/starts-with? script "-") + (throw + (ex-info + (str "Expected script or -, got flag " script " instead") + {:cljs.main/error :invalid-arg})) + + :else + (throw + (ex-info + (str "Script " script " does not exist") + {:cljs.main/error :invalid-arg})))) (when main - (repl/load-file renv (build/ns->source main)) - (repl/evaluate-form renv (ana-api/empty-env) "" - `(~(symbol (name main) "-main") ~@args))) + (let [src (build/ns->source main)] + (when-not (io/resource src) + (throw + (ex-info + (str "Namespace " main " does not exist") + {:cljs.main/error :invalid-arg}))) + (repl/load-file renv src) + (repl/evaluate-form renv (ana-api/empty-env) "" + `(~(symbol (name main) "-main") ~@args)))) (repl/tear-down renv))))))) (defn- main-opt From 58fce56187b76962289398bc917787f586c33b30 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 18:37:35 -0500 Subject: [PATCH 2940/4033] validate the watch option --- src/main/clojure/cljs/cli.clj | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 47a127608..4751ff72d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -144,6 +144,17 @@ classpath. Classpath-relative paths have prefix of @ or @/") (defn- watch-opt [cfg path] + (when-not (.exists (io/file path)) + (if (or (string/starts-with? path "-") + (string/blank? path)) + (throw + (ex-info + (str "Missing watch path") + {:cljs.main/error :invalid-arg})) + (throw + (ex-info + (str "Watch path " path " does not exist") + {:cljs.main/error :invalid-arg})))) (assoc-in cfg [:options :watch] path)) (defn- optimize-opt From b76deb781f6b48b285242fcc5d3bf4b6b1394686 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 18:47:57 -0500 Subject: [PATCH 2941/4033] CLJS-2167: Browser REPL leaves a socket open when it fails to connect to the browser --- src/main/clojure/cljs/repl.cljc | 166 ++++++++++++------------- src/main/clojure/cljs/repl/browser.clj | 2 +- src/main/clojure/cljs/repl/server.clj | 3 +- 3 files changed, 85 insertions(+), 86 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 3ec1bf4cc..6a2db950f 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -882,99 +882,97 @@ ana/*cljs-static-fns* static-fns ana/*fn-invoke-direct* (and static-fns fn-invoke-direct) *repl-opts* opts] - (let [env {:context :expr :locals {}} - special-fns (merge default-special-fns special-fns) - is-special-fn? (set (keys special-fns)) - request-prompt (Object.) - request-exit (Object.) - opts (comp/with-core-cljs opts - (fn [] - (try + (try + (let [env {:context :expr :locals {}} + special-fns (merge default-special-fns special-fns) + is-special-fn? (set (keys special-fns)) + request-prompt (Object.) + request-exit (Object.) + opts (comp/with-core-cljs opts + (fn [] (if-let [merge-opts (:merge-opts (-setup repl-env opts))] (merge opts merge-opts) - opts) - (catch Throwable e - (caught e repl-env opts) - opts)))) - init (do - (evaluate-form repl-env env "" - `(~'set! ~'cljs.core/*print-namespace-maps* true) - identity opts) - (or init + opts))) + init (do + (evaluate-form repl-env env "" + `(~'set! ~'cljs.core/*print-namespace-maps* true) + identity opts) + (or init #(evaluate-form repl-env env "" (with-meta `(~'ns ~'cljs.user (:require ~@repl-requires)) {:line 1 :column 1}) identity opts))) - read-eval-print + read-eval-print + (fn [] + (let [input (binding [*ns* (create-ns ana/*cljs-ns*) + reader/resolve-symbol ana/resolve-symbol + reader/*data-readers* tags/*cljs-data-readers* + reader/*alias-map* + (apply merge + ((juxt :requires :require-macros) + (ana/get-namespace ana/*cljs-ns*)))] + (read request-prompt request-exit))] + (or ({request-exit request-exit + :cljs/quit request-exit + request-prompt request-prompt} input) + (if (and (seq? input) (is-special-fn? (first input))) + (do + ((get special-fns (first input)) repl-env env input opts) + (print nil)) + (let [value (eval repl-env env input opts)] + (print value))))))] + (maybe-install-npm-deps opts) + (comp/with-core-cljs opts (fn [] - (let [input (binding [*ns* (create-ns ana/*cljs-ns*) - reader/resolve-symbol ana/resolve-symbol - reader/*data-readers* tags/*cljs-data-readers* - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros) - (ana/get-namespace ana/*cljs-ns*)))] - (read request-prompt request-exit))] - (or ({request-exit request-exit - :cljs/quit request-exit - request-prompt request-prompt} input) - (if (and (seq? input) (is-special-fn? (first input))) - (do - ((get special-fns (first input)) repl-env env input opts) - (print nil)) - (let [value (eval repl-env env input opts)] - (print value))))))] - (maybe-install-npm-deps opts) - (comp/with-core-cljs opts - (fn [] - (binding [*repl-opts* opts] - (try - (when analyze-path - (if (vector? analyze-path) - (run! #(analyze-source % opts) analyze-path) - (analyze-source analyze-path opts))) - (init) - (run-inits repl-env inits) - (catch Throwable e - (caught e repl-env opts))) - (when-let [src (:watch opts)] - (.start - (Thread. - ((ns-resolve 'clojure.core 'binding-conveyor-fn) - (fn [] - (let [log-file (io/file (util/output-directory opts) "watch.log")] - (err-out (println "Watch compilation log available at:" (str log-file))) - (try - (let [log-out (FileWriter. log-file)] - (binding [*err* log-out - *out* log-out] - (cljsc/watch src (dissoc opts :watch) - env/*compiler* done?))) - (catch Throwable e - (caught e repl-env opts))))))))) - ;; let any setup async messages flush - (Thread/sleep 50) - (binding [*in* (if (true? (:source-map-inline opts)) - *in* - (reader))] - (quit-prompt) - (prompt) - (flush) - (loop [] - (when-not - (try - (identical? (read-eval-print) request-exit) - (catch Throwable e - (caught e repl-env opts) - nil)) - (when (need-prompt) - (prompt) - (flush)) - (recur)))))))) - (reset! done? true) - (-tear-down repl-env))))) + (binding [*repl-opts* opts] + (try + (when analyze-path + (if (vector? analyze-path) + (run! #(analyze-source % opts) analyze-path) + (analyze-source analyze-path opts))) + (init) + (run-inits repl-env inits) + (catch Throwable e + (caught e repl-env opts))) + (when-let [src (:watch opts)] + (.start + (Thread. + ((ns-resolve 'clojure.core 'binding-conveyor-fn) + (fn [] + (let [log-file (io/file (util/output-directory opts) "watch.log")] + (err-out (println "Watch compilation log available at:" (str log-file))) + (try + (let [log-out (FileWriter. log-file)] + (binding [*err* log-out + *out* log-out] + (cljsc/watch src (dissoc opts :watch) + env/*compiler* done?))) + (catch Throwable e + (caught e repl-env opts))))))))) + ;; let any setup async messages flush + (Thread/sleep 50) + (binding [*in* (if (true? (:source-map-inline opts)) + *in* + (reader))] + (quit-prompt) + (prompt) + (flush) + (loop [] + (when-not + (try + (identical? (read-eval-print) request-exit) + (catch Throwable e + (caught e repl-env opts) + nil)) + (when (need-prompt) + (prompt) + (flush)) + (recur)))))))) + (finally + (reset! done? true) + (-tear-down repl-env))))))) (defn repl "Generic, reusable, read-eval-print loop. By default, reads from *in* using diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index ce1827702..7d06e96ae 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -313,7 +313,7 @@ (-tear-down [this] (binding [server/state (:server-state this)] (server/stop)) - (.shutdown (:es this))) + (.shutdownNow (:es this))) repl/IReplEnvOptions (-repl-options [this] {:browser-repl true diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index 343c52549..9f3a85d1c 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -205,4 +205,5 @@ (swap! state (fn [old] (assoc old :socket ss :port (:port opts)))))) (defn stop [] - (.close (:socket @state))) + (when-let [sock (:socket @state)] + (.close sock))) From 835a11767616bc463312d5ca61ac7822022ae99e Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 18:49:57 -0500 Subject: [PATCH 2942/4033] CLJS-2565: cljs.main: doc output for entering namespace --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 4751ff72d..9f1669268 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -34,7 +34,7 @@ With no options or args, runs an interactive Read-Eval-Print Loop %s For --main and --repl: - - Enters the user namespace + - Enters the cljs.user namespace - Binds *command-line-args* to a seq of strings containing command line args that appear after any main option - Runs all init options in order From 435af1a9ee69e225676fe743556d60efff5bde37 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 20:00:38 -0500 Subject: [PATCH 2943/4033] don't build unless first time --- src/main/clojure/cljs/repl/browser.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 7d06e96ae..ad9df2296 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -174,9 +174,11 @@ json/write-str)] ;; TODO: this could be cleaner if compiling forms resulted in a ;; :output-to file with the result of compiling those forms - David - (spit (io/file "out/cljs_deps.js") - (build/build - '[(require '[clojure.browser.repl.preload])] {:optimizations :none})) + (let [f (io/file "out/cljs_deps.js")] + (when-not (.exists f) + (spit f + (build/build + '[(require '[clojure.browser.repl.preload])] {:optimizations :none})))) (server/send-and-close conn 200 (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "var CLOSURE_NO_DEPS = true;\n" From 267cc9fa72b8f342c307fe64545978286a2d4294 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 26 Feb 2018 22:45:19 -0500 Subject: [PATCH 2944/4033] increase timeout to something a bit more reasonable --- src/main/cljs/clojure/browser/repl.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 9e87a09f7..19764bae5 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -120,7 +120,7 @@ (net/connect repl-connection (constantly nil)) - (js/setTimeout #(send-result connection url (wrap-message :ready "ready")) 50)) + (js/setTimeout #(send-result connection url (wrap-message :ready "ready")) 1000)) (js/alert "No 'xpc' param provided to child iframe."))) (def load-queue nil) From 4e2ce8311406ae0aa4f40e39704989514ea71e75 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Feb 2018 00:26:15 -0500 Subject: [PATCH 2945/4033] fix browser REPL concurrency bug, it's not safe to build on the static file serving threads only build the default stuff if output-dir is present and it doesn't already exist on disk --- src/main/clojure/cljs/repl/browser.clj | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index ad9df2296..09973296f 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -172,13 +172,6 @@ clojure.browser.repl/PORT ~port} cljsc/normalize-closure-defines json/write-str)] - ;; TODO: this could be cleaner if compiling forms resulted in a - ;; :output-to file with the result of compiling those forms - David - (let [f (io/file "out/cljs_deps.js")] - (when-not (.exists f) - (spit f - (build/build - '[(require '[clojure.browser.repl.preload])] {:optimizations :none})))) (server/send-and-close conn 200 (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "var CLOSURE_NO_DEPS = true;\n" @@ -281,7 +274,7 @@ ;; ============================================================================= ;; BrowserEnv -(defn setup [{:keys [working-dir] :as repl-env} opts] +(defn setup [{:keys [working-dir] :as repl-env} {:keys [output-dir] :as opts}] (binding [browser-state (:browser-state repl-env) ordering (:ordering repl-env) es (:es repl-env) @@ -294,6 +287,12 @@ {:optimizations :simple :output-dir working-dir} (io/file working-dir "brepl_client.js"))))) + ;; TODO: this could be cleaner if compiling forms resulted in a + ;; :output-to file with the result of compiling those forms - David + (when (and output-dir (not (.exists (io/file output-dir)))) + (spit (io/file "out/cljs_deps.js") + (build/build + '[(require '[clojure.browser.repl.preload])] {:optimizations :none}))) (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) From 8b21c5f0a8c79645d75f45cb4020fb82e1ef5bb8 Mon Sep 17 00:00:00 2001 From: Tim Pote Date: Mon, 26 Feb 2018 21:43:54 -0600 Subject: [PATCH 2946/4033] Add handshake for the inner/outer xpc connections of the browser repl Prior to this commit, the iframe connection just hopes that the roundtrip to the server plus 50ms is enough for the outer xpc connection to get established. This is the reason the browser repl occasionally fails to connect. This commit adds a handshake to that interaction, ensuring that both connections are established prior to sending any work to the outer. Note that it waits for the handshake to complete prior to sending the :ready message to the server. This is slower than sending to both the server and the outer simultaneously, then forcing the inner to wait for an ack from the outer. However, the code is significantly simpler this way. We can revisit if it's an issue. --- .gitignore | 1 + src/main/cljs/clojure/browser/repl.cljs | 46 ++++++++++++++++++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index a2e53453e..f5c766238 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ closure *out .lein* /pom.xml +*.iml .repl* *.swp *.zip diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 19764bae5..3c1214399 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -97,7 +97,31 @@ "Start the REPL server connection." [url] (if-let [repl-connection (net/xpc-connection)] - (let [connection (net/xhr-connection)] + (let [connection (net/xhr-connection) + repl-connected? (atom false) + try-handshake (fn try-handshake [] + (when-not @repl-connected? + (net/transmit repl-connection + :start-handshake + nil) + ;; In case we miss, try again. Parent will only + ;; ack once. + (js/setTimeout try-handshake + 10)))] + (net/connect repl-connection + try-handshake) + + (net/register-service repl-connection + :ack-handshake + (fn [_] + (when-not @repl-connected? + (reset! repl-connected? true) + ;; Now that we're connected to the parent, we can start talking to + ;; the server. + (send-result connection + url + (wrap-message :ready "ready"))))) + (event/listen connection :success (fn [e] @@ -115,12 +139,7 @@ (net/register-service repl-connection :print (fn [data] - (send-print url (wrap-message :print data)))) - - (net/connect repl-connection - (constantly nil)) - - (js/setTimeout #(send-result connection url (wrap-message :ready "ready")) 1000)) + (send-print url (wrap-message :print data))))) (js/alert "No 'xpc' param provided to child iframe."))) (def load-queue nil) @@ -189,10 +208,21 @@ connection is made, the REPL will evaluate forms in the context of the document that called this function." [repl-server-url] - (let [repl-connection + (let [connected? (atom false) + repl-connection (net/xpc-connection {:peer_uri repl-server-url})] (swap! xpc-connection (constantly repl-connection)) + (net/register-service repl-connection + :start-handshake + (fn [_] + ;; Child will keep retrying, but we only want + ;; to ack once. + (when-not @connected? + (reset! connected? true) + (net/transmit repl-connection + :ack-handshake + nil)))) (net/register-service repl-connection :evaluate-javascript (fn [js] From 4003d238e2cf27c7e73be7e62f1a12d80b003a8a Mon Sep 17 00:00:00 2001 From: Andrea Richiardi Date: Tue, 27 Feb 2018 13:24:21 -0800 Subject: [PATCH 2947/4033] CLJS-2580: Cannot use package with directory main package.json entry This solves the problem with the package "pg" (node-postgres) that legitimately uses a directory as main entry in package.json. --- src/main/cljs/cljs/module_deps.js | 2 +- src/main/clojure/cljs/closure.clj | 2 +- src/test/clojure/cljs/closure_tests.clj | 32 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index 19b6cf560..0e62378a2 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -180,7 +180,7 @@ md.on('end', function () { let pkgJson = pkgJsons[i]; const candidates = /\.js(on)?$/.test(pkgJson.mainEntry) ? [pkgJson.mainEntry] - : [pkgJson.mainEntry, pkgJson.mainEntry + '.js', pkgJson.mainEntry + '.json']; + : [pkgJson.mainEntry, pkgJson.mainEntry + '.js', pkgJson.mainEntry + '/index.js', pkgJson.mainEntry + '.json']; for (let j = 0; j < candidates.length; j++) { const candidate = candidates[j]; diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 7f181192a..7bb4bf186 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2417,7 +2417,7 @@ name)) (cond-> [main-path] (nil? (re-find #"\.js(on)?$" main-path)) - (into [(str main-path ".js") (str main-path ".json")])))))) + (into [(str main-path ".js") (str main-path "/index.js") (str main-path ".json")])))))) pkg-jsons)] {:provides (let [module-rel-name (-> (subs path (.lastIndexOf path "node_modules")) (string/replace #"\\" "/") diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 71b120b94..2d75a9fd8 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -410,3 +410,35 @@ (.delete (io/file "package.json")) (test/delete-node-modules) (test/delete-out-files out))) + +(deftest test-cljs-2580 + (spit (io/file "package.json") "{}") + (let [opts {:npm-deps {"pg" "7.4.1" + "pg-native" "2.2.0"} + :target :nodejs} + out (util/output-directory opts)] + (test/delete-node-modules) + (test/delete-out-files out) + (closure/maybe-install-node-deps! opts) + (let [modules (closure/index-node-modules-dir)] + (is (true? (some (fn [module] + (= module + {:file (.getAbsolutePath (io/file "node_modules/pg/lib/index.js")) + :module-type :es6 + :provides ["pg/lib/index.js" + "pg/lib/index" + "pg" + "pg/lib"]})) + modules)))) + (let [modules (closure/index-node-modules ["pg"] opts)] + (is (true? (some (fn [module] + (= module {:module-type :es6 + :file (.getAbsolutePath (io/file "node_modules/pg/lib/index.js")) + :provides ["pg" + "pg/lib/index.js" + "pg/lib/index" + "pg/lib"]})) + modules)))) + (.delete (io/file "package.json")) + (test/delete-node-modules) + (test/delete-out-files out))) From 37916756858d5f1ffc8db0264a6858b3995a4e83 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Feb 2018 18:09:39 -0500 Subject: [PATCH 2948/4033] CLJS-2564: cljs.main: -re docstring should specify parameter --- src/main/clojure/cljs/cli.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 9f1669268..f9412fd61 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -409,9 +409,10 @@ present" {:groups {::main&compile {:desc "init option" :pseudos {["-re" "--repl-env"] - {:doc (str "The REPL environment to use. Built-in " - "supported values: nashorn, node, browser, " - "rhino. Defaults to nashorn")}}} + {:arg "env" + :doc (str "The REPL environment to use. Built-in " + "supported values: nashorn, node, browser, " + "rhino. Defaults to browser")}}} ::main {:desc "init options only for --main and --repl"} ::compile {:desc "init options only for --compile"}} :init From e8d4a75d24bdb1343eeee1eda7e03ffdadefc814 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Feb 2018 18:33:55 -0500 Subject: [PATCH 2949/4033] CLJS-2567: cljs.main: Classpath relative path for -i Change default main which uses the REPL to call load-stream instead of load file to avoid compilation artifacts. --- src/main/clojure/cljs/cli.clj | 48 ++++++++++++++++++++++++--------- src/main/clojure/cljs/repl.cljc | 3 ++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index f9412fd61..ea29d2333 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -181,10 +181,32 @@ classpath. Classpath-relative paths have prefix of @ or @/") (defn- init-opt [cfg file] - (update-in cfg [:inits] - (fnil conj []) - {:type :init-script - :script file})) + (let [file' (cond + (string/starts-with? file "@/") + (io/resource (subs file 2)) + (string/starts-with? file "@") + (io/resource (subs file 1)) + :else + (let [f (io/file file)] + (if (.exists f) + f + (throw + (ex-info + (str "File " file " does not exist") + {:cljs.main/error :invalid-arg})))))] + (when-not file' + (throw + (ex-info + (str "Resource " + (if (string/starts-with? file "@/") + (subs file 2) + (subs file 1)) + " does not exist") + {:cljs.main/error :invalid-arg}))) + (update-in cfg [:inits] + (fnil conj []) + {:type :init-script + :script file'}))) (defn- eval-opt [cfg form-str] @@ -266,20 +288,20 @@ present" (.exists (io/file script)) (repl/load-file renv script) - (string/starts-with? script "@") - (if-let [rsrc (io/resource (subs script 1))] - (repl/load-file renv rsrc) + (string/starts-with? script "@/") + (if-let [rsrc (io/resource (subs script 2))] + (repl/load-stream renv (util/get-name rsrc) rsrc) (throw (ex-info - (str "Resource script " (subs script 1) " does not exist") + (str "Resource script " (subs script 2) " does not exist") {:cljs.main/error :invalid-arg}))) - (string/starts-with? script "@/") - (if-let [rsrc (io/resource (subs script 2))] - (repl/load-file renv rsrc) + (string/starts-with? script "@") + (if-let [rsrc (io/resource (subs script 1))] + (repl/load-stream renv (util/get-name rsrc) rsrc) (throw (ex-info - (str "Resource script " (subs script 2) " does not exist") + (str "Resource script " (subs script 1) " does not exist") {:cljs.main/error :invalid-arg}))) (string/starts-with? script "-") @@ -300,7 +322,7 @@ present" (ex-info (str "Namespace " main " does not exist") {:cljs.main/error :invalid-arg}))) - (repl/load-file renv src) + (repl/load-stream renv (util/get-name src) src) (repl/evaluate-form renv (ana-api/empty-env) "" `(~(symbol (name main) "-main") ~@args)))) (repl/tear-down renv))))))) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 6a2db950f..85a102519 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -793,7 +793,8 @@ (when-not (repl-nil? value) (println value))))) :init-script - (load-file renv (:script init))))) + (let [script (:script init)] + (load-stream renv (util/get-name script) script))))) (defn maybe-install-npm-deps [opts] (when (:install-deps opts) From d1cf0a31f232fbdc58ec93577ce7d3b7f2526722 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Feb 2018 18:53:53 -0500 Subject: [PATCH 2950/4033] CLJS-2577: cljs.main: Side effect of lone -i not occurring in brepl Make bREPL "easy mode" smarter. Only compile if browser REPL preload is not present. Fixup verbose mode, pr-str compiler options --- src/main/clojure/cljs/cli.clj | 2 +- src/main/clojure/cljs/repl/browser.clj | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index ea29d2333..de68876bf 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -265,7 +265,7 @@ present" repl/*repl-opts* opts ana/*verbose* (:verbose opts)] (when ana/*verbose* - (util/debug-prn "Compiler options:" repl/*repl-opts*)) + (util/debug-prn "Compiler options:" (pr-str repl/*repl-opts*))) (comp/with-core-cljs repl/*repl-opts* (fn [] (repl/setup renv repl/*repl-opts*) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 09973296f..b810f2f84 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -177,7 +177,9 @@ "var CLOSURE_NO_DEPS = true;\n" "document.write('');\n" "document.write('');\n" - "document.write('');\n" + (when (.exists (io/file "out" "cljs_deps.js")) + "document.write('');\n") + "document.write('');\n" "document.write('');\n") "text/javascript" "UTF-8")) :else (server/send-404 conn path))) @@ -289,8 +291,8 @@ (io/file working-dir "brepl_client.js"))))) ;; TODO: this could be cleaner if compiling forms resulted in a ;; :output-to file with the result of compiling those forms - David - (when (and output-dir (not (.exists (io/file output-dir)))) - (spit (io/file "out/cljs_deps.js") + (when (and output-dir (not (.exists (io/file output-dir "clojure" "browser" "repl" "preload.js")))) + (spit (io/file "out/brepl_deps.js") (build/build '[(require '[clojure.browser.repl.preload])] {:optimizations :none}))) (repl/err-out From c479467afc23bd01b57a67660063f2884549e4c0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 27 Feb 2018 18:59:51 -0500 Subject: [PATCH 2951/4033] require browser REPL preload, now can use browser REPL on a project that wasn't using it before. --- src/main/clojure/cljs/repl/browser.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index b810f2f84..8465fbc74 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -321,7 +321,7 @@ (-repl-options [this] {:browser-repl true :repl-requires - '[[clojure.browser.repl]] + '[[clojure.browser.repl] [clojure.browser.repl.preload]] :cljs.cli/commands {:groups {::repl {:desc "browser REPL options"}} :init From 4d9f6df5c1de8d5b6357b64cb7c8846e03499dbf Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 28 Feb 2018 09:02:26 -0500 Subject: [PATCH 2952/4033] CLJS-2584: Tweak whitespace / negative space / font sizes of default index.html --- src/main/clojure/cljs/repl/browser.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 8465fbc74..1540300b7 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -99,18 +99,21 @@ "" "" "" + "" "" "" "" "

" "" "" + "
" "

Welcome to the default index.html provided by the ClojureScript Browser REPL.

" "

This page provides the evaluation environment for your Browser REPL and application.

" - "

You can quickly validate the connection by typing (js/alert \"Hello CLJS!\") into the " + "

You can quickly validate the connection by typing (js/alert \"Hello CLJS!\") into the " "ClojureScript REPL that launched this page.

You can easily use your own HTML file to host your application " "and REPL by providing your own index.html in the directory that you launched this REPL from.

" "

Start with this template:

" @@ -125,7 +128,6 @@ " </body>\n" "</html>\n" "" - "
" "
" "" "")) From 07ba75849a12006ca7cf77cfb1836e6a217a5a70 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 28 Feb 2018 13:13:31 -0500 Subject: [PATCH 2953/4033] white bg, black text, code is cljs.org blue --- src/main/clojure/cljs/repl/browser.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 1540300b7..a8625b68b 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -107,8 +107,8 @@ "" "" "
" "

Welcome to the default index.html provided by the ClojureScript Browser REPL.

" From 2b8d5ae97430c63a22cba11720b6d99f8e0b08a7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 28 Feb 2018 11:25:59 -0500 Subject: [PATCH 2954/4033] CLJS-2585: Bad code gen for and / or expressions --- src/main/clojure/cljs/core.cljc | 2 ++ src/test/cljs/cljs/core_test.cljs | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index ce75b4923..f5185f06b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -855,6 +855,7 @@ (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) forms)) (core/let [and-str (core/->> (repeat (count forms) "(~{})") (interpose " && ") + (#(concat ["("] % [")"])) (apply core/str))] (bool-expr `(~'js* ~and-str ~@forms))) `(let [and# ~x] @@ -873,6 +874,7 @@ (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) forms)) (core/let [or-str (core/->> (repeat (count forms) "(~{})") (interpose " || ") + (#(concat ["("] % [")"])) (apply core/str))] (bool-expr `(~'js* ~or-str ~@forms))) `(let [or# ~x] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index ada361688..8884f076b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1525,6 +1525,18 @@ (deftest test-cljs-2552 (is (boolean? (tap> nil)))) +;; Delete a bogus property from the beta? fn +;; Without the fix this js-delete form code-gens to code that deletes the alpha? fn: +;; delete (cljs.core_test.alpha_2585_QMARK_) && (cljs.core_test.beta_2585_QMARK_)["bogus-property"] +(defn ^boolean alpha-2585? [] true) +(defn ^boolean beta-2585? [] true) +(js-delete (and alpha-2585? beta-2585?) "bogus-property") + +(deftest test-cljs-2585 + (is (= true ((or int? string?) 1))) + ;; Make sure we didn't delete the alpha? fn + (is (some? alpha-2585?))) + (comment ;; ObjMap ;; (let [ks (map (partial str "foo") (range 500)) From 0f2a407ef6169da2836d560f5ad72527635f9606 Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Wed, 28 Feb 2018 14:24:56 -0500 Subject: [PATCH 2955/4033] CLJS-2581: Create a cljs.repl/*repl-env* dynamic var and bind it around cljs repl loops --- src/main/clojure/cljs/cli.clj | 3 ++- src/main/clojure/cljs/core/server.clj | 3 ++- src/main/clojure/cljs/repl.cljc | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index de68876bf..2a75fab3a 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -263,7 +263,8 @@ present" (merge copts opts)))] (binding [ana/*cljs-ns* 'cljs.user repl/*repl-opts* opts - ana/*verbose* (:verbose opts)] + ana/*verbose* (:verbose opts) + repl/*repl-env* renv] (when ana/*verbose* (util/debug-prn "Compiler options:" (pr-str repl/*repl-opts*))) (comp/with-core-cljs repl/*repl-opts* diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index e47b2b3fc..d63f24bba 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -79,7 +79,8 @@ (with-bindings (binding [*in* (or stdin in-reader) *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) - *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] + *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil) + repl/*repl-env* repl-env] (let [opts (merge opts (:merge-opts (repl/setup repl-env opts)))] (binding [repl/*repl-opts* opts] (repl/evaluate-form repl-env env "" diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 85a102519..633a2bffe 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -35,6 +35,7 @@ (def ^:dynamic *cljs-verbose* false) (def ^:dynamic *repl-opts* nil) +(def ^:dynamic *repl-env* nil) (def known-repl-opts "Set of all known REPL options." @@ -856,7 +857,8 @@ (env/with-compiler-env (or compiler-env (env/default-compiler-env opts)) (when (:source-map opts) (.start (Thread. (bound-fn [] (read-source-map "cljs/core.aot.js"))))) - (binding [ana/*unchecked-if* false + (binding [*repl-env* repl-env + ana/*unchecked-if* false ana/*unchecked-arrays* false *err* (if bind-err (cond-> *out* From 06e121b9322e042ef277fe8e54af48d7bbdd21dd Mon Sep 17 00:00:00 2001 From: Andrea Richiardi Date: Mon, 26 Feb 2018 16:39:11 -0800 Subject: [PATCH 2956/4033] CLJS-2566: use -ro and -co for the new main options --- src/main/clojure/cljs/cli.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 2a75fab3a..72cbac583 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -472,10 +472,10 @@ present" (str "The JavaScript target. Configures environment bootstrap and " "defaults to browser. Supported values: nodejs, nashorn, " "webworker, none") } - ["-rf" "--repl-opts"] {:group ::main&compile :fn repl-env-opts-opt + ["-ro" "--repl-opts"] {:group ::main&compile :fn repl-env-opts-opt :arg "edn" :doc (str "Options to configure the repl-env")} - ["-cf" "--compile-opts"] {:group ::main&compile :fn compile-opts-opt + ["-co" "--compile-opts"] {:group ::main&compile :fn compile-opts-opt :arg "edn" :doc (str "Options to configure the build")}} :main From 94b8dd071c1c7cbb1c45b50225a025b574d74803 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 28 Feb 2018 15:42:59 -0500 Subject: [PATCH 2957/4033] CLJS-2573: cljs.main: require with -e prints blank line --- src/main/clojure/cljs/repl.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 633a2bffe..b113385f3 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -779,7 +779,7 @@ (.printStackTrace e *err*))) (defn repl-nil? [x] - (boolean (= "nil" x))) + (boolean (#{"" "nil"} x))) (defn run-inits [renv inits] (doseq [{:keys [type] :as init} inits] From 95ab74166595448f68cf86343e44e2fe8b8fc342 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 28 Feb 2018 16:13:29 -0500 Subject: [PATCH 2958/4033] CLJS-2572: cljs.main: throw from -i script doesn't terminate execution --- src/main/cljs/cljs/main.clj | 6 +- src/main/clojure/cljs/cli.clj | 107 +++++++++++++++++----------------- 2 files changed, 59 insertions(+), 54 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index 88845c3c2..de17bb33a 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -57,5 +57,7 @@ args) [js-args args] ((juxt #(take 2 %) #(drop 2 %)) post) repl-opt (get-js-opt js-args)] - (apply cli/main repl-opt (concat pre args)) - (shutdown-agents))) + (try + (apply cli/main repl-opt (concat pre args)) + (finally + (shutdown-agents))))) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 72cbac583..fa869ec9d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -269,64 +269,67 @@ present" (util/debug-prn "Compiler options:" (pr-str repl/*repl-opts*))) (comp/with-core-cljs repl/*repl-opts* (fn [] - (repl/setup renv repl/*repl-opts*) - ;; REPLs don't normally load cljs_deps.js - (when (and coptsf (.exists coptsf)) - (let [depsf (io/file (:output-dir options) "cljs_deps.js")] - (when (.exists depsf) - (repl/evaluate renv "cljs_deps.js" 1 (slurp depsf))))) - (repl/evaluate-form renv (ana-api/empty-env) "" - (when-not (empty? args) - `(set! *command-line-args* (list ~@args)))) - (repl/evaluate-form renv (ana-api/empty-env) "" - `(~'ns ~'cljs.user)) - (repl/run-inits renv inits) - (when script - (cond - (= "-" script) - (repl/load-stream renv "" *in*) - - (.exists (io/file script)) - (repl/load-file renv script) - - (string/starts-with? script "@/") - (if-let [rsrc (io/resource (subs script 2))] - (repl/load-stream renv (util/get-name rsrc) rsrc) - (throw - (ex-info - (str "Resource script " (subs script 2) " does not exist") - {:cljs.main/error :invalid-arg}))) + (try + (repl/setup renv repl/*repl-opts*) + ;; REPLs don't normally load cljs_deps.js + (when (and coptsf (.exists coptsf)) + (let [depsf (io/file (:output-dir options) "cljs_deps.js")] + (when (.exists depsf) + (repl/evaluate renv "cljs_deps.js" 1 (slurp depsf))))) + (repl/evaluate-form renv (ana-api/empty-env) "" + (when-not (empty? args) + `(set! *command-line-args* (list ~@args)))) + (repl/evaluate-form renv (ana-api/empty-env) "" + `(~'ns ~'cljs.user)) + (repl/run-inits renv inits) + (when script + (cond + (= "-" script) + (repl/load-stream renv "" *in*) + + (.exists (io/file script)) + (repl/load-file renv script) + + (string/starts-with? script "@/") + (if-let [rsrc (io/resource (subs script 2))] + (repl/load-stream renv (util/get-name rsrc) rsrc) + (throw + (ex-info + (str "Resource script " (subs script 2) " does not exist") + {:cljs.main/error :invalid-arg}))) - (string/starts-with? script "@") - (if-let [rsrc (io/resource (subs script 1))] - (repl/load-stream renv (util/get-name rsrc) rsrc) + (string/starts-with? script "@") + (if-let [rsrc (io/resource (subs script 1))] + (repl/load-stream renv (util/get-name rsrc) rsrc) + (throw + (ex-info + (str "Resource script " (subs script 1) " does not exist") + {:cljs.main/error :invalid-arg}))) + + (string/starts-with? script "-") (throw (ex-info - (str "Resource script " (subs script 1) " does not exist") - {:cljs.main/error :invalid-arg}))) - - (string/starts-with? script "-") - (throw - (ex-info - (str "Expected script or -, got flag " script " instead") - {:cljs.main/error :invalid-arg})) + (str "Expected script or -, got flag " script " instead") + {:cljs.main/error :invalid-arg})) - :else - (throw - (ex-info - (str "Script " script " does not exist") - {:cljs.main/error :invalid-arg})))) - (when main - (let [src (build/ns->source main)] - (when-not (io/resource src) + :else (throw (ex-info - (str "Namespace " main " does not exist") - {:cljs.main/error :invalid-arg}))) - (repl/load-stream renv (util/get-name src) src) - (repl/evaluate-form renv (ana-api/empty-env) "" - `(~(symbol (name main) "-main") ~@args)))) - (repl/tear-down renv))))))) + (str "Script " script " does not exist") + {:cljs.main/error :invalid-arg})))) + (when main + (let [src (build/ns->source main)] + (when-not (io/resource src) + (throw + (ex-info + (str "Namespace " main " does not exist") + {:cljs.main/error :invalid-arg}))) + (repl/load-stream renv (util/get-name src) src) + (repl/evaluate-form renv (ana-api/empty-env) "" + `(~(symbol (name main) "-main") ~@args)))) + (finally + (util/debug-prn "Tearing down") + (repl/tear-down renv))))))))) (defn- main-opt "Call the -main function from a namespace with string arguments from From da91c84876747353d8537f435d6db697abb3dfb3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 28 Feb 2018 16:26:06 -0500 Subject: [PATCH 2959/4033] CLJS-2571: cljs.main: -e affects *1, *2, *3 also remove stray util/debug-prn --- src/main/clojure/cljs/cli.clj | 1 - src/main/clojure/cljs/repl.cljc | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index fa869ec9d..6b4b0fbc3 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -328,7 +328,6 @@ present" (repl/evaluate-form renv (ana-api/empty-env) "" `(~(symbol (name main) "-main") ~@args)))) (finally - (util/debug-prn "Tearing down") (repl/tear-down renv))))))))) (defn- main-opt diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index b113385f3..fa17c1b53 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -636,6 +636,18 @@ (set! *e e#) (throw e#)))))) +(defn- init-wrap-fn [form] + (cond + (and (seq? form) + (#{'ns 'require 'require-macros + 'use 'use-macros 'import 'refer-clojure} (first form))) + identity + + ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) + :else + (fn [x] + `(cljs.core.pr-str ~x)))) + (defn eval-cljs "Given a REPL evaluation environment, an analysis environment, and a form, evaluate the form and return the result. The result is always the value @@ -788,9 +800,9 @@ (doseq [form (:forms init)] (eval-cljs renv (ana/empty-env) form)) :eval-forms - (binding [*repl-opts* (merge *repl-opts* {:def-emits-var true})] + (binding [*repl-opts* (merge *repl-opts* {:def-emits-var true :wrap init-wrap-fn})] (doseq [form (:forms init)] - (let [value (eval-cljs renv (ana/empty-env) form)] + (let [value (eval-cljs renv (ana/empty-env) form *repl-opts*)] (when-not (repl-nil? value) (println value))))) :init-script From 2cb29c58febd1ce8b2ef23f356a2c55f5bfd0835 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 28 Feb 2018 17:19:48 -0500 Subject: [PATCH 2960/4033] CLJS-2554: cljs.main: dir, source, doc, etc not auto-required in browser REPL We don't need to get REPL options, happens in the REPL --- src/main/clojure/cljs/cli.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 6b4b0fbc3..348590e90 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -234,10 +234,9 @@ classpath. Classpath-relative paths have prefix of @ or @/") "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] {:keys [repl-env-options options inits] :as cfg}] - (let [renv (apply repl-env (mapcat identity repl-env-options)) - opts (merge (repl/repl-options renv) options)] + (let [renv (apply repl-env (mapcat identity repl-env-options))] (repl/repl* renv - (assoc opts + (assoc options :inits (into [{:type :init-forms From 55e7c93c4a611a1cf599aff08eec244263310999 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 28 Feb 2018 17:29:15 -0500 Subject: [PATCH 2961/4033] remove accidental bump to 1.9.0 --- pom.template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 89d6f3154..7948bc956 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.9.0 + 1.8.0 com.google.javascript From 542d3fad1ff499a5e3b00e7e08eff4037083a59d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 28 Feb 2018 17:35:35 -0500 Subject: [PATCH 2962/4033] brepl shouldn't overwrite compiler opts file --- src/main/clojure/cljs/closure.clj | 7 +++++-- src/main/clojure/cljs/repl/browser.clj | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 7bb4bf186..2612843af 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -170,7 +170,7 @@ :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs :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}) + :stable-names :ignore-js-module-exts :opts-cache}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -2229,6 +2229,9 @@ (nil? (:closure-module-roots opts)) (assoc :closure-module-roots []) + (nil? (:opts-cache opts)) + (assoc :opts-cache "cljsc_opts.edn") + (contains? opts :modules) (ensure-module-opts) @@ -2774,7 +2777,7 @@ (map (comp :externs second) (get @compiler-env ::ana/namespaces))) (str (util/output-directory opts) "/inferred_externs.js"))) - _ (spit (io/file (util/output-directory opts) "cljsc_opts.edn") (pr-str orig-opts)) + _ (spit (io/file (util/output-directory opts) (:opts-cache opts)) (pr-str orig-opts)) optim (:optimizations opts) ret (if (and optim (not= optim :none)) (do diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index a8625b68b..133d9c413 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -296,7 +296,9 @@ (when (and output-dir (not (.exists (io/file output-dir "clojure" "browser" "repl" "preload.js")))) (spit (io/file "out/brepl_deps.js") (build/build - '[(require '[clojure.browser.repl.preload])] {:optimizations :none}))) + '[(require '[clojure.browser.repl.preload])] + {:optimizations :none + :opts-cache "brepl_opts.edn"}))) (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) From 848f4fa56517d883b95f56c6d93b7b47342acf7e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 28 Feb 2018 19:26:17 -0500 Subject: [PATCH 2963/4033] clarify -t docstring --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 348590e90..b6a7f783c 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -471,7 +471,7 @@ present" :arg "name" :doc (str "The JavaScript target. Configures environment bootstrap and " - "defaults to browser. Supported values: nodejs, nashorn, " + "defaults to browser. Supported values: node or nodejs, nashorn, " "webworker, none") } ["-ro" "--repl-opts"] {:group ::main&compile :fn repl-env-opts-opt :arg "edn" From f18c318882cd5699d34d1512564bc71e2a0b0235 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Mar 2018 00:51:13 -0500 Subject: [PATCH 2964/4033] ns->source returns resource --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index b6a7f783c..4b8d4fcc9 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -318,7 +318,7 @@ present" {:cljs.main/error :invalid-arg})))) (when main (let [src (build/ns->source main)] - (when-not (io/resource src) + (when-not src (throw (ex-info (str "Namespace " main " does not exist") From a352b332c354ccc231e9568875a7999f12f5fae6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Mar 2018 07:29:26 -0500 Subject: [PATCH 2965/4033] verbose logging of output for -c --- src/main/clojure/cljs/cli.clj | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 4b8d4fcc9..978a49be1 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -386,14 +386,17 @@ present" (:uri (build/ns->location main-ns))) repl? (boolean (#{"-r" "--repl"} (first args))) cenv (env/default-compiler-env)] - (if-let [path (:watch opts)] - (if repl? - (.start (Thread. #(watch-proc cenv path opts))) - (build/watch path opts cenv)) - (build/build source opts cenv)) - (when repl? - (repl-opt repl-env args - (assoc-in cfg [:options :compiler-env] cenv))))) + (binding [ana/*verbose* (:verbose opts)] + (when ana/*verbose* + (util/debug-prn "Compiler options:" (pr-str opts))) + (if-let [path (:watch opts)] + (if repl? + (.start (Thread. #(watch-proc cenv path opts))) + (build/watch path opts cenv)) + (build/build source opts cenv)) + (when repl? + (repl-opt repl-env args + (assoc-in cfg [:options :compiler-env] cenv)))))) (defn- compile-opt [repl-env [_ ns & args] cfg] From 4375a6318ee73e7ca94534fe5899d7708bb98ce3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Mar 2018 07:52:53 -0500 Subject: [PATCH 2966/4033] CLJS-2586: cljs.main: Need to explicitly indicate node REPL environment when compiling with none don't copy over :browser-repl from repl-env opts unless no target specified. --- src/main/clojure/cljs/cli.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 978a49be1..9dd38f8ab 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -370,7 +370,10 @@ present" (merge (when (and coptsf (.exists coptsf)) (edn/read-string (slurp coptsf))) - (select-keys env-opts [:target :browser-repl]) + (select-keys env-opts + (cond-> [:target] + (not (:target options)) + (conj :browser-repl))) options {:main main-ns}) opts (cond-> opts From 93a18ed4bb023aa0e95dfdc4d8809ccc2c6eea3b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 1 Mar 2018 08:02:20 -0500 Subject: [PATCH 2967/4033] remove redundant compiler opts logging --- src/main/clojure/cljs/cli.clj | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 9dd38f8ab..b7da77f21 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -389,17 +389,14 @@ present" (:uri (build/ns->location main-ns))) repl? (boolean (#{"-r" "--repl"} (first args))) cenv (env/default-compiler-env)] - (binding [ana/*verbose* (:verbose opts)] - (when ana/*verbose* - (util/debug-prn "Compiler options:" (pr-str opts))) - (if-let [path (:watch opts)] - (if repl? - (.start (Thread. #(watch-proc cenv path opts))) - (build/watch path opts cenv)) - (build/build source opts cenv)) - (when repl? - (repl-opt repl-env args - (assoc-in cfg [:options :compiler-env] cenv)))))) + (if-let [path (:watch opts)] + (if repl? + (.start (Thread. #(watch-proc cenv path opts))) + (build/watch path opts cenv)) + (build/build source opts cenv)) + (when repl? + (repl-opt repl-env args + (assoc-in cfg [:options :compiler-env] cenv))))) (defn- compile-opt [repl-env [_ ns & args] cfg] From 04e55f88bf61006e0abd4e276eaf2297d91d40ce Mon Sep 17 00:00:00 2001 From: Andrea Richiardi Date: Thu, 1 Mar 2018 12:14:15 -0800 Subject: [PATCH 2968/4033] CLJS-2582: cljs.main: -cf option with :main breaks the node REPL The fix involves removing entry point compiler options -co parameter values. --- src/main/clojure/cljs/cli.clj | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index b7da77f21..ce46676d0 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -230,13 +230,19 @@ classpath. Classpath-relative paths have prefix of @ or @/") ((get-dispatch commands :init opt) ret arg)) {} inits)) +(defn dissoc-entry-point-opts + "Dissoc the entry point options from the input. Necessary when the user +is trying load some arbitrary ns." + [opts] + (dissoc opts :main :output-to)) + (defn- repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] {:keys [repl-env-options options inits] :as cfg}] (let [renv (apply repl-env (mapcat identity repl-env-options))] (repl/repl* renv - (assoc options + (assoc (dissoc-entry-point-opts options) :inits (into [{:type :init-forms @@ -250,16 +256,11 @@ present" (let [renv (repl-env) coptsf (when-let [od (:output-dir options)] (io/file od "cljsc_opts.edn")) - opts (as-> - (build/add-implicit-options - (merge (repl/repl-options renv) options)) opts - (let [copts (when (and coptsf (.exists coptsf)) - (-> (edn/read-string (slurp coptsf)) - ;; need to remove the entry point bits, - ;; user is trying load some arbitrary ns - (dissoc :main) - (dissoc :output-to)))] - (merge copts opts)))] + copts (when (and coptsf (.exists coptsf)) + (-> (edn/read-string (slurp coptsf)) + (dissoc-entry-point-opts))) + opts (merge copts (build/add-implicit-options + (merge (repl/repl-options renv) options)))] (binding [ana/*cljs-ns* 'cljs.user repl/*repl-opts* opts ana/*verbose* (:verbose opts) From 0f263695f70199500fb573225cabc2ff843853e9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 1 Mar 2018 21:31:01 -0500 Subject: [PATCH 2969/4033] CLJS-2588: cljs.main: Windows path issue compiling for Node --- src/main/clojure/cljs/closure.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2612843af..c32fa5d8f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -282,6 +282,8 @@ (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler ) + (. compiler-options (setResolveSourceMapAnnotations false)) ;; Windows bug in Closure it seems + compiler-options) (defn ^CompilerOptions make-options From 8738c90675496a8aad104a47103898557dfd8b53 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 1 Mar 2018 21:45:33 -0500 Subject: [PATCH 2970/4033] revert last commit --- src/main/clojure/cljs/closure.clj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c32fa5d8f..2612843af 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -282,8 +282,6 @@ (setOutputCharset (to-charset (:closure-output-charset opts "UTF-8"))) ;; only works > 20160125 Closure Compiler ) - (. compiler-options (setResolveSourceMapAnnotations false)) ;; Windows bug in Closure it seems - compiler-options) (defn ^CompilerOptions make-options From 0d455574df757f0667b2821753e60dcceea7fdaf Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 2 Mar 2018 07:35:35 -0500 Subject: [PATCH 2971/4033] CLJS-2590: cljs-main: A flag allowing users to opt out of auto-browser launch --- src/main/clojure/cljs/repl/browser.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 133d9c413..8f34cabec 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -278,7 +278,7 @@ ;; ============================================================================= ;; BrowserEnv -(defn setup [{:keys [working-dir] :as repl-env} {:keys [output-dir] :as opts}] +(defn setup [{:keys [working-dir launch-browser] :as repl-env} {:keys [output-dir] :as opts}] (binding [browser-state (:browser-state repl-env) ordering (:ordering repl-env) es (:es repl-env) @@ -303,7 +303,8 @@ (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) (server/start repl-env) - (browse/browse-url (str "http://" (:host repl-env) ":" (:port repl-env))))) + (when launch-browser + (browse/browse-url (str "http://" (:host repl-env) ":" (:port repl-env)))))) (defrecord BrowserEnv [] repl/IJavaScriptEnv @@ -355,6 +356,7 @@ (merge (BrowserEnv.) {:host host :port port + :launch-browser true :working-dir (->> [".repl" (util/clojurescript-version)] (remove empty?) (string/join "-")) :serve-static true @@ -378,6 +380,8 @@ Options: port: The port on which the REPL server will run. Defaults to 9000. + launch-browser: A Boolean indicating whether a browser should be automatically + launched connecting back to the terminal REPL. Defaults to true. working-dir: The directory where the compiled REPL client JavaScript will be stored. Defaults to \".repl\" with a ClojureScript version suffix, eg. \".repl-0.0-2138\". From 8971829275b466280a0b8fc85886e3fa7d353a13 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 2 Mar 2018 11:47:29 -0600 Subject: [PATCH 2972/4033] CLJS-2594 Make main cljs jar AOT --- pom.template.xml | 13 +++++++++++++ src/assembly/slim.xml | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/assembly/slim.xml diff --git a/pom.template.xml b/pom.template.xml index 7948bc956..fd4d95199 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -364,11 +364,24 @@ single + false src/assembly/aot.xml + + slim-jar + package + + single + + + + src/assembly/slim.xml + + + diff --git a/src/assembly/slim.xml b/src/assembly/slim.xml new file mode 100644 index 000000000..ea61760b0 --- /dev/null +++ b/src/assembly/slim.xml @@ -0,0 +1,21 @@ + + slim + + jar + + false + + + src/main/cljs + / + + + src/main/clojure + / + + + resources + / + + + From 330c36585032c19824f6c63b8c7b64d0835bbc82 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 13:43:35 -0500 Subject: [PATCH 2973/4033] tweak cljs.util/ns->relpath so it can work sans file extension --- src/main/clojure/cljs/util.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 34c1f089d..067f0cf5a 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -69,7 +69,8 @@ provide the file extension, defaults to :cljs." ([ns] (ns->relpath ns :cljs)) ([ns ext] - (str (string/replace (munge-path ns) \. \/) "." (name ext)))) + (cond-> (string/replace (munge-path ns) \. \/) + ext (str "." (name ext))))) (defn ns->source "Given a namespace as a symbol return the corresponding resource if it exists." From c91051d969345e39e722a0c39a4403858eb0b195 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 13:46:15 -0500 Subject: [PATCH 2974/4033] don't make build-affecting-option private so we can use it from cljs.closure --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 2b68c1759..de0a297dc 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1288,7 +1288,7 @@ (defn url-path [^File f] (.getPath (.toURL (.toURI f))))) -(defn- build-affecting-options [opts] +(defn build-affecting-options [opts] (select-keys opts [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target :cache-key :checked-arrays :language-out])) From f2a888a4a725cab380389c349f06f4905d639e3e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 13:57:14 -0500 Subject: [PATCH 2975/4033] helpers for computing cacheable files --- src/main/clojure/cljs/closure.clj | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2612843af..a48bd0bbb 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -592,6 +592,26 @@ (.setLastModified ^File out-file (util/last-modified url)) out-file))) +(defn build-affecting-options-sha [opts] + (util/content-sha (pr-str (comp/build-affecting-options opts)) 7)) + +(defn cache-base-path + ([] + (cache-base-path nil)) + ([opts] + (io/file (System/getProperty "user.home") + ".cljs" ".aot_cache" (util/clojurescript-version) + (build-affecting-options-sha opts)))) + +(defn cacheable-files + ([ns] + (cacheable-files ns nil)) + ([ns opts] + (let [path (cache-base-path opts) + name (util/ns->relpath ns nil)] + (map #(io/file path (str name %)) + [".js" ".cljs" ".cljs.cache.edn" ".cljs.cache.json" ".js.map"])))) + ;; TODO: it would be nice if we could consolidate requires-compilation? ;; logic - David (defn compile-from-jar From 605b5705ea18aeeed9a134293932ffd2379c7671 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 15:51:42 -0500 Subject: [PATCH 2976/4033] CLJS-1067: Shared AOT cache for dependencies in JARs also memoize parse-ns --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/main/clojure/cljs/closure.clj | 93 ++++++++++++++++++----------- 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cf646a92c..cb736c40f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3723,7 +3723,7 @@ (symbol (str "cljs.user." name (util/content-sha full-name 7))))))) #?(:clj - (defn parse-ns + (defn ^:dynamic parse-ns "Helper for parsing only the essential namespace information from a ClojureScript source file and returning a cljs.closure/IJavaScript compatible map _not_ a namespace AST node. diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a48bd0bbb..4601bf3c5 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -569,6 +569,33 @@ (map compiled-file (comp/compile-root src-dir out-dir opts)))) +(defn build-affecting-options-sha [opts] + (util/content-sha (pr-str (comp/build-affecting-options opts)) 7)) + +(defn ^File cache-base-path + ([] + (cache-base-path nil)) + ([opts] + (io/file (System/getProperty "user.home") + ".cljs" ".aot_cache" (util/clojurescript-version) + (build-affecting-options-sha opts)))) + +(defn cacheable-files + ([ns ext] + (cacheable-files ns ext nil)) + ([ns ext opts] + (let [path (cache-base-path opts) + name (util/ns->relpath ns nil)] + (into {} + (map + (fn [[k v]] + [k (io/file path (str name v))])) + {:source (str "." ext) + :output-file ".js" + :source-map ".js.map" + :analysis-cache-edn ".cljs.cache.edn" + :analysis-cache-json ".cljs.cache.json"})))) + (defn ^String path-from-jarfile "Given the URL of a file within a jar, return the path of the file from the root of the jar." @@ -592,50 +619,43 @@ (.setLastModified ^File out-file (util/last-modified url)) out-file))) -(defn build-affecting-options-sha [opts] - (util/content-sha (pr-str (comp/build-affecting-options opts)) 7)) - -(defn cache-base-path - ([] - (cache-base-path nil)) - ([opts] - (io/file (System/getProperty "user.home") - ".cljs" ".aot_cache" (util/clojurescript-version) - (build-affecting-options-sha opts)))) - -(defn cacheable-files - ([ns] - (cacheable-files ns nil)) - ([ns opts] - (let [path (cache-base-path opts) - name (util/ns->relpath ns nil)] - (map #(io/file path (str name %)) - [".js" ".cljs" ".cljs.cache.edn" ".cljs.cache.json" ".js.map"])))) - ;; TODO: it would be nice if we could consolidate requires-compilation? ;; logic - David (defn compile-from-jar "Compile a file from a jar if necessary. Returns IJavaScript." [jar-file {:keys [output-file] :as opts}] - (let [out-file (when output-file - (io/file (util/output-directory opts) output-file))] - (if (or (nil? out-file) - (not (.exists ^File out-file)) - (not= (util/compiled-by-version out-file) - (util/clojurescript-version)) - (util/changed? jar-file out-file)) + (let [{:keys [ns]} (ana/parse-ns jar-file) + out-file (when output-file + (io/file (util/output-directory opts) output-file)) + cacheable (cacheable-files ns (util/ext jar-file) opts)] + (when (or (nil? out-file) + (not (.exists ^File out-file)) + (not= (util/compiled-by-version out-file) + (util/clojurescript-version)) + (util/changed? jar-file out-file)) ;; actually compile from JAR - (let [file-on-disk (jar-file-to-disk jar-file (util/output-directory opts) opts)] - (-compile file-on-disk opts)) - ;; have to call compile-file as it includes more IJavaScript - ;; information than ana/parse-ns - (compile-file - (io/file (util/output-directory opts) - (last (string/split (.getPath ^URL jar-file) #"\.jar!/"))) - opts)))) + (let [cache-path (cache-base-path opts)] + (when-not (.exists (:output-file cacheable)) + (-compile (jar-file-to-disk jar-file cache-path opts) opts)) + (doseq [[k ^File f] cacheable] + (when (.exists f) + (let [target (io/file (util/output-directory opts) + (-> (.getAbsolutePath f) + (string/replace (.getAbsolutePath cache-path) "") + (subs 1)))] + (when (and ana/*verbose* (= :source k)) + (util/debug-prn (str "Copying cached " f " to " target))) + (spit target (slurp f)) + (.setLastModified target (util/last-modified jar-file))))))) + ;; have to call compile-file as it includes more IJavaScript + ;; information than ana/parse-ns for now + (compile-file + (io/file (util/output-directory opts) + (last (string/split (.getPath ^URL jar-file) #"\.jar!/"))) + opts))) (defn find-jar-sources - [this opts] + [this opts] () [(comp/find-source (jar-file-to-disk this (util/output-directory opts)))]) (extend-protocol Compilable @@ -2736,6 +2756,7 @@ (binding [comp/*recompiled* (when-not (false? (:recompile-dependents opts)) (atom #{})) ana/*checked-arrays* checked-arrays + ana/parse-ns (memoize ana/parse-ns) ana/*cljs-static-fns* static-fns? ana/*fn-invoke-direct* (or (and static-fns? (:fn-invoke-direct opts)) From f0b7facd3011a82202c2122fd79552af8de9e870 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 16:06:08 -0500 Subject: [PATCH 2977/4033] remove strange call to jar-file-to-disk when finding sources --- src/main/clojure/cljs/closure.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4601bf3c5..a4d545ca1 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -654,9 +654,8 @@ (last (string/split (.getPath ^URL jar-file) #"\.jar!/"))) opts))) -(defn find-jar-sources - [this opts] () - [(comp/find-source (jar-file-to-disk this (util/output-directory opts)))]) +(defn find-jar-sources [this opts] + [(comp/find-source this)]) (extend-protocol Compilable From 93544d67b99a6161cd063f4bde435f2e10801bd7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 16:21:46 -0500 Subject: [PATCH 2978/4033] fix verbose logging, log cached js being copied over, not original source --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a4d545ca1..e125af4f4 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -643,7 +643,7 @@ (-> (.getAbsolutePath f) (string/replace (.getAbsolutePath cache-path) "") (subs 1)))] - (when (and ana/*verbose* (= :source k)) + (when (and (or ana/*verbose* (:verbose opts)) (= :out-file k)) (util/debug-prn (str "Copying cached " f " to " target))) (spit target (slurp f)) (.setLastModified target (util/last-modified jar-file))))))) From 517d623e2ab7c1111fafb00e4f609583a89492cd Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 16:28:39 -0500 Subject: [PATCH 2979/4033] update opts :output-dir when compiling to cache --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e125af4f4..7c7726920 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -636,7 +636,8 @@ ;; actually compile from JAR (let [cache-path (cache-base-path opts)] (when-not (.exists (:output-file cacheable)) - (-compile (jar-file-to-disk jar-file cache-path opts) opts)) + (-compile (jar-file-to-disk jar-file cache-path opts) + (assoc opts :output-dir cache-path))) (doseq [[k ^File f] cacheable] (when (.exists f) (let [target (io/file (util/output-directory opts) From a14bced290b3d5b4890dde55d5f5aa292e1588e5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 16:43:02 -0500 Subject: [PATCH 2980/4033] need to compile to the right place --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 7c7726920..dcaa3f95c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -637,7 +637,7 @@ (let [cache-path (cache-base-path opts)] (when-not (.exists (:output-file cacheable)) (-compile (jar-file-to-disk jar-file cache-path opts) - (assoc opts :output-dir cache-path))) + (assoc opts :output-dir (util/path cache-path)))) (doseq [[k ^File f] cacheable] (when (.exists f) (let [target (io/file (util/output-directory opts) @@ -646,6 +646,7 @@ (subs 1)))] (when (and (or ana/*verbose* (:verbose opts)) (= :out-file k)) (util/debug-prn (str "Copying cached " f " to " target))) + (util/mkdirs target) (spit target (slurp f)) (.setLastModified target (util/last-modified jar-file))))))) ;; have to call compile-file as it includes more IJavaScript From 0f7d2214bc16bf5868c11cddb8ea4f95d4a23fda Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 16:48:37 -0500 Subject: [PATCH 2981/4033] typo --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index dcaa3f95c..0936e2836 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -644,7 +644,7 @@ (-> (.getAbsolutePath f) (string/replace (.getAbsolutePath cache-path) "") (subs 1)))] - (when (and (or ana/*verbose* (:verbose opts)) (= :out-file k)) + (when (and (or ana/*verbose* (:verbose opts)) (= :output-file k)) (util/debug-prn (str "Copying cached " f " to " target))) (util/mkdirs target) (spit target (slurp f)) From a0b952129a087a724fc6f89cb9b1a5960426dfd6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Mar 2018 21:06:18 -0500 Subject: [PATCH 2982/4033] add timestamp to URL so Safari doesn't try to load cached stuff which we may be writing to out --- src/main/clojure/cljs/repl/browser.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 8f34cabec..447a55795 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -304,7 +304,8 @@ (println "Listening for browser REPL connect ...")) (server/start repl-env) (when launch-browser - (browse/browse-url (str "http://" (:host repl-env) ":" (:port repl-env)))))) + (browse/browse-url + (str "http://" (:host repl-env) ":" (:port repl-env) "?rel=" (System/currentTimeMillis)))))) (defrecord BrowserEnv [] repl/IJavaScriptEnv From 1d676bde252fb2ff705b2c459db380aa652c1aa3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Mar 2018 09:25:10 -0500 Subject: [PATCH 2983/4033] CLJS-2597: Shared AOT cache: JAR version not included in hash --- src/main/clojure/cljs/closure.clj | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0936e2836..15334c6c9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -569,22 +569,24 @@ (map compiled-file (comp/compile-root src-dir out-dir opts)))) -(defn build-affecting-options-sha [opts] - (util/content-sha (pr-str (comp/build-affecting-options opts)) 7)) +(defn build-affecting-options-sha [path opts] + (let [m (assoc (comp/build-affecting-options opts) :path path)] + (util/content-sha (pr-str m) 7))) (defn ^File cache-base-path - ([] - (cache-base-path nil)) - ([opts] + ([path] + (cache-base-path path nil)) + ([path opts] (io/file (System/getProperty "user.home") ".cljs" ".aot_cache" (util/clojurescript-version) - (build-affecting-options-sha opts)))) + (build-affecting-options-sha path opts)))) (defn cacheable-files - ([ns ext] - (cacheable-files ns ext nil)) - ([ns ext opts] - (let [path (cache-base-path opts) + ([rsrc ext] + (cacheable-files rsrc ext nil)) + ([rsrc ext opts] + (let [{:keys [ns]} (ana/parse-ns rsrc) + path (cache-base-path (util/path rsrc) opts) name (util/ns->relpath ns nil)] (into {} (map @@ -624,17 +626,16 @@ (defn compile-from-jar "Compile a file from a jar if necessary. Returns IJavaScript." [jar-file {:keys [output-file] :as opts}] - (let [{:keys [ns]} (ana/parse-ns jar-file) - out-file (when output-file + (let [out-file (when output-file (io/file (util/output-directory opts) output-file)) - cacheable (cacheable-files ns (util/ext jar-file) opts)] + cacheable (cacheable-files jar-file (util/ext jar-file) opts)] (when (or (nil? out-file) (not (.exists ^File out-file)) (not= (util/compiled-by-version out-file) (util/clojurescript-version)) (util/changed? jar-file out-file)) ;; actually compile from JAR - (let [cache-path (cache-base-path opts)] + (let [cache-path (cache-base-path (util/path jar-file) opts)] (when-not (.exists (:output-file cacheable)) (-compile (jar-file-to-disk jar-file cache-path opts) (assoc opts :output-dir (util/path cache-path)))) From 60631d06e04501537001d9da2a9134838fd1e0dc Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 3 Mar 2018 08:30:21 -0600 Subject: [PATCH 2984/4033] CLJS-2599 Add pom.xml and pom.properties to output jars --- src/assembly/aot.xml | 50 ++++++++++++++++++++++++++----------------- src/assembly/slim.xml | 10 +++++++++ 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/assembly/aot.xml b/src/assembly/aot.xml index ce9897faf..177bdd72b 100644 --- a/src/assembly/aot.xml +++ b/src/assembly/aot.xml @@ -1,21 +1,31 @@ - aot - - jar - - false - - - target/classes - / - - - - - \ No newline at end of file + aot + + jar + + false + + + target/classes + / + + + + + + + pom.xml + META-INF/maven/org.clojure/clojurescript + + + target/maven-archiver/pom.properties + META-INF/maven/org.clojure/clojurescript + + + diff --git a/src/assembly/slim.xml b/src/assembly/slim.xml index ea61760b0..3e22d5e4f 100644 --- a/src/assembly/slim.xml +++ b/src/assembly/slim.xml @@ -18,4 +18,14 @@ / + + + pom.xml + META-INF/maven/org.clojure/clojurescript + + + target/maven-archiver/pom.properties + META-INF/maven/org.clojure/clojurescript + + From 517f3d8876f90a1bebb2dc0bffbc926297226ea1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Mar 2018 11:33:36 -0500 Subject: [PATCH 2985/4033] clean up how we set *print-fn* *print-err-fn* so it's easier to debug --- src/main/cljs/cljs/core.cljs | 8 ++++---- src/main/cljs/cljs/nodejs.cljs | 4 ++-- src/main/cljs/clojure/browser/repl.cljs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6adfc7d5c..f8e1b1a1b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -186,10 +186,10 @@ "Set *print-fn* to console.log" [] (set! *print-newline* false) - (set! *print-fn* + (set-print-fn! (fn [& args] (.apply (.-log js/console) js/console (into-array args)))) - (set! *print-err-fn* + (set-print-err-fn! (fn [& args] (.apply (.-error js/console) js/console (into-array args)))) nil) @@ -11498,10 +11498,10 @@ reduces them without incurring seq initialization" (identical? *target* "nashorn") (let [system (.type js/Java "java.lang.System")] (set! *print-newline* false) - (set! *print-fn* + (set-print-fn! (fn [& args] (.println (.-out system) (.join (into-array args) "")))) - (set! *print-err-fn* + (set-print-err-fn! (fn [& args] (.println (.-error system) (.join (into-array args) ""))))))) diff --git a/src/main/cljs/cljs/nodejs.cljs b/src/main/cljs/cljs/nodejs.cljs index 356023609..ca587888d 100644 --- a/src/main/cljs/cljs/nodejs.cljs +++ b/src/main/cljs/cljs/nodejs.cljs @@ -18,10 +18,10 @@ (defn enable-util-print! [] (set! *print-newline* false) - (set! *print-fn* + (set-print-fn! (fn [& args] (.apply (.-log js/console) js/console (into-array args)))) - (set! *print-err-fn* + (set-print-err-fn! (fn [& args] (.apply (.-error js/console) js/console (into-array args)))) nil) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 3c1214399..dbd2c1bf0 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -43,9 +43,9 @@ (when-let [conn @xpc-connection] (flush-print-queue! conn))) -(set! *print-fn* repl-print) -(set! *print-err-fn* repl-print) (set! *print-newline* true) +(set-print-fn! repl-print) +(set-print-err-fn! repl-print) (defn get-ua-product [] (cond From a8d200761d70fbccf6cc749de58344fc896b7b99 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Mar 2018 11:49:48 -0500 Subject: [PATCH 2986/4033] remove bad enable-console-print! from browser preload --- src/main/cljs/clojure/browser/repl/preload.cljs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl/preload.cljs b/src/main/cljs/clojure/browser/repl/preload.cljs index dcaa25ebc..8ef31606e 100644 --- a/src/main/cljs/clojure/browser/repl/preload.cljs +++ b/src/main/cljs/clojure/browser/repl/preload.cljs @@ -11,5 +11,3 @@ (defonce conn (repl/connect (str "http://" repl/HOST ":" repl/PORT "/repl"))) - -(enable-console-print!) From 298b1261ad6490ec4e8c02b8c5044d4227ad9b00 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Mar 2018 12:44:52 -0500 Subject: [PATCH 2987/4033] fix browser REPL printing race --- src/main/cljs/clojure/browser/repl.cljs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index dbd2c1bf0..6d0953818 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -31,6 +31,7 @@ (goog-define PORT 9000) (def xpc-connection (atom nil)) +(def parent-connected? (atom false)) (def print-queue (array)) (defn flush-print-queue! [conn] @@ -40,8 +41,8 @@ (defn repl-print [data] (.push print-queue (pr-str data)) - (when-let [conn @xpc-connection] - (flush-print-queue! conn))) + (when @parent-connected? + (flush-print-queue! @xpc-connection))) (set! *print-newline* true) (set-print-fn! repl-print) @@ -220,9 +221,11 @@ ;; to ack once. (when-not @connected? (reset! connected? true) + (reset! parent-connected? true) (net/transmit repl-connection :ack-handshake - nil)))) + nil) + (flush-print-queue! repl-connection)))) (net/register-service repl-connection :evaluate-javascript (fn [js] From 7c711159790f85ed94b33d339f5f98403dc7114e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 3 Mar 2018 14:43:30 -0500 Subject: [PATCH 2988/4033] add new main opt for starting simple web server --- src/main/clojure/cljs/cli.clj | 16 ++++++++++++++++ src/main/clojure/cljs/repl/browser.clj | 14 ++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index ce46676d0..3b6921a7e 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -404,6 +404,19 @@ present" ((::compile (repl/-repl-options (repl-env)) default-compile) repl-env (merge cfg {:args args :ns ns}))) +(defn- serve-opt + [_ [_ address-port & args] {:keys [options] :as cfg}] + (let [[host port] (if address-port + (string/split address-port #":") + ["localhost" 9000])] + (require 'cljs.repl.browser) + ((ns-resolve 'cljs.repl.browser 'serve) + {:host host + :port (if port + (cond-> port (string? port) Integer/parseInt) + 9000) + :output-dir (:output-dir options "out")}))) + (defn get-options [commands k] (if (= :all k) (into (get-options commands :main) (get-options commands :init)) @@ -493,6 +506,9 @@ present" :arg "ns" :doc (str "Compile a namespace. If -r / --repl present after " "namespace will launch a REPL after the compile completes")} + ["-s" "--serve"] {:fn serve-opt + :arg "host:port" + :doc (str "Start a simple web server to serve the current directory")} [nil] {:fn null-opt} ["-h" "--help" "-?"] {:fn help-opt :doc "Print this help message and exit"}}})) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 447a55795..08881d309 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -275,6 +275,15 @@ [repl-env provides url] (browser-eval (slurp url))) +(defn serve [{:keys [host port output-dir] :as opts}] + (println "Serving HTTP on" host "port" port) + (binding [ordering (agent {:expecting nil :fns {}}) + es (Executors/newFixedThreadPool 16) + server/state (atom {:socket nil :connection nil :promised-conn nil})] + (server/start + (merge opts + {:static-dir (cond-> ["." "out/"] output-dir (conj output-dir))})))) + ;; ============================================================================= ;; BrowserEnv @@ -360,12 +369,11 @@ :launch-browser true :working-dir (->> [".repl" (util/clojurescript-version)] (remove empty?) (string/join "-")) - :serve-static true :static-dir (cond-> ["." "out/"] output-dir (conj output-dir)) :preloaded-libs [] :src "src/" :browser-state (atom {:return-value-fn nil - :client-js nil}) + :client-js nil}) :ordering (agent {:expecting nil :fns {}}) :es (Executors/newFixedThreadPool 16) :server-state @@ -386,8 +394,6 @@ working-dir: The directory where the compiled REPL client JavaScript will be stored. Defaults to \".repl\" with a ClojureScript version suffix, eg. \".repl-0.0-2138\". - serve-static: Should the REPL server attempt to serve static content? - Defaults to true. static-dir: List of directories to search for static content. Defaults to [\".\" \"out/\"]. src: The source directory containing user-defined cljs files. Used to From 5f08aaf66546f2afe1b740445668fdd580250db5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 16:19:27 -0500 Subject: [PATCH 2989/4033] builtin webserver now gzips when run standalone --- src/main/clojure/cljs/repl/browser.clj | 15 +++++------- src/main/clojure/cljs/repl/server.clj | 34 ++++++++++++++++++++------ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 08881d309..f454442ad 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -132,7 +132,7 @@ "" "")) -(defn send-static [{path :path :as request} conn {:keys [static-dir host port] :as opts}] +(defn send-static [{path :path :as request} conn {:keys [static-dir host port gzip?] :as opts}] (if (and static-dir (not= "/favicon.ico" path)) (let [path (if (= "/" path) "/index.html" path) local-path @@ -157,13 +157,9 @@ local-path (if-let [ext (some #(if (.endsWith path %) %) (keys ext->mime-type))] (let [mime-type (ext->mime-type ext "text/plain") - encoding (mime-type->encoding mime-type "UTF-8")] - (server/send-and-close - conn - 200 - (slurp local-path :encoding encoding) - mime-type - encoding)) + encoding (mime-type->encoding mime-type "UTF-8")] + (server/send-and-close conn 200 (slurp local-path :encoding encoding) + mime-type encoding (and gzip? (= "text/javascript" mime-type)))) (server/send-and-close conn 200 (slurp local-path) "text/plain")) ;; "/index.html" doesn't exist, provide our own (= path "/index.html") @@ -282,7 +278,8 @@ server/state (atom {:socket nil :connection nil :promised-conn nil})] (server/start (merge opts - {:static-dir (cond-> ["." "out/"] output-dir (conj output-dir))})))) + {:static-dir (cond-> ["." "out/"] output-dir (conj output-dir)) + :gzip? true})))) ;; ============================================================================= ;; BrowserEnv diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index 9f3a85d1c..c0e422236 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -11,6 +11,8 @@ (:require [clojure.string :as str]) (:import java.io.BufferedReader java.io.InputStreamReader + java.io.ByteArrayOutputStream + java.util.zip.GZIPOutputStream java.net.ServerSocket)) (def ^:dynamic state nil) @@ -130,6 +132,18 @@ 404 "HTTP/1.1 404 Not Found" "HTTP/1.1 500 Error")) +(defn ^bytes gzip [^bytes bytes] + (let [baos (ByteArrayOutputStream. (count bytes))] + (try + (let [gzos (GZIPOutputStream. baos)] + (try + (.write gzos bytes) + (finally + (.close gzos)))) + (finally + (.close baos))) + (.toByteArray baos))) + (defn send-and-close "Use the passed connection to send a form to the browser. Send a proper HTTP response." @@ -138,16 +152,20 @@ ([conn status form content-type] (send-and-close conn status form content-type "UTF-8")) ([conn status form content-type encoding] - (let [byte-form (.getBytes form encoding) + (send-and-close conn status form content-type encoding false)) + ([conn status form content-type encoding gzip?] + (let [byte-form (cond-> (.getBytes form encoding) gzip? gzip) content-length (count byte-form) headers (map #(.getBytes (str % "\r\n")) - [(status-line status) - "Server: ClojureScript REPL" - (str "Content-Type: " - content-type - "; charset=" encoding) - (str "Content-Length: " content-length) - ""])] + (cond-> + [(status-line status) + "Server: ClojureScript REPL" + (str "Content-Type: " + content-type + "; charset=" encoding) + (str "Content-Length: " content-length)] + gzip? (conj "Content-Encoding: gzip") + true (conj "")))] (with-open [os (.getOutputStream conn)] (doseq [header headers] (.write os header 0 (count header))) From 1d98bdb348c941080c966b3374721b41b8dc111b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 3 Mar 2018 14:06:41 -0500 Subject: [PATCH 2990/4033] CLJS-2605: Shared AOT Cache: Incorrect extension for cljc files --- src/main/clojure/cljs/closure.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 15334c6c9..e7b75b8c7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -595,8 +595,8 @@ {:source (str "." ext) :output-file ".js" :source-map ".js.map" - :analysis-cache-edn ".cljs.cache.edn" - :analysis-cache-json ".cljs.cache.json"})))) + :analysis-cache-edn (str "." ext ".cache.edn") + :analysis-cache-json (str "." ext ".cache.json")})))) (defn ^String path-from-jarfile "Given the URL of a file within a jar, return the path of the file From 9eae9cac6f6fd637b8df5c2a275e0df0c143765c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 18:35:20 -0500 Subject: [PATCH 2991/4033] CLJS-2595: cljs.main: Mixture of out and directory specified by -d Change browser REPL so "easy" mode is not hardcoded to out. --- src/main/clojure/cljs/cli.clj | 21 ++++++++++----- src/main/clojure/cljs/repl/browser.clj | 36 ++++++++++++++------------ 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 3b6921a7e..c266f640d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -240,7 +240,11 @@ is trying load some arbitrary ns." "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] {:keys [repl-env-options options inits] :as cfg}] - (let [renv (apply repl-env (mapcat identity repl-env-options))] + (let [reopts (merge repl-env-options + (select-keys options [:output-to :output-dir])) + _ (when (or ana/*verbose* (:verbose options)) + (util/debug-prn "REPL env options:" (pr-str reopts))) + renv (apply repl-env (mapcat identity reopts))] (repl/repl* renv (assoc (dissoc-entry-point-opts options) :inits @@ -251,20 +255,25 @@ present" inits))))) (defn default-main - [repl-env {:keys [main script args options inits] :as cfg}] + [repl-env {:keys [main script args repl-env-options options inits] :as cfg}] (env/ensure - (let [renv (repl-env) + (let [reopts (merge repl-env-options + (select-keys options [:output-to :output-dir])) + _ (when (or ana/*verbose* (:verbose options)) + (util/debug-prn "REPL env options:" (pr-str reopts))) + renv (apply repl-env (mapcat identity reopts)) coptsf (when-let [od (:output-dir options)] (io/file od "cljsc_opts.edn")) copts (when (and coptsf (.exists coptsf)) (-> (edn/read-string (slurp coptsf)) (dissoc-entry-point-opts))) - opts (merge copts (build/add-implicit-options - (merge (repl/repl-options renv) options)))] + opts (merge copts + (build/add-implicit-options + (merge (repl/repl-options renv) options)))] (binding [ana/*cljs-ns* 'cljs.user repl/*repl-opts* opts ana/*verbose* (:verbose opts) - repl/*repl-env* renv] + repl/*repl-env* renv] (when ana/*verbose* (util/debug-prn "Compiler options:" (pr-str repl/*repl-opts*))) (comp/with-core-cljs repl/*repl-opts* diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index f454442ad..61fd4d802 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -132,7 +132,9 @@ "" "")) -(defn send-static [{path :path :as request} conn {:keys [static-dir host port gzip?] :as opts}] +(defn send-static + [{path :path :as request} conn + {:keys [static-dir output-to output-dir host port gzip?] :or {output-dir "out"} :as opts}] (if (and static-dir (not= "/favicon.ico" path)) (let [path (if (= "/" path) "/index.html" path) local-path @@ -151,8 +153,7 @@ (#{"/cljs-logo-icon-32.png" "/cljs-logo.svg"} path) (io/resource (subs path 1)) :else nil) - local-path) - copts (when env/*compiler* (get @env/*compiler* :options))] + local-path)] (cond local-path (if-let [ext (some #(if (.endsWith path %) %) (keys ext->mime-type))] @@ -163,9 +164,9 @@ (server/send-and-close conn 200 (slurp local-path) "text/plain")) ;; "/index.html" doesn't exist, provide our own (= path "/index.html") - (let [{:keys [output-to] :or {output-to "out/main.js"}} copts] - (server/send-and-close conn 200 (default-index output-to) "text/html" "UTF-8")) - (= path "/out/main.js") + (server/send-and-close conn 200 + (default-index (or output-to (str output-dir "/main.js"))) "text/html" "UTF-8") + (= path (str "/" output-dir "/main.js") ) (let [closure-defines (-> `{clojure.browser.repl/HOST ~host clojure.browser.repl/PORT ~port} cljsc/normalize-closure-defines @@ -173,11 +174,11 @@ (server/send-and-close conn 200 (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "var CLOSURE_NO_DEPS = true;\n" - "document.write('');\n" - "document.write('');\n" - (when (.exists (io/file "out" "cljs_deps.js")) - "document.write('');\n") - "document.write('');\n" + "document.write('');\n" + "document.write('');\n" + (when (.exists (io/file output-dir "cljs_deps.js")) + "document.write('');\n") + "document.write('');\n" "document.write('');\n") "text/javascript" "UTF-8")) :else (server/send-404 conn path))) @@ -300,11 +301,14 @@ ;; TODO: this could be cleaner if compiling forms resulted in a ;; :output-to file with the result of compiling those forms - David (when (and output-dir (not (.exists (io/file output-dir "clojure" "browser" "repl" "preload.js")))) - (spit (io/file "out/brepl_deps.js") - (build/build - '[(require '[clojure.browser.repl.preload])] - {:optimizations :none - :opts-cache "brepl_opts.edn"}))) + (let [target (io/file output-dir "brepl_deps.js")] + (util/mkdirs target) + (spit target + (build/build + '[(require '[clojure.browser.repl.preload])] + {:optimizations :none + :output-dir output-dir + :opts-cache "brepl_opts.edn"})))) (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) From ff3854d517449a714c765c8807045ab81e82a625 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 18:57:56 -0500 Subject: [PATCH 2992/4033] Browser REPL can now handle absolute path :output-dir, enables -d `mktemp -d` usage --- src/main/clojure/cljs/repl/browser.clj | 94 +++++++++++++------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 61fd4d802..934eec08c 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -135,54 +135,56 @@ (defn send-static [{path :path :as request} conn {:keys [static-dir output-to output-dir host port gzip?] :or {output-dir "out"} :as opts}] - (if (and static-dir (not= "/favicon.ico" path)) - (let [path (if (= "/" path) "/index.html" path) + (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) + local-path + (cond-> + (seq (for [x (if (string? static-dir) [static-dir] static-dir) + :when (.exists (io/file (str x path)))] + (str x path))) + (complement nil?) first) + local-path + (if (nil? local-path) + (cond + (re-find #".jar" path) + (io/resource (second (string/split path #".jar!/"))) + (string/includes? path (System/getProperty "user.dir")) + (io/file (string/replace path (str (System/getProperty "user.dir") "/") "")) + (#{"/cljs-logo-icon-32.png" "/cljs-logo.svg"} path) + (io/resource (subs path 1)) + :else nil) + local-path)] + (cond local-path - (cond-> - (seq (for [x (if (string? static-dir) [static-dir] static-dir) - :when (.exists (io/file (str x path)))] - (str x path))) - (complement nil?) first) - local-path - (if (nil? local-path) - (cond - (re-find #".jar" path) - (io/resource (second (string/split path #".jar!/"))) - (string/includes? path (System/getProperty "user.dir")) - (io/file (string/replace path (str (System/getProperty "user.dir") "/") "")) - (#{"/cljs-logo-icon-32.png" "/cljs-logo.svg"} path) - (io/resource (subs path 1)) - :else nil) - local-path)] - (cond - local-path - (if-let [ext (some #(if (.endsWith path %) %) (keys ext->mime-type))] - (let [mime-type (ext->mime-type ext "text/plain") - encoding (mime-type->encoding mime-type "UTF-8")] - (server/send-and-close conn 200 (slurp local-path :encoding encoding) - mime-type encoding (and gzip? (= "text/javascript" mime-type)))) - (server/send-and-close conn 200 (slurp local-path) "text/plain")) - ;; "/index.html" doesn't exist, provide our own - (= path "/index.html") - (server/send-and-close conn 200 - (default-index (or output-to (str output-dir "/main.js"))) "text/html" "UTF-8") - (= path (str "/" output-dir "/main.js") ) - (let [closure-defines (-> `{clojure.browser.repl/HOST ~host - clojure.browser.repl/PORT ~port} - cljsc/normalize-closure-defines - json/write-str)] + (if-let [ext (some #(if (.endsWith path %) %) (keys ext->mime-type))] + (let [mime-type (ext->mime-type ext "text/plain") + encoding (mime-type->encoding mime-type "UTF-8")] + (server/send-and-close conn 200 (slurp local-path :encoding encoding) + mime-type encoding (and gzip? (= "text/javascript" mime-type)))) + (server/send-and-close conn 200 (slurp local-path) "text/plain")) + ;; "/index.html" doesn't exist, provide our own + (= path "/index.html") (server/send-and-close conn 200 - (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - "var CLOSURE_NO_DEPS = true;\n" - "document.write('');\n" - "document.write('');\n" - (when (.exists (io/file output-dir "cljs_deps.js")) - "document.write('');\n") - "document.write('');\n" - "document.write('');\n") - "text/javascript" "UTF-8")) - :else (server/send-404 conn path))) - (server/send-404 conn path))) + (default-index (or output-to (str output-dir "/main.js"))) + "text/html" "UTF-8") + (= path (cond->> "/main.js" output-dir (str "/" output-dir ))) + (let [closure-defines (-> `{clojure.browser.repl/HOST ~host + clojure.browser.repl/PORT ~port} + cljsc/normalize-closure-defines + json/write-str)] + (server/send-and-close conn 200 + (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "var CLOSURE_NO_DEPS = true;\n" + "document.write('');\n" + "document.write('');\n" + (when (.exists (io/file output-dir "cljs_deps.js")) + "document.write('');\n") + "document.write('');\n" + "document.write('');\n") + "text/javascript" "UTF-8")) + :else (server/send-404 conn path))) + (server/send-404 conn path)))) (server/dispatch-on :get (fn [{:keys [path]} _ _] From 1969c36161b1000a36f28e5acb73d55916775d26 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 19:05:23 -0500 Subject: [PATCH 2993/4033] CLJS-2602: Flag to disable usage of AOT cache --- src/main/clojure/cljs/closure.clj | 37 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e7b75b8c7..ede3a6f2c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -170,7 +170,7 @@ :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs :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}) + :stable-names :ignore-js-module-exts :opts-cache :aot-cache}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -635,21 +635,23 @@ (util/clojurescript-version)) (util/changed? jar-file out-file)) ;; actually compile from JAR - (let [cache-path (cache-base-path (util/path jar-file) opts)] - (when-not (.exists (:output-file cacheable)) - (-compile (jar-file-to-disk jar-file cache-path opts) - (assoc opts :output-dir (util/path cache-path)))) - (doseq [[k ^File f] cacheable] - (when (.exists f) - (let [target (io/file (util/output-directory opts) - (-> (.getAbsolutePath f) - (string/replace (.getAbsolutePath cache-path) "") - (subs 1)))] - (when (and (or ana/*verbose* (:verbose opts)) (= :output-file k)) - (util/debug-prn (str "Copying cached " f " to " target))) - (util/mkdirs target) - (spit target (slurp f)) - (.setLastModified target (util/last-modified jar-file))))))) + (if-not (:aot-cache opts) + (-compile (jar-file-to-disk jar-file (util/output-directory opts) opts) opts) + (let [cache-path (cache-base-path (util/path jar-file) opts)] + (when-not (.exists (:output-file cacheable)) + (-compile (jar-file-to-disk jar-file cache-path opts) + (assoc opts :output-dir (util/path cache-path)))) + (doseq [[k ^File f] cacheable] + (when (.exists f) + (let [target (io/file (util/output-directory opts) + (-> (.getAbsolutePath f) + (string/replace (.getAbsolutePath cache-path) "") + (subs 1)))] + (when (and (or ana/*verbose* (:verbose opts)) (= :output-file k)) + (util/debug-prn (str "Copying cached " f " to " target))) + (util/mkdirs target) + (spit target (slurp f)) + (.setLastModified target (util/last-modified jar-file)))))))) ;; have to call compile-file as it includes more IJavaScript ;; information than ana/parse-ns for now (compile-file @@ -2274,6 +2276,9 @@ (nil? (:opts-cache opts)) (assoc :opts-cache "cljsc_opts.edn") + (nil? (:aot-cache opts)) + (assoc :aot-cache true) + (contains? opts :modules) (ensure-module-opts) From 1849c17df26bd26e0d91f96a010594884d102b16 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 19:25:17 -0500 Subject: [PATCH 2994/4033] CLJS-2601: Shared AOT cache: The file out/cljs/core.cljc does not exist fix, but include note that the strategy is unlikely to work for Windows --- src/main/clojure/cljs/closure.clj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ede3a6f2c..0bf7b8ff9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -581,6 +581,8 @@ ".cljs" ".aot_cache" (util/clojurescript-version) (build-affecting-options-sha path opts)))) +;; TODO: we use a URL to get a bunch of file paths, the forward slashes probably +;; aren't going to work for Windows (defn cacheable-files ([rsrc ext] (cacheable-files rsrc ext nil)) @@ -591,7 +593,11 @@ (into {} (map (fn [[k v]] - [k (io/file path (str name v))])) + [k (io/file path + (if (and (= "cljs/core$macros" name) + (= :source k)) + (str "cljs/core.cljc") + (str name v)))])) {:source (str "." ext) :output-file ".js" :source-map ".js.map" From b763d8dd342f9afc714fb57a50bb55179e361bb8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 19:39:22 -0500 Subject: [PATCH 2995/4033] fix the obvious cross platform issues with AOT cache --- src/main/clojure/cljs/closure.clj | 8 +++----- src/main/clojure/cljs/util.cljc | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0bf7b8ff9..b22ec2e7e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -581,22 +581,20 @@ ".cljs" ".aot_cache" (util/clojurescript-version) (build-affecting-options-sha path opts)))) -;; TODO: we use a URL to get a bunch of file paths, the forward slashes probably -;; aren't going to work for Windows (defn cacheable-files ([rsrc ext] (cacheable-files rsrc ext nil)) ([rsrc ext opts] (let [{:keys [ns]} (ana/parse-ns rsrc) path (cache-base-path (util/path rsrc) opts) - name (util/ns->relpath ns nil)] + name (util/ns->relpath ns nil File/separatorChar)] (into {} (map (fn [[k v]] [k (io/file path - (if (and (= "cljs/core$macros" name) + (if (and (= (str "cljs" File/separatorChar "core$macros") name) (= :source k)) - (str "cljs/core.cljc") + (str "cljs" File/separatorChar "core.cljc") (str name v)))])) {:source (str "." ext) :output-file ".js" diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 067f0cf5a..6564bd862 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -69,7 +69,9 @@ provide the file extension, defaults to :cljs." ([ns] (ns->relpath ns :cljs)) ([ns ext] - (cond-> (string/replace (munge-path ns) \. \/) + (ns->relpath ns ext \/)) + ([ns ext sep] + (cond-> (string/replace (munge-path ns) \. sep) ext (str "." (name ext))))) (defn ns->source From b23c77e230137840241a1ee3da45733813e647ac Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 2 Mar 2018 09:38:21 -0500 Subject: [PATCH 2996/4033] CLJS-2593: Integration test suite for cljs.main --- .travis.yml | 10 +++ script/test-cli | 8 ++ src/test/cljs_cli/cljs_cli/test.clj | 62 +++++++++++++++ src/test/cljs_cli/cljs_cli/test_runner.clj | 12 +++ src/test/cljs_cli/cljs_cli/util.clj | 93 ++++++++++++++++++++++ 5 files changed, 185 insertions(+) create mode 100755 script/test-cli create mode 100644 src/test/cljs_cli/cljs_cli/test.clj create mode 100644 src/test/cljs_cli/cljs_cli/test_runner.clj create mode 100644 src/test/cljs_cli/cljs_cli/util.clj diff --git a/.travis.yml b/.travis.yml index 6dc09f3a1..dc6476860 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ dist: trusty +git: + depth: 500 + language: node_js node_js: @@ -14,6 +17,7 @@ before_install: before_script: - script/bootstrap + - script/uberjar - mkdir -p builds/out-adv - bin/cljsc src/test/cljs "{:optimizations :advanced :output-wrapper true @@ -53,3 +57,9 @@ script: - grep '0 failures, 0 errors.' test-out.txt - script/test-self-parity | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt + - script/test-cli node | tee test-out.txt + - grep '0 failures, 0 errors.' test-out.txt + - script/test-cli nashorn | tee test-out.txt + - grep '0 failures, 0 errors.' test-out.txt + - script/test-cli rhino | tee test-out.txt + - grep '0 failures, 0 errors.' test-out.txt diff --git a/script/test-cli b/script/test-cli new file mode 100755 index 000000000..fecf455d8 --- /dev/null +++ b/script/test-cli @@ -0,0 +1,8 @@ +#!/bin/sh + +if [ ! -f target/cljs.jar ]; then + echo "Run script/uberjar first" + exit 1 +fi + +java -cp target/cljs.jar:src/test/cljs_cli clojure.main -m cljs-cli.test-runner "$@" diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj new file mode 100644 index 000000000..eba6c34c0 --- /dev/null +++ b/src/test/cljs_cli/cljs_cli/test.clj @@ -0,0 +1,62 @@ +(ns cljs-cli.test + (:require + [clojure.test :refer [deftest]] + [clojure.java.io :as io] + [clojure.java.shell :as shell :refer [with-sh-dir]] + [cljs-cli.util :refer [cljs-main output-is with-sources with-post-condition with-repl-env-filter]])) + +(deftest eval-test + (-> (cljs-main "-e" 3 "-e" nil "-e" 4) + (output-is 3 4))) + +(deftest init-test + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (def x 3)"} + (-> (cljs-main "-i" "src/foo/core.cljs" "-e" 'foo.core/x) + (output-is 3)))) + +(deftest main-test + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (defn -main [] (prn :hi))"} + (-> (cljs-main "-m" "foo.core") + (output-is :hi)))) + +(deftest command-line-args-test + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (prn *command-line-args*)"} + (-> (cljs-main "src/foo/core.cljs" "alpha" "beta" "gamma") + (output-is (pr-str '("alpha" "beta" "gamma")))))) + +(deftest command-line-args-empty-test + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (prn *command-line-args*)"} + (-> (cljs-main "src/foo/core.cljs") + (output-is nil)))) + +(deftest initial-ns-test + (-> (cljs-main "-e" "::foo") + (output-is ":cljs.user/foo"))) + +(deftest source-test + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (prn :hi)"} + (-> (cljs-main "src/foo/core.cljs") + (output-is :hi)))) + +(deftest compile-test + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (defn -main [] (prn :hi))"} + (with-post-condition (fn [dir] (.exists (io/file dir "out" "main.js"))) + (-> (cljs-main "-o" "out/main.js" "-c" "foo.core") + (output-is))))) + +(deftest run-optimized-node-test + (with-repl-env-filter #{"node"} + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (prn :hello-from-node)"} + (with-post-condition (fn [dir] + (= {:exit 0, :out ":hello-from-node\n", :err ""} + (with-sh-dir dir + (shell/sh "node" (str (io/file dir "out" "main.js")))))) + (-> (cljs-main "-t" "node" "-o" "out/main.js" "-O" "advanced" "-c" "foo.core") + (output-is)))))) diff --git a/src/test/cljs_cli/cljs_cli/test_runner.clj b/src/test/cljs_cli/cljs_cli/test_runner.clj new file mode 100644 index 000000000..ce647fc94 --- /dev/null +++ b/src/test/cljs_cli/cljs_cli/test_runner.clj @@ -0,0 +1,12 @@ +(ns cljs-cli.test-runner + (:require + [cljs-cli.test] + [cljs-cli.util])) + +(defn -main [& args] + (try + (binding [cljs-cli.util/*repl-env* (or (first args) "nashorn") + cljs-cli.util/*repl-opts* (second args)] + (clojure.test/run-tests 'cljs-cli.test)) + (finally + (shutdown-agents)))) diff --git a/src/test/cljs_cli/cljs_cli/util.clj b/src/test/cljs_cli/cljs_cli/util.clj new file mode 100644 index 000000000..f4f3aa4c4 --- /dev/null +++ b/src/test/cljs_cli/cljs_cli/util.clj @@ -0,0 +1,93 @@ +(ns cljs-cli.util + (:require + [clojure.string :as string] + [clojure.java.io :as io] + [clojure.java.shell :as shell] + [clojure.test :refer [is]]) + (:import + (java.io File) + (java.nio.file Files CopyOption) + (java.nio.file.attribute FileAttribute))) + +(def ^:dynamic *repl-env* "nashorn") +(def ^:dynamic *repl-env-filter* (constantly true)) +(def ^:dynamic *repl-opts* nil) +(def ^:dynamic *sources* nil) +(def ^:dynamic *post-condition* nil) + +(defmacro with-sources + [sources & body] + `(binding [*sources* ~sources] + ~@body)) + +(defmacro with-post-condition + [post-condition & body] + `(binding [*post-condition* ~post-condition] + ~@body)) + +(defmacro with-repl-env-filter + [repl-env-filter & body] + `(binding [*repl-env-filter* ~repl-env-filter] + ~@body)) + +(defn- ^File make-temp-dir [] + (.toFile (Files/createTempDirectory "cljs-cli-test" (make-array FileAttribute 0)))) + +(defn- delete-recursively [fname] + (doseq [f (reverse (file-seq (io/file fname)))] + (io/delete-file f))) + +(defn- copy-uberjar [^File dest] + (Files/copy (.toPath (io/file "target/cljs.jar")) (.toPath (io/file dest "cljs.jar")) (make-array CopyOption 0))) + +(defn- write-sources [temp-dir] + (let [qualified #(io/file temp-dir %)] + (run! #(io/make-parents (qualified %)) (keys *sources*)) + (run! (fn [[file source]] + (spit (qualified file) source)) + *sources*))) + +(defn- run-in-temp-dir [args] + (let [temp-dir (make-temp-dir)] + (try + (write-sources temp-dir) + (copy-uberjar temp-dir) + (let [result (shell/with-sh-dir temp-dir + #_(apply println "running:" args) + (apply shell/sh args))] + (when *post-condition* + (is (*post-condition* temp-dir))) + result) + (finally + (delete-recursively temp-dir))))) + +(defn form-cp [] + (string/join File/pathSeparator ["cljs.jar" "src"])) + +(defn cljs-main [& args] + (if (*repl-env-filter* *repl-env*) + (let [command-line-args (map str args)] + (run-in-temp-dir + (keep (fn [arg] + (when arg + (str arg))) + (into ["java" "-cp" (form-cp) "cljs.main" + "-re" *repl-env* + (when *repl-opts* "-ro") (when *repl-opts* *repl-opts*)] + command-line-args)))) + {:exit 0 :out "" :err ""})) + +(def ^:private expected-browser-err + "Compiling client js ...\nServing HTTP on localhost port 9000\nListening for browser REPL connect ...\n") + +(defn- maybe-print-result-err [{:keys [err]}] + (when (and (not (empty? err)) + (not (= expected-browser-err err))) + (binding [*out* *err*] + (println err)))) + +(defn output-is [result & expected-lines] + (is (zero? (:exit result))) + (maybe-print-result-err result) + (is (= (apply str (map print-str (interleave expected-lines (repeat "\n")))) + (:out result)))) From 69cd63c736a689fb20ef150b3795de85035a6589 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 20:38:23 -0500 Subject: [PATCH 2997/4033] in the REPL case, if :output-dir not supplied create a temporary directory --- src/main/clojure/cljs/cli.clj | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index c266f640d..724bd2a44 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -17,7 +17,7 @@ [cljs.compiler.api :as comp] [cljs.build.api :as build] [cljs.repl :as repl]) - (:import [java.io StringReader FileWriter] + (:import [java.io File StringReader FileWriter] [java.text BreakIterator] [java.util Locale])) @@ -236,17 +236,25 @@ is trying load some arbitrary ns." [opts] (dissoc opts :main :output-to)) +(defn temp-out-dir [] + (let [f (File/createTempFile "out" (Long/toString (System/nanoTime)))] + (.delete f) + (util/mkdirs f) + (util/path f))) + (defn- repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" [repl-env [_ & args] {:keys [repl-env-options options inits] :as cfg}] - (let [reopts (merge repl-env-options - (select-keys options [:output-to :output-dir])) - _ (when (or ana/*verbose* (:verbose options)) + (let [opts (cond-> options + (not (:output-dir options)) + (assoc :output-dir (temp-out-dir))) + reopts (merge repl-env-options (select-keys opts [:output-to :output-dir])) + _ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "REPL env options:" (pr-str reopts))) renv (apply repl-env (mapcat identity reopts))] (repl/repl* renv - (assoc (dissoc-entry-point-opts options) + (assoc (dissoc-entry-point-opts opts) :inits (into [{:type :init-forms From 2f9c703130c2da6038381bbcc2f311121d95d3f6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 20:44:26 -0500 Subject: [PATCH 2998/4033] default to temp directory in -m cases as well --- src/main/clojure/cljs/cli.clj | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 724bd2a44..c3fdc09a3 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -265,19 +265,22 @@ present" (defn default-main [repl-env {:keys [main script args repl-env-options options inits] :as cfg}] (env/ensure - (let [reopts (merge repl-env-options - (select-keys options [:output-to :output-dir])) - _ (when (or ana/*verbose* (:verbose options)) + (let [opts (cond-> options + (not (:output-dir options)) + (assoc :output-dir (temp-out-dir))) + reopts (merge repl-env-options + (select-keys opts [:output-to :output-dir])) + _ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "REPL env options:" (pr-str reopts))) renv (apply repl-env (mapcat identity reopts)) - coptsf (when-let [od (:output-dir options)] + coptsf (when-let [od (:output-dir opts)] (io/file od "cljsc_opts.edn")) copts (when (and coptsf (.exists coptsf)) (-> (edn/read-string (slurp coptsf)) (dissoc-entry-point-opts))) opts (merge copts (build/add-implicit-options - (merge (repl/repl-options renv) options)))] + (merge (repl/repl-options renv) opts)))] (binding [ana/*cljs-ns* 'cljs.user repl/*repl-opts* opts ana/*verbose* (:verbose opts) @@ -290,7 +293,7 @@ present" (repl/setup renv repl/*repl-opts*) ;; REPLs don't normally load cljs_deps.js (when (and coptsf (.exists coptsf)) - (let [depsf (io/file (:output-dir options) "cljs_deps.js")] + (let [depsf (io/file (:output-dir opts) "cljs_deps.js")] (when (.exists depsf) (repl/evaluate renv "cljs_deps.js" 1 (slurp depsf))))) (repl/evaluate-form renv (ana-api/empty-env) "" From 3ae14bc39f4b408b0fab4356b2d265e9d55c5ac7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 21:02:37 -0500 Subject: [PATCH 2999/4033] allow combining -c with -s --- src/main/clojure/cljs/cli.clj | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index c3fdc09a3..7e9731c93 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -381,6 +381,19 @@ present" *out* log-out] (build/watch path (dissoc opts :watch) cenv))))) +(defn- serve-opt + [_ [_ address-port & args] {:keys [options] :as cfg}] + (let [[host port] (if address-port + (string/split address-port #":") + ["localhost" 9000])] + (require 'cljs.repl.browser) + ((ns-resolve 'cljs.repl.browser 'serve) + {:host host + :port (if port + (cond-> port (string? port) Integer/parseInt) + 9000) + :output-dir (:output-dir options "out")}))) + (defn default-compile [repl-env {:keys [ns args options] :as cfg}] (let [env-opts (repl/repl-options (repl-env)) @@ -409,6 +422,7 @@ present" source (when (= :none (:optimizations opts :none)) (:uri (build/ns->location main-ns))) repl? (boolean (#{"-r" "--repl"} (first args))) + serve? (boolean (#{"-s" "--serve"} (first args))) cenv (env/default-compiler-env)] (if-let [path (:watch opts)] (if repl? @@ -417,26 +431,15 @@ present" (build/build source opts cenv)) (when repl? (repl-opt repl-env args - (assoc-in cfg [:options :compiler-env] cenv))))) + (assoc-in cfg [:options :compiler-env] cenv))) + (when serve? + (serve-opt repl-env args cfg)))) (defn- compile-opt [repl-env [_ ns & args] cfg] ((::compile (repl/-repl-options (repl-env)) default-compile) repl-env (merge cfg {:args args :ns ns}))) -(defn- serve-opt - [_ [_ address-port & args] {:keys [options] :as cfg}] - (let [[host port] (if address-port - (string/split address-port #":") - ["localhost" 9000])] - (require 'cljs.repl.browser) - ((ns-resolve 'cljs.repl.browser 'serve) - {:host host - :port (if port - (cond-> port (string? port) Integer/parseInt) - 9000) - :output-dir (:output-dir options "out")}))) - (defn get-options [commands k] (if (= :all k) (into (get-options commands :main) (get-options commands :init)) From 08db3b4c1c277d0b356aa5c20ebb569e978b09a5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 21:18:34 -0500 Subject: [PATCH 3000/4033] clarify that --compile can be followed by --repl or --serve --- src/main/clojure/cljs/cli.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 7e9731c93..d5908995a 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -43,6 +43,8 @@ For --main and --repl: The init options may be repeated and mixed freely, but must appear before any main option. +In the case of --compile you may supply --repl or --serve options afterwards. + Paths may be absolute or relative in the filesystem or relative to classpath. Classpath-relative paths have prefix of @ or @/") From 265f398c2eec62aef2c7daff108c89da865ac562 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 3 Mar 2018 23:38:29 -0500 Subject: [PATCH 3001/4033] CLJS-2598: Socket REPL does not compose with cljs.main --- src/main/clojure/cljs/server/browser.clj | 10 ++++++---- src/main/clojure/cljs/server/nashorn.clj | 10 ++++++---- src/main/clojure/cljs/server/node.clj | 10 ++++++---- src/main/clojure/cljs/server/rhino.clj | 10 ++++++---- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/server/browser.clj b/src/main/clojure/cljs/server/browser.clj index 4b9f9930b..f8e2a8d6c 100644 --- a/src/main/clojure/cljs/server/browser.clj +++ b/src/main/clojure/cljs/server/browser.clj @@ -11,8 +11,10 @@ [cljs.repl.browser :as browser] [cljs.core.server :as server])) -(defn repl [] - (repl/repl (browser/repl-env))) +(defn repl [{:keys [opts env-opts]}] + (repl/repl* (browser/repl-env* env-opts) opts)) -(defn prepl [] - (server/io-prepl :repl-env (browser/repl-env))) +(defn prepl [{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + (merge {:repl-env (browser/repl-env* env-opts)} opts)))) diff --git a/src/main/clojure/cljs/server/nashorn.clj b/src/main/clojure/cljs/server/nashorn.clj index f561951f4..1cb2fa855 100644 --- a/src/main/clojure/cljs/server/nashorn.clj +++ b/src/main/clojure/cljs/server/nashorn.clj @@ -11,8 +11,10 @@ [cljs.repl.nashorn :as nashorn] [cljs.core.server :as server])) -(defn repl [] - (repl/repl (nashorn/repl-env))) +(defn repl [{:keys [opts env-opts]}] + (repl/repl* (nashorn/repl-env* env-opts) opts)) -(defn prepl [] - (server/io-prepl :repl-env (nashorn/repl-env))) +(defn prepl [{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + (merge {:repl-env (nashorn/repl-env* env-opts)} opts)))) diff --git a/src/main/clojure/cljs/server/node.clj b/src/main/clojure/cljs/server/node.clj index a7d562f45..04fe24ac5 100644 --- a/src/main/clojure/cljs/server/node.clj +++ b/src/main/clojure/cljs/server/node.clj @@ -11,8 +11,10 @@ [cljs.repl.node :as node] [cljs.core.server :as server])) -(defn repl [] - (repl/repl (node/repl-env))) +(defn repl [{:keys [opts env-opts]}] + (repl/repl* (node/repl-env* env-opts) opts)) -(defn prepl [] - (server/io-prepl :repl-env (node/repl-env))) +(defn prepl [{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + (merge {:repl-env (node/repl-env* env-opts)} opts)))) diff --git a/src/main/clojure/cljs/server/rhino.clj b/src/main/clojure/cljs/server/rhino.clj index dace6e6c6..f6de4cee3 100644 --- a/src/main/clojure/cljs/server/rhino.clj +++ b/src/main/clojure/cljs/server/rhino.clj @@ -11,8 +11,10 @@ [cljs.repl.rhino :as rhino] [cljs.core.server :as server])) -(defn repl [] - (repl/repl (rhino/repl-env))) +(defn repl [{:keys [opts env-opts]}] + (repl/repl* (rhino/repl-env* env-opts) opts)) -(defn prepl [] - (server/io-prepl :repl-env (rhino/repl-env))) +(defn prepl [{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + (merge {:repl-env (rhino/repl-env* env-opts)} opts)))) From ff573d12d8f868c980585445ff39b957967b6dce Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 4 Mar 2018 01:51:38 -0500 Subject: [PATCH 3002/4033] CLJS-2607: Shared AOT cache: Analyzing cached files Make transit-clj a direct dep, to ensure we can load analysis quickly. Make analysis global AOT cache aware. This mostly benefits REPLs where there is not necessarily a build in dependency order and we have to analyze as we go. --- pom.template.xml | 15 +++--- src/main/clojure/cljs/analyzer.cljc | 74 +++++++++++++++++++++++++---- src/main/clojure/cljs/closure.clj | 37 +-------------- src/main/clojure/cljs/compiler.cljc | 11 ++--- 4 files changed, 76 insertions(+), 61 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index fd4d95199..e1a0240e9 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -53,10 +53,9 @@ 1.3.0-alpha3 - org.clojure - test.check - 0.10.0-alpha2 - test + com.cognitect + transit-clj + 0.8.300 org.clojure @@ -65,10 +64,10 @@ - com.cognitect - transit-clj - 0.8.300 - provided + org.clojure + test.check + 0.10.0-alpha2 + test org.clojure diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cb736c40f..9e922a620 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3833,6 +3833,48 @@ ([format] (if (and (= format :transit) @transit) "json" "edn")))) +#?(:clj + (defn build-affecting-options [opts] + (select-keys opts + [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target + :cache-key :checked-arrays :language-out]))) + +#?(:clj + (defn build-affecting-options-sha [path opts] + (let [m (assoc (build-affecting-options opts) :path path)] + (util/content-sha (pr-str m) 7)))) + +#?(:clj + (defn ^File cache-base-path + ([path] + (cache-base-path path nil)) + ([path opts] + (io/file (System/getProperty "user.home") + ".cljs" ".aot_cache" (util/clojurescript-version) + (build-affecting-options-sha path opts))))) + +#?(:clj + (defn cacheable-files + ([rsrc ext] + (cacheable-files rsrc ext nil)) + ([rsrc ext opts] + (let [{:keys [ns]} (parse-ns rsrc) + path (cache-base-path (util/path rsrc) opts) + name (util/ns->relpath ns nil File/separatorChar)] + (into {} + (map + (fn [[k v]] + [k (io/file path + (if (and (= (str "cljs" File/separatorChar "core$macros") name) + (= :source k)) + (str "cljs" File/separatorChar "core.cljc") + (str name v)))])) + {:source (str "." ext) + :output-file ".js" + :source-map ".js.map" + :analysis-cache-edn (str "." ext ".cache.edn") + :analysis-cache-json (str "." ext ".cache.json")}))))) + #?(:clj (defn cache-file "Given a ClojureScript source file returns the read/write path to the analysis @@ -3840,8 +3882,10 @@ ([src] (cache-file src "out")) ([src output-dir] (cache-file src (parse-ns src) output-dir)) ([src ns-info output-dir] - (cache-file src (parse-ns src) output-dir :read)) + (cache-file src ns-info output-dir :read nil)) ([src ns-info output-dir mode] + (cache-file src ns-info output-dir mode nil)) + ([src ns-info output-dir mode opts] {:pre [(map? ns-info)]} (let [ext (cache-analysis-ext)] (if-let [core-cache @@ -3849,9 +3893,15 @@ (= (:ns ns-info) 'cljs.core) (io/resource (str "cljs/core.cljs.cache.aot." ext)))] core-cache - (let [target-file (util/to-target-file output-dir ns-info - (util/ext (:source-file ns-info)))] - (io/file (str target-file ".cache." ext)))))))) + (let [aot-cache-file + (when (util/url? src) + ((keyword (str "analysis-cache-" ext)) + (cacheable-files src (util/ext src) opts)))] + (if (and aot-cache-file (.exists ^File aot-cache-file)) + aot-cache-file + (let [target-file (util/to-target-file output-dir ns-info + (util/ext (:source-file ns-info)))] + (io/file (str target-file ".cache." ext)))))))))) #?(:clj (defn requires-analysis? @@ -3861,8 +3911,10 @@ ([src] (requires-analysis? src "out")) ([src output-dir] (let [cache (cache-file src output-dir)] - (requires-analysis? src cache output-dir))) + (requires-analysis? src cache output-dir nil))) ([src cache output-dir] + (requires-analysis? src cache output-dir nil)) + ([src cache output-dir opts] (cond (util/url? cache) (let [path (.getPath ^URL cache)] @@ -3876,10 +3928,12 @@ true :else - (let [out-src (util/to-target-file output-dir (parse-ns src))] - (if (not (.exists out-src)) + (let [out-src (util/to-target-file output-dir (parse-ns src)) + cache-src (:output-file (cacheable-files src (util/ext src) opts))] + (if (and (not (.exists out-src)) + (not (.exists ^File cache-src))) true - (util/changed? src cache))))))) + (or (not cache) (util/changed? src cache)))))))) #?(:clj (defn- get-spec-vars @@ -4049,9 +4103,9 @@ (.getPath ^File res) (.getPath ^URL res)) cache (when (:cache-analysis opts) - (cache-file res ns-info output-dir))] + (cache-file res ns-info output-dir :read opts))] (when-not (get-in @env/*compiler* [::namespaces (:ns ns-info) :defs]) - (if (or skip-cache (not cache) (requires-analysis? res output-dir)) + (if (or skip-cache (not cache) (requires-analysis? res cache output-dir opts)) (binding [*cljs-ns* 'cljs.user *cljs-file* path reader/*alias-map* (or reader/*alias-map* {})] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b22ec2e7e..36fbfbd8f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -569,39 +569,6 @@ (map compiled-file (comp/compile-root src-dir out-dir opts)))) -(defn build-affecting-options-sha [path opts] - (let [m (assoc (comp/build-affecting-options opts) :path path)] - (util/content-sha (pr-str m) 7))) - -(defn ^File cache-base-path - ([path] - (cache-base-path path nil)) - ([path opts] - (io/file (System/getProperty "user.home") - ".cljs" ".aot_cache" (util/clojurescript-version) - (build-affecting-options-sha path opts)))) - -(defn cacheable-files - ([rsrc ext] - (cacheable-files rsrc ext nil)) - ([rsrc ext opts] - (let [{:keys [ns]} (ana/parse-ns rsrc) - path (cache-base-path (util/path rsrc) opts) - name (util/ns->relpath ns nil File/separatorChar)] - (into {} - (map - (fn [[k v]] - [k (io/file path - (if (and (= (str "cljs" File/separatorChar "core$macros") name) - (= :source k)) - (str "cljs" File/separatorChar "core.cljc") - (str name v)))])) - {:source (str "." ext) - :output-file ".js" - :source-map ".js.map" - :analysis-cache-edn (str "." ext ".cache.edn") - :analysis-cache-json (str "." ext ".cache.json")})))) - (defn ^String path-from-jarfile "Given the URL of a file within a jar, return the path of the file from the root of the jar." @@ -632,7 +599,7 @@ [jar-file {:keys [output-file] :as opts}] (let [out-file (when output-file (io/file (util/output-directory opts) output-file)) - cacheable (cacheable-files jar-file (util/ext jar-file) opts)] + cacheable (ana/cacheable-files jar-file (util/ext jar-file) opts)] (when (or (nil? out-file) (not (.exists ^File out-file)) (not= (util/compiled-by-version out-file) @@ -641,7 +608,7 @@ ;; actually compile from JAR (if-not (:aot-cache opts) (-compile (jar-file-to-disk jar-file (util/output-directory opts) opts) opts) - (let [cache-path (cache-base-path (util/path jar-file) opts)] + (let [cache-path (ana/cache-base-path (util/path jar-file) opts)] (when-not (.exists (:output-file cacheable)) (-compile (jar-file-to-disk jar-file cache-path opts) (assoc opts :output-dir (util/path cache-path)))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index de0a297dc..e743dfbd9 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1288,11 +1288,6 @@ (defn url-path [^File f] (.getPath (.toURL (.toURI f))))) -(defn build-affecting-options [opts] - (select-keys opts - [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target - :cache-key :checked-arrays :language-out])) - #?(:clj (defn compiled-by-string ([] @@ -1303,7 +1298,7 @@ (str "// Compiled by ClojureScript " (util/clojurescript-version) (when opts - (str " " (pr-str (build-affecting-options opts)))))))) + (str " " (pr-str (ana/build-affecting-options opts)))))))) #?(:clj (defn cached-core [ns ext opts] @@ -1491,8 +1486,8 @@ (and version (not= version version'))) (and opts (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns))) - (not= (build-affecting-options opts) - (build-affecting-options (util/build-options dest)))) + (not= (ana/build-affecting-options opts) + (ana/build-affecting-options (util/build-options dest)))) (and opts (:source-map opts) (if (= (:optimizations opts) :none) (not (.exists (io/file (str (.getPath dest) ".map")))) From ace7e1f2642b5c555059067241725010495d25c8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 4 Mar 2018 03:19:30 -0500 Subject: [PATCH 3003/4033] CLJS-2608: Shared AOT cache: Browser REPL compiling when build-affecting opt specified Consolidate require-compilation? check --- src/main/clojure/cljs/closure.clj | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 36fbfbd8f..50c7315b8 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -592,8 +592,6 @@ (.setLastModified ^File out-file (util/last-modified url)) out-file))) -;; TODO: it would be nice if we could consolidate requires-compilation? -;; logic - David (defn compile-from-jar "Compile a file from a jar if necessary. Returns IJavaScript." [jar-file {:keys [output-file] :as opts}] @@ -601,10 +599,7 @@ (io/file (util/output-directory opts) output-file)) cacheable (ana/cacheable-files jar-file (util/ext jar-file) opts)] (when (or (nil? out-file) - (not (.exists ^File out-file)) - (not= (util/compiled-by-version out-file) - (util/clojurescript-version)) - (util/changed? jar-file out-file)) + (comp/requires-compilation? jar-file out-file opts)) ;; actually compile from JAR (if-not (:aot-cache opts) (-compile (jar-file-to-disk jar-file (util/output-directory opts) opts) opts) From f4751268c21bc54e57fc4d8b433a2ebe9cf1a6f8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 4 Mar 2018 09:13:41 -0500 Subject: [PATCH 3004/4033] CLJS-2603: Shared AOT cache: Failure if home directory not writable --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 50c7315b8..2d5c99035 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -601,7 +601,8 @@ (when (or (nil? out-file) (comp/requires-compilation? jar-file out-file opts)) ;; actually compile from JAR - (if-not (:aot-cache opts) + (if (or (not (:aot-cache opts)) + (not (.canWrite (io/file (System/getProperty "user.home"))))) (-compile (jar-file-to-disk jar-file (util/output-directory opts) opts) opts) (let [cache-path (ana/cache-base-path (util/path jar-file) opts)] (when-not (.exists (:output-file cacheable)) From 6613c7b05fd79aa3698ab413dac7f39cc922d910 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 4 Mar 2018 09:39:11 -0500 Subject: [PATCH 3005/4033] CLJS-2609: Shared AOT cache: New :aot-cache flag doesn't appear to work for browser RPL --- src/main/clojure/cljs/repl/browser.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 934eec08c..b1869f5f5 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -308,9 +308,8 @@ (spit target (build/build '[(require '[clojure.browser.repl.preload])] - {:optimizations :none - :output-dir output-dir - :opts-cache "brepl_opts.edn"})))) + (merge (select-keys opts cljsc/known-opts) + {:opts-cache "brepl_opts.edn"}))))) (repl/err-out (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) (println "Listening for browser REPL connect ...")) From 6f8422ccfd978e7d6ac0612e9c01addd7b0396dd Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 4 Mar 2018 09:10:11 -0500 Subject: [PATCH 3006/4033] CLJS-2606: Shared AOT cache: Don't cache artifacts built from unbuilt ClojureScript dep --- src/main/clojure/cljs/closure.clj | 2 +- src/main/clojure/cljs/util.cljc | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2d5c99035..cabe03ea9 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2244,7 +2244,7 @@ (assoc :opts-cache "cljsc_opts.edn") (nil? (:aot-cache opts)) - (assoc :aot-cache true) + (assoc :aot-cache (util/cljs-built-dep?)) (contains? opts :modules) (ensure-module-opts) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 6564bd862..acc7c8943 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -30,8 +30,10 @@ 0 (Math/abs (hash (slurp file))))) +(def ^:private synthethetic-version-prefix "0.0.") + (def ^:private synthetic-clojurescript-version - (delay (str "0.0." (reduce + (map file-hash (file-seq (main-src-directory))))))) + (delay (str synthethetic-version-prefix (reduce + (map file-hash (file-seq (main-src-directory))))))) (defn ^String clojurescript-version "Returns clojurescript version as a printable string." @@ -49,6 +51,14 @@ "-SNAPSHOT")) @synthetic-clojurescript-version)) +(defn- synthetic-version? [] + (string/starts-with? (clojurescript-version) synthethetic-version-prefix)) + +(defn cljs-built-dep? + "Returns true if ClojureScript itself is a built dep." + [] + (not (synthetic-version?))) + (defn ^String compiled-by-version [f] (with-open [reader (io/reader f)] (let [match (some->> reader line-seq first From 7d069bf0956ca24d020b5f0a7011381c39eb4f43 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 4 Mar 2018 14:55:19 -0500 Subject: [PATCH 3007/4033] Silence browser REPL, things are pretty zippy now --- src/main/clojure/cljs/repl/browser.clj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index b1869f5f5..a194c13c2 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -275,7 +275,6 @@ (browser-eval (slurp url))) (defn serve [{:keys [host port output-dir] :as opts}] - (println "Serving HTTP on" host "port" port) (binding [ordering (agent {:expecting nil :fns {}}) es (Executors/newFixedThreadPool 16) server/state (atom {:socket nil :connection nil :promised-conn nil})] @@ -292,7 +291,6 @@ ordering (:ordering repl-env) es (:es repl-env) server/state (:server-state repl-env)] - (repl/err-out (println "Compiling client js ...")) (swap! browser-state (fn [old] (assoc old :client-js @@ -310,9 +308,6 @@ '[(require '[clojure.browser.repl.preload])] (merge (select-keys opts cljsc/known-opts) {:opts-cache "brepl_opts.edn"}))))) - (repl/err-out - (println "Serving HTTP on" (:host repl-env) "port" (:port repl-env)) - (println "Listening for browser REPL connect ...")) (server/start repl-env) (when launch-browser (browse/browse-url From d9b0e681c8338395a8402028fd57bf4d7baf5d1c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 4 Mar 2018 15:11:36 -0500 Subject: [PATCH 3008/4033] server REPLs can take zero args --- src/main/clojure/cljs/server/browser.clj | 18 ++++++++++++------ src/main/clojure/cljs/server/nashorn.clj | 18 ++++++++++++------ src/main/clojure/cljs/server/node.clj | 18 ++++++++++++------ src/main/clojure/cljs/server/rhino.clj | 18 ++++++++++++------ 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/main/clojure/cljs/server/browser.clj b/src/main/clojure/cljs/server/browser.clj index f8e2a8d6c..8e5126e44 100644 --- a/src/main/clojure/cljs/server/browser.clj +++ b/src/main/clojure/cljs/server/browser.clj @@ -11,10 +11,16 @@ [cljs.repl.browser :as browser] [cljs.core.server :as server])) -(defn repl [{:keys [opts env-opts]}] - (repl/repl* (browser/repl-env* env-opts) opts)) +(defn repl + ([] + (repl nil)) + ([{:keys [opts env-opts]}] + (repl/repl* (browser/repl-env* env-opts) opts))) -(defn prepl [{:keys [opts env-opts]}] - (apply server/io-prepl - (mapcat identity - (merge {:repl-env (browser/repl-env* env-opts)} opts)))) +(defn prepl + ([] + (prepl nil)) + ([{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + (merge {:repl-env (browser/repl-env* env-opts)} opts))))) \ No newline at end of file diff --git a/src/main/clojure/cljs/server/nashorn.clj b/src/main/clojure/cljs/server/nashorn.clj index 1cb2fa855..fb059faac 100644 --- a/src/main/clojure/cljs/server/nashorn.clj +++ b/src/main/clojure/cljs/server/nashorn.clj @@ -11,10 +11,16 @@ [cljs.repl.nashorn :as nashorn] [cljs.core.server :as server])) -(defn repl [{:keys [opts env-opts]}] - (repl/repl* (nashorn/repl-env* env-opts) opts)) +(defn repl + ([] + (repl nil)) + ([{:keys [opts env-opts]}] + (repl/repl* (nashorn/repl-env* env-opts) opts))) -(defn prepl [{:keys [opts env-opts]}] - (apply server/io-prepl - (mapcat identity - (merge {:repl-env (nashorn/repl-env* env-opts)} opts)))) +(defn prepl + ([] + (prepl nil)) + ([{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + (merge {:repl-env (nashorn/repl-env* env-opts)} opts))))) diff --git a/src/main/clojure/cljs/server/node.clj b/src/main/clojure/cljs/server/node.clj index 04fe24ac5..441a942ac 100644 --- a/src/main/clojure/cljs/server/node.clj +++ b/src/main/clojure/cljs/server/node.clj @@ -11,10 +11,16 @@ [cljs.repl.node :as node] [cljs.core.server :as server])) -(defn repl [{:keys [opts env-opts]}] - (repl/repl* (node/repl-env* env-opts) opts)) +(defn repl + ([] + (repl nil)) + ([{:keys [opts env-opts]}] + (repl/repl* (node/repl-env* env-opts) opts))) -(defn prepl [{:keys [opts env-opts]}] - (apply server/io-prepl - (mapcat identity - (merge {:repl-env (node/repl-env* env-opts)} opts)))) +(defn prepl + ([] + (prepl nil)) + ([{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + (merge {:repl-env (node/repl-env* env-opts)} opts))))) diff --git a/src/main/clojure/cljs/server/rhino.clj b/src/main/clojure/cljs/server/rhino.clj index f6de4cee3..817866452 100644 --- a/src/main/clojure/cljs/server/rhino.clj +++ b/src/main/clojure/cljs/server/rhino.clj @@ -11,10 +11,16 @@ [cljs.repl.rhino :as rhino] [cljs.core.server :as server])) -(defn repl [{:keys [opts env-opts]}] - (repl/repl* (rhino/repl-env* env-opts) opts)) +(defn repl + ([] + (repl nil)) + ([{:keys [opts env-opts]}] + (repl/repl* (rhino/repl-env* env-opts) opts))) -(defn prepl [{:keys [opts env-opts]}] - (apply server/io-prepl - (mapcat identity - (merge {:repl-env (rhino/repl-env* env-opts)} opts)))) +(defn prepl + ([] + (prepl nil)) + ([{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + (merge {:repl-env (rhino/repl-env* env-opts)} opts))))) From ce3ecda34db0ed0a660b517dd7ef158983d58420 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 4 Mar 2018 17:22:25 -0500 Subject: [PATCH 3009/4033] use verbose flag names for consistency --- src/main/clojure/cljs/cli.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index d5908995a..e4072bbf7 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -499,7 +499,7 @@ present" "options") } ["-w" "--watch"] {:group ::compile :fn watch-opt :arg "path" - :doc "Continuously build, only effective with -c main option"} + :doc "Continuously build, only effective with the --compile main option"} ["-o" "--output-to"] {:group ::compile :fn output-to-opt :arg "file" :doc "Set the output compiled file"} @@ -507,7 +507,7 @@ present" :arg "level" :doc (str "Set optimization level, only effective with " - "-c main option. Valid values are: none, " + "--compile main option. Valid values are: none, " "whitespace, simple, advanced")} ["-t" "--target"] {:group ::main&compile :fn target-opt :arg "name" @@ -529,7 +529,7 @@ present" :doc "Call the -main function from a namespace with args"} ["-c" "--compile"] {:fn compile-opt :arg "ns" - :doc (str "Compile a namespace. If -r / --repl present after " + :doc (str "Compile a namespace. If --repl present after " "namespace will launch a REPL after the compile completes")} ["-s" "--serve"] {:fn serve-opt :arg "host:port" From 1a00846db500f7421983aa53a236c7fe4928a859 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 4 Mar 2018 20:02:02 -0500 Subject: [PATCH 3010/4033] CLJS-2611: Source mapping not working for Safari / synthetic tmp dir Additionally convey :output-dir if set --- src/main/clojure/cljs/cli.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index e4072bbf7..8a0991ebb 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -420,7 +420,8 @@ present" (dissoc :browser-repl) (not (:output-dir opts)) (assoc :output-dir "out"))) - cfg (update cfg :options merge (select-keys opts repl/known-repl-opts)) + convey (into [:output-dir] repl/known-repl-opts) + cfg (update cfg :options merge (select-keys opts convey)) source (when (= :none (:optimizations opts :none)) (:uri (build/ns->location main-ns))) repl? (boolean (#{"-r" "--repl"} (first args))) From 20e642b5bfef4d8ec1ba5b7f9e4f9982603df5c5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 5 Mar 2018 00:17:49 -0500 Subject: [PATCH 3011/4033] CLJS-2616: Shared AOT cache: SNAPSHOT JAR code cached Use requires-compilation? to determine if cache needs updating --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cabe03ea9..9a160189c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -605,7 +605,7 @@ (not (.canWrite (io/file (System/getProperty "user.home"))))) (-compile (jar-file-to-disk jar-file (util/output-directory opts) opts) opts) (let [cache-path (ana/cache-base-path (util/path jar-file) opts)] - (when-not (.exists (:output-file cacheable)) + (when (comp/requires-compilation? jar-file (:output-file cacheable) opts) (-compile (jar-file-to-disk jar-file cache-path opts) (assoc opts :output-dir (util/path cache-path)))) (doseq [[k ^File f] cacheable] From 92ccc3b90a6aef944620856002f44cd4683dd5f8 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 4 Mar 2018 20:39:44 -0500 Subject: [PATCH 3012/4033] CLJS-2610: Better stack trace if out is a synthetic tmp dir --- src/main/clojure/cljs/cli.clj | 4 ++-- src/main/clojure/cljs/repl.cljc | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 8a0991ebb..0207097e6 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -250,7 +250,7 @@ present" [repl-env [_ & args] {:keys [repl-env-options options inits] :as cfg}] (let [opts (cond-> options (not (:output-dir options)) - (assoc :output-dir (temp-out-dir))) + (assoc :output-dir (temp-out-dir) :temp-output-dir? true)) reopts (merge repl-env-options (select-keys opts [:output-to :output-dir])) _ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "REPL env options:" (pr-str reopts))) @@ -269,7 +269,7 @@ present" (env/ensure (let [opts (cond-> options (not (:output-dir options)) - (assoc :output-dir (temp-out-dir))) + (assoc :output-dir (temp-out-dir) :temp-output-dir? true)) reopts (merge repl-env-options (select-keys opts [:output-to :output-dir])) _ (when (or ana/*verbose* (:verbose opts)) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index fa17c1b53..dff6e315f 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -377,6 +377,13 @@ {}) mapped-frames)) [{}])))))) +(defn file-display + [file {:keys [output-dir temp-output-dir?]}] + (if temp-output-dir? + (let [canonicalize (fn [file] (.getCanonicalPath (io/file file)))] + (subs (canonicalize file) (inc (count (canonicalize output-dir))))) + file)) + (defn print-mapped-stacktrace "Given a vector representing the canonicalized JavaScript stacktrace print the ClojureScript stacktrace. See mapped-stacktrace." @@ -387,7 +394,7 @@ (err-out (println "\t" (str (when function (str function " ")) - "(" file (when line (str ":" line)) (when column (str ":" column)) ")")))))) + "(" (file-display file opts) (when line (str ":" line)) (when column (str ":" column)) ")")))))) (comment (def st (env/default-compiler-env)) From 61a4bdb31af8c2240ac8c83f37a23b4c3628b0ca Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 5 Mar 2018 18:29:04 -0500 Subject: [PATCH 3013/4033] fix io-prepl --- src/main/clojure/cljs/core/server.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index d63f24bba..11c5a2722 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -139,8 +139,7 @@ lock (Object.)] (prepl repl-env opts (readers/source-logging-push-back-reader *in* 1 "NO_SOURCE_FILE") - #(binding [*out* out, ;*flush-on-newline* true, *print-readably* true - ] + #(binding [*out* out, *flush-on-newline* true, *print-readably* true] (locking lock (prn (cond-> %1 (#{:ret :tap} (:tag %1)) From 81a39e581b8bbcb63444e1df7041c2414b0f3958 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 6 Mar 2018 07:54:09 -0500 Subject: [PATCH 3014/4033] better socket REPL support for browser REPL, if host & port are the same, repl-env and compilation environments are shared --- src/main/clojure/cljs/repl/browser.clj | 68 +++++++++++++----------- src/main/clojure/cljs/repl/server.clj | 3 +- src/main/clojure/cljs/server/browser.clj | 38 ++++++++++--- 3 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index a194c13c2..7791e46f7 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -286,32 +286,37 @@ ;; ============================================================================= ;; BrowserEnv -(defn setup [{:keys [working-dir launch-browser] :as repl-env} {:keys [output-dir] :as opts}] - (binding [browser-state (:browser-state repl-env) - ordering (:ordering repl-env) - es (:es repl-env) - server/state (:server-state repl-env)] - (swap! browser-state - (fn [old] - (assoc old :client-js - (cljsc/create-client-js-file - {:optimizations :simple - :output-dir working-dir} - (io/file working-dir "brepl_client.js"))))) - ;; TODO: this could be cleaner if compiling forms resulted in a - ;; :output-to file with the result of compiling those forms - David - (when (and output-dir (not (.exists (io/file output-dir "clojure" "browser" "repl" "preload.js")))) - (let [target (io/file output-dir "brepl_deps.js")] - (util/mkdirs target) - (spit target - (build/build - '[(require '[clojure.browser.repl.preload])] - (merge (select-keys opts cljsc/known-opts) - {:opts-cache "brepl_opts.edn"}))))) - (server/start repl-env) - (when launch-browser - (browse/browse-url - (str "http://" (:host repl-env) ":" (:port repl-env) "?rel=" (System/currentTimeMillis)))))) +(def lock (Object.)) + +(defn setup [{:keys [working-dir launch-browser server-state] :as repl-env} {:keys [output-dir] :as opts}] + (locking lock + (when-not (:socket @server-state) + (binding [browser-state (:browser-state repl-env) + ordering (:ordering repl-env) + es (:es repl-env) + server/state (:server-state repl-env)] + (swap! browser-state + (fn [old] + (assoc old :client-js + (cljsc/create-client-js-file + {:optimizations :simple + :output-dir working-dir} + (io/file working-dir "brepl_client.js"))))) + ;; TODO: this could be cleaner if compiling forms resulted in a + ;; :output-to file with the result of compiling those forms - David + (when (and output-dir (not (.exists (io/file output-dir "clojure" "browser" "repl" "preload.js")))) + (let [target (io/file output-dir "brepl_deps.js")] + (util/mkdirs target) + (spit target + (build/build + '[(require '[clojure.browser.repl.preload])] + (merge (select-keys opts cljsc/known-opts) + {:opts-cache "brepl_opts.edn"}))))) + (server/start repl-env) + (when launch-browser + (browse/browse-url + (str "http://" (:host repl-env) ":" (:port repl-env) "?rel=" (System/currentTimeMillis))))))) + (swap! server-state update :listeners inc)) (defrecord BrowserEnv [] repl/IJavaScriptEnv @@ -326,9 +331,11 @@ (-load [this provides url] (load-javascript this provides url)) (-tear-down [this] - (binding [server/state (:server-state this)] - (server/stop)) - (.shutdownNow (:es this))) + (let [server-state (:server-state this)] + (when (zero? (:listeners (swap! server-state update :listeners dec))) + (binding [server/state server-state] (server/stop)) + (when-not (.isShutdown (:es this)) + (.shutdownNow (:es this)))))) repl/IReplEnvOptions (-repl-options [this] {:browser-repl true @@ -377,7 +384,8 @@ (atom {:socket nil :connection nil - :promised-conn nil})} + :promised-conn nil + :listeners 0})} opts)) (defn repl-env diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index c0e422236..37a0b5e2b 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -224,4 +224,5 @@ (defn stop [] (when-let [sock (:socket @state)] - (.close sock))) + (when-not (.isClosed sock) + (.close sock)))) diff --git a/src/main/clojure/cljs/server/browser.clj b/src/main/clojure/cljs/server/browser.clj index 8e5126e44..96cfde9c6 100644 --- a/src/main/clojure/cljs/server/browser.clj +++ b/src/main/clojure/cljs/server/browser.clj @@ -7,20 +7,46 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.server.browser - (:require [cljs.repl :as repl] + (:require [cljs.env :as env] + [cljs.repl :as repl] [cljs.repl.browser :as browser] - [cljs.core.server :as server])) + [cljs.core.server :as server]) + (:import [java.net ServerSocket])) + +(defonce envs (atom {})) + +(defn env-opts->key [{:keys [host port]}] + [host port]) + +(defn stale? [{:keys [server-state] :as repl-env}] + (.isClosed ^ServerSocket (:socket @server-state))) + +(defn get-envs [env-opts] + (let [env-opts (merge {:host "localhost" :port 9000} env-opts) + k (env-opts->key env-opts)] + (swap! envs + #(cond-> % + (or (not (contains? % k)) + (stale? (get-in % [k 0]))) + (assoc k + [(browser/repl-env* env-opts) + (env/default-compiler-env)]))) + (get @envs k))) (defn repl ([] (repl nil)) ([{:keys [opts env-opts]}] - (repl/repl* (browser/repl-env* env-opts) opts))) + (let [[env cenv] (get-envs env-opts)] + (env/with-compiler-env cenv + (repl/repl* env opts))))) (defn prepl ([] (prepl nil)) ([{:keys [opts env-opts]}] - (apply server/io-prepl - (mapcat identity - (merge {:repl-env (browser/repl-env* env-opts)} opts))))) \ No newline at end of file + (let [[env cenv] (get-envs env-opts)] + (env/with-compiler-env cenv + (apply server/io-prepl + (mapcat identity + (merge {:repl-env env} opts))))))) \ No newline at end of file From 5d27f58bd4fd37f74d496ee56f3e8165affc8d2a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 6 Mar 2018 08:19:10 -0500 Subject: [PATCH 3015/4033] tweak --- src/main/clojure/cljs/server/browser.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/server/browser.clj b/src/main/clojure/cljs/server/browser.clj index 96cfde9c6..ec082883b 100644 --- a/src/main/clojure/cljs/server/browser.clj +++ b/src/main/clojure/cljs/server/browser.clj @@ -19,7 +19,9 @@ [host port]) (defn stale? [{:keys [server-state] :as repl-env}] - (.isClosed ^ServerSocket (:socket @server-state))) + (if-let [sock (:socket @server-state)] + (.isClosed ^ServerSocket sock) + false)) (defn get-envs [env-opts] (let [env-opts (merge {:host "localhost" :port 9000} env-opts) From ed385ad484f9c6b2471b0ee371fac805d8194c3e Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 6 Mar 2018 13:03:27 -0500 Subject: [PATCH 3016/4033] fully resolve MapEntry in defrecord expansion --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index f5185f06b..db9b791f8 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1839,7 +1839,7 @@ (not-empty (dissoc ~'__extmap k#)) nil))) 'ISeqable - `(~'-seq [this#] (seq (concat [~@(map #(core/list `MapEntry. (keyword %) % nil) base-fields)] + `(~'-seq [this#] (seq (concat [~@(map #(core/list 'cljs.core.MapEntry. (keyword %) % nil) base-fields)] ~'__extmap))) 'IIterable From 7aca40c4b6131b8e08153809a410c06bdfa567ab Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 5 Mar 2018 22:26:00 -0500 Subject: [PATCH 3017/4033] CLJS-2620: When REPL starts up, print ClojureScript version --- src/main/clojure/cljs/repl.cljc | 13 ++++++++++--- src/main/clojure/cljs/util.cljc | 5 ++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index dff6e315f..a6c4230a0 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -775,6 +775,10 @@ (doseq [file (comp/cljs-files-in src-dir)] (ana/analyze-file (str "file://" (.getAbsolutePath file)) opts))))) +(defn repl-title [] + (when-not (util/synthetic-version?) + (println "ClojureScript" (util/clojurescript-version)))) + (defn repl-quit-prompt [] (println "To quit, type:" :cljs/quit)) @@ -826,13 +830,13 @@ installed?))))) (defn repl* - [repl-env {:keys [init inits need-prompt quit-prompt prompt flush read eval print caught reader + [repl-env {:keys [init inits need-prompt title quit-prompt prompt flush read eval print caught reader print-no-newline source-map-inline wrap repl-requires compiler-env bind-err] :or {need-prompt #(if (readers/indexing-reader? *in*) (== (readers/get-column-number *in*) 1) (identity true)) - quit-prompt repl-quit-prompt + title repl-title prompt repl-prompt flush flush read repl-read @@ -978,7 +982,10 @@ (binding [*in* (if (true? (:source-map-inline opts)) *in* (reader))] - (quit-prompt) + (when title + (title)) + (when quit-prompt + (quit-prompt)) (prompt) (flush) (loop [] diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index acc7c8943..0dcc2d0dd 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -51,7 +51,10 @@ "-SNAPSHOT")) @synthetic-clojurescript-version)) -(defn- synthetic-version? [] +(defn synthetic-version? + "Returns true if clojurescript-version returns a synthetically-generated + version." + [] (string/starts-with? (clojurescript-version) synthethetic-version-prefix)) (defn cljs-built-dep? From 120a1b84b3e2e9274ebf22c2ae8dc28166e71c0d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 7 Mar 2018 18:06:28 -0500 Subject: [PATCH 3018/4033] CLJS-2627: Browser REPL race in send-to-eval remove the ad-hoc queue, use java.util.concurrent queues --- src/main/clojure/cljs/repl/browser.clj | 4 +-- src/main/clojure/cljs/repl/server.clj | 40 +++++++++++--------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 7791e46f7..6abbb9fcc 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -277,7 +277,7 @@ (defn serve [{:keys [host port output-dir] :as opts}] (binding [ordering (agent {:expecting nil :fns {}}) es (Executors/newFixedThreadPool 16) - server/state (atom {:socket nil :connection nil :promised-conn nil})] + server/state (atom {:socket nil})] (server/start (merge opts {:static-dir (cond-> ["." "out/"] output-dir (conj output-dir)) @@ -383,8 +383,6 @@ :server-state (atom {:socket nil - :connection nil - :promised-conn nil :listeners 0})} opts)) diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index 37a0b5e2b..b9747b2bb 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -13,38 +13,32 @@ java.io.InputStreamReader java.io.ByteArrayOutputStream java.util.zip.GZIPOutputStream - java.net.ServerSocket)) + java.net.ServerSocket + [java.util.concurrent ConcurrentLinkedQueue])) (def ^:dynamic state nil) +(def connq (ConcurrentLinkedQueue.)) +(def promiseq (ConcurrentLinkedQueue.)) (defn connection - "Promise to return a connection when one is available. If a - connection is not available, store the promise in server/state." + "Promise to return a connection when one is available. If no connection is + available put the promise into FIFO queue to get the next available + connection." [] - (let [p (promise) - conn (:connection @state)] + (let [p (promise) + conn (.poll connq)] (if (and conn (not (.isClosed conn))) - (do - (deliver p conn) - p) - (do - (swap! state (fn [old] (assoc old :promised-conn p))) - p)))) + (deliver p conn) + (.offer promiseq p)) + p)) (defn set-connection - "Given a new available connection, either use it to deliver the - connection which was promised or store the connection for later - use." + "Given a new available connection, poll the promise queue for and deliver + the connection. Otherwise put the connection into a FIFO queue." [conn] - (if-let [promised-conn (:promised-conn @state)] - (do - (swap! state - (fn [old] - (-> old - (assoc :connection nil) - (assoc :promised-conn nil)))) - (deliver promised-conn conn)) - (swap! state (fn [old] (assoc old :connection conn))))) + (if-let [p (.poll promiseq)] + (deliver p conn) + (.offer connq conn))) (defonce handlers (atom {})) From 114f5b5ae76b04be31d008c93604773cb7664796 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 7 Mar 2018 19:11:51 -0500 Subject: [PATCH 3019/4033] CLJS-2628: Browser REPL cannot safely accept new connections connection and set-connection are mutually exclusive, threads cannot enter them at the same time. Since every evaluation always provides a connection to use, what we currently have is sufficient for multiple REPLs to use the same browser REPL env --- src/main/clojure/cljs/repl/server.clj | 29 +++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index b9747b2bb..63d43c71c 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -14,31 +14,34 @@ java.io.ByteArrayOutputStream java.util.zip.GZIPOutputStream java.net.ServerSocket - [java.util.concurrent ConcurrentLinkedQueue])) + [java.util LinkedList])) (def ^:dynamic state nil) -(def connq (ConcurrentLinkedQueue.)) -(def promiseq (ConcurrentLinkedQueue.)) +(def connq (LinkedList.)) +(def promiseq (LinkedList.)) +(def lock (Object.)) (defn connection "Promise to return a connection when one is available. If no connection is - available put the promise into FIFO queue to get the next available + available put the promise into a FIFO queue to get the next available connection." [] - (let [p (promise) - conn (.poll connq)] - (if (and conn (not (.isClosed conn))) - (deliver p conn) - (.offer promiseq p)) - p)) + (locking lock + (let [p (promise) + conn (.poll connq)] + (if (and conn (not (.isClosed conn))) + (deliver p conn) + (.offer promiseq p)) + p))) (defn set-connection "Given a new available connection, poll the promise queue for and deliver the connection. Otherwise put the connection into a FIFO queue." [conn] - (if-let [p (.poll promiseq)] - (deliver p conn) - (.offer connq conn))) + (locking lock + (if-let [p (.poll promiseq)] + (deliver p conn) + (.offer connq conn)))) (defonce handlers (atom {})) From add416964575b50e563a6c282d57becc7ae3a2ec Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 6 Mar 2018 20:10:11 -0500 Subject: [PATCH 3020/4033] CLJS-2623: Simplify and edit the copy shown in the browser REPL --- src/main/clojure/cljs/repl/browser.clj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 6abbb9fcc..6f69910d6 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -106,17 +106,17 @@ "
" "" "" "
" - "

Welcome to the default index.html provided by the ClojureScript Browser REPL.

" - "

This page provides the evaluation environment for your Browser REPL and application.

" - "

You can quickly validate the connection by typing (js/alert \"Hello CLJS!\") into the " - "ClojureScript REPL that launched this page.

You can easily use your own HTML file to host your application " - "and REPL by providing your own index.html in the directory that you launched this REPL from.

" - "

Start with this template:

" + "

Welcome to the ClojureScript browser REPL.

" + "

This page hosts your REPL and application evaluation environment. " + "Validate the connection by typing (js/alert \"Hello CLJS!\") in the REPL.

" + "

To provide your own custom page, place an index.html file in " + "the REPL launch directory, starting with this template:

" "
"
     "<!DOCTYPE html>\n"
     "<html>\n"

From 90c0de3bb8d0c6d854c655bdb70da53d15871b76 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Wed, 7 Mar 2018 18:12:00 -0500
Subject: [PATCH 3021/4033] CLJS-2630: Browser REPL: If :launch-browser false,
 indicate connect URL

---
 src/main/clojure/cljs/repl/browser.clj | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 6f69910d6..35bae4b9d 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -313,9 +313,10 @@
                 (merge (select-keys opts cljsc/known-opts)
                   {:opts-cache "brepl_opts.edn"})))))
         (server/start repl-env)
-        (when launch-browser
-          (browse/browse-url
-            (str "http://" (:host repl-env) ":" (:port repl-env) "?rel=" (System/currentTimeMillis)))))))
+        (let [base-url (str "http://" (:host repl-env) ":" (:port repl-env))]
+          (if launch-browser
+            (browse/browse-url (str base-url "?rel=" (System/currentTimeMillis)))
+            (println "Waiting for browser to connect to" base-url "..."))))))
   (swap! server-state update :listeners inc))
 
 (defrecord BrowserEnv []

From b21b92d21ba3df58dc3ec91ef4537597fa7a0968 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 3 Mar 2018 10:12:48 -0500
Subject: [PATCH 3022/4033] CLJS-2600: Unbuilt synthetic version always ends up
 starting with 9s

---
 src/main/clojure/cljs/util.cljc | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc
index 0dcc2d0dd..646e6f520 100644
--- a/src/main/clojure/cljs/util.cljc
+++ b/src/main/clojure/cljs/util.cljc
@@ -28,12 +28,17 @@
 (defn- file-hash [file]
   (if (.isDirectory file)
     0
-    (Math/abs (hash (slurp file)))))
+    (hash (slurp file))))
 
 (def ^:private synthethetic-version-prefix "0.0.")
 
 (def ^:private synthetic-clojurescript-version
-  (delay (str synthethetic-version-prefix (reduce + (map file-hash (file-seq (main-src-directory)))))))
+  (delay (let [qualifier (fn [n]
+                           (if (== n Integer/MIN_VALUE)
+                             0
+                             (Math/abs n)))]
+           (str synthethetic-version-prefix 
+                (qualifier (reduce unchecked-add-int (map file-hash (file-seq (main-src-directory)))))))))
 
 (defn ^String clojurescript-version
   "Returns clojurescript version as a printable string."

From 42072af9d1c49ff39ad8d8e7cd3dee46123fb1c9 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Wed, 7 Mar 2018 23:10:43 -0500
Subject: [PATCH 3023/4033] CLJS-2613: Socket REPL: cannot require namespace
 using :npm-deps

need to pass opts, not merge
---
 src/main/clojure/cljs/server/browser.clj | 2 +-
 src/main/clojure/cljs/server/nashorn.clj | 3 ++-
 src/main/clojure/cljs/server/node.clj    | 3 ++-
 src/main/clojure/cljs/server/rhino.clj   | 3 ++-
 4 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/server/browser.clj b/src/main/clojure/cljs/server/browser.clj
index ec082883b..1c429d703 100644
--- a/src/main/clojure/cljs/server/browser.clj
+++ b/src/main/clojure/cljs/server/browser.clj
@@ -51,4 +51,4 @@
      (env/with-compiler-env cenv
        (apply server/io-prepl
          (mapcat identity
-           (merge {:repl-env env} opts)))))))
\ No newline at end of file
+           {:repl-env env :opts opts}))))))
\ No newline at end of file
diff --git a/src/main/clojure/cljs/server/nashorn.clj b/src/main/clojure/cljs/server/nashorn.clj
index fb059faac..cf91e770e 100644
--- a/src/main/clojure/cljs/server/nashorn.clj
+++ b/src/main/clojure/cljs/server/nashorn.clj
@@ -23,4 +23,5 @@
   ([{:keys [opts env-opts]}]
    (apply server/io-prepl
      (mapcat identity
-       (merge {:repl-env (nashorn/repl-env* env-opts)} opts)))))
+       {:repl-env (nashorn/repl-env* env-opts)
+        :opts opts}))))
diff --git a/src/main/clojure/cljs/server/node.clj b/src/main/clojure/cljs/server/node.clj
index 441a942ac..039fe8df0 100644
--- a/src/main/clojure/cljs/server/node.clj
+++ b/src/main/clojure/cljs/server/node.clj
@@ -23,4 +23,5 @@
   ([{:keys [opts env-opts]}]
    (apply server/io-prepl
      (mapcat identity
-       (merge {:repl-env (node/repl-env* env-opts)} opts)))))
+       {:repl-env (node/repl-env* env-opts)
+        :opts opts}))))
diff --git a/src/main/clojure/cljs/server/rhino.clj b/src/main/clojure/cljs/server/rhino.clj
index 817866452..d1d7df61a 100644
--- a/src/main/clojure/cljs/server/rhino.clj
+++ b/src/main/clojure/cljs/server/rhino.clj
@@ -23,4 +23,5 @@
   ([{:keys [opts env-opts]}]
    (apply server/io-prepl
      (mapcat identity
-       (merge {:repl-env (rhino/repl-env* env-opts)} opts)))))
+       {:repl-env (rhino/repl-env* env-opts)
+        :opts opts}))))

From 8b1f81fad6744d767e8d5d63aca9c6f729cec92d Mon Sep 17 00:00:00 2001
From: Erik Assum 
Date: Mon, 26 Feb 2018 19:32:12 +0100
Subject: [PATCH 3024/4033] CLJS-2432 Improve docstring for clj->js

- Mention new option :keyword-fn
- Name options map to aid tooling
---
 src/main/cljs/cljs/core.cljs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index f8e1b1a1b..3c55f9d7e 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -10525,9 +10525,13 @@ reduces them without incurring seq initialization"
 (defn clj->js
   "Recursively transforms ClojureScript values to JavaScript.
   sets/vectors/lists become Arrays, Keywords and Symbol become Strings,
-  Maps become Objects. Arbitrary keys are encoded to by key->js."
+  Maps become Objects. Arbitrary keys are encoded to by `key->js`.
+  Options is a key-value pair, where the only valid key is
+  :keyword-fn, which should point to a single-argument function to be
+  called on keyword keys. Default to `name`."
   [x & {:keys [keyword-fn]
-        :or   {keyword-fn name}}]
+        :or   {keyword-fn name}
+        :as options}]
   (letfn [(keyfn [k] (key->js k thisfn))
           (thisfn [x] (cond
                         (nil? x) nil

From be5548b7f0c2f49a2293160c577101a6dd3f5fb0 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Wed, 7 Mar 2018 23:30:37 -0500
Subject: [PATCH 3025/4033] typo

---
 src/main/clojure/cljs/cli.clj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 0207097e6..1716a027f 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -473,7 +473,7 @@ present"
 
 (def default-commands
   (add-commands
-    {:groups {::main&compile {:desc "init option"
+    {:groups {::main&compile {:desc "init options"
                               :pseudos
                               {["-re" "--repl-env"]
                                {:arg "env"

From b25d5203d5114b25a0590c9d5b1d7bad86353d19 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Thu, 8 Mar 2018 09:49:08 -0500
Subject: [PATCH 3026/4033] typo

---
 src/main/clojure/cljs/analyzer.cljc | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 9e922a620..cf909b2db 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -4000,9 +4000,8 @@
             analysis (dissoc (get-in @env/*compiler* [::namespaces ns]) :macros)]
         (case ext
           "edn"  (spit cache-file
-                   (str (when
-                     (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n"))
-                       (pr-str analysis)))
+                   (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n"
+                     (pr-str analysis)))
           "json" (when-let [{:keys [writer write]} @transit]
                    (write
                      (writer (FileOutputStream. cache-file) :json

From 717824ecf952d2e52e40bd9d8da23e556d5df502 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 8 Mar 2018 16:24:02 -0500
Subject: [PATCH 3027/4033] CLJS-2640: Just use quit-prompt to print the
 ClojureScript version

---
 src/main/clojure/cljs/repl.cljc | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index a6c4230a0..b2d2340b2 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -830,13 +830,13 @@
           installed?)))))
 
 (defn repl*
-  [repl-env {:keys [init inits need-prompt title quit-prompt prompt flush read eval print caught reader
+  [repl-env {:keys [init inits need-prompt quit-prompt prompt flush read eval print caught reader
                     print-no-newline source-map-inline wrap repl-requires
                     compiler-env bind-err]
              :or {need-prompt #(if (readers/indexing-reader? *in*)
                                 (== (readers/get-column-number *in*) 1)
                                 (identity true))
-                  title repl-title
+                  quit-prompt repl-title
                   prompt repl-prompt
                   flush flush
                   read repl-read
@@ -982,10 +982,7 @@
                  (binding [*in* (if (true? (:source-map-inline opts))
                                   *in*
                                   (reader))]
-                   (when title
-                     (title))
-                   (when quit-prompt
-                     (quit-prompt))
+                   (quit-prompt)
                    (prompt)
                    (flush)
                    (loop []

From fd8334a507d61ea3b4fa2f9373dc20dadd601736 Mon Sep 17 00:00:00 2001
From: Erik Assum 
Date: Thu, 8 Mar 2018 16:53:27 +0100
Subject: [PATCH 3028/4033] CLJS-2361 Lowercase x in docstring

---
 src/main/cljs/cljs/core.cljs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 3c55f9d7e..2bfe53642 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -11489,7 +11489,7 @@ reduces them without incurring seq initialization"
   (.-name ns-obj))
 
 (defn uri?
-  "Returns true X is a goog.Uri instance."
+  "Returns true x is a goog.Uri instance."
   {:added "1.9"}
   [x]
   (instance? goog.Uri x))

From eb6b391db674a7fb36286c0796a9856fd19c1853 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Thu, 8 Mar 2018 22:15:18 -0500
Subject: [PATCH 3029/4033] REPLs should use env/*compiler* if bound

---
 src/main/clojure/cljs/repl.cljc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index a6c4230a0..da6b765ec 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -877,7 +877,7 @@
                :print-no-newline print-no-newline
                :source-map-inline source-map-inline})))
         done? (atom false)]
-    (env/with-compiler-env (or compiler-env (env/default-compiler-env opts))
+    (env/with-compiler-env (or compiler-env env/*compiler* (env/default-compiler-env opts))
      (when (:source-map opts)
        (.start (Thread. (bound-fn [] (read-source-map "cljs/core.aot.js")))))
      (binding [*repl-env* repl-env

From e71471324502e4b8249591e82f0789c19370e456 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Thu, 8 Mar 2018 22:37:58 -0500
Subject: [PATCH 3030/4033] remove unused namespace requires

---
 src/main/clojure/cljs/repl.cljc | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 09f60100a..c3e6454a5 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -15,8 +15,6 @@
             [clojure.tools.reader :as reader]
             [clojure.tools.reader.reader-types :as readers]
             [cljs.tagged-literals :as tags]
-            [clojure.stacktrace :as trace]
-            [clojure.repl :as cljrepl]
             [clojure.edn :as edn]
             [cljs.util :as util]
             [cljs.compiler :as comp]

From 92cbedd12cd39d6bc3083a269a10fba9daa7fc40 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 9 Mar 2018 11:49:34 -0500
Subject: [PATCH 3031/4033] disable :aot-cache, limit to cljs.main usage for
 now

---
 src/main/clojure/cljs/cli.clj     | 12 +++++++++---
 src/main/clojure/cljs/closure.clj |  4 ++--
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 1716a027f..bd48a50e0 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -250,7 +250,9 @@ present"
   [repl-env [_ & args] {:keys [repl-env-options options inits] :as cfg}]
   (let [opts   (cond-> options
                  (not (:output-dir options))
-                 (assoc :output-dir (temp-out-dir) :temp-output-dir? true))
+                 (assoc :output-dir (temp-out-dir) :temp-output-dir? true)
+                 (not (contains? options :aot-cache))
+                 (assoc :aot-cache true))
         reopts (merge repl-env-options (select-keys opts [:output-to :output-dir]))
         _      (when (or ana/*verbose* (:verbose opts))
                  (util/debug-prn "REPL env options:" (pr-str reopts)))
@@ -269,7 +271,9 @@ present"
   (env/ensure
     (let [opts   (cond-> options
                    (not (:output-dir options))
-                   (assoc :output-dir (temp-out-dir) :temp-output-dir? true))
+                   (assoc :output-dir (temp-out-dir) :temp-output-dir? true)
+                   (not (contains? options :aot-cache))
+                   (assoc :aot-cache true))
           reopts (merge repl-env-options
                    (select-keys opts [:output-to :output-dir]))
           _      (when (or ana/*verbose* (:verbose opts))
@@ -419,7 +423,9 @@ present"
                      (= :advanced (:optimizations opts))
                      (dissoc :browser-repl)
                      (not (:output-dir opts))
-                     (assoc :output-dir "out")))
+                     (assoc :output-dir "out")
+                     (not (contains? opts :aot-cache))
+                     (assoc :aot-cache true)))
         convey   (into [:output-dir] repl/known-repl-opts)
         cfg      (update cfg :options merge (select-keys opts convey))
         source   (when (= :none (:optimizations opts :none))
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 9a160189c..560f25b7c 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2243,8 +2243,8 @@
       (nil? (:opts-cache opts))
       (assoc :opts-cache "cljsc_opts.edn")
 
-      (nil? (:aot-cache opts))
-      (assoc :aot-cache (util/cljs-built-dep?))
+      (not (contains? opts :aot-cache))
+      (assoc :aot-cache false)
 
       (contains? opts :modules)
       (ensure-module-opts)

From 233b42338c182e72391466eba2d00bae34271e58 Mon Sep 17 00:00:00 2001
From: Jannis Pohlmann 
Date: Fri, 9 Mar 2018 15:25:21 +0100
Subject: [PATCH 3032/4033] CLJS-2592: :npm-deps using ES6 modules with .mjs
 extensions are not detected correctly

This adds a new :package-json-resolution option that can be used to
control which package.json entry fields are considered during JS module
resolution. As of this commit, the option accepts three values:

    :webpack (for ["browser" "module" "main"])
    :nodejs (for ["main"])
    a custom vector of entries (e.g. ["main" "module"])

If the :package-json-resolution option is not set, we fall back to the
two default modes based on the build target:

  No target => :webpack
  Target :nodejs => :nodejs

The resulting behavior:

    {:target :nodejs}  => ["main"]
    {} => ["browser", "module", "main"]

    {:target 
     :package-json-resolution :webpack} => ["browser", "module", "main"]

    {:target 
     :package-json-resolution :nodejs} => ["main"]

    {:target 
     :package-json-resolution ["foo", "bar"]} => ["foo", "bar"]

Using the new :nodejs mode, this allows to import packages like graphql
and iterall via :npm-deps, which currently point their "module" entries
to unsupported .mjs files.
---
 src/main/cljs/cljs/module_deps.js             |  4 +-
 src/main/clojure/cljs/closure.clj             | 42 +++++++++++++--
 .../package_json_resolution_test/core.cljs    |  8 +++
 src/test/clojure/cljs/build_api_tests.clj     | 53 +++++++++++++++++++
 src/test/clojure/cljs/closure_tests.clj       | 35 ++++++++++++
 5 files changed, 134 insertions(+), 8 deletions(-)
 create mode 100644 src/test/cljs_build/package_json_resolution_test/core.cljs

diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js
index 0e62378a2..ad526a85a 100644
--- a/src/main/cljs/cljs/module_deps.js
+++ b/src/main/cljs/cljs/module_deps.js
@@ -8,9 +8,7 @@ let enhancedResolve = require('enhanced-resolve');
 
 let target = 'CLJS_TARGET';
 let filename = fs.realpathSync(path.resolve(__dirname, 'JS_FILE'));
-let mainFields = target === 'nodejs'
-      ? ['module', 'main']
-      : ['browser', 'module', 'main'];
+let mainFields = MAIN_ENTRIES;
 let aliasFields = target === 'nodejs' ? [] : ['browser'];
 
 // https://github.com/egoist/konan
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 560f25b7c..383338c91 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -1769,6 +1769,35 @@
                         (.getRequires input)))
         (.toSource closure-compiler ast-root)))))
 
+(defn- package-json-entries
+  "Takes options and returns a sequence with the desired order of package.json
+   entries for the given :package-json-resolution mode. If no mode is provided,
+   defaults to :webpack (if no target is set) and :nodejs (if the target is
+   :nodejs)."
+  [opts]
+  {:pre [(or (= (:package-json-resolution opts) :webpack)
+             (= (:package-json-resolution opts) :nodejs)
+             (and (sequential? (:package-json-resolution opts))
+                  (every? string? (:package-json-resolution opts)))
+             (not (contains? opts :package-json-resolution)))]}
+  (let [modes {:nodejs ["main"]
+               :webpack ["browser" "module" "main"]}]
+    (if-let [mode (:package-json-resolution opts)]
+      (if (sequential? mode) mode (get modes mode))
+      (case (:target opts)
+        :nodejs (:nodejs modes)
+        (:webpack modes)))))
+
+(comment
+  (= (package-json-entries {}) ["browser" "module" "main"])
+  (= (package-json-entries {:package-json-resolution :nodejs}) ["main"])
+  (= (package-json-entries {:package-json-resolution :webpack}) ["browser" "module" "main"])
+  (= (package-json-entries {:package-json-resolution ["foo" "bar" "baz"]}) ["foo" "bar" "baz"])
+  (= (package-json-entries {:target :nodejs}) ["main"])
+  (= (package-json-entries {:target :nodejs :package-json-resolution :nodejs}) ["main"])
+  (= (package-json-entries {:target :nodejs :package-json-resolution :webpack}) ["browser" "module" "main"])
+  (= (package-json-entries {:target :nodejs :package-json-resolution ["foo" "bar"]}) ["foo" "bar"]))
+
 (defn convert-js-modules
   "Takes a list JavaScript modules as an IJavaScript and rewrites them into a Google
   Closure-compatible form. Returns list IJavaScript with the converted module
@@ -1781,9 +1810,8 @@
                                    (.setLanguageIn (lang-key->lang-mode :ecmascript6))
                                    (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3)))
                                    (.setDependencyOptions (doto (DependencyOptions.)
-                                                            (.setDependencySorting true))))
-        _ (when (= (:target opts) :nodejs)
-            (.setPackageJsonEntryNames options ^List '("module", "main")))
+                                                            (.setDependencySorting true)))
+                                   (.setPackageJsonEntryNames ^List (package-json-entries opts)))
         closure-compiler (doto (make-closure-compiler)
                            (.init externs source-files options))
         _ (.parse closure-compiler)
@@ -2322,9 +2350,13 @@
      (when env/*compiler*
        (:options @env/*compiler*))))
   ([{:keys [file]} {:keys [target] :as opts}]
-   (let [code (-> (slurp (io/resource "cljs/module_deps.js"))
+   (let [main-entries (str "[" (->> (package-json-entries opts)
+                                    (map #(str "\"" % "\""))
+                                    (string/join ",")) "]")
+         code (-> (slurp (io/resource "cljs/module_deps.js"))
                 (string/replace "JS_FILE" (string/replace file "\\" "\\\\"))
-                (string/replace "CLJS_TARGET" (str "" (when target (name target)))))
+                (string/replace "CLJS_TARGET" (str "" (when target (name target))))
+                (string/replace "MAIN_ENTRIES" main-entries))
          proc (-> (ProcessBuilder. ["node" "--eval" code])
                 .start)
          is   (.getInputStream proc)
diff --git a/src/test/cljs_build/package_json_resolution_test/core.cljs b/src/test/cljs_build/package_json_resolution_test/core.cljs
new file mode 100644
index 000000000..a232412e1
--- /dev/null
+++ b/src/test/cljs_build/package_json_resolution_test/core.cljs
@@ -0,0 +1,8 @@
+(ns package-json-resolution-test.core
+  (:require [iterall]
+            [graphql]))
+
+(enable-console-print!)
+
+(println "Is collection:" (iterall/isCollection #js [1 2]))
+(println "GraphQL:" graphql)
diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj
index c615c76e9..9fe0155f5 100644
--- a/src/test/clojure/cljs/build_api_tests.clj
+++ b/src/test/clojure/cljs/build_api_tests.clj
@@ -573,3 +573,56 @@
       (let [content (slurp (-> opts :modules :c :output-to))]
         (testing "requires code.split.c"
           (is (test/document-write? content 'code.split.c)))))))
+
+(deftest test-cljs-2592
+  (test/delete-node-modules)
+  (spit (io/file "package.json") "{}")
+  (let [cenv (env/default-compiler-env)
+        dir (io/file "src" "test" "cljs_build" "package_json_resolution_test")
+        out (io/file (test/tmp-dir) "package_json_resolution_test")
+        opts {:main 'package-json-resolution-test.core
+              :output-dir (str out)
+              :output-to (str (io/file out "main.js"))
+              :optimizations :none
+              :install-deps true
+              :npm-deps {:iterall "1.2.2"
+                         :graphql "0.13.1"}
+              :package-json-resolution :nodejs
+              :closure-warnings {:check-types :off
+                                 :non-standard-jsdoc :off}}]
+    (test/delete-out-files out)
+    (build/build (build/inputs dir) opts cenv)
+    (testing "processes the iterall index.js"
+      (let [index-js (io/file out "node_modules/iterall/index.js")]
+        (is (.exists index-js))
+        (is (contains? (:js-module-index @cenv) "iterall"))
+        (is (re-find #"goog\.provide\(\"module\$.*\$node_modules\$iterall\$index\"\)" (slurp index-js)))))
+    (testing "processes the graphql index.js"
+      (let [index-js (io/file out "node_modules/graphql/index.js")
+            execution-index-js (io/file out "node_modules/graphql/execution/index.js")
+            ast-from-value-js (io/file out "node_modules/grapqhl/utilities/astFromValue.js")]
+        (is (.exists index-js))
+        (is (contains? (:js-module-index @cenv) "graphql"))
+        (is (re-find  #"goog\.provide\(\"module\$.*\$node_modules\$graphql\$index\"\)" (slurp index-js)))))
+    (testing "processes a nested index.js in graphql"
+      (let [nested-index-js (io/file out "node_modules/graphql/execution/index.js")]
+        (is (.exists nested-index-js))
+        (is (contains? (:js-module-index @cenv) "graphql/execution"))
+        (is (re-find  #"goog\.provide\(\"module\$.*\$node_modules\$graphql\$execution\$index\"\)" (slurp nested-index-js)))))
+    (testing "processes cross-package imports"
+      (let [ast-from-value-js (io/file out "node_modules/graphql/utilities/astFromValue.js")]
+        (is (.exists ast-from-value-js))
+        (is (re-find #"goog.require\(\"module\$.*\$node_modules\$iterall\$index\"\);" (slurp ast-from-value-js)))))
+    (testing "adds dependencies to cljs_deps.js"
+      (let [deps-js (io/file out "cljs_deps.js")]
+        (is (re-find #"goog\.addDependency\(\"..\/node_modules\/iterall\/index.js\"" (slurp deps-js)))
+        (is (re-find #"goog\.addDependency\(\"..\/node_modules\/graphql\/index.js\"" (slurp deps-js)))
+        (is (re-find #"goog\.addDependency\(\"..\/node_modules\/graphql\/execution/index.js\"" (slurp deps-js)))))
+    (testing "adds the right module names to the core.cljs build output"
+      (let [core-js (io/file out "package_json_resolution_test/core.js")]
+        (is (re-find #"goog\.require\('module\$.*\$node_modules\$iterall\$index'\);" (slurp core-js)))
+        (is (re-find #"module\$.+\$node_modules\$iterall\$index\[\"default\"\]\.isCollection" (slurp core-js)))
+        (is (re-find #"goog\.require\('module\$.*\$node_modules\$graphql\$index'\);" (slurp core-js)))
+        (is (re-find  #"module\$.+\$node_modules\$graphql\$index\[\"default\"\]" (slurp core-js))))))
+  (.delete (io/file "package.json"))
+  (test/delete-node-modules))
diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj
index 2d75a9fd8..5ce6c7381 100644
--- a/src/test/clojure/cljs/closure_tests.clj
+++ b/src/test/clojure/cljs/closure_tests.clj
@@ -442,3 +442,38 @@
     (.delete (io/file "package.json"))
     (test/delete-node-modules)
     (test/delete-out-files out)))
+
+(deftest test-cljs-2592
+  (spit (io/file "package.json") "{}")
+  (let [opts {:npm-deps {:iterall "1.2.2"
+                         :graphql "0.13.1"}
+              :package-json-resolution :nodejs}
+        out  (util/output-directory opts)]
+    (test/delete-node-modules)
+    (test/delete-out-files out)
+    (closure/maybe-install-node-deps! opts)
+    (let [modules (closure/index-node-modules ["iterall" "graphql"] opts)]
+      (is (true? (some (fn [module]
+                         (= module {:module-type :es6
+                                    :file (.getAbsolutePath (io/file "node_modules/iterall/index.js"))
+                                    :provides ["iterall"
+                                               "iterall/index.js"
+                                               "iterall/index"]}))
+                       modules)))
+      (is (true? (some (fn [module]
+                         (= module {:module-type :es6
+                                    :file (.getAbsolutePath (io/file "node_modules/graphql/index.js"))
+                                    :provides ["graphql"
+                                               "graphql/index.js"
+                                               "graphql/index"]}))
+                       modules)))
+      (is (true? (some (fn [module]
+                         (= module {:module-type :es6
+                                    :file (.getAbsolutePath (io/file "node_modules/graphql/execution/index.js"))
+                                    :provides ["graphql/execution/index.js"
+                                               "graphql/execution/index"
+                                               "graphql/execution"]}))
+                       modules))))
+    (.delete (io/file "package.json"))
+    (test/delete-node-modules)
+    (test/delete-out-files out)))

From 56c774be6227d51f4aa3a52571cb2640c7325db7 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 10 Mar 2018 11:56:42 -0500
Subject: [PATCH 3033/4033] CLJS-2055: Circular dependencies with parallel
 build cause the compiler get stuck

Add circular dep helper that works on compiler inputs, call before
starting parallel build
---
 src/main/clojure/cljs/closure.clj            |  1 +
 src/main/clojure/cljs/module_graph.cljc      | 22 ++++++
 src/test/clojure/cljs/module_graph_tests.clj | 76 ++++++++++++--------
 3 files changed, 69 insertions(+), 30 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 383338c91..2f5d4b603 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -947,6 +947,7 @@
             (recur ns-info)))))))
 
 (defn parallel-compile-sources [inputs compiler-stats opts]
+  (module-graph/validate-inputs inputs)
   (let [deque     (LinkedBlockingDeque. inputs)
         input-set (atom (into #{} (comp (remove nil?) (map :ns)) inputs))
         cnt       (+ 2 (.. Runtime getRuntime availableProcessors))
diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc
index 36690ba84..c938088dc 100644
--- a/src/main/clojure/cljs/module_graph.cljc
+++ b/src/main/clojure/cljs/module_graph.cljc
@@ -111,6 +111,28 @@
         provides))
     {} inputs))
 
+(defn validate-inputs*
+  [indexed path seen]
+  (let [ns (peek path)
+        {:keys [requires]} (get indexed ns)]
+    (doseq [ns' requires]
+      (if (contains? seen ns')
+        (throw
+          (ex-info
+            (str "Circular dependency detected "
+              (apply str (interpose " -> " (conj path ns'))))
+            {:cljs.closure/error :invalid-inputs}))
+        (validate-inputs* indexed (conj path ns') (conj seen ns'))))))
+
+(defn validate-inputs
+  "Throws on the presence of circular dependencies"
+  ([inputs]
+    (validate-inputs inputs [] #{}))
+  ([inputs path seen]
+   (let [indexed (index-inputs inputs)]
+     (doseq [[ns] (seq indexed)]
+       (validate-inputs* indexed (conj path ns) (conj seen ns))))))
+
 (defn ^:dynamic deps-for
   "Return all dependencies for x in a graph using deps-key."
   [x graph deps-key]
diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj
index 723ee45e1..bb4c73b3b 100644
--- a/src/test/clojure/cljs/module_graph_tests.clj
+++ b/src/test/clojure/cljs/module_graph_tests.clj
@@ -10,7 +10,8 @@
   (:require [clojure.test :as test :refer [deftest is testing]]
             [cljs.closure :as closure]
             [cljs.util :as util]
-            [cljs.module-graph :as module-graph]))
+            [cljs.module-graph :as module-graph])
+  (:import [clojure.lang ExceptionInfo]))
 
 (def opts {:output-dir "out"})
 
@@ -24,35 +25,38 @@
            :depends-on [:shared]
            :output-to (str output-dir "/page2.js")}})
 
-(defn inputs [{:keys [output-dir] :as opts}]
-  [{:provides '[goog]
-    :out-file (str output-dir "/goog/base.js")}
-   {:provides '[cljs.core]
-    :out-file (str output-dir "/cljs/core.js")}
-   {:provides ["cljs.reader"]
-    :requires ["cljs.core"]
-    :out-file (str output-dir "/cljs/reader.js")}
-   {:provides '[events "event.types"]
-    :requires ["cljs.core"]
-    :out-file (str output-dir "/events.js")}
-   {:provides '[shared.a]
-    :requires ["cljs.core"]
-    :out-file (str output-dir "/shared/a.js")}
-   {:provides '[shared.b]
-    :requires '[cljs.core]
-    :out-file (str output-dir "/shared/b.js")}
-   {:provides ["page1.a"]
-    :requires ["cljs.core" "cljs.reader" "events" 'shared.a]
-    :out-file (str output-dir "/page1/a.js")}
-   {:provides ["page1.b"]
-    :requires '[cljs.core shared.b]
-    :out-file (str output-dir "/page1/b.js")}
-   {:provides ["page2.a"]
-    :requires ["cljs.core" "events" 'shared.a]
-    :out-file (str output-dir "/page2/a.js")}
-   {:provides ["page2.b"]
-    :requires ['cljs.core 'shared.b]
-    :out-file (str output-dir "/page2/b.js")}])
+(defn inputs
+  ([]
+   (inputs {:output-dir "out"}))
+  ([{:keys [output-dir] :as opts}]
+   [{:provides '[goog]
+     :out-file (str output-dir "/goog/base.js")}
+    {:provides '[cljs.core]
+     :out-file (str output-dir "/cljs/core.js")}
+    {:provides ["cljs.reader"]
+     :requires ["cljs.core"]
+     :out-file (str output-dir "/cljs/reader.js")}
+    {:provides '[events "event.types"]
+     :requires ["cljs.core"]
+     :out-file (str output-dir "/events.js")}
+    {:provides '[shared.a]
+     :requires ["cljs.core"]
+     :out-file (str output-dir "/shared/a.js")}
+    {:provides '[shared.b]
+     :requires '[cljs.core]
+     :out-file (str output-dir "/shared/b.js")}
+    {:provides ["page1.a"]
+     :requires ["cljs.core" "cljs.reader" "events" 'shared.a]
+     :out-file (str output-dir "/page1/a.js")}
+    {:provides ["page1.b"]
+     :requires '[cljs.core shared.b]
+     :out-file (str output-dir "/page1/b.js")}
+    {:provides ["page2.a"]
+     :requires ["cljs.core" "events" 'shared.a]
+     :out-file (str output-dir "/page2/a.js")}
+    {:provides ["page2.b"]
+     :requires ['cljs.core 'shared.b]
+     :out-file (str output-dir "/page2/b.js")}]))
 
 (deftest test-add-cljs-base
   (is (true? (contains? (module-graph/add-cljs-base (modules opts)) :cljs-base))))
@@ -151,3 +155,15 @@
 (deftest test-module-for
   (is (= :page1 (module-graph/module-for 'page1.a (modules opts))))
   (is (= :page1 (module-graph/module-for "page1.a" (modules opts)))))
+
+(def circular-inputs
+  [{:provides ["foo.core"]
+    :requires ["bar.core"]}
+   {:provides ["bar.core"]
+    :requires ["baz.core"]}
+   {:provides ["baz.core"]
+    :requires ["foo.core"]}])
+
+(deftest test-circular-deps
+  (is (nil? (module-graph/validate-inputs (inputs))))
+  (is (thrown? ExceptionInfo (module-graph/validate-inputs circular-inputs))))

From cc2554d36a1ad7aaa9a5efd475d6ecc8e7cdde2f Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 10 Mar 2018 17:41:10 -0500
Subject: [PATCH 3034/4033] CLJS-2648: cljs.main: Failure running a script with
 browser REPL

---
 src/main/clojure/cljs/cli.clj | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index bd48a50e0..0eb87d985 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -314,7 +314,8 @@ present"
                   (repl/load-stream renv "" *in*)
 
                   (.exists (io/file script))
-                  (repl/load-file renv script)
+                  (with-open [stream (io/reader script)]
+                    (repl/load-stream renv script stream))
 
                   (string/starts-with? script "@/")
                   (if-let [rsrc (io/resource (subs script 2))]

From 728acc44fbabda7642669da5a365d455629e6199 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 11 Mar 2018 05:22:22 -0400
Subject: [PATCH 3035/4033] remove accidental silencing of serve

---
 src/main/clojure/cljs/repl/browser.clj | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 35bae4b9d..32419b898 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -275,6 +275,7 @@
   (browser-eval (slurp url)))
 
 (defn serve [{:keys [host port output-dir] :as opts}]
+  (println "Serving HTTP on" host "port" port)
   (binding [ordering (agent {:expecting nil :fns {}})
             es (Executors/newFixedThreadPool 16)
             server/state (atom {:socket nil})]

From 9dd4d41174079a568f27dcdf061568c985b2bd12 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 11 Mar 2018 14:09:53 -0400
Subject: [PATCH 3036/4033] CLJS-2641: cljs.spec.alpha/fdef with s/* is broken

The problem was while `instrument` redefs the var, the original fn
internals still self references through the namespace, thus
triggering an infinite loop by calling into the instrumented version
again.

This change relies on the fact that `apply` will always bind the
original fn via `Function.prototype.call` when invoking. In the dispatch
helpers we now check if `this` is bound to a Function - if so we invoke
methods on that value instead as it will be the *original* fn.
---
 src/main/clojure/cljs/core.cljc        | 16 ++++++++++++----
 src/test/cljs/cljs/spec/test_test.cljs | 11 ++++++++++-
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index db9b791f8..a1189b74e 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -3016,10 +3016,16 @@
                         `(fn
                            ([~restarg]
                             (let [~@(mapcat param-bind params)]
-                              (. ~sym (~(get-delegate) ~@params ~restarg))))))
+                              (this-as self#
+                                (if (identical? js/Function (type self#))
+                                  (. self# (~(get-delegate) ~@params ~restarg))
+                                  (. ~sym (~(get-delegate) ~@params ~restarg))))))))
                       `(fn
                          ([~restarg]
-                          (. ~sym (~(get-delegate) (seq ~restarg)))))))]
+                          (this-as self#
+                            (if (identical? js/Function (type self#))
+                              (. self# (~(get-delegate) (seq ~restarg)))
+                              (. ~sym (~(get-delegate) (seq ~restarg)))))))))]
        `(do
           (set! (. ~sym ~(get-delegate-prop))
             (fn (~(vec sig) ~@body)))
@@ -3058,8 +3064,10 @@
                (let [argseq# (when (< ~c-1 (alength args#))
                                (new ^::ana/no-resolve cljs.core/IndexedSeq
                                  (.slice args# ~c-1) 0 nil))]
-                 (. ~rname
-                   (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))))))
+                 (this-as self#
+                   (if (identical? js/Function (type self#))
+                     (. self# (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))
+                     (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))))))))
          ~(variadic-fn* rname method)
          ~(core/when emit-var? `(var ~name))))))
 
diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs
index 2953ecc3e..003eefa2b 100644
--- a/src/test/cljs/cljs/spec/test_test.cljs
+++ b/src/test/cljs/cljs/spec/test_test.cljs
@@ -72,4 +72,13 @@
   (stest/instrument `arities)
   (is (arities 1))
   (is (thrown? js/Error (arities "bad")))
-  (stest/unstrument `arities))
\ No newline at end of file
+  (stest/unstrument `arities))
+
+(defn foo [& args] args)
+(s/fdef foo :args (s/cat :args (s/* int?)))
+
+(deftest test-2641
+  (stest/instrument `foo)
+  (is (= [1 2 3] (foo 1 2 3)))
+  (is (thrown? js/Error (foo 1 :hello)))
+  (stest/unstrument `foo))

From 2f233d633f6dc44823105fd544685fc4da43ac6c Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 10 Mar 2018 10:33:29 -0500
Subject: [PATCH 3037/4033] CLJS-2645: cljs.main: failure with :closure-defines
 and non-Nashorn in :none mode

---
 src/main/clojure/cljs/repl/browser.clj | 4 +++-
 src/main/clojure/cljs/repl/node.clj    | 5 ++++-
 src/main/clojure/cljs/repl/rhino.clj   | 8 +++++++-
 src/test/cljs_cli/cljs_cli/test.clj    | 8 ++++++++
 4 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 32419b898..717c944bf 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -171,6 +171,7 @@
           (= path (cond->> "/main.js" output-dir (str "/" output-dir )))
           (let [closure-defines (-> `{clojure.browser.repl/HOST ~host
                                       clojure.browser.repl/PORT ~port}
+                                  (merge (:closure-defines @browser-state))
                                   cljsc/normalize-closure-defines
                                   json/write-str)]
             (server/send-and-close conn 200
@@ -302,7 +303,8 @@
               (cljsc/create-client-js-file
                 {:optimizations :simple
                  :output-dir working-dir}
-                (io/file working-dir "brepl_client.js")))))
+                (io/file working-dir "brepl_client.js"))
+              :closure-defines (:closure-defines opts))))
         ;; TODO: this could be cleaner if compiling forms resulted in a
         ;; :output-to file with the result of compiling those forms - David
         (when (and output-dir (not (.exists (io/file output-dir "clojure" "browser" "repl" "preload.js"))))
diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index 035aa0f36..64f96bdb6 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -196,7 +196,10 @@
                (when (or (not (contains? *loaded-libs* name)) reload)
                  (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
                  (js/CLOSURE_IMPORT_SCRIPT
-                   (unchecked-get (.. js/goog -dependencies_ -nameToPath) name))))))))))
+                   (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))))
+      (node-eval repl-env
+        (str "goog.global.CLOSURE_UNCOMPILED_DEFINES = "
+          (json/write-str (:closure-defines opts)) ";")))))
 
 (defrecord NodeEnv [host port path socket proc]
   repl/IReplEnvOptions
diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj
index b89f80902..3c7fc8573 100644
--- a/src/main/clojure/cljs/repl/rhino.clj
+++ b/src/main/clojure/cljs/repl/rhino.clj
@@ -10,6 +10,7 @@
   (:refer-clojure :exclude [load-file])
   (:require [clojure.string :as string]
             [clojure.java.io :as io]
+            [clojure.data.json :as json]
             [cljs.compiler :as comp]
             [cljs.closure :as closure]
             [cljs.analyzer :as ana]
@@ -154,7 +155,12 @@
              (when (or (not (contains? *loaded-libs* name)) reload)
                (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
                (js/CLOSURE_IMPORT_SCRIPT
-                 (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))))
+                 (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))
+
+    ;; set closure-defines
+    (rhino-eval repl-env "CLOSURE_UNCOMPILED_DEFINES" 1
+      (str "goog.global.CLOSURE_UNCOMPILED_DEFINES = "
+        (json/write-str (:closure-defines opts)) ";"))))
 
 ;; Catching errors and rethrowing in Rhino swallows the original trace
 ;; https://groups.google.com/d/msg/mozilla.dev.tech.js-engine.rhino/inMyVKhPq6M/cY39hX20_z8J
diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj
index eba6c34c0..9c8d31b4a 100644
--- a/src/test/cljs_cli/cljs_cli/test.clj
+++ b/src/test/cljs_cli/cljs_cli/test.clj
@@ -60,3 +60,11 @@
                                  (shell/sh "node" (str (io/file dir "out" "main.js"))))))
         (-> (cljs-main "-t" "node" "-o" "out/main.js" "-O" "advanced" "-c" "foo.core")
           (output-is))))))
+
+(deftest test-cljs-2645
+  (with-sources {"src/foo/core.cljs"
+                 "(ns foo.core) (goog-define configurable \"default-value\") (defn -main [& args] (println configurable))"}
+    (-> (cljs-main "-m" "foo.core")
+      (output-is "default-value"))
+    (-> (cljs-main "-co" "{:closure-defines {foo.core/configurable \"configured-value\"}}" "-m" "foo.core")
+      (output-is "configured-value"))))

From 7a5a65cb4d1eeca63746dd21e138ee9e56676ae4 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 11 Mar 2018 23:09:20 -0400
Subject: [PATCH 3038/4033] formatting

---
 src/main/clojure/cljs/repl/browser.clj | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 717c944bf..5ab6ec6ce 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -235,14 +235,14 @@
   (send-via es ordering add-in-order order f)
   (send-via es ordering run-in-order))
 
-(defmethod handle-post :print [{:keys [content order]} conn _ ]
+(defmethod handle-post :print [{:keys [content order]} conn _]
   (constrain-order order
     (fn []
       (print (read-string content))
       (.flush *out*)))
   (server/send-and-close conn 200 "ignore__"))
 
-(defmethod handle-post :result [{:keys [content order]} conn _ ]
+(defmethod handle-post :result [{:keys [content order]} conn _]
   (constrain-order order
     (fn []
       (return-value content)

From 2f6bda80e0e4e9801a81bfd025b6df67c685a3ae Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Tue, 13 Mar 2018 19:45:56 +0000
Subject: [PATCH 3039/4033] memoize circular dependency check

---
 src/main/clojure/cljs/module_graph.cljc | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc
index c938088dc..5a2573839 100644
--- a/src/main/clojure/cljs/module_graph.cljc
+++ b/src/main/clojure/cljs/module_graph.cljc
@@ -111,7 +111,7 @@
         provides))
     {} inputs))
 
-(defn validate-inputs*
+(defn ^:dynamic validate-inputs*
   [indexed path seen]
   (let [ns (peek path)
         {:keys [requires]} (get indexed ns)]
@@ -130,8 +130,9 @@
     (validate-inputs inputs [] #{}))
   ([inputs path seen]
    (let [indexed (index-inputs inputs)]
-     (doseq [[ns] (seq indexed)]
-       (validate-inputs* indexed (conj path ns) (conj seen ns))))))
+     (binding [validate-inputs* (memoize validate-inputs*)]
+       (doseq [[ns] (seq indexed)]
+         (validate-inputs* indexed (conj path ns) (conj seen ns)))))))
 
 (defn ^:dynamic deps-for
   "Return all dependencies for x in a graph using deps-key."

From ee68db250603d8251d97b8c01456a0b7832da92c Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Tue, 13 Mar 2018 10:45:35 -0400
Subject: [PATCH 3040/4033] CLJS-2639: Compiler crash when using aot cache with
 parallel compile

---
 src/main/clojure/cljs/closure.clj | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 2f5d4b603..570f43600 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2255,6 +2255,9 @@
         :cache-analysis (:cache-analysis opts true)
         :source-map (:source-map opts true))
 
+      (:aot-cache opts)
+      (assoc :cache-analysis true)
+
       (= optimizations :advanced)
       (cond->
         (not (false? (:static-fns opts))) (assoc :static-fns true)

From 424af2ac83c14e72d061f4d49de622b7a4792773 Mon Sep 17 00:00:00 2001
From: John Michael Newman III 
Date: Mon, 12 Mar 2018 22:40:33 -0400
Subject: [PATCH 3041/4033] CLJS-2653: REPL crash when mapping stacktrace in
 Chrome for js/blah

Added logic in cljs/repl.cljc/file-display to differentiate directories
coming from ./out/* vs coming from cli files
---
 src/main/clojure/cljs/repl.cljc | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index c3e6454a5..5e40aceec 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -336,7 +336,7 @@
       {:function name'
        :call     call
        :file     (if no-source-file?
-                   (str "NO_SOURCE_FILE"
+                   (str ""
                         (when file
                           (str " " file)))
                    (io/file file'))
@@ -378,8 +378,12 @@
 (defn file-display
   [file {:keys [output-dir temp-output-dir?]}]
   (if temp-output-dir?
-    (let [canonicalize (fn [file] (.getCanonicalPath (io/file file)))]
-      (subs (canonicalize file) (inc (count (canonicalize output-dir)))))
+    (let [canonicalize (fn [file] (.getCanonicalPath (io/file file)))
+          can-file (canonicalize file)
+          can-out (canonicalize output-dir)]
+      (if (.startsWith can-file can-out)
+        (subs can-file (inc (count can-out)))
+        (subs can-file (inc (.lastIndexOf can-file java.io.File/separator)))))
     file))
 
 (defn print-mapped-stacktrace
@@ -843,7 +847,7 @@
                   caught repl-caught
                   reader #(readers/source-logging-push-back-reader
                            *in*
-                           1 "NO_SOURCE_FILE")
+                           1 "")
                   print-no-newline print
                   source-map-inline true
                   repl-requires '[[cljs.repl :refer-macros [source doc find-doc apropos dir pst]]

From e4f2ac6b059395b4b559302aecafd9a7fd177e1f Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 11:02:21 +0000
Subject: [PATCH 3042/4033] CLJS-2656: module-graph/validate-inputs takes
 forever for complex projects

fix validate-inputs performance, memoize doesn't help because the
path & seen arguments are changing even for nses we've seen before.
Instead track the state of validated nses and skip ones we've seen
before. Because validate-inputs gets inputs in dep order, this is
pretty fast now.
---
 src/main/clojure/cljs/module_graph.cljc | 28 ++++++++++++++++---------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc
index 5a2573839..d08c05020 100644
--- a/src/main/clojure/cljs/module_graph.cljc
+++ b/src/main/clojure/cljs/module_graph.cljc
@@ -93,6 +93,11 @@
         (assoc ret module-name module')))
     {} modules))
 
+(defn normalize-input [input]
+  (-> input
+    (update :provides #(into [] (map (comp str comp/munge)) %))
+    (update :requires #(into [] (map (comp str comp/munge)) %))))
+
 (defn index-inputs
   "Index compiler inputs by :provides. If an input has multiple entries
   in :provides will result in multiple entries in the map. The keys will be munged
@@ -105,14 +110,12 @@
           (fn [provide]
             (vector
               (-> provide comp/munge str)
-              (-> input
-                (update :provides #(into [] (map (comp str comp/munge)) %))
-                (update :requires #(into [] (map (comp str comp/munge)) %))))))
+              (-> input normalize-input))))
         provides))
     {} inputs))
 
-(defn ^:dynamic validate-inputs*
-  [indexed path seen]
+(defn validate-inputs*
+  [indexed path seen validated]
   (let [ns (peek path)
         {:keys [requires]} (get indexed ns)]
     (doseq [ns' requires]
@@ -122,17 +125,22 @@
             (str "Circular dependency detected "
               (apply str (interpose " -> " (conj path ns'))))
             {:cljs.closure/error :invalid-inputs}))
-        (validate-inputs* indexed (conj path ns') (conj seen ns'))))))
+        (when-not (contains? @validated ns)
+          (validate-inputs* indexed (conj path ns') (conj seen ns') validated))))
+    (swap! validated conj ns)))
 
 (defn validate-inputs
   "Throws on the presence of circular dependencies"
   ([inputs]
     (validate-inputs inputs [] #{}))
   ([inputs path seen]
-   (let [indexed (index-inputs inputs)]
-     (binding [validate-inputs* (memoize validate-inputs*)]
-       (doseq [[ns] (seq indexed)]
-         (validate-inputs* indexed (conj path ns) (conj seen ns)))))))
+   (let [indexed   (index-inputs inputs)
+         validated (atom #{})]
+     (binding []
+       (doseq [{:keys [provides]} (map normalize-input inputs)]
+         (let [ns (first provides)]
+           (validate-inputs* indexed (conj path ns) (conj seen ns) validated)
+           (swap! validated conj ns)))))))
 
 (defn ^:dynamic deps-for
   "Return all dependencies for x in a graph using deps-key."

From 0fcc8fd7c2a8d0e679142142ff1578e6a9d96b68 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 14:53:04 +0000
Subject: [PATCH 3043/4033] if :modules default to :stable-names true

---
 src/main/clojure/cljs/closure.clj | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 570f43600..efd541e9d 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2228,8 +2228,13 @@
 
                (or (:closure-defines opts) (shim-process? opts))
                (update :closure-defines normalize-closure-defines)
+
                (:browser-repl opts)
-               (update-in [:preloads] (fnil conj []) 'clojure.browser.repl.preload))
+               (update-in [:preloads] (fnil conj []) 'clojure.browser.repl.preload)
+
+               (and (contains? opts :modules)
+                    (not (contains? opts :stable-names)))
+               (assoc :stable-names true))
         {:keys [libs foreign-libs externs]} (get-upstream-deps)
         emit-constants (or (and (= optimizations :advanced)
                                 (not (false? (:optimize-constants opts))))

From 6b9c8c1aa8e9802314d8591d252bd0818d4e79e7 Mon Sep 17 00:00:00 2001
From: Juho Teperi 
Date: Wed, 14 Mar 2018 00:10:32 +0200
Subject: [PATCH 3044/4033] CLJS-2658: Fix using linked node_modules

enhancedResolve has option which can be set to false to return the
original path instead of the path to which symlink points to.
---
 src/main/cljs/cljs/module_deps.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js
index ad526a85a..a9e33999c 100644
--- a/src/main/cljs/cljs/module_deps.js
+++ b/src/main/cljs/cljs/module_deps.js
@@ -65,7 +65,8 @@ let resolver = enhancedResolve.create({
     extensions: ['.js', '.json'],
     mainFields: mainFields,
     aliasFields: target === 'nodejs' ? [] : ['browser'],
-    moduleExtensions: ['.js', '.json']
+    moduleExtensions: ['.js', '.json'],
+    symlinks: false
 });
 
 let md = mdeps({

From ade4ab7f8ab0fc1c74c627de7b042cf8b33beb14 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 15:39:36 +0000
Subject: [PATCH 3045/4033] extend Compilable to Symbol

---
 src/main/clojure/cljs/closure.clj | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index efd541e9d..43019de35 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -661,6 +661,12 @@
   (-compile [this opts] (-compile (io/file this) opts))
   (-find-sources [this opts] (-find-sources (io/file this) opts))
 
+  clojure.lang.Symbol
+  (-compile [this opts]
+    (-compile (util/ns->source this) opts))
+  (-find-sources [this opts]
+    (-find-sources (util/ns->source this) opts))
+
   clojure.lang.PersistentVector
   (-compile [this opts] (compile-form-seq this))
   (-find-sources [this opts]
@@ -686,6 +692,8 @@
   (-compile '[(ns test.app (:require [goog.array :as array]))
               (defn plus-one [x] (inc x))]
             {})
+
+  (-find-sources 'cljs.core {})
   )
 
 (defn js-dependencies

From bbb1d7d6974f4e04055d7e01087221f099bdba8b Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 15:53:12 +0000
Subject: [PATCH 3046/4033] (WIP) refactor REPL to use same build pipeline as
 cljs.closure/build

---
 src/main/clojure/cljs/repl.cljc | 31 ++++++++++++-------------------
 1 file changed, 12 insertions(+), 19 deletions(-)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 5e40aceec..8fe2fa6e3 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -196,28 +196,21 @@
   only once."
   ([repl-env ns] (load-namespace repl-env ns nil))
   ([repl-env ns opts]
-   (let [ns (if (and (seq? ns)
-                     (= (first ns) 'quote))
-               (second ns)
-               ns)
-         ;; TODO: add pre-condition to source-on-disk, the
-         ;; source must supply at least :url - David
-         sources (binding [ana/*analyze-deps* false]
-                   (cljsc/add-dependencies
-                     (merge (env->opts repl-env) opts)
-                     {:requires [(name ns)] :type :seed}))
-         deps (->> sources
-                (remove (comp #{["goog"]} :provides))
-                (remove (comp #{:seed} :type))
-                (map #(select-keys % [:provides :url])))]
-     (cljsc/handle-js-modules opts sources env/*compiler*)
+   (let [ns (if (and (seq? ns) (= (first ns) 'quote)) (second ns)ns)
+         sources (-> (cljsc/-find-sources ns (merge (env->opts repl-env) opts))
+                   (cljsc/add-dependency-sources opts))
+         opts (cljsc/handle-js-modules opts sources env/*compiler*)
+         _ (swap! env/*compiler* update-in [:options] merge opts)
+         sources (-> sources deps/dependency-order
+                   (cljsc/compile-sources false opts)
+                   (#(map cljsc/add-core-macros-if-cljs-js %))
+                   (cljsc/add-js-sources opts) deps/dependency-order
+                   (->> (map #(cljsc/source-on-disk opts %)) doall))]
      (if (:output-dir opts)
        ;; REPLs that read from :output-dir just need to add deps,
        ;; environment will handle actual loading - David
        (let [sb (StringBuffer.)]
-         (doseq [source (->> sources
-                          (remove (comp #{:seed} :type))
-                          (map #(cljsc/source-on-disk opts %)))]
+         (doseq [source sources]
            (when (:repl-verbose opts)
              (println "Loading:" (:provides source)))
            ;; Need to get :requires and :provides from compiled source
@@ -233,7 +226,7 @@
            (println (.toString sb)))
          (-evaluate repl-env "" 1 (.toString sb)))
        ;; REPLs that stream must manually load each dep - David
-       (doseq [{:keys [url provides]} deps]
+       (doseq [{:keys [url provides]} sources]
          (-load repl-env provides url))))))
 
 (defn- load-dependencies

From e0c26cf5dd21e68bda28f66718c07916f8e1fdf6 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 16:21:40 +0000
Subject: [PATCH 3047/4033] add cljs.closure/find-sources

---
 src/main/clojure/cljs/closure.clj | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 43019de35..b83bde177 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -546,6 +546,11 @@
   [compilable opts]
   (-compile compilable opts))
 
+(defn find-sources
+  "Given a Compilable, find sources and return a sequence of IJavaScript."
+  [compilable opts]
+  (-find-sources compilable opts))
+
 (defn compile-file
   "Compile a single cljs file. If no output-file is specified, returns
   a string of compiled JavaScript. With an output-file option, the

From c2461b85a90f6d2529e32c70f2328d178729c349 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 16:23:24 +0000
Subject: [PATCH 3048/4033] make var public

---
 src/main/clojure/cljs/closure.clj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index b83bde177..c22602410 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -729,7 +729,7 @@
   (js-dependencies {:libs ["closure/library/third_party/closure"]} ["goog.dom.query"])
   )
 
-(defn- add-core-macros-if-cljs-js
+(defn add-core-macros-if-cljs-js
   "If a compiled entity is the cljs.js namespace, explicitly
   add the cljs.core macros namespace dependency to it."
   [compiled]

From ade19ed63b14b98eddd7d2268dba7550e99a7b1b Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 16:38:11 +0000
Subject: [PATCH 3049/4033] deprecate cljs.closure/add-dependencies, remove
 misleading cljs.closure ns docstring, remove comments referring to
 cljs.closure/add-dependencies

---
 src/main/clojure/cljs/build/api.clj |  2 +-
 src/main/clojure/cljs/closure.clj   | 77 +----------------------------
 2 files changed, 2 insertions(+), 77 deletions(-)

diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj
index 15fe56eed..c7c94d0dd 100644
--- a/src/main/clojure/cljs/build/api.clj
+++ b/src/main/clojure/cljs/build/api.clj
@@ -132,7 +132,7 @@
    (closure/source-for-namespace ns compiler-env)))
 
 (defn add-dependencies
-  "Given one or more IJavaScript objects in dependency order, produce
+  "DEPRECATED: Given one or more IJavaScript objects in dependency order, produce
   a new sequence of IJavaScript objects which includes the input list
   plus all dependencies in dependency order."
   [opts & ijss]
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index c22602410..559a96500 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -7,32 +7,6 @@
 ;   You must not remove this notice, or any other, from this software.
 
 (ns cljs.closure
-  "Compile ClojureScript to JavaScript with optimizations from Google
-   Closure Compiler producing runnable JavaScript.
-
-   The Closure Compiler (compiler.jar) must be on the classpath.
-
-   Use the 'build' function for end-to-end compilation.
-
-   build = find-sources -> add-dependencies -> compile -> optimize -> output
-
-   Two protocols are defined: IJavaScript and Compilable. The
-   Compilable protocol is satisfied by something which can return one
-   or more IJavaScripts.
-
-   With IJavaScript objects in hand, calling add-dependencies will
-   produce a sequence of IJavaScript objects which includes all
-   required dependencies from the Closure library and ClojureScript,
-   in dependency order. This function replaces the closurebuilder
-   tool.
-
-   The optimize function converts one or more IJavaScripts into a
-   single string of JavaScript source code using the Closure Compiler
-   API.
-
-   The produced output is either a single string of optimized
-   JavaScript or a deps file for use during development.
-  "
   (:refer-clojure :exclude [compile])
   (:require [cljs.util :as util :refer [distinct-by]]
             [cljs.core :as cljsm]
@@ -861,10 +835,8 @@
   (let [url (deps/to-url (constants-filename opts))]
     (javascript-file nil url [(str ana/constants-ns-sym)] ["cljs.core"])))
 
-;; Internally only REPLs use this. We do expose it in cljs.build.api - David
-
 (defn add-dependencies
-  "Given one or more IJavaScript objects in dependency order, produce
+  "DEPRECATED: Given one or more IJavaScript objects in dependency order, produce
   a new sequence of IJavaScript objects which includes the input list
   plus all dependencies in dependency order."
   [opts & inputs]
@@ -1070,30 +1042,6 @@
          (str "#!" (or hashbang "/usr/bin/env node") "\n"))
        (when preamble (preamble-from-paths preamble))))
 
-(comment
-  ;; add dependencies to literal js
-  (add-dependencies {} "goog.provide('test.app');\ngoog.require('cljs.core');")
-  (add-dependencies {} "goog.provide('test.app');\ngoog.require('goog.array');")
-  (add-dependencies {} (str "goog.provide('test.app');\n"
-                            "goog.require('goog.array');\n"
-                            "goog.require('clojure.set');"))
-  ;; add dependencies with external lib
-  (add-dependencies {:libs ["closure/library/third_party/closure"]}
-                    (str "goog.provide('test.app');\n"
-                         "goog.require('goog.array');\n"
-                         "goog.require('goog.dom.query');"))
-  ;; add dependencies with foreign lib
-  (add-dependencies {:foreign-libs [{:file "samples/hello/src/hello/core.cljs"
-                                     :provides ["example.lib"]}]}
-                    (str "goog.provide('test.app');\n"
-                         "goog.require('example.lib');\n"))
-  ;; add dependencies to a JavaScriptFile record
-  (add-dependencies {} (javascript-file false
-                                        (deps/to-url "samples/hello/src/hello/core.cljs")
-                                        ["hello.core"]
-                                        ["goog.array"]))
-  )
-
 ;; Optimize
 ;; ========
 
@@ -1443,11 +1391,6 @@
 
   ;; optimize a ClojureScript form
   (optimize {:optimizations :simple} (-compile '(def x 3) {}))
-
-  ;; optimize a project
-  (println (->> (-compile "samples/hello/src" {})
-                (apply add-dependencies {})
-                (apply optimize {:optimizations :simple :pretty-print true})))
   )
 
 ;; Output
@@ -1977,24 +1920,6 @@
 
       :else (output-deps-file opts disk-sources))))
 
-(comment
-
-  ;; output unoptimized alone
-  (output-unoptimized {} "goog.provide('test');\ngoog.require('cljs.core');\nalert('hello');\n")
-  ;; output unoptimized with all dependencies
-  (apply output-unoptimized {}
-         (add-dependencies {}
-                           "goog.provide('test');\ngoog.require('cljs.core');\nalert('hello');\n"))
-  ;; output unoptimized with external library
-  (apply output-unoptimized {}
-         (add-dependencies {:libs ["closure/library/third_party/closure"]}
-                           "goog.provide('test');\ngoog.require('cljs.core');\ngoog.require('goog.dom.query');\n"))
-  ;; output unoptimized and write deps file to 'out/test.js'
-  (output-unoptimized {:output-to "out/test.js"}
-                      "goog.provide('test');\ngoog.require('cljs.core');\nalert('hello');\n")
-  )
-
-
 (defn get-upstream-deps*
   "returns a merged map containing all upstream dependencies defined
   by libraries on the classpath."

From bc587aa96ad063106241275058129363fc509c3a Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 17:25:33 +0000
Subject: [PATCH 3050/4033] cleanup switch over to the standard pipeline by
 adding cljs.closure/compile-inputs & cljs.closure/compile-ns

---
 src/main/clojure/cljs/closure.clj | 20 ++++++++++++++++++++
 src/main/clojure/cljs/repl.cljc   | 18 ++++++++----------
 2 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 559a96500..9683b1449 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2656,6 +2656,26 @@
       (util/mkdirs outfile)
       (spit outfile (slurp (io/resource (str "cljs/bootstrap_" target-str ".js")))))))
 
+(defn compile-inputs
+  [inputs opts]
+  (env/ensure
+    (let [sources (-> inputs (add-dependency-sources opts))
+          opts    (handle-js-modules opts sources env/*compiler*)
+          sources (-> (remove (comp #{:seed} :type) sources)
+                    deps/dependency-order
+                    (compile-sources false opts)
+                    (#(map add-core-macros-if-cljs-js %))
+                    (add-js-sources opts) deps/dependency-order
+                    (->> (map #(source-on-disk opts %)) doall))]
+      ;; this is an optimization for handle-js-modules
+      (swap! env/*compiler* update-in [:options] merge opts)
+      sources)))
+
+(defn compile-ns
+  "Compiles a namespace and all of its transitive dependencies."
+  [ns opts]
+  (compile-inputs (find-sources ns opts) opts))
+
 (defn build
   "Given a source which can be compiled, produce runnable JavaScript."
   ([source opts]
diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 8fe2fa6e3..6b9db87d4 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -196,16 +196,14 @@
   only once."
   ([repl-env ns] (load-namespace repl-env ns nil))
   ([repl-env ns opts]
-   (let [ns (if (and (seq? ns) (= (first ns) 'quote)) (second ns)ns)
-         sources (-> (cljsc/-find-sources ns (merge (env->opts repl-env) opts))
-                   (cljsc/add-dependency-sources opts))
-         opts (cljsc/handle-js-modules opts sources env/*compiler*)
-         _ (swap! env/*compiler* update-in [:options] merge opts)
-         sources (-> sources deps/dependency-order
-                   (cljsc/compile-sources false opts)
-                   (#(map cljsc/add-core-macros-if-cljs-js %))
-                   (cljsc/add-js-sources opts) deps/dependency-order
-                   (->> (map #(cljsc/source-on-disk opts %)) doall))]
+   (let [ns (if (and (seq? ns) (= (first ns) 'quote)) (second ns) ns)
+         ;; We need to use a seed because many things (npm deps etc.) cannot be
+         ;; *directly* compiled, they must be a part of some ClojureScript ns
+         ;; form - thus we fabricate a seed
+         sources (->> (cljsc/compile-inputs
+                       [{:requires [(name ns)] :type :seed}]
+                       (merge (env->opts repl-env) opts))
+                   (remove (comp #{["goog"]} :provides)))]
      (if (:output-dir opts)
        ;; REPLs that read from :output-dir just need to add deps,
        ;; environment will handle actual loading - David

From 24845ecc375a27a74a211dfd1aac04483ca53320 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 14 Mar 2018 17:26:33 +0000
Subject: [PATCH 3051/4033] whitespace

---
 src/main/clojure/cljs/repl.cljc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 6b9db87d4..f2be264ec 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -201,8 +201,8 @@
          ;; *directly* compiled, they must be a part of some ClojureScript ns
          ;; form - thus we fabricate a seed
          sources (->> (cljsc/compile-inputs
-                       [{:requires [(name ns)] :type :seed}]
-                       (merge (env->opts repl-env) opts))
+                        [{:requires [(name ns)] :type :seed}]
+                        (merge (env->opts repl-env) opts))
                    (remove (comp #{["goog"]} :provides)))]
      (if (:output-dir opts)
        ;; REPLs that read from :output-dir just need to add deps,

From a095d308484fa2ccea11b6d7256fb310d5d57c33 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Wed, 14 Mar 2018 10:00:33 -0400
Subject: [PATCH 3052/4033] CLJS-2660: Add cljs.core/eval which, delegates to
 an overridable *eval*

---
 src/main/cljs/cljs/core.cljs        | 14 ++++++
 src/main/cljs/cljs/js.cljs          | 61 ++++++++++++++++++++++++-
 src/test/cljs/cljs/eval_test.cljs   | 26 +++++++++++
 src/test/self/self_parity/test.cljs | 70 +----------------------------
 4 files changed, 102 insertions(+), 69 deletions(-)
 create mode 100644 src/test/cljs/cljs/eval_test.cljs

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 2bfe53642..ff68081b8 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -11510,3 +11510,17 @@ reduces them without incurring seq initialization"
           (.println (.-error system) (.join (into-array args) "")))))))
 
 (maybe-enable-print!)
+
+(defonce
+  ^{:doc "Runtime environments may provide a way to evaluate ClojureScript
+  forms. Whatever function *eval* is bound to will be passed any forms which
+  should be evaluated." :dynamic true}
+  *eval*
+  (fn [_]
+    (throw (ex-info "cljs.core/*eval* not bound" {}))))
+
+(defn eval
+  "Evaluates the form data structure (not text!) and returns the result.
+  Delegates to cljs.core/*eval*."
+  [form]
+  (*eval* form))
diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs
index 30f6b4c78..010116f87 100644
--- a/src/main/cljs/cljs/js.cljs
+++ b/src/main/cljs/cljs/js.cljs
@@ -7,7 +7,7 @@
 ;   You must not remove this notice, or any other, from this software.
 
 (ns cljs.js
-  (:refer-clojure :exclude [require])
+  (:refer-clojure :exclude [require eval])
   (:require-macros [cljs.js :refer [dump-core]]
                    [cljs.env.macros :as env])
   (:require [clojure.string :as string]
@@ -786,10 +786,13 @@
 ;; -----------------------------------------------------------------------------
 ;; Eval
 
+(declare clear-fns!)
+
 (defn- eval* [bound-vars form opts cb]
   (let [the-ns     (or (:ns opts) 'cljs.user)
         bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns})
                      (:source-map opts) (assoc :*sm-data* (sm-data)))]
+    (clear-fns!)
     (binding [env/*compiler*         (:*compiler* bound-vars)
               *eval-fn*              (:*eval-fn* bound-vars)
               ana/*cljs-ns*          (:*cljs-ns* bound-vars)
@@ -1037,6 +1040,7 @@
                      (:source-map opts) (assoc :*sm-data* (sm-data)))
         aname      (cond-> name (:macros-ns opts) ana/macro-ns-name)]
     (when (:verbose opts) (debug-prn "Evaluating" name))
+    (clear-fns!)
     (trampoline
      (fn compile-loop [ns]
        (binding [env/*compiler*         (:*compiler* bound-vars)
@@ -1193,6 +1197,61 @@
       :*eval-fn*      (or (:eval opts) *eval-fn*)}
      source name opts cb)))
 
+;;; Support for cljs.core/eval
+
+;; The following volatiles and fns set up a scheme to
+;; emit function values into JavaScript as numeric
+;; references that are looked up. Needed to implement eval.
+
+(defonce ^:private fn-index (volatile! 0))
+(defonce ^:private fn-refs (volatile! {}))
+
+(defn- clear-fns!
+  "Clears saved functions."
+  []
+  (vreset! fn-refs {}))
+
+(defn- put-fn
+  "Saves a function, returning a numeric representation."
+  [f]
+  (let [n (vswap! fn-index inc)]
+    (vswap! fn-refs assoc n f)
+    n))
+
+(defn- get-fn
+  "Gets a function, given its numeric representation."
+  [n]
+  (get @fn-refs n))
+
+(defn- emit-fn [f]
+  (print "cljs.js.get_fn(" (put-fn f) ")"))
+
+(defmethod comp/emit-constant js/Function
+  [f]
+  (emit-fn f))
+
+(defmethod comp/emit-constant cljs.core/Var
+  [f]
+  (emit-fn f))
+
+(defn- eval-impl
+  ([form]
+   (eval-impl form (.-name *ns*)))
+  ([form ns]
+   (let [result (atom nil)]
+     (let [st env/*compiler*]
+       (eval st form
+         {:ns            ns
+          :context       :expr
+          :def-emits-var true}
+         (fn [{:keys [value error]}]
+           (if error
+             (throw error)
+             (reset! result value)))))
+     @result)))
+
+(set! *eval* eval-impl)
+
 (comment
   (require '[cljs.js :as cljs]
            '[cljs.analyzer :as ana])
diff --git a/src/test/cljs/cljs/eval_test.cljs b/src/test/cljs/cljs/eval_test.cljs
new file mode 100644
index 000000000..3b097a630
--- /dev/null
+++ b/src/test/cljs/cljs/eval_test.cljs
@@ -0,0 +1,26 @@
+(ns cljs.eval-test
+  (:require [cljs.test :refer [deftest is]]))
+
+;;; This test namespace should only be loaded by environments that set up cljs.core/*eval*
+
+(def addition-list-1 (list + 1 2))
+(def addition-list-2 (list + 1 'a))
+(def addition-list-3 (list (fn [a b] (+ a b)) 1 2))
+(defn square [x] (* x x))
+(defn cube [x] (* x x x))
+
+(deftest test-eval
+  (is (== 1 (eval 1)))
+  (is (== 3 (eval '(+ 1 2))))
+  (is (== 17 (eval '(let [a 10] (+ 3 4 a)))))
+  (is (= 'a (:name (meta (eval '(def a 3))))))
+  (is (== 3 (eval 'a)))
+  (is (== 3 (eval addition-list-1)))
+  (is (== 4 (eval addition-list-2)))
+  (is (== 13 (eval (concat addition-list-1 [10]))))
+  (is (= 'lucky-number (:name (meta (eval (list 'def 'lucky-number (concat addition-list-1 [20])))))))
+  (is (== 23 (eval 'lucky-number)))
+  (is (== 64 ((eval (list comp square cube)) 2)))
+  (is (== 5 ((eval (eval +)) 2 3)))
+  (is (== 3 (eval addition-list-3)))
+  (is (== 4 (eval (list #'inc 3)))))
diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs
index e83b1bcbb..903940e9d 100644
--- a/src/test/self/self_parity/test.cljs
+++ b/src/test/self/self_parity/test.cljs
@@ -258,73 +258,6 @@
         (print "caused by: ")
         (recur cause)))))
 
-;; The following volatiles and fns set up a scheme to
-;; emit function values into JavaScript as numeric
-;; references that are looked up. Needed to implement eval.
-
-(defonce ^:private fn-index (volatile! 0))
-(defonce ^:private fn-refs (volatile! {}))
-
-(defn- put-fn
-  "Saves a function, returning a numeric representation."
-  [f]
-  (let [n (vswap! fn-index inc)]
-    (vswap! fn-refs assoc n f)
-    n))
-
-(defn- get-fn
-  "Gets a function, given its numeric representation."
-  [n]
-  (get @fn-refs n))
-
-(defn- emit-fn [f]
-  (print "self_parity.test.get_fn(" (put-fn f) ")"))
-
-(defmethod comp/emit-constant js/Function
-  [f]
-  (emit-fn f))
-
-(defmethod comp/emit-constant cljs.core/Var
-  [f]
-  (emit-fn f))
-
-;; Inject an implementation of eval into needed macros namespaces
-
-(defn- eval
-  ([form]
-   (eval form (.-name *ns*)))
-  ([form ns]
-   (let [result (atom nil)]
-     (cljs/eval st form
-       {:ns            ns
-        :context       :expr
-        :def-emits-var true}
-       (fn [{:keys [value error]}]
-         (if error
-           (handle-error error (:source-maps @st))
-           (reset! result value))))
-     @result)))
-
-(defn- intern
-  ([ns name]
-   (when-let [the-ns (find-ns (cond-> ns (instance? Namespace ns) ns-name))]
-     (eval `(def ~name) (ns-name the-ns))))
-  ([ns name val]
-   (when-let [the-ns (find-ns (cond-> ns (instance? Namespace ns) ns-name))]
-     (eval `(def ~name ~val) (ns-name the-ns)))))
-
-(defn- inject-eval
-  [target-ns]
-  (intern target-ns 'eval eval))
-
-(defn- setup-eval []
-  (eval-form st 'cljs.user
-    '(require-macros 'cljs.spec.test.alpha)
-    (fn [{:keys [value error]}]
-      (if error
-        (handle-error error (:source-maps @st))
-        (inject-eval 'cljs.spec.test.alpha$macros)))))
-
 ;; Test suite runner
 
 (defn run-tests
@@ -336,6 +269,7 @@
   (eval-form st 'cljs.user
     '(ns parity.core
        (:require [cljs.test :refer-macros [run-tests]]
+                 [cljs.eval-test]
                  [cljs.primitives-test]
                  [cljs.destructuring-test]
                  [cljs.new-new-test]
@@ -377,6 +311,7 @@
         (handle-error error (:source-maps @st))
         (eval-form st 'parity.core
           '(run-tests
+             'cljs.eval-test
              'cljs.primitives-test
              'cljs.destructuring-test
              'cljs.new-new-test
@@ -419,7 +354,6 @@
 
 (defn -main [& args]
   (init-runtime)
-  (setup-eval)
   (run-tests))
 
 (set! *main-cli-fn* -main)

From 8953f46afdc719d8f9b97f70a12d07dc3f778fbe Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Thu, 15 Mar 2018 12:12:24 +0000
Subject: [PATCH 3053/4033] CLJS-2659: Unable to require namespaces that
 require node modules in node cljs repl

handle-js-modules assumes it runs as an early pass over compiler inputs.
in the REPL of course this assumption is not true, so in order to
correctly update the compilation environment state to reflect known
Node dependencies we must get all transitive dependencies for the require
form and pass them to handle-js-modules
---
 src/main/clojure/cljs/repl.cljc | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index f2be264ec..27a7784b7 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -493,8 +493,11 @@
            ast (->ast form)
            ast (if-not (#{:ns :ns*} (:op ast))
                  ast
-                 (let [ijs (ana/parse-ns [form])] ;; if ns form need to check for js modules - David
-                   (cljsc/handle-js-modules opts [ijs] env/*compiler*)
+                 (let [ijs (ana/parse-ns [form])]
+                   (cljsc/handle-js-modules opts
+                     (deps/dependency-order
+                       (cljsc/add-dependency-sources [ijs] opts))
+                     env/*compiler*)
                    (binding [ana/*check-alias-dupes* false]
                      (ana/no-warn (->ast form))))) ;; need new AST after we know what the modules are - David
            wrap-js

From 616966ca9e94229af128eaf1ff4b54bcb9745c72 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 10:30:51 +0000
Subject: [PATCH 3054/4033] CLJS-2659: Unable to require namespaces that
 require node modules in node cljs repl

Fixes the non-Node.js case. handle-js-modules *must* update
compiler `:options`. If this isn't done, the future compilation runs
will return different results.
---
 src/main/clojure/cljs/closure.clj | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 9683b1449..18b3190c5 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2636,15 +2636,17 @@
                        (:foreign-libs opts)))
                (process-js-modules opts)
                (:options @compiler-env))]
-    (swap! compiler-env (fn [cenv]
-                          (-> cenv
-                            ;; we need to also track the whole top level - this is to support
-                            ;; cljs.analyze/analyze-deps, particularly in REPL contexts - David
-                            (merge {:js-dependency-index (deps/js-dependency-index opts)})
-                            (update-in [:node-module-index] (fnil into #{})
-                              (if (= target :nodejs)
-                                (map str node-required)
-                                (map str (keys top-level)))))))
+    (swap! compiler-env
+      (fn [cenv]
+        (-> cenv
+          ;; we need to also track the whole top level - this is to support
+          ;; cljs.analyze/analyze-deps, particularly in REPL contexts - David
+          (merge {:js-dependency-index (deps/js-dependency-index opts)})
+          (update-in [:options] merge opts)
+          (update-in [:node-module-index] (fnil into #{})
+            (if (= target :nodejs)
+              (map str node-required)
+              (map str (keys top-level)))))))
     opts))
 
 (defn output-bootstrap [{:keys [target] :as opts}]
@@ -2667,8 +2669,6 @@
                     (#(map add-core-macros-if-cljs-js %))
                     (add-js-sources opts) deps/dependency-order
                     (->> (map #(source-on-disk opts %)) doall))]
-      ;; this is an optimization for handle-js-modules
-      (swap! env/*compiler* update-in [:options] merge opts)
       sources)))
 
 (defn compile-ns
@@ -2768,7 +2768,6 @@
                               (-> (-find-sources source opts)
                                   (add-dependency-sources compile-opts)))
                  opts       (handle-js-modules opts js-sources compiler-env)
-                 _ (swap! env/*compiler* update-in [:options] merge opts)
                  js-sources (-> js-sources
                                 deps/dependency-order
                                 (compile-sources compiler-stats compile-opts)

From b9b1a27e25cb6ea43ec5c605bbf1873b03e82e08 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 10:41:04 +0000
Subject: [PATCH 3055/4033] docstrings

---
 src/main/clojure/cljs/closure.clj | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 18b3190c5..d74fd211a 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2659,6 +2659,8 @@
       (spit outfile (slurp (io/resource (str "cljs/bootstrap_" target-str ".js")))))))
 
 (defn compile-inputs
+  "Compile inputs and all of their transitive dependencies including JS modules,
+   libs, and foreign libs. Duplicates the pipeline of build."
   [inputs opts]
   (env/ensure
     (let [sources (-> inputs (add-dependency-sources opts))
@@ -2672,7 +2674,8 @@
       sources)))
 
 (defn compile-ns
-  "Compiles a namespace and all of its transitive dependencies."
+  "Compiles a namespace and all of its transitive dependencies.
+   See compile-inputs."
   [ns opts]
   (compile-inputs (find-sources ns opts) opts))
 

From 1b22a53189e42a933043a759d8ee902a9ea93c40 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 14:22:32 +0000
Subject: [PATCH 3056/4033] CLJS-2667: Non-Node REPL Regression - direct
 require of JS modules no longer works

cljs.closure/compile-input no longer needs :seed bit as
cljs.repl/load-namespaces now handles all input types.

change cljs.compiler/load-libs, global-exports-libs and libs-to-load
are not mutually exclusive.

cljs.repl/load-namespaces can now handle different types of requires
directly. If ClojureScript namespace use the normal build pipeline. If
JavaScript namespace simply add any JS dependencies and ensure the files
are on disk. Removed old hack where we had to read the file system to
get the updated module name. This possible because we now invoke
handle-js-modules (and include transitive module deps) in
cljs.repl/evaluate-form, thus all JS module requires will already be
remapped by the time we get to cljs.repl/load-namespaces.
---
 src/main/clojure/cljs/closure.clj   |  2 +-
 src/main/clojure/cljs/compiler.cljc |  2 +-
 src/main/clojure/cljs/repl.cljc     | 46 +++++++++++++++++++----------
 3 files changed, 32 insertions(+), 18 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index d74fd211a..ddbd810b1 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2665,7 +2665,7 @@
   (env/ensure
     (let [sources (-> inputs (add-dependency-sources opts))
           opts    (handle-js-modules opts sources env/*compiler*)
-          sources (-> (remove (comp #{:seed} :type) sources)
+          sources (-> sources
                     deps/dependency-order
                     (compile-sources false opts)
                     (#(map add-core-macros-if-cljs-js %))
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index e743dfbd9..52c7a109f 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1127,7 +1127,7 @@
                                      (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? libs)]
                                        [node-libs libs-to-load])
                                      [nil libs]))
-        {global-exports-libs true libs-to-load false} (group-by ana/dep-has-global-exports? libs-to-load)]
+        global-exports-libs (filter ana/dep-has-global-exports? libs-to-load)]
     (when (-> libs meta :reload-all)
       (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set([\"cljs.core\"]);")
       (emitln "if(!COMPILED) " loaded-libs " = cljs.core.set([\"cljs.core\"]);"))
diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 27a7784b7..28209bc6a 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -190,6 +190,26 @@
      (assoc :output-dir
             (or (:output-dir opts) (get repl-env :working-dir ".repl"))))))
 
+(defn add-url [ijs]
+  (cond-> ijs
+    (not (contains? ijs :url))
+    (assoc :url (io/resource (:file ijs)))))
+
+(defn ns->input [ns opts]
+  (if-let [input (some-> (util/ns->source ns) (ana/parse-ns opts))]
+    input
+    (if-let [input (some->
+                     (get-in @env/*compiler*
+                       [:js-dependency-index (str ns)])
+                     add-url)]
+      input
+      (throw
+        (ex-info (str ns " does not exist")
+          {::error :invalid-ns})))))
+
+(defn compilable? [input]
+  (contains? input :source-file))
+
 (defn load-namespace
   "Load a namespace and all of its dependencies into the evaluation environment.
   The environment is responsible for ensuring that each namespace is loaded once and
@@ -197,29 +217,23 @@
   ([repl-env ns] (load-namespace repl-env ns nil))
   ([repl-env ns opts]
    (let [ns (if (and (seq? ns) (= (first ns) 'quote)) (second ns) ns)
-         ;; We need to use a seed because many things (npm deps etc.) cannot be
-         ;; *directly* compiled, they must be a part of some ClojureScript ns
-         ;; form - thus we fabricate a seed
-         sources (->> (cljsc/compile-inputs
-                        [{:requires [(name ns)] :type :seed}]
-                        (merge (env->opts repl-env) opts))
-                   (remove (comp #{["goog"]} :provides)))]
+         input (ns->input ns opts)
+         sources (if (compilable? input)
+                   (->> (cljsc/compile-inputs [input]
+                          (merge (env->opts repl-env) opts))
+                     (remove (comp #{["goog"]} :provides)))
+                   (map #(cljsc/source-on-disk opts %)
+                     (cljsc/add-js-sources [input] opts)))]
+     (when (:repl-verbose opts)
+       (println (str "load-namespace " ns " , compiled:") (map :provides sources)))
      (if (:output-dir opts)
        ;; REPLs that read from :output-dir just need to add deps,
        ;; environment will handle actual loading - David
        (let [sb (StringBuffer.)]
          (doseq [source sources]
-           (when (:repl-verbose opts)
-             (println "Loading:" (:provides source)))
-           ;; Need to get :requires and :provides from compiled source
-           ;; not from our own compilation, this issue oddly doesn't seem to
-           ;; affect compiled ClojureScript, should be cleaned up so we
-           ;; don't need this step here - David
            (with-open [rdr (io/reader (:url source))]
              (.append sb
-               (cljsc/add-dep-string opts
-                 (merge source
-                   (deps/parse-js-ns (line-seq rdr)))))))
+               (cljsc/add-dep-string opts source))))
          (when (:repl-verbose opts)
            (println (.toString sb)))
          (-evaluate repl-env "" 1 (.toString sb)))

From c3c352b5d0e8d4b5edcdbf33fa4af22231ad16e0 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 14:40:20 +0000
Subject: [PATCH 3057/4033] CLJS-2662: Add Externs for Nashorn

---
 src/main/cljs/cljs/externs.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/main/cljs/cljs/externs.js b/src/main/cljs/cljs/externs.js
index af77602c4..4d0e0f200 100644
--- a/src/main/cljs/cljs/externs.js
+++ b/src/main/cljs/cljs/externs.js
@@ -19,3 +19,9 @@ Object.prototype.next = function() {};
  */
 function IEquiv() {};
 IEquiv.prototype.equiv = function() {};
+
+/**
+ * @constructor
+ */
+function Java() {};
+Java.prototype.type = function() {};
\ No newline at end of file

From e7dd8f0d9c08dc1f03b8e9b98498c7519cf5c96c Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 15 Mar 2018 11:30:45 -0400
Subject: [PATCH 3058/4033] CLJS-2664: Clarify that cljs.core/eval is really
 intended for bootstrapped ClojureScript

---
 src/main/cljs/cljs/core.cljs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index ff68081b8..098a42dbe 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -11521,6 +11521,7 @@ reduces them without incurring seq initialization"
 
 (defn eval
   "Evaluates the form data structure (not text!) and returns the result.
-  Delegates to cljs.core/*eval*."
+  Delegates to cljs.core/*eval*. Intended for use in self-hosted ClojureScript,
+  which sets up an implementation of cljs.core/*eval* for that environment."
   [form]
   (*eval* form))

From b1dc91b7dbcb222044d1431a932636756db59d89 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Tue, 13 Mar 2018 08:22:40 -0400
Subject: [PATCH 3059/4033] CLJS-2654: Gracefully handle browser launch failure

---
 src/main/clojure/cljs/repl/browser.clj | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 5ab6ec6ce..612c258ad 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -290,6 +290,23 @@
 
 (def lock (Object.))
 
+(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}]
   (locking lock
     (when-not (:socket @server-state)
@@ -318,8 +335,8 @@
         (server/start repl-env)
         (let [base-url (str "http://" (:host repl-env) ":" (:port repl-env))]
           (if launch-browser
-            (browse/browse-url (str base-url "?rel=" (System/currentTimeMillis)))
-            (println "Waiting for browser to connect to" base-url "..."))))))
+            (maybe-browse-url base-url)
+            (println (waiting-to-connect-message base-url)))))))
   (swap! server-state update :listeners inc))
 
 (defrecord BrowserEnv []

From fe021c120acb6d5cbc8079f1b23d4538174e89b8 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 15:40:21 +0000
Subject: [PATCH 3060/4033] CLJS-2657: cljs.main: Watch causing compilation
 errors with -r

Address both reported issues. The first is handled by simply deferring
to existing watch support in the REPL. The other of options embedding
compile-env in options is avoided by binding it instead. REPL were
recently changed to check to see if cljs.env/*compiler* is bound and
to reuse.
---
 src/main/clojure/cljs/cli.clj | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 0eb87d985..a519970d4 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -434,16 +434,15 @@ present"
         repl?    (boolean (#{"-r" "--repl"} (first args)))
         serve?   (boolean (#{"-s" "--serve"} (first args)))
         cenv     (env/default-compiler-env)]
-    (if-let [path (:watch opts)]
-      (if repl?
-        (.start (Thread. #(watch-proc cenv path opts)))
-        (build/watch path opts cenv))
-      (build/build source opts cenv))
-    (when repl?
-      (repl-opt repl-env args
-        (assoc-in cfg [:options :compiler-env] cenv)))
-    (when serve?
-      (serve-opt repl-env args cfg))))
+    (env/with-compiler-env cenv
+      (if-let [path (:watch opts)]
+        (when-not repl?
+          (build/watch path opts cenv))
+        (build/build source opts cenv))
+      (when repl?
+        (repl-opt repl-env args cfg))
+      (when serve?
+        (serve-opt repl-env args cfg)))))
 
 (defn- compile-opt
   [repl-env [_ ns & args] cfg]

From 8e1f6f27c35b1887b6cc901adf8175d86e9987ad Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 16:05:13 +0000
Subject: [PATCH 3061/4033] start converting the externs inference stuff into
 real tests

---
 src/test/clojure/cljs/analyzer_tests.clj | 82 ++++++++++++++++--------
 1 file changed, 56 insertions(+), 26 deletions(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 77ff33ef8..266b56d8b 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -13,6 +13,7 @@
             [cljs.env :as env]
             [cljs.analyzer :as a]
             [cljs.analyzer.api :as ana-api]
+            [cljs.compiler :as comp]
             [cljs.closure :as closure]
             [cljs.externs :as externs]
             [cljs.analyzer :as ana])
@@ -854,13 +855,12 @@
           ))))
   )
 
-(comment
-  (require '[cljs.compiler :as cc])
-  (require '[cljs.closure :as closure])
-
+(deftest test-basic-infer
   (let [test-cenv (atom {::a/externs (externs/externs-map)})]
     (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+              a/*cljs-warnings* (assoc a/*cljs-warnings*
+                                  :infer-warning true
+                                  :undeclared-var false)]
       (e/with-compiler-env test-cenv
         (a/analyze-form-seq
           '[(ns foo.core)
@@ -871,11 +871,14 @@
               (.apply (.-log js/console) js/console (into-array args)))
             (js/console.log js/Number.MAX_VALUE)
             (js/console.log js/Symbol.iterator)])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
+        (is (= "var React;\nReact.Component;\n"
+               (with-out-str
+                 (comp/emit-externs
+                   (reduce util/map-merge {}
+                     (map (comp :externs second)
+                       (get @test-cenv ::a/namespaces)))))))))))
+
+(deftest test-method-infer
   (let [test-cenv (atom {::a/externs (externs/externs-map)})]
     (binding [a/*cljs-ns* a/*cljs-ns*
               a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
@@ -883,12 +886,14 @@
         (a/analyze-form-seq
           '[(defn foo [^js/React.Component c]
               (.render c))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
-  ;; works, does not generate extern
+        (is (= "var React;\nReact.Component;\nReact.Component.prototype.render;\n"
+               (with-out-str
+                 (comp/emit-externs
+                   (reduce util/map-merge {}
+                     (map (comp :externs second)
+                       (get @test-cenv ::a/namespaces)))))))))))
+
+(deftest test-minimal-infer
   (let [test-cenv (atom {::a/externs (externs/externs-map
                                        (closure/load-externs
                                          {:externs ["src/test/externs/test.js"]}))})]
@@ -897,12 +902,14 @@
       (e/with-compiler-env test-cenv
         (a/analyze-form-seq
           '[(js/console.log (.wozMethod (js/baz)))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
-  ;; works, does not generate extern
+        (is (= ""
+              (with-out-str
+                (comp/emit-externs
+                  (reduce util/map-merge {}
+                    (map (comp :externs second)
+                      (get @test-cenv ::a/namespaces)))))))))))
+
+(deftest test-type-hint-minimal-infer
   (let [test-cenv (atom {::a/externs (externs/externs-map
                                        (closure/load-externs
                                          {:externs ["src/test/externs/test.js"]}))})]
@@ -912,10 +919,33 @@
         (a/analyze-form-seq
           '[(defn afun [^js/Foo x]
               (.wozMethod x))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
+        (is (= ""
+              (with-out-str
+                (comp/emit-externs
+                  (reduce util/map-merge {}
+                    (map (comp :externs second)
+                      (get @test-cenv ::a/namespaces)))))))))))
+
+(deftest test-type-hint-minimal-infer-unknown-method
+  (let [test-cenv (atom {::a/externs (externs/externs-map
+                                       (closure/load-externs
+                                         {:externs ["src/test/externs/test.js"]}))})]
+    (binding [a/*cljs-ns* a/*cljs-ns*
+              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+      (e/with-compiler-env test-cenv
+        (a/analyze-form-seq
+          '[(defn baz [^js/Foo a]
+              (.gozMethod a))])
+        (is (= "Foo.prototype.gozMethod;\n"
+               (with-out-str
+                 (comp/emit-externs
+                   (reduce util/map-merge {}
+                     (map (comp :externs second)
+                       (get @test-cenv ::a/namespaces)))))))))))
+
+(comment
+  (require '[cljs.compiler :as cc])
+  (require '[cljs.closure :as closure])
 
   ;; FIXME: generates externs we know about including the one we don't
   (let [test-cenv (atom {::a/externs (externs/externs-map

From f12bce5a1243dad0cd89040c2ddd772a4e7d10a1 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 16:26:38 +0000
Subject: [PATCH 3062/4033] finish converting infer comments into tests

---
 src/test/clojure/cljs/analyzer_tests.clj | 210 ++++++++++++-----------
 1 file changed, 108 insertions(+), 102 deletions(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 266b56d8b..95d0f38ef 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -16,7 +16,8 @@
             [cljs.compiler :as comp]
             [cljs.closure :as closure]
             [cljs.externs :as externs]
-            [cljs.analyzer :as ana])
+            [cljs.analyzer :as ana]
+            [clojure.string :as string])
   (:use clojure.test))
 
 (defn collecting-warning-handler [state]
@@ -926,114 +927,119 @@
                     (map (comp :externs second)
                       (get @test-cenv ::a/namespaces)))))))))))
 
-(deftest test-type-hint-minimal-infer-unknown-method
-  (let [test-cenv (atom {::a/externs (externs/externs-map
+(deftest test-type-hint-infer-unknown-method-in-chain
+  (let [ws (atom [])
+        test-cenv (atom {::a/externs (externs/externs-map
                                        (closure/load-externs
                                          {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(defn baz [^js/Foo a]
-              (.gozMethod a))])
-        (is (= "Foo.prototype.gozMethod;\n"
-               (with-out-str
-                 (comp/emit-externs
-                   (reduce util/map-merge {}
-                     (map (comp :externs second)
-                       (get @test-cenv ::a/namespaces)))))))))))
-
-(comment
-  (require '[cljs.compiler :as cc])
-  (require '[cljs.closure :as closure])
-
-  ;; FIXME: generates externs we know about including the one we don't
-  (let [test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(defn afun [^js/Foo.Bar x]
-              (let [z (.baz x)]
-                (.wozz z)))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
-  ;; works, generates extern
-  (let [test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(defn baz [^js/Foo a]
-              (.gozMethod a))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
-  ;; works, generates extern
-  (let [test-cenv (atom {::a/externs (externs/externs-map
+    (a/with-warning-handlers [(collecting-warning-handler ws)]
+      (binding [a/*cljs-ns* a/*cljs-ns*
+                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+        (e/with-compiler-env test-cenv
+          (a/analyze-form-seq
+            '[(defn afun [^js/Foo.Bar x]
+                (let [z (.baz x)]
+                  (.wozz z)))])
+          (is (= "Foo.Boo.prototype.wozz;\n"
+                 (with-out-str
+                   (comp/emit-externs
+                     (reduce util/map-merge {}
+                       (map (comp :externs second)
+                         (get @test-cenv ::a/namespaces)))))))
+          (is (= 1 (count @ws)))
+          (is (string/starts-with?
+                (first @ws)
+                "Cannot resolve property wozz for inferred type js/Foo.Boo")))))))
+
+(deftest test-type-hint-infer-unknown-property-in-chain
+  (let [ws (atom [])
+        test-cenv (atom {::a/externs (externs/externs-map
                                        (closure/load-externs
                                          {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(.gozMethod (js/baz))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
-  ;; known extern
-  (let [test-cenv (atom {::a/externs (externs/externs-map
+    (a/with-warning-handlers [(collecting-warning-handler ws)]
+      (binding [a/*cljs-ns* a/*cljs-ns*
+                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+        (e/with-compiler-env test-cenv
+          (a/analyze-form-seq
+            '[(defn afun [^js/Foo.Bar x]
+                (let [z (.baz x)]
+                  (.-wozz z)))])
+          (is (= "Foo.Boo.prototype.wozz;\n"
+                (with-out-str
+                  (comp/emit-externs
+                    (reduce util/map-merge {}
+                      (map (comp :externs second)
+                        (get @test-cenv ::a/namespaces)))))))
+          (is (= 1 (count @ws)))
+          (is (string/starts-with?
+                (first @ws)
+                "Cannot resolve property wozz for inferred type js/Foo.Boo")))))))
+
+(deftest test-type-hint-infer-unknown-method
+  (let [ws (atom [])
+        test-cenv (atom {::a/externs (externs/externs-map
                                        (closure/load-externs
                                          {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(.gozMethod (js/baz))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
-  (let [test-cenv (atom {::a/externs (externs/externs-map
+    (a/with-warning-handlers [(collecting-warning-handler ws)]
+      (binding [a/*cljs-ns* a/*cljs-ns*
+                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+        (e/with-compiler-env test-cenv
+          (a/analyze-form-seq
+            '[(defn baz [^js/Foo a]
+                (.gozMethod a))])
+          (is (= "Foo.prototype.gozMethod;\n"
+                 (with-out-str
+                   (comp/emit-externs
+                     (reduce util/map-merge {}
+                       (map (comp :externs second)
+                         (get @test-cenv ::a/namespaces)))))))
+          (is (= 1 (count @ws)))
+          (is (string/starts-with?
+                (first @ws)
+                "Cannot resolve property gozMethod for inferred type js/Foo")))))))
+
+(deftest test-infer-unknown-method-from-externs
+  (let [ws (atom [])
+        test-cenv (atom {::a/externs (externs/externs-map
                                        (closure/load-externs
                                          {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(fn [^js/Foo.Bar x]
-              (let [z (.baz x)]
-                (.-wozz z)))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
-  (let [test-cenv (atom {::a/externs (externs/externs-map
+    (a/with-warning-handlers [(collecting-warning-handler ws)]
+      (binding [a/*cljs-ns* a/*cljs-ns*
+                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+        (e/with-compiler-env test-cenv
+          (a/analyze-form-seq
+            '[(.gozMethod (js/baz))])
+          (is (= "Foo.prototype.gozMethod;\n"
+                 (with-out-str
+                   (comp/emit-externs
+                     (reduce util/map-merge {}
+                       (map (comp :externs second)
+                         (get @test-cenv ::a/namespaces)))))))
+          (is (= 1 (count @ws)))
+          (is (string/starts-with?
+                (first @ws)
+                "Cannot resolve property gozMethod for inferred type js/Foo")))))))
+
+(deftest test-infer-js-require
+  (let [ws (atom [])
+        test-cenv (atom {::a/externs (externs/externs-map
                                        (closure/load-externs
                                          {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(ns foo.core)
-            (def React (js/require "react"))
-            (.log js/console (.-Component React))])
-        (cc/emit-externs
-          (reduce util/map-merge {}
-            (map (comp :externs second)
-              (get @test-cenv ::a/namespaces)))))))
-
-  )
+    (a/with-warning-handlers [(collecting-warning-handler ws)]
+      (binding [a/*cljs-ns* a/*cljs-ns*
+                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+        (e/with-compiler-env test-cenv
+          (a/analyze-form-seq
+            '[(ns foo.core)
+              (def React (js/require "react"))
+              (.log js/console (.-Component React))])
+          (is (= "var require;\nObject.Component;\n"
+                 (with-out-str
+                   (comp/emit-externs
+                     (reduce util/map-merge {}
+                       (map (comp :externs second)
+                         (get @test-cenv ::a/namespaces)))))))
+          (is (= 1 (count @ws)))
+          (is (string/starts-with?
+                (first @ws)
+                "Adding extern to Object for property Component")))))))

From fdc2d8d005b1f591400910adca32d300de0318ad Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 16:40:47 +0000
Subject: [PATCH 3063/4033] cleanup infer tests w/ helper

---
 src/test/clojure/cljs/analyzer_tests.clj | 270 +++++++++--------------
 1 file changed, 100 insertions(+), 170 deletions(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 95d0f38ef..6a8ffec2d 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -856,190 +856,120 @@
           ))))
   )
 
+(defn infer-test-helper [{:keys [forms externs warnings]}]
+  (let [test-cenv (atom {::a/externs
+                         (externs/externs-map
+                           (closure/load-externs {:externs (or externs [])}))})]
+    (a/with-warning-handlers [(collecting-warning-handler (or warnings (atom [])))]
+      (binding [a/*cljs-ns* a/*cljs-ns*
+                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+        (e/with-compiler-env test-cenv
+          (a/analyze-form-seq forms)
+          (with-out-str
+            (comp/emit-externs
+              (reduce util/map-merge {}
+                (map (comp :externs second)
+                  (get @test-cenv ::a/namespaces))))))))))
+
 (deftest test-basic-infer
-  (let [test-cenv (atom {::a/externs (externs/externs-map)})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings*
-                                  :infer-warning true
-                                  :undeclared-var false)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(ns foo.core)
-            (defn bar [a] (js/parseInt a))
-            (def c js/React.Component)
-            (js/console.log "Hello world!")
-            (fn [& args]
-              (.apply (.-log js/console) js/console (into-array args)))
-            (js/console.log js/Number.MAX_VALUE)
-            (js/console.log js/Symbol.iterator)])
-        (is (= "var React;\nReact.Component;\n"
-               (with-out-str
-                 (comp/emit-externs
-                   (reduce util/map-merge {}
-                     (map (comp :externs second)
-                       (get @test-cenv ::a/namespaces)))))))))))
+  (let [res (infer-test-helper
+              {:forms '[(ns foo.core)
+                        (defn bar [a] (js/parseInt a))
+                        (def c js/React.Component)
+                        (js/console.log "Hello world!")
+                        (fn [& args]
+                          (.apply (.-log js/console) js/console (into-array args)))
+                        (js/console.log js/Number.MAX_VALUE)
+                        (js/console.log js/Symbol.iterator)]})]
+    (is (= "var React;\nReact.Component;\n" res))))
 
 (deftest test-method-infer
-  (let [test-cenv (atom {::a/externs (externs/externs-map)})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(defn foo [^js/React.Component c]
-              (.render c))])
-        (is (= "var React;\nReact.Component;\nReact.Component.prototype.render;\n"
-               (with-out-str
-                 (comp/emit-externs
-                   (reduce util/map-merge {}
-                     (map (comp :externs second)
-                       (get @test-cenv ::a/namespaces)))))))))))
+  (let [res (infer-test-helper
+              {:forms '[(defn foo [^js/React.Component c]
+                          (.render c))]})]
+    (is (= "var React;\nReact.Component;\nReact.Component.prototype.render;\n"
+           res))))
 
 (deftest test-minimal-infer
-  (let [test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(js/console.log (.wozMethod (js/baz)))])
-        (is (= ""
-              (with-out-str
-                (comp/emit-externs
-                  (reduce util/map-merge {}
-                    (map (comp :externs second)
-                      (get @test-cenv ::a/namespaces)))))))))))
+  (let [res (infer-test-helper
+              {:forms '[(js/console.log (.wozMethod (js/baz)))]
+               :externs ["src/test/externs/test.js"]})]
+    (is (string/blank? res))))
 
 (deftest test-type-hint-minimal-infer
-  (let [test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (binding [a/*cljs-ns* a/*cljs-ns*
-              a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-      (e/with-compiler-env test-cenv
-        (a/analyze-form-seq
-          '[(defn afun [^js/Foo x]
-              (.wozMethod x))])
-        (is (= ""
-              (with-out-str
-                (comp/emit-externs
-                  (reduce util/map-merge {}
-                    (map (comp :externs second)
-                      (get @test-cenv ::a/namespaces)))))))))))
+  (let [res (infer-test-helper
+              {:forms ''[(defn afun [^js/Foo x]
+                           (.wozMethod x))]
+               :externs ["src/test/externs/test.js"]})]
+    (is (string/blank? res))))
 
 (deftest test-type-hint-infer-unknown-method-in-chain
-  (let [ws (atom [])
-        test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (a/with-warning-handlers [(collecting-warning-handler ws)]
-      (binding [a/*cljs-ns* a/*cljs-ns*
-                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-        (e/with-compiler-env test-cenv
-          (a/analyze-form-seq
-            '[(defn afun [^js/Foo.Bar x]
-                (let [z (.baz x)]
-                  (.wozz z)))])
-          (is (= "Foo.Boo.prototype.wozz;\n"
-                 (with-out-str
-                   (comp/emit-externs
-                     (reduce util/map-merge {}
-                       (map (comp :externs second)
-                         (get @test-cenv ::a/namespaces)))))))
-          (is (= 1 (count @ws)))
-          (is (string/starts-with?
-                (first @ws)
-                "Cannot resolve property wozz for inferred type js/Foo.Boo")))))))
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(defn afun [^js/Foo.Bar x]
+                          (let [z (.baz x)]
+                            (.wozz z)))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws})]
+    (is (= "Foo.Boo.prototype.wozz;\n" res))
+    (is (= 1 (count @ws)))
+    (is (string/starts-with?
+          (first @ws)
+          "Cannot resolve property wozz for inferred type js/Foo.Boo"))))
 
 (deftest test-type-hint-infer-unknown-property-in-chain
-  (let [ws (atom [])
-        test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (a/with-warning-handlers [(collecting-warning-handler ws)]
-      (binding [a/*cljs-ns* a/*cljs-ns*
-                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-        (e/with-compiler-env test-cenv
-          (a/analyze-form-seq
-            '[(defn afun [^js/Foo.Bar x]
-                (let [z (.baz x)]
-                  (.-wozz z)))])
-          (is (= "Foo.Boo.prototype.wozz;\n"
-                (with-out-str
-                  (comp/emit-externs
-                    (reduce util/map-merge {}
-                      (map (comp :externs second)
-                        (get @test-cenv ::a/namespaces)))))))
-          (is (= 1 (count @ws)))
-          (is (string/starts-with?
-                (first @ws)
-                "Cannot resolve property wozz for inferred type js/Foo.Boo")))))))
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(defn afun [^js/Foo.Bar x]
+                          (let [z (.baz x)]
+                            (.-wozz z)))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws})]
+    (is (= "Foo.Boo.prototype.wozz;\n" res))
+    (is (= 1 (count @ws)))
+    (is (string/starts-with?
+          (first @ws)
+          "Cannot resolve property wozz for inferred type js/Foo.Boo"))))
 
 (deftest test-type-hint-infer-unknown-method
-  (let [ws (atom [])
-        test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (a/with-warning-handlers [(collecting-warning-handler ws)]
-      (binding [a/*cljs-ns* a/*cljs-ns*
-                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-        (e/with-compiler-env test-cenv
-          (a/analyze-form-seq
-            '[(defn baz [^js/Foo a]
-                (.gozMethod a))])
-          (is (= "Foo.prototype.gozMethod;\n"
-                 (with-out-str
-                   (comp/emit-externs
-                     (reduce util/map-merge {}
-                       (map (comp :externs second)
-                         (get @test-cenv ::a/namespaces)))))))
-          (is (= 1 (count @ws)))
-          (is (string/starts-with?
-                (first @ws)
-                "Cannot resolve property gozMethod for inferred type js/Foo")))))))
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(defn baz [^js/Foo a]
+                           (.gozMethod a))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws})]
+    (is (= "Foo.prototype.gozMethod;\n" res))
+    (is (= 1 (count @ws)))
+    (is (string/starts-with?
+          (first @ws)
+          "Cannot resolve property gozMethod for inferred type js/Foo"))))
 
 (deftest test-infer-unknown-method-from-externs
-  (let [ws (atom [])
-        test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (a/with-warning-handlers [(collecting-warning-handler ws)]
-      (binding [a/*cljs-ns* a/*cljs-ns*
-                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-        (e/with-compiler-env test-cenv
-          (a/analyze-form-seq
-            '[(.gozMethod (js/baz))])
-          (is (= "Foo.prototype.gozMethod;\n"
-                 (with-out-str
-                   (comp/emit-externs
-                     (reduce util/map-merge {}
-                       (map (comp :externs second)
-                         (get @test-cenv ::a/namespaces)))))))
-          (is (= 1 (count @ws)))
-          (is (string/starts-with?
-                (first @ws)
-                "Cannot resolve property gozMethod for inferred type js/Foo")))))))
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(.gozMethod (js/baz))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws})]
+    (is (= "Foo.prototype.gozMethod;\n" res))
+    (is (= 1 (count @ws)))
+    (is (string/starts-with?
+          (first @ws)
+          "Cannot resolve property gozMethod for inferred type js/Foo"))))
 
 (deftest test-infer-js-require
-  (let [ws (atom [])
-        test-cenv (atom {::a/externs (externs/externs-map
-                                       (closure/load-externs
-                                         {:externs ["src/test/externs/test.js"]}))})]
-    (a/with-warning-handlers [(collecting-warning-handler ws)]
-      (binding [a/*cljs-ns* a/*cljs-ns*
-                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
-        (e/with-compiler-env test-cenv
-          (a/analyze-form-seq
-            '[(ns foo.core)
-              (def React (js/require "react"))
-              (.log js/console (.-Component React))])
-          (is (= "var require;\nObject.Component;\n"
-                 (with-out-str
-                   (comp/emit-externs
-                     (reduce util/map-merge {}
-                       (map (comp :externs second)
-                         (get @test-cenv ::a/namespaces)))))))
-          (is (= 1 (count @ws)))
-          (is (string/starts-with?
-                (first @ws)
-                "Adding extern to Object for property Component")))))))
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(ns foo.core)
+                        (def React (js/require "react"))
+                        (.log js/console (.-Component React))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws})]
+    (is (= "var require;\nObject.Component;\n" res))
+    (is (= 1 (count @ws)))
+    (is (string/starts-with?
+          (first @ws)
+          "Adding extern to Object for property Component"))))
+
+(comment
+
+  )
\ No newline at end of file

From 81144f68d5794c040a8742e77762d0ed47f04237 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 16:54:39 +0000
Subject: [PATCH 3064/4033] CLJS-2668: Unit tests failing as of CLJS-2667

Remove bad precondition
---
 src/main/clojure/cljs/analyzer.cljc | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index cf909b2db..a6d3373a2 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -2095,8 +2095,6 @@
 (defn foreign-dep?
   #?(:cljs {:tag boolean})
   [dep]
-  (assert (symbol? dep)
-    (str "cljs.analyzer/foreign-dep? expected symbol got " (pr-str dep)))
   (let [js-index (:js-dependency-index @env/*compiler*)]
     (if-some [[_ {:keys [foreign]}] (find js-index (name dep))]
       foreign

From 1e06283548647351076a95f98232b97966df441b Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 17:13:39 +0000
Subject: [PATCH 3065/4033] add warn configure flag to infer-test-helper

---
 src/test/clojure/cljs/analyzer_tests.clj | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 6a8ffec2d..6f3d6b980 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -856,13 +856,14 @@
           ))))
   )
 
-(defn infer-test-helper [{:keys [forms externs warnings]}]
+(defn infer-test-helper [{:keys [forms externs warnings warn]}]
   (let [test-cenv (atom {::a/externs
                          (externs/externs-map
                            (closure/load-externs {:externs (or externs [])}))})]
     (a/with-warning-handlers [(collecting-warning-handler (or warnings (atom [])))]
       (binding [a/*cljs-ns* a/*cljs-ns*
-                a/*cljs-warnings* (assoc a/*cljs-warnings* :infer-warning true)]
+                a/*cljs-warnings* (assoc a/*cljs-warnings*
+                                    :infer-warning (if (nil? warn) true warn))]
         (e/with-compiler-env test-cenv
           (a/analyze-form-seq forms)
           (with-out-str
@@ -972,4 +973,15 @@
 
 (comment
 
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(ns warn-on-infer-test.app)
+                        (set! *warn-on-infer* true)
+                        (defn wrap-baz [x]
+                          (.baz x))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws
+               :warn false})]
+    (println (pr-str res) @ws))
+
   )
\ No newline at end of file

From 3ac37d5843541aace91e25a657c3501c7f7c512f Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 16 Mar 2018 17:27:47 +0000
Subject: [PATCH 3066/4033] CLJS-2491: Inference warnings are not reported

Fix a mistake from last year - we cannot blindly rebind *cljs-warning* at
a lower level than analyze-file.

Add test case for `set! *warn-on-infer*` idiom
---
 src/main/clojure/cljs/analyzer.cljc      | 12 ++++--------
 src/test/clojure/cljs/analyzer_tests.clj |  9 ++++-----
 2 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index a6d3373a2..54c15a3d9 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -3595,11 +3595,6 @@
         ast    (analyze-form env form name opts)]
     (reduce (fn [ast pass] (pass env ast opts)) ast passes)))
 
-(defn- warnings-for [form]
-  (if (analyzed? form)
-    (zipmap (keys *cljs-warnings*) (repeat false))
-    *cljs-warnings*))
-
 (defn analyze
   "Given an environment, a map containing {:locals (mapping of names to bindings), :context
   (one of :statement, :expr, :return), :ns (a symbol naming the
@@ -3615,9 +3610,10 @@
   ([env form name opts]
    (ensure
      (wrapping-errors env
-       (binding [*cljs-warnings* (warnings-for form)
-                 reader/*alias-map* (or reader/*alias-map* {})]
-         (analyze* env form name opts))))))
+       (binding [reader/*alias-map* (or reader/*alias-map* {})]
+         (if (analyzed? form)
+           (no-warn (analyze* env form name opts))
+           (analyze* env form name opts)))))))
 
 (defn add-consts
   "Given a compiler state and a map from fully qualified symbols to constant
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 6f3d6b980..0ee68abc1 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -971,8 +971,7 @@
           (first @ws)
           "Adding extern to Object for property Component"))))
 
-(comment
-
+(deftest test-set-warn-on-infer
   (let [ws  (atom [])
         res (infer-test-helper
               {:forms '[(ns warn-on-infer-test.app)
@@ -982,6 +981,6 @@
                :externs ["src/test/externs/test.js"]
                :warnings ws
                :warn false})]
-    (println (pr-str res) @ws))
-
-  )
\ No newline at end of file
+    (is (string/blank? res))
+    (is (= 1 (count @ws)))
+    (is (string/starts-with? (first @ws) "Cannot infer target type"))))

From 1b8c1e79d0d46232c20551036d408caf52477096 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 17 Mar 2018 09:53:52 +0000
Subject: [PATCH 3067/4033] CLJS-2661: Ominous dangerous use of 'this' warning

Just add the type hint for now
---
 src/main/clojure/cljs/core.cljc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index a1189b74e..85f32c7fc 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -3032,6 +3032,7 @@
           ~@(core/when solo
               `[(set! (. ~sym ~'-cljs$lang$maxFixedArity)
                   ~(core/dec (count sig)))])
+          (js-inline-comment " @this {Function} ")
           (set! (. ~sym ~'-cljs$lang$applyTo)
             ~(apply-to)))))))
 

From 56d3ee4bc67f5072469fcad7064a68b2b65f2210 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 17 Mar 2018 10:26:13 +0000
Subject: [PATCH 3068/4033] CLJS-2661: Ominous dangerous use of 'this' warning

Add other bits from Andre Rauh's patch. Variadic invoke can go
directly, internal invokes are done via `this`. The previous commit's
Closure type information is enough to suppress the warning.
---
 src/main/clojure/cljs/core.cljc | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 85f32c7fc..13c6d440e 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -3017,15 +3017,11 @@
                            ([~restarg]
                             (let [~@(mapcat param-bind params)]
                               (this-as self#
-                                (if (identical? js/Function (type self#))
-                                  (. self# (~(get-delegate) ~@params ~restarg))
-                                  (. ~sym (~(get-delegate) ~@params ~restarg))))))))
+                                (. self# (~(get-delegate) ~@params ~restarg)))))))
                       `(fn
                          ([~restarg]
                           (this-as self#
-                            (if (identical? js/Function (type self#))
-                              (. self# (~(get-delegate) (seq ~restarg)))
-                              (. ~sym (~(get-delegate) (seq ~restarg)))))))))]
+                            (. self# (~(get-delegate) (seq ~restarg))))))))]
        `(do
           (set! (. ~sym ~(get-delegate-prop))
             (fn (~(vec sig) ~@body)))
@@ -3065,10 +3061,7 @@
                (let [argseq# (when (< ~c-1 (alength args#))
                                (new ^::ana/no-resolve cljs.core/IndexedSeq
                                  (.slice args# ~c-1) 0 nil))]
-                 (this-as self#
-                   (if (identical? js/Function (type self#))
-                     (. self# (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))
-                     (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))))))))
+                 (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#))))))
          ~(variadic-fn* rname method)
          ~(core/when emit-var? `(var ~name))))))
 

From 7779dc4c6a5370e7411884fd1055ab046f794142 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Fri, 16 Mar 2018 19:44:53 -0400
Subject: [PATCH 3069/4033] CLJS-2670: Update cljs.compiler/warning-types

---
 src/main/clojure/cljs/closure.clj | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index ddbd810b1..1bd93c92d 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -102,9 +102,11 @@
    :internet-explorer-checks DiagnosticGroups/INTERNET_EXPLORER_CHECKS
    :invalid-casts DiagnosticGroups/INVALID_CASTS
    :j2cl-checks DiagnosticGroups/J2CL_CHECKS
+   :jsdoc-missing-type DiagnosticGroups/JSDOC_MISSING_TYPE
    :late-provide DiagnosticGroups/LATE_PROVIDE
    :lint-checks DiagnosticGroups/LINT_CHECKS
    :message-descriptions DiagnosticGroups/MESSAGE_DESCRIPTIONS
+   :misplaced-msg-annotation DiagnosticGroups/MISPLACED_MSG_ANNOTATION
    :misplaced-type-annotation DiagnosticGroups/MISPLACED_TYPE_ANNOTATION
    :missing-getcssname DiagnosticGroups/MISSING_GETCSSNAME
    :missing-override DiagnosticGroups/MISSING_OVERRIDE
@@ -113,18 +115,24 @@
    :missing-provide DiagnosticGroups/MISSING_PROVIDE
    :missing-require DiagnosticGroups/MISSING_REQUIRE
    :missing-return DiagnosticGroups/MISSING_RETURN
+   :missing-sources-warnings DiagnosticGroups/MISSING_SOURCES_WARNINGS
+   :module-load DiagnosticGroups/MODULE_LOAD
+   :msg-conventions DiagnosticGroups/MSG_CONVENTIONS
    :non-standard-jsdoc DiagnosticGroups/NON_STANDARD_JSDOC
    :report-unknown-types DiagnosticGroups/REPORT_UNKNOWN_TYPES
+   :strict-missing-properties DiagnosticGroups/STRICT_MISSING_PROPERTIES
    :strict-missing-require DiagnosticGroups/STRICT_MISSING_REQUIRE
    :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
    :type-invalidation DiagnosticGroups/TYPE_INVALIDATION
    :undefined-names DiagnosticGroups/UNDEFINED_NAMES
    :undefined-variables DiagnosticGroups/UNDEFINED_VARIABLES
    :underscore DiagnosticGroups/UNDERSCORE
    :unknown-defines DiagnosticGroups/UNKNOWN_DEFINES
+   :unnecessary-escape DiagnosticGroups/UNNECESSARY_ESCAPE
    :unused-local-variable DiagnosticGroups/UNUSED_LOCAL_VARIABLE
    :unused-private-property DiagnosticGroups/UNUSED_PRIVATE_PROPERTY
    :use-of-goog-base DiagnosticGroups/USE_OF_GOOG_BASE

From 0f9346778238d111f0952a1360abc137aa02b0a9 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 17 Mar 2018 13:00:55 +0000
Subject: [PATCH 3070/4033] pass the name of the current thread to the client

---
 src/main/cljs/clojure/browser/repl.cljs | 12 +++++++-----
 src/main/clojure/cljs/repl/browser.clj  |  6 +++++-
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs
index 6d0953818..4848cba23 100644
--- a/src/main/cljs/clojure/browser/repl.cljs
+++ b/src/main/cljs/clojure/browser/repl.cljs
@@ -18,6 +18,7 @@
   (:require [goog.dom :as gdom]
             [goog.object :as gobj]
             [goog.array :as garray]
+            [goog.json :as json]
             [goog.userAgent.product :as product]
             [clojure.browser.net :as net]
             [clojure.browser.event :as event]
@@ -228,11 +229,12 @@
           (flush-print-queue! repl-connection))))
     (net/register-service repl-connection
       :evaluate-javascript
-      (fn [js]
-        (net/transmit
-          repl-connection
-          :send-result
-          (evaluate-javascript repl-connection js))))
+      (fn [json]
+        (let [obj (json/parse json)]
+          (net/transmit
+            repl-connection
+            :send-result
+            (evaluate-javascript repl-connection (gobj/get obj "form"))))))
     (net/connect repl-connection
       (constantly nil)
       (fn [iframe]
diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 612c258ad..ea6c2059b 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -70,7 +70,11 @@
     (send-for-eval @(server/connection) form return-value-fn))
   ([conn form return-value-fn]
     (set-return-value-fn return-value-fn)
-    (server/send-and-close conn 200 form "text/javascript")))
+    (server/send-and-close conn 200
+      (json/write-str
+        {"thread" (.getName (Thread/currentThread))
+         "form"   form})
+      "application/json")))
 
 (defn- return-value
   "Called by the server when a return value is received."

From bfa599f668341d690c51c8c17d5218b871b1246a Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 17 Mar 2018 13:05:01 +0000
Subject: [PATCH 3071/4033] thread -> repl

---
 src/main/clojure/cljs/repl/browser.clj | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index ea6c2059b..1b66b23cb 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -72,8 +72,8 @@
     (set-return-value-fn return-value-fn)
     (server/send-and-close conn 200
       (json/write-str
-        {"thread" (.getName (Thread/currentThread))
-         "form"   form})
+        {"repl" (.getName (Thread/currentThread))
+         "form" form})
       "application/json")))
 
 (defn- return-value

From dc53376dad5344af57f659235837967b388d6a7b Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 17 Mar 2018 13:16:02 +0000
Subject: [PATCH 3072/4033] roundtrip repl name for evals

---
 src/main/cljs/clojure/browser/repl.cljs | 29 +++++++++++++++----------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs
index 4848cba23..5edffa621 100644
--- a/src/main/cljs/clojure/browser/repl.cljs
+++ b/src/main/cljs/clojure/browser/repl.cljs
@@ -31,6 +31,7 @@
 (goog-define HOST "localhost")
 (goog-define PORT 9000)
 
+(def ^:dynamic *repl* nil)
 (def xpc-connection (atom nil))
 (def parent-connected? (atom false))
 (def print-queue (array))
@@ -92,8 +93,12 @@
 
 (def order (atom 0))
 
-(defn wrap-message [t data]
-  (pr-str {:type t :content data :order (swap! order inc)}))
+(defn wrap-message [repl t data]
+  (pr-str
+    {:repl repl
+     :type t
+     :content data
+     :order (swap! order inc)}))
 
 (defn start-evaluator
   "Start the REPL server connection."
@@ -110,8 +115,7 @@
                             ;; ack once.
                             (js/setTimeout try-handshake
                                            10)))]
-      (net/connect repl-connection
-                   try-handshake)
+      (net/connect repl-connection try-handshake)
 
       (net/register-service repl-connection
         :ack-handshake
@@ -121,8 +125,7 @@
             ;; Now that we're connected to the parent, we can start talking to
             ;; the server.
             (send-result connection
-                         url
-                         (wrap-message :ready "ready")))))
+              url (wrap-message nil :ready "ready")))))
 
       (event/listen connection
         :success
@@ -130,18 +133,18 @@
           (net/transmit
             repl-connection
             :evaluate-javascript
-            (.getResponseText (.-currentTarget e)
-              ()))))
+            (.getResponseText (.-currentTarget e) ()))))
 
       (net/register-service repl-connection
         :send-result
-        (fn [data]
-          (send-result connection url (wrap-message :result data))))
+        (fn [{:keys [repl result]}]
+          (send-result connection url
+            (wrap-message repl :result result))))
 
       (net/register-service repl-connection
         :print
         (fn [data]
-          (send-print url (wrap-message :print data)))))
+          (send-print url (wrap-message nil :print data)))))
     (js/alert "No 'xpc' param provided to child iframe.")))
 
 (def load-queue nil)
@@ -234,7 +237,9 @@
           (net/transmit
             repl-connection
             :send-result
-            (evaluate-javascript repl-connection (gobj/get obj "form"))))))
+            {:repl   (gobj/get obj "repl")
+             :result (evaluate-javascript repl-connection
+                       (gobj/get obj "form"))}))))
     (net/connect repl-connection
       (constantly nil)
       (fn [iframe]

From a56d06a2f504969119ff5057d0cb24a8174d54e7 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 17 Mar 2018 18:55:29 +0000
Subject: [PATCH 3073/4033] xpc takes strings, serialize/parse to/from JSON

---
 src/main/cljs/clojure/browser/repl.cljs | 33 +++++++++++++++++--------
 1 file changed, 23 insertions(+), 10 deletions(-)

diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs
index 5edffa621..c3c7611df 100644
--- a/src/main/cljs/clojure/browser/repl.cljs
+++ b/src/main/cljs/clojure/browser/repl.cljs
@@ -38,7 +38,10 @@
 
 (defn flush-print-queue! [conn]
   (doseq [str print-queue]
-    (net/transmit conn :print str))
+    (net/transmit conn :print
+      (json/serialize
+        #js {"repl" *repl*
+             "str"  str})))
   (garray/clear print-queue))
 
 (defn repl-print [data]
@@ -137,14 +140,20 @@
 
       (net/register-service repl-connection
         :send-result
-        (fn [{:keys [repl result]}]
-          (send-result connection url
-            (wrap-message repl :result result))))
+        (fn [json]
+          (let [obj    (json/parse json)
+                repl   (gobj/get obj "repl")
+                result (gobj/get obj "result")]
+            (send-result connection url
+              (wrap-message repl :result result)))))
 
       (net/register-service repl-connection
         :print
-        (fn [data]
-          (send-print url (wrap-message nil :print data)))))
+        (fn [json]
+          (let [obj  (json/parse json)
+                repl (gobj/get obj "repl")
+                str  (gobj/get obj "str")]
+            (send-print url (wrap-message repl :print str))))))
     (js/alert "No 'xpc' param provided to child iframe.")))
 
 (def load-queue nil)
@@ -233,13 +242,17 @@
     (net/register-service repl-connection
       :evaluate-javascript
       (fn [json]
-        (let [obj (json/parse json)]
+        (let [obj  (json/parse json)
+              repl (gobj/get obj "repl")
+              form (gobj/get obj "form")]
           (net/transmit
             repl-connection
             :send-result
-            {:repl   (gobj/get obj "repl")
-             :result (evaluate-javascript repl-connection
-                       (gobj/get obj "form"))}))))
+            (json/serialize
+              #js {"repl" repl
+                   "result"
+                   (binding [*repl* repl]
+                     (evaluate-javascript repl-connection form))})))))
     (net/connect repl-connection
       (constantly nil)
       (fn [iframe]

From fecb3f8647a8cb8cae296f96eac598dc813ada5a Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 17 Mar 2018 19:22:44 +0000
Subject: [PATCH 3074/4033] CLJS-2643: Socket REPL output can be directed to
 the wrong place

put outs into a ConcurrentHashMap keyed on thread name. Print to the
correct out for a thread.
---
 src/main/clojure/cljs/repl/browser.clj | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 1b66b23cb..57bcd4cfc 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -22,11 +22,12 @@
             [cljs.stacktrace :as st]
             [cljs.analyzer :as ana]
             [cljs.build.api :as build])
-  (:import [java.util.concurrent Executors]))
+  (:import [java.util.concurrent Executors ConcurrentHashMap]))
 
 (def ^:dynamic browser-state nil)
 (def ^:dynamic ordering nil)
 (def ^:dynamic es nil)
+(def outs (ConcurrentHashMap.))
 
 (def ext->mime-type
   {".html" "text/html"
@@ -239,11 +240,12 @@
   (send-via es ordering add-in-order order f)
   (send-via es ordering run-in-order))
 
-(defmethod handle-post :print [{:keys [content order]} conn _]
+(defmethod handle-post :print [{:keys [repl content order]} conn _]
   (constrain-order order
     (fn []
-      (print (read-string content))
-      (.flush *out*)))
+      (binding [*out* (.get outs repl)]
+        (print (read-string content))
+        (.flush *out*))))
   (server/send-and-close conn 200 "ignore__"))
 
 (defmethod handle-post :result [{:keys [content order]} conn _]
@@ -341,6 +343,7 @@
           (if launch-browser
             (maybe-browse-url base-url)
             (println (waiting-to-connect-message base-url)))))))
+  (.put outs (.getName (Thread/currentThread)) *out*)
   (swap! server-state update :listeners inc))
 
 (defrecord BrowserEnv []
@@ -356,6 +359,7 @@
   (-load [this provides url]
     (load-javascript this provides url))
   (-tear-down [this]
+    (.remove outs (.getName (Thread/currentThread)))
     (let [server-state (:server-state this)]
       (when (zero? (:listeners (swap! server-state update :listeners dec)))
         (binding [server/state server-state] (server/stop))

From c9cf1a76b05b762fe0150b30e4230d56136035ff Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 17 Mar 2018 20:11:08 +0000
Subject: [PATCH 3075/4033] handle case where repl might be nil

---
 src/main/clojure/cljs/repl/browser.clj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 57bcd4cfc..e99bfc22c 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -243,7 +243,7 @@
 (defmethod handle-post :print [{:keys [repl content order]} conn _]
   (constrain-order order
     (fn []
-      (binding [*out* (.get outs repl)]
+      (binding [*out* (or (and repl (.get outs repl)) *out*)]
         (print (read-string content))
         (.flush *out*))))
   (server/send-and-close conn 200 "ignore__"))

From f53f50a12660737f8964a0bbb1e62b1d007d365a Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 18 Mar 2018 11:04:30 +0000
Subject: [PATCH 3076/4033] Shared REPL environments for Node Socket REPLs

---
 src/main/clojure/cljs/repl/node.clj   | 215 ++++++++++++++------------
 src/main/clojure/cljs/server/node.clj |  41 ++++-
 2 files changed, 148 insertions(+), 108 deletions(-)

diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index 64f96bdb6..224085443 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -16,14 +16,18 @@
             [cljs.cli :as cli]
             [cljs.closure :as closure]
             [clojure.data.json :as json])
-  (:import java.net.Socket
-           java.lang.StringBuilder
-           [java.io File BufferedReader BufferedWriter InputStream
+  (:import [java.net Socket]
+           [java.lang StringBuilder]
+           [java.io File BufferedReader BufferedWriter
             Writer InputStreamReader IOException]
-           [java.lang ProcessBuilder Process]))
+           [java.lang ProcessBuilder Process]
+           [java.util.concurrent ConcurrentHashMap]))
 
-(defn socket [host port]
-  (let [socket (Socket. host port)
+(def lock (Object.))
+(def outs (ConcurrentHashMap.))
+
+(defn create-socket [^String host port]
+  (let [socket (Socket. host (int port))
         in     (io/reader socket)
         out    (io/writer socket)]
     {:socket socket :in in :out out}))
@@ -111,97 +115,101 @@
 
 (defn setup
   ([repl-env] (setup repl-env nil))
-  ([repl-env opts]
-    (let [output-dir   (io/file (util/output-directory opts))
-          _            (.mkdirs output-dir)
-          of           (io/file output-dir "node_repl.js")
-          _            (spit of
-                         (string/replace (slurp (io/resource "cljs/repl/node_repl.js"))
-                           "var PORT = 5001;"
-                           (str "var PORT = " (:port repl-env) ";")))
-          proc         (.start (build-process opts repl-env of))
-          _            (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) *out*))))
-                           (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) *err*)))))
-          env          (ana/empty-env)
-          core         (io/resource "cljs/core.cljs")
-          ;; represent paths as vectors so we can emit JS arrays, this is to
-          ;; paper over Windows issues with minimum hassle - David
-          path         (.getPath (.getCanonicalFile output-dir))
-          [fc & cs]    (rest (util/path-seq path)) ;; remove leading empty string
-          root         (.substring path 0 (+ (.indexOf path fc) (count fc)))
-          root-path    (vec (cons root cs))
-          rewrite-path (conj root-path "goog")]
-      (reset! (:proc repl-env) proc)
-      (loop [r nil]
-        (when-not (= r "ready")
-          (Thread/sleep 50)
-          (try
-            (reset! (:socket repl-env) (socket (:host repl-env) (:port repl-env)))
-            (catch Exception e))
-          (if @(:socket repl-env)
-            (recur (read-response (:in @(:socket repl-env))))
-            (recur nil))))
-      ;; compile cljs.core & its dependencies, goog/base.js must be available
-      ;; for bootstrap to load, use new closure/compile as it can handle
-      ;; resources in JARs
-      (let [core-js (closure/compile core
-                      (assoc opts :output-file
-                        (closure/src-file->target-file
-                          core (dissoc opts :output-dir))))
-            deps    (closure/add-dependencies opts core-js)]
-        ;; output unoptimized code and the deps file
-        ;; for all compiled namespaces
-        (apply closure/output-unoptimized
-          (assoc opts
-            :output-to (.getPath (io/file output-dir "node_repl_deps.js")))
-          deps))
-      ;; bootstrap, replace __dirname as __dirname won't be set
-      ;; properly due to how we are running it - David
-      (node-eval repl-env
-        (-> (slurp (io/resource "cljs/bootstrap_nodejs.js"))
-          (string/replace "path.resolve(__dirname, '..', 'base.js')"
-            (platform-path (conj rewrite-path "bootstrap" ".." "base.js")))
-          (string/replace
-            "path.join(\".\", \"..\", src)"
-            (str "path.join(" (platform-path rewrite-path) ", src)"))
-          (string/replace
-            "var CLJS_ROOT = \".\";"
-            (str "var CLJS_ROOT = " (platform-path root-path) ";"))))
-      ;; load the deps file so we can goog.require cljs.core etc.
-      (node-eval repl-env
-        (str "require("
+  ([{:keys [host port socket state] :as repl-env} opts]
+   (locking lock
+     (when-not @socket
+       (let [output-dir   (io/file (util/output-directory opts))
+             _            (.mkdirs output-dir)
+             of           (io/file output-dir "node_repl.js")
+             _            (spit of
+                            (string/replace (slurp (io/resource "cljs/repl/node_repl.js"))
+                              "var PORT = 5001;"
+                              (str "var PORT = " (:port repl-env) ";")))
+             proc         (.start (build-process opts repl-env of))
+             _            (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) *out*))))
+                              (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) *err*)))))
+             env          (ana/empty-env)
+             core         (io/resource "cljs/core.cljs")
+             ;; represent paths as vectors so we can emit JS arrays, this is to
+             ;; paper over Windows issues with minimum hassle - David
+             path         (.getPath (.getCanonicalFile output-dir))
+             [fc & cs]    (rest (util/path-seq path)) ;; remove leading empty string
+             root         (.substring path 0 (+ (.indexOf path fc) (count fc)))
+             root-path    (vec (cons root cs))
+             rewrite-path (conj root-path "goog")]
+         (reset! (:proc repl-env) proc)
+         (loop [r nil]
+           (when-not (= r "ready")
+             (Thread/sleep 50)
+             (try
+               (reset! socket (create-socket host port))
+               (catch Exception e))
+             (if @socket
+               (recur (read-response (:in @socket)))
+               (recur nil))))
+         ;; compile cljs.core & its dependencies, goog/base.js must be available
+         ;; for bootstrap to load, use new closure/compile as it can handle
+         ;; resources in JARs
+         (let [core-js (closure/compile core
+                         (assoc opts :output-file
+                                     (closure/src-file->target-file
+                                       core (dissoc opts :output-dir))))
+               deps    (closure/add-dependencies opts core-js)]
+           ;; output unoptimized code and the deps file
+           ;; for all compiled namespaces
+           (apply closure/output-unoptimized
+             (assoc opts
+               :output-to (.getPath (io/file output-dir "node_repl_deps.js")))
+             deps))
+         ;; bootstrap, replace __dirname as __dirname won't be set
+         ;; properly due to how we are running it - David
+         (node-eval repl-env
+           (-> (slurp (io/resource "cljs/bootstrap_nodejs.js"))
+             (string/replace "path.resolve(__dirname, '..', 'base.js')"
+               (platform-path (conj rewrite-path "bootstrap" ".." "base.js")))
+             (string/replace
+               "path.join(\".\", \"..\", src)"
+               (str "path.join(" (platform-path rewrite-path) ", src)"))
+             (string/replace
+               "var CLJS_ROOT = \".\";"
+               (str "var CLJS_ROOT = " (platform-path root-path) ";"))))
+         ;; load the deps file so we can goog.require cljs.core etc.
+         (node-eval repl-env
+           (str "require("
              (platform-path (conj root-path "node_repl_deps.js"))
              ")"))
-      ;; monkey-patch isProvided_ to avoid useless warnings - David
-      (node-eval repl-env
-        (str "goog.isProvided_ = function(x) { return false; };"))
-      ;; monkey-patch goog.require, skip all the loaded checks
-      (repl/evaluate-form repl-env env ""
-        '(set! (.-require js/goog)
-           (fn [name]
-             (js/CLOSURE_IMPORT_SCRIPT
-               (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))
-      ;; load cljs.core, setup printing
-      (repl/evaluate-form repl-env env ""
-        '(do
-           (.require js/goog "cljs.core")
-           (enable-console-print!)))
-      ;; redef goog.require to track loaded libs
-      (repl/evaluate-form repl-env env ""
-        '(do
-           (set! *target* "nodejs")
-           (set! *loaded-libs* #{"cljs.core"})
-           (set! (.-require js/goog)
-             (fn [name reload]
-               (when (or (not (contains? *loaded-libs* name)) reload)
-                 (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
-                 (js/CLOSURE_IMPORT_SCRIPT
-                   (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))))
-      (node-eval repl-env
-        (str "goog.global.CLOSURE_UNCOMPILED_DEFINES = "
-          (json/write-str (:closure-defines opts)) ";")))))
-
-(defrecord NodeEnv [host port path socket proc]
+         ;; monkey-patch isProvided_ to avoid useless warnings - David
+         (node-eval repl-env
+           (str "goog.isProvided_ = function(x) { return false; };"))
+         ;; monkey-patch goog.require, skip all the loaded checks
+         (repl/evaluate-form repl-env env ""
+           '(set! (.-require js/goog)
+              (fn [name]
+                (js/CLOSURE_IMPORT_SCRIPT
+                  (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))
+         ;; load cljs.core, setup printing
+         (repl/evaluate-form repl-env env ""
+           '(do
+              (.require js/goog "cljs.core")
+              (enable-console-print!)))
+         ;; redef goog.require to track loaded libs
+         (repl/evaluate-form repl-env env ""
+           '(do
+              (set! *target* "nodejs")
+              (set! *loaded-libs* #{"cljs.core"})
+              (set! (.-require js/goog)
+                (fn [name reload]
+                  (when (or (not (contains? *loaded-libs* name)) reload)
+                    (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
+                    (js/CLOSURE_IMPORT_SCRIPT
+                      (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))))
+         (node-eval repl-env
+           (str "goog.global.CLOSURE_UNCOMPILED_DEFINES = "
+             (json/write-str (:closure-defines opts)) ";")))))
+   (.put outs (.getName (Thread/currentThread)) *out*)
+   (swap! state update :listeners inc)))
+
+(defrecord NodeEnv [host port path socket proc state]
   repl/IReplEnvOptions
   (-repl-options [this]
     {:output-dir ".cljs_node_repl"
@@ -217,11 +225,14 @@
   (-load [this provides url]
     (load-javascript this provides url))
   (-tear-down [this]
-    (let [{:keys [out]} @socket]
-      (write out ":cljs/quit")
-      (while (alive? @proc)
-        (Thread/sleep 50))
-      (close-socket @socket))))
+    (swap! state update :listeners dec)
+    (locking lock
+      (when (zero? (:listeners @state))
+        (let [sock @socket]
+          (when-not (.isClosed (:socket sock))
+            (write (:out sock) ":cljs/quit")
+            (while (alive? @proc) (Thread/sleep 50))
+            (close-socket sock)))))))
 
 (defn repl-env* [options]
   (let [{:keys [host port path debug-port]}
@@ -229,7 +240,9 @@
           {:host "localhost"
            :port (+ 49000 (rand-int 10000))}
           options)]
-    (assoc (NodeEnv. host port path (atom nil) (atom nil))
+    (assoc
+      (NodeEnv. host port path
+        (atom nil) (atom nil) (atom {:listeners 0}))
       :debug-port debug-port)))
 
 (defn repl-env
diff --git a/src/main/clojure/cljs/server/node.clj b/src/main/clojure/cljs/server/node.clj
index 039fe8df0..41b8fb426 100644
--- a/src/main/clojure/cljs/server/node.clj
+++ b/src/main/clojure/cljs/server/node.clj
@@ -7,21 +7,48 @@
 ;   You must not remove this notice, or any other, from this software.
 
 (ns cljs.server.node
-  (:require [cljs.repl :as repl]
+  (:require [cljs.env :as env]
+            [cljs.repl :as repl]
             [cljs.repl.node :as node]
-            [cljs.core.server :as server]))
+            [cljs.core.server :as server])
+  (:import [java.net Socket]))
+
+(defonce envs (atom {}))
+
+(defn env-opts->key [{:keys [host port]}]
+  [host port])
+
+(defn stale? [{:keys [socket] :as repl-env}]
+  (if-let [sock (:socket @socket)]
+    (.isClosed ^Socket sock)
+    false))
+
+(defn get-envs [env-opts]
+  (let [env-opts (merge {:host "localhost" :port 49001} env-opts)
+        k (env-opts->key env-opts)]
+    (swap! envs
+      #(cond-> %
+         (or (not (contains? % k))
+             (stale? (get-in % [k 0])))
+         (assoc k
+           [(node/repl-env* env-opts)
+            (env/default-compiler-env)])))
+    (get @envs k)))
 
 (defn repl
   ([]
    (repl nil))
   ([{:keys [opts env-opts]}]
-   (repl/repl* (node/repl-env* env-opts) opts)))
+   (let [[env cenv] (get-envs env-opts)]
+     (env/with-compiler-env cenv
+       (repl/repl* env opts)))))
 
 (defn prepl
   ([]
    (prepl nil))
   ([{:keys [opts env-opts]}]
-   (apply server/io-prepl
-     (mapcat identity
-       {:repl-env (node/repl-env* env-opts)
-        :opts opts}))))
+   (let [[env cenv] (get-envs env-opts)]
+     (env/with-compiler-env cenv
+       (apply server/io-prepl
+         (mapcat identity
+           {:repl-env env :opts opts}))))))

From 196e88e7266581551ebd18e7267dad26c89f67f7 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 18 Mar 2018 11:23:49 +0000
Subject: [PATCH 3077/4033] pass REPL name to Node.js REPL server

---
 src/main/clojure/cljs/repl/node.clj     | 5 ++++-
 src/main/clojure/cljs/repl/node_repl.js | 5 ++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index 224085443..90f28fed5 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -59,7 +59,10 @@
   [repl-env js]
   (let [{:keys [in out]} @(:socket repl-env)]
     ;; escape backslash for Node.js under Windows
-    (write out js)
+    (write out
+      (json/write-str
+        {"repl" (.getName (Thread/currentThread))
+         "form" js}))
     (let [result (json/read-str
                    (read-response in) :key-fn keyword)]
       (condp = (:status result)
diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js
index 657d1631d..416e1d7cf 100644
--- a/src/main/clojure/cljs/repl/node_repl.js
+++ b/src/main/clojure/cljs/repl/node_repl.js
@@ -22,6 +22,7 @@ try {
 var server = net.createServer(function (socket) {
     var buffer = "",
         ret    = null,
+        repl   = null,
         err    = null;
 
     socket.write("ready");
@@ -53,7 +54,9 @@ var server = net.createServer(function (socket) {
                 } else {
                     try {
                         dom.run(function () {
-                            ret = vm.runInThisContext(data, "repl");
+                            var obj = JSON.parse(data);
+                            repl = obj.repl;
+                            ret = vm.runInThisContext(obj.form, "repl");
                         });
                     } catch (e) {
                         err = e;

From ae624118a6cbd447207581b1db48c8ecca92ac3e Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 18 Mar 2018 13:35:42 +0000
Subject: [PATCH 3078/4033] (WIP)

Out is now directed to the correct place but there are two outstanding
issues. The first is that the simple `pipe` no longer works as the
value is now JSON. As we depend on Transit already we could use the
streaming Jackson JSON parser to address this.

The second issue is that since we pipe, the observed order in REPLs
is non-deterministic.
---
 src/main/clojure/cljs/repl/node.clj     | 36 +++++++++++++++++--------
 src/main/clojure/cljs/repl/node_repl.js | 18 ++++++++++++-
 2 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index 90f28fed5..cc1c4cc0a 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -18,13 +18,14 @@
             [clojure.data.json :as json])
   (:import [java.net Socket]
            [java.lang StringBuilder]
-           [java.io File BufferedReader BufferedWriter
-            Writer InputStreamReader IOException]
+           [java.io File Reader BufferedReader BufferedWriter
+           InputStreamReader IOException]
            [java.lang ProcessBuilder Process]
            [java.util.concurrent ConcurrentHashMap]))
 
 (def lock (Object.))
 (def outs (ConcurrentHashMap.))
+(def errs (ConcurrentHashMap.))
 
 (defn create-socket [^String host port]
   (let [socket (Socket. host (int port))
@@ -89,16 +90,22 @@
 (defn- alive? [proc]
   (try (.exitValue proc) false (catch IllegalThreadStateException _ true)))
 
-(defn- pipe [^Process proc in ^Writer out]
+(defn- pipe [^Process proc in stream ios]
   ;; we really do want system-default encoding here
-  (with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)]
-    (loop [buf (char-array 1024)]
+  (with-open [^Reader in (-> in InputStreamReader. BufferedReader.)]
+    (loop [buf (char-array (* 64 1024))]
       (when (alive? proc)
         (try
           (let [len (.read in buf)]
             (when-not (neg? len)
-              (.write out buf 0 len)
-              (.flush out)))
+              (try
+                (let [{:strs [repl data]} (json/read-str (String. buf))
+                      stream (or (.get ios repl) stream)]
+                  (.write stream data 0 (.length ^String data))
+                  (.flush stream))
+                (catch Throwable _
+                  (.write stream buf 0 len)
+                  (.flush stream)))))
           (catch IOException e
             (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed")))
               (.printStackTrace e *err*))))
@@ -119,9 +126,14 @@
 (defn setup
   ([repl-env] (setup repl-env nil))
   ([{:keys [host port socket state] :as repl-env} opts]
+   (let [tname (.getName (Thread/currentThread))]
+     (.put outs tname *out*)
+     (.put errs tname *err*))
    (locking lock
      (when-not @socket
-       (let [output-dir   (io/file (util/output-directory opts))
+       (let [out          *out*
+             err          *err*
+             output-dir   (io/file (util/output-directory opts))
              _            (.mkdirs output-dir)
              of           (io/file output-dir "node_repl.js")
              _            (spit of
@@ -129,8 +141,8 @@
                               "var PORT = 5001;"
                               (str "var PORT = " (:port repl-env) ";")))
              proc         (.start (build-process opts repl-env of))
-             _            (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) *out*))))
-                              (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) *err*)))))
+             _            (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) out outs))))
+                              (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) err errs)))))
              env          (ana/empty-env)
              core         (io/resource "cljs/core.cljs")
              ;; represent paths as vectors so we can emit JS arrays, this is to
@@ -209,7 +221,6 @@
          (node-eval repl-env
            (str "goog.global.CLOSURE_UNCOMPILED_DEFINES = "
              (json/write-str (:closure-defines opts)) ";")))))
-   (.put outs (.getName (Thread/currentThread)) *out*)
    (swap! state update :listeners inc)))
 
 (defrecord NodeEnv [host port path socket proc state]
@@ -229,6 +240,9 @@
     (load-javascript this provides url))
   (-tear-down [this]
     (swap! state update :listeners dec)
+    (let [tname (Thread/currentThread)]
+      (.remove outs tname)
+      (.remove errs tname))
     (locking lock
       (when (zero? (:listeners @state))
         (let [sock @socket]
diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js
index 416e1d7cf..2d51a26bb 100644
--- a/src/main/clojure/cljs/repl/node_repl.js
+++ b/src/main/clojure/cljs/repl/node_repl.js
@@ -13,6 +13,23 @@ var net  = require("net");
 var vm   = require("vm");
 var dom  = require("domain").create();
 var PORT = 5001;
+var repl = null;
+
+process.stdout.write = (function(write) {
+    return function(chunk, encoding, fd) {
+        var args = Array.prototype.slice.call(arguments, 0);
+        args[0] = JSON.stringify({repl: repl, data: chunk});
+        write.apply(process.stdout, args);
+    };
+})(process.stdout.write);
+
+process.stderr.write = (function(write) {
+    return function(chunk, encoding, fd) {
+        var args = Array.prototype.slice.call(arguments, 0);
+        args[0] = JSON.stringify({repl: repl, data: chunk});
+        write.apply(process.stderr, args);
+    };
+})(process.stderr.write);
 
 try {
     require("source-map-support").install();
@@ -22,7 +39,6 @@ try {
 var server = net.createServer(function (socket) {
     var buffer = "",
         ret    = null,
-        repl   = null,
         err    = null;
 
     socket.write("ready");

From 0525655e2fefa5ce34114d13024e1a6d1b67e283 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sun, 18 Mar 2018 00:38:35 -0400
Subject: [PATCH 3079/4033] CLJS-2671: Double analysis warning for source in
 JAR with AOT cache

---
 src/main/clojure/cljs/compiler.cljc | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 52c7a109f..954eaf41b 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1442,11 +1442,11 @@
 
 #?(:clj
    (defn compile-file*
-     ([src dest]
+     ([^File src ^File dest]
       (compile-file* src dest
         (when env/*compiler*
           (:options @env/*compiler*))))
-     ([src dest opts]
+     ([^File src ^File dest opts]
       (ensure
         (with-core-cljs opts
           (fn []
@@ -1459,11 +1459,12 @@
               (let [ext (util/ext src)
                    {:keys [ns] :as ns-info} (ana/parse-ns src)]
                (if-let [cached (cached-core ns ext opts)]
-                 (emit-cached-core src dest cached opts)
+                 [(emit-cached-core src dest cached opts) false]
                  (let [opts (if (macro-ns? ns ext opts)
                               (assoc opts :macros-ns true)
                               opts)
-                       ret (emit-source src dest ext opts)]
+                       dest-exists? (.exists dest)
+                       ret [(emit-source src dest ext opts) dest-exists?]]
                    (.setLastModified ^File dest (util/last-modified src))
                    ret))))))))))
 
@@ -1545,8 +1546,9 @@
                                (not= 'cljs.core ns)
                                (not= :interactive (:mode opts)))
                       (swap! env/*compiler* update-in [::ana/namespaces] dissoc ns))
-                    (let [ret (compile-file* src-file dest-file opts)]
-                      (when *recompiled*
+                    (let [[ret recompiled?] (compile-file* src-file dest-file opts)]
+                      (when (and *recompiled*
+                                 recompiled?)
                         (swap! *recompiled* conj ns))
                       ret))
                   (do

From 427aaba6f557df90e0ad3c25b15531d353fd0ca3 Mon Sep 17 00:00:00 2001
From: r0man 
Date: Sun, 18 Mar 2018 18:04:14 +0100
Subject: [PATCH 3080/4033] CLJS-2650: Fix JAR compilation of cljs.loader

The cljs.loader file needs to be compiled after all other files to
populate the `module-infos` and `module-uris` vars. The
`requires-compilation?` function returned false if the file to compile
was cljs.loader and didn't contain a :cache-key. This prevented
`compile-from-jar` from moving the cljs.loader from the JAR file to
the disk and caused a java.io.FileNotFoundException later on.

Since cljs.loader always get compiled in a later pass, we decided to
remove this special logic.

Since this behaviour only happens when compiling from a JAR, I added a
test that uses the cljs.jar to compile the example from the Code
Splitting guide.
---
 src/main/clojure/cljs/compiler.cljc           | 36 +++++++++----------
 src/test/cljs_build/hello-modules/index.html  |  7 ++++
 src/test/cljs_build/hello-modules/release.clj | 13 +++++++
 src/test/cljs_build/hello-modules/repl.clj    | 18 ++++++++++
 .../hello-modules/src/bar/core.cljs           | 11 ++++++
 .../hello-modules/src/foo/core.cljs           | 17 +++++++++
 src/test/cljs_cli/cljs_cli/test.clj           | 21 ++++++++++-
 7 files changed, 103 insertions(+), 20 deletions(-)
 create mode 100644 src/test/cljs_build/hello-modules/index.html
 create mode 100644 src/test/cljs_build/hello-modules/release.clj
 create mode 100644 src/test/cljs_build/hello-modules/repl.clj
 create mode 100644 src/test/cljs_build/hello-modules/src/bar/core.cljs
 create mode 100644 src/test/cljs_build/hello-modules/src/foo/core.cljs

diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 954eaf41b..65dff6353 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1093,7 +1093,7 @@
 
        (or fn? js? goog?)
        (emits f "(" (comma-sep args)  ")")
-       
+
        :else
        (if (and ana/*cljs-static-fns* (= (:op f) :var))
          ;; higher order case, static information missing
@@ -1477,24 +1477,22 @@
           (:options @env/*compiler*))))
      ([^File src ^File dest opts]
       (let [{:keys [ns requires]} (ana/parse-ns src)]
-        (if (and (= 'cljs.loader ns) (not (contains? opts :cache-key)))
-          false
-          (ensure
-           (or (not (.exists dest))
-               (util/changed? src dest)
-               (let [version' (util/compiled-by-version dest)
-                     version (util/clojurescript-version)]
-                 (and version (not= version version')))
-               (and opts
-                    (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns)))
-                    (not= (ana/build-affecting-options opts)
-                          (ana/build-affecting-options (util/build-options dest))))
-               (and opts (:source-map opts)
-                 (if (= (:optimizations opts) :none)
-                   (not (.exists (io/file (str (.getPath dest) ".map"))))
-                   (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)]))))
-               (when-let [recompiled' (and *recompiled* @*recompiled*)]
-                 (some requires recompiled')))))))))
+        (ensure
+         (or (not (.exists dest))
+             (util/changed? src dest)
+             (let [version' (util/compiled-by-version dest)
+                   version (util/clojurescript-version)]
+               (and version (not= version version')))
+             (and opts
+                  (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns)))
+                  (not= (ana/build-affecting-options opts)
+                        (ana/build-affecting-options (util/build-options dest))))
+             (and opts (:source-map opts)
+                  (if (= (:optimizations opts) :none)
+                    (not (.exists (io/file (str (.getPath dest) ".map"))))
+                    (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)]))))
+             (when-let [recompiled' (and *recompiled* @*recompiled*)]
+               (some requires recompiled'))))))))
 
 #?(:clj
    (defn compile-file
diff --git a/src/test/cljs_build/hello-modules/index.html b/src/test/cljs_build/hello-modules/index.html
new file mode 100644
index 000000000..cfa7ff790
--- /dev/null
+++ b/src/test/cljs_build/hello-modules/index.html
@@ -0,0 +1,7 @@
+
+  
+    
+    
+    
+  
+
diff --git a/src/test/cljs_build/hello-modules/release.clj b/src/test/cljs_build/hello-modules/release.clj
new file mode 100644
index 000000000..ae675c0dc
--- /dev/null
+++ b/src/test/cljs_build/hello-modules/release.clj
@@ -0,0 +1,13 @@
+(require '[cljs.build.api :as b])
+
+(b/build "src"
+  {:output-dir "out"
+   :asset-path "/out"
+   :optimizations :advanced
+   :verbose true
+   :modules {:foo {:entries '#{foo.core}
+                   :output-to "out/foo.js"}
+             :bar {:entries '#{bar.core}
+                   :output-to "out/bar.js"}}})
+
+(System/exit 0)
diff --git a/src/test/cljs_build/hello-modules/repl.clj b/src/test/cljs_build/hello-modules/repl.clj
new file mode 100644
index 000000000..b9130a24b
--- /dev/null
+++ b/src/test/cljs_build/hello-modules/repl.clj
@@ -0,0 +1,18 @@
+(require '[cljs.repl :as r])
+(require '[cljs.build.api :as b])
+(require '[cljs.repl.browser :as rb])
+
+(def opts
+  {:watch "src"
+   :output-dir "out"
+   :asset-path "/out"
+   :optimizations :none
+   :modules {:foo {:entries '#{foo.core}
+                   :output-to "out/foo.js"}
+             :bar {:entries '#{bar.core}
+                   :output-to "out/bar.js"}}
+   :browser-repl true
+   :verbose true})
+
+(b/build "src" opts)
+(r/repl* (rb/repl-env) opts)
diff --git a/src/test/cljs_build/hello-modules/src/bar/core.cljs b/src/test/cljs_build/hello-modules/src/bar/core.cljs
new file mode 100644
index 000000000..ec6f887f8
--- /dev/null
+++ b/src/test/cljs_build/hello-modules/src/bar/core.cljs
@@ -0,0 +1,11 @@
+(ns bar.core
+  (:require [cljs.loader :as loader]))
+
+(enable-console-print!)
+
+(println "I'm bar!")
+
+(defn woz []
+  (println "WOZ!"))
+
+(loader/set-loaded! :bar)
diff --git a/src/test/cljs_build/hello-modules/src/foo/core.cljs b/src/test/cljs_build/hello-modules/src/foo/core.cljs
new file mode 100644
index 000000000..cef2ffcb2
--- /dev/null
+++ b/src/test/cljs_build/hello-modules/src/foo/core.cljs
@@ -0,0 +1,17 @@
+(ns foo.core
+  (:require [goog.dom :as gdom]
+            [goog.events :as events]
+            [cljs.loader :as loader])
+  (:import [goog.events EventType]))
+
+(enable-console-print!)
+
+(println "I'm foo!")
+
+(events/listen (gdom/getElement "button") EventType.CLICK
+  (fn [e]
+    (loader/load :bar
+      (fn []
+        ((resolve 'bar.core/woz))))))
+
+(loader/set-loaded! :foo)
diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj
index 9c8d31b4a..c02213004 100644
--- a/src/test/cljs_cli/cljs_cli/test.clj
+++ b/src/test/cljs_cli/cljs_cli/test.clj
@@ -1,8 +1,9 @@
 (ns cljs-cli.test
   (:require
-   [clojure.test :refer [deftest]]
+   [clojure.test :refer [deftest is]]
    [clojure.java.io :as io]
    [clojure.java.shell :as shell :refer [with-sh-dir]]
+   [clojure.string :as str]
    [cljs-cli.util :refer [cljs-main output-is with-sources with-post-condition with-repl-env-filter]]))
 
 (deftest eval-test
@@ -68,3 +69,21 @@
       (output-is "default-value"))
     (-> (cljs-main "-co" "{:closure-defines {foo.core/configurable \"configured-value\"}}" "-m" "foo.core")
       (output-is "configured-value"))))
+
+(deftest test-cljs-2650-loader-does-not-exists
+  (doseq [optimizations [:none :advanced]]
+    (let [src (io/file "src" "test" "cljs_build" "hello-modules" "src")
+          opts {:output-dir "out"
+                :asset-path "/out"
+                :optimizations optimizations
+                :modules {:foo {:entries '#{foo.core}
+                                :output-to "out/foo.js"}
+                          :bar {:entries '#{bar.core}
+                                :output-to "out/bar.js"}}}]
+      (with-sources
+        {"src/foo/core.cljs" (slurp (io/file src "foo" "core.cljs"))
+         "src/bar/core.cljs" (slurp (io/file src "bar" "core.cljs"))}
+        (let [result (cljs-main "--compile-opts" (pr-str opts)
+                                "--compile" "foo.core")]
+          (is (zero? (:exit result)))
+          (is (str/blank? (:err result))))))))

From eb7b5dd1f31b2a99ac989d1f71fe1cc723e3c366 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sun, 18 Mar 2018 22:30:04 -0400
Subject: [PATCH 3081/4033] CLJS-2673: Regression: Can't require cljs.js

---
 src/main/clojure/cljs/closure.clj   | 5 +++--
 src/test/cljs_cli/cljs_cli/test.clj | 9 +++++++++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 1bd93c92d..bfdbddbea 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2671,12 +2671,13 @@
    libs, and foreign libs. Duplicates the pipeline of build."
   [inputs opts]
   (env/ensure
-    (let [sources (-> inputs (add-dependency-sources opts))
+    (let [sources (-> inputs
+                    (#(map add-core-macros-if-cljs-js %))
+                    (add-dependency-sources opts))
           opts    (handle-js-modules opts sources env/*compiler*)
           sources (-> sources
                     deps/dependency-order
                     (compile-sources false opts)
-                    (#(map add-core-macros-if-cljs-js %))
                     (add-js-sources opts) deps/dependency-order
                     (->> (map #(source-on-disk opts %)) doall))]
       sources)))
diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj
index c02213004..27944e3d1 100644
--- a/src/test/cljs_cli/cljs_cli/test.clj
+++ b/src/test/cljs_cli/cljs_cli/test.clj
@@ -87,3 +87,12 @@
                                 "--compile" "foo.core")]
           (is (zero? (:exit result)))
           (is (str/blank? (:err result))))))))
+
+(deftest test-cljs-2673
+  (with-repl-env-filter #{"node"}
+    (-> (cljs-main
+          "-e" "(require 'cljs.js)"
+          "-e" "(cljs.js/eval-str (cljs.js/empty-state) \"(+ 1 2)\" nil {:eval cljs.js/js-eval :context :expr} prn)")
+      (output-is
+        nil
+        "{:ns cljs.user, :value 3}"))))

From d2d2d0897b5aca532046e4f7d79cb681ad4fcd3a Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 19 Mar 2018 08:37:43 +0000
Subject: [PATCH 3082/4033] make Node.js stdout handling mimic eval. Fixes
 printing of large values at the REPL.

---
 src/main/clojure/cljs/repl/node.clj     | 47 +++++++++++--------------
 src/main/clojure/cljs/repl/node_repl.js |  9 +++--
 2 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index cc1c4cc0a..75e75f33e 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -43,17 +43,15 @@
   (.write out (int 0)) ;; terminator
   (.flush out))
 
-(defn read-response [^BufferedReader in]
+(defn ^String read-response [^BufferedReader in]
   (let [sb (StringBuilder.)]
     (loop [sb sb c (.read in)]
-      (cond
-       (= c 1) (let [ret (str sb)]
-                 (print ret)
-                 (recur (StringBuilder.) (.read in)))
-       (= c 0) (str sb)
-       :else (do
-               (.append sb (char c))
-               (recur sb (.read in)))))))
+      (case c
+        -1 (throw (IOException. "Stream closed"))
+         0 (str sb)
+         (do
+           (.append sb (char c))
+           (recur sb (.read in)))))))
 
 (defn node-eval
   "Evaluate a JavaScript string in the Node REPL process."
@@ -93,23 +91,20 @@
 (defn- pipe [^Process proc in stream ios]
   ;; we really do want system-default encoding here
   (with-open [^Reader in (-> in InputStreamReader. BufferedReader.)]
-    (loop [buf (char-array (* 64 1024))]
-      (when (alive? proc)
-        (try
-          (let [len (.read in buf)]
-            (when-not (neg? len)
-              (try
-                (let [{:strs [repl data]} (json/read-str (String. buf))
-                      stream (or (.get ios repl) stream)]
-                  (.write stream data 0 (.length ^String data))
-                  (.flush stream))
-                (catch Throwable _
-                  (.write stream buf 0 len)
-                  (.flush stream)))))
-          (catch IOException e
-            (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed")))
-              (.printStackTrace e *err*))))
-        (recur buf)))))
+    (while (alive? proc)
+      (try
+        (let [res (read-response in)]
+          (try
+            (let [{:keys [repl content]} (json/read-str res :key-fn keyword)
+                  stream (or (.get ios repl) stream)]
+              (.write stream content 0 (.length ^String content))
+              (.flush stream))
+            (catch Throwable _
+              (.write stream res 0 (.length res))
+              (.flush stream))))
+        (catch IOException e
+          (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed")))
+            (.printStackTrace e *err*)))))))
 
 (defn- build-process
   [opts repl-env input-src]
diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js
index 2d51a26bb..80c226998 100644
--- a/src/main/clojure/cljs/repl/node_repl.js
+++ b/src/main/clojure/cljs/repl/node_repl.js
@@ -18,16 +18,18 @@ var repl = null;
 process.stdout.write = (function(write) {
     return function(chunk, encoding, fd) {
         var args = Array.prototype.slice.call(arguments, 0);
-        args[0] = JSON.stringify({repl: repl, data: chunk});
+        args[0] = JSON.stringify({repl: repl, content: chunk});
         write.apply(process.stdout, args);
+        write.call(process.stdout, "\0");
     };
 })(process.stdout.write);
 
 process.stderr.write = (function(write) {
     return function(chunk, encoding, fd) {
         var args = Array.prototype.slice.call(arguments, 0);
-        args[0] = JSON.stringify({repl: repl, data: chunk});
+        args[0] = JSON.stringify({repl: repl, content: chunk});
         write.apply(process.stderr, args);
+        write.call(process.stderr, "\0");
     };
 })(process.stderr.write);
 
@@ -82,16 +84,19 @@ var server = net.createServer(function (socket) {
 
             if(err) {
                 socket.write(JSON.stringify({
+                    repl: repl,
                     status: "exception",
                     value: err.stack
                 }));
             } else if(ret !== undefined && ret !== null) {
                 socket.write(JSON.stringify({
+                    repl: repl,
                     status: "success",
                     value: ret.toString()
                 }));
             } else {
                 socket.write(JSON.stringify({
+                    repl: repl,
                     status: "success",
                     value: null
                 }));

From 84004ff7d90d48a730b2e1e8ebc2c421830462f7 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 19 Mar 2018 12:21:33 +0100
Subject: [PATCH 3083/4033] refactor so that printing and evaluation result has
 a deterministic order at the REPL.

---
 src/main/clojure/cljs/repl/node.clj     | 64 +++++++++++++------------
 src/main/clojure/cljs/repl/node_repl.js | 38 ++++++++-------
 2 files changed, 53 insertions(+), 49 deletions(-)

diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index 75e75f33e..526153ef8 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -21,12 +21,16 @@
            [java.io File Reader BufferedReader BufferedWriter
            InputStreamReader IOException]
            [java.lang ProcessBuilder Process]
-           [java.util.concurrent ConcurrentHashMap]))
+           [java.util.concurrent ConcurrentHashMap LinkedBlockingQueue]))
 
 (def lock (Object.))
+(def results (ConcurrentHashMap.))
 (def outs (ConcurrentHashMap.))
 (def errs (ConcurrentHashMap.))
 
+(defn thread-name []
+  (.getName (Thread/currentThread)))
+
 (defn create-socket [^String host port]
   (let [socket (Socket. host (int port))
         in     (io/reader socket)
@@ -56,14 +60,10 @@
 (defn node-eval
   "Evaluate a JavaScript string in the Node REPL process."
   [repl-env js]
-  (let [{:keys [in out]} @(:socket repl-env)]
-    ;; escape backslash for Node.js under Windows
-    (write out
-      (json/write-str
-        {"repl" (.getName (Thread/currentThread))
-         "form" js}))
-    (let [result (json/read-str
-                   (read-response in) :key-fn keyword)]
+  (let [tname (thread-name)
+        {:keys [out]} @(:socket repl-env)]
+    (write out (json/write-str {:type "eval" :repl tname :form js}))
+    (let [result (.take ^LinkedBlockingQueue (.get results tname))]
       (condp = (:status result)
         "success"
         {:status :success
@@ -88,23 +88,26 @@
 (defn- alive? [proc]
   (try (.exitValue proc) false (catch IllegalThreadStateException _ true)))
 
-(defn- pipe [^Process proc in stream ios]
+(defn- event-loop [^Process proc in]
   ;; we really do want system-default encoding here
-  (with-open [^Reader in (-> in InputStreamReader. BufferedReader.)]
-    (while (alive? proc)
-      (try
-        (let [res (read-response in)]
-          (try
-            (let [{:keys [repl content]} (json/read-str res :key-fn keyword)
-                  stream (or (.get ios repl) stream)]
-              (.write stream content 0 (.length ^String content))
-              (.flush stream))
-            (catch Throwable _
-              (.write stream res 0 (.length res))
-              (.flush stream))))
-        (catch IOException e
-          (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed")))
-            (.printStackTrace e *err*)))))))
+  (while (alive? proc)
+    (try
+      (let [res (read-response in)]
+        (try
+          (let [{:keys [type repl value] :or {repl "main"} :as event}
+                (json/read-str res :key-fn keyword)]
+            (case type
+              "result"
+              (.offer (.get results repl) event)
+              (when-let [stream (.get (if (= type "out") outs errs) repl)]
+                (.write stream value 0 (.length ^String value))
+                (.flush stream))))
+          (catch Throwable _
+            (.write *out* res 0 (.length res))
+            (.flush *out*))))
+      (catch IOException e
+        (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed")))
+          (.printStackTrace e *err*))))))
 
 (defn- build-process
   [opts repl-env input-src]
@@ -122,13 +125,12 @@
   ([repl-env] (setup repl-env nil))
   ([{:keys [host port socket state] :as repl-env} opts]
    (let [tname (.getName (Thread/currentThread))]
+     (.put results tname (LinkedBlockingQueue.))
      (.put outs tname *out*)
      (.put errs tname *err*))
    (locking lock
      (when-not @socket
-       (let [out          *out*
-             err          *err*
-             output-dir   (io/file (util/output-directory opts))
+       (let [output-dir   (io/file (util/output-directory opts))
              _            (.mkdirs output-dir)
              of           (io/file output-dir "node_repl.js")
              _            (spit of
@@ -136,8 +138,6 @@
                               "var PORT = 5001;"
                               (str "var PORT = " (:port repl-env) ";")))
              proc         (.start (build-process opts repl-env of))
-             _            (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) out outs))))
-                              (.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) err errs)))))
              env          (ana/empty-env)
              core         (io/resource "cljs/core.cljs")
              ;; represent paths as vectors so we can emit JS arrays, this is to
@@ -157,6 +157,7 @@
              (if @socket
                (recur (read-response (:in @socket)))
                (recur nil))))
+         (.start (Thread. (bound-fn [] (event-loop proc (:in @socket)))))
          ;; compile cljs.core & its dependencies, goog/base.js must be available
          ;; for bootstrap to load, use new closure/compile as it can handle
          ;; resources in JARs
@@ -235,7 +236,8 @@
     (load-javascript this provides url))
   (-tear-down [this]
     (swap! state update :listeners dec)
-    (let [tname (Thread/currentThread)]
+    (let [tname (thread-name)]
+      (.remove results tname)
       (.remove outs tname)
       (.remove errs tname))
     (locking lock
diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js
index 80c226998..cdebe62ce 100644
--- a/src/main/clojure/cljs/repl/node_repl.js
+++ b/src/main/clojure/cljs/repl/node_repl.js
@@ -15,24 +15,6 @@ var dom  = require("domain").create();
 var PORT = 5001;
 var repl = null;
 
-process.stdout.write = (function(write) {
-    return function(chunk, encoding, fd) {
-        var args = Array.prototype.slice.call(arguments, 0);
-        args[0] = JSON.stringify({repl: repl, content: chunk});
-        write.apply(process.stdout, args);
-        write.call(process.stdout, "\0");
-    };
-})(process.stdout.write);
-
-process.stderr.write = (function(write) {
-    return function(chunk, encoding, fd) {
-        var args = Array.prototype.slice.call(arguments, 0);
-        args[0] = JSON.stringify({repl: repl, content: chunk});
-        write.apply(process.stderr, args);
-        write.call(process.stderr, "\0");
-    };
-})(process.stderr.write);
-
 try {
     require("source-map-support").install();
 } catch(err) {
@@ -48,6 +30,23 @@ var server = net.createServer(function (socket) {
 
     socket.setEncoding("utf8");
 
+    process.stdout.write = function(chunk, encoding, fd) {
+        var args = Array.prototype.slice.call(arguments, 0);
+        args[0] = JSON.stringify({type: "out", repl: repl, value: chunk});
+        socket.write.apply(socket, args);
+        socket.write("\0");
+    };
+
+    process.stderr.write = (function(write) {
+        return function(chunk, encoding, fd) {
+            var args = Array.prototype.slice.call(arguments, 0);
+            args[0] = JSON.stringify({type: "err", repl: repl, value: chunk});
+            socket.write.apply(socket, args);
+            socket.write("\0");
+        };
+    })(process.stderr.write);
+
+
     dom.on("error", function(ue) {
         console.error(ue.stack);
     });
@@ -84,18 +83,21 @@ var server = net.createServer(function (socket) {
 
             if(err) {
                 socket.write(JSON.stringify({
+                    type: "result",
                     repl: repl,
                     status: "exception",
                     value: err.stack
                 }));
             } else if(ret !== undefined && ret !== null) {
                 socket.write(JSON.stringify({
+                    type: "result",
                     repl: repl,
                     status: "success",
                     value: ret.toString()
                 }));
             } else {
                 socket.write(JSON.stringify({
+                    type: "result",
                     repl: repl,
                     status: "success",
                     value: null

From a1e49165a19046815651027e62ca7f0910806b40 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 19 Mar 2018 12:50:11 +0100
Subject: [PATCH 3084/4033] cljs.repl/load-namespace needs to check for
 node-module case

---
 src/main/clojure/cljs/repl.cljc | 29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 28209bc6a..3b05bd455 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -196,16 +196,11 @@
     (assoc :url (io/resource (:file ijs)))))
 
 (defn ns->input [ns opts]
-  (if-let [input (some-> (util/ns->source ns) (ana/parse-ns opts))]
-    input
-    (if-let [input (some->
-                     (get-in @env/*compiler*
-                       [:js-dependency-index (str ns)])
-                     add-url)]
-      input
+  (or (some-> (util/ns->source ns) (ana/parse-ns opts))
+      (some-> (get-in @env/*compiler* [:js-dependency-index (str ns)]) add-url)
       (throw
         (ex-info (str ns " does not exist")
-          {::error :invalid-ns})))))
+          {::error :invalid-ns}))))
 
 (defn compilable? [input]
   (contains? input :source-file))
@@ -216,14 +211,16 @@
   only once."
   ([repl-env ns] (load-namespace repl-env ns nil))
   ([repl-env ns opts]
-   (let [ns (if (and (seq? ns) (= (first ns) 'quote)) (second ns) ns)
-         input (ns->input ns opts)
-         sources (if (compilable? input)
-                   (->> (cljsc/compile-inputs [input]
-                          (merge (env->opts repl-env) opts))
-                     (remove (comp #{["goog"]} :provides)))
-                   (map #(cljsc/source-on-disk opts %)
-                     (cljsc/add-js-sources [input] opts)))]
+   (let [ns      (if (and (seq? ns) (= (first ns) 'quote)) (second ns) ns)
+         sources (seq
+                   (when-not (ana/node-module-dep? ns)
+                     (let [input (ns->input ns opts)]
+                       (if (compilable? input)
+                         (->> (cljsc/compile-inputs [input]
+                                (merge (env->opts repl-env) opts))
+                           (remove (comp #{["goog"]} :provides)))
+                         (map #(cljsc/source-on-disk opts %)
+                           (cljsc/add-js-sources [input] opts))))))]
      (when (:repl-verbose opts)
        (println (str "load-namespace " ns " , compiled:") (map :provides sources)))
      (if (:output-dir opts)

From 9222844f9726cb846614054818338864659f7597 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 19 Mar 2018 13:29:44 +0100
Subject: [PATCH 3085/4033] organize changes file for next release

---
 changes.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/changes.md b/changes.md
index 2d559ec01..b4e7b16a7 100644
--- a/changes.md
+++ b/changes.md
@@ -1,3 +1,69 @@
+## Next
+
+### Enhancements
+* 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, 
+  defaults to true when using :modules.
+* Add :webworker & :nashorn target
+* pREPL implementation (usage requires Clojure 1.10.0-alpha)
+
+### Changes
+* * CLJS-2592: :npm-deps using ES6 modules with .mjs extensions are not detected correctly
+* AOTed ClojureScript artifact is now the default, for sources only use the
+  "slim" Maven classifier
+* Bump Closure Compiler
+* REPL now show uniform prompts
+* CLJS-2660: Add cljs.core/eval which, delegates to an overridable *eval*
+* CLJS-2375: Remove AMD Module Support
+* CLJS-2413: Port core.specs.alpha to ClojureScript
+* CLJS-2423: Allow custom :output-wrapper function
+* Map entries are no longer two element vectors, now MapEntry instances
+* *print-fn* automatically set
+* CLJS-2561: AOT compile browser REPL client js
+* CLJS-2581: Create a cljs.repl/*repl-env* dynamic var and bind it around cljs repl loops
+
+### Fixes
+* CLJS-2491: Inference warnings are not reported
+* CLJS-2653: REPL crash when mapping stacktrace in Chrome for js/blah
+* CLJS-2639: Compiler crash when using aot cache with parallel compile
+* CLJS-2520: Synthesize ClojureScript version if using non-built ClojureScript dep
+* CLJS-2522: Handle sources that are maps in build-modules
+* CLJS-2521: Only expand module graph when modules are actually used
+* CLJS-2519: Module loader doesn't load :cljs-base properly
+* CLJS-2493: Self host: respect :source-map-timestamp compiler option
+* CLJS-2500: Call process-js-modules after compiler restart
+* CLJS-2516 Build API fails targeting Node (QuickStart)
+* CLJS-2462: subvec on non-integral indexes fails
+* CLJS-2474: with-meta on lazy-seq causes separate realization
+* CLJS-2501: Fix crash in cljs.util/compiled-by-version and build-options
+* CLJS-2476: recur across try should fail compilation
+* CLJS-2495: Closure compilation errors should stop Cljs compilation
+* CLJS-2496 PHM seq and iter should return MapEntry on nil key case
+* CLJS-2473: Infer character literals to have string type
+* CLJS-2455: nth fails on eduction
+* CLJS-2001: Add map-entry? predicate
+* CLJS-2131: Calling empty on a ChunkedSeq should return empty list
+* CLJS-1743: Transient maps should support IFn
+* CLJS-2452: reverse empty vector returns nil
+* CLJS-2450: Allow configuring ignored JS module extensions
+* CLJS-2417: Inter-ns s/fdef expansion side effect fails when load cached source
+* CLJS-2447: Ignore css JS modules
+* CLJS-2397: Multi-arity function instrumentation fails with :static-fns true
+  CLJS-2197: Calling instrumented var fails to check conformance
+* CLJS-2443: doseq should return nil with no collections
+* CLJS-2430: Fix foreign-libs with Node target
+* CLJS-2414: Self-host: Macro specs are instrumented
+* CLJS-2387: CLJS Analyzer does not correctly detect cache hits for analyzed spec files
+* CLJS-2405: Register dumped specs fails
+* CLJS-2416: Self-host: defn macro Var doesn't have :macro true meta
+* CLJS-2425: Remove unnecessary zero? checks from nat-int?
+* CLJS-2377: The CLJS compiled uses deprecated modules on Java 9
+* Allow clj->js to preserve namespaces
+* CLJS-2391: Unable to :stub a function using stest/instrument
+* CLJS-2378: keep the :npm-deps-installed? to avoid to reinstall NPM deps
+
 ## 1.9.946
 
 ### Changes

From ed20adefce0d3f76154577ac36814f48a5107216 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 19 Mar 2018 15:20:29 +0100
Subject: [PATCH 3086/4033] we need to empty the connq after :ready event so we
 don't try to use broken connections. browser REPL now works even after
 refresh.

---
 src/main/clojure/cljs/repl/browser.clj | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index e99bfc22c..eb552b39b 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -208,6 +208,9 @@
 
 (defmethod handle-post :ready [_ conn _]
   (send-via es ordering (fn [_] {:expecting nil :fns {}}))
+  ;; browser refresh, reset connq
+  (locking server/lock
+    (.clear server/connq))
   (send-for-eval conn
     (binding [ana/*cljs-warnings*
               (assoc ana/*cljs-warnings*

From 853260d70086671510c905926fa66ec069b76b44 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Mon, 19 Mar 2018 07:10:32 -0400
Subject: [PATCH 3087/4033] CLJS-2674: script/test-cli: repl-env filter not
 taking effect

---
 src/test/cljs_cli/cljs_cli/util.clj | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/test/cljs_cli/cljs_cli/util.clj b/src/test/cljs_cli/cljs_cli/util.clj
index f4f3aa4c4..e41d06021 100644
--- a/src/test/cljs_cli/cljs_cli/util.clj
+++ b/src/test/cljs_cli/cljs_cli/util.clj
@@ -75,7 +75,7 @@
                  "-re" *repl-env*
                  (when *repl-opts* "-ro") (when *repl-opts* *repl-opts*)]
             command-line-args))))
-    {:exit 0 :out "" :err ""}))
+    {:exit 0 :out "" :err "" :repl-env-filtered true}))
 
 (def ^:private expected-browser-err
   "Compiling client js ...\nServing HTTP on localhost port 9000\nListening for browser REPL connect ...\n")
@@ -89,5 +89,6 @@
 (defn output-is [result & expected-lines]
   (is (zero? (:exit result)))
   (maybe-print-result-err result)
-  (is (= (apply str (map print-str (interleave expected-lines (repeat "\n"))))
-        (:out result))))
+  (when-not (:repl-env-filtered result)
+    (is (= (apply str (map print-str (interleave expected-lines (repeat "\n"))))
+          (:out result)))))

From 90c0985acdb5d57edd399642512f598e793c2041 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 19 Mar 2018 17:50:35 +0100
Subject: [PATCH 3088/4033] enhance -co -ro to take EDN files or resources. Can
 compose and merge left to right via `:`.

change cljs.closure/build so that source can truly be nil, if no sources
ever supplied (no src, no main), then assume :modules is defined. Make
the necessary changes required to make this work.
---
 src/main/clojure/cljs/cli.clj     | 79 +++++++++++++++++++++----------
 src/main/clojure/cljs/closure.clj | 10 +++-
 2 files changed, 62 insertions(+), 27 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index a519970d4..81681ae94 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -172,14 +172,56 @@ classpath. Classpath-relative paths have prefix of @ or @/")
   (let [target (if (= "node" target) "nodejs" target)]
     (assoc-in cfg [:options :target] (keyword target))))
 
+(defn missing-file [x]
+  (throw
+    (ex-info
+      (str "File " x " does not exist")
+      {:cljs.main/error :invalid-arg})))
+
+(defn missing-resource [x]
+  (throw
+    (ex-info
+      (str "Resource "
+        (if (string/starts-with? x "@/")
+          (subs x 2)
+          (subs x 1))
+        " does not exist")
+      {:cljs.main/error :invalid-arg})))
+
+(defn read-edn-opts [str]
+  (letfn [(read-rsrc [rsrc-str orig-str]
+            (if-let [rsrc (io/resource rsrc-str)]
+              (edn/read-string (slurp rsrc))
+              (missing-resource orig-str)))]
+    (cond
+     (string/starts-with? str "@/") (read-rsrc (subs str 2) str)
+     (string/starts-with? str "@") (read-rsrc (subs str 1) str)
+     :else
+     (let [f (io/file str)]
+       (if (.exists f)
+         (edn/read-string (slurp f))
+         (missing-file str))))))
+
+(defn load-edn-opts [str]
+  (reduce merge {} (map read-edn-opts (string/split str #":"))))
+
 (defn- repl-env-opts-opt
   [cfg ropts]
-  (update cfg :repl-env-options merge (edn/read-string ropts)))
+  (let [ropts (string/trim ropts)
+        edn   (if (string/starts-with? ropts "{")
+                (edn/read-string ropts)
+                (load-edn-opts ropts))]
+    (println edn)
+    (update cfg :repl-env-options merge edn)))
 
 (defn- compile-opts-opt
   [cfg copts]
-  (update cfg :options merge (edn/read-string copts)))
-
+  (let [copts (string/trim copts)
+        edn   (if (string/starts-with? copts "{")
+                (edn/read-string copts)
+                (load-edn-opts copts))]
+    (println edn)
+    (update cfg :options merge edn)))
 
 (defn- init-opt
   [cfg file]
@@ -192,19 +234,9 @@ classpath. Classpath-relative paths have prefix of @ or @/")
                 (let [f (io/file file)]
                   (if (.exists f)
                     f
-                    (throw
-                      (ex-info
-                        (str "File " file " does not exist")
-                        {:cljs.main/error :invalid-arg})))))]
+                    (missing-file file))))]
     (when-not file'
-      (throw
-        (ex-info
-          (str "Resource "
-               (if (string/starts-with? file "@/")
-                 (subs file 2)
-                 (subs file 1))
-               " does not exist")
-          {:cljs.main/error :invalid-arg})))
+      (missing-resource file))
     (update-in cfg [:inits]
       (fnil conj [])
       {:type :init-script
@@ -320,18 +352,12 @@ present"
                   (string/starts-with? script "@/")
                   (if-let [rsrc (io/resource (subs script 2))]
                     (repl/load-stream renv (util/get-name rsrc) rsrc)
-                    (throw
-                      (ex-info
-                        (str "Resource script " (subs script 2) " does not exist")
-                        {:cljs.main/error :invalid-arg})))
+                    (missing-resource script))
 
                   (string/starts-with? script "@")
                   (if-let [rsrc (io/resource (subs script 1))]
                     (repl/load-stream renv (util/get-name rsrc) rsrc)
-                    (throw
-                      (ex-info
-                        (str "Resource script " (subs script 1) " does not exist")
-                        {:cljs.main/error :invalid-arg})))
+                    (missing-resource script))
 
                   (string/starts-with? script "-")
                   (throw
@@ -404,7 +430,7 @@ present"
 (defn default-compile
   [repl-env {:keys [ns args options] :as cfg}]
   (let [env-opts (repl/repl-options (repl-env))
-        main-ns  (symbol ns)
+        main-ns  (when ns (symbol ns))
         coptsf   (when-let [od (:output-dir options)]
                    (io/file od "cljsc_opts.edn"))
         opts     (as->
@@ -416,7 +442,8 @@ present"
                          (not (:target options))
                          (conj :browser-repl)))
                      options
-                     {:main main-ns}) opts
+                     (when main-ns
+                       {:main main-ns})) opts
                    (cond-> opts
                      (not (:output-to opts))
                      (assoc :output-to
@@ -429,7 +456,7 @@ present"
                      (assoc :aot-cache true)))
         convey   (into [:output-dir] repl/known-repl-opts)
         cfg      (update cfg :options merge (select-keys opts convey))
-        source   (when (= :none (:optimizations opts :none))
+        source   (when (and (= :none (:optimizations opts :none)) main-ns)
                    (:uri (build/ns->location main-ns)))
         repl?    (boolean (#{"-r" "--repl"} (first args)))
         serve?   (boolean (#{"-s" "--serve"} (first args)))
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index bfdbddbea..362ee8900 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -658,6 +658,12 @@
   (-compile [this opts] (compile-form-seq this))
   (-find-sources [this opts]
     [(ana/parse-ns this opts)])
+
+  clojure.lang.IPersistentSet
+  (-compile [this opts]
+    (doall (map (comp #(-compile % opts) util/ns->source) this)))
+  (-find-sources [this opts]
+    (into [] (mapcat #(-find-sources % opts)) this))
   )
 
 (comment
@@ -2777,7 +2783,9 @@
                  ;; reset :js-module-index so that ana/parse-ns called by -find-sources
                  ;; can find the missing JS modules
                  js-sources (env/with-compiler-env (dissoc @compiler-env :js-module-index)
-                              (-> (-find-sources source opts)
+                              (-> (if source
+                                    (-find-sources source opts)
+                                    (-find-sources (reduce into #{} (map (comp :entries val) (:modules opts))) opts))
                                   (add-dependency-sources compile-opts)))
                  opts       (handle-js-modules opts js-sources compiler-env)
                  js-sources (-> js-sources

From 625c437a046d79fc856b5747bea7a79c11fb283d Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 19 Mar 2018 18:23:37 +0100
Subject: [PATCH 3089/4033] allow nothing, or -r / -r to come directly after -c
 / --compile

do not add browser REPL to compile unless -r is specified, use :main
if provided via options

update help on -c
---
 src/main/clojure/cljs/cli.clj | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 81681ae94..2082aa312 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -429,8 +429,14 @@ present"
 
 (defn default-compile
   [repl-env {:keys [ns args options] :as cfg}]
-  (let [env-opts (repl/repl-options (repl-env))
-        main-ns  (when ns (symbol ns))
+  (let [rfs      #{"-r" "--repl"}
+        sfs      #{"-s" "--serve"}
+        env-opts (repl/repl-options (repl-env))
+        repl?    (boolean (or (rfs ns) (rfs (first args))))
+        serve?   (boolean (or (sfs ns) (sfs (first args))))
+        main-ns  (if (and ns (not ((into rfs sfs) ns)))
+                   (symbol ns)
+                   (:main options))
         coptsf   (when-let [od (:output-dir options)]
                    (io/file od "cljsc_opts.edn"))
         opts     (as->
@@ -438,9 +444,7 @@ present"
                      (when (and coptsf (.exists coptsf))
                        (edn/read-string (slurp coptsf)))
                      (select-keys env-opts
-                       (cond-> [:target]
-                         (not (:target options))
-                         (conj :browser-repl)))
+                       (cond-> [:target] repl? (conj :browser-repl)))
                      options
                      (when main-ns
                        {:main main-ns})) opts
@@ -458,8 +462,6 @@ present"
         cfg      (update cfg :options merge (select-keys opts convey))
         source   (when (and (= :none (:optimizations opts :none)) main-ns)
                    (:uri (build/ns->location main-ns)))
-        repl?    (boolean (#{"-r" "--repl"} (first args)))
-        serve?   (boolean (#{"-s" "--serve"} (first args)))
         cenv     (env/default-compiler-env)]
     (env/with-compiler-env cenv
       (if-let [path (:watch opts)]
@@ -562,9 +564,12 @@ present"
                                 :arg "ns"
                                 :doc "Call the -main function from a namespace with args"}
       ["-c" "--compile"]       {:fn compile-opt
-                                :arg "ns"
-                                :doc (str "Compile a namespace. If --repl present after "
-                                       "namespace will launch a REPL after the compile completes")}
+                                :arg "[ns]"
+                                :doc (str "Run a compile. If optional namespace specified, use as "
+                                          "the main entry point. If --repl follows, "
+                                          "will launch a REPL after the compile completes. "
+                                          "If --server follows, will start a web server that serves "
+                                          "the current directory after the compile completes.")}
       ["-s" "--serve"]         {:fn serve-opt
                                 :arg "host:port"
                                 :doc (str "Start a simple web server to serve the current directory")}

From 071988f61bd83c334483cb0e57aec1a4455a6c21 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Tue, 20 Mar 2018 03:42:49 +0100
Subject: [PATCH 3090/4033] CLJS-2676: Bad cljs.loader behavior for modules
 with multiple provides

Make the type of :out-file consistent - should be string

When computing orphans we need to dissoc all the :provided names of
the module, not just the canonical one.

Update tests
---
 src/main/clojure/cljs/closure.clj            |  2 +-
 src/main/clojure/cljs/compiler.cljc          |  4 ++--
 src/main/clojure/cljs/module_graph.cljc      |  5 ++++-
 src/test/clojure/cljs/module_graph_tests.clj | 13 ++++++++-----
 4 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 362ee8900..57e43d704 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -541,7 +541,7 @@
   IJavaScript."
   [^File file {:keys [output-file] :as opts}]
     (if output-file
-      (let [out-file (io/file (util/output-directory opts) output-file)]
+      (let [out-file (.toString (io/file (util/output-directory opts) output-file))]
         (compiled-file (comp/compile-file file out-file opts)))
       (let [path (.getPath ^File file)]
         (binding [ana/*cljs-file* path]
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 65dff6353..fcb910cb3 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1423,7 +1423,7 @@
                                               (get-in @env/*compiler* [:options :emit-constants])
                                               (conj ana/constants-ns-sym)))
                               :file        dest
-                              :out-file    dest
+                              :out-file    (.toString dest)
                               :source-file src}
                              (when sm-data
                                {:source-map (:source-map sm-data)}))]
@@ -1557,7 +1557,7 @@
                               (and (true? (:optimize-constants opts))
                                    (nil? (get-in nses [ns :defs]))))
                       (with-core-cljs opts (fn [] (ana/analyze-file src-file opts))))
-                    (assoc ns-info :out-file dest-file))))
+                    (assoc ns-info :out-file (.toString dest-file)))))
               (catch Exception e
                 (throw (ex-info (str "failed compiling file:" src) {:file src} e))))
             (throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))))
diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc
index d08c05020..532a85daf 100644
--- a/src/main/clojure/cljs/module_graph.cljc
+++ b/src/main/clojure/cljs/module_graph.cljc
@@ -231,7 +231,10 @@
                    (into {} (map assign1) e->ms))
         orphans  (zipmap
                    (map (comp str comp/munge first :provides)
-                     (-> (reduce-kv (fn [m k _] (dissoc m k)) index assigned)
+                     (-> (reduce-kv
+                           (fn [m k _]
+                             (reduce dissoc m (get-in m [k :provides])))
+                           index assigned)
                        vals set))
                    (repeat :cljs-base))]
     (merge assigned orphans)))
diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj
index bb4c73b3b..ce5a3598e 100644
--- a/src/test/clojure/cljs/module_graph_tests.clj
+++ b/src/test/clojure/cljs/module_graph_tests.clj
@@ -115,7 +115,10 @@
     (is (every?
           (fn [[e m]]
             (= m (get assigns e)))
-          assigns'))))
+          assigns'))
+    ;; events should not have been moved to :cljs-base as an orphan even though
+    ;; it provides multiple nses
+    (is (= (get assigns "events") :shared))))
 
 (def bad-modules
   {:page1 {:entries '[page1.a page1.b events]
@@ -139,10 +142,10 @@
            {:output-dir (:output-dir opts)
             :asset-path "/asset/js"
             :optimizations :none})
-        {:shared ["/asset/js/shared/a.js" "/asset/js/shared/b.js"]
-         :page1 ["/asset/js/cljs/reader.js" "/asset/js/page1/a.js" "/asset/js/page1/b.js"]
-         :page2 ["/asset/js/page2/a.js" "/asset/js/page2/b.js"]
-         :cljs-base ["/asset/js/goog/base.js" "/asset/js/cljs/core.js" "/asset/js/events.js"]}))
+        {:shared ["/asset/js/events.js" "/asset/js/shared/a.js" "/asset/js/shared/b.js"],
+         :page1 ["/asset/js/cljs/reader.js" "/asset/js/page1/a.js" "/asset/js/page1/b.js"],
+         :page2 ["/asset/js/page2/a.js" "/asset/js/page2/b.js"],
+         :cljs-base ["/asset/js/goog/base.js" "/asset/js/cljs/core.js"]}))
   (is (= (module-graph/modules->module-uris (modules opts) (inputs opts)
            {:output-dir (:output-dir opts)
             :asset-path "/asset/js"

From b05b2ca0436fea5cbbbc258362d10cef7e6deae7 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Mon, 19 Mar 2018 17:03:49 -0400
Subject: [PATCH 3091/4033] CLJS-2675: cljs.main: test-cljs-2645 failing (-co
 with :closure-defines edn)

---
 src/main/clojure/cljs/cli.clj | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 2082aa312..3da62ce00 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -211,7 +211,6 @@ classpath. Classpath-relative paths have prefix of @ or @/")
         edn   (if (string/starts-with? ropts "{")
                 (edn/read-string ropts)
                 (load-edn-opts ropts))]
-    (println edn)
     (update cfg :repl-env-options merge edn)))
 
 (defn- compile-opts-opt
@@ -220,7 +219,6 @@ classpath. Classpath-relative paths have prefix of @ or @/")
         edn   (if (string/starts-with? copts "{")
                 (edn/read-string copts)
                 (load-edn-opts copts))]
-    (println edn)
     (update cfg :options merge edn)))
 
 (defn- init-opt

From 1c1364a1e580b771b56344ae9eea35ca44fc83e2 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Tue, 20 Mar 2018 03:48:37 +0100
Subject: [PATCH 3092/4033] Update -ro, -co docstrings

---
 src/main/clojure/cljs/cli.clj | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 3da62ce00..3e0dc3aee 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -551,10 +551,14 @@ present"
                                      "webworker, none") }
       ["-ro" "--repl-opts"]    {:group ::main&compile :fn repl-env-opts-opt
                                 :arg "edn"
-                                :doc (str "Options to configure the repl-env")}
+                                :doc (str "Options to configure the repl-env, can be an EDN string or "
+                                          "colon separated list of EDN files / classpath resources. Options "
+                                          "options will be merged left to right.")}
       ["-co" "--compile-opts"] {:group ::main&compile :fn compile-opts-opt
                                 :arg "edn"
-                                :doc (str "Options to configure the build")}}
+                                :doc (str "Options to configure the build, can be an EDN string or "
+                                          "colon separated list of EDN files / classpath resources. Options "
+                                          "will be merged left to right.")}}
      :main
      {["-r" "--repl"]          {:fn repl-opt
                                 :doc "Run a repl"}

From d8991b43b73751264fdf0a0e326f098fa8561178 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Tue, 20 Mar 2018 04:03:18 +0100
Subject: [PATCH 3093/4033] reflection warning

---
 src/main/clojure/cljs/compiler.cljc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index fcb910cb3..cc945df45 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1423,7 +1423,7 @@
                                               (get-in @env/*compiler* [:options :emit-constants])
                                               (conj ana/constants-ns-sym)))
                               :file        dest
-                              :out-file    (.toString dest)
+                              :out-file    (.toString ^File dest)
                               :source-file src}
                              (when sm-data
                                {:source-map (:source-map sm-data)}))]

From 3a6e71e4bb01f97c7a86f46bfed003a602ed5ad3 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Tue, 20 Mar 2018 23:55:59 +0100
Subject: [PATCH 3094/4033] make source an optional argument to
 cljs.closure/build and cljs.build.api/build

---
 src/main/clojure/cljs/build/api.clj | 5 ++++-
 src/main/clojure/cljs/closure.clj   | 5 ++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj
index c7c94d0dd..4d10521e4 100644
--- a/src/main/clojure/cljs/build/api.clj
+++ b/src/main/clojure/cljs/build/api.clj
@@ -187,7 +187,10 @@
   (apply closure/output-unoptimized opts sources))
 
 (defn build
-  "Given a source which can be compiled, produce runnable JavaScript."
+  "Given compiler options, produce runnable JavaScript. An optional source
+   parameter may be provided."
+  ([opts]
+   (build nil opts))
   ([source opts]
    (build source opts
      (if-not (nil? env/*compiler*)
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 57e43d704..25c1ba4f5 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2695,7 +2695,10 @@
   (compile-inputs (find-sources ns opts) opts))
 
 (defn build
-  "Given a source which can be compiled, produce runnable JavaScript."
+  "Given compiler options, produce runnable JavaScript. An optional source
+   parameter may be provided."
+  ([opts]
+   (build nil opts))
   ([source opts]
     (build source opts
       (if-not (nil? env/*compiler*)

From 9d8d371dff57a737e792d3e58675b035ada3d0dd Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 21 Mar 2018 01:06:29 +0100
Subject: [PATCH 3095/4033] Fix handling of orphans.

- deepest-common-parent should consider that the module under
  consideration may in fact be the deepest common parent.
- orphan module computation was just wrong. we assign too early.
  we cannot begin to assign until we know about orphans. we now
  guess the assignments for all modules, for all module deps,
  for all orphans, and for all orphan deps. We then merge all these
  together and compute the final assignment.

Update test case so that it reflects new more optimal assignment. In the
test case cljs.core isn't moved to base but to the shared module.
---
 src/main/clojure/cljs/module_graph.cljc      | 47 ++++++++++----------
 src/test/clojure/cljs/module_graph_tests.clj |  4 +-
 2 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc
index 532a85daf..06b6afff6 100644
--- a/src/main/clojure/cljs/module_graph.cljc
+++ b/src/main/clojure/cljs/module_graph.cljc
@@ -163,8 +163,9 @@
   "Given a set of modules and a compiler :modules graph, compute the deepest
   common parent module."
   [modules all-modules]
-  (let [common-parents (reduce set/intersection
-                         (map #(set (deps-for-module % all-modules)) modules))]
+  (let [common-parents
+        (reduce set/intersection
+          (map #(conj (set (deps-for-module % all-modules)) %) modules))]
     (apply max-key
       (fn [p] (get-in all-modules [p :depth]))
       common-parents)))
@@ -213,31 +214,31 @@
                       (first maybe-assigned)
                       (deepest-common-parent maybe-assigned modules))])
         canon    (fn [xs] (into #{} (map #(canonical-name % index)) xs))
-        assigns  (fn [f]
-                   (reduce-kv
-                     (fn [ret module-name {:keys [entries] :as module}]
-                       (let [entries' (canon entries)]
-                         (reduce
-                           (fn [ret entry]
-                             (update ret entry (fnil conj #{}) module-name))
-                           ret (canon (f entries')))))
-                     {} modules))
-        e->ms    (binding [deps-for (memoize deps-for)]
-                   (assigns identity))
-        d->ms    (binding [deps-for (memoize deps-for)]
-                   (assigns #(distinct (mapcat deps %))))
-        assigned (merge
-                   (into {} (map assign1) d->ms)
-                   (into {} (map assign1) e->ms))
-        orphans  (zipmap
+        assigns  (fn [f ms]
+                   (binding [deps-for (memoize deps-for)]
+                     (reduce-kv
+                      (fn [ret module-name {:keys [entries] :as module}]
+                        (let [entries' (canon entries)]
+                          (reduce
+                            (fn [ret entry]
+                              (update ret entry (fnil conj #{}) module-name))
+                            ret (canon (f entries')))))
+                      {} ms)))
+        e->ms    (assigns identity modules)
+        d->ms    (assigns #(distinct (mapcat deps %)) modules)
+        e&d->ms  (merge-with into e->ms d->ms)
+        orphans  {:cljs-base
+                  {:entries
                    (map (comp str comp/munge first :provides)
                      (-> (reduce-kv
                            (fn [m k _]
                              (reduce dissoc m (get-in m [k :provides])))
-                           index assigned)
-                       vals set))
-                   (repeat :cljs-base))]
-    (merge assigned orphans)))
+                           index e&d->ms)
+                       vals set))}}
+        o->ms    (assigns identity orphans)
+        od->ms   (assigns #(distinct (mapcat deps %)) orphans)
+        all->ms  (merge-with into e&d->ms o->ms od->ms)]
+    (into {} (map assign1) all->ms)))
 
 (defn expand-modules
   "Given compiler :modules and a dependency sorted list of compiler inputs return
diff --git a/src/test/clojure/cljs/module_graph_tests.clj b/src/test/clojure/cljs/module_graph_tests.clj
index ce5a3598e..3ce70375b 100644
--- a/src/test/clojure/cljs/module_graph_tests.clj
+++ b/src/test/clojure/cljs/module_graph_tests.clj
@@ -142,10 +142,10 @@
            {:output-dir (:output-dir opts)
             :asset-path "/asset/js"
             :optimizations :none})
-        {:shared ["/asset/js/events.js" "/asset/js/shared/a.js" "/asset/js/shared/b.js"],
+        {:shared ["/asset/js/cljs/core.js" "/asset/js/events.js" "/asset/js/shared/a.js" "/asset/js/shared/b.js"],
          :page1 ["/asset/js/cljs/reader.js" "/asset/js/page1/a.js" "/asset/js/page1/b.js"],
          :page2 ["/asset/js/page2/a.js" "/asset/js/page2/b.js"],
-         :cljs-base ["/asset/js/goog/base.js" "/asset/js/cljs/core.js"]}))
+         :cljs-base ["/asset/js/goog/base.js"]}))
   (is (= (module-graph/modules->module-uris (modules opts) (inputs opts)
            {:output-dir (:output-dir opts)
             :asset-path "/asset/js"

From 2ee0f25fca776aea548bb3d5c33b980246b84d9d Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 21 Mar 2018 01:21:08 +0100
Subject: [PATCH 3096/4033] fix orphans bit

---
 src/main/clojure/cljs/module_graph.cljc | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc
index 06b6afff6..3548712b4 100644
--- a/src/main/clojure/cljs/module_graph.cljc
+++ b/src/main/clojure/cljs/module_graph.cljc
@@ -229,12 +229,11 @@
         e&d->ms  (merge-with into e->ms d->ms)
         orphans  {:cljs-base
                   {:entries
-                   (map (comp str comp/munge first :provides)
-                     (-> (reduce-kv
-                           (fn [m k _]
-                             (reduce dissoc m (get-in m [k :provides])))
-                           index e&d->ms)
-                       vals set))}}
+                   (->> (reduce-kv
+                          (fn [m k _]
+                            (reduce dissoc m (get-in m [k :provides])))
+                          index e&d->ms)
+                     vals (map (comp str comp/munge first :provides)) set)}}
         o->ms    (assigns identity orphans)
         od->ms   (assigns #(distinct (mapcat deps %)) orphans)
         all->ms  (merge-with into e&d->ms o->ms od->ms)]

From b949c7ffd82f410043ee44bb052caf7b42660104 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 21 Mar 2018 01:31:36 +0100
Subject: [PATCH 3097/4033] help docstring typo

---
 src/main/clojure/cljs/cli.clj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 3e0dc3aee..40fe2ce76 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -553,7 +553,7 @@ present"
                                 :arg "edn"
                                 :doc (str "Options to configure the repl-env, can be an EDN string or "
                                           "colon separated list of EDN files / classpath resources. Options "
-                                          "options will be merged left to right.")}
+                                          "will be merged left to right.")}
       ["-co" "--compile-opts"] {:group ::main&compile :fn compile-opts-opt
                                 :arg "edn"
                                 :doc (str "Options to configure the build, can be an EDN string or "

From bf6c94689a5be22c008c812f32e714f4795a3cfb Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 22 Mar 2018 09:50:41 -0400
Subject: [PATCH 3098/4033] CLJS-2687: cljs.main: Accept File/separator as -co
 and -ro delimiter

---
 src/main/clojure/cljs/cli.clj      | 6 +++---
 src/main/clojure/cljs/js_deps.cljc | 4 ++--
 src/main/clojure/cljs/util.cljc    | 4 ++++
 3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 40fe2ce76..157998ab8 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -203,7 +203,7 @@ classpath. Classpath-relative paths have prefix of @ or @/")
          (missing-file str))))))
 
 (defn load-edn-opts [str]
-  (reduce merge {} (map read-edn-opts (string/split str #":"))))
+  (reduce merge {} (map read-edn-opts (util/split-paths str))))
 
 (defn- repl-env-opts-opt
   [cfg ropts]
@@ -552,12 +552,12 @@ present"
       ["-ro" "--repl-opts"]    {:group ::main&compile :fn repl-env-opts-opt
                                 :arg "edn"
                                 :doc (str "Options to configure the repl-env, can be an EDN string or "
-                                          "colon separated list of EDN files / classpath resources. Options "
+                                          "system-dependent path-separated list of EDN files / classpath resources. Options "
                                           "will be merged left to right.")}
       ["-co" "--compile-opts"] {:group ::main&compile :fn compile-opts-opt
                                 :arg "edn"
                                 :doc (str "Options to configure the build, can be an EDN string or "
-                                          "colon separated list of EDN files / classpath resources. Options "
+                                          "system-dependent path-separated list of EDN files / classpath resources. Options "
                                           "will be merged left to right.")}}
      :main
      {["-r" "--repl"]          {:fn repl-opt
diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc
index 73f2fe605..99e03e374 100644
--- a/src/main/clojure/cljs/js_deps.cljc
+++ b/src/main/clojure/cljs/js_deps.cljc
@@ -7,7 +7,7 @@
 ;; You must not remove this notice, or any other, from this software.
 
 (ns cljs.js-deps
-  (:require [cljs.util :refer [distinct-by]]
+  (:require [cljs.util :as util :refer [distinct-by]]
             [clojure.java.io :as io]
             [clojure.string :as string])
   (:import [java.io File]
@@ -32,7 +32,7 @@
         (filter (partial instance? URLClassLoader))
         (mapcat #(.getURLs ^URLClassLoader %)))
       (-> (System/getProperty "java.class.path")
-        (string/split (re-pattern File/pathSeparator))))
+        util/split-paths))
     distinct
     (map io/file)))
 
diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc
index 646e6f520..21a438a19 100644
--- a/src/main/clojure/cljs/util.cljc
+++ b/src/main/clojure/cljs/util.cljc
@@ -111,6 +111,10 @@
   ([parts sep]
     (apply str (interpose sep parts))))
 
+(defn split-paths
+  [paths-str]
+  (string/split paths-str (re-pattern File/pathSeparator)))
+
 (declare ext)
 
 (defn ^File to-target-file

From e7ea14b50d5e9e3bac70d93dc852b3072548ff1e Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 22 Mar 2018 14:38:03 -0400
Subject: [PATCH 3099/4033] CLJS-2690: cljs.core.specs.alpha: require :reload
 is rejected

---
 src/main/cljs/cljs/core/specs/alpha.cljc | 25 ++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/src/main/cljs/cljs/core/specs/alpha.cljc b/src/main/cljs/cljs/core/specs/alpha.cljc
index 6f3341700..4b8e4d39f 100644
--- a/src/main/cljs/cljs/core/specs/alpha.cljc
+++ b/src/main/cljs/cljs/core/specs/alpha.cljc
@@ -213,22 +213,23 @@
 (s/fdef core/import
   :args ::quotable-import-list)
 
+(defn- quoted
+  "Returns a spec that accepts a (quote ...) form of the spec"
+  [spec]
+  (s/spec (s/cat :quote #{'quote} :spec spec)))
+
 (s/fdef core/require
-  :args (s/+ (s/spec (s/cat :quote #{'quote}
-                            :spec (s/alt :libspec ::libspec
-                                         :flag #{:reload :reload-all :verbose})))))
+  :args (s/+ (s/alt :libspec (quoted ::libspec)
+                    :flag #{:reload :reload-all :verbose})))
 
 (s/fdef core/require-macros
-  :args (s/+ (s/spec (s/cat :quote #{'quote}
-                            :spec (s/alt :libspec ::macros-libspec
-                                         :flag #{:reload :reload-all :verbose})))))
+  :args (s/+ (s/alt :libspec (quoted ::macros-libspec)
+                    :flag #{:reload :reload-all :verbose})))
 
 (s/fdef core/use
-  :args (s/+ (s/spec (s/cat :quote #{'quote}
-                            :spec (s/alt :libspec ::use-libspec
-                                         :flag #{:reload :reload-all :verbose})))))
+  :args (s/+ (s/alt :libspec (quoted ::use-libspec)
+                    :flag #{:reload :reload-all :verbose})))
 
 (s/fdef core/use-macros
-  :args (s/+ (s/spec (s/cat :quote #{'quote}
-                            :spec (s/alt :libspec ::use-macros-libspec
-                                         :flag #{:reload :reload-all :verbose})))))
+  :args (s/+ (s/alt :libspec (quoted ::use-macros-libspec)
+                    :flag #{:reload :reload-all :verbose})))

From 3265705f5340279596f98c407c9c514078858567 Mon Sep 17 00:00:00 2001
From: Juho Teperi 
Date: Thu, 22 Mar 2018 20:09:02 +0200
Subject: [PATCH 3100/4033] CLJS-2689: Don't try to use node resolve for goog:
 modules

---
 src/main/cljs/cljs/module_deps.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js
index a9e33999c..2c8991e29 100644
--- a/src/main/cljs/cljs/module_deps.js
+++ b/src/main/cljs/cljs/module_deps.js
@@ -81,7 +81,8 @@ let md = mdeps({
         resolver(parentOpts.basedir, id, cb);
     },
     filter: function (id) {
-        return !(target === 'nodejs' && nodeResolve.isCore(id));
+        return !(target === 'nodejs' && nodeResolve.isCore(id)) &&
+            !id.startsWith("goog:");
     },
     detect: function (src) {
         let deps = getDeps(src);

From 0dad6a2d74155ab0c930d4d1df86e2901ea6f394 Mon Sep 17 00:00:00 2001
From: Juho Teperi 
Date: Thu, 22 Mar 2018 20:56:47 +0200
Subject: [PATCH 3101/4033] CLJS-2691: goog.require in module-processed files
 shouldn't require goog.base

Using goog.require or `import x from "goog:something"` adds goog base
into module requires, but because Cljs already adds goog base file to
build always, this causes duplicate file error in optimize step.
---
 src/main/clojure/cljs/closure.clj | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 25c1ba4f5..352d2ae4a 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -1735,9 +1735,13 @@
       ;; https://github.com/google/closure-compiler/pull/2641
       (str
         "goog.provide(\"" module-name "\");\n"
-        (apply str (map (fn [n]
-                          (str "goog.require(\"" n "\");\n"))
-                        (.getRequires input)))
+        (->> (.getRequires input)
+             ;; If CJS/ES6 module uses goog.require, goog is added to requires
+             ;; but this would cause problems with Cljs.
+             (remove #{"goog"})
+             (map (fn [n]
+                    (str "goog.require(\"" n "\");\n")))
+             (apply str))
         (.toSource closure-compiler ast-root)))))
 
 (defn- package-json-entries

From b1de2f8b07207e815e601105e6835889e5d74b5b Mon Sep 17 00:00:00 2001
From: Juho Teperi 
Date: Sat, 24 Mar 2018 10:41:22 +0200
Subject: [PATCH 3102/4033] CLJS-2699: Use higher-level Closure API for
 module-processing

Also fixes CompilerInput.getRequires handling for next Closure release
---
 src/main/clojure/cljs/closure.clj | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 352d2ae4a..e71b22db5 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -37,8 +37,7 @@
               SourceMap$DetailLevel ClosureCodingConvention SourceFile
               Result JSError CheckLevel DiagnosticGroups
               CommandLineRunner AnonymousFunctionNamingPolicy
-              JSModule SourceMap Es6RewriteModules VariableMap
-              ProcessCommonJSModules Es6RewriteScriptsToModules]
+              JSModule SourceMap VariableMap]
            [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode ModuleNames]
            [com.google.javascript.rhino Node]
            [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey
@@ -1736,6 +1735,12 @@
       (str
         "goog.provide(\"" module-name "\");\n"
         (->> (.getRequires input)
+             ;; v20180204 returns string
+             ;; next Closure returns DependencyInfo.Require object
+             (map (fn [i]
+                    (if (string? i)
+                      i
+                      (.getSymbol i))))
              ;; If CJS/ES6 module uses goog.require, goog is added to requires
              ;; but this would cause problems with Cljs.
              (remove #{"goog"})
@@ -1796,9 +1801,11 @@
         ^Node js-root (.getSecondChild extern-and-js-root)
         inputs-by-name (into {} (map (juxt #(.getName %) identity) (vals (.getInputsById closure-compiler))))]
 
-    (.process (Es6RewriteScriptsToModules. closure-compiler) extern-root js-root)
-    (.process (Es6RewriteModules. closure-compiler) extern-root js-root)
-    (.process (ProcessCommonJSModules. closure-compiler) extern-root js-root)
+    ;; This will rewrite CommonJS modules
+    (.whitespaceOnlyPasses closure-compiler)
+    ;; This will take care of converting ES6 to CJS
+    ;; Based on language-in setting, this could also handle ES7/8/TypeScript transpilation.
+    (.transpileAndDontCheck closure-compiler)
 
     (map (partial add-converted-source
            closure-compiler inputs-by-name opts)

From d345e869a8c35a4bc8862cadb6d635477a38d1e6 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 24 Mar 2018 12:06:32 +0100
Subject: [PATCH 3103/4033] remove unused bindings

---
 src/main/clojure/cljs/closure.clj | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index e71b22db5..2f44ef3ee 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -1796,9 +1796,6 @@
                            (.init externs source-files options))
         _ (.parse closure-compiler)
         _ (report-failure (.getResult closure-compiler))
-        ^Node extern-and-js-root (.getRoot closure-compiler)
-        ^Node extern-root (.getFirstChild extern-and-js-root)
-        ^Node js-root (.getSecondChild extern-and-js-root)
         inputs-by-name (into {} (map (juxt #(.getName %) identity) (vals (.getInputsById closure-compiler))))]
 
     ;; This will rewrite CommonJS modules

From fef574bc4332c4e16329cf0fb0ecf969e519275b Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 24 Mar 2018 12:43:56 +0100
Subject: [PATCH 3104/4033] compile should not use cljsc_opts.edn, it's only
 for REPLs which can't get this information by any other means.

---
 src/main/clojure/cljs/cli.clj | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 157998ab8..5eebbd6b6 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -435,12 +435,8 @@ present"
         main-ns  (if (and ns (not ((into rfs sfs) ns)))
                    (symbol ns)
                    (:main options))
-        coptsf   (when-let [od (:output-dir options)]
-                   (io/file od "cljsc_opts.edn"))
         opts     (as->
                    (merge
-                     (when (and coptsf (.exists coptsf))
-                       (edn/read-string (slurp coptsf)))
                      (select-keys env-opts
                        (cond-> [:target] repl? (conj :browser-repl)))
                      options

From bbac9ec87943b1f1e912bf79bb7c42b912dc6ed7 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 24 Mar 2018 13:02:36 +0100
Subject: [PATCH 3105/4033] CLJS-2696: Large code size in Clojurescript 1.10.x
 for minimal code with optimizations advanced

Ensure printing fns don't pull in unnecessary code. Don't throw ex-info
from default *eval*
---
 src/main/cljs/cljs/core.cljs | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 098a42dbe..8b104eec7 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -187,11 +187,13 @@
   []
   (set! *print-newline* false)
   (set-print-fn!
-    (fn [& args]
-      (.apply (.-log js/console) js/console (into-array args))))
+    (fn []
+      (let [xs (js-arguments)]
+        (.apply (.-log js/console) js/console (garray/clone xs)))))
   (set-print-err-fn!
-    (fn [& args]
-      (.apply (.-error js/console) js/console (into-array args))))
+    (fn []
+      (let [xs (js-arguments)]
+        (.apply (.-error js/console) js/console (garray/clone xs)))))
   nil)
 
 (def
@@ -11503,11 +11505,15 @@ reduces them without incurring seq initialization"
     (let [system (.type js/Java "java.lang.System")]
       (set! *print-newline* false)
       (set-print-fn!
-        (fn [& args]
-          (.println (.-out system) (.join (into-array args) ""))))
+        (fn []
+          (let [xs (js-arguments)
+                s  (.join (garray/clone xs) "")]
+            (.println (.-out system) s))))
       (set-print-err-fn!
-        (fn [& args]
-          (.println (.-error system) (.join (into-array args) "")))))))
+        (fn []
+          (let [xs (js-arguments)
+                s  (.join (garray/clone xs) "")]
+            (.println (.-error system) s)))))))
 
 (maybe-enable-print!)
 
@@ -11517,7 +11523,7 @@ reduces them without incurring seq initialization"
   should be evaluated." :dynamic true}
   *eval*
   (fn [_]
-    (throw (ex-info "cljs.core/*eval* not bound" {}))))
+    (throw (js/Error. "cljs.core/*eval* not bound"))))
 
 (defn eval
   "Evaluates the form data structure (not text!) and returns the result.

From 8cebc322955dea16d24c206babd45f4003512e41 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 22 Mar 2018 09:09:11 -0400
Subject: [PATCH 3106/4033] CLJS-2685: cljs.main: Using watch breaks browser
 loading

---
 src/main/clojure/cljs/cli.clj | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 5eebbd6b6..7837f8c1d 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -459,7 +459,8 @@ present"
         cenv     (env/default-compiler-env)]
     (env/with-compiler-env cenv
       (if-let [path (:watch opts)]
-        (when-not repl?
+        (if repl?
+          (build/build source opts cenv)
           (build/watch path opts cenv))
         (build/build source opts cenv))
       (when repl?

From 0a9009c81afeb9f702ea507c05196a648467feda Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 22 Mar 2018 22:51:24 -0400
Subject: [PATCH 3107/4033] CLJS-2692: cljs.core.specs.alpha: Import list needs
 to require quote

---
 src/main/cljs/cljs/core/specs/alpha.cljc | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

diff --git a/src/main/cljs/cljs/core/specs/alpha.cljc b/src/main/cljs/cljs/core/specs/alpha.cljc
index 4b8e4d39f..1f2c9a315 100644
--- a/src/main/cljs/cljs/core/specs/alpha.cljc
+++ b/src/main/cljs/cljs/core/specs/alpha.cljc
@@ -201,23 +201,18 @@
 (s/fdef core/ns-special-form
   :args ::ns-form)
 
-(defmacro ^:private quotable
-  "Returns a spec that accepts both the spec and a (quote ...) form of the spec"
-  [spec]
-  `(s/or :spec ~spec :quoted-spec (s/cat :quote #{'quote} :spec ~spec)))
-
-(s/def ::quotable-import-list
-  (s/* (s/alt :class (quotable simple-symbol?)
-              :package-list (quotable ::package-list))))
-
-(s/fdef core/import
-  :args ::quotable-import-list)
-
 (defn- quoted
   "Returns a spec that accepts a (quote ...) form of the spec"
   [spec]
   (s/spec (s/cat :quote #{'quote} :spec spec)))
 
+(s/def ::quoted-import-list
+  (s/* (s/alt :class (quoted simple-symbol?)
+              :package-list (quoted ::package-list))))
+
+(s/fdef core/import
+  :args ::quoted-import-list)
+
 (s/fdef core/require
   :args (s/+ (s/alt :libspec (quoted ::libspec)
                     :flag #{:reload :reload-all :verbose})))

From 7127f5f1d0d7acaeea9bc28c29eb211f68b1dfc7 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Wed, 21 Mar 2018 11:23:40 -0400
Subject: [PATCH 3108/4033] CLJS-2680: Passing :watch-fn via --compile-opts to
 cljs.main

---
 src/main/clojure/cljs/closure.clj   | 68 ++++++++++++++++++-----------
 src/test/cljs_cli/cljs_cli/test.clj | 29 +++++++++++-
 src/test/cljs_cli/cljs_cli/util.clj | 20 +++++++--
 3 files changed, 87 insertions(+), 30 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 2f44ef3ee..19993a5f8 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -54,6 +54,35 @@
 (defn random-string [length]
   (apply str (take length (repeatedly random-char))))
 
+(defn- sym->var
+  "Converts a namespaced symbol to a var, loading the requisite namespace if
+  needed. For use with a function defined under a keyword in opts. The kw and
+  ex-data arguments are used to form exceptions."
+  [sym kw ex-data]
+  (let [ns     (namespace sym)
+        _      (when (nil? ns)
+                 (throw
+                   (ex-info (str kw " symbol " sym " is not fully qualified")
+                     (merge ex-data {kw sym}))))
+        var-ns (symbol ns)]
+    (when (not (find-ns var-ns))
+      (try
+        (locking ana/load-mutex
+          (require var-ns))
+        (catch Throwable t
+          (throw (ex-info (str "Cannot require namespace referred by " kw " value " sym)
+                   (merge ex-data {kw sym})
+                   t)))))
+
+    (find-var sym)))
+
+(defn- opts-fn
+  "Extracts a function from opts, by default expecting a function value, but
+  converting from a namespaced symbol if needed."
+  [kw opts]
+  (when-let [fn-or-sym (kw opts)]
+    (cond-> fn-or-sym (symbol? fn-or-sym) (sym->var kw {}))))
+
 ;; Closure API
 ;; ===========
 
@@ -2469,30 +2498,14 @@
     (js-transforms js-module opts)
 
     (symbol? preprocess)
-    (let [ns (namespace preprocess)
-          _ (when (nil? ns)
-              (throw
-                (ex-info (str "Preprocess symbol " preprocess " is not fully qualified")
-                  {:file (:file js-module)
-                   :preprocess preprocess})))
-          preprocess-ns (symbol ns)]
-      (when (not (find-ns preprocess-ns))
-        (try
-          (locking ana/load-mutex
-            (require preprocess-ns))
-          (catch Throwable t
-            (throw (ex-info (str "Cannot require namespace referred by :preprocess value " preprocess)
-                            {:file (:file js-module)
-                             :preprocess preprocess}
-                            t)))))
-
+    (let [preprocess-var (sym->var preprocess :preprocess {:file (:file js-module)})]
       (try
-        ((find-var preprocess) js-module opts)
+        (preprocess-var js-module opts)
         (catch Throwable t
           (throw (ex-info (str "Error running preprocessing function " preprocess)
-                          {:file (:file js-module)
-                           :preprocess preprocess}
-                          t)))))
+                   {:file       (:file js-module)
+                    :preprocess preprocess}
+                   t)))))
 
     :else
     (do
@@ -2895,8 +2908,13 @@
   "Given a source directory, produce runnable JavaScript. Watch the source
    directory for changes rebuilding when necessary. Takes the same arguments as
    cljs.closure/build in addition to some watch-specific options:
-    - :watch-fn, a function of no arguments to run after a successful build.
-    - :watch-error-fn, a function receiving the exception of a failed build."
+    - :watch-fn, a function of no arguments to run after a successful build. May
+                 be a function value or a namespaced symbol identifying a function,
+                 in which case the associated namespace willl be loaded and the
+                 symbol resolved.
+    - :watch-error-fn, a function receiving the exception of a failed build. May
+                       be a function value or a namespaced symbol, loaded as
+                       with :watch-fn."
   ([source opts]
     (watch source opts
       (if-not (nil? env/*compiler*)
@@ -2919,10 +2937,10 @@
                     (println "... done. Elapsed"
                       (/ (unchecked-subtract (System/nanoTime) start) 1e9) "seconds")
                     (flush))
-                  (when-let [f (:watch-fn opts)]
+                  (when-let [f (opts-fn :watch-fn opts)]
                     (f))
                   (catch Throwable e
-                    (if-let [f (:watch-error-fn opts)]
+                    (if-let [f (opts-fn :watch-error-fn opts)]
                       (f e)
                       (binding [*out* *err*]
                         (println (Throwables/getStackTraceAsString e)))))))
diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj
index 27944e3d1..ca9fbba5c 100644
--- a/src/test/cljs_cli/cljs_cli/test.clj
+++ b/src/test/cljs_cli/cljs_cli/test.clj
@@ -4,7 +4,8 @@
    [clojure.java.io :as io]
    [clojure.java.shell :as shell :refer [with-sh-dir]]
    [clojure.string :as str]
-   [cljs-cli.util :refer [cljs-main output-is with-sources with-post-condition with-repl-env-filter]]))
+   [cljs-cli.util :refer [cljs-main output-is with-sources with-in with-post-condition with-repl-env-filter repl-title]]
+   [clojure.string :as string]))
 
 (deftest eval-test
   (-> (cljs-main "-e" 3 "-e" nil "-e" 4)
@@ -96,3 +97,29 @@
       (output-is
         nil
         "{:ns cljs.user, :value 3}"))))
+
+(deftest test-cljs-2680
+  (with-repl-env-filter (complement #{"node"})                    ; Exclude Node owing to CLJS-2684
+    (with-sources {"src/foo/core.cljs" "(ns foo.core)"
+                   "src/bar/core.clj"  "(ns bar.core) (defn watch [] (prn :watch-called))"}
+      (with-in ":cljs/quit\n"
+        (with-post-condition (fn [dir]
+                               (some #{":watch-called"}
+                                 (str/split-lines (slurp (io/file dir "out" "watch.log")))))
+          (-> (cljs-main "-co" "{:watch-fn bar.core/watch}" "--watch" "src" "-c" "foo.core" "-r")
+            (output-is
+              "Watch compilation log available at: out/watch.log"
+              (repl-title)
+              "cljs.user=>")))))
+    (with-sources {"src/foo/core.cljs" "(ns foo.core"
+                   "src/bar/core.clj"  "(ns bar.core) (defn watch-error [e] (prn :watch-error-called (.getMessage e)))"}
+      (with-in ":cljs/quit\n"
+        (with-post-condition (fn [dir]
+                               (let [log-contents (slurp (io/file dir "out" "watch.log"))]
+                                 (and (str/includes? log-contents ":watch-error-called")
+                                   (str/includes? log-contents "Unexpected EOF while reading"))))
+          (-> (cljs-main "-co" "{:watch-error-fn bar.core/watch-error}" "--watch" "src" "-c" "foo.core" "-r")
+            (output-is
+              "Watch compilation log available at: out/watch.log"
+              (repl-title)
+              "cljs.user=>")))))))
diff --git a/src/test/cljs_cli/cljs_cli/util.clj b/src/test/cljs_cli/cljs_cli/util.clj
index e41d06021..1aa8ac457 100644
--- a/src/test/cljs_cli/cljs_cli/util.clj
+++ b/src/test/cljs_cli/cljs_cli/util.clj
@@ -1,9 +1,11 @@
 (ns cljs-cli.util
+  (:refer-clojure :exclude [*in*])
   (:require
    [clojure.string :as string]
    [clojure.java.io :as io]
    [clojure.java.shell :as shell]
-   [clojure.test :refer [is]])
+   [clojure.test :refer [is]]
+   [cljs.repl :as repl])
   (:import
    (java.io File)
    (java.nio.file Files CopyOption)
@@ -13,6 +15,7 @@
 (def ^:dynamic *repl-env-filter* (constantly true))
 (def ^:dynamic *repl-opts* nil)
 (def ^:dynamic *sources* nil)
+(def ^:dynamic *in* nil)
 (def ^:dynamic *post-condition* nil)
 
 (defmacro with-sources
@@ -20,6 +23,12 @@
   `(binding [*sources* ~sources]
      ~@body))
 
+
+(defmacro with-in
+  [in & body]
+  `(binding [*in* ~in]
+     ~@body))
+
 (defmacro with-post-condition
   [post-condition & body]
   `(binding [*post-condition* ~post-condition]
@@ -54,7 +63,7 @@
       (copy-uberjar temp-dir)
       (let [result (shell/with-sh-dir temp-dir
                      #_(apply println "running:" args)
-                     (apply shell/sh args))]
+                     (apply shell/sh (if *in* (concat args [:in *in*]) args)))]
         (when *post-condition*
           (is (*post-condition* temp-dir)))
         result)
@@ -90,5 +99,8 @@
   (is (zero? (:exit result)))
   (maybe-print-result-err result)
   (when-not (:repl-env-filtered result)
-    (is (= (apply str (map print-str (interleave expected-lines (repeat "\n"))))
-          (:out result)))))
+    (is (= (string/trim (apply str (map print-str (interleave expected-lines (repeat "\n")))))
+          (string/trim (:out result))))))
+
+(defn repl-title []
+  (string/trim (with-out-str (repl/repl-title))))
\ No newline at end of file

From 58fa824327115be1848fb33aa6d2076bd4654f5a Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Fri, 23 Mar 2018 15:28:49 -0400
Subject: [PATCH 3109/4033] CLJS-2697: script/bootstrap --closure-library-head
 fails

---
 script/bootstrap | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/script/bootstrap b/script/bootstrap
index a3fe8f95b..60d7bddfd 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -62,17 +62,16 @@ cd closure/library
 if [ "$1" = "--closure-library-head" ] ; then
     echo "Building against HEAD of Google Closure library..."
 
-    # Check if svn present
-    type svn >/dev/null 2>&1 || { echo >&2 "Need svn command to checkout HEAD of Google Closure library. Aborting."; exit 1; }
-
     # Existing checkout?
-    if svn info --non-interactive >/dev/null 2>&1; then
+    if [ -d closure-library ] ; then
         echo "Updating Google Closure library from HEAD..."
-        svn update -q --non-interactive
+        cd closure-library
+        git pull
+        cd ..
     else
         echo "Checking out HEAD of Google Closure library..."
         rm -rf *
-        svn checkout -q --non-interactive https://closure-library.googlecode.com/svn/trunk/ ./
+        git clone https://github.com/google/closure-library
     fi
 else
     curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/google-closure-library/$GCLOSURE_LIB_RELEASE/google-closure-library-$GCLOSURE_LIB_RELEASE.jar || { echo "Download failed."; exit 1; }
@@ -94,8 +93,8 @@ rm closure-compiler-v$CLOSURE_RELEASE.jar
 
 if [ "$1" = "--closure-library-head" ] ; then
     echo "Building lib/goog.jar..."
-    echo "jar cf ./lib/goog.jar -C closure/library/closure/ goog"
-    jar cf ./lib/goog.jar -C closure/library/closure/ goog
+    echo "jar cf ./lib/goog.jar -C closure/library/closure-library/closure/ goog"
+    jar cf ./lib/goog.jar -C closure/library/closure-library/closure/ goog
 fi
 
 echo "Fetching Rhino..."

From 927798c9ccc91c87ef58628af36fc47a6f272ffc Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Fri, 23 Mar 2018 16:17:57 -0400
Subject: [PATCH 3110/4033] CLJS-2698: Add --closure-compiler-snapshot option
 to script/bootstrap

---
 script/bootstrap | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/script/bootstrap b/script/bootstrap
index 60d7bddfd..525513cfb 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -86,10 +86,21 @@ fi
 cd ../..
 
 echo "Fetching Google Closure compiler..."
-curl --retry 3 -O -s http://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v$CLOSURE_RELEASE/closure-compiler-v$CLOSURE_RELEASE.jar || { echo "Download failed."; exit 1; }
-cp closure-compiler-v$CLOSURE_RELEASE.jar lib/closure-compiler-v$CLOSURE_RELEASE.jar
-echo "Cleaning up closure-compiler.jar..."
-rm closure-compiler-v$CLOSURE_RELEASE.jar
+if [ "$1" = "--closure-compiler-snapshot" ] ; then
+    curl --retry 3 -O -s https://oss.sonatype.org/content/repositories/snapshots/com/google/javascript/closure-compiler/1.0-SNAPSHOT/maven-metadata.xml || { echo "Download failed."; exit 1; }
+    CC_JAR_VERSION=`grep value maven-metadata.xml | head -1 | awk -F '[<>]' '/value/{print $3}'`
+    CC_JAR_NAME=closure-compiler-$CC_JAR_VERSION.jar
+    curl --retry 3 -O -s https://oss.sonatype.org/content/repositories/snapshots/com/google/javascript/closure-compiler/1.0-SNAPSHOT/$CC_JAR_NAME || { echo "Download failed."; exit 1; }
+    cp $CC_JAR_NAME lib/$CC_JAR_NAME
+    echo "Cleaning up closure-compiler.jar..."
+    rm $CC_JAR_NAME
+    rm maven-metadata.xml
+else
+    curl --retry 3 -O -s http://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v$CLOSURE_RELEASE/closure-compiler-v$CLOSURE_RELEASE.jar || { echo "Download failed."; exit 1; }
+    cp closure-compiler-v$CLOSURE_RELEASE.jar lib/closure-compiler-v$CLOSURE_RELEASE.jar
+    echo "Cleaning up closure-compiler.jar..."
+    rm closure-compiler-v$CLOSURE_RELEASE.jar
+fi
 
 if [ "$1" = "--closure-library-head" ] ; then
     echo "Building lib/goog.jar..."

From 207e2fbbed85442ca581936ca286015c1071cf60 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 24 Mar 2018 09:21:54 -0400
Subject: [PATCH 3111/4033] CLJS-2700: Test for CLJS-2680 failing for Nashorn
 and Rhino

Eliminate bad test
---
 src/test/cljs_cli/cljs_cli/test.clj | 26 --------------------------
 1 file changed, 26 deletions(-)

diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj
index ca9fbba5c..afd4a6e9a 100644
--- a/src/test/cljs_cli/cljs_cli/test.clj
+++ b/src/test/cljs_cli/cljs_cli/test.clj
@@ -97,29 +97,3 @@
       (output-is
         nil
         "{:ns cljs.user, :value 3}"))))
-
-(deftest test-cljs-2680
-  (with-repl-env-filter (complement #{"node"})                    ; Exclude Node owing to CLJS-2684
-    (with-sources {"src/foo/core.cljs" "(ns foo.core)"
-                   "src/bar/core.clj"  "(ns bar.core) (defn watch [] (prn :watch-called))"}
-      (with-in ":cljs/quit\n"
-        (with-post-condition (fn [dir]
-                               (some #{":watch-called"}
-                                 (str/split-lines (slurp (io/file dir "out" "watch.log")))))
-          (-> (cljs-main "-co" "{:watch-fn bar.core/watch}" "--watch" "src" "-c" "foo.core" "-r")
-            (output-is
-              "Watch compilation log available at: out/watch.log"
-              (repl-title)
-              "cljs.user=>")))))
-    (with-sources {"src/foo/core.cljs" "(ns foo.core"
-                   "src/bar/core.clj"  "(ns bar.core) (defn watch-error [e] (prn :watch-error-called (.getMessage e)))"}
-      (with-in ":cljs/quit\n"
-        (with-post-condition (fn [dir]
-                               (let [log-contents (slurp (io/file dir "out" "watch.log"))]
-                                 (and (str/includes? log-contents ":watch-error-called")
-                                   (str/includes? log-contents "Unexpected EOF while reading"))))
-          (-> (cljs-main "-co" "{:watch-error-fn bar.core/watch-error}" "--watch" "src" "-c" "foo.core" "-r")
-            (output-is
-              "Watch compilation log available at: out/watch.log"
-              (repl-title)
-              "cljs.user=>")))))))

From 3f24b3239cc3c0ae856f527652b1300d71c302ed Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 25 Mar 2018 00:24:09 +0100
Subject: [PATCH 3112/4033] hack for nREPL

---
 src/main/clojure/cljs/repl/browser.clj | 10 +++++++---
 src/main/clojure/cljs/repl/node.clj    |  5 +++--
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index eb552b39b..8dbe549d7 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -29,6 +29,10 @@
 (def ^:dynamic es nil)
 (def outs (ConcurrentHashMap.))
 
+(defn thread-name []
+  (let [name (.getName (Thread/currentThread))]
+    (if (string/starts-with? name "nrepl") "main" name)))
+
 (def ext->mime-type
   {".html" "text/html"
    ".css" "text/css"
@@ -73,7 +77,7 @@
     (set-return-value-fn return-value-fn)
     (server/send-and-close conn 200
       (json/write-str
-        {"repl" (.getName (Thread/currentThread))
+        {"repl" (thread-name)
          "form" form})
       "application/json")))
 
@@ -346,7 +350,7 @@
           (if launch-browser
             (maybe-browse-url base-url)
             (println (waiting-to-connect-message base-url)))))))
-  (.put outs (.getName (Thread/currentThread)) *out*)
+  (.put outs (thread-name) *out*)
   (swap! server-state update :listeners inc))
 
 (defrecord BrowserEnv []
@@ -362,7 +366,7 @@
   (-load [this provides url]
     (load-javascript this provides url))
   (-tear-down [this]
-    (.remove outs (.getName (Thread/currentThread)))
+    (.remove outs (thread-name))
     (let [server-state (:server-state this)]
       (when (zero? (:listeners (swap! server-state update :listeners dec)))
         (binding [server/state server-state] (server/stop))
diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index 526153ef8..d63dd5f09 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -29,7 +29,8 @@
 (def errs (ConcurrentHashMap.))
 
 (defn thread-name []
-  (.getName (Thread/currentThread)))
+  (let [name (.getName (Thread/currentThread))]
+    (if (string/starts-with? name "nrepl") "main" name)))
 
 (defn create-socket [^String host port]
   (let [socket (Socket. host (int port))
@@ -124,7 +125,7 @@
 (defn setup
   ([repl-env] (setup repl-env nil))
   ([{:keys [host port socket state] :as repl-env} opts]
-   (let [tname (.getName (Thread/currentThread))]
+   (let [tname (thread-name)]
      (.put results tname (LinkedBlockingQueue.))
      (.put outs tname *out*)
      (.put errs tname *err*))

From e951c9a2201d942168860b35b5890bceb97deb78 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 25 Mar 2018 00:51:12 +0100
Subject: [PATCH 3113/4033] nrepl -> nREPL

---
 src/main/clojure/cljs/repl/browser.clj | 2 +-
 src/main/clojure/cljs/repl/node.clj    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 8dbe549d7..f445a0ae4 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -31,7 +31,7 @@
 
 (defn thread-name []
   (let [name (.getName (Thread/currentThread))]
-    (if (string/starts-with? name "nrepl") "main" name)))
+    (if (string/starts-with? name "nREPL") "main" name)))
 
 (def ext->mime-type
   {".html" "text/html"
diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index d63dd5f09..7f4f5db03 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -30,7 +30,7 @@
 
 (defn thread-name []
   (let [name (.getName (Thread/currentThread))]
-    (if (string/starts-with? name "nrepl") "main" name)))
+    (if (string/starts-with? name "nREPL") "main" name)))
 
 (defn create-socket [^String host port]
   (let [socket (Socket. host (int port))

From 98e2dbb89da75a4439f50d94d6a1c8f8ac62ba13 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 25 Mar 2018 15:57:30 +0200
Subject: [PATCH 3114/4033] 1.10.238

---
 README.md  |  6 +++---
 changes.md | 14 +++++++++++++-
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 95f81b491..06c03dcfb 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,14 @@ Official web site: http://clojurescript.org
 
 ## Releases and dependency information ##
 
-Latest stable release: 1.9.946
+Latest stable release: 1.10.238
 
 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22)
 
 [Leiningen](http://github.com/technomancy/leiningen/) dependency information:
 
 ```
-[org.clojure/clojurescript "1.9.946"]
+[org.clojure/clojurescript "1.10.238"]
 ```
 
 [Maven](http://maven.apache.org) dependency information:
@@ -22,7 +22,7 @@ Latest stable release: 1.9.946
 
   org.clojure
   clojurescript
-  1.9.946
+  1.10.238
 
 ```
 
diff --git a/changes.md b/changes.md
index b4e7b16a7..f211022da 100644
--- a/changes.md
+++ b/changes.md
@@ -1,4 +1,4 @@
-## Next
+## 1.10.238
 
 ### Enhancements
 * cljs.main, simple command line access to Compiler & REPLs
@@ -25,6 +25,18 @@
 * CLJS-2581: Create a cljs.repl/*repl-env* dynamic var and bind it around cljs repl loops
 
 ### Fixes
+* CLJS-2680: Passing :watch-fn via --compile-opts to cljs.main
+* CLJS-2692: cljs.core.specs.alpha: Import list needs to require quote
+* CLJS-2696: Large code size in Clojurescript 1.10.x for minimal code with optimizations advanced
+* CLJS-2699: Use higher-level Closure API for module-processing
+* CLJS-2691: goog.require in module-processed files shouldn't require goog.base
+* CLJS-2689: Don't try to use node resolve for goog: modules
+* CLJS-2676: Bad cljs.loader behavior for modules with multiple provides
+* CLJS-2673: Regression: Can't require cljs.js
+* CLJS-2650: Fix JAR compilation of cljs.loader
+* CLJS-2671: Double analysis warning for source in JAR with AOT cache
+* CLJS-2643: Socket REPL output can be directed to the wrong place
+* CLJS-2670: Update cljs.compiler/warning-types
 * CLJS-2491: Inference warnings are not reported
 * CLJS-2653: REPL crash when mapping stacktrace in Chrome for js/blah
 * CLJS-2639: Compiler crash when using aot cache with parallel compile

From 9a87756a347319aa1de701f1d69c9e4e300ac87b Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 25 Mar 2018 17:14:23 +0200
Subject: [PATCH 3115/4033] remove cached brepl client JS when building

---
 script/build   | 1 +
 script/uberjar | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/script/build b/script/build
index 28254938d..86c44c2b5 100755
--- a/script/build
+++ b/script/build
@@ -11,6 +11,7 @@ fi
 
 cd `dirname $0`/..
 rm -rf target
+rm resources/brepl_client.js
 
 POM_TEMPLATE="pom.template.xml"
 POM_FILE="pom.xml"
diff --git a/script/uberjar b/script/uberjar
index 0cf280911..49e3ccccf 100755
--- a/script/uberjar
+++ b/script/uberjar
@@ -9,6 +9,8 @@ if [[ -z "$CLJS_SCRIPT_QUIET" ]]; then
   set -x
 fi
 
+rm resources/brepl_client.js
+
 # The command `git describe --match v0.0` will return a string like
 #
 # v0.0-856-g329708b

From ee15b19a00d969a309c6f198eacd50d1ff4f501c Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 25 Mar 2018 21:41:17 +0200
Subject: [PATCH 3116/4033] cleanup unused imports & requires

---
 src/main/clojure/cljs/repl/browser.clj | 1 -
 src/main/clojure/cljs/repl/node.clj    | 3 +--
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index f445a0ae4..bc2499cf1 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -14,7 +14,6 @@
             [clojure.edn :as edn]
             [clojure.data.json :as json]
             [cljs.util :as util]
-            [cljs.env :as env]
             [cljs.closure :as cljsc]
             [cljs.repl :as repl]
             [cljs.cli :as cli]
diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index 7f4f5db03..ec8b94279 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -18,8 +18,7 @@
             [clojure.data.json :as json])
   (:import [java.net Socket]
            [java.lang StringBuilder]
-           [java.io File Reader BufferedReader BufferedWriter
-           InputStreamReader IOException]
+           [java.io File BufferedReader BufferedWriter IOException]
            [java.lang ProcessBuilder Process]
            [java.util.concurrent ConcurrentHashMap LinkedBlockingQueue]))
 

From 39609cb28160036607b42cb3977584b927506e0a Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 25 Mar 2018 21:55:04 -0400
Subject: [PATCH 3117/4033] need -f

---
 script/build   | 2 +-
 script/uberjar | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/script/build b/script/build
index 86c44c2b5..eee801c50 100755
--- a/script/build
+++ b/script/build
@@ -11,7 +11,7 @@ fi
 
 cd `dirname $0`/..
 rm -rf target
-rm resources/brepl_client.js
+rm -f resources/brepl_client.js
 
 POM_TEMPLATE="pom.template.xml"
 POM_FILE="pom.xml"
diff --git a/script/uberjar b/script/uberjar
index 49e3ccccf..7ab7b78c8 100755
--- a/script/uberjar
+++ b/script/uberjar
@@ -9,7 +9,7 @@ if [[ -z "$CLJS_SCRIPT_QUIET" ]]; then
   set -x
 fi
 
-rm resources/brepl_client.js
+rm -f resources/brepl_client.js
 
 # The command `git describe --match v0.0` will return a string like
 #

From 9efb91bab16b5261a0ac53e95fe135a9e69535e0 Mon Sep 17 00:00:00 2001
From: Jannis Pohlmann 
Date: Tue, 20 Mar 2018 15:47:50 +0100
Subject: [PATCH 3118/4033] Document the new :package-json-resolution option in
 the changes file

---
 changes.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/changes.md b/changes.md
index f211022da..b28d3f1e6 100644
--- a/changes.md
+++ b/changes.md
@@ -8,9 +8,13 @@
   defaults to true when using :modules.
 * Add :webworker & :nashorn target
 * pREPL implementation (usage requires Clojure 1.10.0-alpha)
+* Add :package-json-resolution build option, allowing to choose which
+  package.json entries are being used; defaults to :webpack (if no :target
+  is set) or :nodejs (if the :target is :nodejs); also supports a custom
+  vector of entries (e.g. ["browser", "main"]).
 
 ### Changes
-* * CLJS-2592: :npm-deps using ES6 modules with .mjs extensions are not detected correctly
+* CLJS-2592: :npm-deps using ES6 modules with .mjs extensions are not detected correctly
 * AOTed ClojureScript artifact is now the default, for sources only use the
   "slim" Maven classifier
 * Bump Closure Compiler

From 9913d8e6dbf3bfed306d49569ebec083f295e010 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Tue, 27 Mar 2018 11:12:00 -0400
Subject: [PATCH 3119/4033] CLJS-2708: Windows. ClojureScript fails to compile
 when node.js module is `require`d

---
 src/main/cljs/cljs/module_deps.js | 7 ++++++-
 src/main/clojure/cljs/closure.clj | 6 +++++-
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js
index 2c8991e29..0ee1e3d47 100644
--- a/src/main/cljs/cljs/module_deps.js
+++ b/src/main/cljs/cljs/module_deps.js
@@ -1,3 +1,8 @@
+// NOTE: This code should only employ single quotes for strings.
+// If double quotes are used, then when the contents of this file
+// are passed to node via --eval on Windows, the double quotes
+// will be elided, leading to syntactically incorrect JavaScript.
+
 let fs = require('fs');
 let path = require('path');
 let mdeps = require('@cljs-oss/module-deps');
@@ -82,7 +87,7 @@ let md = mdeps({
     },
     filter: function (id) {
         return !(target === 'nodejs' && nodeResolve.isCore(id)) &&
-            !id.startsWith("goog:");
+            !id.startsWith('goog:');
     },
     detect: function (src) {
         let deps = getDeps(src);
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 19993a5f8..e4b1e0966 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2348,8 +2348,12 @@
      (when env/*compiler*
        (:options @env/*compiler*))))
   ([{:keys [file]} {:keys [target] :as opts}]
+   ;; NOTE: The code value should only employ single quotes for strings.
+   ;; If double quotes are used, then when the contents of this file
+   ;; are passed to node via --eval on Windows, the double quotes
+   ;; will be elided, leading to syntactically incorrect JavaScript.
    (let [main-entries (str "[" (->> (package-json-entries opts)
-                                    (map #(str "\"" % "\""))
+                                    (map #(str "'" % "'"))
                                     (string/join ",")) "]")
          code (-> (slurp (io/resource "cljs/module_deps.js"))
                 (string/replace "JS_FILE" (string/replace file "\\" "\\\\"))

From 875c42a73f057d6e905a263b41869988348603be Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Tue, 27 Mar 2018 15:27:43 -0400
Subject: [PATCH 3120/4033] REPL classpath lib support

---
 src/main/clojure/cljs/repl.cljc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 3b05bd455..59a8aa4ac 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -198,6 +198,7 @@
 (defn ns->input [ns opts]
   (or (some-> (util/ns->source ns) (ana/parse-ns opts))
       (some-> (get-in @env/*compiler* [:js-dependency-index (str ns)]) add-url)
+      (some-> (deps/find-classpath-lib ns))
       (throw
         (ex-info (str ns " does not exist")
           {::error :invalid-ns}))))

From 3392d2ae3fc438ac13384b1bddc29f1bf12f66da Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Tue, 27 Mar 2018 23:25:46 -0400
Subject: [PATCH 3121/4033] CLJS-2712: Make Windows CI fail if a test fails

---
 appveyor.yml | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/appveyor.yml b/appveyor.yml
index 9818d2181..bf1a7d713 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -39,8 +39,10 @@ install:
 
 test_script:
   - cmd: lein test
-  # surprisingly this is the only way to have the output shown in Appveyor
-  - cmd: powershell -noninteractive -noprofile -command .\script\test.ps1
+  - cmd: powershell -noninteractive -noprofile -command .\script\test.ps1 > test-out.txt
+  - cmd: type test-out.txt
+  # Since tests are currently only run in 1 JavaScript environment (SpiderMonkey), look for exactly one count of "0 failures, 0 errors."
+  - cmd: powershell -noninteractive -noprofile -command if (-not ((sls -Pattern '0 failures, 0 errors.' -SimpleMatch test-out.txt).count -eq 1)) { exit 1 }
 
 # Don't actually build (MSBuild).
 build: off

From f81c8bb02359befda716a9348ff5a714076c97b6 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Tue, 27 Mar 2018 18:45:34 -0400
Subject: [PATCH 3122/4033] CLJS-2711: System newline breaking some tests on
 Windows

---
 src/test/clojure/cljs/analyzer_tests.clj | 17 +++++++++--------
 src/test/clojure/cljs/test_util.clj      |  7 +++++++
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 0ee68abc1..a188165f6 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -17,7 +17,8 @@
             [cljs.closure :as closure]
             [cljs.externs :as externs]
             [cljs.analyzer :as ana]
-            [clojure.string :as string])
+            [clojure.string :as string]
+            [cljs.test-util :refer [unsplit-lines]])
   (:use clojure.test))
 
 (defn collecting-warning-handler [state]
@@ -882,13 +883,13 @@
                           (.apply (.-log js/console) js/console (into-array args)))
                         (js/console.log js/Number.MAX_VALUE)
                         (js/console.log js/Symbol.iterator)]})]
-    (is (= "var React;\nReact.Component;\n" res))))
+    (is (= (unsplit-lines ["var React;" "React.Component;"]) res))))
 
 (deftest test-method-infer
   (let [res (infer-test-helper
               {:forms '[(defn foo [^js/React.Component c]
                           (.render c))]})]
-    (is (= "var React;\nReact.Component;\nReact.Component.prototype.render;\n"
+    (is (= (unsplit-lines ["var React;" "React.Component;" "React.Component.prototype.render;"])
            res))))
 
 (deftest test-minimal-infer
@@ -912,7 +913,7 @@
                             (.wozz z)))]
                :externs ["src/test/externs/test.js"]
                :warnings ws})]
-    (is (= "Foo.Boo.prototype.wozz;\n" res))
+    (is (= (unsplit-lines ["Foo.Boo.prototype.wozz;"]) res))
     (is (= 1 (count @ws)))
     (is (string/starts-with?
           (first @ws)
@@ -926,7 +927,7 @@
                             (.-wozz z)))]
                :externs ["src/test/externs/test.js"]
                :warnings ws})]
-    (is (= "Foo.Boo.prototype.wozz;\n" res))
+    (is (= (unsplit-lines ["Foo.Boo.prototype.wozz;"]) res))
     (is (= 1 (count @ws)))
     (is (string/starts-with?
           (first @ws)
@@ -939,7 +940,7 @@
                            (.gozMethod a))]
                :externs ["src/test/externs/test.js"]
                :warnings ws})]
-    (is (= "Foo.prototype.gozMethod;\n" res))
+    (is (= (unsplit-lines ["Foo.prototype.gozMethod;"]) res))
     (is (= 1 (count @ws)))
     (is (string/starts-with?
           (first @ws)
@@ -951,7 +952,7 @@
               {:forms '[(.gozMethod (js/baz))]
                :externs ["src/test/externs/test.js"]
                :warnings ws})]
-    (is (= "Foo.prototype.gozMethod;\n" res))
+    (is (= (unsplit-lines ["Foo.prototype.gozMethod;"]) res))
     (is (= 1 (count @ws)))
     (is (string/starts-with?
           (first @ws)
@@ -965,7 +966,7 @@
                         (.log js/console (.-Component React))]
                :externs ["src/test/externs/test.js"]
                :warnings ws})]
-    (is (= "var require;\nObject.Component;\n" res))
+    (is (= (unsplit-lines ["var require;" "Object.Component;"]) res))
     (is (= 1 (count @ws)))
     (is (string/starts-with?
           (first @ws)
diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj
index c1ec705ea..2d4ece0ad 100644
--- a/src/test/clojure/cljs/test_util.clj
+++ b/src/test/clojure/cljs/test_util.clj
@@ -62,3 +62,10 @@
 
 (defn platform-path [path]
   (.replace path \/ (.charAt (str File/separator) 0)))
+
+(defn unsplit-lines
+  "Forms a string wherein each line is followed by a system-dependent newline.
+  Roughly an inverse of clojure.string/split-lines."
+  [lines]
+  (with-out-str
+    (run! println lines)))

From 5b5eaf5af3de1a850aa2092254f6ccd40c3c1546 Mon Sep 17 00:00:00 2001
From: rauhs 
Date: Sat, 1 Jul 2017 20:06:48 +0200
Subject: [PATCH 3123/4033] CLJS-2147: apply test suit

---
 src/test/cljs/cljs/apply_test.cljs | 130 +++++++++++++++++++++++++++++
 src/test/cljs/test_runner.cljs     |   2 +
 2 files changed, 132 insertions(+)
 create mode 100644 src/test/cljs/cljs/apply_test.cljs

diff --git a/src/test/cljs/cljs/apply_test.cljs b/src/test/cljs/cljs/apply_test.cljs
new file mode 100644
index 000000000..c8651bd01
--- /dev/null
+++ b/src/test/cljs/cljs/apply_test.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 cljs.apply-test
+  (:require [clojure.test :refer [deftest is]]))
+
+(defn fn-returning-this
+  [x]
+  (this-as this
+    this))
+
+(deftest js-fns-test
+  (is (= 1 (apply js/parseInt ["1"])))
+  (is (= 1 (apply js/parseInt "1" nil)))
+  (is (= 1 (apply js/parseInt "1" [])))
+  (is (= 15 (apply js/parseInt "F" [16])))
+  (is (identical? fn-returning-this (apply fn-returning-this [0]))
+      "apply should supply the this object to be the function itself"))
+
+(deftest data-structures-test
+  (is (= 1 (apply #{1} [1])))
+  (is (= nil (apply #{1} [2])))
+  (is (= 1 (apply #{1} 1 [2])))
+  (is (= 2 (apply #{} 1 [2])))
+  (is (thrown? js/Error (apply #{} []))
+      "We should still get wrong arity errors"))
+
+(def meta-f (with-meta (fn [& a] a) {}))
+
+;; more data structure test:
+(deftest meta-fn-test
+  (is (= nil (apply meta-f [])))
+  (is (= '(1)) (apply meta-f [1]))
+  (is (= '(1)) (apply meta-f 1 []))
+  (is (= '(1 2)) (apply meta-f 1 2 []))
+  (is (= '(1 2 3)) (apply meta-f 1 2 3 []))
+  (is (= '(1 2 3 4)) (apply meta-f 1 2 3 4 []))
+  (is (= '(1 2 3 4 5)) (apply meta-f 1 2 3 4 5 []))
+  (is (= (range 1 8)) (apply meta-f 1 2 3 4 5 [6 7]))
+  ;; Currently: 20 is not seqable :(
+  #_(is (= (range 21) (apply meta-f (range 21)))
+        "Should properly call the last IFn arity with 20 args with last being a seq")
+  ;; Currently: Tries to call arity 21. Fault at .apply of the deftype proto
+  ;; Though, it could probably also be caught right by apply
+  #_(is (= (range 22) (apply meta-f (range 22)))
+        "Should properly call the last IFn arity with 20 args with last being a seq")
+  #_(is (= (range 22) (.apply meta-f nil (to-array (range 22))))
+        ".apply should also handle >20 arguments"))
+
+(deftest multi-arity-test
+  (is (= 2 (apply (fn ([a] a) ([a b] b)) 1 [2])))
+  (is (= 1 (apply (fn ([a] a) ([a b] b)) 1 [])))
+  (is (= 1 (apply (fn ([a] a) ([a b] b)) 1 nil)))
+  (is (thrown? js/Error (apply (fn ([a] a) ([a b] b)) 1 2 3 nil)))
+  (is (thrown? js/Error (apply (fn ([a b] a)
+                                 ([a b c] a)) [1]))))
+
+(deftest single-arity-variadic-test
+  (doseq [f [(fn [& r] r)
+             (fn [a & r] (list* a r))
+             (fn [a b & r] (list* a b r))
+             (fn [a b c & r] (list* a b c r))
+             (fn [a b c d & r] (list* a b c d r))
+             (fn [a b c d e & r] (list* a b c d e r))]]
+    (is (= (range 10) (apply f (range 10))))
+    (is (= (range 10) (apply f 0 (range 1 10))))
+    (is (= (range 10) (apply f 0 1 (range 2 10))))
+    (is (= (range 10) (apply f 0 1 2 (range 3 10))))
+    (is (= (range 10) (apply f 0 1 2 3 (range 4 10))))
+    (is (= (range 10) (apply f 0 1 2 3 4 (range 5 10))))
+    (is (= (range 10) (apply f 0 1 2 3 4 5 (range 6 10)))))
+  (is (nil? (apply (fn [a & b] b) [1]))
+      "rest should stay nil")
+  (is (nil? (apply (fn [a & b] b) 1 []))
+      "rest should be nil'd")
+  (is (= '(2) (apply (fn [a & b] b) 1 [2]))
+      "rest should be nil'd")
+  (is (= (range 30)
+         (apply (fn [_ _ _ _ _ a b c d e _ _ _ _ _ f g h i j k _ _ _ _ _ & R]
+                  (let [a (array)]
+                    (copy-arguments a)
+                    (concat (.slice a 0 26) R)))
+                (range 30)))
+      "Variadic function are not limited to 20 params"))
+
+(deftest multi-arity-variadic-test
+  (doseq [f [(fn ([]) ([& r] r))
+             (fn ([a]) ([a & r] (list* a r)))
+             (fn ([a]) ([a b & r] (list* a b r)))
+             (fn ([a]) ([a b c & r] (list* a b c r)))
+             (fn ([a]) ([a b c d & r] (list* a b c d r)))
+             (fn ([a]) ([a b c d e & r] (list* a b c d e r)))]]
+    (is (= (range 10) (apply f (range 10))))
+    (is (= (range 10) (apply f 0 (range 1 10))))
+    (is (= (range 10) (apply f 0 1 (range 2 10))))
+    (is (= (range 10) (apply f 0 1 2 (range 3 10))))
+    (is (= (range 10) (apply f 0 1 2 3 (range 4 10))))
+    (is (= (range 10) (apply f 0 1 2 3 4 (range 5 10))))
+    (is (= (range 10) (apply f 0 1 2 3 4 5 (range 6 10)))))
+  (is (= 1 (apply (fn ([a] a) ([a & b] b)) [1])))
+  (is (= '(2) (apply (fn ([a] a) ([a & b] b)) 1 [2])))
+  (is (= 1 (apply (fn ([a] a) ([a & b] b)) 1 [])))
+  (is (= 1 (apply (fn ([a] a) ([a & b] b)) [1])))
+  (is (= '(2 3 4 5 6) (apply (fn ([a] a) ([a & b] b)) 1 2 3 4 5 [6])))
+  (is (= '(2 3 4 5) (apply (fn ([a] a) ([a & b] b)) 1 2 3 4 5 [])))
+  (is (= (range 30)
+         (apply (fn ([a])
+                  ([_ _ _ _ _ a b c d e _ _ _ _ _ f g h i j k _ _ _ _ _ & R]
+                   (let [a (array)]
+                     (copy-arguments a)
+                     (concat (.slice a 0 26) R))))
+                (range 30)))
+      "Variadic function are not limited to 20 params"))
+
+(deftest incorrect-invokes-m-arity-test
+  ;; This is the fault of .call currently not throwing. Can't be caught by apply:
+  #_(is (thrown? js/Error
+                 (apply (fn ([a b] a)
+                          ([a b & c] a)) [1]))
+        "Apply should throw on wrong arity."))
+
+(deftest incorrect-invokes-dispatcher-test
+  ;; The dispatcher needs to look at arguments.length:
+  #_(is (thrown? js/Error (.call (fn [a b & b] a) nil 1))
+        "Dispatcher should complain about wrong arity"))
diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs
index f0801780d..4fa7a7200 100644
--- a/src/test/cljs/test_runner.cljs
+++ b/src/test/cljs/test_runner.cljs
@@ -8,6 +8,7 @@
 
 (ns test-runner
   (:require [cljs.test :refer-macros [run-tests]]
+            [cljs.apply-test]
             [cljs.primitives-test]
             [cljs.destructuring-test]
             [cljs.new-new-test]
@@ -51,6 +52,7 @@
 (set-print-fn! js/print)
 
 (run-tests
+  'cljs.apply-test
   'cljs.primitives-test
   'cljs.destructuring-test
   'cljs.new-new-test

From 574abb67d3b2fc5f357cbaeaf28386266e2405ff Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Wed, 28 Mar 2018 18:24:48 -0400
Subject: [PATCH 3124/4033] CLJS-2716: Add ChakraCore to Windows CI (AppVeyor)

---
 appveyor.yml | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/appveyor.yml b/appveyor.yml
index bf1a7d713..6ed0cdaba 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -32,17 +32,21 @@ install:
   - ps: Install-Product node $env:nodejs_version x64
   - ps: wget 'http://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/jsshell-win64.zip' -OutFile "$pwd\jsshell.zip"
   - ps: 7z x "-o$pwd\jsshell" jsshell.zip -r
+  - ps: wget 'https://aka.ms/chakracore/cc_windows_all_1_8_1' -OutFile "$pwd\chakra-core.zip"
+  - ps: 7z x "-o$pwd\chakra-core" chakra-core.zip -r
   - ps: .\script\bootstrap.ps1
   - ps: "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8"
   - ps: $env:SPIDERMONKEY_HOME = "$pwd/jsshell"
   - ps: $SPIDERMONKEY_HOME = "$pwd/jsshell"
+  - ps: $env:CHAKRACORE_HOME = "$pwd/chakra-core/x64_release"
+  - ps: $CHAKRACORE_HOME = "$pwd/chakra-core/x64_release"
 
 test_script:
   - cmd: lein test
   - cmd: powershell -noninteractive -noprofile -command .\script\test.ps1 > test-out.txt
   - cmd: type test-out.txt
-  # Since tests are currently only run in 1 JavaScript environment (SpiderMonkey), look for exactly one count of "0 failures, 0 errors."
-  - cmd: powershell -noninteractive -noprofile -command if (-not ((sls -Pattern '0 failures, 0 errors.' -SimpleMatch test-out.txt).count -eq 1)) { exit 1 }
+  # Since tests are currently only run in 2 JavaScript environments, look for exactly 2 counts of "0 failures, 0 errors."
+  - cmd: powershell -noninteractive -noprofile -command if (-not ((sls -Pattern '0 failures, 0 errors.' -SimpleMatch test-out.txt).count -eq 2)) { exit 1 }
 
 # Don't actually build (MSBuild).
 build: off

From 9cc085bb33ed52d5969499a8d03ff2305a1b2273 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 14:25:48 -0400
Subject: [PATCH 3125/4033] browser REPL should use native JSON

---
 src/main/clojure/cljs/repl/browser.clj | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index bc2499cf1..17cadf7e0 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -177,7 +177,8 @@
             (default-index (or output-to (str output-dir "/main.js")))
             "text/html" "UTF-8")
           (= path (cond->> "/main.js" output-dir (str "/" output-dir )))
-          (let [closure-defines (-> `{clojure.browser.repl/HOST ~host
+          (let [closure-defines (-> `{"goog.json.USE_NATIVE_JSON" true
+                                      clojure.browser.repl/HOST ~host
                                       clojure.browser.repl/PORT ~port}
                                   (merge (:closure-defines @browser-state))
                                   cljsc/normalize-closure-defines

From d9ff9134d89baf6cc12721d8ca8b9c9937e8331b Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 14:26:13 -0400
Subject: [PATCH 3126/4033] add browser REPL script

---
 script/browser_repl.clj | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 script/browser_repl.clj

diff --git a/script/browser_repl.clj b/script/browser_repl.clj
new file mode 100644
index 000000000..a67e4312c
--- /dev/null
+++ b/script/browser_repl.clj
@@ -0,0 +1,3 @@
+(require '[cljs.repl :as repl])
+(require '[cljs.repl.browser :as browser])
+(repl/repl (browser/repl-env))
\ No newline at end of file

From 487404cf5d70a34a9109f2a2008463a744caa46c Mon Sep 17 00:00:00 2001
From: Dieter Komendera 
Date: Mon, 26 Mar 2018 21:05:40 +0200
Subject: [PATCH 3127/4033] CLJS-2706: Better error messages when missing
 namespaces contain dashes

---
 src/main/clojure/cljs/analyzer.cljc | 4 +++-
 src/main/clojure/cljs/cli.clj       | 4 +++-
 src/main/clojure/cljs/closure.clj   | 4 +++-
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 54c15a3d9..77171d656 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -309,7 +309,9 @@
   (str "No such namespace: " ns-sym
        ", could not locate " (ns->relpath ns-sym :cljs)
        ", " (ns->relpath ns-sym :cljc)
-       ", or JavaScript source providing \"" js-provide "\""))
+       ", or JavaScript source providing \"" js-provide "\""
+    (when (string/includes? (ns->relpath ns-sym) "_")
+      " (Please check that namespaces with dashes use underscores in the ClojureScript file name)")))
 
 (defmethod error-message :undeclared-macros-ns
   [warning-type {:keys [ns-sym js-provide] :as info}]
diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 7837f8c1d..debaa62c7 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -373,7 +373,9 @@ present"
                   (when-not src
                     (throw
                       (ex-info
-                        (str "Namespace " main " does not exist")
+                        (str "Namespace " main " does not exist."
+                             (when (string/includes? main "-")
+                               " Please check that namespaces with dashes use underscores in the ClojureScript file name."))
                         {:cljs.main/error :invalid-arg})))
                   (repl/load-stream renv (util/get-name src) src)
                   (repl/evaluate-form renv (ana-api/empty-env) ""
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index e4b1e0966..3d10f5d2a 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -805,7 +805,9 @@
                  {:relative-path relpath :uri js-res :ext :js}
                  (throw
                    (IllegalArgumentException.
-                     (str "Namespace " ns " does not exist"))))))))))))
+                     (str "Namespace " ns " does not exist."
+                          (when (string/includes? ns "-")
+                            " Please check that namespaces with dashes use underscores in the ClojureScript file name.")))))))))))))
 
 (defn cljs-dependencies
   "Given a list of all required namespaces, return a list of

From b471421f945615467cfc097d5e43bf73af1f5d33 Mon Sep 17 00:00:00 2001
From: Juho Teperi 
Date: Wed, 28 Mar 2018 21:01:55 +0300
Subject: [PATCH 3128/4033] CLJS-2669: Use simple dummy package for
 test-cljs-2580

---
 src/test/clojure/cljs/closure_tests.clj | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj
index 5ce6c7381..8e90870dc 100644
--- a/src/test/clojure/cljs/closure_tests.clj
+++ b/src/test/clojure/cljs/closure_tests.clj
@@ -413,8 +413,7 @@
 
 (deftest test-cljs-2580
   (spit (io/file "package.json") "{}")
-  (let [opts {:npm-deps {"pg" "7.4.1"
-                         "pg-native" "2.2.0"}
+  (let [opts {:npm-deps {"npm-package-with-main-entry-pointing-to-folder" "1.0.0"}
               :target :nodejs}
         out (util/output-directory opts)]
     (test/delete-node-modules)
@@ -423,21 +422,21 @@
     (let [modules (closure/index-node-modules-dir)]
       (is (true? (some (fn [module]
                          (= module
-                           {:file (.getAbsolutePath (io/file "node_modules/pg/lib/index.js"))
+                           {:file (.getAbsolutePath (io/file "node_modules/npm-package-with-main-entry-pointing-to-folder/folder/index.js"))
                             :module-type :es6
-                            :provides ["pg/lib/index.js"
-                                       "pg/lib/index"
-                                       "pg"
-                                       "pg/lib"]}))
+                            :provides ["npm-package-with-main-entry-pointing-to-folder/folder/index.js"
+                                       "npm-package-with-main-entry-pointing-to-folder/folder/index"
+                                       "npm-package-with-main-entry-pointing-to-folder"
+                                       "npm-package-with-main-entry-pointing-to-folder/folder"]}))
                    modules))))
-    (let [modules (closure/index-node-modules ["pg"] opts)]
+    (let [modules (closure/index-node-modules ["npm-package-with-main-entry-pointing-to-folder"] opts)]
       (is (true? (some (fn [module]
                          (= module {:module-type :es6
-                                    :file (.getAbsolutePath (io/file "node_modules/pg/lib/index.js"))
-                                    :provides ["pg"
-                                               "pg/lib/index.js"
-                                               "pg/lib/index"
-                                               "pg/lib"]}))
+                                    :file (.getAbsolutePath (io/file "node_modules/npm-package-with-main-entry-pointing-to-folder/folder/index.js"))
+                                    :provides ["npm-package-with-main-entry-pointing-to-folder"
+                                               "npm-package-with-main-entry-pointing-to-folder/folder/index.js"
+                                               "npm-package-with-main-entry-pointing-to-folder/folder/index"
+                                               "npm-package-with-main-entry-pointing-to-folder/folder"]}))
                    modules))))
     (.delete (io/file "package.json"))
     (test/delete-node-modules)

From 9b63f9a3f870b4af57583d3bef4f1c85e11e98fd Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 15:55:42 -0400
Subject: [PATCH 3129/4033] refactor so we can do infer tests with core loaded

---
 src/test/clojure/cljs/analyzer_tests.clj | 51 +++++++++++++++++++-----
 1 file changed, 40 insertions(+), 11 deletions(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index a188165f6..ecb04e837 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -857,21 +857,33 @@
           ))))
   )
 
-(defn infer-test-helper [{:keys [forms externs warnings warn]}]
+(def core-inferred
+  ["var setTimeout;" "var process;" "process.hrtime;"
+   "goog.isArrayLike;" "Java.type;" "Object.out;" "Object.out.println;"
+   "Object.error;" "Object.error.println;"])
+
+(defn infer-test-helper [{:keys [forms externs warnings warn with-core?]}]
   (let [test-cenv (atom {::a/externs
                          (externs/externs-map
-                           (closure/load-externs {:externs (or externs [])}))})]
+                           (closure/load-externs {:externs (or externs [])}))})
+        wrap      (if with-core?
+                    #(comp/with-core-cljs nil %)
+                    #(do (%)))]
     (a/with-warning-handlers [(collecting-warning-handler (or warnings (atom [])))]
-      (binding [a/*cljs-ns* a/*cljs-ns*
-                a/*cljs-warnings* (assoc a/*cljs-warnings*
-                                    :infer-warning (if (nil? warn) true warn))]
+      (binding [a/*analyze-deps* false
+                a/*cljs-ns* a/*cljs-ns*]
         (e/with-compiler-env test-cenv
-          (a/analyze-form-seq forms)
-          (with-out-str
-            (comp/emit-externs
-              (reduce util/map-merge {}
-                (map (comp :externs second)
-                  (get @test-cenv ::a/namespaces))))))))))
+          (wrap
+            (fn []
+              (binding [a/*cljs-warnings*
+                        (assoc a/*cljs-warnings*
+                          :infer-warning (if (nil? warn) true warn))]
+                (a/analyze-form-seq forms))
+              (with-out-str
+                (comp/emit-externs
+                  (reduce util/map-merge {}
+                    (map (comp :externs second)
+                      (get @test-cenv ::a/namespaces))))))))))))
 
 (deftest test-basic-infer
   (let [res (infer-test-helper
@@ -985,3 +997,20 @@
     (is (string/blank? res))
     (is (= 1 (count @ws)))
     (is (string/starts-with? (first @ws) "Cannot infer target type"))))
+
+(comment
+
+  (deftest test-cljs-1970-infer-with-cljs-literals
+    (let [ws  (atom [])
+          res (infer-test-helper
+                {:forms '[(ns cjls-1970.core)
+                          (set! *warn-on-infer* true)
+                          (defn foo [] (list))
+                          (defn bar [] (vector))]
+                 :externs ["src/test/externs/test.js"]
+                 :warnings ws
+                 :with-core? true})]
+      (println @ws)
+      (is (zero? (count @ws)))))
+
+  )
\ No newline at end of file

From 85f348ce37fb0a15ca81daecec9d737e664d1685 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 16:39:00 -0400
Subject: [PATCH 3130/4033] CLJS-1970: Cannot infer target type for list/vector
 expressions

tag deftype/record symbol as function
---
 src/main/clojure/cljs/analyzer.cljc      |  2 ++
 src/test/clojure/cljs/analyzer_tests.clj | 27 ++++++++++--------------
 2 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 77171d656..63dd6b415 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -2802,6 +2802,7 @@
            (fn [m]
              (let [m (assoc (or m {})
                        :name t
+                       :tag 'function
                        :type true
                        :num-fields (count fields)
                        :record (= :defrecord* op))]
@@ -2810,6 +2811,7 @@
                       {:protocols protocols}
                       (source-info tsym env)))))
     {:op op :env env :form form :t t :fields fields :pmasks pmasks
+     :tag 'function
      :protocols (disj protocols 'cljs.core/Object)
      :body (analyze (assoc env :locals locals) body)}))
 
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index ecb04e837..23b0d1392 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -998,19 +998,14 @@
     (is (= 1 (count @ws)))
     (is (string/starts-with? (first @ws) "Cannot infer target type"))))
 
-(comment
-
-  (deftest test-cljs-1970-infer-with-cljs-literals
-    (let [ws  (atom [])
-          res (infer-test-helper
-                {:forms '[(ns cjls-1970.core)
-                          (set! *warn-on-infer* true)
-                          (defn foo [] (list))
-                          (defn bar [] (vector))]
-                 :externs ["src/test/externs/test.js"]
-                 :warnings ws
-                 :with-core? true})]
-      (println @ws)
-      (is (zero? (count @ws)))))
-
-  )
\ No newline at end of file
+(deftest test-cljs-1970-infer-with-cljs-literals
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(ns cjls-1970.core)
+                        (set! *warn-on-infer* true)
+                        (defn foo [] (list))
+                        (defn bar [] (vector))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws
+               :with-core? true})]
+    (is (zero? (count @ws)))))

From 682f5fe37dac8ab5daf2bf83927994a149a2e772 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 16:44:38 -0400
Subject: [PATCH 3131/4033] CLJS-1918: case needs a type hint for keywords case
 when using *warn-on-infer*

add keyword hint
---
 src/main/clojure/cljs/core.cljc          |  2 +-
 src/test/clojure/cljs/analyzer_tests.clj | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 13c6d440e..43dbd4ea0 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -2294,7 +2294,7 @@
                  tests (mapv #(if (seq? %) (mapv kw-str %) [(kw-str %)]) (take-nth 2 no-default))
                  thens (vec (take-nth 2 (drop 1 no-default)))]
         `(let [~esym ~e
-               ~esym (if (keyword? ~esym) (.-fqn ~esym) nil)]
+               ~esym (if (keyword? ~esym) (.-fqn ~(vary-meta esym assoc :tag 'cljs.core/Keyword)) nil)]
            (case* ~esym ~tests ~thens ~default)))
 
       ;; equality
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 23b0d1392..fa37c3a4d 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -1009,3 +1009,16 @@
                :warnings ws
                :with-core? true})]
     (is (zero? (count @ws)))))
+
+(deftest test-cljs-1918-infer-with-case-keywords
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(ns cjls-1918.core)
+                        (defn foo [x]
+                          (cljs.core/case x
+                            :foo 1
+                            nil))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws
+               :with-core? true})]
+    (is (zero? (count @ws)))))

From ffca26bdcd7cc1d7877b81514e1ae1741b65cff0 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 16:54:25 -0400
Subject: [PATCH 3132/4033] CLJS-2385: cljs.analyzer/infer-type pass infers tag
 with incorrect priority

---
 src/main/clojure/cljs/analyzer.cljc      |  8 ++++----
 src/test/clojure/cljs/analyzer_tests.clj | 12 ++++++++++++
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 63dd6b415..e8e5b93d8 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1198,11 +1198,11 @@
   {:op :constant :env env :form sym :tag 'cljs.core/Keyword})
 
 (defn get-tag [e]
-  (if-some [tag (-> e :tag)]
+  (if-some [tag (-> e :form meta :tag)]
+    tag
+    (if-some [tag (-> e :tag)]
       tag
-      (if-some [tag (-> e :info :tag)]
-          tag
-          (-> e :form meta :tag))))
+      (-> e :info :tag))))
 
 (defn find-matching-method [f params]
   ;; if local fn, need to look in :info
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index fa37c3a4d..e87d98923 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -1022,3 +1022,15 @@
                :warnings ws
                :with-core? true})]
     (is (zero? (count @ws)))))
+
+(deftest test-cljs-2385-infer-priority
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(ns cjls-1918.core)
+                        (defn thing [{:as this}]
+                          (.componentDidUpdate ^js/Thing this))]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws
+               :with-core? true})]
+    (is (string/includes? res "Thing.prototype.componentDidUpdate;"))
+    (is (zero? (count @ws)))))

From 26565b559a0cf73c057842674cea774fd4ee00ca Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 19:09:32 -0400
Subject: [PATCH 3133/4033] make externs inference helper more like the actual
 build process

---
 src/test/clojure/cljs/analyzer_tests.clj | 45 +++++++++++++++++++++---
 1 file changed, 40 insertions(+), 5 deletions(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index e87d98923..dc6709c3d 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -862,10 +862,14 @@
    "goog.isArrayLike;" "Java.type;" "Object.out;" "Object.out.println;"
    "Object.error;" "Object.error.println;"])
 
-(defn infer-test-helper [{:keys [forms externs warnings warn with-core?]}]
-  (let [test-cenv (atom {::a/externs
-                         (externs/externs-map
-                           (closure/load-externs {:externs (or externs [])}))})
+(defn infer-test-helper [{:keys [forms externs warnings warn with-core? opts]}]
+  (let [test-cenv (if with-core?
+                    (env/default-compiler-env
+                      (closure/add-externs-sources (merge {:infer-externs true} opts)))
+                    (atom
+                      {::a/externs
+                       (externs/externs-map
+                         (closure/load-externs {:externs (or externs [])}))}))
         wrap      (if with-core?
                     #(comp/with-core-cljs nil %)
                     #(do (%)))]
@@ -875,7 +879,8 @@
         (e/with-compiler-env test-cenv
           (wrap
             (fn []
-              (binding [a/*cljs-warnings*
+              (binding [a/*analyze-deps* true
+                        a/*cljs-warnings*
                         (assoc a/*cljs-warnings*
                           :infer-warning (if (nil? warn) true warn))]
                 (a/analyze-form-seq forms))
@@ -1034,3 +1039,33 @@
                :with-core? true})]
     (is (string/includes? res "Thing.prototype.componentDidUpdate;"))
     (is (zero? (count @ws)))))
+
+(comment
+
+  ;; this includes compiled - don't want that
+  (deftest test-cljs-2392-broken-inferred-externs
+    (let [ws  (atom [])
+          res (infer-test-helper
+                {:forms '[(ns cjls-1918.core
+                            (:require [cljs.nodejscli]))]
+                 :warnings ws
+                 :with-core? true})]
+      (println res)
+      (is (zero? (count @ws)))))
+
+  (let [ws (atom [])
+        res (infer-test-helper
+              {:forms '[(ns cjls-1918.core
+                          (:require [cljs.nodejscli]))]
+               :warnings ws
+               :with-core? true
+               :opts {:target :nodejs}
+               })]
+    (println res))
+
+  (-> @(env/default-compiler-env
+         (closure/add-externs-sources {:infer-externs true}))
+    (get-in [::a/externs])
+    keys sort)
+
+  )
\ No newline at end of file

From 30bc7c2ef251d74c2ab398c2a1461984f9c80469 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 20:35:37 -0400
Subject: [PATCH 3134/4033] fix externs handling in default-compile

---
 src/main/clojure/cljs/cli.clj | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index debaa62c7..8f12963e0 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -16,6 +16,7 @@
             [cljs.analyzer.api :as ana-api]
             [cljs.compiler.api :as comp]
             [cljs.build.api :as build]
+            [cljs.closure :as closure]
             [cljs.repl :as repl])
   (:import [java.io File StringReader FileWriter]
            [java.text BreakIterator]
@@ -458,7 +459,8 @@ present"
         cfg      (update cfg :options merge (select-keys opts convey))
         source   (when (and (= :none (:optimizations opts :none)) main-ns)
                    (:uri (build/ns->location main-ns)))
-        cenv     (env/default-compiler-env)]
+        cenv     (env/default-compiler-env
+                   (closure/add-externs-sources (dissoc opts :foreign-libs)))]
     (env/with-compiler-env cenv
       (if-let [path (:watch opts)]
         (if repl?

From b2d9e6bcd7913663c35f67cdb9d348b372970794 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 20:40:38 -0400
Subject: [PATCH 3135/4033] default to :static-fns for cached core

---
 src/main/clojure/cljs/closure.clj | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 3d10f5d2a..54aa06953 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -3103,7 +3103,8 @@
     (util/mkdirs dest)
     (env/with-compiler-env (env/default-compiler-env {:infer-externs true})
       (comp/compile-file src dest
-        {:source-map true
+        {:static-fns true
+         :source-map true
          :source-map-url "core.js.map"
          :output-dir (str "src" File/separator "main" File/separator "cljs")})
       (ana/write-analysis-cache 'cljs.core cache src)

From 37063139033d6ce1f2e117d85ba2edda2ab29bdd Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 20:53:11 -0400
Subject: [PATCH 3136/4033] add test for CLJS-2392

---
 src/test/clojure/cljs/analyzer_tests.clj | 33 ++++++------------------
 1 file changed, 8 insertions(+), 25 deletions(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index dc6709c3d..a8f335a7a 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -1040,32 +1040,15 @@
     (is (string/includes? res "Thing.prototype.componentDidUpdate;"))
     (is (zero? (count @ws)))))
 
-(comment
-
-  ;; this includes compiled - don't want that
-  (deftest test-cljs-2392-broken-inferred-externs
-    (let [ws  (atom [])
-          res (infer-test-helper
-                {:forms '[(ns cjls-1918.core
-                            (:require [cljs.nodejscli]))]
-                 :warnings ws
-                 :with-core? true})]
-      (println res)
-      (is (zero? (count @ws)))))
-
-  (let [ws (atom [])
+(deftest test-cljs-2392-broken-inferred-externs
+  (let [ws  (atom [])
         res (infer-test-helper
               {:forms '[(ns cjls-1918.core
-                          (:require [cljs.nodejscli]))]
+                          (:require [cljs.nodejs]
+                                    [cljs.nodejscli]))]
                :warnings ws
                :with-core? true
-               :opts {:target :nodejs}
-               })]
-    (println res))
-
-  (-> @(env/default-compiler-env
-         (closure/add-externs-sources {:infer-externs true}))
-    (get-in [::a/externs])
-    keys sort)
-
-  )
\ No newline at end of file
+               :opts {:target :nodejs}})]
+    (not (string/includes? res "COMPILED"))
+    (not (string/includes? res "goog"))
+    (is (zero? (count @ws)))))

From aac49934f41d89c28bbc021a37878e213566decc Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 20:54:09 -0400
Subject: [PATCH 3137/4033] style, js/goog -> goog/

---
 src/main/cljs/cljs/core.cljs      | 8 ++++----
 src/main/cljs/cljs/nodejscli.cljs | 4 ++--
 src/main/clojure/cljs/core.cljc   | 2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 8b104eec7..b2719b50e 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -453,7 +453,7 @@
   ([array idx]
    (when-assert
      (try
-       (assert (or (array? array) (js/goog.isArrayLike array)))
+       (assert (or (array? array) (goog/isArrayLike array)))
        (assert (number? idx))
        (assert (not (neg? idx)))
        (assert (< idx (alength array)))
@@ -467,7 +467,7 @@
   ([array idx val]
    (when-assert
      (try
-       (assert (or (array? array) (js/goog.isArrayLike array)))
+       (assert (or (array? array) (goog/isArrayLike array)))
        (assert (number? idx))
        (assert (not (neg? idx)))
        (assert (< idx (alength array)))
@@ -479,7 +479,7 @@
 
 (defn- checked-aget'
   ([array idx]
-   {:pre [(or (array? array) (js/goog.isArrayLike array))
+   {:pre [(or (array? array) (goog/isArrayLike array))
           (number? idx) (not (neg? idx)) (< idx (alength array))]}
    (unchecked-get array idx))
   ([array idx & idxs]
@@ -487,7 +487,7 @@
 
 (defn- checked-aset'
   ([array idx val]
-   {:pre [(or (array? array) (js/goog.isArrayLike array))
+   {:pre [(or (array? array) (goog/isArrayLike array))
           (number? idx) (not (neg? idx)) (< idx (alength array))]}
    (unchecked-set array idx val))
   ([array idx idx2 & idxv]
diff --git a/src/main/cljs/cljs/nodejscli.cljs b/src/main/cljs/cljs/nodejscli.cljs
index e0a8257c0..256a4fa97 100644
--- a/src/main/cljs/cljs/nodejscli.cljs
+++ b/src/main/cljs/cljs/nodejscli.cljs
@@ -13,9 +13,9 @@
   (:require [cljs.nodejs :as nodejs]
             [goog.object :as gobj]))
 
-;; need to set js/goog.global if COMPILED
+;; need to set goog.global if COMPILED
 (when ^boolean js/COMPILED
-  (set! js/goog.global js/global))
+  (set! goog/global js/global))
 
 ;; Call the user's main function
 (when (fn? cljs.core/*main-cli-fn*)
diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 43dbd4ea0..c2c3fafde 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -2961,7 +2961,7 @@
 
 ;; INTERNAL - do not use, only for Node.js
 (core/defmacro load-file* [f]
-  `(. js/goog (~'nodeGlobalRequire ~f)))
+  `(goog/nodeGlobalRequire ~f))
 
 (core/defmacro macroexpand-1
   "If form represents a macro form, returns its expansion,

From c37442ce331431a8c6203e29556fa949ad78dfc7 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 21:22:12 -0400
Subject: [PATCH 3138/4033] CLJS-2718: Setting *warn-on-infer* in REPL throws a
 SyntaxError

refactor so that we always generate AST for set! calls, only if non-REPL
and a special case do we throw it away

now REPL behavior matches Clojure for set! even for magic compiler flags
---
 src/main/clojure/cljs/analyzer.cljc      | 88 +++++++++++-------------
 src/test/clojure/cljs/analyzer_tests.clj |  4 +-
 2 files changed, 44 insertions(+), 48 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index e8e5b93d8..300af21f7 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -2023,53 +2023,49 @@
                        [`(. ~target ~val) alt]
                        [target val])]
     (disallowing-recur
-     (let [enve (assoc env :context :expr)
-           targetexpr (cond
-                       (and (= target '*unchecked-if*) ;; TODO: proper resolve
-                            (or (true? val) (false? val)))
-                       (do
-                         (set! *unchecked-if* val)
-                         ::set-unchecked-if)
-
-                       (and (= target '*unchecked-arrays*) ;; TODO: proper resolve
-                            (or (true? val) (false? val)))
-                       (do
-                         (set! *unchecked-arrays* val)
-                         ::set-unchecked-arrays)
-
-                       (= target '*warn-on-infer*)
-                       (do
-                         (set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning true))
-                         ::set-warn-on-infer)
-
-                       (symbol? target)
-                       (do
-                         (when (some? (:const (resolve-var (dissoc env :locals) target)))
-                           (throw (error env "Can't set! a constant")))
-                         (let [local (-> env :locals target)]
-                           (when-not (or (nil? local)
-                                         (and (:field local)
-                                              (or (:mutable local)
-                                                  (:unsynchronized-mutable local)
-                                                  (:volatile-mutable local))))
-                             (throw (error env "Can't set! local var or non-mutable field"))))
-                         (analyze-symbol enve target))
-
-                       :else
-                       (when (seq? target)
-                         (let [targetexpr (analyze-seq enve target nil)]
-                           (when (:field targetexpr)
-                             targetexpr))))
-           valexpr (analyze enve val)]
-       (when-not targetexpr
-         (throw (error env "set! target must be a field or a symbol naming a var")))
-       (cond
-        (some? (#{::set-unchecked-if ::set-unchecked-arrays ::set-warn-on-infer} targetexpr))
-        {:env env :op :no-op}
+      (let [enve  (assoc env :context :expr)
+            texpr (cond
+                    (symbol? target)
+                    (do
+                      (cond
+                        (and (= target '*unchecked-if*) ;; TODO: proper resolve
+                             (or (true? val) (false? val)))
+                        (set! *unchecked-if* val)
+
+                        (and (= target '*unchecked-arrays*) ;; TODO: proper resolve
+                             (or (true? val) (false? val)))
+                        (set! *unchecked-arrays* val)
+
+                        (and (= target '*warn-on-infer*)
+                             (or (true? val) (false? val)))
+                        (set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning val)))
+                      (when (some? (:const (resolve-var (dissoc env :locals) target)))
+                        (throw (error env "Can't set! a constant")))
+                      (let [local (-> env :locals target)]
+                        (when-not (or (nil? local)
+                                      (and (:field local)
+                                           (or (:mutable local)
+                                               (:unsynchronized-mutable local)
+                                               (:volatile-mutable local))))
+                          (throw (error env "Can't set! local var or non-mutable field"))))
+                      (analyze-symbol enve target))
+
+                    :else
+                    (when (seq? target)
+                      (let [texpr (analyze-seq enve target nil)]
+                        (when (:field texpr)
+                          texpr))))
+            vexpr (analyze enve val)]
+        (when-not texpr
+          (throw (error env "set! target must be a field or a symbol naming a var")))
+        (cond
+          (and (not (:def-emits-var env)) ;; non-REPL context
+               (some? ('#{*unchecked-if* *unchecked-array* *warn-on-infer*} target)))
+          {:env env :op :no-op}
 
-        :else
-        {:env env :op :set! :form form :target targetexpr :val valexpr
-         :children [targetexpr valexpr]})))))
+          :else
+          {:env env :op :set! :form form :target texpr :val vexpr
+           :children [texpr vexpr]})))))
 
 #?(:clj (declare analyze-file))
 
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index a8f335a7a..50ffc8213 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -998,8 +998,8 @@
                           (.baz x))]
                :externs ["src/test/externs/test.js"]
                :warnings ws
-               :warn false})]
-    (is (string/blank? res))
+               :warn false
+               :with-core? true})]
     (is (= 1 (count @ws)))
     (is (string/starts-with? (first @ws) "Cannot infer target type"))))
 

From 9c1c727674aa7e2bfb399289d6f00bd51745457a Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 29 Mar 2018 21:48:18 -0400
Subject: [PATCH 3139/4033] CLJS-2678: Infer-externs doesn't work for JS
 modules using global-exports

---
 src/main/clojure/cljs/analyzer.cljc      |  9 +++++--
 src/main/clojure/cljs/env.cljc           | 26 ++++++++++----------
 src/test/clojure/cljs/analyzer_tests.clj | 30 +++++++++++++++++-------
 3 files changed, 43 insertions(+), 22 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 300af21f7..9b401de9b 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -976,8 +976,13 @@
 
 (defmethod resolve* :global
   [sym full-ns current-ns]
-  {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." (name sym)))
-   :ns current-ns})
+  (let [pre (into '[Object] (->> (string/split (name sym) #"\.") (map symbol) vec))]
+    (when-not (has-extern? pre)
+      (swap! env/*compiler* update-in
+        (into [::namespaces current-ns :externs] pre) merge {}))
+    {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." (name sym)))
+     :ns current-ns
+     :tag (with-meta 'js {:prefix pre})}))
 
 (defmethod resolve* :default
   [sym full-ns current-ns]
diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc
index dbffc3041..f91704993 100644
--- a/src/main/clojure/cljs/env.cljc
+++ b/src/main/clojure/cljs/env.cljc
@@ -43,21 +43,23 @@ state that is accessed/maintained by many different components."}
 ;; implementation-dependent data.
 (def ^:dynamic *compiler* nil)
 
+(defn default-compiler-env* [options]
+  (merge
+    {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}}
+     :cljs.analyzer/constant-table {}
+     :cljs.analyzer/data-readers {}
+     :cljs.analyzer/externs #?(:clj  (when (:infer-externs options)
+                                       (externs/externs-map (:externs-sources options)))
+                               :cljs nil)
+     :options options}
+    #?@(:clj [(when (= (:target options) :nodejs)
+                {:node-module-index deps/native-node-modules})
+              {:js-dependency-index (deps/js-dependency-index options)}])))
+
 (defn default-compiler-env
   ([] (default-compiler-env {}))
   ([options]
-   (atom
-     (merge
-       {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}}
-        :cljs.analyzer/constant-table {}
-        :cljs.analyzer/data-readers {}
-        :cljs.analyzer/externs #?(:clj  (when (:infer-externs options)
-                                          (externs/externs-map (:externs-sources options)))
-                                  :cljs nil)
-        :options options}
-       #?@(:clj [(when (= (:target options) :nodejs)
-                   {:node-module-index deps/native-node-modules})
-                 {:js-dependency-index (deps/js-dependency-index options)}])))))
+   (atom (default-compiler-env* options))))
 
 #?(:clj
    (defmacro with-compiler-env
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 50ffc8213..84828a9f3 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -862,14 +862,17 @@
    "goog.isArrayLike;" "Java.type;" "Object.out;" "Object.out.println;"
    "Object.error;" "Object.error.println;"])
 
-(defn infer-test-helper [{:keys [forms externs warnings warn with-core? opts]}]
-  (let [test-cenv (if with-core?
-                    (env/default-compiler-env
-                      (closure/add-externs-sources (merge {:infer-externs true} opts)))
-                    (atom
-                      {::a/externs
-                       (externs/externs-map
-                         (closure/load-externs {:externs (or externs [])}))}))
+(defn infer-test-helper
+  [{:keys [forms externs warnings warn js-dependency-index with-core? opts]}]
+  (let [test-cenv (atom
+                    (cond->
+                      (if with-core?
+                        (env/default-compiler-env*
+                          (closure/add-externs-sources (merge {:infer-externs true} opts)))
+                        {::a/externs
+                         (externs/externs-map
+                           (closure/load-externs {:externs (or externs [])}))})
+                      js-dependency-index (assoc :js-dependency-index js-dependency-index)))
         wrap      (if with-core?
                     #(comp/with-core-cljs nil %)
                     #(do (%)))]
@@ -1052,3 +1055,14 @@
     (not (string/includes? res "COMPILED"))
     (not (string/includes? res "goog"))
     (is (zero? (count @ws)))))
+
+(deftest test-cljs-2678-global-exports-infer
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:js-dependency-index {"react" {:global-exports '{react React}}}
+               :forms '[(ns foo.core
+                          (:require [react :as react]))
+                        (.log js/console react/Component)]
+               :warnings ws
+               :warn false})]
+    (is (= "Object.Component;\n" res))))

From d31917e80431f3c7db5931748e8c4f2eb85f51bf Mon Sep 17 00:00:00 2001
From: Juho Teperi 
Date: Wed, 4 Apr 2018 19:03:47 +0300
Subject: [PATCH 3140/4033] CLJS-2723: Update Closure-compiler to v20180319

---
 deps.edn         | 2 +-
 pom.template.xml | 2 +-
 project.clj      | 2 +-
 script/bootstrap | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/deps.edn b/deps.edn
index 31207eb45..225dba582 100644
--- a/deps.edn
+++ b/deps.edn
@@ -6,7 +6,7 @@
   org.clojure/spec.alpha {:mvn/version "0.1.143"}
   org.clojure/core.specs.alpha {:mvn/version "0.1.24"}
   org.clojure/data.json {:mvn/version "0.2.6"}
-  com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180204"}
+  com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180319"}
   org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"}
   org.mozilla/rhino {:mvn/version "1.7R5"}}
  :aliases
diff --git a/pom.template.xml b/pom.template.xml
index e1a0240e9..981469771 100644
--- a/pom.template.xml
+++ b/pom.template.xml
@@ -30,7 +30,7 @@
         
             com.google.javascript
             closure-compiler-unshaded
-            v20180204
+            v20180319
         
         
             org.clojure
diff --git a/project.clj b/project.clj
index bcef10d9a..0fd50ce71 100644
--- a/project.clj
+++ b/project.clj
@@ -16,7 +16,7 @@
                  [org.clojure/test.check "0.10.0-alpha2" :scope "test"]
                  [com.cognitect/transit-clj "0.8.300"]
                  [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"]
-                 [com.google.javascript/closure-compiler-unshaded "v20180204"]
+                 [com.google.javascript/closure-compiler-unshaded "v20180319"]
                  [org.mozilla/rhino "1.7R5"]]
   :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}
              :uberjar {:aot :all :main cljs.main}
diff --git a/script/bootstrap b/script/bootstrap
index 525513cfb..143c6bfb5 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="20180204"
+CLOSURE_RELEASE="20180319"
 DJSON_RELEASE="0.2.6"
 TRANSIT_RELEASE="0.8.300"
 GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b"

From c21f17f2082b0ef24ed3eb3a97209c0c10a607cd Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 7 Apr 2018 12:08:45 -0400
Subject: [PATCH 3141/4033] CLJS-2726: test-cljs-2678-global-exports-infer
 failing on Windows

---
 src/test/clojure/cljs/analyzer_tests.clj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 84828a9f3..708ff673c 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -1065,4 +1065,4 @@
                         (.log js/console react/Component)]
                :warnings ws
                :warn false})]
-    (is (= "Object.Component;\n" res))))
+    (is (= (unsplit-lines ["Object.Component;"]) res))))

From 8bc9b985d53a5f47f970edef7502589363ff27ed Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 7 Apr 2018 15:34:06 -0400
Subject: [PATCH 3142/4033] CLJS-2721: test-cljs-2580 failing in windows CI

---
 src/main/cljs/cljs/module_deps.js | 2 +-
 src/main/clojure/cljs/closure.clj | 6 ++++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js
index 0ee1e3d47..a5ab4d6ad 100644
--- a/src/main/cljs/cljs/module_deps.js
+++ b/src/main/cljs/cljs/module_deps.js
@@ -185,7 +185,7 @@ md.on('end', function () {
         let pkgJson = pkgJsons[i];
         const candidates = /\.js(on)?$/.test(pkgJson.mainEntry)
             ? [pkgJson.mainEntry]
-            : [pkgJson.mainEntry, pkgJson.mainEntry + '.js', pkgJson.mainEntry + '/index.js', pkgJson.mainEntry + '.json'];
+            : [pkgJson.mainEntry, pkgJson.mainEntry + '.js', pkgJson.mainEntry + 'FILE_SEPARATOR' + 'index.js', pkgJson.mainEntry + '.json'];
 
         for (let j = 0; j < candidates.length; j++) {
           const candidate = candidates[j];
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 54aa06953..0e91b368b 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2357,10 +2357,12 @@
    (let [main-entries (str "[" (->> (package-json-entries opts)
                                     (map #(str "'" % "'"))
                                     (string/join ",")) "]")
+         escape-backslashes #(string/replace % "\\" "\\\\")
          code (-> (slurp (io/resource "cljs/module_deps.js"))
-                (string/replace "JS_FILE" (string/replace file "\\" "\\\\"))
+                (string/replace "JS_FILE" (escape-backslashes file))
                 (string/replace "CLJS_TARGET" (str "" (when target (name target))))
-                (string/replace "MAIN_ENTRIES" main-entries))
+                (string/replace "MAIN_ENTRIES" main-entries)
+                (string/replace "FILE_SEPARATOR" (escape-backslashes File/separator)))
          proc (-> (ProcessBuilder. ["node" "--eval" code])
                 .start)
          is   (.getInputStream proc)

From ce505b99dd791a728102bfb95b80ec481b931173 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Wed, 11 Apr 2018 18:19:51 -0400
Subject: [PATCH 3143/4033] CLJS-2734: Add :arglists to defmulti

---
 src/main/clojure/cljs/core.cljc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index c2c3fafde..ec369346c 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -2638,6 +2638,7 @@
     :default    the default dispatch value, defaults to :default
     :hierarchy  the isa? hierarchy to use for dispatching
                 defaults to the global hierarchy"
+  {:arglists '([name docstring? attr-map? dispatch-fn & options])}
   [mm-name & options]
   (core/let [docstring   (if (core/string? (first options))
                            (first options)

From 072d87dac93f93e706f64b3d669578503e01f506 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sun, 8 Apr 2018 22:52:50 -0400
Subject: [PATCH 3144/4033] CLJS-2727: cljs.repl/err-out visible from cljs

---
 src/main/clojure/cljs/cli.clj   | 2 +-
 src/main/clojure/cljs/repl.cljc | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 8f12963e0..677dcdd94 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -409,7 +409,7 @@ present"
 (defn watch-proc [cenv path opts]
   (let [log-file (io/file (util/output-directory opts) "watch.log")]
     (util/mkdirs log-file)
-    (repl/err-out (println "Watch compilation log available at:" (str log-file)))
+    (#'repl/err-out (println "Watch compilation log available at:" (str log-file)))
     (let [log-out (FileWriter. log-file)]
       (binding [*err* log-out
                 *out* log-out]
diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 59a8aa4ac..94c7799bb 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -42,7 +42,7 @@
     :reader :repl-requires :repl-verbose :source-map-inline :watch :watch-fn
     :wrap})
 
-(defmacro err-out [& body]
+(defmacro ^:private err-out [& body]
   `(binding [*out* *err*]
      ~@body))
 

From 9dc684695cee889001d36fbe0753c8aae8dc209b Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Wed, 28 Mar 2018 14:09:47 -0400
Subject: [PATCH 3145/4033] CLJS-2715: Have goog-define return the var at the
 REPL

---
 src/main/clojure/cljs/core.cljc | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index ec369346c..595640547 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -726,6 +726,12 @@
              :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs)))))
         (reduce process-entry [] bents)))))
 
+(core/defmacro ^:private return-first
+  [& body]
+  `(let [ret# ~(first body)]
+     ~@(rest body)
+     ret#))
+
 (core/defmacro goog-define
   "Defines a var using `goog.define`. Passed default value must be
   string, number or boolean.
@@ -751,7 +757,7 @@
                        (core/string? default) "string"
                        (core/number? default) "number"
                        (core/or (core/true? default) (core/false? default)) "boolean")]
-    `(do
+    `(~(if (:def-emits-var &env) `return-first `do)
        (declare ~(core/vary-meta sym
                    (core/fn [m]
                      (core/cond-> m

From fd1784b4b167dff3d8f16c98d7e53c59409ee9d0 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 7 Apr 2018 10:47:40 -0400
Subject: [PATCH 3146/4033] CLJS-2713: test-reader fails on Windows

---
 script/test.ps1 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/script/test.ps1 b/script/test.ps1
index 50499f7ff..ed383eac5 100644
--- a/script/test.ps1
+++ b/script/test.ps1
@@ -11,7 +11,7 @@ $targets =
     @{ env="CHAKRACORE_HOME"; name="ChakraCore"; cmd={ & "$env:CHAKRACORE_HOME\ch" $testjs } }
 $ran = 0
 
-$opts = '{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :language-in :es6 :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}]}"'
+$opts = $('{:optimizations :advanced :output-wrapper true :verbose true :compiler-stats true :parallel-build true :output-dir \"builds/out-adv\" :output-to \"' + $testjs + '\" :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true :language-in :es6 :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}]}"')
 
 function Test-It($env, $name, [scriptblock] $cmd) {
     $env_val = if(Test-Path env:$env) { (Get-Item env:$env).Value } else { "" }
@@ -32,7 +32,7 @@ try {
 
     New-Item builds\out-adv -ItemType Directory -Force | Out-Null
 
-    bin\cljsc src\test\cljs $opts | Set-Content $testjs
+    bin\cljsc src\test\cljs $opts
 
     $targets | Foreach-Object { Test-It @_ }
 }

From 132d3aa232921a3cea66f830d61c89be78c581cb Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Mon, 5 Mar 2018 15:55:04 -0500
Subject: [PATCH 3147/4033] CLJS-2619: clojure.reflect needs exclude for
 macroexpand

---
 src/main/cljs/clojure/reflect.cljs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/cljs/clojure/reflect.cljs b/src/main/cljs/clojure/reflect.cljs
index 7049bed4c..9ca866107 100644
--- a/src/main/cljs/clojure/reflect.cljs
+++ b/src/main/cljs/clojure/reflect.cljs
@@ -1,6 +1,6 @@
 (ns clojure.reflect
   ^{:doc "DEPRECATED. Do not use, superceded by REPL enhancements."}
-  (:refer-clojure :exclude [meta])
+  (:refer-clojure :exclude [meta macroexpand])
   (:require [clojure.browser.net :as net]
             [clojure.browser.event :as event]))
 

From e577d46c1e9fc5ecea8e727f6f6c31cc8e5a6652 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sun, 6 May 2018 17:30:49 -0400
Subject: [PATCH 3148/4033] CLJS-2739: Optimize node_modules indexing

---
 src/main/clojure/cljs/closure.clj | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 0e91b368b..8070a71d5 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2446,7 +2446,11 @@
                         (filter package-json?)
                         (map (fn [path]
                                [path (json/read-str (slurp path))])))
-                      module-fseq)]
+                      module-fseq)
+          trim-package-json (fn [s]
+                              (if (string/ends-with? s "package.json")
+                                (subs s 0 (- (count s) 12))
+                                s))]
       (into []
         (comp
           (map #(.getAbsolutePath %))
@@ -2461,21 +2465,22 @@
                                                ;; should be the only edge case in
                                                ;; the package.json main field - Antonio
                                                (let [main (cond-> main
-                                                            (.startsWith main "./")
+                                                            (string/starts-with? main "./")
                                                             (subs 2))
                                                      main-path (-> pkg-json-path
-                                                                 (string/replace #"\\" "/")
-                                                                 (string/replace #"package\.json$" "")
+                                                                 (string/replace \\ \/)
+                                                                 trim-package-json
                                                                  (str main))]
                                                  (some (fn [candidate]
-                                                         (when (= candidate (string/replace path #"\\" "/"))
+                                                         (when (= candidate (string/replace path \\ \/))
                                                            name))
                                                    (cond-> [main-path]
-                                                     (nil? (re-find #"\.js(on)?$" main-path))
+                                                     (not (or (string/ends-with? main-path ".js")
+                                                              (string/ends-with? main-path ".json")))
                                                      (into [(str main-path ".js") (str main-path "/index.js") (str main-path ".json")]))))))
                                            pkg-jsons)]
                        {:provides (let [module-rel-name (-> (subs path (.lastIndexOf path "node_modules"))
-                                                            (string/replace #"\\" "/")
+                                                            (string/replace \\ \/)
                                                             (string/replace #"node_modules[\\\/]" ""))
                                         provides (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")]
                                                    (some? pkg-json-main)

From a3039bd393b96df0c673440971a97e70078d68d7 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 28 Apr 2018 14:09:50 -0400
Subject: [PATCH 3149/4033] CLJS-2745: Add GraalVM to the set of JavaScript
 engines we can test against

---
 .travis.yml        |  4 ++++
 script/benchmark   |  7 +++++++
 script/test        | 10 +++++++++-
 script/test-simple | 10 +++++++++-
 4 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index dc6476860..f88d798d9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,6 +14,8 @@ before_install:
   - sudo apt-get install -y libjavascriptcoregtk-3.0-bin
   - wget https://aka.ms/chakracore/cc_linux_x64_1_8_1 -O chakra-core.tar.gz
   - tar xvzf chakra-core.tar.gz
+  - wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc1/graalvm-ce-1.0.0-rc1-linux-amd64.tar.gz
+  - tar xzf graalvm-ce-1.0.0-rc1-linux-amd64.tar.gz
 
 before_script:
   - script/bootstrap
@@ -53,6 +55,8 @@ script:
   - grep '0 failures, 0 errors.' test-out.txt
   - ./ChakraCoreFiles/bin/ch builds/out-adv/core-advanced-test.js | tee test-out.txt
   - grep '0 failures, 0 errors.' test-out.txt
+  - ./graalvm-1.0.0-rc1/bin/js builds/out-adv/core-advanced-test.js | tee test-out.txt
+  - grep '0 failures, 0 errors.' test-out.txt
   - script/test-self-host | tee test-out.txt
   - grep '0 failures, 0 errors.' test-out.txt
   - script/test-self-parity | tee test-out.txt
diff --git a/script/benchmark b/script/benchmark
index 189c79d92..c1ae2d2c8 100755
--- a/script/benchmark
+++ b/script/benchmark
@@ -39,3 +39,10 @@ else
   echo "Benchmarking with ChakraCore"
   "${CHAKRACORE_HOME}/ch" builds/out-adv-bench/core-advanced-benchmark.js
 fi
+
+if [ "$GRAALVM_HOME" = "" ]; then
+  echo "GRAALVM_HOME not set, skipping GraalVM benchmarks"
+else
+  echo "Benchmarking with GraalVM"
+  "${GRAALVM_HOME}/js" builds/out-adv-bench/core-advanced-benchmark.js
+fi
diff --git a/script/test b/script/test
index eb1fa4f92..b21d32360 100755
--- a/script/test
+++ b/script/test
@@ -9,7 +9,7 @@ rm -rf package-lock.json
 echo {} > package.json
 mkdir -p builds/out-adv
 
-possible=5
+possible=6
 ran=0
 
 if ! bin/cljsc src/test/cljs "{:optimizations :advanced
@@ -80,4 +80,12 @@ else
   ran=$((ran+1))
 fi
 
+if [ "$GRAALVM_HOME" = "" ]; then
+  echo "GRAALVM_HOME not set, skipping GraalVM tests"
+else
+  echo "Testing with GraalVM"
+  "${GRAALVM_HOME}/js" builds/out-adv/core-advanced-test.js
+  ran=$((ran+1))
+fi
+
 echo "Tested with $ran out of $possible possible js targets"
diff --git a/script/test-simple b/script/test-simple
index 9cd4fc9dc..f5aa833d9 100755
--- a/script/test-simple
+++ b/script/test-simple
@@ -8,7 +8,7 @@ rm -rf package-lock.json
 echo {} > package.json
 mkdir -p builds/out-simp
 
-possible=5
+possible=6
 ran=0
 
 #bin/cljsc test >out/core-test.js
@@ -80,4 +80,12 @@ else
   ran=$[ran+1]
 fi
 
+if [ "$GRAALVM_HOME" = "" ]; then
+  echo "GRAALVM_HOME not set, skipping GraalVM tests"
+else
+  echo "Testing with GraalVM"
+  "${GRAALVM_HOME}/js" builds/out-simp/core-simple-test.js
+  ran=$[ran+1]
+fi
+
 echo "Tested with $ran out of $possible possible js targets"

From e4e4a295cda29ea81d3798e553673bc745c5f4bc Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 21 Apr 2018 21:18:09 -0400
Subject: [PATCH 3150/4033] CLJS-2741: Function invoke errors report arity off
 by 1

---
 src/main/clojure/cljs/compiler.cljc |  5 ++++-
 src/test/cljs/cljs/core_test.cljs   | 11 +++++++++++
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index cc945df45..a3981ac84 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -906,7 +906,10 @@
                 (emitln "return " n ".call(this" (if (zero? pcnt) nil
                                                      (list "," (comma-sep (take pcnt maxparams)))) ");"))))
           (emitln "}")
-          (emitln "throw(new Error('Invalid arity: ' + (arguments.length - 1)));")
+          (let [arg-count-js (if (= 'self__ (-> ms first val :params first :name))
+                               "(arguments.length - 1)"
+                               "arguments.length")]
+            (emitln "throw(new Error('Invalid arity: ' + " arg-count-js "));"))
           (emitln "};")
           (when variadic
             (emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";")
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index 8884f076b..84c2845cb 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -1537,6 +1537,17 @@
   ;; Make sure we didn't delete the alpha? fn
   (is (some? alpha-2585?)))
 
+(defn fn-2741* ([x]) ([x y]))
+(def fn-2741 fn-2741*)
+
+(deftest test-cljs-2741
+  (is (thrown-with-msg? js/Error #".*Invalid arity: 0" ((fn ([x]) ([x y])))))
+  (is (thrown-with-msg? js/Error #".*Invalid arity: 3" ((fn ([x]) ([x y])) 1 2 3)))
+  (is (thrown-with-msg? js/Error #".*Invalid arity: 0" (fn-2741)))
+  (is (thrown-with-msg? js/Error #".*Invalid arity: 3" (fn-2741 1 2 3)))
+  (is (thrown-with-msg? js/Error #".*Invalid arity: 0" ({})))
+  (is (thrown-with-msg? js/Error #".*Invalid arity: 3" ({} 1 2 3))))
+
 (comment
   ;; ObjMap
   ;; (let [ks (map (partial str "foo") (range 500))

From b31a9a9baec5401813e0f994c78a6f1c6db67a3c Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 5 May 2018 12:38:28 -0400
Subject: [PATCH 3151/4033] CLJS-2702: Accomodate new Closure Library
 dependency loading strategy

---
 src/main/cljs/cljs/bootstrap_nodejs.js    | 10 +++++++++-
 src/main/cljs/clojure/browser/repl.cljs   | 21 ++++++++++++++++-----
 src/main/clojure/cljs/repl/nashorn.clj    |  4 +++-
 src/main/clojure/cljs/repl/node.clj       |  8 ++++++--
 src/main/clojure/cljs/repl/rhino.clj      |  5 ++++-
 src/test/clojure/cljs/build_api_tests.clj |  1 +
 src/test/self/self_parity/auxiliary.cljs  |  1 -
 src/test/self/self_parity/test.cljs       |  8 ++++++--
 8 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/src/main/cljs/cljs/bootstrap_nodejs.js b/src/main/cljs/cljs/bootstrap_nodejs.js
index 5c262a314..b0dc88415 100644
--- a/src/main/cljs/cljs/bootstrap_nodejs.js
+++ b/src/main/cljs/cljs/bootstrap_nodejs.js
@@ -73,7 +73,15 @@ global.CLOSURE_IMPORT_SCRIPT = function(src, opt_sourceText) {
     // Sources are always expressed relative to closure's base.js, but
     // require() is always relative to the current source.
     if (opt_sourceText === undefined) {
-        var flags = goog.dependencies_.loadFlags[src];
+        var flags = null;
+        if (goog.debugLoader_) {
+            var dep = goog.debugLoader_.dependencies_[src];
+            if (dep) {
+                flags = dep.loadFlags;
+            }
+        } else {
+            flags = goog.dependencies_.loadFlags[src];
+        }
         if (flags && flags["foreign-lib"]) {
             nodeGlobalRequire(path.resolve(__dirname, "..", src));
         } else {
diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs
index c3c7611df..ba518b18c 100644
--- a/src/main/cljs/clojure/browser/repl.cljs
+++ b/src/main/cljs/clojure/browser/repl.cljs
@@ -199,6 +199,12 @@
           (do
             (set! load-queue #js [])
             (js/goog.writeScriptTag__ src opt_sourceText)))))
+    ;; In the latest Closure library implementation, there is no goog.writeScriptTag_,
+    ;; to monkey-patch. The behavior of interest is instead in goog.Dependency.prototype.load,
+    ;; which first checks and uses CLOSURE_IMPORT_SCRIPT if defined. So we hook our desired
+    ;; behavior here.
+    (when goog/debugLoader_
+      (set! js/CLOSURE_IMPORT_SCRIPT (.-writeScriptTag_ js/goog)))
     ;; we must reuse Closure library dev time dependency management, under namespace
     ;; reload scenarios we simply delete entries from the correct private locations
     (set! (.-require js/goog)
@@ -207,11 +213,16 @@
           (set! (.-cljsReloadAll_ js/goog) true))
         (let [reload? (or reload (.-cljsReloadAll__ js/goog))]
           (when reload?
-            (let [path (gobj/get js/goog.dependencies_.nameToPath src)]
-              (gobj/remove js/goog.dependencies_.visited path)
-              (gobj/remove js/goog.dependencies_.written path)
-              (gobj/remove js/goog.dependencies_.written
-                (str js/goog.basePath path))))
+            (if (some? goog/debugLoader_)
+              (let [path (.getPathFromDeps_ goog/debugLoader_ name)]
+                (gobj/remove (.-written_ goog/debugLoader_) path)
+                (gobj/remove (.-written_ goog/debugLoader_)
+                  (str js/goog.basePath path)))
+              (let [path (gobj/get js/goog.dependencies_.nameToPath src)]
+                (gobj/remove js/goog.dependencies_.visited path)
+                (gobj/remove js/goog.dependencies_.written path)
+                (gobj/remove js/goog.dependencies_.written
+                  (str js/goog.basePath path)))))
           (let [ret (.require__ js/goog src)]
             (when (= reload "reload-all")
               (set! (.-cljsReloadAll_ js/goog) false))
diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj
index af03d8773..4bd767dea 100644
--- a/src/main/clojure/cljs/repl/nashorn.clj
+++ b/src/main/clojure/cljs/repl/nashorn.clj
@@ -127,7 +127,9 @@
                    (when (or (not (contains? *loaded-libs* name)) reload)
                      (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
                      (js/CLOSURE_IMPORT_SCRIPT
-                       (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))))
+                       (if (some? goog/debugLoader_)
+                         (.getPathFromDeps_ goog/debugLoader_ name)
+                         (goog.object/get (.. js/goog -dependencies_ -nameToPath) name))))))))))
       (-evaluate [{engine :engine :as this} filename line js]
         (when debug (println "Evaluating: " js))
         (try
diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index ec8b94279..1a3ccbaa1 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -197,7 +197,9 @@
            '(set! (.-require js/goog)
               (fn [name]
                 (js/CLOSURE_IMPORT_SCRIPT
-                  (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))
+                  (if (some? goog/debugLoader_)
+                    (.getPathFromDeps_ goog/debugLoader_ name)
+                    (unchecked-get (.. js/goog -dependencies_ -nameToPath) name))))))
          ;; load cljs.core, setup printing
          (repl/evaluate-form repl-env env ""
            '(do
@@ -213,7 +215,9 @@
                   (when (or (not (contains? *loaded-libs* name)) reload)
                     (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
                     (js/CLOSURE_IMPORT_SCRIPT
-                      (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))))
+                      (if (some? goog/debugLoader_)
+                        (.getPathFromDeps_ goog/debugLoader_ name)
+                        (unchecked-get (.. js/goog -dependencies_ -nameToPath) name))))))))
          (node-eval repl-env
            (str "goog.global.CLOSURE_UNCOMPILED_DEFINES = "
              (json/write-str (:closure-defines opts)) ";")))))
diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj
index 3c7fc8573..2161a3512 100644
--- a/src/main/clojure/cljs/repl/rhino.clj
+++ b/src/main/clojure/cljs/repl/rhino.clj
@@ -29,6 +29,7 @@
        "        name = \"load-file\","
        "        loadFile = Packages.clojure.lang.RT[\"var\"](ns,name);\n"
        "    if(src) loadFile.invoke(___repl_env, __repl_opts, src);\n"
+       "    return true;\n"
        "};\n"))
 
 ;; =============================================================================
@@ -155,7 +156,9 @@
              (when (or (not (contains? *loaded-libs* name)) reload)
                (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
                (js/CLOSURE_IMPORT_SCRIPT
-                 (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))
+                 (if (some? goog/debugLoader_)
+                   (.getPathFromDeps_ goog/debugLoader_ name)
+                   (goog.object/get (.. js/goog -dependencies_ -nameToPath) name))))))))
 
     ;; set closure-defines
     (rhino-eval repl-env "CLOSURE_UNCOMPILED_DEFINES" 1
diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj
index 9fe0155f5..71d736c0b 100644
--- a/src/test/clojure/cljs/build_api_tests.clj
+++ b/src/test/clojure/cljs/build_api_tests.clj
@@ -187,6 +187,7 @@
    :opts
    {:output-dir output-dir
     :optimizations :none
+    :language-in :es6
     :verbose true
     :foreign-libs [{:file "src/test/cljs_build/loader_test/foreignA.js"
                     :provides ["foreign.a"]}
diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs
index 5cfb6b8c2..ef703d0db 100644
--- a/src/test/self/self_parity/auxiliary.cljs
+++ b/src/test/self/self_parity/auxiliary.cljs
@@ -87,7 +87,6 @@
     goog.iter.Iterable
     goog.iter.Iterator
     goog.json
-    goog.json.EvalJsonProcessor
     goog.json.NativeJsonProcessor
     goog.json.Replacer
     goog.json.Reviver
diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs
index 903940e9d..a03a3b76b 100644
--- a/src/test/self/self_parity/test.cljs
+++ b/src/test/self/self_parity/test.cljs
@@ -42,7 +42,9 @@
   (set! (.-require js/goog)
     (fn [name]
       (js/CLOSURE_IMPORT_SCRIPT
-        (gobj/get (.. js/goog -dependencies_ -nameToPath) name))))
+        (if goog/debugLoader_
+          (.getPathFromDeps_ goog/debugLoader_ name)
+          (gobj/get (.. js/goog -dependencies_ -nameToPath) name)))))
   ;; setup printing
   (nodejs/enable-util-print!)
   ;; redef goog.require to track loaded libs
@@ -52,7 +54,9 @@
       (when (or (not (contains? *loaded-libs* name)) reload)
         (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
         (js/CLOSURE_IMPORT_SCRIPT
-          (gobj/get (.. js/goog -dependencies_ -nameToPath) name))))))
+          (if goog/debugLoader_
+            (.getPathFromDeps_ goog/debugLoader_ name)
+            (gobj/get (.. js/goog -dependencies_ -nameToPath) name)))))))
 
 ;; Node file reading fns
 

From 78753d376d5bfe9afd1dbb98eea15fd5bd312255 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 5 May 2018 19:00:13 -0400
Subject: [PATCH 3152/4033] CLJS-2724: Native Node modules Node (like "fs")
 cannot be required

---
 src/main/clojure/cljs/cli.clj       | 162 ++++++++++++++--------------
 src/test/cljs_cli/cljs_cli/test.clj |   9 ++
 2 files changed, 90 insertions(+), 81 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 677dcdd94..7ebc8a8b4 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -299,90 +299,90 @@ present"
 
 (defn default-main
   [repl-env {:keys [main script args repl-env-options options inits] :as cfg}]
-  (env/ensure
-    (let [opts   (cond-> options
-                   (not (:output-dir options))
-                   (assoc :output-dir (temp-out-dir) :temp-output-dir? true)
-                   (not (contains? options :aot-cache))
-                   (assoc :aot-cache true))
-          reopts (merge repl-env-options
-                   (select-keys opts [:output-to :output-dir]))
-          _      (when (or ana/*verbose* (:verbose opts))
-                   (util/debug-prn "REPL env options:" (pr-str reopts)))
-          renv   (apply repl-env (mapcat identity reopts))
-          coptsf (when-let [od (:output-dir opts)]
-                   (io/file od "cljsc_opts.edn"))
-          copts  (when (and coptsf (.exists coptsf))
-                   (-> (edn/read-string (slurp coptsf))
-                       (dissoc-entry-point-opts)))
-          opts   (merge copts
-                   (build/add-implicit-options
-                     (merge (repl/repl-options renv) opts)))]
-      (binding [ana/*cljs-ns*    'cljs.user
-                repl/*repl-opts* opts
-                ana/*verbose*    (:verbose opts)
-                repl/*repl-env*  renv]
-        (when ana/*verbose*
-          (util/debug-prn "Compiler options:" (pr-str repl/*repl-opts*)))
-        (comp/with-core-cljs repl/*repl-opts*
-          (fn []
-            (try
-              (repl/setup renv repl/*repl-opts*)
-              ;; REPLs don't normally load cljs_deps.js
-              (when (and coptsf (.exists coptsf))
-                (let [depsf (io/file (:output-dir opts) "cljs_deps.js")]
-                  (when (.exists depsf)
-                    (repl/evaluate renv "cljs_deps.js" 1 (slurp depsf)))))
-              (repl/evaluate-form renv (ana-api/empty-env) ""
-                (when-not (empty? args)
-                  `(set! *command-line-args* (list ~@args))))
-              (repl/evaluate-form renv (ana-api/empty-env) ""
-                `(~'ns ~'cljs.user))
-              (repl/run-inits renv inits)
-              (when script
-                (cond
-                  (= "-" script)
-                  (repl/load-stream renv "" *in*)
-
-                  (.exists (io/file script))
-                  (with-open [stream (io/reader script)]
-                    (repl/load-stream renv script stream))
-
-                  (string/starts-with? script "@/")
-                  (if-let [rsrc (io/resource (subs script 2))]
-                    (repl/load-stream renv (util/get-name rsrc) rsrc)
-                    (missing-resource script))
-
-                  (string/starts-with? script "@")
-                  (if-let [rsrc (io/resource (subs script 1))]
-                    (repl/load-stream renv (util/get-name rsrc) rsrc)
-                    (missing-resource script))
-
-                  (string/starts-with? script "-")
-                  (throw
-                    (ex-info
-                      (str "Expected script or -, got flag " script " instead")
-                      {:cljs.main/error :invalid-arg}))
+  (let [opts   (cond-> options
+                 (not (:output-dir options))
+                 (assoc :output-dir (temp-out-dir) :temp-output-dir? true)
+                 (not (contains? options :aot-cache))
+                 (assoc :aot-cache true))
+        reopts (merge repl-env-options
+                 (select-keys opts [:output-to :output-dir]))
+        _      (when (or ana/*verbose* (:verbose opts))
+                 (util/debug-prn "REPL env options:" (pr-str reopts)))
+        renv   (apply repl-env (mapcat identity reopts))
+        coptsf (when-let [od (:output-dir opts)]
+                 (io/file od "cljsc_opts.edn"))
+        copts  (when (and coptsf (.exists coptsf))
+                 (-> (edn/read-string (slurp coptsf))
+                   (dissoc-entry-point-opts)))
+        opts   (merge copts
+                 (build/add-implicit-options
+                   (merge (repl/repl-options renv) opts)))]
+    (binding [env/*compiler*   (env/default-compiler-env opts)
+              ana/*cljs-ns*    'cljs.user
+              repl/*repl-opts* opts
+              ana/*verbose*    (:verbose opts)
+              repl/*repl-env*  renv]
+      (when ana/*verbose*
+        (util/debug-prn "Compiler options:" (pr-str repl/*repl-opts*)))
+      (comp/with-core-cljs repl/*repl-opts*
+        (fn []
+          (try
+            (repl/setup renv repl/*repl-opts*)
+            ;; REPLs don't normally load cljs_deps.js
+            (when (and coptsf (.exists coptsf))
+              (let [depsf (io/file (:output-dir opts) "cljs_deps.js")]
+                (when (.exists depsf)
+                  (repl/evaluate renv "cljs_deps.js" 1 (slurp depsf)))))
+            (repl/evaluate-form renv (ana-api/empty-env) ""
+              (when-not (empty? args)
+                `(set! *command-line-args* (list ~@args))))
+            (repl/evaluate-form renv (ana-api/empty-env) ""
+              `(~'ns ~'cljs.user))
+            (repl/run-inits renv inits)
+            (when script
+              (cond
+                (= "-" script)
+                (repl/load-stream renv "" *in*)
+
+                (.exists (io/file script))
+                (with-open [stream (io/reader script)]
+                  (repl/load-stream renv script stream))
+
+                (string/starts-with? script "@/")
+                (if-let [rsrc (io/resource (subs script 2))]
+                  (repl/load-stream renv (util/get-name rsrc) rsrc)
+                  (missing-resource script))
+
+                (string/starts-with? script "@")
+                (if-let [rsrc (io/resource (subs script 1))]
+                  (repl/load-stream renv (util/get-name rsrc) rsrc)
+                  (missing-resource script))
+
+                (string/starts-with? script "-")
+                (throw
+                  (ex-info
+                    (str "Expected script or -, got flag " script " instead")
+                    {:cljs.main/error :invalid-arg}))
 
-                  :else
+                :else
+                (throw
+                  (ex-info
+                    (str "Script " script " does not exist")
+                    {:cljs.main/error :invalid-arg}))))
+            (when main
+              (let [src (build/ns->source main)]
+                (when-not src
                   (throw
                     (ex-info
-                      (str "Script " script " does not exist")
-                      {:cljs.main/error :invalid-arg}))))
-              (when main
-                (let [src (build/ns->source main)]
-                  (when-not src
-                    (throw
-                      (ex-info
-                        (str "Namespace " main " does not exist."
-                             (when (string/includes? main "-")
-                               " Please check that namespaces with dashes use underscores in the ClojureScript file name."))
-                        {:cljs.main/error :invalid-arg})))
-                  (repl/load-stream renv (util/get-name src) src)
-                  (repl/evaluate-form renv (ana-api/empty-env) ""
-                    `(~(symbol (name main) "-main") ~@args))))
-              (finally
-                (repl/tear-down renv)))))))))
+                      (str "Namespace " main " does not exist."
+                        (when (string/includes? main "-")
+                          " Please check that namespaces with dashes use underscores in the ClojureScript file name."))
+                      {:cljs.main/error :invalid-arg})))
+                (repl/load-stream renv (util/get-name src) src)
+                (repl/evaluate-form renv (ana-api/empty-env) ""
+                  `(~(symbol (name main) "-main") ~@args))))
+            (finally
+              (repl/tear-down renv))))))))
 
 (defn- main-opt
   "Call the -main function from a namespace with string arguments from
diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj
index afd4a6e9a..fb3dc3375 100644
--- a/src/test/cljs_cli/cljs_cli/test.clj
+++ b/src/test/cljs_cli/cljs_cli/test.clj
@@ -97,3 +97,12 @@
       (output-is
         nil
         "{:ns cljs.user, :value 3}"))))
+
+(deftest test-cljs-2724
+  (with-repl-env-filter #{"node"}
+    (-> (cljs-main
+          "-e" "(require 'fs)"
+          "-e" "fs/R_OK")
+      (output-is
+        nil
+        4))))

From 0ef73e0d02bdff3d3e6c46c2eaf5cfff9cf155ba Mon Sep 17 00:00:00 2001
From: Erik Assum 
Date: Thu, 26 Apr 2018 16:17:03 +0200
Subject: [PATCH 3153/4033] CLJS-2743 Fix docstring misspelling

---
 src/main/cljs/cljs/core.cljs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index b2719b50e..6cb85eadd 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -2428,7 +2428,7 @@ reduces them without incurring seq initialization"
 (defn sort-by
   "Returns a sorted sequence of the items in coll, where the sort
    order is determined by comparing (keyfn item).  Comp can be
-   boolean-valued comparison funcion, or a -/0/+ valued comparator.
+   boolean-valued comparison function, or a -/0/+ valued comparator.
    Comp defaults to compare."
   ([keyfn coll]
    (sort-by keyfn compare coll))

From 1d784517cc80201826219e7954233315d659f567 Mon Sep 17 00:00:00 2001
From: Erik Assum 
Date: Thu, 22 Mar 2018 21:02:36 +0100
Subject: [PATCH 3154/4033] CLJS-2618 Fix docstring for `remove-tap`

---
 src/main/cljs/cljs/core.cljs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 6cb85eadd..4c1b88f2a 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -11349,7 +11349,7 @@ reduces them without incurring seq initialization"
   nil)
 
 (defn remove-tap
-  "Remove f from the tap set the tap set."
+  "Remove f from the tap set."
   [f]
   (maybe-init-tapset)
   (swap! tapset disj f)

From 8e723a6f9a615f374349a5fac57aad983ab2b2f9 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Wed, 24 Jan 2018 10:36:47 -0500
Subject: [PATCH 3155/4033] CLJS-2480: Periods at end of analyzer warnings

---
 src/main/clojure/cljs/analyzer.cljc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 9b401de9b..6d1e13095 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -345,7 +345,7 @@
 
 (defmethod error-message :fn-deprecated
   [warning-type info]
-  (str (-> info :fexpr :info :name) " is deprecated."))
+  (str (-> info :fexpr :info :name) " is deprecated"))
 
 (defmethod error-message :undeclared-ns-form
   [warning-type info]
@@ -412,7 +412,7 @@
 
 (defmethod error-message :invalid-arithmetic
   [warning-type info]
-  (str (:js-op info) ", all arguments must be numbers, got " (:types info) " instead."))
+  (str (:js-op info) ", all arguments must be numbers, got " (:types info) " instead"))
 
 (defmethod error-message :invalid-array-access
   [warning-type {:keys [name types]}]

From 0cdbb23abeeabb36c1ce0a6010890aa97da0fad1 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 10 May 2018 09:46:06 -0400
Subject: [PATCH 3156/4033] CLJS-2751: script/bootstrap --closure-library-head
 misses goog/text

---
 script/bootstrap | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/script/bootstrap b/script/bootstrap
index 143c6bfb5..c838389bf 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -103,9 +103,19 @@ else
 fi
 
 if [ "$1" = "--closure-library-head" ] ; then
-    echo "Building lib/goog.jar..."
-    echo "jar cf ./lib/goog.jar -C closure/library/closure-library/closure/ goog"
-    jar cf ./lib/goog.jar -C closure/library/closure-library/closure/ goog
+    echo "Building lib/google-closure-library-HEAD.jar..."
+    # Set up the third-party deps paths to match what we would get when depending on a release
+    sed -e 's/..\/..\/third_party\/closure\/goog\///' closure/library/closure-library/closure/goog/deps.js > revised-deps.js
+    cp closure/library/closure-library/closure/goog/deps.js orig-deps.js
+    mv revised-deps.js closure/library/closure-library/closure/goog/deps.js
+    jar cf ./lib/google-closure-library-HEAD.jar -C closure/library/closure-library/closure/ goog
+    mv orig-deps.js closure/library/closure-library/closure/goog/deps.js
+    echo "Building lib/google-closure-library-third-party-HEAD.jar..."
+    mv closure/library/closure-library/third_party/closure/goog/deps.js orig-deps.js
+    mv closure/library/closure-library/third_party/closure/goog/base.js orig-base.js
+    jar cf ./lib/google-closure-library-third-party-HEAD.jar -C closure/library/closure-library/third_party/closure/ goog 
+    mv orig-base.js closure/library/closure-library/third_party/closure/goog/base.js
+    mv orig-deps.js closure/library/closure-library/third_party/closure/goog/deps.js
 fi
 
 echo "Fetching Rhino..."

From fb312cde295c7738a6dce63b0a4b3f02f095553f Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 18 May 2018 14:01:22 -0400
Subject: [PATCH 3157/4033] CLJS-2733: Throw error message if too few or too
 many args to throw

same as CLJ-1456
---
 src/main/clojure/cljs/analyzer.cljc | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 6d1e13095..875f11b77 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1394,8 +1394,15 @@
      :children (vec (concat [v] tests thens (if default [default])))}))
 
 (defmethod parse 'throw
-  [op env [_ throw :as form] name _]
-  (let [throw-expr (disallowing-recur (analyze (assoc env :context :expr) throw))]
+  [op env [_ throw-form :as form] name _]
+  (cond
+    (= 1 (count form))
+    (throw
+      (error env "Too few arguments to throw, throw expects a single Error instance"))
+    (< 2 (count form))
+    (throw
+      (error env "Too many arguments to throw, throw expects a single Error instance")))
+  (let [throw-expr (disallowing-recur (analyze (assoc env :context :expr) throw-form))]
     {:env env :op :throw :form form
      :throw throw-expr
      :children [throw-expr]}))

From adeaa9be63b7911d4ac0c7765c2ca8fd2aa4d507 Mon Sep 17 00:00:00 2001
From: r0man 
Date: Thu, 22 Mar 2018 19:33:09 +0100
Subject: [PATCH 3158/4033] Recompile cljs.loader in REPL

The cljs.loader namespace has to be compiled last, because all inputs
need to be known to populate the `MODULE_INFOS` and `MODULE_URIS`
vars.

Loading a file or evaluating a form in the REPL could trigger another
compilation of the cljs.loader namespace, without the information for
the `MODULE_INFOS` and `MODULE_URIS` available. This compilation
erased the previously populated `MODULE_INFOS` and `MODULE_URIS` vars
and corrupted the module loader.

This patch modifies the loader compilation pass to swap and merge the
`MODULE_INFOS` and `MODULE_URIS` into the compiler state, to make this
information available to compilation stages happening later.

The patch also changes the REPL to recompile and load the cljs.loader
namespace when a file gets loaded, or a namespace form gets
evaluated. A new module graph will be calculated and merged into the
previously calculated one. Recompilation of the cljs.loader namespace
will only happen if the file or form require the cljs.loader
namespace.

Since the :aot-cache is populated at a point where the information for
the `MODULE_INFOS` and `MODULE_URIS` is not yet available, this patch
also makes sure the cljs.loader namespace is never cached.
---
 src/main/clojure/cljs/closure.clj   | 26 +++++++----
 src/main/clojure/cljs/compiler.cljc | 34 ++++++++-------
 src/main/clojure/cljs/repl.cljc     | 67 ++++++++++++++++++-----------
 3 files changed, 77 insertions(+), 50 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 8070a71d5..2fab104b8 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -634,6 +634,10 @@
                 (util/mkdirs target)
                 (spit target (slurp f))
                 (.setLastModified target (util/last-modified jar-file))))))))
+    ;; Files that don't require compilation (cljs.loader for example)
+    ;; need to be copied from JAR to disk.
+    (when-not (.exists out-file)
+      (jar-file-to-disk jar-file (util/output-directory opts) opts))
     ;; have to call compile-file as it includes more IJavaScript
     ;; information than ana/parse-ns for now
     (compile-file
@@ -1140,6 +1144,11 @@
       :depends-on #{:core}}})
   )
 
+(defn- const-expr-form
+  "Returns the :const-expr form for `sym` from `compiler-state`."
+  [compiler-state sym]
+  (get-in compiler-state [::ana/namespaces (symbol (namespace sym)) :defs (symbol (name sym)) :const-expr :form]))
+
 (defn compile-loader
   "Special compilation pass for cljs.loader namespace. cljs.loader must be
   compiled last after all inputs. This is because all inputs must be known and
@@ -1153,14 +1162,15 @@
                       first)]
     (let [module-uris  (module-graph/modules->module-uris modules inputs opts)
           module-infos (module-graph/modules->module-infos modules)]
-      (env/with-compiler-env
-        (ana/add-consts @env/*compiler*
-          {'cljs.core/MODULE_URIS  module-uris
-           'cljs.core/MODULE_INFOS module-infos})
-        (-compile (:source-file loader)
-          (merge opts
-            {:cache-key   (util/content-sha (pr-str module-uris))
-             :output-file (comp/rename-to-js (util/ns->relpath (:ns loader)))})))))
+      (swap! env/*compiler* ana/add-consts
+             {'cljs.core/MODULE_INFOS
+              (merge (const-expr-form @env/*compiler* 'cljs.core/MODULE_INFOS) module-infos)
+              'cljs.core/MODULE_URIS
+              (merge (const-expr-form @env/*compiler* 'cljs.core/MODULE_URIS) module-uris)})
+      (-compile (:source-file loader)
+        (merge opts
+          {:cache-key   (util/content-sha (pr-str module-uris))
+           :output-file (comp/rename-to-js (util/ns->relpath (:ns loader)))}))))
   inputs)
 
 (defn build-modules
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index a3981ac84..45bcb5775 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1480,22 +1480,24 @@
           (:options @env/*compiler*))))
      ([^File src ^File dest opts]
       (let [{:keys [ns requires]} (ana/parse-ns src)]
-        (ensure
-         (or (not (.exists dest))
-             (util/changed? src dest)
-             (let [version' (util/compiled-by-version dest)
-                   version (util/clojurescript-version)]
-               (and version (not= version version')))
-             (and opts
-                  (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns)))
-                  (not= (ana/build-affecting-options opts)
-                        (ana/build-affecting-options (util/build-options dest))))
-             (and opts (:source-map opts)
-                  (if (= (:optimizations opts) :none)
-                    (not (.exists (io/file (str (.getPath dest) ".map"))))
-                    (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)]))))
-             (when-let [recompiled' (and *recompiled* @*recompiled*)]
-               (some requires recompiled'))))))))
+        (if (and (= 'cljs.loader ns) (not (contains? opts :cache-key)))
+          false
+          (ensure
+           (or (not (.exists dest))
+               (util/changed? src dest)
+               (let [version' (util/compiled-by-version dest)
+                     version (util/clojurescript-version)]
+                 (and version (not= version version')))
+               (and opts
+                    (not (and (io/resource "cljs/core.aot.js") (= 'cljs.core ns)))
+                    (not= (ana/build-affecting-options opts)
+                          (ana/build-affecting-options (util/build-options dest))))
+               (and opts (:source-map opts)
+                    (if (= (:optimizations opts) :none)
+                      (not (.exists (io/file (str (.getPath dest) ".map"))))
+                      (not (get-in @env/*compiler* [::compiled-cljs (.getAbsolutePath dest)]))))
+               (when-let [recompiled' (and *recompiled* @*recompiled*)]
+                 (some requires recompiled')))))))))
 
 #?(:clj
    (defn compile-file
diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 94c7799bb..9b7770a1c 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -206,10 +206,34 @@
 (defn compilable? [input]
   (contains? input :source-file))
 
+(defn- load-sources
+  "Load the compiled `sources` into the REPL."
+  [repl-env sources opts]
+  (if (:output-dir opts)
+    ;; REPLs that read from :output-dir just need to add deps,
+    ;; environment will handle actual loading - David
+    (let [sb (StringBuffer.)]
+      (doseq [source sources]
+        (with-open [rdr (io/reader (:url source))]
+          (.append sb (cljsc/add-dep-string opts source))))
+      (when (:repl-verbose opts)
+        (println (.toString sb)))
+      (-evaluate repl-env "" 1 (.toString sb)))
+    ;; REPLs that stream must manually load each dep - David
+    (doseq [{:keys [url provides]} sources]
+      (-load repl-env provides url))))
+
+(defn- load-cljs-loader
+  "Compile and load the cljs.loader namespace if it's present in `sources`."
+  [repl-env sources opts]
+  (when-let [source (first (filter #(= (:ns %) 'cljs.loader) sources))]
+    (cljsc/compile-loader sources opts)
+    (load-sources repl-env [source] opts)))
+
 (defn load-namespace
   "Load a namespace and all of its dependencies into the evaluation environment.
-  The environment is responsible for ensuring that each namespace is loaded once and
-  only once."
+  The environment is responsible for ensuring that each namespace is
+  loaded once and only once. Returns the compiled sources."
   ([repl-env ns] (load-namespace repl-env ns nil))
   ([repl-env ns opts]
    (let [ns      (if (and (seq? ns) (= (first ns) 'quote)) (second ns) ns)
@@ -221,29 +245,18 @@
                                 (merge (env->opts repl-env) opts))
                            (remove (comp #{["goog"]} :provides)))
                          (map #(cljsc/source-on-disk opts %)
-                           (cljsc/add-js-sources [input] opts))))))]
+                              (cljsc/add-js-sources [input] opts))))))]
      (when (:repl-verbose opts)
        (println (str "load-namespace " ns " , compiled:") (map :provides sources)))
-     (if (:output-dir opts)
-       ;; REPLs that read from :output-dir just need to add deps,
-       ;; environment will handle actual loading - David
-       (let [sb (StringBuffer.)]
-         (doseq [source sources]
-           (with-open [rdr (io/reader (:url source))]
-             (.append sb
-               (cljsc/add-dep-string opts source))))
-         (when (:repl-verbose opts)
-           (println (.toString sb)))
-         (-evaluate repl-env "" 1 (.toString sb)))
-       ;; REPLs that stream must manually load each dep - David
-       (doseq [{:keys [url provides]} sources]
-         (-load repl-env provides url))))))
+     (load-sources repl-env sources opts)
+     sources)))
 
 (defn- load-dependencies
-  ([repl-env requires] (load-dependencies repl-env requires nil))
+  "Compile and load the given `requires` and return the compiled sources."
+  ([repl-env requires]
+   (load-dependencies repl-env requires nil))
   ([repl-env requires opts]
-   (doseq [ns (distinct requires)]
-     (load-namespace repl-env ns opts))))
+   (doall (mapcat #(load-namespace repl-env % opts) (distinct requires)))))
 
 (defn ^File js-src->cljs-src
   "Map a JavaScript output file back to the original ClojureScript source
@@ -544,11 +557,12 @@
                      (ana/no-warn (ana/analyze env form nil opts))
                      (catch #?(:clj Exception :cljs js/Error) e
                          (reset! env/*compiler* backup-comp)
-                       (throw e)))]
-           (load-dependencies repl-env
-             (into (vals (:requires ast))
-               (distinct (vals (:uses ast))))
-             opts)))
+                         (throw e)))
+               sources (load-dependencies repl-env
+                         (into (vals (:requires ast))
+                               (distinct (vals (:uses ast))))
+                         opts)]
+           (load-cljs-loader repl-env sources opts)))
        (when *cljs-verbose*
          (err-out (println wrap-js)))
        (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)]
@@ -596,7 +610,8 @@
               (util/ns->relpath ns (util/ext (:source-url compiled))))
             (slurp src)))
         ;; need to load dependencies first
-        (load-dependencies repl-env (:requires compiled) opts)
+        (let [sources (load-dependencies repl-env (:requires compiled) opts)]
+          (load-cljs-loader repl-env (conj sources compiled) opts))
         (-evaluate repl-env f 1 (cljsc/add-dep-string opts compiled))
         (-evaluate repl-env f 1
           (cljsc/src-file->goog-require src

From e2310802e212cb0e33fead6dbb40bc1972f82012 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Mon, 12 Mar 2018 10:44:53 -0400
Subject: [PATCH 3159/4033] CLJS-2651: Shared AOT cache: Support git deps

---
 src/main/clojure/cljs/closure.clj | 75 ++++++++++++++++++++++++-------
 1 file changed, 60 insertions(+), 15 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 2fab104b8..f976f7bdc 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -46,6 +46,26 @@
            [com.sun.nio.file SensitivityWatchEventModifier]
            [com.google.common.base Throwables]))
 
+;; Copied from clojure.tools.gitlibs
+
+(def ^:private GITLIBS-CACHE-DIR
+  (delay
+    (.getCanonicalPath
+      (let [env (System/getenv "GITLIBS")]
+        (if (string/blank? env)
+          (io/file (System/getProperty "user.home") ".gitlibs")
+          (io/file env))))))
+
+(defn- gitlibs-cache-dir
+  "Returns the gitlibs cache dir, a string."
+  []
+  @GITLIBS-CACHE-DIR)
+
+(defn- gitlibs-src?
+  "Returns true if the file comes from the gitlibs cache."
+  [file]
+  (string/starts-with? (util/path file) (gitlibs-cache-dir)))
+
 (def name-chars (map char (concat (range 48 57) (range 65 90) (range 97 122))))
 
 (defn random-char []
@@ -556,6 +576,29 @@
   [compilable opts]
   (-compile compilable opts))
 
+(def ^:private USER-HOME-WRITABLE
+  (delay (.canWrite (io/file (System/getProperty "user.home")))))
+
+(defn- aot-cache? [opts]
+  "Returns true if compilation artifacts shuold be placed in the
+  shared AOT cache."
+  (and (:aot-cache opts)
+       @USER-HOME-WRITABLE))
+
+(defn- copy-from-cache
+  [cache-path cacheable source-file opts]
+  (doseq [[k ^File f] cacheable]
+    (when (.exists f)
+      (let [target (io/file (util/output-directory opts)
+                     (-> (.getAbsolutePath f)
+                       (string/replace (.getAbsolutePath cache-path) "")
+                       (subs 1)))]
+        (when (and (or ana/*verbose* (:verbose opts)) (= :output-file k))
+          (util/debug-prn (str "Copying cached " f " to " target)))
+        (util/mkdirs target)
+        (spit target (slurp f))
+        (.setLastModified target (util/last-modified source-file))))))
+
 (defn find-sources
   "Given a Compilable, find sources and return a sequence of IJavaScript."
   [compilable opts]
@@ -569,8 +612,21 @@
   IJavaScript."
   [^File file {:keys [output-file] :as opts}]
     (if output-file
-      (let [out-file (.toString (io/file (util/output-directory opts) output-file))]
-        (compiled-file (comp/compile-file file out-file opts)))
+      (let [out-file (io/file (util/output-directory opts) output-file)]
+        (if (and (aot-cache? opts)
+                 (gitlibs-src? file))
+          (let [cacheable  (ana/cacheable-files file (util/ext file) opts)
+                cache-path (ana/cache-base-path (util/path file) opts)]
+            (if (not (.exists (:output-file cacheable)))
+              (let [ret (compiled-file (comp/compile-file file (:output-file cacheable)
+                                         (assoc opts :output-dir (util/path cache-path))))]
+                (copy-from-cache cache-path cacheable file opts)
+                ret)
+              (do
+                (when-not (.exists out-file)
+                  (copy-from-cache cache-path cacheable file opts))
+                (compiled-file (comp/compile-file file (.toString out-file) opts)))))
+          (compiled-file (comp/compile-file file (.toString out-file) opts))))
       (let [path (.getPath ^File file)]
         (binding [ana/*cljs-file* path]
           (with-open [rdr (io/reader file)]
@@ -616,24 +672,13 @@
     (when (or (nil? out-file)
               (comp/requires-compilation? jar-file out-file opts))
       ;; actually compile from JAR
-      (if (or (not (:aot-cache opts))
-              (not (.canWrite (io/file (System/getProperty "user.home")))))
+      (if (not (aot-cache? opts))
         (-compile (jar-file-to-disk jar-file (util/output-directory opts) opts) opts)
         (let [cache-path (ana/cache-base-path (util/path jar-file) opts)]
           (when (comp/requires-compilation? jar-file (:output-file cacheable) opts)
             (-compile (jar-file-to-disk jar-file cache-path opts)
               (assoc opts :output-dir (util/path cache-path))))
-          (doseq [[k ^File f] cacheable]
-            (when (.exists f)
-              (let [target (io/file (util/output-directory opts)
-                             (-> (.getAbsolutePath f)
-                               (string/replace (.getAbsolutePath cache-path) "")
-                               (subs 1)))]
-                (when (and (or ana/*verbose* (:verbose opts)) (= :output-file k))
-                  (util/debug-prn (str "Copying cached " f " to " target)))
-                (util/mkdirs target)
-                (spit target (slurp f))
-                (.setLastModified target (util/last-modified jar-file))))))))
+          (copy-from-cache cache-path cacheable jar-file opts))))
     ;; Files that don't require compilation (cljs.loader for example)
     ;; need to be copied from JAR to disk.
     (when-not (.exists out-file)

From 92c9d60c15c6f9aeef61de0b2c0ab8f117e6341d Mon Sep 17 00:00:00 2001
From: Petter Eriksson 
Date: Wed, 21 Mar 2018 16:19:07 +0100
Subject: [PATCH 3160/4033] CLJS-2681: Accepting multiple paths to the --watch
 option for cljs.main

---
 src/main/clojure/cljs/cli.clj     | 52 +++++++++++++++++++++----------
 src/main/clojure/cljs/closure.clj | 16 ++++++++++
 2 files changed, 52 insertions(+), 16 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 7ebc8a8b4..25677b1d4 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -145,20 +145,36 @@ classpath. Classpath-relative paths have prefix of @ or @/")
   [cfg value]
   (assoc-in cfg [:options :verbose] (= value "true")))
 
+(defn- validate-watch-paths [[path :as paths]]
+  (when (or (nil? path)
+            (and (not (.exists (io/file path)))
+                 (or (string/blank? path)
+                     (string/starts-with? path "-"))))
+    (throw
+      (ex-info
+        (str "Missing watch path(s)")
+        {:cljs.main/error :invalid-arg})))
+  (when-let [non-existent (seq (remove #(.exists (io/file %)) paths))]
+    (throw
+      (ex-info
+        (if (== 1 (count non-existent))
+          (str "Watch path "
+               (first non-existent)
+               " does not exist")
+          (str "Watch paths "
+               (string/join ", " (butlast non-existent))
+               " and "
+               (last non-existent)
+               " does not exist"))
+        {:cljs.main/error :invalid-arg}))))
+
 (defn- watch-opt
-  [cfg path]
-  (when-not (.exists (io/file path))
-    (if (or (string/starts-with? path "-")
-            (string/blank? path))
-      (throw
-        (ex-info
-          (str "Missing watch path")
-          {:cljs.main/error :invalid-arg}))
-      (throw
-        (ex-info
-          (str "Watch path " path " does not exist")
-          {:cljs.main/error :invalid-arg}))))
-  (assoc-in cfg [:options :watch] path))
+  [cfg paths]
+  (let [paths (util/split-paths paths)]
+    (validate-watch-paths paths)
+    (assoc-in cfg [:options :watch] (cond-> paths
+                                            (== 1 (count paths))
+                                            first))))
 
 (defn- optimize-opt
   [cfg level]
@@ -454,7 +470,9 @@ present"
                      (not (:output-dir opts))
                      (assoc :output-dir "out")
                      (not (contains? opts :aot-cache))
-                     (assoc :aot-cache true)))
+                     (assoc :aot-cache true)
+                     (sequential? (:watch opts))
+                     (update :watch cljs.closure/compilable-input-paths)))
         convey   (into [:output-dir] repl/known-repl-opts)
         cfg      (update cfg :options merge (select-keys opts convey))
         source   (when (and (= :none (:optimizations opts :none)) main-ns)
@@ -533,8 +551,10 @@ present"
                                        "will be used to set ClojureScript compiler "
                                        "options") }
       ["-w" "--watch"]         {:group ::compile :fn watch-opt
-                                :arg "path"
-                                :doc "Continuously build, only effective with the --compile main option"}
+                                :arg "paths"
+                                :doc (str "Continuously build, only effective with the "
+                                          "--compile main option. Specifies a system-dependent "
+                                          "path-separated list of directories to watch.")}
       ["-o" "--output-to"]     {:group ::compile :fn output-to-opt
                                 :arg "file"
                                 :doc "Set the output compiled file"}
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index f976f7bdc..6b075013e 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -542,6 +542,22 @@
   (-compile [this opts] "Returns one or more IJavaScripts.")
   (-find-sources [this opts] "Returns one or more IJavascripts, without compiling them."))
 
+(defn compilable-input-paths
+  "Takes a coll of inputs as strings or files and returns a
+  single Inputs and Compilable object."
+  [paths]
+  (reify
+    cljs.closure/Inputs
+    (-paths [_]
+      (mapcat cljs.closure/-paths paths))
+    cljs.closure/Compilable
+    (-compile [_ opts]
+      (mapcat #(cljs.closure/-compile % opts)
+              paths))
+    (-find-sources [_ opts]
+      (mapcat #(cljs.closure/-find-sources % opts)
+              paths))))
+
 (defn compile-form-seq
   "Compile a sequence of forms to a JavaScript source string."
   ([forms]

From 2e15a5cc0d0e32265f86369c3ade17a445dee6cd Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Fri, 18 May 2018 16:52:00 -0400
Subject: [PATCH 3161/4033] CLJS-2688 cljs.main: Accumulate all meaningful
 repeated inits modules using global-exports

---
 src/main/clojure/cljs/cli.clj | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 25677b1d4..59402cdb8 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -172,9 +172,7 @@ classpath. Classpath-relative paths have prefix of @ or @/")
   [cfg paths]
   (let [paths (util/split-paths paths)]
     (validate-watch-paths paths)
-    (assoc-in cfg [:options :watch] (cond-> paths
-                                            (== 1 (count paths))
-                                            first))))
+    (update-in cfg [:options :watch] (fnil into []) paths)))
 
 (defn- optimize-opt
   [cfg level]

From 27c27e0b53f6ef550670d7aed981395a96aae1da Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sun, 20 May 2018 09:51:37 -0400
Subject: [PATCH 3162/4033] CLJS-1677: Requiring [goog] breaks an :advanced
 build, but the compiler exits successfully

This one is a little tricky:

Since goog is added as an implicit dependency at the end of the
compilation pipeline, there is no collision to detect when parsing an
ns form that explicitly requires goog. You could handle this situation
by filtering goog when parsing the ns form, but this leads to a
challenging corner case where goog is required and also aliased: In
that case you want to preserve goog and its alias so that the alias
machinery works properly. But this means that goog will be duplicated
later when add-goog-base is called.

This patch takes the alternative approach of patching things up at the
end: It filters out any existing goog dep before consing it onto the
list of deps, placing a remove-goog-base prior to add-goog-base in the
pipeline.

This fixes the duplicate problem, but oddly results in

  JSC_MISSING_PROVIDE_ERROR. required "goog" namespace never provided

if goog.require('goog') is emitted into the JavaScript. This final
aspect is patched up to ensure that this is not emitted.

A unit test is added covering the alias case.
---
 src/main/clojure/cljs/closure.clj   | 5 +++++
 src/main/clojure/cljs/compiler.cljc | 3 ++-
 src/test/cljs/cljs/ns_test.cljs     | 8 +++++++-
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 6b075013e..2fc6735ce 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -1080,6 +1080,10 @@
                ; - ns-info -> ns -> cljs file relpath -> js relpath
                (merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))})))))))))
 
+(defn remove-goog-base
+  [inputs]
+  (remove #(= (:provides %) ["goog"]) inputs))
+
 (defn add-goog-base
   [inputs]
   (cons (javascript-file nil (io/resource "goog/base.js") ["goog"] nil)
@@ -2904,6 +2908,7 @@
                                 (cond-> (= :nodejs (:target opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") opts)]))
                                 deps/dependency-order
                                 (add-preloads opts)
+                                remove-goog-base
                                 add-goog-base
                                 (cond-> (= :nodejs (:target opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") opts)]))
                                 (->> (map #(source-on-disk opts %)) doall)
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 45bcb5775..25492bfff 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1169,7 +1169,8 @@
         (emitln "goog.require('" (munge lib) "', 'reload-all');")
 
         :else
-        (emitln "goog.require('" (munge lib) "');")))
+        (when-not (= lib 'goog)
+          (emitln "goog.require('" (munge lib) "');"))))
     (doseq [lib node-libs]
       (emitln (munge ns-name) "."
         (ana/munge-node-lib lib)
diff --git a/src/test/cljs/cljs/ns_test.cljs b/src/test/cljs/cljs/ns_test.cljs
index acecbd0fe..da7c0c8c7 100644
--- a/src/test/cljs/cljs/ns_test.cljs
+++ b/src/test/cljs/cljs/ns_test.cljs
@@ -11,7 +11,8 @@
   (:require-macros [clojure.core :as lang :refer [when when-let] :rename {when always
                                                                           when-let always-let}]
                    [cljs.test :refer [deftest is]])
-  (:require [cljs.test]
+  (:require [goog :as goog-alias]
+            [cljs.test]
             [cljs.ns-test.foo :refer [baz]]
             [clojure.set :as s :refer [intersection] :rename {intersection itsc}]
             [cljs.analyzer :as ana])
@@ -36,3 +37,8 @@
   (is (= (always true 42) 42))
   (is (= (core-mapv inc [1 2]) [2 3]))
   (is (= (always-let [foo 42] foo) 42)))
+
+(deftest test-cljs-1677
+  (is (.isNumber js/goog 3))
+  (is (goog/isNumber 3))
+  (is (goog-alias/isNumber 3)))

From 56ea8ee0de17cac909b09e2bdc1281d02e5404c9 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Mon, 21 May 2018 12:46:18 -0400
Subject: [PATCH 3163/4033] CLJS-2755: Can't generate uri instances

---
 src/main/cljs/cljs/spec/gen/alpha.cljs | 4 +++-
 src/test/cljs/cljs/spec/test_test.cljs | 7 +++++--
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/main/cljs/cljs/spec/gen/alpha.cljs b/src/main/cljs/cljs/spec/gen/alpha.cljs
index 27cedfce7..6ff47edef 100644
--- a/src/main/cljs/cljs/spec/gen/alpha.cljs
+++ b/src/main/cljs/cljs/spec/gen/alpha.cljs
@@ -11,7 +11,8 @@
                             char double int keyword symbol string uuid delay])
   (:require-macros [cljs.core :as c]
                    [cljs.spec.gen.alpha :as gen :refer [dynaload lazy-combinators lazy-prims]])
-  (:require [cljs.core :as c]))
+  (:require [cljs.core :as c])
+  (:import (goog Uri)))
 
 (deftype LazyVar [f ^:mutable cached]
   IDeref
@@ -107,6 +108,7 @@ gen-builtins
        simple-symbol? (symbol)
        qualified-symbol? (such-that qualified? (symbol-ns))
        uuid? (uuid)
+       uri? (fmap #(Uri. (str "http://" % ".com")) (uuid))
        inst? (fmap #(js/Date. %)
                     (large-integer))
        seqable? (one-of [(return nil)
diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs
index 003eefa2b..af5124746 100644
--- a/src/test/cljs/cljs/spec/test_test.cljs
+++ b/src/test/cljs/cljs/spec/test_test.cljs
@@ -6,8 +6,8 @@
 
 (s/fdef clojure.core/symbol
   :args (s/alt :separate (s/cat :ns string? :n string?)
-          :str string?
-          :sym symbol?)
+               :str string?
+               :sym symbol?)
   :ret symbol?)
 
 (defn h-cljs-1812 [x] true)
@@ -82,3 +82,6 @@
   (is (= [1 2 3] (foo 1 2 3)))
   (is (thrown? js/Error (foo 1 :hello)))
   (stest/unstrument `foo))
+
+(deftest test-2755
+  (is (uri? (ffirst (s/exercise uri? 1)))))

From 2ad14709f390e4427b30a8ec86eeb05872e5e52c Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 17 May 2018 17:35:09 -0400
Subject: [PATCH 3164/4033] CLJS-1871: A declare with :arglists should generate
 static function calls

---
 src/main/cljs/cljs/js.cljs               | 11 +++++++----
 src/main/cljs/cljs/pprint.cljs           | 20 ++++++++++----------
 src/main/cljs/cljs/spec/alpha.cljs       | 10 +++++-----
 src/main/cljs/clojure/data.cljs          |  2 +-
 src/main/clojure/cljs/analyzer.cljc      | 20 +++++++++++++++++++-
 src/test/cljs/cljs/invoke_test.cljs      |  4 ++++
 src/test/clojure/cljs/analyzer_tests.clj | 10 ++++++++++
 src/test/clojure/cljs/compiler_tests.clj |  4 +++-
 8 files changed, 59 insertions(+), 22 deletions(-)

diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs
index 010116f87..519bf36e1 100644
--- a/src/main/cljs/cljs/js.cljs
+++ b/src/main/cljs/cljs/js.cljs
@@ -194,7 +194,7 @@
 ;; -----------------------------------------------------------------------------
 ;; Analyze
 
-(declare eval-str*)
+(declare ^{:arglists '([bound-vars source name opts cb])} eval-str*)
 
 (def *loaded* (atom #{}))
 
@@ -217,7 +217,10 @@
           (run-async! proc (rest coll) break? cb))))
     (cb nil)))
 
-(declare require)
+(declare ^{:arglists '([name cb]
+                       [name opts cb]
+                       [bound-vars name opts cb]
+                       [bound-vars name reload opts cb])} require)
 
 (defn- process-deps
   [bound-vars names opts cb]
@@ -426,7 +429,7 @@
                      (cb res))))))
            (cb {:value nil})))))))
 
-(declare analyze-str*)
+(declare ^{:arglists '([bound-vars source name opts cb])} analyze-str*)
 
 (defn- analyze-deps
   ([bound-vars ana-env lib deps cb]
@@ -786,7 +789,7 @@
 ;; -----------------------------------------------------------------------------
 ;; Eval
 
-(declare clear-fns!)
+(declare ^{:arglists '([])} clear-fns!)
 
 (defn- eval* [bound-vars form opts cb]
   (let [the-ns     (or (:ns opts) 'cljs.user)
diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs
index 1dfde81e2..ccd79524d 100644
--- a/src/main/cljs/cljs/pprint.cljs
+++ b/src/main/cljs/cljs/pprint.cljs
@@ -221,7 +221,7 @@ beginning of aseq"
 ;; Forward declarations
 ;;======================================================================
 
-(declare get-miser-width)
+(declare ^{:arglists '([this])} get-miser-width)
 
 ;;======================================================================
 ;; The data structures used by pretty-writer
@@ -687,7 +687,7 @@ radix specifier is in the form #XXr where XX is the decimal value of *print-base
 ;; Support for the write function
 ;;======================================================================
 
-(declare format-simple-number)
+(declare ^{:arglists '([n])} format-simple-number)
 
 ;; This map causes var metadata to be included in the compiled output, even
 ;; in advanced compilation. See CLJS-1853 - António Monteiro
@@ -887,9 +887,9 @@ THIS FUNCTION IS NOT YET IMPLEMENTED."
 ;;======================================================================
 
 ;; Forward references
-(declare compile-format)
-(declare execute-format)
-(declare init-navigator)
+(declare ^{:arglists '([format-str])} compile-format)
+(declare ^{:arglists '([stream format args] [format args])} execute-format)
+(declare ^{:arglists '([s])} init-navigator)
 ;; End forward references
 
 (defn cl-format
@@ -1026,7 +1026,7 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm"
 ;; Common handling code for ~A and ~S
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(declare opt-base-str)
+(declare ^{:arglists '([base val])} opt-base-str)
 
 (def ^{:private true}
   special-radix-markers {2 "#b" 8 "#o" 16 "#x"})
@@ -1832,8 +1832,8 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm"
 ;; TODO: make it possible to make these decisions at compile-time.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(declare format-logical-block)
-(declare justify-clauses)
+(declare ^{:arglists '([params navigator offsets])} format-logical-block)
+(declare ^{:arglists '([params navigator offsets])} justify-clauses)
 
 (defn- logical-block-or-justify [params navigator offsets]
   (if (:colon (:right-params params))
@@ -2572,7 +2572,7 @@ of parameters as well."
   (and (:separator (:bracket-info (:def this)))
        (:colon (:params this))))
 
-(declare collect-clauses)
+(declare ^{:arglists '([bracket-info offset remainder])} collect-clauses)
 
 (defn- process-bracket [this remainder]
   (let [[subex remainder] (collect-clauses (:bracket-info (:def this))
@@ -2918,7 +2918,7 @@ type-map {"core$future_call" "Future",
 ;;; Dispatch for the code table
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(declare pprint-simple-code-list)
+(declare ^{:arglists '([alis])} pprint-simple-code-list)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Format the namespace ("ns") macro. This is quite complicated because of all the
diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs
index adad18c7c..10b3f3b47 100644
--- a/src/main/cljs/cljs/spec/alpha.cljs
+++ b/src/main/cljs/cljs/spec/alpha.cljs
@@ -99,8 +99,8 @@
    (implements? IMeta spec)
    (-> (meta spec) ::name)))
 
-(declare spec-impl)
-(declare regex-spec-impl)
+(declare ^{:arglists '([form pred gfn cpred?] [form pred gfn cpred? unc])} spec-impl)
+(declare ^{:arglists '([re gfn])} regex-spec-impl)
 
 (defn- maybe-spec
   "spec-or-k must be a spec, regex or resolvable kw/sym, else returns nil."
@@ -264,7 +264,7 @@
   [spec x]
   (with-out-str (explain spec x)))
 
-(declare valid?)
+(declare ^{:arglists '([spec x] [spec x form])} valid?)
 
 (defn- gensub
   [spec overrides path rmap form]
@@ -1004,7 +1004,7 @@
                (empty? pret))
         nil))
 
-(declare preturn)
+(declare ^{:arglists '([p])} preturn)
 
 (defn- accept-nil? [p]
   (let [{:keys [::op ps p1 p2 forms] :as p} (reg-resolve! p)]
@@ -1019,7 +1019,7 @@
       ::pcat (every? accept-nil? ps)
       ::alt (c/some accept-nil? ps))))
 
-(declare add-ret)
+(declare ^{:arglists '([p r k])} add-ret)
 
 (defn- preturn [p]
   (let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms] :as p} (reg-resolve! p)]
diff --git a/src/main/cljs/clojure/data.cljs b/src/main/cljs/clojure/data.cljs
index 11a84af31..80a608cd9 100644
--- a/src/main/cljs/clojure/data.cljs
+++ b/src/main/cljs/clojure/data.cljs
@@ -12,7 +12,7 @@
   clojure.data
   (:require [clojure.set :as set]))
 
-(declare diff)
+(declare ^{:arglists '([a b])} diff)
 
 (defn- atom-diff
   "Internal helper for diff."
diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 875f11b77..ea2c6c6f0 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -131,6 +131,7 @@
    :fn-var true
    :fn-arity true
    :fn-deprecated true
+   :declared-arglists-mismatch true
    :protocol-deprecated true
    :undeclared-protocol-symbol true
    :invalid-protocol-symbol true
@@ -347,6 +348,12 @@
   [warning-type info]
   (str (-> info :fexpr :info :name) " is deprecated"))
 
+(defmethod error-message :declared-arglists-mismatch
+  [warning-type info]
+  (str (symbol (str (:ns-name info)) (str (:sym info)))
+    " declared arglists " (:declared info)
+    " mismatch defined arglists " (:defined info)))
+
 (defmethod error-message :undeclared-ns-form
   [warning-type info]
   (str "Invalid :refer, " (:type info) " " (:lib info) "/" (:sym info) " does not exist"))
@@ -1527,7 +1534,13 @@
                  (not (:declared sym-meta))
                  *file-defs*
                  (get @*file-defs* sym))
-        (warning :redef-in-file env {:sym sym :line (:line v)})))
+        (warning :redef-in-file env {:sym sym :line (:line v)}))
+      (when (and (:declared v)
+                 (:arglists v)
+                 (not= (:arglists v) (:arglists sym-meta)))
+        (warning :declared-arglists-mismatch env {:ns-name  ns-name :sym sym
+                                                  :declared (second (:arglists v))
+                                                  :defined  (second (:arglists sym-meta))})))
     (let [env (if (or (and (not= ns-name 'cljs.core)
                            (core-name? env sym))
                       (some? (get-in @env/*compiler* [::namespaces ns-name :uses sym])))
@@ -1617,6 +1630,11 @@
                      :method-params params
                      :arglists (:arglists sym-meta)
                      :arglists-meta (doall (map meta (:arglists sym-meta)))}))))
+            (when (and (:declared sym-meta)
+                       (:arglists sym-meta))
+              {:declared true
+               :fn-var true
+               :method-params (second (:arglists sym-meta))})
             (if (and fn-var? (some? tag))
               {:ret-tag tag}
               (when tag {:tag tag})))))
diff --git a/src/test/cljs/cljs/invoke_test.cljs b/src/test/cljs/cljs/invoke_test.cljs
index 47db42ced..c38d1b400 100644
--- a/src/test/cljs/cljs/invoke_test.cljs
+++ b/src/test/cljs/cljs/invoke_test.cljs
@@ -30,3 +30,7 @@
 (gstr/urlEncode "foo")
 
 (js/goog.string.urlDecode "bar")
+
+(declare ^{:arglists '([a b])} declared-fn)
+
+(declared-fn 1 2)
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 708ff673c..ed3d29660 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -712,6 +712,16 @@
                            z)))))
               :tag meta :prefix))))
 
+(deftest test-cljs-1871
+  (let [ws (atom [])]
+    (try
+      (a/with-warning-handlers [(collecting-warning-handler ws)]
+        (a/analyze (ana/empty-env)
+          '(do (declare ^{:arglists '([x y])} foo)
+               (defn foo [x]))))
+      (catch Exception _))
+    (is (string/includes? (first @ws) "declared arglists ([x y]) mismatch defined arglists ([x])"))))
+
 (deftest test-cljs-2023
   (let [form (with-meta 'js/goog.DEBUG {:tag 'boolean})]
     (is (= (-> (ana-api/analyze (a/empty-env) form) :tag) 'boolean))))
diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj
index 230a68c24..4e5386bbe 100644
--- a/src/test/clojure/cljs/compiler_tests.clj
+++ b/src/test/clojure/cljs/compiler_tests.clj
@@ -266,7 +266,9 @@
       (is (re-find #"(?m)^.*var fexpr.*=.*cljs.core.complement\(funexpr1\);$"
                    content))
       (is (re-find #"(?m)^.*var .*=.*inv_arg1.cljs.core.IFn._invoke.arity.0 \?.*$"
-                   content)))))
+                   content))
+      ;; CLJS-1871: A declare hinted with :arglists meta should result in static dispatch
+      (is (str/includes? content "cljs.invoke_test.declared_fn(")))))
 #_(test-vars [#'test-optimized-invoke-emit])
 
 ;; CLJS-1225

From ab00c86280e98d17e1e430947aa9998539397506 Mon Sep 17 00:00:00 2001
From: Erik Assum 
Date: Tue, 29 May 2018 23:07:24 +0200
Subject: [PATCH 3165/4033] CLJS-2760 Make browser repl web-severs mime-type
 case-insensitive

- introduce a function `path->mime-type` which calculates the mime-type for
  a given path
- use this function and simplify the send-static function
---
 src/main/clojure/cljs/repl/browser.clj | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 17cadf7e0..49a70d87e 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -20,7 +20,8 @@
             [cljs.repl.server :as server]
             [cljs.stacktrace :as st]
             [cljs.analyzer :as ana]
-            [cljs.build.api :as build])
+            [cljs.build.api :as build]
+            [clojure.string :as str])
   (:import [java.util.concurrent Executors ConcurrentHashMap]))
 
 (def ^:dynamic browser-state nil)
@@ -140,6 +141,15 @@
     ""
     ""))
 
+(defn- path->mime-type [ext->mime-type path default]
+  (let [lc-path (str/lower-case path)
+        last-dot (.lastIndexOf path ".")]
+    (if (pos? last-dot)
+      (-> lc-path
+          (subs last-dot)
+          (ext->mime-type default))
+      default)))
+
 (defn send-static
   [{path :path :as request} conn
    {:keys [static-dir output-to output-dir host port gzip?] :or {output-dir "out"} :as opts}]
@@ -165,12 +175,10 @@
               local-path)]
         (cond
           local-path
-          (if-let [ext (some #(if (.endsWith path %) %) (keys ext->mime-type))]
-            (let [mime-type (ext->mime-type ext "text/plain")
-                  encoding (mime-type->encoding mime-type "UTF-8")]
-              (server/send-and-close conn 200 (slurp local-path :encoding encoding)
-                mime-type encoding (and gzip? (= "text/javascript" mime-type))))
-            (server/send-and-close conn 200 (slurp local-path) "text/plain"))
+          (let [mime-type (path->mime-type ext->mime-type path "text/plain")
+                encoding (mime-type->encoding mime-type "UTF-8")]
+            (server/send-and-close conn 200 (slurp local-path :encoding encoding)
+                                   mime-type encoding (and gzip? (= "text/javascript" mime-type))))
           ;; "/index.html" doesn't exist, provide our own
           (= path "/index.html")
           (server/send-and-close conn 200
@@ -203,7 +211,7 @@
 
 (server/dispatch-on :get
   (fn [{:keys [path]} _ _]
-    (or (= path "/") (some #(.endsWith path %) (keys ext->mime-type))))
+    (or (= path "/") (path->mime-type ext->mime-type path nil)))
   send-static)
 
 (defmulti handle-post (fn [m _ _ ] (:type m)))

From 9f8ad161eda1eb4b0fdb782e593ee9400b4e1f96 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Fri, 1 Jun 2018 14:23:02 -0400
Subject: [PATCH 3166/4033] CLJS-2764: exists? is not nil safe

---
 src/main/clojure/cljs/core.cljc   | 12 +++++++++---
 src/test/cljs/cljs/core_test.cljs |  6 ++++++
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 595640547..9eff043a3 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -965,9 +965,15 @@
   "Return true if argument exists, analogous to usage of typeof operator
    in JavaScript."
   [x]
-  (bool-expr
-    (core/list 'js* "typeof ~{} !== 'undefined'"
-      (vary-meta x assoc :cljs.analyzer/no-resolve true))))
+  (let [x     (:name (cljs.analyzer/resolve-var &env x))
+        segs  (string/split (core/str (string/replace x #"\/" ".")) #"\.")
+        n     (count segs)
+        syms  (map
+                #(vary-meta (symbol "js" (string/join "." %))
+                   assoc :cljs.analyzer/no-resolve true)
+                (reverse (take n (iterate butlast segs))))
+        js    (string/join " && " (repeat n "(typeof ~{} !== 'undefined')"))]
+    (bool-expr (concat (core/list 'js* js) syms))))
 
 (core/defmacro undefined?
   "Return true if argument is identical to the JavaScript undefined value."
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index 84c2845cb..dc5b0d3aa 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -727,6 +727,12 @@
     (is (false? (exists? js/jQuery)))
     (is (exists? exists?-test-val))))
 
+(deftest test-2764
+  (testing "Testing CLJS-2764, exists? on multi-segment symbols"
+    (is (false? (exists? this.ns.does.not.exist)))
+    (is (true? (exists? cljs.core.first)))
+    (is (true? (exists? cljs.core/first)))))
+
 (deftest test-518
   (is (nil? (:test "test"))))
 

From cae4792015626ca341e03a4fd9154b6705beba1e Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Fri, 1 Jun 2018 14:25:17 -0400
Subject: [PATCH 3167/4033] return false for gitlibs-src? for now

---
 src/main/clojure/cljs/closure.clj | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 2fc6735ce..da0b30446 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -64,7 +64,9 @@
 (defn- gitlibs-src?
   "Returns true if the file comes from the gitlibs cache."
   [file]
-  (string/starts-with? (util/path file) (gitlibs-cache-dir)))
+  #_(string/starts-with? (util/path file) (gitlibs-cache-dir))
+  ;; NOTE: does not work on first build see CLJS-2765
+  false)
 
 (def name-chars (map char (concat (range 48 57) (range 65 90) (range 97 122))))
 

From 80c77a2fa3cffcb9f3b021c6d2be6061294bab1a Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Fri, 1 Jun 2018 15:21:55 -0400
Subject: [PATCH 3168/4033] exists? should ignore js/

---
 src/main/clojure/cljs/core.cljc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 9eff043a3..515a91d9b 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -965,7 +965,8 @@
   "Return true if argument exists, analogous to usage of typeof operator
    in JavaScript."
   [x]
-  (let [x     (:name (cljs.analyzer/resolve-var &env x))
+  (let [x     (cond-> (:name (cljs.analyzer/resolve-var &env x))
+                (= "js" (namespace x)) name)
         segs  (string/split (core/str (string/replace x #"\/" ".")) #"\.")
         n     (count segs)
         syms  (map

From d5e8da2abec401222c1ed452f77e3674b0fa5900 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Fri, 1 Jun 2018 15:33:18 -0400
Subject: [PATCH 3169/4033] assert that argument to exists? is a symbol,
 clarify docstring

---
 src/main/clojure/cljs/core.cljc | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 515a91d9b..25273d55c 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -960,11 +960,12 @@
 (core/defmacro string? [x]
   (bool-expr (core/list 'js* "typeof ~{} === 'string'" x)))
 
-;; TODO: x must be a symbol, not an arbitrary expression
 (core/defmacro exists?
   "Return true if argument exists, analogous to usage of typeof operator
-   in JavaScript."
+   in JavaScript to check for undefined top-level var. x must be a symbol but
+   need not be top-level."
   [x]
+  (core/assert (core/symbol? x))
   (let [x     (cond-> (:name (cljs.analyzer/resolve-var &env x))
                 (= "js" (namespace x)) name)
         segs  (string/split (core/str (string/replace x #"\/" ".")) #"\.")

From 72e99c5d6f7164153cf6cf5613704ea342bde8da Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Fri, 1 Jun 2018 15:39:10 -0400
Subject: [PATCH 3170/4033] exists? should continue to support some? style
 usage

---
 src/main/clojure/cljs/core.cljc   | 26 +++++++++++++-------------
 src/test/cljs/cljs/core_test.cljs |  4 +++-
 2 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 25273d55c..2dd34f298 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -962,20 +962,20 @@
 
 (core/defmacro exists?
   "Return true if argument exists, analogous to usage of typeof operator
-   in JavaScript to check for undefined top-level var. x must be a symbol but
-   need not be top-level."
+   in JavaScript."
   [x]
-  (core/assert (core/symbol? x))
-  (let [x     (cond-> (:name (cljs.analyzer/resolve-var &env x))
-                (= "js" (namespace x)) name)
-        segs  (string/split (core/str (string/replace x #"\/" ".")) #"\.")
-        n     (count segs)
-        syms  (map
-                #(vary-meta (symbol "js" (string/join "." %))
-                   assoc :cljs.analyzer/no-resolve true)
-                (reverse (take n (iterate butlast segs))))
-        js    (string/join " && " (repeat n "(typeof ~{} !== 'undefined')"))]
-    (bool-expr (concat (core/list 'js* js) syms))))
+  (if (core/symbol? x)
+    (let [x     (cond-> (:name (cljs.analyzer/resolve-var &env x))
+                  (= "js" (namespace x)) name)
+          segs  (string/split (core/str (string/replace x #"\/" ".")) #"\.")
+          n     (count segs)
+          syms  (map
+                  #(vary-meta (symbol "js" (string/join "." %))
+                     assoc :cljs.analyzer/no-resolve true)
+                  (reverse (take n (iterate butlast segs))))
+          js    (string/join " && " (repeat n "(typeof ~{} !== 'undefined')"))]
+      (bool-expr (concat (core/list 'js* js) syms)))
+    `(some? ~x)))
 
 (core/defmacro undefined?
   "Return true if argument is identical to the JavaScript undefined value."
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index dc5b0d3aa..fc475a0ae 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -731,7 +731,9 @@
   (testing "Testing CLJS-2764, exists? on multi-segment symbols"
     (is (false? (exists? this.ns.does.not.exist)))
     (is (true? (exists? cljs.core.first)))
-    (is (true? (exists? cljs.core/first)))))
+    (is (true? (exists? cljs.core/first)))
+    (is (true? (exists? (:foo {:foo 1}))))
+    (is (false? (exists? (:foo {}))))))
 
 (deftest test-518
   (is (nil? (:test "test"))))

From 9a4e89efb01669c07d64b328f16fcb3d9edfa198 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 2 Jun 2018 15:16:23 -0400
Subject: [PATCH 3171/4033] CLJS-2766: Revisions to exists? fails in self-host

---
 src/main/clojure/cljs/core.cljc | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 2dd34f298..3c10a90cf 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -965,15 +965,15 @@
    in JavaScript."
   [x]
   (if (core/symbol? x)
-    (let [x     (cond-> (:name (cljs.analyzer/resolve-var &env x))
-                  (= "js" (namespace x)) name)
-          segs  (string/split (core/str (string/replace x #"\/" ".")) #"\.")
-          n     (count segs)
-          syms  (map
-                  #(vary-meta (symbol "js" (string/join "." %))
-                     assoc :cljs.analyzer/no-resolve true)
-                  (reverse (take n (iterate butlast segs))))
-          js    (string/join " && " (repeat n "(typeof ~{} !== 'undefined')"))]
+    (core/let [x     (core/cond-> (:name (cljs.analyzer/resolve-var &env x))
+                       (= "js" (namespace x)) name)
+               segs  (string/split (core/str (string/replace (core/str x) "/" ".")) #"\.")
+               n     (count segs)
+               syms  (map
+                       #(vary-meta (symbol "js" (string/join "." %))
+                          assoc :cljs.analyzer/no-resolve true)
+                       (reverse (take n (iterate butlast segs))))
+               js    (string/join " && " (repeat n "(typeof ~{} !== 'undefined')"))]
       (bool-expr (concat (core/list 'js* js) syms)))
     `(some? ~x)))
 

From 571dc6ffcb87a698bad20946786609cea1015efd Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Thu, 7 Jun 2018 12:41:04 +0200
Subject: [PATCH 3172/4033] add Clojure test runner to deps.edn

---
 deps.edn | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/deps.edn b/deps.edn
index 225dba582..0a7b38f12 100644
--- a/deps.edn
+++ b/deps.edn
@@ -10,5 +10,8 @@
   org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"}
   org.mozilla/rhino {:mvn/version "1.7R5"}}
  :aliases
- {:test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp"
-                       "src/test/clojure" "src/test/self"]}}}
+ {:test {:extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
+                                                 :sha "78d380d00e7a27f7b835bb90af37e73b20c49bcc"}}
+         :extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp"
+                       "src/test/clojure" "src/test/self"]
+         :main-opts ["-m" "cognitect.test-runner"]}}}

From 3d5553d93c4f120e901e8b54053eb2484e443bbb Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Thu, 7 Jun 2018 12:52:52 +0200
Subject: [PATCH 3173/4033] test -> test-clj alias

---
 deps.edn | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/deps.edn b/deps.edn
index 0a7b38f12..30bb9d492 100644
--- a/deps.edn
+++ b/deps.edn
@@ -10,8 +10,8 @@
   org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"}
   org.mozilla/rhino {:mvn/version "1.7R5"}}
  :aliases
- {:test {:extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
-                                                 :sha "78d380d00e7a27f7b835bb90af37e73b20c49bcc"}}
-         :extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp"
-                       "src/test/clojure" "src/test/self"]
-         :main-opts ["-m" "cognitect.test-runner"]}}}
+ {:test-clj {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp"
+                               "src/test/clojure" "src/test/self"]
+             :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
+                                                     :sha "78d380d00e7a27f7b835bb90af37e73b20c49bcc"}}
+             :main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure" "-r" ".*-tests$"]}}}

From 03455b4ba4338ab05ffccefc9ddb41c8fd128cfa Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Thu, 7 Jun 2018 13:28:11 +0200
Subject: [PATCH 3174/4033] elide "use strict" and 'use strict' by default from
 all production builds. Add flag to disable the behavior.

---
 src/main/clojure/cljs/closure.clj | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index da0b30446..3a7862946 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -202,7 +202,7 @@
     :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs
     :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})
+    :stable-names :ignore-js-module-exts :opts-cache :aot-cache :elide-strict})
 
 (def string->charset
   {"iso-8859-1" StandardCharsets/ISO_8859_1
@@ -1589,17 +1589,25 @@
   (deps-file {} [{:url (deps/to-url "out/cljs/core.js") :requires ["goog.string"] :provides ["cljs.core"]}])
   )
 
+(defn elide-strict [js {:keys [elide-strict] :as opts}]
+  (cond-> js
+    (not (false? elide-strict))
+    (->
+      (string/replace "\"use strict\"" "            ")
+      (string/replace "'use strict'" "            "))))
+
 (defn output-one-file [{:keys [output-to] :as opts} js]
-  (cond
-    (nil? output-to) js
+  (let [js (elide-strict js opts)]
+    (cond
+      (nil? output-to) js
 
-    (or (string? output-to)
-        (util/file? output-to))
-    (let [f (io/file output-to)]
-      (util/mkdirs f)
-      (spit f js))
+      (or (string? output-to)
+          (util/file? output-to))
+      (let [f (io/file output-to)]
+        (util/mkdirs f)
+        (spit f js))
 
-    :else (println js)))
+      :else (println js))))
 
 (defn output-deps-file [opts sources]
   (output-one-file opts (deps-file opts sources)))
@@ -1733,7 +1741,7 @@
           out-file (io/file output-to)]
       (util/mkdirs out-file)
       (spit out-file
-        (as-> source source
+        (as-> (elide-strict source opts) source
           (if (= name :cljs-base)
             (add-header opts source)
             source)

From aab9710a8b16b9c2c97c9ac4e87ae76824efbb67 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 8 Jun 2018 10:48:37 +0200
Subject: [PATCH 3175/4033] elide "use strict" after we've prepended foreign
 deps

---
 src/main/clojure/cljs/closure.clj | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 3a7862946..2f98968a3 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -1741,13 +1741,14 @@
           out-file (io/file output-to)]
       (util/mkdirs out-file)
       (spit out-file
-        (as-> (elide-strict source opts) source
+        (as-> source source
           (if (= name :cljs-base)
             (add-header opts source)
             source)
           (if fdeps-str
             (str fdeps-str "\n" source)
             source)
+          (elide-strict source opts)
           (if sm-name
             (add-source-map-link
               (assoc opts

From 215cd3c9495b93f4128a7af9cd6ee038b1d67f27 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 9 Jun 2018 11:03:58 -0400
Subject: [PATCH 3176/4033] CLJS-2769: Eliminate goog.structs.AvlTree.Node in
 self-parity test

---
 src/test/self/self_parity/auxiliary.cljs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs
index ef703d0db..ec8e2e3a3 100644
--- a/src/test/self/self_parity/auxiliary.cljs
+++ b/src/test/self/self_parity/auxiliary.cljs
@@ -131,7 +131,6 @@
     goog.string.newlines.Line
     goog.structs
     goog.structs.AvlTree
-    goog.structs.AvlTree.Node
     goog.structs.CircularBuffer
     goog.structs.Heap
     goog.structs.InversionMap

From 0ffe3d8f2aaa592e8d5b92e040e3dbfd62ab8b83 Mon Sep 17 00:00:00 2001
From: Pieter du Toit 
Date: Sun, 10 Jun 2018 22:51:31 +0200
Subject: [PATCH 3177/4033] CLJS-2771: Elide "use strict" from final output

Narrow regular expression (https://github.com/requirejs/r.js/issues/786)
---
 src/main/clojure/cljs/closure.clj | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 2f98968a3..6edacaf8b 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -1591,10 +1591,7 @@
 
 (defn elide-strict [js {:keys [elide-strict] :as opts}]
   (cond-> js
-    (not (false? elide-strict))
-    (->
-      (string/replace "\"use strict\"" "            ")
-      (string/replace "'use strict'" "            "))))
+    (not (false? elide-strict)) (string/replace #"(?m)^['\"]use strict['\"]" "            ")))
 
 (defn output-one-file [{:keys [output-to] :as opts} js]
   (let [js (elide-strict js opts)]

From 00b8dea4b373f367ef23dd7b8913c79b26e2fa94 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Fri, 18 May 2018 18:14:00 -0400
Subject: [PATCH 3178/4033] CLJS-2754: Broken cli tests

---
 src/main/clojure/cljs/closure.clj | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 6edacaf8b..bc9a3afaf 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -699,7 +699,8 @@
           (copy-from-cache cache-path cacheable jar-file opts))))
     ;; Files that don't require compilation (cljs.loader for example)
     ;; need to be copied from JAR to disk.
-    (when-not (.exists out-file)
+    (when (or (nil? out-file)
+              (not (.exists out-file)))
       (jar-file-to-disk jar-file (util/output-directory opts) opts))
     ;; have to call compile-file as it includes more IJavaScript
     ;; information than ana/parse-ns for now

From 1c4eefac5050c8480137d69d3d5b65fd53478cb2 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 13 Jun 2018 11:57:33 -0400
Subject: [PATCH 3179/4033] CLJS-2767: Externs inference warnings for defrecord
 and deftype

Ignore constructor property access. Type hint `other` when we know the
constructor is the same in defrecord -equiv implement.

Add tests.
---
 src/main/clojure/cljs/analyzer.cljc      |  3 ++-
 src/main/clojure/cljs/core.cljc          |  2 +-
 src/test/clojure/cljs/analyzer_tests.clj | 18 ++++++++++++++++++
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index ea2c6c6f0..c5b7e7844 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -2928,7 +2928,8 @@
                             (vary-meta (normalize-js-tag target-tag)
                               update-in [:prefix] (fnil conj '[Object]) prop))
                        nil)]
-    (when (not (string/starts-with? (str prop) "cljs$"))
+    (when (and (not= 'constructor prop)
+               (not (string/starts-with? (str prop) "cljs$")))
       ;; Adding to Object
       (when (= 'Object (first (-> tag meta :prefix)))
         (warning :infer-warning env
diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 3c10a90cf..f3d6a7684 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -1819,7 +1819,7 @@
                                                (.. ~other ~(to-property field))))
                                          base-fields)
                                   (= (.-__extmap ~this)
-                                     (.-__extmap ~other)))))
+                                     (.-__extmap ~(with-meta other {:tag tagname}))))))
                         'IMeta
                         `(~'-meta [this#] ~'__meta)
                         'IWithMeta
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index ed3d29660..d79fd59bb 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -1076,3 +1076,21 @@
                :warnings ws
                :warn false})]
     (is (= (unsplit-lines ["Object.Component;"]) res))))
+
+(deftest test-cljs-2767-deftype-defrecord
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(ns cjls-2767.core)
+                        (defrecord Foo [])]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws
+               :with-core? true})]
+    (is (empty? @ws)))
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(ns cjls-2767.core)
+                        (deftype Foo [])]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws
+               :with-core? true})]
+    (is (empty? @ws))))

From 3d44a6f5cea500245ddd6a3f07ef52ac20c105be Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 13 Jun 2018 14:34:05 -0400
Subject: [PATCH 3180/4033] validate :main

---
 src/main/clojure/cljs/cli.clj     |  1 +
 src/main/clojure/cljs/closure.clj | 28 ++++++++++++++++++++--------
 2 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 59402cdb8..8ea1cc0cd 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -474,6 +474,7 @@ present"
         convey   (into [:output-dir] repl/known-repl-opts)
         cfg      (update cfg :options merge (select-keys opts convey))
         source   (when (and (= :none (:optimizations opts :none)) main-ns)
+                   (closure/check-main opts)
                    (:uri (build/ns->location main-ns)))
         cenv     (env/default-compiler-env
                    (closure/add-externs-sources (dissoc opts :foreign-libs)))]
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index bc9a3afaf..3c21c7395 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2191,6 +2191,13 @@
   (assert (not (and (= target :nodejs) (= optimizations :none) (not (contains? opts :main))))
     (format ":nodejs target with :none optimizations requires a :main entry")))
 
+(defn check-main [{:keys [main] :as opts}]
+  (when main
+    (assert (symbol? main)
+      (format ":main must be a symbol, got %s instead" main))
+    (assert (not (string/starts-with? (str main) "'"))
+      (format ":main must be an unquoted symbol, got %s instead" main))))
+
 (defn check-preloads [{:keys [preloads optimizations] :as opts}]
   (when (and (some? preloads)
              (not= preloads '[process.env])
@@ -2812,6 +2819,18 @@
   [ns opts]
   (compile-inputs (find-sources ns opts) opts))
 
+(defn validate-opts [opts]
+  (check-output-to opts)
+  (check-output-dir opts)
+  (check-source-map opts)
+  (check-source-map-path opts)
+  (check-output-wrapper opts)
+  (check-node-target opts)
+  (check-preloads opts)
+  (check-cache-analysis-format opts)
+  (check-main opts)
+  opts)
+
 (defn build
   "Given compiler options, produce runnable JavaScript. An optional source
    parameter may be provided."
@@ -2848,14 +2867,7 @@
                            ana/*cljs-static-fns*)
              sources (when source
                        (-find-sources source opts))]
-         (check-output-to opts)
-         (check-output-dir opts)
-         (check-source-map opts)
-         (check-source-map-path opts)
-         (check-output-wrapper opts)
-         (check-node-target opts)
-         (check-preloads opts)
-         (check-cache-analysis-format opts)
+         (validate-opts opts)
          (swap! compiler-env
            #(-> %
              (update-in [:options] merge opts)

From 43bc1482c4adac00b8d1f4072d3f0b75827b172f Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Wed, 13 Jun 2018 15:12:07 -0400
Subject: [PATCH 3181/4033] continue to allow :main to be a string

---
 src/main/clojure/cljs/closure.clj | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 3c21c7395..9694bfa23 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -2193,10 +2193,11 @@
 
 (defn check-main [{:keys [main] :as opts}]
   (when main
-    (assert (symbol? main)
-      (format ":main must be a symbol, got %s instead" main))
-    (assert (not (string/starts-with? (str main) "'"))
-      (format ":main must be an unquoted symbol, got %s instead" main))))
+    (assert (or (symbol? main) (string? main))
+      (format ":main must be a symbol or string, got %s instead" main))
+    (when (symbol? main)
+      (assert (not (string/starts-with? (str main) "'"))
+        (format ":main must be an unquoted symbol, got %s instead" main)))))
 
 (defn check-preloads [{:keys [preloads optimizations] :as opts}]
   (when (and (some? preloads)

From 693cd0da6d89a4d3a583a9e78e68e458580b43cc Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Wed, 13 Jun 2018 17:33:07 -0400
Subject: [PATCH 3182/4033] ignore exists?

---
 src/main/clojure/cljs/analyzer.cljc      | 4 +++-
 src/test/clojure/cljs/analyzer_tests.clj | 6 ++++--
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index c5b7e7844..344113f0a 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1038,7 +1038,9 @@
          (when (contains? locals (-> sym name symbol))
            (warning :js-shadowed-by-local env {:name sym}))
          (let [pre (->> (string/split (name sym) #"\.") (map symbol) vec)]
-           (when-not (has-extern? pre)
+           (when (and (not (has-extern? pre))
+                      ;; ignore exists? usage
+                      (not (-> sym meta ::no-resolve)))
              (swap! env/*compiler* update-in
                (into [::namespaces (-> env :ns :name) :externs] pre) merge {}))
            (merge
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index d79fd59bb..abdb1ae49 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -1085,7 +1085,8 @@
                :externs ["src/test/externs/test.js"]
                :warnings ws
                :with-core? true})]
-    (is (empty? @ws)))
+    (is (empty? @ws))
+    (is (not (string/includes? res "cljs.core"))))
   (let [ws  (atom [])
         res (infer-test-helper
               {:forms '[(ns cjls-2767.core)
@@ -1093,4 +1094,5 @@
                :externs ["src/test/externs/test.js"]
                :warnings ws
                :with-core? true})]
-    (is (empty? @ws))))
+    (is (empty? @ws))
+    (is (not (string/includes? res "cljs.core")))))

From e75706a1754211628363d185945c0319604bd971 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Wed, 13 Jun 2018 12:40:18 -0400
Subject: [PATCH 3183/4033] CLJS-2775: cljs.main: Node modules not installed if
 -re node

If you look at the (non-cljs.main) REPL startup sequence in
cljs.repl/repl*, maybe-install-npm-deps is called prior to run-inits,
but in cljs.cli, there is a call to run-inits without first ensuring
that maybe-install-npm-deps has been called. This is simple to fix by
explicitly making such a call before run-inits is called.
---
 src/main/clojure/cljs/cli.clj       | 1 +
 src/test/cljs_cli/cljs_cli/test.clj | 9 +++++++++
 2 files changed, 10 insertions(+)

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index 8ea1cc0cd..223d93a6d 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -352,6 +352,7 @@ present"
                 `(set! *command-line-args* (list ~@args))))
             (repl/evaluate-form renv (ana-api/empty-env) ""
               `(~'ns ~'cljs.user))
+            (repl/maybe-install-npm-deps opts)
             (repl/run-inits renv inits)
             (when script
               (cond
diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj
index fb3dc3375..17e8f50f9 100644
--- a/src/test/cljs_cli/cljs_cli/test.clj
+++ b/src/test/cljs_cli/cljs_cli/test.clj
@@ -106,3 +106,12 @@
       (output-is
         nil
         4))))
+
+(deftest test-cljs-2775
+  (with-repl-env-filter #{"node"}
+    (-> (cljs-main
+          "-co" "{:npm-deps {:left-pad \"1.3.0\"} :install-deps true}"
+          "-d" "out"
+          "-e" "(require 'left-pad)"
+          "-e" "(left-pad 3 10 0)")
+      (output-is "nil\n\"0000000003\""))))

From 23d97da89232da99a5d42cf6637b70319a01949d Mon Sep 17 00:00:00 2001
From: Juho Teperi 
Date: Thu, 14 Jun 2018 20:31:44 +0300
Subject: [PATCH 3184/4033] CLJS-2777: Bump Closure-compiler

One important fix in the latest Closure-compiler is fix for UMD wrapper
detection which now correctly leaves React CJS bundle intact.

https://github.com/google/closure-compiler/issues/2841
https://github.com/google/closure-compiler/pull/2963
---
 deps.edn         | 2 +-
 pom.template.xml | 2 +-
 project.clj      | 2 +-
 script/bootstrap | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/deps.edn b/deps.edn
index 30bb9d492..4a5792f74 100644
--- a/deps.edn
+++ b/deps.edn
@@ -6,7 +6,7 @@
   org.clojure/spec.alpha {:mvn/version "0.1.143"}
   org.clojure/core.specs.alpha {:mvn/version "0.1.24"}
   org.clojure/data.json {:mvn/version "0.2.6"}
-  com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180319"}
+  com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180610"}
   org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"}
   org.mozilla/rhino {:mvn/version "1.7R5"}}
  :aliases
diff --git a/pom.template.xml b/pom.template.xml
index 981469771..0b8f29dd1 100644
--- a/pom.template.xml
+++ b/pom.template.xml
@@ -30,7 +30,7 @@
         
             com.google.javascript
             closure-compiler-unshaded
-            v20180319
+            v20180610
         
         
             org.clojure
diff --git a/project.clj b/project.clj
index 0fd50ce71..c454afc0d 100644
--- a/project.clj
+++ b/project.clj
@@ -16,7 +16,7 @@
                  [org.clojure/test.check "0.10.0-alpha2" :scope "test"]
                  [com.cognitect/transit-clj "0.8.300"]
                  [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"]
-                 [com.google.javascript/closure-compiler-unshaded "v20180319"]
+                 [com.google.javascript/closure-compiler-unshaded "v20180610"]
                  [org.mozilla/rhino "1.7R5"]]
   :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}
              :uberjar {:aot :all :main cljs.main}
diff --git a/script/bootstrap b/script/bootstrap
index c838389bf..86aa39981 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="20180319"
+CLOSURE_RELEASE="20180610"
 DJSON_RELEASE="0.2.6"
 TRANSIT_RELEASE="0.8.300"
 GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b"

From 3b0ce12d4c7e4a8ba9321531509490710d733662 Mon Sep 17 00:00:00 2001
From: dnolen 
Date: Thu, 14 Jun 2018 18:52:44 -0400
Subject: [PATCH 3185/4033] goog.global lookup must be a string

---
 src/main/cljs/cljs/js.cljs          | 2 +-
 src/main/clojure/cljs/compiler.cljc | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs
index 519bf36e1..ae7f4b185 100644
--- a/src/main/cljs/cljs/js.cljs
+++ b/src/main/cljs/cljs/js.cljs
@@ -659,7 +659,7 @@
           (with-out-str
             (comp/emitln (munge ns-name) "."
               (ana/munge-global-export dep)
-              " = goog.global." (get global-exports (symbol dep)) ";")))))
+              " = goog.global[\"" (get global-exports (symbol dep)) "\"];")))))
     (when (and (seq deps) emit-nil-result?)
       (.append sb "null;"))))
 
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 25492bfff..d6e77c196 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1179,7 +1179,7 @@
       (let [{:keys [global-exports]} (get js-dependency-index (name lib))]
         (emitln (munge ns-name) "."
           (ana/munge-global-export lib)
-          " = goog.global." (get global-exports (symbol lib)) ";")))
+          " = goog.global[\"" (get global-exports (symbol lib)) "\"];")))
     (when (-> libs meta :reload-all)
       (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))
 

From 18b97ab86484c4f52bcb462fe585b011e6cf37f2 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 15 Jun 2018 08:14:02 -0400
Subject: [PATCH 3186/4033] CLJS-2278 & CLJS-2279

---
 src/test/clojure/cljs/build_api_tests.clj | 2 +-
 src/test/self/self_host/test.cljs         | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj
index 71d736c0b..00f27562a 100644
--- a/src/test/clojure/cljs/build_api_tests.clj
+++ b/src/test/clojure/cljs/build_api_tests.clj
@@ -396,7 +396,7 @@
       (ana/with-warning-handlers [(collecting-warning-handler ws)]
         (build/build (build/inputs (io/file inputs "emit_global_requires_test/core.cljs")) opts cenv))
       (is (.exists (io/file out "emit_global_requires_test/core.js")))
-      (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$react_dom\$server = goog\.global\.ReactDOMServer;"
+      (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$react_dom\$server = goog\.global\[\"ReactDOMServer\"\];"
                             (slurp (io/file out "emit_global_requires_test/core.js"))))))
       (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$react_dom\$server\.renderToString"
                             (slurp (io/file out "emit_global_requires_test/core.js"))))))
diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs
index a6bd46e26..a59818e21 100644
--- a/src/test/self/self_host/test.cljs
+++ b/src/test/self/self_host/test.cljs
@@ -1088,7 +1088,7 @@
          :eval node-eval}
         (fn [{:keys [error value] :as m}]
           (is (nil? error))
-          (is (some? (re-find #"foo\.core\.global\$module\$calculator = goog.global.Calculator;" value)))
+          (is (some? (re-find #"foo\.core\.global\$module\$calculator = goog.global\[\"Calculator\"\];" value)))
           (inc! l)))
       (cljs/eval-str
         (atom @st)
@@ -1166,7 +1166,7 @@
            :load calculator-load
            :eval identity}
           (fn [{{:keys [source]} :value}]
-            (is (some? (re-find #"foo\.core\.global\$module\$calculator = goog.global.Calculator;\snull;" source)))
+            (is (some? (re-find #"foo\.core\.global\$module\$calculator = goog.global\[\"Calculator\"\];\snull;" source)))
             (inc! l)))))))
 
 (deftest test-cljs-2261

From 6512df8321b16a819ea4cc870edf25b7c809947e Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 15 Jun 2018 08:39:47 -0400
Subject: [PATCH 3187/4033] 1.10.312

---
 README.md  |  6 +++---
 changes.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 06c03dcfb..0b4dba522 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,14 @@ Official web site: http://clojurescript.org
 
 ## Releases and dependency information ##
 
-Latest stable release: 1.10.238
+Latest stable release: 1.10.312
 
 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22)
 
 [Leiningen](http://github.com/technomancy/leiningen/) dependency information:
 
 ```
-[org.clojure/clojurescript "1.10.238"]
+[org.clojure/clojurescript "1.10.312"]
 ```
 
 [Maven](http://maven.apache.org) dependency information:
@@ -22,7 +22,7 @@ Latest stable release: 1.10.238
 
   org.clojure
   clojurescript
-  1.10.238
+  1.10.312
 
 ```
 
diff --git a/changes.md b/changes.md
index b28d3f1e6..6a91b81e4 100644
--- a/changes.md
+++ b/changes.md
@@ -1,3 +1,58 @@
+## 1.10.312
+
+### Enhancements
+* CLJS-1871: A declare with :arglists should generate static function calls
+* CLJS-2688 cljs.main: Accumulate all meaningful repeated inits modules using global-exports
+* CLJS-2681: Accepting multiple paths to the --watch option for cljs.main
+* CLJS-2706: Better error messages when missing namespaces contain dashes
+
+### Changes
+* CLJS-2777: Bump Closure-compiler
+* validate :main
+* CLJS-2771: Elide "use strict"1 from final output
+
+### Fixes
+* CLJS-2278 & CLJS-2279
+* goog.global lookup must be a string
+* CLJS-2775: cljs.main: Node modules not installed if -re node
+* CLJS-2767: Externs inference warnings for defrecord and deftype
+* CLJS-2754: Broken cli tests
+* CLJS-2769: Eliminate goog.structs.AvlTree.Node in self-parity test
+* CLJS-2766: Revisions to exists? fails in self-host
+* CLJS-2764: exists? is not nil safe
+* CLJS-2760 Make browser repl web-severs mime-type case-insensitive
+* CLJS-2755: Can't generate uri instances
+* CLJS-1677: Requiring [goog] breaks an :advanced build, but the compiler exits successfully
+* Recompile cljs.loader in REPL
+* CLJS-2733: Throw error message if too few or too many args to throw
+* CLJS-2751: script/bootstrap --closure-library-head misses goog/text
+* CLJS-2480: Periods at end of analyzer warnings
+* CLJS-2618 Fix docstring for `remove-tap`
+* CLJS-2743 Fix docstring misspelling
+* CLJS-2724: Native Node modules Node (like "fs") cannot be required
+* CLJS-2702: Accomodate new Closure Library dependency loading strategy
+* CLJS-2741: Function invoke errors report arity off by 1
+* CLJS-2745: Add GraalVM to the set of JavaScript engines we can test against
+* CLJS-2739: Optimize node_modules indexing
+* CLJS-2619: clojure.reflect needs exclude for macroexpand
+* CLJS-2713: test-reader fails on Windows
+* CLJS-2715: Have goog-define return the var at the REPL
+* CLJS-2727: cljs.repl/err-out visible from cljs
+* CLJS-2734: Add :arglists to defmulti
+* CLJS-2721: test-cljs-2580 failing in windows CI
+* CLJS-2726: test-cljs-2678-global-exports-infer failing on Windows
+* CLJS-2678: Infer-externs doesn't work for JS modules using global-exports
+* CLJS-2718: Setting *warn-on-infer* in REPL throws a SyntaxError
+* CLJS-2385: cljs.analyzer/infer-type pass infers tag with incorrect priority
+* CLJS-1918: case needs a type hint for keywords case when using *warn-on-infer*
+* CLJS-1970: Cannot infer target type for list/vector expressions
+* CLJS-2669: Use simple dummy package for test-cljs-2580
+* CLJS-2716: Add ChakraCore to Windows CI (AppVeyor)
+* CLJS-2147: apply test suit
+* CLJS-2711: System newline breaking some tests on Windows
+* CLJS-2712: Make Windows CI fail if a test fails
+* CLJS-2708: Windows. ClojureScript fails to compile when node.js module is `require`d
+
 ## 1.10.238
 
 ### Enhancements

From 9100030be16b4cb792b3b2c124fd41a5f5a67c63 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Fri, 18 May 2018 10:50:25 -0400
Subject: [PATCH 3188/4033] CLJS-2298: REPLs should automatically load
 user.(cljs|cljc) files at root of Java classpath

---
 src/main/clojure/cljs/repl.cljc | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index 9b7770a1c..71d5d8e43 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -953,6 +953,9 @@
                                 (:require ~@repl-requires))
                              {:line 1 :column 1})
                            identity opts)))
+               maybe-load-user-file #(when-let [user-resource (util/ns->source 'user)]
+                                       (when (= "file" (.getProtocol ^URL user-resource))
+                                         (load-file repl-env (io/file user-resource) opts)))
                read-eval-print
                (fn []
                  (let [input (binding [*ns* (create-ns ana/*cljs-ns*)
@@ -983,6 +986,7 @@
                        (analyze-source analyze-path opts)))
                    (init)
                    (run-inits repl-env inits)
+                   (maybe-load-user-file)
                    (catch Throwable e
                      (caught e repl-env opts)))
                  (when-let [src (:watch opts)]

From 6b7e9402dd841eeb63e2bb3ae3b397eced388def Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 15 Jun 2018 15:28:06 -0400
Subject: [PATCH 3189/4033] CLJS-2736: Elements returned from sets as functions
 are not the actual elements in the set

---
 src/main/cljs/cljs/core.cljs             |  4 ++--
 src/test/cljs/cljs/collections_test.cljs | 12 +++++++++++-
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 4c1b88f2a..b8a9c3acd 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -9102,8 +9102,8 @@ reduces them without incurring seq initialization"
   (-lookup [coll v]
     (-lookup coll v nil))
   (-lookup [coll v not-found]
-    (if (-contains-key? hash-map v)
-      v
+    (if-let [entry (-find hash-map v)]
+      (key entry)
       not-found))
 
   ISet
diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs
index 74da730a5..236f546fd 100644
--- a/src/test/cljs/cljs/collections_test.cljs
+++ b/src/test/cljs/cljs/collections_test.cljs
@@ -8,7 +8,7 @@
 
 (ns cljs.collections-test
   (:refer-clojure :exclude [iter])
-  (:require [cljs.test :refer-macros [deftest testing is are]]
+  (:require [cljs.test :refer-macros [deftest testing is are run-tests]]
             [clojure.string :as s]
             [clojure.set :as set]))
 
@@ -998,3 +998,13 @@
     (is (not (realized? xs)))
     (is (not (realized? ys)))
     (is (= () xs ys))))
+
+(deftest test-cljs-2736
+  (let [s #{(with-meta [:a] {:n 42})}]
+    (is (= {:n 42} (meta (s [:a]))))))
+
+(comment
+
+  (run-tests)
+
+  )
\ No newline at end of file

From 343e6aef57b802975fca3fb5fa0fd85a20330d66 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 15 Jun 2018 16:22:29 -0400
Subject: [PATCH 3190/4033] CLJS-2772: Trying to run `cljs.main` repl with
 `:modules` results in `brepl_deps.js` being `clojure.lang.LazySeq`

dissoc :modules when computing brepl_deps.js
---
 src/main/clojure/cljs/repl/browser.clj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 49a70d87e..83c2ad0d1 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -351,7 +351,7 @@
             (spit target
               (build/build
                 '[(require '[clojure.browser.repl.preload])]
-                (merge (select-keys opts cljsc/known-opts)
+                (merge (dissoc (select-keys opts cljsc/known-opts) :modules)
                   {:opts-cache "brepl_opts.edn"})))))
         (server/start repl-env)
         (let [base-url (str "http://" (:host repl-env) ":" (:port repl-env))]

From ec1e416bccf7ad2d63d8e97a156dec989fa1a9d7 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 15 Jun 2018 16:48:21 -0400
Subject: [PATCH 3191/4033] CLJS-2770: Problem with namespaces starting with
 com in Rhino

---
 src/main/clojure/cljs/analyzer.cljc | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 344113f0a..cd570a3ff 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -181,7 +181,9 @@
     "synchronized" "this" "throw" "throws"
     "transient" "try" "typeof" "var" "void"
     "volatile" "while" "with" "yield" "methods"
-    "null" "constructor"})
+    "null" "constructor"
+    "com" ;; for Rhino
+    })
 
 (def es5-allowed
   #{"default"})
@@ -2600,8 +2602,10 @@
     (let [segments (string/split (clojure.core/name name) #"\.")]
       (when (= 1 (count segments))
         (warning :single-segment-namespace env {:name name}))
-      (when (some? (some js-reserved segments))
-        (warning :munged-namespace env {:name name}))
+      (let [segment (some js-reserved segments)]
+        ;; don't complain about "com", for Rhino
+        (when (and (some? segment) (not= "com" segment))
+          (warning :munged-namespace env {:name name})))
       (find-def-clash env name segments)
       #?(:clj
          (when (some (complement util/valid-js-id-start?) segments)
@@ -2613,7 +2617,6 @@
           mdocstr      (-> name meta :doc)
           args         (if (some? docstring) (next args) args)
           metadata     (when (map? (first args)) (first args))
-          form-meta    (meta form)
           args         (desugar-ns-specs
                          #?(:clj  (rewrite-cljs-aliases
                                     (if metadata (next args) args))

From 6950aff480174a25a77c2308aa87a099a774a6e8 Mon Sep 17 00:00:00 2001
From: Corin Lawson 
Date: Thu, 10 May 2018 02:18:37 +1000
Subject: [PATCH 3192/4033] CLJS-2746 Missing provides for JS modules

Prior to this change, JS modules are converted my writing a single
goog.provide statement that corresponds to the file path and name of the
module. The .getProvides method (of another module) may use a shorter
name and this means that the ClojureScript compiler can never find the
module by this shorter name.

This change emits a goog.provide statement for each name specified in
the provides list of the IJavaScriptFile.
---
 src/main/clojure/cljs/closure.clj | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 9694bfa23..7973c80c6 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -1830,7 +1830,7 @@
     "IMPORTED_SCRIPT" :imported-script))
 
 (defn add-converted-source
-  [closure-compiler inputs-by-name opts {:keys [file-min file requires] :as ijs}]
+  [closure-compiler inputs-by-name opts {:keys [file-min file provides requires] :as ijs}]
   (let [processed-file (if-let [min (and (#{:advanced :simple} (:optimizations opts))
                                          file-min)]
                          min
@@ -1838,7 +1838,8 @@
         processed-file (string/replace processed-file "\\" "/")
         ^CompilerInput input (get inputs-by-name processed-file)
         ^Node ast-root (.getAstRoot input closure-compiler)
-        module-name (ModuleNames/fileToModuleName processed-file)
+        provides (distinct (map #(ModuleNames/fileToModuleName %)
+                                (cons processed-file provides)))
         ;; getJsModuleType returns NONE for ES6 files, but getLoadsFlags module returns es6 for those
         module-type (or (some-> (.get (.getLoadFlags input) "module") keyword)
                         (module-type->keyword (.getJsModuleType input)))]
@@ -1848,7 +1849,9 @@
       ;; Add goog.provide/require calls ourselves, not emited by Closure since
       ;; https://github.com/google/closure-compiler/pull/2641
       (str
-        "goog.provide(\"" module-name "\");\n"
+        (apply str (map (fn [n]
+                          (str "goog.provide(\"" n "\");\n"))
+                        provides))
         (->> (.getRequires input)
              ;; v20180204 returns string
              ;; next Closure returns DependencyInfo.Require object

From b8101e637210a155986216e356cfe849393ec996 Mon Sep 17 00:00:00 2001
From: "Jeremy R. Sellars" 
Date: Thu, 3 May 2018 23:55:39 -0500
Subject: [PATCH 3193/4033] CLJS-2731: Failure comparing sorted sets

Catch comparison errors in PersistentTreeSet and PersistentHashSet .-equiv when compared with sorted sets.
---
 src/main/cljs/cljs/core.cljs           | 18 ++++++++++++------
 src/test/cljs/cljs/set_equiv_test.cljs | 18 ++++++++++++++++++
 src/test/self/self_parity/test.cljs    |  2 ++
 3 files changed, 32 insertions(+), 6 deletions(-)
 create mode 100644 src/test/cljs/cljs/set_equiv_test.cljs

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index b8a9c3acd..482dacd26 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -9085,9 +9085,12 @@ reduces them without incurring seq initialization"
      (set? other)
      (== (count coll) (count other))
      ^boolean
-     (reduce-kv
-       #(or (contains? other %2) (reduced false))
-       true hash-map)))
+     (try
+       (reduce-kv
+         #(or (contains? other %2) (reduced false))
+         true hash-map)
+       (catch js/Error ex
+         false))))
 
   IHash
   (-hash [coll] (caching-hash coll hash-unordered-coll __hash))
@@ -9236,9 +9239,12 @@ reduces them without incurring seq initialization"
      (set? other)
      (== (count coll) (count other))
      ^boolean
-     (reduce-kv
-       #(or (contains? other %2) (reduced false))
-       true tree-map)))
+     (try
+       (reduce-kv
+         #(or (contains? other %2) (reduced false))
+         true tree-map)
+       (catch js/Error ex
+         false))))
 
   IHash
   (-hash [coll] (caching-hash coll hash-unordered-coll __hash))
diff --git a/src/test/cljs/cljs/set_equiv_test.cljs b/src/test/cljs/cljs/set_equiv_test.cljs
new file mode 100644
index 000000000..31b5d0573
--- /dev/null
+++ b/src/test/cljs/cljs/set_equiv_test.cljs
@@ -0,0 +1,18 @@
+(ns cljs.set-equiv-test
+  (:refer-clojure :exclude [iter])
+  (:require [cljs.test :refer-macros [deftest testing is]]
+            [clojure.string :as s]
+            [clojure.set :as set]))
+
+(deftest test-set-equality
+  (testing "Testing set equality"
+    (is (= (sorted-set 3 2 1)    (sorted-set 1 2 3)))
+    (is (= (hash-set   3 2 1)    (sorted-set 1 2 3)))
+    (is (= (sorted-set :a :b :c) (hash-set :a :b :c)))
+    (is (= (hash-set   :a :b :c) (hash-set :a :b :c))))
+
+  (testing "CLJS-2731 uncomparable values"
+    (is (not= (sorted-set 3 2 1) (sorted-set :a :b :c)))
+    (is (not= (hash-set 3 2 1)   (sorted-set :a :b :c)))
+    (is (not= (sorted-set 3 2 1) (hash-set   :a :b :c)))
+    (is (not= (hash-set 3 2 1)   (hash-set   :a :b :c)))))
diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs
index a03a3b76b..55023d828 100644
--- a/src/test/self/self_parity/test.cljs
+++ b/src/test/self/self_parity/test.cljs
@@ -303,6 +303,7 @@
                  [cljs.clojure-alias-test]
                  [cljs.hash-map-test]
                  [cljs.map-entry-test]
+                 [cljs.set-equiv-test]
                  [cljs.syntax-quote-test]
                  [cljs.predicates-test]
                  [cljs.test-test]
@@ -345,6 +346,7 @@
              'cljs.clojure-alias-test
              'cljs.hash-map-test
              'cljs.map-entry-test
+             'cljs.set-equiv-test
              'cljs.syntax-quote-test
              'cljs.predicates-test
              'cljs.test-test

From 6d23b1f4a8eb5f9b504a40f7dacac16e2bb38773 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 7 Apr 2018 16:48:49 -0400
Subject: [PATCH 3194/4033] CLJS-2703: module name substitution test fails if
 hypen in directory path

---
 src/test/clojure/cljs/module_processing_tests.clj | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj
index 1514ec563..27b5749ce 100644
--- a/src/test/clojure/cljs/module_processing_tests.clj
+++ b/src/test/clojure/cljs/module_processing_tests.clj
@@ -36,6 +36,7 @@
                    (.getAbsolutePath $)
                    (subs $ 0 (.lastIndexOf $ (str File/separator)))
                    (string/replace $ "/" "$")
+                   (string/replace $ "-" "_")
                    ;; Windows
                    (string/replace $ "\\" "$")
                    (if code?

From 7528dc79fea372fafa365ee80e4568a3af5f1526 Mon Sep 17 00:00:00 2001
From: Erik Assum 
Date: Sat, 16 Jun 2018 08:47:18 +0200
Subject: [PATCH 3195/4033] CLJS-2730 Fix docstrings in filter, filtev, remove,
 and take-while

---
 src/main/cljs/cljs/core.cljs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 482dacd26..eeb247d44 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -5098,7 +5098,7 @@ reduces them without incurring seq initialization"
 
 (defn filter
   "Returns a lazy sequence of the items in coll for which
-  (pred item) returns true. pred must be free of side-effects.
+  (pred item) returns logical true. pred must be free of side-effects.
   Returns a transducer when no collection is provided."
   ([pred]
     (fn [rf]
@@ -5127,7 +5127,7 @@ reduces them without incurring seq initialization"
 
 (defn remove
   "Returns a lazy sequence of the items in coll for which
-  (pred item) returns false. pred must be free of side-effects.
+  (pred item) returns logical false. pred must be free of side-effects.
   Returns a transducer when no collection is provided."
   ([pred] (filter (complement pred)))
   ([pred coll]
@@ -5190,7 +5190,7 @@ reduces them without incurring seq initialization"
 
 (defn filterv
   "Returns a vector of the items in coll for which
-  (pred item) returns true. pred must be free of side-effects."
+  (pred item) returns logical true. pred must be free of side-effects."
   [pred coll]
   (-> (reduce (fn [v o] (if (pred o) (conj! v o) v))
               (transient [])
@@ -9470,7 +9470,7 @@ reduces them without incurring seq initialization"
 
 (defn take-while
   "Returns a lazy sequence of successive items from coll while
-  (pred item) returns true. pred must be free of side-effects.
+  (pred item) returns logical true. pred must be free of side-effects.
   Returns a transducer when no collection is provided."
   ([pred]
      (fn [rf]

From 6048bc95001e1d5f1c996c5cda187cdfc0193bc8 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sat, 16 Jun 2018 22:06:24 -0400
Subject: [PATCH 3196/4033] CLJS-2783: with-out-str conflicts with
 :infer-externs

---
 src/main/clojure/cljs/core.cljc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index f3d6a7684..28338fc78 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -2780,7 +2780,7 @@
   on a fresh StringBuffer.  Returns the string created by any nested
   printing calls."
   [& body]
-  `(let [sb# (js/goog.string.StringBuffer.)]
+  `(let [sb# (goog.string/StringBuffer.)]
      (binding [cljs.core/*print-newline* true
                cljs.core/*print-fn* (fn [x#] (.append sb# x#))]
        ~@body)

From 5dc641ed1bf5ea642a9cb00f0fc770eb479c23bb Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 16 Jun 2018 17:02:11 -0400
Subject: [PATCH 3197/4033] CLJS-2780: Async tests prematurely terminate in
 Node

While the current implementation of -tear-down in the Node REPL allows
any pending (Node termination delaying) processing to finish, it
doesn't allow for any further communication between the REPL and the
Node process: It immediately destroys the Node-side socket while also
discarding any REPL-side communication structures.

This patch moves the code that discards REPL-side communication
structures to occur after awaiting Node process termination so that any
messages that are sent can be displayed. It also replaces the Node-side
destroy call to an unref: This allows any lingering communication to
occur, without causing the socket to keep the Node process alive.
---
 src/main/clojure/cljs/repl/node.clj     | 10 +++++-----
 src/main/clojure/cljs/repl/node_repl.js |  2 +-
 src/test/cljs_cli/cljs_cli/test.clj     |  9 +++++++++
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj
index 1a3ccbaa1..363880ec7 100644
--- a/src/main/clojure/cljs/repl/node.clj
+++ b/src/main/clojure/cljs/repl/node.clj
@@ -240,17 +240,17 @@
     (load-javascript this provides url))
   (-tear-down [this]
     (swap! state update :listeners dec)
-    (let [tname (thread-name)]
-      (.remove results tname)
-      (.remove outs tname)
-      (.remove errs tname))
     (locking lock
       (when (zero? (:listeners @state))
         (let [sock @socket]
           (when-not (.isClosed (:socket sock))
             (write (:out sock) ":cljs/quit")
             (while (alive? @proc) (Thread/sleep 50))
-            (close-socket sock)))))))
+            (close-socket sock)))))
+    (let [tname (thread-name)]
+      (.remove results tname)
+      (.remove outs tname)
+      (.remove errs tname))))
 
 (defn repl-env* [options]
   (let [{:keys [host port path debug-port]}
diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js
index cdebe62ce..109be0d18 100644
--- a/src/main/clojure/cljs/repl/node_repl.js
+++ b/src/main/clojure/cljs/repl/node_repl.js
@@ -66,7 +66,7 @@ var server = net.createServer(function (socket) {
 
                 if(":cljs/quit" == data) {
                     server.close();
-                    socket.destroy();
+                    socket.unref();
                     return;
                 } else {
                     try {
diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj
index 17e8f50f9..285282161 100644
--- a/src/test/cljs_cli/cljs_cli/test.clj
+++ b/src/test/cljs_cli/cljs_cli/test.clj
@@ -115,3 +115,12 @@
           "-e" "(require 'left-pad)"
           "-e" "(left-pad 3 10 0)")
       (output-is "nil\n\"0000000003\""))))
+
+(deftest test-cljs-2780
+  (with-repl-env-filter #{"node" "nashorn"}
+    (-> (cljs-main
+          "-e" "(do (js/setTimeout #(prn :end) 500) nil)"
+          "-e" ":begin")
+      (output-is
+        :begin
+        :end))))

From abf4e784364c075b707ff992f6f65fecf29aae39 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 17 Jun 2018 10:06:01 -0400
Subject: [PATCH 3198/4033] CLJS-2784: Special handling of com breaks requires

Revert CLJS-2770
---
 src/main/clojure/cljs/analyzer.cljc | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index cd570a3ff..079aeb7e8 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -181,9 +181,7 @@
     "synchronized" "this" "throw" "throws"
     "transient" "try" "typeof" "var" "void"
     "volatile" "while" "with" "yield" "methods"
-    "null" "constructor"
-    "com" ;; for Rhino
-    })
+    "null" "constructor"})
 
 (def es5-allowed
   #{"default"})
@@ -2603,8 +2601,7 @@
       (when (= 1 (count segments))
         (warning :single-segment-namespace env {:name name}))
       (let [segment (some js-reserved segments)]
-        ;; don't complain about "com", for Rhino
-        (when (and (some? segment) (not= "com" segment))
+        (when (some? segment)
           (warning :munged-namespace env {:name name})))
       (find-def-clash env name segments)
       #?(:clj

From c7c7449b520940d174d812a3791174ea4bca6fdd Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Tue, 18 Jul 2017 12:18:08 -0400
Subject: [PATCH 3199/4033] rename ast op :constant -> :const

---
 src/main/clojure/cljs/analyzer.cljc | 18 +++++++++---------
 src/main/clojure/cljs/compiler.cljc | 14 +++++++-------
 src/main/clojure/cljs/core.cljc     |  6 +++---
 3 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 079aeb7e8..bb3c9ab61 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1209,7 +1209,7 @@
 (defn analyze-keyword
   [env sym]
   (register-constant! env sym)
-  {:op :constant :env env :form sym :tag 'cljs.core/Keyword})
+  {:op :const :env env :form sym :tag 'cljs.core/Keyword})
 
 (defn get-tag [e]
   (if-some [tag (-> e :form meta :tag)]
@@ -1252,8 +1252,8 @@
 (defn infer-if [env e]
   (let [{{:keys [op form]} :test} e
         then-tag (infer-tag env (:then e))]
-    (if (and #?(:clj (= op :constant)
-                :cljs (keyword-identical? op :constant))
+    (if (and #?(:clj (= op :const)
+                :cljs (keyword-identical? op :const))
              (not (nil? form))
              (not (false? form)))
       then-tag
@@ -1310,7 +1310,7 @@
         :def      (infer-tag env (:init e))
         :invoke   (infer-invoke env e)
         :if       (infer-if env e)
-        :constant (case (:form e)
+        :const    (case (:form e)
                     true BOOLEAN_SYM
                     false BOOLEAN_SYM
                     ANY_SYM)
@@ -1394,7 +1394,7 @@
     (assert (every? (fn [t]
                       (or
                         (-> t :info :const)
-                        (and (= :constant (:op t))
+                        (and (= :const (:op t))
                              ((some-fn number? string? char?) (:form t)))))
               (apply concat tests))
       "case* tests must be numbers, strings, or constants")
@@ -1489,7 +1489,7 @@
 
 (defn constant-value?
   [{:keys [op] :as ast}]
-  (or (= :constant op)
+  (or (= :const op)
       (and (#{:map :set :vector :list} op)
            (every? constant-value? (:children ast)))))
 
@@ -3199,7 +3199,7 @@
   (if ^boolean (:quoted? env)
     (do
       (register-constant! env sym)
-      (analyze-wrap-meta {:op :constant :env env :form sym :tag 'cljs.core/Symbol}))
+      (analyze-wrap-meta {:op :const :env env :form sym :tag 'cljs.core/Symbol}))
     (let [{:keys [line column]} (meta sym)
           env  (if-not (nil? line)
                  (assoc env :line line)
@@ -3591,7 +3591,7 @@
                    (instance? Character form) 'string
                    (true? form) 'boolean
                    (false? form) 'boolean)]
-         (cond-> {:op :constant :env env :form form}
+         (cond-> {:op :const :env env :form form}
            tag (assoc :tag tag))))))
 
 #?(:cljs
@@ -3613,7 +3613,7 @@
                    (string? form) STRING_SYM
                    (true? form) BOOLEAN_SYM
                    (false? form) BOOLEAN_SYM)]
-         (cond-> {:op :constant :env env :form form}
+         (cond-> {:op :const :env env :form form}
            tag (assoc :tag tag))))))
 
 (defn analyze* [env form name opts]
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index d6e77c196..98dfa51e3 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -414,7 +414,7 @@
 (def ^:private array-map-threshold 8)
 
 (defn distinct-keys? [keys]
-  (and (every? #(= (:op %) :constant) keys)
+  (and (every? #(= (:op %) :const) keys)
        (= (count (into #{} keys)) (count keys))))
 
 (defmethod emit* :map
@@ -459,7 +459,7 @@
           (emits "cljs.core.PersistentVector.fromArray([" (comma-sep items) "], true)"))))))
 
 (defn distinct-constants? [items]
-  (and (every? #(= (:op %) :constant) items)
+  (and (every? #(= (:op %) :const) items)
        (= (count (into #{} items)) (count items))))
 
 (defmethod emit* :set
@@ -494,13 +494,13 @@
   (emit-wrap env
     (emits ns ".map__GT_" name "(" items ")")))
 
-(defmethod emit* :constant
+(defmethod emit* :const
   [{:keys [form env]}]
   (when-not (= :statement (:context env))
     (emit-wrap env (emit-constant form))))
 
 (defn truthy-constant? [{:keys [op form const-expr]}]
-  (or (and (= op :constant)
+  (or (and (= op :const)
            form
            (not (or (and (string? form) (= form ""))
                     (and (number? form) (zero? form)))))
@@ -508,7 +508,7 @@
            (truthy-constant? const-expr))))
 
 (defn falsey-constant? [{:keys [op form const-expr]}]
-  (or (and (= op :constant)
+  (or (and (= op :const)
            (or (false? form) (nil? form)))
       (and (some? const-expr)
            (falsey-constant? const-expr))))
@@ -943,7 +943,7 @@
         (when name
           (emits "catch (" (munge name) "){" catch "}"))
         (when finally
-          (assert (not= :constant (:op finally)) "finally block cannot contain constant")
+          (assert (not= :const (:op finally)) "finally block cannot contain constant")
           (emits "finally {" finally "}"))
         (when (= :expr context)
           (emits "})()")))
@@ -1034,7 +1034,7 @@
                     (not (contains? (::ana/namespaces @env/*compiler*) ns))))
 
         keyword? (or (= 'cljs.core/Keyword (ana/infer-tag env f))
-                     (and (= (-> f :op) :constant)
+                     (and (= (-> f :op) :const)
                           (keyword? (-> f :form))))
         [f variadic-invoke]
         (if fn?
diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 28338fc78..24a64cdd7 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -845,7 +845,7 @@
 
 (core/defn- simple-test-expr? [env ast]
   (core/and
-    (#{:var :invoke :constant :dot :js} (:op ast))
+    (#{:var :invoke :const :dot :js} (:op ast))
     ('#{boolean seq} (cljs.analyzer/infer-tag env ast))))
 
 (core/defmacro and
@@ -2528,7 +2528,7 @@
   ([] '(.-EMPTY cljs.core/PersistentArrayMap))
   ([& kvs]
    (core/let [keys (map first (partition 2 kvs))]
-     (if (core/and (every? #(= (:op %) :constant)
+     (if (core/and (every? #(= (:op %) :const)
                      (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) keys))
            (= (count (into #{} keys)) (count keys)))
        `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil)
@@ -2548,7 +2548,7 @@
   ([] `(.-EMPTY cljs.core/PersistentHashSet))
   ([& xs]
     (if (core/and (core/<= (count xs) 8)
-                  (every? #(= (:op %) :constant)
+                  (every? #(= (:op %) :const)
                     (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) xs))
                   (= (count (into #{} xs)) (count xs)))
       `(cljs.core/PersistentHashSet. nil

From 3620434c8ae812d2127f4b162d82e2c99cd104aa Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Tue, 18 Jul 2017 12:26:02 -0400
Subject: [PATCH 3200/4033] add :val to :const node

---
 src/main/clojure/cljs/analyzer.cljc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index bb3c9ab61..9ea0fa436 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1209,7 +1209,7 @@
 (defn analyze-keyword
   [env sym]
   (register-constant! env sym)
-  {:op :const :env env :form sym :tag 'cljs.core/Keyword})
+  {:op :const :val sym :env env :form sym :tag 'cljs.core/Keyword})
 
 (defn get-tag [e]
   (if-some [tag (-> e :form meta :tag)]
@@ -3199,7 +3199,7 @@
   (if ^boolean (:quoted? env)
     (do
       (register-constant! env sym)
-      (analyze-wrap-meta {:op :const :env env :form sym :tag 'cljs.core/Symbol}))
+      (analyze-wrap-meta {:op :const :val sym :env env :form sym :tag 'cljs.core/Symbol}))
     (let [{:keys [line column]} (meta sym)
           env  (if-not (nil? line)
                  (assoc env :line line)
@@ -3591,7 +3591,7 @@
                    (instance? Character form) 'string
                    (true? form) 'boolean
                    (false? form) 'boolean)]
-         (cond-> {:op :const :env env :form form}
+         (cond-> {:op :const :val form :env env :form form}
            tag (assoc :tag tag))))))
 
 #?(:cljs
@@ -3613,7 +3613,7 @@
                    (string? form) STRING_SYM
                    (true? form) BOOLEAN_SYM
                    (false? form) BOOLEAN_SYM)]
-         (cond-> {:op :const :env env :form form}
+         (cond-> {:op :const :val form :env env :form form}
            tag (assoc :tag tag))))))
 
 (defn analyze* [env form name opts]

From 5e4260360d209a50c004cd524317c76e7e0b68e7 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 22 Jun 2018 16:48:44 -0400
Subject: [PATCH 3201/4033] CLJS-2787: Record comparison is broken when
 instance is constructed from another record instance via map factory

If map->Foo argument is a record, pour the extension map contents into
a plain map
---
 src/main/clojure/cljs/core.cljc   | 4 +++-
 src/test/cljs/cljs/core_test.cljs | 7 +++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 24a64cdd7..6052b496f 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -1887,7 +1887,9 @@
              ks (map keyword fields)
              getters (map (core/fn [k] `(~k ~ms)) ks)]
     `(defn ~fn-name ~docstring [~ms]
-       (new ~rname ~@getters nil (not-empty (dissoc ~ms ~@ks)) nil))))
+       (let [extmap# (cond->> (dissoc ~ms ~@ks)
+                        (record? ~ms) (into {}))]
+         (new ~rname ~@getters nil (not-empty extmap#) nil)))))
 
 (core/defmacro defrecord
   "(defrecord name [fields*]  options* specs*)
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index fc475a0ae..7c7cb6c30 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -1586,3 +1586,10 @@
     (is (uri? (goog.Uri. "")))
     (is (uri? (goog.Uri. "http://clojurescript.org")))
     (is (uri? (goog.Uri. "some string")))))
+
+(defrecord CLJS-2787 [])
+
+(deftest test-cljs-2787
+  (let [x (map->CLJS-2787 {1 2})
+        y (map->CLJS-2787 x)]
+    (= x y)))

From e58b4352e59131a17d33c27239403e4630b28295 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 22 Jun 2018 16:54:07 -0400
Subject: [PATCH 3202/4033] 1.10.328

---
 README.md  |  6 +++---
 changes.md | 18 ++++++++++++++++++
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 0b4dba522..7566d1bb3 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,14 @@ Official web site: http://clojurescript.org
 
 ## Releases and dependency information ##
 
-Latest stable release: 1.10.312
+Latest stable release: 1.10.328
 
 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22)
 
 [Leiningen](http://github.com/technomancy/leiningen/) dependency information:
 
 ```
-[org.clojure/clojurescript "1.10.312"]
+[org.clojure/clojurescript "1.10.328"]
 ```
 
 [Maven](http://maven.apache.org) dependency information:
@@ -22,7 +22,7 @@ Latest stable release: 1.10.312
 
   org.clojure
   clojurescript
-  1.10.312
+  1.10.328
 
 ```
 
diff --git a/changes.md b/changes.md
index 6a91b81e4..f5b9cfa73 100644
--- a/changes.md
+++ b/changes.md
@@ -1,3 +1,21 @@
+## 1.10.328
+
+### Changes
+* add :val to :const node
+* rename ast op :constant -> :const
+
+### Fixes
+* CLJS-2787: Record comparison is broken when instance is constructed from another record instance via map factory
+* CLJS-2780: Async tests prematurely terminate in Node
+* CLJS-2783: with-out-str conflicts with :infer-externs
+* CLJS-2730: Fix docstrings in filter, filtev, remove, and take-while
+* CLJS-2703: module name substitution test fails if hypen in directory path
+* CLJS-2731: Failure comparing sorted sets
+* CLJS-2746: Missing provides for JS modules
+* CLJS-2772: Trying to run `cljs.main` repl with `:modules` results in `brepl_deps.js` being `clojure.lang.LazySeq`
+* CLJS-2736: Elements returned from sets as functions are not the actual elements in the set
+* CLJS-2298: REPLs should automatically load user.(cljs|cljc) files at root of Java classpath
+
 ## 1.10.312
 
 ### Enhancements

From 359d34ef57a436c05658a114f9f685c85e28d766 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Fri, 22 Jun 2018 17:00:59 -0400
Subject: [PATCH 3203/4033] 1.10.329

---
 README.md                         | 6 +++---
 changes.md                        | 2 +-
 src/test/cljs/cljs/core_test.cljs | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index 7566d1bb3..a2a1a6ebc 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,14 @@ Official web site: http://clojurescript.org
 
 ## Releases and dependency information ##
 
-Latest stable release: 1.10.328
+Latest stable release: 1.10.329
 
 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22)
 
 [Leiningen](http://github.com/technomancy/leiningen/) dependency information:
 
 ```
-[org.clojure/clojurescript "1.10.328"]
+[org.clojure/clojurescript "1.10.329"]
 ```
 
 [Maven](http://maven.apache.org) dependency information:
@@ -22,7 +22,7 @@ Latest stable release: 1.10.328
 
   org.clojure
   clojurescript
-  1.10.328
+  1.10.329
 
 ```
 
diff --git a/changes.md b/changes.md
index f5b9cfa73..5dda3b2e2 100644
--- a/changes.md
+++ b/changes.md
@@ -1,4 +1,4 @@
-## 1.10.328
+## 1.10.329
 
 ### Changes
 * add :val to :const node
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index 7c7cb6c30..5bbee0a70 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -1592,4 +1592,4 @@
 (deftest test-cljs-2787
   (let [x (map->CLJS-2787 {1 2})
         y (map->CLJS-2787 x)]
-    (= x y)))
+    (is (= x y))))

From 93496676d3f8c920184851967f78d38547b022a0 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 18:23:53 -0400
Subject: [PATCH 3204/4033] rename :var-special op to :the-var

---
 src/main/clojure/cljs/analyzer.cljc | 2 +-
 src/main/clojure/cljs/compiler.cljc | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 9ea0fa436..1b4e42d2e 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1364,7 +1364,7 @@
   [op env [_ sym :as form] _ _]
   (merge
     {:env env
-     :op :var-special
+     :op :the-var
      :form form}
     (var-ast env sym)))
 
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 98dfa51e3..a91a685d6 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -398,7 +398,7 @@
 
                 (emits info)))))))))
 
-(defmethod emit* :var-special
+(defmethod emit* :the-var
   [{:keys [env var sym meta] :as arg}]
   {:pre [(ana/ast? sym) (ana/ast? meta)]}
   (let [{:keys [name]} (:info var)]
@@ -705,7 +705,7 @@
      (when (:def-emits-var env)
        (emitln "; return (")
        (emits (merge
-                {:op  :var-special
+                {:op  :the-var
                  :env (assoc env :context :expr)}
                 var-ast))
        (emitln ");})()"))

From 62f967b6266f71bdf21926993536ceaf69a33180 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 20:28:51 -0400
Subject: [PATCH 3205/4033] rename :deftype* op to :deftype

---
 src/main/clojure/cljs/analyzer.cljc | 2 +-
 src/main/clojure/cljs/compiler.cljc | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 1b4e42d2e..86d978234 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -2845,7 +2845,7 @@
 
 (defmethod parse 'deftype*
   [_ env form _ _]
-  (parse-type :deftype* env form))
+  (parse-type :deftype env form))
 
 (defmethod parse 'defrecord*
   [_ env form _ _]
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index a91a685d6..ee53113b2 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1200,7 +1200,7 @@
   (load-libs requires nil (:require reloads) deps name)
   (load-libs uses requires (:use reloads) deps name))
 
-(defmethod emit* :deftype*
+(defmethod emit* :deftype
   [{:keys [t fields pmasks body protocols]}]
   (let [fields (map munge fields)]
     (emitln "")

From 70910c2127808a8ee1c11f43d8da7c2ab0e1d8b8 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 21:28:17 -0400
Subject: [PATCH 3206/4033] rename :defrecord* op to :defrecord

---
 src/main/clojure/cljs/analyzer.cljc | 6 +++---
 src/main/clojure/cljs/compiler.cljc | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 86d978234..7b8db3ae5 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -2822,7 +2822,7 @@
                                  :volatile-mutable (-> fld meta :volatile-mutable)
                                  :tag (-> fld meta :tag)
                                  :shadow (m fld)}))
-                       {} (if (= :defrecord* op)
+                       {} (if (= :defrecord op)
                             (concat fields '[__meta __extmap ^:mutable __hash])
                             fields))
         protocols (-> tsym meta :protocols)]
@@ -2833,7 +2833,7 @@
                        :tag 'function
                        :type true
                        :num-fields (count fields)
-                       :record (= :defrecord* op))]
+                       :record (= :defrecord op))]
                (merge m
                       (dissoc (meta tsym) :protocols)
                       {:protocols protocols}
@@ -2849,7 +2849,7 @@
 
 (defmethod parse 'defrecord*
   [_ env form _ _]
-  (parse-type :defrecord* env form) )
+  (parse-type :defrecord env form) )
 
 ;; dot accessor code
 
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index ee53113b2..f26f5d125 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1217,7 +1217,7 @@
     (emitln "});")
     (emit body)))
 
-(defmethod emit* :defrecord*
+(defmethod emit* :defrecord
   [{:keys [t fields pmasks body protocols]}]
   (let [fields (concat (map munge fields) '[__meta __extmap __hash])]
     (emitln "")

From a7c0899e073b0f8427e293d0fcb677f33c8d3ea9 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 21:29:35 -0400
Subject: [PATCH 3207/4033] rename :meta op to :with-meta

---
 src/main/clojure/cljs/analyzer.cljc | 2 +-
 src/main/clojure/cljs/compiler.cljc | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 7b8db3ae5..f54b69dd6 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -3486,7 +3486,7 @@
       (let [env (:env expr) ; take on expr's context ourselves
             expr (assoc-in expr [:env :context] :expr) ; change expr to :expr
             meta-expr (analyze-map (:env expr) m)]
-        {:op :meta :env env :form form
+        {:op :with-meta :env env :form form
          :meta meta-expr :expr expr :children [meta-expr expr]})
       expr)))
 
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index f26f5d125..380792bc7 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -406,7 +406,7 @@
       (emits "new cljs.core.Var(function(){return " (munge name) ";},"
         sym "," meta ")"))))
 
-(defmethod emit* :meta
+(defmethod emit* :with-meta
   [{:keys [expr meta env]}]
   (emit-wrap env
     (emits "cljs.core.with_meta(" expr "," meta ")")))

From 4f477f7bb6a8f93f22aded217a2da8020291ac9d Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 24 Jun 2018 20:46:12 -0400
Subject: [PATCH 3208/4033] bump transit in bootstrap and project.clj

---
 project.clj      | 2 +-
 script/bootstrap | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/project.clj b/project.clj
index c454afc0d..f40fd819d 100644
--- a/project.clj
+++ b/project.clj
@@ -14,7 +14,7 @@
                  [org.clojure/data.json "0.2.6"]
                  [org.clojure/tools.reader "1.3.0-alpha3"]
                  [org.clojure/test.check "0.10.0-alpha2" :scope "test"]
-                 [com.cognitect/transit-clj "0.8.300"]
+                 [com.cognitect/transit-clj "0.8.309"]
                  [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"]
                  [com.google.javascript/closure-compiler-unshaded "v20180610"]
                  [org.mozilla/rhino "1.7R5"]]
diff --git a/script/bootstrap b/script/bootstrap
index 86aa39981..482229c08 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -7,7 +7,7 @@ SPEC_ALPHA_RELEASE="0.1.143"
 CORE_SPECS_ALPHA_RELEASE="0.1.24"
 CLOSURE_RELEASE="20180610"
 DJSON_RELEASE="0.2.6"
-TRANSIT_RELEASE="0.8.300"
+TRANSIT_RELEASE="0.8.309"
 GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b"
 RHINO_RELEASE="1_7R5"
 TREADER_RELEASE="1.3.0-alpha3"

From ef3a22d1f068ced3bddcfe37e8ab9821198ce107 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Sun, 24 Jun 2018 20:48:30 -0400
Subject: [PATCH 3209/4033] 1.10.335

---
 README.md  | 6 +++---
 changes.md | 5 +++++
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index a2a1a6ebc..8d9836a18 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,14 @@ Official web site: http://clojurescript.org
 
 ## Releases and dependency information ##
 
-Latest stable release: 1.10.329
+Latest stable release: 1.10.335
 
 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22)
 
 [Leiningen](http://github.com/technomancy/leiningen/) dependency information:
 
 ```
-[org.clojure/clojurescript "1.10.329"]
+[org.clojure/clojurescript "1.10.335"]
 ```
 
 [Maven](http://maven.apache.org) dependency information:
@@ -22,7 +22,7 @@ Latest stable release: 1.10.329
 
   org.clojure
   clojurescript
-  1.10.329
+  1.10.335
 
 ```
 
diff --git a/changes.md b/changes.md
index 5dda3b2e2..d50f4e3a5 100644
--- a/changes.md
+++ b/changes.md
@@ -1,3 +1,8 @@
+## 1.10.335
+
+### Changes
+* Bump transit-clj to 0.8.309
+
 ## 1.10.329
 
 ### Changes

From 44379a6e7e92e9ebbbb9597549855c0aa45df855 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Mon, 25 Jun 2018 09:01:22 -0400
Subject: [PATCH 3210/4033] CLJS-2791: transit-cli should be bumped in
 pom.template.xml

---
 pom.template.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.template.xml b/pom.template.xml
index 0b8f29dd1..7a36e4ada 100644
--- a/pom.template.xml
+++ b/pom.template.xml
@@ -55,7 +55,7 @@
         
             com.cognitect
             transit-clj
-            0.8.300
+            0.8.309
             
                 
                     org.clojure

From d91207cb7386365a07b563b09b6444846657a364 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Mon, 25 Jun 2018 07:52:49 -0400
Subject: [PATCH 3211/4033] CLJS-2790: Externs inference warnings for defrecord
 fields

Applies the same solution to supress access to the other object's
__extmap in the IEquiv implementation (adding a type tag) to generated
accessors to the other object's fields.
---
 src/main/clojure/cljs/core.cljc          |  2 +-
 src/test/clojure/cljs/analyzer_tests.clj | 11 +++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 6052b496f..d999d0f4e 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -1816,7 +1816,7 @@
                                               (.-constructor ~other))
                                   ~@(map (core/fn [field]
                                            `(= (.. ~this ~(to-property field))
-                                               (.. ~other ~(to-property field))))
+                                               (.. ~(with-meta other {:tag tagname}) ~(to-property field))))
                                          base-fields)
                                   (= (.-__extmap ~this)
                                      (.-__extmap ~(with-meta other {:tag tagname}))))))
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index abdb1ae49..89f9fd8b5 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -1096,3 +1096,14 @@
                :with-core? true})]
     (is (empty? @ws))
     (is (not (string/includes? res "cljs.core")))))
+
+(deftest test-cljs-2790-defrecord-fields
+  (let [ws  (atom [])
+        res (infer-test-helper
+              {:forms '[(ns cjls-2790.core)
+                        (defrecord Foo [a b])]
+               :externs ["src/test/externs/test.js"]
+               :warnings ws
+               :with-core? true})]
+    (is (empty? @ws))
+    (is (not (string/includes? res "cljs.core")))))

From b2b13e370545d2eac661caab1a54b42a33d27ee9 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 25 Jun 2018 10:28:07 -0400
Subject: [PATCH 3212/4033] add transit-clj to deps.edn

---
 deps.edn | 1 +
 1 file changed, 1 insertion(+)

diff --git a/deps.edn b/deps.edn
index 4a5792f74..2f4241f00 100644
--- a/deps.edn
+++ b/deps.edn
@@ -6,6 +6,7 @@
   org.clojure/spec.alpha {:mvn/version "0.1.143"}
   org.clojure/core.specs.alpha {:mvn/version "0.1.24"}
   org.clojure/data.json {:mvn/version "0.2.6"}
+  com.cognitect/transit-clj {:mvn/version "0.8.309"}
   com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180610"}
   org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"}
   org.mozilla/rhino {:mvn/version "1.7R5"}}

From b1ade48e21f9e7f78d9db74559ce4dd5846d0c94 Mon Sep 17 00:00:00 2001
From: David Nolen 
Date: Mon, 25 Jun 2018 10:47:49 -0400
Subject: [PATCH 3213/4033] 1.10.339

---
 README.md  | 6 +++---
 changes.md | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 8d9836a18..73cb5b959 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,14 @@ Official web site: http://clojurescript.org
 
 ## Releases and dependency information ##
 
-Latest stable release: 1.10.335
+Latest stable release: 1.10.339
 
 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22)
 
 [Leiningen](http://github.com/technomancy/leiningen/) dependency information:
 
 ```
-[org.clojure/clojurescript "1.10.335"]
+[org.clojure/clojurescript "1.10.339"]
 ```
 
 [Maven](http://maven.apache.org) dependency information:
@@ -22,7 +22,7 @@ Latest stable release: 1.10.335
 
   org.clojure
   clojurescript
-  1.10.335
+  1.10.339
 
 ```
 
diff --git a/changes.md b/changes.md
index d50f4e3a5..3e582c67e 100644
--- a/changes.md
+++ b/changes.md
@@ -1,4 +1,4 @@
-## 1.10.335
+## 1.10.339
 
 ### Changes
 * Bump transit-clj to 0.8.309

From 9a8196ebfe4265feda88a06de84affb9df469012 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sun, 24 Jun 2018 15:39:49 -0400
Subject: [PATCH 3214/4033] CLJS-2442: `set` and `vec` performance enhancements

---
 src/main/cljs/cljs/core.cljs             | 37 ++++++++++++++++--------
 src/test/cljs/cljs/collections_test.cljs | 18 +++++++++++-
 2 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index eeb247d44..1beff8f5b 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -5644,12 +5644,23 @@ reduces them without incurring seq initialization"
 
 (es6-iterable PersistentVector)
 
+(declare map-entry?)
+
 (defn vec
   "Creates a new vector containing the contents of coll. JavaScript arrays
   will be aliased and should not be modified."
   [coll]
-  (if (array? coll)
+  (cond
+    (map-entry? coll)
+    [(key coll) (val coll)]
+
+    (vector? coll)
+    (with-meta coll nil)
+
+    (array? coll)
     (.fromArray PersistentVector coll true)
+
+    :else
     (-persistent!
       (reduce -conj!
         (-as-transient (.-EMPTY PersistentVector))
@@ -9304,19 +9315,21 @@ reduces them without incurring seq initialization"
 (defn set
   "Returns a set of the distinct elements of coll."
   [coll]
-  (let [in (seq coll)]
-    (cond
-      (nil? in) #{}
+  (if (set? coll)
+    (with-meta coll nil)
+    (let [in (seq coll)]
+      (cond
+        (nil? in) #{}
 
-      (and (instance? IndexedSeq in) (zero? (.-i in)))
-      (.createAsIfByAssoc PersistentHashSet (.-arr in))
+        (and (instance? IndexedSeq in) (zero? (.-i in)))
+        (.createAsIfByAssoc PersistentHashSet (.-arr in))
 
-      :else
-      (loop [^not-native in in
-             ^not-native out (-as-transient #{})]
-        (if-not (nil? in)
-          (recur (next in) (-conj! out (-first in)))
-          (persistent! out))))))
+        :else
+        (loop [^not-native in  in
+               ^not-native out (-as-transient #{})]
+          (if-not (nil? in)
+            (recur (next in) (-conj! out (-first in)))
+            (persistent! out)))))))
 
 (defn hash-set
   "Returns a new hash set with supplied keys.  Any equal keys are
diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs
index 236f546fd..ab229091c 100644
--- a/src/test/cljs/cljs/collections_test.cljs
+++ b/src/test/cljs/cljs/collections_test.cljs
@@ -1003,8 +1003,24 @@
   (let [s #{(with-meta [:a] {:n 42})}]
     (is (= {:n 42} (meta (s [:a]))))))
 
+(deftest test-cljs-2442
+  (testing "set ctor"
+    (let [coll #{1 2}]
+      (is (not (identical? coll (set coll)))))
+    (is (= #{1 2} (set #{1 2})))
+    (is (nil? (meta (set ^:a #{1 2})))))
+  (testing "vec ctor"
+    (let [coll [1 2]]
+      (is (not (identical? coll (vec coll)))))
+    (is (= [1 2] (vec [1 2])))
+    (is (nil? (meta (vec ^:a [1 2]))))
+    (let [coll (vec (first {:a 1}))]
+      (is (vector? coll))
+      (is (not (map-entry? coll)))
+      (is (= [:a 1] coll)))))
+
 (comment
 
   (run-tests)
 
-  )
\ No newline at end of file
+  )

From 19e5727e57889da54dc771a8a6bdeb27569010fa Mon Sep 17 00:00:00 2001
From: henryw374 
Date: Tue, 26 Jun 2018 09:29:32 +0100
Subject: [PATCH 3215/4033] CLJS-2589: allow / as a protocol method name in
 cljs

---
 src/main/clojure/cljs/core.cljc   |  4 ++--
 src/test/cljs/cljs/core_test.cljs | 10 ++++++++++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index d999d0f4e..2b6d223ac 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -1497,7 +1497,7 @@
       (ifn-invoke-methods type type-sym form))))
 
 (core/defn- add-proto-methods* [pprefix type type-sym [f & meths :as form]]
-  (core/let [pf (core/str pprefix (name f))]
+  (core/let [pf (core/str pprefix (munge (name f)))]
     (if (vector? (first meths))
       ;; single method case
       (core/let [meth meths]
@@ -2063,7 +2063,7 @@
                                        (cljs.analyzer/warning
                                         :protocol-with-variadic-method
                                         &env {:protocol psym :name fname}))
-                                 slot (symbol (core/str prefix (name fname)))
+                                 slot (symbol (core/str prefix (munge (name fname))))
                                  fname (vary-meta fname assoc
                                          :protocol p
                                          :doc doc)]
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index 5bbee0a70..bfda4aa88 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -621,6 +621,16 @@
   (is (= (meta (with-meta (reify IFoo (foo [this] :foo)) {:foo :bar}))
             {:foo :bar})))
 
+
+(defprotocol Slashy (/ [_]))
+
+(extend-type string
+       Slashy
+       (/ [_] "result"))
+
+(deftest test-protocol-with-slash
+  (is (=  "result" (/ ""))))
+
 (let [x "original"]
   (defn original-closure-stmt [] x))
 

From c98766379cd3dec1264755b866e582522539b1ef Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 23:20:10 -0400
Subject: [PATCH 3216/4033] add :ns entry to :def op

---
 src/main/clojure/cljs/analyzer.cljc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index f54b69dd6..b6b3e3529 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1644,6 +1644,7 @@
         {:env env
          :op :def
          :form form
+         :ns ns-name
          :name var-name
          :var (assoc
                 (analyze

From 0765fc73cb635d90ce0b68af292edf37c4d8126d Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 23:26:11 -0400
Subject: [PATCH 3217/4033] rename :throw entry to :exception in :throw op

---
 src/main/clojure/cljs/analyzer.cljc | 2 +-
 src/main/clojure/cljs/compiler.cljc | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index b6b3e3529..d1b51d67f 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1413,7 +1413,7 @@
       (error env "Too many arguments to throw, throw expects a single Error instance")))
   (let [throw-expr (disallowing-recur (analyze (assoc env :context :expr) throw-form))]
     {:env env :op :throw :form form
-     :throw throw-expr
+     :exception throw-expr
      :children [throw-expr]}))
 
 (defmethod parse 'try
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 380792bc7..141547f44 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -559,7 +559,7 @@
       (emitln "return " gs ";})()"))))
 
 (defmethod emit* :throw
-  [{:keys [throw env]}]
+  [{throw :exception :keys [env]}]
   (if (= :expr (:context env))
     (emits "(function(){throw " throw "})()")
     (emitln "throw " throw ";")))

From 3b4e87217bf8c5feefe1b600af84077055e42479 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 23:31:08 -0400
Subject: [PATCH 3218/4033] rename :try entry to :body in :try op

---
 src/main/clojure/cljs/analyzer.cljc | 2 +-
 src/main/clojure/cljs/compiler.cljc | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index d1b51d67f..96f4986db 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1471,7 +1471,7 @@
         try (disallowing-recur (analyze (if (or e finally) catchenv env) `(do ~@body)))]
 
     {:env env :op :try :form form
-     :try try
+     :body try
      :finally finally
      :name e
      :catch catch
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 141547f44..3301adf18 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -933,7 +933,7 @@
     (when (and statements (= :expr context)) (emitln "})()"))))
 
 (defmethod emit* :try
-  [{:keys [env try catch name finally]}]
+  [{try :body :keys [env catch name finally]}]
   (let [context (:context env)]
     (if (or name finally)
       (do

From c89f5e2b5309781e982702f76427bab97b7230f1 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 23:36:09 -0400
Subject: [PATCH 3219/4033] rename :expr entry to :body in :letfn op

---
 src/main/clojure/cljs/analyzer.cljc | 2 +-
 src/main/clojure/cljs/compiler.cljc | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 96f4986db..15b6aeab2 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1868,7 +1868,7 @@
                      (conj bes be')]))
           [meth-env []] bes)
         expr (analyze (assoc meth-env :context (if (= :expr context) :return context)) `(do ~@exprs))]
-    {:env env :op :letfn :bindings bes :expr expr :form form
+    {:env env :op :letfn :bindings bes :body expr :form form
      :children (conj (vec (map :init bes)) expr)}))
 
 (defn analyze-do-statements* [env exprs]
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 3301adf18..5ac0dc3f3 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -990,7 +990,7 @@
     (emitln "continue;")))
 
 (defmethod emit* :letfn
-  [{:keys [bindings expr env]}]
+  [{expr :body :keys [bindings env]}]
   (let [context (:context env)]
     (when (= :expr context) (emits "(function (){"))
     (doseq [{:keys [init] :as binding} bindings]

From 9cae8d2149cb03f57e40cc836fe1e10e044832e8 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 23:38:48 -0400
Subject: [PATCH 3220/4033] rename :expr entry to :body in :let/:loop op

---
 src/main/clojure/cljs/analyzer.cljc      | 6 +++---
 src/main/clojure/cljs/compiler.cljc      | 2 +-
 src/test/clojure/cljs/analyzer_tests.clj | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 15b6aeab2..0d9af507c 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1303,8 +1303,8 @@
       (case (:op e)
         :recur    IGNORE_SYM
         :throw    IGNORE_SYM
-        :let      (infer-tag env (:expr e))
-        :loop     (infer-tag env (:expr e))
+        :let      (infer-tag env (:body e))
+        :loop     (infer-tag env (:body e))
         :do       (infer-tag env (:ret e))
         :method   (infer-tag env (:expr e))
         :def      (infer-tag env (:init e))
@@ -1983,7 +1983,7 @@
     {:op op
      :env encl-env
      :bindings bes
-     :expr expr
+     :body expr
      :form form
      :children children}))
 
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 5ac0dc3f3..bcef9cf0f 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -950,7 +950,7 @@
       (emits try))))
 
 (defn emit-let
-  [{:keys [bindings expr env]} is-loop]
+  [{expr :body :keys [bindings env]} is-loop]
   (let [context (:context env)]
     (when (= :expr context) (emits "(function (){"))
     (binding [*lexical-renames*
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 89f9fd8b5..3bde9f689 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -642,7 +642,7 @@
   (let [parsed (e/with-compiler-env test-cenv
                  (a/analyze (assoc test-env :def-emits-var true)
                    '(let [y 1] (def y 2))))]
-    (is (some? (-> parsed :expr :ret :var-ast)))))
+    (is (some? (-> parsed :body :ret :var-ast)))))
 
 (deftest test-has-extern?-basic
   (let [externs (externs/externs-map

From c2b864d55e202323af767d7b1ec63f64eabc2399 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Sun, 24 Jun 2018 19:34:01 -0400
Subject: [PATCH 3221/4033] rename :f to :fn in :invoke op

---
 src/main/clojure/cljs/analyzer.cljc | 6 +++---
 src/main/clojure/cljs/compiler.cljc | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 0d9af507c..d0ee7d658 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1282,7 +1282,7 @@
               (into then-tag else-tag))))))))
 
 (defn infer-invoke [env e]
-  (let [{info :info :as f} (:f e)]
+  (let [{info :info :as f} (:fn e)]
     (if-some [ret-tag (if (or (true? (:fn-var info))
                               (true? (:js-fn-var info)))
                         (:ret-tag info)
@@ -3187,7 +3187,7 @@
                ~@(if bind-args? arg-syms args)))))
       (let [ana-expr #(analyze enve %)
             argexprs (map ana-expr args)]
-        {:env env :op :invoke :form form :f fexpr :args (vec argexprs)
+        {:env env :op :invoke :form form :fn fexpr :args (vec argexprs)
          :children (into [fexpr] argexprs)}))))
 
 (defn parse-invoke
@@ -3563,7 +3563,7 @@
   (when (and (not (analyzed? ast))
              #?(:clj  (= :invoke op)
                 :cljs (keyword-identical? :invoke op)))
-    (when-some [[name {:keys [valid? warning-type]}] (find invoke-arg-type-validators (-> ast :f :info :name))]
+    (when-some [[name {:keys [valid? warning-type]}] (find invoke-arg-type-validators (-> ast :fn :info :name))]
       (let [types (mapv :tag (:args ast))]
         (when-not (valid? types)
           (warning warning-type env
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index bcef9cf0f..df60fa3a8 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1005,7 +1005,7 @@
             "$")))
 
 (defmethod emit* :invoke
-  [{:keys [f args env] :as expr}]
+  [{f :fn :keys [args env] :as expr}]
   (let [info (:info f)
         fn? (and ana/*cljs-static-fns*
                  (not (:dynamic info))

From e173e54de9de5174601a35d22b8da33096af39d3 Mon Sep 17 00:00:00 2001
From: Thomas Spellman 
Date: Tue, 26 Jun 2018 23:25:20 +0200
Subject: [PATCH 3222/4033] CLJS-844: Optimize js->clj by switching to
 transients

---
 src/main/cljs/cljs/core.cljs | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 1beff8f5b..9b7152ec0 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -10595,15 +10595,17 @@ reduces them without incurring seq initialization"
                 (MapEntry. (thisfn (key x)) (thisfn (val x)) nil)
 
                 (coll? x)
-                (into (empty x) (map thisfn x))
+                (into (empty x) (map thisfn) x)
 
                 (array? x)
-                (vec (map thisfn x))
+                (persistent!
+                 (reduce #(conj! %1 (thisfn %2))
+                         (transient []) x))
 
                 (identical? (type x) js/Object)
-                (into {} (for [k (js-keys x)]
-                           [(keyfn k) (thisfn (unchecked-get x k))]))
-
+                (persistent!
+                 (reduce (fn [r k] (assoc! r (keyfn k) (thisfn (gobject/get x k))))
+                         (transient {}) (js-keys x)))
                 :else x))]
       (f x))))
 

From 892ed2029db42cc3728e6e99a37d99b4ec53ecc0 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sun, 1 Jul 2018 09:14:40 -0400
Subject: [PATCH 3223/4033] CLJS-2798: ChunkCons -next doesn't handle nil more

This revison aligns more with the Clojure implementation.

In the Clojure implemenentation, if _more is nil, then it would end up
doing a seq on an empty list, which would produce nil. This revision
simply pre-checks if more is nil, and if not, returns nil in that case.

The change also eliminates an unnecessary check of the return value of
-seq (checking for nil and returning nil), and just returns the value
-seq produces.
---
 src/main/cljs/cljs/core.cljs             | 5 ++---
 src/test/cljs/cljs/collections_test.cljs | 5 +++++
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 9b7152ec0..2e04da74e 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -3553,9 +3553,8 @@ reduces them without incurring seq initialization"
   (-next [coll]
     (if (> (-count chunk) 1)
       (ChunkedCons. (-drop-first chunk) more meta nil)
-      (let [more (-seq more)]
-        (when-not (nil? more)
-          more))))
+      (when-not (nil? more)
+        (-seq more))))
 
   IChunkedSeq
   (-chunked-first [coll] chunk)
diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs
index ab229091c..75dcada3f 100644
--- a/src/test/cljs/cljs/collections_test.cljs
+++ b/src/test/cljs/cljs/collections_test.cljs
@@ -1019,6 +1019,11 @@
       (is (not (map-entry? coll)))
       (is (= [:a 1] coll)))))
 
+(deftest test-cljs-2798
+  (is (nil? (let [b (chunk-buffer 1)]
+              (chunk-append b 0)
+              (next (chunk-cons (chunk b) nil))))))
+
 (comment
 
   (run-tests)

From e43c6f7cbe1a2b6cf782f930b12121e1e247538e Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sun, 1 Jul 2018 09:29:11 -0400
Subject: [PATCH 3224/4033] CLJS-2799: Handle nth on seqables with negative
 indexes

This adds a negative-index check to nth, prior to calling
linear-traversal-nth. (The check could concievably be done inside
linear-traversal-nth, but that would be less efficient as that function
recurs on itself and the check would then be made on each loop.)
---
 src/main/cljs/cljs/core.cljs      | 8 ++++++--
 src/test/cljs/cljs/core_test.cljs | 4 ++++
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 2e04da74e..27d3ef45e 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -1866,7 +1866,9 @@ reduces them without incurring seq initialization"
 
       (or (implements? ISeq coll)
           (implements? ISequential coll))
-      (linear-traversal-nth coll n)
+      (if (neg? n)
+        (throw (js/Error. "Index out of bounds"))
+        (linear-traversal-nth coll n))
 
       (native-satisfies? IIndexed coll)
       (-nth coll n)
@@ -1897,7 +1899,9 @@ reduces them without incurring seq initialization"
 
       (or (implements? ISeq coll)
           (implements? ISequential coll))
-      (linear-traversal-nth coll n not-found)
+      (if (neg? n)
+        not-found
+        (linear-traversal-nth coll n not-found))
 
       (native-satisfies? IIndexed coll)
       (-nth coll n not-found)
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index bfda4aa88..6c30b6593 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -1566,6 +1566,10 @@
   (is (thrown-with-msg? js/Error #".*Invalid arity: 0" ({})))
   (is (thrown-with-msg? js/Error #".*Invalid arity: 3" ({} 1 2 3))))
 
+(deftest test-cljs-2799
+  (is (thrown? js/Error (nth (repeat :x) -1)))
+  (is (= ::not-found (nth (repeat :x) -1 ::not-found))))
+
 (comment
   ;; ObjMap
   ;; (let [ks (map (partial str "foo") (range 500))

From 6c8e69cda2425840bacf8f82e2c6e102c8de9fdd Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Sun, 24 Jun 2018 20:10:07 -0400
Subject: [PATCH 3225/4033] CLJS-2797: Prepare :method, :dot, :js-value, and
 :case* for tools.analyzer AST conversion

rename :method op to :fn-method

rename :expr entry to :body in :fn-method op

rename :max-fixed-arity to :fixed-arity in :fn-method op

rename :variadic to :variadic? in :fn-method op

split :dot op into :host-field/:host-call

split :js-value op into :js-object/:js-array

rename :case* op to :case

rename :v to :test in :case op

add :case-node grouping with :case-{test,then} in :case op
---
 src/main/clojure/cljs/analyzer.cljc | 100 ++++++++++++++++++----------
 src/main/clojure/cljs/compiler.cljc |  61 ++++++++++-------
 src/main/clojure/cljs/core.cljc     |   6 +-
 3 files changed, 103 insertions(+), 64 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index d0ee7d658..c29d33056 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1224,8 +1224,8 @@
         c       (count params)]
     (some
       (fn [m]
-        (and (or (== (:max-fixed-arity m) c)
-                 (:variadic m))
+        (and (or (== (:fixed-arity m) c)
+                 (:variadic? m))
              m))
       methods)))
 
@@ -1289,7 +1289,7 @@
                         (when (= 'js (:ns info)) 'js))]
       ret-tag
       (let [args (:args e)
-            me (assoc (find-matching-method f args) :op :method)]
+            me (assoc (find-matching-method f args) :op :fn-method)]
         (if-some [ret-tag (infer-tag env me)]
           ret-tag
           ANY_SYM)))))
@@ -1306,7 +1306,7 @@
         :let      (infer-tag env (:body e))
         :loop     (infer-tag env (:body e))
         :do       (infer-tag env (:ret e))
-        :method   (infer-tag env (:expr e))
+        :fn-method (infer-tag env (:body e))
         :def      (infer-tag env (:init e))
         :invoke   (infer-invoke env e)
         :if       (infer-if env e)
@@ -1317,7 +1317,7 @@
         :var      (if-some [init (:init e)]
                     (infer-tag env init)
                     (infer-tag env (:info e)))
-        :dot      ANY_SYM
+        (:host-field :host-call)      ANY_SYM
         :js       ANY_SYM
         nil)))
 
@@ -1390,6 +1390,25 @@
         v        (disallowing-recur (analyze expr-env sym))
         tests    (mapv #(mapv (fn [t] (analyze expr-env t)) %) tests)
         thens    (mapv #(analyze env %) thens)
+        nodes    (mapv (fn [tests then]
+                         {:op :case-node
+                          ;synthetic node, no :form
+                          :env env
+                          :tests (mapv (fn [test]
+                                         {:op :case-test
+                                          :form (:form test)
+                                          :env expr-env
+                                          :test test
+                                          :children [test]})
+                                       tests)
+                          :then {:op :case-then
+                                 :form (:form then)
+                                 :env env
+                                 :then then
+                                 :children [then]}
+                          :children (conj tests then)})
+                       tests
+                       thens)
         default  (analyze env default)]
     (assert (every? (fn [t]
                       (or
@@ -1398,9 +1417,9 @@
                              ((some-fn number? string? char?) (:form t)))))
               (apply concat tests))
       "case* tests must be numbers, strings, or constants")
-    {:env env :op :case* :form form
-     :v v :tests tests :thens thens :default default
-     :children (vec (concat [v] tests thens (if default [default])))}))
+    {:env env :op :case :form form
+     :test v :nodes nodes :default default
+     :children (vec (concat [v] nodes [default]))}))
 
 (defmethod parse 'throw
   [op env [_ throw-form :as form] name _]
@@ -1627,7 +1646,7 @@
                    :protocol-inline (:protocol-inline init-expr)}
                   (if-some [top-fn-meta (:top-fn sym-meta)]
                     top-fn-meta
-                    {:variadic (:variadic init-expr)
+                    {:variadic? (:variadic? init-expr)
                      :max-fixed-arity (:max-fixed-arity init-expr)
                      :method-params params
                      :arglists (:arglists sym-meta)
@@ -1720,12 +1739,13 @@
                           (analyze-fn-method-body body-env body-form recur-frames))
         recurs          @(:flag recur-frame)]
     {:env env
-     :variadic variadic
+     :op :fn-method
+     :variadic? variadic
      :params params
-     :max-fixed-arity fixed-arity
+     :fixed-arity fixed-arity
      :type type
      :form form
-     :expr expr
+     :body expr
      :recurs recurs}))
 
 (declare analyze-wrap-meta)
@@ -1783,13 +1803,13 @@
                        {:protocol-impl proto-impl
                         :protocol-inline proto-inline})
         methods      (map #(disallowing-ns* (analyze-fn-method menv locals % type (nil? name))) meths)
-        mfa          (apply max (map :max-fixed-arity methods))
-        variadic     (boolean (some :variadic methods))
+        mfa          (apply max (map :fixed-arity methods))
+        variadic     (boolean (some :variadic? methods))
         locals       (if named-fn?
                        (update-in locals [name] assoc
                          ;; TODO: can we simplify? - David
                          :fn-var true
-                         :variadic variadic
+                         :variadic? variadic
                          :max-fixed-arity mfa
                          :method-params (map :params methods))
                        locals)
@@ -1801,13 +1821,13 @@
         form         (vary-meta form dissoc ::protocol-impl ::protocol-inline ::type)
         js-doc       (when (true? variadic)
                        "@param {...*} var_args")
-        children     (mapv :expr methods)
+        children     (mapv :body methods)
         ast          {:op :fn
                       :env env
                       :form form
                       :name name-var
                       :methods methods
-                      :variadic variadic
+                      :variadic? variadic
                       :tag 'function
                       :recur-frames *recur-frames*
                       :loop-lets *loop-lets*
@@ -1816,7 +1836,7 @@
                       :protocol-impl proto-impl
                       :protocol-inline proto-inline
                       :children children}]
-    (let [variadic-methods (filter :variadic methods)
+    (let [variadic-methods (filter :variadic? methods)
           variadic-params  (count (:params (first variadic-methods)))
           param-counts     (map (comp count :params) methods)]
       (when (< 1 (count variadic-methods))
@@ -1846,7 +1866,7 @@
                               :column (get-col n env)
                               :local true
                               :shadow (locals n)
-                              :variadic (:variadic fexpr)
+                              :variadic? (:variadic? fexpr)
                               :max-fixed-arity (:max-fixed-arity fexpr)
                               :method-params (map :params (:methods fexpr))}
                              ret-tag (assoc :ret-tag ret-tag))]
@@ -1861,7 +1881,7 @@
                         fexpr (analyze env (n->fexpr name))
                         be' (assoc be
                               :init fexpr
-                              :variadic (:variadic fexpr)
+                              :variadic? (:variadic? fexpr)
                               :max-fixed-arity (:max-fixed-arity fexpr)
                               :method-params (map :params (:methods fexpr)))]
                     [(assoc-in env [:locals name] be')
@@ -1943,7 +1963,7 @@
                      ;; TODO: can we simplify - David
                      (merge be
                        {:fn-var true
-                        :variadic (:variadic init-expr)
+                        :variadic? (:variadic? init-expr)
                         :max-fixed-arity (:max-fixed-arity init-expr)
                         :method-params (map :params (:methods init-expr))})
                      be)]
@@ -2958,7 +2978,7 @@
             (into [::namespaces (-> env :ns :name) :externs] pre) merge {}))))
     (case dot-action
       ::access (let [children [targetexpr]]
-                 {:op :dot
+                 {:op :host-field
                   :env env
                   :form form
                   :target targetexpr
@@ -2969,7 +2989,7 @@
                          tag)})
       ::call   (let [argexprs (map #(analyze enve %) args)
                      children (into [targetexpr] argexprs)]
-                 {:op :dot
+                 {:op :host-call
                   :env env
                   :form form
                   :target targetexpr
@@ -3155,7 +3175,7 @@
         bind-args? (and HO-invoke?
                         (not (all-values? args)))]
     (when ^boolean fn-var?
-      (let [{:keys [^boolean variadic max-fixed-arity method-params name ns macro]} (:info fexpr)]
+      (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
         ;; that requires itself, as that code is not meant to be executed in the
         ;; `$macros` ns - António Monteiro
@@ -3442,18 +3462,26 @@
 (defn analyze-js-value
   [env ^JSValue form]
   (let [val (.-val form)
-        expr-env (assoc env :context :expr)
-        items (if (map? val)
-                (zipmap (keys val)
-                        (disallowing-recur (doall (map #(analyze expr-env %) (vals val)))))
-                (disallowing-recur (doall (map #(analyze expr-env %) val))))]
-    {:op :js-value
-     :js-type (if (map? val) :object :array)
-     :env env
-     :form form
-     :items items
-     :children items
-     :tag (if (map? val) 'object 'array)}))
+        expr-env (assoc env :context :expr)]
+    (if (map? val)
+      (let [keys (vec (keys val))
+            vals (disallowing-recur
+                   (mapv #(analyze expr-env %) (vals val)))]
+        {:op :js-object
+         :env env
+         :form form
+         :keys keys
+         :vals vals
+         :children vals
+         :tag 'object})
+      (let [items (disallowing-recur
+                    (mapv #(analyze expr-env %) val))]
+        {:op :js-array
+         :env env
+         :form form
+         :items items
+         :children items
+         :tag 'array}))))
 
 (defn analyze-record
   [env x]
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index df60fa3a8..f98f726b5 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -475,19 +475,27 @@
 
       :else (emits "cljs.core.PersistentHashSet.createAsIfByAssoc([" (comma-sep items) "])"))))
 
-(defmethod emit* :js-value
-  [{:keys [items js-type env]}]
+(defn emit-js-object [items]
+  (emits "({")
+  (when-let [items (seq items)]
+    (let [[[k v] & r] items]
+      (emits "\"" (name k) "\": " v)
+      (doseq [[k v] r]
+        (emits ", \"" (name k) "\": " v))))
+  (emits "})"))
+
+(defn emit-js-array [items]
+  (emits "[" (comma-sep items) "]"))
+
+(defmethod emit* :js-object 
+  [{:keys [keys vals env]}]
   (emit-wrap env
-    (if (= js-type :object)
-      (do
-        (emits "({")
-        (when-let [items (seq items)]
-          (let [[[k v] & r] items]
-            (emits "\"" (name k) "\": " v)
-            (doseq [[k v] r]
-              (emits ", \"" (name k) "\": " v))))
-        (emits "})"))
-      (emits "[" (comma-sep items) "]"))))
+    (emit-js-object (map vector keys vals))))
+
+(defmethod emit* :js-array 
+  [{:keys [items env]}]
+  (emit-wrap env
+    (emit-js-array items)))
 
 (defmethod emit* :record-value
   [{:keys [items ns name items env]}]
@@ -534,16 +542,16 @@
           (emitln then "} else {")
           (emitln else "}"))))))
 
-(defmethod emit* :case*
-  [{:keys [v tests thens default env]}]
+(defmethod emit* :case
+  [{v :test :keys [nodes default env]}]
   (when (= (:context env) :expr)
     (emitln "(function(){"))
   (let [gs (gensym "caseval__")]
     (when (= :expr (:context env))
       (emitln "var " gs ";"))
     (emitln "switch (" v ") {")
-    (doseq [[ts then] (partition 2 (interleave tests thens))]
-      (doseq [test ts]
+    (doseq [{ts :tests {:keys [then]} :then} nodes]
+      (doseq [test (map :test ts)]
         (emitln "case " test ":"))
       (if (= :expr (:context env))
         (emitln gs "=" then)
@@ -765,7 +773,7 @@
       (emits ","))))
 
 (defn emit-fn-method
-  [{:keys [type name variadic params expr env recurs max-fixed-arity]}]
+  [{expr :body :keys [type name params env recurs]}]
   (emit-wrap env
     (emits "(function " (munge name) "(")
     (emit-fn-params params)
@@ -794,7 +802,7 @@
     a))
 
 (defn emit-variadic-fn-method
-  [{:keys [type name variadic params expr env recurs max-fixed-arity] :as f}]
+  [{expr :body max-fixed-arity :fixed-arity variadic :variadic? :keys [type name params env recurs] :as f}]
   (emit-wrap env
     (let [name (or name (gensym))
           mname (munge name)
@@ -844,7 +852,7 @@
       (emitln "})()"))))
 
 (defmethod emit* :fn
-  [{:keys [name env methods max-fixed-arity variadic recur-frames loop-lets]}]
+  [{variadic :variadic? :keys [name env methods max-fixed-arity recur-frames loop-lets]}]
   ;;fn statements get erased, serve no purpose and can pollute scope if named
   (when-not (= :statement (:context env))
     (let [loop-locals (->> (concat (mapcat :params (filter #(and % @(:flag %)) recur-frames))
@@ -876,7 +884,7 @@
           (emitln "var " mname " = null;")
           (doseq [[n meth] ms]
             (emits "var " n " = ")
-            (if (:variadic meth)
+            (if (:variadic? meth)
               (emit-variadic-fn-method meth)
               (emit-fn-method meth))
             (emitln ";"))
@@ -889,7 +897,7 @@
             (emitln " = var_args;"))
           (emitln "switch(arguments.length){")
           (doseq [[n meth] ms]
-            (if (:variadic meth)
+            (if (:variadic? meth)
               (do (emitln "default:")
                   (let [restarg (munge (gensym))]
                     (emitln "var " restarg " = null;")
@@ -913,10 +921,10 @@
           (emitln "};")
           (when variadic
             (emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";")
-            (emitln mname ".cljs$lang$applyTo = " (some #(let [[n m] %] (when (:variadic m) n)) ms) ".cljs$lang$applyTo;"))
+            (emitln mname ".cljs$lang$applyTo = " (some #(let [[n m] %] (when (:variadic? m) n)) ms) ".cljs$lang$applyTo;"))
           (doseq [[n meth] ms]
             (let [c (count (:params meth))]
-              (if (:variadic meth)
+              (if (:variadic? meth)
                 (emitln mname ".cljs$core$IFn$_invoke$arity$variadic = " n ".cljs$core$IFn$_invoke$arity$variadic;")
                 (emitln mname ".cljs$core$IFn$_invoke$arity$" c " = " n ";"))))
           (emitln "return " mname ";")
@@ -1039,7 +1047,7 @@
         [f variadic-invoke]
         (if fn?
           (let [arity (count args)
-                variadic? (:variadic info)
+                variadic? (:variadic? info)
                 mps (:method-params info)
                 mfa (:max-fixed-arity info)]
             (cond
@@ -1234,7 +1242,7 @@
     (emitln "});")
     (emit body)))
 
-(defmethod emit* :dot
+(defn emit-dot
   [{:keys [target field method args env]}]
   (emit-wrap env
     (if field
@@ -1243,6 +1251,9 @@
         (comma-sep args)
         ")"))))
 
+(defmethod emit* :host-field [ast] (emit-dot ast))
+(defmethod emit* :host-call [ast] (emit-dot ast))
+
 (defmethod emit* :js
   [{:keys [op env code segs args]}]
   (if (and code #?(:clj  (.startsWith ^String (string/trim code) "/*")
diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 2b6d223ac..34b1339a0 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -845,7 +845,7 @@
 
 (core/defn- simple-test-expr? [env ast]
   (core/and
-    (#{:var :invoke :const :dot :js} (:op ast))
+    (#{:var :invoke :const :host-field :host-call :js} (:op ast))
     ('#{boolean seq} (cljs.analyzer/infer-tag env ast))))
 
 (core/defmacro and
@@ -3065,7 +3065,7 @@
                c-1   (core/dec (count sig))
                meta  (assoc meta
                        :top-fn
-                       {:variadic true
+                       {:variadic? true
                         :max-fixed-arity c-1
                         :method-params [sig]
                         :arglists (core/list arglist)
@@ -3119,7 +3119,7 @@
                             [(core/- (count (first (filter varsig? arglists))) 2)]))
                meta     (assoc meta
                           :top-fn
-                          {:variadic variadic
+                          {:variadic? variadic
                            :max-fixed-arity maxfa
                            :method-params sigs
                            :arglists arglists

From ea53fd0c4675a64ac070caa4f51156167f710cd2 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 29 Jun 2018 17:56:42 -0400
Subject: [PATCH 3226/4033] CLJS-2800: Use tools.analyzer style :children

rename :ctor to :class in :new op

use tools.analyzer-style :children
---
 src/main/clojure/cljs/analyzer.cljc      | 136 ++++++++++++++---------
 src/main/clojure/cljs/analyzer/api.cljc  |   3 +-
 src/main/clojure/cljs/analyzer/utils.clj |   2 +-
 src/main/clojure/cljs/compiler.cljc      |   6 +-
 src/test/clojure/cljs/compiler_tests.clj |   4 +-
 5 files changed, 89 insertions(+), 62 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index c29d33056..460bf83b3 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1365,6 +1365,7 @@
   (merge
     {:env env
      :op :the-var
+     :children [:var :sym :meta]
      :form form}
     (var-ast env sym)))
 
@@ -1380,7 +1381,7 @@
     {:env env :op :if :form form
      :test test-expr :then then-expr :else else-expr
      :unchecked *unchecked-if*
-     :children [test-expr then-expr else-expr]}))
+     :children [:test :then :else]}))
 
 (defmethod parse 'case*
   [op env [_ sym tests thens default :as form] name _]
@@ -1399,14 +1400,14 @@
                                           :form (:form test)
                                           :env expr-env
                                           :test test
-                                          :children [test]})
+                                          :children [:test]})
                                        tests)
                           :then {:op :case-then
                                  :form (:form then)
                                  :env env
                                  :then then
-                                 :children [then]}
-                          :children (conj tests then)})
+                                 :children [:then]}
+                          :children [:tests :then]})
                        tests
                        thens)
         default  (analyze env default)]
@@ -1419,7 +1420,7 @@
       "case* tests must be numbers, strings, or constants")
     {:env env :op :case :form form
      :test v :nodes nodes :default default
-     :children (vec (concat [v] nodes [default]))}))
+     :children [:test :nodes :default]}))
 
 (defmethod parse 'throw
   [op env [_ throw-form :as form] name _]
@@ -1433,7 +1434,7 @@
   (let [throw-expr (disallowing-recur (analyze (assoc env :context :expr) throw-form))]
     {:env env :op :throw :form form
      :exception throw-expr
-     :children [throw-expr]}))
+     :children [:exception]}))
 
 (defmethod parse 'try
   [op env [_ & body :as form] name _]
@@ -1494,7 +1495,12 @@
      :finally finally
      :name e
      :catch catch
-     :children [try catch finally]}))
+     :children (vec
+                 (concat [:body]
+                         (when catch
+                           [:catch])
+                         (when finally
+                           [:finally])))}))
 
 (defn valid-proto [x]
   (when (symbol? x) x))
@@ -1506,11 +1512,19 @@
   (fn [env ast opts]
     (assoc ast :env new-env)))
 
+(defn ast-children [ast]
+  (mapcat (fn [c]
+            (let [g (get ast c)]
+              (cond
+                (vector? g) g
+                g [g])))
+          (:children ast)))
+
 (defn constant-value?
   [{:keys [op] :as ast}]
   (or (= :const op)
       (and (#{:map :set :vector :list} op)
-           (every? constant-value? (:children ast)))))
+           (every? constant-value? (ast-children ast)))))
 
 (defmethod parse 'def
   [op env form _ _]
@@ -1673,8 +1687,7 @@
                   sym)
                 :op :var)
          :doc doc
-         :jsdoc (:jsdoc sym-meta)
-         :init init-expr}
+         :jsdoc (:jsdoc sym-meta)}
         (when (true? (:def-emits-var env))
           {:var-ast (var-ast env sym)})
         (when-some [test (:test sym-meta)]
@@ -1685,7 +1698,10 @@
             {:tag tag}))
         (when (true? dynamic) {:dynamic true})
         (when (some? export-as) {:export export-as})
-        (when (some? init-expr) {:children [init-expr]})))))
+        (if (some? init-expr)
+          {:init init-expr
+           :children [:var :init]}
+          {:children [:var]})))))
 
 (defn analyze-fn-method-param [env]
   (fn [[locals params] name]
@@ -1738,15 +1754,19 @@
         expr            (when analyze-body?
                           (analyze-fn-method-body body-env body-form recur-frames))
         recurs          @(:flag recur-frame)]
-    {:env env
-     :op :fn-method
-     :variadic? variadic
-     :params params
-     :fixed-arity fixed-arity
-     :type type
-     :form form
-     :body expr
-     :recurs recurs}))
+    (merge
+      {:env env
+       :op :fn-method
+       :variadic? variadic
+       :params params
+       :fixed-arity fixed-arity
+       :type type
+       :form form
+       :recurs recurs}
+      (if (some? expr)
+        {:body expr
+         :children [:params :body]}
+        {:children [:params]}))))
 
 (declare analyze-wrap-meta)
 
@@ -1768,7 +1788,7 @@
       (merge name-var ret-tag))))
 
 (defn analyze-fn-methods-pass2* [menv locals type meths]
-  (doall (map #(analyze-fn-method menv locals % type true) meths)))
+  (mapv #(analyze-fn-method menv locals % type true) meths))
 
 (defn analyze-fn-methods-pass2 [menv locals type meths]
   (analyze-fn-methods-pass2* menv locals type meths))
@@ -1817,12 +1837,14 @@
                        ;; a second pass with knowledge of our function-ness/arity
                        ;; lets us optimize self calls
                        (disallowing-ns* (analyze-fn-methods-pass2 menv locals type meths))
-                       methods)
+                       (vec methods))
         form         (vary-meta form dissoc ::protocol-impl ::protocol-inline ::type)
         js-doc       (when (true? variadic)
                        "@param {...*} var_args")
-        children     (mapv :body methods)
-        ast          {:op :fn
+        children     (if (some? name-var)
+                       [:local :methods]
+                       [:methods])
+        ast   (merge {:op :fn
                       :env env
                       :form form
                       :name name-var
@@ -1835,7 +1857,9 @@
                       :max-fixed-arity mfa
                       :protocol-impl proto-impl
                       :protocol-inline proto-inline
-                      :children children}]
+                      :children children}
+                     (when (some? name-var)
+                       {:local name-var}))]
     (let [variadic-methods (filter :variadic? methods)
           variadic-params  (count (:params (first variadic-methods)))
           param-counts     (map (comp count :params) methods)]
@@ -1889,10 +1913,10 @@
           [meth-env []] bes)
         expr (analyze (assoc meth-env :context (if (= :expr context) :return context)) `(do ~@exprs))]
     {:env env :op :letfn :bindings bes :body expr :form form
-     :children (conj (vec (map :init bes)) expr)}))
+     :children [:bindings :body]}))
 
 (defn analyze-do-statements* [env exprs]
-  (seq (doall (map #(analyze (assoc env :context :statement) %) (butlast exprs)))))
+  (mapv #(analyze (assoc env :context :statement) %) (butlast exprs)))
 
 (defn analyze-do-statements [env exprs]
   (disallowing-recur (analyze-do-statements* env exprs)))
@@ -1902,7 +1926,7 @@
   (let [statements (analyze-do-statements env exprs)]
     (if (<= (count exprs) 1)
       (let [ret      (analyze env (first exprs))
-            children (conj (vec statements) ret)]
+            children [:statements :ret]]
         {:op :do
          :env env
          :form form
@@ -1912,7 +1936,7 @@
                        (assoc env :context :statement)
                        (assoc env :context :return))
             ret      (analyze ret-env (last exprs))
-            children (conj (vec statements) ret)]
+            children [:statements :ret]]
         {:op :do
          :env env
          :form form
@@ -1999,7 +2023,7 @@
                        (some? *loop-lets*) (cons {:params bes} *loop-lets*))
         expr         (analyze-let-body env context exprs recur-frames loop-lets)
         op           (if (true? is-loop) :loop :let)
-        children     (conj (vec (map :init bes)) expr)]
+        children     [:bindings :body]]
     {:op op
      :env encl-env
      :bindings bes
@@ -2036,7 +2060,7 @@
     (assoc {:env env :op :recur :form form}
       :frame frame
       :exprs exprs
-      :children exprs)))
+      :children [:exprs])))
 
 (defmethod parse 'quote
   [_ env [_ x] _ _]
@@ -2058,8 +2082,8 @@
      (when (and (not (-> ctor meta :internal-ctor))
                 (some? known-num-fields) (not= known-num-fields argc))
        (warning :fn-arity env {:argc argc :ctor ctor}))
-     {:env env :op :new :form form :ctor ctorexpr :args argexprs
-      :children (into [ctorexpr] argexprs)
+     {:env env :op :new :form form :class ctorexpr :args argexprs
+      :children [:class :args]
       :tag (let [name (-> ctorexpr :info :name)]
              (or ('{js/Object object
                     js/String string
@@ -2118,7 +2142,7 @@
 
           :else
           {:env env :op :set! :form form :target texpr :val vexpr
-           :children [texpr vexpr]})))))
+           :children [:target :val]})))))
 
 #?(:clj (declare analyze-file))
 
@@ -2862,6 +2886,7 @@
     {:op op :env env :form form :t t :fields fields :pmasks pmasks
      :tag 'function
      :protocols (disj protocols 'cljs.core/Object)
+     :children [#_:fields :body]
      :body (analyze (assoc env :locals locals) body)}))
 
 (defmethod parse 'deftype*
@@ -2977,7 +3002,7 @@
           (swap! env/*compiler* update-in
             (into [::namespaces (-> env :ns :name) :externs] pre) merge {}))))
     (case dot-action
-      ::access (let [children [targetexpr]]
+      ::access (let [children [:target]]
                  {:op :host-field
                   :env env
                   :form form
@@ -2987,8 +3012,8 @@
                   :tag (if (js-tag? tag)
                          (or (js-tag (-> tag meta :prefix) :tag) tag)
                          tag)})
-      ::call   (let [argexprs (map #(analyze enve %) args)
-                     children (into [targetexpr] argexprs)]
+      ::call   (let [argexprs (mapv #(analyze enve %) args)
+                     children [:target :args]]
                  {:op :host-call
                   :env env
                   :form form
@@ -3103,7 +3128,7 @@
      :args argexprs
      :tag tag
      :form form
-     :children argexprs
+     :children [:args]
      :js-op js-op
      :numeric numeric}))
 
@@ -3206,9 +3231,9 @@
              (~(analyzed (if bind-f-expr? f-sym f))
                ~@(if bind-args? arg-syms args)))))
       (let [ana-expr #(analyze enve %)
-            argexprs (map ana-expr args)]
-        {:env env :op :invoke :form form :fn fexpr :args (vec argexprs)
-         :children (into [fexpr] argexprs)}))))
+            argexprs (mapv ana-expr args)]
+        {:env env :op :invoke :form form :fn fexpr :args argexprs
+         :children [:fn :args]}))))
 
 (defn parse-invoke
   [env form]
@@ -3434,30 +3459,30 @@
 (defn analyze-map
   [env form]
   (let [expr-env (assoc env :context :expr)
-        ks (disallowing-recur (vec (map #(analyze expr-env %) (keys form))))
-        vs (disallowing-recur (vec (map #(analyze expr-env %) (vals form))))]
+        ks (disallowing-recur (mapv #(analyze expr-env %) (keys form)))
+        vs (disallowing-recur (mapv #(analyze expr-env %) (vals form)))]
     (analyze-wrap-meta {:op :map :env env :form form
                         :keys ks :vals vs
-                        :children (vec (interleave ks vs))
+                        :children [:keys :vals]
                         :tag 'cljs.core/IMap})))
 
 (defn analyze-list
   [env form]
   (let [expr-env (assoc env :context :expr)
-        items (disallowing-recur (doall (map #(analyze expr-env %) form)))]
-    (analyze-wrap-meta {:op :list :env env :form form :items items :children items :tag 'cljs.core/IList})))
+        items (disallowing-recur (mapv #(analyze expr-env %) form))]
+    (analyze-wrap-meta {:op :list :env env :form form :items items :children [:items] :tag 'cljs.core/IList})))
 
 (defn analyze-vector
   [env form]
   (let [expr-env (assoc env :context :expr)
-        items (disallowing-recur (vec (map #(analyze expr-env %) form)))]
-    (analyze-wrap-meta {:op :vector :env env :form form :items items :children items :tag 'cljs.core/IVector})))
+        items (disallowing-recur (mapv #(analyze expr-env %) form))]
+    (analyze-wrap-meta {:op :vector :env env :form form :items items :children [:items] :tag 'cljs.core/IVector})))
 
 (defn analyze-set
   [env form ]
   (let [expr-env (assoc env :context :expr)
-        items (disallowing-recur (vec (map #(analyze expr-env %) form)))]
-    (analyze-wrap-meta {:op :set :env env :form form :items items :children items :tag 'cljs.core/ISet})))
+        items (disallowing-recur (mapv #(analyze expr-env %) form))]
+    (analyze-wrap-meta {:op :set :env env :form form :items items :children [:items] :tag 'cljs.core/ISet})))
 
 (defn analyze-js-value
   [env ^JSValue form]
@@ -3472,7 +3497,7 @@
          :form form
          :keys keys
          :vals vals
-         :children vals
+         :children [:vals]
          :tag 'object})
       (let [items (disallowing-recur
                     (mapv #(analyze expr-env %) val))]
@@ -3480,7 +3505,7 @@
          :env env
          :form form
          :items items
-         :children items
+         :children [:items]
          :tag 'array}))))
 
 (defn analyze-record
@@ -3499,7 +3524,7 @@
      :env env
      :form x
      :items items
-     :children [items]
+     :children [:items]
      :tag (symbol (str ns) (str name))}))
 
 (defn elide-reader-meta [m]
@@ -3516,7 +3541,7 @@
             expr (assoc-in expr [:env :context] :expr) ; change expr to :expr
             meta-expr (analyze-map (:env expr) m)]
         {:op :with-meta :env env :form form
-         :meta meta-expr :expr expr :children [meta-expr expr]})
+         :meta meta-expr :expr expr :children [:meta :expr]})
       expr)))
 
 (defn infer-type [env ast _]
@@ -3662,7 +3687,8 @@
   (one of :statement, :expr, :return), :ns (a symbol naming the
   compilation ns)}, and form, returns an expression object (a map
   containing at least :form, :op and :env keys). If expr has any (immediately)
-  nested exprs, must have :children [exprs...] entry. This will
+  nested exprs, must have a :children entry. This must be a vector of keywords naming
+  the immediately nested fields mapped to an expr or vector of exprs. This will
   facilitate code walking without knowing the details of the op set."
   ([env form] (analyze env form nil))
   ([env form name]
diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc
index 5108a5198..aca644e89 100644
--- a/src/main/clojure/cljs/analyzer/api.cljc
+++ b/src/main/clojure/cljs/analyzer/api.cljc
@@ -71,7 +71,8 @@
      (one of :statement, :expr, :return), :ns (a symbol naming the
      compilation ns)}, and form, returns an expression object (a map
      containing at least :form, :op and :env keys). If expr has any (immediately)
-     nested exprs, must have :children [exprs...] entry. This will
+     nested exprs, must have :children entry. This must be a vector of keywords naming
+     the immediately nested fields mapped to an expr or vector of exprs. This will
      facilitate code walking without knowing the details of the op set."
      ([env form] (analyze env form nil))
      ([env form name] (analyze env form name nil))
diff --git a/src/main/clojure/cljs/analyzer/utils.clj b/src/main/clojure/cljs/analyzer/utils.clj
index 6b7a57180..f61f201dc 100644
--- a/src/main/clojure/cljs/analyzer/utils.clj
+++ b/src/main/clojure/cljs/analyzer/utils.clj
@@ -13,7 +13,7 @@
   (let [env (:env ast)
         ast (if (= op :fn)
               (assoc ast :methods
-                (map #(simplify-env nil %) (:methods ast)))
+                (mapv #(simplify-env nil %) (:methods ast)))
               ast)]
     (assoc (dissoc ast :env)
       :env {:context (:context env)})))
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index f98f726b5..7fb040c79 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -935,10 +935,10 @@
 (defmethod emit* :do
   [{:keys [statements ret env]}]
   (let [context (:context env)]
-    (when (and statements (= :expr context)) (emitln "(function (){"))
+    (when (and (seq statements) (= :expr context)) (emitln "(function (){"))
     (doseq [s statements] (emitln s))
     (emit ret)
-    (when (and statements (= :expr context)) (emitln "})()"))))
+    (when (and (seq statements) (= :expr context)) (emitln "})()"))))
 
 (defmethod emit* :try
   [{try :body :keys [env catch name finally]}]
@@ -1117,7 +1117,7 @@
          (emits f ".call(" (comma-sep (cons "null" args)) ")"))))))
 
 (defmethod emit* :new
-  [{:keys [ctor args env]}]
+  [{ctor :class :keys [args env]}]
   (emit-wrap env
              (emits "(new " ctor "("
                     (comma-sep args)
diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj
index 4e5386bbe..ac5a906a4 100644
--- a/src/test/clojure/cljs/compiler_tests.clj
+++ b/src/test/clojure/cljs/compiler_tests.clj
@@ -49,14 +49,14 @@
              (ana/analyze aenv
                '(defn foo []
                   (fn bar [])))
-             [:init :children 0 :children 0 :name]))
+             [:init :methods 0 :body :ret :local]))
           'cljs$user$foo_$_bar))
   (is (= (comp/munge
            (get-in
              (ana/analyze aenv
                '(fn []
                   (fn console [])))
-             [:children 0 :children 0 :name]))
+             [:methods 0 :body :ret :local]))
          'cljs$user$console)))
 
 (deftest test-js-negative-infinity

From 4ac13fc1424f9d4e600674b85be8afe704691e7d Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Mon, 2 Jul 2018 12:49:55 -0400
Subject: [PATCH 3227/4033] CLJS-1997: Outward function type hint propagation

---
 src/main/clojure/cljs/analyzer.cljc | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 460bf83b3..8b6d2c33a 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1603,7 +1603,7 @@
                           (analyze (assoc env :context :expr) (:init args) sym))))
           fn-var? (and (some? init-expr) (= (:op init-expr) :fn))
           tag (if fn-var?
-                (or (:ret-tag init-expr) tag)
+                (or (:ret-tag init-expr) tag (:inferred-ret-tag init-expr))
                 (or tag (:tag init-expr)))
           export-as (when-let [export-val (-> sym meta :export)]
                       (if (= true export-val) var-name export-val))
@@ -1844,6 +1844,9 @@
         children     (if (some? name-var)
                        [:local :methods]
                        [:methods])
+        inferred-ret-tag (let [inferred-tags (map (partial infer-tag env) (map :body methods))]
+                           (when (apply = inferred-tags)
+                             (first inferred-tags)))
         ast   (merge {:op :fn
                       :env env
                       :form form
@@ -1851,6 +1854,7 @@
                       :methods methods
                       :variadic? variadic
                       :tag 'function
+                      :inferred-ret-tag inferred-ret-tag
                       :recur-frames *recur-frames*
                       :loop-lets *loop-lets*
                       :jsdoc [js-doc]

From f289ffee2270567f7976d45012a0a52c38eb6488 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Sat, 10 Mar 2018 20:33:48 -0500
Subject: [PATCH 3228/4033] CLJS-1702: Warning when using private vars

---
 src/main/cljs/cljs/spec/alpha.cljc         |   2 +-
 src/main/cljs/cljs/spec/test/alpha.cljc    |  12 +-
 src/main/cljs/clojure/core/reducers.cljs   |   2 +-
 src/main/clojure/cljs/analyzer.cljc        | 149 ++++++++++++---------
 src/main/clojure/cljs/analyzer/api.cljc    |   5 +-
 src/main/clojure/cljs/compiler.cljc        |   2 +-
 src/main/clojure/cljs/core.cljc            |   4 +-
 src/main/clojure/cljs/tagged_literals.cljc |   2 +-
 src/test/cljs/cljs/pprint_test.cljs        |   2 +-
 src/test/clojure/cljs/analyzer_tests.clj   |  19 +++
 10 files changed, 122 insertions(+), 77 deletions(-)

diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc
index b8a18ebb6..b2c0a0179 100644
--- a/src/main/cljs/cljs/spec/alpha.cljc
+++ b/src/main/cljs/cljs/spec/alpha.cljc
@@ -551,7 +551,7 @@ value of 'cljs.spec.alpha/*runtime-asserts*', or false if not set. You can
 toggle check-asserts? with (check-asserts bool)."
   [spec x]
   `(if *compile-asserts*
-     (if *runtime-asserts*
+     (if @#'*runtime-asserts*
        (assert* ~spec ~x)
        ~x)
     ~x))
diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc
index a3028129f..78476639f 100644
--- a/src/main/cljs/cljs/spec/test/alpha.cljc
+++ b/src/main/cljs/cljs/spec/test/alpha.cljc
@@ -38,7 +38,7 @@
     (when (and (nil? (:const v))
                #?(:cljs (nil? (:macro v))))
       (swap! instrumented-vars conj (:name v))
-      `(let [checked# (instrument-1* '~s (var ~s) ~opts)]
+      `(let [checked# (#'instrument-1* '~s (var ~s) ~opts)]
          (when checked# (set! ~s checked#))
          '~(:name v)))))
 
@@ -47,7 +47,7 @@
   (when-let [v (ana-api/resolve &env s)]
     (when (@instrumented-vars (:name v))
       (swap! instrumented-vars disj (:name v))
-      `(let [raw# (unstrument-1* ~s (var ~s))]
+      `(let [raw# (#'unstrument-1* '~s (var ~s))]
          (when raw# (set! ~s raw#))
          '~(:name v)))))
 
@@ -165,8 +165,8 @@ Returns a collection of syms naming the vars unstrumented."
             :sym     s# :spec spec#}
 
            (:args spec#)
-           (let [tcret# (quick-check f# spec# opts#)]
-             (make-check-result s# spec# tcret#))
+           (let [tcret# (#'quick-check f# spec# opts#)]
+             (#'make-check-result s# spec# tcret#))
 
            :default
            {:failure (ex-info "No :args spec" {::s/failure :no-args-spec})
@@ -189,7 +189,7 @@ Returns a collection of syms naming the vars unstrumented."
     (checkable-syms* nil))
   ([opts]
    (reduce into #{}
-     [(filter fn-spec-name? (keys @s/registry-ref))
+     [(filter fn-spec-name? (keys @@#'s/registry-ref))
       (keys (:spec opts))])))
 
 (defmacro checkable-syms
@@ -201,7 +201,7 @@ can be checked."
    `(let [opts# ~opts]
       (validate-check-opts opts#)
       (reduce conj #{}
-        '[~@(filter fn-spec-name? (keys @s/registry-ref))
+        '[~@(filter fn-spec-name? (keys @@#'s/registry-ref))
           ~@(keys (:spec opts))]))))
 
 (defmacro check
diff --git a/src/main/cljs/clojure/core/reducers.cljs b/src/main/cljs/clojure/core/reducers.cljs
index 1a9644124..159cb8efd 100644
--- a/src/main/cljs/clojure/core/reducers.cljs
+++ b/src/main/cljs/clojure/core/reducers.cljs
@@ -40,7 +40,7 @@
        (-kv-reduce coll f init)
        (cond
          (nil? coll) init
-         (array? coll) (array-reduce coll f init)
+         (array? coll) (#'array-reduce coll f init)
          :else (-reduce coll f init)))))
 
 (defprotocol CollFold
diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 8b6d2c33a..b3e68cb6d 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -62,6 +62,7 @@
 (def ^:dynamic *macro-infer* true)
 (def ^:dynamic *passes* nil)
 (def ^:dynamic *file-defs* nil)
+(def ^:dynamic *private-var-access-nowarn* false)
 
 (def constants-ns-sym
   "The namespace of the constants table as a symbol."
@@ -123,6 +124,7 @@
   {:preamble-missing true
    :unprovided true
    :undeclared-var true
+   :private-var-access true
    :undeclared-ns true
    :undeclared-ns-form true
    :redef true
@@ -305,6 +307,10 @@
          "Use of undeclared Var ")
     (:prefix info) "/" (:suffix info)))
 
+(defmethod error-message :private-var-access
+  [warning-type info]
+  (str "var: " (:sym info) " is not public"))
+
 (defmethod error-message :undeclared-ns
   [warning-type {:keys [ns-sym js-provide] :as info}]
   (str "No such namespace: " ns-sym
@@ -969,20 +975,20 @@
     (node-module-dep? ns) :node
     (dep-has-global-exports? ns) :global))
 
-(defmulti resolve* (fn [sym full-ns current-ns] (ns->module-type full-ns)))
+(defmulti resolve* (fn [env sym full-ns current-ns] (ns->module-type full-ns)))
 
 (defmethod resolve* :js
-  [sym full-ns current-ns]
+  [env sym full-ns current-ns]
   {:name (symbol (str full-ns) (str (name sym)))
    :ns full-ns})
 
 (defmethod resolve* :node
-  [sym full-ns current-ns]
+  [env sym full-ns current-ns]
   {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym)))
    :ns current-ns})
 
 (defmethod resolve* :global
-  [sym full-ns current-ns]
+  [env sym full-ns current-ns]
   (let [pre (into '[Object] (->> (string/split (name sym) #"\.") (map symbol) vec))]
     (when-not (has-extern? pre)
       (swap! env/*compiler* update-in
@@ -991,11 +997,26 @@
      :ns current-ns
      :tag (with-meta 'js {:prefix pre})}))
 
+(def ^:private private-var-access-exceptions
+  "Specially-treated symbols for which we don't trigger :private-var-access warnings."
+  '#{cljs.core/checked-aget
+     cljs.core/checked-aset
+     cljs.core/checked-aget'
+     cljs.core/checked-aset'})
+
 (defmethod resolve* :default
-  [sym full-ns current-ns]
-  (merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym)))
-    {:name (symbol (str full-ns) (str (name sym)))
-     :ns full-ns}))
+  [env sym full-ns current-ns]
+  (let [sym-ast (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym)))
+        sym-name (symbol (str full-ns) (str (name sym)))]
+    (when (and (not= current-ns full-ns)
+               (:private sym-ast)
+               (not *private-var-access-nowarn*)
+               (not (contains? private-var-access-exceptions sym-name)))
+      (warning :private-var-access env
+        {:sym sym-name}))
+    (merge sym-ast
+      {:name sym-name
+       :ns   full-ns})))
 
 (defn required? [ns env]
   (or (contains? (set (vals (gets env :ns :requires))) ns)
@@ -1070,7 +1091,7 @@
                (when (not= current-ns full-ns)
                  (confirm-ns env full-ns))
                (confirm env full-ns (symbol (name sym))))
-             (resolve* sym full-ns current-ns))
+             (resolve* env sym full-ns current-ns))
 
            (dotted-symbol? sym)
            (let [idx    (.indexOf s ".")
@@ -1090,13 +1111,13 @@
 
            (some? (gets @env/*compiler* ::namespaces current-ns :uses sym))
            (let [full-ns (gets @env/*compiler* ::namespaces current-ns :uses sym)]
-             (resolve* sym full-ns current-ns))
+             (resolve* env sym full-ns current-ns))
 
            (some? (gets @env/*compiler* ::namespaces current-ns :renames sym))
            (let [qualified-symbol (gets @env/*compiler* ::namespaces current-ns :renames sym)
                  full-ns (symbol (namespace qualified-symbol))
                  sym     (symbol (name qualified-symbol))]
-             (resolve* sym full-ns current-ns))
+             (resolve* env sym full-ns current-ns))
 
            (some? (gets @env/*compiler* ::namespaces current-ns :imports sym))
            (recur env (gets @env/*compiler* ::namespaces current-ns :imports sym) confirm)
@@ -1352,13 +1373,14 @@
   [env sym]
   ;; we need to dissoc locals for the `(let [x 1] (def x x))` case, because we
   ;; want the var's AST and `resolve-var` will check locals first. - António Monteiro
-  (let [env (dissoc env :locals)
-        var (resolve-var env sym (confirm-var-exists-throw))
-        expr-env (assoc env :context :expr)]
-    (when-some [var-ns (:ns var)]
-      {:var (analyze expr-env sym)
-       :sym (analyze expr-env `(quote ~(symbol (name var-ns) (name (:name var)))))
-       :meta (var-meta var expr-env)})))
+  (binding [*private-var-access-nowarn* true]
+    (let [env      (dissoc env :locals)
+          var      (resolve-var env sym (confirm-var-exists-throw))
+          expr-env (assoc env :context :expr)]
+      (when-some [var-ns (:ns var)]
+        {:var  (analyze expr-env sym)
+         :sym  (analyze expr-env `(quote ~(symbol (name var-ns) (name (:name var)))))
+         :meta (var-meta var expr-env)}))))
 
 (defmethod parse 'var
   [op env [_ sym :as form] _ _]
@@ -2104,49 +2126,50 @@
                        [`(. ~target ~val) alt]
                        [target val])]
     (disallowing-recur
-      (let [enve  (assoc env :context :expr)
-            texpr (cond
-                    (symbol? target)
-                    (do
-                      (cond
-                        (and (= target '*unchecked-if*) ;; TODO: proper resolve
-                             (or (true? val) (false? val)))
-                        (set! *unchecked-if* val)
-
-                        (and (= target '*unchecked-arrays*) ;; TODO: proper resolve
-                             (or (true? val) (false? val)))
-                        (set! *unchecked-arrays* val)
-
-                        (and (= target '*warn-on-infer*)
-                             (or (true? val) (false? val)))
-                        (set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning val)))
-                      (when (some? (:const (resolve-var (dissoc env :locals) target)))
-                        (throw (error env "Can't set! a constant")))
-                      (let [local (-> env :locals target)]
-                        (when-not (or (nil? local)
-                                      (and (:field local)
-                                           (or (:mutable local)
-                                               (:unsynchronized-mutable local)
-                                               (:volatile-mutable local))))
-                          (throw (error env "Can't set! local var or non-mutable field"))))
-                      (analyze-symbol enve target))
-
-                    :else
-                    (when (seq? target)
-                      (let [texpr (analyze-seq enve target nil)]
-                        (when (:field texpr)
-                          texpr))))
-            vexpr (analyze enve val)]
-        (when-not texpr
-          (throw (error env "set! target must be a field or a symbol naming a var")))
-        (cond
-          (and (not (:def-emits-var env)) ;; non-REPL context
-               (some? ('#{*unchecked-if* *unchecked-array* *warn-on-infer*} target)))
-          {:env env :op :no-op}
-
-          :else
-          {:env env :op :set! :form form :target texpr :val vexpr
-           :children [:target :val]})))))
+      (binding [*private-var-access-nowarn* true]
+        (let [enve  (assoc env :context :expr)
+              texpr (cond
+                      (symbol? target)
+                      (do
+                        (cond
+                          (and (= target '*unchecked-if*)   ;; TODO: proper resolve
+                               (or (true? val) (false? val)))
+                          (set! *unchecked-if* val)
+
+                          (and (= target '*unchecked-arrays*) ;; TODO: proper resolve
+                               (or (true? val) (false? val)))
+                          (set! *unchecked-arrays* val)
+
+                          (and (= target '*warn-on-infer*)
+                               (or (true? val) (false? val)))
+                          (set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning val)))
+                        (when (some? (:const (resolve-var (dissoc env :locals) target)))
+                          (throw (error env "Can't set! a constant")))
+                        (let [local (-> env :locals target)]
+                          (when-not (or (nil? local)
+                                        (and (:field local)
+                                             (or (:mutable local)
+                                                 (:unsynchronized-mutable local)
+                                                 (:volatile-mutable local))))
+                            (throw (error env "Can't set! local var or non-mutable field"))))
+                        (analyze-symbol enve target))
+
+                      :else
+                      (when (seq? target)
+                        (let [texpr (analyze-seq enve target nil)]
+                          (when (:field texpr)
+                            texpr))))
+              vexpr (analyze enve val)]
+          (when-not texpr
+            (throw (error env "set! target must be a field or a symbol naming a var")))
+          (cond
+            (and (not (:def-emits-var env))                 ;; non-REPL context
+                 (some? ('#{*unchecked-if* *unchecked-array* *warn-on-infer*} target)))
+            {:env env :op :no-op}
+
+            :else
+            {:env env :op :set! :form form :target texpr :val vexpr
+             :children [:target :val]}))))))
 
 #?(:clj (declare analyze-file))
 
@@ -3734,7 +3757,9 @@
   (if (and (not (namespace sym))
            (dotted-symbol? sym))
     sym
-    (:name (resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*)) sym))))
+    (:name (binding [*private-var-access-nowarn* true]
+             (resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*))
+               sym)))))
 
 #?(:clj
    (defn forms-seq*
diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc
index aca644e89..8e444c573 100644
--- a/src/main/clojure/cljs/analyzer/api.cljc
+++ b/src/main/clojure/cljs/analyzer/api.cljc
@@ -148,8 +148,9 @@
   [env sym]
   {:pre [(map? env) (symbol? sym)]}
   (try
-    (ana/resolve-var env sym
-      (ana/confirm-var-exists-throw))
+    (binding [ana/*private-var-access-nowarn* true]
+      (ana/resolve-var env sym
+        (ana/confirm-var-exists-throw)))
     (catch #?(:clj Exception :cljs :default) e
       (ana/resolve-macro-var env sym))))
 
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 7fb040c79..4326e4c1b 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -135,7 +135,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 (#'cljs.core/munge-str ss))]
        (if (symbol? s)
          (symbol ms)
          ms)))))
diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 34b1339a0..25a6263a6 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -1864,7 +1864,7 @@
 
                         'IPrintWithWriter
                         `(~'-pr-writer [this# writer# opts#]
-                           (let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# pr-writer "" " " "" opts# keyval#))]
+                           (let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# @#'pr-writer "" " " "" opts# keyval#))]
                              (pr-sequential-writer
                                writer# pr-pair# ~pr-open ", " "}" opts#
                                (concat [~@(map #(core/list `vector (keyword %) %) base-fields)]
@@ -2689,7 +2689,7 @@
                prefer-table# (atom {})
                method-cache# (atom {})
                cached-hierarchy# (atom {})
-               hierarchy# (cljs.core/get ~options :hierarchy (cljs.core/get-global-hierarchy))]
+               hierarchy# (cljs.core/get ~options :hierarchy (#'cljs.core/get-global-hierarchy))]
            (cljs.core/MultiFn. (cljs.core/symbol ~mm-ns ~(name mm-name)) ~dispatch-fn ~default hierarchy#
              method-table# prefer-table# method-cache# cached-hierarchy#))))))
 
diff --git a/src/main/clojure/cljs/tagged_literals.cljc b/src/main/clojure/cljs/tagged_literals.cljc
index 2374337f4..d38832681 100644
--- a/src/main/clojure/cljs/tagged_literals.cljc
+++ b/src/main/clojure/cljs/tagged_literals.cljc
@@ -56,7 +56,7 @@
      (when-not (string? form)
        (throw (js/Error. "Instance literal expects a string for its timestamp.")))
      (try
-       (reader/read-date form)
+       (#'reader/read-date form)
        (catch :default e
          (throw (js/Error. (. e -message)))))))
 
diff --git a/src/test/cljs/cljs/pprint_test.cljs b/src/test/cljs/cljs/pprint_test.cljs
index 2846d796d..a1832df7a 100644
--- a/src/test/cljs/cljs/pprint_test.cljs
+++ b/src/test/cljs/cljs/pprint_test.cljs
@@ -761,7 +761,7 @@ Usage: *hello*
       (doseq [row aseq]
         (doseq [col row]
           (cl-format *out* "~4D~7,vT" col column-width))
-        (prn)))
+        (#'prn)))
     (str sb)
     ;;TODO do we need to extend StringBufferWriter to allow access to underlying StringBuffer?
     #_(str (:base @@(:base @@stream)))))
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index 3bde9f689..4822143e1 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -473,6 +473,25 @@
             (a/analyze test-env '(def foo.core/foo 43))))
       (is (a/analyze test-env '(def cljs.user/foo 43))))))
 
+(deftest test-cljs-1702
+  (let [ws (atom [])]
+    (a/with-warning-handlers [(collecting-warning-handler ws)]
+      (e/with-compiler-env test-cenv
+        (a/analyze-form-seq
+          '[(ns test.cljs-1702-a)
+            (def ^:private a 3)
+            (def ^:private b 3)
+            (defn- test-fn-a [a] a)
+            (defn- test-fn-b [a] b)])
+        (a/analyze-form-seq
+          '[(ns test.cljs-1702-b)
+            (test.cljs-1702-a/test-fn-a 1)
+            (#'test.cljs-1702-a/test-fn-b 1)
+            test.cljs-1702-a/a
+            @#'test.cljs-1702-a/b]))
+      (is (= ["var: test.cljs-1702-a/test-fn-a is not public"
+              "var: test.cljs-1702-a/a is not public"] @ws)))))
+
 (deftest test-cljs-1763
   (let [parsed (a/parse-ns-excludes {} '())]
     (is (= parsed

From 7c99d37fccaad175ba8402253155869905e20950 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Fri, 22 Jun 2018 23:45:41 -0400
Subject: [PATCH 3229/4033] CLJS-2801: Add :quote AST op and remove :list op

1. add :quote AST op
 - This moves a lot more logic into handling :const in the emitter.
   We rename emit-constant -> emit-constant* and turn emit-constant
   into a function that emits metadata for each form before delegating
   to emit-constant*.
 - Some emit* defmethods are factored out into higher-order function helpers
   (:{map,list,vector,set} -> emit-{map,list,vector,set}) and are now called
   from both emit and emit-constant.
 - analyze-const now registers constants, but throws away the analysis results.

2. remove :list :op
 - subsumed by :quote'd :const. analyze-list still used to register constants.
---
 src/main/cljs/cljs/js.cljs          |   4 +-
 src/main/clojure/cljs/analyzer.cljc |  60 +++++--
 src/main/clojure/cljs/closure.clj   |   3 +-
 src/main/clojure/cljs/compiler.cljc | 233 ++++++++++++++++++----------
 src/main/clojure/cljs/core.cljc     |   6 +-
 src/test/self/self_host/test.cljs   |   4 +-
 src/test/self/self_parity/test.cljs |   1 +
 7 files changed, 212 insertions(+), 99 deletions(-)

diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs
index ae7f4b185..aa49725a5 100644
--- a/src/main/cljs/cljs/js.cljs
+++ b/src/main/cljs/cljs/js.cljs
@@ -1229,11 +1229,11 @@
 (defn- emit-fn [f]
   (print "cljs.js.get_fn(" (put-fn f) ")"))
 
-(defmethod comp/emit-constant js/Function
+(defmethod comp/emit-constant* js/Function
   [f]
   (emit-fn f))
 
-(defmethod comp/emit-constant cljs.core/Var
+(defmethod comp/emit-constant* cljs.core/Var
   [f]
   (emit-fn f))
 
diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 460bf83b3..3e150569e 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -1249,8 +1249,14 @@
 
 (def BOOLEAN_OR_SEQ '#{boolean seq})
 
+(defn unwrap-quote [{:keys [op] :as expr}]
+  (if #?(:clj (= op :quote)
+         :cljs (keyword-identical? op :quote))
+    (:expr expr)
+    expr))
+
 (defn infer-if [env e]
-  (let [{{:keys [op form]} :test} e
+  (let [{:keys [op form]} (unwrap-quote (:test e))
         then-tag (infer-tag env (:then e))]
     (if (and #?(:clj (= op :const)
                 :cljs (keyword-identical? op :const))
@@ -1314,6 +1320,7 @@
                     true BOOLEAN_SYM
                     false BOOLEAN_SYM
                     ANY_SYM)
+        :quote    (infer-tag env (:expr e))
         :var      (if-some [init (:init e)]
                     (infer-tag env init)
                     (infer-tag env (:info e)))
@@ -1522,10 +1529,19 @@
 
 (defn constant-value?
   [{:keys [op] :as ast}]
-  (or (= :const op)
-      (and (#{:map :set :vector :list} op)
+  (or (#{:quote :const} op)
+      (and (#{:map :set :vector} op)
            (every? constant-value? (ast-children ast)))))
 
+(defn const-expr->constant-value [{:keys [op] :as e}]
+  (case op 
+    :quote  (const-expr->constant-value (:expr e))
+    :const  (:val e)
+    :map    (zipmap (map const-expr->constant-value (:keys e))
+                    (map const-expr->constant-value (:vals e)))
+    :set    (into #{} (map const-expr->constant-value (:items e)))
+    :vector (into [] (map const-expr->constant-value (:items e)))))
+
 (defmethod parse 'def
   [op env form _ _]
   (when (> (count form) 4)
@@ -2062,9 +2078,28 @@
       :exprs exprs
       :children [:exprs])))
 
+(defn analyze-const
+  [env form]
+  (let [;; register constants
+        {:keys [tag]} (analyze (assoc env :quoted? true) form)]
+    {:op       :const
+     :env      env
+     :literal? true
+     :val      form
+     :tag      tag
+     :form     form}))
+
 (defmethod parse 'quote
-  [_ env [_ x] _ _]
-  (analyze (assoc env :quoted? true) x))
+  [_ env [_ x :as form] _ _]
+  (when (not= 2 (count form))
+    (throw (error env "Wrong number of args to quote")))
+  (let [expr (analyze-const env x)]
+    {:op :quote
+     :expr expr
+     :env env
+     :form form
+     :tag (:tag expr)
+     :children [:expr]}))
 
 (defmethod parse 'new
   [_ env [_ ctor & args :as form] _ _]
@@ -3466,6 +3501,8 @@
                         :children [:keys :vals]
                         :tag 'cljs.core/IMap})))
 
+;; :list is not used in the emitter any more, but analyze-list is called from analyze-const
+;; to hit the `register-constant!` cases for symbols and keywords.
 (defn analyze-list
   [env form]
   (let [expr-env (assoc env :context :expr)
@@ -3533,9 +3570,12 @@
 (defn elide-analyzer-meta [m]
   (dissoc m ::analyzed))
 
+(defn elide-irrelevant-meta [m]
+  (-> m elide-reader-meta elide-analyzer-meta))
+
 (defn analyze-wrap-meta [expr]
   (let [form (:form expr)
-        m    (-> (meta form) elide-reader-meta elide-analyzer-meta)]
+        m    (elide-irrelevant-meta (meta form))]
     (if (some? (seq m))
       (let [env (:env expr) ; take on expr's context ourselves
             expr (assoc-in expr [:env :context] :expr) ; change expr to :expr
@@ -3636,7 +3676,6 @@
        (set? form) (analyze-set env form)
        (keyword? form) (analyze-keyword env form)
        (instance? JSValue form) (analyze-js-value env form)
-       (= () form) (analyze-list env form)
        :else
        (let [tag (cond
                    (nil? form) 'clj-nil
@@ -3644,7 +3683,8 @@
                    (string? form) 'string
                    (instance? Character form) 'string
                    (true? form) 'boolean
-                   (false? form) 'boolean)]
+                   (false? form) 'boolean
+                   (= () form) 'cljs.core/IList)]
          (cond-> {:op :const :val form :env env :form form}
            tag (assoc :tag tag))))))
 
@@ -3659,14 +3699,14 @@
        (cljs-set? form) (analyze-set env form)
        (keyword? form) (analyze-keyword env form)
        (instance? cljs.tagged-literals/JSValue form) (analyze-js-value env form)
-       (= () form) (analyze-list env form)
        :else
        (let [tag (cond
                    (nil? form) CLJ_NIL_SYM
                    (number? form) NUMBER_SYM
                    (string? form) STRING_SYM
                    (true? form) BOOLEAN_SYM
-                   (false? form) BOOLEAN_SYM)]
+                   (false? form) BOOLEAN_SYM
+                   (= () form) 'cljs.core/IList)]
          (cond-> {:op :const :val form :env env :form form}
            tag (assoc :tag tag))))))
 
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index 7973c80c6..4bca9f5a9 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -1215,7 +1215,8 @@
 (defn- const-expr-form
   "Returns the :const-expr form for `sym` from `compiler-state`."
   [compiler-state sym]
-  (get-in compiler-state [::ana/namespaces (symbol (namespace sym)) :defs (symbol (name sym)) :const-expr :form]))
+  (let [const-expr (get-in compiler-state [::ana/namespaces (symbol (namespace sym)) :defs (symbol (name sym)) :const-expr])]
+    (some-> const-expr ana/const-expr->constant-value)))
 
 (defn compile-loader
   "Special compilation pass for cljs.loader namespace. cljs.loader must be
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 7fb040c79..ebb0e3e19 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -30,7 +30,8 @@
                      [cljs.analyzer :as ana]
                      [cljs.source-map :as sm]))
   #?(:clj (:import java.lang.StringBuilder
-                   java.io.File)
+                   java.io.File
+                   [cljs.tagged_literals JSValue])
      :cljs (:import [goog.string StringBuffer])))
 
 #?(:clj (set! *warn-on-reflection* true))
@@ -227,28 +228,54 @@
   (with-out-str (emit expr)))
 
 #?(:clj
-   (defmulti emit-constant class)
+   (defmulti emit-constant* class)
    :cljs
-   (defmulti emit-constant type))
+   (defmulti emit-constant* type))
 
-(defmethod emit-constant :default
+(declare emit-map emit-list emit-vector emit-set emit-js-object emit-js-array
+         emit-with-meta emit-constants-comma-sep emit-constant)
+
+#?(:clj
+   (defn emit-constant-no-meta [x]
+     (cond
+       (seq? x) (emit-list x emit-constants-comma-sep)
+       (map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep #(apply distinct? %))
+       (vector? x) (emit-vector x emit-constants-comma-sep)
+       (set? x) (emit-set x emit-constants-comma-sep)
+       :else (emit-constant* x)))
+   :cljs
+   (defn emit-constant-no-meta [x]
+     (cond
+       (ana/cljs-seq? x) (emit-list x emit-constants-comma-sep)
+       (ana/cljs-map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep #(apply distinct? %))
+       (ana/cljs-vector? x) (emit-vector x emit-constants-comma-sep)
+       (ana/cljs-set? x) (emit-set x emit-constants-comma-sep)
+       :else (emit-constant* x))))
+
+(defn emit-constant [v]
+  (let [m (ana/elide-irrelevant-meta (meta v))]
+    (if (some? (seq m))
+      (emit-with-meta #(emit-constant-no-meta v) #(emit-constant-no-meta m))
+      (emit-constant-no-meta v))))
+
+(defmethod emit-constant* :default
   [x]
   (throw
     (ex-info (str "failed compiling constant: " x "; "
-               (type x) " is not a valid ClojureScript constant.")
+               (pr-str (type x)) " is not a valid ClojureScript constant.")
       {:constant x
        :type (type x)})))
 
-(defmethod emit-constant nil [x] (emits "null"))
+(defmethod emit-constant* nil [x] (emits "null"))
 
 #?(:clj
-   (defmethod emit-constant Long [x] (emits "(" x ")")))
+   (defmethod emit-constant* Long [x] (emits "(" x ")")))
 
 #?(:clj
-   (defmethod emit-constant Integer [x] (emits x))) ; reader puts Integers in metadata
+   (defmethod emit-constant* Integer [x] (emits x))) ; reader puts Integers in metadata
 
 #?(:clj
-   (defmethod emit-constant Double [x]
+   (defmethod emit-constant* Double [x]
      (let [x (double x)]
        (cond (Double/isNaN x)
              (emits "NaN")
@@ -258,7 +285,7 @@
 
              :else (emits x))))
    :cljs
-   (defmethod emit-constant js/Number [x]
+   (defmethod emit-constant* js/Number [x]
      (cond (js/isNaN x)
            (emits "NaN")
 
@@ -268,21 +295,21 @@
            :else (emits "(" x ")"))))
 
 #?(:clj
-   (defmethod emit-constant BigDecimal [x] (emits (.doubleValue ^BigDecimal x))))
+   (defmethod emit-constant* BigDecimal [x] (emits (.doubleValue ^BigDecimal x))))
 
 #?(:clj
-   (defmethod emit-constant clojure.lang.BigInt [x] (emits (.doubleValue ^clojure.lang.BigInt x))))
+   (defmethod emit-constant* clojure.lang.BigInt [x] (emits (.doubleValue ^clojure.lang.BigInt x))))
 
-(defmethod emit-constant #?(:clj String :cljs js/String) [x]
+(defmethod emit-constant* #?(:clj String :cljs js/String) [x]
   (emits (wrap-in-double-quotes (escape-string x))))
 
-(defmethod emit-constant #?(:clj Boolean :cljs js/Boolean) [x] (emits (if x "true" "false")))
+(defmethod emit-constant* #?(:clj Boolean :cljs js/Boolean) [x] (emits (if x "true" "false")))
 
 #?(:clj
-   (defmethod emit-constant Character [x]
+   (defmethod emit-constant* Character [x]
      (emits (wrap-in-double-quotes (escape-char x)))))
 
-(defmethod emit-constant #?(:clj java.util.regex.Pattern :cljs js/RegExp) [x]
+(defmethod emit-constant* #?(:clj java.util.regex.Pattern :cljs js/RegExp) [x]
   (if (= "" (str x))
     (emits "(new RegExp(\"\"))")
     (let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" (str x))]
@@ -324,27 +351,44 @@
     (emit-constant nil)
     (emits ")")))
 
-(defmethod emit-constant #?(:clj clojure.lang.Keyword :cljs Keyword) [x]
+(defmethod emit-constant* #?(:clj clojure.lang.Keyword :cljs Keyword) [x]
   (if-let [value (and (-> @env/*compiler* :options :emit-constants)
                       (-> @env/*compiler* ::ana/constant-table x))]
     (emits "cljs.core." value)
     (emits-keyword x)))
 
-(defmethod emit-constant #?(:clj clojure.lang.Symbol :cljs Symbol) [x]
+(defmethod emit-constant* #?(:clj clojure.lang.Symbol :cljs Symbol) [x]
   (if-let [value (and (-> @env/*compiler* :options :emit-constants)
                       (-> @env/*compiler* ::ana/constant-table x))]
     (emits "cljs.core." value)
     (emits-symbol x)))
 
+(defn emit-constants-comma-sep [cs]
+  (fn []
+    (doall
+      (map-indexed (fn [i m]
+                     (if (even? i)
+                       (emit-constant m)
+                       (emits m)))
+                   (comma-sep cs)))))
+
+(def ^:private array-map-threshold 8)
+
 ;; tagged literal support
 
-(defmethod emit-constant #?(:clj java.util.Date :cljs js/Date) [^java.util.Date date]
+(defmethod emit-constant* #?(:clj java.util.Date :cljs js/Date) [^java.util.Date date]
   (emits "new Date(" (.getTime date) ")"))
 
-(defmethod emit-constant #?(:clj java.util.UUID :cljs UUID) [^java.util.UUID uuid]
+(defmethod emit-constant* #?(:clj java.util.UUID :cljs UUID) [^java.util.UUID uuid]
   (let [uuid-str (.toString uuid)]
     (emits "new cljs.core.UUID(\"" uuid-str "\", " (hash uuid-str) ")")))
 
+(defmethod emit-constant* #?(:clj JSValue :cljs cljs.tagged-literals/JSValue) [^JSValue v]
+  (let [items (.-val v)]
+    (if (map? items)
+      (emit-js-object items #(fn [] (emit-constant %)))
+      (emit-js-array items emit-constants-comma-sep))))
+
 #?(:clj
    (defmacro emit-wrap [env & body]
      `(let [env# ~env]
@@ -406,61 +450,79 @@
       (emits "new cljs.core.Var(function(){return " (munge name) ";},"
         sym "," meta ")"))))
 
+(defn emit-with-meta [expr meta]
+  (emits "cljs.core.with_meta(" expr "," meta ")"))
+
 (defmethod emit* :with-meta
   [{:keys [expr meta env]}]
   (emit-wrap env
-    (emits "cljs.core.with_meta(" expr "," meta ")")))
-
-(def ^:private array-map-threshold 8)
+    (emit-with-meta expr meta)))
 
 (defn distinct-keys? [keys]
-  (and (every? #(= (:op %) :const) keys)
-       (= (count (into #{} keys)) (count keys))))
+  (let [keys (map ana/unwrap-quote keys)]
+    (and (every? #(= (:op %) :const) keys)
+         (= (count (into #{} keys)) (count keys)))))
 
-(defmethod emit* :map
-  [{:keys [env keys vals]}]
-  (emit-wrap env
-    (cond
-      (zero? (count keys))
-      (emits "cljs.core.PersistentArrayMap.EMPTY")
-
-      (<= (count keys) array-map-threshold)
-      (if (distinct-keys? keys)
-        (emits "new cljs.core.PersistentArrayMap(null, " (count keys) ", ["
-          (comma-sep (interleave keys vals))
-          "], null)")
-        (emits "cljs.core.PersistentArrayMap.createAsIfByAssoc(["
-          (comma-sep (interleave keys vals))
-          "])"))
+(defn emit-map [keys vals comma-sep distinct-keys?]
+  (cond
+    (zero? (count keys))
+    (emits "cljs.core.PersistentArrayMap.EMPTY")
+
+    (<= (count keys) array-map-threshold)
+    (if (distinct-keys? keys)
+      (emits "new cljs.core.PersistentArrayMap(null, " (count keys) ", ["
+        (comma-sep (interleave keys vals))
+        "], null)")
+      (emits "cljs.core.PersistentArrayMap.createAsIfByAssoc(["
+        (comma-sep (interleave keys vals))
+        "])"))
 
-      :else
-      (emits "cljs.core.PersistentHashMap.fromArrays(["
-        (comma-sep keys)
-        "],["
-        (comma-sep vals)
-        "])"))))
+    :else
+    (emits "cljs.core.PersistentHashMap.fromArrays(["
+      (comma-sep keys)
+      "],["
+      (comma-sep vals)
+      "])")))
 
-(defmethod emit* :list
-  [{:keys [items env]}]
+(defmethod emit* :map
+  [{:keys [env keys vals]}]
   (emit-wrap env
-    (if (empty? items)
-      (emits "cljs.core.List.EMPTY")
-      (emits "cljs.core.list(" (comma-sep items) ")"))))
+    (emit-map keys vals comma-sep distinct-keys?)))
+
+(defn emit-list [items comma-sep]
+  (if (empty? items)
+    (emits "cljs.core.List.EMPTY")
+    (emits "cljs.core.list(" (comma-sep items) ")")))
+
+(defn emit-vector [items comma-sep]
+  (if (empty? items)
+    (emits "cljs.core.PersistentVector.EMPTY")
+    (let [cnt (count items)]
+      (if (< cnt 32)
+        (emits "new cljs.core.PersistentVector(null, " cnt
+          ", 5, cljs.core.PersistentVector.EMPTY_NODE, ["  (comma-sep items) "], null)")
+        (emits "cljs.core.PersistentVector.fromArray([" (comma-sep items) "], true)")))))
 
 (defmethod emit* :vector
   [{:keys [items env]}]
   (emit-wrap env
-    (if (empty? items)
-      (emits "cljs.core.PersistentVector.EMPTY")
-      (let [cnt (count items)]
-        (if (< cnt 32)
-          (emits "new cljs.core.PersistentVector(null, " cnt
-            ", 5, cljs.core.PersistentVector.EMPTY_NODE, ["  (comma-sep items) "], null)")
-          (emits "cljs.core.PersistentVector.fromArray([" (comma-sep items) "], true)"))))))
+    (emit-vector items comma-sep)))
 
 (defn distinct-constants? [items]
-  (and (every? #(= (:op %) :const) items)
-       (= (count (into #{} items)) (count items))))
+  (let [items (map ana/unwrap-quote items)]
+    (and (every? #(= (:op %) :const) items)
+         (= (count (into #{} items)) (count items)))))
+
+(defn emit-set [items comma-sep]
+  (cond
+    (empty? items)
+    (emits "cljs.core.PersistentHashSet.EMPTY")
+
+    (distinct-constants? items)
+    (emits "new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, " (count items) ", ["
+      (comma-sep (interleave items (repeat "null"))) "], null), null)")
+
+    :else (emits "cljs.core.PersistentHashSet.createAsIfByAssoc([" (comma-sep items) "])")))
 
 (defmethod emit* :set
   [{:keys [items env]}]
@@ -475,51 +537,57 @@
 
       :else (emits "cljs.core.PersistentHashSet.createAsIfByAssoc([" (comma-sep items) "])"))))
 
-(defn emit-js-object [items]
+(defn emit-js-object [items emit-js-object-val]
   (emits "({")
   (when-let [items (seq items)]
     (let [[[k v] & r] items]
-      (emits "\"" (name k) "\": " v)
+      (emits "\"" (name k) "\": " (emit-js-object-val v))
       (doseq [[k v] r]
-        (emits ", \"" (name k) "\": " v))))
+        (emits ", \"" (name k) "\": " (emit-js-object-val v)))))
   (emits "})"))
 
-(defn emit-js-array [items]
+(defn emit-js-array [items comma-sep]
   (emits "[" (comma-sep items) "]"))
 
 (defmethod emit* :js-object 
   [{:keys [keys vals env]}]
   (emit-wrap env
-    (emit-js-object (map vector keys vals))))
+    (emit-js-object (map vector keys vals) identity)))
 
 (defmethod emit* :js-array 
   [{:keys [items env]}]
   (emit-wrap env
-    (emit-js-array items)))
+    (emit-js-array items comma-sep)))
 
 (defmethod emit* :record-value
   [{:keys [items ns name items env]}]
   (emit-wrap env
     (emits ns ".map__GT_" name "(" items ")")))
 
+(defmethod emit* :quote
+  [{:keys [expr]}]
+  (emit expr))
+
 (defmethod emit* :const
   [{:keys [form env]}]
   (when-not (= :statement (:context env))
     (emit-wrap env (emit-constant form))))
 
-(defn truthy-constant? [{:keys [op form const-expr]}]
-  (or (and (= op :const)
-           form
-           (not (or (and (string? form) (= form ""))
-                    (and (number? form) (zero? form)))))
-      (and (some? const-expr)
-           (truthy-constant? const-expr))))
-
-(defn falsey-constant? [{:keys [op form const-expr]}]
-  (or (and (= op :const)
-           (or (false? form) (nil? form)))
-      (and (some? const-expr)
-           (falsey-constant? const-expr))))
+(defn truthy-constant? [expr]
+  (let [{:keys [op form const-expr]} (ana/unwrap-quote expr)]
+    (or (and (= op :const)
+             form
+             (not (or (and (string? form) (= form ""))
+                      (and (number? form) (zero? form)))))
+        (and (some? const-expr)
+             (truthy-constant? const-expr)))))
+
+(defn falsey-constant? [expr]
+  (let [{:keys [op form const-expr]} (ana/unwrap-quote expr)]
+    (or (and (= op :const)
+             (or (false? form) (nil? form)))
+        (and (some? const-expr)
+             (falsey-constant? const-expr)))))
 
 (defn safe-test? [env e]
   (let [tag (ana/infer-tag env e)]
@@ -951,7 +1019,7 @@
         (when name
           (emits "catch (" (munge name) "){" catch "}"))
         (when finally
-          (assert (not= :const (:op finally)) "finally block cannot contain constant")
+          (assert (not= :const (:op (ana/unwrap-quote finally))) "finally block cannot contain constant")
           (emits "finally {" finally "}"))
         (when (= :expr context)
           (emits "})()")))
@@ -1042,8 +1110,9 @@
                     (not (contains? (::ana/namespaces @env/*compiler*) ns))))
 
         keyword? (or (= 'cljs.core/Keyword (ana/infer-tag env f))
-                     (and (= (-> f :op) :const)
-                          (keyword? (-> f :form))))
+                     (let [f (ana/unwrap-quote f)]
+                       (and (= (-> f :op) :const)
+                            (keyword? (-> f :form)))))
         [f variadic-invoke]
         (if fn?
           (let [arity (count args)
diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 34b1339a0..cbf259214 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -845,7 +845,7 @@
 
 (core/defn- simple-test-expr? [env ast]
   (core/and
-    (#{:var :invoke :const :host-field :host-call :js} (:op ast))
+    (#{:var :invoke :const :host-field :host-call :js :quote} (:op ast))
     ('#{boolean seq} (cljs.analyzer/infer-tag env ast))))
 
 (core/defmacro and
@@ -2530,7 +2530,7 @@
   ([] '(.-EMPTY cljs.core/PersistentArrayMap))
   ([& kvs]
    (core/let [keys (map first (partition 2 kvs))]
-     (if (core/and (every? #(= (:op %) :const)
+     (if (core/and (every? #(= (:op (cljs.analyzer/unwrap-quote %)) :const)
                      (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) keys))
            (= (count (into #{} keys)) (count keys)))
        `(cljs.core/PersistentArrayMap. nil ~(clojure.core// (count kvs) 2) (array ~@kvs) nil)
@@ -2550,7 +2550,7 @@
   ([] `(.-EMPTY cljs.core/PersistentHashSet))
   ([& xs]
     (if (core/and (core/<= (count xs) 8)
-                  (every? #(= (:op %) :const)
+                  (every? #(= (:op (cljs.analyzer/unwrap-quote %)) :const)
                     (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) xs))
                   (= (count (into #{} xs)) (count xs)))
       `(cljs.core/PersistentHashSet. nil
diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs
index a59818e21..ea9767ae1 100644
--- a/src/test/self/self_host/test.cljs
+++ b/src/test/self/self_host/test.cljs
@@ -742,7 +742,9 @@
          :context :expr}
         (fn [{:keys [error value]}]
           (is (nil? error))
-          (is (= '.x (:form value)))
+          (is (= :quote (:op value)))
+          (is (= ''.x (:form value)))
+          (is (= '.x (-> value :expr :form)))
           (inc! l)))
       (cljs/compile-str st
         "`.x"
diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs
index 55023d828..cd57d4d4b 100644
--- a/src/test/self/self_parity/test.cljs
+++ b/src/test/self/self_parity/test.cljs
@@ -183,6 +183,7 @@
        'goog.array
        'cljs.core
        'cljs.env
+       'cljs.tagged-literals
        'cljs.tools.reader
        'clojure.walk}) name))
 

From 5aaf6db41b0eebc59f6dd20b3816cf3a16d05a7e Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Thu, 5 Jul 2018 12:13:33 -0400
Subject: [PATCH 3230/4033] CLJS-2807: Macroexpand failure with set literal

---
 src/main/clojure/cljs/analyzer.cljc |  2 +-
 src/main/clojure/cljs/compiler.cljc | 23 +++++++++--------------
 src/test/cljs/cljs/core_test.cljs   |  4 ++++
 3 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 83340f84b..826481de3 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -3543,7 +3543,7 @@
     (analyze-wrap-meta {:op :vector :env env :form form :items items :children [:items] :tag 'cljs.core/IVector})))
 
 (defn analyze-set
-  [env form ]
+  [env form]
   (let [expr-env (assoc env :context :expr)
         items (disallowing-recur (mapv #(analyze expr-env %) form))]
     (analyze-wrap-meta {:op :set :env env :form form :items items :children [:items] :tag 'cljs.core/ISet})))
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index d177d885a..0d20ce56b 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -235,21 +235,24 @@
 (declare emit-map emit-list emit-vector emit-set emit-js-object emit-js-array
          emit-with-meta emit-constants-comma-sep emit-constant)
 
+(defn all-distinct? [xs]
+  (apply distinct? xs))
+
 #?(:clj
    (defn emit-constant-no-meta [x]
      (cond
        (seq? x) (emit-list x emit-constants-comma-sep)
-       (map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep #(apply distinct? %))
+       (map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep all-distinct?)
        (vector? x) (emit-vector x emit-constants-comma-sep)
-       (set? x) (emit-set x emit-constants-comma-sep)
+       (set? x) (emit-set x emit-constants-comma-sep all-distinct?)
        :else (emit-constant* x)))
    :cljs
    (defn emit-constant-no-meta [x]
      (cond
        (ana/cljs-seq? x) (emit-list x emit-constants-comma-sep)
-       (ana/cljs-map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep #(apply distinct? %))
+       (ana/cljs-map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep all-distinct?)
        (ana/cljs-vector? x) (emit-vector x emit-constants-comma-sep)
-       (ana/cljs-set? x) (emit-set x emit-constants-comma-sep)
+       (ana/cljs-set? x) (emit-set x emit-constants-comma-sep all-distinct?)
        :else (emit-constant* x))))
 
 (defn emit-constant [v]
@@ -513,7 +516,7 @@
     (and (every? #(= (:op %) :const) items)
          (= (count (into #{} items)) (count items)))))
 
-(defn emit-set [items comma-sep]
+(defn emit-set [items comma-sep distinct-constants?]
   (cond
     (empty? items)
     (emits "cljs.core.PersistentHashSet.EMPTY")
@@ -527,15 +530,7 @@
 (defmethod emit* :set
   [{:keys [items env]}]
   (emit-wrap env
-    (cond
-      (empty? items)
-      (emits "cljs.core.PersistentHashSet.EMPTY")
-
-      (distinct-constants? items)
-      (emits "new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, " (count items) ", ["
-        (comma-sep (interleave items (repeat "null"))) "], null), null)")
-
-      :else (emits "cljs.core.PersistentHashSet.createAsIfByAssoc([" (comma-sep items) "])"))))
+    (emit-set items comma-sep distinct-constants?)))
 
 (defn emit-js-object [items emit-js-object-val]
   (emits "({")
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index 6c30b6593..febf3398c 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -1607,3 +1607,7 @@
   (let [x (map->CLJS-2787 {1 2})
         y (map->CLJS-2787 x)]
     (is (= x y))))
+
+(deftest test-cljs-2807
+  (testing "Quoted sets should work"
+    (is (macroexpand '(fn [x] #{(into [] x)})))))

From 2072f454cf91d9278ed0aeb3761aa2f383fb9fd9 Mon Sep 17 00:00:00 2001
From: Mike Fikes 
Date: Thu, 5 Jul 2018 06:46:28 -0400
Subject: [PATCH 3231/4033] CLJS-2805: Bump tools.reader to 1.3.0

---
 deps.edn         | 2 +-
 pom.template.xml | 2 +-
 project.clj      | 2 +-
 script/bootstrap | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/deps.edn b/deps.edn
index 2f4241f00..75d194308 100644
--- a/deps.edn
+++ b/deps.edn
@@ -1,7 +1,7 @@
 {:paths ["src/main/clojure" "src/main/cljs" "resources"]
  :deps
  {org.clojure/clojure {:mvn/version "1.9.0"}
-  org.clojure/tools.reader {:mvn/version "1.3.0-alpha3"}
+  org.clojure/tools.reader {:mvn/version "1.3.0"}
   org.clojure/test.check {:mvn/version "0.10.0-alpha2"}
   org.clojure/spec.alpha {:mvn/version "0.1.143"}
   org.clojure/core.specs.alpha {:mvn/version "0.1.24"}
diff --git a/pom.template.xml b/pom.template.xml
index 7a36e4ada..6a25f27d4 100644
--- a/pom.template.xml
+++ b/pom.template.xml
@@ -50,7 +50,7 @@
         
             org.clojure
             tools.reader
-            1.3.0-alpha3
+            1.3.0
         
         
             com.cognitect
diff --git a/project.clj b/project.clj
index f40fd819d..1cb39c794 100644
--- a/project.clj
+++ b/project.clj
@@ -12,7 +12,7 @@
                  [org.clojure/spec.alpha "0.1.143"]
                  [org.clojure/core.specs.alpha "0.1.24"]
                  [org.clojure/data.json "0.2.6"]
-                 [org.clojure/tools.reader "1.3.0-alpha3"]
+                 [org.clojure/tools.reader "1.3.0"]
                  [org.clojure/test.check "0.10.0-alpha2" :scope "test"]
                  [com.cognitect/transit-clj "0.8.309"]
                  [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"]
diff --git a/script/bootstrap b/script/bootstrap
index 482229c08..320bf17ac 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6"
 TRANSIT_RELEASE="0.8.309"
 GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b"
 RHINO_RELEASE="1_7R5"
-TREADER_RELEASE="1.3.0-alpha3"
+TREADER_RELEASE="1.3.0"
 TEST_CHECK_RELEASE="0.10.0-alpha2"
 
 # check dependencies

From 1cd76502d853e85d2ca30a6bf2ac50846cfddd28 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Wed, 4 Jul 2018 14:18:53 -0400
Subject: [PATCH 3232/4033] CLJS-2803: Remove :record-value op and add record
 literal tests

---
 src/main/clojure/cljs/analyzer.cljc          | 25 ++++++++++----------
 src/main/clojure/cljs/compiler.cljc          | 13 ++++++----
 src/test/cljs/data_readers_test/records.cljc | 22 ++++++++++++++++-
 3 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 826481de3..1ee7791e5 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -3572,23 +3572,24 @@
          :children [:items]
          :tag 'array}))))
 
+(defn record-ns+name [x]
+  (map symbol
+       #?(:clj
+          ((juxt (comp #(string/join "." %) butlast) last)
+           (string/split (.getName ^Class (type x)) #"\."))
+          :cljs
+          (string/split (pr-str (type x)) #"/"))))
+
 (defn analyze-record
   [env x]
-  (let [items     (disallowing-recur
+  (let [;; register constansts
+        _items_   (disallowing-recur
                     (analyze (assoc env :context :expr) (into {} x)))
-        [ns name] (map symbol
-                    #?(:clj
-                       ((juxt (comp #(string/join "." %) butlast) last)
-                         (string/split (.getName ^Class (type x)) #"\."))
-                       :cljs
-                       (string/split (pr-str (type x)) #"/")))]
-    {:op :record-value
-     :ns ns
-     :name name
+        [ns name] (record-ns+name x)]
+    {:op :const
+     :val x
      :env env
      :form x
-     :items items
-     :children [:items]
      :tag (symbol (str ns) (str name))}))
 
 (defn elide-reader-meta [m]
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 0d20ce56b..5fef5ff49 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -233,7 +233,7 @@
    (defmulti emit-constant* type))
 
 (declare emit-map emit-list emit-vector emit-set emit-js-object emit-js-array
-         emit-with-meta emit-constants-comma-sep emit-constant)
+         emit-with-meta emit-constants-comma-sep emit-constant emit-record-value)
 
 (defn all-distinct? [xs]
   (apply distinct? xs))
@@ -242,6 +242,8 @@
    (defn emit-constant-no-meta [x]
      (cond
        (seq? x) (emit-list x emit-constants-comma-sep)
+       (record? x) (let [[ns name] (ana/record-ns+name x)]
+                     (emit-record-value ns name #(emit-constant (into {} x))))
        (map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep all-distinct?)
        (vector? x) (emit-vector x emit-constants-comma-sep)
        (set? x) (emit-set x emit-constants-comma-sep all-distinct?)
@@ -250,6 +252,8 @@
    (defn emit-constant-no-meta [x]
      (cond
        (ana/cljs-seq? x) (emit-list x emit-constants-comma-sep)
+       (record? x) (let [[ns name] (ana/record-ns+name x)]
+                     (emit-record-value ns name #(emit-constant (into {} x))))
        (ana/cljs-map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep all-distinct?)
        (ana/cljs-vector? x) (emit-vector x emit-constants-comma-sep)
        (ana/cljs-set? x) (emit-set x emit-constants-comma-sep all-distinct?)
@@ -554,10 +558,9 @@
   (emit-wrap env
     (emit-js-array items comma-sep)))
 
-(defmethod emit* :record-value
-  [{:keys [items ns name items env]}]
-  (emit-wrap env
-    (emits ns ".map__GT_" name "(" items ")")))
+(defn emit-record-value
+  [ns name items]
+  (emits ns ".map__GT_" name "(" items ")"))
 
 (defmethod emit* :quote
   [{:keys [expr]}]
diff --git a/src/test/cljs/data_readers_test/records.cljc b/src/test/cljs/data_readers_test/records.cljc
index c622c7bff..818aca0d6 100644
--- a/src/test/cljs/data_readers_test/records.cljc
+++ b/src/test/cljs/data_readers_test/records.cljc
@@ -3,4 +3,24 @@
 
 (defrecord Foo [a b])
 
-(assert (= (Foo. 1 2) #data_readers_test.records.Foo{:a 1 :b 2}))
\ No newline at end of file
+(assert (= (Foo. 1 2) #data_readers_test.records.Foo{:a 1 :b 2}))
+(assert (= (Foo. 1 2)
+           '#data_readers_test.records.Foo{:a 1 :b 2}))
+(assert (= (Foo. 1 2)
+           (second ''#data_readers_test.records.Foo{:a 1 :b 2})))
+(assert (= (Foo. 'a 'b)
+           (let [a 1
+                 b 2]
+             #data_readers_test.records.Foo{:a a :b b}))
+        (pr-str
+          (let [a 1
+                b 2]
+            #data_readers_test.records.Foo{:a a :b b})))
+(assert (= (Foo. 'a 'b)
+           (let [a 1
+                 b 2]
+             '#data_readers_test.records.Foo{:a a :b b}))
+        (pr-str
+          (let [a 1
+                b 2]
+            '#data_readers_test.records.Foo{:a a :b b})))

From 6cbd40f865132b8b13c6b902b715aed43e64f0b1 Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant 
Date: Sun, 1 Jul 2018 12:35:01 -0400
Subject: [PATCH 3233/4033] CLJS-2257: Split :var op, desugar dotted symbols
 and add tests+doc for analyzer

- :var/:binding/:local/:js-var
  - split :var op into :var/:binding/:local/:js-var
  - desugar dotted :var/:local symbols
    - ensure :context is correctly updated to avoid multiple `return`s
      - Tested: cljs.core-test/var-desugar-test
  - emit :local op if :js-shadowed-by-local
  - change :local to be #{:fn :letfn :let :arg ...}
  - add :arg-id
  - workaround for core.async's updating of :locals
    - see cljs.analyzer/handle-symbol-local
    - Test: cljs.analyzer-tests/test-locals-mapped-to-sym
- argument validation in 'var parsing
  - Tested: cljs.analyzer-tests/var-args-error-test
- :body? entries for synthetic `do` blocks
- Add analyzer Unit tests
  - cljs.analyzer-tests/analyze-ops
- Add AST documentation
  - based on tools.analyzer's, see ast-ref/ folder

========
Commits:
========

make :let/:loop/:letfn :binding ops

make :fn-method :binding ops

add :js-var and :local ops

use :local op if :js-shadowed-by-local

change :local to be #{:fn :letfn :let :arg ...}

merge :info into analyze-symbol output

add unit tests

desugar dotted symbols

don't use symbol namespaces to store :local

desugar :var

unit tests

argument validation in 'var parsing

bad (var sym) form tests

ast ref

add :body? labels

alpha

tweak doc

nicer diff

update doc

work around core.async's :locals extensions

update :context when desugaring dotted vars

add test for core.async workaround

remove quickref.html
---
 ast-ref/ast-ref.edn                      | 313 ++++++++++++
 ast-ref/buildref.sh                      |  15 +
 ast-ref/gen-ref.clj                      |  62 +++
 ast-ref/quickref.html.tpl                | 110 ++++
 src/main/clojure/cljs/analyzer.cljc      | 186 +++++--
 src/main/clojure/cljs/compiler.cljc      |  11 +-
 src/main/clojure/cljs/core.cljc          |   2 +-
 src/test/cljs/cljs/core_test.cljs        |   8 +
 src/test/clojure/cljs/analyzer_tests.clj | 612 ++++++++++++++++++++++-
 src/test/clojure/cljs/compiler_tests.clj |   4 +-
 10 files changed, 1260 insertions(+), 63 deletions(-)
 create mode 100644 ast-ref/ast-ref.edn
 create mode 100755 ast-ref/buildref.sh
 create mode 100644 ast-ref/gen-ref.clj
 create mode 100644 ast-ref/quickref.html.tpl

diff --git a/ast-ref/ast-ref.edn b/ast-ref/ast-ref.edn
new file mode 100644
index 000000000..a0ca1a42b
--- /dev/null
+++ b/ast-ref/ast-ref.edn
@@ -0,0 +1,313 @@
+{:all-keys
+
+ [[:op "The node op"]
+  [:form "The ClojureScript form from which the node originated"]
+  [:env "The environment map"]
+  [:context "Either :expr, :return or :statement."]
+  ^:optional
+  [:children "A vector of keywords, representing the children nodes of this node, in order of evaluation"]
+;  ^:optional
+;  [:raw-forms "If this node's :form has been macroexpanded, a sequence of all the intermediate forms from the original form to the macroexpanded form"]
+  ;^:optional
+  ;[:top-level "`true` if this is the root node"]
+  [:tag "The tag this expression is required to have"]
+;  [:o-tag "The tag of this expression, based on the node's children"]
+;  ^:optional
+;  [:ignore-tag "`true` if this node returns a statement rather than an expression"]
+ ; ^:optional
+ ; [:loops "A set of the loop-ids that might cause this node to recur"]
+  ]
+
+ :node-keys
+ [{:op   :binding
+   :doc  "Node for a binding symbol"
+   :keys [[:form "The binding symbol"]
+          [:name "The binding symbol"]
+          [:local "One of :arg, :catch, :fn, :let, :letfn, :loop or :field"]
+          ^:optional
+          [:variadic? "When :local is :arg, a boolean indicating whether this parameter binds to a variable number of arguments"]
+          ^:optional ^:children
+          [:init "When :local is :let, :letfn or :loop, an AST node representing the bound value"]
+          ^:optional ;^:children
+          [:shadow "When this binding shadows another local binding, an AST node representing the shadowed local"]
+          ]}
+  {:op   :case
+   :doc  "Node for a case* special-form expression"
+   :keys [[:form "`(case* expr shift maks default case-map switch-type test-type skip-check?)`"]
+          ^:children
+          [:test "The AST node for the expression to test against"]
+          ^:children
+          [:nodes "A vector of :case-node AST nodes representing the test/then clauses of the case* expression"]
+          ^:children
+          [:default "An AST node representing the default value of the case expression"]
+          ]}
+  {:op   :case-node
+   :doc  "Grouping node for tests/then expressions in a case* expression"
+   :keys [^:children
+          [:tests "A vector of :case-test AST nodes representing the test values"]
+          ^:children
+          [:then "A :case-then AST node representing the value the case expression will evaluate to when one of the :tests expressions matches the :case :test value"]]}
+  {:op   :case-test
+   :doc  "Node for a test value in a case* expression"
+   :keys [^:children
+          [:test "A :const AST node representing the test value"]
+          #_[:hash]]}
+  {:op   :case-then
+   :doc  "Node for a then expression in a case* expression"
+   :keys [^:children
+          [:then "An AST node representing the expression the case will evaluate to when the :test expression matches this node's corresponding :case-test value"]
+          #_[:hash]]}
+  {:op   :const
+   :doc   "Node for a constant literal or a quoted collection literal"
+   :keys [[:form "A constant literal or a quoted collection literal"]
+          [:literal? "`true`"]
+          [:type "one of :nil, :bool, :keyword, :symbol, :string, :number, :type, :record, :map, :vector, :set, :seq, :char, :regex, :class, :var, or :unknown"]
+          [:val "The value of the constant node"]
+          ;^:optional ^:children
+          ;; FIXME
+          ;[:meta "An AST node representing the metadata of the constant value, if present. The node will be either a :map node or a :const node with :type :map"]
+          ;
+          ;^:optional
+          ;[:id "A numeric id for the constant value, will be the same for every instance of this constant inside the same compilation unit, not present if :type is :nil or :bool"]
+          ]}
+  {:op   :def
+   :doc  "Node for a def special-form expression"
+   :keys [[:form "`(def name docstring? init?)`"]
+          [:name "The var symbol to define in the current namespace"]
+          ;[:var "The Var object created (or found, if it already existed) named by the symbol :name in the current namespace"]
+          ;^:optional ^:children
+          ;[:meta "An AST node representing the metadata attached to :name, if present. The node will be either a :map node or a :const node with :type :map"]
+          ^:optional ^:children
+          [:init "An AST node representing the initial value of the var"]
+          ^:children
+          [:the-var "A :the-var AST node representing the return of this :def."]
+          ;^:optional
+          ;[:doc "The docstring for this var"]
+          ]}
+  {:op   :defrecord
+   :doc  "Node for a defrecord* special-form expression"
+   :keys [[:form "`(deftype* name class.name [arg*] :implements [interface*] method*)`"]
+          ;[:interfaces "A set of the interfaces implemented by the type"]
+          [:t "The symbol name of the defrecord."]
+          ^:children
+          [:body "An AST node containing method implementations for this record."]
+          ;^:children
+          ;[:fields "A vector of :binding AST nodes with :local :field representing the deftype fields"]
+          ]}
+  {:op   :deftype
+   :doc  "Node for a deftype* special-form expression"
+   :keys [[:form "`(deftype* name class.name [arg*] :implements [interface*] method*)`"]
+          ;[:interfaces "A set of the interfaces implemented by the type"]
+          [:t "The symbol name of the deftype"]
+          ;[:class-name "A class for the deftype, should *never* be instantiated or used on instance? checks as this will not be the same class the deftype will evaluate to after compilation"]
+          ^:children
+          [:body "An AST node containing method implemented for this type."]
+          ;^:children
+          ;[:fields "A vector of :binding AST nodes with :local :field representing the deftype fields"]
+          ]}
+  {:op   :do
+   :doc  "Node for a do special-form expression or for another special-form's body"
+   :keys [[:form "`(do statement* ret)`"]
+          ^:children
+          [:statements "A vector of AST nodes representing all but the last expression in the do body"]
+          ^:children
+          [:ret "An AST node representing the last expression in the do body (the block's return value)"]
+          ^:optional
+          [:body? "`true` if this node is a synthetic body"]]}
+  {:op   :fn
+   :doc  "Node for a fn* special-form expression"
+   :keys [[:form "`(fn* name? [arg*] body*)` or `(fn* name? method*)`"]
+          [:variadic? "`true` if this function contains a variadic arity method"]
+          [:max-fixed-arity "The number of arguments taken by the fixed-arity method taking the most arguments"]
+          ^:optional ^:children
+          [:local "A :binding AST node with :local :fn representing the function's local name, if one is supplied"]
+          ^:children
+          [:methods "A vector of :fn-method AST nodes representing the fn method arities"]
+          ]}
+  {:op   :fn-method
+   :doc  "Node for an arity method in a fn* expression"
+   :keys [[:form "`([arg*] body*)`"]
+          [:variadic? "`true` if this fn-method takes a variable number of arguments"]
+          ^:children
+          [:params "A vector of :binding AST nodes with :local :arg representing this fn-method args"]
+          [:fixed-arity "The number of non-variadic args this fn-method takes"]
+          ^:children
+          [:body "Synthetic :do node (with :body? `true`) representing the body of this fn-method"]]}
+  {:op   :host-call
+   :doc  "Node for a host interop call"
+   :keys [[:form "`(.method target arg*)`"]
+          [:method "Symbol naming the method to call"]
+          ^:children
+          [:target "An AST node representing the target object"]
+          ^:children
+          [:args "A vector of AST nodes representing the args passed to the method call"]]}
+  {:op   :host-field
+   :doc  "Node for a host interop field access"
+   :keys [[:form "`(.-field target)`"]
+          [:field "Symbol naming the field to access"]
+          ^:children
+          [:target "An AST node representing the target object"]]}
+  {:op   :if
+   :doc  "Node for an if special-form expression"
+   :keys [[:form "`(if test then else?)`"]
+          ^:children
+          [:test "An AST node representing the test expression"]
+          ^:children
+          [:then "An AST node representing the expression's return value if :test evaluated to a truthy value"]
+          ^:children
+          [:else "An AST node representing the expression's return value if :test evaluated to a falsey value, if not supplied it will default to a :const node representing nil"]]}
+  {:op   :invoke
+   :doc  "Node for an invoke expression"
+   :keys [[:form "`(f arg*)`"]
+          ^:children
+          [:fn "An AST node representing the function to invoke"]
+          ^:children
+          [:args "A vector of AST nodes representing the args to the function"]
+          ;FIXME
+          ;^:optional
+          ;[:meta "Map of metadata attached to the invoke :form"]
+          ]}
+  {:op   :js
+   :doc  "Node for a js* special-form expression"
+   :keys [[:form "`(js* js-string arg*)`"]
+          [:segs "A vector of js strings that delimit the compiled args"]
+          ^:children
+          [:args "A vector of AST nodes representing the cljs expressions that will be interposed with the strings in segs"]]}
+  {:op   :js-array
+   :doc  "Node for a js array literal"
+   :keys [[:form "`#js [item*]`"]
+          ^:children
+          [:items "A vector of AST nodes representing the items of the js array"]]}
+  {:op   :js-object
+   :doc  "Node for a js object literal"
+   :keys [[:form "`#js {[key value]*}`"]
+          [:keys "A vector of values representing the keys of the js object"]
+          ^:children
+          [:vals "A vector of AST nodes representing the vals of the js object"]]}
+  {:op   :js-var
+   :doc  "Node for a js-var symbol"
+   :keys [[:form "A symbol naming the js-var in the form: `js/foo`, `js-ns/foo` or `js-var`"]
+          [:ns "The namespace symbol for this js-var."]
+          [:name "The fully qualified symbol naming this js-var."]
+          ]}
+  {:op   :let
+   :doc  "Node for a let* special-form expression"
+   :keys  [[:form "`(let* [binding*] body*)`"]
+           ^:children
+           [:bindings "A vector of :binding AST nodes with :local :let"]
+           ^:children
+           [:body "Synthetic :do node (with :body? `true`) representing the body of the let expression"]]}
+  {:op   :letfn
+   :doc  "Node for a letfn* special-form expression"
+   :keys  [[:form "`(letfn* [binding*] body*)`"]
+           ^:children
+           [:bindings "A vector of :binding AST nodes with :local :letfn"]
+           ^:children
+           [:body "Synthetic :do node (with :body? `true`) representing the body of the letfn expression"]]}
+  {:op   :local
+   :doc  "Node for a local symbol"
+   :keys [[:form "The local symbol"]
+          [:name "The uniquified local symbol"]
+          [:local "One of :arg, :catch, :fn, :let, :letfn, :loop, :field or :this"]
+          ]}
+  {:op   :loop
+   :doc  "Node a loop* special-form expression"
+   :keys [[:form "`(loop* [binding*] body*)`"]
+          ^:children
+          [:bindings "A vector of :binding AST nodes with :local :loop"]
+          ^:children
+          [:body "Synthetic :do node (with :body? `true`) representing the body of the loop expression"]]}
+  {:op   :map
+   :doc  "Node for a map literal with attached metadata and/or non literal elements"
+   :keys [[:form "`{[key val]*}`"]
+          ^:children
+          [:keys "A vector of AST nodes representing the keys of the map"]
+          ^:children
+          [:vals "A vector of AST nodes representing the vals of the map"]]}
+  {:op   :new
+   :doc  "Node for a new special-form expression"
+   :keys [[:form "`(new Class arg*)`"]
+          ^:children
+          [:class "A :const AST node with :type :class representing the Class to instantiate"]
+          ^:children
+          [:args "A vector of AST nodes representing the arguments passed to the Class constructor"]
+          ]}
+  {:op   :no-op
+   :doc  "Node for a no-op"
+   :keys [
+          ]}
+  {:op   :ns
+   :doc  "Node for a clojure.core/ns form."
+   :keys [
+          ]}
+  {:op   :ns*
+   :doc  "Node for a special file-loading form."
+   :keys [
+          ]}
+  {:op   :quote
+   :doc  "Node for a quote special-form expression"
+   :keys [[:form "`(quote expr)`"]
+          ^:children
+          [:expr "A :const AST node representing the quoted value"]
+          [:literal? "`true`"]]}
+  {:op   :recur
+   :doc  "Node for a recur special-form expression"
+   :keys [[:form "`(recur expr*)`"]
+          ^:children
+          [:exprs "A vector of AST nodes representing the new bound values for the loop binding on the next loop iteration"]]}
+  {:op   :set
+   :doc  "Node for a set literal with attached metadata and/or non literal elements"
+   :keys [[:form "`#{item*}`"]
+          ^:children
+          [:items "A vector of AST nodes representing the items of the set"]]}
+  {:op   :set!
+   :doc  "Node for a set! special-form expression"
+   :keys [[:form "`(set! target val)`"]
+          ^:children
+          [:target "An AST node representing the target of the set! expression, must be :assignable?"]
+          ^:children
+          [:val "An AST node representing the new value for the target"]]}
+  {:op   :the-var
+   :doc  "Node for a var special-form expression"
+   :keys [[:form "`(var var-name)`"]
+          ^:children
+          [:var "A :var AST node that this expression refers to"]
+          ^:children
+          [:sym "An AST node for the quoted fully qualified name of this var."]
+          ^:children
+          [:meta "A :map AST node of this var's metadata."]
+          ]}
+  {:op   :throw
+   :doc  "Node for a throw special-form statement"
+   :keys [[:form "`(throw exception)`"]
+          ^:children
+          [:exception "An AST node representing the exception to throw"]]}
+  {:op   :try
+   :doc  "Node for a try special-form expression"
+   :keys  [[:form "`(try body* catch* finally?)`"]
+           ^:children
+           [:body "Synthetic :do AST node (with :body? `true`) representing the body of this try expression"]
+           ^:optional
+           [:name "A binding in scope in :catch. (symbol)"]
+           ^:optional ^:children
+           [:catch "An AST node representing an unconditional JavaScript catch."]
+           ^:optional ^:children
+           [:finally "Synthetic :do AST node (with :body? `true`) representing the final clause of this try expression"]]}
+  {:op   :var
+   :doc  "Node for a var symbol"
+   :keys [[:form "A symbol naming the var"]
+          [:ns "The namespace symbol this var is defined in."]
+          [:name "The fully qualified symbol naming this var."]
+          ]}
+  {:op   :vector
+   :doc  "Node for a vector literal with attached metadata and/or non literal elements"
+   :keys [[:form "`[item*]`"]
+          ^:children
+          [:items "A vector of AST nodes representing the items of the vector"]]}
+  {:op   :with-meta
+   :doc  "Node for a non quoted collection literal or fn/reify expression with attached metadata"
+   :keys [[:form "Non quoted collection literal or fn/reify expression with attached metadata"]
+          ^:children
+          [:meta "An AST node representing the metadata of expression. The node will be either a :map node or a :const node with :type :map"]
+          ^:children
+          [:expr "The expression this metadata is attached to, :op is one of :vector, :map, :set, :fn or :reify"]]}]}
diff --git a/ast-ref/buildref.sh b/ast-ref/buildref.sh
new file mode 100755
index 000000000..639710eb9
--- /dev/null
+++ b/ast-ref/buildref.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+java -cp .:`lein cp` clojure.main < (str x)
+    (replace #"`(.*?)`" "$1")
+    (replace #":([a-zA-Z\?!\-]*)" ":$1")))
+
+(defn build-children [children]
+  (if (some #(:optional (meta %)) children)
+    (let [[c & rest] children]
+      (let [k (build-children rest)
+            kc (mapv (fn [x] (cons c x)) k)]
+        (if (:optional (meta c))
+          (into k kc)
+          kc)))
+    (if (seq children)
+      [children]
+      [[]])))
+
+(defn children [keys]
+  (when-let [children (seq (filter #(:children (meta %)) keys))]
+    (mapv #(mapv first %) (build-children children))))
+
+(def nodes
+  (apply str (for [{:keys [op doc keys]} (:node-keys tej-ref) :let [op (name op)]]
+               (str "
" + "

" "#" op "

" + "

" doc "

" + "
" + "
:op
:" op "
" + (apply str (for [[k d :as f] keys] + (str "
" k "
" + "
" (if (:optional (meta f)) + "optional ") (fix d) "
"))) + (if-let [c (children keys)] + (str "
:children
" + (join ", " (mapv (fn [c] (str "" c "")) c)) "
")) + "
" + "
\n")))) + +(def nav + (apply str (for [{op :op} (:node-keys tej-ref) :let [op (name op)]] + (str "
  • " op "
  • \n")))) + +(def common + (apply str (str "
    " + "
    " + (apply str (for [[k d :as f] (:all-keys tej-ref)] + (str "
    " k "
    " + "
    " (if (:optional (meta f)) + "optional ") (fix d) "
    "))) + "
    " + "
    \n"))) + +(spit "quickref.html" + (-> html + (replace "{nav}" nav) + (replace "{common}" common) + (replace "{nodes}" nodes))) diff --git a/ast-ref/quickref.html.tpl b/ast-ref/quickref.html.tpl new file mode 100644 index 000000000..913c21070 --- /dev/null +++ b/ast-ref/quickref.html.tpl @@ -0,0 +1,110 @@ + + + + + cljs.analyzer AST Quickref (alpha) + + + + +
    +

    cljs.analyzer AST Quickref (alpha)

    +

    Common AST fields

    + {common} +

    Nodes reference

    + {nodes} +
    + + diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1ee7791e5..04bdf7ed9 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -676,7 +676,7 @@ :locals {} :fn-scope [] :js-globals (into {} - (map #(vector % {:name %}) + (map #(vector % {:op :js-var :name % :ns 'js}) '(alert window document console escape unescape screen location navigator history location global process require module exports)))})) @@ -980,11 +980,13 @@ (defmethod resolve* :js [env sym full-ns current-ns] {:name (symbol (str full-ns) (str (name sym))) + :op :js-var :ns full-ns}) (defmethod resolve* :node [env sym full-ns current-ns] {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) + :op :js-var :ns current-ns}) (defmethod resolve* :global @@ -994,6 +996,7 @@ (swap! env/*compiler* update-in (into [::namespaces current-ns :externs] pre) merge {})) {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." (name sym))) + :op :js-var :ns current-ns :tag (with-meta 'js {:prefix pre})})) @@ -1016,6 +1019,7 @@ {:sym sym-name})) (merge sym-ast {:name sym-name + :op :var :ns full-ns}))) (defn required? [ns env] @@ -1039,14 +1043,27 @@ :js {:name (symbol (or (gets @env/*compiler* :js-module-index ns :name) (resolve-ns-alias env ns))) + :op :js-var :ns 'js} :node {:name (symbol (str current-ns) (munge-node-lib (resolve-ns-alias env ns))) + :op :js-var :ns current-ns} :global {:name (symbol (str current-ns) (munge-global-export (resolve-ns-alias env ns))) + :op :js-var :ns current-ns}))) +;; core.async calls `macroexpand-1` manually with an ill-formed +;; :locals map. Normally :locals maps symbols maps, but +;; core.async adds entries mapping symbols to symbols. We work +;; around that specific case here. This is called defensively +;; every time we lookup the :locals map. +(defn handle-symbol-local [sym lb] + (if (symbol? lb) + {:name sym} + lb)) + (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." @@ -1055,27 +1072,33 @@ (let [locals (:locals env)] (if #?(:clj (= "js" (namespace sym)) :cljs (identical? "js" (namespace sym))) - (do - (when (contains? locals (-> sym name symbol)) - (warning :js-shadowed-by-local env {:name sym})) - (let [pre (->> (string/split (name sym) #"\.") (map symbol) vec)] - (when (and (not (has-extern? pre)) - ;; ignore exists? usage - (not (-> sym meta ::no-resolve))) - (swap! env/*compiler* update-in - (into [::namespaces (-> env :ns :name) :externs] pre) merge {})) - (merge - {:name sym - :ns 'js - :tag (with-meta (or (js-tag pre) (:tag (meta sym)) 'js) {:prefix pre})} - (when-let [ret-tag (js-tag pre :ret-tag)] - {:js-fn-var true - :ret-tag ret-tag})))) + (let [symn (-> sym name symbol) + shadowed-by-local (handle-symbol-local symn (get locals symn))] + (cond + (some? shadowed-by-local) + (do (warning :js-shadowed-by-local env {:name sym}) + (assoc shadowed-by-local :op :local)) + + :else + (let [pre (->> (string/split (name sym) #"\.") (map symbol) vec)] + (when (and (not (has-extern? pre)) + ;; ignore exists? usage + (not (-> sym meta ::no-resolve))) + (swap! env/*compiler* update-in + (into [::namespaces (-> env :ns :name) :externs] pre) merge {})) + (merge + {:name sym + :op :js-var + :ns 'js + :tag (with-meta (or (js-tag pre) (:tag (meta sym)) 'js) {:prefix pre})} + (when-let [ret-tag (js-tag pre :ret-tag)] + {:js-fn-var true + :ret-tag ret-tag}))))) (let [s (str sym) - lb (get locals sym) + lb (handle-symbol-local sym (get locals sym)) current-ns (-> env :ns :name)] (cond - (some? lb) lb + (some? lb) (assoc lb :op :local) (some? (namespace sym)) (let [ns (namespace sym) @@ -1097,16 +1120,20 @@ (let [idx (.indexOf s ".") prefix (symbol (subs s 0 idx)) suffix (subs s (inc idx))] - (if-some [lb (get locals prefix)] - {:name (symbol (str (:name lb)) suffix)} + (if-some [lb (handle-symbol-local prefix (get locals prefix))] + {:op :local + :name (symbol (str (:name lb) "." suffix))} (if-some [full-ns (gets @env/*compiler* ::namespaces current-ns :imports prefix)] - {:name (symbol (str full-ns) suffix)} + {:op :js-var + :name (symbol (str full-ns) suffix)} (if-some [info (gets @env/*compiler* ::namespaces current-ns :defs prefix)] (merge info {:name (symbol (str current-ns) (str sym)) + :op :var :ns current-ns}) (merge (gets @env/*compiler* ::namespaces prefix :defs (symbol suffix)) {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix)) + :op :var :ns prefix}))))) (some? (gets @env/*compiler* ::namespaces current-ns :uses sym)) @@ -1128,6 +1155,7 @@ (confirm env current-ns sym)) (merge (gets @env/*compiler* ::namespaces current-ns :defs sym) {:name (symbol (str current-ns) (str sym)) + :op :var :ns current-ns})) (core-name? env sym) @@ -1136,6 +1164,7 @@ (confirm env 'cljs.core sym)) (merge (gets @env/*compiler* ::namespaces 'cljs.core :defs sym) {:name (symbol "cljs.core" (str sym)) + :op :var :ns 'cljs.core})) (invokeable-ns? s env) @@ -1147,6 +1176,7 @@ (confirm env current-ns sym)) (merge (gets @env/*compiler* ::namespaces current-ns :defs sym) {:name (symbol (str current-ns) (str sym)) + :op :var :ns current-ns})))))))) (defn resolve-existing-var @@ -1342,7 +1372,8 @@ false BOOLEAN_SYM ANY_SYM) :quote (infer-tag env (:expr e)) - :var (if-some [init (:init e)] + (:var :local :js-var :binding) + (if-some [init (:init e)] (infer-tag env init) (infer-tag env (:info e))) (:host-field :host-call) ANY_SYM @@ -1391,6 +1422,10 @@ (defmethod parse 'var [op env [_ sym :as form] _ _] + (when (not= 2 (count form)) + (throw (error env "Wrong number of args to var"))) + (when-not (symbol? sym) + (throw (error env "Argument to var must be symbol"))) (merge {:env env :op :the-var @@ -1493,7 +1528,8 @@ parser)) finally (when (seq fblock) - (disallowing-recur (analyze (assoc env :context :statement) `(do ~@(rest fblock))))) + (-> (disallowing-recur (analyze (assoc env :context :statement) `(do ~@(rest fblock)))) + (assoc :body? true))) e (when (or (seq cblocks) dblock) (gensym "e")) default (if-let [[_ _ name & cb] dblock] `(cljs.core/let [~name ~e] ~@cb) @@ -1520,7 +1556,7 @@ try (disallowing-recur (analyze (if (or e finally) catchenv env) `(do ~@body)))] {:env env :op :try :form form - :body try + :body (assoc try :body? true) :finally finally :name e :catch catch @@ -1742,7 +1778,7 @@ {:children [:var]}))))) (defn analyze-fn-method-param [env] - (fn [[locals params] name] + (fn [[locals params] [arg-id name]] (when (namespace name) (throw (error env (str "Can't use qualified name as parameter: " name)))) (let [line (get-line name env) @@ -1750,15 +1786,17 @@ nmeta (meta name) tag (:tag nmeta) shadow (when (some? locals) - (locals name)) + (handle-symbol-local name (locals name))) env (merge (select-keys env [:context]) {:line line :column column}) - param {:op :var + param {:op :binding :name name :line line :column column :tag tag :shadow shadow + :local :arg + :arg-id arg-id ;; Give the fn params the same shape ;; as a :var, so it gets routed ;; correctly in the compiler @@ -1778,7 +1816,7 @@ body (next form) step (analyze-fn-method-param env) step-init [locals []] - [locals params] (reduce step step-init param-names) + [locals params] (reduce step step-init (map-indexed vector param-names)) params' (if (true? variadic) (butlast params) params) @@ -1802,7 +1840,7 @@ :form form :recurs recurs} (if (some? expr) - {:body expr + {:body (assoc expr :body? true) :children [:params :body]} {:children [:params]})))) @@ -1811,11 +1849,13 @@ (defn fn-name-var [env locals name] (when (some? name) (let [ns (-> env :ns :name) - shadow (get locals name) + shadow (handle-symbol-local name (get locals name)) shadow (when (nil? shadow) (get-in env [:js-globals name])) fn-scope (:fn-scope env) name-var {:name name + :op :binding + :local :fn :info {:fn-self-name true :fn-scope fn-scope :ns ns @@ -1927,11 +1967,12 @@ fexpr (no-warn (analyze env (n->fexpr n))) be (cond-> {:name n + :op :binding :fn-var true :line (get-line n env) :column (get-col n env) - :local true - :shadow (locals n) + :local :letfn + :shadow (handle-symbol-local n (locals n)) :variadic? (:variadic? fexpr) :max-fixed-arity (:max-fixed-arity fexpr) :method-params (map :params (:methods fexpr))} @@ -1953,7 +1994,8 @@ [(assoc-in env [:locals name] be') (conj bes be')])) [meth-env []] bes) - expr (analyze (assoc meth-env :context (if (= :expr context) :return context)) `(do ~@exprs))] + expr (-> (analyze (assoc meth-env :context (if (= :expr context) :return context)) `(do ~@exprs)) + (assoc :body? true))] {:env env :op :letfn :bindings bes :body expr :form form :children [:bindings :body]})) @@ -1997,7 +2039,7 @@ tag (-> init-expr :info :tag)))) -(defn analyze-let-bindings* [encl-env bindings] +(defn analyze-let-bindings* [encl-env bindings op] (loop [bes [] env (assoc encl-env :context :expr) bindings (seq (partition 2 bindings))] @@ -2011,19 +2053,20 @@ (let [init-expr (analyze-let-binding-init env init (cons {:params bes} *loop-lets*)) line (get-line name env) col (get-col name env) + shadow (handle-symbol-local name (get-in env [:locals name])) be {:name name :line line :column col :init init-expr :tag (get-let-tag name init-expr) - :local true - :shadow (-> env :locals name) + :local op + :shadow shadow ;; Give let* bindings same shape as var so ;; they get routed correctly in the compiler - :op :var + :op :binding :env {:line line :column col} :info {:name name - :shadow (-> env :locals name)} + :shadow shadow} :binding-form? true} be (if (= :fn (:op init-expr)) ;; TODO: can we simplify - David @@ -2038,8 +2081,8 @@ (next bindings)))) [bes env]))) -(defn analyze-let-bindings [encl-env bindings] - (disallowing-recur (analyze-let-bindings* encl-env bindings))) +(defn analyze-let-bindings [encl-env bindings op] + (disallowing-recur (analyze-let-bindings* encl-env bindings op))) (defn analyze-let-body* [env context exprs] (analyze (assoc env :context (if (= :expr context) :return context)) `(do ~@exprs))) @@ -2054,7 +2097,8 @@ (when-not (and (vector? bindings) (even? (count bindings))) (throw (error encl-env "bindings must be vector of even number of elements"))) (let [context (:context encl-env) - [bes env] (analyze-let-bindings encl-env bindings) + op (if (true? is-loop) :loop :let) + [bes env] (analyze-let-bindings encl-env bindings op) recur-frame (when (true? is-loop) {:params bes :flag (atom nil)}) recur-frames (if recur-frame @@ -2064,12 +2108,11 @@ (true? is-loop) *loop-lets* (some? *loop-lets*) (cons {:params bes} *loop-lets*)) expr (analyze-let-body env context exprs recur-frames loop-lets) - op (if (true? is-loop) :loop :let) children [:bindings :body]] {:op op :env encl-env :bindings bes - :body expr + :body (assoc expr :body? true) :form form :children children})) @@ -2132,7 +2175,7 @@ (disallowing-recur (let [enve (assoc env :context :expr) ctorexpr (analyze enve ctor) - ctor-var (when (= (:op ctorexpr) :var) + ctor-var (when (#{:var :local :js-var} (:op ctorexpr)) (resolve-existing-var env ctor)) record-args (when (and (:record ctor-var) (not (-> ctor meta :internal-ctor))) @@ -2180,7 +2223,7 @@ (set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning val))) (when (some? (:const (resolve-var (dissoc env :locals) target))) (throw (error env "Can't set! a constant"))) - (let [local (-> env :locals target)] + (let [local (handle-symbol-local target (-> env :locals target))] (when-not (or (nil? local) (and (:field local) (or (:mutable local) @@ -2923,6 +2966,7 @@ {:name fld :line (get-line fld env) :column (get-col fld env) + :local :field :field true :mutable (-> fld meta :mutable) :unsynchronized-mutable (-> fld meta :unsynchronized-mutable) @@ -3301,6 +3345,33 @@ [env form] (disallowing-recur (parse-invoke* env form))) +(defn desugar-dotted-expr [{:keys [op] :as expr}] + (case op + (:var :local) (if (dotted-symbol? (symbol (name (:name expr)))) + (let [s (name (:name expr)) + idx (.lastIndexOf s ".") + _ (assert (not= (inc idx) (count s))) + prefix (with-meta (symbol (namespace (:name expr)) (subs s 0 idx)) + (meta (:form expr))) + field (symbol (subs s (inc idx)))] + (assert (not (:const-expr expr))) + {:op :host-field + :env (:env expr) + :form (list '. prefix field) + :target (desugar-dotted-expr (-> expr + (assoc :name prefix + :form prefix) + (dissoc :tag) + (assoc-in [:info :name] prefix) + (assoc-in [:env :context] :expr))) + :field field + :tag (:tag expr) + :children [:target]}) + expr) + ;:var + expr)) + + (defn analyze-symbol "Finds the var associated with sym" [env sym] @@ -3317,8 +3388,9 @@ env) ret {:env env :form sym} lcls (:locals env)] - (if-some [lb (get lcls sym)] - (assoc ret :op :var :info lb) + (if-some [lb (handle-symbol-local sym (get lcls sym))] + (merge (assoc ret :op :local :info lb) + (select-keys lb [:name :local :arg-id :variadic? :init])) (let [sym-meta (meta sym) sym-ns (namespace sym) cur-ns (str (-> env :ns :name)) @@ -3335,13 +3407,17 @@ info (if-not (contains? sym-meta ::analyzed) (resolve-existing-var env sym) (resolve-var env sym))] - (if-not (true? (:def-var env)) - (merge - (assoc ret :op :var :info info) - (when-let [const-expr (:const-expr info)] - {:const-expr const-expr})) - (let [info (resolve-var env sym)] - (assoc ret :op :var :info info)))))))) + (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}) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 5fef5ff49..e939cde3f 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -186,7 +186,7 @@ (fn [m] (let [minfo (cond-> {:gcol (:gen-col m) :gline (:gen-line m)} - (= (:op ast) :var) + (#{:var :local :js-var} (:op ast)) (assoc :name (str (-> ast :info :name))))] ; Dec the line/column numbers for 0-indexing. ; tools.reader uses 1-indexed sources, chrome @@ -405,7 +405,7 @@ (defmethod emit* :no-op [m]) -(defmethod emit* :var +(defn emit-var [{:keys [info env form] :as ast}] (if-let [const-expr (:const-expr ast)] (emit (assoc const-expr :env env)) @@ -449,6 +449,11 @@ (emits info))))))))) +(defmethod emit* :var [expr] (emit-var expr)) +(defmethod emit* :binding [expr] (emit-var expr)) +(defmethod emit* :js-var [expr] (emit-var expr)) +(defmethod emit* :local [expr] (emit-var expr)) + (defmethod emit* :the-var [{:keys [env var sym meta] :as arg}] {:pre [(ana/ast? sym) (ana/ast? meta)]} @@ -1173,7 +1178,7 @@ (emits f "(" (comma-sep args) ")") :else - (if (and ana/*cljs-static-fns* (= (:op f) :var)) + (if (and ana/*cljs-static-fns* (#{:var :local :js-var} (:op f))) ;; higher order case, static information missing (let [fprop (str ".cljs$core$IFn$_invoke$arity$" (count args))] (if ana/*fn-invoke-direct* diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 55d87eda7..7e44dbcf6 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -845,7 +845,7 @@ (core/defn- simple-test-expr? [env ast] (core/and - (#{:var :invoke :const :host-field :host-call :js :quote} (:op ast)) + (#{:var :js-var :local :invoke :const :host-field :host-call :js :quote} (:op ast)) ('#{boolean seq} (cljs.analyzer/infer-tag env ast)))) (core/defmacro and diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index febf3398c..5b7f84ac9 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1611,3 +1611,11 @@ (deftest test-cljs-2807 (testing "Quoted sets should work" (is (macroexpand '(fn [x] #{(into [] x)}))))) + +(deftest var-desugar-test + (testing "dotted variable in return position" + (= cljs.core.PersistentQueue.EMPTY + ((fn [] cljs.core.PersistentQueue.EMPTY))) + (= 1 + (let [a #js {:b 1}] + ((fn [] a.b)))))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 4822143e1..8e32aa1d2 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -9,6 +9,7 @@ (ns cljs.analyzer-tests (:require [clojure.java.io :as io] [cljs.util :as util] + [clojure.set :as set] [cljs.env :as e] [cljs.env :as env] [cljs.analyzer :as a] @@ -445,9 +446,10 @@ (:use [clojure.set :only [intersection] :rename {intersection foo}]))))) (is (= (e/with-compiler-env (atom {::a/namespaces {'foo.core {:renames '{foo clojure.set/intersection}}}}) - (a/resolve-var {:ns {:name 'foo.core}} 'foo)) - '{:name clojure.set/intersection - :ns clojure.set})) + (select-keys (a/resolve-var {:ns {:name 'foo.core}} 'foo) + [:name :ns])) + '{:name clojure.set/intersection + :ns clojure.set})) (let [rwhen (e/with-compiler-env (atom (update-in @test-cenv [::a/namespaces] merge {'foo.core {:rename-macros '{always cljs.core/when}}})) (a/resolve-macro-var {:ns {:name 'foo.core}} 'always))] @@ -663,6 +665,605 @@ '(let [y 1] (def y 2))))] (is (some? (-> parsed :body :ret :var-ast))))) +(def analyze-ops-cenv (atom @test-cenv)) + +(defn ana' [form] + (e/with-compiler-env analyze-ops-cenv + (a/analyze test-env form))) + +(defmacro ana [form] + `(ana' '~form)) + +(defn prs-ana [fstr] + (e/with-compiler-env analyze-ops-cenv + (let [[form] (a/forms-seq* + (java.io.StringReader. fstr))] + (ana' form)))) + +(def juxt-op-val (juxt :op :val)) + +(deftest analyze-ops + ;constants + (is (empty? (-> (ana 1) :children))) + (is (= (-> (ana 1) juxt-op-val) [:const 1])) + (is (empty? (-> (ana :a) :children))) + (is (= (-> (ana :a) juxt-op-val) [:const :a])) + (is (= (-> (ana ::a) juxt-op-val) [:const ::a])) + (is (= (-> (ana "abc") juxt-op-val) [:const "abc"])) + ;variables + ; FIXME deviates from tools.analyzer, :name is always unqualified + (is (= [:var 'cljs.core 'cljs.core/inc 'inc] (-> (ana inc) ((juxt :op :ns :name :form))))) + (is (= [:var 'cljs.core 'cljs.core/inc 'cljs.core/inc] (-> (ana cljs.core/inc) ((juxt :op :ns :name :form))))) + ;; dotted :var + (is (= [:host-field 'bar :host-field 'foo :var 'cljs.core/inc 'cljs.core/inc] + (-> (ana inc.foo.bar) + ((juxt :op + :field + (comp :op :target) + (comp :field :target) + (comp :op :target :target) + (comp :name :target :target) + (comp :name :info :target :target)))))) + ;; dotted :local + (is (= [:host-field 'c :host-field 'b :local 'a 'a] + (-> (ana (let [a 1] a.b.c)) :body :ret + ((juxt :op + :field + (comp :op :target) + (comp :field :target) + (comp :op :target :target) + (comp :name :target :target) + (comp :name :info :target :target)))))) + ;do + (is (= (-> (ana (do 1 2)) :op) :do)) + (is (= (-> (ana (do 1 2)) :children) [:statements :ret])) + ; :statements + (is (vector? (-> (ana (do)) :statements))) + (is (vector? (-> (ana (do 1)) :statements))) + (is (vector? (-> (ana (do 1 2)) :statements))) + (is (= (-> (ana (do 1 2)) :statements first :op) :const)) + ; :ret + (is (= (-> (ana (do)) :ret juxt-op-val) [:const nil])) + (is (= (-> (ana (do nil)) :ret juxt-op-val) [:const nil])) + (is (= (-> (ana (do 1)) :ret juxt-op-val) [:const 1])) + (is (= (-> (ana (do 1 2)) :ret juxt-op-val) [:const 2])) + ;let + (is (= (-> (ana (let [])) :op) :let)) + (is (= (-> (ana (let [a 1] a)) :children) [:bindings :body])) + ; :bindings + (is ((every-pred vector? empty?) (-> (ana (let [])) :bindings))) + (is (vector? (-> (ana (let [a 1] a)) :bindings))) + (is (vector? (-> (ana (let [a 1 b 2] a)) :bindings))) + (is (= (-> (ana (let [a 1] a)) :bindings first :op) :binding)) + (is (= (-> (ana (let [a 1] a)) :bindings first :init :op) :const)) + ; :body + (is (= (-> (ana (let [a 1] a)) :body :op) :do)) + ;local + (is (empty? (-> (ana (let [a 1] a)) :body :ret :children))) + (is (= (-> (ana (let [a 1] a)) :body :ret :op) :local)) + (is (= (-> (ana (let [a 1] a)) :body :ret :name) 'a)) + (is (= (-> (ana (let [a 1] a)) :body :ret :form) 'a)) + (is (map? (-> (ana (let [a 1] a)) :body :ret :env))) + ;; dotted :local + (is (= [:host-field 'c :host-field 'b :local 'a] + (-> (ana (let [a 1] a.b.c)) :body :ret + ((juxt :op + :field + (comp :op :target) + (comp :field :target) + (comp :op :target :target) + (comp :name :target :target)))))) + ;local shadow + (is (= 'alert + (a/no-warn (-> (ana (let [alert 1] js/alert)) :body + :env :locals + (get 'alert) + :name)))) + (is (= [:local 'alert] + (a/no-warn (-> (ana (let [alert 1] js/alert)) :body :ret + ((juxt :op :name)))))) + ;loop + (is (= (-> (ana (loop [])) :op) :loop)) + (is (= (-> (ana (loop [a 1])) :bindings first :op) :binding)) + (is (= (-> (ana (loop [a 1] a)) :bindings first :init :op) :const)) + (is (= (-> (ana (loop [a 1] a)) :body :ret :local) :loop)) + (is (= (-> (ana (loop [a 1] (recur 1))) :children) [:bindings :body])) + ;recur + (is (= (-> (ana (loop [a 1] (recur 1))) :body :ret :op) :recur)) + (is (= (-> (ana (loop [a 1] (recur 1))) :body :ret :children) [:exprs])) + ; :exprs + (is ((every-pred vector? empty?) (-> (ana (loop [] (recur))) :body :ret :exprs))) + (is (vector? (-> (ana (loop [a 1] (recur 1))) :body :ret :exprs))) + (is (vector? (-> (ana (loop [a 1 b 2] (recur 1 2))) :body :ret :exprs))) + (is (= (-> (ana (loop [a 1] (recur 1))) :body :ret :exprs first :op) :const)) + (is (= (-> (ana (loop [a 1 b 2] (recur 1 2))) :body :ret :exprs second :op) :const)) + ;try + (is (= (-> (ana (try)) :op) :try)) + (is (= (-> (ana (try)) :children) [:body :catch])) ;; not sure why :catch? + (is (= (-> (ana (try (catch :default e))) :children) [:body :catch])) + (is (= (-> (ana (try (catch :default e) (finally))) :children) [:body :catch :finally])) + (is (= (-> (ana (try (finally))) :children) [:body :catch :finally])) ;; not sure why :catch? + ; :name + (is (symbol? (-> (ana (try (catch :default a))) :name))) + (is (nil? (-> (ana (try)) :name))) + ; :catch + (is (keyword? (-> (ana (try (catch :default a))) :catch :op))) + ; :finally + (is (= (-> (ana (try (finally 1))) :finally :op) :do)) + (is (= (-> (ana (try (finally 1))) :finally :ret :op) :const)) + ;TODO case + (is (= (-> (ana (case 1)) :op) :let)) + (is (= (-> (ana (case 1)) :body :ret :op) :case)) + (is (= (-> (ana (case 1)) :body :ret :children) [:test :nodes :default])) + ;; :test + (is (= (-> (ana (case 1)) :body :ret :test :op) :local)) + ;; :nodes + (is (vector? (-> (ana (case 1)) :body :ret :nodes))) + (is (vector? (-> (ana (case 1 :a 1)) :body :ret :nodes))) + (is (vector? (-> (ana (case 1 (:a :b) 1)) :body :ret :nodes))) + ;; :tests + (is (vector? + (-> (ana (case 1 :a 1)) :body :ret :nodes first :tests))) + (is (vector? + (-> (ana (case 1 :a 1 :b 2)) :body :ret :nodes first :tests))) + (is (= (-> (ana (case 1 :a 1)) :body :ret :nodes first :tests first :op) + :case-test)) + (is (= (-> (ana (case 1 :a 1)) :body :ret :nodes first :tests first :test juxt-op-val) + [:const "a"])) + (is (= (-> (ana (case 1 :a 1 :b 2)) :body :ret :nodes second :tests first :test juxt-op-val) + [:const "b"])) + (is (= (-> (ana (case 1 :a 1 (:b :faz) 2)) :body :ret :nodes (nth 1) :tests second :test juxt-op-val) + [:const "faz"])) + ;; :thens + (is (= (-> (ana (case 1 :a 3)) :body :ret :nodes first :then :op) + :case-then)) + (is (= (-> (ana (case 1 :a 3)) :body :ret :nodes first :then :then juxt-op-val) + [:const 3])) + (is (= (-> (ana (case 1 :a 3 :b 4)) :body :ret :nodes second :then :then juxt-op-val) + [:const 4])) + (is (= (-> (ana (case 1 :a 3 (:b :c) 4)) :body :ret :nodes (nth 1) :then :then juxt-op-val) + [:const 4])) + ;; :default + (is (= :throw (-> (ana (case 1)) :body :ret :default :op))) + (is (= [:const 2] (-> (ana (case 1 2)) :body :ret :default juxt-op-val))) + ;def +;TODO :meta node + (is (= :def (-> (ana (def a)) :op))) + (is (= [:var] (-> (ana (def a)) :children))) + (is (= [:var :init] (-> (ana (def a 1)) :children))) + ; :ns/:name + (is (= ['cljs.core 'cljs.core/a] (-> (ana (def a 1)) ((juxt :ns :name))))) + ; :var + (is (= [:var 'cljs.core 'cljs.core/a 'a] + (-> (ana (def a 1)) :var + ((juxt :op :ns :name :form))))) + ; :init + (is (-> (ana (def a)) (contains? :init) false?)) + (is (= [:const 1] (-> (ana (def a 1)) :init juxt-op-val))) + ;deftype + (is (= :deftype (-> (ana (deftype A [])) :statements first :op))) + (is (= [:body] (-> (ana (deftype A [])) :statements first :children))) + ; :body + (is (= :do (-> (ana (deftype A [a] Object (toString [this] a))) :statements first :body :op))) + ; field reference + (is (= [:local :field] + (-> (ana (deftype A [a] Object (toString [this] a))) + :statements first :body :ret :val :methods + first :body :ret :body :ret + ((juxt :op :local))))) + ;defrecord + (is (= :defrecord (-> (ana (defrecord Ab [])) :body :statements first :ret :op))) + (is (= [:body] (-> (ana (defrecord Ab [])) :body :statements first :ret :children))) + ; :body + (is (= :do (-> (ana (defrecord Ab [] Object (toString [this] "a"))) :body :statements first :ret :body :op))) + ;fn + (is (= :fn (-> (ana (fn [])) :op))) + (is (= [:methods] (-> (ana (fn [])) :children))) + (is (= [:local :methods] (-> (ana (fn a [])) :children))) + ; :local + (is (-> (ana (fn [])) (contains? :local) false?)) + (is (= + [:binding 'b :fn] + (-> (ana (fn b [& a])) + :local + ((juxt :op :name :local))))) + (is (= + [:local 'b :fn] + (-> (ana (fn b [] b)) + :methods + first + :body + :ret + ((juxt :op :name :local))))) + (is (= + [:binding 'b :fn] + (-> (ana (fn b [] b)) + :methods + first + :body + :ret + :env + :locals + (get 'b) + ((juxt :op :name :local))))) + ; :variadic? + (is (true? (-> (ana (fn [& a])) :variadic?))) + (is (false? (-> (ana (fn [])) :variadic?))) + ; :methods + (is (vector? (-> (ana (fn [])) :methods))) + (is (vector? (-> (ana (fn ([]) ([a]))) :methods))) + ;fn-method + (is (= :fn-method (-> (ana (fn [])) :methods first :op))) + (is (= [:params :body] (-> (ana (fn [])) :methods first :children))) + (is (= [:params :body] (-> (ana (fn [a])) :methods first :children))) + ; :fixed-arity + (is (= 0 (-> (ana (fn [])) :methods first :fixed-arity))) + (is (= 1 (-> (ana (fn [a])) :methods first :fixed-arity))) + (is (= 2 (-> (ana (fn [a b & c])) :methods first :fixed-arity))) + ; :variadic? + (is (true? (-> (ana (fn [a b & c])) :variadic?))) + (is (false? (-> (ana (fn [a b])) :variadic?))) + ; :body + (is (= [:const 1] (-> (ana (fn [] 1)) :methods first :body :ret juxt-op-val))) + ; :params + (is (vector? + (-> (ana (fn [])) :methods first :params))) + (is (vector? + (-> (ana (fn [a b])) :methods first :params))) + (is (= [:binding 'a :arg] + (-> (ana (fn [a b])) :methods first :params + first ((juxt :op :name :local))))) + (is (= [:binding 'b :arg] + (-> (ana (fn [a b])) :methods first :params + second ((juxt :op :name :local))))) + ;if + (is (= :if (-> (ana (if 1 2)) :op))) + (is (= :if (-> (ana (if 1 2 3)) :op))) + (is (= [:test :then :else] (-> (ana (if 1 2 3)) :children))) + (is (= [:test :then :else] (-> (ana (if 1 2)) :children))) + ; :test + (is (= [:const 1] (-> (ana (if 1 2)) :test juxt-op-val))) + (is (= [:const 1] (-> (ana (if 1 2 3)) :test juxt-op-val))) + ; :then + (is (= [:const 2] (-> (ana (if 1 2)) :then juxt-op-val))) + (is (= [:const 2] (-> (ana (if 1 2 3)) :then juxt-op-val))) + ; :else + (is (= [:const nil] (-> (ana (if 1 2)) :else juxt-op-val))) + (is (= [:const 3] (-> (ana (if 1 2 3)) :else juxt-op-val))) + ;invoke + (is (= :invoke (-> (ana (:a 1)) :op))) + (is (= [:fn :args] (-> (ana (:a 1)) :children))) + (is ((every-pred vector? empty?) (-> (ana (#'str)) :args))) + (is (vector? (-> (ana (:a 1)) :args))) + (is (vector? (-> (ana (:a 1 2)) :args))) + ; :fn + (is (= :the-var (-> (ana (#'str)) :fn :op))) + ; :invoke + (is (= [:const 1] (-> (ana (:a 1)) :args first juxt-op-val))) + (is (= [:const 2] (-> (ana (:a 1 2)) :args second juxt-op-val))) + ;js-array + (is (= :js-array (-> (prs-ana "#js ['a]") :op))) + (is (= [:items] (-> (prs-ana "#js ['a]") :children))) + (is (vector? (-> (prs-ana "#js ['a]") :items))) + (is (= 'array (-> (prs-ana "#js ['a]") :tag))) + (is (= [:const :a] (-> (prs-ana "#js [:a]") :items first juxt-op-val))) + ;js-object + (is (= :js-object (-> (prs-ana "#js {:a 1}]") :op))) +;; FIXME :keys should be an expression too + (is (= [:vals] (-> (prs-ana "#js {:a 1}") :children))) + (is (vector? (-> (prs-ana "#js {:a 1}") :vals))) + (is (= :a (-> (prs-ana "#js {:a 1}") :keys first))) + (is (vector? (-> (prs-ana "#js {:a 1}") :keys))) + (is (= [:const 1] (-> (prs-ana "#js {:a 1}") :vals first juxt-op-val))) + ;js* + (is (= :js (-> (ana (js* "~{}" 'food)) :op))) + (is (= [:args] (-> (ana (js* "~{}" 'food)) :children))) + (is (vector? (-> (ana (js* "~{}" 'food)) :args))) + (is (= [:const 'food] (-> (ana (js* "~{}" 'food)) :args first :expr juxt-op-val))) +;; FIXME why not a vector? + ;(is (vector? (-> (ana (js* "~{} / ~{}" 1 2)) :segs))) + (is (= ["" " / " ""] (-> (ana (js* "~{} / ~{}" 1 2)) :segs))) + ;letfn + (is (= :letfn + (-> (ana (letfn [(my-inc [a] (inc a))] + (my-inc 1))) + :op))) + (is (= [:bindings :body] + (-> (ana (letfn [(my-inc [a] (inc a))] + (my-inc 1))) + :children))) + ; :bindings + (is (vector? + (-> (ana (letfn [(my-inc [a] (inc a))] + (my-inc 1))) + :bindings))) + (is (vector? + (-> (ana (letfn [(my-inc [a] (inc a))] + (my-inc 1))) + :bindings))) + (is (= :binding + (-> (ana (letfn [(my-inc [a] (inc a))] + (my-inc 1))) + :bindings + first + :op))) + (is (= :fn + (-> (ana (letfn [(my-inc [a] (inc a))] + (my-inc 1))) + :bindings + first + :init + :op))) + (is (= :arg + (-> (ana (letfn [(my-inc [a] a)] + (my-inc 1))) + :bindings + first + :init + :methods + first + :body + :ret + :local))) + ; :body + (is (= :invoke + (-> (ana (letfn [(my-inc [a] (inc a))] + (my-inc 1))) + :body :ret :op))) + (is (= [:local :letfn] + (-> (ana (letfn [(my-inc [a] (inc a))] + (my-inc 1))) + :body :ret :fn ((juxt :op :local))))) + ;map + (is (= :map (-> (ana {:a 1}) :op))) + (is (= [:keys :vals] (-> (ana {:a 1}) :children))) + ; :keys + (is ((every-pred vector? empty?) (-> (ana {}) :keys))) + (is (vector? (-> (ana {:a 1}) :keys))) + (is (= [:const :a] (-> (ana {:a 1}) :keys first juxt-op-val))) + ; :vals + (is ((every-pred vector? empty?) (-> (ana {}) :vals))) + (is (vector? (-> (ana {:a 1}) :vals))) + (is (= [:const 1] (-> (ana {:a 1}) :vals first juxt-op-val))) + ;new + (is (= :new + (-> (ana (do (deftype Person [a]) (Person. 1))) + :ret + :op))) + (is (= [:class :args] + (-> (ana (do (deftype Person [a]) (Person. 1))) + :ret + :children))) + ; :class + (is (= [:var 'cljs.core 'cljs.core/Person] + (-> (ana (do (deftype Person [a]) (Person. 1))) + :ret + :class + ((juxt :op :ns :name))))) + ; :args + (is ((every-pred vector? empty?) + (-> (ana (do (deftype Noarg []) (Noarg.))) + :ret + :args))) + (is (= [:const 1] + (-> (ana (do (deftype Person [a]) (Person. 1))) + :ret + :args + first + juxt-op-val))) + ;set + (is (= :set (-> (ana #{:a :b}) :op))) + (is (= [:items] (-> (ana #{:a :b}) :children))) + ; :items + (is ((every-pred vector? empty?) (-> (ana #{}) :items))) + (is (vector? (-> (ana #{:a}) :items))) + (is (vector? (-> (ana #{:a :c :b}) :items))) + (is (= [:const :a] (-> (ana #{:a}) :items first juxt-op-val))) + ;set! + (is (= :set! + (-> (ana (do (def a 1) (set! a "Hi!"))) + :ret :op))) + (is (= [:target :val] + (-> (ana (do (def a 1) (set! a "Hi!"))) + :ret :children))) + ; :target + (is (= [:var 'cljs.core 'cljs.core/a] + (-> (ana (do (def a 1) (set! a "Hi!"))) + :ret :target ((juxt :op :ns :name))))) + ; :val + (is (= [:const "Hi!"] + (-> (ana (do (def a 1) (set! a "Hi!"))) + :ret :val juxt-op-val))) + ;the-var + (is (= :the-var (-> (ana #'+) :op))) + (is (= [:var :sym :meta] (-> (ana #'+) :children))) + ; :var + (is (= [:var 'cljs.core 'cljs.core/+] + (-> (ana #'+) :var ((juxt :op :ns :name))))) + ; :sym + (is (= 'cljs.core/+ (-> (ana #'+) :sym :expr :val))) + ; :meta + (is (= :map (-> (ana #'+) :meta :op))) + (is (empty? + ; ensure at least these entries are present + (set/difference + #{:name :tag :arglists + :line :column :end-line :end-column + :ns :file :doc :test :top-fn} + (->> (ana #'+) :meta :keys (map :val))))) + (run! + (fn [[k v]] + ; ensure entries map to sane things + (case (:val k) + :name (is (= '+ (-> v :expr :val))) + :tag (is (= 'number (-> v :expr :val))) + :arglists (is (= :quote (:op v))) + ;:row (is (= :quote (:op v))) + (:line :column :end-line :end-column) (number? (:val v)) + :ns (is (symbol? (-> v :expr :val))) + :file (is (string? (-> v :expr :val))) + :doc (is (string? (-> v :expr :val))) + :test (is (= :if (:op v))) + :top-fn (do (is (= :const (:op (:expr v)))) + (is (map? (:val (:expr v))))) + ;default + nil)) + (apply zipmap (-> (ana #'+) :meta ((juxt :keys :vals))))) + ;throw + (is (= :throw (-> (ana (throw (js/Error. "bad"))) :op))) + (is (= [:exception] (-> (ana (throw (js/Error. "bad"))) :children))) + ; :exception + (is (= [:js-var 'js 'js/Error] (-> (ana (throw (js/Error. "bad"))) :exception + :class + ((juxt :op :ns :name))))) + ;vector + (is (= :vector (-> (ana [1]) :op))) + (is (= [:items] (-> (ana [1]) :children))) + ; :items + (is ((every-pred vector? empty?) (-> (ana []) :items))) + (is (vector? (-> (ana [1]) :items))) + (is (= [:const 1] (-> (ana [1]) :items first juxt-op-val))) + ;with-meta + (is (= :with-meta (-> (ana ^:blah (fn [])) :op))) + (is (= [:meta :expr] (-> (ana ^:blah (fn [])) :children))) + (is (= :const (-> (ana '^:foo a) :expr :op))) + ; :meta + (is (= :map (-> (ana ^:blah (fn [])) :meta :op))) + (is (= [:const :blah] (-> (ana ^:blah (fn [])) :meta :keys first juxt-op-val))) + (is (= [:const true] (-> (ana ^:blah (fn [])) :meta :vals first juxt-op-val))) + ;(is (= [:const :foo] (-> (ana '^:foo a) :expr :meta :keys first juxt-op-val))) + ;(is (= [:const true] (-> (ana '^:foo a) :expr :meta :vals first juxt-op-val))) + ; :expr + (is (= :fn (-> (ana ^:blah (fn [])) :expr :op))) + ;(is (= :const (-> (ana '^:foo a) :expr :expr :op))) + ;host-field + (is (= :host-field (-> (ana (.-field 'a)) :op))) + (is (= [:target] (-> (ana (.-field 'a)) :children))) + (is (= 'field (-> (ana (.-field 'a)) :field))) + ; :target + (is (= [:const 'a] (-> (ana (.-field 'a)) :target :expr juxt-op-val))) + ;host-call + (is (= :host-call (-> (ana (.call 'a)) :op))) + (is (= [:target :args] (-> (ana (.call 'a)) :children))) + (is (= 'call (-> (ana (.call 'a)) :method))) + ; :target + (is (= [:const 'a] (-> (ana (.call 'a)) :target :expr juxt-op-val))) + ; :args + (is ((every-pred vector? empty?) (-> (ana (.call 'a)) :args))) + (is (= [:const 1] (-> (ana (.call 'a 1)) :args first juxt-op-val))) + ;ns + (is (binding [a/*cljs-ns* 'cljs.user] + (= :ns (-> (ana (ns fazz.foo)) :op)))) + ;ns* + (is (binding [a/*cljs-ns* 'cljs.user] + (= :ns* (-> (ana (refer-clojure :exclude '[locking])) :op)))) + ;quote + (is (= :quote (-> (ana (quote a)) :op))) + (is (= [:expr] (-> (ana (quote a)) :children))) + (is (map? (-> (ana (quote a)) :env))) + (is (= 'quote (-> (ana (quote a)) :form first))) + (is (= (:op (ana '(1 2 3))) :quote)) + ; :expr + (is (= [:const 'a] (-> (ana (quote a)) :expr juxt-op-val))) + ;js-var + (is (= [:js-var 'js/console 'js] (-> (ana js/console) ((juxt :op :name :ns))))) + (is (map? (-> (ana js/console) :env))) + (is (= 'js/-Infinity (-> (ana js/-Infinity) :form))) + ;; TODO dotted :js-var (?) +#_ + (is (= [:js-var 'js/console 'js] + (-> (ana js/console) ((juxt :op :name :ns))))) + ;munging + (is (= + [false 'a] + (-> + (ana (let [a (println 1) + b (println 2)] + [a b])) + :bindings first + ((juxt #(contains? % :ns) :name))))) + ;shadowing + (is (= + 'a + (-> + (ana (let [a (println 1) + a (println 2)] + [a a])) + :bindings second + :shadow + :name))) + (is (= + 'a + (-> + (ana (let [a (println 1) + a (println 2) + a (println 3) + ] + [a a a])) + :bindings (nth 2) + :shadow + :shadow + :name))) + ;ns + (is + (binding [a/*analyze-deps* false] + (binding [a/*cljs-ns* 'cljs.user] + (ana + (ns my.ns.foo + (:require [clojure.repl] + [clojure.string] + [goog.string]) + (:import [goog.string StringBuffer])))))) + ;nested metadata + (is (= :baz + (-> (ana ''#{^{:foo :baz} a}) + :expr + :val + second + first + meta + :foo)))) + +(deftest quote-args-error-test + (is (.startsWith + (try + (ana (quote)) + (catch Exception e + (.getMessage e))) + "Wrong number of args to quote")) + (is (.startsWith + (try + (ana (quote a b)) + (catch Exception e + (.getMessage e))) + "Wrong number of args to quote")) + (is (.startsWith + (try + (ana (quote a b c d)) + (catch Exception e + (.getMessage e))) + "Wrong number of args to quote"))) + +(deftest var-args-error-test + (is (.startsWith + (try + (ana (var)) + (catch Exception e + (.getMessage e))) + "Wrong number of args to var")) + (is (.startsWith + (try + (ana (var a b)) + (catch Exception e + (.getMessage e))) + "Wrong number of args to var")) + (is (.startsWith + (try + (ana (var nil)) + (catch Exception e + (.getMessage e))) + "Argument to var must be symbol"))) + (deftest test-has-extern?-basic (let [externs (externs/externs-map (closure/load-externs @@ -1126,3 +1727,8 @@ :with-core? true})] (is (empty? @ws)) (is (not (string/includes? res "cljs.core"))))) + +(deftest test-locals-mapped-to-sym + (testing "analyze should be robust to :locals mapping to symbols" + (is (= [:local 'a] (-> (a/analyze (assoc-in test-env [:locals 'a] 'foo) 'a) + ((juxt :op :name))))))) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index ac5a906a4..5a1d8c38a 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -89,7 +89,9 @@ :ns cljs.core} (ana/no-warn (env/with-compiler-env cenv - (ana/resolve-var {:ns {:name 'cljs.core}} '..)))))) + (select-keys + (ana/resolve-var {:ns {:name 'cljs.core}} '..) + [:name :ns])))))) (deftest test-cljs-428 (letfn [(check-docs [docs] From 2e62351fc83ba96fd73dce691abeaa81c134d6c7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 8 Jul 2018 15:29:54 -0400 Subject: [PATCH 3234/4033] CLJS-2811: cljs-1537-circular-deps fail on Windows --- src/test/clojure/cljs/build_api_tests.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 00f27562a..23940f543 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -179,8 +179,9 @@ :output-to out}) (is false) (catch Throwable e - (is (re-find #"Circular dependency detected, circular-deps.a -> circular-deps.b -> circular-deps.a" - (.getMessage (.getCause e)))))))) + (let [cause-message (.getMessage (.getCause e))] + (is (or (re-find #"Circular dependency detected, circular-deps.a -> circular-deps.b -> circular-deps.a" cause-message) + (re-find #"Circular dependency detected, circular-deps.b -> circular-deps.a -> circular-deps.b" cause-message)))))))) (defn loader-test-project [output-dir] {:inputs (str (io/file "src" "test" "cljs_build" "loader_test")) From 0773689ec748109a8c09ba924f90c25875eb6a9d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 9 Jul 2018 13:52:42 -0400 Subject: [PATCH 3235/4033] CLJS-2812: Support for overriding object printing --- src/main/cljs/cljs/core.cljs | 4 +- src/test/cljs/cljs/extend_to_native_test.cljs | 64 +++++++++++++++++++ src/test/cljs/cljs/extend_to_object_test.cljs | 26 -------- src/test/cljs/test_runner.cljs | 4 +- src/test/self/self_parity/test.cljs | 4 +- 5 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 src/test/cljs/cljs/extend_to_native_test.cljs delete mode 100644 src/test/cljs/cljs/extend_to_object_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 27d3ef45e..398215d10 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9941,8 +9941,8 @@ reduces them without incurring seq initialization" (.cljs$lang$ctorPrWriter obj obj writer opts) ; Use the new, more efficient, IPrintWithWriter interface when possible. - (implements? IPrintWithWriter obj) - (-pr-writer ^not-native obj writer opts) + (satisfies? IPrintWithWriter obj) + (-pr-writer obj writer opts) (or (true? obj) (false? obj)) (-write writer (str obj)) diff --git a/src/test/cljs/cljs/extend_to_native_test.cljs b/src/test/cljs/cljs/extend_to_native_test.cljs new file mode 100644 index 000000000..b29af3eb2 --- /dev/null +++ b/src/test/cljs/cljs/extend_to_native_test.cljs @@ -0,0 +1,64 @@ +(ns cljs.extend-to-native-test + (:require [cljs.test :refer-macros [deftest is]])) + +;;; Note: The tests in this namespace manipulate native types (at +;;; test run time) and this namespace should be loaded last by test +;;; runners so as to not affect other tests. + +;; r1798 core fn protocol regression +(deftest test-extend-to-object + (extend-type object + ISeqable + (-seq [coll] + (map #(vector % (aget coll %)) (js-keys coll))) + + ILookup + (-lookup + ([coll k] + (-lookup coll k nil)) + ([coll k not-found] + (if-let [v (aget coll k)] + v + not-found)))) + (is (= (seq (js-obj "foo" 1 "bar" 2)) '(["foo" 1] ["bar" 2]))) + (is (= (get (js-obj "foo" 1) "foo") 1)) + (is (= (get (js-obj "foo" 1) "bar" ::not-found) ::not-found)) + (is (= (reduce (fn [s [k v]] (+ s v)) 0 (js-obj "foo" 1 "bar" 2)) 3))) + +(deftest test-cljs-2812 + (extend-protocol IPrintWithWriter + object + (-pr-writer [obj writer _] + (write-all writer "#object[custom-print-cljs-2812]")) + boolean + (-pr-writer [obj writer _] + (write-all writer "#boolean[" (str obj) "]")) + number + (-pr-writer [obj writer _] + (write-all writer "#number[" (str obj) "]")) + string + (-pr-writer [obj writer _] + (write-all writer "#string[" obj "]")) + array + (-pr-writer [obj writer _] + (write-all writer "#array[" (count obj) "]")) + function + (-pr-writer [obj writer _] + (write-all writer "#function[custom-print-cljs-2812]"))) + (is (= "#object[custom-print-cljs-2812]" (pr-str #js {}))) + (is (= "#boolean[true]" (pr-str true))) + (is (= "#number[11]" (pr-str 11))) + (is (= "#string[hello]" (pr-str "hello"))) + (is (= "#array[3]" (pr-str #js [1 2 3]))) + (is (= "#function[custom-print-cljs-2812]" (pr-str map))) + ;; Restore basic native types so that test summary output looks correct + (extend-protocol IPrintWithWriter + boolean + (-pr-writer [obj writer _] + (write-all writer (str obj))) + number + (-pr-writer [obj writer _] + (write-all writer (str obj))) + string + (-pr-writer [obj writer _] + (write-all writer obj)))) diff --git a/src/test/cljs/cljs/extend_to_object_test.cljs b/src/test/cljs/cljs/extend_to_object_test.cljs deleted file mode 100644 index c02d26af6..000000000 --- a/src/test/cljs/cljs/extend_to_object_test.cljs +++ /dev/null @@ -1,26 +0,0 @@ -(ns cljs.extend-to-object-test - (:require [cljs.test :refer-macros [deftest is]])) - -;;; Note: The tests in this namespace manipulate object (at test -;;; run time) and this namespace should be loaded last by test -;;; runners so as to not affect other tests. - -;; r1798 core fn protocol regression -(deftest test-extend-to-object - (extend-type object - ISeqable - (-seq [coll] - (map #(vector % (aget coll %)) (js-keys coll))) - - ILookup - (-lookup - ([coll k] - (-lookup coll k nil)) - ([coll k not-found] - (if-let [v (aget coll k)] - v - not-found)))) - (is (= (seq (js-obj "foo" 1 "bar" 2)) '(["foo" 1] ["bar" 2]))) - (is (= (get (js-obj "foo" 1) "foo") 1)) - (is (= (get (js-obj "foo" 1) "bar" ::not-found) ::not-found)) - (is (= (reduce (fn [s [k v]] (+ s v)) 0 (js-obj "foo" 1 "bar" 2)) 3))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 4fa7a7200..088cde639 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -46,7 +46,7 @@ [static.core-test] [cljs.recur-test] [cljs.array-access-test] - [cljs.extend-to-object-test])) + [cljs.extend-to-native-test])) (set! *print-newline* false) (set-print-fn! js/print) @@ -90,4 +90,4 @@ 'static.core-test 'cljs.recur-test 'cljs.array-access-test - 'cljs.extend-to-object-test) + 'cljs.extend-to-native-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index cd57d4d4b..0bb102052 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -311,7 +311,7 @@ [static.core-test] [cljs.recur-test] [cljs.array-access-test] - [cljs.extend-to-object-test])) + [cljs.extend-to-native-test])) (fn [{:keys [value error]}] (if error (handle-error error (:source-maps @st)) @@ -354,7 +354,7 @@ 'static.core-test 'cljs.recur-test 'cljs.array-access-test - 'cljs.extend-to-object-test) + 'cljs.extend-to-native-test) (fn [{:keys [value error]}] (when error (handle-error error (:source-maps @st))))))))) From 6e78d1384cffb2d88a1c3234c4f34d6fb8b81ce3 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 11 Jul 2018 23:46:46 +0300 Subject: [PATCH 3236/4033] CLJS-2814: Fix munge-node-lib/global-export on self-host --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- src/test/clojure/cljs/analyzer_tests.clj | 5 +++++ src/test/self/self_host/test.cljs | 5 +++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 04bdf7ed9..6163071b3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -949,10 +949,12 @@ (not ^boolean (goog.string/contains s "..")))))) (defn munge-node-lib [name] - (str "node$module$" (munge (string/replace (str name) #"[.\/]" "\\$")))) + (str "node$module$" (munge (string/replace (str name) #"[.\/]" #?(:clj "\\$" + :cljs "$$"))))) (defn munge-global-export [name] - (str "global$module$" (munge (string/replace (str name) #"[.\/]" "\\$")))) + (str "global$module$" (munge (string/replace (str name) #"[.\/]" #?(:clj "\\$" + :cljs "$$"))))) (defn resolve-alias "Takes a namespace and an unqualified symbol and potentially returns a new diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 8e32aa1d2..36587cfe5 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1732,3 +1732,8 @@ (testing "analyze should be robust to :locals mapping to symbols" (is (= [:local 'a] (-> (a/analyze (assoc-in test-env [:locals 'a] 'foo) 'a) ((juxt :op :name))))))) + +(deftest test-cljs-2814 + (is (= "global$module$react" (a/munge-global-export 'react))) + (is (= "global$module$_CIRCA_material_ui$core$styles" (a/munge-global-export "@material-ui/core/styles"))) + (is (= "node$module$_CIRCA_material_ui$core$styles" (ana/munge-node-lib "@material-ui/core/styles")))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index ea9767ae1..63aea5529 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1171,6 +1171,11 @@ (is (some? (re-find #"foo\.core\.global\$module\$calculator = goog.global\[\"Calculator\"\];\snull;" source))) (inc! l))))))) +(deftest test-cljs-2814 + (is (= "global$module$react" (ana/munge-global-export 'react))) + (is (= "global$module$_CIRCA_material_ui$core$styles" (ana/munge-global-export "@material-ui/core/styles"))) + (is (= "node$module$_CIRCA_material_ui$core$styles" (ana/munge-node-lib "@material-ui/core/styles")))) + (deftest test-cljs-2261 (async done (let [st (cljs/empty-state) From 6ae393052616b9fcff6094bc25024dff7937c25e Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 11 Jul 2018 23:50:54 +0300 Subject: [PATCH 3237/4033] CLJS-2815: Support string keys in :global-exports --- src/main/cljs/cljs/js.cljs | 3 ++- src/main/clojure/cljs/analyzer.cljc | 6 +++--- src/main/clojure/cljs/compiler.cljc | 3 ++- .../emit_global_requires_test/core.cljs | 5 ++++- src/test/clojure/cljs/build_api_tests.clj | 10 +++++++++- src/test/self/self_host/test.cljs | 20 +++++++++++++++++++ 6 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index aa49725a5..f170355a6 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -659,7 +659,8 @@ (with-out-str (comp/emitln (munge ns-name) "." (ana/munge-global-export dep) - " = goog.global[\"" (get global-exports (symbol dep)) "\"];"))))) + " = goog.global[\"" (or (get global-exports (symbol dep)) + (get global-exports (str dep))) "\"];"))))) (when (and (seq deps) emit-nil-result?) (.append sb "null;")))) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6163071b3..3f6305fb2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -785,9 +785,9 @@ (defn dep-has-global-exports? [module] - (contains? - (get-in @env/*compiler* [:js-dependency-index (str module) :global-exports]) - (symbol module))) + (let [global-exports (get-in @env/*compiler* [:js-dependency-index (str module) :global-exports])] + (or (contains? global-exports (symbol module)) + (contains? global-exports (name module))))) (defn confirm-var-exists ([env prefix suffix] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index e939cde3f..fd35fbb19 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1259,7 +1259,8 @@ (let [{:keys [global-exports]} (get js-dependency-index (name lib))] (emitln (munge ns-name) "." (ana/munge-global-export lib) - " = goog.global[\"" (get global-exports (symbol lib)) "\"];"))) + " = goog.global[\"" (or (get global-exports (symbol lib)) + (get global-exports (name lib))) "\"];"))) (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) diff --git a/src/test/cljs_build/emit_global_requires_test/core.cljs b/src/test/cljs_build/emit_global_requires_test/core.cljs index 7d2773a7e..a26d69419 100644 --- a/src/test/cljs_build/emit_global_requires_test/core.cljs +++ b/src/test/cljs_build/emit_global_requires_test/core.cljs @@ -1,6 +1,7 @@ (ns emit-global-requires-test.core (:require [react :refer [createElement]] - ["react-dom/server" :as ReactDOMServer])) + ["react-dom/server" :as ReactDOMServer] + ["@material-ui/core/styles" :as mui-styles])) (enable-console-print!) @@ -8,3 +9,5 @@ (.-renderToString ReactDOMServer)) (println "hi" (ReactDOMServer/renderToString (createElement "div" nil "Hello World!"))) + +(mui-styles/createMuiTheme #js {}) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 23940f543..51ff8009b 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -391,7 +391,11 @@ {:file "src/test/cljs_build/thirdparty/add.js" :provides ["react-dom/server"] :requires ["react-dom"] - :global-exports '{react-dom/server ReactDOMServer}}]}} + :global-exports '{react-dom/server ReactDOMServer}} + {:file "src/test/cljs_build/thirdparty/add.js" + :provides ["@material-ui/core/styles"] + ;; Key str because contains multiple /, value shouldn't matter + :global-exports {"@material-ui/core/styles" "MaterialUIStyles"}}]}} cenv (env/default-compiler-env)] (test/delete-out-files out) (ana/with-warning-handlers [(collecting-warning-handler ws)] @@ -401,6 +405,10 @@ (slurp (io/file out "emit_global_requires_test/core.js")))))) (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$react_dom\$server\.renderToString" (slurp (io/file out "emit_global_requires_test/core.js")))))) + (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$_CIRCA_material_ui\$core\$styles = goog\.global\[\"MaterialUIStyles\"\];" + (slurp (io/file out "emit_global_requires_test/core.js")))))) + (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$_CIRCA_material_ui\$core\$styles\.createMuiTheme" + (slurp (io/file out "emit_global_requires_test/core.js")))))) (is (empty? @ws))))) (deftest test-data-readers diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 63aea5529..314903357 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1176,6 +1176,26 @@ (is (= "global$module$_CIRCA_material_ui$core$styles" (ana/munge-global-export "@material-ui/core/styles"))) (is (= "node$module$_CIRCA_material_ui$core$styles" (ana/munge-node-lib "@material-ui/core/styles")))) +(deftest test-cljs-2815 + (async done + (let [st (cljs/empty-state) + l (latch 1 done)] + (let [x-load (fn [_ cb] + (cb {:lang :js + :source "global.X = {};"}))] + (swap! st assoc :js-dependency-index {"@material-ui/core/styles" {:global-exports {"@material-ui/core/styles" "X"}}}) + (cljs/eval-str + (atom @st) + "(ns foo.core (:require [\"@material-ui/core/styles\" :as mui-styles])) (mui-styles/createMuiTheme)" + nil + {:context :expr + :def-emits-var true + :load x-load + :eval identity} + (fn [{{:keys [source]} :value}] + (is (some? (re-find #"foo\.core\.global\$module\$_CIRCA_material_ui\$core\$styles = goog.global\[\"X\"\];\snull;" source))) + (inc! l))))))) + (deftest test-cljs-2261 (async done (let [st (cljs/empty-state) From 17b71461ee6304e3696e397add1f4780525553fc Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Thu, 12 Jul 2018 01:22:29 +0300 Subject: [PATCH 3238/4033] CLJS-2816: Skip non-string package.json browser entry values False value means that the module should be ignored. Currently the value is passed directly to path.resolve which causes exception is the value is `false`. --- src/main/cljs/cljs/module_deps.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/module_deps.js b/src/main/cljs/cljs/module_deps.js index a5ab4d6ad..6b38cd083 100644 --- a/src/main/cljs/cljs/module_deps.js +++ b/src/main/cljs/cljs/module_deps.js @@ -201,14 +201,17 @@ md.on('end', function () { if (fieldValue != null && typeof fieldValue === 'object') { for (let key in fieldValue) { - const replacement = path.resolve(pkgJson.basedir, fieldValue[key]); + // TODO: False value means that the module should be ignored + if (typeof fieldValue[key] === 'string') { + const replacement = path.resolve(pkgJson.basedir, fieldValue[key]); - if (deps_files[replacement] != null) { - const file = path.resolve(pkgJson.basedir, key); - deps_files[replacement].provides = depProvides(deps_files[replacement].provides, file); + if (deps_files[replacement] != null) { + const file = path.resolve(pkgJson.basedir, key); + deps_files[replacement].provides = depProvides(deps_files[replacement].provides, file); - if (file === pkgJson.mainEntry) { - Array.prototype.push.apply(deps_files[replacement].provides, pkgJson.provides); + if (file === pkgJson.mainEntry) { + Array.prototype.push.apply(deps_files[replacement].provides, pkgJson.provides); + } } } } From 9099373fb717808d3be959e3534ef30ec3fcab01 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Mon, 16 Jul 2018 19:00:56 +0300 Subject: [PATCH 3239/4033] CLJS-2829: Fix deep object property access for :global-exports Accessing global object properties using dot form in global-exports worked before 3b0ce12d4c7e4a8ba9321531509490710d733662. This change converts dot access to bracket access for property access. --- src/main/cljs/cljs/js.cljs | 5 +---- src/main/clojure/cljs/compiler.cljc | 18 +++++++++++++---- .../emit_global_requires_test/core.cljs | 4 +++- src/test/clojure/cljs/build_api_tests.clj | 20 +++++++++++++------ src/test/self/self_host/test.cljs | 15 +++++++++++--- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index f170355a6..e64df6f4e 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -657,10 +657,7 @@ (let [{:keys [global-exports]} (get js-dependency-index (name dep))] (.append sb (with-out-str - (comp/emitln (munge ns-name) "." - (ana/munge-global-export dep) - " = goog.global[\"" (or (get global-exports (symbol dep)) - (get global-exports (str dep))) "\"];"))))) + (comp/emit-global-export ns-name global-exports dep))))) (when (and (seq deps) emit-nil-result?) (.append sb "null;")))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index fd35fbb19..314057c8b 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1199,6 +1199,19 @@ [{:keys [target val env]}] (emit-wrap env (emits target " = " val))) +(defn emit-global-export [ns-name global-exports lib] + (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)) + ";")) + (defn load-libs [libs seen reloads deps ns-name] (let [{:keys [options js-dependency-index]} @env/*compiler* @@ -1257,10 +1270,7 @@ " = require('" lib "');")) (doseq [lib global-exports-libs] (let [{:keys [global-exports]} (get js-dependency-index (name lib))] - (emitln (munge ns-name) "." - (ana/munge-global-export lib) - " = goog.global[\"" (or (get global-exports (symbol lib)) - (get global-exports (name lib))) "\"];"))) + (emit-global-export ns-name global-exports lib))) (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) diff --git a/src/test/cljs_build/emit_global_requires_test/core.cljs b/src/test/cljs_build/emit_global_requires_test/core.cljs index a26d69419..86205f792 100644 --- a/src/test/cljs_build/emit_global_requires_test/core.cljs +++ b/src/test/cljs_build/emit_global_requires_test/core.cljs @@ -1,7 +1,8 @@ (ns emit-global-requires-test.core (:require [react :refer [createElement]] ["react-dom/server" :as ReactDOMServer] - ["@material-ui/core/styles" :as mui-styles])) + ["@material-ui/core/styles" :as mui-styles] + ["@material-ui/core/styles/a" :as mui-styles-a])) (enable-console-print!) @@ -11,3 +12,4 @@ (println "hi" (ReactDOMServer/renderToString (createElement "div" nil "Hello World!"))) (mui-styles/createMuiTheme #js {}) +(mui-styles-a/foo) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 51ff8009b..067a631bf 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -393,9 +393,11 @@ :requires ["react-dom"] :global-exports '{react-dom/server ReactDOMServer}} {:file "src/test/cljs_build/thirdparty/add.js" - :provides ["@material-ui/core/styles"] + :provides ["@material-ui/core/styles" + "@material-ui/core/styles/a"] ;; Key str because contains multiple /, value shouldn't matter - :global-exports {"@material-ui/core/styles" "MaterialUIStyles"}}]}} + :global-exports {"@material-ui/core/styles" "MaterialUIStyles" + "@material-ui/core/styles/a" "MaterialUIStyles.a"}}]}} cenv (env/default-compiler-env)] (test/delete-out-files out) (ana/with-warning-handlers [(collecting-warning-handler ws)] @@ -405,10 +407,16 @@ (slurp (io/file out "emit_global_requires_test/core.js")))))) (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$react_dom\$server\.renderToString" (slurp (io/file out "emit_global_requires_test/core.js")))))) - (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$_CIRCA_material_ui\$core\$styles = goog\.global\[\"MaterialUIStyles\"\];" - (slurp (io/file out "emit_global_requires_test/core.js")))))) - (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$_CIRCA_material_ui\$core\$styles\.createMuiTheme" - (slurp (io/file out "emit_global_requires_test/core.js")))))) + (testing "global exports using string key" + (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$_CIRCA_material_ui\$core\$styles = goog\.global\[\"MaterialUIStyles\"\];" + (slurp (io/file out "emit_global_requires_test/core.js")))))) + (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$_CIRCA_material_ui\$core\$styles\.createMuiTheme" + (slurp (io/file out "emit_global_requires_test/core.js"))))))) + (testing "global exports points to a sub property" + (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$_CIRCA_material_ui\$core\$styles\$a = goog\.global\[\"MaterialUIStyles\"\]\[\"a\"\];" + (slurp (io/file out "emit_global_requires_test/core.js")))))) + (is (true? (boolean (re-find #"emit_global_requires_test\.core\.global\$module\$_CIRCA_material_ui\$core\$styles\$a\.foo" + (slurp (io/file out "emit_global_requires_test/core.js"))))))) (is (empty? @ws))))) (deftest test-data-readers diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 314903357..d7764e052 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1183,17 +1183,26 @@ (let [x-load (fn [_ cb] (cb {:lang :js :source "global.X = {};"}))] - (swap! st assoc :js-dependency-index {"@material-ui/core/styles" {:global-exports {"@material-ui/core/styles" "X"}}}) + (swap! st assoc :js-dependency-index {"@material-ui/core/styles" {:global-exports {"@material-ui/core/styles" "X"}} + "@material-ui/core/styles/a" {:global-exports {"@material-ui/core/styles/a" "X.a"}}}) (cljs/eval-str (atom @st) - "(ns foo.core (:require [\"@material-ui/core/styles\" :as mui-styles])) (mui-styles/createMuiTheme)" +"(ns foo.core + (:require [\"@material-ui/core/styles\" :as mui-styles] + [\"@material-ui/core/styles/a\" :as mui-styles-a])) + +(mui-styles/createMuiTheme) +(mui-styles-a/foo)" nil {:context :expr :def-emits-var true :load x-load :eval identity} (fn [{{:keys [source]} :value}] - (is (some? (re-find #"foo\.core\.global\$module\$_CIRCA_material_ui\$core\$styles = goog.global\[\"X\"\];\snull;" source))) + (testing "global exports using string key" + (is (some? (re-find #"foo\.core\.global\$module\$_CIRCA_material_ui\$core\$styles = goog.global\[\"X\"\];\s" source)))) + (testing "global exports points to a sub property" + (is (some? (re-find #"foo\.core\.global\$module\$_CIRCA_material_ui\$core\$styles\$a = goog.global\[\"X\"\]\[\"a\"\];\s" source)))) (inc! l))))))) (deftest test-cljs-2261 From 63fb5d5fc6294ed269b26f19aa01c2b4212ad3ef Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 17 Jul 2018 11:42:08 -0400 Subject: [PATCH 3240/4033] CLJS-2822: cljs.core.specs.alpha: Map bindings should be `:kind map?` --- src/main/cljs/cljs/core/specs/alpha.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core/specs/alpha.cljc b/src/main/cljs/cljs/core/specs/alpha.cljc index 1f2c9a315..fd48e9602 100644 --- a/src/main/cljs/cljs/core/specs/alpha.cljc +++ b/src/main/cljs/cljs/core/specs/alpha.cljc @@ -49,7 +49,7 @@ (s/def ::map-bindings (s/every (s/or :mb ::map-binding :nsk ::ns-keys - :msb (s/tuple #{:as :or :keys :syms :strs} any?)) :into {})) + :msb (s/tuple #{:as :or :keys :syms :strs} any?)) :kind map?)) (s/def ::map-binding-form (s/merge ::map-bindings ::map-special-binding)) From c8a5876be1c937f461ac09fe52fc0837a1630011 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 17 Jul 2018 12:18:28 -0400 Subject: [PATCH 3241/4033] CLJS-2806: Bump test.check to 0.10.0-alpha3 --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- script/test-self-parity | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deps.edn b/deps.edn index 75d194308..5350c5268 100644 --- a/deps.edn +++ b/deps.edn @@ -2,7 +2,7 @@ :deps {org.clojure/clojure {:mvn/version "1.9.0"} org.clojure/tools.reader {:mvn/version "1.3.0"} - org.clojure/test.check {:mvn/version "0.10.0-alpha2"} + org.clojure/test.check {:mvn/version "0.10.0-alpha3"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} diff --git a/pom.template.xml b/pom.template.xml index 6a25f27d4..34f4c324a 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -66,7 +66,7 @@ org.clojure test.check - 0.10.0-alpha2 + 0.10.0-alpha3 test diff --git a/project.clj b/project.clj index 1cb39c794..c2b915106 100644 --- a/project.clj +++ b/project.clj @@ -13,7 +13,7 @@ [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.3.0"] - [org.clojure/test.check "0.10.0-alpha2" :scope "test"] + [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] [com.google.javascript/closure-compiler-unshaded "v20180610"] diff --git a/script/bootstrap b/script/bootstrap index 320bf17ac..03b8595ee 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -11,7 +11,7 @@ TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.3.0" -TEST_CHECK_RELEASE="0.10.0-alpha2" +TEST_CHECK_RELEASE="0.10.0-alpha3" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } diff --git a/script/test-self-parity b/script/test-self-parity index 39a99049c..48e428830 100755 --- a/script/test-self-parity +++ b/script/test-self-parity @@ -10,7 +10,7 @@ if [ ! -f lib/clojure.jar ]; then exit 1 fi jar xvf lib/clojure.jar clojure/template.clj > /dev/null -jar xvf lib/test.check.jar clojure/test/check.cljc clojure/test/check/results.cljc clojure/test/check/clojure_test.cljc clojure/test/check/impl.cljc clojure/test/check/properties.cljc clojure/test/check/generators.cljc clojure/test/check/random.clj clojure/test/check/random/doubles.cljs clojure/test/check/random/longs/bit_count_impl.cljs clojure/test/check/random/longs.cljs clojure/test/check/random.cljs clojure/test/check/rose_tree.cljc > /dev/null +unzip lib/test.check.jar 'clojure/*' > /dev/null mkdir -p builds/out-self-parity/clojure/test mv clojure/template.clj builds/out-self-parity/clojure mv clojure/test builds/out-self-parity/clojure From 9ffa2f7b90089f4ea966a9b15c9de2ab1f0e28a8 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 17 Jul 2018 12:57:55 -0400 Subject: [PATCH 3242/4033] CLJS-2817: Suppress private var warnings for specs on private vars --- src/main/cljs/cljs/spec/alpha.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index b2c0a0179..d53d15b6a 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -56,7 +56,8 @@ "Qualify symbol s by resolving it or using the current *ns*." [env s] (if (namespace s) - (->sym (ana/resolve-var env s)) + (->sym (binding [ana/*private-var-access-nowarn* true] + (ana/resolve-var env s))) (symbol (str ana/*cljs-ns*) (str s)))) (defmacro def From a0cadee3a224e6313ff334d94cd7c7bd3fffffdd Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 17 Jul 2018 13:54:10 -0400 Subject: [PATCH 3243/4033] CLJS-2819: Warn on non-dynamic earmuffed vars --- src/main/clojure/cljs/analyzer.cljc | 23 +++++++++++++++++++++++ src/test/clojure/cljs/analyzer_tests.clj | 7 +++++++ 2 files changed, 30 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3f6305fb2..37b40e747 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -153,6 +153,7 @@ :single-segment-namespace true :munged-namespace true :ns-var-clash true + :non-dynamic-earmuffed-var true :extend-type-invalid-method-shape true :unsupported-js-module-type true :unsupported-preprocess-value true @@ -467,6 +468,11 @@ [warning-type {:keys [ns var] :as info}] (str "Namespace " ns " clashes with var " var)) +(defmethod error-message :non-dynamic-earmuffed-var + [warning-type {:keys [var] :as info}] + (str var " not declared dynamic and thus is not dynamically rebindable, but its name " + "suggests otherwise. Please either indicate ^:dynamic " var " or change the name")) + (defmethod error-message :extend-type-invalid-method-shape [warning-type {:keys [protocol method] :as info}] (str "Bad extend-type method shape for protocol " protocol " method " method @@ -1602,6 +1608,18 @@ :set (into #{} (map const-expr->constant-value (:items e))) :vector (into [] (map const-expr->constant-value (:items e))))) +(defn- earmuffed? [sym] + (let [s (name sym)] + (and (> (count s) 2) + (string/starts-with? s "*") + (string/ends-with? s "*")))) + +(defn- core-ns? [ns-sym] + (let [s (name ns-sym)] + (and (not= 'cljs.user ns-sym) + (or (string/starts-with? s "cljs.") + (string/starts-with? s "clojure."))))) + (defmethod parse 'def [op env form _ _] (when (> (count form) 4) @@ -1639,6 +1657,11 @@ (when-some [doc (:doc args)] (when-not (string? doc) (throw (error env "Too many arguments to def")))) + (when (and (not dynamic) + (earmuffed? sym) + (not (core-ns? ns-name))) + (warning :non-dynamic-earmuffed-var env + {:var (str sym)})) (when-some [v (get-in @env/*compiler* [::namespaces ns-name :defs sym])] (when (and (not *allow-redef*) (not (:declared v)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 36587cfe5..6bb5f8bba 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1737,3 +1737,10 @@ (is (= "global$module$react" (a/munge-global-export 'react))) (is (= "global$module$_CIRCA_material_ui$core$styles" (a/munge-global-export "@material-ui/core/styles"))) (is (= "node$module$_CIRCA_material_ui$core$styles" (ana/munge-node-lib "@material-ui/core/styles")))) + +(deftest test-cljs-2819 + (let [ws (atom [])] + (a/with-warning-handlers [(collecting-warning-handler ws)] + (a/analyze ns-env + '(def *foo* 1))) + (is (string/starts-with? (first @ws) "*foo* not declared dynamic and thus")))) From 99efac4aa8a21d49dc2adacb442abf3561347222 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 17 Jul 2018 14:36:38 -0400 Subject: [PATCH 3244/4033] CLJS-2821: Update doto docstring to not use Java example --- src/main/clojure/cljs/core.cljc | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 7e44dbcf6..93370ab46 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -93,7 +93,6 @@ (import-macros clojure.core [-> ->> .. assert comment cond declare defn- - doto extend-protocol fn for if-let if-not letfn memfn @@ -176,22 +175,21 @@ "defs the supplied var names with no bindings, useful for making forward declarations." [& names] `(do ~@(map #(core/list 'def (vary-meta % assoc :declared true)) names)))) -#?(:cljs - (core/defmacro doto - "Evaluates x then calls all of the methods and functions with the - value of x supplied at the front of the given arguments. The forms - are evaluated in order. Returns x. +(core/defmacro doto + "Evaluates x then calls all of the methods and functions with the + value of x supplied at the front of the given arguments. The forms + are evaluated in order. Returns x. - (doto (new java.util.HashMap) (.put \"a\" 1) (.put \"b\" 2))" - [x & forms] - (core/let [gx (gensym)] - `(let [~gx ~x] - ~@(map (core/fn [f] - (if (seq? f) - `(~(first f) ~gx ~@(next f)) - `(~f ~gx))) - forms) - ~gx)))) + (doto (new js/Map) (.set \"a\" 1) (.set \"b\" 2))" + [x & forms] + (core/let [gx (gensym)] + `(let [~gx ~x] + ~@(map (core/fn [f] + (if (seq? f) + `(~(first f) ~gx ~@(next f)) + `(~f ~gx))) + forms) + ~gx))) #?(:cljs (core/defn- parse-impls [specs] From 3123aa32851c01682cf076e0e3b497e890b26922 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 17 Jul 2018 15:51:25 -0400 Subject: [PATCH 3245/4033] CLJS-2827: Avoid var special in core macros for private var access --- src/main/clojure/cljs/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 93370ab46..d32e634ce 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1862,7 +1862,7 @@ 'IPrintWithWriter `(~'-pr-writer [this# writer# opts#] - (let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# @#'pr-writer "" " " "" opts# keyval#))] + (let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# (~'js* "cljs.core.pr_writer") "" " " "" opts# keyval#))] (pr-sequential-writer writer# pr-pair# ~pr-open ", " "}" opts# (concat [~@(map #(core/list `vector (keyword %) %) base-fields)] @@ -2687,7 +2687,7 @@ prefer-table# (atom {}) method-cache# (atom {}) cached-hierarchy# (atom {}) - hierarchy# (cljs.core/get ~options :hierarchy (#'cljs.core/get-global-hierarchy))] + hierarchy# (cljs.core/get ~options :hierarchy ((~'js* "cljs.core.get_global_hierarchy")))] (cljs.core/MultiFn. (cljs.core/symbol ~mm-ns ~(name mm-name)) ~dispatch-fn ~default hierarchy# method-table# prefer-table# method-cache# cached-hierarchy#)))))) From e78177a73b3ff4dc7d9a7a16ec0c1da768eee953 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 2 Aug 2018 11:29:41 -0400 Subject: [PATCH 3246/4033] CLJS-2855: Browser REPL prints empty string after require --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/cljs_cli/cljs_cli/test.clj | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 314057c8b..85f3b24d0 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1279,7 +1279,7 @@ (load-libs requires nil (:require reloads) deps name) (load-libs uses requires (:use reloads) deps name) (when (:repl-env env) - (emitln "null;"))) + (emitln "'nil';"))) (defmethod emit* :ns [{:keys [name requires uses require-macros reloads env deps]}] diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj index 285282161..bef3fa26b 100644 --- a/src/test/cljs_cli/cljs_cli/test.clj +++ b/src/test/cljs_cli/cljs_cli/test.clj @@ -95,7 +95,6 @@ "-e" "(require 'cljs.js)" "-e" "(cljs.js/eval-str (cljs.js/empty-state) \"(+ 1 2)\" nil {:eval cljs.js/js-eval :context :expr} prn)") (output-is - nil "{:ns cljs.user, :value 3}")))) (deftest test-cljs-2724 @@ -104,7 +103,6 @@ "-e" "(require 'fs)" "-e" "fs/R_OK") (output-is - nil 4)))) (deftest test-cljs-2775 @@ -114,7 +112,7 @@ "-d" "out" "-e" "(require 'left-pad)" "-e" "(left-pad 3 10 0)") - (output-is "nil\n\"0000000003\"")))) + (output-is "\"0000000003\"")))) (deftest test-cljs-2780 (with-repl-env-filter #{"node" "nashorn"} From cbb4896e2afe52d786529c7a5f81c64f3fd394cc Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 18 Jul 2018 10:17:13 -0400 Subject: [PATCH 3247/4033] CLJS-2833: Update to Closure Compiler v20180716 --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index 5350c5268..7c3dcfed3 100644 --- a/deps.edn +++ b/deps.edn @@ -7,7 +7,7 @@ org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} com.cognitect/transit-clj {:mvn/version "0.8.309"} - com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180610"} + com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180716"} org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"} org.mozilla/rhino {:mvn/version "1.7R5"}} :aliases diff --git a/pom.template.xml b/pom.template.xml index 34f4c324a..1ad8acb62 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20180610 + v20180716 org.clojure diff --git a/project.clj b/project.clj index c2b915106..efbc513ed 100644 --- a/project.clj +++ b/project.clj @@ -16,7 +16,7 @@ [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] - [com.google.javascript/closure-compiler-unshaded "v20180610"] + [com.google.javascript/closure-compiler-unshaded "v20180716"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main cljs.main} diff --git a/script/bootstrap b/script/bootstrap index 03b8595ee..874f5f059 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="20180610" +CLOSURE_RELEASE="20180716" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" From 9923fadc659dc1694c88371d46791da8881c026b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 18 Jul 2018 07:55:50 -0400 Subject: [PATCH 3248/4033] CLJS-2832: Bad code gen for `((not empty?) "foo")` when compiled with no optimizations --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 85f3b24d0..4efaab446 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1157,7 +1157,7 @@ (emit-wrap env (cond opt-not? - (emits "!(" (first args) ")") + (emits "(!(" (first args) "))") proto? (let [pimpl (str (munge (protocol-prefix protocol)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 5b7f84ac9..184294ce5 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1619,3 +1619,9 @@ (= 1 (let [a #js {:b 1}] ((fn [] a.b)))))) + +(deftest test-cljs-2832 + (is (true? ((comp not empty?) "foo"))) + (is (false? ((comp not empty?) ""))) + (is (thrown? js/Error ((not empty?) "foo"))) + (is (thrown? js/Error ((not empty?) "")))) From 04e6bd9073daa905543d7decab95a2252c2e53e2 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 17 Jul 2018 23:51:43 -0400 Subject: [PATCH 3249/4033] CLJS-2831: Add a graaljs REPL environment --- .travis.yml | 8 +- src/main/cljs/cljs/bootstrap_graaljs.js | 75 +++++++++ src/main/cljs/cljs/core.cljs | 3 +- src/main/cljs/cljs/stacktrace.cljc | 24 +++ src/main/clojure/cljs/cli.clj | 4 +- src/main/clojure/cljs/closure.clj | 6 +- src/main/clojure/cljs/repl/graaljs.clj | 190 +++++++++++++++++++++++ src/main/clojure/cljs/server/graaljs.clj | 27 ++++ src/test/cljs_cli/cljs_cli/test.clj | 7 +- 9 files changed, 334 insertions(+), 10 deletions(-) create mode 100644 src/main/cljs/cljs/bootstrap_graaljs.js create mode 100644 src/main/clojure/cljs/repl/graaljs.clj create mode 100644 src/main/clojure/cljs/server/graaljs.clj diff --git a/.travis.yml b/.travis.yml index f88d798d9..082f712e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,8 @@ before_install: - sudo apt-get install -y libjavascriptcoregtk-3.0-bin - wget https://aka.ms/chakracore/cc_linux_x64_1_8_1 -O chakra-core.tar.gz - tar xvzf chakra-core.tar.gz - - wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc1/graalvm-ce-1.0.0-rc1-linux-amd64.tar.gz - - tar xzf graalvm-ce-1.0.0-rc1-linux-amd64.tar.gz + - wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc4/graalvm-ce-1.0.0-rc4-linux-amd64.tar.gz + - tar xzf graalvm-ce-1.0.0-rc4-linux-amd64.tar.gz before_script: - script/bootstrap @@ -55,7 +55,7 @@ script: - grep '0 failures, 0 errors.' test-out.txt - ./ChakraCoreFiles/bin/ch builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - ./graalvm-1.0.0-rc1/bin/js builds/out-adv/core-advanced-test.js | tee test-out.txt + - ./graalvm-ce-1.0.0-rc4/bin/js builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - script/test-self-host | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt @@ -67,3 +67,5 @@ script: - grep '0 failures, 0 errors.' test-out.txt - script/test-cli rhino | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt + - PATH=`pwd`/graalvm-ce-1.0.0-rc4/bin:$PATH script/test-cli graaljs | tee test-out.txt + - grep '0 failures, 0 errors.' test-out.txt diff --git a/src/main/cljs/cljs/bootstrap_graaljs.js b/src/main/cljs/cljs/bootstrap_graaljs.js new file mode 100644 index 000000000..4db6faadb --- /dev/null +++ b/src/main/cljs/cljs/bootstrap_graaljs.js @@ -0,0 +1,75 @@ +var global = this; // required by React + +var graaljs_load = function(path) { + var File = Java.type("java.io.File"); + var outputPath = (typeof CLJS_OUTPUT_DIR != "undefined" ? CLJS_OUTPUT_DIR : ".") + File.separator + path; + if (typeof CLJS_DEBUG != "undefined" && CLJS_DEBUG) print("loading:" + outputPath); + load(outputPath); +}; + +goog.global.CLOSURE_IMPORT_SCRIPT = function(path) { + graaljs_load("goog/" + path); + return true; +}; + +goog.global.isProvided_ = function(name) { return false; }; + +var __executors = Java.type("java.util.concurrent.Executors"); +var __executorService = __executors.newScheduledThreadPool(0); +__executorService.setMaximumPoolSize(1); +var __millis = Java.type("java.util.concurrent.TimeUnit").valueOf("MILLISECONDS"); + +var graaljs_tear_down = function() { + __executorService.shutdown(); +} + +function setTimerRequest(handler, delay, interval, args) { + handler = handler || function() {}; + delay = delay || 0; + interval = interval || 0; + var voidType = Java.type("java.lang.Void").TYPE; + var applyHandler = __executors.callable(function() { handler.apply(this, args); }, voidType); + if (interval > 0) { + return __executorService.scheduleWithFixedDelay(applyHandler, delay, interval, __millis); + } else { + return __executorService.schedule(applyHandler, delay, __millis); + }; +} + +function clearTimerRequest(future) { + future.cancel(false); +} + +function setInterval() { + var args = Array.prototype.slice.call(arguments); + var handler = args.shift(); + var ms = args.shift(); + return setTimerRequest(handler, ms, ms, args); +} + +function clearInterval(future) { + clearTimerRequest(future); +} + +function setTimeout() { + var args = Array.prototype.slice.call(arguments); + var handler = args.shift(); + var ms = args.shift(); + + return setTimerRequest(handler, ms, 0, args); +} + +function clearTimeout(future) { + clearTimerRequest(future); +} + +function setImmediate() { + var args = Array.prototype.slice.call(arguments); + var handler = args.shift(); + + return setTimerRequest(handler, 0, 0, args); +} + +function clearImmediate(future) { + clearTimerRequest(future); +} diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 398215d10..8e2cfce80 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11525,7 +11525,8 @@ reduces them without incurring seq initialization" (exists? js/console) (enable-console-print!) - (identical? *target* "nashorn") + (or (identical? *target* "nashorn") + (identical? *target* "graaljs")) (let [system (.type js/Java "java.lang.System")] (set! *print-newline* false) (set-print-fn! diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 0f05a10e3..08d7a9fa5 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -471,6 +471,30 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" (remove nil?) vec))) +;; ----------------------------------------------------------------------------- +;; Graal.JS Stacktrace + +(defmethod parse-stacktrace :graaljs + [repl-env st err {:keys [output-dir] :as opts}] + (letfn [(process-frame [frame-str] + (when-not (string/blank? frame-str) + (let [[function file-and-line] (string/split frame-str #"\(") + [file-part line-part] (string/split file-and-line #":")] + {:file (string/replace file-part + (str output-dir + #?(:clj File/separator :cljs "/")) + "") + :function function + :line (when (and line-part (not (string/blank? line-part))) + (parse-int + (.substring line-part 0 + (dec (count line-part))))) + :column 0})))] + (->> (string/split st #"\n") + (map process-frame) + (remove nil?) + vec))) + (comment (parse-stacktrace {} "Error: 1 is not ISeqable diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 223d93a6d..380144627 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -530,7 +530,7 @@ present" {["-re" "--repl-env"] {:arg "env" :doc (str "The REPL environment to use. Built-in " - "supported values: nashorn, node, browser, " + "supported values: nashorn, graaljs, node, browser, " "rhino. Defaults to browser")}}} ::main {:desc "init options only for --main and --repl"} ::compile {:desc "init options only for --compile"}} @@ -569,7 +569,7 @@ present" :doc (str "The JavaScript target. Configures environment bootstrap and " "defaults to browser. Supported values: node or nodejs, nashorn, " - "webworker, none") } + "graaljs, webworker, none") } ["-ro" "--repl-opts"] {:group ::main&compile :fn repl-env-opts-opt :arg "edn" :doc (str "Options to configure the repl-env, can be an EDN string or " diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4bca9f5a9..1e81a99a7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1635,7 +1635,7 @@ (util/output-directory opts)) closure-defines (json/write-str (:closure-defines opts))] (case (:target opts) - :nashorn + (:nashorn :graaljs) (output-one-file (merge opts (when module @@ -1645,7 +1645,7 @@ (str "var CLJS_OUTPUT_DIR = \"" asset-path "\";\n" "load((new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"), \"base.js\")).getPath());\n" "load((new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"), \"deps.js\")).getPath());\n" - "load((new java.io.File(new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"),\"bootstrap\"),\"nashorn.js\")).getPath());\n" + "load((new java.io.File(new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"),\"bootstrap\"),\"" (name (:target opts)) ".js\")).getPath());\n" "load((new java.io.File(\"" asset-path "\",\"cljs_deps.js\")).getPath());\n" "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" (apply str (preloads (:preloads opts))))) @@ -2794,7 +2794,7 @@ opts)) (defn output-bootstrap [{:keys [target] :as opts}] - (when (and (#{:nodejs :nashorn} target) + (when (and (#{:nodejs :nashorn :graaljs} target) (not= (:optimizations opts) :whitespace)) (let [target-str (name target) outfile (io/file (util/output-directory opts) diff --git a/src/main/clojure/cljs/repl/graaljs.clj b/src/main/clojure/cljs/repl/graaljs.clj new file mode 100644 index 000000000..946a7cc5c --- /dev/null +++ b/src/main/clojure/cljs/repl/graaljs.clj @@ -0,0 +1,190 @@ +;; 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.repl.graaljs + (:require [clojure.java.io :as io] + [clojure.string :as string] + [clojure.stacktrace] + [clojure.data.json :as json] + [cljs.analyzer :as ana] + [cljs.env :as env] + [cljs.util :as util] + [cljs.repl :as repl] + [cljs.cli :as cli] + [cljs.compiler :as comp] + [cljs.closure :as closure] + [cljs.stacktrace :as st]) + (:import [javax.script ScriptEngine ScriptException])) + +(defn create-engine [] + ;; In order to support AOT compilation by JVMs that don't have + ;; GraalVM available, we load and execute engine creation code + ;; here at runtime. + (import '(com.oracle.truffle.js.scriptengine GraalJSScriptEngine)) + (import '(org.graalvm.polyglot Context)) + (let [engine (eval '(GraalJSScriptEngine/create nil + (-> (Context/newBuilder (make-array String 0)) + (.allowHostAccess true) + (.allowCreateThread true) + (.allowNativeAccess true)))) + context (.getContext engine)] + (.setWriter context *out*) + (.setErrorWriter context *err*) + engine)) + +(defn eval-str [^ScriptEngine engine ^String s] + (.eval engine s)) + +(defn eval-resource + "Evaluate a file on the classpath in the engine." + [engine path debug] + (let [r (io/resource path)] + (eval-str engine (slurp r)) + (when debug (println "loaded: " path)))) + +(defn init-engine [engine {:keys [output-dir] :as opts} debug] + (eval-str engine (format "var CLJS_DEBUG = %s;" (boolean debug))) + (eval-str engine (format "var CLJS_OUTPUT_DIR = \"%s\";" output-dir)) + (eval-resource engine "goog/base.js" debug) + (eval-resource engine "goog/deps.js" debug) + (eval-resource engine "cljs/bootstrap_graaljs.js" debug) + (eval-str engine + (format "goog.global.CLOSURE_UNCOMPILED_DEFINES = %s;" + (json/write-str (:closure-defines opts)))) + engine) + +(defn tear-down-engine [engine] + (eval-str engine "graaljs_tear_down();")) + +(defn load-js-file [engine file] + (eval-str engine (format "graaljs_load(\"%s\");" file))) + +;; Create a minimal build of ClojureScript from the core library. +;; Copied from clj.cljs.repl.node. +(defn bootstrap-repl [engine output-dir opts] + (env/ensure + (let [deps-file ".graaljs_repl_deps.js" + core (io/resource "cljs/core.cljs") + core-js (closure/compile core + (assoc opts :output-file + (closure/src-file->target-file + core (dissoc opts :output-dir)))) + deps (closure/add-dependencies opts core-js)] + ;; output unoptimized code and the deps file + ;; for all compiled namespaces + (apply closure/output-unoptimized + (assoc opts :output-to (.getPath (io/file output-dir deps-file))) + deps) + ;; load the deps file so we can goog.require cljs.core etc. + (load-js-file engine deps-file)))) + +(defn load-ns [engine ns] + (eval-str engine + (format "goog.require(\"%s\");" (comp/munge (first ns))))) + +(def repl-filename "") + +(def ^:private skip-types #{"com.oracle.truffle.api.interop.java.TruffleMap" + "com.oracle.truffle.api.interop.java.TruffleMap$FunctionTruffleMap"}) + +(defn- safe-to-string + "A safe version that avoids calling .toString on types known to cause stack overflow. + Also has a guard to return an unreadable containing the type if this is encountered." + [x] + (let [type-str (pr-str (type x))] + (try + (if (contains? skip-types type-str) + (str #"<" type-str ">") + (.toString x)) + (catch StackOverflowError _ + (str "#"))))) + +(defrecord GraalJSEnv [engine debug] + repl/IReplEnvOptions + (-repl-options [this] + {:output-dir ".cljs_graaljs_repl" + :target :graaljs}) + repl/IJavaScriptEnv + (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] + (init-engine engine opts debug) + (let [env (ana/empty-env)] + (if output-to + (load-js-file engine output-to) + (bootstrap-repl engine output-dir opts)) + (repl/evaluate-form this env repl-filename + '(.require js/goog "cljs.core")) + ;; monkey-patch goog.isProvided_ to suppress useless errors + (repl/evaluate-form this env repl-filename + '(set! js/goog.isProvided_ (fn [ns] false))) + ;; monkey-patch goog.require to be more sensible + (repl/evaluate-form this env repl-filename + '(do + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (if (some? goog/debugLoader_) + (.getPathFromDeps_ goog/debugLoader_ name) + (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))))) + (-evaluate [{engine :engine :as this} filename line js] + (when debug (println "Evaluating: " js)) + (try + {:status :success + :value (if-let [r (eval-str engine js)] (safe-to-string r) "")} + (catch ScriptException e + (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] + {:status :exception + :value (.getMessage root-cause) + :stacktrace + (apply str + (interpose "\n" + (map #(subs % 5) + (filter #(clojure.string/starts-with? % ".") + (map str + (.getStackTrace root-cause))))))})) + (catch Throwable e + (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] + {:status :exception + :value (.getMessage root-cause) + :stacktrace + (apply str + (interpose "\n" + (map str + (.getStackTrace root-cause))))})))) + (-load [{engine :engine :as this} ns url] + (load-ns engine ns)) + (-tear-down [this] + (tear-down-engine engine)) + repl/IParseStacktrace + (-parse-stacktrace [this frames-str ret opts] + (st/parse-stacktrace this frames-str + (assoc ret :ua-product :graaljs) opts)) + repl/IParseError + (-parse-error [_ err _] + (update-in err [:stacktrace] + (fn [st] + (string/join "\n" (drop 1 (string/split st #"\n"))))))) + +(defn repl-env* [{:keys [debug] :as opts}] + (let [engine (create-engine)] + (merge + (GraalJSEnv. engine debug) + opts))) + +(defn repl-env + "Create a Graal.JS repl-env for use with the repl/repl* method in ClojureScript." + [& {:as opts}] + (repl-env* opts)) + +;; ------------------------------------------------------------------------- +;; Command Line Support + +(defn -main [& args] + (apply cli/main repl-env args)) diff --git a/src/main/clojure/cljs/server/graaljs.clj b/src/main/clojure/cljs/server/graaljs.clj new file mode 100644 index 000000000..d33360518 --- /dev/null +++ b/src/main/clojure/cljs/server/graaljs.clj @@ -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.server.graaljs + (:require [cljs.repl :as repl] + [cljs.repl.graaljs :as graaljs] + [cljs.core.server :as server])) + +(defn repl + ([] + (repl nil)) + ([{:keys [opts env-opts]}] + (repl/repl* (graaljs/repl-env* env-opts) opts))) + +(defn prepl + ([] + (prepl nil)) + ([{:keys [opts env-opts]}] + (apply server/io-prepl + (mapcat identity + {:repl-env (graaljs/repl-env* env-opts) + :opts opts})))) diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj index bef3fa26b..ccce30233 100644 --- a/src/test/cljs_cli/cljs_cli/test.clj +++ b/src/test/cljs_cli/cljs_cli/test.clj @@ -115,10 +115,15 @@ (output-is "\"0000000003\"")))) (deftest test-cljs-2780 - (with-repl-env-filter #{"node" "nashorn"} + (with-repl-env-filter #{"node" "nashorn" "graaljs"} (-> (cljs-main "-e" "(do (js/setTimeout #(prn :end) 500) nil)" "-e" ":begin") (output-is :begin :end)))) + +(deftest test-graaljs-polyglot + (with-repl-env-filter #{"graaljs"} + (-> (cljs-main "-e" "(.eval js/Polyglot \"js\" \"1+1\")") + (output-is 2)))) From a93ac962c97845a07aa64cbfe95abcca63b5ae9b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 19 Jul 2018 15:03:57 -0400 Subject: [PATCH 3250/4033] CLJS-2541: binding not made in parallel --- src/main/cljs/cljs/js.cljs | 16 ++++++------- src/main/cljs/cljs/pprint.cljs | 35 ++++++++++++++-------------- src/main/clojure/cljs/core.cljc | 10 ++++---- src/test/cljs/cljs/binding_test.cljs | 16 +++++++++++++ 4 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index e64df6f4e..899215bd8 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -184,10 +184,10 @@ (.fromCharCode js/String (str "0x" match)))) (base64/encodeString)))))) -(defn- current-alias-map - [] - (->> (merge (get-in @env/*compiler* [::ana/namespaces ana/*cljs-ns* :requires]) - (get-in @env/*compiler* [::ana/namespaces ana/*cljs-ns* :require-macros])) +(defn- alias-map + [compiler cljs-ns] + (->> (merge (get-in compiler [::ana/namespaces cljs-ns :requires]) + (get-in compiler [::ana/namespaces cljs-ns :require-macros])) (remove (fn [[k v]] (symbol-identical? k v))) (into {}))) @@ -685,7 +685,7 @@ ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) ana/*passes* (:*passes* bound-vars) - r/*alias-map* (current-alias-map) + r/*alias-map* (alias-map @(:*compiler* bound-vars) ns) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars) @@ -801,7 +801,7 @@ ana/*cljs-static-fns* (:static-fns opts) ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns (:*cljs-ns* bound-vars)) - r/*alias-map* (current-alias-map) + r/*alias-map* (alias-map @(:*compiler* bound-vars) (:*cljs-ns* bound-vars)) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] @@ -916,7 +916,7 @@ ana/*cljs-static-fns* (:static-fns opts) ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) - r/*alias-map* (current-alias-map) + r/*alias-map* (alias-map @(:*compiler* bound-vars) ns) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars)] @@ -1051,7 +1051,7 @@ ana/*cljs-static-fns* (:static-fns opts) ana/*fn-invoke-direct* (and (:static-fns opts) (:fn-invoke-direct opts)) *ns* (create-ns ns) - r/*alias-map* (current-alias-map) + r/*alias-map* (alias-map @(:*compiler* bound-vars) ns) r/*data-readers* (:*data-readers* bound-vars) r/resolve-symbol resolve-symbol comp/*source-map-data* (:*sm-data* bound-vars) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index ccd79524d..9335ea85f 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -3304,22 +3304,21 @@ type-map {"core$future_call" "Future", in ks. If ks are not specified, use the keys of the first item in rows." {:added "1.3"} ([ks rows] - (binding [*print-newline*] - (when (seq rows) - (let [widths (map - (fn [k] - (apply max (count (str k)) (map #(count (str (get % k))) rows))) - ks) - spacers (map #(apply str (repeat % "-")) widths) - fmt-row (fn [leader divider trailer row] - (str leader - (apply str (interpose divider - (for [[col width] (map vector (map #(get row %) ks) widths)] - (add-padding width (str col))))) - trailer))] - (cljs.core/println) - (cljs.core/println (fmt-row "| " " | " " |" (zipmap ks ks))) - (cljs.core/println (fmt-row "|-" "-+-" "-|" (zipmap ks spacers))) - (doseq [row rows] - (cljs.core/println (fmt-row "| " " | " " |" row))))))) + (when (seq rows) + (let [widths (map + (fn [k] + (apply max (count (str k)) (map #(count (str (get % k))) rows))) + ks) + spacers (map #(apply str (repeat % "-")) widths) + fmt-row (fn [leader divider trailer row] + (str leader + (apply str (interpose divider + (for [[col width] (map vector (map #(get row %) ks) widths)] + (add-padding width (str col))))) + trailer))] + (cljs.core/println) + (cljs.core/println (fmt-row "| " " | " " |" (zipmap ks ks))) + (cljs.core/println (fmt-row "|-" "-+-" "-|" (zipmap ks spacers))) + (doseq [row rows] + (cljs.core/println (fmt-row "| " " | " " |" row)))))) ([rows] (print-table (keys (first rows)) rows))) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index d32e634ce..03e6bb222 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2160,11 +2160,13 @@ [bindings & body] (core/let [names (take-nth 2 bindings) vals (take-nth 2 (drop 1 bindings)) - tempnames (map (comp gensym name) names) - binds (map core/vector names vals) - resets (reverse (map core/vector names tempnames)) + orig-val-syms (map (comp gensym #(core/str % "-orig-val__") name) names) + temp-val-syms (map (comp gensym #(core/str % "-temp-val__") name) names) + binds (map core/vector names temp-val-syms) + resets (reverse (map core/vector names orig-val-syms)) bind-value (core/fn [[k v]] (core/list 'set! k v))] - `(let [~@(interleave tempnames names)] + `(let [~@(interleave orig-val-syms names) + ~@(interleave temp-val-syms vals)] ~@(map bind-value binds) (try ~@body diff --git a/src/test/cljs/cljs/binding_test.cljs b/src/test/cljs/cljs/binding_test.cljs index fa193f174..ce711697f 100644 --- a/src/test/cljs/cljs/binding_test.cljs +++ b/src/test/cljs/cljs/binding_test.cljs @@ -19,3 +19,19 @@ (is (with-redefs [o/bar 2] (= o/bar 2))) (is (= o/bar 10))) + +(def ^:dynamic *a* 1) +(def ^:dynamic *b* nil) + +(deftest test-binding-parallel + (is (= 2 (binding [*a* 10 + *b* (inc *a*)] + *b*)))) + +(def a 1) +(def b nil) + +(deftest test-redefs-parallel + (is (= 2 (with-redefs [a 10 + b (inc a)] + b)))) From c40a475ff42273d4d355486104d56c1ec5d795d3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 11:12:19 -0400 Subject: [PATCH 3251/4033] CLJS-2837: [spec] `cat` specs should verify value is sequential --- src/main/cljs/cljs/spec/alpha.cljs | 6 +++--- src/test/cljs/cljs/spec_test.cljs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 10b3f3b47..48d074e37 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -1248,14 +1248,14 @@ Spec (conform* [_ x] - (if (c/or (nil? x) (coll? x)) + (if (c/or (nil? x) (sequential? x)) (re-conform re (seq x)) ::invalid)) (unform* [_ x] (op-unform re x)) (explain* [_ path via in x] - (if (c/or (nil? x) (coll? x)) + (if (c/or (nil? x) (sequential? x)) (re-explain path via in re (seq x)) - [{:path path :pred (op-describe re) :val x :via via :in in}])) + [{:path path :pred `(fn [~'%] (c/or (nil? ~'%) (sequential? ~'%))) :val x :via via :in in}])) (gen* [_ overrides path rmap] (if gfn (gfn) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 02130dfda..e68cc304f 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -204,7 +204,7 @@ opt nil nil nil opt [] nil nil - opt :k ::s/invalid '[{:pred (cljs.spec.alpha/? cljs.core/keyword?), :val :k}] + opt :k ::s/invalid '[{:pred (cljs.core/fn [%] (cljs.core/or (cljs.core/nil? %) (cljs.core/sequential? %))), :val :k}] opt [:k] :k nil opt [:k1 :k2] ::s/invalid '[{:reason "Extra input", :pred (cljs.spec.alpha/? cljs.core/keyword?), :val (:k2)}] opt [:k1 :k2 "x"] ::s/invalid '[{:reason "Extra input", :pred (cljs.spec.alpha/? cljs.core/keyword?), :val (:k2 "x")}] @@ -212,7 +212,7 @@ andre nil nil nil andre [] nil nil - andre :k ::s/invalid '[{:pred (cljs.spec.alpha/& (cljs.spec.alpha/* cljs.core/keyword?) cljs.spec-test/even-count?), :val :k}] + andre :k ::s/invalid '[{:pred (cljs.core/fn [%] (cljs.core/or (cljs.core/nil? %) (cljs.core/sequential? %))), :val :k}] andre [:k] ::s/invalid '[{:pred cljs.spec-test/even-count?, :val [:k]}] andre [:j :k] [:j :k] nil From 0ec3870d8e301f2d3c99c7dd9714f3fb101ba5c9 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 12:17:49 -0400 Subject: [PATCH 3252/4033] CLJS-2838: [spec] s/& does not check preds if regex matches empty collection --- src/main/cljs/cljs/spec/alpha.cljs | 5 ++--- src/test/cljs/cljs/spec_test.cljs | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 48d074e37..725a6547c 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -1012,9 +1012,8 @@ ::accept true nil nil ::amp (c/and (accept-nil? p1) - (c/or (noret? p1 (preturn p1)) - (let [ret (-> (preturn p1) (and-preds ps (next forms)))] - (not (invalid? ret))))) + (let [ret (-> (preturn p1) (and-preds ps (next forms)))] + (not (invalid? ret)))) ::rep (c/or (identical? p1 p2) (accept-nil? p1)) ::pcat (every? accept-nil? ps) ::alt (c/some accept-nil? ps)))) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index e68cc304f..711790667 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -126,6 +126,7 @@ plus (s/+ keyword?) opt (s/? keyword?) andre (s/& (s/* keyword?) even-count?) + andre2 (s/& (s/* keyword?) #{[:a]}) m (s/map-of keyword? string?) mkeys (s/map-of (s/and keyword? (s/conformer name)) any?) mkeys2 (s/map-of (s/and keyword? (s/conformer name)) any? :conform-keys true) @@ -216,6 +217,10 @@ andre [:k] ::s/invalid '[{:pred cljs.spec-test/even-count?, :val [:k]}] andre [:j :k] [:j :k] nil + andre2 nil ::s/invalid [{:pred #{[:a]}, :val []}] + andre2 [] ::s/invalid [{:pred #{[:a]}, :val []}] + andre2 [:a] [:a] nil + m nil ::s/invalid '[{:pred cljs.core/map?, :val nil}] m {} {} nil m {:a "b"} {:a "b"} nil From 455724c99afb000d853af6838065d62ce6661d2b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 13:45:54 -0400 Subject: [PATCH 3253/4033] CLJS-2839: [spec] s/& explain-data :pred problem --- src/main/cljs/cljs/spec/alpha.cljc | 2 +- src/main/cljs/cljs/spec/alpha.cljs | 16 ++++++++-------- src/test/cljs/cljs/spec_test.cljs | 6 ++++++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index d53d15b6a..2de3aa70a 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -361,7 +361,7 @@ conjunction of the predicates, and any conforming they might perform." [re & preds] (let [pv (vec preds)] - `(amp-impl ~re ~pv '~(mapv #(res &env %) pv)))) + `(amp-impl ~re '~(res &env re) ~pv '~(mapv #(res &env %) pv)))) (defmacro conformer "takes a predicate function with the semantics of conform i.e. it should return either a diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 725a6547c..b13126d20 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -963,8 +963,8 @@ (defn ^:skip-wiki amp-impl "Do not call this directly, use '&'" - [re preds pred-forms] - {::op ::amp :p1 re :ps preds :forms pred-forms}) + [re re-form preds pred-forms] + {::op ::amp :p1 re :amp re-form :ps preds :forms pred-forms}) (defn- filter-alt [ps ks forms f] (if (c/or ks forms) @@ -1071,7 +1071,7 @@ (defn- deriv [p x] - (let [{[p0 & pr :as ps] :ps, [k0 & kr :as ks] :ks, :keys [::op p1 p2 ret splice forms] :as p} (reg-resolve! p)] + (let [{[p0 & pr :as ps] :ps, [k0 & kr :as ks] :ks, :keys [::op p1 p2 ret splice forms amp] :as p} (reg-resolve! p)] (when p (case op ::accept nil @@ -1082,7 +1082,7 @@ (let [ret (-> (preturn p1) (and-preds ps (next forms)))] (when-not (invalid? ret) (accept ret))) - (amp-impl p1 ps forms))) + (amp-impl p1 amp ps forms))) ::pcat (alt2 (pcat* {:ps (cons (deriv p0 x) pr), :ks ks, :forms forms, :ret ret}) (when (accept-nil? p0) (deriv (pcat* {:ps pr, :ks kr, :forms (next forms), :ret (add-ret p0 ret k0)}) x))) ::alt (alt* (map #(deriv % x) ps) ks forms) @@ -1090,13 +1090,13 @@ (when (accept-nil? p1) (deriv (rep* p2 p2 (add-ret p1 ret nil) splice forms) x))))))) (defn- op-describe [p] - (let [{:keys [::op ps ks forms splice p1 rep+ maybe] :as p} (reg-resolve! p)] + (let [{:keys [::op ps ks forms splice p1 rep+ maybe amp] :as p} (reg-resolve! p)] ;;(prn {:op op :ks ks :forms forms :p p}) (when p (case op ::accept nil nil p - ::amp (list* 'cljs.spec.alpha/& (op-describe p1) forms) + ::amp (list* 'cljs.spec.alpha/& amp forms) ::pcat (if rep+ (list `+ rep+) (cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) forms))) @@ -1126,10 +1126,10 @@ ::amp (if (empty? input) (if (accept-nil? p1) (explain-pred-list forms ps path via in (preturn p1)) - (insufficient path (op-describe p1))) + (insufficient path (:amp p))) (if-let [p1 (deriv p1 x)] (explain-pred-list forms ps path via in (preturn p1)) - (op-explain (op-describe p1) p1 path via in input))) + (op-explain (:amp p) p1 path via in input))) ::pcat (let [pkfs (map vector ps (c/or (seq ks) (repeat nil)) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 711790667..66748298d 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -298,6 +298,12 @@ [{10 10 20 "x"}] [{10 10 20 "x"}])) +(deftest &-explain-pred + (are [val expected] + (= expected (-> (s/explain-data (s/& int? even?) val) ::s/problems first :pred)) + [] 'cljs.core/int? + [0 2] '(cljs.spec.alpha/& cljs.core/int? cljs.core/even?))) + (s/fdef foo.bar/cljs-2275 :args (s/cat :k keyword?) :ret string?) From e58cf89115f7f08354ba09caeb63eff08b142f01 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 13:56:38 -0400 Subject: [PATCH 3254/4033] CLJS-2840: [spec] s/keys explain-data :pred problem --- src/main/cljs/cljs/spec/alpha.cljs | 2 +- src/test/cljs/cljs/spec_test.cljs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index b13126d20..c0002a70c 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -417,7 +417,7 @@ ret)))) (explain* [_ path via in x] (if-not (map? x) - [{:path path :pred 'map? :val x :via via :in in}] + [{:path path :pred `map? :val x :via via :in in}] (let [reg (registry)] (apply concat (when-let [probs (->> (map (fn [pred form] (when-not (pred x) form)) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 66748298d..4b0560561 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -304,6 +304,9 @@ [] 'cljs.core/int? [0 2] '(cljs.spec.alpha/& cljs.core/int? cljs.core/even?))) +(deftest keys-explain-pred + (is (= 'cljs.core/map? (-> (s/explain-data (s/keys :req [::x]) :a) ::s/problems first :pred)))) + (s/fdef foo.bar/cljs-2275 :args (s/cat :k keyword?) :ret string?) From 7187f8f91ee2cc35b41e22899c6103c86674dd2b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 14:30:32 -0400 Subject: [PATCH 3255/4033] CLJS-2844: [spec] Add support for undefining a spec --- src/main/cljs/cljs/spec/alpha.cljc | 7 ++++--- src/main/cljs/cljs/spec/alpha.cljs | 12 +++++++----- src/test/cljs/cljs/spec_test.cljs | 5 +++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 2de3aa70a..1500dd2f5 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -61,9 +61,10 @@ (symbol (str ana/*cljs-ns*) (str s)))) (defmacro def - "Given a namespace-qualified keyword or resolveable symbol k, and a spec, - spec-name, predicate or regex-op makes an entry in the registry mapping k to - the spec" + "Given a namespace-qualified keyword or resolveable symbol k, and a + spec, spec-name, predicate or regex-op makes an entry in the + registry mapping k to the spec. Use nil to remove an entry in + the registry for k." [k spec-form] (let [k (if (symbol? k) (ns-qualify &env k) k) form (res &env spec-form)] diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index c0002a70c..2e3140ead 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -294,11 +294,13 @@ "Do not call this directly, use 'def'" [k form spec] (assert (c/and (ident? k) (namespace k)) "k must be namespaced keyword or resolveable symbol") - (let [spec (if (c/or (spec? spec) (regex? spec) (get @registry-ref spec)) - spec - (spec-impl form spec nil nil))] - (swap! registry-ref assoc k (with-name spec k)) - k)) + (if (nil? spec) + (swap! registry-ref dissoc k) + (let [spec (if (c/or (spec? spec) (regex? spec) (get @registry-ref spec)) + spec + (spec-impl form spec nil nil))] + (swap! registry-ref assoc k (with-name spec k)))) + k) (defn registry "returns the registry map, prefer 'get-spec' to lookup a spec by name" diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 4b0560561..7c04e11b8 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -307,6 +307,11 @@ (deftest keys-explain-pred (is (= 'cljs.core/map? (-> (s/explain-data (s/keys :req [::x]) :a) ::s/problems first :pred)))) +(deftest remove-def + (is (= ::ABC (s/def ::ABC string?))) + (is (= ::ABC (s/def ::ABC nil))) + (is (nil? (s/get-spec ::ABC)))) + (s/fdef foo.bar/cljs-2275 :args (s/cat :k keyword?) :ret string?) From 1261aed07d5e11b38d5deafcf6afa4a7abe4b346 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 14:52:15 -0400 Subject: [PATCH 3256/4033] CLJS-2845: [spec] generate random subsets of or'd required keys in map specs --- src/main/cljs/cljs/spec/alpha.cljs | 58 ++++++++++++++++++++------ src/main/cljs/cljs/spec/gen/alpha.cljs | 6 +-- src/test/cljs/cljs/spec_test.cljs | 46 ++++++++++++++++++++ 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 2e3140ead..cc673ca00 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -378,6 +378,35 @@ (explain* pred path (if-let [name (spec-name pred)] (conj via name) via) in v) [{:path path :pred form :val v :via via :in in}]))) +(declare ^{:arglists '([s] [min-count s])} or-k-gen + ^{:arglists '([s])} and-k-gen) + +(defn- k-gen + "returns a generator for form f, which can be a keyword or a list + starting with 'or or 'and." + [f] + (cond + (keyword? f) (gen/return f) + (= 'or (first f)) (or-k-gen 1 (rest f)) + (= 'and (first f)) (and-k-gen (rest f)))) + +(defn- or-k-gen + "returns a tuple generator made up of generators for a random subset + of min-count (default 0) to all elements in s." + ([s] (or-k-gen 0 s)) + ([min-count s] + (gen/bind (gen/tuple + (gen/choose min-count (count s)) + (gen/shuffle (map k-gen s))) + (fn [[n gens]] + (apply gen/tuple (take n gens)))))) + +(defn- and-k-gen + "returns a tuple generator made up of generators for every element + in s." + [s] + (apply gen/tuple (map k-gen s))) + (defn ^:skip-wiki map-spec-impl "Do not call this directly, use 'spec' with a map argument" [{:keys [req-un opt-un keys-pred pred-exprs opt-keys req-specs req req-keys opt-specs pred-forms opt gfn] @@ -438,21 +467,26 @@ (if gfn (gfn) (let [rmap (inck rmap id) - gen (fn [k s] (gensub s overrides (conj path k) rmap k)) + rgen (fn [k s] [k (gensub s overrides (conj path k) rmap k)]) ogen (fn [k s] (when-not (recur-limit? rmap id path k) [k (gen/delay (gensub s overrides (conj path k) rmap k))])) - req-gens (map gen req-keys req-specs) - opt-gens (remove nil? (map ogen opt-keys opt-specs))] - (when (every? identity (concat req-gens opt-gens)) - (let [reqs (zipmap req-keys req-gens) - opts (into {} opt-gens)] - (gen/bind (gen/choose 0 (count opts)) - #(let [args (concat (seq reqs) (when (seq opts) (shuffle (seq opts))))] - (->> args - (take (c/+ % (count reqs))) - (apply concat) - (apply gen/hash-map))))))))) + reqs (map rgen req-keys req-specs) + opts (remove nil? (map ogen opt-keys opt-specs))] + (when (every? identity (concat (map second reqs) (map second opts))) + (gen/bind + (gen/tuple + (and-k-gen req) + (or-k-gen opt) + (and-k-gen req-un) + (or-k-gen opt-un)) + (fn [[req-ks opt-ks req-un-ks opt-un-ks]] + (let [qks (flatten (concat req-ks opt-ks)) + unqks (map (comp keyword name) (flatten (concat req-un-ks opt-un-ks)))] + (->> (into reqs opts) + (filter #((set (concat qks unqks)) (first %))) + (apply concat) + (apply gen/hash-map))))))))) (with-gen* [_ gfn] (map-spec-impl (assoc argm :gfn gfn))) (describe* [_] (cons `keys (cond-> [] diff --git a/src/main/cljs/cljs/spec/gen/alpha.cljs b/src/main/cljs/cljs/spec/gen/alpha.cljs index 6ff47edef..07af0ba05 100644 --- a/src/main/cljs/cljs/spec/gen/alpha.cljs +++ b/src/main/cljs/cljs/spec/gen/alpha.cljs @@ -8,7 +8,7 @@ (ns cljs.spec.gen.alpha (:refer-clojure :exclude [boolean cat hash-map list map not-empty set vector - char double int keyword symbol string uuid delay]) + char double int keyword symbol string uuid delay shuffle]) (:require-macros [cljs.core :as c] [cljs.spec.gen.alpha :as gen :refer [dynaload lazy-combinators lazy-prims]]) (:require [cljs.core :as c]) @@ -69,7 +69,7 @@ (lazy-combinators hash-map list map not-empty set vector vector-distinct fmap elements bind choose one-of such-that tuple sample return - large-integer* double* frequency) + large-integer* double* frequency shuffle) (lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double int keyword keyword-ns large-integer ratio simple-type simple-type-printable @@ -160,7 +160,7 @@ gen-builtins :b (gen-for-pred keyword?)} opts {:c (gen-for-pred string?)}] (generate (bind (choose 0 (count opts)) - #(let [args (concat (seq reqs) (shuffle (seq opts)))] + #(let [args (concat (seq reqs) (c/shuffle (seq opts)))] (->> args (take (+ % (count reqs))) (mapcat identity) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 7c04e11b8..813269b8d 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -312,6 +312,52 @@ (is (= ::ABC (s/def ::ABC nil))) (is (nil? (s/get-spec ::ABC)))) +;; TODO replace this with a generative test once we have specs for s/keys +(deftest map-spec-generators + (s/def ::a nat-int?) + (s/def ::b boolean?) + (s/def ::c keyword?) + (s/def ::d double?) + (s/def ::e inst?) + + (is (= #{[::a] + [::a ::b] + [::a ::b ::c] + [::a ::c]} + (->> (s/exercise (s/keys :req [::a] :opt [::b ::c]) 100) + (map (comp sort keys first)) + (into #{})))) + + (is (= #{[:a] + [:a :b] + [:a :b :c] + [:a :c]} + (->> (s/exercise (s/keys :req-un [::a] :opt-un [::b ::c]) 100) + (map (comp sort keys first)) + (into #{})))) + + (is (= #{[::a ::b] + [::a ::b ::c ::d] + [::a ::b ::c ::d ::e] + [::a ::b ::c ::e] + [::a ::c ::d] + [::a ::c ::d ::e] + [::a ::c ::e]} + (->> (s/exercise (s/keys :req [::a (or ::b (and ::c (or ::d ::e)))]) 200) + (map (comp vec sort keys first)) + (into #{})))) + + (is (= #{[:a :b] + [:a :b :c :d] + [:a :b :c :d :e] + [:a :b :c :e] + [:a :c :d] + [:a :c :d :e] + [:a :c :e]} + (->> (s/exercise (s/keys :req-un [::a (or ::b (and ::c (or ::d ::e)))]) 200) + (map (comp vec sort keys first)) + (into #{}))))) + (s/fdef foo.bar/cljs-2275 :args (s/cat :k keyword?) :ret string?) From 387ae34f7d667853c6995b0f0c406455d02e9fb3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 14:12:13 -0400 Subject: [PATCH 3257/4033] CLJS-2842: [spec] Clarify s/every docstring for :kind --- src/main/cljs/cljs/spec/alpha.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 1500dd2f5..735df4bb1 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -228,7 +228,7 @@ Takes several kwargs options that further constrain the collection: - :kind - a pred/spec that the collection type must satisfy, e.g. vector? + :kind - a pred that the collection type must satisfy, e.g. vector? (default nil) Note that if :kind is specified and :into is not, this pred must generate in order for every to generate. :count - specifies coll has exactly this count (default nil) From 4d408e1aae6961611200abb9f839838ebf6966a8 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 14:09:13 -0400 Subject: [PATCH 3258/4033] CLJS-2841: [spec] instrument exception doesn't contain function name in ex-data --- src/main/cljs/cljs/spec/test/alpha.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 4c94dddda..7b4997ff6 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -96,6 +96,7 @@ (.-stack (js/Error.)) (get-env) nil)) ed (merge (assoc (s/explain-data* spec [role] [] [] data) + ::s/fn (->sym v) ::s/args args ::s/failure :instrument) (when caller From a6885a0cb21274fcab180cc82c4b791ee96197c5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 17:40:59 -0400 Subject: [PATCH 3259/4033] CLJS-2847: s/coll-of and s/every gen is very slow if :kind specified without :into --- src/main/cljs/cljs/spec/alpha.cljs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index cc673ca00..9e2ffe3a6 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -825,17 +825,19 @@ (with-gen* [_ gfn] (merge-spec-impl forms preds gfn)) (describe* [_] `(merge ~@forms)))) +(def ^:private empty-coll {`vector? [], `set? #{}, `list? (), `map? {}}) + (defn ^:skip-wiki every-impl "Do not call this directly, use 'every', 'every-kv', 'coll-of' or 'map-of'" ([form pred opts] (every-impl form pred opts nil)) - ([form pred {gen-into :into + ([form pred {conform-into :into describe-form ::describe :keys [kind ::kind-form count max-count min-count distinct gen-max ::kfn ::cpred conform-keys ::conform-all] :or {gen-max 20} :as opts} gfn] - (let [conform-into gen-into + (let [gen-into (if conform-into (empty conform-into) (get empty-coll kind-form)) spec (delay (specize pred)) check? #(valid? @spec %) kfn (c/or kfn (fn [i v] i)) @@ -926,7 +928,7 @@ (let [pgen (gensub pred overrides path rmap form)] (gen/bind (cond - gen-into (gen/return (empty gen-into)) + gen-into (gen/return gen-into) kind (gen/fmap #(if (empty? %) % (empty %)) (gensub kind overrides path rmap form)) :else (gen/return [])) From 6152a3165b3196cc25e0929f6b2367d2761f15ac Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 17:23:41 -0400 Subject: [PATCH 3260/4033] CLJS-2846: [spec] s/tuple explain-data :pred problem --- src/main/cljs/cljs/spec/alpha.cljs | 2 +- src/test/cljs/cljs/spec_test.cljs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 9e2ffe3a6..7f5efc3ef 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -617,7 +617,7 @@ (explain* [_ path via in x] (cond (not (vector? x)) - [{:path path :pred 'vector? :val x :via via :in in}] + [{:path path :pred `vector? :val x :via via :in in}] (not= (count x) (count preds)) [{:path path :pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}] diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 813269b8d..fe7d876a2 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -358,6 +358,12 @@ (map (comp vec sort keys first)) (into #{}))))) +(deftest tuple-explain-pred + (are [val expected] + (= expected (-> (s/explain-data (s/tuple int?) val) ::s/problems first :pred)) + :a 'cljs.core/vector? + [] '(cljs.core/= (cljs.core/count %) 1))) + (s/fdef foo.bar/cljs-2275 :args (s/cat :k keyword?) :ret string?) From d2cc14af6b26cd06bbd4d871123d4dd82c8ca4e0 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 21 Jul 2018 17:51:10 -0400 Subject: [PATCH 3261/4033] CLJS-2848: Default explain printer prints root val and spec --- src/main/cljs/cljs/spec/alpha.cljs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 7f5efc3ef..f38a4aaf0 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -238,12 +238,7 @@ (when-not (#{:path :pred :val :reason :via :in} k) (print "\n\t" (pr-str k) " ") (pr v))) - (newline)) - (doseq [[k v] ed] - (when-not (#{::problems} k) - (print (pr-str k) " ") - (pr v) - (newline)))))) + (newline))))) (println "Success!"))) (def ^:dynamic *explain-out* explain-printer) From 8614498e6c4a0613a168bf30f02756b629575871 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 22 Jul 2018 13:41:30 -0400 Subject: [PATCH 3262/4033] CLJS-2665: Port clojure.spec.test.alpha/enumerate-namespace --- src/main/cljs/cljs/spec/test/alpha.cljc | 17 +++++++++++++++++ src/test/cljs/cljs/spec/test/test_ns1.cljs | 5 +++++ src/test/cljs/cljs/spec/test/test_ns2.cljs | 3 +++ src/test/cljs/cljs/spec/test_test.cljs | 14 +++++++++++++- 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs/cljs/spec/test/test_ns1.cljs create mode 100644 src/test/cljs/cljs/spec/test/test_ns2.cljs diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 78476639f..ed93e6159 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -22,6 +22,23 @@ (list x) x)) +(defn- enumerate-namespace* [sym-or-syms] + (into #{} + (mapcat + (fn [sym] + (->> (vals (ana-api/ns-interns sym)) + (map :name) + (map + (fn [name-sym] + (symbol (name sym) (name name-sym))))))) + (collectionize sym-or-syms))) + +(defmacro enumerate-namespace + "Given a symbol naming an ns, or a collection of such symbols, +returns the set of all symbols naming vars in those nses." + [ns-sym-or-syms] + `'~(enumerate-namespace* (eval ns-sym-or-syms))) + (defn- fn-spec-name? [s] (symbol? s)) diff --git a/src/test/cljs/cljs/spec/test/test_ns1.cljs b/src/test/cljs/cljs/spec/test/test_ns1.cljs new file mode 100644 index 000000000..9efe08b6a --- /dev/null +++ b/src/test/cljs/cljs/spec/test/test_ns1.cljs @@ -0,0 +1,5 @@ +(ns cljs.spec.test.test-ns1) + +(def x 1) + +(def y 2) diff --git a/src/test/cljs/cljs/spec/test/test_ns2.cljs b/src/test/cljs/cljs/spec/test/test_ns2.cljs new file mode 100644 index 000000000..e93d3cbc5 --- /dev/null +++ b/src/test/cljs/cljs/spec/test/test_ns2.cljs @@ -0,0 +1,3 @@ +(ns cljs.spec.test.test-ns2) + +(def z 3) diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index af5124746..0bf9294a5 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -2,7 +2,9 @@ (:require-macros [cljs.spec.test.test-macros]) (:require [cljs.test :as test :refer-macros [deftest is are run-tests]] [cljs.spec.alpha :as s] - [cljs.spec.test.alpha :as stest])) + [cljs.spec.test.alpha :as stest] + [cljs.spec.test.test-ns1] + [cljs.spec.test.test-ns2])) (s/fdef clojure.core/symbol :args (s/alt :separate (s/cat :ns string? :n string?) @@ -85,3 +87,13 @@ (deftest test-2755 (is (uri? (ffirst (s/exercise uri? 1))))) + +(deftest test-cljs-2665 + (is (= '#{cljs.spec.test.test-ns1/x cljs.spec.test.test-ns1/y cljs.spec.test.test-ns2/z} + (stest/enumerate-namespace '[cljs.spec.test.test-ns1 cljs.spec.test.test-ns2]))) + (is (= '#{cljs.spec.test.test-ns1/x cljs.spec.test.test-ns1/y cljs.spec.test.test-ns2/z} + (stest/enumerate-namespace ['cljs.spec.test.test-ns1 'cljs.spec.test.test-ns2]))) + (is (= '#{cljs.spec.test.test-ns1/x cljs.spec.test.test-ns1/y} + (stest/enumerate-namespace 'cljs.spec.test.test-ns1))) + (is (= '#{cljs.spec.test.test-ns2/z} + (stest/enumerate-namespace 'cljs.spec.test.test-ns2)))) From b369fe14ec1b899e4b65fbbfe7efdd000a491ee7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 26 Jul 2018 15:52:51 -0400 Subject: [PATCH 3263/4033] CLJS-2725: Doc on spec keywords --- src/main/cljs/cljs/repl.cljs | 4 +++- src/main/clojure/cljs/repl.cljc | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index 2035dd8f9..0acc1735c 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -12,7 +12,7 @@ (defn print-doc [{n :ns nm :name :as m}] (println "-------------------------") - (println (str (when-let [ns (:ns m)] (str ns "/")) (:name m))) + (println (or (:spec m) (str (when-let [ns (:ns m)] (str ns "/")) (:name m)))) (when (:protocol m) (println "Protocol")) (cond @@ -38,6 +38,8 @@ (do (when (:macro m) (println "Macro")) + (when (:spec m) + (println "Spec")) (when (:repl-special-function m) (println "REPL Special Function")) (println " " (:doc m)) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 71d5d8e43..19ec5633c 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1231,7 +1231,8 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) :repl-special-function true)) (defmacro doc - "Prints documentation for a var or special form given its name" + "Prints documentation for a var or special form given its name, + or for a spec if given a keyword" [name] `(print (binding [cljs.core/*print-newline* true] @@ -1245,6 +1246,9 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (repl-special-doc-map name) `(cljs.repl/print-doc (quote ~(repl-special-doc name))) + (keyword? name) + `(cljs.repl/print-doc {:spec ~name :doc (cljs.spec.alpha/describe ~name)}) + (ana-api/find-ns name) `(cljs.repl/print-doc (quote ~(select-keys (ana-api/find-ns name) [:name :doc]))) From f13c08c90f27aa2a71694859f288e9b5f2407c73 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 31 Jul 2018 12:45:43 -0400 Subject: [PATCH 3264/4033] CLJS-2852: Clojure imparity: ns-publics returns different arglists for macros --- src/main/clojure/cljs/core.cljc | 21 +++++++++++++++------ src/test/cljs/cljs/macro_test.cljs | 8 +++++++- src/test/cljs/cljs/macro_test/cljs2852.clj | 21 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 src/test/cljs/cljs/macro_test/cljs2852.clj diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 03e6bb222..df8602724 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3056,6 +3056,13 @@ (.push ~dest (unchecked-get (js-arguments) i#)) (recur (inc i#)))))) +(core/defn- elide-implicit-macro-args [arglists] + (core/map (core/fn [arglist] + (if (core/vector? arglist) + (core/subvec arglist 2) + (core/drop 2 arglist))) + arglists)) + (core/defn- variadic-fn [name meta [[arglist & body :as method] :as fdecl] emit-var?] (core/letfn [(dest-args [c] (map (core/fn [n] `(unchecked-get (js-arguments) ~n)) @@ -3063,12 +3070,13 @@ (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) sig (remove '#{&} arglist) c-1 (core/dec (count sig)) + macro? (:macro meta) meta (assoc meta :top-fn {:variadic? true - :max-fixed-arity c-1 - :method-params [sig] - :arglists (core/list arglist) + :max-fixed-arity (core/cond-> c-1 macro? (core/- 2)) + :method-params (core/cond-> [sig] macro? elide-implicit-macro-args) + :arglists (core/cond-> (core/list arglist) macro? elide-implicit-macro-args) :arglists-meta (doall (map meta [arglist]))})] `(do (def ~(with-meta name meta) @@ -3117,12 +3125,13 @@ (concat (map count sigs) [(core/- (count (first (filter varsig? arglists))) 2)])) + macro? (:macro meta) meta (assoc meta :top-fn {:variadic? variadic - :max-fixed-arity maxfa - :method-params sigs - :arglists arglists + :max-fixed-arity (core/cond-> maxfa macro? (core/- 2)) + :method-params (core/cond-> sigs macro? elide-implicit-macro-args) + :arglists (core/cond-> arglists macro? elide-implicit-macro-args) :arglists-meta (doall (map meta arglists))}) args-sym (gensym "args") param-counts (map count arglists)] diff --git a/src/test/cljs/cljs/macro_test.cljs b/src/test/cljs/cljs/macro_test.cljs index f8f66088f..5e6c41f7a 100644 --- a/src/test/cljs/cljs/macro_test.cljs +++ b/src/test/cljs/cljs/macro_test.cljs @@ -9,7 +9,8 @@ (ns cljs.macro-test (:refer-clojure :exclude [==]) (:require [cljs.test :refer-macros [deftest is]]) - (:use-macros [cljs.macro-test.macros :only [==]])) + (:use-macros [cljs.macro-test.macros :only [==]]) + (:require-macros [cljs.macro-test.cljs2852])) (deftest test-macros (is (= (== 1 1) 2))) @@ -22,3 +23,8 @@ (deftest test-cljs-2283 (is (= ":a" (first (js-keys (js-obj :a 1)))))) + +(deftest test-cljs-2852 + (is (= '([x])) (cljs.macro-test.cljs2852/beta)) + (is (= '([x] [x y])) (cljs.macro-test.cljs2852/delta)) + (is (= '([x] [x & xs])) (cljs.macro-test.cljs2852/zeta))) diff --git a/src/test/cljs/cljs/macro_test/cljs2852.clj b/src/test/cljs/cljs/macro_test/cljs2852.clj new file mode 100644 index 000000000..164c09f48 --- /dev/null +++ b/src/test/cljs/cljs/macro_test/cljs2852.clj @@ -0,0 +1,21 @@ +(ns cljs.macro-test.cljs2852) + +(defmacro alpha + ([x])) + +(defmacro beta [] + `'~(:arglists (meta #'alpha))) + +(defmacro gamma + ([x]) + ([x y])) + +(defmacro delta [] + `'~(:arglists (meta #'gamma))) + +(defmacro epsilon + ([x]) + ([x & xs])) + +(defmacro zeta [] + `'~(:arglists (meta #'epsilon))) From dab61a6f2d66a6353003724745dd55b0ef93d216 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 10 Aug 2018 08:48:19 -0400 Subject: [PATCH 3265/4033] CLJS-2859: Graal.JS: Enable high-res timers by default, allow user-configuration --- src/main/clojure/cljs/repl/graaljs.clj | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/repl/graaljs.clj b/src/main/clojure/cljs/repl/graaljs.clj index 946a7cc5c..5b0e4858e 100644 --- a/src/main/clojure/cljs/repl/graaljs.clj +++ b/src/main/clojure/cljs/repl/graaljs.clj @@ -21,14 +21,24 @@ [cljs.stacktrace :as st]) (:import [javax.script ScriptEngine ScriptException])) -(defn create-engine [] +(defn- js-opt-key? [k] + (and (string? k) + (string/starts-with? k "js."))) + +(defn- form-js-opts [opts] + (for [[k v] opts + :when (js-opt-key? k)] + `(.option ~k ~v))) + +(defn create-engine [opts] ;; In order to support AOT compilation by JVMs that don't have ;; GraalVM available, we load and execute engine creation code ;; here at runtime. (import '(com.oracle.truffle.js.scriptengine GraalJSScriptEngine)) (import '(org.graalvm.polyglot Context)) - (let [engine (eval '(GraalJSScriptEngine/create nil + (let [engine (eval `(GraalJSScriptEngine/create nil (-> (Context/newBuilder (make-array String 0)) + ~@(form-js-opts opts) (.allowHostAccess true) (.allowCreateThread true) (.allowNativeAccess true)))) @@ -172,8 +182,12 @@ (fn [st] (string/join "\n" (drop 1 (string/split st #"\n"))))))) +(def ^:private default-js-opts + {"js.precise-time" "true"}) + (defn repl-env* [{:keys [debug] :as opts}] - (let [engine (create-engine)] + (let [opts (merge default-js-opts opts) + engine (create-engine opts)] (merge (GraalJSEnv. engine debug) opts))) From 81a1ea127974d43a6166fbdae33bcaa296fe9156 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 14 Aug 2018 20:27:24 -0400 Subject: [PATCH 3266/4033] CLJS-2861: Self-host: :checked-arrays not working --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/cljs/cljs/array_access_test.cljs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 37b40e747..4ac973af8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2267,7 +2267,7 @@ (throw (error env "set! target must be a field or a symbol naming a var"))) (cond (and (not (:def-emits-var env)) ;; non-REPL context - (some? ('#{*unchecked-if* *unchecked-array* *warn-on-infer*} target))) + (some? ('#{*unchecked-if* *unchecked-arrays* *warn-on-infer*} target))) {:env env :op :no-op} :else diff --git a/src/test/cljs/cljs/array_access_test.cljs b/src/test/cljs/cljs/array_access_test.cljs index 44a01f334..a98898fa3 100644 --- a/src/test/cljs/cljs/array_access_test.cljs +++ b/src/test/cljs/cljs/array_access_test.cljs @@ -11,6 +11,12 @@ (:require [cljs.test :as test :refer [deftest is]] [cljs.array-access.alpha :as alpha])) +(deftest cljs-2861-test + ;; With cljs-2718, a typo led to (set! *unchecked-arrays* true) as + ;; not being treated as a no-op generating intrisic, which we can + ;; detect here when this test is run in JVM ClojureScript. + (is (false? *unchecked-arrays*))) + (deftest unchecked-arrays-file-scope-test (is (not (alpha/unchecked-arrays?)))) From d38e001b617c849be53fcc588b5a454e3acfa51d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Aug 2018 18:11:41 -0400 Subject: [PATCH 3267/4033] two microoptimizations --- src/main/clojure/cljs/analyzer.cljc | 4 ++-- src/main/clojure/cljs/compiler.cljc | 3 ++- src/test/clojure/cljs/profile.clj | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 src/test/clojure/cljs/profile.clj diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 4ac973af8..31df7665d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1600,7 +1600,7 @@ (every? constant-value? (ast-children ast))))) (defn const-expr->constant-value [{:keys [op] :as e}] - (case op + (case op :quote (const-expr->constant-value (:expr e)) :const (:val e) :map (zipmap (map const-expr->constant-value (:keys e)) @@ -1926,7 +1926,7 @@ {:protocol-impl proto-impl :protocol-inline proto-inline}) methods (map #(disallowing-ns* (analyze-fn-method menv locals % type (nil? name))) meths) - mfa (apply max (map :fixed-arity methods)) + mfa (transduce (map :fixed-arity) max 0 methods) variadic (boolean (some :variadic? methods)) locals (if named-fn? (update-in locals [name] assoc diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 4efaab446..05476ff68 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -205,7 +205,8 @@ #?(:clj (map? x) :cljs (ana/cljs-map? x)) (emit x) #?(:clj (seq? x) :cljs (ana/cljs-seq? x)) (apply emits x) #?(:clj (fn? x) :cljs ^boolean (goog/isFunction x)) (x) - :else (let [s (print-str x)] + :else (let [s (cond-> x + (not (string? x)) print-str)] (when-not (nil? *source-map-data*) (swap! *source-map-data* update-in [:gen-col] #(+ % (count s)))) diff --git a/src/test/clojure/cljs/profile.clj b/src/test/clojure/cljs/profile.clj new file mode 100644 index 000000000..9304c2a24 --- /dev/null +++ b/src/test/clojure/cljs/profile.clj @@ -0,0 +1,21 @@ +(ns cljs.profile + (:require [clojure.java.io :as io] + [cljs.env :as env] + [cljs.analyzer :as ana] + [cljs.compiler :as comp])) + +(comment + + ;; ~900ms + (dotimes [_ 20] + (time (ana/analyze-file (io/resource "cljs/core.cljs")))) + + ;; ~2700ms + ;; after change ~2500 + (dotimes [_ 20] + (time + (env/with-compiler-env (env/default-compiler-env) + (comp/compile-file (.getPath (io/resource "cljs/core.cljs"))) + (.delete (io/file "src/main/cljs/cljs/core.js"))))) + + ) \ No newline at end of file From 5ba2540f2c47361634bdcd6000c1298b5f10f930 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Aug 2018 18:38:25 -0400 Subject: [PATCH 3268/4033] add build.edn --- build.edn | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 build.edn diff --git a/build.edn b/build.edn new file mode 100644 index 000000000..84a99807c --- /dev/null +++ b/build.edn @@ -0,0 +1,24 @@ +{:optimizations :advanced + :main test-runner + :output-wrapper true + :verbose true + :compiler-stats true + :parallel-build true + :output-dir "builds/out-adv" + :npm-deps {:lodash "4.17.4"} + :closure-warnings {:non-standard-jsdoc :off :global-this :off} + :install-deps true + :language-in :es6 + :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}]} \ No newline at end of file From 0dfa5ab5e91fb8df8df1a3b9d9a36ed295780d69 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Aug 2018 18:41:39 -0400 Subject: [PATCH 3269/4033] add build.edn --- build.edn | 1 + 1 file changed, 1 insertion(+) diff --git a/build.edn b/build.edn index 84a99807c..c54f2ec13 100644 --- a/build.edn +++ b/build.edn @@ -1,4 +1,5 @@ {:optimizations :advanced + :output-to "builds/out-adv/core-advanced-test.js" :main test-runner :output-wrapper true :verbose true From f8b4125cbef671143b241881afdfc0195cf36480 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 25 Aug 2018 09:44:18 -0400 Subject: [PATCH 3270/4033] another micro-opt --- src/main/clojure/cljs/compiler.cljc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 05476ff68..a10584448 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -30,7 +30,7 @@ [cljs.analyzer :as ana] [cljs.source-map :as sm])) #?(:clj (:import java.lang.StringBuilder - java.io.File + [java.io File Writer] [cljs.tagged_literals JSValue]) :cljs (:import [goog.string StringBuffer]))) @@ -205,12 +205,11 @@ #?(:clj (map? x) :cljs (ana/cljs-map? x)) (emit x) #?(:clj (seq? x) :cljs (ana/cljs-seq? x)) (apply emits x) #?(:clj (fn? x) :cljs ^boolean (goog/isFunction x)) (x) - :else (let [s (cond-> x - (not (string? x)) print-str)] + :else (let [^String s (cond-> x (not (string? x)) print-str)] (when-not (nil? *source-map-data*) (swap! *source-map-data* update-in [:gen-col] #(+ % (count s)))) - (print s)))) + (.write ^Writer *out* s)))) nil) (defn emitln [& xs] From de3a60384933097e7eedff185a12dc6d1b10d00e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 25 Aug 2018 16:22:15 -0400 Subject: [PATCH 3271/4033] CLJS-2872: Need reader conditional with emits micro-opt --- src/main/clojure/cljs/compiler.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a10584448..c0af5ea9f 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -209,7 +209,8 @@ (when-not (nil? *source-map-data*) (swap! *source-map-data* update-in [:gen-col] #(+ % (count s)))) - (.write ^Writer *out* s)))) + #?(:clj (.write ^Writer *out* s) + :cljs (print s))))) nil) (defn emitln [& xs] From d2e4424cec693b59af67051aebefca971a477567 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 30 Aug 2018 21:58:12 -0400 Subject: [PATCH 3272/4033] CLJS-2877: Use SourceFile/fromPath instead of SourceFile/fromFile --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1e81a99a7..ee6b1740e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -114,7 +114,7 @@ (SourceFile/fromCode name source)) (defmethod js-source-file File [_ ^File source] - (SourceFile/fromFile source)) + (SourceFile/fromPath (.toPath source) StandardCharsets/UTF_8)) (defmethod js-source-file BufferedInputStream [^String name ^BufferedInputStream source] (SourceFile/fromInputStream name source)) From 826b3790e91dff84f502e863d0c4f8cf15cc03a0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Aug 2018 15:22:03 -0400 Subject: [PATCH 3273/4033] another micro-opt - just call toString instead of print-str --- src/main/clojure/cljs/compiler.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index c0af5ea9f..41c50178b 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -199,13 +199,13 @@ (emit* ast))) (defn emits [& xs] - (doseq [x xs] + (doseq [^Object x xs] (cond (nil? x) nil #?(:clj (map? x) :cljs (ana/cljs-map? x)) (emit x) #?(:clj (seq? x) :cljs (ana/cljs-seq? x)) (apply emits x) #?(:clj (fn? x) :cljs ^boolean (goog/isFunction x)) (x) - :else (let [^String s (cond-> x (not (string? x)) print-str)] + :else (let [^String s (cond-> x (not (string? x)) .toString)] (when-not (nil? *source-map-data*) (swap! *source-map-data* update-in [:gen-col] #(+ % (count s)))) From b2a15b86c46aaadb9b839015862629683db3f38e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Aug 2018 15:37:45 -0400 Subject: [PATCH 3274/4033] another micro-opts in intern-macros --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 31df7665d..af15bdb6c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -626,7 +626,7 @@ analysis environment." ([ns] (intern-macros ns false)) ([ns reload] - (when (or (nil? (get-in @env/*compiler* [::namespaces ns :macros])) + (when (or (nil? (gets @env/*compiler* ::namespaces ns :macros)) reload) (swap! env/*compiler* assoc-in [::namespaces ns :macros] (->> #?(:clj (ns-interns ns) :cljs (ns-interns* ns)) From c3732db435b37b5ebd5f87af3860007b39db697b Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Aug 2018 16:49:00 -0400 Subject: [PATCH 3275/4033] another micro-opt + remove gratuitous binding --- src/main/clojure/cljs/analyzer.cljc | 22 +- src/test/clojure/cljs/analyzer_tests.clj | 246 ++++++++++++----------- src/test/clojure/cljs/compiler_tests.clj | 36 ++-- src/test/clojure/cljs/profile.clj | 3 +- 4 files changed, 163 insertions(+), 144 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index af15bdb6c..cf701cad7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1967,9 +1967,13 @@ :children children} (when (some? name-var) {:local name-var}))] - (let [variadic-methods (filter :variadic? methods) - variadic-params (count (:params (first variadic-methods))) - param-counts (map (comp count :params) methods)] + (let [variadic-methods (into [] + (comp (filter :variadic?) (take 1)) + methods) + variadic-params (if (pos? (count variadic-methods)) + (count (:params (nth variadic-methods 0))) + 0) + param-counts (into [] (map (comp count :params)) methods)] (when (< 1 (count variadic-methods)) (warning :multiple-variadic-overloads env {:name name-var})) (when (not (or (zero? variadic-params) (== variadic-params (+ 1 mfa)))) @@ -3581,7 +3585,7 @@ "Given a env, an analysis environment, and form, a ClojureScript form, macroexpand the form once." [env form] - (ensure (wrapping-errors env (macroexpand-1* env form)))) + (wrapping-errors env (macroexpand-1* env form))) (declare analyze-list) @@ -3865,12 +3869,10 @@ (when env/*compiler* (:options @env/*compiler*)))) ([env form name opts] - (ensure - (wrapping-errors env - (binding [reader/*alias-map* (or reader/*alias-map* {})] - (if (analyzed? form) - (no-warn (analyze* env form name opts)) - (analyze* env form name opts))))))) + (wrapping-errors env + (if (analyzed? form) + (no-warn (analyze* env form name opts)) + (analyze* env form name opts))))) (defn add-consts "Given a compiler state and a map from fully qualified symbols to constant diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 6bb5f8bba..974762b7e 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -22,6 +22,14 @@ [cljs.test-util :refer [unsplit-lines]]) (:use clojure.test)) +(defn analyze + ([env form] + (env/ensure (a/analyze env form))) + ([env form name] + (env/ensure (a/analyze env form name))) + ([env form name opts] + (env/ensure (a/analyze env form name opts)))) + (defn collecting-warning-handler [state] (fn [warning-type env extra] (when (warning-type a/*cljs-warnings*) @@ -44,7 +52,7 @@ (when (warning-type a/*cljs-warnings*) (swap! counter inc)))] (a/with-warning-handlers [tracker] - (a/analyze (a/empty-env) form)) + (analyze (a/empty-env) form)) @counter)) (deftest no-warn @@ -61,115 +69,115 @@ (deftest spec-validation (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require {:foo :bar}))) + (analyze ns-env '(ns foo.bar (:require {:foo :bar}))) (catch Exception e (.getMessage e))) "Only [lib.ns & options] and lib.ns specs supported in :require / :require-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require [:foo :bar]))) + (analyze ns-env '(ns foo.bar (:require [:foo :bar]))) (catch Exception e (.getMessage e))) "Library name must be specified as a symbol in :require / :require-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :plop]))) + (analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :plop]))) (catch Exception e (.getMessage e))) "Only :as alias, :refer (names) and :rename {from to} options supported in :require")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :plop true]))) + (analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :plop true]))) (catch Exception e (.getMessage e))) "Only :as, :refer and :rename options supported in :require / :require-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :as boz :refer []]))) + (analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :as boz :refer []]))) (catch Exception e (.getMessage e))) "Each of :as and :refer options may only be specified once in :require / :require-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:refer-clojure :refer []))) + (analyze ns-env '(ns foo.bar (:refer-clojure :refer []))) (catch Exception e (.getMessage e))) "Only [:refer-clojure :exclude (names)] and optionally `:rename {from to}` specs supported")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:refer-clojure :rename [1 2]))) + (analyze ns-env '(ns foo.bar (:refer-clojure :rename [1 2]))) (catch Exception e (.getMessage e))) "Only [:refer-clojure :exclude (names)] and optionally `:rename {from to}` specs supported")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:use [baz.woz :exclude []]))) + (analyze ns-env '(ns foo.bar (:use [baz.woz :exclude []]))) (catch Exception e (.getMessage e))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:use [baz.woz]))) + (analyze ns-env '(ns foo.bar (:use [baz.woz]))) (catch Exception e (.getMessage e))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:use [baz.woz :only]))) + (analyze ns-env '(ns foo.bar (:use [baz.woz :only]))) (catch Exception e (.getMessage e))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:use [baz.woz :only [1 2 3]]))) + (analyze ns-env '(ns foo.bar (:use [baz.woz :only [1 2 3]]))) (catch Exception e (.getMessage e))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:use [baz.woz :rename [1 2]]))) + (analyze ns-env '(ns foo.bar (:use [baz.woz :rename [1 2]]))) (catch Exception e (.getMessage e))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:use [foo.bar :rename {baz qux}]))) + (analyze ns-env '(ns foo.bar (:use [foo.bar :rename {baz qux}]))) (catch Exception e (.getMessage e))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:use [baz.woz :only [foo] :only [bar]]))) + (analyze ns-env '(ns foo.bar (:use [baz.woz :only [foo] :only [bar]]))) (catch Exception e (.getMessage e))) "Each of :only and :rename options may only be specified once in :use / :use-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as []]))) + (analyze ns-env '(ns foo.bar (:require [baz.woz :as []]))) (catch Exception e (.getMessage e))) ":as must be followed by a symbol in :require / :require-macros")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require [baz.woz :as woz] [noz.goz :as woz]))) + (analyze ns-env '(ns foo.bar (:require [baz.woz :as woz] [noz.goz :as woz]))) (catch Exception e (.getMessage e))) ":as alias must be unique")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require [foo.bar :rename {baz qux}]))) + (analyze ns-env '(ns foo.bar (:require [foo.bar :rename {baz qux}]))) (catch Exception e (.getMessage e))) "Renamed symbol baz not referred")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:unless []))) + (analyze ns-env '(ns foo.bar (:unless []))) (catch Exception e (.getMessage e))) "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported. Got (:unless []) instead.")) (is (.startsWith (try - (a/analyze ns-env '(ns foo.bar (:require baz.woz) (:require noz.goz))) + (analyze ns-env '(ns foo.bar (:require baz.woz) (:require noz.goz))) (catch Exception e (.getMessage e))) "Only one "))) @@ -187,60 +195,60 @@ (deftest basic-inference (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '1))) + (:tag (analyze test-env '1))) 'number)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '"foo"))) + (:tag (analyze test-env '"foo"))) 'string)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '\a))) + (:tag (analyze test-env '\a))) 'string)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(make-array 10)))) + (:tag (analyze test-env '(make-array 10)))) 'array)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(js-obj)))) + (:tag (analyze test-env '(js-obj)))) 'object)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '[]))) + (:tag (analyze test-env '[]))) 'cljs.core/IVector)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '{}))) + (:tag (analyze test-env '{}))) 'cljs.core/IMap)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '#{}))) + (:tag (analyze test-env '#{}))) 'cljs.core/ISet)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env ()))) + (:tag (analyze test-env ()))) 'cljs.core/IList)) (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(fn [x] x)))) + (:tag (analyze test-env '(fn [x] x)))) 'function))) (deftest if-inference (is (= (a/no-warn (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(if x "foo" 1))))) + (:tag (analyze test-env '(if x "foo" 1))))) '#{number string}))) (deftest method-inference (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(.foo js/bar)))) + (:tag (analyze test-env '(.foo js/bar)))) 'js))) (deftest fn-inference ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env + ; (:tag (analyze test-env ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] ; (x :one))))) ; 'number)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env + ; (:tag (analyze test-env ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] ; (x :one :two))))) ; 'string)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env + ; (:tag (analyze test-env ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] ; (x :one :two :three))))) ; 'cljs.core/IList)) @@ -248,80 +256,80 @@ (deftest lib-inference (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(+ 1 2)))) + (:tag (analyze test-env '(+ 1 2)))) 'number)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(alength (array))))) + ; (:tag (analyze test-env '(alength (array))))) ; 'number)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(aclone (array))))) + ; (:tag (analyze test-env '(aclone (array))))) ; 'array)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(-count [1 2 3])))) + ; (:tag (analyze test-env '(-count [1 2 3])))) ; 'number)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(count [1 2 3])))) + ; (:tag (analyze test-env '(count [1 2 3])))) ; 'number)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(into-array [1 2 3])))) + ; (:tag (analyze test-env '(into-array [1 2 3])))) ; 'array)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(js-obj)))) + ; (:tag (analyze test-env '(js-obj)))) ; 'object)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(-conj [] 1)))) + ; (:tag (analyze test-env '(-conj [] 1)))) ; 'clj)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(conj [] 1)))) + ; (:tag (analyze test-env '(conj [] 1)))) ; 'clj)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(assoc nil :foo :bar)))) + ; (:tag (analyze test-env '(assoc nil :foo :bar)))) ; 'clj)) ;(is (= (e/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(dissoc {:foo :bar} :foo)))) + ; (:tag (analyze test-env '(dissoc {:foo :bar} :foo)))) ; '#{clj clj-nil})) ) (deftest test-always-true-if (is (= (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(if 1 2 "foo")))) + (:tag (analyze test-env '(if 1 2 "foo")))) 'number))) ;; will only work if the previous test works (deftest test-count ;(is (= (cljs.env/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(count [])))) + ; (:tag (analyze test-env '(count [])))) ; 'number)) ) (deftest test-numeric ;(is (= (a/no-warn ; (cljs.env/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(dec x))))) + ; (:tag (analyze test-env '(dec x))))) ; 'number)) ;(is (= (a/no-warn ; (cljs.env/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(int x))))) + ; (:tag (analyze test-env '(int x))))) ; 'number)) ;(is (= (a/no-warn ; (cljs.env/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(unchecked-int x))))) + ; (:tag (analyze test-env '(unchecked-int x))))) ; 'number)) ;(is (= (a/no-warn ; (cljs.env/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(mod x y))))) + ; (:tag (analyze test-env '(mod x y))))) ; 'number)) ;(is (= (a/no-warn ; (cljs.env/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(quot x y))))) + ; (:tag (analyze test-env '(quot x y))))) ; 'number)) ;(is (= (a/no-warn ; (cljs.env/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(rem x y))))) + ; (:tag (analyze test-env '(rem x y))))) ; 'number)) ;(is (= (a/no-warn ; (cljs.env/with-compiler-env test-cenv - ; (:tag (a/analyze test-env '(bit-count n))))) + ; (:tag (analyze test-env '(bit-count n))))) ; 'number)) ) @@ -331,7 +339,7 @@ (deftest test-defn-error (is (.startsWith (try - (a/analyze test-env '(defn foo 123)) + (analyze test-env '(defn foo 123)) (catch Exception e (.getMessage e))) "Parameter declaration \"123\" should be a vector"))) @@ -370,32 +378,32 @@ (deftest test-namespace-metadata (binding [a/*cljs-ns* a/*cljs-ns*] - (is (= (do (a/analyze ns-env '(ns weeble.ns {:foo bar})) + (is (= (do (analyze ns-env '(ns weeble.ns {:foo bar})) (meta a/*cljs-ns*)) {:foo 'bar})) - (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble.ns)) + (is (= (do (analyze ns-env '(ns ^{:foo bar} weeble.ns)) (meta a/*cljs-ns*)) {:foo 'bar})) - (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble.ns {:baz quux})) + (is (= (do (analyze ns-env '(ns ^{:foo bar} weeble.ns {:baz quux})) (meta a/*cljs-ns*)) {:foo 'bar :baz 'quux})) - (is (= (do (a/analyze ns-env '(ns ^{:foo bar} weeble.ns {:foo baz})) + (is (= (do (analyze ns-env '(ns ^{:foo bar} weeble.ns {:foo baz})) (meta a/*cljs-ns*)) {:foo 'baz})) - (is (= (meta (:name (a/analyze ns-env '(ns weeble.ns {:foo bar})))) + (is (= (meta (:name (analyze ns-env '(ns weeble.ns {:foo bar})))) {:foo 'bar})) - (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble.ns)))) + (is (= (meta (:name (analyze ns-env '(ns ^{:foo bar} weeble.ns)))) {:foo 'bar})) - (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble.ns {:baz quux})))) + (is (= (meta (:name (analyze ns-env '(ns ^{:foo bar} weeble.ns {:baz quux})))) {:foo 'bar :baz 'quux})) - (is (= (meta (:name (a/analyze ns-env '(ns ^{:foo bar} weeble.ns {:foo baz})))) + (is (= (meta (:name (analyze ns-env '(ns ^{:foo bar} weeble.ns {:foo baz})))) {:foo 'baz})))) (deftest test-cljs-1105 @@ -418,13 +426,13 @@ (deftest test-constants (is (.startsWith (try - (a/analyze test-env '(do (def ^:const foo 123) (def foo 246))) + (analyze test-env '(do (def ^:const foo 123) (def foo 246))) (catch Exception e (.getMessage e))) "Can't redefine a constant")) (is (.startsWith (try - (a/analyze test-env '(do (def ^:const foo 123) (set! foo 246))) + (analyze test-env '(do (def ^:const foo 123) (set! foo 246))) (catch Exception e (.getMessage e))) "Can't set! a constant"))) @@ -432,7 +440,7 @@ (deftest test-cljs-1508-rename (binding [a/*cljs-ns* a/*cljs-ns*] (let [parsed-ns (e/with-compiler-env test-cenv - (a/analyze test-env + (analyze test-env '(ns foo.core (:require [clojure.set :as set :refer [intersection] :rename {intersection foo}]))))] (is (nil? (-> parsed-ns :uses (get 'foo)))) @@ -441,7 +449,7 @@ (is (= (-> parsed-ns :renames (get 'foo)) 'clojure.set/intersection))) (is (e/with-compiler-env test-cenv - (a/analyze test-env + (analyze test-env '(ns foo.core (:use [clojure.set :only [intersection] :rename {intersection foo}]))))) (is (= (e/with-compiler-env (atom {::a/namespaces @@ -456,7 +464,7 @@ (is (= (-> rwhen :name) 'cljs.core/when))) (let [parsed-ns (e/with-compiler-env test-cenv - (a/analyze test-env + (analyze test-env '(ns foo.core (:refer-clojure :rename {when always map core-map}))))] @@ -464,7 +472,7 @@ (is (= (-> parsed-ns :rename-macros) '{always cljs.core/when})) (is (= (-> parsed-ns :renames) '{core-map cljs.core/map}))) (is (thrown? Exception (e/with-compiler-env test-cenv - (a/analyze test-env + (analyze test-env '(ns foo.core (:require [clojure.set :rename {intersection foo}])))))))) @@ -472,8 +480,8 @@ (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] (binding [a/*cljs-ns* a/*cljs-ns*] (is (thrown-with-msg? Exception #"Can't def ns-qualified name in namespace foo.core" - (a/analyze test-env '(def foo.core/foo 43)))) - (is (a/analyze test-env '(def cljs.user/foo 43)))))) + (analyze test-env '(def foo.core/foo 43)))) + (is (analyze test-env '(def cljs.user/foo 43)))))) (deftest test-cljs-1702 (let [ws (atom [])] @@ -504,7 +512,7 @@ (deftest test-cljs-1785-js-shadowed-by-local (let [ws (atom [])] (a/with-warning-handlers [(collecting-warning-handler ws)] - (a/analyze ns-env + (analyze ns-env '(fn [foo] (let [x js/foo] (println x))))) @@ -514,7 +522,7 @@ (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(defn myfun ([x] x) ([x] x)))) @@ -549,27 +557,27 @@ (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* nil] (let [test-env (a/empty-env)] - (is (= (-> (a/analyze test-env '(require '[clojure.set :as set])) :requires vals set) + (is (= (-> (analyze test-env '(require '[clojure.set :as set])) :requires vals set) '#{clojure.set}))) (let [test-env (a/empty-env)] - (is (= (-> (a/analyze test-env '(require '[clojure.set :as set :refer [union intersection]])) :uses keys set) + (is (= (-> (analyze test-env '(require '[clojure.set :as set :refer [union intersection]])) :uses keys set) '#{union intersection}))) (let [test-env (a/empty-env)] - (is (= (-> (a/analyze test-env '(require '[clojure.set :as set] + (is (= (-> (analyze test-env '(require '[clojure.set :as set] '[clojure.string :as str])) :requires vals set) '#{clojure.set clojure.string}))) (let [test-env (a/empty-env)] - (is (= (-> (a/analyze test-env '(require-macros '[cljs.test :as test])) :require-macros vals set) + (is (= (-> (analyze test-env '(require-macros '[cljs.test :as test])) :require-macros vals set) '#{cljs.test}))) (let [test-env (a/empty-env) - parsed (a/analyze test-env '(require-macros '[cljs.test :as test :refer [deftest is]]))] + parsed (analyze test-env '(require-macros '[cljs.test :as test :refer [deftest is]]))] (is (= (-> parsed :require-macros vals set) '#{cljs.test})) (is (= (-> parsed :use-macros keys set) '#{is deftest}))) (let [test-env (a/empty-env) - parsed (a/analyze test-env '(require '[cljs.test :as test :refer-macros [deftest is]]))] + parsed (analyze test-env '(require '[cljs.test :as test :refer-macros [deftest is]]))] (is (= (-> parsed :requires vals set) '#{cljs.test})) (is (= (-> parsed :require-macros vals set) @@ -577,40 +585,40 @@ (is (= (-> parsed :use-macros keys set) '#{is deftest}))) (let [test-env (a/empty-env) - parsed (a/analyze test-env '(use '[clojure.set :only [intersection]]))] + parsed (analyze test-env '(use '[clojure.set :only [intersection]]))] (is (= (-> parsed :uses keys set) '#{intersection})) (is (= (-> parsed :requires) '{clojure.set clojure.set}))) (let [test-env (a/empty-env) - parsed (a/analyze test-env '(use-macros '[cljs.test :only [deftest is]]))] + parsed (analyze test-env '(use-macros '[cljs.test :only [deftest is]]))] (is (= (-> parsed :use-macros keys set) '#{deftest is})) (is (= (-> parsed :require-macros) '{cljs.test cljs.test})) (is (nil? (-> parsed :requires)))) (let [test-env (a/empty-env) - parsed (a/analyze test-env '(import '[goog.math Long Integer]))] + parsed (analyze test-env '(import '[goog.math Long Integer]))] (is (= (-> parsed :imports) (-> parsed :requires) '{Long goog.math.Long Integer goog.math.Integer}))) (let [test-env (a/empty-env) - parsed (a/analyze test-env '(refer-clojure :exclude '[map mapv]))] + parsed (analyze test-env '(refer-clojure :exclude '[map mapv]))] (is (= (-> parsed :excludes) '#{map mapv}))) (let [test-env (a/empty-env) - parsed (a/analyze test-env '(refer-clojure :exclude '[map mapv] :rename '{mapv core-mapv}))] + parsed (analyze test-env '(refer-clojure :exclude '[map mapv] :rename '{mapv core-mapv}))] (is (= (-> parsed :excludes) '#{map mapv}))))) (testing "arguments to require should be quoted" (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* nil] (is (thrown-with-msg? Exception #"Arguments to require must be quoted" - (a/analyze test-env + (analyze test-env '(require [clojure.set :as set])))) (is (thrown-with-msg? Exception #"Arguments to require must be quoted" - (a/analyze test-env + (analyze test-env '(require clojure.set)))))) (testing "`:ns` and `:ns*` should throw if not `:top-level`" (binding [a/*cljs-ns* a/*cljs-ns* @@ -618,26 +626,26 @@ (are [analyzed] (thrown-with-msg? Exception #"Namespace declarations must appear at the top-level." analyzed) - (a/analyze test-env + (analyze test-env '(def foo (ns foo.core (:require [clojure.set :as set])))) - (a/analyze test-env + (analyze test-env '(fn [] (ns foo.core (:require [clojure.set :as set])))) - (a/analyze test-env + (analyze test-env '(map #(ns foo.core (:require [clojure.set :as set])) [1 2]))) (are [analyzed] (thrown-with-msg? Exception #"Calls to `require` must appear at the top-level." analyzed) - (a/analyze test-env + (analyze test-env '(def foo (require '[clojure.set :as set]))) - (a/analyze test-env + (analyze test-env '(fn [] (require '[clojure.set :as set]))) - (a/analyze test-env + (analyze test-env '(map #(require '[clojure.set :as set]) [1 2])))))) (deftest test-gen-user-ns @@ -657,11 +665,11 @@ (deftest test-cljs-1536 (let [parsed (e/with-compiler-env test-cenv - (a/analyze (assoc test-env :def-emits-var true) + (analyze (assoc test-env :def-emits-var true) '(def x 1)))] (is (some? (:var-ast parsed)))) (let [parsed (e/with-compiler-env test-cenv - (a/analyze (assoc test-env :def-emits-var true) + (analyze (assoc test-env :def-emits-var true) '(let [y 1] (def y 2))))] (is (some? (-> parsed :body :ret :var-ast))))) @@ -669,7 +677,7 @@ (defn ana' [form] (e/with-compiler-env analyze-ops-cenv - (a/analyze test-env form))) + (analyze test-env form))) (defmacro ana [form] `(ana' '~form)) @@ -1302,32 +1310,32 @@ (is (= 'js/Foo (-> (binding [a/*cljs-ns* a/*cljs-ns*] (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) 'js/baz))) + (analyze (a/empty-env) 'js/baz))) :info :ret-tag))) (is (= 'js/Foo (-> (binding [a/*cljs-ns* a/*cljs-ns*] (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) '(js/baz)))) + (analyze (a/empty-env) '(js/baz)))) :tag))) (is (= 'js (-> (binding [a/*cljs-ns* a/*cljs-ns*] (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) '(js/woz)))) + (analyze (a/empty-env) '(js/woz)))) :tag))) (is (= 'js (-> (binding [a/*cljs-ns* a/*cljs-ns*] (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) '(def foo (js/woz))))) + (analyze (a/empty-env) '(def foo (js/woz))))) :tag))) (is (= 'js (-> (binding [a/*cljs-ns* a/*cljs-ns*] (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) '(def foo js/boz)))) + (analyze (a/empty-env) '(def foo js/boz)))) :tag))) (is (nil? (-> (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(let [z (.baz ^js/Foo.Bar x)] z))))) :tag meta :prefix)))) @@ -1336,7 +1344,7 @@ (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] - (a/analyze (ana/empty-env) + (analyze (ana/empty-env) '(do (declare ^{:arglists '([x y])} foo) (defn foo [x])))) (catch Exception _)) @@ -1374,7 +1382,7 @@ (let [ws (atom [])] (try (a/with-warning-handlers [(collecting-warning-handler ws)] - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(defn foo [] x))) (catch Exception _)) (is (= ["Use of undeclared Var cljs.user/x"] @ws)))) @@ -1385,7 +1393,7 @@ (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(aget (js-obj) "a")))) (catch Exception _)) (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string] instead (consider goog.object/get for object access)"] @ws))) @@ -1393,7 +1401,7 @@ (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(aget (js-obj) "foo" "bar")))) (catch Exception _)) (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string string] instead (consider goog.object/getValueByKeys for object access)"] @ws))) @@ -1401,7 +1409,7 @@ (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(aset (js-obj) "a" 2)))) (catch Exception _)) (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))) @@ -1409,7 +1417,7 @@ (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(let [^objects arr (into-array [1 2 3])] (aget arr 0))))) (catch Exception _)) @@ -1418,7 +1426,7 @@ (try (a/with-warning-handlers [(collecting-warning-handler ws)] (e/with-compiler-env test-cenv - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(and true (or (aget (js-obj "foo" 1) "foo") 2))))) (catch Exception _)) (is (= 1 (count @ws)))))) @@ -1428,14 +1436,14 @@ (binding [a/*cljs-ns* a/*cljs-ns* a/*analyze-deps* false] (is (thrown-with-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" - (a/analyze test-env '(do + (analyze test-env '(do (require '[clojure.string :as str]) (require '[clojure.set :as str]))))) (is (thrown-with-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" - (a/analyze test-env '(do + (analyze test-env '(do (require-macros '[clojure.string :as str]) (require-macros '[clojure.set :as str]))))) - (is (a/analyze test-env '(do + (is (analyze test-env '(do (require '[clojure.string :as str]) (require '[clojure.string :as str]) (require 'clojure.string))))))) @@ -1445,7 +1453,7 @@ (is (thrown-with-msg? Exception #"Argument to resolve must be a quoted symbol" (e/with-compiler-env test-cenv - (a/analyze test-env '(resolve foo.core))))))) + (analyze test-env '(resolve foo.core))))))) (deftest test-cljs-2387 (a/no-warn @@ -1455,7 +1463,7 @@ (deftest test-cljs-2475 (is (thrown-with-msg? Exception #"recur argument count mismatch, expected: 2 args, got: 1" - (a/analyze test-env '(loop [x 1 y 2] (recur 3)))))) + (analyze test-env '(loop [x 1 y 2] (recur 3)))))) (deftest test-cljs-2476 (doseq [invalid-try-recur-form '[(loop [] (try (recur))) @@ -1464,13 +1472,13 @@ (loop [] (try (finally (recur))))]] (is (thrown-with-msg? Exception #"Can't recur here" - (a/analyze test-env invalid-try-recur-form))))) + (analyze test-env invalid-try-recur-form))))) (comment (binding [a/*cljs-ns* a/*cljs-ns*] (a/no-warn (e/with-compiler-env externs-cenv - (a/analyze (a/empty-env) + (analyze (a/empty-env) '(let [React (js/require "react")] React))))) @@ -1479,7 +1487,7 @@ (a/no-warn (e/with-compiler-env externs-cenv (let [aenv (a/empty-env) - _ (a/analyze aenv '(ns foo.core)) + _ (analyze aenv '(ns foo.core)) aenv' (assoc-in aenv [:ns :name] 'foo.core) _ (a/analyze aenv' '(def x 1))] (dissoc (a/analyze-symbol (assoc-in aenv [:ns :name] 'foo.core) 'x) :env) @@ -1730,7 +1738,7 @@ (deftest test-locals-mapped-to-sym (testing "analyze should be robust to :locals mapping to symbols" - (is (= [:local 'a] (-> (a/analyze (assoc-in test-env [:locals 'a] 'foo) 'a) + (is (= [:local 'a] (-> (analyze (assoc-in test-env [:locals 'a] 'foo) 'a) ((juxt :op :name))))))) (deftest test-cljs-2814 @@ -1741,6 +1749,6 @@ (deftest test-cljs-2819 (let [ws (atom [])] (a/with-warning-handlers [(collecting-warning-handler ws)] - (a/analyze ns-env + (analyze ns-env '(def *foo* 1))) (is (string/starts-with? (first @ws) "*foo* not declared dynamic and thus")))) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 5a1d8c38a..801180836 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -18,6 +18,14 @@ [clojure.string :as str]) (:import [java.io File])) +(defn analyze + ([env form] + (env/ensure (ana/analyze env form))) + ([env form name] + (env/ensure (ana/analyze env form name))) + ([env form name opts] + (env/ensure (ana/analyze env form name opts)))) + (def aenv (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) (def cenv (env/default-compiler-env)) @@ -39,21 +47,21 @@ (deftest fn-scope-munge (is (= (comp/munge (get-in - (ana/analyze aenv + (analyze aenv '(defn foo [] (fn bar []))) [:init :name])) 'cljs$user$foo)) (is (= (comp/munge (get-in - (ana/analyze aenv + (analyze aenv '(defn foo [] (fn bar []))) [:init :methods 0 :body :ret :local])) 'cljs$user$foo_$_bar)) (is (= (comp/munge (get-in - (ana/analyze aenv + (analyze aenv '(fn [] (fn console []))) [:methods 0 :body :ret :local])) @@ -62,14 +70,14 @@ (deftest test-js-negative-infinity (is (= (with-out-str (comp/emit - (ana/analyze (assoc aenv :context :expr) 'js/-Infinity))) + (analyze (assoc aenv :context :expr) 'js/-Infinity))) "-Infinity"))) (deftest test-cljs-2352 (are [form result] (= (with-out-str (comp/emit - (ana/analyze (assoc aenv :context :expr) form))) + (analyze (assoc aenv :context :expr) form))) result) Double/NaN "NaN" Double/POSITIVE_INFINITY "Infinity" @@ -82,7 +90,7 @@ (ana/no-warn (env/with-compiler-env cenv (comp/munge - (:info (ana/analyze {:ns {:name 'cljs.core}} 'cljs.core/..)))))))) + (:info (analyze {:ns {:name 'cljs.core}} 'cljs.core/..)))))))) (deftest test-resolve-dotdot (is (= '{:name cljs.core/.. @@ -101,13 +109,13 @@ (comp/emit-comment "/* multiline comments */" nil)))) (check-docs (with-out-str (comp/emit - (ana/analyze aenv + (analyze aenv '(defn foo "foo is */ like this /*/" [] (+ 1 1)))))))) (comment (env/with-compiler-env cenv (comp/emit - (ana/analyze aenv + (analyze aenv '(defn foo ([a]) ([a b]))))) ) @@ -134,7 +142,7 @@ (let [warnings (-> (capture-warnings (with-out-str (comp/emit - (ana/analyze aenv + (analyze aenv '(let [{:keys [a] :or {b 2}} {:a 1}] [a b]))))))] (is (= (ffirst warnings) :undeclared-var)) (is (.startsWith (-> warnings first second) @@ -156,7 +164,7 @@ (env/with-compiler-env (atom cenv-with-foo) (with-out-str (comp/emit - (ana/analyze aenv-with-foo form)))))) + (analyze aenv-with-foo form)))))) '(cljs.user/foo nil) '(cljs.user/foo 0) @@ -217,7 +225,7 @@ (comp/emit (comp/with-core-cljs opts - (fn [] (ana/analyze aenv test-cljs-1925-code nil opts))))))))))) + (fn [] (analyze aenv test-cljs-1925-code nil opts))))))))))) (let [opts {:static-fns true} cenv (env/default-compiler-env opts)] (is (= [] (binding [ana/*unchecked-if* false @@ -228,7 +236,7 @@ (comp/emit (comp/with-core-cljs opts - (fn [] (ana/analyze aenv specify-test-code nil opts)))))))))))) + (fn [] (analyze aenv specify-test-code nil opts)))))))))))) (deftest test-optimized-invoke-emit @@ -279,7 +287,7 @@ (binding [ana/*cljs-static-fns* true] (env/with-compiler-env cenv (comp/emit - (ana/analyze aenv + (analyze aenv '(defn incme [] (let [incme (fn [a queue & args])] (println (incme 1 [1] 1 1)))))))) @@ -291,7 +299,7 @@ (binding [ana/*cljs-static-fns* true] (env/with-compiler-env cenv (comp/emit - (ana/analyze aenv + (analyze aenv '(defn foo [x] (if ^boolean (goog.array/isEmpty x) true diff --git a/src/test/clojure/cljs/profile.clj b/src/test/clojure/cljs/profile.clj index 9304c2a24..b168c02b1 100644 --- a/src/test/clojure/cljs/profile.clj +++ b/src/test/clojure/cljs/profile.clj @@ -8,7 +8,8 @@ ;; ~900ms (dotimes [_ 20] - (time (ana/analyze-file (io/resource "cljs/core.cljs")))) + (time + (ana/analyze-file (io/resource "cljs/core.cljs")))) ;; ~2700ms ;; after change ~2500 From 284872fb50cac0bd3a5d13c8e3aeaecd3481e03f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Aug 2018 19:21:43 -0400 Subject: [PATCH 3276/4033] two more micro-opts --- src/main/clojure/cljs/analyzer.cljc | 9 +++++++-- src/main/clojure/cljs/compiler.cljc | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index cf701cad7..00085db0b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3296,13 +3296,18 @@ "Mark a form as being analyzed. Assumes x satisfies IMeta. Useful to suppress warnings that will have been caught by a first compiler pass." [x] - (vary-meta x assoc ::analyzed true)) + (cond + (map? x) (assoc x ::analyzed true) + :else (vary-meta x assoc ::analyzed true))) (defn analyzed? "Returns boolean if the form has already been marked as analyzed." #?(:cljs {:tag boolean}) [x] - (boolean (::analyzed (meta x)))) + (boolean + (cond + (map? x) (::analyzed x) + :else (::analyzed (meta x))))) (defn- all-values? #?(:cljs {:tag boolean}) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 41c50178b..bbe3d064f 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -65,7 +65,7 @@ ns (subs ns 0 idx)))) -(defn find-ns-starts-with [needle] +(defn ^:dynamic find-ns-starts-with [needle] (reduce-kv (fn [xs ns _] (when (= needle (get-first-ns-segment ns)) @@ -1473,7 +1473,8 @@ (atom {:source-map (sorted-map) :gen-col 0 - :gen-line 0}))] + :gen-line 0})) + find-ns-starts-with (memoize find-ns-starts-with)] (emitln (compiled-by-string opts)) (with-open [rdr (io/reader src)] (let [env (ana/empty-env)] From 980d1fa9f14a4ec5caad1e2a8b734795094e0eba Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 31 Aug 2018 20:10:37 -0400 Subject: [PATCH 3277/4033] remove another gratuitous ensure, fix tests --- src/main/clojure/cljs/compiler.cljc | 39 ++++++++++++------------ src/test/clojure/cljs/compiler_tests.clj | 23 ++++++++------ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index bbe3d064f..a0d8d85a8 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -177,26 +177,25 @@ (defmulti emit* :op) (defn emit [ast] - (ensure - (when *source-map-data* - (let [{:keys [env]} ast] - (when (:line env) - (let [{:keys [line column]} env] - (swap! *source-map-data* - (fn [m] - (let [minfo (cond-> {:gcol (:gen-col m) - :gline (:gen-line m)} - (#{:var :local :js-var} (:op ast)) - (assoc :name (str (-> ast :info :name))))] - ; Dec the line/column numbers for 0-indexing. - ; tools.reader uses 1-indexed sources, chrome - ; expects 0-indexed source maps. - (update-in m [:source-map (dec line)] - (fnil (fn [line] - (update-in line [(if column (dec column) 0)] - (fnil (fn [column] (conj column minfo)) []))) - (sorted-map)))))))))) - (emit* ast))) + (when *source-map-data* + (let [{:keys [env]} ast] + (when (:line env) + (let [{:keys [line column]} env] + (swap! *source-map-data* + (fn [m] + (let [minfo (cond-> {:gcol (:gen-col m) + :gline (:gen-line m)} + (#{:var :local :js-var} (:op ast)) + (assoc :name (str (-> ast :info :name))))] + ; Dec the line/column numbers for 0-indexing. + ; tools.reader uses 1-indexed sources, chrome + ; expects 0-indexed source maps. + (update-in m [:source-map (dec line)] + (fnil (fn [line] + (update-in line [(if column (dec column) 0)] + (fnil (fn [column] (conj column minfo)) []))) + (sorted-map)))))))))) + (emit* ast)) (defn emits [& xs] (doseq [^Object x xs] diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 801180836..59857b9f1 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -26,6 +26,9 @@ ([env form name opts] (env/ensure (ana/analyze env form name opts)))) +(defn emit [ast] + (env/ensure (comp/emit ast))) + (def aenv (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) (def cenv (env/default-compiler-env)) @@ -69,14 +72,14 @@ (deftest test-js-negative-infinity (is (= (with-out-str - (comp/emit + (emit (analyze (assoc aenv :context :expr) 'js/-Infinity))) "-Infinity"))) (deftest test-cljs-2352 (are [form result] (= (with-out-str - (comp/emit + (emit (analyze (assoc aenv :context :expr) form))) result) Double/NaN "NaN" @@ -108,13 +111,13 @@ (env/ensure (comp/emit-comment "/* multiline comments */" nil)))) (check-docs (with-out-str - (comp/emit + (emit (analyze aenv '(defn foo "foo is */ like this /*/" [] (+ 1 1)))))))) (comment (env/with-compiler-env cenv - (comp/emit + (emit (analyze aenv '(defn foo ([a]) ([a b]))))) ) @@ -141,7 +144,7 @@ (ana/analyze-file (File. "src/main/cljs/cljs/core.cljs")) (let [warnings (-> (capture-warnings (with-out-str - (comp/emit + (emit (analyze aenv '(let [{:keys [a] :or {b 2}} {:a 1}] [a b]))))))] (is (= (ffirst warnings) :undeclared-var)) @@ -163,7 +166,7 @@ (capture-warnings (env/with-compiler-env (atom cenv-with-foo) (with-out-str - (comp/emit + (emit (analyze aenv-with-foo form)))))) '(cljs.user/foo nil) @@ -222,7 +225,7 @@ (capture-warnings (env/with-compiler-env cenv (with-out-str - (comp/emit + (emit (comp/with-core-cljs opts (fn [] (analyze aenv test-cljs-1925-code nil opts))))))))))) @@ -233,7 +236,7 @@ (capture-warnings (env/with-compiler-env cenv (with-out-str - (comp/emit + (emit (comp/with-core-cljs opts (fn [] (analyze aenv specify-test-code nil opts)))))))))))) @@ -286,7 +289,7 @@ (comment (binding [ana/*cljs-static-fns* true] (env/with-compiler-env cenv - (comp/emit + (emit (analyze aenv '(defn incme [] (let [incme (fn [a queue & args])] @@ -298,7 +301,7 @@ (binding [ana/*cljs-static-fns* true] (env/with-compiler-env cenv - (comp/emit + (emit (analyze aenv '(defn foo [x] (if ^boolean (goog.array/isEmpty x) From 4f3707624846a2cf0345859e41370ec172da73c4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 1 Sep 2018 13:43:24 -0400 Subject: [PATCH 3278/4033] don't invoke load-core from analyze-form, unroll emits & emitln --- src/main/clojure/cljs/analyzer.cljc | 1 - src/main/clojure/cljs/compiler.cljc | 57 +++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 00085db0b..04b93ff07 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3804,7 +3804,6 @@ #?(:clj (defn analyze-form [env form name opts] - (load-core) (cond (symbol? form) (analyze-symbol env form) (and (seq? form) (seq form)) (analyze-seq env form name opts) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a0d8d85a8..00b3ad124 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -197,25 +197,35 @@ (sorted-map)))))))))) (emit* ast)) -(defn emits [& xs] - (doseq [^Object x xs] - (cond - (nil? x) nil - #?(:clj (map? x) :cljs (ana/cljs-map? x)) (emit x) - #?(:clj (seq? x) :cljs (ana/cljs-seq? x)) (apply emits x) - #?(:clj (fn? x) :cljs ^boolean (goog/isFunction x)) (x) - :else (let [^String s (cond-> x (not (string? x)) .toString)] +(defn emits + ([]) + ([^Object a] + (cond + (nil? a) nil + #?(:clj (map? a) :cljs (ana/cljs-map? a)) (emit a) + #?(:clj (seq? a) :cljs (ana/cljs-seq? a)) (apply emits a) + #?(:clj (fn? a) :cljs ^boolean (goog/isFunction a)) (a) + :else (let [^String s (cond-> a (not (string? a)) .toString)] (when-not (nil? *source-map-data*) (swap! *source-map-data* update-in [:gen-col] #(+ % (count s)))) #?(:clj (.write ^Writer *out* s) - :cljs (print s))))) - nil) - -(defn emitln [& xs] - (apply emits xs) - (binding [*flush-on-newline* false] - (println)) + :cljs (print s)))) + nil) + ([a b] + (emits a) (emits b)) + ([a b c] + (emits a) (emits b) (emits c)) + ([a b c d] + (emits a) (emits b) (emits c) (emits d)) + ([a b c d e] + (emits a) (emits b) (emits c) (emits d) (emits e)) + ([a b c d e & xs] + (emits a) (emits b) (emits c) (emits d) (emits e) + (doseq [x xs] (emits x)))) + +(defn ^:private _emitln [] + (newline) (when *source-map-data* (swap! *source-map-data* (fn [{:keys [gen-line] :as m}] @@ -224,6 +234,23 @@ :gen-col 0)))) nil) +(defn emitln + ([] (_emitln)) + ([a] + (emits a) (_emitln)) + ([a b] + (emits a) (emits b) (_emitln)) + ([a b c] + (emits a) (emits b) (emits c) (_emitln)) + ([a b c d] + (emits a) (emits b) (emits c) (emits d) (_emitln)) + ([a b c d e] + (emits a) (emits b) (emits c) (emits d) (emits e) (_emitln)) + ([a b c d e & xs] + (emits a) (emits b) (emits c) (emits d) (emits e) + (doseq [x xs] (emits x)) + (_emitln))) + (defn ^String emit-str [expr] (with-out-str (emit expr))) From 83866aaf597f183877c0cf586c002f3b8b51d487 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 6 Sep 2018 12:41:29 -0400 Subject: [PATCH 3279/4033] CLJS-2894: Optimize source map gen-col counting --- src/main/clojure/cljs/compiler.cljc | 20 +++++++++++++------- src/main/clojure/cljs/repl.cljc | 5 +++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 00b3ad124..84dd1b4bb 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -31,6 +31,7 @@ [cljs.source-map :as sm])) #?(:clj (:import java.lang.StringBuilder [java.io File Writer] + [java.util.concurrent.atomic AtomicLong] [cljs.tagged_literals JSValue]) :cljs (:import [goog.string StringBuffer]))) @@ -50,6 +51,7 @@ (def ^:dynamic *recompiled* nil) (def ^:dynamic *inputs* nil) (def ^:dynamic *source-map-data* nil) +(def ^:dynamic *source-map-data-gen-col* nil) (def ^:dynamic *lexical-renames* {}) (def cljs-reserved-file-names #{"deps.cljs"}) @@ -183,7 +185,8 @@ (let [{:keys [line column]} env] (swap! *source-map-data* (fn [m] - (let [minfo (cond-> {:gcol (:gen-col m) + (let [minfo (cond-> {:gcol #?(:clj (.get ^AtomicLong *source-map-data-gen-col*) + :cljs (:gen-col m)) :gline (:gen-line m)} (#{:var :local :js-var} (:op ast)) (assoc :name (str (-> ast :info :name))))] @@ -206,9 +209,10 @@ #?(:clj (seq? a) :cljs (ana/cljs-seq? a)) (apply emits a) #?(:clj (fn? a) :cljs ^boolean (goog/isFunction a)) (a) :else (let [^String s (cond-> a (not (string? a)) .toString)] - (when-not (nil? *source-map-data*) - (swap! *source-map-data* - update-in [:gen-col] #(+ % (count s)))) + #?(:clj (when-some [^AtomicLong gen-col *source-map-data-gen-col*] + (.addAndGet gen-col (.length s))) + :cljs (when-some [sm-data *source-map-data*] + (swap! sm-data update :gen-col #(+ % (.-length s))))) #?(:clj (.write ^Writer *out* s) :cljs (print s)))) nil) @@ -227,11 +231,12 @@ (defn ^:private _emitln [] (newline) (when *source-map-data* + #?(:clj (.set ^AtomicLong *source-map-data-gen-col* 0)) (swap! *source-map-data* (fn [{:keys [gen-line] :as m}] (assoc m :gen-line (inc gen-line) - :gen-col 0)))) + #?@(:cljs [:gen-col 0]))))) nil) (defn emitln @@ -1498,8 +1503,8 @@ *source-map-data* (when (:source-map opts) (atom {:source-map (sorted-map) - :gen-col 0 :gen-line 0})) + *source-map-data-gen-col* (AtomicLong.) find-ns-starts-with (memoize find-ns-starts-with)] (emitln (compiled-by-string opts)) (with-open [rdr (io/reader src)] @@ -1538,7 +1543,8 @@ :name ns-name})) (emit ast) (recur (rest forms) ns-name deps)))) - (let [sm-data (when *source-map-data* @*source-map-data*) + (let [sm-data (when *source-map-data* (assoc @*source-map-data* + :gen-col (.get ^AtomicLong *source-map-data-gen-col*))) ret (merge {:ns (or ns-name 'cljs.user) :macros-ns (:macros-ns opts) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 19ec5633c..b3e536c60 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -27,6 +27,7 @@ (:import [java.io File PushbackReader FileWriter PrintWriter] [java.net URL] [java.util Base64] + [java.util.concurrent.atomic AtomicLong] [clojure.lang IExceptionInfo] [java.util.regex Pattern] [com.google.common.base Throwables])) @@ -530,8 +531,8 @@ (if (:source-map repl-env) (binding [comp/*source-map-data* (atom {:source-map (sorted-map) - :gen-col 0 - :gen-line 0})] + :gen-line 0}) + comp/*source-map-data-gen-col* (AtomicLong.)] (let [js (comp/emit-str ast) t (System/currentTimeMillis)] (str js From 3e28f7b9703a8995a58c7a285b05aa36daf76ddd Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 3 Sep 2018 15:43:33 -0400 Subject: [PATCH 3280/4033] CLJS-2884: Support for GraalJS RC6 --- .travis.yml | 8 ++++---- src/main/clojure/cljs/repl/graaljs.clj | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 082f712e9..3bc0b966e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,8 @@ before_install: - sudo apt-get install -y libjavascriptcoregtk-3.0-bin - wget https://aka.ms/chakracore/cc_linux_x64_1_8_1 -O chakra-core.tar.gz - tar xvzf chakra-core.tar.gz - - wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc4/graalvm-ce-1.0.0-rc4-linux-amd64.tar.gz - - tar xzf graalvm-ce-1.0.0-rc4-linux-amd64.tar.gz + - wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc6/graalvm-ce-1.0.0-rc6-linux-amd64.tar.gz + - tar xzf graalvm-ce-1.0.0-rc6-linux-amd64.tar.gz before_script: - script/bootstrap @@ -55,7 +55,7 @@ script: - grep '0 failures, 0 errors.' test-out.txt - ./ChakraCoreFiles/bin/ch builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - ./graalvm-ce-1.0.0-rc4/bin/js builds/out-adv/core-advanced-test.js | tee test-out.txt + - ./graalvm-ce-1.0.0-rc6/bin/js builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - script/test-self-host | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt @@ -67,5 +67,5 @@ script: - grep '0 failures, 0 errors.' test-out.txt - script/test-cli rhino | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - PATH=`pwd`/graalvm-ce-1.0.0-rc4/bin:$PATH script/test-cli graaljs | tee test-out.txt + - PATH=`pwd`/graalvm-ce-1.0.0-rc6/bin:$PATH script/test-cli graaljs | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt diff --git a/src/main/clojure/cljs/repl/graaljs.clj b/src/main/clojure/cljs/repl/graaljs.clj index 5b0e4858e..55b0daaeb 100644 --- a/src/main/clojure/cljs/repl/graaljs.clj +++ b/src/main/clojure/cljs/repl/graaljs.clj @@ -39,8 +39,7 @@ (let [engine (eval `(GraalJSScriptEngine/create nil (-> (Context/newBuilder (make-array String 0)) ~@(form-js-opts opts) - (.allowHostAccess true) - (.allowCreateThread true) + (.allowAllAccess true) (.allowNativeAccess true)))) context (.getContext engine)] (.setWriter context *out*) @@ -183,7 +182,7 @@ (string/join "\n" (drop 1 (string/split st #"\n"))))))) (def ^:private default-js-opts - {"js.precise-time" "true"}) + {"js.timer-resolution" "1"}) (defn repl-env* [{:keys [debug] :as opts}] (let [opts (merge default-js-opts opts) From 2f73857f0af13fb386cedbc4da21f4e95e05fdfc Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 4 Sep 2018 21:25:52 -0400 Subject: [PATCH 3281/4033] CLJS-2888: Printing of spec problems buries the failing predicate which should be more prominent --- src/main/cljs/cljs/spec/alpha.cljs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index f38a4aaf0..e8353a780 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -222,18 +222,15 @@ (with-out-str ;;(prn {:ed ed}) (doseq [{:keys [path pred val reason via in] :as prob} problems] - (when-not (empty? in) - (print "In:" (pr-str in) "")) - (print "val: ") (pr val) - (print " fails") - (when-not (empty? via) - (print " spec:" (pr-str (last via)))) + (print " - failed: ") + (if reason (print reason) (pr (abbrev pred))) + (when-not (empty? in) + (print (str " in: " (pr-str in)))) (when-not (empty? path) - (print " at:" (pr-str path))) - (print " predicate: ") - (pr (abbrev pred)) - (when reason (print ", " reason)) + (print (str " at: " (pr-str path)))) + (when-not (empty? via) + (print (str " spec: " (pr-str (last via))))) (doseq [[k v] prob] (when-not (#{:path :pred :val :reason :via :in} k) (print "\n\t" (pr-str k) " ") From 033d19cdaf53f979034c3b60329f71d1509204c9 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 4 Sep 2018 20:55:26 -0400 Subject: [PATCH 3282/4033] CLJS-2889: Improve sorting on problem printing --- src/main/cljs/cljs/spec/alpha.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index e8353a780..3e0e7084d 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -217,7 +217,9 @@ "Default printer for explain-data. nil indicates a successful validation." [ed] (if ed - (let [problems (sort-by #(- (count (:path %))) (::problems ed))] + (let [problems (->> (::problems ed) + (sort-by #(- (count (:in %)))) + (sort-by #(- (count (:path %)))))] (print (with-out-str ;;(prn {:ed ed}) From 5f0fabc65ae7ba201b32cc513a1e5931a80a2bf7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 4 Sep 2018 22:56:36 -0400 Subject: [PATCH 3283/4033] CLJS-2891: stop including data in ex-info message --- src/main/cljs/cljs/spec/alpha.cljs | 6 +++--- src/main/cljs/cljs/spec/test/alpha.cljs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 3e0e7084d..cb8840182 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -323,10 +323,10 @@ (let [ed (assoc (explain-data* arg-spec [:args] (if-let [name (spec-name arg-spec)] [name] []) [] args) ::args args)] - (throw (js/Error. + (throw (ex-info (str - "Call to " (->sym v) " did not conform to spec:\n" - (with-out-str (explain-out ed)))))))))) + "Call to " (->sym v) " did not conform to spec.") + ed))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; impl ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- recur-limit? [rmap id path k] diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 7b4997ff6..1ceb32c94 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -102,7 +102,7 @@ (when caller {::caller caller}))] (throw (ex-info - (str "Call to " v " did not conform to spec:\n" (with-out-str (s/explain-out ed))) + (str "Call to " v " did not conform to spec." ) ed))) conformed)))] (doto (fn [& args] From ef32778989f7ba2311a1e8a5d99c30e6805f5719 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 4 Sep 2018 21:36:57 -0400 Subject: [PATCH 3284/4033] CLJS-2887: Improve names in core macro specs --- src/main/cljs/cljs/core/specs/alpha.cljc | 59 +++++++++++++----------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/main/cljs/cljs/core/specs/alpha.cljc b/src/main/cljs/cljs/core/specs/alpha.cljc index fd48e9602..f2475aeae 100644 --- a/src/main/cljs/cljs/core/specs/alpha.cljc +++ b/src/main/cljs/cljs/core/specs/alpha.cljc @@ -16,17 +16,17 @@ (s/def ::local-name (s/and simple-symbol? #(not= '& %))) (s/def ::binding-form - (s/or :sym ::local-name - :seq ::seq-binding-form - :map ::map-binding-form)) + (s/or :local-symbol ::local-name + :seq-destructure ::seq-binding-form + :map-destructure ::map-binding-form)) ;; sequential destructuring (s/def ::seq-binding-form (s/and vector? - (s/cat :elems (s/* ::binding-form) - :rest (s/? (s/cat :amp #{'&} :form ::binding-form)) - :as (s/? (s/cat :as #{:as} :sym ::local-name))))) + (s/cat :forms (s/* ::binding-form) + :rest-forms (s/? (s/cat :ampersand #{'&} :form ::binding-form)) + :as-form (s/? (s/cat :as #{:as} :as-sym ::local-name))))) ;; map destructuring @@ -47,16 +47,21 @@ (s/coll-of simple-symbol? :kind vector?))) (s/def ::map-bindings - (s/every (s/or :mb ::map-binding - :nsk ::ns-keys - :msb (s/tuple #{:as :or :keys :syms :strs} any?)) :kind map?)) + (s/every (s/or :map-binding ::map-binding + :qualified-keys-or-syms ::ns-keys + :special-binding (s/tuple #{:as :or :keys :syms :strs} any?)) :kind map?)) (s/def ::map-binding-form (s/merge ::map-bindings ::map-special-binding)) ;; bindings -(s/def ::binding (s/cat :binding ::binding-form :init-expr any?)) -(s/def ::bindings (s/and vector? (s/* ::binding))) +(defn even-number-of-forms? + "Returns true if there are an even number of forms in a binding vector" + [forms] + (even? (count forms))) + +(s/def ::binding (s/cat :form ::binding-form :init-expr any?)) +(s/def ::bindings (s/and vector? even-number-of-forms? (s/* ::binding))) ;; let, if-let, when-let @@ -75,25 +80,25 @@ ;; defn, defn-, fn -(s/def ::arg-list +(s/def ::param-list (s/and vector? - (s/cat :args (s/* ::binding-form) - :varargs (s/? (s/cat :amp #{'&} :form ::binding-form))))) + (s/cat :params (s/* ::binding-form) + :var-params (s/? (s/cat :ampersand #{'&} :var-form ::binding-form))))) -(s/def ::args+body - (s/cat :args ::arg-list +(s/def ::params+body + (s/cat :params ::param-list :body (s/alt :prepost+body (s/cat :prepost map? :body (s/+ any?)) :body (s/* any?)))) (s/def ::defn-args - (s/cat :name simple-symbol? + (s/cat :fn-name simple-symbol? :docstring (s/? string?) :meta (s/? map?) - :bs (s/alt :arity-1 ::args+body - :arity-n (s/cat :bodies (s/+ (s/spec ::args+body)) - :attr (s/? map?))))) + :fn-tail (s/alt :arity-1 ::params+body + :arity-n (s/cat :bodies (s/+ (s/spec ::params+body)) + :attr-map (s/? map?))))) (s/fdef core/defn :args ::defn-args @@ -104,9 +109,9 @@ :ret any?) (s/fdef core/fn - :args (s/cat :name (s/? simple-symbol?) - :bs (s/alt :arity-1 ::args+body - :arity-n (s/+ (s/spec ::args+body)))) + :args (s/cat :fn-name (s/? simple-symbol?) + :fn-tail (s/alt :arity-1 ::params+body + :arity-n (s/+ (s/spec ::params+body)))) :ret any?) ;;;; ns @@ -118,7 +123,7 @@ (s/def ::ns-refer-clojure (s/spec (s/cat :clause #{:refer-clojure} - :filters ::filters))) + :refer-filters ::filters))) (s/def ::refer (s/coll-of simple-symbol?)) (s/def ::refer-macros (s/coll-of simple-symbol?)) @@ -150,7 +155,7 @@ (s/def ::package-list (s/spec (s/cat :package simple-symbol? - :classes (s/* simple-symbol?)))) + :classes (s/+ simple-symbol?)))) (s/def ::import-list (s/* (s/alt :class simple-symbol? @@ -193,10 +198,10 @@ :use-macros ::ns-use-macros))) (s/def ::ns-form - (s/cat :name simple-symbol? + (s/cat :ns-name simple-symbol? :docstring (s/? string?) :attr-map (s/? map?) - :clauses ::ns-clauses)) + :ns-clauses ::ns-clauses)) (s/fdef core/ns-special-form :args ::ns-form) From 47553d8a3173ad4ebfcbfc557b73ecb44ac468b6 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 4 Sep 2018 21:15:45 -0400 Subject: [PATCH 3285/4033] CLJS-2890: fspec role in problem path is not useful --- src/main/cljs/cljs/spec/alpha.cljs | 2 +- src/main/cljs/cljs/spec/test/alpha.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index cb8840182..eed0f385e 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -320,7 +320,7 @@ (let [specs (get-spec v)] (when-let [arg-spec (:args specs)] (when (invalid? (conform arg-spec args)) - (let [ed (assoc (explain-data* arg-spec [:args] + (let [ed (assoc (explain-data* arg-spec [] (if-let [name (spec-name arg-spec)] [name] []) [] args) ::args args)] (throw (ex-info diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 1ceb32c94..58863fe3b 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -95,7 +95,7 @@ (get-host-port) (.-stack (js/Error.)) (get-env) nil)) - ed (merge (assoc (s/explain-data* spec [role] [] [] data) + ed (merge (assoc (s/explain-data* spec [] [] [] data) ::s/fn (->sym v) ::s/args args ::s/failure :instrument) From 0598b93e309150a979d3a738ed6cb6a8558dde05 Mon Sep 17 00:00:00 2001 From: Oliver Eidel Date: Wed, 5 Sep 2018 10:05:03 +0200 Subject: [PATCH 3286/4033] CLJS-2893: seq: use .-length instead of alength for strings --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8e2cfce80..b8362f31e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1214,7 +1214,7 @@ (IndexedSeq. coll 0 nil)) (string? coll) - (when-not (zero? (alength coll)) + (when-not (zero? (.-length coll)) (IndexedSeq. coll 0 nil)) (native-satisfies? ISeqable coll) From fc66a5a558b2749c93e1d29605bb69d58f0ad36f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 7 Sep 2018 11:45:59 -0400 Subject: [PATCH 3287/4033] CLJS-2896: Allow parallel analysis and compilation --- src/main/clojure/cljs/closure.clj | 2 +- src/main/clojure/cljs/compiler.cljc | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ee6b1740e..c8b224df3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1047,7 +1047,7 @@ (module-graph/validate-inputs inputs) (let [deque (LinkedBlockingDeque. inputs) input-set (atom (into #{} (comp (remove nil?) (map :ns)) inputs)) - cnt (+ 2 (.. Runtime getRuntime availableProcessors)) + cnt (+ 2 (int (* 0.6 (.. Runtime getRuntime availableProcessors)))) latch (CountDownLatch. cnt) es (Executors/newFixedThreadPool cnt) compiled (atom []) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 84dd1b4bb..502505f5e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -31,6 +31,7 @@ [cljs.source-map :as sm])) #?(:clj (:import java.lang.StringBuilder [java.io File Writer] + [java.util.concurrent Executors ExecutorService TimeUnit] [java.util.concurrent.atomic AtomicLong] [cljs.tagged_literals JSValue]) :cljs (:import [goog.string StringBuffer]))) @@ -1508,7 +1509,12 @@ find-ns-starts-with (memoize find-ns-starts-with)] (emitln (compiled-by-string opts)) (with-open [rdr (io/reader src)] - (let [env (ana/empty-env)] + (let [env (ana/empty-env) + emitter (when (:parallel-build opts) + (Executors/newSingleThreadExecutor)) + emit (if emitter + #(.execute emitter ^Runnable (bound-fn [] (emit %))) + emit)] (loop [forms (ana/forms-seq* rdr (util/path src)) ns-name nil deps nil] @@ -1543,7 +1549,10 @@ :name ns-name})) (emit ast) (recur (rest forms) ns-name deps)))) - (let [sm-data (when *source-map-data* (assoc @*source-map-data* + (let [_ (when emitter + (.shutdown emitter) + (.awaitTermination emitter 1000 TimeUnit/HOURS)) + sm-data (when *source-map-data* (assoc @*source-map-data* :gen-col (.get ^AtomicLong *source-map-data-gen-col*))) ret (merge {:ns (or ns-name 'cljs.user) From 9e1ff2232da00dcda7fb495784e7d5a1d72573a7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 1 Sep 2018 08:27:12 -0400 Subject: [PATCH 3288/4033] CLJS-2878: Update Closure Compiler to v20180805 --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index 7c3dcfed3..976fa86d8 100644 --- a/deps.edn +++ b/deps.edn @@ -7,7 +7,7 @@ org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} com.cognitect/transit-clj {:mvn/version "0.8.309"} - com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180716"} + com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180805"} org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"} org.mozilla/rhino {:mvn/version "1.7R5"}} :aliases diff --git a/pom.template.xml b/pom.template.xml index 1ad8acb62..aaf9ca808 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20180716 + v20180805 org.clojure diff --git a/project.clj b/project.clj index efbc513ed..e291ae12e 100644 --- a/project.clj +++ b/project.clj @@ -16,7 +16,7 @@ [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] - [com.google.javascript/closure-compiler-unshaded "v20180716"] + [com.google.javascript/closure-compiler-unshaded "v20180805"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main cljs.main} diff --git a/script/bootstrap b/script/bootstrap index 874f5f059..2662d91a6 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="20180716" +CLOSURE_RELEASE="20180805" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" From 1a155adb0984de26b9606d8aa7c0e1388567eb0b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 3 Sep 2018 08:57:15 -0400 Subject: [PATCH 3289/4033] CLJS-2883: Instrumentation fails compilation with a large number of spec'd functions When instrumenting, unstrumenting, or checking, we normally evaluate the suppied form in order to convert it to an (unquoted) symbol or collection of symbols. In the case that this is all speced vars or all checkable syms, this quoted list can be large enough to cause eval to fail. Fortunately, in this special case there is no need to actually apply eval because we know the list of symbols is the second item in the form. --- src/main/cljs/cljs/spec/test/alpha.cljc | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index ed93e6159..0a6576872 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -83,6 +83,17 @@ returns the set of all symbols naming vars in those nses." [sym]))) (collectionize sym-or-syms))) +(defn- form->sym-or-syms + "Helper for extracting a symbol or symbols from a (potentially + user-supplied) quoted form. In the case that the form has ::no-eval meta, we + know it was generated by us and we directly extract the result, assuming the + shape of the form. This avoids applying eval to extremely large forms in the + latter case." + [sym-or-syms] + (if (::no-eval (meta sym-or-syms)) + (second sym-or-syms) + (eval sym-or-syms))) + (defmacro instrument "Instruments the vars named by sym-or-syms, a symbol or collection of symbols, or all instrumentable vars if sym-or-syms is not @@ -122,12 +133,12 @@ invokes the fn you provide, enabling arbitrary stubbing and mocking. Returns a collection of syms naming the vars instrumented." ([] - `(instrument '[~@(#?(:clj s/speced-vars - :cljs cljs.spec.alpha$macros/speced-vars))])) + `(instrument ^::no-eval '[~@(#?(:clj s/speced-vars + :cljs cljs.spec.alpha$macros/speced-vars))])) ([xs] `(instrument ~xs nil)) ([sym-or-syms opts] - (let [syms (sym-or-syms->syms (eval sym-or-syms)) + (let [syms (sym-or-syms->syms (form->sym-or-syms sym-or-syms)) opts-sym (gensym "opts")] `(let [~opts-sym ~opts] (reduce @@ -148,9 +159,9 @@ Returns a collection of syms naming the vars instrumented." as in instrument. With no args, unstruments all instrumented vars. Returns a collection of syms naming the vars unstrumented." ([] - `(unstrument '[~@(deref instrumented-vars)])) + `(unstrument ^::no-eval '[~@(deref instrumented-vars)])) ([sym-or-syms] - (let [syms (sym-or-syms->syms (eval sym-or-syms))] + (let [syms (sym-or-syms->syms (form->sym-or-syms sym-or-syms))] `(reduce (fn [ret# f#] (let [sym# (f#)] @@ -256,11 +267,11 @@ spec itself will have an ::s/failure value in ex-data: :instrument invalid args detected by instrument " ([] - `(check '~(checkable-syms*))) + `(check ^::no-eval '~(checkable-syms*))) ([sym-or-syms] `(check ~sym-or-syms nil)) ([sym-or-syms opts] - (let [syms (sym-or-syms->syms (eval sym-or-syms)) + (let [syms (sym-or-syms->syms (form->sym-or-syms sym-or-syms)) opts-sym (gensym "opts")] `(let [~opts-sym ~opts] [~@(->> syms From 0a0ff32f9b479a002d7875b327d89e6a66d26f82 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 9 Sep 2018 11:08:10 -0400 Subject: [PATCH 3290/4033] CLJS-2897: cljs.main: Display initial REPL prompt sooner --- src/main/clojure/cljs/cli.clj | 8 ++++++++ src/main/clojure/cljs/repl.cljc | 15 +++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 380144627..5d1bb2465 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -289,6 +289,13 @@ is trying load some arbitrary ns." (util/mkdirs f) (util/path f))) +(defn- repl-name [repl-env] + (subs (-> repl-env meta :ns str) (count "cljs.repl."))) + +(defn- fast-initial-prompt? [repl-env inits] + (and (empty? inits) + (contains? #{"node" "nashorn" "graaljs" "rhino"} (repl-name repl-env)))) + (defn- repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" @@ -304,6 +311,7 @@ present" renv (apply repl-env (mapcat identity reopts))] (repl/repl* renv (assoc (dissoc-entry-point-opts opts) + ::repl/fast-initial-prompt? (fast-initial-prompt? repl-env inits) :inits (into [{:type :init-forms diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index b3e536c60..f72c666cf 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -853,13 +853,19 @@ (cljsc/maybe-install-node-deps! opts) installed?))))) +(defn initial-prompt [quit-prompt prompt] + (quit-prompt) + (prompt) + (flush)) + (defn repl* [repl-env {:keys [init inits need-prompt quit-prompt prompt flush read eval print caught reader - print-no-newline source-map-inline wrap repl-requires + print-no-newline source-map-inline wrap repl-requires ::fast-initial-prompt? compiler-env bind-err] :or {need-prompt #(if (readers/indexing-reader? *in*) (== (readers/get-column-number *in*) 1) (identity true)) + fast-initial-prompt? false quit-prompt repl-title prompt repl-prompt flush flush @@ -879,6 +885,8 @@ (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) (set/union known-repl-opts cljsc/known-opts))] (when suggested-opt (println (str "WARNING: Unknown option '" unknown-opt "'. Did you mean '" suggested-opt "'?")))) + (when fast-initial-prompt? + (initial-prompt quit-prompt prompt)) (let [repl-opts (-repl-options repl-env) repl-requires (into repl-requires (:repl-requires repl-opts)) {:keys [analyze-path repl-verbose warn-on-undeclared special-fns @@ -1010,9 +1018,8 @@ (binding [*in* (if (true? (:source-map-inline opts)) *in* (reader))] - (quit-prompt) - (prompt) - (flush) + (when-not fast-initial-prompt? + (initial-prompt quit-prompt prompt)) (loop [] (when-not (try From c2f5aba2f59cf30dbc13c968d8ee3f0e4359d38c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Sep 2018 08:51:06 -0400 Subject: [PATCH 3291/4033] CLJS-2904: Default :npm-deps to false --- src/main/clojure/cljs/closure.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c8b224df3..3c547d769 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2370,6 +2370,9 @@ (not (contains? opts :aot-cache)) (assoc :aot-cache false) + (not (contains? opts :npm-deps)) + (assoc :npm-deps false) + (contains? opts :modules) (ensure-module-opts) From 0e31a09fa48c28dc260da4675a4a7f7ad8b3d131 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Sep 2018 11:04:13 -0400 Subject: [PATCH 3292/4033] fix broken test from last commit --- src/test/clojure/cljs/build_api_tests.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 067a631bf..c453dff19 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -469,6 +469,7 @@ {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) :opts {:main 'node-modules-opt-test.core :output-dir out + :npm-deps true :optimizations :none :closure-warnings {:check-types :off}}} cenv (env/default-compiler-env opts)] From 71f57714e6fc2f591d9de22cbfcfa009500e6742 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 Sep 2018 13:15:23 -0400 Subject: [PATCH 3293/4033] CLJS-2906: cljs.main: Crash when with default REPL repl-name name helper needs to handle case where :ns is nil --- src/main/clojure/cljs/cli.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 5d1bb2465..6dc57533c 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -290,7 +290,9 @@ is trying load some arbitrary ns." (util/path f))) (defn- repl-name [repl-env] - (subs (-> repl-env meta :ns str) (count "cljs.repl."))) + (let [repl-ns (-> repl-env meta :ns str)] + (when (string/starts-with? repl-ns "cljs.repl.") + (subs repl-ns (count "cljs.repl."))))) (defn- fast-initial-prompt? [repl-env inits] (and (empty? inits) From 6062744a1600479d5b9c641db9fb15cbb1df023c Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Sun, 16 Sep 2018 21:59:54 +0200 Subject: [PATCH 3294/4033] CLJS-2911 Avoid infinite loop on infinite partitions --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/seqs_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b8362f31e..8fd22d1d8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9721,7 +9721,7 @@ reduces them without incurring seq initialization" (let [fst (first s) fv (f fst) run (cons fst (take-while #(= fv (f %)) (next s)))] - (cons run (partition-by f (seq (drop (count run) s))))))))) + (cons run (partition-by f (lazy-seq (drop (count run) s))))))))) (defn frequencies "Returns a map from distinct items in coll to the number of times diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index b4ad4d173..8a74a66c8 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -216,3 +216,7 @@ (deftest test-cljs-2482 (testing "seq on defrecord returns map entries" (is (every? map-entry? (seq (->Foo 1 2)))))) + +(deftest test-cljs-2911 + (testing "partition-by works correclty with infinite seqs" + (is (= (first (second (partition-by zero? (range)))) 1)))) From cad53c639283fac289f9896dd88cceaf525ddba2 Mon Sep 17 00:00:00 2001 From: Ray McDermott Date: Tue, 25 Sep 2018 22:22:38 +0200 Subject: [PATCH 3295/4033] CLJS-2782: lein test fails if directory has hyphens The hyphen fix is only needed for code cases. This further fix limits the code to cases where that predicate is true. --- src/test/clojure/cljs/module_processing_tests.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 27b5749ce..f8c7f41e0 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -36,7 +36,7 @@ (.getAbsolutePath $) (subs $ 0 (.lastIndexOf $ (str File/separator))) (string/replace $ "/" "$") - (string/replace $ "-" "_") + (cond-> $ code? (string/replace "-" "_")) ;; Windows (string/replace $ "\\" "$") (if code? From 6eedd0a08c49f7b0d4dcb30977b2fb38c90577bd Mon Sep 17 00:00:00 2001 From: Ray McDermott Date: Tue, 25 Sep 2018 23:57:30 +0200 Subject: [PATCH 3296/4033] CLJS-2915: Tests fail if directory has a period (.) in the path We replace the periods with hyphens. We then leverage the cond-> to selectively transform to underscores. I coded this specific `replace` as char / char - which is different from the string / srting pattern used in the rest of the pipeline - as it feels simpler than the equivalent regex. --- src/test/clojure/cljs/module_processing_tests.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index f8c7f41e0..533d47b2a 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -36,6 +36,7 @@ (.getAbsolutePath $) (subs $ 0 (.lastIndexOf $ (str File/separator))) (string/replace $ "/" "$") + (string/replace $ \. \-) (cond-> $ code? (string/replace "-" "_")) ;; Windows (string/replace $ "\\" "$") From 9da36c6235e3c978ae333970a25fb9ce22147fd7 Mon Sep 17 00:00:00 2001 From: Eugene Kostenko Date: Tue, 23 Oct 2018 17:22:14 +0700 Subject: [PATCH 3297/4033] CLJS-2941: seqable? should return true for nil --- src/main/cljs/cljs/core.cljs | 1 + src/test/cljs/cljs/predicates_test.cljs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8fd22d1d8..c6c3fe545 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2224,6 +2224,7 @@ reduces them without incurring seq initialization" "Return true if the seq function is supported for s" [s] (or + (nil? s) (satisfies? ISeqable s) (array? s) (string? s))) diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 0759888c5..2e49406c5 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -19,7 +19,7 @@ [1.0 false false false false false false false false false false false false] [true true false false false false false false false false false false false] [[] false true true false false false false false false false false false] - [nil false false false false false false false false false false false false] + [nil false false true false false false false false false false false false] [{} false false true false false false false false false false false false] [:foo false false false true false false true false false false true false] [::foo false false false true false false false true false false false true] From c61a2358a6ff7d9f5aa8c50843c29580293be861 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 22 Oct 2018 15:35:27 -0400 Subject: [PATCH 3298/4033] CLJS-2943: Update merge-with to use key / val --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c6c3fe545..f582bce1c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9018,7 +9018,7 @@ reduces them without incurring seq initialization" [f & maps] (when (some identity maps) (let [merge-entry (fn [m e] - (let [k (first e) v (second e)] + (let [k (key e) v (val e)] (if (contains? m k) (assoc m k (f (get m k) v)) (assoc m k v)))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 184294ce5..7866e5433 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1625,3 +1625,15 @@ (is (false? ((comp not empty?) ""))) (is (thrown? js/Error ((not empty?) "foo"))) (is (thrown? js/Error ((not empty?) "")))) + +(deftest test-cljs-2943 + (let [m1 {:a 2, :b 3, :c 5} + m2 {:a 7, :b 11, :d 13, :e 17} + m3 {:a 19, :d 23, :f 29} + m4 {:a 28, :b 14, :c 5, :d 36, :e 17, :f 29} + sorted (fn [m] (into (sorted-map) m))] + (is (= m4 (merge-with + m1 m2 m3))) + (is (= m4 (merge-with + (sorted m1) m2 m3))) + (is (= m4 (merge-with + (sorted m1) (sorted m2) m3))) + (is (= m4 (merge-with + m1 (sorted m2) m3))) + (is (= m4 (merge-with + m1 (sorted m2) (sorted m3)))))) From 1db8679f60c370cf0eca1bab4080e8e0dd925791 Mon Sep 17 00:00:00 2001 From: Eugene Kostenko Date: Mon, 22 Oct 2018 18:54:49 +0700 Subject: [PATCH 3299/4033] CLJS-2937: docstring for to-array --- src/main/cljs/cljs/core.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f582bce1c..934c46875 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3611,10 +3611,10 @@ reduces them without incurring seq initialization" ;;;;;;;;;;;;;;;; (defn to-array - "Naive impl of to-array as a start." - [s] + "Returns an array containing the contents of coll." + [coll] (let [ary (array)] - (loop [s (seq s)] + (loop [s (seq coll)] (if-not (nil? s) (do (. ary push (first s)) (recur (next s))) From d8ae1091d0ef00cc7f362fa88ae6640765f80663 Mon Sep 17 00:00:00 2001 From: Samuel Miller Date: Thu, 27 Sep 2018 20:33:48 +0200 Subject: [PATCH 3300/4033] CLJS-1297 defrecord does not emit IKVReduce protocol Changed the setup for defrecord and provided a simple implementation of kvreduce. The implementation is copied from Clojure. Also added one test. --- src/main/clojure/cljs/core.cljc | 3 +++ src/test/cljs/cljs/new_new_test.cljs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index df8602724..685513317 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1867,6 +1867,9 @@ writer# pr-pair# ~pr-open ", " "}" opts# (concat [~@(map #(core/list `vector (keyword %) %) base-fields)] ~'__extmap)))) + 'IKVReduce + `(~'-kv-reduce [this# f# init#] + (reduce (fn [ret# [k# v#]] (f# ret# k# v#)) init# this#)) ]) [fpps pmasks] (prepare-protocol-masks env impls) protocols (collect-protocols impls env) diff --git a/src/test/cljs/cljs/new_new_test.cljs b/src/test/cljs/cljs/new_new_test.cljs index fac856619..26d6b6518 100644 --- a/src/test/cljs/cljs/new_new_test.cljs +++ b/src/test/cljs/cljs/new_new_test.cljs @@ -80,6 +80,8 @@ (map->Person {:firstname "Fred" :lastname "Mertz" :wife :ethel}))) (is (= (dissoc ethel :husband) (map->Person {:firstname "Ethel" :lastname "Mertz"}))) + (is (= (reduce-kv assoc {:age 30} fred) + {:age 30 :firstname "Fred" :lastname "Mertz"})) (is (= {:foo 'bar} (meta (with-meta (A.) {:foo 'bar})))) (is (= 'bar (:foo (assoc (A.) :foo 'bar)))) (is (= (set (keys letters)) #{:a :b :c})) From 0e3d4f15f5e74639fdd4007afd509728f0e3426f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 18 Aug 2018 20:26:00 -0400 Subject: [PATCH 3301/4033] CLJS-2864: Optimize str macro for single arity case For the single arity case, emit code that doesn't involve constructing a JavaScript array and a call to join, but instead directly calls the runtime single-arity str implementation. --- src/main/clojure/cljs/core.cljc | 26 ++++++++++++++++---------- src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 685513317..97b39389c 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -827,16 +827,22 @@ (core/quot c 32) (core/inc (core/quot c 32))))) -(core/defmacro str [& xs] - (core/let [interpolate (core/fn [x] - (if (core/string? x) - "~{}" - "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})")) - strs (core/->> xs - (map interpolate) - (interpose ",") - (apply core/str))] - (list* 'js* (core/str "[" strs "].join('')") xs))) +(core/defmacro str + ([] "") + ([x] + (if (core/string? x) + x + (core/list 'js* "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})" x))) + ([x & ys] + (core/let [interpolate (core/fn [x] + (if (core/string? x) + "~{}" + "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})")) + strs (core/->> (core/list* x ys) + (map interpolate) + (interpose ",") + (apply core/str))] + (list* 'js* (core/str "[" strs "].join('')") x ys)))) (core/defn- bool-expr [e] (vary-meta e assoc :tag 'boolean)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 7866e5433..cdc19a44b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1626,6 +1626,13 @@ (is (thrown? js/Error ((not empty?) "foo"))) (is (thrown? js/Error ((not empty?) "")))) +(deftest test-cljs-2864 + (is (= "" (str))) + (is (= "a" (str "a"))) + (is (= "1" (str 1))) + (is (= "xyzzy" (str "x" "y" "z" "z" "y"))) + (is (= "a1b2c3" (str "a" 1 "b" 2 "c" 3)))) + (deftest test-cljs-2943 (let [m1 {:a 2, :b 3, :c 5} m2 {:a 7, :b 11, :d 13, :e 17} From 52112c5921d2b145b9dc1b8f43e61cbeae37e81a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 13 Oct 2018 19:29:26 -0400 Subject: [PATCH 3302/4033] CLJS-2934: Enhanced delay printing --- src/main/cljs/cljs/core.cljs | 8 +++++++- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 934c46875..733e0731e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10395,7 +10395,13 @@ reduces them without incurring seq initialization" IPending (-realized? [x] - (not f))) + (not f)) + + IPrintWithWriter + (-pr-writer [x writer opts] + (-write writer "#object[cljs.core.Delay ") + (pr-writer {:status (if (nil? f) :ready :pending), :val value} writer opts) + (-write writer "]"))) (defn ^boolean delay? "returns true if x is a Delay created with delay" diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index cdc19a44b..501ec8080 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1633,6 +1633,12 @@ (is (= "xyzzy" (str "x" "y" "z" "z" "y"))) (is (= "a1b2c3" (str "a" 1 "b" 2 "c" 3)))) +(deftest test-cljs-2934 + (let [x (delay 1)] + (is (= "#object[cljs.core.Delay {:status :pending, :val nil}]" (pr-str x))) + (force x) + (is (= "#object[cljs.core.Delay {:status :ready, :val 1}]" (pr-str x))))) + (deftest test-cljs-2943 (let [m1 {:a 2, :b 3, :c 5} m2 {:a 7, :b 11, :d 13, :e 17} From 5cc0e6c064132cb3e122177a1a110d5b0d98fceb Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Oct 2018 22:20:05 +0200 Subject: [PATCH 3303/4033] CLJS-2903: Support fingerprinting Add a new compiler flag :fingerprint which will append content SHA to file name. manifest.edn is emitted to :output-dir for mapping to fingerprinted names. --- src/main/cljs/cljs/loader.cljs | 8 +- src/main/clojure/cljs/closure.clj | 155 +++++++++++++++------- src/main/clojure/cljs/module_graph.cljc | 3 + src/test/clojure/cljs/build_api_tests.clj | 48 ++++++- 4 files changed, 158 insertions(+), 56 deletions(-) diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 7b2cc406a..249f0e85a 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -12,7 +12,10 @@ [goog.module ModuleManager])) (def module-infos MODULE_INFOS) ;; set by compiler -(def module-uris MODULE_URIS) ;; set by compiler +(def module-uris + (if (exists? js/COMPILED_MODULE_URIS) + js/COMPILED_MODULE_URIS + MODULE_URIS)) ;; set by compiler (defn deps-for [x graph] (let [depends-on (get graph x)] @@ -39,7 +42,8 @@ (defonce ^:dynamic *module-manager* (create-module-manager)) (.setAllModuleInfo *module-manager* (to-js module-infos)) -(.setModuleUris *module-manager* (to-js module-uris)) +(.setModuleUris *module-manager* + (cond-> module-uris (map? module-uris) to-js)) (defn loaded? "Return true if modules is loaded. module-name should be a keyword matching diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3c547d769..0c541c4c3 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -202,7 +202,7 @@ :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs :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}) + :stable-names :ignore-js-module-exts :opts-cache :aot-cache :elide-strict :fingerprint}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -1595,7 +1595,19 @@ (cond-> js (not (false? elide-strict)) (string/replace #"(?m)^['\"]use strict['\"]" " "))) -(defn output-one-file [{:keys [output-to] :as opts} js] +(defn ^File fingerprint-out-file + [content ^File out-file] + (let [dir (.getParent out-file) + fn (.getName out-file) + idx (.lastIndexOf fn ".") + ext (subs fn (inc idx)) + name (subs fn 0 idx)] + (io/file dir + (str name "-" + (string/lower-case + (util/content-sha content 7)) "." ext)))) + +(defn output-one-file [{:keys [output-to fingerprint] :as opts} js] (let [js (elide-strict js opts)] (cond (nil? output-to) js @@ -1604,7 +1616,13 @@ (util/file? output-to)) (let [f (io/file output-to)] (util/mkdirs f) - (spit f js)) + (spit f js) + (when fingerprint + (let [dir (.getParent f) + mf (io/file dir "manifest.edn") + g (fingerprint-out-file js f)] + (.renameTo f g) + (spit mf (pr-str {(.toString f) (.toString g)}))))) :else (println js)))) @@ -1722,56 +1740,99 @@ (when-let [main (:main opts)] [main]))))))))) +(defn fingerprinted-modules [modules fingerprint-info] + (into {} + (map + (fn [[module-name module-info]] + (let [module-info' + (assoc module-info :output-to + (get-in fingerprint-info + [module-name :output-to-fingerprint]))] + [module-name module-info']))) + modules)) + (defn output-modules "Given compiler options, original IJavaScript sources and a sequence of module name and module description tuples output module sources to disk. Modules description must define :output-to and supply :source entry with the JavaScript source to write to disk." [opts js-sources modules] - (doseq [[name {:keys [output-to source foreign-deps] :as module-desc}] modules] - (assert (not (nil? output-to)) - (str "Module " name " does not define :output-to")) - (assert (not (nil? source)) - (str "Module " name " did not supply :source")) - (let [fdeps-str (when-not (empty? foreign-deps) - (foreign-deps-str opts foreign-deps)) - sm-name (when (:source-map opts) - (str output-to ".map")) - out-file (io/file output-to)] - (util/mkdirs out-file) - (spit out-file - (as-> source source - (if (= name :cljs-base) - (add-header opts source) - source) - (if fdeps-str - (str fdeps-str "\n" source) - source) - (elide-strict source opts) - (if sm-name - (add-source-map-link - (assoc opts - :output-to output-to - :source-map sm-name) - source) - source))) - (when (:source-map opts) - (let [sm-json-str (:source-map-json module-desc) - sm-json (json/read-str sm-json-str :key-fn keyword)] - (when (true? (:closure-source-map opts)) - (spit (io/file (:source-map-name module-desc)) sm-json-str)) - (emit-optimized-source-map sm-json js-sources sm-name - (merge opts - {:source-map sm-name - :preamble-line-count - (if (= name :cljs-base) - (+ (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1) - (if (:output-wrapper opts) 1 0)) - 0) - :foreign-deps-line-count - (if fdeps-str - (- (count (.split #"\r?\n" fdeps-str -1)) 1) - 0)}))))))) + (let [fingerprint-info (atom {})] + (doseq [[name {:keys [output-to source foreign-deps] :as module-desc}] modules] + (assert (not (nil? output-to)) + (str "Module " name " does not define :output-to")) + (assert (not (nil? source)) + (str "Module " name " did not supply :source")) + (let [fdeps-str (when-not (empty? foreign-deps) + (foreign-deps-str opts foreign-deps)) + sm-name (when (:source-map opts) + (str output-to ".map")) + out-file (io/file output-to) + _ (util/mkdirs out-file) + js (as-> source source + (if (= name :cljs-base) + (add-header opts source) + source) + (if fdeps-str + (str fdeps-str "\n" source) + source) + (elide-strict source opts) + (if sm-name + (add-source-map-link + (assoc opts + :output-to output-to + :source-map sm-name) + source) + source)) + fingerprint-base? (and (:fingerprint opts) (= :cljs-base name))] + (when-not fingerprint-base? + (spit out-file js)) + (when (:fingerprint opts) + (let [out-file' (fingerprint-out-file js out-file)] + (when-not fingerprint-base? + (.renameTo out-file out-file')) + (swap! fingerprint-info update name merge + (when fingerprint-base? {:source js}) + {:output-to (.toString output-to) + :output-to-fingerprint (.toString out-file')}))) + (when (:source-map opts) + (let [sm-json-str (:source-map-json module-desc) + sm-json (json/read-str sm-json-str :key-fn keyword)] + (when (true? (:closure-source-map opts)) + (spit (io/file (:source-map-name module-desc)) sm-json-str)) + (emit-optimized-source-map sm-json js-sources sm-name + (merge opts + {:source-map sm-name + :preamble-line-count + (if (= name :cljs-base) + (+ (- (count (.split #"\r?\n" (make-preamble opts) -1)) 1) + (if (:output-wrapper opts) 1 0) + (if (:fingerprint opts) 1 0)) + 0) + :foreign-deps-line-count + (if fdeps-str + (- (count (.split #"\r?\n" fdeps-str -1)) 1) + 0)})))))) + (when (:fingerprint opts) + (let [fi @fingerprint-info + g (get-in fi [:cljs-base :output-to-fingerprint]) + out (io/file g) + dir (.getParent out) + mnf (io/file dir "manifest.edn") + uris (module-graph/modules->module-uris + (fingerprinted-modules modules fi) js-sources opts)] + (spit mnf + (pr-str + (into {} + (map (juxt :output-to :output-to-fingerprint)) + (vals fi)))) + (spit out + (str "var COMPILED_MODULE_URIS = " + (json/write-str + (into {} + (map (fn [[k v]] [(-> k name munge) v])) uris)) + ";\n" + (get-in fi [:cljs-base :source]))))))) (defn lib-rel-path [{:keys [lib-path url provides] :as ijs}] (if (nil? lib-path) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index 3548712b4..bca828a4f 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -362,6 +362,9 @@ (:advanced :simple :whitespace) (reduce-kv (fn [ret k {:keys [output-to]}] + ;; TODO: move validation + (assert output-to + (str "Module " k " does not specify :output-to")) (assoc ret k [(-> output-to get-rel-path get-uri)])) {:cljs-base [(-> (or (get-in modules [:cljs-base :output-to]) (io/file output-dir "cljs_base.js")) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index c453dff19..1cc9dfcc2 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -9,16 +9,18 @@ (ns cljs.build-api-tests (:refer-clojure :exclude [compile]) (:import java.io.File) - (:require [clojure.test :refer [deftest is testing]] + (:require [cljs.analyzer :as ana] + [cljs.build.api :as build] + [cljs.closure :as closure] + [cljs.env :as env] + [cljs.test-util :as test] + [cljs.util :as util] [clojure.data.json :as json] + [clojure.edn :as edn] [clojure.java.io :as io] [clojure.java.shell :as sh] - [cljs.env :as env] - [cljs.analyzer :as ana] - [cljs.util :as util] - [cljs.test-util :as test] - [cljs.build.api :as build] - [cljs.closure :as closure])) + [clojure.test :refer [deftest is testing]] + [clojure.string :as string])) (deftest test-target-file-for-cljs-ns (is (= (.getPath (build/target-file-for-cljs-ns 'example.core-lib nil)) @@ -645,3 +647,35 @@ (is (re-find #"module\$.+\$node_modules\$graphql\$index\[\"default\"\]" (slurp core-js)))))) (.delete (io/file "package.json")) (test/delete-node-modules)) + +(deftest test-fingerprint + (let [out (io/file (test/tmp-dir) "cljs-2903-out") + opts {:output-to (.getPath (io/file out "main.js")) + :output-dir (.getPath out) + :fingerprint true + :stable-names true + :optimizations :advanced}] + (test/delete-out-files out) + (build/build "src/test/cljs/hello.cljs" opts) + (let [mf (edn/read-string (slurp (io/file out "manifest.edn"))) + f (io/file (get mf (:output-to opts))) + sha (string/lower-case (util/content-sha (slurp (io/file f)) 7))] + (is (true? (.exists f))) + (is (string/includes? (.getPath f) sha))))) + +(deftest test-fingerprint-modules + (let [out (.getPath (io/file (test/tmp-dir) "cljs-2903-modules-out")) + project (update-in (test/project-with-modules out) + [:opts] merge + {:fingerprint true + :stable-names true + :optimizations :advanced})] + (test/delete-out-files out) + (build/build (build/inputs (:inputs project)) (:opts project)) + (let [mf (edn/read-string (slurp (io/file out "manifest.edn")))] + (doseq [[name {:keys [output-to]}] (get-in project [:opts :modules])] + (when-not (= :cljs-base name) + (let [f (io/file (get mf output-to)) + sha (string/lower-case (util/content-sha (slurp (io/file f)) 7))] + (is (true? (.exists f))) + (is (string/includes? (.getPath f) sha)))))))) From 4fb83eff87cc456600a3fd21c111e99a41c61285 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 25 Oct 2018 11:43:24 -0400 Subject: [PATCH 3304/4033] CLJS-2793: Instrumenting breaks function with varargs --- src/main/cljs/cljs/spec/test/alpha.cljc | 14 ++++++++ src/main/cljs/cljs/spec/test/alpha.cljs | 33 +++++++++--------- src/test/cljs/cljs/spec_test.cljs | 45 +++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 0a6576872..636db8e20 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -279,3 +279,17 @@ spec itself will have an ::s/failure value in ex-data: (map (fn [sym] (do `(check-1 '~sym nil nil ~opts-sym)))))])))) + +(defmacro ^:private maybe-setup-static-dispatch [f ret arity] + (let [arity-accessor (symbol (str ".-cljs$core$IFn$_invoke$arity$" arity)) + argv (mapv #(symbol (str "arg" %)) (range arity))] + `(when (some? (~arity-accessor ~f)) + (set! (~arity-accessor ~ret) + (fn ~argv + (apply ~ret ~argv)))))) + +(defmacro ^:private setup-static-dispatches [f ret max-arity] + `(do + ~@(mapv (fn [arity] + `(maybe-setup-static-dispatch ~f ~ret ~arity)) + (range (inc max-arity))))) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 58863fe3b..6c71686a1 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -7,7 +7,7 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.spec.test.alpha - (:require-macros [cljs.spec.test.alpha :as m :refer [with-instrument-disabled]]) + (:require-macros [cljs.spec.test.alpha :as m :refer [with-instrument-disabled setup-static-dispatches]]) (:require [goog.object :as gobj] [goog.userAgent.product :as product] @@ -104,21 +104,22 @@ (throw (ex-info (str "Call to " v " did not conform to spec." ) ed))) - conformed)))] - (doto (fn [& args] - (if *instrument-enabled* - (with-instrument-disabled - (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) - (binding [*instrument-enabled* true] - (apply f args))) - (apply f args))) - (gobj/extend (MetaFn. (fn [& args] - (if *instrument-enabled* - (with-instrument-disabled - (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) - (binding [*instrument-enabled* true] - (apply f args))) - (apply f args))) nil))))) + conformed))) + ret (fn [& args] + (if *instrument-enabled* + (with-instrument-disabled + (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) + (binding [*instrument-enabled* true] + (apply f args))) + (apply f args)))] + (when-not (and (-> (meta v) :top-fn :variadic?) + (zero? (-> (meta v) :top-fn :max-fixed-arity))) + (setup-static-dispatches f ret 20) + (when-some [variadic (.-cljs$core$IFn$_invoke$arity$variadic f)] + (set! (.-cljs$core$IFn$_invoke$arity$variadic ret) + (fn [& args] + (apply variadic args))))) + ret)) (defn- no-fspec [v spec] diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index fe7d876a2..cc07806e3 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -368,6 +368,51 @@ :args (s/cat :k keyword?) :ret string?) +(defn foo-2793 [m & args] + {:m m, :args args}) + +(defn bar-2793 + ([x] {:x x}) + ([x y] {:x x, :y y}) + ([x y & m] {:x x, :y y, :m m})) + +(defn baz-2793 [x & ys]) + +(defn quux-2793 [& numbers]) + +(s/fdef foo-2793) +(s/fdef bar-2793) +(s/fdef baz-2793 :args (s/cat :x number? :ys (s/* number?))) + +(st/instrument `foo-2793) +(st/instrument `bar-2793) +(st/instrument `baz-2793) + +(deftest cljs-2793-test + (is (= {:m {:x 1 :y 2} + :args nil} + (foo-2793 {:x 1 :y 2}))) + (is (= {:m {:x 1 :y 2} + :args [1]} + (foo-2793 {:x 1 :y 2} 1))) + (is (= {:m {:x 1 :y 2} + :args [1 2]} + (foo-2793 {:x 1 :y 2} 1 2))) + (is (= {:x 1} + (bar-2793 1))) + (is (= {:x 1 + :y 2} + (bar-2793 1 2))) + (is (= {:x 1 + :y 2 + :m [3]} + (bar-2793 1 2 3))) + (is (= {:x 1 + :y 2 + :m [3 4]} + (bar-2793 1 2 3 4))) + (is (nil? (baz-2793 1)))) + (comment (run-tests) From 6353a9b381144d6d0caa621322af9587922e7c07 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 28 Oct 2018 19:40:47 -0400 Subject: [PATCH 3305/4033] CLJS-2948: Stack overflow calling instrumented variadic fn with zero args --- src/main/cljs/cljs/spec/test/alpha.cljs | 14 ++++++++++---- src/test/cljs/cljs/spec_test.cljs | 4 +++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 6c71686a1..999215b7c 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -105,15 +105,21 @@ (str "Call to " v " did not conform to spec." ) ed))) conformed))) + pure-variadic? (and (-> (meta v) :top-fn :variadic?) + (zero? (-> (meta v) :top-fn :max-fixed-arity))) + apply' (fn [f args] + (if (and (nil? args) + pure-variadic?) + (.cljs$core$IFn$_invoke$arity$variadic f) + (apply f args))) ret (fn [& args] (if *instrument-enabled* (with-instrument-disabled (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) (binding [*instrument-enabled* true] - (apply f args))) - (apply f args)))] - (when-not (and (-> (meta v) :top-fn :variadic?) - (zero? (-> (meta v) :top-fn :max-fixed-arity))) + (apply' f args))) + (apply' f args)))] + (when-not pure-variadic? (setup-static-dispatches f ret 20) (when-some [variadic (.-cljs$core$IFn$_invoke$arity$variadic f)] (set! (.-cljs$core$IFn$_invoke$arity$variadic ret) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index cc07806e3..8739f72ef 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -387,6 +387,7 @@ (st/instrument `foo-2793) (st/instrument `bar-2793) (st/instrument `baz-2793) +(st/instrument `quux-2793) (deftest cljs-2793-test (is (= {:m {:x 1 :y 2} @@ -411,7 +412,8 @@ :y 2 :m [3 4]} (bar-2793 1 2 3 4))) - (is (nil? (baz-2793 1)))) + (is (nil? (baz-2793 1))) + (is (nil? (quux-2793)))) (comment From 267893a6ee9c8e558a6255fe408f9f38be5f8381 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 30 Oct 2018 12:30:21 -0400 Subject: [PATCH 3306/4033] CLJS-2940: Can't define nilable spec on undefined pred need to delay calls to the spec same as Clojure --- src/main/cljs/cljs/spec/alpha.cljs | 22 +++++++++++----------- src/test/cljs/cljs/spec_test.cljs | 2 ++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index eed0f385e..fbab8ef7c 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -1368,37 +1368,37 @@ "takes a spec and returns a spec that has the same properties except 'conform' returns the original (not the conformed) value. Note, will specize regex ops." [spec] - (let [spec (specize spec)] + (let [spec (delay (specize spec))] (reify Specize (specize* [s] s) (specize* [s _] s) Spec - (conform* [_ x] (let [ret (conform* spec x)] + (conform* [_ x] (let [ret (conform* @spec x)] (if (invalid? ret) ::invalid x))) - (unform* [_ x] (unform* spec x)) - (explain* [_ path via in x] (explain* spec path via in x)) - (gen* [_ overrides path rmap] (gen* spec overrides path rmap)) - (with-gen* [_ gfn] (nonconforming (with-gen* spec gfn))) - (describe* [_] `(nonconforming ~(describe* spec)))))) + (unform* [_ x] (unform* @spec x)) + (explain* [_ path via in x] (explain* @spec path via in x)) + (gen* [_ overrides path rmap] (gen* @spec overrides path rmap)) + (with-gen* [_ gfn] (nonconforming (with-gen* @spec gfn))) + (describe* [_] `(nonconforming ~(describe* @spec)))))) (defn ^:skip-wiki nilable-impl "Do not call this directly, use 'nilable'" [form pred gfn] - (let [spec (specize pred form)] + (let [spec (delay (specize pred form))] (reify Specize (specize* [s] s) (specize* [s _] s) Spec - (conform* [_ x] (if (nil? x) nil (conform* spec x))) - (unform* [_ x] (if (nil? x) nil (unform* spec x))) + (conform* [_ x] (if (nil? x) nil (conform* @spec x))) + (unform* [_ x] (if (nil? x) nil (unform* @spec x))) (explain* [_ path via in x] - (when-not (c/or (pvalid? spec x) (nil? x)) + (when-not (c/or (pvalid? @spec x) (nil? x)) (conj (explain-1 form pred (conj path ::pred) via in x) {:path (conj path ::nil) :pred 'nil? :val x :via via :in in}))) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 8739f72ef..aa6097e4c 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -415,6 +415,8 @@ (is (nil? (baz-2793 1))) (is (nil? (quux-2793)))) +(s/def ::cljs-2940-foo (s/cat :bar (s/nilable ::cljs-2940-foo))) + (comment (run-tests) From 78a013a295442d272f3b6bbf2cf4f5b464ea222a Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 30 Oct 2018 12:41:09 -0400 Subject: [PATCH 3307/4033] CLJS-2951: Add a spec generator for some? --- src/main/cljs/cljs/spec/gen/alpha.cljs | 1 + src/test/cljs/cljs/spec_test.cljs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/gen/alpha.cljs b/src/main/cljs/cljs/spec/gen/alpha.cljs index 07af0ba05..65b0a87a4 100644 --- a/src/main/cljs/cljs/spec/gen/alpha.cljs +++ b/src/main/cljs/cljs/spec/gen/alpha.cljs @@ -89,6 +89,7 @@ gen-builtins (c/delay (let [simple (simple-type-printable)] {any? (one-of [(return nil) (any-printable)]) + some? (such-that some? (any-printable)) number? (one-of [(large-integer) (double)]) integer? (large-integer) int? (large-integer) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index aa6097e4c..fea726bd7 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -319,6 +319,7 @@ (s/def ::c keyword?) (s/def ::d double?) (s/def ::e inst?) + (s/def ::f some?) (is (= #{[::a] [::a ::b] @@ -356,7 +357,9 @@ [:a :c :e]} (->> (s/exercise (s/keys :req-un [::a (or ::b (and ::c (or ::d ::e)))]) 200) (map (comp vec sort keys first)) - (into #{}))))) + (into #{})))) + + (is (every? some? (map #(-> % first) (s/exercise ::f 10))))) (deftest tuple-explain-pred (are [val expected] From 20ba8ef9415b46c18172a59cfe63ad16d2a35a3c Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 30 Oct 2018 15:09:05 -0400 Subject: [PATCH 3308/4033] CLJS-2843: s/explain of evaluated predicate yields :s/unknown port CLJ-2068, currently won't work under advanced due to munging --- src/main/cljs/cljs/spec/alpha.cljs | 21 ++++++++++++++++++++- src/test/cljs/cljs/spec_test.cljs | 25 +++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index fbab8ef7c..0295fabd0 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -120,6 +120,14 @@ (when (ident? spec-or-k) (throw (js/Error. (str "Unable to resolve spec: " spec-or-k)))))) +(defn fn-sym [f-n] + (when-not (str/blank? f-n) + (let [xs (map demunge (str/split f-n "$"))] + (when (c/and (<= 2 (count xs)) + (every? #(not (str/blank? %)) xs)) + (let [[xs y] ((juxt butlast last) xs)] + (symbol (str (str/join "." xs) "/" y))))))) + (defprotocol Specize (specize* [_] [_ form])) @@ -132,9 +140,20 @@ (specize* ([s] (specize* (reg-resolve! s))) ([s _] (specize* (reg-resolve! s)))) + PersistentHashSet + (specize* ([s] (spec-impl s s nil nil)) + ([s form] (spec-impl form s nil nil))) + + PersistentTreeSet + (specize* ([s] (spec-impl s s nil nil)) + ([s form] (spec-impl form s nil nil))) + default (specize* - ([o] (spec-impl ::unknown o nil nil)) + ([o] + (if-let [f-n (c/and (fn? o) (fn-sym (.-name o)))] + (spec-impl f-n o nil nil) + (spec-impl ::unknown o nil nil))) ([o form] (spec-impl form o nil nil)))) (defn- specize diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index fea726bd7..4f9544239 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -136,6 +136,19 @@ lrange (s/int-in 7 42) drange (s/double-in :infinite? false :NaN? false :min 3.1 :max 3.2) irange (s/inst-in #inst "1939" #inst "1946")] + + (when-not js/COMPILED + ;; CLJS-2483: these won't work with both :advanced and :none optimization settings + (are [spec x conformed ed] + (let [co (s/conform spec x) + e (::s/problems (s/explain-data spec x))] + (when (not= conformed co) (println "conform fail\n\texpect=" conformed "\n\tactual=" co)) + (when (not (every? true? (map submap? ed e))) + (println "explain failures\n\texpect=" ed "\n\tactual failures=" e "\n\tsubmap?=" (map submap? ed e))) + (and (= conformed co) (every? true? (map submap? ed e)))) + keyword? nil ::s/invalid [{:pred `keyword? :val nil}] + keyword? "abc" ::s/invalid [{:pred `keyword? :val "abc"}])) + (are [spec x conformed ed] (let [co (s/conform spec x) e (::s/problems (s/explain-data spec x))] @@ -160,8 +173,6 @@ ;; drange Double/NaN ::s/invalid {[] {:pred '(not (isNaN %)), :val Double/NaN}} keyword? :k :k nil - keyword? nil ::s/invalid [{:pred ::s/unknown :val nil}] - keyword? "abc" ::s/invalid [{:pred ::s/unknown :val "abc"}] a 6 6 nil a 3 ::s/invalid '[{:pred (cljs.core/fn [%] (cljs.core/> % 5)), :val 3}] @@ -420,6 +431,16 @@ (s/def ::cljs-2940-foo (s/cat :bar (s/nilable ::cljs-2940-foo))) +(deftest describing-evaled-specs + (let [sp #{1 2}] + (is (= (s/describe sp) (s/form sp) sp))) + ;; won't work under advanced + (when-not js/COMPILED + (is (= (s/describe odd?) 'odd?)) + (is (= (s/form odd?) 'cljs.core/odd?))) + (is (= (s/describe #(odd? %)) ::s/unknown)) + (is (= (s/form #(odd? %)) ::s/unknown))) + (comment (run-tests) From 6b9a37a294746148d3f4f8c1b6839823fe6e23f3 Mon Sep 17 00:00:00 2001 From: Jordan Biserkov Date: Fri, 28 Sep 2018 19:37:01 +0300 Subject: [PATCH 3309/4033] CLJS-2728: Ability to disable macro spec checks --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- src/main/clojure/cljs/closure.clj | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 04b93ff07..c15513f30 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3523,11 +3523,12 @@ (defn- do-macroexpand-check [form mac-var] - (let [mchk #?(:clj (some-> (find-ns 'clojure.spec.alpha) + (when (not (-> @env/*compiler* :options :spec-skip-macros)) + (let [mchk #?(:clj (some-> (find-ns 'clojure.spec.alpha) (ns-resolve 'macroexpand-check)) :cljs (get-macroexpand-check-var))] (when (some? mchk) - (mchk mac-var (next form))))) + (mchk mac-var (next form)))))) (defn macroexpand-1* [env form] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0c541c4c3..1f5f49f5d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -202,7 +202,7 @@ :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs :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}) + :stable-names :ignore-js-module-exts :opts-cache :aot-cache :elide-strict :fingerprint :spec-skip-macros}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 From e78d4fc5b3cead6f4cec447c2daebda6eaa3489c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 1 Nov 2018 17:42:56 -0400 Subject: [PATCH 3310/4033] CLJS-2953: stest/with-instrument-disabled prints warning of private use --- src/main/cljs/cljs/spec/test/alpha.cljc | 8 ++++++-- src/test/cljs/cljs/spec/test_test.cljs | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 636db8e20..1fcf813d6 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -46,8 +46,12 @@ returns the set of all symbols naming vars in those nses." (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." [& body] - `(binding [*instrument-enabled* nil] - ~@body)) + `(let [orig# @#'*instrument-enabled*] + (set! *instrument-enabled* nil) + (try + ~@body + (finally + (set! *instrument-enabled* orig#))))) (defmacro instrument-1 [[quote s] opts] diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 0bf9294a5..1c31a34db 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -97,3 +97,15 @@ (stest/enumerate-namespace 'cljs.spec.test.test-ns1))) (is (= '#{cljs.spec.test.test-ns2/z} (stest/enumerate-namespace 'cljs.spec.test.test-ns2)))) + +(defn fn-2953 [x] ::ret-val) + +(s/fdef fn-2953 :args (s/cat :x int?)) + +(deftest test-cljs-2953 + (stest/instrument `fn-2953) + (is @#'stest/*instrument-enabled*) + (is (= ::ret-val (stest/with-instrument-disabled + (is (nil? @#'stest/*instrument-enabled*)) + (fn-2953 "abc")))) + (is @#'stest/*instrument-enabled*)) From 39f47c3b840815d338b27bd864fb33115e73c24a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 2 Nov 2018 17:37:36 -0400 Subject: [PATCH 3311/4033] 1.10.439 --- README.md | 6 ++--- changes.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 73cb5b959..6c294e264 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.339 +Latest stable release: 1.10.439 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.339"] +[org.clojure/clojurescript "1.10.439"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.339 org.clojure clojurescript - 1.10.339 + 1.10.439 ``` diff --git a/changes.md b/changes.md index 3e582c67e..72a6222a1 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,82 @@ +## 1.10.439 + +### Changes +* CLJS-2904: Default :npm-deps to false +* CLJS-2878: Update Closure Compiler to v20180805 +* CLJS-2827: Avoid var special in core macros for private var access +* CLJS-2819: Warn on non-dynamic earmuffed vars +* CLJS-2806: Bump test.check to 0.10.0-alpha3 +* CLJS-2815: Support string keys in :global-exports +* CLJS-2812: Support for overriding object printing +* CLJS-2805: Bump tools.reader to 1.3.0 +* CLJS-1702: Warning when using private vars +* Align ClojureScript AST to tools.analyzer + +### Enhancements +* CLJS-2903: Support fingerprinting +* CLJS-2897: cljs.main: Display initial REPL prompt sooner +* CLJS-2884: Support for GraalJS RC6 +* CLJS-2859: Graal.JS: Enable high-res timers by default, allow user-configuration +* CLJS-2831: Add a graaljs REPL environment +* CLJS-1997: Outward function type hint propagation +* CLJS-844: Optimize js->clj by switching to transients +* CLJS-2442: `set` and `vec` performance enhancements + +### Fixes +* CLJS-2953: stest/with-instrument-disabled prints warning of private use +* CLJS-2728: Ability to disable macro spec checks +* CLJS-2843: s/explain of evaluated predicate yields :s/unknown +* CLJS-2951: Add a spec generator for some? +* CLJS-2940: Can't define nilable spec on undefined pred +* CLJS-2948: Stack overflow calling instrumented variadic fn with zero args +* CLJS-2793: Instrumenting breaks function with varargs +* CLJS-2934: Enhanced delay printing +* CLJS-2864: Optimize str macro for single arity case +* CLJS-1297: defrecord does not emit IKVReduce protocol +* CLJS-2937: docstring for to-array +* CLJS-2943: Update merge-with to use key / val +* CLJS-2941: seqable? should return true for nil +* CLJS-2915: Tests fail if directory has a period (.) in the path +* CLJS-2782: lein test fails if directory has hyphens +* CLJS-2911: Avoid infinite loop on infinite partitions +* CLJS-2906: cljs.main: Crash when with default REPL +* CLJS-2883: Instrumentation fails compilation with a large number of spec'd functions +* CLJS-2896: Allow parallel analysis and compilation +* CLJS-2893: seq: use .-length instead of alength for strings +* CLJS-2890: fspec role in problem path is not useful +* CLJS-2887: Improve names in core macro specs +* CLJS-2891: stop including data in ex-info message +* CLJS-2888: Printing of spec problems buries the failing predicate which should be more prominent +* CLJS-2861: Self-host: :checked-arrays not working +* CLJS-2852: Clojure imparity: ns-publics returns different arglists for macros +* CLJS-2725: Doc on spec keywords +* CLJS-2665: Port clojure.spec.test.alpha/enumerate-namespace +* CLJS-2848: Default explain printer prints root val and spec +* CLJS-2846: [spec] s/tuple explain-data :pred problem +* CLJS-2847: s/coll-of and s/every gen is very slow if :kind specified without :into +* CLJS-2841: [spec] instrument exception doesn't contain function name in ex-data +* CLJS-2842: [spec] Clarify s/every docstring for :kind +* CLJS-2845: [spec] generate random subsets of or'd required keys in map specs +* CLJS-2844: [spec] Add support for undefining a spec +* CLJS-2840: [spec] s/keys explain-data :pred problem +* CLJS-2839: [spec] s/& explain-data :pred problem +* CLJS-2838: [spec] s/& does not check preds if regex matches empty collection +* CLJS-2837: [spec] `cat` specs should verify value is sequential +* CLJS-2541: binding not made in parallel +* CLJS-2832: Bad code gen for `((not empty?) "foo")` when compiled with no optimizations +* CLJS-2855: Browser REPL prints empty string after require +* CLJS-2821: Update doto docstring to not use Java example +* CLJS-2817: Suppress private var warnings for specs on private vars +* CLJS-2822: cljs.core.specs.alpha: Map bindings should be `:kind map?` +* CLJS-2829: Fix deep object property access for :global-exports +* CLJS-2816: Skip non-string package.json browser entry values +* CLJS-2814: Fix munge-node-lib/global-export on self-host +* CLJS-2811: cljs-1537-circular-deps fail on Windows +* CLJS-2807: Macroexpand failure with set literal +* CLJS-2799: Handle nth on seqables with negative indexes +* CLJS-2798: ChunkCons -next doesn't handle nil more +* CLJS-2589: allow / as a protocol method name in cljs + ## 1.10.339 ### Changes From 1d1488af14e76c0e73a889bca303bf2f5f79210e Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 20 Nov 2018 11:33:49 +0100 Subject: [PATCH 3312/4033] fix incorrect cljs.core.MapEntry usage --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 97b39389c..19fdee1b7 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1857,7 +1857,7 @@ (not-empty (dissoc ~'__extmap k#)) nil))) 'ISeqable - `(~'-seq [this#] (seq (concat [~@(map #(core/list 'cljs.core.MapEntry. (keyword %) % nil) base-fields)] + `(~'-seq [this#] (seq (concat [~@(map #(core/list 'cljs.core/MapEntry. (keyword %) % nil) base-fields)] ~'__extmap))) 'IIterable From 9250a578513efcd72aec6d80b56d02c313a1be33 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 20 Nov 2018 13:10:23 +0100 Subject: [PATCH 3313/4033] remove redundant exists? check in dynaload --- src/main/cljs/cljs/spec/gen/alpha.cljc | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/spec/gen/alpha.cljc b/src/main/cljs/cljs/spec/gen/alpha.cljc index 6df81798d..43da3842a 100644 --- a/src/main/cljs/cljs/spec/gen/alpha.cljc +++ b/src/main/cljs/cljs/spec/gen/alpha.cljc @@ -12,22 +12,15 @@ [clojure.string :as string])) (defmacro dynaload [[quote s]] - (let [xs (string/split (namespace s) #"\.") - cnt (count xs) - checks (map - (fn [n xs] - `(c/exists? ~(symbol (string/join "." (take n xs))))) - (range 2 cnt) - (repeat xs))] - `(cljs.spec.gen.alpha/LazyVar. - (fn [] - (if (and ~@checks (c/exists? ~s)) - ~(vary-meta s assoc :cljs.analyzer/no-resolve true) - (throw - (js/Error. - (str "Var " '~s " does not exist, " - (namespace '~s) " never required"))))) - nil))) + `(cljs.spec.gen.alpha/LazyVar. + (fn [] + (if (c/exists? ~s) + ~(vary-meta s assoc :cljs.analyzer/no-resolve true) + (throw + (js/Error. + (str "Var " '~s " does not exist, " + (namespace '~s) " never required"))))) + nil)) (defmacro delay "given body that returns a generator, returns a From c0f665a489e068590fb75622e4f8ef47706ff0aa Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 18 Nov 2018 19:42:37 +0100 Subject: [PATCH 3314/4033] CLJS-2979: re-seq is relying on undefined behavior of subs This commit makes re-seq only do a call to subs with defined values. It also optimizes the code. Tests for re-seq regarding CLJS-810 are provided as well. --- src/main/cljs/cljs/core.cljs | 22 ++++++++++++++++------ src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 733e0731e..6ca73661c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9845,15 +9845,25 @@ reduces them without incurring seq initialization" (vec matches)))) (throw (js/TypeError. "re-find must match against a string.")))) +(defn- re-seq* [re s] + (when-some [matches (.exec re s)] + (let [match-str (aget matches 0) + match-vals (if (== (.-length matches) 1) + match-str + (vec matches))] + (cons match-vals + (lazy-seq + (let [post-idx (+ (.-index matches) + (max 1 (.-length match-str)))] + (when (<= post-idx (.-length s)) + (re-seq* re (subs s post-idx))))))))) + (defn re-seq "Returns a lazy sequence of successive matches of re in s." [re s] - (let [match-data (re-find re s) - match-idx (.search s re) - match-str (if (coll? match-data) (first match-data) match-data) - post-idx (+ match-idx (max 1 (count match-str))) - post-match (subs s post-idx)] - (when match-data (lazy-seq (cons match-data (when (<= post-idx (count s)) (re-seq re post-match))))))) + (if (string? s) + (re-seq* re s) + (throw (js/TypeError. "re-seq must match against a string.")))) (defn re-pattern "Returns an instance of RegExp which has compiled the provided string." diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 501ec8080..643c41d8e 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -957,6 +957,10 @@ (is (every? #(= :failed (try (re-find #"nomatch" %) (catch js/TypeError _ :failed))) not-strings)) (is (every? #(= :failed (try (re-matches #"nomatch" %) + (catch js/TypeError _ :failed))) not-strings)) + (is (every? #(= :failed (try (re-seq #"." %) + (catch js/TypeError _ :failed))) not-strings)) + (is (every? #(= :failed (try (re-seq #"nomatch" %) (catch js/TypeError _ :failed))) not-strings))))) (deftest test-853 From 84faeec60554f8e2bc86aec23cb1a36daf0ed588 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 20 Jul 2018 21:42:54 -0400 Subject: [PATCH 3315/4033] CLJS-2825: Eliminate unnecessary ^boolean annotations --- src/main/cljs/cljs/core.cljs | 128 ++++++++++++------------- src/main/cljs/cljs/pprint.cljs | 2 +- src/test/cljs/cljs/inference_test.cljs | 80 ++++++++++++++++ src/test/cljs/cljs/inference_util.clj | 29 ++++++ src/test/cljs/test_runner.cljs | 2 + src/test/self/self_parity/test.cljs | 2 + 6 files changed, 178 insertions(+), 65 deletions(-) create mode 100644 src/test/cljs/cljs/inference_test.cljs create mode 100644 src/test/cljs/cljs/inference_util.clj diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6ca73661c..12367ebf4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -243,7 +243,7 @@ [x] (cljs.core/number? x)) -(defn ^boolean not +(defn not "Returns true if x is logical false, false otherwise." [x] (cond @@ -255,7 +255,7 @@ "Returns true if x is not nil, false otherwise." [x] (not (nil? x))) -(defn ^boolean object? +(defn object? "Returns true if x's constructor is Object" [x] (if-not (nil? x) @@ -267,17 +267,17 @@ [x] (goog/isString x)) -(defn ^boolean char? +(defn char? "Returns true if x is a JavaScript string of length one." [x] (and (string? x) (== 1 (.-length x)))) -(defn ^boolean any? +(defn any? "Returns true if given any argument." [x] true) (set! *unchecked-if* true) -(defn ^boolean native-satisfies? +(defn native-satisfies? "Internal - do not use!" [p x] (let [x (if (nil? x) nil x)] @@ -1175,7 +1175,7 @@ (-invoke [_ a b c d e f g h i j k l m n o p q r s t rest] (apply (val) a b c d e f g h i j k l m n o p q r s t rest))) -(defn ^boolean var? +(defn var? "Returns true if v is of type cljs.core.Var" [v] (instance? cljs.core.Var v)) @@ -1184,7 +1184,7 @@ (declare array-seq prim-seq IndexedSeq) -(defn ^boolean iterable? +(defn iterable? "Return true if x implements IIterable protocol." [x] (satisfies? IIterable x)) @@ -1194,7 +1194,7 @@ [value] (-clone value)) -(defn ^boolean cloneable? +(defn cloneable? "Return true if x implements ICloneable protocol." [value] (satisfies? ICloneable value)) @@ -1386,7 +1386,7 @@ [inst] (inst-ms* inst)) -(defn ^boolean inst? +(defn inst? "Return true if x satisfies Inst" [x] (satisfies? Inst x)) @@ -1423,7 +1423,7 @@ [x] (Reduced. x)) -(defn ^boolean reduced? +(defn reduced? "Returns true if x is the result of a call to reduced" [r] (instance? Reduced r)) @@ -1514,11 +1514,11 @@ reduces them without incurring seq initialization" (declare hash-coll cons drop count nth RSeq List) -(defn ^boolean counted? +(defn counted? "Returns true if coll implements count in constant time" [x] (satisfies? ICounted x)) -(defn ^boolean indexed? +(defn indexed? "Returns true if coll implements nth in constant time" [x] (satisfies? IIndexed x)) @@ -1991,7 +1991,7 @@ reduces them without incurring seq initialization" (recur ret (first ks) (next ks)) ret))))) -(defn ^boolean fn? +(defn fn? "Return true if f is a JavaScript function or satisfies the Fn protocol." [f] (or ^boolean (goog/isFunction f) (satisfies? Fn f))) @@ -2094,65 +2094,65 @@ reduces them without incurring seq initialization" (recur ret (first ks) (next ks)) ret))))) -(defn ^boolean empty? +(defn empty? "Returns true if coll has no items - same as (not (seq coll)). Please use the idiom (seq x) rather than (not (empty? x))" [coll] (or (nil? coll) (not (seq coll)))) -(defn ^boolean coll? +(defn coll? "Returns true if x satisfies ICollection" [x] (if (nil? x) false (satisfies? ICollection x))) -(defn ^boolean set? +(defn set? "Returns true if x satisfies ISet" [x] (if (nil? x) false (satisfies? ISet x))) -(defn ^boolean associative? +(defn associative? "Returns true if coll implements IAssociative" [x] (satisfies? IAssociative x)) -(defn ^boolean ifind? +(defn ifind? "Returns true if coll implements IFind" [x] (satisfies? IFind x)) -(defn ^boolean sequential? +(defn sequential? "Returns true if coll satisfies ISequential" [x] (satisfies? ISequential x)) -(defn ^boolean sorted? +(defn sorted? "Returns true if coll satisfies ISorted" [x] (satisfies? ISorted x)) -(defn ^boolean reduceable? +(defn reduceable? "Returns true if coll satisfies IReduce" [x] (satisfies? IReduce x)) -(defn ^boolean map? +(defn map? "Return true if x satisfies IMap" [x] (if (nil? x) false (satisfies? IMap x))) -(defn ^boolean record? +(defn record? "Return true if x satisfies IRecord" [x] (satisfies? IRecord x)) -(defn ^boolean vector? +(defn vector? "Return true if x satisfies IVector" [x] (satisfies? IVector x)) (declare ChunkedCons ChunkedSeq) -(defn ^boolean chunked-seq? +(defn chunked-seq? "Return true if x is satisfies IChunkedSeq." [x] (implements? IChunkedSeq x)) @@ -2204,7 +2204,7 @@ reduces them without incurring seq initialization" "Returns true if x is the value true, false otherwise." [x] (cljs.core/true? x)) -(defn ^boolean boolean? +(defn boolean? "Return true if x is a Boolean" [x] (or (cljs.core/true? x) (cljs.core/false? x))) @@ -2213,14 +2213,14 @@ reduces them without incurring seq initialization" [x] (cljs.core/undefined? x)) -(defn ^boolean seq? +(defn seq? "Return true if s satisfies ISeq" [s] (if (nil? s) false (satisfies? ISeq s))) -(defn ^boolean seqable? +(defn seqable? "Return true if the seq function is supported for s" [s] (or @@ -2229,7 +2229,7 @@ reduces them without incurring seq initialization" (array? s) (string? s))) -(defn ^boolean boolean +(defn boolean "Coerce to boolean" [x] (cond @@ -2237,12 +2237,12 @@ reduces them without incurring seq initialization" (false? x) false :else true)) -(defn ^boolean ifn? +(defn ifn? "Returns true if f returns true for fn? or satisfies IFn." [f] (or (fn? f) (satisfies? IFn f))) -(defn ^boolean integer? +(defn integer? "Returns true if n is a JavaScript number with no decimal part." [n] (and (number? n) @@ -2250,7 +2250,7 @@ reduces them without incurring seq initialization" (not (identical? n js/Infinity)) (== (js/parseFloat n) (js/parseInt n 10)))) -(defn ^boolean int? +(defn int? "Return true if x satisfies integer? or is an instance of goog.math.Integer or goog.math.Long." [x] @@ -2258,7 +2258,7 @@ reduces them without incurring seq initialization" (instance? goog.math.Integer x) (instance? goog.math.Long x))) -(defn ^boolean pos-int? +(defn pos-int? "Return true if x satisfies int? and is positive." [x] (cond @@ -2288,7 +2288,7 @@ reduces them without incurring seq initialization" :else false)) -(defn ^boolean nat-int? +(defn nat-int? "Return true if x satisfies int? and is a natural integer value." [x] (cond @@ -2303,23 +2303,23 @@ reduces them without incurring seq initialization" :else false)) -(defn ^boolean float? +(defn float? "Returns true for JavaScript numbers, false otherwise." [x] (number? x)) -(defn ^boolean double? +(defn double? "Returns true for JavaScript numbers, false otherwise." [x] (number? x)) -(defn ^boolean infinite? +(defn infinite? "Returns true for Infinity and -Infinity values." [x] (or (identical? x js/Number.POSITIVE_INFINITY) (identical? x js/Number.NEGATIVE_INFINITY))) -(defn ^boolean contains? +(defn 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 @@ -3086,7 +3086,7 @@ reduces them without incurring seq initialization" (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) -(defn ^boolean list? +(defn list? "Returns true if x implements IList" [x] (satisfies? IList x)) @@ -3161,7 +3161,7 @@ reduces them without incurring seq initialization" (es6-iterable EmptyList) -(defn ^boolean reversible? +(defn reversible? "Returns true if coll satisfies? IReversible." [coll] (satisfies? IReversible coll)) @@ -3304,12 +3304,12 @@ reduces them without incurring seq initialization" IPrintWithWriter (-pr-writer [o writer _] (-write writer (str ":" fqn)))) -(defn ^boolean keyword? +(defn keyword? "Return true if x is a Keyword" [x] (instance? Keyword x)) -(defn ^boolean keyword-identical? +(defn keyword-identical? "Efficient test to determine that two keywords are identical." [x y] (if (identical? x y) @@ -3318,7 +3318,7 @@ reduces them without incurring seq initialization" (identical? (.-fqn x) (.-fqn y)) false))) -(defn ^boolean symbol-identical? +(defn symbol-identical? "Efficient test to determine that two symbols are identical." [x y] (if (identical? x y) @@ -3334,31 +3334,31 @@ reduces them without incurring seq initialization" (-namespace ^not-native x) (throw (js/Error. (str "Doesn't support namespace: " x))))) -(defn ^boolean ident? +(defn ident? "Return true if x is a symbol or keyword" [x] (or (keyword? x) (symbol? x))) -(defn ^boolean simple-ident? +(defn simple-ident? "Return true if x is a symbol or keyword without a namespace" [x] (and (ident? x) (nil? (namespace x)))) -(defn ^boolean qualified-ident? +(defn qualified-ident? "Return true if x is a symbol or keyword with a namespace" [x] (boolean (and (ident? x) (namespace x) true))) -(defn ^boolean simple-symbol? +(defn simple-symbol? "Return true if x is a symbol without a namespace" [x] (and (symbol? x) (nil? (namespace x)))) -(defn ^boolean qualified-symbol? +(defn qualified-symbol? "Return true if x is a symbol with a namespace" [x] (boolean (and (symbol? x) (namespace x) true))) -(defn ^boolean simple-keyword? +(defn simple-keyword? "Return true if x is a keyword without a namespace" [x] (and (keyword? x) (nil? (namespace x)))) -(defn ^boolean qualified-keyword? +(defn qualified-keyword? "Return true if x is a keyword with a namespace" [x] (boolean (and (keyword? x) (namespace x) true))) @@ -4179,7 +4179,7 @@ reduces them without incurring seq initialization" (.createMulti TransformerIterator xform (map iter (cons coll colls)))) ()))) -(defn ^boolean every? +(defn every? "Returns true if (pred x) is logical true for every x in coll, else false." [pred coll] @@ -4188,7 +4188,7 @@ reduces them without incurring seq initialization" (pred (first coll)) (recur pred (next coll)) :else false)) -(defn ^boolean not-every? +(defn not-every? "Returns false if (pred x) is logical true for every x in coll, else true." [pred coll] (not (every? pred coll))) @@ -4202,18 +4202,18 @@ reduces them without incurring seq initialization" (when (seq coll) (or (pred (first coll)) (recur pred (next coll))))) -(defn ^boolean not-any? +(defn not-any? "Returns false if (pred x) is logical true for any x in coll, else true." [pred coll] (not (some pred coll))) -(defn ^boolean even? +(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))))) -(defn ^boolean odd? +(defn odd? "Returns true if n is odd, throws an exception if n is not an integer" [n] (not (even? n))) @@ -4532,7 +4532,7 @@ reduces them without incurring seq initialization" [val] (Volatile. val)) -(defn ^boolean volatile? +(defn volatile? "Returns true if x is a volatile." [x] (instance? Volatile x)) @@ -6267,7 +6267,7 @@ reduces them without incurring seq initialization" (def ^:private never-equiv (NeverEquiv.)) -(defn ^boolean equiv-map +(defn equiv-map "Test map equivalence. Returns true if x equals y, otherwise returns false." [x y] (boolean @@ -6662,7 +6662,7 @@ reduces them without incurring seq initialization" (-invoke [node k not-found] (-nth node k not-found))) -(defn ^boolean map-entry? +(defn map-entry? "Returns true if x satisfies IMapEntry" [x] (implements? IMapEntry x)) @@ -7060,7 +7060,7 @@ reduces them without incurring seq initialization" (declare create-inode-seq create-array-node-seq reset! create-node atom deref) -(defn ^boolean key-test [key other] +(defn key-test [key other] (cond (identical? key other) true (keyword-identical? key other) true @@ -9814,7 +9814,7 @@ reduces them without incurring seq initialization" ;;;;;;;;;;;;;;;;;;;;;;;;; Regular Expressions ;;;;;;;;;; -(defn ^boolean regexp? +(defn regexp? "Returns true if x is a JavaScript RegExp instance." [x] (instance? js/RegExp x)) @@ -9931,7 +9931,7 @@ reduces them without incurring seq initialization" (declare print-map) -(defn ^boolean print-meta? [opts obj] +(defn print-meta? [opts obj] (and (boolean (get opts :meta)) (implements? IMeta obj) (not (nil? (meta obj))))) @@ -10413,7 +10413,7 @@ reduces them without incurring seq initialization" (pr-writer {:status (if (nil? f) :ready :pending), :val value} writer opts) (-write writer "]"))) -(defn ^boolean delay? +(defn delay? "returns true if x is a Delay created with delay" [x] (instance? Delay x)) @@ -11143,7 +11143,7 @@ reduces them without incurring seq initialization" (hex) (hex) (hex) (hex) (hex) (hex) (hex) (hex)))))) -(defn ^boolean uuid? +(defn uuid? [x] (implements? IUUID x)) ;;; ExceptionInfo @@ -11271,7 +11271,7 @@ reduces them without incurring seq initialization" (-write writer (str "#" tag " ")) (pr-writer form writer opts))) -(defn ^boolean tagged-literal? +(defn tagged-literal? "Return true if the value is the data representation of a tagged literal" [value] (instance? TaggedLiteral value)) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 9335ea85f..fb349a1fa 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -51,7 +51,7 @@ ;; cljs specific utils ;;====================================================================== -(defn ^boolean float? +(defn float? "Returns true if n is an float." [n] (and (number? n) diff --git a/src/test/cljs/cljs/inference_test.cljs b/src/test/cljs/cljs/inference_test.cljs new file mode 100644 index 000000000..e456677e3 --- /dev/null +++ b/src/test/cljs/cljs/inference_test.cljs @@ -0,0 +1,80 @@ +;; 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.inference-test + (:require-macros [cljs.inference-util]) + (:require [cljs.test :refer [deftest]] + [cljs.pprint])) + +(deftest test-cljs-2825 + (cljs.inference-util/truth_-not-called? + (not nil) + (object? nil) + (char? nil) + (any? nil) + (native-satisfies? ICounted nil) + (var? nil) + (iterable? nil) + (cloneable? nil) + (inst? nil) + (reduced? nil) + (counted? nil) + (indexed? nil) + (fn? nil) + (empty? nil) + (coll? nil) + (set? nil) + (associative? nil) + (ifind? nil) + (sequential? nil) + (sorted? nil) + (reduceable? nil) + (map? nil) + (record? nil) + (vector? nil) + (chunked-seq? nil) + (boolean? nil) + (seq? nil) + (seqable? nil) + (boolean nil) + (ifn? nil) + (integer? nil) + (int? nil) + (pos-int? nil) + (nat-int? nil) + (float? nil) + (double? nil) + (infinite? nil) + (contains? [] nil) + (list? nil) + (reversible? nil) + (keyword? nil) + (keyword-identical? :a :a) + (symbol-identical? 'a 'a) + (ident? nil) + (simple-ident? nil) + (qualified-ident? nil) + (simple-symbol? nil) + (qualified-symbol? nil) + (simple-keyword? nil) + (qualified-keyword? nil) + (every? any? []) + (not-every? any? []) + (not-any? any? []) + (even? 0) + (odd? 0) + (volatile? nil) + (equiv-map {} {}) + (map-entry? nil) + (key-test :a :a) + (regexp? nil) + (print-meta? {} nil) + (delay? nil) + (uuid? nil) + (tagged-literal? nil) + (cljs.pprint/float? nil))) diff --git a/src/test/cljs/cljs/inference_util.clj b/src/test/cljs/cljs/inference_util.clj new file mode 100644 index 000000000..b2b2ef9f1 --- /dev/null +++ b/src/test/cljs/cljs/inference_util.clj @@ -0,0 +1,29 @@ +;; 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.inference-util) + +(defmacro truth_-called? + "Returns whether cljs.core/truth_ is called when evaluating a form as the + test of an if." + [form] + `(let [called?# (volatile! false)] + (with-redefs [cljs.core/truth_ (fn [x#] + (vreset! called?# true) + (cljs.core/truth_ x#))] + (if ~form 1 2)) + @called?#)) + +(defmacro truth_-not-called? + "Returns whether cljs.core/truth_ is not called when evaluating ecah of + forms as the test of an if." + [& forms] + `(do + ~@(map (fn [form] + `(cljs.test/is (not (truth_-called? ~form)))) + forms))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 088cde639..649c104b5 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -46,6 +46,7 @@ [static.core-test] [cljs.recur-test] [cljs.array-access-test] + [cljs.inference-test] [cljs.extend-to-native-test])) (set! *print-newline* false) @@ -90,4 +91,5 @@ 'static.core-test 'cljs.recur-test 'cljs.array-access-test + 'cljs.inference-test 'cljs.extend-to-native-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 0bb102052..1be98f119 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -311,6 +311,7 @@ [static.core-test] [cljs.recur-test] [cljs.array-access-test] + [cljs.inference-test] [cljs.extend-to-native-test])) (fn [{:keys [value error]}] (if error @@ -354,6 +355,7 @@ 'static.core-test 'cljs.recur-test 'cljs.array-access-test + 'cljs.inference-test 'cljs.extend-to-native-test) (fn [{:keys [value error]}] (when error From 0cba37107bfaf72816d115ed4fab1908572c658e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 15 Nov 2018 13:42:32 -0500 Subject: [PATCH 3316/4033] CLJS-2974: empty for non-emptyable should return nil --- src/main/cljs/cljs/core.cljs | 9 ++++++++- src/test/cljs/cljs/extend_to_native_test.cljs | 8 ++++++++ src/test/cljs/cljs/seqs_test.cljs | 6 +++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 12367ebf4..c8b445b7b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1789,7 +1789,14 @@ reduces them without incurring seq initialization" "Returns an empty collection of the same category as coll, or nil" [coll] (when-not (nil? coll) - (-empty coll))) + (cond + (implements? IEmptyableCollection coll) + (-empty ^not-native coll) + + (satisfies? IEmptyableCollection coll) + (-empty coll) + + :else nil))) (defn- accumulating-seq-count [coll] (loop [s (seq coll) acc 0] diff --git a/src/test/cljs/cljs/extend_to_native_test.cljs b/src/test/cljs/cljs/extend_to_native_test.cljs index b29af3eb2..52662f62a 100644 --- a/src/test/cljs/cljs/extend_to_native_test.cljs +++ b/src/test/cljs/cljs/extend_to_native_test.cljs @@ -62,3 +62,11 @@ string (-pr-writer [obj writer _] (write-all writer obj)))) + +(deftest test-cljs-2974 + (extend-protocol IEmptyableCollection + array + (-empty [_] #js [])) + (let [empty-array (empty #js [1 2 3])] + (is (and (array? empty-array) + (empty? empty-array))))) diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 8a74a66c8..94ca0e20f 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -86,7 +86,11 @@ (testing "queue" (is (identical? (type e-queue) PersistentQueue)) (is (empty? e-queue)) - (is (= {:b :c} (meta e-queue))))))) + (is (= {:b :c} (meta e-queue))))) + (testing "non-emptyable" + (is (nil? (empty 1))) + (is (nil? (empty "abc"))) + (is (nil? (empty #js [1 2 3])))))) (deftest test-distinct (testing "Testing distinct? & distinct" From 7d3b94de959cafc3e56be5b869c57977119c51f3 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Wed, 21 Nov 2018 14:52:30 +0100 Subject: [PATCH 3317/4033] CLJS-2975: unstrument returns symbol of non-instrumented var Fixed by checking if var is spec'ed in stest/instrument. --- src/main/cljs/cljs/spec/test/alpha.cljc | 16 ++++++++++------ src/test/cljs/cljs/spec/test_test.cljs | 10 +++++++++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 1fcf813d6..e74a3ad3a 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -56,12 +56,16 @@ returns the set of all symbols naming vars in those nses." (defmacro instrument-1 [[quote s] opts] (when-let [v (ana-api/resolve &env s)] - (when (and (nil? (:const v)) - #?(:cljs (nil? (:macro v)))) - (swap! instrumented-vars conj (:name v)) - `(let [checked# (#'instrument-1* '~s (var ~s) ~opts)] - (when checked# (set! ~s checked#)) - '~(:name v))))) + (let [var-name (:name v)] + (when (and (nil? (:const v)) + #?(:cljs (nil? (:macro v))) + (contains? #?(:clj (s/speced-vars) + :cljs (cljs.spec.alpha$macros/speced-vars)) + var-name)) + (swap! instrumented-vars conj var-name) + `(let [checked# (#'instrument-1* '~s (var ~s) ~opts)] + (when checked# (set! ~s checked#)) + '~var-name))))) (defmacro unstrument-1 [[quote s]] diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 1c31a34db..8334cd8ae 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -1,6 +1,7 @@ (ns cljs.spec.test-test (:require-macros [cljs.spec.test.test-macros]) - (:require [cljs.test :as test :refer-macros [deftest is are run-tests]] + (:require [cljs.test :as test :refer-macros [deftest testing + is are run-tests]] [cljs.spec.alpha :as s] [cljs.spec.test.alpha :as stest] [cljs.spec.test.test-ns1] @@ -109,3 +110,10 @@ (is (nil? @#'stest/*instrument-enabled*)) (fn-2953 "abc")))) (is @#'stest/*instrument-enabled*)) + +(defn fn-2975 [x]) + +(deftest test-2975 + (testing "instrument and unstrument return empty coll when no fdef exists" + (is (empty? (stest/instrument `fn-2975))) + (is (empty? (stest/unstrument `fn-2975))))) From b2f01f5bcfc6cbf54d6d8d96463f68ad6e8f4979 Mon Sep 17 00:00:00 2001 From: Martin Kucera Date: Thu, 6 Sep 2018 14:55:10 +0200 Subject: [PATCH 3318/4033] CLJS-2867: Inferred return type of namespace is string Corrected type inference of the INamed protocol method namespace to {string clj-nil}. --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c8b445b7b..dcfd9d81b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -840,7 +840,7 @@ "Protocol for adding a name." (^string -name [x] "Returns the name String of x.") - (^string -namespace [x] + ( ^{:tag #{string clj-nil}}-namespace [x] "Returns the namespace String of x.")) (defprotocol IAtom From dc18ed22f11c688c3e390acf887d88005221075a Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 22 Nov 2018 18:06:48 +0100 Subject: [PATCH 3319/4033] CLJS-2901: Return type inference for multi-arity & variadic fns For the anonymous fn case simply handle the tagging of :methods in cljs.analyzer/infer-type. This is enough for local fn invoke to be be inferred correctly. The top-level case is a bit trickier due to the fact that defn is a ClojureScript macro that desugars before the compiler to ensure that Closure cross module code motion can work. In this case add a special case to cljs.analyzer/parse 'set!. If we have a top level fn (:top-fn), use its metadata to recover the original fn information pre-desugaring. :tag information comes from analyzing the body. cljs.analyzer/infer-invoke has been changed to prefer inference over :ret-tag. In all cases we make sure enough `:methods` information is available for infer-invoke to work. Tweak cljs.analyzer/analyze-form-seq so that it's a bit more useful under testing. Add basic tests cases for the above as well as bringing back a large number of inference tests that were commented out. --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/analyzer.cljc | 61 +++-- src/main/clojure/cljs/core.cljc | 32 ++- src/test/clojure/cljs/analyzer_tests.clj | 281 +++++++++++++++++------ 4 files changed, 272 insertions(+), 104 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c8b445b7b..4dfc5310f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1817,7 +1817,7 @@ reduces them without incurring seq initialization" (alength coll) (string? coll) - (.-length coll) + ^number (.-length coll) (implements? ISeqable coll) (accumulating-seq-count coll) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c15513f30..230c14377 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1346,16 +1346,15 @@ else-tag #{else-tag})] (into then-tag else-tag)))))))) -(defn infer-invoke [env e] - (let [{info :info :as f} (:fn e)] - (if-some [ret-tag (if (or (true? (:fn-var info)) - (true? (:js-fn-var info))) - (:ret-tag info) - (when (= 'js (:ns info)) 'js))] +(defn infer-invoke [env {f :fn :keys [args] :as e}] + (let [me (assoc (find-matching-method f args) :op :fn-method)] + (if-some [ret-tag (infer-tag env me)] ret-tag - (let [args (:args e) - me (assoc (find-matching-method f args) :op :fn-method)] - (if-some [ret-tag (infer-tag env me)] + (let [{:keys [info]} f] + (if-some [ret-tag (if (or (true? (:fn-var info)) + (true? (:js-fn-var info))) + (:ret-tag info) + (when (= 'js (:ns info)) 'js))] ret-tag ANY_SYM))))) @@ -2101,6 +2100,8 @@ ;; TODO: can we simplify - David (merge be {:fn-var true + ;; copy over the :fn-method information we need for invoke type inference + :methods (into [] (map #(select-keys % [:tag :fixed-arity :variadic?]) (:methods init-expr))) :variadic? (:variadic? init-expr) :max-fixed-arity (:max-fixed-arity init-expr) :method-params (map :params (:methods init-expr))}) @@ -2267,6 +2268,20 @@ (when (:field texpr) texpr)))) vexpr (analyze enve val)] + ;; as top level fns are decomposed for Closure cross-module code motion, we need to + ;; restore their :methods information + (when (seq? target) + (let [sym (some-> target second) + meta (meta sym)] + (when-let [info (and (= :fn (:op vexpr)) (:top-fn meta))] + (swap! env/*compiler* update-in + [::namespaces (-> env :ns :name) :defs sym :methods] + (fnil conj []) + ;; just use original fn meta, as the fn method is already desugared + ;; only get tag from analysis + (merge + (select-keys info [:fixed-arity :variadic?]) + (select-keys (-> vexpr :methods first) [:tag])))))) (when-not texpr (throw (error env "set! target must be a field or a symbol naming a var"))) (cond @@ -3723,12 +3738,18 @@ :meta meta-expr :expr expr :children [:meta :expr]}) expr))) -(defn infer-type [env ast _] - (if (nil? (:tag ast)) +(defn infer-type [env {:keys [tag] :as ast} _] + (if (or (nil? tag) (= 'function tag)) + ;; infer-type won't get a chance to process :methods + ;; so treat :fn as a special case for now, could probably + ;; fix up to use :children to walk child nodes + (if (= :fn (:op ast)) + (update ast :methods + (fn [ms] (into [] (map #(infer-type env % _)) ms))) (if-some [tag (infer-tag env ast)] - (assoc ast :tag tag) - ast) - ast)) + (assoc ast :tag tag) + ast)) + ast)) (defn- repl-self-require? [env deps] (and (:repl-env env) (some #{*cljs-ns*} deps))) @@ -4305,6 +4326,8 @@ (when env/*compiler* (:options @env/*compiler*)))) ([forms opts] + (analyze-form-seq forms opts false)) + ([forms opts return-last?] (let [env (assoc (empty-env) :build-options opts)] (binding [*file-defs* nil #?@(:clj [*unchecked-if* false @@ -4312,15 +4335,17 @@ *cljs-ns* 'cljs.user *cljs-file* nil reader/*alias-map* (or reader/*alias-map* {})] - (loop [ns nil forms forms] + (loop [ns nil forms forms last-ast nil] (if (some? forms) (let [form (first forms) env (assoc env :ns (get-namespace *cljs-ns*)) ast (analyze env form nil opts)] (if (= (:op ast) :ns) - (recur (:name ast) (next forms)) - (recur ns (next forms)))) - ns)))))) + (recur (:name ast) (next forms) ast) + (recur ns (next forms) ast))) + (if return-last? + last-ast + ns))))))) (defn ensure-defs "Ensures that a non-nil defs map exists in the compiler state for a given diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 19fdee1b7..92da5abd5 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3055,7 +3055,8 @@ `[(set! (. ~sym ~'-cljs$lang$maxFixedArity) ~(core/dec (count sig)))]) (js-inline-comment " @this {Function} ") - (set! (. ~sym ~'-cljs$lang$applyTo) + ;; dissoc :top-fn so this helper gets ignored in cljs.analyzer/parse 'set! + (set! (. ~(vary-meta sym dissoc :top-fn) ~'-cljs$lang$applyTo) ~(apply-to))))))) (core/defmacro copy-arguments [dest] @@ -3080,15 +3081,18 @@ sig (remove '#{&} arglist) c-1 (core/dec (count sig)) macro? (:macro meta) + mfa (core/cond-> c-1 macro? (core/- 2)) meta (assoc meta :top-fn {:variadic? true - :max-fixed-arity (core/cond-> c-1 macro? (core/- 2)) + :fixed-arity mfa + :max-fixed-arity mfa :method-params (core/cond-> [sig] macro? elide-implicit-macro-args) :arglists (core/cond-> (core/list arglist) macro? elide-implicit-macro-args) - :arglists-meta (doall (map meta [arglist]))})] + :arglists-meta (doall (map meta [arglist]))}) + name (with-meta name meta)] `(do - (def ~(with-meta name meta) + (def ~name (fn [~'var_args] (let [args# (array)] (copy-arguments args#) @@ -3096,7 +3100,7 @@ (new ^::ana/no-resolve cljs.core/IndexedSeq (.slice args# ~c-1) 0 nil))] (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args c-1) argseq#)))))) - ~(variadic-fn* rname method) + ~(variadic-fn* name method) ~(core/when emit-var? `(var ~name)))))) (core/comment @@ -3117,11 +3121,14 @@ (~(symbol (core/str "cljs$core$IFn$_invoke$arity$" c)) ~@(dest-args c)))])) - (fn-method [[sig & body :as method]] + (fn-method [name [sig & body :as method]] (if (some '#{&} sig) (variadic-fn* name method false) + ;; fix up individual :fn-method meta for + ;; cljs.analyzer/parse 'set! :top-fn handling `(set! - (. ~name + (. ~(vary-meta name update :top-fn merge + {:variadic? false :fixed-arity (count sig)}) ~(symbol (core/str "-cljs$core$IFn$_invoke$arity$" (count sig)))) (fn ~method))))] @@ -3135,19 +3142,22 @@ (map count sigs) [(core/- (count (first (filter varsig? arglists))) 2)])) macro? (:macro meta) + mfa (core/cond-> maxfa macro? (core/- 2)) meta (assoc meta :top-fn {:variadic? variadic - :max-fixed-arity (core/cond-> maxfa macro? (core/- 2)) + :fixed-arity mfa + :max-fixed-arity mfa :method-params (core/cond-> sigs macro? elide-implicit-macro-args) :arglists (core/cond-> arglists macro? elide-implicit-macro-args) :arglists-meta (doall (map meta arglists))}) args-sym (gensym "args") - param-counts (map count arglists)] + param-counts (map count arglists) + name (with-meta name meta)] (core/when (not= (distinct param-counts) param-counts) (ana/warning :overload-arity {} {:name name})) `(do - (def ~(with-meta name meta) + (def ~name (fn [~'var_args] (case (alength (js-arguments)) ~@(mapcat #(fixed-arity rname %) sigs) @@ -3165,7 +3175,7 @@ (str "Invalid arity: " (- (alength (js-arguments)) 2)))) `(throw (js/Error. (str "Invalid arity: " (alength (js-arguments)))))))))) - ~@(map fn-method fdecl) + ~@(map #(fn-method name %) fdecl) ;; optimization properties (set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa) ~(core/when emit-var? `(var ~name)))))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 974762b7e..2aed399aa 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -236,58 +236,191 @@ (:tag (analyze test-env '(.foo js/bar)))) 'js))) +(deftest fn-method-inference + ;; should always infer 'function as tag + (is (= 'function + (:tag + (e/with-compiler-env test-cenv + (analyze test-env + '(fn ([a] 1) ([a b] "foo") ([a b & r] ()))))))) + (is (nil? + (:ret-tag + (e/with-compiler-env test-cenv + (analyze test-env + '(fn ([a] 1) ([a b] "foo") ([a b & r] ())))))) ) + ;; methods should have inferred types + (is (= '(number string cljs.core/IList) + (map :tag + (:methods + (e/with-compiler-env test-cenv + (analyze test-env + '(fn ([a] 1) ([a b] "foo") ([a b & r] ()))))))))) + (deftest fn-inference - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env - ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - ; (x :one))))) - ; 'number)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env - ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - ; (x :one :two))))) - ; 'string)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env - ; '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - ; (x :one :two :three))))) - ; 'cljs.core/IList)) + (is (= 'number + (e/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + (x :one))))))) + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + (x :one :two))))))) + (is (= 'cljs.core/IList + (e/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + (x :one :two :three))))))) + (is (= 'cljs.core/IList + (e/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + (x :one :two :three :four)))))))) + +(deftest top-fn-inference + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-2901) + (defn foo + ([a] 1) + ([a b] "foo") + ([a b & r] ())) + (foo :one)])) + (is (= '[number string cljs.core/IList] + (map :tag + (get-in @test-cenv [::a/namespaces 'test.cljs-2901 :defs 'foo :methods])))) + (is (= 'number + (:tag + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-2901) + (defn foo + ([a] 1) + ([a b] "foo") + ([a b & r] ())) + (foo :one)] + nil true))))) + (is (= 'string + (:tag + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-2901) + (defn foo + ([a] 1) + ([a b] "foo") + ([a b & r] ())) + (foo :one :two)] + nil true))))) + (is (= 'cljs.core/IList + (:tag + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-2901) + (defn foo + ([a] 1) + ([a b] "foo") + ([a b & r] ())) + (foo :one :two :three)] + nil true)))))) + +(deftest variadic-fn-inference + (is (= '(cljs.core/IList) + (map :tag + (:methods + (e/with-compiler-env test-cenv + (analyze test-env + '(fn ([a b & r] ())))))))) + (is (= 'cljs.core/IList + (e/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a b & r] ()))] + (x :one :two))))))) + + (is (= 'cljs.core/IList + (e/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a b & r] ()))] + (x :one :two :three))))))) + + (is (= 'cljs.core/IList + (e/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a b & r] ()))] + (x :one :two :three :four))))))) ) +(deftest top-variadic-fn-inference + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-2901-b) + (defn foo ([a b & r] ())) + (foo :one :two :three :four)] + nil false)) + (is (= '[cljs.core/IList] + (map :tag + (get-in @test-cenv + [::a/namespaces 'test.cljs-2901-b :defs 'foo :methods])))) + (is (= 'cljs.core/IList + (:tag + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-2901-b) + (defn foo ([a b & r] ())) + (foo :one :two)] + nil true))))) + (is (= 'cljs.core/IList + (:tag + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-2901-b) + (defn foo ([a b & r] ())) + (foo :one :two :three)] + nil true))))) + (is (= 'cljs.core/IList + (:tag + (e/with-compiler-env test-cenv + (a/analyze-form-seq + '[(ns test.cljs-2901-b) + (defn foo ([a b & r] ())) + (foo :one :two :three :four)] + nil true)))))) + (deftest lib-inference (is (= (e/with-compiler-env test-cenv (:tag (analyze test-env '(+ 1 2)))) 'number)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(alength (array))))) - ; 'number)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(aclone (array))))) - ; 'array)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(-count [1 2 3])))) - ; 'number)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(count [1 2 3])))) - ; 'number)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(into-array [1 2 3])))) - ; 'array)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(js-obj)))) - ; 'object)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(-conj [] 1)))) - ; 'clj)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(conj [] 1)))) - ; 'clj)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(alength (array))))) + 'number)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(aclone (array))))) + 'array)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(-count [1 2 3])))) + 'number)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(count [1 2 3])))) + 'number)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(into-array [1 2 3])))) + 'array)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(js-obj)))) + 'object)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(-conj [] 1)))) + 'clj)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(conj [] 1)))) + 'clj)) + (is (= (e/with-compiler-env test-cenv + (:tag (analyze test-env '(dissoc {:foo :bar} :foo)))) + '#{clj clj-nil})) + ;; has changed, why does this return #{clj any} ? ;(is (= (e/with-compiler-env test-cenv ; (:tag (analyze test-env '(assoc nil :foo :bar)))) - ; 'clj)) - ;(is (= (e/with-compiler-env test-cenv - ; (:tag (analyze test-env '(dissoc {:foo :bar} :foo)))) - ; '#{clj clj-nil})) + ; 'clj)) ) (deftest test-always-true-if @@ -297,40 +430,40 @@ ;; will only work if the previous test works (deftest test-count - ;(is (= (cljs.env/with-compiler-env test-cenv - ; (:tag (analyze test-env '(count [])))) - ; 'number)) + (is (= (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(count [])))) + 'number)) ) (deftest test-numeric - ;(is (= (a/no-warn - ; (cljs.env/with-compiler-env test-cenv - ; (:tag (analyze test-env '(dec x))))) - ; 'number)) - ;(is (= (a/no-warn - ; (cljs.env/with-compiler-env test-cenv - ; (:tag (analyze test-env '(int x))))) - ; 'number)) - ;(is (= (a/no-warn - ; (cljs.env/with-compiler-env test-cenv - ; (:tag (analyze test-env '(unchecked-int x))))) - ; 'number)) - ;(is (= (a/no-warn - ; (cljs.env/with-compiler-env test-cenv - ; (:tag (analyze test-env '(mod x y))))) - ; 'number)) - ;(is (= (a/no-warn - ; (cljs.env/with-compiler-env test-cenv - ; (:tag (analyze test-env '(quot x y))))) - ; 'number)) - ;(is (= (a/no-warn - ; (cljs.env/with-compiler-env test-cenv - ; (:tag (analyze test-env '(rem x y))))) - ; 'number)) - ;(is (= (a/no-warn - ; (cljs.env/with-compiler-env test-cenv - ; (:tag (analyze test-env '(bit-count n))))) - ; 'number)) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(dec x))))) + 'number)) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(int x))))) + 'number)) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(unchecked-int x))))) + 'number)) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(mod x y))))) + 'number)) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(quot x y))))) + 'number)) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(rem x y))))) + 'number)) + (is (= (a/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(bit-count n))))) + 'number)) ) ;; ============================================================================= From 09ff3315f3780f3d88a9f990d806fc911f5fda3b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 19 Aug 2018 09:40:19 -0400 Subject: [PATCH 3320/4033] CLJS-2866: Predicate-induced type inference Look for if tests that look like the simple application of a predicate to a local, and in that case, map certain predicates to the tags that satisfying that predicate implies. For example a (string? x) test implies that x has the tag string in the then branch of an if. Likewise check for the use of satisfies? and instance? and induce tags accordingly based on the type argument. For core predicates that delegate to satisfies? or instance?, we have hard-coded entries in the table that essentially cause things to behave as if the predicate were inlined. For example, (counted? x) behaves just like (satisfies? ICounted x). We only induce tags if no tag previously exists (or a tag exists but it is the special any tag.) This ensures that we don't override type hints, or generally revise existing logic. (We really only induce a tag as a last resort.) Arrange the code so that we can add more complicated induced tags in the future (perhaps allowing for logical connectives in the test, for example.) --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/analyzer.cljc | 118 ++++++++++++++++++++++- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/cljs/cljs/inference_test.cljs | 12 ++- src/test/clojure/cljs/analyzer_tests.clj | 38 ++++++++ 5 files changed, 167 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 65a771e72..3a7c2f561 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9960,7 +9960,7 @@ reduces them without incurring seq initialization" ; Use the new, more efficient, IPrintWithWriter interface when possible. (satisfies? IPrintWithWriter obj) - (-pr-writer obj writer opts) + (-pr-writer ^any obj writer opts) (or (true? obj) (false? obj)) (-write writer (str obj)) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 230c14377..f79cd2d77 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -68,6 +68,11 @@ "The namespace of the constants table as a symbol." 'cljs.core.constants) +(def ^:private identity-counter (atom 0)) + +(defn- add-identity [m] + (assoc m :identity (swap! identity-counter inc))) + #?(:clj (def transit-read-opts (try @@ -1440,6 +1445,114 @@ :form form} (var-ast env sym))) +(def ^:private predicate->tag + '{ + ;; Base values + cljs.core/nil? clj-nil + cljs.core/undefined? clj-nil + cljs.core/false? boolean + cljs.core/true? boolean + cljs.core/zero? number + cljs.core/infinite? number + + ;; Base types + cljs.core/boolean? boolean + cljs.core/string? string + cljs.core/char? string + cljs.core/number? number + cljs.core/integer? number + cljs.core/float? number + cljs.core/double? number + cljs.core/array? array + cljs.core/seq? seq + + ;; JavaScript types + cljs.core/regexp? js/RegExp + + ;; Types + cljs.core/keyword? cljs.core/Keyword + cljs.core/var? cljs.core/Var + cljs.core/symbol? cljs.core/Symbol + cljs.core/volatile? cljs.core/Volatile + cljs.core/delay? cljs.core/Delay + cljs.core/reduced? cljs.core/Reduced + + ;; Protocols + cljs.core/map-entry? cljs.core/IMapEntry + cljs.core/reversible? cljs.core/IReversible + cljs.core/uuid? cljs.core/IUUID + cljs.core/tagged-literal? cljs.core/ITaggedLiteral + cljs.core/iterable? cljs.core/IIterable + cljs.core/cloneable? cljs.core/ICloneable + cljs.core/inst? cljs.core/Inst + cljs.core/counted? cljs.core/ICounted + cljs.core/indexed? cljs.core/IIndexed + cljs.core/coll? cljs.core/ICollection + cljs.core/set? cljs.core/ISet + cljs.core/associative? cljs.core/IAssociative + cljs.core/ifind? cljs.core/IFind + cljs.core/sequential? cljs.core/ISequential + cljs.core/sorted? cljs.core/ISorted + cljs.core/reduceable cljs.core/IReduce + cljs.core/map? cljs.core/IMap + cljs.core/list? cljs.core/IList + cljs.core/record? cljs.core/IRecord + cljs.core/vector? cljs.core/IVector + cljs.core/chunked-seq? cljs.core/IChunkedSeq + cljs.core/ifn? cljs.core/IFn + + ;; Composites + cljs.core/seqable? #{cljs.core/ISeqable array string} + cljs.core/ident? #{cljs.core/Keyword cljs.core/Symbol} + }) + +(defn- simple-predicate-induced-tag + "Look for a predicate-induced tag when the test expression is a simple + application of a predicate to a local, as in (string? x)." + [env test] + (when (and (list? test) + (== 2 (count test)) + (every? symbol? test)) + (let [analyzed-fn (no-warn (analyze (assoc env :context :expr) (first test)))] + (when (= :var (:op analyzed-fn)) + (when-let [tag (predicate->tag (:name analyzed-fn))] + (let [sym (last test)] + (when (and (nil? (namespace sym)) + (get-in env [:locals sym])) + [sym tag]))))))) + +(defn- type-check-induced-tag + "Look for a type-check-induced tag when the test expression is the use of + satisfies? or instance? on a local, as in (satisfies? ICounted x)." + [env test] + (when (and (list? test) + (== 3 (count test)) + (every? symbol? test)) + (let [analyzed-fn (no-warn (analyze (assoc env :context :expr) (first test)))] + (when (= :var (:op analyzed-fn)) + (when ('#{cljs.core/satisfies? cljs.core/instance?} (:name analyzed-fn)) + (let [analyzed-type (no-warn (analyze (assoc env :context :expr) (second test))) + tag (:name analyzed-type) + sym (last test)] + (when (and (= :var (:op analyzed-type)) + (nil? (namespace sym)) + (get-in env [:locals sym])) + [sym tag]))))))) + +(defn- add-predicate-induced-tags + "Looks at the test and adds any tags which are induced by virtue + of the predicate being satisfied. For exmaple in (if (string? x) x :bar) + the local x in the then branch must be of string type." + [env test] + (let [[local tag] (or (simple-predicate-induced-tag env test) + (type-check-induced-tag env test))] + (cond-> env + local (update-in [:locals local :tag] (fn [prev-tag] + (if (or (nil? prev-tag) + (= 'any prev-tag)) + tag + prev-tag)))))) + (defmethod parse 'if [op env [_ test then else :as form] name _] (when (< (count form) 3) @@ -1447,7 +1560,7 @@ (when (> (count form) 4) (throw (error env "Too many arguments to if"))) (let [test-expr (disallowing-recur (analyze (assoc env :context :expr) test)) - then-expr (allowing-redef (analyze env then)) + then-expr (allowing-redef (analyze (add-predicate-induced-tags env test) then)) else-expr (allowing-redef (analyze env else))] {:env env :op :if :form form :test test-expr :then then-expr :else else-expr @@ -2105,7 +2218,8 @@ :variadic? (:variadic? init-expr) :max-fixed-arity (:max-fixed-arity init-expr) :method-params (map :params (:methods init-expr))}) - be)] + be) + be (add-identity be)] (recur (conj bes be) (assoc-in env [:locals name] be) (next bindings)))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 502505f5e..51b4b8b6a 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -86,7 +86,7 @@ :else d)))) (defn hash-scope [s] - #?(:clj (System/identityHashCode s) + #?(:clj (or (:identity s) (System/identityHashCode s)) :cljs (hash-combine (-hash ^not-native (:name s)) (shadow-depth s)))) diff --git a/src/test/cljs/cljs/inference_test.cljs b/src/test/cljs/cljs/inference_test.cljs index e456677e3..86a91caa7 100644 --- a/src/test/cljs/cljs/inference_test.cljs +++ b/src/test/cljs/cljs/inference_test.cljs @@ -8,7 +8,7 @@ (ns cljs.inference-test (:require-macros [cljs.inference-util]) - (:require [cljs.test :refer [deftest]] + (:require [cljs.test :refer [deftest is]] [cljs.pprint])) (deftest test-cljs-2825 @@ -78,3 +78,13 @@ (uuid? nil) (tagged-literal? nil) (cljs.pprint/float? nil))) + +(deftest cljs-2866-test + ;; Here we are testing that in the JavaScript emitted, + ;; the gensym generated for curr is being passed to dec + (is (zero? ((fn [x] + (while (pos? @x) + (let [curr @x] + (when (number? curr) + (reset! x (dec curr))))) + @x) (atom 1))))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 2aed399aa..56d8291e4 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -231,6 +231,44 @@ (:tag (analyze test-env '(if x "foo" 1))))) '#{number string}))) +(deftest if-induced-inference + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (nil? x) x :kw)))))) + '#{clj-nil cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (boolean? x) x :kw)))))) + '#{boolean cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (number? x) x :kw)))))) + '#{number cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (double? x) x :kw)))))) + '#{number cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (float? x) x :kw)))))) + '#{number cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (integer? x) x :kw)))))) + '#{number cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (seq? x) x :kw)))))) + '#{seq cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (array? x) x :kw)))))) + '#{array cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x ^any []] (if (seqable? x) x :kw)))))) + '#{cljs.core/ISeqable array string cljs.core/Keyword}))) + (deftest method-inference (is (= (e/with-compiler-env test-cenv (:tag (analyze test-env '(.foo js/bar)))) From c15e1257458069b4e71c99841a1a75d39072320f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Nov 2018 11:58:57 -0500 Subject: [PATCH 3321/4033] narrow the previous commit, satisfies? cannot be involved in inference because they may not be direct implementers of the protocol so the fast path is not safe. --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/analyzer.cljc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3a7c2f561..65a771e72 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9960,7 +9960,7 @@ reduces them without incurring seq initialization" ; Use the new, more efficient, IPrintWithWriter interface when possible. (satisfies? IPrintWithWriter obj) - (-pr-writer ^any obj writer opts) + (-pr-writer obj writer opts) (or (true? obj) (false? obj)) (-write writer (str obj)) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f79cd2d77..9e5865a46 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1523,14 +1523,14 @@ (defn- type-check-induced-tag "Look for a type-check-induced tag when the test expression is the use of - satisfies? or instance? on a local, as in (satisfies? ICounted x)." + instance? on a local, as in (instance? ICounted x)." [env test] (when (and (list? test) (== 3 (count test)) (every? symbol? test)) (let [analyzed-fn (no-warn (analyze (assoc env :context :expr) (first test)))] (when (= :var (:op analyzed-fn)) - (when ('#{cljs.core/satisfies? cljs.core/instance?} (:name analyzed-fn)) + (when ('#{cljs.core/instance?} (:name analyzed-fn)) (let [analyzed-type (no-warn (analyze (assoc env :context :expr) (second test))) tag (:name analyzed-type) sym (last test)] @@ -1541,7 +1541,7 @@ (defn- add-predicate-induced-tags "Looks at the test and adds any tags which are induced by virtue - of the predicate being satisfied. For exmaple in (if (string? x) x :bar) + of the predicate being satisfied. For example in (if (string? x) x :bar) the local x in the then branch must be of string type." [env test] (let [[local tag] (or (simple-predicate-induced-tag env test) From 7bd33f5e21501dbf41d7915e2c383c0138277895 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Nov 2018 12:31:21 -0500 Subject: [PATCH 3322/4033] remove type-hints we no longer need since implements? usage can now infer --- src/main/cljs/cljs/core.cljs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 65a771e72..946c01d84 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1207,7 +1207,7 @@ (when-not (nil? coll) (cond (implements? ISeqable coll) - (-seq ^not-native coll) + (-seq coll) (array? coll) (when-not (zero? (alength coll)) @@ -1228,7 +1228,7 @@ [coll] (when-not (nil? coll) (if (implements? ISeq coll) - (-first ^not-native coll) + (-first coll) (let [s (seq coll)] (when-not (nil? s) (-first s)))))) @@ -1239,7 +1239,7 @@ [coll] (if-not (nil? coll) (if (implements? ISeq coll) - (-rest ^not-native coll) + (-rest coll) (let [s (seq coll)] (if s (-rest ^not-native s) @@ -1252,7 +1252,7 @@ [coll] (when-not (nil? coll) (if (implements? INext coll) - (-next ^not-native coll) + (-next coll) (seq (rest coll))))) (defn ^boolean = @@ -1791,7 +1791,7 @@ reduces them without incurring seq initialization" (when-not (nil? coll) (cond (implements? IEmptyableCollection coll) - (-empty ^not-native coll) + (-empty coll) (satisfies? IEmptyableCollection coll) (-empty coll) @@ -1811,7 +1811,7 @@ reduces them without incurring seq initialization" (if-not (nil? coll) (cond (implements? ICounted coll) - (-count ^not-native coll) + (-count coll) (array? coll) (alength coll) @@ -1859,7 +1859,7 @@ reduces them without incurring seq initialization" coll (implements? IIndexed coll) - (-nth ^not-native coll n) + (-nth coll n) (array? coll) (if (and (>= n 0) (< n (.-length coll))) @@ -1892,7 +1892,7 @@ reduces them without incurring seq initialization" not-found (implements? IIndexed coll) - (-nth ^not-native coll n not-found) + (-nth coll n not-found) (array? coll) (if (and (>= n 0) (< n (.-length coll))) @@ -1931,7 +1931,7 @@ reduces them without incurring seq initialization" (when-not (nil? o) (cond (implements? ILookup o) - (-lookup ^not-native o k) + (-lookup o k) (array? o) (when (and (some? k) (< k (.-length o))) @@ -1949,7 +1949,7 @@ reduces them without incurring seq initialization" (if-not (nil? o) (cond (implements? ILookup o) - (-lookup ^not-native o k not-found) + (-lookup o k not-found) (array? o) (if (and (some? k) (>= k 0) (< k (.-length o))) @@ -2507,7 +2507,7 @@ reduces them without incurring seq initialization" ([f coll] (cond (implements? IReduce coll) - (-reduce ^not-native coll f) + (-reduce coll f) (array? coll) (array-reduce coll f) @@ -2526,7 +2526,7 @@ reduces them without incurring seq initialization" ([f val coll] (cond (implements? IReduce coll) - (-reduce ^not-native coll f val) + (-reduce coll f val) (array? coll) (array-reduce coll f val) @@ -3338,7 +3338,7 @@ reduces them without incurring seq initialization" "Returns the namespace String of a symbol or keyword, or nil if not present." [x] (if (implements? INamed x) - (-namespace ^not-native x) + (-namespace x) (throw (js/Error. (str "Doesn't support namespace: " x))))) (defn ident? @@ -9415,7 +9415,7 @@ reduces them without incurring seq initialization" "Returns the name String of a string, symbol or keyword." [x] (if (implements? INamed x) - (-name ^not-native x) + (-name x) (if (string? x) x (throw (js/Error. (str "Doesn't support name: " x)))))) From 88e2470cf328078da6e22361420cc166df220090 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 19 Aug 2018 07:59:20 -0400 Subject: [PATCH 3323/4033] CLJS-2865: Optimize string expression concatenation Borrow a bit of the logic used for boolean expressions to check if arguments to the str macro are of string or clj-nil type, and to also cause the str macro to indicate that its return type is string. --- src/main/clojure/cljs/core.cljc | 21 +++++++++++++++++---- src/test/cljs/cljs/core_test.cljs | 9 +++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 92da5abd5..55e35cf37 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -827,22 +827,35 @@ (core/quot c 32) (core/inc (core/quot c 32))))) +(core/defn- compatible? [inferred-tag allowed-tags] + (if (set? inferred-tag) + (clojure.set/subset? inferred-tag allowed-tags) + (contains? allowed-tags inferred-tag))) + +(core/defn- typed-expr? [env form allowed-tags] + (compatible? (cljs.analyzer/infer-tag env + (cljs.analyzer/no-warn (cljs.analyzer/analyze env form))) + allowed-tags)) + +(core/defn- string-expr [e] + (vary-meta e assoc :tag 'string)) + (core/defmacro str ([] "") ([x] - (if (core/string? x) + (if (typed-expr? &env x '#{string}) x - (core/list 'js* "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})" x))) + (string-expr (core/list 'js* "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})" x)))) ([x & ys] (core/let [interpolate (core/fn [x] - (if (core/string? x) + (if (typed-expr? &env x '#{string clj-nil}) "~{}" "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})")) strs (core/->> (core/list* x ys) (map interpolate) (interpose ",") (apply core/str))] - (list* 'js* (core/str "[" strs "].join('')") x ys)))) + (string-expr (list* 'js* (core/str "[" strs "].join('')") x ys))))) (core/defn- bool-expr [e] (vary-meta e assoc :tag 'boolean)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 643c41d8e..b77998f1b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1637,6 +1637,15 @@ (is (= "xyzzy" (str "x" "y" "z" "z" "y"))) (is (= "a1b2c3" (str "a" 1 "b" 2 "c" 3)))) +(defn str-fn-2865 [] + "hello") + +(deftest test-cljs-2865 + (is (= "ab" (str "a" (let [x true] (when x "b"))))) + (is (= "ab" (str "a" js/undefined "b"))) + (is (= "ab" (str "a" nil "b"))) + (is (= "ahellob" (str "a" (str-fn-2865) "b")))) + (deftest test-cljs-2934 (let [x (delay 1)] (is (= "#object[cljs.core.Delay {:status :pending, :val nil}]" (pr-str x))) From 70a33f20466a3042b39af2d5e6aa85f52a4cdca5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 23 Nov 2018 13:50:33 -0500 Subject: [PATCH 3324/4033] remove two more type hints we no longer need --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 946c01d84..a4cb55376 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -990,7 +990,7 @@ [o] (cond (implements? IHash o) - (bit-xor (-hash ^not-native o) 0) + (bit-xor (-hash o) 0) (number? o) (if (js/isFinite o) @@ -3198,7 +3198,7 @@ reduces them without incurring seq initialization" (.push arr (-first xs)) (recur (-next xs))) arr))))] - (loop [i (alength arr) ^not-native r ()] + (loop [i (alength arr) r ()] (if (> i 0) (recur (dec i) (-conj r (aget arr (dec i)))) r)))) From f6bdfe1d8eca34d961f020f7e44e098664bc1f67 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Nov 2018 11:00:21 -0500 Subject: [PATCH 3325/4033] CLJS-2989: Fast-path issues for predicate-induced inference based on satisfies? --- src/main/clojure/cljs/analyzer.cljc | 21 +++---- src/test/cljs/cljs/extend_to_native_test.cljs | 57 +++++++++++++++++++ 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9e5865a46..766c73e37 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1477,29 +1477,22 @@ cljs.core/delay? cljs.core/Delay cljs.core/reduced? cljs.core/Reduced + ;;; Note: For non-marker protocol entries below, we + ;;; omit predicates that are based on satisfies? because + ;;; we cannot safely apply the fast-path optimization + ;;; which is enabled when the protocol type is inferred. + ;;; If adding a non-marker entry here, also add a test to + ;;; cljs.extend-to-native-test/test-extend-to-protocols. + ;; Protocols cljs.core/map-entry? cljs.core/IMapEntry - cljs.core/reversible? cljs.core/IReversible cljs.core/uuid? cljs.core/IUUID cljs.core/tagged-literal? cljs.core/ITaggedLiteral - cljs.core/iterable? cljs.core/IIterable - cljs.core/cloneable? cljs.core/ICloneable cljs.core/inst? cljs.core/Inst - cljs.core/counted? cljs.core/ICounted - cljs.core/indexed? cljs.core/IIndexed - cljs.core/coll? cljs.core/ICollection - cljs.core/set? cljs.core/ISet - cljs.core/associative? cljs.core/IAssociative - cljs.core/ifind? cljs.core/IFind cljs.core/sequential? cljs.core/ISequential - cljs.core/sorted? cljs.core/ISorted - cljs.core/reduceable cljs.core/IReduce - cljs.core/map? cljs.core/IMap cljs.core/list? cljs.core/IList cljs.core/record? cljs.core/IRecord - cljs.core/vector? cljs.core/IVector cljs.core/chunked-seq? cljs.core/IChunkedSeq - cljs.core/ifn? cljs.core/IFn ;; Composites cljs.core/seqable? #{cljs.core/ISeqable array string} diff --git a/src/test/cljs/cljs/extend_to_native_test.cljs b/src/test/cljs/cljs/extend_to_native_test.cljs index 52662f62a..581ad83c2 100644 --- a/src/test/cljs/cljs/extend_to_native_test.cljs +++ b/src/test/cljs/cljs/extend_to_native_test.cljs @@ -53,6 +53,9 @@ (is (= "#function[custom-print-cljs-2812]" (pr-str map))) ;; Restore basic native types so that test summary output looks correct (extend-protocol IPrintWithWriter + object + (-pr-writer [obj writer _] + (write-all writer (str obj))) boolean (-pr-writer [obj writer _] (write-all writer (str obj))) @@ -70,3 +73,57 @@ (let [empty-array (empty #js [1 2 3])] (is (and (array? empty-array) (empty? empty-array))))) + +(defn test-map-entry [x] (when (map-entry? x) (-key x))) +(defn test-coll [x] (when (coll? x) (-conj x 1))) +(defn test-set [x] (when (set? x) (-disjoin x 1))) +(defn test-associative [x] (when (associative? x) (-assoc x 1 2))) +(defn test-find [x] (when (ifind? x) (-find x 1))) +(defn test-sorted [x] (when (sorted? x) (-sorted-seq x true))) +(defn test-map [x] (when (map? x) (-dissoc x 1))) +(defn test-vector [x] (when (vector? x) (-assoc-n x 1 2))) +(defn test-chunked-seq [x] (when (chunked-seq? x) (-chunked-first x))) +(defn test-ifn [x] (when (ifn? x) (-invoke x))) +(defn test-reversible [x] (when (reversible? x) (-rseq x))) +(defn test-iterable [x] (when (iterable? x) (-iterator x))) +(defn test-cloneable [x] (when (cloneable? x) (-clone x))) +(defn test-counted [x] (when (counted? x) (-count x))) +(defn test-indexed [x] (when (indexed? x) (-nth x 0))) +(defn test-seqable [x] (when (seqable? x) (-seq x))) +(defn test-reduceable [x] (when (reduceable? x) (-reduce x inc))) + +(deftest test-extend-to-protocols + (extend-type string IMapEntry (-key [_] :a)) + (is (nil? (test-map-entry "a"))) + (extend-type string ICollection (-conj [_ _] :b)) + (is (= :b (test-coll "a"))) + (extend-type string ISet (-disjoin [_ _] :c)) + (is (= :c (test-set "a"))) + (extend-type string IAssociative (-assoc [_ _ _] :d)) + (is (= :d (test-associative "a"))) + (extend-type string IFind (-find [_ _] :e)) + (is (= :e (test-find "a"))) + (extend-type string ISorted (-sorted-seq [_ _] :f)) + (is (= :f (test-sorted "a"))) + (extend-type string IMap (-dissoc [_ _] :g)) + (is (= :g (test-map "a"))) + (extend-type string IVector (-assoc-n [_ _ _] :h)) + (is (= :h (test-vector "a"))) + (extend-type string IChunkedSeq (-chunked-first [_] :i)) + (is (nil? (test-chunked-seq "a"))) + (extend-type string IFn (-invoke [_] :j)) + (is (= :j (test-ifn "a"))) + (extend-type string IReversible (-rseq [_] :k)) + (is (= :k (test-reversible "a"))) + (extend-type string IIterable (-iterator [_] :l)) + (is (= :l (test-iterable "a"))) + (extend-type string ICloneable (-clone [_] :m)) + (is (= :m (test-cloneable "a"))) + (extend-type string ICounted (-count [_] :n)) + (is (= :n (test-counted "a"))) + (extend-type string IIndexed (-nth [_] :o)) + (is (= :o (test-indexed "a"))) + (extend-type number ISeqable (-seq [_] :p)) + (is (= :p (test-seqable 1))) + (extend-type string IReduce (-reduce [_ _] :q)) + (is (= :q (test-reduceable "a")))) From a373039ae82a6a9d6973ce87b08ffb46809ab40a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 5 Sep 2018 17:26:36 -0400 Subject: [PATCH 3326/4033] CLJS-2873: Improved inference for loop / recur --- src/main/clojure/cljs/analyzer.cljc | 79 ++++++++++++++++++++---- src/test/clojure/cljs/analyzer_tests.clj | 31 ++++++++++ 2 files changed, 98 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 766c73e37..ac53aa21b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -891,6 +891,41 @@ 'prototype)}) x)) +(defn ->type-set + "Ensures that a type tag is a set." + [t] + (if #?(:clj (set? t) + :cljs (cljs-set? t)) + t + #{t})) + +(defn canonicalize-type [t] + "Ensures that a type tag is either nil, a type symbol, or a non-singleton + set of type symbols, absorbing clj-nil into seq and all types into any." + (cond + (symbol? t) t + (empty? t) nil + (== 1 (count t)) (first t) + (contains? t 'any) 'any + (contains? t 'seq) (let [res (disj t 'clj-nil)] + (if (== 1 (count res)) + 'seq + res)) + :else t)) + +(defn add-types + "Produces a union of types." + ([] 'any) + ([t1] t1) + ([t1 t2] + (if (or (nil? t1) + (nil? t2)) + 'any + (-> (set/union (->type-set t1) (->type-set t2)) + canonicalize-type))) + ([t1 t2 & ts] + (apply add-types (add-types t1 t2) ts))) + (def alias->type '{object Object string String @@ -1953,7 +1988,8 @@ fixed-arity (count params') recur-frame {:protocol-impl (:protocol-impl env) :params params - :flag (atom nil)} + :flag (atom nil) + :tags (atom [])} recur-frames (cons recur-frame *recur-frames*) body-env (assoc env :context :return :locals locals) body-form `(do ~@body) @@ -2230,14 +2266,23 @@ (analyze-let-body* env context exprs))) (defn analyze-let - [encl-env [_ bindings & exprs :as form] is-loop] + [encl-env [_ bindings & exprs :as form] is-loop widened-tags] (when-not (and (vector? bindings) (even? (count bindings))) (throw (error encl-env "bindings must be vector of even number of elements"))) (let [context (:context encl-env) op (if (true? is-loop) :loop :let) + bindings (if widened-tags + (vec (mapcat + (fn [[name init] widened-tag] + [(vary-meta name assoc :tag widened-tag) init]) + (partition 2 bindings) + widened-tags)) + bindings) [bes env] (analyze-let-bindings encl-env bindings op) recur-frame (when (true? is-loop) - {:params bes :flag (atom nil)}) + {:params bes + :flag (atom nil) + :tags (atom (mapv :tag bes))}) recur-frames (if recur-frame (cons recur-frame *recur-frames*) *recur-frames*) @@ -2245,21 +2290,27 @@ (true? is-loop) *loop-lets* (some? *loop-lets*) (cons {:params bes} *loop-lets*)) expr (analyze-let-body env context exprs recur-frames loop-lets) - children [:bindings :body]] - {:op op - :env encl-env - :bindings bes - :body (assoc expr :body? true) - :form form - :children children})) + children [:bindings :body] + nil->any (fnil identity 'any)] + (if (and is-loop + (not widened-tags) + (not= (mapv nil->any @(:tags recur-frame)) + (mapv (comp nil->any :tag) bes))) + (recur encl-env form is-loop @(:tags recur-frame)) + {:op op + :env encl-env + :bindings bes + :body (assoc expr :body? true) + :form form + :children children}))) (defmethod parse 'let* [op encl-env form _ _] - (analyze-let encl-env form false)) + (analyze-let encl-env form false nil)) (defmethod parse 'loop* [op encl-env form _ _] - (analyze-let encl-env form true)) + (analyze-let encl-env form true nil)) (defmethod parse 'recur [op env [_ & exprs :as form] _ _] @@ -2279,6 +2330,10 @@ (not add-implicit-target-object?)) (warning :protocol-impl-recur-with-target env {:form (:form (first exprs))})) (reset! (:flag frame) true) + (swap! (:tags frame) (fn [tags] + (mapv (fn [tag expr] + (add-types tag (:tag expr))) + tags exprs))) (assoc {:env env :op :recur :form form} :frame frame :exprs exprs diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 56d8291e4..dfdf09825 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -269,6 +269,37 @@ (:tag (a/analyze test-env '(let [x ^any []] (if (seqable? x) x :kw)))))) '#{cljs.core/ISeqable array string cljs.core/Keyword}))) +(deftest loop-recur-inference + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(loop [x "a"] x))))) + 'string)) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(loop [x 10] + (if (pos? x) + (dec x) + x)))))) + 'number)) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (analyze test-env '((fn [p?] + (loop [x nil] + (if (p? x) + x + (recur (str x))))) + 11))))) + '#{string clj-nil})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (analyze test-env '((fn [^string x] + (loop [y x] + (if (= "x" y) + y + (recur 1)))) + "a"))))) + '#{number string}))) + (deftest method-inference (is (= (e/with-compiler-env test-cenv (:tag (analyze test-env '(.foo js/bar)))) From ac5c7abc6fa322e3a225a618c1b4771379560e76 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 24 Nov 2018 12:38:12 -0500 Subject: [PATCH 3327/4033] add helper to convert URLs to Closure SourceFile --- src/main/clojure/cljs/closure.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1f5f49f5d..cbcb4be34 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -116,6 +116,9 @@ (defmethod js-source-file File [_ ^File source] (SourceFile/fromPath (.toPath source) StandardCharsets/UTF_8)) +(defmethod js-source-file URL [_ ^URL source] + (js-source-file (io/file (.getPath source)))) + (defmethod js-source-file BufferedInputStream [^String name ^BufferedInputStream source] (SourceFile/fromInputStream name source)) From 1d1cee6d30033ff6a360edbfbb4492b55d4a4496 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 24 Nov 2018 12:55:31 -0500 Subject: [PATCH 3328/4033] fix typo in last commit --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cbcb4be34..201c6657c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -117,7 +117,7 @@ (SourceFile/fromPath (.toPath source) StandardCharsets/UTF_8)) (defmethod js-source-file URL [_ ^URL source] - (js-source-file (io/file (.getPath source)))) + (js-source-file _ (io/file (.getPath source)))) (defmethod js-source-file BufferedInputStream [^String name ^BufferedInputStream source] (SourceFile/fromInputStream name source)) From f97d766defd02f7d43abd37e3e9b04790a521b1e Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 24 Nov 2018 13:32:00 -0500 Subject: [PATCH 3329/4033] example of externs parsing to get ns information and type info from a Closure lib --- src/main/clojure/cljs/externs.clj | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index f7deca6a1..e672b1b92 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -8,7 +8,9 @@ (ns cljs.externs (:require [clojure.string :as string] - [cljs.util :as util]) + [cljs.util :as util] + [clojure.java.io :as io] + [cljs.js-deps :as js-deps]) (:import [java.util.logging Level] [com.google.javascript.jscomp CompilerOptions SourceFile JsAst CommandLineRunner] @@ -153,6 +155,27 @@ defaults sources)))) (comment + (require '[clojure.java.io :as io] + '[cljs.closure :as closure] + '[clojure.pprint :refer [pprint]] + '[cljs.js-deps :as js-deps]) + + (get (js-deps/js-dependency-index {}) "goog.string") + + ;; {:tag Function :ret-tag boolean} + (-> + (nth + (parse-externs + (closure/js-source-file "goog/string/string.js" + (io/input-stream (io/resource "goog/string/string.js")))) + 2) + last meta) + + (externs-map + [(closure/js-source-file "goog/string/string.js" + (io/input-stream (io/resource "goog/string/string.js")))] + {}) + (externs-map) (-> (externs-map) From a1514978ab9fad57845678954c354016b1300043 Mon Sep 17 00:00:00 2001 From: Eugene Kostenko Date: Mon, 5 Nov 2018 20:24:04 +0700 Subject: [PATCH 3330/4033] CLJS-2933: Consistent #object printing whitespace --- src/main/cljs/cljs/core.cljs | 4 ++-- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a4cb55376..9fb1cf647 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10280,13 +10280,13 @@ reduces them without incurring seq initialization" Atom (-pr-writer [a writer opts] - (-write writer "#object [cljs.core.Atom ") + (-write writer "#object[cljs.core.Atom ") (pr-writer {:val (.-state a)} writer opts) (-write writer "]")) Volatile (-pr-writer [a writer opts] - (-write writer "#object [cljs.core.Volatile ") + (-write writer "#object[cljs.core.Volatile ") (pr-writer {:val (.-state a)} writer opts) (-write writer "]")) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b77998f1b..b97f39f14 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1663,3 +1663,7 @@ (is (= m4 (merge-with + (sorted m1) (sorted m2) m3))) (is (= m4 (merge-with + m1 (sorted m2) m3))) (is (= m4 (merge-with + m1 (sorted m2) (sorted m3)))))) + +(deftest test-cljs-2933 + (is (= "#object[cljs.core.Atom {:val 1}]" (pr-str (atom 1)))) + (is (= "#object[cljs.core.Volatile {:val 2}]" (pr-str (volatile! 2))))) From 260556d55035bbe032fc028a723fb652048040b2 Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Sun, 16 Sep 2018 21:48:35 +0200 Subject: [PATCH 3331/4033] CLJS-2912 Reuse seq in some --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9fb1cf647..739d11150 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4206,8 +4206,8 @@ reduces them without incurring seq initialization" this will return :fred if :fred is in the sequence, otherwise nil: (some #{:fred} coll)" [pred coll] - (when (seq coll) - (or (pred (first coll)) (recur pred (next coll))))) + (when-let [s (seq coll)] + (or (pred (first s)) (recur pred (next s))))) (defn not-any? "Returns false if (pred x) is logical true for any x in coll, From 9979feaf69765460238015a8468ba2fc803e3860 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 14 Sep 2018 17:45:58 -0400 Subject: [PATCH 3332/4033] CLJS-2909: clojure.walk/postwalk does not preserve MapEntry type objects And ports clojure.walk tests from Clojure. --- src/main/cljs/clojure/walk.cljs | 3 +- src/test/cljs/cljs/walk_test.cljs | 81 +++++++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 + src/test/self/self_parity/test.cljs | 2 + 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs/cljs/walk_test.cljs diff --git a/src/main/cljs/clojure/walk.cljs b/src/main/cljs/clojure/walk.cljs index ffbb6a7c2..d92d61c91 100644 --- a/src/main/cljs/clojure/walk.cljs +++ b/src/main/cljs/clojure/walk.cljs @@ -44,7 +44,8 @@ the sorting function."} [inner outer form] (cond (list? form) (outer (apply list (map inner form))) - (map-entry? form) (outer (vec (map inner form))) + (map-entry? form) + (outer (MapEntry. (inner (key form)) (inner (val form)) nil)) (seq? form) (outer (doall (map inner form))) (record? form) (outer (reduce (fn [r x] (conj r (inner x))) form form)) (coll? form) (outer (into (empty form) (map inner form))) diff --git a/src/test/cljs/cljs/walk_test.cljs b/src/test/cljs/cljs/walk_test.cljs new file mode 100644 index 000000000..9f28ebf08 --- /dev/null +++ b/src/test/cljs/cljs/walk_test.cljs @@ -0,0 +1,81 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns cljs.walk-test + (:require [cljs.test :refer-macros [deftest testing is]] + [clojure.walk :as w])) + +(deftest t-prewalk-replace + (is (= (w/prewalk-replace {:a :b} [:a {:a :a} (list 3 :c :a)]) + [:b {:b :b} (list 3 :c :b)]))) + +(deftest t-postwalk-replace + (is (= (w/postwalk-replace {:a :b} [:a {:a :a} (list 3 :c :a)]) + [:b {:b :b} (list 3 :c :b)]))) + +(deftest t-stringify-keys + (is (= (w/stringify-keys {:a 1, nil {:b 2 :c 3}, :d 4}) + {"a" 1, nil {"b" 2 "c" 3}, "d" 4}))) + +(deftest t-prewalk-order + (is (= (let [a (atom [])] + (w/prewalk (fn [form] (swap! a conj form) form) + [1 2 {:a 3} (list 4 [5])]) + @a) + [[1 2 {:a 3} (list 4 [5])] + 1 2 {:a 3} [:a 3] :a 3 (list 4 [5]) + 4 [5] 5]))) + +(deftest t-postwalk-order + (is (= (let [a (atom [])] + (w/postwalk (fn [form] (swap! a conj form) form) + [1 2 {:a 3} (list 4 [5])]) + @a) + [1 2 + :a 3 [:a 3] {:a 3} + 4 5 [5] (list 4 [5]) + [1 2 {:a 3} (list 4 [5])]]))) + +(defrecord Foo [a b c]) + +(defmulti get-comparator type) + +(defmethod get-comparator PersistentTreeMap + [o] (.-comp o)) + +(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 walk-mapentry + "Checks that walk preserves the MapEntry type. See CLJS-2909." + (let [coll [:html {:a ["b" 1]} ""] + f (fn [e] (if (and (vector? e) (not (map-entry? e))) (apply list e) e))] + (is (= (list :html {:a (list "b" 1)} "") (w/postwalk f coll))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 649c104b5..0dbb3108e 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -47,6 +47,7 @@ [cljs.recur-test] [cljs.array-access-test] [cljs.inference-test] + [cljs.walk-test] [cljs.extend-to-native-test])) (set! *print-newline* false) @@ -92,4 +93,5 @@ 'cljs.recur-test 'cljs.array-access-test 'cljs.inference-test + 'cljs.walk-test 'cljs.extend-to-native-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 1be98f119..816659043 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -312,6 +312,7 @@ [cljs.recur-test] [cljs.array-access-test] [cljs.inference-test] + [cljs.walk-test] [cljs.extend-to-native-test])) (fn [{:keys [value error]}] (if error @@ -356,6 +357,7 @@ 'cljs.recur-test 'cljs.array-access-test 'cljs.inference-test + 'cljs.walk-test 'cljs.extend-to-native-test) (fn [{:keys [value error]}] (when error From 9e94a79708ef85b171d248dcf99c854de9be5254 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 20 Feb 2018 08:32:29 -0500 Subject: [PATCH 3333/4033] CLJS-2537: Negative fractional index in contains? on array --- src/main/cljs/cljs/core.cljs | 4 ++-- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 739d11150..8c61fce00 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1952,12 +1952,12 @@ reduces them without incurring seq initialization" (-lookup o k not-found) (array? o) - (if (and (some? k) (>= k 0) (< k (.-length o))) + (if (and (some? k) (< -1 k (.-length o))) (aget o (int k)) not-found) (string? o) - (if (and (some? k) (>= k 0) (< k (.-length o))) + (if (and (some? k) (< -1 k (.-length o))) (.charAt o (int k)) not-found) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b97f39f14..f271d2248 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1537,6 +1537,14 @@ (deftest test-cljs-2457 (is (thrown-with-msg? js/Error #".* is not ISeqable" (seq #js {:a 1 :b 2})))) +(deftest test-cljs-2537 + (is (true? (contains? (to-array [7 13 41]) -0.5))) + (is (== 7 (get (to-array [7 13 41]) -0.5))) + (is (== 7 (get (to-array [7 13 41]) -0.5 :not-found))) + (is (true? (contains? "ab" -0.5))) + (is (= \a (get "ab" -0.5))) + (is (= \a (get "ab" -0.5 :not-found)))) + (deftest test-cljs-2549 (let [tap (fn [_])] (add-tap tap) From b1ad22a8e20d2f57e88fba1b556a4f8b11be0ab0 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 20 Feb 2018 08:31:07 -0500 Subject: [PATCH 3334/4033] CLJS-2538: nth on fractional indices near array and string bounds --- src/main/cljs/cljs/core.cljs | 16 ++++++++-------- src/test/cljs/cljs/core_test.cljs | 32 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8c61fce00..da95df9dd 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1862,13 +1862,13 @@ reduces them without incurring seq initialization" (-nth coll n) (array? coll) - (if (and (>= n 0) (< n (.-length coll))) - (aget coll n) + (if (and (< -1 n (.-length coll))) + (aget coll (int n)) (throw (js/Error. "Index out of bounds"))) (string? coll) - (if (and (>= n 0) (< n (.-length coll))) - (.charAt coll n) + (if (and (< -1 n (.-length coll))) + (.charAt coll (int n)) (throw (js/Error. "Index out of bounds"))) (or (implements? ISeq coll) @@ -1895,13 +1895,13 @@ reduces them without incurring seq initialization" (-nth coll n not-found) (array? coll) - (if (and (>= n 0) (< n (.-length coll))) - (aget coll n) + (if (and (< -1 n (.-length coll))) + (aget coll (int n)) not-found) (string? coll) - (if (and (>= n 0) (< n (.-length coll))) - (.charAt coll n) + (if (and (< -1 n (.-length coll))) + (.charAt coll (int n)) not-found) (or (implements? ISeq coll) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index f271d2248..b1d145b49 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1545,6 +1545,38 @@ (is (= \a (get "ab" -0.5))) (is (= \a (get "ab" -0.5 :not-found)))) +(deftest test-cljs-2538 + (testing "fractional indices in nth on arrays" + (is (thrown-with-msg? js/Error #"Index out of bounds" (nth (to-array [1 2]) -1))) + (is (= :not-found (nth (to-array [1 2]) -1 :not-found))) + (is (== 1 (nth (to-array [1 2]) -0.5))) + (is (== 1 (nth (to-array [1 2]) -0.5 :not-found))) + (is (== 1 (nth (to-array [1 2]) 0))) + (is (== 1 (nth (to-array [1 2]) 0 :not-found))) + (is (== 1 (nth (to-array [1 2]) 0.5))) + (is (== 1 (nth (to-array [1 2]) 0.5 :not-found))) + (is (== 2 (nth (to-array [1 2]) 1))) + (is (== 2 (nth (to-array [1 2]) 1 :not-found))) + (is (== 2 (nth (to-array [1 2]) 1.5))) + (is (== 2 (nth (to-array [1 2]) 1.5 :not-found))) + (is (thrown-with-msg? js/Error #"Index out of bounds" (nth (to-array [1 2]) 2))) + (is (= :not-found (nth (to-array [1 2]) 2 :not-found)))) + (testing "fractional indices in nth on strings" + (is (thrown-with-msg? js/Error #"Index out of bounds" (nth "ab" -1))) + (is (= :not-found (nth "ab" -1 :not-found))) + (is (== \a (nth "ab" -0.5))) + (is (== \a (nth "ab" -0.5 :not-found))) + (is (== \a (nth "ab" 0))) + (is (== \a (nth "ab" 0 :not-found))) + (is (== \a (nth "ab" 0.5))) + (is (== \a (nth "ab" 0.5 :not-found))) + (is (== \b (nth "ab" 1))) + (is (== \b (nth "ab" 1 :not-found))) + (is (== \b (nth "ab" 1.5))) + (is (== \b (nth "ab" 1.5 :not-found))) + (is (thrown-with-msg? js/Error #"Index out of bounds" (nth "ab" 2))) + (is (= :not-found (nth "ab" 2 :not-found))))) + (deftest test-cljs-2549 (let [tap (fn [_])] (add-tap tap) From 61fbb03f7ccd1e493ce0e4e7f697799e9aa8194b Mon Sep 17 00:00:00 2001 From: Eugene Kostenko Date: Mon, 26 Nov 2018 22:33:11 +0700 Subject: [PATCH 3335/4033] CLJS-2976: s/fdef docstring should refer to cljs.spec.test.alpha/check --- src/main/cljs/cljs/spec/alpha.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 735df4bb1..fc7d28f75 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -418,10 +418,10 @@ Qualifies fn-sym with resolve, or using *ns* if no resolution found. Registers an fspec in the global registry, where it can be retrieved - by calling get-spec with the var or full-qualified symbol. + by calling get-spec with the var or fully-qualified symbol. Once registered, function specs are included in doc, checked by - instrument, tested by the runner cljs.spec.test.alpha/run-tests, and (if + instrument, tested by the runner cljs.spec.test.alpha/check, and (if a macro) used to explain errors during macroexpansion. Note that :fn specs require the presence of :args and :ret specs to From e005b987a8d1fe96e76c2975b931575fc2bdfce3 Mon Sep 17 00:00:00 2001 From: Eugene Kostenko Date: Mon, 26 Nov 2018 18:37:44 +0700 Subject: [PATCH 3336/4033] CLJS-2971: Make cljs.spec.alpha/fn-sym private --- src/main/cljs/cljs/spec/alpha.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 0295fabd0..19a9241f4 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -120,7 +120,7 @@ (when (ident? spec-or-k) (throw (js/Error. (str "Unable to resolve spec: " spec-or-k)))))) -(defn fn-sym [f-n] +(defn- fn-sym [f-n] (when-not (str/blank? f-n) (let [xs (map demunge (str/split f-n "$"))] (when (c/and (<= 2 (count xs)) From 7db58267bc496b22a2a672e38b3656ce898363ce Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 26 Nov 2018 23:05:38 -0500 Subject: [PATCH 3337/4033] CLJS-2991: Need to wrap js-obj output with parens --- src/main/clojure/cljs/core.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 15 +++++++++++++++ src/test/self/self_host/test.cljs | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 55e35cf37..75029474f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2588,7 +2588,7 @@ (interpose ",") (apply core/str))] (vary-meta - (list* 'js* (core/str "{" kvs-str "}") (apply concat kvs)) + (list* 'js* (core/str "({" kvs-str "})") (apply concat kvs)) assoc :tag 'object))) (core/defmacro js-obj [& rest] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b1d145b49..34aa38149 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1707,3 +1707,18 @@ (deftest test-cljs-2933 (is (= "#object[cljs.core.Atom {:val 1}]" (pr-str (atom 1)))) (is (= "#object[cljs.core.Volatile {:val 2}]" (pr-str (volatile! 2))))) + +(deftest test-cljs-2991 + (let [o (js-obj)] + (is (object? o)) + (is (empty? (js-keys o)))) + (let [o (js-obj "a" 17)] + (is (object? o)) + (is (== 1 (count (js-keys o)))) + (is (= "a" (aget (js-keys o) 0))) + (is (== 17 (gobject/get o "a")))) + (let [o (js-obj "a" 17 "b" 27)] + (is (object? o)) + (is (== 2 (count (js-keys o)))) + (is (== 17 (gobject/get o "a"))) + (is (== 27 (gobject/get o "b"))))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index d7764e052..3985194a7 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1484,6 +1484,21 @@ (is (some cljs-timestamp? (keys sms))) (inc! l))))))))) +(deftest test-cljs-2991 + (async done + (let [l (latch 1 done)] + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(js-obj)" + nil + {:ns 'cljs.user + :target :nodejs + :eval node-eval} + (fn [{:keys [value]}] + (is (object? value)) + (is (empty? (js-keys value))) + (inc! l))))))) + (defn -main [& args] (run-tests)) From 345a9b6c966a790aabcfd4bf6a46cb29a86e232d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 1 Jul 2018 14:46:04 -0400 Subject: [PATCH 3338/4033] CLJS-2693: Have Range implement IChunkedSeq This set of changes mirrors those in Clojure surrounding LongRange and LongChunk. In particular, range is modified to follow the 3-arg LongRange/create. The previous next implementation is preserved. (The Clojure implemenation forces a chunk for each next call, which, in ClojureScript, slows down algorithms like doall which walk the seq.) RangeChunk does not implement IReduce because, unlike in Clojure, ClojureScript doesn't have an internal-reduce which reduces chunked-seq chunks. (Additionally, reduce for ranges is handled directly by Range.) --- src/main/cljs/cljs/core.cljs | 90 ++++++++++++++++++++++++------- src/test/cljs/cljs/core_test.cljs | 17 ++++++ 2 files changed, 88 insertions(+), 19 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index da95df9dd..7de4b2948 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9547,6 +9547,27 @@ reduces them without incurring seq initialization" (take-while (mk-bound-fn sc start-test start-key) (if ((mk-bound-fn sc end-test end-key) e) s (next s)))))) +(deftype RangeChunk [start step count] + ICounted + (-count [coll] count) + + ISeq + (-first [coll] start) + + IIndexed + (-nth [coll i] + (+ start (* i step))) + (-nth [coll i not-found] + (if (and (>= i 0) (< i count)) + (+ start (* i step)) + not-found)) + + IChunk + (-drop-first [coll] + (if (<= count 1) + (throw (js/Error. "-drop-first of empty chunk")) + (RangeChunk. (+ start step) step (dec count))))) + (deftype RangeIterator [^:mutable i end step] Object (hasNext [_] @@ -9558,7 +9579,7 @@ reduces them without incurring seq initialization" (set! i (+ i step)) ret))) -(deftype Range [meta start end step ^:mutable __hash] +(deftype Range [meta start end step ^:mutable chunk ^:mutable chunk-next ^:mutable __hash] Object (toString [coll] (pr-str* coll)) @@ -9572,30 +9593,34 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x (count coll))) (lastIndexOf [coll x start] (-lastIndexOf coll x start)) + (forceChunk [coll] + (when (nil? chunk) + (let [count (-count coll)] + (if (> count 32) + (do + (set! chunk-next (Range. nil (+ start (* step 32)) end step nil nil nil)) + (set! chunk (RangeChunk. start step 32))) + (set! chunk (RangeChunk. start step count)))))) ICloneable - (-clone [_] (Range. meta start end step __hash)) + (-clone [_] (Range. meta start end step chunk chunk-next __hash)) IWithMeta - (-with-meta [rng meta] (Range. meta start end step __hash)) + (-with-meta [rng meta] (Range. meta start end step chunk chunk-next __hash)) IMeta (-meta [rng] meta) ISeqable - (-seq [rng] - (cond - (pos? step) (when (< start end) rng) - (neg? step) (when (> start end) rng) - :else (when-not (== start end) rng))) + (-seq [rng] rng) ISeq - (-first [rng] - (when-not (nil? (-seq rng)) start)) + (-first [rng] start) (-rest [rng] - (if-not (nil? (-seq rng)) - (Range. meta (+ start step) end step nil) - ())) + (let [s (-next rng)] + (if (nil? s) + () + s))) IIterable (-iterator [_] @@ -9605,9 +9630,23 @@ reduces them without incurring seq initialization" (-next [rng] (if (pos? step) (when (< (+ start step) end) - (Range. meta (+ start step) end step nil)) + (Range. meta (+ start step) end step nil nil nil)) (when (> (+ start step) end) - (Range. meta (+ start step) end step nil)))) + (Range. meta (+ start step) end step nil nil nil)))) + + IChunkedSeq + (-chunked-first [rng] + (.forceChunk rng) + chunk) + (-chunked-rest [rng] + (.forceChunk rng) + (if (nil? chunk-next) + () + chunk-next)) + + IChunkedNext + (-chunked-next [rng] + (seq (-chunked-rest rng))) ICollection (-conj [rng o] (cons o rng)) @@ -9624,9 +9663,7 @@ reduces them without incurring seq initialization" ICounted (-count [rng] - (if-not (-seq rng) - 0 - (Math/ceil (/ (- end start) step)))) + (Math/ceil (/ (- end start) step))) IIndexed (-nth [rng n] @@ -9662,7 +9699,22 @@ reduces them without incurring seq initialization" ([] (range 0 (.-MAX_VALUE js/Number) 1)) ([end] (range 0 end 1)) ([start end] (range start end 1)) - ([start end step] (Range. nil start end step nil))) + ([start end step] + (cond + (pos? step) + (if (<= end start) + () + (Range. nil start end step nil nil nil)) + + (neg? step) + (if (>= end start) + () + (Range. nil start end step nil nil nil)) + + :else + (if (== end start) + () + (repeat start))))) (defn take-nth "Returns a lazy seq of every nth item in coll. Returns a stateful diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 34aa38149..514429a22 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1599,6 +1599,23 @@ ;; 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 (satisfies? IChunk (chunk-first (chunk-next (range 33))))) + (is (satisfies? IChunk (chunk-first (chunk-rest (range 33))))) + (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 (= ::not-found (nth (chunk-first (range 100)) 35 ::not-found))) + (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*) From 0be62b2eef8e1c70711b16aee128d5def1cf6028 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 29 Nov 2018 10:52:49 +0100 Subject: [PATCH 3339/4033] Fix source maps missing local binding names --- src/main/clojure/cljs/compiler.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 51b4b8b6a..b9ff5bce5 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -189,7 +189,7 @@ (let [minfo (cond-> {:gcol #?(:clj (.get ^AtomicLong *source-map-data-gen-col*) :cljs (:gen-col m)) :gline (:gen-line m)} - (#{:var :local :js-var} (:op ast)) + (#{:var :local :js-var :binding} (:op ast)) (assoc :name (str (-> ast :info :name))))] ; Dec the line/column numbers for 0-indexing. ; tools.reader uses 1-indexed sources, chrome From 1525386b136a8f36bee2fabacfe87a3cd60254d4 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 29 Nov 2018 20:21:11 +0100 Subject: [PATCH 3340/4033] CLJS-2995: Instrumented self-calling multi-arity fn throws maximum call stack exceeded with optimizations advanced Fixed by calling static arities instead of apply --- src/main/cljs/cljs/spec/test/alpha.cljc | 20 +++++++++++++------- src/main/cljs/cljs/spec/test/alpha.cljs | 22 +++++++++++++--------- src/test/cljs/cljs/spec/test_test.cljs | 17 +++++++++++++++++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index e74a3ad3a..0827be886 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -288,16 +288,22 @@ spec itself will have an ::s/failure value in ex-data: (fn [sym] (do `(check-1 '~sym nil nil ~opts-sym)))))])))) -(defmacro ^:private maybe-setup-static-dispatch [f ret arity] +(defmacro ^:private maybe-setup-static-dispatch [f ret conform! arity] (let [arity-accessor (symbol (str ".-cljs$core$IFn$_invoke$arity$" arity)) argv (mapv #(symbol (str "arg" %)) (range arity))] - `(when (some? (~arity-accessor ~f)) + `(when-some [ac# (~arity-accessor ~f)] (set! (~arity-accessor ~ret) - (fn ~argv - (apply ~ret ~argv)))))) - -(defmacro ^:private setup-static-dispatches [f ret max-arity] + (fn ~argv + (if *instrument-enabled* + (with-instrument-disabled + (~conform! ~argv) + (binding [*instrument-enabled* true] + (ac# ~@argv))) + (ac# ~@argv))))))) + +(defmacro ^:private setup-static-dispatches [f ret conform! max-arity] + ;; ret is for when we don't have arity info `(do ~@(mapv (fn [arity] - `(maybe-setup-static-dispatch ~f ~ret ~arity)) + `(maybe-setup-static-dispatch ~f ~ret ~conform! ~arity)) (range (inc max-arity))))) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 999215b7c..983c42613 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -87,6 +87,7 @@ (defn- spec-checking-fn [v f fn-spec] (let [fn-spec (@#'s/maybe-spec fn-spec) + args-spec (:args fn-spec) conform! (fn [v role spec data args] (let [conformed (s/conform spec data)] (if (= ::s/invalid conformed) @@ -112,15 +113,18 @@ pure-variadic?) (.cljs$core$IFn$_invoke$arity$variadic f) (apply f args))) - ret (fn [& args] - (if *instrument-enabled* - (with-instrument-disabled - (when (:args fn-spec) (conform! v :args (:args fn-spec) args args)) - (binding [*instrument-enabled* true] - (apply' f args))) - (apply' f args)))] - (when-not pure-variadic? - (setup-static-dispatches f ret 20) + conform!* #(conform! v :args args-spec % %) + ret (if args-spec + (fn [& args] + (if *instrument-enabled* + (with-instrument-disabled + (conform!* args) + (binding [*instrument-enabled* true] + (apply' f args))) + (apply' f args))) + f)] + (when (and (not pure-variadic?) args-spec) + (setup-static-dispatches f ret conform!* 20) (when-some [variadic (.-cljs$core$IFn$_invoke$arity$variadic f)] (set! (.-cljs$core$IFn$_invoke$arity$variadic ret) (fn [& args] diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 8334cd8ae..a4859c8bf 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -117,3 +117,20 @@ (testing "instrument and unstrument return empty coll when no fdef exists" (is (empty? (stest/instrument `fn-2975))) (is (empty? (stest/unstrument `fn-2975))))) + +(defn fn-2995 + ([] (fn-2995 0)) + ([a] (fn-2995 a 1)) + ([a b] [a b])) + +(s/fdef fn-2995 + :args (s/cat :a (s/? number?) + :b (s/? number?))) + +(deftest test-2995 + (stest/instrument `fn-2995) + (testing "instrumented self-calling multi-arity function works" + (is (= [0 1] (fn-2995 0 1))) + (is (= [0 1] (fn-2995 0))) + (is (= [0 1] (fn-2995 0))) + (is (thrown? js/Error (fn-2995 "not a number"))))) From 69a09a35aeaf6a14df24afb09d0691da8c6915ff Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 13 Nov 2018 08:06:07 -0500 Subject: [PATCH 3341/4033] CLJS-2968: Support immutable GCC DependencyOptions --- src/main/clojure/cljs/closure.clj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 201c6657c..4707f13b8 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -16,6 +16,7 @@ [cljs.env :as env] [cljs.js-deps :as deps] [clojure.java.io :as io] + [clojure.reflect] [clojure.set :as set] [clojure.string :as string] [clojure.data.json :as json] @@ -1961,6 +1962,13 @@ (= (package-json-entries {:target :nodejs :package-json-resolution :webpack}) ["browser" "module" "main"]) (= (package-json-entries {:target :nodejs :package-json-resolution ["foo" "bar"]}) ["foo" "bar"])) +(defn- sorting-dependency-options [] + (try + (util/compile-if (contains? (:flags (clojure.reflect/reflect DependencyOptions)) :abstract) + (DependencyOptions/sortOnly) + (doto (DependencyOptions.) + (.setDependencySorting true))))) + (defn convert-js-modules "Takes a list JavaScript modules as an IJavaScript and rewrites them into a Google Closure-compatible form. Returns list IJavaScript with the converted module @@ -1972,8 +1980,7 @@ (.setProcessCommonJSModules true) (.setLanguageIn (lang-key->lang-mode :ecmascript6)) (.setLanguageOut (lang-key->lang-mode (:language-out opts :ecmascript3))) - (.setDependencyOptions (doto (DependencyOptions.) - (.setDependencySorting true))) + (.setDependencyOptions (sorting-dependency-options)) (.setPackageJsonEntryNames ^List (package-json-entries opts))) closure-compiler (doto (make-closure-compiler) (.init externs source-files options)) From 8ceee7f839343c0675ee45882fd6b2ea9fe606bd Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 9 Nov 2018 10:24:00 +0100 Subject: [PATCH 3342/4033] CLJS-2967: Make clojure.spec.alpha reloadable def-once'ing atom fixes reloadability of clojure.spec.alpha --- src/main/cljs/cljs/spec/alpha.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index fc7d28f75..2d1b04553 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -400,7 +400,7 @@ (clojure.core/assert (not (empty? preds))) `(tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) -(def ^:private _speced_vars (atom #{})) +(defonce ^:private _speced_vars (atom #{})) (defn speced-vars [] @_speced_vars) From fcc4495462f35ea74b74468354fdc893302c4284 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 8 Oct 2018 17:23:13 -0400 Subject: [PATCH 3343/4033] CLJS-2929: Port datafy --- src/main/cljs/cljs/core.cljs | 20 +++++++++-- src/main/cljs/clojure/datafy.cljs | 55 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/main/cljs/clojure/datafy.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7de4b2948..c63b41c56 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -867,6 +867,13 @@ (-iterator [coll] "Returns an iterator for coll.")) +(defprotocol IDatafiable + (-datafy [o] "return a representation of o as data (default identity)")) + +(defprotocol INavigable + (-nav [coll k v] "return (possibly transformed) v in the context of coll and k (a key/index or nil), +defaults to returning v.")) + ;; Printing support (deftype StringBufferWriter [sb] @@ -1356,7 +1363,10 @@ (extend-type nil ICounted - (-count [_] 0)) + (-count [_] 0) + + IDatafiable + (-datafy [_] nil)) ;; TODO: we should remove this and handle date equality checking ;; by some other means, probably by adding a new primitive type @@ -1405,7 +1415,13 @@ (extend-type default IHash (-hash [o] - (goog/getUid o))) + (goog/getUid o)) + + IDatafiable + (-datafy [o] o) + + INavigable + (-nav [_ _ x] x)) ;;this is primitive because & emits call to array-seq (defn inc diff --git a/src/main/cljs/clojure/datafy.cljs b/src/main/cljs/clojure/datafy.cljs new file mode 100644 index 000000000..46cbddcd8 --- /dev/null +++ b/src/main/cljs/clojure/datafy.cljs @@ -0,0 +1,55 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns + ^{:doc "Functions to turn objects into data. Alpha, subject to change"} + clojure.datafy) + +(defn datafy + "Attempts to return x as data. If :clojure.datafy/datafy is present + as metadata of x, it will be called with x as an argument, else + datafy will return the value of clojure.protocols/datafy. If the + value has been transformed and the result supports + metadata, :clojure.datafy/obj will be set on the metadata to the + original value of x." + [x] + (let [v ((or (-> x meta ::datafy) -datafy) x)] + (if (identical? v x) + v + (if (object? v) + (vary-meta v assoc ::obj x) + v)))) + +(defn nav + "Returns (possibly transformed) v in the context of coll and k (a + key/index or nil). Callers should attempt to provide the key/index + context k for Indexed/Associative/ILookup colls if possible, but not + to fabricate one e.g. for sequences (pass nil). If :clojure.datafy/nav is + present as metadata on coll, it will be called with coll, k and v as + arguments, else nav will call :clojure.protocols/nav." + [coll k v] + ((or (-> coll meta ::nav) -nav) coll k v)) + +(defn- datify-ref [r] + (with-meta [(deref r)] (meta r))) + +(extend-protocol IDatafiable + Var + (-datafy [r] (datify-ref r)) + + Reduced + (-datafy [r] (datify-ref r)) + + Atom + (-datafy [r] (datify-ref r)) + + Volatile + (-datafy [r] (datify-ref r)) + + Delay + (-datafy [r] (datify-ref r))) From ad07bbd3ed5c9108e156983ce4a1d289a0f768dc Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 30 Nov 2018 15:00:28 -0500 Subject: [PATCH 3344/4033] CLJS-2977: Spec instrumentation regression with varargs / :static-fns type hints we no longer need advanced safe variadic invoke for the instrument fn wrapper did not call conform before invoking the original variadic case. Add tests. --- src/main/cljs/cljs/spec/test/alpha.cljs | 7 ++++++- src/test/cljs/cljs/spec_test.cljs | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 983c42613..3618ae3e4 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -128,7 +128,12 @@ (when-some [variadic (.-cljs$core$IFn$_invoke$arity$variadic f)] (set! (.-cljs$core$IFn$_invoke$arity$variadic ret) (fn [& args] - (apply variadic args))))) + (if *instrument-enabled* + (with-instrument-disabled + (conform!* args) + (binding [*instrument-enabled* true] + (apply' variadic args))) + (apply' variadic args)))))) ret)) (defn- no-fspec diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 4f9544239..838c013e1 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -441,6 +441,20 @@ (is (= (s/describe #(odd? %)) ::s/unknown)) (is (= (s/form #(odd? %)) ::s/unknown))) +(defn defk [key & [doc]] + key) + +(s/fdef defk + :args (s/cat :key keyword? + :doc (s/? string?))) + +(st/instrument `defk) + +(deftest cljs-2977-variadic-fn + (is (thrown? js/Error (defk 1 1))) + (is (thrown? js/Error (defk :foo 1))) + (is (= :foo (defk :foo "bar")))) + (comment (run-tests) From d626d572b03b0c072fbc7284de5a2a052b6d379d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 30 Nov 2018 15:03:45 -0500 Subject: [PATCH 3345/4033] CLJS-2980: Calling "check-fn" gives "is not public" warning check-fn macro uses private fn, unmark for now --- src/main/cljs/cljs/spec/test/alpha.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 3618ae3e4..0f295762d 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -248,7 +248,7 @@ with explain-data + ::s/failure." (when-let [shrunk (-> test-check-ret :shrunk)] {:failure (:result shrunk)}))) -(defn- validate-check-opts +(defn validate-check-opts [opts] (assert (every? ident? (keys (:gen opts))) "check :gen expects ident keys")) From 777b10ed3f3edc40fbba279bb4bdee58b4823b13 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 1 Dec 2018 15:35:57 -0500 Subject: [PATCH 3346/4033] CLJS-2997: cljs-2977-variadic-fn-failing in CI --- src/main/cljs/cljs/spec/test/alpha.cljs | 2 +- src/test/cljs/cljs/spec_test.cljs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 0f295762d..897c2911f 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -130,7 +130,7 @@ (fn [& args] (if *instrument-enabled* (with-instrument-disabled - (conform!* args) + (conform!* (apply list* args)) (binding [*instrument-enabled* true] (apply' variadic args))) (apply' variadic args)))))) diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index 838c013e1..c6d1ce2f9 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -442,7 +442,7 @@ (is (= (s/form #(odd? %)) ::s/unknown))) (defn defk [key & [doc]] - key) + [key doc]) (s/fdef defk :args (s/cat :key keyword? @@ -453,7 +453,7 @@ (deftest cljs-2977-variadic-fn (is (thrown? js/Error (defk 1 1))) (is (thrown? js/Error (defk :foo 1))) - (is (= :foo (defk :foo "bar")))) + (is (= [:foo "bar"] (defk :foo "bar")))) (comment From 1b242bb9399a9cf8f41eb6a6ab4d2450aa3c2756 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 1 Dec 2018 17:57:16 -0500 Subject: [PATCH 3347/4033] CLJS-2998: GCC DependencyOptions support statically compiled --- src/main/clojure/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4707f13b8..5baa90f3f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1964,8 +1964,10 @@ (defn- sorting-dependency-options [] (try - (util/compile-if (contains? (:flags (clojure.reflect/reflect DependencyOptions)) :abstract) - (DependencyOptions/sortOnly) + (if (contains? (:flags (clojure.reflect/reflect DependencyOptions)) :abstract) + (eval '(do + (import '(com.google.javascript.jscomp DependencyOptions)) + (DependencyOptions/sortOnly))) (doto (DependencyOptions.) (.setDependencySorting true))))) From 2e5790b781f18a6dba2c92a1e39be07b38cb5209 Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Fri, 30 Nov 2018 22:21:28 +0100 Subject: [PATCH 3348/4033] CLJS-2794 Return identity when with-meta is called with identical meta --- src/main/cljs/cljs/core.cljs | 140 ++++++++++++++++++----- src/test/cljs/cljs/collections_test.cljs | 4 +- 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c63b41c56..ed7c6c274 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1608,7 +1608,9 @@ reduces them without incurring seq initialization" (-meta [coll] meta) IWithMeta (-with-meta [coll new-meta] - (IndexedSeq. arr i new-meta)) + (if (identical? new-meta meta) + coll + (IndexedSeq. arr i new-meta))) ASeq ISeq @@ -1708,7 +1710,9 @@ reduces them without incurring seq initialization" (-meta [coll] meta) IWithMeta (-with-meta [coll new-meta] - (RSeq. ci i new-meta)) + (if (identical? new-meta meta) + coll + (RSeq. ci i new-meta))) ISeqable (-seq [coll] coll) @@ -3063,7 +3067,10 @@ reduces them without incurring seq initialization" (-clone [_] (List. meta first rest count __hash)) IWithMeta - (-with-meta [coll meta] (List. meta first rest count __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (List. new-meta first rest count __hash))) IMeta (-meta [coll] meta) @@ -3137,7 +3144,10 @@ reduces them without incurring seq initialization" (-clone [_] (EmptyList. meta)) IWithMeta - (-with-meta [coll meta] (EmptyList. meta)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (EmptyList. new-meta))) IMeta (-meta [coll] meta) @@ -3240,7 +3250,10 @@ reduces them without incurring seq initialization" (-clone [_] (Cons. meta first rest __hash)) IWithMeta - (-with-meta [coll meta] (Cons. meta first rest __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (Cons. new-meta first rest __hash))) IMeta (-meta [coll] meta) @@ -3436,7 +3449,10 @@ reduces them without incurring seq initialization" (not fn)) IWithMeta - (-with-meta [coll meta] (LazySeq. meta #(-seq coll) nil __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (LazySeq. new-meta #(-seq coll) nil __hash))) IMeta (-meta [coll] meta) @@ -3554,8 +3570,10 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x start)) IWithMeta - (-with-meta [coll m] - (ChunkedCons. chunk more m __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (ChunkedCons. chunk more new-meta __hash))) IMeta (-meta [coll] meta) @@ -4832,7 +4850,10 @@ reduces them without incurring seq initialization" (some? current)) IWithMeta - (-with-meta [coll meta] (Cycle. meta all prev current _next)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (Cycle. new-meta all prev current _next))) IMeta (-meta [coll] meta) @@ -4904,7 +4925,10 @@ reduces them without incurring seq initialization" (-realized? [coll] false) IWithMeta - (-with-meta [coll meta] (Repeat. meta count val next nil)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (Repeat. new-meta count val next nil))) IMeta (-meta [coll] meta) @@ -5010,7 +5034,10 @@ reduces them without incurring seq initialization" (not (identical? seed UNREALIZED-SEED))) IWithMeta - (-with-meta [coll meta] (Iterate. meta f prev-seed seed next)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (Iterate. new-meta f prev-seed seed next))) IMeta (-meta [coll] meta) @@ -5476,7 +5503,10 @@ reduces them without incurring seq initialization" (-clone [_] (PersistentVector. meta cnt shift root tail __hash)) IWithMeta - (-with-meta [coll meta] (PersistentVector. meta cnt shift root tail __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentVector. new-meta cnt shift root tail __hash))) IMeta (-meta [coll] meta) @@ -5718,8 +5748,10 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x start)) IWithMeta - (-with-meta [coll m] - (chunked-seq vec node i off m)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (chunked-seq vec node i off new-meta))) IMeta (-meta [coll] meta) @@ -5813,7 +5845,10 @@ reduces them without incurring seq initialization" (-clone [_] (Subvec. meta v start end __hash)) IWithMeta - (-with-meta [coll meta] (build-subvec meta v start end __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (build-subvec new-meta v start end __hash))) IMeta (-meta [coll] meta) @@ -6172,7 +6207,10 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x start)) IWithMeta - (-with-meta [coll meta] (PersistentQueueSeq. meta front rear __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentQueueSeq. new-meta front rear __hash))) IMeta (-meta [coll] meta) @@ -6234,7 +6272,10 @@ reduces them without incurring seq initialization" (PersistentQueueIter. front (-iterator rear))) IWithMeta - (-with-meta [coll meta] (PersistentQueue. meta count front rear __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentQueue. new-meta count front rear __hash))) IMeta (-meta [coll] meta) @@ -6364,7 +6405,10 @@ reduces them without incurring seq initialization" (-equiv this other)) IWithMeta - (-with-meta [coll meta] (ObjMap. meta keys strobj update-count __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (ObjMap. new-meta keys strobj update-count __hash))) IMeta (-meta [coll] meta) @@ -6710,7 +6754,9 @@ reduces them without incurring seq initialization" IWithMeta (-with-meta [coll new-meta] - (PersistentArrayMapSeq. arr i new-meta)) + (if (identical? new-meta _meta) + coll + (PersistentArrayMapSeq. arr i new-meta))) ICounted (-count [coll] @@ -6794,7 +6840,10 @@ reduces them without incurring seq initialization" (-clone [_] (PersistentArrayMap. meta cnt arr __hash)) IWithMeta - (-with-meta [coll meta] (PersistentArrayMap. meta cnt arr __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentArrayMap. new-meta cnt arr __hash))) IMeta (-meta [coll] meta) @@ -7664,7 +7713,10 @@ reduces them without incurring seq initialization" (-meta [coll] meta) IWithMeta - (-with-meta [coll meta] (NodeSeq. meta nodes i s __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (NodeSeq. new-meta nodes i s __hash))) ICollection (-conj [coll o] (cons o coll)) @@ -7742,7 +7794,10 @@ reduces them without incurring seq initialization" (-meta [coll] meta) IWithMeta - (-with-meta [coll meta] (ArrayNodeSeq. meta nodes i s __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (ArrayNodeSeq. new-meta nodes i s __hash))) ICollection (-conj [coll o] (cons o coll)) @@ -7835,7 +7890,10 @@ reduces them without incurring seq initialization" root-iter))) IWithMeta - (-with-meta [coll meta] (PersistentHashMap. meta cnt root has-nil? nil-val __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentHashMap. new-meta cnt root has-nil? nil-val __hash))) IMeta (-meta [coll] meta) @@ -8161,8 +8219,10 @@ reduces them without incurring seq initialization" (-meta [coll] meta) IWithMeta - (-with-meta [coll meta] - (PersistentTreeMapSeq. meta stack ascending? cnt __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentTreeMapSeq. new-meta stack ascending? cnt __hash))) IReduce (-reduce [coll f] (seq-reduce f coll)) @@ -8703,7 +8763,10 @@ reduces them without incurring seq initialization" (-clone [_] (PersistentTreeMap. comp tree cnt meta __hash)) IWithMeta - (-with-meta [coll meta] (PersistentTreeMap. comp tree cnt meta __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentTreeMap. comp tree cnt new-meta __hash))) IMeta (-meta [coll] meta) @@ -8892,7 +8955,10 @@ reduces them without incurring seq initialization" (-meta [coll] _meta) IWithMeta - (-with-meta [coll new-meta] (KeySeq. mseq new-meta)) + (-with-meta [coll new-meta] + (if (identical? new-meta _meta) + coll + (KeySeq. mseq new-meta))) ISeqable (-seq [coll] coll) @@ -8968,7 +9034,10 @@ reduces them without incurring seq initialization" (-meta [coll] _meta) IWithMeta - (-with-meta [coll new-meta] (ValSeq. mseq new-meta)) + (-with-meta [coll new-meta] + (if (identical? new-meta _meta) + coll + (ValSeq. mseq new-meta))) ISeqable (-seq [coll] coll) @@ -9105,7 +9174,10 @@ reduces them without incurring seq initialization" (HashSetIter. (-iterator hash-map))) IWithMeta - (-with-meta [coll meta] (PersistentHashSet. meta hash-map __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentHashSet. new-meta hash-map __hash))) IMeta (-meta [coll] meta) @@ -9259,7 +9331,10 @@ reduces them without incurring seq initialization" (-clone [_] (PersistentTreeSet. meta tree-map __hash)) IWithMeta - (-with-meta [coll meta] (PersistentTreeSet. meta tree-map __hash)) + (-with-meta [coll new-meta] + (if (identical? new-meta meta) + coll + (PersistentTreeSet. new-meta tree-map __hash))) IMeta (-meta [coll] meta) @@ -9622,7 +9697,10 @@ reduces them without incurring seq initialization" (-clone [_] (Range. meta start end step chunk chunk-next __hash)) IWithMeta - (-with-meta [rng meta] (Range. meta start end step chunk chunk-next __hash)) + (-with-meta [rng new-meta] + (if (identical? new-meta meta) + rng + (Range. new-meta start end step chunk chunk-next __hash))) IMeta (-meta [rng] meta) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 75dcada3f..8077f125b 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -1006,12 +1006,12 @@ (deftest test-cljs-2442 (testing "set ctor" (let [coll #{1 2}] - (is (not (identical? coll (set coll))))) + (is (identical? coll (set coll)))) (is (= #{1 2} (set #{1 2}))) (is (nil? (meta (set ^:a #{1 2}))))) (testing "vec ctor" (let [coll [1 2]] - (is (not (identical? coll (vec coll))))) + (is (identical? coll (vec coll)))) (is (= [1 2] (vec [1 2]))) (is (nil? (meta (vec ^:a [1 2])))) (let [coll (vec (first {:a 1}))] From b96749902ee9a20320c507277c5a79be98ce4c4f Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Fri, 13 Jan 2017 12:13:19 +0000 Subject: [PATCH 3349/4033] CLJS-1888 - Seqs of PHMs and PAMs do not handle metadata correctly --- src/main/cljs/cljs/core.cljs | 42 +++---- src/test/cljs/cljs/collections_test.cljs | 6 +- src/test/cljs/cljs/hash_map_test.cljs | 61 ++++++++++ src/test/cljs/cljs/metadata_test.cljc | 148 +++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 + 5 files changed, 235 insertions(+), 24 deletions(-) create mode 100644 src/test/cljs/cljs/metadata_test.cljc diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ed7c6c274..6bee4bcf0 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1742,7 +1742,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -4874,7 +4874,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) ISequential ISeqable @@ -4963,7 +4963,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) @@ -5060,7 +5060,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) ISequential ISeqable @@ -6774,7 +6774,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) _meta)) + (-empty [coll] (.-EMPTY List)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -6785,13 +6785,13 @@ reduces them without incurring seq initialization" (-rest [coll] (if (< i (- (alength arr) 2)) - (PersistentArrayMapSeq. arr (+ i 2) _meta) + (PersistentArrayMapSeq. arr (+ i 2) nil) ())) INext (-next [coll] (when (< i (- (alength arr) 2)) - (PersistentArrayMapSeq. arr (+ i 2) _meta))) + (PersistentArrayMapSeq. arr (+ i 2) nil))) IReduce (-reduce [coll f] (seq-reduce f coll)) @@ -7722,7 +7722,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) ISequential ISeq @@ -7803,18 +7803,18 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) ISequential ISeq (-first [coll] (first s)) (-rest [coll] - (let [ret (create-array-node-seq nil nodes i (next s))] + (let [ret (create-array-node-seq nodes i (next s))] (if-not (nil? ret) ret ()))) INext (-next [coll] - (create-array-node-seq nil nodes i (next s))) + (create-array-node-seq nodes i (next s))) ISeqable (-seq [this] this) @@ -7832,18 +7832,18 @@ reduces them without incurring seq initialization" (es6-iterable ArrayNodeSeq) (defn- create-array-node-seq - ([nodes] (create-array-node-seq nil nodes 0 nil)) - ([meta nodes i s] + ([nodes] (create-array-node-seq nodes 0 nil)) + ([nodes i s] (if (nil? s) (let [len (alength nodes)] (loop [j i] (if (< j len) (if-let [nj (aget nodes j)] (if-let [ns (.inode-seq nj)] - (ArrayNodeSeq. meta nodes (inc j) ns nil) + (ArrayNodeSeq. nil nodes (inc j) ns nil) (recur (inc j))) (recur (inc j)))))) - (ArrayNodeSeq. meta nodes i s nil)))) + (ArrayNodeSeq. nil nodes i s nil)))) (deftype HashMapIter [nil-val root-iter ^:mutable seen] Object @@ -8972,7 +8972,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) _meta)) + (-empty [coll] (.-EMPTY List)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -8987,7 +8987,7 @@ reduces them without incurring seq initialization" (-next mseq) (next mseq))] (if-not (nil? nseq) - (KeySeq. nseq _meta) + (KeySeq. nseq nil) ()))) INext @@ -8996,7 +8996,7 @@ reduces them without incurring seq initialization" (-next mseq) (next mseq))] (when-not (nil? nseq) - (KeySeq. nseq _meta)))) + (KeySeq. nseq nil)))) IReduce (-reduce [coll f] (seq-reduce f coll)) @@ -9724,9 +9724,9 @@ reduces them without incurring seq initialization" (-next [rng] (if (pos? step) (when (< (+ start step) end) - (Range. meta (+ start step) end step nil nil nil)) + (Range. nil (+ start step) end step nil nil nil)) (when (> (+ start step) end) - (Range. meta (+ start step) end step nil nil nil)))) + (Range. nil (+ start step) end step nil nil nil)))) IChunkedSeq (-chunked-first [rng] @@ -9746,7 +9746,7 @@ reduces them without incurring seq initialization" (-conj [rng o] (cons o rng)) IEmptyableCollection - (-empty [rng] (-with-meta (.-EMPTY List) meta)) + (-empty [rng] (-with-meta (.-EMPTY List) nil)) ISequential IEquiv diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 8077f125b..c4f335021 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -202,7 +202,7 @@ (is (= -1 (.lastIndexOf (cycle []) 19 2))) (is (= {:a 1} (meta (with-meta (cycle [1 2 3]) {:a 1})))) - (is (= {:a 1} (meta (empty (with-meta (cycle [1 2 3]) {:a 1}))))) + (is (nil? (meta (empty (with-meta (cycle [1 2 3]) {:a 1}))))) (is (= (take 7 (with-meta (cycle [1 2 3]) {:a 1})) (take 7 (cycle [1 2 3])))) (is (realized? (cycle [1 2 3]))) @@ -291,7 +291,7 @@ (is (= 3 (.lastIndexOf (repeat 7 5) 5 3))) (is (= {:a 1} (meta (with-meta (repeat 5 7) {:a 1})))) - (is (= {:a 1} (meta (empty (with-meta (repeat 5 7) {:a 1}))))) + (is (nil? (meta (empty (with-meta (repeat 5 7) {:a 1}))))) (is (= (with-meta (repeat 5 7) {:a 1}) (repeat 5 7))) (is (not (realized? (repeat 5 7)))) @@ -338,7 +338,7 @@ (is (not (realized? (rest (iterate inc 0))))) (is (= {:a 1} (meta (with-meta (iterate inc 0) {:a 1})))) - (is (= {:a 1} (meta (empty (with-meta (iterate inc 0) {:a 1}))))) + (is (nil? (meta (empty (with-meta (iterate inc 0) {:a 1}))))) (is (= (take 20 (with-meta (iterate inc 0) {:a 1})) (take 20 (iterate inc 0)))) (is (= [:first 0 1] (take 3 (conj (iterate inc 0) :first)))) diff --git a/src/test/cljs/cljs/hash_map_test.cljs b/src/test/cljs/cljs/hash_map_test.cljs index 90280c01d..3d37e761c 100644 --- a/src/test/cljs/cljs/hash_map_test.cljs +++ b/src/test/cljs/cljs/hash_map_test.cljs @@ -97,3 +97,64 @@ (let [m (array-map nil nil 1 1 2 2)] (is (every? map-entry? m)) (is (every? map-entry? (iter->set (-iterator m)))))))) + +(deftest test-cljs-1888 + (let [arr-map-seq (seq (array-map :a 1 :b 2)) + ;; small hash map will produce a NodeSeq + node-seq (seq (hash-map :a 1 :b 2 :c 3)) + ;; Large hash map will produce an ArrayNodeSeq + array-node-seq (seq (into {} + (map (fn [e] [e nil])) + (range 1000)))] + (testing "PersistentArrayMapSeq" + (is (= {:has :meta} (-> arr-map-seq + (with-meta {:has :meta}) + (meta)))) + (is (= nil (-> arr-map-seq + (with-meta {:has :meta}) + (rest) + (meta)))) + (is (= nil (-> arr-map-seq + (with-meta {:has :meta}) + (next) + (meta)))) + (is (= nil (-> arr-map-seq + (with-meta {:has :meta}) + (empty) + (meta))))) + + (testing "NodeSeq" + (is (instance? NodeSeq node-seq)) + (is (= {:has :meta} (-> node-seq + (with-meta {:has :meta}) + (meta)))) + (is (= nil (-> node-seq + (with-meta {:has :meta}) + (rest) + (meta)))) + (is (= nil (-> node-seq + (with-meta {:has :meta}) + (next) + (meta)))) + (is (= nil (-> node-seq + (with-meta {:has :meta}) + (empty) + (meta))))) + + (testing "ArrayNodeSeq" + (is (instance? ArrayNodeSeq array-node-seq)) + (is (= {:has :meta} (-> array-node-seq + (with-meta {:has :meta}) + (meta)))) + (is (= nil (-> array-node-seq + (with-meta {:has :meta}) + (rest) + (meta)))) + (is (= nil (-> array-node-seq + (with-meta {:has :meta}) + (next) + (meta)))) + (is (= nil (-> array-node-seq + (with-meta {:has :meta}) + (empty) + (meta))))))) diff --git a/src/test/cljs/cljs/metadata_test.cljc b/src/test/cljs/cljs/metadata_test.cljc new file mode 100644 index 000000000..7bf66cb5a --- /dev/null +++ b/src/test/cljs/cljs/metadata_test.cljc @@ -0,0 +1,148 @@ +;; 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.metadata-test + (:require + #?(:cljs [cljs.test :refer-macros [deftest testing is]] + :clj [clojure.test :refer [deftest testing is]]))) + +(defn seq-interface-tests + "Tests that all seqs handle metadata correctly." + [s] + (when (seq s) + (testing "seqs can have metadata" + (is #?(:clj (instance? clojure.lang.IObj s) + :cljs (satisfies? IMeta s))) + (let [m {:meta :data}] + (is (= m (meta (with-meta s m))))))) + + (when (seq s) + (let [s (with-meta s {:meta :data})] + (testing "next should have nil metadata" + (is (nil? (meta (next s))))) + (testing "rest should have nil metadata" + (is (nil? (meta (rest s))))) + (testing "empty should have nil metadata" + (is (nil? (meta (empty s)))))))) + + +(defn coll-interface-tests + "Tests that all collections handle metadata correctly" + [coll] + (testing "collections can have metadata" + (is #?(:clj (instance? clojure.lang.IObj coll) + :cljs (satisfies? IMeta coll))) + (let [m {:meta :data}] + (is (= coll (with-meta coll m))) + (is (= m (meta (with-meta coll m)))))) + + (testing "conj keeps metadata" + (let [m {:meta :data} + coll (with-meta coll m) + thing (if (map? coll) [:k :v] :x)] + (is (= m (meta (conj coll thing)))))) + + (testing "empty keeps metadata" + (let [m {:meta :data} + coll (with-meta coll m)] + (is (= m (meta (empty coll)))))) + + (testing "seq has no metadata" + (let [m {:meta :data} + coll (with-meta coll m)] + (is (nil? (meta (seq coll))))))) + + +(defn disj-interface-tests + "Tests that collections supporting disj handle metadata correctly" + [coll] + (testing "disj keeps metadata" + (let [m {:meta :data} + coll (with-meta (conj coll :k) m)] + (is (= m (meta (disj coll :k))))))) + + +(deftest metadata-tests + (testing "Collection" + (testing "PersistentVector" + (testing "Empty" + (coll-interface-tests [])) + (testing "Medium" + (coll-interface-tests [0 1 2 3])) + (testing "Large" + (coll-interface-tests (vec (range 100))))) + + (testing "PersistentHashSet" + (testing "Empty" + (coll-interface-tests (hash-set)) + (disj-interface-tests (hash-set))) + (testing "Medium" + (coll-interface-tests (hash-set 0 1 2 3 4 5)) + (disj-interface-tests (hash-set 0 1 2 3 4 5))) + (testing "Large" + (coll-interface-tests (apply hash-set (range 100))) + (disj-interface-tests (apply hash-set (range 100))))) + + (testing "PersistentHashMap" + (testing "Empty" + (coll-interface-tests (hash-map))) + (testing "Medium" + (coll-interface-tests (hash-map 0 1 2 3 4 5))) + (testing "Large" + (coll-interface-tests (apply hash-map (range 100))))) + + (testing "PersistentArrayMap" + (testing "Empty" + (coll-interface-tests (array-map))) + (testing "Medium" + (coll-interface-tests (array-map 0 1 2 3 4 5))))) + + + (testing "Seq over collections" + (testing "PersistentVector" + (testing "Empty" + (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 "Large" + (seq-interface-tests (seq (vec (range 100)))) + (seq-interface-tests (rseq (vec (range 100)))))) + + (testing "PersistentHashSet" + (testing "Empty" + (seq-interface-tests (seq (hash-set)))) + (testing "Medium" + (seq-interface-tests (seq (hash-set 0 1 2 3 4 5)))) + (testing "Large" + (seq-interface-tests (seq (apply hash-set (range 100)))))) + + (testing "PersistentHashMap" + (testing "Empty" + (seq-interface-tests (seq (hash-map)))) + (testing "Medium" + (seq-interface-tests (seq (hash-map 0 1 2 3 4 5)))) + (testing "Large" + (seq-interface-tests (seq (apply hash-map (range 100)))))) + + (testing "PersistentArrayMap" + (testing "Empty" + (seq-interface-tests (seq (array-map)))) + (testing "Medium" + (seq-interface-tests (seq (array-map 0 1 2 3 4 5)))))) + + (testing "generators" + (testing "cycle" + (seq-interface-tests (cycle [1 2 3]))) + (testing "range" + (seq-interface-tests (range 10))) + (testing "repeat" + (seq-interface-tests (repeat 10 :x))) + (testing "iterate" + (seq-interface-tests (iterate inc 0))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 0dbb3108e..349e6689c 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -39,6 +39,7 @@ [cljs.clojure-alias-test] [cljs.hash-map-test] [cljs.map-entry-test] + [cljs.metadata-test] [cljs.npm-deps-test] [cljs.predicates-test] [cljs.tagged-literals-test] @@ -83,6 +84,7 @@ 'cljs.clojure-alias-test 'cljs.hash-map-test 'cljs.map-entry-test + 'cljs.metadata-test 'cljs.npm-deps-test 'cljs.pprint-test 'cljs.predicates-test From 23ab9a095599446e3c2aa5013d2b8edf2bbe467f Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sun, 2 Dec 2018 16:51:59 +0100 Subject: [PATCH 3350/4033] Add support for protocols via metadata --- src/main/clojure/cljs/core.cljc | 82 ++++++++++++++++++++++--------- src/test/cljs/cljs/core_test.cljs | 23 +++++++++ 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 75029474f..3c58201fb 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2022,14 +2022,29 @@ => 17" [psym & doc+methods] (core/let [p (:name (cljs.analyzer/resolve-var (dissoc &env :locals) psym)) - [doc methods] (if (core/string? (first doc+methods)) - [(first doc+methods) (next doc+methods)] - [nil doc+methods]) - psym (vary-meta psym assoc - :doc doc - :protocol-symbol true) + [opts methods] + (core/loop [opts {:protocol-symbol true} + methods [] + sigs doc+methods] + (core/if-not (seq sigs) + [opts methods] + (core/let [[head & tail] sigs] + (core/cond + (core/string? head) + (recur (assoc opts :doc head) methods tail) + (core/keyword? head) + (recur (assoc opts head (first tail)) methods (rest tail)) + (core/list? head) + (recur opts (conj methods head) tail) + :else + (throw #?(:clj (Exception. + (core/str "Invalid protocol, " psym " received unexpected argument")) + :cljs (js/Error. + (core/str "Invalid protocol, " psym " received unexpected argument")))) + )))) + psym (vary-meta psym merge opts) ns-name (core/-> &env :ns :name) - fqn (core/fn [n] (symbol (core/str ns-name "." n))) + fqn (core/fn [n] (symbol (core/str ns-name) (core/str n))) prefix (protocol-prefix p) _ (core/doseq [[mname & arities] methods] (core/when (some #{0} (map count (filter vector? arities))) @@ -2047,21 +2062,44 @@ (core/symbol? arg) arg (core/and (map? arg) (core/some? (:as arg))) (:as arg) :else (gensym))) sig) - sig)] - `(~sig - (if (and (not (nil? ~(first sig))) - (not (nil? (. ~(first sig) ~(symbol (core/str "-" slot)))))) ;; Property access needed here. - (. ~(first sig) ~slot ~@sig) - (let [x# (if (nil? ~(first sig)) nil ~(first sig)) - m# (unchecked-get ~(fqn fname) (goog/typeOf x#))] - (if-not (nil? m#) - (m# ~@sig) - (let [m# (unchecked-get ~(fqn fname) "_")] - (if-not (nil? m#) - (m# ~@sig) - (throw - (missing-protocol - ~(core/str psym "." fname) ~(first sig))))))))))) + sig) + + fqn-fname (fqn fname) + fsig (first sig) + + ;; construct protocol checks in reverse order + ;; check the.protocol/fn["_"] for default impl last + check + `(let [m# (unchecked-get ~fqn-fname "_")] + (if-not (nil? m#) + (m# ~@sig) + (throw + (missing-protocol + ~(core/str psym "." fname) ~fsig)))) + + ;; then check protocol fn in metadata (only when protocol is marked with :extend-via-metadata true) + check + (core/if-not (:extend-via-metadata opts) + check + `(if-let [meta-impl# (-> ~fsig (core/meta) (core/get '~fqn-fname))] + (meta-impl# ~@sig) + ~check)) + + ;; then check protocol on js string,function,array,object + check + `(let [x# (if (nil? ~fsig) nil ~fsig) + m# (unchecked-get ~fqn-fname (goog/typeOf x#))] + (if-not (nil? m#) + (m# ~@sig) + ~check)) + + ;; then check protocol property on object (first check actually executed) + check + `(if (and (not (nil? ~fsig)) + (not (nil? (. ~fsig ~(symbol (core/str "-" slot)))))) ;; Property access needed here. + (. ~fsig ~slot ~@sig) + ~check)] + `(~sig ~check))) psym (core/-> psym (vary-meta update-in [:jsdoc] conj "@interface") diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 514429a22..312f43728 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1739,3 +1739,26 @@ (is (== 2 (count (js-keys o)))) (is (== 17 (gobject/get o "a"))) (is (== 27 (gobject/get o "b"))))) + +(defprotocol ExtMetaProtocol + :extend-via-metadata true + (ext-meta-protocol [x])) + +(defprotocol NonMetaProtocol + (non-meta-protocol [x])) + +(defrecord SomeMetaImpl [x] + ExtMetaProtocol + (ext-meta-protocol [_] x) + NonMetaProtocol + (non-meta-protocol [_] x)) + +(deftest test-cljs-2960 + ;; protocol impl via metadata + (is (= 1 (ext-meta-protocol (with-meta {} {`ext-meta-protocol (fn [_] 1)})))) + ;; actual impl before metadata + (is (= 2 (ext-meta-protocol (with-meta (SomeMetaImpl. 2) {`ext-meta-protocol (fn [_] 1)})))) + ;; protocol not marked as :extend-via-metadata so fallthrough to no impl + (is (thrown? js/Error (non-meta-protocol (with-meta {} {`non-meta-protocol (fn [_] 1)})))) + ;; normal impl call just in case + (is (= 2 (non-meta-protocol (with-meta (SomeMetaImpl. 2) {`non-meta-protocol (fn [_] 1)}))))) \ No newline at end of file From 70c4abfffc7f8a442bf1b9cfb949e76268565768 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Mon, 3 Dec 2018 21:34:49 +0000 Subject: [PATCH 3351/4033] CLJS-3000: Don't pass meta to next/rest/empty of seqs * Fix metadata on ChunkedCons, ValSeq and PersistentTreeMapSeq. * Remove multiples useless calls to with-meta when constructing EmptyLists introduced in CLJS-1888 --- src/main/cljs/cljs/core.cljs | 16 ++++++++-------- src/test/cljs/cljs/metadata_test.cljc | 21 ++++++++++++++++++--- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6bee4bcf0..d60a80fb1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3590,7 +3590,7 @@ reduces them without incurring seq initialization" (-first [coll] (-nth chunk 0)) (-rest [coll] (if (> (-count chunk) 1) - (ChunkedCons. (-drop-first chunk) more meta nil) + (ChunkedCons. (-drop-first chunk) more nil nil) (if (nil? more) () more))) @@ -3598,7 +3598,7 @@ reduces them without incurring seq initialization" INext (-next [coll] (if (> (-count chunk) 1) - (ChunkedCons. (-drop-first chunk) more meta nil) + (ChunkedCons. (-drop-first chunk) more nil nil) (when-not (nil? more) (-seq more)))) @@ -3620,7 +3620,7 @@ reduces them without incurring seq initialization" (cons o this)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash))) @@ -8210,7 +8210,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) IHash (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) @@ -9051,7 +9051,7 @@ reduces them without incurring seq initialization" (cons o coll)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) _meta)) + (-empty [coll] (.-EMPTY List)) IHash (-hash [coll] (hash-ordered-coll coll)) @@ -9066,7 +9066,7 @@ reduces them without incurring seq initialization" (-next mseq) (next mseq))] (if-not (nil? nseq) - (ValSeq. nseq _meta) + (ValSeq. nseq nil) ()))) INext @@ -9075,7 +9075,7 @@ reduces them without incurring seq initialization" (-next mseq) (next mseq))] (when-not (nil? nseq) - (ValSeq. nseq _meta)))) + (ValSeq. nseq nil)))) IReduce (-reduce [coll f] (seq-reduce f coll)) @@ -9746,7 +9746,7 @@ reduces them without incurring seq initialization" (-conj [rng o] (cons o rng)) IEmptyableCollection - (-empty [rng] (-with-meta (.-EMPTY List) nil)) + (-empty [rng] (.-EMPTY List)) ISequential IEquiv diff --git a/src/test/cljs/cljs/metadata_test.cljc b/src/test/cljs/cljs/metadata_test.cljc index 7bf66cb5a..4379da18f 100644 --- a/src/test/cljs/cljs/metadata_test.cljc +++ b/src/test/cljs/cljs/metadata_test.cljc @@ -129,13 +129,24 @@ (testing "Medium" (seq-interface-tests (seq (hash-map 0 1 2 3 4 5)))) (testing "Large" - (seq-interface-tests (seq (apply hash-map (range 100)))))) + (seq-interface-tests (seq (apply hash-map (range 100))))) + (testing "KeySeq" + (seq-interface-tests (keys (apply hash-map (range 10))))) + (testing "ValSeq" + (seq-interface-tests (vals (apply hash-map (range 10)))))) (testing "PersistentArrayMap" (testing "Empty" (seq-interface-tests (seq (array-map)))) (testing "Medium" - (seq-interface-tests (seq (array-map 0 1 2 3 4 5)))))) + (seq-interface-tests (seq (array-map 0 1 2 3 4 5)))) + (testing "KeySeq" + (seq-interface-tests (keys (apply array-map (range 10))))) + (testing "ValSeq" + (seq-interface-tests (vals (apply array-map (range 10)))))) + + (testing "PersistentTreeMap" + (seq-interface-tests (seq (sorted-map :a 1 :b 2 :c 3))))) (testing "generators" (testing "cycle" @@ -145,4 +156,8 @@ (testing "repeat" (seq-interface-tests (repeat 10 :x))) (testing "iterate" - (seq-interface-tests (iterate inc 0))))) + (seq-interface-tests (iterate inc 0)))) + + (testing "ChunkedCons" + (let [chunked-cons (seq (map inc (vec (range 100))))] + (seq-interface-tests chunked-cons)))) From e523cfa290e9ceb30570f391c6586182fd08d01d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 3 Dec 2018 18:27:23 -0500 Subject: [PATCH 3352/4033] CLJS-3001: Need CI to clone enough commits to find last major tag --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3bc0b966e..1a7c33258 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ dist: trusty git: - depth: 500 + depth: 1000 language: node_js From b38ad7e6a5edcb901db96d44ef2b44357253bf31 Mon Sep 17 00:00:00 2001 From: Enzzo Cavallo Date: Mon, 5 Nov 2018 08:51:09 -0200 Subject: [PATCH 3353/4033] CLJS-2958 - make symbol work on keywords and vars --- src/main/cljs/cljs/core.cljs | 29 ++++++++++++++++------------- src/test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d60a80fb1..0d5215205 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1098,16 +1098,24 @@ defaults to returning v.")) IPrintWithWriter (-pr-writer [o writer _] (-write writer str))) +(defn var? + "Returns true if v is of type cljs.core.Var" + [v] + (instance? cljs.core.Var v)) + (defn symbol - "Returns a Symbol with the given namespace and name." + "Returns a Symbol with the given namespace and name. Arity-1 works + on strings, keywords, and vars." ([name] - (if (symbol? name) - name - (let [idx (.indexOf name "/")] - (if (< idx 1) - (symbol nil name) - (symbol (.substring name 0 idx) - (.substring name (inc idx) (. name -length))))))) + (cond (symbol? name) name + (string? name) (let [idx (.indexOf name "/")] + (if (< idx 1) + (symbol nil name) + (symbol (.substring name 0 idx) + (.substring name (inc idx) (. name -length))))) + (var? name) (.-sym name) + (keyword? name) (recur (.-fqn name)) + :else (throw (new js/Error "no conversion to symbol")))) ([ns name] (let [sym-str (if-not (nil? ns) (str ns "/" name) @@ -1182,11 +1190,6 @@ defaults to returning v.")) (-invoke [_ a b c d e f g h i j k l m n o p q r s t rest] (apply (val) a b c d e f g h i j k l m n o p q r s t rest))) -(defn var? - "Returns true if v is of type cljs.core.Var" - [v] - (instance? cljs.core.Var v)) - ;;;;;;;;;;;;;;;;;;; fundamentals ;;;;;;;;;;;;;;; (declare array-seq prim-seq IndexedSeq) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 312f43728..f68168f02 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1725,6 +1725,11 @@ (is (= "#object[cljs.core.Atom {:val 1}]" (pr-str (atom 1)))) (is (= "#object[cljs.core.Volatile {:val 2}]" (pr-str (volatile! 2))))) +(deftest test-cljs-2944 + (is (= (symbol :foo/bar) 'foo/bar)) + (is (= (symbol (->Var nil 'bar/foo nil)) 'bar/foo)) + (is (thrown? js/Error (symbol 1)))) + (deftest test-cljs-2991 (let [o (js-obj)] (is (object? o)) From a93859a4c0f723ae3fdec65aca16e963e017d794 Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Wed, 5 Dec 2018 10:11:07 +0000 Subject: [PATCH 3354/4033] CLJS-3005: empty on Cons shouldn't keep metadata Also applying cons to nil, eg: (cons 'x nil) now produces a List as per Clojure rather than a Cons. This is important when trying to handle metadata correctly as a List is a collection and thus preserves metadata when emptied or conj'ed. --- src/main/cljs/cljs/core.cljs | 10 +++++----- src/test/cljs/cljs/metadata_test.cljc | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0d5215205..4534824e7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3274,7 +3274,7 @@ reduces them without incurring seq initialization" (-conj [coll o] (Cons. nil o coll nil)) IEmptyableCollection - (-empty [coll] (-with-meta (.-EMPTY List) meta)) + (-empty [coll] (.-EMPTY List)) ISequential IEquiv @@ -3295,10 +3295,10 @@ reduces them without incurring seq initialization" (defn cons "Returns a new seq where x is the first element and coll is the rest." [x coll] - (if (or (nil? coll) - (implements? ISeq coll)) - (Cons. nil x coll nil) - (Cons. nil x (seq coll) nil))) + (cond + (nil? coll) (List. nil x nil 1 nil) + (implements? ISeq coll) (Cons. nil x coll nil) + :default (Cons. nil x (seq coll) nil))) (defn hash-keyword [k] (int (+ (hash-symbol k) 0x9e3779b9))) diff --git a/src/test/cljs/cljs/metadata_test.cljc b/src/test/cljs/cljs/metadata_test.cljc index 4379da18f..b22c79612 100644 --- a/src/test/cljs/cljs/metadata_test.cljc +++ b/src/test/cljs/cljs/metadata_test.cljc @@ -160,4 +160,8 @@ (testing "ChunkedCons" (let [chunked-cons (seq (map inc (vec (range 100))))] - (seq-interface-tests chunked-cons)))) + (seq-interface-tests chunked-cons))) + + (testing "Cons" + (seq-interface-tests (cons 'a ())) + (seq-interface-tests (cons 'b (cons 'a ()))))) From 820e6fa0fd10a5ac9ce2606e955debfc82934454 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 2 Dec 2018 15:46:25 -0500 Subject: [PATCH 3355/4033] CLJS-2913: improvements to exception messages and printing --- src/main/clojure/cljs/analyzer.cljc | 55 +++++-- src/main/clojure/cljs/analyzer/macros.clj | 7 +- src/main/clojure/cljs/closure.clj | 55 ++++--- src/main/clojure/cljs/compiler.cljc | 14 +- src/main/clojure/cljs/js_deps.cljc | 4 +- src/main/clojure/cljs/module_graph.cljc | 19 ++- src/main/clojure/cljs/repl.cljc | 182 +++++++++++++++++++++- src/main/clojure/cljs/support.cljc | 4 +- src/main/clojure/cljs/util.cljc | 3 + src/test/clojure/cljs/analyzer_tests.clj | 76 ++++----- src/test/clojure/cljs/build_api_tests.clj | 2 +- src/test/clojure/cljs/test_util.clj | 19 +++ src/test/self/self_host/test.cljs | 6 +- 13 files changed, 346 insertions(+), 100 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ac53aa21b..ee53e2273 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -692,6 +692,12 @@ screen location navigator history location global process require module exports)))})) +(defn- source-info->error-data + [{:keys [file line column]}] + {:clojure.error/source file + :clojure.error/line line + :clojure.error/column column}) + (defn source-info ([env] (when (:line env) @@ -716,6 +722,20 @@ (doseq [handler *cljs-warning-handlers*] (handler warning-type env extra))) +(defn- error-data + ([env phase] + (error-data env phase nil)) + ([env phase symbol] + (merge (-> (source-info env) source-info->error-data) + {:clojure.error/phase phase} + (when symbol + {:clojure.error/symbol symbol})))) + +(defn- compile-syntax-error + [env msg symbol] + (ex-info nil (error-data env :compile-syntax-check symbol) + #?(:clj (RuntimeException. ^String msg) :cljs (js/Error. msg)))) + (defn error ([env msg] (error env msg nil)) @@ -729,14 +749,20 @@ [ex] (= :cljs/analysis-error (:tag (ex-data ex)))) +(defn has-error-data? + #?(:cljs {:tag boolean}) + [ex] + (contains? (ex-data ex) :clojure.error/phase)) + #?(:clj (defmacro wrapping-errors [env & body] `(try ~@body (catch Throwable err# - (if (analysis-error? err#) - (throw err#) - (throw (error ~env (.getMessage err#) err#))))))) + (cond + (has-error-data? err#) (throw err#) + (analysis-error? err#) (throw (ex-info nil (error-data ~env :compilation) err#)) + :else (throw (ex-info nil (error-data ~env :compilation) (error ~env (.getMessage err#) err#)))))))) ;; namespaces implicit to the inclusion of cljs.core (def implicit-nses '#{goog goog.object goog.string goog.array Math String}) @@ -1584,9 +1610,9 @@ (defmethod parse 'if [op env [_ test then else :as form] name _] (when (< (count form) 3) - (throw (error env "Too few arguments to if"))) + (throw (compile-syntax-error env "Too few arguments to if" 'if))) (when (> (count form) 4) - (throw (error env "Too many arguments to if"))) + (throw (compile-syntax-error env "Too many arguments to if" 'if))) (let [test-expr (disallowing-recur (analyze (assoc env :context :expr) test)) then-expr (allowing-redef (analyze (add-predicate-induced-tags env test) then)) else-expr (allowing-redef (analyze env else))] @@ -3698,14 +3724,21 @@ (when (some? (find-ns-obj 'cljs.spec.alpha)) @cached-var)))) +(defn- var->sym [var] + #?(:clj (symbol (str (.-ns ^clojure.lang.Var var)) (str (.-sym ^clojure.lang.Var var))) + :cljs (.-sym var))) + (defn- do-macroexpand-check - [form mac-var] + [env form mac-var] (when (not (-> @env/*compiler* :options :spec-skip-macros)) (let [mchk #?(:clj (some-> (find-ns 'clojure.spec.alpha) (ns-resolve 'macroexpand-check)) :cljs (get-macroexpand-check-var))] (when (some? mchk) - (mchk mac-var (next form)))))) + (try + (mchk mac-var (next form)) + (catch #?(:clj Throwable :cljs :default) e + (throw (ex-info nil (error-data env :macro-syntax-check (var->sym mac-var)) e)))))))) (defn macroexpand-1* [env form] @@ -3713,17 +3746,19 @@ (if (contains? specials op) (do (when (= 'ns op) - (do-macroexpand-check form (get-expander 'cljs.core/ns-special-form env))) + (do-macroexpand-check env form (get-expander 'cljs.core/ns-special-form env))) form) ;else (if-some [mac-var (when (symbol? op) (get-expander op env))] (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) - (do-macroexpand-check form mac-var) + (do-macroexpand-check env form mac-var) (let [form' (try (apply @mac-var form env (rest form)) #?(:clj (catch ArityException e - (throw (ArityException. (- (.actual e) 2) (.name e))))))] + (throw (ArityException. (- (.actual e) 2) (.name e))))) + (catch #?(:clj Throwable :cljs :default) e + (throw (ex-info nil (error-data env :macroexpansion (var->sym mac-var)) e))))] (if #?(:clj (seq? form') :cljs (cljs-seq? form')) (let [sym' (first form') sym (first form)] diff --git a/src/main/clojure/cljs/analyzer/macros.clj b/src/main/clojure/cljs/analyzer/macros.clj index 93a0bb828..216670d52 100644 --- a/src/main/clojure/cljs/analyzer/macros.clj +++ b/src/main/clojure/cljs/analyzer/macros.clj @@ -39,9 +39,10 @@ `(try ~@body (catch :default err# - (if (cljs.analyzer/analysis-error? err#) - (throw err#) - (throw (cljs.analyzer/error ~env (.-message err#) err#)))))) + (cond + (cljs.analyzer/has-error-data? err#) (throw err#) + (cljs.analyzer/analysis-error? err#) (throw (ex-info nil (cljs.analyzer/error-data ~env :compilation) err#)) + :else (throw (ex-info nil (cljs.analyzer/error-data ~env :compilation) (cljs.analyzer/error ~env (.getMessage err#) err#))))))) (defmacro disallowing-recur [& body] `(cljs.core/binding [cljs.analyzer/*recur-frames* diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5baa90f3f..c8f5e6f55 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -86,7 +86,8 @@ _ (when (nil? ns) (throw (ex-info (str kw " symbol " sym " is not fully qualified") - (merge ex-data {kw sym})))) + (merge ex-data {kw sym + :clojure.error/phase :compilation})))) var-ns (symbol ns)] (when (not (find-ns var-ns)) (try @@ -94,7 +95,8 @@ (require var-ns)) (catch Throwable t (throw (ex-info (str "Cannot require namespace referred by " kw " value " sym) - (merge ex-data {kw sym}) + (merge ex-data {kw sym + :clojure.error/phase :compilation}) t))))) (find-var sym))) @@ -227,7 +229,7 @@ (ex-info (str "Invalid :closure-output-charset " charset " given, only " (string/join ", " (keys string->charset)) " supported ") - {})))) + {:clojure.error/phase :compilation})))) (defn ^CompilerOptions$LanguageMode lang-key->lang-mode [key] (case (keyword (string/replace (name key) #"^es" "ecmascript")) @@ -261,7 +263,7 @@ :off AnonymousFunctionNamingPolicy/OFF :unmapped AnonymousFunctionNamingPolicy/UNMAPPED :mapped AnonymousFunctionNamingPolicy/MAPPED - (throw (IllegalArgumentException. (str "Invalid :anon-fn-naming-policy value " policy " - only :off, :unmapped, :mapped permitted"))))))) + (throw (util/compilation-error (IllegalArgumentException. (str "Invalid :anon-fn-naming-policy value " policy " - only :off, :unmapped, :mapped permitted")))))))) (when-let [lang-key (:language-in opts :ecmascript5)] (.setLanguageIn compiler-options (lang-key->lang-mode lang-key))) @@ -369,8 +371,8 @@ [{:keys [externs use-only-custom-externs target ups-externs infer-externs] :as opts}] (let [validate (fn validate [p us] (if (empty? us) - (throw (IllegalArgumentException. - (str "Extern " p " does not exist"))) + (throw (util/compilation-error (IllegalArgumentException. + (str "Extern " p " does not exist")))) us)) filter-cp-js (fn [paths] (for [p paths @@ -413,7 +415,7 @@ (doseq [next (seq warnings)] (println "WARNING:" (.toString ^JSError next))) (when (seq errors) - (throw (Exception. "Closure compilation failed")))))) + (throw (util/compilation-error (Exception. "Closure compilation failed"))))))) ;; Protocols for IJavaScript and Compilable ;; ======================================== @@ -876,10 +878,11 @@ (io/resource relpath)))] {:relative-path relpath :uri js-res :ext :js} (throw - (IllegalArgumentException. - (str "Namespace " ns " does not exist." - (when (string/includes? ns "-") - " Please check that namespaces with dashes use underscores in the ClojureScript file name."))))))))))))) + (util/compilation-error + (IllegalArgumentException. + (str "Namespace " ns " does not exist." + (when (string/includes? ns "-") + " Please check that namespaces with dashes use underscores in the ClojureScript file name.")))))))))))))) (defn cljs-dependencies "Given a list of all required namespaces, return a list of @@ -1284,8 +1287,8 @@ (swap! used into entries) (into ret unused)) (throw - (IllegalArgumentException. - (str "Could not find matching namespace for " entry-sym))))) + (util/compilation-error (IllegalArgumentException. + (str "Could not find matching namespace for " entry-sym)))))) [] entries) foreign-deps (atom [])] ;; add inputs to module @@ -1304,8 +1307,8 @@ (when (:verbose opts) (util/debug-prn " module" name "depends on" dep)) (.addDependency js-module ^JSModule parent-module)) - (throw (IllegalArgumentException. - (str "Parent module " dep " does not exist"))))) + (throw (util/compilation-error (IllegalArgumentException. + (str "Parent module " dep " does not exist")))))) (conj ret [name (assoc module-desc :closure-module js-module @@ -2052,7 +2055,7 @@ (deps/-closure-lib? js) (deps/-foreign? js))) (catch Throwable t - (throw (Exception. (str "Could not write JavaScript " (pr-str js))))))) + (throw (util/compilation-error (Exception. (str "Could not write JavaScript " (pr-str js)))))))) (defn source-on-disk "Ensure that the given IJavaScript exists on disk in the output directory. @@ -2160,8 +2163,8 @@ (:url-min ijs)) (:url ijs))] (slurp url) - (throw (IllegalArgumentException. - (str "Foreign lib " ijs " does not exist")))))] + (throw (util/compilation-error (IllegalArgumentException. + (str "Foreign lib " ijs " does not exist"))))))] (str (string/join "\n" (map to-js-str sources)) "\n"))) (defn add-wrapper [{:keys [output-wrapper] :as opts} js] @@ -2689,7 +2692,8 @@ (catch Throwable t (throw (ex-info (str "Error running preprocessing function " preprocess) {:file (:file js-module) - :preprocess preprocess} + :preprocess preprocess + :clojure.error/phase :compilation} t))))) :else @@ -2772,19 +2776,22 @@ (let [new-mappings (reader/read {:eof nil :read-cond :allow} rdr)] (when (not (map? new-mappings)) (throw (ex-info (str "Not a valid data-reader map") - {:url url}))) + {:url url + :clojure.error/phase :compilation}))) (reduce (fn [m [k v]] (when (not (symbol? k)) (throw (ex-info (str "Invalid form in data-reader file") {:url url - :form k}))) + :form k + :clojure.error/phase :compilation}))) (when (and (contains? mappings k) (not= (mappings k) v)) (throw (ex-info "Conflicting data-reader mapping" {:url url :conflict k - :mappings m}))) + :mappings m + :clojure.error/phase :compilation}))) (assoc m k v)) mappings new-mappings))))) @@ -3241,8 +3248,8 @@ "js" (cond-> (:provides (parse-js-ns src)) (not all-provides) first) (throw - (IllegalArgumentException. - (str "Can't create goog.require expression for " src))))] + (util/compilation-error (IllegalArgumentException. + (str "Can't create goog.require expression for " src)))))] (if (and (not all-provides) wrap) (if (:reload options) (str "goog.require(\"" goog-ns "\", true);") diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b9ff5bce5..92593fadb 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -304,7 +304,8 @@ (ex-info (str "failed compiling constant: " x "; " (pr-str (type x)) " is not a valid ClojureScript constant.") {:constant x - :type (type x)}))) + :type (type x) + :clojure.error/phase :compilation}))) (defmethod emit-constant* nil [x] (emits "null")) @@ -1398,8 +1399,8 @@ (clojure.string/replace file-str #"\.cljc$" ".js")) :else - (throw (IllegalArgumentException. - (str "Invalid source file extension " file-str)))))) + (throw (util/compilation-error (IllegalArgumentException. + (str "Invalid source file extension " file-str))))))) #?(:clj (defn with-core-cljs @@ -1702,8 +1703,8 @@ (with-core-cljs opts (fn [] (ana/analyze-file src-file opts)))) (assoc ns-info :out-file (.toString dest-file))))) (catch Exception e - (throw (ex-info (str "failed compiling file:" src) {:file src} e)))) - (throw (java.io.FileNotFoundException. (str "The file " src " does not exist."))))))))) + (throw (ex-info (str "failed compiling file:" src) {:file src :clojure.error/phase :compilation} e)))) + (throw (util/compilation-error (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))))) #?(:clj (defn cljs-files-in @@ -1788,7 +1789,8 @@ :else (throw (ex-info (str "Cannot emit constant for type " (type sym)) - {:error :invalid-constant-type}))) + {:error :invalid-constant-type + :clojure.error/phase :compilation}))) (emits ";\n")))) #?(:clj diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 99e03e374..859ae96fa 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -179,8 +179,8 @@ case." (if-let [file (get-file dep index')] (update-in index' [file] lib-spec-merge dep) (throw - (Exception. - (str "No :file provided for :foreign-libs spec " (pr-str dep))))) + (util/compilation-error (Exception. + (str "No :file provided for :foreign-libs spec " (pr-str dep)))))) (assoc index' (:file dep) dep)))) {} deps)) diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index bca828a4f..8bb64578d 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -10,7 +10,8 @@ (:require [clojure.string :as string] [clojure.set :as set] [clojure.java.io :as io] - [cljs.compiler :as comp])) + [cljs.compiler :as comp] + [cljs.util :as util])) (defn find-sources-for-module-entry "Given an entry as a symbol, find all matching inputs in sources. If the @@ -124,7 +125,8 @@ (ex-info (str "Circular dependency detected " (apply str (interpose " -> " (conj path ns')))) - {:cljs.closure/error :invalid-inputs})) + {:cljs.closure/error :invalid-inputs + :clojure.error/phase :compilation})) (when-not (contains? @validated ns) (validate-inputs* indexed (conj path ns') (conj seen ns') validated)))) (swap! validated conj ns))) @@ -176,7 +178,7 @@ [entry indexed-inputs] (if-let [entry (get indexed-inputs (-> entry comp/munge str))] (-> (:provides entry) first comp/munge str) - (throw (Exception. (str "No input matching \"" entry "\""))))) + (throw (util/compilation-error (Exception. (str "No input matching \"" entry "\"")))))) (defn validate-modules "Check that a compiler :modules map does not contain user supplied duplicates. @@ -189,10 +191,11 @@ (let [seen' @seen] (if-some [module-name' (get seen' entry)] (throw - (Exception. - (str "duplicate entry \"" entry "\", occurs in " module-name - " and " module-name' ". entry :provides is " - (get-in indexed-inputs [entry :provides])))) + (util/compilation-error + (Exception. + (str "duplicate entry \"" entry "\", occurs in " module-name + " and " module-name' ". entry :provides is " + (get-in indexed-inputs [entry :provides]))))) (swap! seen assoc entry module-name)))))))) (defn inputs->assigned-modules @@ -353,7 +356,7 @@ (comp get-uri get-rel-path (fn [{:keys [out-file] :as ijs}] (if-not out-file - (throw (Exception. (str "No :out-file for IJavaScript " (pr-str ijs)))) + (throw (util/compilation-error (Exception. (str "No :out-file for IJavaScript " (pr-str ijs))))) out-file)) #(maybe-add-out-file % opts))) (distinct)) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index f72c666cf..f1ef49ba6 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -809,6 +809,174 @@ (defn repl-prompt [] (print (str ana/*cljs-ns* "=> "))) +(defn demunge + "Given a string representation of a fn class, + as in a stack trace element, returns a readable version." + [fn-name] + (clojure.lang.Compiler/demunge fn-name)) + +(def ^:private core-namespaces + #{"clojure.core" "clojure.core.reducers" "clojure.core.protocols" "clojure.data" "clojure.datafy" + "clojure.edn" "clojure.instant" "clojure.java.io" "clojure.main" "clojure.pprint" "clojure.reflect" + "clojure.repl" "clojure.set" "clojure.spec.alpha" "clojure.spec.gen.alpha" "clojure.spec.test.alpha" + "clojure.string" "clojure.template" "clojure.uuid" "clojure.walk" "clojure.xml" "clojure.zip"}) + +(defn- core-class? + [^String class-name] + (and (not (nil? class-name)) + (or (.startsWith class-name "clojure.lang.") + (contains? core-namespaces (second (re-find #"^([^$]+)\$" class-name)))))) + +(defn- file-name + "Helper to get just the file name part of a path or nil" + [^String full-path] + (when full-path + (try + (.getName (java.io.File. full-path)) + (catch Throwable t)))) + +(defn- java-loc->source + "Convert Java class name and method symbol to source symbol, either a + Clojure function or Java class and method." + [clazz method] + (if (#{'invoke 'invokeStatic} method) + (let [degen #(.replaceAll ^String % "--.*$" "") + [ns-name fn-name & nested] (->> (str clazz) (.split #"\$") (map demunge) (map degen))] + (symbol ns-name (String/join "$" ^"[Ljava.lang.String;" (into-array String (cons fn-name nested))))) + (symbol (name clazz) (name method)))) + +(defn ex-triage + "Returns an analysis of the phase, error, cause, and location of an error that occurred + based on Throwable data, as returned by Throwable->map. All attributes other than phase + are optional: + :clojure.error/phase - keyword phase indicator, one of: + :read-source :compile-syntax-check :compilation :macro-syntax-check :macroexpansion + :execution :read-eval-result :print-eval-result + :clojure.error/source - file name (no path) + :clojure.error/line - integer line number + :clojure.error/column - integer column number + :clojure.error/symbol - symbol being expanded/compiled/invoked + :clojure.error/class - cause exception class symbol + :clojure.error/cause - cause exception message + :clojure.error/spec - explain-data for spec error" + [datafied-throwable] + (let [{:keys [via trace phase] :or {phase :execution}} datafied-throwable + {:keys [type message data]} (last via) + {:keys [:clojure.spec.alpha/problems :clojure.spec.alpha/fn :clojure.spec.test.alpha/caller]} data + {:keys [:clojure.error/source] :as top-data} (:data (first via))] + (assoc + (case phase + :read-source + (let [{:keys [:clojure.error/line :clojure.error/column]} data] + (cond-> (merge (-> via second :data) top-data) + source (assoc :clojure.error/source (file-name source)) + (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} source) (dissoc :clojure.error/source) + message (assoc :clojure.error/cause message))) + + (:compile-syntax-check :compilation :macro-syntax-check :macroexpansion) + (cond-> top-data + source (assoc :clojure.error/source (file-name source)) + (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} source) (dissoc :clojure.error/source) + type (assoc :clojure.error/class type) + message (assoc :clojure.error/cause message) + problems (assoc :clojure.error/spec data)) + + (:read-eval-result :print-eval-result) + (let [[source method file line] (-> trace first)] + (cond-> top-data + line (assoc :clojure.error/line line) + file (assoc :clojure.error/source file) + (and source method) (assoc :clojure.error/symbol (java-loc->source source method)) + type (assoc :clojure.error/class type) + message (assoc :clojure.error/cause message))) + + :execution + (let [[source method file line] (->> trace (drop-while #(core-class? (name (first %)))) first) + file (first (remove #(or (nil? %) (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} %)) [(:file caller) file])) + err-line (or (:line caller) line)] + (cond-> {:clojure.error/class type} + err-line (assoc :clojure.error/line err-line) + message (assoc :clojure.error/cause message) + (or fn (and source method)) (assoc :clojure.error/symbol (or fn (java-loc->source source method))) + file (assoc :clojure.error/source file) + problems (assoc :clojure.error/spec data)))) + :clojure.error/phase phase))) + +(defn ex-str + "Returns a string from exception data, as produced by ex-triage. + The first line summarizes the exception phase and location. + The subsequent lines describe the cause." + [{:keys [:clojure.error/phase :clojure.error/source :clojure.error/line :clojure.error/column + :clojure.error/symbol :clojure.error/class :clojure.error/cause :clojure.error/spec] + :as triage-data}] + (let [spec-loaded? (some? (resolve 'clojure.spec.alpha/explain-out)) + loc (str (or source "REPL") ":" (or line 1) (if column (str ":" column) "")) + class-name (name (or class "")) + simple-class (if class (or (first (re-find #"([^.])++$" class-name)) class-name)) + cause-type (if (contains? #{"Exception" "RuntimeException"} simple-class) + "" ;; omit, not useful + (str " (" simple-class ")"))] + (case phase + :read-source + (format "Syntax error reading source at (%s).%n%s%n" loc cause) + + :macro-syntax-check + (format "Syntax error macroexpanding %sat (%s).%n%s" + (if symbol (str symbol " ") "") + loc + (if (and spec spec-loaded?) + (with-out-str + ((resolve 'clojure.spec.alpha/explain-out) + (if (= @(resolve 'clojure.spec.alpha/*explain-out*) @(resolve 'clojure.spec.alpha/explain-printer)) + (update spec :clojure.spec.alpha/problems + (fn [probs] (map #(dissoc % :in) probs))) + spec))) + (format "%s%n" cause))) + + :macroexpansion + (format "Unexpected error%s macroexpanding %sat (%s).%n%s%n" + cause-type + (if symbol (str symbol " ") "") + loc + cause) + + :compile-syntax-check + (format "Syntax error%s compiling %sat (%s).%n%s%n" + cause-type + (if symbol (str symbol " ") "") + loc + cause) + + :compilation + (format "Unexpected error%s compiling %sat (%s).%n%s%n" + cause-type + (if symbol (str symbol " ") "") + loc + cause) + + :read-eval-result + (format "Error reading eval result%s at %s (%s).%n%s%n" cause-type symbol loc cause) + + :print-eval-result + (format "Error printing return value%s at %s (%s).%n%s%n" cause-type symbol loc cause) + + :execution + (if (and spec spec-loaded?) + (format "Execution error - invalid arguments to %s at (%s).%n%s" + symbol + loc + (with-out-str + ((resolve 'clojure.spec.alpha/explain-out) + (if (= @(resolve 'clojure.spec.alpha/*explain-out*) @(resolve 'clojure.spec.alpha/explain-printer)) + (update spec :clojure.spec.alpha/problems + (fn [probs] (map #(dissoc % :in) probs))) + spec)))) + (format "Execution error%s at %s(%s).%n%s%n" + cause-type + (if symbol (str symbol " ") "") + loc + cause))))) + (defn repl-caught [e repl-env opts] (if (and (instance? IExceptionInfo e) (#{:js-eval-error :js-eval-exception} (:type (ex-data e)))) @@ -823,7 +991,9 @@ #(prn "Error evaluating:" form :as js) (constantly nil)) opts))) - (.printStackTrace e *err*))) + (binding [*out* *err*] + (print (-> e Throwable->map ex-triage ex-str)) + (flush)))) (defn repl-nil? [x] (boolean (#{"" "nil"} x))) @@ -974,7 +1144,10 @@ (apply merge ((juxt :requires :require-macros) (ana/get-namespace ana/*cljs-ns*)))] - (read request-prompt request-exit))] + (try + (read request-prompt request-exit) + (catch Throwable e + (throw (ex-info nil {:clojure.error/phase :read-source} e)))))] (or ({request-exit request-exit :cljs/quit request-exit request-prompt request-prompt} input) @@ -983,7 +1156,10 @@ ((get special-fns (first input)) repl-env env input opts) (print nil)) (let [value (eval repl-env env input opts)] - (print value))))))] + (try + (print value) + (catch Throwable e + (throw (ex-info nil {:clojure.error/phase :print-eval-result} e)))))))))] (maybe-install-npm-deps opts) (comp/with-core-cljs opts (fn [] diff --git a/src/main/clojure/cljs/support.cljc b/src/main/clojure/cljs/support.cljc index d63c8341f..918cce7f5 100644 --- a/src/main/clojure/cljs/support.cljc +++ b/src/main/clojure/cljs/support.cljc @@ -12,7 +12,7 @@ "Internal - do not use!" [fnname & pairs] `(do (when-not ~(first pairs) - (throw (ex-info ~(str fnname " requires " (second pairs)) {}))) + (throw (ex-info ~(str fnname " requires " (second pairs)) {:clojure.error/phase :macro-syntax-check}))) ~(let [more (nnext pairs)] (when more - (list* `assert-args fnname more))))) \ No newline at end of file + (list* `assert-args fnname more))))) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 21a438a19..d05a550ee 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -19,6 +19,9 @@ ;; next line is auto-generated by the build-script - Do not edit! (def ^:dynamic *clojurescript-version*) +(defn compilation-error [cause] + (ex-info nil {:closure.error/phase :compilation}) cause) + (defn- main-src-directory [] (some (fn [file] (when (= "main" (.getName file)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index dfdf09825..74653eb79 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -71,115 +71,115 @@ (try (analyze ns-env '(ns foo.bar (:require {:foo :bar}))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [lib.ns & options] and lib.ns specs supported in :require / :require-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:require [:foo :bar]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Library name must be specified as a symbol in :require / :require-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :plop]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only :as alias, :refer (names) and :rename {from to} options supported in :require")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :plop true]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only :as, :refer and :rename options supported in :require / :require-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:require [baz.woz :as woz :refer [] :as boz :refer []]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Each of :as and :refer options may only be specified once in :require / :require-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:refer-clojure :refer []))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [:refer-clojure :exclude (names)] and optionally `:rename {from to}` specs supported")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:refer-clojure :rename [1 2]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [:refer-clojure :exclude (names)] and optionally `:rename {from to}` specs supported")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:use [baz.woz :exclude []]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:use [baz.woz]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:use [baz.woz :only]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:use [baz.woz :only [1 2 3]]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:use [baz.woz :rename [1 2]]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:use [foo.bar :rename {baz qux}]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only [lib.ns :only (names)] and optionally `:rename {from to}` specs supported in :use / :use-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:use [baz.woz :only [foo] :only [bar]]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Each of :only and :rename options may only be specified once in :use / :use-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:require [baz.woz :as []]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) ":as must be followed by a symbol in :require / :require-macros")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:require [baz.woz :as woz] [noz.goz :as woz]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) ":as alias must be unique")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:require [foo.bar :rename {baz qux}]))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Renamed symbol baz not referred")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:unless []))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported. Got (:unless []) instead.")) (is (.startsWith (try (analyze ns-env '(ns foo.bar (:require baz.woz) (:require noz.goz))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Only one "))) ;; ============================================================================= @@ -543,7 +543,7 @@ (try (analyze test-env '(defn foo 123)) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Parameter declaration \"123\" should be a vector"))) ;; ============================================================================= @@ -630,13 +630,13 @@ (try (analyze test-env '(do (def ^:const foo 123) (def foo 246))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Can't redefine a constant")) (is (.startsWith (try (analyze test-env '(do (def ^:const foo 123) (set! foo 246))) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Can't set! a constant"))) (deftest test-cljs-1508-rename @@ -681,7 +681,7 @@ (deftest test-cljs-1274 (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] (binding [a/*cljs-ns* a/*cljs-ns*] - (is (thrown-with-msg? Exception #"Can't def ns-qualified name in namespace foo.core" + (is (thrown-with-cause-msg? Exception #"Can't def ns-qualified name in namespace foo.core" (analyze test-env '(def foo.core/foo 43)))) (is (analyze test-env '(def cljs.user/foo 43)))))) @@ -816,16 +816,16 @@ (testing "arguments to require should be quoted" (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* nil] - (is (thrown-with-msg? Exception #"Arguments to require must be quoted" + (is (thrown-with-cause-msg? Exception #"Arguments to require must be quoted" (analyze test-env '(require [clojure.set :as set])))) - (is (thrown-with-msg? Exception #"Arguments to require must be quoted" + (is (thrown-with-cause-msg? Exception #"Arguments to require must be quoted" (analyze test-env '(require clojure.set)))))) (testing "`:ns` and `:ns*` should throw if not `:top-level`" (binding [a/*cljs-ns* a/*cljs-ns* a/*cljs-warnings* nil] - (are [analyzed] (thrown-with-msg? Exception + (are [analyzed] (thrown-with-cause-msg? Exception #"Namespace declarations must appear at the top-level." analyzed) (analyze test-env @@ -839,7 +839,7 @@ (analyze test-env '(map #(ns foo.core (:require [clojure.set :as set])) [1 2]))) - (are [analyzed] (thrown-with-msg? Exception + (are [analyzed] (thrown-with-cause-msg? Exception #"Calls to `require` must appear at the top-level." analyzed) (analyze test-env @@ -1439,19 +1439,19 @@ (try (ana (quote)) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Wrong number of args to quote")) (is (.startsWith (try (ana (quote a b)) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Wrong number of args to quote")) (is (.startsWith (try (ana (quote a b c d)) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Wrong number of args to quote"))) (deftest var-args-error-test @@ -1459,19 +1459,19 @@ (try (ana (var)) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Wrong number of args to var")) (is (.startsWith (try (ana (var a b)) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Wrong number of args to var")) (is (.startsWith (try (ana (var nil)) (catch Exception e - (.getMessage e))) + (.getMessage (.getCause e)))) "Argument to var must be symbol"))) (deftest test-has-extern?-basic @@ -1637,11 +1637,11 @@ (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] (binding [a/*cljs-ns* a/*cljs-ns* a/*analyze-deps* false] - (is (thrown-with-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" + (is (thrown-with-cause-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" (analyze test-env '(do (require '[clojure.string :as str]) (require '[clojure.set :as str]))))) - (is (thrown-with-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" + (is (thrown-with-cause-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" (analyze test-env '(do (require-macros '[clojure.string :as str]) (require-macros '[clojure.set :as str]))))) @@ -1652,7 +1652,7 @@ (deftest test-cljs-2182 (let [cenv (atom @test-cenv)] - (is (thrown-with-msg? Exception + (is (thrown-with-cause-msg? Exception #"Argument to resolve must be a quoted symbol" (e/with-compiler-env test-cenv (analyze test-env '(resolve foo.core))))))) @@ -1664,7 +1664,7 @@ (is (= {} (get-in @test-cenv [::a/namespaces 'analyzer-test.no-defs :defs])))) (deftest test-cljs-2475 - (is (thrown-with-msg? Exception #"recur argument count mismatch, expected: 2 args, got: 1" + (is (thrown-with-cause-msg? Exception #"recur argument count mismatch, expected: 2 args, got: 1" (analyze test-env '(loop [x 1 y 2] (recur 3)))))) (deftest test-cljs-2476 @@ -1672,7 +1672,7 @@ (loop [] (try (catch js/Error t (recur)))) (loop [] (try (catch :default t (recur)))) (loop [] (try (finally (recur))))]] - (is (thrown-with-msg? Exception + (is (thrown-with-cause-msg? Exception #"Can't recur here" (analyze test-env invalid-try-recur-form))))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 1cc9dfcc2..a76efc683 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -181,7 +181,7 @@ :output-to out}) (is false) (catch Throwable e - (let [cause-message (.getMessage (.getCause e))] + (let [cause-message (.getMessage (.getCause (.getCause e)))] (is (or (re-find #"Circular dependency detected, circular-deps.a -> circular-deps.b -> circular-deps.a" cause-message) (re-find #"Circular dependency detected, circular-deps.b -> circular-deps.a -> circular-deps.b" cause-message)))))))) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index 2d4ece0ad..c1afc4366 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -69,3 +69,22 @@ [lines] (with-out-str (run! println lines))) + +(defmethod clojure.test/assert-expr 'thrown-with-cause-msg? [msg form] + ;; (is (thrown-with-cause-msg? c re expr)) + ;; Asserts that evaluating expr throws an exception of class c. + ;; Also asserts that the message string of the *cause* exception matches + ;; (with re-find) the regular expression re. + (let [klass (nth form 1) + re (nth form 2) + body (nthnext form 3)] + `(try ~@body + (clojure.test/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) + (catch ~klass e# + (let [m# (if (.getCause e#) (.. e# getCause getMessage) (.getMessage e#))] + (if (re-find ~re m#) + (clojure.test/do-report {:type :pass, :message ~msg, + :expected '~form, :actual e#}) + (clojure.test/do-report {:type :fail, :message ~msg, + :expected '~form, :actual e#}))) + e#)))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 3985194a7..96139c869 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -673,7 +673,7 @@ {:eval node-eval} (fn [{:keys [error value]}] (is (nil? value)) - (is (= "if-let requires exactly 2 forms in binding vector at line 1 " (ex-message (ex-cause error)))) + (is (= "if-let requires exactly 2 forms in binding vector" (ex-message (ex-cause (ex-cause error))))) (inc! l))) (cljs/eval-str st "(if-let [x true] 1 2 3)" @@ -681,7 +681,7 @@ {:eval node-eval} (fn [{:keys [error value]}] (is (nil? value)) - (is (= "if-let requires 1 or 2 forms after binding vector at line 1 " (ex-message (ex-cause error)))) + (is (= "if-let requires 1 or 2 forms after binding vector" (ex-message (ex-cause (ex-cause error))))) (inc! l))) (cljs/eval-str st "(if-let '(x true) 1)" @@ -689,7 +689,7 @@ {:eval node-eval} (fn [{:keys [error value]}] (is (nil? value)) - (is (= "if-let requires a vector for its binding at line 1 " (ex-message (ex-cause error)))) + (is (= "if-let requires a vector for its binding" (ex-message (ex-cause (ex-cause error))))) (inc! l)))))) (deftest test-CLJS-1573 From 848e10a9dac539b9271d83577fc1266f18e949da Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 3 Nov 2018 20:45:04 -0400 Subject: [PATCH 3356/4033] CLJS-2956: Stack overflow when specing core = --- src/main/cljs/cljs/spec/test/alpha.cljc | 6 +++++- src/test/cljs/cljs/spec/test_test.cljs | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 0827be886..a93f3d740 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -46,7 +46,11 @@ returns the set of all symbols naming vars in those nses." (defmacro with-instrument-disabled "Disables instrument's checking of calls, within a scope." [& body] - `(let [orig# @#'*instrument-enabled*] + ;; Note: In order to read the value of this private var, we employ interop + ;; rather than derefing a var special. This eases specing core functions + ;; (and infinite recursion) by avoiding code generated by the var special, + ;; and also produces more compact / efficient code. + `(let [orig# (.-*instrument-enabled* js/cljs.spec.test.alpha)] (set! *instrument-enabled* nil) (try ~@body diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index a4859c8bf..b1085aca4 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -111,6 +111,14 @@ (fn-2953 "abc")))) (is @#'stest/*instrument-enabled*)) +(s/fdef cljs.core/= :args (s/+ any?)) + +(deftest test-cljs-2956 + (stest/instrument 'cljs.core/=) + (is (true? (= 1))) + (is (thrown? js/Error (=))) + (stest/unstrument 'cljs.core/=)) + (defn fn-2975 [x]) (deftest test-2975 From d6f8896452b531a273f99f2716aaa08f09600063 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 8 Dec 2018 13:24:02 -0500 Subject: [PATCH 3357/4033] CLJS-3008: Typo in error phase key placed in exception and misplaced cause --- src/main/clojure/cljs/util.cljc | 2 +- src/test/clojure/cljs/util_tests.clj | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index d05a550ee..4bf6ce014 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -20,7 +20,7 @@ (def ^:dynamic *clojurescript-version*) (defn compilation-error [cause] - (ex-info nil {:closure.error/phase :compilation}) cause) + (ex-info nil {:clojure.error/phase :compilation} cause)) (defn- main-src-directory [] (some (fn [file] diff --git a/src/test/clojure/cljs/util_tests.clj b/src/test/clojure/cljs/util_tests.clj index 9ee44b3ea..f5bf0bac1 100644 --- a/src/test/clojure/cljs/util_tests.clj +++ b/src/test/clojure/cljs/util_tests.clj @@ -60,3 +60,6 @@ (deftest test-content-sha (is (= "40BD001563085FC35165329EA1FF5C5ECBDBBEEF" (util/content-sha "123"))) (is (= "40BD0" (util/content-sha "123" 5)))) + +(deftest test-cljs-3008 + (is (= :compilation (:clojure.error/phase (ex-data (util/compilation-error (Exception.))))))) From fc7abd04fc09fe5db49a772cd16da7b5c0b2c8c8 Mon Sep 17 00:00:00 2001 From: Will Acton Date: Thu, 13 Dec 2018 12:31:40 -0800 Subject: [PATCH 3358/4033] CLJS-3010: Datafy does not properly check for whether the datafied value supports metadata Datafy correctly detects if return value supports metadata --- src/main/cljs/clojure/datafy.cljs | 2 +- src/test/cljs/clojure/datafy_test.cljs | 24 ++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs/clojure/datafy_test.cljs diff --git a/src/main/cljs/clojure/datafy.cljs b/src/main/cljs/clojure/datafy.cljs index 46cbddcd8..0a3510f10 100644 --- a/src/main/cljs/clojure/datafy.cljs +++ b/src/main/cljs/clojure/datafy.cljs @@ -21,7 +21,7 @@ (let [v ((or (-> x meta ::datafy) -datafy) x)] (if (identical? v x) v - (if (object? v) + (if (implements? IWithMeta v) (vary-meta v assoc ::obj x) v)))) diff --git a/src/test/cljs/clojure/datafy_test.cljs b/src/test/cljs/clojure/datafy_test.cljs new file mode 100644 index 000000000..0798e7e30 --- /dev/null +++ b/src/test/cljs/clojure/datafy_test.cljs @@ -0,0 +1,24 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns clojure.datafy-test + (:require [cljs.test :as test + :refer-macros [deftest is testing]] + [clojure.datafy :as d])) + +(deftest datafy-test + (testing "Datafy works when datafied value is arbitrary JS objects" + (let [datafied #js {} + x (with-meta [1 2 3] {:clojure.datafy/datafy (fn [_] datafied)})] + (is (= datafied (d/datafy x))))) + (testing "Datafy adds ::obj metadata when return value != original value and supports metadata" + (let [datafied [2 3 4] + original [1 2 3] + x (with-meta original {:clojure.datafy/datafy (fn [_] datafied)})] + (is (= datafied (d/datafy x))) + (is (= {:clojure.datafy/obj original} (meta (d/datafy x))))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 349e6689c..bd6a28733 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -22,6 +22,7 @@ [cljs.ns-test] [clojure.string-test] [clojure.data-test] + [clojure.datafy-test] [clojure.walk-test] [cljs.macro-test] [cljs.letfn-test] @@ -67,6 +68,7 @@ 'cljs.reader-test 'clojure.string-test 'clojure.data-test + 'clojure.datafy-test 'clojure.walk-test 'cljs.letfn-test 'cljs.reducers-test From 59997385d85e7e1af1559d599eb51fdb1d7e93b1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 14 Dec 2018 15:48:50 -0500 Subject: [PATCH 3359/4033] CLJS-2945: Print spec failure details --- src/main/cljs/cljs/repl.cljs | 179 +++++++++++++++++++++++- src/main/clojure/cljs/repl/node_repl.js | 2 +- 2 files changed, 179 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index 0acc1735c..7999ed427 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -8,7 +8,9 @@ (ns cljs.repl (:require-macros cljs.repl) - (:require [cljs.spec.alpha :as spec])) + (:require [cljs.spec.alpha :as spec] + [goog.string :as gstring] + [goog.string.format])) (defn print-doc [{n :ns nm :name :as m}] (println "-------------------------") @@ -56,3 +58,178 @@ (doseq [role [:args :ret :fn]] (when-let [spec (get fnspec role)] (print (str "\n " (name role) ":") (spec/describe spec))))))))) + +(defn Error->map + "Constructs a data representation for a Error with keys: + :cause - root cause message + :phase - error phase + :via - cause chain, with cause keys: + :type - exception class symbol + :message - exception message + :data - ex-data + :at - top stack element + :trace - root cause stack elements" + [o] + (let [base (fn [t] + (merge {:type (cond + (instance? ExceptionInfo t) 'ExceptionInfo + (instance? js/EvalError t) 'js/EvalError + (instance? js/RangeError t) 'js/RangeError + (instance? js/ReferenceError t) 'js/ReferenceError + (instance? js/SyntaxError t) 'js/SyntaxError + (instance? js/URIError t) 'js/URIError + (instance? js/Error t) 'js/Error + :else nil)} + (when-let [msg (ex-message t)] + {:message msg}) + (when-let [ed (ex-data t)] + {:data ed}) + #_(let [st (extract-canonical-stacktrace t)] + (when (pos? (count st)) + {:at st})))) + via (loop [via [], t o] + (if t + (recur (conj via t) (ex-cause t)) + via)) + root (peek via)] + (merge {:via (vec (map base via)) + :trace nil #_(extract-canonical-stacktrace (or root o))} + (when-let [root-msg (ex-message root)] + {:cause root-msg}) + (when-let [data (ex-data root)] + {:data data}) + (when-let [phase (-> o ex-data :clojure.error/phase)] + {:phase phase})))) + +(defn ex-triage + "Returns an analysis of the phase, error, cause, and location of an error that occurred + based on Throwable data, as returned by Throwable->map. All attributes other than phase + are optional: + :clojure.error/phase - keyword phase indicator, one of: + :read-source :compile-syntax-check :compilation :macro-syntax-check :macroexpansion + :execution :read-eval-result :print-eval-result + :clojure.error/source - file name (no path) + :clojure.error/line - integer line number + :clojure.error/column - integer column number + :clojure.error/symbol - symbol being expanded/compiled/invoked + :clojure.error/class - cause exception class symbol + :clojure.error/cause - cause exception message + :clojure.error/spec - explain-data for spec error" + [datafied-throwable] + (let [{:keys [via trace phase] :or {phase :execution}} datafied-throwable + {:keys [type message data]} (last via) + {::spec/keys [:problems :fn :cljs.spec.test.alpha/caller]} data + {:keys [:clojure.error/source] :as top-data} (:data (first via))] + (assoc + (case phase + :read-source + (let [{:keys [:clojure.error/line :clojure.error/column]} data] + (cond-> (merge (-> via second :data) top-data) + source (assoc :clojure.error/source source) + (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} source) (dissoc :clojure.error/source) + message (assoc :clojure.error/cause message))) + + (:compile-syntax-check :compilation :macro-syntax-check :macroexpansion) + (cond-> top-data + source (assoc :clojure.error/source source) + (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} source) (dissoc :clojure.error/source) + type (assoc :clojure.error/class type) + message (assoc :clojure.error/cause message) + problems (assoc :clojure.error/spec data)) + + (:read-eval-result :print-eval-result) + (let [[source method file line] (-> trace first)] + (cond-> top-data + line (assoc :clojure.error/line line) + file (assoc :clojure.error/source file) + (and source method) (assoc :clojure.error/symbol (vector #_java-loc->source source method)) + type (assoc :clojure.error/class type) + message (assoc :clojure.error/cause message))) + + :execution + (let [[source method file line] (->> trace #_(drop-while #(core-class? (name (first %)))) first) + file (first (remove #(or (nil? %) (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} %)) [(:file caller) file])) + err-line (or (:line caller) line)] + (cond-> {:clojure.error/class type} + err-line (assoc :clojure.error/line err-line) + message (assoc :clojure.error/cause message) + (or fn (and source method)) (assoc :clojure.error/symbol (or fn (vector #_java-loc->source source method))) + file (assoc :clojure.error/source file) + problems (assoc :clojure.error/spec data)))) + :clojure.error/phase phase))) + +(defn ex-str + "Returns a string from exception data, as produced by ex-triage. + The first line summarizes the exception phase and location. + The subsequent lines describe the cause." + [{:clojure.error/keys [phase source line column symbol class cause spec] :as triage-data}] + (let [loc (str (or source "") ":" (or line 1) (if column (str ":" column) "")) + class-name (name (or class "")) + simple-class class-name + cause-type (if (contains? #{"Exception" "RuntimeException"} simple-class) + "" ;; omit, not useful + (str " (" simple-class ")")) + format gstring/format] + (case phase + :read-source + (format "Syntax error reading source at (%s).\n%s\n" loc cause) + + :macro-syntax-check + (format "Syntax error macroexpanding %sat (%s).\n%s" + (if symbol (str symbol " ") "") + loc + (if spec + (with-out-str + (spec/explain-out + (if true #_(= s/*explain-out* s/explain-printer) + (update spec ::spec/problems + (fn [probs] (map #(dissoc % :in) probs))) + spec))) + (format "%s\n" cause))) + + :macroexpansion + (format "Unexpected error%s macroexpanding %sat (%s).\n%s\n" + cause-type + (if symbol (str symbol " ") "") + loc + cause) + + :compile-syntax-check + (format "Syntax error%s compiling %sat (%s).\n%s\n" + cause-type + (if symbol (str symbol " ") "") + loc + cause) + + :compilation + (format "Unexpected error%s compiling %sat (%s).\n%s\n" + cause-type + (if symbol (str symbol " ") "") + loc + cause) + + :read-eval-result + (format "Error reading eval result%s at %s (%s).\n%s\n" cause-type symbol loc cause) + + :print-eval-result + (format "Error printing return value%s at %s (%s).\n%s\n" cause-type symbol loc cause) + + :execution + (if spec + (format "Execution error - invalid arguments to %s at (%s).\n%s" + symbol + loc + (with-out-str + (spec/explain-out + (if true #_(= s/*explain-out* s/explain-printer) + (update spec ::spec/problems + (fn [probs] (map #(dissoc % :in) probs))) + spec)))) + (format "Execution error%s at %s(%s).\n%s\n" + cause-type + (if symbol (str symbol " ") "") + loc + cause))))) + +(defn error->str [error] + (ex-str (ex-triage (Error->map error)))) diff --git a/src/main/clojure/cljs/repl/node_repl.js b/src/main/clojure/cljs/repl/node_repl.js index 109be0d18..8a8c6ef0d 100644 --- a/src/main/clojure/cljs/repl/node_repl.js +++ b/src/main/clojure/cljs/repl/node_repl.js @@ -86,7 +86,7 @@ var server = net.createServer(function (socket) { type: "result", repl: repl, status: "exception", - value: err.stack + value: cljs.repl.error__GT_str(err) })); } else if(ret !== undefined && ret !== null) { socket.write(JSON.stringify({ From 82c10925eb8d9c3cecf23447ddf4e1342f120b69 Mon Sep 17 00:00:00 2001 From: Will Acton Date: Fri, 14 Dec 2018 13:23:32 -0800 Subject: [PATCH 3360/4033] CLJS-2999: Update datafy to use inherent support for protocols via metadata - Moves cljs.core/IDatafiable to clojure.core.protocols/Datafiable - Moves cljs.core/INavigable to clojure.core.protocols/Navigable - Enables :extend-via-metadata on Datafiable and Navigable fix tests --- src/main/cljs/cljs/core.cljs | 20 ++----------- src/main/cljs/clojure/core/protocols.cljs | 29 ++++++++++++++++++ src/main/cljs/clojure/datafy.cljs | 36 ++++++++++++----------- src/test/cljs/clojure/datafy_test.cljs | 4 +-- 4 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 src/main/cljs/clojure/core/protocols.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4534824e7..0a53d9d79 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -867,13 +867,6 @@ (-iterator [coll] "Returns an iterator for coll.")) -(defprotocol IDatafiable - (-datafy [o] "return a representation of o as data (default identity)")) - -(defprotocol INavigable - (-nav [coll k v] "return (possibly transformed) v in the context of coll and k (a key/index or nil), -defaults to returning v.")) - ;; Printing support (deftype StringBufferWriter [sb] @@ -1366,10 +1359,7 @@ defaults to returning v.")) (extend-type nil ICounted - (-count [_] 0) - - IDatafiable - (-datafy [_] nil)) + (-count [_] 0)) ;; TODO: we should remove this and handle date equality checking ;; by some other means, probably by adding a new primitive type @@ -1418,13 +1408,7 @@ defaults to returning v.")) (extend-type default IHash (-hash [o] - (goog/getUid o)) - - IDatafiable - (-datafy [o] o) - - INavigable - (-nav [_ _ x] x)) + (goog/getUid o))) ;;this is primitive because & emits call to array-seq (defn inc diff --git a/src/main/cljs/clojure/core/protocols.cljs b/src/main/cljs/clojure/core/protocols.cljs new file mode 100644 index 000000000..4e7aa7577 --- /dev/null +++ b/src/main/cljs/clojure/core/protocols.cljs @@ -0,0 +1,29 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.core.protocols) + +(defprotocol Datafiable + :extend-via-metadata true + (datafy [o] "return a representation of o as data (default identity)")) + +(extend-protocol Datafiable + nil + (datafy [_] nil) + + default + (datafy [o] o)) + +(defprotocol Navigable + :extend-via-metadata true + (nav [coll k v] "return (possibly transformed) v in the context of coll and k (a key/index or nil), +defaults to returning v.")) + +(extend-protocol Navigable + default + (nav [_ _ x] x)) diff --git a/src/main/cljs/clojure/datafy.cljs b/src/main/cljs/clojure/datafy.cljs index 0a3510f10..6a969d8ae 100644 --- a/src/main/cljs/clojure/datafy.cljs +++ b/src/main/cljs/clojure/datafy.cljs @@ -8,48 +8,50 @@ (ns ^{:doc "Functions to turn objects into data. Alpha, subject to change"} - clojure.datafy) + clojure.datafy + (:require [clojure.core.protocols :as p])) (defn datafy - "Attempts to return x as data. If :clojure.datafy/datafy is present - as metadata of x, it will be called with x as an argument, else - datafy will return the value of clojure.protocols/datafy. If the - value has been transformed and the result supports + "Attempts to return x as data. + datafy will return the value of clojure.protocols/datafy. If + the value has been transformed and the result supports metadata, :clojure.datafy/obj will be set on the metadata to the original value of x." [x] - (let [v ((or (-> x meta ::datafy) -datafy) x)] + (let [v (p/datafy x)] (if (identical? v x) v (if (implements? IWithMeta v) - (vary-meta v assoc ::obj x) + (vary-meta v assoc ::obj x + ;; Circling back to this at a later date per @dnolen + ;; ::class (-> x .-constructor .-name symbol) + ) v)))) (defn nav "Returns (possibly transformed) v in the context of coll and k (a key/index or nil). Callers should attempt to provide the key/index context k for Indexed/Associative/ILookup colls if possible, but not - to fabricate one e.g. for sequences (pass nil). If :clojure.datafy/nav is - present as metadata on coll, it will be called with coll, k and v as - arguments, else nav will call :clojure.protocols/nav." + to fabricate one e.g. for sequences (pass nil). nav will return the + value of clojure.core.protocols/nav." [coll k v] - ((or (-> coll meta ::nav) -nav) coll k v)) + (p/nav coll k v)) (defn- datify-ref [r] (with-meta [(deref r)] (meta r))) -(extend-protocol IDatafiable +(extend-protocol p/Datafiable Var - (-datafy [r] (datify-ref r)) + (datafy [r] (datify-ref r)) Reduced - (-datafy [r] (datify-ref r)) + (datafy [r] (datify-ref r)) Atom - (-datafy [r] (datify-ref r)) + (datafy [r] (datify-ref r)) Volatile - (-datafy [r] (datify-ref r)) + (datafy [r] (datify-ref r)) Delay - (-datafy [r] (datify-ref r))) + (datafy [r] (datify-ref r))) diff --git a/src/test/cljs/clojure/datafy_test.cljs b/src/test/cljs/clojure/datafy_test.cljs index 0798e7e30..ff108d0cc 100644 --- a/src/test/cljs/clojure/datafy_test.cljs +++ b/src/test/cljs/clojure/datafy_test.cljs @@ -14,11 +14,11 @@ (deftest datafy-test (testing "Datafy works when datafied value is arbitrary JS objects" (let [datafied #js {} - x (with-meta [1 2 3] {:clojure.datafy/datafy (fn [_] datafied)})] + x (with-meta [1 2 3] {`clojure.core.protocols/datafy (fn [_] datafied)})] (is (= datafied (d/datafy x))))) (testing "Datafy adds ::obj metadata when return value != original value and supports metadata" (let [datafied [2 3 4] original [1 2 3] - x (with-meta original {:clojure.datafy/datafy (fn [_] datafied)})] + x (with-meta original {`clojure.core.protocols/datafy (fn [_] datafied)})] (is (= datafied (d/datafy x))) (is (= {:clojure.datafy/obj original} (meta (d/datafy x))))))) From 341cf664ff18b316d74f1632c730893913b366df Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 2 Nov 2018 17:14:26 -0400 Subject: [PATCH 3361/4033] CLJS-2955: Self-host: spec check macro compile-time expansion --- src/main/cljs/cljs/spec/test/alpha.cljc | 8 ++++++-- src/test/cljs/cljs/spec/test_test.cljs | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index a93f3d740..111e510b3 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -224,12 +224,16 @@ Returns a collection of syms naming the vars unstrumented." (validate-check-opts opts#) (check-1 nil ~f ~spec opts#)))) +(defn- registry-ref [] + #?(:clj @#'s/registry-ref + :cljs cljs.spec.alpha$macros/registry-ref)) + (defn checkable-syms* ([] (checkable-syms* nil)) ([opts] (reduce into #{} - [(filter fn-spec-name? (keys @@#'s/registry-ref)) + [(filter fn-spec-name? (keys @(registry-ref))) (keys (:spec opts))]))) (defmacro checkable-syms @@ -241,7 +245,7 @@ can be checked." `(let [opts# ~opts] (validate-check-opts opts#) (reduce conj #{} - '[~@(filter fn-spec-name? (keys @@#'s/registry-ref)) + '[~@(filter fn-spec-name? (keys @(registry-ref))) ~@(keys (:spec opts))])))) (defmacro check diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index b1085aca4..8874a5480 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -113,6 +113,15 @@ (s/fdef cljs.core/= :args (s/+ any?)) +(defn foo-2955 [n] "ret") + +(s/fdef foo-2955 + :args (s/cat :n number?) + :ret string?) + +(deftest test-cljs-2955 + (is (seq (stest/check `foo-2955)))) + (deftest test-cljs-2956 (stest/instrument 'cljs.core/=) (is (true? (= 1))) From c7fa4a62765447442fdb22e40ca4d3b2de60e4ec Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 23 Dec 2018 14:36:44 -0500 Subject: [PATCH 3362/4033] CLJS-3025: Typo when updating cljs.analyzer.macros/wrapping-errors --- src/main/clojure/cljs/analyzer/macros.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer/macros.clj b/src/main/clojure/cljs/analyzer/macros.clj index 216670d52..2cdb67ccf 100644 --- a/src/main/clojure/cljs/analyzer/macros.clj +++ b/src/main/clojure/cljs/analyzer/macros.clj @@ -42,7 +42,7 @@ (cond (cljs.analyzer/has-error-data? err#) (throw err#) (cljs.analyzer/analysis-error? err#) (throw (ex-info nil (cljs.analyzer/error-data ~env :compilation) err#)) - :else (throw (ex-info nil (cljs.analyzer/error-data ~env :compilation) (cljs.analyzer/error ~env (.getMessage err#) err#))))))) + :else (throw (ex-info nil (cljs.analyzer/error-data ~env :compilation) (cljs.analyzer/error ~env (.-message err#) err#))))))) (defmacro disallowing-recur [& body] `(cljs.core/binding [cljs.analyzer/*recur-frames* From 857c1e2013d7e743fc46b398f72728d1be970aa3 Mon Sep 17 00:00:00 2001 From: Sahil Kang Date: Wed, 19 Dec 2018 22:41:49 -0800 Subject: [PATCH 3363/4033] CLJS-2652: Line breaks in long options for compile This patch prevents line-breaking in the middle of long-form option descriptions by treating long-form options as single tokens, instead of considering `--` as its own token. Running `cljs --help` without this patch would produce output looking like (notice the line-break between `--` and `compile`: * Set optimization level, only effective with -- compile main option. Valid values are: none, whitespace, simple, advanced With this patch, the output looks like: * Set optimization level, only effective with --compile main option. Valid values are: none, whitespace, simple, advanced --- src/main/clojure/cljs/cli.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 6dc57533c..bbc7e6978 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -60,9 +60,11 @@ classpath. Classpath-relative paths have prefix of @ or @/") (let [w (.substring ws s e) word-len (.length w) line-len (+ line-len word-len)] - (if (> line-len max-len) - (recur e (.next b) word-len w (conj ret line)) - (recur e (.next b) line-len (str line w) ret))) + (if (= w "--") ; long-form options are single tokens (i.e. --repl) + (recur s (.next b) (- line-len 2) line ret) + (if (> line-len max-len) + (recur e (.next b) word-len w (conj ret line)) + (recur e (.next b) line-len (str line w) ret)))) (conj ret (str line (.substring ws s (.length ws))))))))) (defn- opt->str [cs {:keys [arg doc]}] From 632a17177a4a45228b8c3ab381d02dd8969f59e5 Mon Sep 17 00:00:00 2001 From: Anton Fonarev Date: Tue, 18 Dec 2018 00:29:29 +0300 Subject: [PATCH 3364/4033] CLJS-3020: cljs.main: Incorrect documentation for the --compile flag Fixed documentation for the --compile flag of cljs.main. Previously it was mentioning a nonexistent --server option (instead of --serve). --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index bbc7e6978..fb83c72b4 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -603,7 +603,7 @@ present" :doc (str "Run a compile. If optional namespace specified, use as " "the main entry point. If --repl follows, " "will launch a REPL after the compile completes. " - "If --server follows, will start a web server that serves " + "If --serve follows, will start a web server that serves " "the current directory after the compile completes.")} ["-s" "--serve"] {:fn serve-opt :arg "host:port" From 5a52244813e8351cead549de86e81b7b0032da85 Mon Sep 17 00:00:00 2001 From: Oliver Caldwell Date: Fri, 21 Dec 2018 12:12:07 +0000 Subject: [PATCH 3365/4033] CLJS-2994 Ensure all prepl :vals are pr-str-ed The only time this should ever fire is if an exception is caught during evaluation. All other ClojureScript evaluation results are already strings, so we should only ever have to run results of Throwable->map through pr-str. Performing this in the valf allows this to be tweaked by users where required. It also acts as a catch all to ensure _any_ future values that happen to flow through to :val as maps etc instead of strings will get converted. :val should _always_ be a string. This ensures that assumption will always hold true. --- src/main/clojure/cljs/core/server.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 11c5a2722..1accb60a4 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -133,7 +133,7 @@ "prepl bound to *in* and *out*, suitable for use with e.g. server/repl (socket-repl). :ret and :tap vals will be processed by valf, a fn of one argument or a symbol naming same (default identity)" - [& {:keys [valf repl-env opts] :or {valf identity}}] + [& {:keys [valf repl-env opts] :or {valf #(if (string? %) % (pr-str %))}}] (let [valf (resolve-fn valf) out *out* lock (Object.)] From f2aaa2a2211fb734aa1083a31160aabd51d1e4d7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 1 Jan 2019 12:25:23 -0500 Subject: [PATCH 3366/4033] CLJS-3028: atom docstring typo "be come" should be "become", same as CLJ-1055 --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0a53d9d79..96b205163 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4441,7 +4441,7 @@ reduces them without incurring seq initialization" :validator validate-fn - If metadata-map is supplied, it will be come the metadata on the + If metadata-map is supplied, it will become the metadata on the atom. validate-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the validate-fn should From ebbd3eda683a53237349ab3c22e115d8fc68c7d4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 27 Dec 2018 08:55:35 -0500 Subject: [PATCH 3367/4033] CLJS-3027: sorted-map can no longer be returned by a macro unless it has keyword keys --- src/main/clojure/cljs/analyzer.cljc | 8 ++++++-- src/test/cljs/cljs/macro_test.cljs | 5 ++++- src/test/cljs/cljs/macro_test/macros.clj | 5 ++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ee53e2273..d8ef00cb9 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3495,12 +3495,16 @@ ;; TODO: analyzed analyzed? should take pass name as qualified keyword arg ;; then compiler passes can mark/check individually - David +(defn- unsorted-map? [x] + (and (map? x) + (not (sorted? x)))) + (defn analyzed "Mark a form as being analyzed. Assumes x satisfies IMeta. Useful to suppress warnings that will have been caught by a first compiler pass." [x] (cond - (map? x) (assoc x ::analyzed true) + (unsorted-map? x) (assoc x ::analyzed true) :else (vary-meta x assoc ::analyzed true))) (defn analyzed? @@ -3509,7 +3513,7 @@ [x] (boolean (cond - (map? x) (::analyzed x) + (unsorted-map? x) (::analyzed x) :else (::analyzed (meta x))))) (defn- all-values? diff --git a/src/test/cljs/cljs/macro_test.cljs b/src/test/cljs/cljs/macro_test.cljs index 5e6c41f7a..ed433e4f6 100644 --- a/src/test/cljs/cljs/macro_test.cljs +++ b/src/test/cljs/cljs/macro_test.cljs @@ -9,7 +9,7 @@ (ns cljs.macro-test (:refer-clojure :exclude [==]) (:require [cljs.test :refer-macros [deftest is]]) - (:use-macros [cljs.macro-test.macros :only [==]]) + (:use-macros [cljs.macro-test.macros :only [== sm-cljs-3027]]) (:require-macros [cljs.macro-test.cljs2852])) (deftest test-macros @@ -28,3 +28,6 @@ (is (= '([x])) (cljs.macro-test.cljs2852/beta)) (is (= '([x] [x y])) (cljs.macro-test.cljs2852/delta)) (is (= '([x] [x & xs])) (cljs.macro-test.cljs2852/zeta))) + +(deftest test-cljs-3027 + (is (= {"a" "b"} (sm-cljs-3027)))) diff --git a/src/test/cljs/cljs/macro_test/macros.clj b/src/test/cljs/cljs/macro_test/macros.clj index 610cdd1e3..1354e8c01 100644 --- a/src/test/cljs/cljs/macro_test/macros.clj +++ b/src/test/cljs/cljs/macro_test/macros.clj @@ -10,4 +10,7 @@ (:refer-clojure :exclude [==])) (defmacro == [a b] - `(+ ~a ~b)) \ No newline at end of file + `(+ ~a ~b)) + +(defmacro sm-cljs-3027 [] + (sorted-map "a" "b")) From 91be632537f324d114916984b12194e919b7b5b7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 1 Jan 2019 15:56:29 -0500 Subject: [PATCH 3368/4033] CLJS-2981: Mishandling of :npm-deps Boolean value computing upstream deps revert https://dev.clojure.org/jira/browse/CLJS-2904, :install-deps controls installation, no need for a flag on :npm-deps --- src/main/clojure/cljs/closure.clj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c8f5e6f55..d09d7c1b5 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2446,9 +2446,6 @@ (not (contains? opts :aot-cache)) (assoc :aot-cache false) - (not (contains? opts :npm-deps)) - (assoc :npm-deps false) - (contains? opts :modules) (ensure-module-opts) From 8f49d93f446399662d22436a586006eea7be0ed5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 14 Dec 2018 20:47:40 -0500 Subject: [PATCH 3369/4033] CLJS-3011: Port improved runtime exception printing to non-Node REPLs --- src/main/cljs/clojure/browser/repl.cljs | 7 +------ src/main/clojure/cljs/repl/graaljs.clj | 19 +++---------------- src/main/clojure/cljs/repl/nashorn.clj | 10 ++-------- src/main/clojure/cljs/repl/rhino.clj | 3 +-- 4 files changed, 7 insertions(+), 32 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index ba518b18c..a550f6135 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -69,12 +69,7 @@ :value (str (js* "eval(~{block})"))} (catch :default e {:status :exception - :ua-product (get-ua-product) - :value (str e) - :stacktrace - (if (.hasOwnProperty e "stack") - (.-stack e) - "No stacktrace available.")}))] + :value (cljs.repl/error->str e)}))] (pr-str result))) (defn send-result [connection url data] diff --git a/src/main/clojure/cljs/repl/graaljs.clj b/src/main/clojure/cljs/repl/graaljs.clj index 55b0daaeb..57b8f07b5 100644 --- a/src/main/clojure/cljs/repl/graaljs.clj +++ b/src/main/clojure/cljs/repl/graaljs.clj @@ -148,25 +148,12 @@ {:status :success :value (if-let [r (eval-str engine js)] (safe-to-string r) "")} (catch ScriptException e - (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] - {:status :exception - :value (.getMessage root-cause) - :stacktrace - (apply str - (interpose "\n" - (map #(subs % 5) - (filter #(clojure.string/starts-with? % ".") - (map str - (.getStackTrace root-cause))))))})) + {:status :exception + :value (eval-str engine "cljs.repl.error__GT_str(cljs.core._STAR_e)")}) (catch Throwable e (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] {:status :exception - :value (.getMessage root-cause) - :stacktrace - (apply str - (interpose "\n" - (map str - (.getStackTrace root-cause))))})))) + :value (cljs.repl/ex-str (cljs.repl/ex-triage (Throwable->map root-cause)))})))) (-load [{engine :engine :as this} ns url] (load-ns engine ns)) (-tear-down [this] diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj index 4bd767dea..467c72ac5 100644 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ b/src/main/clojure/cljs/repl/nashorn.clj @@ -138,17 +138,11 @@ (catch ScriptException e (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] {:status :exception - :value (.getMessage root-cause) - :stacktrace (NashornException/getScriptStackString root-cause)})) + :value (eval-str engine "cljs.repl.error__GT_str(cljs.core._STAR_e)")})) (catch Throwable e (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] {:status :exception - :value (.getMessage root-cause) - :stacktrace - (apply str - (interpose "\n" - (map str - (.getStackTrace root-cause))))})))) + :value (cljs.repl/ex-str (cljs.repl/ex-triage (Throwable->map root-cause)))})))) (-load [{engine :engine :as this} ns url] (load-ns engine ns)) (-tear-down [this] diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj index 2161a3512..7845938cc 100644 --- a/src/main/clojure/cljs/repl/rhino.clj +++ b/src/main/clojure/cljs/repl/rhino.clj @@ -86,8 +86,7 @@ (ScriptableObject/putProperty top-level "_STAR_e" (Context/javaToJS ex scope)) {:status :exception - :value (.toString ex) - :stacktrace (stacktrace ex)})))) + :value (cljs.repl/ex-str (cljs.repl/ex-triage (Throwable->map ex)))})))) (defn load-file "Load a JavaScript. This is needed to load JavaScript files before the Rhino From 45f56382184e1757262ae43d2fdd344cee258953 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 19 Jun 2017 13:23:56 -0400 Subject: [PATCH 3370/4033] CLJS-2103: clarify arg type and order constraints of keys and vals --- src/main/cljs/cljs/core.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 96b205163..a87421e50 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8992,9 +8992,9 @@ reduces them without incurring seq initialization" (es6-iterable KeySeq) (defn keys - "Returns a sequence of the map's keys." - [hash-map] - (when-let [mseq (seq hash-map)] + "Returns a sequence of the map's keys, in the same order as (seq map)." + [map] + (when-let [mseq (seq map)] (KeySeq. mseq nil))) (defn key @@ -9071,9 +9071,9 @@ reduces them without incurring seq initialization" (es6-iterable ValSeq) (defn vals - "Returns a sequence of the map's values." - [hash-map] - (when-let [mseq (seq hash-map)] + "Returns a sequence of the map's values, in the same order as (seq map)." + [map] + (when-let [mseq (seq map)] (ValSeq. mseq nil))) (defn val From 731be5e0916ad8d619c302bfc9b985c4d10daa8d Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Wed, 7 Nov 2018 16:37:38 +0100 Subject: [PATCH 3371/4033] CLJS-2964: Requiring spec.test.alpha loads clojure.test.check Patch fixes lazy loading of clojure.test.check. Unifies key for passing options to clojure.test.check with Clojure. --- src/main/cljs/cljs/spec/test/alpha.cljc | 19 ++++++++++++------- src/main/cljs/cljs/spec/test/alpha.cljs | 12 +++++------- src/test/cljs/cljs/spec/test_test.cljs | 15 ++++++++++++++- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 111e510b3..0802c4de5 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -255,7 +255,7 @@ is not specified, check all checkable vars. If a symbol identifies a namespace then all symbols in that namespace will be enumerated. The opts map includes the following optional keys, where stc -aliases clojure.test.check: +aliases clojure.spec.test.check: ::stc/opts opts to flow through test.check/quick-check :gen map from spec names to generator overrides @@ -289,12 +289,17 @@ spec itself will have an ::s/failure value in ex-data: ([sym-or-syms opts] (let [syms (sym-or-syms->syms (form->sym-or-syms sym-or-syms)) opts-sym (gensym "opts")] - `(let [~opts-sym ~opts] - [~@(->> syms - (filter (checkable-syms* opts)) - (map - (fn [sym] - (do `(check-1 '~sym nil nil ~opts-sym)))))])))) + `(if (and (cljs.core/exists? clojure.test.check) + (cljs.core/exists? clojure.test.check.properties)) + (let [~opts-sym ~opts] + [~@(->> syms + (filter (checkable-syms* opts)) + (map + (fn [sym] + (do `(check-1 '~sym nil nil ~opts-sym)))))]) + (throw + (js/Error. (str "Require clojure.test.check and " + "clojure.test.check.properties before calling check."))))))) (defmacro ^:private maybe-setup-static-dispatch [f ret conform! arity] (let [arity-accessor (symbol (str ".-cljs$core$IFn$_invoke$arity$" arity)) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 897c2911f..709b9f617 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -15,9 +15,7 @@ [cljs.stacktrace :as st] [cljs.pprint :as pp] [cljs.spec.alpha :as s] - [cljs.spec.gen.alpha :as gen] - [clojure.test.check :as stc] - [clojure.test.check.properties])) + [cljs.spec.gen.alpha :as gen])) (defn distinct-by ([f coll] @@ -228,7 +226,7 @@ with explain-data + ::s/failure." true)))))) (defn- quick-check - [f specs {gen :gen opts ::stc/opts}] + [f specs {gen :gen opts :clojure.spec.test.check/opts}] (let [{:keys [num-tests] :or {num-tests 1000}} opts g (try (s/gen (:args specs) gen) (catch js/Error t t))] (if (instance? js/Error g) @@ -240,7 +238,7 @@ with explain-data + ::s/failure." "Builds spec result map." [check-sym spec test-check-ret] (merge {:spec spec - ::stc/ret test-check-ret} + :clojure.spec.test.check/ret test-check-ret} (when check-sym {:sym check-sym}) (when-let [result (-> test-check-ret :result)] @@ -282,10 +280,10 @@ with explain-data + ::s/failure." suitable for summary use." [x] (if (:failure x) - (-> (dissoc x ::stc/ret) + (-> (dissoc x :clojure.spec.test.check/ret) (update :spec s/describe) (update :failure unwrap-failure)) - (dissoc x :spec ::stc/ret))) + (dissoc x :spec :clojure.spec.test.check/opts))) (defn summarize-results "Given a collection of check-results, e.g. from 'check', pretty diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 8874a5480..7d8a9fb5e 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -20,7 +20,7 @@ (is (= (stest/unstrument `h-cljs-1812) [])) - (stest/check `h-cljs-1812 {:clojure.test.check/opts {:num-tests 1}}) + (stest/check `h-cljs-1812 {:clojure.spec.test.check/opts {:num-tests 1}}) ; Calling h-cljs-1812 with an argument of the wrong type shouldn't throw, ; because the function should not have been instrumented by stest/check. @@ -151,3 +151,16 @@ (is (= [0 1] (fn-2995 0))) (is (= [0 1] (fn-2995 0))) (is (thrown? js/Error (fn-2995 "not a number"))))) + +(defn cljs-2964 [x] true) +(s/fdef cljs-2964 :args (s/cat :x int?) :ret true?) + +(deftest test-cljs-2964 + (let [check-res + (stest/check `cljs-2964 {:clojure.spec.test.check/opts {:num-tests 1}})] + (is (seq check-res)) + (is (every? (fn [res] + (= 1 (-> res + :clojure.spec.test.check/ret + :num-tests))) + check-res)))) From ef56db9be1afdd09487cd680b9e182bcf69b0f8c Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Mon, 7 Jan 2019 19:38:45 +0100 Subject: [PATCH 3372/4033] CLJS-3033: Maintain backward compatibility test.check keyword CLJS-2964 unified the key for passing clojure.test.check options with Clojure. This introduced a breaking change. This commit unbreaks that change by maintaining backward compatibility, supporting both keywords. --- src/main/cljs/cljs/spec/test/alpha.cljc | 13 ++++++++++--- src/main/cljs/cljs/spec/test/alpha.cljs | 4 ++-- src/test/cljs/cljs/spec/test_test.cljs | 13 +++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 0802c4de5..5c131f6c3 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -193,11 +193,17 @@ Returns a collection of syms naming the vars unstrumented." [[quote s :as qs] f spec opts] (let [{:keys [name] :as v} (when qs (ana-api/resolve &env s))] `(let [s# '~name - opts# ~opts v# ~(when v `(var ~name)) spec# (or ~spec ~(when v `(s/get-spec (var ~name)))) re-inst?# (and v# (seq (unstrument '~name)) true) - f# (or ~f (when v# @v#))] + f# (or ~f (when v# @v#)) + opts# ~opts + old-tc-ns# "clojure.test.check" + old-tc-opts-key# (keyword old-tc-ns# "opts") + [tc-ns# opts#] (if-let [old-tc-opts# (get opts# old-tc-opts-key#)] + [old-tc-ns# (assoc opts# :clojure.spec.test.check/opts + old-tc-opts#)] + ["clojure.spec.test.check" opts#])] (try (cond (nil? f#) @@ -206,7 +212,8 @@ Returns a collection of syms naming the vars unstrumented." (:args spec#) (let [tcret# (#'quick-check f# spec# opts#)] - (#'make-check-result s# spec# tcret#)) + (#'make-check-result s# spec# tcret# + (keyword tc-ns# "ret"))) :default {:failure (ex-info "No :args spec" {::s/failure :no-args-spec}) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljs b/src/main/cljs/cljs/spec/test/alpha.cljs index 709b9f617..6a5a0e539 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljs +++ b/src/main/cljs/cljs/spec/test/alpha.cljs @@ -236,9 +236,9 @@ with explain-data + ::s/failure." (defn- make-check-result "Builds spec result map." - [check-sym spec test-check-ret] + [check-sym spec test-check-ret tc-ret-key] (merge {:spec spec - :clojure.spec.test.check/ret test-check-ret} + tc-ret-key test-check-ret} (when check-sym {:sym check-sym}) (when-let [result (-> test-check-ret :result)] diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 7d8a9fb5e..7f2eea155 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -164,3 +164,16 @@ :clojure.spec.test.check/ret :num-tests))) check-res)))) + +(defn cljs-3033 [x] true) +(s/fdef cljs-3033 :args (s/cat :x int?) :ret true?) + +(deftest test-cljs-3033 + (let [check-res + (stest/check `cljs-3033 {:clojure.test.check/opts {:num-tests 1}})] + (is (seq check-res)) + (is (every? (fn [res] + (= 1 (-> res + :clojure.test.check/ret + :num-tests))) + check-res)))) From 75e4e528f84a38b7bb9741a498f153457182a057 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 3 Jan 2019 23:44:05 +0100 Subject: [PATCH 3373/4033] CLJS-3023: Instrumenting next gives maximum call stack size exceeded Use variation of next inside apply-to-simple. --- src/main/cljs/cljs/core.cljs | 15 +++++++++++---- src/test/cljs/cljs/spec/test_test.cljs | 19 ++++++++++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a87421e50..3ac078ab7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3878,6 +3878,13 @@ reduces them without incurring seq initialization" (set! *unchecked-if* true) +(defn- ^seq next* + "Internal. DO NOT USE! Next without the nil? check." + [coll] + (if (implements? INext coll) + (-next ^not-native coll) + (seq (rest coll)))) + (defn- apply-to-simple "Internal. DO NOT USE! Assumes args was already called with seq beforehand!" @@ -3886,25 +3893,25 @@ reduces them without incurring seq initialization" (if (.-cljs$core$IFn$_invoke$arity$0 f) (.cljs$core$IFn$_invoke$arity$0 f) (.call f f)) - (apply-to-simple f (-first args) (next args)))) + (apply-to-simple f (-first args) (next* args)))) ([f a0 ^seq args] (if (nil? args) (if (.-cljs$core$IFn$_invoke$arity$1 f) (.cljs$core$IFn$_invoke$arity$1 f a0) (.call f f a0)) - (apply-to-simple f a0 (-first args) (next args)))) + (apply-to-simple f a0 (-first args) (next* args)))) ([f a0 a1 ^seq args] (if (nil? args) (if (.-cljs$core$IFn$_invoke$arity$2 f) (.cljs$core$IFn$_invoke$arity$2 f a0 a1) (.call f f a0 a1)) - (apply-to-simple f a0 a1 (-first args) (next args)))) + (apply-to-simple f a0 a1 (-first args) (next* args)))) ([f a0 a1 a2 ^seq args] (if (nil? args) (if (.-cljs$core$IFn$_invoke$arity$3 f) (.cljs$core$IFn$_invoke$arity$3 f a0 a1 a2) (.call f f a0 a1 a2)) - (apply-to-simple f a0 a1 a2 (-first args) (next args)))) + (apply-to-simple f a0 a1 a2 (-first args) (next* args)))) ([f a0 a1 a2 a3 ^seq args] (if (nil? args) (if (.-cljs$core$IFn$_invoke$arity$4 f) diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 7f2eea155..07a0e3a47 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -111,8 +111,6 @@ (fn-2953 "abc")))) (is @#'stest/*instrument-enabled*)) -(s/fdef cljs.core/= :args (s/+ any?)) - (defn foo-2955 [n] "ret") (s/fdef foo-2955 @@ -122,11 +120,14 @@ (deftest test-cljs-2955 (is (seq (stest/check `foo-2955)))) +(s/fdef cljs.core/= :args (s/+ any?)) + (deftest test-cljs-2956 - (stest/instrument 'cljs.core/=) + (is (= '[cljs.core/=] (stest/instrument `=))) (is (true? (= 1))) - (is (thrown? js/Error (=))) - (stest/unstrument 'cljs.core/=)) + (is (thrown-with-msg? + js/Error #"Call to #'cljs.core/= did not conform to spec\." (=))) + (is (= '[cljs.core/=] (stest/unstrument `=)))) (defn fn-2975 [x]) @@ -177,3 +178,11 @@ :clojure.test.check/ret :num-tests))) check-res)))) + +(s/fdef cljs.core/next :args (s/cat :coll seqable?)) + +(deftest test-3023 + (is (= '[cljs.core/next] (stest/instrument `next))) + (is (= [2 3] (next [1 2 3]))) + (is (thrown-with-msg? js/Error #"Call to #'cljs.core/next did not conform to spec\." (next 1))) + (is (= '[cljs.core/next] (stest/unstrument `next)))) From 4d54c041703672600eece45d67e559e769f68dbf Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 8 Jan 2019 19:26:29 -0500 Subject: [PATCH 3374/4033] CLJS-3034: Truthy-induced inference --- src/main/clojure/cljs/analyzer.cljc | 28 +++++++++++++++--------- src/test/clojure/cljs/analyzer_tests.clj | 6 ++++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d8ef00cb9..9d8510279 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1593,19 +1593,27 @@ (get-in env [:locals sym])) [sym tag]))))))) -(defn- add-predicate-induced-tags - "Looks at the test and adds any tags which are induced by virtue - of the predicate being satisfied. For example in (if (string? x) x :bar) +(defn- truth-induced-tag + "Refine a tag to exclude clj-nil if the test is a simple symbol." + [env test] + (when (and (symbol? test) + (nil? (namespace test))) + (let [analyzed-symbol (no-warn (analyze (assoc env :context :expr) test))] + (when-let [tag (:tag analyzed-symbol)] + (when (and (set? tag) + (contains? tag 'clj-nil)) + [test (canonicalize-type (disj tag 'clj-nil))]))))) + +(defn- set-test-induced-tags + "Looks at the test and sets any tags which are induced by virtue + of the test being truthy. For example in (if (string? x) x :bar) the local x in the then branch must be of string type." [env test] (let [[local tag] (or (simple-predicate-induced-tag env test) - (type-check-induced-tag env test))] + (type-check-induced-tag env test) + (truth-induced-tag env test))] (cond-> env - local (update-in [:locals local :tag] (fn [prev-tag] - (if (or (nil? prev-tag) - (= 'any prev-tag)) - tag - prev-tag)))))) + local (assoc-in [:locals local :tag] tag)))) (defmethod parse 'if [op env [_ test then else :as form] name _] @@ -1614,7 +1622,7 @@ (when (> (count form) 4) (throw (compile-syntax-error env "Too many arguments to if" 'if))) (let [test-expr (disallowing-recur (analyze (assoc env :context :expr) test)) - then-expr (allowing-redef (analyze (add-predicate-induced-tags env test) then)) + then-expr (allowing-redef (analyze (set-test-induced-tags env test) then)) else-expr (allowing-redef (analyze env else))] {:env env :op :if :form form :test test-expr :then then-expr :else else-expr diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 74653eb79..48dda8e9f 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -267,7 +267,11 @@ (is (= (a/no-warn (e/with-compiler-env test-cenv (:tag (a/analyze test-env '(let [x ^any []] (if (seqable? x) x :kw)))))) - '#{cljs.core/ISeqable array string cljs.core/Keyword}))) + '#{cljs.core/ISeqable array string cljs.core/Keyword})) + (is (= (a/no-warn + (e/with-compiler-env test-cenv + (:tag (a/analyze test-env '(let [x (namespace :x)] (if x x :kw)))))) + '#{string cljs.core/Keyword}))) (deftest loop-recur-inference (is (= (a/no-warn From 90c9a680e3e269a0b5f40cc907d9889d03c8dcbb Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Wed, 16 Jan 2019 17:30:55 +0100 Subject: [PATCH 3375/4033] CLJS-3038: Improve error message when clojure.test.check is not required --- src/main/cljs/cljs/spec/test/alpha.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 5c131f6c3..4259a729c 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -305,8 +305,8 @@ spec itself will have an ::s/failure value in ex-data: (fn [sym] (do `(check-1 '~sym nil nil ~opts-sym)))))]) (throw - (js/Error. (str "Require clojure.test.check and " - "clojure.test.check.properties before calling check."))))))) + (js/Error. (str "Require clojure.test.check " + "before calling check."))))))) (defmacro ^:private maybe-setup-static-dispatch [f ret conform! arity] (let [arity-accessor (symbol (str ".-cljs$core$IFn$_invoke$arity$" arity)) From 515db0a354002edad00a7d73cf18e33d21c9e4a5 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Sun, 13 Jan 2019 13:25:58 -0800 Subject: [PATCH 3376/4033] CLJS-3036: Provide a clojure.edn namespace for Clojure compatibility Adds the clojure.edn namespace, containing read and read-string. When writing CLJC libraries intended for use in Clojure and ClojureScript it is convenient to be able to use clojure.edn/read-string instead of using reader conditionals to use cljs.reader/read-string in ClojureScript. --- src/main/cljs/clojure/edn.cljs | 55 +++++++++++++++++++++++++++++ src/test/cljs/clojure/edn_test.cljs | 24 +++++++++++++ src/test/cljs/test_runner.cljs | 2 ++ src/test/self/self_parity/test.cljs | 2 ++ 4 files changed, 83 insertions(+) create mode 100644 src/main/cljs/clojure/edn.cljs create mode 100644 src/test/cljs/clojure/edn_test.cljs diff --git a/src/main/cljs/clojure/edn.cljs b/src/main/cljs/clojure/edn.cljs new file mode 100644 index 000000000..9af7076b8 --- /dev/null +++ b/src/main/cljs/clojure/edn.cljs @@ -0,0 +1,55 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns clojure.edn + "edn reading. + + This namespace provides alias for cljs.reader/read and cljs.reader/read-string. + Thus Clojure and ClojureScript source can reference these functions in the same way. + In Clojure, read and read-string may cause evaluation, + but clojure.edn/read and clojure.edn/read-string will not. + In ClojureScript cljs.reader/read and cljs.reader/read-string will not cause evaluation, + they only read edn." + (:require [cljs.reader :as reader])) + +(defn read + "Reads the first object from an cljs.tools.reader.reader-types/IPushbackReader. + Returns the object read. If EOF, throws if eof-error? is true otherwise returns eof. + If no reader is provided, *in* will be used. + + Reads data in the edn format (subset of Clojure data): + http://edn-format.org + + cljs.tools.reader.edn/read doesn't depend on dynamic Vars, all configuration + is done by passing an opt map. + + opts is a map that can include the following keys: + :eof - value to return on end-of-file. When not supplied, eof throws an exception. + :readers - a map of tag symbols to data-reader functions to be considered before default-data-readers. + When not supplied, only the default-data-readers will be used. + :default - A function of two args, that will, if present and no reader is found for a tag, + be called with the tag and the value." + ([reader] + (reader/read reader)) + ([opts reader] + (reader/read opts reader)) + ([reader eof-error? eof opts] + (reader/read reader eof-error? eof opts))) + +(defn read-string + "Reads one object from the string s. + Returns nil when s is nil or empty. + + Reads data in the edn format (subset of Clojure data): + http://edn-format.org + + opts is a map as per cljs.tools.reader.edn/read" + ([s] + (reader/read-string s)) + ([opts s] + (reader/read-string opts s))) diff --git a/src/test/cljs/clojure/edn_test.cljs b/src/test/cljs/clojure/edn_test.cljs new file mode 100644 index 000000000..969b9772f --- /dev/null +++ b/src/test/cljs/clojure/edn_test.cljs @@ -0,0 +1,24 @@ +(ns clojure.edn-test + (:require [cljs.test :refer-macros [deftest is testing]] + [clojure.edn :as edn] + [cljs.reader :as reader] + [cljs.tools.reader.reader-types :as reader-types])) + +(defn- test-reader [] + (reader-types/string-push-back-reader "[1 2 3]")) + +(deftest test-read + (testing "Mirrors cljs.reader/read" + (is (= (edn/read (test-reader)) + (reader/read (test-reader)))) + (is (= (edn/read {} (test-reader)) + (reader/read {} (test-reader)))) + (is (= (edn/read (test-reader) false "EOF" {}) + (reader/read (test-reader) false "EOF" {}))))) + +(deftest test-read-string + (testing "Mirrors cljs.reader/read-string" + (is (= (edn/read-string "(+ 1 2)") + (reader/read-string "(+ 1 2)"))) + (is (= (edn/read-string "{:a #{[1]}}") + (reader/read-string "{:a #{[1]}}"))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index bd6a28733..bc19fdccb 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -23,6 +23,7 @@ [clojure.string-test] [clojure.data-test] [clojure.datafy-test] + [clojure.edn-test] [clojure.walk-test] [cljs.macro-test] [cljs.letfn-test] @@ -69,6 +70,7 @@ 'clojure.string-test 'clojure.data-test 'clojure.datafy-test + 'clojure.edn-test 'clojure.walk-test 'cljs.letfn-test 'cljs.reducers-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 816659043..6e075377b 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -288,6 +288,7 @@ #_[cljs.ns-test] [clojure.string-test] [clojure.data-test] + [clojure.edn] [clojure.walk-test] [cljs.macro-test] [cljs.letfn-test] @@ -331,6 +332,7 @@ 'cljs.reader-test 'clojure.string-test 'clojure.data-test + 'clojure.edn 'clojure.walk-test 'cljs.letfn-test 'cljs.reducers-test From a8a8c83ea3e6832163c2f13e38de1a9d51f2a15a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 25 Jan 2019 16:26:12 -0500 Subject: [PATCH 3377/4033] CLJS-3030: Regression with core.async surrounding select-keys / find on String Temporary workaround. core.async just needs macroexpand to work, so the fact that locals is malformed is not that important. Just ignore `lb` if it's not a map for the time being. --- src/main/clojure/cljs/analyzer.cljc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9d8510279..5011ecfdc 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3638,8 +3638,11 @@ ret {:env env :form sym} lcls (:locals env)] (if-some [lb (handle-symbol-local sym (get lcls sym))] - (merge (assoc ret :op :local :info lb) - (select-keys lb [:name :local :arg-id :variadic? :init])) + (merge + (assoc ret :op :local :info lb) + ;; this is a temporary workaround for core.async see CLJS-3030 - David + (when (map? lb) + (select-keys lb [:name :local :arg-id :variadic? :init]))) (let [sym-meta (meta sym) sym-ns (namespace sym) cur-ns (str (-> env :ns :name)) From 179ee9018e448a716d3c3fc0db5c2ed967a53ae1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 3 Jan 2019 22:48:18 -0500 Subject: [PATCH 3378/4033] CLJS-3031: loop / recur inference, warnings not suppressed on initial pass --- src/main/clojure/cljs/analyzer.cljc | 34 ++++++++++++++++++------ src/test/clojure/cljs/analyzer_tests.clj | 19 +++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5011ecfdc..684c4d778 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -11,7 +11,7 @@ :cljs (:refer-clojure :exclude [macroexpand-1 ns-interns ensure js-reserved])) #?(:cljs (:require-macros [cljs.analyzer.macros - :refer [no-warn wrapping-errors + :refer [no-warn wrapping-errors with-warning-handlers disallowing-recur allowing-redef disallowing-ns*]] [cljs.env.macros :refer [ensure]])) #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] @@ -722,6 +722,14 @@ (doseq [handler *cljs-warning-handlers*] (handler warning-type env extra))) +(defn- accumulating-warning-handler [warn-acc] + (fn [warning-type env extra] + (when (warning-type *cljs-warnings*) + (swap! warn-acc conj [warning-type env extra])))) + +(defn- replay-accumulated-warnings [warn-acc] + (run! #(apply warning %) @warn-acc)) + (defn- error-data ([env phase] (error-data env phase nil)) @@ -2323,7 +2331,14 @@ loop-lets (cond (true? is-loop) *loop-lets* (some? *loop-lets*) (cons {:params bes} *loop-lets*)) - expr (analyze-let-body env context exprs recur-frames loop-lets) + ;; Accumulate warnings for deferred replay iff there's a possibility of re-analyzing + warn-acc (when (and is-loop + (not widened-tags)) + (atom [])) + expr (if warn-acc + (with-warning-handlers [(accumulating-warning-handler warn-acc)] + (analyze-let-body env context exprs recur-frames loop-lets)) + (analyze-let-body env context exprs recur-frames loop-lets)) children [:bindings :body] nil->any (fnil identity 'any)] (if (and is-loop @@ -2331,12 +2346,15 @@ (not= (mapv nil->any @(:tags recur-frame)) (mapv (comp nil->any :tag) bes))) (recur encl-env form is-loop @(:tags recur-frame)) - {:op op - :env encl-env - :bindings bes - :body (assoc expr :body? true) - :form form - :children children}))) + (do + (when warn-acc + (replay-accumulated-warnings warn-acc)) + {:op op + :env encl-env + :bindings bes + :body (assoc expr :body? true) + :form form + :children children})))) (defmethod parse 'let* [op encl-env form _ _] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 48dda8e9f..8348d1092 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1958,3 +1958,22 @@ (analyze ns-env '(def *foo* 1))) (is (string/starts-with? (first @ws) "*foo* not declared dynamic and thus")))) + +(deftest test-cljs-3031 + (let [ws (atom [])] + (a/with-warning-handlers [(collecting-warning-handler ws)] + (analyze ns-env + '(loop [x "a"] + (if (identical? "a" x) + (recur true) + (+ 3 x))))) + (is (= 1 (count @ws))) + (is (string/starts-with? (first @ws) "cljs.core/+, all arguments must be numbers, got [number #{boolean string}] instead"))) + (let [ws (atom [])] + (a/with-warning-handlers [(collecting-warning-handler ws)] + (analyze ns-env + '(loop [x "a"] + (if (identical? "a" x) + (recur 1) + (+ 3 x))))) + (is (zero? (count @ws))))) From 950032dba300451835c4c7c2a0c1c74ca6d0b49a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 13 Jan 2019 21:32:44 -0500 Subject: [PATCH 3379/4033] CLJS-3037: Self-host: Add datafy tests to self-parity tests --- src/test/self/self_parity/test.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 6e075377b..5bba020b3 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -288,6 +288,7 @@ #_[cljs.ns-test] [clojure.string-test] [clojure.data-test] + [clojure.datafy-test] [clojure.edn] [clojure.walk-test] [cljs.macro-test] @@ -332,6 +333,7 @@ 'cljs.reader-test 'clojure.string-test 'clojure.data-test + 'clojure.datafy-test 'clojure.edn 'clojure.walk-test 'cljs.letfn-test From 6e01b149dddfa28f76dd63374534900e6cf26d86 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 27 Jan 2019 10:30:41 -0500 Subject: [PATCH 3380/4033] CLJS-3043: error__GT_str not defined for cli scripts --- src/main/clojure/cljs/cli.clj | 3 +++ src/test/cljs_cli/cljs_cli/test.clj | 20 +++++++++++++++++++- src/test/cljs_cli/cljs_cli/util.clj | 4 ++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index fb83c72b4..af069ebee 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -354,6 +354,9 @@ present" (fn [] (try (repl/setup renv repl/*repl-opts*) + ;; Load cljs.repl runtime (so ex-str, ex-triage, etc. are available) + (repl/evaluate-form renv (ana-api/empty-env) "" + `(~'require ~''cljs.repl)) ;; REPLs don't normally load cljs_deps.js (when (and coptsf (.exists coptsf)) (let [depsf (io/file (:output-dir opts) "cljs_deps.js")] diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj index ccce30233..05765306a 100644 --- a/src/test/cljs_cli/cljs_cli/test.clj +++ b/src/test/cljs_cli/cljs_cli/test.clj @@ -4,7 +4,7 @@ [clojure.java.io :as io] [clojure.java.shell :as shell :refer [with-sh-dir]] [clojure.string :as str] - [cljs-cli.util :refer [cljs-main output-is with-sources with-in with-post-condition with-repl-env-filter repl-title]] + [cljs-cli.util :refer [cljs-main output-is check-result with-sources with-in with-post-condition with-repl-env-filter repl-title]] [clojure.string :as string])) (deftest eval-test @@ -127,3 +127,21 @@ (with-repl-env-filter #{"graaljs"} (-> (cljs-main "-e" "(.eval js/Polyglot \"js\" \"1+1\")") (output-is 2)))) + +(deftest test-cljs-3043 + (with-repl-env-filter (complement #{"rhino"}) + (let [check-fn (fn [result] + (is (= 1 (:exit result))) + (is (str/includes? (:err result) "Execution error")) + (is (not (str/includes? (:err result) "error__GT_str"))))] + (-> (cljs-main + "-e" "js/foo") + (check-result check-fn)) + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (prn js/bogus)"} + (-> (cljs-main "-m" "foo.core") + (check-result check-fn))) + (with-sources {"src/foo/core.cljs" + "(ns foo.core) (prn js/bogus)"} + (-> (cljs-main "src/foo/core.cljs") + (check-result check-fn)))))) diff --git a/src/test/cljs_cli/cljs_cli/util.clj b/src/test/cljs_cli/cljs_cli/util.clj index 1aa8ac457..09c9ebf6e 100644 --- a/src/test/cljs_cli/cljs_cli/util.clj +++ b/src/test/cljs_cli/cljs_cli/util.clj @@ -102,5 +102,9 @@ (is (= (string/trim (apply str (map print-str (interleave expected-lines (repeat "\n"))))) (string/trim (:out result)))))) +(defn check-result [result pred] + (when-not (:repl-env-filtered result) + (pred result))) + (defn repl-title [] (string/trim (with-out-str (repl/repl-title)))) \ No newline at end of file From c7cd3a1f4265dd1f9687da7e1076223a4da8ce20 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 31 Jan 2019 07:55:35 -0500 Subject: [PATCH 3381/4033] 1.10.514 --- README.md | 6 ++--- changes.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6c294e264..4b58e4ad3 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.439 +Latest stable release: 1.10.514 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.439"] +[org.clojure/clojurescript "1.10.514"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.439 org.clojure clojurescript - 1.10.439 + 1.10.514 ``` diff --git a/changes.md b/changes.md index 72a6222a1..4d3d933ce 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,72 @@ +## 1.10.514 + +### Changes +* CLJS-3036: Provide a clojure.edn namespace for Clojure compatibility +* CLJS-2967: Make clojure.spec.alpha reloadable +* CLJS-2968: Support immutable GCC DependencyOptions +* CLJS-2693: Have Range implement IChunkedSeq +* CLJS-2971: Make cljs.spec.alpha/fn-sym private +* CLJS-2912: Reuse seq in some + +### Enhancements +* CLJS-2865: Optimize string expression concatenation +* CLJS-2866: Predicate-induced type inference +* CLJS-2901: Return type inference for multi-arity & variadic fns + +### Fixes +* CLJS-3043: error__GT_str not defined for cli scripts +* CLJS-3037: Self-host: Add datafy tests to self-parity tests +* CLJS-3031: loop / recur inference, warnings not suppressed on initial pass +* CLJS-3030: Regression with core.async surrounding select-keys / find on String +* CLJS-3038: Improve error message when clojure.test.check is not required +* CLJS-3034: Truthy-induced inference +* CLJS-3023: Instrumenting next gives maximum call stack size exceeded +* CLJS-3033: Maintain backward compatibility test.check keyword +* CLJS-2964: Requiring spec.test.alpha loads clojure.test.check +* CLJS-2103: clarify arg type and order constraints of keys and vals +* CLJS-3011: Port improved runtime exception printing to non-Node REPLs +* CLJS-2981: Mishandling of :npm-deps Boolean value computing upstream deps +* CLJS-3027: sorted-map can no longer be returned by a macro unless it has keyword keys +* CLJS-3028: atom docstring typo +* CLJS-2994 Ensure all prepl :vals are pr-str-ed +* CLJS-3020: cljs.main: Incorrect documentation for the --compile flag +* CLJS-2652: Line breaks in long options for compile +* CLJS-3025: Typo when updating cljs.analyzer.macros/wrapping-errors +* CLJS-2955: Self-host: spec check macro compile-time expansion +* CLJS-2999: Update datafy to use inherent support for protocols via metadata +* CLJS-2945: Print spec failure details +* CLJS-3010: Datafy does not properly check for whether the datafied value supports metadata +* CLJS-3008: Typo in error phase key placed in exception and misplaced cause +* CLJS-2956: Stack overflow when specing core = +* CLJS-2913: improvements to exception messages and printing +* CLJS-3005: empty on Cons shouldn't keep metadata +* CLJS-2958 - make symbol work on keywords and vars +* CLJS-3000: Don't pass meta to next/rest/empty of seqs +* Add support for protocols via metadata +* CLJS-3000: Don't pass meta to next/rest/empty of seqs +* CLJS-1888 - Seqs of PHMs and PAMs do not handle metadata correctly +* CLJS-2794 :Return identity when with-meta is called with identical meta +* CLJS-2980: Calling "check-fn" gives "is not public" warning +* CLJS-2977: Spec instrumentation regression with varargs / :static-fns +* CLJS-2929: Port datafy +* CLJS-2995: Instrumented self-calling multi-arity fn throws maximum call stack exceeded with optimizations advanced +* Fix source maps missing local binding names +* CLJS-2991: Need to wrap js-obj output with parens +* CLJS-2976: s/fdef docstring should refer to cljs.spec.test.alpha/check +* CLJS-2538: nth on fractional indices near array and string bounds +* CLJS-2909: clojure.walk/postwalk does not preserve MapEntry type objects +* CLJS-2537: Negative fractional index in contains? on array +* CLJS-2933: Consistent #object printing whitespace +* CLJS-2873: Improved inference for loop / recur +* CLJS-2989: Fast-path issues for predicate-induced inference based on satisfies? +* CLJS-2867: Inferred return type of namespace is string +* CLJS-2975: unstrument returns symbol of non-instrumented var +* CLJS-2974: empty for non-emptyable should return nil +* CLJS-2825: Eliminate unnecessary ^boolean annotations +* CLJS-2979: re-seq is relying on undefined behavior of subs +* remove redundant exists? check in dynaload +* fix incorrect cljs.core.MapEntry usage + ## 1.10.439 ### Changes From 4dfe78719bee33d44a058d9549ab98cde9e0b8fb Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 31 Jan 2019 10:07:37 -0500 Subject: [PATCH 3382/4033] fix bad destructuring --- src/main/cljs/cljs/repl.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index 7999ed427..351a86d38 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -118,7 +118,7 @@ [datafied-throwable] (let [{:keys [via trace phase] :or {phase :execution}} datafied-throwable {:keys [type message data]} (last via) - {::spec/keys [:problems :fn :cljs.spec.test.alpha/caller]} data + {:cljs.spec.alpha/keys [problems fn] :cljs.spec.test.alpha/keys [caller]} data {:keys [:clojure.error/source] :as top-data} (:data (first via))] (assoc (case phase From 8a5abc4e02c72d000204674f38c6665c786302a4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 31 Jan 2019 10:10:42 -0500 Subject: [PATCH 3383/4033] 1.10.516 --- README.md | 6 +++--- changes.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4b58e4ad3..a47b3be23 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.514 +Latest stable release: 1.10.516 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.514"] +[org.clojure/clojurescript "1.10.516"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.514 org.clojure clojurescript - 1.10.514 + 1.10.516 ``` diff --git a/changes.md b/changes.md index 4d3d933ce..e892cd3fb 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.10.514 +## 1.10.516 ### Changes * CLJS-3036: Provide a clojure.edn namespace for Clojure compatibility From 65e3e40abaca4bb7dbb5a7dfd9b7405fdcab7b74 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 13 Feb 2019 11:08:10 -0500 Subject: [PATCH 3384/4033] add mime types & encodings to browser REPL --- src/main/clojure/cljs/repl/browser.clj | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 83c2ad0d1..9da106cb7 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -37,11 +37,23 @@ {".html" "text/html" ".css" "text/css" + ".ttf" "font/ttf" + ".otf" "font/otf" + + ".pdf" "application/pdf" + ".jpg" "image/jpeg" ".png" "image/png" ".gif" "image/gif" ".svg" "image/svg+xml" + ".mp4" "video/mp4" + ".m4a" "audio/m4a" + ".m4v" "video/mp4" + ".mp3" "audio/mpeg" + ".mpeg" "video/mpeg" + ".wav" "audio/wav" + ".js" "text/javascript" ".json" "application/json" ".clj" "text/x-clojure" @@ -53,10 +65,23 @@ (def mime-type->encoding {"text/html" "UTF-8" "text/css" "UTF-8" + + "font/ttf" "ISO-8859-1" + "font/otf" "ISO-8859-1" + + "application/pdf" "ISO-8859-1" + "image/jpeg" "ISO-8859-1" "image/png" "ISO-8859-1" "image/gif" "ISO-8859-1" "image/svg+xml" "UTF-8" + + "video/mp4" "ISO-8859-1" + "audio/m4a" "ISO-8859-1" + "audio/mpeg" "ISO-8859-1" + "video/mpeg" "ISO-8859-1" + "audio/wav" "ISO-8859-1" + "text/javascript" "UTF-8" "text/x-clojure" "UTF-8" "application/json" "UTF-8"}) From ee1d2970a4fc349c5d4da39cf3d5867ad36de403 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 9 Feb 2019 18:15:54 +0100 Subject: [PATCH 3385/4033] CLJS-3049: Undefined fdef is still present in result of (stest/checkable-syms) --- src/main/cljs/cljs/spec/alpha.cljc | 5 ++++- src/test/cljs/cljs/spec/test_test.cljs | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 2d1b04553..372e43c1e 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -68,7 +68,10 @@ [k spec-form] (let [k (if (symbol? k) (ns-qualify &env k) k) form (res &env spec-form)] - (swap! registry-ref assoc k form) + (swap! registry-ref (fn [r] + (if (nil? form) + (dissoc r k) + (assoc r k form)))) `(def-impl '~k '~form ~spec-form))) (defmacro spec diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index 07a0e3a47..bbb358e22 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -186,3 +186,12 @@ (is (= [2 3] (next [1 2 3]))) (is (thrown-with-msg? js/Error #"Call to #'cljs.core/next did not conform to spec\." (next 1))) (is (= '[cljs.core/next] (stest/unstrument `next)))) + +(defn cljs-3049 [x] x) +(deftest test-3049 + (s/fdef cljs-3049 :args (s/cat :x number?) :ret number?) + (testing "the spec'ed fn is checkable" + (is (contains? (stest/checkable-syms) `cljs-3049))) + (s/def cljs-3049 nil) + (testing "the spec'ed fn is not checkable anymore" + (is (not (contains? (stest/checkable-syms) `cljs-3049))))) From c87d61e54fbbeae390e522ba315f0207fb0fed75 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 2 Feb 2019 12:59:55 +0100 Subject: [PATCH 3386/4033] CLJS-3048: Revert CLJS-3038 --- src/main/cljs/cljs/spec/test/alpha.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/spec/test/alpha.cljc b/src/main/cljs/cljs/spec/test/alpha.cljc index 4259a729c..5c131f6c3 100644 --- a/src/main/cljs/cljs/spec/test/alpha.cljc +++ b/src/main/cljs/cljs/spec/test/alpha.cljc @@ -305,8 +305,8 @@ spec itself will have an ::s/failure value in ex-data: (fn [sym] (do `(check-1 '~sym nil nil ~opts-sym)))))]) (throw - (js/Error. (str "Require clojure.test.check " - "before calling check."))))))) + (js/Error. (str "Require clojure.test.check and " + "clojure.test.check.properties before calling check."))))))) (defmacro ^:private maybe-setup-static-dispatch [f ret conform! arity] (let [arity-accessor (symbol (str ".-cljs$core$IFn$_invoke$arity$" arity)) From 0c353f1947089cb8b1f010b4294b94ac109d4ef6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 13 Feb 2019 14:45:03 -0500 Subject: [PATCH 3387/4033] 1.10.520 --- README.md | 6 +++--- changes.md | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a47b3be23..27e5a6aea 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.516 +Latest stable release: 1.10.520 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.516"] +[org.clojure/clojurescript "1.10.520"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.516 org.clojure clojurescript - 1.10.516 + 1.10.520 ``` diff --git a/changes.md b/changes.md index e892cd3fb..22410570b 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,12 @@ +## 1.10.520 + +### Changes +* Browser REPL serves more mime types + +### Fixes +* CLJS-3048: Revert CLJS-3038 +* CLJS-3049: Undefined fdef is still present in result of (stest/checkable-syms) + ## 1.10.516 ### Changes From 230e46aee2c9b76e426e85865ab8930c4c26e14f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 10 Feb 2019 08:46:49 -0500 Subject: [PATCH 3388/4033] CLJS-3051: Update to Graal RC12 in CI --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1a7c33258..cee9a3229 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,8 @@ before_install: - sudo apt-get install -y libjavascriptcoregtk-3.0-bin - wget https://aka.ms/chakracore/cc_linux_x64_1_8_1 -O chakra-core.tar.gz - tar xvzf chakra-core.tar.gz - - wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc6/graalvm-ce-1.0.0-rc6-linux-amd64.tar.gz - - tar xzf graalvm-ce-1.0.0-rc6-linux-amd64.tar.gz + - wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc12/graalvm-ce-1.0.0-rc12-linux-amd64.tar.gz + - tar xzf graalvm-ce-1.0.0-rc12-linux-amd64.tar.gz before_script: - script/bootstrap @@ -55,7 +55,7 @@ script: - grep '0 failures, 0 errors.' test-out.txt - ./ChakraCoreFiles/bin/ch builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - ./graalvm-ce-1.0.0-rc6/bin/js builds/out-adv/core-advanced-test.js | tee test-out.txt + - ./graalvm-ce-1.0.0-rc12/bin/js builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - script/test-self-host | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt @@ -67,5 +67,5 @@ script: - grep '0 failures, 0 errors.' test-out.txt - script/test-cli rhino | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - PATH=`pwd`/graalvm-ce-1.0.0-rc6/bin:$PATH script/test-cli graaljs | tee test-out.txt + - PATH=`pwd`/graalvm-ce-1.0.0-rc12/bin:$PATH script/test-cli graaljs | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt From e59cd3c6024f123716cda85075963784a0bb6a0e Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 22 Feb 2019 16:02:24 +0100 Subject: [PATCH 3389/4033] CLJS-3054: Align behavior of set/union and into with Clojure --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3ac078ab7..c87c24578 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5214,7 +5214,7 @@ reduces them without incurring seq initialization" (if (implements? IEditableCollection to) (-with-meta (persistent! (reduce -conj! (transient to) from)) (meta to)) (reduce -conj to from)) - (reduce conj () from))) + (reduce conj to from))) ([to xform from] (if (implements? IEditableCollection to) (-with-meta (persistent! (transduce xform conj! (transient to) from)) (meta to)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index f68168f02..cd939a18d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1766,4 +1766,11 @@ ;; protocol not marked as :extend-via-metadata so fallthrough to no impl (is (thrown? js/Error (non-meta-protocol (with-meta {} {`non-meta-protocol (fn [_] 1)})))) ;; normal impl call just in case - (is (= 2 (non-meta-protocol (with-meta (SomeMetaImpl. 2) {`non-meta-protocol (fn [_] 1)}))))) \ No newline at end of file + (is (= 2 (non-meta-protocol (with-meta (SomeMetaImpl. 2) {`non-meta-protocol (fn [_] 1)}))))) + +(deftest test-cljs-3054 + (testing "`into` behaves the same as Clojure" + (is (nil? (into nil #{}))) + (is (= '(3 2 1) (into nil [1 2 3])))) + (testing "calling `set/union` with nilable sets returns a nilable set" + (is (nil? (set/union #{} nil nil))))) From 03360446b1252d4a85c894c4538756f327d2a773 Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Mon, 18 Mar 2019 13:37:05 +0100 Subject: [PATCH 3390/4033] CLJS-3061 Fix docstring for chunked-seq? --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c87c24578..f0668bf57 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2167,7 +2167,7 @@ reduces them without incurring seq initialization" (declare ChunkedCons ChunkedSeq) (defn chunked-seq? - "Return true if x is satisfies IChunkedSeq." + "Return true if x satisfies IChunkedSeq." [x] (implements? IChunkedSeq x)) ;;;;;;;;;;;;;;;;;;;; js primitives ;;;;;;;;;;;; From 73272a2da45a4c69d090800fa7732abe3fd05c70 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 3 Mar 2019 09:17:45 -0500 Subject: [PATCH 3391/4033] CLJS-3058: Remove dangling goog.date.relativeWithPlurals reference --- src/test/self/self_parity/auxiliary.cljs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs index ec8e2e3a3..36e1f06bb 100644 --- a/src/test/self/self_parity/auxiliary.cljs +++ b/src/test/self/self_parity/auxiliary.cljs @@ -55,7 +55,6 @@ goog.date.month goog.date.relative.TimeDeltaFormatter goog.date.relative.Unit - goog.date.relativeWithPlurals goog.date.weekDay goog.format goog.format.EmailAddress From 95b13de8300123c3c984b80410475d5acd92af6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ku=C4=8Dera?= Date: Wed, 20 Feb 2019 14:57:29 +0100 Subject: [PATCH 3392/4033] CLJS-2868: Add ^string hints Added type hint to subs, munge-str, clojure.string/reverse, clojure.string/replace, clojure.string/replace-first, clojure.string/join, clojure.string/upper-case, clojure.string/lower-case, clojure.string/capitalize, clojure.string/trim, clojure.string/triml, clojure.string/trimr, clojure.string/trim-newline and clojure.string/escape . --- src/main/cljs/cljs/core.cljs | 6 ++--- src/main/cljs/clojure/string.cljs | 26 +++++++++--------- src/test/clojure/cljs/analyzer_tests.clj | 34 ++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f0668bf57..98d4672a8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2970,8 +2970,8 @@ reduces them without incurring seq initialization" (defn subs "Returns the substring of s beginning at start inclusive, and ending at end (defaults to length of string), exclusive." - ([s start] (.substring s start)) - ([s start end] (.substring s start end))) + ([s start] ^string (.substring s start)) + ([s start end] ^string (.substring s start end))) (declare map name) @@ -11475,7 +11475,7 @@ reduces them without incurring seq initialization" (str ret "|\\$")))))) DEMUNGE_PATTERN) -(defn- munge-str [name] +(defn- ^string munge-str [name] (let [sb (StringBuffer.)] (loop [i 0] (if (< i (. name -length)) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 26f63f26f..279aca2ac 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -18,7 +18,7 @@ (def ^:private re-surrogate-pair (js/RegExp. "([\\uD800-\\uDBFF])([\\uDC00-\\uDFFF])" "g")) -(defn reverse +(defn ^string reverse "Returns s with its characters reversed." [s] (-> (.replace s re-surrogate-pair "$2$1") @@ -41,7 +41,7 @@ (f (first matches)) (f (vec matches)))))) -(defn replace +(defn ^string replace "Replaces all instance of match with replacement in s. match/replacement can be: @@ -73,7 +73,7 @@ :else (throw (str "Invalid match arg: " match)))) -(defn replace-first +(defn ^string replace-first "Replaces the first instance of match with replacement in s. match/replacement can be: @@ -104,7 +104,7 @@ (loop [sb (StringBuffer.) coll (seq coll)] (if-not (nil? coll) (recur (. sb (append (str (first coll)))) (next coll)) - (.toString sb)))) + ^string (.toString sb)))) ([separator coll] (loop [sb (StringBuffer.) coll (seq coll)] (if-not (nil? coll) @@ -114,19 +114,19 @@ (when-not (nil? coll) (. sb (append separator))) (recur sb coll))) - (.toString sb))))) + ^string (.toString sb))))) -(defn upper-case +(defn ^string upper-case "Converts string to all upper-case." [s] (.toUpperCase s)) -(defn lower-case +(defn ^string lower-case "Converts string to all lower-case." [s] (.toLowerCase s)) -(defn capitalize +(defn ^string capitalize "Converts first character of the string to upper-case, all other characters to lower-case." [s] @@ -193,22 +193,22 @@ [s] (split s #"\n|\r\n")) -(defn trim +(defn ^string trim "Removes whitespace from both ends of string." [s] (gstring/trim s)) -(defn triml +(defn ^string triml "Removes whitespace from the left side of string." [s] (gstring/trimLeft s)) -(defn trimr +(defn ^string trimr "Removes whitespace from the right side of string." [s] (gstring/trimRight s)) -(defn trim-newline +(defn ^string trim-newline "Removes all trailing newline \\n or return \\r characters from string. Similar to Perl's chomp." [s] @@ -226,7 +226,7 @@ [s] (gstring/isEmptySafe s)) -(defn escape +(defn ^string escape "Return a new string, using cmap to escape each character ch from s as follows: diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 8348d1092..6967d07ea 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1977,3 +1977,37 @@ (recur 1) (+ 3 x))))) (is (zero? (count @ws))))) + +(deftest test-cljs-2868 + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(subs "duck" 1 1)))))) + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(subs "duck" 1)))))) + + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(str)))))) + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(str 1)))))) + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(str 1 2)))))) + + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(pr-str 0)))))) + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(prn-str 0)))))) + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(print-str 0)))))) + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(munge-str "")))))) + (is (= 'string + (e/with-compiler-env test-cenv + (:tag (analyze test-env '(demunge-str ""))))))) \ No newline at end of file From f57f381e6b322f8ab5ac609159d31160e7ac433f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 5 Aug 2017 12:28:59 -0400 Subject: [PATCH 3393/4033] CLJS-2301: Avoid use of deprecated goog.string/isEmptySafe in clojure.string/blank? --- src/main/cljs/clojure/string.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 279aca2ac..94d38605a 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -224,7 +224,7 @@ (defn ^boolean blank? "True is s is nil, empty, or contains only whitespace." [s] - (gstring/isEmptySafe s)) + (gstring/isEmptyOrWhitespace (gstring/makeSafe s))) (defn ^string escape "Return a new string, using cmap to escape each character ch From 1fcce719377d0d1ecc1b197ed8023c48b3f5c720 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 5 Apr 2019 10:32:13 -0400 Subject: [PATCH 3394/4033] CLJS-3068: Compiler error with if and emit-var The test-induced tags implementation is set up to inject induced tags on locals, but the truthy-induced code inadvertently considered a broader scope (any simple symbol, which could be a symbol which resolves to a var). This results in a defect where a new entry is added to :locals for a var, and the fix is to properly limit the scope for truthy-induced. --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- src/test/cljs/cljs/inference_test.cljs | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 684c4d778..0c52bd033 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1602,10 +1602,11 @@ [sym tag]))))))) (defn- truth-induced-tag - "Refine a tag to exclude clj-nil if the test is a simple symbol." + "Refine a tag to exclude clj-nil if the test is a local." [env test] (when (and (symbol? test) - (nil? (namespace test))) + (nil? (namespace test)) + (get-in env [:locals test])) (let [analyzed-symbol (no-warn (analyze (assoc env :context :expr) test))] (when-let [tag (:tag analyzed-symbol)] (when (and (set? tag) diff --git a/src/test/cljs/cljs/inference_test.cljs b/src/test/cljs/cljs/inference_test.cljs index 86a91caa7..d37db3baf 100644 --- a/src/test/cljs/cljs/inference_test.cljs +++ b/src/test/cljs/cljs/inference_test.cljs @@ -88,3 +88,12 @@ (when (number? curr) (reset! x (dec curr))))) @x) (atom 1))))) + +(def foo-var-3068 (merge)) + +(deftest cljs-3068-test + ;; Here we are essentially testing that valid JavaScript is + ;; emitted. Without the fix, tests fail with invalid JavaScript + ;; that cannot be parse by GCC, etc. + (if foo-var-3068 foo-var-3068) + (is true)) From 47386d7c03e6fc36dc4f0145bd62377802ac1c02 Mon Sep 17 00:00:00 2001 From: Martin Kavalar Date: Fri, 5 Apr 2019 14:12:32 +0200 Subject: [PATCH 3395/4033] CLJS-3067: Fix compiler crash when requiring cljs.loader w/o modules --- src/main/clojure/cljs/closure.clj | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d09d7c1b5..a4fc8b8e0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1230,12 +1230,13 @@ compiled last after all inputs. This is because all inputs must be known and they must already be sorted in dependency order." [inputs {:keys [modules] :as opts}] - (when-let [loader (->> inputs - (filter - (fn [input] - (some '#{"cljs.loader" cljs.loader} - (:provides input)))) - first)] + (when-let [loader (when (seq modules) + (->> inputs + (filter + (fn [input] + (some '#{"cljs.loader" cljs.loader} + (:provides input)))) + first))] (let [module-uris (module-graph/modules->module-uris modules inputs opts) module-infos (module-graph/modules->module-infos modules)] (swap! env/*compiler* ana/add-consts From 3a0c07477ae781bf521bdc2b074ed7b783bb93f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 May 2019 14:59:42 -0400 Subject: [PATCH 3396/4033] when compiled under :nodejs target, explicitly bind goog.global to js/global --- src/main/cljs/cljs/core.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 98d4672a8..a4395a6c9 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11720,3 +11720,7 @@ reduces them without incurring seq initialization" which sets up an implementation of cljs.core/*eval* for that environment." [form] (*eval* form)) + +(when ^boolean js/COMPILED + (when (= :nodejs *target*) + (set! goog/global js/global))) From e25ced97af3c15a87afcb15165508aa23e54a3fa Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 23 May 2019 09:55:56 -0400 Subject: [PATCH 3397/4033] :nodejs -> "nodejs" --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a4395a6c9..918e26a86 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11722,5 +11722,5 @@ reduces them without incurring seq initialization" (*eval* form)) (when ^boolean js/COMPILED - (when (= :nodejs *target*) + (when (= "nodejs" *target*) (set! goog/global js/global))) From dc442d93fb0978233abf7f5d61d46a4aadeb369e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 14 May 2019 01:28:01 -0400 Subject: [PATCH 3398/4033] CLJS-3076: let defined variadic functions not destructuring as expected with :static-fns true --- src/main/clojure/cljs/analyzer.cljc | 8 +------- src/main/clojure/cljs/compiler.cljc | 6 +++--- src/test/cljs/cljs/destructuring_test.cljs | 10 +++++++++- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 0c52bd033..270f60ad1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -68,11 +68,6 @@ "The namespace of the constants table as a symbol." 'cljs.core.constants) -(def ^:private identity-counter (atom 0)) - -(defn- add-identity [m] - (assoc m :identity (swap! identity-counter inc))) - #?(:clj (def transit-read-opts (try @@ -2290,8 +2285,7 @@ :variadic? (:variadic? init-expr) :max-fixed-arity (:max-fixed-arity init-expr) :method-params (map :params (:methods init-expr))}) - be) - be (add-identity be)] + be)] (recur (conj bes be) (assoc-in env [:locals name] be) (next bindings)))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 92593fadb..f035ae0d8 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -86,9 +86,9 @@ :else d)))) (defn hash-scope [s] - #?(:clj (or (:identity s) (System/identityHashCode s)) - :cljs (hash-combine (-hash ^not-native (:name s)) - (shadow-depth s)))) + (hash-combine #?(:clj (hash (:name s)) + :cljs (-hash ^not-native (:name s))) + (shadow-depth s))) (declare munge) diff --git a/src/test/cljs/cljs/destructuring_test.cljs b/src/test/cljs/cljs/destructuring_test.cljs index 7667bd659..2823b6d83 100644 --- a/src/test/cljs/cljs/destructuring_test.cljs +++ b/src/test/cljs/cljs/destructuring_test.cljs @@ -176,4 +176,12 @@ (foo-fn [_ {:keys [a b c] :or {c 3}}] {:c c}))] (is (= (foo-fn foo {:a 1 :b 2}) - {:c 3})))) \ No newline at end of file + {:c 3})))) + +(deftest test-cljs-3076 + (let [f (fn [& [a _]] + a)] + (is (nil? (f nil))) + (is (= 1 (f 1))) + (is (= 1 (f 1 2)))) + (let [])) From 2bbc6eb1f9c34c8219004671cab06f9d2a8dca38 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 23 May 2019 12:06:24 -0400 Subject: [PATCH 3399/4033] CLJS-3092: Peek on subvecs doesn't behave properly --- src/main/cljs/cljs/core.cljs | 3 ++- src/test/cljs/cljs/core_test.cljs | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 918e26a86..42381798b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5849,7 +5849,8 @@ reduces them without incurring seq initialization" IStack (-peek [coll] - (-nth v (dec end))) + (when-not (== start end) + (-nth v (dec end)))) (-pop [coll] (if (== start end) (throw (js/Error. "Can't pop empty vector")) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index cd939a18d..20e6ad940 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1774,3 +1774,10 @@ (is (= '(3 2 1) (into nil [1 2 3])))) (testing "calling `set/union` with nilable sets returns a nilable set" (is (nil? (set/union #{} nil nil))))) + +(deftest test-cljs-3092 + (is (nil? (peek (subvec [] 0)))) + (is (nil? (peek (subvec [1] 1)))) + (is (nil? (peek (subvec [1 2] 0 0)))) + (is (nil? (peek (subvec [1 2] 1 1)))) + (is (nil? (peek (subvec [1 2] 2 2))))) From 570d066ecd0e5a3b45849a28b8b8238fa920a4ea Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 23 May 2019 18:33:42 -0400 Subject: [PATCH 3400/4033] CLJS-3093: Check subvec arguments --- src/main/cljs/cljs/core.cljs | 10 ++++------ src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 42381798b..3a35ac688 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5963,12 +5963,10 @@ reduces them without incurring seq initialization" (do (when-not (vector? v) (throw (js/Error. "v must satisfy IVector"))) - (let [c (count v)] - (when (or (neg? start) - (neg? end) - (> start c) - (> end c)) - (throw (js/Error. "Index out of bounds")))) + (when (or (neg? start) + (< end start) + (> end (count v))) + (throw (js/Error. "Index out of bounds"))) (Subvec. meta v start end __hash)))) (defn subvec diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 20e6ad940..046c6fdf6 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1781,3 +1781,11 @@ (is (nil? (peek (subvec [1 2] 0 0)))) (is (nil? (peek (subvec [1 2] 1 1)))) (is (nil? (peek (subvec [1 2] 2 2))))) + +(deftest test-cljs-3093 + (is (thrown-with-msg? js/Error #"Index out of bounds" (subvec [1 2 3 4] -1))) + (is (= [1 2 3 4] (subvec [1 2 3 4] -0.9))) + (is (thrown-with-msg? js/Error #"Index out of bounds" (subvec [1 2 3 4] 2 1))) + (is (= [] (subvec [1 2 3 4] 1.7 1.3))) + (is (thrown-with-msg? js/Error #"Index out of bounds" (subvec [1 2 3 4] 0 5))) + (is (= [1 2 3 4] (subvec [1 2 3 4] 0 4.9)))) From c4a5120294aa83b8f69d35ce10e5d03c951d99ea Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 10 May 2019 07:18:34 -0400 Subject: [PATCH 3401/4033] CLJS-3088: Update CI to use JavaScriptCore 4 --- .travis.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index cee9a3229..a68e6eb93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: trusty +dist: xenial git: depth: 1000 @@ -11,7 +11,7 @@ node_js: before_install: - wget https://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/jsshell-linux-x86_64.zip - unzip jsshell-linux-x86_64.zip -d spidermoney - - sudo apt-get install -y libjavascriptcoregtk-3.0-bin + - sudo apt-get install -y libjavascriptcoregtk-4.0-bin - wget https://aka.ms/chakracore/cc_linux_x64_1_8_1 -O chakra-core.tar.gz - tar xvzf chakra-core.tar.gz - wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc12/graalvm-ce-1.0.0-rc12-linux-amd64.tar.gz @@ -51,8 +51,6 @@ script: - grep '0 failures, 0 errors.' test-out.txt - ./spidermoney/js -f builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - jjs builds/out-adv/core-advanced-test.js | tee test-out.txt - - grep '0 failures, 0 errors.' test-out.txt - ./ChakraCoreFiles/bin/ch builds/out-adv/core-advanced-test.js | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - ./graalvm-ce-1.0.0-rc12/bin/js builds/out-adv/core-advanced-test.js | tee test-out.txt @@ -63,8 +61,6 @@ script: - grep '0 failures, 0 errors.' test-out.txt - script/test-cli node | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - script/test-cli nashorn | tee test-out.txt - - grep '0 failures, 0 errors.' test-out.txt - script/test-cli rhino | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - PATH=`pwd`/graalvm-ce-1.0.0-rc12/bin:$PATH script/test-cli graaljs | tee test-out.txt From 59ae42ec55522e39d4b1e4a511b3a7e1bba6ff09 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 28 May 2019 22:19:09 -0400 Subject: [PATCH 3402/4033] CLJS-3095: `apply vector` with array acts as `vec` In this case, clone the array. --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3a35ac688..0e7d0c244 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5721,7 +5721,7 @@ reduces them without incurring seq initialization" "Creates a new vector containing the args." [& args] (if (and (instance? IndexedSeq args) (zero? (.-i args))) - (.fromArray PersistentVector (.-arr args) true) + (.fromArray PersistentVector (.-arr args) (not (array? (.-arr args)))) (vec args))) (declare subvec) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 046c6fdf6..dd1add954 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1789,3 +1789,9 @@ (is (= [] (subvec [1 2 3 4] 1.7 1.3))) (is (thrown-with-msg? js/Error #"Index out of bounds" (subvec [1 2 3 4] 0 5))) (is (= [1 2 3 4] (subvec [1 2 3 4] 0 4.9)))) + +(deftest test-cljs-3095 + (let [a #js [:original] + v (apply vector a)] + (aset a 0 :modified) + (is (= :original (v 0))))) From e35be199f1c95a005ee40df3b5caeb03d04f960b Mon Sep 17 00:00:00 2001 From: Tom Mulvaney Date: Wed, 9 May 2018 10:51:00 +0100 Subject: [PATCH 3403/4033] CLJS-2750: tag coll in ci-reduce as not-native Also remove unused 4-arity version of ci-reduce --- src/main/cljs/cljs/core.cljs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0e7d0c244..6c2e04daa 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1454,7 +1454,7 @@ (defn- ci-reduce "Accepts any collection which satisfies the ICount and IIndexed protocols and reduces them without incurring seq initialization" - ([cicoll f] + ([^not-native cicoll f] (let [cnt (-count cicoll)] (if (zero? cnt) (f) @@ -1465,18 +1465,9 @@ reduces them without incurring seq initialization" @nval (recur nval (inc n)))) val))))) - ([cicoll f val] + ([^not-native cicoll f val] (let [cnt (-count cicoll)] (loop [val val, n 0] - (if (< n cnt) - (let [nval (f val (-nth cicoll n))] - (if (reduced? nval) - @nval - (recur nval (inc n)))) - val)))) - ([cicoll f val idx] - (let [cnt (-count cicoll)] - (loop [val val, n idx] (if (< n cnt) (let [nval (f val (-nth cicoll n))] (if (reduced? nval) From 42433848bafc973e018f926226d76016aab9a857 Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Thu, 6 Jun 2019 10:59:50 +0200 Subject: [PATCH 3404/4033] CLJS-3097: Fix compatibility with tools.reader 1.3.1 and bump it --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- src/main/clojure/cljs/core/server.clj | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deps.edn b/deps.edn index 976fa86d8..05b490db5 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,7 @@ {:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.clojure/tools.reader {:mvn/version "1.3.0"} + org.clojure/tools.reader {:mvn/version "1.3.2"} org.clojure/test.check {:mvn/version "0.10.0-alpha3"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} diff --git a/pom.template.xml b/pom.template.xml index aaf9ca808..6fced1126 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ org.clojure tools.reader - 1.3.0 + 1.3.2 com.cognitect diff --git a/project.clj b/project.clj index e291ae12e..1341c663d 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.3.0"] + [org.clojure/tools.reader "1.3.2"] [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 1accb60a4..bbbc295d1 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -96,8 +96,8 @@ (apply merge ((juxt :requires :require-macros) (ana/get-namespace ana/*cljs-ns*)))] - (reader/read+string in-reader - {:eof EOF :read-cond :allow :features #{:cljs}}))] + (reader/read+string {:eof EOF :read-cond :allow :features #{:cljs}} + in-reader))] (try (when-not (identical? form EOF) (let [start (System/nanoTime) From 4664996edd6f68ca6b37b2feaefeb0cf52b67e80 Mon Sep 17 00:00:00 2001 From: roman01la Date: Thu, 16 May 2019 14:12:54 +0300 Subject: [PATCH 3405/4033] CLJS-3085: Types are inferred for dynamic vars Infers dynamic vars as 'any, unless def has a type hint --- src/main/clojure/cljs/analyzer.cljc | 8 +++++--- src/test/cljs/cljs/binding_test.cljs | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 270f60ad1..24136f82e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1879,9 +1879,11 @@ (disallowing-ns* (analyze (assoc env :context :expr) (:init args) sym)))) fn-var? (and (some? init-expr) (= (:op init-expr) :fn)) - tag (if fn-var? - (or (:ret-tag init-expr) tag (:inferred-ret-tag init-expr)) - (or tag (:tag init-expr))) + tag (cond + fn-var? (or (:ret-tag init-expr) tag (:inferred-ret-tag init-expr)) + tag tag + dynamic ANY_SYM + :else (:tag init-expr)) export-as (when-let [export-val (-> sym meta :export)] (if (= true export-val) var-name export-val)) doc (or (:doc args) (-> sym meta :doc))] diff --git a/src/test/cljs/cljs/binding_test.cljs b/src/test/cljs/cljs/binding_test.cljs index ce711697f..c16277707 100644 --- a/src/test/cljs/cljs/binding_test.cljs +++ b/src/test/cljs/cljs/binding_test.cljs @@ -35,3 +35,22 @@ (is (= 2 (with-redefs [a 10 b (inc a)] b)))) + +(def ^:dynamic *foo* false) +(def ^:dynamic ^boolean *foo-tagged* false) + +(defn bar [] (if *foo* 1 2)) +(defn bar-tagged [] (if *foo-tagged* 1 2)) + +(deftest test-tag-inference + (is (= 2 (bar))) + (binding [*foo* "abc"] + (is (= 1 (bar)))) + (binding [*foo* ""] + (is (= 1 (bar)))) + + (is (= 2 (bar-tagged))) + (binding [*foo-tagged* "abc"] + (is (= 1 (bar-tagged)))) + (binding [*foo-tagged* ""] + (is (= 2 (bar-tagged))))) From cecb9af2b2bf65ddc31e9402496cc8dd1c443d36 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 16 Jul 2017 10:05:38 -0400 Subject: [PATCH 3406/4033] CLJS-2247: Warn when overwriting protocol method --- src/main/clojure/cljs/analyzer.cljc | 9 +++++++++ src/main/clojure/cljs/core.cljc | 5 +++++ src/test/clojure/cljs/analyzer_tests.clj | 12 ++++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 24136f82e..6cbd50e30 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -148,6 +148,7 @@ :protocol-duped-method true :protocol-multiple-impls true :protocol-with-variadic-method true + :protocol-with-overwriting-method true :protocol-impl-with-variadic-method true :protocol-impl-recur-with-target true :single-segment-namespace true @@ -398,6 +399,14 @@ (str "Protocol " (:protocol info) " declares method " (:name info) " with variadic signature (&)")) +(defmethod error-message :protocol-with-overwriting-method + [warning-type info] + (let [overwritten-protocol (-> info :existing :protocol)] + (str "Protocol " (:protocol info) " is overwriting " + (if overwritten-protocol "method" "function") + " " (:name info) + (when overwritten-protocol (str " of protocol " (name overwritten-protocol)))))) + (defmethod error-message :protocol-impl-with-variadic-method [warning-type info] (str "Protocol " (:protocol info) " implements method " diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3c58201fb..64aaa47ca 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2121,6 +2121,11 @@ (cljs.analyzer/warning :protocol-with-variadic-method &env {:protocol psym :name fname})) + _ (core/when-some [existing (core/get (-> &env :ns :defs) fname)] + (core/when-not (= p (:protocol existing)) + (cljs.analyzer/warning + :protocol-with-overwriting-method + {} {:protocol psym :name fname :existing existing}))) slot (symbol (core/str prefix (munge (name fname)))) fname (vary-meta fname assoc :protocol p diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 6967d07ea..ec7046c82 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1875,6 +1875,18 @@ :with-core? true})] (is (zero? (count @ws))))) +(deftest test-cljs-2247 + (let [ws (atom [])] + (try + (a/with-warning-handlers [(collecting-warning-handler ws)] + (e/with-compiler-env (assoc @test-cenv :repl-env {}) + (a/analyze (ana/empty-env) + '(defn -foo [])) + (a/analyze (ana/empty-env) + '(defprotocol IAlpha (-foo [this]))))) + (catch Exception _)) + (is (= ["Protocol IAlpha is overwriting function -foo"] @ws)))) + (deftest test-cljs-2385-infer-priority (let [ws (atom []) res (infer-test-helper From 4b6da5ea246e543cf8a1b7e5d2fdddc706cf5bd9 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 1 Sep 2018 15:31:36 -0400 Subject: [PATCH 3407/4033] CLJS-2879: Close analysis cache file Sets things up so that the file output stream is closed and that we also set up a buffered output stream and remove related unused imports. --- src/main/clojure/cljs/analyzer.cljc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6cbd50e30..f963bf360 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -32,7 +32,7 @@ [cljs.tools.reader :as reader] [cljs.tools.reader.reader-types :as readers] [cljs.reader :as edn])) - #?(:clj (:import [java.io File Reader PushbackReader FileOutputStream FileInputStream] + #?(:clj (:import [java.io File Reader PushbackReader] [java.util.regex Pattern] [java.net URL] [java.lang Throwable] @@ -4520,10 +4520,8 @@ (str ";; Analyzed by ClojureScript " (util/clojurescript-version) "\n" (pr-str analysis))) "json" (when-let [{:keys [writer write]} @transit] - (write - (writer (FileOutputStream. cache-file) :json - transit-write-opts) - analysis)))) + (with-open [os (io/output-stream cache-file)] + (write (writer os :json transit-write-opts) analysis))))) (when src (.setLastModified ^File cache-file (util/last-modified src)))))) From 23b5dde1e0442f8821997165b8ab7f18c5d57e38 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 2 Sep 2018 11:41:05 -0400 Subject: [PATCH 3408/4033] CLJS-2881: cl-format character directive with \space fails --- src/main/cljs/cljs/pprint.cljs | 1 + src/test/cljs/cljs/pprint_test.cljs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index fb349a1fa..2f60bcebf 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -32,6 +32,7 @@ (defn- print-char [c] (-write *out* (condp = c \backspace "\\backspace" + \space "\\space" \tab "\\tab" \newline "\\newline" \formfeed "\\formfeed" diff --git a/src/test/cljs/cljs/pprint_test.cljs b/src/test/cljs/cljs/pprint_test.cljs index a1832df7a..3719cdeb4 100644 --- a/src/test/cljs/cljs/pprint_test.cljs +++ b/src/test/cljs/cljs/pprint_test.cljs @@ -11,7 +11,7 @@ (:require-macros [cljs.pprint-test :refer [simple-tests code-block]]) (:require - [cljs.test :as t :refer-macros [deftest is]] + [cljs.test :as t :refer-macros [deftest is are]] [cljs.pprint :refer [pprint cl-format get-pretty-writer prn print-table *print-pprint-dispatch* simple-dispatch *print-right-margin* *print-miser-width* @@ -1087,3 +1087,14 @@ Usage: *hello* " ) +(deftest test-cljs-2881 + (are [expected ch] + (= expected (with-out-str (cl-format true "~@c" ch))) + "\\newline" \newline + "\\space" \space + "\\tab" \tab + "\\backspace" \backspace + "\\return" \return + "\\formfeed" \formfeed + "\\\"" \" + "\\\\" \\)) From 3768e771e906684da36d065a3c9d863b8e90bf40 Mon Sep 17 00:00:00 2001 From: uosl Date: Tue, 25 Sep 2018 20:50:34 +0200 Subject: [PATCH 3409/4033] CLJS-2683: Suppress compiler options in watch log Do not log `opts` passed to ClojureScript compiler when build is started from `cljs.closure/watch`. --- src/main/clojure/cljs/closure.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a4fc8b8e0..85ec58e47 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2985,7 +2985,7 @@ (repeat warnings)) warnings))) ana/*verbose* (:verbose opts)] - (when ana/*verbose* + (when (and ana/*verbose* (not (::watch-triggered-build? opts))) (util/debug-prn "Options passed to ClojureScript compiler:" (pr-str opts))) (let [one-file? (and (:main opts) (#{:advanced :simple :whitespace} (:optimizations opts))) @@ -3128,8 +3128,9 @@ srvc (.newWatchService fs)] (letfn [(buildf [] (try - (let [start (System/nanoTime)] - (build source opts compiler-env) + (let [start (System/nanoTime) + watch-opts (assoc opts ::watch-triggered-build? true)] + (build source watch-opts compiler-env) (println "... done. Elapsed" (/ (unchecked-subtract (System/nanoTime) start) 1e9) "seconds") (flush)) From f312d5de4ef59781ff370a947e5d11af56562f50 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 30 Oct 2018 08:50:05 -0400 Subject: [PATCH 3410/4033] CLJS-2950: Direct field access for keyword lookup on records --- src/main/clojure/cljs/analyzer.cljc | 27 ++++++++++++++++++++++-- src/test/cljs/cljs/invoke_test.cljs | 6 ++++++ src/test/clojure/cljs/compiler_tests.clj | 4 +++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f963bf360..c9d75506d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3558,6 +3558,23 @@ [argc method-params] (boolean (some #{argc} (map count method-params)))) +(defn- record-tag? + [tag] + (boolean (and (symbol? tag) + (some? (namespace tag)) + (get-in @env/*compiler* [::namespaces (symbol (namespace tag)) :defs (symbol (name tag)) :record])))) + +(defn- record-basis + [tag] + (let [positional-factory (symbol (str "->" (name tag))) + fields (first (get-in @env/*compiler* [::namespaces (symbol (namespace tag)) :defs positional-factory :method-params]))] + (into #{} fields))) + +(defn- record-with-field? + [tag field] + (and (record-tag? tag) + (contains? (record-basis tag) field))) + (defn parse-invoke* [env [f & args :as form]] (let [enve (assoc env :context :expr) @@ -3611,8 +3628,14 @@ ~@(if bind-args? arg-syms args))))) (let [ana-expr #(analyze enve %) argexprs (mapv ana-expr args)] - {:env env :op :invoke :form form :fn fexpr :args argexprs - :children [:fn :args]})))) + (if (and (and (keyword? f) + (nil? (namespace f))) + (== 1 (count args)) + (record-with-field? (:tag (first argexprs)) (symbol (name f)))) + (let [field-access-form (list* (symbol (str ".-" (name f))) args)] + (analyze env field-access-form)) + {:env env :op :invoke :form form :fn fexpr :args argexprs + :children [:fn :args]}))))) (defn parse-invoke [env form] diff --git a/src/test/cljs/cljs/invoke_test.cljs b/src/test/cljs/cljs/invoke_test.cljs index c38d1b400..194e959bb 100644 --- a/src/test/cljs/cljs/invoke_test.cljs +++ b/src/test/cljs/cljs/invoke_test.cljs @@ -34,3 +34,9 @@ (declare ^{:arglists '([a b])} declared-fn) (declared-fn 1 2) + +(defrecord Foo [foo-field-a foo-field-b]) + +(def foo-record (->Foo 1 2)) + +(:foo-field-a foo-record) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 59857b9f1..b48e3c751 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -281,7 +281,9 @@ (is (re-find #"(?m)^.*var .*=.*inv_arg1.cljs.core.IFn._invoke.arity.0 \?.*$" content)) ;; CLJS-1871: A declare hinted with :arglists meta should result in static dispatch - (is (str/includes? content "cljs.invoke_test.declared_fn("))))) + (is (str/includes? content "cljs.invoke_test.declared_fn(")) + ;; CLJS-2950: Direct field access for keyword lookup on records + (is (str/includes? content "cljs.invoke_test.foo_record.foo_field_a;"))))) #_(test-vars [#'test-optimized-invoke-emit]) ;; CLJS-1225 From d64b2333f3127b51dbe410874a7cdff4bac1edf8 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 15 Apr 2019 22:10:43 -0400 Subject: [PATCH 3411/4033] CLJS-2886: count specializations for string and array --- src/main/clojure/cljs/compiler.cljc | 8 +++++++- src/test/cljs/cljs/core_test.cljs | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index f035ae0d8..4d68b81e5 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1136,8 +1136,11 @@ (not ('#{any clj clj-or-nil clj-nil number string boolean function object array js} tag)) (when-let [ps (:protocols (ana/resolve-existing-var env tag))] (ps protocol))))))) + first-arg-tag (ana/infer-tag env (first (:args expr))) opt-not? (and (= (:name info) 'cljs.core/not) - (= (ana/infer-tag env (first (:args expr))) 'boolean)) + (= first-arg-tag 'boolean)) + opt-count? (and (= (:name info) 'cljs.core/count) + (boolean ('#{string array} first-arg-tag))) ns (:ns info) js? (or (= ns 'js) (= ns 'Math)) goog? (when ns @@ -1193,6 +1196,9 @@ opt-not? (emits "(!(" (first args) "))") + opt-count? + (emits "((" (first args) ").length)") + proto? (let [pimpl (str (munge (protocol-prefix protocol)) (munge (name (:name info))) "$arity$" (count args))] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index dd1add954..ceb9d02d7 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1703,6 +1703,14 @@ (is (= "ab" (str "a" nil "b"))) (is (= "ahellob" (str "a" (str-fn-2865) "b")))) +(deftest test-cljs-2886 + (is (zero? (count ""))) + (is (== 1 (count "a"))) + (is (zero? (count #js []))) + (is (== 1 (count #js [1]))) + (is (zero? (count []))) + (is (== 1 (count [1])))) + (deftest test-cljs-2934 (let [x (delay 1)] (is (= "#object[cljs.core.Delay {:status :pending, :val nil}]" (pr-str x))) From bc6ecdd6e53ccff42315ed747e34ace465def986 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 15 Dec 2018 15:05:02 -0500 Subject: [PATCH 3412/4033] CLJS-3017: Error->map: Map js/InternalError and js/TypeError --- src/main/cljs/cljs/repl.cljs | 7 +------ src/test/cljs/cljs/repl_test.cljs | 16 ++++++++++++++++ src/test/cljs/test_runner.cljs | 2 ++ src/test/self/self_parity/test.cljs | 5 ++++- 4 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 src/test/cljs/cljs/repl_test.cljs diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index 351a86d38..b28b2a153 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -73,12 +73,7 @@ (let [base (fn [t] (merge {:type (cond (instance? ExceptionInfo t) 'ExceptionInfo - (instance? js/EvalError t) 'js/EvalError - (instance? js/RangeError t) 'js/RangeError - (instance? js/ReferenceError t) 'js/ReferenceError - (instance? js/SyntaxError t) 'js/SyntaxError - (instance? js/URIError t) 'js/URIError - (instance? js/Error t) 'js/Error + (instance? js/Error t) (symbol "js" (.-name t)) :else nil)} (when-let [msg (ex-message t)] {:message msg}) diff --git a/src/test/cljs/cljs/repl_test.cljs b/src/test/cljs/cljs/repl_test.cljs new file mode 100644 index 000000000..27ecb63e4 --- /dev/null +++ b/src/test/cljs/cljs/repl_test.cljs @@ -0,0 +1,16 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns cljs.repl-test + (:require + [cljs.repl] + [cljs.test :refer [deftest is]])) + +(deftest test-cljs-3017 + (let [m (cljs.repl/Error->map (js/TypeError.))] + (is (= 'js/TypeError (get-in m [:via 0 :type]))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index bc19fdccb..7e1ddd7c5 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -51,6 +51,7 @@ [cljs.array-access-test] [cljs.inference-test] [cljs.walk-test] + [cljs.repl-test] [cljs.extend-to-native-test])) (set! *print-newline* false) @@ -100,4 +101,5 @@ 'cljs.array-access-test 'cljs.inference-test 'cljs.walk-test + 'cljs.repl-test 'cljs.extend-to-native-test) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 5bba020b3..63119dc56 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -176,7 +176,8 @@ technical issues)." [name macros] ((if macros - #{'cljs.core} + #{'cljs.core + 'cljs.repl} #{'goog.object 'goog.string 'goog.string.StringBuffer @@ -315,6 +316,7 @@ [cljs.array-access-test] [cljs.inference-test] [cljs.walk-test] + [cljs.repl-test] [cljs.extend-to-native-test])) (fn [{:keys [value error]}] (if error @@ -362,6 +364,7 @@ 'cljs.array-access-test 'cljs.inference-test 'cljs.walk-test + 'cljs.repl-test 'cljs.extend-to-native-test) (fn [{:keys [value error]}] (when error From be5e2c698f01800cab8da900235bb85a2636fd93 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 25 Jun 2019 16:11:37 -0400 Subject: [PATCH 3413/4033] clean up the analyzer tests ns - remove duped reuqires, order requires, drop :use --- src/test/clojure/cljs/analyzer_tests.clj | 627 +++++++++++------------ 1 file changed, 313 insertions(+), 314 deletions(-) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index ec7046c82..52e48bb76 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -7,33 +7,32 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.analyzer-tests - (:require [clojure.java.io :as io] - [cljs.util :as util] - [clojure.set :as set] - [cljs.env :as e] - [cljs.env :as env] - [cljs.analyzer :as a] - [cljs.analyzer.api :as ana-api] - [cljs.compiler :as comp] - [cljs.closure :as closure] - [cljs.externs :as externs] - [cljs.analyzer :as ana] - [clojure.string :as string] - [cljs.test-util :refer [unsplit-lines]]) - (:use clojure.test)) + (:require + [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] + [cljs.compiler :as comp] + [cljs.closure :as closure] + [cljs.env :as env] + [cljs.externs :as externs] + [cljs.test-util :refer [unsplit-lines]] + [cljs.util :as util] + [clojure.java.io :as io] + [clojure.set :as set] + [clojure.string :as string] + [clojure.test :refer [is are deftest testing]])) (defn analyze ([env form] - (env/ensure (a/analyze env form))) + (env/ensure (ana/analyze env form))) ([env form name] - (env/ensure (a/analyze env form name))) + (env/ensure (ana/analyze env form name))) ([env form name opts] - (env/ensure (a/analyze env form name opts)))) + (env/ensure (ana/analyze env form name opts)))) (defn collecting-warning-handler [state] (fn [warning-type env extra] - (when (warning-type a/*cljs-warnings*) - (when-let [s (a/error-message warning-type extra)] + (when (warning-type ana/*cljs-warnings*) + (when-let [s (ana/error-message warning-type extra)] (swap! state conj s))))) ;;****************************************************************************** @@ -49,22 +48,22 @@ (defn warn-count [form] (let [counter (atom 0) tracker (fn [warning-type env & [extra]] - (when (warning-type a/*cljs-warnings*) + (when (warning-type ana/*cljs-warnings*) (swap! counter inc)))] - (a/with-warning-handlers [tracker] - (analyze (a/empty-env) form)) + (ana/with-warning-handlers [tracker] + (analyze (ana/empty-env) form)) @counter)) (deftest no-warn - (is (every? zero? (map (fn [[name form]] (a/no-warn (warn-count form))) warning-forms)))) + (is (every? zero? (map (fn [[name form]] (ana/no-warn (warn-count form))) warning-forms)))) (deftest all-warn - (is (every? #(= 1 %) (map (fn [[name form]] (a/all-warn (warn-count form))) warning-forms)))) + (is (every? #(= 1 %) (map (fn [[name form]] (ana/all-warn (warn-count form))) warning-forms)))) ;; ============================================================================= ;; NS parsing -(def ns-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)) +(def ns-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) (deftest spec-validation (is (.startsWith @@ -186,107 +185,107 @@ ;; Inference tests (def test-cenv (atom {})) -(def test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.core)) +(def test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.core)) -(a/no-warn - (e/with-compiler-env test-cenv - (binding [a/*analyze-deps* false] - (a/analyze-file (io/file "src/main/cljs/cljs/core.cljs"))))) +(ana/no-warn + (env/with-compiler-env test-cenv + (binding [ana/*analyze-deps* false] + (ana/analyze-file (io/file "src/main/cljs/cljs/core.cljs"))))) (deftest basic-inference - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '1))) 'number)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '"foo"))) 'string)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '\a))) 'string)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(make-array 10)))) 'array)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(js-obj)))) 'object)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '[]))) 'cljs.core/IVector)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '{}))) 'cljs.core/IMap)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '#{}))) 'cljs.core/ISet)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env ()))) 'cljs.core/IList)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(fn [x] x)))) 'function))) (deftest if-inference - (is (= (a/no-warn - (e/with-compiler-env test-cenv + (is (= (ana/no-warn + (env/with-compiler-env test-cenv (:tag (analyze test-env '(if x "foo" 1))))) '#{number string}))) (deftest if-induced-inference - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (nil? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (nil? x) x :kw)))))) '#{clj-nil cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (boolean? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (boolean? x) x :kw)))))) '#{boolean cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (number? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (number? x) x :kw)))))) '#{number cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (double? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (double? x) x :kw)))))) '#{number cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (float? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (float? x) x :kw)))))) '#{number cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (integer? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (integer? x) x :kw)))))) '#{number cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (seq? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (seq? x) x :kw)))))) '#{seq cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (array? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (array? x) x :kw)))))) '#{array cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x ^any []] (if (seqable? x) x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (seqable? x) x :kw)))))) '#{cljs.core/ISeqable array string cljs.core/Keyword})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv - (:tag (a/analyze test-env '(let [x (namespace :x)] (if x x :kw)))))) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x (namespace :x)] (if x x :kw)))))) '#{string cljs.core/Keyword}))) (deftest loop-recur-inference - (is (= (a/no-warn - (e/with-compiler-env test-cenv + (is (= (ana/no-warn + (env/with-compiler-env test-cenv (:tag (analyze test-env '(loop [x "a"] x))))) 'string)) - (is (= (a/no-warn - (e/with-compiler-env test-cenv + (is (= (ana/no-warn + (env/with-compiler-env test-cenv (:tag (analyze test-env '(loop [x 10] (if (pos? x) (dec x) x)))))) 'number)) - (is (= (a/no-warn - (e/with-compiler-env test-cenv + (is (= (ana/no-warn + (env/with-compiler-env test-cenv (:tag (analyze test-env '((fn [p?] (loop [x nil] (if (p? x) @@ -294,8 +293,8 @@ (recur (str x))))) 11))))) '#{string clj-nil})) - (is (= (a/no-warn - (e/with-compiler-env test-cenv + (is (= (ana/no-warn + (env/with-compiler-env test-cenv (:tag (analyze test-env '((fn [^string x] (loop [y x] (if (= "x" y) @@ -305,7 +304,7 @@ '#{number string}))) (deftest method-inference - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(.foo js/bar)))) 'js))) @@ -313,47 +312,47 @@ ;; should always infer 'function as tag (is (= 'function (:tag - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (analyze test-env '(fn ([a] 1) ([a b] "foo") ([a b & r] ()))))))) (is (nil? (:ret-tag - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (analyze test-env '(fn ([a] 1) ([a b] "foo") ([a b & r] ())))))) ) ;; methods should have inferred types (is (= '(number string cljs.core/IList) (map :tag (:methods - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (analyze test-env '(fn ([a] 1) ([a b] "foo") ([a b & r] ()))))))))) (deftest fn-inference (is (= 'number - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] (x :one))))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] (x :one :two))))))) (is (= 'cljs.core/IList - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] (x :one :two :three))))))) (is (= 'cljs.core/IList - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] (x :one :two :three :four)))))))) (deftest top-fn-inference - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-2901) (defn foo ([a] 1) @@ -362,11 +361,11 @@ (foo :one)])) (is (= '[number string cljs.core/IList] (map :tag - (get-in @test-cenv [::a/namespaces 'test.cljs-2901 :defs 'foo :methods])))) + (get-in @test-cenv [::ana/namespaces 'test.cljs-2901 :defs 'foo :methods])))) (is (= 'number (:tag - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-2901) (defn foo ([a] 1) @@ -376,8 +375,8 @@ nil true))))) (is (= 'string (:tag - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-2901) (defn foo ([a] 1) @@ -387,8 +386,8 @@ nil true))))) (is (= 'cljs.core/IList (:tag - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-2901) (defn foo ([a] 1) @@ -401,31 +400,31 @@ (is (= '(cljs.core/IList) (map :tag (:methods - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (analyze test-env '(fn ([a b & r] ())))))))) (is (= 'cljs.core/IList - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(let [x (fn ([a b & r] ()))] (x :one :two))))))) (is (= 'cljs.core/IList - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(let [x (fn ([a b & r] ()))] (x :one :two :three))))))) (is (= 'cljs.core/IList - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(let [x (fn ([a b & r] ()))] (x :one :two :three :four))))))) ) (deftest top-variadic-fn-inference - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-2901-b) (defn foo ([a b & r] ())) (foo :one :two :three :four)] @@ -433,71 +432,71 @@ (is (= '[cljs.core/IList] (map :tag (get-in @test-cenv - [::a/namespaces 'test.cljs-2901-b :defs 'foo :methods])))) + [::ana/namespaces 'test.cljs-2901-b :defs 'foo :methods])))) (is (= 'cljs.core/IList (:tag - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-2901-b) (defn foo ([a b & r] ())) (foo :one :two)] nil true))))) (is (= 'cljs.core/IList (:tag - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-2901-b) (defn foo ([a b & r] ())) (foo :one :two :three)] nil true))))) (is (= 'cljs.core/IList (:tag - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-2901-b) (defn foo ([a b & r] ())) (foo :one :two :three :four)] nil true)))))) (deftest lib-inference - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(+ 1 2)))) 'number)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(alength (array))))) 'number)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(aclone (array))))) 'array)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(-count [1 2 3])))) 'number)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(count [1 2 3])))) 'number)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(into-array [1 2 3])))) 'array)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(js-obj)))) 'object)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(-conj [] 1)))) 'clj)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(conj [] 1)))) 'clj)) - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(dissoc {:foo :bar} :foo)))) '#{clj clj-nil})) ;; has changed, why does this return #{clj any} ? - ;(is (= (e/with-compiler-env test-cenv + ;(is (= (env/with-compiler-env test-cenv ; (:tag (analyze test-env '(assoc nil :foo :bar)))) ; 'clj)) ) (deftest test-always-true-if - (is (= (e/with-compiler-env test-cenv + (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(if 1 2 "foo")))) 'number))) @@ -509,31 +508,31 @@ ) (deftest test-numeric - (is (= (a/no-warn + (is (= (ana/no-warn (cljs.env/with-compiler-env test-cenv (:tag (analyze test-env '(dec x))))) 'number)) - (is (= (a/no-warn + (is (= (ana/no-warn (cljs.env/with-compiler-env test-cenv (:tag (analyze test-env '(int x))))) 'number)) - (is (= (a/no-warn + (is (= (ana/no-warn (cljs.env/with-compiler-env test-cenv (:tag (analyze test-env '(unchecked-int x))))) 'number)) - (is (= (a/no-warn + (is (= (ana/no-warn (cljs.env/with-compiler-env test-cenv (:tag (analyze test-env '(mod x y))))) 'number)) - (is (= (a/no-warn + (is (= (ana/no-warn (cljs.env/with-compiler-env test-cenv (:tag (analyze test-env '(quot x y))))) 'number)) - (is (= (a/no-warn + (is (= (ana/no-warn (cljs.env/with-compiler-env test-cenv (:tag (analyze test-env '(rem x y))))) 'number)) - (is (= (a/no-warn + (is (= (ana/no-warn (cljs.env/with-compiler-env test-cenv (:tag (analyze test-env '(bit-count n))))) 'number)) @@ -555,25 +554,25 @@ (deftest test-cljs-975 (let [spec '((:require [bar :refer [baz] :refer-macros [quux]] :reload))] - (is (= (set (a/desugar-ns-specs spec)) + (is (= (set (ana/desugar-ns-specs spec)) (set '((:require-macros (bar :refer [quux]) :reload) (:require (bar :refer [baz]) :reload))))))) (deftest test-rewrite-cljs-aliases - (is (= (a/rewrite-cljs-aliases + (is (= (ana/rewrite-cljs-aliases '((:require-macros (bar :refer [quux]) :reload) (:require (clojure.spec.alpha :as s :refer [fdef]) :reload))) '((:require-macros (bar :refer [quux]) :reload) (:require (cljs.spec.alpha :as s :refer [fdef]) (cljs.spec.alpha :as clojure.spec.alpha) :reload)))) - (is (= (a/rewrite-cljs-aliases + (is (= (ana/rewrite-cljs-aliases '((:refer-clojure :exclude [first]) (:require-macros (bar :refer [quux]) :reload) (:require (clojure.spec.alpha :as s) :reload))) '((:refer-clojure :exclude [first]) (:require-macros (bar :refer [quux]) :reload) (:require (cljs.spec.alpha :as s) (cljs.spec.alpha :as clojure.spec.alpha) :reload)))) - (is (= (a/rewrite-cljs-aliases + (is (= (ana/rewrite-cljs-aliases '((:require-macros (bar :refer [quux]) :reload) (:require clojure.spec.alpha :reload))) '((:require-macros (bar :refer [quux]) :reload) @@ -583,21 +582,21 @@ ;; Namespace metadata (deftest test-namespace-metadata - (binding [a/*cljs-ns* a/*cljs-ns*] + (binding [ana/*cljs-ns* ana/*cljs-ns*] (is (= (do (analyze ns-env '(ns weeble.ns {:foo bar})) - (meta a/*cljs-ns*)) + (meta ana/*cljs-ns*)) {:foo 'bar})) (is (= (do (analyze ns-env '(ns ^{:foo bar} weeble.ns)) - (meta a/*cljs-ns*)) + (meta ana/*cljs-ns*)) {:foo 'bar})) (is (= (do (analyze ns-env '(ns ^{:foo bar} weeble.ns {:baz quux})) - (meta a/*cljs-ns*)) + (meta ana/*cljs-ns*)) {:foo 'bar :baz 'quux})) (is (= (do (analyze ns-env '(ns ^{:foo bar} weeble.ns {:foo baz})) - (meta a/*cljs-ns*)) + (meta ana/*cljs-ns*)) {:foo 'baz})) (is (= (meta (:name (analyze ns-env '(ns weeble.ns {:foo bar})))) @@ -614,18 +613,18 @@ (deftest test-cljs-1105 ;; munge turns - into _, must preserve the dash first - (is (not= (a/gen-constant-id :test-kw) - (a/gen-constant-id :test_kw)))) + (is (not= (ana/gen-constant-id :test-kw) + (ana/gen-constant-id :test_kw)))) (deftest test-symbols-munge-cljs-1432 - (is (not= (a/gen-constant-id :$) - (a/gen-constant-id :.))) - (is (not= (a/gen-constant-id '$) - (a/gen-constant-id '.)))) + (is (not= (ana/gen-constant-id :$) + (ana/gen-constant-id :.))) + (is (not= (ana/gen-constant-id '$) + (ana/gen-constant-id '.)))) (deftest test-unicode-munging-cljs-1457 - (is (= (a/gen-constant-id :C♯) 'cst$kw$C_u266f_) - (= (a/gen-constant-id 'C♯) 'cst$sym$C_u266f_))) + (is (= (ana/gen-constant-id :C♯) 'cst$kw$C_u266f_) + (= (ana/gen-constant-id 'C♯) 'cst$sym$C_u266f_))) ;; Constants @@ -644,8 +643,8 @@ "Can't set! a constant"))) (deftest test-cljs-1508-rename - (binding [a/*cljs-ns* a/*cljs-ns*] - (let [parsed-ns (e/with-compiler-env test-cenv + (binding [ana/*cljs-ns* ana/*cljs-ns*] + (let [parsed-ns (env/with-compiler-env test-cenv (analyze test-env '(ns foo.core (:require [clojure.set :as set :refer [intersection] :rename {intersection foo}]))))] @@ -654,22 +653,22 @@ (is (some? (-> parsed-ns :renames (get 'foo)))) (is (= (-> parsed-ns :renames (get 'foo)) 'clojure.set/intersection))) - (is (e/with-compiler-env test-cenv + (is (env/with-compiler-env test-cenv (analyze test-env '(ns foo.core (:use [clojure.set :only [intersection] :rename {intersection foo}]))))) - (is (= (e/with-compiler-env (atom {::a/namespaces + (is (= (env/with-compiler-env (atom {::ana/namespaces {'foo.core {:renames '{foo clojure.set/intersection}}}}) - (select-keys (a/resolve-var {:ns {:name 'foo.core}} 'foo) + (select-keys (ana/resolve-var {:ns {:name 'foo.core}} 'foo) [:name :ns])) '{:name clojure.set/intersection :ns clojure.set})) - (let [rwhen (e/with-compiler-env (atom (update-in @test-cenv [::a/namespaces] + (let [rwhen (env/with-compiler-env (atom (update-in @test-cenv [::ana/namespaces] merge {'foo.core {:rename-macros '{always cljs.core/when}}})) - (a/resolve-macro-var {:ns {:name 'foo.core}} 'always))] + (ana/resolve-macro-var {:ns {:name 'foo.core}} 'always))] (is (= (-> rwhen :name) 'cljs.core/when))) - (let [parsed-ns (e/with-compiler-env test-cenv + (let [parsed-ns (env/with-compiler-env test-cenv (analyze test-env '(ns foo.core (:refer-clojure :rename {when always @@ -677,29 +676,29 @@ (is (= (-> parsed-ns :excludes) #{})) (is (= (-> parsed-ns :rename-macros) '{always cljs.core/when})) (is (= (-> parsed-ns :renames) '{core-map cljs.core/map}))) - (is (thrown? Exception (e/with-compiler-env test-cenv + (is (thrown? Exception (env/with-compiler-env test-cenv (analyze test-env '(ns foo.core (:require [clojure.set :rename {intersection foo}])))))))) (deftest test-cljs-1274 - (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] - (binding [a/*cljs-ns* a/*cljs-ns*] + (let [test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)] + (binding [ana/*cljs-ns* ana/*cljs-ns*] (is (thrown-with-cause-msg? Exception #"Can't def ns-qualified name in namespace foo.core" (analyze test-env '(def foo.core/foo 43)))) (is (analyze test-env '(def cljs.user/foo 43)))))) (deftest test-cljs-1702 (let [ws (atom [])] - (a/with-warning-handlers [(collecting-warning-handler ws)] - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-1702-a) (def ^:private a 3) (def ^:private b 3) (defn- test-fn-a [a] a) (defn- test-fn-b [a] b)]) - (a/analyze-form-seq + (ana/analyze-form-seq '[(ns test.cljs-1702-b) (test.cljs-1702-a/test-fn-a 1) (#'test.cljs-1702-a/test-fn-b 1) @@ -709,7 +708,7 @@ "var: test.cljs-1702-a/a is not public"] @ws))))) (deftest test-cljs-1763 - (let [parsed (a/parse-ns-excludes {} '())] + (let [parsed (ana/parse-ns-excludes {} '())] (is (= parsed {:excludes #{} :renames {}})) @@ -717,7 +716,7 @@ (deftest test-cljs-1785-js-shadowed-by-local (let [ws (atom [])] - (a/with-warning-handlers [(collecting-warning-handler ws)] + (ana/with-warning-handlers [(collecting-warning-handler ws)] (analyze ns-env '(fn [foo] (let [x js/foo] @@ -727,8 +726,8 @@ (deftest test-cljs-2005 (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] - (analyze (a/empty-env) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (analyze (ana/empty-env) '(defn myfun ([x] x) ([x] x)))) @@ -736,53 +735,53 @@ (is (.startsWith (first @ws) "myfun: Can't have 2 overloads with same arity")))) (deftest test-canonicalize-specs - (is (= (a/canonicalize-specs '((quote [clojure.set :as set]))) + (is (= (ana/canonicalize-specs '((quote [clojure.set :as set]))) '([clojure.set :as set]))) - (is (= (a/canonicalize-specs '(:exclude (quote [map mapv]))) + (is (= (ana/canonicalize-specs '(:exclude (quote [map mapv]))) '(:exclude [map mapv]))) - (is (= (a/canonicalize-specs '(:require (quote [clojure.set :as set]))) + (is (= (ana/canonicalize-specs '(:require (quote [clojure.set :as set]))) '(:require [clojure.set :as set]))) - (is (= (a/canonicalize-specs '(:require (quote clojure.set))) + (is (= (ana/canonicalize-specs '(:require (quote clojure.set))) '(:require [clojure.set]))) - (is (= (a/canonicalize-specs '(:refer-clojure :exclude '[map] :rename '{map core-map})) + (is (= (ana/canonicalize-specs '(:refer-clojure :exclude '[map] :rename '{map core-map})) '(:refer-clojure :exclude [map] :rename {map core-map})))) (deftest test-canonicalize-import-specs - (is (= (a/canonicalize-import-specs '(:import (quote [goog Uri]))) + (is (= (ana/canonicalize-import-specs '(:import (quote [goog Uri]))) '(:import [goog Uri]))) - (is (= (a/canonicalize-import-specs '(:import (quote (goog Uri)))) + (is (= (ana/canonicalize-import-specs '(:import (quote (goog Uri)))) '(:import (goog Uri)))) - (is (= (a/canonicalize-import-specs '(:import (quote goog.Uri))) + (is (= (ana/canonicalize-import-specs '(:import (quote goog.Uri))) '(:import goog.Uri)))) (deftest test-cljs-1346 (testing "`ns*` special form conformance" - (let [test-env (a/empty-env)] - (is (= (-> (a/parse-ns '((require '[clojure.set :as set]))) :requires) + (let [test-env (ana/empty-env)] + (is (= (-> (ana/parse-ns '((require '[clojure.set :as set]))) :requires) '#{cljs.core clojure.set}))) - (binding [a/*cljs-ns* a/*cljs-ns* - a/*cljs-warnings* nil] - (let [test-env (a/empty-env)] + (binding [ana/*cljs-ns* ana/*cljs-ns* + ana/*cljs-warnings* nil] + (let [test-env (ana/empty-env)] (is (= (-> (analyze test-env '(require '[clojure.set :as set])) :requires vals set) '#{clojure.set}))) - (let [test-env (a/empty-env)] + (let [test-env (ana/empty-env)] (is (= (-> (analyze test-env '(require '[clojure.set :as set :refer [union intersection]])) :uses keys set) '#{union intersection}))) - (let [test-env (a/empty-env)] + (let [test-env (ana/empty-env)] (is (= (-> (analyze test-env '(require '[clojure.set :as set] '[clojure.string :as str])) :requires vals set) '#{clojure.set clojure.string}))) - (let [test-env (a/empty-env)] + (let [test-env (ana/empty-env)] (is (= (-> (analyze test-env '(require-macros '[cljs.test :as test])) :require-macros vals set) '#{cljs.test}))) - (let [test-env (a/empty-env) + (let [test-env (ana/empty-env) parsed (analyze test-env '(require-macros '[cljs.test :as test :refer [deftest is]]))] (is (= (-> parsed :require-macros vals set) '#{cljs.test})) (is (= (-> parsed :use-macros keys set) '#{is deftest}))) - (let [test-env (a/empty-env) + (let [test-env (ana/empty-env) parsed (analyze test-env '(require '[cljs.test :as test :refer-macros [deftest is]]))] (is (= (-> parsed :requires vals set) '#{cljs.test})) @@ -790,36 +789,36 @@ '#{cljs.test})) (is (= (-> parsed :use-macros keys set) '#{is deftest}))) - (let [test-env (a/empty-env) + (let [test-env (ana/empty-env) parsed (analyze test-env '(use '[clojure.set :only [intersection]]))] (is (= (-> parsed :uses keys set) '#{intersection})) (is (= (-> parsed :requires) '{clojure.set clojure.set}))) - (let [test-env (a/empty-env) + (let [test-env (ana/empty-env) parsed (analyze test-env '(use-macros '[cljs.test :only [deftest is]]))] (is (= (-> parsed :use-macros keys set) '#{deftest is})) (is (= (-> parsed :require-macros) '{cljs.test cljs.test})) (is (nil? (-> parsed :requires)))) - (let [test-env (a/empty-env) + (let [test-env (ana/empty-env) parsed (analyze test-env '(import '[goog.math Long Integer]))] (is (= (-> parsed :imports) (-> parsed :requires) '{Long goog.math.Long Integer goog.math.Integer}))) - (let [test-env (a/empty-env) + (let [test-env (ana/empty-env) parsed (analyze test-env '(refer-clojure :exclude '[map mapv]))] (is (= (-> parsed :excludes) '#{map mapv}))) - (let [test-env (a/empty-env) + (let [test-env (ana/empty-env) parsed (analyze test-env '(refer-clojure :exclude '[map mapv] :rename '{mapv core-mapv}))] (is (= (-> parsed :excludes) '#{map mapv}))))) (testing "arguments to require should be quoted" - (binding [a/*cljs-ns* a/*cljs-ns* - a/*cljs-warnings* nil] + (binding [ana/*cljs-ns* ana/*cljs-ns* + ana/*cljs-warnings* nil] (is (thrown-with-cause-msg? Exception #"Arguments to require must be quoted" (analyze test-env '(require [clojure.set :as set])))) @@ -827,8 +826,8 @@ (analyze test-env '(require clojure.set)))))) (testing "`:ns` and `:ns*` should throw if not `:top-level`" - (binding [a/*cljs-ns* a/*cljs-ns* - a/*cljs-warnings* nil] + (binding [ana/*cljs-ns* ana/*cljs-ns* + ana/*cljs-warnings* nil] (are [analyzed] (thrown-with-cause-msg? Exception #"Namespace declarations must appear at the top-level." analyzed) @@ -858,23 +857,23 @@ ;; note: can't use `with-redefs` because direct-linking is enabled (let [s "src/cljs/foo.cljs" sha (util/content-sha s)] - (is (= (a/gen-user-ns s) (symbol (str "cljs.user.foo" (apply str (take 7 sha))))))) + (is (= (ana/gen-user-ns s) (symbol (str "cljs.user.foo" (apply str (take 7 sha))))))) (let [a "src/cljs/foo.cljs" b "src/cljs/foo.cljc"] ;; namespaces should have different names because the filename hash will be different - (is (not= (a/gen-user-ns a) (a/gen-user-ns b))) + (is (not= (ana/gen-user-ns a) (ana/gen-user-ns b))) ;; specifically, only the hashes should differ - (let [nsa (str (a/gen-user-ns a)) - nsb (str (a/gen-user-ns b))] + (let [nsa (str (ana/gen-user-ns a)) + nsb (str (ana/gen-user-ns b))] (is (not= (.substring nsa (- (count nsa) 7)) (.substring nsb (- (count nsb) 7)))) (is (= (.substring nsa 0 (- (count nsa) 7)) (.substring nsb 0 (- (count nsb) 7))))))) (deftest test-cljs-1536 - (let [parsed (e/with-compiler-env test-cenv + (let [parsed (env/with-compiler-env test-cenv (analyze (assoc test-env :def-emits-var true) '(def x 1)))] (is (some? (:var-ast parsed)))) - (let [parsed (e/with-compiler-env test-cenv + (let [parsed (env/with-compiler-env test-cenv (analyze (assoc test-env :def-emits-var true) '(let [y 1] (def y 2))))] (is (some? (-> parsed :body :ret :var-ast))))) @@ -882,15 +881,15 @@ (def analyze-ops-cenv (atom @test-cenv)) (defn ana' [form] - (e/with-compiler-env analyze-ops-cenv + (env/with-compiler-env analyze-ops-cenv (analyze test-env form))) (defmacro ana [form] `(ana' '~form)) (defn prs-ana [fstr] - (e/with-compiler-env analyze-ops-cenv - (let [[form] (a/forms-seq* + (env/with-compiler-env analyze-ops-cenv + (let [[form] (ana/forms-seq* (java.io.StringReader. fstr))] (ana' form)))) @@ -969,12 +968,12 @@ (comp :name :target :target)))))) ;local shadow (is (= 'alert - (a/no-warn (-> (ana (let [alert 1] js/alert)) :body + (ana/no-warn (-> (ana (let [alert 1] js/alert)) :body :env :locals (get 'alert) :name)))) (is (= [:local 'alert] - (a/no-warn (-> (ana (let [alert 1] js/alert)) :body :ret + (ana/no-warn (-> (ana (let [alert 1] js/alert)) :body :ret ((juxt :op :name)))))) ;loop (is (= (-> (ana (loop [])) :op) :loop)) @@ -1366,10 +1365,10 @@ (is ((every-pred vector? empty?) (-> (ana (.call 'a)) :args))) (is (= [:const 1] (-> (ana (.call 'a 1)) :args first juxt-op-val))) ;ns - (is (binding [a/*cljs-ns* 'cljs.user] + (is (binding [ana/*cljs-ns* 'cljs.user] (= :ns (-> (ana (ns fazz.foo)) :op)))) ;ns* - (is (binding [a/*cljs-ns* 'cljs.user] + (is (binding [ana/*cljs-ns* 'cljs.user] (= :ns* (-> (ana (refer-clojure :exclude '[locking])) :op)))) ;quote (is (= :quote (-> (ana (quote a)) :op))) @@ -1420,8 +1419,8 @@ :name))) ;ns (is - (binding [a/*analyze-deps* false] - (binding [a/*cljs-ns* 'cljs.user] + (binding [ana/*analyze-deps* false] + (binding [ana/*cljs-ns* 'cljs.user] (ana (ns my.ns.foo (:require [clojure.repl] @@ -1483,22 +1482,22 @@ (closure/load-externs {:externs ["src/test/externs/test.js"] :use-only-custom-externs true}))] - (is (true? (a/has-extern? '[Foo] externs))) - (is (true? (a/has-extern? '[Foo wozMethod] externs))) - (is (false? (a/has-extern? '[foo] externs))) - (is (false? (a/has-extern? '[Foo gozMethod] externs))) - (is (true? (a/has-extern? '[baz] externs))) - (is (false? (a/has-extern? '[Baz] externs))))) + (is (true? (ana/has-extern? '[Foo] externs))) + (is (true? (ana/has-extern? '[Foo wozMethod] externs))) + (is (false? (ana/has-extern? '[foo] externs))) + (is (false? (ana/has-extern? '[Foo gozMethod] externs))) + (is (true? (ana/has-extern? '[baz] externs))) + (is (false? (ana/has-extern? '[Baz] externs))))) (deftest test-has-extern?-defaults (let [externs (externs/externs-map)] - (is (true? (a/has-extern? '[console] externs))) - (is (true? (a/has-extern? '[console log] externs))) - (is (true? (a/has-extern? '[Number isNaN] externs))))) + (is (true? (ana/has-extern? '[console] externs))) + (is (true? (ana/has-extern? '[console log] externs))) + (is (true? (ana/has-extern? '[Number isNaN] externs))))) (def externs-cenv (atom - {::a/externs + {::ana/externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))})) @@ -1507,41 +1506,41 @@ (let [externs (externs/externs-map (closure/load-externs {:externs ["src/test/externs/test.js"]}))] - (is (= 'js/Console (a/js-tag '[console] :tag externs))) - (is (= 'js/Function (a/js-tag '[console log] :tag externs))) - (is (= 'js/Boolean (a/js-tag '[Number isNaN] :ret-tag externs))) - (is (= 'js/Foo (a/js-tag '[baz] :ret-tag externs))))) + (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/Foo (ana/js-tag '[baz] :ret-tag externs))))) (deftest test-externs-infer (is (= 'js/Foo - (-> (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env externs-cenv - (analyze (a/empty-env) 'js/baz))) + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) 'js/baz))) :info :ret-tag))) (is (= 'js/Foo - (-> (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env externs-cenv - (analyze (a/empty-env) '(js/baz)))) + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(js/baz)))) :tag))) (is (= 'js - (-> (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env externs-cenv - (analyze (a/empty-env) '(js/woz)))) + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(js/woz)))) :tag))) (is (= 'js - (-> (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env externs-cenv - (analyze (a/empty-env) '(def foo (js/woz))))) + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(def foo (js/woz))))) :tag))) (is (= 'js - (-> (binding [a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env externs-cenv - (analyze (a/empty-env) '(def foo js/boz)))) + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(def foo js/boz)))) :tag))) - (is (nil? (-> (binding [a/*cljs-ns* a/*cljs-ns*] - (a/no-warn - (e/with-compiler-env externs-cenv - (analyze (a/empty-env) + (is (nil? (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (ana/no-warn + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(let [z (.baz ^js/Foo.Bar x)] z))))) :tag meta :prefix)))) @@ -1549,7 +1548,7 @@ (deftest test-cljs-1871 (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] + (ana/with-warning-handlers [(collecting-warning-handler ws)] (analyze (ana/empty-env) '(do (declare ^{:arglists '([x y])} foo) (defn foo [x])))) @@ -1558,24 +1557,24 @@ (deftest test-cljs-2023 (let [form (with-meta 'js/goog.DEBUG {:tag 'boolean})] - (is (= (-> (ana-api/analyze (a/empty-env) form) :tag) 'boolean)))) + (is (= (-> (ana-api/analyze (ana/empty-env) form) :tag) 'boolean)))) (deftest test-cljs-1992 ;; declare after def should have no effect - (let [test-cenv (e/default-compiler-env)] - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (let [test-cenv (env/default-compiler-env)] + (env/with-compiler-env test-cenv + (ana/analyze-form-seq '[(ns test.cljs-1992) (defn test-fn [a b c] :foo) (declare test-fn)] )) - (let [def (get-in @test-cenv [::a/namespaces 'test.cljs-1992 :defs 'test-fn])] + (let [def (get-in @test-cenv [::ana/namespaces 'test.cljs-1992 :defs 'test-fn])] (is (:fn-var def))))) (deftest test-cljs-2101 - (let [test-cenv (e/default-compiler-env)] - (e/with-compiler-env test-cenv - (a/analyze-form-seq + (let [test-cenv (env/default-compiler-env)] + (env/with-compiler-env test-cenv + (ana/analyze-form-seq ['(ns test.cljs-2101) `(do ;; Splice in 32 forms in order to consume first chunk in chunked sequence @@ -1587,8 +1586,8 @@ (deftest test-cljs-2139 (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] - (analyze (a/empty-env) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (analyze (ana/empty-env) '(defn foo [] x))) (catch Exception _)) (is (= ["Use of undeclared Var cljs.user/x"] @ws)))) @@ -1597,50 +1596,50 @@ (binding [ana/*checked-arrays* :warn] (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] - (e/with-compiler-env test-cenv - (analyze (a/empty-env) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env test-cenv + (analyze (ana/empty-env) '(aget (js-obj) "a")))) (catch Exception _)) (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string] instead (consider goog.object/get for object access)"] @ws))) (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] - (e/with-compiler-env test-cenv - (analyze (a/empty-env) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env test-cenv + (analyze (ana/empty-env) '(aget (js-obj) "foo" "bar")))) (catch Exception _)) (is (= ["cljs.core/aget, arguments must be an array followed by numeric indices, got [object string string] instead (consider goog.object/getValueByKeys for object access)"] @ws))) (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] - (e/with-compiler-env test-cenv - (analyze (a/empty-env) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env test-cenv + (analyze (ana/empty-env) '(aset (js-obj) "a" 2)))) (catch Exception _)) (is (= ["cljs.core/aset, arguments must be an array, followed by numeric indices, followed by a value, got [object string number] instead (consider goog.object/set for object access)"] @ws))) (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] - (e/with-compiler-env test-cenv - (analyze (a/empty-env) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env test-cenv + (analyze (ana/empty-env) '(let [^objects arr (into-array [1 2 3])] (aget arr 0))))) (catch Exception _)) (is (empty? @ws))) (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] - (e/with-compiler-env test-cenv - (analyze (a/empty-env) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env test-cenv + (analyze (ana/empty-env) '(and true (or (aget (js-obj "foo" 1) "foo") 2))))) (catch Exception _)) (is (= 1 (count @ws)))))) (deftest test-cljs-2037 - (let [test-env (assoc-in (a/empty-env) [:ns :name] 'cljs.user)] - (binding [a/*cljs-ns* a/*cljs-ns* - a/*analyze-deps* false] + (let [test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)] + (binding [ana/*cljs-ns* ana/*cljs-ns* + ana/*analyze-deps* false] (is (thrown-with-cause-msg? Exception #"Alias str already exists in namespace cljs.user, aliasing clojure.string" (analyze test-env '(do (require '[clojure.string :as str]) @@ -1658,14 +1657,14 @@ (let [cenv (atom @test-cenv)] (is (thrown-with-cause-msg? Exception #"Argument to resolve must be a quoted symbol" - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (analyze test-env '(resolve foo.core))))))) (deftest test-cljs-2387 - (a/no-warn - (e/with-compiler-env test-cenv - (a/analyze-file (io/file "src/test/cljs_build/analyzer_test/no_defs.cljs")))) - (is (= {} (get-in @test-cenv [::a/namespaces 'analyzer-test.no-defs :defs])))) + (ana/no-warn + (env/with-compiler-env test-cenv + (ana/analyze-file (io/file "src/test/cljs_build/analyzer_test/no_defs.cljs")))) + (is (= {} (get-in @test-cenv [::ana/namespaces 'analyzer-test.no-defs :defs])))) (deftest test-cljs-2475 (is (thrown-with-cause-msg? Exception #"recur argument count mismatch, expected: 2 args, got: 1" @@ -1681,23 +1680,23 @@ (analyze test-env invalid-try-recur-form))))) (comment - (binding [a/*cljs-ns* a/*cljs-ns*] - (a/no-warn - (e/with-compiler-env externs-cenv - (analyze (a/empty-env) + (binding [ana/*cljs-ns* ana/*cljs-ns*] + (ana/no-warn + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(let [React (js/require "react")] React))))) ;; FIXME: we don't preserve tag information - (binding [a/*cljs-ns* a/*cljs-ns*] - (a/no-warn - (e/with-compiler-env externs-cenv - (let [aenv (a/empty-env) + (binding [ana/*cljs-ns* ana/*cljs-ns*] + (ana/no-warn + (env/with-compiler-env externs-cenv + (let [aenv (ana/empty-env) _ (analyze aenv '(ns foo.core)) aenv' (assoc-in aenv [:ns :name] 'foo.core) - _ (a/analyze aenv' '(def x 1))] - (dissoc (a/analyze-symbol (assoc-in aenv [:ns :name] 'foo.core) 'x) :env) - ;(get-in @externs-cenv [::a/namespaces 'foo.core]) + _ (ana/analyze aenv' '(def x 1))] + (dissoc (ana/analyze-symbol (assoc-in aenv [:ns :name] 'foo.core) 'x) :env) + ;(get-in @externs-cenv [::ana/namespaces 'foo.core]) )))) ) @@ -1713,29 +1712,29 @@ (if with-core? (env/default-compiler-env* (closure/add-externs-sources (merge {:infer-externs true} opts))) - {::a/externs + {::ana/externs (externs/externs-map (closure/load-externs {:externs (or externs [])}))}) js-dependency-index (assoc :js-dependency-index js-dependency-index))) wrap (if with-core? #(comp/with-core-cljs nil %) #(do (%)))] - (a/with-warning-handlers [(collecting-warning-handler (or warnings (atom [])))] - (binding [a/*analyze-deps* false - a/*cljs-ns* a/*cljs-ns*] - (e/with-compiler-env test-cenv + (ana/with-warning-handlers [(collecting-warning-handler (or warnings (atom [])))] + (binding [ana/*analyze-deps* false + ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env test-cenv (wrap (fn [] - (binding [a/*analyze-deps* true - a/*cljs-warnings* - (assoc a/*cljs-warnings* + (binding [ana/*analyze-deps* true + ana/*cljs-warnings* + (assoc ana/*cljs-warnings* :infer-warning (if (nil? warn) true warn))] - (a/analyze-form-seq forms)) + (ana/analyze-form-seq forms)) (with-out-str (comp/emit-externs (reduce util/map-merge {} (map (comp :externs second) - (get @test-cenv ::a/namespaces)))))))))))) + (get @test-cenv ::ana/namespaces)))))))))))) (deftest test-basic-infer (let [res (infer-test-helper @@ -1878,11 +1877,11 @@ (deftest test-cljs-2247 (let [ws (atom [])] (try - (a/with-warning-handlers [(collecting-warning-handler ws)] - (e/with-compiler-env (assoc @test-cenv :repl-env {}) - (a/analyze (ana/empty-env) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env (assoc @test-cenv :repl-env {}) + (ana/analyze (ana/empty-env) '(defn -foo [])) - (a/analyze (ana/empty-env) + (ana/analyze (ana/empty-env) '(defprotocol IAlpha (-foo [this]))))) (catch Exception _)) (is (= ["Protocol IAlpha is overwriting function -foo"] @ws)))) @@ -1960,20 +1959,20 @@ ((juxt :op :name))))))) (deftest test-cljs-2814 - (is (= "global$module$react" (a/munge-global-export 'react))) - (is (= "global$module$_CIRCA_material_ui$core$styles" (a/munge-global-export "@material-ui/core/styles"))) + (is (= "global$module$react" (ana/munge-global-export 'react))) + (is (= "global$module$_CIRCA_material_ui$core$styles" (ana/munge-global-export "@material-ui/core/styles"))) (is (= "node$module$_CIRCA_material_ui$core$styles" (ana/munge-node-lib "@material-ui/core/styles")))) (deftest test-cljs-2819 (let [ws (atom [])] - (a/with-warning-handlers [(collecting-warning-handler ws)] + (ana/with-warning-handlers [(collecting-warning-handler ws)] (analyze ns-env '(def *foo* 1))) (is (string/starts-with? (first @ws) "*foo* not declared dynamic and thus")))) (deftest test-cljs-3031 (let [ws (atom [])] - (a/with-warning-handlers [(collecting-warning-handler ws)] + (ana/with-warning-handlers [(collecting-warning-handler ws)] (analyze ns-env '(loop [x "a"] (if (identical? "a" x) @@ -1982,7 +1981,7 @@ (is (= 1 (count @ws))) (is (string/starts-with? (first @ws) "cljs.core/+, all arguments must be numbers, got [number #{boolean string}] instead"))) (let [ws (atom [])] - (a/with-warning-handlers [(collecting-warning-handler ws)] + (ana/with-warning-handlers [(collecting-warning-handler ws)] (analyze ns-env '(loop [x "a"] (if (identical? "a" x) @@ -1992,34 +1991,34 @@ (deftest test-cljs-2868 (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(subs "duck" 1 1)))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(subs "duck" 1)))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(str)))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(str 1)))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(str 1 2)))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(pr-str 0)))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(prn-str 0)))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(print-str 0)))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(munge-str "")))))) (is (= 'string - (e/with-compiler-env test-cenv + (env/with-compiler-env test-cenv (:tag (analyze test-env '(demunge-str ""))))))) \ No newline at end of file From 8ce0d0d7200d811b319a1dd6bc55b0fecba13795 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 26 Jun 2019 10:01:52 -0400 Subject: [PATCH 3414/4033] split up testing aliases, require clojure.test in test_util.clj --- deps.edn | 10 +++++----- src/test/clojure/cljs/test_util.clj | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/deps.edn b/deps.edn index 05b490db5..07a4e879a 100644 --- a/deps.edn +++ b/deps.edn @@ -11,8 +11,8 @@ org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"} org.mozilla/rhino {:mvn/version "1.7R5"}} :aliases - {:test-clj {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" - "src/test/clojure" "src/test/self"] - :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" - :sha "78d380d00e7a27f7b835bb90af37e73b20c49bcc"}} - :main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure" "-r" ".*-tests$"]}}} + {:test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" + "src/test/clojure" "src/test/self"] + :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" + :sha "209b64504cb3bd3b99ecfec7937b358a879f55c1"}}} + :run-tests {:main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure" "-r" ".*-tests$"]}}} diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index c1afc4366..d1f306375 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -8,7 +8,8 @@ (ns cljs.test-util (:require [clojure.java.io :as io] - [clojure.string :as string]) + [clojure.string :as string] + [clojure.test]) (:import [java.io File])) (defn delete-out-files From 13fde33d737b0d47bdaaf102ed8e1c49342ade3c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Jun 2019 15:48:04 -0400 Subject: [PATCH 3415/4033] CLJS-3120: Add :sigs to protocol var for compatibility with Clojure Renamed cljs.core/warn-and-update-protocol to something more sensible - update-protocol-var. Add :sigs to protocol var which matches Clojure data shape. Add test case to cljs.analyzer-tests. Add some helpers - cached core analysis, and analyze-forms helper to drop some boilerplate. --- src/main/clojure/cljs/core.cljc | 19 +++++++++---- src/test/clojure/cljs/analyzer_tests.clj | 34 +++++++++++++++++++++++- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 64aaa47ca..2beaca2bf 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1409,7 +1409,7 @@ (core/defn- to-property [sym] (symbol (core/str "-" sym))) -(core/defn- warn-and-update-protocol [p type env] +(core/defn- update-protocol-var [p type env] (core/when-not (= 'Object p) (core/if-let [var (cljs.analyzer/resolve-existing-var (dissoc env :locals) p)] (do @@ -1440,7 +1440,7 @@ ret))) (core/defn- base-assign-impls [env resolve tsym type [p sigs]] - (warn-and-update-protocol p tsym env) + (update-protocol-var p tsym env) (core/let [psym (resolve p) pfn-prefix (subs (core/str psym) 0 (clojure.core/inc (.indexOf (core/str psym) "/")))] @@ -1526,7 +1526,7 @@ meths)))) (core/defn- proto-assign-impls [env resolve type-sym type [p sigs]] - (warn-and-update-protocol p type env) + (update-protocol-var p type env) (core/let [psym (resolve p) pprefix (protocol-prefix psym) skip-flag (set (core/-> type-sym meta :skip-protocol-flag))] @@ -2101,8 +2101,7 @@ ~check)] `(~sig ~check))) psym (core/-> psym - (vary-meta update-in [:jsdoc] conj - "@interface") + (vary-meta update-in [:jsdoc] conj "@interface") (vary-meta assoc-in [:protocol-info :methods] (into {} (map @@ -2112,6 +2111,16 @@ sigs (take-while vector? sigs)] [(vary-meta fname assoc :doc doc) (vec sigs)])) + methods))) + ;; for compatibility with Clojure + (vary-meta assoc-in [:sigs] + (into {} + (map + (core/fn [[fname & sigs]] + (core/let [doc (core/as-> (last sigs) doc + (core/when (core/string? doc) doc)) + sigs (take-while vector? sigs)] + [(keyword fname) {:name fname :arglists (list* sigs) :doc doc}])) methods)))) method (core/fn [[fname & sigs]] (core/let [doc (core/as-> (last sigs) doc diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 52e48bb76..197a2c7f5 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -186,6 +186,22 @@ (def test-cenv (atom {})) (def test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.core)) +(def test-core-env (atom {})) + +(binding [ana/*unchecked-if* false + ana/*analyze-deps* false] + (env/with-compiler-env test-core-env + (comp/with-core-cljs nil + (fn [])))) + +(defn core-env [] + (atom @test-core-env)) + +(defn analyze-forms [cenv xs] + (binding [ana/*unchecked-if* false + ana/*analyze-deps* false] + (env/with-compiler-env cenv + (ana/analyze-form-seq xs)))) (ana/no-warn (env/with-compiler-env test-cenv @@ -2021,4 +2037,20 @@ (:tag (analyze test-env '(munge-str "")))))) (is (= 'string (env/with-compiler-env test-cenv - (:tag (analyze test-env '(demunge-str ""))))))) \ No newline at end of file + (:tag (analyze test-env '(demunge-str ""))))))) + +(deftest test-cljs-3120 + (let [cenv (core-env) + _ (analyze-forms cenv + '[(ns goz.core) + (defprotocol IAlpha + (foo [this] "foo fn") + (bar [this x] "bar fn") + (woz [this x y] "baz fn"))]) + sigs (get-in @cenv [::ana/namespaces 'goz.core :defs 'IAlpha :sigs])] + (is (= #{:foo :bar :woz} (set (keys sigs)))) + (is (every? #(set/subset? #{:name :doc :arglists} (set (keys %))) (vals sigs))) + (is #(= '#{foo bar woz} (set (map :name (vals sigs))))) + (is #(= '#{([this] [this x] [this x y])} (set (map :arglists (vals sigs))))) + (is #(= '#{"foo fn" "bar fn" "baz fn"} (set (map :doc (vals sigs))))))) + From 7972430b13c4296a651a5517e2046d27573c463a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jun 2019 15:33:09 -0400 Subject: [PATCH 3416/4033] include original JS docstring when parsing externs --- src/main/clojure/cljs/externs.clj | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index e672b1b92..11e84c936 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -14,6 +14,7 @@ (:import [java.util.logging Level] [com.google.javascript.jscomp CompilerOptions SourceFile JsAst CommandLineRunner] + [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino Node Token JSTypeExpression])) @@ -38,16 +39,19 @@ (when node (let [info (.getJSDocInfo node)] (when info - (if-let [^JSTypeExpression ty (.getType info)] - {:tag (get-type* ty)} - (if (or (.isConstructor info) (.isInterface info)) - (let [qname (symbol (.. node getFirstChild getQualifiedName))] - (cond-> {:tag 'Function} - (.isConstructor info) (merge {:ctor qname}) - (.isInterface info) (merge {:iface qname}))) - (if (.hasReturnType info) - {:tag 'Function - :ret-tag (get-type* (.getReturnType info))}))))))) + (merge + (if-let [^JSTypeExpression ty (.getType info)] + {:tag (get-type* ty)} + (if (or (.isConstructor info) (.isInterface info)) + (let [qname (symbol (.. node getFirstChild getQualifiedName))] + (cond-> {:tag 'Function} + (.isConstructor info) (merge {:ctor qname}) + (.isInterface info) (merge {:iface qname}))) + (if (.hasReturnType info) + {:tag 'Function + :ret-tag (get-type* (.getReturnType info))}))) + (when-let [doc (.getOriginalCommentString info)] + {:doc doc})))))) (defmulti parse-extern-node (fn [^Node node] @@ -108,7 +112,10 @@ (defmethod parse-extern-node :default [node]) (defn parse-externs [^SourceFile source-file] - (let [^CompilerOptions compiler-options (CompilerOptions.) + (let [^CompilerOptions compiler-options + (doto (CompilerOptions.) + (.setParseJsDocDocumentation + Config$JsDocParsing/INCLUDE_DESCRIPTIONS_WITH_WHITESPACE)) closure-compiler (doto (let [compiler (com.google.javascript.jscomp.Compiler.)] From 5c001135a5662b2e32e5f74f555bc26ac855f03d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jun 2019 15:39:47 -0400 Subject: [PATCH 3417/4033] compute :arglists when parsing externs --- src/main/clojure/cljs/externs.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 11e84c936..267de88ff 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -49,7 +49,8 @@ (.isInterface info) (merge {:iface qname}))) (if (.hasReturnType info) {:tag 'Function - :ret-tag (get-type* (.getReturnType info))}))) + :ret-tag (get-type* (.getReturnType info)) + :arglists (list (into [] (map symbol (.getParameterNames info))))}))) (when-let [doc (.getOriginalCommentString info)] {:doc doc})))))) From 4e4ac4aa8ca0b1fea5e028fadb32227c118156e6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jun 2019 15:48:25 -0400 Subject: [PATCH 3418/4033] add test ns for externs parsing --- src/test/clojure/cljs/externs_parsing_test.clj | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/test/clojure/cljs/externs_parsing_test.clj diff --git a/src/test/clojure/cljs/externs_parsing_test.clj b/src/test/clojure/cljs/externs_parsing_test.clj new file mode 100644 index 000000000..c4d02d5e4 --- /dev/null +++ b/src/test/clojure/cljs/externs_parsing_test.clj @@ -0,0 +1,3 @@ +(ns cljs.externs-parsing-test + (:require [cljs.externs :as externs] + [clojure.test :as test :refer [deftest is]])) From 26d338446b46c02ca167061a1168d606f539c0ed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jun 2019 16:10:00 -0400 Subject: [PATCH 3419/4033] add line number to parsed JS node --- src/main/clojure/cljs/externs.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 267de88ff..f47f5a825 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -51,6 +51,7 @@ {:tag 'Function :ret-tag (get-type* (.getReturnType info)) :arglists (list (into [] (map symbol (.getParameterNames info))))}))) + {:line (.getLineno node)} (when-let [doc (.getOriginalCommentString info)] {:doc doc})))))) @@ -179,6 +180,10 @@ 2) last meta) + (parse-externs + (closure/js-source-file "goog/string/string.js" + (io/input-stream (io/resource "goog/string/string.js")))) + (externs-map [(closure/js-source-file "goog/string/string.js" (io/input-stream (io/resource "goog/string/string.js")))] From 6057da87e30295c9336a19670f19f0c1e9f25e3b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jun 2019 16:22:06 -0400 Subject: [PATCH 3420/4033] add :file --- src/main/clojure/cljs/externs.clj | 44 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index f47f5a825..4c0598bfe 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -18,6 +18,8 @@ [com.google.javascript.rhino Node Token JSTypeExpression])) +(def ^:dynamic *source-file* nil) + ;; ------------------------------------------------------------------------------ ;; Externs Parsing @@ -51,7 +53,8 @@ {:tag 'Function :ret-tag (get-type* (.getReturnType info)) :arglists (list (into [] (map symbol (.getParameterNames info))))}))) - {:line (.getLineno node)} + {:file *source-file* + :line (.getLineno node)} (when-let [doc (.getOriginalCommentString info)] {:doc doc})))))) @@ -114,25 +117,26 @@ (defmethod parse-extern-node :default [node]) (defn parse-externs [^SourceFile source-file] - (let [^CompilerOptions compiler-options - (doto (CompilerOptions.) - (.setParseJsDocDocumentation - Config$JsDocParsing/INCLUDE_DESCRIPTIONS_WITH_WHITESPACE)) - closure-compiler - (doto - (let [compiler (com.google.javascript.jscomp.Compiler.)] - (com.google.javascript.jscomp.Compiler/setLoggingLevel Level/WARNING) - compiler) - (.init (list source-file) '() compiler-options)) - js-ast (JsAst. source-file) - ^Node root (.getAstRoot js-ast closure-compiler)] - (loop [nodes (.children root) - externs []] - (if (empty? nodes) - externs - (let [node (first nodes) - new-extern (parse-extern-node node)] - (recur (rest nodes) (concat externs new-extern))))))) + (binding [*source-file* (.getName source-file)] + (let [^CompilerOptions compiler-options + (doto (CompilerOptions.) + (.setParseJsDocDocumentation + Config$JsDocParsing/INCLUDE_DESCRIPTIONS_WITH_WHITESPACE)) + closure-compiler + (doto + (let [compiler (com.google.javascript.jscomp.Compiler.)] + (com.google.javascript.jscomp.Compiler/setLoggingLevel Level/WARNING) + compiler) + (.init (list source-file) '() compiler-options)) + js-ast (JsAst. source-file) + ^Node root (.getAstRoot js-ast closure-compiler)] + (loop [nodes (.children root) + externs []] + (if (empty? nodes) + externs + (let [node (first nodes) + new-extern (parse-extern-node node)] + (recur (rest nodes) (concat externs new-extern)))))))) (defn index-externs [externs] (reduce From c247649fa8b390fd0b72b6111d7f42229f9215c6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Jun 2019 16:52:18 -0400 Subject: [PATCH 3421/4033] CLJS-3121: Bad externs parsing for aliased Fns Add dynamic var *ignore-var*. Skip parsing GETPROP case if *ignore-var* true. Add simple test --- src/main/clojure/cljs/externs.clj | 14 +++++++++----- src/test/clojure/cljs/externs_parsing_test.clj | 14 +++++++++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 4c0598bfe..ce2ea9a91 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -18,6 +18,7 @@ [com.google.javascript.rhino Node Token JSTypeExpression])) +(def ^:dynamic *ignore-var* false) (def ^:dynamic *source-file* nil) ;; ------------------------------------------------------------------------------ @@ -78,7 +79,9 @@ lhs (cond-> (first (parse-extern-node (.getFirstChild node))) ty (annotate ty))] (if (> (.getChildCount node) 1) - (let [externs (parse-extern-node (.getChildAtIndex node 1))] + (let [externs + (binding [*ignore-var* true] + (parse-extern-node (.getChildAtIndex node 1)))] (conj (map (fn [ext] (concat lhs ext)) externs) lhs)) [lhs])))) @@ -92,10 +95,11 @@ [lhs]))) (defmethod parse-extern-node Token/GETPROP [node] - (let [props (map symbol (string/split (.getQualifiedName node) #"\."))] - [(if-let [ty (get-type node)] - (annotate props ty) - props)])) + (when-not *ignore-var* + (let [props (map symbol (string/split (.getQualifiedName node) #"\."))] + [(if-let [ty (get-type node)] + (annotate props ty) + props)]))) (defmethod parse-extern-node Token/OBJECTLIT [node] (when (> (.getChildCount node) 0) diff --git a/src/test/clojure/cljs/externs_parsing_test.clj b/src/test/clojure/cljs/externs_parsing_test.clj index c4d02d5e4..0109fe782 100644 --- a/src/test/clojure/cljs/externs_parsing_test.clj +++ b/src/test/clojure/cljs/externs_parsing_test.clj @@ -1,3 +1,15 @@ (ns cljs.externs-parsing-test - (:require [cljs.externs :as externs] + (:require [cljs.closure :as closure] + [cljs.externs :as externs] + [clojure.java.io :as io] [clojure.test :as test :refer [deftest is]])) + +(deftest cljs-3121 + (let [externs (externs/parse-externs + (closure/js-source-file "goog/string/string.js" + (io/input-stream (io/resource "goog/string/string.js"))))] + (is (every? + (fn [xs] + (= (count (distinct xs)) + (count xs))) + externs)))) From 2931216430c8acff6a683a0c3b1bb341bc5ef886 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 4 Jul 2019 10:43:18 -0400 Subject: [PATCH 3422/4033] CLJS-3125: set! emits invalid js when used as expression --- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/cljs/cljs/specials_test.cljs | 25 +++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 ++ src/test/self/self_parity/test.cljs | 2 ++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs/cljs/specials_test.cljs diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 4d68b81e5..404e8a4c1 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1237,7 +1237,7 @@ (defmethod emit* :set! [{:keys [target val env]}] - (emit-wrap env (emits target " = " val))) + (emit-wrap env (emits "(" target " = " val ")"))) (defn emit-global-export [ns-name global-exports lib] (emitln (munge ns-name) "." diff --git a/src/test/cljs/cljs/specials_test.cljs b/src/test/cljs/cljs/specials_test.cljs new file mode 100644 index 000000000..670667e9a --- /dev/null +++ b/src/test/cljs/cljs/specials_test.cljs @@ -0,0 +1,25 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns cljs.specials-test + (:require [cljs.test :refer-macros [deftest is]])) + +(defprotocol IFoo3125 + (-mutate [this])) + +(defrecord Foo3125 [^:mutable x] + IFoo3125 + (-mutate [this] (* 3 (set! x (inc x))))) + +(def ^:dynamic *test-cljs-3125* 4) + +(deftest test-cljs-3125 + (is (== 12 (let [o #js {}] (* 6 (set! (.-a o) 2))))) + (is (== 12 (let [o #js {}] (* 6 (set! o -a 2))))) + (is (== 15 (* 3 (set! *test-cljs-3125* (inc *test-cljs-3125*))))) + (is (== 18 (-mutate (->Foo3125 5))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 7e1ddd7c5..7a6023c85 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -37,6 +37,7 @@ [cljs.pprint] [cljs.pprint-test] [cljs.spec-test] + [cljs.specials-test] [cljs.spec.test-test] [cljs.clojure-alias-test] [cljs.hash-map-test] @@ -85,6 +86,7 @@ 'cljs.import-test 'cljs.pprint 'cljs.spec-test + 'cljs.specials-test 'cljs.spec.test-test 'cljs.clojure-alias-test 'cljs.hash-map-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 63119dc56..4ed5928c1 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -303,6 +303,7 @@ [cljs.pprint] [cljs.pprint-test] [cljs.spec-test] + [cljs.specials-test] [cljs.spec.test-test] [cljs.clojure-alias-test] [cljs.hash-map-test] @@ -351,6 +352,7 @@ 'cljs.pprint 'cljs.pprint-test 'cljs.spec-test + 'cljs.specials-test 'cljs.spec.test-test 'cljs.clojure-alias-test 'cljs.hash-map-test From 11de795dc748e004d31474f11cf04e5fc58b5d24 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 5 Jul 2019 16:01:23 -0400 Subject: [PATCH 3423/4033] cljs.externs: organize requires, better names - get-type* -> get-tag, get-type -> get-var-info --- src/main/clojure/cljs/externs.clj | 36 ++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index ce2ea9a91..5d5579d43 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -7,16 +7,16 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.externs - (:require [clojure.string :as string] - [cljs.util :as util] + (:require [cljs.util :as util] + [cljs.js-deps :as js-deps] [clojure.java.io :as io] - [cljs.js-deps :as js-deps]) - (:import [java.util.logging Level] - [com.google.javascript.jscomp + [clojure.string :as string]) + (:import [com.google.javascript.jscomp CompilerOptions SourceFile JsAst CommandLineRunner] [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino - Node Token JSTypeExpression])) + Node Token JSTypeExpression] + [java.util.logging Level])) (def ^:dynamic *ignore-var* false) (def ^:dynamic *source-file* nil) @@ -30,7 +30,7 @@ (into [] (butlast props)) (with-meta (last props) ty)))) -(defn get-type* [^JSTypeExpression texpr] +(defn get-tag [^JSTypeExpression texpr] (when-let [root (.getRoot texpr)] (if (.isString root) (symbol (.getString root)) @@ -38,13 +38,13 @@ (if (.isString child) (symbol (.. child getString))))))) -(defn get-type [^Node node] +(defn get-var-info [^Node node] (when node (let [info (.getJSDocInfo node)] (when info (merge (if-let [^JSTypeExpression ty (.getType info)] - {:tag (get-type* ty)} + {:tag (get-tag ty)} (if (or (.isConstructor info) (.isInterface info)) (let [qname (symbol (.. node getFirstChild getQualifiedName))] (cond-> {:tag 'Function} @@ -52,7 +52,7 @@ (.isInterface info) (merge {:iface qname}))) (if (.hasReturnType info) {:tag 'Function - :ret-tag (get-type* (.getReturnType info)) + :ret-tag (get-tag (.getReturnType info)) :arglists (list (into [] (map symbol (.getParameterNames info))))}))) {:file *source-file* :line (.getLineno node)} @@ -65,7 +65,7 @@ (defmethod parse-extern-node Token/VAR [node] (when (> (.getChildCount node) 0) - (let [ty (get-type node)] + (let [ty (get-var-info node)] (cond-> (parse-extern-node (.getFirstChild node)) ty (-> first (annotate ty) vector))))) @@ -75,7 +75,7 @@ (defmethod parse-extern-node Token/ASSIGN [node] (when (> (.getChildCount node) 0) - (let [ty (get-type node) + (let [ty (get-var-info node) lhs (cond-> (first (parse-extern-node (.getFirstChild node))) ty (annotate ty))] (if (> (.getChildCount node) 1) @@ -97,7 +97,7 @@ (defmethod parse-extern-node Token/GETPROP [node] (when-not *ignore-var* (let [props (map symbol (string/split (.getQualifiedName node) #"\."))] - [(if-let [ty (get-type node)] + [(if-let [ty (get-var-info node)] (annotate props ty) props)]))) @@ -171,7 +171,17 @@ externs (index-externs (parse-externs externs-file)))) defaults sources)))) +(defn analyze-goog-file [f] + (let [rsrc (io/resource f) + desc (js-deps/parse-js-ns (line-seq (io/reader rsrc)))] + ;; TODO: figure out what to do about other provides + [(first (:provides desc)) + ])) + (comment + + (analyze-goog-file "goog/string/string.js") + (require '[clojure.java.io :as io] '[cljs.closure :as closure] '[clojure.pprint :refer [pprint]] From 76a03f5408e601a4324aa170f1e4818c2c442493 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 7 Jul 2019 16:11:03 -0400 Subject: [PATCH 3424/4033] CLJS-3133: simple-* / qualified-* predicate-induced inference --- src/main/clojure/cljs/analyzer.cljc | 10 +++++++ src/test/clojure/cljs/analyzer_tests.clj | 37 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c9d75506d..1dcdf37bb 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1550,6 +1550,12 @@ cljs.core/delay? cljs.core/Delay cljs.core/reduced? cljs.core/Reduced + ;; Subtypes + cljs.core/simple-keyword? cljs.core/Keyword + cljs.core/qualified-keyword? cljs.core/Keyword + cljs.core/simple-symbol? cljs.core/Symbol + cljs.core/qualified-symbol? cljs.core/Symbol + ;;; Note: For non-marker protocol entries below, we ;;; omit predicates that are based on satisfies? because ;;; we cannot safely apply the fast-path optimization @@ -1570,6 +1576,10 @@ ;; Composites cljs.core/seqable? #{cljs.core/ISeqable array string} cljs.core/ident? #{cljs.core/Keyword cljs.core/Symbol} + + ;; Composite subtypes + cljs.core/simple-ident? #{cljs.core/Keyword cljs.core/Symbol} + cljs.core/qualified-ident? #{cljs.core/Keyword cljs.core/Symbol} }) (defn- simple-predicate-induced-tag diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 197a2c7f5..8b7113606 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -2054,3 +2054,40 @@ (is #(= '#{([this] [this x] [this x y])} (set (map :arglists (vals sigs))))) (is #(= '#{"foo fn" "bar fn" "baz fn"} (set (map :doc (vals sigs))))))) +(deftest test-cljs-3133 + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (keyword? x) x nil)))))) + '#{cljs.core/Keyword clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (simple-keyword? x) x nil)))))) + '#{cljs.core/Keyword clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (qualified-keyword? x) x nil)))))) + '#{cljs.core/Keyword clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (symbol? x) x nil)))))) + '#{cljs.core/Symbol clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (simple-symbol? x) x nil)))))) + '#{cljs.core/Symbol clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (qualified-symbol? x) x nil)))))) + '#{cljs.core/Symbol clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (ident? x) x nil)))))) + '#{cljs.core/Keyword cljs.core/Symbol clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (simple-ident? x) x nil)))))) + '#{cljs.core/Keyword cljs.core/Symbol clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (qualified-ident? x) x nil)))))) + '#{cljs.core/Keyword cljs.core/Symbol clj-nil}))) From 76be2c002f44c15ae871cdd60431e3cf211aa60e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 6 Jul 2019 21:42:58 -0400 Subject: [PATCH 3425/4033] CLJS-3128: Several namespaces need copyright header --- src/main/cljs/clojure/reflect.cljs | 8 ++++++++ src/test/cljs/baz.cljs | 8 ++++++++ src/test/cljs/cljs/array_access/alpha.cljs | 8 ++++++++ src/test/cljs/cljs/array_access/beta.cljs | 8 ++++++++ src/test/cljs/cljs/array_access/helper.clj | 8 ++++++++ src/test/cljs/cljs/eval_test.cljs | 8 ++++++++ src/test/cljs/cljs/extend_to_native_test.cljs | 8 ++++++++ src/test/cljs/cljs/invoke_test.cljs | 8 ++++++++ src/test/cljs/cljs/keyword_macros.clj | 8 ++++++++ src/test/cljs/cljs/macro_test/cljs2261.clj | 8 ++++++++ src/test/cljs/cljs/macro_test/cljs2261.cljs | 8 ++++++++ src/test/cljs/cljs/macro_test/cljs2852.clj | 8 ++++++++ src/test/cljs/cljs/npm_deps_test.cljs | 8 ++++++++ src/test/cljs/cljs/ns_test/bar.cljs | 8 ++++++++ src/test/cljs/cljs/ns_test/foo.cljs | 8 ++++++++ src/test/cljs/cljs/set_equiv_test.cljs | 8 ++++++++ src/test/cljs/cljs/spec/test/test_macros.cljc | 8 ++++++++ src/test/cljs/cljs/spec/test/test_ns1.cljs | 8 ++++++++ src/test/cljs/cljs/spec/test/test_ns2.cljs | 8 ++++++++ src/test/cljs/cljs/spec/test_test.cljs | 8 ++++++++ src/test/cljs/cljs/test_test.cljs | 8 ++++++++ src/test/cljs/clojure/edn_test.cljs | 8 ++++++++ src/test/cljs/data_readers_test/core.cljc | 8 ++++++++ src/test/cljs/data_readers_test/records.cljc | 8 ++++++++ src/test/cljs/preloads_test/core.cljs | 8 ++++++++ src/test/cljs/preloads_test/preload.cljs | 8 ++++++++ src/test/cljs/static/core_test.cljs | 8 ++++++++ src/test/clojure/cljs/externs_parsing_test.clj | 8 ++++++++ src/test/clojure/cljs/module_processing_tests.clj | 8 ++++++++ src/test/clojure/cljs/profile.clj | 8 ++++++++ 30 files changed, 240 insertions(+) diff --git a/src/main/cljs/clojure/reflect.cljs b/src/main/cljs/clojure/reflect.cljs index 9ca866107..67164f023 100644 --- a/src/main/cljs/clojure/reflect.cljs +++ b/src/main/cljs/clojure/reflect.cljs @@ -1,3 +1,11 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + (ns clojure.reflect ^{:doc "DEPRECATED. Do not use, superceded by REPL enhancements."} (:refer-clojure :exclude [meta macroexpand]) diff --git a/src/test/cljs/baz.cljs b/src/test/cljs/baz.cljs index a5c1dde59..09b1ad76f 100644 --- a/src/test/cljs/baz.cljs +++ b/src/test/cljs/baz.cljs @@ -1,3 +1,11 @@ +;; 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 baz) (defn f [x] x) diff --git a/src/test/cljs/cljs/array_access/alpha.cljs b/src/test/cljs/cljs/array_access/alpha.cljs index b3e934350..318b31bd5 100644 --- a/src/test/cljs/cljs/array_access/alpha.cljs +++ b/src/test/cljs/cljs/array_access/alpha.cljs @@ -1,3 +1,11 @@ +;; 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.array-access.alpha (:require-macros [cljs.array-access.helper :as helper]) (:require [cljs.array-access.beta])) diff --git a/src/test/cljs/cljs/array_access/beta.cljs b/src/test/cljs/cljs/array_access/beta.cljs index dab735ff4..867b3e477 100644 --- a/src/test/cljs/cljs/array_access/beta.cljs +++ b/src/test/cljs/cljs/array_access/beta.cljs @@ -1,3 +1,11 @@ +;; 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.array-access.beta (:require-macros [cljs.array-access.helper :as helper])) diff --git a/src/test/cljs/cljs/array_access/helper.clj b/src/test/cljs/cljs/array_access/helper.clj index f60bf8834..70c960047 100644 --- a/src/test/cljs/cljs/array_access/helper.clj +++ b/src/test/cljs/cljs/array_access/helper.clj @@ -1,3 +1,11 @@ +;; 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.array-access.helper (:require [cljs.analyzer :as ana])) diff --git a/src/test/cljs/cljs/eval_test.cljs b/src/test/cljs/cljs/eval_test.cljs index 3b097a630..ea93b2126 100644 --- a/src/test/cljs/cljs/eval_test.cljs +++ b/src/test/cljs/cljs/eval_test.cljs @@ -1,3 +1,11 @@ +;; 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.eval-test (:require [cljs.test :refer [deftest is]])) diff --git a/src/test/cljs/cljs/extend_to_native_test.cljs b/src/test/cljs/cljs/extend_to_native_test.cljs index 581ad83c2..378451d15 100644 --- a/src/test/cljs/cljs/extend_to_native_test.cljs +++ b/src/test/cljs/cljs/extend_to_native_test.cljs @@ -1,3 +1,11 @@ +;; 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.extend-to-native-test (:require [cljs.test :refer-macros [deftest is]])) diff --git a/src/test/cljs/cljs/invoke_test.cljs b/src/test/cljs/cljs/invoke_test.cljs index 194e959bb..9760b2241 100644 --- a/src/test/cljs/cljs/invoke_test.cljs +++ b/src/test/cljs/cljs/invoke_test.cljs @@ -1,3 +1,11 @@ +;; 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.invoke-test (:require [goog.string :as gstr])) diff --git a/src/test/cljs/cljs/keyword_macros.clj b/src/test/cljs/cljs/keyword_macros.clj index 0e3f0e58f..2047a6614 100644 --- a/src/test/cljs/cljs/keyword_macros.clj +++ b/src/test/cljs/cljs/keyword_macros.clj @@ -1,3 +1,11 @@ +;; 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.keyword-macros) (defmacro add diff --git a/src/test/cljs/cljs/macro_test/cljs2261.clj b/src/test/cljs/cljs/macro_test/cljs2261.clj index 6a13e3f46..380be3f3a 100644 --- a/src/test/cljs/cljs/macro_test/cljs2261.clj +++ b/src/test/cljs/cljs/macro_test/cljs2261.clj @@ -1,3 +1,11 @@ +;; 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.macro-test.cljs2261) (defmacro cake [] diff --git a/src/test/cljs/cljs/macro_test/cljs2261.cljs b/src/test/cljs/cljs/macro_test/cljs2261.cljs index 8d3632c78..64842aa10 100644 --- a/src/test/cljs/cljs/macro_test/cljs2261.cljs +++ b/src/test/cljs/cljs/macro_test/cljs2261.cljs @@ -1,3 +1,11 @@ +;; 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.macro-test.cljs2261) (defrecord X []) diff --git a/src/test/cljs/cljs/macro_test/cljs2852.clj b/src/test/cljs/cljs/macro_test/cljs2852.clj index 164c09f48..e8f0a0144 100644 --- a/src/test/cljs/cljs/macro_test/cljs2852.clj +++ b/src/test/cljs/cljs/macro_test/cljs2852.clj @@ -1,3 +1,11 @@ +;; 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.macro-test.cljs2852) (defmacro alpha diff --git a/src/test/cljs/cljs/npm_deps_test.cljs b/src/test/cljs/cljs/npm_deps_test.cljs index 9c19ab985..59b3c658c 100644 --- a/src/test/cljs/cljs/npm_deps_test.cljs +++ b/src/test/cljs/cljs/npm_deps_test.cljs @@ -1,3 +1,11 @@ +;; 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.npm-deps-test (:refer-clojure :exclude [array vector]) (:require [cljs.test :refer [deftest is]] diff --git a/src/test/cljs/cljs/ns_test/bar.cljs b/src/test/cljs/cljs/ns_test/bar.cljs index b70cdd1e0..5a55ea22b 100644 --- a/src/test/cljs/cljs/ns_test/bar.cljs +++ b/src/test/cljs/cljs/ns_test/bar.cljs @@ -1,3 +1,11 @@ +;; 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.ns-test.bar) (defn quux [] 123) diff --git a/src/test/cljs/cljs/ns_test/foo.cljs b/src/test/cljs/cljs/ns_test/foo.cljs index 5fa9cadc6..568d1a778 100644 --- a/src/test/cljs/cljs/ns_test/foo.cljs +++ b/src/test/cljs/cljs/ns_test/foo.cljs @@ -1,3 +1,11 @@ +;; 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.ns-test.foo (:require [cljs.test :refer-macros [deftest is]])) diff --git a/src/test/cljs/cljs/set_equiv_test.cljs b/src/test/cljs/cljs/set_equiv_test.cljs index 31b5d0573..de9ce88fc 100644 --- a/src/test/cljs/cljs/set_equiv_test.cljs +++ b/src/test/cljs/cljs/set_equiv_test.cljs @@ -1,3 +1,11 @@ +;; 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.set-equiv-test (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] diff --git a/src/test/cljs/cljs/spec/test/test_macros.cljc b/src/test/cljs/cljs/spec/test/test_macros.cljc index 4a26ace6a..ce85c37ad 100644 --- a/src/test/cljs/cljs/spec/test/test_macros.cljc +++ b/src/test/cljs/cljs/spec/test/test_macros.cljc @@ -1,3 +1,11 @@ +;; 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.spec.test.test-macros #?(:cljs (:require [cljs.spec.alpha :as s]))) diff --git a/src/test/cljs/cljs/spec/test/test_ns1.cljs b/src/test/cljs/cljs/spec/test/test_ns1.cljs index 9efe08b6a..8893d980a 100644 --- a/src/test/cljs/cljs/spec/test/test_ns1.cljs +++ b/src/test/cljs/cljs/spec/test/test_ns1.cljs @@ -1,3 +1,11 @@ +;; 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.spec.test.test-ns1) (def x 1) diff --git a/src/test/cljs/cljs/spec/test/test_ns2.cljs b/src/test/cljs/cljs/spec/test/test_ns2.cljs index e93d3cbc5..17b506ebc 100644 --- a/src/test/cljs/cljs/spec/test/test_ns2.cljs +++ b/src/test/cljs/cljs/spec/test/test_ns2.cljs @@ -1,3 +1,11 @@ +;; 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.spec.test.test-ns2) (def z 3) diff --git a/src/test/cljs/cljs/spec/test_test.cljs b/src/test/cljs/cljs/spec/test_test.cljs index bbb358e22..14b8e31b5 100644 --- a/src/test/cljs/cljs/spec/test_test.cljs +++ b/src/test/cljs/cljs/spec/test_test.cljs @@ -1,3 +1,11 @@ +;; 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.spec.test-test (:require-macros [cljs.spec.test.test-macros]) (:require [cljs.test :as test :refer-macros [deftest testing diff --git a/src/test/cljs/cljs/test_test.cljs b/src/test/cljs/cljs/test_test.cljs index f4b072bec..db53bfcb3 100644 --- a/src/test/cljs/cljs/test_test.cljs +++ b/src/test/cljs/cljs/test_test.cljs @@ -1,3 +1,11 @@ +;; 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.test-test (:require [cljs.test :refer-macros [deftest testing is] :as ct] [clojure.string :as s] diff --git a/src/test/cljs/clojure/edn_test.cljs b/src/test/cljs/clojure/edn_test.cljs index 969b9772f..66d85bff1 100644 --- a/src/test/cljs/clojure/edn_test.cljs +++ b/src/test/cljs/clojure/edn_test.cljs @@ -1,3 +1,11 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + (ns clojure.edn-test (:require [cljs.test :refer-macros [deftest is testing]] [clojure.edn :as edn] diff --git a/src/test/cljs/data_readers_test/core.cljc b/src/test/cljs/data_readers_test/core.cljc index 163850394..62ca33bca 100644 --- a/src/test/cljs/data_readers_test/core.cljc +++ b/src/test/cljs/data_readers_test/core.cljc @@ -1,3 +1,11 @@ +;; 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 data-readers-test.core) (def custom-identity identity) diff --git a/src/test/cljs/data_readers_test/records.cljc b/src/test/cljs/data_readers_test/records.cljc index 818aca0d6..e75572200 100644 --- a/src/test/cljs/data_readers_test/records.cljc +++ b/src/test/cljs/data_readers_test/records.cljc @@ -1,3 +1,11 @@ +;; 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 data-readers-test.records #?(:cljs (:require-macros [data-readers-test.records]))) diff --git a/src/test/cljs/preloads_test/core.cljs b/src/test/cljs/preloads_test/core.cljs index a775cdc65..cc50ecb11 100644 --- a/src/test/cljs/preloads_test/core.cljs +++ b/src/test/cljs/preloads_test/core.cljs @@ -1,3 +1,11 @@ +;; 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 preloads-test.core) (def foo :foo) diff --git a/src/test/cljs/preloads_test/preload.cljs b/src/test/cljs/preloads_test/preload.cljs index 9f54615b7..7983a61b4 100644 --- a/src/test/cljs/preloads_test/preload.cljs +++ b/src/test/cljs/preloads_test/preload.cljs @@ -1,3 +1,11 @@ +;; 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 preloads-test.preload) (def preload-var :foo) diff --git a/src/test/cljs/static/core_test.cljs b/src/test/cljs/static/core_test.cljs index 8b3c45b3c..6b01d887c 100644 --- a/src/test/cljs/static/core_test.cljs +++ b/src/test/cljs/static/core_test.cljs @@ -1,3 +1,11 @@ +;; 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 static.core-test (:require [cljs.test :refer-macros [deftest is]])) diff --git a/src/test/clojure/cljs/externs_parsing_test.clj b/src/test/clojure/cljs/externs_parsing_test.clj index 0109fe782..88446d116 100644 --- a/src/test/clojure/cljs/externs_parsing_test.clj +++ b/src/test/clojure/cljs/externs_parsing_test.clj @@ -1,3 +1,11 @@ +;; 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.externs-parsing-test (:require [cljs.closure :as closure] [cljs.externs :as externs] diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index 533d47b2a..a9ab89b5e 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -1,3 +1,11 @@ +;; 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.module-processing-tests (:require [clojure.java.io :as io] [cljs.closure :as closure] diff --git a/src/test/clojure/cljs/profile.clj b/src/test/clojure/cljs/profile.clj index b168c02b1..2d80d20d4 100644 --- a/src/test/clojure/cljs/profile.clj +++ b/src/test/clojure/cljs/profile.clj @@ -1,3 +1,11 @@ +;; 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.profile (:require [clojure.java.io :as io] [cljs.env :as env] From 83a1da899bc56278a75378dd422d1fa257c83356 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 7 Jul 2019 19:01:45 -0400 Subject: [PATCH 3426/4033] CLJS-3134: Thread predicate-induced inference through and --- src/main/clojure/cljs/analyzer.cljc | 12 +++++++++++- src/main/clojure/cljs/core.cljc | 10 ++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1dcdf37bb..6d1c724d4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3483,13 +3483,23 @@ (contains? t 'js) (some array-types t)))))) +(defn- analyze-js-star-args [js-op env args] + (first (reduce + (fn [[argexprs env] arg] + [(conj argexprs (analyze env arg)) + (if (= js-op 'cljs.core/and) + (set-test-induced-tags env arg) + env)]) + [[] env] + args))) + (defn analyze-js-star* [env jsform args form] (let [enve (assoc env :context :expr) - argexprs (vec (map #(analyze enve %) args)) form-meta (meta form) segs (js-star-seg jsform) tag (get-js-tag form) js-op (:js-op form-meta) + argexprs (analyze-js-star-args js-op enve args) numeric (:numeric form-meta) validate (fn [warning-type valid-types?] (let [types (map #(infer-tag env %) argexprs)] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 2beaca2bf..e27cdfe98 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -874,13 +874,19 @@ ([x] x) ([x & next] (core/let [forms (concat [x] next)] - (if (every? #(simple-test-expr? &env %) - (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) forms)) + (core/cond + (every? #(simple-test-expr? &env %) + (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) forms)) (core/let [and-str (core/->> (repeat (count forms) "(~{})") (interpose " && ") (#(concat ["("] % [")"])) (apply core/str))] (bool-expr `(~'js* ~and-str ~@forms))) + + (typed-expr? &env x '#{boolean}) + `(if ~x (and ~@next) false) + + :else `(let [and# ~x] (if and# (and ~@next) and#)))))) From 402d47eb12b8fe5614f244dcf1ed300140e9bf51 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 6 Jul 2019 17:35:44 -0400 Subject: [PATCH 3427/4033] CLJS-3123: 'for' loop silently ignores extra forms in body --- src/main/clojure/cljs/analyzer.cljc | 21 +++++++++-- src/test/self/self_host/test.cljs | 58 +++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6d1c724d4..eb065e0df 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3595,6 +3595,11 @@ (and (record-tag? tag) (contains? (record-basis tag) field))) +(defn- invalid-arity? [argc method-params variadic max-fixed-arity] + (and (not (valid-arity? argc method-params)) + (or (not variadic) + (and variadic (< argc max-fixed-arity))))) + (defn parse-invoke* [env [f & args :as form]] (let [enve (assoc env :context :expr) @@ -3623,9 +3628,7 @@ (when (and #?(:cljs (not (and (gstring/endsWith (str cur-ns) "$macros") (symbol-identical? cur-ns ns) (true? macro)))) - (not (valid-arity? argc method-params)) - (or (not variadic) - (and variadic (< argc max-fixed-arity)))) + (invalid-arity? argc method-params variadic max-fixed-arity)) (warning :fn-arity env {:name name :argc argc})))) (when (and kw? (not (or (== 1 argc) (== 2 argc)))) (warning :fn-arity env {:name (first form) :argc argc})) @@ -3822,6 +3825,17 @@ (catch #?(:clj Throwable :cljs :default) e (throw (ex-info nil (error-data env :macro-syntax-check (var->sym mac-var)) e)))))))) +#?(:cljs + (defn- check-macro-arity [mac-var form] + (let [mac-sym (.-sym mac-var)] + (when-let [{:keys [variadic? max-fixed-arity method-params]} + (get-in @env/*compiler* [::namespaces (symbol (namespace mac-sym)) :defs (symbol (name mac-sym))])] + (let [argc (count (rest form)) + offset (if (= '&form (ffirst method-params)) 2 0)] + (when (invalid-arity? argc (map #(nthrest %1 offset) method-params) + variadic? (when max-fixed-arity (- max-fixed-arity offset))) + (throw (js/Error. (error-message :fn-arity {:argc argc, :name mac-sym}))))))))) + (defn macroexpand-1* [env form] (let [op (first form)] @@ -3836,6 +3850,7 @@ :cljs [do]) (do-macroexpand-check env form mac-var) (let [form' (try + #?(:cljs (check-macro-arity mac-var form)) (apply @mac-var form env (rest form)) #?(:clj (catch ArityException e (throw (ArityException. (- (.actual e) 2) (.name e))))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 96139c869..a42e12d58 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1499,6 +1499,64 @@ (is (empty? (js-keys value))) (inc! l))))))) +(deftest test-cljs-3129 + (async done + (let [l (latch 1 done)] + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(ns cljs.user (:require-macros foo-3129-1.core))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo-3129-1.core) (defmacro add [a b] `(+ ~a ~b))"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo-3129-1.core/add 1)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "Wrong number of args (1) passed to foo-3129-1.core$macros/add" + (ex-message (ex-cause (ex-cause error))))) + (inc! l)))))) + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(ns cljs.user (:require-macros foo-3129-2.core))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo-3129-2.core) (defmacro add [a b] `(+ ~a ~b))"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo-3129-2.core/add 1 2 3)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "Wrong number of args (3) passed to foo-3129-2.core$macros/add" + (ex-message (ex-cause (ex-cause error))))) + (inc! l)))))) + (let [st (cljs/empty-state)] + (cljs/eval-str st + "(ns cljs.user (:require-macros foo-3129-3.core))" + nil + {:eval node-eval + :load (fn [_ cb] (cb {:lang :clj :source "(ns foo-3129-3.core) (defmacro when [test & body])"}))} + (fn [{:keys [value error]}] + (is (nil? error)) + (cljs/eval-str st + "(foo-3129-3.core/when)" + nil + {:eval node-eval + :context :expr} + (fn [{:keys [error value]}] + (is (nil? value)) + (is (= "Wrong number of args (0) passed to foo-3129-3.core$macros/when" + (ex-message (ex-cause (ex-cause error))))) + (inc! l))))))))) + (defn -main [& args] (run-tests)) From 331fea688a2a75c97c2993b740991c04067d334f Mon Sep 17 00:00:00 2001 From: LeaveNhA Date: Tue, 23 Jul 2019 22:05:11 +0300 Subject: [PATCH 3428/4033] CLJS-3143: assoc docstring regarding growing vector --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 6c2e04daa..ebfa7c5cb 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1971,7 +1971,7 @@ reduces them without incurring seq initialization" "assoc[iate]. When applied to a map, returns a new map of the same (hashed/sorted) type, that contains the mapping of key(s) to val(s). When applied to a vector, returns a new vector that - contains val at index." + contains val at index. Note - index must be <= (count vector)." ([coll k v] (if-not (nil? coll) (-assoc coll k v) From 63cddf1d1cee375e6b3fe0d3c853631731fe15bc Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 21 Jul 2019 16:53:35 -0400 Subject: [PATCH 3429/4033] CLJS-3141: Improve perf of cljs.source-map.base64/encode --- src/main/clojure/cljs/source_map/base64.clj | 13 ++++++++----- .../clojure/cljs/source_map/base64_tests.clj | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 src/test/clojure/cljs/source_map/base64_tests.clj diff --git a/src/main/clojure/cljs/source_map/base64.clj b/src/main/clojure/cljs/source_map/base64.clj index e786c74f9..a53f8f96b 100644 --- a/src/main/clojure/cljs/source_map/base64.clj +++ b/src/main/clojure/cljs/source_map/base64.clj @@ -12,11 +12,14 @@ (def char->int (zipmap chars64 (range 0 64))) (def int->char (zipmap (range 0 64) chars64)) -(defn encode [n] - (let [e (find int->char n)] - (if e - (second e) - (throw (Error. (str "Must be between 0 and 63: " n)))))) +(defn encode [^long n] + (case n + 0 \A 1 \B 2 \C 3 \D 4 \E 5 \F 6 \G 7 \H 8 \I 9 \J 10 \K 11 \L 12 \M + 13 \N 14 \O 15 \P 16 \Q 17 \R 18 \S 19 \T 20 \U 21 \V 22 \W 23 \X 24 \Y 25 \Z + 26 \a 27 \b 28 \c 29 \d 30 \e 31 \f 32 \g 33 \h 34 \i 35 \j 36 \k 37 \l 38 \m + 39 \n 40 \o 41 \p 42 \q 43 \r 44 \s 45 \t 46 \u 47 \v 48 \w 49 \x 50 \y 51 \z + 52 \0 53 \1 54 \2 55 \3 56 \4 57 \5 58 \6 59 \7 60 \8 61 \9 62 \+ 63 \/ + (throw (Error. (str "Must be between 0 and 63: " n))))) (defn ^Character decode [c] (let [e (find char->int c)] diff --git a/src/test/clojure/cljs/source_map/base64_tests.clj b/src/test/clojure/cljs/source_map/base64_tests.clj new file mode 100644 index 000000000..4e82c3127 --- /dev/null +++ b/src/test/clojure/cljs/source_map/base64_tests.clj @@ -0,0 +1,17 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns cljs.source-map.base64-tests + (:require + [clojure.test :refer [deftest is]] + [cljs.source-map.base64 :as base64])) + +(deftest encode-test + (doseq [n (range 64)] + (is (= (get base64/int->char n) (base64/encode n)))) + (is (thrown-with-msg? Error #"Must be between 0 and 63: 64" (base64/encode 64)))) From 3c7c37b27cb24810b2abb6410aa063b599fe2223 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 21 Jul 2019 09:33:20 -0400 Subject: [PATCH 3430/4033] CLJS-3140: Not inferring on implements? --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- src/test/clojure/cljs/analyzer_tests.clj | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index eb065e0df..ab6ac1802 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1599,14 +1599,15 @@ (defn- type-check-induced-tag "Look for a type-check-induced tag when the test expression is the use of - instance? on a local, as in (instance? ICounted x)." + instance? on a local, as in (instance? UUID x) or implements? on a local, as + in (implements? ICounted x)." [env test] (when (and (list? test) (== 3 (count test)) (every? symbol? test)) (let [analyzed-fn (no-warn (analyze (assoc env :context :expr) (first test)))] (when (= :var (:op analyzed-fn)) - (when ('#{cljs.core/instance?} (:name analyzed-fn)) + (when ('#{cljs.core/instance? cljs.core/implements?} (:name analyzed-fn)) (let [analyzed-type (no-warn (analyze (assoc env :context :expr) (second test))) tag (:name analyzed-type) sym (last test)] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 8b7113606..890084902 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -2091,3 +2091,13 @@ (env/with-compiler-env test-cenv (:tag (ana/analyze test-env '(let [x ^any []] (if (qualified-ident? x) x nil)))))) '#{cljs.core/Keyword cljs.core/Symbol clj-nil}))) + +(deftest test-cljs-3140 + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (instance? UUID x) x nil)))))) + '#{cljs.core/UUID clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (implements? ICounted x) x nil)))))) + '#{cljs.core/ICounted clj-nil}))) From 1a537e57a1fda9ac54f86ed4a88e92713aefdb36 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 24 Jul 2019 15:53:28 -0400 Subject: [PATCH 3431/4033] tag Node.js vars as 'js to avoid .call invokes --- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/main/clojure/cljs/compiler.cljc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ab6ac1802..7377388a5 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1073,7 +1073,8 @@ [env sym full-ns current-ns] {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) :op :js-var - :ns current-ns}) + :ns current-ns + :tag 'js}) (defmethod resolve* :global [env sym full-ns current-ns] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 404e8a4c1..ad43727db 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1142,7 +1142,7 @@ opt-count? (and (= (:name info) 'cljs.core/count) (boolean ('#{string array} first-arg-tag))) ns (:ns info) - js? (or (= ns 'js) (= ns 'Math)) + js? (or (= ns 'js) (= ns 'Math) (= tag 'js)) goog? (when ns (or (= ns 'goog) (when-let [ns-str (str ns)] From f021e99f2fab0069967ed134eecdf4be3b05958c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Jul 2019 14:46:14 -0400 Subject: [PATCH 3432/4033] CLJS-3145: Node.js support libs cljs.nodejs and cljs.nodejscli generate random files set :output-file so the libs don't have randomly generated names --- src/main/clojure/cljs/closure.clj | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 85ec58e47..ee04d5a7f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -3012,12 +3012,18 @@ (compile-sources compiler-stats compile-opts) (#(map add-core-macros-if-cljs-js %)) (add-js-sources opts) - (cond-> (= :nodejs (:target opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") opts)])) + (cond-> (= :nodejs (:target opts)) + (concat + [(-compile (io/resource "cljs/nodejs.cljs") + (assoc opts :output-file "nodejs.js"))])) deps/dependency-order (add-preloads opts) remove-goog-base add-goog-base - (cond-> (= :nodejs (:target opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") opts)])) + (cond-> (= :nodejs (:target opts)) + (concat + [(-compile (io/resource "cljs/nodejscli.cljs") + (assoc opts :output-file "nodejscli.js"))])) (->> (map #(source-on-disk opts %)) doall) (compile-loader opts)) _ (when (:emit-constants opts) From 82fda4ab7305c83cc45dda6ff695bedfe0fe9c70 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Jul 2019 15:35:50 -0400 Subject: [PATCH 3433/4033] CLJS-3144: NPM Modules should have all their vars marked to avoid .call invokes add new var information to vars resolved from Node or global foreign modules - mark them as :foreign true. In the compiler if a f is :foreign, then do not emit the .call convention. By doing this we avoid issues when the library itself is a singleton instance as .call convention breaks `this`. --- src/main/clojure/cljs/analyzer.cljc | 13 ++++++------ src/main/clojure/cljs/compiler.cljc | 2 +- src/test/clojure/cljs/build_api_tests.clj | 24 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7377388a5..e021b5e95 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1071,10 +1071,10 @@ (defmethod resolve* :node [env sym full-ns current-ns] - {:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) + {:ns current-ns + :name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) :op :js-var - :ns current-ns - :tag 'js}) + :foreign true}) (defmethod resolve* :global [env sym full-ns current-ns] @@ -1082,10 +1082,11 @@ (when-not (has-extern? pre) (swap! env/*compiler* update-in (into [::namespaces current-ns :externs] pre) merge {})) - {:name (symbol (str current-ns) (str (munge-global-export full-ns) "." (name sym))) + {:ns current-ns + :name (symbol (str current-ns) (str (munge-global-export full-ns) "." (name sym))) :op :js-var - :ns current-ns - :tag (with-meta 'js {:prefix pre})})) + :tag (with-meta 'js {:prefix pre}) + :foreign true})) (def ^:private private-var-access-exceptions "Specially-treated symbols for which we don't trigger :private-var-access warnings." diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index ad43727db..2b323fdcf 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1142,7 +1142,7 @@ opt-count? (and (= (:name info) 'cljs.core/count) (boolean ('#{string array} first-arg-tag))) ns (:ns info) - js? (or (= ns 'js) (= ns 'Math) (= tag 'js)) + js? (or (= ns 'js) (= ns 'Math) (:foreign info)) ;; foreign - i.e. global / Node.js library goog? (when ns (or (= ns 'goog) (when-let [ns-str (str ns)] diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index a76efc683..ccf889265 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -286,6 +286,30 @@ (.delete (io/file "package.json")) (test/delete-node-modules)) +(deftest test-npm-deps-invoke-cljs-3144 + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (let [cenv (env/default-compiler-env) + out (.getPath (io/file "test-out" #_(test/tmp-dir) "npm-deps-test-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'npm-deps-test.invoke + :output-dir out + :optimizations :none + :install-deps true + :npm-deps {:react "15.6.1" + :react-dom "15.6.1" + :lodash-es "4.17.4" + :lodash "4.17.4"} + :closure-warnings {:check-types :off + :non-standard-jsdoc :off}}}] + (test/delete-out-files out) + (testing "invoking fns from Node.js libraries should not emit .call convention" + (build/build (build/inputs (io/file inputs "npm_deps_test/invoke.cljs")) opts cenv) + (is (.exists (io/file out "node_modules/react/react.js"))) + (is (not (string/includes? (slurp (io/file out "npm_deps_test/invoke.cljs")) "call"))))) + (.delete (io/file "package.json")) + (test/delete-node-modules)) + (deftest test-preloads (let [out (.getPath (io/file (test/tmp-dir) "preloads-test-out")) {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs")) From 7ef8fd92e8a4a566fc5a26d91923f30d3f2f862c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Jul 2019 15:52:27 -0400 Subject: [PATCH 3434/4033] fix test for last commit --- src/test/clojure/cljs/build_api_tests.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index ccf889265..71021b0cd 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -290,7 +290,7 @@ (test/delete-node-modules) (spit (io/file "package.json") "{}") (let [cenv (env/default-compiler-env) - out (.getPath (io/file "test-out" #_(test/tmp-dir) "npm-deps-test-out")) + out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out")) {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) :opts {:main 'npm-deps-test.invoke :output-dir out From 8f38049d543b04b8da55029f140b6577e3ec245a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 25 Jul 2019 16:01:49 -0400 Subject: [PATCH 3435/4033] support hinting fns as ^js to avoid call --- src/main/clojure/cljs/compiler.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 2b323fdcf..76b1c683e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1142,14 +1142,16 @@ opt-count? (and (= (:name info) 'cljs.core/count) (boolean ('#{string array} first-arg-tag))) ns (:ns info) - js? (or (= ns 'js) (= ns 'Math) (:foreign info)) ;; foreign - i.e. global / Node.js library + ftag (ana/infer-tag env f) + js? (or (= ns 'js) (= ns 'Math) + (ana/js-tag? ftag) (:foreign info)) ;; foreign - i.e. global / Node.js library goog? (when ns (or (= ns 'goog) (when-let [ns-str (str ns)] (= (get (string/split ns-str #"\.") 0 nil) "goog")) (not (contains? (::ana/namespaces @env/*compiler*) ns)))) - keyword? (or (= 'cljs.core/Keyword (ana/infer-tag env f)) + keyword? (or (= 'cljs.core/Keyword ftag) (let [f (ana/unwrap-quote f)] (and (= (-> f :op) :const) (keyword? (-> f :form))))) From 885238e97fec2008f03b9413a987ed432d1970a0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Jul 2019 15:17:13 -0400 Subject: [PATCH 3436/4033] add missing test file from last commit --- src/test/cljs_build/npm_deps_test/invoke.cljs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/test/cljs_build/npm_deps_test/invoke.cljs diff --git a/src/test/cljs_build/npm_deps_test/invoke.cljs b/src/test/cljs_build/npm_deps_test/invoke.cljs new file mode 100644 index 000000000..deb595cfd --- /dev/null +++ b/src/test/cljs_build/npm_deps_test/invoke.cljs @@ -0,0 +1,10 @@ +(ns npm-deps-test.invoke + (:require [react :refer [createElement]] + ["react-dom/server" :as ReactDOMServer] + ["lodash-es/array" :as array])) + +(createElement "div") + +(ReactDOMServer/renderToString nil) + +(array/findIndex #js [1 2] 2) From f824c728628076df2c36a44f6e93ad411387a8da Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Jul 2019 20:52:17 -0400 Subject: [PATCH 3437/4033] CLJS-3147: Allow Node require from foreign lib Export require to global. --- src/main/cljs/cljs/bootstrap_nodejs.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/bootstrap_nodejs.js b/src/main/cljs/cljs/bootstrap_nodejs.js index b0dc88415..e06c124a2 100644 --- a/src/main/cljs/cljs/bootstrap_nodejs.js +++ b/src/main/cljs/cljs/bootstrap_nodejs.js @@ -108,12 +108,26 @@ global.CLOSURE_LOAD_FILE_SYNC = function(src) { // Declared here so it can be used to require base.js function nodeGlobalRequire(file) { - var _module = global.module, _exports = global.exports; + var _module = global.module, + _exports = global.exports, + exportedRequire = false; + global.module = undefined; global.exports = undefined; + + if(global.require == undefined) { + exportedRequire = true; + global.require = require; + } + vm.runInThisContext.call(global, fs.readFileSync(file), file); + global.exports = _exports; global.module = _module; + + if(exportedRequire) { + global.require = undefined; + } } From 3f5a60a31ac1aae9eb509da26249db51316c34e6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 29 Jul 2019 21:14:51 -0400 Subject: [PATCH 3438/4033] add comments --- src/main/cljs/cljs/bootstrap_nodejs.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/cljs/cljs/bootstrap_nodejs.js b/src/main/cljs/cljs/bootstrap_nodejs.js index e06c124a2..b96ce400c 100644 --- a/src/main/cljs/cljs/bootstrap_nodejs.js +++ b/src/main/cljs/cljs/bootstrap_nodejs.js @@ -112,9 +112,12 @@ function nodeGlobalRequire(file) { _exports = global.exports, exportedRequire = false; + // to circumvent Node.js environment detection in bundled libraries global.module = undefined; global.exports = undefined; + // to allow requires of Node.js libraries (i.e. platform libs) that + // couldn't be bundled for some reason if(global.require == undefined) { exportedRequire = true; global.require = require; From b38ded99dc0967a48824d55ea644bee86b4eae5b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 12 Aug 2019 13:50:57 -0400 Subject: [PATCH 3439/4033] do not remove .call for ^js inferred fns --- src/main/clojure/cljs/compiler.cljc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 76b1c683e..a531e085e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1143,8 +1143,7 @@ (boolean ('#{string array} first-arg-tag))) ns (:ns info) ftag (ana/infer-tag env f) - js? (or (= ns 'js) (= ns 'Math) - (ana/js-tag? ftag) (:foreign info)) ;; foreign - i.e. global / Node.js library + js? (or (= ns 'js) (= ns 'Math) (:foreign info)) ;; foreign - i.e. global / Node.js library goog? (when ns (or (= ns 'goog) (when-let [ns-str (str ns)] From d79078fbeaef6321d51177756a22e5b13477e5cd Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 12 Sep 2019 16:00:02 +0200 Subject: [PATCH 3440/4033] add some more ignores --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index f5c766238..cfe85231a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,8 @@ src/main/cljs/cljs/core.aot.js src/main/cljs/cljs/core.aot.js.map src/main/cljs/cljs/core.cljs.cache.aot.edn src/main/cljs/cljs/core.cljs.cache.aot.json +.node_repl +package.json +package-lock.json +.cpcache +resources/brepl_client.js From 5865ebbfda4c7a45ffd9428001221a2aa0077210 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 12 Sep 2019 16:19:54 +0200 Subject: [PATCH 3441/4033] get var visibility --- src/main/clojure/cljs/externs.clj | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 5d5579d43..fc81782ef 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -15,7 +15,7 @@ CompilerOptions SourceFile JsAst CommandLineRunner] [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino - Node Token JSTypeExpression] + Node Token JSTypeExpression JSDocInfo$Visibility] [java.util.logging Level])) (def ^:dynamic *ignore-var* false) @@ -33,8 +33,7 @@ (defn get-tag [^JSTypeExpression texpr] (when-let [root (.getRoot texpr)] (if (.isString root) - (symbol (.getString root)) - (if-let [child (.. root getFirstChild)] + (symbol (.getString root))(if-let [child (.. root getFirstChild)] (if (.isString child) (symbol (.. child getString))))))) @@ -57,7 +56,9 @@ {:file *source-file* :line (.getLineno node)} (when-let [doc (.getOriginalCommentString info)] - {:doc doc})))))) + {:doc doc}) + (when (= JSDocInfo$Visibility/PRIVATE (.getVisibility info)) + {:private true})))))) (defmulti parse-extern-node (fn [^Node node] @@ -202,10 +203,13 @@ (closure/js-source-file "goog/string/string.js" (io/input-stream (io/resource "goog/string/string.js")))) - (externs-map - [(closure/js-source-file "goog/string/string.js" - (io/input-stream (io/resource "goog/string/string.js")))] - {}) + (-> (externs-map + [(closure/js-source-file "goog/string/string.js" + (io/input-stream (io/resource "goog/string/string.js")))] + {}) + (get-in '[goog string]) + (find 'numberAwareCompare_) + first meta) (externs-map) From 9f4390a94c2d56803c1d02bdbe5ef6a02ff04afe Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 12 Sep 2019 16:51:06 +0200 Subject: [PATCH 3442/4033] add helpers for parsing goog lib into a ns --- src/main/clojure/cljs/externs.clj | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index fc81782ef..9e1966321 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -172,12 +172,22 @@ externs (index-externs (parse-externs externs-file)))) defaults sources)))) +(defn parsed->defs [externs] + (reduce + (fn [m xs] + (let [sym (last xs)] + (cond-> m + (seq xs) (assoc sym (meta sym))))) + {} externs)) + (defn analyze-goog-file [f] (let [rsrc (io/resource f) desc (js-deps/parse-js-ns (line-seq (io/reader rsrc)))] ;; TODO: figure out what to do about other provides - [(first (:provides desc)) - ])) + {:name (first (:provides desc)) + :defs (parsed->defs + (parse-externs + (SourceFile/fromInputStream f (io/input-stream rsrc))))})) (comment From 1d6d4cd6c7099b4ecfa8a51c815fc1c5574d17a3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 12 Sep 2019 18:25:58 +0200 Subject: [PATCH 3443/4033] CLJS-3123: analyze google closure namespaces arity checking, formatted docstrings, and type inference appear to be working --- src/main/clojure/cljs/analyzer.cljc | 44 ++++++++++++++++------------- src/main/clojure/cljs/externs.clj | 41 ++++++++++++++++++++------- 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index e021b5e95..d6a5fc330 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -14,16 +14,17 @@ :refer [no-warn wrapping-errors with-warning-handlers disallowing-recur allowing-redef disallowing-ns*]] [cljs.env.macros :refer [ensure]])) - #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] - [clojure.java.io :as io] - [clojure.string :as string] - [clojure.set :as set] - [cljs.env :as env :refer [ensure]] - [cljs.js-deps :as deps] - [cljs.tagged-literals :as tags] - [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers] - [clojure.edn :as edn]) + #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] + [clojure.java.io :as io] + [clojure.string :as string] + [clojure.set :as set] + [cljs.env :as env :refer [ensure]] + [cljs.js-deps :as deps] + [cljs.tagged-literals :as tags] + [clojure.tools.reader :as reader] + [clojure.tools.reader.reader-types :as readers] + [clojure.edn :as edn] + [cljs.externs :as externs]) :cljs (:require [goog.string :as gstring] [clojure.string :as string] [clojure.set :as set] @@ -2576,18 +2577,23 @@ (some *cljs-dep-set* deps)))))) (doseq [dep deps] (when-not (or (some? (get-in compiler [::namespaces dep :defs])) - (contains? (:js-dependency-index compiler) (name dep)) (node-module-dep? dep) (js-module-exists? (name dep)) #?(:clj (deps/find-classpath-lib dep))) - #?(:clj (if-some [src (locate-src dep)] - (analyze-file src opts) - (throw - (error env - (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))) - :cljs (throw - (error env - (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))) + (if (contains? (:js-dependency-index compiler) (name dep)) + (let [dep-name (name dep)] + (when (string/starts-with? dep-name "goog.") + (let [js-lib (get-in compiler [:js-dependency-index dep-name]) + ns (externs/analyze-goog-file (:file js-lib))] + (swap! env/*compiler* update-in [::namespaces dep] merge ns)))) + #?(:clj (if-some [src (locate-src dep)] + (analyze-file src opts) + (throw + (error env + (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))) + :cljs (throw + (error env + (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)}))))))))))) (defn missing-use? [lib sym cenv] (let [js-lib (get-in cenv [:js-dependency-index (name lib)])] diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 9e1966321..14fd844b5 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -20,6 +20,7 @@ (def ^:dynamic *ignore-var* false) (def ^:dynamic *source-file* nil) +(def ^:dynamic *goog-ns* nil) ;; ------------------------------------------------------------------------------ ;; Externs Parsing @@ -37,6 +38,16 @@ (if (.isString child) (symbol (.. child getString))))))) +(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)))))) + (defn get-var-info [^Node node] (when node (let [info (.getJSDocInfo node)] @@ -50,9 +61,15 @@ (.isConstructor info) (merge {:ctor qname}) (.isInterface info) (merge {:iface qname}))) (if (.hasReturnType info) - {:tag 'Function - :ret-tag (get-tag (.getReturnType info)) - :arglists (list (into [] (map symbol (.getParameterNames info))))}))) + (let [arglist (into [] (map symbol (.getParameterNames info))) + arglists (params->method-params arglist)] + {:tag 'Function + :fn-var true + :ret-tag (get-tag (.getReturnType info)) + :variadic? (boolean (some '#{var_args} arglist)) + :max-fixed-arity (count (take-while #(not= 'var_args %) arglist)) + :method-params arglists + :arglists arglists})))) {:file *source-file* :line (.getLineno node)} (when-let [doc (.getOriginalCommentString info)] @@ -177,21 +194,25 @@ (fn [m xs] (let [sym (last xs)] (cond-> m - (seq xs) (assoc sym (meta sym))))) + (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym}))))) {} externs)) (defn analyze-goog-file [f] (let [rsrc (io/resource f) - desc (js-deps/parse-js-ns (line-seq (io/reader rsrc)))] + desc (js-deps/parse-js-ns (line-seq (io/reader rsrc))) + ns (-> (:provides desc) first symbol)] ;; TODO: figure out what to do about other provides - {:name (first (:provides desc)) - :defs (parsed->defs - (parse-externs - (SourceFile/fromInputStream f (io/input-stream rsrc))))})) + (binding [*goog-ns* ns] + {:name ns + :defs (parsed->defs + (parse-externs + (SourceFile/fromInputStream f (io/input-stream rsrc))))}))) (comment - (analyze-goog-file "goog/string/string.js") + (pprint (analyze-goog-file "goog/object/object.js")) + + (pprint (analyze-goog-file "goog/string/string.js")) (require '[clojure.java.io :as io] '[cljs.closure :as closure] From afc82b17270a35e5ff3f988cbce10b8c41ce1f5b Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 14 Sep 2019 17:54:11 +0200 Subject: [PATCH 3444/4033] CLJS-3165: Browser REPL broken with goog.object.get.cljs$core$IFn$_invoke$arity$2: don't treat JS fn vars as CLJS fn vars, tweak invoke check for change --- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/main/clojure/cljs/externs.clj | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d6a5fc330..3c9c2f7ec 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3614,7 +3614,8 @@ (let [enve (assoc env :context :expr) fexpr (analyze enve f) argc (count args) - fn-var? (-> fexpr :info :fn-var) + fn-var? (or (-> fexpr :info :fn-var) + (-> fexpr :info :js-fn-var)) kw? (= 'cljs.core/Keyword (:tag fexpr)) cur-ns (-> env :ns :name) HO-invoke? (and (boolean *cljs-static-fns*) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 14fd844b5..c3fbcfb37 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -64,7 +64,7 @@ (let [arglist (into [] (map symbol (.getParameterNames info))) arglists (params->method-params arglist)] {:tag 'Function - :fn-var true + :js-fn-var true :ret-tag (get-tag (.getReturnType info)) :variadic? (boolean (some '#{var_args} arglist)) :max-fixed-arity (count (take-while #(not= 'var_args %) arglist)) From da6432f05e6be1eeb0a2ba6cb525c3797efb024c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 14 Sep 2019 18:40:14 +0200 Subject: [PATCH 3445/4033] CLJS-3167: Warn regarding goog.dom/setTextContext with browser REPL goog var support found typo. If info has @param, consider as fn. if no @return, assume returns nil. NOTE: still need to consider user supplied libs which may not provide this kind of type information. --- src/main/cljs/clojure/browser/repl.cljs | 2 +- src/main/clojure/cljs/externs.clj | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index a550f6135..4f72283a5 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -185,7 +185,7 @@ (gobj/set "onreadystatechange" onload)) ;; IE (if (nil? opt_sourceText) (doto script (gobj/set "src" src)) - (doto script (gdom/setTextContext opt_sourceText)))))))) + (doto script (gdom/setTextContent opt_sourceText)))))))) ;; queue or load (set! (.-writeScriptTag_ js/goog) (fn [src opt_sourceText] diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index c3fbcfb37..4899186c4 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -60,12 +60,15 @@ (cond-> {:tag 'Function} (.isConstructor info) (merge {:ctor qname}) (.isInterface info) (merge {:iface qname}))) - (if (.hasReturnType info) + (if (or (.hasReturnType info) + (as-> (.getParameterCount info) c + (and c (pos? c)))) (let [arglist (into [] (map symbol (.getParameterNames info))) arglists (params->method-params arglist)] {:tag 'Function :js-fn-var true - :ret-tag (get-tag (.getReturnType info)) + :ret-tag (or (some-> (.getReturnType info) get-tag) + 'clj-nil) :variadic? (boolean (some '#{var_args} arglist)) :max-fixed-arity (count (take-while #(not= 'var_args %) arglist)) :method-params arglists @@ -209,16 +212,17 @@ (SourceFile/fromInputStream f (io/input-stream rsrc))))}))) (comment - - (pprint (analyze-goog-file "goog/object/object.js")) - - (pprint (analyze-goog-file "goog/string/string.js")) - (require '[clojure.java.io :as io] '[cljs.closure :as closure] '[clojure.pprint :refer [pprint]] '[cljs.js-deps :as js-deps]) + (pprint + (get-in (analyze-goog-file "goog/dom/dom.js") + [:defs 'setTextContent])) + + (pprint (analyze-goog-file "goog/string/string.js")) + (get (js-deps/js-dependency-index {}) "goog.string") ;; {:tag Function :ret-tag boolean} From a760439b5084937a556712d6ee056e95f4f075c7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 14 Sep 2019 18:50:22 +0200 Subject: [PATCH 3446/4033] CLJS-3168: Self-host: externs ns used unconditionally in analyzer conditionalize cljs.externs/analyze-goog-file --- src/main/clojure/cljs/analyzer.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3c9c2f7ec..1f8ce54e1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2583,9 +2583,9 @@ (if (contains? (:js-dependency-index compiler) (name dep)) (let [dep-name (name dep)] (when (string/starts-with? dep-name "goog.") - (let [js-lib (get-in compiler [:js-dependency-index dep-name]) - ns (externs/analyze-goog-file (:file js-lib))] - (swap! env/*compiler* update-in [::namespaces dep] merge ns)))) + #?(:clj (let [js-lib (get-in compiler [:js-dependency-index dep-name]) + ns (externs/analyze-goog-file (:file js-lib))] + (swap! env/*compiler* update-in [::namespaces dep] merge ns))))) #?(:clj (if-some [src (locate-src dep)] (analyze-file src opts) (throw From e1857b454978b60969859bf0792ee744534fb582 Mon Sep 17 00:00:00 2001 From: LeaveNhA Date: Tue, 30 Jul 2019 11:08:47 +0300 Subject: [PATCH 3447/4033] CLJS-3148: Quote in goog-define docstring example --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index e27cdfe98..e360ea791 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -743,7 +743,7 @@ ;; can be overridden with :closure-defines {\"your_app.core.DEBUG_BANG_\" true} or - :closure-defines {'your-app.core/DEBUG! true}" + :closure-defines {your-app.core/DEBUG! true}" [sym default] (assert-args goog-define (core/or (core/string? default) From 8e8c776c278b6b88494f2d1748007acc75421651 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Aug 2019 16:22:53 -0400 Subject: [PATCH 3448/4033] CLJS-3158: Improperly widened cross-param loop/recur inference --- src/main/clojure/cljs/analyzer.cljc | 6 +++++- src/test/clojure/cljs/analyzer_tests.clj | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 1f8ce54e1..28dc34800 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2404,7 +2404,11 @@ (reset! (:flag frame) true) (swap! (:tags frame) (fn [tags] (mapv (fn [tag expr] - (add-types tag (:tag expr))) + ;; Widen by adding the type of the recur expression, except when recurring with a + ;; loop local: Since its final widened type is unknown, conservatively assume 'any. + (if (= :loop (:local expr)) + 'any + (add-types tag (:tag expr)))) tags exprs))) (assoc {:env env :op :recur :form form} :frame frame diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 890084902..a864bf8bc 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -2101,3 +2101,12 @@ (env/with-compiler-env test-cenv (:tag (ana/analyze test-env '(let [x ^any []] (if (implements? ICounted x) x nil)))))) '#{cljs.core/ICounted clj-nil}))) + +(deftest test-cljs-3158 + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (analyze test-env '(loop [a "x" b "y"] + (if (= a 1) + a + (recur b 1))))))) + 'any))) From b5e22d29928c8c65e595e1f008548b21674a75d3 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 18 Sep 2019 23:25:55 -0400 Subject: [PATCH 3449/4033] CLJS-3172: Unable to import goog.async.ConditionalDelay --- src/main/clojure/cljs/js_deps.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 859ae96fa..5fc100b1b 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -114,6 +114,8 @@ case." (letfn [(conj-in [m k v] (update-in m [k] (fn [old] (conj old v))))] (->> (for [line lines x (string/split line #";")] x) (map string/trim) + (drop-while #(not (or (string/includes? % "goog.provide(") + (string/includes? % "goog.require(")))) (take-while #(not (re-matches #".*=[\s]*function\(.*\)[\s]*[{].*" %))) (map #(re-matches #".*goog\.(provide|require)\(['\"](.*)['\"]\)" %)) (remove nil?) From 9158e595c2e92de303cad2ef8923e05a4f57e5b6 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 19 Sep 2019 21:43:50 -0400 Subject: [PATCH 3450/4033] CLJS-3171: Infer from externs tests failing --- src/main/clojure/cljs/analyzer.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 28dc34800..ba2651df7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3589,7 +3589,8 @@ (defn- valid-arity? #?(:cljs {:tag boolean}) [argc method-params] - (boolean (some #{argc} (map count method-params)))) + (or (nil? method-params) ; Assume valid if method-params unavailable + (boolean (some #{argc} (map count method-params))))) (defn- record-tag? [tag] From d79eda372f35e3c79c1f9cec1219266b60d40cb4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 5 Sep 2019 11:53:38 -0400 Subject: [PATCH 3451/4033] CLJS-3163: Skip analyzing specials in type-check-induced-tag --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ba2651df7..c2c496cb4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1600,6 +1600,8 @@ (get-in env [:locals sym])) [sym tag]))))))) +(declare specials) + (defn- type-check-induced-tag "Look for a type-check-induced tag when the test expression is the use of instance? on a local, as in (instance? UUID x) or implements? on a local, as @@ -1607,7 +1609,8 @@ [env test] (when (and (list? test) (== 3 (count test)) - (every? symbol? test)) + (every? symbol? test) + (not (contains? specials (first test)))) (let [analyzed-fn (no-warn (analyze (assoc env :context :expr) (first test)))] (when (= :var (:op analyzed-fn)) (when ('#{cljs.core/instance? cljs.core/implements?} (:name analyzed-fn)) From 5ad96a8b3ae2e3616a19715ba9ba2471a36933a2 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 11 Sep 2019 15:34:00 -0400 Subject: [PATCH 3452/4033] CLJS-3164: Optimize assoc on IAssociative values --- src/main/cljs/cljs/core.cljs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ebfa7c5cb..2e2a9ff5a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1973,9 +1973,11 @@ reduces them without incurring seq initialization" val(s). When applied to a vector, returns a new vector that contains val at index. Note - index must be <= (count vector)." ([coll k v] - (if-not (nil? coll) - (-assoc coll k v) - (array-map k v))) + (if (implements? IAssociative coll) + (-assoc coll k v) + (if-not (nil? coll) + (-assoc coll k v) + (array-map k v)))) ([coll k v & kvs] (let [ret (assoc coll k v)] (if kvs From 850ce95967ba3ef32165f0308bffcd8394f7d41f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 31 Oct 2019 15:23:49 +0100 Subject: [PATCH 3453/4033] CLJS-3149: REPL load-file doesn't resolve npm requires correctly need to call handle-js-modules in load-file --- src/main/clojure/cljs/repl.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index f1ef49ba6..d804fba2a 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -599,6 +599,8 @@ (.exists (io/file f)) (io/file f) :else (io/resource f)) compiled (binding [ana/*reload-macros* true] + (cljsc/handle-js-modules + opts [(ana/parse-ns src)] env/*compiler*) (cljsc/compile src (assoc opts :output-file (cljsc/src-file->target-file src) From 90c0b7848010c1146898fa8a35486b0003cd926e Mon Sep 17 00:00:00 2001 From: roman01la Date: Tue, 16 Jul 2019 15:49:40 +0300 Subject: [PATCH 3454/4033] CLJS-3124: Non-number lookup on transient vector succeeds after persistent! --- src/main/cljs/cljs/core.cljs | 8 +++++--- src/test/cljs/cljs/collections_test.cljs | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 2e2a9ff5a..29619ab69 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6149,9 +6149,11 @@ reduces them without incurring seq initialization" ILookup (-lookup [coll k] (-lookup coll k nil)) - (-lookup [coll k not-found] (if (number? k) - (-nth coll k not-found) - not-found)) + (-lookup [coll k not-found] + (cond + (not ^boolean (.-edit root)) (throw (js/Error. "lookup after persistent!")) + (number? k) (-nth coll k not-found) + :else not-found)) IFn (-invoke [coll k] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index c4f335021..2c63d297a 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -1024,6 +1024,11 @@ (chunk-append b 0) (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)))))) + (comment (run-tests) From de6ec365624e06ea4d88d27599b00360c00f68dd Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 31 Oct 2019 16:24:38 +0100 Subject: [PATCH 3455/4033] CLJS-3176: Unexpected number of args warning for some closure functions need to pass the ns being loaded to analyze-goog-file ignore definitions from other provided namespaces add tests --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/main/clojure/cljs/externs.clj | 54 +++++++++++++------ .../clojure/cljs/externs_parsing_test.clj | 15 ++++++ 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c2c496cb4..35243a8f0 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2591,7 +2591,7 @@ (let [dep-name (name dep)] (when (string/starts-with? dep-name "goog.") #?(:clj (let [js-lib (get-in compiler [:js-dependency-index dep-name]) - ns (externs/analyze-goog-file (:file js-lib))] + ns (externs/analyze-goog-file (:file js-lib) (symbol dep-name))] (swap! env/*compiler* update-in [::namespaces dep] merge ns))))) #?(:clj (if-some [src (locate-src dep)] (analyze-file src opts) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 4899186c4..d529201ad 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -192,24 +192,35 @@ externs (index-externs (parse-externs externs-file)))) defaults sources)))) -(defn parsed->defs [externs] - (reduce - (fn [m xs] - (let [sym (last xs)] - (cond-> m - (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym}))))) - {} externs)) +(defn ns-match? [ns-segs var-segs] + (and + (= (inc (count ns-segs)) (count var-segs)) + (= ns-segs (take (count ns-segs) var-segs)))) -(defn analyze-goog-file [f] - (let [rsrc (io/resource f) - desc (js-deps/parse-js-ns (line-seq (io/reader rsrc))) - ns (-> (:provides desc) first symbol)] - ;; TODO: figure out what to do about other provides - (binding [*goog-ns* ns] - {:name ns - :defs (parsed->defs - (parse-externs - (SourceFile/fromInputStream f (io/input-stream rsrc))))}))) +(defn parsed->defs [externs] + (let [ns-segs (into [] (map symbol (string/split (str *goog-ns*) #"\.")))] + (reduce + (fn [m xs] + ;; ignore definitions from other provided namespaces not under consideration + (if (ns-match? ns-segs xs) + (let [sym (last xs)] + (cond-> m + (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym})))) + m)) + {} externs))) + +(defn analyze-goog-file + ([f] + (analyze-goog-file f nil)) + ([f ns] + (let [rsrc (io/resource f) + desc (js-deps/parse-js-ns (line-seq (io/reader rsrc))) + ns (or ns (-> (:provides desc) first symbol))] + (binding [*goog-ns* ns] + {:name ns + :defs (parsed->defs + (parse-externs + (SourceFile/fromInputStream f (io/input-stream rsrc))))})))) (comment (require '[clojure.java.io :as io] @@ -246,6 +257,15 @@ (find 'numberAwareCompare_) first meta) + (-> (externs-map + [(closure/js-source-file "goog/date/date.js" + (io/input-stream (io/resource "goog/date/date.js")))] + {}) + (get-in '[goog date month]) + ) + + (pprint (analyze-goog-file "goog/date/date.js" 'goog.date.month)) + (externs-map) (-> (externs-map) diff --git a/src/test/clojure/cljs/externs_parsing_test.clj b/src/test/clojure/cljs/externs_parsing_test.clj index 88446d116..2af87352c 100644 --- a/src/test/clojure/cljs/externs_parsing_test.clj +++ b/src/test/clojure/cljs/externs_parsing_test.clj @@ -21,3 +21,18 @@ (= (count (distinct xs)) (count xs))) externs)))) + +(deftest cljs-3176 + (let [ns (externs/analyze-goog-file "goog/date/date.js") + v (get-in ns [:defs 'getWeekNumber])] + (is (= 3 (-> v :method-params first count)))) + (let [ns (externs/analyze-goog-file "goog/date/date.js" 'goog.date.month)] + (is (= 12 (-> ns :defs count))))) + +(comment + + (test/run-tests) + + (externs/analyze-goog-file "goog/date/date.js" 'goog.date.month) + + ) \ No newline at end of file From 6431e6435ad9fc85e766752f4d6939ff978db5a5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 2 Nov 2019 12:11:20 +0100 Subject: [PATCH 3456/4033] remove dead links --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 27e5a6aea..9f1aa7b0c 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ Latest stable release: 1.10.520 ## Getting Started ## -* [Compare with JavaScript](http://himera.herokuapp.com/synonym.html) -* [Try it online](http://himera.herokuapp.com/index.html) * Read the [Quick Start](http://clojurescript.org/guides/quick-start) guide. * Read the [Documentation](http://clojurescript.org). * Try a [tutorial](http://clojurescript.org/guides). From 82ab13febad04d641fb86204d785395105399046 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 3 Nov 2019 10:42:26 +0100 Subject: [PATCH 3457/4033] CLJS-3137: fspec cannot be reused in clojurescript but can be in clojure move the var fdef vars bits into def, add tests --- src/main/cljs/cljs/spec/alpha.cljc | 20 +++++++++++--------- src/test/cljs/cljs/spec_test.cljs | 13 +++++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljc b/src/main/cljs/cljs/spec/alpha.cljc index 372e43c1e..a181c699b 100644 --- a/src/main/cljs/cljs/spec/alpha.cljc +++ b/src/main/cljs/cljs/spec/alpha.cljc @@ -60,13 +60,23 @@ (ana/resolve-var env s))) (symbol (str ana/*cljs-ns*) (str s)))) +(defonce ^:private _speced_vars (atom #{})) + +(defn speced-vars [] + @_speced_vars) + (defmacro def "Given a namespace-qualified keyword or resolveable symbol k, and a spec, spec-name, predicate or regex-op makes an entry in the registry mapping k to the spec. Use nil to remove an entry in the registry for k." [k spec-form] - (let [k (if (symbol? k) (ns-qualify &env k) k) + (let [k (if (symbol? k) + (let [sym (ns-qualify &env k)] + (swap! _speced_vars conj + (vary-meta sym assoc :fdef-ns (-> &env :ns :name))) + sym) + k) form (res &env spec-form)] (swap! registry-ref (fn [r] (if (nil? form) @@ -403,11 +413,6 @@ (clojure.core/assert (not (empty? preds))) `(tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) -(defonce ^:private _speced_vars (atom #{})) - -(defn speced-vars [] - @_speced_vars) - (defmacro fdef "Takes a symbol naming a function, and one or more of the following: @@ -441,9 +446,6 @@ :sym symbol?) :ret symbol?)" [fn-sym & specs] - (swap! _speced_vars conj - (vary-meta (ns-qualify &env fn-sym) - assoc :fdef-ns (-> &env :ns :name))) `(cljs.spec.alpha/def ~fn-sym (fspec ~@specs))) (defmacro keys* diff --git a/src/test/cljs/cljs/spec_test.cljs b/src/test/cljs/cljs/spec_test.cljs index c6d1ce2f9..d40ecb37e 100644 --- a/src/test/cljs/cljs/spec_test.cljs +++ b/src/test/cljs/cljs/spec_test.cljs @@ -455,6 +455,19 @@ (is (thrown? js/Error (defk :foo 1))) (is (= [:foo "bar"] (defk :foo "bar")))) +(s/def ::add-spec + (s/fspec :args (s/cat :n pos?) + :ret number?)) + +(s/def add2 ::add-spec) +(defn add2 [n] + (+ n 2)) + +(st/instrument `add2) + +(deftest cljs-3137 + (is (thrown? js/Error (add2 0)))) + (comment (run-tests) From df1837048d01b157a04bb3dc7fedc58ee349a24a Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 15 Jun 2019 23:16:56 -0400 Subject: [PATCH 3458/4033] CLJS-3107: Eliminate checked ifs in TransientArrayMap --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 29619ab69..44839720b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7025,7 +7025,7 @@ reduces them without incurring seq initialization" (declare array->transient-hash-map) -(deftype TransientArrayMap [^:mutable editable? +(deftype TransientArrayMap [^:mutable ^boolean editable? ^:mutable len arr] ICounted From fce78f1209ed49189a5aad7cb1b38414802d0b5f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 14 Nov 2019 15:22:51 -0500 Subject: [PATCH 3459/4033] CLJS-3149: REPL load-file doesn't resolve npm requires correctly (:npm-deps + :target :nodejs) need load the dep graph of loaded file for all npm requires to be resolved correctly --- src/main/clojure/cljs/repl.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index d804fba2a..a47857125 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -599,8 +599,10 @@ (.exists (io/file f)) (io/file f) :else (io/resource f)) compiled (binding [ana/*reload-macros* true] - (cljsc/handle-js-modules - opts [(ana/parse-ns src)] env/*compiler*) + (cljsc/handle-js-modules opts + (deps/dependency-order + (cljsc/add-dependency-sources [(ana/parse-ns src)] opts)) + env/*compiler*) (cljsc/compile src (assoc opts :output-file (cljsc/src-file->target-file src) From 973687b874e29ba6563085bf6785acd2bbe0e760 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 28 May 2019 17:18:50 +0200 Subject: [PATCH 3460/4033] CLJS-3077: Avoid generating unnecessary functions Only need to capture locals when creating functions inside a loop or recur'ing fn. --- src/main/clojure/cljs/analyzer.cljc | 21 +++++++---- src/main/clojure/cljs/compiler.cljc | 14 +++++--- src/test/clojure/cljs/compiler_tests.clj | 45 ++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 35243a8f0..47dc2d402 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2126,12 +2126,15 @@ type (::type form-meta) proto-impl (::protocol-impl form-meta) proto-inline (::protocol-inline form-meta) - menv (if (> (count meths) 1) - (assoc env :context :expr) - env) - menv (merge menv - {:protocol-impl proto-impl - :protocol-inline proto-inline}) + menv (-> env + (cond-> + (> (count meths) 1) + (assoc :context :expr)) + ;; clear loop flag since method bodies won't be in a loop at first + ;; only tracking this to keep track of locals we need to capture + (dissoc :in-loop) + (merge {:protocol-impl proto-impl + :protocol-inline proto-inline})) methods (map #(disallowing-ns* (analyze-fn-method menv locals % type (nil? name))) meths) mfa (transduce (map :fixed-arity) max 0 methods) variadic (boolean (some :variadic? methods)) @@ -2166,6 +2169,7 @@ :tag 'function :inferred-ret-tag inferred-ret-tag :recur-frames *recur-frames* + :in-loop (:in-loop env) :loop-lets *loop-lets* :jsdoc [js-doc] :max-fixed-arity mfa @@ -2343,7 +2347,10 @@ (partition 2 bindings) widened-tags)) bindings) - [bes env] (analyze-let-bindings encl-env bindings op) + [bes env] (-> encl-env + (cond-> + (true? is-loop) (assoc :in-loop true)) + (analyze-let-bindings bindings op)) recur-frame (when (true? is-loop) {:params bes :flag (atom nil) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a531e085e..6d6bff080 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -957,13 +957,17 @@ (emitln "})()")))) (defmethod emit* :fn - [{variadic :variadic? :keys [name env methods max-fixed-arity recur-frames loop-lets]}] + [{variadic :variadic? :keys [name env methods max-fixed-arity recur-frames in-loop loop-lets]}] ;;fn statements get erased, serve no purpose and can pollute scope if named (when-not (= :statement (:context env)) - (let [loop-locals (->> (concat (mapcat :params (filter #(and % @(:flag %)) recur-frames)) - (mapcat :params loop-lets)) - (map munge) - seq)] + (let [recur-params (mapcat :params (filter #(and % @(:flag %)) recur-frames)) + loop-locals + (->> (concat recur-params + ;; need to capture locals only if in recur fn or loop + (when (or in-loop (seq recur-params)) + (mapcat :params loop-lets))) + (map munge) + seq)] (when loop-locals (when (= :return (:context env)) (emits "return ")) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index b48e3c751..cb0e71e17 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -286,6 +286,51 @@ (is (str/includes? content "cljs.invoke_test.foo_record.foo_field_a;"))))) #_(test-vars [#'test-optimized-invoke-emit]) +(deftest test-cljs-3077 + (let [opts {} + cenv (env/default-compiler-env opts) + + test-compile + (fn [code] + (env/with-compiler-env cenv + (with-out-str + (emit + (comp/with-core-cljs + opts + (fn [] (analyze aenv code nil opts))))))) + + snippet1 + (test-compile + '(defn wrapper1 [foo] + (let [x 1] + (prn (fn inner [] foo)) + (recur (inc foo))))) + + snippet2 + (test-compile + '(defn wrapper2 [foo] + (loop [x 1] + (prn (fn inner [] x)) + (recur (inc x)) + ))) + + snippet3 + (test-compile + '(defn no-wrapper1 [foo] + (let [x 1] + (prn (fn inner [] foo)))))] + + ;; FIXME: not exactly a clean way to test if function wrappers are created or not + ;; captures foo,x + (is (str/includes? snippet1 "(function (foo,x){")) + ;; captures x + (is (str/includes? snippet2 "(function (x){")) + ;; no capture, no loop or recur + (is (not (str/includes? snippet3 "(function (foo,x){"))) + (is (not (str/includes? snippet3 "(function (foo){"))) + (is (not (str/includes? snippet3 "(function (x){"))) + )) + ;; CLJS-1225 (comment From 910fbe304b7cff60ff117851311a9534de3885d6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 15 Nov 2019 15:36:42 -0500 Subject: [PATCH 3461/4033] CLJS-3170: Checked-arrays triggers on inferred Array type if we see Array return type, convert to array. add test. CLJS-3189: Incorrect inference for goog.object/get if we have a generic type, set return type to 'any. add test --- src/main/clojure/cljs/externs.clj | 14 +++++++++++++- src/test/clojure/cljs/externs_parsing_test.clj | 5 +++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index d529201ad..a20a897cb 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -48,6 +48,17 @@ (recur (conj ret (conj (last ret) opt)) (drop 1 opts)) (seq ret)))))) +(defn generic? [t] + (let [s (name t)] + (boolean (re-matches #"[A-Z]" s)))) + +(defn gtype->cljs-type [t] + (when t + (cond + (generic? t) 'any + (= t 'Array) 'array + :else t))) + (defn get-var-info [^Node node] (when node (let [info (.getJSDocInfo node)] @@ -67,7 +78,8 @@ arglists (params->method-params arglist)] {:tag 'Function :js-fn-var true - :ret-tag (or (some-> (.getReturnType info) get-tag) + :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)) diff --git a/src/test/clojure/cljs/externs_parsing_test.clj b/src/test/clojure/cljs/externs_parsing_test.clj index 2af87352c..46fe5aa37 100644 --- a/src/test/clojure/cljs/externs_parsing_test.clj +++ b/src/test/clojure/cljs/externs_parsing_test.clj @@ -29,6 +29,11 @@ (let [ns (externs/analyze-goog-file "goog/date/date.js" 'goog.date.month)] (is (= 12 (-> ns :defs count))))) +(deftest cljs-3170&3189 + (let [ns (externs/analyze-goog-file "goog/object/object.js")] + (is (= 'any (get-in ns [:defs 'get :ret-tag]))) + (is (= 'array (get-in ns [:defs 'getKeys :ret-tag]))))) + (comment (test/run-tests) From 6ed949278ba61dceeafb709583415578b6f7649b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 17 Nov 2019 09:25:43 -0500 Subject: [PATCH 3462/4033] CLJS-3190: Double arity warning constructing directly-accessed record --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/clojure/cljs/analyzer_tests.clj | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 47dc2d402..b75c48c76 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3681,7 +3681,7 @@ (== 1 (count args)) (record-with-field? (:tag (first argexprs)) (symbol (name f)))) (let [field-access-form (list* (symbol (str ".-" (name f))) args)] - (analyze env field-access-form)) + (no-warn (analyze env field-access-form))) {:env env :op :invoke :form form :fn fexpr :args argexprs :children [:fn :args]}))))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index a864bf8bc..6a4130f95 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -2110,3 +2110,14 @@ a (recur b 1))))))) 'any))) + +(deftest test-cljs-3190 + (let [ws (atom [])] + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env @test-cenv + (analyze (ana/empty-env) + '(do + (defrecord Foo [a]) + (:a (->Foo)))))) + (is (= 1 (count @ws))) + (is (string/starts-with? (first @ws) "Wrong number of args (0) passed to cljs.user/->Foo")))) From 23cedecbf4f704f9fee672e395bbfa1e3fe3ee1a Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 18 Nov 2019 19:47:50 -0500 Subject: [PATCH 3463/4033] 1.10.597 --- README.md | 6 +++--- changes.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9f1aa7b0c..068096db8 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.520 +Latest stable release: 1.10.597 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.520"] +[org.clojure/clojurescript "1.10.597"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.520 org.clojure clojurescript - 1.10.520 + 1.10.597 ``` diff --git a/changes.md b/changes.md index 22410570b..0d686c26a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,56 @@ +## 1.10.597 + +### Changes +* CLJS-3120: Add :sigs to protocol var for compatibility with Clojure +* CLJS-2247: Warn when overwriting protocol method +* CLJS-3085: Types are inferred for dynamic vars +* CLJS-3097: Fix compatibility with tools.reader 1.3.1 and bump it +* CLJS-2750: tag coll in ci-reduce as not-native +* CLJS-3095: `apply vector` with array acts as `vec` +* CLJS-3093: Check subvec arguments +* CLJS-2868: Add ^string hints +* CLJS-3054: Align behavior of set/union and into with Clojure + +### Enhancements +* CLJS-3077: Avoid generating unnecessary functions +* CLJS-3107: Eliminate checked ifs in TransientArrayMap +* CLJS-3164: Optimize assoc on IAssociative values +* CLJS-3147: Allow Node require from foreign lib +* CLJS-3144: NPM Modules should have all their vars marked to avoid .call invokes +* CLJS-3145: Node.js support libs cljs.nodejs and cljs.nodejscli generate random files +* CLJS-3141: Improve perf of cljs.source-map.base64/encode +* CLJS-3134: Thread predicate-induced inference through and +* CLJS-3123: analyze google closure namespaces +* CLJS-3133: simple-* / qualified-* predicate-induced inference +* CLJS-2886: count specializations for string and array +* CLJS-2950: Direct field access for keyword lookup on records + +### Fixes +* CLJS-3190: Double arity warning constructing directly-accessed record +* CLJS-3137: fspec cannot be reused in clojurescript but can be in clojure +* CLJS-3124: Non-number lookup on transient vector succeeds after persistent! +* CLJS-3149: REPL load-file doesn't resolve npm requires correctly +* CLJS-3163: Skip analyzing specials in type-check-induced-tag +* CLJS-3172: Unable to import goog.async.ConditionalDelay +* CLJS-3158: Improperly widened cross-param loop/recur inference +* CLJS-3168: Self-host: externs ns used unconditionally in analyzer +* CLJS-3140: Not inferring on implements? +* CLJS-3143: assoc docstring regarding growing vector +* CLJS-3123: 'for' loop silently ignores extra forms in body +* CLJS-3017: Error->map: Map js/InternalError and js/TypeError +* CLJS-2683: Suppress compiler options in watch log +* CLJS-2881: cl-format character directive with \space fails +* CLJS-2879: Close analysis cache file +* CLJS-3051: Update to Graal RC12 in CI +* CLJS-3088: Update CI to use JavaScriptCore 4 +* CLJS-3092: Peek on subvecs doesn't behave properly +* CLJS-3076: let defined variadic functions not destructuring as expected with :static-fns true +* CLJS-3067: Fix compiler crash when requiring cljs.loader w/o modules +* CLJS-3068: Compiler error with if and emit-var +* CLJS-2301: Avoid use of deprecated goog.string/isEmptySafe in clojure.string/blank? +* CLJS-3058: Remove dangling goog.date.relativeWithPlurals reference +* CLJS-3061 Fix docstring for chunked-seq? + ## 1.10.520 ### Changes From dcc8e61c79bfc701fe9e1414fe5db93edf6f1853 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 9 Jan 2020 14:47:23 -0500 Subject: [PATCH 3464/4033] CLJS-3199: Interop with JavaScript's iterable objects via Iterator protocol Move es6-iterator-seq declare. Add js-iterable? predicate. Add case to seq. --- src/main/cljs/cljs/core.cljs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 44839720b..0ca832fcc 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1192,6 +1192,11 @@ [x] (satisfies? IIterable x)) +(defn js-iterable? + "Return true if x has a JavaScript iterator property" + [x] + (gobject/containsKey x ITER_SYMBOL)) + (defn clone "Clone the supplied value which must implement ICloneable." [value] @@ -1202,6 +1207,8 @@ [value] (satisfies? ICloneable value)) +(declare es6-iterator-seq) + (defn ^seq seq "Returns a seq on the collection. If the collection is empty, returns nil. (seq nil) returns nil. seq also works on @@ -1220,6 +1227,10 @@ (when-not (zero? (.-length coll)) (IndexedSeq. coll 0 nil)) + (js-iterable? coll) + (es6-iterator-seq + (.call (gobject/get coll ITER_SYMBOL) coll)) + (native-satisfies? ISeqable coll) (-seq coll) @@ -1291,8 +1302,6 @@ [coll] (ES6Iterator. (seq coll))) -(declare es6-iterator-seq) - (deftype ES6IteratorSeq [value iter ^:mutable _rest] ISeqable (-seq [this] this) From 79c1e828449cbb8c354fdd5ec0d999a5424249d3 Mon Sep 17 00:00:00 2001 From: Colin Kahn Date: Sun, 15 Dec 2019 09:37:30 -0800 Subject: [PATCH 3465/4033] CLJS-3203: Overriding root path in s/cat using s/gen gives an error --- src/main/cljs/cljs/spec/alpha.cljs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index 19a9241f4..c88079f5f 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -1213,7 +1213,8 @@ (defn- re-gen [p overrides path rmap f] ;;(prn {:op op :ks ks :forms forms}) - (let [{:keys [::op ps ks p1 p2 forms splice ret id ::gfn] :as p} (reg-resolve! p) + (let [origp p + {:keys [::op ps ks p1 p2 forms splice ret id ::gfn] :as p} (reg-resolve! p) rmap (if id (inck rmap id) rmap) ggens (fn [ps ks forms] (let [gen (fn [p k f] @@ -1223,10 +1224,12 @@ (gen/delay (re-gen p overrides (if k (conj path k) path) rmap (c/or f p))) (re-gen p overrides (if k (conj path k) path) rmap (c/or f p)))))] (map gen ps (c/or (seq ks) (repeat nil)) (c/or (seq forms) (repeat nil)))))] - (c/or (when-let [g (get overrides path)] + (c/or (when-let [gfn (c/or (get overrides (spec-name origp)) + (get overrides (spec-name p)) + (get overrides path))] (case op - (:accept nil) (gen/fmap vector g) - g)) + (:accept nil) (gen/fmap vector (gfn)) + (gfn))) (when gfn (gfn)) (when p From 4ffcaab10adde2167d0a5f92b49410345a370255 Mon Sep 17 00:00:00 2001 From: Camilo Polymeris Date: Mon, 9 Dec 2019 22:58:29 -0300 Subject: [PATCH 3466/4033] CLJS-3202: Make :/ keyword hash consistent with = Add special case to keyword fn for the "/" string. --- src/main/cljs/cljs/core.cljs | 1 + src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0ca832fcc..806314528 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3395,6 +3395,7 @@ reduces them without incurring seq initialization" (symbol? name) (Keyword. (cljs.core/namespace name) (cljs.core/name name) (.-str name) nil) + (= "/" name) (Keyword. nil name name nil) (string? name) (let [parts (.split name "/")] (if (== (alength parts) 2) (Keyword. (aget parts 0) (aget parts 1) name nil) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index ceb9d02d7..e2ee44efc 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1803,3 +1803,7 @@ v (apply vector a)] (aset a 0 :modified) (is (= :original (v 0))))) + +(deftest test-cljs-3202 + (is (= :/ (keyword "/"))) + (is (= (hash :/) (hash (keyword "/"))))) \ No newline at end of file From 3b516c69c4972ece23afee930ea4514ea9e8aa23 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 22 Jan 2020 19:38:37 -0500 Subject: [PATCH 3467/4033] http -> https in bootstrap scripts --- script/bootstrap | 2 +- script/bootstrap.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/bootstrap b/script/bootstrap index 2662d91a6..1f07eba91 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -96,7 +96,7 @@ if [ "$1" = "--closure-compiler-snapshot" ] ; then rm $CC_JAR_NAME rm maven-metadata.xml else - curl --retry 3 -O -s http://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v$CLOSURE_RELEASE/closure-compiler-v$CLOSURE_RELEASE.jar || { echo "Download failed."; exit 1; } + curl --retry 3 -O -s https://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v$CLOSURE_RELEASE/closure-compiler-v$CLOSURE_RELEASE.jar || { echo "Download failed."; exit 1; } cp closure-compiler-v$CLOSURE_RELEASE.jar lib/closure-compiler-v$CLOSURE_RELEASE.jar echo "Cleaning up closure-compiler.jar..." rm closure-compiler-v$CLOSURE_RELEASE.jar diff --git a/script/bootstrap.ps1 b/script/bootstrap.ps1 index 3c099bb8f..e2c17563e 100644 --- a/script/bootstrap.ps1 +++ b/script/bootstrap.ps1 @@ -124,7 +124,7 @@ Get-WebResource ` Write-Host "Fetching Google Closure compiler..." Get-WebResource ` - http://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v$CLOSURE_RELEASE/closure-compiler-v$CLOSURE_RELEASE.jar ` + https://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v$CLOSURE_RELEASE/closure-compiler-v$CLOSURE_RELEASE.jar ` $root\closure-compiler-v$CLOSURE_RELEASE.jar Copy-File $root\closure-compiler-v$CLOSURE_RELEASE.jar $root\lib\compiler.jar Delete-File $root\closure-compiler-v$CLOSURE_RELEASE.jar From 7d4325232ae180d367e6764ccf3ea58986f6ef8a Mon Sep 17 00:00:00 2001 From: roman01la Date: Mon, 18 Nov 2019 13:59:27 +0100 Subject: [PATCH 3468/4033] CLJS-3193: Optimize cljs.core/re-pattern --- src/main/cljs/cljs/core.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 806314528..f70109a4c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10022,7 +10022,9 @@ reduces them without incurring seq initialization" (if (instance? js/RegExp s) s (let [[prefix flags] (re-find #"^\(\?([idmsux]*)\)" s) - pattern (subs s (count prefix))] + pattern (subs s (if (nil? prefix) + 0 + (count ^string prefix)))] (js/RegExp. pattern (or flags ""))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Printing ;;;;;;;;;;;;;;;; From bdc5b0b49d7327542fb63b44f6bbc38b11cec781 Mon Sep 17 00:00:00 2001 From: roman01la Date: Mon, 18 Nov 2019 13:28:17 +0100 Subject: [PATCH 3469/4033] CLJS-3192: Optimize cljs.core/re-matches --- src/main/cljs/cljs/core.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f70109a4c..ca046563c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9975,9 +9975,10 @@ reduces them without incurring seq initialization" [re s] (if (string? s) (let [matches (.exec re s)] - (when (= (first matches) s) - (if (== (count matches) 1) - (first matches) + (when (and (not (nil? matches)) + (= (aget matches 0) s)) + (if (== (count ^array matches) 1) + (aget matches 0) (vec matches)))) (throw (js/TypeError. "re-matches must match against a string.")))) From 6583407eb9387971420932d84a4c3fdce5a2a31d Mon Sep 17 00:00:00 2001 From: roman01la Date: Mon, 18 Nov 2019 13:05:55 +0100 Subject: [PATCH 3470/4033] CLJS-3191: Optimize cljs.core/re-find --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ca046563c..70992339c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9992,8 +9992,8 @@ reduces them without incurring seq initialization" (if (string? s) (let [matches (.exec re s)] (when-not (nil? matches) - (if (== (count matches) 1) - (first matches) + (if (== (count ^array matches) 1) + (aget matches 0) (vec matches)))) (throw (js/TypeError. "re-find must match against a string.")))) From 6ab76973ab31033b2307f88a2ebc5ad9ebd5cf3e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 10 Nov 2019 10:15:29 -0500 Subject: [PATCH 3471/4033] CLJS-3185: Facilitate protocol static dispatch inlining --- src/main/clojure/cljs/core.cljc | 52 ++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index e360ea791..a12f431b7 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2061,16 +2061,18 @@ :cljs (js/Error. (core/str "Invalid protocol, " psym " defines method " mname " with arity 0")))))) - expand-sig (core/fn [fname slot sig] - (core/let [sig (core/if-not (every? core/symbol? sig) - (mapv (core/fn [arg] - (core/cond - (core/symbol? arg) arg - (core/and (map? arg) (core/some? (:as arg))) (:as arg) - :else (gensym))) sig) - sig) - - fqn-fname (fqn fname) + sig->syms (core/fn [sig] + (core/if-not (every? core/symbol? sig) + (mapv (core/fn [arg] + (core/cond + (core/symbol? arg) arg + (core/and (map? arg) (core/some? (:as arg))) (:as arg) + :else (gensym))) sig) + sig)) + expand-dyn (core/fn [fname sig] + (core/let [sig (sig->syms sig) + + fqn-fname (with-meta (fqn fname) {:cljs.analyzer/no-resolve true}) fsig (first sig) ;; construct protocol checks in reverse order @@ -2091,20 +2093,25 @@ (meta-impl# ~@sig) ~check)) - ;; then check protocol on js string,function,array,object + ;; then check protocol on js string,function,array,object (first dynamic check actually executed) check `(let [x# (if (nil? ~fsig) nil ~fsig) m# (unchecked-get ~fqn-fname (goog/typeOf x#))] (if-not (nil? m#) (m# ~@sig) - ~check)) + ~check))] + `(~sig ~check))) + expand-sig (core/fn [dyn-name slot sig] + (core/let [sig (sig->syms sig) + + fsig (first sig) - ;; then check protocol property on object (first check actually executed) + ;; check protocol property on object (first check executed) check `(if (and (not (nil? ~fsig)) (not (nil? (. ~fsig ~(symbol (core/str "-" slot)))))) ;; Property access needed here. (. ~fsig ~slot ~@sig) - ~check)] + (~dyn-name ~@sig))] `(~sig ~check))) psym (core/-> psym (vary-meta update-in [:jsdoc] conj "@interface") @@ -2142,15 +2149,20 @@ :protocol-with-overwriting-method {} {:protocol psym :name fname :existing existing}))) slot (symbol (core/str prefix (munge (name fname)))) + dyn-name (symbol (core/str slot "$dyn")) fname (vary-meta fname assoc :protocol p :doc doc)] - `(defn ~fname - ~@(map (core/fn [sig] - (expand-sig fname - (symbol (core/str slot "$arity$" (count sig))) - sig)) - sigs))))] + `(let [~dyn-name (core/fn + ~@(map (core/fn [sig] + (expand-dyn fname sig)) + sigs))] + (defn ~fname + ~@(map (core/fn [sig] + (expand-sig dyn-name + (symbol (core/str slot "$arity$" (count sig))) + sig)) + sigs)))))] `(do (set! ~'*unchecked-if* true) (def ~psym (~'js* "function(){}")) From 470e91eb6b179e70d3947168651b221152c932d1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Jan 2020 11:25:08 -0500 Subject: [PATCH 3472/4033] = -> identical? for the `nodejs` check --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 70992339c..ed0b8101b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11729,5 +11729,5 @@ reduces them without incurring seq initialization" (*eval* form)) (when ^boolean js/COMPILED - (when (= "nodejs" *target*) + (when (identical? "nodejs" *target*) (set! goog/global js/global))) From 5d64c2a142e5b41a4e4390f563d4412502704e23 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Feb 2020 15:37:20 -0500 Subject: [PATCH 3473/4033] CLJS-3209: With clojurescript 1.10.597 HelloWorld compiled with to 94K of JS with advanced optimizations turned on Switch = to identical? in Node.js target check at bottom of core.cljs. Add test case to check for file size regressions. --- project.clj | 2 +- src/test/cljs_build/trivial/core.cljs | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 13 +++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs_build/trivial/core.cljs diff --git a/project.clj b/project.clj index 1341c663d..8cbd62722 100644 --- a/project.clj +++ b/project.clj @@ -7,7 +7,7 @@ :jvm-opts ^:replace ["-Dclojure.compiler.direct-linking=true" "-Xmx512m" "-server"] :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs" "resources"] - :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_cp"] + :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_build" "src/test/cljs_cp"] :dependencies [[org.clojure/clojure "1.10.0-alpha4"] [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] diff --git a/src/test/cljs_build/trivial/core.cljs b/src/test/cljs_build/trivial/core.cljs new file mode 100644 index 000000000..f96fb7748 --- /dev/null +++ b/src/test/cljs_build/trivial/core.cljs @@ -0,0 +1,3 @@ +(ns trivial.core) + +(. js/console (log "Hello!")) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 71021b0cd..5c017d05f 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -703,3 +703,16 @@ sha (string/lower-case (util/content-sha (slurp (io/file f)) 7))] (is (true? (.exists f))) (is (string/includes? (.getPath f) sha)))))))) + +(deftest cljs-3209-trivial-output-size + (let [out (.getPath (io/file (test/tmp-dir) "3209-test-out")) + out-file (io/file out "main.js") + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'trivial.core + :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/core.cljs")) opts cenv) + (is (< (.length out-file) 10000)))) From 3d0dcc8e3f5ee0239028ab65538cf41798cb18db Mon Sep 17 00:00:00 2001 From: Dominic Monroe Date: Tue, 25 Feb 2020 21:06:52 +0000 Subject: [PATCH 3474/4033] CLJS-3213: Browser REPL server hang when receive unknown POST --- src/main/clojure/cljs/repl/server.clj | 29 +++++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index 63d43c71c..98e3dd1bd 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -180,17 +180,24 @@ "text/html")) (defn- dispatch-request [request conn opts] - (if-let [handlers ((:method request) @handlers)] - (if-let [handler - (some (fn [{:keys [pred handler]}] - (when (pred request conn opts) - handler)) - handlers)] - (if (= :post (:method request)) - (handler (read-string (:content request)) conn opts ) - (handler request conn opts)) - (send-404 conn (:path request))) - (.close conn))) + (try + (if-let [handlers ((:method request) @handlers)] + (if-let [handler + (some (fn [{:keys [pred handler]}] + (when (pred request conn opts) + handler)) + handlers)] + (if (= :post (:method request)) + (handler (read-string (:content request)) conn opts ) + (handler request conn opts)) + (send-404 conn (:path request)))) + (catch Throwable t + (try + (send-and-close conn 500 (str "" + "

    Exception while handling

    " + "")) + (catch Throwable _)) + (throw t)))) (defn- handle-connection [opts conn] From ccdc896e1e567c746f42ca760e7d8e843668aaba Mon Sep 17 00:00:00 2001 From: roman01la Date: Fri, 7 Feb 2020 14:40:57 +0100 Subject: [PATCH 3475/4033] CLJS-3210: Single arity arithmetic ops don't warn on bad arguments --- src/main/clojure/cljs/core.cljc | 4 ++-- src/test/clojure/cljs/analyzer_tests.clj | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a12f431b7..d97dc8007 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1069,7 +1069,7 @@ (core/defmacro ^::ana/numeric + ([] 0) - ([x] x) + ([x] (core/list 'js* "(~{})" x)) ([x y] (core/list 'js* "(~{} + ~{})" x y)) ([x y & more] `(+ (+ ~x ~y) ~@more))) @@ -1133,7 +1133,7 @@ (core/defmacro ^::ana/numeric * ([] 1) - ([x] x) + ([x] (core/list 'js* "(~{})" x)) ([x y] (core/list 'js* "(~{} * ~{})" x y)) ([x y & more] `(* (* ~x ~y) ~@more))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 6a4130f95..25181dba7 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -2121,3 +2121,19 @@ (:a (->Foo)))))) (is (= 1 (count @ws))) (is (string/starts-with? (first @ws) "Wrong number of args (0) passed to cljs.user/->Foo")))) + +(deftest test-cljs-3210 + (let [ws (atom [])] + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (analyze ns-env + '(do + (+ "a") + (- "a") + (/ "a") + (* "a")))) + (is (= 4 (count @ws))) + (let [[w1 w2 w3 w4] @ws] + (is (= w1 "cljs.core/+, all arguments must be numbers, got [string] instead")) + (is (= w2 "cljs.core/-, all arguments must be numbers, got [string] instead")) + (is (= w3 "cljs.core//, all arguments must be numbers, got [number string] instead")) + (is (= w4 "cljs.core/*, all arguments must be numbers, got [string] instead"))))) From b76f7f270e921fc3523b91be63982a820c7a04f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 28 Jan 2020 14:52:32 -0500 Subject: [PATCH 3476/4033] bump compiler & library --- deps.edn | 4 ++-- pom.template.xml | 4 ++-- project.clj | 4 ++-- script/bootstrap | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deps.edn b/deps.edn index 07a4e879a..aeb60f3c1 100644 --- a/deps.edn +++ b/deps.edn @@ -7,8 +7,8 @@ org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} com.cognitect/transit-clj {:mvn/version "0.8.309"} - com.google.javascript/closure-compiler-unshaded {:mvn/version "v20180805"} - org.clojure/google-closure-library {:mvn/version "0.0-20170809-b9c14c6b"} + com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200112"} + org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"} org.mozilla/rhino {:mvn/version "1.7R5"}} :aliases {:test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" diff --git a/pom.template.xml b/pom.template.xml index 6fced1126..2068ba005 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,12 +30,12 @@ com.google.javascript closure-compiler-unshaded - v20180805 + v20200112 org.clojure google-closure-library - 0.0-20170809-b9c14c6b + 0.0-20191016-6ae1f72f org.clojure diff --git a/project.clj b/project.clj index 8cbd62722..a28479249 100644 --- a/project.clj +++ b/project.clj @@ -15,8 +15,8 @@ [org.clojure/tools.reader "1.3.2"] [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] - [org.clojure/google-closure-library "0.0-20170809-b9c14c6b"] - [com.google.javascript/closure-compiler-unshaded "v20180805"] + [org.clojure/google-closure-library "0.0-20191016-6ae1f72f"] + [com.google.javascript/closure-compiler-unshaded "v20200112"] [org.mozilla/rhino "1.7R5"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main cljs.main} diff --git a/script/bootstrap b/script/bootstrap index 1f07eba91..aaa2e4872 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,10 +5,10 @@ set -e CLOJURE_RELEASE="1.9.0" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" -CLOSURE_RELEASE="20180805" +CLOSURE_RELEASE="20200112" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" -GCLOSURE_LIB_RELEASE="0.0-20170809-b9c14c6b" +GCLOSURE_LIB_RELEASE="0.0-20191016-6ae1f72f" RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.3.0" TEST_CHECK_RELEASE="0.10.0-alpha3" From da06f55ac2f333215544c41c49c9fe9b582bcdcb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 28 Jan 2020 15:25:34 -0500 Subject: [PATCH 3477/4033] fix up goog-define codegen for recent Closure Compiler release --- src/main/clojure/cljs/analyzer.cljc | 2 + src/main/clojure/cljs/compiler.cljc | 65 +++++++++++++++-------------- src/main/clojure/cljs/core.cljc | 3 +- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index b75c48c76..eb1e4c4b3 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1993,6 +1993,8 @@ :op :var) :doc doc :jsdoc (:jsdoc sym-meta)} + (when-let [goog-type (:goog-define sym-meta)] + {:goog-define goog-type}) (when (true? (:def-emits-var env)) {:var-ast (var-ast env sym)}) (when-some [test (:test sym-meta)] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 6d6bff080..f95051629 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -799,41 +799,44 @@ (pr-str define)))))) (defmethod emit* :def - [{:keys [name var init env doc jsdoc export test var-ast]}] + [{:keys [name var init env doc goog-define jsdoc export test var-ast]}] ;; We only want to emit if an init is supplied, this is to avoid dead code ;; elimination issues. The REPL is the exception to this rule. (when (or init (:def-emits-var env)) (let [mname (munge name)] - (emit-comment env doc (concat jsdoc (:jsdoc init))) - (when (= :return (:context env)) - (emitln "return (")) - (when (:def-emits-var env) - (emitln "(function (){")) - (emits var) - (when init - (emits " = " - (if-let [define (get-define mname jsdoc)] - define - init))) - (when (:def-emits-var env) - (emitln "; return (") - (emits (merge - {:op :the-var - :env (assoc env :context :expr)} - var-ast)) - (emitln ");})()")) - (when (= :return (:context env)) - (emitln ")")) - ;; NOTE: JavaScriptCore does not like this under advanced compilation - ;; this change was primarily for REPL interactions - David - ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") - (when-not (= :expr (:context env)) (emitln ";")) - (when export - (emitln "goog.exportSymbol('" (munge export) "', " mname ");")) - (when (and ana/*load-tests* test) - (when (= :expr (:context env)) - (emitln ";")) - (emitln var ".cljs$lang$test = " test ";"))))) + (emit-comment env doc (concat + (when goog-define + [(str "@define {" goog-define "}")]) + jsdoc (:jsdoc init))) + (when (= :return (:context env)) + (emitln "return (")) + (when (:def-emits-var env) + (emitln "(function (){")) + (emits var) + (when init + (emits " = " + (if-let [define (get-define mname jsdoc)] + define + init))) + (when (:def-emits-var env) + (emitln "; return (") + (emits (merge + {:op :the-var + :env (assoc env :context :expr)} + var-ast)) + (emitln ");})()")) + (when (= :return (:context env)) + (emitln ")")) + ;; NOTE: JavaScriptCore does not like this under advanced compilation + ;; this change was primarily for REPL interactions - David + ;(emits " = (typeof " mname " != 'undefined') ? " mname " : undefined") + (when-not (= :expr (:context env)) (emitln ";")) + (when export + (emitln "goog.exportSymbol('" (munge export) "', " mname ");")) + (when (and ana/*load-tests* test) + (when (= :expr (:context env)) + (emitln ";")) + (emitln var ".cljs$lang$test = " test ";"))))) (defn emit-apply-to [{:keys [name params env]}] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index d97dc8007..3fdd62d44 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -762,8 +762,7 @@ (core/not (core/contains? m :tag)) (core/assoc :tag (core/symbol type)) )))) - (~'js* ~(core/str "/** @define {" type "} */")) - (goog/define ~defname ~default)))) + (def ~(vary-meta sym assoc :goog-define type) (goog/define ~defname ~default))))) (core/defmacro let "binding => binding-form init-expr From 6602af56af975872b9e0f59840ca041d0bac1c7c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 28 Jan 2020 15:41:15 -0500 Subject: [PATCH 3478/4033] removed diagnostics from latest Closure release --- src/main/clojure/cljs/closure.clj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ee04d5a7f..982e6ab6d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -132,9 +132,7 @@ (def warning-types {:access-controls DiagnosticGroups/ACCESS_CONTROLS - :ambiguous-function-decl DiagnosticGroups/AMBIGUOUS_FUNCTION_DECL :analyzer-checks DiagnosticGroups/ANALYZER_CHECKS - :check-eventful-object-disposal DiagnosticGroups/CHECK_EVENTFUL_OBJECT_DISPOSAL :check-regexp DiagnosticGroups/CHECK_REGEXP :check-types DiagnosticGroups/CHECK_TYPES :check-useless-code DiagnosticGroups/CHECK_USELESS_CODE @@ -148,14 +146,11 @@ :deprecated-annotations DiagnosticGroups/DEPRECATED_ANNOTATIONS :duplicate-message DiagnosticGroups/DUPLICATE_MESSAGE :duplicate-vars DiagnosticGroups/DUPLICATE_VARS - :es3 DiagnosticGroups/ES3 :es5-strict DiagnosticGroups/ES5_STRICT :externs-validation DiagnosticGroups/EXTERNS_VALIDATION :extra-require DiagnosticGroups/EXTRA_REQUIRE - :fileoverview-jsdoc DiagnosticGroups/FILEOVERVIEW_JSDOC :function-params DiagnosticGroups/FUNCTION_PARAMS :global-this DiagnosticGroups/GLOBAL_THIS - :internet-explorer-checks DiagnosticGroups/INTERNET_EXPLORER_CHECKS :invalid-casts DiagnosticGroups/INVALID_CASTS :j2cl-checks DiagnosticGroups/J2CL_CHECKS :jsdoc-missing-type DiagnosticGroups/JSDOC_MISSING_TYPE From 7f50a025579c57887821faacdaf805df24965c63 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 28 Jan 2020 15:42:40 -0500 Subject: [PATCH 3479/4033] change export convention so that Closure can detect it test-cljs-2286 & test-global-exports failing --- src/test/cljs/calculator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/cljs/calculator.js b/src/test/cljs/calculator.js index c69a6960c..559413dd3 100644 --- a/src/test/cljs/calculator.js +++ b/src/test/cljs/calculator.js @@ -7,4 +7,4 @@ var calculator = { } }; -module.exports = calculator; +exports = calculator; From 523ef83661c9f83e409098131666592314820802 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 29 Jan 2020 11:57:39 -0500 Subject: [PATCH 3480/4033] revert change to calculator.js, invert the order of processing - es6 first, then commonjs --- src/main/clojure/cljs/closure.clj | 4 ++-- src/test/cljs/calculator.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 982e6ab6d..1364b50a8 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1989,11 +1989,11 @@ _ (report-failure (.getResult closure-compiler)) inputs-by-name (into {} (map (juxt #(.getName %) identity) (vals (.getInputsById closure-compiler))))] - ;; This will rewrite CommonJS modules - (.whitespaceOnlyPasses closure-compiler) ;; This will take care of converting ES6 to CJS ;; Based on language-in setting, this could also handle ES7/8/TypeScript transpilation. (.transpileAndDontCheck closure-compiler) + ;; This will rewrite CommonJS modules + (.whitespaceOnlyPasses closure-compiler) (map (partial add-converted-source closure-compiler inputs-by-name opts) diff --git a/src/test/cljs/calculator.js b/src/test/cljs/calculator.js index 559413dd3..c69a6960c 100644 --- a/src/test/cljs/calculator.js +++ b/src/test/cljs/calculator.js @@ -7,4 +7,4 @@ var calculator = { } }; -exports = calculator; +module.exports = calculator; From ca80dad497785c8e950ccb970e8840f5853265de Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jan 2020 14:36:50 -0500 Subject: [PATCH 3481/4033] update test build config to :language-in :es6 for update Closure Library --- src/test/clojure/cljs/test_util.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index d1f306375..97dd62ee7 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -45,6 +45,7 @@ :output-dir output-dir :optimizations :advanced :verbose true + :language-in :es6 :modules {:cljs-base {:output-to (str (io/file output-dir "module-main.js"))} From 9ef73dac7a982a5c6fa10f043fac2924fd8bcbf3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jan 2020 15:09:14 -0500 Subject: [PATCH 3482/4033] add missing :language-in, change the regex string assertion for changes to module rewriting --- src/test/clojure/cljs/build_api_tests.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 5c017d05f..3a2fc7df9 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -126,6 +126,7 @@ ["common" "app"]) opts {:optimizations :simple :output-dir out + :language-in :es6 :modules {:common {:entries #{"hello.foo.bar"} :output-to (.getAbsolutePath common-tmp)} :app {:entries #{"hello.core"} @@ -582,7 +583,7 @@ ;; assert Closure finds and processes the left-pad dep in node_modules ;; if it can't be found the require will be issued to module$left_pad ;; so we assert it's of the form module$path$to$node_modules$left_pad$index - (is (re-find #"module\$.*\$node_modules\$left_pad\$index\[\"default\"\]\(42,5,0\)" (slurp foreign-lib-file)))) + (is (re-find #"module\$.*\$node_modules\$left_pad\$index\[\"default\"\]\)\(42,5,0\)" (slurp foreign-lib-file)))) (test/delete-out-files out) (test/delete-node-modules))) @@ -676,6 +677,7 @@ (let [out (io/file (test/tmp-dir) "cljs-2903-out") opts {:output-to (.getPath (io/file out "main.js")) :output-dir (.getPath out) + :language-in :es6 :fingerprint true :stable-names true :optimizations :advanced}] From a8688ddbf684e802c8d0004ce4d9e2ec4ef89396 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 29 Jan 2020 16:17:38 -0500 Subject: [PATCH 3483/4033] bundler transpilation related imports --- src/main/clojure/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1364b50a8..64af48008 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -23,7 +23,7 @@ [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers] [cljs.module-graph :as module-graph]) - (:import [java.lang ProcessBuilder] + (:import [java.lang ProcessBuilder StringBuilder] [java.io File BufferedInputStream BufferedReader Writer InputStreamReader IOException StringWriter ByteArrayInputStream] @@ -39,7 +39,9 @@ Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy JSModule SourceMap VariableMap] - [com.google.javascript.jscomp.deps ModuleLoader$ResolutionMode ModuleNames] + [com.google.javascript.jscomp.transpile BaseTranspiler] + [com.google.javascript.jscomp.deps ClosureBundler + ModuleLoader$ResolutionMode ModuleNames SimpleDependencyInfo] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult] From 79271ccf646302717cde6e1bef378354bf3f3e13 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 18 Mar 2020 16:46:00 -0400 Subject: [PATCH 3484/4033] Fix Node.js REPL. Make reusable goog.require monkey patch for REPL environments. This goog.require preserves the old require and handles the reloading logic. We need to use the original require because of module handling and conditional transpilation. If we monkey patch these modules can't be loaded on the fly. We may just compile these AOT later depending on what Closure does with the debug loader. Consistently use double quote over single quote in bootstrap_node.js. goog.CLOSURE_LOAD_FILE_SYNC path wasn't being rewritten in bootstrap_node.js when bootstrapping the Node.js environment. Remove the blind monkey patching of goog.require in Node REPL, use the new cljs.repl.bootstrap/install-repl-goog-require helper. --- src/main/cljs/cljs/bootstrap_nodejs.js | 4 +-- src/main/clojure/cljs/repl/bootstrap.clj | 41 ++++++++++++++++++++++++ src/main/clojure/cljs/repl/node.clj | 27 +++------------- 3 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 src/main/clojure/cljs/repl/bootstrap.clj diff --git a/src/main/cljs/cljs/bootstrap_nodejs.js b/src/main/cljs/cljs/bootstrap_nodejs.js index b96ce400c..a1cbd771c 100644 --- a/src/main/cljs/cljs/bootstrap_nodejs.js +++ b/src/main/cljs/cljs/bootstrap_nodejs.js @@ -102,7 +102,7 @@ global.CLOSURE_IMPORT_SCRIPT = function(src, opt_sourceText) { */ global.CLOSURE_LOAD_FILE_SYNC = function(src) { return fs.readFileSync( - path.resolve(__dirname, '..', src), {encoding: 'utf-8'}); + path.resolve(__dirname, "..", src), {encoding: "utf-8"}); }; @@ -137,7 +137,7 @@ function nodeGlobalRequire(file) { // Load Closure's base.js into memory. It is assumed base.js is in the // directory above this directory given this script's location in // bootstrap/nodejs.js. -nodeGlobalRequire(path.resolve(__dirname, '..', 'base.js')); +nodeGlobalRequire(path.resolve(__dirname, "..", "base.js")); /** diff --git a/src/main/clojure/cljs/repl/bootstrap.clj b/src/main/clojure/cljs/repl/bootstrap.clj new file mode 100644 index 000000000..1d7a8c39e --- /dev/null +++ b/src/main/clojure/cljs/repl/bootstrap.clj @@ -0,0 +1,41 @@ +;; 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.repl.bootstrap + (:require [cljs.repl :as repl])) + +(defn install-repl-goog-require + "Install a version of goog.require that supports namespace reloading. + IMPORTANT: must be invoked *after* loading cljs.core." + [repl-env env] + (repl/evaluate-form repl-env env "" + '(set! (.-require__ js/goog) js/goog.require)) + ;; monkey-patch goog.require + (repl/evaluate-form repl-env env "" + '(set! (.-require js/goog) + (fn [src reload] + (when (= reload "reload-all") + (set! (.-cljsReloadAll_ js/goog) true)) + (let [reload? (or reload (.-cljsReloadAll__ js/goog))] + (when reload? + ;; check for new-ish private goog/debugLoader + (if (some? goog/debugLoader_) + (let [path (.getPathFromDeps_ goog/debugLoader_ name)] + (goog.object/remove (.-written_ goog/debugLoader_) path) + (goog.object/remove (.-written_ goog/debugLoader_) + (str js/goog.basePath path))) + ;; legacy approach + (let [path (goog.object/get js/goog.dependencies_.nameToPath src)] + (goog.object/remove js/goog.dependencies_.visited path) + (goog.object/remove js/goog.dependencies_.written path) + (goog.object/remove js/goog.dependencies_.written + (str js/goog.basePath path))))) + (let [ret (.require__ js/goog src)] + (when (= reload "reload-all") + (set! (.-cljsReloadAll_ js/goog) false)) + ret)))))) \ No newline at end of file diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index 363880ec7..b6319cc3f 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -13,6 +13,7 @@ [cljs.analyzer :as ana] [cljs.compiler :as comp] [cljs.repl :as repl] + [cljs.repl.bootstrap :as bootstrap] [cljs.cli :as cli] [cljs.closure :as closure] [clojure.data.json :as json]) @@ -176,11 +177,13 @@ ;; properly due to how we are running it - David (node-eval repl-env (-> (slurp (io/resource "cljs/bootstrap_nodejs.js")) - (string/replace "path.resolve(__dirname, '..', 'base.js')" + (string/replace "path.resolve(__dirname, \"..\", \"base.js\")" (platform-path (conj rewrite-path "bootstrap" ".." "base.js"))) (string/replace "path.join(\".\", \"..\", src)" (str "path.join(" (platform-path rewrite-path) ", src)")) + (string/replace "path.resolve(__dirname, \"..\", src)" + (str "path.join(" (platform-path rewrite-path) ", src)")) (string/replace "var CLJS_ROOT = \".\";" (str "var CLJS_ROOT = " (platform-path root-path) ";")))) @@ -192,32 +195,12 @@ ;; monkey-patch isProvided_ to avoid useless warnings - David (node-eval repl-env (str "goog.isProvided_ = function(x) { return false; };")) - ;; monkey-patch goog.require, skip all the loaded checks - (repl/evaluate-form repl-env env "" - '(set! (.-require js/goog) - (fn [name] - (js/CLOSURE_IMPORT_SCRIPT - (if (some? goog/debugLoader_) - (.getPathFromDeps_ goog/debugLoader_ name) - (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))) ;; load cljs.core, setup printing (repl/evaluate-form repl-env env "" '(do (.require js/goog "cljs.core") (enable-console-print!))) - ;; redef goog.require to track loaded libs - (repl/evaluate-form repl-env env "" - '(do - (set! *target* "nodejs") - (set! *loaded-libs* #{"cljs.core"}) - (set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (if (some? goog/debugLoader_) - (.getPathFromDeps_ goog/debugLoader_ name) - (unchecked-get (.. js/goog -dependencies_ -nameToPath) name)))))))) + (bootstrap/install-repl-goog-require repl-env env) (node-eval repl-env (str "goog.global.CLOSURE_UNCOMPILED_DEFINES = " (json/write-str (:closure-defines opts)) ";"))))) From e4300da64c4781735146cafc0ca029046b83944c Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 19 Mar 2020 21:00:45 -0400 Subject: [PATCH 3485/4033] need to set :language-in to :es6 on the trivial build size test --- src/test/clojure/cljs/build_api_tests.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 3a2fc7df9..99f1b7baa 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -711,6 +711,7 @@ out-file (io/file out "main.js") {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) :opts {:main 'trivial.core + :language-in :es6 :output-dir out :output-to (.getPath out-file) :optimizations :advanced}} From 747de66411122be00fa145a04d9741722a35c1c5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 19 Mar 2020 21:03:25 -0400 Subject: [PATCH 3486/4033] Remove Rhino, Nashorn, Graal REPL support - these can be developed elsewhere. After getting these working it's pretty clear we have a sufficient API to support such efforts but removing this relieves a lot maintenance burden given that Closure changes with relative frequency and will likely continue to. --- src/main/cljs/cljs/bootstrap_graaljs.js | 75 ------- src/main/cljs/cljs/bootstrap_nashorn.js | 72 ------ src/main/clojure/cljs/repl/graaljs.clj | 190 ---------------- src/main/clojure/cljs/repl/nashorn.clj | 190 ---------------- src/main/clojure/cljs/repl/rhino.clj | 272 ----------------------- src/main/clojure/cljs/server/nashorn.clj | 27 --- src/main/clojure/cljs/server/rhino.clj | 27 --- src/test/clojure/cljs/repl_tests.clj | 1 - 8 files changed, 854 deletions(-) delete mode 100644 src/main/cljs/cljs/bootstrap_graaljs.js delete mode 100644 src/main/cljs/cljs/bootstrap_nashorn.js delete mode 100644 src/main/clojure/cljs/repl/graaljs.clj delete mode 100644 src/main/clojure/cljs/repl/nashorn.clj delete mode 100644 src/main/clojure/cljs/repl/rhino.clj delete mode 100644 src/main/clojure/cljs/server/nashorn.clj delete mode 100644 src/main/clojure/cljs/server/rhino.clj diff --git a/src/main/cljs/cljs/bootstrap_graaljs.js b/src/main/cljs/cljs/bootstrap_graaljs.js deleted file mode 100644 index 4db6faadb..000000000 --- a/src/main/cljs/cljs/bootstrap_graaljs.js +++ /dev/null @@ -1,75 +0,0 @@ -var global = this; // required by React - -var graaljs_load = function(path) { - var File = Java.type("java.io.File"); - var outputPath = (typeof CLJS_OUTPUT_DIR != "undefined" ? CLJS_OUTPUT_DIR : ".") + File.separator + path; - if (typeof CLJS_DEBUG != "undefined" && CLJS_DEBUG) print("loading:" + outputPath); - load(outputPath); -}; - -goog.global.CLOSURE_IMPORT_SCRIPT = function(path) { - graaljs_load("goog/" + path); - return true; -}; - -goog.global.isProvided_ = function(name) { return false; }; - -var __executors = Java.type("java.util.concurrent.Executors"); -var __executorService = __executors.newScheduledThreadPool(0); -__executorService.setMaximumPoolSize(1); -var __millis = Java.type("java.util.concurrent.TimeUnit").valueOf("MILLISECONDS"); - -var graaljs_tear_down = function() { - __executorService.shutdown(); -} - -function setTimerRequest(handler, delay, interval, args) { - handler = handler || function() {}; - delay = delay || 0; - interval = interval || 0; - var voidType = Java.type("java.lang.Void").TYPE; - var applyHandler = __executors.callable(function() { handler.apply(this, args); }, voidType); - if (interval > 0) { - return __executorService.scheduleWithFixedDelay(applyHandler, delay, interval, __millis); - } else { - return __executorService.schedule(applyHandler, delay, __millis); - }; -} - -function clearTimerRequest(future) { - future.cancel(false); -} - -function setInterval() { - var args = Array.prototype.slice.call(arguments); - var handler = args.shift(); - var ms = args.shift(); - return setTimerRequest(handler, ms, ms, args); -} - -function clearInterval(future) { - clearTimerRequest(future); -} - -function setTimeout() { - var args = Array.prototype.slice.call(arguments); - var handler = args.shift(); - var ms = args.shift(); - - return setTimerRequest(handler, ms, 0, args); -} - -function clearTimeout(future) { - clearTimerRequest(future); -} - -function setImmediate() { - var args = Array.prototype.slice.call(arguments); - var handler = args.shift(); - - return setTimerRequest(handler, 0, 0, args); -} - -function clearImmediate(future) { - clearTimerRequest(future); -} diff --git a/src/main/cljs/cljs/bootstrap_nashorn.js b/src/main/cljs/cljs/bootstrap_nashorn.js deleted file mode 100644 index 19d087d50..000000000 --- a/src/main/cljs/cljs/bootstrap_nashorn.js +++ /dev/null @@ -1,72 +0,0 @@ -var global = this; // required by React - -var nashorn_load = function(path) { - var outputPath = (typeof CLJS_OUTPUT_DIR != "undefined" ? CLJS_OUTPUT_DIR : ".") + java.io.File.separator + path; - if (typeof CLJS_DEBUG != "undefined" && CLJS_DEBUG) print("loading:" + outputPath); - load(outputPath); -}; - -goog.global.CLOSURE_IMPORT_SCRIPT = function(path) { - nashorn_load("goog/" + path); - return true; -}; - -goog.global.isProvided_ = function(name) { return false; }; - -var __executorService = Java.type("java.util.concurrent.Executors").newScheduledThreadPool(0); -__executorService.setMaximumPoolSize(1); -var __millis = Java.type("java.util.concurrent.TimeUnit").valueOf("MILLISECONDS"); - -var nashorn_tear_down = function() { - __executorService.shutdown(); -} - -function setTimerRequest(handler, delay, interval, args) { - handler = handler || function() {}; - delay = delay || 0; - interval = interval || 0; - var applyHandler = function() { handler.apply(this, args); } - if (interval > 0) { - return __executorService.scheduleWithFixedDelay(applyHandler, delay, interval, __millis); - } else { - return __executorService["schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit)"](applyHandler, delay, __millis); - }; -} - -function clearTimerRequest(future) { - future.cancel(false); -} - -function setInterval() { - var args = Array.prototype.slice.call(arguments); - var handler = args.shift(); - var ms = args.shift(); - return setTimerRequest(handler, ms, ms, args); -} - -function clearInterval(future) { - clearTimerRequest(future); -} - -function setTimeout() { - var args = Array.prototype.slice.call(arguments); - var handler = args.shift(); - var ms = args.shift(); - - return setTimerRequest(handler, ms, 0, args); -} - -function clearTimeout(future) { - clearTimerRequest(future); -} - -function setImmediate() { - var args = Array.prototype.slice.call(arguments); - var handler = args.shift(); - - return setTimerRequest(handler, 0, 0, args); -} - -function clearImmediate(future) { - clearTimerRequest(future); -} diff --git a/src/main/clojure/cljs/repl/graaljs.clj b/src/main/clojure/cljs/repl/graaljs.clj deleted file mode 100644 index 57b8f07b5..000000000 --- a/src/main/clojure/cljs/repl/graaljs.clj +++ /dev/null @@ -1,190 +0,0 @@ -;; 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.repl.graaljs - (:require [clojure.java.io :as io] - [clojure.string :as string] - [clojure.stacktrace] - [clojure.data.json :as json] - [cljs.analyzer :as ana] - [cljs.env :as env] - [cljs.util :as util] - [cljs.repl :as repl] - [cljs.cli :as cli] - [cljs.compiler :as comp] - [cljs.closure :as closure] - [cljs.stacktrace :as st]) - (:import [javax.script ScriptEngine ScriptException])) - -(defn- js-opt-key? [k] - (and (string? k) - (string/starts-with? k "js."))) - -(defn- form-js-opts [opts] - (for [[k v] opts - :when (js-opt-key? k)] - `(.option ~k ~v))) - -(defn create-engine [opts] - ;; In order to support AOT compilation by JVMs that don't have - ;; GraalVM available, we load and execute engine creation code - ;; here at runtime. - (import '(com.oracle.truffle.js.scriptengine GraalJSScriptEngine)) - (import '(org.graalvm.polyglot Context)) - (let [engine (eval `(GraalJSScriptEngine/create nil - (-> (Context/newBuilder (make-array String 0)) - ~@(form-js-opts opts) - (.allowAllAccess true) - (.allowNativeAccess true)))) - context (.getContext engine)] - (.setWriter context *out*) - (.setErrorWriter context *err*) - engine)) - -(defn eval-str [^ScriptEngine engine ^String s] - (.eval engine s)) - -(defn eval-resource - "Evaluate a file on the classpath in the engine." - [engine path debug] - (let [r (io/resource path)] - (eval-str engine (slurp r)) - (when debug (println "loaded: " path)))) - -(defn init-engine [engine {:keys [output-dir] :as opts} debug] - (eval-str engine (format "var CLJS_DEBUG = %s;" (boolean debug))) - (eval-str engine (format "var CLJS_OUTPUT_DIR = \"%s\";" output-dir)) - (eval-resource engine "goog/base.js" debug) - (eval-resource engine "goog/deps.js" debug) - (eval-resource engine "cljs/bootstrap_graaljs.js" debug) - (eval-str engine - (format "goog.global.CLOSURE_UNCOMPILED_DEFINES = %s;" - (json/write-str (:closure-defines opts)))) - engine) - -(defn tear-down-engine [engine] - (eval-str engine "graaljs_tear_down();")) - -(defn load-js-file [engine file] - (eval-str engine (format "graaljs_load(\"%s\");" file))) - -;; Create a minimal build of ClojureScript from the core library. -;; Copied from clj.cljs.repl.node. -(defn bootstrap-repl [engine output-dir opts] - (env/ensure - (let [deps-file ".graaljs_repl_deps.js" - core (io/resource "cljs/core.cljs") - core-js (closure/compile core - (assoc opts :output-file - (closure/src-file->target-file - core (dissoc opts :output-dir)))) - deps (closure/add-dependencies opts core-js)] - ;; output unoptimized code and the deps file - ;; for all compiled namespaces - (apply closure/output-unoptimized - (assoc opts :output-to (.getPath (io/file output-dir deps-file))) - deps) - ;; load the deps file so we can goog.require cljs.core etc. - (load-js-file engine deps-file)))) - -(defn load-ns [engine ns] - (eval-str engine - (format "goog.require(\"%s\");" (comp/munge (first ns))))) - -(def repl-filename "") - -(def ^:private skip-types #{"com.oracle.truffle.api.interop.java.TruffleMap" - "com.oracle.truffle.api.interop.java.TruffleMap$FunctionTruffleMap"}) - -(defn- safe-to-string - "A safe version that avoids calling .toString on types known to cause stack overflow. - Also has a guard to return an unreadable containing the type if this is encountered." - [x] - (let [type-str (pr-str (type x))] - (try - (if (contains? skip-types type-str) - (str #"<" type-str ">") - (.toString x)) - (catch StackOverflowError _ - (str "#"))))) - -(defrecord GraalJSEnv [engine debug] - repl/IReplEnvOptions - (-repl-options [this] - {:output-dir ".cljs_graaljs_repl" - :target :graaljs}) - repl/IJavaScriptEnv - (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] - (init-engine engine opts debug) - (let [env (ana/empty-env)] - (if output-to - (load-js-file engine output-to) - (bootstrap-repl engine output-dir opts)) - (repl/evaluate-form this env repl-filename - '(.require js/goog "cljs.core")) - ;; monkey-patch goog.isProvided_ to suppress useless errors - (repl/evaluate-form this env repl-filename - '(set! js/goog.isProvided_ (fn [ns] false))) - ;; monkey-patch goog.require to be more sensible - (repl/evaluate-form this env repl-filename - '(do - (set! *loaded-libs* #{"cljs.core"}) - (set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (if (some? goog/debugLoader_) - (.getPathFromDeps_ goog/debugLoader_ name) - (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))))) - (-evaluate [{engine :engine :as this} filename line js] - (when debug (println "Evaluating: " js)) - (try - {:status :success - :value (if-let [r (eval-str engine js)] (safe-to-string r) "")} - (catch ScriptException e - {:status :exception - :value (eval-str engine "cljs.repl.error__GT_str(cljs.core._STAR_e)")}) - (catch Throwable e - (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] - {:status :exception - :value (cljs.repl/ex-str (cljs.repl/ex-triage (Throwable->map root-cause)))})))) - (-load [{engine :engine :as this} ns url] - (load-ns engine ns)) - (-tear-down [this] - (tear-down-engine engine)) - repl/IParseStacktrace - (-parse-stacktrace [this frames-str ret opts] - (st/parse-stacktrace this frames-str - (assoc ret :ua-product :graaljs) opts)) - repl/IParseError - (-parse-error [_ err _] - (update-in err [:stacktrace] - (fn [st] - (string/join "\n" (drop 1 (string/split st #"\n"))))))) - -(def ^:private default-js-opts - {"js.timer-resolution" "1"}) - -(defn repl-env* [{:keys [debug] :as opts}] - (let [opts (merge default-js-opts opts) - engine (create-engine opts)] - (merge - (GraalJSEnv. engine debug) - opts))) - -(defn repl-env - "Create a Graal.JS repl-env for use with the repl/repl* method in ClojureScript." - [& {:as opts}] - (repl-env* opts)) - -;; ------------------------------------------------------------------------- -;; Command Line Support - -(defn -main [& args] - (apply cli/main repl-env args)) diff --git a/src/main/clojure/cljs/repl/nashorn.clj b/src/main/clojure/cljs/repl/nashorn.clj deleted file mode 100644 index 467c72ac5..000000000 --- a/src/main/clojure/cljs/repl/nashorn.clj +++ /dev/null @@ -1,190 +0,0 @@ -;; 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.repl.nashorn - (:require [clojure.java.io :as io] - [clojure.string :as string] - [clojure.stacktrace] - [clojure.data.json :as json] - [cljs.analyzer :as ana] - [cljs.env :as env] - [cljs.util :as util] - [cljs.repl :as repl] - [cljs.cli :as cli] - [cljs.compiler :as comp] - [cljs.closure :as closure] - [cljs.stacktrace :as st]) - (:import [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory])) - -(util/compile-if (Class/forName "jdk.nashorn.api.scripting.NashornException") - (do - (import 'jdk.nashorn.api.scripting.NashornException) - ;; Implementation - - (defn create-engine - ([] (create-engine nil)) - ([{:keys [code-cache] :or {code-cache true}}] - (let [args (when code-cache ["-pcc"]) - factories (.getEngineFactories (ScriptEngineManager.)) - factory (get (zipmap (map #(.getEngineName %) factories) factories) "Oracle Nashorn")] - (if-let [engine (if-not (empty? args) - (.getScriptEngine ^ScriptEngineFactory factory (into-array args)) - (.getScriptEngine ^ScriptEngineFactory factory))] - (let [context (.getContext engine)] - (.setWriter context *out*) - (.setErrorWriter context *err*) - engine) - (throw (IllegalArgumentException. - "Cannot find the Nashorn script engine, use a JDK version 8 or higher.")))))) - - (defn eval-str [^ScriptEngine engine ^String s] - (.eval engine s)) - - (defn eval-resource - "Evaluate a file on the classpath in the engine." - [engine path debug] - (let [r (io/resource path)] - (eval-str engine (slurp r)) - (when debug (println "loaded: " path)))) - - (defn init-engine [engine {:keys [output-dir] :as opts} debug] - (eval-str engine (format "var CLJS_DEBUG = %s;" (boolean debug))) - (eval-str engine (format "var CLJS_OUTPUT_DIR = \"%s\";" output-dir)) - (eval-resource engine "goog/base.js" debug) - (eval-resource engine "goog/deps.js" debug) - (eval-resource engine "cljs/bootstrap_nashorn.js" debug) - (eval-str engine - (format "goog.global.CLOSURE_UNCOMPILED_DEFINES = %s;" - (json/write-str (:closure-defines opts)))) - engine) - - (defn tear-down-engine [engine] - (eval-str engine "nashorn_tear_down();")) - - (defn load-js-file [engine file] - (eval-str engine (format "nashorn_load(\"%s\");" file))) - - ;; Create a minimal build of Clojurescript from the core library. - ;; Copied from clj.cljs.repl.node. - (defn bootstrap-repl [engine output-dir opts] - (env/ensure - (let [deps-file ".nashorn_repl_deps.js" - core (io/resource "cljs/core.cljs") - core-js (closure/compile core - (assoc opts :output-file - (closure/src-file->target-file - core (dissoc opts :output-dir)))) - deps (closure/add-dependencies opts core-js)] - ;; output unoptimized code and the deps file - ;; for all compiled namespaces - (apply closure/output-unoptimized - (assoc opts :output-to (.getPath (io/file output-dir deps-file))) - deps) - ;; load the deps file so we can goog.require cljs.core etc. - (load-js-file engine deps-file)))) - - (defn load-ns [engine ns] - (eval-str engine - (format "goog.require(\"%s\");" (comp/munge (first ns))))) - - ;; Nashorn script stacktraces have a relative path which includes the output-dir - (defn- strip-file-name [^String file-name output-dir] - (let [with-slash (str output-dir "/")] - (if (.startsWith file-name with-slash) - (string/replace-first file-name with-slash "") - file-name))) - - (def repl-filename "") - - (defrecord NashornEnv [engine debug] - repl/IReplEnvOptions - (-repl-options [this] - {:output-dir ".cljs_nashorn_repl" - :target :nashorn}) - repl/IJavaScriptEnv - (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] - (init-engine engine opts debug) - (let [env (ana/empty-env)] - (if output-to - (load-js-file engine output-to) - (bootstrap-repl engine output-dir opts)) - (repl/evaluate-form this env repl-filename - '(.require js/goog "cljs.core")) - ;; monkey-patch goog.isProvided_ to suppress useless errors - (repl/evaluate-form this env repl-filename - '(set! js/goog.isProvided_ (fn [ns] false))) - ;; monkey-patch goog.require to be more sensible - (repl/evaluate-form this env repl-filename - '(do - (set! *loaded-libs* #{"cljs.core"}) - (set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (if (some? goog/debugLoader_) - (.getPathFromDeps_ goog/debugLoader_ name) - (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))))) - (-evaluate [{engine :engine :as this} filename line js] - (when debug (println "Evaluating: " js)) - (try - {:status :success - :value (if-let [r (eval-str engine js)] (.toString r) "")} - (catch ScriptException e - (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] - {:status :exception - :value (eval-str engine "cljs.repl.error__GT_str(cljs.core._STAR_e)")})) - (catch Throwable e - (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] - {:status :exception - :value (cljs.repl/ex-str (cljs.repl/ex-triage (Throwable->map root-cause)))})))) - (-load [{engine :engine :as this} ns url] - (load-ns engine ns)) - (-tear-down [this] - (tear-down-engine engine)) - repl/IParseStacktrace - (-parse-stacktrace [this frames-str ret opts] - (st/parse-stacktrace this frames-str - (assoc ret :ua-product :nashorn) opts)) - repl/IParseError - (-parse-error [_ err _] - (update-in err [:stacktrace] - (fn [st] - (string/join "\n" (drop 1 (string/split st #"\n"))))))) - - (defn repl-env* [{:keys [debug] :as opts}] - (let [engine (create-engine opts)] - (merge - (NashornEnv. engine debug) - opts))) - - (defn repl-env - "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." - [& {:as opts}] - (repl-env* opts)) - - ;; ------------------------------------------------------------------------- - ;; Command Line Support - - (defn -main [& args] - (apply cli/main repl-env args))) - - (do - (defn repl-env* [{:keys [debug] :as opts}] - (throw (ex-info "Nashorn not supported" {:type :repl-error}))) - - (defn repl-env - "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." - [& {:as opts}] - (throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error}))) - - (defn -main [] - (throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error}))))) - - - diff --git a/src/main/clojure/cljs/repl/rhino.clj b/src/main/clojure/cljs/repl/rhino.clj deleted file mode 100644 index 7845938cc..000000000 --- a/src/main/clojure/cljs/repl/rhino.clj +++ /dev/null @@ -1,272 +0,0 @@ -;; 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.repl.rhino - (:refer-clojure :exclude [load-file]) - (:require [clojure.string :as string] - [clojure.java.io :as io] - [clojure.data.json :as json] - [cljs.compiler :as comp] - [cljs.closure :as closure] - [cljs.analyzer :as ana] - [cljs.repl :as repl] - [cljs.cli :as cli] - [cljs.util :as util] - [cljs.stacktrace :as st]) - (:import [java.io File Reader] - [org.mozilla.javascript Context ScriptableObject - RhinoException Undefined])) - -(def ^String bootjs - (str "var global = this;\n" - "var CLOSURE_IMPORT_SCRIPT = function(src) {\n" - " var ns = \"cljs.repl.rhino\"," - " name = \"load-file\"," - " loadFile = Packages.clojure.lang.RT[\"var\"](ns,name);\n" - " if(src) loadFile.invoke(___repl_env, __repl_opts, src);\n" - " return true;\n" - "};\n")) - -;; ============================================================================= -;; Protocols - -(defprotocol IEval - (-eval [this env filename line])) - -(extend-protocol IEval - String - (-eval [this {:keys [cx scope]} filename line] - (.evaluateString cx scope this filename line nil)) - - Reader - (-eval [this {:keys [cx scope]} filename line] - (.evaluateReader cx scope this filename line nil))) - -;; ============================================================================= -;; Stacktrace & eval support - -(defmulti stacktrace class) - -(defmethod stacktrace :default [e] - (apply str - (interpose "\n" - (map #(str " " (.toString %)) - (.getStackTrace e))))) - -(defmethod stacktrace RhinoException [^RhinoException e] - (.getScriptStackTrace e)) - -(defmulti eval-result class) - -(defmethod eval-result :default [r] - (.toString r)) - -(defmethod eval-result nil [_] "") - -(defmethod eval-result Undefined [_] "") - -;; ============================================================================= - -(defn rhino-eval - [{:keys [scope] :as repl-env} filename line js] - (try - (let [linenum (or line Integer/MIN_VALUE)] - {:status :success - :value (eval-result (-eval js repl-env filename linenum))}) - (catch Throwable ex - ;; manually set *e - (let [top-level (-> scope - (ScriptableObject/getProperty "cljs") - (ScriptableObject/getProperty "core"))] - (ScriptableObject/putProperty top-level "_STAR_e" - (Context/javaToJS ex scope)) - {:status :exception - :value (cljs.repl/ex-str (cljs.repl/ex-triage (Throwable->map ex)))})))) - -(defn load-file - "Load a JavaScript. This is needed to load JavaScript files before the Rhino - environment is bootstrapped. After bootstrapping load-javascript will be - used." - [repl-env opts src] - (let [goog-path (io/file (util/output-directory opts) "goog" src)] - (rhino-eval repl-env (.getPath goog-path) 1 (io/reader goog-path)))) - -(defn load-javascript [repl-env ns url] - (try - (with-open [reader (io/reader url)] - (-eval reader repl-env (.toString url) 1)) - ;; TODO: don't show errors for goog/base.js line number 105 - (catch Throwable ex (println (.getMessage ex))))) - -(defn rhino-setup [repl-env opts] - (let [scope (:scope repl-env) - env (ana/empty-env) - core (io/resource "cljs/core.cljs") - base-js (io/resource "goog/base.js") - core-js (closure/compile core - (assoc opts :output-file - (closure/src-file->target-file - core (dissoc opts :output-dir)))) - deps (closure/add-dependencies opts core-js) - output-dir (util/output-directory opts) - repl-deps (io/file output-dir "rhino_repl_deps.js")] - ;; emit core and deps - (apply closure/output-unoptimized - (assoc opts :output-to (.getPath repl-deps)) deps) - - ;; setup back references & output stream - (ScriptableObject/putProperty scope - "___repl_env" (Context/javaToJS repl-env scope)) - (ScriptableObject/putProperty scope "__repl_opts" - (Context/javaToJS opts scope)) - (ScriptableObject/putProperty scope - "out" (Context/javaToJS *out* scope)) - (ScriptableObject/putProperty scope - "err" (Context/javaToJS *err* scope)) - - ;; define file loading, load goog.base, load repl deps - (rhino-eval repl-env "bootjs" 1 bootjs) - (rhino-eval repl-env "goog/base.js" 1 (io/reader base-js)) - (rhino-eval repl-env "rhino_repl_deps.js" 1 (io/reader repl-deps)) - - ;; === Bootstrap === - ;; load cljs.core, setup printing - (repl/evaluate-form repl-env env "" - '(do - (.require js/goog "cljs.core") - (set! *print-fn* (fn [x] (.write js/out x))) - (set! *print-err-fn* (fn [x] (.write js/err x))))) - - ;; allow namespace reloading - (repl/evaluate-form repl-env env "" - '(set! js/goog.isProvided_ (fn [x] false))) - - ;; monkey-patch goog.require - (repl/evaluate-form repl-env env "" - '(do - (set! *loaded-libs* #{"cljs.core"}) - (set! (.-require js/goog) - (fn [name reload] - (when (or (not (contains? *loaded-libs* name)) reload) - (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (if (some? goog/debugLoader_) - (.getPathFromDeps_ goog/debugLoader_ name) - (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))) - - ;; set closure-defines - (rhino-eval repl-env "CLOSURE_UNCOMPILED_DEFINES" 1 - (str "goog.global.CLOSURE_UNCOMPILED_DEFINES = " - (json/write-str (:closure-defines opts)) ";")))) - -;; Catching errors and rethrowing in Rhino swallows the original trace -;; https://groups.google.com/d/msg/mozilla.dev.tech.js-engine.rhino/inMyVKhPq6M/cY39hX20_z8J -(defn wrap-fn [form] - (cond - (and (seq? form) - (#{'ns 'require 'require-macros - 'use 'use-macros 'import 'refer-clojure} (first form))) - identity - - ('#{*1 *2 *3 *e} form) (fn [x] `(cljs.core.pr-str ~x)) - - :else - (fn [x] - `(cljs.core.pr-str - (let [ret# ~x] - (set! *3 *2) - (set! *2 *1) - (set! *1 ret#) - ret#))))) - -(defrecord RhinoEnv [] - repl/IReplEnvOptions - (-repl-options [this] - {:output-dir ".cljs_rhino_repl" - :wrap wrap-fn}) - repl/IParseStacktrace - (-parse-stacktrace [this frames-str ret opts] - (st/parse-stacktrace this frames-str - (assoc ret :ua-product :rhino) opts)) - repl/IGetError - (-get-error [this e env opts] - (let [{:keys [scope]} this - ex (-> scope - (ScriptableObject/getProperty "cljs") - (ScriptableObject/getProperty "core") - (ScriptableObject/getProperty "_STAR_e") - .unwrap)] - {:status :exception - :value (.toString ex) - :stacktrace (stacktrace ex)})) - repl/IJavaScriptEnv - (-setup [this opts] - (rhino-setup this opts)) - (-evaluate [this filename line js] - (rhino-eval this filename line js)) - (-load [this ns url] - (load-javascript this ns url)) - (-tear-down [_] (Context/exit))) - -(defn repl-env* - [opts] - (let [cx (Context/enter)] - ;; just avoid the 64K method limit - ;; Rhino is slow even with optimizations enabled - (.setOptimizationLevel cx -1) - (merge (RhinoEnv.) - {:cx cx - :scope (.initStandardObjects cx)}))) - -(defn repl-env - "Returns a fresh JS environment, suitable for passing to repl. - Hang on to return for use across repl calls." - [& {:as opts}] - (repl-env* opts)) - -(defn -main [& args] - (apply cli/main repl-env args)) - -(comment - - (repl/-parse-stacktrace (repl-env) - "\tat .cljs_rhino_repl/goog/../cljs/core.js:4215 (seq) -\tat .cljs_rhino_repl/goog/../cljs/core.js:4245 (first) -\tat .cljs_rhino_repl/goog/../cljs/core.js:5295 (ffirst) -\tat :1 -\tat :1" - nil - {:output-dir ".cljs_rhino_repl"}) - - (require '[cljs.repl :as repl]) - (require '[cljs.repl.rhino :as rhino]) - (def env (rhino/repl-env)) - (repl/repl env) - (+ 1 1) - "hello" - {:a "hello"} - (:a {:a "hello"}) - (:a {:a :b}) - (reduce + [1 2 3 4 5]) - (time (reduce + [1 2 3 4 5])) - (even? :a) - (throw (js/Error. "There was an error")) - (clojure.core/load-file "clojure/string.cljs") - (clojure.string/triml " hello") - (clojure.string/reverse " hello") - - (load-namespace 'clojure.set) - - (ns test.crypt - (:require [goog.crypt :as c])) - (c/stringToByteArray "Hello") - - (load-namespace 'goog.date.Date) - (goog.date.Date.) - - ) diff --git a/src/main/clojure/cljs/server/nashorn.clj b/src/main/clojure/cljs/server/nashorn.clj deleted file mode 100644 index cf91e770e..000000000 --- a/src/main/clojure/cljs/server/nashorn.clj +++ /dev/null @@ -1,27 +0,0 @@ -; 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.server.nashorn - (:require [cljs.repl :as repl] - [cljs.repl.nashorn :as nashorn] - [cljs.core.server :as server])) - -(defn repl - ([] - (repl nil)) - ([{:keys [opts env-opts]}] - (repl/repl* (nashorn/repl-env* env-opts) opts))) - -(defn prepl - ([] - (prepl nil)) - ([{:keys [opts env-opts]}] - (apply server/io-prepl - (mapcat identity - {:repl-env (nashorn/repl-env* env-opts) - :opts opts})))) diff --git a/src/main/clojure/cljs/server/rhino.clj b/src/main/clojure/cljs/server/rhino.clj deleted file mode 100644 index d1d7df61a..000000000 --- a/src/main/clojure/cljs/server/rhino.clj +++ /dev/null @@ -1,27 +0,0 @@ -; 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.server.rhino - (:require [cljs.repl :as repl] - [cljs.repl.rhino :as rhino] - [cljs.core.server :as server])) - -(defn repl - ([] - (repl nil)) - ([{:keys [opts env-opts]}] - (repl/repl* (rhino/repl-env* env-opts) opts))) - -(defn prepl - ([] - (prepl nil)) - ([{:keys [opts env-opts]}] - (apply server/io-prepl - (mapcat identity - {:repl-env (rhino/repl-env* env-opts) - :opts opts})))) diff --git a/src/test/clojure/cljs/repl_tests.clj b/src/test/clojure/cljs/repl_tests.clj index dda28e38d..e1a349be5 100644 --- a/src/test/clojure/cljs/repl_tests.clj +++ b/src/test/clojure/cljs/repl_tests.clj @@ -12,7 +12,6 @@ [cljs.analyzer.api :as ana-api] [cljs.env :as env] [cljs.repl :as repl] - [cljs.repl.rhino :as rhino] [cljs.compiler :as comp]) (:use clojure.test)) From 51f484953561b7468a49a28afe721f5c65133525 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 19 Mar 2020 21:14:43 -0400 Subject: [PATCH 3487/4033] fix aot-cache-core, the client-js needs to have :language-in set to :es6 --- src/main/clojure/cljs/closure.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 64af48008..d4f531eaf 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -3263,7 +3263,7 @@ ;; Browser REPL client stuff (defn compile-client-js [opts] - (let [copts (select-keys opts [:optimizations :output-dir])] + (let [copts (select-keys opts [:optimizations :output-dir :language-in])] ;; we're inside the REPL process where cljs.env/*compiler* is already ;; established, need to construct a new one to avoid mutating the one ;; the REPL uses @@ -3304,7 +3304,8 @@ (ana/write-analysis-cache 'cljs.core cache src) (ana/write-analysis-cache 'cljs.core tcache src)) (create-client-js-file - {:optimizations :simple + {:language-in :es6 + :optimizations :simple :output-dir "aot_out"} (io/file "resources" "brepl_client.js")) (doseq [f (file-seq (io/file "aot_out")) From 370e58784112852c4094b68de40209de8528ddee Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 19 Mar 2020 21:17:41 -0400 Subject: [PATCH 3488/4033] default :language-in to :es6 now that we depend on a newer Closure Library --- src/main/clojure/cljs/closure.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d4f531eaf..17867d507 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2447,6 +2447,9 @@ (contains? opts :modules) (ensure-module-opts) + (nil? (:language-in opts)) + (assoc :language-in :es6) + (:stable-names opts) (as-> opts (let [out-dir (if (true? (:stable-names opts)) From e4e1ac2520a2d5a1e5affdfc0a92a59da9dbbd2f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 20 Mar 2020 13:55:42 -0400 Subject: [PATCH 3489/4033] remove Rhino dep, remove non-supported REPL scripts, stacktrace support, build support, fix CI script, update CLI tests, cleanup bootstrap scripts --- .travis.yml | 1 - deps.edn | 3 +- pom.template.xml | 6 - project.clj | 3 +- samples/repl/README.md | 9 -- script/bootstrap | 11 -- script/bootstrap.ps1 | 8 -- script/nashorn_repl.clj | 3 - script/nashornrepljs | 15 --- script/repljs | 15 --- script/repljs.bat | 14 --- script/rhino_repl.clj | 3 - script/self-compile | 2 +- src/main/cljs/cljs/main.clj | 2 +- src/main/cljs/cljs/stacktrace.cljc | 138 --------------------- src/main/clojure/cljs/cli.clj | 10 +- src/main/clojure/cljs/closure.clj | 23 +--- src/test/cljs_cli/cljs_cli/test.clj | 4 +- src/test/cljs_cli/cljs_cli/test_runner.clj | 2 +- src/test/cljs_cli/cljs_cli/util.clj | 2 +- 20 files changed, 14 insertions(+), 260 deletions(-) delete mode 100644 script/nashorn_repl.clj delete mode 100755 script/nashornrepljs delete mode 100755 script/repljs delete mode 100644 script/repljs.bat delete mode 100644 script/rhino_repl.clj diff --git a/.travis.yml b/.travis.yml index a68e6eb93..7ee4c3809 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,7 +61,6 @@ script: - grep '0 failures, 0 errors.' test-out.txt - script/test-cli node | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - script/test-cli rhino | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - PATH=`pwd`/graalvm-ce-1.0.0-rc12/bin:$PATH script/test-cli graaljs | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt diff --git a/deps.edn b/deps.edn index aeb60f3c1..bf1d3d028 100644 --- a/deps.edn +++ b/deps.edn @@ -8,8 +8,7 @@ org.clojure/data.json {:mvn/version "0.2.6"} com.cognitect/transit-clj {:mvn/version "0.8.309"} com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200112"} - org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"} - org.mozilla/rhino {:mvn/version "1.7R5"}} + org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"}} :aliases {:test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" "src/test/clojure" "src/test/self"] diff --git a/pom.template.xml b/pom.template.xml index 2068ba005..ed8f2d802 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -42,11 +42,6 @@ data.json 0.2.6 - - org.mozilla - rhino - 1.7R5 - org.clojure tools.reader @@ -311,7 +306,6 @@ cljs.repl.browser cljs.repl.node cljs.repl.reflect - cljs.repl.rhino cljs.repl.server cljs.main cljs.cli diff --git a/project.clj b/project.clj index a28479249..56876bde7 100644 --- a/project.clj +++ b/project.clj @@ -16,8 +16,7 @@ [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20191016-6ae1f72f"] - [com.google.javascript/closure-compiler-unshaded "v20200112"] - [org.mozilla/rhino "1.7R5"]] + [com.google.javascript/closure-compiler-unshaded "v20200112"]] :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/samples/repl/README.md b/samples/repl/README.md index d9493b8c9..8af28b93c 100644 --- a/samples/repl/README.md +++ b/samples/repl/README.md @@ -17,15 +17,6 @@ There are currently four steps in starting a ClojureScript REPL. 3. create a new evaluation environment 4. start the REPL with the created environment -## Evaluating with Rhino - -```clj -(require '[cljs.repl :as repl]) -(require '[cljs.repl.rhino :as rhino]) -(def env (rhino/repl-env)) -(repl/repl env) -``` - ## Evaluating in the Browser A browser-connected REPL works in much the same way as a normal REPL: diff --git a/script/bootstrap b/script/bootstrap index aaa2e4872..46553b67c 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -9,7 +9,6 @@ CLOSURE_RELEASE="20200112" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20191016-6ae1f72f" -RHINO_RELEASE="1_7R5" TREADER_RELEASE="1.3.0" TEST_CHECK_RELEASE="0.10.0-alpha3" @@ -118,16 +117,6 @@ if [ "$1" = "--closure-library-head" ] ; then mv orig-deps.js closure/library/closure-library/third_party/closure/goog/deps.js fi -echo "Fetching Rhino..." -curl --retry 3 -LOk -s https://github.com/mozilla/rhino/releases/download/Rhino${RHINO_RELEASE}_RELEASE/rhino$RHINO_RELEASE.zip || { echo "Download failed."; exit 1; } -unzip -qu rhino$RHINO_RELEASE.zip -echo "Copying rhino$RHINO_RELEASE/js.jar to lib/js.jar..." -cp rhino$RHINO_RELEASE/js.jar lib/js.jar -echo "Cleaning up Rhino directory..." -rm -rf rhino$RHINO_RELEASE/ -echo "Cleaning up Rhino archive..." -rm rhino$RHINO_RELEASE.zip - echo "Fetching tools.reader $TREADER_RELEASE ..." curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar || { echo "Download failed."; exit 1; } diff --git a/script/bootstrap.ps1 b/script/bootstrap.ps1 index e2c17563e..d0091dc20 100644 --- a/script/bootstrap.ps1 +++ b/script/bootstrap.ps1 @@ -129,14 +129,6 @@ Get-WebResource ` Copy-File $root\closure-compiler-v$CLOSURE_RELEASE.jar $root\lib\compiler.jar Delete-File $root\closure-compiler-v$CLOSURE_RELEASE.jar -Write-Host "Fetching Rhino..." -Get-WebResource ` - https://github.com/mozilla/rhino/releases/download/Rhino${RHINO_RELEASE}_RELEASE/rhino$RHINO_RELEASE.zip ` - $root\rhino$RHINO_RELEASE.zip -Delete-File $root\lib\js.jar -Expand-ZipFile $root\rhino$RHINO_RELEASE.zip $root\lib rhino$RHINO_RELEASE\js.jar -Delete-File $root\rhino$RHINO_RELEASE.zip - Write-Host "Fetching tools.reader $TREADER_RELEASE ..." Get-WebResource ` https://repo1.maven.org/maven2/org/clojure/tools.reader/$TREADER_RELEASE/tools.reader-$TREADER_RELEASE.jar ` diff --git a/script/nashorn_repl.clj b/script/nashorn_repl.clj deleted file mode 100644 index 252042f4d..000000000 --- a/script/nashorn_repl.clj +++ /dev/null @@ -1,3 +0,0 @@ -(require '[cljs.repl :as repl]) -(require '[cljs.repl.nashorn :as nashorn]) -(repl/repl (nashorn/repl-env)) \ No newline at end of file diff --git a/script/nashornrepljs b/script/nashornrepljs deleted file mode 100755 index e3f31fe96..000000000 --- a/script/nashornrepljs +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -if [ "$CLOJURESCRIPT_HOME" = "" ]; then - CLOJURESCRIPT_HOME="`dirname $0`/.." -fi - -CLJSC_CP='' -for next in lib/*: src/main/clojure: src/main/cljs: test/cljs; do - CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next -done - -java -server -cp $CLJSC_CP clojure.main -e \ -"(require '[cljs.repl :as repl]) -(require '[cljs.repl.nashorn :as nashorn]) -(repl/repl (nashorn/repl-env $1))" diff --git a/script/repljs b/script/repljs deleted file mode 100755 index 5e4946afd..000000000 --- a/script/repljs +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -if [ "$CLOJURESCRIPT_HOME" = "" ]; then - CLOJURESCRIPT_HOME="`dirname $0`/.." -fi - -CLJSC_CP='' -for next in classes: lib/*: src/main/clojure: src/main/cljs: test/cljs; do - CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" -done - -java -server -cp "$CLJSC_CP" clojure.main -e \ -"(require '[cljs.repl :as repl]) -(require '[cljs.repl.rhino :as rhino]) -(repl/repl (rhino/repl-env) :warn-on-undeclared true)" diff --git a/script/repljs.bat b/script/repljs.bat deleted file mode 100644 index f255cf6bc..000000000 --- a/script/repljs.bat +++ /dev/null @@ -1,14 +0,0 @@ -@echo off -setLocal EnableDelayedExpansion - -if "%CLOJURESCRIPT_HOME%" == "" set CLOJURESCRIPT_HOME=%~dp0..\ - -set CLASSPATH=%CLOJURESCRIPT_HOME%src\clj;%CLOJURESCRIPT_HOME%src\cljs" -for /R "%CLOJURESCRIPT_HOME%\lib" %%a in (*.jar) do ( - set CLASSPATH=!CLASSPATH!;%%a -) -set CLASSPATH=!CLASSPATH!" - -set REPL_CLJ="(require '[cljs.repl :as repl])(require '[cljs.repl.rhino :as rhino])(repl/repl (rhino/repl-env))" - -java -server -cp "%CLASSPATH%" clojure.main -e %REPL_CLJ% diff --git a/script/rhino_repl.clj b/script/rhino_repl.clj deleted file mode 100644 index e3b4e31f5..000000000 --- a/script/rhino_repl.clj +++ /dev/null @@ -1,3 +0,0 @@ -(require '[cljs.repl :as repl]) -(require '[cljs.repl.rhino :as rhino]) -(repl/repl (rhino/repl-env)) diff --git a/script/self-compile b/script/self-compile index 22026f016..26822a6d1 100755 --- a/script/self-compile +++ b/script/self-compile @@ -2,4 +2,4 @@ rm -rf classes mkdir classes -./script/repl -e "(compile 'cljs.repl.node) (compile 'cljs.repl.browser) (compile 'cljs.repl.rhino) (compile 'cljs.core)" +./script/repl -e "(compile 'cljs.repl.node) (compile 'cljs.repl.browser) (compile 'cljs.core)" diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index de17bb33a..605afbe43 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -16,7 +16,7 @@ (let [repl-ns (symbol (str "cljs.repl." (if (= 1 (count args)) - "nashorn" + "browser" (nth args 1))))] (try (require repl-ns) diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index 08d7a9fa5..b1ee6b965 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -383,144 +383,6 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" nil) ) -;; ----------------------------------------------------------------------------- -;; Rhino Stacktrace - -(defmethod parse-stacktrace :rhino - [repl-env st err {:keys [output-dir] :as opts}] - (letfn [(process-frame [frame-str] - (when-not (or (string/blank? frame-str) - (== -1 (.indexOf frame-str "\tat"))) - (let [[file-side line-fn-side] (string/split frame-str #":") - file (string/replace file-side #"\s+at\s+" "") - [line function] (string/split line-fn-side #"\s+")] - {:file (string/replace file - (str output-dir - #?(:clj File/separator :cljs "/")) - "") - :function (when function - (-> function - (string/replace "(" "") - (string/replace ")" ""))) - :line (when (and line (not (string/blank? line))) - (parse-int line)) - :column 0})))] - (->> (string/split st #"\n") - (map process-frame) - (remove nil?) - vec))) - -(comment - (parse-stacktrace {} - "\tat .cljs_rhino_repl/goog/../cljs/core.js:4215 (seq) - \tat .cljs_rhino_repl/goog/../cljs/core.js:4245 (first) - \tat .cljs_rhino_repl/goog/../cljs/core.js:5295 (ffirst) - \tat :1 - \tat :1" - {:ua-product :rhino} - {:output-dir ".cljs_rhino_repl"}) - - (parse-stacktrace {} - "org.mozilla.javascript.JavaScriptException: Error: 1 is not ISeqable (.cljs_rhino_repl/goog/../cljs/core.js#3998) - \tat .cljs_rhino_repl/goog/../cljs/core.js:3998 (cljs$core$seq) - \tat .cljs_rhino_repl/goog/../cljs/core.js:4017 (cljs$core$first) - \tat .cljs_rhino_repl/goog/../cljs/core.js:5160 (cljs$core$ffirst) - \tat .cljs_rhino_repl/goog/../cljs/core.js:16005 - \tat .cljs_rhino_repl/goog/../cljs/core.js:16004 - \tat .cljs_rhino_repl/goog/../cljs/core.js:10243 - \tat .cljs_rhino_repl/goog/../cljs/core.js:10334 - \tat .cljs_rhino_repl/goog/../cljs/core.js:3979 (cljs$core$seq) - \tat .cljs_rhino_repl/goog/../cljs/core.js:28083 (cljs$core$pr_sequential_writer) - \tat .cljs_rhino_repl/goog/../cljs/core.js:28811 - \tat .cljs_rhino_repl/goog/../cljs/core.js:28267 (cljs$core$pr_writer_impl) - \tat .cljs_rhino_repl/goog/../cljs/core.js:28349 (cljs$core$pr_writer) - \tat .cljs_rhino_repl/goog/../cljs/core.js:28353 (cljs$core$pr_seq_writer) - \tat .cljs_rhino_repl/goog/../cljs/core.js:28416 (cljs$core$pr_sb_with_opts) - \tat .cljs_rhino_repl/goog/../cljs/core.js:28430 (cljs$core$pr_str_with_opts) - \tat .cljs_rhino_repl/goog/../cljs/core.js:28524 - \tat .cljs_rhino_repl/goog/../cljs/core.js:28520 (cljs$core$pr_str) - at :1 - " - {:ua-product :rhino} - {:output-dir ".cljs_rhino_repl"}) - ) - -;; ----------------------------------------------------------------------------- -;; Nashorn Stacktrace - -(defmethod parse-stacktrace :nashorn - [repl-env st err {:keys [output-dir] :as opts}] - (letfn [(process-frame [frame-str] - (when-not (or (string/blank? frame-str) - (== -1 (.indexOf frame-str "\tat"))) - (let [frame-str (string/replace frame-str #"\s+at\s+" "") - [function file-and-line] (string/split frame-str #"\s+") - [file-part line-part] (string/split file-and-line #":")] - {:file (string/replace (.substring file-part 1) - (str output-dir - #?(:clj File/separator :cljs "/")) - "") - :function function - :line (when (and line-part (not (string/blank? line-part))) - (parse-int - (.substring line-part 0 - (dec (count line-part))))) - :column 0})))] - (->> (string/split st #"\n") - (map process-frame) - (remove nil?) - vec))) - -;; ----------------------------------------------------------------------------- -;; Graal.JS Stacktrace - -(defmethod parse-stacktrace :graaljs - [repl-env st err {:keys [output-dir] :as opts}] - (letfn [(process-frame [frame-str] - (when-not (string/blank? frame-str) - (let [[function file-and-line] (string/split frame-str #"\(") - [file-part line-part] (string/split file-and-line #":")] - {:file (string/replace file-part - (str output-dir - #?(:clj File/separator :cljs "/")) - "") - :function function - :line (when (and line-part (not (string/blank? line-part))) - (parse-int - (.substring line-part 0 - (dec (count line-part))))) - :column 0})))] - (->> (string/split st #"\n") - (map process-frame) - (remove nil?) - vec))) - -(comment - (parse-stacktrace {} - "Error: 1 is not ISeqable - \tat cljs$core$seq (.cljs_nashorn_repl/goog/../cljs/core.js:3998) - \tat cljs$core$first (.cljs_nashorn_repl/goog/../cljs/core.js:4017) - \tat cljs$core$ffirst (.cljs_nashorn_repl/goog/../cljs/core.js:5160) - \tat (.cljs_nashorn_repl/goog/../cljs/core.js:16005) - \tat (.cljs_nashorn_repl/goog/../cljs/core.js:16004) - \tat sval (.cljs_nashorn_repl/goog/../cljs/core.js:10243) - \tat cljs$core$ISeqable$_seq$arity$1-6 (.cljs_nashorn_repl/goog/../cljs/core.js:10334) - \tat cljs$core$seq (.cljs_nashorn_repl/goog/../cljs/core.js:3979) - \tat cljs$core$pr_sequential_writer (.cljs_nashorn_repl/goog/../cljs/core.js:28083) - \tat cljs$core$IPrintWithWriter$_pr_writer$arity$3-5 (.cljs_nashorn_repl/goog/../cljs/core.js:28811) - \tat cljs$core$pr_writer_impl (.cljs_nashorn_repl/goog/../cljs/core.js:28267) - \tat cljs$core$pr_writer (.cljs_nashorn_repl/goog/../cljs/core.js:28349) - \tat cljs$core$pr_seq_writer (.cljs_nashorn_repl/goog/../cljs/core.js:28353) - \tat cljs$core$pr_sb_with_opts (.cljs_nashorn_repl/goog/../cljs/core.js:28416) - \tat cljs$core$pr_str_with_opts (.cljs_nashorn_repl/goog/../cljs/core.js:28430) - \tat cljs$core$IFn$_invoke$arity$variadic-71 (.cljs_nashorn_repl/goog/../cljs/core.js:28524) - \tat cljs$core$pr_str (.cljs_nashorn_repl/goog/../cljs/core.js:28520) - \tat (:1) - \tat (:1)\n" - {:ua-product :nashorn} - {:output-dir ".cljs_nashorn_repl"}) - ) - ;; ----------------------------------------------------------------------------- ;; Node.js Stacktrace diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index af069ebee..e3b0a2e3b 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -298,7 +298,7 @@ is trying load some arbitrary ns." (defn- fast-initial-prompt? [repl-env inits] (and (empty? inits) - (contains? #{"node" "nashorn" "graaljs" "rhino"} (repl-name repl-env)))) + (contains? #{"node"} (repl-name repl-env)))) (defn- repl-opt "Start a repl with args and inits. Print greeting if no eval options were @@ -545,8 +545,8 @@ present" {["-re" "--repl-env"] {:arg "env" :doc (str "The REPL environment to use. Built-in " - "supported values: nashorn, graaljs, node, browser, " - "rhino. Defaults to browser")}}} + "supported values: node, browser. " + "Defaults to browser")}}} ::main {:desc "init options only for --main and --repl"} ::compile {:desc "init options only for --compile"}} :init @@ -583,8 +583,8 @@ present" :arg "name" :doc (str "The JavaScript target. Configures environment bootstrap and " - "defaults to browser. Supported values: node or nodejs, nashorn, " - "graaljs, webworker, none") } + "defaults to browser. Supported values: node or nodejs " + "webworker, none") } ["-ro" "--repl-opts"] {:group ::main&compile :fn repl-env-opts-opt :arg "edn" :doc (str "Options to configure the repl-env, can be an EDN string or " diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 17867d507..df4a382ff 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1658,27 +1658,6 @@ (util/output-directory opts)) closure-defines (json/write-str (:closure-defines opts))] (case (:target opts) - (:nashorn :graaljs) - (output-one-file - (merge opts - (when module - {:output-to (:output-to module)})) - (add-header opts - (str (when (or (not module) (= :cljs-base (:module-name opts))) - (str "var CLJS_OUTPUT_DIR = \"" asset-path "\";\n" - "load((new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"), \"base.js\")).getPath());\n" - "load((new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"), \"deps.js\")).getPath());\n" - "load((new java.io.File(new java.io.File(new java.io.File(\"" asset-path "\",\"goog\"),\"bootstrap\"),\"" (name (:target opts)) ".js\")).getPath());\n" - "load((new java.io.File(\"" asset-path "\",\"cljs_deps.js\")).getPath());\n" - "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - (apply str (preloads (:preloads opts))))) - (apply str - (map (fn [entry] - (str "goog.require(\"" (comp/munge entry) "\");\n")) - (if-let [entries (when module (:entries module))] - entries - [(:main opts)])))))) - :nodejs (output-one-file (merge opts @@ -2875,7 +2854,7 @@ opts)) (defn output-bootstrap [{:keys [target] :as opts}] - (when (and (#{:nodejs :nashorn :graaljs} target) + (when (and (#{:nodejs} target) (not= (:optimizations opts) :whitespace)) (let [target-str (name target) outfile (io/file (util/output-directory opts) diff --git a/src/test/cljs_cli/cljs_cli/test.clj b/src/test/cljs_cli/cljs_cli/test.clj index 05765306a..e694638dd 100644 --- a/src/test/cljs_cli/cljs_cli/test.clj +++ b/src/test/cljs_cli/cljs_cli/test.clj @@ -115,7 +115,7 @@ (output-is "\"0000000003\"")))) (deftest test-cljs-2780 - (with-repl-env-filter #{"node" "nashorn" "graaljs"} + (with-repl-env-filter #{"node"} (-> (cljs-main "-e" "(do (js/setTimeout #(prn :end) 500) nil)" "-e" ":begin") @@ -129,7 +129,7 @@ (output-is 2)))) (deftest test-cljs-3043 - (with-repl-env-filter (complement #{"rhino"}) + (with-repl-env-filter identity (let [check-fn (fn [result] (is (= 1 (:exit result))) (is (str/includes? (:err result) "Execution error")) diff --git a/src/test/cljs_cli/cljs_cli/test_runner.clj b/src/test/cljs_cli/cljs_cli/test_runner.clj index ce647fc94..f6ab983df 100644 --- a/src/test/cljs_cli/cljs_cli/test_runner.clj +++ b/src/test/cljs_cli/cljs_cli/test_runner.clj @@ -5,7 +5,7 @@ (defn -main [& args] (try - (binding [cljs-cli.util/*repl-env* (or (first args) "nashorn") + (binding [cljs-cli.util/*repl-env* (or (first args) "node") cljs-cli.util/*repl-opts* (second args)] (clojure.test/run-tests 'cljs-cli.test)) (finally diff --git a/src/test/cljs_cli/cljs_cli/util.clj b/src/test/cljs_cli/cljs_cli/util.clj index 09c9ebf6e..d272c8444 100644 --- a/src/test/cljs_cli/cljs_cli/util.clj +++ b/src/test/cljs_cli/cljs_cli/util.clj @@ -11,7 +11,7 @@ (java.nio.file Files CopyOption) (java.nio.file.attribute FileAttribute))) -(def ^:dynamic *repl-env* "nashorn") +(def ^:dynamic *repl-env* "node") (def ^:dynamic *repl-env-filter* (constantly true)) (def ^:dynamic *repl-opts* nil) (def ^:dynamic *sources* nil) From 20f34d8533f1fd324b0c34da96f49ac0a5574d5b Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 24 Mar 2020 19:24:34 -0400 Subject: [PATCH 3490/4033] bump Closure Compiler --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 4 ++-- src/main/clojure/cljs/closure.clj | 1 - 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/deps.edn b/deps.edn index bf1d3d028..29a2c258e 100644 --- a/deps.edn +++ b/deps.edn @@ -7,7 +7,7 @@ org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} com.cognitect/transit-clj {:mvn/version "0.8.309"} - com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200112"} + com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200315"} org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"}} :aliases {:test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" diff --git a/pom.template.xml b/pom.template.xml index ed8f2d802..23edfc657 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20200112 + v20200315 org.clojure diff --git a/project.clj b/project.clj index 56876bde7..d2c017c3c 100644 --- a/project.clj +++ b/project.clj @@ -16,7 +16,7 @@ [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20191016-6ae1f72f"] - [com.google.javascript/closure-compiler-unshaded "v20200112"]] + [com.google.javascript/closure-compiler-unshaded "v20200315"]] :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/script/bootstrap b/script/bootstrap index 46553b67c..12b001b30 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="20200112" +CLOSURE_RELEASE="20200315" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20191016-6ae1f72f" @@ -112,7 +112,7 @@ if [ "$1" = "--closure-library-head" ] ; then echo "Building lib/google-closure-library-third-party-HEAD.jar..." mv closure/library/closure-library/third_party/closure/goog/deps.js orig-deps.js mv closure/library/closure-library/third_party/closure/goog/base.js orig-base.js - jar cf ./lib/google-closure-library-third-party-HEAD.jar -C closure/library/closure-library/third_party/closure/ goog + jar cf ./lib/google-closure-library-third-party-HEAD.jar -C closure/library/closure-library/third_party/closure/ goog mv orig-base.js closure/library/closure-library/third_party/closure/goog/base.js mv orig-deps.js closure/library/closure-library/third_party/closure/goog/deps.js fi diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index df4a382ff..70e1ae0a7 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -188,7 +188,6 @@ :unnecessary-escape DiagnosticGroups/UNNECESSARY_ESCAPE :unused-local-variable DiagnosticGroups/UNUSED_LOCAL_VARIABLE :unused-private-property DiagnosticGroups/UNUSED_PRIVATE_PROPERTY - :use-of-goog-base DiagnosticGroups/USE_OF_GOOG_BASE :violated-module-dep DiagnosticGroups/VIOLATED_MODULE_DEP :visiblity DiagnosticGroups/VISIBILITY}) From dd3d50fc6b696306de7331e8dfe9fcbe2a135620 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 26 Mar 2020 15:18:45 -0400 Subject: [PATCH 3491/4033] basic lowering of goog.module --- src/main/clojure/cljs/closure.clj | 39 +++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 70e1ae0a7..1bab9314b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -23,28 +23,27 @@ [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers] [cljs.module-graph :as module-graph]) - (:import [java.lang ProcessBuilder StringBuilder] + (:import [java.lang ProcessBuilder] [java.io File BufferedInputStream BufferedReader Writer InputStreamReader IOException StringWriter ByteArrayInputStream] - [java.net URL] + [java.net URI URL] [java.util.logging Level] [java.util List Random] [java.util.concurrent TimeUnit LinkedBlockingDeque Executors CountDownLatch] [com.google.javascript.jscomp CompilerOptions CompilationLevel - CompilerInput CompilerInput$ModuleType DependencyOptions - CompilerOptions$LanguageMode SourceMap$Format - SourceMap$DetailLevel ClosureCodingConvention SourceFile - Result JSError CheckLevel DiagnosticGroups - CommandLineRunner AnonymousFunctionNamingPolicy - JSModule SourceMap VariableMap] - [com.google.javascript.jscomp.transpile BaseTranspiler] - [com.google.javascript.jscomp.deps ClosureBundler - ModuleLoader$ResolutionMode ModuleNames SimpleDependencyInfo] + CompilerInput CompilerInput$ModuleType DependencyOptions + CompilerOptions$LanguageMode SourceMap$Format + SourceMap$DetailLevel ClosureCodingConvention SourceFile + Result JSError CheckLevel DiagnosticGroups + CommandLineRunner AnonymousFunctionNamingPolicy + JSModule SourceMap VariableMap] + [com.google.javascript.jscomp.bundle Transpiler] + [com.google.javascript.jscomp.deps DepsGenerator ModuleLoader$ResolutionMode ModuleNames] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey - WatchEvent FileVisitor FileVisitResult] + WatchEvent FileVisitor FileVisitResult FileSystems] [java.nio.charset Charset StandardCharsets] [com.sun.nio.file SensitivityWatchEventModifier] [com.google.common.base Throwables])) @@ -1989,6 +1988,22 @@ (ana/warning :unsupported-preprocess-value @env/*compiler* ijs) ijs) +(defn url->nio-path [url] + (let [raw-uri (.toURI url) + arr (-> raw-uri .toString (.split "!")) + uri (-> arr (aget 0) URI/create) + fs (FileSystems/getFileSystem uri)] + (.getPath fs ^String (.toString raw-uri) (make-array String 0)))) + +(defn transpile-goog-module [rsc] + (let [cc (Transpiler/compilerSupplier) + result (.compile cc (url->nio-path rsc) (slurp rsc))] + (.source result))) + +(comment + (transpile-goog-module (io/resource "goog/math/long.js")) + ) + (defn write-javascript "Write or copy a JavaScript file to output directory. Only write if the file does not already exist. Return IJavaScript for the file on disk at the new From a537f38e4c0cae8cec5a35549ba9694cb7318b38 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 26 Mar 2020 15:46:08 -0400 Subject: [PATCH 3492/4033] Lift out helper to map collection of CLJS style sources into collection of Closure SourceFiles --- src/main/clojure/cljs/closure.clj | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 1bab9314b..866a53b4f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1461,6 +1461,15 @@ :source-map-name source-map-name})))]))) (report-failure result)))) +(defn ->js-source-files [sources] + (doall + (map (fn [src] + (let [src' (cond-> src + (and (not (record? src)) (map? src)) + map->javascript-file)] + (js-source-file (javascript-name src') src'))) + sources))) + (defn optimize "Use the Closure Compiler to optimize one or more JavaScript files." [opts & sources] @@ -1472,14 +1481,7 @@ sources (if (= :whitespace (:optimizations opts)) (cons "var CLOSURE_NO_DEPS = true;" sources) sources) - ^List inputs (doall - (map - (fn [source] - (let [source (cond-> source - (and (not (record? source)) (map? source)) - map->javascript-file)] - (js-source-file (javascript-name source) source))) - sources)) + ^List inputs (->js-source-files sources) ^Result result (util/measure (:compiler-stats opts) "Optimizing with Google Closure Compiler" (.compile closure-compiler externs inputs compiler-options))] From 8e380b9ffdc77b9550db7399b6421d4cd4c7a577 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 26 Mar 2020 18:24:31 -0400 Subject: [PATCH 3493/4033] mark goog.module files --- src/main/clojure/cljs/js_deps.cljc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 5fc100b1b..f87554692 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -115,16 +115,21 @@ case." (->> (for [line lines x (string/split line #";")] x) (map string/trim) (drop-while #(not (or (string/includes? % "goog.provide(") + (string/includes? % "goog.module(") (string/includes? % "goog.require(")))) (take-while #(not (re-matches #".*=[\s]*function\(.*\)[\s]*[{].*" %))) - (map #(re-matches #".*goog\.(provide|require)\(['\"](.*)['\"]\)" %)) + (map #(re-matches #".*goog\.(provide|module|require)\(['\"](.*)['\"]\)" %)) (remove nil?) (map #(drop 1 %)) (reduce (fn [m ns] (let [munged-ns (string/replace (last ns) "_" "-")] - (if (= (first ns) "require") - (conj-in m :requires munged-ns) - (conj-in m :provides munged-ns)))) + (println (first ns)) + (case (first ns) + "provide" (conj-in m :provides munged-ns) + "module" (-> m + (conj-in :provides munged-ns) + (assoc :module :goog)) + "require" (conj-in m :requires munged-ns)))) {:requires [] :provides []})))) (defprotocol IJavaScript From 4bd964e720e03f7d87e38add8683d4dd00ce5d34 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 26 Mar 2020 18:29:35 -0400 Subject: [PATCH 3494/4033] remove println --- src/main/clojure/cljs/js_deps.cljc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index f87554692..ec13e09b2 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -123,7 +123,6 @@ case." (map #(drop 1 %)) (reduce (fn [m ns] (let [munged-ns (string/replace (last ns) "_" "-")] - (println (first ns)) (case (first ns) "provide" (conj-in m :provides munged-ns) "module" (-> m From 697e88f193d4ec4b1d8cc4c75919558b6996c6bd Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 26 Mar 2020 19:59:05 -0400 Subject: [PATCH 3495/4033] always lower goog.module files, generate our own complete dep-file remove `module: goog` entries from loadOpts as we always lower those. make sure cljs.js-deps/goog-dependencies adds :module information parsed from Closure Library deps.js file. --- src/main/clojure/cljs/closure.clj | 79 +++++++++++++++++++++--------- src/main/clojure/cljs/js_deps.cljc | 22 +++++++-- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 866a53b4f..a284bf7af 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -29,7 +29,7 @@ Writer InputStreamReader IOException StringWriter ByteArrayInputStream] [java.net URI URL] [java.util.logging Level] - [java.util List Random] + [java.util List Random HashMap] [java.util.concurrent TimeUnit LinkedBlockingDeque Executors CountDownLatch] [com.google.javascript.jscomp CompilerOptions CompilationLevel @@ -38,9 +38,11 @@ SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroups CommandLineRunner AnonymousFunctionNamingPolicy - JSModule SourceMap VariableMap] + JSModule SourceMap VariableMap PrintStreamErrorManager] [com.google.javascript.jscomp.bundle Transpiler] - [com.google.javascript.jscomp.deps DepsGenerator ModuleLoader$ResolutionMode ModuleNames] + [com.google.javascript.jscomp.deps DepsGenerator ModuleLoader$ResolutionMode ModuleNames + DepsGenerator$InclusionStrategy BrowserModuleResolver + ModuleLoader ModuleLoader$PathResolver] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult FileSystems] @@ -516,6 +518,8 @@ {:out-file out-file}) (when (:closure-lib m) {:closure-lib true}) + (when-let [module (:module m)] + {:module module}) (when-let [ns (:ns m)] {:ns ns}) (when (:macros-ns m) @@ -809,9 +813,15 @@ (comment ;; find dependencies - (js-dependencies {} ["goog.array"]) + (binding [env/*compiler* (env/default-compiler-env)] + (js-dependencies {} ["goog.array"])) + ;; find dependencies in an external library - (js-dependencies {:libs ["closure/library/third_party/closure"]} ["goog.dom.query"]) + (binding [env/*compiler* (env/default-compiler-env)] + (js-dependencies {:libs ["closure/library/third_party/closure"]} ["goog.dom.query"])) + + (binding [env/*compiler* (env/default-compiler-env)] + (js-dependencies {} ["goog.math.Long"])) ) (defn add-core-macros-if-cljs-js @@ -1585,10 +1595,18 @@ (if (deps/-foreign? input) ", {'foreign-lib': true}") ");\n"))) -(defn deps-file - "Return a deps file string for a sequence of inputs." - [opts sources] - (apply str (map #(add-dep-string opts %) sources))) +(defn deps-file [{:keys [output-dir] :as opts} sources] + (let [xs (->js-source-files sources) + gen (DepsGenerator. [] xs DepsGenerator$InclusionStrategy/ALWAYS + (.getAbsolutePath (io/file output-dir "goog")) + ;; TODO: modernize + (PrintStreamErrorManager. System/err) + (ModuleLoader. nil [output-dir] [] + BrowserModuleResolver/FACTORY ModuleLoader$PathResolver/ABSOLUTE))] + ;; We *always* lower goog.module files + (-> (.computeDependencyCalls gen) + (string/replace ", 'module': 'goog'" "") + (string/replace "'module': 'goog'" "")))) (comment (path-relative-to (io/file "out/goog/base.js") {:url (deps/to-url "out/cljs/core.js")}) @@ -1994,16 +2012,31 @@ (let [raw-uri (.toURI url) arr (-> raw-uri .toString (.split "!")) uri (-> arr (aget 0) URI/create) - fs (FileSystems/getFileSystem uri)] + fs (try + (FileSystems/getFileSystem uri) + (catch Throwable t + (FileSystems/newFileSystem uri (HashMap.))))] (.getPath fs ^String (.toString raw-uri) (make-array String 0)))) -(defn transpile-goog-module [rsc] - (let [cc (Transpiler/compilerSupplier) - result (.compile cc (url->nio-path rsc) (slurp rsc))] - (.source result))) +(defn maybe-transpile-goog-module [rsc {:keys [module]}] + (let [cc (Transpiler/compilerSupplier) + result (.compile cc (url->nio-path rsc) (slurp rsc)) + source (.source result)] + (if (.transformed result) + (cond-> source + (= :goog module) + ;; remove things not removed by lowering + (-> + (string/replace "goog.module(" "goog.provide(") + (string/replace "goog.module.declareLegacyNamespace();\n" ""))) + source))) (comment - (transpile-goog-module (io/resource "goog/math/long.js")) + (println (slurp (io/resource "goog/math/long.js"))) + (deps/parse-js-ns (-> (io/resource "goog/math/long.js") io/reader line-seq)) + (url->nio-path (io/resource "goog/math/long.js") ) + (println + (maybe-transpile-goog-module (io/resource "goog/math/long.js") {:module :goog})) ) (defn write-javascript @@ -2028,9 +2061,11 @@ (or (not (.exists out-file)) (and res (util/changed? out-file res)))) (when (and res (or ana/*verbose* (:verbose opts))) - (util/debug-prn "Copying" (str res) "to" (str out-file))) + (util/debug-prn "Copying JS" (str res) "to" (str out-file))) (util/mkdirs out-file) - (spit out-file (deps/-source js)) + (if (= :goog (:module js)) + (spit out-file (maybe-transpile-goog-module res js)) + (spit out-file (deps/-source js))) (when res (.setLastModified ^File out-file (util/last-modified res)))) (if (map? js) @@ -2095,16 +2130,14 @@ libraries." [{:keys [modules] :as opts} & sources] ;; this source-on-disk call is currently necessary for REPLs - David - (let [disk-sources (doall - (remove #(= (:group %) :goog) - (map #(source-on-disk opts %) sources))) - goog-deps (io/file (util/output-directory opts) "goog" "deps.js") + (doall (map #(source-on-disk opts %) sources)) + (let [goog-deps (io/file (util/output-directory opts) "goog" "deps.js") main (:main opts) output-deps #(output-deps-file (assoc opts :output-to (str (util/output-directory opts) File/separator "cljs_deps.js")) - disk-sources)] + sources)] (util/mkdirs goog-deps) (spit goog-deps (slurp (io/resource "goog/deps.js"))) (when (:debug-inputs opts) @@ -2128,7 +2161,7 @@ (output-deps) (output-main-file opts)) - :else (output-deps-file opts disk-sources)))) + :else (output-deps-file opts sources)))) (defn get-upstream-deps* "returns a merged map containing all upstream dependencies defined diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index ec13e09b2..5b860c144 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -8,6 +8,7 @@ (ns cljs.js-deps (:require [cljs.util :as util :refer [distinct-by]] + [clojure.data.json :as json] [clojure.java.io :as io] [clojure.string :as string]) (:import [java.io File] @@ -317,6 +318,10 @@ JavaScript library containing provide/require 'declarations'." ; (re-find #"(\/google-closure-library-0.0*|\/google-closure-library\/)" (.getPath ^URL res))) ; (enumeration-seq (.getResources (.getContextClassLoader (Thread/currentThread)) path))))) +;; NOTE: because this looks at deps.js for indexing the Closure Library we +;; don't need to bother parsing file in Closure Library. But it's also a +;; potential source of confusion as *other* Closure style libs will need to be +;; parsed, user won't typically provide a deps.js (defn goog-dependencies* "Create an index of Google dependencies by namespace and file name." [] @@ -325,14 +330,21 @@ JavaScript library containing provide/require 'declarations'." (string/split #"'\s*,\s*'"))))] (with-open [reader (io/reader (io/resource "goog/deps.js"))] (->> (line-seq reader) - (map #(re-matches #"^goog\.addDependency\(['\"](.*)['\"],\s*\[(.*)\],\s*\[(.*)\],.*\);.*" %)) + (map #(re-matches #"^goog\.addDependency\(['\"](.*)['\"],\s*\[(.*)\],\s*\[(.*)\],\s*(\{.*\})\);.*" %)) (remove nil?) (map #(drop 1 %)) (remove #(.startsWith ^String (first %) "../../third_party")) - (map #(hash-map :file (str "goog/" (nth % 0)) - :provides (parse-list (nth % 1)) - :requires (parse-list (nth % 2)) - :group :goog)) + (map + (fn [[file provides requires load-opts-str]] + (let [{:strs [module]} (json/read-str + (string/replace load-opts-str "'" "\""))] + (merge + {:file (str "goog/" file) + :provides (parse-list provides) + :requires (parse-list requires) + :group :goog} + (when module + {:module (keyword module)}))))) (doall))))) (def goog-dependencies (memoize goog-dependencies*)) From 4d0c6ddd9585c515d8d7067943ae11b696bc7f8a Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 26 Mar 2020 22:51:22 -0400 Subject: [PATCH 3496/4033] get :lang from deps.js --- src/main/clojure/cljs/js_deps.cljc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 5b860c144..d589dc671 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -336,15 +336,17 @@ JavaScript library containing provide/require 'declarations'." (remove #(.startsWith ^String (first %) "../../third_party")) (map (fn [[file provides requires load-opts-str]] - (let [{:strs [module]} (json/read-str - (string/replace load-opts-str "'" "\""))] + (let [{:strs [lang module]} + (-> (string/replace load-opts-str "'" "\"") (json/read-str))] (merge {:file (str "goog/" file) :provides (parse-list provides) :requires (parse-list requires) :group :goog} (when module - {:module (keyword module)}))))) + {:module (keyword module)}) + (when lang + {:lang (keyword lang)}))))) (doall))))) (def goog-dependencies (memoize goog-dependencies*)) From 30b2b3465afc6acf13eb97a7f2542b6f75bcd69e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Mar 2020 08:05:33 -0400 Subject: [PATCH 3497/4033] initial transpile support for Google Closure library. Handles goog.module and ground work for GCL libs that are higher than ES3. No caching support yet for transpiled libs - normally we just copy and the original files can be used as inputs - but transpiled files cannot because certain expectations become broken around goog.module conventions & annotations --- src/main/clojure/cljs/closure.clj | 96 +++++++++++++++++++------------ 1 file changed, 60 insertions(+), 36 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a284bf7af..5fa8beb5f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -40,9 +40,8 @@ CommandLineRunner AnonymousFunctionNamingPolicy JSModule SourceMap VariableMap PrintStreamErrorManager] [com.google.javascript.jscomp.bundle Transpiler] - [com.google.javascript.jscomp.deps DepsGenerator ModuleLoader$ResolutionMode ModuleNames - DepsGenerator$InclusionStrategy BrowserModuleResolver - ModuleLoader ModuleLoader$PathResolver] + [com.google.javascript.jscomp.deps ClosureBundler ModuleLoader$ResolutionMode ModuleNames + SimpleDependencyInfo] [com.google.javascript.rhino Node] [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult FileSystems] @@ -228,8 +227,16 @@ (string/join ", " (keys string->charset)) " supported ") {:clojure.error/phase :compilation})))) +(def lang-level + [:ecmascript3 :ecmascript5 :ecmascript5-strict :ecmascript6 :ecmascript6-strict + :ecmascript-2015 :ecmascript6-typed :ecmascript-2016 :ecmascript-2017 :ecmascript-next + :no-transpile]) + +(defn expand-lang-key [key] + (keyword (string/replace (name key) #"^es" "ecmascript"))) + (defn ^CompilerOptions$LanguageMode lang-key->lang-mode [key] - (case (keyword (string/replace (name key) #"^es" "ecmascript")) + (case (expand-lang-key key) :no-transpile CompilerOptions$LanguageMode/NO_TRANSPILE ;; same mode as input (for language-out only) :ecmascript3 CompilerOptions$LanguageMode/ECMASCRIPT3 :ecmascript5 CompilerOptions$LanguageMode/ECMASCRIPT5 @@ -520,6 +527,8 @@ {:closure-lib true}) (when-let [module (:module m)] {:module module}) + (when-let [lang (:lang m)] + {:lang lang}) (when-let [ns (:ns m)] {:ns ns}) (when (:macros-ns m) @@ -818,10 +827,13 @@ ;; find dependencies in an external library (binding [env/*compiler* (env/default-compiler-env)] - (js-dependencies {:libs ["closure/library/third_party/closure"]} ["goog.dom.query"])) + (js-dependencies {:libs ["closure/library/third_party/closure"]} ["goog.dom.query"])) (binding [env/*compiler* (env/default-compiler-env)] (js-dependencies {} ["goog.math.Long"])) + + (binding [env/*compiler* (env/default-compiler-env)] + (js-dependencies {} ["goog.string.StringBuffer"])) ) (defn add-core-macros-if-cljs-js @@ -1595,23 +1607,15 @@ (if (deps/-foreign? input) ", {'foreign-lib': true}") ");\n"))) -(defn deps-file [{:keys [output-dir] :as opts} sources] - (let [xs (->js-source-files sources) - gen (DepsGenerator. [] xs DepsGenerator$InclusionStrategy/ALWAYS - (.getAbsolutePath (io/file output-dir "goog")) - ;; TODO: modernize - (PrintStreamErrorManager. System/err) - (ModuleLoader. nil [output-dir] [] - BrowserModuleResolver/FACTORY ModuleLoader$PathResolver/ABSOLUTE))] - ;; We *always* lower goog.module files - (-> (.computeDependencyCalls gen) - (string/replace ", 'module': 'goog'" "") - (string/replace "'module': 'goog'" "")))) +(defn deps-file + "Return a deps file string for a sequence of inputs." + [opts sources] + (apply str (map #(add-dep-string opts %) sources))) (comment (path-relative-to (io/file "out/goog/base.js") {:url (deps/to-url "out/cljs/core.js")}) (add-dep-string {} {:url (deps/to-url "out/cljs/core.js") :requires ["goog.string"] :provides ["cljs.core"]}) - (deps-file {} [{:url (deps/to-url "out/cljs/core.js") :requires ["goog.string"] :provides ["cljs.core"]}]) + (deps-file {:output-dir "pubic/js"} [{:url (deps/to-url "out/cljs/core.js") :requires ["goog.string"] :provides ["cljs.core"]}]) ) (defn elide-strict [js {:keys [elide-strict] :as opts}] @@ -2018,53 +2022,73 @@ (FileSystems/newFileSystem uri (HashMap.))))] (.getPath fs ^String (.toString raw-uri) (make-array String 0)))) -(defn maybe-transpile-goog-module [rsc {:keys [module]}] - (let [cc (Transpiler/compilerSupplier) - result (.compile cc (url->nio-path rsc) (slurp rsc)) - source (.source result)] - (if (.transformed result) - (cond-> source - (= :goog module) - ;; remove things not removed by lowering - (-> - (string/replace "goog.module(" "goog.provide(") - (string/replace "goog.module.declareLegacyNamespace();\n" ""))) - source))) +(defn add-goog-load [source] + (let [sb (StringBuilder.) + module (-> (SimpleDependencyInfo/builder "" "") + (.setGoogModule true) .build) + bundler (ClosureBundler.)] + (.appendTo bundler sb module source) + (.toString sb))) + +;; TODO: better error handling +;; TODO: actually respect the target :language-out level +;; currently just using default options for Transpiler +(defn transpile + [{:keys [language-out] :or {language-out :es3}} rsc {:keys [module lang] :as js}] + (let [source (slurp rsc) + source' (if (< (.indexOf lang-level (expand-lang-key language-out)) + (.indexOf lang-level (expand-lang-key lang))) + (let [cc (Transpiler/compilerSupplier) + result (.compile cc (url->nio-path rsc) source)] + (.source result)) + source)] + (cond-> source' + (= :goog module) add-goog-load))) (comment (println (slurp (io/resource "goog/math/long.js"))) + (deps/parse-js-ns (-> (io/resource "goog/math/long.js") io/reader line-seq)) - (url->nio-path (io/resource "goog/math/long.js") ) + (deps/parse-js-ns (-> (io/resource "goog/string/stringbuffer.js") io/reader line-seq)) + + (url->nio-path (io/resource "goog/math/long.js")) + (println - (maybe-transpile-goog-module (io/resource "goog/math/long.js") {:module :goog})) + (maybe-transpile {} (io/resource "goog/math/long.js") {:module :goog :lang :es6})) ) +(defn transpile? [opts {:keys [module lang]}] + (or module lang)) + (defn write-javascript "Write or copy a JavaScript file to output directory. Only write if the file does not already exist. Return IJavaScript for the file on disk at the new location." - [opts js] + [{:keys [optimizations] :as opts} js] (let [out-dir (io/file (util/output-directory opts)) out-name (rel-output-path js opts) out-file (io/file out-dir out-name) res (or (:url js) (:source-file js)) js-module? (and res out-dir (.startsWith (util/path res) (util/path out-dir))) ;; We already Closure processed it and wrote it out + transpile? (transpile? opts js) ijs (merge {:requires (deps/-requires js) :provides (deps/-provides js) :group (:group js)} - (when (not js-module?) + (when-not js-module? {:url (deps/to-url out-file) :out-file (.toString out-file)}))] (when (and (not js-module?) (or (not (.exists out-file)) + ;; no caching yet for GCL files that need transpilation + transpile? (and res (util/changed? out-file res)))) (when (and res (or ana/*verbose* (:verbose opts))) (util/debug-prn "Copying JS" (str res) "to" (str out-file))) (util/mkdirs out-file) - (if (= :goog (:module js)) - (spit out-file (maybe-transpile-goog-module res js)) + (if (and (= :none optimizations) transpile?) + (spit out-file (transpile opts res js)) (spit out-file (deps/-source js))) (when res (.setLastModified ^File out-file (util/last-modified res)))) From 872e46a70081178ce9be530235caf8d5ae3d5c4a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Mar 2020 08:40:05 -0400 Subject: [PATCH 3498/4033] remove logging change --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5fa8beb5f..4a73bdd2e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2085,7 +2085,7 @@ transpile? (and res (util/changed? out-file res)))) (when (and res (or ana/*verbose* (:verbose opts))) - (util/debug-prn "Copying JS" (str res) "to" (str out-file))) + (util/debug-prn "Copying" (str res) "to" (str out-file))) (util/mkdirs out-file) (if (and (= :none optimizations) transpile?) (spit out-file (transpile opts res js)) From 814b723b238412f1f8d05104603daae99a9caf93 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 27 Mar 2020 08:57:12 -0400 Subject: [PATCH 3499/4033] revert disk-sources deps-file generation change in output-unoptimized --- src/main/clojure/cljs/closure.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4a73bdd2e..910bdfc40 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2154,14 +2154,14 @@ libraries." [{:keys [modules] :as opts} & sources] ;; this source-on-disk call is currently necessary for REPLs - David - (doall (map #(source-on-disk opts %) sources)) - (let [goog-deps (io/file (util/output-directory opts) "goog" "deps.js") + (let [disk-sources (doall (map #(source-on-disk opts %) sources)) + goog-deps (io/file (util/output-directory opts) "goog" "deps.js") main (:main opts) output-deps #(output-deps-file (assoc opts :output-to (str (util/output-directory opts) File/separator "cljs_deps.js")) - sources)] + disk-sources)] (util/mkdirs goog-deps) (spit goog-deps (slurp (io/resource "goog/deps.js"))) (when (:debug-inputs opts) @@ -2185,7 +2185,7 @@ (output-deps) (output-main-file opts)) - :else (output-deps-file opts sources)))) + :else (output-deps-file opts disk-sources)))) (defn get-upstream-deps* "returns a merged map containing all upstream dependencies defined From f227fadb0f234cbd3c04110c1ce76abf740f283f Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 28 Mar 2020 11:43:43 -0400 Subject: [PATCH 3500/4033] CLJS-3214: script/uberjar fails with GraalJS --- src/main/clojure/cljs/server/graaljs.clj | 27 ------------------------ 1 file changed, 27 deletions(-) delete mode 100644 src/main/clojure/cljs/server/graaljs.clj diff --git a/src/main/clojure/cljs/server/graaljs.clj b/src/main/clojure/cljs/server/graaljs.clj deleted file mode 100644 index d33360518..000000000 --- a/src/main/clojure/cljs/server/graaljs.clj +++ /dev/null @@ -1,27 +0,0 @@ -; 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.server.graaljs - (:require [cljs.repl :as repl] - [cljs.repl.graaljs :as graaljs] - [cljs.core.server :as server])) - -(defn repl - ([] - (repl nil)) - ([{:keys [opts env-opts]}] - (repl/repl* (graaljs/repl-env* env-opts) opts))) - -(defn prepl - ([] - (prepl nil)) - ([{:keys [opts env-opts]}] - (apply server/io-prepl - (mapcat identity - {:repl-env (graaljs/repl-env* env-opts) - :opts opts})))) From bdee30854d905a4587bfc6f804d03195912092be Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 20 Jun 2019 20:22:31 -0400 Subject: [PATCH 3501/4033] CLJS-3119: get with negative ndx on string inconsistent with Clojure --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ed0b8101b..4b29d9358 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1945,7 +1945,7 @@ reduces them without incurring seq initialization" (aget o (int k))) (string? o) - (when (and (some? k) (< k (.-length o))) + (when (and (some? k) (< -1 k (.-length o))) (.charAt o (int k))) (native-satisfies? ILookup o) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index e2ee44efc..845f29ff0 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1804,6 +1804,10 @@ (aset a 0 :modified) (is (= :original (v 0))))) +(deftest test-cljs-3119 + (is (= "a" (get "abc" -0.5))) + (is (nil? (get "abc" -1)))) + (deftest test-cljs-3202 (is (= :/ (keyword "/"))) (is (= (hash :/) (hash (keyword "/"))))) \ No newline at end of file From d3f6e57746aa37a804eadb2ce63dafc6b485d9aa Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 29 Mar 2020 15:59:53 -0400 Subject: [PATCH 3502/4033] CLJS-3219: Problem with asserts namespace inside goog.math.Long move more shareable logic into cljs.repl.bootstrap - do the goog.isProvided_ monkeypatch there - go ahead and keep a reference to the original just in case we need it in the future. tweak goog.require monkey patch to check if we're in the goog.module loader and in that case return the module. This is only for goog.module - nothing we would ever generate in ClojureScript --- src/main/clojure/cljs/repl/bootstrap.clj | 14 ++++++++++++-- src/main/clojure/cljs/repl/node.clj | 3 --- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/repl/bootstrap.clj b/src/main/clojure/cljs/repl/bootstrap.clj index 1d7a8c39e..f54b1d2a9 100644 --- a/src/main/clojure/cljs/repl/bootstrap.clj +++ b/src/main/clojure/cljs/repl/bootstrap.clj @@ -13,9 +13,16 @@ "Install a version of goog.require that supports namespace reloading. IMPORTANT: must be invoked *after* loading cljs.core." [repl-env env] + ;; monkey patch goog.provide - it throws when namespaces are loaded multiple times + ;; we never care how many times a namespace is loaded it doesn't matter if + ;; Google Closure Library or ClojureScript (repl/evaluate-form repl-env env "" - '(set! (.-require__ js/goog) js/goog.require)) + '(set! (.-isProvided__ js/goog) js/goog.isProvided_)) + (repl/evaluate-form repl-env env "" + '(set! (.-isProvided_ js/goog) (fn [x] false))) ;; monkey-patch goog.require + (repl/evaluate-form repl-env env "" + '(set! (.-require__ js/goog) js/goog.require)) (repl/evaluate-form repl-env env "" '(set! (.-require js/goog) (fn [src reload] @@ -38,4 +45,7 @@ (let [ret (.require__ js/goog src)] (when (= reload "reload-all") (set! (.-cljsReloadAll_ js/goog) false)) - ret)))))) \ No newline at end of file + ;; handle requires from Closure Library goog.modules + (if (js/goog.isInModuleLoader_) + (js/goog.module.getInternal_ src) + ret))))))) diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index b6319cc3f..03f6335f2 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -192,9 +192,6 @@ (str "require(" (platform-path (conj root-path "node_repl_deps.js")) ")")) - ;; monkey-patch isProvided_ to avoid useless warnings - David - (node-eval repl-env - (str "goog.isProvided_ = function(x) { return false; };")) ;; load cljs.core, setup printing (repl/evaluate-form repl-env env "" '(do From b057116111705a07a0a0ace57c41b12681cbffb9 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 28 Mar 2020 13:13:14 -0400 Subject: [PATCH 3503/4033] CLJS-3215: Travis has remnant CLI test involving GraalJS --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7ee4c3809..b5b47f05a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,5 +62,3 @@ script: - script/test-cli node | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - PATH=`pwd`/graalvm-ce-1.0.0-rc12/bin:$PATH script/test-cli graaljs | tee test-out.txt - - grep '0 failures, 0 errors.' test-out.txt From 5959b6bc57871c8dda505db9939b0da11e1877db Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 28 Mar 2020 13:42:12 -0400 Subject: [PATCH 3504/4033] CLJS-3217: script/test-self-parity compilation failure stale reference to goog.text.LoremIpsum --- src/test/self/self_parity/auxiliary.cljs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs index 36e1f06bb..3d0f69392 100644 --- a/src/test/self/self_parity/auxiliary.cljs +++ b/src/test/self/self_parity/auxiliary.cljs @@ -147,5 +147,4 @@ goog.structs.SimplePool goog.structs.StringSet goog.structs.TreeNode - goog.structs.Trie - goog.text.LoremIpsum)) + goog.structs.Trie)) From faa98a5f9b25d670110d99f1ed47c2a818cdd3a4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 29 Mar 2020 16:10:34 -0400 Subject: [PATCH 3505/4033] CLJS-3218: NPE during Closure transpilation in self-host tests check for :lang before trying to compare --- src/main/clojure/cljs/closure.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 910bdfc40..83d638bcc 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2036,8 +2036,9 @@ (defn transpile [{:keys [language-out] :or {language-out :es3}} rsc {:keys [module lang] :as js}] (let [source (slurp rsc) - source' (if (< (.indexOf lang-level (expand-lang-key language-out)) - (.indexOf lang-level (expand-lang-key lang))) + source' (if (and lang + (< (.indexOf lang-level (expand-lang-key language-out)) + (.indexOf lang-level (expand-lang-key lang)))) (let [cc (Transpiler/compilerSupplier) result (.compile cc (url->nio-path rsc) source)] (.source result)) From 072c9e4d409bd27fdf72fda7655348cbbe41ee94 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 29 Mar 2020 18:12:27 -0400 Subject: [PATCH 3506/4033] CLJS-3220: Self-host test-import failing on asserts.assert --- src/test/self/self_parity/test.cljs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 4ed5928c1..7765e5d33 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -30,6 +30,17 @@ "src/main/clojure" "src/test/cljs"]) +(defn require* + [name reload] + (let [ret (js/CLOSURE_IMPORT_SCRIPT + (if goog/debugLoader_ + (.getPathFromDeps_ goog/debugLoader_ name) + (gobj/get (.. js/goog -dependencies_ -nameToPath) name)))] + ;; handle requires from Closure Library goog.modules + (if (.isInModuleLoader_ js/goog) + (.getInternal_ (.. js/goog -module) name) + ret))) + (defn init-runtime "Initializes the runtime so that we can use the cljs.user namespace and so that Google Closure is set up to work @@ -39,12 +50,7 @@ ;; monkey-patch isProvided_ to avoid useless warnings (js* "goog.isProvided_ = function(x) { return false; };") ;; monkey-patch goog.require, skip all the loaded checks - (set! (.-require js/goog) - (fn [name] - (js/CLOSURE_IMPORT_SCRIPT - (if goog/debugLoader_ - (.getPathFromDeps_ goog/debugLoader_ name) - (gobj/get (.. js/goog -dependencies_ -nameToPath) name))))) + (set! (.-require js/goog) require*) ;; setup printing (nodejs/enable-util-print!) ;; redef goog.require to track loaded libs @@ -53,10 +59,7 @@ (fn [name reload] (when (or (not (contains? *loaded-libs* name)) reload) (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) - (js/CLOSURE_IMPORT_SCRIPT - (if goog/debugLoader_ - (.getPathFromDeps_ goog/debugLoader_ name) - (gobj/get (.. js/goog -dependencies_ -nameToPath) name))))))) + (require* name reload))))) ;; Node file reading fns From f8b5e6a2b685ce705de8d9a70a61d806fc57b16a Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 09:53:08 -0400 Subject: [PATCH 3507/4033] typo in handle-js-modules docstring --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 83d638bcc..a2c7f9fd2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2876,7 +2876,7 @@ (defn handle-js-modules "Given all Cljs sources (build inputs and dependencies in classpath) - - index all the node node modules + - index all the node modules - process the JS modules (preprocess + convert to Closure JS) - save js-dependency-index for compilation" [{:keys [npm-deps target] :as opts} js-sources compiler-env] From eab8eeb971c9a8186e2a7aa8bf59f5d2b91deb5f Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 14:39:14 -0400 Subject: [PATCH 3508/4033] add public cljs.build.api/compilable->ijs helper --- src/main/clojure/cljs/build/api.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 4d10521e4..032a64fe8 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -131,6 +131,14 @@ ([ns compiler-env] (closure/source-for-namespace ns compiler-env))) +(defn compilable->ijs + "Given a cljs.closure/Compilable value, return the corresponding + cljs.closure/IJavaScript value." + ([x] + (compilable->ijs x {})) + ([x opts] + (closure/-find-sources x opts))) + (defn add-dependencies "DEPRECATED: Given one or more IJavaScript objects in dependency order, produce a new sequence of IJavaScript objects which includes the input list From 102fa19867e57005ec1c2404e0c2ef737a211ec4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 14:53:48 -0400 Subject: [PATCH 3509/4033] add internal compiler-state helper, add cljs.build.api/add-dependency-sources --- src/main/clojure/cljs/build/api.clj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 032a64fe8..dcf5ee23c 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -23,6 +23,11 @@ [cljs.js-deps :as js-deps]) (:import [java.io File])) +(defn- compiler-state [] + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env))) + ;; ============================================================================= ;; Useful Utilities @@ -139,6 +144,17 @@ ([x opts] (closure/-find-sources x opts))) +(defn add-dependency-sources + "Given a sequence of cljs.closure/IJavaSript values, return a set that includes + all dependencies." + ([xs] + (add-dependency-sources xs {})) + ([xs opts] + (add-dependency-sources (compiler-state) xs opts)) + ([state xs opts] + (env/with-compiler-env state + (closure/add-dependency-sources xs opts)))) + (defn add-dependencies "DEPRECATED: Given one or more IJavaScript objects in dependency order, produce a new sequence of IJavaScript objects which includes the input list From 32a6383067d5b2ccf179ea921643247ec428073b Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 14:54:53 -0400 Subject: [PATCH 3510/4033] typo --- src/main/clojure/cljs/build/api.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index dcf5ee23c..5489a7f86 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -145,7 +145,7 @@ (closure/-find-sources x opts))) (defn add-dependency-sources - "Given a sequence of cljs.closure/IJavaSript values, return a set that includes + "Given a sequence of cljs.closure/IJavaScript values, return a set that includes all dependencies." ([xs] (add-dependency-sources xs {})) From 975d41f2637250c177b77d7cc137aa6d5edde43f Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 15:07:04 -0400 Subject: [PATCH 3511/4033] make compiler-state public, document, refactor existing fns to use it --- src/main/clojure/cljs/build/api.clj | 54 ++++++++++++----------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 5489a7f86..c85443a1a 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -23,14 +23,20 @@ [cljs.js-deps :as js-deps]) (:import [java.io File])) -(defn- compiler-state [] - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env))) - ;; ============================================================================= ;; Useful Utilities +(defn compiler-state + "Return a compiler state that can be used with the api. opts is a map + representing the compiler configuration. See the documentation + for details: https://clojurescript.org/reference/compiler-options" + ([] + (compiler-state nil)) + ([opts] + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env opts)))) + (defn ^File target-file-for-cljs-ns "Given an output directory and a clojurescript namespace return the compilation target file for that namespace. @@ -59,9 +65,7 @@ ('example.core 'example.util)" ([namespaces] (closure/cljs-dependents-for-macro-namespaces - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env)) + (compiler-state) namespaces)) ([state namespaces] (closure/cljs-dependents-for-macro-namespaces state namespaces))) @@ -79,9 +83,7 @@ ([src] (src-file->target-file src nil)) ([src opts] (src-file->target-file - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) + (compiler-state opts) src opts)) ([state src opts] (env/with-compiler-env state @@ -94,9 +96,7 @@ ([src] (src-file->goog-require src nil)) ([src options] (src-file->goog-require - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env options)) + (compiler-state options) src options)) ([state src options] (env/with-compiler-env state @@ -130,9 +130,7 @@ :uri a URL." ([ns] (ns->location ns - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env)))) + (compiler-state))) ([ns compiler-env] (closure/source-for-namespace ns compiler-env))) @@ -192,9 +190,7 @@ "Given a Compilable, compile it and return an IJavaScript." ([opts compilable] (compile - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) + (compiler-state opts) opts compilable)) ([state opts compilable] (env/with-compiler-env state @@ -217,13 +213,11 @@ (build nil opts)) ([source opts] (build source opts - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env - ;; need to dissoc :foreign-libs since we won't know what overriding - ;; foreign libspecs are referring to until after add-implicit-options - ;; - David - (closure/add-externs-sources (dissoc opts :foreign-libs)))))) + (compiler-state + ;; need to dissoc :foreign-libs since we won't know what overriding + ;; foreign libspecs are referring to until after add-implicit-options + ;; - David + (closure/add-externs-sources (dissoc opts :foreign-libs))))) ([source opts compiler-env] (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)] (when suggested-opt @@ -235,10 +229,8 @@ "Given a source which can be compiled, watch it for changes to produce." ([source opts] (watch source opts - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env - (closure/add-externs-sources opts))))) + (compiler-state + (closure/add-externs-sources opts)))) ([source opts compiler-env] (watch source opts compiler-env nil)) ([source opts compiler-env stop] From 4d447d278d7b75b3131ea59e896e9a6ef44ef238 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 15:19:43 -0400 Subject: [PATCH 3512/4033] remove unused nses, remove comments --- src/main/clojure/cljs/build/api.clj | 40 ++--------------------------- 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index c85443a1a..056b40d2d 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -5,6 +5,7 @@ ; 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.build.api "This is intended to be a stable api for those who need programmatic access to ClojureScript's project building facilities. @@ -13,14 +14,10 @@ files so that they will be recompiled." (:refer-clojure :exclude [compile]) (:require [clojure.java.io :as io] - [clojure.string :as string] - [clojure.data.json :as json] [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] - [cljs.compiler :as comp] - [cljs.closure :as closure] - [cljs.js-deps :as js-deps]) + [cljs.closure :as closure]) (:import [java.io File])) ;; ============================================================================= @@ -283,14 +280,6 @@ (distinct (concat (keys (:npm-deps opts)) (map str dependencies))) opts))) -(comment - (node-module-deps - {:file (.getAbsolutePath (io/file "src/test/node/test.js"))}) - - (node-module-deps - {:file (.getAbsolutePath (io/file "src/test/node/test.js"))}) - ) - (defn node-inputs "EXPERIMENTAL: return the foreign libs entries as computed by running the module-deps package on the supplied JavaScript entry points. Assumes @@ -302,28 +291,3 @@ (:options @env/*compiler*)))) ([entries opts] (closure/node-inputs entries opts))) - -(comment - (node-inputs - [{:file "src/test/node/test.js"}]) - ) - -(comment - (def test-cenv (atom {})) - (def test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) - - (binding [ana/*cljs-ns* 'cljs.user] - (env/with-compiler-env test-cenv - (ana/no-warn - (ana/analyze test-env - '(ns cljs.user - (:use [clojure.string :only [join]])))))) - - (env/with-compiler-env test-cenv - (ns-dependents 'clojure.string)) - - (map - #(target-file-for-cljs-ns % "out-dev") - (env/with-compiler-env test-cenv - (ns-dependents 'clojure.string))) - ) From def033769b5440911fccb13c7b7c0d47eb2a0137 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 16:00:18 -0400 Subject: [PATCH 3513/4033] add index-ijs, node-modules helpers to cljs.build.api --- src/main/clojure/cljs/build/api.clj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 056b40d2d..82ebdfdc4 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -100,6 +100,16 @@ (binding [ana/*cljs-warning-handlers* (:warning-handlers options ana/*cljs-warning-handlers*)] (closure/src-file->goog-require src options))))) +(defn index-ijs + "Given a sequence of cljs.closure/IJavaScript values, create an index using + :provides. The original values will appear under each :provide." + [xs] + (reduce + (fn [index x] + (merge index + (zipmap (:provides x) (repeat x)))) + {} xs)) + ;; ============================================================================= ;; Main API @@ -291,3 +301,9 @@ (:options @env/*compiler*)))) ([entries opts] (closure/node-inputs entries opts))) + +(defn node-modules + "Return a sequence of requirable libraries found under node_modules." + [state] + (env/with-compiler-env state + (filter :provides (closure/index-node-modules-dir)))) From 0e7ab3552906c810ecd1f2d716e4480d8ccb1fe3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 16:03:16 -0400 Subject: [PATCH 3514/4033] construct compiler state w/ opts --- src/main/clojure/cljs/build/api.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 82ebdfdc4..2469a1c59 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -155,7 +155,7 @@ ([xs] (add-dependency-sources xs {})) ([xs opts] - (add-dependency-sources (compiler-state) xs opts)) + (add-dependency-sources (compiler-state opts) xs opts)) ([state xs opts] (env/with-compiler-env state (closure/add-dependency-sources xs opts)))) From 465acf8fba60cb46dc4688a57df8a08942be27d6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 30 Mar 2020 17:56:39 -0400 Subject: [PATCH 3515/4033] expand -re so that any namespace providing repl-env fn can be used instead --- src/main/cljs/cljs/main.clj | 16 ++++++++++------ src/main/clojure/cljs/cli.clj | 4 +++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index 605afbe43..6950a948a 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -8,16 +8,20 @@ (ns cljs.main (:require [cljs.repl.browser :as browser] - [cljs.cli :as cli]) + [cljs.cli :as cli] + [clojure.string :as string]) (:gen-class)) +(defn single-segment? [x] + (== 1 (count (string/split x #"\.")))) + (defn- get-js-opt [args] (if (= 2 (count args)) - (let [repl-ns (symbol - (str "cljs.repl." - (if (= 1 (count args)) - "browser" - (nth args 1))))] + (let [ns-frag (nth args 1) + repl-ns (symbol + (cond->> ns-frag + (single-segment? ns-frag) + (str "cljs.repl.")))] (try (require repl-ns) (if-let [repl-env (ns-resolve repl-ns 'repl-env)] diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index e3b0a2e3b..c6b9213ef 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -546,7 +546,9 @@ present" {:arg "env" :doc (str "The REPL environment to use. Built-in " "supported values: node, browser. " - "Defaults to browser")}}} + "Defaults to browser. If given a " + "non-single-segment namespace, will " + "use the repl-env fn found there.")}}} ::main {:desc "init options only for --main and --repl"} ::compile {:desc "init options only for --compile"}} :init From 1099debd767f6953f1055ffffb678f10ae4cc581 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 31 Mar 2020 10:10:08 -0400 Subject: [PATCH 3516/4033] change node-modules to take opts, not state --- src/main/clojure/cljs/build/api.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 2469a1c59..2ae4246f0 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -304,6 +304,6 @@ (defn node-modules "Return a sequence of requirable libraries found under node_modules." - [state] - (env/with-compiler-env state - (filter :provides (closure/index-node-modules-dir)))) + ([opts] + (env/with-compiler-env (compiler-state opts) + (filter :provides (closure/index-node-modules-dir))))) From e860a7c4d6a0bcce21de19ee305cc7a78ce36107 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 31 Mar 2020 10:53:23 -0400 Subject: [PATCH 3517/4033] refactor api nses to consistently use cljs.analyzer.api/empty-state or a new current|empty-state helpers --- src/main/clojure/cljs/analyzer/api.cljc | 39 ++++++++++---------- src/main/clojure/cljs/build/api.clj | 49 ++++++++----------------- src/main/clojure/cljs/compiler/api.clj | 34 ++++------------- 3 files changed, 42 insertions(+), 80 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 8e444c573..382f45a07 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -18,9 +18,22 @@ ;; Useful Utilities (defn empty-state - "Creates an empty compilation state Atom." + "Creates an empty compilation state Atom. The optional opts arg is a map + representing the compiler configuration. See the documentation + for details: https://clojurescript.org/reference/compiler-options" + ([] + (if-not (nil? env/*compiler*) + env/*compiler* + (env/default-compiler-env))) + ([opts] + (env/default-compiler-env opts))) + +(defn current|empty-state + "Returns the currently bound compiler state or an empty one" [] - (env/default-compiler-env)) + (if-not (nil? env/*compiler*) + env/*compiler* + (empty-state))) (defmacro with-state "Run the body with the given compilation state Atom." @@ -77,11 +90,7 @@ ([env form] (analyze env form nil)) ([env form name] (analyze env form name nil)) ([env form name opts] - (analyze - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - env form name opts)) + (analyze (current|empty-state) env form name opts)) ([state env form name opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -99,7 +108,7 @@ "Helper for parsing only the essential namespace information from a ClojureScript source file and returning a cljs.closure/IJavaScript compatible map _not_ a namespace AST node. - + By default does not load macros or perform any analysis of dependencies. If opts parameter provided :analyze-deps and :load-macros keys their values will be used for *analyze-deps* and *load-macros* bindings respectively. This @@ -108,11 +117,7 @@ ([src] (parse-ns src nil nil)) ([src opts] (parse-ns src nil opts)) ([src dest opts] - (parse-ns - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - src dest opts)) + (parse-ns (current|empty-state) src dest opts)) ([state src dest opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -121,7 +126,7 @@ (defn analyze-file "Given a java.io.File, java.net.URL or a string identifying a resource on the classpath attempt to analyze it. - + This function side-effects the ambient compilation environment `cljs.env/*compiler*` to aggregate analysis information. opts argument is compiler options, if :cache-analysis true will cache analysis to @@ -129,11 +134,7 @@ meaningful value." ([f] (analyze-file f nil)) ([f opts] - (analyze-file - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - f opts)) + (analyze-file (current|empty-state) f opts)) ([state f opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 2ae4246f0..949a985a5 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -17,23 +17,13 @@ [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] [cljs.closure :as closure]) (:import [java.io File])) ;; ============================================================================= ;; Useful Utilities -(defn compiler-state - "Return a compiler state that can be used with the api. opts is a map - representing the compiler configuration. See the documentation - for details: https://clojurescript.org/reference/compiler-options" - ([] - (compiler-state nil)) - ([opts] - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)))) - (defn ^File target-file-for-cljs-ns "Given an output directory and a clojurescript namespace return the compilation target file for that namespace. @@ -62,8 +52,7 @@ ('example.core 'example.util)" ([namespaces] (closure/cljs-dependents-for-macro-namespaces - (compiler-state) - namespaces)) + (ana-api/current|empty-state) namespaces)) ([state namespaces] (closure/cljs-dependents-for-macro-namespaces state namespaces))) @@ -79,9 +68,7 @@ provide build options with :output-dir specified." ([src] (src-file->target-file src nil)) ([src opts] - (src-file->target-file - (compiler-state opts) - src opts)) + (src-file->target-file (ana-api/current|empty-state) src opts)) ([state src opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -91,14 +78,12 @@ "Given a ClojureScript or Google Closure style JavaScript source file return the goog.require statement for it." ([src] (src-file->goog-require src nil)) - ([src options] - (src-file->goog-require - (compiler-state options) - src options)) - ([state src options] + ([src opts] + (src-file->goog-require (ana-api/current|empty-state) src opts)) + ([state src opts] (env/with-compiler-env state - (binding [ana/*cljs-warning-handlers* (:warning-handlers options ana/*cljs-warning-handlers*)] - (closure/src-file->goog-require src options))))) + (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] + (closure/src-file->goog-require src opts))))) (defn index-ijs "Given a sequence of cljs.closure/IJavaScript values, create an index using @@ -136,8 +121,7 @@ .cljs, .cljc, .js. Returns a map containing :relative-path a string, and :uri a URL." ([ns] - (ns->location ns - (compiler-state))) + (ns->location ns (ana-api/current|empty-state))) ([ns compiler-env] (closure/source-for-namespace ns compiler-env))) @@ -155,7 +139,7 @@ ([xs] (add-dependency-sources xs {})) ([xs opts] - (add-dependency-sources (compiler-state opts) xs opts)) + (add-dependency-sources (ana-api/current|empty-state) xs opts)) ([state xs opts] (env/with-compiler-env state (closure/add-dependency-sources xs opts)))) @@ -196,9 +180,7 @@ (defn compile "Given a Compilable, compile it and return an IJavaScript." ([opts compilable] - (compile - (compiler-state opts) - opts compilable)) + (compile (ana-api/current|empty-state) opts compilable)) ([state opts compilable] (env/with-compiler-env state (closure/compile compilable opts)))) @@ -220,7 +202,7 @@ (build nil opts)) ([source opts] (build source opts - (compiler-state + (ana-api/empty-state ;; need to dissoc :foreign-libs since we won't know what overriding ;; foreign libspecs are referring to until after add-implicit-options ;; - David @@ -236,7 +218,7 @@ "Given a source which can be compiled, watch it for changes to produce." ([source opts] (watch source opts - (compiler-state + (ana-api/empty-state (closure/add-externs-sources opts)))) ([source opts compiler-env] (watch source opts compiler-env nil)) @@ -297,13 +279,12 @@ installed." ([entries] (node-inputs entries - (when-not (nil? env/*compiler*) - (:options @env/*compiler*)))) + (:options (ana-api/current|empty-state)))) ([entries opts] (closure/node-inputs entries opts))) (defn node-modules "Return a sequence of requirable libraries found under node_modules." ([opts] - (env/with-compiler-env (compiler-state opts) + (env/with-compiler-env (ana-api/empty-state opts) (filter :provides (closure/index-node-modules-dir))))) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index 811a60289..7a01890d5 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -12,6 +12,7 @@ (:require [cljs.util :as util] [cljs.env :as env] [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] [cljs.compiler :as comp] [cljs.closure :as closure])) @@ -21,11 +22,7 @@ (defn emit "Given an AST node generated by the analyzer emit JavaScript as a string." ([ast] - (emit - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env)) - ast)) + (emit (ana-api/current|empty-state) ast)) ([state ast] (env/with-compiler-env state (with-out-str @@ -35,15 +32,10 @@ "Ensure that core.cljs has been loaded." ([] (comp/with-core-cljs - (when env/*compiler* - (:options @env/*compiler*)))) + (:options (ana-api/current|empty-state)))) ([opts] (with-core-cljs opts (fn []))) ([opts body] - (with-core-cljs - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - opts body)) + (with-core-cljs (ana-api/current|empty-state) opts body)) ([state opts body] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -53,11 +45,7 @@ "Return true if the src file requires compilation." ([src dest] (requires-compilation? src dest nil)) ([src dest opts] - (requires-compilation? - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - src dest opts)) + (requires-compilation? (ana-api/current|empty-state) src dest opts)) ([state src dest opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -81,11 +69,7 @@ ([src dest] (compile-file src dest nil)) ([src dest opts] - (compile-file - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - src dest opts)) + (compile-file (ana-api/current|empty-state) src dest opts)) ([state src dest opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -105,11 +89,7 @@ ([src-dir] (compile-root src-dir "out")) ([src-dir target-dir] (compile-root src-dir target-dir nil)) ([src-dir target-dir opts] - (compile-root - (if-not (nil? env/*compiler*) - env/*compiler* - (env/default-compiler-env opts)) - src-dir target-dir opts)) + (compile-root (ana-api/current|empty-state) src-dir target-dir opts)) ([state src-dir target-dir opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] From c057c92bd20bb4bea61970fb87247582ae2f5423 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 31 Mar 2020 12:28:54 -0400 Subject: [PATCH 3518/4033] CLJS-1628: Make instances of js/Symbol printable --- src/main/cljs/cljs/core.cljs | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4b29d9358..205dcd1b1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -529,6 +529,13 @@ [obj s & args] (.apply (unchecked-get obj s) obj (into-array args))) +(defn js-symbol? + "Returns true if x is an instance of Symbol" + [x] + (or (identical? (goog/typeOf x) "symbol") + (and (exists? js/Symbol) + (instance? js/Symbol x)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;; core protocols ;;;;;;;;;;;;; (defprotocol Fn @@ -1715,7 +1722,7 @@ reduces them without incurring seq initialization" (if (pos? i) (RSeq. ci (dec i) nil) ())) - + INext (-next [coll] (when (pos? i) @@ -1822,7 +1829,7 @@ reduces them without incurring seq initialization" (array? coll) (alength coll) - + (string? coll) ^number (.-length coll) @@ -2539,7 +2546,7 @@ reduces them without incurring seq initialization" (array? coll) (array-reduce coll f val) - + (string? coll) (array-reduce coll f val) @@ -3271,7 +3278,7 @@ reduces them without incurring seq initialization" ISeqable (-seq [coll] coll) - + IReduce (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) @@ -3306,7 +3313,7 @@ reduces them without incurring seq initialization" (toString [_] (str ":" fqn)) (equiv [this other] (-equiv this other)) - + IEquiv (-equiv [_ other] (if (instance? Keyword other) @@ -4920,7 +4927,7 @@ reduces them without incurring seq initialization" IPending (-realized? [coll] false) - + IWithMeta (-with-meta [coll new-meta] (if (identical? new-meta meta) @@ -4968,7 +4975,7 @@ reduces them without incurring seq initialization" ISequential ISeqable (-seq [coll] coll) - + IEquiv (-equiv [coll other] (equiv-sequential coll other)) @@ -6961,13 +6968,13 @@ reduces them without incurring seq initialization" @init (recur (+ i 2) init))) init)))) - + IReduce (-reduce [coll f] (iter-reduce coll f)) (-reduce [coll f start] (iter-reduce coll f start)) - + IFn (-invoke [coll k] (-lookup coll k)) @@ -7108,7 +7115,7 @@ reduces them without incurring seq initialization" (set! len (- len 2))) tcoll) (throw (js/Error. "dissoc! after persistent!")))) - + IFn (-invoke [tcoll key] (-lookup tcoll key nil)) @@ -8974,7 +8981,7 @@ reduces them without incurring seq initialization" IHash (-hash [coll] (hash-ordered-coll coll)) - + ISeq (-first [coll] (let [^not-native me (-first mseq)] @@ -9523,7 +9530,7 @@ reduces them without incurring seq initialization" (defn max-key "Returns the x for which (k x), a number, is greatest. - + If there are multiple such xs, the last one is returned." ([k x] x) ([k x y] (if (> (k x) (k y)) x y)) @@ -10167,6 +10174,8 @@ reduces them without incurring seq initialization" (regexp? obj) (write-all writer "#\"" (.-source obj) "\"") + (js-symbol? obj) (write-all writer "#object[" (.toString obj) "]" ) + :else (if (some-> obj .-constructor .-cljs$lang$ctorStr) (write-all writer @@ -10461,7 +10470,7 @@ reduces them without incurring seq initialization" (if (vector? y) (compare-indexed x y) (throw (js/Error. (str "Cannot compare " x " to " y))))) - + PersistentVector (-compare [x y] (if (vector? y) From 3b83460581508ca4d17a942dfcd03fda8b06cf3c Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2020 11:43:30 -0400 Subject: [PATCH 3519/4033] add a way to remove the Node.js runtime support (namespaces helpers, printing and cli) --- src/main/clojure/cljs/closure.clj | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a2c7f9fd2..9c02c324f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -204,7 +204,8 @@ :fn-invoke-direct :checked-arrays :closure-module-roots :rewrite-polyfills :use-only-custom-externs :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}) + :stable-names :ignore-js-module-exts :opts-cache :aot-cache :elide-strict :fingerprint :spec-skip-macros + :nodejs-rt}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -1702,7 +1703,8 @@ (if-let [entries (when module (:entries module))] entries [(:main opts)]))) - "goog.require(\"cljs.nodejscli\");\n"))) + (when (:nodejs-rt opts) + "goog.require(\"cljs.nodejscli\");\n")))) :webworker (output-one-file @@ -2469,6 +2471,11 @@ (assoc-in [:closure-defines (str (comp/munge 'cljs.core/*target*))] (name (:target opts))) + (= :nodejs (:target opts)) + (merge + (when (nil? (:nodejs-rt opts)) + {:nodejs-rt true})) + (= optimizations :none) (assoc :cache-analysis (:cache-analysis opts true) @@ -3065,7 +3072,9 @@ (compile-sources compiler-stats compile-opts) (#(map add-core-macros-if-cljs-js %)) (add-js-sources opts) - (cond-> (= :nodejs (:target opts)) + (cond-> + (and (= :nodejs (:target opts)) + (:nodejs-rt opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") (assoc opts :output-file "nodejs.js"))])) @@ -3073,7 +3082,9 @@ (add-preloads opts) remove-goog-base add-goog-base - (cond-> (= :nodejs (:target opts)) + (cond-> + (and (= :nodejs (:target opts)) + (:nodejs-rt opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") (assoc opts :output-file "nodejscli.js"))])) From 69c80bf4cec272a705a7dfeb4e0d676162d46ff6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2020 12:22:07 -0400 Subject: [PATCH 3520/4033] add new compiler option :target-fn which can be used to bootstrap some arbitrary JS environment --- src/main/clojure/cljs/closure.clj | 138 ++++++++++++++---------------- 1 file changed, 66 insertions(+), 72 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9c02c324f..726863d76 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -205,7 +205,7 @@ :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}) + :nodejs-rt :target-fn}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -1676,77 +1676,71 @@ (assert (or (not (contains? opts :module-name)) (get (:modules opts) (:module-name opts))) (str "Module " (:module-name opts) " does not exist")) - (let [module (get (:modules opts) (:module-name opts)) - asset-path (or (:asset-path opts) - (util/output-directory opts)) - closure-defines (json/write-str (:closure-defines opts))] - (case (:target opts) - :nodejs - (output-one-file - (merge opts - (when module - {:output-to (:output-to module)})) - (add-header opts - (str (when (or (not module) (= :cljs-base (:module-name opts))) - (str "var path = require(\"path\");\n" - "try {\n" - " require(\"source-map-support\").install();\n" - "} catch(err) {\n" - "}\n" - "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" - "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" - "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - (apply str (preloads (:preloads opts))))) - (apply str - (map (fn [entry] - (str "goog.require(\"" (comp/munge entry) "\");\n")) - (if-let [entries (when module (:entries module))] - entries - [(:main opts)]))) - (when (:nodejs-rt opts) - "goog.require(\"cljs.nodejscli\");\n")))) - - :webworker - (output-one-file - (merge opts - (when module - {:output-to (:output-to module)})) - (str (when (or (not module) (= :cljs-base (:module-name opts))) - (str "var CLOSURE_BASE_PATH = \"" asset-path "/goog/\";\n" - "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - "var CLOSURE_IMPORT_SCRIPT = (function(global) { return function(src) {global['importScripts'](src); return true;};})(this);\n" - "if(typeof goog == 'undefined') importScripts(\"" asset-path "/goog/base.js\");\n" - "importScripts(\"" asset-path "/cljs_deps.js\");\n" - (apply str (preloads (:preloads opts))))) - (apply str - (map (fn [entry] - (when-not (= "goog" entry) - (str "goog.require(\"" (comp/munge entry) "\");\n"))) - (if-let [entries (when module (:entries module))] - entries - (when-let [main (:main opts)] - [main])))))) - - (output-one-file - (merge opts - (when module - {:output-to (:output-to module)})) - (str (when (or (not module) (= :cljs-base (:module-name opts))) - (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - "var CLOSURE_NO_DEPS = true;\n" - "if(typeof goog == \"undefined\") document.write('');\n" - "document.write('');\n" - "document.write('');\n" - "document.write('');\n" - (apply str (preloads (:preloads opts) :browser)))) - (apply str - (map (fn [entry] - (when-not (= "goog" entry) - (str "document.write('');\n"))) - (if-let [entries (when module (:entries module))] - entries - (when-let [main (:main opts)] - [main]))))))))) + (let [module (get (:modules opts) (:module-name opts))] + (output-one-file + (merge opts + (when module + {:output-to (:output-to module)})) + (if-let [target-fn (opts-fn :target-fn opts)] + (target-fn opts) + (let [asset-path (or (:asset-path opts) + (util/output-directory opts)) + closure-defines (json/write-str (:closure-defines opts))] + (case (:target opts) + :nodejs + (add-header opts + (str (when (or (not module) (= :cljs-base (:module-name opts))) + (str "var path = require(\"path\");\n" + "try {\n" + " require(\"source-map-support\").install();\n" + "} catch(err) {\n" + "}\n" + "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"goog\",\"bootstrap\",\"nodejs.js\"));\n" + "require(path.join(path.resolve(\".\"),\"" asset-path "\",\"cljs_deps.js\"));\n" + "goog.global.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + (apply str (preloads (:preloads opts))))) + (apply str + (map (fn [entry] + (str "goog.require(\"" (comp/munge entry) "\");\n")) + (if-let [entries (when module (:entries module))] + entries + [(:main opts)]))) + (when (:nodejs-rt opts) + "goog.require(\"cljs.nodejscli\");\n"))) + + :webworker + (str (when (or (not module) (= :cljs-base (:module-name opts))) + (str "var CLOSURE_BASE_PATH = \"" asset-path "/goog/\";\n" + "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "var CLOSURE_IMPORT_SCRIPT = (function(global) { return function(src) {global['importScripts'](src); return true;};})(this);\n" + "if(typeof goog == 'undefined') importScripts(\"" asset-path "/goog/base.js\");\n" + "importScripts(\"" asset-path "/cljs_deps.js\");\n" + (apply str (preloads (:preloads opts))))) + (apply str + (map (fn [entry] + (when-not (= "goog" entry) + (str "goog.require(\"" (comp/munge entry) "\");\n"))) + (if-let [entries (when module (:entries module))] + entries + (when-let [main (:main opts)] + [main]))))) + + (str (when (or (not module) (= :cljs-base (:module-name opts))) + (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "var CLOSURE_NO_DEPS = true;\n" + "if(typeof goog == \"undefined\") document.write('');\n" + "document.write('');\n" + "document.write('');\n" + "document.write('');\n" + (apply str (preloads (:preloads opts) :browser)))) + (apply str + (map (fn [entry] + (when-not (= "goog" entry) + (str "document.write('');\n"))) + (if-let [entries (when module (:entries module))] + entries + (when-let [main (:main opts)] + [main]))))))))))) (defn fingerprinted-modules [modules fingerprint-info] (into {} From 2030cd0d77761c85ea394a3b0ec8ba13c1dc29a6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2020 14:00:56 -0400 Subject: [PATCH 3521/4033] make cljs.cli/repl-env-opts-opt and cljs.cli/compile-opts-opt fns public so they can be reused while extending their behavior. --- src/main/clojure/cljs/cli.clj | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index c6b9213ef..eaca1937c 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -222,7 +222,10 @@ classpath. Classpath-relative paths have prefix of @ or @/") (defn load-edn-opts [str] (reduce merge {} (map read-edn-opts (util/split-paths str)))) -(defn- repl-env-opts-opt +(defn repl-env-opts-opt + "Handles the --repl-env-opts (-ro) option. Can be overridden via + ::cljs.cli/commands key on the repl-env supplied via --repl-env (-re). + See also default-commands." [cfg ropts] (let [ropts (string/trim ropts) edn (if (string/starts-with? ropts "{") @@ -230,7 +233,10 @@ classpath. Classpath-relative paths have prefix of @ or @/") (load-edn-opts ropts))] (update cfg :repl-env-options merge edn))) -(defn- compile-opts-opt +(defn compile-opts-opt + "Handles the --compile-opts (-co) option. Can be overridden via + ::cljs.cli/commands key on the repl-env supplied via --repl-env (-re). + See also default-commands." [cfg copts] (let [copts (string/trim copts) edn (if (string/starts-with? copts "{") From e05b2f7d566664f4046aa50dd609dc393f76f764 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2020 15:00:25 -0400 Subject: [PATCH 3522/4033] revert last commit for now - cljs.repl/IReplEnvOptions is well supported both in cljs.repl as well as cljs.cli as a means of setting custom default options --- src/main/clojure/cljs/cli.clj | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index eaca1937c..c6b9213ef 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -222,10 +222,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") (defn load-edn-opts [str] (reduce merge {} (map read-edn-opts (util/split-paths str)))) -(defn repl-env-opts-opt - "Handles the --repl-env-opts (-ro) option. Can be overridden via - ::cljs.cli/commands key on the repl-env supplied via --repl-env (-re). - See also default-commands." +(defn- repl-env-opts-opt [cfg ropts] (let [ropts (string/trim ropts) edn (if (string/starts-with? ropts "{") @@ -233,10 +230,7 @@ classpath. Classpath-relative paths have prefix of @ or @/") (load-edn-opts ropts))] (update cfg :repl-env-options merge edn))) -(defn compile-opts-opt - "Handles the --compile-opts (-co) option. Can be overridden via - ::cljs.cli/commands key on the repl-env supplied via --repl-env (-re). - See also default-commands." +(defn- compile-opts-opt [cfg copts] (let [copts (string/trim copts) edn (if (string/starts-with? copts "{") From 6058847f3d2df046e2e13cfbd8db4763947c05a3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2020 20:09:10 -0400 Subject: [PATCH 3523/4033] make opts-fn public so that other namespaces like cljs.cli can reuse --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 726863d76..ca88e9efa 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -103,7 +103,7 @@ (find-var sym))) -(defn- opts-fn +(defn opts-fn "Extracts a function from opts, by default expecting a function value, but converting from a namespaced symbol if needed." [kw opts] From 553c9a93207e77fad7c405846a5e13506298ccbf Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2020 21:11:00 -0400 Subject: [PATCH 3524/4033] revert last commit - again there is a better way to do this - cljs.cli really has all the necessary extension points --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ca88e9efa..726863d76 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -103,7 +103,7 @@ (find-var sym))) -(defn opts-fn +(defn- opts-fn "Extracts a function from opts, by default expecting a function value, but converting from a namespaced symbol if needed." [kw opts] From 63d4007ef87c9d9e2a7b7c2845cc453b23abfd75 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2020 21:17:56 -0400 Subject: [PATCH 3525/4033] break out public reusable cljs.cli/get-main-ns helper, whitespace tweaks --- src/main/clojure/cljs/cli.clj | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index c6b9213ef..da9e8dc36 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -458,6 +458,11 @@ present" 9000) :output-dir (:output-dir options "out")}))) +(defn get-main-ns [{:keys [ns options] :as cfg}] + (if (and ns (not (#{"-r" "--repl" "-s" "--serve"} ns))) + (symbol ns) + (:main options))) + (defn default-compile [repl-env {:keys [ns args options] :as cfg}] (let [rfs #{"-r" "--repl"} @@ -465,9 +470,7 @@ present" env-opts (repl/repl-options (repl-env)) repl? (boolean (or (rfs ns) (rfs (first args)))) serve? (boolean (or (sfs ns) (sfs (first args)))) - main-ns (if (and ns (not ((into rfs sfs) ns))) - (symbol ns) - (:main options)) + main-ns (get-main-ns cfg) opts (as-> (merge (select-keys env-opts @@ -479,12 +482,16 @@ present" (not (:output-to opts)) (assoc :output-to (.getPath (io/file (:output-dir opts "out") "main.js"))) + (= :advanced (:optimizations opts)) (dissoc :browser-repl) + (not (:output-dir opts)) (assoc :output-dir "out") + (not (contains? opts :aot-cache)) (assoc :aot-cache true) + (sequential? (:watch opts)) (update :watch cljs.closure/compilable-input-paths))) convey (into [:output-dir] repl/known-repl-opts) From a7e26f8bc19721c9382f6ab7c1cfd6786cc731cf Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 1 Apr 2020 22:31:41 -0400 Subject: [PATCH 3526/4033] tweak CLI help string about conditionality of --serve --- src/main/clojure/cljs/cli.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index da9e8dc36..71189d00d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -44,7 +44,8 @@ For --main and --repl: The init options may be repeated and mixed freely, but must appear before any main option. -In the case of --compile you may supply --repl or --serve options afterwards. +In the case of --compile you may supply --repl or --serve (if applicable) +options afterwards. Paths may be absolute or relative in the filesystem or relative to classpath. Classpath-relative paths have prefix of @ or @/") From 5b2d4b8a33355dfcde16e54b9175b97cc6d76c91 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Apr 2020 10:46:19 -0400 Subject: [PATCH 3527/4033] we should take all known compiler-opts from repl-env options --- src/main/clojure/cljs/cli.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 71189d00d..41b35d4e4 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -475,7 +475,8 @@ present" opts (as-> (merge (select-keys env-opts - (cond-> [:target] repl? (conj :browser-repl))) + (cond-> closure/known-opts + repl? (conj :browser-repl))) options (when main-ns {:main main-ns})) opts From 45022fa177dc900b774c6736e04e698426bf55c8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 3 Apr 2020 11:26:02 -0400 Subject: [PATCH 3528/4033] remove cljs.analyzer.api current|empty-state - just a unpleasant api. Instead add a new `current-state` helper to get dynamic state if needed for some reason. Refactor build & compiler api to consistently use analyzer api for dealing with cljs.env --- src/main/clojure/cljs/analyzer/api.cljc | 18 +++++--------- src/main/clojure/cljs/build/api.clj | 33 ++++++++++++------------- src/main/clojure/cljs/compiler/api.clj | 27 ++++++++++---------- 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 382f45a07..875ede467 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -28,16 +28,12 @@ ([opts] (env/default-compiler-env opts))) -(defn current|empty-state - "Returns the currently bound compiler state or an empty one" - [] - (if-not (nil? env/*compiler*) - env/*compiler* - (empty-state))) +(defn current-state [] + env/*compiler*) (defmacro with-state "Run the body with the given compilation state Atom." - [state body] + [state & body] `(env/with-compiler-env ~state ~@body)) @@ -67,14 +63,12 @@ (defn get-options "Return the compiler options from compiler state." - ([] (get-options env/*compiler*)) ([state] (get @state :options))) (defn get-js-index "Return the currently computed Google Closure js dependency index from the compiler state." - ([] (get-options env/*compiler*)) ([state] (get @state :js-dependency-index))) @@ -90,7 +84,7 @@ ([env form] (analyze env form nil)) ([env form name] (analyze env form name nil)) ([env form name opts] - (analyze (current|empty-state) env form name opts)) + (analyze (empty-state opts) env form name opts)) ([state env form name opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -117,7 +111,7 @@ ([src] (parse-ns src nil nil)) ([src opts] (parse-ns src nil opts)) ([src dest opts] - (parse-ns (current|empty-state) src dest opts)) + (parse-ns (empty-state opts) src dest opts)) ([state src dest opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -134,7 +128,7 @@ meaningful value." ([f] (analyze-file f nil)) ([f opts] - (analyze-file (current|empty-state) f opts)) + (analyze-file (empty-state opts) f opts)) ([state f opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 949a985a5..97179a9d5 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -15,7 +15,6 @@ (:refer-clojure :exclude [compile]) (:require [clojure.java.io :as io] [cljs.util :as util] - [cljs.env :as env] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.closure :as closure]) @@ -52,7 +51,7 @@ ('example.core 'example.util)" ([namespaces] (closure/cljs-dependents-for-macro-namespaces - (ana-api/current|empty-state) namespaces)) + (ana-api/empty-state) namespaces)) ([state namespaces] (closure/cljs-dependents-for-macro-namespaces state namespaces))) @@ -68,9 +67,9 @@ provide build options with :output-dir specified." ([src] (src-file->target-file src nil)) ([src opts] - (src-file->target-file (ana-api/current|empty-state) src opts)) + (src-file->target-file (ana-api/empty-state opts) src opts)) ([state src opts] - (env/with-compiler-env state + (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/src-file->target-file src opts))))) @@ -79,9 +78,9 @@ the goog.require statement for it." ([src] (src-file->goog-require src nil)) ([src opts] - (src-file->goog-require (ana-api/current|empty-state) src opts)) + (src-file->goog-require (ana-api/empty-state opts) src opts)) ([state src opts] - (env/with-compiler-env state + (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (closure/src-file->goog-require src opts))))) @@ -121,7 +120,7 @@ .cljs, .cljc, .js. Returns a map containing :relative-path a string, and :uri a URL." ([ns] - (ns->location ns (ana-api/current|empty-state))) + (ns->location ns (ana-api/empty-state))) ([ns compiler-env] (closure/source-for-namespace ns compiler-env))) @@ -139,9 +138,9 @@ ([xs] (add-dependency-sources xs {})) ([xs opts] - (add-dependency-sources (ana-api/current|empty-state) xs opts)) + (add-dependency-sources (ana-api/empty-state opts) xs opts)) ([state xs opts] - (env/with-compiler-env state + (ana-api/with-state state (closure/add-dependency-sources xs opts)))) (defn add-dependencies @@ -180,9 +179,9 @@ (defn compile "Given a Compilable, compile it and return an IJavaScript." ([opts compilable] - (compile (ana-api/current|empty-state) opts compilable)) + (compile (ana-api/empty-state opts) opts compilable)) ([state opts compilable] - (env/with-compiler-env state + (ana-api/with-state state (closure/compile compilable opts)))) (defn output-unoptimized @@ -245,8 +244,8 @@ (if (compiler-opts? dependencies) (install-node-deps! (:npm-deps dependencies) dependencies) (install-node-deps! dependencies - (when-not (nil? env/*compiler*) - (:options @env/*compiler*))))) + (when-let [state (ana-api/current-state)] + (:options @state))))) ([dependencies opts] {:pre [(map? dependencies)]} (closure/check-npm-deps opts) @@ -264,8 +263,8 @@ (if (compiler-opts? dependencies) (get-node-deps (keys (:npm-deps dependencies)) dependencies) (get-node-deps dependencies - (when-not (nil? env/*compiler*) - (:options @env/*compiler*))))) + (when-let [state (ana-api/current-state)] + (:options @state))))) ([dependencies opts] {:pre [(sequential? dependencies)]} (closure/index-node-modules @@ -279,12 +278,12 @@ installed." ([entries] (node-inputs entries - (:options (ana-api/current|empty-state)))) + (:options (ana-api/empty-state)))) ([entries opts] (closure/node-inputs entries opts))) (defn node-modules "Return a sequence of requirable libraries found under node_modules." ([opts] - (env/with-compiler-env (ana-api/empty-state opts) + (ana-api/with-state (ana-api/empty-state opts) (filter :provides (closure/index-node-modules-dir))))) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index 7a01890d5..43c79887f 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -9,9 +9,7 @@ (ns cljs.compiler.api "This is intended to be a stable api for those who need programmatic access to the compiler." - (:require [cljs.util :as util] - [cljs.env :as env] - [cljs.analyzer :as ana] + (:require [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.compiler :as comp] [cljs.closure :as closure])) @@ -22,9 +20,9 @@ (defn emit "Given an AST node generated by the analyzer emit JavaScript as a string." ([ast] - (emit (ana-api/current|empty-state) ast)) + (emit (ana-api/empty-state) ast)) ([state ast] - (env/with-compiler-env state + (ana-api/with-state state (with-out-str (comp/emit ast))))) @@ -32,12 +30,13 @@ "Ensure that core.cljs has been loaded." ([] (comp/with-core-cljs - (:options (ana-api/current|empty-state)))) + (when-let [state (ana-api/current-state)] + (:options @state)))) ([opts] (with-core-cljs opts (fn []))) ([opts body] - (with-core-cljs (ana-api/current|empty-state) opts body)) + (with-core-cljs (ana-api/empty-state opts) opts body)) ([state opts body] - (env/with-compiler-env state + (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (comp/with-core-cljs opts body))))) @@ -45,9 +44,9 @@ "Return true if the src file requires compilation." ([src dest] (requires-compilation? src dest nil)) ([src dest opts] - (requires-compilation? (ana-api/current|empty-state) src dest opts)) + (requires-compilation? (ana-api/empty-state opts) src dest opts)) ([state src dest opts] - (env/with-compiler-env state + (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (comp/requires-compilation? src dest opts))))) @@ -69,9 +68,9 @@ ([src dest] (compile-file src dest nil)) ([src dest opts] - (compile-file (ana-api/current|empty-state) src dest opts)) + (compile-file (ana-api/empty-state opts) src dest opts)) ([state src dest opts] - (env/with-compiler-env state + (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (comp/compile-file src dest opts))))) @@ -89,8 +88,8 @@ ([src-dir] (compile-root src-dir "out")) ([src-dir target-dir] (compile-root src-dir target-dir nil)) ([src-dir target-dir opts] - (compile-root (ana-api/current|empty-state) src-dir target-dir opts)) + (compile-root (ana-api/empty-state opts) src-dir target-dir opts)) ([state src-dir target-dir opts] - (env/with-compiler-env state + (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (comp/compile-root src-dir target-dir opts))))) From 0f6d24669c95966d781f905d168f20bce9105bbe Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 4 Apr 2020 09:06:22 -0400 Subject: [PATCH 3529/4033] CLJS-3221: cljs.test/cljs-output-dir wrong number of args passed to cljs.analyzer.api/get-options --- src/main/clojure/cljs/analyzer/api.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 875ede467..c95abf8c3 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -63,12 +63,14 @@ (defn get-options "Return the compiler options from compiler state." + ([] (get-options (current-state))) ([state] (get @state :options))) (defn get-js-index "Return the currently computed Google Closure js dependency index from the compiler state." + ([] (get-options (current-state))) ([state] (get @state :js-dependency-index))) From 348c4ffbcfe2d3d416e4d7e64922e4ffae00534e Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 4 Apr 2020 12:35:29 -0400 Subject: [PATCH 3530/4033] cljs.cli - don't hard code boolean options to -v --verbose --- src/main/clojure/cljs/cli.clj | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 41b35d4e4..d160f7aa9 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -525,6 +525,14 @@ present" (-> (get commands (keyword (str (name k) "-dispatch"))) keys set))) +(defn bool-init-options [commands] + (reduce + (fn [ret [flags config]] + (cond-> ret + (= "bool" (:arg config)) + (into flags))) + #{} (:init commands))) + (defn dispatch? [commands k opt] (contains? (get-options commands k) opt)) @@ -628,7 +636,7 @@ present" (defn normalize [commands args] (if (not (contains? (get-options commands :main) (first args))) - (let [pred (complement #{"-v" "--verbose"}) + (let [pred (complement (bool-init-options commands)) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) args)] From b3b4df39e8e9189045317170b47cea6185ebfc4d Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 4 Apr 2020 12:39:35 -0400 Subject: [PATCH 3531/4033] change normalize to compute fixpoint --- src/main/clojure/cljs/cli.clj | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index d160f7aa9..d00b82eb3 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -635,22 +635,27 @@ present" :doc "Print this help message and exit"}}})) (defn normalize [commands args] - (if (not (contains? (get-options commands :main) (first args))) - (let [pred (complement (bool-init-options commands)) - [pre post] ((juxt #(take-while pred %) - #(drop-while pred %)) - args)] - (cond - (= pre args) pre - - (not (#{"true" "false"} (fnext post))) - (concat pre [(first post) "true"] - (normalize commands (next post))) - - :else - (concat pre [(first post) (fnext post)] - (normalize commands (nnext post))))) - args)) + (letfn [(normalize* [args*] + (if (not (contains? (get-options commands :main) (first args*))) + (let [pred (complement (bool-init-options commands)) + [pre post] ((juxt #(take-while pred %) + #(drop-while pred %)) + args*)] + (cond + (= pre args*) pre + + (not (#{"true" "false"} (fnext post))) + (concat pre [(first post) "true"] + (normalize commands (next post))) + + :else + (concat pre [(first post) (fnext post)] + (normalize commands (nnext post))))) + args*))] + (loop [args' (normalize* args)] + (if (= args args') + args' + (recur (normalize* args)))))) (defn merged-commands [repl-env] (add-commands default-commands From c00dee83649e1ca90a452cfee9dac1c013604bb2 Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Sat, 4 Apr 2020 18:51:02 +0200 Subject: [PATCH 3532/4033] CLJS-3222: Allow build targets using :target-fn to supply a bootstrap file --- src/main/clojure/cljs/closure.clj | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 726863d76..3ed651f39 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2928,14 +2928,17 @@ (map str (keys top-level))))))) opts)) -(defn output-bootstrap [{:keys [target] :as opts}] - (when (and (#{:nodejs} target) - (not= (:optimizations opts) :whitespace)) +(defn output-bootstrap [{:keys [target target-fn] :as opts}] + (when (or (and (#{:nodejs} target) + (not= (:optimizations opts) :whitespace)) + target-fn) (let [target-str (name target) outfile (io/file (util/output-directory opts) "goog" "bootstrap" (str target-str ".js"))] - (util/mkdirs outfile) - (spit outfile (slurp (io/resource (str "cljs/bootstrap_" target-str ".js"))))))) + ;; not all targets using :target-fn might provide a bootstrap file to include + (when-let [bootstrap-file (io/resource (str "cljs/bootstrap_" target-str ".js"))] + (util/mkdirs outfile) + (spit outfile (slurp bootstrap-file)))))) (defn compile-inputs "Compile inputs and all of their transitive dependencies including JS modules, From 2c516d488b140c680829b7bdf0e53cbe8eb60240 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 4 Apr 2020 17:07:38 -0400 Subject: [PATCH 3533/4033] CLJS-3223: get-js-index 0-arity should call get-js-index 1-arity --- src/main/clojure/cljs/analyzer/api.cljc | 2 +- src/test/clojure/cljs/analyzer_api_tests.clj | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index c95abf8c3..30043e6dc 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -70,7 +70,7 @@ (defn get-js-index "Return the currently computed Google Closure js dependency index from the compiler state." - ([] (get-options (current-state))) + ([] (get-js-index (current-state))) ([state] (get @state :js-dependency-index))) diff --git a/src/test/clojure/cljs/analyzer_api_tests.clj b/src/test/clojure/cljs/analyzer_api_tests.clj index 0f9fcd9a2..831734c41 100644 --- a/src/test/clojure/cljs/analyzer_api_tests.clj +++ b/src/test/clojure/cljs/analyzer_api_tests.clj @@ -40,3 +40,15 @@ (ana-api/analyze test-cenv test-env warning-form nil {:warning-handlers [(warning-handler counter)]})) (is (= 1 @counter)))) + +(deftest get-options-test + (let [state (atom {:options {:a 1}})] + (is (= {:a 1} (ana-api/get-options state))) + (ana-api/with-state state + (is (= {:a 1} (ana-api/get-options)))))) + +(deftest get-js-index-test + (let [state (atom {:js-dependency-index {:a 1}})] + (is (= {:a 1} (ana-api/get-js-index state))) + (ana-api/with-state state + (is (= {:a 1} (ana-api/get-js-index)))))) From f742cd9db9ed32442c2cff97c06788f39e26d8d1 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 5 Apr 2020 11:51:35 -0400 Subject: [PATCH 3534/4033] fix bad fixpoint logic in cljs.cli/normalize --- src/main/clojure/cljs/cli.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index d00b82eb3..e8c577f6d 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -652,10 +652,10 @@ present" (concat pre [(first post) (fnext post)] (normalize commands (nnext post))))) args*))] - (loop [args' (normalize* args)] + (loop [args args args' (normalize* args)] (if (= args args') args' - (recur (normalize* args)))))) + (recur args' (normalize* args')))))) (defn merged-commands [repl-env] (add-commands default-commands From f3b7f9b8ac4666c51549dbff24b451e3c9993b7b Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 6 Apr 2020 10:13:46 -0400 Subject: [PATCH 3535/4033] if we cannot establish a REPL due to an error during REPL initialization throw ex-info with the cause --- src/main/clojure/cljs/repl.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index a47857125..79b1df257 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1211,6 +1211,8 @@ (prompt) (flush)) (recur)))))))) + (catch Throwable t + (throw (ex-info "Unexpected error during REPL initialization" {} t))) (finally (reset! done? true) (-tear-down repl-env))))))) From 89925838cf67a01c591958bf5ec6fe1e3ce2b7ae Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Mon, 6 Apr 2020 17:33:51 +0200 Subject: [PATCH 3536/4033] CLJS-3224: Fix cljs.loader due to usage of removed setModuleUris API setModuleUris was deprecated here: https://github.com/google/closure-library/commit/27512f7e420d594f9b198f97ba230e5dcbb11f04 And removed here: https://github.com/google/closure-library/commit/c23f255ab09b728bd92504eee7dc5f618e9fb2c2 --- src/main/cljs/cljs/loader.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 249f0e85a..8618d2fc4 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -42,7 +42,7 @@ (defonce ^:dynamic *module-manager* (create-module-manager)) (.setAllModuleInfo *module-manager* (to-js module-infos)) -(.setModuleUris *module-manager* +(.setModuleTrustedUris *module-manager* (cond-> module-uris (map? module-uris) to-js)) (defn loaded? From dbd232a0cf4b43edeb10ba618c95b92b272cc0ae Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Apr 2020 11:36:06 -0400 Subject: [PATCH 3537/4033] if we can't load the REPL ns, include the cause --- src/main/cljs/cljs/main.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/main.clj b/src/main/cljs/cljs/main.clj index 6950a948a..eec3fb5ac 100644 --- a/src/main/cljs/cljs/main.clj +++ b/src/main/cljs/cljs/main.clj @@ -29,10 +29,10 @@ (throw (ex-info (str "REPL namespace " repl-ns " does not define repl-env var") {:repl-ns repl-ns}))) - (catch Throwable _ + (catch Throwable t (throw - (ex-info (str "REPL namespace " repl-ns " does not exist") - {:repl-ns repl-ns}))))) + (ex-info (str "Failed to load REPL namespace " repl-ns) + {:repl-ns repl-ns} t))))) browser/repl-env)) (defn- normalize* [args] From 3ecbc3bdfd598ba19bad94ec804f96781d90817b Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Apr 2020 12:34:38 -0400 Subject: [PATCH 3538/4033] caching support for transpiled GCL libraries under :none, under higher optimization just always remit GCL libs un-transpiled --- src/main/clojure/cljs/closure.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3ed651f39..0b5d66cc0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2078,13 +2078,14 @@ :out-file (.toString out-file)}))] (when (and (not js-module?) (or (not (.exists out-file)) - ;; no caching yet for GCL files that need transpilation - transpile? - (and res (util/changed? out-file res)))) + (and res (util/changed? out-file res)) + ;; always re-emit GCL libs under optimizations higher than :none + ;; :none will just use the cached transpiled result + (and transpile? (not= :none optimizations)))) (when (and res (or ana/*verbose* (:verbose opts))) (util/debug-prn "Copying" (str res) "to" (str out-file))) (util/mkdirs out-file) - (if (and (= :none optimizations) transpile?) + (if (and transpile? (= :none optimizations)) (spit out-file (transpile opts res js)) (spit out-file (deps/-source js))) (when res From fa264e30b163af19063a3cad40658531090c3f74 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 7 Apr 2020 13:08:07 -0400 Subject: [PATCH 3539/4033] make fast-initial-prompt? accessible to 3rd party REPLs. also make it possible to show it after -setup by supporting :after-setup as a value. Only show the prompt as soon as possible if the value is explicitly true. --- src/main/clojure/cljs/cli.clj | 10 +++++++--- src/main/clojure/cljs/repl.cljc | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index e8c577f6d..d7a1ddd4b 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -298,8 +298,9 @@ is trying load some arbitrary ns." (subs repl-ns (count "cljs.repl."))))) (defn- fast-initial-prompt? [repl-env inits] - (and (empty? inits) - (contains? #{"node"} (repl-name repl-env)))) + (boolean + (and (empty? inits) + (contains? #{"node"} (repl-name repl-env))))) (defn- repl-opt "Start a repl with args and inits. Print greeting if no eval options were @@ -316,7 +317,10 @@ present" renv (apply repl-env (mapcat identity reopts))] (repl/repl* renv (assoc (dissoc-entry-point-opts opts) - ::repl/fast-initial-prompt? (fast-initial-prompt? repl-env inits) + ::repl/fast-initial-prompt? + (or (fast-initial-prompt? repl-env inits) + (::repl/fast-initial-prompt? (repl/repl-options renv))) + :inits (into [{:type :init-forms diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 79b1df257..f04008007 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1059,7 +1059,7 @@ (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) (set/union known-repl-opts cljsc/known-opts))] (when suggested-opt (println (str "WARNING: Unknown option '" unknown-opt "'. Did you mean '" suggested-opt "'?")))) - (when fast-initial-prompt? + (when (true? fast-initial-prompt?) (initial-prompt quit-prompt prompt)) (let [repl-opts (-repl-options repl-env) repl-requires (into repl-requires (:repl-requires repl-opts)) @@ -1125,6 +1125,8 @@ (if-let [merge-opts (:merge-opts (-setup repl-env opts))] (merge opts merge-opts) opts))) + _ (when (= :after-setup fast-initial-prompt?) + (initial-prompt quit-prompt prompt)) init (do (evaluate-form repl-env env "" `(~'set! ~'cljs.core/*print-namespace-maps* true) From ccfdf1c0e2ced10b0f129c40b34220c0666a5cf0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 8 Apr 2020 13:30:12 -0400 Subject: [PATCH 3540/4033] add new :main terminal flag, --install-deps --- src/main/clojure/cljs/cli.clj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index d7a1ddd4b..f638d53a7 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -463,6 +463,10 @@ present" 9000) :output-dir (:output-dir options "out")}))) +(defn- install-deps-opt + [_ _ {:keys [options] :as cfg}] + (closure/maybe-install-node-deps! options)) + (defn get-main-ns [{:keys [ns options] :as cfg}] (if (and ns (not (#{"-r" "--repl" "-s" "--serve"} ns))) (symbol ns) @@ -619,7 +623,9 @@ present" "system-dependent path-separated list of EDN files / classpath resources. Options " "will be merged left to right.")}} :main - {["-r" "--repl"] {:fn repl-opt + {["--install-deps"] {:fn install-deps-opt + :doc "Install all :npm-deps found upstream and in supplied compiler options"} + ["-r" "--repl"] {:fn repl-opt :doc "Run a repl"} ["-m" "--main"] {:fn main-opt :arg "ns" From 77c7cab5384fbf9776d09d07319b664599b54a8d Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 8 Apr 2020 14:30:49 -0400 Subject: [PATCH 3541/4033] add some data to the repl init fail ex-info --- src/main/clojure/cljs/repl.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index f04008007..f07a68723 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1214,7 +1214,9 @@ (flush)) (recur)))))))) (catch Throwable t - (throw (ex-info "Unexpected error during REPL initialization" {} t))) + (throw + (ex-info "Unexpected error during REPL initialization" + {::error :init-failed} t))) (finally (reset! done? true) (-tear-down repl-env))))))) From c20592150f01fffea95ce296483ccdbd57376cb7 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 13:51:15 -0400 Subject: [PATCH 3542/4033] Allow configuration of the node modules dependency tool Add :deps-cmd compiler option. Add --deps-cmd cljs.main flag. Update cljs.closure/maybe-install-node-deps! to respect :deps-cmd --- src/main/clojure/cljs/cli.clj | 7 +++++++ src/main/clojure/cljs/closure.clj | 9 ++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index f638d53a7..623936529 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -185,6 +185,10 @@ classpath. Classpath-relative paths have prefix of @ or @/") [cfg path] (assoc-in cfg [:options :output-to] path)) +(defn- deps-cmd-opt + [cfg deps-cmd] + (assoc-in cfg [:options :deps-cmd] deps-cmd)) + (defn- target-opt [cfg target] (let [target (if (= "node" target) "nodejs" target)] @@ -600,6 +604,9 @@ present" ["-o" "--output-to"] {:group ::compile :fn output-to-opt :arg "file" :doc "Set the output compiled file"} + ["--deps-cmd"] {:group ::compile :fn deps-cmd-opt + :arg "string" + :doc "Set the node dependency manager. Only npm or yarn supported"} ["-O" "--optimizations"] {:group ::compile :fn optimize-opt :arg "level" :doc diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0b5d66cc0..76ef74f33 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -205,7 +205,7 @@ :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}) + :nodejs-rt :target-fn :deps-cmd}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -2539,7 +2539,7 @@ (recur buf))))) (defn maybe-install-node-deps! - [{:keys [npm-deps verbose] :as opts}] + [{:keys [deps-cmd npm-deps verbose] :or {deps-cmd "npm"} :as opts}] (let [npm-deps (merge npm-deps (compute-upstream-npm-deps opts))] (when-not (empty? npm-deps) (let [pkg-json (io/file "package.json")] @@ -2548,7 +2548,10 @@ (when-not (.exists pkg-json) (spit pkg-json "{}")) (let [proc (-> (ProcessBuilder. - (into (cond->> ["npm" "install" "@cljs-oss/module-deps"] + (into (cond->> + [deps-cmd + ({"npm" "install" "yarn" "add"} deps-cmd) + "@cljs-oss/module-deps"] util/windows? (into ["cmd" "/c"])) (map (fn [[dep version]] (str (name dep) "@" version))) npm-deps)) From 483edea05f6ebdfc7b69204877c46cf9968bca7c Mon Sep 17 00:00:00 2001 From: roman01la Date: Tue, 19 Nov 2019 23:15:52 +0100 Subject: [PATCH 3543/4033] CLJS-3181: Externs inference fails, even when manual type hint is provided --- src/main/clojure/cljs/analyzer.cljc | 9 ++-- src/test/clojure/cljs/analyzer_tests.clj | 56 +++++++++++++++--------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index eb1e4c4b3..3ee82d2d7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3671,11 +3671,14 @@ f-sym (when bind-f-expr? (gensym "fexpr__")) bindings (cond-> [] bind-args? (into (interleave arg-syms args)) - bind-f-expr? (conj f-sym (analyzed f)))] + bind-f-expr? (conj f-sym (analyzed f))) + tag (:tag (meta form))] (analyze env `(let [~@bindings] - (~(analyzed (if bind-f-expr? f-sym f)) - ~@(if bind-args? arg-syms args))))) + ~(with-meta + `(~(analyzed (if bind-f-expr? f-sym f)) + ~@(if bind-args? arg-syms args)) + {:tag tag})))) (let [ana-expr #(analyze enve %) argexprs (mapv ana-expr args)] (if (and (and (keyword? f) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 25181dba7..5c8f5d1a8 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -926,7 +926,7 @@ ;; dotted :var (is (= [:host-field 'bar :host-field 'foo :var 'cljs.core/inc 'cljs.core/inc] (-> (ana inc.foo.bar) - ((juxt :op + ((juxt :op :field (comp :op :target) (comp :field :target) @@ -936,7 +936,7 @@ ;; dotted :local (is (= [:host-field 'c :host-field 'b :local 'a 'a] (-> (ana (let [a 1] a.b.c)) :body :ret - ((juxt :op + ((juxt :op :field (comp :op :target) (comp :field :target) @@ -974,9 +974,9 @@ (is (= (-> (ana (let [a 1] a)) :body :ret :form) 'a)) (is (map? (-> (ana (let [a 1] a)) :body :ret :env))) ;; dotted :local - (is (= [:host-field 'c :host-field 'b :local 'a] + (is (= [:host-field 'c :host-field 'b :local 'a] (-> (ana (let [a 1] a.b.c)) :body :ret - ((juxt :op + ((juxt :op :field (comp :op :target) (comp :field :target) @@ -984,12 +984,12 @@ (comp :name :target :target)))))) ;local shadow (is (= 'alert - (ana/no-warn (-> (ana (let [alert 1] js/alert)) :body + (ana/no-warn (-> (ana (let [alert 1] js/alert)) :body :env :locals (get 'alert) :name)))) (is (= [:local 'alert] - (ana/no-warn (-> (ana (let [alert 1] js/alert)) :body :ret + (ana/no-warn (-> (ana (let [alert 1] js/alert)) :body :ret ((juxt :op :name)))))) ;loop (is (= (-> (ana (loop [])) :op) :loop)) @@ -1020,7 +1020,7 @@ ; :finally (is (= (-> (ana (try (finally 1))) :finally :op) :do)) (is (= (-> (ana (try (finally 1))) :finally :ret :op) :const)) - ;TODO case + ;TODO case (is (= (-> (ana (case 1)) :op) :let)) (is (= (-> (ana (case 1)) :body :ret :op) :case)) (is (= (-> (ana (case 1)) :body :ret :children) [:test :nodes :default])) @@ -1063,7 +1063,7 @@ ; :ns/:name (is (= ['cljs.core 'cljs.core/a] (-> (ana (def a 1)) ((juxt :ns :name))))) ; :var - (is (= [:var 'cljs.core 'cljs.core/a 'a] + (is (= [:var 'cljs.core 'cljs.core/a 'a] (-> (ana (def a 1)) :var ((juxt :op :ns :name :form))))) ; :init @@ -1076,9 +1076,9 @@ (is (= :do (-> (ana (deftype A [a] Object (toString [this] a))) :statements first :body :op))) ; field reference (is (= [:local :field] - (-> (ana (deftype A [a] Object (toString [this] a))) + (-> (ana (deftype A [a] Object (toString [this] a))) :statements first :body :ret :val :methods - first :body :ret :body :ret + first :body :ret :body :ret ((juxt :op :local))))) ;defrecord (is (= :defrecord (-> (ana (defrecord Ab [])) :body :statements first :ret :op))) @@ -1139,10 +1139,10 @@ (-> (ana (fn [])) :methods first :params))) (is (vector? (-> (ana (fn [a b])) :methods first :params))) - (is (= [:binding 'a :arg] + (is (= [:binding 'a :arg] (-> (ana (fn [a b])) :methods first :params first ((juxt :op :name :local))))) - (is (= [:binding 'b :arg] + (is (= [:binding 'b :arg] (-> (ana (fn [a b])) :methods first :params second ((juxt :op :name :local))))) ;if @@ -1342,7 +1342,7 @@ (is (= :throw (-> (ana (throw (js/Error. "bad"))) :op))) (is (= [:exception] (-> (ana (throw (js/Error. "bad"))) :children))) ; :exception - (is (= [:js-var 'js 'js/Error] (-> (ana (throw (js/Error. "bad"))) :exception + (is (= [:js-var 'js 'js/Error] (-> (ana (throw (js/Error. "bad"))) :exception :class ((juxt :op :ns :name))))) ;vector @@ -1405,39 +1405,39 @@ ;munging (is (= [false 'a] - (-> + (-> (ana (let [a (println 1) b (println 2)] [a b])) - :bindings first + :bindings first ((juxt #(contains? % :ns) :name))))) ;shadowing (is (= 'a - (-> + (-> (ana (let [a (println 1) a (println 2)] [a a])) - :bindings second + :bindings second :shadow :name))) (is (= 'a - (-> + (-> (ana (let [a (println 1) a (println 2) a (println 3) ] [a a a])) - :bindings (nth 2) + :bindings (nth 2) :shadow :shadow :name))) ;ns - (is + (is (binding [ana/*analyze-deps* false] (binding [ana/*cljs-ns* 'cljs.user] - (ana + (ana (ns my.ns.foo (:require [clojure.repl] [clojure.string] @@ -2137,3 +2137,17 @@ (is (= w2 "cljs.core/-, all arguments must be numbers, got [string] instead")) (is (= w3 "cljs.core//, all arguments must be numbers, got [number string] instead")) (is (= w4 "cljs.core/*, all arguments must be numbers, got [string] instead"))))) + +(deftest test-cljs-3181 + (let [ws (atom []) + res (binding [ana/*cljs-static-fns* true] + (infer-test-helper + {:forms '[(ns warn-on-infer-test.app) + (set! *warn-on-infer* true) + (defn f [gfn] + (.then ^js/Promise (gfn (inc 1)) identity))] + :externs ["src/test/externs/test.js"] + :warnings ws + :warn false + :with-core? true}))] + (is (empty? @ws)))) From 5b070b68032d00bc2cd42fe2bd32da18d2d2f92f Mon Sep 17 00:00:00 2001 From: roman01la Date: Fri, 30 Aug 2019 19:33:24 +0200 Subject: [PATCH 3544/4033] CLJS-3161: Include :property for :warning-type :target --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3ee82d2d7..7be8e27df 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3391,7 +3391,7 @@ ;; Cannot determine type of the target (when (or (nil? target-tag) ('#{any} target-tag)) (warning :infer-warning env - {:warn-type :target :form form})) + {:warn-type :target :form form :property prop})) ;; Unresolveable property on existing extern (let [[pre' pre] ((juxt butlast identity) (-> tag meta :prefix))] (when (and (has-extern? pre') (not (has-extern? pre))) From c3afc8a87b80cc36379feb21e08411a4b75d6a7a Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 19:23:26 -0400 Subject: [PATCH 3545/4033] move all externs infer related tests into a new namespace --- src/test/clojure/cljs/analyzer_tests.clj | 358 ----------------- src/test/clojure/cljs/externs_infer_tests.clj | 368 ++++++++++++++++++ 2 files changed, 368 insertions(+), 358 deletions(-) create mode 100644 src/test/clojure/cljs/externs_infer_tests.clj diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 5c8f5d1a8..aca1edd2d 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -11,9 +11,7 @@ [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.compiler :as comp] - [cljs.closure :as closure] [cljs.env :as env] - [cljs.externs :as externs] [cljs.test-util :refer [unsplit-lines]] [cljs.util :as util] [clojure.java.io :as io] @@ -1493,74 +1491,6 @@ (.getMessage (.getCause e)))) "Argument to var must be symbol"))) -(deftest test-has-extern?-basic - (let [externs (externs/externs-map - (closure/load-externs - {:externs ["src/test/externs/test.js"] - :use-only-custom-externs true}))] - (is (true? (ana/has-extern? '[Foo] externs))) - (is (true? (ana/has-extern? '[Foo wozMethod] externs))) - (is (false? (ana/has-extern? '[foo] externs))) - (is (false? (ana/has-extern? '[Foo gozMethod] externs))) - (is (true? (ana/has-extern? '[baz] externs))) - (is (false? (ana/has-extern? '[Baz] externs))))) - -(deftest test-has-extern?-defaults - (let [externs (externs/externs-map)] - (is (true? (ana/has-extern? '[console] externs))) - (is (true? (ana/has-extern? '[console log] externs))) - (is (true? (ana/has-extern? '[Number isNaN] externs))))) - -(def externs-cenv - (atom - {::ana/externs - (externs/externs-map - (closure/load-externs - {:externs ["src/test/externs/test.js"]}))})) - -(deftest test-js-tag - (let [externs (externs/externs-map - (closure/load-externs - {: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/Foo (ana/js-tag '[baz] :ret-tag externs))))) - -(deftest test-externs-infer - (is (= 'js/Foo - (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] - (env/with-compiler-env externs-cenv - (analyze (ana/empty-env) 'js/baz))) - :info :ret-tag))) - (is (= 'js/Foo - (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] - (env/with-compiler-env externs-cenv - (analyze (ana/empty-env) '(js/baz)))) - :tag))) - (is (= 'js - (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] - (env/with-compiler-env externs-cenv - (analyze (ana/empty-env) '(js/woz)))) - :tag))) - (is (= 'js - (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] - (env/with-compiler-env externs-cenv - (analyze (ana/empty-env) '(def foo (js/woz))))) - :tag))) - (is (= 'js - (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] - (env/with-compiler-env externs-cenv - (analyze (ana/empty-env) '(def foo js/boz)))) - :tag))) - (is (nil? (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] - (ana/no-warn - (env/with-compiler-env externs-cenv - (analyze (ana/empty-env) - '(let [z (.baz ^js/Foo.Bar x)] - z))))) - :tag meta :prefix)))) - (deftest test-cljs-1871 (let [ws (atom [])] (try @@ -1695,280 +1625,6 @@ #"Can't recur here" (analyze test-env invalid-try-recur-form))))) -(comment - (binding [ana/*cljs-ns* ana/*cljs-ns*] - (ana/no-warn - (env/with-compiler-env externs-cenv - (analyze (ana/empty-env) - '(let [React (js/require "react")] - React))))) - - ;; FIXME: we don't preserve tag information - (binding [ana/*cljs-ns* ana/*cljs-ns*] - (ana/no-warn - (env/with-compiler-env externs-cenv - (let [aenv (ana/empty-env) - _ (analyze aenv '(ns foo.core)) - aenv' (assoc-in aenv [:ns :name] 'foo.core) - _ (ana/analyze aenv' '(def x 1))] - (dissoc (ana/analyze-symbol (assoc-in aenv [:ns :name] 'foo.core) 'x) :env) - ;(get-in @externs-cenv [::ana/namespaces 'foo.core]) - )))) - ) - -(def core-inferred - ["var setTimeout;" "var process;" "process.hrtime;" - "goog.isArrayLike;" "Java.type;" "Object.out;" "Object.out.println;" - "Object.error;" "Object.error.println;"]) - -(defn infer-test-helper - [{:keys [forms externs warnings warn js-dependency-index with-core? opts]}] - (let [test-cenv (atom - (cond-> - (if with-core? - (env/default-compiler-env* - (closure/add-externs-sources (merge {:infer-externs true} opts))) - {::ana/externs - (externs/externs-map - (closure/load-externs {:externs (or externs [])}))}) - js-dependency-index (assoc :js-dependency-index js-dependency-index))) - wrap (if with-core? - #(comp/with-core-cljs nil %) - #(do (%)))] - (ana/with-warning-handlers [(collecting-warning-handler (or warnings (atom [])))] - (binding [ana/*analyze-deps* false - ana/*cljs-ns* ana/*cljs-ns*] - (env/with-compiler-env test-cenv - (wrap - (fn [] - (binding [ana/*analyze-deps* true - ana/*cljs-warnings* - (assoc ana/*cljs-warnings* - :infer-warning (if (nil? warn) true warn))] - (ana/analyze-form-seq forms)) - (with-out-str - (comp/emit-externs - (reduce util/map-merge {} - (map (comp :externs second) - (get @test-cenv ::ana/namespaces)))))))))))) - -(deftest test-basic-infer - (let [res (infer-test-helper - {:forms '[(ns foo.core) - (defn bar [a] (js/parseInt a)) - (def c js/React.Component) - (js/console.log "Hello world!") - (fn [& args] - (.apply (.-log js/console) js/console (into-array args))) - (js/console.log js/Number.MAX_VALUE) - (js/console.log js/Symbol.iterator)]})] - (is (= (unsplit-lines ["var React;" "React.Component;"]) res)))) - -(deftest test-method-infer - (let [res (infer-test-helper - {:forms '[(defn foo [^js/React.Component c] - (.render c))]})] - (is (= (unsplit-lines ["var React;" "React.Component;" "React.Component.prototype.render;"]) - res)))) - -(deftest test-minimal-infer - (let [res (infer-test-helper - {:forms '[(js/console.log (.wozMethod (js/baz)))] - :externs ["src/test/externs/test.js"]})] - (is (string/blank? res)))) - -(deftest test-type-hint-minimal-infer - (let [res (infer-test-helper - {:forms ''[(defn afun [^js/Foo x] - (.wozMethod x))] - :externs ["src/test/externs/test.js"]})] - (is (string/blank? res)))) - -(deftest test-type-hint-infer-unknown-method-in-chain - (let [ws (atom []) - res (infer-test-helper - {:forms '[(defn afun [^js/Foo.Bar x] - (let [z (.baz x)] - (.wozz z)))] - :externs ["src/test/externs/test.js"] - :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")))) - -(deftest test-type-hint-infer-unknown-property-in-chain - (let [ws (atom []) - res (infer-test-helper - {:forms '[(defn afun [^js/Foo.Bar x] - (let [z (.baz x)] - (.-wozz z)))] - :externs ["src/test/externs/test.js"] - :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")))) - -(deftest test-type-hint-infer-unknown-method - (let [ws (atom []) - res (infer-test-helper - {:forms '[(defn baz [^js/Foo a] - (.gozMethod a))] - :externs ["src/test/externs/test.js"] - :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")))) - -(deftest test-infer-unknown-method-from-externs - (let [ws (atom []) - res (infer-test-helper - {:forms '[(.gozMethod (js/baz))] - :externs ["src/test/externs/test.js"] - :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")))) - -(deftest test-infer-js-require - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns foo.core) - (def React (js/require "react")) - (.log js/console (.-Component React))] - :externs ["src/test/externs/test.js"] - :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")))) - -(deftest test-set-warn-on-infer - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns warn-on-infer-test.app) - (set! *warn-on-infer* true) - (defn wrap-baz [x] - (.baz x))] - :externs ["src/test/externs/test.js"] - :warnings ws - :warn false - :with-core? true})] - (is (= 1 (count @ws))) - (is (string/starts-with? (first @ws) "Cannot infer target type")))) - -(deftest test-cljs-1970-infer-with-cljs-literals - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns cjls-1970.core) - (set! *warn-on-infer* true) - (defn foo [] (list)) - (defn bar [] (vector))] - :externs ["src/test/externs/test.js"] - :warnings ws - :with-core? true})] - (is (zero? (count @ws))))) - -(deftest test-cljs-1918-infer-with-case-keywords - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns cjls-1918.core) - (defn foo [x] - (cljs.core/case x - :foo 1 - nil))] - :externs ["src/test/externs/test.js"] - :warnings ws - :with-core? true})] - (is (zero? (count @ws))))) - -(deftest test-cljs-2247 - (let [ws (atom [])] - (try - (ana/with-warning-handlers [(collecting-warning-handler ws)] - (env/with-compiler-env (assoc @test-cenv :repl-env {}) - (ana/analyze (ana/empty-env) - '(defn -foo [])) - (ana/analyze (ana/empty-env) - '(defprotocol IAlpha (-foo [this]))))) - (catch Exception _)) - (is (= ["Protocol IAlpha is overwriting function -foo"] @ws)))) - -(deftest test-cljs-2385-infer-priority - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns cjls-1918.core) - (defn thing [{:as this}] - (.componentDidUpdate ^js/Thing this))] - :externs ["src/test/externs/test.js"] - :warnings ws - :with-core? true})] - (is (string/includes? res "Thing.prototype.componentDidUpdate;")) - (is (zero? (count @ws))))) - -(deftest test-cljs-2392-broken-inferred-externs - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns cjls-1918.core - (:require [cljs.nodejs] - [cljs.nodejscli]))] - :warnings ws - :with-core? true - :opts {:target :nodejs}})] - (not (string/includes? res "COMPILED")) - (not (string/includes? res "goog")) - (is (zero? (count @ws))))) - -(deftest test-cljs-2678-global-exports-infer - (let [ws (atom []) - res (infer-test-helper - {:js-dependency-index {"react" {:global-exports '{react React}}} - :forms '[(ns foo.core - (:require [react :as react])) - (.log js/console react/Component)] - :warnings ws - :warn false})] - (is (= (unsplit-lines ["Object.Component;"]) res)))) - -(deftest test-cljs-2767-deftype-defrecord - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns cjls-2767.core) - (defrecord Foo [])] - :externs ["src/test/externs/test.js"] - :warnings ws - :with-core? true})] - (is (empty? @ws)) - (is (not (string/includes? res "cljs.core")))) - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns cjls-2767.core) - (deftype Foo [])] - :externs ["src/test/externs/test.js"] - :warnings ws - :with-core? true})] - (is (empty? @ws)) - (is (not (string/includes? res "cljs.core"))))) - -(deftest test-cljs-2790-defrecord-fields - (let [ws (atom []) - res (infer-test-helper - {:forms '[(ns cjls-2790.core) - (defrecord Foo [a b])] - :externs ["src/test/externs/test.js"] - :warnings ws - :with-core? true})] - (is (empty? @ws)) - (is (not (string/includes? res "cljs.core"))))) - (deftest test-locals-mapped-to-sym (testing "analyze should be robust to :locals mapping to symbols" (is (= [:local 'a] (-> (analyze (assoc-in test-env [:locals 'a] 'foo) 'a) @@ -2137,17 +1793,3 @@ (is (= w2 "cljs.core/-, all arguments must be numbers, got [string] instead")) (is (= w3 "cljs.core//, all arguments must be numbers, got [number string] instead")) (is (= w4 "cljs.core/*, all arguments must be numbers, got [string] instead"))))) - -(deftest test-cljs-3181 - (let [ws (atom []) - res (binding [ana/*cljs-static-fns* true] - (infer-test-helper - {:forms '[(ns warn-on-infer-test.app) - (set! *warn-on-infer* true) - (defn f [gfn] - (.then ^js/Promise (gfn (inc 1)) identity))] - :externs ["src/test/externs/test.js"] - :warnings ws - :warn false - :with-core? true}))] - (is (empty? @ws)))) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj new file mode 100644 index 000000000..2ae26ea61 --- /dev/null +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -0,0 +1,368 @@ +(ns cljs.externs-infer-tests + (:require + [cljs.analyzer :as ana] + [cljs.analyzer-tests :refer [analyze collecting-warning-handler test-cenv]] + [cljs.compiler :as comp] + [cljs.closure :as closure] + [cljs.env :as env] + [cljs.externs :as externs] + [cljs.test-util :refer [unsplit-lines]] + [cljs.util :as util] + [clojure.string :as string] + [clojure.test :refer [is are deftest testing]])) + +(def externs-cenv + (atom + {::ana/externs + (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"]}))})) + +(def core-inferred + ["var setTimeout;" "var process;" "process.hrtime;" + "goog.isArrayLike;" "Java.type;" "Object.out;" "Object.out.println;" + "Object.error;" "Object.error.println;"]) + +(deftest test-has-extern?-basic + (let [externs (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true}))] + (is (true? (ana/has-extern? '[Foo] externs))) + (is (true? (ana/has-extern? '[Foo wozMethod] externs))) + (is (false? (ana/has-extern? '[foo] externs))) + (is (false? (ana/has-extern? '[Foo gozMethod] externs))) + (is (true? (ana/has-extern? '[baz] externs))) + (is (false? (ana/has-extern? '[Baz] externs))))) + +(deftest test-has-extern?-defaults + (let [externs (externs/externs-map)] + (is (true? (ana/has-extern? '[console] externs))) + (is (true? (ana/has-extern? '[console log] externs))) + (is (true? (ana/has-extern? '[Number isNaN] externs))))) + +(deftest test-js-tag + (let [externs (externs/externs-map + (closure/load-externs + {: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/Foo (ana/js-tag '[baz] :ret-tag externs))))) + +(defn infer-test-helper + [{:keys [forms externs warnings warn js-dependency-index with-core? opts]}] + (let [test-cenv (atom + (cond-> + (if with-core? + (env/default-compiler-env* + (closure/add-externs-sources (merge {:infer-externs true} opts))) + {::ana/externs + (externs/externs-map + (closure/load-externs {:externs (or externs [])}))}) + js-dependency-index (assoc :js-dependency-index js-dependency-index))) + wrap (if with-core? + #(comp/with-core-cljs nil %) + #(do (%)))] + (ana/with-warning-handlers [(collecting-warning-handler (or warnings (atom [])))] + (binding [ana/*analyze-deps* false + ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env test-cenv + (wrap + (fn [] + (binding [ana/*analyze-deps* true + ana/*cljs-warnings* + (assoc ana/*cljs-warnings* + :infer-warning (if (nil? warn) true warn))] + (ana/analyze-form-seq forms)) + (with-out-str + (comp/emit-externs + (reduce util/map-merge {} + (map (comp :externs second) + (get @test-cenv ::ana/namespaces)))))))))))) + +(deftest test-externs-infer + (is (= 'js/Foo + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) 'js/baz))) + :info :ret-tag))) + (is (= 'js/Foo + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(js/baz)))) + :tag))) + (is (= 'js + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(js/woz)))) + :tag))) + (is (= 'js + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(def foo (js/woz))))) + :tag))) + (is (= 'js + (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) '(def foo js/boz)))) + :tag))) + (is (nil? (-> (binding [ana/*cljs-ns* ana/*cljs-ns*] + (ana/no-warn + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) + '(let [z (.baz ^js/Foo.Bar x)] + z))))) + :tag meta :prefix)))) + +(deftest test-basic-infer + (let [res (infer-test-helper + {:forms '[(ns foo.core) + (defn bar [a] (js/parseInt a)) + (def c js/React.Component) + (js/console.log "Hello world!") + (fn [& args] + (.apply (.-log js/console) js/console (into-array args))) + (js/console.log js/Number.MAX_VALUE) + (js/console.log js/Symbol.iterator)]})] + (is (= (unsplit-lines ["var React;" "React.Component;"]) res)))) + +(deftest test-method-infer + (let [res (infer-test-helper + {:forms '[(defn foo [^js/React.Component c] + (.render c))]})] + (is (= (unsplit-lines ["var React;" "React.Component;" "React.Component.prototype.render;"]) + res)))) + +(deftest test-minimal-infer + (let [res (infer-test-helper + {:forms '[(js/console.log (.wozMethod (js/baz)))] + :externs ["src/test/externs/test.js"]})] + (is (string/blank? res)))) + +(deftest test-type-hint-minimal-infer + (let [res (infer-test-helper + {:forms ''[(defn afun [^js/Foo x] + (.wozMethod x))] + :externs ["src/test/externs/test.js"]})] + (is (string/blank? res)))) + +(deftest test-type-hint-infer-unknown-method-in-chain + (let [ws (atom []) + res (infer-test-helper + {:forms '[(defn afun [^js/Foo.Bar x] + (let [z (.baz x)] + (.wozz z)))] + :externs ["src/test/externs/test.js"] + :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")))) + +(deftest test-type-hint-infer-unknown-property-in-chain + (let [ws (atom []) + res (infer-test-helper + {:forms '[(defn afun [^js/Foo.Bar x] + (let [z (.baz x)] + (.-wozz z)))] + :externs ["src/test/externs/test.js"] + :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")))) + +(deftest test-type-hint-infer-unknown-method + (let [ws (atom []) + res (infer-test-helper + {:forms '[(defn baz [^js/Foo a] + (.gozMethod a))] + :externs ["src/test/externs/test.js"] + :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")))) + +(deftest test-infer-unknown-method-from-externs + (let [ws (atom []) + res (infer-test-helper + {:forms '[(.gozMethod (js/baz))] + :externs ["src/test/externs/test.js"] + :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")))) + +(deftest test-infer-js-require + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns foo.core) + (def React (js/require "react")) + (.log js/console (.-Component React))] + :externs ["src/test/externs/test.js"] + :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")))) + +(deftest test-set-warn-on-infer + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns warn-on-infer-test.app) + (set! *warn-on-infer* true) + (defn wrap-baz [x] + (.baz x))] + :externs ["src/test/externs/test.js"] + :warnings ws + :warn false + :with-core? true})] + (is (= 1 (count @ws))) + (is (string/starts-with? (first @ws) "Cannot infer target type")))) + +(deftest test-cljs-1970-infer-with-cljs-literals + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns cjls-1970.core) + (set! *warn-on-infer* true) + (defn foo [] (list)) + (defn bar [] (vector))] + :externs ["src/test/externs/test.js"] + :warnings ws + :with-core? true})] + (is (zero? (count @ws))))) + +(deftest test-cljs-1918-infer-with-case-keywords + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns cjls-1918.core) + (defn foo [x] + (cljs.core/case x + :foo 1 + nil))] + :externs ["src/test/externs/test.js"] + :warnings ws + :with-core? true})] + (is (zero? (count @ws))))) + +(deftest test-cljs-2247 + (let [ws (atom [])] + (try + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env (assoc @test-cenv :repl-env {}) + (ana/analyze (ana/empty-env) + '(defn -foo [])) + (ana/analyze (ana/empty-env) + '(defprotocol IAlpha (-foo [this]))))) + (catch Exception _)) + (is (= ["Protocol IAlpha is overwriting function -foo"] @ws)))) + +(deftest test-cljs-2385-infer-priority + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns cjls-1918.core) + (defn thing [{:as this}] + (.componentDidUpdate ^js/Thing this))] + :externs ["src/test/externs/test.js"] + :warnings ws + :with-core? true})] + (is (string/includes? res "Thing.prototype.componentDidUpdate;")) + (is (zero? (count @ws))))) + +(deftest test-cljs-2392-broken-inferred-externs + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns cjls-1918.core + (:require [cljs.nodejs] + [cljs.nodejscli]))] + :warnings ws + :with-core? true + :opts {:target :nodejs}})] + (not (string/includes? res "COMPILED")) + (not (string/includes? res "goog")) + (is (zero? (count @ws))))) + +(deftest test-cljs-2678-global-exports-infer + (let [ws (atom []) + res (infer-test-helper + {:js-dependency-index {"react" {:global-exports '{react React}}} + :forms '[(ns foo.core + (:require [react :as react])) + (.log js/console react/Component)] + :warnings ws + :warn false})] + (is (= (unsplit-lines ["Object.Component;"]) res)))) + +(deftest test-cljs-2767-deftype-defrecord + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns cjls-2767.core) + (defrecord Foo [])] + :externs ["src/test/externs/test.js"] + :warnings ws + :with-core? true})] + (is (empty? @ws)) + (is (not (string/includes? res "cljs.core")))) + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns cjls-2767.core) + (deftype Foo [])] + :externs ["src/test/externs/test.js"] + :warnings ws + :with-core? true})] + (is (empty? @ws)) + (is (not (string/includes? res "cljs.core"))))) + +(deftest test-cljs-2790-defrecord-fields + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns cjls-2790.core) + (defrecord Foo [a b])] + :externs ["src/test/externs/test.js"] + :warnings ws + :with-core? true})] + (is (empty? @ws)) + (is (not (string/includes? res "cljs.core"))))) + +(deftest test-cljs-3181 + (let [ws (atom []) + res (binding [ana/*cljs-static-fns* true] + (infer-test-helper + {:forms '[(ns warn-on-infer-test.app) + (set! *warn-on-infer* true) + (defn f [gfn] + (.then ^js/Promise (gfn (inc 1)) identity))] + :externs ["src/test/externs/test.js"] + :warnings ws + :warn false + :with-core? true}))] + (is (empty? @ws)))) + +(comment + (binding [ana/*cljs-ns* ana/*cljs-ns*] + (ana/no-warn + (env/with-compiler-env externs-cenv + (analyze (ana/empty-env) + '(let [React (js/require "react")] + React))))) + + ;; FIXME: we don't preserve tag information + (binding [ana/*cljs-ns* ana/*cljs-ns*] + (ana/no-warn + (env/with-compiler-env externs-cenv + (let [aenv (ana/empty-env) + _ (analyze aenv '(ns foo.core)) + aenv' (assoc-in aenv [:ns :name] 'foo.core) + _ (ana/analyze aenv' '(def x 1))] + (dissoc (ana/analyze-symbol (assoc-in aenv [:ns :name] 'foo.core) 'x) :env) + ;(get-in @externs-cenv [::ana/namespaces 'foo.core]) + )))) + ) From 98effc9b6f9cc8d281b58761cf818a8bad041711 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 19:56:22 -0400 Subject: [PATCH 3546/4033] add test for CLJS-1924 --- src/test/clojure/cljs/externs_infer_tests.clj | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 2ae26ea61..9569e90e1 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -346,6 +346,16 @@ :with-core? true}))] (is (empty? @ws)))) +(deftest test-cljs-1924 + (let [ws (atom []) + res (binding [ana/*cljs-static-fns* true] + (infer-test-helper + {:forms '[(defrecord Foo [])] + :warnings ws + :warn true + :with-core? true}))] + (is (empty? @ws)))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From 889bfd196ceb59f3c358cfe5bee5060c1bc88af5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 19:57:43 -0400 Subject: [PATCH 3547/4033] a bit redundant with fix for CLJS-2767 but good to have a test that's exactly the original reported issue --- src/test/clojure/cljs/externs_infer_tests.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 9569e90e1..0d5cca902 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -350,7 +350,8 @@ (let [ws (atom []) res (binding [ana/*cljs-static-fns* true] (infer-test-helper - {:forms '[(defrecord Foo [])] + {:forms '[(set! *warn-on-infer* true) + (defrecord Foo [])] :warnings ws :warn true :with-core? true}))] From 7b4bd89c81e8da7fbe357759c85634acd9cf17ed Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 20:03:31 -0400 Subject: [PATCH 3548/4033] add failing test --- src/test/clojure/cljs/externs_infer_tests.clj | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 0d5cca902..42a90c05f 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -357,6 +357,20 @@ :with-core? true}))] (is (empty? @ws)))) +(deftest test-cljs-2862 + (let [ws (atom []) + res (binding [ana/*cljs-static-fns* true] + (infer-test-helper + {:forms '[(ns demo.app) + (set! *warn-on-infer* true) + (deftype Foo [] + Object + (bar [this] :bar))] + :warnings ws + :warn true + :with-core? true}))] + (is (empty? @ws)))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From 5cd08b4909a9184925fe82a56268e55f7b61e78d Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 20:29:03 -0400 Subject: [PATCH 3549/4033] CLJS-2862: Externs inference warning when extending Object extend-type macro modifies the prototype with one helper. Just add meta to the returned form and check that case in 'set! analysis. --- src/main/clojure/cljs/analyzer.cljc | 8 +++++++- src/main/clojure/cljs/core.cljc | 3 ++- src/test/clojure/cljs/externs_infer_tests.clj | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7be8e27df..83c20d815 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2514,7 +2514,13 @@ :else (when (seq? target) - (let [texpr (analyze-seq enve target nil)] + (let [texpr (if (-> target meta :extend-type) + ;; we're setting a prototype via extend-type macro + ;; nothing to warn + (binding [*cljs-warnings* + (assoc *cljs-warnings* :infer-warning false)] + (analyze-seq enve target nil)) + (analyze-seq enve target nil))] (when (:field texpr) texpr)))) vexpr (analyze enve val)] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3fdd62d44..15efe5055 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1461,7 +1461,8 @@ [tsym sym] `(.. ~tsym ~(to-property sym))) (core/defmethod extend-prefix :default - [tsym sym] `(.. ~tsym ~'-prototype ~(to-property sym))) + [tsym sym] + (with-meta `(.. ~tsym ~'-prototype ~(to-property sym)) {:extend-type true})) (core/defn- adapt-obj-params [type [[this & args :as sig] & body]] (core/list (vec args) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 42a90c05f..b296625e9 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -9,7 +9,7 @@ [cljs.test-util :refer [unsplit-lines]] [cljs.util :as util] [clojure.string :as string] - [clojure.test :refer [is are deftest testing]])) + [clojure.test :as test :refer [is are deftest testing]])) (def externs-cenv (atom From ccdd5f58856fe5f711a52f36c6a09788c15c2a5d Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 20:33:15 -0400 Subject: [PATCH 3550/4033] add failing test --- src/test/clojure/cljs/externs_infer_tests.clj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index b296625e9..d623e9510 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -371,6 +371,22 @@ :with-core? true}))] (is (empty? @ws)))) +(deftest test-cljs-2957 + (let [ws (atom []) + res (binding [ana/*cljs-static-fns* true] + (infer-test-helper + {:forms '[(ns test.foo + (:import [goog.history Html5History])) + + (set! *warn-on-infer* true) + + (doto (Html5History.) + (.setUseFragment false))] + :warnings ws + :warn true + :with-core? true}))] + (is (empty? @ws)))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From 8365848f3c9574c0896ebf122e8345241b1125f3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 21:09:30 -0400 Subject: [PATCH 3551/4033] add a test case to show that :import does in fact trigger goog ns analysis, leave note that this isn't good enough for the infer test to pass. --- src/test/clojure/cljs/analyzer_tests.clj | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index aca1edd2d..0c0eefaec 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -12,6 +12,7 @@ [cljs.analyzer.api :as ana-api] [cljs.compiler :as comp] [cljs.env :as env] + [cljs.js-deps :as deps] [cljs.test-util :refer [unsplit-lines]] [cljs.util :as util] [clojure.java.io :as io] @@ -1793,3 +1794,14 @@ (is (= w2 "cljs.core/-, all arguments must be numbers, got [string] instead")) (is (= w3 "cljs.core//, all arguments must be numbers, got [number string] instead")) (is (= w4 "cljs.core/*, all arguments must be numbers, got [string] instead"))))) + +;; this test does pass, but shows a current problem goog file analysis +;; we only consider the functional API, we don't include information needed +;; to infer usage of classes +(deftest test-analyze-goog-ns + (let [cenv (env/default-compiler-env)] + (env/with-compiler-env cenv + (ana/analyze-form-seq + '[(ns test.foo + (:import [goog.history Html5History]))])) + (is (some? (get-in @cenv [::ana/namespaces 'goog.history.Html5History :defs]))))) From 09d3220e979dcad2e8cfe9697aff918d885fabd0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 9 Apr 2020 21:29:53 -0400 Subject: [PATCH 3552/4033] whitespace, add TODO for simpler first test --- src/test/clojure/cljs/externs_infer_tests.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index d623e9510..f85bab46d 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -377,15 +377,16 @@ (infer-test-helper {:forms '[(ns test.foo (:import [goog.history Html5History])) - (set! *warn-on-infer* true) - (doto (Html5History.) (.setUseFragment false))] :warnings ws :warn true :with-core? true}))] - (is (empty? @ws)))) + (is (empty? @ws)) + ;; TODO: + ;; test that the ctor returns the expected type in a let + )) (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] From 99bacf26958d5521b61e2a2142cfb9e5be13db95 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 06:24:30 -0400 Subject: [PATCH 3553/4033] move type inference tests into their own ns --- src/test/clojure/cljs/analyzer_tests.clj | 349 ----------------- .../clojure/cljs/type_inference_tests.clj | 360 ++++++++++++++++++ 2 files changed, 360 insertions(+), 349 deletions(-) create mode 100644 src/test/clojure/cljs/type_inference_tests.clj diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 0c0eefaec..d2625edbc 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -180,9 +180,6 @@ (.getMessage (.getCause e)))) "Only one "))) -;; ============================================================================= -;; Inference tests - (def test-cenv (atom {})) (def test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.core)) (def test-core-env (atom {})) @@ -207,352 +204,6 @@ (binding [ana/*analyze-deps* false] (ana/analyze-file (io/file "src/main/cljs/cljs/core.cljs"))))) -(deftest basic-inference - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '1))) - 'number)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '"foo"))) - 'string)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '\a))) - 'string)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(make-array 10)))) - 'array)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(js-obj)))) - 'object)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '[]))) - 'cljs.core/IVector)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '{}))) - 'cljs.core/IMap)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '#{}))) - 'cljs.core/ISet)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env ()))) - 'cljs.core/IList)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(fn [x] x)))) - 'function))) - -(deftest if-inference - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (analyze test-env '(if x "foo" 1))))) - '#{number string}))) - -(deftest if-induced-inference - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (nil? x) x :kw)))))) - '#{clj-nil cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (boolean? x) x :kw)))))) - '#{boolean cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (number? x) x :kw)))))) - '#{number cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (double? x) x :kw)))))) - '#{number cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (float? x) x :kw)))))) - '#{number cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (integer? x) x :kw)))))) - '#{number cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (seq? x) x :kw)))))) - '#{seq cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (array? x) x :kw)))))) - '#{array cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x ^any []] (if (seqable? x) x :kw)))))) - '#{cljs.core/ISeqable array string cljs.core/Keyword})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (ana/analyze test-env '(let [x (namespace :x)] (if x x :kw)))))) - '#{string cljs.core/Keyword}))) - -(deftest loop-recur-inference - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (analyze test-env '(loop [x "a"] x))))) - 'string)) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (analyze test-env '(loop [x 10] - (if (pos? x) - (dec x) - x)))))) - 'number)) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (analyze test-env '((fn [p?] - (loop [x nil] - (if (p? x) - x - (recur (str x))))) - 11))))) - '#{string clj-nil})) - (is (= (ana/no-warn - (env/with-compiler-env test-cenv - (:tag (analyze test-env '((fn [^string x] - (loop [y x] - (if (= "x" y) - y - (recur 1)))) - "a"))))) - '#{number string}))) - -(deftest method-inference - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(.foo js/bar)))) - 'js))) - -(deftest fn-method-inference - ;; should always infer 'function as tag - (is (= 'function - (:tag - (env/with-compiler-env test-cenv - (analyze test-env - '(fn ([a] 1) ([a b] "foo") ([a b & r] ()))))))) - (is (nil? - (:ret-tag - (env/with-compiler-env test-cenv - (analyze test-env - '(fn ([a] 1) ([a b] "foo") ([a b & r] ())))))) ) - ;; methods should have inferred types - (is (= '(number string cljs.core/IList) - (map :tag - (:methods - (env/with-compiler-env test-cenv - (analyze test-env - '(fn ([a] 1) ([a b] "foo") ([a b & r] ()))))))))) - -(deftest fn-inference - (is (= 'number - (env/with-compiler-env test-cenv - (:tag (analyze test-env - '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - (x :one))))))) - (is (= 'string - (env/with-compiler-env test-cenv - (:tag (analyze test-env - '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - (x :one :two))))))) - (is (= 'cljs.core/IList - (env/with-compiler-env test-cenv - (:tag (analyze test-env - '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - (x :one :two :three))))))) - (is (= 'cljs.core/IList - (env/with-compiler-env test-cenv - (:tag (analyze test-env - '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] - (x :one :two :three :four)))))))) - -(deftest top-fn-inference - (env/with-compiler-env test-cenv - (ana/analyze-form-seq - '[(ns test.cljs-2901) - (defn foo - ([a] 1) - ([a b] "foo") - ([a b & r] ())) - (foo :one)])) - (is (= '[number string cljs.core/IList] - (map :tag - (get-in @test-cenv [::ana/namespaces 'test.cljs-2901 :defs 'foo :methods])))) - (is (= 'number - (:tag - (env/with-compiler-env test-cenv - (ana/analyze-form-seq - '[(ns test.cljs-2901) - (defn foo - ([a] 1) - ([a b] "foo") - ([a b & r] ())) - (foo :one)] - nil true))))) - (is (= 'string - (:tag - (env/with-compiler-env test-cenv - (ana/analyze-form-seq - '[(ns test.cljs-2901) - (defn foo - ([a] 1) - ([a b] "foo") - ([a b & r] ())) - (foo :one :two)] - nil true))))) - (is (= 'cljs.core/IList - (:tag - (env/with-compiler-env test-cenv - (ana/analyze-form-seq - '[(ns test.cljs-2901) - (defn foo - ([a] 1) - ([a b] "foo") - ([a b & r] ())) - (foo :one :two :three)] - nil true)))))) - -(deftest variadic-fn-inference - (is (= '(cljs.core/IList) - (map :tag - (:methods - (env/with-compiler-env test-cenv - (analyze test-env - '(fn ([a b & r] ())))))))) - (is (= 'cljs.core/IList - (env/with-compiler-env test-cenv - (:tag (analyze test-env - '(let [x (fn ([a b & r] ()))] - (x :one :two))))))) - - (is (= 'cljs.core/IList - (env/with-compiler-env test-cenv - (:tag (analyze test-env - '(let [x (fn ([a b & r] ()))] - (x :one :two :three))))))) - - (is (= 'cljs.core/IList - (env/with-compiler-env test-cenv - (:tag (analyze test-env - '(let [x (fn ([a b & r] ()))] - (x :one :two :three :four))))))) - ) - -(deftest top-variadic-fn-inference - (env/with-compiler-env test-cenv - (ana/analyze-form-seq - '[(ns test.cljs-2901-b) - (defn foo ([a b & r] ())) - (foo :one :two :three :four)] - nil false)) - (is (= '[cljs.core/IList] - (map :tag - (get-in @test-cenv - [::ana/namespaces 'test.cljs-2901-b :defs 'foo :methods])))) - (is (= 'cljs.core/IList - (:tag - (env/with-compiler-env test-cenv - (ana/analyze-form-seq - '[(ns test.cljs-2901-b) - (defn foo ([a b & r] ())) - (foo :one :two)] - nil true))))) - (is (= 'cljs.core/IList - (:tag - (env/with-compiler-env test-cenv - (ana/analyze-form-seq - '[(ns test.cljs-2901-b) - (defn foo ([a b & r] ())) - (foo :one :two :three)] - nil true))))) - (is (= 'cljs.core/IList - (:tag - (env/with-compiler-env test-cenv - (ana/analyze-form-seq - '[(ns test.cljs-2901-b) - (defn foo ([a b & r] ())) - (foo :one :two :three :four)] - nil true)))))) - -(deftest lib-inference - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(+ 1 2)))) - 'number)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(alength (array))))) - 'number)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(aclone (array))))) - 'array)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(-count [1 2 3])))) - 'number)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(count [1 2 3])))) - 'number)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(into-array [1 2 3])))) - 'array)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(js-obj)))) - 'object)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(-conj [] 1)))) - 'clj)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(conj [] 1)))) - 'clj)) - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(dissoc {:foo :bar} :foo)))) - '#{clj clj-nil})) - ;; 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 test-always-true-if - (is (= (env/with-compiler-env test-cenv - (:tag (analyze test-env '(if 1 2 "foo")))) - 'number))) - -;; will only work if the previous test works -(deftest test-count - (is (= (cljs.env/with-compiler-env test-cenv - (:tag (analyze test-env '(count [])))) - 'number)) - ) - -(deftest test-numeric - (is (= (ana/no-warn - (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)) - (is (= (ana/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (analyze test-env '(unchecked-int x))))) - 'number)) - (is (= (ana/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (analyze test-env '(mod x y))))) - 'number)) - (is (= (ana/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (analyze test-env '(quot x y))))) - 'number)) - (is (= (ana/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (analyze test-env '(rem x y))))) - 'number)) - (is (= (ana/no-warn - (cljs.env/with-compiler-env test-cenv - (:tag (analyze test-env '(bit-count n))))) - 'number)) - ) - ;; ============================================================================= ;; Catching errors during macroexpansion diff --git a/src/test/clojure/cljs/type_inference_tests.clj b/src/test/clojure/cljs/type_inference_tests.clj new file mode 100644 index 000000000..8d13401c1 --- /dev/null +++ b/src/test/clojure/cljs/type_inference_tests.clj @@ -0,0 +1,360 @@ +(ns cljs.type-inference-tests + (:require + [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api] + [cljs.analyzer-tests :refer [analyze test-env test-cenv]] + [cljs.compiler :as comp] + [cljs.env :as env] + [cljs.js-deps :as deps] + [cljs.test-util :refer [unsplit-lines]] + [cljs.util :as util] + [clojure.java.io :as io] + [clojure.set :as set] + [clojure.string :as string] + [clojure.test :refer [is are deftest testing]])) + +(deftest basic-inference + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '1))) + 'number)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '"foo"))) + 'string)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '\a))) + 'string)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(make-array 10)))) + 'array)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(js-obj)))) + 'object)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '[]))) + 'cljs.core/IVector)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '{}))) + 'cljs.core/IMap)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '#{}))) + 'cljs.core/ISet)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env ()))) + 'cljs.core/IList)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(fn [x] x)))) + 'function))) + +(deftest if-inference + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (analyze test-env '(if x "foo" 1))))) + '#{number string}))) + +(deftest if-induced-inference + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (nil? x) x :kw)))))) + '#{clj-nil cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (boolean? x) x :kw)))))) + '#{boolean cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (number? x) x :kw)))))) + '#{number cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (double? x) x :kw)))))) + '#{number cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (float? x) x :kw)))))) + '#{number cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (integer? x) x :kw)))))) + '#{number cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (seq? x) x :kw)))))) + '#{seq cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (array? x) x :kw)))))) + '#{array cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x ^any []] (if (seqable? x) x :kw)))))) + '#{cljs.core/ISeqable array string cljs.core/Keyword})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (ana/analyze test-env '(let [x (namespace :x)] (if x x :kw)))))) + '#{string cljs.core/Keyword}))) + +(deftest loop-recur-inference + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (analyze test-env '(loop [x "a"] x))))) + 'string)) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (analyze test-env '(loop [x 10] + (if (pos? x) + (dec x) + x)))))) + 'number)) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (analyze test-env '((fn [p?] + (loop [x nil] + (if (p? x) + x + (recur (str x))))) + 11))))) + '#{string clj-nil})) + (is (= (ana/no-warn + (env/with-compiler-env test-cenv + (:tag (analyze test-env '((fn [^string x] + (loop [y x] + (if (= "x" y) + y + (recur 1)))) + "a"))))) + '#{number string}))) + +(deftest method-inference + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(.foo js/bar)))) + 'js))) + +(deftest fn-method-inference + ;; should always infer 'function as tag + (is (= 'function + (:tag + (env/with-compiler-env test-cenv + (analyze test-env + '(fn ([a] 1) ([a b] "foo") ([a b & r] ()))))))) + (is (nil? + (:ret-tag + (env/with-compiler-env test-cenv + (analyze test-env + '(fn ([a] 1) ([a b] "foo") ([a b & r] ())))))) ) + ;; methods should have inferred types + (is (= '(number string cljs.core/IList) + (map :tag + (:methods + (env/with-compiler-env test-cenv + (analyze test-env + '(fn ([a] 1) ([a b] "foo") ([a b & r] ()))))))))) + +(deftest fn-inference + (is (= 'number + (env/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + (x :one))))))) + (is (= 'string + (env/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + (x :one :two))))))) + (is (= 'cljs.core/IList + (env/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + (x :one :two :three))))))) + (is (= 'cljs.core/IList + (env/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a] 1) ([a b] "foo") ([a b & r] ()))] + (x :one :two :three :four)))))))) + +(deftest top-fn-inference + (env/with-compiler-env test-cenv + (ana/analyze-form-seq + '[(ns test.cljs-2901) + (defn foo + ([a] 1) + ([a b] "foo") + ([a b & r] ())) + (foo :one)])) + (is (= '[number string cljs.core/IList] + (map :tag + (get-in @test-cenv [::ana/namespaces 'test.cljs-2901 :defs 'foo :methods])))) + (is (= 'number + (:tag + (env/with-compiler-env test-cenv + (ana/analyze-form-seq + '[(ns test.cljs-2901) + (defn foo + ([a] 1) + ([a b] "foo") + ([a b & r] ())) + (foo :one)] + nil true))))) + (is (= 'string + (:tag + (env/with-compiler-env test-cenv + (ana/analyze-form-seq + '[(ns test.cljs-2901) + (defn foo + ([a] 1) + ([a b] "foo") + ([a b & r] ())) + (foo :one :two)] + nil true))))) + (is (= 'cljs.core/IList + (:tag + (env/with-compiler-env test-cenv + (ana/analyze-form-seq + '[(ns test.cljs-2901) + (defn foo + ([a] 1) + ([a b] "foo") + ([a b & r] ())) + (foo :one :two :three)] + nil true)))))) + +(deftest variadic-fn-inference + (is (= '(cljs.core/IList) + (map :tag + (:methods + (env/with-compiler-env test-cenv + (analyze test-env + '(fn ([a b & r] ())))))))) + (is (= 'cljs.core/IList + (env/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a b & r] ()))] + (x :one :two))))))) + + (is (= 'cljs.core/IList + (env/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a b & r] ()))] + (x :one :two :three))))))) + + (is (= 'cljs.core/IList + (env/with-compiler-env test-cenv + (:tag (analyze test-env + '(let [x (fn ([a b & r] ()))] + (x :one :two :three :four))))))) + ) + +(deftest top-variadic-fn-inference + (env/with-compiler-env test-cenv + (ana/analyze-form-seq + '[(ns test.cljs-2901-b) + (defn foo ([a b & r] ())) + (foo :one :two :three :four)] + nil false)) + (is (= '[cljs.core/IList] + (map :tag + (get-in @test-cenv + [::ana/namespaces 'test.cljs-2901-b :defs 'foo :methods])))) + (is (= 'cljs.core/IList + (:tag + (env/with-compiler-env test-cenv + (ana/analyze-form-seq + '[(ns test.cljs-2901-b) + (defn foo ([a b & r] ())) + (foo :one :two)] + nil true))))) + (is (= 'cljs.core/IList + (:tag + (env/with-compiler-env test-cenv + (ana/analyze-form-seq + '[(ns test.cljs-2901-b) + (defn foo ([a b & r] ())) + (foo :one :two :three)] + nil true))))) + (is (= 'cljs.core/IList + (:tag + (env/with-compiler-env test-cenv + (ana/analyze-form-seq + '[(ns test.cljs-2901-b) + (defn foo ([a b & r] ())) + (foo :one :two :three :four)] + nil true)))))) + +(deftest lib-inference + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(+ 1 2)))) + 'number)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(alength (array))))) + 'number)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(aclone (array))))) + 'array)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(-count [1 2 3])))) + 'number)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(count [1 2 3])))) + 'number)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(into-array [1 2 3])))) + 'array)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(js-obj)))) + 'object)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(-conj [] 1)))) + 'clj)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(conj [] 1)))) + 'clj)) + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(dissoc {:foo :bar} :foo)))) + '#{clj clj-nil})) + ;; 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 test-always-true-if + (is (= (env/with-compiler-env test-cenv + (:tag (analyze test-env '(if 1 2 "foo")))) + 'number))) + +;; will only work if the previous test works +(deftest test-count + (is (= (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(count [])))) + 'number)) + ) + +(deftest test-numeric + (is (= (ana/no-warn + (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)) + (is (= (ana/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(unchecked-int x))))) + 'number)) + (is (= (ana/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(mod x y))))) + 'number)) + (is (= (ana/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(quot x y))))) + 'number)) + (is (= (ana/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(rem x y))))) + 'number)) + (is (= (ana/no-warn + (cljs.env/with-compiler-env test-cenv + (:tag (analyze test-env '(bit-count n))))) + 'number)) + ) From aead1377637c4e667b8e41dbe313880fb1caf90a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 06:25:25 -0400 Subject: [PATCH 3554/4033] remove unused requires --- src/test/clojure/cljs/type_inference_tests.clj | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/test/clojure/cljs/type_inference_tests.clj b/src/test/clojure/cljs/type_inference_tests.clj index 8d13401c1..dc5a687bb 100644 --- a/src/test/clojure/cljs/type_inference_tests.clj +++ b/src/test/clojure/cljs/type_inference_tests.clj @@ -1,16 +1,9 @@ (ns cljs.type-inference-tests (:require [cljs.analyzer :as ana] - [cljs.analyzer.api :as ana-api] [cljs.analyzer-tests :refer [analyze test-env test-cenv]] - [cljs.compiler :as comp] [cljs.env :as env] - [cljs.js-deps :as deps] [cljs.test-util :refer [unsplit-lines]] - [cljs.util :as util] - [clojure.java.io :as io] - [clojure.set :as set] - [clojure.string :as string] [clojure.test :refer [is are deftest testing]])) (deftest basic-inference From 2055de564936bfe7975cce9aec4d98685e2aeace Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 06:29:51 -0400 Subject: [PATCH 3555/4033] add ctor inference assertion --- src/test/clojure/cljs/type_inference_tests.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/type_inference_tests.clj b/src/test/clojure/cljs/type_inference_tests.clj index dc5a687bb..dd0233af7 100644 --- a/src/test/clojure/cljs/type_inference_tests.clj +++ b/src/test/clojure/cljs/type_inference_tests.clj @@ -36,7 +36,11 @@ 'cljs.core/IList)) (is (= (env/with-compiler-env test-cenv (:tag (analyze test-env '(fn [x] x)))) - 'function))) + 'function)) + (is (= (env/with-compiler-env test-cenv + (ana/no-warn + (:tag (analyze test-env '(Foo.))))) + 'cljs.core/Foo))) (deftest if-inference (is (= (ana/no-warn From 09e513cc1e2a27cf5d539902eccf9964dda8217e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 06:35:53 -0400 Subject: [PATCH 3556/4033] add assertion for ctor inference in a let binding --- src/test/clojure/cljs/type_inference_tests.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/clojure/cljs/type_inference_tests.clj b/src/test/clojure/cljs/type_inference_tests.clj index dd0233af7..5f6454582 100644 --- a/src/test/clojure/cljs/type_inference_tests.clj +++ b/src/test/clojure/cljs/type_inference_tests.clj @@ -355,3 +355,11 @@ (:tag (analyze test-env '(bit-count n))))) 'number)) ) + +(deftest test-ctor-infer + (is (= (env/with-compiler-env test-cenv + (ana/no-warn + (:tag (analyze test-cenv + '(let [g (Foo.)] + q))))) + 'cljs.core/Foo))) From cfcc13da3f08496d26301d87a8f106b88bc6949e Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 09:02:57 -0400 Subject: [PATCH 3557/4033] cljs.externs/ns-match? was ignoring ctors that are exactly the namespace, add test case --- src/main/clojure/cljs/externs.clj | 11 +++++++---- src/test/clojure/cljs/analyzer_tests.clj | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index a20a897cb..c2f73954b 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -205,9 +205,12 @@ defaults sources)))) (defn ns-match? [ns-segs var-segs] - (and - (= (inc (count ns-segs)) (count var-segs)) - (= ns-segs (take (count ns-segs) var-segs)))) + (or + ;; exact match (i.e. ctors) + (= ns-segs var-segs) + (and + (= (inc (count ns-segs)) (count var-segs)) + (= ns-segs (take (count ns-segs) var-segs))))) (defn parsed->defs [externs] (let [ns-segs (into [] (map symbol (string/split (str *goog-ns*) #"\.")))] @@ -325,4 +328,4 @@ parse-externs index-externs (get 'React) (find 'Component) first meta) - ) \ No newline at end of file + ) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index d2625edbc..3b28a9f8e 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1456,3 +1456,22 @@ '[(ns test.foo (:import [goog.history Html5History]))])) (is (some? (get-in @cenv [::ana/namespaces 'goog.history.Html5History :defs]))))) + +(deftest test-analyze-goog-ns-ctor + (let [cenv (env/default-compiler-env)] + (env/with-compiler-env cenv + (ana/analyze-form-seq + '[(ns test.foo + (:import [goog.history Html5History]))])) + (is (some? (get-in @cenv [::ana/namespaces 'goog.history.Html5History :defs 'Html5History]))))) + +(comment + + (let [cenv (env/default-compiler-env)] + (env/with-compiler-env cenv + (ana/analyze-form-seq + '[(ns test.foo + (:import [goog.history Html5History]))])) + (get-in @cenv [::ana/namespaces 'goog.history.Html5History :defs])) + + ) From abe31601a6c4506d6e2f3446c8f99d0e85a1b60a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 12:49:58 -0400 Subject: [PATCH 3558/4033] fix externs test, we have one new def from externs parsing now that we're letting ctors through --- src/test/clojure/cljs/externs_parsing_test.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/externs_parsing_test.clj b/src/test/clojure/cljs/externs_parsing_test.clj index 46fe5aa37..0995e1638 100644 --- a/src/test/clojure/cljs/externs_parsing_test.clj +++ b/src/test/clojure/cljs/externs_parsing_test.clj @@ -27,7 +27,7 @@ v (get-in ns [:defs 'getWeekNumber])] (is (= 3 (-> v :method-params first count)))) (let [ns (externs/analyze-goog-file "goog/date/date.js" 'goog.date.month)] - (is (= 12 (-> ns :defs count))))) + (is (= 13 (-> ns :defs count))))) (deftest cljs-3170&3189 (let [ns (externs/analyze-goog-file "goog/object/object.js")] @@ -40,4 +40,4 @@ (externs/analyze-goog-file "goog/date/date.js" 'goog.date.month) - ) \ No newline at end of file + ) From aa5380716c71cc6f290d3268c982eba80bb9ffe5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 14:29:00 -0400 Subject: [PATCH 3559/4033] CLJS-712 & CLJS-2957: resolve-var for dot still wrong, externs inference regression The AST alignment changes reveal that our handling of the dot form was still incorrect. When resolve-var returns a dotted :var like foo.bar/baz.woz it gets desugared. In this case of GCL import this would lead to malformed :host-field AST nodes. Now in resolve var if we have a dotted symbol first we check that the prefix is *not* something that can be resolved. If can't be resolved then we don't do anything other than convert the form from foo.bar.baz to foo.bar/baz. Add tests cases for type inference, externs inference, and code gen. --- src/main/clojure/cljs/analyzer.cljc | 33 +++++++++---------- src/test/clojure/cljs/analyzer_tests.clj | 1 - src/test/clojure/cljs/compiler_tests.clj | 27 +++++++++++++++ src/test/clojure/cljs/externs_infer_tests.clj | 5 +-- .../clojure/cljs/type_inference_tests.clj | 23 +++++++++---- 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 83c20d815..6daf0ab21 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1156,8 +1156,11 @@ (defn resolve-var "Resolve a var. Accepts a side-effecting confirm fn for producing warnings about unresolved vars." - ([env sym] (resolve-var env sym nil)) + ([env sym] + (resolve-var env sym nil)) ([env sym confirm] + (resolve-var env sym confirm true)) + ([env sym confirm default?] (let [locals (:locals env)] (if #?(:clj (= "js" (namespace sym)) :cljs (identical? "js" (namespace sym))) @@ -1209,21 +1212,15 @@ (let [idx (.indexOf s ".") prefix (symbol (subs s 0 idx)) suffix (subs s (inc idx))] - (if-some [lb (handle-symbol-local prefix (get locals prefix))] - {:op :local - :name (symbol (str (:name lb) "." suffix))} - (if-some [full-ns (gets @env/*compiler* ::namespaces current-ns :imports prefix)] - {:op :js-var - :name (symbol (str full-ns) suffix)} - (if-some [info (gets @env/*compiler* ::namespaces current-ns :defs prefix)] - (merge info - {:name (symbol (str current-ns) (str sym)) - :op :var - :ns current-ns}) - (merge (gets @env/*compiler* ::namespaces prefix :defs (symbol suffix)) - {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix)) - :op :var - :ns prefix}))))) + ;; check if prefix is some existing def + (if-let [resolved (resolve-var env prefix nil false)] + (update resolved :name #(symbol (str % "." suffix))) + (let [idx (.lastIndexOf s ".") + pre (subs s 0 idx) + suf (subs s (inc idx))] + {:op :var + :name (symbol pre suf) + :ns (symbol pre)}))) (some? (gets @env/*compiler* ::namespaces current-ns :uses sym)) (let [full-ns (gets @env/*compiler* ::namespaces current-ns :uses sym)] @@ -1236,7 +1233,7 @@ (resolve* env sym full-ns current-ns)) (some? (gets @env/*compiler* ::namespaces current-ns :imports sym)) - (recur env (gets @env/*compiler* ::namespaces current-ns :imports sym) confirm) + (recur env (gets @env/*compiler* ::namespaces current-ns :imports sym) confirm default?) (some? (gets @env/*compiler* ::namespaces current-ns :defs sym)) (do @@ -1260,7 +1257,7 @@ (resolve-invokeable-ns s current-ns env) :else - (do + (when default? (when (some? confirm) (confirm env current-ns sym)) (merge (gets @env/*compiler* ::namespaces current-ns :defs sym) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 3b28a9f8e..6377b617e 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -12,7 +12,6 @@ [cljs.analyzer.api :as ana-api] [cljs.compiler :as comp] [cljs.env :as env] - [cljs.js-deps :as deps] [cljs.test-util :refer [unsplit-lines]] [cljs.util :as util] [clojure.java.io :as io] diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index cb0e71e17..aaddd6647 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -32,6 +32,17 @@ (def aenv (assoc-in (ana/empty-env) [:ns :name] 'cljs.user)) (def cenv (env/default-compiler-env)) +(defn compile-form-seq + ([forms] + (compile-form-seq forms + (when env/*compiler* + (:options @env/*compiler*)))) + ([forms opts] + (with-out-str + (binding [ana/*cljs-ns* 'cljs.user] + (doseq [form forms] + (comp/emit (ana/analyze (ana/empty-env) form))))))) + #_(deftest should-recompile (let [src (File. "test/hello.cljs") dst (File/createTempFile "compilertest" ".cljs") @@ -331,6 +342,22 @@ (is (not (str/includes? snippet3 "(function (x){"))) )) +(deftest test-goog-ctor-import-gen + (is (true? (str/includes? + (env/with-compiler-env (env/default-compiler-env) + (compile-form-seq + '[(ns test.foo + (:import [goog.history Html5History])) + (defn bar [] Html5History)])) + "return goog.history.Html5History;"))) + (is (true? (str/includes? + (env/with-compiler-env (env/default-compiler-env) + (compile-form-seq + '[(ns test.foo + (:import [goog.history Html5History])) + (def hist (Html5History.))])) + "(new goog.history.Html5History());")))) + ;; CLJS-1225 (comment diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index f85bab46d..20efe1667 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -383,10 +383,7 @@ :warnings ws :warn true :with-core? true}))] - (is (empty? @ws)) - ;; TODO: - ;; test that the ctor returns the expected type in a let - )) + (is (empty? @ws)))) (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] diff --git a/src/test/clojure/cljs/type_inference_tests.clj b/src/test/clojure/cljs/type_inference_tests.clj index 5f6454582..c9c5f6343 100644 --- a/src/test/clojure/cljs/type_inference_tests.clj +++ b/src/test/clojure/cljs/type_inference_tests.clj @@ -357,9 +357,20 @@ ) (deftest test-ctor-infer - (is (= (env/with-compiler-env test-cenv - (ana/no-warn - (:tag (analyze test-cenv - '(let [g (Foo.)] - q))))) - 'cljs.core/Foo))) + (is (= 'cljs.core/Foo + (:tag + (env/with-compiler-env test-cenv + (ana/no-warn + (analyze test-env + '(let [g (Foo.)] + g)))))))) + +(deftest test-goog-import-ctor-infer + (is (= 'goog.history/Html5History + (:tag + (env/with-compiler-env (env/default-compiler-env) + (ana/analyze-form-seq + '[(ns test.foo + (:import [goog.history Html5History])) + (Html5History.)] + {} true)))))) From f7436cd53f31aca3f00403357e048598e2513d22 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 14:39:44 -0400 Subject: [PATCH 3560/4033] CLJS-3211: Widen cljs.core/defprotocol sig arguments from list to seq (to include Cons) --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 15efe5055..0eefd9aef 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2040,7 +2040,7 @@ (recur (assoc opts :doc head) methods tail) (core/keyword? head) (recur (assoc opts head (first tail)) methods (rest tail)) - (core/list? head) + (core/seq? head) (recur opts (conj methods head) tail) :else (throw #?(:clj (Exception. From 05cd4d5171f25ba856ac194c409842fcae27162e Mon Sep 17 00:00:00 2001 From: roman01la Date: Fri, 12 Jul 2019 14:35:51 +0300 Subject: [PATCH 3561/4033] CLJS-3086: Switch clj-nil to not be considered a numeric type --- src/main/clojure/cljs/analyzer.cljc | 1 - src/test/clojure/cljs/analyzer_tests.clj | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6daf0ab21..867b245e7 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3477,7 +3477,6 @@ ;; warning without this - David (cond (nil? t) true - (= 'clj-nil t) true (js-tag? t) true ;; TODO: revisit :else (if (and (symbol? t) (some? (get NUMERIC_SET t))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 6377b617e..97902053c 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1346,6 +1346,16 @@ (env/with-compiler-env test-cenv (:tag (analyze test-env '(demunge-str ""))))))) +(deftest test-cljs-3086 + (let [ws (atom [])] + (try + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (cljs.env/with-compiler-env test-cenv + (analyze (ana/empty-env) + '(+ nil 1)))) + (catch Exception _)) + (is (= ["cljs.core/+, all arguments must be numbers, got [clj-nil number] instead"] @ws)))) + (deftest test-cljs-3120 (let [cenv (core-env) _ (analyze-forms cenv From f4cc1282a2b1087d80af26ff5dd3c53792511a15 Mon Sep 17 00:00:00 2001 From: roman01la Date: Sun, 17 Nov 2019 23:20:00 +0100 Subject: [PATCH 3562/4033] CLJS-2863: Need to reject incorrect fn with fixed arity of more params than variadic --- src/main/clojure/cljs/core.cljc | 24 +++++++++++++++++------- src/test/clojure/cljs/analyzer_tests.clj | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 0eefd9aef..51d30bf5f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3217,18 +3217,23 @@ (fn ~method))))] (core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name)) arglists (map first fdecl) - varsig? #(some '#{&} %) - variadic (boolean (some varsig? arglists)) - sigs (remove varsig? arglists) + macro? (:macro meta) + varsig? #(boolean (some '#{&} %)) + {sigs false var-sigs true} (group-by varsig? arglists) + variadic? (core/pos? (core/count var-sigs)) + variadic-params (if variadic? + (core/cond-> (remove '#{&} (first var-sigs)) + true core/count + macro? (core/- 2)) + 0) maxfa (apply core/max (concat (map count sigs) - [(core/- (count (first (filter varsig? arglists))) 2)])) - macro? (:macro meta) + [(core/- (count (first var-sigs)) 2)])) mfa (core/cond-> maxfa macro? (core/- 2)) meta (assoc meta :top-fn - {:variadic? variadic + {:variadic? variadic? :fixed-arity mfa :max-fixed-arity mfa :method-params (core/cond-> sigs macro? elide-implicit-macro-args) @@ -3237,6 +3242,11 @@ args-sym (gensym "args") param-counts (map count arglists) name (with-meta name meta)] + (core/when (core/< 1 (count var-sigs)) + (ana/warning :multiple-variadic-overloads {} {:name name})) + (core/when (core/and (core/pos? variadic-params) + (not (core/== variadic-params (core/+ 1 mfa)))) + (ana/warning :variadic-max-arity {} {:name name})) (core/when (not= (distinct param-counts) param-counts) (ana/warning :overload-arity {} {:name name})) `(do @@ -3244,7 +3254,7 @@ (fn [~'var_args] (case (alength (js-arguments)) ~@(mapcat #(fixed-arity rname %) sigs) - ~(if variadic + ~(if variadic? `(let [args-arr# (array)] (copy-arguments args-arr#) (let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 97902053c..2bec4eb60 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -399,6 +399,27 @@ (catch Exception _)) (is (.startsWith (first @ws) "myfun: Can't have 2 overloads with same arity")))) +(deftest test-cljs-2863 + (let [ws (atom [])] + (try + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (analyze (ana/empty-env) + '(defn myfun + ([x] x) + ([& xs] xs)))) + (catch Exception _)) + (is (.startsWith (first @ws) "myfun: Can't have fixed arity function with more params than variadic function"))) + + (let [ws (atom [])] + (try + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (analyze (ana/empty-env) + '(defn myfun + ([& x] x) + ([& xs] xs)))) + (catch Exception _)) + (is (.startsWith (first @ws) "myfun: Can't have more than 1 variadic overload")))) + (deftest test-canonicalize-specs (is (= (ana/canonicalize-specs '((quote [clojure.set :as set]))) '([clojure.set :as set]))) From 12ce923f89f5c77c7a0a50b3351606f471901bf8 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 9 Sep 2018 11:26:05 -0400 Subject: [PATCH 3563/4033] CLJS-2898: cljs.main: ClojureScript version should not be displayed if there are inits --- src/main/clojure/cljs/cli.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 623936529..be8623239 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -325,6 +325,11 @@ present" (or (fast-initial-prompt? repl-env inits) (::repl/fast-initial-prompt? (repl/repl-options renv))) + :quit-prompt + (if (empty? inits) + repl/repl-title + (constantly nil)) + :inits (into [{:type :init-forms From adaef453d0e58d910f6ab7a1f3e1ec215fa84e9a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 10 Apr 2020 21:02:46 -0400 Subject: [PATCH 3564/4033] add cljs.analyzer/node-like? helper for detecting when we're generating code that's Node-like, but not actually targeting the Node.js runtime. In this case we should generate externs for library usage. --- src/main/clojure/cljs/analyzer.cljc | 40 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 867b245e7..7b8e6fc9c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -165,11 +165,14 @@ (defn unchecked-arrays? [] *unchecked-arrays*) +(defn compiler-options [] + (get @env/*compiler* :options)) + (defn checked-arrays "Returns false-y, :warn, or :error based on configuration and the current value of *unchecked-arrays*." [] - (when (and (not (-> @env/*compiler* :options :advanced)) + (when (and (not (:advanced (compiler-options))) (not *unchecked-arrays*)) *checked-arrays*)) @@ -1070,19 +1073,38 @@ :op :js-var :ns full-ns}) +(defn extern-pre [sym current-ns] + (let [pre (into '[Object] (->> (string/split (name sym) #"\.") (map symbol) vec))] + (when-not (has-extern? pre) + (swap! env/*compiler* update-in + (into [::namespaces current-ns :externs] pre) merge {})) + pre)) + +(defn node-like? + ([] + (node-like? (compiler-options))) + ([opts] + (and (= :nodejs (:target opts)) + (false? (:nodejs-rt opts))))) + (defmethod resolve* :node [env sym full-ns current-ns] - {:ns current-ns - :name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) - :op :js-var - :foreign true}) + ;; not actually targeting Node.js, we need to generate externs + (if (node-like?) + (let [pre (extern-pre sym current-ns)] + {:ns current-ns + :name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) + :op :js-var + :tag (with-meta 'js {:prefix pre}) + :foreign true}) + {:ns current-ns + :name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym))) + :op :js-var + :foreign true})) (defmethod resolve* :global [env sym full-ns current-ns] - (let [pre (into '[Object] (->> (string/split (name sym) #"\.") (map symbol) vec))] - (when-not (has-extern? pre) - (swap! env/*compiler* update-in - (into [::namespaces current-ns :externs] pre) merge {})) + (let [pre (extern-pre sym current-ns)] {:ns current-ns :name (symbol (str current-ns) (str (munge-global-export full-ns) "." (name sym))) :op :js-var From 8c7bc5e4486a823509e5c3ff4cd2c1e50da85c16 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Fri, 14 Sep 2018 16:57:14 -0400 Subject: [PATCH 3565/4033] CLJS-2908: Don't show quick prompt if :verbose or :repl-verbose --- src/main/clojure/cljs/cli.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index be8623239..9013f3433 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -301,9 +301,11 @@ is trying load some arbitrary ns." (when (string/starts-with? repl-ns "cljs.repl.") (subs repl-ns (count "cljs.repl."))))) -(defn- fast-initial-prompt? [repl-env inits] +(defn- fast-initial-prompt? [repl-env options inits] (boolean (and (empty? inits) + (not (:verbose options)) + (not (:repl-verbose options)) (contains? #{"node"} (repl-name repl-env))))) (defn- repl-opt @@ -322,7 +324,7 @@ present" (repl/repl* renv (assoc (dissoc-entry-point-opts opts) ::repl/fast-initial-prompt? - (or (fast-initial-prompt? repl-env inits) + (or (fast-initial-prompt? repl-env options inits) (::repl/fast-initial-prompt? (repl/repl-options renv))) :quit-prompt From 0daf21ea5d70e3a2b36a6d5504649f61b2a3f95e Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 13:05:27 -0400 Subject: [PATCH 3566/4033] and cljs.closure/bundle? helper, :target :bundle is just sugar for :target :nodejs and :nodejs-rt false --- src/main/clojure/cljs/closure.clj | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 76ef74f33..64a349899 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1669,6 +1669,10 @@ (if (= :browser mode) "');\n" "\n")))] (map preload-str syms)))) +(defn bundle? [opts] + (and (= :nodejs (:target opts)) + (false? (:nodejs-rt opts)))) + (defn output-main-file "Output an entry point. In the non-modules case, opts is simply compiler options. When emitting a module entry point, opts must contain :module-name." @@ -1726,12 +1730,18 @@ [main]))))) (str (when (or (not module) (= :cljs-base (:module-name opts))) - (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + (str (when (bundle? opts) + "import {npmDeps} from \"./deps-rt.js\";") + "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "var CLOSURE_NO_DEPS = true;\n" "if(typeof goog == \"undefined\") document.write('');\n" "document.write('');\n" "document.write('');\n" "document.write('');\n" + (when (bundle? opts) + "window.require = function(lib) {\n" + " return npmDeps[lib];\n" + "}") (apply str (preloads (:preloads opts) :browser)))) (apply str (map (fn [entry] @@ -2471,6 +2481,13 @@ (when (nil? (:nodejs-rt opts)) {:nodejs-rt true})) + ;; :bundle is just sugar for + ;; :target :nodejs + :nodejs-rt false + (= :bundle (:target opts)) + (merge + {:target :nodejs + :nodejs-rt false}) + (= optimizations :none) (assoc :cache-analysis (:cache-analysis opts true) From 4a753fc0651255a1cd1ccdc7245082541785a3f4 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 13:12:35 -0400 Subject: [PATCH 3567/4033] helpers to generate file for runtime require of bundle modules --- src/main/clojure/cljs/closure.clj | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 64a349899..495a7e213 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1673,6 +1673,19 @@ (and (= :nodejs (:target opts)) (false? (:nodejs-rt opts)))) +(defn export-dep [dep] + (str "\""dep "\": require('" dep "')" )) + +(defn deps-rt-js + "Returns the JavaScript code to support runtime require of bundled modules." + [node-requires] + (str + "module.exports = {\n" + " npmDeps: {\n" + (string/join ",\n" (map (comp #(str " " %) export-dep) node-requires)) + " }\n" + "};\n")) + (defn output-main-file "Output an entry point. In the non-modules case, opts is simply compiler options. When emitting a module entry point, opts must contain :module-name." From 838fb481bc5f3d82be32466b57d98e3e80220279 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 13:18:18 -0400 Subject: [PATCH 3568/4033] generate deps_rt.js under :none when bundle? true --- src/main/clojure/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 495a7e213..f9abff65f 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1744,7 +1744,7 @@ (str (when (or (not module) (= :cljs-base (:module-name opts))) (str (when (bundle? opts) - "import {npmDeps} from \"./deps-rt.js\";") + "import {npmDeps} from \"./deps_rt.js\";") "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "var CLOSURE_NO_DEPS = true;\n" "if(typeof goog == \"undefined\") document.write('');\n" @@ -2188,6 +2188,9 @@ (when (:debug-inputs opts) (util/debug-prn "DEBUG: all compiler inputs") (util/debug-prn (pr-str sources))) + (when (bundle? opts) + (spit (io/file (util/output-directory opts) "deps_rt.js") + (deps-rt-js (keys (get @env/*compiler* :node-module-index))))) (cond modules (let [modules' (module-graph/expand-modules modules sources)] From 4332f949bf4c63f846cb030df7b2804c85cc464e Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 13:24:54 -0400 Subject: [PATCH 3569/4033] naming, deps-rt -> npm-deps --- src/main/clojure/cljs/closure.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f9abff65f..898955e20 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1676,7 +1676,7 @@ (defn export-dep [dep] (str "\""dep "\": require('" dep "')" )) -(defn deps-rt-js +(defn npm-deps-js "Returns the JavaScript code to support runtime require of bundled modules." [node-requires] (str @@ -1744,7 +1744,7 @@ (str (when (or (not module) (= :cljs-base (:module-name opts))) (str (when (bundle? opts) - "import {npmDeps} from \"./deps_rt.js\";") + "import {npmDeps} from \"./npm_deps.js\";") "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" "var CLOSURE_NO_DEPS = true;\n" "if(typeof goog == \"undefined\") document.write('');\n" @@ -2189,8 +2189,8 @@ (util/debug-prn "DEBUG: all compiler inputs") (util/debug-prn (pr-str sources))) (when (bundle? opts) - (spit (io/file (util/output-directory opts) "deps_rt.js") - (deps-rt-js (keys (get @env/*compiler* :node-module-index))))) + (spit (io/file (util/output-directory opts) "npm_deps.js") + (npm-deps-js (keys (get @env/*compiler* :node-module-index))))) (cond modules (let [modules' (module-graph/expand-modules modules sources)] From a5716a3573a0171e7afd357535521a759a71dad8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 13:29:33 -0400 Subject: [PATCH 3570/4033] :node-module-index is a set --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 898955e20..553c008a8 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2190,7 +2190,7 @@ (util/debug-prn (pr-str sources))) (when (bundle? opts) (spit (io/file (util/output-directory opts) "npm_deps.js") - (npm-deps-js (keys (get @env/*compiler* :node-module-index))))) + (npm-deps-js (:node-module-index @env/*compiler*)))) (cond modules (let [modules' (module-graph/expand-modules modules sources)] From f443151de9f3fd90a210d46fbc4c0a305b4bf411 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 13:37:01 -0400 Subject: [PATCH 3571/4033] :bundle sugar needs to also add :npm-deps true and :infer-externs true --- src/main/clojure/cljs/closure.clj | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 553c008a8..c8ae85f67 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2500,9 +2500,12 @@ ;; :bundle is just sugar for ;; :target :nodejs + :nodejs-rt false (= :bundle (:target opts)) - (merge - {:target :nodejs - :nodejs-rt false}) + (->> + (merge + {:infer-externs true + :nodejs-rt false + :npm-deps true + :target :nodejs})) (= optimizations :none) (assoc From 609bfef16f5abc2f879a4e9d9df8d9013e120e53 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 13:50:17 -0400 Subject: [PATCH 3572/4033] there's no need to support any finer grained manipulation of :bundle target options --- src/main/clojure/cljs/closure.clj | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c8ae85f67..8be415b5b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2497,15 +2497,13 @@ (when (nil? (:nodejs-rt opts)) {:nodejs-rt true})) - ;; :bundle is just sugar for - ;; :target :nodejs + :nodejs-rt false + ;; :bundle is just sugar (= :bundle (:target opts)) - (->> - (merge - {:infer-externs true - :nodejs-rt false - :npm-deps true - :target :nodejs})) + (merge + {:infer-externs true + :nodejs-rt false + :npm-deps true + :target :nodejs}) (= optimizations :none) (assoc From 33935448ce565e8093a7519fb24704e916bbd924 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 13:55:28 -0400 Subject: [PATCH 3573/4033] dissoc :target if we're actually dealing with bundle? true --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8be415b5b..9330c7f24 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1703,7 +1703,7 @@ (let [asset-path (or (:asset-path opts) (util/output-directory opts)) closure-defines (json/write-str (:closure-defines opts))] - (case (:target opts) + (case (:target (cond-> opts (bundle? opts) (dissoc :target))) :nodejs (add-header opts (str (when (or (not module) (= :cljs-base (:module-name opts))) From 955fdbdf34acfe15923ba462c7aa4918289c4114 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 14:04:12 -0400 Subject: [PATCH 3574/4033] switch bundle? to return true if we see :nodejs-rt false only, :nodejs-rt option is meaningless by itself anyway tweak main file gen --- src/main/clojure/cljs/closure.clj | 35 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 9330c7f24..3bf1f2d11 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1670,8 +1670,7 @@ (map preload-str syms)))) (defn bundle? [opts] - (and (= :nodejs (:target opts)) - (false? (:nodejs-rt opts)))) + (false? (:nodejs-rt opts))) (defn export-dep [dep] (str "\""dep "\": require('" dep "')" )) @@ -1742,20 +1741,18 @@ (when-let [main (:main opts)] [main]))))) - (str (when (or (not module) (= :cljs-base (:module-name opts))) - (str (when (bundle? opts) - "import {npmDeps} from \"./npm_deps.js\";") - "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - "var CLOSURE_NO_DEPS = true;\n" - "if(typeof goog == \"undefined\") document.write('');\n" - "document.write('');\n" - "document.write('');\n" - "document.write('');\n" - (when (bundle? opts) - "window.require = function(lib) {\n" - " return npmDeps[lib];\n" - "}") - (apply str (preloads (:preloads opts) :browser)))) + (str + (when (bundle? opts) + "import {npmDeps} from \"./npm_deps.js\";\n") + (when (or (not module) (= :cljs-base (:module-name opts))) + (str + "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "var CLOSURE_NO_DEPS = true;\n" + "if(typeof goog == \"undefined\") document.write('');\n" + "document.write('');\n" + "document.write('');\n" + "document.write('');\n" + (apply str (preloads (:preloads opts) :browser)))) (apply str (map (fn [entry] (when-not (= "goog" entry) @@ -1763,7 +1760,11 @@ (if-let [entries (when module (:entries module))] entries (when-let [main (:main opts)] - [main]))))))))))) + [main])))) + (when (bundle? opts) + "window.require = function(lib) {\n" + " return npmDeps[lib];\n" + "}\n")))))))) (defn fingerprinted-modules [modules fingerprint-info] (into {} From 9034817750fb1ce864f6b95bc865348d32223787 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 14:08:40 -0400 Subject: [PATCH 3575/4033] typo --- src/main/clojure/cljs/closure.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3bf1f2d11..8f1f4b84e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1762,9 +1762,10 @@ (when-let [main (:main opts)] [main])))) (when (bundle? opts) - "window.require = function(lib) {\n" - " return npmDeps[lib];\n" - "}\n")))))))) + (str + "window.require = function(lib) {\n" + " return npmDeps[lib];\n" + "}\n"))))))))) (defn fingerprinted-modules [modules fingerprint-info] (into {} From 309304a475f025b65a95e85a668220105679bc0c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 15:04:23 -0400 Subject: [PATCH 3576/4033] hash bang false --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8f1f4b84e..5afd134fe 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2502,7 +2502,8 @@ ;; :bundle is just sugar (= :bundle (:target opts)) (merge - {:infer-externs true + {:hashbang false + :infer-externs true :nodejs-rt false :npm-deps true :target :nodejs}) From 66f6bc53b6a80a6cc6bb5049934f91fbd5148e8f Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 19:09:02 -0400 Subject: [PATCH 3577/4033] add :bundle-cmd --- src/main/clojure/cljs/closure.clj | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 5afd134fe..be18fe018 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -16,6 +16,7 @@ [cljs.env :as env] [cljs.js-deps :as deps] [clojure.java.io :as io] + [clojure.java.shell :as sh] [clojure.reflect] [clojure.set :as set] [clojure.string :as string] @@ -205,7 +206,7 @@ :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}) + :nodejs-rt :target-fn :deps-cmd :bundle-cmd}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -3169,6 +3170,32 @@ (output-one-file opts))))) (apply output-unoptimized opts js-sources))] (output-bootstrap opts) + (when (bundle? opts) + (when-let [cmd (and (= :none optim) + (get-in opts [:bundle-cmd :none]))] + (let [{:keys [exit out]} + (try + (apply sh/sh cmd) + (catch Throwable t + (throw + (ex-info ":build-cmd :none failed" + {:cmd cmd} t))))] + (when-not (== 0 exit) + (throw + (ex-info ":bundle-cmd :none failed" + {:cmd cmd :exit-code exit :std-out out}))))) + (when-let [cmd (and (not= :none optim) + (get-in opts [:bundle-cmd :default]))] + (let [{:keys [exit out]} + (try + (apply sh/sh cmd) + (catch Throwable t + (ex-info ":build-cmd :default failed" + {:cmd cmd} t)))] + (when-not (== 0 exit) + (throw + (ex-info ":bundle-cmd :default failed" + {:cmd cmd :exit-code exit :std-out out})))))) ret)))))) (comment From 95386f5e80deabf7abe4b0e3357ecbb3d35e49f9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 19:39:22 -0400 Subject: [PATCH 3578/4033] mark transpiled files so we can switch between :none & :advanced w/o issue. looking at the bigger picture we should look at what it would take to repurpose the :aot-cache logic for a local cache --- src/main/clojure/cljs/closure.clj | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index be18fe018..fa1c9033c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2065,8 +2065,13 @@ result (.compile cc (url->nio-path rsc) source)] (.source result)) source)] - (cond-> source' - (= :goog module) add-goog-load))) + (str "/*TRANSPILED*/" + (cond-> source' + (= :goog module) add-goog-load)))) + +(defn requires-transpile? [out-file] + (let [line (first (line-seq (io/reader out-file)))] + (not (string/starts-with? line "/*TRANSPILED*/")))) (comment (println (slurp (io/resource "goog/math/long.js"))) @@ -2107,7 +2112,9 @@ (and res (util/changed? out-file res)) ;; always re-emit GCL libs under optimizations higher than :none ;; :none will just use the cached transpiled result - (and transpile? (not= :none optimizations)))) + (and transpile? + (or (not= :none optimizations) + (requires-transpile? out-file))))) (when (and res (or ana/*verbose* (:verbose opts))) (util/debug-prn "Copying" (str res) "to" (str out-file))) (util/mkdirs out-file) From 7af2ff6ae545b9feee6d800efe2868f122ab036a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 21:07:34 -0400 Subject: [PATCH 3579/4033] add note about browser REPL leveraging behavior from cljs.cli --- src/main/clojure/cljs/repl/browser.clj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 9da106cb7..847f01910 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -177,6 +177,10 @@ (defn send-static [{path :path :as request} conn + ;; NOTE: :output-to and :output-dir are available here because cljs.cli + ;; does this - should maybe rethink that, we need a better way to supply + ;; the main JS file to load because it might not be compiler options :output-to + ;; i.e. bundler step {:keys [static-dir output-to output-dir host port gzip?] :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)) From 9e0a84e86373c7cdb0f40b7305319bf5ba5c86a0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 11 Apr 2020 21:33:31 -0400 Subject: [PATCH 3580/4033] remove copying over :output-to in cljs.cli for browser repl, this kind of customization is just not necessary, user can provide their own index page if they're going to change :output-to away from :output-dir/main.js --- src/main/clojure/cljs/cli.clj | 2 +- src/main/clojure/cljs/repl/browser.clj | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 9013f3433..459ef6987 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -317,7 +317,7 @@ present" (assoc :output-dir (temp-out-dir) :temp-output-dir? true) (not (contains? options :aot-cache)) (assoc :aot-cache true)) - reopts (merge repl-env-options (select-keys opts [:output-to :output-dir])) + reopts (merge repl-env-options (select-keys opts [:output-dir])) _ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "REPL env options:" (pr-str reopts))) renv (apply repl-env (mapcat identity reopts))] diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 847f01910..67b7a9f12 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -177,11 +177,7 @@ (defn send-static [{path :path :as request} conn - ;; NOTE: :output-to and :output-dir are available here because cljs.cli - ;; does this - should maybe rethink that, we need a better way to supply - ;; the main JS file to load because it might not be compiler options :output-to - ;; i.e. bundler step - {:keys [static-dir output-to output-dir host port gzip?] :or {output-dir "out"} :as opts}] + {:keys [static-dir output-dir host port gzip?] :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) @@ -208,11 +204,14 @@ encoding (mime-type->encoding mime-type "UTF-8")] (server/send-and-close conn 200 (slurp local-path :encoding encoding) mime-type encoding (and gzip? (= "text/javascript" mime-type)))) + ;; "/index.html" doesn't exist, provide our own (= path "/index.html") (server/send-and-close conn 200 - (default-index (or output-to (str output-dir "/main.js"))) + (default-index (str output-dir "/main.js")) "text/html" "UTF-8") + + ;; "/main.js" doesn't exist, provide our own (= path (cond->> "/main.js" output-dir (str "/" output-dir ))) (let [closure-defines (-> `{"goog.json.USE_NATIVE_JSON" true clojure.browser.repl/HOST ~host @@ -230,6 +229,7 @@ "document.write('');\n" "document.write('');\n") "text/javascript" "UTF-8")) + :else (server/send-404 conn path))) (server/send-404 conn path)))) From 7792adf10e8961cbd5b0267a30a7d9655c770086 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 12 Apr 2020 14:12:01 -0400 Subject: [PATCH 3581/4033] prep CHANGES.md --- changes.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/changes.md b/changes.md index 0d686c26a..44ae12c20 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,52 @@ +## Next + +### Changes +* Removed REPL/target support for Rhino, Nashorn, Graaljs +* Update Closure Compiler and Google Closure Compiler dependencies +* CLJS-1628: Make instances of js/Symbol printable + +### Enhancements +* Add :target :bundle for integration with JavaScript bunders (webpack, metro, etc.) +* Add cljs.main --install-deps flag +* Add :bundle-cmd compiler option for triggering JS bundler at end of build +* Add :nodejs-rt compiler option to diable Node.js runtime support (for bundles) +* Add :target-fn compiler option to allow users to support other targets outside of ClojureScript +* Add :npm-cmd to allow users to choose `npm` or `yarn` for their dep tool +* Make fast REPL prompt availble to 3rd party REPLs +* Transpile GCL libraries that need it +* Enhance cljs.main to be properly extensible +* repl-env providing namespaces can now be arbitrary not limited to cljs.repl.* +* CLJS-3185: Facilitate protocol static dispatch inlining +* CLJS-3199: Interop with JavaScript's iterable objects via Iterator protocol + +### Fixes +* CLJS-2908: Don't show quick prompt if :verbose or :repl-verbose +* CLJS-2898: cljs.main: ClojureScript version should not be displayed if there are inits +* CLJS-2863: Need to reject incorrect fn with fixed arity of more params than variadic +* CLJS-3086: Switch clj-nil to not be considered a numeric type +* CLJS-3211: Widen cljs.core/defprotocol sig arguments from list to seq (to include Cons) +* CLJS-712 & CLJS-2957: resolve-var for dot still wrong, externs inference regression +* CLJS-2862: Externs inference warning when extending Object +* CLJS-3161: Include :property for :warning-type :target +* CLJS-3181: Externs inference fails, even when manual type hint is provided +* CLJS-3224: Fix cljs.loader due to usage of removed setModuleUris API +* CLJS-3223: get-js-index 0-arity should call get-js-index 1-arity +* CLJS-3220: Self-host test-import failing on asserts.assert +* CLJS-3218: NPE during Closure transpilation in self-host tests +* CLJS-3217: script/test-self-parity compilation failure stale reference to goog.text.LoremIpsum +* CLJS-3215: Travis has remnant CLI test involving GraalJS +* CLJS-3219: Problem with asserts namespace inside goog.math.Long +* CLJS-3119: get with negative ndx on string inconsistent with Clojure +* CLJS-3214: script/uberjar fails with GraalJS +* CLJS-3210: Single arity arithmetic ops don't warn on bad arguments +* CLJS-3213: Browser REPL server hang when receive unknown POST +* CLJS-3209: With clojurescript 1.10.597 HelloWorld compiled with to 94K of JS with advanced optimizations turned on +* CLJS-3191: Optimize cljs.core/re-find +* CLJS-3192: Optimize cljs.core/re-matches +* CLJS-3193: Optimize cljs.core/re-pattern +* CLJS-3202: Make :/ keyword hash consistent with = +* CLJS-3203: Overriding root path in s/cat using s/gen gives an error + ## 1.10.597 ### Changes From 5d15026429ccb97308d09d70bc2b6d7676569f4e Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 12 Apr 2020 20:23:18 -0400 Subject: [PATCH 3582/4033] fast initial prompt for browser REPL --- src/main/clojure/cljs/repl/browser.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 67b7a9f12..9d5bf664b 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -414,6 +414,7 @@ {:browser-repl true :repl-requires '[[clojure.browser.repl] [clojure.browser.repl.preload]] + ::repl/fast-initial-prompt? :after-setup :cljs.cli/commands {:groups {::repl {:desc "browser REPL options"}} :init From c1cf559a74760436babe016b9bae55b70b5c8495 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 12 Apr 2020 20:46:28 -0400 Subject: [PATCH 3583/4033] how we handled externs in compiler state was just not very functional one problem is that we always *compute* extern information when analyzing files, not having externs in compiler state just means that extern information is wrong. another problem is that :infer-externs may be discovered *after* the compiler evironment is built, meaning we end up back at problem one. just memoize it, make it easier to change our mind later, fix tests --- src/main/clojure/cljs/analyzer.cljc | 7 +++++-- src/main/clojure/cljs/compiler.cljc | 11 +++++------ src/main/clojure/cljs/env.cljc | 11 +++++------ src/main/clojure/cljs/externs.clj | 8 +++++--- src/test/clojure/cljs/analyzer_tests.clj | 4 ++-- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 7b8e6fc9c..0532f510a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -168,6 +168,9 @@ (defn compiler-options [] (get @env/*compiler* :options)) +(defn get-externs [] + (::externs @env/*compiler*)) + (defn checked-arrays "Returns false-y, :warn, or :error based on configuration and the current value of *unchecked-arrays*." @@ -1005,7 +1008,7 @@ (defn has-extern? ([pre] - (has-extern? pre (get @env/*compiler* ::externs))) + (has-extern? pre (get-externs))) ([pre externs] (or (has-extern?* pre externs) (when (= 1 (count pre)) @@ -1018,7 +1021,7 @@ ([pre] (js-tag pre :tag)) ([pre tag-type] - (js-tag pre tag-type (get @env/*compiler* ::externs))) + (js-tag pre tag-type (get-externs))) ([pre tag-type externs] (js-tag pre tag-type externs externs)) ([pre tag-type externs top] diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index f95051629..f687c46a7 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -201,7 +201,7 @@ (sorted-map)))))))))) (emit* ast)) -(defn emits +(defn emits ([]) ([^Object a] (cond @@ -587,12 +587,12 @@ (defn emit-js-array [items comma-sep] (emits "[" (comma-sep items) "]")) -(defmethod emit* :js-object +(defmethod emit* :js-object [{:keys [keys vals env]}] (emit-wrap env (emit-js-object (map vector keys vals) identity))) -(defmethod emit* :js-array +(defmethod emit* :js-array [{:keys [items env]}] (emit-wrap env (emit-js-array items comma-sep))) @@ -1133,7 +1133,7 @@ protocol (:protocol info) tag (ana/infer-tag env (first (:args expr))) proto? (and protocol tag - (or (and ana/*cljs-static-fns* protocol (= tag 'not-native)) + (or (and ana/*cljs-static-fns* protocol (= tag 'not-native)) (and (or ana/*cljs-static-fns* (:protocol-inline env)) @@ -1817,8 +1817,7 @@ (defn emit-externs ([externs] (emit-externs [] externs (atom #{}) - (when env/*compiler* - (::ana/externs @env/*compiler*)))) + (when env/*compiler* (ana/get-externs)))) ([prefix externs top-level known-externs] (loop [ks (seq (keys externs))] (when ks diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index f91704993..65eed3715 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -45,13 +45,12 @@ state that is accessed/maintained by many different components."} (defn default-compiler-env* [options] (merge - {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} + {:cljs.analyzer/namespaces {'cljs.user {:name 'cljs.user}} :cljs.analyzer/constant-table {} - :cljs.analyzer/data-readers {} - :cljs.analyzer/externs #?(:clj (when (:infer-externs options) - (externs/externs-map (:externs-sources options))) - :cljs nil) - :options options} + :cljs.analyzer/data-readers {} + :cljs.analyzer/externs #?(:clj (externs/externs-map (:externs-sources options)) + :cljs nil) + :options options} #?@(:clj [(when (= (:target options) :nodejs) {:node-module-index deps/native-node-modules}) {:js-dependency-index (deps/js-dependency-index options)}]))) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index c2f73954b..417fd3403 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -182,11 +182,11 @@ (seq xs) (update-in xs merge {}))) {} externs)) -(defn externs-map +(defn externs-map* ([] - (externs-map (CommandLineRunner/getDefaultExterns))) + (externs-map* (CommandLineRunner/getDefaultExterns))) ([sources] - (externs-map sources + (externs-map* sources '{eval {} global {} goog {nodeGlobalRequire {}} @@ -204,6 +204,8 @@ externs (index-externs (parse-externs externs-file)))) defaults sources)))) +(def externs-map (memoize externs-map*)) + (defn ns-match? [ns-segs var-segs] (or ;; exact match (i.e. ctors) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 2bec4eb60..54ac85431 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -179,9 +179,9 @@ (.getMessage (.getCause e)))) "Only one "))) -(def test-cenv (atom {})) +(def test-cenv (env/default-compiler-env)) (def test-env (assoc-in (ana/empty-env) [:ns :name] 'cljs.core)) -(def test-core-env (atom {})) +(def test-core-env (env/default-compiler-env)) (binding [ana/*unchecked-if* false ana/*analyze-deps* false] From f83b1f766c32b60a3e2660170e0faf15c6e2a34e Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 12 Apr 2020 23:09:15 -0400 Subject: [PATCH 3584/4033] gracefully handle repl client page server requests in server only mode --- src/main/clojure/cljs/repl/browser.clj | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 9d5bf664b..50cb4c76c 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -116,17 +116,20 @@ (slurp (:client-js @browser-state))) (defn send-repl-client-page - [request conn opts] - (server/send-and-close conn 200 - (str " - " - "" + "" - "") - "text/html")) + "") + "text/html"))) (defn default-index [output-to] (str From e58cabf7af731ca683189fcfc1cb6f4ad97525c5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 13 Apr 2020 07:08:17 -0400 Subject: [PATCH 3585/4033] CLJS-3227: :bundle target breaks ^:export --- src/main/cljs/cljs/core.cljs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 205dcd1b1..67129347a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11738,5 +11738,9 @@ reduces them without incurring seq initialization" (*eval* form)) (when ^boolean js/COMPILED - (when (identical? "nodejs" *target*) + (cond + (identical? "bundle" *target*) + (set! goog/global js/window) + + (identical? "nodejs" *target*) (set! goog/global js/global))) From b58b5ffc957f0b4dd5a7bef87adc4d23613ce03f Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 13 Apr 2020 07:55:03 -0400 Subject: [PATCH 3586/4033] revert last commit --- src/main/cljs/cljs/core.cljs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 67129347a..205dcd1b1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11738,9 +11738,5 @@ reduces them without incurring seq initialization" (*eval* form)) (when ^boolean js/COMPILED - (cond - (identical? "bundle" *target*) - (set! goog/global js/window) - - (identical? "nodejs" *target*) + (when (identical? "nodejs" *target*) (set! goog/global js/global))) From f95a13b3e3b193749e10e15d95ef98506ce3be29 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 13 Apr 2020 08:44:08 -0400 Subject: [PATCH 3587/4033] CLJS-3227: :bundle target breaks ^:export add a new *global* goog-define, only support the obvious cases --- src/main/cljs/cljs/core.cljs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 205dcd1b1..dc25f0327 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -47,6 +47,12 @@ :closure-defines option."} *target* "default") +(goog-define + ^{:dynamic true + :doc "Manually set the JavaScript global context. Only \"window\", \"self\" + , and \"global\" supported. "} + *global* "default") + (def ^{:dynamic true :doc "Var bound to the current namespace. Only used for bootstrapping." @@ -11739,4 +11745,8 @@ reduces them without incurring seq initialization" (when ^boolean js/COMPILED (when (identical? "nodejs" *target*) - (set! goog/global js/global))) + (set! goog/global js/global)) + (cond + (identical? "window" *global*) (set! goog/global js/window) + (identical? "self" *global*) (set! goog/global js/self) + (identical? "global" *global*) (set! goog/global js/global))) From d30fa77e49949430dd82826ff29736411fb8db20 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 13 Apr 2020 10:55:31 -0400 Subject: [PATCH 3588/4033] don't add Node native modules if we're not targeting Node.js runtime --- src/main/clojure/cljs/env.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/env.cljc b/src/main/clojure/cljs/env.cljc index 65eed3715..3aecc4c85 100644 --- a/src/main/clojure/cljs/env.cljc +++ b/src/main/clojure/cljs/env.cljc @@ -51,7 +51,8 @@ state that is accessed/maintained by many different components."} :cljs.analyzer/externs #?(:clj (externs/externs-map (:externs-sources options)) :cljs nil) :options options} - #?@(:clj [(when (= (:target options) :nodejs) + #?@(:clj [(when (and (= :nodejs (:target options)) + (not (false? (:nodejs-rt options)))) {:node-module-index deps/native-node-modules}) {:js-dependency-index (deps/js-dependency-index options)}]))) From 2248bafbb18bfd42e24f9dab4fbba07e3c871935 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 13 Apr 2020 11:05:11 -0400 Subject: [PATCH 3589/4033] decomplect the npm_deps.js generation from output-unoptimized --- src/main/clojure/cljs/closure.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index fa1c9033c..e86953d73 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2198,9 +2198,6 @@ (when (:debug-inputs opts) (util/debug-prn "DEBUG: all compiler inputs") (util/debug-prn (pr-str sources))) - (when (bundle? opts) - (spit (io/file (util/output-directory opts) "npm_deps.js") - (npm-deps-js (:node-module-index @env/*compiler*)))) (cond modules (let [modules' (module-graph/expand-modules modules sources)] @@ -3175,7 +3172,11 @@ (str fdeps-str) (add-header opts) (output-one-file opts))))) - (apply output-unoptimized opts js-sources))] + (do + (when (bundle? opts) + (spit (io/file (util/output-directory opts) "npm_deps.js") + (npm-deps-js (:node-module-index @env/*compiler*)))) + (apply output-unoptimized opts js-sources)))] (output-bootstrap opts) (when (bundle? opts) (when-let [cmd (and (= :none optim) From 5f2d0a09856780308805a93ed902be190b03cadc Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 13 Apr 2020 14:19:28 -0400 Subject: [PATCH 3590/4033] add public cljs.compiler.api/munge --- src/main/clojure/cljs/compiler/api.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index 43c79887f..b37935520 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -17,6 +17,11 @@ ;; ============================================================================= ;; Main API +(defn munge + "Munge a symbol or string. Preserves the original type." + [s] + (comp/munge s)) + (defn emit "Given an AST node generated by the analyzer emit JavaScript as a string." ([ast] From c193a4b777b8516d46315da37e8c4aa79d8a8a60 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 13 Apr 2020 14:42:48 -0400 Subject: [PATCH 3591/4033] add missing munge exclude --- src/main/clojure/cljs/compiler/api.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index b37935520..a197e0a9b 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -9,6 +9,7 @@ (ns cljs.compiler.api "This is intended to be a stable api for those who need programmatic access to the compiler." + (:refer-clojure :exclude [munge]) (:require [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] [cljs.compiler :as comp] From 8db5b7689b6a1d4ba1a0270db71a8cdb31381c5e Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 13 Apr 2020 16:10:26 -0400 Subject: [PATCH 3592/4033] add public cljs.build.api/handle-js-sources & public cljs.build.api/dependency-order --- src/main/clojure/cljs/build/api.clj | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 97179a9d5..2ae9657fc 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -17,7 +17,8 @@ [cljs.util :as util] [cljs.analyzer :as ana] [cljs.analyzer.api :as ana-api] - [cljs.closure :as closure]) + [cljs.closure :as closure] + [cljs.js-deps :as deps]) (:import [java.io File])) ;; ============================================================================= @@ -150,6 +151,18 @@ [opts & ijss] (closure/add-dependencies opts ijss)) +(defn handle-js-modules + "Given a collection of IJavaScript values representing a build, index all + node modules, convert all JS modules (ES6 etc), and store the updated + js-dependency-index (likely changed due to modules) in compiler state." + [state xs opts] + (closure/handle-js-modules opts xs state)) + +(defn dependency-order + "Topologically sort a collection of IJavaScript values." + [xs] + (deps/dependency-order xs)) + (defn add-implicit-options "Given a valid map of build options add any standard implicit options. For example :optimizations :none implies :cache-analysis true and :source-map From 1c2094cfa775746448260c76f332ceaa5c4cedaa Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Apr 2020 13:09:55 -0400 Subject: [PATCH 3593/4033] add public with-warning-handlers --- src/main/clojure/cljs/analyzer/api.cljc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 30043e6dc..d346f8f57 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -61,6 +61,16 @@ [warning-type env extra] (ana/default-warning-handler warning-type env extra)) +(defmacro with-warning-handlers + "Helper macro for custom handling of emitted warnings. Handlers should be + a vector of functions. The signature of these functions is + [warn-type env warn-info]. warn-type is a keyword describing the warning, + env is the analysis environment, and warn-info is a map of extra useful + information for a particular warning type." + [handlers & body] + `(ana/with-warning-handlers ~handlers + ~@body)) + (defn get-options "Return the compiler options from compiler state." ([] (get-options (current-state))) From e66131422cbd67c4a594e194f09b49f2c3ae3d2e Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 14 Apr 2020 13:41:03 -0400 Subject: [PATCH 3594/4033] add cljs.analyzer.api/warning-message --- src/main/clojure/cljs/analyzer/api.cljc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index d346f8f57..5f90b99fb 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -71,6 +71,12 @@ `(ana/with-warning-handlers ~handlers ~@body)) +(defn warning-message + "Helper for generating the standard analyzer messages for warnings. Should be + passed warn-type and warn-info. See with-warning-handlers." + [warn-type warn-info] + (ana/error-message warn-type warn-info)) + (defn get-options "Return the compiler options from compiler state." ([] (get-options (current-state))) From a00c8f3fa85c320929b2786c018b296bb8e5b7af Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Apr 2020 09:23:48 -0400 Subject: [PATCH 3595/4033] CLJS-3230: seq on empty Iterable produces an empty seq --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/seqs_test.cljs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index dc25f0327..12ada0da5 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1330,7 +1330,7 @@ [iter] (let [v (.next iter)] (if (.-done v) - () + nil (ES6IteratorSeq. (.-value v) iter nil)))) ;;;;;;;;;;;;;;;;;;; Murmur3 Helpers ;;;;;;;;;;;;;;;; diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 94ca0e20f..2ecc71449 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -224,3 +224,10 @@ (deftest test-cljs-2911 (testing "partition-by works correclty with infinite seqs" (is (= (first (second (partition-by zero? (range)))) 1)))) + +(deftest test-cljs-3230 + (testing "sequence ops on ES6 collections" + (let [s (js/Set.)] + (is (= () (rest s))) + (is (nil? (next s))) + (is (empty? s))))) From 2a2b472d8a43eb1f6c64f5a6001d7e3848cd6c1d Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Apr 2020 09:49:55 -0400 Subject: [PATCH 3596/4033] always print the REPL version, this is used by some tools like Cursive to detect the REPL type --- src/main/clojure/cljs/repl.cljc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index f07a68723..07632974d 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -804,8 +804,7 @@ (ana/analyze-file (str "file://" (.getAbsolutePath file)) opts))))) (defn repl-title [] - (when-not (util/synthetic-version?) - (println "ClojureScript" (util/clojurescript-version)))) + (println "ClojureScript" (util/clojurescript-version))) (defn repl-quit-prompt [] (println "To quit, type:" :cljs/quit)) From 16395d2a2fcab47c6007e09e65e0b4e7f0bb974a Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Apr 2020 13:07:57 -0400 Subject: [PATCH 3597/4033] lift passes into default-passes var, expose in cljs.analyzer.api, add cljs.analyzer.api/with-passes helper --- src/main/clojure/cljs/analyzer.cljc | 7 +++++-- src/main/clojure/cljs/analyzer/api.cljc | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 0532f510a..a15e6f715 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4220,11 +4220,14 @@ (cond-> {:op :const :val form :env env :form form} tag (assoc :tag tag)))))) +(def default-passes + #?(:clj [infer-type check-invoke-arg-types ns-side-effects] + :cljs [infer-type check-invoke-arg-types])) + (defn analyze* [env form name opts] (let [passes *passes* passes (if (nil? passes) - #?(:clj [infer-type check-invoke-arg-types ns-side-effects] - :cljs [infer-type check-invoke-arg-types]) + default-passes passes) form (if (instance? LazySeq form) (if (seq form) form ()) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 5f90b99fb..e3c338036 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -90,6 +90,12 @@ ([state] (get @state :js-dependency-index))) +(def default-passes ana/default-passes) + +(defn with-passes [passes & body] + `(binding [ana/*passes* ~passes] + ~@body)) + #?(:clj (defn analyze "Given an environment, a map containing {:locals (mapping of names to bindings), :context From c3cbd8077a2161ecb5593dedee79ddcfcbcb6615 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 21 Apr 2020 13:25:30 -0400 Subject: [PATCH 3598/4033] defn -> defmacro --- src/main/clojure/cljs/analyzer/api.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index e3c338036..48464f954 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -92,7 +92,7 @@ (def default-passes ana/default-passes) -(defn with-passes [passes & body] +(defmacro with-passes [passes & body] `(binding [ana/*passes* ~passes] ~@body)) From 7ca05517af2fdf698e5f1f23ac38dc45bb5d51e9 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 22 Apr 2020 10:19:45 -0400 Subject: [PATCH 3599/4033] cljs.cli/default-compile: add a way to run something after the build before the repl or the server (which do not exit) --- src/main/clojure/cljs/cli.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 459ef6987..71b799584 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -484,7 +484,7 @@ present" (:main options))) (defn default-compile - [repl-env {:keys [ns args options] :as cfg}] + [repl-env {:keys [ns args options post-compile-fn] :as cfg}] (let [rfs #{"-r" "--repl"} sfs #{"-s" "--serve"} env-opts (repl/repl-options (repl-env)) @@ -528,6 +528,8 @@ present" (build/build source opts cenv) (build/watch path opts cenv)) (build/build source opts cenv)) + (when (fn? post-compile-fn) + (post-compile-fn)) (when repl? (repl-opt repl-env args cfg)) (when serve? From b79007367818f0d1567646f28f09e2de3450a99e Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 23 Apr 2020 21:47:44 -0400 Subject: [PATCH 3600/4033] cljs.analyzer.api/enabled-warnings --- src/main/clojure/cljs/analyzer/api.cljc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 48464f954..d18e30af1 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -77,6 +77,11 @@ [warn-type warn-info] (ana/error-message warn-type warn-info)) +(defn enabled-warnings + "Get the enabled warning types." + [] + ana/*cljs-warnings*) + (defn get-options "Return the compiler options from compiler state." ([] (get-options (current-state))) From ec32b73bf1ec288a19908159810ac9e2183a962c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Apr 2020 09:23:34 -0400 Subject: [PATCH 3601/4033] 1.10.738 --- README.md | 6 +++--- changes.md | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 068096db8..6ada687bb 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.597 +Latest stable release: 1.10.738 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.597"] +[org.clojure/clojurescript "1.10.738"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.597 org.clojure clojurescript - 1.10.597 + 1.10.738 ``` diff --git a/changes.md b/changes.md index 44ae12c20..f2f0ec4da 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## Next +## 1.10.738 ### Changes * Removed REPL/target support for Rhino, Nashorn, Graaljs @@ -20,6 +20,7 @@ * CLJS-3199: Interop with JavaScript's iterable objects via Iterator protocol ### Fixes +* CLJS-3230: seq on empty Iterable produces an empty seq * CLJS-2908: Don't show quick prompt if :verbose or :repl-verbose * CLJS-2898: cljs.main: ClojureScript version should not be displayed if there are inits * CLJS-2863: Need to reject incorrect fn with fixed arity of more params than variadic From 99d669ff78e89ca58259689adab0b8d1e61db580 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Apr 2020 09:45:25 -0400 Subject: [PATCH 3602/4033] 1.10.739 --- README.md | 6 +++--- changes.md | 2 +- src/main/clojure/cljs/analyzer.cljc | 1 + src/test/clojure/cljs/analyzer_tests.clj | 10 ---------- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6ada687bb..005102d02 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.738 +Latest stable release: 1.10.739 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.738"] +[org.clojure/clojurescript "1.10.739"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.738 org.clojure clojurescript - 1.10.738 + 1.10.739 ``` diff --git a/changes.md b/changes.md index f2f0ec4da..c2b54534d 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.10.738 +## 1.10.739 ### Changes * Removed REPL/target support for Rhino, Nashorn, Graaljs diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a15e6f715..540c40c84 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3502,6 +3502,7 @@ ;; warning without this - David (cond (nil? t) true + (= 'clj-nil t) true (js-tag? t) true ;; TODO: revisit :else (if (and (symbol? t) (some? (get NUMERIC_SET t))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 54ac85431..542f93656 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1367,16 +1367,6 @@ (env/with-compiler-env test-cenv (:tag (analyze test-env '(demunge-str ""))))))) -(deftest test-cljs-3086 - (let [ws (atom [])] - (try - (ana/with-warning-handlers [(collecting-warning-handler ws)] - (cljs.env/with-compiler-env test-cenv - (analyze (ana/empty-env) - '(+ nil 1)))) - (catch Exception _)) - (is (= ["cljs.core/+, all arguments must be numbers, got [clj-nil number] instead"] @ws)))) - (deftest test-cljs-3120 (let [cenv (core-env) _ (analyze-forms cenv From 20f3277b03e7acb0c0558ffd6053a023a6e1608a Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Apr 2020 12:05:03 -0400 Subject: [PATCH 3603/4033] 1.10.740 --- README.md | 6 +++--- changes.md | 2 +- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/seqs_test.cljs | 5 +++++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 005102d02..295d8a4fb 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.739 +Latest stable release: 1.10.740 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.739"] +[org.clojure/clojurescript "1.10.740"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.739 org.clojure clojurescript - 1.10.739 + 1.10.740 ``` diff --git a/changes.md b/changes.md index c2b54534d..0bceec7cb 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.10.739 +## 1.10.740 ### Changes * Removed REPL/target support for Rhino, Nashorn, Graaljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 12ada0da5..520603cbd 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1208,7 +1208,7 @@ (defn js-iterable? "Return true if x has a JavaScript iterator property" [x] - (gobject/containsKey x ITER_SYMBOL)) + (not (nil? (js* "~{}[~{}]" x ITER_SYMBOL)))) (defn clone "Clone the supplied value which must implement ICloneable." diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 2ecc71449..2fc52c905 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -231,3 +231,8 @@ (is (= () (rest s))) (is (nil? (next s))) (is (empty? s))))) + +(deftest test-js-iterable? + (testing "test that js-iterable? works on ES6 collections and normal values" + (is (true? (js-iterable? (js/Set.)))) + (is (false? (js-iterable? 1))))) From 799d62fe3bba999287558538ca6b61501de02d49 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 24 Apr 2020 12:20:41 -0400 Subject: [PATCH 3604/4033] 1.10.741 --- README.md | 6 +++--- changes.md | 2 +- src/main/cljs/cljs/core.cljs | 3 ++- src/test/cljs/cljs/seqs_test.cljs | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 295d8a4fb..086633f01 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.740 +Latest stable release: 1.10.741 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.740"] +[org.clojure/clojurescript "1.10.741"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.740 org.clojure clojurescript - 1.10.740 + 1.10.741 ``` diff --git a/changes.md b/changes.md index 0bceec7cb..184d53487 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.10.740 +## 1.10.741 ### Changes * Removed REPL/target support for Rhino, Nashorn, Graaljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 520603cbd..05b2f4e60 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1208,7 +1208,8 @@ (defn js-iterable? "Return true if x has a JavaScript iterator property" [x] - (not (nil? (js* "~{}[~{}]" x ITER_SYMBOL)))) + (and (not (nil? x)) + (not (nil? (js* "~{}[~{}]" x ITER_SYMBOL))))) (defn clone "Clone the supplied value which must implement ICloneable." diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 2fc52c905..d9e56fcaf 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -235,4 +235,5 @@ (deftest test-js-iterable? (testing "test that js-iterable? works on ES6 collections and normal values" (is (true? (js-iterable? (js/Set.)))) - (is (false? (js-iterable? 1))))) + (is (false? (js-iterable? 1))) + (is (false? (js-iterable? nil))))) From 6ea2974a92d80c236f7966a7aae41ed327046e60 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 25 Apr 2020 10:38:41 -0400 Subject: [PATCH 3605/4033] fix (require ... :reload) --- src/main/cljs/clojure/browser/repl.cljs | 2 +- src/main/clojure/cljs/repl/bootstrap.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 4f72283a5..a0eca7600 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -209,7 +209,7 @@ (let [reload? (or reload (.-cljsReloadAll__ js/goog))] (when reload? (if (some? goog/debugLoader_) - (let [path (.getPathFromDeps_ goog/debugLoader_ name)] + (let [path (.getPathFromDeps_ goog/debugLoader_ src)] (gobj/remove (.-written_ goog/debugLoader_) path) (gobj/remove (.-written_ goog/debugLoader_) (str js/goog.basePath path))) diff --git a/src/main/clojure/cljs/repl/bootstrap.clj b/src/main/clojure/cljs/repl/bootstrap.clj index f54b1d2a9..5728a2808 100644 --- a/src/main/clojure/cljs/repl/bootstrap.clj +++ b/src/main/clojure/cljs/repl/bootstrap.clj @@ -32,7 +32,7 @@ (when reload? ;; check for new-ish private goog/debugLoader (if (some? goog/debugLoader_) - (let [path (.getPathFromDeps_ goog/debugLoader_ name)] + (let [path (.getPathFromDeps_ goog/debugLoader_ src)] (goog.object/remove (.-written_ goog/debugLoader_) path) (goog.object/remove (.-written_ goog/debugLoader_) (str js/goog.basePath path))) From 017d0f98b53e3b2ef5e6e09b0427f4ff6ebe79bb Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 25 Apr 2020 12:03:00 -0400 Subject: [PATCH 3606/4033] add failing test --- src/test/clojure/cljs/externs_infer_tests.clj | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 20efe1667..6f2cdda3c 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -385,6 +385,19 @@ :with-core? true}))] (is (empty? @ws)))) +(deftest test-cljs-3236 + (let [ws (atom []) + res (binding [ana/*cljs-static-fns* true] + (infer-test-helper + {:forms '[(ns test.foo) + (set! *warn-on-infer* true) + (defprotocol IFoo + (bar [this]))] + :warnings ws + :warn true + :with-core? true}))] + (is (empty? @ws)))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From 6a6e4e30f14b38a20b383d38e89c1f2604803cf0 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 25 Apr 2020 12:55:36 -0400 Subject: [PATCH 3607/4033] CLJS-3236: defprotocol externs inference warnings in defprotocol macro mark protocol properties, ignore them --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- src/main/clojure/cljs/core.cljc | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 540c40c84..31afe38d4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3351,7 +3351,8 @@ ;; (. (...) -p) (defmethod build-dot-form [::expr ::property ()] [[target prop _]] - {:dot-action ::access :target target :field (-> prop name (.substring 1) symbol)}) + {:dot-action ::access :target target + :field (with-meta (-> prop name (.substring 1) symbol) (meta prop))}) ;; (. o -p ) (defmethod build-dot-form [::expr ::property ::list] @@ -3410,7 +3411,8 @@ update-in [:prefix] (fnil conj '[Object]) prop)) nil)] (when (and (not= 'constructor prop) - (not (string/starts-with? (str prop) "cljs$"))) + (not (string/starts-with? (str prop) "cljs$")) + (not (-> prop meta :protocol-method))) ;; Adding to Object (when (= 'Object (first (-> tag meta :prefix))) (warning :infer-warning env diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 51d30bf5f..feb7b506f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2109,7 +2109,8 @@ ;; check protocol property on object (first check executed) check `(if (and (not (nil? ~fsig)) - (not (nil? (. ~fsig ~(symbol (core/str "-" slot)))))) ;; Property access needed here. + ;; Property access needed here. + (not (nil? (. ~fsig ~(with-meta (symbol (core/str "-" slot)) {:protocol-method true}))))) (. ~fsig ~slot ~@sig) (~dyn-name ~@sig))] `(~sig ~check))) @@ -2160,7 +2161,8 @@ (defn ~fname ~@(map (core/fn [sig] (expand-sig dyn-name - (symbol (core/str slot "$arity$" (count sig))) + (with-meta (symbol (core/str slot "$arity$" (count sig))) + {:protocol-method true}) sig)) sigs)))))] `(do From 6c3276e3248c7e1b9fa3a6d00cffe77ad2860853 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 26 Apr 2020 10:31:20 -0400 Subject: [PATCH 3608/4033] CLJS-3237: compiling with --target node errors at runtime with document undefined The problem was uncovered by the change in default-compile to pull in all relevant compiler options from repl-options returned from the repl-env. The issue was that --target node would still use the browser-repl repl-env which sets :browser-repl true. Add a target->repl-env helper that looks at the target and returns the true repl-env in all cases. This also means you can start a node REPL now with `clj -m cljs.main -t node -r` Fixes the later half of the Quick Start --- src/main/clojure/cljs/cli.clj | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 71b799584..d358f71cf 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -308,6 +308,13 @@ is trying load some arbitrary ns." (not (:repl-verbose options)) (contains? #{"node"} (repl-name repl-env))))) +(defn target->repl-env [target default] + (if (= :nodejs target) + (do + (require 'cljs.repl.node) + (resolve 'cljs.repl.node/repl-env)) + default)) + (defn- repl-opt "Start a repl with args and inits. Print greeting if no eval options were present" @@ -320,7 +327,7 @@ present" reopts (merge repl-env-options (select-keys opts [:output-dir])) _ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "REPL env options:" (pr-str reopts))) - renv (apply repl-env (mapcat identity reopts))] + renv (apply (target->repl-env (:target options) repl-env) (mapcat identity reopts))] (repl/repl* renv (assoc (dissoc-entry-point-opts opts) ::repl/fast-initial-prompt? @@ -350,7 +357,7 @@ present" (select-keys opts [:output-to :output-dir])) _ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "REPL env options:" (pr-str reopts))) - renv (apply repl-env (mapcat identity reopts)) + renv (apply (target->repl-env (:target options) repl-env) (mapcat identity reopts)) coptsf (when-let [od (:output-dir opts)] (io/file od "cljsc_opts.edn")) copts (when (and coptsf (.exists coptsf)) @@ -487,7 +494,7 @@ present" [repl-env {:keys [ns args options post-compile-fn] :as cfg}] (let [rfs #{"-r" "--repl"} sfs #{"-s" "--serve"} - env-opts (repl/repl-options (repl-env)) + env-opts (repl/repl-options ((target->repl-env (:target options) repl-env))) repl? (boolean (or (rfs ns) (rfs (first args)))) serve? (boolean (or (sfs ns) (sfs (first args)))) main-ns (get-main-ns cfg) From 16e2ce41333bfa8a9440349aaa8fac636723e3c5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 26 Apr 2020 11:04:58 -0400 Subject: [PATCH 3609/4033] CLJS-3238: Watch incompatible with :bundle, throws :nodejs-related exception Need to stop looking at `:target` and instead consider `:nodejs-rt`. Clean up the bundle command runner. Deduplicate and include :stderr --- src/main/clojure/cljs/closure.clj | 48 ++++++++++++------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e86953d73..01a481985 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2346,10 +2346,10 @@ (assert (not (and output-wrapper (= :whitespace optimizations))) ":output-wrapper cannot be combined with :optimizations :whitespace")) -(defn check-node-target [{:keys [target optimizations] :as opts}] - (assert (not (and (= target :nodejs) (= optimizations :whitespace))) +(defn check-node-target [{:keys [nodejs-rt optimizations] :as opts}] + (assert (not (and nodejs-rt (= optimizations :whitespace))) (format ":nodejs target not compatible with :whitespace optimizations")) - (assert (not (and (= target :nodejs) (= optimizations :none) (not (contains? opts :main)))) + (assert (not (and nodejs-rt (= optimizations :none) (not (contains? opts :main)))) (format ":nodejs target with :none optimizations requires a :main entry"))) (defn check-main [{:keys [main] :as opts}] @@ -3020,6 +3020,21 @@ (check-main opts) opts) +(defn run-bundle-cmd [opts] + (let [cmd-type (or (#{:none} (:optimizations opts)) :default)] + (when-let [cmd (get-in opts [:bundle-cmd cmd-type])] + (let [{:keys [exit out err]} + (try + (apply sh/sh cmd) + (catch Throwable t + (throw + (ex-info (str ":build-cmd " cmd-type " failed") + {:cmd cmd} t))))] + (when-not (== 0 exit) + (throw + (ex-info (str ":bundle-cmd " cmd-type " failed") + {:cmd cmd :exit-code exit :stdout out :stderr err}))))))) + (defn build "Given compiler options, produce runnable JavaScript. An optional source parameter may be provided." @@ -3178,32 +3193,7 @@ (npm-deps-js (:node-module-index @env/*compiler*)))) (apply output-unoptimized opts js-sources)))] (output-bootstrap opts) - (when (bundle? opts) - (when-let [cmd (and (= :none optim) - (get-in opts [:bundle-cmd :none]))] - (let [{:keys [exit out]} - (try - (apply sh/sh cmd) - (catch Throwable t - (throw - (ex-info ":build-cmd :none failed" - {:cmd cmd} t))))] - (when-not (== 0 exit) - (throw - (ex-info ":bundle-cmd :none failed" - {:cmd cmd :exit-code exit :std-out out}))))) - (when-let [cmd (and (not= :none optim) - (get-in opts [:bundle-cmd :default]))] - (let [{:keys [exit out]} - (try - (apply sh/sh cmd) - (catch Throwable t - (ex-info ":build-cmd :default failed" - {:cmd cmd} t)))] - (when-not (== 0 exit) - (throw - (ex-info ":bundle-cmd :default failed" - {:cmd cmd :exit-code exit :std-out out})))))) + (when (bundle? opts) (run-bundle-cmd opts)) ret)))))) (comment From 7171db2ed335aed39a4ac5f6f6afdb3c10a17c12 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 26 Apr 2020 14:04:22 -0400 Subject: [PATCH 3610/4033] CLJS-3239: Infinite analysis with dotted symbols The analysis change for dotted symbols revealed a bug with import parsing. `(:import goog)` would result in `:imports {goog goog}` in the ns analysis map. Then when resolving a symbol like `goog.debug.Console`, the analyzer would go into an infinite loop. This loop was in the `:import` branch of resolve-var. This is because the expectation is that the value side of `:imports` map is *different*. But in this case, `{goog goog}`, it is not different, so the analyzer would get stuck. Notably, `(:import [goog])` doesn't have this problem. Make `(:import goog)` match `(:import [goog])` --- src/main/clojure/cljs/analyzer.cljc | 9 +++++++-- src/test/clojure/cljs/analyzer_tests.clj | 9 +++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 31afe38d4..ac66b9281 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2915,11 +2915,16 @@ (every? symbol? spec)) (and (symbol? spec) (nil? (namespace spec)))) (throw (error env (parse-ns-error-msg spec "Only lib.ns.Ctor or [lib.ns Ctor*] spec supported in :import")))) - (let [import-map (if (sequential? spec) + (let [import-map (cond + (sequential? spec) (->> (rest spec) (map #(vector % (symbol (str (first spec) "." %)))) (into {})) - {(symbol (last (string/split (str spec) #"\."))) spec})] + + (not (== -1 (.indexOf (str spec) "."))) + {(symbol (last (string/split (str spec) #"\."))) spec} + + :else {})] (doseq [[_ spec] import-map] (swap! deps conj spec)) {:import import-map diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 542f93656..6c0041f92 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1485,13 +1485,10 @@ (:import [goog.history Html5History]))])) (is (some? (get-in @cenv [::ana/namespaces 'goog.history.Html5History :defs 'Html5History]))))) -(comment - +(deftest test-cljs-3239 (let [cenv (env/default-compiler-env)] (env/with-compiler-env cenv (ana/analyze-form-seq '[(ns test.foo - (:import [goog.history Html5History]))])) - (get-in @cenv [::ana/namespaces 'goog.history.Html5History :defs])) - - ) + (:import goog))])) + (is (= {} (get-in @cenv [::ana/namespaces 'test.foo :imports]))))) From 419cd4b2f87e1e5557f5a1ed936fc1075bed9cd5 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 26 Apr 2020 15:42:47 -0400 Subject: [PATCH 3611/4033] Fix foreign-lib loading, was checking for `:nodejs` instead of `:nodejs-rt` --- src/main/clojure/cljs/compiler.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index f687c46a7..99f3854af 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1263,7 +1263,7 @@ (defn load-libs [libs seen reloads deps ns-name] (let [{:keys [options js-dependency-index]} @env/*compiler* - {:keys [target optimizations]} options + {:keys [target nodejs-rt optimizations]} options loaded-libs (munge 'cljs.core.*loaded-libs*) loaded-libs-temp (munge (gensym 'cljs.core.*loaded-libs*)) [node-libs libs-to-load] (let [libs (remove (set (vals seen)) (filter (set (vals libs)) deps))] @@ -1284,7 +1284,7 @@ ;; have handled it - David (when (and (= :none optimizations) (not (contains? options :modules))) - (if (= :nodejs target) + (if nodejs-rt ;; under node.js we load foreign libs globally (let [ijs (get js-dependency-index (name lib))] (emitln "cljs.core.load_file(" From 2f8dd2efdc5a2cb5e708bde09f65784669a72627 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 29 Apr 2020 13:55:25 -0400 Subject: [PATCH 3612/4033] add public functions for getting the current-ns & the current-file, add missing docstrings --- src/main/clojure/cljs/analyzer/api.cljc | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index d18e30af1..c0ba2e905 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -28,9 +28,21 @@ ([opts] (env/default-compiler-env opts))) -(defn current-state [] +(defn current-state + "Return the current compiler state atom." + [] env/*compiler*) +(defn current-file + "Return the current file under analysis or compilation." + [] + ana/*cljs-file*) + +(defn current-ns + "Return the current ns under analysis or compilation." + [] + ana/*cljs-ns*) + (defmacro with-state "Run the body with the given compilation state Atom." [state & body] @@ -95,9 +107,13 @@ ([state] (get @state :js-dependency-index))) -(def default-passes ana/default-passes) +(def + ^{:doc "ClojureScript's default analysis passes."} + default-passes ana/default-passes) -(defmacro with-passes [passes & body] +(defmacro with-passes + "Evaluate the body with the provided sequence of compiler passes." + [passes & body] `(binding [ana/*passes* ~passes] ~@body)) From 0bf9b1b0b6f2d1a036d52fa5e111d7c2ee79ba8e Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Apr 2020 10:07:42 -0400 Subject: [PATCH 3613/4033] add public read-analysis-cache --- src/main/clojure/cljs/analyzer/api.cljc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index c0ba2e905..18c035d02 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -11,8 +11,11 @@ to the analyzer." (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve find-ns ns-publics remove-ns]) - (:require [cljs.env :as env] - [cljs.analyzer :as ana])) + (:require [cljs.analyzer :as ana] + [cljs.env :as env] + [cljs.util :as util] + [clojure.edn :as edn] + [clojure.java.io :as io])) ;; ============================================================================= ;; Useful Utilities @@ -179,6 +182,15 @@ (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (ana/analyze-file f opts)))))) +(defn read-analysis-cache + "Read an analysis cache." + [cache-file] + (case (util/ext cache-file) + "edn" (edn/read-string (slurp cache-file)) + "json" (let [{:keys [reader read]} @ana/transit] + (with-open [is (io/input-stream cache-file)] + (read (reader is :json ana/transit-read-opts)))))) + ;; ============================================================================= ;; Main API From 052204d97a13e821b3a35689ee7279555b6c6d2a Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 30 Apr 2020 10:08:51 -0400 Subject: [PATCH 3614/4033] only provide cljs.analyzer.api/read-analysis-cache to non-bootstrapped --- src/main/clojure/cljs/analyzer/api.cljc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 18c035d02..43cc0c1c1 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -182,14 +182,15 @@ (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (ana/analyze-file f opts)))))) -(defn read-analysis-cache - "Read an analysis cache." - [cache-file] - (case (util/ext cache-file) - "edn" (edn/read-string (slurp cache-file)) - "json" (let [{:keys [reader read]} @ana/transit] - (with-open [is (io/input-stream cache-file)] - (read (reader is :json ana/transit-read-opts)))))) +#?(:clj + (defn read-analysis-cache + "Read an analysis cache." + [cache-file] + (case (util/ext cache-file) + "edn" (edn/read-string (slurp cache-file)) + "json" (let [{:keys [reader read]} @ana/transit] + (with-open [is (io/input-stream cache-file)] + (read (reader is :json ana/transit-read-opts))))))) ;; ============================================================================= ;; Main API From fef7d05ab398bb20acf07d16cb16aa467631f279 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 1 May 2020 14:52:13 -0400 Subject: [PATCH 3615/4033] add missing str --- src/main/clojure/cljs/repl/browser.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 50cb4c76c..1ec1b16fe 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -228,7 +228,7 @@ "document.write('');\n" "document.write('');\n" (when (.exists (io/file output-dir "cljs_deps.js")) - "document.write('');\n") + (str "document.write('');\n")) "document.write('');\n" "document.write('');\n") "text/javascript" "UTF-8")) From 917e1d2338873246b2dabaa04e339cd10c6e27ca Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 1 May 2020 15:04:52 -0400 Subject: [PATCH 3616/4033] 1.10.753 --- README.md | 6 +++--- changes.md | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 086633f01..9cbdde147 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.741 +Latest stable release: 1.10.753 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.741"] +[org.clojure/clojurescript "1.10.753"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.741 org.clojure clojurescript - 1.10.741 + 1.10.753 ``` diff --git a/changes.md b/changes.md index 184d53487..3ca633089 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,16 @@ +## 1.10.753 + +### Changes +* More useful functions added to cljs.analyzer.api + +### Fixes +* Fix foreign-lib loading, was checking for `:nodejs` instead of `:nodejs-rt` +* CLJS-3239: Infinite analysis with dotted symbols +* CLJS-3238: Watch incompatible with :bundle, throws :nodejs-related exception +* CLJS-3237: compiling with --target node errors at runtime with document undefined +* CLJS-3236: defprotocol externs inference warnings +* Fix (require ... :reload) REPL pattern + ## 1.10.741 ### Changes From 610ae65a749639e194bfef3c65e0283ac9508899 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 3 May 2020 13:41:43 -0400 Subject: [PATCH 3617/4033] CLJS-3242: Code Splitting Breakage Address breaking API changes to Closure Library. Quick fix for the trusted resource URL problem, will need compiler changes for this to be done in a better way. Already loaded modules are marked differently now. --- src/main/cljs/cljs/loader.cljs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 8618d2fc4..262dfefb3 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -7,7 +7,8 @@ ; You must not remove this notice, or any other, from this software (ns cljs.loader - (:require [goog.object :as gobj]) + (:require [goog.object :as gobj] + [goog.html.legacyconversions :as legacy]) (:import [goog.module ModuleLoader] [goog.module ModuleManager])) @@ -26,10 +27,14 @@ (cond-> x (keyword? x) (-> name munge))) +(defn to-tr-url [x] + (cond-> x + (not (keyword? x)) legacy/trustedResourceUrlFromString)) + (defn to-js [m] (reduce-kv (fn [ret k xs] - (let [arr (into-array (map munge-kw xs))] + (let [arr (into-array (map (comp munge-kw to-tr-url) xs))] (doto ret (gobj/set (-> k name munge) arr)))) #js {} m)) @@ -78,8 +83,8 @@ (str "Module " module-name " does not exist")) (let [xs (deps-for module-name module-infos)] (doseq [x xs] - (.setLoaded *module-manager* (munge-kw x))) - (.setLoaded *module-manager* (munge-kw module-name)))) + (.setLoaded (.getModuleInfo *module-manager* (munge-kw x)))) + (.setLoaded (.getModuleInfo *module-manager* (munge-kw module-name))))) (defn prefetch "Prefetch a module. module-name should be a keyword matching a :modules From f4631853660526a0ffbd9a57f3335d9bc005f551 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 4 May 2020 07:03:05 -0400 Subject: [PATCH 3618/4033] CLJS-3244: Warnings for clojure.browser.net with :static-fns true In :invoke emission of the compiler we were resolving the tag to check to see if the type implements a protocol. But this could easily be a type we can't possibly know about - i.e. Google Closure Library ctor, foreign library ctor, etc. Explicit type-hinting is not idiomatic over predicate driven optimization, so just suppress any warnings. --- src/main/clojure/cljs/compiler.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 99f3854af..25d1da5be 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1141,7 +1141,11 @@ ;; ignore new type hints for now - David (and (not (set? tag)) (not ('#{any clj clj-or-nil clj-nil number string boolean function object array js} tag)) - (when-let [ps (:protocols (ana/resolve-existing-var env tag))] + (when-let [ps (:protocols + (ana/resolve-existing-var env + ;; we're just checking for protocol methods, + ;; an internal optimization, don't emit warnings + (vary-meta tag assoc ::ana/no-resolve true)))] (ps protocol))))))) first-arg-tag (ana/infer-tag env (first (:args expr))) opt-not? (and (= (:name info) 'cljs.core/not) From 7b136b0fc0afa3e64e1ec9810501aa498a43fe05 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 4 May 2020 07:14:55 -0400 Subject: [PATCH 3619/4033] 1.10.756 --- README.md | 6 +++--- changes.md | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9cbdde147..ea6a13e5d 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.753 +Latest stable release: 1.10.756 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.753"] +[org.clojure/clojurescript "1.10.756"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.753 org.clojure clojurescript - 1.10.753 + 1.10.756 ``` diff --git a/changes.md b/changes.md index 3ca633089..2b2a81d07 100644 --- a/changes.md +++ b/changes.md @@ -1,9 +1,11 @@ -## 1.10.753 +## 1.10.756 ### Changes * More useful functions added to cljs.analyzer.api ### Fixes +* CLJS-3242: Code Splitting Breakage +* CLJS-3244: Warnings for clojure.browser.net with :static-fns true * Fix foreign-lib loading, was checking for `:nodejs` instead of `:nodejs-rt` * CLJS-3239: Infinite analysis with dotted symbols * CLJS-3238: Watch incompatible with :bundle, throws :nodejs-related exception From 615b1f79c10c62ac4292da4cff3eb56006cf0547 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 2 May 2020 12:18:10 -0400 Subject: [PATCH 3620/4033] CLJS-3245: self-host: cljs.analyzer namespace unconditionally requires clojure.java.io --- src/main/clojure/cljs/analyzer/api.cljc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 43cc0c1c1..c9251fe87 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -11,11 +11,13 @@ to the analyzer." (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve find-ns ns-publics remove-ns]) - (:require [cljs.analyzer :as ana] - [cljs.env :as env] - [cljs.util :as util] - [clojure.edn :as edn] - [clojure.java.io :as io])) + #?(:clj (:require [cljs.analyzer :as ana] + [cljs.env :as env] + [cljs.util :as util] + [clojure.edn :as edn] + [clojure.java.io :as io]) + :cljs (:require [cljs.analyzer :as ana] + [cljs.env :as env]))) ;; ============================================================================= ;; Useful Utilities From f5a9766110227c0d2a8702b32c44d52059b97a53 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 4 May 2020 09:34:13 -0400 Subject: [PATCH 3621/4033] 1.10.758 --- README.md | 6 +++--- changes.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ea6a13e5d..98682912b 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.756 +Latest stable release: 1.10.758 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.756"] +[org.clojure/clojurescript "1.10.758"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.756 org.clojure clojurescript - 1.10.756 + 1.10.758 ``` diff --git a/changes.md b/changes.md index 2b2a81d07..dd63f1c91 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.10.756 +## 1.10.758 ### Changes * More useful functions added to cljs.analyzer.api From 0c5ca7fc19b9a446ede721143f59b00769dc0ac6 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 May 2020 09:15:50 -0400 Subject: [PATCH 3622/4033] fix typo in changes.md --- changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.md b/changes.md index dd63f1c91..f8d893148 100644 --- a/changes.md +++ b/changes.md @@ -21,7 +21,7 @@ * CLJS-1628: Make instances of js/Symbol printable ### Enhancements -* Add :target :bundle for integration with JavaScript bunders (webpack, metro, etc.) +* Add :target :bundle for integration with JavaScript bundlers (webpack, metro, etc.) * Add cljs.main --install-deps flag * Add :bundle-cmd compiler option for triggering JS bundler at end of build * Add :nodejs-rt compiler option to diable Node.js runtime support (for bundles) From b8a2f980595eb20a9ca2aaabd987b4e430a67e51 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 May 2020 12:00:34 -0400 Subject: [PATCH 3623/4033] copy over :main from compile opts --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index d358f71cf..e77443a7e 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -324,7 +324,7 @@ present" (assoc :output-dir (temp-out-dir) :temp-output-dir? true) (not (contains? options :aot-cache)) (assoc :aot-cache true)) - reopts (merge repl-env-options (select-keys opts [:output-dir])) + 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))] From be792c950ad56b23454e9136260ce7a49073323c Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 6 May 2020 14:02:20 -0400 Subject: [PATCH 3624/4033] Revert CLJS-2582, the original patch dissoc'ed :main and :output-to which only addressed the symptom, not the cause. When compiling core in the Node.js REPL set the :target to :none so we get only the deps file. We don't need the target bootstrap file normally generated for the target because we're bootstrapping the Node.js environment ourselves. --- src/main/clojure/cljs/cli.clj | 11 ++--------- src/main/clojure/cljs/repl/node.clj | 6 +++--- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index e77443a7e..4f9339518 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -284,12 +284,6 @@ classpath. Classpath-relative paths have prefix of @ or @/") ((get-dispatch commands :init opt) ret arg)) {} inits)) -(defn dissoc-entry-point-opts - "Dissoc the entry point options from the input. Necessary when the user -is trying load some arbitrary ns." - [opts] - (dissoc opts :main :output-to)) - (defn temp-out-dir [] (let [f (File/createTempFile "out" (Long/toString (System/nanoTime)))] (.delete f) @@ -329,7 +323,7 @@ present" (util/debug-prn "REPL env options:" (pr-str reopts))) renv (apply (target->repl-env (:target options) repl-env) (mapcat identity reopts))] (repl/repl* renv - (assoc (dissoc-entry-point-opts opts) + (assoc opts ::repl/fast-initial-prompt? (or (fast-initial-prompt? repl-env options inits) (::repl/fast-initial-prompt? (repl/repl-options renv))) @@ -361,8 +355,7 @@ present" coptsf (when-let [od (:output-dir opts)] (io/file od "cljsc_opts.edn")) copts (when (and coptsf (.exists coptsf)) - (-> (edn/read-string (slurp coptsf)) - (dissoc-entry-point-opts))) + (edn/read-string (slurp coptsf))) opts (merge copts (build/add-implicit-options (merge (repl/repl-options renv) opts)))] diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index 03f6335f2..18bd4bf68 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -167,10 +167,10 @@ (closure/src-file->target-file core (dissoc opts :output-dir)))) deps (closure/add-dependencies opts core-js)] - ;; output unoptimized code and the deps file - ;; for all compiled namespaces + ;; output unoptimized code and only the deps file for all compiled + ;; namespaces, we don't need the bootstrap target file (apply closure/output-unoptimized - (assoc opts + (assoc (assoc opts :target :none) :output-to (.getPath (io/file output-dir "node_repl_deps.js"))) deps)) ;; bootstrap, replace __dirname as __dirname won't be set From 538dbe8133895c56f7da84609bcd696c85aa925c Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 12 May 2020 11:04:01 -0400 Subject: [PATCH 3625/4033] If :main supplied to -c pass it along to -r --- src/main/clojure/cljs/cli.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 4f9339518..e46108663 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -531,7 +531,8 @@ present" (when (fn? post-compile-fn) (post-compile-fn)) (when repl? - (repl-opt repl-env args cfg)) + (repl-opt repl-env args + (cond-> cfg main-ns (update :options merge {:main main-ns})))) (when serve? (serve-opt repl-env args cfg))))) From 0c557550fe9d1f32814a66deda820d093a34dc1b Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 13 May 2020 12:12:36 -0400 Subject: [PATCH 3626/4033] just export the special Google config directly to window --- src/main/clojure/cljs/closure.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 01a481985..b9266744b 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1747,8 +1747,8 @@ "import {npmDeps} from \"./npm_deps.js\";\n") (when (or (not module) (= :cljs-base (:module-name opts))) (str - "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" - "var CLOSURE_NO_DEPS = true;\n" + "window.CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n" + "window.CLOSURE_NO_DEPS = true;\n" "if(typeof goog == \"undefined\") document.write('');\n" "document.write('');\n" "document.write('');\n" From ac23fec265bdf0ca971eb35c16da4b59191da5ca Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 13 May 2020 19:17:02 -0400 Subject: [PATCH 3627/4033] 1.10.764 --- README.md | 6 +++--- changes.md | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 98682912b..7dd37aaad 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.758 +Latest stable release: 1.10.764 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.758"] +[org.clojure/clojurescript "1.10.764"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.758 org.clojure clojurescript - 1.10.758 + 1.10.764 ``` diff --git a/changes.md b/changes.md index f8d893148..365d5992c 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,10 @@ +## 1.10.764 + +### Fixes +* Export Google Closure Library config directly to window in brwoser +* CLI: If :main supplied to -c pass it along to -r +* Revert CLJS-2582 + ## 1.10.758 ### Changes From ec0fc33030ae1858a29c52f38e81fba4180d492b Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 May 2020 11:53:52 -0400 Subject: [PATCH 3628/4033] pass compiler env to repl if using -c -r --- src/main/clojure/cljs/cli.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index e46108663..62c9b09e0 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -532,7 +532,8 @@ present" (post-compile-fn)) (when repl? (repl-opt repl-env args - (cond-> cfg main-ns (update :options merge {:main main-ns})))) + (cond-> (assoc-in cfg [:options :compiler-env] cenv) + main-ns (update :options merge {:main main-ns})))) (when serve? (serve-opt repl-env args cfg))))) From 4624562e5c0603c099a17e2d080e5667b6150020 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 May 2020 14:32:57 -0400 Subject: [PATCH 3629/4033] clarify start-evaluator docstring --- src/main/cljs/clojure/browser/repl.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index a0eca7600..61044a43c 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -99,7 +99,8 @@ :order (swap! order inc)})) (defn start-evaluator - "Start the REPL server connection." + "Start the REPL server connection process. This process runs inside the + embedded iframe." [url] (if-let [repl-connection (net/xpc-connection)] (let [connection (net/xhr-connection) From d06269e89c14b51eb6ea0fed89299169af4d3a76 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 May 2020 16:05:05 -0400 Subject: [PATCH 3630/4033] clarifying comments in client browser repl code --- src/main/cljs/clojure/browser/repl.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 61044a43c..71fb31f8a 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -32,8 +32,12 @@ (goog-define PORT 9000) (def ^:dynamic *repl* nil) + +;; these two defs are top-level so we can use them for printing (def xpc-connection (atom nil)) (def parent-connected? (atom false)) + +;; captures any printing that occurs *before* we actually have a connection (def print-queue (array)) (defn flush-print-queue! [conn] From 00079768f9104b17f4130cd710a2f636e046cb07 Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 14 May 2020 21:04:40 -0400 Subject: [PATCH 3631/4033] if :main supplied to REPL analyze it --- src/main/clojure/cljs/repl.cljc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 07632974d..867077a6c 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1174,6 +1174,10 @@ (if (vector? analyze-path) (run! #(analyze-source % opts) analyze-path) (analyze-source analyze-path opts))) + (when-let [main-ns (:main opts)] + (.start + (Thread. + (bound-fn [] (ana/analyze-file (util/ns->source main-ns)))))) (init) (run-inits repl-env inits) (maybe-load-user-file) From 0eaa19f4326f02d4dc4e8660ad5f13329b73e3af Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 15 May 2020 07:10:41 -0400 Subject: [PATCH 3632/4033] CLJS-1147: Reconnect logic for browser REPL Add an error handler on the xhr-connection in the evaluator process. If this fails, set state as disconnected, notify parent of disconnect and retry after a 1 second delay. Remove the timeout from try-handshake now that this logic is in place. Now browser REPL works in all ordering scenarios. A browser can be closed and reopened, a REPL can be stopped and a new one started to reconnect to an open browser w/o refresh. --- src/main/cljs/clojure/browser/repl.cljs | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 71fb31f8a..337408eee 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -111,13 +111,7 @@ repl-connected? (atom false) try-handshake (fn try-handshake [] (when-not @repl-connected? - (net/transmit repl-connection - :start-handshake - nil) - ;; In case we miss, try again. Parent will only - ;; ack once. - (js/setTimeout try-handshake - 10)))] + (net/transmit repl-connection :start-handshake nil)))] (net/connect repl-connection try-handshake) (net/register-service repl-connection @@ -130,6 +124,13 @@ (send-result connection url (wrap-message nil :ready "ready"))))) + (event/listen connection + :error + (fn [e] + (reset! repl-connected? false) + (net/transmit repl-connection :reconnect nil) + (js/setTimeout try-handshake 1000))) + (event/listen connection :success (fn [e] @@ -234,9 +235,7 @@ the document that called this function." [repl-server-url] (let [connected? (atom false) - repl-connection - (net/xpc-connection - {:peer_uri repl-server-url})] + repl-connection (net/xpc-connection {:peer_uri repl-server-url})] (swap! xpc-connection (constantly repl-connection)) (net/register-service repl-connection :start-handshake @@ -246,10 +245,13 @@ (when-not @connected? (reset! connected? true) (reset! parent-connected? true) - (net/transmit repl-connection - :ack-handshake - nil) + (net/transmit repl-connection :ack-handshake nil) (flush-print-queue! repl-connection)))) + (net/register-service repl-connection + :reconnect + (fn [_] + (reset! connected? false) + (reset! parent-connected? false))) (net/register-service repl-connection :evaluate-javascript (fn [json] From 5b1bdbae9ab44ce86015e5fc6c95ef5ac86ca9b9 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Thu, 7 May 2020 12:51:26 +0200 Subject: [PATCH 3633/4033] Provide accurate file name and line numbers in cljs.test Instead of inaccurately inferring these from the call stack in `do-report`, capture them during macro-expansion based on metadata. Co-authored-by: Thomas Heller --- src/main/cljs/cljs/test.cljc | 118 ++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 49 deletions(-) diff --git a/src/main/cljs/cljs/test.cljc b/src/main/cljs/cljs/test.cljc index d648326ed..1722d2d62 100644 --- a/src/main/cljs/cljs/test.cljc +++ b/src/main/cljs/cljs/test.cljc @@ -30,31 +30,37 @@ be wrapped in (not...)." [msg form] (let [args (rest form) - pred (first form)] + pred (first form) + {:keys [file line end-line column end-column]} (meta form)] `(let [values# (list ~@args) result# (apply ~pred values#)] (if result# - (do-report - {:type :pass, :message ~msg, - :expected '~form, :actual (cons ~pred values#)}) - (do-report - {:type :fail, :message ~msg, - :expected '~form, :actual (list '~'not (cons '~pred values#))})) + (report + {:type :pass, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual (cons '~pred values#)}) + (report + {:type :fail, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual (list '~'not (cons '~pred values#))})) result#))) (defn assert-any "Returns generic assertion code for any test, including macros, Java method calls, or isolated symbols." [msg form] - `(let [value# ~form] - (if value# - (do-report - {:type :pass, :message ~msg, - :expected '~form, :actual value#}) - (do-report - {:type :fail, :message ~msg, - :expected '~form, :actual value#})) - value#)) + (let [{:keys [file line end-line column end-column]} (meta form)] + `(let [value# ~form] + (if value# + (report + {:type :pass, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual value#}) + (report + {:type :fail, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual value#})) + value#))) (defmacro ^:private cljs-output-dir [] (let [{:keys [output-dir]} (ana-api/get-options)] @@ -76,7 +82,9 @@ (defmethod assert-expr :always-fail [menv msg form] ;; nil test: always fail - `(do-report {:type :fail, :message ~msg})) + (let [{:keys [file line end-line column end-column]} (meta form)] + `(report {:type :fail, :message ~msg + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column}))) (defmethod assert-expr :default [menv msg form] (if (and (sequential? form) @@ -86,33 +94,39 @@ (defmethod assert-expr 'instance? [menv msg form] ;; Test if x is an instance of y. - `(let [klass# ~(nth form 1) - object# ~(nth form 2)] - (let [result# (instance? klass# object#)] - (if result# - (do-report - {:type :pass, :message ~msg, - :expected '~form, :actual (type object#)}) - (do-report - {:type :fail, :message ~msg, - :expected '~form, :actual (type object#)})) - result#))) + (let [{:keys [file line end-line column end-column]} (meta form)] + `(let [klass# ~(nth form 1) + object# ~(nth form 2)] + (let [result# (instance? klass# object#)] + (if result# + (report + {:type :pass, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual (type object#)}) + (report + {:type :fail, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual (type object#)})) + result#)))) (defmethod assert-expr 'thrown? [menv msg form] ;; (is (thrown? c expr)) ;; Asserts that evaluating expr throws an exception of class c. ;; Returns the exception thrown. - (let [klass (second form) + (let [{:keys [file line end-line column end-column]} (meta form) + klass (second form) body (nthnext form 2)] `(try ~@body - (do-report - {:type :fail, :message ~msg, - :expected '~form, :actual nil}) + (report + {:type :fail, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual nil}) (catch ~klass e# - (do-report - {:type :pass, :message ~msg, - :expected '~form, :actual e#}) + (report + {:type :pass, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual e#}) e#)))) (defmethod assert-expr 'thrown-with-msg? [menv msg form] @@ -120,33 +134,39 @@ ;; Asserts that evaluating expr throws an exception of class c. ;; Also asserts that the message string of the exception matches ;; (with re-find) the regular expression re. - (let [klass (nth form 1) + (let [{:keys [file line end-line column end-column]} (meta form) + klass (nth form 1) re (nth form 2) body (nthnext form 3)] `(try ~@body - (do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) + (report {:type :fail, :message ~msg, :expected '~form, :actual nil + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column}) (catch ~klass e# (let [m# (.-message e#)] (if (re-find ~re m#) - (do-report - {:type :pass, :message ~msg, - :expected '~form, :actual e#}) - (do-report - {:type :fail, :message ~msg, - :expected '~form, :actual e#})) + (report + {:type :pass, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual e#}) + (report + {:type :fail, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual e#})) e#))))) (defmacro try-expr "Used by the 'is' macro to catch unexpected exceptions. You don't call this." [msg form] - `(try - ~(assert-expr &env msg form) - (catch :default t# - (do-report - {:type :error, :message ~msg, - :expected '~form, :actual t#})))) + (let [{:keys [file line end-line column end-column]} (meta form)] + `(try + ~(assert-expr &env msg form) + (catch :default t# + (report + {:type :error, :message ~msg, + :file ~file :line ~line :end-line ~end-line :column ~column :end-column ~end-column + :expected '~form, :actual t#}))))) ;; ============================================================================= ;; Assertion Macros From 1084cca73ac5082ae76f1939356f664f35284c4d Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 22 May 2020 14:23:30 -0400 Subject: [PATCH 3634/4033] browser REPL cli support wasn't setting :closure-defines for port and host --- src/main/clojure/cljs/repl/browser.clj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 1ec1b16fe..e51d9499b 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -422,11 +422,18 @@ {:groups {::repl {:desc "browser REPL options"}} :init {["-H" "--host"] - {:group ::repl :fn #(assoc-in %1 [:repl-env-options :host] %2) + {:group ::repl + :fn #(-> %1 + (assoc-in [:repl-env-options :host] %2) + (assoc-in [:options :closure-defines 'clojure.browser.repl/HOST] %2)) :arg "address" :doc "Address to bind"} ["-p" "--port"] - {:group ::repl :fn #(assoc-in %1 [:repl-env-options :port] (Integer/parseInt %2)) + {:group ::repl + :fn #(let [port (Integer/parseInt %2)] + (-> %1 + (assoc-in [:repl-env-options :port] port) + (assoc-in [:options :closure-defines 'clojure.browser.repl/PORT] port))) :arg "number" :doc "Port to bind"}}}}) repl/IParseStacktrace From 57941cf07d6745e79d04410538d8d19bbb86a7f8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 May 2020 12:17:54 -0400 Subject: [PATCH 3635/4033] change cljs.closure/check-npm-deps so it works if `:npm-deps true` instead of a just a map --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b9266744b..275786ceb 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2375,7 +2375,8 @@ (pr-str cache-analysis-format)))) (defn check-npm-deps [{:keys [npm-deps]}] - (let [{ups-npm-deps :npm-deps} (get-upstream-deps) + (let [npm-deps (if (true? npm-deps) {} npm-deps) + {ups-npm-deps :npm-deps} (get-upstream-deps) conflicts (filter (fn [[dep v]] (and (coll? v) (not (contains? npm-deps dep)))) ups-npm-deps)] From 790bcbf5f45d3775c15d597eee240fb3f3636681 Mon Sep 17 00:00:00 2001 From: dnolen Date: Tue, 26 May 2020 13:09:37 -0400 Subject: [PATCH 3636/4033] `:bundle` should expand to overrides except for `:npm-deps` --- src/main/clojure/cljs/closure.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 275786ceb..b5a0d1c06 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2511,8 +2511,9 @@ {:hashbang false :infer-externs true :nodejs-rt false - :npm-deps true - :target :nodejs}) + :target :nodejs} + (when-not (:npm-deps opts) + {:npm-deps true})) (= optimizations :none) (assoc From 39709c9614d37b9a3dd398be8fed83cd3dda534b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 2 Sep 2018 10:57:47 -0400 Subject: [PATCH 3637/4033] CLJS-2880: cl-format octal and Unicode character directives fail --- src/main/cljs/cljs/pprint.cljs | 4 ++-- src/test/cljs/cljs/pprint_test.cljs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/pprint.cljs b/src/main/cljs/cljs/pprint.cljs index 2f60bcebf..718ba6ec1 100644 --- a/src/main/cljs/cljs/pprint.cljs +++ b/src/main/cljs/cljs/pprint.cljs @@ -1362,8 +1362,8 @@ http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm" (defn- readable-character [params navigator offsets] (let [[c navigator] (next-arg navigator)] (condp = (:char-format params) - \o (cl-format true "\\o~3, '0o" (char-code c)) - \u (cl-format true "\\u~4, '0x" (char-code c)) + \o (cl-format true "\\o~3,'0o" (char-code c)) + \u (cl-format true "\\u~4,'0x" (char-code c)) nil (print-char c)) navigator)) diff --git a/src/test/cljs/cljs/pprint_test.cljs b/src/test/cljs/cljs/pprint_test.cljs index 3719cdeb4..2f95ea50b 100644 --- a/src/test/cljs/cljs/pprint_test.cljs +++ b/src/test/cljs/cljs/pprint_test.cljs @@ -1087,6 +1087,13 @@ Usage: *hello* " ) +(deftest test-cljs-2880 + (are [expected format] + (= expected (with-out-str (cl-format true format \a))) + "\\a" "~@c" + "\\o141" "~'o@c" + "\\u0061" "~'u@c")) + (deftest test-cljs-2881 (are [expected ch] (= expected (with-out-str (cl-format true "~@c" ch))) From 946348da8eb705da23f465be29246d4f8b73d45f Mon Sep 17 00:00:00 2001 From: dnolen Date: Thu, 4 Jun 2020 10:09:53 -0400 Subject: [PATCH 3638/4033] add `:nodejs-rt` to the list of build affecting options --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ac66b9281..f4938a97f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4479,7 +4479,7 @@ #?(:clj (defn build-affecting-options [opts] (select-keys opts - [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target + [:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target :nodejs-rt :cache-key :checked-arrays :language-out]))) #?(:clj From 7a0ec41731f0fa5faccfd9abb5f906d918795c16 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 Jun 2020 14:07:12 -0400 Subject: [PATCH 3639/4033] CLJS-3257: `satisfies?` produces an inference warning when given an unhinted argument We're checking protocol methods so mark the symbol as such. Add test case. --- src/main/clojure/cljs/core.cljc | 6 ++++-- src/test/clojure/cljs/externs_infer_tests.clj | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index feb7b506f..4bf6b0ab1 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2212,7 +2212,8 @@ `(let [~xsym ~x] (if-not (nil? ~xsym) (if (or ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit) false) - (identical? cljs.core/PROTOCOL_SENTINEL (. ~xsym ~(symbol (core/str "-" prefix))))) + (identical? cljs.core/PROTOCOL_SENTINEL + (. ~xsym ~(with-meta (symbol (core/str "-" prefix)) {:protocol-method true})))) true (if (coercive-not (. ~xsym ~msym)) (cljs.core/native-satisfies? ~psym ~xsym) @@ -2220,7 +2221,8 @@ (cljs.core/native-satisfies? ~psym ~xsym))) `(if-not (nil? ~x) (if (or ~(if bit `(unsafe-bit-and (. ~x ~msym) ~bit) false) - (identical? cljs.core/PROTOCOL_SENTINEL (. ~x ~(symbol (core/str "-" prefix))))) + (identical? cljs.core/PROTOCOL_SENTINEL + (. ~x ~(with-meta (symbol (core/str "-" prefix)) {:protocol-method true})))) true (if (coercive-not (. ~x ~msym)) (cljs.core/native-satisfies? ~psym ~x) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 6f2cdda3c..6f3286b14 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -398,6 +398,21 @@ :with-core? true}))] (is (empty? @ws)))) +(deftest test-cljs-3257 + (let [ws (atom []) + res (binding [ana/*cljs-static-fns* true] + (infer-test-helper + {:forms '[(ns app.core) + (set! *warn-on-infer* true) + (defprotocol IFoo + (bar [this])) + (defn not-ok? [v] + (satisfies? IFoo v))] + :warnings ws + :warn true + :with-core? true}))] + (is (empty? @ws)))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From 411806572ed0bfeb2e6ee2ed1b6d116ba2457a0f Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 5 Jun 2020 14:26:33 -0400 Subject: [PATCH 3640/4033] :protocol-method -> :protocol-prop, that's what we're actually checking for --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/main/clojure/cljs/core.cljc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f4938a97f..d46d6216f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3417,7 +3417,7 @@ nil)] (when (and (not= 'constructor prop) (not (string/starts-with? (str prop) "cljs$")) - (not (-> prop meta :protocol-method))) + (not (-> prop meta :protocol-prop))) ;; Adding to Object (when (= 'Object (first (-> tag meta :prefix))) (warning :infer-warning env diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 4bf6b0ab1..cdadb9e99 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2110,7 +2110,7 @@ check `(if (and (not (nil? ~fsig)) ;; Property access needed here. - (not (nil? (. ~fsig ~(with-meta (symbol (core/str "-" slot)) {:protocol-method true}))))) + (not (nil? (. ~fsig ~(with-meta (symbol (core/str "-" slot)) {:protocol-prop true}))))) (. ~fsig ~slot ~@sig) (~dyn-name ~@sig))] `(~sig ~check))) @@ -2162,7 +2162,7 @@ ~@(map (core/fn [sig] (expand-sig dyn-name (with-meta (symbol (core/str slot "$arity$" (count sig))) - {:protocol-method true}) + {:protocol-prop true}) sig)) sigs)))))] `(do @@ -2213,7 +2213,7 @@ (if-not (nil? ~xsym) (if (or ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit) false) (identical? cljs.core/PROTOCOL_SENTINEL - (. ~xsym ~(with-meta (symbol (core/str "-" prefix)) {:protocol-method true})))) + (. ~xsym ~(with-meta (symbol (core/str "-" prefix)) {:protocol-prop true})))) true (if (coercive-not (. ~xsym ~msym)) (cljs.core/native-satisfies? ~psym ~xsym) @@ -2222,7 +2222,7 @@ `(if-not (nil? ~x) (if (or ~(if bit `(unsafe-bit-and (. ~x ~msym) ~bit) false) (identical? cljs.core/PROTOCOL_SENTINEL - (. ~x ~(with-meta (symbol (core/str "-" prefix)) {:protocol-method true})))) + (. ~x ~(with-meta (symbol (core/str "-" prefix)) {:protocol-prop true})))) true (if (coercive-not (. ~x ~msym)) (cljs.core/native-satisfies? ~psym ~x) From 42bcb07b8bf23d57f98e4617e4c4c93347f09715 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 6 Jun 2020 12:35:05 -0400 Subject: [PATCH 3641/4033] compute-npm-deps needs to check for :npm-deps true case --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b5a0d1c06..12df2f611 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2437,7 +2437,7 @@ (reduce (fn [m [dep v]] (cond-> m - (not (contains? npm-deps dep)) + (and (map? npm-deps) (not (contains? npm-deps dep))) (assoc dep (if (coll? v) (last (sort v)) v)))) From ba048aa3cfee4977c49372f03e58e4a4fcaef7b5 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 18 Jun 2020 13:14:05 -0500 Subject: [PATCH 3642/4033] add LICENSE text file --- LICENSE | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..e246f6a21 --- /dev/null +++ b/LICENSE @@ -0,0 +1,205 @@ +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation + distributed under this Agreement, and +b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are + distributed by that particular Contributor. A Contribution 'originates' + from a Contributor if it was added to the Program by such Contributor + itself or anyone acting on such Contributor's behalf. Contributions do not + include additions to the Program which: (i) are separate modules of + software distributed in conjunction with the Program under their own + license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + a) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free copyright license to + reproduce, prepare derivative works of, publicly display, publicly + perform, distribute and sublicense the Contribution of such Contributor, + if any, and such derivative works, in source code and object code form. + b) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free patent license under + Licensed Patents to make, use, sell, offer to sell, import and otherwise + transfer the Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the combination of + the Contribution and the Program if, at the time the Contribution is + added by the Contributor, such addition of the Contribution causes such + combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Contribution. + No hardware per se is licensed hereunder. + c) Recipient understands that although each Contributor grants the licenses + to its Contributions set forth herein, no assurances are provided by any + Contributor that the Program does not infringe the patent or other + intellectual property rights of any other entity. Each Contributor + disclaims any liability to Recipient for claims brought by any other + entity based on infringement of intellectual property rights or + otherwise. As a condition to exercising the rights and licenses granted + hereunder, each Recipient hereby assumes sole responsibility to secure + any other intellectual property rights needed, if any. For example, if a + third party patent license is required to allow Recipient to distribute + the Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + d) Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + b) its license agreement: + i) effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii) effectively excludes on behalf of all Contributors all liability for + damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; + iii) states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party; and + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable + manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + b) a copy of this Agreement must be included with each copy of the Program. + Contributors may not remove or alter any copyright notices contained + within the Program. + +Each Contributor must identify itself as the originator of its Contribution, +if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, +if a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits and +other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such Commercial +Contributor in connection with its distribution of the Program in a commercial +product offering. The obligations in this section do not apply to any claims +or Losses relating to any actual or alleged intellectual property +infringement. In order to qualify, an Indemnified Contributor must: +a) promptly notify the Commercial Contributor in writing of such claim, and +b) allow the Commercial Contributor to control, and cooperate with the +Commercial Contributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such claim at +its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If +that Commercial Contributor then makes performance claims, or offers +warranties related to Product X, those performance claims and warranties are +such Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to the +risks and costs of program errors, compliance with applicable laws, damage to +or loss of data, programs or equipment, and unavailability or interruption of +operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION +LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of the +remainder of the terms of this Agreement, and without further action by the +parties hereto, such provision shall be reformed to the minimum extent +necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted +under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to +time. No one other than the Agreement Steward has the right to modify this +Agreement. The Eclipse Foundation is the initial Agreement Steward. The +Eclipse Foundation may assign the responsibility to serve as the Agreement +Steward to a suitable separate entity. Each new version of the Agreement will +be given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version of the +Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly +stated in Sections 2(a) and 2(b) above, Recipient receives no rights or +licenses to the intellectual property of any Contributor under this Agreement, +whether expressly, by implication, estoppel or otherwise. All rights in the +Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial in +any resulting litigation. + + From 25c9587d6ec37d4aacb291bb2fec8b46d5b80a7d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 29 Mar 2020 21:27:27 -0400 Subject: [PATCH 3643/4033] CLJS-3130: UUID compares equal to other values --- src/main/cljs/cljs/core.cljs | 6 ++++-- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 05b2f4e60..651e9ac47 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11293,8 +11293,10 @@ reduces them without incurring seq initialization" __hash) IComparable - (-compare [_ other] - (garray/defaultCompare uuid (.-uuid other)))) + (-compare [this other] + (if (instance? UUID other) + (garray/defaultCompare uuid (.-uuid other)) + (throw (js/Error. (str "Cannot compare " this " to " other)))))) (defn uuid [s] (assert (string? s)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 845f29ff0..576f339b6 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1808,6 +1808,10 @@ (is (= "a" (get "abc" -0.5))) (is (nil? (get "abc" -1)))) +(deftest test-cljs-3130 + (is (thrown-with-msg? js/Error #"Cannot compare f151d12d-7bd5-4409-9352-5900ee07baf7 to a" + (compare (uuid "f151d12d-7bd5-4409-9352-5900ee07baf7") "a")))) + (deftest test-cljs-3202 (is (= :/ (keyword "/"))) (is (= (hash :/) (hash (keyword "/"))))) \ No newline at end of file From 92707a0b1ffb2a9fb6c019f877008eaeaedf97e5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 15 Dec 2018 15:05:02 -0500 Subject: [PATCH 3644/4033] CLJS-3019: Error->map should produce qualified symbols for :type --- src/main/cljs/cljs/repl.cljs | 2 +- src/test/cljs/cljs/repl_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index b28b2a153..964de2d38 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -72,7 +72,7 @@ [o] (let [base (fn [t] (merge {:type (cond - (instance? ExceptionInfo t) 'ExceptionInfo + (instance? ExceptionInfo t) `ExceptionInfo (instance? js/Error t) (symbol "js" (.-name t)) :else nil)} (when-let [msg (ex-message t)] diff --git a/src/test/cljs/cljs/repl_test.cljs b/src/test/cljs/cljs/repl_test.cljs index 27ecb63e4..4e0083a00 100644 --- a/src/test/cljs/cljs/repl_test.cljs +++ b/src/test/cljs/cljs/repl_test.cljs @@ -14,3 +14,7 @@ (deftest test-cljs-3017 (let [m (cljs.repl/Error->map (js/TypeError.))] (is (= 'js/TypeError (get-in m [:via 0 :type]))))) + +(deftest test-cljs-3019 + (let [m (cljs.repl/Error->map (ex-info "" {}))] + (is (= 'cljs.core/ExceptionInfo (get-in m [:via 0 :type]))))) From d82aab4a2a32fb45184dc5acfee8a6f3b5fe1f43 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 14:20:50 -0400 Subject: [PATCH 3645/4033] bump test runner, add resources/test.edn --- deps.edn | 10 +++++----- resources/test.edn | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 resources/test.edn diff --git a/deps.edn b/deps.edn index 29a2c258e..d1da50af4 100644 --- a/deps.edn +++ b/deps.edn @@ -10,8 +10,8 @@ com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200315"} org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"}} :aliases - {:test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" - "src/test/clojure" "src/test/self"] - :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" - :sha "209b64504cb3bd3b99ecfec7937b358a879f55c1"}}} - :run-tests {:main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure" "-r" ".*-tests$"]}}} + {:compiler-test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" + "src/test/clojure" "src/test/self"] + :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" + :sha "f7ef16dc3b8332b0d77bc0274578ad5270fbfedd"}}} + :run-compiler-tests {:main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure" "-r" ".*-tests$"]}}} diff --git a/resources/test.edn b/resources/test.edn new file mode 100644 index 000000000..96bacd267 --- /dev/null +++ b/resources/test.edn @@ -0,0 +1,25 @@ +{:optimizations :advanced + :main test-runner + :output-wrapper true + :verbose true + :compiler-stats true + :parallel-build true + :output-dir "builds/out-adv" + :npm-deps {:lodash "4.17.4"} + :closure-warnings {:non-standard-jsdoc :off :global-this :off} + :install-deps true + :language-in :es6 + :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}]} From 114b113af729cc513960391d0bd24861ea57da1b Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 14:28:30 -0400 Subject: [PATCH 3646/4033] tweak aliases, add new alias for building runtime tests --- deps.edn | 6 ++++-- resources/test.edn | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/deps.edn b/deps.edn index d1da50af4..65fa4236b 100644 --- a/deps.edn +++ b/deps.edn @@ -10,8 +10,10 @@ com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200315"} org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"}} :aliases - {:compiler-test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" + {:runtime.test.build {:extra-paths ["src/test/cljs"] + :main-opts ["-m" "cljs.main" "-co" "resources/test.edn" "-c"]} + :compiler.test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" "src/test/clojure" "src/test/self"] :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" :sha "f7ef16dc3b8332b0d77bc0274578ad5270fbfedd"}}} - :run-compiler-tests {:main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure" "-r" ".*-tests$"]}}} + :compiler.test.run {:main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure" "-r" ".*-tests$"]}}} diff --git a/resources/test.edn b/resources/test.edn index 96bacd267..1ec229c32 100644 --- a/resources/test.edn +++ b/resources/test.edn @@ -1,10 +1,11 @@ {:optimizations :advanced :main test-runner + :output-to "builds/out-adv/core-advanced-test.js" + :output-dir "builds/out-adv" :output-wrapper true :verbose true :compiler-stats true :parallel-build true - :output-dir "builds/out-adv" :npm-deps {:lodash "4.17.4"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true From 99dee6479374be170d68d9edf2087fbf8955d206 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 14:51:28 -0400 Subject: [PATCH 3647/4033] workflow wip --- .github/workflows/test.yaml | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 000000000..461671920 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,59 @@ +name: Tests +on: [push] + +jobs: + # runtime tests + runtime-test: + name: Runtime Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: DeLaGuardo/setup-clojure@2.0 + with: + tools-deps: '1.10.1.536' + + - name: Cache maven + uses: actions/cache@v1 + env: + cache-name: cache-maven + with: + path: ~/.m2 + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Cache gitlibs + uses: actions/cache@v1 + env: + cache-name: cache-gitlibs + with: + path: ~/.gitlibs + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Cache node modules + uses: actions/cache@v1 + env: + cache-name: cache-nodemodules + with: + path: ~/node_modules + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Build tests + run: clj -A:runtime.test.build + + - name: Install V8 + run: wget https://github.com/blazerod/v8-prebuilt/releases/download/v8.0.426.8/v8-v8.0.426.8-linux.tar.gz; tar -xvzf v8-v8.0.426.8-linux.tar.gz v8 + + - name: Run tests + run: v8/bin/d8 builds/out-adv/core-advanced-test.js From 9d5cd85816ed821ec00cfeba7a2873c2c0c96f5b Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 14:52:28 -0400 Subject: [PATCH 3648/4033] clj -> clojure --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 461671920..805a241c3 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -50,7 +50,7 @@ jobs: ${{ runner.os }}- - name: Build tests - run: clj -A:runtime.test.build + run: clojure -A:runtime.test.build - name: Install V8 run: wget https://github.com/blazerod/v8-prebuilt/releases/download/v8.0.426.8/v8-v8.0.426.8-linux.tar.gz; tar -xvzf v8-v8.0.426.8-linux.tar.gz v8 From 0617f434ea4899f013929c806d12f608934ffa63 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 15:04:57 -0400 Subject: [PATCH 3649/4033] change v8 install --- .github/workflows/test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 805a241c3..7ea0ea9c9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -53,7 +53,7 @@ jobs: run: clojure -A:runtime.test.build - name: Install V8 - run: wget https://github.com/blazerod/v8-prebuilt/releases/download/v8.0.426.8/v8-v8.0.426.8-linux.tar.gz; tar -xvzf v8-v8.0.426.8-linux.tar.gz v8 + run: sudo apt-get install v8 - name: Run tests - run: v8/bin/d8 builds/out-adv/core-advanced-test.js + run: v8-v8.0.426.8-linux/bin/d8 builds/out-adv/core-advanced-test.js From b68182faa85f76943cf1c2240b1bbf9eab4c1c5e Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 15:09:34 -0400 Subject: [PATCH 3650/4033] v8 -> libv8 --- .github/workflows/test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7ea0ea9c9..48e671605 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -53,7 +53,7 @@ jobs: run: clojure -A:runtime.test.build - name: Install V8 - run: sudo apt-get install v8 + run: sudo apt-get install libv8 - name: Run tests - run: v8-v8.0.426.8-linux/bin/d8 builds/out-adv/core-advanced-test.js + run: d8 builds/out-adv/core-advanced-test.js From a2cff5192939a954f47f9df035e93e72146e0148 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 15:14:16 -0400 Subject: [PATCH 3651/4033] another try --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 48e671605..2519c6785 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -53,7 +53,7 @@ jobs: run: clojure -A:runtime.test.build - name: Install V8 - run: sudo apt-get install libv8 + run: sudo apt-get install -y libv8-dev - name: Run tests run: d8 builds/out-adv/core-advanced-test.js From 42b279d21a9f11687861ff4f5d07df98afee37bd Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 15:55:19 -0400 Subject: [PATCH 3652/4033] try JSC, will look into caching later --- .github/workflows/test.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2519c6785..4c05edc89 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -52,8 +52,8 @@ jobs: - name: Build tests run: clojure -A:runtime.test.build - - name: Install V8 - run: sudo apt-get install -y libv8-dev + - name: Install JSC + run: git clone --depth=1 git://git.webkit.org/WebKit.git WebKit; cd WebKit; Tools/Scripts/build-jsc --jsc-only; cd .. - name: Run tests - run: d8 builds/out-adv/core-advanced-test.js + run: WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js From d1aa9ccc863e27ebbb149fa15f90883b3da56e00 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 16:30:11 -0400 Subject: [PATCH 3653/4033] remove node_modules caching, cleanup caching, try to cache JSC --- .github/workflows/test.yaml | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4c05edc89..39f7568ec 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -14,41 +14,35 @@ jobs: tools-deps: '1.10.1.536' - name: Cache maven - uses: actions/cache@v1 + uses: actions/cache@v2 env: cache-name: cache-maven with: path: ~/.m2 - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + ${{ runner.os }}-${{ env.cache-name }}- - name: Cache gitlibs - uses: actions/cache@v1 + uses: actions/cache@v2 env: cache-name: cache-gitlibs with: path: ~/.gitlibs - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - - name: Cache node modules - uses: actions/cache@v1 + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Cache JSC + uses: actions/cache@v2 env: - cache-name: cache-nodemodules + cache-name: cache-jsc with: - path: ~/node_modules - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }} + path: WebKit + key: ${{ runner.os }}-jsc restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - + ${{ runner.os }}-jsc + - name: Build tests run: clojure -A:runtime.test.build From 7290329a4cc7efbff97e62741bfda1d434806f3c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 16:40:18 -0400 Subject: [PATCH 3654/4033] add runtime tests --- .github/workflows/test.yaml | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 39f7568ec..db7fbcd07 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -2,7 +2,7 @@ name: Tests on: [push] jobs: - # runtime tests + # Runtime Tests runtime-test: name: Runtime Tests runs-on: ubuntu-latest @@ -51,3 +51,37 @@ jobs: - name: Run tests run: WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js + + # Compiler Tests + compiler-test: + name: Compiler Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: DeLaGuardo/setup-clojure@2.0 + with: + tools-deps: '1.10.1.536' + + - name: Cache maven + uses: actions/cache@v2 + env: + cache-name: cache-maven + with: + path: ~/.m2 + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Cache gitlibs + uses: actions/cache@v2 + env: + cache-name: cache-gitlibs + with: + path: ~/.gitlibs + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Run tests + run: clj -A:compiler.test:compiler.test.run From 066319c103f24b6c8d464fc6c0c345c46df1d6f2 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 16:41:50 -0400 Subject: [PATCH 3655/4033] clj -> clojure --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index db7fbcd07..c6164df3c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -84,4 +84,4 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Run tests - run: clj -A:compiler.test:compiler.test.run + run: clojure -A:compiler.test:compiler.test.run From 45e29f555afd319cc44507b4eef3805d3629126c Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 17:40:46 -0400 Subject: [PATCH 3656/4033] add a test runner ns --- ...sing_test.clj => externs_parsing_tests.clj} | 0 src/test/clojure/cljs/test_runner.clj | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+) rename src/test/clojure/cljs/{externs_parsing_test.clj => externs_parsing_tests.clj} (100%) create mode 100644 src/test/clojure/cljs/test_runner.clj diff --git a/src/test/clojure/cljs/externs_parsing_test.clj b/src/test/clojure/cljs/externs_parsing_tests.clj similarity index 100% rename from src/test/clojure/cljs/externs_parsing_test.clj rename to src/test/clojure/cljs/externs_parsing_tests.clj diff --git a/src/test/clojure/cljs/test_runner.clj b/src/test/clojure/cljs/test_runner.clj new file mode 100644 index 000000000..1e59c2438 --- /dev/null +++ b/src/test/clojure/cljs/test_runner.clj @@ -0,0 +1,18 @@ +(ns cljs.test-runner + (:require [cljs.source-map.base64-tests] + [cljs.analyzer-api-tests] + [cljs.analyzer-tests] + [cljs.build-api-tests] + [cljs.closure-tests] + [cljs.compiler-tests] + [cljs.externs-infer-tests] + [cljs.externs-parsing-tests] + [cljs.module-graph-tests] + [cljs.module-processing-tests] + [cljs.module-graph-tests] + [cljs.module-processing-tests] + [cljs.type-inference-tests] + [cljs.util-tests] + [clojure.test :refer [run-all-tests]])) + +(run-all-tests) From b5f1860daa9e467e4e65e5d2eb9cfe2b27dc6fcf Mon Sep 17 00:00:00 2001 From: dnolen Date: Sat, 27 Jun 2020 17:47:24 -0400 Subject: [PATCH 3657/4033] remove cognitect test-runner, just do this manually to remove all variables --- deps.edn | 2 +- src/test/clojure/cljs/test_runner.clj | 28 +++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/deps.edn b/deps.edn index 65fa4236b..08e6f08aa 100644 --- a/deps.edn +++ b/deps.edn @@ -16,4 +16,4 @@ "src/test/clojure" "src/test/self"] :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" :sha "f7ef16dc3b8332b0d77bc0274578ad5270fbfedd"}}} - :compiler.test.run {:main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure" "-r" ".*-tests$"]}}} + :compiler.test.run {:main-opts ["-m" "cljs.test-runner"]}}} diff --git a/src/test/clojure/cljs/test_runner.clj b/src/test/clojure/cljs/test_runner.clj index 1e59c2438..98715cc92 100644 --- a/src/test/clojure/cljs/test_runner.clj +++ b/src/test/clojure/cljs/test_runner.clj @@ -1,6 +1,5 @@ (ns cljs.test-runner - (:require [cljs.source-map.base64-tests] - [cljs.analyzer-api-tests] + (:require [cljs.analyzer-api-tests] [cljs.analyzer-tests] [cljs.build-api-tests] [cljs.closure-tests] @@ -9,10 +8,27 @@ [cljs.externs-parsing-tests] [cljs.module-graph-tests] [cljs.module-processing-tests] - [cljs.module-graph-tests] - [cljs.module-processing-tests] + [cljs.source-map.base64-tests] [cljs.type-inference-tests] [cljs.util-tests] - [clojure.test :refer [run-all-tests]])) + [clojure.test :refer [run-tests]])) -(run-all-tests) +(defn -main [] + (let [{:keys [fail error]} + (run-tests + 'cljs.analyzer-api-tests + 'cljs.analyzer-tests + 'cljs.build-api-tests + 'cljs.closure-tests + 'cljs.compiler-tests + 'cljs.externs-infer-tests + 'cljs.externs-parsing-tests + 'cljs.module-graph-tests + 'cljs.module-processing-tests + 'cljs.source-map.base64-tests + 'cljs.type-inference-tests + 'cljs.util-tests)] + (if (or (not (zero? fail)) + (not (zero? error))) + (System/exit 1) + (System/exit 0)))) From 2dbc98548bf4a8d10b43ce62dc6a4237bd123e48 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Jun 2020 08:45:25 -0400 Subject: [PATCH 3658/4033] change the way the we were running the tests for data literals. It appears there's a different class loader when using the REPL - w/o that loader the data literal tests fail due to record class not being found. instead of going through -main to kick off the tests go through an interactive path where Clojure REPL bits will probably get loaded. --- deps.edn | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/deps.edn b/deps.edn index 08e6f08aa..1aa6c6350 100644 --- a/deps.edn +++ b/deps.edn @@ -1,6 +1,6 @@ {:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps - {org.clojure/clojure {:mvn/version "1.9.0"} + {org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/tools.reader {:mvn/version "1.3.2"} org.clojure/test.check {:mvn/version "0.10.0-alpha3"} org.clojure/spec.alpha {:mvn/version "0.1.143"} @@ -13,7 +13,6 @@ {:runtime.test.build {:extra-paths ["src/test/cljs"] :main-opts ["-m" "cljs.main" "-co" "resources/test.edn" "-c"]} :compiler.test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" - "src/test/clojure" "src/test/self"] - :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git" - :sha "f7ef16dc3b8332b0d77bc0274578ad5270fbfedd"}}} - :compiler.test.run {:main-opts ["-m" "cljs.test-runner"]}}} + "src/test/clojure" "src/test/self"]} + :compiler.test.run {:main-opts ["-i" "src/test/clojure/cljs/test_runner.clj" + "-e" "(cljs.test-runner/-main)"]}}} From ffb81219a8c5164215587850df716c6ccabb3ae3 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Jun 2020 08:48:24 -0400 Subject: [PATCH 3659/4033] don't rebuild WebKit if it's been cached --- .github/workflows/test.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c6164df3c..229d7678c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - + - uses: DeLaGuardo/setup-clojure@2.0 with: tools-deps: '1.10.1.536' @@ -32,7 +32,7 @@ jobs: key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - + - name: Cache JSC uses: actions/cache@v2 env: @@ -47,10 +47,10 @@ jobs: run: clojure -A:runtime.test.build - name: Install JSC - run: git clone --depth=1 git://git.webkit.org/WebKit.git WebKit; cd WebKit; Tools/Scripts/build-jsc --jsc-only; cd .. + run: [[ -d WebKit ]] || { git clone --depth=1 git://git.webkit.org/WebKit.git WebKit; cd WebKit; Tools/Scripts/build-jsc --jsc-only; cd .. } - name: Run tests - run: WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js + run: WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js # Compiler Tests compiler-test: @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - + - uses: DeLaGuardo/setup-clojure@2.0 with: tools-deps: '1.10.1.536' From a49460a0c1eaf459ad0751f1302698a86fc8129a Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Jun 2020 08:57:02 -0400 Subject: [PATCH 3660/4033] switch to a script --- .github/workflows/test.yaml | 2 +- ci/install_jsc.sh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 ci/install_jsc.sh diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 229d7678c..00e07f8d8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -47,7 +47,7 @@ jobs: run: clojure -A:runtime.test.build - name: Install JSC - run: [[ -d WebKit ]] || { git clone --depth=1 git://git.webkit.org/WebKit.git WebKit; cd WebKit; Tools/Scripts/build-jsc --jsc-only; cd .. } + run: ./ci/install_jsc.sh - name: Run tests run: WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js diff --git a/ci/install_jsc.sh b/ci/install_jsc.sh new file mode 100644 index 000000000..4026a0cef --- /dev/null +++ b/ci/install_jsc.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +[[ -d WebKit ]] || { git clone --depth=1 git://git.webkit.org/WebKit.git WebKit; cd WebKit; Tools/Scripts/build-jsc --jsc-only; cd .. } From 081ee4cc658ee9a53c2398ce0074992b8aca5ef8 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Jun 2020 09:03:52 -0400 Subject: [PATCH 3661/4033] ci script permissions --- ci/install_jsc.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ci/install_jsc.sh diff --git a/ci/install_jsc.sh b/ci/install_jsc.sh old mode 100644 new mode 100755 From d25507d90032b97755bedee0203a530fa76979cc Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Jun 2020 09:19:44 -0400 Subject: [PATCH 3662/4033] fix jsc install script --- ci/install_jsc.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ci/install_jsc.sh b/ci/install_jsc.sh index 4026a0cef..0313c7b83 100755 --- a/ci/install_jsc.sh +++ b/ci/install_jsc.sh @@ -1,2 +1,8 @@ #!/usr/bin/env bash -[[ -d WebKit ]] || { git clone --depth=1 git://git.webkit.org/WebKit.git WebKit; cd WebKit; Tools/Scripts/build-jsc --jsc-only; cd .. } +if [ ! -d WebKit ] +then + git clone --depth=1 git://git.webkit.org/WebKit.git WebKit; + cd WebKit; + Tools/Scripts/build-jsc --jsc-only; + cd .. +fi From f99cbbf2644ca128b6e51eb8a0c1cf0c9cd3f778 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 28 Jun 2020 09:23:10 -0400 Subject: [PATCH 3663/4033] fix ns --- src/test/clojure/cljs/externs_parsing_tests.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index 0995e1638..cc6bd0136 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -6,7 +6,7 @@ ;; the terms of this license. ;; You must not remove this notice, or any other, from this software. -(ns cljs.externs-parsing-test +(ns cljs.externs-parsing-tests (:require [cljs.closure :as closure] [cljs.externs :as externs] [clojure.java.io :as io] From df80b65cbefa2914b2e0ab8dc62459b492379596 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Jun 2020 13:05:19 -0400 Subject: [PATCH 3664/4033] test self-host in CI --- .github/workflows/test.yaml | 37 ++++++++++++++++++++++++++++++++++++ deps.edn | 4 +++- resources/self_host_test.edn | 10 ++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 resources/self_host_test.edn diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 00e07f8d8..a989bdafe 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -52,6 +52,43 @@ jobs: - name: Run tests run: WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js + # Self-host Tests + self-host-test: + name: Self-host Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: DeLaGuardo/setup-clojure@2.0 + with: + tools-deps: '1.10.1.536' + + - name: Cache maven + uses: actions/cache@v2 + env: + cache-name: cache-maven + with: + path: ~/.m2 + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Cache gitlibs + uses: actions/cache@v2 + env: + cache-name: cache-gitlibs + with: + path: ~/.gitlibs + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Build tests + run: clojure -A:selfhost.test.build + + - name: Run tests + run: node builds/out-self/core-self-test.js + # Compiler Tests compiler-test: name: Compiler Tests diff --git a/deps.edn b/deps.edn index 1aa6c6350..00ed8a8bb 100644 --- a/deps.edn +++ b/deps.edn @@ -10,7 +10,9 @@ com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200315"} org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"}} :aliases - {:runtime.test.build {:extra-paths ["src/test/cljs"] + {:selfhost.test.build {:extra-paths ["src/test/self"] + :main-opts ["-m" "cljs.main" "-co" "resources/self_host_test.edn" "-c"]} + :runtime.test.build {:extra-paths ["src/test/cljs"] :main-opts ["-m" "cljs.main" "-co" "resources/test.edn" "-c"]} :compiler.test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" "src/test/clojure" "src/test/self"]} diff --git a/resources/self_host_test.edn b/resources/self_host_test.edn new file mode 100644 index 000000000..abbfc16af --- /dev/null +++ b/resources/self_host_test.edn @@ -0,0 +1,10 @@ +{:optimizations :simple + :main self-host.test + :static-fns true + :output-to "builds/out-self/core-self-test.js" + :output-dir "builds/out-self" + :optimize-constants true + :verbose true + :compiler-stats true + :parallel-build true + :target :nodejs} \ No newline at end of file From 6ed209cf133525ab1e73f7788cc765d5137f487c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Jun 2020 13:17:54 -0400 Subject: [PATCH 3665/4033] formatting --- deps.edn | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/deps.edn b/deps.edn index 00ed8a8bb..ab381c5ce 100644 --- a/deps.edn +++ b/deps.edn @@ -1,20 +1,20 @@ {:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps - {org.clojure/clojure {:mvn/version "1.10.0"} - org.clojure/tools.reader {:mvn/version "1.3.2"} - org.clojure/test.check {:mvn/version "0.10.0-alpha3"} - org.clojure/spec.alpha {:mvn/version "0.1.143"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200315"} + com.cognitect/transit-clj {:mvn/version "0.8.309"} + org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} - com.cognitect/transit-clj {:mvn/version "0.8.309"} - com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200315"} - org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"}} + org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"} + org.clojure/spec.alpha {:mvn/version "0.1.143"} + org.clojure/tools.reader {:mvn/version "1.3.2"} + org.clojure/test.check {:mvn/version "0.10.0-alpha3"}} :aliases - {:selfhost.test.build {:extra-paths ["src/test/self"] - :main-opts ["-m" "cljs.main" "-co" "resources/self_host_test.edn" "-c"]} - :runtime.test.build {:extra-paths ["src/test/cljs"] - :main-opts ["-m" "cljs.main" "-co" "resources/test.edn" "-c"]} - :compiler.test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" - "src/test/clojure" "src/test/self"]} - :compiler.test.run {:main-opts ["-i" "src/test/clojure/cljs/test_runner.clj" - "-e" "(cljs.test-runner/-main)"]}}} + {:compiler.test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" + "src/test/clojure" "src/test/self"]} + :compiler.test.run {:main-opts ["-i" "src/test/clojure/cljs/test_runner.clj" + "-e" "(cljs.test-runner/-main)"]} + :runtime.test.build {:extra-paths ["src/test/cljs"] + :main-opts ["-m" "cljs.main" "-co" "resources/test.edn" "-c"]} + :selfhost.test.build {:extra-paths ["src/test/self"] + :main-opts ["-m" "cljs.main" "-co" "resources/self_host_test.edn" "-c"]}}} From 0982e17e6a6174325fb1f4be905e73788a2665bd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Jun 2020 13:38:24 -0400 Subject: [PATCH 3666/4033] CLI tests --- .github/workflows/test.yaml | 37 +++++++++++++++++++++++++++++++++++++ deps.edn | 5 ++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a989bdafe..3866398f8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -122,3 +122,40 @@ jobs: - name: Run tests run: clojure -A:compiler.test:compiler.test.run + + # CLI Tests + cli-test: + name: CLI Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: DeLaGuardo/setup-clojure@2.0 + with: + tools-deps: '1.10.1.536' + + - name: Cache maven + uses: actions/cache@v2 + env: + cache-name: cache-maven + with: + path: ~/.m2 + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Cache gitlibs + uses: actions/cache@v2 + env: + cache-name: cache-gitlibs + with: + path: ~/.gitlibs + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Build Uberjar + - run: ./script/uberjar + + - name: Run tests + run: clojure -A:cli.test.run diff --git a/deps.edn b/deps.edn index ab381c5ce..1726ec04d 100644 --- a/deps.edn +++ b/deps.edn @@ -10,7 +10,10 @@ org.clojure/tools.reader {:mvn/version "1.3.2"} org.clojure/test.check {:mvn/version "0.10.0-alpha3"}} :aliases - {:compiler.test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp" + {: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" "src/test/clojure" "src/test/self"]} :compiler.test.run {:main-opts ["-i" "src/test/clojure/cljs/test_runner.clj" "-e" "(cljs.test-runner/-main)"]} From 2ed8fad7c125754f48c57a255d9e6cd70f5af474 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Jun 2020 13:39:44 -0400 Subject: [PATCH 3667/4033] typo in cli test --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3866398f8..a5012a0b7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -155,7 +155,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Build Uberjar - - run: ./script/uberjar + run: ./script/uberjar - name: Run tests run: clojure -A:cli.test.run From 60578742e61b0fe37f119082873759ad29ac4b51 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Jun 2020 13:46:27 -0400 Subject: [PATCH 3668/4033] fetch all history --- .github/workflows/test.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a5012a0b7..e48594383 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -8,6 +8,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - uses: DeLaGuardo/setup-clojure@2.0 with: From a19f8e04dc0971f13316a8dd584156e2ffb67b96 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Jun 2020 13:48:47 -0400 Subject: [PATCH 3669/4033] fetch all in history in the right job --- .github/workflows/test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e48594383..6c56c1ead 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -8,8 +8,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - with: - fetch-depth: 0 - uses: DeLaGuardo/setup-clojure@2.0 with: @@ -131,6 +129,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - uses: DeLaGuardo/setup-clojure@2.0 with: From 6691bae122a2159e04fcc2fbe56d14d8f6639a63 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 4 Jul 2020 08:54:00 -0400 Subject: [PATCH 3670/4033] CLJS-3262: Add self-parity tests to GitHub actions --- .github/workflows/test.yaml | 37 +++++++++++++++++++ deps.edn | 6 ++- resources/self_parity_test.edn | 6 +++ src/test/self/self_parity/auxiliary.cljs | 3 +- src/test/self/self_parity/setup.clj | 47 ++++++++++++++++++++++++ src/test/self/self_parity/test.cljs | 3 +- 6 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 resources/self_parity_test.edn create mode 100644 src/test/self/self_parity/setup.clj diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6c56c1ead..ed0d554bb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -89,6 +89,43 @@ jobs: - name: Run tests run: node builds/out-self/core-self-test.js + # Self-parity Tests + self-parity-test: + name: Self-parity Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: DeLaGuardo/setup-clojure@2.0 + with: + tools-deps: '1.10.1.536' + + - name: Cache maven + uses: actions/cache@v2 + env: + cache-name: cache-maven + with: + path: ~/.m2 + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Cache gitlibs + uses: actions/cache@v2 + env: + cache-name: cache-gitlibs + with: + path: ~/.gitlibs + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Build tests + run: clojure -A:selfparity.test.build + + - name: Run tests + run: node builds/out-self-parity/main.js + # Compiler Tests compiler-test: name: Compiler Tests diff --git a/deps.edn b/deps.edn index 1726ec04d..be2ed2a36 100644 --- a/deps.edn +++ b/deps.edn @@ -20,4 +20,8 @@ :runtime.test.build {:extra-paths ["src/test/cljs"] :main-opts ["-m" "cljs.main" "-co" "resources/test.edn" "-c"]} :selfhost.test.build {:extra-paths ["src/test/self"] - :main-opts ["-m" "cljs.main" "-co" "resources/self_host_test.edn" "-c"]}}} + :main-opts ["-m" "cljs.main" "-co" "resources/self_host_test.edn" "-c"]} + :selfparity.test.build {:extra-paths ["src/test/self"] + :main-opts ["-i" "src/test/self/self_parity/setup.clj" + "-e" "(self-parity.setup/-main)" + "-m" "cljs.main" "-co" "resources/self_parity_test.edn" "-c"]}}} diff --git a/resources/self_parity_test.edn b/resources/self_parity_test.edn new file mode 100644 index 000000000..db12d0605 --- /dev/null +++ b/resources/self_parity_test.edn @@ -0,0 +1,6 @@ +{:optimizations :none + :main self-parity.test + :output-to "builds/out-self-parity/main.js" + :output-dir "builds/out-self-parity" + :cache-analysis-format :edn + :target :nodejs} diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs index 3d0f69392..4ee2181f1 100644 --- a/src/test/self/self_parity/auxiliary.cljs +++ b/src/test/self/self_parity/auxiliary.cljs @@ -6,8 +6,7 @@ ;; the terms of this license. ;; You must not remove this notice, or any other, from this software. -(ns ^{:doc "This auxiliary namespace is not actually loaded. - Its mere presence cause it to be compiled and thus causes +(ns ^{:doc "This auxiliary namespace only exists to cause the libs listed here to be dumped into the compiler output directory where they can be loaded on demand when running the compiler tests in bootstrap mode."} diff --git a/src/test/self/self_parity/setup.clj b/src/test/self/self_parity/setup.clj new file mode 100644 index 000000000..b29c1ed10 --- /dev/null +++ b/src/test/self/self_parity/setup.clj @@ -0,0 +1,47 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns self-parity.setup + ^{:doc "Sets up the filesystem, priming the output directory + with needed source files so that the self-hosted compiler + being executed within Node has various dependency sources + available (without the benefit of being able to load resources + from a classpath)."} + (:require + [clojure.java.io :as io])) + +(def out-path (io/file "builds" "out-self-parity")) + +(defn copy-source + [source-resource-name] + (let [target-file (io/file out-path source-resource-name)] + (io/make-parents target-file) + (io/copy (io/input-stream (io/resource source-resource-name)) target-file))) + +(def test-check-source-resource-names + ["clojure/test/check.cljc" + "clojure/test/check/random.clj" + "clojure/test/check/random.cljs" + "clojure/test/check/rose_tree.cljc" + "clojure/test/check/clojure_test.cljc" + "clojure/test/check/clojure_test/assertions.cljc" + "clojure/test/check/clojure_test/assertions/cljs.cljc" + "clojure/test/check/results.cljc" + "clojure/test/check/impl.cljc" + "clojure/test/check/properties.cljc" + "clojure/test/check/random/longs.cljs" + "clojure/test/check/random/doubles.cljs" + "clojure/test/check/random/longs/bit_count_impl.cljs" + "clojure/test/check/generators.cljc"]) + +(def source-resource-names + (into ["clojure/template.clj"] + test-check-source-resource-names)) + +(defn -main [] + (run! copy-source source-resource-names)) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 7765e5d33..3a6cfcb52 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -21,7 +21,8 @@ [cljs.js :as cljs] [cljs.tools.reader :as reader] [cljs.stacktrace :as st] - [goog.object :as gobj])) + [goog.object :as gobj] + [self-parity.auxiliary])) (def out-dir "builds/out-self-parity") From b7895aee30efc00ef1b572fc0fae73d0a2d6fac5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 8 Jul 2020 17:55:46 -0400 Subject: [PATCH 3671/4033] CLJS-3259: revert public api change made in 45022fa Restore checking for a bound compiler state. --- src/main/clojure/cljs/analyzer/api.cljc | 6 ++-- src/main/clojure/cljs/build/api.clj | 37 +++++++++++++++---------- src/main/clojure/cljs/compiler/api.clj | 10 +++---- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index c9251fe87..29eb384f7 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -134,7 +134,7 @@ ([env form] (analyze env form nil)) ([env form name] (analyze env form name nil)) ([env form name opts] - (analyze (empty-state opts) env form name opts)) + (analyze (or (current-state) (empty-state opts)) env form name opts)) ([state env form name opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -161,7 +161,7 @@ ([src] (parse-ns src nil nil)) ([src opts] (parse-ns src nil opts)) ([src dest opts] - (parse-ns (empty-state opts) src dest opts)) + (parse-ns (or (current-state) (empty-state opts)) src dest opts)) ([state src dest opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -178,7 +178,7 @@ meaningful value." ([f] (analyze-file f nil)) ([f opts] - (analyze-file (empty-state opts) f opts)) + (analyze-file (or (current-state) (empty-state opts)) f opts)) ([state f opts] (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] diff --git a/src/main/clojure/cljs/build/api.clj b/src/main/clojure/cljs/build/api.clj index 2ae9657fc..43f451c18 100644 --- a/src/main/clojure/cljs/build/api.clj +++ b/src/main/clojure/cljs/build/api.clj @@ -52,7 +52,7 @@ ('example.core 'example.util)" ([namespaces] (closure/cljs-dependents-for-macro-namespaces - (ana-api/empty-state) namespaces)) + (or (ana-api/current-state) (ana-api/empty-state)) namespaces)) ([state namespaces] (closure/cljs-dependents-for-macro-namespaces state namespaces))) @@ -68,7 +68,8 @@ provide build options with :output-dir specified." ([src] (src-file->target-file src nil)) ([src opts] - (src-file->target-file (ana-api/empty-state opts) src opts)) + (src-file->target-file + (or (ana-api/current-state) (ana-api/empty-state opts)) src opts)) ([state src opts] (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -79,7 +80,8 @@ the goog.require statement for it." ([src] (src-file->goog-require src nil)) ([src opts] - (src-file->goog-require (ana-api/empty-state opts) src opts)) + (src-file->goog-require + (or (ana-api/current-state) (ana-api/empty-state opts)) src opts)) ([state src opts] (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -121,7 +123,7 @@ .cljs, .cljc, .js. Returns a map containing :relative-path a string, and :uri a URL." ([ns] - (ns->location ns (ana-api/empty-state))) + (ns->location ns (or (ana-api/current-state) (ana-api/empty-state)))) ([ns compiler-env] (closure/source-for-namespace ns compiler-env))) @@ -139,7 +141,7 @@ ([xs] (add-dependency-sources xs {})) ([xs opts] - (add-dependency-sources (ana-api/empty-state opts) xs opts)) + (add-dependency-sources (or (ana-api/current-state) (ana-api/empty-state opts)) xs opts)) ([state xs opts] (ana-api/with-state state (closure/add-dependency-sources xs opts)))) @@ -192,7 +194,7 @@ (defn compile "Given a Compilable, compile it and return an IJavaScript." ([opts compilable] - (compile (ana-api/empty-state opts) opts compilable)) + (compile (or (ana-api/current-state) (ana-api/empty-state opts)) opts compilable)) ([state opts compilable] (ana-api/with-state state (closure/compile compilable opts)))) @@ -214,11 +216,13 @@ (build nil opts)) ([source opts] (build source opts - (ana-api/empty-state - ;; need to dissoc :foreign-libs since we won't know what overriding - ;; foreign libspecs are referring to until after add-implicit-options - ;; - David - (closure/add-externs-sources (dissoc opts :foreign-libs))))) + (or + (ana-api/current-state) + (ana-api/empty-state + ;; need to dissoc :foreign-libs since we won't know what overriding + ;; foreign libspecs are referring to until after add-implicit-options + ;; - David + (closure/add-externs-sources (dissoc opts :foreign-libs)))))) ([source opts compiler-env] (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) closure/known-opts)] (when suggested-opt @@ -230,8 +234,9 @@ "Given a source which can be compiled, watch it for changes to produce." ([source opts] (watch source opts - (ana-api/empty-state - (closure/add-externs-sources opts)))) + (or (ana-api/current-state) + (ana-api/empty-state + (closure/add-externs-sources opts))))) ([source opts compiler-env] (watch source opts compiler-env nil)) ([source opts compiler-env stop] @@ -291,12 +296,14 @@ installed." ([entries] (node-inputs entries - (:options (ana-api/empty-state)))) + (:options (or (ana-api/current-state) (ana-api/empty-state))))) ([entries opts] (closure/node-inputs entries opts))) (defn node-modules "Return a sequence of requirable libraries found under node_modules." + ([] + (node-modules {})) ([opts] - (ana-api/with-state (ana-api/empty-state opts) + (ana-api/with-state (or (ana-api/current-state) (ana-api/empty-state opts)) (filter :provides (closure/index-node-modules-dir))))) diff --git a/src/main/clojure/cljs/compiler/api.clj b/src/main/clojure/cljs/compiler/api.clj index a197e0a9b..b268ac6ed 100644 --- a/src/main/clojure/cljs/compiler/api.clj +++ b/src/main/clojure/cljs/compiler/api.clj @@ -26,7 +26,7 @@ (defn emit "Given an AST node generated by the analyzer emit JavaScript as a string." ([ast] - (emit (ana-api/empty-state) ast)) + (emit (or (ana-api/current-state) (ana-api/empty-state)) ast)) ([state ast] (ana-api/with-state state (with-out-str @@ -40,7 +40,7 @@ (:options @state)))) ([opts] (with-core-cljs opts (fn []))) ([opts body] - (with-core-cljs (ana-api/empty-state opts) opts body)) + (with-core-cljs (or (ana-api/current-state) (ana-api/empty-state opts)) opts body)) ([state opts body] (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -50,7 +50,7 @@ "Return true if the src file requires compilation." ([src dest] (requires-compilation? src dest nil)) ([src dest opts] - (requires-compilation? (ana-api/empty-state opts) src dest opts)) + (requires-compilation? (or (ana-api/current-state)(ana-api/empty-state opts)) src dest opts)) ([state src dest opts] (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -74,7 +74,7 @@ ([src dest] (compile-file src dest nil)) ([src dest opts] - (compile-file (ana-api/empty-state opts) src dest opts)) + (compile-file (or (ana-api/current-state) (ana-api/empty-state opts)) src dest opts)) ([state src dest opts] (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] @@ -94,7 +94,7 @@ ([src-dir] (compile-root src-dir "out")) ([src-dir target-dir] (compile-root src-dir target-dir nil)) ([src-dir target-dir opts] - (compile-root (ana-api/empty-state opts) src-dir target-dir opts)) + (compile-root (or (ana-api/current-state) (ana-api/empty-state opts)) src-dir target-dir opts)) ([state src-dir target-dir opts] (ana-api/with-state state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] From 0ea117bd3cc332ec430fde94da505d6cf6cdbf4b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 8 Jul 2020 19:24:54 -0400 Subject: [PATCH 3672/4033] CLJS-3255: cljs.build.api/build doesn't work with single arity / 2-arity with nil While actually supported make a small change so that `source` argument to cljs.build/build can actually be nil in all cases. If `:main` is supplied, `:optimizations` `:none`, and source nil, go ahead supply source as that will always be what the user intended. Note cljs.cli already fixed this problem some time ago but at a higher level. --- src/main/clojure/cljs/closure.clj | 6 +++++- src/test/clojure/cljs/build_api_tests.clj | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 12df2f611..16cc303d0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -3109,11 +3109,15 @@ (util/debug-prn "Options passed to ClojureScript compiler:" (pr-str opts))) (let [one-file? (and (:main opts) (#{:advanced :simple :whitespace} (:optimizations opts))) - source (if one-file? + source (if (or one-file? + ;; if source is nil, :main is supplied, :optimizations :none, + ;; fix up source for the user, see CLJS-3255 + (and (nil? source) (:main opts) (= :none (:optimizations opts)))) (let [main (:main opts) uri (:uri (cljs-source-for-namespace main))] (assert uri (str "No file for namespace " main " exists")) uri) + ;; old compile directory behavior, or code-splitting source) compile-opts (if one-file? (assoc opts :output-file (:output-to opts)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 99f1b7baa..5f4a70dd7 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -719,3 +719,15 @@ (test/delete-out-files out) (build/build (build/inputs (io/file inputs "trivial/core.cljs")) opts cenv) (is (< (.length out-file) 10000)))) + +(deftest cljs-3255-nil-inputs-build + (let [out (.getPath (io/file (test/tmp-dir) "3255-test-out")) + out-file (io/file out "main.js") + opts {:main 'trivial.core + :output-to (.getPath out-file) + :output-dir out + :language-in :es6 + :optimizations :none} + cenv (env/default-compiler-env)] + (test/delete-out-files out) + (build/build nil opts cenv))) From 0efe8fede9e06b8e1aa2fcb3a1c70f66cad6392e Mon Sep 17 00:00:00 2001 From: Hyunwoo Nam Date: Sun, 4 Nov 2018 23:38:07 +0100 Subject: [PATCH 3673/4033] CLJS-2959: sort and sort-by should retain meta --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 651e9ac47..b8d4a7953 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2457,7 +2457,7 @@ reduces them without incurring seq initialization" (let [a (to-array coll)] ;; matching Clojure's stable sort, though docs don't promise it (garray/stableSort a (fn->comparator comp)) - (seq a)) + (with-meta (seq a) (meta coll))) ()))) (defn sort-by diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 576f339b6..6223e5c26 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1738,6 +1738,10 @@ (is (= (symbol (->Var nil 'bar/foo nil)) 'bar/foo)) (is (thrown? js/Error (symbol 1)))) +(deftest test-cljs-2959 + (is (= {:a true} (meta (sort (with-meta (range 10) {:a true}))))) + (is (= {:a true} (meta (sort-by :a (with-meta (seq [{:a 5} {:a 2} {:a 3}]) {:a true})))))) + (deftest test-cljs-2991 (let [o (js-obj)] (is (object? o)) From e294c1c19ae212d1ec22c09f370d9f4e9d72b7a2 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 11 Jul 2020 13:40:30 -0400 Subject: [PATCH 3674/4033] CLJS-3264: Cause GitHub Actions-based tests to indicate failures --- .github/workflows/test.yaml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ed0d554bb..04165956a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -50,7 +50,9 @@ jobs: run: ./ci/install_jsc.sh - name: Run tests - run: WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js + run: | + WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js | tee test-out.txt + grep -qxF '0 failures, 0 errors.' test-out.txt # Self-host Tests self-host-test: @@ -87,7 +89,9 @@ jobs: run: clojure -A:selfhost.test.build - name: Run tests - run: node builds/out-self/core-self-test.js + run: | + node builds/out-self/core-self-test.js | tee test-out.txt + grep -qxF '0 failures, 0 errors.' test-out.txt # Self-parity Tests self-parity-test: @@ -124,7 +128,9 @@ jobs: run: clojure -A:selfparity.test.build - name: Run tests - run: node builds/out-self-parity/main.js + run: | + node builds/out-self-parity/main.js | tee test-out.txt + grep -qxF '0 failures, 0 errors.' test-out.txt # Compiler Tests compiler-test: @@ -197,4 +203,6 @@ jobs: run: ./script/uberjar - name: Run tests - run: clojure -A:cli.test.run + run: | + clojure -A:cli.test.run | tee test-out.txt + grep -qxF '0 failures, 0 errors.' test-out.txt From dc6ae8c4e1baf12fbb563a0eb13f93a9d4ae1103 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 28 Jun 2020 09:04:57 -0400 Subject: [PATCH 3675/4033] CLJS-3261: Docstring for cljs.js/eval-str specifies incorrect default for :context --- src/main/cljs/cljs/js.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 899215bd8..0cf1716ec 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -759,7 +759,7 @@ false. :context - optional, sets the context for the source. Possible values are `:expr`, `:statement` and `:return`. Defaults to - `:expr`. + `:statement`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -875,7 +875,7 @@ false. :context - optional, sets the context for the source. Possible values are `:expr`, `:statement` and `:return`. Defaults to - `:expr`. + `:statement`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -1002,7 +1002,7 @@ false. :context - optional, sets the context for the source. Possible values are `:expr`, `:statement` and `:return`. Defaults to - `:expr`. + `:statement`. cb (function) callback, will be invoked with a map. If successful the map will contain @@ -1174,7 +1174,7 @@ false. :context - optional, sets the context for the source. Possible values are `:expr`, `:statement` and `:return`. Defaults to - `:expr`. + `:statement`. cb (function) callback, will be invoked with a map. If succesful the map will contain From f5f9b79f6f446cef768e190971cf67fc78bf5b93 Mon Sep 17 00:00:00 2001 From: dnolen Date: Mon, 20 Jul 2020 15:20:00 -0400 Subject: [PATCH 3676/4033] CLJS-3235: Support accessing a property of a library as a namespace itself add lib&sublib helper to handle foo$bar change ana/node-module-dep? to handle foo$bar case change ana/analyze-deps :js-dependency case to handle foo$bar change handle-js-source so that we match node_modules against foo not foo$bar change emit-global-export to select bar from foo$bar change :node-js require case in load-libs to select bar from foo$bar ana/dep-has-global-exports? needs to handle sublib pattern ana/foreign? needs to handle sublib pattern comp/load-libs foreign lib case needs to handle sublib pattern, same for global export emission add test cases covering node and foreign lib require patterns --- src/main/clojure/cljs/analyzer.cljc | 52 ++++++++++++-------- src/main/clojure/cljs/closure.clj | 5 +- src/main/clojure/cljs/compiler.cljc | 60 ++++++++++++++--------- src/test/cljs_build/cljs_3235/core.cljs | 7 +++ src/test/cljs_build/cljs_3235/foreign.js | 10 ++++ src/test/clojure/cljs/build_api_tests.clj | 37 ++++++++++++++ 6 files changed, 126 insertions(+), 45 deletions(-) create mode 100644 src/test/cljs_build/cljs_3235/core.cljs create mode 100644 src/test/cljs_build/cljs_3235/foreign.js diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d46d6216f..2cd000e6c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -800,6 +800,14 @@ :suffix suffix :macro-present? (not (nil? (get-expander (symbol (str prefix) (str suffix)) env)))}))) +(defn lib&sublib + "If a library name has the form foo$bar, return a vector of the library and + the sublibrary property." + [lib] + (if-let [xs (re-matches #"(.*)\$(.*)" (str lib))] + (drop 1 xs) + [lib nil])) + (defn loaded-js-ns? "Check if a JavaScript namespace has been loaded. JavaScript vars are not currently checked." @@ -830,18 +838,20 @@ (defn node-module-dep? #?(:cljs {:tag boolean}) [module] - #?(:clj (contains? - (get-in @env/*compiler* [:node-module-index]) - (str module)) + #?(:clj (let [idx (get @env/*compiler* :node-module-index)] + (contains? idx (str (-> module lib&sublib first)))) :cljs (try (and (= *target* "nodejs") - (boolean (js/require.resolve (str module)))) + (boolean + (or (js/require.resolve (str module)) + (js/require.resolve (-> module lib&sublib first))))) (catch :default _ false)))) (defn dep-has-global-exports? [module] - (let [global-exports (get-in @env/*compiler* [:js-dependency-index (str module) :global-exports])] + (let [[module _] (lib&sublib module) + global-exports (get-in @env/*compiler* [:js-dependency-index (str module) :global-exports])] (or (contains? global-exports (symbol module)) (contains? global-exports (name module))))) @@ -2598,7 +2608,7 @@ #?(:cljs {:tag boolean}) [dep] (let [js-index (:js-dependency-index @env/*compiler*)] - (if-some [[_ {:keys [foreign]}] (find js-index (name dep))] + (if-some [[_ {:keys [foreign]}] (find js-index (name (-> dep lib&sublib first)))] foreign false))) @@ -2624,20 +2634,22 @@ (node-module-dep? dep) (js-module-exists? (name dep)) #?(:clj (deps/find-classpath-lib dep))) - (if (contains? (:js-dependency-index compiler) (name dep)) - (let [dep-name (name dep)] - (when (string/starts-with? dep-name "goog.") - #?(:clj (let [js-lib (get-in compiler [:js-dependency-index dep-name]) - ns (externs/analyze-goog-file (:file js-lib) (symbol dep-name))] - (swap! env/*compiler* update-in [::namespaces dep] merge ns))))) - #?(:clj (if-some [src (locate-src dep)] - (analyze-file src opts) - (throw - (error env - (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))) - :cljs (throw - (error env - (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)}))))))))))) + (let [idx (:js-dependency-index compiler) + dep (-> dep lib&sublib first)] + (if (contains? idx (name dep)) + (let [dep-name (name dep)] + (when (string/starts-with? dep-name "goog.") + #?(:clj (let [js-lib (get idx dep-name) + ns (externs/analyze-goog-file (:file js-lib) (symbol dep-name))] + (swap! env/*compiler* update-in [::namespaces dep] merge ns))))) + #?(:clj (if-some [src (locate-src dep)] + (analyze-file src opts) + (throw + (error env + (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))) + :cljs (throw + (error env + (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))))) (defn missing-use? [lib sym cenv] (let [js-lib (get-in cenv [:js-dependency-index (name lib)])] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 16cc303d0..bc18fa0b0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2937,7 +2937,10 @@ ;; if :npm-deps option is false, node_modules/ dir shouldn't be indexed (if (not (false? npm-deps)) (index-node-modules-dir))) - requires (set (mapcat deps/-requires js-sources)) + requires (->> (mapcat deps/-requires js-sources) + ;; fixup foo$default cases, foo is the lib, default is a property + (map #(-> % ana/lib&sublib first)) + set) ;; Select Node files that are required by Cljs code, ;; and create list of all their dependencies node-required (set/intersection (set (keys top-level)) requires) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 25d1da5be..d3e891292 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1251,18 +1251,28 @@ [{:keys [target val env]}] (emit-wrap env (emits "(" target " = " val ")"))) +(defn sublib-select + [sublib] + (when sublib + (let [xs (string/split sublib #"\.")] + (apply str + (map #(str "['" % "']") xs))))) + (defn emit-global-export [ns-name global-exports lib] - (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)) - ";")) + (let [[lib' sublib] (ana/lib&sublib lib)] + (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)) + (sublib-select sublib) + ";"))) (defn load-libs [libs seen reloads deps ns-name] @@ -1288,18 +1298,19 @@ ;; have handled it - David (when (and (= :none optimizations) (not (contains? options :modules))) - (if nodejs-rt - ;; under node.js we load foreign libs globally - (let [ijs (get js-dependency-index (name lib))] - (emitln "cljs.core.load_file(" - (-> (io/file (util/output-directory options) - (or (deps/-relative-path ijs) - (util/relative-name (:url ijs)))) + (let [[lib _] (ana/lib&sublib lib)] + (if nodejs-rt + ;; under node.js we load foreign libs globally + (let [ijs (get js-dependency-index (name lib))] + (emitln "cljs.core.load_file(" + (-> (io/file (util/output-directory options) + (or (deps/-relative-path ijs) + (util/relative-name (:url ijs)))) str escape-string wrap-in-double-quotes) - ");")) - (emitln "goog.require('" (munge lib) "');")))] + ");")) + (emitln "goog.require('" (munge lib) "');"))))] :cljs [(and (ana/foreign-dep? lib) (not (keyword-identical? optimizations :none))) @@ -1317,11 +1328,12 @@ (when-not (= lib 'goog) (emitln "goog.require('" (munge lib) "');")))) (doseq [lib node-libs] - (emitln (munge ns-name) "." - (ana/munge-node-lib lib) - " = require('" lib "');")) + (let [[lib' sublib] (ana/lib&sublib lib)] + (emitln (munge ns-name) "." + (ana/munge-node-lib lib) + " = require('" lib' "')" (sublib-select sublib) ";"))) (doseq [lib global-exports-libs] - (let [{:keys [global-exports]} (get js-dependency-index (name lib))] + (let [{:keys [global-exports]} (get js-dependency-index (name (-> lib ana/lib&sublib first)))] (emit-global-export ns-name global-exports lib))) (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");")))) diff --git a/src/test/cljs_build/cljs_3235/core.cljs b/src/test/cljs_build/cljs_3235/core.cljs new file mode 100644 index 000000000..70693e450 --- /dev/null +++ b/src/test/cljs_build/cljs_3235/core.cljs @@ -0,0 +1,7 @@ +(ns cljs-3235.core + (:require [some-foreign :refer [woz]] + [some-foreign$woz :as sf-woz] + [some-foreign$foz.boz :as sf-foz-boz] + [react-select :refer [foo bar]] + [react-select$default :as select] + [react-select$default.baz :as select-baz])) diff --git a/src/test/cljs_build/cljs_3235/foreign.js b/src/test/cljs_build/cljs_3235/foreign.js new file mode 100644 index 000000000..8b30ebcb3 --- /dev/null +++ b/src/test/cljs_build/cljs_3235/foreign.js @@ -0,0 +1,10 @@ +window.globalLib = { + woz: function() { + + }, + foz: { + boz: function() { + + } + } +}; diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 5f4a70dd7..df91fcefe 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -731,3 +731,40 @@ cenv (env/default-compiler-env)] (test/delete-out-files out) (build/build nil opts cenv))) + +(deftest test-cljs-3235 + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (testing "Test various require patterns for Node and foreign libraries" + (let [ws (atom []) + out (.getPath (io/file (test/tmp-dir) "cljs-3235-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'cljs-3235.core + :output-dir out + :optimizations :none + :target :nodejs + :install-deps true + :npm-deps {:react "15.6.1" + :react-dom "15.6.1" + :react-select "3.1.0"} + :foreign-libs [{:file (.getPath (io/file "src" "test" "cljs_build" "cljs_3235" "foreign.js")) + :provides ["some-foreign"] + :global-exports '{some-foreign globalLib}}] + :closure-warnings {:check-types :off + :non-standard-jsdoc :off}}} + cenv (env/default-compiler-env opts)] + (test/delete-out-files out) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (build/build (build/inputs (io/file inputs "cljs_3235/core.cljs")) opts cenv)) + (is (.exists (io/file out "cljs_3235/core.js"))) + (is (true? (boolean (re-find #"cljs_3235\.core\.node\$module\$react_select\$default = require\('react-select'\)\['default'\];" + (slurp (io/file out "cljs_3235/core.js")))))) + (is (true? (boolean (re-find #"cljs_3235\.core\.node\$module\$react_select\$default\$baz = require\('react-select'\)\['default'\]\['baz'\];" + (slurp (io/file out "cljs_3235/core.js")))))) + (is (true? (boolean (re-find #"cljs_3235\.core\.global\$module\$some_foreign\$woz = goog.global\[\"globalLib\"\]\['woz'\];" + (slurp (io/file out "cljs_3235/core.js")))))) + (is (true? (boolean (re-find #"cljs_3235\.core\.global\$module\$some_foreign\$foz\$boz = goog.global\[\"globalLib\"\]\['foz'\]\['boz'\];" + (slurp (io/file out "cljs_3235/core.js")))))) + (is (empty? @ws)))) + (.delete (io/file "package.json")) + (test/delete-node-modules)) From bdbd6c5a406559a6e5a95870777a527f30e9da5c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 25 Jul 2020 10:54:04 -0400 Subject: [PATCH 3677/4033] CLJS-3271: nth on range produces nonexistent values considering floating point --- src/main/cljs/cljs/core.cljs | 138 ++++++++++++++++-- src/test/cljs/cljs/collections_test.cljs | 51 +++++++ src/test/cljs/cljs/core_test.cljs | 18 +-- src/test/cljs/cljs/extend_to_native_test.cljs | 9 ++ 4 files changed, 193 insertions(+), 23 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b8d4a7953..af9a35cfb 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9650,7 +9650,7 @@ reduces them without incurring seq initialization" (take-while (mk-bound-fn sc start-test start-key) (if ((mk-bound-fn sc end-test end-key) e) s (next s)))))) -(deftype RangeChunk [start step count] +(deftype IntegerRangeChunk [start step count] ICounted (-count [coll] count) @@ -9669,7 +9669,7 @@ reduces them without incurring seq initialization" (-drop-first [coll] (if (<= count 1) (throw (js/Error. "-drop-first of empty chunk")) - (RangeChunk. (+ start step) step (dec count))))) + (IntegerRangeChunk. (+ start step) step (dec count))))) (deftype RangeIterator [^:mutable i end step] Object @@ -9682,7 +9682,7 @@ reduces them without incurring seq initialization" (set! i (+ i step)) ret))) -(deftype Range [meta start end step ^:mutable chunk ^:mutable chunk-next ^:mutable __hash] +(deftype IntegerRange [meta start end step ^:mutable chunk ^:mutable chunk-next ^:mutable __hash] Object (toString [coll] (pr-str* coll)) @@ -9701,18 +9701,18 @@ reduces them without incurring seq initialization" (let [count (-count coll)] (if (> count 32) (do - (set! chunk-next (Range. nil (+ start (* step 32)) end step nil nil nil)) - (set! chunk (RangeChunk. start step 32))) - (set! chunk (RangeChunk. start step count)))))) + (set! chunk-next (IntegerRange. nil (+ start (* step 32)) end step nil nil nil)) + (set! chunk (IntegerRangeChunk. start step 32))) + (set! chunk (IntegerRangeChunk. start step count)))))) ICloneable - (-clone [_] (Range. meta start end step chunk chunk-next __hash)) + (-clone [_] (IntegerRange. meta start end step chunk chunk-next __hash)) IWithMeta (-with-meta [rng new-meta] (if (identical? new-meta meta) rng - (Range. new-meta start end step chunk chunk-next __hash))) + (IntegerRange. new-meta start end step chunk chunk-next __hash))) IMeta (-meta [rng] meta) @@ -9736,9 +9736,9 @@ reduces them without incurring seq initialization" (-next [rng] (if (pos? step) (when (< (+ start step) end) - (Range. nil (+ start step) end step nil nil nil)) + (IntegerRange. nil (+ start step) end step nil nil nil)) (when (> (+ start step) end) - (Range. nil (+ start step) end step nil nil nil)))) + (IntegerRange. nil (+ start step) end step nil nil nil)))) IChunkedSeq (-chunked-first [rng] @@ -9796,6 +9796,113 @@ reduces them without incurring seq initialization" (recur (+ i step) ret))) ret)))) +(es6-iterable IntegerRange) + +(deftype Range [meta start end step ^:mutable chunk ^:mutable chunk-next ^:mutable __hash] + Object + (toString [coll] + (pr-str* coll)) + (equiv [this other] + (-equiv this other)) + (indexOf [coll x] + (-indexOf coll x 0)) + (indexOf [coll x start] + (-indexOf coll x start)) + (lastIndexOf [coll x] + (-lastIndexOf coll x (count coll))) + (lastIndexOf [coll x start] + (-lastIndexOf coll x start)) + (forceChunk [coll] + (when (nil? chunk) + (let [arr (make-array 32) + val (loop [n 0 val start] + (if (< n 32) + (do + (aset arr n val) + (let [n (inc n) + val (+ val step)] + (if (if (pos? step) (< val end) (> val end)) + (recur n val) + (set! chunk (array-chunk arr 0 n))))) + val))] + (when (nil? chunk) + (set! chunk (array-chunk arr 0 32)) + (when (if (pos? step) (< val end) (> val end)) + (set! chunk-next (Range. nil val end step nil nil nil))))))) + + ICloneable + (-clone [_] (Range. meta start end step chunk chunk-next __hash)) + + IWithMeta + (-with-meta [rng new-meta] + (if (identical? new-meta meta) + rng + (Range. new-meta start end step chunk chunk-next __hash))) + + IMeta + (-meta [rng] meta) + + ISeqable + (-seq [rng] rng) + + ISeq + (-first [rng] start) + (-rest [rng] + (let [s (-next rng)] + (if (nil? s) + () + s))) + + IIterable + (-iterator [_] + (RangeIterator. start end step)) + + INext + (-next [rng] + (if (pos? step) + (when (< (+ start step) end) + (Range. nil (+ start step) end step nil nil nil)) + (when (> (+ start step) end) + (Range. nil (+ start step) end step nil nil nil)))) + + IChunkedSeq + (-chunked-first [rng] + (.forceChunk rng) + chunk) + (-chunked-rest [rng] + (.forceChunk rng) + (if (nil? chunk-next) + () + chunk-next)) + + IChunkedNext + (-chunked-next [rng] + (seq (-chunked-rest rng))) + + ICollection + (-conj [rng o] (cons o rng)) + + IEmptyableCollection + (-empty [rng] (.-EMPTY List)) + + ISequential + IEquiv + (-equiv [rng other] (equiv-sequential rng other)) + + IHash + (-hash [rng] (caching-hash rng hash-ordered-coll __hash)) + + IReduce + (-reduce [rng f] (seq-reduce f rng)) + (-reduce [rng f init] + (loop [i start ret init] + (if (if (pos? step) (< i end) (> i end)) + (let [ret (f ret i)] + (if (reduced? ret) + @ret + (recur (+ i step) ret))) + ret)))) + (es6-iterable Range) (defn range @@ -9810,12 +9917,16 @@ reduces them without incurring seq initialization" (pos? step) (if (<= end start) () - (Range. nil start end step nil nil nil)) + (if (and (integer? start) (integer? end) (integer? step)) + (IntegerRange. nil start end step nil nil nil) + (Range. nil start end step nil nil nil))) (neg? step) (if (>= end start) () - (Range. nil start end step nil nil nil)) + (if (and (integer? start) (integer? end) (integer? step)) + (IntegerRange. nil start end step nil nil nil) + (Range. nil start end step nil nil nil))) :else (if (== end start) @@ -10429,6 +10540,9 @@ reduces them without incurring seq initialization" Range (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + IntegerRange + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) + Cycle (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll)) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 2c63d297a..f41ce7821 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -9,6 +9,10 @@ (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])) @@ -141,10 +145,18 @@ (is (= #{1} (disj #{1 2 3} 2 3))) (is (nil? (disj nil :foo))))) +(defspec integerrange-equals-range 100 + (prop/for-all [start gen/int + end gen/int + step gen/s-pos-int] + (= (Range. nil start end step nil nil nil) + (IntegerRange. nil start end step nil nil nil)))) + (deftest test-range (testing "Testing Range" ;; Range (is (= (range 0 10 3) (list 0 3 6 9))) + (is (= (range 2.5) '(0 1 2))) (is (= (count (range 0 10 3)) 4)) (is (= (range 0 -10 -3) (list 0 -3 -6 -9))) (is (= (count (range 0 -10 -3)) 4)) @@ -163,6 +175,45 @@ (is (= (count (range 0 0 0)) 0)) (is (= (take 3 (range 1 0 0)) (list 1 1 1))) (is (= (take 3 (range 3 1 0)) (list 3 3 3))) + (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))) + (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))) + (is (= (reduce + (range 0 100)) 4950)) + (is (= (reduce + 0 (range 0 100)) 4950)) + (is (= (reduce + (range 0.1 100)) 4960)) + (is (= (reduce + 0 (range 0.1 100)) 4960)) + (is (= (reduce + (map inc (range 0 100 1))) 5050)) + (is (= (reduce + 0 (map inc (range 0 100 1))) 5050)) + (is (= (reduce + (map inc (range 0 100 0.1))) 51051)) + (is (= (reduce + 0 (map inc (range 0 100 0.1))) 51051)) + (is (= (reduce + (range 0 3.1 0.1)) 46.500000000000014)) + (is (= (reduce + 0 (range 0 3.1 0.1)) 46.500000000000014)) + (is (= (reduce + (range 0 3.2 0.1)) 49.600000000000016)) + (is (= (reduce + 0 (range 0 3.2 0.1)) 49.600000000000016)) + (is (= (reduce + (range 0 3.3 0.1)) 52.80000000000002)) + (is (= (reduce + 0 (range 0 3.3 0.1)) 52.80000000000002)) + (is (= (reduce + (range 0 -3.1 -0.1)) -46.500000000000014)) + (is (= (reduce + 0 (range 0 -3.1 -0.1)) -46.500000000000014)) + (is (= (reduce + (range 0 -3.2 -0.1)) -49.600000000000016)) + (is (= (reduce + 0 (range 0 -3.2 -0.1)) -49.600000000000016)) + (is (= (reduce + (range 0 -3.3 -0.1)) -52.80000000000002)) + (is (= (reduce + 0 (range 0 -3.3 -0.1)) -52.80000000000002)) + (is (= (reduce + (map inc (range 0 3.1 0.1))) 77.50000000000001)) + (is (= (reduce + 0 (map inc (range 0 3.1 0.1))) 77.50000000000001)) + (is (= (reduce + (map inc (range 0 3.2 0.1))) 81.60000000000002)) + (is (= (reduce + 0 (map inc (range 0 3.2 0.1))) 81.60000000000002)) + (is (= (reduce + (map inc (range 0 3.3 0.1))) 85.80000000000003)) + (is (= (reduce + 0 (map inc (range 0 3.3 0.1))) 85.80000000000003)) + (is (= (reduce + (map inc (range 0 -3.1 -0.1))) -15.500000000000012)) + (is (= (reduce + 0 (map inc (range 0 -3.1 -0.1))) -15.500000000000012)) + (is (= (reduce + (map inc (range 0 -3.2 -0.1))) -17.600000000000016)) + (is (= (reduce + 0 (map inc (range 0 -3.2 -0.1))) -17.600000000000016)) + (is (= (reduce + (map inc (range 0 -3.3 -0.1))) -19.80000000000002)) + (is (= (reduce + 0 (map inc (range 0 -3.3 -0.1))) -19.80000000000002)) )) (deftest test-cycle diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 6223e5c26..ff38f8abc 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -621,16 +621,6 @@ (is (= (meta (with-meta (reify IFoo (foo [this] :foo)) {:foo :bar})) {:foo :bar}))) - -(defprotocol Slashy (/ [_])) - -(extend-type string - Slashy - (/ [_] "result")) - -(deftest test-protocol-with-slash - (is (= "result" (/ "")))) - (let [x "original"] (defn original-closure-stmt [] x)) @@ -1818,4 +1808,10 @@ (deftest test-cljs-3202 (is (= :/ (keyword "/"))) - (is (= (hash :/) (hash (keyword "/"))))) \ No newline at end of file + (is (= (hash :/) (hash (keyword "/"))))) + +(deftest test-cljs-3270 + (is (== 10 (count (range 0 (+ 1 (/ 9)) (/ 9)))))) + +(deftest test-cljs-3271 + (is (== 0.6 (nth (range 0 1 0.1) 6)))) diff --git a/src/test/cljs/cljs/extend_to_native_test.cljs b/src/test/cljs/cljs/extend_to_native_test.cljs index 378451d15..e0d7446b5 100644 --- a/src/test/cljs/cljs/extend_to_native_test.cljs +++ b/src/test/cljs/cljs/extend_to_native_test.cljs @@ -135,3 +135,12 @@ (is (= :p (test-seqable 1))) (extend-type string IReduce (-reduce [_ _] :q)) (is (= :q (test-reduceable "a")))) + +(defprotocol Slashy (/ [_])) + +(extend-type string + Slashy + (/ [_] "result")) + +(deftest test-protocol-with-slash + (is (= "result" (/ "")))) From 4c780171839fae8c619d5b1bf3f4ec1b70c9d6b2 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 22 Jul 2020 13:15:30 +0200 Subject: [PATCH 3678/4033] CLJS-3200: reduce code generated by destructure macro for maps --- src/main/cljs/cljs/core.cljs | 5 +++++ src/main/clojure/cljs/core.cljc | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index af9a35cfb..620777d59 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3986,6 +3986,11 @@ reduces them without incurring seq initialization" (set! *unchecked-if* false) +;; 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 [x] + (if (implements? ISeq x) (apply cljs.core/hash-map x) x)) + (defn vary-meta "Returns an object of the same type and value as obj, with (apply f (meta obj) args) as its metadata." diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index cdadb9e99..e7b31489f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -666,7 +666,7 @@ (core/let [gmap (gensym "map__") defaults (:or b)] (core/loop [ret (core/-> bvec (conj gmap) (conj v) - (conj gmap) (conj `(if (implements? ISeq ~gmap) (apply cljs.core/hash-map ~gmap) ~gmap)) + (conj gmap) (conj `(--destructure-map ~gmap)) ((core/fn [ret] (if (:as b) (conj ret (:as b) gmap) From ffbdf90f5f85a18300fb0554129def32d5809068 Mon Sep 17 00:00:00 2001 From: dnolen Date: Wed, 5 Aug 2020 19:59:55 -0400 Subject: [PATCH 3679/4033] revert usage of helper from CLJS-3200 fix, it seems it causes a strange JavaScriptCore failure in CI --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index e7b31489f..cdadb9e99 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -666,7 +666,7 @@ (core/let [gmap (gensym "map__") defaults (:or b)] (core/loop [ret (core/-> bvec (conj gmap) (conj v) - (conj gmap) (conj `(--destructure-map ~gmap)) + (conj gmap) (conj `(if (implements? ISeq ~gmap) (apply cljs.core/hash-map ~gmap) ~gmap)) ((core/fn [ret] (if (:as b) (conj ret (:as b) gmap) From 154b19a40e33fb9285bd5c16bc96ec0ab88c4261 Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Aug 2020 08:09:25 -0400 Subject: [PATCH 3680/4033] coerce setTimeout result to boolean, fix for CLJS-3274 (running tests w/ recent WebKit) --- src/main/cljs/cljs/core.cljs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 620777d59..34cbc888c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -74,14 +74,16 @@ Strings which should be printed." :dynamic true} *print-fn* nil) +(declare boolean) + (defn ^{:doc "Arranges to have tap functions executed via the supplied f, a function of no arguments. Returns true if successful, false otherwise." :dynamic true} *exec-tap-fn* [f] (and - (exists? js/setTimeout) - (js/setTimeout f 0) - true)) + (exists? js/setTimeout) + ;; See CLJS-3274 - workaround for recent WebKit releases + (boolean (js/setTimeout f 0)))) (defonce ^{:doc "Each runtime environment provides a different way to print error output. From f884af0aef03147f3eef7a680579f704a7b6b81c Mon Sep 17 00:00:00 2001 From: dnolen Date: Fri, 7 Aug 2020 08:48:29 -0400 Subject: [PATCH 3681/4033] restore CLJS-3200 --- src/main/clojure/cljs/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index cdadb9e99..e7b31489f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -666,7 +666,7 @@ (core/let [gmap (gensym "map__") defaults (:or b)] (core/loop [ret (core/-> bvec (conj gmap) (conj v) - (conj gmap) (conj `(if (implements? ISeq ~gmap) (apply cljs.core/hash-map ~gmap) ~gmap)) + (conj gmap) (conj `(--destructure-map ~gmap)) ((core/fn [ret] (if (:as b) (conj ret (:as b) gmap) From b5b099ab38412421d5e30fdfacf7c5dea22c1368 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Thu, 6 Aug 2020 14:16:35 +0200 Subject: [PATCH 3682/4033] CLJS-3273 preserve ns-name when processing an :ns* op This happens when a namespace contains both an `ns` form and a `require` statement. The `require` will expand to `(ns* (require ...))`, and when processed will generate a namespace name of the form `cljs.user.*` which ends up replacing the ns-name set in the `ns` declaration. --- src/main/clojure/cljs/compiler.cljc | 4 +++- src/test/clojure/cljs/compiler_tests.clj | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index d3e891292..d41879a5e 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1564,7 +1564,9 @@ (= :ns* (:op ast)) (let [ns-emitted? (some? ns-name) - ns-name (ana/gen-user-ns src)] + ns-name (if-not ns-emitted? + (ana/gen-user-ns src) + ns-name)] (if-not ns-emitted? (emit (assoc ast :name ns-name :op :ns)) (emit ast)) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index aaddd6647..e934c4991 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -358,6 +358,14 @@ (def hist (Html5History.))])) "(new goog.history.Html5History());")))) +(deftest emit-source-ns*-retains-ns-name ;; CLJS-3273 + (let [input (java.io.File/createTempFile "foo" ".cljs") + output (java.io.File/createTempFile "foo" ".js") + _ (spit input "(ns foo.foo) (require 'clojure.string)") + ns-info (env/ensure (comp/emit-source input output "cljs" {}))] + (is (= 'foo.foo (:ns ns-info))))) + + ;; CLJS-1225 (comment From ccde79a01cd1d26397393c50c23ec7e78ca82dde Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Thu, 6 Aug 2020 17:53:37 +0200 Subject: [PATCH 3683/4033] CLJS-3275: compute upstream npm-deps when :npm-deps is not set When :npm-deps is not explicitly configured it should still be able to find upstream dependencies through deps.cljs. This also fixes cljs.main --install-deps. --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index bc18fa0b0..a9fbfb4be 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2437,7 +2437,8 @@ (reduce (fn [m [dep v]] (cond-> m - (and (map? npm-deps) (not (contains? npm-deps dep))) + (and (or (nil? npm-deps) (map? npm-deps)) + (not (contains? npm-deps dep))) (assoc dep (if (coll? v) (last (sort v)) v)))) From 5b6558b9065311c77e3fa07174320e129984273e Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 9 Aug 2020 15:46:37 -0400 Subject: [PATCH 3684/4033] CLJS-3278: Update to tools.reader 1.3.3 --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/test/cljs/cljs/reader_test.cljs | 3 +++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index be2ed2a36..332216f7d 100644 --- a/deps.edn +++ b/deps.edn @@ -7,7 +7,7 @@ org.clojure/data.json {:mvn/version "0.2.6"} org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"} org.clojure/spec.alpha {:mvn/version "0.1.143"} - org.clojure/tools.reader {:mvn/version "1.3.2"} + org.clojure/tools.reader {:mvn/version "1.3.3"} org.clojure/test.check {:mvn/version "0.10.0-alpha3"}} :aliases {:cli.test.run {:extra-paths ["src/test/cljs_cli"] diff --git a/pom.template.xml b/pom.template.xml index 23edfc657..fe3f1f20d 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -45,7 +45,7 @@ org.clojure tools.reader - 1.3.2 + 1.3.3 com.cognitect diff --git a/project.clj b/project.clj index d2c017c3c..08d8af286 100644 --- a/project.clj +++ b/project.clj @@ -12,7 +12,7 @@ [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] - [org.clojure/tools.reader "1.3.2"] + [org.clojure/tools.reader "1.3.3"] [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20191016-6ae1f72f"] diff --git a/script/bootstrap b/script/bootstrap index 12b001b30..13b47e43d 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -9,7 +9,7 @@ CLOSURE_RELEASE="20200315" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20191016-6ae1f72f" -TREADER_RELEASE="1.3.0" +TREADER_RELEASE="1.3.3" TEST_CHECK_RELEASE="0.10.0-alpha3" # check dependencies diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 9a8a9b43d..9ba1aa2f8 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -236,3 +236,6 @@ ;(deftest test-error-messages ; (testing "Leading numbers in keywords" ; (is (thrown-with-msg? js/Error #"Invalid keyword :0s" (reader/read-string ":0s"))))) + +(deftest testing-cljs-3278 + (is (nil? (reader/read-string {:readers {'foo (constantly nil)}} "#foo 1")))) From a15247a743d4d1c5d73224038f7289c447b38ca8 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Thu, 13 Aug 2020 11:49:31 +0200 Subject: [PATCH 3685/4033] CLJ-3279 Error when :npm-deps is boolean and :install-deps true Don't assume that :npm-deps is a map/nil, since it can also be boolean. --- src/main/clojure/cljs/closure.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a9fbfb4be..a1f35faef 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2585,7 +2585,10 @@ (defn maybe-install-node-deps! [{:keys [deps-cmd npm-deps verbose] :or {deps-cmd "npm"} :as opts}] - (let [npm-deps (merge npm-deps (compute-upstream-npm-deps opts))] + (let [npm-deps (merge (if (map? npm-deps) + npm-deps + {}) + (compute-upstream-npm-deps opts))] (when-not (empty? npm-deps) (let [pkg-json (io/file "package.json")] (when (or ana/*verbose* verbose) From 0e85a1a26ca5aa3f9708eda7514624467da91656 Mon Sep 17 00:00:00 2001 From: dnolen Date: Sun, 23 Aug 2020 07:33:14 -0400 Subject: [PATCH 3686/4033] move two more symbols to impl move the impl ns into the cljs part use self-host perf predicates from cljs.analyzer.impl ns, remove from cljs.analyzer organize cljs.compiler ns form unset JAVA_TOOL_OPTIONS before running CLI tests add cljs.analyzer.impl ns, move pre-allocated symbols used for perf there, add self-host perf predicates too, not used yet organize the analyzer ns form --- .github/workflows/test.yaml | 1 + src/main/clojure/cljs/analyzer.cljc | 170 ++++++++--------------- src/main/clojure/cljs/analyzer/impl.cljc | 59 ++++++++ src/main/clojure/cljs/compiler.cljc | 53 +++---- 4 files changed, 148 insertions(+), 135 deletions(-) create mode 100644 src/main/clojure/cljs/analyzer/impl.cljc diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 04165956a..fc1143bc7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -204,5 +204,6 @@ jobs: - name: Run tests run: | + unset JAVA_TOOL_OPTIONS clojure -A:cli.test.run | tee test-out.txt grep -qxF '0 failures, 0 errors.' test-out.txt diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2cd000e6c..c9f7b7fb0 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -7,38 +7,39 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.analyzer - #?(:clj (:refer-clojure :exclude [macroexpand-1 ensure]) - :cljs (:refer-clojure :exclude [macroexpand-1 ns-interns ensure js-reserved])) - #?(:cljs (:require-macros - [cljs.analyzer.macros - :refer [no-warn wrapping-errors with-warning-handlers - disallowing-recur allowing-redef disallowing-ns*]] + #?(:clj (:refer-clojure :exclude [ensure macroexpand-1]) + :cljs (:refer-clojure :exclude [ensure js-reserved macroexpand-1 ns-interns])) + #?(:cljs (:require-macros [cljs.analyzer.macros + :refer [allowing-redef disallowing-ns* disallowing-recur + no-warn with-warning-handlers wrapping-errors]] [cljs.env.macros :refer [ensure]])) - #?(:clj (:require [cljs.util :as util :refer [ns->relpath topo-sort]] - [clojure.java.io :as io] - [clojure.string :as string] - [clojure.set :as set] + #?(:clj (:require [cljs.analyzer.impl :as impl] [cljs.env :as env :refer [ensure]] + [cljs.externs :as externs] [cljs.js-deps :as deps] [cljs.tagged-literals :as tags] - [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers] + [cljs.util :as util :refer [ns->relpath topo-sort]] [clojure.edn :as edn] - [cljs.externs :as externs]) - :cljs (:require [goog.string :as gstring] - [clojure.string :as string] + [clojure.java.io :as io] [clojure.set :as set] + [clojure.string :as string] + [clojure.tools.reader :as reader] + [clojure.tools.reader.reader-types :as readers]) + :cljs (:require [cljs.analyzer.impl :as impl] [cljs.env :as env] + [cljs.reader :as edn] [cljs.tagged-literals :as tags] [cljs.tools.reader :as reader] [cljs.tools.reader.reader-types :as readers] - [cljs.reader :as edn])) - #?(:clj (:import [java.io File Reader PushbackReader] - [java.util.regex Pattern] - [java.net URL] - [java.lang Throwable] + [clojure.set :as set] + [clojure.string :as string] + [goog.string :as gstring])) + #?(:clj (:import [cljs.tagged_literals JSValue] [clojure.lang Namespace Var LazySeq ArityException] - [cljs.tagged_literals JSValue]))) + [java.io File Reader PushbackReader] + [java.lang Throwable] + [java.net URL] + [java.util.regex Pattern]))) #?(:clj (set! *warn-on-reflection* true)) @@ -220,52 +221,6 @@ (when-not (identical? m SENTINEL) (get m k3))))))))) -#?(:cljs - (def CLJ_NIL_SYM 'clj-nil)) - -#?(:cljs - (def NUMBER_SYM 'number)) - -#?(:cljs - (def STRING_SYM 'string)) - -(def BOOLEAN_SYM 'boolean) - -#?(:cljs - (def JS_STAR_SYM 'js*)) - -#?(:cljs - (def DOT_SYM '.)) - -#?(:cljs - (def NEW_SYM 'new)) - -#?(:cljs - (def CLJS_CORE_SYM 'cljs.core)) - -#?(:cljs - (def CLJS_CORE_MACROS_SYM 'cljs.core$macros)) - -(def IGNORE_SYM 'ignore) - -(def ANY_SYM 'any) - -#?(:cljs - (defn ^boolean cljs-seq? [x] - (implements? ISeq x))) - -#?(:cljs - (defn ^boolean cljs-map? [x] - (implements? IMap x))) - -#?(:cljs - (defn ^boolean cljs-vector? [x] - (implements? IVector x))) - -#?(:cljs - (defn ^boolean cljs-set? [x] - (implements? ISet x))) - #?(:cljs (defn munge-path [ss] (munge (str ss)))) @@ -950,7 +905,7 @@ "Ensures that a type tag is a set." [t] (if #?(:clj (set? t) - :cljs (cljs-set? t)) + :cljs (impl/cljs-set? t)) t #{t})) @@ -1348,7 +1303,7 @@ (let [ns (cond (some? (get-in namespaces [ns :macros sym])) ns (core-name? env sym) #?(:clj 'cljs.core - :cljs CLJS_CORE_MACROS_SYM))] + :cljs impl/CLJS_CORE_MACROS_SYM))] (when (some? ns) #?(:clj (get-in namespaces [ns :macros sym]) :cljs (get-in namespaces [ns :defs sym]))))))) @@ -1417,10 +1372,6 @@ (declare infer-tag) -(def NOT_NATIVE '#{clj not-native}) - -(def BOOLEAN_OR_SEQ '#{boolean seq}) - (defn unwrap-quote [{:keys [op] :as expr}] (if #?(:clj (= op :quote) :cljs (keyword-identical? op :quote)) @@ -1439,23 +1390,23 @@ (cond (or #?(:clj (= then-tag else-tag) :cljs (symbol-identical? then-tag else-tag)) - #?(:clj (= else-tag IGNORE_SYM) - :cljs (symbol-identical? else-tag IGNORE_SYM))) then-tag - #?(:clj (= then-tag IGNORE_SYM) - :cljs (symbol-identical? then-tag IGNORE_SYM)) else-tag + #?(:clj (= else-tag impl/IGNORE_SYM) + :cljs (symbol-identical? else-tag impl/IGNORE_SYM))) then-tag + #?(:clj (= then-tag impl/IGNORE_SYM) + :cljs (symbol-identical? then-tag impl/IGNORE_SYM)) else-tag ;; TODO: temporary until we move not-native -> clj - David - (and (or (some? (get NOT_NATIVE then-tag)) (type? env then-tag)) - (or (some? (get NOT_NATIVE else-tag)) (type? env else-tag))) + (and (or (some? (get impl/NOT_NATIVE then-tag)) (type? env then-tag)) + (or (some? (get impl/NOT_NATIVE else-tag)) (type? env else-tag))) 'clj :else - (if (and (some? (get BOOLEAN_OR_SEQ then-tag)) - (some? (get BOOLEAN_OR_SEQ else-tag))) + (if (and (some? (get impl/BOOLEAN_OR_SEQ then-tag)) + (some? (get impl/BOOLEAN_OR_SEQ else-tag))) 'seq (let [then-tag (if #?(:clj (set? then-tag) - :cljs (cljs-set? then-tag)) + :cljs (impl/cljs-set? then-tag)) then-tag #{then-tag}) else-tag (if #?(:clj (set? else-tag) - :cljs (cljs-set? else-tag)) + :cljs (impl/cljs-set? else-tag)) else-tag #{else-tag})] (into then-tag else-tag)))))))) @@ -1469,7 +1420,7 @@ (:ret-tag info) (when (= 'js (:ns info)) 'js))] ret-tag - ANY_SYM))))) + impl/ANY_SYM))))) (defn infer-tag "Given env, an analysis environment, and e, an AST node, return the inferred @@ -1478,8 +1429,8 @@ (if-some [tag (get-tag e)] tag (case (:op e) - :recur IGNORE_SYM - :throw IGNORE_SYM + :recur impl/IGNORE_SYM + :throw impl/IGNORE_SYM :let (infer-tag env (:body e)) :loop (infer-tag env (:body e)) :do (infer-tag env (:ret e)) @@ -1488,16 +1439,17 @@ :invoke (infer-invoke env e) :if (infer-if env e) :const (case (:form e) - true BOOLEAN_SYM - false BOOLEAN_SYM - ANY_SYM) + true impl/BOOLEAN_SYM + false impl/BOOLEAN_SYM + impl/ANY_SYM) :quote (infer-tag env (:expr e)) (:var :local :js-var :binding) (if-some [init (:init e)] (infer-tag env init) (infer-tag env (:info e))) - (:host-field :host-call) ANY_SYM - :js ANY_SYM + (:host-field :host-call) + impl/ANY_SYM + :js impl/ANY_SYM nil))) (defmulti parse (fn [op & rest] op)) @@ -1940,7 +1892,7 @@ tag (cond fn-var? (or (:ret-tag init-expr) tag (:inferred-ret-tag init-expr)) tag tag - dynamic ANY_SYM + dynamic impl/ANY_SYM :else (:tag init-expr)) export-as (when-let [export-val (-> sym meta :export)] (if (= true export-val) var-name export-val)) @@ -3527,7 +3479,7 @@ (if (and (symbol? t) (some? (get NUMERIC_SET t))) true (when #?(:clj (set? t) - :cljs (cljs-set? t)) + :cljs (impl/cljs-set? t)) (or (contains? t 'number) (contains? t 'long) (contains? t 'double) @@ -3550,7 +3502,7 @@ :else (boolean (when #?(:clj (set? t) - :cljs (cljs-set? t)) + :cljs (impl/cljs-set? t)) (or (contains? t 'any) (contains? t 'js) (some array-types t)))))) @@ -3837,7 +3789,7 @@ nstr (if (some? res) (str res) nstr)] (cond #?@(:clj [(= "clojure.core" nstr) (find-ns 'cljs.core)] - :cljs [(identical? "clojure.core" nstr) (find-macros-ns CLJS_CORE_MACROS_SYM)]) + :cljs [(identical? "clojure.core" nstr) (find-macros-ns impl/CLJS_CORE_MACROS_SYM)]) #?@(:clj [(= "clojure.repl" nstr) (find-ns 'cljs.repl)] :cljs [(identical? "clojure.repl" nstr) (find-macros-ns 'cljs.repl)]) #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] @@ -3868,7 +3820,7 @@ (.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 CLJS_CORE_MACROS_SYM)) sym))))))) + #?(: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 @@ -3933,11 +3885,11 @@ (throw (ArityException. (- (.actual e) 2) (.name e))))) (catch #?(:clj Throwable :cljs :default) e (throw (ex-info nil (error-data env :macroexpansion (var->sym mac-var)) e))))] - (if #?(:clj (seq? form') :cljs (cljs-seq? form')) + (if #?(:clj (seq? form') :cljs (impl/cljs-seq? form')) (let [sym' (first form') sym (first form)] (if #?(:clj (= sym' 'js*) - :cljs (symbol-identical? sym' JS_STAR_SYM)) + :cljs (symbol-identical? sym' impl/JS_STAR_SYM)) (let [sym (if (some? (namespace sym)) sym (symbol "cljs.core" (str sym))) @@ -3960,14 +3912,14 @@ #?(:clj (first opname) :cljs (.charAt opname 0))) (let [[target & args] (next form)] - (with-meta (list* #?(:clj '. :cljs DOT_SYM) target (symbol (subs opname 1)) args) + (with-meta (list* #?(:clj '. :cljs impl/DOT_SYM) target (symbol (subs opname 1)) args) (meta form))) (identical? \. #?(:clj (last opname) :cljs (.charAt opname (dec (. opname -length))))) (with-meta - (list* #?(:clj 'new :cljs NEW_SYM) (symbol (subs opname 0 (dec (count opname)))) (next form)) + (list* #?(:clj 'new :cljs impl/NEW_SYM) (symbol (subs opname 0 (dec (count opname)))) (next form)) (meta form)) :else form)) @@ -4222,20 +4174,20 @@ (defn analyze-form [env form name opts] (cond (symbol? form) (analyze-symbol env form) - (and (cljs-seq? form) (some? (seq form))) (analyze-seq env form name opts) + (and (impl/cljs-seq? form) (some? (seq form))) (analyze-seq env form name opts) (record? form) (analyze-record env form) - (cljs-map? form) (analyze-map env form) - (cljs-vector? form) (analyze-vector env form) - (cljs-set? form) (analyze-set env form) + (impl/cljs-map? form) (analyze-map env form) + (impl/cljs-vector? form) (analyze-vector env form) + (impl/cljs-set? form) (analyze-set env form) (keyword? form) (analyze-keyword env form) (instance? cljs.tagged-literals/JSValue form) (analyze-js-value env form) :else (let [tag (cond - (nil? form) CLJ_NIL_SYM - (number? form) NUMBER_SYM - (string? form) STRING_SYM - (true? form) BOOLEAN_SYM - (false? form) BOOLEAN_SYM + (nil? form) impl/CLJ_NIL_SYM + (number? form) impl/NUMBER_SYM + (string? form) impl/STRING_SYM + (true? form) impl/BOOLEAN_SYM + (false? form) impl/BOOLEAN_SYM (= () form) 'cljs.core/IList)] (cond-> {:op :const :val form :env env :form form} tag (assoc :tag tag)))))) diff --git a/src/main/clojure/cljs/analyzer/impl.cljc b/src/main/clojure/cljs/analyzer/impl.cljc new file mode 100644 index 000000000..75ebfa7d5 --- /dev/null +++ b/src/main/clojure/cljs/analyzer/impl.cljc @@ -0,0 +1,59 @@ +; 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.impl) + +(def ANY_SYM 'any) + +(def BOOLEAN_OR_SEQ '#{boolean seq}) + +(def BOOLEAN_SYM 'boolean) + +#?(:cljs + (def CLJ_NIL_SYM 'clj-nil)) + +#?(:cljs + (def CLJS_CORE_MACROS_SYM 'cljs.core$macros)) + +#?(:cljs + (def CLJS_CORE_SYM 'cljs.core)) + +#?(:cljs + (def DOT_SYM '.)) + +(def IGNORE_SYM 'ignore) + +#?(:cljs + (def JS_STAR_SYM 'js*)) + +#?(:cljs + (def NEW_SYM 'new)) + +(def NOT_NATIVE '#{clj not-native}) + +#?(:cljs + (def NUMBER_SYM 'number)) + +#?(:cljs + (def STRING_SYM 'string)) + +#?(:cljs + (defn ^boolean cljs-map? [x] + (implements? IMap x))) + +#?(:cljs + (defn ^boolean cljs-seq? [x] + (implements? ISeq x))) + +#?(:cljs + (defn ^boolean cljs-vector? [x] + (implements? IVector x))) + +#?(:cljs + (defn ^boolean cljs-set? [x] + (implements? ISet x))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index d41879a5e..1435aa4a8 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -7,33 +7,34 @@ ; You must not remove this notice, or any other, from this software. (ns cljs.compiler - #?(:clj (:refer-clojure :exclude [munge macroexpand-1 ensure]) - :cljs (:refer-clojure :exclude [munge macroexpand-1 ensure js-reserved])) + #?(:clj (:refer-clojure :exclude [ensure macroexpand-1 munge]) + :cljs (:refer-clojure :exclude [ensure js-reserved macroexpand-1 munge])) #?(:cljs (:require-macros [cljs.compiler.macros :refer [emit-wrap]] [cljs.env.macros :refer [ensure]])) - #?(:clj (:require [cljs.util :as util] - [clojure.java.io :as io] - [clojure.string :as string] - [clojure.set :as set] - [clojure.tools.reader :as reader] + #?(:clj (:require [cljs.analyzer :as ana] [cljs.env :as env :refer [ensure]] - [cljs.tagged-literals :as tags] - [cljs.analyzer :as ana] + [cljs.js-deps :as deps] [cljs.source-map :as sm] + [cljs.tagged-literals :as tags] + [cljs.util :as util] [clojure.data.json :as json] - [cljs.js-deps :as deps]) - :cljs (:require [goog.string :as gstring] - [clojure.string :as string] - [clojure.set :as set] - [cljs.tools.reader :as reader] + [clojure.java.io :as io] + [clojure.set :as set] + [clojure.string :as string] + [clojure.tools.reader :as reader]) + :cljs (:require [cljs.analyzer :as ana] + [cljs.analyzer.impl :as ana.impl] [cljs.env :as env] - [cljs.analyzer :as ana] - [cljs.source-map :as sm])) - #?(:clj (:import java.lang.StringBuilder + [cljs.source-map :as sm] + [cljs.tools.reader :as reader] + [clojure.set :as set] + [clojure.string :as string] + [goog.string :as gstring])) + #?(:clj (:import [cljs.tagged_literals JSValue] + java.lang.StringBuilder [java.io File Writer] [java.util.concurrent Executors ExecutorService TimeUnit] - [java.util.concurrent.atomic AtomicLong] - [cljs.tagged_literals JSValue]) + [java.util.concurrent.atomic AtomicLong]) :cljs (:import [goog.string StringBuffer]))) #?(:clj (set! *warn-on-reflection* true)) @@ -112,7 +113,7 @@ ([s] (munge s js-reserved)) ([s reserved] (if #?(:clj (map? s) - :cljs (ana/cljs-map? s)) + :cljs (ana.impl/cljs-map? s)) (let [name-var s name (:name name-var) field (:field name-var) @@ -206,8 +207,8 @@ ([^Object a] (cond (nil? a) nil - #?(:clj (map? a) :cljs (ana/cljs-map? a)) (emit a) - #?(:clj (seq? a) :cljs (ana/cljs-seq? a)) (apply emits a) + #?(:clj (map? a) :cljs (ana.impl/cljs-map? a)) (emit a) + #?(:clj (seq? a) :cljs (ana.impl/cljs-seq? a)) (apply emits a) #?(:clj (fn? a) :cljs ^boolean (goog/isFunction a)) (a) :else (let [^String s (cond-> a (not (string? a)) .toString)] #?(:clj (when-some [^AtomicLong gen-col *source-map-data-gen-col*] @@ -284,12 +285,12 @@ :cljs (defn emit-constant-no-meta [x] (cond - (ana/cljs-seq? x) (emit-list x emit-constants-comma-sep) + (ana.impl/cljs-seq? x) (emit-list x emit-constants-comma-sep) (record? x) (let [[ns name] (ana/record-ns+name x)] (emit-record-value ns name #(emit-constant (into {} x)))) - (ana/cljs-map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep all-distinct?) - (ana/cljs-vector? x) (emit-vector x emit-constants-comma-sep) - (ana/cljs-set? x) (emit-set x emit-constants-comma-sep all-distinct?) + (ana.impl/cljs-map? x) (emit-map (keys x) (vals x) emit-constants-comma-sep all-distinct?) + (ana.impl/cljs-vector? x) (emit-vector x emit-constants-comma-sep) + (ana.impl/cljs-set? x) (emit-set x emit-constants-comma-sep all-distinct?) :else (emit-constant* x)))) (defn emit-constant [v] From 599cd05fd271b4ce672e8a6124f0f785b1b3b2d0 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 28 Mar 2020 13:17:28 -0400 Subject: [PATCH 3687/4033] CLJS-3216: Leftover CI check for errors in output --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b5b47f05a..c05c84b40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,4 +61,3 @@ script: - grep '0 failures, 0 errors.' test-out.txt - script/test-cli node | tee test-out.txt - grep '0 failures, 0 errors.' test-out.txt - - grep '0 failures, 0 errors.' test-out.txt From 5e88d3383e0f950c4de410d3d6ee11769f3714f4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 4 Oct 2020 11:25:45 -0400 Subject: [PATCH 3688/4033] CLJS-3281: CLI help for target missing comma --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 62c9b09e0..8b73d8dba 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -628,7 +628,7 @@ present" :arg "name" :doc (str "The JavaScript target. Configures environment bootstrap and " - "defaults to browser. Supported values: node or nodejs " + "defaults to browser. Supported values: node or nodejs, " "webworker, none") } ["-ro" "--repl-opts"] {:group ::main&compile :fn repl-env-opts-opt :arg "edn" From 017ce8812d9b73f5df076b20e818e0ae90b90812 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 26 Dec 2020 12:11:35 -0500 Subject: [PATCH 3689/4033] CLJS-3291: Incorrect #inst parsing with respect to Julian / Gregorian calendar transition --- src/main/clojure/cljs/compiler.cljc | 10 +++- src/main/clojure/cljs/instant.clj | 54 ++++++++++++++++++++++ src/main/clojure/cljs/tagged_literals.cljc | 4 +- src/test/cljs/cljs/reader_test.cljs | 15 ++++++ src/test/clojure/cljs/instant_tests.clj | 22 +++++++++ src/test/clojure/cljs/test_runner.clj | 2 + 6 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 src/main/clojure/cljs/instant.clj create mode 100644 src/test/clojure/cljs/instant_tests.clj diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1435aa4a8..1f1e33053 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -33,6 +33,7 @@ #?(:clj (:import [cljs.tagged_literals JSValue] java.lang.StringBuilder [java.io File Writer] + [java.time Instant] [java.util.concurrent Executors ExecutorService TimeUnit] [java.util.concurrent.atomic AtomicLong]) :cljs (:import [goog.string StringBuffer]))) @@ -418,8 +419,15 @@ ;; tagged literal support +(defn- emit-inst [inst-ms] + (emits "new Date(" inst-ms ")")) + (defmethod emit-constant* #?(:clj java.util.Date :cljs js/Date) [^java.util.Date date] - (emits "new Date(" (.getTime date) ")")) + (emit-inst (.getTime date))) + +#?(:clj + (defmethod emit-constant* java.time.Instant [^java.time.Instant inst] + (emit-inst (.toEpochMilli inst)))) (defmethod emit-constant* #?(:clj java.util.UUID :cljs UUID) [^java.util.UUID uuid] (let [uuid-str (.toString uuid)] diff --git a/src/main/clojure/cljs/instant.clj b/src/main/clojure/cljs/instant.clj new file mode 100644 index 000000000..81df024d1 --- /dev/null +++ b/src/main/clojure/cljs/instant.clj @@ -0,0 +1,54 @@ +;; 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.instant + (:require [clojure.instant :as inst]) + (:import [java.time Instant OffsetDateTime ZoneOffset] + [java.time.format DateTimeFormatter DateTimeFormatterBuilder] + [java.util Locale Locale$Category])) + +(set! *warn-on-reflection* true) + +(def ^:private ^java.time.format.DateTimeFormatter utc-format + (-> (DateTimeFormatterBuilder.) + (.appendInstant 9) + (.toFormatter (Locale/getDefault Locale$Category/FORMAT)))) + +(defn- remove-last-char ^String [s] + (subs s 0 (dec (count s)))) + +(defn- print-instant + "Print a java.time.Instant as RFC3339 timestamp, always in UTC." + [^java.time.Instant instant, ^java.io.Writer w] + (.write w "#inst \"") + (.write w (remove-last-char (.format utc-format instant))) + (.write w "-00:00\"")) + +(defmethod print-method java.time.Instant + [^java.time.Instant instant, ^java.io.Writer w] + (print-instant instant w)) + +(defmethod print-dup java.time.Instant + [^java.time.Instant instant, ^java.io.Writer w] + (print-instant instant w)) + +(defn- construct-instant + "Construct a java.time.Instant, which has nanosecond precision." + [years months days hours minutes seconds nanoseconds + offset-sign offset-hours offset-minutes] + (Instant/from + (OffsetDateTime/of years months days hours minutes seconds nanoseconds + (ZoneOffset/ofHoursMinutes (* offset-sign offset-hours) (* offset-sign offset-minutes))))) + +(defn read-instant-instant + "To read an instant as a java.time.Instant, bind *data-readers* to a + map with this var as the value for the 'inst key. Instant preserves + fractional seconds with nanosecond precision. The timezone offset will + be used to convert into UTC." + [^CharSequence cs] + (inst/parse-timestamp (inst/validated construct-instant) cs)) diff --git a/src/main/clojure/cljs/tagged_literals.cljc b/src/main/clojure/cljs/tagged_literals.cljc index d38832681..09d53cbb1 100644 --- a/src/main/clojure/cljs/tagged_literals.cljc +++ b/src/main/clojure/cljs/tagged_literals.cljc @@ -7,7 +7,7 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.tagged-literals - #?(:clj (:require [clojure.instant :as inst]) + #?(:clj (:require [cljs.instant :as inst]) :cljs (:require [cljs.reader :as reader]))) (defn read-queue @@ -46,7 +46,7 @@ (when-not (string? form) (throw (RuntimeException. "Instance literal expects a string for its timestamp."))) (try - (inst/read-instant-date form) + (inst/read-instant-instant form) (catch Throwable e (throw (RuntimeException. (.getMessage e))))))) diff --git a/src/test/cljs/cljs/reader_test.cljs b/src/test/cljs/cljs/reader_test.cljs index 9ba1aa2f8..e4c01923f 100644 --- a/src/test/cljs/cljs/reader_test.cljs +++ b/src/test/cljs/cljs/reader_test.cljs @@ -239,3 +239,18 @@ (deftest testing-cljs-3278 (is (nil? (reader/read-string {:readers {'foo (constantly nil)}} "#foo 1")))) + +(deftest testing-cljs-3291 + (is (= "#inst \"1500-01-01T00:00:00.000-00:00\"" (pr-str #inst "1500"))) + (is (= "#inst \"1582-10-04T00:00:00.000-00:00\"" (pr-str #inst "1582-10-04"))) + (is (= "#inst \"1582-10-04T23:59:59.999-00:00\"" (pr-str #inst "1582-10-04T23:59:59.999"))) + (is (= "#inst \"1582-10-05T00:00:00.000-00:00\"" (pr-str #inst "1582-10-05"))) + (is (= "#inst \"1582-10-07T00:00:00.000-00:00\"" (pr-str #inst "1582-10-07"))) + (is (= "#inst \"1582-10-14T23:59:59.999-00:00\"" (pr-str #inst "1582-10-14T23:59:59.999"))) + (is (= "#inst \"1582-10-15T00:00:00.000-00:00\"" (pr-str #inst "1582-10-15"))) + (is (= "#inst \"1582-10-17T00:00:00.000-00:00\"" (pr-str #inst "1582-10-17"))) + (is (= "#inst \"1700-01-01T00:00:00.000-00:00\"" (pr-str #inst "1700"))) + (is (= "#inst \"1850-01-01T00:00:00.000-00:00\"" (pr-str #inst "1850"))) + (is (= "#inst \"1984-01-01T00:00:00.000-00:00\"" (pr-str #inst "1984"))) + (is (= "#inst \"2000-01-01T00:00:10.123-00:00\"" (pr-str #inst "2000-01-01T00:00:10.123456789-00:00"))) + (is (= "#inst \"2020-01-01T05:00:00.000-00:00\"" (pr-str #inst "2020-01-01T00:00:00.000-05:00")))) diff --git a/src/test/clojure/cljs/instant_tests.clj b/src/test/clojure/cljs/instant_tests.clj new file mode 100644 index 000000000..69b0cfa27 --- /dev/null +++ b/src/test/clojure/cljs/instant_tests.clj @@ -0,0 +1,22 @@ +;; 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.instant-tests + (:require + [cljs.instant :as inst] + [clojure.test :refer [deftest is]])) + +(deftest read-instant-instant-test + ;; Clojure uses hybrid Julian / Gregorian, while Instant is proleptic Gregorian + (is (not= #inst "1500" (inst/read-instant-instant "1500"))) + (is (not= (inst-ms #inst "1500") (inst-ms (inst/read-instant-instant "1500")))) + (is (= -14831769600000 (inst-ms (inst/read-instant-instant "1500")))) + (is (= "#inst \"1500-01-01T00:00:00.123456789-00:00\"" + (pr-str (inst/read-instant-instant "1500-01-01T00:00:00.123456789-00:00")))) + (is (= "#inst \"2020-01-01T05:00:00.000000000-00:00\"" + (pr-str (inst/read-instant-instant "2020-01-01T00:00:00.000-05:00"))))) diff --git a/src/test/clojure/cljs/test_runner.clj b/src/test/clojure/cljs/test_runner.clj index 98715cc92..5a8799cb1 100644 --- a/src/test/clojure/cljs/test_runner.clj +++ b/src/test/clojure/cljs/test_runner.clj @@ -6,6 +6,7 @@ [cljs.compiler-tests] [cljs.externs-infer-tests] [cljs.externs-parsing-tests] + [cljs.instant-tests] [cljs.module-graph-tests] [cljs.module-processing-tests] [cljs.source-map.base64-tests] @@ -23,6 +24,7 @@ 'cljs.compiler-tests 'cljs.externs-infer-tests 'cljs.externs-parsing-tests + 'cljs.instant-tests 'cljs.module-graph-tests 'cljs.module-processing-tests 'cljs.source-map.base64-tests From fdbd29e232e9605a4004dd64f6651a602edb29f6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 4 Jan 2021 10:55:18 -0500 Subject: [PATCH 3690/4033] bump setup-clojure & tools-deps --- .github/workflows/test.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fc1143bc7..32c7287f8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,9 +9,9 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: DeLaGuardo/setup-clojure@2.0 + - uses: DeLaGuardo/setup-clojure@3.1 with: - tools-deps: '1.10.1.536' + tools-deps: '1.10.1.763' - name: Cache maven uses: actions/cache@v2 @@ -61,9 +61,9 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: DeLaGuardo/setup-clojure@2.0 + - uses: DeLaGuardo/setup-clojure@3.1 with: - tools-deps: '1.10.1.536' + tools-deps: '1.10.1.763' - name: Cache maven uses: actions/cache@v2 @@ -100,9 +100,9 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: DeLaGuardo/setup-clojure@2.0 + - uses: DeLaGuardo/setup-clojure@3.1 with: - tools-deps: '1.10.1.536' + tools-deps: '1.10.1.763' - name: Cache maven uses: actions/cache@v2 @@ -139,9 +139,9 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: DeLaGuardo/setup-clojure@2.0 + - uses: DeLaGuardo/setup-clojure@3.1 with: - tools-deps: '1.10.1.536' + tools-deps: '1.10.1.763' - name: Cache maven uses: actions/cache@v2 @@ -175,9 +175,9 @@ jobs: with: fetch-depth: 0 - - uses: DeLaGuardo/setup-clojure@2.0 + - uses: DeLaGuardo/setup-clojure@3.1 with: - tools-deps: '1.10.1.536' + tools-deps: '1.10.1.763' - name: Cache maven uses: actions/cache@v2 From fa4b8d853be08120cb864782e4ea48826b9d757e Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Fri, 10 Jul 2020 21:59:39 +0200 Subject: [PATCH 3691/4033] CLJS-3263 Always print #inst year with 4 digits --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 34cbc888c..f11b30223 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10288,7 +10288,7 @@ reduces them without incurring seq initialization" ns)))] (write-all writer "#inst \"" - (str (.getUTCFullYear obj)) "-" + (normalize (.getUTCFullYear obj) 4) "-" (normalize (inc (.getUTCMonth obj)) 2) "-" (normalize (.getUTCDate obj) 2) "T" (normalize (.getUTCHours obj) 2) ":" diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index ff38f8abc..cd48f71cf 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1810,6 +1810,10 @@ (is (= :/ (keyword "/"))) (is (= (hash :/) (hash (keyword "/"))))) +(deftest test-cljs-3263 + (is (= "#inst \"0985-04-12T23:20:50.520-00:00\"" (pr-str #inst "0985-04-12T23:20:50.520-00:00"))) + (is (= "#inst \"1970-12-18T23:20:50.520-00:00\"" (pr-str #inst "1970-12-18T23:20:50.520-00:00")))) + (deftest test-cljs-3270 (is (== 10 (count (range 0 (+ 1 (/ 9)) (/ 9)))))) From afe2cf748fb77c5194bac0608e32358e15da067d Mon Sep 17 00:00:00 2001 From: Matthew Huebert Date: Mon, 4 Jan 2021 12:41:10 -0500 Subject: [PATCH 3692/4033] CLJS-3287: selfhost: eval does not catch and return errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit in cljs.js, eval-str catches errors during analysis, compilation and evaluation, and returns a {:value … :error …} map. In contrast, the eval function doesn’t catch errors during the evaluation step. Attached patch + tests to make these behave consistently. --- src/main/cljs/cljs/js.cljs | 10 ++++++++-- src/test/self/self_host/test.cljs | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 0cf1716ec..fb13e0054 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -837,9 +837,15 @@ (filter ana/dep-has-global-exports? (:deps ast)) ns-name (:def-emits-var opts)) - (cb {:value (*eval-fn* {:source (.toString sb)})}))))) + (cb (try + {:ns ns-name :value (*eval-fn* {:source (.toString sb)})} + (catch :default cause + (wrap-error (ana/error aenv "ERROR" cause))))))))) (let [src (with-out-str (comp/emit ast))] - (cb {:value (*eval-fn* {:source src})}))))))))) + (cb (try + {:value (*eval-fn* {:source src})} + (catch :default cause + (wrap-error (ana/error aenv "ERROR" cause))))))))))))) (defn eval "Evaluate a single ClojureScript form. The parameters: diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index a42e12d58..f89b8df07 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1557,6 +1557,28 @@ (ex-message (ex-cause (ex-cause error))))) (inc! l))))))))) +(deftest test-cljs-3287 + (async done + (let [st (cljs/empty-state) + l (latch 2 done)] + (cljs/eval-str st + "(throw (js/Error. \"eval error\"))" + nil + {:ns 'cljs.user + :target :nodejs + :eval node-eval} + (fn [{:keys [error]}] + (is (some? error)) + (inc! l))) + (cljs/eval st + '(throw (js/Error. "eval error")) + {:ns 'cljs.user + :target :nodejs + :eval node-eval} + (fn [{:keys [error]}] + (is (some? error)) + (inc! l)))))) + (defn -main [& args] (run-tests)) From 9d3b8ecbd3795047bf12f427ee73f048155dcd37 Mon Sep 17 00:00:00 2001 From: Matthew Huebert Date: Mon, 4 Jan 2021 12:54:28 -0500 Subject: [PATCH 3693/4033] CLJS-3286: ns form does not merge ns-info In self-host, the ns form overwrites existing mappings instead of merging them. As in Clojure, one should be able to call (ns X) to enter a namespace without modifying it. --- src/main/clojure/cljs/analyzer.cljc | 70 ++++++++++++++--------------- src/test/self/self_host/test.cljs | 13 ++++++ 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c9f7b7fb0..a21a7aa29 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3036,6 +3036,38 @@ (symbol (str name-str "$macros")) name))) +(defn- check-duplicate-aliases + [env old new] + (let [ns-name (:name old)] + (doseq [k [:requires :require-macros]] + (let [old-aliases (get old k) + new-aliases (get new k)] + (when-some [alias (some (set (keys new-aliases)) + (->> old-aliases + (remove (fn [[k v :as entry]] + (or (= k v) + (= entry (find new-aliases k))))) + keys))] + (throw (error env + (str "Alias " alias " already exists in namespace " ns-name + ", aliasing " (get old-aliases alias))))))))) + +(defn- merge-ns-info [old new env] + (if (pos? (count old)) + (let [deep-merge-keys + [:use-macros :require-macros :rename-macros + :uses :requires :renames :imports]] + #?(:clj + (when *check-alias-dupes* + (check-duplicate-aliases env old new))) + (merge + old + (select-keys new [:excludes]) + (merge-with merge + (select-keys old deep-merge-keys) + (select-keys new deep-merge-keys)))) + new)) + (defmethod parse 'ns [_ env [_ name & args :as form] _ opts] (when-not *allow-ns* @@ -3129,7 +3161,7 @@ :requires requires :renames (merge renames core-renames) :imports imports}] - (swap! env/*compiler* update-in [::namespaces name] merge ns-info) + (swap! env/*compiler* update-in [::namespaces name] merge-ns-info ns-info env) (merge {:op :ns :env env :form form @@ -3144,22 +3176,6 @@ (update-in [:requires] (fn [m] (with-meta m {(@reload :require) true}))))))))) -(defn- check-duplicate-aliases - [env old new] - (let [ns-name (:name old)] - (doseq [k [:requires :require-macros]] - (let [old-aliases (get old k) - new-aliases (get new k)] - (when-some [alias (some (set (keys new-aliases)) - (->> old-aliases - (remove (fn [[k v :as entry]] - (or (= k v) - (= entry (find new-aliases k))))) - keys))] - (throw (error env - (str "Alias " alias " already exists in namespace " ns-name - ", aliasing " (get old-aliases alias))))))))) - (defmethod parse 'ns* [_ env [_ quoted-specs :as form] _ opts] (when-let [not-quoted (->> (remove keyword? quoted-specs) @@ -3222,24 +3238,8 @@ :uses uses :requires requires :renames (merge renames core-renames) - :imports imports} - ns-info - (let [ns-info' (get-in @env/*compiler* [::namespaces name])] - (if (pos? (count ns-info')) - (let [merge-keys - [:use-macros :require-macros :rename-macros - :uses :requires :renames :imports]] - #?(:clj - (when *check-alias-dupes* - (check-duplicate-aliases env ns-info' require-info))) - (merge - ns-info' - {:excludes excludes} - (merge-with merge - (select-keys ns-info' merge-keys) - (select-keys require-info merge-keys)))) - require-info))] - (swap! env/*compiler* update-in [::namespaces name] merge ns-info) + :imports imports}] + (swap! env/*compiler* update-in [::namespaces name] merge-ns-info require-info env) (merge {:op :ns* :env env :form form diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index f89b8df07..854b1f1f7 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -871,6 +871,19 @@ (is (== 1 value)) (inc! l)))))) +(deftest test-ns-merge + (async done + (cljs/eval-str st + "(ns foo.bar (:require [bootstrap-test.core :refer [foo]])) + (ns foo.bar) + (foo 1 1)" + nil + {:eval node-eval + :load node-load} + (fn [{:keys [value error]}] + (is (nil? error)) + (done))))) + (deftest test-cljs-1651 (let [st (cljs/empty-state)] (async done From 2fb51dd5941be506ca7d8aa703a6b6a8fdefa4fd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 4 Jan 2021 13:21:15 -0500 Subject: [PATCH 3694/4033] revert last commit --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a21a7aa29..844a0362d 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3161,7 +3161,7 @@ :requires requires :renames (merge renames core-renames) :imports imports}] - (swap! env/*compiler* update-in [::namespaces name] merge-ns-info ns-info env) + (swap! env/*compiler* update-in [::namespaces name] merge ns-info) (merge {:op :ns :env env :form form From 6b9e8b1041ac2db797a3ad138fa46ef6280d33d4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 4 Jan 2021 13:23:36 -0500 Subject: [PATCH 3695/4033] revert test for CLJS-3286, incomplete --- src/test/self/self_host/test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 854b1f1f7..285383b26 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -871,7 +871,7 @@ (is (== 1 value)) (inc! l)))))) -(deftest test-ns-merge +#_(deftest test-ns-merge (async done (cljs/eval-str st "(ns foo.bar (:require [bootstrap-test.core :refer [foo]])) From 496cbc294d8503be38253dc6a5042fca721892a8 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 4 Jan 2021 13:53:32 -0500 Subject: [PATCH 3696/4033] update closure-library release script, there is no css directory anymore --- script/closure-library-release/closure-library-release.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index 59e1595a0..3b85659a8 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -84,7 +84,7 @@ fi now=$(date "+%Y%m%d%H%M%S") work_dir="tmp-build" -echo "Working directory: $work_dir" +echo "Working directory: $work_dir" rm -rf "$work_dir" mkdir "$work_dir" @@ -119,7 +119,6 @@ cp -r \ "$closure_library_dir/LICENSE" \ "$closure_library_dir/README.md" \ "$closure_library_dir/closure/goog" \ - "$closure_library_dir/closure/css" \ "$src_dir" cp -r \ From 715cdc07dbb1d595af91ea12affb6faf0b615d4b Mon Sep 17 00:00:00 2001 From: davidnolen Date: Thu, 4 Mar 2021 08:28:09 -0500 Subject: [PATCH 3697/4033] Bump Google Closure Compiler & Library Google Closure Compiler is now latest. GCL snapshot is from December. Address various minor breaking changes in GCC & GCL. GCL removed some long outstanding type detection helpers - goog/isFunction, goog/isString, goog/isArray. For isFunction add a new js-fn? macro which uses `typeOf` operator. For isString rely on string? now. for isArray, isNumber use goog/typeOf Throwable from goog common Java lib is now gone. Just print the Throwable for now. More GCL libs are now written in ES6. This broke self-parity because cljs.closure/transpile did not respect `:language-out`. Port GCC Transpiler implementation so we have more control. Self-parity build now uses :language-out :es6 and passes. Add tests for new transpile helpers. GCL now has a new import mechanism only for types `goog.requireType`. This signals to GCC that the file is required but only for type checking. Update cljs.js-deps so that `:require-type` is included when parsing JS files. Update cljs.closure/js-dependencies so that `:require-types` deps are inputs to the build so that GCC can type check. Add JS file parsing tests that check for `:require-types` in both the JS index and for individual file parsing. Update the CI script to adopt -M over -A. --- .github/workflows/test.yaml | 10 ++-- bin/cljsc | 2 +- deps.edn | 4 +- pom.template.xml | 4 +- project.clj | 6 +-- resources/self_parity_test.edn | 3 ++ script/bootstrap | 4 +- script/test-self-parity | 2 +- src/main/cljs/cljs/core.cljs | 22 ++++---- src/main/clojure/cljs/closure.clj | 65 ++++++++++++++--------- src/main/clojure/cljs/compiler.cljc | 2 +- src/main/clojure/cljs/core.cljc | 5 +- src/main/clojure/cljs/js_deps.cljc | 36 +++++++------ src/main/clojure/cljs/repl.cljc | 5 +- src/test/cljs/cljs/core_test.cljs | 6 +-- src/test/cljs/cljs/ns_test.cljs | 7 +-- src/test/clojure/cljs/js_deps_tests.clj | 22 ++++++++ src/test/clojure/cljs/transpile_tests.clj | 22 ++++++++ src/test/self/self_parity/auxiliary.cljs | 2 - 19 files changed, 150 insertions(+), 79 deletions(-) create mode 100644 src/test/clojure/cljs/js_deps_tests.clj create mode 100644 src/test/clojure/cljs/transpile_tests.clj diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 32c7287f8..33943a27e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -44,7 +44,7 @@ jobs: ${{ runner.os }}-jsc - name: Build tests - run: clojure -A:runtime.test.build + run: clojure -M:runtime.test.build - name: Install JSC run: ./ci/install_jsc.sh @@ -86,7 +86,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Build tests - run: clojure -A:selfhost.test.build + run: clojure -M:selfhost.test.build - name: Run tests run: | @@ -125,7 +125,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Build tests - run: clojure -A:selfparity.test.build + run: clojure -M:selfparity.test.build - name: Run tests run: | @@ -164,7 +164,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Run tests - run: clojure -A:compiler.test:compiler.test.run + run: clojure -M:compiler.test:compiler.test.run # CLI Tests cli-test: @@ -205,5 +205,5 @@ jobs: - name: Run tests run: | unset JAVA_TOOL_OPTIONS - clojure -A:cli.test.run | tee test-out.txt + clojure -M:cli.test.run | tee test-out.txt grep -qxF '0 failures, 0 errors.' test-out.txt diff --git a/bin/cljsc b/bin/cljsc index 4a866c998..a1b46c23d 100755 --- a/bin/cljsc +++ b/bin/cljsc @@ -13,7 +13,7 @@ if ! test "$(ls -A "$CLOJURESCRIPT_HOME/lib" 2>/dev/null)"; then fi CLJSC_CP='' -for next in lib/*: src/main/clojure: src/main/cljs: src/test/cljs; do +for next in lib/*: src/main/clojure: src/main/cljs: src/test/cljs: src/test/self; do CLJSC_CP="${CLJSC_CP}${CLOJURESCRIPT_HOME}/${next}" done diff --git a/deps.edn b/deps.edn index 332216f7d..19692100f 100644 --- a/deps.edn +++ b/deps.edn @@ -1,11 +1,11 @@ {:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps - {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20200315"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20210202"} com.cognitect/transit-clj {:mvn/version "0.8.309"} org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} - org.clojure/google-closure-library {:mvn/version "0.0-20191016-6ae1f72f"} + org.clojure/google-closure-library {:mvn/version "0.0-20201211-3e6c510d"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/tools.reader {:mvn/version "1.3.3"} org.clojure/test.check {:mvn/version "0.10.0-alpha3"}} diff --git a/pom.template.xml b/pom.template.xml index fe3f1f20d..5747d062b 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,12 +30,12 @@ com.google.javascript closure-compiler-unshaded - v20200315 + v20210202 org.clojure google-closure-library - 0.0-20191016-6ae1f72f + 0.0-20201211-3e6c510d org.clojure diff --git a/project.clj b/project.clj index 08d8af286..524445651 100644 --- a/project.clj +++ b/project.clj @@ -8,15 +8,15 @@ :source-paths ["src/main/clojure" "src/main/cljs"] :resource-paths ["src/main/cljs" "resources"] :test-paths ["src/test/clojure" "src/test/cljs" "src/test/self" "src/test/cljs_build" "src/test/cljs_cp"] - :dependencies [[org.clojure/clojure "1.10.0-alpha4"] + :dependencies [[org.clojure/clojure "1.10.0"] [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.3.3"] [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] - [org.clojure/google-closure-library "0.0-20191016-6ae1f72f"] - [com.google.javascript/closure-compiler-unshaded "v20200315"]] + [org.clojure/google-closure-library "0.0-20201211-3e6c510d"] + [com.google.javascript/closure-compiler-unshaded "v20210202"]] :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/self_parity_test.edn b/resources/self_parity_test.edn index db12d0605..502d1bcd8 100644 --- a/resources/self_parity_test.edn +++ b/resources/self_parity_test.edn @@ -1,5 +1,8 @@ {:optimizations :none :main self-parity.test + :language-in :es6 + :language-out :es6 + :verbose true :output-to "builds/out-self-parity/main.js" :output-dir "builds/out-self-parity" :cache-analysis-format :edn diff --git a/script/bootstrap b/script/bootstrap index 13b47e43d..378941b1d 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,10 +5,10 @@ set -e CLOJURE_RELEASE="1.9.0" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" -CLOSURE_RELEASE="20200315" +CLOSURE_RELEASE="20210202" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" -GCLOSURE_LIB_RELEASE="0.0-20191016-6ae1f72f" +GCLOSURE_LIB_RELEASE="0.0-20201211-3e6c510d" TREADER_RELEASE="1.3.3" TEST_CHECK_RELEASE="0.10.0-alpha3" diff --git a/script/test-self-parity b/script/test-self-parity index 48e428830..bb8f7a100 100755 --- a/script/test-self-parity +++ b/script/test-self-parity @@ -15,7 +15,7 @@ mkdir -p builds/out-self-parity/clojure/test mv clojure/template.clj builds/out-self-parity/clojure mv clojure/test builds/out-self-parity/clojure -if ! bin/cljsc src/test/self/self_parity "{:optimizations :none :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}"; then +if ! bin/cljsc src/test/self/self_parity "{:optimizations :simple :language-in :es6 :language-out :es5 :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}"; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f11b30223..fa71ac6e7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -273,7 +273,7 @@ (defn ^boolean string? "Returns true if x is a JavaScript string." [x] - (goog/isString x)) + (identical? "string" (goog/typeOf x))) (defn char? "Returns true if x is a JavaScript string of length one." @@ -2026,7 +2026,7 @@ reduces them without incurring seq initialization" (defn fn? "Return true if f is a JavaScript function or satisfies the Fn protocol." [f] - (or ^boolean (goog/isFunction f) (satisfies? Fn f))) + (or (js-fn? f) (satisfies? Fn f))) (deftype MetaFn [afn meta] IMeta @@ -2085,7 +2085,7 @@ reduces them without incurring seq initialization" "Returns an object of the same type and value as obj, with map m as its metadata." [o meta] - (if ^boolean (goog/isFunction o) + (if (js-fn? o) (MetaFn. o meta) (when-not (nil? o) (-with-meta o meta)))) @@ -6461,14 +6461,14 @@ reduces them without incurring seq initialization" ILookup (-lookup [coll k] (-lookup coll k nil)) (-lookup [coll k not-found] - (if (and ^boolean (goog/isString k) + (if (and (string? k) (not (nil? (scan-array 1 k keys)))) (unchecked-get strobj k) not-found)) IAssociative (-assoc [coll k v] - (if ^boolean (goog/isString k) + (if (string? k) (if (or (> update-count (.-HASHMAP_THRESHOLD ObjMap)) (>= (alength keys) (.-HASHMAP_THRESHOLD ObjMap))) (obj-map->hash-map coll k v) @@ -6484,14 +6484,14 @@ reduces them without incurring seq initialization" ;; non-string key. game over. (obj-map->hash-map coll k v))) (-contains-key? [coll k] - (if (and ^boolean (goog/isString k) + (if (and (string? k) (not (nil? (scan-array 1 k keys)))) true false)) IFind (-find [coll k] - (when (and ^boolean (goog/isString k) + (when (and (string? k) (not (nil? (scan-array 1 k keys)))) (MapEntry. k (unchecked-get strobj k) nil))) @@ -6510,7 +6510,7 @@ reduces them without incurring seq initialization" IMap (-dissoc [coll k] - (if (and ^boolean (goog/isString k) + (if (and (string? k) (not (nil? (scan-array 1 k keys)))) (let [new-keys (aclone keys) new-strobj (obj-clone strobj keys)] @@ -6624,7 +6624,7 @@ reduces them without incurring seq initialization" (cond (keyword? k) (array-index-of-keyword? arr k) - (or ^boolean (goog/isString k) (number? k)) + (or (string? k) (number? k)) (array-index-of-identical? arr k) (symbol? k) (array-index-of-symbol? arr k) @@ -10264,12 +10264,12 @@ reduces them without incurring seq initialization" (array? obj) (pr-sequential-writer writer pr-writer "#js [" " " "]" opts obj) - ^boolean (goog/isString obj) + (string? obj) (if (:readably opts) (-write writer (quote-string obj)) (-write writer obj)) - ^boolean (goog/isFunction obj) + (js-fn? obj) (let [name (.-name obj) name (if (or (nil? name) (gstring/isEmpty name)) "Function" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index a1f35faef..64b33352c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -37,9 +37,10 @@ CompilerInput CompilerInput$ModuleType DependencyOptions CompilerOptions$LanguageMode SourceMap$Format SourceMap$DetailLevel ClosureCodingConvention SourceFile - Result JSError CheckLevel DiagnosticGroups - CommandLineRunner AnonymousFunctionNamingPolicy - JSModule SourceMap VariableMap PrintStreamErrorManager] + Result JSError CheckLevel DiagnosticGroup DiagnosticGroups + CommandLineRunner + JSModule SourceMap VariableMap PrintStreamErrorManager DiagnosticType + VariableRenamingPolicy PropertyRenamingPolicy] [com.google.javascript.jscomp.bundle Transpiler] [com.google.javascript.jscomp.deps ClosureBundler ModuleLoader$ResolutionMode ModuleNames SimpleDependencyInfo] @@ -47,8 +48,7 @@ [java.nio.file Path Paths Files StandardWatchEventKinds WatchKey WatchEvent FileVisitor FileVisitResult FileSystems] [java.nio.charset Charset StandardCharsets] - [com.sun.nio.file SensitivityWatchEventModifier] - [com.google.common.base Throwables])) + [com.sun.nio.file SensitivityWatchEventModifier])) ;; Copied from clojure.tools.gitlibs @@ -162,7 +162,6 @@ :message-descriptions DiagnosticGroups/MESSAGE_DESCRIPTIONS :misplaced-msg-annotation DiagnosticGroups/MISPLACED_MSG_ANNOTATION :misplaced-type-annotation DiagnosticGroups/MISPLACED_TYPE_ANNOTATION - :missing-getcssname DiagnosticGroups/MISSING_GETCSSNAME :missing-override DiagnosticGroups/MISSING_OVERRIDE :missing-polyfill DiagnosticGroups/MISSING_POLYFILL :missing-properties DiagnosticGroups/MISSING_PROPERTIES @@ -175,7 +174,6 @@ :non-standard-jsdoc DiagnosticGroups/NON_STANDARD_JSDOC :report-unknown-types DiagnosticGroups/REPORT_UNKNOWN_TYPES :strict-missing-properties DiagnosticGroups/STRICT_MISSING_PROPERTIES - :strict-missing-require DiagnosticGroups/STRICT_MISSING_REQUIRE :strict-module-dep-check DiagnosticGroups/STRICT_MODULE_DEP_CHECK :strict-requires DiagnosticGroups/STRICT_REQUIRES :suspicious-code DiagnosticGroups/SUSPICIOUS_CODE @@ -262,15 +260,6 @@ (when (contains? opts :pseudo-names) (set! (.generatePseudoNames compiler-options) (:pseudo-names opts))) - (when (contains? opts :anon-fn-naming-policy) - (let [policy (:anon-fn-naming-policy opts)] - (set! (.anonymousFunctionNaming compiler-options) - (case policy - :off AnonymousFunctionNamingPolicy/OFF - :unmapped AnonymousFunctionNamingPolicy/UNMAPPED - :mapped AnonymousFunctionNamingPolicy/MAPPED - (throw (util/compilation-error (IllegalArgumentException. (str "Invalid :anon-fn-naming-policy value " policy " - only :off, :unmapped, :mapped permitted")))))))) - (when-let [lang-key (:language-in opts :ecmascript5)] (.setLanguageIn compiler-options (lang-key->lang-mode lang-key))) @@ -816,7 +805,8 @@ (if (seq requires) (let [node (or (get (@env/*compiler* :js-dependency-index) (first requires)) (deps/find-classpath-lib (first requires))) - new-req (remove #(contains? visited %) (:requires node))] + new-req (remove #(contains? visited %) + (into (:requires node) (:require-types node)))] (recur (into (rest requires) new-req) (into visited new-req) (conj deps node))) @@ -2052,18 +2042,45 @@ (.appendTo bundler sb module source) (.toString sb))) +(defn ^DiagnosticGroup es5-warnings [] + (DiagnosticGroup. + (into-array DiagnosticType + [(DiagnosticType/error "JSC_CANNOT_CONVERT" "")]))) + +(defn ^CompilerOptions transpile-options [] + (doto (CompilerOptions.) + (.setQuoteKeywordProperties true) + (.setSkipNonTranspilationPasses true) + (.setVariableRenaming VariableRenamingPolicy/OFF) + (.setPropertyRenaming PropertyRenamingPolicy/OFF) + (.setWrapGoogModulesForWhitespaceOnly false) + (.setPrettyPrint true) + (.setSourceMapOutputPath "/dev/null") + (.setSourceMapIncludeSourcesContent true) + (.setWarningLevel (es5-warnings) CheckLevel/OFF))) + +(defn closure-transpile + "Transpile a single JavaScript file to JavaScript. Used to lower Closure + Library files written in more recent versions of the JavaScript standard." + ([rsc opts] + (closure-transpile (util/path rsc) (slurp rsc) opts)) + ([path source opts] + (let [cc (make-closure-compiler) + cc-opts (set-options opts (transpile-options)) + externs (SourceFile/fromCode "externs.js" "function Symbol() {}") + source (SourceFile/fromCode path source) + result (.compile cc externs source cc-opts)] + ;; TODO: error handling + (.toSource cc)))) + ;; TODO: better error handling -;; TODO: actually respect the target :language-out level -;; currently just using default options for Transpiler (defn transpile - [{:keys [language-out] :or {language-out :es3}} rsc {:keys [module lang] :as js}] + [{:keys [language-out] :or {language-out :es3} :as opts} rsc {:keys [module lang] :as js}] (let [source (slurp rsc) source' (if (and lang (< (.indexOf lang-level (expand-lang-key language-out)) (.indexOf lang-level (expand-lang-key lang)))) - (let [cc (Transpiler/compilerSupplier) - result (.compile cc (url->nio-path rsc) source)] - (.source result)) + (closure-transpile (util/path rsc) source opts) source)] (str "/*TRANSPILED*/" (cond-> source' @@ -3286,7 +3303,7 @@ (if-let [f (opts-fn :watch-error-fn opts)] (f e) (binding [*out* *err*] - (println (Throwables/getStackTraceAsString e))))))) + (println e)))))) (watch-all [^Path root] (Files/walkFileTree root (reify diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 1f1e33053..a03b7b8ae 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -210,7 +210,7 @@ (nil? a) nil #?(:clj (map? a) :cljs (ana.impl/cljs-map? a)) (emit a) #?(:clj (seq? a) :cljs (ana.impl/cljs-seq? a)) (apply emits a) - #?(:clj (fn? a) :cljs ^boolean (goog/isFunction a)) (a) + #?(:clj (fn? a) :cljs (js-fn? a)) (a) :else (let [^String s (cond-> a (not (string? a)) .toString)] #?(:clj (when-some [^AtomicLong gen-col *source-map-data-gen-col*] (.addAndGet gen-col (.length s))) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index e7b31489f..50dbebca3 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -42,7 +42,7 @@ #?@(:cljs [alias coercive-not coercive-not= coercive-= coercive-boolean truth_ js-arguments js-delete js-in js-debugger exists? divide js-mod unsafe-bit-and bit-shift-right-zero-fill mask bitpos caching-hash - defcurried rfn specify! js-this this-as implements? array js-obj + defcurried rfn specify! js-this this-as implements? array js-obj js-fn? simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined? specify copy-arguments goog-define js-comment js-inline-comment unsafe-cast require-macros use-macros gen-apply-to-simple unchecked-get unchecked-set])]) @@ -982,6 +982,9 @@ (core/defmacro string? [x] (bool-expr (core/list 'js* "typeof ~{} === 'string'" x))) +(core/defmacro js-fn? [x] + (bool-expr (core/list 'js* "typeof ~{} === 'function'" x))) + (core/defmacro exists? "Return true if argument exists, analogous to usage of typeof operator in JavaScript." diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index d589dc671..2aa450af1 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -117,20 +117,22 @@ case." (map string/trim) (drop-while #(not (or (string/includes? % "goog.provide(") (string/includes? % "goog.module(") - (string/includes? % "goog.require(")))) + (string/includes? % "goog.require(") + (string/includes? % "goog.requireType(")))) (take-while #(not (re-matches #".*=[\s]*function\(.*\)[\s]*[{].*" %))) - (map #(re-matches #".*goog\.(provide|module|require)\(['\"](.*)['\"]\)" %)) + (map #(re-matches #".*goog\.(provide|module|require|requireType)\(['\"](.*)['\"]\)" %)) (remove nil?) (map #(drop 1 %)) (reduce (fn [m ns] (let [munged-ns (string/replace (last ns) "_" "-")] (case (first ns) - "provide" (conj-in m :provides munged-ns) - "module" (-> m - (conj-in :provides munged-ns) - (assoc :module :goog)) - "require" (conj-in m :requires munged-ns)))) - {:requires [] :provides []})))) + "provide" (conj-in m :provides munged-ns) + "module" (-> m + (conj-in :provides munged-ns) + (assoc :module :goog)) + "require" (conj-in m :requires munged-ns) + "requireType" (conj-in m :require-types munged-ns)))) + {:requires [] :provides [] :require-types []})))) (defprotocol IJavaScript (-foreign? [this] "Whether the Javascript represents a foreign @@ -319,11 +321,12 @@ JavaScript library containing provide/require 'declarations'." ; (enumeration-seq (.getResources (.getContextClassLoader (Thread/currentThread)) path))))) ;; NOTE: because this looks at deps.js for indexing the Closure Library we -;; don't need to bother parsing file in Closure Library. But it's also a +;; don't need to bother parsing files in Closure Library. But it's also a ;; potential source of confusion as *other* Closure style libs will need to be ;; parsed, user won't typically provide a deps.js (defn goog-dependencies* - "Create an index of Google dependencies by namespace and file name." + "Create an index of Google dependencies by namespace and file name from + goog/deps.js" [] (letfn [(parse-list [s] (when (> (count s) 0) (-> (.substring ^String s 1 (dec (count s))) @@ -337,12 +340,15 @@ JavaScript library containing provide/require 'declarations'." (map (fn [[file provides requires load-opts-str]] (let [{:strs [lang module]} - (-> (string/replace load-opts-str "'" "\"") (json/read-str))] + (-> (string/replace load-opts-str "'" "\"") (json/read-str)) + file' (str "goog/" file)] (merge - {:file (str "goog/" file) - :provides (parse-list provides) - :requires (parse-list requires) - :group :goog} + {:file file' + :provides (parse-list provides) + :requires (parse-list requires) + :require-types (-> file' io/resource io/reader line-seq + parse-js-ns :require-types) + :group :goog} (when module {:module (keyword module)}) (when lang diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 867077a6c..f3c2ce8e1 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -29,8 +29,7 @@ [java.util Base64] [java.util.concurrent.atomic AtomicLong] [clojure.lang IExceptionInfo] - [java.util.regex Pattern] - [com.google.common.base Throwables])) + [java.util.regex Pattern])) (def ^:dynamic *cljs-verbose* false) (def ^:dynamic *repl-opts* nil) @@ -483,7 +482,7 @@ (catch Throwable e (when (:repl-verbose opts) (println "Failed to canonicalize stacktrace") - (println (Throwables/getStackTraceAsString e)))))] + (println e))))] (if (vector? cst) (if (satisfies? IPrintStacktrace repl-env) (-print-stacktrace repl-env cst ret opts) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index cd48f71cf..0eff67b6a 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -146,9 +146,9 @@ (is (= (clj->js 1) 1)) (is (= (clj->js nil) (js* "null"))) (is (= (clj->js true) (js* "true"))) - (is (goog/isArray (clj->js []))) - (is (goog/isArray (clj->js #{}))) - (is (goog/isArray (clj->js '()))) + (is (goog/typeOf "array" (clj->js []))) + (is (goog/typeOf "array" (clj->js #{}))) + (is (goog/typeOf "array" (clj->js '()))) (is (goog/isObject (clj->js {}))) (is (= (gobject/get (clj->js {:a 1}) "a") 1)) (is (= (-> (clj->js {:a {:b {{:k :ey} :d}}}) diff --git a/src/test/cljs/cljs/ns_test.cljs b/src/test/cljs/cljs/ns_test.cljs index da7c0c8c7..83c48d306 100644 --- a/src/test/cljs/cljs/ns_test.cljs +++ b/src/test/cljs/cljs/ns_test.cljs @@ -39,6 +39,7 @@ (is (= (always-let [foo 42] foo) 42))) (deftest test-cljs-1677 - (is (.isNumber js/goog 3)) - (is (goog/isNumber 3)) - (is (goog-alias/isNumber 3))) + (let [array-like #js {:length 3}] + (is (.isArrayLike js/goog array-like)) + (is (goog/isArrayLike array-like)) + (is (goog-alias/isArrayLike array-like)))) diff --git a/src/test/clojure/cljs/js_deps_tests.clj b/src/test/clojure/cljs/js_deps_tests.clj new file mode 100644 index 000000000..8678d248d --- /dev/null +++ b/src/test/clojure/cljs/js_deps_tests.clj @@ -0,0 +1,22 @@ +;; 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.js-deps-tests + (:require [cljs.js-deps :as js-deps] + [clojure.java.io :as io] + [clojure.test :refer [deftest is run-tests]])) + +(deftest test-parse-js-ns-returns-require-types + (let [ns-info (js-deps/parse-js-ns + (line-seq (io/reader (io/resource "goog/events/eventhandler.js"))))] + (is (true? (contains? ns-info :require-types))))) + +(deftest test-js-dependency-index-has-require-types + (let [deps (js-deps/build-index (js-deps/goog-dependencies*)) + ns-info (get deps "goog.events.EventHandler")] + (is (true? (contains? ns-info :require-types))))) diff --git a/src/test/clojure/cljs/transpile_tests.clj b/src/test/clojure/cljs/transpile_tests.clj new file mode 100644 index 000000000..d43856eea --- /dev/null +++ b/src/test/clojure/cljs/transpile_tests.clj @@ -0,0 +1,22 @@ +;; 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.transpile-tests + (:require [cljs.closure :as closure :refer [closure-transpile]] + [clojure.java.io :as io] + [clojure.test :refer [deftest is run-tests]])) + +(deftest test-transpile-lang-in-lang-out + (let [source (closure-transpile + (io/resource "goog/async/throttle.js") + {:language-in :es6 :language-out :es6})] + (is (nil? (re-find #"jscomp" source)))) + (let [source (closure-transpile + (io/resource "goog/async/throttle.js") + {:language-in :es6 :language-out :es5})] + (is (some? (re-find #"jscomp" source))))) diff --git a/src/test/self/self_parity/auxiliary.cljs b/src/test/self/self_parity/auxiliary.cljs index 4ee2181f1..6d8707c8f 100644 --- a/src/test/self/self_parity/auxiliary.cljs +++ b/src/test/self/self_parity/auxiliary.cljs @@ -12,7 +12,6 @@ the compiler tests in bootstrap mode."} self-parity.auxiliary (:require - goog.Delay goog.Disposable goog.Promise goog.Throttle @@ -93,7 +92,6 @@ goog.locale goog.locale.TimeZoneFingerprint goog.locale.defaultLocaleNameConstants - goog.locale.genericFontNames goog.locale.timeZoneDetection goog.math goog.math.AffineTransform From 69b66374d857a176c5a9cd8cc0cbb7506876640d Mon Sep 17 00:00:00 2001 From: davidnolen Date: Fri, 5 Mar 2021 07:15:44 -0500 Subject: [PATCH 3698/4033] bump Clsoure to v20210302 --- deps.edn | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deps.edn b/deps.edn index 19692100f..b02f39572 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-unshaded {:mvn/version "v20210202"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20210302"} com.cognitect/transit-clj {:mvn/version "0.8.309"} org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} diff --git a/project.clj b/project.clj index 524445651..5bffd0e4e 100644 --- a/project.clj +++ b/project.clj @@ -16,7 +16,7 @@ [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20201211-3e6c510d"] - [com.google.javascript/closure-compiler-unshaded "v20210202"]] + [com.google.javascript/closure-compiler-unshaded "v20210302"]] :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/script/bootstrap b/script/bootstrap index 378941b1d..495c33005 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="20210202" +CLOSURE_RELEASE="20210302" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20201211-3e6c510d" From afbc4874518e26b800386ba2927fa532b2df7d90 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 12 Mar 2021 14:41:36 -0500 Subject: [PATCH 3699/4033] make REPL bootstrap helper idempotent - don't try to backup goog.isProvided_ or goog.require if already done --- script/test | 1 + src/main/clojure/cljs/repl/bootstrap.clj | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/script/test b/script/test index b21d32360..bfaef7e29 100755 --- a/script/test +++ b/script/test @@ -22,6 +22,7 @@ if ! bin/cljsc src/test/cljs "{:optimizations :advanced :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true + :pseudo-names true :language-in :es6 :language-out :es5 :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" diff --git a/src/main/clojure/cljs/repl/bootstrap.clj b/src/main/clojure/cljs/repl/bootstrap.clj index 5728a2808..99b10d135 100644 --- a/src/main/clojure/cljs/repl/bootstrap.clj +++ b/src/main/clojure/cljs/repl/bootstrap.clj @@ -17,12 +17,14 @@ ;; we never care how many times a namespace is loaded it doesn't matter if ;; Google Closure Library or ClojureScript (repl/evaluate-form repl-env env "" - '(set! (.-isProvided__ js/goog) js/goog.isProvided_)) + '(when-not (.-isProvided__ js/goog) + (set! (.-isProvided__ js/goog) js/goog.isProvided_))) (repl/evaluate-form repl-env env "" '(set! (.-isProvided_ js/goog) (fn [x] false))) ;; monkey-patch goog.require (repl/evaluate-form repl-env env "" - '(set! (.-require__ js/goog) js/goog.require)) + '(when-not (.-require__ js/goog) + (set! (.-require__ js/goog) js/goog.require))) (repl/evaluate-form repl-env env "" '(set! (.-require js/goog) (fn [src reload] From 73bd23c1c0623123e1aec33e8a35169ba84c3d07 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Mar 2021 09:43:55 -0400 Subject: [PATCH 3700/4033] revert minor change to test script --- script/test | 1 - 1 file changed, 1 deletion(-) diff --git a/script/test b/script/test index bfaef7e29..b21d32360 100755 --- a/script/test +++ b/script/test @@ -22,7 +22,6 @@ if ! bin/cljsc src/test/cljs "{:optimizations :advanced :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true - :pseudo-names true :language-in :es6 :language-out :es5 :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" From a1fb2048aea0eb9ec2dea94612832d14d767a086 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 15 Mar 2021 10:53:27 -0400 Subject: [PATCH 3701/4033] fix cljsReloadAll_ typo in cljs/repl/bootstrap.clj and clojure/browser/repl.cljs --- src/main/cljs/clojure/browser/repl.cljs | 2 +- src/main/clojure/cljs/repl/bootstrap.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 337408eee..891f7a03b 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -212,7 +212,7 @@ (fn [src reload] (when (= reload "reload-all") (set! (.-cljsReloadAll_ js/goog) true)) - (let [reload? (or reload (.-cljsReloadAll__ js/goog))] + (let [reload? (or reload (.-cljsReloadAll_ js/goog))] (when reload? (if (some? goog/debugLoader_) (let [path (.getPathFromDeps_ goog/debugLoader_ src)] diff --git a/src/main/clojure/cljs/repl/bootstrap.clj b/src/main/clojure/cljs/repl/bootstrap.clj index 99b10d135..be0ffefd9 100644 --- a/src/main/clojure/cljs/repl/bootstrap.clj +++ b/src/main/clojure/cljs/repl/bootstrap.clj @@ -30,7 +30,7 @@ (fn [src reload] (when (= reload "reload-all") (set! (.-cljsReloadAll_ js/goog) true)) - (let [reload? (or reload (.-cljsReloadAll__ js/goog))] + (let [reload? (or reload (.-cljsReloadAll_ js/goog))] (when reload? ;; check for new-ish private goog/debugLoader (if (some? goog/debugLoader_) From 48372e0a6dc79f3be560be2be2f47d2e5a4fb703 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Mar 2021 08:47:38 -0400 Subject: [PATCH 3702/4033] clone a specific WebKit release --- ci/install_jsc.sh | 2 +- src/main/cljs/cljs/core.cljs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/install_jsc.sh b/ci/install_jsc.sh index 0313c7b83..ed2fd3cb2 100755 --- a/ci/install_jsc.sh +++ b/ci/install_jsc.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash if [ ! -d WebKit ] then - git clone --depth=1 git://git.webkit.org/WebKit.git WebKit; + git clone -b Safari-611.1.5.1 --depth=1 git@github.com:WebKit/WebKit.git WebKit; cd WebKit; Tools/Scripts/build-jsc --jsc-only; cd .. diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fa71ac6e7..eda1acab3 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2918,7 +2918,7 @@ reduces them without incurring seq initialization" "Bitwise shift right" [x n] (cljs.core/bit-shift-right x n)) -(defn bit-shift-right-zero-fill +(defn ^int bit-shift-right-zero-fill "DEPRECATED: Bitwise shift right with zero fill" [x n] (cljs.core/bit-shift-right-zero-fill x n)) @@ -5394,7 +5394,7 @@ reduces them without incurring seq initialization" _ (pv-aset r 0 embed)] (recur (- ll 5) r))))) -(defn- push-tail [pv level parent tailnode] +(defn- push-tail [pv ^int level parent tailnode] (let [ret (pv-clone-node parent) subidx (bit-and (bit-shift-right-zero-fill (dec (.-cnt pv)) level) 0x01f)] (if (== 5 level) From 0b5502b57c4f04709e870b4509ea0d16983160c5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Mar 2021 09:04:41 -0400 Subject: [PATCH 3703/4033] use ubuntu-16.04 runner --- .github/workflows/test.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 33943a27e..a8a7e46b7 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: ubuntu-latest + runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 @@ -57,7 +57,7 @@ jobs: # Self-host Tests self-host-test: name: Self-host Tests - runs-on: ubuntu-latest + runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 @@ -96,7 +96,7 @@ jobs: # Self-parity Tests self-parity-test: name: Self-parity Tests - runs-on: ubuntu-latest + runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 @@ -135,7 +135,7 @@ jobs: # Compiler Tests compiler-test: name: Compiler Tests - runs-on: ubuntu-latest + runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 @@ -169,7 +169,7 @@ jobs: # CLI Tests cli-test: name: CLI Tests - runs-on: ubuntu-latest + runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 with: From a752299e97d432603d696bb5b608aa052f0cdbb9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Mar 2021 10:03:12 -0400 Subject: [PATCH 3704/4033] revert accidental type hints --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index eda1acab3..fa71ac6e7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2918,7 +2918,7 @@ reduces them without incurring seq initialization" "Bitwise shift right" [x n] (cljs.core/bit-shift-right x n)) -(defn ^int bit-shift-right-zero-fill +(defn bit-shift-right-zero-fill "DEPRECATED: Bitwise shift right with zero fill" [x n] (cljs.core/bit-shift-right-zero-fill x n)) @@ -5394,7 +5394,7 @@ reduces them without incurring seq initialization" _ (pv-aset r 0 embed)] (recur (- ll 5) r))))) -(defn- push-tail [pv ^int level parent tailnode] +(defn- push-tail [pv level parent tailnode] (let [ret (pv-clone-node parent) subidx (bit-and (bit-shift-right-zero-fill (dec (.-cnt pv)) level) 0x01f)] (if (== 5 level) From 3fdaabedb1343f434b6cb4f75fa28e748f96eff0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 16 Mar 2021 11:10:52 -0400 Subject: [PATCH 3705/4033] switch to OS X runner, use stable WebKit tag, drop lein - use depstar for uberjar step --- .github/workflows/test.yaml | 10 +++++----- ci/install_jsc.sh | 2 +- deps.edn | 5 ++++- script/uberjar | 3 +-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a8a7e46b7..c264439fc 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: ubuntu-16.04 + runs-on: macos-10.15 steps: - uses: actions/checkout@v2 @@ -57,7 +57,7 @@ jobs: # Self-host Tests self-host-test: name: Self-host Tests - runs-on: ubuntu-16.04 + runs-on: macos-10.15 steps: - uses: actions/checkout@v2 @@ -96,7 +96,7 @@ jobs: # Self-parity Tests self-parity-test: name: Self-parity Tests - runs-on: ubuntu-16.04 + runs-on: macos-10.15 steps: - uses: actions/checkout@v2 @@ -135,7 +135,7 @@ jobs: # Compiler Tests compiler-test: name: Compiler Tests - runs-on: ubuntu-16.04 + runs-on: macos-10.15 steps: - uses: actions/checkout@v2 @@ -169,7 +169,7 @@ jobs: # CLI Tests cli-test: name: CLI Tests - runs-on: ubuntu-16.04 + runs-on: macos-10.15 steps: - uses: actions/checkout@v2 with: diff --git a/ci/install_jsc.sh b/ci/install_jsc.sh index ed2fd3cb2..b5255c94c 100755 --- a/ci/install_jsc.sh +++ b/ci/install_jsc.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash if [ ! -d WebKit ] then - git clone -b Safari-611.1.5.1 --depth=1 git@github.com:WebKit/WebKit.git WebKit; + git clone -b Safari-611.1.5.1 --depth=1 https://github.com/WebKit/WebKit.git WebKit; cd WebKit; Tools/Scripts/build-jsc --jsc-only; cd .. diff --git a/deps.edn b/deps.edn index b02f39572..85dd95531 100644 --- a/deps.edn +++ b/deps.edn @@ -24,4 +24,7 @@ :selfparity.test.build {:extra-paths ["src/test/self"] :main-opts ["-i" "src/test/self/self_parity/setup.clj" "-e" "(self-parity.setup/-main)" - "-m" "cljs.main" "-co" "resources/self_parity_test.edn" "-c"]}}} + "-m" "cljs.main" "-co" "resources/self_parity_test.edn" "-c"]} + :uberjar {:extra-deps {com.github.seancorfield/depstar {:mvn/version "2.0.193"}} + :exec-fn hf.depstar/uberjar + :exec-args {:aot true}}}} diff --git a/script/uberjar b/script/uberjar index 7ab7b78c8..83d685e81 100755 --- a/script/uberjar +++ b/script/uberjar @@ -52,8 +52,7 @@ AOT_CACHE_FILE=`mktemp /tmp/core.cljs.cache.aot.edn.XXXXXXXXXXX` sed -e "s/0.0.0000/$MAJOR.$MINOR-$REVISION/" src/main/cljs/cljs/core.cljs.cache.aot.edn > $AOT_CACHE_FILE mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn -lein uberjar -mv target/clojurescript-0.0-SNAPSHOT-standalone.jar target/cljs.jar +clojure -X:uberjar :jar target/cljs.jar :compile-ns :all rm -f src/main/cljs/cljs/core.aot.js rm -f src/main/cljs/cljs/core.aot.js.map From a4673b880756531ac5690f7b4721ad76c0810327 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 30 Mar 2021 11:36:51 -0400 Subject: [PATCH 3706/4033] 1.10.844 --- README.md | 6 +++--- changes.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7dd37aaad..551d8f457 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.764 +Latest stable release: 1.10.844 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.764"] +[org.clojure/clojurescript "1.10.844"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.764 org.clojure clojurescript - 1.10.764 + 1.10.844 ``` diff --git a/changes.md b/changes.md index 365d5992c..396d91d85 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,31 @@ +## 1.10.844 + +### Changes +* Google Closure Compiler v20210302, Google Closure Library +* Update to tools.reader 1.3.3 + +### Enhancements +* CLJS-3235: Support accessing a property of a library as a namespace itself + +### Fixes +* CLJS-3287: selfhost: eval does not catch and return errors +* CLJS-3263: Always print #inst year with 4 digits +* CLJS-3291: Incorrect #inst parsing with respect to Julian / Gregorian calendar transition +* CLJS-3281: CLI help for target missing comma +* CLJ-3279: Error when :npm-deps is boolean and :install-deps true +* CLJS-3275: compute upstream npm-deps when :npm-deps is not set +* CLJS-3273: preserve ns-name when processing an :ns* op +* CLJS-3200: reduce code generated by destructure macro for maps +* CLJS-3271: nth on range produces nonexistent values considering floating point +* CLJS-3261: Docstring for cljs.js/eval-str specifies incorrect default for :context +* CLJS-2959: sort and sort-by should retain meta +* CLJS-3255: cljs.build.api/build doesn't work with single arity / 2-arity with nil +* CLJS-3019: Error->map should produce qualified symbols for :type +* CLJS-3130: UUID compares equal to other values +* CLJS-3257: `satisfies?` produces an inference warning when given an unhinted argument +* add `:nodejs-rt` to the list of build affecting options +* CLJS-2880: cl-format octal and Unicode character directives fail + ## 1.10.764 ### Fixes From a8422ee060d7d98f7578c9acb2824a5e346e7958 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Fri, 7 Aug 2020 16:41:21 +0200 Subject: [PATCH 3707/4033] CLJS-3276 Support macros that expand to require statements Support this pattern: (ns foo.bar (:require-macros [foo.baz :refer [macro-that-expands-to-require]])) (macro-that-expands-to-require) To do so we adapt ana/parse-ns to look for consecutive :ns/:ns* ops (ns and require forms), until a non :ns/:ns* form is encountered. The information of consecutive forms are merged so the ns-info that parse-ns returns is complete. This was already happening for :ns* ops (require forms), but not for :ns forms. This means that parse-ns analyzes/parses at least one form beyond the last ns/require form, but this can be problematic since this modifies the compiler env. For instance if the form after ns is a (def ^const ...) then analyzing this twice will cause an error. Hence the check to only analyze macro forms or ns/require forms. This should also help in general to avoid unnecessary work. To make sure the invocation is seen as a macro, and is able to be expanded, we need to keep track of namespace information in the env across multiple :ns/:ns* ops. When encountering a macro invocation we load the macro ns on the fly by invoking the ns-side-effects analyzer pass directly. After this we can call analyze on the form. --- src/main/clojure/cljs/analyzer.cljc | 84 ++++++++++++++++-------- src/test/cljs_build/cljs_3276/foo.cljs | 1 + src/test/cljs_build/cljs_3276/macros.clj | 4 ++ src/test/clojure/cljs/analyzer_tests.clj | 10 +++ 4 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 src/test/cljs_build/cljs_3276/foo.cljs create mode 100644 src/test/cljs_build/cljs_3276/macros.clj diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 844a0362d..3b4bf9fec 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4329,6 +4329,19 @@ (.lastIndexOf full-name "."))] (symbol (str "cljs.user." name (util/content-sha full-name 7))))))) +#?(:clj + (defn macro-call? [form env] + (when (and (seq? form) (seq form) (and (symbol? (first form)))) + (let [sym (first form) + nstr (namespace sym)] + (or (and (some? nstr) + (some? (gets env :ns :require-macros (symbol nstr)))) + (some? (gets env :ns :rename-macros sym)) + (some? (gets env :ns :use-macros sym))))))) + +#?(:clj + (declare ns-side-effects macroexpand-1)) + #?(:clj (defn ^:dynamic parse-ns "Helper for parsing only the essential namespace information from a @@ -4354,6 +4367,9 @@ (binding [env/*compiler* (if (false? (:restore opts)) env/*compiler* (atom @env/*compiler*)) + *file-defs* nil + #?@(:clj [*unchecked-if* false + *unchecked-arrays* false]) *cljs-ns* 'cljs.user *cljs-file* src *macro-infer* @@ -4370,7 +4386,8 @@ false)] (let [rdr (when-not (sequential? src) (io/reader src))] (try - (loop [forms (if rdr + (loop [env (empty-env) + forms (if rdr (forms-seq* rdr (source-path src)) src) ret (merge @@ -4385,8 +4402,15 @@ {:lines (with-open [reader (io/reader dest)] (-> reader line-seq count))}))] (if (seq forms) - (let [env (empty-env) - ast (no-warn (analyze env (first forms) nil opts))] + (let [form (first forms) + macro? (macro-call? form env) + env (if macro? + (binding [*load-macros* true] + (assoc (:env (ns-side-effects env (:ast ret) opts)) :ns (:ns env))) + env) + ast (when (or macro? (and (seq? form) ('#{ns ns* require use require-macros} (first form)))) + (no-warn (analyze env form nil opts))) + env (assoc (:env ast) :ns (:ns env))] (cond (= :ns (:op ast)) (let [ns-name (:name ast) @@ -4394,34 +4418,38 @@ (= "cljc" (util/ext src))) 'cljs.core$macros ns-name) - deps (merge (:uses ast) (:requires ast))] - (merge - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= 'cljs.core ns-name) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj constants-ns-sym))) - :file dest - :source-file (when rdr src) - :source-forms (when-not rdr src) - :ast ast - :macros-ns (or (:macros-ns opts) - (= 'cljs.core$macros ns-name))} - (when (and dest (.exists ^File dest)) - {:lines (with-open [reader (io/reader dest)] - (-> reader line-seq count))}))) + deps (merge (:uses ast) (:requires ast)) + env (assoc (:env ast) :ns (dissoc ast :env))] + (recur env + (rest forms) + (cond-> + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= 'cljs.core ns-name) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj constants-ns-sym))) + :file dest + :source-file (when rdr src) + :source-forms (when-not rdr src) + :ast ast + :macros-ns (or (:macros-ns opts) + (= 'cljs.core$macros ns-name))} + (and dest (.exists ^File dest)) + (assoc :lines (with-open [reader (io/reader dest)] + (-> reader line-seq count)))))) (= :ns* (:op ast)) (let [deps (merge (:uses ast) (:requires ast))] - (recur (rest forms) - (cond-> (update-in ret [:requires] into (set (vals deps))) - ;; we need to defer generating the user namespace - ;; until we actually need or it will break when - ;; `src` is a sequence of forms - António Monteiro - (not (:ns ret)) - (assoc :ns (gen-user-ns src) :provides [(gen-user-ns src)])))) + (recur (:env ast) + (rest forms) + (cond-> (update-in ret [:requires] into (set (vals deps))) + ;; we need to defer generating the user namespace + ;; until we actually need or it will break when + ;; `src` is a sequence of forms - António Monteiro + (not (:ns ret)) + (assoc :ns (gen-user-ns src) :provides [(gen-user-ns src)])))) :else ret)) ret)) diff --git a/src/test/cljs_build/cljs_3276/foo.cljs b/src/test/cljs_build/cljs_3276/foo.cljs new file mode 100644 index 000000000..898dbef8c --- /dev/null +++ b/src/test/cljs_build/cljs_3276/foo.cljs @@ -0,0 +1 @@ +(ns cljs-3276.foo) diff --git a/src/test/cljs_build/cljs_3276/macros.clj b/src/test/cljs_build/cljs_3276/macros.clj new file mode 100644 index 000000000..bef99f137 --- /dev/null +++ b/src/test/cljs_build/cljs_3276/macros.clj @@ -0,0 +1,4 @@ +(ns cljs-3276.macros) + +(defmacro macro-that-requires [] + `(require 'cljs-3276.foo)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 6c0041f92..90d3fd1d8 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1492,3 +1492,13 @@ '[(ns test.foo (:import goog))])) (is (= {} (get-in @cenv [::ana/namespaces 'test.foo :imports]))))) + +(deftest test-cljs-3276-require-from-macro + (let [cenv (env/default-compiler-env)] + (env/with-compiler-env cenv + (ana/analyze-form-seq + '[(ns test.foo + (:require-macros [cljs-3276.macros :refer [macro-that-requires]])) + (macro-that-requires)])) + (is (= '{cljs-3276.foo cljs-3276.foo} (get-in @cenv [::ana/namespaces 'test.foo :requires]))) + (is (contains? (get @cenv ::ana/namespaces) 'cljs-3276.foo)))) From db319f31a66740b86770929b74c67aee73ebd8d1 Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Wed, 7 Apr 2021 08:09:14 +0200 Subject: [PATCH 3708/4033] CLJS-3302 Fixup docstring for `default-dispatch-val` and `dispatch-fn` --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index fa71ac6e7..453f66354 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11381,11 +11381,11 @@ reduces them without incurring seq initialization" [multifn] (-prefers multifn)) (defn default-dispatch-val - "Given a multimethod, return it's default-dispatch-val." + "Given a multimethod, return its default-dispatch-val." [multifn] (-default-dispatch-val multifn)) (defn dispatch-fn - "Given a multimethod, return it's dispatch-fn." + "Given a multimethod, return its dispatch-fn." [multifn] (-dispatch-fn multifn)) ;; UUID From 0951a99c70cec7b8f3f2eb575430c05cf7ab14f7 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Fri, 23 Apr 2021 23:47:55 -0400 Subject: [PATCH 3709/4033] CLJS-3298: visibility diagnostic group typo --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 64b33352c..4efbd0d61 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -188,7 +188,7 @@ :unused-local-variable DiagnosticGroups/UNUSED_LOCAL_VARIABLE :unused-private-property DiagnosticGroups/UNUSED_PRIVATE_PROPERTY :violated-module-dep DiagnosticGroups/VIOLATED_MODULE_DEP - :visiblity DiagnosticGroups/VISIBILITY}) + :visibility DiagnosticGroups/VISIBILITY}) (def known-opts "Set of all known compiler options." From aa2f0e60afb7bf749d326bcb715696bac8b832e5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 20 Jan 2021 17:03:43 -0500 Subject: [PATCH 3710/4033] CLJS-3296: Update conj docstring for missing arities --- src/main/cljs/cljs/core.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 453f66354..cfa48f7ca 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1795,8 +1795,10 @@ reduces them without incurring seq initialization" (defn conj "conj[oin]. Returns a new collection with the xs - 'added'. (conj nil item) returns (item). The 'addition' may - happen at different 'places' depending on the concrete type." + 'added'. (conj nil item) returns (item). + (conj coll) returns coll. (conj) returns []. + The 'addition' may happen at different 'places' depending + on the concrete type." ([] []) ([coll] coll) ([coll x] From 184083e53c53c575eb7805348a974f53f88c40a2 Mon Sep 17 00:00:00 2001 From: Wilker Lucio Date: Sat, 10 Oct 2020 07:15:42 -0300 Subject: [PATCH 3711/4033] CLJS-3283 Support -contains-key? protocol check in contains? --- src/main/cljs/cljs/core.cljs | 8 +++++++- src/test/cljs/cljs/core_test.cljs | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index cfa48f7ca..5e0fe0f54 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2360,8 +2360,14 @@ reduces them without incurring seq initialization" range of indexes. 'contains?' operates constant or logarithmic time; it will not perform a linear search for a value. See also 'some'." [coll v] - (if (identical? (get coll v lookup-sentinel) lookup-sentinel) + (cond + (implements? IAssociative coll) + (-contains-key? coll v) + + (identical? (get coll v lookup-sentinel) lookup-sentinel) false + + :else true)) (defn find diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 0eff67b6a..95bebf3d2 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -84,7 +84,14 @@ (is (not (contains? (to-array [5 6 7]) 3))) (is (not (contains? nil 42))) (is (contains? "f" 0)) - (is (not (contains? "f" 55))))) + (is (not (contains? "f" 55)))) + + (testing "Testing contains? with IAssociative protocol" + (let [ds (reify + IAssociative + (-contains-key? [_ k] (= k :valid)))] + (is (contains? ds :valid)) + (is (not (contains? ds :invalid)))))) (deftest test-run! (testing "Testing run!" From 5091bab07e8e60f64d06e43bf07ba08204071b0d Mon Sep 17 00:00:00 2001 From: davidnolen Date: Fri, 23 Apr 2021 23:56:21 -0400 Subject: [PATCH 3712/4033] CLJS-3282: document bundle target in CLI help --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 8b73d8dba..f404fac9a 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -629,7 +629,7 @@ present" :doc (str "The JavaScript target. Configures environment bootstrap and " "defaults to browser. Supported values: node or nodejs, " - "webworker, none") } + "webworker, bundle, none") } ["-ro" "--repl-opts"] {:group ::main&compile :fn repl-env-opts-opt :arg "edn" :doc (str "Options to configure the repl-env, can be an EDN string or " From fe0c6e9341a3c3613bbd90cf897a9c96b2cccd4f Mon Sep 17 00:00:00 2001 From: Dominic Monroe Date: Sat, 24 Apr 2021 08:42:31 +0100 Subject: [PATCH 3713/4033] CLJS-3284: Use of private deftype by public function in another namespace when inside an if causes warning Suppress warnings in `type?` which are of private-var-access. A lot of type inference is automatic, and users don't necessarily know that it's happening. The type itself isn't secret in this context, even if the library indicates the user shouldn't rely on the exact existence of the type. --- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/test/cljs_build/cljs_3284/bean.cljs | 7 +++++++ src/test/cljs_build/cljs_3284/core.cljs | 9 +++++++++ src/test/clojure/cljs/build_api_tests.clj | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/test/cljs_build/cljs_3284/bean.cljs create mode 100644 src/test/cljs_build/cljs_3284/core.cljs diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3b4bf9fec..ff5c080a8 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1361,7 +1361,8 @@ [env t] ;; don't use resolve-existing-var to avoid warnings (when (and (some? t) (symbol? t)) - (let [var (resolve-var env t)] + (let [var (binding [*private-var-access-nowarn* true] + (resolve-var env t))] (if-some [type (:type var)] type (if-some [type (-> var :info :type)] diff --git a/src/test/cljs_build/cljs_3284/bean.cljs b/src/test/cljs_build/cljs_3284/bean.cljs new file mode 100644 index 000000000..df790949d --- /dev/null +++ b/src/test/cljs_build/cljs_3284/bean.cljs @@ -0,0 +1,7 @@ +(ns cljs-3284.bean) + +(deftype ^:private SomeType [a]) + +(defn some-type + [a] + (SomeType. a)) diff --git a/src/test/cljs_build/cljs_3284/core.cljs b/src/test/cljs_build/cljs_3284/core.cljs new file mode 100644 index 000000000..e5c026825 --- /dev/null +++ b/src/test/cljs_build/cljs_3284/core.cljs @@ -0,0 +1,9 @@ +(ns cljs-3284.core + (:require + cljs-3284.bean)) + +(defn maybe-bean + [x] + (if (object? x) + (cljs-3284.bean/some-type x) + x)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index df91fcefe..fbfaf2692 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -768,3 +768,17 @@ (is (empty? @ws)))) (.delete (io/file "package.json")) (test/delete-node-modules)) + +(deftest test-cljs-3284 + (testing "Type hint warnings don't fire just because of private types" + (let [ws (atom []) + out (.getPath (io/file (test/tmp-dir) "cljs-3235-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'cljs-3284.core + :output-dir out + :optimizations :none}} + cenv (env/default-compiler-env opts)] + (test/delete-out-files out) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (build/build (build/inputs (io/file inputs "cljs_3284/core.cljs")) opts cenv)) + (is (empty? @ws))))) From 5b1a7f0914b5aa50aac2deb2fbfce4e595243f7d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 24 Apr 2021 12:48:34 -0400 Subject: [PATCH 3714/4033] CLJS-3304: Higher order checked arrays --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ff5c080a8..76461af3b 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1235,7 +1235,7 @@ :ns current-ns})) (core-name? env sym) - (do + (let [sym (resolve-alias 'cljs.core sym)] (when (some? confirm) (confirm env 'cljs.core sym)) (merge (gets @env/*compiler* ::namespaces 'cljs.core :defs sym) From 6abd05d12cb8499d43d1d4f57c559e4d44742c03 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 22 Apr 2021 23:40:35 -0400 Subject: [PATCH 3715/4033] CLJS-3303: checked arrays enabled in advanced --- src/main/clojure/cljs/analyzer.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 76461af3b..2a0f434ae 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -176,7 +176,7 @@ "Returns false-y, :warn, or :error based on configuration and the current value of *unchecked-arrays*." [] - (when (and (not (:advanced (compiler-options))) + (when (and (not= :advanced (:optimizations (compiler-options))) (not *unchecked-arrays*)) *checked-arrays*)) @@ -4473,7 +4473,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]))) + :cache-key :checked-arrays :language-out :optimizations]))) #?(:clj (defn build-affecting-options-sha [path opts] From 168b8a431901e215a5748ba688272984171d0327 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 25 Apr 2021 11:32:08 -0400 Subject: [PATCH 3716/4033] CLJS-3305: defrecord must implement IAssociative -contains-key? --- src/main/clojure/cljs/core.cljc | 6 ++++++ src/test/cljs/cljs/core_test.cljs | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 50dbebca3..3c9aa540a 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1866,6 +1866,12 @@ this# entry#))) 'IAssociative + `(~'-contains-key? [this# ~ksym] + ~(if (seq base-fields) + `(case ~ksym + (~@(map keyword base-fields)) true + (cljs.core/contains? ~'__extmap ~ksym)) + `(cljs.core/contains? ~'__extmap ~ksym))) `(~'-assoc [this# k# ~gs] (condp keyword-identical? k# ~@(mapcat (core/fn [fld] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 95bebf3d2..9c51d0600 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1826,3 +1826,21 @@ (deftest test-cljs-3271 (is (== 0.6 (nth (range 0 1 0.1) 6)))) + +(defrecord CLJS3305A []) +(defrecord CLJS3305B [a b]) + +(deftest test-cljs-3305 + (let [empty-basis (->CLJS3305A) + nonempty-basis (->CLJS3305B 1 2) + empty-extended (assoc empty-basis :y 1) + nonempty-extended (assoc nonempty-basis :y 1)] + (is (false? (contains? empty-basis :a))) + (is (true? (contains? nonempty-basis :a))) + (is (false? (contains? nonempty-basis :c))) + (is (true? (contains? empty-extended :y))) + (is (false? (contains? empty-extended :z))) + (is (true? (contains? nonempty-extended :a))) + (is (false? (contains? nonempty-extended :c))) + (is (true? (contains? nonempty-extended :y))) + (is (false? (contains? nonempty-extended :z))))) From 8cb81dbb986e67a97d466c7c92973758cfa0f6b2 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 25 Apr 2021 20:26:01 -0400 Subject: [PATCH 3717/4033] CLJS-3307: Allow extending IAssociative -contains-key? to native --- src/main/cljs/cljs/core.cljs | 3 +++ src/test/cljs/cljs/extend_to_native_test.cljs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5e0fe0f54..7ce58222c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2364,6 +2364,9 @@ reduces them without incurring seq initialization" (implements? IAssociative coll) (-contains-key? coll v) + (native-satisfies? IAssociative coll) + (-contains-key? coll v) + (identical? (get coll v lookup-sentinel) lookup-sentinel) false diff --git a/src/test/cljs/cljs/extend_to_native_test.cljs b/src/test/cljs/cljs/extend_to_native_test.cljs index e0d7446b5..0513f2f9a 100644 --- a/src/test/cljs/cljs/extend_to_native_test.cljs +++ b/src/test/cljs/cljs/extend_to_native_test.cljs @@ -144,3 +144,11 @@ (deftest test-protocol-with-slash (is (= "result" (/ "")))) + +(deftest test-cljs-3307 + (extend-type object + IAssociative + (-contains-key? [_ k] (= k :valid))) + + (is (contains? #js {} :valid)) + (is (not (contains? #js {} :invalid)))) From 4a73bc8b4c95cfedc614dcabb0fe1795da371d37 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 25 Apr 2021 18:32:04 -0400 Subject: [PATCH 3718/4033] CLJS-3306: subvecs must implement IAssociative -contains-key? --- src/main/cljs/cljs/core.cljs | 4 ++++ src/test/cljs/cljs/core_test.cljs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7ce58222c..867f4232e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5943,6 +5943,10 @@ reduces them without incurring seq initialization" (if (number? key) (-assoc-n coll key val) (throw (js/Error. "Subvec's key for assoc must be a number.")))) + (-contains-key? [coll key] + (if (integer? key) + (and (<= 0 key) (< key (- end start))) + false)) IFind (-find [coll n] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 9c51d0600..65ffcc631 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1844,3 +1844,13 @@ (is (false? (contains? nonempty-extended :c))) (is (true? (contains? nonempty-extended :y))) (is (false? (contains? nonempty-extended :z))))) + +(deftest test-cljs-3306 + (let [sv (subvec [0 1 2 3 4] 2 4)] + (is (true? (contains? sv 0))) + (is (false? (contains? sv 0.5))) + (is (true? (contains? sv 1))) + (is (false? (contains? sv 1.5))) + (is (false? (contains? sv :kw)))) + (let [sv (subvec [0 1 2 3 4] 2 2)] + (is (false? (contains? sv 0))))) From d82b10a904eb0d4b066e62bd6c1c6509eb3d91d8 Mon Sep 17 00:00:00 2001 From: Dieter Komendera Date: Sat, 16 Jan 2021 22:53:09 +0100 Subject: [PATCH 3719/4033] CLJS-3293: Some npm packages fail to require --- src/main/clojure/cljs/closure.clj | 42 +++++++++++++------------ src/test/clojure/cljs/closure_tests.clj | 14 +++++++++ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 4efbd0d61..85c94ae60 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2733,7 +2733,7 @@ [])))) (defn- node-file-seq->libs-spec* - [module-fseq] + [module-fseq opts] (letfn [(package-json? [path] (boolean (re-find #"node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$" path)))] (let [pkg-jsons (into {} @@ -2756,24 +2756,26 @@ :module-type :es6} (when-not (package-json? path) (let [pkg-json-main (some - (fn [[pkg-json-path {:strs [main name]}]] - (when-not (nil? main) - ;; should be the only edge case in - ;; the package.json main field - Antonio - (let [main (cond-> main - (string/starts-with? main "./") - (subs 2)) - main-path (-> pkg-json-path - (string/replace \\ \/) - trim-package-json - (str main))] - (some (fn [candidate] - (when (= candidate (string/replace path \\ \/)) - name)) - (cond-> [main-path] - (not (or (string/ends-with? main-path ".js") - (string/ends-with? main-path ".json"))) - (into [(str main-path ".js") (str main-path "/index.js") (str main-path ".json")])))))) + (fn [[pkg-json-path {:as pkg-json :strs [name]}]] + (let [entries (package-json-entries opts) + entry (first (keep (partial get pkg-json) entries))] + (when-not (nil? entry) + ;; should be the only edge case in + ;; the package.json main field - Antonio + (let [entry (cond-> entry + (string/starts-with? entry "./") + (subs 2)) + entry-path (-> pkg-json-path + (string/replace \\ \/) + trim-package-json + (str entry))] + (some (fn [candidate] + (when (= candidate (string/replace path \\ \/)) + name)) + (cond-> [entry-path] + (not (or (string/ends-with? entry-path ".js") + (string/ends-with? entry-path ".json"))) + (into [(str entry-path ".js") (str entry-path "/index.js") (str entry-path ".json")]))))))) pkg-jsons)] {:provides (let [module-rel-name (-> (subs path (.lastIndexOf path "node_modules")) (string/replace \\ \/) @@ -2797,7 +2799,7 @@ (:options @env/*compiler*)))) ([opts] (let [module-fseq (util/module-file-seq)] - (node-file-seq->libs-spec module-fseq)))) + (node-file-seq->libs-spec module-fseq opts)))) (defn preprocess-js "Given js-module map, apply preprocessing defined by :preprocess value in the map." diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 8e90870dc..b35cee9f7 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -159,6 +159,20 @@ "jss-extend/lib"]})) modules)))) (.delete (io/file "package.json")) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (closure/maybe-install-node-deps! {:npm-deps {"@codemirror/state" "0.17.1"}}) + (let [modules (closure/index-node-modules-dir)] + (is (true? (some (fn [module] + (= module + {:file (.getAbsolutePath (io/file "node_modules/@codemirror/state/dist/index.js")) + :module-type :es6 + :provides ["@codemirror/state/dist/index.js" + "@codemirror/state/dist/index" + "@codemirror/state" + "@codemirror/state/dist"]})) + modules)))) + (.delete (io/file "package.json")) (test/delete-node-modules)) (deftest test-index-node-modules-module-deps-js From 927f221f8fc26a49db7d0dcfd1d70008a986fd8f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 12 May 2021 15:48:56 -0400 Subject: [PATCH 3720/4033] CLJS-3260: and/or optimization as compiler pass (#94) Introduce and/or optimization as a compiler pass. Tests for all the various interesting cases. No serious compiler perf regression, examine the codegen for persistent data structures looks good. --- .../cljs/cljs/analyzer/passes/and_or.cljc | 97 ++++++++++++++ src/main/clojure/cljs/analyzer.cljc | 6 +- src/main/clojure/cljs/core.cljc | 30 +---- src/test/clojure/cljs/analyzer_pass_tests.clj | 118 ++++++++++++++++++ 4 files changed, 223 insertions(+), 28 deletions(-) create mode 100644 src/main/cljs/cljs/analyzer/passes/and_or.cljc create mode 100644 src/test/clojure/cljs/analyzer_pass_tests.clj diff --git a/src/main/cljs/cljs/analyzer/passes/and_or.cljc b/src/main/cljs/cljs/analyzer/passes/and_or.cljc new file mode 100644 index 000000000..2cc617b46 --- /dev/null +++ b/src/main/cljs/cljs/analyzer/passes/and_or.cljc @@ -0,0 +1,97 @@ +;; 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.and-or) + +(def simple-ops + #{:var :js-var :local :invoke :const :host-field :host-call :js :quote}) + +(defn ->expr-env [ast] + (assoc-in ast [:env :context] :expr)) + +(defn simple-op? [ast] + (contains? simple-ops (:op ast))) + +(defn simple-test-expr? + [{:keys [op] :as ast}] + (boolean + (and (simple-op? ast) + ('#{boolean seq} + (or (:tag ast) + (when (#{:local :var} op) + (-> ast :info :tag))))))) + +(defn single-binding-let? [ast] + (and (= :let (:op ast)) + (= 1 (count (-> ast :bindings))))) + +(defn no-statements? [let-ast] + (= [] (-> let-ast :body :statements))) + +(defn returns-if? [let-ast] + (= :if (-> let-ast :body :ret :op))) + +(defn simple-test-binding-let? [ast] + (and (single-binding-let? ast) + (no-statements? ast) + (simple-test-expr? (-> ast :bindings first :init)) + (returns-if? ast))) + +(defn test=then? [if-ast] + ;; remove :env, if same, local will differ only by + ;; :context (:expr | :statement) + (= (dissoc (:test if-ast) :env) + (dissoc (:then if-ast) :env))) + +(defn test=else? [if-ast] + ;; remove :env, if same, local will differ only by + ;; :context (:expr | :statement) + (= (dissoc (:test if-ast) :env) + (dissoc (:else if-ast) :env))) + +(defn simple-and? [ast] + (and (simple-test-binding-let? ast) + (test=else? (-> ast :body :ret)))) + +(defn simple-or? [ast] + (and (simple-test-binding-let? ast) + (test=then? (-> ast :body :ret)))) + +(defn optimizable-and? [ast] + (and (simple-and? ast) + (simple-test-expr? (-> ast :body :ret :then)))) + +(defn optimizable-or? [ast] + (and (simple-or? ast) + (simple-test-expr? (-> ast :body :ret :else)))) + +(defn optimize-and [ast] + {:op :js + :env (:env ast) + :segs ["((" ") && (" "))"] + :args [(-> ast :bindings first :init) + (->expr-env (-> ast :body :ret :then))] + :form (:form ast) + :children [:args] + :tag 'boolean}) + +(defn optimize-or [ast] + {:op :js + :env (:env ast) + :segs ["((" ") || (" "))"] + :args [(-> ast :bindings first :init) + (->expr-env (-> ast :body :ret :else))] + :form (:form ast) + :children [:args] + :tag 'boolean}) + +(defn optimize [env ast _] + (cond + (optimizable-and? ast) (optimize-and ast) + (optimizable-or? ast) (optimize-or ast) + :else ast)) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2a0f434ae..f1d01cbc2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -14,6 +14,7 @@ no-warn with-warning-handlers wrapping-errors]] [cljs.env.macros :refer [ensure]])) #?(:clj (:require [cljs.analyzer.impl :as impl] + [cljs.analyzer.passes.and-or :as and-or] [cljs.env :as env :refer [ensure]] [cljs.externs :as externs] [cljs.js-deps :as deps] @@ -26,6 +27,7 @@ [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers]) :cljs (:require [cljs.analyzer.impl :as impl] + [cljs.analyzer.passes.and-or :as and-or] [cljs.env :as env] [cljs.reader :as edn] [cljs.tagged-literals :as tags] @@ -4194,8 +4196,8 @@ tag (assoc :tag tag)))))) (def default-passes - #?(:clj [infer-type check-invoke-arg-types ns-side-effects] - :cljs [infer-type check-invoke-arg-types])) + #?(:clj [infer-type and-or/optimize check-invoke-arg-types ns-side-effects] + :cljs [infer-type and-or/optimize check-invoke-arg-types])) (defn analyze* [env form name opts] (let [passes *passes* diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3c9aa540a..61b705f93 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -872,22 +872,8 @@ ([] true) ([x] x) ([x & next] - (core/let [forms (concat [x] next)] - (core/cond - (every? #(simple-test-expr? &env %) - (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) forms)) - (core/let [and-str (core/->> (repeat (count forms) "(~{})") - (interpose " && ") - (#(concat ["("] % [")"])) - (apply core/str))] - (bool-expr `(~'js* ~and-str ~@forms))) - - (typed-expr? &env x '#{boolean}) - `(if ~x (and ~@next) false) - - :else - `(let [and# ~x] - (if and# (and ~@next) and#)))))) + `(let [and# ~x] + (if and# (and ~@next) and#)))) (core/defmacro or "Evaluates exprs one at a time, from left to right. If a form @@ -897,16 +883,8 @@ ([] nil) ([x] x) ([x & next] - (core/let [forms (concat [x] next)] - (if (every? #(simple-test-expr? &env %) - (map #(cljs.analyzer/no-warn (cljs.analyzer/analyze &env %)) forms)) - (core/let [or-str (core/->> (repeat (count forms) "(~{})") - (interpose " || ") - (#(concat ["("] % [")"])) - (apply core/str))] - (bool-expr `(~'js* ~or-str ~@forms))) - `(let [or# ~x] - (if or# or# (or ~@next))))))) + `(let [or# ~x] + (if or# or# (or ~@next))))) (core/defmacro nil? [x] `(coercive-= ~x nil)) diff --git a/src/test/clojure/cljs/analyzer_pass_tests.clj b/src/test/clojure/cljs/analyzer_pass_tests.clj new file mode 100644 index 000000000..76521e7df --- /dev/null +++ b/src/test/clojure/cljs/analyzer_pass_tests.clj @@ -0,0 +1,118 @@ +;; 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-pass-tests + (:require [cljs.analyzer :as ana] + [cljs.analyzer.passes.and-or :as and-or] + [cljs.analyzer-tests :as ana-tests :refer [analyze]] + [cljs.compiler :as comp] + [cljs.compiler-tests :as comp-tests :refer [compile-form-seq emit]] + [cljs.env :as env] + [clojure.string :as string] + [clojure.test :as test :refer [deftest is testing]])) + +(deftest test-and-or-code-gen-pass + (testing "and/or optimization code gen pass" + (let [expr-env (assoc (ana/empty-env) :context :expr) + ast (->> `(and true false) + (analyze expr-env)) + code (with-out-str (emit ast))] + (is (= code "((true) && (false))"))) + (let [expr-env (assoc (ana/empty-env) :context :expr) + ast (analyze expr-env + `(and true (or true false))) + code (with-out-str (emit ast))] + (is (= code "((true) && (((true) || (false))))"))) + (let [expr-env (assoc (ana/empty-env) :context :expr) + ast (analyze expr-env + `(or true (and false true))) + code (with-out-str (emit ast))] + (is (= code "((true) || (((false) && (true))))"))) + (let [expr-env (assoc (ana/empty-env) :context :expr) + local (gensym) + ast (analyze expr-env + `(let [~local true] + (and true (or ~local false)))) + code (with-out-str (emit ast))] + (is (= code + (string/replace + "(function (){var $SYM = true;\nreturn ((true) && ((($SYM) || (false))));\n})()" + "$SYM" (str local))))))) + +(deftest test-and-or-local + (testing "and/or optimizable with boolean local" + (let [expr-env (assoc (ana/empty-env) :context :expr) + ast (->> `(let [x# true] + (and x# true false)) + (analyze expr-env)) + code (with-out-str (emit ast))] + (is (= 2 (count (re-seq #"&&" code))))))) + +(deftest test-and-or-boolean-fn-arg + (testing "and/or optimizable with boolean fn arg" + (let [arg (with-meta 'x {:tag 'boolean}) + ast (analyze (assoc (ana/empty-env) :context :expr) + `(fn [~arg] + (and ~arg false false))) + code (with-out-str (emit ast))] + (is (= 2 (count (re-seq #"&&" code))))))) + +(deftest test-and-or-boolean-var + (testing "and/or optimizable with boolean var" + (let [code (env/with-compiler-env (env/default-compiler-env) + (compile-form-seq + '[(ns foo.bar) + (def baz true) + (defn woz [] + (and baz false))]))] + (is (= 1 (count (re-seq #"&&" code))))))) + +(deftest test-and-or-js-boolean-var + (testing "and/or optimizable with js boolean var" + (let [code (env/with-compiler-env (env/default-compiler-env) + (compile-form-seq + '[(ns foo.bar) + (defn baz [] + (and ^boolean js/woz false))]))] + (is (= 1 (count (re-seq #"&&" code))))))) + +(deftest test-and-or-host-call + (testing "and/or optimizable with host call" + (let [code (env/with-compiler-env (env/default-compiler-env) + (compile-form-seq + '[(ns foo.bar) + (defn bar [x] + (and ^boolean (.woz x) false))]))] + (is (= 1 (count (re-seq #"&&" code))))))) + +(deftest test-and-or-host-field + (testing "and/or optimizable with host field" + (let [code (env/with-compiler-env (env/default-compiler-env) + (compile-form-seq + '[(ns foo.bar) + (defn bar [x] + (and ^boolean (.-woz x) false))]))] + (is (= 1 (count (re-seq #"&&" code))))))) + +(deftest test-core-predicates + (testing "and/or optimizable with core predicates" + (let [code (env/with-compiler-env (env/default-compiler-env) + (comp/with-core-cljs {} + (fn [] + (compile-form-seq + '[(ns foo.bar) + (defn bar [] + (and (even? 1) false))]))))] + (is (= 1 (count (re-seq #"&&" code))))))) + +(comment + (test/run-tests) + + (require '[clojure.pprint :refer [pprint]]) + + ) From fc265c52ae705169627557056ef89a23fce41117 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Thu, 13 May 2021 07:55:45 -0400 Subject: [PATCH 3721/4033] formatting --- src/main/cljs/cljs/analyzer/passes/and_or.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/analyzer/passes/and_or.cljc b/src/main/cljs/cljs/analyzer/passes/and_or.cljc index 2cc617b46..c4fe8e5be 100644 --- a/src/main/cljs/cljs/analyzer/passes/and_or.cljc +++ b/src/main/cljs/cljs/analyzer/passes/and_or.cljc @@ -28,7 +28,7 @@ (defn single-binding-let? [ast] (and (= :let (:op ast)) - (= 1 (count (-> ast :bindings))))) + (= 1 (count (-> ast :bindings))))) (defn no-statements? [let-ast] (= [] (-> let-ast :body :statements))) @@ -46,7 +46,7 @@ ;; remove :env, if same, local will differ only by ;; :context (:expr | :statement) (= (dissoc (:test if-ast) :env) - (dissoc (:then if-ast) :env))) + (dissoc (:then if-ast) :env))) (defn test=else? [if-ast] ;; remove :env, if same, local will differ only by @@ -60,7 +60,7 @@ (defn simple-or? [ast] (and (simple-test-binding-let? ast) - (test=then? (-> ast :body :ret)))) + (test=then? (-> ast :body :ret)))) (defn optimizable-and? [ast] (and (simple-and? ast) From e84995f5ad36d3cc0d92a918a8446d3170a602f2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 May 2021 11:38:39 -0400 Subject: [PATCH 3722/4033] CLJS-3300: cljs.loader regression --- src/main/cljs/cljs/loader.cljs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 262dfefb3..33405cd47 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -70,6 +70,7 @@ (assert (contains? module-infos module-name) (str "Module " module-name " does not exist")) (let [mname (-> module-name name munge)] + (.beforeLoadModuleCode *module-manager* mname) (if-not (nil? cb) (.execOnLoad *module-manager* mname cb) (.load *module-manager* mname))))) @@ -81,10 +82,16 @@ [module-name] (assert (contains? module-infos module-name) (str "Module " module-name " does not exist")) - (let [xs (deps-for module-name module-infos)] - (doseq [x xs] - (.setLoaded (.getModuleInfo *module-manager* (munge-kw x)))) - (.setLoaded (.getModuleInfo *module-manager* (munge-kw module-name))))) + (let [deps (deps-for module-name module-infos)] + (doseq [dep deps] + (let [dep' (munge-kw dep)] + (when (.isModuleLoading *module-manager* dep') + (.setLoaded *module-manager* dep')) + (.setLoaded (.getModuleInfo *module-manager* dep')))) + (let [module-name' (munge-kw module-name)] + (when (.isModuleLoading *module-manager* module-name') + (.setLoaded *module-manager* module-name')) + (.setLoaded (.getModuleInfo *module-manager* module-name'))))) (defn prefetch "Prefetch a module. module-name should be a keyword matching a :modules From 154e553699b2b7189da34d08618eba250d66840c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 May 2021 12:35:20 -0400 Subject: [PATCH 3723/4033] bump to Closure Compiler v20210505 --- deps.edn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.edn b/deps.edn index 85dd95531..45052ea24 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-unshaded {:mvn/version "v20210302"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20210505"} com.cognitect/transit-clj {:mvn/version "0.8.309"} org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} From 6fc5e9bbede2118d8f6590425e93ffc4f3a213f1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 May 2021 12:37:56 -0400 Subject: [PATCH 3724/4033] remove ES6-typed --- src/main/clojure/cljs/closure.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 85c94ae60..b3fb25b44 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -229,7 +229,7 @@ (def lang-level [:ecmascript3 :ecmascript5 :ecmascript5-strict :ecmascript6 :ecmascript6-strict - :ecmascript-2015 :ecmascript6-typed :ecmascript-2016 :ecmascript-2017 :ecmascript-next + :ecmascript-2015 :ecmascript-2016 :ecmascript-2017 :ecmascript-next :no-transpile]) (defn expand-lang-key [key] @@ -244,7 +244,6 @@ :ecmascript6 CompilerOptions$LanguageMode/ECMASCRIPT_2015 ;; (deprecated and remapped) :ecmascript6-strict CompilerOptions$LanguageMode/ECMASCRIPT_2015 ;; (deprecated and remapped) :ecmascript-2015 CompilerOptions$LanguageMode/ECMASCRIPT_2015 - :ecmascript6-typed CompilerOptions$LanguageMode/ECMASCRIPT6_TYPED :ecmascript-2016 CompilerOptions$LanguageMode/ECMASCRIPT_2016 :ecmascript-2017 CompilerOptions$LanguageMode/ECMASCRIPT_2017 :ecmascript-next CompilerOptions$LanguageMode/ECMASCRIPT_NEXT)) From 8ef4bd2408e25f102992ca18f5e1d3959b68dac5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 14 May 2021 13:21:05 -0400 Subject: [PATCH 3725/4033] bump all closure compiler versions --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 5747d062b..ca6a12900 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20210202 + v20210505 org.clojure diff --git a/project.clj b/project.clj index 5bffd0e4e..6c297ea30 100644 --- a/project.clj +++ b/project.clj @@ -16,7 +16,7 @@ [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20201211-3e6c510d"] - [com.google.javascript/closure-compiler-unshaded "v20210302"]] + [com.google.javascript/closure-compiler-unshaded "v20210505"]] :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/script/bootstrap b/script/bootstrap index 495c33005..44e636280 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="20210302" +CLOSURE_RELEASE="20210505" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20201211-3e6c510d" From 15f330f31a57ef7586b37a81d539f67f2a66b168 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 May 2021 11:39:52 -0400 Subject: [PATCH 3726/4033] CLJS-3309 (#96) The `and/or` optimization removes the intermediate let, however the child expressions will still have the local in their environments, and more importantly anon fns in loops will have captured the local in the `:loop-lets` AST field. We add a pass that removes the local as well as loop-lets. Add compile and runtime tests based on cases from the ticket. --- src/main/cljs/cljs/analyzer/passes.cljc | 32 +++++++++++ .../cljs/cljs/analyzer/passes/and_or.cljc | 55 +++++++++++++------ src/test/cljs/cljs/core_test.cljs | 12 ++++ src/test/clojure/cljs/analyzer_pass_tests.clj | 48 ++++++++++++++++ 4 files changed, 130 insertions(+), 17 deletions(-) create mode 100644 src/main/cljs/cljs/analyzer/passes.cljc diff --git a/src/main/cljs/cljs/analyzer/passes.cljc b/src/main/cljs/cljs/analyzer/passes.cljc new file mode 100644 index 000000000..5d1a3a9cf --- /dev/null +++ b/src/main/cljs/cljs/analyzer/passes.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) + +(defn apply-passes + ([ast passes] + (apply-passes ast passes nil)) + ([ast passes opts] + (reduce + (fn [ast pass] + (pass (:env ast) ast opts)) + ast passes))) + +(defn walk + ([ast passes] + (walk ast passes nil)) + ([ast passes opts] + (reduce + (fn [ast child-k] + (assoc ast + child-k + (let [child (get ast child-k)] + (if (vector? child) + (into [] (map #(walk % passes opts)) child) + (walk child passes))))) + (some-> ast (apply-passes passes opts)) (:children ast)))) diff --git a/src/main/cljs/cljs/analyzer/passes/and_or.cljc b/src/main/cljs/cljs/analyzer/passes/and_or.cljc index c4fe8e5be..52bc76c8a 100644 --- a/src/main/cljs/cljs/analyzer/passes/and_or.cljc +++ b/src/main/cljs/cljs/analyzer/passes/and_or.cljc @@ -6,7 +6,8 @@ ;; the terms of this license. ;; You must not remove this notice, or any other, from this software. -(ns cljs.analyzer.passes.and-or) +(ns cljs.analyzer.passes.and-or + (:require [cljs.analyzer.passes :as passes])) (def simple-ops #{:var :js-var :local :invoke :const :host-field :host-call :js :quote}) @@ -70,25 +71,45 @@ (and (simple-or? ast) (simple-test-expr? (-> ast :body :ret :else)))) +(defn remove-loop-let [fn-ast local] + (update fn-ast :loop-lets + (fn [loop-lets] + (map + (fn [m] + (update m :params + (fn [xs] (remove #(= local (:name %)) xs)))) + loop-lets)))) + +(defn remove-local-pass [local] + (fn [env ast opts] + (cond-> (update-in ast [:env :locals] dissoc local) + (= :fn (:op ast)) (remove-loop-let local)))) + (defn optimize-and [ast] - {:op :js - :env (:env ast) - :segs ["((" ") && (" "))"] - :args [(-> ast :bindings first :init) - (->expr-env (-> ast :body :ret :then))] - :form (:form ast) - :children [:args] - :tag 'boolean}) + (let [{:keys [init name]} (-> ast :bindings first)] + {:op :js + :env (:env ast) + :segs ["((" ") && (" "))"] + :args [init + (passes/walk + (->expr-env (-> ast :body :ret :then)) + [(remove-local-pass name)])] + :form (:form ast) + :children [:args] + :tag 'boolean})) (defn optimize-or [ast] - {:op :js - :env (:env ast) - :segs ["((" ") || (" "))"] - :args [(-> ast :bindings first :init) - (->expr-env (-> ast :body :ret :else))] - :form (:form ast) - :children [:args] - :tag 'boolean}) + (let [{:keys [init name]} (-> ast :bindings first)] + {:op :js + :env (:env ast) + :segs ["((" ") || (" "))"] + :args [init + (passes/walk + (->expr-env (-> ast :body :ret :else)) + [(remove-local-pass name)])] + :form (:form ast) + :children [:args] + :tag 'boolean})) (defn optimize [env ast _] (cond diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 65ffcc631..8c3ad289c 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1854,3 +1854,15 @@ (is (false? (contains? sv :kw)))) (let [sv (subvec [0 1 2 3 4] 2 2)] (is (false? (contains? sv 0))))) + +(deftest test-cljs-3309 + (is (= :ok + (loop [x 4] + (if (or (< x 4) (not-any? (fn [y] x) [1])) + (recur 5) + :ok)))) + (is (= '([]) + ((fn [s] + (for [e s :when (and (sequential? e) (every? (fn [x] x) e))] + e)) + [[]])))) diff --git a/src/test/clojure/cljs/analyzer_pass_tests.clj b/src/test/clojure/cljs/analyzer_pass_tests.clj index 76521e7df..a9cc90166 100644 --- a/src/test/clojure/cljs/analyzer_pass_tests.clj +++ b/src/test/clojure/cljs/analyzer_pass_tests.clj @@ -8,6 +8,7 @@ (ns cljs.analyzer-pass-tests (:require [cljs.analyzer :as ana] + [cljs.analyzer.passes :as passes] [cljs.analyzer.passes.and-or :as and-or] [cljs.analyzer-tests :as ana-tests :refer [analyze]] [cljs.compiler :as comp] @@ -16,6 +17,33 @@ [clojure.string :as string] [clojure.test :as test :refer [deftest is testing]])) +(deftest test-walk + (testing "walking visits every node" + (let [expr-env (assoc (ana/empty-env) :context :expr) + ast (->> `(and true false) + (analyze expr-env)) + ast' (passes/walk ast [(fn [_ ast _] (dissoc ast :env))])] + (is (not (contains? ast' :env))) + (is (not (some #(contains? % :env) (:args ast'))))))) + +(deftest remove-local + (testing "and/or remove local pass" + (let [ast {:op :fn + :env '{:locals {x {}}} + :loop-lets '[{:params [{:name x}]}]} + pass (and-or/remove-local-pass 'x) + ast' (passes/apply-passes ast [pass])] + (is (contains? (-> ast :env :locals) 'x)) + (is (not (contains? (-> ast' :env :locals) 'x))) + (is (some + (fn [{:keys [params]}] + (some #(= 'x (:name %)) params)) + (:loop-lets ast))) + (is (not (some + (fn [{:keys [params]}] + (some #(= 'x (:name %)) params)) + (:loop-lets ast'))))))) + (deftest test-and-or-code-gen-pass (testing "and/or optimization code gen pass" (let [expr-env (assoc (ana/empty-env) :context :expr) @@ -110,6 +138,26 @@ (and (even? 1) false))]))))] (is (= 1 (count (re-seq #"&&" code))))))) +(deftest test-cljs-3309 + (testing "CLJS-3309: and/or optimization removes discarded local and loop-lets" + (let [code (env/with-compiler-env (env/default-compiler-env) + (comp/with-core-cljs {} + (fn [] + (compile-form-seq + '[(loop [x 4] + (when (or (< x 4) (not-any? (fn [y] x) [1])) + (recur 5)))]))))] + (is (empty? (re-seq #"or_" code)))) + (let [code (env/with-compiler-env (env/default-compiler-env) + (comp/with-core-cljs {} + (fn [] + (compile-form-seq + '[((fn [s] + (for [e s :when (and (sequential? e) (every? (fn [x] x) e))] + e)) + [[]])]))))] + (is (empty? (re-seq #"and_" code)))))) + (comment (test/run-tests) From 58e39cb5f672ce7419955285bd78a71d4cbf595b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 May 2021 15:08:22 -0400 Subject: [PATCH 3727/4033] binding ast nodes were missing :children key fix small bug in passes - not passing opts if provided add compile test cased based on 4clojure example --- src/main/cljs/cljs/analyzer/passes.cljc | 2 +- src/main/clojure/cljs/analyzer.cljc | 6 +++-- src/test/clojure/cljs/analyzer_pass_tests.clj | 24 ++++++++++++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/analyzer/passes.cljc b/src/main/cljs/cljs/analyzer/passes.cljc index 5d1a3a9cf..422504493 100644 --- a/src/main/cljs/cljs/analyzer/passes.cljc +++ b/src/main/cljs/cljs/analyzer/passes.cljc @@ -28,5 +28,5 @@ (let [child (get ast child-k)] (if (vector? child) (into [] (map #(walk % passes opts)) child) - (walk child passes))))) + (walk child passes opts))))) (some-> ast (apply-passes passes opts)) (:children ast)))) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f1d01cbc2..bfe05dcdf 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2219,7 +2219,8 @@ :init fexpr :variadic? (:variadic? fexpr) :max-fixed-arity (:max-fixed-arity fexpr) - :method-params (map :params (:methods fexpr)))] + :method-params (map :params (:methods fexpr)) + :children [:init])] [(assoc-in env [:locals name] be') (conj bes be')])) [meth-env []] bes) @@ -2296,7 +2297,8 @@ :env {:line line :column col} :info {:name name :shadow shadow} - :binding-form? true} + :binding-form? true + :children [:init]} be (if (= :fn (:op init-expr)) ;; TODO: can we simplify - David (merge be diff --git a/src/test/clojure/cljs/analyzer_pass_tests.clj b/src/test/clojure/cljs/analyzer_pass_tests.clj index a9cc90166..984492564 100644 --- a/src/test/clojure/cljs/analyzer_pass_tests.clj +++ b/src/test/clojure/cljs/analyzer_pass_tests.clj @@ -24,7 +24,17 @@ (analyze expr-env)) ast' (passes/walk ast [(fn [_ ast _] (dissoc ast :env))])] (is (not (contains? ast' :env))) - (is (not (some #(contains? % :env) (:args ast'))))))) + (is (not (some #(contains? % :env) (:args ast'))))) + (let [expr-env (assoc (ana/empty-env) :context :expr) + ast (->> `(let [x# 1 + y# (fn [] x#) + z# (fn [] y#)] + 'x) + (analyze expr-env)) + ast' (passes/walk ast [(fn [_ ast _] (dissoc ast :env))])] + (is (not (contains? ast' :env))) + (is (= 3 (count (:bindings ast')))) + (is (not (some #(contains? % :env) (:bindings ast'))))))) (deftest remove-local (testing "and/or remove local pass" @@ -156,11 +166,19 @@ (for [e s :when (and (sequential? e) (every? (fn [x] x) e))] e)) [[]])]))))] - (is (empty? (re-seq #"and_" code)))))) + (is (empty? (re-seq #"and_" code)))) + (let [code (env/with-compiler-env (env/default-compiler-env) + (comp/with-core-cljs {} + (fn [] + (compile-form-seq + '[(or false + (boolean + (for [s (range 1)] + (map (fn [x] x) s))))]))))] + (is (empty? (re-seq #"or_" code)))))) (comment (test/run-tests) (require '[clojure.pprint :refer [pprint]]) - ) From 1aa56667620198eee5b42a36e36691d514d47c9b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 21 May 2021 12:39:57 -0400 Subject: [PATCH 3728/4033] 1.10.866 --- README.md | 6 +++--- changes.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 551d8f457..7c0dc0a06 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.844 +Latest stable release: 1.10.866 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.844"] +[org.clojure/clojurescript "1.10.866"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.844 org.clojure clojurescript - 1.10.844 + 1.10.866 ``` diff --git a/changes.md b/changes.md index 396d91d85..6b764d545 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,31 @@ +## 1.10.866 + +### Changes +* Google Closure v20210505 + +### Enhancements +* CLJS-3260: and/or optimization as compiler pass, fixes core.async go macro issue +* CLJS-3276: Support macros that expand to require statements + + +### Fixes +* CLJS-3309: and/or opt bug - passes to remove dropped locals from anon fns, + missing :children keys on binding nodes +* CLJS-3300: cljs.loader regression +* CLJS-3293: Some npm packages fail to require +* CLJS-3306: subvecs must implement IAssociative -contains-key? +* CLJS-3307: Allow extending IAssociative -contains-key? to native +* CLJS-3305: defrecord must implement IAssociative -contains-key? +* CLJS-3303: checked arrays enabled in advanced +* CLJS-3304: Higher order checked arrays +* CLJS-3284: Use of private deftype by public function in another namespace when + inside an if causes warning +* CLJS-3282: document bundle target in CLI help +* CLJS-3283: Support -contains-key? protocol check in contains? +* CLJS-3296: Update conj docstring for missing arities +* CLJS-3298: visibility diagnostic group typo +* CLJS-3302: Fixup docstring for `default-dispatch-val` and `dispatch-fn` + ## 1.10.844 ### Changes From 9027c02852bbeb512cda728e5f0551145e921a9c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 11 Jun 2021 16:28:02 -0400 Subject: [PATCH 3729/4033] CLJS-3313: Protocol implementations via metadata: ClojureScript behaves differently from Clojure CLJS-3313: Protocol implementations via metadata: ClojureScript behaves differently from Clojure need to check the metadata implementation first. Fix test expectations, add ticket test case. --- src/main/clojure/cljs/core.cljc | 23 ++++++++++++----------- src/test/cljs/cljs/core_test.cljs | 12 ++++++++++-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 61b705f93..a055f5adf 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2072,14 +2072,6 @@ (missing-protocol ~(core/str psym "." fname) ~fsig)))) - ;; then check protocol fn in metadata (only when protocol is marked with :extend-via-metadata true) - check - (core/if-not (:extend-via-metadata opts) - check - `(if-let [meta-impl# (-> ~fsig (core/meta) (core/get '~fqn-fname))] - (meta-impl# ~@sig) - ~check)) - ;; then check protocol on js string,function,array,object (first dynamic check actually executed) check `(let [x# (if (nil? ~fsig) nil ~fsig) @@ -2088,9 +2080,10 @@ (m# ~@sig) ~check))] `(~sig ~check))) - expand-sig (core/fn [dyn-name slot sig] + expand-sig (core/fn [fname dyn-name slot sig] (core/let [sig (sig->syms sig) + fqn-fname (with-meta (fqn fname) {:cljs.analyzer/no-resolve true}) fsig (first sig) ;; check protocol property on object (first check executed) @@ -2099,7 +2092,15 @@ ;; Property access needed here. (not (nil? (. ~fsig ~(with-meta (symbol (core/str "-" slot)) {:protocol-prop true}))))) (. ~fsig ~slot ~@sig) - (~dyn-name ~@sig))] + (~dyn-name ~@sig)) + + ;; then check protocol fn in metadata (only when protocol is marked with :extend-via-metadata true) + check + (core/if-not (:extend-via-metadata opts) + check + `(if-let [meta-impl# (-> ~fsig (core/meta) (core/get '~fqn-fname))] + (meta-impl# ~@sig) + ~check))] `(~sig ~check))) psym (core/-> psym (vary-meta update-in [:jsdoc] conj "@interface") @@ -2147,7 +2148,7 @@ sigs))] (defn ~fname ~@(map (core/fn [sig] - (expand-sig dyn-name + (expand-sig fname dyn-name (with-meta (symbol (core/str slot "$arity$" (count sig))) {:protocol-prop true}) sig)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 8c3ad289c..82e075744 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1770,13 +1770,21 @@ (deftest test-cljs-2960 ;; protocol impl via metadata (is (= 1 (ext-meta-protocol (with-meta {} {`ext-meta-protocol (fn [_] 1)})))) - ;; actual impl before metadata - (is (= 2 (ext-meta-protocol (with-meta (SomeMetaImpl. 2) {`ext-meta-protocol (fn [_] 1)})))) + ;; metadata before actual impl + (is (= 1 (ext-meta-protocol (with-meta (SomeMetaImpl. 2) {`ext-meta-protocol (fn [_] 1)})))) ;; protocol not marked as :extend-via-metadata so fallthrough to no impl (is (thrown? js/Error (non-meta-protocol (with-meta {} {`non-meta-protocol (fn [_] 1)})))) ;; normal impl call just in case (is (= 2 (non-meta-protocol (with-meta (SomeMetaImpl. 2) {`non-meta-protocol (fn [_] 1)}))))) +(extend-type PersistentArrayMap + ExtMetaProtocol + (ext-meta-protocol [m] 2)) + +(deftest test-cljs-3313 + (testing "metadata protocol fn takes precedence over direct implementation" + (= 1 (ext-meta-protocol (with-meta (array-map) {`ext-meta-protocol (fn [_] 1)}))))) + (deftest test-cljs-3054 (testing "`into` behaves the same as Clojure" (is (nil? (into nil #{}))) From 3e9d650ede64e707040249cbebb07f03392ab75d Mon Sep 17 00:00:00 2001 From: Oliver Caldwell Date: Mon, 10 Jun 2019 16:59:54 +0100 Subject: [PATCH 3730/4033] CLJS-3096 Add :exception true to prepl errors --- src/main/clojure/cljs/core/server.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index bbbc295d1..7f43d59fc 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -118,11 +118,13 @@ true))) (catch Throwable ex (out-fn {:tag :ret :val (Throwable->map ex) - :ns (name ana/*cljs-ns*) :form s}) + :ns (name ana/*cljs-ns*) :form s + :exception true}) true))) (catch Throwable ex (out-fn {:tag :ret :val (Throwable->map ex) - :ns (name ana/*cljs-ns*)}) + :ns (name ana/*cljs-ns*) + :exception true}) true)) (recur))) (finally From 465c88e07bcdb6b55ecf8f1c73fe67fc9b5ad4a6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jul 2021 13:43:55 -0400 Subject: [PATCH 3731/4033] revert 3276, fix tests to show that there was way for this to work previously --- src/main/clojure/cljs/analyzer.cljc | 86 ++++++++---------------- src/test/cljs_build/cljs_3276/macros.clj | 3 +- 2 files changed, 31 insertions(+), 58 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index bfe05dcdf..aa98136a1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4334,19 +4334,6 @@ (.lastIndexOf full-name "."))] (symbol (str "cljs.user." name (util/content-sha full-name 7))))))) -#?(:clj - (defn macro-call? [form env] - (when (and (seq? form) (seq form) (and (symbol? (first form)))) - (let [sym (first form) - nstr (namespace sym)] - (or (and (some? nstr) - (some? (gets env :ns :require-macros (symbol nstr)))) - (some? (gets env :ns :rename-macros sym)) - (some? (gets env :ns :use-macros sym))))))) - -#?(:clj - (declare ns-side-effects macroexpand-1)) - #?(:clj (defn ^:dynamic parse-ns "Helper for parsing only the essential namespace information from a @@ -4372,9 +4359,6 @@ (binding [env/*compiler* (if (false? (:restore opts)) env/*compiler* (atom @env/*compiler*)) - *file-defs* nil - #?@(:clj [*unchecked-if* false - *unchecked-arrays* false]) *cljs-ns* 'cljs.user *cljs-file* src *macro-infer* @@ -4391,8 +4375,7 @@ false)] (let [rdr (when-not (sequential? src) (io/reader src))] (try - (loop [env (empty-env) - forms (if rdr + (loop [forms (if rdr (forms-seq* rdr (source-path src)) src) ret (merge @@ -4407,54 +4390,43 @@ {:lines (with-open [reader (io/reader dest)] (-> reader line-seq count))}))] (if (seq forms) - (let [form (first forms) - macro? (macro-call? form env) - env (if macro? - (binding [*load-macros* true] - (assoc (:env (ns-side-effects env (:ast ret) opts)) :ns (:ns env))) - env) - ast (when (or macro? (and (seq? form) ('#{ns ns* require use require-macros} (first form)))) - (no-warn (analyze env form nil opts))) - env (assoc (:env ast) :ns (:ns env))] + (let [env (empty-env) + ast (no-warn (analyze env (first forms) nil opts))] (cond (= :ns (:op ast)) (let [ns-name (:name ast) ns-name (if (and (= 'cljs.core ns-name) - (= "cljc" (util/ext src))) + (= "cljc" (util/ext src))) 'cljs.core$macros ns-name) - deps (merge (:uses ast) (:requires ast)) - env (assoc (:env ast) :ns (dissoc ast :env))] - (recur env - (rest forms) - (cond-> - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= 'cljs.core ns-name) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj constants-ns-sym))) - :file dest - :source-file (when rdr src) - :source-forms (when-not rdr src) - :ast ast - :macros-ns (or (:macros-ns opts) - (= 'cljs.core$macros ns-name))} - (and dest (.exists ^File dest)) - (assoc :lines (with-open [reader (io/reader dest)] - (-> reader line-seq count)))))) + deps (merge (:uses ast) (:requires ast))] + (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= 'cljs.core ns-name) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj constants-ns-sym))) + :file dest + :source-file (when rdr src) + :source-forms (when-not rdr src) + :ast ast + :macros-ns (or (:macros-ns opts) + (= 'cljs.core$macros ns-name))} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))}))) (= :ns* (:op ast)) (let [deps (merge (:uses ast) (:requires ast))] - (recur (:env ast) - (rest forms) - (cond-> (update-in ret [:requires] into (set (vals deps))) - ;; we need to defer generating the user namespace - ;; until we actually need or it will break when - ;; `src` is a sequence of forms - António Monteiro - (not (:ns ret)) - (assoc :ns (gen-user-ns src) :provides [(gen-user-ns src)])))) + (recur (rest forms) + (cond-> (update-in ret [:requires] into (set (vals deps))) + ;; we need to defer generating the user namespace + ;; until we actually need or it will break when + ;; `src` is a sequence of forms - António Monteiro + (not (:ns ret)) + (assoc :ns (gen-user-ns src) :provides [(gen-user-ns src)])))) :else ret)) ret)) diff --git a/src/test/cljs_build/cljs_3276/macros.clj b/src/test/cljs_build/cljs_3276/macros.clj index bef99f137..5cfb3fc2e 100644 --- a/src/test/cljs_build/cljs_3276/macros.clj +++ b/src/test/cljs_build/cljs_3276/macros.clj @@ -1,4 +1,5 @@ (ns cljs-3276.macros) (defmacro macro-that-requires [] - `(require 'cljs-3276.foo)) + '(ns test.foo + (:require cljs-3276.foo))) From 98800e5a0f806e34dab380f10091317bff8e0cb9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jul 2021 15:04:31 -0400 Subject: [PATCH 3732/4033] always deep-merge ns-info, then macros can emit ns forms that augments the existing ns --- src/main/clojure/cljs/analyzer.cljc | 11 ++++++++++- src/test/clojure/cljs/analyzer_tests.clj | 17 +++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index aa98136a1..a19528e17 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3166,7 +3166,16 @@ :requires requires :renames (merge renames core-renames) :imports imports}] - (swap! env/*compiler* update-in [::namespaces name] merge ns-info) + (swap! env/*compiler* update-in [::namespaces name] + (fn [the-ns] + ;; just merge - do not replace - then macros can emit + ;; modifications to the ns + (reduce-kv + (fn [the-ns k v] + (if (map? v) + (update the-ns k merge v) + (assoc the-ns k v))) + the-ns ns-info))) (merge {:op :ns :env env :form form diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 90d3fd1d8..abb72f096 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1496,9 +1496,14 @@ (deftest test-cljs-3276-require-from-macro (let [cenv (env/default-compiler-env)] (env/with-compiler-env cenv - (ana/analyze-form-seq - '[(ns test.foo - (:require-macros [cljs-3276.macros :refer [macro-that-requires]])) - (macro-that-requires)])) - (is (= '{cljs-3276.foo cljs-3276.foo} (get-in @cenv [::ana/namespaces 'test.foo :requires]))) - (is (contains? (get @cenv ::ana/namespaces) 'cljs-3276.foo)))) + (comp/with-core-cljs {} + (fn [] + (ana/analyze-form-seq + '[(ns test.foo + (:require [clojure.set :as set]) + (:require-macros [cljs-3276.macros :refer [macro-that-requires]])) + (macro-that-requires)])))) + (is (= '{set clojure.set, clojure.set clojure.set, cljs-3276.foo cljs-3276.foo} + (get-in @cenv [::ana/namespaces 'test.foo :requires]))) + (is (contains? (get @cenv ::ana/namespaces) 'cljs-3276.foo)) + (is (contains? (get @cenv ::ana/namespaces) 'clojure.set)))) From 8bf3a59c802211e544aab00e6b5237dc896ac22b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jul 2021 22:05:37 -0400 Subject: [PATCH 3733/4033] cannot use ns because ns will emit goog.provide - update test code to use ns* which works --- src/test/cljs_build/cljs_3276/macros.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/cljs_build/cljs_3276/macros.clj b/src/test/cljs_build/cljs_3276/macros.clj index 5cfb3fc2e..a2a961011 100644 --- a/src/test/cljs_build/cljs_3276/macros.clj +++ b/src/test/cljs_build/cljs_3276/macros.clj @@ -1,5 +1,4 @@ (ns cljs-3276.macros) (defmacro macro-that-requires [] - '(ns test.foo - (:require cljs-3276.foo))) + '(ns* (:require 'cljs-3276.foo))) From 4fa72cf31232e6767f04c3ae7e4ab4cc754be342 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jul 2021 22:08:01 -0400 Subject: [PATCH 3734/4033] revert ns-info deep merge from analyze parse 'ns case - not needed --- src/main/clojure/cljs/analyzer.cljc | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a19528e17..aa98136a1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3166,16 +3166,7 @@ :requires requires :renames (merge renames core-renames) :imports imports}] - (swap! env/*compiler* update-in [::namespaces name] - (fn [the-ns] - ;; just merge - do not replace - then macros can emit - ;; modifications to the ns - (reduce-kv - (fn [the-ns k v] - (if (map? v) - (update the-ns k merge v) - (assoc the-ns k v))) - the-ns ns-info))) + (swap! env/*compiler* update-in [::namespaces name] merge ns-info) (merge {:op :ns :env env :form form From 5ebca2cb5a57abe41098e0049bb43518123907f2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jul 2021 22:29:35 -0400 Subject: [PATCH 3735/4033] formatting --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index aa98136a1..c70eabc7e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4396,7 +4396,7 @@ (= :ns (:op ast)) (let [ns-name (:name ast) ns-name (if (and (= 'cljs.core ns-name) - (= "cljc" (util/ext src))) + (= "cljc" (util/ext src))) 'cljs.core$macros ns-name) deps (merge (:uses ast) (:requires ast))] From ed95660efae23c76b5aa81f130f91194ce1f6a06 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jul 2021 22:40:51 -0400 Subject: [PATCH 3736/4033] formatting --- src/main/clojure/cljs/analyzer/api.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 29eb384f7..cff4b97f4 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -166,6 +166,7 @@ (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (ana/parse-ns src dest opts)))))) + #?(:clj (defn analyze-file "Given a java.io.File, java.net.URL or a string identifying a resource on the From 4d27944429a24bbe39208597141b220cf4f779b1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Jul 2021 23:46:09 -0400 Subject: [PATCH 3737/4033] revert all of CLJS-3276 for now --- src/test/cljs_build/cljs_3276/foo.cljs | 1 - src/test/cljs_build/cljs_3276/macros.clj | 4 ---- src/test/clojure/cljs/analyzer_tests.clj | 15 --------------- 3 files changed, 20 deletions(-) delete mode 100644 src/test/cljs_build/cljs_3276/foo.cljs delete mode 100644 src/test/cljs_build/cljs_3276/macros.clj diff --git a/src/test/cljs_build/cljs_3276/foo.cljs b/src/test/cljs_build/cljs_3276/foo.cljs deleted file mode 100644 index 898dbef8c..000000000 --- a/src/test/cljs_build/cljs_3276/foo.cljs +++ /dev/null @@ -1 +0,0 @@ -(ns cljs-3276.foo) diff --git a/src/test/cljs_build/cljs_3276/macros.clj b/src/test/cljs_build/cljs_3276/macros.clj deleted file mode 100644 index a2a961011..000000000 --- a/src/test/cljs_build/cljs_3276/macros.clj +++ /dev/null @@ -1,4 +0,0 @@ -(ns cljs-3276.macros) - -(defmacro macro-that-requires [] - '(ns* (:require 'cljs-3276.foo))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index abb72f096..6c0041f92 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1492,18 +1492,3 @@ '[(ns test.foo (:import goog))])) (is (= {} (get-in @cenv [::ana/namespaces 'test.foo :imports]))))) - -(deftest test-cljs-3276-require-from-macro - (let [cenv (env/default-compiler-env)] - (env/with-compiler-env cenv - (comp/with-core-cljs {} - (fn [] - (ana/analyze-form-seq - '[(ns test.foo - (:require [clojure.set :as set]) - (:require-macros [cljs-3276.macros :refer [macro-that-requires]])) - (macro-that-requires)])))) - (is (= '{set clojure.set, clojure.set clojure.set, cljs-3276.foo cljs-3276.foo} - (get-in @cenv [::ana/namespaces 'test.foo :requires]))) - (is (contains? (get @cenv ::ana/namespaces) 'cljs-3276.foo)) - (is (contains? (get @cenv ::ana/namespaces) 'clojure.set)))) From 9c39d871fd58a20c75b918564a10072a3200bd40 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 17 Jul 2021 00:00:22 -0400 Subject: [PATCH 3738/4033] Revert CLJS-3276 (#99) Revert CLJS-3276, fixes CLJS-3311 and CLJS-3312 --- src/main/clojure/cljs/analyzer.cljc | 84 ++++++++---------------- src/main/clojure/cljs/analyzer/api.cljc | 1 + src/test/cljs_build/cljs_3276/foo.cljs | 1 - src/test/cljs_build/cljs_3276/macros.clj | 4 -- src/test/clojure/cljs/analyzer_tests.clj | 10 --- 5 files changed, 29 insertions(+), 71 deletions(-) delete mode 100644 src/test/cljs_build/cljs_3276/foo.cljs delete mode 100644 src/test/cljs_build/cljs_3276/macros.clj diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index bfe05dcdf..c70eabc7e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4334,19 +4334,6 @@ (.lastIndexOf full-name "."))] (symbol (str "cljs.user." name (util/content-sha full-name 7))))))) -#?(:clj - (defn macro-call? [form env] - (when (and (seq? form) (seq form) (and (symbol? (first form)))) - (let [sym (first form) - nstr (namespace sym)] - (or (and (some? nstr) - (some? (gets env :ns :require-macros (symbol nstr)))) - (some? (gets env :ns :rename-macros sym)) - (some? (gets env :ns :use-macros sym))))))) - -#?(:clj - (declare ns-side-effects macroexpand-1)) - #?(:clj (defn ^:dynamic parse-ns "Helper for parsing only the essential namespace information from a @@ -4372,9 +4359,6 @@ (binding [env/*compiler* (if (false? (:restore opts)) env/*compiler* (atom @env/*compiler*)) - *file-defs* nil - #?@(:clj [*unchecked-if* false - *unchecked-arrays* false]) *cljs-ns* 'cljs.user *cljs-file* src *macro-infer* @@ -4391,8 +4375,7 @@ false)] (let [rdr (when-not (sequential? src) (io/reader src))] (try - (loop [env (empty-env) - forms (if rdr + (loop [forms (if rdr (forms-seq* rdr (source-path src)) src) ret (merge @@ -4407,15 +4390,8 @@ {:lines (with-open [reader (io/reader dest)] (-> reader line-seq count))}))] (if (seq forms) - (let [form (first forms) - macro? (macro-call? form env) - env (if macro? - (binding [*load-macros* true] - (assoc (:env (ns-side-effects env (:ast ret) opts)) :ns (:ns env))) - env) - ast (when (or macro? (and (seq? form) ('#{ns ns* require use require-macros} (first form)))) - (no-warn (analyze env form nil opts))) - env (assoc (:env ast) :ns (:ns env))] + (let [env (empty-env) + ast (no-warn (analyze env (first forms) nil opts))] (cond (= :ns (:op ast)) (let [ns-name (:name ast) @@ -4423,38 +4399,34 @@ (= "cljc" (util/ext src))) 'cljs.core$macros ns-name) - deps (merge (:uses ast) (:requires ast)) - env (assoc (:env ast) :ns (dissoc ast :env))] - (recur env - (rest forms) - (cond-> - {:ns (or ns-name 'cljs.user) - :provides [ns-name] - :requires (if (= 'cljs.core ns-name) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj constants-ns-sym))) - :file dest - :source-file (when rdr src) - :source-forms (when-not rdr src) - :ast ast - :macros-ns (or (:macros-ns opts) - (= 'cljs.core$macros ns-name))} - (and dest (.exists ^File dest)) - (assoc :lines (with-open [reader (io/reader dest)] - (-> reader line-seq count)))))) + deps (merge (:uses ast) (:requires ast))] + (merge + {:ns (or ns-name 'cljs.user) + :provides [ns-name] + :requires (if (= 'cljs.core ns-name) + (set (vals deps)) + (cond-> (conj (set (vals deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj constants-ns-sym))) + :file dest + :source-file (when rdr src) + :source-forms (when-not rdr src) + :ast ast + :macros-ns (or (:macros-ns opts) + (= 'cljs.core$macros ns-name))} + (when (and dest (.exists ^File dest)) + {:lines (with-open [reader (io/reader dest)] + (-> reader line-seq count))}))) (= :ns* (:op ast)) (let [deps (merge (:uses ast) (:requires ast))] - (recur (:env ast) - (rest forms) - (cond-> (update-in ret [:requires] into (set (vals deps))) - ;; we need to defer generating the user namespace - ;; until we actually need or it will break when - ;; `src` is a sequence of forms - António Monteiro - (not (:ns ret)) - (assoc :ns (gen-user-ns src) :provides [(gen-user-ns src)])))) + (recur (rest forms) + (cond-> (update-in ret [:requires] into (set (vals deps))) + ;; we need to defer generating the user namespace + ;; until we actually need or it will break when + ;; `src` is a sequence of forms - António Monteiro + (not (:ns ret)) + (assoc :ns (gen-user-ns src) :provides [(gen-user-ns src)])))) :else ret)) ret)) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 29eb384f7..cff4b97f4 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -166,6 +166,7 @@ (env/with-compiler-env state (binding [ana/*cljs-warning-handlers* (:warning-handlers opts ana/*cljs-warning-handlers*)] (ana/parse-ns src dest opts)))))) + #?(:clj (defn analyze-file "Given a java.io.File, java.net.URL or a string identifying a resource on the diff --git a/src/test/cljs_build/cljs_3276/foo.cljs b/src/test/cljs_build/cljs_3276/foo.cljs deleted file mode 100644 index 898dbef8c..000000000 --- a/src/test/cljs_build/cljs_3276/foo.cljs +++ /dev/null @@ -1 +0,0 @@ -(ns cljs-3276.foo) diff --git a/src/test/cljs_build/cljs_3276/macros.clj b/src/test/cljs_build/cljs_3276/macros.clj deleted file mode 100644 index bef99f137..000000000 --- a/src/test/cljs_build/cljs_3276/macros.clj +++ /dev/null @@ -1,4 +0,0 @@ -(ns cljs-3276.macros) - -(defmacro macro-that-requires [] - `(require 'cljs-3276.foo)) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 90d3fd1d8..6c0041f92 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1492,13 +1492,3 @@ '[(ns test.foo (:import goog))])) (is (= {} (get-in @cenv [::ana/namespaces 'test.foo :imports]))))) - -(deftest test-cljs-3276-require-from-macro - (let [cenv (env/default-compiler-env)] - (env/with-compiler-env cenv - (ana/analyze-form-seq - '[(ns test.foo - (:require-macros [cljs-3276.macros :refer [macro-that-requires]])) - (macro-that-requires)])) - (is (= '{cljs-3276.foo cljs-3276.foo} (get-in @cenv [::ana/namespaces 'test.foo :requires]))) - (is (contains? (get @cenv ::ana/namespaces) 'cljs-3276.foo)))) From a4637f873d32fb3bbc2d4d167a8119c1efe553cb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 19 Jul 2021 09:08:26 -0400 Subject: [PATCH 3739/4033] add regression test for CLJS-3311 --- src/test/cljs_build/cljs_3311_regress/core.cljs | 5 +++++ src/test/cljs_build/cljs_3311_regress/tests.cljs | 5 +++++ src/test/clojure/cljs/build_api_tests.clj | 14 ++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/test/cljs_build/cljs_3311_regress/core.cljs create mode 100644 src/test/cljs_build/cljs_3311_regress/tests.cljs diff --git a/src/test/cljs_build/cljs_3311_regress/core.cljs b/src/test/cljs_build/cljs_3311_regress/core.cljs new file mode 100644 index 000000000..bb7fa5cfb --- /dev/null +++ b/src/test/cljs_build/cljs_3311_regress/core.cljs @@ -0,0 +1,5 @@ +(ns cljs-3311-regress.core + (:require [cljs.test :refer-macros [run-tests]] + [cljs-3311-regress.tests])) + +(run-tests 'cljs-3311-regress.tests) diff --git a/src/test/cljs_build/cljs_3311_regress/tests.cljs b/src/test/cljs_build/cljs_3311_regress/tests.cljs new file mode 100644 index 000000000..ba07f0e60 --- /dev/null +++ b/src/test/cljs_build/cljs_3311_regress/tests.cljs @@ -0,0 +1,5 @@ +(ns cljs-3311-regress.tests + (:require [cljs.test :refer [deftest is]])) + +(deftest some-test + (is (= 1 1))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index fbfaf2692..21fbadbef 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -782,3 +782,17 @@ (ana/with-warning-handlers [(collecting-warning-handler ws)] (build/build (build/inputs (io/file inputs "cljs_3284/core.cljs")) opts cenv)) (is (empty? @ws))))) + +(deftest test-cljs-3311-regress + (testing "Test that CLJS-3311 did not regress" + (let [ws (atom []) + out (.getPath (io/file (test/tmp-dir) "cljs-3311-regress-out")) + {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'cljs-3311-regress.core + :output-dir out + :optimizations :none}} + cenv (env/default-compiler-env opts)] + (test/delete-out-files out) + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (build/build (build/inputs (io/file inputs "cljs_3311_regress/core.cljs")) opts cenv)) + (is (empty? @ws))))) From b68e8b5714ec61f3194d3a4749d68b9479326103 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 19 Jul 2021 09:31:47 -0400 Subject: [PATCH 3740/4033] 1.10.879 --- README.md | 6 +++--- changes.md | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7c0dc0a06..4a8647821 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.866 +Latest stable release: 1.10.879 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.866"] +[org.clojure/clojurescript "1.10.879"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.866 org.clojure clojurescript - 1.10.866 + 1.10.879 ``` diff --git a/changes.md b/changes.md index 6b764d545..8da1bcfc9 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,12 @@ +## 1.10.879 + +### Changes +* Revert CLJS-3276 - macros that expand to require + +### Fixes +* CLJS-3096 Add :exception true to prepl errors +* CLJS-3313: Protocol implementations via metadata: ClojureScript behaves differently from Clojure + ## 1.10.866 ### Changes @@ -7,7 +16,6 @@ * CLJS-3260: and/or optimization as compiler pass, fixes core.async go macro issue * CLJS-3276: Support macros that expand to require statements - ### Fixes * CLJS-3309: and/or opt bug - passes to remove dropped locals from anon fns, missing :children keys on binding nodes From c0045c5718014f4cc40db55742140d2eb561807c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 22 Jul 2021 09:53:39 -0400 Subject: [PATCH 3741/4033] CLJS-3317: PersistentVector invoke does not align with Clojure (#100) Remove not-found case - does not exist in Clojure. Throw if k is not a number. --- src/main/cljs/cljs/core.cljs | 6 +++--- src/test/cljs/cljs/collections_test.cljs | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 867f4232e..ba0b6e7df 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5693,9 +5693,9 @@ reduces them without incurring seq initialization" IFn (-invoke [coll k] - (-nth coll k)) - (-invoke [coll k not-found] - (-nth coll k not-found)) + (if (number? k) + (-nth coll k) + (throw (js/Error. "Key must be integer")))) IEditableCollection (-as-transient [coll] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index f41ce7821..64f0fcbc0 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -1080,6 +1080,10 @@ (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" + (is (thrown-with-msg? js/Error #"Key must be integer" ([1 2] nil))))) + (comment (run-tests) From 2ccf81c1727765a5506c351a430b01a4c997cc80 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Aug 2021 20:34:56 -0400 Subject: [PATCH 3742/4033] CLJS-3316: Google Closure Library build script needs to generate deps.js --- .../closure-library-release.sh | 3 ++ script/closure_deps_graph.clj | 39 +++++++++++++++++++ script/closure_deps_graph.sh | 33 ++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 script/closure_deps_graph.clj create mode 100755 script/closure_deps_graph.sh diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index 3b85659a8..2e0a9335a 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -112,6 +112,9 @@ third_party_src_dir="$third_party_project_dir/src/main/resources" mkdir -p "$src_dir" "$third_party_src_dir" +## Generate deps.js +../closure_deps_graph.sh + ## Copy Closure sources cp -r \ diff --git a/script/closure_deps_graph.clj b/script/closure_deps_graph.clj new file mode 100644 index 000000000..fa9a40094 --- /dev/null +++ b/script/closure_deps_graph.clj @@ -0,0 +1,39 @@ +(ns closure-deps-graph + (:require [clojure.java.io :as io]) + (:import [java.io File] + [com.google.javascript.jscomp SourceFile BasicErrorManager] + [com.google.javascript.jscomp.deps + BrowserModuleResolver + DepsGenerator DepsGenerator$InclusionStrategy ModuleLoader + ModuleLoader$PathResolver])) + +(defn js-files-in + "Return a sequence of all .js files in the given directory." + [dir] + (filter + #(let [name (.getName ^File %)] + (and (.endsWith name ".js") + (not= \. (first name)))) + (file-seq dir))) + +(spit (io/file "closure-library/closure/goog/deps.js") + (.computeDependencyCalls + (DepsGenerator. + [] + (map #(SourceFile/fromFile (.getAbsolutePath %)) + (mapcat (comp js-files-in io/file) + ["closure-library/closure/goog"])) + DepsGenerator$InclusionStrategy/ALWAYS + (.getAbsolutePath (io/file "closure-library/closure/goog")) + (proxy [BasicErrorManager] [] + (report [level error] + (println error)) + (println [level error] + (println error))) + (-> (ModuleLoader/builder) + (.setErrorHandler nil) + (.setModuleRoots []) + (.setInputs []) + (.setFactory BrowserModuleResolver/FACTORY) + (.setPathResolver ModuleLoader$PathResolver/ABSOLUTE) + (.build))))) diff --git a/script/closure_deps_graph.sh b/script/closure_deps_graph.sh new file mode 100755 index 000000000..223339b3a --- /dev/null +++ b/script/closure_deps_graph.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -e + +if [[ -z "$CLJS_SCRIPT_QUIET" ]]; then + set -x +fi + +FILE_SEP='/' +PATH_SEP=':' +OS_ID=`uname | tr [:upper:] [:lower:]` +CLJS_SCRIPT_MVN_OPTS=${CLJS_SCRIPT_MVN_OPTS:-""} + +if [[ $OS_ID == *mingw* ]] +then + echo "MINGW detected" + # Refer to http://www.mingw.org/wiki/Posix_path_conversion + FILE_SEP='//' + PATH_SEP=';' +fi + +CP_FILE=`mktemp /tmp/cljs_cp.txt.XXXXXXXXXXX` + +mvn -f ../../pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP $CLJS_SCRIPT_MVN_OPTS + +CLJS_CP=`cat $CP_FILE` + +# For Hudson server +if [ "$HUDSON" = "true" ]; then + $JAVA_HOME/bin/java -server -cp "$CLJS_CP""$PATH_SEP""src/main/clojure" clojure.main ../closure_deps_graph.clj +else + java -server -cp "$CLJS_CP""$PATH_SEP""src/main/clojure" clojure.main ../closure_deps_graph.clj +fi From 4d4abbf302e5c6c47d9013ccf24651f77a34fc2c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 Aug 2021 21:35:14 -0400 Subject: [PATCH 3743/4033] need to normalize deps.js --- script/closure-library-release/closure-library-release.sh | 7 +++++++ script/closure_deps_graph.clj | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index 2e0a9335a..8a2d3486a 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -115,6 +115,13 @@ mkdir -p "$src_dir" "$third_party_src_dir" ## Generate deps.js ../closure_deps_graph.sh +## Normalize deps.js +perl -p -i -e 's/\]\);/\], \{\}\);/go' \ + "$closure_library_base/goog/deps.js" + +perl -p -i -e 's/..\/..\/third_party\/closure\/goog\///go' \ + "$closure_library_base/goog/deps.js" + ## Copy Closure sources cp -r \ diff --git a/script/closure_deps_graph.clj b/script/closure_deps_graph.clj index fa9a40094..094cd7b05 100644 --- a/script/closure_deps_graph.clj +++ b/script/closure_deps_graph.clj @@ -22,7 +22,7 @@ [] (map #(SourceFile/fromFile (.getAbsolutePath %)) (mapcat (comp js-files-in io/file) - ["closure-library/closure/goog"])) + ["closure-library/closure/goog" "closure-library/third_party/closure/goog"])) DepsGenerator$InclusionStrategy/ALWAYS (.getAbsolutePath (io/file "closure-library/closure/goog")) (proxy [BasicErrorManager] [] From 29363bbd1d34bb2aeb7b4d762ee4f4ba2f173ddf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 15 Aug 2021 21:41:31 -0400 Subject: [PATCH 3744/4033] CLJS-3321: Upgrade to Closure Compiler v20210808 (#101) CLJS-3321: Upgrade to Closure Compiler v20210808 --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 33 +++++++++++++++---------- src/main/clojure/cljs/externs.clj | 6 ++++- src/test/clojure/cljs/closure_tests.clj | 4 +-- 7 files changed, 31 insertions(+), 20 deletions(-) diff --git a/deps.edn b/deps.edn index 45052ea24..03fea05ea 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-unshaded {:mvn/version "v20210505"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20210808"} com.cognitect/transit-clj {:mvn/version "0.8.309"} org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} diff --git a/pom.template.xml b/pom.template.xml index ca6a12900..2161fff9d 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20210505 + v20210808 org.clojure diff --git a/project.clj b/project.clj index 6c297ea30..402b11d14 100644 --- a/project.clj +++ b/project.clj @@ -16,7 +16,7 @@ [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20201211-3e6c510d"] - [com.google.javascript/closure-compiler-unshaded "v20210505"]] + [com.google.javascript/closure-compiler-unshaded "v20210808"]] :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/script/bootstrap b/script/bootstrap index 44e636280..8455dac4b 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="20210505" +CLOSURE_RELEASE="20210808" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20201211-3e6c510d" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b3fb25b44..8575bbf79 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -26,7 +26,7 @@ [cljs.module-graph :as module-graph]) (:import [java.lang ProcessBuilder] [java.io - File BufferedInputStream BufferedReader + File BufferedReader BufferedInputStream Writer InputStreamReader IOException StringWriter ByteArrayInputStream] [java.net URI URL] [java.util.logging Level] @@ -39,9 +39,8 @@ SourceMap$DetailLevel ClosureCodingConvention SourceFile Result JSError CheckLevel DiagnosticGroup DiagnosticGroups CommandLineRunner - JSModule SourceMap VariableMap PrintStreamErrorManager DiagnosticType + JSChunk SourceMap VariableMap PrintStreamErrorManager DiagnosticType VariableRenamingPolicy PropertyRenamingPolicy] - [com.google.javascript.jscomp.bundle Transpiler] [com.google.javascript.jscomp.deps ClosureBundler ModuleLoader$ResolutionMode ModuleNames SimpleDependencyInfo] [com.google.javascript.rhino Node] @@ -117,16 +116,25 @@ (defmulti js-source-file (fn [_ source] (class source))) (defmethod js-source-file String [^String name ^String source] - (SourceFile/fromCode name source)) + (-> (SourceFile/builder) + (.withPath name) + (.withContent source) + (.build))) (defmethod js-source-file File [_ ^File source] - (SourceFile/fromPath (.toPath source) StandardCharsets/UTF_8)) + (-> (SourceFile/builder) + (.withPath (.toPath source)) + (.withCharset StandardCharsets/UTF_8) + (.build))) (defmethod js-source-file URL [_ ^URL source] (js-source-file _ (io/file (.getPath source)))) (defmethod js-source-file BufferedInputStream [^String name ^BufferedInputStream source] - (SourceFile/fromInputStream name source)) + (-> (SourceFile/builder) + (.withPath name) + (.withContent source) + (.build))) (def check-level {:error CheckLevel/ERROR @@ -180,7 +188,6 @@ :too-many-type-params DiagnosticGroups/TOO_MANY_TYPE_PARAMS :tweaks DiagnosticGroups/TWEAKS :type-invalidation DiagnosticGroups/TYPE_INVALIDATION - :undefined-names DiagnosticGroups/UNDEFINED_NAMES :undefined-variables DiagnosticGroups/UNDEFINED_VARIABLES :underscore DiagnosticGroups/UNDERSCORE :unknown-defines DiagnosticGroups/UNKNOWN_DEFINES @@ -1262,7 +1269,7 @@ "Given a list of IJavaScript sources in dependency order and compiler options return a dependency sorted list of module name / description tuples. The module descriptions will be augmented with a :closure-module entry holding - the Closure JSModule. Each module description will also be augmented with + the Closure JSChunk. Each module description will also be augmented with a :foreign-deps vector containing foreign IJavaScript sources in dependency order." [sources opts] @@ -1287,7 +1294,7 @@ (str "Module " name " does not define any :entries")) (when (:verbose opts) (util/debug-prn "Building module" name)) - (let [js-module (JSModule. (clojure.core/name name)) + (let [js-module (JSChunk. (clojure.core/name name)) module-sources (reduce (fn [ret entry-sym] @@ -1315,7 +1322,7 @@ (do (when (:verbose opts) (util/debug-prn " module" name "depends on" dep)) - (.addDependency js-module ^JSModule parent-module)) + (.addDependency js-module ^JSChunk parent-module)) (throw (util/compilation-error (IllegalArgumentException. (str "Parent module " dep " does not exist")))))) (conj ret @@ -1424,7 +1431,7 @@ (io/file prop-out))))) (defn optimize-modules - "Use the Closure Compiler to optimize one or more Closure JSModules. Returns + "Use the Closure Compiler to optimize one or more Closure JSChunks. Returns a dependency sorted list of module name and description tuples." [opts & sources] ;; the following pre-condition can't be enabled @@ -1445,7 +1452,7 @@ sources) modules (build-modules sources opts) ^List inputs (map (comp :closure-module second) modules) - _ (doseq [^JSModule input inputs] + _ (doseq [^JSChunk input inputs] (.sortInputsByDeps input closure-compiler)) _ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "Applying optimizations" (:optimizations opts) "to" (count sources) "sources")) @@ -1465,7 +1472,7 @@ :source (do (when source-map (.reset source-map)) - (.toSource closure-compiler ^JSModule closure-module))) + (.toSource closure-compiler ^JSChunk closure-module))) (when source-map (let [sw (StringWriter.) source-map-name (str output-to ".map.closure")] diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 417fd3403..37008ad49 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -16,6 +16,7 @@ [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino Node Token JSTypeExpression JSDocInfo$Visibility] + [java.nio.charset StandardCharsets] [java.util.logging Level])) (def ^:dynamic *ignore-var* false) @@ -237,7 +238,10 @@ {:name ns :defs (parsed->defs (parse-externs - (SourceFile/fromInputStream f (io/input-stream rsrc))))})))) + (-> (SourceFile/builder) + (.withPath (.toPath (io/file (.getPath rsrc)))) + (.withContent (io/input-stream rsrc)) + (.build))))})))) (comment (require '[clojure.java.io :as io] diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index b35cee9f7..72e3082d7 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -19,7 +19,7 @@ [clojure.java.io :as io] [clojure.string :as string]) (:import [java.io File] - [com.google.javascript.jscomp JSModule])) + [com.google.javascript.jscomp JSChunk])) (deftest test-make-preamble (testing "no options" @@ -60,7 +60,7 @@ (test/delete-out-files out) (build/build (build/inputs (:inputs project)) (:opts project)) (let [compiler (closure/make-closure-compiler) - module (JSModule. "module-c")] + module (JSChunk. "module-c")] (.initOptions compiler (closure/make-options (:opts project))) (doseq [file ["cljs/core/constants.js" "module_test/modules/a.js" From 5590b8d718dc1c421581f844214a156a3d2d24b0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 25 Aug 2021 23:29:06 -0400 Subject: [PATCH 3745/4033] CLJS-3324: hash-map behavior differs from Clojure (#102) * CLJS-3324: hash-map behavior differs from Clojure Add runtime check to hash-map w/o changing perf. Add runtime check to PersistentHashMap.fromArrays w/o significant perf change. hash-map is also an inlining macro which invokes PersistentHashMap.fromArrays. naive use of partition leads to garbage in / out. Pad to fill in the missing value, then remove - then when computing the values drop when we hit a non-pair. This way we get non-matching array lengths and can defer to the runtime check. Add tests. --- src/main/cljs/cljs/core.cljs | 9 +++++++-- src/main/clojure/cljs/core.cljc | 7 +++++-- src/test/cljs/cljs/collections_test.cljs | 7 +++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ba0b6e7df..d316c559f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -8052,7 +8052,9 @@ reduces them without incurring seq initialization" (let [len (alength ks)] (loop [i 0 ^not-native out (transient (.-EMPTY PersistentHashMap))] (if (< i len) - (recur (inc i) (-assoc! out (aget ks i) (aget vs i))) + (if (<= (alength vs) 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)))))) (set! (.-createWithCheck PersistentHashMap) @@ -8926,7 +8928,10 @@ reduces them without incurring seq initialization" [& keyvals] (loop [in (seq keyvals), out (transient (.-EMPTY PersistentHashMap))] (if in - (recur (nnext in) (assoc! out (first in) (second in))) + (let [in' (next in)] + (if (nil? in') + (throw (js/Error. (str "No value supplied for key: " (first in)))) + (recur (next in') (assoc! out (first in) (first in')) ))) (persistent! out)))) (defn array-map diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a055f5adf..ab3bc3bf9 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2623,9 +2623,12 @@ (core/defmacro hash-map ([] `(.-EMPTY cljs.core/PersistentHashMap)) ([& kvs] - (core/let [pairs (partition 2 kvs) + (core/let [pairs (map + (core/fn [pair] + (remove #{::missing} pair)) + (partition 2 2 (repeat ::missing) kvs)) ks (map first pairs) - vs (map second pairs)] + vs (map second (take-while #(= 2 (count %)) pairs))] (vary-meta `(.fromArrays cljs.core/PersistentHashMap (array ~@ks) (array ~@vs)) assoc :tag 'cljs.core/PersistentHashMap)))) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 64f0fcbc0..9f44633d1 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -1084,6 +1084,13 @@ (testing "persistent vector invoke matches clojure" (is (thrown-with-msg? js/Error #"Key must be integer" ([1 2] nil))))) +(deftest test-cljs-3324 + (testing "hash-map behavior with missing values matches clojure" + (is (thrown-with-msg? js/Error #"No value supplied for key: :a" + (hash-map :a))) + (is (thrown-with-msg? js/Error #"No value supplied for key: :a" + (apply hash-map [:a]))))) + (comment (run-tests) From aec9f0c576ac6b11d3e30e79553c8ebe67bf0f73 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 31 Aug 2021 07:34:25 -0400 Subject: [PATCH 3746/4033] CLJS-3074: Resolve :warning-handlers compiler option when symbols Handle in add-implicit-options. Add tests. --- src/main/clojure/cljs/closure.clj | 67 +++++++++++++++++-------- src/test/clojure/cljs/closure_tests.clj | 21 ++++++++ 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8575bbf79..b84b7382a 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -83,25 +83,27 @@ "Converts a namespaced symbol to a var, loading the requisite namespace if needed. For use with a function defined under a keyword in opts. The kw and ex-data arguments are used to form exceptions." - [sym kw ex-data] - (let [ns (namespace sym) - _ (when (nil? ns) - (throw - (ex-info (str kw " symbol " sym " is not fully qualified") - (merge ex-data {kw sym - :clojure.error/phase :compilation})))) - var-ns (symbol ns)] - (when (not (find-ns var-ns)) - (try - (locking ana/load-mutex - (require var-ns)) - (catch Throwable t - (throw (ex-info (str "Cannot require namespace referred by " kw " value " sym) - (merge ex-data {kw sym - :clojure.error/phase :compilation}) - t))))) - - (find-var sym))) + ([sym kw] + (sym->var sym kw nil)) + ([sym kw ex-data] + (let [ns (namespace sym) + _ (when (nil? ns) + (throw + (ex-info (str kw " symbol " sym " is not fully qualified") + (merge ex-data {kw sym + :clojure.error/phase :compilation})))) + var-ns (symbol ns)] + (when (not (find-ns var-ns)) + (try + (locking ana/load-mutex + (require var-ns)) + (catch Throwable t + (throw (ex-info (str "Cannot require namespace referred by " kw " value " sym) + (merge ex-data {kw sym + :clojure.error/phase :compilation}) + t))))) + + (find-var sym)))) (defn- opts-fn "Extracts a function from opts, by default expecting a function value, but @@ -2483,6 +2485,28 @@ [(if (symbol? k) (str (comp/munge k)) k) v]) defines))) +(defn resolve-warning-handlers [fns] + (reduce + (fn [ret afn] + (cond + (fn? afn) (conj ret afn) + + (symbol? afn) + (let [afn' (sym->var afn :warning-handlers)] + (when-not afn' + (throw + (ex-info (str "Could not resolve warning handler: " afn) + {:warning-handlers fns + :clojure.error/phase :compilation}))) + (conj ret afn')) + + :else + (throw + (ex-info (str "Invalid warning handler " afn " of type " (type afn)) + {:warning-handlers fns + :clojure.error/phase :compilation})))) + [] fns)) + (defn add-implicit-options [{:keys [optimizations output-dir] :or {optimizations :none @@ -2586,7 +2610,10 @@ opts))) (nil? (:ignore-js-module-exts opts)) - (assoc :ignore-js-module-exts [".css"])))) + (assoc :ignore-js-module-exts [".css"]) + + (:warning-handlers opts) + (update :warning-handlers resolve-warning-handlers)))) (defn- alive? [proc] (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 72e3082d7..4964a2c6b 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -490,3 +490,24 @@ (.delete (io/file "package.json")) (test/delete-node-modules) (test/delete-out-files out))) + +(defn empty-handler [warning-type env extra]) + +(deftest test-cljs-3074 + (testing "CLJS-3074: resolve-warning-handlers\n" + (testing "\tfunctions are left alone" + (let [h (fn [warning-type env extra])] + (is (= [h] (closure/resolve-warning-handlers [h]))))) + (testing "\tsymbols are resolved" + (is (= [#'empty-handler] (closure/resolve-warning-handlers [`empty-handler])))) + (testing "\tsymbols and fns can be mixed" + (let [h (fn [warning-type env extra])] + (is (= [h #'empty-handler] (closure/resolve-warning-handlers [h `empty-handler]))))) + (testing "\tinvalid warning handler types are detected" + (is (thrown-with-msg? Throwable + #"Invalid warning handler 1 of type class java.lang.Long" + (closure/resolve-warning-handlers [1])))) + (testing "\tnon-existent handlers are detected" + (is (thrown-with-msg? Throwable + #"Could not resolve warning handler: clojure.core/foo" + (closure/resolve-warning-handlers ['clojure.core/foo])))))) From 46f7f2d47540e76179736c5d5dd783fffd2d220d Mon Sep 17 00:00:00 2001 From: Chance Russell Date: Sun, 24 Mar 2019 11:27:47 -0500 Subject: [PATCH 3747/4033] CLJS-3056: runtime namespace load order is independent from ordering in ns macro :require form Ensures that the :requires construction in cljs.compiler/emit-source maintains the ordering that namespaces were required in the code being compiled. Currently, emit-source throws the analyzed dependencies into a Clojure set, which does not preserve ordering. The patch uses the same approach as a similar fix for CLJS-1453. --- src/main/clojure/cljs/compiler.cljc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a03b7b8ae..7415678f7 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1557,7 +1557,7 @@ emit)] (loop [forms (ana/forms-seq* rdr (util/path src)) ns-name nil - deps nil] + deps []] (if (seq forms) (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*)) {:keys [op] :as ast} (ana/analyze env (first forms) nil opts)] @@ -1569,7 +1569,7 @@ 'cljs.core$macros ns-name)] (emit ast) - (recur (rest forms) ns-name (merge (:uses ast) (:requires ast)))) + (recur (rest forms) ns-name (into deps (:deps ast)))) (= :ns* (:op ast)) (let [ns-emitted? (some? ns-name) @@ -1579,7 +1579,7 @@ (if-not ns-emitted? (emit (assoc ast :name ns-name :op :ns)) (emit ast)) - (recur (rest forms) ns-name (merge deps (:uses ast) (:requires ast)))) + (recur (rest forms) ns-name (into deps (:deps ast)))) :else (let [ns-emitted? (some? ns-name) @@ -1600,11 +1600,11 @@ {:ns (or ns-name 'cljs.user) :macros-ns (:macros-ns opts) :provides [ns-name] - :requires (if (= ns-name 'cljs.core) - (set (vals deps)) - (cond-> (conj (set (vals deps)) 'cljs.core) - (get-in @env/*compiler* [:options :emit-constants]) - (conj ana/constants-ns-sym))) + :requires (cond-> (distinct deps) + (get-in @env/*compiler* [:options :emit-constants]) + (conj ana/constants-ns-sym) + (not= ns-name 'cljs.core) + (conj 'cljs.core)) :file dest :out-file (.toString ^File dest) :source-file src} From 71c6f47ab90340fe511314238c2c5ca11cbaf319 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 Oct 2021 16:20:27 -0400 Subject: [PATCH 3748/4033] * dep.js generation missing base.js special case --- script/closure-library-release/closure-library-release.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index 8a2d3486a..0a9595a2d 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -122,6 +122,9 @@ perl -p -i -e 's/\]\);/\], \{\}\);/go' \ perl -p -i -e 's/..\/..\/third_party\/closure\/goog\///go' \ "$closure_library_base/goog/deps.js" +perl -p -i -e "s/goog.addDependency\('base.js', \[\], \[\], \{\}\);/goog.addDependency\(\'base.js\', \[\'goog\'\], \[\], \{\}\);/go" \ + "$closure_library_base/goog/deps.js" + ## Copy Closure sources cp -r \ From 76ac6bf41300f821f052d03537edf7e86694fc6d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 15 Oct 2021 12:13:34 -0400 Subject: [PATCH 3749/4033] CLJS-3322: Upgrade to latest Closure Library - handle non-legacy GCL goog.module namespaces (#105) * bump gcl * add goog.module handling case to analyzer * add goog.module handling case to compiler * add missing case to browser REPL bootstrap for goog.module * generalize munge-goog-module-lib * add resolve-import helper * fixup emission test * resolve goog.module imports in resolve-var * check that emission doesn't shadow import usage * wrap goog.module.get in goog.scope to avoid silly Closure warnings * remove dependency on goog.object from cljs.core macros file * add comment about goog.scope workaround * add docstrings to extern parsing ns * add Token/FUNCTION case * Token/NAME should check for simple name * handle goog.module case in parse-externs * make parsed->defs a multimethod * add resource->source-file helper * ^Node type hints * in NAME case stop if parent is OBJECTLIT * STRING_KEY case does not need split on "." * when parsing goog.modules, only provide ClojureScript defs for exported defs * remove unneeded condition * add internal reference to goog.math.Long (to allow self-parity tests to pass) * test cases --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/cljs/cljs/core.cljs | 4 + src/main/cljs/cljs/loader.cljs | 4 +- src/main/cljs/clojure/browser/repl.cljs | 5 +- src/main/clojure/cljs/analyzer.cljc | 36 ++++- src/main/clojure/cljs/compiler.cljc | 18 +++ src/main/clojure/cljs/core.cljc | 10 +- src/main/clojure/cljs/externs.clj | 132 +++++++++++++----- src/test/cljs/cljs/predicates_test.cljs | 16 ++- .../cljs/analyzer/glib_module_test.clj | 62 ++++++++ .../cljs/compiler/glib_module_test.clj | 21 +++ .../clojure/cljs/externs_parsing_tests.clj | 5 + 15 files changed, 269 insertions(+), 52 deletions(-) create mode 100644 src/test/clojure/cljs/analyzer/glib_module_test.clj create mode 100644 src/test/clojure/cljs/compiler/glib_module_test.clj diff --git a/deps.edn b/deps.edn index 03fea05ea..82f1a4324 100644 --- a/deps.edn +++ b/deps.edn @@ -5,7 +5,7 @@ org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "0.2.6"} - org.clojure/google-closure-library {:mvn/version "0.0-20201211-3e6c510d"} + org.clojure/google-closure-library {:mvn/version "0.0-20211011-0726fdeb"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/tools.reader {:mvn/version "1.3.3"} org.clojure/test.check {:mvn/version "0.10.0-alpha3"}} diff --git a/pom.template.xml b/pom.template.xml index 2161fff9d..77f35e8bd 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20201211-3e6c510d + 0.0-20210811-6da97fe1 org.clojure diff --git a/project.clj b/project.clj index 402b11d14..27d0ddd13 100644 --- a/project.clj +++ b/project.clj @@ -15,7 +15,7 @@ [org.clojure/tools.reader "1.3.3"] [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] - [org.clojure/google-closure-library "0.0-20201211-3e6c510d"] + [org.clojure/google-closure-library "0.0-20210811-6da97fe1"] [com.google.javascript/closure-compiler-unshaded "v20210808"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main cljs.main} diff --git a/script/bootstrap b/script/bootstrap index 8455dac4b..45e4838e0 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -8,7 +8,7 @@ CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20210808" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" -GCLOSURE_LIB_RELEASE="0.0-20201211-3e6c510d" +GCLOSURE_LIB_RELEASE="0.0-20210811-6da97fe1" TREADER_RELEASE="1.3.3" TEST_CHECK_RELEASE="0.10.0-alpha3" diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d316c559f..aca00175d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2284,6 +2284,10 @@ reduces them without incurring seq initialization" (not (identical? n js/Infinity)) (== (js/parseFloat n) (js/parseInt n 10)))) +(def + ^{:doc "INTERNAL: do not use"} + LongImpl goog.math.Long) + (defn int? "Return true if x satisfies integer? or is an instance of goog.math.Integer or goog.math.Long." diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs index 33405cd47..473d0e5b8 100644 --- a/src/main/cljs/cljs/loader.cljs +++ b/src/main/cljs/cljs/loader.cljs @@ -9,8 +9,8 @@ (ns cljs.loader (:require [goog.object :as gobj] [goog.html.legacyconversions :as legacy]) - (:import [goog.module ModuleLoader] - [goog.module ModuleManager])) + (:import [goog.module ModuleManager] + [goog.module ModuleLoader])) (def module-infos MODULE_INFOS) ;; set by compiler (def module-uris diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs index 891f7a03b..2010f8c51 100644 --- a/src/main/cljs/clojure/browser/repl.cljs +++ b/src/main/cljs/clojure/browser/repl.cljs @@ -227,7 +227,10 @@ (let [ret (.require__ js/goog src)] (when (= reload "reload-all") (set! (.-cljsReloadAll_ js/goog) false)) - ret)))))) + ;; handle requires from Closure Library goog.modules + (if (js/goog.isInModuleLoader_) + (js/goog.module.getInternal_ src) + ret))))))) (defn connect "Connects to a REPL server from an HTML document. After the diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c70eabc7e..12a6bab46 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -812,6 +812,12 @@ (or (contains? global-exports (symbol module)) (contains? global-exports (name module))))) +(defn goog-module-dep? + [module] + (let [[module _] (lib&sublib module) + module-type (get-in @env/*compiler* [:js-dependency-index (str module) :module])] + (= :goog module-type))) + (defn confirm-var-exists ([env prefix suffix] (let [warn (confirm-var-exist-warning env prefix suffix)] @@ -1010,6 +1016,12 @@ (str "node$module$" (munge (string/replace (str name) #"[.\/]" #?(:clj "\\$" :cljs "$$"))))) +(defn munge-goog-module-lib + ([name] + (str "goog$module$" (munge (string/replace (str name) #"[.\/]" #?(:clj "\\$" :cljs "$$"))))) + ([ns name] + (str (munge ns) "." (munge-goog-module-lib name)))) + (defn munge-global-export [name] (str "global$module$" (munge (string/replace (str name) #"[.\/]" #?(:clj "\\$" :cljs "$$"))))) @@ -1031,6 +1043,7 @@ (defn ns->module-type [ns] (cond + (goog-module-dep? ns) :goog-module (js-module-exists? ns) :js (node-module-dep? ns) :node (dep-has-global-exports? ns) :global)) @@ -1072,6 +1085,12 @@ :op :js-var :foreign true})) +(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}) + (defmethod resolve* :global [env sym full-ns current-ns] (let [pre (extern-pre sym current-ns)] @@ -1135,6 +1154,15 @@ :op :js-var :ns current-ns}))) +(defn resolve-import + "goog.modules are deterministically assigned to a property of the namespace, + we cannot expect the reference will be globally available, so we resolve to + namespace local reference." + [env import] + (if (goog-module-dep? import) + (symbol (munge-goog-module-lib (-> env :ns :name) import)) + import)) + ;; core.async calls `macroexpand-1` manually with an ill-formed ;; :locals map. Normally :locals maps symbols maps, but ;; core.async adds entries mapping symbols to symbols. We work @@ -1207,7 +1235,13 @@ ;; check if prefix is some existing def (if-let [resolved (resolve-var env prefix nil false)] (update resolved :name #(symbol (str % "." suffix))) - (let [idx (.lastIndexOf s ".") + ;; glib imports (i.e. (:import [goog.module ModuleLoader]) + ;; are always just dotted symbols after the recursion + (let [s (str + (cond->> s + (goog-module-dep? sym) + (resolve-import env))) + idx (.lastIndexOf (str s) ".") pre (subs s 0 idx) suf (subs s (inc idx))] {:op :var diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 7415678f7..cc5675a32 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1294,6 +1294,9 @@ (let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? libs)] [node-libs libs-to-load]) [nil libs])) + [goog-modules libs-to-load] (let [{goog-modules true libs-to-load false} + (group-by ana/goog-module-dep? libs-to-load)] + [goog-modules libs-to-load]) global-exports-libs (filter ana/dep-has-global-exports? libs-to-load)] (when (-> libs meta :reload-all) (emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set([\"cljs.core\"]);") @@ -1336,11 +1339,26 @@ :else (when-not (= lib 'goog) (emitln "goog.require('" (munge lib) "');")))) + ;; Node Libraries (doseq [lib node-libs] (let [[lib' sublib] (ana/lib&sublib lib)] (emitln (munge ns-name) "." (ana/munge-node-lib lib) " = require('" lib' "')" (sublib-select sublib) ";"))) + ;; Google Closure Library Modules (i.e. goog.module(...)) + ;; these must be assigned to vars + (doseq [lib goog-modules] + (let [[lib' sublib] (ana/lib&sublib lib)] + (emitln "goog.require('" lib' "');") + ;; we emit goog.scope here to suppress a Closure error about + ;; goog.module.get when compiling - meant to discourage incorrect + ;; usage by hand written code - not applicable here + (emitln "goog.scope(function(){") + (emitln (munge ns-name) "." + (ana/munge-goog-module-lib lib) + " = goog.module.get('" lib' "')" (sublib-select sublib) ";") + (emitln "});"))) + ;; 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))) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index ab3bc3bf9..63ad476e9 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1430,9 +1430,9 @@ (core/let [psym (resolve p) pfn-prefix (subs (core/str psym) 0 (clojure.core/inc (.indexOf (core/str psym) "/")))] - (cons `(goog.object/set ~psym ~type true) + (cons `(unchecked-set ~psym ~type true) (map (core/fn [[f & meths :as form]] - `(goog.object/set ~(symbol (core/str pfn-prefix f)) + `(unchecked-set ~(symbol (core/str pfn-prefix f)) ~type ~(with-meta `(fn ~@meths) (meta form)))) sigs)))) @@ -2672,8 +2672,8 @@ (js-obj* '()) `(let [~@(apply concat (clojure.set/map-invert expr->local)) ~obj ~(js-obj* (filter-on-keys core/string? kvs))] - ~@(map (core/fn [[k v]] `(goog.object/set ~obj ~k ~v)) sym-pairs) - ~@(map (core/fn [[k v]] `(goog.object/set ~obj ~v ~(core/get kvs k))) expr->local) + ~@(map (core/fn [[k v]] `(unchecked-set ~obj ~k ~v)) sym-pairs) + ~@(map (core/fn [[k v]] `(unchecked-set ~obj ~v ~(core/get kvs k))) expr->local) ~obj)))) (core/defmacro alength [a] @@ -2888,7 +2888,7 @@ (core/list 'js* "''+~{}" s)) (core/defmacro es6-iterable [ty] - `(goog.object/set (.-prototype ~ty) cljs.core/ITER_SYMBOL + `(unchecked-set (.-prototype ~ty) cljs.core/ITER_SYMBOL (fn [] (this-as this# (cljs.core/es6-iterator this#))))) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 37008ad49..b7b42fa36 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -16,7 +16,6 @@ [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino Node Token JSTypeExpression JSDocInfo$Visibility] - [java.nio.charset StandardCharsets] [java.util.logging Level])) (def ^:dynamic *ignore-var* false) @@ -26,7 +25,10 @@ ;; ------------------------------------------------------------------------------ ;; Externs Parsing -(defn annotate [props ty] +(defn annotate + "Given a sequential list of properties [foo core baz] representing segments + of the namespace, annotate the last symbol with the type information." + [props ty] (when (seq props) (conj (into [] (butlast props)) @@ -35,7 +37,8 @@ (defn get-tag [^JSTypeExpression texpr] (when-let [root (.getRoot texpr)] (if (.isString root) - (symbol (.getString root))(if-let [child (.. root getFirstChild)] + (symbol (.getString root)) + (if-let [child (.. root getFirstChild)] (if (.isString child) (symbol (.. child getString))))))) @@ -97,17 +100,26 @@ (fn [^Node node] (.getToken node))) -(defmethod parse-extern-node Token/VAR [node] +;; handle named function case (i.e. goog.modules) +;; function foo {}, the entire function is the node +(defmethod parse-extern-node Token/FUNCTION [^Node node] + (when (> (.getChildCount node) 0) + (let [ty (get-var-info node)] + (doto + (cond-> (parse-extern-node (.getFirstChild node)) + ty (-> first (annotate ty) vector)))))) + +(defmethod parse-extern-node Token/VAR [^Node node] (when (> (.getChildCount node) 0) (let [ty (get-var-info node)] (cond-> (parse-extern-node (.getFirstChild node)) ty (-> first (annotate ty) vector))))) -(defmethod parse-extern-node Token/EXPR_RESULT [node] +(defmethod parse-extern-node Token/EXPR_RESULT [^Node node] (when (> (.getChildCount node) 0) (parse-extern-node (.getFirstChild node)))) -(defmethod parse-extern-node Token/ASSIGN [node] +(defmethod parse-extern-node Token/ASSIGN [^Node node] (when (> (.getChildCount node) 0) (let [ty (get-var-info node) lhs (cond-> (first (parse-extern-node (.getFirstChild node))) @@ -120,13 +132,24 @@ lhs)) [lhs])))) -(defmethod parse-extern-node Token/NAME [node] - (let [lhs (map symbol (string/split (.getQualifiedName node) #"\."))] - (if (> (.getChildCount node) 0) - (let [externs (parse-extern-node (.getFirstChild node))] - (conj (map (fn [ext] (concat lhs ext)) externs) - lhs)) - [lhs]))) +;; JavaScript name +;; function foo {}, in this case the `foo` name node +;; {"foo": bar}, in this case the `bar` name node +(defmethod parse-extern-node Token/NAME [^Node node] + (if (= Token/STRING_KEY (-> node .getParent .getToken)) + ;; if we are inside an object literal we are done + [] + ;; also check .getString - goog.module defs won't have qualified names + (let [name (or (.getQualifiedName node) (.getString node)) + lhs (when-not (string/blank? name) + (map symbol (string/split name #"\.")))] + (if (seq lhs) + (if (> (.getChildCount node) 0) + (let [externs (parse-extern-node (.getFirstChild node))] + (conj (map (fn [ext] (concat lhs ext)) externs) + lhs)) + [lhs]) + [])))) (defmethod parse-extern-node Token/GETPROP [node] (when-not *ignore-var* @@ -135,6 +158,8 @@ (annotate props ty) props)]))) +;; JavaScript Object literal +;; { ... } (defmethod parse-extern-node Token/OBJECTLIT [node] (when (> (.getChildCount node) 0) (loop [nodes (.children node) @@ -144,8 +169,10 @@ (recur (rest nodes) (concat externs (parse-extern-node (first nodes)))))))) -(defmethod parse-extern-node Token/STRING_KEY [node] - (let [lhs (map symbol (string/split (.getString node) #"\."))] +;; Object literal string key node +;; {"foo": bar} - the key and value together +(defmethod parse-extern-node Token/STRING_KEY [^Node node] + (let [lhs [(-> node .getString symbol)]] (if (> (.getChildCount node) 0) (let [externs (parse-extern-node (.getFirstChild node))] (conj (map (fn [ext] (concat lhs ext)) externs) @@ -154,7 +181,17 @@ (defmethod parse-extern-node :default [node]) -(defn parse-externs [^SourceFile source-file] +(defn parse-externs + "Returns a sequential collection of the form: + + [[foo core first] + [foo core next] + [foo core baz last] ...] + + Where the last symbol is annotated with var info via metadata. This simple + structure captures the nested form of Closure namespaces and aids + direct indexing." + [^SourceFile source-file] (binding [*source-file* (.getName source-file)] (let [^CompilerOptions compiler-options (doto (CompilerOptions.) @@ -167,8 +204,13 @@ compiler) (.init (list source-file) '() compiler-options)) js-ast (JsAst. source-file) - ^Node root (.getAstRoot js-ast closure-compiler)] - (loop [nodes (.children root) + ^Node root (.getAstRoot js-ast closure-compiler) + nodes (.children root)] + (loop [nodes (cond-> nodes + ;; handle goog.modules which won't have top-levels + ;; need to look at internal children + (= Token/MODULE_BODY (some-> nodes first .getToken)) + (-> first .children)) externs []] (if (empty? nodes) externs @@ -215,17 +257,42 @@ (= (inc (count ns-segs)) (count var-segs)) (= ns-segs (take (count ns-segs) var-segs))))) -(defn parsed->defs [externs] - (let [ns-segs (into [] (map symbol (string/split (str *goog-ns*) #"\.")))] - (reduce - (fn [m xs] - ;; ignore definitions from other provided namespaces not under consideration - (if (ns-match? ns-segs xs) - (let [sym (last xs)] - (cond-> m - (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym})))) - m)) - {} externs))) +(defmulti parsed->defs (fn [_ module-type] module-type)) + +(defmethod parsed->defs :goog + ([externs _] + (let [grouped (group-by #(= 'exports (first %)) externs) + exports (->> (get grouped true) + (map (comp vec rest)) + (remove empty?) + set) + exported (filter exports (get grouped false))] + (reduce + (fn [m xs] + (let [sym (last xs)] + (cond-> m + (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym}))))) + {} exported)))) + +(defmethod parsed->defs :default + ([externs _] + (let [ns-segs (into [] (map symbol (string/split (str *goog-ns*) #"\.")))] + (reduce + (fn [m xs] + ;; ignore definitions from other provided namespaces not under consideration + (if (ns-match? ns-segs xs) + (let [sym (last xs)] + (cond-> m + (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym})))) + m)) + {} externs)))) + +(defn resource->source-file + [resource] + (-> (SourceFile/builder) + (.withPath (.toPath (io/file (.getPath resource)))) + (.withContent (io/input-stream resource)) + (.build))) (defn analyze-goog-file ([f] @@ -237,11 +304,8 @@ (binding [*goog-ns* ns] {:name ns :defs (parsed->defs - (parse-externs - (-> (SourceFile/builder) - (.withPath (.toPath (io/file (.getPath rsrc)))) - (.withContent (io/input-stream rsrc)) - (.build))))})))) + (parse-externs (resource->source-file rsrc)) + (:module desc))})))) (comment (require '[clojure.java.io :as io] diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs index 2e49406c5..acb50f4b0 100644 --- a/src/test/cljs/cljs/predicates_test.cljs +++ b/src/test/cljs/cljs/predicates_test.cljs @@ -8,7 +8,7 @@ (ns cljs.predicates-test (:require [cljs.test :as test :refer-macros [deftest is]]) - (:import [goog.math Long Integer])) + (:import [goog.math Integer])) (def pred-val-table (let [uuid (uuid "00000000-0000-0000-0000-000000000000")] @@ -41,9 +41,15 @@ (let [posint 10e10 negint -10e10 neg0 (/ ##-Inf) - natl (Long.getZero) - posl (Long.fromNumber posint) - negl (Long.fromNumber negint) + ;; NOTE: we must go through a var because in self-parity tests + ;; we cannot simply import goog.module namespaces if cljs.core + ;; depends on the type - that's because cljs.core was *separately + ;; compiled* already bundling goog.modules. In many cases this is + ;; not an issue, but it is an issue if internally we use the type + ;; to make instanceof assertions - which we do for Long + natl (.getZero LongImpl) + posl (.fromNumber LongImpl posint) + negl (.fromNumber LongImpl negint) nati Integer.ZERO posi (Integer.fromNumber posint) negi (Integer.fromNumber negint)] @@ -69,4 +75,4 @@ (let [v (first row)] (dotimes [i (count row)] (is (= ((nth preds i) v) (nth row i)) - (pr-str (list (nth preds i) v)))))))) \ No newline at end of file + (pr-str (list (nth preds i) v)))))))) diff --git a/src/test/clojure/cljs/analyzer/glib_module_test.clj b/src/test/clojure/cljs/analyzer/glib_module_test.clj new file mode 100644 index 000000000..ad4d126ea --- /dev/null +++ b/src/test/clojure/cljs/analyzer/glib_module_test.clj @@ -0,0 +1,62 @@ +(ns cljs.analyzer.glib-module-test + (:require [cljs.analyzer :as ana] + [cljs.analyzer-tests :as ana-tests] + [clojure.test :as test :refer [deftest is testing]] + [cljs.env :as env])) + +(deftest glib-module-detect-test + (testing "Basic glib module detection" + (is (= :goog (get-in @ana-tests/test-cenv [:js-dependency-index (munge "goog.module.ModuleLoader") :module]))))) + +(deftest glib-module-predicate-test + (testing "glib module detection predicate" + (env/with-compiler-env ana-tests/test-cenv + (is (ana/goog-module-dep? 'goog.module.ModuleLoader))))) + +(deftest glib-module-classification-test + (testing "glib module classification" + (env/with-compiler-env ana-tests/test-cenv + (is (= :goog-module (ana/ns->module-type 'goog.module.ModuleLoader)))))) + +(deftest glib-module-resolve-var-test + (testing "glib module var resolution" + (let [cenv (env/default-compiler-env) + ns-ast (ana-tests/analyze-forms cenv + '[(ns foo.core + (:require [goog.module.ModuleLoader :as module-loader]))]) + aenv (assoc (ana/empty-env) :ns (ana/get-namespace cenv 'foo.core))] + (is (= '{:name foo.core/goog$module$goog$module$ModuleLoader.EventType + :ns foo.core + :op :var} + (env/with-compiler-env cenv + (ana/resolve-var aenv 'module-loader/EventType))))))) + +(deftest glib-module-resolve-import-test + (testing "glib module resolve import helper test" + (let [cenv (env/default-compiler-env) + ns-ast (ana-tests/analyze-forms cenv + '[(ns foo.core + (:require [goog.module.ModuleLoader :as module-loader]))]) + aenv (assoc (ana/empty-env) :ns (ana/get-namespace cenv 'foo.core))] + (is (= 'foo.core.goog$module$goog$module$ModuleLoader + (env/with-compiler-env cenv + (ana/resolve-import aenv 'goog.module.ModuleLoader))))))) + +(deftest glib-module-resolve-import-var-test + (testing "glib module :import var resolution" + (let [cenv (env/default-compiler-env) + ns-ast (ana-tests/analyze-forms cenv + '[(ns foo.core + (:import [goog.module ModuleLoader]))]) + aenv (assoc (ana/empty-env) :ns (ana/get-namespace cenv 'foo.core))] + (is (= '{:name foo.core.goog$module$goog$module$ModuleLoader + :ns goog.module ;; a bit odd, but doesn't matter, for emission we just :name + :op :var} + (env/with-compiler-env cenv + (ana/resolve-var aenv 'ModuleLoader))))))) + +(comment + + (test/run-tests) + + ) diff --git a/src/test/clojure/cljs/compiler/glib_module_test.clj b/src/test/clojure/cljs/compiler/glib_module_test.clj new file mode 100644 index 000000000..c9813d209 --- /dev/null +++ b/src/test/clojure/cljs/compiler/glib_module_test.clj @@ -0,0 +1,21 @@ +(ns cljs.compiler.glib-module-test + (:require [cljs.compiler-tests :as comp-tests] + [cljs.env :as env] + [clojure.test :as test :refer [deftest is testing]])) + +(deftest test-glib-module-compile + (testing "glib modules compiled to Closure Compile expectations" + (let [src (env/with-compiler-env (env/default-compiler-env) + (comp-tests/compile-form-seq + '[(ns test.foo + (:import [goog.module ModuleLoader])) + (def module-loader (ModuleLoader.))]))] + (is (re-find #"goog\.require\('goog\.module\.ModuleLoader'\)" src)) + (is (re-find #"test\.foo\.goog\$module\$goog\$module\$ModuleLoader = goog\.module\.get\('goog.module.ModuleLoader'\)" src)) + (is (re-find #"test\.foo\.module_loader = \(new test\.foo\.goog\$module\$goog\$module\$ModuleLoader\(\)\)" src))))) + +(comment + + (test/run-tests) + + ) diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index cc6bd0136..7bcc44539 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -36,6 +36,11 @@ (comment + (externs/parse-externs + (externs/resource->source-file (io/resource "goog/object/object.js"))) + + (externs/analyze-goog-file "goog/object/object.js") + (test/run-tests) (externs/analyze-goog-file "goog/date/date.js" 'goog.date.month) From 1a9a10f283429f716838e982881b6512325ad09c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 19 Oct 2021 16:29:58 -0400 Subject: [PATCH 3750/4033] CLJS-3330: Flag for legacy loading of goog.object & goog.array --- src/main/clojure/cljs/analyzer.cljc | 10 ++++++++-- src/main/clojure/cljs/closure.clj | 2 +- .../clojure/cljs/compiler/glib_module_test.clj | 14 +++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 12a6bab46..9cd94fe8f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -815,8 +815,14 @@ (defn goog-module-dep? [module] (let [[module _] (lib&sublib module) - module-type (get-in @env/*compiler* [:js-dependency-index (str module) :module])] - (= :goog module-type))) + module-str (str module) + options (compiler-options)] + ;; CLJS-3330: flag for loading some old things in the old way to give time + ;; for library authors to migrate + (if (and (:global-goog-object&array options) + (#{"goog.object" "goog.array"} module-str)) + false + (= :goog (get-in @env/*compiler* [:js-dependency-index module-str :module]))))) (defn confirm-var-exists ([env prefix suffix] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b84b7382a..c3f5ea251 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -213,7 +213,7 @@ :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}) + :nodejs-rt :target-fn :deps-cmd :bundle-cmd :global-goog-object&array}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 diff --git a/src/test/clojure/cljs/compiler/glib_module_test.clj b/src/test/clojure/cljs/compiler/glib_module_test.clj index c9813d209..3e6f12c0f 100644 --- a/src/test/clojure/cljs/compiler/glib_module_test.clj +++ b/src/test/clojure/cljs/compiler/glib_module_test.clj @@ -5,7 +5,7 @@ (deftest test-glib-module-compile (testing "glib modules compiled to Closure Compile expectations" - (let [src (env/with-compiler-env (env/default-compiler-env) + (let [src (env/with-compiler-env (env/default-compiler-env ) (comp-tests/compile-form-seq '[(ns test.foo (:import [goog.module ModuleLoader])) @@ -14,6 +14,18 @@ (is (re-find #"test\.foo\.goog\$module\$goog\$module\$ModuleLoader = goog\.module\.get\('goog.module.ModuleLoader'\)" src)) (is (re-find #"test\.foo\.module_loader = \(new test\.foo\.goog\$module\$goog\$module\$ModuleLoader\(\)\)" src))))) +(deftest cljs-3330-global-goog-object&array + (testing "migration path for goog.module impact on goog.object & goog.array" + (let [src (env/with-compiler-env + (env/default-compiler-env {:global-goog-object&array true}) + (comp-tests/compile-form-seq + '[(ns test.foo + (:require [goog.object :as gobj] + [goog.array :as garray])) + (def module-loader (ModuleLoader.))]))] + (is (re-find #"goog\.require\('goog\.object\'\)" src)) + (is (re-find #"goog\.require\('goog\.array\'\)" src))))) + (comment (test/run-tests) From 5f08efbfa84e2e410cc076f70195eff8ad1591cb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 4 Nov 2021 11:19:59 -0400 Subject: [PATCH 3751/4033] 1.10.891 --- README.md | 6 +++--- changes.md | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4a8647821..65072be1b 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.879 +Latest stable release: 1.10.891 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.879"] +[org.clojure/clojurescript "1.10.891"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.879 org.clojure clojurescript - 1.10.879 + 1.10.891 ``` diff --git a/changes.md b/changes.md index 8da1bcfc9..25e7cebdd 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,15 @@ +## 1.10.891 + +### Changes +* Update Google Closure Compiler `v20210808` +* Update Google Closure Library `0.0-20211011-0726fdeb` +* CLJS-3330: Flag for legacy loading of goog.object & goog.array + +### Fixes +* CLJS-3056: runtime namespace load order is independent from ordering in ns macro :require form +* CLJS-3074: Resolve :warning-handlers compiler option when symbols +* CLJS-3317: PersistentVector invoke does not align with Clojure + ## 1.10.879 ### Changes From 6443518850d8e4f1e09be99d4669e8d4a5e893a3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 5 Nov 2021 13:54:50 -0400 Subject: [PATCH 3752/4033] update corelib.org --- devnotes/corelib.org | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/devnotes/corelib.org b/devnotes/corelib.org index 600fb91ab..f74b36913 100644 --- a/devnotes/corelib.org +++ b/devnotes/corelib.org @@ -13,13 +13,13 @@ * DONE *e * *err* * *file* -* *flush-on-newline* +* DONE *flush-on-newline* * *fn-loader* * *in* * *math-context* -* *ns* -* *out* -* *print-dup* +* DONE *ns* +* DONE *out* +* DONE *print-dup* * DONE *print-length* * DONE *print-level* * DONE *print-meta* @@ -37,7 +37,7 @@ does what? * -' * DONE -> * DONE ->> -* ->ArrayChunk +* DONE ->ArrayChunk * ->Vec * ->VecNode * ->VecSeq @@ -123,7 +123,7 @@ does what? * char-array * char-escape-string * char-name-string -* char? +* DONE char? * DONE chars * DONE chunk * DONE chunk-append @@ -209,7 +209,7 @@ For macros only, uses clojure.core version * enumeration-seq * error-handler * error-mode -* eval +* DONE eval (bootstrapped) * DONE even? * DONE every-pred * DONE every? @@ -226,7 +226,7 @@ macro currently expands into extend call * DONE filter * DONE find * TODO find-keyword -* find-ns +* DONE find-ns (not advanced compliation compatible) * find-protocol-impl * find-protocol-method * find-var @@ -234,9 +234,9 @@ macro currently expands into extend call * DONE flatten * DONE float * float-array -* float? +* DONE float? * DONE floats -* flush +* DONE flush * DONE fn * DONE fn? * DONE fnext @@ -327,7 +327,7 @@ does what? * DONE mapcat * DONE max * DONE max-key -* memfn +* DONE memfn * DONE memoize * DONE merge * DONE merge-with @@ -342,7 +342,7 @@ does what? * DONE namespace * namespace-munge * DONE neg? -* newline +* DONE newline * DONE next * DONE nfirst * DONE nil? @@ -424,9 +424,9 @@ dunno about regex * DONE re-matches * DONE re-pattern * DONE re-seq -* read +* DONE read (via tools.reader) * read-line -* read-string +* DONE read-string (via tools.reader) * DONE realized? * DONE reduce * DONE reductions @@ -453,8 +453,8 @@ dunno about regex * TODO require ticket #8 * DONE reset! -* reset-meta! -* resolve +* DONE reset-meta! +* DONE resolve (as macro) * DONE rest * restart-agent * resultset-seq @@ -575,7 +575,7 @@ as macro * with-open * DONE with-out-str * with-precision -* with-redefs +* DONE with-redefs * with-redefs-fn * TODO xml-seq * DONE zero? From 79aef5056e6e45bcc21bdd5fd2075979a5ca33ac Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 8 Nov 2021 11:11:52 -0500 Subject: [PATCH 3753/4033] Runtime tests for Windows (#109) * Windows runtime test GH action * fix resource->source-file * enable console print if js/print is not available in test runner --- .github/workflows/test.yaml | 21 +++++++++++++++++++++ src/main/clojure/cljs/externs.clj | 9 ++++++--- src/test/cljs/test_runner.cljs | 6 +++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c264439fc..a7e4fca73 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -54,6 +54,27 @@ jobs: WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-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 + steps: + - uses: actions/checkout@v2 + + - uses: DeLaGuardo/setup-clojure@3.5 + with: + cli: '1.10.1.763' + + - name: Build tests + run: clojure -M:runtime.test.build + shell: powershell + + - name: Run tests + run: | + node builds/out-adv/core-advanced-test.js | tee test-out.txt + findstr "0 failures, 0 errors." test-out.txt + shell: powershell + # Self-host Tests self-host-test: name: Self-host Tests diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index b7b42fa36..e940461e7 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -16,7 +16,8 @@ [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino Node Token JSTypeExpression JSDocInfo$Visibility] - [java.util.logging Level])) + [java.util.logging Level] + [java.net URL])) (def ^:dynamic *ignore-var* false) (def ^:dynamic *source-file* nil) @@ -288,9 +289,9 @@ {} externs)))) (defn resource->source-file - [resource] + [^URL resource] (-> (SourceFile/builder) - (.withPath (.toPath (io/file (.getPath resource)))) + (.withPath (.getPath resource)) (.withContent (io/input-stream resource)) (.build))) @@ -313,6 +314,8 @@ '[clojure.pprint :refer [pprint]] '[cljs.js-deps :as js-deps]) + (resource->source-file (io/resource "goog/dom/dom.js")) + (pprint (get-in (analyze-goog-file "goog/dom/dom.js") [:defs 'setTextContent])) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 7a6023c85..0c2900009 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -56,7 +56,11 @@ [cljs.extend-to-native-test])) (set! *print-newline* false) -(set-print-fn! js/print) + +;; When testing Windows we default to Node.js +(if (exists? js/print) + (set-print-fn! js/print) + (enable-console-print!)) (run-tests 'cljs.apply-test From 5a8dd274c7ad4e5974c05fe4058a2de181db4f2b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 11 Nov 2021 08:39:20 -0500 Subject: [PATCH 3754/4033] add missing CLJS-3324 (hash-map behavior fix) to Changes.md --- changes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changes.md b/changes.md index 25e7cebdd..a3aefffb0 100644 --- a/changes.md +++ b/changes.md @@ -6,6 +6,7 @@ * CLJS-3330: Flag for legacy loading of goog.object & goog.array ### Fixes +* CLJS-3324: hash-map behavior differs from Clojure * CLJS-3056: runtime namespace load order is independent from ordering in ns macro :require form * CLJS-3074: Resolve :warning-handlers compiler option when symbols * CLJS-3317: PersistentVector invoke does not align with Clojure From 02b450af9c5f3a1fa2ee949c28c3a671987d7e54 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 29 Nov 2021 12:10:36 -0500 Subject: [PATCH 3755/4033] CLJS-3337: REPL, Regression for :reload REPL was using goog.object. Instead use low-level JS macros. --- src/main/clojure/cljs/repl/bootstrap.clj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/repl/bootstrap.clj b/src/main/clojure/cljs/repl/bootstrap.clj index be0ffefd9..a4e86270b 100644 --- a/src/main/clojure/cljs/repl/bootstrap.clj +++ b/src/main/clojure/cljs/repl/bootstrap.clj @@ -35,14 +35,14 @@ ;; check for new-ish private goog/debugLoader (if (some? goog/debugLoader_) (let [path (.getPathFromDeps_ goog/debugLoader_ src)] - (goog.object/remove (.-written_ goog/debugLoader_) path) - (goog.object/remove (.-written_ goog/debugLoader_) + (cljs.core/js-delete (.-written_ goog/debugLoader_) path) + (cljs.core/js-delete (.-written_ goog/debugLoader_) (str js/goog.basePath path))) ;; legacy approach - (let [path (goog.object/get js/goog.dependencies_.nameToPath src)] - (goog.object/remove js/goog.dependencies_.visited path) - (goog.object/remove js/goog.dependencies_.written path) - (goog.object/remove js/goog.dependencies_.written + (let [path (cljs.core/unchecked-get js/goog.dependencies_.nameToPath src)] + (cljs.core/js-delete js/goog.dependencies_.visited path) + (cljs.core/js-delete js/goog.dependencies_.written path) + (cljs.core/js-delete js/goog.dependencies_.written (str js/goog.basePath path))))) (let [ret (.require__ js/goog src)] (when (= reload "reload-all") From d47755e4559fb62c13a0f1204b4b759fc7044e11 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 1 Dec 2021 10:06:56 -0500 Subject: [PATCH 3756/4033] CLJS-3336: REGRESSION: Cannot require `goog` Google Closure Library version bump --- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 77f35e8bd..8f1438ba7 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20210811-6da97fe1 + 0.0-20211011-0726fdeb org.clojure diff --git a/project.clj b/project.clj index 27d0ddd13..8fd0f5d72 100644 --- a/project.clj +++ b/project.clj @@ -15,7 +15,7 @@ [org.clojure/tools.reader "1.3.3"] [org.clojure/test.check "0.10.0-alpha3" :scope "test"] [com.cognitect/transit-clj "0.8.309"] - [org.clojure/google-closure-library "0.0-20210811-6da97fe1"] + [org.clojure/google-closure-library "0.0-20211011-0726fdeb"] [com.google.javascript/closure-compiler-unshaded "v20210808"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main cljs.main} diff --git a/script/bootstrap b/script/bootstrap index 45e4838e0..13a71d814 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -8,7 +8,7 @@ CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20210808" DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" -GCLOSURE_LIB_RELEASE="0.0-20210811-6da97fe1" +GCLOSURE_LIB_RELEASE="0.0-20211011-0726fdeb" TREADER_RELEASE="1.3.3" TEST_CHECK_RELEASE="0.10.0-alpha3" From a416e7f0debbae62a665db915d89c61e49d9f752 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Dec 2021 09:38:37 -0500 Subject: [PATCH 3757/4033] Update README & CHANGES for 1.10.896 --- README.md | 4 ++-- changes.md | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 65072be1b..4f6eef7fb 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Latest stable release: 1.10.891 [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.891"] +[org.clojure/clojurescript "1.10.896"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.891 org.clojure clojurescript - 1.10.891 + 1.10.896 ``` diff --git a/changes.md b/changes.md index a3aefffb0..b2ad8e043 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,10 @@ +## 1.10.896 + +### Fixes +* CLJS-3336: REGRESSION: Cannot require `goog` +* CLJS-3337: REPL, Regression for :reload +* Fix Windows path issue in cljs.externs + ## 1.10.891 ### Changes From 3433d5f69ffd292798cf33771c93a467fad4f3c5 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 13 Nov 2021 19:05:58 -0500 Subject: [PATCH 3758/4033] CLJS-3335: test-and-or-code-gen-pass fails on Windows --- src/test/clojure/cljs/analyzer_pass_tests.clj | 3 ++- src/test/clojure/cljs/test_util.clj | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/analyzer_pass_tests.clj b/src/test/clojure/cljs/analyzer_pass_tests.clj index 984492564..c87ec7bce 100644 --- a/src/test/clojure/cljs/analyzer_pass_tests.clj +++ b/src/test/clojure/cljs/analyzer_pass_tests.clj @@ -14,6 +14,7 @@ [cljs.compiler :as comp] [cljs.compiler-tests :as comp-tests :refer [compile-form-seq emit]] [cljs.env :as env] + [cljs.test-util :refer [equiv-modulo-newlines]] [clojure.string :as string] [clojure.test :as test :refer [deftest is testing]])) @@ -77,7 +78,7 @@ `(let [~local true] (and true (or ~local false)))) code (with-out-str (emit ast))] - (is (= code + (is (equiv-modulo-newlines code (string/replace "(function (){var $SYM = true;\nreturn ((true) && ((($SYM) || (false))));\n})()" "$SYM" (str local))))))) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index 97dd62ee7..0a71f86ce 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -72,6 +72,12 @@ (with-out-str (run! println lines))) +(defn equiv-modulo-newlines + "Returns whether strings are equivalent, disregarding differences in + embedded system-dependent newlines." + [s & more] + (== 1 (count (group-by string/split-lines (list* s more))))) + (defmethod clojure.test/assert-expr 'thrown-with-cause-msg? [msg form] ;; (is (thrown-with-cause-msg? c re expr)) ;; Asserts that evaluating expr throws an exception of class c. From b153cac5bd76e8b34fa4ef037d6624f707cbf837 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 12 Dec 2021 19:41:02 -0500 Subject: [PATCH 3759/4033] CLJS-3440: Compiler tests fail test-cljs-3235 regarding react-select --- src/test/clojure/cljs/build_api_tests.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 21fbadbef..b5874f83a 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -744,9 +744,9 @@ :optimizations :none :target :nodejs :install-deps true - :npm-deps {:react "15.6.1" - :react-dom "15.6.1" - :react-select "3.1.0"} + :npm-deps {:react "16.13.0" + :react-dom "16.13.0" + :react-select "5.2.1"} :foreign-libs [{:file (.getPath (io/file "src" "test" "cljs_build" "cljs_3235" "foreign.js")) :provides ["some-foreign"] :global-exports '{some-foreign globalLib}}] From 70a5cdfe8c2685d77675703317156d819bc443ef Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 7 Dec 2021 23:26:17 -0500 Subject: [PATCH 3760/4033] CLJS-3339: cljs.core/hash should type hint call to js/isFinite --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index aca00175d..f0f219078 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1008,7 +1008,7 @@ (bit-xor (-hash o) 0) (number? o) - (if (js/isFinite o) + (if ^boolean (js/isFinite o) (js-mod (Math/floor o) 2147483647) (case o ##Inf From 3cfc0b483e596d4c44c458b0f4908c2dd54a30b1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 13 Nov 2021 16:55:53 -0500 Subject: [PATCH 3761/4033] CLJS-3333: defonce expansion non-hygienic with respect to core names --- src/main/clojure/cljs/core.cljc | 7 +++++-- src/test/cljs/cljs/core_test.cljs | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 63ad476e9..0d89f3699 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -618,8 +618,11 @@ "defs name to have the root value of init iff the named var has no root value, else init is unevaluated" [x init] - `(when-not (exists? ~x) - (def ~x ~init))) + (core/let [qualified (if (namespace x) + x + (symbol (core/str (core/-> &env :ns :name)) (name x)))] + `(when-not (exists? ~qualified) + (def ~x ~init)))) (core/defn destructure [bindings] (core/let [bents (partition 2 bindings) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 82e075744..b36d70a8a 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1874,3 +1874,7 @@ (for [e s :when (and (sequential? e) (every? (fn [x] x) e))] e)) [[]])))) + +(deftest test-cljs-3333 + (defonce not-native 17) ;; Intentionally matching a core name + (is (== 17 not-native))) From 4a37302236ff855a5c057a988f15529e6c8b9d7b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 13 Dec 2021 20:55:15 -0500 Subject: [PATCH 3762/4033] CLJS-3342: Run compiler unit tests on Windows --- .github/workflows/test.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a7e4fca73..28e698d52 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -187,6 +187,21 @@ jobs: - name: Run tests run: clojure -M:compiler.test:compiler.test.run + # Compiler Windows Tests + compiler-windows-test: + name: Compiler Windows Tests + runs-on: windows-2019 + steps: + - uses: actions/checkout@v2 + + - uses: DeLaGuardo/setup-clojure@3.5 + with: + cli: '1.10.1.763' + + - name: Run tests + run: clojure -M:compiler.test:compiler.test.run + shell: powershell + # CLI Tests cli-test: name: CLI Tests From 88ea212319f744365384b373502c17df8421a844 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Dec 2021 12:43:42 -0500 Subject: [PATCH 3763/4033] CLJS-3343: Failing js module processing test on Windows * Use platform generic newline match in test regex * Add failure case --- src/main/clojure/cljs/closure.clj | 7 ++++++- src/test/clojure/cljs/module_processing_tests.clj | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index c3f5ea251..269757276 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2889,7 +2889,12 @@ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "Ignoring JS module" url "based on the file extension")) (assoc js :source "")) - (assoc js :source (deps/-source js opts)))))) + (if-let [src (deps/-source js opts)] + (assoc js :source src) + (throw + (ex-info (str "Could not get source for JS module") + {:js-module lib + :clojure.error/phase :compilation}))))))) (map (fn [js] (if (:preprocess js) (preprocess-js js opts) diff --git a/src/test/clojure/cljs/module_processing_tests.clj b/src/test/clojure/cljs/module_processing_tests.clj index a9ab89b5e..552b82151 100644 --- a/src/test/clojure/cljs/module_processing_tests.clj +++ b/src/test/clojure/cljs/module_processing_tests.clj @@ -23,11 +23,11 @@ (defn preprocess-jsx [ijs _] (assoc ijs :source (clojure.string/replace (:source ijs) - (re-pattern (str "\\(\n" - "\\s*\n" - "\\s*\n" - "\\s*\n" - "\\s*\n" + (re-pattern (str "\\(\\R" + "\\s*\\R" + "\\s*\\R" + "\\s*\\R" + "\\s*\\R" "\\s*\\)")) (str " React.createElement(\"svg\", {width:\"200px\", height:\"200px\", className:\"center\"}, " "React.createElement(\"circle\", {cx:\"100px\", cy:\"100px\", r:\"100px\", fill:this.props.color})" From 04b21073783f8244f5d047bd94f63c51520da2e3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Dec 2021 13:29:58 -0500 Subject: [PATCH 3764/4033] README version typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f6eef7fb..82cd32dfd 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.891 +Latest stable release: 1.10.896 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) From f2cb3787ffe4c51e75ea3468e430bb85ea98c83b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 14 Dec 2021 23:23:33 -0500 Subject: [PATCH 3765/4033] CLJS-3334: exists? evaluates to true for cljs.user// --- src/main/clojure/cljs/core.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 0d89f3699..3801081de 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -973,7 +973,7 @@ (if (core/symbol? x) (core/let [x (core/cond-> (:name (cljs.analyzer/resolve-var &env x)) (= "js" (namespace x)) name) - segs (string/split (core/str (string/replace (core/str x) "/" ".")) #"\.") + segs (string/split (core/str (string/replace-first (core/str x) "/" ".")) #"\.") n (count segs) syms (map #(vary-meta (symbol "js" (string/join "." %)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index b36d70a8a..e52cf057f 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1878,3 +1878,8 @@ (deftest test-cljs-3333 (defonce not-native 17) ;; Intentionally matching a core name (is (== 17 not-native))) + +(deftest test-cljs-3334 + (is (exists? /)) + (is (exists? cljs.core//)) + (is (not (exists? cljs.core-test//)))) From 85d4bcac81d669769960a6e2a2353120899fb620 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 15 Dec 2021 15:09:52 -0500 Subject: [PATCH 3766/4033] CLJS-3341: Windows loader test failing with StackOverflow * revert incorrect changes in dep order patch * add informative error to module_graph deps-for --- src/main/clojure/cljs/compiler.cljc | 10 +++++----- src/main/clojure/cljs/module_graph.cljc | 9 +++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index cc5675a32..b3aa15a51 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1618,11 +1618,11 @@ {:ns (or ns-name 'cljs.user) :macros-ns (:macros-ns opts) :provides [ns-name] - :requires (cond-> (distinct deps) - (get-in @env/*compiler* [:options :emit-constants]) - (conj ana/constants-ns-sym) - (not= ns-name 'cljs.core) - (conj 'cljs.core)) + :requires (if (= ns-name 'cljs.core) + (vec (distinct deps)) + (cond-> (conj (vec (distinct deps)) 'cljs.core) + (get-in @env/*compiler* [:options :emit-constants]) + (conj ana/constants-ns-sym))) :file dest :out-file (.toString ^File dest) :source-file src} diff --git a/src/main/clojure/cljs/module_graph.cljc b/src/main/clojure/cljs/module_graph.cljc index 8bb64578d..026cad710 100644 --- a/src/main/clojure/cljs/module_graph.cljc +++ b/src/main/clojure/cljs/module_graph.cljc @@ -148,8 +148,13 @@ "Return all dependencies for x in a graph using deps-key." [x graph deps-key] (let [requires (get-in graph [x deps-key])] - (-> (mapcat #(deps-for % graph deps-key) requires) - (concat requires) distinct vec))) + (try + (-> (mapcat #(deps-for % graph deps-key) requires) + (concat requires) distinct vec) + (catch Throwable t + (throw + (ex-info (str "Failed to compute deps for " x) + {:lib x :requires requires} t)))))) (defn deps-for-entry "Return all dependencies for an entry using a compiler inputs index." From f49d3778b7aac343d52e881417b1a90f797a113e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Dec 2021 10:28:57 -0500 Subject: [PATCH 3767/4033] CLJS-3338: Missing test coverage * add missing tests * update module test for new `goog.module` handling --- src/test/clojure/cljs/analyzer/glib_module_test.clj | 4 ++-- src/test/clojure/cljs/test_runner.clj | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/cljs/analyzer/glib_module_test.clj b/src/test/clojure/cljs/analyzer/glib_module_test.clj index ad4d126ea..04c2781d3 100644 --- a/src/test/clojure/cljs/analyzer/glib_module_test.clj +++ b/src/test/clojure/cljs/analyzer/glib_module_test.clj @@ -49,8 +49,8 @@ '[(ns foo.core (:import [goog.module ModuleLoader]))]) aenv (assoc (ana/empty-env) :ns (ana/get-namespace cenv 'foo.core))] - (is (= '{:name foo.core.goog$module$goog$module$ModuleLoader - :ns goog.module ;; a bit odd, but doesn't matter, for emission we just :name + (is (= '{:name foo.core/goog$module$goog$module$ModuleLoader + :ns foo.core :op :var} (env/with-compiler-env cenv (ana/resolve-var aenv 'ModuleLoader))))))) diff --git a/src/test/clojure/cljs/test_runner.clj b/src/test/clojure/cljs/test_runner.clj index 5a8799cb1..12836b3d8 100644 --- a/src/test/clojure/cljs/test_runner.clj +++ b/src/test/clojure/cljs/test_runner.clj @@ -1,15 +1,19 @@ (ns cljs.test-runner (:require [cljs.analyzer-api-tests] + [cljs.analyzer-pass-tests] [cljs.analyzer-tests] [cljs.build-api-tests] [cljs.closure-tests] [cljs.compiler-tests] + [cljs.compiler.glib-module-test] [cljs.externs-infer-tests] [cljs.externs-parsing-tests] [cljs.instant-tests] + [cljs.js-deps-tests] [cljs.module-graph-tests] [cljs.module-processing-tests] [cljs.source-map.base64-tests] + [cljs.transpile-tests] [cljs.type-inference-tests] [cljs.util-tests] [clojure.test :refer [run-tests]])) @@ -18,16 +22,20 @@ (let [{:keys [fail error]} (run-tests 'cljs.analyzer-api-tests + 'cljs.analyzer-pass-tests 'cljs.analyzer-tests 'cljs.build-api-tests 'cljs.closure-tests 'cljs.compiler-tests + 'cljs.compiler.glib-module-test 'cljs.externs-infer-tests 'cljs.externs-parsing-tests 'cljs.instant-tests + 'cljs.js-deps-tests 'cljs.module-graph-tests 'cljs.module-processing-tests 'cljs.source-map.base64-tests + 'cljs.transpile-tests 'cljs.type-inference-tests 'cljs.util-tests)] (if (or (not (zero? fail)) From c5a06c825222360cb960596f794f3462d50b7a0e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 17 Dec 2021 15:20:02 -0500 Subject: [PATCH 3768/4033] CLJS-3332: Cannot require `@firebase/auth` Handle libraries that declare exports. Previously we only looked at top-level package.json. Expand this so that if top-level package.json declares exports, then we consider those package.json files as well. This leads to computing correct :provides with the exist logic which has worked suitably for some time. Add tests. Add some comments and docstrings to aid readability in the future. --- src/main/clojure/cljs/closure.clj | 70 ++++++++++++++++++----- src/main/clojure/cljs/util.cljc | 12 ++-- src/test/cljs_build/firebase/core.cljs | 3 + src/test/clojure/cljs/build_api_tests.clj | 21 +++++++ 4 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 src/test/cljs_build/firebase/core.cljs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 269757276..0eb7e362d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2766,20 +2766,58 @@ [])))) (defn- node-file-seq->libs-spec* + "Given a sequence of non-nested node_module paths where the extension ends in + `.js/.json`, return lib-spec maps for each path containing at least :file, + :module-type, and :provides." [module-fseq opts] (letfn [(package-json? [path] - (boolean (re-find #"node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$" path)))] - (let [pkg-jsons (into {} - (comp - (map #(.getAbsolutePath %)) - (filter package-json?) - (map (fn [path] - [path (json/read-str (slurp path))]))) - module-fseq) - trim-package-json (fn [s] - (if (string/ends-with? s "package.json") - (subs s 0 (- (count s) 12)) - s))] + (= "package.json" (.getName (io/file path)))) + + (top-level-package-json? [path] + (boolean (re-find #"node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$" path))) + + ;; the path sans the package.json part + ;; i.e. some_lib/package.json -> some_lib + (trim-package-json [s] + (if (string/ends-with? s "package.json") + (subs s 0 (- (count s) 12)) + s)) + + (trim-relative [path] + (cond-> path + (string/starts-with? path "./") + (subs 2))) + + (add-exports [pkg-jsons] + (reduce-kv + (fn [pkg-jsons path {:strs [exports] :as pkg-json}] + (reduce-kv + (fn [pkg-jsons export _] + ;; NOTE: ignore "." exports for now + (if (= "." export) + pkg-jsons + (let [export-pkg-json + (io/file + (.getCanonicalPath (io/file (trim-package-json path) export)) + "package.json")] + (cond-> pkg-jsons + (.exists export-pkg-json) + (assoc + (.getAbsolutePath export-pkg-json) + (json/read-str (slurp export-pkg-json))))))) + pkg-jsons exports)) + pkg-jsons pkg-jsons))] + (let [ + ;; a map of all the *top-level* package.json paths and their exports + ;; to the package.json contents as EDN + pkg-jsons (add-exports + (into {} + (comp + (map #(.getAbsolutePath %)) + (filter top-level-package-json?) + (map (fn [path] + [path (json/read-str (slurp path))]))) + module-fseq))] (into [] (comp (map #(.getAbsolutePath %)) @@ -2787,6 +2825,8 @@ (merge {:file path :module-type :es6} + ;; if the file is *not* a package.json, then compute what + ;; namespaces it :provides to ClojureScript (when-not (package-json? path) (let [pkg-json-main (some (fn [[pkg-json-path {:as pkg-json :strs [name]}]] @@ -2795,13 +2835,13 @@ (when-not (nil? entry) ;; should be the only edge case in ;; the package.json main field - Antonio - (let [entry (cond-> entry - (string/starts-with? entry "./") - (subs 2)) + (let [entry (trim-relative entry) entry-path (-> pkg-json-path (string/replace \\ \/) trim-package-json (str entry))] + ;; find a package.json entry point that matches + ;; the `path` (some (fn [candidate] (when (= candidate (string/replace path \\ \/)) name)) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 4bf6ce014..b2d588ad8 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -40,7 +40,7 @@ (if (== n Integer/MIN_VALUE) 0 (Math/abs n)))] - (str synthethetic-version-prefix + (str synthethetic-version-prefix (qualifier (reduce unchecked-add-int (map file-hash (file-seq (main-src-directory))))))))) (defn ^String clojurescript-version @@ -390,14 +390,18 @@ (str (string/join ", " (pop xs)) " and " (peek xs))))) (defn module-file-seq + "Return a seq of all files in `node_modules` ending in `.js` or `.json` that are + not in an internally nested `node_modules` dir." ([] (module-file-seq (io/file "node_modules"))) ([dir] (let [fseq (tree-seq (fn [^File f] + ;; ignore embedded node_modules, the user did not install + ;; these (and (. f (isDirectory)) - (not (boolean - (re-find #"node_modules[\\\/].*[\\\/]node_modules" - (.getPath f)))))) + (not (boolean + (re-find #"node_modules[\\\/].*[\\\/]node_modules" + (.getPath f)))))) (fn [^File d] (seq (. d (listFiles)))) dir)] diff --git a/src/test/cljs_build/firebase/core.cljs b/src/test/cljs_build/firebase/core.cljs new file mode 100644 index 000000000..ec81c0550 --- /dev/null +++ b/src/test/cljs_build/firebase/core.cljs @@ -0,0 +1,3 @@ +(ns firebase.core + (:require ["firebase/auth" :as auth])) + diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index b5874f83a..30c6ca074 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -796,3 +796,24 @@ (ana/with-warning-handlers [(collecting-warning-handler ws)] (build/build (build/inputs (io/file inputs "cljs_3311_regress/core.cljs")) opts cenv)) (is (empty? @ws))))) + +(deftest test-cljs-3332 + (testing "Test that package.json w/ exports work, Firebase as example" + (let [out (.getPath (io/file (test/tmp-dir) "npm-deps-test-out"))] + (test/delete-out-files out) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (let [{:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'firebase.core + :output-dir out + :optimizations :none + :install-deps true + :npm-deps {:firebase "9.3.0"} + :closure-warnings {:check-types :off} + :target :bundle}} + cenv (env/default-compiler-env)] + (build/build (build/inputs (io/file inputs "firebase/core.cljs")) opts cenv) + (is (= #{"firebase/auth"} (:node-module-index @cenv)))) + (.delete (io/file "package.json")) + (test/delete-node-modules) + (test/delete-out-files out)))) From a0116ce9c99a478fa3c81cecd23e1acd31e123f4 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Fri, 17 Dec 2021 15:56:17 -0500 Subject: [PATCH 3769/4033] fix windows issue with CLJS-3332 patch --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0eb7e362d..edb0d1f17 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2798,7 +2798,8 @@ pkg-jsons (let [export-pkg-json (io/file - (.getCanonicalPath (io/file (trim-package-json path) export)) + (trim-package-json path) + (trim-relative export) "package.json")] (cond-> pkg-jsons (.exists export-pkg-json) From fcf031d4522352f3d15ed6e8420c50190c4fbbcd Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 20 Dec 2021 10:05:32 -0500 Subject: [PATCH 3770/4033] Update README.md & changes.md for 1.10.914 --- README.md | 6 +++--- changes.md | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 82cd32dfd..8d56ac0dd 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.896 +Latest stable release: 1.10.914 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.896"] +[org.clojure/clojurescript "1.10.914"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.896 org.clojure clojurescript - 1.10.896 + 1.10.914 ``` diff --git a/changes.md b/changes.md index b2ad8e043..a051ba11e 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,19 @@ +## 1.10.914 + +### Fixes +* CLJS-3339: cljs.core/hash should type hint call to js/isFinite +* CLJS-3333: defonce expansion non-hygienic with respect to core names +* CLJS-3334: exists? evaluates to true for cljs.user// +* CLJS-3341: Revert dependency issue in dep order patch + +### Changes +* CLJS-3332: Cannot require `@firebase/auth` +* CLJS-3335: CI, test-and-or-code-gen-pass fails on Windows +* CLJS-3440: CI, Compiler tests fail test-cljs-3235 regarding react-select +* CLJS-3342: Run CI compiler unit tests on Windows +* CLJS-3338: Missing CI test coverage +* CLJS-3343: Failing js module processing test on Windows + ## 1.10.896 ### Fixes From 754b2a30d235de38495a2204a1cb32a0222a98fa Mon Sep 17 00:00:00 2001 From: davidnolen Date: Fri, 24 Dec 2021 18:01:35 -0500 Subject: [PATCH 3771/4033] 1.11.X --- script/build | 2 +- script/revision | 2 +- script/uberjar | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/script/build b/script/build index eee801c50..20bb36d06 100755 --- a/script/build +++ b/script/build @@ -25,7 +25,7 @@ CLJS_SCRIPT_MVN_OPTS=${CLJS_SCRIPT_MVN_OPTS:-""} # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="10" +MINOR="11" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. diff --git a/script/revision b/script/revision index 4b5e21159..d8b78483a 100755 --- a/script/revision +++ b/script/revision @@ -10,7 +10,7 @@ set -ex # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="9" +MINOR="11" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. Do this in two steps so diff --git a/script/uberjar b/script/uberjar index 83d685e81..8b88196f0 100755 --- a/script/uberjar +++ b/script/uberjar @@ -19,7 +19,7 @@ rm -f resources/brepl_client.js # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="10" +MINOR="11" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. From 1d32ebb06b77235d58e990ea5a087536c1cd537c Mon Sep 17 00:00:00 2001 From: davidnolen Date: Fri, 24 Dec 2021 18:11:02 -0500 Subject: [PATCH 3772/4033] one commit --- deps.edn | 1 + 1 file changed, 1 insertion(+) diff --git a/deps.edn b/deps.edn index 82f1a4324..47a4ea8b9 100644 --- a/deps.edn +++ b/deps.edn @@ -28,3 +28,4 @@ :uberjar {:extra-deps {com.github.seancorfield/depstar {:mvn/version "2.0.193"}} :exec-fn hf.depstar/uberjar :exec-args {:aot true}}}} + From 8edcebe1343318b6fb683f37dfd8f523900377ab Mon Sep 17 00:00:00 2001 From: davidnolen Date: Mon, 27 Dec 2021 14:43:10 -0500 Subject: [PATCH 3773/4033] CLJS-3345: package.json exports can be a dupe of main --- src/main/clojure/cljs/closure.clj | 36 +++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index edb0d1f17..34716156c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2791,22 +2791,26 @@ (add-exports [pkg-jsons] (reduce-kv (fn [pkg-jsons path {:strs [exports] :as pkg-json}] - (reduce-kv - (fn [pkg-jsons export _] - ;; NOTE: ignore "." exports for now - (if (= "." export) - pkg-jsons - (let [export-pkg-json - (io/file - (trim-package-json path) - (trim-relative export) - "package.json")] - (cond-> pkg-jsons - (.exists export-pkg-json) - (assoc - (.getAbsolutePath export-pkg-json) - (json/read-str (slurp export-pkg-json))))))) - pkg-jsons exports)) + ;; "exports" can just be a dupe of "main", i.e. a string - ignore + ;; https://nodejs.org/api/packages.html#main-entry-point-export + (if (string? exports) + pkg-jsons + (reduce-kv + (fn [pkg-jsons export _] + ;; NOTE: ignore "." exports for now + (if (= "." export) + pkg-jsons + (let [export-pkg-json + (io/file + (trim-package-json path) + (trim-relative export) + "package.json")] + (cond-> pkg-jsons + (.exists export-pkg-json) + (assoc + (.getAbsolutePath export-pkg-json) + (json/read-str (slurp export-pkg-json))))))) + pkg-jsons exports))) pkg-jsons pkg-jsons))] (let [ ;; a map of all the *top-level* package.json paths and their exports From 8a7b907e195347091cc288e4ca63147c834f3a45 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 28 Dec 2021 08:23:10 -0500 Subject: [PATCH 3774/4033] 1.11.4 --- README.md | 6 +++--- changes.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d56ac0dd..36c2e604b 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.10.914 +Latest stable release: 1.11.4 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.10.914"] +[org.clojure/clojurescript "1.11.4"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.10.914 org.clojure clojurescript - 1.10.914 + 1.11.4 ``` diff --git a/changes.md b/changes.md index a051ba11e..3d46a8658 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,8 @@ +## 1.11.4 + +### Fixes +* CLJS-3345: package.json exports can be a dupe of main + ## 1.10.914 ### Fixes From 9835a1bbaacee590a6def82eca418cd906030917 Mon Sep 17 00:00:00 2001 From: Timothy Pratley Date: Wed, 13 Oct 2021 15:02:03 -0700 Subject: [PATCH 3775/4033] add update-vals and update-keys To keep up to date with Clojure: https://clojure.org/releases/devchangelog#v1.11.0-alpha2 CLJ-1959 Add implementation of update-keys CLJ-2651 Add implementation of update-vals --- src/main/cljs/cljs/core.cljs | 29 +++++++++++++++++++++++++++++ src/test/cljs/cljs/core_test.cljs | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f0f219078..cab20a2cc 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11724,6 +11724,35 @@ reduces them without incurring seq initialization" (tap x) (catch js/Error ex)))))) +(defn update-vals + "m f => {k (f v) ...} + Given a map m and a function f of 1-argument, returns a new map where the keys of m + are mapped to result of applying f to the corresponding values of m." + {:added "1.11"} + [m f] + (with-meta + (persistent! + (reduce-kv (fn [acc k v] (assoc! acc k (f v))) + (if (instance? IEditableCollection m) + (transient m) + (transient {})) + m)) + (meta m))) + +(defn update-keys + "m f => {(f k) v ...} + Given a map m and a function f of 1-argument, returns a new map whose + keys are the result of applying f to the keys of m, mapped to the + corresponding values of m. + f must return a unique key for each key of m, else the behavior is undefined." + {:added "1.11"} + [m f] + (let [ret (persistent! + (reduce-kv (fn [acc k v] (assoc! acc (f k) v)) + (transient {}) + m))] + (with-meta ret (meta m)))) + ;; ----------------------------------------------------------------------------- ;; Bootstrap helpers - incompatible with advanced compilation diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index e52cf057f..472984d8b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1883,3 +1883,21 @@ (is (exists? /)) (is (exists? cljs.core//)) (is (not (exists? cljs.core-test//)))) + +(deftest test-update-vals + (let [inm (with-meta {:a 1 :b 2} {:has :meta})] + (are [result expr] (= result expr) + {:a 2 :b 3} (update-vals inm inc) + {:has :meta} (meta (update-vals inm inc)) + {0 2 2 4} (update-vals (hash-map 0 1 2 3) inc) + {0 2 2 4} (update-vals (array-map 0 1 2 3) inc) + {0 2 2 4} (update-vals (sorted-map 2 3 0 1) inc)))) + +(deftest test-update-keys + (let [inm (with-meta {:a 1 :b 2} {:has :meta})] + (are [result expr] (= result expr) + {"a" 1 "b" 2} (update-keys inm name) + {:has :meta} (meta (update-keys inm name)) + {1 1 3 3} (update-keys (hash-map 0 1 2 3) inc) + {1 1 3 3} (update-keys (array-map 0 1 2 3) inc) + {1 1 3 3} (update-keys (sorted-map 2 3 0 1) inc)))) From 3f199d930a87277eb7c307232ade36a5dc76fbed Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 5 Jan 2022 12:43:41 -0500 Subject: [PATCH 3776/4033] CLJS-3331: instance? -> implements? --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index cab20a2cc..85169c362 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11733,7 +11733,7 @@ reduces them without incurring seq initialization" (with-meta (persistent! (reduce-kv (fn [acc k v] (assoc! acc k (f v))) - (if (instance? IEditableCollection m) + (if (implements? IEditableCollection m) (transient m) (transient {})) m)) From 851000f41c664f4012b04eeca6531768ee7b699a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 25 Jan 2022 08:14:46 -0500 Subject: [PATCH 3777/4033] CLJS-3346: :as-alias (#119) Implement `:as-alias` support as simple structural pass on the ns form EDN. All `:as-alias` are collected, and the ns libspecs are returned with `:as-alias` elided. If a lib spec has *only* `:as-alias` it is *removed* entirely. This is possible since `:as-alias` does not load. The structural pass approach means further alterations to the analyzer or compiler are not needed. This aligns w/ the purpose of `:as-alias`, as it is primarily about feeding the reader. One interesting thing to note is that ClojureScript has two difference namespaces - one for defs and one for macros and they may in fact collide. Since `:as-alias` is only about the reader - collisions are not allowed across defs and macros. That is, if a `:require-macros` libspec declares an `:as-alias` it cannot be reused by a `:require` libspec. * change analyzer `ns` and `ns*` to support `:as-alias` * change ns merging to deep merge `:as-aliases` * change `form-seq` reader helper to include `:as-aliases` from the current namespace * change REPL reader to include `:as-aliases` from the current namespace --- src/main/clojure/cljs/analyzer.cljc | 20 +++-- .../cljs/analyzer/impl/namespaces.cljc | 68 ++++++++++++++ src/main/clojure/cljs/repl.cljc | 2 +- .../cljs_build/cljs_3346_as_alias/core.cljs | 5 ++ .../clojure/cljs/analyzer/as_alias_test.clj | 90 +++++++++++++++++++ src/test/clojure/cljs/build_api_tests.clj | 24 +++++ src/test/clojure/cljs/test_runner.clj | 2 + 7 files changed, 204 insertions(+), 7 deletions(-) create mode 100644 src/main/clojure/cljs/analyzer/impl/namespaces.cljc create mode 100644 src/test/cljs_build/cljs_3346_as_alias/core.cljs create mode 100644 src/test/clojure/cljs/analyzer/as_alias_test.clj diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 9cd94fe8f..c5fd8342a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -14,6 +14,7 @@ no-warn with-warning-handlers wrapping-errors]] [cljs.env.macros :refer [ensure]])) #?(:clj (:require [cljs.analyzer.impl :as impl] + [cljs.analyzer.impl.namespaces :as nses] [cljs.analyzer.passes.and-or :as and-or] [cljs.env :as env :refer [ensure]] [cljs.externs :as externs] @@ -27,6 +28,7 @@ [clojure.tools.reader :as reader] [clojure.tools.reader.reader-types :as readers]) :cljs (:require [cljs.analyzer.impl :as impl] + [cljs.analyzer.impl.namespaces :as nses] [cljs.analyzer.passes.and-or :as and-or] [cljs.env :as env] [cljs.reader :as edn] @@ -3101,7 +3103,7 @@ (if (pos? (count old)) (let [deep-merge-keys [:use-macros :require-macros :rename-macros - :uses :requires :renames :imports]] + :uses :requires :renames :imports :as-aliases]] #?(:clj (when *check-alias-dupes* (check-duplicate-aliases env old new))) @@ -3141,13 +3143,15 @@ #?(:clj (rewrite-cljs-aliases (if metadata (next args) args)) :cljs (if (some? metadata) (next args) args))) + {:keys [as-aliases] args :libspecs} (nses/elide-aliases-from-ns-specs args) name (vary-meta name merge metadata) {excludes :excludes core-renames :renames} (parse-ns-excludes env args) core-renames (reduce (fn [m [original renamed]] (assoc m renamed (symbol "cljs.core" (str original)))) {} core-renames) deps (atom []) - aliases (atom {:fns {} :macros {}}) + ;; as-aliases can only be used *once* because they are about the reader + aliases (atom {:fns as-aliases :macros as-aliases}) spec-parsers {:require (partial parse-require-spec env false deps aliases) :require-macros (partial parse-require-spec env true deps aliases) :use (comp (partial parse-require-spec env false deps aliases) @@ -3196,7 +3200,8 @@ spec-map)) [require-macros use-macros])])] (set! *cljs-ns* name) (let [ns-info - {:name name + {:as-aliases as-aliases + :name name :doc (or docstring mdocstr) :excludes excludes :use-macros use-macros @@ -3239,12 +3244,14 @@ #?(:clj (list (process-rewrite-form specs)) :cljs (list specs))) + {:keys [as-aliases] args :libspecs} (nses/elide-aliases-from-ns-specs args) {excludes :excludes core-renames :renames} (parse-ns-excludes env args) core-renames (reduce (fn [m [original renamed]] (assoc m renamed (symbol "cljs.core" (str original)))) {} core-renames) deps (atom []) - aliases (atom {:fns {} :macros {}}) + ;; as-aliases can only be used *once* because they are about the reader + aliases (atom {:fns as-aliases :macros as-aliases}) spec-parsers {:require (partial parse-require-spec env false deps aliases) :require-macros (partial parse-require-spec env true deps aliases) :use (comp (partial parse-require-spec env false deps aliases) @@ -3275,7 +3282,8 @@ {} (remove (fn [[r]] (= r :refer-clojure)) args))] (set! *cljs-ns* name) (let [require-info - {:name name + {:as-aliases as-aliases + :name name :excludes excludes :use-macros use-macros :require-macros require-macros @@ -4324,7 +4332,7 @@ reader/*data-readers* data-readers reader/*alias-map* (apply merge - ((juxt :requires :require-macros) + ((juxt :requires :require-macros :as-aliases) (get-namespace *cljs-ns*))) reader/resolve-symbol resolve-symbol] (reader/read opts pbr))] diff --git a/src/main/clojure/cljs/analyzer/impl/namespaces.cljc b/src/main/clojure/cljs/analyzer/impl/namespaces.cljc new file mode 100644 index 000000000..a97c6646a --- /dev/null +++ b/src/main/clojure/cljs/analyzer/impl/namespaces.cljc @@ -0,0 +1,68 @@ +;; 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.impl.namespaces) + +(defn check-and-remove-as-alias + "Given a libspec return a map of :as-alias alias, if was present. Return the + libspec with :as-alias elided. If the libspec was *only* :as-alias do not + return it." + [libspec] + ;; ignore simple requires (symbols) and + ;; REPL stuff (keywords, i.e. :reload) + (if (or (symbol? libspec) + (keyword? libspec)) + {:libspec libspec} + (let [[lib & spec :as libspec] libspec + [pre-spec [_ alias & post-spec :as post]] (split-with (complement #{:as-alias}) spec)] + (if (seq post) + (let [libspec' (into [lib] (concat pre-spec post-spec))] + (assert (symbol? alias) + (str ":as-alias must be followed by a symbol, got: " alias)) + (cond-> {:as-alias {alias lib}} + (> (count libspec') 1) (assoc :libspec libspec'))) + {:libspec libspec})))) + +(defn check-as-alias-duplicates + [as-aliases new-as-aliases] + (doseq [[alias _] new-as-aliases] + (assert (not (contains? as-aliases alias)) + (str "Duplicate :as-alias " alias ", already in use for lib " + (get as-aliases alias))))) + +(defn elide-aliases-from-libspecs + "Given libspecs, elide all :as-alias. Return a map of :libspecs (filtered) + and :as-aliases." + ([libspecs] + (elide-aliases-from-libspecs libspecs {})) + ([libspecs as-aliases] + (let [ret {:as-aliases as-aliases + :libspecs []}] + (reduce + (fn [ret libspec] + (let [{:keys [as-alias libspec]} (check-and-remove-as-alias libspec)] + (check-as-alias-duplicates (:as-aliases ret) as-alias) + (cond-> ret + libspec (update :libspecs conj libspec) + as-alias (update :as-aliases merge as-alias)))) + ret libspecs)))) + +(defn elide-aliases-from-ns-specs [ns-specs] + "Given ns specs, elide all :as-alias. Return a map of :libspecs (filtered) + and :as-aliases." + (let [ret {:as-aliases {} + :libspecs []}] + (reduce + (fn [{:keys [as-aliases] :as ret} [spec-key & libspecs]] + (if-not (= :refer-clojure spec-key) + (let [{:keys [as-aliases libspecs]} (elide-aliases-from-libspecs libspecs as-aliases)] + (cond-> ret + (not (empty? as-aliases)) (update :as-aliases merge as-aliases) + (not (empty? libspecs)) (update :libspecs conj (list* spec-key libspecs)))) + (update ret :libspecs conj (list* spec-key libspecs)))) + ret ns-specs))) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index f3c2ce8e1..d10e05807 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1146,7 +1146,7 @@ reader/*data-readers* tags/*cljs-data-readers* reader/*alias-map* (apply merge - ((juxt :requires :require-macros) + ((juxt :requires :require-macros :as-aliases) (ana/get-namespace ana/*cljs-ns*)))] (try (read request-prompt request-exit) diff --git a/src/test/cljs_build/cljs_3346_as_alias/core.cljs b/src/test/cljs_build/cljs_3346_as_alias/core.cljs new file mode 100644 index 000000000..cb09f13db --- /dev/null +++ b/src/test/cljs_build/cljs_3346_as_alias/core.cljs @@ -0,0 +1,5 @@ +(ns cljs-3346-as-alias.core + (:require [clojure.set :as-alias set] + [made.up.lib :as-alias lib])) + +(println ::set/foo ::lib/bar) diff --git a/src/test/clojure/cljs/analyzer/as_alias_test.clj b/src/test/clojure/cljs/analyzer/as_alias_test.clj new file mode 100644 index 000000000..6ef0458bf --- /dev/null +++ b/src/test/clojure/cljs/analyzer/as_alias_test.clj @@ -0,0 +1,90 @@ +;; 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.as-alias-test + (:require [cljs.analyzer.impl.namespaces :as ana-nses] + [cljs.env :as env] + [clojure.test :as test :refer [deftest testing is]])) + +;; ============================================================================= + +(deftest test-check-and-remove-as-alias + (let [cenv (env/default-compiler-env)] + (env/with-compiler-env cenv + (testing "check-and-remove-as-alias basic tests" + (is (= '{:as-alias {bar bar.core}} + (ana-nses/check-and-remove-as-alias '[bar.core :as-alias bar]))) + (is (= '{:as-alias {bar bar.core} + :libspec [bar.core :as boo]} + (ana-nses/check-and-remove-as-alias '[bar.core :as-alias bar :as boo]))) + (is (thrown? Throwable + (ana-nses/check-and-remove-as-alias '[bar.core :as-alias :bar])))) + (testing "check-and-remove-as-alias should not elide simple specs" + (is (= '{:libspec bar.core} + (ana-nses/check-and-remove-as-alias 'bar.core))) + (is (= '{:libspec [bar.core]} + (ana-nses/check-and-remove-as-alias '[bar.core]))))))) + +(deftest test-elide-aliases-from-libspecs + (let [cenv (env/default-compiler-env)] + (env/with-compiler-env cenv + (is (= '{:as-aliases {foo foo.core + bar bar.core + woz woz.core} + :libspecs [[woz.core :as wozc]]} + (ana-nses/elide-aliases-from-libspecs + '([foo.core :as-alias foo] + [bar.core :as-alias bar] + [woz.core :as-alias woz :as wozc])))) + (is (thrown? Throwable + (ana-nses/elide-aliases-from-libspecs + '([foo.core :as-alias foo] + [bar.core :as-alias bar] + [woz.core :as-alias woz :as wozc] + [foo.impl :as-alias foo]))))))) + +(deftest test-elide-aliases-from-ns-specs + (let [cenv (env/default-compiler-env)] + (env/with-compiler-env cenv + (is (= '{:as-aliases {blah blah.core, foo foo.core, bar bar.core}, + :libspecs [(:require-macros [[lala.core :as-lias lala :as tralala]]) + (:require [[woz.core :as woz]])]}) + (ana-nses/elide-aliases-from-ns-specs + '((:require-macros [blah.core :as-alias blah] + [lala.core :as-alias lala :as tralala]) + (:require + [foo.core :as-alias foo] + [bar.core :as-alias bar] + [woz.core :as woz])))) + (testing "Proper handling of ns-spec edgecases" + (is (= '{:as-aliases {} :libspecs [(:require foo.core bar.core woz.core)]} + (ana-nses/elide-aliases-from-ns-specs + '((:require foo.core bar.core woz.core))))) + (is (= '{:as-aliases {} :libspecs [(:require [foo.core] [bar.core] [woz.core])]} + (ana-nses/elide-aliases-from-ns-specs + '((:require [foo.core] [bar.core] [woz.core])))))) + (testing ":refer-clojure is ignored" + (is (= '{:as-aliases {} + :libspecs [(:refer-clojure :exclude [first]) + (:require foo.core bar.core woz.core)]} + (ana-nses/elide-aliases-from-ns-specs + '((:refer-clojure :exclude [first]) + (:require foo.core bar.core woz.core)))))) + (testing ":reload/:reload-all is ignored" + (is (= '{:as-aliases {}, + :libspecs [(:refer-clojure :exclude [first]) + (:require foo.core bar.core woz.core :reload-all)]} + (ana-nses/elide-aliases-from-ns-specs + '((:refer-clojure :exclude [first]) + (:require foo.core bar.core woz.core :reload-all))))))))) + +(comment + + (test/run-tests) + + ) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 30c6ca074..f28694b39 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -817,3 +817,27 @@ (.delete (io/file "package.json")) (test/delete-node-modules) (test/delete-out-files out)))) + +(deftest test-cljs-3346-as-alias + (testing "Test that using :as-alias does not load the namespace, and that + a namespace that does not exist on file can be used." + (let [out (.getPath (io/file #_(test/tmp-dir) "cljs-3346-as-alias-out"))] + (test/delete-out-files out) + (test/delete-node-modules) + (spit (io/file "package.json") "{}") + (let [{:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) + :opts {:main 'cljs-3346-as-alias.core + :output-dir out + :optimizations :none + :closure-warnings {:check-types :off}}} + cenv (env/default-compiler-env)] + (build/build (build/inputs (io/file inputs "cljs_3346_as_alias/core.cljs")) opts cenv)) + (let [source (slurp (io/file out "cljs_3346_as_alias/core.js"))] + (is (true? (boolean (re-find #"goog.require\('cljs.core'\)" source)))) + (is (false? (boolean (re-find #"goog.require\('clojure.set'\)" source)))) + (is (false? (boolean (re-find #"goog.require\('made.up.lib'\)" source)))) + (is (true? (boolean (re-find #"clojure\.set\/foo" source)))) + (is (true? (boolean (re-find #"made\.up\.lib\/bar" source))))) + (.delete (io/file "package.json")) + (test/delete-node-modules) + (test/delete-out-files out)))) diff --git a/src/test/clojure/cljs/test_runner.clj b/src/test/clojure/cljs/test_runner.clj index 12836b3d8..0b0c9f2ca 100644 --- a/src/test/clojure/cljs/test_runner.clj +++ b/src/test/clojure/cljs/test_runner.clj @@ -1,5 +1,6 @@ (ns cljs.test-runner (:require [cljs.analyzer-api-tests] + [cljs.analyzer.as-alias-test] [cljs.analyzer-pass-tests] [cljs.analyzer-tests] [cljs.build-api-tests] @@ -22,6 +23,7 @@ (let [{:keys [fail error]} (run-tests 'cljs.analyzer-api-tests + 'cljs.analyzer.as-alias-test 'cljs.analyzer-pass-tests 'cljs.analyzer-tests 'cljs.build-api-tests From b3670a6d4a32dce751958ce393a16cf2e6e9c805 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 25 Jan 2022 08:18:54 -0500 Subject: [PATCH 3778/4033] remove stray #_ --- src/test/clojure/cljs/build_api_tests.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index f28694b39..4882c9bd6 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -821,7 +821,7 @@ (deftest test-cljs-3346-as-alias (testing "Test that using :as-alias does not load the namespace, and that a namespace that does not exist on file can be used." - (let [out (.getPath (io/file #_(test/tmp-dir) "cljs-3346-as-alias-out"))] + (let [out (.getPath (io/file (test/tmp-dir) "cljs-3346-as-alias-out"))] (test/delete-out-files out) (test/delete-node-modules) (spit (io/file "package.json") "{}") From 5379f722588370f9f1934e9a78e777e24e953c81 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 25 Jan 2022 13:06:51 -0500 Subject: [PATCH 3779/4033] CLJS-3294: data_readers.cljc doesn't provide a way to have target-specific behaviour (#120) - The function that reads the data_readers.cljc files from the classpath has been moved to the analyzer ns, so that custom literals are available to the ana/forms-seq fns also. - in cljs.reader/add-data-readers macro, the set of custom data-readers returned is the fully qualified var names of those readers - as those will be interpreted as cljs functions. Co-authored-by: Henry Widd --- src/main/cljs/cljs/reader.clj | 2 +- src/main/clojure/cljs/analyzer.cljc | 66 ++++++++++++++++++++++- src/main/clojure/cljs/closure.clj | 47 +--------------- src/main/clojure/cljs/core/server.clj | 3 +- src/main/clojure/cljs/repl.cljc | 6 ++- src/test/cljs/data_readers.cljc | 3 +- src/test/cljs/data_readers_test/core.cljc | 12 +++++ src/test/clojure/cljs/build_api_tests.clj | 6 ++- 8 files changed, 92 insertions(+), 53 deletions(-) diff --git a/src/main/cljs/cljs/reader.clj b/src/main/cljs/cljs/reader.clj index 5aa4b86a3..09afd3a53 100644 --- a/src/main/cljs/cljs/reader.clj +++ b/src/main/cljs/cljs/reader.clj @@ -13,6 +13,6 @@ (let [data-readers (->> (get @env/*compiler* :cljs.analyzer/data-readers) (map (fn [[k v]] - `['~k (fn [x#] (~(vary-meta v assoc :cljs.analyzer/no-resolve true) x#))])) + `['~k (fn [x#] (~(vary-meta (-> v meta :sym) assoc :cljs.analyzer/no-resolve true) x#))])) (into {}))] `(do (merge ~default-readers ~data-readers)))) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c5fd8342a..d7dde3fe1 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -618,6 +618,65 @@ #?(:clj (def load-mutex (Object.))) +#?(:clj + (defn- load-data-reader-file [mappings ^java.net.URL url] + (with-open [rdr (readers/input-stream-push-back-reader (.openStream url))] + (binding [*file* (.getFile url)] + (let [new-mappings (reader/read {:eof nil :read-cond :allow :features #{:cljs}} rdr)] + (when (not (map? new-mappings)) + (throw (ex-info (str "Not a valid data-reader map") + {:url url + :clojure.error/phase :compilation}))) + (reduce + (fn [m [k v]] + (when (not (symbol? k)) + (throw (ex-info (str "Invalid form in data-reader file") + {:url url + :form k + :clojure.error/phase :compilation}))) + (when (and (contains? mappings k) + (not= (mappings k) v)) + (throw (ex-info "Conflicting data-reader mapping" + {:url url + :conflict k + :mappings m + :clojure.error/phase :compilation}))) + (assoc m k v)) + mappings + new-mappings)))))) + +#?(:clj + (defn get-data-readers* + "returns a merged map containing all data readers defined by libraries + on the classpath." + ([] + (get-data-readers* (. (Thread/currentThread) (getContextClassLoader)))) + ([^ClassLoader classloader] + (let [data-reader-urls (enumeration-seq (. classloader (getResources "data_readers.cljc")))] + (reduce load-data-reader-file {} data-reader-urls))))) + +#?(:clj + (def get-data-readers (memoize get-data-readers*))) + +#?(:clj + (defn load-data-readers* [] + (let [data-readers (get-data-readers) + nses (map (comp symbol namespace) (vals data-readers))] + (doseq [ns nses] + (try + (locking load-mutex + (require ns)) + (catch Throwable _))) + (->> data-readers + (map (fn [[tag reader-fn]] + [tag + (-> reader-fn find-var var-get + (with-meta {:sym reader-fn}))])) + (into {}))))) + +#?(:clj + (def load-data-readers (memoize load-data-readers*))) + #?(:clj (defn load-core [] (when (not @-cljs-macros-loaded) @@ -4310,6 +4369,7 @@ (resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*)) sym))))) + #?(:clj (defn forms-seq* "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally @@ -4324,7 +4384,8 @@ {:read-cond :allow :features #{:cljs}})) pbr (readers/indexing-push-back-reader (PushbackReader. rdr) 1 filename) - data-readers tags/*cljs-data-readers* + data-readers (merge tags/*cljs-data-readers* + (load-data-readers)) forms-seq_ (fn forms-seq_ [] (lazy-seq @@ -4352,7 +4413,8 @@ (let [rdr (io/reader f) pbr (readers/indexing-push-back-reader (PushbackReader. rdr) 1 filename) - data-readers tags/*cljs-data-readers* + data-readers (merge tags/*cljs-data-readers* + (load-data-readers)) forms-seq* (fn forms-seq* [] (lazy-seq diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 34716156c..6c86b4996 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2976,52 +2976,9 @@ opts js-modules))) opts))) -(defn- load-data-reader-file [mappings ^java.net.URL url] - (with-open [rdr (readers/input-stream-push-back-reader (.openStream url))] - (binding [*file* (.getFile url)] - (let [new-mappings (reader/read {:eof nil :read-cond :allow} rdr)] - (when (not (map? new-mappings)) - (throw (ex-info (str "Not a valid data-reader map") - {:url url - :clojure.error/phase :compilation}))) - (reduce - (fn [m [k v]] - (when (not (symbol? k)) - (throw (ex-info (str "Invalid form in data-reader file") - {:url url - :form k - :clojure.error/phase :compilation}))) - (when (and (contains? mappings k) - (not= (mappings k) v)) - (throw (ex-info "Conflicting data-reader mapping" - {:url url - :conflict k - :mappings m - :clojure.error/phase :compilation}))) - (assoc m k v)) - mappings - new-mappings))))) - -(defn get-data-readers* - "returns a merged map containing all data readers defined by libraries - on the classpath." - ([] - (get-data-readers* (. (Thread/currentThread) (getContextClassLoader)))) - ([classloader] - (let [data-reader-urls (enumeration-seq (. classloader (getResources "data_readers.cljc")))] - (reduce load-data-reader-file {} data-reader-urls)))) - -(def get-data-readers (memoize get-data-readers*)) - (defn load-data-readers! [compiler] - (let [data-readers (get-data-readers) - nses (map (comp symbol namespace) (vals data-readers))] - (swap! compiler update-in [:cljs.analyzer/data-readers] merge (get-data-readers)) - (doseq [ns nses] - (try - (locking ana/load-mutex - (require ns)) - (catch Throwable _))))) + (swap! compiler update-in [:cljs.analyzer/data-readers] merge + (ana/load-data-readers))) (defn add-externs-sources [opts] (cond-> opts diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 7f43d59fc..384f17bfc 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -91,7 +91,8 @@ (when (try (let [[form s] (binding [*ns* (create-ns ana/*cljs-ns*) reader/resolve-symbol ana/resolve-symbol - reader/*data-readers* tags/*cljs-data-readers* + reader/*data-readers* (merge tags/*cljs-data-readers* + (ana/load-data-readers)) reader/*alias-map* (apply merge ((juxt :requires :require-macros) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index d10e05807..ff758d834 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1143,7 +1143,8 @@ (fn [] (let [input (binding [*ns* (create-ns ana/*cljs-ns*) reader/resolve-symbol ana/resolve-symbol - reader/*data-readers* tags/*cljs-data-readers* + reader/*data-readers* (merge tags/*cljs-data-readers* + (ana/load-data-readers)) reader/*alias-map* (apply merge ((juxt :requires :require-macros :as-aliases) @@ -1510,7 +1511,8 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (let [rdr (readers/source-logging-push-back-reader pbr)] (dotimes [_ (dec (:line v))] (readers/read-line rdr)) (binding [reader/*alias-map* identity - reader/*data-readers* tags/*cljs-data-readers*] + reader/*data-readers* (merge tags/*cljs-data-readers* + (ana/load-data-readers))] (-> (reader/read {:read-cond :allow :features #{:cljs}} rdr) meta :source))))))))) diff --git a/src/test/cljs/data_readers.cljc b/src/test/cljs/data_readers.cljc index 59daf97f8..57c306152 100644 --- a/src/test/cljs/data_readers.cljc +++ b/src/test/cljs/data_readers.cljc @@ -9,4 +9,5 @@ {cljs/tag clojure.core/identity cljs/inc clojure.core/inc cljs/union clojure.set/union - test/custom-identity data-readers-test.core/custom-identity} + test/custom-identity data-readers-test.core/custom-identity + test/custom-form #?(:cljs data-readers-test.core/custom-form-cljs :clj clojure.core/identity)} diff --git a/src/test/cljs/data_readers_test/core.cljc b/src/test/cljs/data_readers_test/core.cljc index 62ca33bca..c45fef7fe 100644 --- a/src/test/cljs/data_readers_test/core.cljc +++ b/src/test/cljs/data_readers_test/core.cljc @@ -11,3 +11,15 @@ (def custom-identity identity) (assert (= 1 #test/custom-identity 1)) + +(defn custom-form-cljs + "a clojure and clojurescript function - in both cases targeting only cljs. + + returns a clojurescript form (from :clj branch, when compiling) + and executes js from :cljs branch when using cljs.reader/read" + [x] + #?(:clj `(js/Array.of ~x) + :cljs (js/Array.of x))) + +#?(:cljs + (def result #test/custom-form"foo")) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 4882c9bd6..dad29746e 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -456,7 +456,11 @@ cenv (env/default-compiler-env)] (test/delete-out-files out) (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) - (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)))) + (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)) + (is (true? (boolean (re-find #"Array\.of\(\"foo\"\)" + (slurp (io/file + out ;"data-readers-test-out" + "data_readers_test" "core.js")))))))) (deftest test-data-readers-records (let [out (.getPath (io/file (test/tmp-dir) "data-readers-test-records-out")) From f0e11477db3c1c0c70f7964de309bbdaadf55953 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 25 Jan 2022 13:10:25 -0500 Subject: [PATCH 3780/4033] add missing :as-aliases to Socket REPL --- src/main/clojure/cljs/core/server.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 384f17bfc..7e8c7ed07 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -95,7 +95,7 @@ (ana/load-data-readers)) reader/*alias-map* (apply merge - ((juxt :requires :require-macros) + ((juxt :requires :require-macros :as-aliases) (ana/get-namespace ana/*cljs-ns*)))] (reader/read+string {:eof EOF :read-cond :allow :features #{:cljs}} in-reader))] From 72aa5c64a2f28024b561f4b898050a8989ca7515 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Jan 2022 11:47:43 -0500 Subject: [PATCH 3781/4033] CLJS-3319 make PersistentHashMap release inodes correctly (#121) Co-authored-by: Hyunwoo Nam --- src/main/cljs/cljs/core.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 85169c362..9c251d794 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7329,7 +7329,9 @@ reduces them without incurring seq initialization" (== bitmap bit) nil :else (BitmapIndexedNode. nil (bit-xor bitmap bit) (remove-pair arr idx)))) (key-test key key-or-nil) - (BitmapIndexedNode. nil (bit-xor bitmap bit) (remove-pair arr idx)) + (if (== bitmap bit) + nil + (BitmapIndexedNode. nil (bit-xor bitmap bit) (remove-pair arr idx))) :else inode))))) (inode-lookup [inode shift hash key not-found] From c260cea7b03b223d6262ddd6e8c1f7346b92e965 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 27 Jan 2022 13:03:39 -0500 Subject: [PATCH 3782/4033] CLJS-3299: port CLJ-2603 (#122) Port CLJ-2603: Added support for trailing, conj-able element in map-destructuring support for seqs Faithfully port over all the relevant Java bits. * update --destructure-map helper * move key-test up for use by PAM.createAsIfByAssoc(ComplexPath) * port new PAM.createAsIfByAssoc - don't copy init, check for trailing & dupes * add PAM.createAsIfByComplexPath, ported from Java * add seq-to-map-for-destructuring * update docstrings * add tests --- src/main/cljs/cljs/core.cljs | 124 +++++++++++++++++---- src/main/clojure/cljs/core.cljc | 17 ++- src/test/cljs/cljs/destructuring_test.cljs | 44 ++++++++ 3 files changed, 159 insertions(+), 26 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 9c251d794..ca363c686 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4005,8 +4005,14 @@ reduces them without incurring seq initialization" ;; 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 [x] - (if (implements? ISeq x) (apply cljs.core/hash-map x) x)) +(defn --destructure-map [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 @@ -7057,19 +7063,91 @@ reduces them without incurring seq initialization" (let [cnt (/ (alength arr) 2)] (PersistentArrayMap. nil cnt arr nil))))) +(defn key-test [key other] + (cond + (identical? key other) true + (keyword-identical? key other) true + :else (= key other))) + +(defn- ^boolean pam-dupes? [arr] + (loop [i 0] + (if (< i (alength arr)) + (let [dupe? (loop [j 0] + (if (< j i) + (or + (key-test (aget arr i) (aget arr j)) + (recur (+ 2 j))) + false))] + (or dupe? (recur (+ 2 i)))) + false))) + +(defn- pam-new-size [arr] + (loop [i 0 n 0] + (if (< i (alength arr)) + (let [dupe? (loop [j 0] + (if (< j i) + (or + (key-test (aget arr i) (aget arr j)) + (recur (+ 2 j))) + false))] + (recur (+ 2 i) (if dupe? n (+ n 2)))) + n))) + +(defn- pam-grow-seed-array [seed trailing] + (let [seed-cnt (dec (alength seed)) + extra-kvs (seq trailing) + ret (make-array (+ seed-cnt (* 2 (count extra-kvs)))) + ret (array-copy seed 0 ret 0 seed-cnt)] + (loop [i seed-cnt extra-kvs extra-kvs] + (if extra-kvs + (let [kv (first extra-kvs)] + (aset ret i (-key kv)) + (aset ret (inc i) (-val kv)) + (recur (+ 2 seed-cnt) (next extra-kvs))) + ret)))) + (set! (.-createAsIfByAssoc PersistentArrayMap) - (fn [arr] - (let [ret (array)] - (loop [i 0] - (when (< i (alength arr)) - (let [k (aget arr i) - v (aget arr (inc i)) - idx (array-index-of ret k)] - (if (== idx -1) - (doto ret (.push k) (.push v)) - (aset ret (inc idx) v))) - (recur (+ i 2)))) - (PersistentArrayMap. nil (/ (alength ret) 2) ret nil)))) + (fn [init] + ;; check trailing element + (let [len (alength init) + 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?))))) + +(set! (.-createAsIfByAssocComplexPath PersistentArrayMap) + (fn [init ^boolean has-trailing?] + (let [init (if has-trailing? + (pam-grow-seed-array init + ;; into {} in case the final element is not a map but something conj-able + ;; for parity with Clojure implementation of CLJ-2603 + (into {} (aget init (dec (alength init))))) + init) + n (pam-new-size init) + len (alength init)] + (if (< n len) + (let [nodups (make-array n)] + (loop [i 0 m 0] + (if (< i len) + (let [dupe? (loop [j 0] + (if (< j m) + (or + (key-test (aget init i) (aget init j)) + (recur (+ 2 j))) + false))] + (if-not dupe? + (let [j (loop [j (- len 2)] + (if (>= j i) + (if (key-test (aget init i) (aget init j)) + j + (recur (- j 2))) + j))] + (aset nodups m (aget init i)) + (aset nodups (inc m) (aget init (inc j))) + (recur (+ 2 i) (+ 2 m))) + (recur (+ 2 i) m))))) + (PersistentArrayMap. nil (/ (alength nodups) 2) nodups nil)) + (PersistentArrayMap. nil (/ (alength init) 2) init nil))))) (es6-iterable PersistentArrayMap) @@ -7170,12 +7248,6 @@ reduces them without incurring seq initialization" (declare create-inode-seq create-array-node-seq reset! create-node atom deref) -(defn key-test [key other] - (cond - (identical? key other) true - (keyword-identical? key other) true - :else (= key other))) - (defn- mask [hash shift] (bit-and (bit-shift-right-zero-fill hash shift) 0x01f)) @@ -8947,7 +9019,17 @@ reduces them without incurring seq initialization" (let [arr (if (and (instance? IndexedSeq keyvals) (zero? (.-i keyvals))) (.-arr keyvals) (into-array keyvals))] - (.createAsIfByAssoc PersistentArrayMap arr))) + (if (odd? (alength 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 diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3801081de..49ea65a1b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -267,12 +267,14 @@ #?(:cljs (core/defmacro fn - "params => positional-params* , or positional-params* & next-param + "params => positional-params* , or positional-params* & rest-param positional-param => binding-form - next-param => binding-form - name => symbol + rest-param => binding-form + binding-form => name, or destructuring-form - Defines a function" + Defines a function + + See https://clojure.org/reference/special_forms#fn for more information" {:forms '[(fn name? [params*] exprs*) (fn name? ([params*] exprs*) +)]} [& sigs] (core/let [name (if (core/symbol? (first sigs)) (first sigs) nil) @@ -769,10 +771,15 @@ (core/defmacro let "binding => binding-form init-expr + binding-form => name, or destructuring-form + destructuring-form => map-destructure-form, or seq-destructure-form Evaluates the exprs in a lexical context in which the symbols in the binding-forms are bound to their respective init-exprs or parts - therein." + therein. + + See https://clojure.org/reference/special_forms#binding-forms for + more information about destructuring." [bindings & body] (assert-args let (vector? bindings) "a vector for its binding" diff --git a/src/test/cljs/cljs/destructuring_test.cljs b/src/test/cljs/cljs/destructuring_test.cljs index 2823b6d83..d8caf7387 100644 --- a/src/test/cljs/cljs/destructuring_test.cljs +++ b/src/test/cljs/cljs/destructuring_test.cljs @@ -185,3 +185,47 @@ (is (= 1 (f 1))) (is (= 1 (f 1 2)))) (let [])) + +(deftest test-pam-dupes? + (is (false? (#'pam-dupes? #js [:a 1 :b 2 :c 3]))) + (is (true? (#'pam-dupes? #js [:a 1 :b 2 :a 3])))) + +(deftest test-pam-new-size + (is (== 6 (#'pam-new-size #js [:a 1 :b 2 :c 3]))) + (is (== 4 (#'pam-new-size #js [:a 1 :b 2 :a 3])))) + +(deftest singleton-map-in-destructure-context + (let [sample-map {:a 1 :b 2} + {:keys [a] :as m1} (list sample-map)] + (is (= m1 sample-map)) + (is (= a 1)))) + +(deftest trailing-map-destructuring + (let [add (fn [& {:keys [a b]}] (+ a b)) + addn (fn [n & {:keys [a b]}] (+ n a b))] + (testing "that kwargs are applied properly given a map in place of the key/val pairs" + (is (= 3 (add :a 1 :b 2))) + (is (= 3 (add {:a 1 :b 2}))) + (is (= 13 (addn 10 :a 1 :b 2))) + (is (= 13 (addn 10 {:a 1 :b 2}))) + (is (= 103 ((partial addn 100) :a 1 {:b 2}))) + (is (= 103 ((partial addn 100 :a 1) {:b 2}))) + (is (= 107 ((partial addn 100 :a 1) {:a 5 :b 2})))) + (testing "built maps" + (let [{:as m1} (list :a 1 :b 2) + {:as m2} (list :a 1 :b 2 {:c 3}) + {:as m3} (list :a 1 :b 2 {:a 0}) + {:keys [a4] :as m4} (list nil)] + (= m1 {:a 1 :b 2}) + (= m2 {:a 1 :b 2 :c 3}) + (= m3 {:a 0 :b 2}) + (= m1 (seq-to-map-for-destructuring (list :a 1 :b 2))) + (= m2 (seq-to-map-for-destructuring (list :a 1 :b 2 {:c 3}))) + (= m3 (seq-to-map-for-destructuring (list :a 1 :b 2 {:a 0}))) + (= a4 nil))))) + +(comment + + (cljs.test/run-tests) + + ) From 5e7fd72f7fa25e10241f229998e5620d79bc0aad Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 27 Jan 2022 16:07:46 -0500 Subject: [PATCH 3783/4033] - add ana/get-aliases helper (#123) - use consistently --- src/main/clojure/cljs/analyzer.cljc | 13 ++++++++----- src/main/clojure/cljs/core/server.clj | 5 +---- src/main/clojure/cljs/repl.cljc | 5 +---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index d7dde3fe1..165c02ac4 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -674,7 +674,7 @@ (with-meta {:sym reader-fn}))])) (into {}))))) -#?(:clj +#?(:clj (def load-data-readers (memoize load-data-readers*))) #?(:clj @@ -4369,6 +4369,12 @@ (resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*)) sym))))) +(defn get-aliases + "Get all alias maps for a namespace." + [ns] + (apply merge + ((juxt :requires :require-macros :as-aliases) + (get-namespace ns)))) #?(:clj (defn forms-seq* @@ -4391,10 +4397,7 @@ (lazy-seq (let [form (binding [*ns* (create-ns *cljs-ns*) reader/*data-readers* data-readers - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros :as-aliases) - (get-namespace *cljs-ns*))) + reader/*alias-map* (get-aliases *cljs-ns*) reader/resolve-symbol resolve-symbol] (reader/read opts pbr))] (if (identical? form eof-sentinel) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index 7e8c7ed07..e2661b59f 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -93,10 +93,7 @@ reader/resolve-symbol ana/resolve-symbol reader/*data-readers* (merge tags/*cljs-data-readers* (ana/load-data-readers)) - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros :as-aliases) - (ana/get-namespace ana/*cljs-ns*)))] + reader/*alias-map* (ana/get-aliases ana/*cljs-ns*)] (reader/read+string {:eof EOF :read-cond :allow :features #{:cljs}} in-reader))] (try diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index ff758d834..24644bdc0 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1145,10 +1145,7 @@ reader/resolve-symbol ana/resolve-symbol reader/*data-readers* (merge tags/*cljs-data-readers* (ana/load-data-readers)) - reader/*alias-map* - (apply merge - ((juxt :requires :require-macros :as-aliases) - (ana/get-namespace ana/*cljs-ns*)))] + reader/*alias-map* (ana/get-aliases ana/*cljs-ns*)] (try (read request-prompt request-exit) (catch Throwable e From b25910d6f9d22b8a8331c018897da10b37fc9698 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 29 Jan 2022 16:02:29 -0500 Subject: [PATCH 3784/4033] CLJS-3349: Add :as-alias in alias map for self-hosted --- src/main/cljs/cljs/js.cljs | 4 ++-- src/test/cljs/cljs/ns_test/foo.cljs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index fb13e0054..22db97868 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -186,8 +186,8 @@ (defn- alias-map [compiler cljs-ns] - (->> (merge (get-in compiler [::ana/namespaces cljs-ns :requires]) - (get-in compiler [::ana/namespaces cljs-ns :require-macros])) + (->> (env/with-compiler-env compiler + (ana/get-aliases cljs-ns)) (remove (fn [[k v]] (symbol-identical? k v))) (into {}))) diff --git a/src/test/cljs/cljs/ns_test/foo.cljs b/src/test/cljs/cljs/ns_test/foo.cljs index 568d1a778..2da20eae1 100644 --- a/src/test/cljs/cljs/ns_test/foo.cljs +++ b/src/test/cljs/cljs/ns_test/foo.cljs @@ -7,7 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns cljs.ns-test.foo - (:require [cljs.test :refer-macros [deftest is]])) + (:require [cljs.test :refer-macros [deftest is]] + [made.up.lib :as-alias lib])) (defn baz [] 123) @@ -17,3 +18,6 @@ (deftest test-namespaced-keywords (is (= (str kw) ":cljs.ns-test.foo/foo")) (is (= (str qkw) ":cljs.ns-test.foo/foo"))) + +(deftest test-as-alias-keywords + (is (keyword-identical? ::lib/foo :made.up.lib/foo))) From 0572fd555b01d4b0ea51acce75c32951a43434f2 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 30 Jan 2022 09:39:38 -0500 Subject: [PATCH 3785/4033] CLJS-3350: Update test.check dependency --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index 47a4ea8b9..fb338ca94 100644 --- a/deps.edn +++ b/deps.edn @@ -8,7 +8,7 @@ org.clojure/google-closure-library {:mvn/version "0.0-20211011-0726fdeb"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/tools.reader {:mvn/version "1.3.3"} - org.clojure/test.check {:mvn/version "0.10.0-alpha3"}} + org.clojure/test.check {:mvn/version "1.1.1"}} :aliases {:cli.test.run {:extra-paths ["src/test/cljs_cli"] :main-opts ["-i" "src/test/cljs_cli/cljs_cli/test_runner.clj" diff --git a/pom.template.xml b/pom.template.xml index 8f1438ba7..f7e9853c2 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -61,7 +61,7 @@ org.clojure test.check - 0.10.0-alpha3 + 1.1.1 test diff --git a/project.clj b/project.clj index 8fd0f5d72..766a2d0e5 100644 --- a/project.clj +++ b/project.clj @@ -13,7 +13,7 @@ [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "0.2.6"] [org.clojure/tools.reader "1.3.3"] - [org.clojure/test.check "0.10.0-alpha3" :scope "test"] + [org.clojure/test.check "1.1.1" :scope "test"] [com.cognitect/transit-clj "0.8.309"] [org.clojure/google-closure-library "0.0-20211011-0726fdeb"] [com.google.javascript/closure-compiler-unshaded "v20210808"]] diff --git a/script/bootstrap b/script/bootstrap index 13a71d814..0063c8398 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -10,7 +10,7 @@ DJSON_RELEASE="0.2.6" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20211011-0726fdeb" TREADER_RELEASE="1.3.3" -TEST_CHECK_RELEASE="0.10.0-alpha3" +TEST_CHECK_RELEASE="1.1.1" # check dependencies curl -V >/dev/null || { echo "cURL is missing, or not on your system path."; exit 1; } From 90497c105145202413d95feaf5e937bcf5ad6765 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 1 Feb 2022 15:26:54 -0500 Subject: [PATCH 3786/4033] CLJS-3351: Self-parity tests fail with tests requiring clojure.test --- src/test/self/self_parity/test.cljs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 3a6cfcb52..48330c1d9 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -135,7 +135,7 @@ using a supplied read-file-fn, calling back upon first successful read, otherwise calling back with nil. Before calling back, first attempts to read AOT artifacts (JavaScript and cache edn)." - [[filename & more-filenames] read-file-fn cb] + [[filename & more-filenames] macros read-file-fn cb] (if filename (read-file-fn filename @@ -144,8 +144,9 @@ (let [source-cb-value {:lang (filename->lang filename) :file filename :source source}] - (if (or (string/ends-with? filename ".cljs") - (string/ends-with? filename ".cljc")) + (if (and (not macros) + (or (string/ends-with? filename ".cljs") + (string/ends-with? filename ".cljc"))) (read-file-fn (replace-extension filename ".js") (fn [javascript-source] @@ -160,7 +161,7 @@ (cb source-cb-value)))) (cb source-cb-value)))) (cb source-cb-value))) - (read-some more-filenames read-file-fn cb)))) + (read-some more-filenames macros read-file-fn cb)))) (cb nil))) (defn filenames-to-try @@ -192,17 +193,6 @@ 'cljs.tools.reader 'clojure.walk}) name)) -;; An atom to keep track of things we've already loaded -(def loaded (atom #{})) - -(defn load? - "Determines whether the given namespace should be loaded." - [name macros] - (let [do-not-load (or (@loaded [name macros]) - (skip-load? name macros))] - (swap! loaded conj [name macros]) - (not do-not-load))) - (defn make-load-fn "Makes a load function that will read from a sequence of src-paths using a supplied read-file-fn. It returns a cljs.js-compatible @@ -212,10 +202,10 @@ with the source of the library (as string)." [src-paths read-file-fn] (fn [{:keys [name macros path]} cb] - (if (load? name macros) + (if-not (skip-load? name macros) (if (re-matches #"^goog/.*" path) (load-goog name cb) - (read-some (filenames-to-try src-paths macros path) read-file-fn cb)) + (read-some (filenames-to-try src-paths macros path) macros read-file-fn cb)) (cb {:source "" :lang :js})))) From b24c036618b958d30b3b405625ae917fbf07e1f4 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Tue, 1 Feb 2022 20:05:10 -0500 Subject: [PATCH 3787/4033] CLJS-3352: Self-host negative zero emitted as positive zero --- src/main/clojure/cljs/compiler.cljc | 3 +++ src/test/cljs/cljs/primitives_test.cljs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b3aa15a51..8d0bdcb14 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -335,6 +335,9 @@ (not (js/isFinite x)) (emits (if (pos? x) "Infinity" "-Infinity")) + (and (zero? x) (neg? (/ x))) + (emits "(-0)") + :else (emits "(" x ")")))) #?(:clj diff --git a/src/test/cljs/cljs/primitives_test.cljs b/src/test/cljs/cljs/primitives_test.cljs index 7656e5c75..d2ec0b57c 100644 --- a/src/test/cljs/cljs/primitives_test.cljs +++ b/src/test/cljs/cljs/primitives_test.cljs @@ -953,6 +953,10 @@ (is (= 1 (gobject/get #js {:a 1} "a"))) (is (= 1 (.-a #js {:a 1}))))) +(deftest test-3352 + (is (== ##-Inf (/ -0.0))) + (is (== ##Inf (/ -0)))) + (deftest test-char? (is (char? "0")) (is (char? (String/fromCharCode 13))) From d77e73929247c9a8ec44be22e6d58c50afa8489e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 3 Feb 2022 10:52:11 -0500 Subject: [PATCH 3788/4033] bump data.json (#126) --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index fb338ca94..df4a2251f 100644 --- a/deps.edn +++ b/deps.edn @@ -4,7 +4,7 @@ com.cognitect/transit-clj {:mvn/version "0.8.309"} org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} - org.clojure/data.json {:mvn/version "0.2.6"} + org.clojure/data.json {:mvn/version "2.4.0"} org.clojure/google-closure-library {:mvn/version "0.0-20211011-0726fdeb"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/tools.reader {:mvn/version "1.3.3"} diff --git a/pom.template.xml b/pom.template.xml index f7e9853c2..e17d1e24b 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -40,7 +40,7 @@ org.clojure data.json - 0.2.6 + 2.4.0 org.clojure diff --git a/project.clj b/project.clj index 766a2d0e5..61e9e2f79 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :dependencies [[org.clojure/clojure "1.10.0"] [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] - [org.clojure/data.json "0.2.6"] + [org.clojure/data.json "2.4.0"] [org.clojure/tools.reader "1.3.3"] [org.clojure/test.check "1.1.1" :scope "test"] [com.cognitect/transit-clj "0.8.309"] diff --git a/script/bootstrap b/script/bootstrap index 0063c8398..582cefdb5 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,7 +6,7 @@ CLOJURE_RELEASE="1.9.0" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20210808" -DJSON_RELEASE="0.2.6" +DJSON_RELEASE="2.4.0" TRANSIT_RELEASE="0.8.309" GCLOSURE_LIB_RELEASE="0.0-20211011-0726fdeb" TREADER_RELEASE="1.3.3" From c9a7e0d3172e2c7cb35903413f7d2b704718998a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 3 Feb 2022 11:18:40 -0500 Subject: [PATCH 3789/4033] bump transit (#127) --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps.edn b/deps.edn index df4a2251f..17b28187c 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,7 @@ {:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20210808"} - com.cognitect/transit-clj {:mvn/version "0.8.309"} + com.cognitect/transit-clj {:mvn/version "1.0.324"} org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "2.4.0"} diff --git a/pom.template.xml b/pom.template.xml index e17d1e24b..f5e4ac3a2 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -50,7 +50,7 @@ com.cognitect transit-clj - 0.8.309 + 1.0.324 org.clojure diff --git a/project.clj b/project.clj index 61e9e2f79..2c6e68c42 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/data.json "2.4.0"] [org.clojure/tools.reader "1.3.3"] [org.clojure/test.check "1.1.1" :scope "test"] - [com.cognitect/transit-clj "0.8.309"] + [com.cognitect/transit-clj "1.0.324"] [org.clojure/google-closure-library "0.0-20211011-0726fdeb"] [com.google.javascript/closure-compiler-unshaded "v20210808"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} diff --git a/script/bootstrap b/script/bootstrap index 582cefdb5..3435c454f 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -7,7 +7,7 @@ SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20210808" DJSON_RELEASE="2.4.0" -TRANSIT_RELEASE="0.8.309" +TRANSIT_RELEASE="1.0.324" GCLOSURE_LIB_RELEASE="0.0-20211011-0726fdeb" TREADER_RELEASE="1.3.3" TEST_CHECK_RELEASE="1.1.1" From f5e8fa93506e3e6cb9716c860540f20a5d2e5fc2 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 3 Feb 2022 22:26:09 -0500 Subject: [PATCH 3790/4033] CLJS-3353: Add the new iteration function introduced in Clojure 1.11 --- src/main/cljs/cljs/core.cljs | 43 +++++++++++++ src/test/cljs/cljs/seqs_test.cljs | 100 ++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ca363c686..47ec5ebf0 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10944,6 +10944,49 @@ reduces them without incurring seq initialization" (reduce #(proc %2) nil coll) nil) +(defn iteration + "Creates a seqable/reducible via repeated calls to step, + a function of some (continuation token) 'k'. The first call to step + will be passed initk, returning 'ret'. Iff (somef ret) is true, + (vf ret) will be included in the iteration, else iteration will + terminate and vf/kf will not be called. If (kf ret) is non-nil it + will be passed to the next step call, else iteration will terminate. + This can be used e.g. to consume APIs that return paginated or batched data. + step - (possibly impure) fn of 'k' -> 'ret' + :somef - fn of 'ret' -> logical true/false, default 'some?' + :vf - fn of 'ret' -> 'v', a value produced by the iteration, default 'identity' + :kf - fn of 'ret' -> 'next-k' or nil (signaling 'do not continue'), default 'identity' + :initk - the first value passed to step, default 'nil' + It is presumed that step with non-initk is unreproducible/non-idempotent. + If step with initk is unreproducible it is on the consumer to not consume twice." + {:added "1.11"} + [step & {:keys [somef vf kf initk] + :or {vf identity + kf identity + somef some? + initk nil}}] + (reify + ISeqable + (-seq [_] + ((fn next [ret] + (when (somef ret) + (cons (vf ret) + (when-some [k (kf ret)] + (lazy-seq (next (step k))))))) + (step initk))) + IReduce + (-reduce [_ rf init] + (loop [acc init + ret (step initk)] + (if (somef ret) + (let [acc (rf acc (vf ret))] + (if (reduced? acc) + @acc + (if-some [k (kf ret)] + (recur acc (step k)) + acc))) + acc))))) + (defprotocol IEncodeJS (-clj->js [x] "Recursively transforms clj values to JavaScript") (-key->js [x] "Transforms map keys to valid JavaScript keys. Arbitrary keys are diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index d9e56fcaf..988f01e21 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -9,6 +9,11 @@ (ns cljs.seqs-test (:refer-clojure :exclude [iter]) (:require [cljs.test :refer-macros [deftest testing is]] + [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.test.check.random :as random] [clojure.string :as s] [clojure.set :as set])) @@ -237,3 +242,98 @@ (is (true? (js-iterable? (js/Set.)))) (is (false? (js-iterable? 1))) (is (false? (js-iterable? nil))))) + +(deftest test-iteration-opts + (let [genstep (fn [steps] + (fn [k] (swap! steps inc) (inc k))) + test (fn [expect & iteropts] + (is (= expect + (let [nsteps (atom 0) + iter (apply iteration (genstep nsteps) iteropts) + ret (doall (seq iter))] + {:ret ret :steps @nsteps}) + (let [nsteps (atom 0) + iter (apply iteration (genstep nsteps) iteropts) + ret (into [] iter)] + {:ret ret :steps @nsteps}))))] + (test {:ret [1 2 3 4] + :steps 5} + :initk 0 :somef #(< % 5)) + (test {:ret [1 2 3 4 5] + :steps 5} + :initk 0 :kf (fn [ret] (when (< ret 5) ret))) + (test {:ret ["1"] + :steps 2} + :initk 0 :somef #(< % 2) :vf str)) + + ;; kf does not stop on false + (let [iter #(iteration (fn [k] + (if (boolean? k) + [10 :boolean] + [k k])) + :vf second + :kf (fn [[k v]] + (cond + (= k 3) false + (< k 14) (inc k))) + :initk 0)] + (is (= [0 1 2 3 :boolean 11 12 13 14] + (into [] (iter)) + (seq (iter)))))) + +(deftest test-iteration + ;; equivalence to es6-iterator-seq + (let [arr #js [1 nil 3 true false 4 6 nil 7]] + (is (= (let [iter (es6-iterator arr)] + (vec (iteration (fn [_] (.next iter)) + :somef #(not (.-done %)) + :vf #(.-value %)))) + (let [iter (es6-iterator arr)] + (vec (es6-iterator-seq iter)))))) + + ;; paginated API + (let [items 12 pgsize 5 + src (vec (repeatedly items #(random-uuid))) + api (fn [tok] + (let [tok (or tok 0)] + (when (< tok items) + {:tok (+ tok pgsize) + :ret (subvec src tok (min (+ tok pgsize) items))})))] + (is (= src + (mapcat identity (iteration api :kf :tok :vf :ret)) + (into [] cat (iteration api :kf :tok :vf :ret))))) + + (let [src [:a :b :c :d :e] + api (fn [k] + (let [k (or k 0)] + (if (< k (count src)) + {:item (nth src k) + :k (inc k)})))] + (is (= [:a :b :c] + (vec (iteration api + :somef (comp #{:a :b :c} :item) + :kf :k + :vf :item)) + (vec (iteration api + :kf #(some-> % :k #{0 1 2}) + :vf :item)))))) + +(defn- make-rng [seed] + (atom (random/make-random seed))) + +(defn- next-long [rng] + (let [[r1 r2] (random/split @rng)] + (reset! rng r2) + (long (random/rand-long r1)))) + +(defspec iteration-seq-equals-reduce 1000 + (prop/for-all [initk gen/small-integer + seed gen/small-integer] + (let [src (fn [] + (let [rng (make-rng seed)] + (iteration #(unchecked-add % (next-long rng)) + :somef (complement #(zero? (mod % 1000))) + :vf str + :initk initk)))] + (= (into [] (src)) + (into [] (seq (src))))))) From d61bb65628c9f9a6687c0ff733279a6bb74a0f98 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 09:56:45 -0500 Subject: [PATCH 3791/4033] CLJS-3355: Port Clojure's clojure.set unit tests to ClojureScript --- src/test/cljs/clojure/set_test.cljs | 217 ++++++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 + src/test/self/self_parity/test.cljs | 2 + 3 files changed, 221 insertions(+) create mode 100644 src/test/cljs/clojure/set_test.cljs diff --git a/src/test/cljs/clojure/set_test.cljs b/src/test/cljs/clojure/set_test.cljs new file mode 100644 index 000000000..ad81ce743 --- /dev/null +++ b/src/test/cljs/clojure/set_test.cljs @@ -0,0 +1,217 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns clojure.set-test + (:require [clojure.test :refer [are deftest is]] + [clojure.set :as set])) + +(deftest test-union + (are [x y] (= x y) + (set/union) #{} + + ; identity + (set/union #{}) #{} + (set/union #{1}) #{1} + (set/union #{1 2 3}) #{1 2 3} + + ; 2 sets, at least one is empty + (set/union #{} #{}) #{} + (set/union #{} #{1}) #{1} + (set/union #{} #{1 2 3}) #{1 2 3} + (set/union #{1} #{}) #{1} + (set/union #{1 2 3} #{}) #{1 2 3} + + ; 2 sets + (set/union #{1} #{2}) #{1 2} + (set/union #{1} #{1 2}) #{1 2} + (set/union #{2} #{1 2}) #{1 2} + (set/union #{1 2} #{3}) #{1 2 3} + (set/union #{1 2} #{2 3}) #{1 2 3} + + ; 3 sets, some are empty + (set/union #{} #{} #{}) #{} + (set/union #{1} #{} #{}) #{1} + (set/union #{} #{1} #{}) #{1} + (set/union #{} #{} #{1}) #{1} + (set/union #{1 2} #{2 3} #{}) #{1 2 3} + + ; 3 sets + (set/union #{1 2} #{3 4} #{5 6}) #{1 2 3 4 5 6} + (set/union #{1 2} #{2 3} #{1 3 4}) #{1 2 3 4} + + ; different data types + (set/union #{1 2} #{:a :b} #{nil} #{false true} #{\c "abc"} #{[] [1 2]} + #{{} {:a 1}} #{#{} #{1 2}}) + #{1 2 :a :b nil false true \c "abc" [] [1 2] {} {:a 1} #{} #{1 2}} + + ; different types of sets + (set/union (hash-set) (hash-set 1 2) (hash-set 2 3)) + (hash-set 1 2 3) + (set/union (sorted-set) (sorted-set 1 2) (sorted-set 2 3)) + (sorted-set 1 2 3) + (set/union (hash-set) (hash-set 1 2) (hash-set 2 3) + (sorted-set) (sorted-set 4 5) (sorted-set 5 6)) + (hash-set 1 2 3 4 5 6) ; also equals (sorted-set 1 2 3 4 5 6) + )) + +(deftest test-intersection + (are [x y] (= x y) + ; identity + (set/intersection #{}) #{} + (set/intersection #{1}) #{1} + (set/intersection #{1 2 3}) #{1 2 3} + + ; 2 sets, at least one is empty + (set/intersection #{} #{}) #{} + (set/intersection #{} #{1}) #{} + (set/intersection #{} #{1 2 3}) #{} + (set/intersection #{1} #{}) #{} + (set/intersection #{1 2 3} #{}) #{} + + ; 2 sets + (set/intersection #{1 2} #{1 2}) #{1 2} + (set/intersection #{1 2} #{3 4}) #{} + (set/intersection #{1 2} #{1}) #{1} + (set/intersection #{1 2} #{2}) #{2} + (set/intersection #{1 2 4} #{2 3 4 5}) #{2 4} + + ; 3 sets, some are empty + (set/intersection #{} #{} #{}) #{} + (set/intersection #{1} #{} #{}) #{} + (set/intersection #{1} #{1} #{}) #{} + (set/intersection #{1} #{} #{1}) #{} + (set/intersection #{1 2} #{2 3} #{}) #{} + + ; 3 sets + (set/intersection #{1 2} #{2 3} #{5 2}) #{2} + (set/intersection #{1 2 3} #{1 3 4} #{1 3}) #{1 3} + (set/intersection #{1 2 3} #{3 4 5} #{8 2 3}) #{3} + + ; different types of sets + (set/intersection (hash-set 1 2) (hash-set 2 3)) #{2} + (set/intersection (sorted-set 1 2) (sorted-set 2 3)) #{2} + (set/intersection + (hash-set 1 2) (hash-set 2 3) + (sorted-set 1 2) (sorted-set 2 3)) #{2} )) + +(deftest test-difference + (are [x y] (= x y) + ; identity + (set/difference #{}) #{} + (set/difference #{1}) #{1} + (set/difference #{1 2 3}) #{1 2 3} + + ; 2 sets + (set/difference #{1 2} #{1 2}) #{} + (set/difference #{1 2} #{3 4}) #{1 2} + (set/difference #{1 2} #{1}) #{2} + (set/difference #{1 2} #{2}) #{1} + (set/difference #{1 2 4} #{2 3 4 5}) #{1} + + ; 3 sets + (set/difference #{1 2} #{2 3} #{5 2}) #{1} + (set/difference #{1 2 3} #{1 3 4} #{1 3}) #{2} + (set/difference #{1 2 3} #{3 4 5} #{8 2 3}) #{1} )) + +(deftest test-select + (are [x y] (= x y) + (set/select integer? #{}) #{} + (set/select integer? #{1 2}) #{1 2} + (set/select integer? #{1 2 :a :b :c}) #{1 2} + (set/select integer? #{:a :b :c}) #{}) ) + +(def compositions + #{{:name "Art of the Fugue" :composer "J. S. Bach"} + {:name "Musical Offering" :composer "J. S. Bach"} + {:name "Requiem" :composer "Giuseppe Verdi"} + {:name "Requiem" :composer "W. A. Mozart"}}) + +(deftest test-project + (are [x y] (= x y) + (set/project compositions [:name]) #{{:name "Art of the Fugue"} + {:name "Requiem"} + {:name "Musical Offering"}} + (set/project compositions [:composer]) #{{:composer "W. A. Mozart"} + {:composer "Giuseppe Verdi"} + {:composer "J. S. Bach"}} + (set/project compositions [:year]) #{{}} + (set/project #{{}} [:name]) #{{}} )) + +(deftest test-rename + (are [x y] (= x y) + (set/rename compositions {:name :title}) #{{:title "Art of the Fugue" :composer "J. S. Bach"} + {:title "Musical Offering" :composer "J. S. Bach"} + {:title "Requiem" :composer "Giuseppe Verdi"} + {:title "Requiem" :composer "W. A. Mozart"}} + (set/rename compositions {:year :decade}) #{{:name "Art of the Fugue" :composer "J. S. Bach"} + {:name "Musical Offering" :composer "J. S. Bach"} + {:name "Requiem" :composer "Giuseppe Verdi"} + {:name "Requiem" :composer "W. A. Mozart"}} + (set/rename #{{}} {:year :decade}) #{{}})) + +(deftest test-rename-keys + (are [x y] (= x y) + (set/rename-keys {:a "one" :b "two"} {:a :z}) {:z "one" :b "two"} + (set/rename-keys {:a "one" :b "two"} {:a :z :c :y}) {:z "one" :b "two"} + (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) {:a "two" :b "one" :c "three"})) + +(deftest test-index + (are [x y] (= x y) + (set/index #{{:c 2} {:b 1} {:a 1 :b 2}} [:b]) {{:b 2} #{{:a 1 :b 2}}, {:b 1} #{{:b 1}} {} #{{:c 2}}} + )) + +(deftest test-join + (are [x y] (= x y) + (set/join compositions compositions) compositions + (set/join compositions #{{:name "Art of the Fugue" :genre "Classical"}}) + #{{:name "Art of the Fugue" :composer "J. S. Bach" :genre "Classical"}} + )) + +(deftest test-map-invert + (are [x y] (= x y) + (set/map-invert {:a "one" :b "two"}) {"one" :a "two" :b})) + +(deftest test-subset? + (are [sub super] (set/subset? sub super) + #{} #{} + #{} #{1} + #{1} #{1} + #{1 2} #{1 2} + #{1 2} #{1 2 42} + #{false} #{false} + #{nil} #{nil} + #{nil} #{nil false} + #{1 2 nil} #{1 2 nil 4}) + (are [notsub super] (not (set/subset? notsub super)) + #{1} #{} + #{2} #{1} + #{1 3} #{1} + #{nil} #{false} + #{false} #{nil} + #{false nil} #{nil} + #{1 2 nil} #{1 2})) + +(deftest test-superset? + (are [super sub] (set/superset? super sub) + #{} #{} + #{1} #{} + #{1} #{1} + #{1 2} #{1 2} + #{1 2 42} #{1 2} + #{false} #{false} + #{nil} #{nil} + #{false nil} #{false} + #{1 2 4 nil false} #{1 2 nil}) + (are [notsuper sub] (not (set/superset? notsuper sub)) + #{} #{1} + #{2} #{1} + #{1} #{1 3} + #{nil} #{false} + #{false} #{nil} + #{nil} #{false nil} + #{nil 2 3} #{false nil 2 3})) \ No newline at end of file diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 0c2900009..f677d2e87 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -20,6 +20,7 @@ [cljs.reader-test] [cljs.binding-test] [cljs.ns-test] + [clojure.set-test] [clojure.string-test] [clojure.data-test] [clojure.datafy-test] @@ -73,6 +74,7 @@ 'cljs.hashing-test 'cljs.core-test 'cljs.reader-test + 'clojure.set-test 'clojure.string-test 'clojure.data-test 'clojure.datafy-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 48330c1d9..aabe60478 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -281,6 +281,7 @@ [cljs.reader-test] [cljs.binding-test] #_[cljs.ns-test] + [clojure.set-test] [clojure.string-test] [clojure.data-test] [clojure.datafy-test] @@ -328,6 +329,7 @@ 'cljs.hashing-test 'cljs.core-test 'cljs.reader-test + 'clojure.set-test 'clojure.string-test 'clojure.data-test 'clojure.datafy-test From 9114d2509eef48f1a167bb128880bf4598b06f36 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 09:30:37 -0500 Subject: [PATCH 3792/4033] CLJS-3354: map-invert should use transients and reduce-kv instead of reduce --- src/main/cljs/clojure/set.cljs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/set.cljs b/src/main/cljs/clojure/set.cljs index b9ba41f7b..7eddf4bc7 100644 --- a/src/main/cljs/clojure/set.cljs +++ b/src/main/cljs/clojure/set.cljs @@ -96,7 +96,11 @@ (defn map-invert "Returns the map with the vals mapped to the keys." - [m] (reduce (fn [m [k v]] (assoc m v k)) {} m)) + [m] + (persistent! + (reduce-kv (fn [m k v] (assoc! m v k)) + (transient {}) + m))) (defn join "When passed 2 rels, returns the rel corresponding to the natural From 573790c372c35ee350343a6735d06a5f742e0433 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 17:43:16 -0500 Subject: [PATCH 3793/4033] CLJS-3357: Malformed test at end of test-cljs-1837 --- src/test/cljs/cljs/core_test.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 472984d8b..13ca428ae 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1282,8 +1282,8 @@ [5 4])) (is (= (transduce (halt-when #{1} (fn [ret input] (conj ret input))) conj [] [5 4 1 2 3]) [5 4 1])) - (is (= (into [] (halt-when #{1} (fn [ret in] (conj! ret in))) [2 3 1])) - [2 3 1]))) + (is (= (into [] (halt-when #{1} (fn [ret in] (conj! ret in))) [2 3 1]) + [2 3 1])))) (deftest test-cljs-1839 (let [x #js {:foo (fn [])} From 990ed92ce5292b98ff9568c8c3b5d609de524ada Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Mon, 7 Feb 2022 11:56:44 -0500 Subject: [PATCH 3794/4033] CLJS-3356: halt-when not usable within #'c.c/into --- src/main/cljs/cljs/core.cljs | 6 +++++- src/test/cljs/cljs/core_test.cljs | 11 ++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 47ec5ebf0..304b3aac4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5262,7 +5262,11 @@ reduces them without incurring seq initialization" (reduce conj to from))) ([to xform from] (if (implements? IEditableCollection to) - (-with-meta (persistent! (transduce xform conj! (transient to) from)) (meta to)) + (let [tm (meta to) + rf (fn + ([coll] (-> (persistent! coll) (-with-meta tm))) + ([coll v] (conj! coll v)))] + (transduce xform rf (transient to) from)) (transduce xform conj to from)))) (defn mapv diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 13ca428ae..040ac75a6 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -387,6 +387,15 @@ (testing "CLJS-2258" (is (= ["1"] (sequence (map str) (eduction [1])))))) +(deftest test-into+halt-when + (is (= :anomaly (into [] (comp (filter some?) (halt-when #{:anomaly})) + [1 2 3 :anomaly 4]))) + (is (= {:anomaly :oh-no!, + :partial-results [1 2]} + (into [] + (halt-when :anomaly #(assoc %2 :partial-results %1)) + [1 2 {:anomaly :oh-no!} 3 4])))) + (deftest test-obj-equiv (testing "Object equiv method" (is (.equiv :foo :foo)) @@ -1282,7 +1291,7 @@ [5 4])) (is (= (transduce (halt-when #{1} (fn [ret input] (conj ret input))) conj [] [5 4 1 2 3]) [5 4 1])) - (is (= (into [] (halt-when #{1} (fn [ret in] (conj! ret in))) [2 3 1]) + (is (= (into [] (halt-when #{1} (fn [ret in] (conj ret in))) [2 3 1]) [2 3 1])))) (deftest test-cljs-1839 From f21d16f7612b6c46bc1c0399aea5098e691cb68d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 21:15:22 -0500 Subject: [PATCH 3795/4033] CLJS-3358: Add definition to reify docstring --- src/main/clojure/cljs/core.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 49ea65a1b..a4e110b7c 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1319,7 +1319,8 @@ 'js/Function "function"}) (core/defmacro reify - "reify is a macro with the following structure: + "reify creates an object implementing a protocol. + reify is a macro with the following structure: (reify options* specs*) From 0b621e2451da21d2891652fcbab110be05389a36 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 21:25:53 -0500 Subject: [PATCH 3796/4033] CLJS-3359: Clarify cljs.core/get docstring regarding sets, strings, arrays, ILookup --- src/main/cljs/cljs/core.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 304b3aac4..c4d534cf8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1951,7 +1951,8 @@ reduces them without incurring seq initialization" xs))) (defn get - "Returns the value mapped to key, not-found or nil if key not present." + "Returns the value mapped to key, not-found or nil if key not present + in associative collection, set, string, array, or ILookup instance." ([o k] (when-not (nil? o) (cond From 052e731d62e28e16f428c9e1c57165c3c62dfe8b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 21:32:10 -0500 Subject: [PATCH 3797/4033] CLJS-3360: Update clojure.string/split docstring regarding trailing empty parts --- src/main/cljs/clojure/string.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 94d38605a..854a78c64 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -166,7 +166,8 @@ (defn split "Splits string on a regular expression. Optional argument limit is - the maximum number of splits. Not lazy. Returns vector of the splits." + the maximum number of parts. Not lazy. Returns vector of the parts. + Trailing empty strings are not returned - pass limit of -1 to return all." ([s re] (split s re 0)) ([s re limit] @@ -189,7 +190,7 @@ (conj parts s)))))))))) (defn split-lines - "Splits s on \\n or \\r\\n." + "Splits s on \\n or \\r\\n. Trailing empty lines are not returned." [s] (split s #"\n|\r\n")) From 51a5b05770ab6dd903509c059e7fe42fd5e1c5d7 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 21:42:13 -0500 Subject: [PATCH 3798/4033] CLJS-3361: Add docstrings to uuid, uuid?, and random-uuid --- src/main/cljs/cljs/core.cljs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c4d534cf8..79f3629c7 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11575,11 +11575,15 @@ reduces them without incurring seq initialization" (garray/defaultCompare uuid (.-uuid other)) (throw (js/Error. (str "Cannot compare " this " to " other)))))) -(defn uuid [s] +(defn uuid + "Returns a UUID consistent with the string s." + [s] (assert (string? s)) (UUID. (.toLowerCase s) nil)) -(defn random-uuid [] +(defn random-uuid + "Returns a pseudo-randomly generated UUID instance (i.e. type 4)." + [] (letfn [(hex [] (.toString (rand-int 16) 16))] (let [rhex (.toString (bit-or 0x8 (bit-and 0x3 (rand-int 16))) 16)] (uuid @@ -11593,6 +11597,7 @@ reduces them without incurring seq initialization" (hex) (hex) (hex) (hex)))))) (defn uuid? + "Return true if x is a UUID." [x] (implements? IUUID x)) ;;; ExceptionInfo From fbfa114239f11dd71630df7788120bf91bc2d3d6 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 21:54:29 -0500 Subject: [PATCH 3799/4033] CLJS-3362: some-fn has different short-circuiting when using 3 predicates --- src/main/cljs/cljs/core.cljs | 8 +- src/test/cljs/cljs/other_functions_test.cljs | 379 +++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 + src/test/self/self_parity/test.cljs | 2 + 4 files changed, 387 insertions(+), 4 deletions(-) create mode 100644 src/test/cljs/cljs/other_functions_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 79f3629c7..f85e285fe 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4683,8 +4683,8 @@ reduces them without incurring seq initialization" (fn ep3 ([] true) ([x] (boolean (and (p1 x) (p2 x) (p3 x)))) - ([x y] (boolean (and (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y)))) - ([x y z] (boolean (and (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y) (p1 z) (p2 z) (p3 z)))) + ([x y] (boolean (and (p1 x) (p1 y) (p2 x) (p2 y) (p3 x) (p3 y)))) + ([x y z] (boolean (and (p1 x) (p1 y) (p1 z) (p2 x) (p2 y) (p2 z) (p3 x) (p3 y) (p3 z)))) ([x y z & args] (boolean (and (ep3 x y z) (every? #(and (p1 %) (p2 %) (p3 %)) args)))))) ([p1 p2 p3 & ps] @@ -4722,8 +4722,8 @@ reduces them without incurring seq initialization" (fn sp3 ([] nil) ([x] (or (p1 x) (p2 x) (p3 x))) - ([x y] (or (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y))) - ([x y z] (or (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y) (p1 z) (p2 z) (p3 z))) + ([x y] (or (p1 x) (p1 y) (p2 x) (p2 y) (p3 x) (p3 y))) + ([x y z] (or (p1 x) (p1 y) (p1 z) (p2 x) (p2 y) (p2 z) (p3 x) (p3 y) (p3 z))) ([x y z & args] (or (sp3 x y z) (some #(or (p1 %) (p2 %) (p3 %)) args))))) ([p1 p2 p3 & ps] diff --git a/src/test/cljs/cljs/other_functions_test.cljs b/src/test/cljs/cljs/other_functions_test.cljs new file mode 100644 index 000000000..52b2736f6 --- /dev/null +++ b/src/test/cljs/cljs/other_functions_test.cljs @@ -0,0 +1,379 @@ +; 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. + +; Author: Frantisek Sodomka + + +(ns cljs.other-functions-test + (:require [clojure.test :refer [deftest are is]])) + +(deftest test-identity + ; exactly 1 argument needed + ; (is (thrown? IllegalArgumentException (identity))) + ; (is (thrown? IllegalArgumentException (identity 1 2))) + + (are [x] (= (identity x) x) + nil + false true + 0 42 + 0.0 3.14 + 0M 1M + \c + "" "abc" + 'sym + :kw + () '(1 2) + [] [1 2] + {} {:a 1 :b 2} + #{} #{1 2} ) + + ; evaluation + (are [x y] (= (identity x) y) + (+ 1 2) 3 + (> 5 0) true )) + + +(deftest test-name + (are [x y] (= x (name y)) + "foo" :foo + "bar" 'bar + "quux" "quux")) + +(deftest test-fnil + (let [f1 (fnil vector :a) + f2 (fnil vector :a :b) + f3 (fnil vector :a :b :c)] + (are [result input] (= result [(apply f1 input) (apply f2 input) (apply f3 input)]) + [[1 2 3 4] [1 2 3 4] [1 2 3 4]] [1 2 3 4] + [[:a 2 3 4] [:a 2 3 4] [:a 2 3 4]] [nil 2 3 4] + [[:a nil 3 4] [:a :b 3 4] [:a :b 3 4]] [nil nil 3 4] + [[:a nil nil 4] [:a :b nil 4] [:a :b :c 4]] [nil nil nil 4] + [[:a nil nil nil] [:a :b nil nil] [:a :b :c nil]] [nil nil nil nil])) + (are [x y] (= x y) + ((fnil + 0) nil 42) 42 + ((fnil conj []) nil 42) [42] + (reduce #(update-in %1 [%2] (fnil inc 0)) {} + ["fun" "counting" "words" "fun"]) + {"words" 1, "counting" 1, "fun" 2} + (reduce #(update-in %1 [(first %2)] (fnil conj []) (second %2)) {} + [[:a 1] [:a 2] [:b 3]]) + {:b [3], :a [1 2]})) + +; time assert comment doc + +; partial +; comp + +(deftest test-comp + (let [c0 (comp)] + (are [x] (= (identity x) (c0 x)) + nil + 42 + [1 2 3] + #{} + :foo) + (are [x y] (= (identity x) (c0 y)) + (+ 1 2 3) 6 + (keyword "foo") :foo))) + +; complement + +(deftest test-complement + (let [not-contains? (complement contains?)] + (is (= true (not-contains? [2 3 4] 5))) + (is (= false (not-contains? [2 3 4] 2)))) + (let [first-elem-not-1? (complement (fn [x] (= 1 (first x))))] + (is (= true (first-elem-not-1? [2 3]))) + (is (= false (first-elem-not-1? [1 2]))))) + +; constantly + +(deftest test-constantly + (let [c0 (constantly 10)] + (are [x] (= 10 (c0 x)) + nil + 42 + "foo"))) +;juxt + +(deftest test-juxt + ;; juxt for colls + (let [m0 {:a 1 :b 2} + a0 [1 2]] + (is (= [1 2] ((juxt :a :b) m0))) + (is (= [2 1] ((juxt fnext first) a0)))) + ;; juxt for fns + (let [a1 (fn [a] (+ 2 a)) + b1 (fn [b] (* 2 b))] + (is (= [5 6] ((juxt a1 b1) 3))))) + +;partial + +(deftest test-partial + (let [p0 (partial inc) + p1 (partial + 20) + p2 (partial conj [1 2])] + (is (= 41 (p0 40))) + (is (= 40 (p1 20))) + (is (= [1 2 3] (p2 3))))) + +; every-pred +(deftest test-every-pred + (are [result expr] (= result expr) + ;; 1 pred + true ((every-pred even?)) + true ((every-pred even?) 2) + true ((every-pred even?) 2 4) + true ((every-pred even?) 2 4 6) + true ((every-pred even?) 2 4 6 8) + true ((every-pred even?) 2 4 6 8 10) + false ((every-pred odd?) 2) + false ((every-pred odd?) 2 4) + false ((every-pred odd?) 2 4 6) + false ((every-pred odd?) 2 4 6 8) + false ((every-pred odd?) 2 4 6 8 10) + ;; 2 preds + true ((every-pred even? number?)) + true ((every-pred even? number?) 2) + true ((every-pred even? number?) 2 4) + true ((every-pred even? number?) 2 4 6) + true ((every-pred even? number?) 2 4 6 8) + true ((every-pred even? number?) 2 4 6 8 10) + false ((every-pred number? odd?) 2) + false ((every-pred number? odd?) 2 4) + false ((every-pred number? odd?) 2 4 6) + false ((every-pred number? odd?) 2 4 6 8) + false ((every-pred number? odd?) 2 4 6 8 10) + ;; 2 preds, short-circuiting + false ((every-pred number? odd?) 1 :a) + false ((every-pred number? odd?) 1 3 :a) + false ((every-pred number? odd?) 1 3 5 :a) + false ((every-pred number? odd?) 1 3 5 7 :a) + false ((every-pred number? odd?) 1 :a 3 5 7) + ;; 3 preds + true ((every-pred even? number? #(> % 0))) + true ((every-pred even? number? #(> % 0)) 2) + true ((every-pred even? number? #(> % 0)) 2 4) + true ((every-pred even? number? #(> % 0)) 2 4 6) + true ((every-pred even? number? #(> % 0)) 2 4 6 8) + true ((every-pred even? number? #(> % 0)) 2 4 6 8 10) + true ((every-pred number? even? #(> % 0)) 2 4 6 8 10 12) + false ((every-pred number? odd? #(> % 0)) 2) + false ((every-pred number? odd? #(> % 0)) 2 4) + false ((every-pred number? odd? #(> % 0)) 2 4 6) + false ((every-pred number? odd? #(> % 0)) 2 4 6 8) + false ((every-pred number? odd? #(> % 0)) 2 4 6 8 10) + false ((every-pred number? odd? #(> % 0)) 2 4 6 8 -10) + ;; 3 preds, short-circuiting + false ((every-pred number? odd? #(> % 0)) 1 :a) + false ((every-pred number? odd? #(> % 0)) 1 3 :a) + false ((every-pred number? odd? #(> % 0)) 1 3 5 :a) + false ((every-pred number? odd? #(> % 0)) 1 3 5 7 :a) + false ((every-pred number? odd? #(> % 0)) 1 :a 3 5 7) + ;; 4 preds + true ((every-pred even? number? #(> % 0) #(<= % 12))) + true ((every-pred even? number? #(> % 0) #(<= % 12)) 2) + true ((every-pred even? number? #(> % 0) #(<= % 12)) 2 4) + true ((every-pred even? number? #(> % 0) #(<= % 12)) 2 4 6) + true ((every-pred even? number? #(> % 0) #(<= % 12)) 2 4 6 8) + true ((every-pred even? number? #(> % 0) #(<= % 12)) 2 4 6 8 10) + true ((every-pred number? even? #(> % 0) #(<= % 12)) 2 4 6 8 10 12) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4 6) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4 6 8) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4 6 8 10) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 2 4 6 8 14) + ;; 4 preds, short-circuiting + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 :a) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 3 :a) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 3 5 :a) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 3 5 7 :a) + false ((every-pred number? odd? #(> % 0) #(<= % 12)) 1 :a 3 5 7) + ;; 5 preds + true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2)))) + true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2) + true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4) + true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6) + true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8) + true ((every-pred even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10) + true ((every-pred number? even? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10 12) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 13) + ;; 5 preds, short-circuiting + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 :a) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 :a) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 5 :a) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 5 7 :a) + false ((every-pred number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 :a 3 5 7) + ;; truthiness + true (reduce #(and % %2) + (for [i (range 1 25)] + (apply (apply every-pred (repeat i identity)) + (range i)))))) + +; some-fn + +(deftest test-some-fn + (are [result] (identity result) + ;; 1 pred + (not ((some-fn even?))) + ((some-fn even?) 2) + ((some-fn even?) 2 4) + ((some-fn even?) 2 4 6) + ((some-fn even?) 2 4 6 8) + ((some-fn even?) 2 4 6 8 10) + (not ((some-fn odd?) 2)) + (not ((some-fn odd?) 2 4)) + (not ((some-fn odd?) 2 4 6)) + (not ((some-fn odd?) 2 4 6 8)) + (not ((some-fn odd?) 2 4 6 8 10)) + ;; 2 preds + (not ((some-fn even? number?))) + ((some-fn even? number?) 2) + ((some-fn even? number?) 2 4) + ((some-fn even? number?) 2 4 6) + ((some-fn even? number?) 2 4 6 8) + ((some-fn even? number?) 2 4 6 8 10) + ((some-fn number? odd?) 2) + ((some-fn number? odd?) 2 4) + ((some-fn number? odd?) 2 4 6) + ((some-fn number? odd?) 2 4 6 8) + ((some-fn number? odd?) 2 4 6 8 10) + ;; 2 preds, short-circuiting + ((some-fn number? odd?) 1 :a) + ((some-fn number? odd?) 1 3 :a) + ((some-fn number? odd?) 1 3 5 :a) + ((some-fn number? odd?) 1 3 5 7 :a) + ((some-fn number? odd?) 1 :a 3 5 7) + ;; 3 preds + (not ((some-fn even? number? #(> % 0)))) + ((some-fn even? number? #(> % 0)) 2) + ((some-fn even? number? #(> % 0)) 2 4) + ((some-fn even? number? #(> % 0)) 2 4 6) + ((some-fn even? number? #(> % 0)) 2 4 6 8) + ((some-fn even? number? #(> % 0)) 2 4 6 8 10) + ((some-fn number? even? #(> % 0)) 2 4 6 8 10 12) + ((some-fn number? odd? #(> % 0)) 2) + ((some-fn number? odd? #(> % 0)) 2 4) + ((some-fn number? odd? #(> % 0)) 2 4 6) + ((some-fn number? odd? #(> % 0)) 2 4 6 8) + ((some-fn number? odd? #(> % 0)) 2 4 6 8 10) + ((some-fn number? odd? #(> % 0)) 2 4 6 8 -10) + ;; 3 preds, short-circuiting + ((some-fn number? odd? #(> % 0)) 1 :a) + ((some-fn number? odd? #(> % 0)) :a 1) + ((some-fn number? odd? #(> % 0)) 1 3 :a) + ((some-fn number? odd? #(> % 0)) :a 1 3) + ((some-fn number? odd? #(> % 0)) 1 3 5 :a) + ((some-fn number? odd? #(> % 0)) 1 :a 3 5 7) + ;; 4 preds + (not ((some-fn even? number? #(> % 0) #(<= % 12)))) + ((some-fn even? number? #(> % 0) #(<= % 12)) 2) + ((some-fn even? number? #(> % 0) #(<= % 12)) 2 4) + ((some-fn even? number? #(> % 0) #(<= % 12)) 2 4 6) + ((some-fn even? number? #(> % 0) #(<= % 12)) 2 4 6 8) + ((some-fn even? number? #(> % 0) #(<= % 12)) 2 4 6 8 10) + ((some-fn number? even? #(> % 0) #(<= % 12)) 2 4 6 8 10 12) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 2) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4 6) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4 6 8) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4 6 8 10) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 2 4 6 8 14) + ;; 4 preds, short-circuiting + ((some-fn number? odd? #(> % 0) #(<= % 12)) 1 :a) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 1 3 :a) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 1 3 5 :a) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 1 3 5 7 :a) + ((some-fn number? odd? #(> % 0) #(<= % 12)) 1 :a 3 5 7) + ;; 5 preds + (not ((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))))) + ((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2) + ((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4) + ((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6) + ((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8) + ((some-fn even? number? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10) + ((some-fn number? even? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10 12) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 10) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 2 4 6 8 13) + ;; 5 preds, short-circuiting + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 :a) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 :a) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 5 :a) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 3 5 7 :a) + ((some-fn number? odd? #(> % 0) #(<= % 12) #(zero? (rem % 2))) 1 :a 3 5 7) + ;; truthiness + (reduce #(or % %2) + (conj + (vec + (for [i (range 1 25)] + (apply (apply some-fn (repeat i (comp not boolean))) (range i)))) + true)))) + + +(deftest test-max-min-key + (are [k coll min-item max-item] (and (= min-item (apply min-key k coll)) + (= max-item (apply max-key k coll))) + count ["longest" "a" "xy" "foo" "bar"] "a" "longest" + - [5 10 15 20 25] 25 5 + #(if (neg? %) (- %) %) [-2 -1 0 1 2 3 4] 0 4 + {nil 1 false -1 true 0} [true true false nil] false nil) + (are [f k coll expected] (= expected (apply f k coll)) + min-key :x [{:x 1000} {:x 1001} {:x 1002} {:x 1000 :second true}] {:x 1000 :second true} + max-key :x [{:x 1000} {:x 999} {:x 998} {:x 1000 :second true}] {:x 1000 :second true})) + + +; Printing +; pr prn print println newline +; pr-str prn-str print-str println-str [with-out-str (vars.clj)] + +; update + +(deftest test-update + (are [result expr] (= result expr) + {:a [1 2]} (update {:a [1]} :a conj 2) + [1] (update [0] 0 inc) + ;; higher-order usage + {:a {:b 2}} (update-in {:a {:b 1}} [:a] update :b inc) + ;; missing field = nil + {:a 1 :b nil} (update {:a 1} :b identity) + ;; 4 hard-coded arities + {:a 1} (update {:a 1} :a +) + {:a 2} (update {:a 1} :a + 1) + {:a 3} (update {:a 1} :a + 1 1) + {:a 4} (update {:a 1} :a + 1 1 1) + ;; rest arity + {:a 5} (update {:a 1} :a + 1 1 1 1) + {:a 6} (update {:a 1} :a + 1 1 1 1 1))) + +(deftest test-update-vals + (let [inm (with-meta {:a 1 :b 2} {:has :meta})] + (are [result expr] (= result expr) + {:a 2 :b 3} (update-vals inm inc) + {:has :meta} (meta (update-vals inm inc)) + {0 2 2 4} (update-vals (hash-map 0 1 2 3) inc) + {0 2 2 4} (update-vals (array-map 0 1 2 3) inc) + {0 2 2 4} (update-vals (sorted-map 2 3 0 1) inc)))) + +(deftest test-update-keys + (let [inm (with-meta {:a 1 :b 2} {:has :meta})] + (are [result expr] (= result expr) + {"a" 1 "b" 2} (update-keys inm name) + {:has :meta} (meta (update-keys inm name)) + {1 1 3 3} (update-keys (hash-map 0 1 2 3) inc) + {1 1 3 3} (update-keys (array-map 0 1 2 3) inc) + {1 1 3 3} (update-keys (sorted-map 2 3 0 1) inc)))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index f677d2e87..64b51bd41 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -45,6 +45,7 @@ [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] @@ -99,6 +100,7 @@ 'cljs.map-entry-test 'cljs.metadata-test 'cljs.npm-deps-test + 'cljs.other-functions-test 'cljs.pprint-test 'cljs.predicates-test 'cljs.syntax-quote-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index aabe60478..866dfa074 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -305,6 +305,7 @@ [cljs.map-entry-test] [cljs.set-equiv-test] [cljs.syntax-quote-test] + [cljs.other-functions-test] [cljs.predicates-test] [cljs.test-test] [static.core-test] @@ -355,6 +356,7 @@ 'cljs.map-entry-test 'cljs.set-equiv-test 'cljs.syntax-quote-test + 'cljs.other-functions-test 'cljs.predicates-test 'cljs.test-test 'static.core-test From f3e0bb503333bb53c4312d4bc19aa24505185667 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 6 Feb 2022 22:31:05 -0500 Subject: [PATCH 3800/4033] CLJS-3364: Typo in docstring of test-vars --- src/main/cljs/cljs/test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/test.cljs b/src/main/cljs/cljs/test.cljs index a6aa186c3..0ff5dff40 100644 --- a/src/main/cljs/cljs/test.cljs +++ b/src/main/cljs/cljs/test.cljs @@ -587,7 +587,7 @@ (group-by (comp :ns meta) vars))) (defn test-vars - "Groups vars by their namespace and runs test-vars on them with + "Groups vars by their namespace and runs test-var on them with appropriate fixtures assuming they are present in the current testing environment." [vars] From 8b16b44364d045d3f3638b006bcfb68b50a7a180 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 Apr 2022 12:32:25 -0400 Subject: [PATCH 3801/4033] CLJS-3288: selfhost: *eval-fn* not bound for :js sources (#163) When *load-fn* returns asynchronously, the dynamic var *eval-fn* is no longer bound. For :clj sources it is then re-bound inside eval-str*, but nothing equivalent happens for :js sources, which leads to the error No eval-fn set. This patch reads :*eval-fn* from bound-vars. Co-authored-by: Matthew Huebert --- src/main/cljs/cljs/js.cljs | 4 ++-- src/test/self/self_host/test.cljs | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 22db97868..076350951 100644 --- a/src/main/cljs/cljs/js.cljs +++ b/src/main/cljs/cljs/js.cljs @@ -838,12 +838,12 @@ ns-name (:def-emits-var opts)) (cb (try - {:ns ns-name :value (*eval-fn* {:source (.toString sb)})} + {:ns ns-name :value ((:*eval-fn* bound-vars) {:source (.toString sb)})} (catch :default cause (wrap-error (ana/error aenv "ERROR" cause))))))))) (let [src (with-out-str (comp/emit ast))] (cb (try - {:value (*eval-fn* {:source src})} + {:value ((:*eval-fn* bound-vars) {:source src})} (catch :default cause (wrap-error (ana/error aenv "ERROR" cause))))))))))))) diff --git a/src/test/self/self_host/test.cljs b/src/test/self/self_host/test.cljs index 285383b26..345404b4f 100644 --- a/src/test/self/self_host/test.cljs +++ b/src/test/self/self_host/test.cljs @@ -1592,6 +1592,31 @@ (is (some? error)) (inc! l)))))) +(deftest test-cljs-3288 + (async done + (let [st (cljs/empty-state) + l (latch 2 done) + load (fn [_ cb] (js/setTimeout #(cb {:lang :js :source ""}) 0))] + (cljs/eval st + '(require 'bootstrap-test.js-source) + {:ns 'cljs.user + :target :nodejs + :eval node-eval + :load load} + (fn [{:as res :keys [error]}] + (is (nil? error)) + (inc! l))) + (cljs/eval-str st + "(require 'bootstrap-test.js-source)" + nil + {:ns 'cljs.user + :target :nodejs + :eval node-eval + :load load} + (fn [{:as res :keys [error]}] + (is (nil? error)) + (inc! l)))))) + (defn -main [& args] (run-tests)) From 6e0bd78b62e3d72874614a4634d82c19e9dfd66d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 Apr 2022 14:20:47 -0400 Subject: [PATCH 3802/4033] CLJS-3347: Create clojure.math namespace (#124) * CLJS-3347: Create clojure.math namespace This introduces the cljs.math namespace and clojure.core/abs for parity with Clojure 1.11.0-alpha4. Also introduces clojure.math-tests, which is an equivalent sets of ClojureScript tests to the associated tests in Clojure. Also include the Clojure test clojure.gen-math-test to compare all reimplemented functions between the JVM versions and the equivalent function calls executed on a ClojureScript prepl. * Cleaned up references to Java Double in docs Co-authored-by: Paula Gearon --- src/main/cljs/cljs/core.cljs | 5 + src/main/cljs/cljs/math.cljs | 869 ++++++++++++++++++++++++ src/test/cljs/clojure/gen_math_test.clj | 285 ++++++++ src/test/cljs/clojure/math_test.cljs | 318 +++++++++ src/test/cljs/test_runner.cljs | 2 + src/test/self/self_parity/test.cljs | 2 + 6 files changed, 1481 insertions(+) create mode 100644 src/main/cljs/cljs/math.cljs create mode 100644 src/test/cljs/clojure/gen_math_test.clj create mode 100644 src/test/cljs/clojure/math_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f85e285fe..d06e19a8d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2712,6 +2712,11 @@ reduces them without incurring seq initialization" "Returns a number one less than num." [x] (- x 1)) +(defn ^number abs + {:doc "Returns the absolute value of a." + :added "1.11.10"} + [a] (Math/abs a)) + (defn ^number max "Returns the greatest of the nums." ([x] x) diff --git a/src/main/cljs/cljs/math.cljs b/src/main/cljs/cljs/math.cljs new file mode 100644 index 000000000..1d6c4ff20 --- /dev/null +++ b/src/main/cljs/cljs/math.cljs @@ -0,0 +1,869 @@ +(ns ^{:doc "ClojureScript wrapper functions for math operations" + :author "Paula Gearon" } + cljs.math) + +(def + ^{:doc "Constant for Euler's number e, the base for natural logarithms. + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/E" + :added "1.11.10" + :tag number + :const true} E Math/E) + +(def + ^{:doc "Constant for pi, the ratio of the circumference of a circle to its diameter. + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/PI" + :added "1.11.10" + :tag number + :const true} PI Math/PI) + +(def + ^{:doc "Constant used to convert an angular value in degrees to the equivalent in radians" + :private true + :added "1.11.10" + :const true} DEGREES-TO-RADIANS 0.017453292519943295) + +(def + ^{:doc "Constant used to convert an angular value in radians to the equivalent in degrees" + :private true + :added "1.11.10" + :const true} RADIANS-TO-DEGREES 57.29577951308232) + +(def ^{:private true :const true} TWO-TO-THE-52 0x10000000000000) + +(def ^{:private true :const true} SIGNIFICAND-WIDTH32 21) + +(def ^{:private true :const true} EXP-BIAS 1023) + +(def ^{:private true :const true} EXP-BITMASK32 0x7FF00000) + +(def ^{:private true :const true} EXP-MAX EXP-BIAS) + +(def ^{:private true :const true} EXP-MIN -1022) + +;; js/Number.MIN_VALUE has a bit representation of 0x0000000000000001 + +;; js/Number.MAX_VALUE has a bit representation of 0x7FEFFFFFFFFFFFFF + +(defn- get-little-endian + "Tests the platform for endianness. Returns true when little-endian, false otherwise." + [] + (let [a (js/ArrayBuffer. 4) + i (js/Uint32Array. a) + b (js/Uint8Array. a)] + (aset i 0 0x33221100) + (zero? (aget b 0)))) + +(defonce ^:private little-endian? (get-little-endian)) + +;; the HI and LO labels are terse to reflect the C macros they represent +(def ^{:private true :doc "offset of hi integers in 64-bit values"} HI (if little-endian? 1 0)) + +(def ^{:private true :doc "offset of hi integers in 64-bit values"} LO (- 1 HI)) + +(def ^{:private true :const true} INT32-MASK 0xFFFFFFFF) + +(def ^{:private true :const true} INT32-NON-SIGN-BIT 0x80000000) + +(def ^{:private true :const true} INT32-NON-SIGN-BITS 0x7FFFFFFF) + +(defn u< + {:doc "unsigned less-than comparator for 32-bit values" + :private true} + [a b] + ;; compare the top nybble + (let [ab (unsigned-bit-shift-right a 28) + bb (unsigned-bit-shift-right b 28)] + (or (< ab bb) ;; if the top nybble of a is less then the whole value is less + (and (== ab bb) ;; if the top nybble is equal then compare the remaining bits of both + (< (bit-and a 0x0fffffff) (bit-and b 0x0fffffff)))))) + +(defn ^number sin + {:doc "Returns the sine of an angle. + If a is ##NaN, ##-Inf, ##Inf => ##NaN + If a is zero => zero with the same sign as a + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin" + :added "1.11.10"} + [a] (Math/sin a)) + +(defn ^number cos + {:doc "Returns the cosine of an angle. + If a is ##NaN, ##-Inf, ##Inf => ##NaN + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos" + :added "1.11.10"} + [a] (Math/cos a)) + +(defn ^number tan + {:doc "Returns the tangent of an angle. + If a is ##NaN, ##-Inf, ##Inf => ##NaN + If a is zero => zero with the same sign as a + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan" + :added "1.11.10"} + [a] (Math/tan a)) + +(defn ^number asin + {:doc "Returns the arc sine of an angle, in the range -pi/2 to pi/2. + If a is ##NaN or |a|>1 => ##NaN + If a is zero => zero with the same sign as a + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin" + :added "1.11.10"} + [a] (Math/asin a)) + +(defn ^number acos + {:doc "Returns the arc cosine of a, in the range 0.0 to pi. + If a is ##NaN or |a|>1 => ##NaN + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos" + :added "1.11.10"} + [a] (Math/acos a)) + +(defn ^number atan + {:doc "Returns the arc tangent of a, in the range of -pi/2 to pi/2. + If a is ##NaN => ##NaN + If a is zero => zero with the same sign as a + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan" + :added "1.11.10"} + [a] (Math/atan a)) + +(defn ^number to-radians + {:doc "Converts an angle in degrees to an approximate equivalent angle in radians. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toRadians-double-" + :added "1.11.10"} + [deg] + (* deg DEGREES-TO-RADIANS)) + +(defn ^number to-degrees + {:doc "Converts an angle in radians to an approximate equivalent angle in degrees. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toDegrees-double-" + :added "1.11.10"} + [r] + (* r RADIANS-TO-DEGREES)) + +(defn ^number exp + {:doc "Returns Euler's number e raised to the power of a. + If a is ##NaN => ##NaN + If a is ##Inf => ##Inf + If a is ##-Inf => +0.0 + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp" + :added "1.11.10"} + [a] (Math/exp a)) + +(defn ^number log + {:doc "Returns the natural logarithm (base e) of a. + If a is ##NaN or negative => ##NaN + If a is ##Inf => ##Inf + If a is zero => ##-Inf + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log" + :added "1.11.10"} + [a] (Math/log a)) + +(defn ^number log10 + {:doc "Returns the logarithm (base 10) of a. + If a is ##NaN or negative => ##NaN + If a is ##Inf => ##Inf + If a is zero => ##-Inf + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10" + :added "1.11.10"} + [a] (Math/log10 a)) + +(defn ^number sqrt + {:doc "Returns the positive square root of a. + If a is ##NaN or negative => ##NaN + If a is ##Inf => ##Inf + If a is zero => a + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt" + :added "1.11.10"} + [a] (Math/sqrt a)) + +(defn ^number cbrt + {:doc "Returns the cube root of a. + If a is ##NaN => ##NaN + If a is ##Inf or ##-Inf => a + If a is zero => zero with sign matching a + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt" + :added "1.11.10"} + [a] (Math/cbrt a)) + +(defn ^number fabs + {:doc "Internal function to convert doubles to absolute values. + This duplicates the C implementations in Java, in case there is are corner-case differences." + :private true + :added "1.11.10"} + [x] + ;; create a buffer large enough for a double + (let [a (js/ArrayBuffer. 8) + ;; represent the buffer as a double array + d (js/Float64Array. a) + ;; represent the buffer as 32 bit ints + i (js/Uint32Array. a) + hi (if little-endian? 1 0)] + ;; insert the double value into the buffer + (aset d 0 x) + ;; update the sign bit + (aset i hi (bit-and (aget i hi) INT32-NON-SIGN-BITS)) + ;; return the new double + (aget d 0))) + +(def ^{:private true} Zero + ;; a buffer that can hold a pair of 64 bit doubles + (let [a (js/ArrayBuffer. 16) + ;; represent the buffer as a 2 double array + d (js/Float64Array. a) + ;; represent the buffer as an array of bytes + b (js/Uint8Array. a)] + ;; initialize both doubles to 0.0 + (aset d 0 0.0) + (aset d 1 0.0) + ;; update the sign bit on the second double + (aset b (if little-endian? 15 8) -0x80) + ;; save the array of 2 doubles [0.0, -0.0] + d)) + +(def ^{:private true :const true} xpos 0) +(def ^{:private true :const true} ypos 1) +(def ^{:private true} HI-x (+ (* 2 xpos) HI)) +(def ^{:private true} LO-x (+ (* 2 xpos) LO)) +(def ^{:private true} HI-y (+ (* 2 ypos) HI)) +(def ^{:private true} LO-y (+ (* 2 ypos) LO)) + +(defn ^number ilogb + {:doc "internal function for ilogb(x)" + :private true} + [hx lx] + (if (< hx 0x00100000) ;; subnormal + (let [hx-zero? (zero? hx) + start-ix (if hx-zero? -1043 -1022) + start-i (if hx-zero? lx (bit-shift-left hx 11))] + (loop [ix start-ix i start-i] + (if-not (> i 0) + ix + (recur (dec ix) (bit-shift-left i 1))))) + (- (bit-shift-right hx 20) 1023))) + +(defn ^number setup-hl + {:doc "internal function to setup and align integer words" + :private true} + [i h l] + (if (>= i -1022) + [(bit-or 0x00100000 (bit-and 0x000fffff h)) l] + (let [n (- -1022 i)] + (if (<= n 31) + [(bit-or (bit-shift-left h n) (unsigned-bit-shift-right l (- 32 n))) (bit-shift-left l n)] + [(bit-shift-left l (- n 32)) 0])))) + +(defn ^number IEEE-fmod + {:doc "Return x mod y in exact arithmetic. Method: shift and subtract. + Reimplements __ieee754_fmod from the JDK. + Ported from: https://github.com/openjdk/jdk/blob/master/src/java.base/share/native/libfdlibm/e_fmod.c + bit-shift-left and bit-shift-right convert numbers to signed 32-bit + Fortunately the values that are shifted are expected to be 32 bit signed." + :private true} + [x y] + ;; return exception values + (if (or (zero? y) ^boolean (js/isNaN y) (not ^boolean (js/isFinite x))) + ##NaN + + ;; create a buffer large enough for 2 doubles + (let [a (js/ArrayBuffer. 16) + ;; represent the buffer as a double array + d (js/Float64Array. a) + ;; represent the buffer as 32 bit ints + i (js/Uint32Array. a) + ;; set the doubles to x and y + _ (aset d xpos x) + _ (aset d ypos y) + hx (aget i HI-x) + lx (aget i LO-x) + hy (aget i HI-y) + ly (aget i LO-y) + sx (bit-and hx INT32-NON-SIGN-BIT) ;; capture the sign of x + hx (bit-and hx INT32-NON-SIGN-BITS) ;; set x to |x| + hy (bit-and hy INT32-NON-SIGN-BITS) ;; set y to |y| + hx<=hy (<= hx hy)] + (cond + ;; additional exception values + (and hx<=hy (or (< hx hy) (< lx ly))) x ;; |x|<|y| return x + (and hx<=hy (== lx ly)) (aget Zero (unsigned-bit-shift-right sx 31)) ;; |x|=|y| return x*0 + + :default + ;; determine ix = ilogb(x), iy = ilogb(y) + (try + (let [ix (ilogb hx lx) + iy (ilogb hy ly) + ;; set up {hx,lx}, {hy,ly} and align y to x + [hx lx] (setup-hl ix hx lx) + [hy ly] (setup-hl iy hy ly) + ;; fix point fmod + [hx lx] (loop [n (- ix iy) hx hx lx lx] + (if (zero? n) + [hx lx] + (let [hz (if (u< lx ly) (- hx hy 1) (- hx hy)) + lz (- lx ly) + [hx lx] (if (< hz 0) + [(+ hx hx (unsigned-bit-shift-right lx 31)) (+ lx lx)] + (if (zero? (bit-or hz lz)) + (throw (ex-info "Signed zero" {:zero true})) + [(+ hz hz (unsigned-bit-shift-right lz 31)) (+ lz lz)]))] + (recur (dec n) (bit-and INT32-MASK hx) (bit-and INT32-MASK lx))))) + hz (if (u< lx ly) (- hx hy 1) (- hx hy)) + lz (- lx ly) + [hx lx] (if (>= hz 0) [hz lz] [hx lx]) + + _ (when (zero? (bit-or hx lx)) + (throw (ex-info "Signed zero" {:zero true}))) + ;; convert back to floating value and restore the sign + [hx lx iy] (loop [hx hx lx lx iy iy] + (if-not (< hx 0x00100000) + [hx lx iy] + (recur (+ hx hx (unsigned-bit-shift-right lx 31)) (+ lx lx) (dec iy))))] + ;; use these high and low ints to update the double and return it + (if (>= iy -1022) + (let [hx (bit-or (- hx 0x00100000) (bit-shift-left (+ iy 1023) 20))] + (aset i HI-x (bit-or hx sx)) + (aset i LO-x lx) + (aget d xpos)) + (let [n (- -1022 iy) + [hx lx] (cond + (<= n 20) [(bit-shift-right hx n) + (bit-or (unsigned-bit-shift-right lx n) (bit-shift-left hx (- 32 n)))] + (<= n 31) [sx + (bit-or (bit-shift-left hx (- 32 n)) (unsigned-bit-shift-right lx n))] + :default [sx (bit-shift-right hx (- n 32))])] + (aset i HI-x (bit-or hx sx)) + (aset i LO-x lx) + (* (aget d xpos) 1.0)))) + (catch :default _ (aget Zero (unsigned-bit-shift-right sx 31)))))))) + +(defn ^number IEEE-remainder + {:doc "Returns the remainder per IEEE 754 such that + remainder = dividend - divisor * n + where n is the integer closest to the exact value of dividend / divisor. + If two integers are equally close, then n is the even one. + If the remainder is zero, sign will match dividend. + If dividend or divisor is ##NaN, or dividend is ##Inf or ##-Inf, or divisor is zero => ##NaN + If dividend is finite and divisor is infinite => dividend + + Method: based on fmod return x-[x/p]chopped*p exactlp. + Ported from: https://github.com/openjdk/jdk/blob/master/src/java.base/share/native/libfdlibm/e_remainder.c + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#IEEEremainder-double-double-" + :added "1.11.10"} + [dividend divisor] + ;; check for exception values + (cond + (zero? divisor) ##NaN + ^boolean (js/isNaN divisor) ##NaN + ;; check if dividend is ##Inf ##-Inf or ##NaN + ^boolean (js/isNaN dividend) ##NaN + (not ^boolean (js/isFinite dividend)) ##NaN + ;; dividend is finish, check if divisor is infinite + (not ^boolean (js/isFinite divisor)) dividend + + :default + ;; create a buffer large enough for 2 doubles + (let [a (js/ArrayBuffer. 16) + ;; represent the buffer as a double array + d (js/Float64Array. a) + ;; represent the buffer as 32 bit ints + i (js/Uint32Array. a)] + (aset d 0 dividend) + (aset d 1 divisor) + ;; x gets the dividend high and low ints + (let [hx (aget i HI) + lx (aget i LO) + ;; p gets the divisor high and low ints + hp (aget i (+ HI 2)) + lp (aget i (+ LO 2)) + ;; sx is the sign bit + sx (bit-and hx INT32-NON-SIGN-BIT) + ;; strip the sign bit from hp and hx + hp (bit-and hp INT32-NON-SIGN-BITS) + hx (bit-and hx INT32-NON-SIGN-BITS) + + ;;make x < 2p + dividend (if (<= hp 0x7FDFFFFF) (IEEE-fmod dividend (+ divisor divisor)) dividend)] + (if (zero? (bit-or (- hx hp) (- lx lp))) + (* 0.0 dividend) + ;; convert dividend and divisor to absolute values. + (let [dividend (Math/abs dividend) + divisor (Math/abs divisor) + ;; reduce dividend within range of the divisor + dividend (if (< hp 0x00200000) + ;; smaller divisor compare 2*dividend to the divisor + (if (> (+ dividend dividend) divisor) + (let [dividend (- dividend divisor)] ;; reduce the dividend + (if (>= (+ dividend dividend) divisor) ;; 2*dividend still larger + (- dividend divisor) ;; reduce again + dividend)) + dividend) + ;; compare dividend to half the divisor + (let [divisor-half (* 0.5 divisor)] + (if (> dividend divisor-half) + (let [dividend (- dividend divisor)] ;; reduce the dividend + (if (>= dividend divisor-half) ;; still larger than half divisor + (- dividend divisor) ;; reduce again + dividend)) + dividend)))] + ;; update the buffer with the new dividend value + (aset d 0 dividend) + ;; calculate a new hi int for the dividend using the saved sign bit + (let [hx (bit-xor (aget i HI) sx)] + ;; set the dividend with this new sign bit + (aset i HI hx) + ;; retrieve the updated dividend + (aget d 0)))))))) + +(defn ^number ceil + {:doc "Returns the smallest double greater than or equal to a, and equal to a + mathematical integer. + If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a + Note that if a is `nil` then an exception will be thrown. This matches Clojure, rather than js/Math.ceil + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil" + :added "1.11.10"} + [a] + (if (some? a) + (Math/ceil a) + (throw (ex-info "Unexpected Null passed to ceil" {:fn "ceil"})))) + +(defn ^number floor + {:doc "Returns the largest double less than or equal to a, and equal to a + mathematical integer. + If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a + If a is less than zero but greater than -1.0 => -0.0 + Note that if a is `nil` then an exception will be thrown. This matches Clojure, rather than js/Math.floor + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor" + :added "1.11.10"} + [a] + (if (some? a) + (Math/floor a) + (throw (ex-info "Unexpected Null passed to floor" {:fn "floor"})))) + +(defn ^number copy-sign + {:doc "Returns a double with the magnitude of the first argument and the sign of + the second. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#copySign-double-double-" + :added "1.11.10"} + [magnitude sign] + ;; create a buffer large enough for 2 doubles + (let [a (js/ArrayBuffer. 16) + ;; represent the buffer as a double array + d (js/Float64Array. a) + ;; represent the buffer as bytes + b (js/Uint8Array. a) + ;; find the offset of the byte that holds the sign bit + sbyte (if little-endian? 7 0)] + ;; the first double holds the magnitude, the second holds the sign value + (aset d 0 magnitude) + (aset d 1 sign) + ;; read the sign bit from the sign value + (let [sign-sbyte (bit-and 0x80 (aget b (+ 8 sbyte))) + ;; read all the bits that aren't the sign bit in the same byte of the magnitude + mag-sbyte (bit-and 0x7F (aget b sbyte))] + ;; combine the sign bit from the sign value and the non-sign-bits from the magnitude value + ;; write it back into the byte in the magnitude + (aset b sbyte (bit-or sign-sbyte mag-sbyte)) + ;; retrieve the full magnitude value with the updated byte + (aget d 0)))) + +(defn ^number rint + {:doc "Returns the double closest to a and equal to a mathematical integer. + If two values are equally close, return the even one. + If a is ##NaN or ##Inf or ##-Inf or zero => a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#rint-double-" + :added "1.11.10"} + [a] + (let [sign (copy-sign 1.0, a) + a (Math/abs a) + a (if (< a TWO-TO-THE-52) + (- (+ TWO-TO-THE-52 a) TWO-TO-THE-52) a)] + (* sign a))) + +(defn ^number atan2 + {:doc "Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta). + Computes the phase theta by computing an arc tangent of y/x in the range of -pi to pi. + For more details on special cases, see: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan" + :added "1.11.10"} + [y x] (Math/atan2 y x)) + +(defn ^number pow + {:doc "Returns the value of a raised to the power of b. + For more details on special cases, see: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow" + :added "1.11.10"} + [a b] (Math/pow a b)) + +(defn ^number round + {:doc "Returns the closest long to a. If equally close to two values, return the one + closer to ##Inf. + If a is ##NaN => 0 + If a is ##-Inf => js/Number.MIN_SAFE_INTEGER + If a is ##Inf => js/Number.MAX_SAFE_INTEGER + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round" + :added "1.11.10"} + [a] + (cond + ^boolean (js/isNaN a) 0 + ^boolean (js/isFinite a) (Math/round a) + (== ##Inf a) js/Number.MAX_SAFE_INTEGER + :default js/Number.MIN_SAFE_INTEGER)) + +(defn ^number random + {:doc "Returns a positive double between 0.0 and 1.0, chosen pseudorandomly with + approximately random distribution. Not cryptographically secure. The seed is chosen internally + and cannot be selected. + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random" + :added "1.11.10"} + [] (Math/random)) + +(defn ^number add-exact + {:doc "Returns the sum of x and y, throws an exception on overflow. " + :added "1.11.10"} + [x y] + (let [r (clojure.core/+ x y)] + (if (or (> r js/Number.MAX_SAFE_INTEGER) (< r js/Number.MIN_SAFE_INTEGER)) + (throw (ex-info "Integer overflow" {:fn "add-exact"})) + r))) + +(defn ^number subtract-exact + {:doc "Returns the difference of x and y, throws ArithmeticException on overflow. " + :added "1.11.10"} + [x y] + (let [r (- x y)] + (if (or (> r js/Number.MAX_SAFE_INTEGER) (< r js/Number.MIN_SAFE_INTEGER)) + (throw (ex-info "Integer overflow" {:fn "subtract-exact"})) + r))) + +(defn ^number multiply-exact + {:doc "Returns the product of x and y, throws ArithmeticException on overflow. " + :added "1.11.10"} + [x y] + (let [r (* x y)] + (if (or (> r js/Number.MAX_SAFE_INTEGER) (< r js/Number.MIN_SAFE_INTEGER)) + (throw (ex-info "Integer overflow" {:fn "multiply-exact"})) + r))) + +(defn ^number increment-exact + {:doc "Returns a incremented by 1, throws ArithmeticException on overflow." + :added "1.11.10"} + [a] + (if (or (>= a js/Number.MAX_SAFE_INTEGER) (< a js/Number.MIN_SAFE_INTEGER)) + (throw (ex-info "Integer overflow" {:fn "increment-exact"})) + (inc a))) + +(defn ^number decrement-exact + {:doc "Returns a decremented by 1, throws ArithmeticException on overflow. " + :added "1.11.10"} + [a] + (if (or (<= a js/Number.MIN_SAFE_INTEGER) (> a js/Number.MAX_SAFE_INTEGER)) + (throw (ex-info "Integer overflow" {:fn "decrement-exact"})) + (dec a))) + +(defn ^number negate-exact + {:doc "Returns the negation of a, throws ArithmeticException on overflow. " + :added "1.11.10"} + [a] + (if (or (> a js/Number.MAX_SAFE_INTEGER) (< a js/Number.MIN_SAFE_INTEGER)) + (throw (ex-info "Integer overflow" {:fn "negate-exact"})) + (- a))) + +(defn- xor + [^boolean a ^boolean b] + (or (and a (not b)) (and (not a) b))) + +(defn ^number floor-div + {:doc "Integer division that rounds to negative infinity (as opposed to zero). + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorDiv-long-long-" + :added "1.11.10"} + [x y] + (if-not (and ^boolean (js/Number.isSafeInteger x) ^boolean (js/Number.isSafeInteger y)) + (throw (ex-info "floor-div called with non-safe-integer arguments" + {:x-int? (js/Number.isSafeInteger x) :y-int? (js/Number.isSafeInteger y)})) + (let [r (long (/ x y))] + (if (and (xor (< x 0) (< y 0)) (not (== (* r y) x))) + (dec r) + r)))) + +(defn ^number floor-mod + {:doc "Integer modulus x - (floorDiv(x, y) * y). Sign matches y and is in the + range -|y| < r < |y|. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorMod-long-long-" + :added "1.11.10"} + [x y] + (if-not (and ^boolean (js/Number.isSafeInteger x) ^boolean (js/Number.isSafeInteger y)) + (throw (ex-info "floor-mod called with non-safe-integer arguments" + {:x-int? (js/Number.isSafeInteger x) :y-int? (js/Number.isSafeInteger y)})) + ;; this avoids using floor-div to keep within the safe integer range + (let [r (long (/ x y))] + (if (and (xor (< x 0) (< y 0)) (not (== (* r y) x))) + (- x (* y r) (- y)) + (- x (* y r)))))) + +(defn ^number get-exponent + {:doc "Returns the exponent of d. + If d is ##NaN, ##Inf, ##-Inf => max_Float64_exponent + 1 + If d is zero or subnormal => min_Float64_exponent - 1 + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#getExponent-double-" + :added "1.11.10"} + [d] + (cond + (or ^boolean (js/isNaN d) (not ^boolean (js/isFinite d))) (inc EXP-MAX) + (zero? d) (dec EXP-MIN) + :default (let [a (js/ArrayBuffer. 8) + f (js/Float64Array. a) + i (js/Uint32Array. a) + hi (if little-endian? 1 0)] + (aset f 0 d) + (- (bit-shift-right (bit-and (aget i hi) EXP-BITMASK32) (dec SIGNIFICAND-WIDTH32)) EXP-BIAS)))) + +(defn ^number hi-lo->double + {:doc "Converts a pair of 32 bit integers into an IEEE-754 64 bit floating point number. + h is the high 32 bits, l is the low 32 bits." + :private true} + [h l] + (let [a (js/ArrayBuffer. 8) + f (js/Float64Array. a) + i (js/Uint32Array. a)] + (aset i LO l) + (aset i HI h) + (aget f 0))) + +(defn ^number power-of-two + {:doc "returns a floating point power of two in the normal range" + :private true} + [n] + (assert (and (>= n EXP-MIN) (<= n EXP-MAX))) + (hi-lo->double + (bit-and (bit-shift-left (+ n EXP-BIAS) (dec SIGNIFICAND-WIDTH32)) EXP-BITMASK32) 0)) + +(defn ^number ulp + {:doc "Returns the size of an ulp (unit in last place) for d. + If d is ##NaN => ##NaN + If d is ##Inf or ##-Inf => ##Inf + If d is zero => Number/MIN_VALUE + If d is +/- Number/MAX_VALUE => 2^971 + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ulp-double-" + :added "1.11.10"} + [d] + (cond + ^boolean (js/isNaN d) d + ^boolean (js/isFinite d) + (let [e (get-exponent d)] + (case e + 1024 (Math/abs d) ;; EXP-MAX + 1 + -1023 js/Number.MIN_VALUE ;; EXP-MIN - 1 + (let [e (- e (+ 31 SIGNIFICAND-WIDTH32))] ;; SIGNIFICAND_WIDTH64 -1 + (if (>= e EXP-MIN) + (power-of-two e) + (let [shift (- e (- EXP-MIN 31 SIGNIFICAND-WIDTH32))] + (if (< shift 32) + (hi-lo->double 0 (bit-shift-left 1 shift)) + (hi-lo->double (bit-shift-left 1 (- shift 32)) 0))))))) + :default ##Inf)) + +(defn ^number signum + {:doc "Returns the signum function of d - zero for zero, 1.0 if >0, -1.0 if <0. + If d is ##NaN => ##NaN + If d is ##Inf or ##-Inf => sign of d + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#signum-double-" + :added "1.11.10"} + [d] + (if (or (zero? d) ^boolean (js/isNaN d)) + d + (copy-sign 1.0 d))) + +(defn ^number sinh + {:doc "Returns the hyperbolic sine of x, (e^x - e^-x)/2. + If x is ##NaN => ##NaN + If x is ##Inf or ##-Inf or zero => x + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh" + :added "1.11.10"} + [x] (Math/sinh x)) + +(defn ^number cosh + {:doc "Returns the hyperbolic cosine of x, (e^x + e^-x)/2. + If x is ##NaN => ##NaN + If x is ##Inf or ##-Inf => ##Inf + If x is zero => 1.0 + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh" + :added "1.11.10"} + [x] (Math/cosh x)) + +(defn ^number tanh + {:doc "Returns the hyperbolic tangent of x, sinh(x)/cosh(x). + If x is ##NaN => ##NaN + If x is zero => zero, with same sign + If x is ##Inf => +1.0 + If x is ##-Inf => -1.0 + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh" + :added "1.11.10"} + [x] (Math/tanh x)) + +(defn ^number hypot + {:doc "Returns sqrt(x^2 + y^2) without intermediate underflow or overflow. + If x or y is ##Inf or ##-Inf => ##Inf + If x or y is ##NaN and neither is ##Inf or ##-Inf => ##NaN + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot" + :added "1.11.10"} + [x y] (Math/hypot x y)) + +(defn ^number expm1 + {:doc "Returns e^x - 1. Near 0, expm1(x)+1 is more accurate to e^x than exp(x). + If x is ##NaN => ##NaN + If x is ##Inf => #Inf + If x is ##-Inf => -1.0 + If x is zero => x + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1" + :added "1.11.10"} + [x] (Math/expm1 x)) + +(defn ^number log1p + {:doc "Returns ln(1+x). For small values of x, log1p(x) is more accurate than + log(1.0+x). + If x is ##NaN or ##-Inf or < -1 => ##NaN + If x is -1 => ##-Inf + If x is ##Inf => ##Inf + See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p" + :added "1.11.10"} + [x] (Math/log1p x)) + +(defn ^number add64 + {:doc "Takes the high and low words for 2 different 64 bit integers, and adds them. + This handles overflow from the low-order words into the high order words." + :private true} + [hx lx hy ly] + (let [sx (unsigned-bit-shift-right (bit-and lx INT32-NON-SIGN-BIT) 31) + sy (unsigned-bit-shift-right (bit-and ly INT32-NON-SIGN-BIT) 31) + lr (+ (bit-and INT32-NON-SIGN-BITS lx) (bit-and INT32-NON-SIGN-BITS ly)) + c31 (unsigned-bit-shift-right (bit-and lr INT32-NON-SIGN-BIT) 31) + b31 (+ sx sy c31) + lr (bit-or (bit-and lr INT32-NON-SIGN-BITS) (bit-shift-left b31 31)) + c32 (bit-shift-right b31 1) + hr (bit-and INT32-MASK (+ hx hy c32))] + [hr lr])) + +(defn ^number next-after + {:doc "Returns the adjacent floating point number to start in the direction of + the second argument. If the arguments are equal, the second is returned. + If either arg is #NaN => #NaN + If both arguments are signed zeros => direction + If start is +-Number/MIN_VALUE and direction would cause a smaller magnitude + => zero with sign matching start + If start is ##Inf or ##-Inf and direction would cause a smaller magnitude + => Number/MAX_VALUE with same sign as start + If start is equal to +=Number/MAX_VALUE and direction would cause a larger magnitude + => ##Inf or ##-Inf with sign matching start + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextAfter-double-double-" + :added "1.11.10"} + [start direction] + ; Branch to descending case first as it is more costly than ascending + ; case due to start != 0.0f conditional. + (let [a (js/ArrayBuffer. 8) + f (js/Float64Array. a) + i (js/Uint32Array. a)] + (cond + (> start direction) (if-not (zero? start) + (let [_ (aset f 0 start) + ht (aget i HI) + lt (aget i LO) + ;; ht< != 0 since start != 0.0 + ;; So long as the top bit is not set, then whole number is > 0 + [hr lr] (if (zero? (bit-and ht INT32-NON-SIGN-BIT)) + (add64 ht lt 0xFFFFFFFF 0xFFFFFFFF) + (add64 ht lt 0 1))] + (aset i HI hr) + (aset i LO lr) + (aget f 0)) + ;; start == 0.0 && direction < 0.0 + (- js/Number.MIN_VALUE)) + ;; Add +0.0 to get rid of a -0.0 (+0.0 + -0.0 => +0.0) + ;; then bitwise convert start to integer + (< start direction) (let [_ (aset f 0 (+ start 0.0)) + ht (aget i HI) + lt (aget i LO) + [hr lr] (if (zero? (bit-and ht INT32-NON-SIGN-BIT)) + (add64 ht lt 0 1) + (add64 ht lt 0xFFFFFFFF 0xFFFFFFFF))] + (aset i HI hr) + (aset i LO lr) + (aget f 0)) + (== start direction) direction + :default (+ start direction)))) ;; isNaN(start) || isNaN(direction) + +(defn ^number next-up + {:doc "Returns the adjacent double of d in the direction of ##Inf. + If d is ##NaN => ##NaN + If d is ##Inf => ##Inf + If d is zero => Number/MIN_VALUE + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextUp-double-" + :added "1.11.10"} + [d] + ;; Use a single conditional and handle the likely cases first + (if (< d js/Number.POSITIVE_INFINITY) + (let [a (js/ArrayBuffer. 8) + f (js/Float64Array. a) + i (js/Uint32Array. a) + ;; Add +0.0 to get rid of a -0.0 (+0.0 + -0.0 => +0.0) + _ (aset f 0 (+ d 0.0)) + ht (aget i HI) + lt (aget i LO) + [hr lr] (if (zero? (bit-and ht INT32-NON-SIGN-BIT)) + (add64 ht lt 0 1) + (add64 ht lt 0xFFFFFFFF 0xFFFFFFFF))] + (aset i HI hr) + (aset i LO lr) + (aget f 0)) + ;; d is NaN or +Infinity + d)) + +(defn ^number next-down + {:doc "Returns the adjacent double of d in the direction of ##-Inf. + If d is ##NaN => ##NaN + If d is ##Inf => Number/MAX_VALUE + If d is zero => -Number/MIN_VALUE + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-" + :added "1.11.10"} + [d] + (cond + (or ^boolean (js/isNaN d) (== ##-Inf d)) d + (zero? d) (- js/Number.MIN_VALUE) + :default + (let [a (js/ArrayBuffer. 8) + f (js/Float64Array. a) + i (js/Uint32Array. a) + _ (aset f 0 d) + ht (aget i HI) + lt (aget i LO) + [hr lr] (if (> d 0) + (add64 ht lt 0xFFFFFFFF 0xFFFFFFFF) + (add64 ht lt 0 1))] + (aset i HI hr) + (aset i LO lr) + (aget f 0)))) + +(def ^:private MAX_SCALE (+ EXP-MAX (- EXP-MIN) SIGNIFICAND-WIDTH32 32 1)) + +(def ^:private two-to-the-double-scale-up (power-of-two 512)) + +(def ^:private two-to-the-double-scale-down (power-of-two -512)) + +(defn ^number scalb + {:doc "Returns d * 2^scaleFactor, scaling by a factor of 2. If the exponent + is between min_Float64_exponent and max_Float64_exponent. + scaleFactor is an integer + If d is ##NaN => ##NaN + If d is ##Inf or ##-Inf => ##Inf or ##-Inf respectively + If d is zero => zero of same sign as d + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-" + :added "1.11.10"} + [d scaleFactor] + (let [[scale-factor + scale-increment + exp-delta] (if (< scaleFactor 0) + [(Math/max scaleFactor (- MAX_SCALE)) -512 two-to-the-double-scale-down] + [(Math/min scaleFactor MAX_SCALE) 512 two-to-the-double-scale-up]) + ;; Calculate (scaleFactor % +/-512), 512 = 2^9 + ;; technique from "Hacker's Delight" section 10-2 + t (unsigned-bit-shift-right (bit-shift-right scale-factor 8) 23) + exp-adjust (- (bit-and (+ scale-factor t) 511) t)] + (loop [d (* d (power-of-two exp-adjust)) scale-factor (- scale-factor exp-adjust)] + (if (zero? scale-factor) + d + (recur (* d exp-delta) (- scale-factor scale-increment)))))) diff --git a/src/test/cljs/clojure/gen_math_test.clj b/src/test/cljs/clojure/gen_math_test.clj new file mode 100644 index 000000000..ee72d280b --- /dev/null +++ b/src/test/cljs/clojure/gen_math_test.clj @@ -0,0 +1,285 @@ +(ns ^{:doc "Tests clojure.math to compare between JVM provided functions and the + clojure.math implementations on a ClojureScript instance running on NodeJS. + Tests are generative, but not run through the defspec framework to minimize + i/o to the ClojureScript instance." + :authors ["Michiel Borkent" "Paula Gearon"]} + clojure.gen-math-test + (:require [cljs.core.server] + [cljs.repl.node] + [clojure.core.server :as server] + [clojure.edn :as edn] + [clojure.java.io :as io] + [clojure.test :as t :refer [deftest is]] + [clojure.test.check.clojure-test :refer [defspec]] + [clojure.test.check.generators :as gen] + [clojure.test.check.properties :as prop])) + +(def ^:const Number-MAX_SAFE_INTEGER 9007199254740991) +(def ^:const Number-MIN_SAFE_INTEGER -9007199254740991) +(defn Number-isSafeInteger + [n] + (and (>= n Number-MIN_SAFE_INTEGER) + (<= n Number-MAX_SAFE_INTEGER))) + +(def gen-small-integer + "Generates a positive or negative integer bounded by the generator's + `size` parameter. Shrinks to zero." + (gen/sized (fn [size] (gen/choose (- size) size)))) + +(def reader (atom nil)) +(def writer (atom nil)) + +(defn cljs-eval [expr] + (-> (binding [*out* @writer + *in* @reader] + (println expr) + (read-line)) + edn/read-string + :val)) + +(t/use-fixtures :once + (fn [f] + (println "Launching test pREPL.") + (let [server (server/start-server {:accept 'cljs.core.server/io-prepl + :address "127.0.0.1" + :port 0 + :name "clojure.math-repl" + :args [:repl-env (cljs.repl.node/repl-env)]}) + port (-> server (.getLocalPort))] + (println "Server opened on port" port) + (with-open [socket (java.net.Socket. "127.0.0.1" port) + rdr (io/reader socket) + wrtr (io/writer socket)] + (reset! reader rdr) + (reset! writer wrtr) + (println "Executing tests") + (cljs-eval "(require 'clojure.math)") + (f) + (println "Tearing down test pREPL."))))) + +(deftest sanity-test + (is (= "6" (cljs-eval "(+ 1 2 3)")))) + +(deftest cljs-match-sanity-test + (is (= "1" (cljs-eval "(clojure.math/cos 0.0)")))) + +(defn n== + [a b] + (or (and (Double/isNaN a) (Double/isNaN b)) + (and (number? a) (number? b) (== a b)) + (= a b))) + +(defn maxi== + [a b] + (or (and (Double/isNaN a) (Double/isNaN b)) + (and (= a Number-MAX_SAFE_INTEGER) (= b Long/MAX_VALUE)) + (and (= a Number-MIN_SAFE_INTEGER) (= b Long/MIN_VALUE)) + (and (number? a) (number? b) (== a b)) + (= a b))) + +(defmacro test-t->t + [n jfn cfn gen & [equals]] + (let [jmfn (symbol "Math" (str jfn)) + cmfn (name cfn) + eq (or equals n==)] + `(let [ds# (gen/sample ~gen ~n)] + (is (every? identity + (map ~eq + (read-string + (cljs-eval (str "(->> '" (pr-str ds#) + " (map double)" + " (map clojure.math/" ~cmfn "))"))) + (map #(~jmfn %) ds#))) + (str "data: " (pr-str ds#)))))) + +(defmacro test-double->double + [n jfn cfn & [equals]] + `(test-t->t ~n ~jfn ~cfn gen/double ~equals)) + +(defmacro test-t-t->double + [n jfn cfn gen1 gen2 & [equals]] + (let [jmfn (symbol "Math" (str jfn)) + cmfn (name cfn) + eq (or equals n==)] + `(let [ds# (gen/sample ~gen1 ~n) + ds2# (gen/sample ~gen2 ~n)] + (is (every? identity + (map ~eq + (read-string + (cljs-eval (str "(->> (map #(vector %1 %2) '" + (pr-str ds#) " '" (pr-str ds2#) ")" + " (map #(try (apply clojure.math/" ~cmfn " %) (catch :default _ :exception))))"))) + (map #(~jmfn %1 %2) ds# ds2#))) + (str "data: " (pr-str (map vector ds# ds2#))))))) + +(defmacro test-double-double->double + [n jfn cfn & [equals]] + `(test-t-t->double ~n ~jfn ~cfn gen/double gen/double ~equals)) + +(def safe-integer (gen/sized (fn [_] (gen/choose Number-MIN_SAFE_INTEGER Number-MAX_SAFE_INTEGER)))) + +(defn e== + [a b] + (or (and (number? a) (number? b) (== a b)) + (= a b))) + +(defmacro test-zlong-long->long + [n jfn cfn] + (let [jmfn (symbol "Math" (str jfn)) + cmfn (name cfn)] + `(let [lzs# (gen/sample safe-integer ~n) + ls# (gen/sample (gen/such-that #(not= % 0) safe-integer) ~n)] + (is (every? identity + (map e== + (read-string + (cljs-eval (str "(->> (map #(vector (long %1) (long %2)) '" + (pr-str lzs#) " '" (pr-str ls#) ")" + " (map #(try (apply clojure.math/" ~cmfn " %) (catch :default _ :exception))))"))) + (map #(~jmfn (long %1) (long %2)) lzs# ls#))) + (str "data: " (pr-str (map vector lzs# ls#))))))) + +;; Tests clojure.core/abs. This function has recently moved to core +(deftest abs-test + (let [ds (gen/sample gen/double 100)] + (is (every? identity + (map #(or (= (double %1) %2) (and (Double/isNaN %1) (Double/isNaN %2))) + (read-string (cljs-eval (str "(->> '" (pr-str ds) + " (map double)" + " (map abs))"))) + (map #(Math/abs %) ds))) ;; This can change to clojure.core/math after Clojure 11 + (str "data: " (pr-str ds))))) + +(def ^:const delta 1E-15) + +(defn nd== + [label a b] + (or (and (Double/isNaN a) (Double/isNaN b)) + (== a b) + (do + (println label "variance:" a "\u2260" b) + (< (Math/abs (- a b)) delta)))) + +(deftest sin-test + (test-double->double 100 sin sin #(nd== "sin()" %1 %2))) + +(deftest to-radians-test + (test-double->double 100 toRadians to-radians)) + +(deftest to-degrees-test + (test-double->double 100 toDegrees to-degrees)) + +(deftest ieee-remainder-test + (test-double-double->double 100 IEEEremainder IEEE-remainder)) + +(deftest ceil-test + (test-double->double 100 ceil ceil)) + +(deftest ceil-null-test + (is (= ":exception" (cljs-eval (str "(try (clojure.math/ceil nil) (catch :default _ :exception))"))))) + +(deftest floor-test + (test-double->double 100 floor floor)) + +(deftest floor-null-test + (is (= ":exception" (cljs-eval (str "(try (clojure.math/floor nil) (catch :default _ :exception))"))))) + +(deftest copy-sign-test + (test-double-double->double 100 copySign copy-sign)) + +(deftest rint-test + (test-double->double 100 rint rint)) + +(deftest round-test + (test-t->t 100 round round (gen/double* {:min Number-MIN_SAFE_INTEGER :max Number-MAX_SAFE_INTEGER}) maxi==)) + +(deftest floor-div-test + (test-zlong-long->long 100 floorDiv floor-div)) + +(deftest floor-mod-test + (test-zlong-long->long 100 floorMod floor-mod)) + +(deftest get-exponent-test + (test-double->double 100 getExponent get-exponent)) + +(deftest ulp-test + (test-double->double 100 ulp ulp)) + +(deftest signum-test + (test-double->double 100 signum signum)) + +(deftest next-after-test + (test-double-double->double 100 nextAfter next-after)) + +(deftest next-up-test + (test-double->double 100 nextUp next-up)) + +(deftest next-down-test + (test-double->double 100 nextDown next-down)) + +(def ^:const MAX-INT 0x7fffffff) + +(deftest scalb-test + (test-t-t->double 100 scalb scalb + gen/double + (gen/such-that + #(<= % MAX-INT) + (gen/resize (inc MAX-INT) gen-small-integer)))) + +;; utililties for the -exact tests +(def safe-integer (gen/choose Number-MIN_SAFE_INTEGER Number-MAX_SAFE_INTEGER)) + +(defn no-overflow? + [f ^long x ^long y] + (try + (Number-isSafeInteger (f x y)) + (catch ArithmeticException _ false))) + +(defmacro test-safe-safe->safe + [n jfn cfn op] + (let [jmfn (symbol "Math" (str jfn)) + cmfn (name cfn)] + `(let [ls1# (gen/sample safe-integer ~n) + ls2# (gen/sample safe-integer ~n)] + (is (every? identity + (map e== + (read-string + (cljs-eval (str "(->> (map #(vector (long %1) (long %2)) '" + (pr-str ls1#) " '" (pr-str ls2#) ")" + " (map (fn [[a b]]" + " (try (clojure.math/" ~cmfn " a b)" + " (catch :default _ :exception)))))"))) + (map #(if (no-overflow? ~op %1 %2) + (~jmfn (long %1) (long %2)) + :exception) ls1# ls2#))) + (str "data: " (pr-str (map vector ls1# ls2#))))))) + +(deftest add-exact-test + (test-safe-safe->safe 100 addExact add-exact +)) + +(deftest subtract-exact + (test-safe-safe->safe 100 subtractExact subtract-exact -)) + +(deftest multiply-exact + (test-safe-safe->safe 100 multiplyExact multiply-exact *)) + +(defmacro test-safe->safe + [n jfn cfn op] + (let [jmfn (symbol "Math" (str jfn)) + cmfn (name cfn)] + `(let [ls# (gen/sample safe-integer ~n)] + (is (every? identity + (map e== + (read-string + (cljs-eval (str "(->> '" (pr-str ls#) + " (map #(try (clojure.math/" ~cmfn " %)" + " (catch :default _ :exception))))"))) + (map #(if (no-overflow? ~op % 1) + (~jmfn (long %)) + :exception) ls#))) + (str "data: " (pr-str (map vector ls#))))))) + +(deftest increment-exact + (test-safe->safe 100 incrementExact increment-exact +)) + +(deftest decrement-exact + (test-safe->safe 100 decrementExact decrement-exact -)) diff --git a/src/test/cljs/clojure/math_test.cljs b/src/test/cljs/clojure/math_test.cljs new file mode 100644 index 000000000..49a79a58a --- /dev/null +++ b/src/test/cljs/clojure/math_test.cljs @@ -0,0 +1,318 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.math-test + (:require + [clojure.test :refer [deftest is]] + [cljs.math :as m])) + +(defn neg-zero? + [d] + (and (zero? d) (== -1.0 (m/copy-sign 1.0 d)))) + +(defn pos-zero? + [d] + (and (zero? d) (== 1.0 (m/copy-sign 1.0 d)))) + +(defn ulp= + "Tests that y = x +/- m*ulp(x)" + [x y m] + (let [mu (* (m/ulp x) m)] + (<= (- x mu) y (+ x mu)))) + +(deftest test-sin + (is (js/isNaN (m/sin ##NaN))) + (is (js/isNaN (m/sin ##-Inf))) + (is (js/isNaN (m/sin ##Inf))) + (is (pos-zero? (m/sin 0.0))) + (is (neg-zero? (m/sin -0.0))) + (is (ulp= (m/sin m/PI) (- (m/sin (- m/PI))) 1))) + +(deftest test-cos + (is (js/isNaN (m/cos ##NaN))) + (is (js/isNaN (m/cos ##-Inf))) + (is (js/isNaN (m/cos ##Inf))) + (is (= 1.0 (m/cos 0.0) (m/cos -0.0))) + (is (ulp= (m/cos m/PI) (m/cos (- m/PI)) 1))) + +(deftest test-tan + (is (js/isNaN (m/tan ##NaN))) + (is (js/isNaN (m/tan ##-Inf))) + (is (js/isNaN (m/tan ##Inf))) + (is (pos-zero? (m/tan 0.0))) + (is (neg-zero? (m/tan -0.0))) + (is (ulp= (- (m/tan m/PI)) (m/tan (- m/PI)) 1))) + +(deftest test-asin + (is (js/isNaN (m/asin ##NaN))) + (is (js/isNaN (m/asin 2.0))) + (is (js/isNaN (m/asin -2.0))) + (is (zero? (m/asin -0.0)))) + +(deftest test-acos + (is (js/isNaN (m/acos ##NaN))) + (is (js/isNaN (m/acos -2.0))) + (is (js/isNaN (m/acos 2.0))) + (is (ulp= (* 2 (m/acos 0.0)) m/PI 1))) + +(deftest test-atan + (is (js/isNaN (m/atan ##NaN))) + (is (pos-zero? (m/atan 0.0))) + (is (neg-zero? (m/atan -0.0))) + (is (ulp= (m/atan 1) 0.7853981633974483 1))) + +(deftest test-radians-degrees-roundtrip + (doseq [d (range 0.0 360.0 5.0)] + (is (ulp= (m/round d) (m/round (-> d m/to-radians m/to-degrees)) 1)))) + +(deftest test-exp + (is (js/isNaN (m/exp ##NaN))) + (is (= ##Inf (m/exp ##Inf))) + (is (pos-zero? (m/exp ##-Inf))) + (is (ulp= (m/exp 0.0) 1.0 1)) + (is (ulp= (m/exp 1) m/E 1))) + +(deftest test-log + (is (js/isNaN (m/log ##NaN))) + (is (js/isNaN (m/log -1.0))) + (is (= ##Inf (m/log ##Inf))) + (is (= ##-Inf (m/log 0.0))) + (is (ulp= (m/log m/E) 1.0 1))) + +(deftest test-log10 + (is (js/isNaN (m/log10 ##NaN))) + (is (js/isNaN (m/log10 -1.0))) + (is (= ##Inf (m/log10 ##Inf))) + (is (= ##-Inf (m/log10 0.0))) + (is (ulp= (m/log10 10) 1.0 1))) + +(deftest test-sqrt + (is (js/isNaN (m/sqrt ##NaN))) + (is (js/isNaN (m/sqrt -1.0))) + (is (= ##Inf (m/sqrt ##Inf))) + (is (pos-zero? (m/sqrt 0))) + (is (= (m/sqrt 4.0) 2.0))) + +(deftest test-cbrt + (is (js/isNaN (m/cbrt ##NaN))) + (is (= ##-Inf (m/cbrt ##-Inf))) + (is (= ##Inf (m/cbrt ##Inf))) + (is (pos-zero? (m/cbrt 0))) + (is (= 2.0 (m/cbrt 8.0)))) + +(deftest test-IEEE-remainder + (is (js/isNaN (m/IEEE-remainder ##NaN 1.0))) + (is (js/isNaN (m/IEEE-remainder 1.0 ##NaN))) + (is (js/isNaN (m/IEEE-remainder ##Inf 2.0))) + (is (js/isNaN (m/IEEE-remainder ##-Inf 2.0))) + (is (js/isNaN (m/IEEE-remainder 2 0.0))) + (is (= 1.0 (m/IEEE-remainder 5.0 4.0)))) + +(deftest test-ceil + (is (js/isNaN (m/ceil ##NaN))) + (is (= ##Inf (m/ceil ##Inf))) + (is (= ##-Inf (m/ceil ##-Inf))) + (is (= 4.0 (m/ceil m/PI)))) + +(deftest test-floor + (is (js/isNaN (m/floor ##NaN))) + (is (= ##Inf (m/floor ##Inf))) + (is (= ##-Inf (m/floor ##-Inf))) + (is (= 3.0 (m/floor m/PI)))) + +(deftest test-rint + (is (js/isNaN (m/rint ##NaN))) + (is (= ##Inf (m/rint ##Inf))) + (is (= ##-Inf (m/rint ##-Inf))) + (is (= 1.0 (m/rint 1.2))) + (is (neg-zero? (m/rint -0.01)))) + +(deftest test-atan2 + (is (js/isNaN (m/atan2 ##NaN 1.0))) + (is (js/isNaN (m/atan2 1.0 ##NaN))) + (is (pos-zero? (m/atan2 0.0 1.0))) + (is (neg-zero? (m/atan2 -0.0 1.0))) + (is (ulp= (m/atan2 0.0 -1.0) m/PI 2)) + (is (ulp= (m/atan2 -0.0 -1.0) (- m/PI) 2)) + (is (ulp= (* 2.0 (m/atan2 1.0 0.0)) m/PI 2)) + (is (ulp= (* -2.0 (m/atan2 -1.0 0.0)) m/PI 2)) + (is (ulp= (* 4.0 (m/atan2 ##Inf ##Inf)) m/PI 2)) + (is (ulp= (/ (* 4.0 (m/atan2 ##Inf ##-Inf)) 3.0) m/PI 2)) + (is (ulp= (* -4.0 (m/atan2 ##-Inf ##Inf)) m/PI 2)) + (is (ulp= (/ (* -4.0 (m/atan2 ##-Inf ##-Inf)) 3.0) m/PI 2))) + +(deftest test-pow + (is (= 1.0 (m/pow 4.0 0.0))) + (is (= 1.0 (m/pow 4.0 -0.0))) + (is (= 4.2 (m/pow 4.2 1.0))) + (is (js/isNaN (m/pow 4.2 ##NaN))) + (is (js/isNaN (m/pow ##NaN 2.0))) + (is (= ##Inf (m/pow 2.0 ##Inf))) + (is (= ##Inf (m/pow 0.5 ##-Inf))) + (is (= 0.0 (m/pow 2.0 ##-Inf))) + (is (= 0.0 (m/pow 0.5 ##Inf))) + (is (js/isNaN (m/pow 1.0 ##Inf))) + (is (pos-zero? (m/pow 0.0 1.5))) + (is (pos-zero? (m/pow ##Inf -2.0))) + (is (= ##Inf (m/pow 0.0 -2.0))) + (is (= ##Inf (m/pow ##Inf 2.0))) + (is (pos-zero? (m/pow -0.0 1.5))) + (is (pos-zero? (m/pow ##-Inf -1.5))) + (is (neg-zero? (m/pow -0.0 3.0))) + (is (neg-zero? (m/pow ##-Inf -3.0))) + (is (= ##Inf (m/pow -0.0 -1.5))) + (is (= ##Inf (m/pow ##-Inf 2.5))) + (is (= ##-Inf (m/pow -0.0 -3.0))) + (is (= ##-Inf (m/pow ##-Inf 3.0))) + (is (= 4.0 (m/pow -2.0 2.0))) + (is (= -8.0 (m/pow -2.0 3.0))) + (is (= 8.0 (m/pow 2.0 3.0)))) + +(deftest test-round + (is (= 0 (m/round ##NaN))) + (is (= js/Number.MIN_SAFE_INTEGER (m/round ##-Inf))) + (is (= js/Number.MAX_SAFE_INTEGER (m/round ##Inf))) + (is (= 4 (m/round 3.5)))) + +(deftest test-add-exact + (try + (m/add-exact js/Number.MAX_SAFE_INTEGER 1) + (is false) + (catch ExceptionInfo _ + (is true)))) + +(deftest test-subtract-exact + (try + (m/subtract-exact js/Number.MIN_SAFE_INTEGER 1) + (is false) + (catch ExceptionInfo _ + (is true)))) + +(deftest test-multiply-exact + (try + (m/multiply-exact js/Number.MAX_SAFE_INTEGER 2) + (is false) + (catch ExceptionInfo _ + (is true)))) + +(deftest test-increment-exact + (try + (m/increment-exact js/Number.MAX_SAFE_INTEGER) + (is false) + (catch ExceptionInfo _ + (is true)))) + +(deftest test-decrement-exact + (try + (m/decrement-exact js/Number.MIN_SAFE_INTEGER) + (is false) + (catch ExceptionInfo _ + (is true)))) + +(deftest test-negate-exact + (is (= js/Number.MIN_SAFE_INTEGER (m/negate-exact js/Number.MAX_SAFE_INTEGER))) + (is (= js/Number.MAX_SAFE_INTEGER (m/negate-exact js/Number.MIN_SAFE_INTEGER)))) + +(deftest test-floor-div + (is (= js/Number.MAX_SAFE_INTEGER (m/floor-div js/Number.MIN_SAFE_INTEGER -1))) + (is (= -1 (m/floor-div -2 5)))) + +(deftest test-floor-mod + (is (= 3 (m/floor-mod -2 5)))) + +(deftest test-ulp + (is (js/isNaN (m/ulp ##NaN))) + (is (= ##Inf (m/ulp ##Inf))) + (is (= ##Inf (m/ulp ##-Inf))) + (is (= js/Number.MIN_VALUE (m/ulp 0.0))) + (is (= (m/pow 2 971) (m/ulp js/Number.MAX_VALUE))) + (is (= (m/pow 2 971) (m/ulp (- js/Number.MAX_VALUE))))) + +(deftest test-signum + (is (js/isNaN (m/signum ##NaN))) + (is (zero? (m/signum 0.0))) + (is (zero? (m/signum -0.0))) + (is (= 1.0 (m/signum 42.0))) + (is (= -1.0 (m/signum -42.0)))) + +(deftest test-sinh + (is (js/isNaN (m/sinh ##NaN))) + (is (= ##Inf (m/sinh ##Inf))) + (is (= ##-Inf (m/sinh ##-Inf))) + (is (= 0.0 (m/sinh 0.0)))) + +(deftest test-cosh + (is (js/isNaN (m/cosh ##NaN))) + (is (= ##Inf (m/cosh ##Inf))) + (is (= ##Inf (m/cosh ##-Inf))) + (is (= 1.0 (m/cosh 0.0)))) + +(deftest test-tanh + (is (js/isNaN (m/tanh ##NaN))) + (is (= 1.0 (m/tanh ##Inf))) + (is (= -1.0 (m/tanh ##-Inf))) + (is (= 0.0 (m/tanh 0.0)))) + +(deftest test-hypot + (is (= ##Inf (m/hypot 1.0 ##Inf))) + (is (= ##Inf (m/hypot ##Inf 1.0))) + (is (js/isNaN (m/hypot ##NaN 1.0))) + (is (js/isNaN (m/hypot 1.0 ##NaN))) + (is (= 13.0 (m/hypot 5.0 12.0)))) + +(deftest test-expm1 + (is (js/isNaN (m/expm1 ##NaN))) + (is (= ##Inf (m/expm1 ##Inf))) + (is (= -1.0 (m/expm1 ##-Inf))) + (is (= 0.0 (m/expm1 0.0)))) + +(deftest test-log1p + (is (js/isNaN (m/log1p ##NaN))) + (is (= ##Inf (m/log1p ##Inf))) + (is (= ##-Inf (m/log1p -1.0))) + (is (pos-zero? (m/log1p 0.0))) + (is (neg-zero? (m/log1p -0.0)))) + +(deftest test-copy-sign + (is (= 1.0 (m/copy-sign 1.0 42.0))) + (is (= -1.0 (m/copy-sign 1.0 -42.0))) + (is (= -1.0 (m/copy-sign 1.0 ##-Inf)))) + +(deftest test-get-exponent + (is (= (inc @#'cljs.math/EXP-MAX) (m/get-exponent ##NaN))) + (is (= (inc @#'cljs.math/EXP-MAX) (m/get-exponent ##Inf))) + (is (= (inc @#'cljs.math/EXP-MAX) (m/get-exponent ##-Inf))) + (is (= (dec @#'cljs.math/EXP-MIN) (m/get-exponent 0.0))) + (is (= 0 (m/get-exponent 1.0))) + (is (= 13 (m/get-exponent 12345.678)))) + +(deftest test-next-after + (is (js/isNaN (m/next-after ##NaN 1))) + (is (js/isNaN (m/next-after 1 ##NaN))) + (is (pos-zero? (m/next-after 0.0 0.0))) + (is (neg-zero? (m/next-after -0.0 -0.0))) + (is (= js/Number.MAX_VALUE (m/next-after ##Inf 1.0))) + (is (pos-zero? (m/next-after js/Number.MIN_VALUE -1.0)))) + +(deftest test-next-up + (is (js/isNaN (m/next-up ##NaN))) + (is (= ##Inf (m/next-up ##Inf))) + (is (= js/Number.MIN_VALUE (m/next-up 0.0)))) + +(deftest test-next-down + (is (js/isNaN (m/next-down ##NaN))) + (is (= ##-Inf (m/next-down ##-Inf))) + (is (= (- js/Number.MIN_VALUE) (m/next-down 0.0)))) + +(deftest test-scalb + (is (js/isNaN (m/scalb ##NaN 1))) + (is (= ##Inf (m/scalb ##Inf 1))) + (is (= ##-Inf (m/scalb ##-Inf 1))) + (is (pos-zero? (m/scalb 0.0 2))) + (is (neg-zero? (m/scalb -0.0 2))) + (is (= 32.0 (m/scalb 2.0 4)))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 64b51bd41..d244ab6b3 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -26,6 +26,7 @@ [clojure.datafy-test] [clojure.edn-test] [clojure.walk-test] + [clojure.math-test] [cljs.macro-test] [cljs.letfn-test] [foo.ns-shadow-test] @@ -81,6 +82,7 @@ 'clojure.datafy-test 'clojure.edn-test 'clojure.walk-test + 'clojure.math-test 'cljs.letfn-test 'cljs.reducers-test 'cljs.binding-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index 866dfa074..e6f87349d 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -286,6 +286,7 @@ [clojure.data-test] [clojure.datafy-test] [clojure.edn] + [clojure.math-test] [clojure.walk-test] [cljs.macro-test] [cljs.letfn-test] @@ -335,6 +336,7 @@ 'clojure.data-test 'clojure.datafy-test 'clojure.edn + 'clojure.math-test 'clojure.walk-test 'cljs.letfn-test 'cljs.reducers-test From 8528937440d4c5a2c8fc86813c5a9928ab184daa Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 Apr 2022 16:01:52 -0400 Subject: [PATCH 3803/4033] CLJS-3348: Implement new functions for parity with Clojure 1.11 (#165) Co-authored-by: Paula Gearon --- src/main/cljs/cljs/core.cljs | 60 +++++++++++++++++ src/test/cljs/cljs/parse_test.cljs | 101 ++++++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 + src/test/self/self_parity/test.cljs | 2 + 4 files changed, 165 insertions(+) create mode 100644 src/test/cljs/cljs/parse_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d06e19a8d..cf22d5f99 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12025,6 +12025,66 @@ 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)))) + +(defn ^number parse-long + "Parse string of decimal digits with optional leading -/+ and return an + integer value, or nil if parse fails" + [s] + (if (string? s) + (and (re-matches #"[+-]?\d+" s) + (let [i (js/parseInt s)] + (when (and (<= i js/Number.MAX_SAFE_INTEGER) + (>= i js/Number.MIN_SAFE_INTEGER)) + i))) + (throw (js/Error. (parsing-err s))))) + +(defn ^number parse-double + "Parse string with floating point components and return a floating point value, + or nil if parse fails. + Grammar: https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#valueOf-java.lang.String-" + [s] + (if (string? s) + (cond + ^boolean (re-matches #"[\x00-\x20]*[+-]?NaN[\x00-\x20]*" s) ##NaN + ^boolean (re-matches + #"[\x00-\x20]*[+-]?(Infinity|((\d+\.?\d*|\.\d+)([eE][+-]?\d+)?)[dDfF]?)[\x00-\x20]*" + s) (js/parseFloat s) + :default nil) + (throw (js/Error. (parsing-err s))))) + +(def ^:private uuid-regex + #"^[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]-[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]-[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]-[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]-[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]$") + +(defn parse-uuid + "Parse a string representing a UUID and return a UUID instance, + or nil if parse fails. + Grammar: https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html#toString--" + [s] + (if (string? s) + (when ^boolean (re-matches uuid-regex s) + (uuid s)) + (throw (js/Error. (parsing-err s))))) + +(defn parse-boolean + "Parse strings \"true\" or \"false\" and return a boolean, or nil if invalid. Note that this explicitly + excludes strings with different cases, or space characters." + [s] + (if (string? s) + (case s + "true" true + "false" false + nil) + (throw (js/Error. (parsing-err s))))) + (defn- maybe-enable-print! [] (cond (exists? js/console) diff --git a/src/test/cljs/cljs/parse_test.cljs b/src/test/cljs/cljs/parse_test.cljs new file mode 100644 index 000000000..437f82e8f --- /dev/null +++ b/src/test/cljs/cljs/parse_test.cljs @@ -0,0 +1,101 @@ +(ns cljs.parse-test + (:require + [clojure.test :refer [deftest is are]] + [clojure.test.check :as chk] + [clojure.test.check.generators :as gen] + [clojure.test.check.properties :as prop])) + +(deftest test-parse-long + (are [s expected] + (= expected (parse-long s)) + "100" 100 + "+100" 100 + "0" 0 + "+0" 0 + "-0" 0 + "-42" -42 + "9007199254740991" js/Number.MAX_SAFE_INTEGER ;; largest parsable: 999999999999999934463 + "+9007199254740991" js/Number.MAX_SAFE_INTEGER + "-9007199254740991" js/Number.MIN_SAFE_INTEGER + "077" 77) ;; leading 0s are ignored! (not octal) + + (are [s] ;; do not parse + (nil? (parse-long s)) + "0.3" ;; no float + "9007199254740992" ;; past max long + "-9007199254740992" ;; past min long + "0xA0" ;; no hex + "2r010")) ;; no radix support + +;; generative test - gen long -> str -> parse, compare +(deftest test-gen-parse-long + (let [res (chk/quick-check + 100000 + (prop/for-all* [gen/large-integer] + #(= % (-> % str parse-long))))] + (if (:result res) + (is true) ;; pass + (is (:result res) (pr-str res))))) + +(deftest test-parse-double + (are [s expected] + (= expected (parse-double s)) + "1.234" 1.234 + "+1.234" 1.234 + "-1.234" -1.234 + "+0" +0.0 + "-0.0" -0.0 + "0.0" 0.0 + "5" 5.0 + ".5" 0.5 + "Infinity" ##Inf + "-Infinity" ##-Inf + "1.7976931348623157E308" js/Number.MAX_VALUE + "4.9E-324" js/Number.MIN_VALUE + "1.7976931348623157E309" js/Number.POSITIVE_INFINITY ;; past max double + "2.5e-324" js/Number.MIN_VALUE ;; past min double, above half minimum + "2.4e-324" 0.0) ;; below minimum double + (is (js/isNaN (parse-double "NaN"))) + (are [s] ;; nil on invalid string + (nil? (parse-double s)) + "double" ;; invalid string + "1.7976931348623157G309")) ;; close, but not valid + +;; generative test - gen double -> str -> parse, compare +(deftest test-gen-parse-double + (let [res (chk/quick-check + 100000 + (prop/for-all* [gen/double] + #(let [parsed (-> % str parse-double)] + (if (js/isNaN %) + (js/isNaN parsed) + (= % parsed)))))] + (if (:result res) + (is true) ;; pass + (is (:result res) (pr-str res))))) + +(deftest test-parse-uuid + (is (parse-uuid (str (random-uuid)))) + (is (nil? (parse-uuid "BOGUS"))) ;; nil on invalid uuid string + (are [s] ;; throw on invalid type (not string) + (try (parse-uuid s) (is false) (catch :default _ (is true))) + 123 + nil)) + +(deftest test-parse-boolean + (is (identical? true (parse-boolean "true"))) + (is (identical? false (parse-boolean "false"))) + + (are [s] ;; nil on invalid string + (nil? (parse-boolean s)) + "abc" + "TRUE" + "FALSE" + " true ") + + (are [s] ;; throw on invalid type (not string) + (try (parse-boolean s) (is false) (catch :default _ (is true))) + nil + false + true + 100)) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index d244ab6b3..29218923f 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -19,6 +19,7 @@ [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] + [cljs.parse-test] [cljs.ns-test] [clojure.set-test] [clojure.string-test] @@ -76,6 +77,7 @@ 'cljs.hashing-test 'cljs.core-test 'cljs.reader-test + 'cljs.parse-test 'clojure.set-test 'clojure.string-test 'clojure.data-test diff --git a/src/test/self/self_parity/test.cljs b/src/test/self/self_parity/test.cljs index e6f87349d..9b2a7b1e1 100644 --- a/src/test/self/self_parity/test.cljs +++ b/src/test/self/self_parity/test.cljs @@ -280,6 +280,7 @@ [cljs.core-test :as core-test] [cljs.reader-test] [cljs.binding-test] + [cljs.parse-test] #_[cljs.ns-test] [clojure.set-test] [clojure.string-test] @@ -331,6 +332,7 @@ 'cljs.hashing-test 'cljs.core-test 'cljs.reader-test + 'cljs.parse-test 'clojure.set-test 'clojure.string-test 'clojure.data-test From ba2672463e2a9c1c5f6b75940556270c3e3fb3d3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 11 Apr 2022 12:03:53 -0400 Subject: [PATCH 3804/4033] CLJS-3370: improved uuid regex to only accept hex characters (#166) Co-authored-by: Paula Gearon --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/parse_test.cljs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index cf22d5f99..c310d9704 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12062,7 +12062,7 @@ reduces them without incurring seq initialization" (throw (js/Error. (parsing-err s))))) (def ^:private uuid-regex - #"^[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]-[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]-[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]-[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]-[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]$") + #"^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$") (defn parse-uuid "Parse a string representing a UUID and return a UUID instance, diff --git a/src/test/cljs/cljs/parse_test.cljs b/src/test/cljs/cljs/parse_test.cljs index 437f82e8f..582ea58c5 100644 --- a/src/test/cljs/cljs/parse_test.cljs +++ b/src/test/cljs/cljs/parse_test.cljs @@ -80,7 +80,15 @@ (are [s] ;; throw on invalid type (not string) (try (parse-uuid s) (is false) (catch :default _ (is true))) 123 - nil)) + nil) + ;; parse the nil uuid + (is (parse-uuid "00000000-0000-0000-0000-000000000000")) + ;; parse a version 1 UUID + (is (parse-uuid "123e4567-e89b-12d3-a456-426614174000")) + ;; parse a version 2 UUID + (is (parse-uuid "123e4567-e89b-22d3-a456-426614174000")) + ;; ensure that bad characters are invalid + (is (nil? (parse-uuid "123e4567-eg9b-12d3-a456-426614174000")))) (deftest test-parse-boolean (is (identical? true (parse-boolean "true"))) From 8b3ce24a6e7e1863802b09618985a0f3a604a0c9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 13 Apr 2022 15:44:13 -0400 Subject: [PATCH 3805/4033] Fix apply of IFn for more than 20 arguments. (#167) Co-authored-by: David Frese --- src/main/clojure/cljs/core.cljc | 9 +++++++-- src/test/cljs/cljs/apply_test.cljs | 9 +++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a4e110b7c..b64b2c9a3 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1499,7 +1499,8 @@ (core/defn- add-ifn-methods [type type-sym [f & meths :as form]] (core/let [meths (map #(adapt-ifn-params type %) meths) this-sym (with-meta 'self__ {:tag type}) - argsym (gensym "args")] + argsym (gensym "args") + max-ifn-arity 20] (concat [`(set! ~(extend-prefix type-sym 'call) ~(with-meta `(fn ~@meths) (meta form))) `(set! ~(extend-prefix type-sym 'apply) @@ -1507,7 +1508,11 @@ `(fn ~[this-sym argsym] (this-as ~this-sym (.apply (.-call ~this-sym) ~this-sym - (.concat (array ~this-sym) (cljs.core/aclone ~argsym))))) + (.concat (array ~this-sym) + (if (> (.-length ~argsym) ~max-ifn-arity) + (doto (.slice ~argsym 0 ~max-ifn-arity) + (.push (.slice ~argsym ~max-ifn-arity))) + ~argsym))))) (meta form)))] (ifn-invoke-methods type type-sym form)))) diff --git a/src/test/cljs/cljs/apply_test.cljs b/src/test/cljs/cljs/apply_test.cljs index c8651bd01..adcee65a6 100644 --- a/src/test/cljs/cljs/apply_test.cljs +++ b/src/test/cljs/cljs/apply_test.cljs @@ -42,14 +42,11 @@ (is (= '(1 2 3 4)) (apply meta-f 1 2 3 4 [])) (is (= '(1 2 3 4 5)) (apply meta-f 1 2 3 4 5 [])) (is (= (range 1 8)) (apply meta-f 1 2 3 4 5 [6 7])) - ;; Currently: 20 is not seqable :( - #_(is (= (range 21) (apply meta-f (range 21))) + (is (= (range 21) (apply meta-f (range 21))) "Should properly call the last IFn arity with 20 args with last being a seq") - ;; Currently: Tries to call arity 21. Fault at .apply of the deftype proto - ;; Though, it could probably also be caught right by apply - #_(is (= (range 22) (apply meta-f (range 22))) + (is (= (range 22) (apply meta-f (range 22))) "Should properly call the last IFn arity with 20 args with last being a seq") - #_(is (= (range 22) (.apply meta-f nil (to-array (range 22)))) + (is (= (range 22) (.apply meta-f nil (to-array (range 22)))) ".apply should also handle >20 arguments")) (deftest multi-arity-test From 7be4ae09f92e602e1338b38a55dc02035e4ff54e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 14 Apr 2022 10:18:59 -0400 Subject: [PATCH 3806/4033] CLJS-3371: Invalid warning on record constructor (#168) Record constructor has a second arity that accept 2 additionnal parameters Co-authored-by: Roland THIOLLIERE --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- src/test/clojure/cljs/analyzer_tests.clj | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 165c02ac4..3cdb48d13 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2554,7 +2554,10 @@ known-num-fields (:num-fields ctor-var) argc (count args)] (when (and (not (-> ctor meta :internal-ctor)) - (some? known-num-fields) (not= known-num-fields argc)) + (some? known-num-fields) + (not (or (= known-num-fields argc) + (and (:record ctor-var) + (= (+ 2 known-num-fields) argc))))) (warning :fn-arity env {:argc argc :ctor ctor})) {:env env :op :new :form form :class ctorexpr :args argexprs :children [:class :args] diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 6c0041f92..9b823bdac 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1492,3 +1492,23 @@ '[(ns test.foo (:import goog))])) (is (= {} (get-in @cenv [::ana/namespaces 'test.foo :imports]))))) + +(deftest test-cljs-3371 + (let [ws (atom [])] + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env @test-cenv + (analyze (ana/empty-env) + '(do + (defrecord Foo [a]) + (Foo. nil) + (Foo. nil nil nil))))) + (is (empty? @ws))) + (let [ws (atom [])] + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (env/with-compiler-env @test-cenv + (analyze (ana/empty-env) + '(do + (defrecord Foo [a]) + (Foo. nil nil))))) + (is (= 1 (count @ws))) + (is (string/starts-with? (first @ws) "Wrong number of args (2) passed to Foo")))) From e07ace76650423e4034c55a7415987ddffc7ea0a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 20 Apr 2022 10:28:46 -0400 Subject: [PATCH 3807/4033] CLJS-2820 Compile cljs.loader regardless of whether :modules are used (#169) Co-authored-by: Tom Connors --- src/main/clojure/cljs/closure.clj | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 6c86b4996..96a9f9209 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1247,15 +1247,16 @@ compiled last after all inputs. This is because all inputs must be known and they must already be sorted in dependency order." [inputs {:keys [modules] :as opts}] - (when-let [loader (when (seq modules) - (->> inputs - (filter - (fn [input] - (some '#{"cljs.loader" cljs.loader} - (:provides input)))) - first))] - (let [module-uris (module-graph/modules->module-uris modules inputs opts) - module-infos (module-graph/modules->module-infos modules)] + (when-let [loader (->> inputs + (filter + (fn [input] + (some '#{"cljs.loader" cljs.loader} + (:provides input)))) + first)] + (let [module-uris (when (seq modules) + (module-graph/modules->module-uris modules inputs opts)) + module-infos (when (seq modules) + (module-graph/modules->module-infos modules))] (swap! env/*compiler* ana/add-consts {'cljs.core/MODULE_INFOS (merge (const-expr-form @env/*compiler* 'cljs.core/MODULE_INFOS) module-infos) From 0d5b1baddacc590b60979c77c2353f8ae32ed351 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 20 Apr 2022 11:27:24 -0400 Subject: [PATCH 3808/4033] CLJS-3367: Backward conflict test in prefer-method causes incorrect exception (#170) Reverse prefers* test arguments, add test case. --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c310d9704..865ab0507 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11490,7 +11490,7 @@ reduces them without incurring seq initialization" prefer-table method-cache cached-hierarchy default-dispatch-val))) (-prefer-method [mf dispatch-val-x dispatch-val-y] - (when (prefers* dispatch-val-x dispatch-val-y prefer-table) + (when (prefers* dispatch-val-y dispatch-val-x prefer-table) (throw (js/Error. (str "Preference conflict in multimethod '" name "': " dispatch-val-y " is already preferred to " dispatch-val-x)))) (swap! prefer-table diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 040ac75a6..fbbaa737b 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -312,6 +312,16 @@ (is (= :parent (multi-with-h :child))) ))) +(def tmph (make-hierarchy)) +(defmulti fooz (fn [a b] (keyword b)) :hierarchy #'tmph) +(defmethod fooz :a [a b] a) +(defmethod fooz :b [a b] b) +(prefer-method fooz :a :b) + +(deftest test-cljs-3367-backward-conflict-prefers + (testing "CLJS-3367: Verify no backward conflict in prefer-method" + (is (some? (prefer-method fooz :a :b))))) + (deftest test-transducers (testing "Testing transducers" (is (= (sequence (map inc) (array 1 2 3)) '(2 3 4))) From d7a0c1d69c868997cfc928ada493dc819850758e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 21 Apr 2022 13:16:36 -0400 Subject: [PATCH 3809/4033] CLJS-3368: let binding can shadow globals, leading to strange behavior (#171) * check :js-globals for shadowing in analysis of let bindings * use the standard analyzer context in the REPL * add a compiler test --- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/main/clojure/cljs/repl.cljc | 2 +- src/test/clojure/cljs/compiler_tests.clj | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3cdb48d13..85474d367 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2384,7 +2384,8 @@ (let [init-expr (analyze-let-binding-init env init (cons {:params bes} *loop-lets*)) line (get-line name env) col (get-col name env) - shadow (handle-symbol-local name (get-in env [:locals name])) + shadow (or (handle-symbol-local name (get-in env [:locals name])) + (get-in env [:js-globals name])) be {:name name :line line :column col diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 24644bdc0..649ddf8f0 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1113,7 +1113,7 @@ ana/*fn-invoke-direct* (and static-fns fn-invoke-direct) *repl-opts* opts] (try - (let [env {:context :expr :locals {}} + (let [env (assoc (ana/empty-env) :context :expr) special-fns (merge default-special-fns special-fns) is-special-fn? (set (keys special-fns)) request-prompt (Object.) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index e934c4991..bb6a9bfc3 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -365,6 +365,14 @@ ns-info (env/ensure (comp/emit-source input output "cljs" {}))] (is (= 'foo.foo (:ns ns-info))))) +(deftest test-3368-global-shadowing + (testing "Let binding which use JS global names should get shadowed" + (let [code (env/with-compiler-env (env/default-compiler-env) + (compile-form-seq + '[(defn foo [] + (let [window js/window] + window))]))] + (is (re-find #"window__\$1" code))))) ;; CLJS-1225 From 40358fc4c53090676da82e3f4c52920003678902 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 21 Apr 2022 13:36:57 -0400 Subject: [PATCH 3810/4033] Fix bug in fn-name-var shadowing (#172) --- src/main/clojure/cljs/analyzer.cljc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 85474d367..3e68bf431 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2171,9 +2171,8 @@ (defn fn-name-var [env locals name] (when (some? name) (let [ns (-> env :ns :name) - shadow (handle-symbol-local name (get locals name)) - shadow (when (nil? shadow) - (get-in env [:js-globals name])) + shadow (or (handle-symbol-local name (get locals name)) + (get-in env [:js-globals name])) fn-scope (:fn-scope env) name-var {:name name :op :binding From 9e9b6d954368f37516bf82fc6ba353f72c9de091 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 May 2022 13:55:48 -0400 Subject: [PATCH 3811/4033] CLJS-3373: Externs Inference issue with vars invoked from foreign libs (#173) Fix invoke inference to handle invokes on vars from foreign libs. If we have a :js-var we cannot know whether it is a function or property. Note this is different from the `:js-fn-var` case where a provided extern did disambiguate. In the case of `:js-var` we throw away the leading prefix since the types simply cannot be known. Add a test case based on the one provided by Timothy Pratley. --- src/main/clojure/cljs/analyzer.cljc | 30 ++++++++++++++----- src/test/clojure/cljs/externs_infer_tests.clj | 20 +++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3e68bf431..02725c6fe 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1514,15 +1514,31 @@ else-tag #{else-tag})] (into then-tag else-tag)))))))) -(defn infer-invoke [env {f :fn :keys [args] :as e}] - (let [me (assoc (find-matching-method f args) :op :fn-method)] +(defn js-var? [ast] + (= :js-var (:op ast))) + +(defn js-var-fn? [fn-ast] + (js-var? (:info fn-ast))) + +(defn fn-ast->tag + [{:keys [info] :as fn-ast}] + (cond + ;; ClojureScript Fn + (:fn-var info) (:ret-tag info) + ;; Global foreign JS Fn inferred via externs + (:js-fn-var info) (:ret-tag info) + ;; Node foreign JS *var*, we cannot distinguish between properties + ;; and functions from such libs at this time, we cannot possibly + ;; know the returns so break the leading prefix (start with raw 'js tag) + (js-var-fn? fn-ast) 'js + :else (when (= 'js (:ns info)) 'js))) + +(defn infer-invoke [env {fn-ast :fn :keys [args] :as e}] + (let [me (assoc (find-matching-method fn-ast args) :op :fn-method)] (if-some [ret-tag (infer-tag env me)] ret-tag - (let [{:keys [info]} f] - (if-some [ret-tag (if (or (true? (:fn-var info)) - (true? (:js-fn-var info))) - (:ret-tag info) - (when (= 'js (:ns info)) 'js))] + (let [] + (if-some [ret-tag (fn-ast->tag fn-ast)] ret-tag impl/ANY_SYM))))) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 6f3286b14..3038254bc 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -413,6 +413,26 @@ :with-core? true}))] (is (empty? @ws)))) +(deftest test-cljs-3373 + (testing "var from foreign libraries that are invoked as fn should propagate 'js hints" + (let [ws (atom []) + res (infer-test-helper + {:js-dependency-index {"firebase" {:global-exports '{firebase Firebase}}} + :forms '[(ns foo.core + (:require [firebase :refer [getAuth]])) + (def auth + (doto (getAuth) + (.useDeviceLanguage) + (.onAuthStateChanged (fn [user]))))] + :warnings ws + :warn true + :with-core? false})] + (is (= (unsplit-lines + ["Object.getAuth;" + "Object.useDeviceLanguage;" + "Object.onAuthStateChanged;"]) + res))))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From 9b9551aa0093e0f43af0834029d6d5d1fde0e40b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 May 2022 14:19:31 -0400 Subject: [PATCH 3812/4033] Rename node args consistently to `ast` Rename node args consistently to `ast` in the inference code to improve readability --- src/main/clojure/cljs/analyzer.cljc | 56 ++++++++++++++--------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 02725c6fe..05706ae2e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1441,16 +1441,16 @@ (register-constant! env sym) {:op :const :val sym :env env :form sym :tag 'cljs.core/Keyword}) -(defn get-tag [e] - (if-some [tag (-> e :form meta :tag)] +(defn get-tag [ast] + (if-some [tag (-> ast :form meta :tag)] tag - (if-some [tag (-> e :tag)] + (if-some [tag (-> ast :tag)] tag - (-> e :info :tag)))) + (-> ast :info :tag)))) -(defn find-matching-method [f params] +(defn find-matching-method [fn-ast params] ;; if local fn, need to look in :info - (let [methods (or (:methods f) (-> f :info :methods)) + (let [methods (or (:methods fn-ast) (-> fn-ast :info :methods)) c (count params)] (some (fn [m] @@ -1476,21 +1476,21 @@ (declare infer-tag) -(defn unwrap-quote [{:keys [op] :as expr}] +(defn unwrap-quote [{:keys [op] :as ast}] (if #?(:clj (= op :quote) :cljs (keyword-identical? op :quote)) - (:expr expr) - expr)) + (:expr ast) + ast)) -(defn infer-if [env e] - (let [{:keys [op form]} (unwrap-quote (:test e)) - then-tag (infer-tag env (:then e))] +(defn infer-if [env ast] + (let [{:keys [op form]} (unwrap-quote (:test ast)) + then-tag (infer-tag env (:then ast))] (if (and #?(:clj (= op :const) :cljs (keyword-identical? op :const)) (not (nil? form)) (not (false? form))) then-tag - (let [else-tag (infer-tag env (:else e))] + (let [else-tag (infer-tag env (:else ast))] (cond (or #?(:clj (= then-tag else-tag) :cljs (symbol-identical? then-tag else-tag)) @@ -1533,7 +1533,7 @@ (js-var-fn? fn-ast) 'js :else (when (= 'js (:ns info)) 'js))) -(defn infer-invoke [env {fn-ast :fn :keys [args] :as e}] +(defn infer-invoke [env {fn-ast :fn :keys [args] :as ast}] (let [me (assoc (find-matching-method fn-ast args) :op :fn-method)] (if-some [ret-tag (infer-tag env me)] ret-tag @@ -1545,28 +1545,28 @@ (defn infer-tag "Given env, an analysis environment, and e, an AST node, return the inferred type of the node" - [env e] - (if-some [tag (get-tag e)] + [env ast] + (if-some [tag (get-tag ast)] tag - (case (:op e) + (case (:op ast) :recur impl/IGNORE_SYM :throw impl/IGNORE_SYM - :let (infer-tag env (:body e)) - :loop (infer-tag env (:body e)) - :do (infer-tag env (:ret e)) - :fn-method (infer-tag env (:body e)) - :def (infer-tag env (:init e)) - :invoke (infer-invoke env e) - :if (infer-if env e) - :const (case (:form e) + :let (infer-tag env (:body ast)) + :loop (infer-tag env (:body ast)) + :do (infer-tag env (:ret ast)) + :fn-method (infer-tag env (:body ast)) + :def (infer-tag env (:init ast)) + :invoke (infer-invoke env ast) + :if (infer-if env ast) + :const (case (:form ast) true impl/BOOLEAN_SYM false impl/BOOLEAN_SYM impl/ANY_SYM) - :quote (infer-tag env (:expr e)) + :quote (infer-tag env (:expr ast)) (:var :local :js-var :binding) - (if-some [init (:init e)] + (if-some [init (:init ast)] (infer-tag env init) - (infer-tag env (:info e))) + (infer-tag env (:info ast))) (:host-field :host-call) impl/ANY_SYM :js impl/ANY_SYM From 1dd1910aab235c37d60d6aeae7f3e9f63f591dc9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 May 2022 15:48:55 -0400 Subject: [PATCH 3813/4033] bump transit and tools.reader (#175) --- deps.edn | 4 ++-- pom.template.xml | 4 ++-- project.clj | 4 ++-- script/bootstrap | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deps.edn b/deps.edn index 17b28187c..97fed0f4c 100644 --- a/deps.edn +++ b/deps.edn @@ -1,13 +1,13 @@ {:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20210808"} - com.cognitect/transit-clj {:mvn/version "1.0.324"} + com.cognitect/transit-clj {:mvn/version "1.0.329"} org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} org.clojure/data.json {:mvn/version "2.4.0"} org.clojure/google-closure-library {:mvn/version "0.0-20211011-0726fdeb"} org.clojure/spec.alpha {:mvn/version "0.1.143"} - org.clojure/tools.reader {:mvn/version "1.3.3"} + 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"] diff --git a/pom.template.xml b/pom.template.xml index f5e4ac3a2..d4ad9eeb6 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -45,12 +45,12 @@ org.clojure tools.reader - 1.3.3 + 1.3.6 com.cognitect transit-clj - 1.0.324 + 1.0.329 org.clojure diff --git a/project.clj b/project.clj index 2c6e68c42..b230b72a8 100644 --- a/project.clj +++ b/project.clj @@ -12,9 +12,9 @@ [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] [org.clojure/data.json "2.4.0"] - [org.clojure/tools.reader "1.3.3"] + [org.clojure/tools.reader "1.3.6"] [org.clojure/test.check "1.1.1" :scope "test"] - [com.cognitect/transit-clj "1.0.324"] + [com.cognitect/transit-clj "1.0.329"] [org.clojure/google-closure-library "0.0-20211011-0726fdeb"] [com.google.javascript/closure-compiler-unshaded "v20210808"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} diff --git a/script/bootstrap b/script/bootstrap index 3435c454f..4e19ee728 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -7,9 +7,9 @@ SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20210808" DJSON_RELEASE="2.4.0" -TRANSIT_RELEASE="1.0.324" +TRANSIT_RELEASE="1.0.329" GCLOSURE_LIB_RELEASE="0.0-20211011-0726fdeb" -TREADER_RELEASE="1.3.3" +TREADER_RELEASE="1.3.6" TEST_CHECK_RELEASE="1.1.1" # check dependencies From 695afb8dd451d1ab5aa48cff3a6928c273d380db Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 May 2022 10:54:04 -0400 Subject: [PATCH 3814/4033] CLJS-3374: Bump Closure Compiler to latest version (#176) * fix #3374 bump closure compiler to latest version, svg.js was not included in the default externs previously * remove missing diagnostic group Co-authored-by: Timothy Pratley --- deps.edn | 3 +-- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 1 - 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/deps.edn b/deps.edn index 97fed0f4c..37be73d85 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-unshaded {:mvn/version "v20210808"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20220502"} com.cognitect/transit-clj {:mvn/version "1.0.329"} org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/core.specs.alpha {:mvn/version "0.1.24"} @@ -28,4 +28,3 @@ :uberjar {:extra-deps {com.github.seancorfield/depstar {:mvn/version "2.0.193"}} :exec-fn hf.depstar/uberjar :exec-args {:aot true}}}} - diff --git a/pom.template.xml b/pom.template.xml index d4ad9eeb6..66ddf41eb 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20210808 + v20220502 org.clojure diff --git a/project.clj b/project.clj index b230b72a8..fbc2417ab 100644 --- a/project.clj +++ b/project.clj @@ -16,7 +16,7 @@ [org.clojure/test.check "1.1.1" :scope "test"] [com.cognitect/transit-clj "1.0.329"] [org.clojure/google-closure-library "0.0-20211011-0726fdeb"] - [com.google.javascript/closure-compiler-unshaded "v20210808"]] + [com.google.javascript/closure-compiler-unshaded "v20220502"]] :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/script/bootstrap b/script/bootstrap index 4e19ee728..3d78cce70 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="20210808" +CLOSURE_RELEASE="20220502" DJSON_RELEASE="2.4.0" TRANSIT_RELEASE="1.0.329" GCLOSURE_LIB_RELEASE="0.0-20211011-0726fdeb" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 96a9f9209..0cff465ed 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -193,7 +193,6 @@ :undefined-variables DiagnosticGroups/UNDEFINED_VARIABLES :underscore DiagnosticGroups/UNDERSCORE :unknown-defines DiagnosticGroups/UNKNOWN_DEFINES - :unnecessary-escape DiagnosticGroups/UNNECESSARY_ESCAPE :unused-local-variable DiagnosticGroups/UNUSED_LOCAL_VARIABLE :unused-private-property DiagnosticGroups/UNUSED_PRIVATE_PROPERTY :violated-module-dep DiagnosticGroups/VIOLATED_MODULE_DEP From 279e190dc01502c7ce6fbfc7157f61d03dda4821 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 May 2022 13:06:34 -0400 Subject: [PATCH 3815/4033] * bump to Clojure 1.10 as minimum dep * bump clojure-maven-plugin and configure to exclude AOTed deps * change the assembly to exclude any dangling AOTed dep class files DOES NOT WORK, trying to use the resulting JAR leads to strange failures about the elided deftypes --- pom.template.xml | 35 ++++++----------------------------- src/assembly/aot.xml | 3 --- 2 files changed, 6 insertions(+), 32 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 66ddf41eb..6b50ac1f9 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -25,7 +25,7 @@ org.clojure clojure - 1.8.0 + 1.10.0 com.google.javascript @@ -269,7 +269,7 @@ com.theoryinpractise clojure-maven-plugin - 1.3.13 + 1.8.3 false @@ -281,34 +281,11 @@ compile - true + true - cljs.util - cljs.env - cljs.js-deps - cljs.core - cljs.source-map.base64 - cljs.source-map.base64-vlq - cljs.source-map - cljs.analyzer - cljs.analyzer.utils - cljs.compiler - cljs.closure - cljs.tagged-literals - cljs.test - cljs.analyzer.api - cljs.build.api - cljs.compiler.api - cljs.spec.alpha - cljs.spec.test.alpha - cljs.spec.gen.alpha - cljs.repl - cljs.repl.browser - cljs.repl.node - cljs.repl.reflect - cljs.repl.server - cljs.main - cljs.cli + !clojure.tools.reader.* + !clojure.data.json.* + !cognitect.transit.* diff --git a/src/assembly/aot.xml b/src/assembly/aot.xml index 177bdd72b..d23524c61 100644 --- a/src/assembly/aot.xml +++ b/src/assembly/aot.xml @@ -8,14 +8,11 @@ target/classes / - - From 91c597d99b3480c0463b3648016e4f27b0b0b018 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 May 2022 13:12:03 -0400 Subject: [PATCH 3816/4033] * revert the assembly change in previous commit --- src/assembly/aot.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/assembly/aot.xml b/src/assembly/aot.xml index d23524c61..63b796e4d 100644 --- a/src/assembly/aot.xml +++ b/src/assembly/aot.xml @@ -8,11 +8,14 @@ target/classes / + + From ebf9c6e79ef25b18b44206d40020fdebee96033d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 11 May 2022 18:03:36 -0400 Subject: [PATCH 3817/4033] CLJS-3372: Vendorize data.json, transit-clj, and tools.reader (#177) vendorize data.json, transit-clj & tools.reader --- deps.edn | 3 +- pom.template.xml | 11 +- project.clj | 3 +- script/bootstrap | 16 - script/vendorize_deps | 42 + src/main/cljs/cljs/stacktrace.cljc | 2 +- src/main/clojure/cljs/analyzer.cljc | 16 +- src/main/clojure/cljs/closure.clj | 4 +- src/main/clojure/cljs/compiler.cljc | 4 +- src/main/clojure/cljs/core/macros.clj | 6 +- src/main/clojure/cljs/core/server.clj | 4 +- src/main/clojure/cljs/js_deps.cljc | 2 +- src/main/clojure/cljs/repl.cljc | 6 +- src/main/clojure/cljs/repl/browser.clj | 2 +- src/main/clojure/cljs/repl/node.clj | 2 +- src/main/clojure/cljs/source_map.clj | 8 +- .../clojure/cljs/vendor/clojure/data/json.clj | 809 +++++++++++++ .../cljs/vendor/clojure/tools/reader.clj | 1031 +++++++++++++++++ .../tools/reader/default_data_readers.clj | 303 +++++ .../cljs/vendor/clojure/tools/reader/edn.clj | 440 +++++++ .../clojure/tools/reader/impl/commons.clj | 131 +++ .../clojure/tools/reader/impl/errors.clj | 214 ++++ .../clojure/tools/reader/impl/inspect.clj | 91 ++ .../clojure/tools/reader/impl/utils.clj | 127 ++ .../clojure/tools/reader/reader_types.clj | 431 +++++++ .../clojure/cljs/vendor/cognitect/transit.clj | 479 ++++++++ src/test/clojure/cljs/build_api_tests.clj | 4 +- src/test/clojure/cljs/closure_tests.clj | 2 +- 28 files changed, 4132 insertions(+), 61 deletions(-) create mode 100755 script/vendorize_deps create mode 100644 src/main/clojure/cljs/vendor/clojure/data/json.clj create mode 100644 src/main/clojure/cljs/vendor/clojure/tools/reader.clj create mode 100644 src/main/clojure/cljs/vendor/clojure/tools/reader/default_data_readers.clj create mode 100644 src/main/clojure/cljs/vendor/clojure/tools/reader/edn.clj create mode 100644 src/main/clojure/cljs/vendor/clojure/tools/reader/impl/commons.clj create mode 100644 src/main/clojure/cljs/vendor/clojure/tools/reader/impl/errors.clj create mode 100644 src/main/clojure/cljs/vendor/clojure/tools/reader/impl/inspect.clj create mode 100644 src/main/clojure/cljs/vendor/clojure/tools/reader/impl/utils.clj create mode 100644 src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj create mode 100644 src/main/clojure/cljs/vendor/cognitect/transit.clj diff --git a/deps.edn b/deps.edn index 37be73d85..c1f13dc16 100644 --- a/deps.edn +++ b/deps.edn @@ -1,10 +1,9 @@ {:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20220502"} - com.cognitect/transit-clj {:mvn/version "1.0.329"} + 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"} - org.clojure/data.json {:mvn/version "2.4.0"} org.clojure/google-closure-library {:mvn/version "0.0-20211011-0726fdeb"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/tools.reader {:mvn/version "1.3.6"} diff --git a/pom.template.xml b/pom.template.xml index 6b50ac1f9..28fb75858 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -37,11 +37,6 @@ google-closure-library 0.0-20211011-0726fdeb - - org.clojure - data.json - 2.4.0 - org.clojure tools.reader @@ -49,8 +44,8 @@ com.cognitect - transit-clj - 1.0.329 + transit-java + 1.0.362 org.clojure @@ -284,8 +279,6 @@ true !clojure.tools.reader.* - !clojure.data.json.* - !cognitect.transit.* diff --git a/project.clj b/project.clj index fbc2417ab..821f29dd7 100644 --- a/project.clj +++ b/project.clj @@ -11,10 +11,9 @@ :dependencies [[org.clojure/clojure "1.10.0"] [org.clojure/spec.alpha "0.1.143"] [org.clojure/core.specs.alpha "0.1.24"] - [org.clojure/data.json "2.4.0"] [org.clojure/tools.reader "1.3.6"] [org.clojure/test.check "1.1.1" :scope "test"] - [com.cognitect/transit-clj "1.0.329"] + [com.cognitect/transit-java "1.0.362"] [org.clojure/google-closure-library "0.0-20211011-0726fdeb"] [com.google.javascript/closure-compiler-unshaded "v20220502"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} diff --git a/script/bootstrap b/script/bootstrap index 3d78cce70..f28d6e9ea 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,8 +6,6 @@ CLOJURE_RELEASE="1.9.0" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20220502" -DJSON_RELEASE="2.4.0" -TRANSIT_RELEASE="1.0.329" GCLOSURE_LIB_RELEASE="0.0-20211011-0726fdeb" TREADER_RELEASE="1.3.6" TEST_CHECK_RELEASE="1.1.1" @@ -41,20 +39,6 @@ cp core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar lib/core.specs.alpha-$CORE_SPE echo "Cleaning up core.specs.alpha..." rm core.specs.alpha-$CORE_SPECS_ALPHA_RELEASE.jar -echo "Fetching data.json..." -curl --retry 3 -O -s https://repo1.maven.org/maven2/org/clojure/data.json/$DJSON_RELEASE/data.json-$DJSON_RELEASE.jar || { echo "Download failed."; exit 1; } -echo "Copying data.json-$DJSON_RELEASE.jar to lib/data.json-$DJSON_RELEASE.jar..." -cp data.json-$DJSON_RELEASE.jar lib/data.json-$DJSON_RELEASE.jar -echo "Cleaning up data.json..." -rm data.json-$DJSON_RELEASE.jar - -echo "Fetching transit-clj..." -curl --retry 3 -O -s https://repo1.maven.org/maven2/com/cognitect/transit-clj/$TRANSIT_RELEASE/transit-clj-$TRANSIT_RELEASE.jar || { echo "Download failed."; exit 1; } -echo "Copying transit-clj-$TRANSIT_RELEASE.jar to lib/transit-clj-$TRANSIT_RELEASE.jar..." -cp transit-clj-$TRANSIT_RELEASE.jar lib/transit-clj-$TRANSIT_RELEASE.jar -echo "Cleaning up transit-clj..." -rm transit-clj-$TRANSIT_RELEASE.jar - echo "Fetching Google Closure library..." mkdir -p closure/library cd closure/library diff --git a/script/vendorize_deps b/script/vendorize_deps new file mode 100755 index 000000000..419adca8a --- /dev/null +++ b/script/vendorize_deps @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +set -e + +mkdir -p src/main/clojure/cljs/vendor +cd src/main/clojure/cljs + +DJSON_RELEASE="2.4.0" +TRANSIT_RELEASE="1.0.329" +TREADER_RELEASE="1.3.6" + +rm -rf data.json +git clone -b "v$DJSON_RELEASE" --depth 1 git@github.com:clojure/data.json.git +mkdir -p vendor/clojure/data +mv data.json/src/main/clojure/clojure/data/json.clj vendor/clojure/data/ +rm -rf data.json +DATA_JSON_FILE=`mktemp /tmp/json.clj.XXXXXXXXXXX` +sed -e 's/clojure.data.json/cljs.vendor.clojure.data.json/' vendor/clojure/data/json.clj > $DATA_JSON_FILE +mv $DATA_JSON_FILE vendor/clojure/data/json.clj + +rm -rf transit-clj +git clone -b "v$TRANSIT_RELEASE" --depth 1 git@github.com:cognitect/transit-clj.git +mkdir -p vendor/cognitect +mv transit-clj/src/cognitect/transit.clj vendor/cognitect/ +rm -rf transit-clj +TRANSIT_FILE=`mktemp /tmp/transit.clj.XXXXXXXXXXX` +sed -e 's/ns cognitect.transit/ns cljs.vendor.cognitect.transit/' vendor/cognitect/transit.clj > $TRANSIT_FILE +mv $TRANSIT_FILE vendor/cognitect/transit.clj +TRANSIT_FILE=`mktemp /tmp/transit.clj.XXXXXXXXXXX` +sed -e 's/cognitect.transit.WithMeta/cljs.vendor.cognitect.transit.WithMeta/' vendor/cognitect/transit.clj > $TRANSIT_FILE +mv $TRANSIT_FILE vendor/cognitect/transit.clj + +rm -rf tools.reader +rm -rf vendor/clojure/tools +git clone -b "v$TREADER_RELEASE" --depth 1 git@github.com:clojure/tools.reader.git +mkdir -p vendor/clojure/tools +mv tools.reader/src/main/clojure/clojure/tools/* vendor/clojure/tools/ +rm -rf tools.reader + +echo "rewriting tool.reader namespaces" +find vendor/clojure/tools -name '*.clj' -print0 | xargs -0 sed -iBAK 's/clojure.tools/cljs.vendor.clojure.tools/g' +find vendor/clojure/tools -name '*BAK' -delete diff --git a/src/main/cljs/cljs/stacktrace.cljc b/src/main/cljs/cljs/stacktrace.cljc index b1ee6b965..4e2d5bbc7 100644 --- a/src/main/cljs/cljs/stacktrace.cljc +++ b/src/main/cljs/cljs/stacktrace.cljc @@ -542,7 +542,7 @@ goog.events.getProxy/f<@http://localhost:9000/out/goog/events/events.js:276:16" (comment (require '[cljs.closure :as cljsc] - '[clojure.data.json :as json] + '[cljs.vendor.clojure.data.json :as json] '[cljs.source-map :as sm] '[clojure.pprint :as pp]) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 05706ae2e..5017d747e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -25,8 +25,8 @@ [clojure.java.io :as io] [clojure.set :as set] [clojure.string :as string] - [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers]) + [cljs.vendor.clojure.tools.reader :as reader] + [cljs.vendor.clojure.tools.reader.reader-types :as readers]) :cljs (:require [cljs.analyzer.impl :as impl] [cljs.analyzer.impl.namespaces :as nses] [cljs.analyzer.passes.and-or :as and-or] @@ -77,8 +77,8 @@ #?(:clj (def transit-read-opts (try - (require '[cognitect.transit]) - (when-some [ns (find-ns 'cognitect.transit)] + (require '[cljs.vendor.cognitect.transit]) + (when-some [ns (find-ns 'cljs.vendor.cognitect.transit)] (let [read-handler @(ns-resolve ns 'read-handler) read-handler-map @(ns-resolve ns 'read-handler-map)] {:handlers @@ -91,8 +91,8 @@ #?(:clj (def transit-write-opts (try - (require '[cognitect.transit]) - (when-some [ns (find-ns 'cognitect.transit)] + (require '[cljs.vendor.cognitect.transit]) + (when-some [ns (find-ns 'cljs.vendor.cognitect.transit)] (let [write-handler @(ns-resolve ns 'write-handler) write-handler-map @(ns-resolve ns 'write-handler-map)] {:handlers @@ -112,8 +112,8 @@ (def transit (delay (try - (require '[cognitect.transit]) - (when-some [ns (find-ns 'cognitect.transit)] + (require '[cljs.vendor.cognitect.transit]) + (when-some [ns (find-ns 'cljs.vendor.cognitect.transit)] {:writer @(ns-resolve ns 'writer) :reader @(ns-resolve ns 'reader) :write @(ns-resolve ns 'write) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0cff465ed..86c80cf0e 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -20,9 +20,7 @@ [clojure.reflect] [clojure.set :as set] [clojure.string :as string] - [clojure.data.json :as json] - [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers] + [cljs.vendor.clojure.data.json :as json] [cljs.module-graph :as module-graph]) (:import [java.lang ProcessBuilder] [java.io diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 8d0bdcb14..dfc37fb67 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -17,11 +17,11 @@ [cljs.source-map :as sm] [cljs.tagged-literals :as tags] [cljs.util :as util] - [clojure.data.json :as json] + [cljs.vendor.clojure.data.json :as json] [clojure.java.io :as io] [clojure.set :as set] [clojure.string :as string] - [clojure.tools.reader :as reader]) + [cljs.vendor.clojure.tools.reader :as reader]) :cljs (:require [cljs.analyzer :as ana] [cljs.analyzer.impl :as ana.impl] [cljs.env :as env] diff --git a/src/main/clojure/cljs/core/macros.clj b/src/main/clojure/cljs/core/macros.clj index 8aaa87954..1b0e117b1 100644 --- a/src/main/clojure/cljs/core/macros.clj +++ b/src/main/clojure/cljs/core/macros.clj @@ -9,8 +9,8 @@ (ns cljs.core.macros (:refer-clojure :exclude [alias]) (:require [clojure.java.io :as io] - [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers] + [cljs.vendor.clojure.tools.reader :as reader] + [cljs.vendor.clojure.tools.reader.reader-types :as readers] [cljs.env :as env] [cljs.analyzer :as ana] [cljs.repl :refer [source]]) @@ -40,4 +40,4 @@ (defmacro alias [[_ ns] [_ alias]] (swap! env/*compiler* assoc-in [::namespaces (.getName *ns*) :requires alias] ns) - nil) \ No newline at end of file + nil) diff --git a/src/main/clojure/cljs/core/server.clj b/src/main/clojure/cljs/core/server.clj index e2661b59f..0c8923b9e 100644 --- a/src/main/clojure/cljs/core/server.clj +++ b/src/main/clojure/cljs/core/server.clj @@ -8,8 +8,8 @@ (ns cljs.core.server (:refer-clojure :exclude [with-bindings resolve-fn prepl io-prepl]) - (:require [clojure.tools.reader.reader-types :as readers] - [clojure.tools.reader :as reader] + (:require [cljs.vendor.clojure.tools.reader.reader-types :as readers] + [cljs.vendor.clojure.tools.reader :as reader] [cljs.env :as env] [cljs.closure :as closure] [cljs.analyzer :as ana] diff --git a/src/main/clojure/cljs/js_deps.cljc b/src/main/clojure/cljs/js_deps.cljc index 2aa450af1..e13c8ada8 100644 --- a/src/main/clojure/cljs/js_deps.cljc +++ b/src/main/clojure/cljs/js_deps.cljc @@ -8,7 +8,7 @@ (ns cljs.js-deps (:require [cljs.util :as util :refer [distinct-by]] - [clojure.data.json :as json] + [cljs.vendor.clojure.data.json :as json] [clojure.java.io :as io] [clojure.string :as string]) (:import [java.io File] diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 649ddf8f0..511750696 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -11,9 +11,9 @@ (:require [clojure.java.io :as io] [clojure.string :as string] [clojure.set :as set] - [clojure.data.json :as json] - [clojure.tools.reader :as reader] - [clojure.tools.reader.reader-types :as readers] + [cljs.vendor.clojure.data.json :as json] + [cljs.vendor.clojure.tools.reader :as reader] + [cljs.vendor.clojure.tools.reader.reader-types :as readers] [cljs.tagged-literals :as tags] [clojure.edn :as edn] [cljs.util :as util] diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index e51d9499b..4c5b516d5 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -12,7 +12,7 @@ [clojure.java.browse :as browse] [clojure.string :as string] [clojure.edn :as edn] - [clojure.data.json :as json] + [cljs.vendor.clojure.data.json :as json] [cljs.util :as util] [cljs.closure :as cljsc] [cljs.repl :as repl] diff --git a/src/main/clojure/cljs/repl/node.clj b/src/main/clojure/cljs/repl/node.clj index 18bd4bf68..a147808bd 100644 --- a/src/main/clojure/cljs/repl/node.clj +++ b/src/main/clojure/cljs/repl/node.clj @@ -16,7 +16,7 @@ [cljs.repl.bootstrap :as bootstrap] [cljs.cli :as cli] [cljs.closure :as closure] - [clojure.data.json :as json]) + [cljs.vendor.clojure.data.json :as json]) (:import [java.net Socket] [java.lang StringBuilder] [java.io File BufferedReader BufferedWriter IOException] diff --git a/src/main/clojure/cljs/source_map.clj b/src/main/clojure/cljs/source_map.clj index 1fdddf4ec..ed33d4ef0 100644 --- a/src/main/clojure/cljs/source_map.clj +++ b/src/main/clojure/cljs/source_map.clj @@ -9,7 +9,7 @@ (ns cljs.source-map (:require [clojure.java.io :as io] [clojure.string :as string] - [clojure.data.json :as json] + [cljs.vendor.clojure.data.json :as json] [clojure.set :as set] [cljs.source-map.base64-vlq :as base64-vlq])) @@ -104,7 +104,7 @@ (sorted-map))))) (defn decode-reverse - "Convert a v3 source map JSON object into a nested sorted map + "Convert a v3 source map JSON object into a nested sorted map organized as file, line, and column. Note this source map maps from *original* source location to generated source location." ([source-map] @@ -345,11 +345,11 @@ (comment ;; INSTRUCTIONS: - + ;; switch into samples/hello ;; run repl to start clojure ;; build with - + (require '[cljs.closure :as cljsc]) (cljsc/build "src" {:optimizations :simple diff --git a/src/main/clojure/cljs/vendor/clojure/data/json.clj b/src/main/clojure/cljs/vendor/clojure/data/json.clj new file mode 100644 index 000000000..361a306e2 --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/data/json.clj @@ -0,0 +1,809 @@ +;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use +;; and distribution terms for this software are covered by the Eclipse +;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this +;; distribution. By using this software in any fashion, you are +;; agreeing to be bound by the terms of this license. You must not +;; remove this notice, or any other, from this software. + +(ns ^{:author "Stuart Sierra" + :doc "JavaScript Object Notation (JSON) parser/generator. + See http://www.json.org/"} + cljs.vendor.clojure.data.json + (:refer-clojure :exclude (read)) + (:require [clojure.pprint :as pprint]) + (:import (java.io PrintWriter PushbackReader StringWriter + Writer StringReader EOFException))) + +;;; JSON READER + +(set! *warn-on-reflection* true) + +(defn- default-write-key-fn + [x] + (cond (instance? clojure.lang.Named x) + (name x) + (nil? x) + (throw (Exception. "JSON object properties may not be nil")) + :else (str x))) + +(defn- default-value-fn [k v] v) + +(declare -read) + +(defmacro ^:private codepoint [c] + (int c)) + +(defn- codepoint-clause [[test result]] + (cond (list? test) + [(map int test) result] + (= test :whitespace) + ['(9 10 13 32) result] + (= test :js-separators) + ['(16r2028 16r2029) result] + :else + [(int test) result])) + +(defmacro ^:private codepoint-case [e & clauses] + `(case ~e + ~@(mapcat codepoint-clause (partition 2 clauses)) + ~@(when (odd? (count clauses)) + [(last clauses)]))) + +(defn- read-hex-char [^PushbackReader stream] + ;; Expects to be called with the head of the stream AFTER the + ;; initial "\u". Reads the next four characters from the stream. + (let [a (.read stream) + b (.read stream) + c (.read stream) + d (.read stream)] + (when (or (neg? a) (neg? b) (neg? c) (neg? d)) + (throw (EOFException. + "JSON error (end-of-file inside Unicode character escape)"))) + (let [s (str (char a) (char b) (char c) (char d))] + (char (Integer/parseInt s 16))))) + +(defn- read-escaped-char [^PushbackReader stream] + ;; Expects to be called with the head of the stream AFTER the + ;; initial backslash. + (let [c (.read stream)] + (when (neg? c) + (throw (EOFException. "JSON error (end-of-file inside escaped char)"))) + (codepoint-case c + (\" \\ \/) (char c) + \b \backspace + \f \formfeed + \n \newline + \r \return + \t \tab + \u (read-hex-char stream)))) + +(defn- slow-read-string [^PushbackReader stream ^String already-read] + (let [buffer (StringBuilder. already-read)] + (loop [] + (let [c (.read stream)] + (when (neg? c) + (throw (EOFException. "JSON error (end-of-file inside string)"))) + (codepoint-case c + \" (str buffer) + \\ (do (.append buffer (read-escaped-char stream)) + (recur)) + (do (.append buffer (char c)) + (recur))))))) + +(defn- read-quoted-string [^PushbackReader stream] + ;; Expects to be called with the head of the stream AFTER the + ;; opening quotation mark. + (let [buffer ^chars (char-array 64) + read (.read stream buffer 0 64) + end-index (unchecked-dec-int read)] + (when (neg? read) + (throw (EOFException. "JSON error (end-of-file inside string)"))) + (loop [i (int 0)] + (let [c (int (aget buffer i))] + (codepoint-case c + \" (let [off (unchecked-inc-int i) + len (unchecked-subtract-int read off)] + (.unread stream buffer off len) + (String. buffer 0 i)) + \\ (let [off i + len (unchecked-subtract-int read off)] + (.unread stream buffer off len) + (slow-read-string stream (String. buffer 0 i))) + (if (= i end-index) + (do (.unread stream c) + (slow-read-string stream (String. buffer 0 i))) + (recur (unchecked-inc-int i)))))))) + +(defn- read-integer [^String string] + (if (< (count string) 18) ; definitely fits in a Long + (Long/valueOf string) + (or (try (Long/valueOf string) + (catch NumberFormatException e nil)) + (bigint string)))) + +(defn- read-decimal [^String string bigdec?] + (if bigdec? + (bigdec string) + (Double/valueOf string))) + +(defn- read-number [^PushbackReader stream bigdec?] + (let [buffer (StringBuilder.) + decimal? (loop [stage :minus] + (let [c (.read stream)] + (case stage + :minus + (codepoint-case c + \- + (do (.append buffer (char c)) + (recur :int-zero)) + \0 + (do (.append buffer (char c)) + (recur :frac-point)) + (\1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.append buffer (char c)) + (recur :int-digit)) + (throw (Exception. "JSON error (invalid number literal)"))) + ;; Number must either be a single 0 or 1-9 followed by 0-9 + :int-zero + (codepoint-case c + \0 + (do (.append buffer (char c)) + (recur :frac-point)) + (\1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.append buffer (char c)) + (recur :int-digit)) + (throw (Exception. "JSON error (invalid number literal)"))) + ;; at this point, there is at least one digit + :int-digit + (codepoint-case c + (\0 \1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.append buffer (char c)) + (recur :int-digit)) + \. + (do (.append buffer (char c)) + (recur :frac-first)) + (\e \E) + (do (.append buffer (char c)) + (recur :exp-symbol)) + ;; early exit + :whitespace + (do (.unread stream c) + false) + (\, \] \} -1) + (do (.unread stream c) + false) + (throw (Exception. "JSON error (invalid number literal)"))) + ;; previous character is a "0" + :frac-point + (codepoint-case c + \. + (do (.append buffer (char c)) + (recur :frac-first)) + (\e \E) + (do (.append buffer (char c)) + (recur :exp-symbol)) + ;; early exit + :whitespace + (do (.unread stream c) + false) + (\, \] \} -1) + (do (.unread stream c) + false) + ;; Disallow zero-padded numbers or invalid characters + (throw (Exception. "JSON error (invalid number literal)"))) + ;; previous character is a "." + :frac-first + (codepoint-case c + (\0 \1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.append buffer (char c)) + (recur :frac-digit)) + (throw (Exception. "JSON error (invalid number literal)"))) + ;; any number of following digits + :frac-digit + (codepoint-case c + (\0 \1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.append buffer (char c)) + (recur :frac-digit)) + (\e \E) + (do (.append buffer (char c)) + (recur :exp-symbol)) + ;; early exit + :whitespace + (do (.unread stream c) + true) + (\, \] \} -1) + (do (.unread stream c) + true) + (throw (Exception. "JSON error (invalid number literal)"))) + ;; previous character is a "e" or "E" + :exp-symbol + (codepoint-case c + (\- \+) + (do (.append buffer (char c)) + (recur :exp-first)) + (\0 \1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.append buffer (char c)) + (recur :exp-digit))) + ;; previous character is a "-" or "+" + ;; must have at least one digit + :exp-first + (codepoint-case c + (\0 \1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.append buffer (char c)) + (recur :exp-digit)) + (throw (Exception. "JSON error (invalid number literal)"))) + ;; any number of following digits + :exp-digit + (codepoint-case c + (\0 \1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.append buffer (char c)) + (recur :exp-digit)) + :whitespace + (do (.unread stream c) + true) + (\, \] \} -1) + (do (.unread stream c) + true) + (throw (Exception. "JSON error (invalid number literal)"))))))] + (if decimal? + (read-decimal (str buffer) bigdec?) + (read-integer (str buffer))))) + +(defn- next-token [^PushbackReader stream] + (loop [c (.read stream)] + (if (< 32 c) + (int c) + (codepoint-case (int c) + :whitespace (recur (.read stream)) + -1 -1)))) + +(defn invalid-array-exception [] + (Exception. "JSON error (invalid array)")) + +(defn- read-array* [^PushbackReader stream options] + ;; Handles all array values after the first. + (loop [result (transient [])] + (let [r (conj! result (-read stream true nil options))] + (codepoint-case (int (next-token stream)) + \] (persistent! r) + \, (recur r) + (throw (invalid-array-exception)))))) + +(defn- read-array [^PushbackReader stream options] + ;; Expects to be called with the head of the stream AFTER the + ;; opening bracket. + ;; Only handles array value. + (let [c (int (next-token stream))] + (codepoint-case c + \] [] + \, (throw (invalid-array-exception)) + (do (.unread stream c) + (read-array* stream options))))) + +(defn- read-key [^PushbackReader stream] + (let [c (int (next-token stream))] + (if (= c (codepoint \")) + (let [key (read-quoted-string stream)] + (if (= (codepoint \:) (int (next-token stream))) + key + (throw (Exception. "JSON error (missing `:` in object)")))) + (if (= c (codepoint \})) + nil + (throw (Exception. (str "JSON error (non-string key in object), found `" (char c) "`, expected `\"`"))))))) + +(defn- read-object [^PushbackReader stream options] + ;; Expects to be called with the head of the stream AFTER the + ;; opening bracket. + (let [key-fn (get options :key-fn) + value-fn (get options :value-fn)] + (loop [result (transient {})] + (if-let [key (read-key stream)] + (let [key (cond-> key key-fn key-fn) + value (-read stream true nil options) + r (if value-fn + (let [out-value (value-fn key value)] + (if-not (= value-fn out-value) + (assoc! result key out-value) + result)) + (assoc! result key value))] + (codepoint-case (int (next-token stream)) + \, (recur r) + \} (persistent! r) + (throw (Exception. "JSON error (missing entry in object)")))) + (let [r (persistent! result)] + (if (empty? r) + r + (throw (Exception. "JSON error empty entry in object is not allowed")))))))) + +(defn- -read + [^PushbackReader stream eof-error? eof-value options] + (let [c (int (next-token stream))] + (codepoint-case c + ;; Read numbers + (\- \0 \1 \2 \3 \4 \5 \6 \7 \8 \9) + (do (.unread stream c) + (read-number stream (:bigdec options))) + + ;; Read strings + \" (read-quoted-string stream) + + ;; Read null as nil + \n (if (and (= (codepoint \u) (.read stream)) + (= (codepoint \l) (.read stream)) + (= (codepoint \l) (.read stream))) + nil + (throw (Exception. "JSON error (expected null)"))) + + ;; Read true + \t (if (and (= (codepoint \r) (.read stream)) + (= (codepoint \u) (.read stream)) + (= (codepoint \e) (.read stream))) + true + (throw (Exception. "JSON error (expected true)"))) + + ;; Read false + \f (if (and (= (codepoint \a) (.read stream)) + (= (codepoint \l) (.read stream)) + (= (codepoint \s) (.read stream)) + (= (codepoint \e) (.read stream))) + false + (throw (Exception. "JSON error (expected false)"))) + + ;; Read JSON objects + \{ (read-object stream options) + + ;; Read JSON arrays + \[ (read-array stream options) + + (if (neg? c) ;; Handle end-of-stream + (if eof-error? + (throw (EOFException. "JSON error (end-of-file)")) + eof-value) + (throw (Exception. + (str "JSON error (unexpected character): " (char c)))))))) + +(def default-read-options {:bigdec false + :key-fn nil + :value-fn nil}) +(defn read + "Reads a single item of JSON data from a java.io.Reader. Options are + key-value pairs, valid options are: + + :eof-error? boolean + + If true (default) will throw exception if the stream is empty. + + :eof-value Object + + Object to return if the stream is empty and eof-error? is + false. Default is nil. + + :bigdec boolean + + If true use BigDecimal for decimal numbers instead of Double. + Default is false. + + :key-fn function + + Single-argument function called on JSON property names; return + value will replace the property names in the output. Default + is clojure.core/identity, use clojure.core/keyword to get + keyword properties. + + :value-fn function + + Function to transform values in maps (\"objects\" in JSON) in + the output. For each JSON property, value-fn is called with + two arguments: the property name (transformed by key-fn) and + the value. The return value of value-fn will replace the value + in the output. If value-fn returns itself, the property will + be omitted from the output. The default value-fn returns the + value unchanged. This option does not apply to non-map + collections." + [reader & {:as options}] + (let [{:keys [eof-error? eof-value] + :or {eof-error? true}} options] + (->> options + (merge default-read-options) + (-read (PushbackReader. reader 64) eof-error? eof-value)))) + +(defn read-str + "Reads one JSON value from input String. Options are the same as for + read." + [string & {:as options}] + (let [{:keys [eof-error? eof-value] + :or {eof-error? true}} options] + (->> options + (merge default-read-options) + (-read (PushbackReader. (StringReader. string) 64) eof-error? eof-value)))) + +;;; JSON WRITER + + +(defprotocol JSONWriter + (-write [object out options] + "Print object to Appendable out as JSON")) + +(defn- ->hex-string [^Appendable out cp] + (let [cpl (long cp)] + (.append out "\\u") + (cond + (< cpl 16) + (.append out "000") + (< cpl 256) + (.append out "00") + (< cpl 4096) + (.append out "0")) + (.append out (Integer/toHexString cp)))) + +(def ^{:tag "[S"} codepoint-decoder + (let [shorts (short-array 128)] + (dotimes [i 128] + (codepoint-case i + \" (aset shorts i (short 1)) + \\ (aset shorts i (short 1)) + \/ (aset shorts i (short 2)) + \backspace (aset shorts i (short 3)) + \formfeed (aset shorts i (short 4)) + \newline (aset shorts i (short 5)) + \return (aset shorts i (short 6)) + \tab (aset shorts i (short 7)) + (if (< i 32) + (aset shorts i (short 8)) + (aset shorts i (short 0))))) + shorts)) + +(defn- write-string [^CharSequence s ^Appendable out options] + (let [decoder codepoint-decoder] + (.append out \") + (dotimes [i (.length s)] + (let [cp (int (.charAt s i))] + (if (< cp 128) + (case (aget decoder cp) + 0 (.append out (char cp)) + 1 (do (.append out (char (codepoint \\))) (.append out (char cp))) + 2 (.append out (if (get options :escape-slash) "\\/" "/")) + 3 (.append out "\\b") + 4 (.append out "\\f") + 5 (.append out "\\n") + 6 (.append out "\\r") + 7 (.append out "\\t") + 8 (->hex-string out cp)) + (codepoint-case cp + :js-separators (if (get options :escape-js-separators) + (->hex-string out cp) + (.append out (char cp))) + (if (get options :escape-unicode) + (->hex-string out cp) ; Hexadecimal-escaped + (.append out (char cp))))))) + (.append out \"))) + +(defn- write-indent [^Appendable out options] + (let [indent-depth (:indent-depth options)] + (.append out \newline) + (loop [i indent-depth] + (when (pos? i) + (.append out " ") + (recur (dec i)))))) + +(defn- write-object [m ^Appendable out options] + (let [key-fn (get options :key-fn) + value-fn (get options :value-fn) + indent (get options :indent) + opts (cond-> options + indent (update :indent-depth inc))] + (.append out \{) + (when (and indent (seq m)) + (write-indent out opts)) + (loop [x m, have-printed-kv false] + (when (seq x) + (let [[k v] (first x) + out-key (key-fn k) + out-value (value-fn k v) + nxt (next x)] + (when-not (string? out-key) + (throw (Exception. "JSON object keys must be strings"))) + (if-not (= value-fn out-value) + (do + (when have-printed-kv + (.append out \,) + (when indent + (write-indent out opts))) + (write-string out-key out opts) + (.append out \:) + (when indent + (.append out \space)) + (-write out-value out opts) + (when (seq nxt) + (recur nxt true))) + (when (seq nxt) + (recur nxt have-printed-kv)))))) + (when (and indent (seq m)) + (write-indent out options))) + (.append out \})) + +(defn- write-array [s ^Appendable out options] + (let [indent (get options :indent) + opts (cond-> options + indent (update :indent-depth inc))] + (.append out \[) + (when (and indent (seq s)) + (write-indent out opts)) + (loop [x s] + (when (seq x) + (let [fst (first x) + nxt (next x)] + (-write fst out opts) + (when (seq nxt) + (.append out \,) + (when indent + (write-indent out opts)) + (recur nxt))))) + (when (and indent (seq s)) + (write-indent out options))) + (.append out \])) + +(defn- write-bignum [x ^Appendable out options] + (.append out (str x))) + +(defn- write-float [^Float x ^Appendable out options] + (cond (.isInfinite x) + (throw (Exception. "JSON error: cannot write infinite Float")) + (.isNaN x) + (throw (Exception. "JSON error: cannot write Float NaN")) + :else + (.append out (str x)))) + +(defn- write-double [^Double x ^Appendable out options] + (cond (.isInfinite x) + (throw (Exception. "JSON error: cannot write infinite Double")) + (.isNaN x) + (throw (Exception. "JSON error: cannot write Double NaN")) + :else + (.append out (str x)))) + +(defn- write-plain [x ^Appendable out options] + (.append out (str x))) + +(defn- write-uuid [^java.util.UUID x ^Appendable out options] + (.append out \") + (.append out (.toString x)) + (.append out \")) + +(defn- write-instant [^java.time.Instant x ^Appendable out options] + (let [formatter ^java.time.format.DateTimeFormatter (:date-formatter options)] + (.append out \") + (.append out (.format formatter x)) + (.append out \"))) + +(defn- write-date [^java.util.Date x ^Appendable out options] + (write-instant (.toInstant x) out options)) + +(defn- default-sql-date->instant-fn [^java.sql.Date d] + (.toInstant (.atStartOfDay (.toLocalDate d) (java.time.ZoneId/systemDefault)))) + +(defn- write-sql-date [^java.sql.Date x ^Appendable out options] + (let [->instant (:sql-date-converter options)] + (write-instant (->instant x) out options))) + +(defn- write-null [x ^Appendable out options] + (.append out "null")) + +(defn- write-named [x out options] + (write-string (name x) out options)) + +(defn- write-generic [x out options] + (if (.isArray (class x)) + (-write (seq x) out options) + (throw (Exception. (str "Don't know how to write JSON of " (class x)))))) + +(defn- write-ratio [x out options] + (-write (double x) out options)) + +;; nil, true, false +(extend nil JSONWriter {:-write write-null}) +(extend java.lang.Boolean JSONWriter {:-write write-plain}) + +;; Numbers +(extend java.lang.Byte JSONWriter {:-write write-plain}) +(extend java.lang.Short JSONWriter {:-write write-plain}) +(extend java.lang.Integer JSONWriter {:-write write-plain}) +(extend java.lang.Long JSONWriter {:-write write-plain}) +(extend java.lang.Float JSONWriter {:-write write-float}) +(extend java.lang.Double JSONWriter {:-write write-double}) +(extend clojure.lang.Ratio JSONWriter {:-write write-ratio}) +(extend java.math.BigInteger JSONWriter {:-write write-bignum}) +(extend java.math.BigDecimal JSONWriter {:-write write-bignum}) +(extend java.util.concurrent.atomic.AtomicInteger JSONWriter {:-write write-plain}) +(extend java.util.concurrent.atomic.AtomicLong JSONWriter {:-write write-plain}) +(extend java.util.UUID JSONWriter {:-write write-uuid}) +(extend java.time.Instant JSONWriter {:-write write-instant}) +(extend java.util.Date JSONWriter {:-write write-date}) +(extend java.sql.Date JSONWriter {:-write write-sql-date}) +(extend clojure.lang.BigInt JSONWriter {:-write write-bignum}) + +;; Symbols, Keywords, and Strings +(extend clojure.lang.Named JSONWriter {:-write write-named}) +(extend java.lang.CharSequence JSONWriter {:-write write-string}) + +;; Collections +(extend java.util.Map JSONWriter {:-write write-object}) +(extend java.util.Collection JSONWriter {:-write write-array}) + +;; Maybe a Java array, otherwise fail +(extend java.lang.Object JSONWriter {:-write write-generic}) + +(def default-write-options {:escape-unicode true + :escape-js-separators true + :escape-slash true + :sql-date-converter default-sql-date->instant-fn + :date-formatter java.time.format.DateTimeFormatter/ISO_INSTANT + :key-fn default-write-key-fn + :value-fn default-value-fn + :indent false + :indent-depth 0}) +(defn write + "Write JSON-formatted output to a java.io.Writer. Options are + key-value pairs, valid options are: + + :escape-unicode boolean + + If true (default) non-ASCII characters are escaped as \\uXXXX + + :escape-js-separators boolean + + If true (default) the Unicode characters U+2028 and U+2029 will + be escaped as \\u2028 and \\u2029 even if :escape-unicode is + false. (These two characters are valid in pure JSON but are not + valid in JavaScript strings.) + + :escape-slash boolean + + If true (default) the slash / is escaped as \\/ + + :sql-date-converter function + + Single-argument function used to convert a java.sql.Date to + a java.time.Instant. As java.sql.Date does not have a + time-component (which is required by java.time.Instant), it needs + to be computed. The default implementation, `default-sql-date->instant-fn` + uses + ``` + (.toInstant (.atStartOfDay (.toLocalDate sql-date) (java.time.ZoneId/systemDefault))) + ``` + + :date-formatter + + A java.time.DateTimeFormatter instance, defaults to DateTimeFormatter/ISO_INSTANT + + :key-fn function + + Single-argument function called on map keys; return value will + replace the property names in the output. Must return a + string. Default calls clojure.core/name on symbols and + keywords and clojure.core/str on everything else. + + :value-fn function + + Function to transform values in maps before writing. For each + key-value pair in an input map, called with two arguments: the + key (BEFORE transformation by key-fn) and the value. The + return value of value-fn will replace the value in the output. + If the return value is a number, boolean, string, or nil it + will be included literally in the output. If the return value + is a non-map collection, it will be processed recursively. If + the return value is a map, it will be processed recursively, + calling value-fn again on its key-value pairs. If value-fn + returns itself, the key-value pair will be omitted from the + output. This option does not apply to non-map collections." + [x ^Writer writer & {:as options}] + (-write x writer (merge default-write-options options))) + +(defn write-str + "Converts x to a JSON-formatted string. Options are the same as + write." + ^String [x & {:as options}] + (let [sw (StringWriter.)] + (-write x sw (merge default-write-options options)) + (.toString sw))) + +;;; JSON PRETTY-PRINTER + +;; Based on code by Tom Faulhaber + +(defn- pprint-array [s] + ((pprint/formatter-out "~<[~;~@{~w~^, ~:_~}~;]~:>") s)) + +(defn- pprint-object [m options] + (let [key-fn (:key-fn options)] + ((pprint/formatter-out "~<{~;~@{~<~w:~_~w~:>~^, ~_~}~;}~:>") + (for [[k v] m] [(key-fn k) v])))) + +(defn- pprint-generic [x options] + (if (.isArray (class x)) + (pprint-array (seq x)) + ;; pprint proxies Writer, so we can't just wrap it + (print (with-out-str (-write x (PrintWriter. *out*) options))))) + +(defn- pprint-dispatch [x options] + (cond (nil? x) (print "null") + (instance? java.util.Map x) (pprint-object x options) + (instance? java.util.Collection x) (pprint-array x) + (instance? clojure.lang.ISeq x) (pprint-array x) + :else (pprint-generic x options))) + +(defn pprint + "Pretty-prints JSON representation of x to *out*. Options are the + same as for write except :value-fn, which is not supported." + [x & {:as options}] + (let [opts (merge default-write-options options)] + (pprint/with-pprint-dispatch #(pprint-dispatch % opts) + (pprint/pprint x)))) + +;; DEPRECATED APIs from 0.1.x + +(defn read-json + "DEPRECATED; replaced by read-str. + + Reads one JSON value from input String or Reader. If keywordize? is + true (default), object keys will be converted to keywords. If + eof-error? is true (default), empty input will throw an + EOFException; if false EOF will return eof-value." + ([input] + (read-json input true true nil)) + ([input keywordize?] + (read-json input keywordize? true nil)) + ([input keywordize? eof-error? eof-value] + (let [key-fn (if keywordize? keyword identity)] + (condp instance? input + String + (read-str input + :key-fn key-fn + :eof-error? eof-error? + :eof-value eof-value) + java.io.Reader + (read input + :key-fn key-fn + :eof-error? eof-error? + :eof-value eof-value))))) + +(defn write-json + "DEPRECATED; replaced by 'write'. + + Print object to PrintWriter out as JSON" + [x out escape-unicode?] + (write x out :escape-unicode escape-unicode?)) + +(defn json-str + "DEPRECATED; replaced by 'write-str'. + + Converts x to a JSON-formatted string. + + Valid options are: + :escape-unicode false + to turn of \\uXXXX escapes of Unicode characters." + [x & options] + (apply write-str x options)) + +(defn print-json + "DEPRECATED; replaced by 'write' to *out*. + + Write JSON-formatted output to *out*. + + Valid options are: + :escape-unicode false + to turn off \\uXXXX escapes of Unicode characters." + [x & options] + (apply write x *out* options)) + +(defn pprint-json + "DEPRECATED; replaced by 'pprint'. + + Pretty-prints JSON representation of x to *out*. + + Valid options are: + :escape-unicode false + to turn off \\uXXXX escapes of Unicode characters." + [x & options] + (apply pprint x options)) \ No newline at end of file diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader.clj new file mode 100644 index 000000000..ab9db30da --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader.clj @@ -0,0 +1,1031 @@ +;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns ^{:doc "A clojure reader in clojure" + :author "Bronsa"} + cljs.vendor.clojure.tools.reader + (:refer-clojure :exclude [read read-line read-string char read+string + default-data-readers *default-data-reader-fn* + *read-eval* *data-readers* *suppress-read*]) + (:require [cljs.vendor.clojure.tools.reader.reader-types :refer + [read-char unread peek-char indexing-reader? source-logging-push-back-reader source-logging-reader? + get-line-number get-column-number get-file-name string-push-back-reader log-source]] + [cljs.vendor.clojure.tools.reader.impl.utils :refer :all] ;; [char ex-info? whitespace? numeric? desugar-meta] + [cljs.vendor.clojure.tools.reader.impl.errors :as err] + [cljs.vendor.clojure.tools.reader.impl.commons :refer :all] + [cljs.vendor.clojure.tools.reader.default-data-readers :as data-readers]) + (:import (clojure.lang PersistentHashSet IMeta + RT Symbol Reflector Var IObj + PersistentVector IRecord Namespace) + cljs.vendor.clojure.tools.reader.reader_types.SourceLoggingPushbackReader + java.lang.reflect.Constructor + java.util.regex.Pattern + (java.util List LinkedList))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; helpers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare ^:private read* + macros dispatch-macros + ^:dynamic *read-eval* + ^:dynamic *data-readers* + ^:dynamic *default-data-reader-fn* + ^:dynamic *suppress-read* + default-data-readers) + +(defn ^:private ns-name* [x] + (if (instance? Namespace x) + (name (ns-name x)) + (name x))) + +(defn- macro-terminating? [ch] + (case ch + (\" \; \@ \^ \` \~ \( \) \[ \] \{ \} \\) true + false)) + +(defn- ^String read-token + "Read in a single logical token from the reader" + [rdr kind initch] + (if-not initch + (err/throw-eof-at-start rdr kind) + (loop [sb (StringBuilder.) ch initch] + (if (or (whitespace? ch) + (macro-terminating? ch) + (nil? ch)) + (do (when ch + (unread rdr ch)) + (str sb)) + (recur (.append sb ch) (read-char rdr)))))) + +(declare read-tagged) + +(defn- read-dispatch + [rdr _ opts pending-forms] + (if-let [ch (read-char rdr)] + (if-let [dm (dispatch-macros ch)] + (dm rdr ch opts pending-forms) + (read-tagged (doto rdr (unread ch)) ch opts pending-forms)) ;; ctor reader is implemented as a tagged literal + (err/throw-eof-at-dispatch rdr))) + +(defn- read-unmatched-delimiter + [rdr ch opts pending-forms] + (err/throw-unmatch-delimiter rdr ch)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; readers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn read-regex + [rdr ch opts pending-forms] + (let [sb (StringBuilder.)] + (loop [ch (read-char rdr)] + (if (identical? \" ch) + (Pattern/compile (str sb)) + (if (nil? ch) + (err/throw-eof-reading rdr :regex sb) + (do + (.append sb ch ) + (when (identical? \\ ch) + (let [ch (read-char rdr)] + (if (nil? ch) + (err/throw-eof-reading rdr :regex sb)) + (.append sb ch))) + (recur (read-char rdr)))))))) + +(defn- read-unicode-char + ([^String token ^long offset ^long length ^long base] + (let [l (+ offset length)] + (when-not (== (count token) l) + (err/throw-invalid-unicode-literal nil token)) + (loop [i offset uc 0] + (if (== i l) + (char uc) + (let [d (Character/digit (int (nth token i)) (int base))] + (if (== d -1) + (err/throw-invalid-unicode-digit-in-token nil (nth token i) token) + (recur (inc i) (long (+ d (* uc base)))))))))) + + ([rdr initch base length exact?] + (let [base (long base) + length (long length)] + (loop [i 1 uc (long (Character/digit (int initch) (int base)))] + (if (== uc -1) + (err/throw-invalid-unicode-digit rdr initch) + (if-not (== i length) + (let [ch (peek-char rdr)] + (if (or (whitespace? ch) + (macros ch) + (nil? ch)) + (if exact? + (err/throw-invalid-unicode-len rdr i length) + (char uc)) + (let [d (Character/digit (int ch) (int base))] + (read-char rdr) + (if (== d -1) + (err/throw-invalid-unicode-digit rdr ch) + (recur (inc i) (long (+ d (* uc base)))))))) + (char uc))))))) + +(def ^:private ^:const upper-limit (int \uD7ff)) +(def ^:private ^:const lower-limit (int \uE000)) + +(defn- read-char* + "Read in a character literal" + [rdr backslash opts pending-forms] + (let [ch (read-char rdr)] + (if-not (nil? ch) + (let [token (if (or (macro-terminating? ch) + (whitespace? ch)) + (str ch) + (read-token rdr :character ch)) + token-len (count token)] + (cond + + (== 1 token-len) (Character/valueOf (nth token 0)) + + (= token "newline") \newline + (= token "space") \space + (= token "tab") \tab + (= token "backspace") \backspace + (= token "formfeed") \formfeed + (= token "return") \return + + (.startsWith token "u") + (let [c (read-unicode-char token 1 4 16) + ic (int c)] + (if (and (> ic upper-limit) + (< ic lower-limit)) + (err/throw-invalid-character-literal rdr (Integer/toString ic 16)) + c)) + + (.startsWith token "o") + (let [len (dec token-len)] + (if (> len 3) + (err/throw-invalid-octal-len rdr token) + (let [uc (read-unicode-char token 1 len 8)] + (if (> (int uc) 0377) + (err/throw-bad-octal-number rdr) + uc)))) + + :else (err/throw-unsupported-character rdr token))) + (err/throw-eof-in-character rdr)))) + +(defn ^:private starting-line-col-info [rdr] + (when (indexing-reader? rdr) + [(get-line-number rdr) (int (dec (int (get-column-number rdr))))])) + +(defn ^:private ending-line-col-info [rdr] + (when (indexing-reader? rdr) + [(get-line-number rdr) (get-column-number rdr)])) + +(defonce ^:private READ_EOF (Object.)) +(defonce ^:private READ_FINISHED (Object.)) + +(def ^:dynamic *read-delim* false) +(defn- ^PersistentVector read-delimited + "Reads and returns a collection ended with delim" + [kind delim rdr opts pending-forms] + (let [[start-line start-column] (starting-line-col-info rdr) + delim (char delim)] + (binding [*read-delim* true] + (loop [a (transient [])] + (let [form (read* rdr false READ_EOF delim opts pending-forms)] + (if (identical? form READ_FINISHED) + (persistent! a) + (if (identical? form READ_EOF) + (err/throw-eof-delimited rdr kind start-line start-column (count a)) + (recur (conj! a form))))))))) + +(defn- read-list + "Read in a list, including its location if the reader is an indexing reader" + [rdr _ opts pending-forms] + (let [[start-line start-column] (starting-line-col-info rdr) + the-list (read-delimited :list \) rdr opts pending-forms) + [end-line end-column] (ending-line-col-info rdr)] + (with-meta (if (empty? the-list) + '() + (clojure.lang.PersistentList/create the-list)) + (when start-line + (merge + (when-let [file (get-file-name rdr)] + {:file file}) + {:line start-line + :column start-column + :end-line end-line + :end-column end-column}))))) + +(defn- read-vector + "Read in a vector, including its location if the reader is an indexing reader" + [rdr _ opts pending-forms] + (let [[start-line start-column] (starting-line-col-info rdr) + the-vector (read-delimited :vector \] rdr opts pending-forms) + [end-line end-column] (ending-line-col-info rdr)] + (with-meta the-vector + (when start-line + (merge + (when-let [file (get-file-name rdr)] + {:file file}) + {:line start-line + :column start-column + :end-line end-line + :end-column end-column}))))) + +(defn- read-map + "Read in a map, including its location if the reader is an indexing reader" + [rdr _ opts pending-forms] + (let [[start-line start-column] (starting-line-col-info rdr) + the-map (read-delimited :map \} rdr opts pending-forms) + map-count (count the-map) + [end-line end-column] (ending-line-col-info rdr)] + (when (odd? map-count) + (err/throw-odd-map rdr start-line start-column the-map)) + (with-meta + (if (zero? map-count) + {} + (RT/map (to-array the-map))) + (when start-line + (merge + (when-let [file (get-file-name rdr)] + {:file file}) + {:line start-line + :column start-column + :end-line end-line + :end-column end-column}))))) + +(defn- read-number + [rdr initch] + (loop [sb (doto (StringBuilder.) (.append initch)) + ch (read-char rdr)] + (if (or (whitespace? ch) (macros ch) (nil? ch)) + (let [s (str sb)] + (unread rdr ch) + (or (match-number s) + (err/throw-invalid-number rdr s))) + (recur (doto sb (.append ch)) (read-char rdr))))) + +(defn- escape-char [sb rdr] + (let [ch (read-char rdr)] + (case ch + \t "\t" + \r "\r" + \n "\n" + \\ "\\" + \" "\"" + \b "\b" + \f "\f" + \u (let [ch (read-char rdr)] + (if (== -1 (Character/digit (int ch) 16)) + (err/throw-invalid-unicode-escape rdr ch) + (read-unicode-char rdr ch 16 4 true))) + (if (numeric? ch) + (let [ch (read-unicode-char rdr ch 8 3 false)] + (if (> (int ch) 0377) + (err/throw-bad-octal-number rdr) + ch)) + (err/throw-bad-escape-char rdr ch))))) + +(defn- read-string* + [reader _ opts pending-forms] + (loop [sb (StringBuilder.) + ch (read-char reader)] + (case ch + nil (err/throw-eof-reading reader :string sb) + \\ (recur (doto sb (.append (escape-char sb reader))) + (read-char reader)) + \" (str sb) + (recur (doto sb (.append ch)) (read-char reader))))) + +(defn- read-symbol + [rdr initch] + (let [[line column] (starting-line-col-info rdr)] + (when-let [token (read-token rdr :symbol initch)] + (case token + + ;; special symbols + "nil" nil + "true" true + "false" false + "/" '/ + + (or (when-let [p (parse-symbol token)] + (with-meta (symbol (p 0) (p 1)) + (when line + (merge + (when-let [file (get-file-name rdr)] + {:file file}) + (let [[end-line end-column] (ending-line-col-info rdr)] + {:line line + :column column + :end-line end-line + :end-column end-column}))))) + (err/throw-invalid rdr :symbol token)))))) + +(def ^:dynamic *alias-map* + "Map from ns alias to ns, if non-nil, it will be used to resolve read-time + ns aliases instead of (ns-aliases *ns*). + + Defaults to nil" + nil) + +(defn- resolve-alias [sym] + ((or *alias-map* + (ns-aliases *ns*)) sym)) + +(defn- resolve-ns [sym] + (or (resolve-alias sym) + (find-ns sym))) + +(defn- read-keyword + [reader initch opts pending-forms] + (let [ch (read-char reader)] + (if-not (whitespace? ch) + (let [token (read-token reader :keyword ch) + s (parse-symbol token)] + (if s + (let [^String ns (s 0) + ^String name (s 1)] + (if (identical? \: (nth token 0)) + (if ns + (let [ns (resolve-alias (symbol (subs ns 1)))] + (if ns + (keyword (str ns) name) + (err/throw-invalid reader :keyword (str \: token)))) + (keyword (str *ns*) (subs name 1))) + (keyword ns name))) + (err/throw-invalid reader :keyword (str \: token)))) + (err/throw-single-colon reader)))) + +(defn- wrapping-reader + "Returns a function which wraps a reader in a call to sym" + [sym] + (fn [rdr _ opts pending-forms] + (list sym (read* rdr true nil opts pending-forms)))) + +(defn- read-meta + "Read metadata and return the following object with the metadata applied" + [rdr _ opts pending-forms] + (log-source rdr + (let [[line column] (starting-line-col-info rdr) + m (desugar-meta (read* rdr true nil opts pending-forms))] + (when-not (map? m) + (err/throw-bad-metadata rdr m)) + (let [o (read* rdr true nil opts pending-forms)] + (if (instance? IMeta o) + (let [m (if (and line (seq? o)) + (assoc m :line line :column column) + m)] + (if (instance? IObj o) + (with-meta o (merge (meta o) m)) + (reset-meta! o m))) + (err/throw-bad-metadata-target rdr o)))))) + +(defn- read-set + [rdr _ opts pending-forms] + (let [[start-line start-column] (starting-line-col-info rdr) + ;; subtract 1 from start-column so it includes the # in the leading #{ + start-column (if start-column (int (dec (int start-column)))) + the-set (PersistentHashSet/createWithCheck + (read-delimited :set \} rdr opts pending-forms)) + [end-line end-column] (ending-line-col-info rdr)] + (with-meta the-set + (when start-line + (merge + (when-let [file (get-file-name rdr)] + {:file file}) + {:line start-line + :column start-column + :end-line end-line + :end-column end-column}))))) + +(defn- read-discard + "Read and discard the first object from rdr" + [rdr _ opts pending-forms] + (doto rdr + (read* true nil opts pending-forms))) + +(defn- read-symbolic-value + [rdr _ opts pending-forms] + (let [sym (read* rdr true nil opts pending-forms)] + (case sym + Inf Double/POSITIVE_INFINITY + -Inf Double/NEGATIVE_INFINITY + NaN Double/NaN + (err/reader-error rdr (str "Invalid token: ##" sym))))) + +(def ^:private RESERVED_FEATURES #{:else :none}) + +(defn- has-feature? + [rdr feature opts] + (if (keyword? feature) + (or (= :default feature) (contains? (get opts :features) feature)) + (err/throw-feature-not-keyword rdr feature))) + +;; WIP, move to errors in the future +(defn- check-eof-error + [form rdr ^long first-line] + (when (identical? form READ_EOF) + (err/throw-eof-error rdr (and (< first-line 0) first-line)))) + +(defn- check-reserved-features + [rdr form] + (when (get RESERVED_FEATURES form) + (err/reader-error rdr "Feature name " form " is reserved"))) + +(defn- check-invalid-read-cond + [form rdr ^long first-line] + (when (identical? form READ_FINISHED) + (if (< first-line 0) + (err/reader-error rdr "read-cond requires an even number of forms") + (err/reader-error rdr "read-cond starting on line " first-line " requires an even number of forms")))) + +(defn- read-suppress + "Read next form and suppress. Return nil or READ_FINISHED." + [first-line rdr opts pending-forms] + (binding [*suppress-read* true] + (let [form (read* rdr false READ_EOF \) opts pending-forms)] + (check-eof-error form rdr first-line) + (when (identical? form READ_FINISHED) + READ_FINISHED)))) + +(def ^:private NO_MATCH (Object.)) + +(defn- match-feature + "Read next feature. If matched, read next form and return. + Otherwise, read and skip next form, returning READ_FINISHED or nil." + [first-line rdr opts pending-forms] + (let [feature (read* rdr false READ_EOF \) opts pending-forms)] + (check-eof-error feature rdr first-line) + (if (= feature READ_FINISHED) + READ_FINISHED + (do + (check-reserved-features rdr feature) + (if (has-feature? rdr feature opts) + ;; feature matched, read selected form + (doto (read* rdr false READ_EOF \) opts pending-forms) + (check-eof-error rdr first-line) + (check-invalid-read-cond rdr first-line)) + ;; feature not matched, ignore next form + (or (read-suppress first-line rdr opts pending-forms) + NO_MATCH)))))) + +(defn- read-cond-delimited + [rdr splicing opts pending-forms] + (let [first-line (if (indexing-reader? rdr) (get-line-number rdr) -1) + result (loop [matched NO_MATCH + finished nil] + (cond + ;; still looking for match, read feature+form + (identical? matched NO_MATCH) + (let [match (match-feature first-line rdr opts pending-forms)] + (if (identical? match READ_FINISHED) + READ_FINISHED + (recur match nil))) + + ;; found match, just read and ignore the rest + (not (identical? finished READ_FINISHED)) + (recur matched (read-suppress first-line rdr opts pending-forms)) + + :else + matched))] + (if (identical? result READ_FINISHED) + rdr + (if splicing + (if (instance? List result) + (do + (.addAll ^List pending-forms 0 ^List result) + rdr) + (err/reader-error rdr "Spliced form list in read-cond-splicing must implement java.util.List.")) + result)))) + +(defn- read-cond + [rdr _ opts pending-forms] + (when (not (and opts (#{:allow :preserve} (:read-cond opts)))) + (throw (RuntimeException. "Conditional read not allowed"))) + (if-let [ch (read-char rdr)] + (let [splicing (= ch \@) + ch (if splicing (read-char rdr) ch)] + (when splicing + (when-not *read-delim* + (err/reader-error rdr "cond-splice not in list"))) + (if-let [ch (if (whitespace? ch) (read-past whitespace? rdr) ch)] + (if (not= ch \() + (throw (RuntimeException. "read-cond body must be a list")) + (binding [*suppress-read* (or *suppress-read* (= :preserve (:read-cond opts)))] + (if *suppress-read* + (reader-conditional (read-list rdr ch opts pending-forms) splicing) + (read-cond-delimited rdr splicing opts pending-forms)))) + (err/throw-eof-in-character rdr))) + (err/throw-eof-in-character rdr))) + +(def ^:private ^:dynamic arg-env) + +(defn- garg + "Get a symbol for an anonymous ?argument?" + [^long n] + (symbol (str (if (== -1 n) "rest" (str "p" n)) + "__" (RT/nextID) "#"))) + +(defn- read-fn + [rdr _ opts pending-forms] + (if (thread-bound? #'arg-env) + (throw (IllegalStateException. "Nested #()s are not allowed"))) + (binding [arg-env (sorted-map)] + (let [form (read* (doto rdr (unread \()) true nil opts pending-forms) ;; this sets bindings + rargs (rseq arg-env) + args (if rargs + (let [higharg (long (key ( first rargs)))] + (let [args (loop [i 1 args (transient [])] + (if (> i higharg) + (persistent! args) + (recur (inc i) (conj! args (or (get arg-env i) + (garg i)))))) + args (if (arg-env -1) + (conj args '& (arg-env -1)) + args)] + args)) + [])] + (list 'fn* args form)))) + +(defn- register-arg + "Registers an argument to the arg-env" + [n] + (if (thread-bound? #'arg-env) + (if-let [ret (arg-env n)] + ret + (let [g (garg n)] + (set! arg-env (assoc arg-env n g)) + g)) + (throw (IllegalStateException. "Arg literal not in #()")))) ;; should never hit this + +(declare read-symbol) + +(defn- read-arg + [rdr pct opts pending-forms] + (if-not (thread-bound? #'arg-env) + (read-symbol rdr pct) + (let [ch (peek-char rdr)] + (cond + (or (whitespace? ch) + (macro-terminating? ch) + (nil? ch)) + (register-arg 1) + + (identical? ch \&) + (do (read-char rdr) + (register-arg -1)) + + :else + (let [n (read* rdr true nil opts pending-forms)] + (if-not (integer? n) + (throw (IllegalStateException. "Arg literal must be %, %& or %integer")) + (register-arg n))))))) + +(defn- read-eval + "Evaluate a reader literal" + [rdr _ opts pending-forms] + (when-not *read-eval* + (err/reader-error rdr "#= not allowed when *read-eval* is false")) + (eval (read* rdr true nil opts pending-forms))) + +(def ^:private ^:dynamic gensym-env nil) + +(defn- read-unquote + [rdr comma opts pending-forms] + (if-let [ch (peek-char rdr)] + (if (identical? \@ ch) + ((wrapping-reader 'clojure.core/unquote-splicing) (doto rdr read-char) \@ opts pending-forms) + ((wrapping-reader 'clojure.core/unquote) rdr \~ opts pending-forms)))) + +(declare syntax-quote*) +(defn- unquote-splicing? [form] + (and (seq? form) + (= (first form) 'clojure.core/unquote-splicing))) + +(defn- unquote? [form] + (and (seq? form) + (= (first form) 'clojure.core/unquote))) + +(defn- expand-list + "Expand a list by resolving its syntax quotes and unquotes" + [s] + (loop [s (seq s) r (transient [])] + (if s + (let [item (first s) + ret (conj! r + (cond + (unquote? item) (list 'clojure.core/list (second item)) + (unquote-splicing? item) (second item) + :else (list 'clojure.core/list (syntax-quote* item))))] + (recur (next s) ret)) + (seq (persistent! r))))) + +(defn- flatten-map + "Flatten a map into a seq of alternate keys and values" + [form] + (loop [s (seq form) key-vals (transient [])] + (if s + (let [e (first s)] + (recur (next s) (-> key-vals + (conj! (key e)) + (conj! (val e))))) + (seq (persistent! key-vals))))) + +(defn- register-gensym [sym] + (if-not gensym-env + (throw (IllegalStateException. "Gensym literal not in syntax-quote"))) + (or (get gensym-env sym) + (let [gs (symbol (str (subs (name sym) + 0 (dec (count (name sym)))) + "__" (RT/nextID) "__auto__"))] + (set! gensym-env (assoc gensym-env sym gs)) + gs))) + +(defn ^:dynamic resolve-symbol + "Resolve a symbol s into its fully qualified namespace version" + [s] + (if (pos? (.indexOf (name s) ".")) + (if (.endsWith (name s) ".") + (let [csym (symbol (subs (name s) 0 (dec (count (name s)))))] + (symbol (str (name (resolve-symbol csym)) "."))) + s) + (if-let [ns-str (namespace s)] + (let [ns (resolve-ns (symbol ns-str))] + (if (or (nil? ns) + (= (ns-name* ns) ns-str)) ;; not an alias + s + (symbol (ns-name* ns) (name s)))) + (if-let [o ((ns-map *ns*) s)] + (if (class? o) + (symbol (.getName ^Class o)) + (if (var? o) + (symbol (-> ^Var o .ns ns-name*) (-> ^Var o .sym name)))) + (symbol (ns-name* *ns*) (name s)))))) + +(defn- add-meta [form ret] + (if (and (instance? IObj form) + (seq (dissoc (meta form) :line :column :end-line :end-column :file :source))) + (list 'clojure.core/with-meta ret (syntax-quote* (meta form))) + ret)) + +(defn- syntax-quote-coll [type coll] + ;; We use sequence rather than seq here to fix https://clojure.atlassian.net/browse/CLJ-1444 + ;; But because of https://clojure.atlassian.net/browse/CLJ-1586 we still need to call seq on the form + (let [res (list 'clojure.core/sequence + (list 'clojure.core/seq + (cons 'clojure.core/concat + (expand-list coll))))] + (if type + (list 'clojure.core/apply type res) + res))) + +(defn map-func + "Decide which map type to use, array-map if less than 16 elements" + [coll] + (if (>= (count coll) 16) + 'clojure.core/hash-map + 'clojure.core/array-map)) + +(defn- syntax-quote* [form] + (->> + (cond + (special-symbol? form) (list 'quote form) + + (symbol? form) + (list 'quote + (if (namespace form) + (let [maybe-class ((ns-map *ns*) + (symbol (namespace form)))] + (if (class? maybe-class) + (symbol (.getName ^Class maybe-class) (name form)) + (resolve-symbol form))) + (let [sym (str form)] + (cond + (.endsWith sym "#") + (register-gensym form) + + (.startsWith sym ".") + form + + :else (resolve-symbol form))))) + + (unquote? form) (second form) + (unquote-splicing? form) (throw (IllegalStateException. "unquote-splice not in list")) + + (coll? form) + (cond + + (instance? IRecord form) form + (map? form) (syntax-quote-coll (map-func form) (flatten-map form)) + (vector? form) (list 'clojure.core/vec (syntax-quote-coll nil form)) + (set? form) (syntax-quote-coll 'clojure.core/hash-set form) + (or (seq? form) (list? form)) + (let [seq (seq form)] + (if seq + (syntax-quote-coll nil seq) + '(clojure.core/list))) + + :else (throw (UnsupportedOperationException. "Unknown Collection type"))) + + (or (keyword? form) + (number? form) + (char? form) + (string? form) + (nil? form) + (instance? Boolean form) + (instance? Pattern form)) + form + + :else (list 'quote form)) + (add-meta form))) + +(defn- read-syntax-quote + [rdr backquote opts pending-forms] + (binding [gensym-env {}] + (-> (read* rdr true nil opts pending-forms) + syntax-quote*))) + +(defn- read-namespaced-map + [rdr _ opts pending-forms] + (let [[start-line start-column] (starting-line-col-info rdr) + token (read-token rdr :namespaced-map (read-char rdr))] + (if-let [ns (cond + (= token ":") + (ns-name *ns*) + + (= \: (first token)) + (some-> token (subs 1) parse-symbol second' symbol resolve-ns ns-name) + + :else + (some-> token parse-symbol second'))] + + (let [ch (read-past whitespace? rdr)] + (if (identical? ch \{) + (let [items (read-delimited :namespaced-map \} rdr opts pending-forms) + [end-line end-column] (ending-line-col-info rdr)] + (when (odd? (count items)) + (err/throw-odd-map rdr nil nil items)) + (let [keys (take-nth 2 items) + vals (take-nth 2 (rest items))] + (with-meta + (RT/map (to-array (mapcat list (namespace-keys (str ns) keys) vals))) + (when start-line + (merge + (when-let [file (get-file-name rdr)] + {:file file}) + {:line start-line + :column start-column + :end-line end-line + :end-column end-column}))))) + (err/throw-ns-map-no-map rdr token))) + (err/throw-bad-ns rdr token)))) + +(defn- macros [ch] + (case ch + \" read-string* + \: read-keyword + \; read-comment + \' (wrapping-reader 'quote) + \@ (wrapping-reader 'clojure.core/deref) + \^ read-meta + \` read-syntax-quote ;;(wrapping-reader 'syntax-quote) + \~ read-unquote + \( read-list + \) read-unmatched-delimiter + \[ read-vector + \] read-unmatched-delimiter + \{ read-map + \} read-unmatched-delimiter + \\ read-char* + \% read-arg + \# read-dispatch + nil)) + +(defn- dispatch-macros [ch] + (case ch + \^ read-meta ;deprecated + \' (wrapping-reader 'var) + \( read-fn + \= read-eval + \{ read-set + \< (throwing-reader "Unreadable form") + \" read-regex + \! read-comment + \_ read-discard + \? read-cond + \: read-namespaced-map + \# read-symbolic-value + nil)) + +(defn- read-ctor [rdr class-name opts pending-forms] + (when-not *read-eval* + (err/reader-error rdr "Record construction syntax can only be used when *read-eval* == true")) + (let [class (Class/forName (name class-name) false (RT/baseLoader)) + ch (read-past whitespace? rdr)] ;; differs from clojure + (if-let [[end-ch form] (case ch + \[ [\] :short] + \{ [\} :extended] + nil)] + (let [entries (to-array (read-delimited :record-ctor end-ch rdr opts pending-forms)) + numargs (count entries) + all-ctors (.getConstructors class) + ctors-num (count all-ctors)] + (case form + :short + (loop [i 0] + (if (>= i ctors-num) + (err/reader-error rdr "Unexpected number of constructor arguments to " (str class) + ": got " numargs) + (if (== (count (.getParameterTypes ^Constructor (aget all-ctors i))) + numargs) + (Reflector/invokeConstructor class entries) + (recur (inc i))))) + :extended + (let [vals (RT/map entries)] + (loop [s (keys vals)] + (if s + (if-not (keyword? (first s)) + (err/reader-error rdr "Unreadable ctor form: key must be of type clojure.lang.Keyword") + (recur (next s))))) + (Reflector/invokeStaticMethod class "create" (object-array [vals]))))) + (err/reader-error rdr "Invalid reader constructor form")))) + +(defn- read-tagged [rdr initch opts pending-forms] + (let [tag (read* rdr true nil opts pending-forms)] + (if-not (symbol? tag) + (err/throw-bad-reader-tag rdr tag)) + (if *suppress-read* + (tagged-literal tag (read* rdr true nil opts pending-forms)) + (if-let [f (or (*data-readers* tag) + (default-data-readers tag))] + (f (read* rdr true nil opts pending-forms)) + (if (.contains (name tag) ".") + (read-ctor rdr tag opts pending-forms) + (if-let [f *default-data-reader-fn*] + (f tag (read* rdr true nil opts pending-forms)) + (err/throw-unknown-reader-tag rdr tag))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Public API +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:dynamic *read-eval* + "Defaults to true. + + ***WARNING*** + This setting implies that the full power of the reader is in play, + including syntax that can cause code to execute. It should never be + used with untrusted sources. See also: cljs.vendor.clojure.tools.reader.edn/read. + + When set to logical false in the thread-local binding, + the eval reader (#=) and *record/type literal syntax* are disabled in read/load. + Example (will fail): (binding [*read-eval* false] (read-string \"#=(* 2 21)\")) + + When set to :unknown all reads will fail in contexts where *read-eval* + has not been explicitly bound to either true or false. This setting + can be a useful diagnostic tool to ensure that all of your reads + occur in considered contexts." + true) + +(def ^:dynamic *data-readers* + "Map from reader tag symbols to data reader Vars. + Reader tags without namespace qualifiers are reserved for Clojure. + Default reader tags are defined in cljs.vendor.clojure.tools.reader/default-data-readers + and may be overridden by binding this Var." + {}) + +(def ^:dynamic *default-data-reader-fn* + "When no data reader is found for a tag and *default-data-reader-fn* + is non-nil, it will be called with two arguments, the tag and the value. + If *default-data-reader-fn* is nil (the default value), an exception + will be thrown for the unknown tag." + nil) + +(def ^:dynamic *suppress-read* false) + +(def default-data-readers + "Default map of data reader functions provided by Clojure. + May be overridden by binding *data-readers*" + {'inst #'data-readers/read-instant-date + 'uuid #'data-readers/default-uuid-reader}) + +(defn ^:private read* + ([reader eof-error? sentinel opts pending-forms] + (read* reader eof-error? sentinel nil opts pending-forms)) + ([reader eof-error? sentinel return-on opts pending-forms] + (when (= :unknown *read-eval*) + (err/reader-error "Reading disallowed - *read-eval* bound to :unknown")) + (try + (loop [] + (let [ret (log-source reader + (if (seq pending-forms) + (.remove ^List pending-forms 0) + (let [ch (read-char reader)] + (cond + (whitespace? ch) reader + (nil? ch) (if eof-error? (err/throw-eof-error reader nil) sentinel) + (= ch return-on) READ_FINISHED + (number-literal? reader ch) (read-number reader ch) + :else (if-let [f (macros ch)] + (f reader ch opts pending-forms) + (read-symbol reader ch))))))] + (if (identical? ret reader) + (recur) + ret))) + (catch Exception e + (if (ex-info? e) + (let [d (ex-data e)] + (if (= :reader-exception (:type d)) + (throw e) + (throw (ex-info (.getMessage e) + (merge {:type :reader-exception} + d + (if (indexing-reader? reader) + {:line (get-line-number reader) + :column (get-column-number reader) + :file (get-file-name reader)})) + e)))) + (throw (ex-info (.getMessage e) + (merge {:type :reader-exception} + (if (indexing-reader? reader) + {:line (get-line-number reader) + :column (get-column-number reader) + :file (get-file-name reader)})) + e))))))) + +(defn read + "Reads the first object from an IPushbackReader or a java.io.PushbackReader. + Returns the object read. If EOF, throws if eof-error? is true. + Otherwise returns sentinel. If no stream is provided, *in* will be used. + + Opts is a persistent map with valid keys: + :read-cond - :allow to process reader conditionals, or + :preserve to keep all branches + :features - persistent set of feature keywords for reader conditionals + :eof - on eof, return value unless :eofthrow, then throw. + if not specified, will throw + + ***WARNING*** + Note that read can execute code (controlled by *read-eval*), + and as such should be used only with trusted sources. + + To read data structures only, use cljs.vendor.clojure.tools.reader.edn/read + + Note that the function signature of cljs.vendor.clojure.tools.reader/read and + cljs.vendor.clojure.tools.reader.edn/read is not the same for eof-handling" + {:arglists '([] [reader] [opts reader] [reader eof-error? eof-value])} + ([] (read *in* true nil)) + ([reader] (read reader true nil)) + ([{eof :eof :as opts :or {eof :eofthrow}} reader] + (when (source-logging-reader? reader) + (let [^StringBuilder buf (:buffer @(.source-log-frames ^SourceLoggingPushbackReader reader))] + (.setLength buf 0))) + (read* reader (= eof :eofthrow) eof nil opts (LinkedList.))) + ([reader eof-error? sentinel] + (when (source-logging-reader? reader) + (let [^StringBuilder buf (:buffer @(.source-log-frames ^SourceLoggingPushbackReader reader))] + (.setLength buf 0))) + (read* reader eof-error? sentinel nil {} (LinkedList.)))) + +(defn read-string + "Reads one object from the string s. + Returns nil when s is nil or empty. + + ***WARNING*** + Note that read-string can execute code (controlled by *read-eval*), + and as such should be used only with trusted sources. + + To read data structures only, use cljs.vendor.clojure.tools.reader.edn/read-string + + Note that the function signature of cljs.vendor.clojure.tools.reader/read-string and + cljs.vendor.clojure.tools.reader.edn/read-string is not the same for eof-handling" + ([s] + (read-string {} s)) + ([opts s] + (when (and s (not (identical? s ""))) + (read opts (string-push-back-reader s))))) + +(defmacro syntax-quote + "Macro equivalent to the syntax-quote reader macro (`)." + [form] + (binding [gensym-env {}] + (syntax-quote* form))) + +(defn read+string + "Like read, and taking the same args. reader must be a SourceLoggingPushbackReader. + Returns a vector containing the object read and the (whitespace-trimmed) string read." + ([] (read+string (source-logging-push-back-reader *in*))) + ([stream] (read+string stream true nil)) + ([^SourceLoggingPushbackReader stream eof-error? eof-value] + (let [o (log-source stream (read stream eof-error? eof-value)) + s (.trim (str (:buffer @(.source-log-frames stream))))] + [o s])) + ([opts ^SourceLoggingPushbackReader stream] + (let [o (log-source stream (read opts stream)) + s (.trim (str (:buffer @(.source-log-frames stream))))] + [o s]))) diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader/default_data_readers.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader/default_data_readers.clj new file mode 100644 index 000000000..3a61f9775 --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader/default_data_readers.clj @@ -0,0 +1,303 @@ +; 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. + +;;; copied from clojure.instant and clojure.uuid ;;; + +(ns ^:skip-wiki cljs.vendor.clojure.tools.reader.default-data-readers + (:import [java.util Calendar Date GregorianCalendar TimeZone] + [java.sql Timestamp])) + +;;; clojure.instant ;;; + +;;; ------------------------------------------------------------------------ +;;; convenience macros + +(defmacro ^:private fail + [msg] + `(throw (RuntimeException. ~msg))) + +(defmacro ^:private verify + ([test msg] `(when-not ~test (fail ~msg))) + ([test] `(verify ~test ~(str "failed: " (pr-str test))))) + +(defn- divisible? + [num div] + (zero? (mod num div))) + +(defn- indivisible? + [num div] + (not (divisible? num div))) + + +;;; ------------------------------------------------------------------------ +;;; parser implementation + +(defn- parse-int [^String s] + (Long/parseLong s)) + +(defn- zero-fill-right [^String s width] + (cond (= width (count s)) s + (< width (count s)) (.substring s 0 width) + :else (loop [b (StringBuilder. s)] + (if (< (.length b) width) + (recur (.append b \0)) + (.toString b))))) + +(def parse-timestamp + "Parse a string containing an RFC3339-like like timestamp. + +The function new-instant is called with the following arguments. + + min max default + --- ------------ ------- + years 0 9999 N/A (s must provide years) + months 1 12 1 + days 1 31 1 (actual max days depends + hours 0 23 0 on month and year) + minutes 0 59 0 + seconds 0 60 0 (though 60 is only valid + nanoseconds 0 999999999 0 when minutes is 59) + offset-sign -1 1 0 + offset-hours 0 23 0 + offset-minutes 0 59 0 + +These are all integers and will be non-nil. (The listed defaults +will be passed if the corresponding field is not present in s.) + +Grammar (of s): + + date-fullyear = 4DIGIT + date-month = 2DIGIT ; 01-12 + date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on + ; month/year + time-hour = 2DIGIT ; 00-23 + time-minute = 2DIGIT ; 00-59 + time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second + ; rules + time-secfrac = '.' 1*DIGIT + time-numoffset = ('+' / '-') time-hour ':' time-minute + time-offset = 'Z' / time-numoffset + + time-part = time-hour [ ':' time-minute [ ':' time-second + [time-secfrac] [time-offset] ] ] + + timestamp = date-year [ '-' date-month [ '-' date-mday + [ 'T' time-part ] ] ] + +Unlike RFC3339: + + - we only parse the timestamp format + - timestamp can elide trailing components + - time-offset is optional (defaults to +00:00) + +Though time-offset is syntactically optional, a missing time-offset +will be treated as if the time-offset zero (+00:00) had been +specified. +" + (let [timestamp #"(\d\d\d\d)(?:-(\d\d)(?:-(\d\d)(?:[T](\d\d)(?::(\d\d)(?::(\d\d)(?:[.](\d+))?)?)?)?)?)?(?:[Z]|([-+])(\d\d):(\d\d))?"] + + (fn [new-instant ^CharSequence cs] + (if-let [[_ years months days hours minutes seconds fraction + offset-sign offset-hours offset-minutes] + (re-matches timestamp cs)] + (new-instant + (parse-int years) + (if-not months 1 (parse-int months)) + (if-not days 1 (parse-int days)) + (if-not hours 0 (parse-int hours)) + (if-not minutes 0 (parse-int minutes)) + (if-not seconds 0 (parse-int seconds)) + (if-not fraction 0 (parse-int (zero-fill-right fraction 9))) + (cond (= "-" offset-sign) -1 + (= "+" offset-sign) 1 + :else 0) + (if-not offset-hours 0 (parse-int offset-hours)) + (if-not offset-minutes 0 (parse-int offset-minutes))) + (fail (str "Unrecognized date/time syntax: " cs)))))) + + +;;; ------------------------------------------------------------------------ +;;; Verification of Extra-Grammatical Restrictions from RFC3339 + +(defn- leap-year? + [year] + (and (divisible? year 4) + (or (indivisible? year 100) + (divisible? year 400)))) + +(def ^:private days-in-month + (let [dim-norm [nil 31 28 31 30 31 30 31 31 30 31 30 31] + dim-leap [nil 31 29 31 30 31 30 31 31 30 31 30 31]] + (fn [month leap-year?] + ((if leap-year? dim-leap dim-norm) month)))) + +(defn validated + "Return a function which constructs and instant by calling constructor +after first validating that those arguments are in range and otherwise +plausible. The resulting function will throw an exception if called +with invalid arguments." + [new-instance] + (fn [years months days hours minutes seconds nanoseconds + offset-sign offset-hours offset-minutes] + (verify (<= 1 months 12)) + (verify (<= 1 days (days-in-month months (leap-year? years)))) + (verify (<= 0 hours 23)) + (verify (<= 0 minutes 59)) + (verify (<= 0 seconds (if (= minutes 59) 60 59))) + (verify (<= 0 nanoseconds 999999999)) + (verify (<= -1 offset-sign 1)) + (verify (<= 0 offset-hours 23)) + (verify (<= 0 offset-minutes 59)) + (new-instance years months days hours minutes seconds nanoseconds + offset-sign offset-hours offset-minutes))) + + +;;; ------------------------------------------------------------------------ +;;; print integration + +(def ^:private ^ThreadLocal thread-local-utc-date-format + ;; SimpleDateFormat is not thread-safe, so we use a ThreadLocal proxy for access. + ;; http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4228335 + (proxy [ThreadLocal] [] + (initialValue [] + (doto (java.text.SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ss.SSS-00:00") + ;; RFC3339 says to use -00:00 when the timezone is unknown (+00:00 implies a known GMT) + (.setTimeZone (java.util.TimeZone/getTimeZone "GMT")))))) + +(defn- print-date + "Print a java.util.Date as RFC3339 timestamp, always in UTC." + [^java.util.Date d, ^java.io.Writer w] + (let [utc-format (.get thread-local-utc-date-format)] + (.write w "#inst \"") + (.write w ^String (.format ^java.text.SimpleDateFormat utc-format d)) + (.write w "\""))) + +(defmethod print-method java.util.Date + [^java.util.Date d, ^java.io.Writer w] + (print-date d w)) + +(defmethod print-dup java.util.Date + [^java.util.Date d, ^java.io.Writer w] + (print-date d w)) + +(defn- print-calendar + "Print a java.util.Calendar as RFC3339 timestamp, preserving timezone." + [^java.util.Calendar c, ^java.io.Writer w] + (let [calstr (format "%1$tFT%1$tT.%1$tL%1$tz" c) + offset-minutes (- (.length calstr) 2)] + ;; calstr is almost right, but is missing the colon in the offset + (.write w "#inst \"") + (.write w calstr 0 offset-minutes) + (.write w ":") + (.write w calstr offset-minutes 2) + (.write w "\""))) + +(defmethod print-method java.util.Calendar + [^java.util.Calendar c, ^java.io.Writer w] + (print-calendar c w)) + +(defmethod print-dup java.util.Calendar + [^java.util.Calendar c, ^java.io.Writer w] + (print-calendar c w)) + + +(def ^:private ^ThreadLocal thread-local-utc-timestamp-format + ;; SimpleDateFormat is not thread-safe, so we use a ThreadLocal proxy for access. + ;; http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4228335 + (proxy [ThreadLocal] [] + (initialValue [] + (doto (java.text.SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ss") + (.setTimeZone (java.util.TimeZone/getTimeZone "GMT")))))) + +(defn- print-timestamp + "Print a java.sql.Timestamp as RFC3339 timestamp, always in UTC." + [^java.sql.Timestamp ts, ^java.io.Writer w] + (let [utc-format (.get thread-local-utc-timestamp-format)] + (.write w "#inst \"") + (.write w ^String (.format ^java.text.SimpleDateFormat utc-format ts)) + ;; add on nanos and offset + ;; RFC3339 says to use -00:00 when the timezone is unknown (+00:00 implies a known GMT) + (.write w (format ".%09d-00:00" (.getNanos ts))) + (.write w "\""))) + +(defmethod print-method java.sql.Timestamp + [^java.sql.Timestamp ts, ^java.io.Writer w] + (print-timestamp ts w)) + +(defmethod print-dup java.sql.Timestamp + [^java.sql.Timestamp ts, ^java.io.Writer w] + (print-timestamp ts w)) + + +;;; ------------------------------------------------------------------------ +;;; reader integration + +(defn- construct-calendar + "Construct a java.util.Calendar, preserving the timezone +offset, but truncating the subsecond fraction to milliseconds." + ^GregorianCalendar + [years months days hours minutes seconds nanoseconds + offset-sign offset-hours offset-minutes] + (doto (GregorianCalendar. years (dec months) days hours minutes seconds) + (.set Calendar/MILLISECOND (quot nanoseconds 1000000)) + (.setTimeZone (TimeZone/getTimeZone + (format "GMT%s%02d:%02d" + (if (neg? offset-sign) "-" "+") + offset-hours offset-minutes))))) + +(defn- construct-date + "Construct a java.util.Date, which expresses the original instant as +milliseconds since the epoch, UTC." + [years months days hours minutes seconds nanoseconds + offset-sign offset-hours offset-minutes] + (.getTime (construct-calendar years months days + hours minutes seconds nanoseconds + offset-sign offset-hours offset-minutes))) + +(defn- construct-timestamp + "Construct a java.sql.Timestamp, which has nanosecond precision." + [years months days hours minutes seconds nanoseconds + offset-sign offset-hours offset-minutes] + (doto (Timestamp. + (.getTimeInMillis + (construct-calendar years months days + hours minutes seconds 0 + offset-sign offset-hours offset-minutes))) + ;; nanos must be set separately, pass 0 above for the base calendar + (.setNanos nanoseconds))) + +(def read-instant-date + "To read an instant as a java.util.Date, bind *data-readers* to a map with +this var as the value for the 'inst key. The timezone offset will be used +to convert into UTC." + (partial parse-timestamp (validated construct-date))) + +(def read-instant-calendar + "To read an instant as a java.util.Calendar, bind *data-readers* to a map with +this var as the value for the 'inst key. Calendar preserves the timezone +offset." + (partial parse-timestamp (validated construct-calendar))) + +(def read-instant-timestamp + "To read an instant as a java.sql.Timestamp, bind *data-readers* to a +map with this var as the value for the 'inst key. Timestamp preserves +fractional seconds with nanosecond precision. The timezone offset will +be used to convert into UTC." + (partial parse-timestamp (validated construct-timestamp))) + +;;; clojure.uuid ;;; + +(defn default-uuid-reader [form] + {:pre [(string? form)]} + (java.util.UUID/fromString form)) + +(defmethod print-method java.util.UUID [uuid ^java.io.Writer w] + (.write w (str "#uuid \"" (str uuid) "\""))) + +(defmethod print-dup java.util.UUID [o w] + (print-method o w)) diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader/edn.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader/edn.clj new file mode 100644 index 000000000..2de4ad94a --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader/edn.clj @@ -0,0 +1,440 @@ +;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns ^{:doc "An EDN reader in clojure" + :author "Bronsa"} + cljs.vendor.clojure.tools.reader.edn + (:refer-clojure :exclude [read read-string char default-data-readers]) + (:require [cljs.vendor.clojure.tools.reader.reader-types :refer + [read-char unread peek-char indexing-reader? + get-line-number get-column-number get-file-name string-push-back-reader]] + [cljs.vendor.clojure.tools.reader.impl.utils :refer + [char ex-info? whitespace? numeric? desugar-meta namespace-keys second']] + [cljs.vendor.clojure.tools.reader.impl.commons :refer :all] + [cljs.vendor.clojure.tools.reader.impl.errors :as err] + [cljs.vendor.clojure.tools.reader :refer [default-data-readers]]) + (:import (clojure.lang PersistentHashSet IMeta RT PersistentVector))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; helpers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare read macros dispatch-macros) + +(defn- macro-terminating? [ch] + (and (not (identical? \# ch)) + (not (identical? \' ch)) + (not (identical? \: ch)) + (macros ch))) + +(defn- not-constituent? [ch] + (or (identical? \@ ch) + (identical? \` ch) + (identical? \~ ch))) + +(defn- ^String read-token + ([rdr kind initch] + (read-token rdr kind initch true)) + + ([rdr kind initch validate-leading?] + (cond + (not initch) + (err/throw-eof-at-start rdr kind) + + (and validate-leading? + (not-constituent? initch)) + (err/throw-bad-char rdr kind initch) + + :else + (loop [sb (StringBuilder.) + ch initch] + (if (or (whitespace? ch) + (macro-terminating? ch) + (nil? ch)) + (do (unread rdr ch) + (str sb)) + (if (not-constituent? ch) + (err/throw-bad-char rdr kind ch) + (recur (doto sb (.append ch)) (read-char rdr)))))))) + + + +(declare read-tagged) + +(defn- read-dispatch + [rdr _ opts] + (if-let [ch (read-char rdr)] + (if-let [dm (dispatch-macros ch)] + (dm rdr ch opts) + (read-tagged (doto rdr (unread ch)) ch opts)) + (err/throw-eof-at-dispatch rdr))) + +(defn- read-unmatched-delimiter + [rdr ch opts] + (err/throw-unmatch-delimiter rdr ch)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; readers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defn- read-unicode-char + ([^String token ^long offset ^long length ^long base] + (let [l (+ offset length)] + (when-not (== (count token) l) + (err/throw-invalid-unicode-literal nil token)) + (loop [i offset uc 0] + (if (== i l) + (char uc) + (let [d (Character/digit (int (nth token i)) (int base))] + (if (== d -1) + (err/throw-invalid-unicode-digit-in-token nil (nth token i) token) + (recur (inc i) (long (+ d (* uc base)))))))))) + + ([rdr initch base length exact?] + (let [length (long length) + base (long base)] + (loop [i 1 uc (Character/digit (int initch) (int base))] + (if (== uc -1) + (err/throw-invalid-unicode-digit rdr initch) + (if-not (== i length) + (let [ch (peek-char rdr)] + (if (or (whitespace? ch) + (macros ch) + (nil? ch)) + (if exact? + (err/throw-invalid-unicode-len rdr i length) + (char uc)) + (let [d (Character/digit (int ch) (int base))] + (read-char rdr) + (if (== d -1) + (err/throw-invalid-unicode-digit rdr ch) + (recur (inc i) (long (+ d (* uc base)))))))) + (char uc))))))) + +(def ^:private ^:const upper-limit (int \uD7ff)) +(def ^:private ^:const lower-limit (int \uE000)) + +(defn- read-char* + [rdr backslash opts] + (let [ch (read-char rdr)] + (if-not (nil? ch) + (let [token (if (or (macro-terminating? ch) + (not-constituent? ch) + (whitespace? ch)) + (str ch) + (read-token rdr :character ch false)) + token-len (count token)] + (cond + + (== 1 token-len) (Character/valueOf (nth token 0)) + + (= token "newline") \newline + (= token "space") \space + (= token "tab") \tab + (= token "backspace") \backspace + (= token "formfeed") \formfeed + (= token "return") \return + + (.startsWith token "u") + (let [c (read-unicode-char token 1 4 16) + ic (int c)] + (if (and (> ic upper-limit) + (< ic lower-limit)) + (err/throw-invalid-character-literal rdr (Integer/toString ic 16)) + c)) + + (.startsWith token "o") + (let [len (dec token-len)] + (if (> len 3) + (err/throw-invalid-octal-len rdr token) + (let [uc (read-unicode-char token 1 len 8)] + (if (> (int uc) 0377) + (err/throw-bad-octal-number rdr) + uc)))) + + :else (err/throw-unsupported-character rdr token))) + (err/throw-eof-in-character rdr)))) + +(defn ^:private starting-line-col-info [rdr] + (when (indexing-reader? rdr) + [(get-line-number rdr) (int (dec (int (get-column-number rdr))))])) + +(defn- ^PersistentVector read-delimited + [kind delim rdr opts] + (let [[start-line start-column] (starting-line-col-info rdr) + delim (char delim)] + (loop [a (transient [])] + (let [ch (read-past whitespace? rdr)] + (when-not ch + (err/throw-eof-delimited rdr kind start-line start-column (count a))) + + (if (identical? delim (char ch)) + (persistent! a) + (if-let [macrofn (macros ch)] + (let [mret (macrofn rdr ch opts)] + (recur (if-not (identical? mret rdr) (conj! a mret) a))) + (let [o (read (doto rdr (unread ch)) true nil opts)] + (recur (if-not (identical? o rdr) (conj! a o) a))))))))) + +(defn- read-list + [rdr _ opts] + (let [the-list (read-delimited :list \) rdr opts)] + (if (empty? the-list) + '() + (clojure.lang.PersistentList/create the-list)))) + +(defn- read-vector + [rdr _ opts] + (read-delimited :vector \] rdr opts)) + +(defn- read-map + [rdr _ opts] + (let [[start-line start-column] (starting-line-col-info rdr) + coll (read-delimited :map \} rdr opts) + l (to-array coll)] + (when (== 1 (bit-and (alength l) 1)) + (err/throw-odd-map rdr start-line start-column coll)) + (RT/map l))) + +(defn- read-number + [rdr initch opts] + (loop [sb (doto (StringBuilder.) (.append initch)) + ch (read-char rdr)] + (if (or (whitespace? ch) (macros ch) (nil? ch)) + (let [s (str sb)] + (unread rdr ch) + (or (match-number s) + (err/throw-invalid-number rdr s))) + (recur (doto sb (.append ch)) (read-char rdr))))) + + +(defn- escape-char [sb rdr] + (let [ch (read-char rdr)] + (case ch + \t "\t" + \r "\r" + \n "\n" + \\ "\\" + \" "\"" + \b "\b" + \f "\f" + \u (let [ch (read-char rdr)] + (if (== -1 (Character/digit (int ch) 16)) + (err/throw-invalid-unicode-escape rdr ch) + (read-unicode-char rdr ch 16 4 true))) + (if (numeric? ch) + (let [ch (read-unicode-char rdr ch 8 3 false)] + (if (> (int ch) 0377) + (err/throw-bad-octal-number rdr) + ch)) + (err/throw-bad-escape-char rdr ch))))) + +(defn- read-string* + [rdr _ opts] + (loop [sb (StringBuilder.) + ch (read-char rdr)] + (case ch + nil (err/throw-eof-reading rdr :string \" sb) + \\ (recur (doto sb (.append (escape-char sb rdr))) + (read-char rdr)) + \" (str sb) + (recur (doto sb (.append ch)) (read-char rdr))))) + +(defn- read-symbol + [rdr initch] + (when-let [token (read-token rdr :symbol initch)] + (case token + + ;; special symbols + "nil" nil + "true" true + "false" false + "/" '/ + + (or (when-let [p (parse-symbol token)] + (symbol (p 0) (p 1))) + (err/throw-invalid rdr :symbol token))))) + +(defn- read-keyword + [reader initch opts] + (let [ch (read-char reader)] + (if-not (whitespace? ch) + (let [token (read-token reader :keyword ch) + s (parse-symbol token)] + (if (and s (== -1 (.indexOf token "::"))) + (let [^String ns (s 0) + ^String name (s 1)] + (if (identical? \: (nth token 0)) + (err/throw-invalid reader :keyword (str \: token)) ; No ::kw in edn. + (keyword ns name))) + (err/throw-invalid reader :keyword (str \: token)))) + (err/throw-single-colon reader)))) + +(defn- wrapping-reader + [sym] + (fn [rdr _ opts] + (list sym (read rdr true nil opts)))) + +(defn- read-meta + [rdr _ opts] + (let [m (desugar-meta (read rdr true nil opts))] + (when-not (map? m) + (err/throw-bad-metadata rdr m)) + + (let [o (read rdr true nil opts)] + (if (instance? IMeta o) + (with-meta o (merge (meta o) m)) + (err/throw-bad-metadata-target rdr o))))) + +(defn- read-set + [rdr _ opts] + (PersistentHashSet/createWithCheck (read-delimited :set \} rdr opts))) + +(defn- read-discard + [rdr _ opts] + (doto rdr + (read true nil true))) + +(defn- read-namespaced-map + [rdr _ opts] + (let [token (read-token rdr :namespaced-map (read-char rdr))] + (if-let [ns (some-> token parse-symbol second)] + (let [ch (read-past whitespace? rdr)] + (if (identical? ch \{) + (let [items (read-delimited :namespaced-map \} rdr opts)] + (when (odd? (count items)) + (err/throw-odd-map rdr nil nil items)) + (let [keys (take-nth 2 items) + vals (take-nth 2 (rest items))] + (RT/map (to-array (mapcat list (namespace-keys (str ns) keys) vals))))) + (err/throw-ns-map-no-map rdr token))) + (err/throw-bad-ns rdr token)))) + +(defn- read-symbolic-value + [rdr _ opts] + (let [sym (read rdr true nil opts)] + (case sym + Inf Double/POSITIVE_INFINITY + -Inf Double/NEGATIVE_INFINITY + NaN Double/NaN + (err/reader-error rdr (str "Invalid token: ##" sym))))) + +(defn- macros [ch] + (case ch + \" read-string* + \: read-keyword + \; read-comment + \^ read-meta + \( read-list + \) read-unmatched-delimiter + \[ read-vector + \] read-unmatched-delimiter + \{ read-map + \} read-unmatched-delimiter + \\ read-char* + \# read-dispatch + nil)) + +(defn- dispatch-macros [ch] + (case ch + \^ read-meta ;deprecated + \{ read-set + \< (throwing-reader "Unreadable form") + \! read-comment + \_ read-discard + \: read-namespaced-map + \# read-symbolic-value + nil)) + +(defn- read-tagged [rdr initch opts] + (let [tag (read rdr true nil opts) + object (read rdr true nil opts)] + (if-not (symbol? tag) + (err/throw-bad-reader-tag rdr "Reader tag must be a symbol")) + (if-let [f (or (get (:readers opts) tag) + (default-data-readers tag))] + (f object) + (if-let [d (:default opts)] + (d tag object) + (err/throw-unknown-reader-tag rdr tag))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Public API +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn read + "Reads the first object from an IPushbackReader or a java.io.PushbackReader. + Returns the object read. If EOF, throws if eof-error? is true otherwise returns eof. + If no reader is provided, *in* will be used. + + Reads data in the edn format (subset of Clojure data): + http://edn-format.org + + cljs.vendor.clojure.tools.reader.edn/read doesn't depend on dynamic Vars, all configuration + is done by passing an opt map. + + opts is a map that can include the following keys: + :eof - value to return on end-of-file. When not supplied, eof throws an exception. + :readers - a map of tag symbols to data-reader functions to be considered before default-data-readers. + When not supplied, only the default-data-readers will be used. + :default - A function of two args, that will, if present and no reader is found for a tag, + be called with the tag and the value." + ([] (read *in*)) + ([reader] (read {} reader)) + ([{:keys [eof] :as opts} reader] + (let [eof-error? (not (contains? opts :eof))] + (read reader eof-error? eof opts))) + ([reader eof-error? eof opts] + (try + (loop [] + (let [ch (read-char reader)] + (cond + (whitespace? ch) (recur) + (nil? ch) (if eof-error? (err/throw-eof-error reader nil) eof) + (number-literal? reader ch) (read-number reader ch opts) + :else (let [f (macros ch)] + (if f + (let [res (f reader ch opts)] + (if (identical? res reader) + (recur) + res)) + (read-symbol reader ch)))))) + (catch Exception e + (if (ex-info? e) + (let [d (ex-data e)] + (if (= :reader-exception (:type d)) + (throw e) + (throw (ex-info (.getMessage e) + (merge {:type :reader-exception} + d + (if (indexing-reader? reader) + {:line (get-line-number reader) + :column (get-column-number reader) + :file (get-file-name reader)})) + e)))) + (throw (ex-info (.getMessage e) + (merge {:type :reader-exception} + (if (indexing-reader? reader) + {:line (get-line-number reader) + :column (get-column-number reader) + :file (get-file-name reader)})) + e))))))) + +(defn read-string + "Reads one object from the string s. + Returns nil when s is nil or empty. + + Reads data in the edn format (subset of Clojure data): + http://edn-format.org + + opts is a map as per cljs.vendor.clojure.tools.reader.edn/read" + ([s] (read-string {:eof nil} s)) + ([opts s] + (when (and s (not (identical? s ""))) + (read opts (string-push-back-reader s))))) diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/commons.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/commons.clj new file mode 100644 index 000000000..8162909c2 --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/commons.clj @@ -0,0 +1,131 @@ +;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. +;; 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.vendor.clojure.tools.reader.impl.commons + (:refer-clojure :exclude [char]) + (:require [cljs.vendor.clojure.tools.reader.reader-types :refer [peek-char read-char]] + [cljs.vendor.clojure.tools.reader.impl.errors :refer [reader-error]] + [cljs.vendor.clojure.tools.reader.impl.utils :refer [numeric? newline? char]]) + (:import (clojure.lang BigInt Numbers) + (java.util.regex Pattern Matcher) + java.lang.reflect.Constructor)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; helpers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn number-literal? + "Checks whether the reader is at the start of a number literal" + [reader initch] + (or (numeric? initch) + (and (or (identical? \+ initch) (identical? \- initch)) + (numeric? (peek-char reader))))) + +(defn read-past + "Read until first character that doesn't match pred, returning + char." + [pred rdr] + (loop [ch (read-char rdr)] + (if (pred ch) + (recur (read-char rdr)) + ch))) + +(defn skip-line + "Advances the reader to the end of a line. Returns the reader" + [reader] + (loop [] + (when-not (newline? (read-char reader)) + (recur))) + reader) + +(def ^Pattern int-pattern #"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?") +(def ^Pattern ratio-pattern #"([-+]?[0-9]+)/([0-9]+)") +(def ^Pattern float-pattern #"([-+]?[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?") + +(defn- match-int + [^Matcher m] + (if (.group m 2) + (if (.group m 8) 0N 0) + (let [negate? (= "-" (.group m 1)) + a (cond + (.group m 3) [(.group m 3) 10] + (.group m 4) [(.group m 4) 16] + (.group m 5) [(.group m 5) 8] + (.group m 7) [(.group m 7) (Integer/parseInt (.group m 6))] + :else [nil nil]) + ^String n (a 0)] + (when n + (let [bn (BigInteger. n (int (a 1))) + bn (if negate? (.negate bn) bn)] + (if (.group m 8) + (BigInt/fromBigInteger bn) + (if (< (.bitLength bn) 64) + (.longValue bn) + (BigInt/fromBigInteger bn)))))))) + +(defn- match-ratio + [^Matcher m] + (let [^String numerator (.group m 1) + ^String denominator (.group m 2) + numerator (if (.startsWith numerator "+") + (subs numerator 1) + numerator)] + (/ (-> numerator BigInteger. BigInt/fromBigInteger Numbers/reduceBigInt) + (-> denominator BigInteger. BigInt/fromBigInteger Numbers/reduceBigInt)))) + +(defn- match-float + [^String s ^Matcher m] + (if (.group m 4) + (BigDecimal. ^String (.group m 1)) + (Double/parseDouble s))) + +(defn match-number [^String s] + (let [int-matcher (.matcher int-pattern s)] + (if (.matches int-matcher) + (match-int int-matcher) + (let [float-matcher (.matcher float-pattern s)] + (if (.matches float-matcher) + (match-float s float-matcher) + (let [ratio-matcher (.matcher ratio-pattern s)] + (when (.matches ratio-matcher) + (match-ratio ratio-matcher)))))))) + +(defn parse-symbol + "Parses a string into a vector of the namespace and symbol" + [^String token] + (when-not (or (= "" token) + (.endsWith token ":") + (.startsWith token "::")) + (let [ns-idx (.indexOf token "/")] + (if-let [^String ns (and (pos? ns-idx) + (subs token 0 ns-idx))] + (let [ns-idx (inc ns-idx)] + (when-not (== ns-idx (count token)) + (let [sym (subs token ns-idx)] + (when (and (not (numeric? (nth sym 0))) + (not (= "" sym)) + (not (.endsWith ns ":")) + (or (= sym "/") + (== -1 (.indexOf sym "/")))) + [ns sym])))) + (when (or (= token "/") + (== -1 (.indexOf token "/"))) + [nil token]))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; readers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn read-comment + [rdr & _] + (skip-line rdr)) + +(defn throwing-reader + [msg] + (fn [rdr & _] + (reader-error rdr msg))) diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/errors.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/errors.clj new file mode 100644 index 000000000..862982882 --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/errors.clj @@ -0,0 +1,214 @@ +;; Copyright (c) Russ Olsen, Nicola Mometto, Rich Hickey & contributors. +;; 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.vendor.clojure.tools.reader.impl.errors + (:require [cljs.vendor.clojure.tools.reader.reader-types :as types] + [cljs.vendor.clojure.tools.reader.impl.inspect :as i])) + +(defn- location-details [rdr ex-type] + (let [details {:type :reader-exception + :ex-kind ex-type}] + (if (types/indexing-reader? rdr) + (assoc + details + :file (types/get-file-name rdr) + :line (types/get-line-number rdr) + :col (types/get-column-number rdr)) + details))) + +(defn ^:private throw-ex + [rdr ex-type & msg] + (let [details (location-details rdr ex-type) + file (:file details) + line (:line details) + col (:col details) + msg1 (if file (str file " ")) + msg2 (if line (str "[line " line ", col " col "]")) + msg3 (if (or msg1 msg2) " ") + full-msg (apply str msg1 msg2 msg3 msg)] + (throw (ex-info full-msg details)))) + +(defn reader-error + "Throws an ExceptionInfo with the given message. + If rdr is an IndexingReader, additional information about column and line number is provided" + [rdr & msgs] + (throw-ex rdr :reader-error (apply str msgs))) + +(defn eof-error + "Throws an ExceptionInfo with the given message. + If rdr is an IndexingReader, additional information about column and line number is provided" + [rdr & msgs] + (throw-ex rdr :eof (apply str msgs))) + +(defn illegal-arg-error + "Throws an ExceptionInfo with the given message. + If rdr is an IndexingReader, additional information about column and line number is provided" + [rdr & msgs] + (throw-ex rdr :illegal-argument (apply str msgs))) + +(defn throw-eof-delimited + ([rdr kind line column] (throw-eof-delimited rdr kind line column nil)) + ([rdr kind line column n] + (eof-error + rdr + "Unexpected EOF while reading " + (if n + (str "item " n " of ")) + (name kind) + (if line + (str ", starting at line " line " and column " column)) + "."))) + +(defn throw-odd-map [rdr line col elements] + (reader-error + rdr + "The map literal starting with " + (i/inspect (first elements)) + (if line (str " on line " line " column " col)) + " contains " + (count elements) + " form(s). Map literals must contain an even number of forms.")) + +(defn throw-invalid-number [rdr token] + (reader-error + rdr + "Invalid number: " + token + ".")) + +(defn throw-invalid-unicode-literal [rdr token] + (throw + (illegal-arg-error rdr + "Invalid unicode literal: \\" token "."))) + +(defn throw-invalid-unicode-escape [rdr ch] + (reader-error + rdr + "Invalid unicode escape: \\u" + ch + ".")) + +(defn throw-invalid [rdr kind token] + (reader-error rdr "Invalid " (name kind) ": " token ".")) + +(defn throw-eof-at-start [rdr kind] + (eof-error rdr "Unexpected EOF while reading start of " (name kind) ".")) + +(defn throw-bad-char [rdr kind ch] + (reader-error rdr "Invalid character: " ch " found while reading " (name kind) ".")) + +(defn throw-eof-at-dispatch [rdr] + (eof-error rdr "Unexpected EOF while reading dispatch character.")) + +(defn throw-unmatch-delimiter [rdr ch] + (reader-error rdr "Unmatched delimiter " ch ".")) + +(defn throw-eof-reading [rdr kind & start] + (let [init (case kind :regex "#\"" :string \")] + (eof-error rdr "Unexpected EOF reading " (name kind) " starting " (apply str init start) "."))) + +(defn throw-invalid-unicode-char[rdr token] + (throw + (illegal-arg-error rdr + "Invalid unicode character \\" token "."))) + +(defn throw-invalid-unicode-digit-in-token [rdr ch token] + (throw + (illegal-arg-error rdr + "Invalid digit " ch " in unicode character \\" token "."))) + +(defn throw-invalid-unicode-digit[rdr ch] + (throw + (illegal-arg-error rdr + "Invalid digit " ch " in unicode character."))) + +(defn throw-invalid-unicode-len[rdr actual expected] + (throw + (illegal-arg-error rdr + "Invalid unicode literal. Unicode literals should be " + expected + " characters long. " + "Value supplied is " + actual + " characters long."))) + +(defn throw-invalid-character-literal[rdr token] + (reader-error rdr "Invalid character literal \\u" token ".")) + +(defn throw-invalid-octal-len[rdr token] + (reader-error + rdr + "Invalid octal escape sequence in a character literal: " + token + ". Octal escape sequences must be 3 or fewer digits.")) + +(defn throw-bad-octal-number [rdr] + (reader-error rdr "Octal escape sequence must be in range [0, 377].")) + +(defn throw-unsupported-character[rdr token] + (reader-error + rdr + "Unsupported character: " + token + ".")) + +(defn throw-eof-in-character[rdr] + (eof-error rdr "Unexpected EOF while reading character.")) + +(defn throw-bad-escape-char [rdr ch] + (reader-error rdr "Unsupported escape character: \\" ch ".")) + +(defn throw-single-colon [rdr] + (reader-error rdr "A single colon is not a valid keyword.")) + +(defn throw-bad-metadata [rdr x] + (reader-error + rdr + "Metadata cannot be " + (i/inspect x) + ". Metadata must be a Symbol, Keyword, String or Map.")) + +(defn throw-bad-metadata-target [rdr target] + (reader-error + rdr + "Metadata can not be applied to " + (i/inspect target) + ". " + "Metadata can only be applied to IMetas.")) + +(defn throw-feature-not-keyword [rdr feature] + (reader-error + rdr + "Feature cannot be " + (i/inspect feature) + ". Features must be keywords.")) + +(defn throw-ns-map-no-map [rdr ns-name] + (reader-error rdr "Namespaced map with namespace " ns-name " does not specify a map.")) + +(defn throw-bad-ns [rdr ns-name] + (reader-error rdr "Invalid value used as namespace in namespaced map: " ns-name ".")) + +(defn throw-bad-reader-tag [rdr tag] + (reader-error + rdr + "Invalid reader tag: " + (i/inspect tag) + ". Reader tags must be symbols.")) + +(defn throw-unknown-reader-tag [rdr tag] + (reader-error + rdr + "No reader function for tag " + (i/inspect tag) + ".")) + +(defn throw-eof-error [rdr line] + (if line + (eof-error rdr "EOF while reading, starting at line " line ".") + (eof-error rdr "EOF while reading."))) diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/inspect.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/inspect.clj new file mode 100644 index 000000000..cd7be5641 --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/inspect.clj @@ -0,0 +1,91 @@ +;; Copyright (c) Russ Olsen, Nicola Mometto, Rich Hickey & contributors. +;; 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.vendor.clojure.tools.reader.impl.inspect) + +(declare inspect*) + +(defn- inspect*-col [truncate col start end] + (let [n (count col) + l (if truncate 0 (min 10 n)) + elements (map (partial inspect* true) (take l col)) + content (apply str (interpose " " elements)) + suffix (if (< l n) "...")] + (str start content suffix end))) + +(defn- dispatch-inspect + [_ x] + (cond + (nil? x) :nil + (string? x) :string + (keyword? x) :strable + (number? x) :strable + (symbol? x) :strable + (vector? x) :vector + (list? x) :list + (map? x) :map + (set? x) :set + (= x true) :strable + (= x false) :strable + :default (class x))) + +(defmulti inspect* dispatch-inspect) + +(defmethod inspect* :string [truncate ^String x] + (let [n (if truncate 5 20) + suffix (if (> (.length x) n) "...\"" "\"")] + (str + \" + (.substring ^String x 0 (min n (.length x))) + suffix))) + +(defmethod inspect* :strable [truncate x] (str x)) + +(defmethod inspect* clojure.lang.PersistentVector$ChunkedSeq [truncate x] + "") + +(defmethod inspect* clojure.lang.PersistentArrayMap$Seq [truncate x] + "") + +(defmethod inspect* clojure.lang.PersistentHashMap$NodeSeq [truncate x] + "") + +(defmethod inspect* clojure.lang.Cons [truncate x] "") + +(defmethod inspect* clojure.lang.LazySeq [truncate x] "") + +(defmethod inspect* :nil [_ _] "nil") + +(defmethod inspect* :list [truncate col] + (inspect*-col truncate col \( \))) + +(defmethod inspect* :map [truncate m] + (let [len (count m) + n-shown (if truncate 0 len) + contents (apply concat (take n-shown m)) + suffix (if (> len n-shown) "...}" \})] + (inspect*-col truncate contents \{ suffix))) + +(defmethod inspect* :set [truncate col] + (inspect*-col truncate col "#{" \})) + +(defmethod inspect* :vector [truncate col] + (inspect*-col truncate col \[ \])) + +(defmethod inspect* :default [truncate x] + (let [classname (if (nil? x) "nil" (.getName (class x)))] + (str "<" classname ">"))) + +(defn inspect + "Return a string description of the value supplied. + May be the a string version of the value itself (e.g. \"true\") + or it may be a description (e.g. \"an instance of Foo\"). + If truncate is true then return a very terse version of + the inspection." + ([x] (inspect* false x)) + ([truncate x] (inspect* truncate x))) diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/utils.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/utils.clj new file mode 100644 index 000000000..0b814e8e7 --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader/impl/utils.clj @@ -0,0 +1,127 @@ +;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. +;; 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 ^:skip-wiki cljs.vendor.clojure.tools.reader.impl.utils + (:refer-clojure :exclude [char reader-conditional tagged-literal])) + +(defn char [x] + (when x + (clojure.core/char x))) + +(def <=clojure-1-7-alpha5 + (let [{:keys [minor qualifier]} *clojure-version*] + (or (< minor 7) + (and (= minor 7) + (= "alpha" + (when qualifier + (subs qualifier 0 (dec (count qualifier))))) + (<= (read-string (subs qualifier (dec (count qualifier)))) + 5))))) + +(defmacro compile-when [cond & then] + (when (eval cond) + `(do ~@then))) + +(defn ex-info? [ex] + (instance? clojure.lang.ExceptionInfo ex)) + +(compile-when <=clojure-1-7-alpha5 + (defrecord TaggedLiteral [tag form]) + + (defn tagged-literal? + "Return true if the value is the data representation of a tagged literal" + [value] + (instance? cljs.vendor.clojure.tools.reader.impl.utils.TaggedLiteral value)) + + (defn tagged-literal + "Construct a data representation of a tagged literal from a + tag symbol and a form." + [tag form] + (cljs.vendor.clojure.tools.reader.impl.utils.TaggedLiteral. tag form)) + + (ns-unmap *ns* '->TaggedLiteral) + (ns-unmap *ns* 'map->TaggedLiteral) + + (defmethod print-method cljs.vendor.clojure.tools.reader.impl.utils.TaggedLiteral [o ^java.io.Writer w] + (.write w "#") + (print-method (:tag o) w) + (.write w " ") + (print-method (:form o) w)) + + (defrecord ReaderConditional [splicing? form]) + (ns-unmap *ns* '->ReaderConditional) + (ns-unmap *ns* 'map->ReaderConditional) + + (defn reader-conditional? + "Return true if the value is the data representation of a reader conditional" + [value] + (instance? cljs.vendor.clojure.tools.reader.impl.utils.ReaderConditional value)) + + (defn reader-conditional + "Construct a data representation of a reader conditional. + If true, splicing? indicates read-cond-splicing." + [form splicing?] + (cljs.vendor.clojure.tools.reader.impl.utils.ReaderConditional. splicing? form)) + + (defmethod print-method cljs.vendor.clojure.tools.reader.impl.utils.ReaderConditional [o ^java.io.Writer w] + (.write w "#?") + (when (:splicing? o) (.write w "@")) + (print-method (:form o) w))) + +(defn whitespace? + "Checks whether a given character is whitespace" + [ch] + (when ch + (or (Character/isWhitespace ^Character ch) + (identical? \, ch)))) + +(defn numeric? + "Checks whether a given character is numeric" + [^Character ch] + (when ch + (Character/isDigit ch))) + +(defn newline? + "Checks whether the character is a newline" + [c] + (or (identical? \newline c) + (nil? c))) + +(defn desugar-meta + "Resolves syntactical sugar in metadata" ;; could be combined with some other desugar? + [f] + (cond + (keyword? f) {f true} + (symbol? f) {:tag f} + (string? f) {:tag f} + :else f)) + +(defn make-var + "Returns an anonymous unbound Var" + [] + (with-local-vars [x nil] x)) + +(defn namespace-keys [ns keys] + (for [key keys] + (if (or (symbol? key) + (keyword? key)) + (let [[key-ns key-name] ((juxt namespace name) key) + ->key (if (symbol? key) symbol keyword)] + (cond + (nil? key-ns) + (->key ns key-name) + + (= "_" key-ns) + (->key key-name) + + :else + key)) + key))) + +(defn second' [[a b]] + (when-not a b)) diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj new file mode 100644 index 000000000..8be8cd96c --- /dev/null +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj @@ -0,0 +1,431 @@ +;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns ^{:doc "Protocols and default Reader types implementation" + :author "Bronsa"} + cljs.vendor.clojure.tools.reader.reader-types + (:refer-clojure :exclude [char read-line]) + (:require [cljs.vendor.clojure.tools.reader.impl.utils :refer [char whitespace? newline? make-var]]) + (:import clojure.lang.LineNumberingPushbackReader + (java.io InputStream BufferedReader Closeable))) + +(defmacro ^:private update! [what f] + (list 'set! what (list f what))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; reader protocols +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defprotocol Reader + (read-char [reader] + "Returns the next char from the Reader, nil if the end of stream has been reached") + (peek-char [reader] + "Returns the next char from the Reader without removing it from the reader stream")) + +(defprotocol IPushbackReader + (unread [reader ch] + "Pushes back a single character on to the stream")) + +(defprotocol IndexingReader + (get-line-number [reader] + "Returns the line number of the next character to be read from the stream") + (get-column-number [reader] + "Returns the column number of the next character to be read from the stream") + (get-file-name [reader] + "Returns the file name the reader is reading from, or nil")) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; reader deftypes +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(deftype StringReader + [^String s ^long s-len ^:unsynchronized-mutable ^long s-pos] + Reader + (read-char [reader] + (when (> s-len s-pos) + (let [r (nth s s-pos)] + (update! s-pos inc) + r))) + (peek-char [reader] + (when (> s-len s-pos) + (nth s s-pos)))) + +(deftype InputStreamReader [^InputStream is ^:unsynchronized-mutable ^"[B" buf] + Reader + (read-char [reader] + (if buf + (let [c (aget buf 0)] + (set! buf nil) + (char c)) + (let [c (.read is)] + (when (>= c 0) + (char c))))) + (peek-char [reader] + (when-not buf + (set! buf (byte-array 1)) + (when (== -1 (.read is buf)) + (set! buf nil))) + (when buf + (char (aget buf 0)))) + Closeable + (close [this] + (.close is))) + +(deftype PushbackReader + [rdr ^"[Ljava.lang.Object;" buf ^long buf-len ^:unsynchronized-mutable ^long buf-pos] + Reader + (read-char [reader] + (char + (if (< buf-pos buf-len) + (let [r (aget buf buf-pos)] + (update! buf-pos inc) + r) + (read-char rdr)))) + (peek-char [reader] + (char + (if (< buf-pos buf-len) + (aget buf buf-pos) + (peek-char rdr)))) + IPushbackReader + (unread [reader ch] + (when ch + (if (zero? buf-pos) (throw (RuntimeException. "Pushback buffer is full"))) + (update! buf-pos dec) + (aset buf buf-pos ch))) + Closeable + (close [this] + (when (instance? Closeable rdr) + (.close ^Closeable rdr)))) + +(deftype IndexingPushbackReader + [rdr ^:unsynchronized-mutable ^long line ^:unsynchronized-mutable ^long column + ^:unsynchronized-mutable line-start? ^:unsynchronized-mutable prev + ^:unsynchronized-mutable ^long prev-column file-name + ^:unsynchronized-mutable normalize?] + Reader + (read-char [reader] + (when-let [ch (read-char rdr)] + (let [ch (if normalize? + (do (set! normalize? false) + (if (or (identical? \newline ch) + (identical? \formfeed ch)) + (read-char rdr) + ch)) + ch) + ch (if (identical? \return ch) + (do (set! normalize? true) + \newline) + ch)] + (set! prev line-start?) + (set! line-start? (newline? ch)) + (when line-start? + (set! prev-column column) + (set! column 0) + (update! line inc)) + (update! column inc) + ch))) + + (peek-char [reader] + (peek-char rdr)) + + IPushbackReader + (unread [reader ch] + (if line-start? + (do (update! line dec) + (set! column prev-column)) + (update! column dec)) + (set! line-start? prev) + ;; This may look a bit convoluted, but it helps in the following + ;; scenario: + ;; + The underlying reader is about to return \return from the + ;; next read-char, and then \newline after that. + ;; + read-char gets \return, sets normalize? to true, returns + ;; \newline instead. + ;; + Caller calls unread on the \newline it just got. If we + ;; unread the \newline to the underlying reader, now it is ready + ;; to return two \newline chars in a row, which will throw off + ;; the tracked line numbers. + (let [ch (if normalize? + (do (set! normalize? false) + (if (identical? \newline ch) + \return + ch)) + ch)] + (unread rdr ch))) + + IndexingReader + (get-line-number [reader] (int line)) + (get-column-number [reader] (int column)) + (get-file-name [reader] file-name) + + Closeable + (close [this] + (when (instance? Closeable rdr) + (.close ^Closeable rdr)))) + +;; Java interop + +(extend-type java.io.PushbackReader + Reader + (read-char [rdr] + (let [c (.read ^java.io.PushbackReader rdr)] + (when (>= c 0) + (char c)))) + + (peek-char [rdr] + (when-let [c (read-char rdr)] + (unread rdr c) + c)) + + IPushbackReader + (unread [rdr c] + (when c + (.unread ^java.io.PushbackReader rdr (int c))))) + +(extend LineNumberingPushbackReader + IndexingReader + {:get-line-number (fn [rdr] (.getLineNumber ^LineNumberingPushbackReader rdr)) + :get-column-number (fn [rdr] + (.getColumnNumber ^LineNumberingPushbackReader rdr)) + :get-file-name (constantly nil)}) + +(defprotocol ReaderCoercer + (to-rdr [rdr])) + +(declare string-reader push-back-reader) + +(extend-protocol ReaderCoercer + Object + (to-rdr [rdr] + (if (satisfies? Reader rdr) + rdr + (throw (IllegalArgumentException. (str "Argument of type: " (class rdr) " cannot be converted to Reader"))))) + cljs.vendor.clojure.tools.reader.reader_types.Reader + (to-rdr [rdr] rdr) + String + (to-rdr [str] (string-reader str)) + java.io.Reader + (to-rdr [rdr] (java.io.PushbackReader. rdr))) + +(defprotocol PushbackReaderCoercer + (to-pbr [rdr buf-len])) + +(extend-protocol PushbackReaderCoercer + Object + (to-pbr [rdr buf-len] + (if (satisfies? Reader rdr) + (push-back-reader rdr buf-len) + (throw (IllegalArgumentException. (str "Argument of type: " (class rdr) " cannot be converted to IPushbackReader"))))) + cljs.vendor.clojure.tools.reader.reader_types.Reader + (to-pbr [rdr buf-len] (push-back-reader rdr buf-len)) + cljs.vendor.clojure.tools.reader.reader_types.PushbackReader + (to-pbr [rdr buf-len] (push-back-reader rdr buf-len)) + String + (to-pbr [str buf-len] (push-back-reader str buf-len)) + java.io.Reader + (to-pbr [rdr buf-len] (java.io.PushbackReader. rdr buf-len))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Source Logging support +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defn merge-meta + "Returns an object of the same type and value as `obj`, with its + metadata merged over `m`." + [obj m] + (let [orig-meta (meta obj)] + (with-meta obj (merge m (dissoc orig-meta :source))))) + +(defn- peek-source-log + "Returns a string containing the contents of the top most source + logging frame." + [source-log-frames] + (let [current-frame @source-log-frames] + (.substring ^StringBuilder (:buffer current-frame) (:offset current-frame)))) + +(defn- log-source-char + "Logs `char` to all currently active source logging frames." + [source-log-frames char] + (when-let [^StringBuilder buffer (:buffer @source-log-frames)] + (.append buffer char))) + +(defn- drop-last-logged-char + "Removes the last logged character from all currently active source + logging frames. Called when pushing a character back." + [source-log-frames] + (when-let [^StringBuilder buffer (:buffer @source-log-frames)] + (.deleteCharAt buffer (dec (.length buffer))))) + +(deftype SourceLoggingPushbackReader + [rdr ^:unsynchronized-mutable ^long line ^:unsynchronized-mutable ^long column + ^:unsynchronized-mutable line-start? ^:unsynchronized-mutable prev + ^:unsynchronized-mutable ^long prev-column file-name source-log-frames + ^:unsynchronized-mutable normalize?] + Reader + (read-char [reader] + (when-let [ch (read-char rdr)] + (let [ch (if normalize? + (do (set! normalize? false) + (if (or (identical? \newline ch) + (identical? \formfeed ch)) + (read-char rdr) + ch)) + ch) + ch (if (identical? \return ch) + (do (set! normalize? true) + \newline) + ch)] + (set! prev line-start?) + (set! line-start? (newline? ch)) + (when line-start? + (set! prev-column column) + (set! column 0) + (update! line inc)) + (update! column inc) + (log-source-char source-log-frames ch) + ch))) + + (peek-char [reader] + (peek-char rdr)) + + IPushbackReader + (unread [reader ch] + (if line-start? + (do (update! line dec) + (set! column prev-column)) + (update! column dec)) + (set! line-start? prev) + (when ch + (drop-last-logged-char source-log-frames)) + (unread rdr ch)) + + IndexingReader + (get-line-number [reader] (int line)) + (get-column-number [reader] (int column)) + (get-file-name [reader] file-name) + + Closeable + (close [this] + (when (instance? Closeable rdr) + (.close ^Closeable rdr)))) + +(defn log-source* + [reader f] + (let [frame (.source-log-frames ^SourceLoggingPushbackReader reader) + ^StringBuilder buffer (:buffer @frame) + new-frame (assoc-in @frame [:offset] (.length buffer))] + (with-bindings {frame new-frame} + (let [ret (f)] + (if (instance? clojure.lang.IObj ret) + (merge-meta ret {:source (peek-source-log frame)}) + ret))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Public API +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; fast check for provided implementations +(defn indexing-reader? + "Returns true if the reader satisfies IndexingReader" + [rdr] + (or (instance? cljs.vendor.clojure.tools.reader.reader_types.IndexingReader rdr) + (instance? LineNumberingPushbackReader rdr) + (and (not (instance? cljs.vendor.clojure.tools.reader.reader_types.PushbackReader rdr)) + (not (instance? cljs.vendor.clojure.tools.reader.reader_types.StringReader rdr)) + (not (instance? cljs.vendor.clojure.tools.reader.reader_types.InputStreamReader rdr)) + (get (:impls IndexingReader) (class rdr))))) + +(defn string-reader + "Creates a StringReader from a given string" + ([^String s] + (StringReader. s (count s) 0))) + +(defn ^Closeable push-back-reader + "Creates a PushbackReader from a given reader or string" + ([rdr] (push-back-reader rdr 1)) + ([rdr buf-len] (PushbackReader. (to-rdr rdr) (object-array buf-len) buf-len buf-len))) + +(defn ^Closeable string-push-back-reader + "Creates a PushbackReader from a given string" + ([s] + (string-push-back-reader s 1)) + ([^String s buf-len] + (push-back-reader (string-reader s) buf-len))) + +(defn ^Closeable input-stream-reader + "Creates an InputStreamReader from an InputStream" + [is] + (InputStreamReader. is nil)) + +(defn ^Closeable input-stream-push-back-reader + "Creates a PushbackReader from a given InputStream" + ([is] + (input-stream-push-back-reader is 1)) + ([^InputStream is buf-len] + (push-back-reader (input-stream-reader is) buf-len))) + +(defn ^Closeable indexing-push-back-reader + "Creates an IndexingPushbackReader from a given string or PushbackReader" + ([s-or-rdr] + (indexing-push-back-reader s-or-rdr 1)) + ([s-or-rdr buf-len] + (indexing-push-back-reader s-or-rdr buf-len nil)) + ([s-or-rdr buf-len file-name] + (IndexingPushbackReader. + (to-pbr s-or-rdr buf-len) 1 1 true nil 0 file-name false))) + +(defn ^Closeable source-logging-push-back-reader + "Creates a SourceLoggingPushbackReader from a given string or PushbackReader" + ([s-or-rdr] + (source-logging-push-back-reader s-or-rdr 1)) + ([s-or-rdr buf-len] + (source-logging-push-back-reader s-or-rdr buf-len nil)) + ([s-or-rdr buf-len file-name] + (SourceLoggingPushbackReader. + (to-pbr s-or-rdr buf-len) + 1 + 1 + true + nil + 0 + file-name + (doto (make-var) + (alter-var-root (constantly {:buffer (StringBuilder.) + :offset 0}))) + false))) + +(defn read-line + "Reads a line from the reader or from *in* if no reader is specified" + ([] (read-line *in*)) + ([rdr] + (if (or (instance? LineNumberingPushbackReader rdr) + (instance? BufferedReader rdr)) + (binding [*in* rdr] + (clojure.core/read-line)) + (loop [c (read-char rdr) s (StringBuilder.)] + (if (newline? c) + (str s) + (recur (read-char rdr) (.append s c))))))) + +(defn source-logging-reader? + [rdr] + (instance? SourceLoggingPushbackReader rdr)) + +(defmacro log-source + "If reader is a SourceLoggingPushbackReader, execute body in a source + logging context. Otherwise, execute body, returning the result." + [reader & body] + `(if (and (source-logging-reader? ~reader) + (not (whitespace? (peek-char ~reader)))) + (log-source* ~reader (^:once fn* [] ~@body)) + (do ~@body))) + +(defn line-start? + "Returns true if rdr is an IndexingReader and the current char starts a new line" + [rdr] + (when (indexing-reader? rdr) + (== 1 (int (get-column-number rdr))))) diff --git a/src/main/clojure/cljs/vendor/cognitect/transit.clj b/src/main/clojure/cljs/vendor/cognitect/transit.clj new file mode 100644 index 000000000..43fa0da96 --- /dev/null +++ b/src/main/clojure/cljs/vendor/cognitect/transit.clj @@ -0,0 +1,479 @@ +;; Copyright 2014 Rich Hickey. All Rights Reserved. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS-IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +(ns cljs.vendor.cognitect.transit + "An implementation of the transit-format for Clojure built + on top of the transit-java library." + (:refer-clojure :exclude [read]) + (:require [clojure.string :as str]) + (:import [com.cognitect.transit WriteHandler ReadHandler ArrayReadHandler MapReadHandler + ArrayReader TransitFactory TransitFactory$Format MapReader] + [com.cognitect.transit.SPI ReaderSPI] + [java.io InputStream OutputStream] + [java.util.function Function])) + +(defprotocol HandlerMapProvider + (handler-map [this])) + +(deftype HandlerMapContainer [m] + HandlerMapProvider + (handler-map [this] m)) + +;; writing + +(set! *warn-on-reflection* true) + +(defn- transit-format + "Converts a keyword to a TransitFactory$Format value." + [kw] + (TransitFactory$Format/valueOf + (str/join "_" (-> kw + name + str/upper-case + (str/split #"-"))))) + +(defn tagged-value + "Creates a TaggedValue object." + [tag rep] (TransitFactory/taggedValue tag rep)) + +(defn nsed-name + "Convert a keyword or symbol to a string in + namespace/name format." + [^clojure.lang.Named kw-or-sym] + (if-let [ns (.getNamespace kw-or-sym)] + (str ns "/" (.getName kw-or-sym)) + (.getName kw-or-sym))) + +(defn- fn-or-val + [f] + (if (fn? f) f (constantly f))) + +(defn write-handler + "Creates a transit WriteHandler whose tag, rep, + stringRep, and verboseWriteHandler methods + invoke the provided fns. + + If a non-fn is passed as an argument, implemented + handler method returns the value unaltered." + ([tag-fn rep-fn] + (write-handler tag-fn rep-fn nil nil)) + ([tag-fn rep-fn str-rep-fn] + (write-handler tag-fn rep-fn str-rep-fn nil)) + ([tag-fn rep-fn str-rep-fn verbose-handler-fn] + (let [tag-fn (fn-or-val tag-fn) + rep-fn (fn-or-val rep-fn) + str-rep-fn (fn-or-val str-rep-fn) + verbose-handler-fn (fn-or-val verbose-handler-fn)] + (reify WriteHandler + (tag [_ o] (tag-fn o)) + (rep [_ o] (rep-fn o)) + (stringRep [_ o] (when str-rep-fn (str-rep-fn o))) + (getVerboseHandler [_] (when verbose-handler-fn (verbose-handler-fn))))))) + +(deftype WithMeta [value meta]) + +(def default-write-handlers + "Returns a map of default WriteHandlers for + Clojure types. Java types are handled + by the default WriteHandlers provided by the + transit-java library." + { + java.util.List + (reify WriteHandler + (tag [_ l] (if (seq? l) "list" "array")) + (rep [_ l] (if (seq? l) (TransitFactory/taggedValue "array" l) l)) + (stringRep [_ _] nil) + (getVerboseHandler [_] nil)) + + clojure.lang.BigInt + (reify WriteHandler + (tag [_ _] "n") + (rep [_ bi] (str (biginteger bi))) + (stringRep [this bi] (.rep this bi)) + (getVerboseHandler [_] nil)) + + clojure.lang.Keyword + (reify WriteHandler + (tag [_ _] ":") + (rep [_ kw] (nsed-name kw)) + (stringRep [_ kw] (nsed-name kw)) + (getVerboseHandler [_] nil)) + + clojure.lang.Ratio + (reify WriteHandler + (tag [_ _] "ratio") + (rep [_ r] (TransitFactory/taggedValue "array" [(numerator r) (denominator r)])) + (stringRep [_ _] nil) + (getVerboseHandler [_] nil)) + + clojure.lang.Symbol + (reify WriteHandler + (tag [_ _] "$") + (rep [_ sym] (nsed-name sym)) + (stringRep [_ sym] (nsed-name sym)) + (getVerboseHandler [_] nil)) + + cljs.vendor.cognitect.transit.WithMeta + (reify WriteHandler + (tag [_ _] "with-meta") + (rep [_ o] + (TransitFactory/taggedValue "array" + [(.-value ^cljs.vendor.cognitect.transit.WithMeta o) + (.-meta ^cljs.vendor.cognitect.transit.WithMeta o)])) + (stringRep [_ _] nil) + (getVerboseHandler [_] nil))}) + +(deftype Writer [w]) + +(defn writer + "Creates a writer over the provided destination `out` using + the specified format, one of: :msgpack, :json or :json-verbose. + + An optional opts map may be passed. Supported options are: + + :handlers - a map of types to WriteHandler instances, they are merged + with the default-handlers and then with the default handlers + provided by transit-java. + + :default-handler - a default WriteHandler to use if NO handler is + found for a type. If no default is specified, an error will be + thrown for an unknown type. + + :transform - a function of one argument that will transform values before + they are written." + ([out type] (writer out type {})) + ([^OutputStream out type {:keys [handlers default-handler transform]}] + (if (#{:json :json-verbose :msgpack} type) + (let [handler-map (if (instance? HandlerMapContainer handlers) + (handler-map handlers) + (merge default-write-handlers handlers))] + (Writer. (TransitFactory/writer (transit-format type) out handler-map default-handler + (when transform + (reify Function + (apply [_ x] + (transform x))))))) + (throw (ex-info "Type must be :json, :json-verbose or :msgpack" {:type type}))))) + +(defn write + "Writes a value to a transit writer." + [^Writer writer o] + (.write ^com.cognitect.transit.Writer (.w writer) o)) + + +;; reading + +(defn read-handler + "Creates a transit ReadHandler whose fromRep + method invokes the provided fn." + [from-rep] + (reify ReadHandler + (fromRep [_ o] (from-rep o)))) + +(defn read-map-handler + "Creates a Transit MapReadHandler whose fromRep + and mapReader methods invoke the provided fns." + [from-rep map-reader] + (reify MapReadHandler + (fromRep [_ o] (from-rep o)) + (mapReader [_] (map-reader)))) + +(defn read-array-handler + "Creates a Transit ArrayReadHandler whose fromRep + and arrayReader methods invoke the provided fns." + [from-rep array-reader] + (reify ArrayReadHandler + (fromRep [_ o] (from-rep o)) + (arrayReader [_] (array-reader)))) + + +(def default-read-handlers + "Returns a map of default ReadHandlers for + Clojure types. Java types are handled + by the default ReadHandlers provided by the + transit-java library." + {":" + (reify ReadHandler + (fromRep [_ o] (keyword o))) + + "$" + (reify ReadHandler + (fromRep [_ o] (symbol o))) + + "ratio" + (reify ReadHandler + (fromRep [_ o] (/ (.get ^java.util.List o 0) + (.get ^java.util.List o 1)))) + + "n" + (reify ReadHandler + (fromRep [_ o] (clojure.lang.BigInt/fromBigInteger + (BigInteger. ^String o)))) + + "set" + (reify ArrayReadHandler + (fromRep [_ o] o) + (arrayReader [_] + (reify ArrayReader + (init [_] (transient #{})) + (init [_ ^int size] (transient #{})) + (add [_ s item] (conj! s item)) + (complete [_ s] (persistent! s))))) + + "list" + (reify ArrayReadHandler + (fromRep [_ o] o) + (arrayReader [_] + (reify ArrayReader + (init [_] (java.util.ArrayList.)) + (init [_ ^int size] (java.util.ArrayList. size)) + (add [_ l item] (.add ^java.util.List l item) l) + (complete [_ l] (or (seq l) '()))))) + + "cmap" + (reify ArrayReadHandler + (fromRep [_ o]) + (arrayReader [_] + (let [marker (Object.) + ^objects next-key (object-array [marker])] + (reify ArrayReader + (init [_] (transient {})) + (init [_ ^int size] (transient {})) + (add [_ m item] + (let [k (aget next-key 0)] + (if (identical? k marker) + (do + (aset next-key 0 item) + m) + (do + (aset next-key 0 marker) + (assoc! m k item))))) + (complete [_ m] (persistent! m)))))) + + "with-meta" + (reify ReadHandler + (fromRep [_ o] + (with-meta (get ^java.util.List o 0) (get ^java.util.List o 1))))}) + +(defn map-builder + "Creates a MapBuilder that makes Clojure- + compatible maps." + [] + (reify MapReader + (init [_] (transient {})) + (init [_ ^int size] (transient {})) + (add [_ m k v] (assoc! m k v)) + (complete [_ m] (persistent! m)))) + +(defn list-builder + "Creates an ArrayBuilder that makes Clojure- + compatible lists." + [] + (reify ArrayReader + (init [_] (transient [])) + (init [_ ^int size] (transient [])) + (add [_ v item] (conj! v item)) + (complete [_ v] (persistent! v)))) + +(deftype Reader [r]) + +(defn reader + "Creates a reader over the provided source `in` using + the specified format, one of: :msgpack, :json or :json-verbose. + + An optional opts map may be passed. Supported options are: + + :handlers - a map of tags to ReadHandler instances, they are merged + with the Clojure default-read-handlers and then with the default ReadHandlers + provided by transit-java. + + :default-handler - an instance of DefaultReadHandler, used to process + transit encoded values for which there is no other ReadHandler; if + :default-handler is not specified, non-readable values are returned + as TaggedValues." + ([in type] (reader in type {})) + ([^InputStream in type {:keys [handlers default-handler]}] + (if (#{:json :json-verbose :msgpack} type) + (let [handler-map (if (instance? HandlerMapContainer handlers) + (handler-map handlers) + (merge default-read-handlers handlers)) + reader (TransitFactory/reader (transit-format type) + in + handler-map + default-handler)] + (Reader. (.setBuilders ^ReaderSPI reader + (map-builder) + (list-builder)))) + (throw (ex-info "Type must be :json, :json-verbose or :msgpack" {:type type}))))) + +(defn read + "Reads a value from a reader. Throws a RuntimeException when + the reader's InputStream is empty." + [^Reader reader] + (.read ^com.cognitect.transit.Reader (.r reader))) + +(defn record-write-handler + "Creates a WriteHandler for a record type" + [^Class type] + (reify WriteHandler + (tag [_ _] (.getName type)) + (rep [_ rec] (tagged-value "map" rec)) + (stringRep [_ _] nil) + (getVerboseHandler [_] nil))) + +(defn record-write-handlers + "Creates a map of record types to WriteHandlers" + [& types] + (reduce (fn [h t] (assoc h t (record-write-handler t))) + {} + types)) + +(defn record-read-handler + "Creates a ReadHandler for a record type" + [^Class type] + (let [type-name (map #(str/replace % "_" "-") (str/split (.getName type) #"\.")) + map-ctor (-> (str (str/join "." (butlast type-name)) "/map->" (last type-name)) + symbol + resolve)] + (reify ReadHandler + (fromRep [_ m] (map-ctor m))))) + +(defn record-read-handlers + "Creates a map of record type tags to ReadHandlers" + [& types] + (reduce (fn [d ^Class t] (assoc d (.getName t) (record-read-handler t))) + {} + types)) + +(defn read-handler-map + "Returns a HandlerMapContainer containing a ReadHandlerMap + containing all the default handlers for Clojure and Java and any + custom handlers that you supply, letting you store the return value + and pass it to multiple invocations of reader. This can be more + efficient than repeatedly handing the same raw map of tags -> custom + handlers to reader." + [custom-handlers] + (HandlerMapContainer. + (TransitFactory/readHandlerMap (merge default-read-handlers custom-handlers)))) + +(defn write-handler-map + "Returns a HandlerMapContainer containing a WriteHandlerMap + containing all the default handlers for Clojure and Java and any + custom handlers that you supply, letting you store the return value + and pass it to multiple invocations of writer. This can be more + efficient than repeatedly handing the same raw map of types -> custom + handlers to writer." + [custom-handlers] + (HandlerMapContainer. + (TransitFactory/writeHandlerMap (merge default-write-handlers custom-handlers)))) + +(defn write-meta + "For :transform. Will write any metadata present on the value." + [x] + (if (instance? clojure.lang.IObj x) + (if-let [m (meta x)] + (WithMeta. (with-meta x nil) m) + x) + x)) + +(comment + (require 'cognitect.transit) + (in-ns 'cognitect.transit) + + (import [java.io File ByteArrayInputStream ByteArrayOutputStream OutputStreamWriter]) + + (def out (ByteArrayOutputStream. 2000)) + + (def w (writer out :json)) + (def w (writer out :json-verbose)) + (def w (writer out :msgpack)) + (def w (writer out :msgpack {:transform write-meta})) + (def w (writer out :json {:transform write-meta})) + + (write w "foo") + (write w 10) + (write w [1 2 3]) + (write w (with-meta [1 2 3] {:foo 'bar})) + (String. (.toByteArray out)) + + (write w {:a-key 1 :b-key 2}) + (write w {"a" "1" "b" "2"}) + (write w {:a-key [1 2]}) + (write w #{1 2}) + (write w [{:a-key 1} {:a-key 2}]) + (write w [#{1 2} #{1 2}]) + (write w (int-array (range 10))) + (write w {[:a :b] 2}) + (write w [123N]) + (write w 1/3) + (write w {false 10 [] 20}) + + (def in (ByteArrayInputStream. (.toByteArray out))) + + (def r (reader in :json)) + + (def r (reader in :msgpack)) + + (def x (read r)) + (meta x) + + (type (read r)) + + ;; extensibility + + (defrecord Point [x y]) + + (defrecord Circle [c r]) + + (def ext-write-handlers + {Point + (write-handler "point" (fn [p] [(.x p) (.y p)])) + Circle + (write-handler "circle" (fn [c] [(.c c) (.r c)]))}) + + (def ext-read-handlers + {"point" + (read-handler (fn [[x y]] (prn "making a point") (Point. x y))) + "circle" + (read-handler (fn [[c r]] (prn "making a circle") (Circle. c r)))}) + + (def ext-write-handlers + (record-write-handlers Point Circle)) + + (def ext-read-handlers + (record-read-handlers Point Circle)) + + (def out (ByteArrayOutputStream. 2000)) + (def w (writer out :json {:handlers ext-write-handlers})) + (write w (Point. 10 20)) + (write w (Circle. (Point. 10 20) 30)) + (write w [(Point. 10 20) (Point. 20 40) (Point. 0 0)]) + + (def in (ByteArrayInputStream. (.toByteArray out))) + (def r (reader in :json {:handlers ext-read-handlers})) + (read r) + + ;; write and read handler maps + + (def custom-write-handler-map (write-handler-map ext-write-handlers)) + (def custom-read-handler-map (read-handler-map ext-read-handlers)) + + (def out (ByteArrayOutputStream. 2000)) + (def w (writer out :json {:handlers custom-write-handler-map})) + + (write w (Point. 10 20)) + + (def in (ByteArrayInputStream. (.toByteArray out))) + (def r (reader in :json {:handlers custom-read-handler-map})) + (read r) + ) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index dad29746e..390f7f41b 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -15,7 +15,7 @@ [cljs.env :as env] [cljs.test-util :as test] [cljs.util :as util] - [clojure.data.json :as json] + [cljs.vendor.clojure.data.json :as json] [clojure.edn :as edn] [clojure.java.io :as io] [clojure.java.shell :as sh] @@ -458,7 +458,7 @@ (build/build (build/inputs (io/file inputs "data_readers_test")) opts cenv) (is (contains? (-> @cenv ::ana/data-readers) 'test/custom-identity)) (is (true? (boolean (re-find #"Array\.of\(\"foo\"\)" - (slurp (io/file + (slurp (io/file out ;"data-readers-test-out" "data_readers_test" "core.js")))))))) diff --git a/src/test/clojure/cljs/closure_tests.clj b/src/test/clojure/cljs/closure_tests.clj index 4964a2c6b..d373efd9e 100644 --- a/src/test/clojure/cljs/closure_tests.clj +++ b/src/test/clojure/cljs/closure_tests.clj @@ -10,7 +10,7 @@ (:refer-clojure :exclude [compile]) (:use cljs.closure clojure.test) (:require [cljs.build.api :as build] - [clojure.data.json :as json] + [cljs.vendor.clojure.data.json :as json] [clojure.java.shell :as sh] [cljs.closure :as closure] [cljs.js-deps :as deps] From d1809c5ef8cbaf13363047e7e1565fac57571283 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 May 2022 13:20:18 -0400 Subject: [PATCH 3818/4033] CLJS-3375: Bridge tools.reader (#178) * add cljs.vendor.bridge file to bridge tools.reader and vendorized tools.reader * return clojure.tools.reader/*alias-map* first * only auto-bridge when invoking the REPL --- pom.template.xml | 2 +- src/main/clojure/cljs/analyzer.cljc | 12 +++++-- src/main/clojure/cljs/compiler.cljc | 2 +- src/main/clojure/cljs/repl.cljc | 4 +++ src/main/clojure/cljs/vendor/bridge.clj | 42 +++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 src/main/clojure/cljs/vendor/bridge.clj diff --git a/pom.template.xml b/pom.template.xml index 28fb75858..aaf6411d2 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -278,7 +278,7 @@ true - !clojure.tools.reader.* + !cljs.vendor.bridge diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 5017d747e..153bc2332 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4395,6 +4395,14 @@ ((juxt :requires :require-macros :as-aliases) (get-namespace ns)))) +(defn get-bridged-alias-map + "Returns clojure.tools.reader/*alias-map* for bridging" + [] + (try + @(ns-resolve 'clojure.tools.reader '*alias-map*) + (catch Throwable t + nil))) + #?(:clj (defn forms-seq* "Seq of Clojure/ClojureScript forms from rdr, a java.io.Reader. Optionally @@ -4795,7 +4803,7 @@ *unchecked-arrays* false]) *cljs-ns* 'cljs.user *cljs-file* nil - reader/*alias-map* (or reader/*alias-map* {})] + reader/*alias-map* (or (get-bridged-alias-map) reader/*alias-map* {})] (loop [ns nil forms forms last-ast nil] (if (some? forms) (let [form (first forms) @@ -4853,7 +4861,7 @@ (if (or skip-cache (not cache) (requires-analysis? res cache output-dir opts)) (binding [*cljs-ns* 'cljs.user *cljs-file* path - reader/*alias-map* (or reader/*alias-map* {})] + reader/*alias-map* (or (get-bridged-alias-map) reader/*alias-map* {})] (when (or *verbose* (:verbose opts)) (util/debug-prn "Analyzing" (str res))) (let [env (assoc (empty-env) :build-options opts) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index dfc37fb67..540bb6c92 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1559,7 +1559,7 @@ (binding [*out* out ana/*cljs-ns* 'cljs.user ana/*cljs-file* (.getPath ^File src) - reader/*alias-map* (or reader/*alias-map* {}) + reader/*alias-map* (or (ana/get-bridged-alias-map) reader/*alias-map* {}) ana/*checked-arrays* (or ana/*checked-arrays* (:checked-arrays opts)) ana/*cljs-static-fns* (or ana/*cljs-static-fns* (:static-fns opts)) *source-map-data* (when (:source-map opts) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 511750696..8aad46308 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1054,6 +1054,10 @@ [cljs.pprint :refer [pprint] :refer-macros [pp]]] bind-err true} :as opts}] + ;; bridge clojure.tools.reader to satisfy the old contract + (when (and (find-ns 'clojure.tools.reader) + (not (find-ns 'cljs.vendor.bridge))) + (clojure.core/load "vendor/bridge")) (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) (set/union known-repl-opts cljsc/known-opts))] (when suggested-opt (println (str "WARNING: Unknown option '" unknown-opt "'. Did you mean '" suggested-opt "'?")))) diff --git a/src/main/clojure/cljs/vendor/bridge.clj b/src/main/clojure/cljs/vendor/bridge.clj new file mode 100644 index 000000000..4a2e24c09 --- /dev/null +++ b/src/main/clojure/cljs/vendor/bridge.clj @@ -0,0 +1,42 @@ +; 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.vendor.bridge + (:require [cljs.vendor.clojure.tools.reader.reader-types :as vendor] + [clojure.tools.reader.reader-types :as readers])) + +(extend-protocol vendor/Reader + clojure.tools.reader.reader_types.Reader + (read-char [reader] + (readers/read-char reader)) + (peek-char [reader] + (readers/peek-char reader))) + +(extend-protocol vendor/IPushbackReader + clojure.tools.reader.reader_types.IPushbackReader + (unread [reader ch] + (readers/unread reader ch))) + +(extend-protocol vendor/IndexingReader + clojure.tools.reader.reader_types.IndexingReader + (get-line-number [reader] + (readers/get-line-number reader)) + (get-column-number [reader] + (readers/get-column-number reader)) + (get-file-name [reader] + (readers/get-file-name reader))) + +(extend-protocol vendor/ReaderCoercer + clojure.tools.reader.reader_types.ReaderCoercer + (to-rdr [reader] + (readers/to-rdr reader))) + +(extend-protocol vendor/PushbackReaderCoercer + clojure.tools.reader.reader_types.PushbackReaderCoercer + (to-pbr [reader buflen] + (readers/to-pbr reader buflen))) From 970a7835972d54edb82ad60662e22cfbef3a3809 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 May 2022 11:33:54 -0400 Subject: [PATCH 3819/4033] 1.11.51 --- README.md | 6 +++--- changes.md | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 36c2e604b..d740d1bd4 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.11.4 +Latest stable release: 1.11.51 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.11.4"] +[org.clojure/clojurescript "1.11.51"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.11.4 org.clojure clojurescript - 1.11.4 + 1.11.51 ``` diff --git a/changes.md b/changes.md index 3d46a8658..0aec4fe44 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,39 @@ +## 1.11.51 + +### Changes +* 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 +* 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 +* Update / clarify docstrings, CLJS-3358, CLJS-3359, CLJS-3360, CLJS-3361, CLJS-3364 +* CLJS-3354: map-invert should use transients and reduce-kv instead of reduce +* CLJS-3350: Update test.check dependency +* CLJS-3294: data_readers.cljc doesn't provide a way to have target-specific + behaviour + +### Fixes +* CLJS-3373: Externs Inference issue with vars invoked from foreign libs +* CLJS-3368: let binding can shadow globals, leading to strange behavior +* CLJS-3367: Backward conflict test in prefer-method causes incorrect exception +* CLJS-3371: Invalid warning on record constructor +* Fix apply of IFn for more than 20 arguments +* CLJS-3288: selfhost: *eval-fn* not bound for :js sources +* CLJS-3362: some-fn has different short-circuiting when using 3 predicates +* CLJS-3356: halt-when not usable within #'c.c/into +* CLJS-3352: Self-host negative zero emitted as positive zero +* CLJS-3319: make PersistentHashMap release inodes correctly + +### Enhancemnets +* CLJS-3348: Implement new functions for parity with Clojure 1.11 +* CLJS-3353: Add the new iteration function introduced in Clojure 1.11 +* CLJS-3347: Create clojure.math namespace +* CLJS-3299: port CLJ-2603 +* CLJS-3346: :as-alias +* add update-vals & update-keys + ## 1.11.4 ### Fixes From e30e26dbd221b5d7c4bbc567d10d0c3c01cf5f98 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 May 2022 12:47:58 -0400 Subject: [PATCH 3820/4033] typo in changes.md --- changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.md b/changes.md index 0aec4fe44..3f23df376 100644 --- a/changes.md +++ b/changes.md @@ -26,7 +26,7 @@ * CLJS-3352: Self-host negative zero emitted as positive zero * CLJS-3319: make PersistentHashMap release inodes correctly -### Enhancemnets +### Enhancements * CLJS-3348: Implement new functions for parity with Clojure 1.11 * CLJS-3353: Add the new iteration function introduced in Clojure 1.11 * CLJS-3347: Create clojure.math namespace From 1b6d4e559bfaa716052c557f6fa31111384eb336 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 May 2022 12:32:16 -0400 Subject: [PATCH 3821/4033] switch to `require` from `load` for loading cljs.vendor.bridge, fixes Figwheel issue (#179) reported by Bruce Hauman --- README.md | 6 +++--- changes.md | 6 ++++++ src/main/clojure/cljs/repl.cljc | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d740d1bd4..a6b120007 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.11.51 +Latest stable release: 1.11.52 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.11.51"] +[org.clojure/clojurescript "1.11.52"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.11.51 org.clojure clojurescript - 1.11.51 + 1.11.52 ``` diff --git a/changes.md b/changes.md index 3f23df376..020df9a32 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,9 @@ +## 1.11.52 + +### Changes +* use `require` instead of `load` for `cljs.vendor.bridge`, addresses issue + reported by Bruce Hauman wrt. Figwheel + ## 1.11.51 ### Changes diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 8aad46308..b5c53738a 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1057,7 +1057,7 @@ ;; bridge clojure.tools.reader to satisfy the old contract (when (and (find-ns 'clojure.tools.reader) (not (find-ns 'cljs.vendor.bridge))) - (clojure.core/load "vendor/bridge")) + (require 'cljs.vendor.bridge)) (doseq [[unknown-opt suggested-opt] (util/unknown-opts (set (keys opts)) (set/union known-repl-opts cljsc/known-opts))] (when suggested-opt (println (str "WARNING: Unknown option '" unknown-opt "'. Did you mean '" suggested-opt "'?")))) From b236032061bc3c68c18adab4e5bcc3e47404d65c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 16 May 2022 12:34:15 -0400 Subject: [PATCH 3822/4033] 1.11.54 --- README.md | 6 +++--- changes.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a6b120007..dedae3ca5 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.11.52 +Latest stable release: 1.11.54 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.11.52"] +[org.clojure/clojurescript "1.11.54"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.11.52 org.clojure clojurescript - 1.11.52 + 1.11.54 ``` diff --git a/changes.md b/changes.md index 020df9a32..900fcfb41 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.11.52 +## 1.11.54 ### Changes * use `require` instead of `load` for `cljs.vendor.bridge`, addresses issue From c0d305274d7813e22cd2753d247a02e9dd95ddee Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 17 May 2022 10:23:18 -0400 Subject: [PATCH 3823/4033] get-bridged-alias-map is not needed in self-hosted (#180) --- src/main/clojure/cljs/analyzer.cljc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 153bc2332..861cdfa50 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4395,13 +4395,14 @@ ((juxt :requires :require-macros :as-aliases) (get-namespace ns)))) -(defn get-bridged-alias-map - "Returns clojure.tools.reader/*alias-map* for bridging" - [] - (try - @(ns-resolve 'clojure.tools.reader '*alias-map*) - (catch Throwable t - nil))) +#?(:clj + (defn get-bridged-alias-map + "Returns clojure.tools.reader/*alias-map* for bridging" + [] + (try + @(ns-resolve 'clojure.tools.reader '*alias-map*) + (catch Throwable t + nil)))) #?(:clj (defn forms-seq* @@ -4803,7 +4804,7 @@ *unchecked-arrays* false]) *cljs-ns* 'cljs.user *cljs-file* nil - reader/*alias-map* (or (get-bridged-alias-map) reader/*alias-map* {})] + reader/*alias-map* (or #?(:clj (get-bridged-alias-map)) reader/*alias-map* {})] (loop [ns nil forms forms last-ast nil] (if (some? forms) (let [form (first forms) From 9562ae11422243e0648a12c39e7c990ef3f94260 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 18 May 2022 09:01:51 -0400 Subject: [PATCH 3824/4033] CLJS-3377: Objects created from required JS constructor are not poisoned (#181) CLJS-3377: Objects created from required JS constructor are not poisoned (#181) fix 'new analyzer case to return 'js if the tag of the constructor is 'js add test case --- src/main/clojure/cljs/analyzer.cljc | 27 +++++++++++++------ src/test/clojure/cljs/externs_infer_tests.clj | 20 ++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 861cdfa50..132a3a981 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2556,6 +2556,19 @@ :tag (:tag expr) :children [:expr]})) +(def js-prim-ctor->tag + '{js/Object object + js/String string + js/Array array + js/Number number + js/Function function + js/Boolean boolean}) + +(defn prim-ctor? + "Test whether a tag is a constructor for a JS primitive" + [t] + (contains? js-prim-ctor->tag t)) + (defmethod parse 'new [_ env [_ ctor & args :as form] _ _] (disallowing-recur @@ -2577,14 +2590,12 @@ (warning :fn-arity env {:argc argc :ctor ctor})) {:env env :op :new :form form :class ctorexpr :args argexprs :children [:class :args] - :tag (let [name (-> ctorexpr :info :name)] - (or ('{js/Object object - js/String string - js/Array array - js/Number number - js/Function function - js/Boolean boolean} name) - name))}))) + :tag (let [tag (-> ctorexpr :info :tag)] + (if (and (js-tag? tag) + (not (prim-ctor? tag))) + 'js ; some foreign thing, drop the prefix + (let [name (-> ctorexpr :info :name)] + (or (js-prim-ctor->tag name) name))))}))) (defmethod parse 'set! [_ env [_ target val alt :as form] _ _] diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 3038254bc..6e52fd641 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -433,6 +433,26 @@ "Object.onAuthStateChanged;"]) res))))) +(deftest test-cljs-3377 + (testing "constructors from foreign libraries that used via `new` should propagate 'js hints" + (let [ws (atom []) + res (infer-test-helper + {:js-dependency-index {"firebase" {:global-exports '{firebase Firebase}}} + :forms '[(ns foo.core + (:require [firebase :refer [GoogleAuthProvider]])) + (def goog-provider + (GoogleAuthProvider.)) + (.someMethod goog-provider) + (.-someProperty goog-provider)] + :warnings ws + :warn true + :with-core? false})] + (is (= (unsplit-lines + ["Object.GoogleAuthProvider;" + "Object.someMethod;" + "Object.someProperty;"]) + res))))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From e4ff22fcc66bce00a19863c581051e8ac20b76da Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 11 Jun 2022 08:53:30 -0400 Subject: [PATCH 3825/4033] 1.11.57 --- README.md | 6 +++--- changes.md | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dedae3ca5..1721d4504 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.11.54 +Latest stable release: 1.11.57 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.11.54"] +[org.clojure/clojurescript "1.11.57"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.11.54 org.clojure clojurescript - 1.11.54 + 1.11.57 ``` diff --git a/changes.md b/changes.md index 900fcfb41..7c33e6fce 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,9 @@ +## 1.11.57 + +### Fixes +* CLJS-3377: Objects created from required JS constructor are not correctly hinted +* get-bridged-alias-map is not needed in self-hosted + ## 1.11.54 ### Changes From 7af8c42f6a9c045b4c5e213163a04816935ebe42 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 21 Jun 2022 15:09:42 -0400 Subject: [PATCH 3826/4033] CLJS-3382: Fix regression in .apply after CLJS-3024 (#182) Co-authored-by: Nikita Prokopov --- src/main/clojure/cljs/core.cljc | 13 +++++++------ src/test/cljs/cljs/apply_test.cljs | 5 ++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b64b2c9a3..3e1478df8 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1507,12 +1507,13 @@ ~(with-meta `(fn ~[this-sym argsym] (this-as ~this-sym - (.apply (.-call ~this-sym) ~this-sym - (.concat (array ~this-sym) - (if (> (.-length ~argsym) ~max-ifn-arity) - (doto (.slice ~argsym 0 ~max-ifn-arity) - (.push (.slice ~argsym ~max-ifn-arity))) - ~argsym))))) + (let [args# (cljs.core/aclone ~argsym)] + (.apply (.-call ~this-sym) ~this-sym + (.concat (array ~this-sym) + (if (> (.-length args#) ~max-ifn-arity) + (doto (.slice args# 0 ~max-ifn-arity) + (.push (.slice args# ~max-ifn-arity))) + args#)))))) (meta form)))] (ifn-invoke-methods type type-sym form)))) diff --git a/src/test/cljs/cljs/apply_test.cljs b/src/test/cljs/cljs/apply_test.cljs index adcee65a6..9379ffb53 100644 --- a/src/test/cljs/cljs/apply_test.cljs +++ b/src/test/cljs/cljs/apply_test.cljs @@ -47,7 +47,10 @@ (is (= (range 22) (apply meta-f (range 22))) "Should properly call the last IFn arity with 20 args with last being a seq") (is (= (range 22) (.apply meta-f nil (to-array (range 22)))) - ".apply should also handle >20 arguments")) + ".apply should also handle >20 arguments") + (let [ctor #(.apply meta-f nil (js-arguments))] ; CLJS-3382 + (is (= '(1 2 3) (.apply ctor nil #js [1 2 3]))) + (is (= (range 30) (.apply ctor nil (to-array (range 30))))))) (deftest multi-arity-test (is (= 2 (apply (fn ([a] a) ([a b] b)) 1 [2]))) From 8306bd72c5d188648256dddbc4d13f61ee3132b0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 22 Jun 2022 13:58:08 -0400 Subject: [PATCH 3827/4033] broaden scope of UUID equiv (#183) --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 865ab0507..ec530eca4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11562,7 +11562,7 @@ reduces them without incurring seq initialization" IEquiv (-equiv [_ other] - (and (instance? UUID other) (identical? uuid (.-uuid other)))) + (and (implements? IUUID other) (identical? uuid (.-uuid other)))) IPrintWithWriter (-pr-writer [_ writer _] From e7cdc70d0371a26e07e394ea9cd72d5c43e5e363 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 23 Jun 2022 15:02:38 -0400 Subject: [PATCH 3828/4033] 1.11.60 --- README.md | 6 +++--- changes.md | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1721d4504..85a3098a2 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ Official web site: http://clojurescript.org ## Releases and dependency information ## -Latest stable release: 1.11.57 +Latest stable release: 1.11.60 * [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) [Leiningen](http://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.11.57"] +[org.clojure/clojurescript "1.11.60"] ``` [Maven](http://maven.apache.org) dependency information: @@ -22,7 +22,7 @@ Latest stable release: 1.11.57 org.clojure clojurescript - 1.11.57 + 1.11.60 ``` diff --git a/changes.md b/changes.md index 7c33e6fce..3f94a513e 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,9 @@ +## 1.11.60 + +### Fixes +* broaden scope of UUID equiv to implementers of protocol rather than concrete type +* CLJS-3382: Fix regression in .apply after CLJS-3024 + ## 1.11.57 ### Fixes From 358ec6ceb833f73ceccef2f912f839913c5f4930 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 15 Jul 2022 15:17:44 -0400 Subject: [PATCH 3829/4033] CLJS-3384: get-in should not unreduce values. (#184) Co-authored-by: Enzzo Cavallo --- src/main/cljs/cljs/core.cljs | 7 ++++++- src/test/cljs/cljs/core_test.cljs | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ec530eca4..f168201c4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5329,7 +5329,12 @@ reduces them without incurring seq initialization" {:added "1.2" :static true} ([m ks] - (reduce get m ks)) + (loop [m m + ks (seq ks)] + (if (nil? ks) + m + (recur (get m (first ks)) + (next ks))))) ([m ks not-found] (loop [sentinel lookup-sentinel m m diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index fbbaa737b..d935b4202 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -127,6 +127,8 @@ (is (= 1 (get-in {:foo 1 :bar 2} [:foo]))) (is (= 2 (get-in {:foo {:bar 2}} [:foo :bar]))) (is (= 1 (get-in [{:foo 1}, {:foo 2}] [0 :foo]))) + (let [v (reduced 42)] + (is (= v (get-in {:foo v} [:foo])))) (is (= 4 (get-in [{:foo 1 :bar [{:baz 1}, {:buzz 2}]}, {:foo 3 :bar [{:baz 3}, {:buzz 4}]}] [1 :bar 1 :buzz])))) ) From 9c01d9b0a70ada1cb17cf3ce65ae93d76d0d3b08 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 15 Jul 2022 16:14:40 -0400 Subject: [PATCH 3830/4033] CLJS-3383: brepl always true in cljs.cli/default-compile (#185) Co-authored-by: John Newman --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index f404fac9a..0f91b2fab 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -495,7 +495,7 @@ present" (merge (select-keys env-opts (cond-> closure/known-opts - repl? (conj :browser-repl))) + (not repl?) (disj :browser-repl))) options (when main-ns {:main main-ns})) opts From 8a02be12646d7f94e5dab81e9c78eb1cab802c59 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Fri, 29 Jul 2022 14:04:37 +0200 Subject: [PATCH 3831/4033] CLJS-3379: Add support for node_modules with .cjs extension --- src/main/clojure/cljs/closure.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 86c80cf0e..244c02325 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2851,7 +2851,8 @@ (cond-> [entry-path] (not (or (string/ends-with? entry-path ".js") (string/ends-with? entry-path ".json"))) - (into [(str entry-path ".js") (str entry-path "/index.js") (str entry-path ".json")]))))))) + (into [(str entry-path ".js") (str entry-path "/index.js") (str entry-path ".json") + (string/replace entry-path #"\.cjs$" ".js")]))))))) pkg-jsons)] {:provides (let [module-rel-name (-> (subs path (.lastIndexOf path "node_modules")) (string/replace \\ \/) From 21e52cb14e0d49afb8628364feba09ebc3d982e6 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Fri, 29 Jul 2022 14:08:15 +0200 Subject: [PATCH 3832/4033] CLJS-3378: Change default :language-in to :ecmascript-next --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 86c80cf0e..b84dbe0bd 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -265,7 +265,7 @@ (when (contains? opts :pseudo-names) (set! (.generatePseudoNames compiler-options) (:pseudo-names opts))) - (when-let [lang-key (:language-in opts :ecmascript5)] + (when-let [lang-key (:language-in opts :ecmascript-next)] (.setLanguageIn compiler-options (lang-key->lang-mode lang-key))) (when-let [lang-key (:language-out opts)] From e8643ab1cbaae9b01d3ed306711883f6473115aa Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Tue, 2 Aug 2022 11:07:15 +0200 Subject: [PATCH 3833/4033] CLJS-3386: defn varargs argument should be nil when no varargs are passed --- src/main/clojure/cljs/core.cljc | 5 +++-- src/test/cljs/cljs/core_test.cljs | 11 +++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 3e1478df8..b479aa036 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3269,8 +3269,9 @@ ~(if variadic? `(let [args-arr# (array)] (copy-arguments args-arr#) - (let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq - (.slice args-arr# ~maxfa) 0 nil)] + (let [argseq# (when (< ~maxfa (alength args-arr#)) + (new ^::ana/no-resolve cljs.core/IndexedSeq + (.slice args-arr# ~maxfa) 0 nil))] (. ~rname (~'cljs$core$IFn$_invoke$arity$variadic ~@(dest-args maxfa) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d935b4202..0f93b8c63 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1142,8 +1142,8 @@ (is (nil? (seq xs))) (is (= (rest xs) ())) (is (= (pr-str xs) "()")) - (is (= (foo-1284 0) [0 ()])) - (is (= (pr-str (foo-1284 0)) "[0 ()]")) + (is (= (foo-1284 0) [0 nil])) + (is (= (pr-str (foo-1284 0)) "[0 nil]")) (is (zero? (count ys))) (is (= (transduce (map inc) conj [] ys) [])))) @@ -1922,3 +1922,10 @@ {1 1 3 3} (update-keys (hash-map 0 1 2 3) inc) {1 1 3 3} (update-keys (array-map 0 1 2 3) inc) {1 1 3 3} (update-keys (sorted-map 2 3 0 1) inc)))) + +(defn cljs-3386-test-fn + ([x] x) ([_ _ & zs] zs)) + +(deftest test-cljs-3386 + (is (nil? (cljs-3386-test-fn 1 2))) + (is (= '(3 4) (cljs-3386-test-fn 1 2 3 4)))) From 95c5cf384a128503b072b7b1916af1a1d5c8871c Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sun, 24 Jul 2022 15:31:03 -0400 Subject: [PATCH 3834/4033] CLJS-3385: Extend empty? to counted? colls that arent seqable, such as transients --- src/main/cljs/cljs/core.cljs | 16 ++++++++++++---- src/test/cljs/cljs/seqs_test.cljs | 28 +++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f168201c4..68ef2e13d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2130,10 +2130,18 @@ reduces them without incurring seq initialization" ret))))) (defn empty? - "Returns true if coll has no items - same as (not (seq coll)). - Please use the idiom (seq x) rather than (not (empty? x))" - [coll] (or (nil? coll) - (not (seq coll)))) + "Returns true if coll has no items. To check the emptiness of a seq, + please use the idiom (seq x) rather than (not (empty? x))" + [coll] + (cond + (nil? coll) + true + + (satisfies? ICounted coll) + (zero? (-count coll)) + + :else + (not (seq coll)))) (defn coll? "Returns true if x satisfies ICollection" diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 988f01e21..246c79550 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -8,7 +8,7 @@ (ns cljs.seqs-test (:refer-clojure :exclude [iter]) - (:require [cljs.test :refer-macros [deftest testing is]] + (:require [cljs.test :refer-macros [deftest testing are is]] [clojure.test.check :as tc] [clojure.test.check.clojure-test :refer-macros [defspec]] [clojure.test.check.generators :as gen] @@ -97,6 +97,32 @@ (is (nil? (empty "abc"))) (is (nil? (empty #js [1 2 3])))))) +(deftest test-empty? + (are [x] (empty? x) + nil + () + (lazy-seq nil) ; => () + [] + {} + #{} + "" + (into-array []) + (transient []) + (transient #{}) + (transient {})) + + (are [x] (not (empty? x)) + '(1 2) + (lazy-seq [1 2]) + [1 2] + {:a 1 :b 2} + #{1 2} + "abc" + (into-array [1 2]) + (transient [1]) + (transient #{1}) + (transient {1 2}))) + (deftest test-distinct (testing "Testing distinct? & distinct" (is (distinct? 1 2 3)) From e477acced26c7eba12e9969de6f65eeb57c7f8d1 Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Thu, 4 Aug 2022 16:20:46 -0400 Subject: [PATCH 3835/4033] Update CI to use newer macOS version --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 28e698d52..11e67e9ed 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-10.15 + runs-on: macos-12 steps: - uses: actions/checkout@v2 From 60dd29eeadae6df64f1fb954bfbeb6e7aa4819c1 Mon Sep 17 00:00:00 2001 From: Will Cohen Date: Thu, 4 Aug 2022 12:24:45 -0400 Subject: [PATCH 3836/4033] CLJS-3387: Browser repl unable to serve wasm files This commit: 1) Adds .wasm as an accepted file with an "application/wasm" mime type. It enables gzip encoding for application/wasm files. 2) Additionally, by default the REPL sends a charset for all files, which causes wasm files to fail to stream correctly. This omits sending a charset encoding for application/wasm files. --- src/main/clojure/cljs/repl/browser.clj | 9 ++++++--- src/main/clojure/cljs/repl/server.clj | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 4c5b516d5..cf4a34523 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -60,7 +60,8 @@ ".cljs" "text/x-clojure" ".cljc" "text/x-clojure" ".edn" "text/x-clojure" - ".map" "application/json"}) + ".map" "application/json" + ".wasm" "application/wasm"}) (def mime-type->encoding {"text/html" "UTF-8" @@ -84,7 +85,8 @@ "text/javascript" "UTF-8" "text/x-clojure" "UTF-8" - "application/json" "UTF-8"}) + "application/json" "UTF-8" + "application/wasm" "ISO-8859-1"}) (defn- set-return-value-fn "Save the return value function which will be called when the next @@ -206,7 +208,8 @@ (let [mime-type (path->mime-type ext->mime-type path "text/plain") encoding (mime-type->encoding mime-type "UTF-8")] (server/send-and-close conn 200 (slurp local-path :encoding encoding) - mime-type encoding (and gzip? (= "text/javascript" mime-type)))) + mime-type encoding (and gzip? (or (= "text/javascript" mime-type) + (= "application/wasm" mime-type))))) ;; "/index.html" doesn't exist, provide our own (= path "/index.html") diff --git a/src/main/clojure/cljs/repl/server.clj b/src/main/clojure/cljs/repl/server.clj index 98e3dd1bd..18aacfd56 100644 --- a/src/main/clojure/cljs/repl/server.clj +++ b/src/main/clojure/cljs/repl/server.clj @@ -157,9 +157,12 @@ (cond-> [(status-line status) "Server: ClojureScript REPL" - (str "Content-Type: " - content-type - "; charset=" encoding) + (if (not= content-type "application/wasm") + (str "Content-Type: " + content-type + "; charset=" encoding) + (str "Content-Type: " + content-type)) (str "Content-Length: " content-length)] gzip? (conj "Content-Encoding: gzip") true (conj "")))] From 098e00c2307dd54c8ab54f69ec556ad918f4822a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Aug 2022 11:49:42 -0400 Subject: [PATCH 3837/4033] update to macos-12 --- .github/workflows/test.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 11e67e9ed..0a26d955c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -78,7 +78,7 @@ jobs: # Self-host Tests self-host-test: name: Self-host Tests - runs-on: macos-10.15 + runs-on: macos-12 steps: - uses: actions/checkout@v2 @@ -117,7 +117,7 @@ jobs: # Self-parity Tests self-parity-test: name: Self-parity Tests - runs-on: macos-10.15 + runs-on: macos-12 steps: - uses: actions/checkout@v2 @@ -156,7 +156,7 @@ jobs: # Compiler Tests compiler-test: name: Compiler Tests - runs-on: macos-10.15 + runs-on: macos-12 steps: - uses: actions/checkout@v2 @@ -205,7 +205,7 @@ jobs: # CLI Tests cli-test: name: CLI Tests - runs-on: macos-10.15 + runs-on: macos-12 steps: - uses: actions/checkout@v2 with: From 723b7ab92cd5291b7251f4a09da988d48c3aac36 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 26 Aug 2022 14:30:01 -0400 Subject: [PATCH 3838/4033] CLJS-3327 Add :node-modules-dirs configuration (#188) * CLJS-3327 Add :node-modules-dirs configuration * revert function move Co-authored-by: Allen Rohner --- src/main/clojure/cljs/closure.clj | 52 ++++++++++++++++--------------- src/main/clojure/cljs/util.cljc | 41 +++++++++++++----------- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 0948d28fc..3e2addfe2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -210,7 +210,7 @@ :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}) + :nodejs-rt :target-fn :deps-cmd :bundle-cmd :global-goog-object&array :node-modules-dirs}) (def string->charset {"iso-8859-1" StandardCharsets/ISO_8859_1 @@ -2739,29 +2739,31 @@ (when env/*compiler* (:options @env/*compiler*)))) ([modules opts] - (let [node-modules (io/file "node_modules")] - (if (and (not (empty? modules)) (.exists node-modules) (.isDirectory node-modules)) - (let [modules (into #{} (map name) modules) - deps-file (io/file (util/output-directory opts) "cljs$node_modules.js") - old-contents (when (.exists deps-file) - (slurp deps-file)) - new-contents (let [sb (StringBuffer.)] - (run! #(.append sb (str "require('" % "');\n")) modules) - (str sb))] - (util/mkdirs deps-file) - (if (or (not= old-contents new-contents) - (nil? env/*compiler*) - (nil? (::transitive-dep-set @env/*compiler*))) - (do - (spit deps-file new-contents) - (let [transitive-js (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)] - (when-not (nil? env/*compiler*) - (swap! env/*compiler* update-in [::transitive-dep-set] - assoc modules transitive-js)) - transitive-js)) - (when-not (nil? env/*compiler*) - (get-in @env/*compiler* [::transitive-dep-set modules])))) - [])))) + (->> (or (:node-modules-dirs opts) ["node_modules"]) + (map io/file) + (mapcat (fn [dir] + (when (and (seq modules) (.exists dir) (.isDirectory dir)) + (let [modules (into #{} (map name) modules) + deps-file (io/file (util/output-directory opts) "cljs$node_modules.js") + old-contents (when (.exists deps-file) + (slurp deps-file)) + new-contents (let [sb (StringBuffer.)] + (run! #(.append sb (str "require('" % "');\n")) modules) + (str sb))] + (util/mkdirs deps-file) + (if (or (not= old-contents new-contents) + (nil? env/*compiler*) + (nil? (::transitive-dep-set @env/*compiler*))) + (do + (spit deps-file new-contents) + (let [transitive-js (node-inputs [{:file (.getAbsolutePath deps-file)}] opts)] + (when-not (nil? env/*compiler*) + (swap! env/*compiler* update-in [::transitive-dep-set] + assoc modules transitive-js)) + transitive-js)) + (when-not (nil? env/*compiler*) + (get-in @env/*compiler* [::transitive-dep-set modules]))))))) + (filterv identity)))) (defn- node-file-seq->libs-spec* "Given a sequence of non-nested node_module paths where the extension ends in @@ -2875,7 +2877,7 @@ (when env/*compiler* (:options @env/*compiler*)))) ([opts] - (let [module-fseq (util/module-file-seq)] + (let [module-fseq (util/module-file-seq opts)] (node-file-seq->libs-spec module-fseq opts)))) (defn preprocess-js diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index b2d588ad8..5ed980ef1 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -389,24 +389,27 @@ 2 (str (first xs) " and " (second xs)) (str (string/join ", " (pop xs)) " and " (peek xs))))) -(defn module-file-seq +(defn module-file-seq-1 [dir] + [dir] + (let [fseq (tree-seq + (fn [^File f] + (and (. f (isDirectory)) + (not (boolean + (re-find #"node_modules[\\\/].*[\\\/]node_modules" + (.getPath f)))))) + (fn [^File d] + (seq (. d (listFiles)))) + dir)] + (filter (fn [^File f] + (let [path (.getPath f)] + (or (.endsWith path ".json") + (.endsWith path ".js")))) + fseq))) + +(defn node-path-modules [opts] + (map io/file (or (:node-modules-dirs opts) [(io/file "node_modules")]))) + +(defn module-file-seq [opts] "Return a seq of all files in `node_modules` ending in `.js` or `.json` that are not in an internally nested `node_modules` dir." - ([] (module-file-seq (io/file "node_modules"))) - ([dir] - (let [fseq (tree-seq - (fn [^File f] - ;; ignore embedded node_modules, the user did not install - ;; these - (and (. f (isDirectory)) - (not (boolean - (re-find #"node_modules[\\\/].*[\\\/]node_modules" - (.getPath f)))))) - (fn [^File d] - (seq (. d (listFiles)))) - dir)] - (filter (fn [^File f] - (let [path (.getPath f)] - (or (.endsWith path ".json") - (.endsWith path ".js")))) - fseq)))) + (mapcat module-file-seq-1 (node-path-modules opts))) From eef2f2e6ced2328032dbd9e8b4988da4f1a7ae45 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 13 Sep 2022 12:50:47 -0400 Subject: [PATCH 3839/4033] fix clojure.string/blank? docstring typo --- src/main/cljs/clojure/string.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 854a78c64..23956b1b6 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -223,7 +223,7 @@ (.substring s 0 index)))))) (defn ^boolean blank? - "True is s is nil, empty, or contains only whitespace." + "True if s is nil, empty, or contains only whitespace." [s] (gstring/isEmptyOrWhitespace (gstring/makeSafe s))) From 961807166c8cf4d45a225d63416f06464fb27eaf Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 Sep 2022 08:58:15 -0400 Subject: [PATCH 3840/4033] remove declares that are no longer needed, impacts some IDEs --- src/main/cljs/cljs/core.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 68ef2e13d..529fc4514 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -5540,7 +5540,7 @@ reduces them without incurring seq initialization" (recur nacc (inc i) arr))) acc)))) -(declare tv-editable-root tv-editable-tail TransientVector deref +(declare tv-editable-root tv-editable-tail TransientVector pr-sequential-writer pr-writer chunked-seq) (defprotocol APersistentVector @@ -7269,7 +7269,7 @@ reduces them without incurring seq initialization" (deftype Box [^:mutable val]) -(declare create-inode-seq create-array-node-seq reset! create-node atom deref) +(declare create-inode-seq create-array-node-seq create-node) (defn- mask [hash shift] (bit-and (bit-shift-right-zero-fill hash shift) 0x01f)) From e596c355dbecec58beffc3eafc3f7f86fb4ddbb6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 11 Nov 2022 12:13:06 -0500 Subject: [PATCH 3841/4033] CLJS-3391: add cljs.test/run-test (#192) Co-authored-by: Michiel Borkent --- src/main/cljs/cljs/test.cljc | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/cljs/cljs/test.cljc b/src/main/cljs/cljs/test.cljc index 1722d2d62..5afb03ca2 100644 --- a/src/main/cljs/cljs/test.cljc +++ b/src/main/cljs/cljs/test.cljc @@ -396,6 +396,36 @@ [(fn [] (clear-env!))])))) +(defmacro run-test + "Runs a single test. + + Because the intent is to run a single test, there is no check for the namespace test-ns-hook." + [test-symbol] + (let [test-var (ana-api/resolve &env test-symbol)] + (cond (nil? test-var) + `(cljs.core/*print-err-fn* "Unable to resolve" ~(str test-symbol) "to a test function.") + (not (:test test-var)) + `(cljs.core/*print-err-fn* ~(str test-symbol) "is not a test") + :else + (let [ns (:ns test-var)] + `(let [env# (get-current-env)] + (run-block + (concat + [(fn [] + (when (nil? env#) + (set-env! (empty-env))) + ~(when (ana-api/resolve &env 'cljs-test-once-fixtures) + `(update-current-env! [:once-fixtures] assoc '~ns + ~(symbol (str ns) "cljs-test-once-fixtures"))) + ~(when (ana-api/resolve &env 'cljs-test-each-fixtures) + `(update-current-env! [:each-fixtures] assoc '~ns + ~(symbol (str ns) "cljs-test-each-fixtures"))))] + (test-vars-block + [(var ~test-symbol)]) + [(fn [] + (when (nil? env#) + (clear-env!)))]))))))) + ;; ============================================================================= ;; Fixes From 9359f40e7537307b9272fd322ae5cfff88e079f2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 15 Nov 2022 15:06:06 -0500 Subject: [PATCH 3842/4033] CLJS-3369: Speed up random-uuid by generating 4 digits at a time (#194) The speedup for (simple-benchmark [] (random-uuid) num), where num 1e5, 1e6 and 1e7 is respectively: iterations: 1e5 1e6 1e7 current (ms): 161 1596 15649 patch (ms): 55 522 5193 So there is about 3x speedup. Co-authored-by: Adam Kalisz --- src/main/cljs/cljs/core.cljs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 529fc4514..2821ff25f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11602,17 +11602,19 @@ reduces them without incurring seq initialization" (defn random-uuid "Returns a pseudo-randomly generated UUID instance (i.e. type 4)." [] - (letfn [(hex [] (.toString (rand-int 16) 16))] - (let [rhex (.toString (bit-or 0x8 (bit-and 0x3 (rand-int 16))) 16)] + (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) + 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 (hex) (hex) (hex) (hex) - (hex) (hex) (hex) (hex) "-" - (hex) (hex) (hex) (hex) "-" - "4" (hex) (hex) (hex) "-" - rhex (hex) (hex) (hex) "-" - (hex) (hex) (hex) (hex) - (hex) (hex) (hex) (hex) - (hex) (hex) (hex) (hex)))))) + (str (quad-hex) (quad-hex) "-" (quad-hex) "-" + ver-tripple-hex "-" res-tripple-hex "-" + (quad-hex) (quad-hex) (quad-hex)))))) (defn uuid? "Return true if x is a UUID." From c8ab636594f3914a2430b89a7d09fa1e6693e2db Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 19 Nov 2022 11:49:26 -0500 Subject: [PATCH 3843/4033] CLJS-3014: Promote Error->map to be a core fn --- src/main/cljs/cljs/core.cljs | 37 +++++++++++++++++++++ src/main/cljs/cljs/repl.cljs | 26 +-------------- src/test/cljs/cljs/core_test.cljs | 53 +++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 2821ff25f..f7a0caf25 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11691,6 +11691,43 @@ reduces them without incurring seq initialization" (when (instance? ExceptionInfo ex) (.-cause ex))) +(defn Throwable->map + "Constructs a data representation for an Error with keys: + :cause - root cause message + :phase - error phase + :via - cause chain, with cause keys: + :type - exception class symbol + :message - exception message + :data - ex-data + :at - top stack element + :trace - root cause stack elements" + [o] + (let [base (fn [t] + (merge {:type (cond + (instance? ExceptionInfo t) `ExceptionInfo + (instance? js/Error t) (symbol "js" (.-name t)) + :else nil)} + (when-let [msg (ex-message t)] + {:message msg}) + (when-let [ed (ex-data t)] + {:data ed}) + #_(let [st (extract-canonical-stacktrace t)] + (when (pos? (count st)) + {:at st})))) + via (loop [via [], t o] + (if t + (recur (conj via t) (ex-cause t)) + via)) + root (peek via)] + (merge {:via (vec (map base via)) + :trace nil #_(extract-canonical-stacktrace (or root o))} + (when-let [root-msg (ex-message root)] + {:cause root-msg}) + (when-let [data (ex-data root)] + {:data data}) + (when-let [phase (-> o ex-data :clojure.error/phase)] + {:phase phase})))) + (defn comparator "Returns an JavaScript compatible comparator based upon pred." [pred] diff --git a/src/main/cljs/cljs/repl.cljs b/src/main/cljs/cljs/repl.cljs index 964de2d38..16116ea46 100644 --- a/src/main/cljs/cljs/repl.cljs +++ b/src/main/cljs/cljs/repl.cljs @@ -70,31 +70,7 @@ :at - top stack element :trace - root cause stack elements" [o] - (let [base (fn [t] - (merge {:type (cond - (instance? ExceptionInfo t) `ExceptionInfo - (instance? js/Error t) (symbol "js" (.-name t)) - :else nil)} - (when-let [msg (ex-message t)] - {:message msg}) - (when-let [ed (ex-data t)] - {:data ed}) - #_(let [st (extract-canonical-stacktrace t)] - (when (pos? (count st)) - {:at st})))) - via (loop [via [], t o] - (if t - (recur (conj via t) (ex-cause t)) - via)) - root (peek via)] - (merge {:via (vec (map base via)) - :trace nil #_(extract-canonical-stacktrace (or root o))} - (when-let [root-msg (ex-message root)] - {:cause root-msg}) - (when-let [data (ex-data root)] - {:data data}) - (when-let [phase (-> o ex-data :clojure.error/phase)] - {:phase phase})))) + (Throwable->map o)) (defn ex-triage "Returns an analysis of the phase, error, cause, and location of an error that occurred diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 0f93b8c63..11bf3b859 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -702,6 +702,59 @@ (is (= (str (ex-info "abc" {:x 1} "def")) "#error {:message \"abc\", :data {:x 1}, :cause \"def\"}")) (is (not (instance? cljs.core.ExceptionInfo (js/Error.))))) +(deftest test-Throwable->map + (let [msg-0 "message-0" + data-0 {:a 0} + msg-1 "message-1" + data-1 {:b 1} + msg-2 "message-2"] + ;; Check ex-info style error + (let [ex (ex-info msg-0 data-0) + m (Throwable->map ex)] + (is (= msg-0 (:cause m))) + (is (= data-0 (:data m))) + (is (nil? (:trace m))) + (let [via (:via m)] + (is (== 1 (count via))) + ;; Check via 0 + (is (= `ExceptionInfo (:type (nth via 0)))) + (is (= msg-0 (:message (nth via 0)))) + (is (= data-0 (:data (nth via 0)))))) + ;; Check plain js/Error style error + (let [ex (js/Error. msg-0) + m (Throwable->map ex)] + (is (= msg-0 (:cause m))) + (is (nil? (:data m))) + (is (nil? (:trace m))) + (let [via (:via m)] + (is (== 1 (count via))) + ;; Check via 0 + (is (= 'js/Error (:type (nth via 0)))) + (is (= msg-0 (:message (nth via 0)))) + (is (nil? (:data (nth via 0)))))) + ;; Check ex-info style with chain ending in js/Error + (let [ex (ex-info msg-0 data-0 + (ex-info msg-1 data-1 + (js/Error. msg-2))) + m (Throwable->map ex)] + (is (= msg-2 (:cause m))) + (is (nil? (:data m))) + (is (nil? (:trace m))) + (let [via (:via m)] + (is (== 3 (count via))) + ;; Check via 0 + (is (= `ExceptionInfo (:type (nth via 0)))) + (is (= msg-0 (:message (nth via 0)))) + (is (= data-0 (:data (nth via 0)))) + ;; Check via 1 + (is (= `ExceptionInfo (:type (nth via 1)))) + (is (= msg-1 (:message (nth via 1)))) + (is (= data-1 (:data (nth via 1)))) + ;; Check via 2 + (is (= 'js/Error (:type (nth via 2)))) + (is (= msg-2 (:message (nth via 2)))) + (is (nil? (:data (nth via 2)))))))) + (deftest test-2067 (is (= 0 (reduce-kv (fn [x k _] From 9cd4bb62746ab549a19bd54b0ac10fcd41094786 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 18 Jan 2023 23:04:59 -0500 Subject: [PATCH 3844/4033] CLJS-3394: Add ECMASCRIPT options for 2018-2021 (#197) This patch adds options for language levels ECMASCRIPT_2018, ECMASCRIPT_2019, ECMASCRIPT_2020, and ECMASCRIPT_2021, which are available in Closure Compiler. Co-authored-by: Will Cohen --- src/main/clojure/cljs/closure.clj | 7 ++++++- src/main/clojure/cljs/compiler.cljc | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3e2addfe2..d45e1bbab 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -235,7 +235,8 @@ (def lang-level [:ecmascript3 :ecmascript5 :ecmascript5-strict :ecmascript6 :ecmascript6-strict - :ecmascript-2015 :ecmascript-2016 :ecmascript-2017 :ecmascript-next + :ecmascript-2015 :ecmascript-2016 :ecmascript-2017 :ecmascript-2018 + :ecmascript-2019 :ecmascript-2020 :ecmascript-2021 :ecmascript-next :no-transpile]) (defn expand-lang-key [key] @@ -252,6 +253,10 @@ :ecmascript-2015 CompilerOptions$LanguageMode/ECMASCRIPT_2015 :ecmascript-2016 CompilerOptions$LanguageMode/ECMASCRIPT_2016 :ecmascript-2017 CompilerOptions$LanguageMode/ECMASCRIPT_2017 + :ecmascript-2018 CompilerOptions$LanguageMode/ECMASCRIPT_2018 + :ecmascript-2019 CompilerOptions$LanguageMode/ECMASCRIPT_2019 + :ecmascript-2020 CompilerOptions$LanguageMode/ECMASCRIPT_2020 + :ecmascript-2021 CompilerOptions$LanguageMode/ECMASCRIPT_2021 :ecmascript-next CompilerOptions$LanguageMode/ECMASCRIPT_NEXT)) (defn set-options diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 540bb6c92..b96c09b36 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -49,6 +49,7 @@ [lang (keyword (string/replace (name lang) #"^ecmascript" "es"))]))) [:ecmascript5 :ecmascript5-strict :ecmascript6 :ecmascript6-strict :ecmascript-2015 :ecmascript6-typed :ecmascript-2016 :ecmascript-2017 + :ecmascript-2018 :ecmascript-2019 :ecmascript-2020 :ecmascript-2021 :ecmascript-next])) (def ^:dynamic *recompiled* nil) From 08caed4e97991708694d4e52c9621f3f4e68ba9a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 25 Jan 2023 13:06:22 -0500 Subject: [PATCH 3845/4033] REPL vars missing `:dynamic` meta (#199) --- src/main/cljs/cljs/core.cljs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f7a0caf25..20b627d91 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -205,19 +205,23 @@ nil) (def - ^{:doc "bound in a repl thread to the most recent value printed"} + ^{:dynamic true + :doc "bound in a repl thread to the most recent value printed"} *1) (def - ^{:doc "bound in a repl thread to the second most recent value printed"} + ^{:dynamic true + :doc "bound in a repl thread to the second most recent value printed"} *2) (def - ^{:doc "bound in a repl thread to the third most recent value printed"} + ^{:dynamic true + :doc "bound in a repl thread to the third most recent value printed"} *3) (def - ^{:doc "bound in a repl thread to the most recent exception caught by the repl"} + ^{:dynamic true + :doc "bound in a repl thread to the most recent exception caught by the repl"} *e) (defn truth_ From 6dac3165d4bc8f785741ea86aef946b11c9d7d0f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 25 Jan 2023 13:06:57 -0500 Subject: [PATCH 3846/4033] CLJS-2268 Make clojure.string/escape constent with Clojure (#198) - treat `cmap` as a function, not as a map Co-authored-by: Erik Assum --- src/main/cljs/clojure/string.cljs | 2 +- src/test/cljs/clojure/string_test.cljs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/clojure/string.cljs b/src/main/cljs/clojure/string.cljs index 23956b1b6..44e8b4eef 100644 --- a/src/main/cljs/clojure/string.cljs +++ b/src/main/cljs/clojure/string.cljs @@ -240,7 +240,7 @@ (if (== length index) (. buffer (toString)) (let [ch (.charAt s index) - replacement (get cmap ch)] + replacement (cmap ch)] (if-not (nil? replacement) (.append buffer (str replacement)) (.append buffer ch)) diff --git a/src/test/cljs/clojure/string_test.cljs b/src/test/cljs/clojure/string_test.cljs index 5837f806b..538d2636b 100644 --- a/src/test/cljs/clojure/string_test.cljs +++ b/src/test/cljs/clojure/string_test.cljs @@ -81,7 +81,9 @@ (is (= " \\\"foo\\\" " (s/escape " \"foo\" " {\" "\\\""}))) (is (= "faabor" - (s/escape "foobar" {\a \o, \o \a})))) + (s/escape "foobar" {\a \o, \o \a}))) + (is (= "aaa" + (s/escape "foo" (fn [c] \a))))) (testing "Testing string replace-first" (is (= "barbarfoo" (s/replace-first "foobarfoo" "foo" "bar"))) From 30171a3c423ee265e0aa814f2063ebb042931b75 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 30 Mar 2023 11:40:46 -0400 Subject: [PATCH 3847/4033] CLJS-3400: macroexpand does not expand and and or without args correctly (#202) Co-authored-by: Michiel Borkent --- src/main/clojure/cljs/analyzer.cljc | 82 +++++++++++++++-------------- src/test/cljs/cljs/core_test.cljs | 5 ++ 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 132a3a981..574bfe6cc 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4009,63 +4009,65 @@ (defn macroexpand-1* [env form] - (let [op (first form)] - (if (contains? specials op) - (do - (when (= 'ns op) - (do-macroexpand-check env form (get-expander 'cljs.core/ns-special-form env))) - form) - ;else + (if (seq? form) + (let [op (first form)] + (if (contains? specials op) + (do + (when (= 'ns op) + (do-macroexpand-check env form (get-expander 'cljs.core/ns-special-form env))) + form) + ;else (if-some [mac-var (when (symbol? op) (get-expander op env))] (#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]] :cljs [do]) - (do-macroexpand-check env form mac-var) - (let [form' (try - #?(:cljs (check-macro-arity mac-var form)) - (apply @mac-var form env (rest form)) - #?(:clj (catch ArityException e - (throw (ArityException. (- (.actual e) 2) (.name e))))) - (catch #?(:clj Throwable :cljs :default) e - (throw (ex-info nil (error-data env :macroexpansion (var->sym mac-var)) e))))] - (if #?(:clj (seq? form') :cljs (impl/cljs-seq? form')) - (let [sym' (first form') - sym (first form)] - (if #?(:clj (= sym' 'js*) - :cljs (symbol-identical? sym' impl/JS_STAR_SYM)) - (let [sym (if (some? (namespace sym)) - sym - (symbol "cljs.core" (str sym))) - js-op {:js-op sym} - numeric #?(:clj (-> mac-var meta ::numeric) - :cljs (let [mac-var-ns (symbol (namespace (.-sym mac-var))) - mac-var-name (symbol (name (.-sym mac-var)))] - (get-in @env/*compiler* - [::namespaces mac-var-ns :defs mac-var-name :meta ::numeric]))) - js-op (if (true? numeric) - (assoc js-op :numeric true) - js-op)] - (vary-meta form' merge js-op)) - form')) - form'))) + (do-macroexpand-check env form mac-var) + (let [form' (try + #?(:cljs (check-macro-arity mac-var form)) + (apply @mac-var form env (rest form)) + #?(:clj (catch ArityException e + (throw (ArityException. (- (.actual e) 2) (.name e))))) + (catch #?(:clj Throwable :cljs :default) e + (throw (ex-info nil (error-data env :macroexpansion (var->sym mac-var)) e))))] + (if #?(:clj (seq? form') :cljs (impl/cljs-seq? form')) + (let [sym' (first form') + sym (first form)] + (if #?(:clj (= sym' 'js*) + :cljs (symbol-identical? sym' impl/JS_STAR_SYM)) + (let [sym (if (some? (namespace sym)) + sym + (symbol "cljs.core" (str sym))) + js-op {:js-op sym} + numeric #?(:clj (-> mac-var meta ::numeric) + :cljs (let [mac-var-ns (symbol (namespace (.-sym mac-var))) + mac-var-name (symbol (name (.-sym mac-var)))] + (get-in @env/*compiler* + [::namespaces mac-var-ns :defs mac-var-name :meta ::numeric]))) + js-op (if (true? numeric) + (assoc js-op :numeric true) + js-op)] + (vary-meta form' merge js-op)) + form')) + form'))) (if (symbol? op) (let [opname (str op)] (cond (identical? \. - #?(:clj (first opname) - :cljs (.charAt opname 0))) + #?(:clj (first opname) + :cljs (.charAt opname 0))) (let [[target & args] (next form)] (with-meta (list* #?(:clj '. :cljs impl/DOT_SYM) target (symbol (subs opname 1)) args) (meta form))) (identical? \. - #?(:clj (last opname) - :cljs (.charAt opname (dec (. opname -length))))) + #?(:clj (last opname) + :cljs (.charAt opname (dec (. opname -length))))) (with-meta (list* #?(:clj 'new :cljs impl/NEW_SYM) (symbol (subs opname 0 (dec (count opname)))) (next form)) (meta form)) :else form)) - form))))) + form)))) + form)) (defn macroexpand-1 "Given a env, an analysis environment, and form, a ClojureScript form, diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 11bf3b859..a71d0f74f 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1982,3 +1982,8 @@ (deftest test-cljs-3386 (is (nil? (cljs-3386-test-fn 1 2))) (is (= '(3 4) (cljs-3386-test-fn 1 2 3 4)))) + +(deftest test-cljs-3400 + (testing "macroexpanding non-seqs should work" + (is (true? (macroexpand '(and)))) + (is (nil? (macroexpand '(or)))))) From 0ccd19fc79f04a5082764901bd6413a5841ca428 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 10 Apr 2023 08:54:43 -0400 Subject: [PATCH 3848/4033] CLJS-3401: dedupe '+ and '_PLUS symbols with :optimize-constants (#203) Co-authored-by: Michiel Borkent --- src/main/clojure/cljs/analyzer.cljc | 6 ++++-- src/test/clojure/cljs/analyzer_tests.clj | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 574bfe6cc..65f68af46 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -513,14 +513,16 @@ (symbol? value) "cst$sym$" :else (throw - #?(:clj (Exception. (str "constant type " (type value) " not supported")) - :cljs (js/Error. (str "constant type " (type value) " not supported"))))) + #?(:clj (Exception. (str "constant type " (type value) " not supported")) + :cljs (js/Error. (str "constant type " (type value) " not supported"))))) name (if (keyword? value) (subs (str value) 1) (str value)) name (if (= "." name) "_DOT_" (-> name + (string/replace "_" "__") + (string/replace "$" "$$") (string/replace "-" "_DASH_") (munge) (string/replace "." "$") diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 9b823bdac..8ae750ddc 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1512,3 +1512,9 @@ (Foo. nil nil))))) (is (= 1 (count @ws))) (is (string/starts-with? (first @ws) "Wrong number of args (2) passed to Foo")))) + +(deftest test-cljs-3401 + (is (not= (ana/gen-constant-id '_PLUS_) + (ana/gen-constant-id '+))) + (is (not= (ana/gen-constant-id 'foo.bar) + (ana/gen-constant-id 'foo$bar)))) From 64be690b715ca0bbd6ce72edabbc9a0894727d7a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 29 Apr 2023 07:06:32 -0400 Subject: [PATCH 3849/4033] CLJS-3399: :as-alias does not work with symbols (#204) resolve-ns-alias needs to look at as-aliases. Add tests. --- src/main/clojure/cljs/analyzer.cljc | 7 +++++-- src/test/cljs/cljs/ns_test.cljs | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 65f68af46..f291d8ed9 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -917,8 +917,11 @@ ([env name] (resolve-ns-alias env name (symbol name))) ([env name not-found] - (let [sym (symbol name)] - (get (:requires (:ns env)) sym not-found)))) + (let [sym (symbol name) + {:keys [requires as-aliases]} (:ns env)] + (or (get requires sym) + (get as-aliases sym) + not-found)))) (defn resolve-macro-ns-alias ([env name] diff --git a/src/test/cljs/cljs/ns_test.cljs b/src/test/cljs/cljs/ns_test.cljs index 83c48d306..004085116 100644 --- a/src/test/cljs/cljs/ns_test.cljs +++ b/src/test/cljs/cljs/ns_test.cljs @@ -15,7 +15,8 @@ [cljs.test] [cljs.ns-test.foo :refer [baz]] [clojure.set :as s :refer [intersection] :rename {intersection itsc}] - [cljs.analyzer :as ana]) + [cljs.analyzer :as ana] + [fake.ns :as-alias fake]) (:use [cljs.ns-test.bar :only [quux]])) (def + -) @@ -43,3 +44,7 @@ (is (.isArrayLike js/goog array-like)) (is (goog/isArrayLike array-like)) (is (goog-alias/isArrayLike array-like)))) + +(deftest test-cljs-3399 + (is (= ::fake/foo :fake.ns/foo) + (= `fake/foo `fake.ns/foo))) From 02b7ead0e5a042d2a73c0324a2aa9f439bd0f49c Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sat, 29 Apr 2023 07:14:58 -0400 Subject: [PATCH 3850/4033] CLJS-3398: Docstring of with-redefs should mention usage of ^:dynamic in production --- src/main/clojure/cljs/core.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b479aa036..c4858d629 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -2257,7 +2257,11 @@ temp-value-exprs will be evaluated and each resulting value will replace in parallel the root value of its var. After the body is executed, the root values of all the vars will be set back to their - old values. Useful for mocking out functions during testing." + old values. Useful for mocking out functions during testing. + + Note that under advanced compilation vars are statically resolved, + preventing with-redef usage. If var redefinition is desired in a production + setting then the var to be redefined must be declared ^:dynamic." [bindings & body] (core/let [names (take-nth 2 bindings) vals (take-nth 2 (drop 1 bindings)) From 07d2ebc8b79476ee619c6ccec7f69010ced32ee1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 1 May 2023 13:02:18 -0400 Subject: [PATCH 3851/4033] CLJS-3395: `(set! a -x false)` doesn't work (#205) Count the forms, add runtime test case --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f291d8ed9..c6dee722c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2604,7 +2604,7 @@ (defmethod parse 'set! [_ env [_ target val alt :as form] _ _] - (let [[target val] (if alt + (let [[target val] (if (= 4 (count form)) ;; (set! o -prop val) [`(. ~target ~val) alt] [target val])] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index a71d0f74f..2759fae1d 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1987,3 +1987,9 @@ (testing "macroexpanding non-seqs should work" (is (true? (macroexpand '(and)))) (is (nil? (macroexpand '(or)))))) + +(deftest test-cljs-3395 + (testing "(set! foo -bar baz) pattern" + (let [a #js {}] + (set! a -x false) + (is (false? (.-x a)))))) From a1fa32b80b28d73db438c6caa79c869063d196cb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 1 May 2023 13:32:33 -0400 Subject: [PATCH 3852/4033] CLJS-3392: datafy support for js/Error and ExceptionInfo (#206) Co-authored-by: Mike Fikes --- src/main/cljs/clojure/datafy.cljs | 6 ++++++ src/test/cljs/clojure/datafy_test.cljs | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/cljs/clojure/datafy.cljs b/src/main/cljs/clojure/datafy.cljs index 6a969d8ae..fa141f038 100644 --- a/src/main/cljs/clojure/datafy.cljs +++ b/src/main/cljs/clojure/datafy.cljs @@ -41,6 +41,12 @@ (with-meta [(deref r)] (meta r))) (extend-protocol p/Datafiable + js/Error + (datafy [x] (Throwable->map x)) + + ExceptionInfo + (datafy [x] (Throwable->map x)) + Var (datafy [r] (datify-ref r)) diff --git a/src/test/cljs/clojure/datafy_test.cljs b/src/test/cljs/clojure/datafy_test.cljs index ff108d0cc..b4a04fb45 100644 --- a/src/test/cljs/clojure/datafy_test.cljs +++ b/src/test/cljs/clojure/datafy_test.cljs @@ -22,3 +22,14 @@ x (with-meta original {`clojure.core.protocols/datafy (fn [_] datafied)})] (is (= datafied (d/datafy x))) (is (= {:clojure.datafy/obj original} (meta (d/datafy x))))))) + +(deftest datafy-js-errors-test + (let [x (js/Error. "foo")] + (is (= (Throwable->map x) (d/datafy x)))) + ;; Ensure we can datafy objects that extend js/Error + (let [x (js/RangeError. "x must be between 1 and 5")] + (is (= (Throwable->map x) (d/datafy x))))) + +(deftest datafy-ex-info-test + (let [x (ex-info "foo" {:a 1} (ex-info "bar" {:b 2}))] + (is (= (Throwable->map x) (d/datafy x))))) From 240a9a5e8a1e73e7e26c7a9709c9a2e0afd75be6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 1 May 2023 14:59:06 -0400 Subject: [PATCH 3853/4033] CLJS-3381: Invokable JS namespaces used as constructors not hinted properly (#207) * tag invokeable :node / :global nses as 'js * add node-module-index to infer-test-helper * add test case --- src/main/clojure/cljs/analyzer.cljc | 6 +++-- src/test/clojure/cljs/externs_infer_tests.clj | 26 +++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index c6dee722c..2bbc2a7ea 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1220,11 +1220,13 @@ :node {:name (symbol (str current-ns) (munge-node-lib (resolve-ns-alias env ns))) :op :js-var - :ns current-ns} + :ns current-ns + :tag 'js} :global {:name (symbol (str current-ns) (munge-global-export (resolve-ns-alias env ns))) :op :js-var - :ns current-ns}))) + :ns current-ns + :tag 'js}))) (defn resolve-import "goog.modules are deterministically assigned to a property of the namespace, diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 6e52fd641..f832ed730 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -51,7 +51,7 @@ (is (= 'js/Foo (ana/js-tag '[baz] :ret-tag externs))))) (defn infer-test-helper - [{:keys [forms externs warnings warn js-dependency-index with-core? opts]}] + [{:keys [forms externs warnings warn js-dependency-index node-module-index with-core? opts]}] (let [test-cenv (atom (cond-> (if with-core? @@ -60,7 +60,8 @@ {::ana/externs (externs/externs-map (closure/load-externs {:externs (or externs [])}))}) - js-dependency-index (assoc :js-dependency-index js-dependency-index))) + js-dependency-index (assoc :js-dependency-index js-dependency-index) + node-module-index (assoc :node-module-index node-module-index))) wrap (if with-core? #(comp/with-core-cljs nil %) #(do (%)))] @@ -453,6 +454,27 @@ "Object.someProperty;"]) res))))) +(deftest test-cljs-3381 + (testing "invokeable js namespaces not hinted properly" + (let [ws (atom []) + res (infer-test-helper + {:node-module-index #{"markdown-it"} + :forms '[(ns foo.core + (:require [markdown-it])) + (defonce mdi + (doto (new markdown-it + (js-obj + "linkify" true + "typographer" true)) + (.renderInline mdi "hi")))] + :warnings ws + :warn true + :with-core? false + :target :nodejs})] + (is (= (unsplit-lines + ["Object.renderInline;"]) + res))))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn From 47d43bedeb519de15827c8b7ff3f414e97e20914 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 18 May 2023 23:13:02 -0400 Subject: [PATCH 3854/4033] Bump closure lib to 2023 release (#208) * bump closure lib * remove explicit cases of setting :language-in in various tests * set default to :ecmascript-next, AOT compile core with :es-next --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- resources/self_parity_test.edn | 1 - resources/test.edn | 1 - script/bootstrap | 2 +- script/test | 1 - script/test-self-parity | 2 +- script/test-simple | 1 - src/main/clojure/cljs/closure.clj | 4 ++-- src/test/clojure/cljs/build_api_tests.clj | 5 ----- src/test/clojure/cljs/test_util.clj | 1 - 12 files changed, 7 insertions(+), 17 deletions(-) diff --git a/deps.edn b/deps.edn index c1f13dc16..d888580f0 100644 --- a/deps.edn +++ b/deps.edn @@ -4,7 +4,7 @@ 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"} - org.clojure/google-closure-library {:mvn/version "0.0-20211011-0726fdeb"} + org.clojure/google-closure-library {:mvn/version "0.0-20230227-c7c0a541"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/tools.reader {:mvn/version "1.3.6"} org.clojure/test.check {:mvn/version "1.1.1"}} diff --git a/pom.template.xml b/pom.template.xml index aaf6411d2..4ddb49875 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -35,7 +35,7 @@ org.clojure google-closure-library - 0.0-20211011-0726fdeb + 0.0-20230227-c7c0a541 org.clojure diff --git a/project.clj b/project.clj index 821f29dd7..6b273d48a 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/tools.reader "1.3.6"] [org.clojure/test.check "1.1.1" :scope "test"] [com.cognitect/transit-java "1.0.362"] - [org.clojure/google-closure-library "0.0-20211011-0726fdeb"] + [org.clojure/google-closure-library "0.0-20230227-c7c0a541"] [com.google.javascript/closure-compiler-unshaded "v20220502"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main cljs.main} diff --git a/resources/self_parity_test.edn b/resources/self_parity_test.edn index 502d1bcd8..2c5baffe7 100644 --- a/resources/self_parity_test.edn +++ b/resources/self_parity_test.edn @@ -1,6 +1,5 @@ {:optimizations :none :main self-parity.test - :language-in :es6 :language-out :es6 :verbose true :output-to "builds/out-self-parity/main.js" diff --git a/resources/test.edn b/resources/test.edn index 1ec229c32..a8f258139 100644 --- a/resources/test.edn +++ b/resources/test.edn @@ -9,7 +9,6 @@ :npm-deps {:lodash "4.17.4"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true - :language-in :es6 :language-out :es5 :foreign-libs [{:file "src/test/cljs/calculator_global.js" diff --git a/script/bootstrap b/script/bootstrap index f28d6e9ea..ecc2d2631 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,7 +6,7 @@ CLOJURE_RELEASE="1.9.0" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20220502" -GCLOSURE_LIB_RELEASE="0.0-20211011-0726fdeb" +GCLOSURE_LIB_RELEASE="0.0-20230227-c7c0a541" TREADER_RELEASE="1.3.6" TEST_CHECK_RELEASE="1.1.1" diff --git a/script/test b/script/test index b21d32360..7d9d31042 100755 --- a/script/test +++ b/script/test @@ -22,7 +22,6 @@ if ! bin/cljsc src/test/cljs "{:optimizations :advanced :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true - :language-in :es6 :language-out :es5 :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] diff --git a/script/test-self-parity b/script/test-self-parity index bb8f7a100..2af3069a9 100755 --- a/script/test-self-parity +++ b/script/test-self-parity @@ -15,7 +15,7 @@ mkdir -p builds/out-self-parity/clojure/test mv clojure/template.clj builds/out-self-parity/clojure mv clojure/test builds/out-self-parity/clojure -if ! bin/cljsc src/test/self/self_parity "{:optimizations :simple :language-in :es6 :language-out :es5 :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}"; then +if ! bin/cljsc src/test/self/self_parity "{:optimizations :simple :language-out :es5 :output-to \"builds/out-self-parity/main.js\" :output-dir \"builds/out-self-parity\" :main self-parity.test :target :nodejs}"; then >&2 echo ClojureScript compilation failed exit 1 fi; diff --git a/script/test-simple b/script/test-simple index f5aa833d9..90495bea1 100755 --- a/script/test-simple +++ b/script/test-simple @@ -22,7 +22,6 @@ if ! bin/cljsc src/test/cljs "{:optimizations :simple :npm-deps {:lodash \"4.17.4\"} :closure-warnings {:non-standard-jsdoc :off :global-this :off} :install-deps true - :language-in :es6 :language-out :es5 :foreign-libs [{:file \"src/test/cljs/calculator_global.js\" :provides [\"calculator\"] diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index d45e1bbab..e689af1b2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2598,7 +2598,7 @@ (ensure-module-opts) (nil? (:language-in opts)) - (assoc :language-in :es6) + (assoc :language-in :ecmascript-next) (:stable-names opts) (as-> opts @@ -3512,7 +3512,7 @@ (ana/write-analysis-cache 'cljs.core cache src) (ana/write-analysis-cache 'cljs.core tcache src)) (create-client-js-file - {:language-in :es6 + {:language-in :ecmascript-next :optimizations :simple :output-dir "aot_out"} (io/file "resources" "brepl_client.js")) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 390f7f41b..e788c1ace 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -126,7 +126,6 @@ ["common" "app"]) opts {:optimizations :simple :output-dir out - :language-in :es6 :modules {:common {:entries #{"hello.foo.bar"} :output-to (.getAbsolutePath common-tmp)} :app {:entries #{"hello.core"} @@ -191,7 +190,6 @@ :opts {:output-dir output-dir :optimizations :none - :language-in :es6 :verbose true :foreign-libs [{:file "src/test/cljs_build/loader_test/foreignA.js" :provides ["foreign.a"]} @@ -681,7 +679,6 @@ (let [out (io/file (test/tmp-dir) "cljs-2903-out") opts {:output-to (.getPath (io/file out "main.js")) :output-dir (.getPath out) - :language-in :es6 :fingerprint true :stable-names true :optimizations :advanced}] @@ -715,7 +712,6 @@ out-file (io/file out "main.js") {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build")) :opts {:main 'trivial.core - :language-in :es6 :output-dir out :output-to (.getPath out-file) :optimizations :advanced}} @@ -730,7 +726,6 @@ opts {:main 'trivial.core :output-to (.getPath out-file) :output-dir out - :language-in :es6 :optimizations :none} cenv (env/default-compiler-env)] (test/delete-out-files out) diff --git a/src/test/clojure/cljs/test_util.clj b/src/test/clojure/cljs/test_util.clj index 0a71f86ce..eefe5d82e 100644 --- a/src/test/clojure/cljs/test_util.clj +++ b/src/test/clojure/cljs/test_util.clj @@ -45,7 +45,6 @@ :output-dir output-dir :optimizations :advanced :verbose true - :language-in :es6 :modules {:cljs-base {:output-to (str (io/file output-dir "module-main.js"))} From 8a9d38eea79a24f3f8e43199689dc026ece7c537 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 22 May 2023 14:57:35 -0400 Subject: [PATCH 3855/4033] Late Spring cleaning of Node lib indexing (#209) * move node_module indexing logic into a separate namespace * refactor into smaller units of functionality * add unit tests * enhance export processing a little --- src/main/clojure/cljs/closure.clj | 134 +------------- src/main/clojure/cljs/foreign/node.clj | 185 ++++++++++++++++++++ src/main/clojure/cljs/util.cljc | 3 +- src/test/clojure/cljs/foreign/node_test.clj | 86 +++++++++ 4 files changed, 274 insertions(+), 134 deletions(-) create mode 100644 src/main/clojure/cljs/foreign/node.clj create mode 100644 src/test/clojure/cljs/foreign/node_test.clj diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index e689af1b2..ca46b4a1d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -14,6 +14,7 @@ [cljs.analyzer :as ana] [cljs.source-map :as sm] [cljs.env :as env] + [cljs.foreign.node :refer [package-json-entries node-file-seq->libs-spec*]] [cljs.js-deps :as deps] [clojure.java.io :as io] [clojure.java.shell :as sh] @@ -1958,35 +1959,6 @@ (apply str)) (.toSource closure-compiler ast-root))))) -(defn- package-json-entries - "Takes options and returns a sequence with the desired order of package.json - entries for the given :package-json-resolution mode. If no mode is provided, - defaults to :webpack (if no target is set) and :nodejs (if the target is - :nodejs)." - [opts] - {:pre [(or (= (:package-json-resolution opts) :webpack) - (= (:package-json-resolution opts) :nodejs) - (and (sequential? (:package-json-resolution opts)) - (every? string? (:package-json-resolution opts))) - (not (contains? opts :package-json-resolution)))]} - (let [modes {:nodejs ["main"] - :webpack ["browser" "module" "main"]}] - (if-let [mode (:package-json-resolution opts)] - (if (sequential? mode) mode (get modes mode)) - (case (:target opts) - :nodejs (:nodejs modes) - (:webpack modes))))) - -(comment - (= (package-json-entries {}) ["browser" "module" "main"]) - (= (package-json-entries {:package-json-resolution :nodejs}) ["main"]) - (= (package-json-entries {:package-json-resolution :webpack}) ["browser" "module" "main"]) - (= (package-json-entries {:package-json-resolution ["foo" "bar" "baz"]}) ["foo" "bar" "baz"]) - (= (package-json-entries {:target :nodejs}) ["main"]) - (= (package-json-entries {:target :nodejs :package-json-resolution :nodejs}) ["main"]) - (= (package-json-entries {:target :nodejs :package-json-resolution :webpack}) ["browser" "module" "main"]) - (= (package-json-entries {:target :nodejs :package-json-resolution ["foo" "bar"]}) ["foo" "bar"])) - (defn- sorting-dependency-options [] (try (if (contains? (:flags (clojure.reflect/reflect DependencyOptions)) :abstract) @@ -2770,110 +2742,6 @@ (get-in @env/*compiler* [::transitive-dep-set modules]))))))) (filterv identity)))) -(defn- node-file-seq->libs-spec* - "Given a sequence of non-nested node_module paths where the extension ends in - `.js/.json`, return lib-spec maps for each path containing at least :file, - :module-type, and :provides." - [module-fseq opts] - (letfn [(package-json? [path] - (= "package.json" (.getName (io/file path)))) - - (top-level-package-json? [path] - (boolean (re-find #"node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$" path))) - - ;; the path sans the package.json part - ;; i.e. some_lib/package.json -> some_lib - (trim-package-json [s] - (if (string/ends-with? s "package.json") - (subs s 0 (- (count s) 12)) - s)) - - (trim-relative [path] - (cond-> path - (string/starts-with? path "./") - (subs 2))) - - (add-exports [pkg-jsons] - (reduce-kv - (fn [pkg-jsons path {:strs [exports] :as pkg-json}] - ;; "exports" can just be a dupe of "main", i.e. a string - ignore - ;; https://nodejs.org/api/packages.html#main-entry-point-export - (if (string? exports) - pkg-jsons - (reduce-kv - (fn [pkg-jsons export _] - ;; NOTE: ignore "." exports for now - (if (= "." export) - pkg-jsons - (let [export-pkg-json - (io/file - (trim-package-json path) - (trim-relative export) - "package.json")] - (cond-> pkg-jsons - (.exists export-pkg-json) - (assoc - (.getAbsolutePath export-pkg-json) - (json/read-str (slurp export-pkg-json))))))) - pkg-jsons exports))) - pkg-jsons pkg-jsons))] - (let [ - ;; a map of all the *top-level* package.json paths and their exports - ;; to the package.json contents as EDN - pkg-jsons (add-exports - (into {} - (comp - (map #(.getAbsolutePath %)) - (filter top-level-package-json?) - (map (fn [path] - [path (json/read-str (slurp path))]))) - module-fseq))] - (into [] - (comp - (map #(.getAbsolutePath %)) - (map (fn [path] - (merge - {:file path - :module-type :es6} - ;; if the file is *not* a package.json, then compute what - ;; namespaces it :provides to ClojureScript - (when-not (package-json? path) - (let [pkg-json-main (some - (fn [[pkg-json-path {:as pkg-json :strs [name]}]] - (let [entries (package-json-entries opts) - entry (first (keep (partial get pkg-json) entries))] - (when-not (nil? entry) - ;; should be the only edge case in - ;; the package.json main field - Antonio - (let [entry (trim-relative entry) - entry-path (-> pkg-json-path - (string/replace \\ \/) - trim-package-json - (str entry))] - ;; find a package.json entry point that matches - ;; the `path` - (some (fn [candidate] - (when (= candidate (string/replace path \\ \/)) - name)) - (cond-> [entry-path] - (not (or (string/ends-with? entry-path ".js") - (string/ends-with? entry-path ".json"))) - (into [(str entry-path ".js") (str entry-path "/index.js") (str entry-path ".json") - (string/replace entry-path #"\.cjs$" ".js")]))))))) - pkg-jsons)] - {:provides (let [module-rel-name (-> (subs path (.lastIndexOf path "node_modules")) - (string/replace \\ \/) - (string/replace #"node_modules[\\\/]" "")) - provides (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")] - (some? pkg-json-main) - (conj pkg-json-main)) - index-replaced (string/replace module-rel-name #"[\\\/]index\.js(on)?$" "")] - (cond-> provides - (and (boolean (re-find #"[\\\/]index\.js(on)?$" module-rel-name)) - (not (some #{index-replaced} provides))) - (conj index-replaced)))})))))) - module-fseq)))) - (def node-file-seq->libs-spec (memoize node-file-seq->libs-spec*)) (defn index-node-modules-dir diff --git a/src/main/clojure/cljs/foreign/node.clj b/src/main/clojure/cljs/foreign/node.clj new file mode 100644 index 000000000..0ae672295 --- /dev/null +++ b/src/main/clojure/cljs/foreign/node.clj @@ -0,0 +1,185 @@ +(ns cljs.foreign.node + (:require [cljs.vendor.clojure.data.json :as json] + [clojure.java.io :as io] + [clojure.string :as string])) + +(defn package-json-entries + "Takes options and returns a sequence with the desired order of package.json + entries for the given :package-json-resolution mode. If no mode is provided, + defaults to :webpack (if no target is set) and :nodejs (if the target is + :nodejs)." + [opts] + {:pre [(or (= (:package-json-resolution opts) :webpack) + (= (:package-json-resolution opts) :nodejs) + (and (sequential? (:package-json-resolution opts)) + (every? string? (:package-json-resolution opts))) + (not (contains? opts :package-json-resolution)))]} + (let [modes {:nodejs ["main"] + :webpack ["browser" "module" "main"]}] + (if-let [mode (:package-json-resolution opts)] + (if (sequential? mode) mode (get modes mode)) + (case (:target opts) + :nodejs (:nodejs modes) + (:webpack modes))))) + +(comment + (= (package-json-entries {}) ["browser" "module" "main"]) + (= (package-json-entries {:package-json-resolution :nodejs}) ["main"]) + (= (package-json-entries {:package-json-resolution :webpack}) ["browser" "module" "main"]) + (= (package-json-entries {:package-json-resolution ["foo" "bar" "baz"]}) ["foo" "bar" "baz"]) + (= (package-json-entries {:target :nodejs}) ["main"]) + (= (package-json-entries {:target :nodejs :package-json-resolution :nodejs}) ["main"]) + (= (package-json-entries {:target :nodejs :package-json-resolution :webpack}) ["browser" "module" "main"]) + (= (package-json-entries {:target :nodejs :package-json-resolution ["foo" "bar"]}) ["foo" "bar"])) + +(defn- package-json? [path] + (= "package.json" (.getName (io/file path)))) + +(defn- top-level-package-json? [path] + (boolean (re-find #"node_modules[/\\](@[^/\\]+?[/\\])?[^/\\]+?[/\\]package\.json$" path))) + +;; the path sans the package.json part +;; i.e. some_lib/package.json -> some_lib +(defn- trim-package-json [s] + (if (string/ends-with? s "package.json") + (subs s 0 (- (count s) 12)) + s)) + +(defn- trim-relative [path] + (cond-> path + (string/starts-with? path "./") + (subs 2))) + +(defn- ->export-pkg-json [path export] + (io/file + (trim-package-json path) + (trim-relative export) + "package.json")) + +(defn- export-subpaths + "Examine the export subpaths to compute subpackages" + [pkg-jsons export-subpath export path pkg-name] + ;; NOTE: ignore "." exports for now + (if (= "." export-subpath) + pkg-jsons + ;; technically the following logic is a bit brittle since `exports` is + ;; supposed to be used to hide the package structure. + ;; instead, here we assume the export subpath does match the library structure + ;; on disk, if we find a package.json we add it to pkg-jsons map + ;; and we synthesize "name" key based on subpath + (let [export-pkg-json (->export-pkg-json path export-subpath)] + ;; note this will ignore export wildcards etc. + (cond-> pkg-jsons + (.exists export-pkg-json) + (-> (assoc + (.getAbsolutePath export-pkg-json) + (merge + (json/read-str (slurp export-pkg-json)) + ;; add the name field so that path->main-name works later + (when (and (map? export) + (contains? export "require")) + {"name" (str pkg-name (string/replace export-subpath "./" "/"))})))))))) + +(defn- add-exports + "Given a list of pkg-jsons examine them for the `exports` field. `exports` + is now the preferred way to declare an entrypoint to a Node.js library. However, + for backwards compatibility it is often combined with `main`. + + `export` can also be a JS object - if so, it can define subpaths. `.` points + to main and other subpaths can be defined relative to that. + + See https://nodejs.org/api/packages.html#main-entry-point-export for more + detailed information." + [pkg-jsons opts] + (reduce-kv + (fn [pkg-jsons path {:strs [exports] :as pkg-json}] + (if (string? exports) + pkg-jsons + ;; map case + (reduce-kv + (fn [pkg-jsons export-subpath export] + (export-subpaths pkg-jsons + export-subpath export path (get pkg-json "name"))) + pkg-jsons exports))) + pkg-jsons pkg-jsons)) + +(defn path->main-name + "Determine whether a path is a main entrypoint in the provided package.json. + If so return the name entry provided in the package.json file." + [path [pkg-json-path {:as pkg-json :strs [name]}] opts] + (let [entries (package-json-entries opts) + entry (first (keep (partial get pkg-json) entries))] + (when-not (nil? entry) + ;; should be the only edge case in + ;; the package.json main field - Antonio + (let [entry (trim-relative entry) + entry-path (-> pkg-json-path (string/replace \\ \/) + trim-package-json (str entry))] + ;; find a package.json entry point that matches + ;; the `path` + (some (fn [candidate] + (when (= candidate (string/replace path \\ \/)) name)) + (cond-> [entry-path] + ;; if we have an entry point that doesn't end in .js or .json + ;; try to match some alternatives + (not (or (string/ends-with? entry-path ".js") + (string/ends-with? entry-path ".json"))) + (into [(str entry-path ".js") (str entry-path "/index.js") (str entry-path ".json") + (string/replace entry-path #"\.cjs$" ".js")]))))))) + +(defn- path->rel-name [path] + (-> (subs path (.lastIndexOf path "node_modules")) + (string/replace \\ \/) + (string/replace #"node_modules[\\\/]" ""))) + +(defn path->provides + "For a given path in node_modules, determine what namespaces that file would + provide to ClojureScript. Note it is assumed that we *already* processed all + package.json files and they are present via pkg-jsons parameter as we need them + to figure out the provides." + [path pkg-jsons opts] + (merge + {:file path + :module-type :es6} + ;; if the file is *not* a package.json, then compute what + ;; namespaces it :provides to ClojureScript + (when-not (package-json? path) + ;; given some path search the package.json to determine whether it is a + ;; main entry point or not + (let [pkg-json-main (some #(path->main-name path % opts) pkg-jsons)] + {:provides (let [module-rel-name (path->rel-name path) + provides (cond-> [module-rel-name (string/replace module-rel-name #"\.js(on)?$" "")] + (some? pkg-json-main) (conj pkg-json-main)) + index-replaced (string/replace module-rel-name #"[\\\/]index\.js(on)?$" "")] + (cond-> provides + (and (boolean (re-find #"[\\\/]index\.js(on)?$" module-rel-name)) + (not (some #{index-replaced} provides))) + (conj index-replaced)))})))) + +(defn get-pkg-jsons + "Given all a seq of files in node_modules return a map of all package.json + files indexed by path. Includes any `export` package.json files as well" + ([module-fseq] + (get-pkg-jsons module-fseq nil)) + ([module-fseq opts] + (add-exports + (into {} + (comp (map #(.getAbsolutePath %)) + (filter top-level-package-json?) + (map (fn [path] [path (json/read-str (slurp path))]))) + module-fseq) opts))) + +(defn node-file-seq->libs-spec* + "Given a sequence of non-nested node_module paths where the extension ends in + `.js/.json`, return lib-spec maps for each path containing at least :file, + :module-type, and :provides." + [module-fseq opts] + (let [;; a map of all the *top-level* package.json paths and their exports + ;; to the package.json contents as EDN + pkg-jsons (get-pkg-jsons module-fseq opts)] + (into [] + (comp + (map #(.getAbsolutePath %)) + ;; for each file, figure out what it will provide to ClojureScript + (map #(path->provides % pkg-jsons opts))) + module-fseq))) diff --git a/src/main/clojure/cljs/util.cljc b/src/main/clojure/cljs/util.cljc index 5ed980ef1..0964fec07 100644 --- a/src/main/clojure/cljs/util.cljc +++ b/src/main/clojure/cljs/util.cljc @@ -403,7 +403,8 @@ (filter (fn [^File f] (let [path (.getPath f)] (or (.endsWith path ".json") - (.endsWith path ".js")))) + (.endsWith path ".js") + (.endsWith path ".cjs")))) fseq))) (defn node-path-modules [opts] diff --git a/src/test/clojure/cljs/foreign/node_test.clj b/src/test/clojure/cljs/foreign/node_test.clj new file mode 100644 index 000000000..71c00ae40 --- /dev/null +++ b/src/test/clojure/cljs/foreign/node_test.clj @@ -0,0 +1,86 @@ +(ns cljs.foreign.node-test + (:require [cljs.foreign.node :as node] + [cljs.test-util :as test-util] + [cljs.util :as util] + [clojure.java.io :as io] + [clojure.java.shell :as sh] + [clojure.test :as test :refer [deftest is testing]])) + +(defn cleanup + ([] (cleanup #())) + ([f] + (test-util/delete-node-modules) + (doseq [f (map io/file + ["package.json" "package-lock.json" "yarn.lock" + "yarn-error.log"])] + (when (.exists f) + (io/delete-file f))) + (f))) + +(defn install + ([lib version] + (install :npm lib version)) + ([cmd lib version] + (let [action ({:npm "install" :yarn "add"} cmd)] + (sh/sh (name cmd) action (str lib "@" version))))) + +(test/use-fixtures :once cleanup) + +;; ============================================================================= +;; Tests + +(defn pkg-jsons [] + (-> (util/module-file-seq {}) + node/get-pkg-jsons)) + +(defn indexed-lib-specs [] + (as-> (-> (util/module-file-seq {}) + (node/node-file-seq->libs-spec* {})) + xs (zipmap (map :file xs) xs))) + +(defn relpath->data + ([index path] + (relpath->data index path :get)) + ([index path type] + (let [abs-path (.getAbsolutePath (io/file path))] + (case type + :get (get index abs-path) + :find (find index abs-path))))) + +(deftest test-basic + (install "left-pad" "1.3.0") + (testing "Install left-pad, verify that it is indexed and has a sensible lib-spec" + (let [index (indexed-lib-specs)] + (let [left-pad (relpath->data index "node_modules/left-pad/index.js")] + (is (some? (:file left-pad))) + (is (some? (:module-type left-pad))) + (is (= #{"left-pad/index.js" "left-pad/index" "left-pad"} + (into #{} (:provides left-pad)))))))) + +(deftest test-path->main-name + (install :yarn "react-select" "5.7.2") + (testing "Verify that path->main works as expected" + (is (= "react-select" + (node/path->main-name + (.getAbsolutePath (io/file "node_modules/react-select/dist/react-select.cjs.js")) + (relpath->data (pkg-jsons) + "node_modules/react-select/package.json" :find) + {:package-json-resolution :nodejs}))) + (is (= "react-select/creatable" + (node/path->main-name + (.getAbsolutePath (io/file "node_modules/react-select/creatable/dist/react-select-creatable.cjs.js")) + (relpath->data (pkg-jsons) + "node_modules/react-select/creatable/package.json" :find) + {:package-json-resolution :nodejs}))) + (is (nil? (node/path->main-name + (.getAbsolutePath (io/file "node_modules/react-select/dist/react-select.cjs.js")) + (relpath->data (pkg-jsons) + "node_modules/react-select/package.json" :find) + {:package-json-resolution :webpack}))))) + +(comment + + (test/run-tests) + (cleanup) + + ) From 825d40c6e0e7b98df044f45c70f80ee56a411dff Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 8 Jun 2023 16:16:04 -0500 Subject: [PATCH 3856/4033] Add new workflow for cljs release --- .github/workflows/release.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..9040194ba --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: Release on demand + +on: [workflow_dispatch] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v3 + - name: Set Github identity + run: | + git config --global user.name clojure-build + git config --global user.email "clojure-build@users.noreply.github.com" + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: 'temurin' + cache: 'maven' + server-id: sonatype-nexus-staging + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} + gpg-passphrase: GPG_PASSPHRASE + - name: Release + run: script/build + env: + HUDSON: true From c4c2e495197e7e600a01a797457cb29e3b6be11c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 8 Jun 2023 16:22:39 -0500 Subject: [PATCH 3857/4033] add google closure release workflow --- .github/workflows/closure.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/closure.yml diff --git a/.github/workflows/closure.yml b/.github/workflows/closure.yml new file mode 100644 index 000000000..847170805 --- /dev/null +++ b/.github/workflows/closure.yml @@ -0,0 +1,31 @@ +name: Release on demand + +on: [workflow_dispatch] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v3 + - name: Set Github identity + run: | + git config --global user.name clojure-build + git config --global user.email "clojure-build@users.noreply.github.com" + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: 'temurin' + cache: 'maven' + server-id: sonatype-nexus-staging + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} + gpg-passphrase: GPG_PASSPHRASE + - name: Release + run: | + cd script/closure-library-release + ./closure-library-release.sh + env: + HUDSON: true From fa884984c14cb2cd0b1f3231630123caec76766a Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 8 Jun 2023 16:23:37 -0500 Subject: [PATCH 3858/4033] fix name of closure action --- .github/workflows/closure.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/closure.yml b/.github/workflows/closure.yml index 847170805..47c4cc07c 100644 --- a/.github/workflows/closure.yml +++ b/.github/workflows/closure.yml @@ -1,4 +1,4 @@ -name: Release on demand +name: Stage Google closure library on: [workflow_dispatch] From 2541e1677536e1d41894fcdc0e3abd63e4ca7624 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 8 Jun 2023 16:29:10 -0500 Subject: [PATCH 3859/4033] skip maven cache in closure workflow as its not at the root --- .github/workflows/closure.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/closure.yml b/.github/workflows/closure.yml index 47c4cc07c..c568540f0 100644 --- a/.github/workflows/closure.yml +++ b/.github/workflows/closure.yml @@ -17,7 +17,6 @@ jobs: with: java-version: 8 distribution: 'temurin' - cache: 'maven' server-id: sonatype-nexus-staging server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD From ce720cec79916b1acaa06c301d8ed2dddd35b1dd Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 8 Jun 2023 16:34:29 -0500 Subject: [PATCH 3860/4033] In google closure library script, use https url instead of ssh --- script/closure-library-release/closure-library-release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index 0a9595a2d..d4dbc726b 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -34,7 +34,7 @@ set -e POM_TEMPLATE_FILE="google-closure-library.pom.template" THIRD_PARTY_POM_TEMPLATE_FILE="google-closure-library-third-party.pom.template" -GIT_CLONE_URL="git@github.com:google/closure-library.git" +GIT_CLONE_URL="https://github.com/google/closure-library.git" ### Functions From 520b0808e617164a30f70fd5e4e2a75c2ddcf5a5 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 8 Jun 2023 16:41:03 -0500 Subject: [PATCH 3861/4033] in google closure script, turn off download progress monitoring --- script/closure-library-release/closure-library-release.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh index d4dbc726b..e5cdb29f0 100755 --- a/script/closure-library-release/closure-library-release.sh +++ b/script/closure-library-release/closure-library-release.sh @@ -167,7 +167,7 @@ perl -p -e "s/RELEASE_VERSION/$release_version/go" \ if [ "$HUDSON" = "true" ]; then ( cd "$third_party_project_dir" - mvn --fail-at-end \ + mvn -ntp -B --fail-at-end \ -Psonatype-oss-release \ -Dsource.skip=true \ clean deploy @@ -175,7 +175,7 @@ if [ "$HUDSON" = "true" ]; then ( cd "$project_dir" - mvn --fail-at-end \ + mvn -ntp -B --fail-at-end \ -Psonatype-oss-release \ -Dsource.skip=true \ clean deploy @@ -188,13 +188,13 @@ else ( cd "$third_party_project_dir" mvn clean - mvn package + mvn -ntp -B package mvn install:install-file -Dfile=./target/google-closure-library-third-party-$release_version.jar -DpomFile=pom.xml ) ( cd "$project_dir" mvn clean - mvn package + mvn -ntp -B package mvn install:install-file -Dfile=./target/google-closure-library-$release_version.jar -DpomFile=pom.xml ) fi From 9d9816eceb67613b853a0b61396bb0c1f1ff5d76 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 8 Jun 2023 17:05:52 -0500 Subject: [PATCH 3862/4033] add env vars to closure script --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9040194ba..b42b6f158 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,3 +27,6 @@ jobs: run: script/build env: HUDSON: true + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} From 219951678b16575ec29bb9b88047356cf1437aec Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 8 Jun 2023 17:11:29 -0500 Subject: [PATCH 3863/4033] closure deps graph - turn off dependency download progress --- script/closure_deps_graph.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/closure_deps_graph.sh b/script/closure_deps_graph.sh index 223339b3a..cf62b423b 100755 --- a/script/closure_deps_graph.sh +++ b/script/closure_deps_graph.sh @@ -21,7 +21,7 @@ fi CP_FILE=`mktemp /tmp/cljs_cp.txt.XXXXXXXXXXX` -mvn -f ../../pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP $CLJS_SCRIPT_MVN_OPTS +mvn -ntp -B -f ../../pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP $CLJS_SCRIPT_MVN_OPTS CLJS_CP=`cat $CP_FILE` From 6aefc7354c3f7033d389634595d912f618c2abfc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 2 Jul 2023 19:58:33 -0400 Subject: [PATCH 3864/4033] CLJS-3393: Efficient drop, partition for persistent/algo colls (#196) Co-authored-by: Mike Fikes --- src/main/cljs/cljs/core.cljs | 197 ++++++++++++++++++----- src/main/clojure/cljs/core.cljc | 2 +- src/test/cljs/cljs/collections_test.cljs | 60 +++++++ src/test/cljs/cljs/core_test.cljs | 5 +- src/test/cljs/cljs/seqs_test.cljs | 160 ++++++++++++++++++ 5 files changed, 384 insertions(+), 40 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 20b627d91..195f9af8c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -886,6 +886,14 @@ (-iterator [coll] "Returns an iterator for coll.")) +(defprotocol IDrop + "Protocol for persistent or algorithmically defined collections to provide a + means of dropping N items that is more efficient than sequential walking." + (^clj-or-nil -drop [coll n] + "Returns a collection that is ISequential, ISeq, and IReduce, or nil if past + the end. The number of items to drop n must be > 0. It is also useful if the + returned coll implements IDrop for subsequent use in a partition-like scenario.")) + ;; Printing support (deftype StringBufferWriter [sb] @@ -1631,6 +1639,14 @@ reduces them without incurring seq initialization" (IndexedSeq. arr (inc i) nil) nil)) + IDrop + (-drop [coll n] + (if (pos? n) + (if (< (+ i n) (alength arr)) + (IndexedSeq. arr (+ i n) nil) + nil) + coll)) + ICounted (-count [_] (max 0 (- (alength arr) i))) @@ -1949,10 +1965,14 @@ reduces them without incurring seq initialization" (defn nthrest "Returns the nth rest of coll, coll when n is 0." [coll n] - (loop [n n xs coll] - (if-let [xs (and (pos? n) (seq xs))] - (recur (dec n) (rest xs)) - xs))) + (if (implements? IDrop coll) + (if (pos? n) + (or (-drop coll (Math/ceil n)) ()) + coll) + (loop [n n xs coll] + (if-let [xs (and (pos? n) (seq xs))] + (recur (dec n) (rest xs)) + xs)))) (defn get "Returns the value mapped to key, not-found or nil if key not present @@ -2997,10 +3017,14 @@ reduces them without incurring seq initialization" (defn nthnext "Returns the nth next of coll, (seq coll) when n is 0." [coll n] - (loop [n n xs (seq coll)] - (if (and xs (pos? n)) - (recur (dec n) (next xs)) - xs))) + (if (implements? IDrop coll) + (if (pos? n) + (-drop coll (Math/ceil n)) + (seq coll)) + (loop [n n xs (seq coll)] + (if (and xs (pos? n)) + (recur (dec n) (next xs)) + xs)))) ;;;;;;;;;;;;;;;;;;;;;;;;;; basics ;;;;;;;;;;;;;;;;;; @@ -4828,7 +4852,7 @@ reduces them without incurring seq initialization" (cons (first s) (take (dec n) (rest s)))))))) (defn drop - "Returns a lazy sequence of all but the first n items in coll. + "Returns a laziness-preserving sequence of all but the first n items in coll. Returns a stateful transducer when no collection is provided." ([n] {:pre [(number? n)]} @@ -4845,12 +4869,18 @@ reduces them without incurring seq initialization" (rf result input)))))))) ([n coll] {:pre [(number? n)]} - (let [step (fn [n coll] - (let [s (seq coll)] - (if (and (pos? n) s) - (recur (dec n) (rest s)) - s)))] - (lazy-seq (step n coll))))) + (if (implements? IDrop coll) + (or + (if (pos? n) + (-drop coll (Math/ceil n)) + (seq coll)) + ()) + (let [step (fn [n coll] + (let [s (seq coll)] + (if (and (pos? n) s) + (recur (dec n) (rest s)) + s)))] + (lazy-seq (step n coll)))))) (defn drop-last "Return a lazy sequence of all but the last n (default 1) items in coll" @@ -5019,6 +5049,14 @@ reduces them without incurring seq initialization" ICollection (-conj [coll o] (cons o coll)) + IDrop + (-drop [coll n] + (if (== count -1) + coll + (let [dropped-count (- count n)] + (when (pos? dropped-count) + (Repeat. nil dropped-count val nil nil))))) + IEmptyableCollection (-empty [coll] (.-EMPTY List)) @@ -5645,6 +5683,13 @@ reduces them without incurring seq initialization" (<= cnt 32) (IndexedSeq. tail 0 nil) :else (chunked-seq coll (first-array-for-longvec coll) 0 0))) + IDrop + (-drop [coll n] + (if (< n cnt) + (let [offset (js-mod n 32)] + (chunked-seq coll (unchecked-array-for coll n) (- n offset) offset)) + nil)) + ICounted (-count [coll] cnt) @@ -5849,6 +5894,17 @@ reduces them without incurring seq initialization" s)) (-chunked-next coll))) + IDrop + (-drop [coll n] + (let [o (+ off n)] + (if (< o (alength node)) + (chunked-seq vec node i o) + (let [i (+ i o)] + (if (< i (-count vec)) + (let [new-offset (js-mod i 32)] + (chunked-seq vec (unchecked-array-for vec i) (- i new-offset) new-offset)) + nil))))) + ICollection (-conj [coll o] (cons o coll)) @@ -6864,6 +6920,11 @@ reduces them without incurring seq initialization" (when (< i (- (alength arr) 2)) (PersistentArrayMapSeq. arr (+ i 2) nil))) + IDrop + (-drop [coll n] + (when (< n (-count coll)) + (PersistentArrayMapSeq. arr (+ i (* 2 n)) nil))) + IReduce (-reduce [coll f] (seq-reduce f coll)) (-reduce [coll f start] (seq-reduce f start coll))) @@ -6964,6 +7025,11 @@ reduces them without incurring seq initialization" (-seq [coll] (persistent-array-map-seq arr 0 nil)) + IDrop + (-drop [coll n] + (when-some [s (-seq coll)] + (-drop s n))) + ICounted (-count [coll] cnt) @@ -9737,6 +9803,47 @@ reduces them without incurring seq initialization" (when-let [s (seq coll)] (cons (take n s) (partition-all n step (drop step s))))))) +(defn splitv-at + "Returns a vector of [(into [] (take n) coll) (drop n coll)]" + [n coll] + [(into [] (take n) coll) (drop n coll)]) + +(defn partitionv + "Returns a lazy sequence of vectors of n items each, at offsets step + apart. If step is not supplied, defaults to n, i.e. the partitions + do not overlap. If a pad collection is supplied, use its elements as + necessary to complete last partition upto n items. In case there are + not enough padding elements, return a partition with less than n items." + ([n coll] + (partitionv n n coll)) + ([n step coll] + (lazy-seq + (when-let [s (seq coll)] + (let [p (into [] (take n) s)] + (when (= n (count p)) + (cons p (partitionv n step (nthrest s step)))))))) + ([n step pad coll] + (lazy-seq + (when-let [s (seq coll)] + (let [p (into [] (take n) s)] + (if (= n (count p)) + (cons p (partitionv n step pad (nthrest s step))) + (list (into [] (take n) (concat p pad))))))))) + +(defn partitionv-all + "Returns a lazy sequence of vector partitions, but may include + partitions with fewer than n items at the end. + Returns a stateful transducer when no collection is provided." + ([n] + (partition-all n)) + ([n coll] + (partitionv-all n n coll)) + ([n step coll] + (lazy-seq + (when-let [s (seq coll)] + (let [seg (into [] (take n) coll)] + (cons seg (partitionv-all n step (drop step s)))))))) + (defn take-while "Returns a lazy sequence of successive items from coll while (pred item) returns logical true. pred must be free of side-effects. @@ -9824,7 +9931,12 @@ reduces them without incurring seq initialization" (set! i (+ i step)) ret))) -(deftype IntegerRange [meta start end step ^:mutable chunk ^:mutable chunk-next ^:mutable __hash] +(defn- range-count + "Returns exact size of remaining items in an IntegerRange." + [start end step] + (Math/ceil (/ (- end start) step))) + +(deftype IntegerRange [meta start end step cnt ^:mutable __hash] Object (toString [coll] (pr-str* coll)) @@ -9838,23 +9950,15 @@ reduces them without incurring seq initialization" (-lastIndexOf coll x (count coll))) (lastIndexOf [coll x start] (-lastIndexOf coll x start)) - (forceChunk [coll] - (when (nil? chunk) - (let [count (-count coll)] - (if (> count 32) - (do - (set! chunk-next (IntegerRange. nil (+ start (* step 32)) end step nil nil nil)) - (set! chunk (IntegerRangeChunk. start step 32))) - (set! chunk (IntegerRangeChunk. start step count)))))) ICloneable - (-clone [_] (IntegerRange. meta start end step chunk chunk-next __hash)) + (-clone [_] (IntegerRange. meta start end step cnt __hash)) IWithMeta (-with-meta [rng new-meta] (if (identical? new-meta meta) rng - (IntegerRange. new-meta start end step chunk chunk-next __hash))) + (IntegerRange. new-meta start end step cnt __hash))) IMeta (-meta [rng] meta) @@ -9878,19 +9982,40 @@ reduces them without incurring seq initialization" (-next [rng] (if (pos? step) (when (< (+ start step) end) - (IntegerRange. nil (+ start step) end step nil nil nil)) + (IntegerRange. nil (+ start step) end step (range-count (+ start step) end step) nil)) (when (> (+ start step) end) - (IntegerRange. nil (+ start step) end step nil nil nil)))) + (IntegerRange. nil (+ start step) end step (range-count (+ start step) end step) nil)))) + + IDrop + (-drop [rng n] + (if (pos? n) + (if (< n cnt) + (IntegerRange. nil (+ start (* step n)) end step (- cnt n) nil) + nil) + rng)) IChunkedSeq (-chunked-first [rng] - (.forceChunk rng) - chunk) + (IntegerRangeChunk. start step (min cnt 32))) (-chunked-rest [rng] - (.forceChunk rng) - (if (nil? chunk-next) + (if (<= cnt 32) () - chunk-next)) + (let [start (+ start (* step 32))] + (cond + (pos? step) + (if (<= end start) + () + (IntegerRange. nil start end step (range-count start end step) nil)) + + (neg? step) + (if (>= end start) + () + (IntegerRange. nil start end step (range-count start end step) nil)) + + :else + (if (== end start) + () + (repeat start)))))) IChunkedNext (-chunked-next [rng] @@ -9911,7 +10036,7 @@ reduces them without incurring seq initialization" ICounted (-count [rng] - (Math/ceil (/ (- end start) step))) + cnt) IIndexed (-nth [rng n] @@ -10060,14 +10185,14 @@ reduces them without incurring seq initialization" (if (<= end start) () (if (and (integer? start) (integer? end) (integer? step)) - (IntegerRange. nil start end step nil nil nil) + (IntegerRange. nil start end step (range-count start end step) nil) (Range. nil start end step nil nil nil))) (neg? step) (if (>= end start) () (if (and (integer? start) (integer? end) (integer? step)) - (IntegerRange. nil start end step nil nil nil) + (IntegerRange. nil start end step (range-count start end step) nil) (Range. nil start end step nil nil nil))) :else diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index c4858d629..fa5aaf685 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -820,7 +820,7 @@ IPrintWithWriter IPending IWatchable IEditableCollection ITransientCollection ITransientAssociative ITransientMap ITransientVector ITransientSet IMultiFn IChunkedSeq IChunkedNext IComparable INamed ICloneable IAtom - IReset ISwap IIterable]) + IReset ISwap IIterable IDrop]) (iterate (core/fn [[p b]] (if (core/== 2147483648 b) [(core/inc p) 1] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 9f44633d1..d66a87cda 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -47,6 +47,14 @@ (is (= (find [1 2 3] 10) nil))) ) +(deftest test-map + (testing "IDrop" + (let [am (apply array-map (interleave (range 7) (range 7)))] + (is (satisfies? IDrop am)) + (is (= [[3 3] [4 4] [5 5] [6 6]] (drop 3 am))) + (is (satisfies? IDrop (drop 3 am))) + (is (= [[5 5] [6 6]] (drop 2 (drop 3 am))))))) + (deftest test-vectors (testing "Testing vectors" (is (= :a (nth [:a :b :c :d] 0))) @@ -66,6 +74,16 @@ (testing "stack operations" (is (= 95 (peek stack1))) (is (= 94 (peek stack2))))) + (testing "IDrop" + (is (satisfies? IDrop (vec (range 39)))) + (is (= (range 3 39) (drop 3 (vec (range 39))))) + (is (= (range 31 39) (drop 31 (vec (range 39))))) + (is (= (range 32 39) (drop 32 (vec (range 39))))) + (is (= (range 33 39) (drop 33 (vec (range 39))))) + (is (satisfies? IDrop (drop 3 (vec (range 39))))) + (is (= (range 31 39) (drop 28 (drop 3 (vec (range 39)))))) + (is (= (range 32 39) (drop 29 (drop 3 (vec (range 39)))))) + (is (= (range 33 39) (drop 30 (drop 3 (vec (range 39))))))) (let [v1 (vec (range 10)) v2 (vec (range 5)) s (subvec v1 2 8)] @@ -182,6 +200,11 @@ (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" + (is (satisfies? IDrop (range 10))) + (is (= [5 6 7 8 9] (drop 5 (range 10)))) + (is (satisfies? IDrop (drop 5 (range 10)))) + (is (= [8 9] (drop 3 (drop 5 (range 10)))))) (is (= (reduce + (range 0 100)) 4950)) (is (= (reduce + 0 (range 0 100)) 4950)) (is (= (reduce + (range 0.1 100)) 4960)) @@ -347,7 +370,20 @@ (is (not (realized? (repeat 5 7)))) + (is (= [1 1] (into [] (drop 98 (repeat 100 1))))) (is (= [1 1] (into [] (drop 98) (repeat 100 1)))) + (is (= [1 1] (into [] (take 2 (drop 98 (repeat 1)))))) + + (is (= [1] (drop 0 (repeat 1 1)))) + (is (= '(:a) (drop 1 (repeat 2 :a)))) + (is (= () (drop 2 (repeat 2 :a)))) + (is (= () (drop 3 (repeat 2 :a)))) + + (testing "IDrop" + (is (satisfies? IDrop (repeat 10 0))) + (is (= [0 0 0 0 0] (drop 5 (repeat 10 0)))) + (is (satisfies? IDrop (drop 5 (repeat 10 0)))) + (is (= [0 0] (drop 3 (drop 5 (repeat 10 0)))))) (is (= () (empty (repeat 100 1)))) (is (= () (empty (repeat 7)))) @@ -376,6 +412,16 @@ 2 [:x 1 1] 3 [:x 1 1]))) +(deftest test-string + (testing "IDrop" + (is (satisfies? IDrop (seq "aaaaaaaaaa"))) + (is (= [\a \a \a \a \a] (drop 5 "aaaaaaaaaa"))) + (is (= [\a \a \a \a \a] (drop 5 (seq "aaaaaaaaaa")))) + (is (not (satisfies? IDrop (drop 5 "aaaaaaaaaa")))) + (is (satisfies? IDrop (drop 5 (seq "aaaaaaaaaa")))) + (is (= [\a \a] (drop 3 (drop 5 "aaaaaaaaaa")))) + (is (= [\a \a] (drop 3 (drop 5 (seq "aaaaaaaaaa"))))))) + (deftest test-iterate (testing "Testing Iterate" (are [x y] (= x y) @@ -437,6 +483,17 @@ (split-at -1 [1 2 3]) [() (list 1 2 3)] (split-at -5 [1 2 3]) [() (list 1 2 3)] )) +(deftest test-splitv-at + (is (vector? (splitv-at 2 []))) + (is (vector? (first (splitv-at 2 [])))) + (is (vector? (splitv-at 2 [1 2 3]))) + (is (vector? (first (splitv-at 2 [1 2 3]))))) + +(defspec splitv-at-equals-split-at 100 + (prop/for-all [n gen/nat + coll (gen/vector gen/nat)] + (= (splitv-at n coll) (split-at n coll)))) + (deftest test-rseq (testing "Testing RSeq" (is (= '(3 2 1) (reverse (seq (array 1 2 3))))) @@ -1091,6 +1148,9 @@ (is (thrown-with-msg? js/Error #"No value supplied for key: :a" (apply hash-map [:a]))))) +(deftest test-cljs-3393 + (is (= '(0 2 4) (take 3 (filter even? (range 100000000)))))) + (comment (run-tests) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 2759fae1d..69a8ba0ce 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1674,14 +1674,13 @@ (is (chunked-seq? (range 5))) (is (satisfies? IChunk (chunk-first (range 5)))) (is (nil? (chunk-next (range 32)))) - (is (satisfies? IChunk (chunk-first (chunk-next (range 33))))) - (is (satisfies? IChunk (chunk-first (chunk-rest (range 33))))) (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 (= ::not-found (nth (chunk-first (range 100)) 35 ::not-found))) + (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)))) diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 246c79550..3d7606da9 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -352,6 +352,166 @@ (reset! rng r2) (long (random/rand-long r1)))) +(deftest test-take + (are [x y] (= x y) + (take 1 [1 2 3 4 5]) '(1) + (take 3 [1 2 3 4 5]) '(1 2 3) + (take 5 [1 2 3 4 5]) '(1 2 3 4 5) + (take 9 [1 2 3 4 5]) '(1 2 3 4 5) + + (take 0 [1 2 3 4 5]) () + (take -1 [1 2 3 4 5]) () + (take -2 [1 2 3 4 5]) () + + (take 0.25 [1 2 3 4 5]) '(1))) + + +(deftest test-drop + (are [x y] (= x y) + (drop 1 [1 2 3 4 5]) '(2 3 4 5) + (drop 3 [1 2 3 4 5]) '(4 5) + (drop 5 [1 2 3 4 5]) () + (drop 9 [1 2 3 4 5]) () + + (drop 0 [1 2 3 4 5]) '(1 2 3 4 5) + (drop -1 [1 2 3 4 5]) '(1 2 3 4 5) + (drop -2 [1 2 3 4 5]) '(1 2 3 4 5) + + (drop 0.25 [1 2 3 4 5]) '(2 3 4 5) ) + + (are [coll] (= (drop 4 coll) (drop -2 (drop 4 coll))) + [0 1 2 3 4 5] + (seq [0 1 2 3 4 5]) + (range 6) + (repeat 6 :x))) + +(deftest test-nthrest + (are [x y] (= x y) + (nthrest [1 2 3 4 5] 1) '(2 3 4 5) + (nthrest [1 2 3 4 5] 3) '(4 5) + (nthrest [1 2 3 4 5] 5) () + (nthrest [1 2 3 4 5] 9) () + + (nthrest [1 2 3 4 5] 0) '(1 2 3 4 5) + (nthrest [1 2 3 4 5] -1) '(1 2 3 4 5) + (nthrest [1 2 3 4 5] -2) '(1 2 3 4 5) + + (nthrest [1 2 3 4 5] 0.25) '(2 3 4 5) + (nthrest [1 2 3 4 5] 1.2) '(3 4 5)) + + ;; (nthrest coll 0) should return coll + (are [coll] (let [r (nthrest coll 0)] (and (= coll r) (= (type coll) (type r)))) + [1 2 3] + (seq [1 2 3]) + (range 10) + (repeat 10 :x) + (seq "abc"))) + +(deftest test-nthnext + (are [x y] (= x y) + (nthnext [1 2 3 4 5] 1) '(2 3 4 5) + (nthnext [1 2 3 4 5] 3) '(4 5) + (nthnext [1 2 3 4 5] 5) nil + (nthnext [1 2 3 4 5] 9) nil + + (nthnext [1 2 3 4 5] 0) '(1 2 3 4 5) + (nthnext [1 2 3 4 5] -1) '(1 2 3 4 5) + (nthnext [1 2 3 4 5] -2) '(1 2 3 4 5) + + (nthnext [1 2 3 4 5] 0.25) '(2 3 4 5) + (nthnext [1 2 3 4 5] 1.2) '(3 4 5) )) + +(deftest test-partitionv-all + (is (= (partitionv-all 4 [1 2 3 4 5 6 7 8 9]) + [[1 2 3 4] [5 6 7 8] [9]])) + (is (= (partitionv-all 4 2 [1 2 3 4 5 6 7 8 9]) + [[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]]))) + +(deftest test-partition + (are [x y] (= x y) + (partition 2 [1 2 3]) '((1 2)) + (partition 2 [1 2 3 4]) '((1 2) (3 4)) + (partition 2 []) () + + (partition 2 3 [1 2 3 4 5 6 7]) '((1 2) (4 5)) + (partition 2 3 [1 2 3 4 5 6 7 8]) '((1 2) (4 5) (7 8)) + (partition 2 3 []) () + + (partition 1 []) () + (partition 1 [1 2 3]) '((1) (2) (3)) + + (partition 5 [1 2 3]) () + + (partition 4 4 [0 0 0] (range 10)) '((0 1 2 3) (4 5 6 7) (8 9 0 0)) + + (partition -1 [1 2 3]) () + (partition -2 [1 2 3]) ()) + + ;; reduce + (is (= [1 2 4 8 16] (map #(reduce * (repeat % 2)) (range 5)))) + (is (= [3 6 12 24 48] (map #(reduce * 3 (repeat % 2)) (range 5)))) + + ;; equality and hashing + (is (= (repeat 5 :x) (repeat 5 :x))) + (is (= (repeat 5 :x) '(:x :x :x :x :x))) + (is (= (hash (repeat 5 :x)) (hash '(:x :x :x :x :x)))) + (is (= (assoc (array-map (repeat 1 :x) :y) '(:x) :z) {'(:x) :z})) + (is (= (assoc (hash-map (repeat 1 :x) :y) '(:x) :z) {'(:x) :z}))) + +(deftest test-partitionv + (are [x y] (= x y) + (partitionv 2 [1 2 3]) '((1 2)) + (partitionv 2 [1 2 3 4]) '((1 2) (3 4)) + (partitionv 2 []) () + + (partitionv 2 3 [1 2 3 4 5 6 7]) '((1 2) (4 5)) + (partitionv 2 3 [1 2 3 4 5 6 7 8]) '((1 2) (4 5) (7 8)) + (partitionv 2 3 []) () + + (partitionv 1 []) () + (partitionv 1 [1 2 3]) '((1) (2) (3)) + + (partitionv 5 [1 2 3]) () + + (partitionv -1 [1 2 3]) () + (partitionv -2 [1 2 3]) ())) + +(deftest test-reduce-on-coll-seqs + ;; reduce on seq of coll, both with and without an init + (are [coll expected expected-init] + (and + (= expected-init (reduce conj [:init] (seq coll))) + (= expected (reduce conj (seq coll)))) + ;; (seq [ ... ]) + [] [] [:init] + [1] 1 [:init 1] + [[1] 2] [1 2] [:init [1] 2] + + ;; (seq { ... }) + {} [] [:init] + {1 1} [1 1] [:init [1 1]] + {1 1 2 2} [1 1 [2 2]] [:init [1 1] [2 2]] + + ;; (seq (hash-map ... )) + (hash-map) [] [:init] + (hash-map 1 1) [1 1] [:init [1 1]] + (hash-map 1 1 2 2) [1 1 [2 2]] [:init [1 1] [2 2]] + + ;; (seq (sorted-map ... )) + (sorted-map) [] [:init] + (sorted-map 1 1) [1 1] [:init [1 1]] + (sorted-map 1 1 2 2) [1 1 [2 2]] [:init [1 1] [2 2]]) + + (are [coll expected expected-init] + (and + (= expected-init (reduce + 100 (seq coll))) + (= expected (reduce + (seq coll)))) + + ;; (seq (range ...)) + (range 0) 0 100 + (range 1 2) 1 101 + (range 1 3) 3 103)) + (defspec iteration-seq-equals-reduce 1000 (prop/for-all [initk gen/small-integer seed gen/small-integer] From 8a4f8d1025151ae1281185ed9d101c389661554d Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Sat, 1 Jul 2023 15:50:32 -0400 Subject: [PATCH 3865/4033] CLJS-3363: reduce-kv on seq of map entries --- src/main/cljs/cljs/core.cljs | 6 ++++-- src/test/cljs/cljs/core_test.cljs | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 195f9af8c..620bc8abc 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2624,9 +2624,11 @@ reduces them without incurring seq initialization" and f is not called. Note that reduce-kv is supported on vectors, where the keys will be the ordinals." ([f init coll] - (if-not (nil? coll) + (if (satisfies? IKVReduce coll) (-kv-reduce coll f init) - init))) + (reduce (fn [ret me] + (f ret (-key me) (-val me))) + init coll)))) (defn identity "Returns its argument." diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 69a8ba0ce..812d3a74a 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1975,6 +1975,12 @@ {1 1 3 3} (update-keys (array-map 0 1 2 3) inc) {1 1 3 3} (update-keys (sorted-map 2 3 0 1) inc)))) +(deftest test-cljs-3363 + (is (= {} + (reduce-kv #(assoc %1 %3 %2) {} nil))) + (is (= {1 :a 2 :b} + (reduce-kv #(assoc %1 %3 %2) {} (seq {:a 1 :b 2}))))) + (defn cljs-3386-test-fn ([x] x) ([_ _ & zs] zs)) From 8835a55cdc1a7e805f769be1b5f0eef6c0756f7f Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 14 Aug 2023 11:09:59 -0500 Subject: [PATCH 3866/4033] run aot_core maven build in batch mode --- script/aot_core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/aot_core b/script/aot_core index dfa9c36ee..350c5a29c 100755 --- a/script/aot_core +++ b/script/aot_core @@ -21,7 +21,7 @@ fi CP_FILE=`mktemp /tmp/cljs_cp.txt.XXXXXXXXXXX` -mvn -f pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP $CLJS_SCRIPT_MVN_OPTS +mvn -B -f pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP $CLJS_SCRIPT_MVN_OPTS CLJS_CP=`cat $CP_FILE` From 2c493eb1b342775ccc9b13b437d3764bcc7a6c32 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 14 Aug 2023 11:18:23 -0500 Subject: [PATCH 3867/4033] set maven evn vars during release step --- .github/workflows/closure.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/closure.yml b/.github/workflows/closure.yml index c568540f0..0caa8eb0f 100644 --- a/.github/workflows/closure.yml +++ b/.github/workflows/closure.yml @@ -27,4 +27,7 @@ jobs: cd script/closure-library-release ./closure-library-release.sh env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} HUDSON: true From 3cb6c47bfe7fbb3f24f0feef9756d708fc4ac7a9 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 14 Aug 2023 11:29:39 -0500 Subject: [PATCH 3868/4033] check in pom to make setup-java action happy --- .gitignore | 1 - pom.xml | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 pom.xml diff --git a/.gitignore b/.gitignore index cfe85231a..ad9f11ed9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ closure /out *out .lein* -/pom.xml *.iml .repl* *.swp diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..60c528f92 --- /dev/null +++ b/pom.xml @@ -0,0 +1,391 @@ + + 4.0.0 + org.clojure + clojurescript + + 1.10.843 + jar + ClojureScript + + https://github.com/clojure/clojurescript + + + ClojureScript compiler and core runtime library. + + + + + Eclipse Public License 1.0 + http://opensource.org/licenses/eclipse-1.0.php + repo + + + + + + org.clojure + clojure + 1.8.0 + + + com.google.javascript + closure-compiler-unshaded + v20210202 + + + org.clojure + google-closure-library + 0.0-20201211-3e6c510d + + + org.clojure + data.json + 0.2.6 + + + org.clojure + tools.reader + 1.3.3 + + + com.cognitect + transit-clj + 0.8.309 + + + org.clojure + clojure + + + + + org.clojure + test.check + 0.10.0-alpha3 + test + + + org.clojure + clojure + + + + + + + Aaron Bedra + Alan Dipert + Alex Dowad + Alan Malloy + Alen Ribic + Alex Redington + Ambrose Bonnaire-Sergeant + Andrew Rosa + Antonin Hildebrand + Ben Moss + Benjamin Meyer + Bo Jeanes + Bobby Calderwood + Brandon Bloom + Brenton Ashworth + Brian Jenkins + Brian Kim + Brian Taylor + Bruce Hauman + Chad Taylor + Chas Emerick + Charles Duffy + Chris Granger + Chris Pickard + Chris Houser + Chris Truter + Christopher Redinger + Colin Jones + Creighton Kirkendall + David Nolen + Daniel Compton + Daniel Skarda + Dave Sann + Devin Walters + Dylan Butman + Edward Tsech + Eric Normand + Eric Thorsen + Erik Ouchterlony + Evan Mezeske + Francis Avila + Frank Failla + Francoise De Serre + Gary Fredericks + Gary Trakhman + Herwig Hochleitner + Hubert Iwaniuk + Hugo Duncan + Immo Heikkinen + Ivan Willig + J. Pablo Fernandez + Jamie Brandon + Jeff Dik + Jess Martin + Joel Holdbrooks + Joel Martin + John Li + Jonas De Vuyst + Jonas Enlund + Jonathan Boston + Jozef Wagner + Juergen Hoetzel + Juho Teperi + Julian Eluard + Justin Tirrell + Kovas Boguta + Kevin J. Lynagh + Laszlo Toeroek + Leon Grapenthin + Luke VanderHart + Maria Geller + Martin Klepsch + Matjaz Gregoric + Max Gonzih + Max Penet + Max Veytsman + Michael Ballantyne + Michael Fogus + Michael Glaesemann + Michael Griffiths + Michael O. Church + Michał Marczyk + Michiel Borkent + Mike Fikes + Moritz Ulrich + Murphy McMahon + Nelson Morris + Nicola Mometto + Nikita Prokopov + Osbert Feng + Paul Michael Bauer + Paul deGrandis + Peter Schuck + Peter Stephens + Peter Taoussanis + Pieter van Prooijen + Raphaël Amiard + Raymond Huang + Rich Hickey + Roman Gonzalez + Roman Scherer + Rupa Shankar + Russ Olsen + Sam Umbach + Samuel Miller + Sean Grove + Sebastien Bensusan + Sean LeBron + Steven Kallstrom + Stuart Halloway + Stuart Mitchell + Stuart Sierra + Takahiro Hozumi + Thomas Heller + Thomas Scheiblauer + Tim Griesser + Timothy Pratley + Toby Crawley + Tom Hickey + Tom Jack + Tom Marble + Travis Thieman + Travis Vachon + Wilkes Joiner + Zachary Allaun + Zach Oakes + Zubair Quraishi + + + + scm:git:git://github.com/clojure/clojurescript.git + scm:git:git@github.com:clojure/clojurescript.git + https://github.com/clojure/clojurescript + + + + org.clojure + pom.contrib + 1.1.0 + + + + UTF-8 + src/main/clojure + src/main/cljs + src/main/cljs + resources + true + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.5 + + + add-clojure-source-dirs + generate-sources + + add-source + add-resource + + + + ${clojure.source.dir} + ${cljs.source.dir} + + + + ${clojure.source.dir} + + + ${cljs.source.dir} + + + ${resources.dir} + + + + + + + + com.theoryinpractise + clojure-maven-plugin + 1.7.1 + + false + + + + clojure-compile + compile + + compile + + + true + + cljs.util + cljs.env + cljs.js-deps + cljs.core + cljs.source-map.base64 + cljs.source-map.base64-vlq + cljs.source-map + cljs.analyzer + cljs.analyzer.utils + cljs.compiler + cljs.closure + cljs.tagged-literals + cljs.test + cljs.analyzer.api + cljs.build.api + cljs.compiler.api + cljs.spec.alpha + cljs.spec.test.alpha + cljs.spec.gen.alpha + cljs.repl + cljs.repl.browser + cljs.repl.node + cljs.repl.reflect + cljs.repl.server + cljs.main + cljs.cli + + + + + + + maven-jar-plugin + 2.4 + + + + cljs.main + + + + + + default-jar + package + + jar + + + + **/*.clj + **/*.cljc + **/*.cljs + **/*.js + **/*.map + **/*.edn + **/*.svg + **/*.png + + + + + + + maven-assembly-plugin + 2.4 + + + aot-jar + package + + single + + + false + + src/assembly/aot.xml + + + + + slim-jar + package + + single + + + + src/assembly/slim.xml + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + r@{project.version} + + + + + From ce08c1d6a41c17292249d05884c2f7928af15c08 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 14 Aug 2023 11:38:41 -0500 Subject: [PATCH 3869/4033] fetch deep --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b42b6f158..17edc1d0d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,8 @@ jobs: steps: - name: Check out uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Set Github identity run: | git config --global user.name clojure-build From 6bf9e6c95afe08b64f4649f977419ea96c77bca2 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 14 Aug 2023 11:44:08 -0500 Subject: [PATCH 3870/4033] update pom.template.xml to the modern world --- pom.template.xml | 65 ++++++------------------------------------------ 1 file changed, 8 insertions(+), 57 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 4ddb49875..3da22ca4e 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -203,18 +203,10 @@ https://github.com/clojure/clojurescript - - - - org.sonatype.oss - oss-parent - 7 + 1.1.0 @@ -349,62 +341,21 @@ org.apache.maven.plugins - maven-gpg-plugin - 1.4 + maven-compiler-plugin + 3.1 - Clojure/core + 1.8 + 1.8 org.apache.maven.plugins - maven-compiler-plugin - 3.1 + maven-release-plugin + 2.5.3 - 1.7 - 1.7 + r@{project.version} - - - - sonatype-oss-release - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.5 - - - default-deploy - deploy - - - deploy - - - - - - https://oss.sonatype.org/ - - sonatype-nexus-staging - - - - - - From d78f0524baea2e7c2a21714f45fe4478f00fba5a Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 14 Aug 2023 11:44:49 -0500 Subject: [PATCH 3871/4033] dont push to maven at the end of release build for now --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 17edc1d0d..5515f8879 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: - name: Release run: script/build env: - HUDSON: true + HUDSON: false MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} From a071d9ff419a83b4ce44d6dc77d9817752f177b4 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 14 Aug 2023 11:45:45 -0500 Subject: [PATCH 3872/4033] use batch mode during cljs build --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index 20bb36d06..f69b10c65 100755 --- a/script/build +++ b/script/build @@ -64,7 +64,7 @@ mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server if [ "$HUDSON" = "true" ]; then - mvn --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ + mvn -B --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ clean deploy nexus-staging:release echo "Creating tag $TAG" From 0a5f5edcc1c00eecceb6d32c478e66083a30503b Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 12:22:26 -0500 Subject: [PATCH 3873/4033] Revert "update pom.template.xml to the modern world" This reverts commit 6bf9e6c95afe08b64f4649f977419ea96c77bca2. --- pom.template.xml | 65 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index 3da22ca4e..4ddb49875 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -203,10 +203,18 @@ https://github.com/clojure/clojurescript + + + + org.sonatype.oss + oss-parent + 7 @@ -341,21 +349,62 @@ org.apache.maven.plugins - maven-compiler-plugin - 3.1 + maven-gpg-plugin + 1.4 - 1.8 - 1.8 + Clojure/core org.apache.maven.plugins - maven-release-plugin - 2.5.3 + maven-compiler-plugin + 3.1 - r@{project.version} + 1.7 + 1.7 + + + + sonatype-oss-release + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.5 + + + default-deploy + deploy + + + deploy + + + + + + https://oss.sonatype.org/ + + sonatype-nexus-staging + + + + + + From 55d55691040d8dcaace08e635ac5b7420d49fe51 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 13:20:23 -0500 Subject: [PATCH 3874/4033] test the release signing --- script/build | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script/build b/script/build index f69b10c65..47e041c71 100755 --- a/script/build +++ b/script/build @@ -64,7 +64,7 @@ mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server if [ "$HUDSON" = "true" ]; then - mvn -B --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ + mvn -B -ntp --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ clean deploy nexus-staging:release echo "Creating tag $TAG" @@ -72,7 +72,8 @@ if [ "$HUDSON" = "true" ]; then git push origin "$TAG" else echo "Skipping remote deployment and Git tag because we are not on Hudson." - mvn $CLJS_SCRIPT_MVN_OPTS clean install + mvn -B -ntp -DskipNexusStagingDeployMojo=true --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ + clean install fi rm -f src/main/cljs/cljs/core.aot.js From ab802262d2165185feab235f137cac9f68819d0c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 13:41:18 -0500 Subject: [PATCH 3875/4033] dont run in batch mode --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index 47e041c71..f58c7ecf8 100755 --- a/script/build +++ b/script/build @@ -72,7 +72,7 @@ if [ "$HUDSON" = "true" ]; then git push origin "$TAG" else echo "Skipping remote deployment and Git tag because we are not on Hudson." - mvn -B -ntp -DskipNexusStagingDeployMojo=true --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ + mvn -ntp -DskipNexusStagingDeployMojo=true --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ clean install fi From c01e3a7f16c73b37f81f58784e7a34d1eee383d8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 13:45:06 -0500 Subject: [PATCH 3876/4033] gpg signing settings --- pom.template.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 4ddb49875..4f02cc72d 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -352,7 +352,11 @@ maven-gpg-plugin 1.4 - Clojure/core + + + --pinentry-mode + loopback + From c2dfca1d504e420c4635508ade11f0e97787a8e8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 13:45:58 -0500 Subject: [PATCH 3877/4033] mvn debugging --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index f58c7ecf8..98259144a 100755 --- a/script/build +++ b/script/build @@ -72,7 +72,7 @@ if [ "$HUDSON" = "true" ]; then git push origin "$TAG" else echo "Skipping remote deployment and Git tag because we are not on Hudson." - mvn -ntp -DskipNexusStagingDeployMojo=true --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ + mvn -X -e -ntp -DskipNexusStagingDeployMojo=true --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ clean install fi From 87e34ac71b5354a4953bdf667216a44f61edc73e Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 13:49:32 -0500 Subject: [PATCH 3878/4033] bump gpg-sign plugin version --- pom.template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 4f02cc72d..95033e1ed 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -350,7 +350,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.4 + 3.1.0 From 44c35cd1ed59f9c9f1ae5ccb5afe3e44e49ccdad Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 13:56:55 -0500 Subject: [PATCH 3879/4033] remove key name, not needed now --- pom.template.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 95033e1ed..d41bfba3e 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -352,7 +352,6 @@ maven-gpg-plugin 3.1.0 - --pinentry-mode loopback From c9eb8ec6fed29acfa31272af2effe9b159d461a8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 13:57:20 -0500 Subject: [PATCH 3880/4033] revert no hudson build back to what it was --- script/build | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/build b/script/build index 98259144a..9122296e8 100755 --- a/script/build +++ b/script/build @@ -72,8 +72,7 @@ if [ "$HUDSON" = "true" ]; then git push origin "$TAG" else echo "Skipping remote deployment and Git tag because we are not on Hudson." - mvn -X -e -ntp -DskipNexusStagingDeployMojo=true --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ - clean install + mvn -B -ntp $CLJS_SCRIPT_MVN_OPTS clean install fi rm -f src/main/cljs/cljs/core.aot.js From 66a65efedf59a98215c861aefdb9f60f3c35dca0 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 14:07:13 -0500 Subject: [PATCH 3881/4033] Add workflow input to do deploy in release --- .github/workflows/release.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5515f8879..794901126 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,13 @@ name: Release on demand -on: [workflow_dispatch] +on: + workflow_call: + inputs: + deploy: + description: "Deploy to Maven Central" + required: true + default: false + type: boolean jobs: build: @@ -28,7 +35,7 @@ jobs: - name: Release run: script/build env: - HUDSON: false + HUDSON: ${{ github.event.inputs.deploy }} MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} From 15539d253059e52dba600616b2a812206b29a171 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 15 Aug 2023 14:08:51 -0500 Subject: [PATCH 3882/4033] fix workflow trigger event type --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 794901126..585e84929 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,7 @@ name: Release on demand on: - workflow_call: + workflow_dispatch: inputs: deploy: description: "Deploy to Maven Central" From 8eba755046ff3ae915ed3378574486840621a491 Mon Sep 17 00:00:00 2001 From: JarrodCTaylor Date: Tue, 22 Aug 2023 08:59:45 -0500 Subject: [PATCH 3883/4033] Remove closure build related scripts The closure build has been spun off into its own repo https://github.com/clojure/cljs.tools.closure --- .github/workflows/closure.yml | 33 --- script/closure-library-release/.gitignore | 2 - .../closure-library-release.sh | 200 ------------------ ...e-closure-library-third-party.pom.template | 142 ------------- .../google-closure-library.pom.template | 140 ------------ script/closure_deps_graph.clj | 39 ---- script/closure_deps_graph.sh | 33 --- 7 files changed, 589 deletions(-) delete mode 100644 .github/workflows/closure.yml delete mode 100644 script/closure-library-release/.gitignore delete mode 100755 script/closure-library-release/closure-library-release.sh delete mode 100644 script/closure-library-release/google-closure-library-third-party.pom.template delete mode 100644 script/closure-library-release/google-closure-library.pom.template delete mode 100644 script/closure_deps_graph.clj delete mode 100755 script/closure_deps_graph.sh diff --git a/.github/workflows/closure.yml b/.github/workflows/closure.yml deleted file mode 100644 index 0caa8eb0f..000000000 --- a/.github/workflows/closure.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Stage Google closure library - -on: [workflow_dispatch] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Check out - uses: actions/checkout@v3 - - name: Set Github identity - run: | - git config --global user.name clojure-build - git config --global user.email "clojure-build@users.noreply.github.com" - - name: Set up Java - uses: actions/setup-java@v3 - with: - java-version: 8 - distribution: 'temurin' - server-id: sonatype-nexus-staging - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} - gpg-passphrase: GPG_PASSPHRASE - - name: Release - run: | - cd script/closure-library-release - ./closure-library-release.sh - env: - MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} - HUDSON: true diff --git a/script/closure-library-release/.gitignore b/script/closure-library-release/.gitignore deleted file mode 100644 index 4a225889a..000000000 --- a/script/closure-library-release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -closure-library/ -tmp-build/ diff --git a/script/closure-library-release/closure-library-release.sh b/script/closure-library-release/closure-library-release.sh deleted file mode 100755 index e5cdb29f0..000000000 --- a/script/closure-library-release/closure-library-release.sh +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env bash - -# closure-library-release.sh - -# ClojureScript depends on the Google Closure JavaScript Libraries, -# but Google does not publish those libraries in a Maven repository. -# This script builds generates Maven projects for the Google Closure -# Library and, optionally, deploys them. - -# The Google Closure Libraries are divided into two parts: the main -# library and third-party extensions. The main library is Apache -# licensed; the third-party extensions have a variety of different -# licenses. However, code in the main library depends on the -# third-party extensions, not the other way around. See CLJS-418 for -# details. - -# To manage this, we build two JARs, google-closure-library and -# google-closure-library-third-party, with the former declaring an -# explicit dependency on the latter. This permits consumers to exclude -# the third-party libraries (and their various licenses) if they know -# they don't need them. - -# To match this structure, we need to alter the deps.js file that the -# Google Closure Compiler uses to resolve dependencies. See CLJS-276 -# for details. - -# The last release ZIP made by Google was 20130212-95c19e7f0f5f. To -# get newer versions, we have to go to the Git repository. - - -set -e - -### Constants - -POM_TEMPLATE_FILE="google-closure-library.pom.template" -THIRD_PARTY_POM_TEMPLATE_FILE="google-closure-library-third-party.pom.template" -GIT_CLONE_URL="https://github.com/google/closure-library.git" - -### Functions - -function print_usage { - echo "Usage: ./make-closure-library-jars.sh - - is the root directory of the Google Closure library -Git repository." -} - -### MAIN SCRIPT BEGINS HERE - -## Command-line validation - -if [[ ! -e $POM_TEMPLATE_FILE || ! -e $THIRD_PARTY_POM_TEMPLATE_FILE ]]; then - echo "This script must be run from the directory containing -google-closure-library.pom.template and -google-closure-library-third-party.pom.template" - exit 1 -fi - -## Fetch the Git repo - -closure_library_dir="closure-library" - -if [[ ! -d $closure_library_dir ]]; then - git clone "$GIT_CLONE_URL" "$closure_library_dir" -fi - -( - cd "$closure_library_dir" - git clean -fdx - git checkout master - git pull -) - -closure_library_base="$closure_library_dir/closure" -third_party_base="$closure_library_dir/third_party/closure" - -if [[ ! -d $closure_library_base || ! -d $third_party_base ]]; then - echo "$closure_library_dir does not look like the Closure library" - exit 1 -fi - -## Working directory - -now=$(date "+%Y%m%d%H%M%S") -work_dir="tmp-build" - -echo "Working directory: $work_dir" - -rm -rf "$work_dir" -mkdir "$work_dir" - -## Git parsing for release version - -commit_details=$(cd "$closure_library_dir"; git log -n 1 '--pretty=format:%H %ci') - -commit_short_sha=${commit_details:0:8} -commit_date="${commit_details:41:4}${commit_details:46:2}${commit_details:49:2}" -release_version="0.0-${commit_date}-${commit_short_sha}" - -echo "HEAD commit: $commit_details" -echo "Date: $commit_date" -echo "Short SHA: $commit_short_sha" -echo "Release version: $release_version" - -## Creating directories - -project_dir="$work_dir/google-closure-library" -src_dir="$project_dir/src/main/resources" - -third_party_project_dir="$work_dir/google-closure-library-third-party" -third_party_src_dir="$third_party_project_dir/src/main/resources" - -mkdir -p "$src_dir" "$third_party_src_dir" - -## Generate deps.js -../closure_deps_graph.sh - -## Normalize deps.js -perl -p -i -e 's/\]\);/\], \{\}\);/go' \ - "$closure_library_base/goog/deps.js" - -perl -p -i -e 's/..\/..\/third_party\/closure\/goog\///go' \ - "$closure_library_base/goog/deps.js" - -perl -p -i -e "s/goog.addDependency\('base.js', \[\], \[\], \{\}\);/goog.addDependency\(\'base.js\', \[\'goog\'\], \[\], \{\}\);/go" \ - "$closure_library_base/goog/deps.js" - -## Copy Closure sources - -cp -r \ - "$closure_library_dir/AUTHORS" \ - "$closure_library_dir/LICENSE" \ - "$closure_library_dir/README.md" \ - "$closure_library_dir/closure/goog" \ - "$src_dir" - -cp -r \ - "$closure_library_dir/AUTHORS" \ - "$closure_library_dir/LICENSE" \ - "$closure_library_dir/README.md" \ - "$closure_library_dir/third_party/closure/goog" \ - "$third_party_src_dir" - -## Modify main deps.js for third-party JAR; see CLJS-276: - -perl -p -i -e 's/..\/..\/third_party\/closure\/goog\///go' \ - "$src_dir/goog/deps.js" - -## Remove empty third-party deps.js and base.js - -rm -f \ - "$third_party_src_dir/goog/deps.js" \ - "$third_party_src_dir/goog/base.js" - -## Generate the POM files: - -perl -p -e "s/RELEASE_VERSION/$release_version/go" \ - "$POM_TEMPLATE_FILE" \ - > "$project_dir/pom.xml" - -perl -p -e "s/RELEASE_VERSION/$release_version/go" \ - "$THIRD_PARTY_POM_TEMPLATE_FILE" \ - > "$third_party_project_dir/pom.xml" - -## Deploy the files if we are on Hudson - -if [ "$HUDSON" = "true" ]; then - ( - cd "$third_party_project_dir" - mvn -ntp -B --fail-at-end \ - -Psonatype-oss-release \ - -Dsource.skip=true \ - clean deploy - ) - - ( - cd "$project_dir" - mvn -ntp -B --fail-at-end \ - -Psonatype-oss-release \ - -Dsource.skip=true \ - clean deploy - ) - - echo "Now log in to https://oss.sonatype.org/ to release" - echo "the staging repository." -else - echo "Not on Hudson, local Maven install" - ( - cd "$third_party_project_dir" - mvn clean - mvn -ntp -B package - mvn install:install-file -Dfile=./target/google-closure-library-third-party-$release_version.jar -DpomFile=pom.xml - ) - ( - cd "$project_dir" - mvn clean - mvn -ntp -B package - mvn install:install-file -Dfile=./target/google-closure-library-$release_version.jar -DpomFile=pom.xml - ) -fi diff --git a/script/closure-library-release/google-closure-library-third-party.pom.template b/script/closure-library-release/google-closure-library-third-party.pom.template deleted file mode 100644 index b04c2cfa3..000000000 --- a/script/closure-library-release/google-closure-library-third-party.pom.template +++ /dev/null @@ -1,142 +0,0 @@ - - 4.0.0 - org.clojure - google-closure-library-third-party - RELEASE_VERSION - jar - Google Closure Library Third-Party Extensions - - - org.sonatype.oss - oss-parent - 9 - - - http://code.google.com/p/closure-library/ - - - The Google Closure Library is a collection of JavaScript code - designed for use with the Google Closure JavaScript Compiler. - - This non-official distribution was prepared by the ClojureScript - team at http://clojure.org/ - - This package contains extensions to the Google Closure Library - using third-party components, which may be distributed under - licenses other than the Apache license. Licenses for individual - library components may be found in source-code comments. - - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.html - repo - - Note: the Google Closure library third-party extensions - contain code under a variety of licenses, which may be found - in source-code comments in each file. - - - - - - Google - http://www.google.com - - - - - Google, Inc. - - - Mohamed Mansour - hello@mohamedmansour.com - - - Bjorn Tipling - bjorn.tipling@gmail.com - - - SameGoal LLC - help@samegoal.com - - - Guido Tapia - guido.tapia@gmail.com - - - Andrew Mattie - amattie@gmail.com - - - Ilia Mirkin - ibmirkin@gmail.com - - - Ivan Kozik - ivan.kozik@gmail.com - - - Rich Dougherty - rich@rd.gen.nz - - - - - scm:https://github.com/google/closure-library.git - - - scm:git:https://github.com/google/closure-library.git - - https://github.com/google/closure-library - - - - code.google.com - http://code.google.com/p/closure-library/issues - - - - - sonatype-oss-release - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.4.4 - - - default-deploy - deploy - - - deploy - - - - - - https://oss.sonatype.org/ - - sonatype-nexus-staging - - - - - - - diff --git a/script/closure-library-release/google-closure-library.pom.template b/script/closure-library-release/google-closure-library.pom.template deleted file mode 100644 index 05f421d11..000000000 --- a/script/closure-library-release/google-closure-library.pom.template +++ /dev/null @@ -1,140 +0,0 @@ - - 4.0.0 - org.clojure - google-closure-library - RELEASE_VERSION - jar - Google Closure Library - - - org.sonatype.oss - oss-parent - 9 - - - http://code.google.com/p/closure-library/ - - - The Google Closure Library is a collection of JavaScript code - designed for use with the Google Closure JavaScript Compiler. - - This non-official distribution was prepared by the ClojureScript - team at http://clojure.org/ - - - - - org.clojure - google-closure-library-third-party - RELEASE_VERSION - - - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.html - repo - - - - - Google - http://www.google.com - - - - - Google, Inc. - - - Mohamed Mansour - hello@mohamedmansour.com - - - Bjorn Tipling - bjorn.tipling@gmail.com - - - SameGoal LLC - help@samegoal.com - - - Guido Tapia - guido.tapia@gmail.com - - - Andrew Mattie - amattie@gmail.com - - - Ilia Mirkin - ibmirkin@gmail.com - - - Ivan Kozik - ivan.kozik@gmail.com - - - Rich Dougherty - rich@rd.gen.nz - - - - - scm:https://github.com/google/closure-library.git - - - scm:git:https://github.com/google/closure-library.git - - https://github.com/google/closure-library - - - - code.google.com - http://code.google.com/p/closure-library/issues - - - - - sonatype-oss-release - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.4.4 - - - default-deploy - deploy - - - deploy - - - - - - https://oss.sonatype.org/ - - sonatype-nexus-staging - - - - - - - diff --git a/script/closure_deps_graph.clj b/script/closure_deps_graph.clj deleted file mode 100644 index 094cd7b05..000000000 --- a/script/closure_deps_graph.clj +++ /dev/null @@ -1,39 +0,0 @@ -(ns closure-deps-graph - (:require [clojure.java.io :as io]) - (:import [java.io File] - [com.google.javascript.jscomp SourceFile BasicErrorManager] - [com.google.javascript.jscomp.deps - BrowserModuleResolver - DepsGenerator DepsGenerator$InclusionStrategy ModuleLoader - ModuleLoader$PathResolver])) - -(defn js-files-in - "Return a sequence of all .js files in the given directory." - [dir] - (filter - #(let [name (.getName ^File %)] - (and (.endsWith name ".js") - (not= \. (first name)))) - (file-seq dir))) - -(spit (io/file "closure-library/closure/goog/deps.js") - (.computeDependencyCalls - (DepsGenerator. - [] - (map #(SourceFile/fromFile (.getAbsolutePath %)) - (mapcat (comp js-files-in io/file) - ["closure-library/closure/goog" "closure-library/third_party/closure/goog"])) - DepsGenerator$InclusionStrategy/ALWAYS - (.getAbsolutePath (io/file "closure-library/closure/goog")) - (proxy [BasicErrorManager] [] - (report [level error] - (println error)) - (println [level error] - (println error))) - (-> (ModuleLoader/builder) - (.setErrorHandler nil) - (.setModuleRoots []) - (.setInputs []) - (.setFactory BrowserModuleResolver/FACTORY) - (.setPathResolver ModuleLoader$PathResolver/ABSOLUTE) - (.build))))) diff --git a/script/closure_deps_graph.sh b/script/closure_deps_graph.sh deleted file mode 100755 index cf62b423b..000000000 --- a/script/closure_deps_graph.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -z "$CLJS_SCRIPT_QUIET" ]]; then - set -x -fi - -FILE_SEP='/' -PATH_SEP=':' -OS_ID=`uname | tr [:upper:] [:lower:]` -CLJS_SCRIPT_MVN_OPTS=${CLJS_SCRIPT_MVN_OPTS:-""} - -if [[ $OS_ID == *mingw* ]] -then - echo "MINGW detected" - # Refer to http://www.mingw.org/wiki/Posix_path_conversion - FILE_SEP='//' - PATH_SEP=';' -fi - -CP_FILE=`mktemp /tmp/cljs_cp.txt.XXXXXXXXXXX` - -mvn -ntp -B -f ../../pom.template.xml dependency:build-classpath -Dmdep.outputFile=$CP_FILE -Dmdep.fileSeparator=$FILE_SEP -Dmdep.pathSeparator=$PATH_SEP $CLJS_SCRIPT_MVN_OPTS - -CLJS_CP=`cat $CP_FILE` - -# For Hudson server -if [ "$HUDSON" = "true" ]; then - $JAVA_HOME/bin/java -server -cp "$CLJS_CP""$PATH_SEP""src/main/clojure" clojure.main ../closure_deps_graph.clj -else - java -server -cp "$CLJS_CP""$PATH_SEP""src/main/clojure" clojure.main ../closure_deps_graph.clj -fi From 17068fa91cae169ed7c881adb593d85138efe6d0 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 29 Aug 2023 17:22:08 -0500 Subject: [PATCH 3884/4033] Update links in readme to https and fixed some broken links --- README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 85a3098a2..829b75a29 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ ## What is ClojureScript? ## -[ClojureScript](http://clojurescript.org) is a compiler for [Clojure](http://clojure.org) that targets JavaScript. It is designed to emit JavaScript code which is compatible with the advanced compilation mode of the [Google Closure](https://developers.google.com/closure/compiler/) optimizing compiler. +[ClojureScript](https://clojurescript.org) is a compiler for [Clojure](https://clojure.org) that targets JavaScript. It is designed to emit JavaScript code which is compatible with the advanced compilation mode of the [Google Closure](https://developers.google.com/closure/compiler/) optimizing compiler. -Official web site: http://clojurescript.org +Official web site: https://clojurescript.org ## Releases and dependency information ## Latest stable release: 1.11.60 -* [All released versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) +* [All released versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22) -[Leiningen](http://github.com/technomancy/leiningen/) dependency information: +[Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` [org.clojure/clojurescript "1.11.60"] ``` -[Maven](http://maven.apache.org) dependency information: +[Maven](https://maven.apache.org) dependency information: ``` @@ -28,21 +28,21 @@ Latest stable release: 1.11.60 ## Getting Started ## -* Read the [Quick Start](http://clojurescript.org/guides/quick-start) guide. -* Read the [Documentation](http://clojurescript.org). -* Try a [tutorial](http://clojurescript.org/guides). -* [Companies using ClojureScript](http://clojurescript.org/community/companies) +* Read the [Quick Start](https://clojurescript.org/guides/quick-start) guide. +* Read the [Documentation](https://clojurescript.org). +* Try a [tutorial](https://clojurescript.org/guides). +* [Companies using ClojureScript](https://clojurescript.org/community/companies) ## Questions, Feedback? ## Please point all of your questions and feedback to the -[Clojure mailing list](http://groups.google.com/group/clojure). There +[Clojure mailing list](https://groups.google.com/group/clojure). There is a community run -[ClojureScript user mailing list](http://groups.google.com/group/clojurescript) and +[ClojureScript user mailing list](https://groups.google.com/group/clojurescript) and the IRC channel, `#clojurescript` on [freenode.net](https://freenode.net/), is quite active. -There is also a community run [Slack channel](http://clojurians.slack.com). The +There is also a community run [Slack channel](https://clojurians.slack.com). The Jira bug/feature tracking application is located at -. Before submitting issues +. Before submitting issues please read the [Reporting Issues](https://github.com/clojure/clojurescript/wiki/Reporting-Issues) page first. @@ -53,28 +53,28 @@ ClojureScript operates under the same license as Clojure. All contributors must have a signed CA (Contributor's Agreement) and submit their patch via the appropriate channels. If you're interested in contributing to the project, please see the -[contributing](http://clojure.org/contributing) page on -[clojure.org](http://clojure.org). For more information about working +[contributing](https://clojure.org/dev/contributing) page on +[clojure.org](https://clojure.org). For more information about working on the compiler and testing check the -[Developer section of the wiki](http://github.com/clojure/clojurescript/wiki/Developers). +[Developer section of the wiki](https://github.com/clojure/clojurescript/wiki/Developers). YourKit ---- - + YourKit has given an open source license for their profiler, greatly simplifying the profiling of ClojureScript performance. YourKit supports open source projects with its full-featured Java Profiler. -YourKit, LLC is the creator of YourKit Java Profiler -and YourKit .NET Profiler, +YourKit, LLC is the creator of YourKit Java Profiler +and YourKit .NET Profiler, innovative and intelligent tools for profiling Java and .NET applications. ## License ## 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) + Public License 1.0 (https://opensource.org/license/epl-1-0/) which can be found in the file epl-v10.html at the root of this distribution. By using this software in any fashion, you are agreeing to be bound by the terms of this license. You must From 0da9884836e9f8ba190f3b789adcb6b9b85a669c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 13 Sep 2023 09:27:33 -0400 Subject: [PATCH 3885/4033] revert CLJ-3383 --- src/main/clojure/cljs/cli.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index 0f91b2fab..f404fac9a 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -495,7 +495,7 @@ present" (merge (select-keys env-opts (cond-> closure/known-opts - (not repl?) (disj :browser-repl))) + repl? (conj :browser-repl))) options (when main-ns {:main main-ns})) opts From df04ee2c41ce7683b52c5793d0154ca88771f0f7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 25 Sep 2023 14:29:36 -0400 Subject: [PATCH 3886/4033] add symbol and bigint prim types (#213) --- src/main/clojure/cljs/core.cljc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index fa5aaf685..72f465427 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1304,18 +1304,23 @@ {nil "null" 'object "object" 'string "string" + 'symbol "symbol" 'number "number" + 'bigint "bigint" 'array "array" 'function "function" 'boolean "boolean" 'default "_"}) +;; only used for generating warnings when extending fundamental JS types (def ^:private js-base-type {'js/Boolean "boolean" 'js/String "string" + 'js/Symbol "symbol" 'js/Array "array" 'js/Object "object" 'js/Number "number" + 'js/BigInt "bigint" 'js/Function "function"}) (core/defmacro reify From a5b2c8a57c1069d740aa329ce60818c1ccf4435e Mon Sep 17 00:00:00 2001 From: davidnolen Date: Mon, 25 Sep 2023 23:09:27 -0400 Subject: [PATCH 3887/4033] * add deps.edn dep info --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 829b75a29..adccde146 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ Latest stable release: 1.11.60 * [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.11.60"} + ``` + [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` From 067eaef03678a06d83caf3f66ddf20f9ee71db5b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 28 Sep 2023 17:03:38 -0400 Subject: [PATCH 3888/4033] * refactor some names for readability (#215) * add resolve-export which tries to find the entrypoint based on :package-json-resolution * make sure compiler opts are threaded through * add unit test for reported @mantine/core issue --- src/main/clojure/cljs/foreign/node.clj | 46 ++++++++++---- src/test/clojure/cljs/foreign/node_test.clj | 66 ++++++++++++++------- 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/src/main/clojure/cljs/foreign/node.clj b/src/main/clojure/cljs/foreign/node.clj index 0ae672295..e37794de2 100644 --- a/src/main/clojure/cljs/foreign/node.clj +++ b/src/main/clojure/cljs/foreign/node.clj @@ -50,31 +50,53 @@ (string/starts-with? path "./") (subs 2))) -(defn- ->export-pkg-json [path export] +(defn- ->export-pkg-json [package-path export] (io/file - (trim-package-json path) + (trim-package-json package-path) (trim-relative export) "package.json")) +(defn resolve-export + "Given an export value, find the entry point based on the + :package-json-resolution value, defaults to :nodejs. Returns nil + if we couldn't resolve it." + [export opts] + (if (string? export) + export + ;; we check for require to attempt to filter out + ;; strange cases, i.e. import but no require etc. + (when (and (map? export) (contains? export "require")) + (let [resolve (:package-json-resolution opts :nodejs) + lookup (if (sequential? resolve) + (or (some #{"import" "require"} resolve) "require") + ({:webpack "import" :nodejs "require"} resolve)) + entry (get export lookup)] + (if (map? entry) + (get entry "default") + entry))))) + (defn- export-subpaths - "Examine the export subpaths to compute subpackages" - [pkg-jsons export-subpath export path pkg-name] + "Examine the export subpaths to compute subpackages. Add them to pkg-json + parameter (this is a reduce-kv helper)." + [pkg-jsons export-subpath export package-path pkg-name opts] ;; NOTE: ignore "." exports for now (if (= "." export-subpath) - pkg-jsons + (if-let [resolved (resolve-export export opts)] + (assoc-in pkg-jsons [package-path "main"] resolved) + pkg-jsons) ;; technically the following logic is a bit brittle since `exports` is ;; supposed to be used to hide the package structure. ;; instead, here we assume the export subpath does match the library structure ;; on disk, if we find a package.json we add it to pkg-jsons map ;; and we synthesize "name" key based on subpath - (let [export-pkg-json (->export-pkg-json path export-subpath)] + (let [export-pkg-json-file (->export-pkg-json package-path export-subpath)] ;; note this will ignore export wildcards etc. (cond-> pkg-jsons - (.exists export-pkg-json) + (.exists export-pkg-json-file) (-> (assoc - (.getAbsolutePath export-pkg-json) + (.getAbsolutePath export-pkg-json-file) (merge - (json/read-str (slurp export-pkg-json)) + (json/read-str (slurp export-pkg-json-file)) ;; add the name field so that path->main-name works later (when (and (map? export) (contains? export "require")) @@ -92,14 +114,14 @@ detailed information." [pkg-jsons opts] (reduce-kv - (fn [pkg-jsons path {:strs [exports] :as pkg-json}] + (fn [pkg-jsons package-path {:strs [exports] :as pkg-json}] (if (string? exports) pkg-jsons ;; map case (reduce-kv (fn [pkg-jsons export-subpath export] - (export-subpaths pkg-jsons - export-subpath export path (get pkg-json "name"))) + (export-subpaths pkg-jsons export-subpath + export package-path (get pkg-json "name") opts)) pkg-jsons exports))) pkg-jsons pkg-jsons)) diff --git a/src/test/clojure/cljs/foreign/node_test.clj b/src/test/clojure/cljs/foreign/node_test.clj index 71c00ae40..926ccd03a 100644 --- a/src/test/clojure/cljs/foreign/node_test.clj +++ b/src/test/clojure/cljs/foreign/node_test.clj @@ -29,14 +29,20 @@ ;; ============================================================================= ;; Tests -(defn pkg-jsons [] - (-> (util/module-file-seq {}) - node/get-pkg-jsons)) +(defn pkg-jsons + ([] + (pkg-jsons {})) + ([opts] + (-> (util/module-file-seq opts) + (node/get-pkg-jsons opts)))) -(defn indexed-lib-specs [] - (as-> (-> (util/module-file-seq {}) - (node/node-file-seq->libs-spec* {})) - xs (zipmap (map :file xs) xs))) +(defn indexed-lib-specs + ([] + (indexed-lib-specs {})) + ([opts] + (as-> (-> (util/module-file-seq opts) + (node/node-file-seq->libs-spec* opts)) + xs (zipmap (map :file xs) xs)))) (defn relpath->data ([index path] @@ -60,27 +66,45 @@ (deftest test-path->main-name (install :yarn "react-select" "5.7.2") (testing "Verify that path->main works as expected" - (is (= "react-select" - (node/path->main-name + (let [node-opts {:package-json-resolution :nodejs} + webpack-opts {:package-json-resolution :webpack}] + (is (= "react-select" + (node/path->main-name (.getAbsolutePath (io/file "node_modules/react-select/dist/react-select.cjs.js")) - (relpath->data (pkg-jsons) + (relpath->data (pkg-jsons node-opts) "node_modules/react-select/package.json" :find) - {:package-json-resolution :nodejs}))) - (is (= "react-select/creatable" + node-opts))) + (is (= "react-select/creatable" (node/path->main-name (.getAbsolutePath (io/file "node_modules/react-select/creatable/dist/react-select-creatable.cjs.js")) - (relpath->data (pkg-jsons) + (relpath->data (pkg-jsons node-opts) "node_modules/react-select/creatable/package.json" :find) - {:package-json-resolution :nodejs}))) - (is (nil? (node/path->main-name - (.getAbsolutePath (io/file "node_modules/react-select/dist/react-select.cjs.js")) - (relpath->data (pkg-jsons) - "node_modules/react-select/package.json" :find) - {:package-json-resolution :webpack}))))) + node-opts))) + (is (nil? (node/path->main-name + (.getAbsolutePath (io/file "node_modules/react-select/dist/react-select.cjs.js")) + (relpath->data (pkg-jsons webpack-opts) + "node_modules/react-select/package.json" :find) + webpack-opts)))))) -(comment +(deftest test-exports-with-choices + (install :yarn "@mantine/core" "7.0.2") + (testing "Verify that complex exports are handled" + (let [node-opts {:package-json-resolution :nodejs} + webpack-opts {:package-json-resolution :webpack}] + (is (= "@mantine/core" + (node/path->main-name + (.getAbsolutePath (io/file "node_modules/@mantine/core/cjs/index.js")) + (relpath->data (pkg-jsons node-opts) + "node_modules/@mantine/core/package.json" :find) + node-opts))) + (is (= "@mantine/core" + (node/path->main-name + (.getAbsolutePath (io/file "node_modules/@mantine/core/esm/index.mjs")) + (relpath->data (pkg-jsons webpack-opts) + "node_modules/@mantine/core/package.json" :find) + webpack-opts)))))) +(comment (test/run-tests) (cleanup) - ) From 803603217139cce6a3807d0c76511de6b688a5d4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Nov 2023 11:46:11 -0500 Subject: [PATCH 3889/4033] CLJS-3406 implement reset-vals! and swap-vals! through protocols (#216) Co-authored-by: Nikita Prokopov --- src/main/cljs/cljs/core.cljs | 36 ++++++++++++++++--------- src/test/cljs/cljs/core_test.cljs | 44 +++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 620bc8abc..baa502dab 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4569,15 +4569,17 @@ reduces them without incurring seq initialization" atom before and after the reset." {:added "1.9"} [a new-value] - (let [validate (.-validator a)] - (when-not (nil? validate) - (when-not (validate new-value) - (throw (js/Error. "Validator rejected reference state")))) - (let [old-value (.-state a)] - (set! (.-state a) new-value) - (when-not (nil? (.-watches a)) - (-notify-watches a old-value new-value)) - [old-value new-value]))) + (if (instance? Atom a) + (let [validate (.-validator a)] + (when-not (nil? validate) + (when-not (validate new-value) + (throw (js/Error. "Validator rejected reference state")))) + (let [old-value (.-state a)] + (set! (.-state a) new-value) + (when-not (nil? (.-watches a)) + (-notify-watches a old-value new-value)) + [old-value new-value])) + [(-deref a) (-reset! a new-value)])) (defn swap! "Atomically swaps the value of atom to be: @@ -4608,13 +4610,21 @@ reduces them without incurring seq initialization" Returns [old new], the value of the atom before and after the swap." {:added "1.9"} ([a f] - (reset-vals! a (f (.-state a)))) + (if (instance? Atom a) + (reset-vals! a (f (.-state a))) + [(-deref a) (-swap! a f)])) ([a f x] - (reset-vals! a (f (.-state a) x))) + (if (instance? Atom a) + (reset-vals! a (f (.-state a) x)) + [(-deref a) (-swap! a f x)])) ([a f x y] - (reset-vals! a (f (.-state a) x y))) + (if (instance? Atom a) + (reset-vals! a (f (.-state a) x y)) + [(-deref a) (-swap! a f x y)])) ([a f x y & more] - (reset-vals! a (apply f (.-state a) x y more)))) + (if (instance? Atom a) + (reset-vals! a (apply f (.-state a) x y more)) + [(-deref a) (-swap! a f x y more)]))) (defn compare-and-set! "Atomically sets the value of atom to newval if and only if the diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 812d3a74a..625c14c3f 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1998,3 +1998,47 @@ (let [a #js {}] (set! a -x false) (is (false? (.-x a)))))) + +(deftest test-cljs-3406 + (testing "ISwap/IReset protocols" + (let [a (atom {:x 0}) + c (reify + IDeref + (-deref [_] + (:x @a)) + + ISwap + (-swap! [o f] + (:x (swap! a update :x f))) + (-swap! [o f x] + (:x (swap! a update :x f x))) + (-swap! [o f x y] + (:x (swap! a update :x f x y))) + (-swap! [o f x y zs] + (:x (swap! a #(apply update % :x f x y zs)))) + + IReset + (-reset! [o new-value] + (:x (swap! a assoc :x new-value))))] + (is (= 0 @c)) + (is (= 1 (swap! c inc))) + (is (= 1 @c)) + (is (= 2 (swap! c + 1))) + (is (= 2 @c)) + (is (= 5 (swap! c + 1 2))) + (is (= 5 @c)) + (is (= 11 (swap! c + 1 2 3))) + (is (= 11 @c)) + (is (= 0 (reset! c 0))) + (is (= 0 @c)) + + (is (= [0 1] (swap-vals! c inc))) + (is (= 1 @c)) + (is (= [1 2] (swap-vals! c + 1))) + (is (= 2 @c)) + (is (= [2 5] (swap-vals! c + 1 2))) + (is (= 5 @c)) + (is (= [5 11] (swap-vals! c + 1 2 3))) + (is (= 11 @c)) + (is (= [11 0] (reset-vals! c 0))) + (is (= 0 @c))))) From 80acf18ee4ef60768f39589d1f4007895db49ba7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 16 Nov 2023 11:51:08 -0500 Subject: [PATCH 3890/4033] remove EXPERIMENTAL from ES6 iterator support --- src/main/cljs/cljs/core.cljs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index baa502dab..4b9ecc521 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1315,7 +1315,6 @@ (= y (first more))) false))) -;; EXPERIMENTAL: subject to change (deftype ES6Iterator [^:mutable s] Object (next [_] @@ -1326,7 +1325,7 @@ #js {:value nil :done true}))) (defn es6-iterator - "EXPERIMENTAL: Return a ES2015 compatible iterator for coll." + "Return a ES2015+ compatible iterator for coll." [coll] (ES6Iterator. (seq coll))) @@ -1341,7 +1340,7 @@ _rest)) (defn es6-iterator-seq - "EXPERIMENTAL: Given an ES2015 compatible iterator return a seq." + "Given an ES2015+ compatible iterator return a seq." [iter] (let [v (.next iter)] (if (.-done v) @@ -6668,7 +6667,6 @@ reduces them without incurring seq initialization" (.next ext-map-iter))) (remove [_] (js/Error. "Unsupported operation"))) -;; EXPERIMENTAL: subject to change (deftype ES6EntriesIterator [^:mutable s] Object (next [_] @@ -6681,7 +6679,6 @@ reduces them without incurring seq initialization" (defn es6-entries-iterator [coll] (ES6EntriesIterator. (seq coll))) -;; EXPERIMENTAL: subject to change (deftype ES6SetEntriesIterator [^:mutable s] Object (next [_] @@ -6964,8 +6961,6 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) - - ;; EXPERIMENTAL: subject to change (keys [coll] (es6-iterator (keys coll))) (entries [coll] @@ -8080,8 +8075,6 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) - - ;; EXPERIMENTAL: subject to change (keys [coll] (es6-iterator (keys coll))) (entries [coll] @@ -8954,8 +8947,6 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) - - ;; EXPERIMENTAL: subject to change (keys [coll] (es6-iterator (keys coll))) (entries [coll] @@ -9384,8 +9375,6 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) - - ;; EXPERIMENTAL: subject to change (keys [coll] (es6-iterator (seq coll))) (entries [coll] @@ -9545,8 +9534,6 @@ reduces them without incurring seq initialization" (pr-str* coll)) (equiv [this other] (-equiv this other)) - - ;; EXPERIMENTAL: subject to change (keys [coll] (es6-iterator (seq coll))) (entries [coll] From 0c5ecd7b8030b610c979b06bc85ff0991d882f69 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 17 Nov 2023 09:30:43 -0500 Subject: [PATCH 3891/4033] CLJS-3407: bump tools.reader to 1.3.7 * bump tools.reader to 1.3.7 (#217) * re-vendorize --- script/vendorize_deps | 2 +- .../clojure/cljs/vendor/clojure/tools/reader.clj | 12 +++++++----- .../vendor/clojure/tools/reader/reader_types.clj | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/script/vendorize_deps b/script/vendorize_deps index 419adca8a..27f1aa363 100755 --- a/script/vendorize_deps +++ b/script/vendorize_deps @@ -7,7 +7,7 @@ cd src/main/clojure/cljs DJSON_RELEASE="2.4.0" TRANSIT_RELEASE="1.0.329" -TREADER_RELEASE="1.3.6" +TREADER_RELEASE="1.3.7" rm -rf data.json git clone -b "v$DJSON_RELEASE" --depth 1 git@github.com:clojure/data.json.git diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader.clj index ab9db30da..ca71aeab8 100644 --- a/src/main/clojure/cljs/vendor/clojure/tools/reader.clj +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader.clj @@ -378,7 +378,7 @@ (let [o (read* rdr true nil opts pending-forms)] (if (instance? IMeta o) (let [m (if (and line (seq? o)) - (assoc m :line line :column column) + (merge {:line line :column column} m) m)] (if (instance? IObj o) (with-meta o (merge (meta o) m)) @@ -1022,10 +1022,12 @@ ([] (read+string (source-logging-push-back-reader *in*))) ([stream] (read+string stream true nil)) ([^SourceLoggingPushbackReader stream eof-error? eof-value] - (let [o (log-source stream (read stream eof-error? eof-value)) - s (.trim (str (:buffer @(.source-log-frames stream))))] + (let [^StringBuilder buf (doto (:buffer @(.source-log-frames stream)) (.setLength 0)) + o (log-source stream (read stream eof-error? eof-value)) + s (.trim (str buf))] [o s])) ([opts ^SourceLoggingPushbackReader stream] - (let [o (log-source stream (read opts stream)) - s (.trim (str (:buffer @(.source-log-frames stream))))] + (let [^StringBuilder buf (doto (:buffer @(.source-log-frames stream)) (.setLength 0)) + o (log-source stream (read opts stream)) + s (.trim (str buf))] [o s]))) diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj index 8be8cd96c..dab408fd4 100644 --- a/src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader/reader_types.clj @@ -317,7 +317,7 @@ [reader f] (let [frame (.source-log-frames ^SourceLoggingPushbackReader reader) ^StringBuilder buffer (:buffer @frame) - new-frame (assoc-in @frame [:offset] (.length buffer))] + new-frame (assoc @frame :offset (.length buffer))] (with-bindings {frame new-frame} (let [ret (f)] (if (instance? clojure.lang.IObj ret) From 38fa4c289cbe94ec981333d1f2ee632bf6f55034 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 5 Dec 2023 11:01:44 -0500 Subject: [PATCH 3892/4033] CLJS-3408: Handle @extends in externs (#218) * externs: parse @extends into :super * add test case * check base type when checking externs * add CLJS-3408 test, verifying reported issue is resolved --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- src/main/clojure/cljs/externs.clj | 4 +++- src/test/clojure/cljs/externs_infer_tests.clj | 11 +++++++++++ src/test/clojure/cljs/externs_parsing_tests.clj | 13 ++++++++++++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2bbc2a7ea..6759c6d24 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1048,7 +1048,10 @@ 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)) + (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)))))))) (defn has-extern? diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index e940461e7..ddccfdc4e 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -73,7 +73,9 @@ {:tag (get-tag ty)} (if (or (.isConstructor info) (.isInterface info)) (let [qname (symbol (.. node getFirstChild getQualifiedName))] - (cond-> {:tag 'Function} + (cond-> (merge {:tag 'Function} + (when (.hasBaseType info) + {:super (get-tag (.getBaseType info))})) (.isConstructor info) (merge {:ctor qname}) (.isInterface info) (merge {:iface qname}))) (if (or (.hasReturnType info) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index f832ed730..8ca7ff9aa 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -475,6 +475,17 @@ ["Object.renderInline;"]) res))))) +(deftest test-cljs-3408 + (testing "inheritance of JS Types is inferred" + (let [ws (atom []) + res (infer-test-helper + {:forms '[(ns foo.core) + (.querySelectorAll js/document "div")] + :warnings ws + :warn true + :with-core? true})] + (is (empty? @ws))))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index 7bcc44539..c2b199cf1 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -10,7 +10,8 @@ (:require [cljs.closure :as closure] [cljs.externs :as externs] [clojure.java.io :as io] - [clojure.test :as test :refer [deftest is]])) + [clojure.test :as test :refer [deftest is]]) + (:import [com.google.javascript.jscomp CommandLineRunner])) (deftest cljs-3121 (let [externs (externs/parse-externs @@ -34,6 +35,16 @@ (is (= 'any (get-in ns [:defs 'get :ret-tag]))) (is (= 'array (get-in ns [:defs 'getKeys :ret-tag]))))) +(deftest test-parse-super + (let [info (-> + (filter + (fn [s] + (= "externs.zip//w3c_dom2.js" (.getName s))) + (CommandLineRunner/getDefaultExterns)) + first externs/parse-externs externs/index-externs + (find 'HTMLDocument) first meta)] + (is (= 'Document (:super info))))) + (comment (externs/parse-externs From 22e3dc42d60747c3187bedb82079101caf049549 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 5 Dec 2023 11:14:41 -0500 Subject: [PATCH 3893/4033] * refactor last commit --- src/main/clojure/cljs/externs.clj | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index ddccfdc4e..5da818a72 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -73,11 +73,10 @@ {:tag (get-tag ty)} (if (or (.isConstructor info) (.isInterface info)) (let [qname (symbol (.. node getFirstChild getQualifiedName))] - (cond-> (merge {:tag 'Function} - (when (.hasBaseType info) - {:super (get-tag (.getBaseType info))})) + (cond-> {:tag 'Function} (.isConstructor info) (merge {:ctor qname}) - (.isInterface info) (merge {:iface qname}))) + (.isInterface info) (merge {:iface qname}) + (.hasBaseType info) (merge {:super (get-tag (.getBaseType info))}))) (if (or (.hasReturnType info) (as-> (.getParameterCount info) c (and c (pos? c)))) From 54c27fbdf884142ac4e0d9d4ed38ec119905f17e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 22 Jan 2024 17:16:04 -0500 Subject: [PATCH 3894/4033] CLJS-3410: JavaScript double values should not hash to the same result (#220) * add `hash-long` * add `hash-double` * use `hash-double` if `!Number.isSafeInteger(n)` * reported test case + additional assertions --- src/main/cljs/cljs/core.cljs | 14 +++++++++++++- src/test/cljs/cljs/hashing_test.cljs | 9 +++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4b9ecc521..0afb3d475 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -960,6 +960,16 @@ h1 (m3-mix-H1 m3-seed k1)] (m3-fmix h1 4)))) +(defn hash-long [high low] + (bit-xor high low)) + +(defn hash-double [f] + (let [arr (doto (js/Float64Array. 1) (aset 0 f)) + buf (.-buffer arr) + high (.getInt32 (js/DataView. buf 0 4)) + low (.getInt32 (js/DataView. buf 4 4))] + (hash-long high low))) + (defn ^number m3-hash-unencoded-chars [in] (let [h1 (loop [i 1 h1 m3-seed] (if (< i (.-length in)) @@ -1021,7 +1031,9 @@ (number? o) (if ^boolean (js/isFinite o) - (js-mod (Math/floor o) 2147483647) + (if-not ^boolean (.isSafeInteger js/Number o) + (hash-double o) + (js-mod (Math/floor o) 2147483647)) (case o ##Inf 2146435072 diff --git a/src/test/cljs/cljs/hashing_test.cljs b/src/test/cljs/cljs/hashing_test.cljs index e40178341..2a2ffcf18 100644 --- a/src/test/cljs/cljs/hashing_test.cljs +++ b/src/test/cljs/cljs/hashing_test.cljs @@ -93,3 +93,12 @@ (deftest test-cljs-1818 (is (= (hash true) 1231)) (is (= (hash false) 1237))) + +(deftest test-cljs-3410 + (testing "Small doubles should not hash the same" + (is (not= (hash-double -0.32553251) (hash-double -0.0000032553251))) + (is (not= (hash -0.32553251) (hash -0.0000032553251)))) + (testing "Same double hashes the same" + (is (= (hash 0.5) (hash 0.5))) + (is (= (hash -0.32553251) (hash -0.32553251))) + (is (= (hash -0.0000032553251) (hash -0.0000032553251))))) From 26dea31110502ae718399868ad7385ffb8efe6f6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 23 Jan 2024 14:34:15 -0500 Subject: [PATCH 3895/4033] 1.11.132 --- README.md | 8 ++++---- changes.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index adccde146..e156881af 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.11.60 +Latest stable release: 1.11.132 * [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.11.60"} + org.clojure/clojurescript {:mvn/version "1.11.132"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.11.60"] +[org.clojure/clojurescript "1.11.132"] ``` [Maven](https://maven.apache.org) dependency information: @@ -28,7 +28,7 @@ Latest stable release: 1.11.60 org.clojure clojurescript - 1.11.60 + 1.11.132 ``` diff --git a/changes.md b/changes.md index 3f94a513e..f20db794f 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,36 @@ +## 1.11.132 + +### Fixes +* CLJS-3410: JavaScript double values should not hash to the same result +* CLJS-3381: Invokable JS namespaces used as constructors not hinted properly +* CLJS-3395: `(set! a -x false)` doesn't work +* CLJS-3399: :as-alias does not work with symbols +* CLJS-3401: dedupe '+ and '_PLUS symbols with :optimize-constants +* CLJS-3400: macroexpand does not expand and and or without args correctly +* CLJS-3398: Docstring of with-redefs should mention usage of ^:dynamic in production +* CLJS-3386: defn varargs argument should be nil when no varargs are passed +* CLJS-3384: get-in should not unreduce values. + +### Changes +* CLJS-3378: Change default :language-in to :ecmascript-next +* CLJS-3385: Extend empty? to counted? colls that arent seqable, such as transients +* CLJS-3327 Add :node-modules-dirs configuration +* CLJS-3391: add cljs.test/run-test +* CLJS-3369: Speed up random-uuid by generating 4 digits at a time +* CLJS-3014: Promote Error->map to be a core fn +* CLJS-3394: Add ECMASCRIPT options for 2018-2021 +* CLJS-2268: Make clojure.string/escape consistent with Clojure +* Bump closure lib to 2023 release +* CLJS-3407: bump tools.reader to 1.3.7 +* remove EXPERIMENTAL from ES6 iterator support +* CLJS-3406 implement reset-vals! and swap-vals! through protocol +* CLJS-3363: reduce-kv on seq of map entries +* CLJS-3393: Efficient drop, partition for persistent/algo colls +* CLJS-3408: Handle @extends in externs +* CLJS-3392: datafy support for js/Error and ExceptionInfo +* CLJS-3379: Add support for node_modules with .cjs extension +* CLJS-3387: Browser repl unable to serve wasm files + ## 1.11.60 ### Fixes From 270deb16539c54393f2301a112bb106ce589a7a4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 16 Feb 2024 14:06:19 -0500 Subject: [PATCH 3896/4033] next iteration --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index 9122296e8..c6119122e 100755 --- a/script/build +++ b/script/build @@ -25,7 +25,7 @@ CLJS_SCRIPT_MVN_OPTS=${CLJS_SCRIPT_MVN_OPTS:-""} # find the v0.0 tag and will always return the total number of commits (even # if the tag is v0.0.1). MAJOR="1" -MINOR="11" +MINOR="12" REVISION=`git --no-replace-objects describe --match v$MAJOR.$MINOR` # Extract the version number from the string. From 3d110e433d804f5e6cede84ced57a5bacaf00423 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 19 Feb 2024 19:10:29 -0500 Subject: [PATCH 3897/4033] Bump Closure to v20230802 (#221) * bump Closure to v20230802 * JDK 11 for CI --- .github/workflows/test.yaml | 35 +++++++++++++++++++++++++++++++ deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 16 +++++++------- 6 files changed, 46 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0a26d955c..f6c1a86ea 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,6 +9,11 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + - uses: DeLaGuardo/setup-clojure@3.1 with: tools-deps: '1.10.1.763' @@ -61,6 +66,11 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + - uses: DeLaGuardo/setup-clojure@3.5 with: cli: '1.10.1.763' @@ -82,6 +92,11 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + - uses: DeLaGuardo/setup-clojure@3.1 with: tools-deps: '1.10.1.763' @@ -121,6 +136,11 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + - uses: DeLaGuardo/setup-clojure@3.1 with: tools-deps: '1.10.1.763' @@ -160,6 +180,11 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + - uses: DeLaGuardo/setup-clojure@3.1 with: tools-deps: '1.10.1.763' @@ -194,6 +219,11 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + - uses: DeLaGuardo/setup-clojure@3.5 with: cli: '1.10.1.763' @@ -211,6 +241,11 @@ jobs: with: fetch-depth: 0 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + - uses: DeLaGuardo/setup-clojure@3.1 with: tools-deps: '1.10.1.763' diff --git a/deps.edn b/deps.edn index d888580f0..72690d6ac 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-unshaded {:mvn/version "v20220502"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20230802"} 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"} diff --git a/pom.template.xml b/pom.template.xml index d41bfba3e..823ecf116 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20220502 + v20230802 org.clojure diff --git a/project.clj b/project.clj index 6b273d48a..c109bbeaa 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-20230227-c7c0a541"] - [com.google.javascript/closure-compiler-unshaded "v20220502"]] + [com.google.javascript/closure-compiler-unshaded "v20230802"]] :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/script/bootstrap b/script/bootstrap index ecc2d2631..fc3407dd3 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="20220502" +CLOSURE_RELEASE="20230802 GCLOSURE_LIB_RELEASE="0.0-20230227-c7c0a541" TREADER_RELEASE="1.3.6" TEST_CHECK_RELEASE="1.1.1" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index ca46b4a1d..7b58ac2a2 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -352,15 +352,13 @@ ;; name is not actually used by Closure in :modules case, ;; but we need to provide _something_ for Closure to not ;; complain - (set! (.sourceMapOutputPath compiler-options) - (str (io/file (util/output-directory opts) - "cljs_modules.map"))) - (set! (.sourceMapOutputPath compiler-options) - (:source-map opts))) - (set! (.sourceMapDetailLevel compiler-options) - SourceMap$DetailLevel/ALL) - (set! (.sourceMapFormat compiler-options) - SourceMap$Format/V3)) + (.setSourceMapOutputPath compiler-options + (str (io/file (util/output-directory opts) + "cljs_modules.map"))) + (.setSourceMapOutputPath compiler-options + (:source-map opts))) + (.setSourceMapDetailLevel compiler-options SourceMap$DetailLevel/ALL) + (.setSourceMapFormat compiler-options SourceMap$Format/V3)) (do (.setOptionsForCompilationLevel level compiler-options) (set-options opts compiler-options) From 2c5dbdf0f728fd78ff6f44e7608a56e1f4365113 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 20 Feb 2024 14:45:38 -0500 Subject: [PATCH 3898/4033] CLJ-XXXX remove unnecessary key-check for HashCollisionNode (#222) Co-authored-by: Hyunwoo Nam port of fix to Clojure https://github.com/clojure/clojure/commit/8b4261bc1e377c8a702f2b5538ab728700737b0f --- src/main/cljs/cljs/core.cljs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 0afb3d475..ac7dbfc45 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7828,15 +7828,13 @@ reduces them without incurring seq initialization" (inode-lookup [inode shift hash key not-found] (let [idx (hash-collision-node-find-index arr cnt key)] - (cond (< idx 0) not-found - (key-test key (aget arr idx)) (aget arr (inc idx)) - :else not-found))) + (cond (< idx 0) not-found + :else (aget arr (inc idx))))) (inode-find [inode shift hash key not-found] (let [idx (hash-collision-node-find-index arr cnt key)] - (cond (< idx 0) not-found - (key-test key (aget arr idx)) (MapEntry. (aget arr idx) (aget arr (inc idx)) nil) - :else not-found))) + (cond (< idx 0) not-found + :else (MapEntry. (aget arr idx) (aget arr (inc idx)) nil)))) (inode-seq [inode] (create-inode-seq arr)) From 4f400cb926e2a567293131d3743e71395691fe21 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 21 Feb 2024 12:29:40 -0500 Subject: [PATCH 3899/4033] CLJS-3320: Compiler warning on trying to use `js` as an ns (#224) Co-authored-by: Mike Fikes --- src/main/clojure/cljs/analyzer.cljc | 8 ++++++++ src/test/clojure/cljs/analyzer_tests.clj | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 6759c6d24..f0f179a49 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -159,6 +159,7 @@ :protocol-impl-recur-with-target true :single-segment-namespace true :munged-namespace true + :js-used-as-alias true :ns-var-clash true :non-dynamic-earmuffed-var true :extend-type-invalid-method-shape true @@ -439,6 +440,10 @@ (str "Namespace " name " contains a reserved JavaScript keyword," " the corresponding Google Closure namespace will be munged to " munged))) +(defmethod error-message :js-used-as-alias + [warning-type {:keys [spec] :as info}] + (str "In " (pr-str spec) ", the alias name js is reserved for JavaScript interop")) + (defmethod error-message :ns-var-clash [warning-type {:keys [ns var] :as info}] (str "Namespace " ns " clashes with var " var)) @@ -2994,6 +2999,9 @@ lib' ((alias-type @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 [(if macros? :fns :macros) 'js])) ; warn only once + (warning :js-used-as-alias env {:spec spec}))) (swap! aliases update-in [alias-type] conj [alias lib] (when js-module-provides [js-module-provides lib])))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index 8ae750ddc..e96b6ae3f 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -1493,6 +1493,13 @@ (:import goog))])) (is (= {} (get-in @cenv [::ana/namespaces 'test.foo :imports]))))) +(deftest test-cljs-3320 + (let [ws (atom [])] + (ana/with-warning-handlers [(collecting-warning-handler ws)] + (binding [ana/*cljs-ns* 'cljs.user] + (analyze ns-env '(ns cljs3320.core (:require [cljs.js :as js]))))) + (is (string/includes? (first @ws) "the alias name js is reserved for JavaScript interop")))) + (deftest test-cljs-3371 (let [ws (atom [])] (ana/with-warning-handlers [(collecting-warning-handler ws)] From 346b8104c1150c5c40f89f8a7e7a269a67136f4b Mon Sep 17 00:00:00 2001 From: Mike Fikes Date: Wed, 21 Feb 2024 12:37:01 -0500 Subject: [PATCH 3900/4033] CLJS-3320: Compiler warning on trying to use `js` as an ns (#223) From fdff5644081eb96e037a910f0688fcd5e9235c31 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 23 Feb 2024 09:25:37 -0500 Subject: [PATCH 3901/4033] CLJS-3290: implement IHash for js Symbol (#225) * implement IHash for js Symbol * add test cases --- src/main/cljs/cljs/core.cljs | 5 +++++ src/test/cljs/cljs/hashing_test.cljs | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ac7dbfc45..e384f1560 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1458,6 +1458,11 @@ (-hash [o] (goog/getUid o))) +(extend-type symbol + IHash + (-hash [o] + (hash (.toString o)))) + ;;this is primitive because & emits call to array-seq (defn inc "Returns a number one greater than num." diff --git a/src/test/cljs/cljs/hashing_test.cljs b/src/test/cljs/cljs/hashing_test.cljs index 2a2ffcf18..7bad7169e 100644 --- a/src/test/cljs/cljs/hashing_test.cljs +++ b/src/test/cljs/cljs/hashing_test.cljs @@ -102,3 +102,12 @@ (is (= (hash 0.5) (hash 0.5))) (is (= (hash -0.32553251) (hash -0.32553251))) (is (= (hash -0.0000032553251) (hash -0.0000032553251))))) + +(deftest test-cljs-3290 + (testing "JS Symbol hash" + (let [s (.for js/Symbol "foo")] + (is (number? (hash s))) + (is (== (hash s) (hash s))) + (is (not (== (hash s) (hash (.for js/Symbol "bar"))))) + (let [m {s 2}] + (is (== 2 (get m s))))))) From acbefb9b1e79b659b639919fbf96cb3726719e25 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 27 Feb 2024 19:59:21 -0500 Subject: [PATCH 3902/4033] CLJS-3411: cljs.core/test behavior does not match docstring (#226) * correct doc string * support vars * add test case --- src/main/cljs/cljs/core.cljs | 13 ++++++++++--- src/test/cljs/cljs/core_test.cljs | 6 +++--- src/test/cljs/cljs/var_test.cljs | 22 ++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 6 ++++-- 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 src/test/cljs/cljs/var_test.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e384f1560..27a2f9e37 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11886,10 +11886,17 @@ reduces them without incurring seq initialization" x)) (defn test - "test [v] finds fn at key :test in var metadata and calls it, - presuming failure will throw exception" + "test [v] - if var, finds fn at key :test in var metadata, if function, finds + special test property. Calls it, presuming failure will throw exception. + + Examples: + + (test my-fn) ;; :ok + (test #'my-fn) ;; :ok" [v] - (let [f (.-cljs$lang$test v)] + (let [f (if (instance? Var v) + (-> v meta :test) + (some-> v .-cljs$lang$test))] (if f (do (f) :ok) :no-test))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 625c14c3f..08581d603 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2006,7 +2006,7 @@ IDeref (-deref [_] (:x @a)) - + ISwap (-swap! [o f] (:x (swap! a update :x f))) @@ -2016,7 +2016,7 @@ (:x (swap! a update :x f x y))) (-swap! [o f x y zs] (:x (swap! a #(apply update % :x f x y zs)))) - + IReset (-reset! [o new-value] (:x (swap! a assoc :x new-value))))] @@ -2031,7 +2031,7 @@ (is (= 11 @c)) (is (= 0 (reset! c 0))) (is (= 0 @c)) - + (is (= [0 1] (swap-vals! c inc))) (is (= 1 @c)) (is (= [1 2] (swap-vals! c + 1))) diff --git a/src/test/cljs/cljs/var_test.cljs b/src/test/cljs/cljs/var_test.cljs new file mode 100644 index 000000000..886326a76 --- /dev/null +++ b/src/test/cljs/cljs/var_test.cljs @@ -0,0 +1,22 @@ +;; 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.var-test + (:require [cljs.test :refer-macros [deftest is testing]])) + +(defn cljs-3411-function + "this function adds two numbers" + {:test #(do + (assert (= (cljs-3411-function 2 3) 5)) + (assert (= (cljs-3411-function 4 4) 8)))} + ([x y] (+ x y))) + +(deftest cljs-3411 + (testing "cljs.core/test respects docstring" + (is (= :ok (test cljs-3411-function))) + (is (= :ok (test #'cljs-3411-function))))) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 29218923f..a0e411309 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -57,7 +57,8 @@ [cljs.inference-test] [cljs.walk-test] [cljs.repl-test] - [cljs.extend-to-native-test])) + [cljs.extend-to-native-test] + [cljs.var-test])) (set! *print-newline* false) @@ -116,4 +117,5 @@ 'cljs.inference-test 'cljs.walk-test 'cljs.repl-test - 'cljs.extend-to-native-test) + 'cljs.extend-to-native-test + 'cljs.var-test) From c39a1b0494806b70e2792229458ac440e3bc595f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 27 Mar 2024 13:34:58 -0400 Subject: [PATCH 3903/4033] * fix as alias test patch from Michiel Borkent --- src/test/cljs/cljs/ns_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/cljs/cljs/ns_test.cljs b/src/test/cljs/cljs/ns_test.cljs index 004085116..2cb85865c 100644 --- a/src/test/cljs/cljs/ns_test.cljs +++ b/src/test/cljs/cljs/ns_test.cljs @@ -47,4 +47,4 @@ (deftest test-cljs-3399 (is (= ::fake/foo :fake.ns/foo) - (= `fake/foo `fake.ns/foo))) + (is (= `fake/foo 'fake.ns/foo)))) From a53e163d9c495904389bd111665e93c4ff0c398e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 27 Mar 2024 14:42:26 -0400 Subject: [PATCH 3904/4033] Bump to GCC 20240317 (#227) --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deps.edn b/deps.edn index 72690d6ac..8925f7e20 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-unshaded {:mvn/version "v20230802"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20240317"} 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"} diff --git a/pom.template.xml b/pom.template.xml index 823ecf116..5906ed5a0 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler-unshaded - v20230802 + v20240317 org.clojure diff --git a/project.clj b/project.clj index c109bbeaa..165a553e0 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-20230227-c7c0a541"] - [com.google.javascript/closure-compiler-unshaded "v20230802"]] + [com.google.javascript/closure-compiler-unshaded "v20240317"]] :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/script/bootstrap b/script/bootstrap index fc3407dd3..6fc56b539 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="20230802 +CLOSURE_RELEASE="20240317" GCLOSURE_LIB_RELEASE="0.0-20230227-c7c0a541" TREADER_RELEASE="1.3.6" TEST_CHECK_RELEASE="1.1.1" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 7b58ac2a2..8d4b41244 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1460,7 +1460,7 @@ (.sortInputsByDeps input closure-compiler)) _ (when (or ana/*verbose* (:verbose opts)) (util/debug-prn "Applying optimizations" (:optimizations opts) "to" (count sources) "sources")) - ^Result result (.compileModules closure-compiler externs inputs compiler-options) + ^Result result (.compileChunks closure-compiler externs inputs compiler-options) ^SourceMap source-map (when (:source-map opts) (.getSourceMap closure-compiler))] (assert (or (nil? (:source-map opts)) source-map) From e74d48d5b0fd8d08c154672349cfa4d8a4166d2d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 18 Apr 2024 10:36:56 -0400 Subject: [PATCH 3905/4033] Upgrade to tools.reader 1.4.2 (#228) --- script/vendorize_deps | 2 +- src/main/clojure/cljs/vendor/clojure/tools/reader.clj | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/script/vendorize_deps b/script/vendorize_deps index 27f1aa363..1353079cd 100755 --- a/script/vendorize_deps +++ b/script/vendorize_deps @@ -7,7 +7,7 @@ cd src/main/clojure/cljs DJSON_RELEASE="2.4.0" TRANSIT_RELEASE="1.0.329" -TREADER_RELEASE="1.3.7" +TREADER_RELEASE="1.4.2" rm -rf data.json git clone -b "v$DJSON_RELEASE" --depth 1 git@github.com:clojure/data.json.git diff --git a/src/main/clojure/cljs/vendor/clojure/tools/reader.clj b/src/main/clojure/cljs/vendor/clojure/tools/reader.clj index ca71aeab8..f5f3afd9e 100644 --- a/src/main/clojure/cljs/vendor/clojure/tools/reader.clj +++ b/src/main/clojure/cljs/vendor/clojure/tools/reader.clj @@ -27,6 +27,8 @@ java.util.regex.Pattern (java.util List LinkedList))) +(set! *warn-on-reflection* true) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -759,7 +761,7 @@ (ns-name *ns*) (= \: (first token)) - (some-> token (subs 1) parse-symbol second' symbol resolve-ns ns-name) + (some-> token (subs 1) parse-symbol second' symbol resolve-ns) :else (some-> token parse-symbol second'))] @@ -1022,12 +1024,12 @@ ([] (read+string (source-logging-push-back-reader *in*))) ([stream] (read+string stream true nil)) ([^SourceLoggingPushbackReader stream eof-error? eof-value] - (let [^StringBuilder buf (doto (:buffer @(.source-log-frames stream)) (.setLength 0)) + (let [^StringBuilder buf (doto ^StringBuilder (:buffer @(.source-log-frames stream)) (.setLength 0)) o (log-source stream (read stream eof-error? eof-value)) s (.trim (str buf))] [o s])) ([opts ^SourceLoggingPushbackReader stream] - (let [^StringBuilder buf (doto (:buffer @(.source-log-frames stream)) (.setLength 0)) + (let [^StringBuilder buf (doto ^StringBuilder (:buffer @(.source-log-frames stream)) (.setLength 0)) o (log-source stream (read opts stream)) s (.trim (str buf))] [o s]))) From 47a7bfdefd156027b7b98cb483ddd93add71fbb2 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 7 Jun 2024 15:39:24 -0400 Subject: [PATCH 3906/4033] CLJS-3413: Macros not loaded w/ single segment namespace loaded via `:preloads` (#229) * test demonstrating that single segment macro namespaces by themselves are not a problem * if we cannot find a macro namespace in the current namespace try one more time w/ a global lookup * add clarifying comment about `:preloads` in `cljs.closure/build` --- src/main/clojure/cljs/analyzer.cljc | 8 ++++++-- src/main/clojure/cljs/closure.clj | 4 ++++ src/test/cljs/cljs/macro_test.cljs | 8 ++++++-- src/test/cljs/single_seg_macros.clj | 4 ++++ 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/test/cljs/single_seg_macros.clj diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f0f179a49..376956d8e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3954,8 +3954,12 @@ :cljs [(identical? "clojure.repl" nstr) (find-macros-ns 'cljs.repl)]) #?@(:clj [(.contains nstr ".") (find-ns (symbol nstr))] :cljs [(goog.string/contains nstr ".") (find-macros-ns (symbol nstr))]) - :else (some-> env :ns :require-macros (get (symbol nstr)) #?(:clj find-ns - :cljs find-macros-ns))))) + :else + (or (some-> env :ns :require-macros (get (symbol nstr)) #?(:clj find-ns + :cljs find-macros-ns)) + ;; single segment namespace case + #?(:clj (find-ns (symbol nstr)) + :cljs (find-macros-ns (symbol nstr))))))) (defn get-expander* [sym env] (when-not (or (some? (gets env :locals sym)) ; locals hide macros diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 8d4b41244..dffd923fc 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -3081,6 +3081,10 @@ [(-compile (io/resource "cljs/nodejs.cljs") (assoc opts :output-file "nodejs.js"))])) deps/dependency-order + ;; NOTE: :preloads are compiled *after* + ;; user specified inputs. Thus user code cannot + ;; depend on anything (i.e. fn/macros) defined + ;; in preloads via global access pattern (add-preloads opts) remove-goog-base add-goog-base diff --git a/src/test/cljs/cljs/macro_test.cljs b/src/test/cljs/cljs/macro_test.cljs index ed433e4f6..6c7354d56 100644 --- a/src/test/cljs/cljs/macro_test.cljs +++ b/src/test/cljs/cljs/macro_test.cljs @@ -8,9 +8,10 @@ (ns cljs.macro-test (:refer-clojure :exclude [==]) - (:require [cljs.test :refer-macros [deftest is]]) + (:require [cljs.test :as test :refer-macros [deftest is]]) (:use-macros [cljs.macro-test.macros :only [== sm-cljs-3027]]) - (:require-macros [cljs.macro-test.cljs2852])) + (:require-macros [cljs.macro-test.cljs2852] + [single-seg-macros])) (deftest test-macros (is (= (== 1 1) 2))) @@ -31,3 +32,6 @@ (deftest test-cljs-3027 (is (= {"a" "b"} (sm-cljs-3027)))) + +(deftest test-cljs-3413 + (is (= 5 (single-seg-macros/test-macro 2 3)))) diff --git a/src/test/cljs/single_seg_macros.clj b/src/test/cljs/single_seg_macros.clj new file mode 100644 index 000000000..679f07fd9 --- /dev/null +++ b/src/test/cljs/single_seg_macros.clj @@ -0,0 +1,4 @@ +(ns single-seg-macros) + +(defmacro test-macro [a b] + `(+ ~a ~b)) From c6a7a410eaf1a05339a7434bf7db2334d412e559 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 14 Aug 2024 08:13:36 -0400 Subject: [PATCH 3907/4033] cljs.cli: add docstrings to cljs.cli, use clearer names (#232) * add docstrings to cljs.cli, use clearer names --- src/main/clojure/cljs/cli.clj | 71 +++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index f404fac9a..da4f2761f 100644 --- a/src/main/clojure/cljs/cli.clj +++ b/src/main/clojure/cljs/cli.clj @@ -341,6 +341,8 @@ present" inits))))) (defn default-main + "Default handler for the --main flag. Will start REPL, invoke -main with the + supplied arguments." [repl-env {:keys [main script args repl-env-options options inits] :as cfg}] (let [opts (cond-> options (not (:output-dir options)) @@ -432,7 +434,9 @@ present" (defn- main-opt "Call the -main function from a namespace with string arguments from - the command line." + the command line. Can be customized with ::cljs.cli/main fn entry in + the map returned by cljs.repl/IReplEnvOptions. For default behavior + see default-main." [repl-env [_ ns & args] cfg] ((::main (repl/repl-options (repl-env)) default-main) repl-env (merge cfg {:main ns :args args}))) @@ -448,6 +452,10 @@ present" (println (help-str repl-env))) (defn- script-opt + "If no main option was given (compile, repl, main), handles running in + 'script' mode. Can be customized with ::cljs.cli/main fn entry in + the map returned by cljs.repl/IReplEnvOptions. For default behavior see + default-main." [repl-env [path & args] cfg] ((::main (repl/repl-options (repl-env)) default-main) repl-env (merge cfg {:script path :args args}))) @@ -538,17 +546,30 @@ present" (serve-opt repl-env args cfg))))) (defn- compile-opt + "Handle the compile flag. Custom compilation is possible by providing + :cljs.cli/compile fn in the map returned by cljs.repl/IReplEnvOptions. + For default behavior see default-compile." [repl-env [_ ns & args] cfg] ((::compile (repl/-repl-options (repl-env)) default-compile) repl-env (merge cfg {:args args :ns ns}))) -(defn get-options [commands k] - (if (= :all k) +(defn get-options + "Given a commands map and a phase (:init or :main), return all flags + which can be handled as a set. If phase is :all will return the entire + flag set (:init + :main)." + [commands phase] + (if (= :all phase) (into (get-options commands :main) (get-options commands :init)) - (-> (get commands (keyword (str (name k) "-dispatch"))) + (-> (get commands (keyword (str (name phase) "-dispatch"))) keys set))) -(defn bool-init-options [commands] +(defn get-flags-set + "See get-options, this just provides a better name." + [commands phase] + (get-options commands phase)) + +(defn bool-init-options + [commands] (reduce (fn [ret [flags config]] (cond-> ret @@ -556,20 +577,26 @@ present" (into flags))) #{} (:init commands))) -(defn dispatch? [commands k opt] - (contains? (get-options commands k) opt)) +(defn dispatch? + "Given a commands map, a phase (:init or :main) and a command line flag, + return true if the flag has a handler." + [commands phase opt] + (contains? (get-flags-set commands phase) opt)) (defn add-commands + "Given commands map (see below), create a commands map with :init-dispatch + and :main-dispatch keys where short and long arguments are mapped individually + to their processing fn." ([commands] (add-commands {:main-dispatch nil :init-dispatch nil} commands)) ([commands {:keys [groups main init]}] - (letfn [(merge-dispatch [st k options] - (update-in st [k] + (letfn [(merge-dispatch [commands dispatch-key options] + (update-in commands [dispatch-key] (fn [m] (reduce - (fn [ret [cs csm]] + (fn [ret [flag-names flag-config]] (merge ret - (zipmap cs (repeat (:fn csm))))) + (zipmap flag-names (repeat (:fn flag-config))))) m options))))] (-> commands (update-in [:groups] merge groups) @@ -578,7 +605,12 @@ present" (merge-dispatch :init-dispatch init) (merge-dispatch :main-dispatch main))))) -(def default-commands +(def ^{:doc "Default commands for ClojureScript REPLs. :groups are to support +printing organized output for --help. a :main option must come at the end, they +specify things like running a -main fn, compile, repl, or web serving. Sometimes +:main options can be used together (i.e. --compile --repl), but this is not +generic - the combinations must be explicitly supported"} + default-commands (add-commands {:groups {::main&compile {:desc "init options" :pseudos @@ -662,9 +694,14 @@ present" ["-h" "--help" "-?"] {:fn help-opt :doc "Print this help message and exit"}}})) -(defn normalize [commands args] +(defn normalize + "Given a commands map (flag + value -> option processor fn) and the sequence of + command line arguments passed to the process, normalize it. Boolean flags don't + need to specify anything, insert the implied trues and return the normalized + command line arguments." + [commands args] (letfn [(normalize* [args*] - (if (not (contains? (get-options commands :main) (first args*))) + (if (not (contains? (get-flags-set commands :main) (first args*))) (let [pred (complement (bool-init-options commands)) [pre post] ((juxt #(take-while pred %) #(drop-while pred %)) @@ -685,7 +722,11 @@ present" args' (recur args' (normalize* args')))))) -(defn merged-commands [repl-env] +(defn merged-commands + "Given a repl environment combine the default commands with the custom + REPL commands. Commands are a mapping from a command line argument + (flag + value) to a function to handle that particular flag + value." + [repl-env] (add-commands default-commands (::commands (repl/repl-options (repl-env))))) From d44c56b974b3efc25e7ef2c4c2e644144426e9f1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 28 Aug 2024 12:22:51 -0400 Subject: [PATCH 3908/4033] CLJS-3418: Some Closure libraries are not lowered because they declare (instead of just ) in deps.js for Google Closure Library (#233) Co-authored-by: yardwerkz <123331694+yardwerkz@users.noreply.github.com> --- src/main/clojure/cljs/closure.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index dffd923fc..58c9e0912 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -236,6 +236,7 @@ (def lang-level [:ecmascript3 :ecmascript5 :ecmascript5-strict :ecmascript6 :ecmascript6-strict + :ecmascript8 :ecmascript-2015 :ecmascript-2016 :ecmascript-2017 :ecmascript-2018 :ecmascript-2019 :ecmascript-2020 :ecmascript-2021 :ecmascript-next :no-transpile]) @@ -251,6 +252,7 @@ :ecmascript5-strict CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT :ecmascript6 CompilerOptions$LanguageMode/ECMASCRIPT_2015 ;; (deprecated and remapped) :ecmascript6-strict CompilerOptions$LanguageMode/ECMASCRIPT_2015 ;; (deprecated and remapped) + :ecmasscript8 CompilerOptions$LanguageMode/ECMASCRIPT_2017 :ecmascript-2015 CompilerOptions$LanguageMode/ECMASCRIPT_2015 :ecmascript-2016 CompilerOptions$LanguageMode/ECMASCRIPT_2016 :ecmascript-2017 CompilerOptions$LanguageMode/ECMASCRIPT_2017 From ae6ccbbe25b2690d85012d3742ed634d24eb1311 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Wed, 28 Aug 2024 18:30:18 -0400 Subject: [PATCH 3909/4033] * fix typo in last commit --- src/main/clojure/cljs/closure.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 58c9e0912..cf589b85c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -252,7 +252,7 @@ :ecmascript5-strict CompilerOptions$LanguageMode/ECMASCRIPT5_STRICT :ecmascript6 CompilerOptions$LanguageMode/ECMASCRIPT_2015 ;; (deprecated and remapped) :ecmascript6-strict CompilerOptions$LanguageMode/ECMASCRIPT_2015 ;; (deprecated and remapped) - :ecmasscript8 CompilerOptions$LanguageMode/ECMASCRIPT_2017 + :ecmascript8 CompilerOptions$LanguageMode/ECMASCRIPT_2017 :ecmascript-2015 CompilerOptions$LanguageMode/ECMASCRIPT_2015 :ecmascript-2016 CompilerOptions$LanguageMode/ECMASCRIPT_2016 :ecmascript-2017 CompilerOptions$LanguageMode/ECMASCRIPT_2017 From b7ede4bce3e273ab85155fdd8463c291a6f81d43 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 29 Aug 2024 13:32:53 -0400 Subject: [PATCH 3910/4033] CLJS-3419: JS Map & Set should return true for seqable? (#234) --- src/main/cljs/cljs/core.cljs | 9 +++++---- src/test/cljs/cljs/seqs_test.cljs | 8 +++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 27a2f9e37..73bd921f2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2307,10 +2307,11 @@ reduces them without incurring seq initialization" "Return true if the seq function is supported for s" [s] (or - (nil? s) - (satisfies? ISeqable s) - (array? s) - (string? s))) + (nil? s) + (satisfies? ISeqable s) + (js-iterable? s) + (array? s) + (string? s))) (defn boolean "Coerce to boolean" diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 3d7606da9..9e43a7340 100644 --- a/src/test/cljs/cljs/seqs_test.cljs +++ b/src/test/cljs/cljs/seqs_test.cljs @@ -443,7 +443,7 @@ (partition 5 [1 2 3]) () (partition 4 4 [0 0 0] (range 10)) '((0 1 2 3) (4 5 6 7) (8 9 0 0)) - + (partition -1 [1 2 3]) () (partition -2 [1 2 3]) ()) @@ -523,3 +523,9 @@ :initk initk)))] (= (into [] (src)) (into [] (seq (src))))))) + +(deftest cljs-3419-seq-js-iterable + (let [js-set (js/Set. #js [1 2 3 4]) + js-map (js/Map. #js [#js [1 2] #js [3 4]])] + (is (seqable? js-set)) + (is (seqable? js-map)))) From f1fc819da94147411e353ecb17c751af6a18ce2e Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Tue, 8 Oct 2024 14:40:18 +0200 Subject: [PATCH 3911/4033] CLJS-3421: Throw when calling ana-api/ns-publics on non-existing ns --- src/main/clojure/cljs/analyzer/api.cljc | 23 ++++++++++++++------ src/test/clojure/cljs/analyzer_api_tests.clj | 6 +++++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index cff4b97f4..2d143a42b 100644 --- a/src/main/clojure/cljs/analyzer/api.cljc +++ b/src/main/clojure/cljs/analyzer/api.cljc @@ -10,7 +10,7 @@ "This is intended to be a stable api for those who need programmatic access to the analyzer." (:refer-clojure :exclude [all-ns ns-interns ns-resolve resolve find-ns - ns-publics remove-ns]) + ns-publics remove-ns the-ns]) #?(:clj (:require [cljs.analyzer :as ana] [cljs.env :as env] [cljs.util :as util] @@ -227,6 +227,16 @@ {:pre [(symbol? sym)]} (get-in @state [::ana/namespaces sym]))) +(defn the-ns + "Given a namespace return the corresponding namespace analysis map, throwing an + exception if not found. Analagous to clojure.core/the-ns." + ([ns] + (the-ns env/*compiler* ns)) + ([state sym] + {:pre [(symbol? sym)]} + (or (find-ns state sym) + (throw (ex-info (str "No namespace found: " sym) {:ns sym}))))) + (defn ns-interns "Given a namespace return all the var analysis maps. Analagous to clojure.core/ns-interns but returns var analysis maps not vars." @@ -234,9 +244,10 @@ (ns-interns env/*compiler* ns)) ([state ns] {:pre [(symbol? ns)]} - (merge - (get-in @state [::ana/namespaces ns :macros]) - (get-in @state [::ana/namespaces ns :defs])))) + (let [ns (the-ns state ns)] + (merge + (:macros ns) + (:defs ns))))) (defn ns-publics "Given a namespace return all the public var analysis maps. Analagous to @@ -245,9 +256,7 @@ (ns-publics env/*compiler* ns)) ([state ns] {:pre [(symbol? ns)]} - (->> (merge - (get-in @state [::ana/namespaces ns :macros]) - (get-in @state [::ana/namespaces ns :defs])) + (->> (ns-interns state ns) (remove (fn [[k v]] (:private v))) (into {})))) diff --git a/src/test/clojure/cljs/analyzer_api_tests.clj b/src/test/clojure/cljs/analyzer_api_tests.clj index 831734c41..243281ff6 100644 --- a/src/test/clojure/cljs/analyzer_api_tests.clj +++ b/src/test/clojure/cljs/analyzer_api_tests.clj @@ -52,3 +52,9 @@ (is (= {:a 1} (ana-api/get-js-index state))) (ana-api/with-state state (is (= {:a 1} (ana-api/get-js-index)))))) + +(deftest throw-test + (let [state (atom {})] + (is (thrown? Exception (ana-api/the-ns state 'non.existing))) + (is (thrown? Exception (ana-api/ns-interns state 'non.existing))) + (is (thrown? Exception (ana-api/ns-publics state 'non.existing))))) From 2c5f442ce5b83cb9c46b10d5afb51b088f3ee7f1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 21 Oct 2024 10:27:45 -0400 Subject: [PATCH 3912/4033] CLJS-3240: lazy-seq does not cache result of calling seq (#237) CLJS-3240: lazy-seq does not cache result of calling seq - patch adapted from Ambrose Bonnaire-Sergeant --- src/main/cljs/cljs/core.cljs | 30 ++++++++++-------------- src/test/cljs/cljs/collections_test.cljs | 8 +++++++ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 73bd921f2..3d6ea2f1f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3514,7 +3514,10 @@ reduces them without incurring seq initialization" (if (nil? fn) s (do - (set! s (fn)) + (loop [ls (fn)] + (if (instance? LazySeq ls) + (recur (.sval ls)) + (set! s (seq ls)))) (set! fn nil) s))) (indexOf [coll x] @@ -3534,27 +3537,27 @@ reduces them without incurring seq initialization" (-with-meta [coll new-meta] (if (identical? new-meta meta) coll - (LazySeq. new-meta #(-seq coll) nil __hash))) + (LazySeq. new-meta #(.sval coll) nil __hash))) IMeta (-meta [coll] meta) ISeq (-first [coll] - (-seq coll) + (.sval coll) (when-not (nil? s) - (first s))) + (-first s))) (-rest [coll] - (-seq coll) + (.sval coll) (if-not (nil? s) - (rest s) + (-rest s) ())) INext (-next [coll] - (-seq coll) + (.sval coll) (when-not (nil? s) - (next s))) + (-next s))) ICollection (-conj [coll o] (cons o coll)) @@ -3570,14 +3573,7 @@ reduces them without incurring seq initialization" (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable - (-seq [coll] - (.sval coll) - (when-not (nil? s) - (loop [ls s] - (if (instance? LazySeq ls) - (recur (.sval ls)) - (do (set! s ls) - (seq s)))))) + (-seq [coll] (.sval coll)) IReduce (-reduce [coll f] (seq-reduce f coll)) @@ -7216,7 +7212,7 @@ reduces them without incurring seq initialization" extra-kvs (seq trailing) ret (make-array (+ seed-cnt (* 2 (count extra-kvs)))) ret (array-copy seed 0 ret 0 seed-cnt)] - (loop [i seed-cnt extra-kvs extra-kvs] + (loop [i seed-cnt extra-kvs extra-kvs]00 (if extra-kvs (let [kv (first extra-kvs)] (aset ret i (-key kv)) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index d66a87cda..04d89d4aa 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -1151,6 +1151,14 @@ (deftest test-cljs-3393 (is (= '(0 2 4) (take 3 (filter even? (range 100000000)))))) +(deftest test-cljs-3420-lazy-seq-caching-bug + (testing "LazySeq should realize seq once" + (let [a (atom 0) + x (eduction (map (fn [_] (swap! a inc))) [nil]) + l (lazy-seq x)] + (dotimes [_ 10] + (is (= [1] l)))))) + (comment (run-tests) From 79414ff31b87ae9b4475338e97ac78d640750518 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Mon, 21 Oct 2024 22:46:27 -0400 Subject: [PATCH 3913/4033] fix typo in last commit --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3d6ea2f1f..dcdf23c31 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7212,7 +7212,7 @@ reduces them without incurring seq initialization" extra-kvs (seq trailing) ret (make-array (+ seed-cnt (* 2 (count extra-kvs)))) ret (array-copy seed 0 ret 0 seed-cnt)] - (loop [i seed-cnt extra-kvs extra-kvs]00 + (loop [i seed-cnt extra-kvs extra-kvs] (if extra-kvs (let [kv (first extra-kvs)] (aset ret i (-key kv)) From 9696bac5bd47abbc8741631989448db48ee017db Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 21 Nov 2024 14:19:49 -0800 Subject: [PATCH 3914/4033] Add specs to analyzer, add spec based tests (#238) * clojure.spec specs for the AST based on the AST reference * unit tests for all the AST node types + spec assertions * fix minor cases where the AST diverges from the AST reference --- deps.edn | 3 +- src/main/clojure/cljs/analyzer.cljc | 26 +- src/test/clojure/cljs/analyzer/spec_tests.clj | 288 +++++++++++++++ src/test/clojure/cljs/analyzer/specs.cljc | 328 ++++++++++++++++++ 4 files changed, 637 insertions(+), 8 deletions(-) create mode 100644 src/test/clojure/cljs/analyzer/spec_tests.clj create mode 100644 src/test/clojure/cljs/analyzer/specs.cljc diff --git a/deps.edn b/deps.edn index 8925f7e20..9a8f42b00 100644 --- a/deps.edn +++ b/deps.edn @@ -13,7 +13,8 @@ :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" - "src/test/clojure" "src/test/self"]} + "src/test/clojure" "src/test/self"] + :extra-deps {org.clojure/spec.alpha {:mvn/version "0.5.238"}}} :compiler.test.run {:main-opts ["-i" "src/test/clojure/cljs/test_runner.clj" "-e" "(cljs.test-runner/-main)"]} :runtime.test.build {:extra-paths ["src/test/cljs"] diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 376956d8e..887ae349a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1880,7 +1880,12 @@ (assoc locals e {:name e :line (get-line e env) - :column (get-col e env)}) + :column (get-col e env) + ;; :local is required for {:op :local ...} nodes + ;; but previously we had no way to figure this out + ;; for `catch` locals, by adding it here we can recover + ;; it later + :local :catch}) locals) catch (when cblock (disallowing-recur (analyze (assoc catchenv :locals locals) cblock))) @@ -2143,6 +2148,7 @@ {:line line :column column}) param {:op :binding :name name + :form name :line line :column column :tag tag @@ -2205,8 +2211,10 @@ shadow (or (handle-symbol-local name (get locals name)) (get-in env [:js-globals name])) fn-scope (:fn-scope env) - name-var {:name name - :op :binding + name-var {:op :binding + :env env + :form name + :name name :local :fn :info {:fn-self-name true :fn-scope fn-scope @@ -2326,8 +2334,10 @@ (let [ret-tag (-> n meta :tag) fexpr (no-warn (analyze env (n->fexpr n))) be (cond-> - {:name n - :op :binding + {:op :binding + :name n + :form n + :env env :fn-var true :line (get-line n env) :column (get-col n env) @@ -2416,7 +2426,9 @@ col (get-col name env) shadow (or (handle-symbol-local name (get-in env [:locals name])) (get-in env [:js-globals name])) - be {:name name + be {:op :binding + :name name + :form name :line line :column col :init init-expr @@ -2425,7 +2437,6 @@ :shadow shadow ;; Give let* bindings same shape as var so ;; they get routed correctly in the compiler - :op :binding :env {:line line :column col} :info {:name name :shadow shadow} @@ -2565,6 +2576,7 @@ (throw (error env "Wrong number of args to quote"))) (let [expr (analyze-const env x)] {:op :quote + :literal? true :expr expr :env env :form form diff --git a/src/test/clojure/cljs/analyzer/spec_tests.clj b/src/test/clojure/cljs/analyzer/spec_tests.clj new file mode 100644 index 000000000..57134c703 --- /dev/null +++ b/src/test/clojure/cljs/analyzer/spec_tests.clj @@ -0,0 +1,288 @@ +;; 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.spec-tests + (:require [cljs.analyzer :as ana] + [cljs.analyzer.api :as ana-api :refer [no-warn]] + [cljs.compiler.api :as comp-api] + [cljs.analyzer-tests :refer [analyze ns-env]] + [cljs.analyzer.specs :as a] + [clojure.test :as test :refer [deftest is]] + [clojure.spec.alpha :as s]) + (:import [java.io StringReader])) + +(deftest test-binding + (let [node (analyze ns-env '(let [x 1] x)) + binding (-> node :bindings first)] + (is (= :binding (:op binding))) + (is (s/valid? ::a/node binding)))) + +(deftest test-case + (let [let-node (no-warn (analyze ns-env '(case x 1 :foo 2 :bar))) + node (-> let-node :body :ret)] + (is (= :case (:op node))) + (is (s/valid? ::a/node node)) + (let [nodes (-> node :nodes) + case-node (first nodes)] + (is (= :case-node (:op case-node))) + (is (s/valid? ::a/node case-node)) + (let [case-tests (:tests case-node) + case-test (first case-tests) + case-then (:then case-node)] + (is (= :case-test (:op case-test))) + (is (s/valid? ::a/node case-test)) + (is (= :case-then (:op case-then))) + (is (s/valid? ::a/node case-then)))))) + +(deftest test-const + (is (s/valid? ::a/node (analyze ns-env 1))) + (is (s/valid? ::a/node (analyze ns-env 1.2))) + (is (s/valid? ::a/node (analyze ns-env true))) + (is (s/valid? ::a/node (analyze ns-env "foo"))) + (let [node (analyze ns-env [])] + (is (= :vector (:op node))) + (is (s/valid? ::a/node node))) + (is (s/valid? ::a/node (analyze ns-env [1 2 3]))) + (is (s/valid? ::a/node (analyze ns-env {}))) + (let [node (analyze ns-env {1 2 3 4})] + (is (= :map (:op node))) + (is (s/valid? ::a/node node))) + (is (s/valid? ::a/node (analyze ns-env #{}))) + (let [node (analyze ns-env #{1 2 3})] + (is (= :set (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-def + (let [node (no-warn (analyze ns-env '(def x)))] + (is (= :def (:op node))) + (is (s/valid? ::a/node node))) + (is (s/valid? ::a/node (analyze ns-env '(def x 1)))) + (is (s/valid? ::a/node (analyze ns-env '(def x (fn []))))) + (is (s/valid? ::a/node (analyze ns-env '(def x (fn [y] y)))))) + +(deftest test-defn + (is (s/valid? ::a/node (analyze ns-env '(defn x [])))) + (is (s/valid? ::a/node (analyze ns-env '(defn x [] 1)))) + (is (s/valid? ::a/node (analyze ns-env '(defn x [y] y))))) + +(deftest test-defrecord + (let [node (no-warn (analyze ns-env '(defrecord A []))) + body (:body node)] + (is (= :defrecord (-> body :statements first :ret :op))) + (is (s/valid? ::a/node node)))) + +(deftest test-deftype + (let [node (no-warn (analyze ns-env '(deftype A [])))] + (is (= :deftype (-> node :statements first :op))) + (is (s/valid? ::a/node node)))) + +(deftest test-do + (let [node (analyze ns-env '(do))] + (is (= :do (:op node))) + (is (s/valid? ::a/node node))) + (is (s/valid? ::a/node (analyze ns-env '(do 1)))) + (is (s/valid? ::a/node (analyze ns-env '(do 1 2 3))))) + +(deftest test-fn + (let [node (no-warn (analyze ns-env '(fn [])))] + (is (= :fn (:op node))) + (is (s/valid? ::a/node node))) + (is (s/valid? ::a/node (analyze ns-env '(fn [] 1)))) + (is (s/valid? ::a/node (analyze ns-env '(fn [x])))) + (is (s/valid? ::a/node (analyze ns-env '(fn [x] 1))))) + +(deftest test-fn-method + (let [node (analyze ns-env '(fn ([]) ([x] x))) + methods (:methods node) + fn0 (first methods) + fn1 (second methods)] + (is (= :fn-method (:op fn0))) + (is (s/valid? ::a/node fn0)) + (is (= :fn-method (:op fn1))) + (is (s/valid? ::a/node fn1)))) + +(deftest test-host-call + (let [node (analyze ns-env '(.substring "foo" 0 1))] + (is (= :host-call (:op node))) + (is (s/valid? ::a/node node))) + (let [node (analyze ns-env '(. "foo" (substring 0 1)))] + (is (= :host-call (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-host-field + (let [node (analyze ns-env '(.-length "foo"))] + (is (= :host-field (:op node))) + (is (s/valid? ::a/node node))) + (let [node (analyze ns-env '(. "foo" -length))] + (is (= :host-field (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-if + (let [node (analyze ns-env '(if true true))] + (is (= :if (:op node))) + (is (s/valid? ::a/node node))) + (is (s/valid? ::a/node (analyze ns-env '(if true true false))))) + +(deftest test-invoke + (let [node (no-warn (analyze ns-env '(count "foo")))] + (is (= :invoke (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-js + (let [node (analyze ns-env '(js* "~{}" 1))] + (is (= :js (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-js-array + (let [node (analyze ns-env + (ana-api/with-state (ana-api/empty-state) + (first (ana-api/forms-seq (StringReader. "#js [1 2 3]")))))] + (is (= :js-array (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-js-object + (let [node (analyze ns-env + (ana-api/with-state (ana-api/empty-state) + (first (ana-api/forms-seq (StringReader. "#js {:foo 1 :bar 2}")))))] + (is (= :js-object (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-js-var + (let [node (analyze ns-env 'js/String)] + (is (= :js-var (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-let + (let [node (analyze ns-env '(let []))] + (is (= :let (:op node))) + (is (s/valid? ::a/node node))) + (is (s/valid? ::a/node (analyze ns-env '(let [x 1])))) + (is (s/valid? ::a/node (analyze ns-env '(let [x 1] x))))) + +(deftest test-letfn + (let [node (analyze ns-env '(letfn [(foo [] (bar)) (bar [] (foo))]))] + (is (= :letfn (:op node))) + (is (s/valid? ::a/node node)))) + +;; list, no longer needed, subsumed by :quote + +(deftest test-local + (let [node (analyze ns-env '(fn [x] x)) + fn-method (-> node :methods first) + body (-> fn-method :body) + ret (:ret body)] + (is (= :local (:op ret))) + (is (s/valid? ::a/node node)))) + +(deftest test-loop + (let [node (analyze ns-env '(loop []))] + (is (= :loop (:op node))) + (is (s/valid? ::a/node node))) + (let [node (analyze ns-env '(loop [x 1] x))] + (is (s/valid? ::a/node node))) + (let [node (analyze ns-env '(loop [x 1] (recur (inc x))))] + (is (s/valid? ::a/node node))) + (let [node (no-warn + (analyze ns-env + '(loop [x 100] + (if (pos? x) + (recur (dec x)) + x))))] + (is (s/valid? ::a/node node)))) + +(deftest test-map + (let [node (no-warn (analyze ns-env '{:foo 1 :bar 2}))] + (is (= :map (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-new + (let [node (no-warn (analyze ns-env '(new String)))] + (is (= :new (:op node))) + (is (s/valid? ::a/node node))) + (is (s/valid? ::a/node (analyze ns-env '(new js/String)))) + (is (s/valid? ::a/node (no-warn (analyze ns-env '(String.))))) + (is (s/valid? ::a/node (analyze ns-env '(js/String.))))) + +(deftest test-no-op + (let [node (binding [ana/*unchecked-if* true] + (no-warn (analyze ns-env '(set! *unchecked-if* false))))] + (is (= :no-op (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-ns + (let [node (no-warn + (binding [ana/*cljs-ns* 'cljs.user] + (analyze ns-env '(ns foo (:require [goog.string])))))] + (is (= :ns (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-ns* + (let [node (no-warn + (binding [ana/*cljs-ns* 'cljs.user] + (analyze ns-env '(ns* (:require '[goog.string])))))] + (is (= :ns* (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-quote + (let [node (analyze ns-env ''(1 2 3))] + (is (= :quote (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-recur + (let [node (no-warn (analyze ns-env '(fn [x] (recur (inc x)))))] + (is (s/valid? ::a/node node)))) + +(deftest test-set + (let [node (analyze ns-env #{1 2 3})] + (is (= :set (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-set! + (let [node (no-warn (analyze ns-env '(set! x 1)))] + (is (= :set! (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-the-var + (let [node (comp-api/with-core-cljs {} + #(analyze ns-env '(var first)))] + (is (= :the-var (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-throw + (let [node (no-warn (analyze ns-env '(throw (js/Error. "foo"))))] + (is (= :throw (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-try + (let [node (no-warn (analyze ns-env '(try 1 (catch :default e) (finally))))] + (is (= :try (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-var + (let [node (no-warn (analyze ns-env '(fn [] x))) + fn-method (-> node :methods first) + body (-> fn-method :body) + ret (:ret body)] + (is (= :var (:op ret))) + (is (s/valid? ::a/node node)))) + +(deftest test-vector + (let [node (no-warn (analyze ns-env '[1 2]))] + (is (= :vector (:op node))) + (is (s/valid? ::a/node node)))) + +(deftest test-with-meta + (let [node (analyze ns-env ^{:meta 2} {:foo 1})] + (is (= :with-meta (:op node))) + (is (s/valid? ::a/node node)))) + +(comment + + (test/run-tests) + + ) diff --git a/src/test/clojure/cljs/analyzer/specs.cljc b/src/test/clojure/cljs/analyzer/specs.cljc new file mode 100644 index 000000000..ec5079bf9 --- /dev/null +++ b/src/test/clojure/cljs/analyzer/specs.cljc @@ -0,0 +1,328 @@ +;; 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.specs + (:require [clojure.spec.alpha :as s])) + +(s/def ::op keyword?) +(s/def ::form any?) +(s/def ::env map?) +(s/def ::context #{:expr :return :statement}) + +(defmulti node :op) +(s/def ::node (s/multi-spec node :op)) + +(s/def ::test ::node) +(s/def ::then ::node) +(s/def ::else ::node) + +;; TODO: :tag +(s/def ::base + (s/keys + :req-un [::op ::env ::form])) + +(s/def ::name symbol?) +(s/def :cljs.analyzer.specs.binding/local + #{:arg :catch :fn :let :letfn :loop :field}) +(s/def ::variadic? boolean?) +(s/def ::init ::node) +(s/def ::shadow + (s/or :nil nil? + :node ::node)) + +(defmethod node :binding [_] + (s/merge + ::base + (s/keys + :req-un [::name :cljs.analyzer.specs.binding/local] + :opt-un [::variadic? ::init ::shadow]))) + +(s/def ::nodes (s/* ::node)) +(s/def ::default ::node) + +(defmethod node :case [_] + (s/merge ::base + (s/keys + :req-un [::test ::nodes ::default]))) + +(defmethod node :case-node [_] + (s/keys + :req-un [::op ::env ::tests ::then])) + +(defmethod node :case-test [_] + (s/merge ::base + (s/keys + :req-un [::test]))) + +(defmethod node :case-then [_] + (s/merge ::base + (s/keys + :req-un [::then]))) + +(s/def ::literal? boolean?) +(s/def ::val any?) + +(defmethod node :const [_] + (s/merge ::base + (s/keys + :req-un [::val] + ;; ::literal? is required in the AST REF, but we don't actually use it + ;; should check tools.analyzer + :opt-un [::literal?]))) + +(defmethod node :def [_] + (s/merge ::base + (s/keys + :req-un [::name] + :opt-un [::init ::the-var]))) + +(s/def ::body ::node) +(s/def ::t symbol?) + +(defmethod node :defrecord [_] + (s/merge ::base + (s/keys + :req-un [::t ::body]))) + +(defmethod node :deftype [_] + (s/merge ::base + (s/keys + :req-un [::t ::body]))) + +(s/def ::statements (s/* ::node)) +(s/def ::ret ::node) +(s/def ::body? boolean?) + +(defmethod node :do [_] + (s/merge ::base + (s/keys + :req-un [::statements ::ret] + :opt-un [::body?]))) + +(s/def ::local ::node) +(s/def ::max-fixed-arity int?) +(s/def ::methods (s/+ ::node)) + +(defmethod node :fn [_] + (s/merge ::base + (s/keys + :req-un [::variadic? ::max-fixed-arity ::methods] + :opt-un [::local]))) + +(s/def ::fixed-arity int?) +(s/def ::params (s/* ::node)) + +(defmethod node :fn-method [_] + (s/merge ::base + (s/keys + :req-un [::fixed-arity ::params ::body]))) + +(s/def ::method symbol?) +(s/def ::target ::node) +(s/def ::args (s/* ::node)) + +(defmethod node :host-call [_] + (s/merge ::base + (s/keys + :req-un [::method ::target ::args]))) + +(s/def ::field symbol?) + +(defmethod node :host-field [_] + (s/merge ::base + (s/keys + :req-un [::field ::target]))) + +(defmethod node :if [_] + (s/merge ::base + (s/keys + :req-un [::test ::then] + :opt-un [::else]))) + +(s/def ::fn ::node) + +(defmethod node :invoke [_] + (s/merge ::base + (s/keys + :req-un [::fn ::args]))) + +(s/def ::code string?) + +(defmethod node :js [_] + (s/merge ::base + (s/keys + :opt-un [::code]))) + +(defmethod node :js-array [_] + (s/merge ::base + (s/keys + :req-un [::items]))) + +(defmethod node :js-object [_] + (s/merge ::base + (s/keys + :req-un [::vals]))) + +(s/def ::ns symbol?) + +(defmethod node :js-var [_] + (s/merge ::base + (s/keys + :req-un [::ns ::name]))) + +(s/def ::bindings (s/* ::node)) + +(defmethod node :let [_] + (s/merge ::base + (s/keys + :req-un [::bindings ::body]))) + +(defmethod node :letfn [_] + (s/merge ::base + (s/keys + :req-un [::bindings ::body]))) + +(s/def ::items (s/* ::node)) + +;; TODO: not in ast-ref +(defmethod node :list [_] + (s/merge ::base + (s/keys + :req-un [::items]))) + +(defmethod node :local [_] + (s/merge ::base + (s/keys + :req-un [:cljs.analyzer.specs.binding/local ::name]))) + +(defmethod node :loop [_] + (s/merge ::base + (s/keys + :req-un [::bindings ::body]))) + +(s/def ::vals (s/* ::node)) + +(defmethod node :map [_] + (s/merge ::base + (s/keys :req-un [::keys ::vals]))) + +(s/def ::class ::node) + +(defmethod node :new [_] + (s/merge ::base + (s/keys + :req-un [::class ::args]))) + +(defmethod node :no-op [_] + (s/keys + :req-un [::env ::op])) + +(defmethod node :ns [_] + ::base) + +(defmethod node :ns* [_] + ::base) + +(s/def ::expr ::node) + +(defmethod node :quote [_] + (s/merge ::base + (s/keys + :req-un [::expr ::literal?]))) + +(s/def ::exprs (s/* ::node)) + +(defmethod node :recur [_] + (s/merge ::base + (s/keys + :req-un [::exprs]))) + +(defmethod node :set [_] + (s/merge ::base + (s/keys + :req-un [::items]))) + +(defmethod node :set! [_] + (s/merge ::base + (s/keys + :req-un [::target ::val]))) + +(s/def ::var ::node) +(s/def ::sym ::node) +(s/def ::meta map?) + +(defmethod node :the-var [_] + (s/merge ::base + (s/keys + :opt-un [::var ::sym ::meta]))) + +(s/def ::the-var ::node) + +(s/def ::exception ::node) + +(defmethod node :throw [_] + (s/merge ::base + (s/keys + :req-un [::exception]))) + +(s/def ::catch ::node) +(s/def ::finally ::node) + +(defmethod node :try [_] + (s/merge ::base + (s/keys + :req-un [::body ::catch ::name ::finally]))) + +(defmethod node :var [_] + (s/merge ::base + (s/keys + :req-un [::ns ::name]))) + +(s/def ::meta ::node) + +(defmethod node :vector [_] + (s/merge ::base + (s/keys + :req-un [::items]))) + +(defmethod node :with-meta [_] + (s/merge ::base + (s/keys + :req-un [::meta ::expr]))) + +(comment + + (s/valid? ::node 1) + (s/valid? ::node + {:op :const + :env {} + :form 1 + :literal? true + :val 1}) + + (s/explain-data ::node + {:op :if + :env {} + :form '(if true true false) + :test {:op :const + :env {} + :form true + :literal? true + :val true} + :then {:op :const + :env {} + :form true + :literal? true + :val true} + :else {:op :const + :env 1 + :form false + :literal? true + :val false}}) + + ) From b3a6f4f9b9d871edbb37e58994145cf41d84224a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 29 Nov 2024 08:20:20 -0500 Subject: [PATCH 3915/4033] Remove deprecated Closure externs pattern (#239) * remove deprecated Closure externs pattern --- src/main/clojure/cljs/closure.clj | 5 +++-- src/main/clojure/cljs/externs.clj | 16 ++++++++++------ src/test/clojure/cljs/externs_parsing_tests.clj | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index cf589b85c..28faeb04d 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -8,7 +8,8 @@ (ns cljs.closure (:refer-clojure :exclude [compile]) - (:require [cljs.util :as util :refer [distinct-by]] + (:require [cljs.externs :as externs] + [cljs.util :as util :refer [distinct-by]] [cljs.core :as cljsm] [cljs.compiler :as comp] [cljs.analyzer :as ana] @@ -402,7 +403,7 @@ (cond-> (if use-only-custom-externs all-sources - (into all-sources (CommandLineRunner/getDefaultExterns))) + (into all-sources (externs/default-externs))) infer-externs (conj (js-source-file nil (io/file (util/output-directory opts) "inferred_externs.js"))))))) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 5da818a72..d86aa91ef 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -12,7 +12,7 @@ [clojure.java.io :as io] [clojure.string :as string]) (:import [com.google.javascript.jscomp - CompilerOptions SourceFile JsAst CommandLineRunner] + CompilerOptions CompilerOptions$Environment SourceFile JsAst CommandLineRunner] [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino Node Token JSTypeExpression JSDocInfo$Visibility] @@ -23,6 +23,9 @@ (def ^:dynamic *source-file* nil) (def ^:dynamic *goog-ns* nil) +(defn default-externs [] + (CommandLineRunner/getBuiltinExterns CompilerOptions$Environment/BROWSER)) + ;; ------------------------------------------------------------------------------ ;; Externs Parsing @@ -207,6 +210,7 @@ (.init (list source-file) '() compiler-options)) js-ast (JsAst. source-file) ^Node root (.getAstRoot js-ast closure-compiler) + ;; TODO: switch to getFirstChild + getNext in the loop nodes (.children root)] (loop [nodes (cond-> nodes ;; handle goog.modules which won't have top-levels @@ -229,7 +233,7 @@ (defn externs-map* ([] - (externs-map* (CommandLineRunner/getDefaultExterns))) + (externs-map* (default-externs))) ([sources] (externs-map* sources '{eval {} @@ -242,7 +246,7 @@ ([sources defaults] (let [sources (if-not (empty? sources) sources - (CommandLineRunner/getDefaultExterns))] + (default-externs))] (reduce (fn [externs externs-file] (util/map-merge @@ -376,13 +380,13 @@ (fn [s] (let [m (-> s parse-externs index-externs)] (get-in m '[Window prototype console]))) - (CommandLineRunner/getDefaultExterns)) + (default-externs)) (-> (filter (fn [s] (= "externs.zip//webkit_dom.js" (.getName s))) - (CommandLineRunner/getDefaultExterns)) + (default-externs)) first parse-externs index-externs (find 'console) first meta) @@ -390,7 +394,7 @@ (filter (fn [s] (= "externs.zip//webkit_dom.js" (.getName s))) - (CommandLineRunner/getDefaultExterns)) + (default-externs)) first parse-externs index-externs (get-in '[Console prototype]) (find 'log) first meta) diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index c2b199cf1..effad773d 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -40,7 +40,7 @@ (filter (fn [s] (= "externs.zip//w3c_dom2.js" (.getName s))) - (CommandLineRunner/getDefaultExterns)) + (externs/default-externs)) first externs/parse-externs externs/index-externs (find 'HTMLDocument) first meta)] (is (= 'Document (:super info))))) From 7f7a986be91836046f8d7b29d6ee75b71a8a3cb4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 2 Dec 2024 08:02:44 -0500 Subject: [PATCH 3916/4033] cljs-2292 refer-clojure rename should also exclude (#240) Co-authored-by: Enzzo Cavallo --- src/main/clojure/cljs/analyzer.cljc | 6 +++++- src/test/cljs/cljs/ns_test.cljs | 4 ++++ src/test/clojure/cljs/analyzer_tests.clj | 10 +++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 887ae349a..8c61c4586 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2907,7 +2907,7 @@ (parse-ns-error-msg spec "Each of :as and :refer options may only be specified once in :require / :require-macros")))))) -(defn parse-ns-excludes [env args] +(defn- parse-ns-excludes-impl [env args] (reduce (fn [s [k & filters]] (if (= k :refer-clojure) @@ -2947,6 +2947,10 @@ {:excludes #{} :renames {}} args)) +(defn parse-ns-excludes [env args] + (let [s (parse-ns-excludes-impl env args)] + (update s :excludes into (keys (:renames s))))) + (defn use->require [env [lib & filters :as spec]] (when-not (and (symbol? lib) (odd? (count spec))) (throw diff --git a/src/test/cljs/cljs/ns_test.cljs b/src/test/cljs/cljs/ns_test.cljs index 2cb85865c..7243eae0a 100644 --- a/src/test/cljs/cljs/ns_test.cljs +++ b/src/test/cljs/cljs/ns_test.cljs @@ -48,3 +48,7 @@ (deftest test-cljs-3399 (is (= ::fake/foo :fake.ns/foo) (is (= `fake/foo 'fake.ns/foo)))) + +(deftest test-cljs-2292 + (is (= false (exists? mapv))) + (is (= true (exists? core-mapv)))) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index e96b6ae3f..f1b639938 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -338,7 +338,7 @@ '(ns foo.core (:refer-clojure :rename {when always map core-map}))))] - (is (= (-> parsed-ns :excludes) #{})) + (is (= (-> parsed-ns :excludes) '#{when map})) (is (= (-> parsed-ns :rename-macros) '{always cljs.core/when})) (is (= (-> parsed-ns :renames) '{core-map cljs.core/map}))) (is (thrown? Exception (env/with-compiler-env test-cenv @@ -379,6 +379,14 @@ :renames {}})) (is (set? (:excludes parsed))))) + +(deftest test-cljs-2292 + (let [parsed (ana/parse-ns-excludes {} '((:refer-clojure :rename {map clj-map})))] + (is (= parsed + '{:excludes #{map} + :renames {map clj-map}})) + (is (set? (:excludes parsed))))) + (deftest test-cljs-1785-js-shadowed-by-local (let [ws (atom [])] (ana/with-warning-handlers [(collecting-warning-handler ws)] From b1a891e96e2f1d4843d9a09f0e9d5917a57775b6 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 7 Jan 2025 21:06:59 -0500 Subject: [PATCH 3917/4033] * seed-cnt -> i --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index dcdf23c31..3cc6dd58a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7217,7 +7217,7 @@ reduces them without incurring seq initialization" (let [kv (first extra-kvs)] (aset ret i (-key kv)) (aset ret (inc i) (-val kv)) - (recur (+ 2 seed-cnt) (next extra-kvs))) + (recur (+ 2 i) (next extra-kvs))) ret)))) (set! (.-createAsIfByAssoc PersistentArrayMap) From 1cc0f1ee57f04962314238346dc8c7c4115af87d Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 7 Jan 2025 21:10:18 -0500 Subject: [PATCH 3918/4033] * test case --- src/test/cljs/cljs/core_test.cljs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 08581d603..94dda64f8 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2042,3 +2042,17 @@ (is (= 11 @c)) (is (= [11 0] (reset-vals! c 0))) (is (= 0 @c))))) + +(defn test-keys [& {:as opts, :keys [a b]}] + [a b opts]) + +(deftest test-cljs-3299-trailing-keys + (testing "verify proper handling of trailing keys" + (is (= (test-keys :a 1, :b 2) + [1 2 {:a 1, :b 2}])) + (is (= (test-keys {:a 1, :b 2}) + [1 2 {:a 1, :b 2}])) + (is (= (test-keys {:a 1, :b 2, :c 3}) + [1 2 {:a 1, :b 2, :c 3}])) + (is (= (test-keys :d 4 {:a 1, :b 2, :c 3}) + [1 nil {:d 4, :a 1, :c 3, nil nil}])))) From f42b15013f08963698e47a18a5598a80f35c0ce7 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 7 Jan 2025 21:12:04 -0500 Subject: [PATCH 3919/4033] * seed-cnt -> i --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index dcdf23c31..3cc6dd58a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7217,7 +7217,7 @@ reduces them without incurring seq initialization" (let [kv (first extra-kvs)] (aset ret i (-key kv)) (aset ret (inc i) (-val kv)) - (recur (+ 2 seed-cnt) (next extra-kvs))) + (recur (+ 2 i) (next extra-kvs))) ret)))) (set! (.-createAsIfByAssoc PersistentArrayMap) From 4dbe5faa1e1ddca48fae4b702f2de7b17d1b626e Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 7 Jan 2025 21:27:01 -0500 Subject: [PATCH 3920/4033] * macos-12 -> macos-14 --- .github/workflows/test.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f6c1a86ea..4263ab9f3 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-12 + runs-on: macos-14 steps: - uses: actions/checkout@v2 @@ -88,7 +88,7 @@ jobs: # Self-host Tests self-host-test: name: Self-host Tests - runs-on: macos-12 + runs-on: macos-14 steps: - uses: actions/checkout@v2 @@ -132,7 +132,7 @@ jobs: # Self-parity Tests self-parity-test: name: Self-parity Tests - runs-on: macos-12 + runs-on: macos-14 steps: - uses: actions/checkout@v2 @@ -176,7 +176,7 @@ jobs: # Compiler Tests compiler-test: name: Compiler Tests - runs-on: macos-12 + runs-on: macos-14 steps: - uses: actions/checkout@v2 @@ -235,7 +235,7 @@ jobs: # CLI Tests cli-test: name: CLI Tests - runs-on: macos-12 + runs-on: macos-14 steps: - uses: actions/checkout@v2 with: From 39dc80a9588a9de237225f82c5db31d3f9442387 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 7 Jan 2025 21:36:03 -0500 Subject: [PATCH 3921/4033] * revert CLJS-3299 changes --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3cc6dd58a..dcdf23c31 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7217,7 +7217,7 @@ reduces them without incurring seq initialization" (let [kv (first extra-kvs)] (aset ret i (-key kv)) (aset ret (inc i) (-val kv)) - (recur (+ 2 i) (next extra-kvs))) + (recur (+ 2 seed-cnt) (next extra-kvs))) ret)))) (set! (.-createAsIfByAssoc PersistentArrayMap) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 94dda64f8..330c18e7e 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2043,10 +2043,10 @@ (is (= [11 0] (reset-vals! c 0))) (is (= 0 @c))))) -(defn test-keys [& {:as opts, :keys [a b]}] +#_(defn test-keys [& {:as opts, :keys [a b]}] [a b opts]) -(deftest test-cljs-3299-trailing-keys +#_(deftest test-cljs-3299-trailing-keys (testing "verify proper handling of trailing keys" (is (= (test-keys :a 1, :b 2) [1 2 {:a 1, :b 2}])) From e78c6cdb47724b7b3fa0dee8d8a1dca060cba85c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 8 Jan 2025 08:18:56 -0500 Subject: [PATCH 3922/4033] use installed JSC (#242) --- .github/workflows/test.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4263ab9f3..24cf44527 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -38,25 +38,25 @@ jobs: restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - - name: Cache JSC - uses: actions/cache@v2 - env: - cache-name: cache-jsc - with: - path: WebKit - key: ${{ runner.os }}-jsc - restore-keys: | - ${{ runner.os }}-jsc + # - name: Cache JSC + # uses: actions/cache@v2 + # 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: Install JSC + # run: ./ci/install_jsc.sh - name: Run tests run: | - WebKit/WebKitBuild/Release/bin/jsc builds/out-adv/core-advanced-test.js | tee test-out.txt + /System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Helpers/jsc builds/out-adv/core-advanced-test.js | tee test-out.txt grep -qxF '0 failures, 0 errors.' test-out.txt # Runtime Tests From fc8522e12036e4e72feff17b3bc73dbdf84f69eb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 8 Jan 2025 09:26:53 -0500 Subject: [PATCH 3923/4033] CLJS-3242: trailing keys bug (#243) --- src/main/cljs/cljs/core.cljs | 2 +- src/test/cljs/cljs/core_test.cljs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index dcdf23c31..3cc6dd58a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -7217,7 +7217,7 @@ reduces them without incurring seq initialization" (let [kv (first extra-kvs)] (aset ret i (-key kv)) (aset ret (inc i) (-val kv)) - (recur (+ 2 seed-cnt) (next extra-kvs))) + (recur (+ 2 i) (next extra-kvs))) ret)))) (set! (.-createAsIfByAssoc PersistentArrayMap) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 330c18e7e..9c4a62528 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2043,10 +2043,10 @@ (is (= [11 0] (reset-vals! c 0))) (is (= 0 @c))))) -#_(defn test-keys [& {:as opts, :keys [a b]}] +(defn test-keys [& {:as opts, :keys [a b]}] [a b opts]) -#_(deftest test-cljs-3299-trailing-keys +(deftest test-cljs-3299-trailing-keys (testing "verify proper handling of trailing keys" (is (= (test-keys :a 1, :b 2) [1 2 {:a 1, :b 2}])) @@ -2055,4 +2055,4 @@ (is (= (test-keys {:a 1, :b 2, :c 3}) [1 2 {:a 1, :b 2, :c 3}])) (is (= (test-keys :d 4 {:a 1, :b 2, :c 3}) - [1 nil {:d 4, :a 1, :c 3, nil nil}])))) + [1 2 {:d 4, :a 1, :b 2, :c 3}])))) From 6d4bee65ac216205a6bb6bb6a3d878cdacc193f6 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 3 Mar 2025 11:39:05 -0500 Subject: [PATCH 3924/4033] bump to cache/v4 --- .github/workflows/test.yaml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 24cf44527..22070f16a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -19,7 +19,7 @@ jobs: tools-deps: '1.10.1.763' - name: Cache maven - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-maven with: @@ -29,7 +29,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Cache gitlibs - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-gitlibs with: @@ -39,7 +39,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- # - name: Cache JSC - # uses: actions/cache@v2 + # uses: actions/cache@v4 # env: # cache-name: cache-jsc # with: @@ -102,7 +102,7 @@ jobs: tools-deps: '1.10.1.763' - name: Cache maven - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-maven with: @@ -112,7 +112,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Cache gitlibs - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-gitlibs with: @@ -146,7 +146,7 @@ jobs: tools-deps: '1.10.1.763' - name: Cache maven - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-maven with: @@ -156,7 +156,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Cache gitlibs - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-gitlibs with: @@ -190,7 +190,7 @@ jobs: tools-deps: '1.10.1.763' - name: Cache maven - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-maven with: @@ -200,7 +200,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Cache gitlibs - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-gitlibs with: @@ -251,7 +251,7 @@ jobs: tools-deps: '1.10.1.763' - name: Cache maven - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-maven with: @@ -261,7 +261,7 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}- - name: Cache gitlibs - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-gitlibs with: From d701b452b3f0b09f13191094bff9d5763a449191 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Mar 2025 11:31:57 -0400 Subject: [PATCH 3925/4033] CLJS-3429: Handle More Complex Closure Type Annotations (#245) * add cljs.externs/info helper * add cljs.externs/filter-externs helper * improve type parsing by handling the various cases and adding a simplifier * cleanup some old reflection warnings * add new test cases --- src/main/clojure/cljs/externs.clj | 68 ++++++++++++++++--- .../clojure/cljs/externs_parsing_tests.clj | 28 +++++++- 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index d86aa91ef..910643b49 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -38,13 +38,43 @@ (into [] (butlast props)) (with-meta (last props) ty)))) +(def token->kw + {Token/BANG :bang + Token/BLOCK :block + Token/PIPE :pipe + Token/STRINGLIT :string-lit + Token/QMARK :qmark + Token/STAR :star}) + +(defn parse-texpr [^Node root] + (when-let [token (get token->kw (.getToken root))] + (let [children (.children root)] + (merge + {:type token} + (when-not (empty? children) + {:children (vec (map parse-texpr (.children root)))}) + (when (= :string-lit token) + {:value (.getString root)}))))) + +(defn undefined? + [{:keys [type value] :as texpr}] + (and (= type :string-lit) + (= "undefined" value))) + +(defn simplify-texpr + [texpr] + (case (:type texpr) + :string-lit (some-> (:value texpr) symbol) + (:star :qmark) 'any + :bang (simplify-texpr (-> texpr :children first)) + :pipe (let [[x y] (:children texpr)] + (if (undefined? y) + (simplify-texpr x) + 'any)) + 'any)) + (defn get-tag [^JSTypeExpression texpr] - (when-let [root (.getRoot texpr)] - (if (.isString root) - (symbol (.getString root)) - (if-let [child (.. root getFirstChild)] - (if (.isString child) - (symbol (.. child getString))))))) + (some-> (.getRoot texpr) parse-texpr simplify-texpr)) (defn params->method-params [xs] (letfn [(not-opt? [x] @@ -156,7 +186,7 @@ [lhs]) [])))) -(defmethod parse-extern-node Token/GETPROP [node] +(defmethod parse-extern-node Token/GETPROP [^Node node] (when-not *ignore-var* (let [props (map symbol (string/split (.getQualifiedName node) #"\."))] [(if-let [ty (get-var-info node)] @@ -165,7 +195,7 @@ ;; JavaScript Object literal ;; { ... } -(defmethod parse-extern-node Token/OBJECTLIT [node] +(defmethod parse-extern-node Token/OBJECTLIT [^Node node] (when (> (.getChildCount node) 0) (loop [nodes (.children node) externs []] @@ -215,8 +245,8 @@ (loop [nodes (cond-> nodes ;; handle goog.modules which won't have top-levels ;; need to look at internal children - (= Token/MODULE_BODY (some-> nodes first .getToken)) - (-> first .children)) + (= Token/MODULE_BODY (some-> nodes ^Node (first) .getToken)) + (-> ^Node (first) .children)) externs []] (if (empty? nodes) externs @@ -313,6 +343,24 @@ (parse-externs (resource->source-file rsrc)) (:module desc))})))) +(defn info + "Helper for grabbing var info from an externs map. + Example: + (info externs '[Number isNaN]) + See `externs-map`" + [externs props] + (-> externs + (get-in (butlast props)) + (find (last props)) + first meta)) + +(defn filtered-externs [f] + (->> + (filter + #(= f (.getName %)) + (default-externs)) + first parse-externs index-externs)) + (comment (require '[clojure.java.io :as io] '[cljs.closure :as closure] diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj index effad773d..ed0cfdb70 100644 --- a/src/test/clojure/cljs/externs_parsing_tests.clj +++ b/src/test/clojure/cljs/externs_parsing_tests.clj @@ -8,9 +8,11 @@ (ns cljs.externs-parsing-tests (:require [cljs.closure :as closure] + [cljs.analyzer :as ana] + [cljs.env :as env] [cljs.externs :as externs] [clojure.java.io :as io] - [clojure.test :as test :refer [deftest is]]) + [clojure.test :as test :refer [deftest is testing]]) (:import [com.google.javascript.jscomp CommandLineRunner])) (deftest cljs-3121 @@ -45,8 +47,32 @@ (find 'HTMLDocument) first meta)] (is (= 'Document (:super info))))) +(deftest test-parse-closure-type-annotations + (let [externs (::ana/externs @(env/default-compiler-env))] + (testing "JS global console has tag Console" + (let [info (externs/info externs '[console])] + (is (= 'Console (:tag info))))) + (testing "JS global crypto has tag webCrypto.Crypto from: + @type {!webCrypto.Crypto|undefined}" + (let [info (externs/info externs '[crypto])] + (is (= 'webCrypto.Crypto (:tag info))))) + (testing "Generic return type on crypto methods returns ClojureScript relevant + type info:" + (testing "@return {!Promise}" + (let [info (externs/info externs '[webCrypto SubtleCrypto prototype encrypt])] + (is (= 'Promise (:ret-tag info))))) + (testing "@return {!Promise}" + (let [info (externs/info externs '[webCrypto SubtleCrypto prototype deriveKey])] + (is (= 'Promise (:ret-tag info))))) + (testing "@return {!Int8Array|!Uint8Array|!Uint8ClampedArray|!Int16Array|!Uint16Array|!Int32Array|!Uint32Array|!BigInt64Array|!BigUint64Array}" + (let [info (externs/info externs '[webCrypto Crypto prototype getRandomValues])] + (is (= 'any (:ret-tag info)))))))) + (comment + (let [externs (::ana/externs @(env/default-compiler-env))] + (externs/info externs '[webCrypto Crypto prototype getRandomValues])) + (externs/parse-externs (externs/resource->source-file (io/resource "goog/object/object.js"))) From 7f5411d5f8c66b546a8c75af7deaf7c17ba32157 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 18 Apr 2025 12:39:03 -0400 Subject: [PATCH 3926/4033] CLJS-3432: Bump to Closure Compiler v20250402 (#247) * bump to Closure Compiler v20250402 * Java 21 now required * bump closure-library to Clojure fork w/o debug loader breaking changes --- .github/workflows/release.yml | 2 +- .github/workflows/test.yaml | 14 +++++++------- deps.edn | 4 ++-- pom.xml | 8 ++++---- project.clj | 4 ++-- script/bootstrap | 4 ++-- src/main/clojure/cljs/closure.clj | 1 - src/main/clojure/cljs/externs.clj | 4 ++-- 8 files changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 585e84929..766c05f63 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: - name: Set up Java uses: actions/setup-java@v3 with: - java-version: 8 + java-version: 21 distribution: 'temurin' cache: 'maven' server-id: sonatype-nexus-staging diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 22070f16a..e6a4590d0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - uses: DeLaGuardo/setup-clojure@3.1 with: @@ -69,7 +69,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - uses: DeLaGuardo/setup-clojure@3.5 with: @@ -95,7 +95,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - uses: DeLaGuardo/setup-clojure@3.1 with: @@ -139,7 +139,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - uses: DeLaGuardo/setup-clojure@3.1 with: @@ -183,7 +183,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - uses: DeLaGuardo/setup-clojure@3.1 with: @@ -222,7 +222,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - uses: DeLaGuardo/setup-clojure@3.5 with: @@ -244,7 +244,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - uses: DeLaGuardo/setup-clojure@3.1 with: diff --git a/deps.edn b/deps.edn index 9a8f42b00..d5ba05166 100644 --- a/deps.edn +++ b/deps.edn @@ -1,10 +1,10 @@ {:paths ["src/main/clojure" "src/main/cljs" "resources"] :deps - {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20240317"} + {com.google.javascript/closure-compiler-unshaded {:mvn/version "v20250402"} 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"} - org.clojure/google-closure-library {:mvn/version "0.0-20230227-c7c0a541"} + org.clojure/google-closure-library {:mvn/version "0.0-20250418-2ce9ab6d"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/tools.reader {:mvn/version "1.3.6"} org.clojure/test.check {:mvn/version "1.1.1"}} diff --git a/pom.xml b/pom.xml index 60c528f92..8a808cd22 100644 --- a/pom.xml +++ b/pom.xml @@ -30,12 +30,12 @@ com.google.javascript closure-compiler-unshaded - v20210202 + v20250402 org.clojure google-closure-library - 0.0-20201211-3e6c510d + 0.0-20250418-2ce9ab6d org.clojure @@ -374,8 +374,8 @@ maven-compiler-plugin 3.1 - 1.8 - 1.8 + 21 + 21 diff --git a/project.clj b/project.clj index 165a553e0..d46b9a126 100644 --- a/project.clj +++ b/project.clj @@ -14,8 +14,8 @@ [org.clojure/tools.reader "1.3.6"] [org.clojure/test.check "1.1.1" :scope "test"] [com.cognitect/transit-java "1.0.362"] - [org.clojure/google-closure-library "0.0-20230227-c7c0a541"] - [com.google.javascript/closure-compiler-unshaded "v20240317"]] + [org.clojure/google-closure-library "0.0-20250418-2ce9ab6d"] + [com.google.javascript/closure-compiler-unshaded "v20250402"]] :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/script/bootstrap b/script/bootstrap index 6fc56b539..0ba5fa670 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -5,8 +5,8 @@ set -e CLOJURE_RELEASE="1.9.0" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" -CLOSURE_RELEASE="20240317" -GCLOSURE_LIB_RELEASE="0.0-20230227-c7c0a541" +CLOSURE_RELEASE="20250402" +GCLOSURE_LIB_RELEASE="0.0-20250418-2ce9ab6d" TREADER_RELEASE="1.3.6" TEST_CHECK_RELEASE="1.1.1" diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 28faeb04d..b215573f6 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -194,7 +194,6 @@ :underscore DiagnosticGroups/UNDERSCORE :unknown-defines DiagnosticGroups/UNKNOWN_DEFINES :unused-local-variable DiagnosticGroups/UNUSED_LOCAL_VARIABLE - :unused-private-property DiagnosticGroups/UNUSED_PRIVATE_PROPERTY :violated-module-dep DiagnosticGroups/VIOLATED_MODULE_DEP :visibility DiagnosticGroups/VISIBILITY}) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index 910643b49..c5343e1b1 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -12,7 +12,7 @@ [clojure.java.io :as io] [clojure.string :as string]) (:import [com.google.javascript.jscomp - CompilerOptions CompilerOptions$Environment SourceFile JsAst CommandLineRunner] + CompilerOptions CompilerOptions$Environment SourceFile CompilerInput CommandLineRunner] [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino Node Token JSTypeExpression JSDocInfo$Visibility] @@ -238,7 +238,7 @@ (com.google.javascript.jscomp.Compiler/setLoggingLevel Level/WARNING) compiler) (.init (list source-file) '() compiler-options)) - js-ast (JsAst. source-file) + js-ast (CompilerInput. source-file) ^Node root (.getAstRoot js-ast closure-compiler) ;; TODO: switch to getFirstChild + getNext in the loop nodes (.children root)] From e53f46ebd1b1919b83f1f4273e0f507d5910e566 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Wed, 23 Apr 2025 07:34:25 -0400 Subject: [PATCH 3927/4033] 1.12.33 --- README.md | 8 ++++---- changes.md | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e156881af..812346499 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.11.132 +Latest stable release: 1.12.33 * [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.11.132"} + org.clojure/clojurescript {:mvn/version "1.12.33"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.11.132"] +[org.clojure/clojurescript "1.12.33"] ``` [Maven](https://maven.apache.org) dependency information: @@ -28,7 +28,7 @@ Latest stable release: 1.11.132 org.clojure clojurescript - 1.11.132 + 1.12.33 ``` diff --git a/changes.md b/changes.md index f20db794f..208029bd1 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,24 @@ +## 1.12.33 + +### Changes +* Update Google Closure Compiler dependency to v20250402 +* Depend on Clojure fork of Google Closure Library +* CLJS-3290: implement IHash for js Symbol (#225) +* Updated vendorized tools.reader to 1.4.2 +* CLJS-3419: JS Map & Set should return true for seqable? +* CLJS-3421: Throw when calling ana-api/ns-publics on non-existing ns + +### Fixes +* CLJS-3242: trailing keys bug +* CLJS-2292: refer-clojure rename should also exclude +* CLJS-3240: lazy-seq does not cache result of calling seq +* CLJS-3418: Some Closure libraries are not lowered +* CLJS-3413: Macros not loaded w/ single segment namespace loaded via `:preloads` +* CLJS-3411: cljs.core/test behavior does not match docstring (#226) +* CLJS-3320: Compiler warning on trying to use `js` as an ns +* remove unnecessary key-check for HashCollisionNode +* CLJS-3429: Handle More Complex Closure Type Annotations + ## 1.11.132 ### Fixes From f92364a488a7d9242ce6b73cf5957e8a05999c8b Mon Sep 17 00:00:00 2001 From: JarrodCTaylor Date: Wed, 23 Apr 2025 11:15:23 -0500 Subject: [PATCH 3928/4033] Bump nexus-staging-maven-plugin version --- pom.template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 5906ed5a0..f4c92ecd4 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -388,7 +388,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.5 + 1.7.0 default-deploy From f46f8750ee3bb259ade1b08029c56a69947296db Mon Sep 17 00:00:00 2001 From: JarrodCTaylor Date: Wed, 23 Apr 2025 13:29:36 -0500 Subject: [PATCH 3929/4033] Add mvn flag --- script/build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/build b/script/build index c6119122e..ebcd00558 100755 --- a/script/build +++ b/script/build @@ -17,6 +17,8 @@ POM_TEMPLATE="pom.template.xml" POM_FILE="pom.xml" CLJS_SCRIPT_MVN_OPTS=${CLJS_SCRIPT_MVN_OPTS:-""} +export MAVEN_OPTS="${MAVEN_OPTS} --add-opens java.base/java.util=ALL-UNNAMED" + # The command `git describe --match v0.0` will return a string like # # v0.0-856-g329708b From 105d3febb8cb5e72c1dd27f396f3f363a73e7879 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 23 Apr 2025 16:20:30 -0400 Subject: [PATCH 3930/4033] align version --- README.md | 8 ++++---- changes.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 812346499..ac9fd567b 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.33 +Latest stable release: 1.12.35 * [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.33"} + org.clojure/clojurescript {:mvn/version "1.12.35"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.12.33"] +[org.clojure/clojurescript "1.12.35"] ``` [Maven](https://maven.apache.org) dependency information: @@ -28,7 +28,7 @@ Latest stable release: 1.12.33 org.clojure clojurescript - 1.12.33 + 1.12.35 ``` diff --git a/changes.md b/changes.md index 208029bd1..0ddce7f58 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.12.33 +## 1.12.35 ### Changes * Update Google Closure Compiler dependency to v20250402 From c9c803190b3e8128a3b107b364517ba7161b6d87 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 30 Apr 2025 15:56:40 -0400 Subject: [PATCH 3931/4033] revert CLJS-3420 (#248) * revert CLJS-3420 * comment out test * add a regression test if we're going to bring this back later --- src/main/cljs/cljs/core.cljs | 29 +++++++++++++----------- src/test/cljs/cljs/collections_test.cljs | 6 ++++- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3cc6dd58a..3e789b6dc 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3503,7 +3503,6 @@ reduces them without incurring seq initialization" :else name)] (Keyword. ns name (str (when ns (str ns "/")) name) nil)))) - (deftype LazySeq [meta ^:mutable fn ^:mutable s ^:mutable __hash] Object (toString [coll] @@ -3514,10 +3513,7 @@ reduces them without incurring seq initialization" (if (nil? fn) s (do - (loop [ls (fn)] - (if (instance? LazySeq ls) - (recur (.sval ls)) - (set! s (seq ls)))) + (set! s (fn)) (set! fn nil) s))) (indexOf [coll x] @@ -3537,27 +3533,27 @@ reduces them without incurring seq initialization" (-with-meta [coll new-meta] (if (identical? new-meta meta) coll - (LazySeq. new-meta #(.sval coll) nil __hash))) + (LazySeq. new-meta #(-seq coll) nil __hash))) IMeta (-meta [coll] meta) ISeq (-first [coll] - (.sval coll) + (-seq coll) (when-not (nil? s) - (-first s))) + (first s))) (-rest [coll] - (.sval coll) + (-seq coll) (if-not (nil? s) - (-rest s) + (rest s) ())) INext (-next [coll] - (.sval coll) + (-seq coll) (when-not (nil? s) - (-next s))) + (next s))) ICollection (-conj [coll o] (cons o coll)) @@ -3573,7 +3569,14 @@ reduces them without incurring seq initialization" (-hash [coll] (caching-hash coll hash-ordered-coll __hash)) ISeqable - (-seq [coll] (.sval coll)) + (-seq [coll] + (.sval coll) + (when-not (nil? s) + (loop [ls s] + (if (instance? LazySeq ls) + (recur (.sval ls)) + (do (set! s ls) + (seq s)))))) IReduce (-reduce [coll f] (seq-reduce f coll)) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 04d89d4aa..3c85985c0 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -1151,7 +1151,7 @@ (deftest test-cljs-3393 (is (= '(0 2 4) (take 3 (filter even? (range 100000000)))))) -(deftest test-cljs-3420-lazy-seq-caching-bug +#_(deftest test-cljs-3420-lazy-seq-caching-bug (testing "LazySeq should realize seq once" (let [a (atom 0) x (eduction (map (fn [_] (swap! a inc))) [nil]) @@ -1159,6 +1159,10 @@ (dotimes [_ 10] (is (= [1] l)))))) +(deftest test-cljs-3240-overflow-regress + (let [things (zipmap (range 15000) (repeat 0))] + (is (zero? (count (filter #(-> % key string?) things)))))) + (comment (run-tests) From 1f9bd07dad44c039107804ad4e54845a70905978 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 30 Apr 2025 15:58:22 -0400 Subject: [PATCH 3932/4033] 1.12.38 --- README.md | 8 ++++---- changes.md | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ac9fd567b..028ed0b43 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.35 +Latest stable release: 1.12.38 * [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.35"} + org.clojure/clojurescript {:mvn/version "1.12.38"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.12.35"] +[org.clojure/clojurescript "1.12.38"] ``` [Maven](https://maven.apache.org) dependency information: @@ -28,7 +28,7 @@ Latest stable release: 1.12.35 org.clojure clojurescript - 1.12.35 + 1.12.38 ``` diff --git a/changes.md b/changes.md index 0ddce7f58..6770df7db 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.12.35 +## 1.12.38 ### Changes * Update Google Closure Compiler dependency to v20250402 @@ -11,7 +11,6 @@ ### Fixes * CLJS-3242: trailing keys bug * CLJS-2292: refer-clojure rename should also exclude -* CLJS-3240: lazy-seq does not cache result of calling seq * CLJS-3418: Some Closure libraries are not lowered * CLJS-3413: Macros not loaded w/ single segment namespace loaded via `:preloads` * CLJS-3411: cljs.core/test behavior does not match docstring (#226) From 978c27347288646da45c8b90e1685d4408384882 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 15 May 2025 22:03:04 -0400 Subject: [PATCH 3933/4033] Dependency cleanup and updates (#249) - delete pom.xml - add latest GCL release --- deps.edn | 2 +- pom.template.xml | 14 +- pom.xml | 391 ----------------------------------------------- project.clj | 2 +- script/bootstrap | 2 +- 5 files changed, 10 insertions(+), 401 deletions(-) delete mode 100644 pom.xml diff --git a/deps.edn b/deps.edn index d5ba05166..3703b8f0f 100644 --- a/deps.edn +++ b/deps.edn @@ -4,7 +4,7 @@ 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"} - org.clojure/google-closure-library {:mvn/version "0.0-20250418-2ce9ab6d"} + org.clojure/google-closure-library {:mvn/version "0.0-20250515-f04e4c0e"} org.clojure/spec.alpha {:mvn/version "0.1.143"} org.clojure/tools.reader {:mvn/version "1.3.6"} org.clojure/test.check {:mvn/version "1.1.1"}} diff --git a/pom.template.xml b/pom.template.xml index f4c92ecd4..cb0381efa 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,17 +30,12 @@ com.google.javascript closure-compiler-unshaded - v20240317 + v20250402 org.clojure google-closure-library - 0.0-20230227-c7c0a541 - - - org.clojure - tools.reader - 1.3.6 + 0.0-20250515-f04e4c0e com.cognitect @@ -53,6 +48,11 @@ + + org.clojure + tools.reader + 1.3.6 + org.clojure test.check diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 8a808cd22..000000000 --- a/pom.xml +++ /dev/null @@ -1,391 +0,0 @@ - - 4.0.0 - org.clojure - clojurescript - - 1.10.843 - jar - ClojureScript - - https://github.com/clojure/clojurescript - - - ClojureScript compiler and core runtime library. - - - - - Eclipse Public License 1.0 - http://opensource.org/licenses/eclipse-1.0.php - repo - - - - - - org.clojure - clojure - 1.8.0 - - - com.google.javascript - closure-compiler-unshaded - v20250402 - - - org.clojure - google-closure-library - 0.0-20250418-2ce9ab6d - - - org.clojure - data.json - 0.2.6 - - - org.clojure - tools.reader - 1.3.3 - - - com.cognitect - transit-clj - 0.8.309 - - - org.clojure - clojure - - - - - org.clojure - test.check - 0.10.0-alpha3 - test - - - org.clojure - clojure - - - - - - - Aaron Bedra - Alan Dipert - Alex Dowad - Alan Malloy - Alen Ribic - Alex Redington - Ambrose Bonnaire-Sergeant - Andrew Rosa - Antonin Hildebrand - Ben Moss - Benjamin Meyer - Bo Jeanes - Bobby Calderwood - Brandon Bloom - Brenton Ashworth - Brian Jenkins - Brian Kim - Brian Taylor - Bruce Hauman - Chad Taylor - Chas Emerick - Charles Duffy - Chris Granger - Chris Pickard - Chris Houser - Chris Truter - Christopher Redinger - Colin Jones - Creighton Kirkendall - David Nolen - Daniel Compton - Daniel Skarda - Dave Sann - Devin Walters - Dylan Butman - Edward Tsech - Eric Normand - Eric Thorsen - Erik Ouchterlony - Evan Mezeske - Francis Avila - Frank Failla - Francoise De Serre - Gary Fredericks - Gary Trakhman - Herwig Hochleitner - Hubert Iwaniuk - Hugo Duncan - Immo Heikkinen - Ivan Willig - J. Pablo Fernandez - Jamie Brandon - Jeff Dik - Jess Martin - Joel Holdbrooks - Joel Martin - John Li - Jonas De Vuyst - Jonas Enlund - Jonathan Boston - Jozef Wagner - Juergen Hoetzel - Juho Teperi - Julian Eluard - Justin Tirrell - Kovas Boguta - Kevin J. Lynagh - Laszlo Toeroek - Leon Grapenthin - Luke VanderHart - Maria Geller - Martin Klepsch - Matjaz Gregoric - Max Gonzih - Max Penet - Max Veytsman - Michael Ballantyne - Michael Fogus - Michael Glaesemann - Michael Griffiths - Michael O. Church - Michał Marczyk - Michiel Borkent - Mike Fikes - Moritz Ulrich - Murphy McMahon - Nelson Morris - Nicola Mometto - Nikita Prokopov - Osbert Feng - Paul Michael Bauer - Paul deGrandis - Peter Schuck - Peter Stephens - Peter Taoussanis - Pieter van Prooijen - Raphaël Amiard - Raymond Huang - Rich Hickey - Roman Gonzalez - Roman Scherer - Rupa Shankar - Russ Olsen - Sam Umbach - Samuel Miller - Sean Grove - Sebastien Bensusan - Sean LeBron - Steven Kallstrom - Stuart Halloway - Stuart Mitchell - Stuart Sierra - Takahiro Hozumi - Thomas Heller - Thomas Scheiblauer - Tim Griesser - Timothy Pratley - Toby Crawley - Tom Hickey - Tom Jack - Tom Marble - Travis Thieman - Travis Vachon - Wilkes Joiner - Zachary Allaun - Zach Oakes - Zubair Quraishi - - - - scm:git:git://github.com/clojure/clojurescript.git - scm:git:git@github.com:clojure/clojurescript.git - https://github.com/clojure/clojurescript - - - - org.clojure - pom.contrib - 1.1.0 - - - - UTF-8 - src/main/clojure - src/main/cljs - src/main/cljs - resources - true - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.5 - - - add-clojure-source-dirs - generate-sources - - add-source - add-resource - - - - ${clojure.source.dir} - ${cljs.source.dir} - - - - ${clojure.source.dir} - - - ${cljs.source.dir} - - - ${resources.dir} - - - - - - - - com.theoryinpractise - clojure-maven-plugin - 1.7.1 - - false - - - - clojure-compile - compile - - compile - - - true - - cljs.util - cljs.env - cljs.js-deps - cljs.core - cljs.source-map.base64 - cljs.source-map.base64-vlq - cljs.source-map - cljs.analyzer - cljs.analyzer.utils - cljs.compiler - cljs.closure - cljs.tagged-literals - cljs.test - cljs.analyzer.api - cljs.build.api - cljs.compiler.api - cljs.spec.alpha - cljs.spec.test.alpha - cljs.spec.gen.alpha - cljs.repl - cljs.repl.browser - cljs.repl.node - cljs.repl.reflect - cljs.repl.server - cljs.main - cljs.cli - - - - - - - maven-jar-plugin - 2.4 - - - - cljs.main - - - - - - default-jar - package - - jar - - - - **/*.clj - **/*.cljc - **/*.cljs - **/*.js - **/*.map - **/*.edn - **/*.svg - **/*.png - - - - - - - maven-assembly-plugin - 2.4 - - - aot-jar - package - - single - - - false - - src/assembly/aot.xml - - - - - slim-jar - package - - single - - - - src/assembly/slim.xml - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - 21 - 21 - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - r@{project.version} - - - - - diff --git a/project.clj b/project.clj index d46b9a126..1aa1fb4f3 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ [org.clojure/tools.reader "1.3.6"] [org.clojure/test.check "1.1.1" :scope "test"] [com.cognitect/transit-java "1.0.362"] - [org.clojure/google-closure-library "0.0-20250418-2ce9ab6d"] + [org.clojure/google-closure-library "0.0-20250515-f04e4c0e"] [com.google.javascript/closure-compiler-unshaded "v20250402"]] :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :uberjar {:aot :all :main cljs.main} diff --git a/script/bootstrap b/script/bootstrap index 0ba5fa670..6b2a6e44b 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -6,7 +6,7 @@ CLOJURE_RELEASE="1.9.0" SPEC_ALPHA_RELEASE="0.1.143" CORE_SPECS_ALPHA_RELEASE="0.1.24" CLOSURE_RELEASE="20250402" -GCLOSURE_LIB_RELEASE="0.0-20250418-2ce9ab6d" +GCLOSURE_LIB_RELEASE="0.0-20250515-f04e4c0e" TREADER_RELEASE="1.3.6" TEST_CHECK_RELEASE="1.1.1" From 49092ace08a907c3932e9bc518d92fdd892e5452 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Thu, 15 May 2025 22:07:41 -0400 Subject: [PATCH 3934/4033] 1.12.40 --- README.md | 6 +++--- changes.md | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 028ed0b43..56dfb2e9a 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.38 +Latest stable release: 1.12.40 * [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.38"} + org.clojure/clojurescript {:mvn/version "1.12.40"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.12.38"] +[org.clojure/clojurescript "1.12.40"] ``` [Maven](https://maven.apache.org) dependency information: diff --git a/changes.md b/changes.md index 6770df7db..47cadd067 100644 --- a/changes.md +++ b/changes.md @@ -1,8 +1,12 @@ -## 1.12.38 +## 1.12.40 ### Changes * Update Google Closure Compiler dependency to v20250402 -* Depend on Clojure fork of Google Closure Library +* Depend on Clojure fork of Google Closure Library, 0.0-20250515-f04e4c0e + - restores goog.dom.query + - restores goog.isString and other simple fns to goog.base that were unnecessarily removed + - restore debug loader as default + - remove unused Closure directive `unusedPrivateMembers` * CLJS-3290: implement IHash for js Symbol (#225) * Updated vendorized tools.reader to 1.4.2 * CLJS-3419: JS Map & Set should return true for seqable? From ca20fac7f6883e0604708e984a6cbfef1cb88538 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Thu, 15 May 2025 23:27:24 -0400 Subject: [PATCH 3935/4033] add more recent pom.xml back in, it seems it is needed --- pom.xml | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 pom.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..b767c964d --- /dev/null +++ b/pom.xml @@ -0,0 +1,380 @@ + + 4.0.0 + org.clojure + clojurescript + + 1.12.40 + jar + ClojureScript + + https://github.com/clojure/clojurescript + + + ClojureScript compiler and core runtime library. + + + + + Eclipse Public License 1.0 + http://opensource.org/licenses/eclipse-1.0.php + repo + + + + + + org.clojure + clojure + 1.10.0 + + + com.google.javascript + closure-compiler-unshaded + v20250402 + + + org.clojure + google-closure-library + 0.0-20250515-f04e4c0e + + + com.cognitect + transit-java + 1.0.362 + + + org.clojure + clojure + + + + + org.clojure + tools.reader + 1.3.6 + + + org.clojure + test.check + 1.1.1 + test + + + org.clojure + clojure + + + + + + + Aaron Bedra + Alan Dipert + Alex Dowad + Alan Malloy + Alen Ribic + Alex Redington + Ambrose Bonnaire-Sergeant + Andrew Rosa + Antonin Hildebrand + Ben Moss + Benjamin Meyer + Bo Jeanes + Bobby Calderwood + Brandon Bloom + Brenton Ashworth + Brian Jenkins + Brian Kim + Brian Taylor + Bruce Hauman + Chad Taylor + Chas Emerick + Charles Duffy + Chris Granger + Chris Pickard + Chris Houser + Chris Truter + Christopher Redinger + Colin Jones + Creighton Kirkendall + David Nolen + Daniel Compton + Daniel Skarda + Dave Sann + Devin Walters + Dylan Butman + Edward Tsech + Eric Normand + Eric Thorsen + Erik Ouchterlony + Evan Mezeske + Francis Avila + Frank Failla + Francoise De Serre + Gary Fredericks + Gary Trakhman + Herwig Hochleitner + Hubert Iwaniuk + Hugo Duncan + Immo Heikkinen + Ivan Willig + J. Pablo Fernandez + Jamie Brandon + Jeff Dik + Jess Martin + Joel Holdbrooks + Joel Martin + John Li + Jonas De Vuyst + Jonas Enlund + Jonathan Boston + Jozef Wagner + Juergen Hoetzel + Juho Teperi + Julian Eluard + Justin Tirrell + Kovas Boguta + Kevin J. Lynagh + Laszlo Toeroek + Leon Grapenthin + Luke VanderHart + Maria Geller + Martin Klepsch + Matjaz Gregoric + Max Gonzih + Max Penet + Max Veytsman + Michael Ballantyne + Michael Fogus + Michael Glaesemann + Michael Griffiths + Michael O. Church + Michał Marczyk + Michiel Borkent + Mike Fikes + Moritz Ulrich + Murphy McMahon + Nelson Morris + Nicola Mometto + Nikita Prokopov + Osbert Feng + Paul Michael Bauer + Paul deGrandis + Peter Schuck + Peter Stephens + Peter Taoussanis + Pieter van Prooijen + Raphaël Amiard + Raymond Huang + Rich Hickey + Roman Gonzalez + Roman Scherer + Rupa Shankar + Russ Olsen + Sam Umbach + Samuel Miller + Sean Grove + Sebastien Bensusan + Sean LeBron + Steven Kallstrom + Stuart Halloway + Stuart Mitchell + Stuart Sierra + Takahiro Hozumi + Thomas Heller + Thomas Scheiblauer + Tim Griesser + Timothy Pratley + Toby Crawley + Tom Hickey + Tom Jack + Tom Marble + Travis Thieman + Travis Vachon + Wilkes Joiner + Zachary Allaun + Zach Oakes + Zubair Quraishi + + + + scm:git:git://github.com/clojure/clojurescript.git + scm:git:git@github.com:clojure/clojurescript.git + https://github.com/clojure/clojurescript + + + + + + org.sonatype.oss + oss-parent + 7 + + + + UTF-8 + src/main/clojure + src/main/cljs + src/main/cljs + resources + true + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.5 + + + add-clojure-source-dirs + generate-sources + + add-source + add-resource + + + + ${clojure.source.dir} + ${cljs.source.dir} + + + + ${clojure.source.dir} + + + ${cljs.source.dir} + + + ${resources.dir} + + + + + + + + com.theoryinpractise + clojure-maven-plugin + 1.8.3 + + false + + + + clojure-compile + compile + + compile + + + true + + !cljs.vendor.bridge + + + + + + + maven-jar-plugin + 2.4 + + + + cljs.main + + + + + + default-jar + package + + jar + + + + **/*.clj + **/*.cljc + **/*.cljs + **/*.js + **/*.map + **/*.edn + **/*.svg + **/*.png + + + + + + + maven-assembly-plugin + 2.4 + + + aot-jar + package + + single + + + false + + src/assembly/aot.xml + + + + + slim-jar + package + + single + + + + src/assembly/slim.xml + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.1.0 + + + --pinentry-mode + loopback + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 21 + 21 + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + r@{project.version} + + + + + From 9b4225ecaa7b6ceecd025ea3519eeb7884e81cfa Mon Sep 17 00:00:00 2001 From: davidnolen Date: Thu, 15 May 2025 23:45:11 -0400 Subject: [PATCH 3936/4033] 1.12.42 - use the shaded closure-compiler, this seems more sensible - Java 21 in template pom - update pom - update readme / changes --- README.md | 6 +++--- changes.md | 2 +- deps.edn | 2 +- pom.template.xml | 6 +++--- pom.xml | 53 +++++++++++++++++++++++++++++++++++++++--------- project.clj | 2 +- 6 files changed, 52 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 56dfb2e9a..98323b88b 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.40 +Latest stable release: 1.12.42 * [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.40"} + org.clojure/clojurescript {:mvn/version "1.12.42"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.12.40"] +[org.clojure/clojurescript "1.12.42"] ``` [Maven](https://maven.apache.org) dependency information: diff --git a/changes.md b/changes.md index 47cadd067..4e616fb81 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.12.40 +## 1.12.42 ### Changes * Update Google Closure Compiler dependency to v20250402 diff --git a/deps.edn b/deps.edn index 3703b8f0f..012e22069 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-unshaded {:mvn/version "v20250402"} + {com.google.javascript/closure-compiler {:mvn/version "v20250402"} 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"} diff --git a/pom.template.xml b/pom.template.xml index cb0381efa..c99ccb834 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -29,7 +29,7 @@ com.google.javascript - closure-compiler-unshaded + closure-compiler v20250402 @@ -363,8 +363,8 @@ maven-compiler-plugin 3.1 - 1.7 - 1.7 + 21 + 21 diff --git a/pom.xml b/pom.xml index b767c964d..755cebc9b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.clojure clojurescript - 1.12.40 + 1.12.41 jar ClojureScript @@ -29,7 +29,7 @@ com.google.javascript - closure-compiler-unshaded + closure-compiler v20250402 @@ -367,14 +367,47 @@ 21 - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - r@{project.version} - - + + + + sonatype-oss-release + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.7.0 + + + default-deploy + deploy + + + deploy + + + + + + https://oss.sonatype.org/ + + sonatype-nexus-staging + + + + + + diff --git a/project.clj b/project.clj index 1aa1fb4f3..647d5a664 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-unshaded "v20250402"]] + [com.google.javascript/closure-compiler "v20250402"]] :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"]]}} From 526ab9a50475433505b530541ae42066127834dc Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 30 May 2025 15:34:52 -0500 Subject: [PATCH 3937/4033] update to new parent pom --- pom.template.xml | 2 +- pom.xml | 2 +- project.clj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index c99ccb834..b10649dd0 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -207,7 +207,7 @@ org.clojure pom.contrib - 0.1.2 + 1.3.0 --> diff --git a/pom.xml b/pom.xml index 755cebc9b..8e310075a 100644 --- a/pom.xml +++ b/pom.xml @@ -207,7 +207,7 @@ org.clojure pom.contrib - 0.1.2 + 1.3.0 --> diff --git a/project.clj b/project.clj index 647d5a664..3977529e5 100644 --- a/project.clj +++ b/project.clj @@ -1,6 +1,6 @@ (defproject org.clojure/clojurescript "0.0-SNAPSHOT" :description "ClojureScript compiler and core runtime library" - :parent [org.clojure/pom.contrib "0.1.2"] + :parent [org.clojure/pom.contrib "1.3.0"] :url "https://github.com/clojure/clojurescript" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} From 6fb3ce18dae51dc507b27adfe52e1f6fa1d8c497 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 30 May 2025 17:04:50 -0500 Subject: [PATCH 3938/4033] update build deployment plugins --- pom.template.xml | 128 ++++++++++++++++++++++++++--------------------- pom.xml | 128 ++++++++++++++++++++++++++--------------------- script/build | 3 +- 3 files changed, 141 insertions(+), 118 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index b10649dd0..dabde1b99 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -203,20 +203,6 @@ https://github.com/clojure/clojurescript - - - - org.sonatype.oss - oss-parent - 7 - - UTF-8 src/main/clojure @@ -226,13 +212,30 @@ true + + + central + https://central.sonatype.com + + + central-snapshot + https://central.sonatype.com/repository/maven-snapshots/ + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + org.codehaus.mojo build-helper-maven-plugin - 1.5 + 3.0.0 add-clojure-source-dirs @@ -286,7 +289,7 @@ maven-jar-plugin - 2.4 + 2.4.2 @@ -318,7 +321,7 @@ maven-assembly-plugin - 2.4 + 3.7.1 aot-jar @@ -352,62 +355,71 @@ maven-gpg-plugin 3.1.0 - - --pinentry-mode - loopback - + + --pinentry-mode + loopback + org.apache.maven.plugins maven-compiler-plugin - 3.1 + 3.8.1 21 21 + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 + true + + central + true + + + - sonatype-oss-release - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 - - - default-deploy - deploy - - - deploy - - - - - - https://oss.sonatype.org/ - - sonatype-nexus-staging - - - - + sign + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + diff --git a/pom.xml b/pom.xml index 8e310075a..a0f07896c 100644 --- a/pom.xml +++ b/pom.xml @@ -203,20 +203,6 @@ https://github.com/clojure/clojurescript - - - - org.sonatype.oss - oss-parent - 7 - - UTF-8 src/main/clojure @@ -226,13 +212,30 @@ true + + + central + https://central.sonatype.com + + + central-snapshot + https://central.sonatype.com/repository/maven-snapshots/ + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + org.codehaus.mojo build-helper-maven-plugin - 1.5 + 3.0.0 add-clojure-source-dirs @@ -286,7 +289,7 @@ maven-jar-plugin - 2.4 + 3.4.2 @@ -318,7 +321,7 @@ maven-assembly-plugin - 2.4 + 3.7.1 aot-jar @@ -352,62 +355,71 @@ maven-gpg-plugin 3.1.0 - - --pinentry-mode - loopback - + + --pinentry-mode + loopback + org.apache.maven.plugins maven-compiler-plugin - 3.1 + 3.8.1 21 21 + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 + true + + central + true + + + - sonatype-oss-release - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 - - - default-deploy - deploy - - - deploy - - - - - - https://oss.sonatype.org/ - - sonatype-nexus-staging - - - - + sign + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + diff --git a/script/build b/script/build index ebcd00558..9ecd7f03e 100755 --- a/script/build +++ b/script/build @@ -66,8 +66,7 @@ mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server if [ "$HUDSON" = "true" ]; then - mvn -B -ntp --fail-at-end -Psonatype-oss-release $CLJS_SCRIPT_MVN_OPTS \ - clean deploy nexus-staging:release + mvn -B -ntp --fail-at-end -DskipStaging=true -Psign $CLJS_SCRIPT_MVN_OPTS clean deploy echo "Creating tag $TAG" git tag -f "$TAG" From 4c45e0235131ac78d279a9e2a2518e61e96cc684 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 2 Jun 2025 16:55:41 -0500 Subject: [PATCH 3939/4033] build source and javadoc jars for Maven central deployment --- pom.template.xml | 28 +++++++++++++++++++++++++--- pom.xml | 30 ++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/pom.template.xml b/pom.template.xml index dabde1b99..884a2d628 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -221,7 +221,7 @@ central-snapshot https://central.sonatype.com/repository/maven-snapshots/ - + @@ -229,8 +229,17 @@ org.apache.maven.plugins maven-source-plugin 3.3.1 + + + attach-sources + package + + jar + + + - + org.codehaus.mojo @@ -289,7 +298,7 @@ maven-jar-plugin - 2.4.2 + 3.4.2 @@ -317,6 +326,19 @@ + + javadoc-jar + package + + jar + + + + ** + + javadoc + + diff --git a/pom.xml b/pom.xml index a0f07896c..b524959cc 100644 --- a/pom.xml +++ b/pom.xml @@ -229,6 +229,15 @@ org.apache.maven.plugins maven-source-plugin 3.3.1 + + + attach-sources + package + + jar + + + @@ -317,6 +326,19 @@ + + javadoc-jar + package + + jar + + + + ** + + javadoc + + @@ -355,10 +377,10 @@ maven-gpg-plugin 3.1.0 - - --pinentry-mode - loopback - + + --pinentry-mode + loopback + From d26872c35229bc16c773008ebdc3f1dae177a3e5 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 2 Jun 2025 16:58:15 -0500 Subject: [PATCH 3940/4033] use release plugin --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index 9ecd7f03e..e50a121f0 100755 --- a/script/build +++ b/script/build @@ -66,7 +66,7 @@ mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server if [ "$HUDSON" = "true" ]; then - mvn -B -ntp --fail-at-end -DskipStaging=true -Psign $CLJS_SCRIPT_MVN_OPTS clean deploy + mvn -B -ntp --fail-at-end -DskipStaging=true -Psign $CLJS_SCRIPT_MVN_OPTS clean deploy release echo "Creating tag $TAG" git tag -f "$TAG" From 2e81eb3b1626e9021509a5ffd935b0091a389141 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 2 Jun 2025 17:03:14 -0500 Subject: [PATCH 3941/4033] Revert "use release plugin" This reverts commit d26872c35229bc16c773008ebdc3f1dae177a3e5. --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index e50a121f0..9ecd7f03e 100755 --- a/script/build +++ b/script/build @@ -66,7 +66,7 @@ mv $AOT_CACHE_FILE src/main/cljs/cljs/core.cljs.cache.aot.edn # For Hudson server if [ "$HUDSON" = "true" ]; then - mvn -B -ntp --fail-at-end -DskipStaging=true -Psign $CLJS_SCRIPT_MVN_OPTS clean deploy release + mvn -B -ntp --fail-at-end -DskipStaging=true -Psign $CLJS_SCRIPT_MVN_OPTS clean deploy echo "Creating tag $TAG" git tag -f "$TAG" From 4d13556023452ec85d23f13d1f25798e07d6931c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 Jun 2025 20:26:47 -0400 Subject: [PATCH 3942/4033] Fix protocol fn DCE issue (#252) * fixes invoke to cljs.core/str in protocol fns * add another trivial dce test w/ a protocol fn --- src/main/clojure/cljs/core.cljc | 4 ++-- src/test/cljs_build/trivial/core2.cljs | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 13 +++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/test/cljs_build/trivial/core2.cljs diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 72f465427..8418c5eca 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -3287,9 +3287,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/test/cljs_build/trivial/core2.cljs b/src/test/cljs_build/trivial/core2.cljs new file mode 100644 index 000000000..5e2f4fb0d --- /dev/null +++ b/src/test/cljs_build/trivial/core2.cljs @@ -0,0 +1,3 @@ +(ns trivial.core2) + +(. js/console (-lookup 1 2)) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index e788c1ace..f65c1580f 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -720,6 +720,19 @@ (build/build (build/inputs (io/file inputs "trivial/core.cljs")) opts cenv) (is (< (.length out-file) 10000)))) +(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) 10000)))) + (deftest cljs-3255-nil-inputs-build (let [out (.getPath (io/file (test/tmp-dir) "3255-test-out")) out-file (io/file out "main.js") From 0bf4c3ff9eb4b2a8af8294f6b7314c756fd32f4a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 6 Jun 2025 16:53:54 -0400 Subject: [PATCH 3943/4033] More DCE cleanup (#253) Tiny DCE improvements - just use empty list in IndexedSeq - just invoke toString on StringBuffer - inline toString impl for EmptyList - reify should not emit basis static method - reify should set meta to nil if no actual meta, not empty map --- src/main/cljs/cljs/core.cljs | 7 +++---- src/main/clojure/cljs/core.cljc | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3e789b6dc..79a8fe96d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -909,7 +909,7 @@ writer (StringBufferWriter. sb)] (-pr-writer obj writer (pr-opts)) (-flush writer) - (str sb))) + (.toString sb))) ;;;;;;;;;;;;;;;;;;; Murmur3 ;;;;;;;;;;;;;;; @@ -1648,7 +1648,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)) @@ -3206,8 +3206,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] diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8418c5eca..1424674a2 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1365,7 +1365,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 +1382,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." @@ -1789,17 +1793,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)))) From e34ba40399add1bee917f7073687cfdcf4262019 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Jun 2025 09:00:00 -0400 Subject: [PATCH 3944/4033] pr-writer-impl lower level impl for js object printing * remove pr-writer-impl dependence on lazy seq, MapEntry - use Array.map instead of map - reify IMapEntry instead of concrete MapEntry * use primitive regex method --- src/main/cljs/cljs/core.cljs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 79a8fe96d..f6a8d24ea 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10529,9 +10529,15 @@ reduces them without incurring seq initialization" (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] + (reify + IMapEntry + (-key [_] + (cond-> k (some? (.match k #"^[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*$")) keyword)) + (-val [_] + (unchecked-get obj k))))) pr-writer writer opts)) (array? obj) From ffacd2314221e262c9460506a9688d3103041804 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Jun 2025 12:03:18 -0400 Subject: [PATCH 3945/4033] Remove pr-opts calls, backwards compatibility tweaks (#257) * remove calls to pr-opts - just use dynamic binding - keep it backwards compatible * add long missing infer-tag case for :try --- src/main/cljs/cljs/core.cljs | 61 +++++++++++++++++++++-------- src/main/clojure/cljs/analyzer.cljc | 1 + 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f6a8d24ea..f67cb27a1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -267,6 +267,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] @@ -907,7 +932,7 @@ [^not-native obj] (let [sb (StringBuffer.) writer (StringBufferWriter. sb)] - (-pr-writer obj writer (pr-opts)) + (-pr-writer obj writer nil) (-flush writer) (.toString sb))) @@ -10441,13 +10466,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) @@ -10491,7 +10516,7 @@ 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))))) @@ -10544,7 +10569,7 @@ reduces them without incurring seq initialization" (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)) @@ -10643,18 +10668,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 @@ -10662,38 +10687,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] diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 8c61c4586..a13c08545 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1568,6 +1568,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)) From e611bd0b0b1afbc6cf45ed13b374599cf762f6eb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Jun 2025 17:02:08 -0400 Subject: [PATCH 3946/4033] avoid call to assoc, use -assoc --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f67cb27a1..5c94309c4 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10624,7 +10624,7 @@ 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] From 078d59df2095c9a3f60b216e8d478c4614b55597 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 9 Jun 2025 23:38:54 -0400 Subject: [PATCH 3947/4033] Use primitives in print-map (#258) * lower print-map - lift pr-map-entry-helper, implement ISeqable - lift-ns uses array of MapEntry instead of actual map --- src/main/cljs/cljs/core.cljs | 50 +++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 5c94309c4..12025eaac 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10520,6 +10520,14 @@ reduces them without incurring seq initialization" (implements? IMeta obj) (not (nil? (meta obj))))) +(defn- pr-map-entry [k v] + (reify + IMapEntry + (-key [_] k) + (-val [_] v) + ISeqable + (-seq [_] (IndexedSeq. #js [k v] 0 nil)))) + (defn- pr-writer-impl [obj writer opts] (cond @@ -10557,12 +10565,9 @@ reduces them without incurring seq initialization" (.map (js-keys obj) (fn [k] - (reify - IMapEntry - (-key [_] - (cond-> k (some? (.match k #"^[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*$")) keyword)) - (-val [_] - (unchecked-get obj k))))) + (pr-map-entry + (cond-> k (some? (.match k #"^[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*$")) keyword) + (unchecked-get obj k)))) pr-writer writer opts)) (array? obj) @@ -10731,20 +10736,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 (pr-map-entry (strip-ns k) v)) + (recur ns entries)) + (when-let [new-ns (namespace k)] + (.push lm (pr-map-entry (strip-ns k) v)) + (recur new-ns entries)))) + #js [ns lm]))))) (defn print-prefix-map [prefix m print-one writer opts] (pr-sequential-writer @@ -10757,10 +10764,11 @@ reduces them without incurring seq initialization" 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 From 9bb394258ac7755833fdaced57ac6be5ed9d7fa5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Jun 2025 08:47:24 -0400 Subject: [PATCH 3948/4033] Remove the circularity that str has with IndexedSeq (#255) * remove the circularity that str has with IndexedSeq - add private str_ for cljs.core usage - keep str for now to avoid any potential breakage - add some simple tests for apply + str_ - the other cases already well covered by existing tests around printing --- src/main/cljs/cljs/core.cljs | 203 +++++++++++++++++------------- src/main/clojure/cljs/core.cljc | 18 +++ src/test/cljs/cljs/core_test.cljs | 9 ++ 3 files changed, 140 insertions(+), 90 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 12025eaac..b97f00fa2 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -357,7 +357,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] @@ -1175,7 +1175,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)))) @@ -1184,7 +1184,7 @@ (isMacro [_] (. (val) -cljs$lang$macro)) (toString [_] - (str "#'" sym)) + (str_ "#'" sym)) IDeref (-deref [_] (val)) IMeta @@ -1299,7 +1299,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 @@ -1448,7 +1448,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])) @@ -1967,7 +1967,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 @@ -2000,7 +2000,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 @@ -2495,7 +2495,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) @@ -2504,7 +2504,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." @@ -3072,6 +3072,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 @@ -3081,10 +3104,10 @@ reduces them without incurring seq initialization" "" (.join #js [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 @@ -3419,7 +3442,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)) @@ -3443,7 +3466,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" @@ -3473,7 +3496,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" @@ -3525,7 +3548,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 @@ -4187,7 +4210,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 @@ -4199,7 +4222,7 @@ reduces them without incurring seq initialization" (isEmpty [this] (zero? (.-length vals))) (toString [this] - (str "Many: " vals))) + (str_ "Many: " vals))) (def ^:private NONE #js {}) @@ -4213,21 +4236,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] @@ -4374,8 +4397,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" @@ -5549,7 +5572,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. @@ -5778,14 +5801,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] @@ -6104,7 +6127,7 @@ 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) "]"))) + (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)))) IReduce @@ -6292,7 +6315,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] @@ -7199,7 +7222,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))))) @@ -8268,7 +8291,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)))))) @@ -8280,7 +8303,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)))) @@ -9143,7 +9166,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)))) @@ -9155,7 +9178,7 @@ 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 @@ -9518,7 +9541,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) @@ -9767,7 +9790,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." @@ -10508,7 +10531,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))) \")) @@ -10548,7 +10571,7 @@ 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 @@ -10556,7 +10579,7 @@ reduces them without incurring seq initialization" ^boolean (js/isNaN obj) "##NaN" (identical? obj js/Number.POSITIVE_INFINITY) "##Inf" (identical? obj js/Number.NEGATIVE_INFINITY) "##-Inf" - :else (str obj))) + :else (str_ obj))) (object? obj) (do @@ -10585,15 +10608,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 \"" @@ -10621,7 +10644,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 @@ -10651,7 +10674,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)" @@ -10660,7 +10683,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 @@ -10760,7 +10783,7 @@ 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] @@ -10768,7 +10791,7 @@ reduces them without incurring seq initialization" (lift-ns m)) ns (some-> ns&lift-map (aget 0))] (if ns - (print-prefix-map (str "#:" ns) (aget ns&lift-map 1) 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 @@ -10901,43 +10924,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 ;;;;;;;;;;;;;;;; @@ -10998,7 +11021,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 ;;;;;;;;;;;;;;;;;;;; @@ -11228,7 +11251,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))) @@ -11252,7 +11275,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) @@ -11427,9 +11450,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)}) @@ -11492,7 +11515,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) @@ -11523,7 +11546,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] @@ -11689,7 +11712,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] @@ -11764,7 +11787,7 @@ reduces them without incurring seq initialization" IPrintWithWriter (-pr-writer [_ writer _] - (-write writer (str "#uuid \"" uuid "\""))) + (-write writer (str_ "#uuid \"" uuid "\""))) IHash (-hash [this] @@ -11776,7 +11799,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." @@ -11790,14 +11813,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)))))) @@ -11971,7 +11994,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? @@ -12024,11 +12047,11 @@ 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] @@ -12044,10 +12067,10 @@ reduces them without incurring seq initialization" (.toString sb))) (defn munge [name] - (let [name' (munge-str (str 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') @@ -12062,17 +12085,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'))))) @@ -12151,14 +12174,14 @@ reduces them without incurring seq initialization" (deftype Namespace [obj name] Object (findInternedVar [this sym] - (let [k (munge (str sym))] + (let [k (munge (str_ sym))] (when ^boolean (gobject/containsKey obj k) - (let [var-sym (symbol (str name) (str sym)) + (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) @@ -12183,10 +12206,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 @@ -12201,7 +12224,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. @@ -12213,7 +12236,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 @@ -12244,9 +12267,9 @@ reduces them without incurring seq initialization" [ns] (when (nil? NS_CACHE) (set! NS_CACHE (atom {}))) - (let [ns-str (str ns) + (let [ns-str (str_ ns) ns (if (not ^boolean (gstring/contains ns-str "$macros")) - (symbol (str ns-str "$macros")) + (symbol (str_ ns-str "$macros")) ns) the-ns (get @NS_CACHE ns)] (if-not (nil? the-ns) @@ -12277,7 +12300,7 @@ reduces them without incurring seq initialization" (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 diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 1424674a2..8393a1a67 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -849,6 +849,24 @@ (core/defn- string-expr [e] (vary-meta e assoc :tag 'string)) +(core/defmacro str_ + ([] "") + ([x] + (if (typed-expr? &env x '#{string}) + 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_(~{})")) + strs (core/->> (core/list* x ys) + (map interpolate) + (interpose ",") + (apply core/str))] + (string-expr (list* 'js* (core/str "[" strs "].join('')") x ys))))) + +;; TODO: should probably be a compiler pass to avoid the code duplication (core/defmacro str ([] "") ([x] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 9c4a62528..58720b5a1 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -2056,3 +2056,12 @@ [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])))) \ No newline at end of file From ad83b3edd3ef088ef63f9e3c05bf594824e569ab Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 10 Jun 2025 09:52:49 -0400 Subject: [PATCH 3949/4033] More trivial source build tests verifying DCE doesn't regress (#259) * add two more trivial output tests - keyword should be very small - vector by itself should be reasonable - bump windows version --- .github/workflows/test.yaml | 4 +-- src/test/cljs_build/trivial/core2.cljs | 2 +- src/test/cljs_build/trivial/core3.cljs | 3 +++ src/test/cljs_build/trivial/core4.cljs | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 30 +++++++++++++++++++++-- 5 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 src/test/cljs_build/trivial/core3.cljs create mode 100644 src/test/cljs_build/trivial/core4.cljs diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e6a4590d0..e98aa8818 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -62,7 +62,7 @@ jobs: # Runtime Tests runtime-windows-test: name: Runtime Windows Tests - runs-on: windows-2019 + runs-on: windows-2022 steps: - uses: actions/checkout@v2 @@ -215,7 +215,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 diff --git a/src/test/cljs_build/trivial/core2.cljs b/src/test/cljs_build/trivial/core2.cljs index 5e2f4fb0d..a79e64e80 100644 --- a/src/test/cljs_build/trivial/core2.cljs +++ b/src/test/cljs_build/trivial/core2.cljs @@ -1,3 +1,3 @@ (ns trivial.core2) -(. js/console (-lookup 1 2)) +(.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 000000000..a66db571c --- /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 000000000..f8f4c6d25 --- /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/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index f65c1580f..f05a4ac3f 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -718,7 +718,7 @@ 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")) @@ -731,7 +731,33 @@ 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) 10000)))) + (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) 32768)))) (deftest cljs-3255-nil-inputs-build (let [out (.getPath (io/file (test/tmp-dir) "3255-test-out")) From 90a40f69d5d3f2c34aa2c2e6612bb14296b2e383 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 13 Jun 2025 05:47:11 -0400 Subject: [PATCH 3950/4033] Add code size ratchet for PHM --- src/test/cljs_build/trivial/core5.cljs | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/test/cljs_build/trivial/core5.cljs diff --git a/src/test/cljs_build/trivial/core5.cljs b/src/test/cljs_build/trivial/core5.cljs new file mode 100644 index 000000000..1e7f87756 --- /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/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index f05a4ac3f..a1a2f3871 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -759,6 +759,19 @@ (build/build (build/inputs (io/file inputs "trivial/core4.cljs")) opts cenv) (is (< (.length out-file) 32768)))) +(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 cljs-3255-nil-inputs-build (let [out (.getPath (io/file (test/tmp-dir) "3255-test-out")) out-file (io/file out "main.js") From 55491752ad2ec03f773692ddbd1ce97cac7e3722 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 6 Jul 2025 09:59:08 -0400 Subject: [PATCH 3951/4033] Refactor has-extern? / js-tag (#246) * fix up tests so they don't throw if no warnings * resolve-extern, which can be used by both has-extern? and js-tag, returns both resolved prefix and var info * remove various hacks around extern resolution (Number, Window, prototype etc.), resolve-extern handles everything * undefined is a ref cycle, special case * change normalize-js-tag so it marks the ctor prop * add normalize-unresolved-prefix to fix up the cases we can't find * add impl unit tests * more unit test - can finally resolve crypto.subtle, verify type inference as well * add lift-tag-to-js helper * in resolve-var add the ctor to the tag to track later, this also lets the extra information flow * in analyze-dot check to see if the target is a constructor - if it is use that as tag instead, Function is not useful * test assertion that we can figure out the return even if a extern js ctor is bound to a local * add compiler test case for inferring return of Number.isNaN * add non-ctor inference for array, string, boolean and number, will be useful later * add js/isNaN test * add isArray extern test * don't return raised js/Foo types for boolean, number, string * remove ^boolean hint from array? ass test * remove hint for make-array, add test * remove hints for isFinite and isSafeInteger, tests * can infer distinct?, add test * remove various ^boolean cases no longer needed * FIXME comments about dubious ^boolean cases * move ^boolean hint from special-symbol? to contains? where it belongs, test case * goog.object/containsKey type inference doesn't work for reason, leave a trail for later * goog.string/contains does work, add test * remove hint from NaN? * FIXME note about re-matches, another dubious case of ^boolean hints --- src/main/cljs/cljs/core.cljs | 30 ++-- src/main/clojure/cljs/analyzer.cljc | 150 +++++++++++----- src/main/clojure/cljs/compiler.cljc | 3 +- src/main/clojure/cljs/externs.clj | 17 +- src/test/clojure/cljs/compiler_tests.clj | 19 +- src/test/clojure/cljs/externs_infer_tests.clj | 164 ++++++++++++++++-- .../clojure/cljs/externs_parsing_tests.clj | 6 + .../clojure/cljs/type_inference_tests.clj | 40 +++++ 8 files changed, 350 insertions(+), 79 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index b97f00fa2..e1c7b067e 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -243,7 +243,7 @@ [x] (coercive-= x nil)) -(defn ^boolean array? +(defn array? "Returns true if x is a JavaScript array." [x] (if (identical? *target* "nodejs") @@ -444,7 +444,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 @@ -1055,8 +1055,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 @@ -2355,7 +2355,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)))) @@ -2432,7 +2432,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 @@ -2462,12 +2462,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)] @@ -8351,6 +8351,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)) @@ -8372,6 +8373,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))) @@ -10562,6 +10564,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) @@ -10576,7 +10579,7 @@ reduces them without incurring seq initialization" (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))) @@ -11942,7 +11945,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? @@ -12175,6 +12178,8 @@ reduces them without incurring seq initialization" Object (findInternedVar [this sym] (let [k (munge (str_ sym))] + ;; FIXME: this shouldn't need ^boolean due to GCL library analysis, + ;; but not currently working (when ^boolean (gobject/containsKey obj k) (let [var-sym (symbol (str_ name) (str_ sym)) var-meta {:ns this}] @@ -12268,7 +12273,7 @@ reduces them without incurring seq initialization" (when (nil? NS_CACHE) (set! NS_CACHE (atom {}))) (let [ns-str (str_ ns) - ns (if (not ^boolean (gstring/contains ns-str "$macros")) + ns (if (not (gstring/contains ns-str "$macros")) (symbol (str_ ns-str "$macros")) ns) the-ns (get @NS_CACHE ns)] @@ -12292,7 +12297,7 @@ reduces them without incurring seq initialization" [x] (instance? goog.Uri x)) -(defn ^boolean NaN? +(defn NaN? "Returns true if num is NaN, else false" [val] (js/isNaN val)) @@ -12321,6 +12326,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]*" diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index a13c08545..2a6364c8c 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -980,10 +980,10 @@ (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 (->> (string/split (name x) #"\.") (map symbol)) + [xs y] ((juxt butlast last) props)] + (with-meta 'js + {:prefix (vec (concat xs [(with-meta y {:ctor true})]))})) x)) (defn ->type-set @@ -1030,46 +1030,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)) + (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 +1121,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)] @@ -1274,8 +1318,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 +1329,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)] @@ -2585,12 +2632,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" @@ -3543,13 +3590,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) @@ -3581,7 +3640,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 diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index b96c09b36..fcc03ab96 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -641,7 +641,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]}] diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index c5343e1b1..d25987cde 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -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) diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index bb6a9bfc3..95204e650 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,22 @@ 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)))))) + ;; CLJS-1225 (comment diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 8ca7ff9aa..967164d1f 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 ed0cfdb70..e5a399c84 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 c9c5f6343..5435cc90f 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")))) @@ -374,3 +394,23 @@ (: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))))) + ;; FIXME: infers any instead of boolean, nothing wrong w/ the externs parsing + ;; but this definitely does not work at the moment + #_(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)))))) + From aa5e7516e5031f81857ded5e0d2a2a476d4cfaff Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 6 Jul 2025 13:38:14 -0400 Subject: [PATCH 3952/4033] CLJS-3439: REPL doc support for externs (#261) * break out ->pre * add cljs.analyzer.api/resolve-extern * new doc lookup path in repl for js/foo symbols --- src/main/clojure/cljs/analyzer.cljc | 5 ++++- src/main/clojure/cljs/analyzer/api.cljc | 9 +++++++++ src/main/clojure/cljs/repl.cljc | 5 +++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 2a6364c8c..ac521fa78 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -977,10 +977,13 @@ (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) - (let [props (->> (string/split (name x) #"\.") (map symbol)) + (let [props (->pre x) [xs y] ((juxt butlast last) props)] (with-meta 'js {:prefix (vec (concat xs [(with-meta y {:ctor true})]))})) diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc index 2d143a42b..2fa4f2a13 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/repl.cljc b/src/main/clojure/cljs/repl.cljc index b5c53738a..b685a62e5 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1447,6 +1447,11 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (keyword? name) `(cljs.repl/print-doc {:spec ~name :doc (cljs.spec.alpha/describe ~name)}) + (= "js" (namespace name)) + `(cljs.repl/print-doc + (quote ~(merge (select-keys (ana-api/resolve-extern name) [:doc :arglists]) + {:name name}))) + (ana-api/find-ns name) `(cljs.repl/print-doc (quote ~(select-keys (ana-api/find-ns name) [:name :doc]))) From 60c9055bb037a5111249ec6d846571bd6e57a9a3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 7 Jul 2025 14:03:04 -0400 Subject: [PATCH 3953/4033] CLJS-3438: Inference for `goog.object/containsKey` returns any, not boolean (#262) - fix cljs.analyzer/desugar-dotted-expr, generated malformed AST in the case of goog.module var - compiler test for goog.object/containsKey - fix parameter parsing in cljs.externs to properly handle var args and optional arguments - fix fn-arity warning so that we use unaliased names if available (goog.module names are aliases) - cljs.core: goog.object/containsKey hint no longer needed --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/analyzer.cljc | 23 ++++++++---- src/main/clojure/cljs/externs.clj | 37 ++++++++++++------- src/test/clojure/cljs/compiler_tests.clj | 10 +++++ .../clojure/cljs/type_inference_tests.clj | 5 +-- 5 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index e1c7b067e..efbde48e3 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12180,7 +12180,7 @@ reduces them without incurring seq initialization" (let [k (munge (str_ sym))] ;; FIXME: this shouldn't need ^boolean due to GCL library analysis, ;; but not currently working - (when ^boolean (gobject/containsKey obj k) + (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))))) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ac521fa78..709531e59 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1214,9 +1214,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] @@ -3887,15 +3890,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) @@ -3946,7 +3949,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) @@ -3954,6 +3960,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 diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index d25987cde..e7bf3014b 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -13,9 +13,9 @@ [clojure.string :as string]) (:import [com.google.javascript.jscomp CompilerOptions CompilerOptions$Environment SourceFile CompilerInput CommandLineRunner] - [com.google.javascript.jscomp.parsing Config$JsDocParsing] + [com.google.javascript.jscomp.parsing Config$JsDocParsing JsDocInfoParser$ExtendedTypeInfo] [com.google.javascript.rhino - Node Token JSTypeExpression JSDocInfo$Visibility] + Node Token JSTypeExpression JSDocInfo JSDocInfo$Visibility] [java.util.logging Level] [java.net URL])) @@ -88,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)] @@ -108,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)] @@ -124,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/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj index 95204e650..f6f7b560b 100644 --- a/src/test/clojure/cljs/compiler_tests.clj +++ b/src/test/clojure/cljs/compiler_tests.clj @@ -391,6 +391,16 @@ (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/type_inference_tests.clj b/src/test/clojure/cljs/type_inference_tests.clj index 5435cc90f..2bd0855c3 100644 --- a/src/test/clojure/cljs/type_inference_tests.clj +++ b/src/test/clojure/cljs/type_inference_tests.clj @@ -403,9 +403,7 @@ (:require [goog.string :as gstring])) (gstring/contains "foobar" "foo")] {} true))))) - ;; FIXME: infers any instead of boolean, nothing wrong w/ the externs parsing - ;; but this definitely does not work at the moment - #_(is (= 'boolean + (is (= 'boolean (:tag (env/with-compiler-env (env/default-compiler-env) (ana/analyze-form-seq @@ -413,4 +411,3 @@ (:require [goog.object :as gobject])) (gobject/containsKey (js-object) "foo")] {} true)))))) - From e1328b7b99d376335bde3e5a0d59c6fab85cebeb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 7 Jul 2025 14:07:08 -0400 Subject: [PATCH 3954/4033] - remove unused import from last commit --- src/main/clojure/cljs/externs.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj index e7bf3014b..e354aac74 100644 --- a/src/main/clojure/cljs/externs.clj +++ b/src/main/clojure/cljs/externs.clj @@ -13,7 +13,7 @@ [clojure.string :as string]) (:import [com.google.javascript.jscomp CompilerOptions CompilerOptions$Environment SourceFile CompilerInput CommandLineRunner] - [com.google.javascript.jscomp.parsing Config$JsDocParsing JsDocInfoParser$ExtendedTypeInfo] + [com.google.javascript.jscomp.parsing Config$JsDocParsing] [com.google.javascript.rhino Node Token JSTypeExpression JSDocInfo JSDocInfo$Visibility] [java.util.logging Level] From 5027991ea61a2018f008d1f6a81cef025019ee8d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 7 Jul 2025 15:45:30 -0400 Subject: [PATCH 3955/4033] remove FIXME comment for gobj/containsKey usage in cljs.core --- src/main/cljs/cljs/core.cljs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index efbde48e3..f70e544f8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12178,8 +12178,6 @@ reduces them without incurring seq initialization" Object (findInternedVar [this sym] (let [k (munge (str_ sym))] - ;; FIXME: this shouldn't need ^boolean due to GCL library analysis, - ;; but not currently working (when (gobject/containsKey obj k) (let [var-sym (symbol (str_ name) (str_ sym)) var-meta {:ns this}] From 89506d4d6109d34bfbf50c2dfd3b3bf58c9fe642 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 17:57:25 -0400 Subject: [PATCH 3956/4033] Copy over the 2011 copy-on-write versions of the data structures --- src/main/cljs/cljs/core.cljs | 501 ++++++++++++++++++++++++----------- 1 file changed, 343 insertions(+), 158 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index f70e544f8..57d578867 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6560,164 +6560,7 @@ reduces them without incurring seq initialization" (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 @@ -12400,3 +12243,345 @@ 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 + +;;; Vector + +(deftype Vector [meta array] + IWithMeta + (-with-meta [coll meta] (Vector. meta array)) + + IMeta + (-meta [coll] meta) + + IStack + (-peek [coll] + (let [count (.-length array)] + (when (> count 0) + (aget array (dec count))))) + (-pop [coll] + (if (> (.-length array) 0) + (let [new-array (aclone array)] + (. new-array (pop)) + (Vector. meta new-array)) + (throw (js/Error. "Can't pop empty vector")))) + + ICollection + (-conj [coll o] + (let [new-array (aclone array)] + (.push new-array o) + (Vector. meta new-array))) + + IEmptyableCollection + (-empty [coll] (with-meta cljs.core.Vector/EMPTY meta)) + + ISequential + IEquiv + (-equiv [coll other] (equiv-sequential coll other)) + + IHash + (-hash [coll] (hash-coll coll)) + + ISeqable + (-seq [coll] + (when (> (.-length array) 0) + (let [vector-seq + (fn vector-seq [i] + (lazy-seq + (when (< i (.-length array)) + (cons (aget array i) (vector-seq (inc i))))))] + (vector-seq 0)))) + + ICounted + (-count [coll] (.-length array)) + + IIndexed + (-nth [coll n] + (if (and (<= 0 n) (< n (.-length array))) + (aget array n) + #_(throw (js/Error. (str "No item " n " in vector of length " (.-length array)))))) + (-nth [coll n not-found] + (if (and (<= 0 n) (< n (.-length array))) + (aget array n) + not-found)) + + ILookup + (-lookup [coll k] (-nth coll k nil)) + (-lookup [coll k not-found] (-nth coll k not-found)) + + IAssociative + (-assoc [coll k v] + (let [new-array (aclone array)] + (aset new-array k v) + (Vector. meta new-array))) + + IVector + (-assoc-n [coll n val] (-assoc coll n val)) + + IReduce + (-reduce [v f] + (ci-reduce array f)) + (-reduce [v f start] + (ci-reduce array f start)) + + IFn + (-invoke [coll k] + (-lookup coll k)) + (-invoke [coll k not-found] + (-lookup coll k not-found))) + +(set! (. Vector -EMPTY) (Vector. nil (array))) + +(set! (. Vector -fromArray) (fn [xs] (Vector. nil xs))) + +; 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-contains-key? + ([k strobj] + (obj-map-contains-key? k strobj true false)) + ([k strobj true-val false-val] + (if (and (goog/isString k) (.hasOwnProperty strobj k)) + true-val + false-val))) + +(defn- obj-map-compare-keys [a b] + (let [a (hash a) + b (hash b)] + (cond + (< a b) -1 + (> a b) 1 + :else 0))) + +(deftype ObjMap [meta keys strobj] + IWithMeta + (-with-meta [coll meta] (ObjMap. meta keys strobj)) + + 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 cljs.core.ObjMap/EMPTY meta)) + + IEquiv + (-equiv [coll other] (equiv-map coll other)) + + IHash + (-hash [coll] (hash-coll coll)) + + ISeqable + (-seq [coll] + (when (pos? (.-length keys)) + (map #(vector % (aget strobj %)) + (.sort keys obj-map-compare-keys)))) + + ICounted + (-count [coll] (.-length keys)) + + ILookup + (-lookup [coll k] (-lookup coll k nil)) + (-lookup [coll k not-found] + (obj-map-contains-key? k strobj (aget strobj k) not-found)) + + IAssociative + (-assoc [coll k v] + (if (goog/isString k) + (let [new-strobj (goog.object/clone strobj) + overwrite? (.hasOwnProperty new-strobj k)] + (aset new-strobj k v) + (if overwrite? + (ObjMap. meta keys new-strobj) ; overwrite + (let [new-keys (aclone keys)] ; append + (.push new-keys k) + (ObjMap. meta new-keys new-strobj)))) + ; non-string key. game over. + (with-meta (into (hash-map k v) (seq coll)) meta))) + (-contains-key? [coll k] + (obj-map-contains-key? k strobj)) + + IMap + (-dissoc [coll k] + (if (and (goog/isString k) (.hasOwnProperty strobj k)) + (let [new-keys (aclone keys) + new-strobj (goog.object/clone strobj)] + (.splice new-keys (scan-array 1 k new-keys) 1) + (js-delete new-strobj k) + (ObjMap. meta new-keys new-strobj)) + coll)) ; key not found, return coll unchanged + + IFn + (-invoke [coll k] + (-lookup coll k)) + (-invoke [coll k not-found] + (-lookup coll k not-found))) + +(set! (. ObjMap -EMPTY) (ObjMap. nil (array) (js-obj))) + +(set! (. ObjMap -fromObject) (fn [ks obj] (ObjMap. nil ks obj))) + +; 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 HashMap [meta count hashobj] + IWithMeta + (-with-meta [coll meta] (HashMap. meta count hashobj)) + + 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 cljs.core.HashMap/EMPTY meta)) + + IEquiv + (-equiv [coll other] (equiv-map coll other)) + + IHash + (-hash [coll] (hash-coll coll)) + + ISeqable + (-seq [coll] + (when (pos? count) + (let [hashes (.sort (js-keys hashobj))] + (mapcat #(map vec (partition 2 (aget hashobj %))) + hashes)))) + + ICounted + (-count [coll] count) + + ILookup + (-lookup [coll k] (-lookup coll k nil)) + (-lookup [coll k not-found] + (let [bucket (aget hashobj (hash k)) + i (when bucket (scan-array 2 k bucket))] + (if i + (aget bucket (inc i)) + not-found))) + + IAssociative + (-assoc [coll k v] + (let [h (hash k) + bucket (aget hashobj h)] + (if bucket + (let [new-bucket (aclone bucket) + new-hashobj (goog.object/clone hashobj)] + (aset new-hashobj h new-bucket) + (if-let [i (scan-array 2 k new-bucket)] + (do ; found key, replace + (aset new-bucket (inc i) v) + (HashMap. meta count new-hashobj)) + (do ; did not find key, append + (.push new-bucket k v) + (HashMap. meta (inc count) new-hashobj)))) + (let [new-hashobj (goog.object/clone hashobj)] ; did not find bucket + (aset new-hashobj h (array k v)) + (HashMap. meta (inc count) new-hashobj))))) + (-contains-key? [coll k] + (let [bucket (aget hashobj (hash k)) + i (when bucket (scan-array 2 k bucket))] + (if i + true + false))) + + IMap + (-dissoc [coll k] + (let [h (hash k) + bucket (aget hashobj h) + i (when bucket (scan-array 2 k bucket))] + (if (not i) + coll ; key not found, return coll unchanged + (let [new-hashobj (goog.object/clone hashobj)] + (if (> 3 (.-length bucket)) + (js-delete new-hashobj h) + (let [new-bucket (aclone bucket)] + (.splice new-bucket i 2) + (aset new-hashobj h new-bucket))) + (HashMap. meta (dec count) new-hashobj))))) + + IFn + (-invoke [coll k] + (-lookup coll k)) + (-invoke [coll k not-found] + (-lookup coll k not-found))) + +(set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj))) + +(set! cljs.core.HashMap/fromArrays (fn [ks vs] + (let [len (.-length ks)] + (loop [i 0, out cljs.core.HashMap/EMPTY] + (if (< i len) + (recur (inc i) (assoc out (aget ks i) (aget vs i))) + out))))) + +(deftype Set [meta hash-map] + IWithMeta + (-with-meta [coll meta] (Set. meta hash-map)) + + IMeta + (-meta [coll] meta) + + ICollection + (-conj [coll o] + (Set. meta (assoc hash-map o nil))) + + IEmptyableCollection + (-empty [coll] (with-meta cljs.core.Set/EMPTY meta)) + + IEquiv + (-equiv [coll other] + (and + (set? other) + (= (count coll) (count other)) + (every? #(contains? coll %) + other))) + + IHash + (-hash [coll] (hash-coll coll)) + + ISeqable + (-seq [coll] (keys hash-map)) + + ICounted + (-count [coll] (count (seq coll))) + + ILookup + (-lookup [coll v] + (-lookup coll v nil)) + (-lookup [coll v not-found] + (if (-contains-key? hash-map v) + v + not-found)) + + ISet + (-disjoin [coll v] + (Set. meta (dissoc hash-map v))) + + IFn + (-invoke [coll k] + (-lookup coll k)) + (-invoke [coll k not-found] + (-lookup coll k not-found))) + +(set! (. Set -EMPTY) (Set. nil (hash-map))) From 45e34d261c3abd7d8d5642fee4b1cfa891119a92 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 18:03:15 -0400 Subject: [PATCH 3957/4033] revert additions to master. Keep ObjMap removed for now. --- src/main/cljs/cljs/core.cljs | 342 ----------------------------------- 1 file changed, 342 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 57d578867..8458f7e16 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12243,345 +12243,3 @@ 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 - -;;; Vector - -(deftype Vector [meta array] - IWithMeta - (-with-meta [coll meta] (Vector. meta array)) - - IMeta - (-meta [coll] meta) - - IStack - (-peek [coll] - (let [count (.-length array)] - (when (> count 0) - (aget array (dec count))))) - (-pop [coll] - (if (> (.-length array) 0) - (let [new-array (aclone array)] - (. new-array (pop)) - (Vector. meta new-array)) - (throw (js/Error. "Can't pop empty vector")))) - - ICollection - (-conj [coll o] - (let [new-array (aclone array)] - (.push new-array o) - (Vector. meta new-array))) - - IEmptyableCollection - (-empty [coll] (with-meta cljs.core.Vector/EMPTY meta)) - - ISequential - IEquiv - (-equiv [coll other] (equiv-sequential coll other)) - - IHash - (-hash [coll] (hash-coll coll)) - - ISeqable - (-seq [coll] - (when (> (.-length array) 0) - (let [vector-seq - (fn vector-seq [i] - (lazy-seq - (when (< i (.-length array)) - (cons (aget array i) (vector-seq (inc i))))))] - (vector-seq 0)))) - - ICounted - (-count [coll] (.-length array)) - - IIndexed - (-nth [coll n] - (if (and (<= 0 n) (< n (.-length array))) - (aget array n) - #_(throw (js/Error. (str "No item " n " in vector of length " (.-length array)))))) - (-nth [coll n not-found] - (if (and (<= 0 n) (< n (.-length array))) - (aget array n) - not-found)) - - ILookup - (-lookup [coll k] (-nth coll k nil)) - (-lookup [coll k not-found] (-nth coll k not-found)) - - IAssociative - (-assoc [coll k v] - (let [new-array (aclone array)] - (aset new-array k v) - (Vector. meta new-array))) - - IVector - (-assoc-n [coll n val] (-assoc coll n val)) - - IReduce - (-reduce [v f] - (ci-reduce array f)) - (-reduce [v f start] - (ci-reduce array f start)) - - IFn - (-invoke [coll k] - (-lookup coll k)) - (-invoke [coll k not-found] - (-lookup coll k not-found))) - -(set! (. Vector -EMPTY) (Vector. nil (array))) - -(set! (. Vector -fromArray) (fn [xs] (Vector. nil xs))) - -; 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-contains-key? - ([k strobj] - (obj-map-contains-key? k strobj true false)) - ([k strobj true-val false-val] - (if (and (goog/isString k) (.hasOwnProperty strobj k)) - true-val - false-val))) - -(defn- obj-map-compare-keys [a b] - (let [a (hash a) - b (hash b)] - (cond - (< a b) -1 - (> a b) 1 - :else 0))) - -(deftype ObjMap [meta keys strobj] - IWithMeta - (-with-meta [coll meta] (ObjMap. meta keys strobj)) - - 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 cljs.core.ObjMap/EMPTY meta)) - - IEquiv - (-equiv [coll other] (equiv-map coll other)) - - IHash - (-hash [coll] (hash-coll coll)) - - ISeqable - (-seq [coll] - (when (pos? (.-length keys)) - (map #(vector % (aget strobj %)) - (.sort keys obj-map-compare-keys)))) - - ICounted - (-count [coll] (.-length keys)) - - ILookup - (-lookup [coll k] (-lookup coll k nil)) - (-lookup [coll k not-found] - (obj-map-contains-key? k strobj (aget strobj k) not-found)) - - IAssociative - (-assoc [coll k v] - (if (goog/isString k) - (let [new-strobj (goog.object/clone strobj) - overwrite? (.hasOwnProperty new-strobj k)] - (aset new-strobj k v) - (if overwrite? - (ObjMap. meta keys new-strobj) ; overwrite - (let [new-keys (aclone keys)] ; append - (.push new-keys k) - (ObjMap. meta new-keys new-strobj)))) - ; non-string key. game over. - (with-meta (into (hash-map k v) (seq coll)) meta))) - (-contains-key? [coll k] - (obj-map-contains-key? k strobj)) - - IMap - (-dissoc [coll k] - (if (and (goog/isString k) (.hasOwnProperty strobj k)) - (let [new-keys (aclone keys) - new-strobj (goog.object/clone strobj)] - (.splice new-keys (scan-array 1 k new-keys) 1) - (js-delete new-strobj k) - (ObjMap. meta new-keys new-strobj)) - coll)) ; key not found, return coll unchanged - - IFn - (-invoke [coll k] - (-lookup coll k)) - (-invoke [coll k not-found] - (-lookup coll k not-found))) - -(set! (. ObjMap -EMPTY) (ObjMap. nil (array) (js-obj))) - -(set! (. ObjMap -fromObject) (fn [ks obj] (ObjMap. nil ks obj))) - -; 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 HashMap [meta count hashobj] - IWithMeta - (-with-meta [coll meta] (HashMap. meta count hashobj)) - - 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 cljs.core.HashMap/EMPTY meta)) - - IEquiv - (-equiv [coll other] (equiv-map coll other)) - - IHash - (-hash [coll] (hash-coll coll)) - - ISeqable - (-seq [coll] - (when (pos? count) - (let [hashes (.sort (js-keys hashobj))] - (mapcat #(map vec (partition 2 (aget hashobj %))) - hashes)))) - - ICounted - (-count [coll] count) - - ILookup - (-lookup [coll k] (-lookup coll k nil)) - (-lookup [coll k not-found] - (let [bucket (aget hashobj (hash k)) - i (when bucket (scan-array 2 k bucket))] - (if i - (aget bucket (inc i)) - not-found))) - - IAssociative - (-assoc [coll k v] - (let [h (hash k) - bucket (aget hashobj h)] - (if bucket - (let [new-bucket (aclone bucket) - new-hashobj (goog.object/clone hashobj)] - (aset new-hashobj h new-bucket) - (if-let [i (scan-array 2 k new-bucket)] - (do ; found key, replace - (aset new-bucket (inc i) v) - (HashMap. meta count new-hashobj)) - (do ; did not find key, append - (.push new-bucket k v) - (HashMap. meta (inc count) new-hashobj)))) - (let [new-hashobj (goog.object/clone hashobj)] ; did not find bucket - (aset new-hashobj h (array k v)) - (HashMap. meta (inc count) new-hashobj))))) - (-contains-key? [coll k] - (let [bucket (aget hashobj (hash k)) - i (when bucket (scan-array 2 k bucket))] - (if i - true - false))) - - IMap - (-dissoc [coll k] - (let [h (hash k) - bucket (aget hashobj h) - i (when bucket (scan-array 2 k bucket))] - (if (not i) - coll ; key not found, return coll unchanged - (let [new-hashobj (goog.object/clone hashobj)] - (if (> 3 (.-length bucket)) - (js-delete new-hashobj h) - (let [new-bucket (aclone bucket)] - (.splice new-bucket i 2) - (aset new-hashobj h new-bucket))) - (HashMap. meta (dec count) new-hashobj))))) - - IFn - (-invoke [coll k] - (-lookup coll k)) - (-invoke [coll k not-found] - (-lookup coll k not-found))) - -(set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj))) - -(set! cljs.core.HashMap/fromArrays (fn [ks vs] - (let [len (.-length ks)] - (loop [i 0, out cljs.core.HashMap/EMPTY] - (if (< i len) - (recur (inc i) (assoc out (aget ks i) (aget vs i))) - out))))) - -(deftype Set [meta hash-map] - IWithMeta - (-with-meta [coll meta] (Set. meta hash-map)) - - IMeta - (-meta [coll] meta) - - ICollection - (-conj [coll o] - (Set. meta (assoc hash-map o nil))) - - IEmptyableCollection - (-empty [coll] (with-meta cljs.core.Set/EMPTY meta)) - - IEquiv - (-equiv [coll other] - (and - (set? other) - (= (count coll) (count other)) - (every? #(contains? coll %) - other))) - - IHash - (-hash [coll] (hash-coll coll)) - - ISeqable - (-seq [coll] (keys hash-map)) - - ICounted - (-count [coll] (count (seq coll))) - - ILookup - (-lookup [coll v] - (-lookup coll v nil)) - (-lookup [coll v not-found] - (if (-contains-key? hash-map v) - v - not-found)) - - ISet - (-disjoin [coll v] - (Set. meta (dissoc hash-map v))) - - IFn - (-invoke [coll k] - (-lookup coll k)) - (-invoke [coll k not-found] - (-lookup coll k not-found))) - -(set! (. Set -EMPTY) (Set. nil (hash-map))) From 9c4f2efc1233922706827a31cb92b00c5e2a8768 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 18:17:50 -0400 Subject: [PATCH 3958/4033] - remove straggling ObjMap stuff - cleanup ObjMap tests, either comment out, or remove things which will no longer be relevant (promotion to PHM) --- src/main/cljs/cljs/core.cljs | 17 ----------------- src/test/cljs/cljs/collections_test.cljs | 10 ++-------- src/test/cljs/cljs/core_test.cljs | 4 ++-- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8458f7e16..cae817d98 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9034,19 +9034,6 @@ reduces them without incurring seq initialization" (.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))))) - (defn sorted-map "keyval => key val Returns a new sorted map with supplied mappings." @@ -10698,10 +10685,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)) diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 3c85985c0..44d5e3f46 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -21,8 +21,8 @@ (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))) @@ -879,12 +879,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))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 58720b5a1..9d9b4306e 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -196,9 +196,9 @@ (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) From e5c984041d150b6c67ad5756d34b3b86fbd6133f Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 18:44:17 -0400 Subject: [PATCH 3959/4033] fully revert the obj-map changes for now, not worth the hassle - it's coming back anyway. so the only real change is the removal of the promotion test which we want to keep. --- src/main/cljs/cljs/core.cljs | 176 ++++++++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index cae817d98..4305440a8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6560,7 +6560,164 @@ reduces them without incurring seq initialization" (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 @@ -9034,6 +9191,19 @@ reduces them without incurring seq initialization" (.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))))) + (defn sorted-map "keyval => key val Returns a new sorted map with supplied mappings." @@ -10685,6 +10855,10 @@ 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)) From d998f1384cfc2de437f8f4dbec50528f0f08e688 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 18:06:39 -0400 Subject: [PATCH 3960/4033] readd the changes --- src/main/cljs/cljs/core.cljs | 342 +++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4305440a8..a76f1f191 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12400,3 +12400,345 @@ 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 + +;;; Vector + +(deftype Vector [meta array] + IWithMeta + (-with-meta [coll meta] (Vector. meta array)) + + IMeta + (-meta [coll] meta) + + IStack + (-peek [coll] + (let [count (.-length array)] + (when (> count 0) + (aget array (dec count))))) + (-pop [coll] + (if (> (.-length array) 0) + (let [new-array (aclone array)] + (. new-array (pop)) + (Vector. meta new-array)) + (throw (js/Error. "Can't pop empty vector")))) + + ICollection + (-conj [coll o] + (let [new-array (aclone array)] + (.push new-array o) + (Vector. meta new-array))) + + IEmptyableCollection + (-empty [coll] (with-meta cljs.core.Vector/EMPTY meta)) + + ISequential + IEquiv + (-equiv [coll other] (equiv-sequential coll other)) + + IHash + (-hash [coll] (hash-coll coll)) + + ISeqable + (-seq [coll] + (when (> (.-length array) 0) + (let [vector-seq + (fn vector-seq [i] + (lazy-seq + (when (< i (.-length array)) + (cons (aget array i) (vector-seq (inc i))))))] + (vector-seq 0)))) + + ICounted + (-count [coll] (.-length array)) + + IIndexed + (-nth [coll n] + (if (and (<= 0 n) (< n (.-length array))) + (aget array n) + #_(throw (js/Error. (str "No item " n " in vector of length " (.-length array)))))) + (-nth [coll n not-found] + (if (and (<= 0 n) (< n (.-length array))) + (aget array n) + not-found)) + + ILookup + (-lookup [coll k] (-nth coll k nil)) + (-lookup [coll k not-found] (-nth coll k not-found)) + + IAssociative + (-assoc [coll k v] + (let [new-array (aclone array)] + (aset new-array k v) + (Vector. meta new-array))) + + IVector + (-assoc-n [coll n val] (-assoc coll n val)) + + IReduce + (-reduce [v f] + (ci-reduce array f)) + (-reduce [v f start] + (ci-reduce array f start)) + + IFn + (-invoke [coll k] + (-lookup coll k)) + (-invoke [coll k not-found] + (-lookup coll k not-found))) + +(set! (. Vector -EMPTY) (Vector. nil (array))) + +(set! (. Vector -fromArray) (fn [xs] (Vector. nil xs))) + +; 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-contains-key? + ([k strobj] + (obj-map-contains-key? k strobj true false)) + ([k strobj true-val false-val] + (if (and (goog/isString k) (.hasOwnProperty strobj k)) + true-val + false-val))) + +(defn- obj-map-compare-keys [a b] + (let [a (hash a) + b (hash b)] + (cond + (< a b) -1 + (> a b) 1 + :else 0))) + +(deftype ObjMap [meta keys strobj] + IWithMeta + (-with-meta [coll meta] (ObjMap. meta keys strobj)) + + 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 cljs.core.ObjMap/EMPTY meta)) + + IEquiv + (-equiv [coll other] (equiv-map coll other)) + + IHash + (-hash [coll] (hash-coll coll)) + + ISeqable + (-seq [coll] + (when (pos? (.-length keys)) + (map #(vector % (aget strobj %)) + (.sort keys obj-map-compare-keys)))) + + ICounted + (-count [coll] (.-length keys)) + + ILookup + (-lookup [coll k] (-lookup coll k nil)) + (-lookup [coll k not-found] + (obj-map-contains-key? k strobj (aget strobj k) not-found)) + + IAssociative + (-assoc [coll k v] + (if (goog/isString k) + (let [new-strobj (goog.object/clone strobj) + overwrite? (.hasOwnProperty new-strobj k)] + (aset new-strobj k v) + (if overwrite? + (ObjMap. meta keys new-strobj) ; overwrite + (let [new-keys (aclone keys)] ; append + (.push new-keys k) + (ObjMap. meta new-keys new-strobj)))) + ; non-string key. game over. + (with-meta (into (hash-map k v) (seq coll)) meta))) + (-contains-key? [coll k] + (obj-map-contains-key? k strobj)) + + IMap + (-dissoc [coll k] + (if (and (goog/isString k) (.hasOwnProperty strobj k)) + (let [new-keys (aclone keys) + new-strobj (goog.object/clone strobj)] + (.splice new-keys (scan-array 1 k new-keys) 1) + (js-delete new-strobj k) + (ObjMap. meta new-keys new-strobj)) + coll)) ; key not found, return coll unchanged + + IFn + (-invoke [coll k] + (-lookup coll k)) + (-invoke [coll k not-found] + (-lookup coll k not-found))) + +(set! (. ObjMap -EMPTY) (ObjMap. nil (array) (js-obj))) + +(set! (. ObjMap -fromObject) (fn [ks obj] (ObjMap. nil ks obj))) + +; 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 HashMap [meta count hashobj] + IWithMeta + (-with-meta [coll meta] (HashMap. meta count hashobj)) + + 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 cljs.core.HashMap/EMPTY meta)) + + IEquiv + (-equiv [coll other] (equiv-map coll other)) + + IHash + (-hash [coll] (hash-coll coll)) + + ISeqable + (-seq [coll] + (when (pos? count) + (let [hashes (.sort (js-keys hashobj))] + (mapcat #(map vec (partition 2 (aget hashobj %))) + hashes)))) + + ICounted + (-count [coll] count) + + ILookup + (-lookup [coll k] (-lookup coll k nil)) + (-lookup [coll k not-found] + (let [bucket (aget hashobj (hash k)) + i (when bucket (scan-array 2 k bucket))] + (if i + (aget bucket (inc i)) + not-found))) + + IAssociative + (-assoc [coll k v] + (let [h (hash k) + bucket (aget hashobj h)] + (if bucket + (let [new-bucket (aclone bucket) + new-hashobj (goog.object/clone hashobj)] + (aset new-hashobj h new-bucket) + (if-let [i (scan-array 2 k new-bucket)] + (do ; found key, replace + (aset new-bucket (inc i) v) + (HashMap. meta count new-hashobj)) + (do ; did not find key, append + (.push new-bucket k v) + (HashMap. meta (inc count) new-hashobj)))) + (let [new-hashobj (goog.object/clone hashobj)] ; did not find bucket + (aset new-hashobj h (array k v)) + (HashMap. meta (inc count) new-hashobj))))) + (-contains-key? [coll k] + (let [bucket (aget hashobj (hash k)) + i (when bucket (scan-array 2 k bucket))] + (if i + true + false))) + + IMap + (-dissoc [coll k] + (let [h (hash k) + bucket (aget hashobj h) + i (when bucket (scan-array 2 k bucket))] + (if (not i) + coll ; key not found, return coll unchanged + (let [new-hashobj (goog.object/clone hashobj)] + (if (> 3 (.-length bucket)) + (js-delete new-hashobj h) + (let [new-bucket (aclone bucket)] + (.splice new-bucket i 2) + (aset new-hashobj h new-bucket))) + (HashMap. meta (dec count) new-hashobj))))) + + IFn + (-invoke [coll k] + (-lookup coll k)) + (-invoke [coll k not-found] + (-lookup coll k not-found))) + +(set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj))) + +(set! cljs.core.HashMap/fromArrays (fn [ks vs] + (let [len (.-length ks)] + (loop [i 0, out cljs.core.HashMap/EMPTY] + (if (< i len) + (recur (inc i) (assoc out (aget ks i) (aget vs i))) + out))))) + +(deftype Set [meta hash-map] + IWithMeta + (-with-meta [coll meta] (Set. meta hash-map)) + + IMeta + (-meta [coll] meta) + + ICollection + (-conj [coll o] + (Set. meta (assoc hash-map o nil))) + + IEmptyableCollection + (-empty [coll] (with-meta cljs.core.Set/EMPTY meta)) + + IEquiv + (-equiv [coll other] + (and + (set? other) + (= (count coll) (count other)) + (every? #(contains? coll %) + other))) + + IHash + (-hash [coll] (hash-coll coll)) + + ISeqable + (-seq [coll] (keys hash-map)) + + ICounted + (-count [coll] (count (seq coll))) + + ILookup + (-lookup [coll v] + (-lookup coll v nil)) + (-lookup [coll v not-found] + (if (-contains-key? hash-map v) + v + not-found)) + + ISet + (-disjoin [coll v] + (Set. meta (dissoc hash-map v))) + + IFn + (-invoke [coll k] + (-lookup coll k)) + (-invoke [coll k not-found] + (-lookup coll k not-found))) + +(set! (. Set -EMPTY) (Set. nil (hash-map))) From f9d7b1718e6c750160c3dca6f292bfe3d92b726f Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 18:28:51 -0400 Subject: [PATCH 3961/4033] - add back printing logic --- src/main/cljs/cljs/core.cljs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a76f1f191..62415e1ba 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12487,7 +12487,10 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-lookup coll k)) (-invoke [coll k not-found] - (-lookup coll k not-found))) + (-lookup coll k not-found)) + + IPrintWithWriter + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))) (set! (. Vector -EMPTY) (Vector. nil (array))) @@ -12583,12 +12586,29 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-lookup coll k)) (-invoke [coll k not-found] - (-lookup coll k not-found))) + (-lookup coll k not-found)) + + IPrintWithWriter + (-pr-writer [coll writer opts] + (print-map coll pr-writer writer opts))) (set! (. ObjMap -EMPTY) (ObjMap. nil (array) (js-obj))) (set! (. ObjMap -fromObject) (fn [ks obj] (ObjMap. nil ks obj))) +(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))))) + ; 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 @@ -12681,7 +12701,11 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-lookup coll k)) (-invoke [coll k not-found] - (-lookup coll k not-found))) + (-lookup coll k not-found)) + + IPrintWithWriter + (-pr-writer [coll writer opts] + (print-map coll pr-writer writer opts))) (set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj))) @@ -12739,6 +12763,9 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-lookup coll k)) (-invoke [coll k not-found] - (-lookup coll k not-found))) + (-lookup coll k not-found)) + + IPrintWithWriter + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "#{" " " "}" opts coll))) (set! (. Set -EMPTY) (Set. nil (hash-map))) From 1f38c967be9c8a19a451bccd3b6583e3768ec8c9 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 19:04:37 -0400 Subject: [PATCH 3962/4033] remove the deprecated access pattern that clashed w/ namespaces --- src/main/cljs/cljs/core.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 62415e1ba..d04fd38db 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12432,7 +12432,7 @@ reduces them without incurring seq initialization" (Vector. meta new-array))) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.Vector/EMPTY meta)) + (-empty [coll] (with-meta (. Vector -EMPTY) meta)) ISequential IEquiv @@ -12534,7 +12534,7 @@ reduces them without incurring seq initialization" entry))) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.ObjMap/EMPTY meta)) + (-empty [coll] (with-meta (. ObjMap -EMPTY) meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -12630,7 +12630,7 @@ reduces them without incurring seq initialization" entry))) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.HashMap/EMPTY meta)) + (-empty [coll] (with-meta (. HashMap -EMPTY) meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -12709,9 +12709,9 @@ reduces them without incurring seq initialization" (set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj))) -(set! cljs.core.HashMap/fromArrays (fn [ks vs] +(set! (. HashMap -fromArrays) (fn [ks vs] (let [len (.-length ks)] - (loop [i 0, out cljs.core.HashMap/EMPTY] + (loop [i 0, out (. HashMap -EMPTY)] (if (< i len) (recur (inc i) (assoc out (aget ks i) (aget vs i))) out))))) @@ -12728,7 +12728,7 @@ reduces them without incurring seq initialization" (Set. meta (assoc hash-map o nil))) IEmptyableCollection - (-empty [coll] (with-meta cljs.core.Set/EMPTY meta)) + (-empty [coll] (with-meta (. Set -EMPTY) meta)) IEquiv (-equiv [coll other] From 9ca79311dd27f8f84a0d197f9ee3eb64fba823ff Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 19:10:14 -0400 Subject: [PATCH 3963/4033] Revert "remove the deprecated access pattern that clashed w/ namespaces" This reverts commit 1f38c967be9c8a19a451bccd3b6583e3768ec8c9. --- src/main/cljs/cljs/core.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d04fd38db..62415e1ba 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12432,7 +12432,7 @@ reduces them without incurring seq initialization" (Vector. meta new-array))) IEmptyableCollection - (-empty [coll] (with-meta (. Vector -EMPTY) meta)) + (-empty [coll] (with-meta cljs.core.Vector/EMPTY meta)) ISequential IEquiv @@ -12534,7 +12534,7 @@ reduces them without incurring seq initialization" entry))) IEmptyableCollection - (-empty [coll] (with-meta (. ObjMap -EMPTY) meta)) + (-empty [coll] (with-meta cljs.core.ObjMap/EMPTY meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -12630,7 +12630,7 @@ reduces them without incurring seq initialization" entry))) IEmptyableCollection - (-empty [coll] (with-meta (. HashMap -EMPTY) meta)) + (-empty [coll] (with-meta cljs.core.HashMap/EMPTY meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -12709,9 +12709,9 @@ reduces them without incurring seq initialization" (set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj))) -(set! (. HashMap -fromArrays) (fn [ks vs] +(set! cljs.core.HashMap/fromArrays (fn [ks vs] (let [len (.-length ks)] - (loop [i 0, out (. HashMap -EMPTY)] + (loop [i 0, out cljs.core.HashMap/EMPTY] (if (< i len) (recur (inc i) (assoc out (aget ks i) (aget vs i))) out))))) @@ -12728,7 +12728,7 @@ reduces them without incurring seq initialization" (Set. meta (assoc hash-map o nil))) IEmptyableCollection - (-empty [coll] (with-meta (. Set -EMPTY) meta)) + (-empty [coll] (with-meta cljs.core.Set/EMPTY meta)) IEquiv (-equiv [coll other] From 92de06bcf5d1da1e49854dc60f71a74571e97483 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 19:10:22 -0400 Subject: [PATCH 3964/4033] Revert "- add back printing logic" This reverts commit f9d7b1718e6c750160c3dca6f292bfe3d92b726f. --- src/main/cljs/cljs/core.cljs | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 62415e1ba..a76f1f191 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12487,10 +12487,7 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-lookup coll k)) (-invoke [coll k not-found] - (-lookup coll k not-found)) - - IPrintWithWriter - (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))) + (-lookup coll k not-found))) (set! (. Vector -EMPTY) (Vector. nil (array))) @@ -12586,29 +12583,12 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-lookup coll k)) (-invoke [coll k not-found] - (-lookup coll k not-found)) - - IPrintWithWriter - (-pr-writer [coll writer opts] - (print-map coll pr-writer writer opts))) + (-lookup coll k not-found))) (set! (. ObjMap -EMPTY) (ObjMap. nil (array) (js-obj))) (set! (. ObjMap -fromObject) (fn [ks obj] (ObjMap. nil ks obj))) -(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))))) - ; 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 @@ -12701,11 +12681,7 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-lookup coll k)) (-invoke [coll k not-found] - (-lookup coll k not-found)) - - IPrintWithWriter - (-pr-writer [coll writer opts] - (print-map coll pr-writer writer opts))) + (-lookup coll k not-found))) (set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj))) @@ -12763,9 +12739,6 @@ reduces them without incurring seq initialization" (-invoke [coll k] (-lookup coll k)) (-invoke [coll k not-found] - (-lookup coll k not-found)) - - IPrintWithWriter - (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "#{" " " "}" opts coll))) + (-lookup coll k not-found))) (set! (. Set -EMPTY) (Set. nil (hash-map))) From 8e6fa1ce191ec66c073b8889489ac29050f6c1db Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 29 Jul 2025 19:10:26 -0400 Subject: [PATCH 3965/4033] Revert "readd the changes" This reverts commit d998f1384cfc2de437f8f4dbec50528f0f08e688. --- src/main/cljs/cljs/core.cljs | 342 ----------------------------------- 1 file changed, 342 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index a76f1f191..4305440a8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12400,345 +12400,3 @@ 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 - -;;; Vector - -(deftype Vector [meta array] - IWithMeta - (-with-meta [coll meta] (Vector. meta array)) - - IMeta - (-meta [coll] meta) - - IStack - (-peek [coll] - (let [count (.-length array)] - (when (> count 0) - (aget array (dec count))))) - (-pop [coll] - (if (> (.-length array) 0) - (let [new-array (aclone array)] - (. new-array (pop)) - (Vector. meta new-array)) - (throw (js/Error. "Can't pop empty vector")))) - - ICollection - (-conj [coll o] - (let [new-array (aclone array)] - (.push new-array o) - (Vector. meta new-array))) - - IEmptyableCollection - (-empty [coll] (with-meta cljs.core.Vector/EMPTY meta)) - - ISequential - IEquiv - (-equiv [coll other] (equiv-sequential coll other)) - - IHash - (-hash [coll] (hash-coll coll)) - - ISeqable - (-seq [coll] - (when (> (.-length array) 0) - (let [vector-seq - (fn vector-seq [i] - (lazy-seq - (when (< i (.-length array)) - (cons (aget array i) (vector-seq (inc i))))))] - (vector-seq 0)))) - - ICounted - (-count [coll] (.-length array)) - - IIndexed - (-nth [coll n] - (if (and (<= 0 n) (< n (.-length array))) - (aget array n) - #_(throw (js/Error. (str "No item " n " in vector of length " (.-length array)))))) - (-nth [coll n not-found] - (if (and (<= 0 n) (< n (.-length array))) - (aget array n) - not-found)) - - ILookup - (-lookup [coll k] (-nth coll k nil)) - (-lookup [coll k not-found] (-nth coll k not-found)) - - IAssociative - (-assoc [coll k v] - (let [new-array (aclone array)] - (aset new-array k v) - (Vector. meta new-array))) - - IVector - (-assoc-n [coll n val] (-assoc coll n val)) - - IReduce - (-reduce [v f] - (ci-reduce array f)) - (-reduce [v f start] - (ci-reduce array f start)) - - IFn - (-invoke [coll k] - (-lookup coll k)) - (-invoke [coll k not-found] - (-lookup coll k not-found))) - -(set! (. Vector -EMPTY) (Vector. nil (array))) - -(set! (. Vector -fromArray) (fn [xs] (Vector. nil xs))) - -; 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-contains-key? - ([k strobj] - (obj-map-contains-key? k strobj true false)) - ([k strobj true-val false-val] - (if (and (goog/isString k) (.hasOwnProperty strobj k)) - true-val - false-val))) - -(defn- obj-map-compare-keys [a b] - (let [a (hash a) - b (hash b)] - (cond - (< a b) -1 - (> a b) 1 - :else 0))) - -(deftype ObjMap [meta keys strobj] - IWithMeta - (-with-meta [coll meta] (ObjMap. meta keys strobj)) - - 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 cljs.core.ObjMap/EMPTY meta)) - - IEquiv - (-equiv [coll other] (equiv-map coll other)) - - IHash - (-hash [coll] (hash-coll coll)) - - ISeqable - (-seq [coll] - (when (pos? (.-length keys)) - (map #(vector % (aget strobj %)) - (.sort keys obj-map-compare-keys)))) - - ICounted - (-count [coll] (.-length keys)) - - ILookup - (-lookup [coll k] (-lookup coll k nil)) - (-lookup [coll k not-found] - (obj-map-contains-key? k strobj (aget strobj k) not-found)) - - IAssociative - (-assoc [coll k v] - (if (goog/isString k) - (let [new-strobj (goog.object/clone strobj) - overwrite? (.hasOwnProperty new-strobj k)] - (aset new-strobj k v) - (if overwrite? - (ObjMap. meta keys new-strobj) ; overwrite - (let [new-keys (aclone keys)] ; append - (.push new-keys k) - (ObjMap. meta new-keys new-strobj)))) - ; non-string key. game over. - (with-meta (into (hash-map k v) (seq coll)) meta))) - (-contains-key? [coll k] - (obj-map-contains-key? k strobj)) - - IMap - (-dissoc [coll k] - (if (and (goog/isString k) (.hasOwnProperty strobj k)) - (let [new-keys (aclone keys) - new-strobj (goog.object/clone strobj)] - (.splice new-keys (scan-array 1 k new-keys) 1) - (js-delete new-strobj k) - (ObjMap. meta new-keys new-strobj)) - coll)) ; key not found, return coll unchanged - - IFn - (-invoke [coll k] - (-lookup coll k)) - (-invoke [coll k not-found] - (-lookup coll k not-found))) - -(set! (. ObjMap -EMPTY) (ObjMap. nil (array) (js-obj))) - -(set! (. ObjMap -fromObject) (fn [ks obj] (ObjMap. nil ks obj))) - -; 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 HashMap [meta count hashobj] - IWithMeta - (-with-meta [coll meta] (HashMap. meta count hashobj)) - - 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 cljs.core.HashMap/EMPTY meta)) - - IEquiv - (-equiv [coll other] (equiv-map coll other)) - - IHash - (-hash [coll] (hash-coll coll)) - - ISeqable - (-seq [coll] - (when (pos? count) - (let [hashes (.sort (js-keys hashobj))] - (mapcat #(map vec (partition 2 (aget hashobj %))) - hashes)))) - - ICounted - (-count [coll] count) - - ILookup - (-lookup [coll k] (-lookup coll k nil)) - (-lookup [coll k not-found] - (let [bucket (aget hashobj (hash k)) - i (when bucket (scan-array 2 k bucket))] - (if i - (aget bucket (inc i)) - not-found))) - - IAssociative - (-assoc [coll k v] - (let [h (hash k) - bucket (aget hashobj h)] - (if bucket - (let [new-bucket (aclone bucket) - new-hashobj (goog.object/clone hashobj)] - (aset new-hashobj h new-bucket) - (if-let [i (scan-array 2 k new-bucket)] - (do ; found key, replace - (aset new-bucket (inc i) v) - (HashMap. meta count new-hashobj)) - (do ; did not find key, append - (.push new-bucket k v) - (HashMap. meta (inc count) new-hashobj)))) - (let [new-hashobj (goog.object/clone hashobj)] ; did not find bucket - (aset new-hashobj h (array k v)) - (HashMap. meta (inc count) new-hashobj))))) - (-contains-key? [coll k] - (let [bucket (aget hashobj (hash k)) - i (when bucket (scan-array 2 k bucket))] - (if i - true - false))) - - IMap - (-dissoc [coll k] - (let [h (hash k) - bucket (aget hashobj h) - i (when bucket (scan-array 2 k bucket))] - (if (not i) - coll ; key not found, return coll unchanged - (let [new-hashobj (goog.object/clone hashobj)] - (if (> 3 (.-length bucket)) - (js-delete new-hashobj h) - (let [new-bucket (aclone bucket)] - (.splice new-bucket i 2) - (aset new-hashobj h new-bucket))) - (HashMap. meta (dec count) new-hashobj))))) - - IFn - (-invoke [coll k] - (-lookup coll k)) - (-invoke [coll k not-found] - (-lookup coll k not-found))) - -(set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj))) - -(set! cljs.core.HashMap/fromArrays (fn [ks vs] - (let [len (.-length ks)] - (loop [i 0, out cljs.core.HashMap/EMPTY] - (if (< i len) - (recur (inc i) (assoc out (aget ks i) (aget vs i))) - out))))) - -(deftype Set [meta hash-map] - IWithMeta - (-with-meta [coll meta] (Set. meta hash-map)) - - IMeta - (-meta [coll] meta) - - ICollection - (-conj [coll o] - (Set. meta (assoc hash-map o nil))) - - IEmptyableCollection - (-empty [coll] (with-meta cljs.core.Set/EMPTY meta)) - - IEquiv - (-equiv [coll other] - (and - (set? other) - (= (count coll) (count other)) - (every? #(contains? coll %) - other))) - - IHash - (-hash [coll] (hash-coll coll)) - - ISeqable - (-seq [coll] (keys hash-map)) - - ICounted - (-count [coll] (count (seq coll))) - - ILookup - (-lookup [coll v] - (-lookup coll v nil)) - (-lookup [coll v not-found] - (if (-contains-key? hash-map v) - v - not-found)) - - ISet - (-disjoin [coll v] - (Set. meta (dissoc hash-map v))) - - IFn - (-invoke [coll k] - (-lookup coll k)) - (-invoke [coll k not-found] - (-lookup coll k not-found))) - -(set! (. Set -EMPTY) (Set. nil (hash-map))) From 0277a7566215e177dd9edf37a2572c1e99423a71 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 5 Aug 2025 21:41:43 -0400 Subject: [PATCH 3966/4033] break out chunked-seq, es6-iterator, and interop .equiv test from cljs.core-test (#266) --- src/test/cljs/cljs/chunked_seq.cljs | 27 ++++++++++ src/test/cljs/cljs/core_test.cljs | 75 ---------------------------- src/test/cljs/cljs/interop_test.cljs | 70 ++++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 4 ++ 4 files changed, 101 insertions(+), 75 deletions(-) create mode 100644 src/test/cljs/cljs/chunked_seq.cljs create mode 100644 src/test/cljs/cljs/interop_test.cljs diff --git a/src/test/cljs/cljs/chunked_seq.cljs b/src/test/cljs/cljs/chunked_seq.cljs new file mode 100644 index 000000000..73abf707f --- /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/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 9d9b4306e..fca3dd11e 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -408,17 +408,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)] @@ -456,54 +445,6 @@ (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]))) @@ -1670,22 +1611,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*) diff --git a/src/test/cljs/cljs/interop_test.cljs b/src/test/cljs/cljs/interop_test.cljs new file mode 100644 index 000000000..ee158ec18 --- /dev/null +++ b/src/test/cljs/cljs/interop_test.cljs @@ -0,0 +1,70 @@ +; 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)) + (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})) + )) + +(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))))) + )) \ No newline at end of file diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index a0e411309..10abbdf54 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -17,6 +17,8 @@ [cljs.collections-test] [cljs.hashing-test] [cljs.core-test :as core-test] + [cljs.chunked-seq] + [cljs.interop-test] [cljs.reader-test] [cljs.binding-test] [cljs.parse-test] @@ -77,6 +79,8 @@ 'cljs.collections-test 'cljs.hashing-test 'cljs.core-test + 'cljs.chunked-seq + 'cljs.interop-test 'cljs.reader-test 'cljs.parse-test 'clojure.set-test From ddf3cfe14e71b342fbe29ff858c620752ccfc4cb Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 6 Aug 2025 07:04:58 -0400 Subject: [PATCH 3967/4033] Break out iterator tests from cljs.core-test (#268) --- src/test/cljs/cljs/core_test.cljs | 37 --------------------- src/test/cljs/cljs/iterator_test.cljs | 47 +++++++++++++++++++++++++++ src/test/cljs/test_runner.cljs | 2 ++ 3 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 src/test/cljs/cljs/iterator_test.cljs diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index fca3dd11e..686a0fd72 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -408,43 +408,6 @@ (halt-when :anomaly #(assoc %2 :partial-results %1)) [1 2 {:anomaly :oh-no!} 3 4])))) -(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-reader-literals (testing "Testing reader literals" (is (= #queue [1] (into cljs.core.PersistentQueue.EMPTY [1]))) diff --git a/src/test/cljs/cljs/iterator_test.cljs b/src/test/cljs/cljs/iterator_test.cljs new file mode 100644 index 000000000..84e9e6a4d --- /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/test_runner.cljs b/src/test/cljs/test_runner.cljs index 10abbdf54..7e551f9de 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -19,6 +19,7 @@ [cljs.core-test :as core-test] [cljs.chunked-seq] [cljs.interop-test] + [cljs.iterator-test] [cljs.reader-test] [cljs.binding-test] [cljs.parse-test] @@ -81,6 +82,7 @@ 'cljs.core-test 'cljs.chunked-seq 'cljs.interop-test + 'cljs.iterator-test 'cljs.reader-test 'cljs.parse-test 'clojure.set-test From f9a6856d91e45377391406fc34a581bc4043615e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 3 Oct 2025 22:05:31 -0400 Subject: [PATCH 3968/4033] Experimental Lite Mode (#265) new code-size compiler flags: :lite-mode, :elide-to-string :lite-mode emits updated variants of the original copy-on-write data structures from 2011. Notably, in `:lite-mode` chunked-seqs are not supported as they pull in a significant amount of code, which defeats the purpose of the new flags. Also while the transient interfaces are implemented on the copy-on-write data structures, they do not actually provide a fast path. This is not by design, but simply a practical scoping of effort as the work done here is already considerable. The other serious code-size issue are `.toString` impls. There are many interesting smaller programs that simply do not need the convenient `.toString` invoking the ClojureScript recursive printing machinery. By combining `:lite-mode true` with `:elide-to-string true` the emitted code is cut in half or more for simpler programs. For a rough idea of the space-savings, a hash-map literal is ~6K after advanced compilation with the new flags and brotli compression. ClojureScript 1.12.42 is ~17K brotli for the same program. The following expression (.log js/console (->> (map inc (range 10)) (filter even?) (partition 2) (drop 1) (mapcat identity) into-array)) Is ~6K after advanced compilation + brotli compression. ClojureScript is 1.12.42 is ~19K. For users that want the full expressiveness of the Clojure APIs and the interactivity of the REPL for smaller and/or simpler projects, but don't need every last bit of performance, these two flags should lead to compact artifacts. --- .github/workflows/test.yaml | 57 + deps.edn | 2 + resources/lite_test.edn | 28 + src/main/cljs/cljs/analyzer/passes/lite.cljc | 32 + src/main/cljs/cljs/core.cljs | 1036 +++++++++++++---- src/main/cljs/cljs/spec/alpha.cljs | 4 + src/main/clojure/cljs/analyzer.cljc | 20 +- src/main/clojure/cljs/closure.clj | 7 +- src/main/clojure/cljs/compiler.cljc | 48 +- src/main/clojure/cljs/core.cljc | 19 +- src/test/cljs/cljs/collections_test.cljs | 81 +- src/test/cljs/cljs/interop_test.cljs | 13 +- src/test/cljs/cljs/lite_collections_test.cljs | 32 + src/test/cljs/cljs/metadata_test.cljc | 12 +- src/test/cljs/cljs/seqs_test.cljs | 35 +- src/test/cljs/cljs/walk_test.cljs | 45 +- src/test/cljs/lite_test_runner.cljs | 127 ++ src/test/cljs_build/trivial/core6.cljs | 3 + src/test/clojure/cljs/analyzer_pass_tests.clj | 19 + src/test/clojure/cljs/build_api_tests.clj | 50 +- 20 files changed, 1391 insertions(+), 279 deletions(-) create mode 100644 resources/lite_test.edn create mode 100644 src/main/cljs/cljs/analyzer/passes/lite.cljc create mode 100644 src/test/cljs/cljs/lite_collections_test.cljs create mode 100644 src/test/cljs/lite_test_runner.cljs create mode 100644 src/test/cljs_build/trivial/core6.cljs diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e98aa8818..4420e9d0b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -59,6 +59,63 @@ jobs: /System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Helpers/jsc 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: macos-14 + 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 + env: + cache-name: cache-maven + with: + path: ~/.m2 + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}- + + - name: Cache gitlibs + uses: actions/cache@v4 + env: + cache-name: cache-gitlibs + with: + path: ~/.gitlibs + key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/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:lite.test.build + + # - name: Install JSC + # run: ./ci/install_jsc.sh + + - name: Run tests + run: | + /System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Helpers/jsc 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 diff --git a/deps.edn b/deps.edn index 012e22069..e3a236c2a 100644 --- a/deps.edn +++ b/deps.edn @@ -19,6 +19,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/resources/lite_test.edn b/resources/lite_test.edn new file mode 100644 index 000000000..44508575d --- /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/src/main/cljs/cljs/analyzer/passes/lite.cljc b/src/main/cljs/cljs/analyzer/passes/lite.cljc new file mode 100644 index 000000000..08a7e03de --- /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->simple-ctor + '{cljs.core/vector cljs.core/simple-vector + cljs.core/vec cljs.core/simple-vec}) + +(defn update-var [{:keys [name] :as ast}] + (let [replacement (get ctor->simple-ctor name)] + (-> ast + (assoc :name replacement) + (assoc-in [:info :name] replacement)))) + +(defn replace-var? [ast] + (and (var? ast) + (contains? ctor->simple-ctor (:name ast)))) + +(defn use-lite-types + [env ast _] + (cond-> ast + (replace-var? ast) update-var)) \ No newline at end of file diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 4305440a8..8eabe774b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -53,6 +53,11 @@ , and \"global\" supported. "} *global* "default") +(goog-define + ^{:doc "Boolean flag for LITE_MODE" + :jsdoc ["@type {boolean}"]} + LITE_MODE false) + (def ^{:dynamic true :doc "Var bound to the current namespace. Only used for bootstrapping." @@ -1763,7 +1768,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)) @@ -2070,7 +2075,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 @@ -2262,7 +2267,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 @@ -3606,7 +3614,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 discared the metadata, we change 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 @@ -6520,7 +6534,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) @@ -6552,172 +6566,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 @@ -9191,19 +9039,6 @@ reduces them without incurring seq initialization" (.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))))) - (defn sorted-map "keyval => key val Returns a new sorted map with supplied mappings." @@ -10509,8 +10344,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*) @@ -10545,13 +10382,7 @@ reduces them without incurring seq initialization" (implements? IMeta obj) (not (nil? (meta obj))))) -(defn- pr-map-entry [k v] - (reify - IMapEntry - (-key [_] k) - (-val [_] v) - ISeqable - (-seq [_] (IndexedSeq. #js [k v] 0 nil)))) +(declare Vector) (defn- pr-writer-impl [obj writer opts] @@ -10591,9 +10422,10 @@ reduces them without incurring seq initialization" (.map (js-keys obj) (fn [k] - (pr-map-entry + (MapEntry. (cond-> k (some? (.match k #"^[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*$")) keyword) - (unchecked-get obj k)))) + (unchecked-get obj k) + nil))) pr-writer writer opts)) (array? obj) @@ -10660,9 +10492,11 @@ reduces them without incurring seq initialization" (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.) @@ -10772,10 +10606,10 @@ reduces them without incurring seq initialization" (when (or (keyword? k) (symbol? k)) (if ns (when (= ns (namespace k)) - (.push lm (pr-map-entry (strip-ns k) v)) + (.push lm (MapEntry. (strip-ns k) v nil)) (recur ns entries)) (when-let [new-ns (namespace k)] - (.push lm (pr-map-entry (strip-ns k) v)) + (.push lm (MapEntry. (strip-ns k) v nil)) (recur new-ns entries)))) #js [ns lm]))))) @@ -10855,10 +10689,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)) @@ -12400,3 +12230,801 @@ 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 + +;;; Vector + +(deftype VectorIterator [arr ^:mutable i] + Object + (hasNext [_] + (< i (alength arr))) + (next [_] + (let [x (aget arr i)] + (set! i (inc i)) + x))) + +(deftype Vector [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) (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) (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 + (Vector. new-meta array __hash))) + + ICloneable + (-clone [coll] (Vector. 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)) + (Vector. 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) + (Vector. meta new-array nil))) + + IEmptyableCollection + (-empty [coll] (with-meta (. Vector -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) + (let [vector-seq + (fn vector-seq [i] + (lazy-seq + (when (< i (alength array)) + (cons (aget array i) (vector-seq (inc i))))))] + (vector-seq 0)))) + + 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) + (Vector. 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] + (VectorIterator. array 0)) + + IPrintWithWriter + (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))) + +(es6-iterable PersistentVector) + +(set! (. Vector -EMPTY) (Vector. nil (array) nil)) + +(set! (. Vector -fromArray) (fn [xs] (Vector. nil xs nil))) + +(defn simple-vector + [& args] + (if (and (instance? IndexedSeq args) (zero? (.-i args))) + (.fromArray Vector (aclone (.-arr args))) + (Vector. nil (into-array args) nil))) + +(defn simple-vec + [coll] + (cond + (map-entry? coll) + [(key coll) (val coll)] + + (vector? coll) + (with-meta coll nil) + + (array? coll) + (.fromArray Vector 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 simple-hash-map HashMap) + +(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)) + (let [new-strobj (obj-clone strobj strkeys)] + (gobject/set new-strobj k v) + (ObjMap. meta strkeys new-strobj nil)) ;overwrite + (let [new-strobj (obj-clone strobj strkeys) ; append + 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)) + (simple-hash-map 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 + "keyval => key val + Returns a new object map with supplied mappings." + [& 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))))) + +(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 HashMap [meta count hashobj ^:mutable __hash] + Object + (toString [coll] + (pr-str* coll)) + (keys [coll] + (es6-iterator (map #(-key %) (-seq coll)))) + (entries [coll] + (es6-entries-iterator (-seq coll))) + (values [coll] + (es6-iterator (map #(-val %) (-key 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] (HashMap. meta count hashobj __hash)) + + IMeta + (-meta [coll] meta) + + ICloneable + (-clone [coll] (HashMap. 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 (. HashMap -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) + (do + ; found key, replace + (aset new-bucket (inc i) v) + (HashMap. meta count new-hashobj nil)) + (do + ; did not find key, append + (.push new-bucket k v) + (HashMap. meta (inc count) new-hashobj nil)))) + (let [new-hashobj (gobject/clone hashobj)] + ; did not find bucket + (unchecked-set new-hashobj h (array k v)) + (HashMap. 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))) + (HashMap. 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 HashMap) + +(set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj) empty-unordered-hash)) + +(set! (. HashMap -fromArrays) (fn [ks vs] + (let [len (.-length ks)] + (loop [i 0, out (. HashMap -EMPTY)] + (if (< i len) + (recur (inc i) (assoc out (aget ks i) (aget vs i))) + out))))) + +(defn simple-hash-map + "keyval => key val + Returns a new hash map with supplied mappings." + [& keyvals] + (loop [in (seq keyvals), out (. HashMap -EMPTY)] + (if in + (recur (nnext in) (-assoc out (first in) (second in))) + out))) + +(deftype Set [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 + (Set. new-meta hash-map __hash))) + + IMeta + (-meta [coll] meta) + + ICloneable + (-clone [coll] (Set. meta hash-map __hash)) + + ICollection + (-conj [coll o] + (Set. meta (assoc hash-map o o) nil)) + + IEmptyableCollection + (-empty [coll] (with-meta (. Set -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] + (Set. meta (-dissoc hash-map v) 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 Set) + +(set! (. Set -EMPTY) (Set. nil (. HashMap -EMPTY) empty-unordered-hash)) + +(defn simple-set + [coll] + (if (set? coll) + (-with-meta coll nil) + (let [in (seq coll)] + (if (nil? in) + #{} + (loop [in in out (. Set -EMPTY)] + (if-not (nil? in) + (recur (next in) (-conj out (first in))) + out)))))) diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index c88079f5f..b348d9928 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))) + Set + (specize* ([s] (spec-impl s s nil nil)) + ([s form] (spec-impl form s nil nil))) + default (specize* ([o] diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 709531e59..ceebbe973 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] @@ -4072,8 +4080,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 @@ -4452,10 +4462,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) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index b215573f6..550fd68d5 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -211,7 +211,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 @@ -2519,6 +2520,10 @@ :cache-analysis-format (:cache-analysis-format opts :transit)) (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*))] (name (:target opts))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index fcc03ab96..faba462b5 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -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.HashMap.EMPTY") + (emits "cljs.core.HashMap.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.Vector.EMPTY") + (emits "new cljs.core.Vector(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.Set.EMPTY") + (emits "cljs.core.simple_set([" (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 "({") diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 8393a1a67..b326a3fa6 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1507,13 +1507,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 diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 44d5e3f46..20aae48f0 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -9,12 +9,9 @@ (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" @@ -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" @@ -1020,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" @@ -1127,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" @@ -1157,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-simple-hash-map + (let [a (simple-hash-map)] + (is (empty? a)) + (is (zero? (count a)))) + (let [b (simple-hash-map :a 1)] + (is (not (empty? b))) + (is (== 1 (count b)))) + (let [c (simple-hash-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 (= (simple-hash-map :a 1 :b 2 :c 3) + (simple-hash-map :a 1 :b 2 :c 3))) + (is (= (simple-hash-map :a 1 :b 2) + (into (simple-hash-map) [[:a 1] [:b 2]]))) + (is (= (merge-with + + (simple-hash-map :a 1 :b 2) + (simple-hash-map :a 1 :b 2)) + (into (simple-hash-map) [[:a 2] [:b 4]]))) + (is (= (transient (simple-hash-map :a 1 :b 2)) + (simple-hash-map :a 1 :b 2)))) + +(deftest test-simple-set + (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 (= (simple-set [[3 4] [1 2] [5 6]]) + (into #{} [[3 4] [1 2] [5 6]])))) + (comment (run-tests) diff --git a/src/test/cljs/cljs/interop_test.cljs b/src/test/cljs/cljs/interop_test.cljs index ee158ec18..f736ea15c 100644 --- a/src/test/cljs/cljs/interop_test.cljs +++ b/src/test/cljs/cljs/interop_test.cljs @@ -14,12 +14,17 @@ (testing "Object equiv method" (is (.equiv :foo :foo)) (is (.equiv 'foo 'foo)) - (is (.equiv {:foo 1 :bar 2} {:foo 1 :bar 2})) + ;; .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]))) - (is (.equiv #{:cat :dog :bird} #{:cat :dog :bird})) - )) + ;; .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" @@ -67,4 +72,4 @@ (is (#{:cat :bird :dog} (.-value (.next iter)))) (is (#{:cat :bird :dog} (.-value (.next iter)))) (is (.-done (.next iter))))) - )) \ 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 000000000..f481be9ff --- /dev/null +++ b/src/test/cljs/cljs/lite_collections_test.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.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-simple-set-with-set + (is (= (simple-set []) (set []))) + (is (= (set []) (simple-set []))) + (is (= (simple-set [(MapEntry. 1 2 nil)]) + (set [(MapEntry. 1 2 nil)])))) + +(comment + + (require '[cljs.lite-collections-test] :reload) + (cljs.test/run-tests) + + ) diff --git a/src/test/cljs/cljs/metadata_test.cljc b/src/test/cljs/cljs/metadata_test.cljc index b22c79612..8f5e99411 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/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs index 9e43a7340..00d648fa1 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 9f28ebf08..fb3476f53 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 000000000..95313b3f8 --- /dev/null +++ b/src/test/cljs/lite_test_runner.cljs @@ -0,0 +1,127 @@ +;; 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.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.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_build/trivial/core6.cljs b/src/test/cljs_build/trivial/core6.cljs new file mode 100644 index 000000000..3eed31bc6 --- /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 c87ec7bce..1a451d491 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/simple-vec + (-> 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/simple-vector + (-> ast :name) + (-> ast :info :name)))))) + (comment (test/run-tests) (require '[clojure.pprint :refer [pprint]]) + ) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index a1a2f3871..535f1c2c0 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -757,7 +757,23 @@ 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) 32768)))) + (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")) @@ -772,6 +788,38 @@ (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")) out-file (io/file out "main.js") From 00ac012a22703888b1eaff0b43be63739a114c0b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 4 Oct 2025 10:04:34 -0400 Subject: [PATCH 3969/4033] CLJS-3451: make munge-str public (#269) * CLJS-3451: make munge-str public --------- Co-authored-by: Michiel Borkent --- src/main/cljs/cljs/core.cljs | 12 ++++++++++-- src/main/clojure/cljs/compiler.cljc | 2 +- src/test/cljs/cljs/core_test.cljs | 8 ++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8eabe774b..27464f4f0 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -11887,7 +11887,9 @@ reduces them without incurring seq initialization" (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)) @@ -11899,7 +11901,13 @@ reduces them without incurring seq initialization" (recur (inc i))))) (.toString sb))) -(defn munge [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_" diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index faba462b5..522a88357 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))))) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 686a0fd72..d92fb5105 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1136,7 +1136,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])) @@ -1952,4 +1956,4 @@ (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])))) \ No newline at end of file + (is (= "1234" (apply cljs.core/str_ 1 2 [3 4])))) From be21ba13822c2b3edcce0d3fac6d46a5fc6e6d0d Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Oct 2025 14:08:24 -0400 Subject: [PATCH 3970/4033] CLJS-3233: `:refer-global` + `:only`, `:require-global` (#263) New `ns` form cases `:refer-global` and `:require-global` Global values and values in ClojureScript must always be referenced by prefixing the magic `js` namespace. None of the usual affordances for managing names are available for these cases. `:refer-global` and `:require-global` provide these missing affordances. `:refer-global` allows users to refer to JavaScript global objects. For example: (ns foo (:refer-global :only [Date])) Now `Date` can be used in the namespace w/o needing to write `js/Date`. Note that ClojureScript will track these globals values as being "tainted", i.e. externs inference will continue to work for these cases. Renames are supported: (ns foo (:refer-global :only [Date] :rename {Date MyDate})) `:require-global` allows users to treat some global JavaScript object as a library. This streamlines many cases where users may wish to avoid additional ceremony around build tooling. (ns foo (:require-global [SomeLib :as lib :refer [bar]])) All of the usul expecations for `:require` are supported, renames, sublibs etc. This feature builds upon existing `:global-exports` foreign library functionalty. Foreign lib entries can now declare `:external?` to communicate to the ClojureScript compiler that no source file exists for the entry. `:require-global` leverages this new facility. You can now simply include script tags on the page, and from ClojureScript use that library w/o any additional configuration. Both of the above cases have corresponding `ns*` macro variants permitting interactive usage at the REPL: (refer-global :only '[Date]) (require-global '[SomeLib :as lib :refer [bar]]) --- src/main/clojure/cljs/analyzer.cljc | 143 ++++++++++++++++++++--- src/main/clojure/cljs/closure.clj | 24 ++-- src/main/clojure/cljs/compiler.cljc | 5 +- src/main/clojure/cljs/core.cljc | 16 ++- src/main/clojure/cljs/repl.cljc | 11 +- src/test/clojure/cljs/analyzer_tests.clj | 60 +++++++++- 6 files changed, 228 insertions(+), 31 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index ceebbe973..924ac3477 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2781,6 +2781,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] @@ -2828,13 +2839,19 @@ (error env (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))))) +(defn global-ns? [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)) @@ -3047,6 +3064,90 @@ 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 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]) @@ -3300,6 +3401,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* @@ -3334,6 +3439,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}) @@ -3343,8 +3449,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 @@ -3352,8 +3459,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) @@ -3370,7 +3477,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] @@ -3392,9 +3499,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 @@ -3434,6 +3541,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}) @@ -3443,7 +3551,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 @@ -3464,7 +3573,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 @@ -3473,9 +3582,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* diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 550fd68d5..2c923d85c 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1127,13 +1127,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))) @@ -1604,7 +1608,11 @@ "], [" ;; 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 external? foreign deps - they are already loaded + ;; in the environment, there is nothing to do. + ;; :require-global is the typical case here + (remove ana/external-dep? (deps/-requires input)) ;; under Node.js we emit native `require`s for these (= :nodejs (:target opts)) (filter (complement ana/node-module-dep?)))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 522a88357..3e0d42480 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1367,7 +1367,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))) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index b326a3fa6..a58ec944f 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -12,7 +12,7 @@ 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 @@ -3121,6 +3121,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)) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index b685a62e5..85117c725 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 @@ -652,7 +657,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 +678,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 diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index f1b639938..ca388182d 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 Symbol 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}))))) From e86a369d7f99a43e5745e7b425f199d7966be426 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Oct 2025 20:56:38 -0400 Subject: [PATCH 3971/4033] runtime existence check for :require-global libs under :optimizations :none (#270) --- src/main/clojure/cljs/compiler.cljc | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 3e0d42480..8a17d17d2 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1312,19 +1312,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) ";"))) @@ -1409,7 +1414,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 ");")))) From e46a486566645e6e274798342b554eac7011cd70 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 10 Oct 2025 21:37:21 -0400 Subject: [PATCH 3972/4033] test case for reported :lite-mode bug (#271) - can't repro yet --- src/test/cljs/cljs/lite_collections_test.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/cljs/cljs/lite_collections_test.cljs b/src/test/cljs/cljs/lite_collections_test.cljs index f481be9ff..16d0296c1 100644 --- a/src/test/cljs/cljs/lite_collections_test.cljs +++ b/src/test/cljs/cljs/lite_collections_test.cljs @@ -24,6 +24,10 @@ (is (= (simple-set [(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"))) + (comment (require '[cljs.lite-collections-test] :reload) From db5cbfd68849f9633f8c3d6c20143ba6a3cc81c6 Mon Sep 17 00:00:00 2001 From: Paula Gearon Date: Tue, 14 Oct 2025 08:14:44 -0400 Subject: [PATCH 3973/4033] CLJS-3454: New set instances are created when redundant data is added --- src/main/cljs/cljs/core.cljs | 20 ++++++++++++++++---- src/test/cljs/cljs/core_test.cljs | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 27464f4f0..1e96c24f6 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -9304,7 +9304,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)) @@ -9341,7 +9344,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] @@ -9459,7 +9465,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)) @@ -9513,7 +9522,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] diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d92fb5105..9ce1a9976 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -737,6 +737,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)) From 6cb2c4dfa971abd56b2b1acee5f1eb97fda9bac1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 14 Oct 2025 20:27:19 -0400 Subject: [PATCH 3974/4033] CLJS-3452: optimize str by compiling to + / .toString + compile time optimizations (#273) - Avoiding Array.join in favor of + gives around 4x perf boost - Avoiding 1-arity str calls for compile time constants Co-authored-by: Michiel Borkent --- src/main/cljs/cljs/core.cljs | 2 +- src/main/clojure/cljs/core.cljc | 36 +++++++++++-------- src/test/cljs/cljs/core_test.cljs | 8 +++++ .../cljs_3452_str_optimizations/core.cljs | 9 +++++ src/test/clojure/cljs/build_api_tests.clj | 18 ++++++++++ 5 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 src/test/cljs_build/cljs_3452_str_optimizations/core.cljs diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 27464f4f0..c87b75179 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3110,7 +3110,7 @@ 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 diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index a58ec944f..1b7a1235b 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -866,23 +866,29 @@ (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 - ([] "") - ([x] - (if (typed-expr? &env x '#{string}) - x - (string-expr (core/list 'js* "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})" 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(~{})")) - strs (core/->> (core/list* x ys) - (map interpolate) - (interpose ",") - (apply core/str))] - (string-expr (list* 'js* (core/str "[" strs "].join('')") x ys))))) + [& xs] + (core/let [interpolate (core/fn [x] + (core/cond + (typed-expr? &env x '#{clj-nil}) + nil + (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)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index d92fb5105..5d887d313 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1957,3 +1957,11 @@ (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))))))) 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 000000000..75456c406 --- /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/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 535f1c2c0..5c6ce522e 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -940,3 +940,21 @@ (.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)))) From a761d54e43ae1f6b04a8373cc587648252cad55e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 17 Oct 2025 17:32:13 -0400 Subject: [PATCH 3975/4033] CLJS-2471: ChunkedSeq should implemented ICounted (#275) Co-authored-by: Roman Liutikov --- src/main/cljs/cljs/core.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c87b75179..3515da790 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6002,6 +6002,10 @@ reduces them without incurring seq initialization" (-empty [coll] ()) + ICounted + (-count [coll] + (- (-count vec) (+ i off))) + IChunkedSeq (-chunked-first [coll] (array-chunk node off)) From 249faa2a584f9a41a39d6fcd07712e1ac414720a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 19 Oct 2025 11:31:47 -0400 Subject: [PATCH 3976/4033] CLJS-3457: `:lite-mode` data structures should satisfy `identical?` if unchanged by `conj` (#276) --- src/main/cljs/cljs/core.cljs | 27 ++++++++++++------- src/test/cljs/cljs/lite_collections_test.cljs | 8 ++++++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 3515da790..3beaa425c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12603,10 +12603,14 @@ reduces them without incurring seq initialization" (let [k (if-not (keyword? k) k (keyword->obj-map-key k))] (if (string? k) (if-not (nil? (scan-array 1 k strkeys)) - (let [new-strobj (obj-clone strobj strkeys)] - (gobject/set new-strobj k v) - (ObjMap. meta strkeys new-strobj nil)) ;overwrite - (let [new-strobj (obj-clone strobj strkeys) ; append + (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) @@ -12812,10 +12816,12 @@ reduces them without incurring seq initialization" i (scan-array-equiv 2 k new-bucket)] (aset new-hashobj h new-bucket) (if (some? i) - (do - ; found key, replace - (aset new-bucket (inc i) v) - (HashMap. meta count new-hashobj nil)) + (if (identical? v (aget new-bucket (inc i))) + coll + (do + ; found key, replace + (aset new-bucket (inc i) v) + (HashMap. meta count new-hashobj nil))) (do ; did not find key, append (.push new-bucket k v) @@ -12954,7 +12960,10 @@ reduces them without incurring seq initialization" ICollection (-conj [coll o] - (Set. meta (assoc hash-map o o) nil)) + (let [new-hash-map (assoc hash-map o o)] + (if (identical? new-hash-map hash-map) + coll + (Set. meta new-hash-map nil)))) IEmptyableCollection (-empty [coll] (with-meta (. Set -EMPTY) meta)) diff --git a/src/test/cljs/cljs/lite_collections_test.cljs b/src/test/cljs/cljs/lite_collections_test.cljs index 16d0296c1..a96148f00 100644 --- a/src/test/cljs/cljs/lite_collections_test.cljs +++ b/src/test/cljs/cljs/lite_collections_test.cljs @@ -28,6 +28,14 @@ (= 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 (simple-hash-map :foo 1)] + (identical? m (assoc m :foo 1))) + (let [s (simple-set [:foo])] + (identical? s (conj s :foo)))) + (comment (require '[cljs.lite-collections-test] :reload) From 4eff03dd809120578326573e5de325dd67cc4885 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 19 Oct 2025 11:41:10 -0400 Subject: [PATCH 3977/4033] add missing simple Set disjoin identical? check --- src/main/cljs/cljs/core.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d889a7ca9..82bd82c6b 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -13014,7 +13014,10 @@ reduces them without incurring seq initialization" ISet (-disjoin [coll v] - (Set. meta (-dissoc hash-map v) nil)) + (let [new-hash-map (-dissoc hash-map v)] + (if (identical? new-hash-map hash-map) + coll + (Set. meta new-hash-map nil)))) IEditableCollection (-as-transient [coll] From d912f929e4ca9b486a182575659dde95be20c386 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 19 Oct 2025 11:55:14 -0400 Subject: [PATCH 3978/4033] cljs.analyzer/global-ns? should check that x is a symbol first --- src/main/clojure/cljs/analyzer.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 924ac3477..af28ca2eb 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2840,8 +2840,9 @@ (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))))) (defn global-ns? [x] - (or (= 'js x) - (= "js" (namespace x)))) + (and (symbol? x) + (or (= 'js x) + (= "js" (namespace x))))) (defn missing-use? [lib sym cenv] ;; ignore globals referred via :refer-global From 075edcca59b4863d6fe04c55703e3fbeaaa61bbc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 4 Nov 2025 14:35:31 -0500 Subject: [PATCH 3979/4033] add :lite-mode & :elide-to-string to cljs.analyzer/build-affecting-options --- src/main/clojure/cljs/analyzer.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index af28ca2eb..321489c30 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4832,7 +4832,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] From b69da0eee62551f3fcc6c05eddaa6f27f2e89c63 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Nov 2025 09:28:20 -0500 Subject: [PATCH 3980/4033] don't add the imul.js preamble in lite-mode (#277) --- src/main/clojure/cljs/closure.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 2c923d85c..3f20a25bb 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -2525,8 +2525,10 @@ :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))] From c3c3773755d9c5b7454c36c4aeabece3fe6f7fbe Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Nov 2025 11:47:04 -0500 Subject: [PATCH 3981/4033] CLJS-3461: don't hard-code destructuring to PAM (#278) - update `--destructure-map`, `seq-to-map-for-destructuring` for `:lite-mode` - `ObjMap.createAsIfByAssoc`, perf is not critical for `:lite-mode` opt for simplicity for now - add REPL aliases to deps.edn to make it easier to play around with `:lite-mode` --- deps.edn | 6 ++++- src/main/cljs/cljs/core.cljs | 51 ++++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/deps.edn b/deps.edn index e3a236c2a..5233627bc 100644 --- a/deps.edn +++ b/deps.edn @@ -9,7 +9,11 @@ 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-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" diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 82bd82c6b..707a7f0cd 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -4124,16 +4124,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 @@ -7126,7 +7136,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?))))) @@ -9039,9 +9049,13 @@ reduces them without incurring seq initialization" "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)))) + (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 @@ -12731,6 +12745,21 @@ reduces them without incurring seq initialization" (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] From acaefa15e6adfc92d32ead7e882d1f0e73ecd4a7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Nov 2025 11:49:11 -0500 Subject: [PATCH 3982/4033] Fix up missing "sources" in advanced compiled source map (#251) --- src/main/clojure/cljs/closure.clj | 33 ++++++++++++----------- src/test/cljs_build/adv_src_map/core.cljs | 3 +++ src/test/clojure/cljs/build_api_tests.clj | 27 +++++++++++++++++++ 3 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 src/test/cljs_build/adv_src_map/core.cljs diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 3f20a25bb..05fa761aa 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1394,11 +1394,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)] @@ -1415,19 +1416,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)] 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 000000000..22cc32cab --- /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/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 5c6ce522e..3e44d1b4b 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -958,3 +958,30 @@ (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 From bed630170d1468d10bcda7106d11191213b170ca Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 8 Nov 2025 18:51:52 -0500 Subject: [PATCH 3983/4033] unify (cljs.repl/doc ...) handling of js/console (#279) - just handle everything in the final resolve branch - fixes bug where (doc console) did not work after a (refer-global ...) --- src/main/clojure/cljs/repl.cljc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 85117c725..919bd54ff 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -1452,11 +1452,6 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) (keyword? name) `(cljs.repl/print-doc {:spec ~name :doc (cljs.spec.alpha/describe ~name)}) - (= "js" (namespace name)) - `(cljs.repl/print-doc - (quote ~(merge (select-keys (ana-api/resolve-extern name) [:doc :arglists]) - {:name name}))) - (ana-api/find-ns name) `(cljs.repl/print-doc (quote ~(select-keys (ana-api/find-ns name) [:name :doc]))) @@ -1464,8 +1459,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 From ceba37b5a8513ca837f0d6939681b2c05df32e1c Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 9 Nov 2025 12:01:10 -0500 Subject: [PATCH 3984/4033] fix typo to make Vector es6-iterable --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 707a7f0cd..8d05e8a7f 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12477,7 +12477,7 @@ reduces them without incurring seq initialization" IPrintWithWriter (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))) -(es6-iterable PersistentVector) +(es6-iterable Vector) (set! (. Vector -EMPTY) (Vector. nil (array) nil)) From d9365bf51a3684b48a7e4fb3e374bdd6eba17db3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Nov 2025 20:15:54 -0500 Subject: [PATCH 3985/4033] remove Vector dep on LazySeq (#280) --- src/main/cljs/cljs/core.cljs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 8d05e8a7f..c2319c699 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12361,12 +12361,7 @@ reduces them without incurring seq initialization" ISeqable (-seq [coll] (when (> (alength array) 0) - (let [vector-seq - (fn vector-seq [i] - (lazy-seq - (when (< i (alength array)) - (cons (aget array i) (vector-seq (inc i))))))] - (vector-seq 0)))) + (prim-seq array))) ICounted (-count [coll] (alength array)) From 10fc535d91f4e0e84c86a4c3e4ded3fec77a1d2b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 9 Nov 2025 20:54:17 -0500 Subject: [PATCH 3986/4033] remove hash-map dep on map (#281) --- src/main/cljs/cljs/core.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index c2319c699..507b13f3a 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12773,11 +12773,13 @@ reduces them without incurring seq initialization" (toString [coll] (pr-str* coll)) (keys [coll] - (es6-iterator (map #(-key %) (-seq coll)))) + (let [arr (. (-seq coll) -arr)] + (es6-iterator (prim-seq (.map arr -key (-seq coll)))))) (entries [coll] (es6-entries-iterator (-seq coll))) (values [coll] - (es6-iterator (map #(-val %) (-key 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] From 274701280de787b598071460cdac0a3709a5bc11 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 11 Nov 2025 16:47:45 -0500 Subject: [PATCH 3987/4033] CLJS-3425: Incorrect handling of ##NaN with min/max (#282) - prefix inlining min/max as unchecked - fix min and max fns to do what Clojure does --- src/main/cljs/cljs/core.cljs | 40 +++++++++++++++++++------------ src/main/clojure/cljs/core.cljc | 20 +++++++++------- src/test/cljs/cljs/core_test.cljs | 7 ++++++ 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 507b13f3a..af6cb080c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1608,7 +1608,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) @@ -1624,7 +1624,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) @@ -1695,7 +1695,7 @@ reduces them without incurring seq initialization" ICounted (-count [_] - (max 0 (- (alength arr) i))) + (unchecked-max 0 (- (alength arr) i))) IIndexed (-nth [coll n] @@ -2801,17 +2801,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))) @@ -6156,7 +6171,7 @@ reduces them without incurring seq initialization" (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)))) + (build-subvec meta (assoc v v-pos val) start (unchecked-max end (inc v-pos)) nil)))) IReduce (-reduce [coll f] @@ -9924,7 +9939,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) () @@ -10326,7 +10341,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))))))))) @@ -12163,11 +12178,6 @@ reduces them without incurring seq initialization" [x] (instance? goog.Uri x)) -(defn 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] @@ -12296,7 +12306,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) @@ -12309,7 +12319,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) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 1b7a1235b..f6cfb7a1e 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1201,17 +1201,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)) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 83f1a65ef..c62b93934 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1986,3 +1986,10 @@ (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))))))) + +(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))))) From 1a3d19e05d91ed2a9047a3b521e08488fb5f4ca7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 16 Nov 2025 19:45:45 -0500 Subject: [PATCH 3988/4033] fix load-file bug (#283) --- src/main/clojure/cljs/repl.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc index 919bd54ff..c588b4fbf 100644 --- a/src/main/clojure/cljs/repl.cljc +++ b/src/main/clojure/cljs/repl.cljc @@ -609,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 From eaf67ed12671104d7f8e784bca2319a352c6f46b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 16 Nov 2025 20:46:44 -0500 Subject: [PATCH 3989/4033] put the browser REPL new window behavior behind a flag, default to false (#284) --- src/main/clojure/cljs/repl/browser.clj | 41 +++++++++++++++----------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index cf4a34523..2c907c6d1 100644 --- a/src/main/clojure/cljs/repl/browser.clj +++ b/src/main/clojure/cljs/repl/browser.clj @@ -349,21 +349,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 +397,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 +464,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/" From 4ae0694acf2422016e1ac2081bff3cb390acf81b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 17 Nov 2025 20:45:46 -0500 Subject: [PATCH 3990/4033] MapEntry: switch to case from cond (#285) --- src/main/cljs/cljs/core.cljs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index af6cb080c..12082bdf6 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -6762,14 +6762,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)) @@ -6779,7 +6781,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] From 54d013d279a91580b43c5df144e07221355212ac Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 18 Nov 2025 20:39:13 -0500 Subject: [PATCH 3991/4033] increase string hash cache to 1024 --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 12082bdf6..d1b71a35c 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1041,7 +1041,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) From 19288496e44915d675aa30610cb9fc52427bdc80 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 19 Nov 2025 11:25:59 -0500 Subject: [PATCH 3992/4033] cljs.proxy (#286) - add `cljs.proxy` namespace, provides a single function `builder` which can take optional `key-fn`. can proxy maps as Objects, and Vector as array-like. --- src/main/cljs/cljs/proxy.cljs | 137 +++++++++++++++++++++++++++++ src/main/cljs/cljs/proxy/impl.cljs | 15 ++++ 2 files changed, 152 insertions(+) create mode 100644 src/main/cljs/cljs/proxy.cljs create mode 100644 src/main/cljs/cljs/proxy/impl.cljs diff --git a/src/main/cljs/cljs/proxy.cljs b/src/main/cljs/cljs/proxy.cljs new file mode 100644 index 000000000..a071868de --- /dev/null +++ b/src/main/cljs/cljs/proxy.cljs @@ -0,0 +1,137 @@ +(ns cljs.proxy + (:refer-global :only [Proxy isNaN]) + (:require [cljs.proxy.impl :refer [SimpleCache]])) + +(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: Return a JavaScript Proxy ctor fn with the provided key-fn. You + can proxy ClojureScript map and vectors. Access pattern from JavaScript + will lazily wrap collection values in Proxy if needed. Note key-fn + is only used for proxied ClojureScript maps. This function should map + strings to the appropriate key representation. All maps proxied from the + same ctor fn will share the same key-fn cache." + ([] + (builder keyword)) + ([key-fn] + (js* "var __ctor") + (let [cache-key-fn (write-through key-fn) + vec-handler #js {:get (fn [^cljs.core/IIndexed target prop receiver] + (if (identical? prop "length") + (-count ^cljs.core/ICounted target) + (let [n (js* "+~{}" prop)] + (when (and (number? n) + (not (isNaN n))) + (js/__ctor (-nth target n nil)))))) + + :has (fn [^cljs.core/IAssociative target prop] + (if (identical? prop "length") + true + (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))) + +(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")) + +) diff --git a/src/main/cljs/cljs/proxy/impl.cljs b/src/main/cljs/cljs/proxy/impl.cljs new file mode 100644 index 000000000..56c99430d --- /dev/null +++ b/src/main/cljs/cljs/proxy/impl.cljs @@ -0,0 +1,15 @@ +(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))) From 65945bbae1cc7202e8025129f58d2e6f0a35ed29 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Wed, 19 Nov 2025 19:10:44 -0500 Subject: [PATCH 3993/4033] better docstring w/ examples --- src/main/cljs/cljs/proxy.cljs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/proxy.cljs b/src/main/cljs/cljs/proxy.cljs index a071868de..9a98ae7f2 100644 --- a/src/main/cljs/cljs/proxy.cljs +++ b/src/main/cljs/cljs/proxy.cljs @@ -16,12 +16,29 @@ :enumerable true}) (defn builder - "EXPERIMENTAL: Return a JavaScript Proxy ctor fn with the provided key-fn. You - can proxy ClojureScript map and vectors. Access pattern from JavaScript - will lazily wrap collection values in Proxy if needed. Note key-fn - is only used for proxied ClojureScript maps. This function should map - strings to the appropriate key representation. All maps proxied from the - same ctor fn will share the same key-fn cache." + "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. All maps proxied from the same ctor fn will share + the same key-fn cache." ([] (builder keyword)) ([key-fn] From 3f139e82e9f3fe39ba21079e7908a2999d8a1d40 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Wed, 19 Nov 2025 19:11:32 -0500 Subject: [PATCH 3994/4033] tweak --- src/main/cljs/cljs/proxy.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/proxy.cljs b/src/main/cljs/cljs/proxy.cljs index 9a98ae7f2..e02e084d8 100644 --- a/src/main/cljs/cljs/proxy.cljs +++ b/src/main/cljs/cljs/proxy.cljs @@ -29,7 +29,7 @@ (def proxied-vec (proxy [1 2 3 4])) (aget proxied-vec 1) ;; => 2 - Access patterns from JavaScript on these proxied values will lazily + Access patterns from JavaScript on these proxied values will lazily, recursively return further proxied values: (def nested-proxies (proxy [{:foo 1 :bar 2}])) From 98fced6934fc9018ad7cdd9470c2df02ac50523d Mon Sep 17 00:00:00 2001 From: davidnolen Date: Wed, 19 Nov 2025 19:17:14 -0500 Subject: [PATCH 3995/4033] clarify defaults --- src/main/cljs/cljs/proxy.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/proxy.cljs b/src/main/cljs/cljs/proxy.cljs index e02e084d8..b0d40655f 100644 --- a/src/main/cljs/cljs/proxy.cljs +++ b/src/main/cljs/cljs/proxy.cljs @@ -37,8 +37,8 @@ Note key-fn is only used for proxied ClojureScript maps. This function should map strings to the appropriate key - representation. All maps proxied from the same ctor fn will share - the same key-fn cache." + representation. If unspecified, key-fn defaults to keyword. All maps + proxied from the same ctor fn will share the same key-fn cache." ([] (builder keyword)) ([key-fn] From 7c718adfce0929fedfd15fcc148d85c85cc1c273 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 20 Nov 2025 21:17:51 -0500 Subject: [PATCH 3996/4033] Support Persistent/VECTOR in a backwards compatible way (#287) If we have a symbol of the form A/B and the following conditions are met: 1. A is not an namespace alias 2. A is not a dotted symbol 3. A is an unqualifed var defined or referred in the current ns Then interpret A/B as A.B Note this means that namespace aliases will always shadow unqualified vars defined or referred in the current ns --- src/main/clojure/cljs/analyzer.cljc | 37 ++++++++++++++++++----------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 321489c30..aa52f6fc9 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1313,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." @@ -1356,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 ".") From 78f35bc1cd6380f2517e4340110e7e3066d3c9f3 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sat, 22 Nov 2025 17:09:00 -0500 Subject: [PATCH 3997/4033] add :qualified-method :ast, String/.toUpperCase and String/new etc. work (#289) * add :qualified-method :ast, String/.toUpperCase and String/new etc. work * self-hosted special case * fix busted actions, switch to ubuntu and node --- .github/workflows/test.yaml | 90 ++++++++++------------------- src/main/clojure/cljs/analyzer.cljc | 42 +++++++++----- src/main/clojure/cljs/compiler.cljc | 8 +++ 3 files changed, 69 insertions(+), 71 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4420e9d0b..b8c5ae94b 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,37 @@ 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: | - /System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Helpers/jsc builds/out-adv/core-advanced-test.js | tee test-out.txt + 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: macos-14 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -76,44 +63,31 @@ 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:lite.test.build - # - name: Install JSC - # run: ./ci/install_jsc.sh - - name: Run tests run: | - /System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Helpers/jsc builds/out-lite/lite-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 @@ -145,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 @@ -159,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 }}- @@ -189,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 @@ -203,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 }}- @@ -233,7 +207,7 @@ jobs: # Compiler Tests compiler-test: name: Compiler Tests - runs-on: macos-14 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -247,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 }}- @@ -292,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: @@ -308,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/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index aa52f6fc9..91ce2f13e 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4120,6 +4120,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` @@ -4130,21 +4131,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 (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}) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 8a17d17d2..a791cc2ba 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -1301,6 +1301,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 ")"))) From 6c3f12733a4081acb8afddc02e0106a5387fafe4 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 23 Nov 2025 08:32:50 -0500 Subject: [PATCH 3998/4033] Add some simple method value tests (#290) --- src/test/cljs/cljs/core_test.cljs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index c62b93934..3cd24205a 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] @@ -1993,3 +1994,16 @@ (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"])))) From 0bb9e9c6fb4761a1fceab882fb5e04e3565b7819 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 23 Nov 2025 08:47:21 -0500 Subject: [PATCH 3999/4033] bump Closure Compiler to v20250820 (#291) - bump version - remove diagnostic that no longer exists - remove DependencyOptions logic that does not work anymore --- deps.edn | 2 +- pom.template.xml | 2 +- project.clj | 2 +- script/bootstrap | 2 +- src/main/clojure/cljs/closure.clj | 9 +-------- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/deps.edn b/deps.edn index 5233627bc..7651fd0c1 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"} diff --git a/pom.template.xml b/pom.template.xml index 884a2d628..04b86a6c6 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -30,7 +30,7 @@ com.google.javascript closure-compiler - v20250402 + v20250820 org.clojure diff --git a/project.clj b/project.clj index 3977529e5..0bba1a489 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/script/bootstrap b/script/bootstrap index 6b2a6e44b..464cc08da 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/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index 05fa761aa..f91c52d77 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 @@ -1970,13 +1969,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 From 90e7fba4360435dae7c44d0e3d9437115f9ceacc Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 23 Nov 2025 08:49:06 -0500 Subject: [PATCH 4000/4033] update changes (#292) --- changes.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/changes.md b/changes.md index 4e616fb81..0f970948a 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,26 @@ +## 1.12. + +### 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 From 81fde7c3b9893b5f12fb91463839f947f46a1385 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 23 Nov 2025 10:40:43 -0500 Subject: [PATCH 4001/4033] fixed qualified-method resolution bug --- src/main/clojure/cljs/analyzer.cljc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 91ce2f13e..f1c337420 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4141,12 +4141,12 @@ {:op :qualified-method :env env :form sym - :class (symbol sym-ns)} - (if (= "new" sym-name) - {:kind :new - :name (symbol sym-name)} - {:kind :method - :name (symbol (subs sym-name 1))})) + :class (analyze-symbol env (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))] From 0b2fc9618234c0baa4f996e0d0d80db98af34333 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 23 Nov 2025 13:20:04 -0500 Subject: [PATCH 4002/4033] 1.12.110 --- README.md | 6 +++--- changes.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 98323b88b..b574f7151 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.110 * [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.110"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.12.42"] +[org.clojure/clojurescript "1.12.110"] ``` [Maven](https://maven.apache.org) dependency information: diff --git a/changes.md b/changes.md index 0f970948a..6ae85724b 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.12. +## 1.12.110 ### Changes * CLJS-3233: `:refer-global` + `:only`, `:require-global` From 7f17d12309810fd0121e4076cff16a1c4ef6f397 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 23 Nov 2025 23:27:00 -0500 Subject: [PATCH 4003/4033] fix REPL issue w/ :refer-global usage in namespaces found by trying to use cljs.proxy --- src/main/clojure/cljs/closure.clj | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj index f91c52d77..55e7181f0 100644 --- a/src/main/clojure/cljs/closure.clj +++ b/src/main/clojure/cljs/closure.clj @@ -1609,10 +1609,14 @@ ;; even under Node.js where runtime require is possible ;; this is necessary - see CLJS-2151 (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 - (remove ana/external-dep? (deps/-requires input)) + ;; 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?)))) From 2c220503fa2c105a89b0fae968aa108965007eee Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 23 Nov 2025 23:29:04 -0500 Subject: [PATCH 4004/4033] 1.12.112 --- README.md | 6 +++--- changes.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b574f7151..80f7d1f5f 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.110 +Latest stable release: 1.12.112 * [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.110"} + org.clojure/clojurescript {:mvn/version "1.12.112"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.12.110"] +[org.clojure/clojurescript "1.12.112"] ``` [Maven](https://maven.apache.org) dependency information: diff --git a/changes.md b/changes.md index 6ae85724b..1f185fa48 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.12.110 +## 1.12.112 ### Changes * CLJS-3233: `:refer-global` + `:only`, `:require-global` From f2c8575ff5de5016247f14fadecc2d841692c64f Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 24 Nov 2025 00:55:06 -0500 Subject: [PATCH 4005/4033] fix maven example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80f7d1f5f..92b1ab55d 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Latest stable release: 1.12.112 org.clojure clojurescript - 1.12.38 + 1.12.112 ``` From c57b06666a0850728ec1ff9a25e452467f5fbae2 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Mon, 24 Nov 2025 10:22:48 -0500 Subject: [PATCH 4006/4033] bump central publishing plugin --- pom.template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.template.xml b/pom.template.xml index 04b86a6c6..f6a75f483 100644 --- a/pom.template.xml +++ b/pom.template.xml @@ -409,7 +409,7 @@ org.sonatype.central central-publishing-maven-plugin - 0.7.0 + 0.9.0 true central From b52c788e806136fbe56232be83f8e6ed865741fd Mon Sep 17 00:00:00 2001 From: JarrodCTaylor Date: Mon, 24 Nov 2025 10:36:24 -0600 Subject: [PATCH 4007/4033] Change server-id from sonatype to central Use the new central publishing system --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 766c05f63..31686eb49 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ 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 }} From deff2db82be4a7ee38f3f1b708e162c253c1682f Mon Sep 17 00:00:00 2001 From: JarrodCTaylor Date: Mon, 24 Nov 2025 11:13:54 -0600 Subject: [PATCH 4008/4033] Use central creds for release workflow --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 31686eb49..8a2d87838 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,12 +30,12 @@ jobs: 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 }} From cdf7d4572e67d54936483578541693535526710f Mon Sep 17 00:00:00 2001 From: davidnolen Date: Mon, 24 Nov 2025 12:29:23 -0500 Subject: [PATCH 4009/4033] update for actual released version --- README.md | 8 ++++---- changes.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 92b1ab55d..3fc82e6e0 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.112 +Latest stable release: 1.12.116 * [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.112"} + org.clojure/clojurescript {:mvn/version "1.12.116"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.12.112"] +[org.clojure/clojurescript "1.12.116"] ``` [Maven](https://maven.apache.org) dependency information: @@ -28,7 +28,7 @@ Latest stable release: 1.12.112 org.clojure clojurescript - 1.12.112 + 1.12.116 ``` diff --git a/changes.md b/changes.md index 1f185fa48..55c6119e2 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.12.112 +## 1.12.116 ### Changes * CLJS-3233: `:refer-global` + `:only`, `:require-global` From 8757eaabe952d329db83940d31718ccab019886e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 24 Nov 2025 20:42:40 -0500 Subject: [PATCH 4010/4033] CLJS-3463: rename all the lite mode data structures / fns to avoid clashing (#293) --- src/main/cljs/cljs/analyzer/passes/lite.cljc | 12 +-- src/main/cljs/cljs/core.cljs | 90 +++++++++---------- src/main/cljs/cljs/spec/alpha.cljs | 2 +- src/main/clojure/cljs/compiler.cljc | 12 +-- src/test/cljs/cljs/collections_test.cljs | 30 +++---- src/test/cljs/cljs/lite_collections_test.cljs | 12 +-- src/test/clojure/cljs/analyzer_pass_tests.clj | 4 +- 7 files changed, 81 insertions(+), 81 deletions(-) diff --git a/src/main/cljs/cljs/analyzer/passes/lite.cljc b/src/main/cljs/cljs/analyzer/passes/lite.cljc index 08a7e03de..d0ea8c659 100644 --- a/src/main/cljs/cljs/analyzer/passes/lite.cljc +++ b/src/main/cljs/cljs/analyzer/passes/lite.cljc @@ -12,21 +12,21 @@ (defn var? [ast] (= :var (:op ast))) -(def ctor->simple-ctor - '{cljs.core/vector cljs.core/simple-vector - cljs.core/vec cljs.core/simple-vec}) +(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->simple-ctor name)] + (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->simple-ctor (:name ast)))) + (contains? ctor->ctor-lite (:name ast)))) (defn use-lite-types [env ast _] (cond-> ast - (replace-var? ast) update-var)) \ No newline at end of file + (replace-var? ast) update-var)) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index d1b71a35c..ad87e9505 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10432,7 +10432,7 @@ reduces them without incurring seq initialization" (implements? IMeta obj) (not (nil? (meta obj))))) -(declare Vector) +(declare VectorLite) (defn- pr-writer-impl [obj writer opts] @@ -12287,9 +12287,9 @@ reduces them without incurring seq initialization" ;; ----------------------------------------------------------------------------- ;; Original 2011 Copy-on-Write Types -;;; Vector +;;; VectorLite -(deftype VectorIterator [arr ^:mutable i] +(deftype VectorLiteIterator [arr ^:mutable i] Object (hasNext [_] (< i (alength arr))) @@ -12298,7 +12298,7 @@ reduces them without incurring seq initialization" (set! i (inc i)) x))) -(deftype Vector [meta array ^:mutable __hash] +(deftype VectorLite [meta array ^:mutable __hash] Object (toString [coll] (pr-str* coll)) @@ -12337,10 +12337,10 @@ reduces them without incurring seq initialization" (-with-meta [coll new-meta] (if (identical? new-meta meta) coll - (Vector. new-meta array __hash))) + (VectorLite. new-meta array __hash))) ICloneable - (-clone [coll] (Vector. meta array __hash)) + (-clone [coll] (VectorLite. meta array __hash)) IMeta (-meta [coll] meta) @@ -12354,17 +12354,17 @@ reduces them without incurring seq initialization" (if (> (alength array) 0) (let [new-array (aclone array)] (. new-array (pop)) - (Vector. meta new-array nil)) + (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) - (Vector. meta new-array nil))) + (VectorLite. meta new-array nil))) IEmptyableCollection - (-empty [coll] (with-meta (. Vector -EMPTY) meta)) + (-empty [coll] (with-meta (. VectorLite -EMPTY) meta)) ISequential IEquiv @@ -12405,7 +12405,7 @@ reduces them without incurring seq initialization" (if (number? k) (let [new-array (aclone array)] (aset new-array k v) - (Vector. meta new-array nil)) + (VectorLite. meta new-array nil)) (throw (js/Error. "Vector's key for assoc must be a number.")))) (-contains-key? [coll k] (if (integer? k) @@ -12482,24 +12482,24 @@ reduces them without incurring seq initialization" IIterable (-iterator [coll] - (VectorIterator. array 0)) + (VectorLiteIterator. array 0)) IPrintWithWriter (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))) -(es6-iterable Vector) +(es6-iterable VectorLite) -(set! (. Vector -EMPTY) (Vector. nil (array) nil)) +(set! (. VectorLite -EMPTY) (VectorLite. nil (array) nil)) -(set! (. Vector -fromArray) (fn [xs] (Vector. nil xs nil))) +(set! (. VectorLite -fromArray) (fn [xs] (VectorLite. nil xs nil))) -(defn simple-vector +(defn vector-lite [& args] (if (and (instance? IndexedSeq args) (zero? (.-i args))) - (.fromArray Vector (aclone (.-arr args))) - (Vector. nil (into-array args) nil))) + (.fromArray VectorLite (aclone (.-arr args))) + (VectorLite. nil (into-array args) nil))) -(defn simple-vec +(defn vec-lite [coll] (cond (map-entry? coll) @@ -12509,7 +12509,7 @@ reduces them without incurring seq initialization" (with-meta coll nil) (array? coll) - (.fromArray Vector coll) + (.fromArray VectorLite coll) :else (into [] coll))) @@ -12538,7 +12538,7 @@ reduces them without incurring seq initialization" (recur (inc i))))) new-obj)) -(declare simple-hash-map HashMap) +(declare hash-map-lite HashMapLite) (defn- keyword->obj-map-key [k] @@ -12656,7 +12656,7 @@ reduces them without incurring seq initialization" (-kv-reduce coll (fn [ret k v] (-assoc ret k v)) - (simple-hash-map k v)) + (hash-map-lite k v)) meta)))) (-contains-key? [coll k] (let [k (if-not (keyword? k) k (keyword->obj-map-key k))] @@ -12783,7 +12783,7 @@ reduces them without incurring seq initialization" ; 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 HashMap [meta count hashobj ^:mutable __hash] +(deftype HashMapLite [meta count hashobj ^:mutable __hash] Object (toString [coll] (pr-str* coll)) @@ -12806,13 +12806,13 @@ reduces them without incurring seq initialization" #(f (-val %) (-key %)))))) IWithMeta - (-with-meta [coll meta] (HashMap. meta count hashobj __hash)) + (-with-meta [coll meta] (HashMapLite. meta count hashobj __hash)) IMeta (-meta [coll] meta) ICloneable - (-clone [coll] (HashMap. meta count hashobj __hash)) + (-clone [coll] (HashMapLite. meta count hashobj __hash)) ICollection (-conj [coll entry] @@ -12821,7 +12821,7 @@ reduces them without incurring seq initialization" (reduce -conj coll entry))) IEmptyableCollection - (-empty [coll] (with-meta (. HashMap -EMPTY) meta)) + (-empty [coll] (with-meta (. HashMapLite -EMPTY) meta)) IEquiv (-equiv [coll other] (equiv-map coll other)) @@ -12874,15 +12874,15 @@ reduces them without incurring seq initialization" (do ; found key, replace (aset new-bucket (inc i) v) - (HashMap. meta count new-hashobj nil))) + (HashMapLite. meta count new-hashobj nil))) (do ; did not find key, append (.push new-bucket k v) - (HashMap. meta (inc count) new-hashobj nil)))) + (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)) - (HashMap. meta (inc count) new-hashobj nil))))) + (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))] @@ -12902,7 +12902,7 @@ reduces them without incurring seq initialization" (let [new-bucket (aclone bucket)] (.splice new-bucket i 2) (unchecked-set new-hashobj h new-bucket))) - (HashMap. meta (dec count) new-hashobj nil)) + (HashMapLite. meta (dec count) new-hashobj nil)) ; key not found, return coll unchanged coll))) @@ -12961,27 +12961,27 @@ reduces them without incurring seq initialization" (-pr-writer [coll writer opts] (print-map coll pr-writer writer opts))) -(es6-iterable HashMap) +(es6-iterable HashMapLite) -(set! (. HashMap -EMPTY) (HashMap. nil 0 (js-obj) empty-unordered-hash)) +(set! (. HashMapLite -EMPTY) (HashMapLite. nil 0 (js-obj) empty-unordered-hash)) -(set! (. HashMap -fromArrays) (fn [ks vs] +(set! (. HashMapLite -fromArrays) (fn [ks vs] (let [len (.-length ks)] - (loop [i 0, out (. HashMap -EMPTY)] + (loop [i 0, out (. HashMapLite -EMPTY)] (if (< i len) (recur (inc i) (assoc out (aget ks i) (aget vs i))) out))))) -(defn simple-hash-map +(defn hash-map-lite "keyval => key val Returns a new hash map with supplied mappings." [& keyvals] - (loop [in (seq keyvals), out (. HashMap -EMPTY)] + (loop [in (seq keyvals), out (. HashMapLite -EMPTY)] (if in (recur (nnext in) (-assoc out (first in) (second in))) out))) -(deftype Set [meta hash-map ^:mutable __hash] +(deftype SetLite [meta hash-map ^:mutable __hash] Object (toString [coll] (pr-str* coll)) @@ -13003,23 +13003,23 @@ reduces them without incurring seq initialization" (-with-meta [coll new-meta] (if (identical? new-meta meta) coll - (Set. new-meta hash-map __hash))) + (SetLite. new-meta hash-map __hash))) IMeta (-meta [coll] meta) ICloneable - (-clone [coll] (Set. meta hash-map __hash)) + (-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 - (Set. meta new-hash-map nil)))) + (SetLite. meta new-hash-map nil)))) IEmptyableCollection - (-empty [coll] (with-meta (. Set -EMPTY) meta)) + (-empty [coll] (with-meta (. SetLite -EMPTY) meta)) IEquiv (-equiv [coll other] @@ -13058,7 +13058,7 @@ reduces them without incurring seq initialization" (let [new-hash-map (-dissoc hash-map v)] (if (identical? new-hash-map hash-map) coll - (Set. meta new-hash-map nil)))) + (SetLite. meta new-hash-map nil)))) IEditableCollection (-as-transient [coll] @@ -13090,18 +13090,18 @@ reduces them without incurring seq initialization" IPrintWithWriter (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "#{" " " "}" opts coll))) -(es6-iterable Set) +(es6-iterable SetLite) -(set! (. Set -EMPTY) (Set. nil (. HashMap -EMPTY) empty-unordered-hash)) +(set! (. SetLite -EMPTY) (SetLite. nil (. HashMapLite -EMPTY) empty-unordered-hash)) -(defn simple-set +(defn set-lite [coll] (if (set? coll) (-with-meta coll nil) (let [in (seq coll)] (if (nil? in) #{} - (loop [in in out (. Set -EMPTY)] + (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/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs index b348d9928..740262aad 100644 --- a/src/main/cljs/cljs/spec/alpha.cljs +++ b/src/main/cljs/cljs/spec/alpha.cljs @@ -148,7 +148,7 @@ (specize* ([s] (spec-impl s s nil nil)) ([s form] (spec-impl form s nil nil))) - Set + SetLite (specize* ([s] (spec-impl s s nil nil)) ([s form] (spec-impl form s nil nil))) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index a791cc2ba..6e9d152ff 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -540,8 +540,8 @@ (defn emit-lite-map [keys vals comma-sep distinct-keys?] (if (zero? (count keys)) - (emits "cljs.core.HashMap.EMPTY") - (emits "cljs.core.HashMap.fromArrays([" (comma-sep keys) "], [" (comma-sep vals) "])"))) + (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 @@ -590,8 +590,8 @@ (defn emit-lite-vector [items comma-sep] (if (empty? items) - (emits "cljs.core.Vector.EMPTY") - (emits "new cljs.core.Vector(null, [" (comma-sep items) "], null)"))) + (emits "cljs.core.VectorLite.EMPTY") + (emits "new cljs.core.VectorLite(null, [" (comma-sep items) "], null)"))) (defmethod emit* :vector [{:keys [items env]}] @@ -618,8 +618,8 @@ (defn emit-lite-set [items comma-sep distinct-constants?] (if (empty? items) - (emits "cljs.core.Set.EMPTY") - (emits "cljs.core.simple_set([" (comma-sep items) "])"))) + (emits "cljs.core.SetLite.EMPTY") + (emits "cljs.core.set_lite([" (comma-sep items) "])"))) (defmethod emit* :set [{:keys [items env]}] diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs index 20aae48f0..8607d30c2 100644 --- a/src/test/cljs/cljs/collections_test.cljs +++ b/src/test/cljs/cljs/collections_test.cljs @@ -1184,36 +1184,36 @@ (is (= (transient (obj-map :a 1 :b 2)) (obj-map :a 1 :b 2)))) -(deftest test-simple-hash-map - (let [a (simple-hash-map)] +(deftest test-hash-map-lite + (let [a (hash-map-lite)] (is (empty? a)) (is (zero? (count a)))) - (let [b (simple-hash-map :a 1)] + (let [b (hash-map-lite :a 1)] (is (not (empty? b))) (is (== 1 (count b)))) - (let [c (simple-hash-map :a 1 :b 2 :c 3)] + (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 (= (simple-hash-map :a 1 :b 2 :c 3) - (simple-hash-map :a 1 :b 2 :c 3))) - (is (= (simple-hash-map :a 1 :b 2) - (into (simple-hash-map) [[:a 1] [:b 2]]))) + (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 + - (simple-hash-map :a 1 :b 2) - (simple-hash-map :a 1 :b 2)) - (into (simple-hash-map) [[:a 2] [:b 4]]))) - (is (= (transient (simple-hash-map :a 1 :b 2)) - (simple-hash-map :a 1 :b 2)))) + (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-simple-set +(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 (= (simple-set [[3 4] [1 2] [5 6]]) + (is (= (set-lite [[3 4] [1 2] [5 6]]) (into #{} [[3 4] [1 2] [5 6]])))) (comment diff --git a/src/test/cljs/cljs/lite_collections_test.cljs b/src/test/cljs/cljs/lite_collections_test.cljs index a96148f00..99cb2be29 100644 --- a/src/test/cljs/cljs/lite_collections_test.cljs +++ b/src/test/cljs/cljs/lite_collections_test.cljs @@ -18,10 +18,10 @@ (let [a {:foo 1}] (is (== 1 (:foo a))))) -(deftest test-simple-set-with-set - (is (= (simple-set []) (set []))) - (is (= (set []) (simple-set []))) - (is (= (simple-set [(MapEntry. 1 2 nil)]) +(deftest test-set-lite-with-set + (is (= (set-lite []) (set []))) + (is (= (set []) (set-lite []))) + (is (= (set-lite [(MapEntry. 1 2 nil)]) (set [(MapEntry. 1 2 nil)])))) (deftest test-obj-map-clj->js @@ -31,9 +31,9 @@ (deftest test-unchanged-identical? (let [m (obj-map :foo 1)] (identical? m (assoc m :foo 1))) - (let [m (simple-hash-map :foo 1)] + (let [m (hash-map-lite :foo 1)] (identical? m (assoc m :foo 1))) - (let [s (simple-set [:foo])] + (let [s (set-lite [:foo])] (identical? s (conj s :foo)))) (comment diff --git a/src/test/clojure/cljs/analyzer_pass_tests.clj b/src/test/clojure/cljs/analyzer_pass_tests.clj index 1a451d491..643352b7a 100644 --- a/src/test/clojure/cljs/analyzer_pass_tests.clj +++ b/src/test/clojure/cljs/analyzer_pass_tests.clj @@ -185,14 +185,14 @@ (comp/with-core-cljs {} (fn [] (analyze aenv 'cljs.core/vec))))] - (is (= 'cljs.core/simple-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/simple-vector + (is (= 'cljs.core/vector-lite (-> ast :name) (-> ast :info :name)))))) From c4bc714ae4853ae91e55f342e89df0809aaed310 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 25 Nov 2025 06:10:21 -0500 Subject: [PATCH 4011/4033] be less specific around int coercion fns (#294) cljs.tools.reader uses `int` to coerce numeric unicode chars, with better inference of JS APIs this triggered a numeric warning. But this warning is a bit bogus in that `int` is for coercion anyway. Clojure is vague about `int` behavior, we should be equally vague. --- src/main/cljs/cljs/core.cljs | 8 ++++---- src/main/clojure/cljs/core.cljc | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index ad87e9505..40944f0c8 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -2932,22 +2932,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)) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index f6cfb7a1e..adde92ad0 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -1236,8 +1236,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)) From cc0d6e08758e84e848e571086465bb011d5b3755 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 25 Nov 2025 06:36:02 -0500 Subject: [PATCH 4012/4033] remove now irrelevant test around int --- src/test/clojure/cljs/type_inference_tests.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/clojure/cljs/type_inference_tests.clj b/src/test/clojure/cljs/type_inference_tests.clj index 2bd0855c3..1a9170020 100644 --- a/src/test/clojure/cljs/type_inference_tests.clj +++ b/src/test/clojure/cljs/type_inference_tests.clj @@ -350,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))))) From f6abdc850aa2f0a6d350e23e0719ca1e6e8c16df Mon Sep 17 00:00:00 2001 From: davidnolen Date: Tue, 25 Nov 2025 20:14:13 -0500 Subject: [PATCH 4013/4033] docstrings for all the :lite-mode ctor fns that are not intended to be used directly --- src/main/cljs/cljs/core.cljs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 40944f0c8..7d8fbeaaf 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -12494,12 +12494,14 @@ reduces them without incurring seq initialization" (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) @@ -12742,8 +12744,7 @@ reduces them without incurring seq initialization" (set! (. ObjMap -fromObject) (fn [ks obj] (ObjMap. nil ks obj nil))) (defn obj-map - "keyval => key val - Returns a new object map with supplied mappings." + ":lite-mode simple key hash-map, not intended to be used directly." [& keyvals] (let [ks (array) obj (js-obj)] @@ -12973,8 +12974,7 @@ reduces them without incurring seq initialization" out))))) (defn hash-map-lite - "keyval => key val - Returns a new hash map with supplied mappings." + ":lite-mode version of hash-map, not intended to be used directly." [& keyvals] (loop [in (seq keyvals), out (. HashMapLite -EMPTY)] (if in @@ -13095,6 +13095,7 @@ reduces them without incurring seq initialization" (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) From 5a26a08265b67f327f525ddbdac9920f4a041359 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Tue, 25 Nov 2025 23:03:22 -0500 Subject: [PATCH 4014/4033] add proxy tests to main test runner and lite mode runner (#295) --- src/test/cljs/cljs/lite_collections_test.cljs | 2 ++ src/test/cljs/cljs/proxy_test.cljs | 33 +++++++++++++++++++ src/test/cljs/lite_test_runner.cljs | 7 ++-- src/test/cljs/test_runner.cljs | 4 ++- 4 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/test/cljs/cljs/proxy_test.cljs diff --git a/src/test/cljs/cljs/lite_collections_test.cljs b/src/test/cljs/cljs/lite_collections_test.cljs index 99cb2be29..56d44d0eb 100644 --- a/src/test/cljs/cljs/lite_collections_test.cljs +++ b/src/test/cljs/cljs/lite_collections_test.cljs @@ -21,6 +21,8 @@ (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)])))) diff --git a/src/test/cljs/cljs/proxy_test.cljs b/src/test/cljs/cljs/proxy_test.cljs new file mode 100644 index 000000000..36b53d7cd --- /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/lite_test_runner.cljs b/src/test/cljs/lite_test_runner.cljs index 95313b3f8..0b0c45b56 100644 --- a/src/test/cljs/lite_test_runner.cljs +++ b/src/test/cljs/lite_test_runner.cljs @@ -7,7 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns lite-test-runner - (:require [cljs.test :refer-macros [run-tests]] + (:require [cljs.proxy-test] + [cljs.test :refer-macros [run-tests]] [cljs.apply-test] [cljs.primitives-test] [cljs.destructuring-test] @@ -72,6 +73,7 @@ (enable-console-print!)) (run-tests + 'cljs.proxy-test 'cljs.apply-test 'cljs.primitives-test 'cljs.destructuring-test @@ -123,5 +125,4 @@ 'cljs.repl-test 'cljs.lite-collections-test 'cljs.extend-to-native-test - 'cljs.var-test - ) + 'cljs.var-test) diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 7e551f9de..16a1cbf18 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -7,7 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns test-runner - (:require [cljs.test :refer-macros [run-tests]] + (:require [cljs.proxy-test] + [cljs.test :refer-macros [run-tests]] [cljs.apply-test] [cljs.primitives-test] [cljs.destructuring-test] @@ -71,6 +72,7 @@ (enable-console-print!)) (run-tests + 'cljs.proxy-test 'cljs.apply-test 'cljs.primitives-test 'cljs.destructuring-test From 9f77604d532c9ee831e0b300def280b669e780a0 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Wed, 26 Nov 2025 09:24:24 -0500 Subject: [PATCH 4015/4033] CLJS-3464: `parents` does not walk JavaScript prototype chain (#296) - implement `bases` return immediate prototype of arg - implement `supers` returns immediate and indirect protoypes of arg - fix hierarchy code to use js-fn? where Clojure used class - add assertions to derive - uncomment some test assertions - do not mutate the root object --- src/main/cljs/cljs/core.cljs | 78 +++++++++++++++++++++++-------- src/test/cljs/cljs/core_test.cljs | 6 +-- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 7d8fbeaaf..88e52f31d 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -1483,10 +1483,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 @@ -11262,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 @@ -11270,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 @@ -11288,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 @@ -11296,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 @@ -11305,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 @@ -11315,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) diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 3cd24205a..2286c2cef 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -197,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 ObjMap ::collection) + (derive ObjMap ::collection) (derive cljs.core.PersistentHashSet ::collection) - ;(is (true? (isa? 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 From 8e6f05288fda6faa604202d9114cdccae6d7c211 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Fri, 28 Nov 2025 07:14:16 -0500 Subject: [PATCH 4016/4033] CLJS-3466: support qualified method in return position (#297) Co-authored-by: Michiel Borkent --- src/main/clojure/cljs/analyzer.cljc | 2 +- src/test/cljs/cljs/qualified_method_test.cljs | 17 +++++++++++++++++ src/test/cljs/lite_test_runner.cljs | 4 +++- src/test/cljs/test_runner.cljs | 4 +++- 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/test/cljs/cljs/qualified_method_test.cljs diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index f1c337420..fc3fc4321 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -4141,7 +4141,7 @@ {:op :qualified-method :env env :form sym - :class (analyze-symbol env (symbol sym-ns))} + :class (analyze-symbol (assoc env :context :expr) (symbol sym-ns))} (if (= "new" sym-name) {:kind :new :name (symbol sym-name)} 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 000000000..df339b6b8 --- /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/lite_test_runner.cljs b/src/test/cljs/lite_test_runner.cljs index 0b0c45b56..c9f58d677 100644 --- a/src/test/cljs/lite_test_runner.cljs +++ b/src/test/cljs/lite_test_runner.cljs @@ -7,7 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns lite-test-runner - (:require [cljs.proxy-test] + (:require [cljs.qualified-method-test] + [cljs.proxy-test] [cljs.test :refer-macros [run-tests]] [cljs.apply-test] [cljs.primitives-test] @@ -73,6 +74,7 @@ (enable-console-print!)) (run-tests + 'cljs.qualified-method-test 'cljs.proxy-test 'cljs.apply-test 'cljs.primitives-test diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs index 16a1cbf18..666e0ea82 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -7,7 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns test-runner - (:require [cljs.proxy-test] + (:require [cljs.qualified-method-test] + [cljs.proxy-test] [cljs.test :refer-macros [run-tests]] [cljs.apply-test] [cljs.primitives-test] @@ -72,6 +73,7 @@ (enable-console-print!)) (run-tests + 'cljs.qualified-method-test 'cljs.proxy-test 'cljs.apply-test 'cljs.primitives-test From 5bb99126d7eecfde4bf1514c00d2f9ea5e11ab38 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 30 Nov 2025 09:35:55 -0500 Subject: [PATCH 4017/4033] add Browser REPL alias to deps.edn --- deps.edn | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deps.edn b/deps.edn index 7651fd0c1..b7057d93f 100644 --- a/deps.edn +++ b/deps.edn @@ -11,6 +11,8 @@ :aliases {: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"] From 9a55107a23f6d8fe1a9c8f5ece623f6fa1c6aeab Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 30 Nov 2025 15:15:51 -0500 Subject: [PATCH 4018/4033] cljs.proxy doesn't handle `for .. of` correctly (#298) - check for Symbol.iterator case in vec and map handler - need to bind iterator to target for it to work - add cljs.proxy.impl/MapIterator * apply the proxy ctor to the iterator results --- src/main/cljs/cljs/proxy.cljs | 24 +++++++++++++++++++----- src/main/cljs/cljs/proxy/impl.cljs | 9 +++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/main/cljs/cljs/proxy.cljs b/src/main/cljs/cljs/proxy.cljs index b0d40655f..c3ef55414 100644 --- a/src/main/cljs/cljs/proxy.cljs +++ b/src/main/cljs/cljs/proxy.cljs @@ -1,6 +1,6 @@ (ns cljs.proxy - (:refer-global :only [Proxy isNaN]) - (:require [cljs.proxy.impl :refer [SimpleCache]])) + (:refer-global :only [isNaN Proxy Symbol]) + (:require [cljs.proxy.impl :refer [SimpleCache MapIterator]])) (defn- write-through [f] (let [cache (SimpleCache. #js {} 0)] @@ -45,16 +45,28 @@ (js* "var __ctor") (let [cache-key-fn (write-through key-fn) vec-handler #js {:get (fn [^cljs.core/IIndexed target prop receiver] - (if (identical? prop "length") + (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] - (if (identical? prop "length") - true + (cond + (identical? prop "length") true + + (identical? (. Symbol -iterator) prop) true + + :else (let [n (js* "+~{}" prop)] (and (number? n) (not (isNaN n)) @@ -150,5 +162,7 @@ (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 index 56c99430d..3914cc115 100644 --- a/src/main/cljs/cljs/proxy/impl.cljs +++ b/src/main/cljs/cljs/proxy/impl.cljs @@ -13,3 +13,12 @@ (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)))) From df70c1a4c55da1dc49b2e1ec84f75af11a7e1809 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 30 Nov 2025 16:25:11 -0500 Subject: [PATCH 4019/4033] bootstrap wasn't updated for cljs.compiler/emit-global-export change (#299) --- src/main/cljs/cljs/js.cljs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs index 076350951..bb8e7fbc4 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) From e3065b3ad6a8223d5e12203a85cb794b0782cc57 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 30 Nov 2025 19:00:19 -0500 Subject: [PATCH 4020/4033] update changes.md --- changes.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/changes.md b/changes.md index 55c6119e2..f41315f45 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,16 @@ +## 1.12.1NN + +### Changes +* Be less specific about the behavior of integer coercion fns + +### Fixes +* Docstrings for `:lite-mode` support fns +* CLJS-3456: bootstrap wasn't updated for cljs.compiler/emit-global-export change +* cljs.proxy doesn't handle `for .. of` correctly +* CLJS-3466: support qualified method in return position +* CLJS-3464: `parents` does not walk JavaScript prototype chain +* CLJS-3463: rename all the lite mode data structures / fns to avoid clashing + ## 1.12.116 ### Changes From 54b1e3397a5824a68b11bd4189de2dbbdc5fbbc9 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 30 Nov 2025 20:29:06 -0500 Subject: [PATCH 4021/4033] allow cljs.proxy cache customization, provide default proxy (#300) --- src/main/cljs/cljs/proxy.cljs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/proxy.cljs b/src/main/cljs/cljs/proxy.cljs index c3ef55414..0940d472c 100644 --- a/src/main/cljs/cljs/proxy.cljs +++ b/src/main/cljs/cljs/proxy.cljs @@ -38,12 +38,17 @@ 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." + 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 (write-through key-fn) + (let [cache-key-fn (cache-fn key-fn) vec-handler #js {:get (fn [^cljs.core/IIndexed target prop receiver] (cond (identical? "length" prop) @@ -106,6 +111,9 @@ :else target))] __ctor))) +(def ^{:doc "Default proxy for maps and vectors."} + proxy (builder)) + (comment (def c (SimpleCache. #js {} 0)) From 6bb6df196506286994d1f4259c89ea0f5a0dffd4 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 30 Nov 2025 20:31:38 -0500 Subject: [PATCH 4022/4033] update changes.md for cljs.proxy changes --- changes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changes.md b/changes.md index f41315f45..d5b8d48c5 100644 --- a/changes.md +++ b/changes.md @@ -2,6 +2,8 @@ ### Changes * Be less specific about the behavior of integer coercion fns +* `cljs.proxy/builder`, `cache-fn` parameterization +* Provide `cljs.proxy/proxy` default ### Fixes * Docstrings for `:lite-mode` support fns From 9d59f911cec44a7779efaed403c9bd954f426387 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 30 Nov 2025 21:30:17 -0500 Subject: [PATCH 4023/4033] typos in comment --- src/main/cljs/cljs/core.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 88e52f31d..679b17925 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -3639,7 +3639,7 @@ reduces them without incurring seq initialization" IEmptyableCollection (-empty [coll] ;; MAYBE FIXME: :lite-mode testing uncovered a very old bug, empty on seq - ;; should discared the metadata, we change the behavior in LITE_MODE for now + ;; 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) From 2bc5688b5c82121d38f35e9ab9173dda44426a25 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 30 Nov 2025 21:43:00 -0500 Subject: [PATCH 4024/4033] missing notices --- src/main/cljs/cljs/proxy.cljs | 8 ++++++++ src/main/cljs/cljs/proxy/impl.cljs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/main/cljs/cljs/proxy.cljs b/src/main/cljs/cljs/proxy.cljs index 0940d472c..23a85a9f7 100644 --- a/src/main/cljs/cljs/proxy.cljs +++ b/src/main/cljs/cljs/proxy.cljs @@ -1,3 +1,11 @@ +; 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]])) diff --git a/src/main/cljs/cljs/proxy/impl.cljs b/src/main/cljs/cljs/proxy/impl.cljs index 3914cc115..c47652931 100644 --- a/src/main/cljs/cljs/proxy/impl.cljs +++ b/src/main/cljs/cljs/proxy/impl.cljs @@ -1,3 +1,11 @@ +; 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] From 49774b1bbcd6d6a14c7aaaf73c92aa4c6ea8b96c Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 7 Dec 2025 16:42:01 -0500 Subject: [PATCH 4025/4033] re-order latest changes --- changes.md | 22 +++++++++++----------- src/main/clojure/cljs/analyzer.cljc | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/changes.md b/changes.md index d5b8d48c5..901bba22d 100644 --- a/changes.md +++ b/changes.md @@ -2,15 +2,15 @@ ### Changes * Be less specific about the behavior of integer coercion fns -* `cljs.proxy/builder`, `cache-fn` parameterization * 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-3456: bootstrap wasn't updated for cljs.compiler/emit-global-export change -* cljs.proxy doesn't handle `for .. of` correctly * 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 @@ -117,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 @@ -618,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) @@ -897,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 @@ -996,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 @@ -1049,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) @@ -1378,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 @@ -1753,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/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index fc3fc4321..12940689a 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1067,7 +1067,7 @@ ;; i.e. [console] -> [Console] but :tag is Console _not_ Function vs. ;; [console log] -> [Console prototype log] where :tag is Function (and (empty? (next pre)) - (not (contains? ret :info))) +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'))) From c4295f303100bbf5afac449242d30bca1126f1a1 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Mon, 8 Dec 2025 19:19:37 -0500 Subject: [PATCH 4026/4033] 1.12.134 --- README.md | 10 +++++----- changes.md | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3fc82e6e0..2975a3f15 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.116 +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.116"} + org.clojure/clojurescript {:mvn/version "1.12.134"} ``` [Leiningen](https://github.com/technomancy/leiningen/) dependency information: ``` -[org.clojure/clojurescript "1.12.116"] +[org.clojure/clojurescript "1.12.134"] ``` [Maven](https://maven.apache.org) dependency information: @@ -28,7 +28,7 @@ Latest stable release: 1.12.116 org.clojure clojurescript - 1.12.116 + 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 901bba22d..54c63d805 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -## 1.12.1NN +## 1.12.134 ### Changes * Be less specific about the behavior of integer coercion fns From 3122975e70f0a612cee6aab82be31428ac810dd1 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Dec 2025 08:53:11 -0500 Subject: [PATCH 4027/4033] CLJS-3468: :refer-global should not make unrenamed object available (#301) Co-authored-by: Michiel Borkent --- src/main/clojure/cljs/analyzer.cljc | 3 ++- src/test/clojure/cljs/analyzer_tests.clj | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 12940689a..daa4872a0 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3098,7 +3098,8 @@ x (not (contains? ret :info))) (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 only (repeat 'js)) + {:use (zipmap (if rename (remove rename only) + only) (repeat 'js)) :rename (into {} (map (fn [[orig new-name]] [new-name (symbol "js" (str orig))])) diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj index ca388182d..07aff9247 100644 --- a/src/test/clojure/cljs/analyzer_tests.clj +++ b/src/test/clojure/cljs/analyzer_tests.clj @@ -391,7 +391,7 @@ (let [parsed (ana/parse-global-refer-spec {} '((:refer-global :only [Date Symbol] :rename {Symbol JSSymbol})))] (is (= parsed - '{:use {Date js Symbol js} + '{:use {Date js} :rename {JSSymbol js/Symbol}})))) (deftest test-parse-require-global From c4fcc9ccd80fe30ea4bf29abdf1cbc59a3f76b67 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 28 Dec 2025 12:43:23 -0500 Subject: [PATCH 4028/4033] goog-define types are already inferred --- src/main/cljs/cljs/core.cljs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 679b17925..60078fcf1 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -54,8 +54,7 @@ *global* "default") (goog-define - ^{:doc "Boolean flag for LITE_MODE" - :jsdoc ["@type {boolean}"]} + ^{:doc "Boolean flag for LITE_MODE"} LITE_MODE false) (def From 8a596a8bdf8188112332207e5351e143740168f5 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Sun, 28 Dec 2025 14:30:39 -0500 Subject: [PATCH 4029/4033] CLJS-3469: ClojureScript :preloads doesn't work with just cljs.main --repl (#302) Co-authored-by: Juan Monetta --- src/main/clojure/cljs/cli.clj | 3 ++- src/main/clojure/cljs/repl/browser.clj | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj index da4f2761f..9087151c9 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/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj index 2c907c6d1..9b333127e 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")) From 091df76c65478f9150e83ebbb13b4cf1097a2b2a Mon Sep 17 00:00:00 2001 From: David Nolen Date: Mon, 2 Feb 2026 17:43:20 -0500 Subject: [PATCH 4030/4033] CLJS-3471: fix printing of negative zero (#305) Co-authored-by: Michiel Borkent --- src/main/cljs/cljs/core.cljs | 1 + src/test/cljs/cljs/printing_test.cljs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs index 60078fcf1..c5d186689 100644 --- a/src/main/cljs/cljs/core.cljs +++ b/src/main/cljs/cljs/core.cljs @@ -10470,6 +10470,7 @@ reduces them without incurring seq initialization" (js/isNaN obj) "##NaN" (identical? obj js/Number.POSITIVE_INFINITY) "##Inf" (identical? obj js/Number.NEGATIVE_INFINITY) "##-Inf" + (js/Object.is obj -0.0) "-0.0" :else (str_ obj))) (object? obj) diff --git a/src/test/cljs/cljs/printing_test.cljs b/src/test/cljs/cljs/printing_test.cljs index c114893df..008172ed1 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")) From 96cd8ad1405cec4a8893c09c973ca9c3c6d3355e Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 12 Feb 2026 20:08:45 -0500 Subject: [PATCH 4031/4033] CLJS-3472: str on var that is set! returns empty string (#307) * CLJS-3472: str on var that is set! returns empty string * comment out str optimization tests for now --------- Co-authored-by: Michiel Borkent --- src/main/clojure/cljs/core.cljc | 2 -- src/test/cljs/cljs/core_test.cljs | 5 +++++ src/test/clojure/cljs/build_api_tests.clj | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index adde92ad0..494f4014e 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -878,8 +878,6 @@ [& xs] (core/let [interpolate (core/fn [x] (core/cond - (typed-expr? &env x '#{clj-nil}) - nil (compile-time-constant? x) ["+~{}" x] :else diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs index 2286c2cef..01f6f0b52 100644 --- a/src/test/cljs/cljs/core_test.cljs +++ b/src/test/cljs/cljs/core_test.cljs @@ -1988,6 +1988,11 @@ (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))) diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj index 3e44d1b4b..caa8da41f 100644 --- a/src/test/clojure/cljs/build_api_tests.clj +++ b/src/test/clojure/cljs/build_api_tests.clj @@ -941,7 +941,7 @@ (test/delete-node-modules) (test/delete-out-files out)))) -(deftest test-cljs-3452-str-optimizations +#_(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) From dfdf09113756ec28e1b524d32baab9967655f589 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 14 Feb 2026 13:12:21 +0100 Subject: [PATCH 4032/4033] CLJS-3470: add async/await support --- resources/test.edn | 2 +- src/main/cljs/cljs/test.cljc | 2 +- src/main/clojure/cljs/analyzer.cljc | 6 + src/main/clojure/cljs/compiler.cljc | 86 +++++---- src/main/clojure/cljs/core.cljc | 118 ++++++------ src/test/cljs/cljs/async_await_test.cljs | 231 +++++++++++++++++++++++ src/test/cljs/cljs/macro_test/macros.clj | 4 + src/test/cljs/test_runner.cljs | 4 +- 8 files changed, 357 insertions(+), 96 deletions(-) create mode 100644 src/test/cljs/cljs/async_await_test.cljs diff --git a/resources/test.edn b/resources/test.edn index a8f258139..a9b40633e 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/src/main/cljs/cljs/test.cljc b/src/main/cljs/cljs/test.cljc index 5afb03ca2..27776ad4b 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 daa4872a0..c63b857f2 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -2314,6 +2314,12 @@ x (not (contains? ret :info))) 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) diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc index 6e9d152ff..1fbf54ec2 100644 --- a/src/main/clojure/cljs/compiler.cljc +++ b/src/main/clojure/cljs/compiler.cljc @@ -701,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 ";")) @@ -723,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 @@ -865,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 " = " @@ -878,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 @@ -936,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. @@ -968,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 ","))) @@ -984,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 @@ -1024,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)) @@ -1054,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)) @@ -1101,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]}] @@ -1112,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 "}")) @@ -1120,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) @@ -1145,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)) @@ -1166,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) diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc index 494f4014e..d8a2d0fd5 100644 --- a/src/main/clojure/cljs/core.cljc +++ b/src/main/clojure/cljs/core.cljc @@ -7,7 +7,7 @@ ; 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 @@ -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- @@ -972,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] @@ -3209,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)))]) @@ -3290,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) 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 000000000..75ffb6758 --- /dev/null +++ b/src/test/cljs/cljs/async_await_test.cljs @@ -0,0 +1,231 @@ +(ns cljs.async-await-test + (:require [clojure.test :refer [deftest is async]] + [cljs.core :as cc :refer [await] :rename {await aw}]) + (: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))))) diff --git a/src/test/cljs/cljs/macro_test/macros.clj b/src/test/cljs/cljs/macro_test/macros.clj index 1354e8c01..d57250362 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/test_runner.cljs b/src/test/cljs/test_runner.cljs index 666e0ea82..0488aa477 100644 --- a/src/test/cljs/test_runner.cljs +++ b/src/test/cljs/test_runner.cljs @@ -7,7 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns test-runner - (:require [cljs.qualified-method-test] + (:require [cljs.async-await-test] + [cljs.qualified-method-test] [cljs.proxy-test] [cljs.test :refer-macros [run-tests]] [cljs.apply-test] @@ -73,6 +74,7 @@ (enable-console-print!)) (run-tests + 'cljs.async-await-test 'cljs.qualified-method-test 'cljs.proxy-test 'cljs.apply-test From 106991f96b037b0655b6051b30662714b01fe647 Mon Sep 17 00:00:00 2001 From: davidnolen Date: Sun, 8 Mar 2026 11:55:39 -0400 Subject: [PATCH 4033/4033] Add additional async/await tests: literals, new, destructure :or --- src/test/cljs/cljs/async_await_test.cljs | 53 ++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/test/cljs/cljs/async_await_test.cljs b/src/test/cljs/cljs/async_await_test.cljs index 75ffb6758..c3d9d447a 100644 --- a/src/test/cljs/cljs/async_await_test.cljs +++ b/src/test/cljs/cljs/async_await_test.cljs @@ -1,6 +1,8 @@ (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}]) + [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] @@ -192,9 +194,9 @@ (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 ))) + ;; 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 @@ -229,3 +231,46 @@ (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) + )